Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 31 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,39 @@ Using bun:
$ bun add ordapi
```

## Import

Import the client and types depending on your needs:

```typescript
// Using default import
import OrdClient from 'ordapi';

const client = new OrdClient('https://ord-server.com');
const block = await client.getBlock(0);
```

```typescript
// Using both client and types
import OrdClient, { Inscription } from 'ordapi';

async function getInscription(id: string): Promise<Inscription> {
const client = new OrdClient('https://ord-server.com');
return await client.getInscription(id);
}
```

## Usage

```typescript
import { OrdClient, Block } from 'ordapi';
import OrdClient, { Block } from 'ordapi';

function App() {
const [blockInfo, setBlockInfo] = useState<Block | null>(null);

useEffect(() => {
// Create client instance
const client = new OrdClient('https://your-ord-server.xyz');
const client = new OrdClient('https://ord-server.xyz');

// Fetch genesis block info
async function fetchBlock() {
Expand All @@ -57,9 +79,13 @@ function App() {
return (
<div>
<h1>Genesis Block</h1>
<p>Height: {blockInfo.height}</p>
<p>Hash: {blockInfo.hash}</p>
<p>Number of transactions: {blockInfo.transactions.length}</p>
{blockInfo && (
<>
<p>Height: {blockInfo.height}</p>
<p>Hash: {blockInfo.hash}</p>
<p>Number of transactions: {blockInfo.transactions.length}</p>
</>
)}
</div>
);
}
Expand Down
12 changes: 6 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
{
"name": "ordapi",
"version": "0.0.2",
"module": "dist/index.js",
"type": "module",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"files": ["dist"],
"scripts": {
"build": "bun build ./src/index.ts --outdir ./dist --target node",
"build": "tsc --emitDeclarationOnly && bun build ./src/index.ts --outdir ./dist --target node",
"test": "bun test ./src/test/**/*.test.ts",
"format": "prettier --write 'src/**/*.ts'",
"lint": "eslint . && tsc -b",
"prepublishOnly": "bun run build"
},
"imports": {
"#src/*": "./src/*",
"#schemas/*": "./src/schemas/*",
"#types/*": "./src/types/*"
"exports": {
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
}
},
"dependencies": {
"typescript-eslint": "^8.22.0",
Expand Down
41 changes: 41 additions & 0 deletions src/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { OutputType } from './types';

const api = {
address: (address: string) => `/address/${address}`,
block: (heightOrHash: number | string) => `/block/${heightOrHash}`,
blockcount: '/blockcount',
blockhash: {
latest: '/blockhash',
byHeight: (height: number) => `/blockhash/${height}`,
},
blockheight: '/blockheight',
blocks: '/blocks',
blocktime: '/blocktime',
inscription: (id: string) => `/inscription/${id}`,
inscriptionChild: (id: string, child: number) =>
`/inscription/${id}/${child}`,
inscriptions: {
base: '/inscriptions',
latest: '/inscriptions',
byPage: (page: number) => `/inscriptions/${page}`,
byBlock: (height: number) => `/inscriptions/block/${height}`,
},
output: (outpoint: string) => `/output/${outpoint}`,
outputs: {
base: '/outputs',
byAddress: (address: string, type?: OutputType) => {
const base = `/outputs/${address}`;
return type ? `${base}?type=${type}` : base;
},
},
rune: (name: string) => `/rune/${name}`,
runes: {
latest: '/runes',
byPage: (page: number) => `/runes/${page}`,
},
sat: (number: number) => `/sat/${number}`,
tx: (txId: string) => `/tx/${txId}`,
status: '/status',
} as const;

export default api;
222 changes: 222 additions & 0 deletions src/client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
import { z } from 'zod';
import api from './api';
import {
BlockInfoSchema,
BlockHashSchema,
AddressInfoSchema,
BlocksResponseSchema,
InscriptionSchema,
InscriptionsResponseSchema,
OutputInfoSchema,
RuneResponseSchema,
RunesResponseSchema,
SatSchema,
ServerStatusSchema,
TransactionInfoSchema,
} from './schemas';
import type {
BlockInfo,
BlockHash,
AddressInfo,
BlocksResponse,
InscriptionInfo,
InscriptionsResponse,
OutputInfo,
RuneResponse,
RunesResponse,
SatInfo,
TransactionInfo,
ServerStatus,
OutputType,
} from './types';

type ApiResponse<T> =
| { success: true; data: T }
| { success: false; error: string };

export class OrdClient {
private headers: HeadersInit;

constructor(
private baseUrl: string,
headers: HeadersInit = {},
) {
this.headers = {
Accept: 'application/json',
...headers,
};
}

private async fetch<T extends z.ZodType>(
endpoint: string,
schema: T,
): Promise<z.infer<T>> {
const response = await fetch(`${this.baseUrl}${endpoint}`, {
headers: this.headers,
});

if (!response.ok) {
throw new Error(`API request failed: ${response.statusText}`);
}

const text = await response.text();
const result = schema.safeParse(text);

if (!result.success) {
try {
const json = JSON.parse(text);
const jsonResult = schema.safeParse(json);
if (jsonResult.success) {
return jsonResult.data;
}
} catch {}

throw new Error(`Validation error: ${result.error.message}`);
}

return result.data;
}

private async fetchPost<T extends z.ZodType, P extends object>(
endpoint: string,
payload: P,
schema: T,
): Promise<z.infer<T>> {
const response = await fetch(`${this.baseUrl}${endpoint}`, {
method: 'POST',
headers: {
...this.headers,
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
});

if (!response.ok) {
throw new Error(`API request failed: ${response.statusText}`);
}

const data = await response.json();
const result = schema.safeParse(data);

if (!result.success) {
throw new Error(`Validation error: ${result.error.message}`);
}

return result.data;
}

async getAddressInfo(address: string): Promise<AddressInfo> {
return this.fetch(api.address(address), AddressInfoSchema);
}

async getBlock(heightOrHash: number | BlockHash): Promise<BlockInfo> {
return this.fetch(api.block(heightOrHash), BlockInfoSchema);
}

async getBlockCount(): Promise<number> {
return this.fetch(api.blockcount, z.number().int().nonnegative());
}

async getBlockHashByHeight(height: number): Promise<BlockHash> {
return this.fetch(api.blockhash.byHeight(height), BlockHashSchema);
}

async getLatestBlockHash(): Promise<BlockHash> {
return this.fetch(api.blockhash.latest, BlockHashSchema);
}

async getLatestBlockHeight(): Promise<number> {
return this.fetch(api.blockheight, z.number().int().nonnegative());
}

async getLatestBlocks(): Promise<BlocksResponse> {
return this.fetch(api.blocks, BlocksResponseSchema);
}

async getLatestBlockTime(): Promise<number> {
return this.fetch(api.blocktime, z.number().int());
}

async getInscription(id: string): Promise<InscriptionInfo> {
return this.fetch(api.inscription(id), InscriptionSchema);
}

async getInscriptionChild(
id: string,
child: number,
): Promise<InscriptionInfo> {
return this.fetch(api.inscriptionChild(id, child), InscriptionSchema);
}

async getLatestInscriptions(): Promise<InscriptionsResponse> {
return this.fetch(api.inscriptions.latest, InscriptionsResponseSchema);
}

async getInscriptionsByIds(ids: string[]): Promise<InscriptionInfo[]> {
return this.fetchPost(
api.inscriptions.base,
ids,
z.array(InscriptionSchema),
);
}

async getInscriptionsByPage(page: number): Promise<InscriptionsResponse> {
return this.fetch(
api.inscriptions.byPage(page),
InscriptionsResponseSchema,
);
}

async getInscriptionsByBlock(height: number): Promise<InscriptionsResponse> {
return this.fetch(
api.inscriptions.byBlock(height),
InscriptionsResponseSchema,
);
}

async getOutput(outpoint: string): Promise<OutputInfo> {
return this.fetch(api.output(outpoint), OutputInfoSchema);
}

async getOutputs(outpoints: string[]): Promise<OutputInfo[]> {
return this.fetchPost(
api.outputs.base,
outpoints,
z.array(OutputInfoSchema),
);
}

async getOutputsByAddress(
address: string,
type?: OutputType,
): Promise<OutputInfo[]> {
return this.fetch(
api.outputs.byAddress(address, type),
z.array(OutputInfoSchema),
);
}

async getRune(name: string): Promise<RuneResponse> {
return this.fetch(api.rune(name), RuneResponseSchema);
}

async getLatestRunes(): Promise<RunesResponse> {
return this.fetch(api.runes.latest, RunesResponseSchema);
}

async getRunesByPage(page: number): Promise<RunesResponse> {
return this.fetch(api.runes.byPage(page), RunesResponseSchema);
}

async getSat(number: number): Promise<SatInfo> {
return this.fetch(api.sat(number), SatSchema);
}

async getTransaction(txId: string): Promise<TransactionInfo> {
return this.fetch(api.tx(txId), TransactionInfoSchema);
}

async getServerStatus(): Promise<ServerStatus> {
return this.fetch(api.status, ServerStatusSchema);
}
}
10 changes: 0 additions & 10 deletions src/endpoints.ts

This file was deleted.

Loading