Summary
The root entry point re-exports QueueSchemaService, whose constructor
signature is constructor(connection: Knex). That single export is the only
reason the generated root declaration file imports knex:
// build/index.d.ts
import { Knex } from 'knex' // line 4
…
declare class QueueSchemaService {
constructor(connection: Knex) // line 536
createJobsTable(tableName?: string,
extend?: (table: Knex.CreateTableBuilder) => void): Promise<void> // line 541
createSchedulesTable(tableName?: string,
extend?: (table: Knex.CreateTableBuilder) => void): Promise<void> // line 554
}
knex is declared as an optional peer dependency:
"peerDependencies": { "knex": "^3.0.0" },
"peerDependenciesMeta": { "knex": { "optional": true } }
but because the type is reachable from the root barrel, any consumer type
checking with skipLibCheck: false has to install knex even when they only
use the Kysely, Redis or Sync driver — or a custom Adapter of their own.
Reproduction
mkdir repro && cd repro
npm init -y && npm pkg set type=module
npm i @boringnode/queue
npm i -D typescript@7.0.2 @types/node@25.9.4
cat > tsconfig.json <<'JSON'
{
"compilerOptions": {
"target": "esnext",
"module": "preserve",
"moduleResolution": "bundler",
"types": ["node"],
"strict": true,
"skipLibCheck": false,
"noEmit": true
}
}
JSON
printf 'import { Job } from "@boringnode/queue"\nexport const j = Job\n' > index.ts
npx tsc --noEmit
Result:
node_modules/@boringnode/queue/build/index.d.ts(4,22): error TS2307:
Cannot find module 'knex' or its corresponding type declarations.
Installing knex purely for its types — it is never imported by the
application — makes the error disappear:
npm i -D knex@3.1.0
npx tsc --noEmit # clean
Environment: @boringnode/queue@0.7.1, typescript@7.0.2,
@types/node@25.9.4.
Why this looks like an oversight rather than a design choice
The repository already applies the intended layout everywhere else. The root
barrel is the only place it is not applied:
| Module |
Adapter |
Schema service |
src/drivers/kysely_adapter.ts |
KyselyAdapter |
KyselyQueueSchemaService — re-exported at line 17 |
src/drivers/knex_adapter.ts |
KnexAdapter |
— |
index.ts |
— |
QueueSchemaService — exported at line 8 |
src/drivers/kysely_adapter.ts:17 is exactly the line this issue is asking for
in the Knex module:
export { KyselyQueueSchemaService } from '../services/kysely_queue_schema.js'
Because Kysely's schema service is scoped to its driver subpath, importing
@boringnode/queue never pulls in kysely. The same holds for ioredis and
for @opentelemetry/*. Only knex leaks, and only through this one export.
Proposed fix
Move the export next to the adapter that needs it, mirroring the Kysely module:
// index.ts
- export { QueueSchemaService } from './src/services/queue_schema.js'
// src/drivers/knex_adapter.ts
+ export { QueueSchemaService } from '../services/queue_schema.js'
Consumers would then import it the same way they already import the Kysely
equivalent:
import { KnexAdapter, QueueSchemaService } from '@boringnode/queue/drivers/knex_adapter'
Renaming it to KnexQueueSchemaService at the same time would make the two
modules symmetrical, though that is orthogonal to the leak itself.
This is a breaking change for anyone importing QueueSchemaService from the
root today. If that matters for the current release line, an alternative that
keeps the leak out of the barrel without breaking anyone is to type the
constructor structurally and drop the import { Knex }, since the class only
needs a query-builder-shaped value. The move is cleaner, though, and consistent
with how the package already treats Kysely.
Happy to send a PR
The change is small and I have a working reproduction; I can open a PR with
whichever of the two options you prefer.
Related packaging/typecheck reports opened at the same time, from the same evaluation: #25 (tracingChannel type arguments on @types/node v26) and #26 (./types re-exporting an OpenTelemetry type). They are independent of each other; each reproduction above pins the environment so only one of them shows up at a time.
Summary
The root entry point re-exports
QueueSchemaService, whose constructorsignature is
constructor(connection: Knex). That single export is the onlyreason the generated root declaration file imports
knex:knexis declared as an optional peer dependency:but because the type is reachable from the root barrel, any consumer type
checking with
skipLibCheck: falsehas to installknexeven when they onlyuse the Kysely, Redis or Sync driver — or a custom
Adapterof their own.Reproduction
Result:
Installing
knexpurely for its types — it is never imported by theapplication — makes the error disappear:
npm i -D knex@3.1.0 npx tsc --noEmit # cleanEnvironment:
@boringnode/queue@0.7.1,typescript@7.0.2,@types/node@25.9.4.Why this looks like an oversight rather than a design choice
The repository already applies the intended layout everywhere else. The root
barrel is the only place it is not applied:
src/drivers/kysely_adapter.tsKyselyAdapterKyselyQueueSchemaService— re-exported at line 17src/drivers/knex_adapter.tsKnexAdapterindex.tsQueueSchemaService— exported at line 8src/drivers/kysely_adapter.ts:17is exactly the line this issue is asking forin the Knex module:
Because Kysely's schema service is scoped to its driver subpath, importing
@boringnode/queuenever pulls inkysely. The same holds forioredisandfor
@opentelemetry/*. Onlyknexleaks, and only through this one export.Proposed fix
Move the export next to the adapter that needs it, mirroring the Kysely module:
// index.ts - export { QueueSchemaService } from './src/services/queue_schema.js'// src/drivers/knex_adapter.ts + export { QueueSchemaService } from '../services/queue_schema.js'Consumers would then import it the same way they already import the Kysely
equivalent:
Renaming it to
KnexQueueSchemaServiceat the same time would make the twomodules symmetrical, though that is orthogonal to the leak itself.
This is a breaking change for anyone importing
QueueSchemaServicefrom theroot today. If that matters for the current release line, an alternative that
keeps the leak out of the barrel without breaking anyone is to type the
constructor structurally and drop the
import { Knex }, since the class onlyneeds a query-builder-shaped value. The move is cleaner, though, and consistent
with how the package already treats Kysely.
Happy to send a PR
The change is small and I have a working reproduction; I can open a PR with
whichever of the two options you prefer.
Related packaging/typecheck reports opened at the same time, from the same evaluation: #25 (
tracingChanneltype arguments on@types/nodev26) and #26 (./typesre-exporting an OpenTelemetry type). They are independent of each other; each reproduction above pins the environment so only one of them shows up at a time.