• 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

94.7
/src/query-builder/QueryExpressionMap.ts
1
import { Alias } from "./Alias"
26✔
2
import { ObjectLiteral } from "../common/ObjectLiteral"
26✔
3
import { OrderByCondition } from "../find-options/OrderByCondition"
26✔
4
import { JoinAttribute } from "./JoinAttribute"
26✔
5
import { QueryBuilder } from "./QueryBuilder"
26✔
6
import { QueryBuilderCteOptions } from "./QueryBuilderCte"
26✔
7
import { RelationIdAttribute } from "./relation-id/RelationIdAttribute"
26✔
8
import { RelationCountAttribute } from "./relation-count/RelationCountAttribute"
26✔
9
import { DataSource } from "../data-source/DataSource"
26✔
10
import { EntityMetadata } from "../metadata/EntityMetadata"
26✔
11
import { SelectQuery } from "./SelectQuery"
26✔
12
import { ColumnMetadata } from "../metadata/ColumnMetadata"
26✔
13
import { RelationMetadata } from "../metadata/RelationMetadata"
26✔
14
import { SelectQueryBuilderOption } from "./SelectQueryBuilderOption"
26✔
15
import { TypeORMError } from "../error"
26✔
16
import { WhereClause } from "./WhereClause"
26✔
17
import { UpsertType } from "../driver/types/UpsertType"
26✔
18
import { CockroachConnectionOptions } from "../driver/cockroachdb/CockroachConnectionOptions"
26✔
19

26✔
20
/**
26✔
21
 * Contains all properties of the QueryBuilder that needs to be build a final query.
26✔
22
 */
26✔
23
export class QueryExpressionMap {
26✔
24
    // -------------------------------------------------------------------------
26✔
25
    // Public Properties
26✔
26
    // -------------------------------------------------------------------------
26✔
27

26✔
28
    /**
26✔
29
     * Strategy to load relations.
26✔
30
     */
26✔
31
    relationLoadStrategy: "join" | "query" = "join"
26✔
32

26✔
33
    /**
26✔
34
     * Indicates if QueryBuilder used to select entities and not a raw results.
26✔
35
     */
26✔
36
    queryEntity: boolean = false
26✔
37

26✔
38
    /**
26✔
39
     * Main alias is a main selection object selected by QueryBuilder.
26✔
40
     */
26✔
41
    mainAlias?: Alias
26✔
42

26✔
43
    /**
26✔
44
     * All aliases (including main alias) used in the query.
26✔
45
     */
26✔
46
    aliases: Alias[] = []
26✔
47

26✔
48
    /**
26✔
49
     * Represents query type. QueryBuilder is able to build SELECT, UPDATE and DELETE queries.
26✔
50
     */
26✔
51
    queryType:
26✔
52
        | "select"
26✔
53
        | "update"
26✔
54
        | "delete"
26✔
55
        | "insert"
26✔
56
        | "relation"
26✔
57
        | "soft-delete"
26✔
58
        | "restore" = "select"
26✔
59

26✔
60
    /**
26✔
61
     * Data needs to be SELECT-ed.
26✔
62
     */
26✔
63
    selects: SelectQuery[] = []
26✔
64

26✔
65
    /**
26✔
66
     * Max execution time in millisecond.
26✔
67
     */
26✔
68
    maxExecutionTime: number = 0
26✔
69

26✔
70
    /**
26✔
71
     * Whether SELECT is DISTINCT.
26✔
72
     */
26✔
73
    selectDistinct: boolean = false
26✔
74

26✔
75
    /**
26✔
76
     * SELECT DISTINCT ON query (postgres).
26✔
77
     */
26✔
78
    selectDistinctOn: string[] = []
26✔
79

26✔
80
    /**
26✔
81
     * FROM-s to be selected.
26✔
82
     */
26✔
83
    // froms: { target: string, alias: string }[] = [];
26✔
84

26✔
85
    /**
26✔
86
     * If update query was used, it needs "update set" - properties which will be updated by this query.
26✔
87
     * If insert query was used, it needs "insert set" - values that needs to be inserted.
26✔
88
     */
26✔
89
    valuesSet?: ObjectLiteral | ObjectLiteral[]
26✔
90

26✔
91
    /**
26✔
92
     * Optional returning (or output) clause for insert, update or delete queries.
26✔
93
     */
26✔
94
    returning: string | string[]
26✔
95

26✔
96
    /**
26✔
97
     * Extra returning columns to be added to the returning statement if driver supports it.
26✔
98
     */
26✔
99
    extraReturningColumns: ColumnMetadata[] = []
26✔
100

26✔
101
    /**
26✔
102
     * Optional on conflict statement used in insertion query in postgres.
26✔
103
     */
26✔
104
    onConflict: string = ""
26✔
105

26✔
106
    /**
26✔
107
     * Optional on ignore statement used in insertion query in databases.
26✔
108
     */
26✔
109
    onIgnore: boolean = false
26✔
110

26✔
111
    /**
26✔
112
     * Optional on update statement used in insertion query in databases.
26✔
113
     */
26✔
114
    onUpdate: {
26✔
115
        conflict?: string | string[]
26✔
116
        columns?: string[]
26✔
117
        overwrite?: string[]
26✔
118
        skipUpdateIfNoValuesChanged?: boolean
26✔
119
        indexPredicate?: string
26✔
120
        upsertType?: UpsertType
26✔
121
        overwriteCondition?: WhereClause[]
26✔
122
    }
26✔
123

26✔
124
    /**
26✔
125
     * JOIN queries.
26✔
126
     */
26✔
127
    joinAttributes: JoinAttribute[] = []
26✔
128

26✔
129
    /**
26✔
130
     * RelationId queries.
26✔
131
     */
26✔
132
    relationIdAttributes: RelationIdAttribute[] = []
26✔
133

26✔
134
    /**
26✔
135
     * Relation count queries.
26✔
136
     */
26✔
137
    relationCountAttributes: RelationCountAttribute[] = []
26✔
138

26✔
139
    /**
26✔
140
     * WHERE queries.
26✔
141
     */
26✔
142
    wheres: WhereClause[] = []
26✔
143

26✔
144
    /**
26✔
145
     * HAVING queries.
26✔
146
     */
26✔
147
    havings: { type: "simple" | "and" | "or"; condition: string }[] = []
26✔
148

26✔
149
    /**
26✔
150
     * ORDER BY queries.
26✔
151
     */
26✔
152
    orderBys: OrderByCondition = {}
26✔
153

26✔
154
    /**
26✔
155
     * GROUP BY queries.
26✔
156
     */
26✔
157
    groupBys: string[] = []
26✔
158

26✔
159
    /**
26✔
160
     * LIMIT query.
26✔
161
     */
26✔
162
    limit?: number
26✔
163

26✔
164
    /**
26✔
165
     * OFFSET query.
26✔
166
     */
26✔
167
    offset?: number
26✔
168

26✔
169
    /**
26✔
170
     * Number of rows to skip of result using pagination.
26✔
171
     */
26✔
172
    skip?: number
26✔
173

26✔
174
    /**
26✔
175
     * Number of rows to take using pagination.
26✔
176
     */
26✔
177
    take?: number
26✔
178

26✔
179
    /**
26✔
180
     * Use certain index for the query.
26✔
181
     *
26✔
182
     * SELECT * FROM table_name USE INDEX (col1_index, col2_index) WHERE col1=1 AND col2=2 AND col3=3;
26✔
183
     */
26✔
184
    useIndex?: string
26✔
185

26✔
186
    /**
26✔
187
     * Locking mode.
26✔
188
     */
26✔
189
    lockMode?:
26✔
190
        | "optimistic"
26✔
191
        | "pessimistic_read"
26✔
192
        | "pessimistic_write"
26✔
193
        | "dirty_read"
26✔
194
        /*
26✔
195
            "pessimistic_partial_write" and "pessimistic_write_or_fail" are deprecated and
26✔
196
            will be removed in a future version.
26✔
197

26✔
198
            Use onLocked instead.
26✔
199
         */
26✔
200
        | "pessimistic_partial_write"
26✔
201
        | "pessimistic_write_or_fail"
26✔
202
        | "for_no_key_update"
26✔
203
        | "for_key_share"
26✔
204

26✔
205
    /**
26✔
206
     * Current version of the entity, used for locking.
26✔
207
     */
26✔
208
    lockVersion?: number | Date
26✔
209

26✔
210
    /**
26✔
211
     * Tables to be specified in the "FOR UPDATE OF" clause, referred by their alias
26✔
212
     */
26✔
213
    lockTables?: string[]
26✔
214

26✔
215
    /**
26✔
216
     * Modify behavior when encountering locked rows. NOWAIT or SKIP LOCKED
26✔
217
     */
26✔
218
    onLocked?: "nowait" | "skip_locked"
26✔
219

26✔
220
    /**
26✔
221
     * Indicates if soft-deleted rows should be included in entity result.
26✔
222
     * By default the soft-deleted rows are not included.
26✔
223
     */
26✔
224
    withDeleted: boolean = false
26✔
225

26✔
226
    /**
26✔
227
     * Parameters used to be escaped in final query.
26✔
228
     */
26✔
229
    parameters: ObjectLiteral = {}
26✔
230

26✔
231
    /**
26✔
232
     * Indicates if alias, table names and column names will be escaped by driver, or not.
26✔
233
     *
26✔
234
     * todo: rename to isQuotingDisabled, also think if it should be named "escaping"
26✔
235
     */
26✔
236
    disableEscaping: boolean = true
26✔
237

26✔
238
    /**
26✔
239
     * Indicates if virtual columns should be included in entity result.
26✔
240
     *
26✔
241
     * todo: what to do with it? is it properly used? what about persistence?
26✔
242
     */
26✔
243
    enableRelationIdValues: boolean = false
26✔
244

26✔
245
    /**
26✔
246
     * Extra where condition appended to the end of original where conditions with AND keyword.
26✔
247
     * Original condition will be wrapped into brackets.
26✔
248
     */
26✔
249
    extraAppendedAndWhereCondition: string = ""
26✔
250

26✔
251
    /**
26✔
252
     * Indicates if query builder creates a subquery.
26✔
253
     */
26✔
254
    subQuery: boolean = false
26✔
255

26✔
256
    /**
26✔
257
     * Indicates if property names are prefixed with alias names during property replacement.
26✔
258
     * By default this is enabled, however we need this because aliases are not supported in UPDATE and DELETE queries,
26✔
259
     * but user can use them in WHERE expressions.
26✔
260
     */
26✔
261
    aliasNamePrefixingEnabled: boolean = true
26✔
262

26✔
263
    /**
26✔
264
     * Indicates if query result cache is enabled or not.
26✔
265
     * It is undefined by default to avoid overriding the `alwaysEnabled` config
26✔
266
     */
26✔
267
    cache?: boolean
26✔
268

26✔
269
    /**
26✔
270
     * Time in milliseconds in which cache will expire.
26✔
271
     * If not set then global caching time will be used.
26✔
272
     */
26✔
273
    cacheDuration: number
26✔
274

26✔
275
    /**
26✔
276
     * Cache id.
26✔
277
     * Used to identifier your cache queries.
26✔
278
     */
26✔
279
    cacheId: string
26✔
280

26✔
281
    /**
26✔
282
     * Options that define QueryBuilder behaviour.
26✔
283
     */
26✔
284
    options: SelectQueryBuilderOption[] = []
26✔
285

26✔
286
    /**
26✔
287
     * Property path of relation to work with.
26✔
288
     * Used in relational query builder.
26✔
289
     */
26✔
290
    relationPropertyPath: string
26✔
291

26✔
292
    /**
26✔
293
     * Entity (target) which relations will be updated.
26✔
294
     */
26✔
295
    of: any | any[]
26✔
296

26✔
297
    /**
26✔
298
     * List of columns where data should be inserted.
26✔
299
     * Used in INSERT query.
26✔
300
     */
26✔
301
    insertColumns: string[] = []
26✔
302

26✔
303
    /**
26✔
304
     * Used if user wants to update or delete a specific entities.
26✔
305
     */
26✔
306
    whereEntities: ObjectLiteral[] = []
26✔
307

26✔
308
    /**
26✔
309
     * Indicates if entity must be updated after insertion / updation.
26✔
310
     * This may produce extra query or use RETURNING / OUTPUT statement (depend on database).
26✔
311
     */
26✔
312
    updateEntity: boolean = true
26✔
313

26✔
314
    /**
26✔
315
     * Indicates if listeners and subscribers must be called before and after query execution.
26✔
316
     */
26✔
317
    callListeners: boolean = true
26✔
318

26✔
319
    /**
26✔
320
     * Indicates if query must be wrapped into transaction.
26✔
321
     */
26✔
322
    useTransaction: boolean = false
26✔
323

26✔
324
    /**
26✔
325
     * Indicates if query should be time travel query
26✔
326
     * https://www.cockroachlabs.com/docs/stable/as-of-system-time.html
26✔
327
     */
26✔
328
    timeTravel?: boolean | string
26✔
329

26✔
330
    /**
26✔
331
     * Extra parameters.
26✔
332
     *
26✔
333
     * @deprecated Use standard parameters instead
26✔
334
     */
26✔
335
    nativeParameters: ObjectLiteral = {}
26✔
336

26✔
337
    /**
26✔
338
     * Query Comment to include extra information for debugging or other purposes.
26✔
339
     */
26✔
340
    comment?: string
26✔
341

26✔
342
    /**
26✔
343
     * Items from an entity that have been locally generated & are recorded here for later use.
26✔
344
     * Examples include the UUID generation when the database does not natively support it.
26✔
345
     * These are included in the entity index order.
26✔
346
     */
26✔
347
    locallyGenerated: { [key: number]: ObjectLiteral } = {}
26✔
348

26✔
349
    commonTableExpressions: {
26✔
350
        queryBuilder: QueryBuilder<any> | string
26✔
351
        alias: string
26✔
352
        options: QueryBuilderCteOptions
26✔
353
    }[] = []
26✔
354

26✔
355
    // -------------------------------------------------------------------------
26✔
356
    // Constructor
26✔
357
    // -------------------------------------------------------------------------
26✔
358

26✔
359
    constructor(protected connection: DataSource) {
26✔
360
        if (connection.options.relationLoadStrategy) {
1,118,876✔
361
            this.relationLoadStrategy = connection.options.relationLoadStrategy
120,408✔
362
        }
120,408✔
363

1,118,876✔
364
        this.timeTravel =
1,118,876✔
365
            (connection.options as CockroachConnectionOptions)
1,118,876✔
366
                ?.timeTravelQueries || false
1,118,876✔
367
    }
1,118,876✔
368

26✔
369
    // -------------------------------------------------------------------------
26✔
370
    // Accessors
26✔
371
    // -------------------------------------------------------------------------
26✔
372

26✔
373
    /**
26✔
374
     * Get all ORDER BY queries - if order by is specified by user then it uses them,
26✔
375
     * otherwise it uses default entity order by if it was set.
26✔
376
     */
26✔
377
    get allOrderBys() {
26✔
378
        if (
543,206✔
379
            !Object.keys(this.orderBys).length &&
543,206✔
380
            this.mainAlias!.hasMetadata &&
543,206✔
381
            this.options.indexOf("disable-global-order") === -1
515,345✔
382
        ) {
543,206✔
383
            const entityOrderBy = this.mainAlias!.metadata.orderBy || {}
514,133✔
384
            return Object.keys(entityOrderBy).reduce((orderBy, key) => {
514,133✔
385
                orderBy[this.mainAlias!.name + "." + key] = entityOrderBy[key]
224✔
386
                return orderBy
224✔
387
            }, {} as OrderByCondition)
514,133✔
388
        }
514,133✔
389

29,073✔
390
        return this.orderBys
29,073✔
391
    }
29,073✔
392

26✔
393
    // -------------------------------------------------------------------------
26✔
394
    // Public Methods
26✔
395
    // -------------------------------------------------------------------------
26✔
396

26✔
397
    /**
26✔
398
     * Creates a main alias and adds it to the current expression map.
26✔
399
     */
26✔
400
    setMainAlias(alias: Alias): Alias {
26✔
401
        // if main alias is already set then remove it from the array
796,265✔
402
        // if (this.mainAlias)
796,265✔
403
        //     this.aliases.splice(this.aliases.indexOf(this.mainAlias));
796,265✔
404

796,265✔
405
        // set new main alias
796,265✔
406
        this.mainAlias = alias
796,265✔
407

796,265✔
408
        return alias
796,265✔
409
    }
796,265✔
410

26✔
411
    /**
26✔
412
     * Creates a new alias and adds it to the current expression map.
26✔
413
     */
26✔
414
    createAlias(options: {
26✔
415
        type: "from" | "select" | "join" | "other"
5,136,765✔
416
        name?: string
5,136,765✔
417
        target?: Function | string
5,136,765✔
418
        tablePath?: string
5,136,765✔
419
        subQuery?: string
5,136,765✔
420
        metadata?: EntityMetadata
5,136,765✔
421
    }): Alias {
5,136,765✔
422
        let aliasName = options.name
5,136,765✔
423
        if (!aliasName && options.tablePath) aliasName = options.tablePath
5,136,765✔
424
        if (!aliasName && typeof options.target === "function")
5,136,765!
425
            aliasName = options.target.name
5,136,765!
426
        if (!aliasName && typeof options.target === "string")
5,136,765!
427
            aliasName = options.target
5,136,765!
428

5,136,765✔
429
        const alias = new Alias()
5,136,765✔
430
        alias.type = options.type
5,136,765✔
431
        if (aliasName) alias.name = aliasName
5,136,765✔
432
        if (options.metadata) alias.metadata = options.metadata
5,136,765✔
433
        if (options.target && !alias.hasMetadata)
5,136,765!
434
            alias.metadata = this.connection.getMetadata(options.target)
5,136,765!
435
        if (options.tablePath) alias.tablePath = options.tablePath
5,136,765✔
436
        if (options.subQuery) alias.subQuery = options.subQuery
5,136,765✔
437

5,136,765✔
438
        this.aliases.push(alias)
5,136,765✔
439
        return alias
5,136,765✔
440
    }
5,136,765✔
441

26✔
442
    /**
26✔
443
     * Finds alias with the given name.
26✔
444
     * If alias was not found it throw an exception.
26✔
445
     */
26✔
446
    findAliasByName(aliasName: string): Alias {
26✔
447
        const alias = this.aliases.find((alias) => alias.name === aliasName)
3,059,336✔
448
        if (!alias)
3,059,336✔
449
            throw new TypeORMError(
3,059,336!
450
                `"${aliasName}" alias was not found. Maybe you forgot to join it?`,
×
451
            )
×
452

3,059,336✔
453
        return alias
3,059,336✔
454
    }
3,059,336✔
455

26✔
456
    findColumnByAliasExpression(
26✔
457
        aliasExpression: string,
×
458
    ): ColumnMetadata | undefined {
×
459
        const [aliasName, propertyPath] = aliasExpression.split(".")
×
460
        const alias = this.findAliasByName(aliasName)
×
461
        return alias.metadata.findColumnWithPropertyName(propertyPath)
×
462
    }
×
463

26✔
464
    /**
26✔
465
     * Gets relation metadata of the relation this query builder works with.
26✔
466
     *
26✔
467
     * todo: add proper exceptions
26✔
468
     */
26✔
469
    get relationMetadata(): RelationMetadata {
26✔
470
        if (!this.mainAlias)
3,540✔
471
            throw new TypeORMError(`Entity to work with is not specified!`) // todo: better message
3,540!
472

3,540✔
473
        const relationMetadata =
3,540✔
474
            this.mainAlias.metadata.findRelationWithPropertyPath(
3,540✔
475
                this.relationPropertyPath,
3,540✔
476
            )
3,540✔
477
        if (!relationMetadata)
3,540✔
478
            throw new TypeORMError(
3,540!
479
                `Relation ${this.relationPropertyPath} was not found in entity ${this.mainAlias.name}`,
×
480
            ) // todo: better message
3,540✔
481

3,540✔
482
        return relationMetadata
3,540✔
483
    }
3,540✔
484

26✔
485
    /**
26✔
486
     * Copies all properties of the current QueryExpressionMap into a new one.
26✔
487
     * Useful when QueryBuilder needs to create a copy of itself.
26✔
488
     */
26✔
489
    clone(): QueryExpressionMap {
26✔
490
        const map = new QueryExpressionMap(this.connection)
283,525✔
491
        map.queryType = this.queryType
283,525✔
492
        map.selects = this.selects.map((select) => select)
283,525✔
493
        map.maxExecutionTime = this.maxExecutionTime
283,525✔
494
        map.selectDistinct = this.selectDistinct
283,525✔
495
        map.selectDistinctOn = this.selectDistinctOn
283,525✔
496
        this.aliases.forEach((alias) => map.aliases.push(new Alias(alias)))
283,525✔
497
        map.relationLoadStrategy = this.relationLoadStrategy
283,525✔
498
        map.mainAlias = this.mainAlias
283,525✔
499
        map.valuesSet = this.valuesSet
283,525✔
500
        map.returning = this.returning
283,525✔
501
        map.onConflict = this.onConflict
283,525✔
502
        map.onIgnore = this.onIgnore
283,525✔
503
        map.onUpdate = this.onUpdate
283,525✔
504
        map.joinAttributes = this.joinAttributes.map(
283,525✔
505
            (join) => new JoinAttribute(this.connection, this, join),
283,525✔
506
        )
283,525✔
507
        map.relationIdAttributes = this.relationIdAttributes.map(
283,525✔
508
            (relationId) => new RelationIdAttribute(this, relationId),
283,525✔
509
        )
283,525✔
510
        map.relationCountAttributes = this.relationCountAttributes.map(
283,525✔
511
            (relationCount) => new RelationCountAttribute(this, relationCount),
283,525✔
512
        )
283,525✔
513
        map.wheres = this.wheres.map((where) => ({ ...where }))
283,525✔
514
        map.havings = this.havings.map((having) => ({ ...having }))
283,525✔
515
        map.orderBys = Object.assign({}, this.orderBys)
283,525✔
516
        map.groupBys = this.groupBys.map((groupBy) => groupBy)
283,525✔
517
        map.limit = this.limit
283,525✔
518
        map.offset = this.offset
283,525✔
519
        map.skip = this.skip
283,525✔
520
        map.take = this.take
283,525✔
521
        map.useIndex = this.useIndex
283,525✔
522
        map.lockMode = this.lockMode
283,525✔
523
        map.onLocked = this.onLocked
283,525✔
524
        map.lockVersion = this.lockVersion
283,525✔
525
        map.lockTables = this.lockTables
283,525✔
526
        map.withDeleted = this.withDeleted
283,525✔
527
        map.parameters = Object.assign({}, this.parameters)
283,525✔
528
        map.disableEscaping = this.disableEscaping
283,525✔
529
        map.enableRelationIdValues = this.enableRelationIdValues
283,525✔
530
        map.extraAppendedAndWhereCondition = this.extraAppendedAndWhereCondition
283,525✔
531
        map.subQuery = this.subQuery
283,525✔
532
        map.aliasNamePrefixingEnabled = this.aliasNamePrefixingEnabled
283,525✔
533
        map.cache = this.cache
283,525✔
534
        map.cacheId = this.cacheId
283,525✔
535
        map.cacheDuration = this.cacheDuration
283,525✔
536
        map.relationPropertyPath = this.relationPropertyPath
283,525✔
537
        map.of = this.of
283,525✔
538
        map.insertColumns = this.insertColumns
283,525✔
539
        map.whereEntities = this.whereEntities
283,525✔
540
        map.updateEntity = this.updateEntity
283,525✔
541
        map.callListeners = this.callListeners
283,525✔
542
        map.useTransaction = this.useTransaction
283,525✔
543
        map.timeTravel = this.timeTravel
283,525✔
544
        map.nativeParameters = Object.assign({}, this.nativeParameters)
283,525✔
545
        map.comment = this.comment
283,525✔
546
        map.commonTableExpressions = this.commonTableExpressions.map(
283,525✔
547
            (cteOptions) => ({
283,525✔
548
                alias: cteOptions.alias,
×
549
                queryBuilder:
×
550
                    typeof cteOptions.queryBuilder === "string"
×
551
                        ? cteOptions.queryBuilder
×
552
                        : cteOptions.queryBuilder.clone(),
×
553
                options: cteOptions.options,
×
554
            }),
283,525✔
555
        )
283,525✔
556
        return map
283,525✔
557
    }
283,525✔
558
}
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