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

typeorm / typeorm / 19549987525

20 Nov 2025 08:11PM UTC coverage: 80.769% (+4.3%) from 76.433%
19549987525

push

github

web-flow
ci: run tests on commits to master and next (#11783)

Co-authored-by: Oleg "OSA413" Sokolov <OSA413@users.noreply.github.com>

26500 of 32174 branches covered (82.36%)

Branch coverage included in aggregate %.

91252 of 113615 relevant lines covered (80.32%)

88980.79 hits per line

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

76.06
/src/driver/sqljs/SqljsDriver.ts
1
import { AbstractSqliteDriver } from "../sqlite-abstract/AbstractSqliteDriver"
26✔
2
import { SqljsConnectionOptions } from "./SqljsConnectionOptions"
26✔
3
import { SqljsQueryRunner } from "./SqljsQueryRunner"
26✔
4
import { QueryRunner } from "../../query-runner/QueryRunner"
26✔
5
import { DataSource } from "../../data-source/DataSource"
26✔
6
import { DriverPackageNotInstalledError } from "../../error/DriverPackageNotInstalledError"
26✔
7
import { DriverOptionNotSetError } from "../../error/DriverOptionNotSetError"
26✔
8
import { PlatformTools } from "../../platform/PlatformTools"
26✔
9
import { EntityMetadata } from "../../metadata/EntityMetadata"
26✔
10
import { OrmUtils } from "../../util/OrmUtils"
26✔
11
import { ObjectLiteral } from "../../common/ObjectLiteral"
26✔
12
import { ReplicationMode } from "../types/ReplicationMode"
26✔
13
import { TypeORMError } from "../../error"
26✔
14

26✔
15
// This is needed to satisfy the typescript compiler.
26✔
16
interface Window {
26✔
17
    SQL: any
26✔
18
    localforage: any
26✔
19
}
26✔
20
declare let window: Window
26✔
21

26✔
22
export class SqljsDriver extends AbstractSqliteDriver {
26✔
23
    // The driver specific options.
26✔
24
    options: SqljsConnectionOptions
26✔
25

26✔
26
    // -------------------------------------------------------------------------
26✔
27
    // Constructor
26✔
28
    // -------------------------------------------------------------------------
26✔
29

26✔
30
    constructor(connection: DataSource) {
26✔
31
        super(connection)
1,290✔
32

1,290✔
33
        // If autoSave is enabled by user, location or autoSaveCallback have to be set
1,290✔
34
        // because either autoSave saves to location or calls autoSaveCallback.
1,290✔
35
        if (
1,290✔
36
            this.options.autoSave &&
1,290✔
37
            !this.options.location &&
1,290✔
38
            !this.options.autoSaveCallback
3✔
39
        ) {
1,290!
40
            throw new DriverOptionNotSetError(`location or autoSaveCallback`)
×
41
        }
×
42

1,290✔
43
        // load sql.js package
1,290✔
44
        this.loadDependencies()
1,290✔
45
    }
1,290✔
46

26✔
47
    // -------------------------------------------------------------------------
26✔
48
    // Public Methods
26✔
49
    // -------------------------------------------------------------------------
26✔
50

26✔
51
    /**
26✔
52
     * Performs connection to the database.
26✔
53
     */
26✔
54
    async connect(): Promise<void> {
26✔
55
        this.databaseConnection = await this.createDatabaseConnection()
1,290✔
56
    }
1,290✔
57

26✔
58
    /**
26✔
59
     * Closes connection with database.
26✔
60
     */
26✔
61
    async disconnect(): Promise<void> {
26✔
62
        this.queryRunner = undefined
1,290✔
63
        this.databaseConnection.close()
1,290✔
64
    }
1,290✔
65

26✔
66
    /**
26✔
67
     * Creates a query runner used to execute database queries.
26✔
68
     */
26✔
69
    createQueryRunner(mode: ReplicationMode): QueryRunner {
26✔
70
        if (!this.queryRunner) this.queryRunner = new SqljsQueryRunner(this)
32,718✔
71

32,718✔
72
        return this.queryRunner
32,718✔
73
    }
32,718✔
74

26✔
75
    /**
26✔
76
     * Loads a database from a given file (Node.js), local storage key (browser) or array.
26✔
77
     * This will delete the current database!
26✔
78
     */
26✔
79
    async load(
26✔
80
        fileNameOrLocalStorageOrData: string | Uint8Array,
12✔
81
        checkIfFileOrLocalStorageExists: boolean = true,
12✔
82
    ): Promise<any> {
12✔
83
        if (typeof fileNameOrLocalStorageOrData === "string") {
12✔
84
            // content has to be loaded
12✔
85
            if (PlatformTools.type === "node") {
12✔
86
                // Node.js
12✔
87
                // fileNameOrLocalStorageOrData should be a path to the file
12✔
88
                if (PlatformTools.fileExist(fileNameOrLocalStorageOrData)) {
12✔
89
                    const database = PlatformTools.readFileSync(
6✔
90
                        fileNameOrLocalStorageOrData,
6✔
91
                    )
6✔
92
                    return this.createDatabaseConnectionWithImport(database)
6✔
93
                } else if (checkIfFileOrLocalStorageExists) {
6✔
94
                    throw new TypeORMError(
3✔
95
                        `File ${fileNameOrLocalStorageOrData} does not exist`,
3✔
96
                    )
3✔
97
                } else {
3✔
98
                    // File doesn't exist and checkIfFileOrLocalStorageExists is set to false.
3✔
99
                    // Therefore open a database without importing an existing file.
3✔
100
                    // File will be written on first write operation.
3✔
101
                    return this.createDatabaseConnectionWithImport()
3✔
102
                }
3✔
103
            } else {
12!
104
                // browser
×
105
                // fileNameOrLocalStorageOrData should be a local storage / indexedDB key
×
106
                let localStorageContent = null
×
107
                if (this.options.useLocalForage) {
×
108
                    if (window.localforage) {
×
109
                        localStorageContent = await window.localforage.getItem(
×
110
                            fileNameOrLocalStorageOrData,
×
111
                        )
×
112
                    } else {
×
113
                        throw new TypeORMError(
×
114
                            `localforage is not defined - please import localforage.js into your site`,
×
115
                        )
×
116
                    }
×
117
                } else {
×
118
                    localStorageContent =
×
119
                        PlatformTools.getGlobalVariable().localStorage.getItem(
×
120
                            fileNameOrLocalStorageOrData,
×
121
                        )
×
122
                }
×
123

×
124
                if (localStorageContent != null) {
×
125
                    // localStorage value exists.
×
126
                    return this.createDatabaseConnectionWithImport(
×
127
                        JSON.parse(localStorageContent),
×
128
                    )
×
129
                } else if (checkIfFileOrLocalStorageExists) {
×
130
                    throw new TypeORMError(
×
131
                        `File ${fileNameOrLocalStorageOrData} does not exist`,
×
132
                    )
×
133
                } else {
×
134
                    // localStorage value doesn't exist and checkIfFileOrLocalStorageExists is set to false.
×
135
                    // Therefore open a database without importing anything.
×
136
                    // localStorage value will be written on first write operation.
×
137
                    return this.createDatabaseConnectionWithImport()
×
138
                }
×
139
            }
×
140
        } else {
12!
141
            return this.createDatabaseConnectionWithImport(
×
142
                fileNameOrLocalStorageOrData,
×
143
            )
×
144
        }
×
145
    }
12✔
146

26✔
147
    /**
26✔
148
     * Saved the current database to the given file (Node.js), local storage key (browser) or
26✔
149
     * indexedDB key (browser with enabled useLocalForage option).
26✔
150
     * If no location path is given, the location path in the options (if specified) will be used.
26✔
151
     */
26✔
152
    async save(location?: string) {
26✔
153
        if (!location && !this.options.location) {
42!
154
            throw new TypeORMError(
×
155
                `No location is set, specify a location parameter or add the location option to your configuration`,
×
156
            )
×
157
        }
×
158

42✔
159
        let path = ""
42✔
160
        if (location) {
42✔
161
            path = location
3✔
162
        } else if (this.options.location) {
42✔
163
            path = this.options.location
39✔
164
        }
39✔
165

42✔
166
        if (PlatformTools.type === "node") {
42✔
167
            try {
42✔
168
                const content = Buffer.from(this.databaseConnection.export())
42✔
169
                await PlatformTools.writeFile(path, content)
42✔
170
            } catch (e) {
42!
171
                throw new TypeORMError(`Could not save database, error: ${e}`)
×
172
            }
×
173
        } else {
42!
174
            const database: Uint8Array = this.databaseConnection.export()
×
175
            // convert Uint8Array to number array to improve local-storage storage
×
176
            const databaseArray = [].slice.call(database)
×
177
            if (this.options.useLocalForage) {
×
178
                if (window.localforage) {
×
179
                    await window.localforage.setItem(
×
180
                        path,
×
181
                        JSON.stringify(databaseArray),
×
182
                    )
×
183
                } else {
×
184
                    throw new TypeORMError(
×
185
                        `localforage is not defined - please import localforage.js into your site`,
×
186
                    )
×
187
                }
×
188
            } else {
×
189
                PlatformTools.getGlobalVariable().localStorage.setItem(
×
190
                    path,
×
191
                    JSON.stringify(databaseArray),
×
192
                )
×
193
            }
×
194
        }
×
195
    }
42✔
196

26✔
197
    /**
26✔
198
     * This gets called by the QueryRunner when a change to the database is made.
26✔
199
     * If a custom autoSaveCallback is specified, it get's called with the database as Uint8Array,
26✔
200
     * otherwise the save method is called which saves it to file (Node.js), local storage (browser)
26✔
201
     * or indexedDB (browser with enabled useLocalForage option).
26✔
202
     * Don't auto-save when in transaction as the call to export will end the current transaction
26✔
203
     */
26✔
204
    async autoSave() {
26✔
205
        if (this.options.autoSave && !this.queryRunner?.isTransactionActive) {
34,128✔
206
            if (this.options.autoSaveCallback) {
63✔
207
                await this.options.autoSaveCallback(this.export())
24✔
208
            } else {
63✔
209
                await this.save()
39✔
210
            }
39✔
211
        }
63✔
212
    }
34,128✔
213

26✔
214
    /**
26✔
215
     * Returns the current database as Uint8Array.
26✔
216
     */
26✔
217
    export(): Uint8Array {
26✔
218
        return this.databaseConnection.export()
27✔
219
    }
27✔
220

26✔
221
    /**
26✔
222
     * Creates generated map of values generated or returned by database after INSERT query.
26✔
223
     */
26✔
224
    createGeneratedMap(metadata: EntityMetadata, insertResult: any) {
26✔
225
        const generatedMap = metadata.generatedColumns.reduce(
28,044✔
226
            (map, generatedColumn) => {
28,044✔
227
                // seems to be the only way to get the inserted id, see https://github.com/kripken/sql.js/issues/77
10,659✔
228
                if (
10,659✔
229
                    generatedColumn.isPrimary &&
10,659✔
230
                    generatedColumn.generationStrategy === "increment"
10,659✔
231
                ) {
10,659✔
232
                    const query = "SELECT last_insert_rowid()"
10,326✔
233
                    try {
10,326✔
234
                        const result = this.databaseConnection.exec(query)
10,326✔
235
                        this.connection.logger.logQuery(query)
10,326✔
236
                        return OrmUtils.mergeDeep(
10,326✔
237
                            map,
10,326✔
238
                            generatedColumn.createValueMap(
10,326✔
239
                                result[0].values[0][0],
10,326✔
240
                            ),
10,326✔
241
                        )
10,326✔
242
                    } catch (e) {
10,326!
243
                        this.connection.logger.logQueryError(e, query, [])
×
244
                    }
×
245
                }
10,326✔
246

333✔
247
                return map
333✔
248
            },
28,044✔
249
            {} as ObjectLiteral,
28,044✔
250
        )
28,044✔
251

28,044✔
252
        return Object.keys(generatedMap).length > 0 ? generatedMap : undefined
28,044✔
253
    }
28,044✔
254

26✔
255
    // -------------------------------------------------------------------------
26✔
256
    // Protected Methods
26✔
257
    // -------------------------------------------------------------------------
26✔
258

26✔
259
    /**
26✔
260
     * Creates connection with the database.
26✔
261
     * If the location option is set, the database is loaded first.
26✔
262
     */
26✔
263
    protected createDatabaseConnection(): Promise<any> {
26✔
264
        if (this.options.location) {
1,290✔
265
            return this.load(this.options.location, false)
3✔
266
        }
3✔
267

1,287✔
268
        return this.createDatabaseConnectionWithImport(this.options.database)
1,287✔
269
    }
1,287✔
270

26✔
271
    /**
26✔
272
     * Creates connection with an optional database.
26✔
273
     * If database is specified it is loaded, otherwise a new empty database is created.
26✔
274
     */
26✔
275
    protected async createDatabaseConnectionWithImport(
26✔
276
        database?: Uint8Array,
1,296✔
277
    ): Promise<any> {
1,296✔
278
        // sql.js < 1.0 exposes an object with a `Database` method.
1,296✔
279
        const isLegacyVersion = typeof this.sqlite.Database === "function"
1,296✔
280
        const sqlite = isLegacyVersion
1,296✔
281
            ? this.sqlite
1,296!
282
            : await this.sqlite(this.options.sqlJsConfig)
1,296✔
283
        if (database && database.length > 0) {
1,296✔
284
            this.databaseConnection = new sqlite.Database(database)
6✔
285
        } else {
1,296✔
286
            this.databaseConnection = new sqlite.Database()
1,290✔
287
        }
1,290✔
288

1,296✔
289
        this.databaseConnection.exec(`PRAGMA foreign_keys = ON`)
1,296✔
290

1,296✔
291
        return this.databaseConnection
1,296✔
292
    }
1,296✔
293

26✔
294
    /**
26✔
295
     * If driver dependency is not given explicitly, then try to load it via "require".
26✔
296
     */
26✔
297
    protected loadDependencies(): void {
26✔
298
        if (PlatformTools.type === "browser") {
1,290!
299
            const sqlite = this.options.driver || window.SQL
×
300
            this.sqlite = sqlite
×
301
        } else {
1,290✔
302
            try {
1,290✔
303
                const sqlite =
1,290✔
304
                    this.options.driver || PlatformTools.load("sql.js")
1,290✔
305
                this.sqlite = sqlite
1,290✔
306
            } catch (e) {
1,290!
307
                throw new DriverPackageNotInstalledError("sql.js", "sql.js")
×
308
            }
×
309
        }
1,290✔
310
    }
1,290✔
311
}
26✔
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