@imqueue/job 2.0.6 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.oxfmtrc.json +9 -0
- package/.oxlintrc.json +20 -0
- package/CONTRIBUTING.md +58 -0
- package/CONTRIBUTION-TERMS.md +79 -0
- package/SECURITY.md +40 -0
- package/index.d.ts +2 -2
- package/index.js +25 -20
- package/package.json +30 -65
- package/scripts/strip-comment-coverage.mjs +472 -0
- package/eslint.config.mjs +0 -132
package/.oxfmtrc.json
ADDED
package/.oxlintrc.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "./node_modules/oxlint/configuration_schema.json",
|
|
3
|
+
"categories": {
|
|
4
|
+
"correctness": "error"
|
|
5
|
+
},
|
|
6
|
+
"rules": {
|
|
7
|
+
"no-debugger": "error",
|
|
8
|
+
"no-console": ["warn", { "allow": ["warn", "error", "info"] }],
|
|
9
|
+
"no-unused-vars": "warn"
|
|
10
|
+
},
|
|
11
|
+
"ignorePatterns": [
|
|
12
|
+
"**/*.js",
|
|
13
|
+
"**/*.d.ts",
|
|
14
|
+
"docs/",
|
|
15
|
+
"coverage/",
|
|
16
|
+
".nyc_output/",
|
|
17
|
+
".agent-out/",
|
|
18
|
+
"node_modules/"
|
|
19
|
+
]
|
|
20
|
+
}
|
package/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Contributing to @imqueue
|
|
2
|
+
|
|
3
|
+
Thanks for your interest in improving @imqueue! Contributions of all kinds are
|
|
4
|
+
welcome — bug fixes, features, tests, and documentation.
|
|
5
|
+
|
|
6
|
+
## Contribution terms — please read first
|
|
7
|
+
|
|
8
|
+
@imqueue is **dual-licensed**: it is free to everyone under **GPL-3.0**, and it is
|
|
9
|
+
also offered under **commercial licenses** for closed-source use. To make that
|
|
10
|
+
sustainable, contributions are accepted under the
|
|
11
|
+
**[@imqueue Contribution Terms](./CONTRIBUTION-TERMS.md)**.
|
|
12
|
+
|
|
13
|
+
**By opening a pull request — or otherwise contributing — you agree to those
|
|
14
|
+
terms.** In short:
|
|
15
|
+
|
|
16
|
+
- You **keep the copyright** in your contribution.
|
|
17
|
+
- Your contribution stays available to everyone under **GPL-3.0**.
|
|
18
|
+
- You grant the project owner the right to **also license your contribution
|
|
19
|
+
commercially**, royalty-free — you will not receive a fee for it.
|
|
20
|
+
|
|
21
|
+
**If you do not agree, please do not contribute.** Read the full text in
|
|
22
|
+
[CONTRIBUTION-TERMS.md](./CONTRIBUTION-TERMS.md).
|
|
23
|
+
|
|
24
|
+
## How to contribute
|
|
25
|
+
|
|
26
|
+
1. **Open an issue first** for anything non-trivial, so we can agree on the
|
|
27
|
+
approach before you invest time.
|
|
28
|
+
2. **Fork** the repository and create a topic branch from `master`
|
|
29
|
+
(e.g. `fix/redis-reconnect` or `feat/lock-timeout`).
|
|
30
|
+
3. **Make your change**, following the existing code style. Keep pull requests
|
|
31
|
+
focused — one logical change per PR.
|
|
32
|
+
4. **Add or update tests** so the change is covered, and make sure the full suite
|
|
33
|
+
passes locally:
|
|
34
|
+
```bash
|
|
35
|
+
npm ci
|
|
36
|
+
npm test
|
|
37
|
+
```
|
|
38
|
+
5. **Write clear commit messages** and a descriptive PR title and summary.
|
|
39
|
+
6. **Open the pull request** against `master` and fill in the PR template,
|
|
40
|
+
including the contribution-terms checkbox.
|
|
41
|
+
|
|
42
|
+
## Guidelines
|
|
43
|
+
|
|
44
|
+
- Match the existing TypeScript style and formatting already used in the file you
|
|
45
|
+
are editing.
|
|
46
|
+
- Keep public API changes documented (doc-blocks / README as appropriate).
|
|
47
|
+
- Be respectful and constructive in reviews and discussions.
|
|
48
|
+
|
|
49
|
+
## Reporting security issues
|
|
50
|
+
|
|
51
|
+
Please **do not** open a public issue for security vulnerabilities. Report them
|
|
52
|
+
privately to the maintainers (see the repository's security policy or contact
|
|
53
|
+
address) so they can be addressed responsibly.
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
Questions about the terms or a larger/corporate contribution? Reach out before you
|
|
58
|
+
start and we'll help.
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# @imqueue Contribution Terms (v1.0)
|
|
2
|
+
|
|
3
|
+
These terms apply to every contribution to any repository of the **@imqueue**
|
|
4
|
+
project — the packages published under the `@imqueue` npm scope and any repository
|
|
5
|
+
in the imqueue GitHub organisation.
|
|
6
|
+
|
|
7
|
+
**By submitting a contribution — opening a pull request, pushing a commit, sending
|
|
8
|
+
a patch, or otherwise offering work for inclusion — you accept these terms in
|
|
9
|
+
full. If you do not agree with them, do not contribute.**
|
|
10
|
+
|
|
11
|
+
In these terms, **"Owner"** means Mykhailo Stadnyk, together with his successors,
|
|
12
|
+
assigns and any entity he controls (including VaryLogic s.r.o.), and
|
|
13
|
+
**"Contribution"** means any work of authorship — code, documentation or other
|
|
14
|
+
material — you submit to an @imqueue repository.
|
|
15
|
+
|
|
16
|
+
## 1. What you grant
|
|
17
|
+
|
|
18
|
+
You grant the Owner a **perpetual, worldwide, irrevocable, royalty-free,
|
|
19
|
+
non-exclusive, transferable and sublicensable** license to use, reproduce,
|
|
20
|
+
modify, adapt, publish, distribute and otherwise exploit your Contribution and
|
|
21
|
+
derivative works of it, **and to license and sub-license it under any terms the
|
|
22
|
+
Owner chooses — including commercial and proprietary (closed-source) licenses,
|
|
23
|
+
without any obligation to release source code.**
|
|
24
|
+
|
|
25
|
+
In plain terms: the Owner may include your Contribution in @imqueue and **sell
|
|
26
|
+
commercial licenses that cover it**, alongside the open-source edition.
|
|
27
|
+
|
|
28
|
+
## 2. No royalties, fees or compensation
|
|
29
|
+
|
|
30
|
+
The rights above are granted **free of charge**. You **waive** any right to
|
|
31
|
+
royalties, license fees, revenue share or any other compensation arising from the
|
|
32
|
+
Owner's use or licensing of your Contribution, including its commercial licensing,
|
|
33
|
+
and you agree not to seek any such payment now or in the future.
|
|
34
|
+
|
|
35
|
+
## 3. You keep your copyright
|
|
36
|
+
|
|
37
|
+
You **retain ownership** of the copyright in your Contribution and may use it
|
|
38
|
+
elsewhere for your own purposes. You are granting the Owner the rights in Section
|
|
39
|
+
1 — you are not required to transfer ownership. Your Contribution also remains
|
|
40
|
+
available to the public under the repository's open-source license (GPL-3.0).
|
|
41
|
+
|
|
42
|
+
## 4. Patent license
|
|
43
|
+
|
|
44
|
+
You grant the Owner and all downstream recipients a perpetual, worldwide,
|
|
45
|
+
royalty-free, irrevocable (except as stated) patent license to make, use, sell,
|
|
46
|
+
offer to sell, import and transfer your Contribution, limited to the patent claims
|
|
47
|
+
you can license that are necessarily infringed by your Contribution alone or in
|
|
48
|
+
combination with the project. If you start patent litigation alleging the project
|
|
49
|
+
or your Contribution infringes your patents, the patent license you received
|
|
50
|
+
terminates.
|
|
51
|
+
|
|
52
|
+
## 5. Your promises
|
|
53
|
+
|
|
54
|
+
You confirm that: (a) the Contribution is your own original work, or you have the
|
|
55
|
+
right to submit it and to grant these rights; (b) if your employer has any rights
|
|
56
|
+
in your work, you have their permission to contribute, or they have waived those
|
|
57
|
+
rights; and (c) you have identified any third-party material in your Contribution
|
|
58
|
+
and its license.
|
|
59
|
+
|
|
60
|
+
## 6. Moral rights
|
|
61
|
+
|
|
62
|
+
To the maximum extent permitted by applicable law, you waive, or agree not to
|
|
63
|
+
exercise, any moral rights in your Contribution in a way that would prevent the
|
|
64
|
+
Owner from exercising the rights in Section 1. (Certain moral rights are
|
|
65
|
+
inalienable under Slovak/EU law; nothing here purports to transfer those.)
|
|
66
|
+
|
|
67
|
+
## 7. No obligation; "as is"
|
|
68
|
+
|
|
69
|
+
The Owner is under no obligation to use your Contribution. It is provided **"as
|
|
70
|
+
is"**, without warranty of any kind.
|
|
71
|
+
|
|
72
|
+
## 8. Governing law
|
|
73
|
+
|
|
74
|
+
These terms are governed by the laws of the Slovak Republic, without regard to its
|
|
75
|
+
conflict-of-laws rules.
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
**If you do not agree to all of the above, do not contribute to @imqueue.**
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Reporting a vulnerability
|
|
4
|
+
|
|
5
|
+
If you believe you have found a security vulnerability in **@imqueue/job** (or any
|
|
6
|
+
`@imqueue/*` package), please report it **privately** — do not open a public issue,
|
|
7
|
+
pull request, or discussion for it.
|
|
8
|
+
|
|
9
|
+
Two private channels:
|
|
10
|
+
|
|
11
|
+
- **GitHub** — use *Security → Report a vulnerability* on this repository to open a
|
|
12
|
+
private advisory (preferred; it keeps the report and the fix coordinated in one
|
|
13
|
+
place).
|
|
14
|
+
- **Email** — <support@imqueue.com> with the details below.
|
|
15
|
+
|
|
16
|
+
Please include:
|
|
17
|
+
|
|
18
|
+
- the affected package and version(s);
|
|
19
|
+
- a description of the issue and its impact;
|
|
20
|
+
- steps to reproduce, or a proof of concept, where possible.
|
|
21
|
+
|
|
22
|
+
## What to expect
|
|
23
|
+
|
|
24
|
+
- We aim to acknowledge a report within a few business days.
|
|
25
|
+
- We'll confirm the issue, keep you updated on progress, and coordinate a fix and a
|
|
26
|
+
disclosure timeline with you.
|
|
27
|
+
- Once a fix is released we'll credit the reporter in the advisory unless you prefer
|
|
28
|
+
to remain anonymous.
|
|
29
|
+
|
|
30
|
+
## Supported versions
|
|
31
|
+
|
|
32
|
+
Security fixes land on the latest published release line of each `@imqueue/*`
|
|
33
|
+
package on npm. Please make sure you can reproduce an issue against the current
|
|
34
|
+
release before reporting.
|
|
35
|
+
|
|
36
|
+
## Scope
|
|
37
|
+
|
|
38
|
+
The `@imqueue` framework is open source under GPL-3.0. This policy covers the code
|
|
39
|
+
in the `@imqueue/*` packages. Vulnerabilities in third-party dependencies should be
|
|
40
|
+
reported to those projects, though we're glad to help coordinate an upgrade.
|
package/index.d.ts
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* purchase a proprietary commercial license. Please contact us at
|
|
22
22
|
* <support@imqueue.com> to get commercial licensing options.
|
|
23
23
|
*/
|
|
24
|
-
import { ILogger, IMessageQueue } from '@imqueue/core';
|
|
24
|
+
import { type ILogger, type IMessageQueue } from '@imqueue/core';
|
|
25
25
|
/**
|
|
26
26
|
* Job queues options
|
|
27
27
|
*/
|
|
@@ -184,7 +184,7 @@ export interface AnyJobQueuePublisher<T, U> {
|
|
|
184
184
|
export declare abstract class BaseJobQueue<T, U> implements AnyJobQueue<T> {
|
|
185
185
|
protected options: JobQueueOptions;
|
|
186
186
|
protected imq: IMessageQueue;
|
|
187
|
-
protected handler
|
|
187
|
+
protected handler?: JobQueuePopHandler<U>;
|
|
188
188
|
readonly logger: ILogger;
|
|
189
189
|
protected constructor(options: JobQueueOptions);
|
|
190
190
|
/**
|
package/index.js
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.JobQueueWorker = exports.JobQueuePublisher = exports.BaseJobQueue = void 0;
|
|
4
1
|
/*!
|
|
5
2
|
* Job Queue for @imqueue framework
|
|
6
3
|
*
|
|
@@ -24,11 +21,15 @@ exports.JobQueueWorker = exports.JobQueuePublisher = exports.BaseJobQueue = void
|
|
|
24
21
|
* purchase a proprietary commercial license. Please contact us at
|
|
25
22
|
* <support@imqueue.com> to get commercial licensing options.
|
|
26
23
|
*/
|
|
27
|
-
|
|
24
|
+
import IMQ, { IMQMode, } from '@imqueue/core';
|
|
28
25
|
/**
|
|
29
26
|
* Abstract job queue, handles base implementations of AnyJobQueue interface.
|
|
30
27
|
*/
|
|
31
|
-
class BaseJobQueue {
|
|
28
|
+
export class BaseJobQueue {
|
|
29
|
+
options;
|
|
30
|
+
imq;
|
|
31
|
+
handler;
|
|
32
|
+
logger;
|
|
32
33
|
constructor(options) {
|
|
33
34
|
this.options = options;
|
|
34
35
|
this.logger = options.logger || console;
|
|
@@ -68,7 +69,6 @@ class BaseJobQueue {
|
|
|
68
69
|
await this.imq.destroy();
|
|
69
70
|
}
|
|
70
71
|
}
|
|
71
|
-
exports.BaseJobQueue = BaseJobQueue;
|
|
72
72
|
/**
|
|
73
73
|
* Creates and returns IMQOptions derived from a given JobQueueOptions
|
|
74
74
|
*
|
|
@@ -83,10 +83,10 @@ function toIMQOptions(options, logger) {
|
|
|
83
83
|
username: options.username,
|
|
84
84
|
password: options.password,
|
|
85
85
|
cleanup: false,
|
|
86
|
-
safeDelivery: typeof options.safe === 'undefined'
|
|
87
|
-
? true : options.safe,
|
|
86
|
+
safeDelivery: typeof options.safe === 'undefined' ? true : options.safe,
|
|
88
87
|
safeDeliveryTtl: typeof options.safeLockTtl === 'undefined'
|
|
89
|
-
? 10000
|
|
88
|
+
? 10000
|
|
89
|
+
: options.safeLockTtl,
|
|
90
90
|
prefix: options.prefix || 'imq-job',
|
|
91
91
|
verbose: options.verbose,
|
|
92
92
|
verboseExtended: options.verboseExtended,
|
|
@@ -98,7 +98,7 @@ function toIMQOptions(options, logger) {
|
|
|
98
98
|
* Implements simple scheduled job queue publisher. Job queue publisher is only
|
|
99
99
|
* responsible for pushing queue messages.
|
|
100
100
|
*/
|
|
101
|
-
class JobQueuePublisher extends BaseJobQueue {
|
|
101
|
+
export class JobQueuePublisher extends BaseJobQueue {
|
|
102
102
|
/**
|
|
103
103
|
* Constructor. Instantiates new JobQueue instance.
|
|
104
104
|
*
|
|
@@ -107,7 +107,7 @@ class JobQueuePublisher extends BaseJobQueue {
|
|
|
107
107
|
*/
|
|
108
108
|
constructor(options) {
|
|
109
109
|
super(options);
|
|
110
|
-
this.imq =
|
|
110
|
+
this.imq = IMQ.create(options.name, toIMQOptions(options, this.logger), IMQMode.PUBLISHER);
|
|
111
111
|
}
|
|
112
112
|
/**
|
|
113
113
|
* Pushes new job to this queue
|
|
@@ -118,17 +118,24 @@ class JobQueuePublisher extends BaseJobQueue {
|
|
|
118
118
|
*/
|
|
119
119
|
push(job, options) {
|
|
120
120
|
options = options || {};
|
|
121
|
-
this.imq
|
|
121
|
+
this.imq
|
|
122
|
+
.send(this.name, {
|
|
123
|
+
job: job,
|
|
124
|
+
...(options.ttl
|
|
125
|
+
? { expire: Date.now() + options.ttl }
|
|
126
|
+
: {}),
|
|
127
|
+
...(options.delay ? { delay: options.delay } : {}),
|
|
128
|
+
}, options.delay)
|
|
129
|
+
.catch(err => this.logger.log('[JobQueue] push error:', err));
|
|
122
130
|
return this;
|
|
123
131
|
}
|
|
124
132
|
}
|
|
125
|
-
exports.JobQueuePublisher = JobQueuePublisher;
|
|
126
133
|
// noinspection JSUnusedGlobalSymbols
|
|
127
134
|
/**
|
|
128
135
|
* Implements simple scheduled job queue worker. Job queue worker is only
|
|
129
136
|
* responsible for processing queue messages.
|
|
130
137
|
*/
|
|
131
|
-
class JobQueueWorker extends BaseJobQueue {
|
|
138
|
+
export class JobQueueWorker extends BaseJobQueue {
|
|
132
139
|
/**
|
|
133
140
|
* Constructor. Instantiates new JobQueue instance.
|
|
134
141
|
*
|
|
@@ -137,7 +144,7 @@ class JobQueueWorker extends BaseJobQueue {
|
|
|
137
144
|
*/
|
|
138
145
|
constructor(options) {
|
|
139
146
|
super(options);
|
|
140
|
-
this.imq =
|
|
147
|
+
this.imq = IMQ.create(options.name, toIMQOptions(options, this.logger), IMQMode.WORKER);
|
|
141
148
|
}
|
|
142
149
|
/**
|
|
143
150
|
* Sets up job handler, which is called when the job is popped from this
|
|
@@ -156,7 +163,7 @@ class JobQueueWorker extends BaseJobQueue {
|
|
|
156
163
|
const { job, expire, delay } = message;
|
|
157
164
|
let rescheduleDelay;
|
|
158
165
|
try {
|
|
159
|
-
rescheduleDelay = this.handler(job);
|
|
166
|
+
rescheduleDelay = this.handler?.(job);
|
|
160
167
|
if (rescheduleDelay &&
|
|
161
168
|
typeof rescheduleDelay === 'object' &&
|
|
162
169
|
rescheduleDelay &&
|
|
@@ -179,7 +186,6 @@ class JobQueueWorker extends BaseJobQueue {
|
|
|
179
186
|
return this;
|
|
180
187
|
}
|
|
181
188
|
}
|
|
182
|
-
exports.JobQueueWorker = JobQueueWorker;
|
|
183
189
|
// noinspection JSUnusedGlobalSymbols
|
|
184
190
|
/**
|
|
185
191
|
* Implements simple scheduled job queue. Job scheduling is optional. It may
|
|
@@ -188,7 +194,7 @@ exports.JobQueueWorker = JobQueueWorker;
|
|
|
188
194
|
* the job is removed from a queue.
|
|
189
195
|
* Supports graceful shutdown, if TERM or SIGINT is sent to the process.
|
|
190
196
|
*/
|
|
191
|
-
class JobQueue extends BaseJobQueue {
|
|
197
|
+
export default class JobQueue extends BaseJobQueue {
|
|
192
198
|
/**
|
|
193
199
|
* Constructor. Instantiates new JobQueue instance.
|
|
194
200
|
*
|
|
@@ -197,7 +203,7 @@ class JobQueue extends BaseJobQueue {
|
|
|
197
203
|
*/
|
|
198
204
|
constructor(options) {
|
|
199
205
|
super(options);
|
|
200
|
-
this.imq =
|
|
206
|
+
this.imq = IMQ.create(options.name, toIMQOptions(options, this.logger));
|
|
201
207
|
}
|
|
202
208
|
/**
|
|
203
209
|
* Starts processing job queue. Throws if handler is not set before start.
|
|
@@ -236,5 +242,4 @@ class JobQueue extends BaseJobQueue {
|
|
|
236
242
|
return JobQueueWorker.prototype.onPop.call(this, handler);
|
|
237
243
|
}
|
|
238
244
|
}
|
|
239
|
-
exports.default = JobQueue;
|
|
240
245
|
//# sourceMappingURL=index.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@imqueue/job",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.1",
|
|
4
4
|
"description": "Simple job queue",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"message-queue",
|
|
@@ -13,17 +13,20 @@
|
|
|
13
13
|
"json-message-queue"
|
|
14
14
|
],
|
|
15
15
|
"scripts": {
|
|
16
|
-
"prepare": "
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"
|
|
16
|
+
"prepare": "npm run build",
|
|
17
|
+
"build": "npm run clean-compiled && tsc",
|
|
18
|
+
"lint": "oxlint",
|
|
19
|
+
"format": "oxfmt \"index.ts\" \"test/**/*.ts\"",
|
|
20
|
+
"format:check": "oxfmt --check \"index.ts\" \"test/**/*.ts\"",
|
|
21
|
+
"test": "npm run build && node --experimental-test-module-mocks --import ./test/mocks/index.js --test --test-timeout=15000 $(find test -name '*.spec.js')",
|
|
22
|
+
"test-coverage": "npm run build && node --experimental-test-module-mocks --enable-source-maps --import ./test/mocks/index.js --test --experimental-test-coverage --test-timeout=15000 $(find test -name '*.spec.js')",
|
|
23
|
+
"test-lcov": "npm run build && mkdir -p coverage && node --experimental-test-module-mocks --enable-source-maps --import ./test/mocks/index.js --test --experimental-test-coverage --test-reporter=lcov --test-reporter-destination=coverage/lcov.info --test-timeout=15000 $(find test -name '*.spec.js'); node scripts/strip-comment-coverage.mjs coverage/lcov.info",
|
|
24
|
+
"clean-typedefs": "find . -name '*.d.ts' -not -wholename '*node_modules*' -type f -delete",
|
|
25
|
+
"clean-maps": "find . -name '*.js.map' -not -wholename '*node_modules*' -type f -delete",
|
|
26
|
+
"clean-js": "find . -name '*.js' -not -wholename '*node_modules*' -not -wholename '*scripts*' -type f -delete",
|
|
27
|
+
"clean-compiled": "npm run clean-js && npm run clean-typedefs && npm run clean-maps",
|
|
22
28
|
"clean-tests": "rm -rf .nyc_output coverage",
|
|
23
|
-
"clean
|
|
24
|
-
"clean-benchmark": "rm -rf benchmark-result",
|
|
25
|
-
"clean": "npm run clean-tests && npm run clean-typedefs && npm run clean-maps && npm run clean-js && npm run clean-doc && npm run clean-benchmark",
|
|
26
|
-
"doc": "rm -rf docs && typedoc --excludePrivate --excludeExternals --hideGenerator --exclude \"**/+(test|node_modules|docs|coverage|benchmark|.nyc_output)/**/*\" --out ./docs . && /usr/bin/env node -e \"import('open').then(open => open.default('file://`pwd`/docs/index.html',{wait:false}))\""
|
|
29
|
+
"clean": "npm run clean-tests && npm run clean-compiled"
|
|
27
30
|
},
|
|
28
31
|
"repository": {
|
|
29
32
|
"type": "git",
|
|
@@ -32,70 +35,32 @@
|
|
|
32
35
|
"bugs": {
|
|
33
36
|
"url": "https://github.com/imqueue/job/issues"
|
|
34
37
|
},
|
|
35
|
-
"homepage": "https://imqueue.
|
|
38
|
+
"homepage": "https://imqueue.org/",
|
|
36
39
|
"author": "imqueue.com <support@imqueue.com> (https://imqueue.com)",
|
|
37
40
|
"license": "GPL-3.0-only",
|
|
38
41
|
"dependencies": {
|
|
39
|
-
"@imqueue/core": "^2.
|
|
42
|
+
"@imqueue/core": "^3.2.2"
|
|
40
43
|
},
|
|
41
44
|
"devDependencies": {
|
|
42
|
-
"@
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"@types/mock-require": "^3.0.0",
|
|
47
|
-
"@types/node": "^24.9.2",
|
|
48
|
-
"@types/sinon": "^17.0.4",
|
|
49
|
-
"@typescript-eslint/eslint-plugin": "^8.43.0",
|
|
50
|
-
"@typescript-eslint/parser": "^8.43.0",
|
|
51
|
-
"@typescript-eslint/typescript-estree": "^8.43.0",
|
|
52
|
-
"chai": "^6.0.1",
|
|
53
|
-
"eslint": "^9.35.0",
|
|
54
|
-
"globals": "^16.4.0",
|
|
55
|
-
"mocha": "^11.7.2",
|
|
56
|
-
"mocha-lcov-reporter": "^1.3.0",
|
|
57
|
-
"mock-require": "^3.0.3",
|
|
58
|
-
"npm-scripts-help": "^0.8.0",
|
|
59
|
-
"nyc": "^17.1.0",
|
|
60
|
-
"open": "^10.2.0",
|
|
61
|
-
"reflect-metadata": "^0.2.2",
|
|
62
|
-
"sinon": "^21.0.0",
|
|
63
|
-
"source-map-support": "^0.5.21",
|
|
64
|
-
"ts-node": "^10.9.2",
|
|
65
|
-
"typedoc": "^0.28.12",
|
|
66
|
-
"typescript": "^5.9.2",
|
|
67
|
-
"yargs": "^18.0.0"
|
|
45
|
+
"@types/node": "^24.9.1",
|
|
46
|
+
"oxfmt": "0.57.0",
|
|
47
|
+
"oxlint": "1.72.0",
|
|
48
|
+
"typescript": "^7.0.2"
|
|
68
49
|
},
|
|
69
50
|
"main": "index.js",
|
|
70
51
|
"typescript": {
|
|
71
52
|
"definitions": "index.d.ts"
|
|
72
53
|
},
|
|
73
|
-
"
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
"
|
|
54
|
+
"type": "module",
|
|
55
|
+
"types": "index.d.ts",
|
|
56
|
+
"exports": {
|
|
57
|
+
".": {
|
|
58
|
+
"types": "./index.d.ts",
|
|
59
|
+
"default": "./index.js"
|
|
60
|
+
},
|
|
61
|
+
"./package.json": "./package.json"
|
|
81
62
|
},
|
|
82
|
-
"
|
|
83
|
-
"
|
|
84
|
-
"extension": [
|
|
85
|
-
".ts"
|
|
86
|
-
],
|
|
87
|
-
"exclude": [
|
|
88
|
-
"**/*.d.ts",
|
|
89
|
-
"**/test/**"
|
|
90
|
-
],
|
|
91
|
-
"require": [
|
|
92
|
-
"ts-node/register"
|
|
93
|
-
],
|
|
94
|
-
"reporter": [
|
|
95
|
-
"html",
|
|
96
|
-
"text",
|
|
97
|
-
"text-summary",
|
|
98
|
-
"lcovonly"
|
|
99
|
-
]
|
|
63
|
+
"engines": {
|
|
64
|
+
"node": ">=22.12.0"
|
|
100
65
|
}
|
|
101
66
|
}
|
|
@@ -0,0 +1,472 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Coverage post-processor
|
|
3
|
+
*
|
|
4
|
+
* I'm Queue Software Project
|
|
5
|
+
* Copyright (C) 2025 imqueue.com <support@imqueue.com>
|
|
6
|
+
*
|
|
7
|
+
* This program is free software: you can redistribute it and/or modify
|
|
8
|
+
* it under the terms of the GNU General Public License as published by
|
|
9
|
+
* the Free Software Foundation, either version 3 of the License, or
|
|
10
|
+
* (at your option) any later version.
|
|
11
|
+
*
|
|
12
|
+
* This program is distributed in the hope that it will be useful,
|
|
13
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
14
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
15
|
+
* GNU General Public License for more details.
|
|
16
|
+
*
|
|
17
|
+
* You should have received a copy of the GNU General Public License
|
|
18
|
+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
19
|
+
*
|
|
20
|
+
* If you want to use this code in a closed source (commercial) project, you can
|
|
21
|
+
* purchase a proprietary commercial license. Please contact us at
|
|
22
|
+
* <support@imqueue.com> to get commercial licensing options.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Node's built-in coverage (`--experimental-test-coverage --enable-source-maps`)
|
|
27
|
+
* reports every non-code line of a `.ts` source as uncovered, because comment
|
|
28
|
+
* and blank lines have no source-map mappings and therefore fall outside every
|
|
29
|
+
* covered V8 byte-range. That paints the license header and each JSDoc block
|
|
30
|
+
* red in the HTML report and pushes per-file line% far below reality.
|
|
31
|
+
*
|
|
32
|
+
* Node's reporter also emits records that the (strict) genhtml LCOV 2.x tool
|
|
33
|
+
* rejects — function entries with `undefined` line numbers or synthetic,
|
|
34
|
+
* duplicated names (`<instance_members_initializer>`, and decorated methods all
|
|
35
|
+
* mis-named `has`/`get`), plus `BRDA:undefined,...` branch records. Left in,
|
|
36
|
+
* they produce a wall of warnings and a fatal "unexpected category" error.
|
|
37
|
+
*
|
|
38
|
+
* This script rewrites an lcov file in place: it drops the `DA`/`BRDA` records
|
|
39
|
+
* for lines that contain no executable code (comments and blank lines), drops
|
|
40
|
+
* all function records and malformed branch records, then recomputes the
|
|
41
|
+
* `LF`/`LH`/`BRF`/`BRH` totals. It uses the TypeScript scanner (already a dev
|
|
42
|
+
* dependency) to locate comments reliably — comment markers inside strings,
|
|
43
|
+
* template literals and regexes are not misdetected. The result renders in
|
|
44
|
+
* genhtml with accurate line + branch coverage and no warnings or errors.
|
|
45
|
+
*/
|
|
46
|
+
import { readFileSync, writeFileSync, existsSync } from 'node:fs';
|
|
47
|
+
// TypeScript 7 (native port) no longer exposes the classic compiler API from
|
|
48
|
+
// the package root — the lightweight scanner/AST primitives live under the
|
|
49
|
+
// `typescript/unstable/ast` entry point (createSourceFile is gone entirely, so
|
|
50
|
+
// import spans are located with the scanner rather than a parsed AST).
|
|
51
|
+
import * as ts from 'typescript/unstable/ast';
|
|
52
|
+
|
|
53
|
+
const lcovPath = process.argv[2] || 'coverage/lcov.info';
|
|
54
|
+
|
|
55
|
+
if (!existsSync(lcovPath)) {
|
|
56
|
+
console.warn(`strip-comment-coverage: ${lcovPath} not found, skipping`);
|
|
57
|
+
process.exit(0);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Returns the set of 1-based line numbers in the given source that hold no
|
|
62
|
+
* executable code — i.e. blank lines and lines whose only content is a comment.
|
|
63
|
+
*
|
|
64
|
+
* @param {string} text
|
|
65
|
+
* @returns {Set<number>}
|
|
66
|
+
*/
|
|
67
|
+
function nonCodeLines(text) {
|
|
68
|
+
const commentChars = new Uint8Array(text.length);
|
|
69
|
+
const scanner = ts.createScanner(
|
|
70
|
+
ts.ScriptTarget.Latest,
|
|
71
|
+
/* skipTrivia */ false,
|
|
72
|
+
ts.LanguageVariant.Standard,
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
scanner.setText(text);
|
|
76
|
+
|
|
77
|
+
let token = scanner.scan();
|
|
78
|
+
|
|
79
|
+
while (token !== ts.SyntaxKind.EndOfFile) {
|
|
80
|
+
if (
|
|
81
|
+
token === ts.SyntaxKind.SingleLineCommentTrivia ||
|
|
82
|
+
token === ts.SyntaxKind.MultiLineCommentTrivia
|
|
83
|
+
) {
|
|
84
|
+
const start = scanner.getTokenStart();
|
|
85
|
+
const end = scanner.getTokenEnd();
|
|
86
|
+
|
|
87
|
+
for (let i = start; i < end; i++) {
|
|
88
|
+
commentChars[i] = 1;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
token = scanner.scan();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const lines = text.split('\n');
|
|
96
|
+
const result = new Set();
|
|
97
|
+
let pos = 0;
|
|
98
|
+
|
|
99
|
+
for (let ln = 0; ln < lines.length; ln++) {
|
|
100
|
+
const line = lines[ln];
|
|
101
|
+
let hasCode = false;
|
|
102
|
+
|
|
103
|
+
for (let i = 0; i < line.length; i++) {
|
|
104
|
+
const ch = line[i];
|
|
105
|
+
|
|
106
|
+
if (ch === ' ' || ch === '\t' || ch === '\r') {
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (!commentChars[pos + i]) {
|
|
111
|
+
hasCode = true;
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (!hasCode) {
|
|
117
|
+
result.add(ln + 1);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
pos += line.length + 1; // account for the split '\n'
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return result;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Returns the set of 1-based lines occupied by top-level import statements.
|
|
128
|
+
* Imports compile to `require(...)` that runs on module load, so they are
|
|
129
|
+
* always executed, yet Node's source-mapped coverage frequently mis-reports
|
|
130
|
+
* them as uncovered (the mapping lands outside the covered byte-range). Treating
|
|
131
|
+
* them as non-coverable corrects that artifact rather than hiding real logic.
|
|
132
|
+
*
|
|
133
|
+
* @param {string} text
|
|
134
|
+
* @returns {Set<number>}
|
|
135
|
+
*/
|
|
136
|
+
function importLines(text) {
|
|
137
|
+
// TypeScript 7 dropped `createSourceFile`, so top-level import statements are
|
|
138
|
+
// located with the scanner: a top-level `import` keyword (depth 0, not the
|
|
139
|
+
// dynamic `import(...)` call nor `import.meta`) begins a declaration whose
|
|
140
|
+
// span reaches the terminating semicolon — both `import ... from '...'` and
|
|
141
|
+
// `import x = require('...')` end that way in this codebase.
|
|
142
|
+
const scanner = ts.createScanner(
|
|
143
|
+
ts.ScriptTarget.Latest,
|
|
144
|
+
/* skipTrivia */ true,
|
|
145
|
+
ts.LanguageVariant.Standard,
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
scanner.setText(text);
|
|
149
|
+
|
|
150
|
+
const lineStarts = ts.computeLineStarts(text);
|
|
151
|
+
// 0-based line holding `pos`: largest index with lineStarts[i] <= pos
|
|
152
|
+
const lineOf = pos => {
|
|
153
|
+
let lo = 0;
|
|
154
|
+
let hi = lineStarts.length - 1;
|
|
155
|
+
|
|
156
|
+
while (lo < hi) {
|
|
157
|
+
const mid = (lo + hi + 1) >> 1;
|
|
158
|
+
|
|
159
|
+
if (lineStarts[mid] <= pos) {
|
|
160
|
+
lo = mid;
|
|
161
|
+
} else {
|
|
162
|
+
hi = mid - 1;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return lo;
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
const K = ts.SyntaxKind;
|
|
170
|
+
const result = new Set();
|
|
171
|
+
let depth = 0;
|
|
172
|
+
let token = scanner.scan();
|
|
173
|
+
|
|
174
|
+
while (token !== K.EndOfFile) {
|
|
175
|
+
if (
|
|
176
|
+
token === K.OpenBraceToken ||
|
|
177
|
+
token === K.OpenParenToken ||
|
|
178
|
+
token === K.OpenBracketToken
|
|
179
|
+
) {
|
|
180
|
+
depth++;
|
|
181
|
+
} else if (
|
|
182
|
+
token === K.CloseBraceToken ||
|
|
183
|
+
token === K.CloseParenToken ||
|
|
184
|
+
token === K.CloseBracketToken
|
|
185
|
+
) {
|
|
186
|
+
depth--;
|
|
187
|
+
} else if (depth === 0 && token === K.ImportKeyword) {
|
|
188
|
+
const start = scanner.getTokenStart();
|
|
189
|
+
let next = scanner.scan();
|
|
190
|
+
|
|
191
|
+
// `import(...)` dynamic call or `import.meta` — not a declaration
|
|
192
|
+
if (next === K.OpenParenToken || next === K.DotToken) {
|
|
193
|
+
token = next;
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// consume to the terminating semicolon at bracket depth 0
|
|
198
|
+
let inner = 0;
|
|
199
|
+
let end = scanner.getTokenEnd();
|
|
200
|
+
|
|
201
|
+
while (next !== K.EndOfFile) {
|
|
202
|
+
if (next === K.OpenBraceToken || next === K.OpenParenToken) {
|
|
203
|
+
inner++;
|
|
204
|
+
} else if (
|
|
205
|
+
next === K.CloseBraceToken ||
|
|
206
|
+
next === K.CloseParenToken
|
|
207
|
+
) {
|
|
208
|
+
inner--;
|
|
209
|
+
} else if (next === K.SemicolonToken && inner === 0) {
|
|
210
|
+
end = scanner.getTokenEnd();
|
|
211
|
+
break;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
end = scanner.getTokenEnd();
|
|
215
|
+
next = scanner.scan();
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
for (let line = lineOf(start); line <= lineOf(end); line++) {
|
|
219
|
+
result.add(line + 1);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
token = scanner.scan();
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
token = scanner.scan();
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return result;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const B64 =
|
|
233
|
+
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
|
234
|
+
const B64MAP = new Map([...B64].map((c, i) => [c, i]));
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Decodes one base64 VLQ source-map segment into its integer fields.
|
|
238
|
+
*
|
|
239
|
+
* @param {string} seg
|
|
240
|
+
* @returns {number[]}
|
|
241
|
+
*/
|
|
242
|
+
function decodeVLQ(seg) {
|
|
243
|
+
const out = [];
|
|
244
|
+
let shift = 0;
|
|
245
|
+
let value = 0;
|
|
246
|
+
|
|
247
|
+
for (const ch of seg) {
|
|
248
|
+
const digit = B64MAP.get(ch);
|
|
249
|
+
|
|
250
|
+
if (digit === undefined) {
|
|
251
|
+
break;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
value += (digit & 31) << shift;
|
|
255
|
+
|
|
256
|
+
if (digit & 32) {
|
|
257
|
+
shift += 5;
|
|
258
|
+
} else {
|
|
259
|
+
out.push(value & 1 ? -(value >> 1) : value >> 1);
|
|
260
|
+
value = 0;
|
|
261
|
+
shift = 0;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return out;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Returns the set of 1-based source lines that produced generated JavaScript,
|
|
270
|
+
* read from the file's `.js.map`. These are exactly the coverable lines — every
|
|
271
|
+
* other line (comments, blanks, type-only declarations, interfaces) emits no
|
|
272
|
+
* code and can never be executed. Returns null when no source map is present.
|
|
273
|
+
*
|
|
274
|
+
* @param {string} sourceFile - path of the .ts source (e.g. src/RedisQueue.ts)
|
|
275
|
+
* @returns {Set<number> | null}
|
|
276
|
+
*/
|
|
277
|
+
function emittedLines(sourceFile) {
|
|
278
|
+
const mapPath = sourceFile.replace(/\.ts$/, '.js.map');
|
|
279
|
+
|
|
280
|
+
if (!existsSync(mapPath)) {
|
|
281
|
+
return null;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
let map;
|
|
285
|
+
|
|
286
|
+
try {
|
|
287
|
+
map = JSON.parse(readFileSync(mapPath, 'utf8'));
|
|
288
|
+
} catch {
|
|
289
|
+
return null;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (typeof map.mappings !== 'string') {
|
|
293
|
+
return null;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const lines = new Set();
|
|
297
|
+
let srcIndex = 0;
|
|
298
|
+
let srcLine = 0;
|
|
299
|
+
|
|
300
|
+
for (const group of map.mappings.split(';')) {
|
|
301
|
+
for (const seg of group.split(',')) {
|
|
302
|
+
if (!seg) {
|
|
303
|
+
continue;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
const fields = decodeVLQ(seg);
|
|
307
|
+
|
|
308
|
+
// [genCol, srcIndex, srcLine, srcCol, nameIndex]; <4 fields = no
|
|
309
|
+
// source position for this segment
|
|
310
|
+
if (fields.length >= 4) {
|
|
311
|
+
srcIndex += fields[1];
|
|
312
|
+
srcLine += fields[2];
|
|
313
|
+
|
|
314
|
+
if (srcIndex === 0) {
|
|
315
|
+
lines.add(srcLine + 1); // map lines are 0-based
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
return lines;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const coverableCache = new Map();
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Resolves how to filter a source file's line records:
|
|
328
|
+
* - `{ mode: 'keep', set }` — keep only lines in `set` (source-map driven);
|
|
329
|
+
* - `{ mode: 'drop', set }` — drop lines in `set` (comment-scanner fallback).
|
|
330
|
+
*
|
|
331
|
+
* @param {string} sourceFile
|
|
332
|
+
* @returns {{ mode: 'keep' | 'drop', set: Set<number> }}
|
|
333
|
+
*/
|
|
334
|
+
function coverableInfo(sourceFile) {
|
|
335
|
+
if (coverableCache.has(sourceFile)) {
|
|
336
|
+
return coverableCache.get(sourceFile);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const emitted = sourceFile ? emittedLines(sourceFile) : null;
|
|
340
|
+
let info;
|
|
341
|
+
|
|
342
|
+
if (emitted && existsSync(sourceFile)) {
|
|
343
|
+
// A line is coverable only if it emitted JS (has a source-map mapping)
|
|
344
|
+
// AND is not a comment/blank AND is not an import statement. The comment
|
|
345
|
+
// check is required because `removeComments: false` keeps license/JSDoc
|
|
346
|
+
// comments in the output, so they carry mappings too; the mapping check
|
|
347
|
+
// drops type-only lines (interfaces, type aliases) which emit nothing;
|
|
348
|
+
// the import check drops the mis-mapped import artifact.
|
|
349
|
+
const text = readFileSync(sourceFile, 'utf8');
|
|
350
|
+
const nonCode = nonCodeLines(text);
|
|
351
|
+
const imports = importLines(text);
|
|
352
|
+
const keep = new Set(
|
|
353
|
+
[...emitted].filter(
|
|
354
|
+
line => !nonCode.has(line) && !imports.has(line),
|
|
355
|
+
),
|
|
356
|
+
);
|
|
357
|
+
info = { mode: 'keep', set: keep };
|
|
358
|
+
} else if (sourceFile && existsSync(sourceFile)) {
|
|
359
|
+
const text = readFileSync(sourceFile, 'utf8');
|
|
360
|
+
const skip = nonCodeLines(text);
|
|
361
|
+
|
|
362
|
+
for (const line of importLines(text)) {
|
|
363
|
+
skip.add(line);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
info = { mode: 'drop', set: skip };
|
|
367
|
+
} else {
|
|
368
|
+
info = { mode: 'drop', set: new Set() };
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
coverableCache.set(sourceFile, info);
|
|
372
|
+
|
|
373
|
+
return info;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
let strippedLines = 0;
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Rewrites a single lcov record so that genhtml accepts it without warnings or
|
|
380
|
+
* errors. It:
|
|
381
|
+
* - drops DA/BRDA entries on non-code (comment/blank) lines;
|
|
382
|
+
* - drops all function records (FN/FNDA/FNF/FNH) — Node's source-mapped
|
|
383
|
+
* function detection is unreliable here (undefined line numbers, mis-named
|
|
384
|
+
* and duplicated entries), which genhtml rejects, so line + branch coverage
|
|
385
|
+
* is kept instead;
|
|
386
|
+
* - drops malformed branch records such as `BRDA:undefined,...`;
|
|
387
|
+
* - recomputes the LF/LH/BRF/BRH totals.
|
|
388
|
+
*
|
|
389
|
+
* @param {string[]} recordLines - record body, excluding the end_of_record line
|
|
390
|
+
* @returns {string}
|
|
391
|
+
*/
|
|
392
|
+
function processRecord(recordLines) {
|
|
393
|
+
const sfLine = recordLines.find(line => line.startsWith('SF:'));
|
|
394
|
+
const sourceFile = sfLine ? sfLine.slice(3).trim() : '';
|
|
395
|
+
const info = coverableInfo(sourceFile);
|
|
396
|
+
|
|
397
|
+
// true when a source line produces no executable code and must be dropped
|
|
398
|
+
const drop = n => (info.mode === 'keep' ? !info.set.has(n) : info.set.has(n));
|
|
399
|
+
|
|
400
|
+
let lf = 0;
|
|
401
|
+
let lh = 0;
|
|
402
|
+
let brf = 0;
|
|
403
|
+
let brh = 0;
|
|
404
|
+
|
|
405
|
+
const kept = recordLines.filter(line => {
|
|
406
|
+
// drop all function records — unreliable under source maps
|
|
407
|
+
if (/^(FN:|FNDA:|FNF:|FNH:)/.test(line)) {
|
|
408
|
+
return false;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
const da = line.match(/^DA:(\d+),(\d+)/);
|
|
412
|
+
|
|
413
|
+
if (da) {
|
|
414
|
+
if (drop(+da[1])) {
|
|
415
|
+
strippedLines++;
|
|
416
|
+
|
|
417
|
+
return false;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
lf++;
|
|
421
|
+
|
|
422
|
+
if (+da[2] > 0) {
|
|
423
|
+
lh++;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
return true;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
if (line.startsWith('BRDA:')) {
|
|
430
|
+
const brda = line.match(/^BRDA:(\d+),\d+,\d+,(\d+|-)$/);
|
|
431
|
+
|
|
432
|
+
// drop malformed (e.g. BRDA:undefined,...) or non-code branches
|
|
433
|
+
if (!brda || drop(+brda[1])) {
|
|
434
|
+
return false;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
brf++;
|
|
438
|
+
|
|
439
|
+
if (brda[2] !== '-' && +brda[2] > 0) {
|
|
440
|
+
brh++;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
return true;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// drop the old totals — they are recomputed below
|
|
447
|
+
return !/^(LF|LH|BRF|BRH):/.test(line);
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
return (
|
|
451
|
+
`${kept.join('\n')}\n` +
|
|
452
|
+
`LF:${lf}\nLH:${lh}\nBRF:${brf}\nBRH:${brh}\nend_of_record\n`
|
|
453
|
+
);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
const output = [];
|
|
457
|
+
let record = [];
|
|
458
|
+
|
|
459
|
+
for (const line of readFileSync(lcovPath, 'utf8').split('\n')) {
|
|
460
|
+
if (line === 'end_of_record') {
|
|
461
|
+
output.push(processRecord(record));
|
|
462
|
+
record = [];
|
|
463
|
+
} else if (line !== '') {
|
|
464
|
+
record.push(line);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
writeFileSync(lcovPath, output.join(''));
|
|
469
|
+
console.info(
|
|
470
|
+
`strip-comment-coverage: removed ${strippedLines} comment/blank line ` +
|
|
471
|
+
`entries from ${lcovPath}`,
|
|
472
|
+
);
|
package/eslint.config.mjs
DELETED
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
import typescriptEslint from "@typescript-eslint/eslint-plugin";
|
|
2
|
-
import globals from "globals";
|
|
3
|
-
import tsParser from "@typescript-eslint/parser";
|
|
4
|
-
import path from "node:path";
|
|
5
|
-
import { fileURLToPath } from "node:url";
|
|
6
|
-
import js from "@eslint/js";
|
|
7
|
-
import { FlatCompat } from "@eslint/eslintrc";
|
|
8
|
-
|
|
9
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
10
|
-
const __dirname = path.dirname(__filename);
|
|
11
|
-
const compat = new FlatCompat({
|
|
12
|
-
baseDirectory: __dirname,
|
|
13
|
-
recommendedConfig: js.configs.recommended,
|
|
14
|
-
allConfig: js.configs.all
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
export default [...compat.extends(
|
|
18
|
-
"plugin:@typescript-eslint/recommended",
|
|
19
|
-
"plugin:@typescript-eslint/recommended-requiring-type-checking",
|
|
20
|
-
), {
|
|
21
|
-
plugins: {
|
|
22
|
-
"@typescript-eslint": typescriptEslint,
|
|
23
|
-
},
|
|
24
|
-
|
|
25
|
-
languageOptions: {
|
|
26
|
-
globals: {
|
|
27
|
-
...globals.browser,
|
|
28
|
-
...globals.node,
|
|
29
|
-
},
|
|
30
|
-
|
|
31
|
-
parser: tsParser,
|
|
32
|
-
ecmaVersion: 5,
|
|
33
|
-
sourceType: "module",
|
|
34
|
-
|
|
35
|
-
parserOptions: {
|
|
36
|
-
project: "tsconfig.json",
|
|
37
|
-
},
|
|
38
|
-
},
|
|
39
|
-
|
|
40
|
-
rules: {
|
|
41
|
-
"@typescript-eslint/adjacent-overload-signatures": "error",
|
|
42
|
-
"@typescript-eslint/array-type": "error",
|
|
43
|
-
"@typescript-eslint/class-name-casing": "off",
|
|
44
|
-
"@typescript-eslint/consistent-type-assertions": "error",
|
|
45
|
-
"@typescript-eslint/interface-name-prefix": "off",
|
|
46
|
-
"@typescript-eslint/no-unsafe-assignment": "off",
|
|
47
|
-
"@typescript-eslint/no-unsafe-member-access": "off",
|
|
48
|
-
"@typescript-eslint/explicit-module-boundary-types": "off",
|
|
49
|
-
"@typescript-eslint/no-unsafe-return": "off",
|
|
50
|
-
"@typescript-eslint/await-thenable": "off",
|
|
51
|
-
"@typescript-eslint/no-misused-promises": "off",
|
|
52
|
-
"@typescript-eslint/no-empty-function": "error",
|
|
53
|
-
"@typescript-eslint/no-empty-interface": "error",
|
|
54
|
-
"@typescript-eslint/no-explicit-any": "off",
|
|
55
|
-
"@typescript-eslint/no-misused-new": "error",
|
|
56
|
-
"@typescript-eslint/no-namespace": "error",
|
|
57
|
-
"@typescript-eslint/no-parameter-properties": "off",
|
|
58
|
-
"@typescript-eslint/no-use-before-define": "off",
|
|
59
|
-
"@typescript-eslint/no-var-requires": "off",
|
|
60
|
-
"@typescript-eslint/prefer-for-of": "error",
|
|
61
|
-
"@typescript-eslint/prefer-function-type": "error",
|
|
62
|
-
"@typescript-eslint/prefer-namespace-keyword": "error",
|
|
63
|
-
"@typescript-eslint/unbound-method": "off",
|
|
64
|
-
"@typescript-eslint/no-shadow": ["error"],
|
|
65
|
-
|
|
66
|
-
"quotes": ["error", "single", {
|
|
67
|
-
avoidEscape: true,
|
|
68
|
-
}],
|
|
69
|
-
|
|
70
|
-
"semi": "error",
|
|
71
|
-
"@typescript-eslint/triple-slash-reference": "error",
|
|
72
|
-
"@typescript-eslint/unified-signatures": "error",
|
|
73
|
-
"arrow-parens": ["off", "as-needed"],
|
|
74
|
-
camelcase: "error",
|
|
75
|
-
"comma-dangle": "off",
|
|
76
|
-
complexity: "off",
|
|
77
|
-
"constructor-super": "error",
|
|
78
|
-
"dot-notation": "error",
|
|
79
|
-
eqeqeq: ["error", "smart"],
|
|
80
|
-
"guard-for-in": "error",
|
|
81
|
-
|
|
82
|
-
"id-blacklist": [
|
|
83
|
-
"error",
|
|
84
|
-
"any",
|
|
85
|
-
"Number",
|
|
86
|
-
"number",
|
|
87
|
-
"String",
|
|
88
|
-
"string",
|
|
89
|
-
"Boolean",
|
|
90
|
-
"boolean",
|
|
91
|
-
"Undefined",
|
|
92
|
-
"undefined",
|
|
93
|
-
],
|
|
94
|
-
|
|
95
|
-
"id-match": "error",
|
|
96
|
-
"max-classes-per-file": "off",
|
|
97
|
-
|
|
98
|
-
"max-len": ["error", {
|
|
99
|
-
code: 80,
|
|
100
|
-
}],
|
|
101
|
-
|
|
102
|
-
"new-parens": "error",
|
|
103
|
-
"no-bitwise": "off",
|
|
104
|
-
"no-caller": "error",
|
|
105
|
-
"no-cond-assign": "error",
|
|
106
|
-
"no-console": "off",
|
|
107
|
-
"no-debugger": "error",
|
|
108
|
-
"no-empty": "error",
|
|
109
|
-
"no-eval": "error",
|
|
110
|
-
"no-fallthrough": "off",
|
|
111
|
-
"no-invalid-this": "off",
|
|
112
|
-
"no-multiple-empty-lines": "off",
|
|
113
|
-
"no-new-wrappers": "error",
|
|
114
|
-
"no-shadow": "off",
|
|
115
|
-
"no-throw-literal": "error",
|
|
116
|
-
"no-trailing-spaces": "error",
|
|
117
|
-
"no-undef-init": "error",
|
|
118
|
-
"no-underscore-dangle": "error",
|
|
119
|
-
"no-unsafe-finally": "error",
|
|
120
|
-
"no-unused-expressions": "off",
|
|
121
|
-
"no-unused-labels": "error",
|
|
122
|
-
"no-var": "error",
|
|
123
|
-
"object-shorthand": "error",
|
|
124
|
-
"one-var": ["error", "never"],
|
|
125
|
-
"prefer-arrow/prefer-arrow-functions": "off",
|
|
126
|
-
"prefer-const": "error",
|
|
127
|
-
radix: "error",
|
|
128
|
-
"spaced-comment": "off",
|
|
129
|
-
"use-isnan": "error",
|
|
130
|
-
"valid-typeof": "off",
|
|
131
|
-
},
|
|
132
|
-
}];
|