feat: implement indexer foundation#42
Conversation
|
@ayomideadeniran Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
📝 WalkthroughWalkthroughAdds shared indexer config validation, Soroban RPC client construction, processed-event repository support, poller retry/cursor handling, and new Streams entities plus migration and exports. ChangesCommon indexer infrastructure
Streams database schema
Sequence Diagram(s)sequenceDiagram
participant SorobanPoller
participant fetchEvents
participant processEvent
participant updateCursor
SorobanPoller->>fetchEvents: fetchEvents(startLedger, endLedger)
alt transient fetch error
SorobanPoller->>SorobanPoller: retry fetchEvents()
SorobanPoller->>fetchEvents: fetchEvents(startLedger, endLedger)
end
loop each event
SorobanPoller->>processEvent: processEvent(event)
end
alt all events succeed
SorobanPoller->>updateCursor: updateCursor(endLedger)
else processEvent fails
SorobanPoller-->>SorobanPoller: return success=false
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@indexer/common/src/db/repository.ts`:
- Around line 40-57: In recordEventProcessed, the current .orIgnore path can
silently skip duplicate inserts but still returns true, so update the logic to
detect the ignored insert result and return false when no row was written. Use
the repository/createQueryBuilder insert flow in repository.ts to distinguish a
successful insert from a conflict-suppressed no-op, and keep the
unique-violation fallback in the catch block consistent with the duplicate case.
Add test coverage around recordEventProcessed for the duplicate-suppressed path
to verify it returns false when the insert is ignored.
In `@indexer/common/src/index.ts`:
- Around line 6-7: The package entrypoint is pulling in eager side effects by
re-exporting from config and RPC modules that call loadConfig() and
createSorobanClient() at import time. Update the barrel to avoid importing those
singleton-producing modules directly; instead export only factories/types from
index.ts and move singleton initialization behind an explicit runtime entrypoint
or separate import. Keep the symbols config, loadConfig, createSorobanClient,
and sorobanClient available without forcing module-load configuration/client
construction.
In `@indexer/common/src/poller/index.ts`:
- Around line 53-60: The transient error check in isTransientError() is missing
plain HTTP 429 rate-limit failures, so polling can stop instead of retrying;
update the matching logic to recognize 429 and “too many requests” in addition
to the existing timeout/rate limit cases. Normalize the error message casing
before checking, and keep the changes localized to isTransientError() inside the
retry path used by withRetry().
In `@indexer/streams/src/db/migrations/00001_InitialStreamsSchema.ts`:
- Line 23: The initial streams schema migration uses uuid_generate_v4() for
stream_withdrawal_action.id and stream_cancel_action.id, but the uuid-ossp
extension is not guaranteed to exist on a fresh database. Update
00001_InitialStreamsSchema.ts to provision the extension first by adding CREATE
EXTENSION IF NOT EXISTS "uuid-ossp"; before the table definitions that depend on
uuid_generate_v4(), keeping the migration order in sync with the
stream_withdrawal_action and stream_cancel_action setup.
- Around line 13-16: The stream amount columns are still using 64-bit-safe
bigint even though Soroban token values are i128-sized. Update the initial
streams schema migration and the matching entity definitions for the relevant
amount fields in the streams database model to use a 128-bit-safe type such as
numeric, keeping the names totalAmount, amountWithdrawn, and amount aligned
across the migration and entity classes.
In `@indexer/tsconfig.base.json`:
- Line 23: The shared tsconfig is incorrectly applying Vitest globals to all
packages through the base config, so move the vitest/globals entry out of
tsconfig.base.json and into a dedicated test-only tsconfig. Update the relevant
package test configuration to extend the base settings while keeping normal
source builds for indexer/common, indexer/distributions, and indexer/streams
free of test ambient types, and verify any package-specific ambient types are
still declared where needed.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 47e4d0a6-fd2e-4880-ae73-f339e0df3899
⛔ Files ignored due to path filters (1)
bun.lockis excluded by!**/*.lock
📒 Files selected for processing (17)
indexer/common/package.jsonindexer/common/src/config/index.tsindexer/common/src/db/entity/IndexedEvent.tsindexer/common/src/db/repository.test.tsindexer/common/src/db/repository.tsindexer/common/src/index.tsindexer/common/src/poller/index.test.tsindexer/common/src/poller/index.tsindexer/common/src/rpc/client.test.tsindexer/common/src/rpc/client.tsindexer/streams/package.jsonindexer/streams/src/db/entity/CancelAction.tsindexer/streams/src/db/entity/Stream.tsindexer/streams/src/db/entity/WithdrawalAction.tsindexer/streams/src/db/migrations/00001_InitialStreamsSchema.tsindexer/streams/src/index.tsindexer/tsconfig.base.json
| try { | ||
| await this.repo | ||
| .createQueryBuilder() | ||
| .insert() | ||
| .into(IndexedEvent) | ||
| .values({ | ||
| contractId, | ||
| ledgerNumber, | ||
| txHash, | ||
| eventIndex, | ||
| }) | ||
| .orIgnore("uq_indexed_event_identity") // Safe duplicate insert | ||
| .execute(); | ||
| return true; | ||
| } catch (err) { | ||
| // If the driver doesn't support orIgnore, fallback to simple insert and catch unique violation. | ||
| if (err instanceof Error && err.message.includes("unique constraint")) { | ||
| return false; |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major
🧩 Analysis chain
🏁 Script executed:
# Inspect the repository file and surrounding call sites/tests for EventRepository behavior
git ls-files | rg '^indexer/common/src/db/repository\.ts$|^indexer/common/src/db/.*test.*|^indexer/common/src/db/.*spec.*|^indexer/common/src/db/.*repository.*'Repository: Fundable-Protocol/Backend
Length of output: 240
🏁 Script executed:
# Read the target file with line numbers and map its structure
wc -l indexer/common/src/db/repository.ts && cat -n indexer/common/src/db/repository.ts | sed -n '1,220p'Repository: Fundable-Protocol/Backend
Length of output: 2195
🏁 Script executed:
# Search for repository usage and tests that assert boolean semantics
rg -n "recordEventProcessed|isEventProcessed|orIgnore\\(|unique constraint|already processed|IndexedEvent" indexer/common/srcRepository: Fundable-Protocol/Backend
Length of output: 1772
🏁 Script executed:
# Read the repository tests to see what is already covered
cat -n indexer/common/src/db/repository.test.ts | sed -n '1,220p'Repository: Fundable-Protocol/Backend
Length of output: 3082
🏁 Script executed:
# Inspect TypeORM insert/orIgnore result shape from public docs or source references
web_search
TypeORM InsertQueryBuilder orIgnore execute InsertResult affected rows duplicate ignored behaviorRepository: Fundable-Protocol/Backend
Length of output: 252
🌐 Web query:
TypeORM InsertQueryBuilder orIgnore execute InsertResult affected rows duplicate ignored behavior
💡 Result:
In TypeORM, using .orIgnore() with InsertQueryBuilder enables the ON CONFLICT DO NOTHING behavior (or INSERT IGNORE for MySQL) to suppress errors when unique constraint violations occur [1][2][3]. Key behaviors and limitations regarding InsertResult and affected rows include: 1. No Reliable Affected Row Count: TypeORM's InsertResult does not consistently or accurately report the number of "affected" rows when orIgnore() is used [4]. Because the database operation is suppressed by the SQL engine, TypeORM often cannot distinguish between successfully inserted rows and those that were ignored due to conflicts [4][5]. 2. Limitations on Conflict Targets: The .orIgnore() method currently functions as a simple boolean toggle [6][7]. It does not support specifying a conflict target (e.g., a specific unique index or column) [6][8]. Providing a string argument to orIgnore() (e.g., .orIgnore("id")) is discarded because the method coerces the input to a boolean [6][7]. Consequently, it will ignore conflicts on any unique index defined on the table, not just the one you might intend [8]. 3. Impact on Subscribers and Result Mapping: When using .orIgnore(), you may encounter issues where: - Subscribers like afterInsert() are triggered even for rows that were not actually inserted [5]. - Entity ID mapping or generated column assignment may become inconsistent because the InsertResult does not account for the skipped rows, potentially leading to incorrect data mapping back to your entity objects [4]. If you require precise control over conflict handling or need accurate feedback on which rows were inserted versus ignored, .orIgnore() is generally insufficient. It is recommended to handle conflict logic through database-specific constraints or, where supported, more granular upsert logic [1][2].
Citations:
- 1: https://typeorm.io/docs/query-builder/insert-query-builder/
- 2: https://orkhan.gitbook.io/typeorm/docs/docs/query-builder/2-insert-query-builder.md
- 3: typeorm/typeorm@a9cf89e
- 4: QueryBuilder .insert().orIgnore(true) incorrectly assigns autogenerated columns to Entities typeorm/typeorm#5226
- 5: afterInsert() is called with rows that are not actually inserted when using orIgnore() typeorm/typeorm#5449
- 6: refactor(QueryBuilder)!: remove onConflict(), deprecated orUpdate() overload, and setNativeParameters() typeorm/typeorm#12090
- 7: https://github.com/typeorm/typeorm/blob/d4f7b44f/src/query-builder/InsertQueryBuilder.ts
- 8: orIgnore statement does nothing typeorm/typeorm#10644
🏁 Script executed:
cat -n indexer/common/src/db/repository.test.ts | sed -n '1,220p'Repository: Fundable-Protocol/Backend
Length of output: 3082
🌐 Web query:
TypeORM InsertQueryBuilder orIgnore execute InsertResult affected rows duplicate ignored behavior
💡 Result:
In TypeORM, the .orIgnore() function is used within an InsertQueryBuilder to suppress errors caused by duplicate key conflicts or invalid data during an insertion [1][2]. When executed, this method instructs the database to perform an "insert ignore" operation, such as INSERT IGNORE (MySQL) or ON CONFLICT DO NOTHING (PostgreSQL, SQLite, etc.) [1][3][4][5]. Regarding the behavior of orIgnore() and the returned InsertResult: 1. Effect on Affected Rows: When orIgnore() is used, the affected property in the InsertResult may not accurately reflect the number of rows actually written to the database [6]. Because the database is instructed to skip conflicts rather than throw an error, it may still report the operation as successful, and the ORM might count intended rows rather than rows successfully committed [6]. 2. Limitations: The orIgnore() method applies a blanket "ignore" policy for the entire insert statement [7]. It does not support specifying individual columns or specific constraints to ignore (e.g., ON CONFLICT (col_name) DO NOTHING) [7][8]. It simply maps to the database's native "ignore" or "do nothing" syntax [3][4]. 3. Subscriber Behavior: A known side effect is that TypeORM's lifecycle subscribers (like afterInsert()) may still be triggered for entities that were ultimately ignored by the database [6]. Developers have reported needing to implement manual checks (e.g., verifying if a generated date or ID field is populated) within these subscribers to determine if an entity was truly persisted [6]. In summary, while orIgnore() is effective for suppressing errors during bulk inserts of potentially redundant data, it provides limited granularity and may require cautious handling of returned result data and lifecycle hooks [7][6].
Citations:
- 1: https://typeorm.io/docs/query-builder/insert-query-builder/
- 2: https://orkhan.gitbook.io/typeorm/docs/docs/query-builder/2-insert-query-builder.md
- 3: https://github.com/typeorm/typeorm/blob/master/src/query-builder/InsertQueryBuilder.ts
- 4: https://github.com/typeorm/typeorm/blob/d4f7b44f/src/query-builder/InsertQueryBuilder.ts
- 5: Support for insertion ignore on duplicate error typeorm/typeorm#1780
- 6: afterInsert() is called with rows that are not actually inserted when using orIgnore() typeorm/typeorm#5449
- 7: orIgnore statement does nothing typeorm/typeorm#10644
- 8: Regression: Deprecation of .onConflict in InsertQueryBuilder typeorm/typeorm#8124
recordEventProcessed() still returns true when the row is ignored. .orIgnore("uq_indexed_event_identity") does not target that constraint; it suppresses conflicts broadly, so a duplicate can resolve without writing anything. Return false for the ignored case and add coverage for the duplicate-suppressed path.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@indexer/common/src/db/repository.ts` around lines 40 - 57, In
recordEventProcessed, the current .orIgnore path can silently skip duplicate
inserts but still returns true, so update the logic to detect the ignored insert
result and return false when no row was written. Use the
repository/createQueryBuilder insert flow in repository.ts to distinguish a
successful insert from a conflict-suppressed no-op, and keep the
unique-violation fallback in the catch block consistent with the duplicate case.
Add test coverage around recordEventProcessed for the duplicate-suppressed path
to verify it returns false when the insert is ignored.
There was a problem hiding this comment.
recordEventProcessed() still returns true when the row is ignored. .orIgnore("uq_indexed_event_identity") does not target that constraint; it suppresses conflicts broadly, so a duplicate can resolve without writing anything. Return false for the ignored case and add coverage for the duplicate-suppressed path.
There was a problem hiding this comment.
@pragmaticAweds, good catch — I’d make this a small two-part fix.
- Update
indexer/common/src/db/repository.tssorecordEventProcessed()distinguishes a real insert from anorIgnore()no-op and returnsfalsefor the ignored duplicate case. - Keep the existing unique-violation fallback in the
catchblock aligned with that same duplicate semantics. - Add a focused test in
indexer/common/src/db/repository.test.tsfor the duplicate-suppressed path.
Failed to handle agent chat message. Please try again.
| export { config, loadConfig } from "./config/index.js"; | ||
| export { createSorobanClient, sorobanClient } from "./rpc/client.js"; |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift
Avoid import-time config/client side effects in the package entrypoint.
These barrel exports now force ./config/index.js and ./rpc/client.js to execute on every @fundable-indexer/common import. Because those modules eagerly call loadConfig() and createSorobanClient(), even consumers that only import unrelated metadata become dependent on valid runtime env and client construction at module-load time. Please keep the barrel side-effect-free by exporting factories/types here and moving the singletons behind an explicit runtime import.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@indexer/common/src/index.ts` around lines 6 - 7, The package entrypoint is
pulling in eager side effects by re-exporting from config and RPC modules that
call loadConfig() and createSorobanClient() at import time. Update the barrel to
avoid importing those singleton-producing modules directly; instead export only
factories/types from index.ts and move singleton initialization behind an
explicit runtime entrypoint or separate import. Keep the symbols config,
loadConfig, createSorobanClient, and sorobanClient available without forcing
module-load configuration/client construction.
| return ( | ||
| message.includes("timeout") || | ||
| message.includes("rate limit") || | ||
| message.includes("ECONNRESET") || | ||
| message.includes("503") || | ||
| message.includes("504") || | ||
| message.includes("502") | ||
| ); |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Retry classification misses HTTP 429 responses.
withRetry() claims to handle rate limiting, but isTransientError() never matches a plain 429 Too Many Requests error. Those failures will abort polling immediately instead of retrying. Add 429/too many requests handling and normalize message casing before matching.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@indexer/common/src/poller/index.ts` around lines 53 - 60, The transient error
check in isTransientError() is missing plain HTTP 429 rate-limit failures, so
polling can stop instead of retrying; update the matching logic to recognize 429
and “too many requests” in addition to the existing timeout/rate limit cases.
Normalize the error message casing before checking, and keep the changes
localized to isTransientError() inside the retry path used by withRetry().
| "totalAmount" bigint NOT NULL, | ||
| "startTime" bigint NOT NULL, | ||
| "endTime" bigint NOT NULL, | ||
| "amountWithdrawn" bigint NOT NULL DEFAULT '0', |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n== files ==\n'
git ls-files | rg 'indexer/streams/src/db/migrations/00001_InitialStreamsSchema.ts|indexer/streams/src/db|uuid-ossp|migration' || true
printf '\n== outline migration ==\n'
ast-grep outline indexer/streams/src/db/migrations/00001_InitialStreamsSchema.ts --view expanded || true
printf '\n== relevant snippets ==\n'
sed -n '1,220p' indexer/streams/src/db/migrations/00001_InitialStreamsSchema.tsRepository: Fundable-Protocol/Backend
Length of output: 3165
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n== search for stream entities and column types ==\n'
rg -n --glob '!**/node_modules/**' --glob '!**/dist/**' --glob '!**/build/**' \
'totalAmount|amountWithdrawn|amount:|uuid_generate_v4|CREATE EXTENSION|uuid-ossp|numeric\(39,0\)|bigint' \
indexer/streams/src || true
printf '\n== find entity files ==\n'
git ls-files indexer/streams/src | rg 'entity|typeorm|model|schema|migration' || trueRepository: Fundable-Protocol/Backend
Length of output: 2447
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n== search for uuid extension setup ==\n'
rg -n --glob '!**/node_modules/**' --glob '!**/dist/**' --glob '!**/build/**' \
'CREATE EXTENSION|uuid-ossp|uuid_generate_v4\(\)' .
printf '\n== inspect migration/entity types around IDs and amounts ==\n'
cat -n indexer/streams/src/db/entity/Stream.ts
printf '\n---\n'
cat -n indexer/streams/src/db/entity/WithdrawalAction.ts
printf '\n---\n'
cat -n indexer/streams/src/db/entity/CancelAction.tsRepository: Fundable-Protocol/Backend
Length of output: 4301
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n== search for stream/token amount domain assumptions ==\n'
rg -n --glob '!**/node_modules/**' --glob '!**/dist/**' --glob '!**/build/**' \
'i128|int128|128-bit|amountWithdrawn|totalAmount|WithdrawalAction|Stream' indexer docs src || trueRepository: Fundable-Protocol/Backend
Length of output: 3264
🌐 Web query:
Soroban TokenInterface amount type i128 docs.rs bigint 64-bit PostgreSQL
💡 Result:
In Soroban smart contracts, the amount type used in the TokenInterface is i128 (a signed 128-bit integer) [1][2][3]. This type is used for balances, allowances, and transfer amounts to provide a wide range of values that accommodate high-precision token amounts (even with 18 decimal places) while supporting negative values if necessary [3]. Regarding the integration of Soroban i128 values into a PostgreSQL database: 1. PostgreSQL Integer Limits: PostgreSQL's bigint type is a signed 8-byte (64-bit) integer [4][5][6]. It has a range of approximately -9.22 quintillion to +9.22 quintillion [4][6]. 2. Compatibility Gap: Because Soroban uses a 128-bit integer (i128) and PostgreSQL's native bigint (int8) is only 64-bit, you cannot store all possible Soroban i128 values directly in a PostgreSQL bigint column without risk of overflow [4][5]. 3. Recommended Storage Strategy: - Numeric/Decimal: The most straightforward way to store 128-bit integers in PostgreSQL is to use the NUMERIC type [4][5]. While potentially slower than fixed-width integers, it supports arbitrary precision and can easily accommodate the range of a 128-bit integer [4][5]. - Application-Level Handling: If you must maintain performance for aggregations and are confident the values will not exceed the 64-bit range, you might store them as bigint; however, this is unsafe for general i128 data [4][7]. - Custom Extensions: There is no native unsigned 64-bit or signed 128-bit integer type in standard PostgreSQL [4][7], meaning the NUMERIC type remains the standard, reliable choice for handling values exceeding the 64-bit signed integer limit [4][5].
Citations:
- 1: https://docs.rs/soroban-sdk/latest/soroban%5Fsdk/token/trait.TokenInterface.html
- 2: https://developers.stellar.org/docs/tokens/token-interface
- 3: https://github.com/stellar/stellar-protocol/blob/master/core/cap-0046-06.md
- 4: https://www.postgresql.org/docs/current/datatype-numeric.html
- 5: https://www.postgresql.org/docs/18/datatype-numeric.html
- 6: https://openillumi.com/en/en-postgresql-bigint-int8-difference/
- 7: https://stackoverflow.com/questions/21909027/postgres-data-types-how-to-store-uint64-in-postresql
🌐 Web query:
Soroban TokenInterface transfer amount i128 docs.rs
💡 Result:
In the Soroban SDK, the TokenInterface trait defines the transfer method with the following signature [1][2]: fn transfer(env: Env, from: Address, to: MuxedAddress, amount: i128) The amount parameter is of type i128, which represents the quantity of tokens to be transferred [1][2]. This i128 type is standard for token amounts in Soroban to accommodate the full range of potential token balances while maintaining compatibility with the underlying Stellar ledger's representation [1][3]. When implementing or calling this interface: - The from parameter specifies the source address holding the tokens [1][2]. - The to parameter is a MuxedAddress, which allows for flexible destination handling (e.g., direct transfers to an exchange) [1][4][2]. - The amount must be a non-negative value; contract implementations typically include logic to verify this (e.g., checking that the amount is greater than or equal to zero) before executing the balance update [5][6]. For transfer_from, which consumes an allowance, the signature also uses i128 for the amount [1][2]: fn transfer_from(env: Env, spender: Address, from: Address, to: Address, amount: i128) In this case, the spender must have a sufficient allowance established by the from address, and the amount transferred reduces that allowance accordingly [1][2].
Citations:
- 1: https://docs.rs/soroban-sdk/latest/soroban_sdk/token/trait.TokenInterface.html
- 2: https://docs.rs/soroban-sdk/latest/soroban%5Fsdk/token/trait.TokenInterface.html
- 3: https://developers.stellar.org/docs/tokens/token-interface
- 4: https://docs.rs/soroban-token-sdk/latest/soroban_token_sdk/_migrating/v23_token_transfer/index.html
- 5: https://developers.stellar.org/docs/build/smart-contracts/example-contracts/tokens
- 6: https://github.com/stellar/soroban-examples/blob/main/token/src/contract.rs
Use a 128-bit-safe type for token amounts. bigint is only 64-bit, while Soroban token amounts use i128, so totalAmount, amountWithdrawn, and amount can overflow. Update the migration and matching entity columns to numeric (or another 128-bit-safe type) to keep the schema aligned.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@indexer/streams/src/db/migrations/00001_InitialStreamsSchema.ts` around lines
13 - 16, The stream amount columns are still using 64-bit-safe bigint even
though Soroban token values are i128-sized. Update the initial streams schema
migration and the matching entity definitions for the relevant amount fields in
the streams database model to use a 128-bit-safe type such as numeric, keeping
the names totalAmount, amountWithdrawn, and amount aligned across the migration
and entity classes.
| ); | ||
|
|
||
| CREATE TABLE "stream_withdrawal_action" ( | ||
| "id" uuid PRIMARY KEY DEFAULT uuid_generate_v4(), |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Searching for UUID extension provisioning or alternative UUID defaults..."
rg -n 'uuid_generate_v4|gen_random_uuid|uuidv4|CREATE EXTENSION|uuid-ossp|pgcrypto' indexer
echo
echo "Searching for datasource / migration config that pins the target database..."
fd -i 'data-source|datasource|ormconfig|package.json|README' indexerRepository: Fundable-Protocol/Backend
Length of output: 696
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Outline of the migration file:"
ast-grep outline indexer/streams/src/db/migrations/00001_InitialStreamsSchema.ts --view expanded || true
echo
echo "Relevant lines from the migration:"
sed -n '1,120p' indexer/streams/src/db/migrations/00001_InitialStreamsSchema.ts
echo
echo "Search for database setup / extension provisioning across indexer:"
rg -n 'CREATE EXTENSION|uuid-ossp|pgcrypto|gen_random_uuid|uuid_generate_v4|DataSource|typeorm|migrations' indexer/streams indexer/common indexer -g '!**/node_modules/**'Repository: Fundable-Protocol/Backend
Length of output: 6273
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Outline of the migration file:"
ast-grep outline indexer/streams/src/db/migrations/00001_InitialStreamsSchema.ts --view expanded || true
echo
echo "Relevant lines from the migration:"
sed -n '1,120p' indexer/streams/src/db/migrations/00001_InitialStreamsSchema.ts
echo
echo "Search for database setup / extension provisioning across indexer:"
rg -n 'CREATE EXTENSION|uuid-ossp|pgcrypto|gen_random_uuid|uuid_generate_v4|DataSource|typeorm|migrations' indexer/streams indexer/common indexer -g '!**/node_modules/**'Repository: Fundable-Protocol/Backend
Length of output: 6273
Provision uuid-ossp before using uuid_generate_v4()
stream_withdrawal_action.id and stream_cancel_action.id rely on uuid_generate_v4(), which will fail on a fresh PostgreSQL database unless uuid-ossp is enabled first. Add CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; before these tables.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@indexer/streams/src/db/migrations/00001_InitialStreamsSchema.ts` at line 23,
The initial streams schema migration uses uuid_generate_v4() for
stream_withdrawal_action.id and stream_cancel_action.id, but the uuid-ossp
extension is not guaranteed to exist on a fresh database. Update
00001_InitialStreamsSchema.ts to provision the extension first by adding CREATE
EXTENSION IF NOT EXISTS "uuid-ossp"; before the table definitions that depend on
uuid_generate_v4(), keeping the migration order in sync with the
stream_withdrawal_action and stream_cancel_action setup.
| "strict": true, | ||
| "target": "ES2023", | ||
| "types": ["vitest/globals"] | ||
| "types": ["vitest/globals"], |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect the shared tsconfig and the configs that extend it.
git ls-files 'indexer/tsconfig.base.json' 'indexer/**/tsconfig*.json' | sort
printf '\n--- indexer/tsconfig.base.json ---\n'
cat -n indexer/tsconfig.base.json
printf '\n--- configs extending tsconfig.base.json ---\n'
rg -n '"extends"\s*:\s*"\.\.?/tsconfig\.base\.json"|"\.\./tsconfig\.base\.json"|"\./tsconfig\.base\.json"' indexer -g 'tsconfig*.json'Repository: Fundable-Protocol/Backend
Length of output: 1515
🏁 Script executed:
#!/bin/bash
set -euo pipefail
git ls-files 'indexer/tsconfig.base.json' 'indexer/**/tsconfig*.json' | sort
printf '\n--- indexer/tsconfig.base.json ---\n'
cat -n indexer/tsconfig.base.json
printf '\n--- configs extending tsconfig.base.json ---\n'
rg -n '"extends"\s*:\s*"\.\.?/tsconfig\.base\.json"|"\.\./tsconfig\.base\.json"|"\./tsconfig\.base\.json"' indexer -g 'tsconfig*.json'Repository: Fundable-Protocol/Backend
Length of output: 1515
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect the package tsconfigs for include/exclude or overrides, and look for separate test tsconfigs.
for f in indexer/common/tsconfig.json indexer/distributions/tsconfig.json indexer/streams/tsconfig.json; do
printf '\n--- %s ---\n' "$f"
cat -n "$f"
done
printf '\n--- test tsconfig files under indexer ---\n'
fd -a 'tsconfig*.json' indexerRepository: Fundable-Protocol/Backend
Length of output: 1227
🏁 Script executed:
#!/bin/bash
set -euo pipefail
for f in indexer/common/tsconfig.json indexer/distributions/tsconfig.json indexer/streams/tsconfig.json; do
printf '\n--- %s ---\n' "$f"
cat -n "$f"
done
printf '\n--- test tsconfig files under indexer ---\n'
fd -a 'tsconfig*.json' indexerRepository: Fundable-Protocol/Backend
Length of output: 1227
Move Vitest globals out of the shared base tsconfig.
indexer/tsconfig.base.json:23 applies vitest/globals to every package build (indexer/common, indexer/distributions, indexer/streams), so test globals are available in normal source compilation and package-specific ambient types must be re-added elsewhere. Put this in a dedicated test tsconfig instead.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@indexer/tsconfig.base.json` at line 23, The shared tsconfig is incorrectly
applying Vitest globals to all packages through the base config, so move the
vitest/globals entry out of tsconfig.base.json and into a dedicated test-only
tsconfig. Update the relevant package test configuration to extend the base
settings while keeping normal source builds for indexer/common,
indexer/distributions, and indexer/streams free of test ambient types, and
verify any package-specific ambient types are still declared where needed.
Resolves the foundational indexer issues:
Fixes #27
Fixes #28
Fixes #31
Fixes #32
Summary by CodeRabbit
New Features
Bug Fixes
Tests