diff --git a/apps/web/jest.config.js b/apps/web/jest.config.js index 53f8f875..8672c572 100644 --- a/apps/web/jest.config.js +++ b/apps/web/jest.config.js @@ -11,6 +11,10 @@ module.exports = { "^linkora-sdk$": "/../../packages/sdk/src/index.ts", "^@linkora/types/src/(.*)$": "/../../packages/types/src/$1", "^@linkora/types$": "/../../packages/types/src/index.ts", + // linkora-sdk's source uses ESM-style ".js" relative imports that + // resolve to sibling ".ts" files at build time; strip the extension so + // Jest's resolver falls through to moduleFileExtensions. + "^(\\.{1,2}/.*)\\.js$": "$1", }, transform: { "^.+\\.(ts|tsx)$": [ diff --git a/apps/web/src/app/profile/[address]/page.tsx b/apps/web/src/app/profile/[address]/page.tsx index d2c7c38d..455b9d3f 100644 --- a/apps/web/src/app/profile/[address]/page.tsx +++ b/apps/web/src/app/profile/[address]/page.tsx @@ -25,6 +25,7 @@ import { import { signTransaction } from "@stellar/freighter-api"; import { LinkoraClient } from "linkora-sdk"; import { buildSignAndSubmit } from "@/lib/tx"; +import { addToBlockedList, removeFromBlockedList } from "@/lib/blockedStore"; const CONTRACT_ID = process.env.NEXT_PUBLIC_CONTRACT_ID || "CDUMMY"; const RPC_URL = process.env.NEXT_PUBLIC_RPC_URL || "https://soroban-testnet.stellar.org"; diff --git a/apps/web/src/lib/tx.test.ts b/apps/web/src/lib/tx.test.ts index 8153c5b9..3ef5128c 100644 --- a/apps/web/src/lib/tx.test.ts +++ b/apps/web/src/lib/tx.test.ts @@ -1,4 +1,3 @@ -import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; import { signAndSubmitTransaction, buildSignAndSubmit } from "./tx"; import { signTransaction } from "@stellar/freighter-api"; import { @@ -6,50 +5,59 @@ import { BASE_FEE, Contract, Address, + Account, rpc as StellarRpc, } from "@stellar/stellar-sdk"; // Mock Freighter API -vi.mock("@stellar/freighter-api", () => ({ - signTransaction: vi.fn(), +jest.mock("@stellar/freighter-api", () => ({ + signTransaction: jest.fn(), })); -// Mock Stellar SDK -vi.mock("@stellar/stellar-sdk", async (importOriginal) => { - const actual = await importOriginal(); +// Mock Stellar SDK. `Server` always resolves to the same instance so that +// `new StellarRpc.Server(...)` inside tx.ts returns the object the test +// configures via `mockServer` below, instead of an unrelated fresh mock. +jest.mock("@stellar/stellar-sdk", () => { + const actual = jest.requireActual("@stellar/stellar-sdk"); + const mockServerInstance = { + getAccount: jest.fn(), + simulateTransaction: jest.fn(), + sendTransaction: jest.fn(), + getTransaction: jest.fn(), + }; + // `buildSignAndSubmit` constructs a real TransactionBuilder (`new + // TransactionBuilder(...)`), while `signAndSubmitTransaction` only calls + // the static `fromXDR`. Extend the real class so `new` keeps working, and + // override just the static method the latter needs to mock. + class MockTransactionBuilder extends actual.TransactionBuilder {} + MockTransactionBuilder.fromXDR = jest.fn(); + return { ...actual, - TransactionBuilder: { - fromXDR: vi.fn(), - }, + TransactionBuilder: MockTransactionBuilder, rpc: { - Server: vi.fn().mockImplementation(() => ({ - getAccount: vi.fn(), - simulateTransaction: vi.fn(), - sendTransaction: vi.fn(), - getTransaction: vi.fn(), - })), + Server: jest.fn(() => mockServerInstance), Api: { - isSimulationError: vi.fn(), + isSimulationError: jest.fn(), }, - assembleTransaction: vi.fn(), + assembleTransaction: jest.fn(), }, }; }); describe("Transaction Utility Functions", () => { const mockConfig = { - contractId: "CDUMMY", + contractId: "CAAQCAIBAEAQCAIBAEAQCAIBAEAQCAIBAEAQCAIBAEAQCAIBAEAQC526", rpcUrl: "https://soroban-testnet.stellar.org", networkPassphrase: "Test SDF Network ; September 2015", }; beforeEach(() => { - vi.clearAllMocks(); + jest.clearAllMocks(); }); afterEach(() => { - vi.restoreAllMocks(); + jest.restoreAllMocks(); }); describe("signAndSubmitTransaction", () => { @@ -115,9 +123,10 @@ describe("Transaction Utility Functions", () => { describe("buildSignAndSubmit", () => { it("should build, sign, and submit contract method call", async () => { - const mockAccount = { - sequence: "1234567890", - }; + const mockAccount = new Account( + "GCMEDW2SHDHZC3YKI3ZQ574VZPDDHR5SZDYPPZLQVR2V56X7UOYX7ZMO", + "1234567890" + ); const mockSimulated = { minResourceFee: "100", transactionData: null, @@ -135,8 +144,8 @@ describe("Transaction Utility Functions", () => { (mockServer.simulateTransaction as any).mockResolvedValue(mockSimulated); (StellarRpc.Api.isSimulationError as any).mockReturnValue(false); (StellarRpc.assembleTransaction as any).mockReturnValue({ - build: vi.fn().mockReturnValue({ - toXDR: vi.fn().mockReturnValue("unsigned-xdr"), + build: jest.fn().mockReturnValue({ + toXDR: jest.fn().mockReturnValue("unsigned-xdr"), }), }); (signTransaction as any).mockResolvedValue(mockSignedXdr); @@ -146,13 +155,13 @@ describe("Transaction Utility Functions", () => { }); const args = [ - Address.fromString("GABC123").toScVal(), - Address.fromString("GDEF456").toScVal(), + Address.fromString("GCMEDW2SHDHZC3YKI3ZQ574VZPDDHR5SZDYPPZLQVR2V56X7UOYX7ZMO").toScVal(), + Address.fromString("GBNM7FGFC5BCY6VN6UIFNBYUGNTSLR446YDTWY2BIQU2MHIMAXP2SUM6").toScVal(), ]; - const result = await buildSignAndSubmit("test_method", args, "GABC123", mockConfig); + const result = await buildSignAndSubmit("test_method", args, "GCMEDW2SHDHZC3YKI3ZQ574VZPDDHR5SZDYPPZLQVR2V56X7UOYX7ZMO", mockConfig); - expect(mockServer.getAccount).toHaveBeenCalledWith("GABC123"); + expect(mockServer.getAccount).toHaveBeenCalledWith("GCMEDW2SHDHZC3YKI3ZQ574VZPDDHR5SZDYPPZLQVR2V56X7UOYX7ZMO"); expect(mockServer.simulateTransaction).toHaveBeenCalled(); expect(signTransaction).toHaveBeenCalled(); expect(mockServer.sendTransaction).toHaveBeenCalled(); @@ -161,9 +170,10 @@ describe("Transaction Utility Functions", () => { }); it("should throw error if simulation fails", async () => { - const mockAccount = { - sequence: "1234567890", - }; + const mockAccount = new Account( + "GCMEDW2SHDHZC3YKI3ZQ574VZPDDHR5SZDYPPZLQVR2V56X7UOYX7ZMO", + "1234567890" + ); const mockSimulated = { error: "Simulation error", }; @@ -174,11 +184,11 @@ describe("Transaction Utility Functions", () => { (StellarRpc.Api.isSimulationError as any).mockReturnValue(true); const args = [ - Address.fromString("GABC123").toScVal(), - Address.fromString("GDEF456").toScVal(), + Address.fromString("GCMEDW2SHDHZC3YKI3ZQ574VZPDDHR5SZDYPPZLQVR2V56X7UOYX7ZMO").toScVal(), + Address.fromString("GBNM7FGFC5BCY6VN6UIFNBYUGNTSLR446YDTWY2BIQU2MHIMAXP2SUM6").toScVal(), ]; - await expect(buildSignAndSubmit("test_method", args, "GABC123", mockConfig)).rejects.toThrow( + await expect(buildSignAndSubmit("test_method", args, "GCMEDW2SHDHZC3YKI3ZQ574VZPDDHR5SZDYPPZLQVR2V56X7UOYX7ZMO", mockConfig)).rejects.toThrow( "Transaction simulation failed" ); }); @@ -190,7 +200,7 @@ describe("Transaction Utility Functions", () => { // The old bug was: const _txXdr = client.likePost(...); // XDR discarded const mockConfig = { - contractId: "CDUMMY", + contractId: "CAAQCAIBAEAQCAIBAEAQCAIBAEAQCAIBAEAQCAIBAEAQCAIBAEAQC526", rpcUrl: "https://soroban-testnet.stellar.org", networkPassphrase: "Test SDF Network ; September 2015", }; diff --git a/packages/contracts/contracts/linkora-contracts/src/lib.rs b/packages/contracts/contracts/linkora-contracts/src/lib.rs index cda48d7a..62774651 100644 --- a/packages/contracts/contracts/linkora-contracts/src/lib.rs +++ b/packages/contracts/contracts/linkora-contracts/src/lib.rs @@ -12,8 +12,9 @@ mod validation; pub use errors::{ContractError, RentError}; use validation::{ validate_address_list, validate_amount, validate_gov_parameter, validate_non_default_address, - validate_protocol_fee, validate_pubkey_32, validate_report_verdict, validate_signature, validate_u32_range, - validate_username, MAX_BIO_LEN, MAX_CONTENT_LEN, MAX_FEE_BPS, MAX_QUORUM, + validate_protocol_fee, validate_pubkey_32, validate_report_verdict, + validate_reporter_can_report, validate_signature, validate_u32_range, validate_username, + MAX_BIO_LEN, MAX_CONTENT_LEN, MAX_FEE_BPS, MAX_QUORUM, }; // ── Storage Key Enum ────────────────────────────────────────────────────────── @@ -65,7 +66,7 @@ pub enum StorageKey { PostReportersIdx(u64, u32), // persistent: (post_id, seq) -> Address (Count is ReportCount) PostTipCooldownsCount(u64), // persistent: post_id -> u32 PostTipCooldownsIdx(u64, u32), // persistent: (post_id, seq) -> Address - UpgradeProposal, // instance: staged WASM upgrade proposal + UpgradeProposal, // instance: staged WASM upgrade proposal } // ── Instance-storage key constants (small scalars, not contracttype) ────────── @@ -1104,7 +1105,7 @@ impl LinkoraContract { Self::bump(&env, &author_key); } - let remaining_entries: u32 = f_count + following_count + author_posts.len() as u32; + let remaining_entries: u32 = f_count + following_count + author_posts.len(); if f_count == 0 && following_count == 0 && author_posts.is_empty() { env.storage().persistent().remove(&tombstone_key); @@ -3458,13 +3459,20 @@ impl LinkoraContract { upgrader.require_auth(); validate_non_default_address(&env, "upgrader", &upgrader); Self::require_role(&env, &upgrader, Role::Upgrader); - require_with_error!(&env, new_wasm_hash != BytesN::from_array(&env, &[0u8; 32]), "wasm hash must not be empty"); + require_with_error!( + &env, + new_wasm_hash != BytesN::from_array(&env, &[0u8; 32]), + "wasm hash must not be empty" + ); let proposed_ledger = env.ledger().sequence(); - env.storage().instance().set(&StorageKey::UpgradeProposal, &UpgradeProposal { - new_wasm_hash, - proposed_ledger, - executable_ledger: proposed_ledger.saturating_add(UPGRADE_TIMELOCK_LEDGERS), - }); + env.storage().instance().set( + &StorageKey::UpgradeProposal, + &UpgradeProposal { + new_wasm_hash, + proposed_ledger, + executable_ledger: proposed_ledger.saturating_add(UPGRADE_TIMELOCK_LEDGERS), + }, + ); } /// Executes the previously proposed contract WASM upgrade after the timelock. @@ -3474,15 +3482,32 @@ impl LinkoraContract { validate_non_default_address(&env, "upgrader", &upgrader); Self::require_role(&env, &upgrader, Role::Upgrader); Self::require_not_paused(&env); - let proposal: UpgradeProposal = env.storage().instance().get(&StorageKey::UpgradeProposal).expect("upgrade not proposed"); - require_with_error!(&env, env.ledger().sequence() >= proposal.executable_ledger, "upgrade timelock not elapsed"); + let proposal: UpgradeProposal = env + .storage() + .instance() + .get(&StorageKey::UpgradeProposal) + .expect("upgrade not proposed"); + require_with_error!( + &env, + env.ledger().sequence() >= proposal.executable_ledger, + "upgrade timelock not elapsed" + ); let mut state: ContractState = env.storage().instance().get(&CONTRACT_STATE).unwrap(); - state.version = state.version.checked_add(1).expect("contract version overflow"); + state.version = state + .version + .checked_add(1) + .expect("contract version overflow"); state.implementation_wasm_hash = Some(proposal.new_wasm_hash.clone()); env.storage().instance().set(&CONTRACT_STATE, &state); - env.deployer().update_current_contract_wasm(proposal.new_wasm_hash.clone()); - env.storage().instance().remove(&StorageKey::UpgradeProposal); - ContractUpgraded { new_wasm_hash: proposal.new_wasm_hash }.publish(&env); + env.deployer() + .update_current_contract_wasm(proposal.new_wasm_hash.clone()); + env.storage() + .instance() + .remove(&StorageKey::UpgradeProposal); + ContractUpgraded { + new_wasm_hash: proposal.new_wasm_hash, + } + .publish(&env); } /// Deprecated immediate-upgrade entrypoint. Upgrades must use diff --git a/packages/contracts/contracts/linkora-contracts/src/test.rs b/packages/contracts/contracts/linkora-contracts/src/test.rs index c8d3bd6a..145fbdda 100644 --- a/packages/contracts/contracts/linkora-contracts/src/test.rs +++ b/packages/contracts/contracts/linkora-contracts/src/test.rs @@ -1365,7 +1365,10 @@ fn test_get_pool_admins_returns_some_for_existing_pool() { ); let admins = client.get_pool_admins(&pool_id); - assert!(admins.is_some(), "get_pool_admins must return Some for an existing pool"); + assert!( + admins.is_some(), + "get_pool_admins must return Some for an existing pool" + ); let admins = admins.unwrap(); assert_eq!(admins.len(), 2); assert!(admins.iter().any(|a| a == pool_admin1)); @@ -8078,4 +8081,3 @@ fn test_batch_cleanup_post_emits_event_summary() { client.batch_cleanup_post(&post_id, &10); assert!(client.get_post(&post_id).is_none()); } - diff --git a/packages/contracts/contracts/token-factory/src/lib.rs b/packages/contracts/contracts/token-factory/src/lib.rs index b53756d0..6d925e8a 100644 --- a/packages/contracts/contracts/token-factory/src/lib.rs +++ b/packages/contracts/contracts/token-factory/src/lib.rs @@ -166,12 +166,12 @@ impl TokenFactoryContract { ); require_with_error!( &env, - name.len() > 0 && name.len() <= MAX_NAME_LEN, + !name.is_empty() && name.len() <= MAX_NAME_LEN, "name must be 1-64 characters" ); require_with_error!( &env, - symbol.len() > 0 && symbol.len() <= MAX_SYMBOL_LEN, + !symbol.is_empty() && symbol.len() <= MAX_SYMBOL_LEN, "symbol must be 1-16 characters" ); diff --git a/packages/contracts/contracts/token-factory/src/test.rs b/packages/contracts/contracts/token-factory/src/test.rs index 9fbb51b5..87472ae8 100644 --- a/packages/contracts/contracts/token-factory/src/test.rs +++ b/packages/contracts/contracts/token-factory/src/test.rs @@ -6,8 +6,9 @@ use soroban_sdk::{testutils::Address as _, Address, BytesN, Env, String}; // ── Helpers ─────────────────────────────────────────────────────────────────── /// Placeholder 32-byte hash used when we don't need real WASM semantics. +/// Must be non-zero: `initialize`/`update_token_wasm` reject the all-zero hash. fn dummy_wasm_hash(env: &Env) -> BytesN<32> { - BytesN::from_array(env, &[0u8; 32]) + BytesN::from_array(env, &[0xABu8; 32]) } fn setup(env: &Env) -> (TokenFactoryContractClient<'_>, Address, BytesN<32>) { diff --git a/packages/sdk/src/__tests__/simulation.test.ts b/packages/sdk/src/__tests__/simulation.test.ts index 069bc715..0a798a75 100644 --- a/packages/sdk/src/__tests__/simulation.test.ts +++ b/packages/sdk/src/__tests__/simulation.test.ts @@ -257,7 +257,12 @@ describe("LinkoraClient simulation and fee injection", () => { _isSuccess: true, minResourceFee: "10000", transactionData: null, - result: { retval: null }, + // One simulated result per operation, each carrying its own auth + // entries — the shape `buildMultiOpTx` requires since #1250. + result: [ + { auth: [], retval: null }, + { auth: [], retval: null }, + ], }; mockSimulateTransaction.mockResolvedValue(mockResult); diff --git a/packages/sdk/src/__tests__/write.test.ts b/packages/sdk/src/__tests__/write.test.ts index 2641400a..3bb093ea 100644 --- a/packages/sdk/src/__tests__/write.test.ts +++ b/packages/sdk/src/__tests__/write.test.ts @@ -33,7 +33,7 @@ jest.mock("@stellar/stellar-base", () => ({ })), scValToNative: jest.fn(), TransactionBuilder: jest.fn(() => ({ addOperation: mockAddOperation })), - Account: jest.fn(), + Account: jest.fn((accountId: string, sequence: string) => ({ _accountId: accountId, sequence })), Keypair: { random: jest.fn(() => ({ publicKey: () => "GWRITEKEYXXXXXXXXXXXXXXXXXXXXXXXXXX" })) }, xdr: {}, })); diff --git a/packages/sdk/src/client.ts b/packages/sdk/src/client.ts index 867561da..eae98a27 100644 --- a/packages/sdk/src/client.ts +++ b/packages/sdk/src/client.ts @@ -25,7 +25,12 @@ import { GovParameter } from "./generated/types.js"; import type { GovProposal } from "./generated/types.js"; import { ConnectionHealthMonitor, HealthCheckConfig, ConnectionStatusCallback } from "./health.js"; import { fetchWithTimeout } from "./utils/fetch.js"; -import type { QueueSigner, RunOptions } from "./queue.js"; +import { + createRpcClientAdapter, + type QueueSigner, + type RpcClient, + type RunOptions, +} from "./queue.js"; import { submitTransaction } from "./submit.js"; const { isSimulationError, isSimulationSuccess } = rpc.Api; @@ -262,6 +267,11 @@ export class LinkoraClient extends GeneratedLinkoraClient { return new rpc.Server(this._rpcUrl, { allowHttp: this._allowHttp }); } + /** Build a string-XDR {@link RpcClient} adapter for use with `TransactionQueue`. */ + createRpcClient(): RpcClient { + return createRpcClientAdapter(this.createRpcServer(), this._networkPassphrase); + } + /** * Convenience method to sign and submit a transaction using the TransactionQueue. * diff --git a/packages/sdk/src/queue.ts b/packages/sdk/src/queue.ts index 809b2db9..828d414e 100644 --- a/packages/sdk/src/queue.ts +++ b/packages/sdk/src/queue.ts @@ -18,10 +18,18 @@ * deadline is exceeded the step is treated as a failure and rollbacks fire. */ +import * as rpc from "@stellar/stellar-sdk/rpc"; +import { + TransactionBuilder, + type Transaction, + type FeeBumpTransaction, +} from "@stellar/stellar-base"; import { CircuitBreakerError, NetworkError, SigningError, SimulationError } from "./errors.js"; import { resolveRetryConfig, type RetryConfig } from "./config.js"; import { CircuitBreaker, withRetry, type RetryLogger } from "./utils/retry.js"; +const { isSimulationError, isSimulationSuccess } = rpc.Api; + export type TxStatus = "pending" | "simulated" | "submitted" | "confirmed" | "failed"; export interface TxStatusEvent { @@ -77,6 +85,50 @@ export interface RpcClient { getTransaction(hash: string): Promise<{ status: string; errorResultXdr?: string }>; } +/** + * Adapts a raw `@stellar/stellar-sdk` RPC server handle — whose methods take + * parsed `Transaction` objects — to the string-XDR {@link RpcClient} shape + * `TransactionQueue` expects. + */ +export function createRpcClientAdapter(server: rpc.Server, networkPassphrase: string): RpcClient { + const parse = (xdr: string): Transaction | FeeBumpTransaction => + TransactionBuilder.fromXDR(xdr, networkPassphrase); + + return { + async simulateTransaction(xdr: string): Promise { + const result = await server.simulateTransaction(parse(xdr)); + + if (isSimulationError(result)) { + return { success: false, resourceFee: "0", error: result.error }; + } + if (!isSimulationSuccess(result)) { + return { success: false, resourceFee: "0", error: "Unknown simulation error" }; + } + return { success: true, resourceFee: result.minResourceFee || "0" }; + }, + + async sendTransaction(signedXdr: string) { + const result = await server.sendTransaction(parse(signedXdr)); + return { + hash: result.hash, + status: result.status, + errorResultXdr: result.errorResult?.toXDR("base64"), + }; + }, + + async getTransaction(hash: string) { + const result = await server.getTransaction(hash); + return { + status: result.status, + errorResultXdr: + result.status === rpc.Api.GetTransactionStatus.FAILED + ? result.resultXdr.toXDR("base64") + : undefined, + }; + }, + }; +} + /** Options that can be passed per `run()` call to override queue-level defaults. */ export interface RunOptions { /** diff --git a/packages/sdk/src/submit.ts b/packages/sdk/src/submit.ts index ede1a880..6f45596e 100644 --- a/packages/sdk/src/submit.ts +++ b/packages/sdk/src/submit.ts @@ -19,19 +19,19 @@ export async function submitTransaction( opts?: RunOptions ): Promise { const xdrString = typeof xdrOrTx === "string" ? xdrOrTx : xdrOrTx.toEnvelope().toXDR("base64"); - + const queue = new TransactionQueue({ signer, - rpc: client.createRpcServer(), + rpc: client.createRpcClient(), }); queue.enqueue(xdrString); await queue.run(opts); - + const hashes = queue.submittedHashes; if (hashes.length === 0 && !opts?.dryRun) { throw new Error("Transaction was not submitted successfully."); } - + return hashes[0] ?? ""; } diff --git a/services/indexer/src/__tests__/ratelimit.test.ts b/services/indexer/src/__tests__/ratelimit.test.ts index ad71ec67..cb7b39a7 100644 --- a/services/indexer/src/__tests__/ratelimit.test.ts +++ b/services/indexer/src/__tests__/ratelimit.test.ts @@ -3,7 +3,7 @@ * deterministic and instant. */ -import { TokenBucket } from "../ratelimit"; +import { TokenBucket, wsMaxMessageBytesFromEnv, DEFAULT_WS_MAX_MESSAGE_BYTES } from "../ratelimit"; /** Controllable clock + sleep where sleeping advances the clock. */ function fakeTime() { @@ -83,24 +83,23 @@ describe("TokenBucket", () => { describe("wsMaxMessageBytesFromEnv", () => { it("returns default when no env var is set", () => { - const { wsMaxMessageBytesFromEnv, DEFAULT_WS_MAX_MESSAGE_BYTES } = require("../ratelimit"); expect(wsMaxMessageBytesFromEnv({})).toBe(DEFAULT_WS_MAX_MESSAGE_BYTES); }); it("parses WS_MAX_MESSAGE_BYTES from env", () => { - const { wsMaxMessageBytesFromEnv } = require("../ratelimit"); expect(wsMaxMessageBytesFromEnv({ WS_MAX_MESSAGE_BYTES: "1024" })).toBe(1024); }); it("parses WS_MAX_PAYLOAD_BYTES from env fallback", () => { - const { wsMaxMessageBytesFromEnv } = require("../ratelimit"); expect(wsMaxMessageBytesFromEnv({ WS_MAX_PAYLOAD_BYTES: "2048" })).toBe(2048); }); it("falls back to default for invalid number", () => { - const { wsMaxMessageBytesFromEnv, DEFAULT_WS_MAX_MESSAGE_BYTES } = require("../ratelimit"); - expect(wsMaxMessageBytesFromEnv({ WS_MAX_MESSAGE_BYTES: "abc" })).toBe(DEFAULT_WS_MAX_MESSAGE_BYTES); - expect(wsMaxMessageBytesFromEnv({ WS_MAX_MESSAGE_BYTES: "-10" })).toBe(DEFAULT_WS_MAX_MESSAGE_BYTES); + expect(wsMaxMessageBytesFromEnv({ WS_MAX_MESSAGE_BYTES: "abc" })).toBe( + DEFAULT_WS_MAX_MESSAGE_BYTES + ); + expect(wsMaxMessageBytesFromEnv({ WS_MAX_MESSAGE_BYTES: "-10" })).toBe( + DEFAULT_WS_MAX_MESSAGE_BYTES + ); }); }); - diff --git a/services/indexer/src/db.ts b/services/indexer/src/db.ts index 7dbfa312..bf78b435 100644 --- a/services/indexer/src/db.ts +++ b/services/indexer/src/db.ts @@ -171,7 +171,8 @@ export interface Database { offset: number; }): Promise<{ posts: Post[]; total: number }>; searchPosts(filters: { - q: string; + q?: string; + tag?: string; limit: number; offset: number; }): Promise<{ posts: Post[]; total: number }>; diff --git a/services/indexer/src/utils.ts b/services/indexer/src/utils.ts deleted file mode 100644 index 2dcc6eaa..00000000 Binary files a/services/indexer/src/utils.ts and /dev/null differ