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

typeorm / typeorm / 23390157208

21 Mar 2026 10:26PM UTC coverage: 56.678% (-16.6%) from 73.277%
23390157208

Pull #12252

github

web-flow
Merge 5b60ba41c into 7038fa166
Pull Request #12252: fix: unskip cascade soft remove test

17767 of 26580 branches covered (66.84%)

Branch coverage included in aggregate %.

64033 of 117744 relevant lines covered (54.38%)

1514.83 hits per line

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

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

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

28✔
22
export class SqljsDriver extends AbstractSqliteDriver {
28✔
23
    // The driver specific options.
28✔
24
    declare options: SqljsDataSourceOptions
28✔
25

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

28✔
30
    constructor(connection: DataSource) {
28✔
31
        super(connection)
3✔
32

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

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

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

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

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

28✔
66
    /**
28✔
67
     * Creates a query runner used to execute database queries.
28✔
68
     * @param mode
28✔
69
     */
28✔
70
    createQueryRunner(mode: ReplicationMode): QueryRunner {
28✔
71
        if (!this.queryRunner) this.queryRunner = new SqljsQueryRunner(this)
48✔
72

48✔
73
        return this.queryRunner
48✔
74
    }
48✔
75

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

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

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

×
163
        let path = ""
×
164
        if (location) {
×
165
            path = location
×
166
        } else if (this.options.location) {
×
167
            path = this.options.location
×
168
        }
×
169

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

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

28✔
218
    /**
28✔
219
     * Returns the current database as Uint8Array.
28✔
220
     */
28✔
221
    export(): Uint8Array {
28✔
222
        return this.databaseConnection.export()
×
223
    }
×
224

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

×
253
                return map
×
254
            },
39✔
255
            {} as ObjectLiteral,
39✔
256
        )
39✔
257

39✔
258
        return Object.keys(generatedMap).length > 0 ? generatedMap : undefined
39✔
259
    }
39✔
260

28✔
261
    // -------------------------------------------------------------------------
28✔
262
    // Protected Methods
28✔
263
    // -------------------------------------------------------------------------
28✔
264

28✔
265
    /**
28✔
266
     * Creates connection with the database.
28✔
267
     * If the location option is set, the database is loaded first.
28✔
268
     */
28✔
269
    protected createDatabaseConnection(): Promise<any> {
28✔
270
        if (this.options.location) {
3!
271
            return this.load(this.options.location, false)
×
272
        }
×
273

3✔
274
        return this.createDatabaseConnectionWithImport(this.options.database)
3✔
275
    }
3✔
276

28✔
277
    /**
28✔
278
     * Creates connection with an optional database.
28✔
279
     * If database is specified it is loaded, otherwise a new empty database is created.
28✔
280
     * @param database
28✔
281
     */
28✔
282
    protected async createDatabaseConnectionWithImport(
28✔
283
        database?: Uint8Array,
3✔
284
    ): Promise<any> {
3✔
285
        // sql.js < 1.0 exposes an object with a `Database` method.
3✔
286
        const isLegacyVersion = typeof this.sqlite.Database === "function"
3✔
287
        const sqlite = isLegacyVersion
3✔
288
            ? this.sqlite
3!
289
            : await this.sqlite(this.options.sqlJsConfig)
3✔
290
        if (database && database.length > 0) {
3!
291
            this.databaseConnection = new sqlite.Database(database)
×
292
        } else {
3✔
293
            this.databaseConnection = new sqlite.Database()
3✔
294
        }
3✔
295

3✔
296
        this.databaseConnection.exec(`PRAGMA foreign_keys = ON`)
3✔
297

3✔
298
        return this.databaseConnection
3✔
299
    }
3✔
300

28✔
301
    /**
28✔
302
     * If driver dependency is not given explicitly, then try to load it via "require".
28✔
303
     */
28✔
304
    protected loadDependencies(): void {
28✔
305
        if (PlatformTools.type === "browser") {
3!
306
            const sqlite = this.options.driver || window.SQL
×
307
            this.sqlite = sqlite
×
308
        } else {
3✔
309
            try {
3✔
310
                const sqlite =
3✔
311
                    this.options.driver || PlatformTools.load("sql.js")
3✔
312
                this.sqlite = sqlite
3✔
313
            } catch (e) {
3!
314
                throw new DriverPackageNotInstalledError("sql.js", "sql.js")
×
315
            }
×
316
        }
3✔
317
    }
3✔
318
}
28✔
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