• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

AJGranowski / preceding-tag-action / 25261660619

02 May 2026 08:54PM UTC coverage: 93.454% (-0.3%) from 93.711%
25261660619

push

github

web-flow
Add request cache (#77)

173 of 184 branches covered (94.02%)

Branch coverage included in aggregate %.

178 of 191 new or added lines in 5 files covered. (93.19%)

498 of 534 relevant lines covered (93.26%)

11.89 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.0
/src/ETagRequestCacheDB.ts
1
import { DatabaseSync as SQLiteDB } from "node:sqlite";
1✔
2
import type { SQLOutputValue } from "node:sqlite";
3
import { join as pathJoin } from "path";
1✔
4

5
import type { CachedResponse } from "./types/CachedResponse";
6

7
const CACHE_DIR = pathJoin("/", "tmp", ".cache", "preceding-tag-action");
1✔
8
const REQUEST_CACHE_FILE = "request-cache.sqlite3";
1✔
9

10
/**
11
 * ```
12
 * const cache = new CacheDB();
13
 * cache.open();
14
 * cache.queryETag(...);
15
 * cache.queryResponse(...); // on cache hit
16
 * cache.insertRequestResponse(...); // on cache miss
17
 * cache.close();
18
 * ```
19
 */
20
class ETagRequestCacheDB {
1✔
21
    private readonly db: SQLiteDB;
1✔
22

23
    constructor(db = new SQLiteDB(pathJoin(CACHE_DIR, REQUEST_CACHE_FILE), {open: false})) {
1✔
24
        this.db = db;
14✔
25
    }
14✔
26

27
    async close(): Promise<void> {
1✔
28
        this.db.close();
3✔
29
    }
3✔
30

31
    async isOpen(): Promise<boolean> {
1✔
32
        return this.db.isOpen;
14✔
33
    }
14✔
34

35
    /**
36
     * Query the ETag related with the given request hash.
37
     * Returns null if an ETag does not exist.
38
     */
39
    async matchETag(requestHash: string): Promise<string | null> {
1✔
40
        const statement = this.db.prepare("SELECT etag FROM request_response WHERE request_hash=? LIMIT 1");
3✔
41
        const result = statement.get(requestHash);
3✔
42
        if (result == null || result["etag"] == null) {
3✔
43
            return null;
1✔
44
        }
1✔
45

46
        return result["etag"].toString();
2✔
47
    }
3✔
48

49
    /**
50
     * Query the cached response associated with the given ETag.
51
     * Returns null if a cached response does not exist.
52
     */
53
    async matchResponse(eTag: string): Promise<CachedResponse | null> {
1✔
54
        const statement = this.db.prepare("SELECT response, timestamp_z_ms FROM request_response WHERE etag=? LIMIT 1");
3✔
55
        const result = statement.get(eTag);
3✔
56
        if (result == null || result["response"] == null || result["timestamp_z_ms"] == null) {
3✔
57
            return null;
1✔
58
        }
1✔
59

60
        return {
2✔
61
            timestampZMS: this.sqlOutputValueToInteger(result["timestamp_z_ms"])!,
2✔
62
            response: JSON.parse(result["response"].toString())
2✔
63
        };
2✔
64
    }
3✔
65

66
    /**
67
     * Open the database and initialize tables.
68
     */
69
    async open(): Promise<void> {
1✔
70
        this.db.open();
5✔
71
        this.initialize();
5✔
72
    }
5✔
73

74
    /**
75
     * Insert a network request & response.
76
     */
77
    async put(requestHash: string, eTag: string, response: object, timestampZMS: number): Promise<void> {
1✔
78
        const pruneExisting = this.db.prepare("DELETE FROM request_response WHERE request_hash=? OR etag=?");
3✔
79
        const statement = this.db.prepare("INSERT INTO request_response (request_hash, etag, response, timestamp_z_ms) VALUES (?, ?, ?, ?)");
3✔
80
        pruneExisting.run(requestHash, eTag);
3✔
81
        statement.run(requestHash, eTag, JSON.stringify(response), Math.round(timestampZMS));
3✔
82
    }
3✔
83

84
    /**
85
     * Initialize tables if they don't exist.
86
     */
87
    private async initialize(): Promise<void> {
1✔
88
        this.db.exec(
5✔
89
            "CREATE TABLE IF NOT EXISTS request_response(" +
5✔
90
            "request_hash TEXT UNIQUE NOT NULL," +
91
            "etag TEXT UNIQUE NOT NULL," +
92
            "response TEXT NOT NULL," +
93
            "timestamp_z_ms INTEGER," +
94
            "PRIMARY KEY (request_hash, etag)" +
95
            ") STRICT"
96
        );
5✔
97
    }
5✔
98

99
    /**
100
     * Parse the SQL Output into a JavaScript primitive
101
     */
102
    private sqlOutputValueToInteger(value: Exclude<SQLOutputValue, null>): number {
1✔
103
        if (typeof value === "number") {
2✔
104
            return value;
2✔
105
        }
2!
106

NEW
107
        if (typeof value === "string") {
×
NEW
108
            const result = Number.parseFloat(value);
×
NEW
109
            if (result.toString() === value) {
×
NEW
110
                return result;
×
NEW
111
            }
×
NEW
112
        }
×
113

NEW
114
        throw new TypeError(`Unexpected type "${typeof value}" returned by SQLite`);
×
115
    }
2✔
116
}
1✔
117

118
export { ETagRequestCacheDB };
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc