@onlineapps/cookbook-router 2.0.0 → 3.0.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.
- package/README.md +21 -1
- package/package.json +10 -5
- package/src/index.js +11 -2
- package/src/queueManager.js +55 -20
- package/src/router.js +1 -1
package/README.md
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
> Status: current
|
|
2
|
+
> Owns: the publication of a workflow message to a service's queue, once the registry says that service is available
|
|
3
|
+
|
|
4
|
+
<!-- BEGIN GENERATED: library-uniform — regenerate: npx oa-sync-template readme-uniform --all -->
|
|
5
|
+
Uniform: [library/orchestration](../../connector/conn-orch-validator/manifests/library.manifest.json)
|
|
6
|
+
|
|
7
|
+
Duty sections that apply:
|
|
8
|
+
|
|
9
|
+
- `all`: L-MAIN, L-ENGINES, L-TESTS, L-TEST-SCRIPT, L-PACK-TESTS, L-PINS, L-NO-FILE-RANGE, L-CHANGELOG, L-README, L-README-REGION, L-CONSUMER
|
|
10
|
+
<!-- END GENERATED: library-uniform -->
|
|
11
|
+
|
|
1
12
|
# @onlineapps/cookbook-router
|
|
2
13
|
|
|
3
14
|
Publishes a workflow message to a service's workflow queue, after checking with
|
|
@@ -62,7 +73,16 @@ verbatim to both collaborators, so their keys travel through it:
|
|
|
62
73
|
| `cacheEnabled` | `ServiceDiscovery` | `true` |
|
|
63
74
|
| `cacheTTL` | `ServiceDiscovery` (ms) | `300000` |
|
|
64
75
|
| `ensureQueues` | `QueueManager` — assert the queue before the first publish | `true` |
|
|
65
|
-
| `
|
|
76
|
+
| `queueOptions` | `QueueManager` — merged into every queue declaration | `{ durable: true }` |
|
|
77
|
+
| `publishOptions` | `QueueManager` — merged into every publish | `{ persistent: true }` |
|
|
78
|
+
|
|
79
|
+
A queue declaration and a message are two different things, so their defaults
|
|
80
|
+
are two objects with two owners. `queueOptions` reaches
|
|
81
|
+
`mqClient.assertQueue()`, whose declared option set is `durable`, `arguments`,
|
|
82
|
+
`exclusive` and `autoDelete`; `publishOptions` reaches `mqClient.publish()` as
|
|
83
|
+
amqplib message properties. One object fed both until d.396d, which meant the
|
|
84
|
+
queue was declared with `persistent` and the message published with `durable` —
|
|
85
|
+
neither of which the receiving side reads.
|
|
66
86
|
|
|
67
87
|
A key not in this table is not read by anything here.
|
|
68
88
|
|
package/package.json
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onlineapps/cookbook-router",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "Message routing for cookbook workflows - handles service discovery and queue routing",
|
|
5
|
+
"oa": {
|
|
6
|
+
"category": "orchestration"
|
|
7
|
+
},
|
|
5
8
|
"main": "src/index.js",
|
|
6
9
|
"scripts": {
|
|
7
|
-
"test": "
|
|
10
|
+
"test": "npm run test:unit",
|
|
11
|
+
"test:unit": "jest tests/unit",
|
|
8
12
|
"test:watch": "jest --watch",
|
|
9
13
|
"test:coverage": "jest --coverage",
|
|
10
|
-
"docs": "jsdoc2md --files src/**/*.js > API.md"
|
|
14
|
+
"docs": "jsdoc2md --files 'src/**/*.js' > API.md.tmp && mv API.md.tmp API.md || (rm -f API.md.tmp; exit 1)"
|
|
11
15
|
},
|
|
12
16
|
"keywords": [
|
|
13
17
|
"cookbook",
|
|
@@ -20,10 +24,11 @@
|
|
|
20
24
|
"license": "PROPRIETARY",
|
|
21
25
|
"dependencies": {},
|
|
22
26
|
"devDependencies": {
|
|
23
|
-
"jest": "^29.7.0"
|
|
27
|
+
"jest": "^29.7.0",
|
|
28
|
+
"jsdoc-to-markdown": "^8.0.0"
|
|
24
29
|
},
|
|
25
30
|
"engines": {
|
|
26
|
-
"node": ">=
|
|
31
|
+
"node": ">=24.0.0 <25"
|
|
27
32
|
},
|
|
28
33
|
"files": [
|
|
29
34
|
"src"
|
package/src/index.js
CHANGED
|
@@ -22,6 +22,9 @@ const CookbookRouter = require('./router');
|
|
|
22
22
|
const ServiceDiscovery = require('./serviceDiscovery');
|
|
23
23
|
const QueueManager = require('./queueManager');
|
|
24
24
|
|
|
25
|
+
// Single source of the published version — see module.exports.VERSION below.
|
|
26
|
+
const pkg = require('../package.json');
|
|
27
|
+
|
|
25
28
|
module.exports = {
|
|
26
29
|
// Main router class
|
|
27
30
|
CookbookRouter,
|
|
@@ -35,6 +38,12 @@ module.exports = {
|
|
|
35
38
|
return new CookbookRouter(mqClient, registryClient, options);
|
|
36
39
|
},
|
|
37
40
|
|
|
38
|
-
// Utility exports
|
|
39
|
-
|
|
41
|
+
// Utility exports.
|
|
42
|
+
//
|
|
43
|
+
// Read, never retyped: until 2026-09-03 this said '1.0.0' while the package
|
|
44
|
+
// was published at 2.0.0, so every consumer was told the package still sat on
|
|
45
|
+
// the major that the 2026-09-02 routing-rail removal had ended. A version is
|
|
46
|
+
// a descriptive fact and never belongs in a hand-written literal
|
|
47
|
+
// (.claude/rules/doc-code-binding.md §1).
|
|
48
|
+
VERSION: pkg.version
|
|
40
49
|
};
|
package/src/queueManager.js
CHANGED
|
@@ -1,21 +1,58 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* What describes the QUEUE. Handed to `mqClient.assertQueue()`, whose declared
|
|
5
|
+
* option set is `durable`, `arguments`, `exclusive`, `autoDelete`.
|
|
5
6
|
*/
|
|
7
|
+
const DEFAULT_QUEUE_OPTIONS = Object.freeze({ durable: true });
|
|
8
|
+
|
|
9
|
+
/** What describes the MESSAGE. Handed to `mqClient.publish()`. */
|
|
10
|
+
const DEFAULT_PUBLISH_OPTIONS = Object.freeze({ persistent: true });
|
|
6
11
|
|
|
12
|
+
/**
|
|
13
|
+
* QueueManager - Queue operations and management
|
|
14
|
+
*
|
|
15
|
+
* A QUEUE and a MESSAGE are two different things, and neither one's defaults
|
|
16
|
+
* belong to the other. Until d.396d this class held ONE object for both —
|
|
17
|
+
* `defaultOptions: { durable: true, persistent: true }` — and spread it into the
|
|
18
|
+
* publish AND into the queue declaration, so each call site was handed a key it
|
|
19
|
+
* has no use for. Measured in both directions on the unit tier:
|
|
20
|
+
*
|
|
21
|
+
* assertQueue('split.queue', { durable: true, persistent: true })
|
|
22
|
+
* publish('control.publish', …, { durable: true, persistent: true })
|
|
23
|
+
*
|
|
24
|
+
* `persistent` is a message property (amqplib `Options.Publish`) and means
|
|
25
|
+
* nothing to a queue; `durable` is a queue property and means nothing to a
|
|
26
|
+
* message. Nothing objected, because `@onlineapps/mq-client-core` read the two
|
|
27
|
+
* keys it knew and dropped the rest in silence — and since d.396c it no longer
|
|
28
|
+
* does: `assertQueue()` refuses an option it does not read, by name. What was an
|
|
29
|
+
* invisible confusion becomes a throw the moment the pin moves, and the cure is
|
|
30
|
+
* not to catch it but to stop sending a message property to a queue.
|
|
31
|
+
*
|
|
32
|
+
* So there are two defaults with two owners, `queueOptions` and `publishOptions`,
|
|
33
|
+
* and each call site merges only its own.
|
|
34
|
+
*/
|
|
7
35
|
class QueueManager {
|
|
8
36
|
constructor(mqClient, options = {}) {
|
|
9
37
|
this.mqClient = mqClient;
|
|
38
|
+
// `...options` comes FIRST so the computed keys below survive it. It used to
|
|
39
|
+
// come last, which undid the merge it was written to extend: a caller passing
|
|
40
|
+
// `{ durable: false }` replaced the whole defaults object rather than
|
|
41
|
+
// overriding one key of it, and lost the other default in the process. Every
|
|
42
|
+
// key this class does not compute is still forwarded untouched — `router.js`
|
|
43
|
+
// hands its whole options object to both collaborators.
|
|
10
44
|
this.options = {
|
|
45
|
+
...options,
|
|
11
46
|
ensureQueues: options.ensureQueues !== false,
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
...options.defaultOptions
|
|
47
|
+
queueOptions: {
|
|
48
|
+
...DEFAULT_QUEUE_OPTIONS,
|
|
49
|
+
...options.queueOptions
|
|
16
50
|
},
|
|
17
|
-
|
|
18
|
-
|
|
51
|
+
publishOptions: {
|
|
52
|
+
...DEFAULT_PUBLISH_OPTIONS,
|
|
53
|
+
...options.publishOptions
|
|
54
|
+
},
|
|
55
|
+
logger: options.logger || console
|
|
19
56
|
};
|
|
20
57
|
|
|
21
58
|
this.ensuredQueues = new Set();
|
|
@@ -25,7 +62,7 @@ class QueueManager {
|
|
|
25
62
|
* Publish message to queue
|
|
26
63
|
* @param {string} queueName - Target queue name
|
|
27
64
|
* @param {Object} message - Message to publish
|
|
28
|
-
* @param {Object} options - Publishing options
|
|
65
|
+
* @param {Object} options - Publishing options (amqplib `Options.Publish`)
|
|
29
66
|
* @returns {Promise<boolean>}
|
|
30
67
|
*/
|
|
31
68
|
async publish(queueName, message, options = {}) {
|
|
@@ -43,9 +80,9 @@ class QueueManager {
|
|
|
43
80
|
timestamp: new Date().toISOString()
|
|
44
81
|
};
|
|
45
82
|
|
|
46
|
-
// Merge publishing options
|
|
83
|
+
// Merge publishing options — the MESSAGE defaults, never the queue's.
|
|
47
84
|
const publishOptions = {
|
|
48
|
-
...this.options.
|
|
85
|
+
...this.options.publishOptions,
|
|
49
86
|
...options
|
|
50
87
|
};
|
|
51
88
|
|
|
@@ -73,7 +110,8 @@ class QueueManager {
|
|
|
73
110
|
/**
|
|
74
111
|
* Ensure queue exists
|
|
75
112
|
* @param {string} queueName - Queue name
|
|
76
|
-
* @param {Object} options - Queue options
|
|
113
|
+
* @param {Object} options - Queue options, as `mqClient.assertQueue()` declares
|
|
114
|
+
* them (`durable`, `arguments`, `exclusive`, `autoDelete`)
|
|
77
115
|
* @returns {Promise<boolean>}
|
|
78
116
|
*/
|
|
79
117
|
async ensureQueue(queueName, options = {}) {
|
|
@@ -82,19 +120,16 @@ class QueueManager {
|
|
|
82
120
|
return true;
|
|
83
121
|
}
|
|
84
122
|
|
|
123
|
+
// The QUEUE defaults, never the message's.
|
|
85
124
|
const queueOptions = {
|
|
86
|
-
...this.options.
|
|
125
|
+
...this.options.queueOptions,
|
|
87
126
|
...options
|
|
88
127
|
};
|
|
89
128
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
return true;
|
|
94
|
-
} catch (error) {
|
|
95
|
-
throw error;
|
|
96
|
-
}
|
|
129
|
+
await this.mqClient.assertQueue(queueName, queueOptions);
|
|
130
|
+
this.ensuredQueues.add(queueName);
|
|
131
|
+
return true;
|
|
97
132
|
}
|
|
98
133
|
}
|
|
99
134
|
|
|
100
|
-
module.exports = QueueManager;
|
|
135
|
+
module.exports = QueueManager;
|
package/src/router.js
CHANGED
|
@@ -27,7 +27,7 @@ class CookbookRouter {
|
|
|
27
27
|
// reads is dead (`.claude/rules/change-discipline.md` § Removing).
|
|
28
28
|
// `options` is still forwarded whole to the two collaborators below, so
|
|
29
29
|
// their own keys (`cacheEnabled`, `cacheTTL`, `ensureQueues`,
|
|
30
|
-
// `
|
|
30
|
+
// `queueOptions`, `publishOptions`) reach them unchanged.
|
|
31
31
|
this.options = {
|
|
32
32
|
logger: console,
|
|
33
33
|
...options
|