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
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,6 @@ The Token API provides access to onchain NFT and fungible token data, including
| `LARGE_QUERIES_ROWS_TRIGGER` | Row threshold for large-query metrics | `10000000` | No |
| `LARGE_QUERIES_BYTES_TRIGGER` | Byte threshold for large-query metrics | `1000000000` | No |
| `SKIP_NETWORKS_VALIDATION` | Skip startup validation that configured networks exist in ClickHouse | `false` | No |
| `PLANS` | Plan limits as `name:limit,batched,intervals` entries | empty (disabled) | No |
| `PRETTY_LOGGING` | Enable pretty console logging | `false` | No |
| `VERBOSE` | Enable verbose logging | `false` | No |

Expand Down
8 changes: 1 addition & 7 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,7 @@ const app = new Hono();

// Tracking all incoming requests
app.use(async (c: Context, next) => {
const pathname = c.req.path;
logger.trace(`Incoming request: '${pathname}'`);

// Set `X-Plan` to free by default if none received
// This will have no effect unless `config.plans` is setup through ENV or CLI
if (!c.req.header('X-Plan')) c.req.raw.headers.set('X-Plan', 'free');

logger.trace(`Incoming request: '${c.req.path}'`);
await next();
});

Expand Down
81 changes: 0 additions & 81 deletions src/config.spec.ts

This file was deleted.

67 changes: 0 additions & 67 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,6 @@ export const DEFAULT_SKIP_NETWORKS_VALIDATION = false;
export const DEFAULT_CACHE_SERVER_MAX_AGE = 600; // s-maxage for shared/proxy caches
export const DEFAULT_CACHE_MAX_AGE = 60; // max-age for browser caches
export const DEFAULT_CACHE_STALE_WHILE_REVALIDATE = 30; // RFC 5861 stale-while-revalidate window
export const DEFAULT_PLANS = '';

// GitHub metadata
const GIT_COMMIT = (process.env.GIT_COMMIT ?? (await $`git rev-parse HEAD`.text())).replace(/\n/, '').slice(0, 7);
const GIT_DATE = (process.env.GIT_DATE ?? (await $`git log -1 --format=%cd --date=short`.text())).replace(/\n/, '');
Expand All @@ -58,65 +56,6 @@ export const APP_NAME = pkg.name;
export const APP_DESCRIPTION = pkg.description;
export const APP_VERSION = `${GIT_APP.version}+${GIT_APP.commit} (${GIT_APP.date})`;

/**
* Parse plan configuration string into a Map.
* Format: "name:limit,batched,intervals;name2:limit,batched,intervals"
* Intervals are pipe-separated: "1m|5m|15m"
* Returns null if input is empty (bypasses plan limits for local development).
*/
export function parsePlans(val: string) {
if (!val || val.trim() === '') {
return null;
}

const plans = new Map<
string,
{
maxLimit: number;
maxBatched: number;
allowedIntervals: string[];
}
>();

val.split(';').forEach((planDef) => {
const [name, limits] = planDef.split(':');
if (!name || !limits) {
throw new Error(`Malformed plan entry: "${planDef}". Skipping.`);
}

// Format: name:limit,batched,intervals
const parts = limits.split(',');
if (parts.length !== 3) {
throw new Error(`Invalid limits format for plan "${name}". Expected 3 values.`);
}

const [limit, batched, intervals] = parts;
const maxLimit = Number(limit);
const maxBatched = Number(batched);
const allowedIntervals = intervals ? intervals.split('|').filter((s) => s.length > 0) : [];

if (Number.isNaN(maxLimit) || Number.isNaN(maxBatched)) {
throw new Error(`Invalid numeric limits for plan "${name}".`);
}

plans.set(name, {
maxLimit,
maxBatched,
allowedIntervals,
});

if (!name.startsWith('tgm-')) {
plans.set(`tgm-${name.toUpperCase()}`, {
maxLimit,
maxBatched,
allowedIntervals,
});
}
});

return plans;
}

// parse command line options
const opts = program
.name(pkg.name)
Expand Down Expand Up @@ -250,11 +189,6 @@ const opts = program
.env('CACHE_STALE_WHILE_REVALIDATE')
.default(DEFAULT_CACHE_STALE_WHILE_REVALIDATE)
)
.addOption(
new Option('--plans <string>', 'Plan configurations (name:limit,batched,intervals)')
.env('PLANS')
.default(DEFAULT_PLANS)
)
.allowUnknownOption()
.allowExcessArguments()
.parse()
Expand Down Expand Up @@ -293,7 +227,6 @@ const config = z
cacheServerMaxAge: z.coerce.number().nonnegative('Cache server max-age must be non-negative'),
cacheMaxAge: z.coerce.number().nonnegative('Cache max-age must be non-negative'),
cacheStaleWhileRevalidate: z.coerce.number().nonnegative('Cache stale-while-revalidate must be non-negative'),
plans: z.string().transform(parsePlans),
})
.transform((data) => {
// Use YAML config as the authoritative source — spread all database maps dynamically
Expand Down
4 changes: 1 addition & 3 deletions src/routes/routes.perf.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -587,7 +587,7 @@ function getNetworkForChain(chain: ChainType): string {

async function fetchRoute(url: string): Promise<{ status: number; body: any; duration_ms: number }> {
const start = performance.now();
const response = await app.request(url, { headers: { 'X-Plan': 'free', 'Cache-Control': 'no-cache' } });
const response = await app.request(url, { headers: { 'Cache-Control': 'no-cache' } });
const body = await response.json();
const duration_ms = Math.round((performance.now() - start) * 100) / 100;
return { status: response.status, body, duration_ms };
Expand All @@ -613,8 +613,6 @@ const allResults: TestResult[] = [];
describe.skipIf(!DB_TESTS)('Database performance', () => {
beforeAll(async () => {
const { config } = await import('../config.js');
// Bypass plan limits for tests
(config as any).plans = null;

app = new Hono();
const routes = await import('./index.js');
Expand Down
5 changes: 1 addition & 4 deletions src/routes/routes.sql.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ const SINGLE_FILTER_ROUTE_CASES = [
] as const;

async function fetchRoute(path: string) {
const response = await app.request(path, { headers: { 'X-Plan': 'free' } });
const response = await app.request(path);
const body = await response.json();
return { response, body };
}
Expand Down Expand Up @@ -238,9 +238,6 @@ describe.skipIf(!DB_TESTS)('SQL queries', () => {
const { config } = await import('../config.js');
const { hasDatabase } = await import('../supported-routes.js');

// Bypass plan limits for DB integration tests (config.plans may be mutated by other test files)
(config as any).plans = null;

app = new Hono();
const routes = await import('./index.js');
app.route('/', routes.default);
Expand Down
4 changes: 1 addition & 3 deletions src/routes/swaps/evm.uniswap-v3.sql.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
let hasEvmDex: boolean;

async function fetchRoute(path: string) {
const response = await app.request(path, { headers: { 'X-Plan': 'free' } });
const response = await app.request(path);
const body = await response.json();
return { response, body };
}
Expand All @@ -24,8 +24,6 @@
const { hasDatabase } = await import('../../supported-routes.js');
const { default: evmSwapsRoute } = await import('./evm.js');

(config as any).plans = null;

app = new Hono();
app.route('/v1/evm/swaps', evmSwapsRoute);

Expand All @@ -40,7 +38,7 @@
`/v1/evm/swaps?network=${evmNetwork}&transaction_id=${EVM_TRANSACTION_SWAP_EXAMPLE}&limit=1`
);

expect(response.status).toBe(200);

Check failure on line 41 in src/routes/swaps/evm.uniswap-v3.sql.spec.ts

View workflow job for this annotation

GitHub Actions / bun-test-db

error: expect(received).toBe(expected)

Expected: 200 Received: 504 at <anonymous> (/home/runner/work/token-api/token-api/src/routes/swaps/evm.uniswap-v3.sql.spec.ts:41:33)
expect(body.data).toBeArray();
expect(body.data.length).toBe(1);

Expand All @@ -60,7 +58,7 @@

const { response, body } = await fetchRoute(`/v1/evm/swaps?network=${evmNetwork}&protocol=uniswap_v3&limit=5`);

expect(response.status).toBe(200);

Check failure on line 61 in src/routes/swaps/evm.uniswap-v3.sql.spec.ts

View workflow job for this annotation

GitHub Actions / bun-test-db

error: expect(received).toBe(expected)

Expected: 200 Received: 504 at <anonymous> (/home/runner/work/token-api/token-api/src/routes/swaps/evm.uniswap-v3.sql.spec.ts:61:33)
expect(body.data).toBeArray();
expect(body.data.length).toBeGreaterThan(0);

Expand All @@ -76,7 +74,7 @@
`/v1/evm/swaps?network=${evmNetwork}&protocol=uniswap_v3&transaction_id=${EVM_TRANSACTION_SWAP_EXAMPLE}&limit=1`
);

expect(response.status).toBe(200);

Check failure on line 77 in src/routes/swaps/evm.uniswap-v3.sql.spec.ts

View workflow job for this annotation

GitHub Actions / bun-test-db

error: expect(received).toBe(expected)

Expected: 200 Received: 504 at <anonymous> (/home/runner/work/token-api/token-api/src/routes/swaps/evm.uniswap-v3.sql.spec.ts:77:33)
expect(body.data).toBeArray();
expect(body.data.length).toBe(1);

Expand Down Expand Up @@ -105,7 +103,7 @@
`/v1/evm/swaps?network=${evmNetwork}&protocol=uniswap_v3&input_contract=${EVM_CONTRACT_USDC_EXAMPLE}&limit=5`
);

expect(response.status).toBe(200);

Check failure on line 106 in src/routes/swaps/evm.uniswap-v3.sql.spec.ts

View workflow job for this annotation

GitHub Actions / bun-test-db

error: expect(received).toBe(expected)

Expected: 200 Received: 504 at <anonymous> (/home/runner/work/token-api/token-api/src/routes/swaps/evm.uniswap-v3.sql.spec.ts:106:33)
expect(body.data).toBeArray();
expect(body.data.length).toBeGreaterThan(0);

Expand Down
Loading
Loading