Skip to content

Switch to awilix 13#424

Merged
kibertoad merged 10 commits intomainfrom
feat/awilix-13
Apr 2, 2026
Merged

Switch to awilix 13#424
kibertoad merged 10 commits intomainfrom
feat/awilix-13

Conversation

@kibertoad
Copy link
Copy Markdown
Owner

@kibertoad kibertoad commented Apr 2, 2026

Summary by CodeRabbit

  • Refactor

    • Safer early-abort checks and stronger TypeScript typing in test utilities for improved runtime stability and developer safety.
  • Chores

    • Bumped awilix dev dependency to v13.0.3 across packages.
    • Local compose startup now waits for the messaging broker and includes a broker healthcheck.
  • Tests

    • Reformatted test declarations and standardized setup/teardown for greater consistency.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 2, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Minor code cleanups and typing tweaks: optional chaining in AMQP consumer, Awilix devDependency bumped to v13 across packages, test DI containers given explicit generics, Docker Compose adds RabbitMQ healthcheck and amqp start script uses --wait, and several GCP Pub/Sub tests were reformatted.

Changes

Cohort / File(s) Summary
Optional chaining refactor
packages/amqp/lib/AbstractAmqpConsumer.ts
Replaced two early-abort checks with optional chaining (?.) when inspecting resolved message results; behavior unchanged.
Awilix devDependency upgrade
packages/amqp/package.json, packages/core/package.json, packages/gcp-pubsub/package.json, packages/kafka/package.json, packages/sns/package.json, packages/sqs/package.json
Bumped awilix devDependency to ^13.0.3 in multiple packages; only package.json changes.
Container generic type annotations (tests)
packages/amqp/test/utils/testContext.ts, packages/core/test/testContext.ts, packages/gcp-pubsub/test/utils/testContext.ts, packages/kafka/test/utils/testContext.ts, packages/sns/test/utils/testContext.ts, packages/sqs/test/utils/testContext.ts
Added explicit generic parameter createContainer<Dependencies>(...) to Awilix container creation in test utilities to tighten TypeScript types only.
Docker Compose / start script
docker-compose.yml, packages/amqp/package.json
Added RabbitMQ healthcheck to docker-compose.yml; updated packages/amqp docker:start script to docker compose up -d --wait rabbitmq.
GCP Pub/Sub test formatting and flows
packages/gcp-pubsub/test/consumers/PubSubPermissionConsumer.deduplication.spec.ts, packages/gcp-pubsub/test/consumers/PubSubPermissionConsumer.payloadOffloading.spec.ts, packages/gcp-pubsub/test/consumers/PubSubPermissionConsumer.subscriptionRetry.spec.ts
Reformatted Vitest it(...) blocks and adjusted test scaffolding (setup/try-finally, creation vs locator configs) without changing assertions or core test logic.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • kjamrog
  • CarlosGamero

Poem

🐇 I hopped through lines with joyful cheer,

Replaced checks that tripped up here and there,
Typed my crates and nudged a script to wait,
RabbitMQ now purrs—how neat, how great! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Switch to awilix 13' directly reflects the main change: upgrading the awilix dependency from version 12.x to 13.0.3 across multiple packages and adapting code to use Awilix's generic type parameters.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/awilix-13

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/amqp/test/utils/testContext.ts (1)

114-123: ⚠️ Potential issue | 🟠 Major

Restore lifetime: Lifetime.SINGLETON to amqpConnectionManager.

The amqpConnectionManager registration is missing an explicit lifetime specification. In Awilix, the default is TRANSIENT, meaning a new instance is created per resolution. This is problematic for a connection manager with asyncInit/asyncDispose lifecycle hooks:

  • Multiple AMQP connections will be created if the dependency is resolved more than once
  • Resource cleanup will be incomplete since only one instance's asyncDispose will be properly tracked

All other similar registrations in this file (permissionConsumer, permissionPublisher, queuePublisherManager, topicPublisherManager) explicitly specify lifetime: Lifetime.SINGLETON. This registration should too.

Fix
     amqpConnectionManager: asFunction(
       ({ logger }: Dependencies) => {
         return new AmqpConnectionManager(config, logger)
       },
       {
+        lifetime: Lifetime.SINGLETON,
         asyncInit: 'init',
         asyncDispose: 'close',
         asyncDisposePriority: 1,
       },
     ),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/amqp/test/utils/testContext.ts` around lines 114 - 123, The
amqpConnectionManager registration is missing an explicit Lifetime.SINGLETON,
causing Awilix to treat it as TRANSIENT and create multiple
AmqpConnectionManager instances (breaking asyncInit/asyncDispose lifecycle
tracking); update the asFunction registration for amqpConnectionManager to
include lifetime: Lifetime.SINGLETON (matching other registrations like
permissionConsumer/permissionPublisher/queuePublisherManager/topicPublisherManager)
so a single AmqpConnectionManager instance is created and its
asyncInit/asyncDispose hooks are managed correctly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/amqp/package.json`:
- Around line 40-53: Update the package TypeScript configuration to pin
TS6-breaking defaults so builds don’t change: add or update tsconfig.json's
"compilerOptions" to explicitly set "strict" (false or your intended value),
"target" (e.g., "es2022" or your prior target), "module" (e.g., "commonjs" or
"esnext" to match runtime), "types" (include needed libs instead of empty), and
"noUncheckedSideEffectImports" (set to false if you relied on previous
behavior), and include "ignoreDeprecations": "6.0" to silence TS6 deprecation
errors; ensure the package.json's "typescript" devDependency remains as intended
and run CI/tests to verify everything passes.

---

Outside diff comments:
In `@packages/amqp/test/utils/testContext.ts`:
- Around line 114-123: The amqpConnectionManager registration is missing an
explicit Lifetime.SINGLETON, causing Awilix to treat it as TRANSIENT and create
multiple AmqpConnectionManager instances (breaking asyncInit/asyncDispose
lifecycle tracking); update the asFunction registration for
amqpConnectionManager to include lifetime: Lifetime.SINGLETON (matching other
registrations like
permissionConsumer/permissionPublisher/queuePublisherManager/topicPublisherManager)
so a single AmqpConnectionManager instance is created and its
asyncInit/asyncDispose hooks are managed correctly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 06ffdfa1-24b5-4577-bf63-7b6a55229e3d

📥 Commits

Reviewing files that changed from the base of the PR and between c98a004 and d40d66b.

📒 Files selected for processing (9)
  • packages/amqp/lib/AbstractAmqpConsumer.ts
  • packages/amqp/package.json
  • packages/amqp/test/utils/testContext.ts
  • packages/core/package.json
  • packages/gcp-pubsub/package.json
  • packages/kafka/lib/utils/KafkaMessageBatchStream.ts
  • packages/kafka/package.json
  • packages/sns/package.json
  • packages/sqs/package.json

Comment on lines +40 to +53
"@biomejs/biome": "^2.3.15",
"@lokalise/biome-config": "^3.1.0",
"@lokalise/tsconfig": "^3.0.0",
"@message-queue-toolkit/core": "*",
"@types/amqplib": "0.10.8",
"@types/node": "^25.0.2",
"@vitest/coverage-v8": "^4.0.15",
"amqplib": "^0.10.8",
"awilix": "^12.0.5",
"awilix-manager": "^6.1.0",
"rimraf": "^6.0.1",
"typescript": "^5.9.3",
"vitest": "^4.0.15",
"zod": "^4.1.13"
"@types/node": "^25.5.0",
"@types/amqplib": "^0.10.8",
"@vitest/coverage-v8": "^4.0.18",
"amqplib": "^1.0.3",
"awilix": "^13.0.3",
"awilix-manager": "^6.4.0",
"rimraf": "^6.1.3",
"typescript": "^6.0.2",
"vitest": "^4.0.18",
"zod": "^4.3.6"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check for any amqplib-specific type imports or usages that might be affected
rg -n --type ts "from ['\"]amqplib['\"]"

Repository: kibertoad/message-queue-toolkit

Length of output: 1826


🌐 Web query:

amqplib 1.0 breaking changes from 0.10

💡 Result:

The breaking changes in amqplib 1.0.0 from 0.10.x are primarily: - Minimum Node.js requirement raised to v18 (previously supported down to v10 in 0.10.x). Other changes include: - Introduction of Biome formatter for code formatting. - Automated formatting via pre-commit hooks using Lefthook. - Formatted entire codebase with standardised rules. - Added npm format script. - Enabled and fixed lint rules: noUnusedFunctionParameters and noUnusedVariables. No API or behavioral changes to the AMQP client functionality are mentioned; the update appears to be a maintenance release focused on tooling, linting, and Node.js version support. The library remains an AMQP 0-9-1 client compatible with RabbitMQ.

Citations:


🌐 Web query:

awilix 13.0 breaking changes from 12.x

💡 Result:

The breaking changes in Awilix 13.0.0 from 12.x are all TypeScript-only and do not affect runtime behavior. They are: - Default cradle type changed from any to {}: createContainer without an explicit generic now returns AwilixContainer<{}> instead of AwilixContainer. This enables better type accumulation via register chaining. - register returns a new container type that includes the registered resolvers. - asValue now preserves literal types and deep readonly for objects. No migration is required for JavaScript users or runtime code. Subsequent patch releases (13.0.1-13.0.3) fixed ESM/CJS interop issues but introduced no additional breaking changes.

Citations:


🌐 Web query:

typescript 6.0 breaking changes

💡 Result:

TypeScript 6.0, released in March 2026, is a bridge release preparing for the native Go-based TypeScript 7.0. It introduces several breaking changes, deprecations, and default shifts. Deprecations can be temporarily ignored with "ignoreDeprecations": "6.0" in tsconfig.json, but will be removed in 7.0. Key breaking changes and deprecations: New Defaults (potentially breaking if relying on old defaults): - strict: true (was false) - target: es2025 (was ES3/ES5) - module: esnext - types: [] (was ["*"], now explicitly set e.g. ["node"]) - rootDir: config directory (was inferred) - noUncheckedSideEffectImports: true - alwaysStrict assumed everywhere (alwaysStrict: false deprecated) Deprecated/Removed Options: - target: es5 (minimum now es2015) - --outFile (removed; use bundlers) - baseUrl (deprecated; migrate paths) - moduleResolution: node10/node/classic (use nodenext/bundler) - module: amd/umd/system (removed) - downlevelIteration (deprecated) - esModuleInterop/allowSyntheticDefaultImports: false (now always true) - Legacy module Foo {} syntax (use namespace) - Import assertions: assert {} (use with {}; deprecated in static/dynamic imports) - Triple-slash references /// <reference ... /> - --stableTypeOrdering flag for diagnosing 7.0 differences Behavioral Changes: - Less context-sensitivity for this-less functions (tighter inference) - Type ordering in .d.ts may differ (use --stableTypeOrdering to preview) - dom lib now includes dom.iterable/asynciterable Migration Tips: - Use experimental ts5to6 codemod for baseUrl/rootDir. - Explicitly set types array. - Set rootDir if sources are in subdir. - Update imports for new interop. - See https://aka.ms/ts6 for official migration info. These align TypeScript with modern JS ecosystem (ESM, bundlers, evergreen browsers).

Citations:


Prioritize testing TypeScript 6.0 compatibility and configuration adjustments.

While amqplib 1.0.x has no API breaking changes (only Node.js v18+ requirement), TypeScript 6.0 introduces significant breaking changes including new compiler defaults: strict: true, target: es2025, module: esnext, types: [], and noUncheckedSideEffectImports: true. These defaults could affect the build and type checking without an explicit ignoreDeprecations: "6.0" configuration.

The awilix 13.x changes are TypeScript-only (type inference changes to cradle) and don't affect runtime behavior.

Verify that the tsconfig.json explicitly sets required configuration options and that CI passes with these new compiler defaults.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/amqp/package.json` around lines 40 - 53, Update the package
TypeScript configuration to pin TS6-breaking defaults so builds don’t change:
add or update tsconfig.json's "compilerOptions" to explicitly set "strict"
(false or your intended value), "target" (e.g., "es2022" or your prior target),
"module" (e.g., "commonjs" or "esnext" to match runtime), "types" (include
needed libs instead of empty), and "noUncheckedSideEffectImports" (set to false
if you relied on previous behavior), and include "ignoreDeprecations": "6.0" to
silence TS6 deprecation errors; ensure the package.json's "typescript"
devDependency remains as intended and run CI/tests to verify everything passes.

@kibertoad kibertoad changed the title Switch to awilix 13 and amqp 1.x Switch to awilix 13 Apr 2, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@packages/gcp-pubsub/test/consumers/PubSubPermissionConsumer.subscriptionRetry.spec.ts`:
- Around line 379-381: The delayed call using globalThis.setTimeout with an
async callback calling topic.createSubscription(SUBSCRIPTION_NAME) can produce
unhandled promise rejections; update the timeout callback to handle errors
(e.g., wrap the await topic.createSubscription(...) in a try/catch or attach a
.catch handler) and log or propagate the error appropriately so any rejection
from createSubscription is handled within the
PubSubPermissionConsumer.subscriptionRetry.spec.ts test.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a6b85c3a-5291-408a-9ce1-f96e2ece44f0

📥 Commits

Reviewing files that changed from the base of the PR and between a554bab and f4d7f5a.

📒 Files selected for processing (5)
  • docker-compose.yml
  • packages/amqp/package.json
  • packages/gcp-pubsub/test/consumers/PubSubPermissionConsumer.deduplication.spec.ts
  • packages/gcp-pubsub/test/consumers/PubSubPermissionConsumer.payloadOffloading.spec.ts
  • packages/gcp-pubsub/test/consumers/PubSubPermissionConsumer.subscriptionRetry.spec.ts
✅ Files skipped from review due to trivial changes (2)
  • packages/gcp-pubsub/test/consumers/PubSubPermissionConsumer.payloadOffloading.spec.ts
  • packages/gcp-pubsub/test/consumers/PubSubPermissionConsumer.deduplication.spec.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/amqp/package.json

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
packages/gcp-pubsub/test/consumers/PubSubPermissionConsumer.subscriptionRetry.spec.ts (1)

378-383: ⚠️ Potential issue | 🟠 Major

Detach-free delayed subscription creation to avoid flaky async failures.

The delayed globalThis.setTimeout task is not part of the test lifecycle, so failures can surface out-of-band and make this test flaky. Prefer an awaited delayed promise so errors are deterministic and cleanup-safe.

Proposed fix
-      // Create subscription after a delay (simulating eventual consistency)
-      globalThis.setTimeout(() => {
-        topic.createSubscription(SUBSCRIPTION_NAME).catch((err) => {
-          expect.unreachable(`Failed to create subscription in delayed callback: ${err}`)
-        })
-      }, 1500)
+      // Create subscription after a delay (simulating eventual consistency)
+      const delayedSubscriptionCreation = (async () => {
+        await setTimeout(1500)
+        await topic.createSubscription(SUBSCRIPTION_NAME)
+      })()

       try {
         // This should retry and eventually succeed when subscription is created
         await consumer.start()

         // `@ts-expect-error` - accessing private field for testing
         expect(consumer.isConsuming).toBe(true)
+        await delayedSubscriptionCreation
       } finally {
+        await delayedSubscriptionCreation.catch(() => undefined)
         await consumer.close()
       }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/gcp-pubsub/test/consumers/PubSubPermissionConsumer.subscriptionRetry.spec.ts`
around lines 378 - 383, The delayed globalThis.setTimeout callback that calls
topic.createSubscription(SUBSCRIPTION_NAME) should be converted into an awaited
delay inside the test so failures are caught by the test runner; replace the
detached setTimeout with an await new Promise(resolve => setTimeout(resolve,
1500)) (or a test util delay) followed by await
topic.createSubscription(SUBSCRIPTION_NAME) wrapped in try/catch and failing the
test with expect.unreachable or rethrowing the error so the error is
deterministic and cleanup-safe; update any references in this spec
(PubSubPermissionConsumer.subscriptionRetry.spec.ts) accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In
`@packages/gcp-pubsub/test/consumers/PubSubPermissionConsumer.subscriptionRetry.spec.ts`:
- Around line 378-383: The delayed globalThis.setTimeout callback that calls
topic.createSubscription(SUBSCRIPTION_NAME) should be converted into an awaited
delay inside the test so failures are caught by the test runner; replace the
detached setTimeout with an await new Promise(resolve => setTimeout(resolve,
1500)) (or a test util delay) followed by await
topic.createSubscription(SUBSCRIPTION_NAME) wrapped in try/catch and failing the
test with expect.unreachable or rethrowing the error so the error is
deterministic and cleanup-safe; update any references in this spec
(PubSubPermissionConsumer.subscriptionRetry.spec.ts) accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7539c4af-6fbb-410e-bf13-1830c7a574c5

📥 Commits

Reviewing files that changed from the base of the PR and between f4d7f5a and fcebbf8.

📒 Files selected for processing (1)
  • packages/gcp-pubsub/test/consumers/PubSubPermissionConsumer.subscriptionRetry.spec.ts

@kibertoad kibertoad merged commit b86d454 into main Apr 2, 2026
20 checks passed
@kibertoad kibertoad deleted the feat/awilix-13 branch April 2, 2026 22:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant