lonnymq 0.0.26 → 0.0.28

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # LonnyMQ
2
2
 
3
- A high-performance, multi-tenant PostgreSQL message queue implementation for Node.js/TypeScript.
3
+ A high-performance, multi-tenant PostgreSQL message queue implementation for Node.js/TypeScript. Docs can be found [here](https://tlonny.github.io/lonnymq)
4
4
 
5
5
  ## Features
6
6
 
@@ -35,14 +35,16 @@ for (const sql of queue.install()) {
35
35
  for (let ix = 0; ix < 500; ix += 1) {
36
36
  await queue.message.create({
37
37
  databaseClient,
38
- content: Buffer.from("Hello"),
39
- lockMs: 30_000
38
+ content: Buffer.from("Hello")
40
39
  })
41
40
  }
42
41
 
43
42
  // Process messages
44
43
  while (true) {
45
- const dequeueResult = await queue.dequeue({ databaseClient })
44
+ const dequeueResult = await queue.dequeue({
45
+ databaseClient,
46
+ lockMs: 30_000
47
+ })
46
48
  if (dequeueResult.resultType === "MESSAGE_NOT_AVAILABLE") {
47
49
  break
48
50
  }
@@ -87,18 +89,16 @@ await queue
87
89
 
88
90
  ## Message Creation
89
91
 
90
- You can add a message to the queue using the `create` function. By default, messages are assigned to a random channel, ensuring fair distribution:
92
+ You can add a message to the queue using the `create` function. By default, messages are assigned to a unique channel, resulting in basic FIFO behaviour.
91
93
 
92
94
  ```typescript
93
95
  await queue.message.create({
94
96
  databaseClient,
95
97
  content: Buffer.from("Hello, world"),
96
- lockMs: 30000,
97
- delayMs: 5000
98
98
  })
99
99
  ```
100
100
 
101
- If you need to assign messages to specific channels (for example, to take advantage of concurrency or rate limiting features), you can specify the channel explicitly:
101
+ If you need to assign messages to specific channels (for example, to take advantage of fairness, concurrency or rate limiting features), you can specify the channel explicitly:
102
102
 
103
103
  ```typescript
104
104
  await queue
@@ -106,22 +106,21 @@ await queue
106
106
  .message
107
107
  .create({
108
108
  databaseClient,
109
- content: Buffer.from("Hello, world"),
110
- lockMs: 30000,
111
- delayMs: 5000
109
+ content: Buffer.from("Hello, world")
112
110
  })
113
111
  ```
114
112
 
115
- The `lockMs` parameter is required and specifies how long a message will remain exclusively locked after being dequeued. While locked, the message is **not available** for subsequent `dequeue()` calls, preventing duplicate processing. If your process crashes or takes longer than expected, the message will automatically become available for dequeue again after the lock expires.
116
-
117
113
  The `delayMs` parameter is optional and allows you to delay when the message becomes available for processing.
118
114
 
119
115
  ## Message Processing
120
116
 
121
- Messages can be fetched for processing by calling `dequeue` on the `Queue` - this locks the message. Once processing is complete, messages must be "finalized" via **deletion** or **deferral** (for further processing in the future).
117
+ Messages can be fetched for processing by calling `dequeue` on the `Queue` - this locks the message for a specified duration. Once processing is complete, messages must be "finalized" via **deletion** or **deferral** (for further processing in the future).
122
118
 
123
119
  ```typescript
124
- const dequeueResult = await queue.dequeue({ databaseClient })
120
+ const dequeueResult = await queue.dequeue({
121
+ databaseClient,
122
+ lockMs: 60_000
123
+ })
125
124
 
126
125
  if (dequeueResult.resultType === "MESSAGE_DEQUEUED") {
127
126
  const { message } = dequeueResult
@@ -157,15 +156,50 @@ if (dequeueResult.resultType === "MESSAGE_DEQUEUED") {
157
156
  }
158
157
  ```
159
158
 
159
+ The `lockMs` parameter on `dequeue()` specifies how long a message will remain exclusively locked after being dequeued. While locked, the message is **not available** for subsequent `dequeue()` calls, preventing duplicate processing. If your process crashes or takes longer than expected, the message will automatically become available for dequeue again after the lock expires.
160
+
160
161
  When deferring a message, you can optionally specify `delayMs` and `state` arguments. The `delayMs` parameter tells the queue how long to wait before making the message available for reprocessing, and `state` allows you to "save your work" and implement durable and/or repeating/scheduled tasks.
161
162
 
162
163
  **Note:** The above shows just one processing pattern (defer on failure with retry limits). You have complete flexibility in how you handle message processing - you might delete messages immediately, defer them unconditionally, implement different retry strategies based on error types, or use the message metadata (attempts, state, channel) to make sophisticated routing decisions.
163
164
 
164
- ### Graceful Shutdowns and Message Recovery
165
+ ### Extending Message Locks with Heartbeats
165
166
 
166
- If your program ends unexpectedly, messages that are currently being processed may become "orphaned" in a locked state - causing channel blockages and reducing throughput. To mitigate this problem, it's essential that you shut down gracefully by catching unhandled exceptions and signals (i.e., `SIGINT`/`SIGTERM`) and finalize all outstanding messages before exiting.
167
+ For messages that take a long time to process, setting a large initial lock is far from ideal. A crash shortly after message dequeue will result in channel throughput being degraded for a significant time (if the channel is concurrency-constrained). To mitigate this, you can set a short initial lock time that can be periodically renewed during message processing via a heartbeat:
168
+
169
+ ```typescript
170
+ const dequeueResult = await queue.dequeue({
171
+ databaseClient,
172
+ lockMs: 30_000
173
+ })
167
174
 
168
- That said, despite our best efforts, if we run out of memory, suffer a power loss, or receive a `SIGKILL`, we will be unable to finalize messages that are currently locked. To mitigate this, we set a `lockMs` during message creation (by default this is 1 hour) which limits the maximum amount of time a message can be locked before becoming available again for dequeue. This facility ensures that regardless of the nature of the shutdown, the queue will always recover automatically.
175
+ if (dequeueResult.resultType === "MESSAGE_DEQUEUED") {
176
+ const { message } = dequeueResult
177
+
178
+ // Start long-running process
179
+ const longTask = processLongRunningTask(message.content)
180
+
181
+ // Set up heartbeat to extend lock every 20 seconds
182
+ const heartbeatInterval = setInterval(async () => {
183
+ await message.heartbeat({
184
+ databaseClient,
185
+ lockMs: 30_000
186
+ })
187
+ }, 20_000)
188
+
189
+ try {
190
+ await longTask
191
+ await message.delete({ databaseClient })
192
+ } catch (error) {
193
+ await message.defer({ databaseClient, delayMs: 60_000 })
194
+ } finally {
195
+ clearInterval(heartbeatInterval)
196
+ }
197
+ }
198
+ ```
199
+
200
+ ### Graceful Shutdowns and Message Recovery
201
+
202
+ If your program ends unexpectedly, messages that are currently being processed may become "orphaned" in a locked state - causing channel blockages and reducing throughput until the lock expires. To mitigate this problem, it's essential that you shut down gracefully by catching unhandled exceptions and signals (i.e., `SIGINT`/`SIGTERM`) and finalize all outstanding messages before exiting.
169
203
 
170
204
  ## Events
171
205
 
@@ -185,21 +219,37 @@ const install = queue.install({ eventChannel: "EVENTS"})
185
219
 
186
220
  The simplest approach for processing messages is to call `dequeue` in a loop, backing off with a sleep when no messages are available. The downside of this approach is that we lose reactivity as we increase the polling timeout interval.
187
221
 
188
- To improve reactivity, you can use the `retryMs` returned when failing to dequeue a message. This will either be `null` or tell you how long until the next message becomes available for processing (a message might be deferred for a period of time). Thus, you can tune your sleep to use the minimum of your `retryMs` and a default poll timeout.
222
+ ```typescript
223
+ // Basic polling approach
224
+ while (true) {
225
+ const result = await queue.dequeue({ databaseClient, lockMs: 30_000 })
226
+
227
+ if (result.resultType === "MESSAGE_NOT_AVAILABLE") {
228
+ await sleep(5_000)
229
+ continue
230
+ }
231
+
232
+ // Process message...
233
+ await processMessage(result.message)
234
+ await result.message.delete({ databaseClient })
235
+ }
236
+ ```
189
237
 
190
- Unfortunately, this doesn't help in situations where a message is created or deferred while a worker is sleeping. However, by tracking the `delayMs` provided by the `MESSAGE_CREATED` and `MESSAGE_DEFERRED` events, we can determine the minimum amount of time to sleep until a message becomes available.
238
+ To improve reactivity, you can use the events system to track when new messages become available. By listening for `MESSAGE_CREATED` and `MESSAGE_DEFERRED` events and tracking their `delayMs`, you can determine the optimal time to retry dequeuing:
191
239
 
192
240
  ```typescript
193
241
  // LISTEN/NOTIFY only works with a single connection - not on a connection pool.
194
242
  const client = await databaseClient.connect()
195
243
  await client.query(`LISTEN "EVENTS"`)
244
+
245
+ let nextWakeTime = Date.now()
246
+
196
247
  client.on("notification", (msg) => {
197
- if (msg.channel === "EVENTS") {}
248
+ if (msg.channel === "EVENTS") {
198
249
  const event = queueEventDecode(msg.payload as string)
199
- if(event.eventType === "MESSAGE_CREATED") {
200
- console.log(`Should wake in ${event.delayMs} ms`)
201
- } else if(event.eventType === "MESSAGE_DEFERRED") {
202
- console.log(`Should wake in ${event.delayMs} ms`)
250
+ if (event.eventType === "MESSAGE_CREATED" || event.eventType === "MESSAGE_DEFERRED") {
251
+ const messageAvailableAt = Date.now() + event.delayMs
252
+ nextWakeTime = Math.min(nextWakeTime, messageAvailableAt)
203
253
  }
204
254
  }
205
255
  })
@@ -209,28 +259,6 @@ client.on("notification", (msg) => {
209
259
 
210
260
  The `MESSAGE_DELETED` event can be used to create coordination patterns where one part of your application waits for an unrelated job to complete. By listening for deletion events on specific message IDs, you can implement blocking operations that wait for background work to finish.
211
261
 
212
- ```typescript
213
- const client = await databaseClient.connect()
214
- await client.query(`LISTEN "EVENTS"`)
215
-
216
- const wait = (messageId: string) : Promise<void> => {
217
- return new Promise((resolve) => {
218
- const handler = (msg) => {
219
- if (msg.channel === "EVENTS") {
220
- const event = queueEventDecode(msg.payload as string)
221
- if (event.eventType === "MESSAGE_DELETED" && event.id === messageId) {
222
- client.off("notification", handler)
223
- resolve()
224
- }
225
- }
226
- }
227
- client.on("notification", handler)
228
- })
229
- }
230
-
231
- await wait(messageId)
232
- ```
233
-
234
262
  ## Deadlocks
235
263
 
236
264
  If all queue actions are isolated to their own transaction, there is zero risk of deadlocks occurring. That being said, it is *possible* to safely bulk-perform the following actions within a single transaction if we ensure they are performed in a consistent ordering with respect to the target channel name:
@@ -244,6 +272,7 @@ Beyond the actions specified above, it is manifestly **unsafe** to bulk-perform
244
272
  - Message dequeue
245
273
  - Message defer
246
274
  - Message delete
275
+ - Message heartbeat
247
276
 
248
277
  ## Database Clients
249
278
 
@@ -263,12 +292,10 @@ For database clients that don't match the expected interface exactly, LonnyMQ pr
263
292
 
264
293
  ```typescript
265
294
  import { Queue } from "lonnymq"
266
- import { Pool } from "pg"
267
295
 
268
- const customClient = new SomeOtherPostgresClient()
269
- const queue = new Queue({
296
+ const queue = new Queue<NonCompliantDatabaseClient>({
270
297
  schema: "lonny",
271
- adaptor: (client) => ({
298
+ adaptor: (client : NonCompliantDatabaseClient) => ({
272
299
  query: async (sql, params) => {
273
300
  // Adapt the client's interface to match DatabaseClient
274
301
  const result = await client.executeQuery(sql, params)
package/dist/index.cjs CHANGED
@@ -1,8 +1,8 @@
1
- var{defineProperty:h,getOwnPropertyNames:k,getOwnPropertyDescriptor:P}=Object,Q=Object.prototype.hasOwnProperty;var M=new WeakMap,V=(e)=>{var t=M.get(e),a;if(t)return t;if(t=h({},"__esModule",{value:!0}),e&&typeof e==="object"||typeof e==="function")k(e).map((r)=>!Q.call(t,r)&&h(t,r,{get:()=>e[r],enumerable:!(a=P(e,r))||a.enumerable}));return M.set(e,t),t};var W=(e,t)=>{for(var a in t)h(e,a,{get:t[a],enumerable:!0,configurable:!0,set:(r)=>t[a]=()=>r})};var le={};W(le,{queueEventDecode:()=>v,Queue:()=>y,MessageHeartbeatCommand:()=>d,MessageDequeueCommand:()=>m,MessageDeleteCommand:()=>i,MessageDeferCommand:()=>u,MessageCreateCommand:()=>l,ChannelPolicySetCommand:()=>o,ChannelPolicyClearCommand:()=>c});module.exports=V(le);var I=(e)=>{return e*1000};var T=I(0);var v=(e)=>{let t=JSON.parse(e);if(t.type===0)return{eventType:"MESSAGE_CREATED",id:t.id,delayMs:t.delay_ms};else if(t.type===1)return{eventType:"MESSAGE_DELETED",id:t.id};else if(t.type===2)return{eventType:"MESSAGE_DEFERRED",id:t.id,delayMs:t.delay_ms};else throw new Error("Unknown event type")};var E=(e)=>({nodeType:"VALUE",value:e}),s=(e)=>({nodeType:"REF",value:e}),Y=(e)=>({nodeType:"RAW",value:e}),z=(e)=>{return`'${e.replace(/'/g,"''")}'`},J=(e)=>{if(e===null)return"NULL";else if(typeof e==="string")return z(e);else if(typeof e==="number")return e.toString();else if(typeof e==="boolean")return e?"TRUE":"FALSE";else if(e instanceof Date)return`'${e.toISOString()}'`;else if(typeof e==="bigint")return e.toString();else throw new Error(`Unsupported value type: ${typeof e}`)},X=(e)=>{return`"${e.replace(/"/g,'""')}"`},j=(e)=>{if(e.nodeType==="VALUE")return J(e.value);else if(e.nodeType==="REF")return X(e.value);else if(e.nodeType==="RAW")return e.value;else throw new Error("Unsupported SQL node type")};var n=(e,...t)=>{let a=[];for(let r=0;r<e.length;r+=1)if(a.push(e[r]),r<t.length)a.push(j(t[r]));return Y(a.join(""))};class c{schema;channelName;createdAt;constructor(e){this.schema=e.schema,this.channelName=e.channelName,this.createdAt=new Date}async execute(e){await e.query(n`
1
+ var{defineProperty:h,getOwnPropertyNames:k,getOwnPropertyDescriptor:P}=Object,Q=Object.prototype.hasOwnProperty;var M=new WeakMap,V=(e)=>{var t=M.get(e),a;if(t)return t;if(t=h({},"__esModule",{value:!0}),e&&typeof e==="object"||typeof e==="function")k(e).map((r)=>!Q.call(t,r)&&h(t,r,{get:()=>e[r],enumerable:!(a=P(e,r))||a.enumerable}));return M.set(e,t),t};var W=(e,t)=>{for(var a in t)h(e,a,{get:t[a],enumerable:!0,configurable:!0,set:(r)=>t[a]=()=>r})};var le={};W(le,{queueEventDecode:()=>v,Queue:()=>y,MessageHeartbeatCommand:()=>d,MessageDequeueCommand:()=>m,MessageDeleteCommand:()=>u,MessageDeferCommand:()=>i,MessageCreateCommand:()=>l,ChannelPolicySetCommand:()=>c,ChannelPolicyClearCommand:()=>o});module.exports=V(le);var I=(e)=>{return e*1000};var T=I(0);var v=(e)=>{let t=JSON.parse(e);if(t.type===0)return{eventType:"MESSAGE_CREATED",id:t.id,delayMs:t.delay_ms};else if(t.type===1)return{eventType:"MESSAGE_DELETED",id:t.id};else if(t.type===2)return{eventType:"MESSAGE_DEFERRED",id:t.id,delayMs:t.delay_ms};else throw new Error("Unknown event type")};var E=(e)=>({nodeType:"VALUE",value:e}),s=(e)=>({nodeType:"REF",value:e}),Y=(e)=>({nodeType:"RAW",value:e}),z=(e)=>{return`'${e.replace(/'/g,"''")}'`},X=(e)=>{if(e===null)return"NULL";else if(typeof e==="string")return z(e);else if(typeof e==="number")return e.toString();else if(typeof e==="boolean")return e?"TRUE":"FALSE";else if(e instanceof Date)return`'${e.toISOString()}'`;else if(typeof e==="bigint")return e.toString();else throw new Error(`Unsupported value type: ${typeof e}`)},J=(e)=>{return`"${e.replace(/"/g,'""')}"`},j=(e)=>{if(e.nodeType==="VALUE")return X(e.value);else if(e.nodeType==="REF")return J(e.value);else if(e.nodeType==="RAW")return e.value;else throw new Error("Unsupported SQL node type")};var n=(e,...t)=>{let a=[];for(let r=0;r<e.length;r+=1)if(a.push(e[r]),r<t.length)a.push(j(t[r]));return Y(a.join(""))};class o{schema;channelName;createdAt;constructor(e){this.schema=e.schema,this.channelName=e.channelName,this.createdAt=new Date}async execute(e){await e.query(n`
2
2
  SELECT 1 FROM ${s(this.schema)}."channel_policy_clear"(
3
3
  $1
4
4
  )
5
- `.value,[this.channelName])}}class o{schema;channelName;maxConcurrency;releaseIntervalMs;createdAt;constructor(e){this.schema=e.schema,this.channelName=e.channelName;let t=e.maxConcurrency??null;this.maxConcurrency=t!==null?Math.max(0,t):null;let a=e.releaseIntervalMs??null;this.releaseIntervalMs=a!==null?Math.max(0,a):null,this.createdAt=new Date}async execute(e){await e.query(n`
5
+ `.value,[this.channelName])}}class c{schema;channelName;maxConcurrency;releaseIntervalMs;createdAt;constructor(e){this.schema=e.schema,this.channelName=e.channelName;let t=e.maxConcurrency??null;this.maxConcurrency=t!==null?Math.max(0,t):null;let a=e.releaseIntervalMs??null;this.releaseIntervalMs=a!==null?Math.max(0,a):null,this.createdAt=new Date}async execute(e){await e.query(n`
6
6
  SELECT 1 FROM ${s(this.schema)}."channel_policy_set"(
7
7
  $1,
8
8
  $2::INTEGER,
@@ -17,19 +17,19 @@ var{defineProperty:h,getOwnPropertyNames:k,getOwnPropertyDescriptor:P}=Object,Q=
17
17
  $2,
18
18
  $3::BIGINT
19
19
  )
20
- `.value,[this.channelName,this.content,this.delayMs]).then((a)=>a.rows[0]);if(t.result_code===0)return{resultType:"MESSAGE_CREATED",metadata:{id:t.metadata.id,channelSize:t.metadata.channel_size}};else throw new Error("Unexpected result code")}}class m{schema;lockMs;constructor(e){this.schema=e.schema,this.lockMs=e.lockMs}async execute(e){let t=await e.query(n`
20
+ `.value,[this.channelName,this.content,this.delayMs]).then((a)=>a.rows[0]);if(t.result_code===0)return{resultType:"MESSAGE_CREATED",id:t.metadata.id,channelSize:t.metadata.channel_size};else throw new Error("Unexpected result code")}}class m{schema;lockMs;constructor(e){this.schema=e.schema,this.lockMs=e.lockMs}async execute(e){let t=await e.query(n`
21
21
  SELECT
22
22
  result_code,
23
23
  metadata,
24
24
  content,
25
25
  state
26
26
  FROM ${s(this.schema)}."message_dequeue"($1::BIGINT)
27
- `.value,[this.lockMs]).then((a)=>a.rows[0]);if(t.result_code===0)return{resultType:"MESSAGE_NOT_AVAILABLE",retryMs:t.metadata.retry_ms};else if(t.result_code===1)return{resultType:"MESSAGE_DEQUEUED",message:{id:t.metadata.id,channelName:t.metadata.channel_name,isUnlocked:t.metadata.is_unlocked,content:t.content,state:t.state,numAttempts:t.metadata.num_attempts}};else throw new Error("Unexpected dequeue result")}}class i{schema;id;numAttempts;constructor(e){this.schema=e.schema,this.id=e.id,this.numAttempts=e.numAttempts}async execute(e){let t=await e.query(n`
27
+ `.value,[this.lockMs]).then((a)=>a.rows[0]);if(t.result_code===0)return{resultType:"MESSAGE_NOT_AVAILABLE"};else if(t.result_code===1)return{resultType:"MESSAGE_DEQUEUED",id:t.metadata.id,channelName:t.metadata.channel_name,isUnlocked:t.metadata.is_unlocked,content:t.content,state:t.state,numAttempts:t.metadata.num_attempts};else throw new Error("Unexpected dequeue result")}}class u{schema;id;numAttempts;constructor(e){this.schema=e.schema,this.id=e.id,this.numAttempts=e.numAttempts}async execute(e){let t=await e.query(n`
28
28
  SELECT * FROM ${s(this.schema)}."message_delete"(
29
29
  $1,
30
30
  $2::BIGINT
31
31
  )
32
- `.value,[this.id,this.numAttempts]).then((a)=>a.rows[0]);if(t.result_code===0)return{resultType:"MESSAGE_NOT_FOUND"};else if(t.result_code===1)return{resultType:"STATE_INVALID"};else if(t.result_code===2)return{resultType:"MESSAGE_DELETED"};else throw new Error("Unexpected result")}}class u{schema;id;numAttempts;delayMs;state;constructor(e){let t=e.delayMs===void 0?T:e.delayMs;this.schema=e.schema,this.numAttempts=e.numAttempts,this.id=e.id,this.delayMs=t,this.state=e.state??null}async execute(e){let t=await e.query(n`
32
+ `.value,[this.id,this.numAttempts]).then((a)=>a.rows[0]);if(t.result_code===0)return{resultType:"MESSAGE_NOT_FOUND"};else if(t.result_code===1)return{resultType:"STATE_INVALID"};else if(t.result_code===2)return{resultType:"MESSAGE_DELETED"};else throw new Error("Unexpected result")}}class i{schema;id;numAttempts;delayMs;state;constructor(e){let t=e.delayMs===void 0?T:e.delayMs;this.schema=e.schema,this.numAttempts=e.numAttempts,this.id=e.id,this.delayMs=t,this.state=e.state??null}async execute(e){let t=await e.query(n`
33
33
  SELECT * FROM ${s(this.schema)}."message_defer"(
34
34
  $1,
35
35
  $2::BIGINT,
@@ -67,8 +67,8 @@ var{defineProperty:h,getOwnPropertyNames:k,getOwnPropertyDescriptor:P}=Object,Q=
67
67
  "message_id" UUID,
68
68
  "message_seq_no" BIGINT,
69
69
  "message_dequeue_at" TIMESTAMP,
70
- "active_prev_at" TIMESTAMP NOT NULL,
71
- "active_next_at" TIMESTAMP NULL,
70
+ "dequeue_prev_at" TIMESTAMP NOT NULL,
71
+ "dequeue_next_at" TIMESTAMP NULL,
72
72
  "created_at" TIMESTAMP NOT NULL,
73
73
  PRIMARY KEY ("id")
74
74
  );
@@ -78,10 +78,10 @@ var{defineProperty:h,getOwnPropertyNames:k,getOwnPropertyDescriptor:P}=Object,Q=
78
78
  `,n`
79
79
  CREATE INDEX ${s(a)}
80
80
  ON ${s(e.schema)}."channel_state" (
81
- "active_next_at" ASC
81
+ "dequeue_next_at" ASC
82
82
  ) WHERE "message_id" IS NOT NULL
83
83
  AND ("max_concurrency" IS NULL OR "current_concurrency" < "max_concurrency");
84
- `]}};var __filename="/home/runner/work/lonnymq/lonnymq/src/install/02-table-message.ts",G={name:_(__filename),sql:(e)=>{return[n`
84
+ `]}};var __filename="/home/runner/work/lonnymq/lonnymq/src/install/02-table-message.ts",q={name:_(__filename),sql:(e)=>{return[n`
85
85
  CREATE TABLE ${s(e.schema)}."message" (
86
86
  "id" UUID NOT NULL,
87
87
  "channel_name" TEXT NOT NULL,
@@ -107,7 +107,7 @@ var{defineProperty:h,getOwnPropertyNames:k,getOwnPropertyDescriptor:P}=Object,Q=
107
107
  ON ${s(e.schema)}."message" (
108
108
  "unlock_at" ASC
109
109
  ) WHERE "is_locked";
110
- `]}};var __filename="/home/runner/work/lonnymq/lonnymq/src/install/03-function-message-create.ts",q={name:_(__filename),sql:(e)=>{return[n`
110
+ `]}};var __filename="/home/runner/work/lonnymq/lonnymq/src/install/03-function-message-create.ts",G={name:_(__filename),sql:(e)=>{return[n`
111
111
  CREATE FUNCTION ${s(e.schema)}."message_create" (
112
112
  p_channel_name TEXT,
113
113
  p_content BYTEA,
@@ -160,7 +160,7 @@ var{defineProperty:h,getOwnPropertyNames:k,getOwnPropertyDescriptor:P}=Object,Q=
160
160
  "current_size",
161
161
  "max_concurrency",
162
162
  "release_interval_ms",
163
- "active_prev_at",
163
+ "dequeue_prev_at",
164
164
  "created_at"
165
165
  ) VALUES (
166
166
  p_channel_name,
@@ -178,7 +178,7 @@ var{defineProperty:h,getOwnPropertyNames:k,getOwnPropertyDescriptor:P}=Object,Q=
178
178
  "current_size",
179
179
  "max_concurrency",
180
180
  "release_interval_ms",
181
- "active_prev_at",
181
+ "dequeue_prev_at",
182
182
  "message_id",
183
183
  "message_dequeue_at",
184
184
  "message_seq_no"
@@ -194,8 +194,8 @@ var{defineProperty:h,getOwnPropertyNames:k,getOwnPropertyDescriptor:P}=Object,Q=
194
194
  "message_id" = v_message."id",
195
195
  "message_dequeue_at" = v_message."dequeue_at",
196
196
  "message_seq_no" = v_message."seq_no",
197
- "active_next_at" = GREATEST(
198
- v_channel_state."active_prev_at" + INTERVAL '1 MILLISECOND' * COALESCE(v_channel_state."release_interval_ms", 0),
197
+ "dequeue_next_at" = GREATEST(
198
+ v_channel_state."dequeue_prev_at" + INTERVAL '1 MILLISECOND' * COALESCE(v_channel_state."release_interval_ms", 0),
199
199
  v_message."dequeue_at"
200
200
  )
201
201
  WHERE "id" = v_channel_state."id";
@@ -242,13 +242,13 @@ var{defineProperty:h,getOwnPropertyNames:k,getOwnPropertyDescriptor:P}=Object,Q=
242
242
  "channel_state"."name",
243
243
  "channel_state"."release_interval_ms",
244
244
  "channel_state"."message_id",
245
- "channel_state"."active_next_at",
246
- "channel_state"."active_prev_at",
245
+ "channel_state"."dequeue_next_at",
246
+ "channel_state"."dequeue_prev_at",
247
247
  "channel_state"."current_concurrency"
248
248
  FROM ${s(e.schema)}."channel_state"
249
249
  WHERE "message_id" IS NOT NULL
250
250
  AND ("max_concurrency" IS NULL OR "current_concurrency" < "max_concurrency")
251
- ORDER BY "active_next_at" ASC
251
+ ORDER BY "dequeue_next_at" ASC
252
252
  `,_e=(e)=>n`
253
253
  SELECT
254
254
  "message"."id",
@@ -308,23 +308,12 @@ var{defineProperty:h,getOwnPropertyNames:k,getOwnPropertyDescriptor:P}=Object,Q=
308
308
  LIMIT 1
309
309
  INTO v_channel_state;
310
310
 
311
- IF v_channel_state."id" IS NULL THEN
311
+ IF v_channel_state."id" IS NULL OR v_channel_state."dequeue_next_at" > v_now THEN
312
312
  RETURN QUERY SELECT
313
313
  ${E(0)},
314
314
  NULL::BYTEA,
315
315
  NULL::BYTEA,
316
- JSON_BUILD_OBJECT('retry_ms', NULL);
317
- RETURN;
318
- END IF;
319
-
320
- IF v_channel_state."active_next_at" > v_now THEN
321
- RETURN QUERY SELECT
322
- ${E(0)},
323
- NULL::BYTEA,
324
- NULL::BYTEA,
325
- JSON_BUILD_OBJECT(
326
- 'retry_ms', CEIL(1_000 * EXTRACT(EPOCH FROM v_channel_state."active_next_at" - v_now))
327
- );
316
+ NULL::JSON;
328
317
  RETURN;
329
318
  END IF;
330
319
 
@@ -351,7 +340,7 @@ var{defineProperty:h,getOwnPropertyNames:k,getOwnPropertyDescriptor:P}=Object,Q=
351
340
  IF v_message_next."id" IS NULL THEN
352
341
  UPDATE ${s(e.schema)}."channel_state" SET
353
342
  "current_concurrency" = v_channel_state."current_concurrency" + 1,
354
- "active_prev_at" = v_now,
343
+ "dequeue_prev_at" = v_now,
355
344
  "message_id" = NULL
356
345
  WHERE "id" = v_channel_state."id";
357
346
  ELSE
@@ -360,8 +349,8 @@ var{defineProperty:h,getOwnPropertyNames:k,getOwnPropertyDescriptor:P}=Object,Q=
360
349
  "message_id" = v_message_next."id",
361
350
  "message_dequeue_at" = v_message_next."dequeue_at",
362
351
  "message_seq_no" = v_message_next."seq_no",
363
- "active_prev_at" = v_now,
364
- "active_next_at" = GREATEST(
352
+ "dequeue_prev_at" = v_now,
353
+ "dequeue_next_at" = GREATEST(
365
354
  v_message_next."dequeue_at",
366
355
  v_now + (COALESCE(v_channel_state."release_interval_ms", 0) * INTERVAL '1 millisecond')
367
356
  )
@@ -370,13 +359,13 @@ var{defineProperty:h,getOwnPropertyNames:k,getOwnPropertyDescriptor:P}=Object,Q=
370
359
 
371
360
  RETURN QUERY SELECT
372
361
  ${E(1)},
373
- v_message_dequeue.content,
374
- v_message_dequeue.state,
362
+ v_message_dequeue."content",
363
+ v_message_dequeue."state",
375
364
  JSON_BUILD_OBJECT(
376
- 'id', v_message_dequeue.id,
365
+ 'id', v_message_dequeue."id",
377
366
  'is_unlocked', FALSE,
378
- 'channel_name', v_message_dequeue.channel_name,
379
- 'num_attempts', v_message_dequeue.num_attempts + 1
367
+ 'channel_name', v_message_dequeue."channel_name",
368
+ 'num_attempts', v_message_dequeue."num_attempts" + 1
380
369
  );
381
370
  RETURN;
382
371
  END;
@@ -502,7 +491,7 @@ var{defineProperty:h,getOwnPropertyNames:k,getOwnPropertyDescriptor:P}=Object,Q=
502
491
  "channel_state"."release_interval_ms",
503
492
  "channel_state"."message_id",
504
493
  "channel_state"."message_dequeue_at",
505
- "channel_state"."active_prev_at",
494
+ "channel_state"."dequeue_prev_at",
506
495
  "channel_state"."message_seq_no"
507
496
  FROM ${s(e.schema)}."channel_state"
508
497
  WHERE "name" = v_message."channel_name"
@@ -521,8 +510,8 @@ var{defineProperty:h,getOwnPropertyNames:k,getOwnPropertyDescriptor:P}=Object,Q=
521
510
  "message_id" = v_message."id",
522
511
  "message_dequeue_at" = v_dequeue_at,
523
512
  "message_seq_no" = v_message."seq_no",
524
- "active_next_at" = GREATEST(
525
- v_channel_state."active_prev_at" + INTERVAL '1 MILLISECOND' * COALESCE(v_channel_state."release_interval_ms", 0),
513
+ "dequeue_next_at" = GREATEST(
514
+ v_channel_state."dequeue_prev_at" + INTERVAL '1 MILLISECOND' * COALESCE(v_channel_state."release_interval_ms", 0),
526
515
  v_dequeue_at
527
516
  )
528
517
  WHERE "name" = v_message."channel_name";
@@ -614,7 +603,7 @@ var{defineProperty:h,getOwnPropertyNames:k,getOwnPropertyDescriptor:P}=Object,Q=
614
603
  WHERE "name" = p_name;
615
604
  END;
616
605
  $$ LANGUAGE plpgsql;
617
- `]}};class R{schema;channelName;adaptor;constructor(e){this.schema=e.schema,this.adaptor=e.adaptor,this.channelName=e.channelName}async create(e){let t=this.adaptor(e.databaseClient),a=new l({schema:this.schema,channelName:this.channelName,content:e.content,delayMs:e.delayMs}),r=await a.execute(t);return{messageId:r.metadata.id,channelName:a.channelName,channelSize:r.metadata.channelSize}}}class A{schema;adaptor;channelName;constructor(e){this.schema=e.schema,this.adaptor=e.adaptor,this.channelName=e.channelName}set(e){let t=this.adaptor(e.databaseClient);return new o({schema:this.schema,channelName:this.channelName,maxConcurrency:e.maxConcurrency,releaseIntervalMs:e.releaseIntervalMs}).execute(t)}clear(e){let t=this.adaptor(e.databaseClient);return new c({schema:this.schema,channelName:this.channelName}).execute(t)}}class g{policy;message;constructor(e){this.message=new R({schema:e.schema,adaptor:e.adaptor,channelName:e.channelName}),this.policy=new A({schema:e.schema,adaptor:e.adaptor,channelName:e.channelName})}}class D{schema;adaptor;id;isUnlocked;channelName;content;state;numAttempts;constructor(e){this.schema=e.schema,this.adaptor=e.adaptor,this.id=e.id,this.channelName=e.channelName,this.isUnlocked=e.isUnlocked,this.content=e.content,this.state=e.state,this.numAttempts=e.numAttempts}async defer(e){let t=this.adaptor(e.databaseClient);return new u({schema:this.schema,id:this.id,numAttempts:this.numAttempts,delayMs:e.delayMs,state:e.state}).execute(t)}async delete(e){let t=this.adaptor(e.databaseClient);return new i({schema:this.schema,numAttempts:this.numAttempts,id:this.id}).execute(t)}async heartbeat(e){let t=this.adaptor(e.databaseClient);return new d({schema:this.schema,id:this.id,numAttempts:this.numAttempts,lockMs:e.lockMs}).execute(t)}}class p{schema;adaptor;constructor(e){this.schema=e.schema,this.adaptor=e.adaptor}async create(e){let t=this.adaptor(e.databaseClient),a=new l({schema:this.schema,content:e.content,delayMs:e.delayMs}),r=await a.execute(t);return{messageId:r.metadata.id,channelName:a.channelName,channelSize:r.metadata.channelSize}}}var __filename="/home/runner/work/lonnymq/lonnymq/src/install/07-function-message-heartbeat.ts",w={name:_(__filename),sql:(e)=>{return[n`
606
+ `]}};class R{schema;channelName;adaptor;constructor(e){this.schema=e.schema,this.adaptor=e.adaptor,this.channelName=e.channelName}async create(e){let t=this.adaptor(e.databaseClient),a=new l({schema:this.schema,channelName:this.channelName,content:e.content,delayMs:e.delayMs}),r=await a.execute(t);return{messageId:r.id,channelName:a.channelName,channelSize:r.channelSize}}}class A{schema;adaptor;channelName;constructor(e){this.schema=e.schema,this.adaptor=e.adaptor,this.channelName=e.channelName}set(e){let t=this.adaptor(e.databaseClient);return new c({schema:this.schema,channelName:this.channelName,maxConcurrency:e.maxConcurrency,releaseIntervalMs:e.releaseIntervalMs}).execute(t)}clear(e){let t=this.adaptor(e.databaseClient);return new o({schema:this.schema,channelName:this.channelName}).execute(t)}}class g{policy;message;constructor(e){this.message=new R({schema:e.schema,adaptor:e.adaptor,channelName:e.channelName}),this.policy=new A({schema:e.schema,adaptor:e.adaptor,channelName:e.channelName})}}class p{schema;adaptor;id;isUnlocked;channelName;content;state;numAttempts;constructor(e){this.schema=e.schema,this.adaptor=e.adaptor,this.id=e.id,this.channelName=e.channelName,this.isUnlocked=e.isUnlocked,this.content=e.content,this.state=e.state,this.numAttempts=e.numAttempts}async defer(e){let t=this.adaptor(e.databaseClient);return new i({schema:this.schema,id:this.id,numAttempts:this.numAttempts,delayMs:e.delayMs,state:e.state}).execute(t)}async delete(e){let t=this.adaptor(e.databaseClient);return new u({schema:this.schema,numAttempts:this.numAttempts,id:this.id}).execute(t)}async heartbeat(e){let t=this.adaptor(e.databaseClient);return new d({schema:this.schema,id:this.id,numAttempts:this.numAttempts,lockMs:e.lockMs}).execute(t)}}class D{schema;adaptor;constructor(e){this.schema=e.schema,this.adaptor=e.adaptor}async create(e){let t=this.adaptor(e.databaseClient),a=new l({schema:this.schema,content:e.content,delayMs:e.delayMs}),r=await a.execute(t);return{messageId:r.id,channelName:a.channelName,channelSize:r.channelSize}}}var __filename="/home/runner/work/lonnymq/lonnymq/src/install/07-function-message-heartbeat.ts",w={name:_(__filename),sql:(e)=>{return[n`
618
607
  CREATE FUNCTION ${s(e.schema)}."message_heartbeat" (
619
608
  p_id UUID,
620
609
  p_num_attempts BIGINT,
@@ -664,4 +653,4 @@ var{defineProperty:h,getOwnPropertyNames:k,getOwnPropertyDescriptor:P}=Object,Q=
664
653
  RETURN;
665
654
  END;
666
655
  $$ LANGUAGE plpgsql;
667
- `]}};class y{schema;message;adaptor;constructor(e){this.schema=e.schema,this.adaptor=e.adaptor?e.adaptor:(t)=>t,this.message=new p({schema:this.schema,adaptor:this.adaptor})}async dequeue(e){let t=new m({schema:this.schema,lockMs:e.lockMs}),a=this.adaptor(e.databaseClient),r=await t.execute(a);if(r.resultType==="MESSAGE_DEQUEUED")return{resultType:"MESSAGE_DEQUEUED",message:new D({schema:this.schema,adaptor:this.adaptor,id:r.message.id,channelName:r.message.channelName,isUnlocked:r.message.isUnlocked,content:r.message.content,state:r.message.state,numAttempts:r.message.numAttempts})};else return r}channel(e){return new g({adaptor:this.adaptor,schema:this.schema,channelName:e})}install(e={}){return[O,f,G,q,x,$,F,w,B,b].sort((t,a)=>t.name.localeCompare(a.name)).flatMap((t)=>t.sql({schema:this.schema,eventChannel:e.eventChannel??null})).map((t)=>U(t.value))}}
656
+ `]}};class y{schema;message;adaptor;constructor(e){this.schema=e.schema,this.adaptor=e.adaptor?e.adaptor:(t)=>t,this.message=new D({schema:this.schema,adaptor:this.adaptor})}async dequeue(e){let t=new m({schema:this.schema,lockMs:e.lockMs}),a=this.adaptor(e.databaseClient),r=await t.execute(a);if(r.resultType==="MESSAGE_DEQUEUED")return{resultType:"MESSAGE_DEQUEUED",message:new p({schema:this.schema,adaptor:this.adaptor,id:r.id,channelName:r.channelName,isUnlocked:r.isUnlocked,content:r.content,state:r.state,numAttempts:r.numAttempts})};else return r}channel(e){return new g({adaptor:this.adaptor,schema:this.schema,channelName:e})}install(e={}){return[O,f,q,G,x,$,F,w,B,b].sort((t,a)=>t.name.localeCompare(a.name)).flatMap((t)=>t.sql({schema:this.schema,eventChannel:e.eventChannel??null})).map((t)=>U(t.value))}}
package/dist/index.d.ts CHANGED
@@ -54,10 +54,8 @@ export declare class ChannelPolicySetCommand {
54
54
  }
55
55
  export type MessageCreateCommandResult = {
56
56
  resultType: "MESSAGE_CREATED";
57
- metadata: {
58
- id: string;
59
- channelSize: number;
60
- };
57
+ id: string;
58
+ channelSize: number;
61
59
  };
62
60
  export declare class MessageCreateCommand {
63
61
  readonly schema: string;
@@ -75,18 +73,15 @@ export declare class MessageCreateCommand {
75
73
  }
76
74
  export type MessageDequeueCommandResultMessageDequeued = {
77
75
  resultType: "MESSAGE_DEQUEUED";
78
- message: {
79
- id: string;
80
- channelName: string;
81
- isUnlocked: boolean;
82
- content: Buffer;
83
- state: Buffer | null;
84
- numAttempts: number;
85
- };
76
+ id: string;
77
+ channelName: string;
78
+ isUnlocked: boolean;
79
+ content: Buffer;
80
+ state: Buffer | null;
81
+ numAttempts: number;
86
82
  };
87
83
  export type MessageDequeueCommandResultMessageNotAvailable = {
88
84
  resultType: "MESSAGE_NOT_AVAILABLE";
89
- retryMs: number | null;
90
85
  };
91
86
  export type MessageDequeueCommandResult = MessageDequeueCommandResultMessageDequeued | MessageDequeueCommandResultMessageNotAvailable;
92
87
  export declare class MessageDequeueCommand {
@@ -264,7 +259,6 @@ export declare class QueueMessageModule<T> {
264
259
  }
265
260
  export type MessageDequeueResult<T> = {
266
261
  resultType: "MESSAGE_NOT_AVAILABLE";
267
- retryMs: number | null;
268
262
  } | {
269
263
  resultType: "MESSAGE_DEQUEUED";
270
264
  message: QueueMessage<T>;
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- var D=(e)=>{return e*1000};var c=D(0);var b=(e)=>{let t=JSON.parse(e);if(t.type===0)return{eventType:"MESSAGE_CREATED",id:t.id,delayMs:t.delay_ms};else if(t.type===1)return{eventType:"MESSAGE_DELETED",id:t.id};else if(t.type===2)return{eventType:"MESSAGE_DEFERRED",id:t.id,delayMs:t.delay_ms};else throw new Error("Unknown event type")};var r=(e)=>({nodeType:"VALUE",value:e}),s=(e)=>({nodeType:"REF",value:e}),B=(e)=>({nodeType:"RAW",value:e}),w=(e)=>{return`'${e.replace(/'/g,"''")}'`},H=(e)=>{if(e===null)return"NULL";else if(typeof e==="string")return w(e);else if(typeof e==="number")return e.toString();else if(typeof e==="boolean")return e?"TRUE":"FALSE";else if(e instanceof Date)return`'${e.toISOString()}'`;else if(typeof e==="bigint")return e.toString();else throw new Error(`Unsupported value type: ${typeof e}`)},k=(e)=>{return`"${e.replace(/"/g,'""')}"`},P=(e)=>{if(e.nodeType==="VALUE")return H(e.value);else if(e.nodeType==="REF")return k(e.value);else if(e.nodeType==="RAW")return e.value;else throw new Error("Unsupported SQL node type")};var n=(e,...t)=>{let a=[];for(let E=0;E<e.length;E+=1)if(a.push(e[E]),E<t.length)a.push(P(t[E]));return B(a.join(""))};class o{schema;channelName;createdAt;constructor(e){this.schema=e.schema,this.channelName=e.channelName,this.createdAt=new Date}async execute(e){await e.query(n`
1
+ var p=(e)=>{return e*1000};var o=p(0);var b=(e)=>{let t=JSON.parse(e);if(t.type===0)return{eventType:"MESSAGE_CREATED",id:t.id,delayMs:t.delay_ms};else if(t.type===1)return{eventType:"MESSAGE_DELETED",id:t.id};else if(t.type===2)return{eventType:"MESSAGE_DEFERRED",id:t.id,delayMs:t.delay_ms};else throw new Error("Unknown event type")};var E=(e)=>({nodeType:"VALUE",value:e}),s=(e)=>({nodeType:"REF",value:e}),B=(e)=>({nodeType:"RAW",value:e}),w=(e)=>{return`'${e.replace(/'/g,"''")}'`},H=(e)=>{if(e===null)return"NULL";else if(typeof e==="string")return w(e);else if(typeof e==="number")return e.toString();else if(typeof e==="boolean")return e?"TRUE":"FALSE";else if(e instanceof Date)return`'${e.toISOString()}'`;else if(typeof e==="bigint")return e.toString();else throw new Error(`Unsupported value type: ${typeof e}`)},k=(e)=>{return`"${e.replace(/"/g,'""')}"`},P=(e)=>{if(e.nodeType==="VALUE")return H(e.value);else if(e.nodeType==="REF")return k(e.value);else if(e.nodeType==="RAW")return e.value;else throw new Error("Unsupported SQL node type")};var n=(e,...t)=>{let a=[];for(let r=0;r<e.length;r+=1)if(a.push(e[r]),r<t.length)a.push(P(t[r]));return B(a.join(""))};class c{schema;channelName;createdAt;constructor(e){this.schema=e.schema,this.channelName=e.channelName,this.createdAt=new Date}async execute(e){await e.query(n`
2
2
  SELECT 1 FROM ${s(this.schema)}."channel_policy_clear"(
3
3
  $1
4
4
  )
@@ -8,7 +8,7 @@ var D=(e)=>{return e*1000};var c=D(0);var b=(e)=>{let t=JSON.parse(e);if(t.type=
8
8
  $2::INTEGER,
9
9
  $3::INTEGER
10
10
  )
11
- `.value,[this.channelName,this.maxConcurrency,this.releaseIntervalMs])}}import{randomBytes as Q}from"crypto";var p=()=>{return Q(16).toString("base64url")};class l{schema;channelName;content;delayMs;createdAt;constructor(e){let t=e.delayMs===void 0?c:e.delayMs;this.schema=e.schema,this.channelName=e.channelName??p(),this.content=e.content,this.delayMs=t,this.createdAt=new Date}async execute(e){let t=await e.query(n`
11
+ `.value,[this.channelName,this.maxConcurrency,this.releaseIntervalMs])}}import{randomBytes as Q}from"crypto";var D=()=>{return Q(16).toString("base64url")};class l{schema;channelName;content;delayMs;createdAt;constructor(e){let t=e.delayMs===void 0?o:e.delayMs;this.schema=e.schema,this.channelName=e.channelName??D(),this.content=e.content,this.delayMs=t,this.createdAt=new Date}async execute(e){let t=await e.query(n`
12
12
  SELECT
13
13
  result_code,
14
14
  metadata
@@ -17,19 +17,19 @@ var D=(e)=>{return e*1000};var c=D(0);var b=(e)=>{let t=JSON.parse(e);if(t.type=
17
17
  $2,
18
18
  $3::BIGINT
19
19
  )
20
- `.value,[this.channelName,this.content,this.delayMs]).then((a)=>a.rows[0]);if(t.result_code===0)return{resultType:"MESSAGE_CREATED",metadata:{id:t.metadata.id,channelSize:t.metadata.channel_size}};else throw new Error("Unexpected result code")}}class i{schema;lockMs;constructor(e){this.schema=e.schema,this.lockMs=e.lockMs}async execute(e){let t=await e.query(n`
20
+ `.value,[this.channelName,this.content,this.delayMs]).then((a)=>a.rows[0]);if(t.result_code===0)return{resultType:"MESSAGE_CREATED",id:t.metadata.id,channelSize:t.metadata.channel_size};else throw new Error("Unexpected result code")}}class u{schema;lockMs;constructor(e){this.schema=e.schema,this.lockMs=e.lockMs}async execute(e){let t=await e.query(n`
21
21
  SELECT
22
22
  result_code,
23
23
  metadata,
24
24
  content,
25
25
  state
26
26
  FROM ${s(this.schema)}."message_dequeue"($1::BIGINT)
27
- `.value,[this.lockMs]).then((a)=>a.rows[0]);if(t.result_code===0)return{resultType:"MESSAGE_NOT_AVAILABLE",retryMs:t.metadata.retry_ms};else if(t.result_code===1)return{resultType:"MESSAGE_DEQUEUED",message:{id:t.metadata.id,channelName:t.metadata.channel_name,isUnlocked:t.metadata.is_unlocked,content:t.content,state:t.state,numAttempts:t.metadata.num_attempts}};else throw new Error("Unexpected dequeue result")}}class u{schema;id;numAttempts;constructor(e){this.schema=e.schema,this.id=e.id,this.numAttempts=e.numAttempts}async execute(e){let t=await e.query(n`
27
+ `.value,[this.lockMs]).then((a)=>a.rows[0]);if(t.result_code===0)return{resultType:"MESSAGE_NOT_AVAILABLE"};else if(t.result_code===1)return{resultType:"MESSAGE_DEQUEUED",id:t.metadata.id,channelName:t.metadata.channel_name,isUnlocked:t.metadata.is_unlocked,content:t.content,state:t.state,numAttempts:t.metadata.num_attempts};else throw new Error("Unexpected dequeue result")}}class i{schema;id;numAttempts;constructor(e){this.schema=e.schema,this.id=e.id,this.numAttempts=e.numAttempts}async execute(e){let t=await e.query(n`
28
28
  SELECT * FROM ${s(this.schema)}."message_delete"(
29
29
  $1,
30
30
  $2::BIGINT
31
31
  )
32
- `.value,[this.id,this.numAttempts]).then((a)=>a.rows[0]);if(t.result_code===0)return{resultType:"MESSAGE_NOT_FOUND"};else if(t.result_code===1)return{resultType:"STATE_INVALID"};else if(t.result_code===2)return{resultType:"MESSAGE_DELETED"};else throw new Error("Unexpected result")}}class d{schema;id;numAttempts;delayMs;state;constructor(e){let t=e.delayMs===void 0?c:e.delayMs;this.schema=e.schema,this.numAttempts=e.numAttempts,this.id=e.id,this.delayMs=t,this.state=e.state??null}async execute(e){let t=await e.query(n`
32
+ `.value,[this.id,this.numAttempts]).then((a)=>a.rows[0]);if(t.result_code===0)return{resultType:"MESSAGE_NOT_FOUND"};else if(t.result_code===1)return{resultType:"STATE_INVALID"};else if(t.result_code===2)return{resultType:"MESSAGE_DELETED"};else throw new Error("Unexpected result")}}class d{schema;id;numAttempts;delayMs;state;constructor(e){let t=e.delayMs===void 0?o:e.delayMs;this.schema=e.schema,this.numAttempts=e.numAttempts,this.id=e.id,this.delayMs=t,this.state=e.state??null}async execute(e){let t=await e.query(n`
33
33
  SELECT * FROM ${s(this.schema)}."message_defer"(
34
34
  $1,
35
35
  $2::BIGINT,
@@ -43,8 +43,8 @@ var D=(e)=>{return e*1000};var c=D(0);var b=(e)=>{let t=JSON.parse(e);if(t.type=
43
43
  $3::BIGINT
44
44
  )
45
45
  `.value,[this.id,this.numAttempts,this.lockMs]).then((a)=>a.rows[0]);if(t.result_code===0)return{resultType:"MESSAGE_NOT_FOUND"};else if(t.result_code===1)return{resultType:"MESSAGE_STATE_INVALID"};else if(t.result_code===2)return{resultType:"MESSAGE_HEARTBEATED"};else throw new Error("Unexpected result")}}var y=(e)=>{let t=e.split(`
46
- `),a=Number.MAX_SAFE_INTEGER;for(let E of t){if(E.trim().length===0)continue;let F=E.search(/\S/);a=Math.min(a,F)}return t.map((E)=>E.slice(a)).join(`
47
- `).trim()};import{dirname as M}from"path";var __filename="/home/runner/work/lonnymq/lonnymq/src/core/path.ts",X=M(M(__filename)),j=new RegExp(`^${X}/`),_=(e)=>{return e.replace(j,"")};var __filename="/home/runner/work/lonnymq/lonnymq/src/install/00-table-channel-policy.ts",I={name:_(__filename),sql:(e)=>{let t=[e.schema,"channel_policy_name_ux"].join("_");return[n`
46
+ `),a=Number.MAX_SAFE_INTEGER;for(let r of t){if(r.trim().length===0)continue;let F=r.search(/\S/);a=Math.min(a,F)}return t.map((r)=>r.slice(a)).join(`
47
+ `).trim()};import{dirname as M}from"path";var __filename="/home/runner/work/lonnymq/lonnymq/src/core/path.ts",J=M(M(__filename)),j=new RegExp(`^${J}/`),_=(e)=>{return e.replace(j,"")};var __filename="/home/runner/work/lonnymq/lonnymq/src/install/00-table-channel-policy.ts",I={name:_(__filename),sql:(e)=>{let t=[e.schema,"channel_policy_name_ux"].join("_");return[n`
48
48
  CREATE TABLE ${s(e.schema)}."channel_policy" (
49
49
  "id" UUID NOT NULL DEFAULT GEN_RANDOM_UUID(),
50
50
  "name" TEXT NOT NULL,
@@ -67,8 +67,8 @@ var D=(e)=>{return e*1000};var c=D(0);var b=(e)=>{let t=JSON.parse(e);if(t.type=
67
67
  "message_id" UUID,
68
68
  "message_seq_no" BIGINT,
69
69
  "message_dequeue_at" TIMESTAMP,
70
- "active_prev_at" TIMESTAMP NOT NULL,
71
- "active_next_at" TIMESTAMP NULL,
70
+ "dequeue_prev_at" TIMESTAMP NOT NULL,
71
+ "dequeue_next_at" TIMESTAMP NULL,
72
72
  "created_at" TIMESTAMP NOT NULL,
73
73
  PRIMARY KEY ("id")
74
74
  );
@@ -78,7 +78,7 @@ var D=(e)=>{return e*1000};var c=D(0);var b=(e)=>{let t=JSON.parse(e);if(t.type=
78
78
  `,n`
79
79
  CREATE INDEX ${s(a)}
80
80
  ON ${s(e.schema)}."channel_state" (
81
- "active_next_at" ASC
81
+ "dequeue_next_at" ASC
82
82
  ) WHERE "message_id" IS NOT NULL
83
83
  AND ("max_concurrency" IS NULL OR "current_concurrency" < "max_concurrency");
84
84
  `]}};var __filename="/home/runner/work/lonnymq/lonnymq/src/install/02-table-message.ts",L={name:_(__filename),sql:(e)=>{return[n`
@@ -160,7 +160,7 @@ var D=(e)=>{return e*1000};var c=D(0);var b=(e)=>{let t=JSON.parse(e);if(t.type=
160
160
  "current_size",
161
161
  "max_concurrency",
162
162
  "release_interval_ms",
163
- "active_prev_at",
163
+ "dequeue_prev_at",
164
164
  "created_at"
165
165
  ) VALUES (
166
166
  p_channel_name,
@@ -178,7 +178,7 @@ var D=(e)=>{return e*1000};var c=D(0);var b=(e)=>{let t=JSON.parse(e);if(t.type=
178
178
  "current_size",
179
179
  "max_concurrency",
180
180
  "release_interval_ms",
181
- "active_prev_at",
181
+ "dequeue_prev_at",
182
182
  "message_id",
183
183
  "message_dequeue_at",
184
184
  "message_seq_no"
@@ -194,8 +194,8 @@ var D=(e)=>{return e*1000};var c=D(0);var b=(e)=>{let t=JSON.parse(e);if(t.type=
194
194
  "message_id" = v_message."id",
195
195
  "message_dequeue_at" = v_message."dequeue_at",
196
196
  "message_seq_no" = v_message."seq_no",
197
- "active_next_at" = GREATEST(
198
- v_channel_state."active_prev_at" + INTERVAL '1 MILLISECOND' * COALESCE(v_channel_state."release_interval_ms", 0),
197
+ "dequeue_next_at" = GREATEST(
198
+ v_channel_state."dequeue_prev_at" + INTERVAL '1 MILLISECOND' * COALESCE(v_channel_state."release_interval_ms", 0),
199
199
  v_message."dequeue_at"
200
200
  )
201
201
  WHERE "id" = v_channel_state."id";
@@ -205,11 +205,11 @@ var D=(e)=>{return e*1000};var c=D(0);var b=(e)=>{let t=JSON.parse(e);if(t.type=
205
205
  WHERE "id" = v_channel_state."id";
206
206
  END IF;
207
207
 
208
- IF ${r(e.eventChannel!==null)} THEN
208
+ IF ${E(e.eventChannel!==null)} THEN
209
209
  PERFORM PG_NOTIFY(
210
- ${r(e.eventChannel)},
210
+ ${E(e.eventChannel)},
211
211
  JSON_BUILD_OBJECT(
212
- 'type', ${r(0)},
212
+ 'type', ${E(0)},
213
213
  'id', v_message."id",
214
214
  'delay_ms', p_delay_ms
215
215
  )::TEXT
@@ -217,7 +217,7 @@ var D=(e)=>{return e*1000};var c=D(0);var b=(e)=>{let t=JSON.parse(e);if(t.type=
217
217
  END IF;
218
218
 
219
219
  RETURN QUERY SELECT
220
- ${r(0)},
220
+ ${E(0)},
221
221
  JSON_BUILD_OBJECT(
222
222
  'id', v_message."id",
223
223
  'channel_size', v_channel_state."current_size" + 1
@@ -242,13 +242,13 @@ var D=(e)=>{return e*1000};var c=D(0);var b=(e)=>{let t=JSON.parse(e);if(t.type=
242
242
  "channel_state"."name",
243
243
  "channel_state"."release_interval_ms",
244
244
  "channel_state"."message_id",
245
- "channel_state"."active_next_at",
246
- "channel_state"."active_prev_at",
245
+ "channel_state"."dequeue_next_at",
246
+ "channel_state"."dequeue_prev_at",
247
247
  "channel_state"."current_concurrency"
248
248
  FROM ${s(e.schema)}."channel_state"
249
249
  WHERE "message_id" IS NOT NULL
250
250
  AND ("max_concurrency" IS NULL OR "current_concurrency" < "max_concurrency")
251
- ORDER BY "active_next_at" ASC
251
+ ORDER BY "dequeue_next_at" ASC
252
252
  `,ee=(e)=>n`
253
253
  SELECT
254
254
  "message"."id",
@@ -258,7 +258,7 @@ var D=(e)=>{return e*1000};var c=D(0);var b=(e)=>{let t=JSON.parse(e);if(t.type=
258
258
  WHERE NOT "is_locked"
259
259
  AND "channel_name" = ${e.channelName}
260
260
  ORDER BY "dequeue_at" ASC, "seq_no" ASC
261
- `,U={name:_(__filename),sql:(e)=>{let t=K({now:n`v_now`,schema:e.schema}),a=ee({channelName:n`v_channel_state."name"`,schema:e.schema}),E=Z({schema:e.schema});return[n`
261
+ `,U={name:_(__filename),sql:(e)=>{let t=K({now:n`v_now`,schema:e.schema}),a=ee({channelName:n`v_channel_state."name"`,schema:e.schema}),r=Z({schema:e.schema});return[n`
262
262
  CREATE FUNCTION ${s(e.schema)}."message_dequeue" (
263
263
  p_lock_ms BIGINT
264
264
  )
@@ -290,7 +290,7 @@ var D=(e)=>{return e*1000};var c=D(0);var b=(e)=>{let t=JSON.parse(e);if(t.type=
290
290
  WHERE "id" = v_message_locked."id";
291
291
 
292
292
  RETURN QUERY SELECT
293
- ${r(1)},
293
+ ${E(1)},
294
294
  v_message_locked.content,
295
295
  v_message_locked.state,
296
296
  JSON_BUILD_OBJECT(
@@ -302,29 +302,18 @@ var D=(e)=>{return e*1000};var c=D(0);var b=(e)=>{let t=JSON.parse(e);if(t.type=
302
302
  RETURN;
303
303
  END IF;
304
304
 
305
- ${E}
305
+ ${r}
306
306
  FOR UPDATE
307
307
  SKIP LOCKED
308
308
  LIMIT 1
309
309
  INTO v_channel_state;
310
310
 
311
- IF v_channel_state."id" IS NULL THEN
311
+ IF v_channel_state."id" IS NULL OR v_channel_state."dequeue_next_at" > v_now THEN
312
312
  RETURN QUERY SELECT
313
- ${r(0)},
313
+ ${E(0)},
314
314
  NULL::BYTEA,
315
315
  NULL::BYTEA,
316
- JSON_BUILD_OBJECT('retry_ms', NULL);
317
- RETURN;
318
- END IF;
319
-
320
- IF v_channel_state."active_next_at" > v_now THEN
321
- RETURN QUERY SELECT
322
- ${r(0)},
323
- NULL::BYTEA,
324
- NULL::BYTEA,
325
- JSON_BUILD_OBJECT(
326
- 'retry_ms', CEIL(1_000 * EXTRACT(EPOCH FROM v_channel_state."active_next_at" - v_now))
327
- );
316
+ NULL::JSON;
328
317
  RETURN;
329
318
  END IF;
330
319
 
@@ -351,7 +340,7 @@ var D=(e)=>{return e*1000};var c=D(0);var b=(e)=>{let t=JSON.parse(e);if(t.type=
351
340
  IF v_message_next."id" IS NULL THEN
352
341
  UPDATE ${s(e.schema)}."channel_state" SET
353
342
  "current_concurrency" = v_channel_state."current_concurrency" + 1,
354
- "active_prev_at" = v_now,
343
+ "dequeue_prev_at" = v_now,
355
344
  "message_id" = NULL
356
345
  WHERE "id" = v_channel_state."id";
357
346
  ELSE
@@ -360,8 +349,8 @@ var D=(e)=>{return e*1000};var c=D(0);var b=(e)=>{let t=JSON.parse(e);if(t.type=
360
349
  "message_id" = v_message_next."id",
361
350
  "message_dequeue_at" = v_message_next."dequeue_at",
362
351
  "message_seq_no" = v_message_next."seq_no",
363
- "active_prev_at" = v_now,
364
- "active_next_at" = GREATEST(
352
+ "dequeue_prev_at" = v_now,
353
+ "dequeue_next_at" = GREATEST(
365
354
  v_message_next."dequeue_at",
366
355
  v_now + (COALESCE(v_channel_state."release_interval_ms", 0) * INTERVAL '1 millisecond')
367
356
  )
@@ -369,14 +358,14 @@ var D=(e)=>{return e*1000};var c=D(0);var b=(e)=>{let t=JSON.parse(e);if(t.type=
369
358
  END IF;
370
359
 
371
360
  RETURN QUERY SELECT
372
- ${r(1)},
373
- v_message_dequeue.content,
374
- v_message_dequeue.state,
361
+ ${E(1)},
362
+ v_message_dequeue."content",
363
+ v_message_dequeue."state",
375
364
  JSON_BUILD_OBJECT(
376
- 'id', v_message_dequeue.id,
365
+ 'id', v_message_dequeue."id",
377
366
  'is_unlocked', FALSE,
378
- 'channel_name', v_message_dequeue.channel_name,
379
- 'num_attempts', v_message_dequeue.num_attempts + 1
367
+ 'channel_name', v_message_dequeue."channel_name",
368
+ 'num_attempts', v_message_dequeue."num_attempts" + 1
380
369
  );
381
370
  RETURN;
382
371
  END;
@@ -406,11 +395,11 @@ var D=(e)=>{return e*1000};var c=D(0);var b=(e)=>{let t=JSON.parse(e);if(t.type=
406
395
 
407
396
  IF v_message."id" IS NULL THEN
408
397
  RETURN QUERY SELECT
409
- ${r(0)};
398
+ ${E(0)};
410
399
  RETURN;
411
400
  ELSIF NOT v_message."is_locked" OR v_message."num_attempts" <> p_num_attempts THEN
412
401
  RETURN QUERY SELECT
413
- ${r(1)};
402
+ ${E(1)};
414
403
  RETURN;
415
404
  END IF;
416
405
 
@@ -440,11 +429,11 @@ var D=(e)=>{return e*1000};var c=D(0);var b=(e)=>{let t=JSON.parse(e);if(t.type=
440
429
  WHERE "id" = v_channel_state."id";
441
430
  END IF;
442
431
 
443
- IF ${r(e.eventChannel!==null)} THEN
432
+ IF ${E(e.eventChannel!==null)} THEN
444
433
  PERFORM PG_NOTIFY(
445
- ${r(e.eventChannel)},
434
+ ${E(e.eventChannel)},
446
435
  JSON_BUILD_OBJECT(
447
- 'type', ${r(1)},
436
+ 'type', ${E(1)},
448
437
  'id', p_id
449
438
  )::TEXT
450
439
  );
@@ -454,7 +443,7 @@ var D=(e)=>{return e*1000};var c=D(0);var b=(e)=>{let t=JSON.parse(e);if(t.type=
454
443
  WHERE "id" = p_id;
455
444
 
456
445
  RETURN QUERY SELECT
457
- ${r(2)};
446
+ ${E(2)};
458
447
  RETURN;
459
448
  END;
460
449
  $$ LANGUAGE plpgsql;
@@ -489,11 +478,11 @@ var D=(e)=>{return e*1000};var c=D(0);var b=(e)=>{let t=JSON.parse(e);if(t.type=
489
478
 
490
479
  IF v_message."id" IS NULL THEN
491
480
  RETURN QUERY SELECT
492
- ${r(0)};
481
+ ${E(0)};
493
482
  RETURN;
494
483
  ELSIF NOT v_message."is_locked" OR v_message."num_attempts" <> p_num_attempts THEN
495
484
  RETURN QUERY SELECT
496
- ${r(1)};
485
+ ${E(1)};
497
486
  RETURN;
498
487
  END IF;
499
488
 
@@ -502,7 +491,7 @@ var D=(e)=>{return e*1000};var c=D(0);var b=(e)=>{let t=JSON.parse(e);if(t.type=
502
491
  "channel_state"."release_interval_ms",
503
492
  "channel_state"."message_id",
504
493
  "channel_state"."message_dequeue_at",
505
- "channel_state"."active_prev_at",
494
+ "channel_state"."dequeue_prev_at",
506
495
  "channel_state"."message_seq_no"
507
496
  FROM ${s(e.schema)}."channel_state"
508
497
  WHERE "name" = v_message."channel_name"
@@ -521,8 +510,8 @@ var D=(e)=>{return e*1000};var c=D(0);var b=(e)=>{let t=JSON.parse(e);if(t.type=
521
510
  "message_id" = v_message."id",
522
511
  "message_dequeue_at" = v_dequeue_at,
523
512
  "message_seq_no" = v_message."seq_no",
524
- "active_next_at" = GREATEST(
525
- v_channel_state."active_prev_at" + INTERVAL '1 MILLISECOND' * COALESCE(v_channel_state."release_interval_ms", 0),
513
+ "dequeue_next_at" = GREATEST(
514
+ v_channel_state."dequeue_prev_at" + INTERVAL '1 MILLISECOND' * COALESCE(v_channel_state."release_interval_ms", 0),
526
515
  v_dequeue_at
527
516
  )
528
517
  WHERE "name" = v_message."channel_name";
@@ -538,11 +527,11 @@ var D=(e)=>{return e*1000};var c=D(0);var b=(e)=>{let t=JSON.parse(e);if(t.type=
538
527
  "dequeue_at" = v_dequeue_at
539
528
  WHERE "id" = p_id;
540
529
 
541
- IF ${r(e.eventChannel!==null)} THEN
530
+ IF ${E(e.eventChannel!==null)} THEN
542
531
  PERFORM PG_NOTIFY(
543
- ${r(e.eventChannel)},
532
+ ${E(e.eventChannel)},
544
533
  JSON_BUILD_OBJECT(
545
- 'type', ${r(2)},
534
+ 'type', ${E(2)},
546
535
  'delay_ms', p_delay_ms,
547
536
  'id', p_id
548
537
  )::TEXT
@@ -550,11 +539,11 @@ var D=(e)=>{return e*1000};var c=D(0);var b=(e)=>{let t=JSON.parse(e);if(t.type=
550
539
  END IF;
551
540
 
552
541
  RETURN QUERY SELECT
553
- ${r(2)};
542
+ ${E(2)};
554
543
  RETURN;
555
544
  END;
556
545
  $$ LANGUAGE plpgsql;
557
- `]}};var __filename="/home/runner/work/lonnymq/lonnymq/src/install/08-function-channel-policy-clear.ts",G={name:_(__filename),sql:(e)=>{return[n`
546
+ `]}};var __filename="/home/runner/work/lonnymq/lonnymq/src/install/08-function-channel-policy-clear.ts",q={name:_(__filename),sql:(e)=>{return[n`
558
547
  CREATE FUNCTION ${s(e.schema)}."channel_policy_clear" (
559
548
  p_name TEXT
560
549
  ) RETURNS VOID AS $$
@@ -583,7 +572,7 @@ var D=(e)=>{return e*1000};var c=D(0);var b=(e)=>{let t=JSON.parse(e);if(t.type=
583
572
  END IF;
584
573
  END;
585
574
  $$ LANGUAGE plpgsql;
586
- `]}};var __filename="/home/runner/work/lonnymq/lonnymq/src/install/09-function-channel-policy-set.ts",q={name:_(__filename),sql:(e)=>{return[n`
575
+ `]}};var __filename="/home/runner/work/lonnymq/lonnymq/src/install/09-function-channel-policy-set.ts",G={name:_(__filename),sql:(e)=>{return[n`
587
576
  CREATE FUNCTION ${s(e.schema)}."channel_policy_set" (
588
577
  p_name TEXT,
589
578
  p_max_concurrency INTEGER,
@@ -614,7 +603,7 @@ var D=(e)=>{return e*1000};var c=D(0);var b=(e)=>{let t=JSON.parse(e);if(t.type=
614
603
  WHERE "name" = p_name;
615
604
  END;
616
605
  $$ LANGUAGE plpgsql;
617
- `]}};class N{schema;channelName;adaptor;constructor(e){this.schema=e.schema,this.adaptor=e.adaptor,this.channelName=e.channelName}async create(e){let t=this.adaptor(e.databaseClient),a=new l({schema:this.schema,channelName:this.channelName,content:e.content,delayMs:e.delayMs}),E=await a.execute(t);return{messageId:E.metadata.id,channelName:a.channelName,channelSize:E.metadata.channelSize}}}class S{schema;adaptor;channelName;constructor(e){this.schema=e.schema,this.adaptor=e.adaptor,this.channelName=e.channelName}set(e){let t=this.adaptor(e.databaseClient);return new m({schema:this.schema,channelName:this.channelName,maxConcurrency:e.maxConcurrency,releaseIntervalMs:e.releaseIntervalMs}).execute(t)}clear(e){let t=this.adaptor(e.databaseClient);return new o({schema:this.schema,channelName:this.channelName}).execute(t)}}class R{policy;message;constructor(e){this.message=new N({schema:e.schema,adaptor:e.adaptor,channelName:e.channelName}),this.policy=new S({schema:e.schema,adaptor:e.adaptor,channelName:e.channelName})}}class A{schema;adaptor;id;isUnlocked;channelName;content;state;numAttempts;constructor(e){this.schema=e.schema,this.adaptor=e.adaptor,this.id=e.id,this.channelName=e.channelName,this.isUnlocked=e.isUnlocked,this.content=e.content,this.state=e.state,this.numAttempts=e.numAttempts}async defer(e){let t=this.adaptor(e.databaseClient);return new d({schema:this.schema,id:this.id,numAttempts:this.numAttempts,delayMs:e.delayMs,state:e.state}).execute(t)}async delete(e){let t=this.adaptor(e.databaseClient);return new u({schema:this.schema,numAttempts:this.numAttempts,id:this.id}).execute(t)}async heartbeat(e){let t=this.adaptor(e.databaseClient);return new T({schema:this.schema,id:this.id,numAttempts:this.numAttempts,lockMs:e.lockMs}).execute(t)}}class g{schema;adaptor;constructor(e){this.schema=e.schema,this.adaptor=e.adaptor}async create(e){let t=this.adaptor(e.databaseClient),a=new l({schema:this.schema,content:e.content,delayMs:e.delayMs}),E=await a.execute(t);return{messageId:E.metadata.id,channelName:a.channelName,channelSize:E.metadata.channelSize}}}var __filename="/home/runner/work/lonnymq/lonnymq/src/install/07-function-message-heartbeat.ts",x={name:_(__filename),sql:(e)=>{return[n`
606
+ `]}};class N{schema;channelName;adaptor;constructor(e){this.schema=e.schema,this.adaptor=e.adaptor,this.channelName=e.channelName}async create(e){let t=this.adaptor(e.databaseClient),a=new l({schema:this.schema,channelName:this.channelName,content:e.content,delayMs:e.delayMs}),r=await a.execute(t);return{messageId:r.id,channelName:a.channelName,channelSize:r.channelSize}}}class S{schema;adaptor;channelName;constructor(e){this.schema=e.schema,this.adaptor=e.adaptor,this.channelName=e.channelName}set(e){let t=this.adaptor(e.databaseClient);return new m({schema:this.schema,channelName:this.channelName,maxConcurrency:e.maxConcurrency,releaseIntervalMs:e.releaseIntervalMs}).execute(t)}clear(e){let t=this.adaptor(e.databaseClient);return new c({schema:this.schema,channelName:this.channelName}).execute(t)}}class R{policy;message;constructor(e){this.message=new N({schema:e.schema,adaptor:e.adaptor,channelName:e.channelName}),this.policy=new S({schema:e.schema,adaptor:e.adaptor,channelName:e.channelName})}}class A{schema;adaptor;id;isUnlocked;channelName;content;state;numAttempts;constructor(e){this.schema=e.schema,this.adaptor=e.adaptor,this.id=e.id,this.channelName=e.channelName,this.isUnlocked=e.isUnlocked,this.content=e.content,this.state=e.state,this.numAttempts=e.numAttempts}async defer(e){let t=this.adaptor(e.databaseClient);return new d({schema:this.schema,id:this.id,numAttempts:this.numAttempts,delayMs:e.delayMs,state:e.state}).execute(t)}async delete(e){let t=this.adaptor(e.databaseClient);return new i({schema:this.schema,numAttempts:this.numAttempts,id:this.id}).execute(t)}async heartbeat(e){let t=this.adaptor(e.databaseClient);return new T({schema:this.schema,id:this.id,numAttempts:this.numAttempts,lockMs:e.lockMs}).execute(t)}}class g{schema;adaptor;constructor(e){this.schema=e.schema,this.adaptor=e.adaptor}async create(e){let t=this.adaptor(e.databaseClient),a=new l({schema:this.schema,content:e.content,delayMs:e.delayMs}),r=await a.execute(t);return{messageId:r.id,channelName:a.channelName,channelSize:r.channelSize}}}var __filename="/home/runner/work/lonnymq/lonnymq/src/install/07-function-message-heartbeat.ts",x={name:_(__filename),sql:(e)=>{return[n`
618
607
  CREATE FUNCTION ${s(e.schema)}."message_heartbeat" (
619
608
  p_id UUID,
620
609
  p_num_attempts BIGINT,
@@ -644,11 +633,11 @@ var D=(e)=>{return e*1000};var c=D(0);var b=(e)=>{let t=JSON.parse(e);if(t.type=
644
633
 
645
634
  IF v_message."id" IS NULL THEN
646
635
  RETURN QUERY SELECT
647
- ${r(0)};
636
+ ${E(0)};
648
637
  RETURN;
649
638
  ELSIF NOT v_message."is_locked" OR v_message."num_attempts" <> p_num_attempts THEN
650
639
  RETURN QUERY SELECT
651
- ${r(1)};
640
+ ${E(1)};
652
641
  RETURN;
653
642
  END IF;
654
643
 
@@ -660,8 +649,8 @@ var D=(e)=>{return e*1000};var c=D(0);var b=(e)=>{let t=JSON.parse(e);if(t.type=
660
649
  WHERE "id" = p_id;
661
650
 
662
651
  RETURN QUERY SELECT
663
- ${r(2)};
652
+ ${E(2)};
664
653
  RETURN;
665
654
  END;
666
655
  $$ LANGUAGE plpgsql;
667
- `]}};class ${schema;message;adaptor;constructor(e){this.schema=e.schema,this.adaptor=e.adaptor?e.adaptor:(t)=>t,this.message=new g({schema:this.schema,adaptor:this.adaptor})}async dequeue(e){let t=new i({schema:this.schema,lockMs:e.lockMs}),a=this.adaptor(e.databaseClient),E=await t.execute(a);if(E.resultType==="MESSAGE_DEQUEUED")return{resultType:"MESSAGE_DEQUEUED",message:new A({schema:this.schema,adaptor:this.adaptor,id:E.message.id,channelName:E.message.channelName,isUnlocked:E.message.isUnlocked,content:E.message.content,state:E.message.state,numAttempts:E.message.numAttempts})};else return E}channel(e){return new R({adaptor:this.adaptor,schema:this.schema,channelName:e})}install(e={}){return[I,v,L,C,U,O,f,x,q,G].sort((t,a)=>t.name.localeCompare(a.name)).flatMap((t)=>t.sql({schema:this.schema,eventChannel:e.eventChannel??null})).map((t)=>y(t.value))}}export{b as queueEventDecode,$ as Queue,T as MessageHeartbeatCommand,i as MessageDequeueCommand,u as MessageDeleteCommand,d as MessageDeferCommand,l as MessageCreateCommand,m as ChannelPolicySetCommand,o as ChannelPolicyClearCommand};
656
+ `]}};class ${schema;message;adaptor;constructor(e){this.schema=e.schema,this.adaptor=e.adaptor?e.adaptor:(t)=>t,this.message=new g({schema:this.schema,adaptor:this.adaptor})}async dequeue(e){let t=new u({schema:this.schema,lockMs:e.lockMs}),a=this.adaptor(e.databaseClient),r=await t.execute(a);if(r.resultType==="MESSAGE_DEQUEUED")return{resultType:"MESSAGE_DEQUEUED",message:new A({schema:this.schema,adaptor:this.adaptor,id:r.id,channelName:r.channelName,isUnlocked:r.isUnlocked,content:r.content,state:r.state,numAttempts:r.numAttempts})};else return r}channel(e){return new R({adaptor:this.adaptor,schema:this.schema,channelName:e})}install(e={}){return[I,v,L,C,U,O,f,x,G,q].sort((t,a)=>t.name.localeCompare(a.name)).flatMap((t)=>t.sql({schema:this.schema,eventChannel:e.eventChannel??null})).map((t)=>y(t.value))}}export{b as queueEventDecode,$ as Queue,T as MessageHeartbeatCommand,u as MessageDequeueCommand,i as MessageDeleteCommand,d as MessageDeferCommand,l as MessageCreateCommand,m as ChannelPolicySetCommand,c as ChannelPolicyClearCommand};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lonnymq",
3
- "version": "0.0.26",
3
+ "version": "0.0.28",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -18,7 +18,8 @@
18
18
  "@typescript-eslint/parser": "8.26.1",
19
19
  "bun-plugin-dts": "^0.3.0",
20
20
  "eslint": "9.22.0",
21
- "pg": "^8.16.3"
21
+ "pg": "^8.16.3",
22
+ "typedoc": "^0.28.13"
22
23
  },
23
24
  "peerDependencies": {
24
25
  "typescript": "^5.0.0"