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

typeorm / typeorm / 15219332477

23 May 2025 09:13PM UTC coverage: 17.216% (-59.1%) from 76.346%
15219332477

Pull #11332

github

naorpeled
cr comments - move if block
Pull Request #11332: feat: add new undefined and null behavior flags

1603 of 12759 branches covered (12.56%)

Branch coverage included in aggregate %.

0 of 31 new or added lines in 3 files covered. (0.0%)

14132 existing lines in 166 files now uncovered.

4731 of 24033 relevant lines covered (19.69%)

60.22 hits per line

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

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

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

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

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

30
    constructor(connection: DataSource) {
UNCOV
31
        super(connection)
×
32

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

43
        // load sql.js package
UNCOV
44
        this.loadDependencies()
×
45
    }
46

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

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

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

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

UNCOV
72
        return this.queryRunner
×
73
    }
74

75
    /**
76
     * Loads a database from a given file (Node.js), local storage key (browser) or array.
77
     * This will delete the current database!
78
     */
79
    async load(
80
        fileNameOrLocalStorageOrData: string | Uint8Array,
81
        checkIfFileOrLocalStorageExists: boolean = true,
×
82
    ): Promise<any> {
UNCOV
83
        if (typeof fileNameOrLocalStorageOrData === "string") {
×
84
            // content has to be loaded
UNCOV
85
            if (PlatformTools.type === "node") {
×
86
                // Node.js
87
                // fileNameOrLocalStorageOrData should be a path to the file
UNCOV
88
                if (PlatformTools.fileExist(fileNameOrLocalStorageOrData)) {
×
UNCOV
89
                    const database = PlatformTools.readFileSync(
×
90
                        fileNameOrLocalStorageOrData,
91
                    )
UNCOV
92
                    return this.createDatabaseConnectionWithImport(database)
×
UNCOV
93
                } else if (checkIfFileOrLocalStorageExists) {
×
UNCOV
94
                    throw new TypeORMError(
×
95
                        `File ${fileNameOrLocalStorageOrData} does not exist`,
96
                    )
97
                } else {
98
                    // File doesn't exist and checkIfFileOrLocalStorageExists is set to false.
99
                    // Therefore open a database without importing an existing file.
100
                    // File will be written on first write operation.
UNCOV
101
                    return this.createDatabaseConnectionWithImport()
×
102
                }
103
            } else {
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 {
141
            return this.createDatabaseConnectionWithImport(
×
142
                fileNameOrLocalStorageOrData,
143
            )
144
        }
145
    }
146

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

UNCOV
159
        let path = ""
×
UNCOV
160
        if (location) {
×
UNCOV
161
            path = location
×
UNCOV
162
        } else if (this.options.location) {
×
UNCOV
163
            path = this.options.location
×
164
        }
165

UNCOV
166
        if (PlatformTools.type === "node") {
×
UNCOV
167
            try {
×
UNCOV
168
                const content = Buffer.from(this.databaseConnection.export())
×
UNCOV
169
                await PlatformTools.writeFile(path, content)
×
170
            } catch (e) {
171
                throw new TypeORMError(`Could not save database, error: ${e}`)
×
172
            }
173
        } else {
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
    }
196

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

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

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

UNCOV
247
                return map
×
248
            },
249
            {} as ObjectLiteral,
250
        )
251

UNCOV
252
        return Object.keys(generatedMap).length > 0 ? generatedMap : undefined
×
253
    }
254

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

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

UNCOV
268
        return this.createDatabaseConnectionWithImport(this.options.database)
×
269
    }
270

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

UNCOV
289
        this.databaseConnection.exec(`PRAGMA foreign_keys = ON`)
×
290

UNCOV
291
        return this.databaseConnection
×
292
    }
293

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

© 2025 Coveralls, Inc