Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
timeout-minutes: 10
strategy:
matrix:
node: [22, 24]
node: [22, 24, 26]
services:
postgres:
image: postgres:17
Expand Down Expand Up @@ -63,3 +63,5 @@ jobs:
run: npm install
- name: Run linter
run: npm run lint
- name: Type check
run: npm run test:types
40 changes: 40 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
declare function createLeaderElector (
options: createLeaderElector.LeaderElectorOptions
): createLeaderElector.LeaderElector

declare namespace createLeaderElector {
export interface Pool {
connect (): Promise<unknown>
query (text: string, ...args: unknown[]): Promise<unknown>
}

export interface Logger {
info: (...args: unknown[]) => void
debug: (...args: unknown[]) => void
warn: (...args: unknown[]) => void
error: (...args: unknown[]) => void
}

export interface NotificationChannel<TPayload = unknown> {
channel: string
onNotification: (payload: TPayload) => void | Promise<void>
}

export interface LeaderElectorOptions {
pool: Pool
lock: number
poll?: number
channels?: NotificationChannel[]
log?: Logger
onLeadershipChange?: ((isLeader: boolean) => void) | null
}

export interface LeaderElector {
start (): Promise<void>
stop (): Promise<void>
notify (payload: unknown, channelName: string): Promise<void>
isLeader (): boolean
}
}

export = createLeaderElector
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,19 @@
"version": "0.2.0",
"description": "PostgreSQL advisory lock-based leader election with notification channels",
"main": "index.js",
"types": "index.d.ts",
"scripts": {
"test": "node --test test/*.test.js",
"test:types": "tsc -p tsconfig.json",
"lint": "standard ."
},
"dependencies": {
"pg": "^8.20.0",
"pino": "^10.3.1"
},
"devDependencies": {
"standard": "^17.1.2"
"standard": "^17.1.2",
"typescript": "^5.9.3"
},
"engines": {
"node": ">=22.0.0"
Expand Down
36 changes: 36 additions & 0 deletions test/types.test-d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import createLeaderElector = require('../index')

const fakePool: createLeaderElector.Pool = {
connect: async () => ({}),
query: async () => ({})
}

const elector: createLeaderElector.LeaderElector = createLeaderElector({
pool: fakePool,
lock: 42,
poll: 1000,
channels: [
{
channel: 'deferred_messages',
onNotification: async (payload: unknown) => { void payload }
}
],
log: {
info: () => {},
debug: () => {},
warn: () => {},
error: () => {}
},
onLeadershipChange: (isLeader: boolean) => { void isLeader }
})

const _minimal: createLeaderElector.LeaderElector = createLeaderElector({
pool: fakePool,
lock: 1
})

const _start: Promise<void> = elector.start()
const _stop: Promise<void> = elector.stop()
const _notify: Promise<void> = elector.notify({ id: 1 }, 'channel')
const _isLeader: boolean = elector.isLeader()
void _minimal; void _start; void _stop; void _notify; void _isLeader
11 changes: 11 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"compilerOptions": {
"module": "nodenext",
"moduleResolution": "nodenext",
"target": "esnext",
"strict": true,
"noEmit": true,
"skipLibCheck": true
},
"include": ["index.d.ts", "test/types.test-d.ts"]
}
Loading