mongodash 2.6.0 → 2.8.0

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.
Files changed (66) hide show
  1. package/README.md +45 -0
  2. package/dist/lib/ConcurrentRunner.js +47 -2
  3. package/dist/lib/ConcurrentRunner.js.map +1 -1
  4. package/dist/lib/createContinuousLock.js +23 -6
  5. package/dist/lib/createContinuousLock.js.map +1 -1
  6. package/dist/lib/cronTasks.js +119 -64
  7. package/dist/lib/cronTasks.js.map +1 -1
  8. package/dist/lib/index.js +11 -6
  9. package/dist/lib/index.js.map +1 -1
  10. package/dist/lib/reactiveTasks/LeaderElector.js +21 -3
  11. package/dist/lib/reactiveTasks/LeaderElector.js.map +1 -1
  12. package/dist/lib/reactiveTasks/MetricsCollector.js +118 -39
  13. package/dist/lib/reactiveTasks/MetricsCollector.js.map +1 -1
  14. package/dist/lib/reactiveTasks/ReactiveTaskPlanner.js +66 -31
  15. package/dist/lib/reactiveTasks/ReactiveTaskPlanner.js.map +1 -1
  16. package/dist/lib/reactiveTasks/ReactiveTaskRepository.js +19 -1
  17. package/dist/lib/reactiveTasks/ReactiveTaskRepository.js.map +1 -1
  18. package/dist/lib/reactiveTasks/ReactiveTaskTypes.js +7 -1
  19. package/dist/lib/reactiveTasks/ReactiveTaskTypes.js.map +1 -1
  20. package/dist/lib/reactiveTasks/ReactiveTaskWorker.js +80 -5
  21. package/dist/lib/reactiveTasks/ReactiveTaskWorker.js.map +1 -1
  22. package/dist/lib/reactiveTasks/index.js +20 -13
  23. package/dist/lib/reactiveTasks/index.js.map +1 -1
  24. package/dist/lib/task-management/OperationalTaskController.js +1 -1
  25. package/dist/lib/task-management/OperationalTaskController.js.map +1 -1
  26. package/dist/lib/testing/assertNoReactiveTaskErrors.js +16 -12
  27. package/dist/lib/testing/assertNoReactiveTaskErrors.js.map +1 -1
  28. package/dist/lib/testing/index.js +2 -0
  29. package/dist/lib/testing/index.js.map +1 -1
  30. package/dist/lib/testing/resolveWhitelistFilter.js +48 -0
  31. package/dist/lib/testing/resolveWhitelistFilter.js.map +1 -0
  32. package/dist/lib/testing/waitUntilReactiveTasksIdle.js +17 -46
  33. package/dist/lib/testing/waitUntilReactiveTasksIdle.js.map +1 -1
  34. package/dist/types/ConcurrentRunner.d.ts +16 -0
  35. package/dist/types/createContinuousLock.d.ts +17 -1
  36. package/dist/types/cronTasks.d.ts +17 -2
  37. package/dist/types/index.d.ts +2 -2
  38. package/dist/types/reactiveTasks/LeaderElector.d.ts +15 -1
  39. package/dist/types/reactiveTasks/MetricsCollector.d.ts +19 -8
  40. package/dist/types/reactiveTasks/ReactiveTaskPlanner.d.ts +11 -0
  41. package/dist/types/reactiveTasks/ReactiveTaskRepository.d.ts +10 -1
  42. package/dist/types/reactiveTasks/ReactiveTaskTypes.d.ts +19 -0
  43. package/dist/types/reactiveTasks/index.d.ts +8 -2
  44. package/dist/types/testing/assertNoReactiveTaskErrors.d.ts +4 -4
  45. package/dist/types/testing/index.d.ts +2 -0
  46. package/dist/types/testing/resolveWhitelistFilter.d.ts +35 -0
  47. package/dist/types/testing/waitUntilReactiveTasksIdle.d.ts +7 -13
  48. package/docs/.vitepress/config.mts +9 -1
  49. package/docs/cron-tasks.md +130 -1
  50. package/docs/error-handling.md +156 -0
  51. package/docs/reactive-tasks/guides.md +1 -1
  52. package/docs/reactive-tasks/index.md +2 -2
  53. package/docs/reactive-tasks/monitoring.md +7 -0
  54. package/docs/reactive-tasks/testing.md +187 -0
  55. package/docs/testing.md +60 -94
  56. package/package.json +36 -24
  57. package/docs/.vitepress/cache/deps/_metadata.json +0 -31
  58. package/docs/.vitepress/cache/deps/chunk-LE5NDSFD.js +0 -12824
  59. package/docs/.vitepress/cache/deps/chunk-LE5NDSFD.js.map +0 -7
  60. package/docs/.vitepress/cache/deps/package.json +0 -3
  61. package/docs/.vitepress/cache/deps/vitepress___@vue_devtools-api.js +0 -4505
  62. package/docs/.vitepress/cache/deps/vitepress___@vue_devtools-api.js.map +0 -7
  63. package/docs/.vitepress/cache/deps/vitepress___@vueuse_core.js +0 -9731
  64. package/docs/.vitepress/cache/deps/vitepress___@vueuse_core.js.map +0 -7
  65. package/docs/.vitepress/cache/deps/vue.js +0 -347
  66. package/docs/.vitepress/cache/deps/vue.js.map +0 -7
package/docs/testing.md CHANGED
@@ -1,123 +1,89 @@
1
- # Testing Utilities
1
+ # Testing
2
2
 
3
- Mongodash provides a set of utilities to help you write robust End-to-End (E2E) tests for your asynchronous applications.
3
+ Mongodash's asynchronous subsystems (reactive tasks, cron, change streams)
4
+ need more than a fixed `setTimeout` in tests. This page lists the testing
5
+ utilities the library provides, grouped by scope.
4
6
 
5
- ## `waitUntilReactiveTasksIdle`
7
+ ## Generic
6
8
 
7
- A specialized helper for testing **Reactive Tasks**. It blocks execution until the reactive task system is completely quiesced.
9
+ ### `waitUntil`
8
10
 
9
- This is essential for testing "side effects" and "cascading tasks" where you want to assert the final state of the system after a chain of events has completed.
11
+ A general-purpose polling helper that resolves once a condition function
12
+ returns `true`. Useful when the built-in waits are too broad (e.g. waiting
13
+ for an HTTP side effect or an external queue) or as a building block for
14
+ your own utilities.
10
15
 
11
- ### What it waits for
12
-
13
- It resolves only when **ALL** of the following validation checks pass simultaneously (or for the `stabilityDurationMs`):
14
-
15
- 1. **Planner Empty**: The internal `ReactiveTaskPlanner` has no buffered Change Stream events waiting to be flushed.
16
- 2. **Workers Idle**: No `ResponsiveTaskWorker` is currently processing a task (active count is 0).
17
- 3. **Database Settled**: No tasks in any registered task collection are in `pending`, `processing`, or `processing_dirty` state.
18
- * **Exception**: Pending tasks scheduled for the **distant future** (beyond the current timeout + stability window) are ignored. This ensures that long-term retries (e.g., "retry in 1 hour") do not cause your tests to timeout.
19
-
20
- ### Usage
16
+ It includes a **time-jump detector**: if a breakpoint or a suspended laptop
17
+ pauses the event loop for more than a second, the deadline is extended by
18
+ the observed pause so the wait does not falsely time out.
21
19
 
22
20
  ```typescript
23
- import { waitUntilReactiveTasksIdle } from 'mongodash/testing';
24
-
25
- it('should process user registration workflow', async () => {
26
- // 1. Trigger the workflow
27
- await users.insertOne({ email: 'test@example.com', status: 'new' });
21
+ import { waitUntil } from 'mongodash/testing';
28
22
 
29
- // 2. Wait for the reactive task to process ONLY the insert
30
- // AND any subsequent tasks that might be triggered (e.g. sending email)
31
- await waitUntilReactiveTasksIdle();
32
-
33
- // 3. Assert the final state
34
- const emailTask = await emailTasks.findOne({ email: 'test@example.com' });
35
- expect(emailTask).toBeDefined();
36
- expect(emailTask.status).toBe('sent');
37
- });
23
+ await waitUntil(
24
+ async () => (await getBalance(userId)) === 100,
25
+ { timeoutMs: 5000, pollIntervalMs: 50, stabilityDurationMs: 100 },
26
+ );
38
27
  ```
39
28
 
40
- ### Configuration
41
-
42
- You can override the default options if needed, though the defaults are tuned for general use.
29
+ | Option | Default | Description |
30
+ | :--- | :--- | :--- |
31
+ | `timeoutMs` | `10000` | Maximum time before throwing. |
32
+ | `pollIntervalMs` | `50` | How often the condition is evaluated. |
33
+ | `stabilityDurationMs` | `0` | How long the condition must _remain_ true before resolving. Prevents flakiness when a state briefly flips true then false. |
43
34
 
44
- ```typescript
45
- await waitUntilReactiveTasksIdle({
46
- timeoutMs: 30000,
47
- stabilityDurationMs: 200, // Wait for 200ms of "silence" to catch in-flight cascading tasks
48
- });
49
- ```
35
+ ## Reactive tasks
50
36
 
51
- ## `assertNoReactiveTaskErrors`
37
+ See [**Testing Reactive Tasks**](./reactive-tasks/testing.md) for:
52
38
 
53
- Ensures that no reactive tasks have failed during your test execution. This is critical for catching "silent failures" where a task failed but didn't crash the application or the test.
39
+ - `waitUntilReactiveTasksIdle` wait for the reactive-task subsystem to
40
+ fully settle (planner empty, workers idle, DB drained).
41
+ - `assertNoReactiveTaskErrors` — catch silent failures a handler logged
42
+ but never threw up the stack.
43
+ - `configureForTesting` — replace production debounce / polling intervals
44
+ with testing-friendly values.
45
+ - `WhitelistRule` — scope the above helpers to specific
46
+ collections / documents / tasks for parallel tests.
54
47
 
55
- ### Features
48
+ ## Cron tasks
56
49
 
57
- - **Time filtering**: Only checks for errors that occurred after a specific time (e.g., the start of your test).
58
- - **Scope**: Can check globally or for specific source documents.
59
- - **Filtering**: Allows ignoring "expected" errors (e.g., negative tests).
50
+ Cron tasks expose a few helpers from the main entry point that are primarily
51
+ useful in tests. They live on the main `mongodash` module alongside the
52
+ cron API rather than under `mongodash/testing`:
60
53
 
61
- ### Usage
54
+ ### Run a task synchronously
62
55
 
63
56
  ```typescript
64
- import { assertNoReactiveTaskErrors } from 'mongodash/testing';
65
-
66
- it('should process successfully', async () => {
67
- const startTime = new Date();
57
+ import { runCronTask } from 'mongodash';
68
58
 
69
- // ... run test steps ...
70
- await waitUntilReactiveTasksIdle();
71
-
72
- // Verify no tasks failed
73
- await assertNoReactiveTaskErrors({ since: startTime });
59
+ it('processes pending invoices', async () => {
60
+ await runCronTask('invoice-sweep');
61
+ const processed = await invoices.countDocuments({ status: 'processed' });
62
+ expect(processed).toBeGreaterThan(0);
74
63
  });
75
64
  ```
76
65
 
77
- ### Options
66
+ `runCronTask(taskId)` enqueues the task and awaits its completion. It throws
67
+ if called from inside another running cron task (use
68
+ `scheduleCronTaskImmediately` / `triggerCronTask` there instead).
78
69
 
79
- ```typescript
80
- await assertNoReactiveTaskErrors({
81
- since: startTime, // Required: Check for errors after this time
82
- sourceDocIds: [docId], // Optional: Limit check to specific source documents
83
- excludeErrors: [ // Optional: Allow known errors (strings or RegEx)
84
- 'Expected Failure',
85
- /Authorization Error/
86
- ]
87
- });
88
- ```
89
-
90
- ## `configureForTesting`
91
-
92
- A helper to configure the library for fast, deterministic testing execution.
93
-
94
- By default, the library is optimized for production scenarios (e.g. `debounce: 1000ms`, `minPollMs: 200ms`). In tests, these delays cause unnecessary slowness.
95
-
96
- `configureForTesting` overrides these defaults globally to minimal values (e.g. `10ms`).
97
-
98
- ### Usage
99
-
100
- Call this **once** in your global test setup (e.g. `jest.setup.js` or `beforeAll`).
70
+ ### Disable the scheduler
101
71
 
102
- It works regardless of whether you call it before or after registering your tasks.
72
+ Running background cron jobs in unit tests causes non-determinism. Two
73
+ options:
103
74
 
104
75
  ```typescript
105
- import { configureForTesting } from 'mongodash/testing';
106
-
107
- beforeAll(() => {
108
- // Sets debounce, polling, and batching to 10ms
109
- configureForTesting();
110
- });
76
+ // Option A: never auto-start.
77
+ await mongodash.init({ ..., runCronTasks: false });
78
+
79
+ // Option B: stop after init.
80
+ import { stopCronTasks, startCronTasks } from 'mongodash';
81
+ stopCronTasks();
82
+ // ...
83
+ startCronTasks(); // if a test needs it back
111
84
  ```
112
85
 
113
- ### Options
114
-
115
- You can customize specific values if needed:
86
+ Called before the first `cronTask()` registration, `stopCronTasks()` also
87
+ prevents any task from starting later in the process.
116
88
 
117
- ```typescript
118
- configureForTesting({
119
- debounce: 0, // Force 0ms debounce (immediate execution)
120
- minPollMs: 50, // Poll every 50ms
121
- minBatchIntervalMs: 50
122
- });
123
- ```
89
+ See [**Cron Tasks**](./cron-tasks.md) for the full cron reference.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mongodash",
3
- "version": "2.6.0",
3
+ "version": "2.8.0",
4
4
  "type": "commonjs",
5
5
  "description": "An utility library delivering super-useful and super-simple tools using MongoDB",
6
6
  "main": "./dist/lib/index.js",
@@ -10,6 +10,18 @@
10
10
  "types": "./dist/types/index.d.ts",
11
11
  "import": "./dist/lib/index.js",
12
12
  "default": "./dist/lib/index.js"
13
+ },
14
+ "./testing": {
15
+ "types": "./dist/types/testing/index.d.ts",
16
+ "import": "./dist/lib/testing/index.js",
17
+ "default": "./dist/lib/testing/index.js"
18
+ }
19
+ },
20
+ "typesVersions": {
21
+ "*": {
22
+ "testing": [
23
+ "./dist/types/testing/index.d.ts"
24
+ ]
13
25
  }
14
26
  },
15
27
  "files": [
@@ -49,7 +61,7 @@
49
61
  },
50
62
  "dependencies": {
51
63
  "@sapphire/duration": "^1.2.0",
52
- "cron-parser": "^5.4.0",
64
+ "cron-parser": "^5.5.0",
53
65
  "debug": "^4.4.3",
54
66
  "fast-json-stable-stringify": "^2.1.0"
55
67
  },
@@ -64,41 +76,41 @@
64
76
  },
65
77
  "devDependencies": {
66
78
  "@arethetypeswrong/cli": "^0.18.2",
67
- "@commitlint/cli": "^20.2.0",
79
+ "@commitlint/cli": "^20.5.0",
68
80
  "@semantic-release/changelog": "^6.0.3",
69
81
  "@semantic-release/git": "^10.0.1",
70
- "@stryker-mutator/core": "^9.4.0",
71
- "@stryker-mutator/jest-runner": "^9.4.0",
72
- "@types/debug": "^4.1.12",
82
+ "@stryker-mutator/core": "^9.6.1",
83
+ "@stryker-mutator/jest-runner": "^9.6.1",
84
+ "@types/debug": "^4.1.13",
73
85
  "@types/jest": "^30.0.0",
74
- "@types/lodash": "^4.17.21",
75
- "@types/node": "^25.0.3",
76
- "@types/sinon": "^21.0.0",
77
- "@typescript-eslint/eslint-plugin": "^8.51.0",
78
- "@typescript-eslint/parser": "^8.51.0",
86
+ "@types/lodash": "^4.17.24",
87
+ "@types/node": "^25.6.0",
88
+ "@types/sinon": "^21.0.1",
89
+ "@typescript-eslint/eslint-plugin": "^8.58.2",
90
+ "@typescript-eslint/parser": "^8.58.2",
79
91
  "correlation-id": "^5.2.0",
80
92
  "coveralls": "^3.1.1",
81
93
  "deepdash": "^5.3.9",
82
- "eslint": "^9.39.2",
94
+ "eslint": "^9.39.4",
83
95
  "eslint-config-prettier": "^10.1.8",
84
96
  "eslint-plugin-no-only-tests": "^3.3.0",
85
- "eslint-plugin-prettier": "^5.5.4",
86
- "globals": "^16.5.0",
97
+ "eslint-plugin-prettier": "^5.5.5",
98
+ "globals": "^17.5.0",
87
99
  "husky": "^9.1.7",
88
- "jest": "^30.2.0",
100
+ "jest": "^30.3.0",
89
101
  "js-yaml": "4.1.1",
90
- "lint-staged": "^16.2.7",
91
- "lodash": "^4.17.21",
92
- "mongodb": "^7.0.0",
102
+ "lint-staged": "^16.4.0",
103
+ "lodash": "^4.18.1",
104
+ "mongodb": "^7.1.1",
93
105
  "npm-run-all": "^4.1.5",
94
106
  "organize-imports-cli": "^0.10.0",
95
- "path-to-regexp": "8.3.0",
96
- "prettier": "^3.7.4",
107
+ "path-to-regexp": "8.4.2",
108
+ "prettier": "^3.8.3",
97
109
  "prom-client": "^15.1.3",
98
- "publint": "^0.3.16",
99
- "semantic-release": "^25.0.2",
100
- "sinon": "^21.0.1",
101
- "ts-jest": "^29.4.6",
110
+ "publint": "^0.3.18",
111
+ "semantic-release": "^25.0.3",
112
+ "sinon": "^21.1.2",
113
+ "ts-jest": "^29.4.9",
102
114
  "ts-node": "^10.9.2",
103
115
  "typescript": "^5.9.3",
104
116
  "vitepress": "^1.6.4"
@@ -1,31 +0,0 @@
1
- {
2
- "hash": "65ae190e",
3
- "configHash": "1734f516",
4
- "lockfileHash": "68b03582",
5
- "browserHash": "246dbe54",
6
- "optimized": {
7
- "vue": {
8
- "src": "../../../../node_modules/vue/dist/vue.runtime.esm-bundler.js",
9
- "file": "vue.js",
10
- "fileHash": "1ab8f95b",
11
- "needsInterop": false
12
- },
13
- "vitepress > @vue/devtools-api": {
14
- "src": "../../../../node_modules/@vue/devtools-api/dist/index.js",
15
- "file": "vitepress___@vue_devtools-api.js",
16
- "fileHash": "eb38336e",
17
- "needsInterop": false
18
- },
19
- "vitepress > @vueuse/core": {
20
- "src": "../../../../node_modules/@vueuse/core/index.mjs",
21
- "file": "vitepress___@vueuse_core.js",
22
- "fileHash": "d06fa19f",
23
- "needsInterop": false
24
- }
25
- },
26
- "chunks": {
27
- "chunk-LE5NDSFD": {
28
- "file": "chunk-LE5NDSFD.js"
29
- }
30
- }
31
- }