@parity/product-sdk-bulletin 0.1.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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/client.ts","../src/authorization.ts","../src/errors.ts","../src/cid.ts","../src/gateway.ts","../src/query.ts","../src/resolve-query.ts","../src/upload.ts","../src/resolve-signer.ts"],"sourcesContent":["import { getChainAPI } from \"@parity/product-sdk-chain-client\";\nimport type { PolkadotSigner } from \"polkadot-api\";\n\nimport { checkAuthorization } from \"./authorization.js\";\nimport {\n type CidCodec,\n type HashAlgorithm,\n cidToPreimageKey,\n computeCid,\n hashToCid,\n} from \"./cid.js\";\nimport { cidExists, getGateway, gatewayUrl } from \"./gateway.js\";\nimport { executeQuery } from \"./query.js\";\nimport { resolveQueryStrategy, type QueryStrategy } from \"./resolve-query.js\";\nimport { batchUpload, upload } from \"./upload.js\";\nimport type {\n AuthorizationStatus,\n BatchUploadItem,\n BatchUploadOptions,\n BatchUploadResult,\n BulletinApi,\n Environment,\n QueryOptions,\n UploadOptions,\n UploadResult,\n} from \"./types.js\";\n\n/**\n * Ergonomic entry point for Bulletin Chain operations.\n *\n * Bundles a typed Bulletin API (from chain-client) and an IPFS gateway URL\n * so callers don't need to re-pass them on every call.\n *\n * Both upload and query paths use the host container APIs:\n * - **Uploads** — the host preimage API signs and submits automatically.\n * - **Queries** (`fetchBytes`/`fetchJson`) — uses host preimage lookup with caching.\n *\n * @example\n * ```ts\n * const bulletin = await BulletinClient.create(\"paseo\");\n * const result = await bulletin.upload(fileBytes);\n * const metadata = await bulletin.fetchJson<Metadata>(result.cid);\n * ```\n */\nexport class BulletinClient {\n readonly api: BulletinApi;\n readonly gateway: string;\n\n private queryStrategyPromise: Promise<QueryStrategy> | null = null;\n\n private constructor(api: BulletinApi, gateway: string) {\n this.api = api;\n this.gateway = gateway;\n }\n\n /** Lazily resolve and cache the query strategy for the client lifetime. */\n private resolveQuery(): Promise<QueryStrategy> {\n if (!this.queryStrategyPromise) {\n this.queryStrategyPromise = resolveQueryStrategy();\n }\n return this.queryStrategyPromise;\n }\n\n /** Create from an environment — resolves API via chain-client, gateway from known list. */\n static async create(env: Environment): Promise<BulletinClient> {\n const chain = await getChainAPI(env);\n return new BulletinClient(chain.bulletin, getGateway(env));\n }\n\n /** Create from an explicit API and gateway (custom setups, testing). */\n static from(api: BulletinApi, gateway: string): BulletinClient {\n return new BulletinClient(api, gateway);\n }\n\n /** Compute CID without uploading. Static — no instance needed. */\n static computeCid(data: Uint8Array): string {\n return computeCid(data);\n }\n\n /**\n * Reconstruct a CID from a `0x`-prefixed hex hash. Static — no instance needed.\n *\n * Useful for converting on-chain hashes back to CIDs for IPFS gateway lookups.\n * Pass optional hash algorithm and codec to match the on-chain CID configuration.\n *\n * @see {@link hashToCid} for full documentation.\n */\n static hashToCid(hexHash: `0x${string}`, hashCode?: HashAlgorithm, codec?: CidCodec): string {\n return hashToCid(hexHash, hashCode, codec);\n }\n\n /**\n * Upload data to the Bulletin Chain.\n *\n * @param data - Raw bytes to store.\n * @param signer - Optional signer. When omitted, uses the host preimage API.\n * @param options - Upload options (timeout, waitFor, status callback).\n */\n async upload(\n data: Uint8Array,\n signer?: PolkadotSigner,\n options?: Omit<UploadOptions, \"gateway\">,\n ): Promise<UploadResult> {\n return upload(this.api, data, signer, { ...options, gateway: this.gateway });\n }\n\n /**\n * Upload multiple items sequentially.\n *\n * @param items - Array of items to upload, each with data and a label.\n * @param signer - Optional signer. When omitted, auto-resolved.\n * @param options - Batch upload options (timeout, progress callback).\n */\n async batchUpload(\n items: BatchUploadItem[],\n signer?: PolkadotSigner,\n options?: Omit<BatchUploadOptions, \"gateway\">,\n ): Promise<BatchUploadResult[]> {\n return batchUpload(this.api, items, signer, { ...options, gateway: this.gateway });\n }\n\n /**\n * Fetch raw bytes by CID.\n *\n * Uses host preimage lookup with caching.\n */\n async fetchBytes(cid: string, options?: QueryOptions): Promise<Uint8Array> {\n const strategy = await this.resolveQuery();\n return executeQuery(strategy, cid, options);\n }\n\n /**\n * Fetch and parse JSON by CID.\n *\n * Auto-resolves query path (same as {@link fetchBytes}).\n */\n async fetchJson<T>(cid: string, options?: QueryOptions): Promise<T> {\n const bytes = await this.fetchBytes(cid, options);\n return JSON.parse(new TextDecoder().decode(bytes)) as T;\n }\n\n /** Check if a CID exists on the gateway. */\n async cidExists(cid: string): Promise<boolean> {\n return cidExists(cid, this.gateway);\n }\n\n /** Build the full gateway URL for a CID. */\n gatewayUrl(cid: string): string {\n return gatewayUrl(cid, this.gateway);\n }\n\n /**\n * Check whether an account is authorized to store data on the Bulletin Chain.\n *\n * Use as a pre-flight check before {@link upload} to provide clear UX\n * instead of letting the transaction fail mid-execution.\n *\n * @param address - SS58-encoded account address to check.\n * @returns Authorization status with remaining quota.\n *\n * @see {@link checkAuthorization} for the standalone function equivalent.\n */\n async checkAuthorization(address: string): Promise<AuthorizationStatus> {\n return checkAuthorization(this.api, address);\n }\n}\n\nif (import.meta.vitest) {\n const { describe, test, expect, vi } = import.meta.vitest;\n\n const mockApi = {\n tx: {\n TransactionStorage: {\n store: vi.fn().mockReturnValue({\n signSubmitAndWatch: () => ({\n subscribe: (handlers: { next: (e: unknown) => void }) => {\n queueMicrotask(() => {\n handlers.next({ type: \"signed\", txHash: \"0x\" });\n handlers.next({\n type: \"txBestBlocksState\",\n txHash: \"0x\",\n found: true,\n ok: true,\n block: { hash: \"0xblock\", number: 1, index: 0 },\n events: [],\n });\n });\n return { unsubscribe: vi.fn() };\n },\n }),\n }),\n },\n },\n } as unknown as BulletinApi;\n\n const GATEWAY = \"https://test-gw/ipfs/\";\n\n describe(\"BulletinClient\", () => {\n test(\"from() creates client with given API and gateway\", () => {\n const client = BulletinClient.from(mockApi, GATEWAY);\n expect(client.api).toBe(mockApi);\n expect(client.gateway).toBe(GATEWAY);\n });\n\n test(\"computeCid() is static and delegates to standalone\", () => {\n const data = new TextEncoder().encode(\"hello\");\n const cid = BulletinClient.computeCid(data);\n expect(cid).toBe(computeCid(data));\n });\n\n test(\"hashToCid() is static and delegates to standalone\", () => {\n const data = new TextEncoder().encode(\"hello\");\n const cid = computeCid(data);\n const key = cidToPreimageKey(cid);\n expect(BulletinClient.hashToCid(key)).toBe(cid);\n });\n\n test(\"gatewayUrl() returns gateway + cid\", () => {\n const client = BulletinClient.from(mockApi, GATEWAY);\n expect(client.gatewayUrl(\"bafyabc\")).toBe(\"https://test-gw/ipfs/bafyabc\");\n });\n\n test(\"upload() passes gateway from client with explicit signer\", async () => {\n const client = BulletinClient.from(mockApi, GATEWAY);\n const data = new TextEncoder().encode(\"test\");\n const result = await client.upload(data, {} as PolkadotSigner);\n expect(result.gatewayUrl).toContain(GATEWAY);\n expect(result.cid).toBeTruthy();\n });\n\n // Note: fetchBytes and fetchJson tests require e2e testing as they\n // depend on the host container environment for strategy resolution.\n\n test(\"checkAuthorization delegates to standalone\", async () => {\n const authMockApi = {\n ...mockApi,\n query: {\n TransactionStorage: {\n Authorizations: {\n getValue: vi.fn().mockResolvedValue({\n extent: { transactions: 5, bytes: 2000n },\n expiration: 100,\n }),\n },\n },\n },\n } as unknown as BulletinApi;\n const client = BulletinClient.from(authMockApi, GATEWAY);\n const status = await client.checkAuthorization(\"5GrwvaEF...\");\n expect(status.authorized).toBe(true);\n expect(status.remainingTransactions).toBe(5);\n expect(status.remainingBytes).toBe(2000n);\n });\n });\n}\n","import { createLogger } from \"@parity/product-sdk-logger\";\nimport { Enum } from \"polkadot-api\";\n\nimport { BulletinAuthorizationError } from \"./errors.js\";\nimport type { AuthorizationStatus, BulletinApi } from \"./types.js\";\n\nconst log = createLogger(\"bulletin\");\n\nconst NOT_AUTHORIZED: AuthorizationStatus = Object.freeze({\n authorized: false,\n remainingTransactions: 0,\n remainingBytes: 0n,\n expiration: 0,\n});\n\n/**\n * Check whether an account is authorized to store data on the Bulletin Chain.\n *\n * Queries `TransactionStorage.Authorizations` for the given address and returns\n * the raw authorization quota. Use this as a pre-flight check before calling\n * {@link upload} to provide clear UX (\"not authorized\" / \"insufficient quota\")\n * instead of letting the transaction fail mid-execution.\n *\n * The expiration block number is returned as-is — the chain enforces expiration\n * at submission time, so callers can optionally compare against the current\n * block for display purposes.\n *\n * @param api - Typed Bulletin Chain API instance.\n * @param address - SS58-encoded account address to check.\n * @returns Authorization status with remaining quota.\n *\n * @example\n * ```ts\n * import { checkAuthorization } from \"@parity/product-sdk-bulletin\";\n *\n * const auth = await checkAuthorization(api, address);\n * if (!auth.authorized) {\n * console.error(\"Account is not authorized for bulletin storage\");\n * } else if (auth.remainingBytes < BigInt(fileBytes.length)) {\n * console.error(`Insufficient quota: ${auth.remainingBytes} bytes remaining`);\n * }\n * ```\n *\n * @see {@link BulletinClient.checkAuthorization} for the client method equivalent.\n */\nexport async function checkAuthorization(\n api: BulletinApi,\n address: string,\n): Promise<AuthorizationStatus> {\n let auth;\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n auth = await (api as any).query.TransactionStorage.Authorizations.getValue(\n Enum(\"Account\", address),\n );\n } catch (error) {\n log.error(\"checkAuthorization: query failed\", { address, error });\n throw new BulletinAuthorizationError(address, error);\n }\n\n if (!auth) {\n log.debug(\"checkAuthorization: no authorization found\", { address });\n return NOT_AUTHORIZED;\n }\n\n const status: AuthorizationStatus = {\n authorized: true,\n remainingTransactions: auth.extent.transactions,\n remainingBytes: auth.extent.bytes,\n expiration: auth.expiration,\n };\n\n log.debug(\"checkAuthorization\", {\n address,\n remainingTransactions: status.remainingTransactions,\n remainingBytes: status.remainingBytes.toString(),\n expiration: status.expiration,\n });\n\n return status;\n}\n\nif (import.meta.vitest) {\n const { describe, test, expect, vi } = import.meta.vitest;\n\n function createMockApi(authResult: unknown) {\n return {\n query: {\n TransactionStorage: {\n Authorizations: {\n getValue: vi.fn().mockResolvedValue(authResult),\n },\n },\n },\n } as unknown as BulletinApi;\n }\n\n describe(\"checkAuthorization\", () => {\n test(\"returns not authorized when no authorization exists\", async () => {\n const api = createMockApi(undefined);\n const status = await checkAuthorization(api, \"5GrwvaEF...\");\n\n expect(status.authorized).toBe(false);\n expect(status.remainingTransactions).toBe(0);\n expect(status.remainingBytes).toBe(0n);\n expect(status.expiration).toBe(0);\n });\n\n test(\"returns authorization with full quota\", async () => {\n const api = createMockApi({\n extent: { transactions: 10, bytes: 1_000_000n },\n expiration: 999,\n });\n const status = await checkAuthorization(api, \"5GrwvaEF...\");\n\n expect(status.authorized).toBe(true);\n expect(status.remainingTransactions).toBe(10);\n expect(status.remainingBytes).toBe(1_000_000n);\n expect(status.expiration).toBe(999);\n });\n\n test(\"returns authorization with zero transactions remaining\", async () => {\n const api = createMockApi({\n extent: { transactions: 0, bytes: 1_000_000n },\n expiration: 999,\n });\n const status = await checkAuthorization(api, \"5GrwvaEF...\");\n\n expect(status.authorized).toBe(true);\n expect(status.remainingTransactions).toBe(0);\n });\n\n test(\"returns authorization with zero bytes remaining\", async () => {\n const api = createMockApi({\n extent: { transactions: 5, bytes: 0n },\n expiration: 999,\n });\n const status = await checkAuthorization(api, \"5GrwvaEF...\");\n\n expect(status.authorized).toBe(true);\n expect(status.remainingBytes).toBe(0n);\n });\n\n test(\"preserves expiration block number\", async () => {\n const api = createMockApi({\n extent: { transactions: 1, bytes: 500n },\n expiration: 12345,\n });\n const status = await checkAuthorization(api, \"5GrwvaEF...\");\n\n expect(status.expiration).toBe(12345);\n });\n\n test(\"throws BulletinAuthorizationError when query fails\", async () => {\n const api = {\n query: {\n TransactionStorage: {\n Authorizations: {\n getValue: vi.fn().mockRejectedValue(new Error(\"RPC connection lost\")),\n },\n },\n },\n } as unknown as BulletinApi;\n\n const err = await checkAuthorization(api, \"5GrwvaEF...\").catch((e: unknown) => e);\n expect(err).toBeInstanceOf(BulletinAuthorizationError);\n const error = err as BulletinAuthorizationError;\n expect(error.address).toBe(\"5GrwvaEF...\");\n expect(error.cause).toBeInstanceOf(Error);\n expect((error.cause as Error).message).toBe(\"RPC connection lost\");\n });\n\n test(\"passes correct Enum key to the query\", async () => {\n const getValue = vi.fn().mockResolvedValue(undefined);\n const api = {\n query: {\n TransactionStorage: {\n Authorizations: { getValue },\n },\n },\n } as unknown as BulletinApi;\n\n await checkAuthorization(api, \"5GrwvaEF...\");\n\n expect(getValue).toHaveBeenCalledTimes(1);\n const arg = getValue.mock.calls[0][0];\n expect(arg.type).toBe(\"Account\");\n expect(arg.value).toBe(\"5GrwvaEF...\");\n });\n });\n}\n","/**\n * Base class for all Bulletin Chain errors.\n *\n * Use `instanceof BulletinError` to catch any bulletin-related error.\n *\n * @example\n * ```ts\n * try {\n * await bulletin.upload(data);\n * } catch (e) {\n * if (e instanceof BulletinError) {\n * console.error(\"Bulletin operation failed:\", e.message);\n * }\n * }\n * ```\n */\nexport class BulletinError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"BulletinError\";\n }\n}\n\n/**\n * The host preimage API is unavailable.\n *\n * Thrown when bulletin operations require the host container but it's not available.\n * This typically means the SDK is running outside of Polkadot Browser/Desktop.\n */\nexport class BulletinHostUnavailableError extends BulletinError {\n constructor(operation: \"upload\" | \"query\") {\n super(\n `Host preimage API unavailable for ${operation}. Ensure you are running inside a host container (Polkadot Browser / Desktop).`,\n );\n this.name = \"BulletinHostUnavailableError\";\n }\n}\n\n/**\n * The host preimage lookup timed out.\n *\n * The host was unable to retrieve the requested content within the timeout period.\n * The content may still become available later.\n */\nexport class BulletinLookupTimeoutError extends BulletinError {\n /** The CID that was being looked up. */\n readonly cid: string;\n /** The timeout duration in milliseconds. */\n readonly timeoutMs: number;\n\n constructor(cid: string, timeoutMs: number) {\n super(`Host preimage lookup timed out after ${timeoutMs}ms for CID: ${cid}`);\n this.name = \"BulletinLookupTimeoutError\";\n this.cid = cid;\n this.timeoutMs = timeoutMs;\n }\n}\n\n/**\n * The host interrupted the preimage lookup.\n *\n * The host terminated the lookup subscription, typically after repeated failures\n * or when the host determines the content is unavailable.\n */\nexport class BulletinLookupInterruptedError extends BulletinError {\n /** The CID that was being looked up. */\n readonly cid: string;\n\n constructor(cid: string) {\n super(`Host preimage lookup was interrupted for CID: ${cid}`);\n this.name = \"BulletinLookupInterruptedError\";\n this.cid = cid;\n }\n}\n\n/**\n * Failed to check authorization status for an account.\n *\n * Wraps RPC or query errors that occur when checking if an account\n * is authorized to store data on the Bulletin Chain.\n */\nexport class BulletinAuthorizationError extends BulletinError {\n /** The address that was being checked. */\n readonly address: string;\n\n constructor(address: string, cause?: unknown) {\n super(`Failed to check authorization for ${address}`, { cause });\n this.name = \"BulletinAuthorizationError\";\n this.address = address;\n }\n}\n\n/**\n * The IPFS gateway for the specified environment is not available.\n *\n * Thrown when attempting to use gateway features for a network that\n * doesn't have a live bulletin gateway yet.\n */\nexport class BulletinGatewayUnavailableError extends BulletinError {\n /** The environment that was requested. */\n readonly environment: string;\n\n constructor(environment: string) {\n super(`Bulletin gateway for \"${environment}\" is not yet available`);\n this.name = \"BulletinGatewayUnavailableError\";\n this.environment = environment;\n }\n}\n\n/**\n * An IPFS gateway request failed.\n *\n * Thrown when a fetch to the IPFS gateway returns a non-OK response.\n */\nexport class BulletinGatewayFetchError extends BulletinError {\n /** The CID that was being fetched. */\n readonly cid: string;\n /** The HTTP status code returned by the gateway. */\n readonly status: number;\n /** The HTTP status text returned by the gateway. */\n readonly statusText: string;\n\n constructor(cid: string, status: number, statusText: string) {\n super(`Gateway fetch failed for ${cid}: ${status} ${statusText}`);\n this.name = \"BulletinGatewayFetchError\";\n this.cid = cid;\n this.status = status;\n this.statusText = statusText;\n }\n}\n\n/**\n * Invalid CID format or version.\n *\n * Thrown when a CID string cannot be parsed or has an unexpected version/codec.\n */\nexport class BulletinCidError extends BulletinError {\n /** The invalid CID string, if available. */\n readonly cid?: string;\n\n constructor(message: string, cid?: string) {\n super(message);\n this.name = \"BulletinCidError\";\n this.cid = cid;\n }\n}\n\nif (import.meta.vitest) {\n const { describe, test, expect } = import.meta.vitest;\n\n describe(\"BulletinError hierarchy\", () => {\n test(\"BulletinError is instanceof Error\", () => {\n const err = new BulletinError(\"test\");\n expect(err).toBeInstanceOf(Error);\n expect(err).toBeInstanceOf(BulletinError);\n expect(err.name).toBe(\"BulletinError\");\n });\n\n test(\"BulletinHostUnavailableError\", () => {\n const err = new BulletinHostUnavailableError(\"upload\");\n expect(err).toBeInstanceOf(BulletinError);\n expect(err.name).toBe(\"BulletinHostUnavailableError\");\n expect(err.message).toContain(\"upload\");\n expect(err.message).toContain(\"Host preimage API unavailable\");\n });\n\n test(\"BulletinLookupTimeoutError\", () => {\n const err = new BulletinLookupTimeoutError(\"bafyabc123\", 30000);\n expect(err).toBeInstanceOf(BulletinError);\n expect(err.name).toBe(\"BulletinLookupTimeoutError\");\n expect(err.cid).toBe(\"bafyabc123\");\n expect(err.timeoutMs).toBe(30000);\n expect(err.message).toContain(\"30000ms\");\n expect(err.message).toContain(\"bafyabc123\");\n });\n\n test(\"BulletinLookupInterruptedError\", () => {\n const err = new BulletinLookupInterruptedError(\"bafyabc123\");\n expect(err).toBeInstanceOf(BulletinError);\n expect(err.name).toBe(\"BulletinLookupInterruptedError\");\n expect(err.cid).toBe(\"bafyabc123\");\n expect(err.message).toContain(\"interrupted\");\n });\n\n test(\"BulletinAuthorizationError with cause\", () => {\n const cause = new Error(\"RPC timeout\");\n const err = new BulletinAuthorizationError(\"5GrwvaEF...\", cause);\n expect(err).toBeInstanceOf(BulletinError);\n expect(err.name).toBe(\"BulletinAuthorizationError\");\n expect(err.address).toBe(\"5GrwvaEF...\");\n expect(err.cause).toBe(cause);\n });\n\n test(\"BulletinGatewayUnavailableError\", () => {\n const err = new BulletinGatewayUnavailableError(\"polkadot\");\n expect(err).toBeInstanceOf(BulletinError);\n expect(err.name).toBe(\"BulletinGatewayUnavailableError\");\n expect(err.environment).toBe(\"polkadot\");\n expect(err.message).toContain(\"polkadot\");\n });\n\n test(\"BulletinGatewayFetchError\", () => {\n const err = new BulletinGatewayFetchError(\"bafyabc\", 404, \"Not Found\");\n expect(err).toBeInstanceOf(BulletinError);\n expect(err.name).toBe(\"BulletinGatewayFetchError\");\n expect(err.cid).toBe(\"bafyabc\");\n expect(err.status).toBe(404);\n expect(err.statusText).toBe(\"Not Found\");\n expect(err.message).toContain(\"404\");\n });\n\n test(\"BulletinCidError\", () => {\n const err = new BulletinCidError(\"Expected CIDv1, got CIDv0\", \"Qmabc\");\n expect(err).toBeInstanceOf(BulletinError);\n expect(err.name).toBe(\"BulletinCidError\");\n expect(err.cid).toBe(\"Qmabc\");\n });\n\n test(\"all errors can be caught with BulletinError\", () => {\n const errors = [\n new BulletinHostUnavailableError(\"query\"),\n new BulletinLookupTimeoutError(\"cid\", 1000),\n new BulletinLookupInterruptedError(\"cid\"),\n new BulletinAuthorizationError(\"addr\"),\n new BulletinGatewayUnavailableError(\"env\"),\n new BulletinGatewayFetchError(\"cid\", 500, \"Error\"),\n new BulletinCidError(\"bad cid\"),\n ];\n\n for (const err of errors) {\n expect(err).toBeInstanceOf(BulletinError);\n }\n });\n });\n}\n","import { blake2b256, bytesToHex, hexToBytes } from \"@parity/product-sdk-crypto\";\nimport { createLogger } from \"@parity/product-sdk-logger\";\nimport { CID } from \"multiformats/cid\";\nimport * as Digest from \"multiformats/hashes/digest\";\n\nimport { BulletinCidError } from \"./errors.js\";\n\nconst log = createLogger(\"bulletin\");\n\n/**\n * Hash algorithms supported by the Bulletin Chain.\n *\n * Values are multihash codes as defined in the\n * {@link https://github.com/multiformats/multicodec multicodec table}.\n */\nexport const HashAlgorithm = {\n /** BLAKE2b-256 — default for product-sdk and the chain SDK. */\n Blake2b256: 0xb220,\n /** SHA2-256 — default for bulletin-deploy. */\n Sha2_256: 0x12,\n /** Keccak-256 — Ethereum compatibility. */\n Keccak256: 0x1b,\n} as const;\n\n/** A multihash code supported by the Bulletin Chain. */\nexport type HashAlgorithm = (typeof HashAlgorithm)[keyof typeof HashAlgorithm];\n\n/**\n * CID codecs supported by the Bulletin Chain.\n *\n * Values are multicodec codes.\n */\nexport const CidCodec = {\n /** Raw binary — default for single-chunk data. */\n Raw: 0x55,\n /** DAG-PB — used for multi-chunk manifests / directory structures. */\n DagPb: 0x70,\n /** DAG-CBOR — alternative DAG encoding. */\n DagCbor: 0x71,\n} as const;\n\n/** A multicodec code supported by the Bulletin Chain. */\nexport type CidCodec = (typeof CidCodec)[keyof typeof CidCodec];\n\nconst SUPPORTED_HASH_CODES = new Set<number>(Object.values(HashAlgorithm));\nconst SUPPORTED_CODEC_CODES = new Set<number>(Object.values(CidCodec));\nconst EXPECTED_HEX_LENGTH = 66; // \"0x\" + 64 hex chars = 32 bytes\n\n/**\n * Compute the CIDv1 (blake2b-256, raw codec) for arbitrary data.\n * Deterministic: same input always produces the same CID.\n */\nexport function computeCid(data: Uint8Array): string {\n const hash = blake2b256(data);\n return CID.createV1(CidCodec.Raw, Digest.create(HashAlgorithm.Blake2b256, hash)).toString();\n}\n\n/**\n * Extract the content hash digest from a CIDv1 string and return it as a\n * `0x`-prefixed hex string — the preimage key format used by the host API.\n *\n * Accepts CIDv1 with any hash algorithm supported by the Bulletin Chain\n * (blake2b-256, sha2-256, keccak-256).\n *\n * @param cid - CIDv1 base32 string (as produced by {@link computeCid} or {@link hashToCid}).\n * @returns `0x`-prefixed hex string of the 32-byte hash digest.\n * @throws If the CID is not CIDv1 or uses an unsupported hash algorithm.\n */\nexport function cidToPreimageKey(cid: string): `0x${string}` {\n const parsed = CID.parse(cid);\n if (parsed.version !== 1) {\n throw new BulletinCidError(`Expected CIDv1, got CIDv${parsed.version}`, cid);\n }\n if (!SUPPORTED_HASH_CODES.has(parsed.multihash.code)) {\n throw new BulletinCidError(\n `Unsupported hash algorithm 0x${parsed.multihash.code.toString(16)}; ` +\n `expected one of: ${[...SUPPORTED_HASH_CODES].map((c) => `0x${c.toString(16)}`).join(\", \")}`,\n cid,\n );\n }\n return `0x${bytesToHex(parsed.multihash.digest)}`;\n}\n\n/**\n * Reconstruct a CIDv1 from a `0x`-prefixed hex hash stored on-chain.\n *\n * This is the inverse of {@link cidToPreimageKey}: given a 32-byte content hash\n * and the CID configuration used when the data was stored, it rebuilds the\n * original CIDv1 so you can construct IPFS gateway URLs.\n *\n * The Bulletin Chain supports multiple hash algorithms and codecs — pass the\n * values that match the on-chain `TransactionInfo` to get the correct CID.\n * When omitted, defaults match {@link computeCid} (blake2b-256, raw).\n *\n * @param hexHash - `0x`-prefixed hex string of a 32-byte hash digest\n * (66 characters total: `\"0x\"` + 64 hex chars).\n * @param hashCode - Multihash code of the hashing algorithm (default: blake2b-256 `0xb220`).\n * Use {@link HashAlgorithm} for the supported values.\n * @param codec - Multicodec code of the CID codec (default: raw `0x55`).\n * Use {@link CidCodec} for the supported values.\n * @returns Base32-lower CIDv1 string.\n * @throws If `hexHash` is not exactly 66 characters, or if the hash/codec is unsupported.\n *\n * @example\n * ```ts\n * import { hashToCid, HashAlgorithm, CidCodec, gatewayUrl, getGateway } from \"@parity/product-sdk-bulletin\";\n *\n * // Default (blake2b-256, raw) — matches computeCid output\n * const cid = hashToCid(onChainHash);\n *\n * // SHA2-256 content stored via bulletin-deploy\n * const cid2 = hashToCid(onChainHash, HashAlgorithm.Sha2_256);\n *\n * // DAG-PB manifest with blake2b-256\n * const cid3 = hashToCid(manifestHash, HashAlgorithm.Blake2b256, CidCodec.DagPb);\n *\n * const url = gatewayUrl(cid, getGateway(\"paseo\"));\n * ```\n *\n * @see {@link cidToPreimageKey} for the reverse direction (CID → hex hash).\n * @see {@link computeCid} for computing a CID from raw data.\n * @see {@link HashAlgorithm} for supported hash algorithms.\n * @see {@link CidCodec} for supported CID codecs.\n */\nexport function hashToCid(\n hexHash: `0x${string}`,\n hashCode: HashAlgorithm = HashAlgorithm.Blake2b256,\n codec: CidCodec = CidCodec.Raw,\n): string {\n if (hexHash.length !== EXPECTED_HEX_LENGTH) {\n throw new BulletinCidError(\n `Expected a 0x-prefixed 32-byte hex hash (${EXPECTED_HEX_LENGTH} chars), ` +\n `got ${hexHash.length} chars`,\n );\n }\n if (!SUPPORTED_HASH_CODES.has(hashCode)) {\n throw new BulletinCidError(\n `Unsupported hash algorithm 0x${hashCode.toString(16)}; ` +\n `expected one of: ${[...SUPPORTED_HASH_CODES].map((c) => `0x${c.toString(16)}`).join(\", \")}`,\n );\n }\n if (!SUPPORTED_CODEC_CODES.has(codec)) {\n throw new BulletinCidError(\n `Unsupported CID codec 0x${codec.toString(16)}; ` +\n `expected one of: ${[...SUPPORTED_CODEC_CODES].map((c) => `0x${c.toString(16)}`).join(\", \")}`,\n );\n }\n const digest = hexToBytes(hexHash.slice(2));\n const cid = CID.createV1(codec, Digest.create(hashCode, digest)).toString();\n log.debug(\"hashToCid\", { hexHash, hashCode, codec, cid });\n return cid;\n}\n\nif (import.meta.vitest) {\n const { describe, test, expect } = import.meta.vitest;\n\n describe(\"computeCid\", () => {\n test(\"produces known CID for known input\", () => {\n const data = new TextEncoder().encode(\"hello bulletin\");\n const cid = computeCid(data);\n expect(cid).toBe(computeCid(new TextEncoder().encode(\"hello bulletin\")));\n expect(cid).toMatch(/^b[a-z2-7]+$/);\n });\n\n test(\"deterministic — same input, same output\", () => {\n const data = new Uint8Array([1, 2, 3, 4, 5]);\n expect(computeCid(data)).toBe(computeCid(data));\n });\n\n test(\"different inputs produce different CIDs\", () => {\n const a = computeCid(new Uint8Array([1]));\n const b = computeCid(new Uint8Array([2]));\n expect(a).not.toBe(b);\n });\n\n test(\"empty input produces valid CID\", () => {\n const cid = computeCid(new Uint8Array(0));\n expect(cid).toMatch(/^b[a-z2-7]+$/);\n });\n\n test(\"matches reference implementation (manual varint)\", () => {\n const data = new TextEncoder().encode(\"test\");\n const cid = computeCid(data);\n expect(cid[0]).toBe(\"b\");\n const parsed = CID.parse(cid);\n expect(parsed.version).toBe(1);\n expect(parsed.code).toBe(CidCodec.Raw);\n });\n });\n\n describe(\"cidToPreimageKey\", () => {\n test(\"round-trips with computeCid — returns 0x-prefixed 64-char hex\", () => {\n const data = new TextEncoder().encode(\"hello bulletin\");\n const cid = computeCid(data);\n const key = cidToPreimageKey(cid);\n expect(key).toMatch(/^0x[0-9a-f]{64}$/);\n });\n\n test(\"deterministic — same CID always yields same key\", () => {\n const cid = computeCid(new Uint8Array([1, 2, 3]));\n expect(cidToPreimageKey(cid)).toBe(cidToPreimageKey(cid));\n });\n\n test(\"matches raw blake2b-256 hash\", () => {\n const data = new TextEncoder().encode(\"test\");\n const cid = computeCid(data);\n const key = cidToPreimageKey(cid);\n const hash = blake2b256(data);\n const expected = `0x${bytesToHex(hash)}`;\n expect(key).toBe(expected);\n });\n\n test(\"accepts CIDv1 with sha2-256\", () => {\n const hash = new Uint8Array(32).fill(0xcd);\n const cidV1 = CID.createV1(CidCodec.Raw, Digest.create(HashAlgorithm.Sha2_256, hash));\n const key = cidToPreimageKey(cidV1.toString());\n expect(key).toMatch(/^0x[0-9a-f]{64}$/);\n expect(key).toBe(`0x${bytesToHex(hash)}`);\n });\n\n test(\"accepts CIDv1 with keccak-256\", () => {\n const hash = new Uint8Array(32).fill(0xef);\n const cidV1 = CID.createV1(CidCodec.Raw, Digest.create(HashAlgorithm.Keccak256, hash));\n const key = cidToPreimageKey(cidV1.toString());\n expect(key).toBe(`0x${bytesToHex(hash)}`);\n });\n\n test(\"throws BulletinCidError for CIDv0 input\", () => {\n const hash = new Uint8Array(32).fill(0xab);\n const cidV0 = CID.create(0, 0x70, Digest.create(HashAlgorithm.Sha2_256, hash));\n expect(() => cidToPreimageKey(cidV0.toString())).toThrow(BulletinCidError);\n });\n\n test(\"throws BulletinCidError for CIDv1 with unsupported hash algorithm\", () => {\n const unsupportedCode = 0x99;\n const hash = new Uint8Array(32).fill(0xab);\n const cidV1 = CID.createV1(CidCodec.Raw, Digest.create(unsupportedCode, hash));\n expect(() => cidToPreimageKey(cidV1.toString())).toThrow(BulletinCidError);\n });\n });\n\n describe(\"hashToCid\", () => {\n test(\"round-trips with cidToPreimageKey — hex → CID → hex\", () => {\n const data = new TextEncoder().encode(\"hello bulletin\");\n const originalCid = computeCid(data);\n const hex = cidToPreimageKey(originalCid);\n const reconstructed = hashToCid(hex);\n expect(reconstructed).toBe(originalCid);\n });\n\n test(\"full cycle: data → CID → hex → CID\", () => {\n const data = new Uint8Array([10, 20, 30, 40, 50]);\n const cid1 = computeCid(data);\n const hex = cidToPreimageKey(cid1);\n const cid2 = hashToCid(hex);\n expect(cid2).toBe(cid1);\n });\n\n test(\"deterministic — same hex always yields same CID\", () => {\n const hex = cidToPreimageKey(computeCid(new Uint8Array([1, 2, 3])));\n expect(hashToCid(hex)).toBe(hashToCid(hex));\n });\n\n test(\"produces valid base32-lower CIDv1 (default: blake2b-256, raw)\", () => {\n const hex = cidToPreimageKey(computeCid(new TextEncoder().encode(\"test\")));\n const cid = hashToCid(hex);\n expect(cid).toMatch(/^b[a-z2-7]+$/);\n const parsed = CID.parse(cid);\n expect(parsed.version).toBe(1);\n expect(parsed.code).toBe(CidCodec.Raw);\n expect(parsed.multihash.code).toBe(HashAlgorithm.Blake2b256);\n });\n\n test(\"sha2-256 produces different CID from blake2b-256 for same hash\", () => {\n const hex = `0x${\"ab\".repeat(32)}` as `0x${string}`;\n const blake = hashToCid(hex, HashAlgorithm.Blake2b256);\n const sha = hashToCid(hex, HashAlgorithm.Sha2_256);\n expect(blake).not.toBe(sha);\n // Both should be valid CIDv1\n expect(CID.parse(blake).version).toBe(1);\n expect(CID.parse(sha).version).toBe(1);\n });\n\n test(\"sha2-256 round-trips through cidToPreimageKey\", () => {\n const hex = `0x${\"cd\".repeat(32)}` as `0x${string}`;\n const cid = hashToCid(hex, HashAlgorithm.Sha2_256);\n const extracted = cidToPreimageKey(cid);\n expect(extracted).toBe(hex);\n });\n\n test(\"keccak-256 round-trips through cidToPreimageKey\", () => {\n const hex = `0x${\"ef\".repeat(32)}` as `0x${string}`;\n const cid = hashToCid(hex, HashAlgorithm.Keccak256);\n const extracted = cidToPreimageKey(cid);\n expect(extracted).toBe(hex);\n });\n\n test(\"dag-pb codec produces different CID from raw for same hash\", () => {\n const hex = `0x${\"ab\".repeat(32)}` as `0x${string}`;\n const rawCid = hashToCid(hex, HashAlgorithm.Blake2b256, CidCodec.Raw);\n const dagPbCid = hashToCid(hex, HashAlgorithm.Blake2b256, CidCodec.DagPb);\n expect(rawCid).not.toBe(dagPbCid);\n expect(CID.parse(dagPbCid).code).toBe(CidCodec.DagPb);\n });\n\n test(\"dag-cbor codec works\", () => {\n const hex = `0x${\"ab\".repeat(32)}` as `0x${string}`;\n const cid = hashToCid(hex, HashAlgorithm.Blake2b256, CidCodec.DagCbor);\n expect(CID.parse(cid).code).toBe(CidCodec.DagCbor);\n });\n\n test(\"throws BulletinCidError for hex that is too short\", () => {\n expect(() => hashToCid(\"0xabcd\" as `0x${string}`)).toThrow(BulletinCidError);\n });\n\n test(\"throws BulletinCidError for hex that is too long\", () => {\n const tooLong = `0x${\"aa\".repeat(33)}` as `0x${string}`;\n expect(() => hashToCid(tooLong)).toThrow(BulletinCidError);\n });\n\n test(\"throws for non-hex characters\", () => {\n const bad = `0x${\"zz\".repeat(32)}` as `0x${string}`;\n expect(() => hashToCid(bad)).toThrow();\n });\n\n test(\"throws BulletinCidError for unsupported hash algorithm\", () => {\n const hex = `0x${\"ab\".repeat(32)}` as `0x${string}`;\n expect(() => hashToCid(hex, 0x99 as HashAlgorithm)).toThrow(BulletinCidError);\n });\n\n test(\"throws BulletinCidError for unsupported codec\", () => {\n const hex = `0x${\"ab\".repeat(32)}` as `0x${string}`;\n expect(() => hashToCid(hex, HashAlgorithm.Blake2b256, 0x99 as CidCodec)).toThrow(\n BulletinCidError,\n );\n });\n });\n}\n","import { BulletinGatewayFetchError, BulletinGatewayUnavailableError } from \"./errors.js\";\nimport type { Environment, FetchOptions } from \"./types.js\";\n\n/** Add entries here as bulletin gateways go live on each network. */\nconst GATEWAYS: Partial<Record<Environment, string>> = {\n paseo: \"https://paseo-ipfs.polkadot.io/ipfs/\",\n};\n\nconst DEFAULT_FETCH_TIMEOUT_MS = 30_000;\n\n/**\n * Get the IPFS gateway URL for an environment.\n * @throws {BulletinGatewayUnavailableError} If the network doesn't have a live gateway yet.\n */\nexport function getGateway(env: Environment): string {\n const gw = GATEWAYS[env];\n if (!gw) {\n throw new BulletinGatewayUnavailableError(env);\n }\n return gw;\n}\n\n/** Build the full gateway URL for a CID. */\nexport function gatewayUrl(cid: string, gateway: string): string {\n return `${gateway}${cid}`;\n}\n\n/** Check if a CID exists on the gateway (HEAD request). Returns false on any error or timeout. */\nexport async function cidExists(\n cid: string,\n gateway: string,\n options?: FetchOptions,\n): Promise<boolean> {\n const timeoutMs = options?.timeoutMs ?? DEFAULT_FETCH_TIMEOUT_MS;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n try {\n const response = await fetch(gatewayUrl(cid, gateway), {\n method: \"HEAD\",\n signal: controller.signal,\n });\n return response.ok;\n } catch {\n return false;\n } finally {\n clearTimeout(timer);\n }\n}\n\n/**\n * Fetch raw bytes from the gateway.\n * @throws {BulletinGatewayFetchError} If the gateway returns a non-OK response.\n */\nexport async function fetchBytes(\n cid: string,\n gateway: string,\n options?: FetchOptions,\n): Promise<Uint8Array> {\n const timeoutMs = options?.timeoutMs ?? DEFAULT_FETCH_TIMEOUT_MS;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n\n try {\n const response = await fetch(gatewayUrl(cid, gateway), { signal: controller.signal });\n if (!response.ok) {\n throw new BulletinGatewayFetchError(cid, response.status, response.statusText);\n }\n return new Uint8Array(await response.arrayBuffer());\n } finally {\n clearTimeout(timer);\n }\n}\n\n/** Fetch and parse JSON from the gateway. */\nexport async function fetchJson<T>(\n cid: string,\n gateway: string,\n options?: FetchOptions,\n): Promise<T> {\n const bytes = await fetchBytes(cid, gateway, options);\n return JSON.parse(new TextDecoder().decode(bytes)) as T;\n}\n\nif (import.meta.vitest) {\n const { describe, test, expect, vi, afterEach } = import.meta.vitest;\n\n afterEach(() => {\n vi.restoreAllMocks();\n });\n\n describe(\"getGateway\", () => {\n test(\"returns known URL for paseo\", () => {\n const gw = getGateway(\"paseo\");\n expect(gw).toMatch(/^https:\\/\\//);\n expect(gw).toMatch(/\\/ipfs\\/$/);\n });\n\n test(\"throws BulletinGatewayUnavailableError for environments without a live gateway\", () => {\n expect(() => getGateway(\"polkadot\")).toThrow(BulletinGatewayUnavailableError);\n expect(() => getGateway(\"kusama\")).toThrow(BulletinGatewayUnavailableError);\n });\n });\n\n describe(\"gatewayUrl\", () => {\n test(\"concatenates gateway and CID\", () => {\n expect(gatewayUrl(\"bafyabc\", \"https://gw.example/ipfs/\")).toBe(\n \"https://gw.example/ipfs/bafyabc\",\n );\n });\n });\n\n describe(\"cidExists\", () => {\n test(\"returns true for 200 response\", async () => {\n vi.stubGlobal(\"fetch\", vi.fn().mockResolvedValue({ ok: true }));\n expect(await cidExists(\"bafyabc\", \"https://gw/ipfs/\")).toBe(true);\n });\n\n test(\"returns false for 404 response\", async () => {\n vi.stubGlobal(\"fetch\", vi.fn().mockResolvedValue({ ok: false, status: 404 }));\n expect(await cidExists(\"bafyabc\", \"https://gw/ipfs/\")).toBe(false);\n });\n\n test(\"returns false on network error\", async () => {\n vi.stubGlobal(\"fetch\", vi.fn().mockRejectedValue(new Error(\"network\")));\n expect(await cidExists(\"bafyabc\", \"https://gw/ipfs/\")).toBe(false);\n });\n\n test(\"returns false on timeout\", async () => {\n vi.stubGlobal(\n \"fetch\",\n vi.fn().mockImplementation(\n (_url: string, init: { signal: AbortSignal }) =>\n new Promise((_resolve, reject) => {\n init.signal.addEventListener(\"abort\", () =>\n reject(new DOMException(\"aborted\", \"AbortError\")),\n );\n }),\n ),\n );\n expect(await cidExists(\"bafyabc\", \"https://gw/ipfs/\", { timeoutMs: 10 })).toBe(false);\n });\n });\n\n describe(\"fetchBytes\", () => {\n test(\"returns bytes from response\", async () => {\n const payload = new Uint8Array([1, 2, 3]);\n vi.stubGlobal(\n \"fetch\",\n vi.fn().mockResolvedValue({\n ok: true,\n arrayBuffer: () => Promise.resolve(payload.buffer),\n }),\n );\n const result = await fetchBytes(\"bafyabc\", \"https://gw/ipfs/\");\n expect(result).toEqual(payload);\n });\n\n test(\"throws BulletinGatewayFetchError on non-ok response\", async () => {\n vi.stubGlobal(\n \"fetch\",\n vi.fn().mockResolvedValue({\n ok: false,\n status: 500,\n statusText: \"Internal Server Error\",\n }),\n );\n const err = await fetchBytes(\"bafyabc\", \"https://gw/ipfs/\").catch((e) => e);\n expect(err).toBeInstanceOf(BulletinGatewayFetchError);\n expect(err.cid).toBe(\"bafyabc\");\n expect(err.status).toBe(500);\n });\n\n test(\"throws on timeout\", async () => {\n vi.stubGlobal(\n \"fetch\",\n vi.fn().mockImplementation(\n (_url: string, init: { signal: AbortSignal }) =>\n new Promise((_resolve, reject) => {\n init.signal.addEventListener(\"abort\", () =>\n reject(new DOMException(\"aborted\", \"AbortError\")),\n );\n }),\n ),\n );\n await expect(\n fetchBytes(\"bafyabc\", \"https://gw/ipfs/\", { timeoutMs: 10 }),\n ).rejects.toThrow();\n });\n });\n\n describe(\"fetchJson\", () => {\n test(\"parses JSON from response\", async () => {\n const obj = { name: \"test\", value: 42 };\n const bytes = new TextEncoder().encode(JSON.stringify(obj));\n vi.stubGlobal(\n \"fetch\",\n vi.fn().mockResolvedValue({\n ok: true,\n arrayBuffer: () => Promise.resolve(bytes.buffer),\n }),\n );\n const result = await fetchJson<{ name: string; value: number }>(\n \"bafyabc\",\n \"https://gw/ipfs/\",\n );\n expect(result).toEqual(obj);\n });\n });\n}\n","import { createLogger } from \"@parity/product-sdk-logger\";\n\nimport { resolveQueryStrategy, type QueryStrategy } from \"./resolve-query.js\";\nimport type { QueryOptions } from \"./types.js\";\n\nconst log = createLogger(\"bulletin\");\n\n/**\n * Fetch raw bytes for a CID using the host preimage lookup.\n *\n * Uses local cache + managed IPFS polling via the host container.\n *\n * @param cid - CIDv1 string to fetch.\n * @param options - Query options (lookupTimeoutMs for host).\n * @returns Raw bytes of the content.\n * @throws {Error} If the host preimage API is unavailable.\n */\nexport async function queryBytes(cid: string, options?: QueryOptions): Promise<Uint8Array> {\n const strategy = await resolveQueryStrategy();\n return executeQuery(strategy, cid, options);\n}\n\n/**\n * Fetch and parse JSON for a CID, auto-resolving the query path.\n *\n * Delegates to {@link queryBytes} and parses the result as JSON.\n *\n * @param cid - CIDv1 string to fetch.\n * @param options - Query options.\n * @returns Parsed JSON value.\n * @throws {Error} If the host preimage API is unavailable.\n */\nexport async function queryJson<T>(cid: string, options?: QueryOptions): Promise<T> {\n const bytes = await queryBytes(cid, options);\n return JSON.parse(new TextDecoder().decode(bytes)) as T;\n}\n\n/**\n * Execute a query using a pre-resolved strategy.\n *\n * Exposed so that {@link BulletinClient} can resolve the strategy once and\n * reuse it across multiple calls without re-detecting the environment.\n *\n * @param strategy - Pre-resolved query strategy.\n * @param cid - CIDv1 string to fetch.\n * @param options - Query options.\n * @returns Raw bytes of the content.\n */\nexport async function executeQuery(\n strategy: QueryStrategy,\n cid: string,\n options?: QueryOptions,\n): Promise<Uint8Array> {\n log.info(\"querying via host preimage lookup\", { cid });\n return strategy.lookup(cid, options?.lookupTimeoutMs);\n}\n\nif (import.meta.vitest) {\n const { describe, test, expect, vi } = import.meta.vitest;\n\n // Note: queryBytes and queryJson tests require e2e testing as they\n // depend on the host container environment for strategy resolution.\n\n describe(\"executeQuery\", () => {\n const testData = new Uint8Array([1, 2, 3]);\n\n test(\"executes host-lookup strategy\", async () => {\n const lookup = vi.fn().mockResolvedValue(testData);\n const strategy: QueryStrategy = { kind: \"host-lookup\", lookup };\n const result = await executeQuery(strategy, \"bafytest\");\n expect(result).toBe(testData);\n expect(lookup).toHaveBeenCalledWith(\"bafytest\", undefined);\n });\n\n test(\"passes lookupTimeoutMs to host-lookup\", async () => {\n const lookup = vi.fn().mockResolvedValue(testData);\n const strategy: QueryStrategy = { kind: \"host-lookup\", lookup };\n await executeQuery(strategy, \"bafytest\", { lookupTimeoutMs: 5000 });\n expect(lookup).toHaveBeenCalledWith(\"bafytest\", 5000);\n });\n });\n}\n","import { getPreimageManager, type PreimageManager } from \"@parity/product-sdk-host\";\nimport { createLogger } from \"@parity/product-sdk-logger\";\n\nimport { cidToPreimageKey, computeCid } from \"./cid.js\";\nimport {\n BulletinHostUnavailableError,\n BulletinLookupInterruptedError,\n BulletinLookupTimeoutError,\n} from \"./errors.js\";\n\nconst log = createLogger(\"bulletin\");\n\nconst DEFAULT_LOOKUP_TIMEOUT_MS = 30_000;\n\n/**\n * Query strategy for the Bulletin Chain.\n *\n * The host manages the lookup via its preimage subscription API,\n * which includes local caching and managed IPFS polling.\n */\nexport interface QueryStrategy {\n kind: \"host-lookup\";\n lookup: (cid: string, timeoutMs?: number) => Promise<Uint8Array>;\n}\n\n/**\n * Determine the query strategy for the Bulletin Chain.\n *\n * Uses the host preimage lookup API which caches results and manages\n * IPFS polling automatically.\n *\n * @returns The resolved query strategy.\n * @throws {BulletinHostUnavailableError} If the host preimage manager is unavailable.\n */\nexport async function resolveQueryStrategy(): Promise<QueryStrategy> {\n const preimageManager = await getPreimageManager();\n if (preimageManager) {\n log.info(\"using host preimage lookup for bulletin queries\");\n return {\n kind: \"host-lookup\",\n lookup: (cid, timeoutMs) => lookupViaHost(preimageManager, cid, timeoutMs),\n };\n }\n\n throw new BulletinHostUnavailableError(\"query\");\n}\n\n/**\n * Wrap `preimageManager.lookup` (subscription-based) into a one-shot Promise.\n *\n * Converts the CID to a hex preimage key, subscribes, and resolves on the\n * first non-null callback. Rejects on timeout or if the host interrupts the\n * subscription (e.g. after repeated failures). Always unsubscribes on settlement.\n *\n * @param manager - The product-sdk preimage manager.\n * @param cid - CIDv1 string to look up.\n * @param timeoutMs - Maximum wait time. Default: 30_000ms.\n * @returns The raw bytes of the preimage.\n */\nexport function lookupViaHost(\n manager: PreimageManager,\n cid: string,\n timeoutMs: number = DEFAULT_LOOKUP_TIMEOUT_MS,\n): Promise<Uint8Array> {\n const key = cidToPreimageKey(cid);\n\n return new Promise<Uint8Array>((resolve, reject) => {\n const cleanup = () => {\n cancelInterrupt();\n sub.unsubscribe();\n };\n\n const settle = (fn: () => void) => {\n if (timer === null) return;\n clearTimeout(timer);\n timer = null;\n cleanup();\n fn();\n };\n\n let timer: ReturnType<typeof setTimeout> | null = setTimeout(() => {\n settle(() => {\n reject(new BulletinLookupTimeoutError(cid, timeoutMs));\n });\n }, timeoutMs);\n\n const sub = manager.lookup(key, (preimage) => {\n if (preimage !== null) {\n settle(() => resolve(preimage));\n }\n // null means \"not found yet\" — host will keep polling\n });\n\n const cancelInterrupt = sub.onInterrupt(() => {\n settle(() => {\n reject(new BulletinLookupInterruptedError(cid));\n });\n });\n });\n}\n\nif (import.meta.vitest) {\n const { describe, test, expect, vi } = import.meta.vitest;\n\n // Note: resolveQueryStrategy tests require e2e testing as they\n // depend on the host container environment.\n\n describe(\"lookupViaHost\", () => {\n function createMockManager(\n behavior: \"resolve\" | \"null-then-resolve\" | \"hang\" | \"interrupt\",\n ) {\n const unsubscribe = vi.fn();\n const cancelInterrupt = vi.fn();\n let interruptCb: VoidFunction | undefined;\n\n const lookup = vi.fn((_key: string, callback: (p: Uint8Array | null) => void) => {\n const data = new Uint8Array([10, 20, 30]);\n queueMicrotask(() => {\n if (behavior === \"resolve\") {\n callback(data);\n } else if (behavior === \"null-then-resolve\") {\n callback(null);\n queueMicrotask(() => callback(data));\n } else if (behavior === \"interrupt\") {\n interruptCb?.();\n }\n // \"hang\" does nothing\n });\n return {\n unsubscribe,\n onInterrupt: (cb: VoidFunction) => {\n interruptCb = cb;\n return cancelInterrupt;\n },\n };\n });\n\n return { lookup, unsubscribe, cancelInterrupt, submit: vi.fn() };\n }\n\n const testCid = computeCid(new TextEncoder().encode(\"test\"));\n\n test(\"resolves on first non-null callback\", async () => {\n const manager = createMockManager(\"resolve\");\n const result = await lookupViaHost(manager, testCid);\n expect(result).toEqual(new Uint8Array([10, 20, 30]));\n });\n\n test(\"ignores null callbacks and resolves on subsequent data\", async () => {\n const manager = createMockManager(\"null-then-resolve\");\n const result = await lookupViaHost(manager, testCid);\n expect(result).toEqual(new Uint8Array([10, 20, 30]));\n });\n\n test(\"rejects with BulletinLookupTimeoutError on timeout\", async () => {\n const { BulletinLookupTimeoutError } = await import(\"./errors.js\");\n const manager = createMockManager(\"hang\");\n const err = await lookupViaHost(manager, testCid, 50).catch((e) => e);\n expect(err).toBeInstanceOf(BulletinLookupTimeoutError);\n expect(err.cid).toBe(testCid);\n expect(err.timeoutMs).toBe(50);\n });\n\n test(\"rejects with BulletinLookupInterruptedError on interrupt\", async () => {\n const { BulletinLookupInterruptedError } = await import(\"./errors.js\");\n const manager = createMockManager(\"interrupt\");\n const err = await lookupViaHost(manager, testCid).catch((e) => e);\n expect(err).toBeInstanceOf(BulletinLookupInterruptedError);\n expect(err.cid).toBe(testCid);\n });\n\n test(\"calls unsubscribe and cancelInterrupt on resolution\", async () => {\n const manager = createMockManager(\"resolve\");\n await lookupViaHost(manager, testCid);\n expect(manager.unsubscribe).toHaveBeenCalledOnce();\n expect(manager.cancelInterrupt).toHaveBeenCalledOnce();\n });\n\n test(\"calls unsubscribe on interrupt\", async () => {\n const manager = createMockManager(\"interrupt\");\n await lookupViaHost(manager, testCid).catch(() => {});\n expect(manager.unsubscribe).toHaveBeenCalledOnce();\n });\n\n test(\"passes correct hex key to manager\", async () => {\n const expectedKey = cidToPreimageKey(testCid);\n const manager = createMockManager(\"resolve\");\n await lookupViaHost(manager, testCid);\n expect(manager.lookup).toHaveBeenCalledWith(expectedKey, expect.any(Function));\n });\n });\n}\n","import { createLogger } from \"@parity/product-sdk-logger\";\nimport { submitAndWatch, withRetry } from \"@parity/product-sdk-tx\";\nimport type { PolkadotSigner } from \"polkadot-api\";\nimport { Binary } from \"polkadot-api\";\n\nimport { computeCid } from \"./cid.js\";\nimport { gatewayUrl } from \"./gateway.js\";\nimport { resolveUploadStrategy } from \"./resolve-signer.js\";\nimport type {\n BatchUploadItem,\n BatchUploadOptions,\n BatchUploadResult,\n BulletinApi,\n UploadOptions,\n UploadResult,\n} from \"./types.js\";\n\nconst log = createLogger(\"bulletin\");\n\n/**\n * Upload data to the Bulletin Chain.\n *\n * When a signer is provided, submits a `TransactionStorage.store` transaction\n * directly. When omitted, uses the host preimage API — the host signs and\n * submits automatically.\n *\n * Computes the CIDv1 (blake2b-256, raw codec) locally.\n *\n * @param api - Typed Bulletin Chain API.\n * @param data - Raw bytes to store.\n * @param signer - Optional signer. When omitted, uses host preimage API.\n * @param options - Upload options (gateway, timeout, waitFor, status callback).\n * @returns Upload result with CID and either blockHash or preimageKey.\n */\nexport async function upload(\n api: BulletinApi,\n data: Uint8Array,\n signer?: PolkadotSigner,\n options?: UploadOptions,\n): Promise<UploadResult> {\n const strategy = await resolveUploadStrategy(signer);\n const cid = computeCid(data);\n\n if (strategy.kind === \"preimage\") {\n log.info(\"uploading via host preimage API\", { cid, size: data.byteLength });\n const preimageKey = await strategy.submit(data);\n log.info(\"preimage submitted successfully\", { cid, preimageKey });\n return {\n kind: \"preimage\",\n cid,\n preimageKey,\n gatewayUrl: options?.gateway ? gatewayUrl(cid, options.gateway) : undefined,\n };\n }\n\n log.info(\"uploading via TransactionStorage.store\", { cid, size: data.byteLength });\n const result = await withRetry(() => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const tx = (api as any).tx.TransactionStorage.store({ data: Binary.fromBytes(data) });\n return submitAndWatch(tx, strategy.signer, {\n waitFor: options?.waitFor,\n timeoutMs: options?.timeoutMs,\n onStatus: options?.onStatus,\n });\n });\n\n log.info(\"transaction included in block\", { cid, blockHash: result.block.hash });\n return {\n kind: \"transaction\",\n cid,\n blockHash: result.block.hash,\n gatewayUrl: options?.gateway ? gatewayUrl(cid, options.gateway) : undefined,\n };\n}\n\n/**\n * Upload multiple items sequentially to the Bulletin Chain.\n *\n * Bulletin Chain requires sequential transaction submission (nonce ordering).\n * Individual failures are captured in results — the batch does not abort.\n *\n * Signer resolution follows the same rules as {@link upload}: when omitted,\n * the strategy is auto-resolved once and reused for all items.\n *\n * @param api - Typed Bulletin Chain API.\n * @param items - Array of items to upload, each with data and a label.\n * @param signer - Optional signer. When omitted, auto-resolved.\n * @param options - Batch upload options (gateway, timeout, progress callback).\n * @returns Array of results, one per item, preserving input order.\n */\nexport async function batchUpload(\n api: BulletinApi,\n items: BatchUploadItem[],\n signer?: PolkadotSigner,\n options?: BatchUploadOptions,\n): Promise<BatchUploadResult[]> {\n if (items.length === 0) return [];\n\n const strategy = await resolveUploadStrategy(signer);\n const results: BatchUploadResult[] = [];\n\n for (let i = 0; i < items.length; i++) {\n const item = items[i]!;\n const cid = computeCid(item.data);\n\n try {\n if (strategy.kind === \"preimage\") {\n log.info(\"batch: uploading item via preimage\", {\n label: item.label,\n index: i,\n total: items.length,\n });\n const preimageKey = await strategy.submit(item.data);\n\n const entry: BatchUploadResult = {\n kind: \"preimage\",\n label: item.label,\n cid,\n success: true,\n preimageKey,\n gatewayUrl: options?.gateway ? gatewayUrl(cid, options.gateway) : undefined,\n };\n results.push(entry);\n options?.onProgress?.(i + 1, items.length, entry);\n } else {\n log.info(\"batch: uploading item via transaction\", {\n label: item.label,\n index: i,\n total: items.length,\n });\n const result = await withRetry(() => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const tx = (api as any).tx.TransactionStorage.store({\n data: Binary.fromBytes(item.data),\n });\n return submitAndWatch(tx, strategy.signer, {\n waitFor: options?.waitFor,\n timeoutMs: options?.timeoutMs,\n });\n });\n\n const entry: BatchUploadResult = {\n kind: \"transaction\",\n label: item.label,\n cid,\n success: true,\n blockHash: result.block.hash,\n gatewayUrl: options?.gateway ? gatewayUrl(cid, options.gateway) : undefined,\n };\n results.push(entry);\n options?.onProgress?.(i + 1, items.length, entry);\n }\n } catch (err) {\n log.error(\"batch: item upload failed\", {\n label: item.label,\n index: i,\n error: err instanceof Error ? err.message : String(err),\n });\n const entry: BatchUploadResult = {\n kind: strategy.kind === \"preimage\" ? \"preimage\" : \"transaction\",\n label: item.label,\n cid,\n success: false,\n error: err instanceof Error ? err.message : String(err),\n };\n results.push(entry);\n options?.onProgress?.(i + 1, items.length, entry);\n }\n }\n\n return results;\n}\n\nif (import.meta.vitest) {\n const { describe, test, expect, vi } = import.meta.vitest;\n\n function createMockApi() {\n return {\n tx: {\n TransactionStorage: {\n store: vi.fn().mockReturnValue({\n signSubmitAndWatch: () => ({\n subscribe: (handlers: { next: (e: unknown) => void }) => {\n queueMicrotask(() => {\n handlers.next({ type: \"signed\", txHash: \"0xtxhash\" });\n handlers.next({\n type: \"txBestBlocksState\",\n txHash: \"0xtxhash\",\n found: true,\n ok: true,\n block: { hash: \"0xblockhash\", number: 1, index: 0 },\n events: [],\n });\n });\n return { unsubscribe: vi.fn() };\n },\n }),\n }),\n },\n },\n };\n }\n\n const mockSigner = {} as PolkadotSigner;\n\n describe(\"upload\", () => {\n test(\"calls TransactionStorage.store and returns CID + blockHash with explicit signer\", async () => {\n const api = createMockApi();\n const data = new TextEncoder().encode(\"test data\");\n const result = await upload(api as unknown as BulletinApi, data, mockSigner);\n\n expect(api.tx.TransactionStorage.store).toHaveBeenCalledOnce();\n expect(result.kind).toBe(\"transaction\");\n expect(result.cid).toBeTruthy();\n if (result.kind === \"transaction\") {\n expect(result.blockHash).toBe(\"0xblockhash\");\n }\n });\n\n test(\"includes gatewayUrl when gateway option provided\", async () => {\n const api = createMockApi();\n const data = new TextEncoder().encode(\"test\");\n const result = await upload(api as unknown as BulletinApi, data, mockSigner, {\n gateway: \"https://gw/ipfs/\",\n });\n\n expect(result.gatewayUrl).toBe(`https://gw/ipfs/${result.cid}`);\n });\n\n test(\"omits gatewayUrl when no gateway option\", async () => {\n const api = createMockApi();\n const data = new TextEncoder().encode(\"test\");\n const result = await upload(api as unknown as BulletinApi, data, mockSigner);\n\n expect(result.gatewayUrl).toBeUndefined();\n });\n });\n\n describe(\"batchUpload\", () => {\n test(\"returns empty array for empty items\", async () => {\n const api = createMockApi();\n const results = await batchUpload(api as unknown as BulletinApi, [], mockSigner);\n expect(results).toEqual([]);\n });\n\n test(\"processes items sequentially with explicit signer\", async () => {\n const api = createMockApi();\n const items: BatchUploadItem[] = [\n { data: new TextEncoder().encode(\"a\"), label: \"file-a\" },\n { data: new TextEncoder().encode(\"b\"), label: \"file-b\" },\n ];\n const results = await batchUpload(api as unknown as BulletinApi, items, mockSigner);\n\n expect(results).toHaveLength(2);\n expect(results[0]!.kind).toBe(\"transaction\");\n expect(results[0]!.label).toBe(\"file-a\");\n expect(results[0]!.success).toBe(true);\n expect(results[1]!.kind).toBe(\"transaction\");\n expect(results[1]!.label).toBe(\"file-b\");\n expect(results[1]!.success).toBe(true);\n });\n\n test(\"captures individual failures without aborting batch\", async () => {\n const api = createMockApi();\n // Make the second call fail with a dispatch error (non-retryable)\n let callCount = 0;\n api.tx.TransactionStorage.store.mockImplementation(() => {\n callCount++;\n if (callCount === 2) {\n return {\n signSubmitAndWatch: () => ({\n subscribe: (handlers: { next: (e: unknown) => void }) => {\n queueMicrotask(() => {\n handlers.next({ type: \"signed\", txHash: \"0x\" });\n handlers.next({\n type: \"txBestBlocksState\",\n txHash: \"0x\",\n found: true,\n ok: false,\n block: { hash: \"0xblock\", number: 1, index: 0 },\n events: [],\n dispatchError: { type: \"BadOrigin\" },\n });\n });\n return { unsubscribe: vi.fn() };\n },\n }),\n };\n }\n return {\n signSubmitAndWatch: () => ({\n subscribe: (handlers: { next: (e: unknown) => void }) => {\n queueMicrotask(() => {\n handlers.next({ type: \"signed\", txHash: \"0x\" });\n handlers.next({\n type: \"txBestBlocksState\",\n txHash: \"0x\",\n found: true,\n ok: true,\n block: { hash: \"0xblock\", number: 1, index: 0 },\n events: [],\n });\n });\n return { unsubscribe: vi.fn() };\n },\n }),\n };\n });\n\n const items: BatchUploadItem[] = [\n { data: new TextEncoder().encode(\"a\"), label: \"ok\" },\n { data: new TextEncoder().encode(\"b\"), label: \"fail\" },\n { data: new TextEncoder().encode(\"c\"), label: \"ok2\" },\n ];\n const results = await batchUpload(api as unknown as BulletinApi, items, mockSigner);\n\n expect(results).toHaveLength(3);\n expect(results[0]!.kind).toBe(\"transaction\");\n expect(results[0]!.success).toBe(true);\n const f1 = results[1]!;\n expect(f1.kind).toBe(\"transaction\");\n expect(f1.success).toBe(false);\n if (!f1.success) expect(f1.error).toContain(\"BadOrigin\");\n expect(results[2]!.success).toBe(true);\n });\n\n test(\"calls onProgress for each item\", async () => {\n const api = createMockApi();\n const items: BatchUploadItem[] = [\n { data: new TextEncoder().encode(\"a\"), label: \"a\" },\n { data: new TextEncoder().encode(\"b\"), label: \"b\" },\n ];\n const progress: Array<[number, number, string]> = [];\n await batchUpload(api as unknown as BulletinApi, items, mockSigner, {\n onProgress: (done, total, current) => progress.push([done, total, current.label]),\n });\n\n expect(progress).toEqual([\n [1, 2, \"a\"],\n [2, 2, \"b\"],\n ]);\n });\n });\n}\n","import { getPreimageManager } from \"@parity/product-sdk-host\";\nimport { createLogger } from \"@parity/product-sdk-logger\";\nimport type { PolkadotSigner } from \"polkadot-api\";\n\nimport { BulletinHostUnavailableError } from \"./errors.js\";\n\nconst log = createLogger(\"bulletin\");\n\n/**\n * Discriminated union describing how data will be uploaded to the Bulletin Chain.\n *\n * - `\"preimage\"` — the host handles signing and chain submission via its preimage API.\n * - `\"signer\"` — a `TransactionStorage.store` transaction is signed and submitted directly.\n */\nexport type UploadStrategy =\n | { kind: \"preimage\"; submit: (data: Uint8Array) => Promise<string> }\n | { kind: \"signer\"; signer: PolkadotSigner };\n\n/**\n * Determine the upload strategy for the Bulletin Chain.\n *\n * Resolution order:\n * 1. If an explicit signer is provided, use it directly.\n * 2. Otherwise, use the host preimage API (the SDK is designed to run inside a container).\n *\n * @param explicitSigner - Optional signer provided by the caller. When present,\n * skips host auto-detection entirely.\n * @returns The resolved upload strategy.\n * @throws {Error} If no signer is provided and the host preimage API is unavailable.\n */\nexport async function resolveUploadStrategy(\n explicitSigner?: PolkadotSigner,\n): Promise<UploadStrategy> {\n if (explicitSigner) {\n log.debug(\"using explicit signer provided by caller\");\n return { kind: \"signer\", signer: explicitSigner };\n }\n\n // Use the host preimage API (inside container)\n const preimageManager = await getPreimageManager();\n if (preimageManager) {\n log.info(\"using host preimage API for bulletin upload\");\n return { kind: \"preimage\", submit: (data) => preimageManager.submit(data) };\n }\n\n throw new BulletinHostUnavailableError(\"upload\");\n}\n\nif (import.meta.vitest) {\n const { describe, test, expect, vi } = import.meta.vitest;\n\n describe(\"resolveUploadStrategy\", () => {\n test(\"returns explicit signer when provided\", async () => {\n const signer = { publicKey: new Uint8Array(32) } as PolkadotSigner;\n const strategy = await resolveUploadStrategy(signer);\n expect(strategy.kind).toBe(\"signer\");\n if (strategy.kind === \"signer\") {\n expect(strategy.signer).toBe(signer);\n }\n });\n\n // Note: Tests for host preimage manager integration require e2e testing\n // as they depend on the actual host container environment.\n // The explicit signer path above validates the core logic.\n });\n}\n"],"mappings":"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","names":["createMockApi","createLogger","log","createLogger","createLogger","createLogger","log","createLogger","computeCid","BulletinLookupTimeoutError","BulletinLookupInterruptedError","log","createLogger","createLogger","getPreimageManager","createLogger","log","createLogger","getPreimageManager","log","createLogger","createMockApi","cidToPreimageKey"]}
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@parity/product-sdk-bulletin",
3
+ "version": "0.1.0",
4
+ "description": "TypeScript SDK for uploading and retrieving data on the Polkadot Bulletin Chain",
5
+ "type": "module",
6
+ "sideEffects": false,
7
+ "main": "./dist/index.js",
8
+ "module": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "import": "./dist/index.js",
13
+ "types": "./dist/index.d.ts"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "src"
19
+ ],
20
+ "dependencies": {
21
+ "multiformats": "^13.3.0",
22
+ "polkadot-api": "^1.9.0",
23
+ "@parity/product-sdk-chain-client": "0.1.0",
24
+ "@parity/product-sdk-crypto": "0.1.0",
25
+ "@parity/product-sdk-host": "0.1.0",
26
+ "@parity/product-sdk-logger": "0.1.0",
27
+ "@parity/product-sdk-tx": "0.1.0"
28
+ },
29
+ "devDependencies": {
30
+ "typescript": "^5.7.0",
31
+ "vitest": "^3.0.0"
32
+ },
33
+ "license": "Apache-2.0",
34
+ "scripts": {
35
+ "build": "tsup",
36
+ "test": "vitest",
37
+ "clean": "rm -rf dist"
38
+ }
39
+ }
@@ -0,0 +1,191 @@
1
+ import { createLogger } from "@parity/product-sdk-logger";
2
+ import { Enum } from "polkadot-api";
3
+
4
+ import { BulletinAuthorizationError } from "./errors.js";
5
+ import type { AuthorizationStatus, BulletinApi } from "./types.js";
6
+
7
+ const log = createLogger("bulletin");
8
+
9
+ const NOT_AUTHORIZED: AuthorizationStatus = Object.freeze({
10
+ authorized: false,
11
+ remainingTransactions: 0,
12
+ remainingBytes: 0n,
13
+ expiration: 0,
14
+ });
15
+
16
+ /**
17
+ * Check whether an account is authorized to store data on the Bulletin Chain.
18
+ *
19
+ * Queries `TransactionStorage.Authorizations` for the given address and returns
20
+ * the raw authorization quota. Use this as a pre-flight check before calling
21
+ * {@link upload} to provide clear UX ("not authorized" / "insufficient quota")
22
+ * instead of letting the transaction fail mid-execution.
23
+ *
24
+ * The expiration block number is returned as-is — the chain enforces expiration
25
+ * at submission time, so callers can optionally compare against the current
26
+ * block for display purposes.
27
+ *
28
+ * @param api - Typed Bulletin Chain API instance.
29
+ * @param address - SS58-encoded account address to check.
30
+ * @returns Authorization status with remaining quota.
31
+ *
32
+ * @example
33
+ * ```ts
34
+ * import { checkAuthorization } from "@parity/product-sdk-bulletin";
35
+ *
36
+ * const auth = await checkAuthorization(api, address);
37
+ * if (!auth.authorized) {
38
+ * console.error("Account is not authorized for bulletin storage");
39
+ * } else if (auth.remainingBytes < BigInt(fileBytes.length)) {
40
+ * console.error(`Insufficient quota: ${auth.remainingBytes} bytes remaining`);
41
+ * }
42
+ * ```
43
+ *
44
+ * @see {@link BulletinClient.checkAuthorization} for the client method equivalent.
45
+ */
46
+ export async function checkAuthorization(
47
+ api: BulletinApi,
48
+ address: string,
49
+ ): Promise<AuthorizationStatus> {
50
+ let auth;
51
+ try {
52
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
53
+ auth = await (api as any).query.TransactionStorage.Authorizations.getValue(
54
+ Enum("Account", address),
55
+ );
56
+ } catch (error) {
57
+ log.error("checkAuthorization: query failed", { address, error });
58
+ throw new BulletinAuthorizationError(address, error);
59
+ }
60
+
61
+ if (!auth) {
62
+ log.debug("checkAuthorization: no authorization found", { address });
63
+ return NOT_AUTHORIZED;
64
+ }
65
+
66
+ const status: AuthorizationStatus = {
67
+ authorized: true,
68
+ remainingTransactions: auth.extent.transactions,
69
+ remainingBytes: auth.extent.bytes,
70
+ expiration: auth.expiration,
71
+ };
72
+
73
+ log.debug("checkAuthorization", {
74
+ address,
75
+ remainingTransactions: status.remainingTransactions,
76
+ remainingBytes: status.remainingBytes.toString(),
77
+ expiration: status.expiration,
78
+ });
79
+
80
+ return status;
81
+ }
82
+
83
+ if (import.meta.vitest) {
84
+ const { describe, test, expect, vi } = import.meta.vitest;
85
+
86
+ function createMockApi(authResult: unknown) {
87
+ return {
88
+ query: {
89
+ TransactionStorage: {
90
+ Authorizations: {
91
+ getValue: vi.fn().mockResolvedValue(authResult),
92
+ },
93
+ },
94
+ },
95
+ } as unknown as BulletinApi;
96
+ }
97
+
98
+ describe("checkAuthorization", () => {
99
+ test("returns not authorized when no authorization exists", async () => {
100
+ const api = createMockApi(undefined);
101
+ const status = await checkAuthorization(api, "5GrwvaEF...");
102
+
103
+ expect(status.authorized).toBe(false);
104
+ expect(status.remainingTransactions).toBe(0);
105
+ expect(status.remainingBytes).toBe(0n);
106
+ expect(status.expiration).toBe(0);
107
+ });
108
+
109
+ test("returns authorization with full quota", async () => {
110
+ const api = createMockApi({
111
+ extent: { transactions: 10, bytes: 1_000_000n },
112
+ expiration: 999,
113
+ });
114
+ const status = await checkAuthorization(api, "5GrwvaEF...");
115
+
116
+ expect(status.authorized).toBe(true);
117
+ expect(status.remainingTransactions).toBe(10);
118
+ expect(status.remainingBytes).toBe(1_000_000n);
119
+ expect(status.expiration).toBe(999);
120
+ });
121
+
122
+ test("returns authorization with zero transactions remaining", async () => {
123
+ const api = createMockApi({
124
+ extent: { transactions: 0, bytes: 1_000_000n },
125
+ expiration: 999,
126
+ });
127
+ const status = await checkAuthorization(api, "5GrwvaEF...");
128
+
129
+ expect(status.authorized).toBe(true);
130
+ expect(status.remainingTransactions).toBe(0);
131
+ });
132
+
133
+ test("returns authorization with zero bytes remaining", async () => {
134
+ const api = createMockApi({
135
+ extent: { transactions: 5, bytes: 0n },
136
+ expiration: 999,
137
+ });
138
+ const status = await checkAuthorization(api, "5GrwvaEF...");
139
+
140
+ expect(status.authorized).toBe(true);
141
+ expect(status.remainingBytes).toBe(0n);
142
+ });
143
+
144
+ test("preserves expiration block number", async () => {
145
+ const api = createMockApi({
146
+ extent: { transactions: 1, bytes: 500n },
147
+ expiration: 12345,
148
+ });
149
+ const status = await checkAuthorization(api, "5GrwvaEF...");
150
+
151
+ expect(status.expiration).toBe(12345);
152
+ });
153
+
154
+ test("throws BulletinAuthorizationError when query fails", async () => {
155
+ const api = {
156
+ query: {
157
+ TransactionStorage: {
158
+ Authorizations: {
159
+ getValue: vi.fn().mockRejectedValue(new Error("RPC connection lost")),
160
+ },
161
+ },
162
+ },
163
+ } as unknown as BulletinApi;
164
+
165
+ const err = await checkAuthorization(api, "5GrwvaEF...").catch((e: unknown) => e);
166
+ expect(err).toBeInstanceOf(BulletinAuthorizationError);
167
+ const error = err as BulletinAuthorizationError;
168
+ expect(error.address).toBe("5GrwvaEF...");
169
+ expect(error.cause).toBeInstanceOf(Error);
170
+ expect((error.cause as Error).message).toBe("RPC connection lost");
171
+ });
172
+
173
+ test("passes correct Enum key to the query", async () => {
174
+ const getValue = vi.fn().mockResolvedValue(undefined);
175
+ const api = {
176
+ query: {
177
+ TransactionStorage: {
178
+ Authorizations: { getValue },
179
+ },
180
+ },
181
+ } as unknown as BulletinApi;
182
+
183
+ await checkAuthorization(api, "5GrwvaEF...");
184
+
185
+ expect(getValue).toHaveBeenCalledTimes(1);
186
+ const arg = getValue.mock.calls[0][0];
187
+ expect(arg.type).toBe("Account");
188
+ expect(arg.value).toBe("5GrwvaEF...");
189
+ });
190
+ });
191
+ }