Skip to content

Commit fb5f0f0

Browse files
committed
fix: throws should reject
1 parent 91cf636 commit fb5f0f0

File tree

2 files changed

+82
-56
lines changed

2 files changed

+82
-56
lines changed

src/blockCache/sqlite.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ describe("createSqliteBlockCache", () => {
4141

4242
it("should throw if already initialized", async () => {
4343
await blockCache.init();
44-
await expect(blockCache.init()).rejects.toThrow("");
44+
await expect(blockCache.init()).rejects.toThrow("Already initialized");
4545
});
4646

4747
it("should save and retrieve a block by number", async () => {

src/blockCache/sqlite.ts

Lines changed: 81 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -16,28 +16,34 @@ interface Row {
1616
timestamp: number;
1717
}
1818

19+
type UninitializedState = { state: "uninitialized" };
20+
type InitializedState = {
21+
state: "initialized";
22+
db: Sqlite.Database;
23+
getTimestampByBlockNumberStmt: Sqlite.Statement;
24+
getBlockNumberByTimestampStmt: Sqlite.Statement;
25+
saveBlockStmt: Sqlite.Statement;
26+
getBeforeStmt: Sqlite.Statement;
27+
getAfterStmt: Sqlite.Statement;
28+
};
29+
type State = UninitializedState | InitializedState;
30+
1931
export function createSqliteBlockCache(opts: Options): BlockCache {
20-
let db: Sqlite.Database | null = null;
32+
let dbState: State = { state: "uninitialized" };
2133

22-
if (opts.tableName !== undefined && opts.tableName.match(/[^a-zA-Z0-9_]/)) {
23-
throw new Error(
24-
`Table name ${opts.tableName} contains invalid characters. Only alphanumeric and underscore characters are allowed.`
25-
);
34+
if (opts.tableName !== undefined && /[^a-zA-Z0-9_]/.test(opts.tableName)) {
35+
throw new Error(`Table name ${opts.tableName} has invalid characters.`);
2636
}
2737

2838
const tableName = opts.tableName ?? defaultTableName;
2939

3040
return {
31-
init(): Promise<void> {
32-
if (db) {
41+
async init(): Promise<void> {
42+
if (dbState.state === "initialized") {
3343
throw new Error("Already initialized");
3444
}
3545

36-
if ("db" in opts) {
37-
db = opts.db;
38-
} else {
39-
db = new Sqlite(opts.dbPath);
40-
}
46+
const db = "db" in opts ? opts.db : new Sqlite(opts.dbPath);
4147

4248
db.exec("PRAGMA journal_mode = WAL;");
4349

@@ -56,85 +62,105 @@ export function createSqliteBlockCache(opts: Options): BlockCache {
5662
ON ${tableName} (chainId, timestamp, blockNumber DESC);`
5763
);
5864

65+
dbState = {
66+
state: "initialized",
67+
db,
68+
getTimestampByBlockNumberStmt: db.prepare(
69+
`SELECT * FROM ${tableName} WHERE chainId = ? AND blockNumber = ?`
70+
),
71+
getBlockNumberByTimestampStmt: db.prepare(
72+
`SELECT * FROM ${tableName} WHERE chainId = ? AND timestamp = ?`
73+
),
74+
saveBlockStmt: db.prepare(
75+
`INSERT OR REPLACE INTO ${tableName} (chainId, blockNumber, timestamp) VALUES (?, ?, ?)`
76+
),
77+
getBeforeStmt: db.prepare(
78+
`SELECT * FROM ${tableName} WHERE chainId = ? AND timestamp < ? ORDER BY timestamp DESC, blockNumber DESC LIMIT 1`
79+
),
80+
getAfterStmt: db.prepare(
81+
`SELECT * FROM ${tableName} WHERE chainId = ? AND timestamp >= ? ORDER BY timestamp ASC, blockNumber ASC LIMIT 1`
82+
),
83+
};
84+
5985
return Promise.resolve();
6086
},
6187

62-
getTimestampByBlockNumber(chainId, blockNumber): Promise<number | null> {
63-
if (!db) {
88+
async getTimestampByBlockNumber(
89+
chainId,
90+
blockNumber
91+
): Promise<number | null> {
92+
if (dbState.state === "uninitialized") {
6493
throw new Error("SQLite database not initialized");
6594
}
6695

67-
const row = db
68-
.prepare(
69-
`SELECT * FROM ${tableName} WHERE chainId = ? AND blockNumber = ?`
70-
)
71-
.get(chainId, blockNumber.toString()) as Row | undefined;
96+
const row = dbState.getTimestampByBlockNumberStmt.get(
97+
chainId,
98+
blockNumber.toString()
99+
) as Row | undefined;
72100

73-
return Promise.resolve(row?.timestamp ?? null);
101+
return Promise.resolve(row ? row.timestamp : null);
74102
},
75103

76-
getBlockNumberByTimestamp(chainId, timestamp): Promise<bigint | null> {
77-
if (!db) {
104+
async getBlockNumberByTimestamp(
105+
chainId,
106+
timestamp
107+
): Promise<bigint | null> {
108+
if (dbState.state === "uninitialized") {
78109
throw new Error("SQLite database not initialized");
79110
}
80111

81-
const row = db
82-
.prepare(
83-
`SELECT * FROM ${tableName} WHERE chainId = ? AND timestamp = ?`
84-
)
85-
.get(chainId, timestamp) as Row | undefined;
112+
const row = dbState.getBlockNumberByTimestampStmt.get(
113+
chainId,
114+
timestamp
115+
) as Block | undefined;
86116

87-
return Promise.resolve(row?.blockNumber ? BigInt(row.blockNumber) : null);
117+
return Promise.resolve(row ? BigInt(row.blockNumber) : null);
88118
},
89119

90-
saveBlock(block: Block): Promise<void> {
91-
if (!db) {
120+
async saveBlock(block: Block): Promise<void> {
121+
if (dbState.state === "uninitialized") {
92122
throw new Error("SQLite database not initialized");
93123
}
94124

95-
db.prepare(
96-
`INSERT OR REPLACE INTO ${tableName} (chainId, blockNumber, timestamp) VALUES (?, ?, ?)`
97-
).run(block.chainId, block.blockNumber.toString(), block.timestampInSecs);
125+
dbState.saveBlockStmt.run(
126+
block.chainId,
127+
block.blockNumber.toString(),
128+
block.timestampInSecs
129+
);
98130

99131
return Promise.resolve();
100132
},
101133

102-
getClosestBoundsForTimestamp(
134+
async getClosestBoundsForTimestamp(
103135
chainId,
104136
timestamp
105-
): Promise<{
106-
before: Block | null;
107-
after: Block | null;
108-
}> {
109-
if (!db) {
137+
): Promise<{ before: Block | null; after: Block | null }> {
138+
if (dbState.state === "uninitialized") {
110139
throw new Error("SQLite database not initialized");
111140
}
112-
const before = db
113-
.prepare(
114-
`SELECT * FROM ${tableName} WHERE chainId = ? AND timestamp < ? ORDER BY timestamp DESC, blockNumber DESC LIMIT 1`
115-
)
116-
.get(chainId, timestamp) as Row | undefined;
117141

118-
const after = db
119-
.prepare(
120-
`SELECT * FROM ${tableName} WHERE chainId = ? AND timestamp >= ? ORDER BY timestamp ASC, blockNumber ASC LIMIT 1`
121-
)
122-
.get(chainId, timestamp) as Row | undefined;
142+
const before = dbState.getBeforeStmt.get(chainId, timestamp) as
143+
| Row
144+
| undefined;
145+
146+
const after = dbState.getAfterStmt.get(chainId, timestamp) as
147+
| Row
148+
| undefined;
123149

124150
return Promise.resolve({
125151
before: before
126-
? ({
127-
...before,
152+
? {
153+
chainId: before.chainId,
128154
timestampInSecs: before.timestamp,
129155
blockNumber: BigInt(before.blockNumber),
130-
} as Block)
156+
}
131157
: null,
132158
after: after
133-
? ({
134-
...after,
159+
? {
160+
chainId: after.chainId,
135161
timestampInSecs: after.timestamp,
136162
blockNumber: BigInt(after.blockNumber),
137-
} as Block)
163+
}
138164
: null,
139165
});
140166
},

0 commit comments

Comments
 (0)