amqplib 2.2.0 → 2.3.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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Change log for amqplib
2
2
 
3
+ ## v2.3.0
4
+ - Reject a `connection.tune` whose `frameMax` is below the protocol minimum of 4096 (the spec allows 0 or at least frame-min-size). Previously such a value was accepted, and the first publish whose encoded frames total 2048 bytes or more entered a loop in `sendContent` that never advanced, exhausting memory. `sendContent` now also throws if `frameMax` leaves no room for a body frame (GHSA-37f9-7m79-6866, reported by Rizwan Bedekar ([@r-bedekar](https://github.com/r-bedekar)); fix based in part on their patch and tests)
5
+ - Add a `maxFrameSize` connection option, also settable as a URL query parameter, bounding the payload size of frames accepted from the server. The default is 64 MiB, or the negotiated `frameMax` if that is larger, so raising `frameMax` needs no second option; `0` disables the limit. Incoming frame sizes were previously unchecked, so a frame header declaring up to 4 GiB made the client buffer, and repeatedly re-copy, that much data before looking at it. An oversized frame is now refused from its header alone and closes the connection with `Frame size N exceeds maxFrameSize M`. The limit is deliberately separate from the negotiated `frameMax`, since some brokers send larger frames without splitting them (see #784). **Upgrade note:** a broker that delivers an unsplit frame larger than the limit will now have that connection closed; pass `maxFrameSize: 0`, or a higher value, to restore the previous behaviour (GHSA-37f9-7m79-6866)
6
+
7
+ ## v2.2.1
8
+ - Return the server acknowledgement from promise-based `channel.cancel`, matching the callback API (thanks [@edsonsims](https://github.com/edsonsims)).
9
+ - Type `cancel` replies as `Replies.Cancel` (`{ consumerTag }`) in both the promise and callback API declarations, which previously claimed `Replies.Empty`.
10
+ - Fix callback-based `waitForConfirms` never calling back when invoked after the confirm channel closed with unconfirmed messages; it now yields a `channel closed` error, matching the promise API (thanks [@youdie006](https://github.com/youdie006)).
11
+ - `waitForConfirms` (both APIs) now always fails with `channel closed` after the channel closes with unconfirmed messages. Previously it reported success if every outstanding message had its own callback, or on any call after the first.
12
+ - The callback API's `waitForConfirms` callback is now required in the TypeScript declarations, matching the docs. Calling it without one threw at runtime.
13
+ - Replace `.npmignore` with a `files` allowlist in `package.json` so the published package no longer includes tests, tooling config, dotfiles or the defs generator. Install size drops from 577 kB to 358 kB (fixes #859, thanks [@rubiesonthesky](https://github.com/rubiesonthesky))
14
+
3
15
  ## v2.2.0
4
16
  - Add `calculateDelay` recovery option for supplying a custom reconnect delay strategy, e.g. full jitter, decorrelated jitter or a fixed schedule. When set, `maxDelay` is not applied to its return value; when it throws or returns an invalid value, recovery gives up as if `maxRetries` were exhausted (fixes #855, thanks [@GiHoon1123](https://github.com/GiHoon1123))
5
17
  - The built-in reconnect delay no longer exceeds `maxDelay`. Previously the cap was applied before jitter, so delays could overshoot it by up to the jitter fraction. The exponential base is now capped at `maxDelay / (1 + jitter)` so the full jitter range fits under the cap; with default settings the steady-state delay is now spread over 20-30s rather than 24-36s
package/callback_api.d.ts CHANGED
@@ -103,7 +103,7 @@ export interface Channel extends events.EventEmitter {
103
103
  callback?: (err: Error, ok: Replies.Consume) => void,
104
104
  ): void;
105
105
 
106
- cancel(consumerTag: string, callback?: (err: Error, ok: Replies.Empty) => void): void;
106
+ cancel(consumerTag: string, callback?: (err: Error, ok: Replies.Cancel) => void): void;
107
107
  get(queue: string, options?: Options.Get, callback?: (err: Error, ok: GetMessage | false) => void): void;
108
108
 
109
109
  ack(message: Message, allUpTo?: boolean): void;
@@ -132,7 +132,7 @@ export interface ConfirmChannel extends Channel {
132
132
  callback?: (err: Error, ok: Replies.Empty) => void,
133
133
  ): boolean;
134
134
 
135
- waitForConfirms(callback?: (err?: Error) => void): void;
135
+ waitForConfirms(callback: (err?: Error) => void): void;
136
136
  }
137
137
 
138
138
  export declare class IllegalOperationError extends Error {
package/index.d.ts CHANGED
@@ -77,7 +77,7 @@ export interface Channel extends events.EventEmitter {
77
77
  options?: Options.Consume,
78
78
  ): Promise<Replies.Consume>;
79
79
 
80
- cancel(consumerTag: string): Promise<Replies.Empty>;
80
+ cancel(consumerTag: string): Promise<Replies.Cancel>;
81
81
  get(queue: string, options?: Options.Get): Promise<GetMessage | false>;
82
82
 
83
83
  ack(message: Message, allUpTo?: boolean): void;
@@ -255,28 +255,9 @@ class ConfirmChannel extends Channel {
255
255
  }
256
256
 
257
257
  waitForConfirms(k) {
258
- const awaiting = [];
259
- const unconfirmed = this.unconfirmed;
260
- unconfirmed.forEach((val, index) => {
261
- if (val === null); // already confirmed
262
- else {
263
- const confirmed = new Promise((resolve, reject) => {
264
- unconfirmed[index] = (err) => {
265
- if (val) val(err);
266
- if (err === null) resolve();
267
- else reject(err);
268
- };
269
- });
270
- awaiting.push(confirmed);
271
- }
272
- });
273
- return Promise.all(awaiting).then(
274
- () => {
275
- k();
276
- },
277
- (err) => {
278
- k(err);
279
- },
258
+ this._waitForConfirms().then(
259
+ () => k(),
260
+ (err) => k(err),
280
261
  );
281
262
  }
282
263
  }
package/lib/channel.js CHANGED
@@ -21,6 +21,7 @@ class Channel extends EventEmitter {
21
21
  // for unconfirmed messages
22
22
  this.lwm = 1; // the least, unconfirmed deliveryTag
23
23
  this.unconfirmed = []; // rolling window of delivery callbacks
24
+ this.closedWithUnconfirmed = false;
24
25
  this.on(
25
26
  'ack',
26
27
  this.handleConfirm.bind(this, (cb) => {
@@ -39,7 +40,10 @@ class Channel extends EventEmitter {
39
40
  const cb = this.unconfirmed[i];
40
41
  if (cb) cb(err);
41
42
  }
42
- this.unconfirmed = this.unconfirmed.filter((cb) => cb === false);
43
+ // Remember that confirms were lost, so every later waitForConfirms
44
+ // fails, whether or not the messages had their own callbacks
45
+ this.closedWithUnconfirmed = this.unconfirmed.some((cb) => cb !== null);
46
+ this.unconfirmed = [];
43
47
  });
44
48
  // message frame state machine
45
49
  this.handleMessage = acceptDeliveryOrReturn;
@@ -243,6 +247,28 @@ class Channel extends EventEmitter {
243
247
  }
244
248
  }
245
249
 
250
+ // Resolves once every outstanding message is confirmed, and rejects on
251
+ // the first nack, or if the channel closes before all are confirmed.
252
+ // Shared by the promise and callback ConfirmChannel#waitForConfirms.
253
+ _waitForConfirms() {
254
+ if (this.closedWithUnconfirmed) return Promise.reject(new Error('channel closed'));
255
+ const awaiting = [];
256
+ const unconfirmed = this.unconfirmed;
257
+ unconfirmed.forEach((cb, index) => {
258
+ if (cb !== null) {
259
+ const confirmed = new Promise((resolve, reject) => {
260
+ unconfirmed[index] = (err) => {
261
+ if (cb) cb(err);
262
+ if (err === null) resolve();
263
+ else reject(err);
264
+ };
265
+ });
266
+ awaiting.push(confirmed);
267
+ }
268
+ });
269
+ return Promise.all(awaiting);
270
+ }
271
+
246
272
  pushConfirmCallback(cb) {
247
273
  // `null` is used specifically for marking already confirmed slots,
248
274
  // so I coerce `undefined` and `null` to false; functions are never
@@ -146,12 +146,9 @@ class Channel extends BaseChannel {
146
146
  }
147
147
 
148
148
  async cancel(consumerTag) {
149
- await promisify((cb) => {
150
- this._rpc(defs.BasicCancel, Args.cancel(consumerTag), defs.BasicCancelOk, cb);
151
- })().then((ok) => {
152
- this.unregisterConsumer(consumerTag);
153
- return ok.fields;
154
- });
149
+ const fields = await this.rpc(defs.BasicCancel, Args.cancel(consumerTag), defs.BasicCancelOk);
150
+ this.unregisterConsumer(consumerTag);
151
+ return fields;
155
152
  }
156
153
 
157
154
  get(queue, options) {
@@ -234,29 +231,7 @@ class ConfirmChannel extends Channel {
234
231
  }
235
232
 
236
233
  waitForConfirms() {
237
- const awaiting = [];
238
- const unconfirmed = this.unconfirmed;
239
- unconfirmed.forEach((cb, index) => {
240
- if (cb !== null) {
241
- const confirmed = new Promise((resolve, reject) => {
242
- unconfirmed[index] = (err) => {
243
- if (cb) cb(err);
244
- if (err === null) resolve();
245
- else reject(err);
246
- };
247
- });
248
- awaiting.push(confirmed);
249
- }
250
- });
251
- // Channel closed
252
- if (!this.pending) {
253
- const err = new Error('channel closed');
254
- while (unconfirmed.length > 0) {
255
- const cb = unconfirmed.shift();
256
- if (cb) cb(err);
257
- }
258
- }
259
- return Promise.all(awaiting);
234
+ return this._waitForConfirms();
260
235
  }
261
236
  }
262
237
 
package/lib/connect.js CHANGED
@@ -47,6 +47,11 @@ function openFrames(vhost, query, credentials, extraClientProperties) {
47
47
  return val === undefined ? def : parseInt(val, 10);
48
48
  }
49
49
 
50
+ function maxFrameSizeOrUndefined(val) {
51
+ if (val == null) return undefined;
52
+ return /^\d+$/.test(String(val)) ? Number(val) : NaN;
53
+ }
54
+
50
55
  const clientProperties = Object.create(CLIENT_PROPERTIES);
51
56
 
52
57
  return {
@@ -61,6 +66,8 @@ function openFrames(vhost, query, credentials, extraClientProperties) {
61
66
  frameMax: intOrDefault(query.frameMax, 131072),
62
67
  heartbeat: query.heartbeat != null ? intOrDefault(query.heartbeat, 0) : null,
63
68
 
69
+ maxFrameSize: maxFrameSizeOrUndefined(query.maxFrameSize),
70
+
64
71
  // open
65
72
  virtualHost: vhost,
66
73
  capabilities: '',
@@ -120,6 +127,7 @@ function connect(url, socketOptions, openCallback) {
120
127
  channelMax: url.channelMax,
121
128
  frameMax: url.frameMax,
122
129
  heartbeat: url.heartbeat,
130
+ maxFrameSize: url.maxFrameSize,
123
131
  };
124
132
 
125
133
  fields = openFrames(url.vhost, config, sockopts.credentials || credentials.plain(user, pass), extraClientProperties);
package/lib/connection.js CHANGED
@@ -27,6 +27,7 @@ const DEFAULT_WRITE_HWM = 1024;
27
27
  // at once. Note that this is less than the minimum frame size: if it
28
28
  // was greater, we might have to fragment the content.
29
29
  const SINGLE_CHUNK_THRESHOLD = 2048;
30
+ const DEFAULT_MAX_FRAME_SIZE = 64 * 1024 * 1024;
30
31
 
31
32
  class Connection extends EventEmitter {
32
33
  constructor(underlying) {
@@ -38,6 +39,7 @@ class Connection extends EventEmitter {
38
39
  // frames
39
40
  this.rest = Buffer.alloc(0);
40
41
  this.frameMax = constants.FRAME_MIN_SIZE;
42
+ this.maxFrameSize = DEFAULT_MAX_FRAME_SIZE;
41
43
  this.sentSinceLastCheck = false;
42
44
  this.recvSinceLastCheck = false;
43
45
 
@@ -86,6 +88,17 @@ class Connection extends EventEmitter {
86
88
  const self = this;
87
89
  const openCallback = openCallback0 || (() => {});
88
90
 
91
+ const maxFrameSize = allFields.maxFrameSize;
92
+ const maxFrameSizeDefaulted = maxFrameSize === undefined || maxFrameSize === null;
93
+ if (maxFrameSizeDefaulted) {
94
+ self.maxFrameSize = DEFAULT_MAX_FRAME_SIZE;
95
+ } else if (!Number.isInteger(maxFrameSize) || (maxFrameSize !== 0 && maxFrameSize < constants.FRAME_MIN_SIZE)) {
96
+ bail(new TypeError(fmt('maxFrameSize must be 0 (no limit) or an integer of at least %d; got %s', constants.FRAME_MIN_SIZE, maxFrameSize)));
97
+ return;
98
+ } else {
99
+ self.maxFrameSize = maxFrameSize;
100
+ }
101
+
89
102
  // This is where we'll put our negotiated values
90
103
  const tunedOptions = Object.create(allFields);
91
104
 
@@ -157,6 +170,10 @@ class Connection extends EventEmitter {
157
170
  break;
158
171
  case defs.ConnectionTune: {
159
172
  const fields = reply.fields;
173
+ if (fields.frameMax !== 0 && fields.frameMax < constants.FRAME_MIN_SIZE) {
174
+ bail(new Error(fmt('Server frameMax %d is below the protocol minimum %d', fields.frameMax, constants.FRAME_MIN_SIZE)));
175
+ return;
176
+ }
160
177
  tunedOptions.frameMax = negotiate(fields.frameMax, allFields.frameMax);
161
178
  tunedOptions.channelMax = negotiate(fields.channelMax, allFields.channelMax);
162
179
  if (allFields.heartbeat == null) {
@@ -191,6 +208,9 @@ class Connection extends EventEmitter {
191
208
  // value is zero, meaning "no, no limits"
192
209
  self.channelMax = tunedOptions.channelMax || 0xffff;
193
210
  self.frameMax = tunedOptions.frameMax || 0xffffffff;
211
+ if (maxFrameSizeDefaulted && tunedOptions.frameMax !== 0 && tunedOptions.frameMax > self.maxFrameSize) {
212
+ self.maxFrameSize = tunedOptions.frameMax;
213
+ }
194
214
  // 0 means "no heartbeat", rather than "maximum period of
195
215
  // heartbeating"
196
216
  self.heartbeat = tunedOptions.heartbeat;
@@ -526,6 +546,9 @@ class Connection extends EventEmitter {
526
546
  const buffer = this.channels[channel].buffer;
527
547
 
528
548
  const maxBody = this.frameMax - FRAME_OVERHEAD;
549
+ if (!(maxBody > 0)) {
550
+ throw new Error(fmt('Cannot send content: frameMax %d leaves no room for a body frame', this.frameMax));
551
+ }
529
552
 
530
553
  for (let offset = 0; offset < body.length; offset += maxBody) {
531
554
  const end = offset + maxBody;
@@ -539,7 +562,7 @@ class Connection extends EventEmitter {
539
562
 
540
563
  recvFrame() {
541
564
  // %%% identifying invariants might help here?
542
- const frame = parseFrame(this.rest);
565
+ const frame = parseFrame(this.rest, this.maxFrameSize);
543
566
 
544
567
  if (!frame) {
545
568
  const incoming = this.stream.read();
@@ -654,3 +677,4 @@ function isFatalError(error) {
654
677
 
655
678
  module.exports.Connection = Connection;
656
679
  module.exports.isFatalError = isFatalError;
680
+ module.exports.DEFAULT_MAX_FRAME_SIZE = DEFAULT_MAX_FRAME_SIZE;
package/lib/frame.js CHANGED
@@ -78,7 +78,7 @@ module.exports.makeBodyFrame = (channel, payload) => {
78
78
  * @arg { number } max
79
79
  * @returns { FrameStructure | boolean }
80
80
  */
81
- function parseFrame(bin) {
81
+ function parseFrame(bin, max) {
82
82
  if (bin.length < FRAME_HEADER_BYTES) {
83
83
  return false;
84
84
  }
@@ -87,6 +87,10 @@ function parseFrame(bin) {
87
87
  const channel = bin.readUInt16BE(1);
88
88
  const size = bin.readUInt32BE(3);
89
89
 
90
+ if (max > 0 && size > max) {
91
+ throw new Error(`Frame size ${size} exceeds maxFrameSize ${max}`);
92
+ }
93
+
90
94
  const totalSize = FRAME_HEADER_BYTES + size + FRAME_END_BYTES;
91
95
 
92
96
  if (bin.length < totalSize) {
@@ -19,6 +19,9 @@ export namespace Replies {
19
19
  interface Consume {
20
20
  consumerTag: string;
21
21
  }
22
+ interface Cancel {
23
+ consumerTag: string;
24
+ }
22
25
  }
23
26
 
24
27
  export namespace Options {
@@ -30,6 +33,7 @@ export namespace Options {
30
33
  password?: string;
31
34
  locale?: string;
32
35
  frameMax?: number;
36
+ maxFrameSize?: number;
33
37
  heartbeat?: number;
34
38
  vhost?: string;
35
39
  channelMax?: number;
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "amqplib",
3
3
  "homepage": "http://amqp-node.github.io/amqplib/",
4
4
  "main": "./channel_api.js",
5
- "version": "2.2.0",
5
+ "version": "2.3.0",
6
6
  "types": "./index.d.ts",
7
7
  "exports": {
8
8
  ".": {
@@ -46,5 +46,15 @@
46
46
  "RabbitMQ"
47
47
  ],
48
48
  "author": "Michael Bridgen <mikeb@squaremobius.net>",
49
- "license": "MIT"
49
+ "license": "MIT",
50
+ "files": [
51
+ "lib",
52
+ "examples",
53
+ "channel_api.js",
54
+ "callback_api.js",
55
+ "index.d.ts",
56
+ "callback_api.d.ts",
57
+ "CHANGELOG.md",
58
+ "LICENSE-MIT"
59
+ ]
50
60
  }
@@ -1,9 +0,0 @@
1
- Please follow the following process when I ask you to disable a lint rule using /lint-disable $rule
2
-
3
- 1. Create and change to a new branch. Ask for the branch name.
4
- 2. If the rule is off by default, delete it (See https://biomejs.dev/linter/javascript/rules/ for rule details)
5
- 3. If the rule is on by default, configure it to off in biome.json
6
- 4. Update the project changelog
7
- 5. Add the changes
8
- 6. Commit the changes
9
- 7. Push the changes
@@ -1,12 +0,0 @@
1
- Please follow the following process when I ask you to enable a lint rule using /lint-enable $rule
2
-
3
- 1. Create and change to a new branch. Ask for the branch name.
4
- 2. Configure the rule to error in biome.json
5
- 3. Run lint (npm run lint). If there are NO errors, goto step 8
6
- 4. Use biome to SAFELY auto fix (npm run lint -- --fix). If there are errors stop and ask for instruction.
7
- 5. Run the tests (npm test). If there are errors, stop and ask for instruction.
8
- 6. If the rule defaults to error, delete it (See https://biomejs.dev/linter/javascript/rules/ for rule details)
9
- 7. Update the project changelog
10
- 8. Add the changes
11
- 9. Commit the changes
12
- 10. Push the changes
@@ -1,41 +0,0 @@
1
- ---
2
- name: Bug report / Support request
3
- about: Report a bug or ask for help
4
- title: ''
5
- labels: ''
6
- assignees: ''
7
-
8
- ---
9
-
10
- Before reporting a bug or requesting support please...
11
-
12
- 1. Read the troubleshooting guide
13
-
14
- - https://amqp-node.github.io/amqplib/#troubleshooting
15
-
16
- 2. Search for existing open and closed issues
17
-
18
- - https://github.com/amqp-node/amqplib/issues?q=is%3Aissue+TYPE+YOUR+KEYWORDS+HERE
19
-
20
- 3. Ensure you are familiar with the amqplib Channel API and RabbitMQ basics
21
-
22
- - https://amqp-node.github.io/amqplib/channel_api.html
23
- - https://www.cloudamqp.com/blog/part1-rabbitmq-for-beginners-what-is-rabbitmq.html
24
- - https://www.rabbitmq.com/tutorials/amqp-concepts.html
25
-
26
- If the above does not help, please provide as much of the following information as is relevant...
27
-
28
- - A clear and concise description of the problem
29
- - RabbitMQ version
30
- - amqplib version
31
- - Node.js version
32
- - The simplest possible code snippet that demonstrates the problem
33
- - A stack trace
34
-
35
- Please format code snippets and/or stack traces using GitHub Markdown
36
-
37
- - https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks).
38
-
39
- Thank you
40
-
41
-
@@ -1,113 +0,0 @@
1
- # Runs when a GitHub release is published (draft releases do not trigger it).
2
- #
3
- # Tests the tagged commit on every supported Node.js version, then stages the
4
- # package on npm with `npm stage publish` using OIDC trusted publishing, so no
5
- # npm token is stored in this repository. Nothing goes live until a maintainer
6
- # reviews the staged version and approves it with 2FA:
7
- #
8
- # npm stage list amqplib
9
- # npm stage approve <stage-id>
10
- #
11
- # Prerequisites on npmjs.com: the package's trusted publisher must be set to
12
- # this repository and this workflow file (publish.yml), with allowed actions
13
- # restricted to `npm stage publish`.
14
-
15
- name: Publish
16
-
17
- on:
18
- release:
19
- types: [published]
20
-
21
- permissions:
22
- contents: read
23
-
24
- jobs:
25
- build:
26
- runs-on: ubuntu-latest
27
- services:
28
- rabbitmq:
29
- image: rabbitmq:4.2-alpine
30
- ports:
31
- - 5672:5672
32
-
33
- strategy:
34
- matrix:
35
- node-version: [18.x, 20.x, 22.x, 24.x]
36
- # See supported Node.js release schedule at https://nodejs.org/en/about/releases/
37
-
38
- steps:
39
- - uses: actions/checkout@v4
40
-
41
- - name: Use Node.js ${{ matrix.node-version }}
42
- uses: actions/setup-node@v4
43
- with:
44
- node-version: ${{ matrix.node-version }}
45
- cache: "npm"
46
-
47
- - run: npm ci
48
-
49
- - name: Wait for RabbitMQ
50
- run: |
51
- n=0
52
- while :
53
- do
54
- sleep 5
55
- echo 'HELO\n\n\n\n' | nc localhost 5672 | grep AMQP
56
- [[ $? = 0 ]] && break || ((n++))
57
- (( n >= 5 )) && break
58
- done
59
-
60
- - run: echo 'HELO\n\n\n\n' | nc localhost 5672 | grep AMQP
61
-
62
- - run: make test
63
-
64
- stage:
65
- needs: build
66
- runs-on: ubuntu-latest
67
- permissions:
68
- contents: read
69
- id-token: write # required for OIDC trusted publishing
70
- steps:
71
- - uses: actions/checkout@v4
72
-
73
- - uses: actions/setup-node@v4
74
- with:
75
- node-version: 24.x
76
- cache: "npm"
77
- registry-url: https://registry.npmjs.org/
78
-
79
- # npm stage publish needs npm 11.15.0 or later
80
- - run: npm install -g npm@^11.15.0
81
-
82
- - run: npm ci
83
-
84
- - name: Check release tag matches package.json version
85
- id: version
86
- env:
87
- RELEASE_TAG: ${{ github.event.release.tag_name }}
88
- run: |
89
- version="$(node -p "require('./package.json').version")"
90
- if [[ "$RELEASE_TAG" != "v${version}" ]]; then
91
- echo "Release tag ${RELEASE_TAG} does not match package.json version v${version}" >&2
92
- exit 1
93
- fi
94
- echo "version=${version}" >> "$GITHUB_OUTPUT"
95
-
96
- # Pre-release versions (e.g. 2.2.0-rc.0) are staged under the `next`
97
- # dist-tag; everything else under `latest`. The tag is fixed at staging
98
- # time and cannot be changed on approval.
99
- - name: Choose dist-tag
100
- id: dist-tag
101
- env:
102
- VERSION: ${{ steps.version.outputs.version }}
103
- run: |
104
- if [[ "$VERSION" == *-* ]]; then
105
- echo "tag=next" >> "$GITHUB_OUTPUT"
106
- else
107
- echo "tag=latest" >> "$GITHUB_OUTPUT"
108
- fi
109
-
110
- - name: Stage publish
111
- env:
112
- DIST_TAG: ${{ steps.dist-tag.outputs.tag }}
113
- run: npm stage publish --tag "$DIST_TAG" --provenance
@@ -1,54 +0,0 @@
1
- # This workflow will do a clean install of node dependencies, cache/restore them, build the source code and run tests across different versions of node
2
- # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
3
-
4
- name: Node.js CI
5
-
6
- on:
7
- push:
8
- branches: [main]
9
- pull_request:
10
- branches: [main]
11
-
12
- jobs:
13
- build:
14
- runs-on: ubuntu-latest
15
- services:
16
- rabbitmq:
17
- image: rabbitmq:4.2-alpine
18
- ports:
19
- - 5672:5672
20
-
21
- strategy:
22
- matrix:
23
- node-version: [18.x, 20.x, 22.x, 24.x]
24
- # See supported Node.js release schedule at https://nodejs.org/en/about/releases/
25
-
26
- steps:
27
- - uses: actions/checkout@v3
28
- - name: Use Node.js ${{ matrix.node-version }}
29
- uses: actions/setup-node@v4
30
- with:
31
- node-version: ${{ matrix.node-version }}
32
- cache: "npm"
33
-
34
- # Install all prerequisites
35
- - run: npm ci
36
-
37
- # Ensure RabbitMQ is available before continuing
38
- - run: |
39
- n=0
40
- while :
41
- do
42
- sleep 5
43
- echo 'HELO\n\n\n\n' | nc localhost 5672 | grep AMQP
44
- [[ $? = 0 ]] && break || ((n++))
45
- (( n >= 5 )) && break
46
- done
47
-
48
- - run: echo 'HELO\n\n\n\n' | nc localhost 5672 | grep AMQP
49
-
50
- # Check TypeScript types
51
- - run: npm run typecheck
52
-
53
- # Run the tests
54
- - run: make test
package/CLAUDE.md DELETED
@@ -1,12 +0,0 @@
1
- # Project-Specific Guidelines for amqplib
2
-
3
- ## Generated Files - DO NOT EDIT
4
- - `lib/defs.js` - This file is generated code. NEVER modify it directly.
5
- - Generated via `make lib/defs.js` from the AMQP specification
6
- - If linting issues arise, exclude this file from linting rules rather than modifying it
7
-
8
- ## Commit Requirements
9
- - NEVER commit when tests are failing
10
- - NEVER commit when there are lint errors
11
- - Always run `npm test` and `npm run lint` before committing
12
- - If tests are failing, investigate and fix the issue before proceeding
package/Makefile DELETED
@@ -1,42 +0,0 @@
1
- RABBITMQ_SRC_VERSION=v3.12.13
2
- JSON=amqp-rabbitmq-0.9.1.json
3
- AMQP_JSON=https://raw.githubusercontent.com/rabbitmq/rabbitmq-server/$(RABBITMQ_SRC_VERSION)/deps/rabbitmq_codegen/$(JSON)
4
-
5
- NODEJS_VERSIONS='18.20.0' '20.10.0' '22.14.0' '24.7.0'
6
-
7
- UGLIFY=./node_modules/.bin/uglifyjs
8
-
9
- .PHONY: test test-all-nodejs coverage lib/defs.js
10
-
11
- error:
12
- @echo "Please choose one of the following targets: test, test-all-nodejs, coverage, lib/defs.js"
13
- @exit 1
14
-
15
- test:
16
- node --test --test-reporter=spec
17
-
18
- test-all-nodejs:
19
- for v in $(NODEJS_VERSIONS); \
20
- do echo "-- Node version $$v --"; \
21
- nave use $$v node --test --test-reporter=spec; \
22
- done
23
-
24
- coverage:
25
- node --test --test-coverage --test-reporter=spec
26
-
27
- lib/defs.js: clean bin/generate-defs test
28
-
29
- clean:
30
- rm -f lib/defs.js bin/amqp-rabbitmq-0.9.1.json
31
-
32
- bin/generate-defs: $(UGLIFY) bin/generate-defs.js bin/amqp-rabbitmq-0.9.1.json
33
- (cd bin; node ./generate-defs.js > ../lib/defs.js)
34
- $(UGLIFY) ./lib/defs.js -o ./lib/defs.js \
35
- -c 'sequences=false' --comments \
36
- -b 'indent-level=2' 2>&1 | (grep -v 'WARN' || true)
37
-
38
- bin/amqp-rabbitmq-0.9.1.json:
39
- curl -L $(AMQP_JSON) > $@
40
-
41
- $(UGLIFY):
42
- npm install