lonnymq 0.0.1 → 0.0.3

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/dist/index.cjs CHANGED
@@ -1,20 +1,21 @@
1
- var{defineProperty:g,getOwnPropertyNames:K,getOwnPropertyDescriptor:Q}=Object,Y=Object.prototype.hasOwnProperty;var q=new WeakMap,j=(e)=>{var n=q.get(e),_;if(n)return n;if(n=g({},"__esModule",{value:!0}),e&&typeof e==="object"||typeof e==="function")K(e).map((c)=>!Y.call(n,c)&&g(n,c,{get:()=>e[c],enumerable:!(_=Q(e,c))||_.enumerable}));return q.set(e,n),n};var Z=(e,n)=>{for(var _ in n)g(e,_,{get:n[_],enumerable:!0,configurable:!0,set:(c)=>n[_]=()=>c})};var oe={};Z(oe,{Queue:()=>p,MessageDequeueCommand:()=>l,MessageDeleteCommand:()=>d,MessageDeferCommand:()=>h,MessageCreateCommand:()=>i,ChannelPolicySetCommand:()=>o,ChannelPolicyClearCommand:()=>E});module.exports=j(oe);var s=(e)=>({nodeType:"VALUE",value:e}),t=(e)=>({nodeType:"REF",value:e}),ee=(e)=>({nodeType:"RAW",value:e}),ne=(e)=>{return`'${e.replace(/'/g,"''")}'`},te=(e)=>{if(e===null)return"NULL";else if(typeof e==="string")return ne(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 throw new Error(`Unsupported value type: ${typeof e}`)},_e=(e)=>{return`"${e.replace(/"/g,'""')}"`},se=(e)=>{if(e.nodeType==="VALUE")return te(e.value);else if(e.nodeType==="REF")return _e(e.value);else if(e.nodeType==="RAW")return e.value;else throw new Error("Unsupported SQL node type")};var a=(e,...n)=>{let _=[];for(let c=0;c<e.length;c+=1)if(_.push(e[c]),c<n.length)_.push(se(n[c]));return ee(_.join(""))};class E{schema;channelName;createdAt;constructor(e){this.schema=e.schema,this.channelName=e.channelName,this.createdAt=new Date}sortKeyGet(){return JSON.stringify([this.channelName,null,this.createdAt.toISOString()])}async execute(e){await e.query(a`
1
+ var{defineProperty:R,getOwnPropertyNames:X,getOwnPropertyDescriptor:V}=Object,K=Object.prototype.hasOwnProperty;var f=new WeakMap,Q=(e)=>{var n=f.get(e),_;if(n)return n;if(n=R({},"__esModule",{value:!0}),e&&typeof e==="object"||typeof e==="function")X(e).map((c)=>!K.call(n,c)&&R(n,c,{get:()=>e[c],enumerable:!(_=V(e,c))||_.enumerable}));return f.set(e,n),n};var Y=(e,n)=>{for(var _ in n)R(e,_,{get:n[_],enumerable:!0,configurable:!0,set:(c)=>n[_]=()=>c})};var Ee={};Y(Ee,{Queue:()=>U,MessageDequeueCommand:()=>u,MessageDeleteCommand:()=>h,MessageDeferCommand:()=>N,MessageCreateCommand:()=>m,ChannelPolicySetCommand:()=>o,ChannelPolicyClearCommand:()=>r});module.exports=Q(Ee);var s=(e)=>({nodeType:"VALUE",value:e}),t=(e)=>({nodeType:"REF",value:e}),j=(e)=>({nodeType:"RAW",value:e}),Z=(e)=>{return`'${e.replace(/'/g,"''")}'`},ee=(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}`)},ne=(e)=>{return`"${e.replace(/"/g,'""')}"`},te=(e)=>{if(e.nodeType==="VALUE")return ee(e.value);else if(e.nodeType==="REF")return ne(e.value);else if(e.nodeType==="RAW")return e.value;else throw new Error("Unsupported SQL node type")};var a=(e,...n)=>{let _=[];for(let c=0;c<e.length;c+=1)if(_.push(e[c]),c<n.length)_.push(te(n[c]));return j(_.join(""))};class r{schema;channelName;createdAt;constructor(e){this.schema=e.schema,this.channelName=e.channelName,this.createdAt=new Date}sortKeyGet(){return JSON.stringify([this.channelName,null,this.createdAt.toISOString()])}async execute(e){await e.query(a`
2
2
  SELECT 1 FROM ${t(this.schema)}."channel_policy_clear"(
3
3
  ${s(this.channelName)}
4
4
  )
5
- `.value)}}class o{schema;channelName;maxSize;maxConcurrency;createdAt;constructor(e){this.schema=e.schema,this.channelName=e.channelName,this.maxConcurrency=e.maxConcurrency===null?null:Math.max(0,e.maxConcurrency),this.maxSize=e.maxSize===null?null:Math.max(0,e.maxSize),this.createdAt=new Date}sortKeyGet(){return JSON.stringify([this.channelName,null,this.createdAt.toISOString()])}async execute(e){await e.query(a`
5
+ `.value)}}class o{schema;channelName;maxSize;maxConcurrency;releaseIntervalMs;createdAt;constructor(e){this.schema=e.schema,this.channelName=e.channelName;let n=e.maxConcurrency??null;this.maxConcurrency=n!==null?Math.max(0,n):null;let _=e.maxSize??null;this.maxSize=_!==null?Math.max(0,_):null;let c=e.releaseIntervalMs??null;this.releaseIntervalMs=c!==null?Math.max(0,c):null,this.createdAt=new Date}sortKeyGet(){return JSON.stringify([this.channelName,null,this.createdAt.toISOString()])}async execute(e){await e.query(a`
6
6
  SELECT 1 FROM ${t(this.schema)}."channel_policy_set"(
7
7
  ${s(this.channelName)},
8
- ${s(this.maxSize)}::BIGINT,
9
- ${s(this.maxConcurrency)}::BIGINT
8
+ ${s(this.maxSize)}::INTEGER,
9
+ ${s(this.maxConcurrency)}::INTEGER,
10
+ ${s(this.releaseIntervalMs)}::INTEGER
10
11
  )
11
- `.value)}}var R=(e)=>{return e*1000},ae=(e)=>{return R(e*60)},I=(e)=>{return ae(e*60)},C=(e)=>{return I(e*24)};var $=require("node:crypto");class F{value;constructor(e){this.value=e}toString(e){return $.createHash("sha256").update(e).update(this.value).digest("base64").replace(/=/g,"")}}var N=new F("WAKE"),T=C(1000),M=!1,S=R(0),B=I(1);var A=require("path"),__filename="/home/runner/work/lonnymq/lonnymq/src/core/path.ts",ce=A.dirname(A.dirname(__filename)),re=new RegExp(`^${ce}/`),r=(e)=>{return e.replace(re,"")};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/04-function-message-create.ts";var G={name:r(__filename),sql:(e)=>{return[a`
12
+ `.value)}}var S=(e)=>{return e*1000},_e=(e)=>{return S(e*60)},p=(e)=>{return _e(e*60)};var q=require("node:crypto");class C{value;constructor(e){this.value=e}toString(e){return q.createHash("sha256").update(e).update(this.value).digest("base64").replace(/=/g,"")}}var T=new C("WAKE"),$=!1,d=S(0),F=p(1);var g=require("path"),__filename="/home/runner/work/lonnymq/lonnymq/src/core/path.ts",se=g.dirname(g.dirname(__filename)),ae=new RegExp(`^${se}/`),E=(e)=>{return e.replace(ae,"")};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/04-function-message-create.ts";var M={name:E(__filename),sql:(e)=>{return[a`
12
13
  CREATE FUNCTION ${t(e.schema)}."message_create" (
13
14
  p_channel_name TEXT,
14
15
  p_name TEXT,
15
16
  p_content TEXT,
16
- p_lock_ms BIGINT,
17
- p_delay_ms BIGINT
17
+ p_lock_ms INTEGER,
18
+ p_delay_ms INTEGER
18
19
  ) RETURNS JSONB AS $$
19
20
  DECLARE
20
21
  v_now TIMESTAMP;
@@ -23,11 +24,12 @@ var{defineProperty:g,getOwnPropertyNames:K,getOwnPropertyDescriptor:Q}=Object,Y=
23
24
  v_message RECORD;
24
25
  v_message_next_dequeue_after TIMESTAMP;
25
26
  BEGIN
26
- v_now := NOW() + INTERVAL '1 MILLISECOND' * p_delay_ms;
27
+ v_now := NOW();
27
28
 
28
29
  SELECT
29
30
  "max_size",
30
- "max_concurrency"
31
+ "max_concurrency",
32
+ "release_interval_ms"
31
33
  FROM ${t(e.schema)}."channel_policy"
32
34
  WHERE "name" = p_channel_name
33
35
  FOR SHARE
@@ -39,6 +41,7 @@ var{defineProperty:g,getOwnPropertyNames:K,getOwnPropertyDescriptor:Q}=Object,Y=
39
41
  "current_concurrency",
40
42
  "max_size",
41
43
  "max_concurrency",
44
+ "release_interval_ms",
42
45
  "message_next_id",
43
46
  "message_next_dequeue_after"
44
47
  ) VALUES (
@@ -47,6 +50,7 @@ var{defineProperty:g,getOwnPropertyNames:K,getOwnPropertyDescriptor:Q}=Object,Y=
47
50
  0,
48
51
  v_channel_policy."max_size",
49
52
  v_channel_policy."max_concurrency",
53
+ v_channel_policy."release_interval_ms",
50
54
  NULL,
51
55
  NULL
52
56
  ) ON CONFLICT ("name")
@@ -82,7 +86,8 @@ var{defineProperty:g,getOwnPropertyNames:K,getOwnPropertyDescriptor:Q}=Object,Y=
82
86
  ) ON CONFLICT ("channel_name", "name")
83
87
  WHERE "num_attempts" = 0
84
88
  DO UPDATE SET
85
- "id" = EXCLUDED."id"
89
+ "channel_name" = EXCLUDED."channel_name",
90
+ "name" = EXCLUDED."name"
86
91
  RETURNING
87
92
  "id",
88
93
  "xmax",
@@ -103,7 +108,7 @@ var{defineProperty:g,getOwnPropertyNames:K,getOwnPropertyDescriptor:Q}=Object,Y=
103
108
  UPDATE ${t(e.schema)}."channel_state" SET
104
109
  "current_size" = v_channel_state."current_size" + 1,
105
110
  "message_next_id" = v_message."id",
106
- "message_next_dequeue_after" = v_message."dequeue_after"
111
+ "message_next_dequeue_after" = GREATEST(v_now, v_message."dequeue_after")
107
112
  WHERE "id" = v_channel_state."id";
108
113
  ELSE
109
114
  UPDATE ${t(e.schema)}."channel_state" SET
@@ -119,20 +124,20 @@ var{defineProperty:g,getOwnPropertyNames:K,getOwnPropertyDescriptor:Q}=Object,Y=
119
124
  );
120
125
  END;
121
126
  $$ LANGUAGE plpgsql;
122
- `]}};class i{schema;channelName;name;content;lockMs;delayMs;createdAt;constructor(e){let n=e.name??null,_=e.lockMs===void 0?B:Math.max(0,e.lockMs),c;if(e.priority)c=-T;else c=e.delayMs===void 0?S:Math.max(0,e.delayMs);this.schema=e.schema,this.channelName=e.channelName,this.content=e.content,this.name=n,this.lockMs=_,this.delayMs=c,this.createdAt=new Date}async execute(e){let n=await e.query(a`
127
+ `]}};class m{schema;channelName;name;content;lockMs;delayMs;createdAt;constructor(e){let n=e.name??null,_=e.lockMs===void 0?F:Math.max(0,e.lockMs),c=e.delayMs===void 0?d:e.delayMs;this.schema=e.schema,this.channelName=e.channelName,this.content=e.content,this.name=n,this.lockMs=_,this.delayMs=c,this.createdAt=new Date}async execute(e){let n=await e.query(a`
123
128
  SELECT ${t(this.schema)}."message_create"(
124
129
  ${s(this.channelName)},
125
130
  ${s(this.name)},
126
131
  ${s(this.content)},
127
- ${s(this.lockMs)}::BIGINT,
128
- ${s(this.delayMs)}::BIGINT
132
+ ${s(this.lockMs)}::INTEGER,
133
+ ${s(this.delayMs)}::INTEGER
129
134
  ) AS "result"
130
- `.value).then((_)=>_.rows[0].result);if(n.result_code===1)return{resultType:"MESSAGE_DROPPED"};else if(n.result_code===2)return{resultType:"MESSAGE_DEDUPLICATED",id:n.id};else if(n.result_code===0)return{resultType:"MESSAGE_CREATED",id:n.id};else throw new Error("Unexpected result")}}var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/05-function-message-dequeue.ts";var P={name:r(__filename),sql:(e)=>{return[a`
135
+ `.value).then((_)=>_.rows[0].result);if(n.result_code===1)return{resultType:"MESSAGE_DROPPED"};else if(n.result_code===2)return{resultType:"MESSAGE_DEDUPLICATED",id:BigInt(n.id)};else if(n.result_code===0)return{resultType:"MESSAGE_CREATED",id:BigInt(n.id)};else throw new Error("Unexpected result")}}var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/05-function-message-dequeue.ts";var G={name:E(__filename),sql:(e)=>{return[a`
131
136
  CREATE FUNCTION ${t(e.schema)}."message_dequeue" ()
132
137
  RETURNS JSONB AS $$
133
138
  DECLARE
134
139
  v_now TIMESTAMP;
135
- v_dequeue_id UUID;
140
+ v_dequeue_nonce UUID;
136
141
  v_channel_state RECORD;
137
142
  v_message_locked RECORD;
138
143
  v_retry_after TIMESTAMP;
@@ -141,7 +146,7 @@ var{defineProperty:g,getOwnPropertyNames:K,getOwnPropertyDescriptor:Q}=Object,Y=
141
146
  v_message_next_dequeue_after TIMESTAMP;
142
147
  BEGIN
143
148
  v_now := NOW();
144
- v_dequeue_id := GEN_RANDOM_UUID();
149
+ v_dequeue_nonce := GEN_RANDOM_UUID();
145
150
 
146
151
  SELECT
147
152
  "id",
@@ -164,7 +169,7 @@ var{defineProperty:g,getOwnPropertyNames:K,getOwnPropertyDescriptor:Q}=Object,Y=
164
169
  UPDATE ${t(e.schema)}."message" SET
165
170
  "num_attempts" = v_message_locked."num_attempts" + 1,
166
171
  "dequeue_after" = v_now + (v_message_locked."lock_ms" * INTERVAL '1 millisecond'),
167
- "dequeue_id" = v_dequeue_id
172
+ "dequeue_nonce" = v_dequeue_nonce
168
173
  WHERE "id" = v_message_locked."id";
169
174
 
170
175
  PERFORM ${t(e.schema)}."wake"(v_message_locked."lock_ms");
@@ -175,7 +180,7 @@ var{defineProperty:g,getOwnPropertyNames:K,getOwnPropertyDescriptor:Q}=Object,Y=
175
180
  'channel_name', v_message_locked.channel_name,
176
181
  'state', v_message_locked.state,
177
182
  'name', v_message_locked.name,
178
- 'dequeue_id', v_dequeue_id,
183
+ 'dequeue_nonce', v_dequeue_nonce,
179
184
  'content', v_message_locked.content,
180
185
  'num_attempts', v_message_locked.num_attempts
181
186
  );
@@ -184,6 +189,7 @@ var{defineProperty:g,getOwnPropertyNames:K,getOwnPropertyDescriptor:Q}=Object,Y=
184
189
  SELECT
185
190
  "id",
186
191
  "name",
192
+ "release_interval_ms",
187
193
  "message_next_id",
188
194
  "message_next_dequeue_after",
189
195
  "current_concurrency"
@@ -223,7 +229,7 @@ var{defineProperty:g,getOwnPropertyNames:K,getOwnPropertyDescriptor:Q}=Object,Y=
223
229
  UPDATE ${t(e.schema)}."message" SET
224
230
  "is_locked" = TRUE,
225
231
  "num_attempts" = v_message_dequeue."num_attempts" + 1,
226
- "dequeue_id" = v_dequeue_id,
232
+ "dequeue_nonce" = v_dequeue_nonce,
227
233
  "dequeue_after" = v_now + (v_message_dequeue."lock_ms" * INTERVAL '1 millisecond')
228
234
  WHERE "id" = v_message_dequeue."id";
229
235
 
@@ -242,7 +248,7 @@ var{defineProperty:g,getOwnPropertyNames:K,getOwnPropertyDescriptor:Q}=Object,Y=
242
248
  IF v_message_next_dequeue."id" IS NOT NULL THEN
243
249
  v_message_next_dequeue_after := GREATEST(
244
250
  v_message_next_dequeue."dequeue_after",
245
- v_now
251
+ v_now + (COALESCE(v_channel_state."release_interval_ms", 0) * INTERVAL '1 millisecond')
246
252
  );
247
253
 
248
254
  UPDATE ${t(e.schema)}."channel_state" SET
@@ -263,29 +269,30 @@ var{defineProperty:g,getOwnPropertyNames:K,getOwnPropertyDescriptor:Q}=Object,Y=
263
269
  'id', v_message_dequeue.id,
264
270
  'channel_name', v_message_dequeue.channel_name,
265
271
  'state', v_message_dequeue.state,
266
- 'dequeue_id', v_dequeue_id,
272
+ 'dequeue_nonce', v_dequeue_nonce,
267
273
  'name', v_message_dequeue.name,
268
274
  'content', v_message_dequeue.content,
269
275
  'num_attempts', v_message_dequeue.num_attempts
270
276
  );
271
277
  END;
272
278
  $$ LANGUAGE plpgsql;
273
- `]}};class l{schema;constructor(e){this.schema=e.schema}async execute(e){let n=await e.query(a`
279
+ `]}};class u{schema;constructor(e){this.schema=e.schema}async execute(e){let n=await e.query(a`
274
280
  SELECT ${t(this.schema)}."message_dequeue"() AS "result"
275
- `.value).then((_)=>_.rows[0].result);if(n.result_code===0)return{resultType:"MESSAGE_NOT_AVAILABLE",retryMs:n.retry_ms};else if(n.result_code===1)return{resultType:"MESSAGE_DEQUEUED",message:{id:n.id,channelName:n.channel_name,name:n.name,content:n.content,dequeueId:n.dequeue_id,state:n.state,numAttempts:n.num_attempts,lockMs:n.lock_ms}};else throw new Error("Unexpected dequeue result")}}var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/06-function-message-delete.ts";var k={name:r(__filename),sql:(e)=>{return[a`
281
+ `.value).then((_)=>_.rows[0].result);if(n.result_code===0)return{resultType:"MESSAGE_NOT_AVAILABLE",retryMs:n.retry_ms};else if(n.result_code===1)return{resultType:"MESSAGE_DEQUEUED",message:{id:BigInt(n.id),channelName:n.channel_name,name:n.name,content:n.content,dequeueNonce:n.dequeue_nonce,state:n.state,numAttempts:n.num_attempts}};else throw new Error("Unexpected dequeue result")}}var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/06-function-message-delete.ts";var B={name:E(__filename),sql:(e)=>{return[a`
276
282
  CREATE FUNCTION ${t(e.schema)}."message_delete" (
277
- p_id UUID,
278
- p_dequeue_id UUID
283
+ p_id BIGINT,
284
+ p_dequeue_nonce UUID
279
285
  )
280
286
  RETURNS JSONB AS $$
281
287
  DECLARE
288
+ v_channel_policy RECORD;
282
289
  v_channel_state RECORD;
283
290
  v_message RECORD;
284
291
  BEGIN
285
292
  SELECT
286
293
  "id",
287
294
  "channel_name",
288
- "dequeue_id"
295
+ "dequeue_nonce"
289
296
  FROM ${t(e.schema)}."message"
290
297
  WHERE "id" = p_id
291
298
  FOR UPDATE
@@ -295,12 +302,19 @@ var{defineProperty:g,getOwnPropertyNames:K,getOwnPropertyDescriptor:Q}=Object,Y=
295
302
  RETURN JSONB_BUILD_OBJECT(
296
303
  'result_code', ${s(0)}
297
304
  );
298
- ELSEIF v_message."dequeue_id" != p_dequeue_id THEN
305
+ ELSEIF v_message."dequeue_nonce" != p_dequeue_nonce THEN
299
306
  RETURN JSONB_BUILD_OBJECT(
300
307
  'result_code', ${s(1)}
301
308
  );
302
309
  END IF;
303
310
 
311
+ SELECT
312
+ "id"
313
+ FROM ${t(e.schema)}."channel_policy"
314
+ WHERE "name" = v_message."channel_name"
315
+ FOR SHARE
316
+ INTO v_channel_policy;
317
+
304
318
  SELECT
305
319
  "id",
306
320
  "current_size",
@@ -310,7 +324,7 @@ var{defineProperty:g,getOwnPropertyNames:K,getOwnPropertyDescriptor:Q}=Object,Y=
310
324
  FOR UPDATE
311
325
  INTO v_channel_state;
312
326
 
313
- IF v_channel_state."current_size" = 1 THEN
327
+ IF v_channel_policy."id" IS NULL AND v_channel_state."current_size" = 1 THEN
314
328
  DELETE FROM ${t(e.schema)}."channel_state"
315
329
  WHERE "id" = v_channel_state."id";
316
330
  ELSE
@@ -328,15 +342,15 @@ var{defineProperty:g,getOwnPropertyNames:K,getOwnPropertyDescriptor:Q}=Object,Y=
328
342
  );
329
343
  END;
330
344
  $$ LANGUAGE plpgsql;
331
- `]}};class d{schema;id;dequeueId;constructor(e){this.schema=e.schema,this.id=e.id,this.dequeueId=e.dequeueId}async execute(e){let n=await e.query(a`
345
+ `]}};class h{schema;id;dequeueNonce;constructor(e){this.schema=e.schema,this.id=e.id,this.dequeueNonce=e.dequeueNonce}async execute(e){let n=await e.query(a`
332
346
  SELECT ${t(this.schema)}."message_delete"( ${s(this.id)},
333
- ${s(this.dequeueId)}
347
+ ${s(this.dequeueNonce)}
334
348
  ) AS "result"
335
- `.value).then((_)=>_.rows[0].result);if(n.result_code===0)return{resultType:"MESSAGE_NOT_FOUND"};else if(n.result_code===1)return{resultType:"STATE_INVALID"};else if(n.result_code===2)return{resultType:"MESSAGE_DELETED"};else throw new Error("Unexpected result")}}var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/07-function-message-defer.ts";var b={name:r(__filename),sql:(e)=>{return[a`
349
+ `.value).then((_)=>_.rows[0].result);if(n.result_code===0)return{resultType:"MESSAGE_NOT_FOUND"};else if(n.result_code===1)return{resultType:"STATE_INVALID"};else if(n.result_code===2)return{resultType:"MESSAGE_DELETED"};else throw new Error("Unexpected result")}}var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/07-function-message-defer.ts";var P={name:E(__filename),sql:(e)=>{return[a`
336
350
  CREATE FUNCTION ${t(e.schema)}."message_defer" (
337
- p_id UUID,
338
- p_dequeue_id UUID,
339
- p_delay_ms BIGINT,
351
+ p_id BIGINT,
352
+ p_dequeue_nonce UUID,
353
+ p_delay_ms INTEGER,
340
354
  p_state TEXT
341
355
  )
342
356
  RETURNS JSONB AS $$
@@ -348,7 +362,7 @@ var{defineProperty:g,getOwnPropertyNames:K,getOwnPropertyDescriptor:Q}=Object,Y=
348
362
  SELECT
349
363
  "id",
350
364
  "channel_name",
351
- "dequeue_id"
365
+ "dequeue_nonce"
352
366
  FROM ${t(e.schema)}."message"
353
367
  WHERE "id" = p_id
354
368
  FOR UPDATE
@@ -358,7 +372,7 @@ var{defineProperty:g,getOwnPropertyNames:K,getOwnPropertyDescriptor:Q}=Object,Y=
358
372
  RETURN JSONB_BUILD_OBJECT(
359
373
  'result_code', ${s(0)}
360
374
  );
361
- ELSEIF v_message."dequeue_id" != p_dequeue_id THEN
375
+ ELSEIF v_message."dequeue_nonce" != p_dequeue_nonce THEN
362
376
  RETURN JSONB_BUILD_OBJECT(
363
377
  'result_code', ${s(1)}
364
378
  );
@@ -403,35 +417,37 @@ var{defineProperty:g,getOwnPropertyNames:K,getOwnPropertyDescriptor:Q}=Object,Y=
403
417
  );
404
418
  END;
405
419
  $$ LANGUAGE plpgsql;
406
- `]}};class h{schema;id;dequeueId;delayMs;state;constructor(e){let n;if(e.priority)n=-T;else n=e.delayMs===void 0?S:Math.max(0,e.delayMs);this.schema=e.schema,this.id=e.id,this.dequeueId=e.dequeueId,this.delayMs=n,this.state=e.state??null}async execute(e){let n=await e.query(a`
420
+ `]}};class N{schema;id;dequeueNonce;delayMs;state;constructor(e){let n=e.delayMs===void 0?d:e.delayMs;this.schema=e.schema,this.id=e.id,this.dequeueNonce=e.dequeueNonce,this.delayMs=n,this.state=e.state??null}async execute(e){let n=await e.query(a`
407
421
  SELECT ${t(this.schema)}."message_defer"(
408
422
  ${s(this.id)},
409
- ${s(this.dequeueId)},
410
- ${s(this.delayMs)}::BIGINT,
423
+ ${s(this.dequeueNonce)},
424
+ ${s(this.delayMs)},
411
425
  ${s(this.state)}
412
426
  ) AS "result"
413
427
  `.value).then((_)=>_.rows[0].result);if(n.result_code===0)return{resultType:"MESSAGE_NOT_FOUND"};else if(n.result_code===1)return{resultType:"STATE_INVALID"};else if(n.result_code===2)return{resultType:"MESSAGE_DEFERRED"};else throw new Error("Unexpected result")}}var H=(e)=>{let n=e.split(`
414
- `),_=Number.MAX_SAFE_INTEGER;for(let c of n){if(c.trim().length===0)continue;let u=c.search(/\S/);_=Math.min(_,u)}return n.map((c)=>c.slice(_)).join(`
415
- `).trim()};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/00-table-channel-policy.ts",z={name:r(__filename),sql:(e)=>{return[a`
428
+ `),_=Number.MAX_SAFE_INTEGER;for(let c of n){if(c.trim().length===0)continue;let l=c.search(/\S/);_=Math.min(_,l)}return n.map((c)=>c.slice(_)).join(`
429
+ `).trim()};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/00-table-channel-policy.ts",b={name:E(__filename),sql:(e)=>{return[a`
416
430
  CREATE TABLE ${t(e.schema)}."channel_policy" (
417
- "id" UUID NOT NULL DEFAULT GEN_RANDOM_UUID(),
431
+ "id" BIGSERIAL NOT NULL,
418
432
  "name" TEXT NOT NULL,
419
- "max_size" BIGINT,
420
- "max_concurrency" BIGINT,
433
+ "max_size" INTEGER,
434
+ "max_concurrency" INTEGER,
435
+ "release_interval_ms" INTEGER,
421
436
  PRIMARY KEY ("id")
422
437
  );
423
438
  `,a`
424
439
  CREATE UNIQUE INDEX "channel_policy_name_ux"
425
440
  ON ${t(e.schema)}."channel_policy" ("name");
426
- `]}};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/01-table-channel-state.ts",w={name:r(__filename),sql:(e)=>{return[a`
441
+ `]}};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/01-table-channel-state.ts",k={name:E(__filename),sql:(e)=>{return[a`
427
442
  CREATE TABLE ${t(e.schema)}."channel_state" (
428
- "id" UUID NOT NULL DEFAULT GEN_RANDOM_UUID(),
443
+ "id" BIGSERIAL NOT NULL,
429
444
  "name" TEXT NOT NULL,
430
- "max_size" BIGINT,
431
- "max_concurrency" BIGINT,
432
- "current_size" BIGINT NOT NULL,
433
- "current_concurrency" BIGINT NOT NULL,
434
- "message_next_id" UUID,
445
+ "max_size" INTEGER,
446
+ "max_concurrency" INTEGER,
447
+ "release_interval_ms" INTEGER,
448
+ "current_size" INTEGER NOT NULL,
449
+ "current_concurrency" INTEGER NOT NULL,
450
+ "message_next_id" BIGINT,
435
451
  "message_next_dequeue_after" TIMESTAMP,
436
452
  PRIMARY KEY ("id")
437
453
  );
@@ -444,19 +460,18 @@ var{defineProperty:g,getOwnPropertyNames:K,getOwnPropertyDescriptor:Q}=Object,Y=
444
460
  "message_next_dequeue_after" ASC
445
461
  ) WHERE "message_next_id" IS NOT NULL
446
462
  AND ("max_concurrency" IS NULL OR "current_concurrency" < "max_concurrency");
447
- `]}};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/02-table-message.ts",W={name:r(__filename),sql:(e)=>{return[a`
463
+ `]}};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/02-table-message.ts",z={name:E(__filename),sql:(e)=>{return[a`
448
464
  CREATE TABLE ${t(e.schema)}."message" (
449
- "id" UUID NOT NULL DEFAULT GEN_RANDOM_UUID(),
465
+ "id" BIGSERIAL NOT NULL,
450
466
  "channel_name" TEXT NOT NULL,
451
- "dequeue_id" UUID,
467
+ "dequeue_nonce" UUID,
452
468
  "name" TEXT,
453
469
  "content" TEXT NOT NULL,
454
470
  "state" TEXT,
455
- "lock_ms" BIGINT NOT NULL,
471
+ "lock_ms" INTEGER NOT NULL,
456
472
  "is_locked" BOOLEAN NOT NULL DEFAULT FALSE,
457
- "num_attempts" BIGINT NOT NULL DEFAULT 0,
473
+ "num_attempts" INTEGER NOT NULL DEFAULT 0,
458
474
  "dequeue_after" TIMESTAMP NOT NULL,
459
- "sweep_after" TIMESTAMP,
460
475
  PRIMARY KEY ("id")
461
476
  );
462
477
  `,a`
@@ -476,9 +491,9 @@ var{defineProperty:g,getOwnPropertyNames:K,getOwnPropertyDescriptor:Q}=Object,Y=
476
491
  ON ${t(e.schema)}."message" (
477
492
  "dequeue_after" ASC
478
493
  ) WHERE "is_locked";
479
- `]}};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/03-function-wake.ts",J={name:r(__filename),sql:(e)=>{let n=N.toString(e.schema);return[a`
494
+ `]}};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/03-function-wake.ts",w={name:E(__filename),sql:(e)=>{let n=T.toString(e.schema);return[a`
480
495
  CREATE FUNCTION ${t(e.schema)}."wake" (
481
- p_delay_ms BIGINT
496
+ p_delay_ms INTEGER
482
497
  ) RETURNS VOID AS $$
483
498
  BEGIN
484
499
  IF ${s(e.useWake)} THEN
@@ -486,47 +501,68 @@ var{defineProperty:g,getOwnPropertyNames:K,getOwnPropertyDescriptor:Q}=Object,Y=
486
501
  END IF;
487
502
  END;
488
503
  $$ LANGUAGE plpgsql;
489
- `]}};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/08-function-channel-policy-set.ts",V={name:r(__filename),sql:(e)=>{return[a`
504
+ `]}};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/08-function-channel-policy-set.ts",W={name:E(__filename),sql:(e)=>{return[a`
490
505
  CREATE FUNCTION ${t(e.schema)}."channel_policy_set" (
491
506
  p_name TEXT,
492
- p_max_size BIGINT,
493
- p_max_concurrency BIGINT
507
+ p_max_size INTEGER,
508
+ p_max_concurrency INTEGER,
509
+ p_release_interval_ms INTEGER
494
510
  ) RETURNS VOID AS $$
495
511
  BEGIN
496
512
  INSERT INTO ${t(e.schema)}."channel_policy" (
497
513
  "name",
498
514
  "max_size",
499
- "max_concurrency"
515
+ "max_concurrency",
516
+ "release_interval_ms"
500
517
  ) VALUES (
501
518
  p_name,
502
519
  p_max_size,
503
- p_max_concurrency
520
+ p_max_concurrency,
521
+ p_release_interval_ms
504
522
  ) ON CONFLICT ("name") DO UPDATE SET
505
523
  "max_size" = EXCLUDED."max_size",
506
- "max_concurrency" = EXCLUDED."max_concurrency";
524
+ "max_concurrency" = EXCLUDED."max_concurrency",
525
+ "release_interval_ms" = EXCLUDED."release_interval_ms";
507
526
 
508
527
  UPDATE ${t(e.schema)}."channel_state" SET
509
528
  "max_size" = p_max_size,
510
- "max_concurrency" = p_max_concurrency
529
+ "max_concurrency" = p_max_concurrency,
530
+ "release_interval_ms" = p_release_interval_ms
511
531
  WHERE "name" = p_name;
512
532
 
513
533
  PERFORM ${t(e.schema)}."wake"(0);
514
534
  END;
515
535
  $$ LANGUAGE plpgsql;
516
- `]}};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/09-function-channel-policy-clear.ts",X={name:r(__filename),sql:(e)=>{return[a`
536
+ `]}};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/09-function-channel-policy-clear.ts",J={name:E(__filename),sql:(e)=>{return[a`
517
537
  CREATE FUNCTION ${t(e.schema)}."channel_policy_clear" (
518
538
  p_name TEXT
519
539
  ) RETURNS VOID AS $$
540
+ DECLARE
541
+ v_channel_state RECORD;
520
542
  BEGIN
521
543
  DELETE FROM ${t(e.schema)}."channel_policy"
522
544
  WHERE "name" = p_name;
523
545
 
524
- UPDATE ${t(e.schema)}."channel_state" SET
525
- "max_size" = NULL,
526
- "max_concurrency" = NULL
527
- WHERE "name" = p_name;
546
+ SELECT
547
+ "id",
548
+ "current_size"
549
+ FROM ${t(e.schema)}."channel_state"
550
+ WHERE "name" = p_name
551
+ FOR UPDATE
552
+ INTO v_channel_state;
553
+
554
+ IF v_channel_state."current_size" = 0 THEN
555
+ DELETE FROM ${t(e.schema)}."channel_state"
556
+ WHERE "id" = v_channel_state."id";
557
+ ELSE
558
+ UPDATE ${t(e.schema)}."channel_state" SET
559
+ "max_size" = NULL,
560
+ "max_concurrency" = NULL,
561
+ "release_interval_ms" = NULL
562
+ WHERE "name" = p_name;
563
+ END IF;
528
564
 
529
565
  PERFORM ${t(e.schema)}."wake"(0);
530
566
  END;
531
567
  $$ LANGUAGE plpgsql;
532
- `]}};class m{value;isSet;constructor(){this.isSet=!1,this.value=null}get(){return this.isSet?{resultType:"RESULT_SET",value:this.value}:{resultType:"RESULT_NOT_SET"}}set(e){this.isSet=!0,this.value=e}}class O{schema;channelName;registerFn;constructor(e){this.schema=e.schema,this.channelName=e.channelName,this.registerFn=e.registerFn}create(e){let n=new i({schema:this.schema,channelName:this.channelName,name:e.name,content:e.content,lockMs:e.lockMs,delayMs:e.delayMs,priority:e.priority}),_=new m;return this.registerFn({sortKey:JSON.stringify([n.channelName,n.name,n.createdAt.toISOString()]),execute:(c)=>n.execute(c).then((u)=>_.set(u))}),_}}class D{schema;channelName;registerFn;constructor(e){this.schema=e.schema,this.channelName=e.channelName,this.registerFn=e.registerFn}set(e){let n=new o({schema:this.schema,channelName:this.channelName,maxConcurrency:e.maxConcurrency,maxSize:e.maxSize}),_=new m;return this.registerFn({sortKey:JSON.stringify([n.channelName,null,n.createdAt.toISOString()]),execute:(c)=>n.execute(c).then((u)=>_.set(u))}),_}clear(){let e=new E({schema:this.schema,channelName:this.channelName}),n=new m;return this.registerFn({sortKey:JSON.stringify([e.channelName,null,e.createdAt.toISOString()]),execute:(_)=>e.execute(_).then((c)=>n.set(c))}),n}}class U{policy;message;constructor(e){this.message=new O(e),this.policy=new D(e)}}var Ee=(e,n)=>{return e.sortKey.localeCompare(n.sortKey)};class y{commands;schema;constructor(e){this.commands=[],this.schema=e.schema}channel(e){return new U({schema:this.schema,channelName:e,registerFn:(n)=>{this.commands.push(n)}})}async execute(e){for(let n of this.commands.sort(Ee))await n.execute(e.databaseClient)}}class L{schema;channelName;constructor(e){this.schema=e.schema,this.channelName=e.channelName}async create(e){return new i({schema:this.schema,channelName:this.channelName,name:e.name,content:e.content,lockMs:e.lockMs,delayMs:e.delayMs,priority:e.priority}).execute(e.databaseClient)}}class x{schema;channelName;constructor(e){this.schema=e.schema,this.channelName=e.channelName}set(e){return new o({schema:this.schema,channelName:this.channelName,maxConcurrency:e.maxConcurrency,maxSize:e.maxSize}).execute(e.databaseClient)}clear(e){return new E({schema:this.schema,channelName:this.channelName}).execute(e.databaseClient)}}class v{policy;message;constructor(e){this.message=new L({schema:e.schema,channelName:e.channelName}),this.policy=new x({schema:e.schema,channelName:e.channelName})}}class f{schema;id;channelName;name;content;dequeueId;state;numAttempts;lockMs;constructor(e){this.schema=e.schema,this.id=e.id,this.channelName=e.channelName,this.dequeueId=e.dequeueId,this.name=e.name,this.content=e.content,this.state=e.state,this.numAttempts=e.numAttempts,this.lockMs=e.lockMs}async defer(e){return new h({schema:this.schema,id:this.id,dequeueId:this.dequeueId,delayMs:e.delayMs,state:e.state,priority:e.priority}).execute(e.databaseClient)}async delete(e){return new d({schema:this.schema,id:this.id,dequeueId:this.dequeueId}).execute(e.databaseClient)}}class p{schema;constructor(e){this.schema=e}async dequeue(e){let _=await new l({schema:this.schema}).execute(e.databaseClient);if(_.resultType==="MESSAGE_DEQUEUED")return{resultType:"MESSAGE_DEQUEUED",message:new f({schema:this.schema,id:_.message.id,dequeueId:_.message.dequeueId,channelName:_.message.channelName,name:_.message.name,content:_.message.content,state:_.message.state,numAttempts:_.message.numAttempts,lockMs:_.message.lockMs})};else return _}channel(e){return new v({schema:this.schema,channelName:e})}batch(){return new y({schema:this.schema})}migrations(e){return[z,w,W,J,G,P,k,b,V,X].map((n)=>({name:n.name,sql:n.sql({schema:this.schema,useWake:e.useWake??M}).map((_)=>H(_.value))})).sort((n,_)=>n.name.localeCompare(_.name))}wakeChannel(){return N.toString(this.schema)}}
568
+ `]}};class i{value;isSet;constructor(){this.isSet=!1,this.value=null}get(){return this.isSet?{resultType:"RESULT_SET",value:this.value}:{resultType:"RESULT_NOT_SET"}}set(e){this.isSet=!0,this.value=e}}class I{schema;channelName;registerFn;constructor(e){this.schema=e.schema,this.channelName=e.channelName,this.registerFn=e.registerFn}create(e){let n=new m({schema:this.schema,channelName:this.channelName,name:e.name,content:e.content,lockMs:e.lockMs,delayMs:e.delayMs}),_=new i;return this.registerFn({sortKey:JSON.stringify([n.channelName,n.name,n.createdAt.toISOString()]),execute:(c)=>n.execute(c).then((l)=>_.set(l))}),_}}class O{schema;channelName;registerFn;constructor(e){this.schema=e.schema,this.channelName=e.channelName,this.registerFn=e.registerFn}set(e){let n=new o({schema:this.schema,channelName:this.channelName,maxConcurrency:e.maxConcurrency,maxSize:e.maxSize,releaseIntervalMs:e.releaseIntervalMs}),_=new i;return this.registerFn({sortKey:JSON.stringify([n.channelName,null,n.createdAt.toISOString()]),execute:(c)=>n.execute(c).then((l)=>_.set(l))}),_}clear(){let e=new r({schema:this.schema,channelName:this.channelName}),n=new i;return this.registerFn({sortKey:JSON.stringify([e.channelName,null,e.createdAt.toISOString()]),execute:(_)=>e.execute(_).then((c)=>n.set(c))}),n}}class A{policy;message;constructor(e){this.message=new I(e),this.policy=new O(e)}}var ce=(e,n)=>{return e.sortKey.localeCompare(n.sortKey)};class L{commands;schema;constructor(e){this.commands=[],this.schema=e.schema}channel(e){return new A({schema:this.schema,channelName:e,registerFn:(n)=>{this.commands.push(n)}})}async execute(e){for(let n of this.commands.sort(ce))await n.execute(e.databaseClient)}}class v{schema;channelName;constructor(e){this.schema=e.schema,this.channelName=e.channelName}async create(e){return new m({schema:this.schema,channelName:this.channelName,name:e.name,content:e.content,lockMs:e.lockMs,delayMs:e.delayMs}).execute(e.databaseClient)}}class D{schema;channelName;constructor(e){this.schema=e.schema,this.channelName=e.channelName}set(e){return new o({schema:this.schema,channelName:this.channelName,maxConcurrency:e.maxConcurrency,maxSize:e.maxSize,releaseIntervalMs:e.releaseIntervalMs}).execute(e.databaseClient)}clear(e){return new r({schema:this.schema,channelName:this.channelName}).execute(e.databaseClient)}}class y{policy;message;constructor(e){this.message=new v({schema:e.schema,channelName:e.channelName}),this.policy=new D({schema:e.schema,channelName:e.channelName})}}class x{schema;id;channelName;name;content;dequeueNonce;state;numAttempts;constructor(e){this.schema=e.schema,this.id=e.id,this.channelName=e.channelName,this.dequeueNonce=e.dequeueNonce,this.name=e.name,this.content=e.content,this.state=e.state,this.numAttempts=e.numAttempts}async defer(e){return new N({schema:this.schema,id:this.id,dequeueNonce:this.dequeueNonce,delayMs:e.delayMs,state:e.state}).execute(e.databaseClient)}async delete(e){return new h({schema:this.schema,id:this.id,dequeueNonce:this.dequeueNonce}).execute(e.databaseClient)}}class U{schema;constructor(e){this.schema=e}async dequeue(e){let _=await new u({schema:this.schema}).execute(e.databaseClient);if(_.resultType==="MESSAGE_DEQUEUED")return{resultType:"MESSAGE_DEQUEUED",message:new x({schema:this.schema,id:_.message.id,dequeueNonce:_.message.dequeueNonce,channelName:_.message.channelName,name:_.message.name,content:_.message.content,state:_.message.state,numAttempts:_.message.numAttempts})};else return _}channel(e){return new y({schema:this.schema,channelName:e})}batch(){return new L({schema:this.schema})}migrations(e){return[b,k,z,w,M,G,B,P,W,J].map((n)=>({name:n.name,sql:n.sql({schema:this.schema,useWake:e.useWake??$}).map((_)=>H(_.value))})).sort((n,_)=>n.name.localeCompare(_.name))}wakeChannel(){return T.toString(this.schema)}}
package/dist/index.d.ts CHANGED
@@ -35,23 +35,25 @@ export declare class ChannelPolicySetCommand {
35
35
  readonly channelName: string;
36
36
  readonly maxSize: number | null;
37
37
  readonly maxConcurrency: number | null;
38
+ readonly releaseIntervalMs: number | null;
38
39
  readonly createdAt: Date;
39
40
  constructor(params: {
40
41
  schema: string;
41
42
  channelName: string;
42
- maxSize: number | null;
43
- maxConcurrency: number | null;
43
+ maxSize?: number | null;
44
+ maxConcurrency?: number | null;
45
+ releaseIntervalMs?: number | null;
44
46
  });
45
47
  sortKeyGet(): string;
46
48
  execute(databaseClient: DatabaseClient): Promise<void>;
47
49
  }
48
50
  export type MessageCreateCommandResultMessageCreated = {
49
51
  resultType: "MESSAGE_CREATED";
50
- id: string;
52
+ id: bigint;
51
53
  };
52
54
  export type MessageCreateCommandResultMessageDeduplicated = {
53
55
  resultType: "MESSAGE_DEDUPLICATED";
54
- id: string;
56
+ id: bigint;
55
57
  };
56
58
  export type MessageCreateCommandResultMessageDropped = {
57
59
  resultType: "MESSAGE_DROPPED";
@@ -72,21 +74,19 @@ export declare class MessageCreateCommand {
72
74
  content: string;
73
75
  lockMs?: number;
74
76
  delayMs?: number;
75
- priority?: boolean;
76
77
  });
77
78
  execute(databaseClient: DatabaseClient): Promise<MessageCreateCommandResult>;
78
79
  }
79
80
  export type MessageDequeueCommandResultMessageDequeued = {
80
81
  resultType: "MESSAGE_DEQUEUED";
81
82
  message: {
82
- id: string;
83
+ id: bigint;
83
84
  channelName: string;
84
85
  name: string | null;
85
86
  content: string;
86
- dequeueId: string;
87
+ dequeueNonce: string;
87
88
  state: string | null;
88
89
  numAttempts: number;
89
- lockMs: number;
90
90
  };
91
91
  };
92
92
  export type MessageDequeueCommandResultMessageNotAvailable = {
@@ -113,12 +113,12 @@ export type MessageDeleteCommandResultMessageDeleted = {
113
113
  export type MessageDeleteCommandResult = MessageDeleteCommandResultMessageNotFound | MessageDeleteCommandResultStateInvalid | MessageDeleteCommandResultMessageDeleted;
114
114
  export declare class MessageDeleteCommand {
115
115
  readonly schema: string;
116
- readonly id: string;
117
- readonly dequeueId: string;
116
+ readonly id: bigint;
117
+ readonly dequeueNonce: string;
118
118
  constructor(params: {
119
119
  schema: string;
120
- id: string;
121
- dequeueId: string;
120
+ id: bigint;
121
+ dequeueNonce: string;
122
122
  });
123
123
  execute(databaseClient: DatabaseClient): Promise<MessageDeleteCommandResult>;
124
124
  }
@@ -134,17 +134,16 @@ export type MessageDeferCommandResultMessageDeferred = {
134
134
  export type MessageDeferCommandResult = MessageDeferCommandResultMessageNotFound | MessageDeferCommandResultStateInvalid | MessageDeferCommandResultMessageDeferred;
135
135
  export declare class MessageDeferCommand {
136
136
  readonly schema: string;
137
- readonly id: string;
138
- readonly dequeueId: string;
137
+ readonly id: bigint;
138
+ readonly dequeueNonce: string;
139
139
  readonly delayMs: number;
140
140
  readonly state: string | null;
141
141
  constructor(params: {
142
142
  schema: string;
143
- id: string;
144
- dequeueId: string;
143
+ id: bigint;
144
+ dequeueNonce: string;
145
145
  delayMs?: number;
146
146
  state?: string | null;
147
- priority?: boolean;
148
147
  });
149
148
  execute(databaseClient: DatabaseClient): Promise<MessageDeferCommandResult>;
150
149
  }
@@ -162,7 +161,6 @@ export declare class QueueBatchChannelMessage {
162
161
  lockMs?: number;
163
162
  content: string;
164
163
  delayMs?: number;
165
- priority?: boolean;
166
164
  }): Deferred<MessageCreateCommandResult>;
167
165
  }
168
166
  export declare class QueueBatchChannelPolicy {
@@ -175,8 +173,9 @@ export declare class QueueBatchChannelPolicy {
175
173
  registerFn: BatchedCommandRegisterFn;
176
174
  });
177
175
  set(params: {
178
- maxConcurrency: number | null;
179
- maxSize: number | null;
176
+ maxConcurrency?: number | null;
177
+ maxSize?: number | null;
178
+ releaseIntervalMs?: number | null;
180
179
  }): Deferred<void>;
181
180
  clear(): Deferred<void>;
182
181
  }
@@ -218,7 +217,6 @@ export declare class QueueChannelMessage {
218
217
  lockMs?: number;
219
218
  content: string;
220
219
  delayMs?: number;
221
- priority?: boolean;
222
220
  }): Promise<MessageCreateCommandResult>;
223
221
  }
224
222
  export declare class QueueChannelPolicy {
@@ -230,8 +228,9 @@ export declare class QueueChannelPolicy {
230
228
  });
231
229
  set(params: {
232
230
  databaseClient: DatabaseClient;
233
- maxConcurrency: number | null;
234
- maxSize: number | null;
231
+ maxConcurrency?: number | null;
232
+ maxSize?: number | null;
233
+ releaseIntervalMs?: number | null;
235
234
  }): Promise<void>;
236
235
  clear(params: {
237
236
  databaseClient: DatabaseClient;
@@ -247,29 +246,26 @@ export declare class QueueChannel {
247
246
  }
248
247
  export declare class QueueMessage {
249
248
  private readonly schema;
250
- readonly id: string;
249
+ readonly id: bigint;
251
250
  readonly channelName: string;
252
251
  readonly name: string | null;
253
252
  readonly content: string;
254
- readonly dequeueId: string;
253
+ readonly dequeueNonce: string;
255
254
  readonly state: string | null;
256
255
  readonly numAttempts: number;
257
- readonly lockMs: number;
258
256
  constructor(params: {
259
257
  schema: string;
260
- id: string;
261
- dequeueId: string;
258
+ id: bigint;
259
+ dequeueNonce: string;
262
260
  channelName: string;
263
261
  name: string | null;
264
262
  content: string;
265
263
  state: string | null;
266
264
  numAttempts: number;
267
- lockMs: number;
268
265
  });
269
266
  defer(params: {
270
267
  databaseClient: DatabaseClient;
271
268
  delayMs?: number;
272
- priority?: boolean;
273
269
  state?: string;
274
270
  }): Promise<MessageDeferCommandResult>;
275
271
  delete(params: {
package/dist/index.js CHANGED
@@ -1,20 +1,21 @@
1
- var s=(e)=>({nodeType:"VALUE",value:e}),n=(e)=>({nodeType:"REF",value:e}),J=(e)=>({nodeType:"RAW",value:e}),V=(e)=>{return`'${e.replace(/'/g,"''")}'`},X=(e)=>{if(e===null)return"NULL";else if(typeof e==="string")return V(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 throw new Error(`Unsupported value type: ${typeof e}`)},K=(e)=>{return`"${e.replace(/"/g,'""')}"`},Q=(e)=>{if(e.nodeType==="VALUE")return X(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 a=(e,...t)=>{let _=[];for(let c=0;c<e.length;c+=1)if(_.push(e[c]),c<t.length)_.push(Q(t[c]));return J(_.join(""))};class E{schema;channelName;createdAt;constructor(e){this.schema=e.schema,this.channelName=e.channelName,this.createdAt=new Date}sortKeyGet(){return JSON.stringify([this.channelName,null,this.createdAt.toISOString()])}async execute(e){await e.query(a`
1
+ var s=(e)=>({nodeType:"VALUE",value:e}),n=(e)=>({nodeType:"REF",value:e}),w=(e)=>({nodeType:"RAW",value:e}),W=(e)=>{return`'${e.replace(/'/g,"''")}'`},J=(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}`)},X=(e)=>{return`"${e.replace(/"/g,'""')}"`},V=(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 a=(e,...t)=>{let _=[];for(let c=0;c<e.length;c+=1)if(_.push(e[c]),c<t.length)_.push(V(t[c]));return w(_.join(""))};class r{schema;channelName;createdAt;constructor(e){this.schema=e.schema,this.channelName=e.channelName,this.createdAt=new Date}sortKeyGet(){return JSON.stringify([this.channelName,null,this.createdAt.toISOString()])}async execute(e){await e.query(a`
2
2
  SELECT 1 FROM ${n(this.schema)}."channel_policy_clear"(
3
3
  ${s(this.channelName)}
4
4
  )
5
- `.value)}}class o{schema;channelName;maxSize;maxConcurrency;createdAt;constructor(e){this.schema=e.schema,this.channelName=e.channelName,this.maxConcurrency=e.maxConcurrency===null?null:Math.max(0,e.maxConcurrency),this.maxSize=e.maxSize===null?null:Math.max(0,e.maxSize),this.createdAt=new Date}sortKeyGet(){return JSON.stringify([this.channelName,null,this.createdAt.toISOString()])}async execute(e){await e.query(a`
5
+ `.value)}}class o{schema;channelName;maxSize;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 _=e.maxSize??null;this.maxSize=_!==null?Math.max(0,_):null;let c=e.releaseIntervalMs??null;this.releaseIntervalMs=c!==null?Math.max(0,c):null,this.createdAt=new Date}sortKeyGet(){return JSON.stringify([this.channelName,null,this.createdAt.toISOString()])}async execute(e){await e.query(a`
6
6
  SELECT 1 FROM ${n(this.schema)}."channel_policy_set"(
7
7
  ${s(this.channelName)},
8
- ${s(this.maxSize)}::BIGINT,
9
- ${s(this.maxConcurrency)}::BIGINT
8
+ ${s(this.maxSize)}::INTEGER,
9
+ ${s(this.maxConcurrency)}::INTEGER,
10
+ ${s(this.releaseIntervalMs)}::INTEGER
10
11
  )
11
- `.value)}}var g=(e)=>{return e*1000},Y=(e)=>{return g(e*60)},R=(e)=>{return Y(e*60)},v=(e)=>{return R(e*24)};import{createHash as j}from"node:crypto";class f{value;constructor(e){this.value=e}toString(e){return j("sha256").update(e).update(this.value).digest("base64").replace(/=/g,"")}}var l=new f("WAKE"),d=v(1000),p=!1,h=g(0),q=R(1);import{dirname as C}from"path";var __filename="/home/runner/work/lonnymq/lonnymq/src/core/path.ts",Z=C(C(__filename)),ee=new RegExp(`^${Z}/`),r=(e)=>{return e.replace(ee,"")};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/04-function-message-create.ts";var $={name:r(__filename),sql:(e)=>{return[a`
12
+ `.value)}}var R=(e)=>{return e*1000},K=(e)=>{return R(e*60)},y=(e)=>{return K(e*60)};import{createHash as Q}from"node:crypto";class x{value;constructor(e){this.value=e}toString(e){return Q("sha256").update(e).update(this.value).digest("base64").replace(/=/g,"")}}var u=new x("WAKE"),U=!1,h=R(0),f=y(1);import{dirname as p}from"path";var __filename="/home/runner/work/lonnymq/lonnymq/src/core/path.ts",Y=p(p(__filename)),j=new RegExp(`^${Y}/`),E=(e)=>{return e.replace(j,"")};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/04-function-message-create.ts";var q={name:E(__filename),sql:(e)=>{return[a`
12
13
  CREATE FUNCTION ${n(e.schema)}."message_create" (
13
14
  p_channel_name TEXT,
14
15
  p_name TEXT,
15
16
  p_content TEXT,
16
- p_lock_ms BIGINT,
17
- p_delay_ms BIGINT
17
+ p_lock_ms INTEGER,
18
+ p_delay_ms INTEGER
18
19
  ) RETURNS JSONB AS $$
19
20
  DECLARE
20
21
  v_now TIMESTAMP;
@@ -23,11 +24,12 @@ var s=(e)=>({nodeType:"VALUE",value:e}),n=(e)=>({nodeType:"REF",value:e}),J=(e)=
23
24
  v_message RECORD;
24
25
  v_message_next_dequeue_after TIMESTAMP;
25
26
  BEGIN
26
- v_now := NOW() + INTERVAL '1 MILLISECOND' * p_delay_ms;
27
+ v_now := NOW();
27
28
 
28
29
  SELECT
29
30
  "max_size",
30
- "max_concurrency"
31
+ "max_concurrency",
32
+ "release_interval_ms"
31
33
  FROM ${n(e.schema)}."channel_policy"
32
34
  WHERE "name" = p_channel_name
33
35
  FOR SHARE
@@ -39,6 +41,7 @@ var s=(e)=>({nodeType:"VALUE",value:e}),n=(e)=>({nodeType:"REF",value:e}),J=(e)=
39
41
  "current_concurrency",
40
42
  "max_size",
41
43
  "max_concurrency",
44
+ "release_interval_ms",
42
45
  "message_next_id",
43
46
  "message_next_dequeue_after"
44
47
  ) VALUES (
@@ -47,6 +50,7 @@ var s=(e)=>({nodeType:"VALUE",value:e}),n=(e)=>({nodeType:"REF",value:e}),J=(e)=
47
50
  0,
48
51
  v_channel_policy."max_size",
49
52
  v_channel_policy."max_concurrency",
53
+ v_channel_policy."release_interval_ms",
50
54
  NULL,
51
55
  NULL
52
56
  ) ON CONFLICT ("name")
@@ -82,7 +86,8 @@ var s=(e)=>({nodeType:"VALUE",value:e}),n=(e)=>({nodeType:"REF",value:e}),J=(e)=
82
86
  ) ON CONFLICT ("channel_name", "name")
83
87
  WHERE "num_attempts" = 0
84
88
  DO UPDATE SET
85
- "id" = EXCLUDED."id"
89
+ "channel_name" = EXCLUDED."channel_name",
90
+ "name" = EXCLUDED."name"
86
91
  RETURNING
87
92
  "id",
88
93
  "xmax",
@@ -103,7 +108,7 @@ var s=(e)=>({nodeType:"VALUE",value:e}),n=(e)=>({nodeType:"REF",value:e}),J=(e)=
103
108
  UPDATE ${n(e.schema)}."channel_state" SET
104
109
  "current_size" = v_channel_state."current_size" + 1,
105
110
  "message_next_id" = v_message."id",
106
- "message_next_dequeue_after" = v_message."dequeue_after"
111
+ "message_next_dequeue_after" = GREATEST(v_now, v_message."dequeue_after")
107
112
  WHERE "id" = v_channel_state."id";
108
113
  ELSE
109
114
  UPDATE ${n(e.schema)}."channel_state" SET
@@ -119,20 +124,20 @@ var s=(e)=>({nodeType:"VALUE",value:e}),n=(e)=>({nodeType:"REF",value:e}),J=(e)=
119
124
  );
120
125
  END;
121
126
  $$ LANGUAGE plpgsql;
122
- `]}};class i{schema;channelName;name;content;lockMs;delayMs;createdAt;constructor(e){let t=e.name??null,_=e.lockMs===void 0?q:Math.max(0,e.lockMs),c;if(e.priority)c=-d;else c=e.delayMs===void 0?h:Math.max(0,e.delayMs);this.schema=e.schema,this.channelName=e.channelName,this.content=e.content,this.name=t,this.lockMs=_,this.delayMs=c,this.createdAt=new Date}async execute(e){let t=await e.query(a`
127
+ `]}};class m{schema;channelName;name;content;lockMs;delayMs;createdAt;constructor(e){let t=e.name??null,_=e.lockMs===void 0?f:Math.max(0,e.lockMs),c=e.delayMs===void 0?h:e.delayMs;this.schema=e.schema,this.channelName=e.channelName,this.content=e.content,this.name=t,this.lockMs=_,this.delayMs=c,this.createdAt=new Date}async execute(e){let t=await e.query(a`
123
128
  SELECT ${n(this.schema)}."message_create"(
124
129
  ${s(this.channelName)},
125
130
  ${s(this.name)},
126
131
  ${s(this.content)},
127
- ${s(this.lockMs)}::BIGINT,
128
- ${s(this.delayMs)}::BIGINT
132
+ ${s(this.lockMs)}::INTEGER,
133
+ ${s(this.delayMs)}::INTEGER
129
134
  ) AS "result"
130
- `.value).then((_)=>_.rows[0].result);if(t.result_code===1)return{resultType:"MESSAGE_DROPPED"};else if(t.result_code===2)return{resultType:"MESSAGE_DEDUPLICATED",id:t.id};else if(t.result_code===0)return{resultType:"MESSAGE_CREATED",id:t.id};else throw new Error("Unexpected result")}}var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/05-function-message-dequeue.ts";var F={name:r(__filename),sql:(e)=>{return[a`
135
+ `.value).then((_)=>_.rows[0].result);if(t.result_code===1)return{resultType:"MESSAGE_DROPPED"};else if(t.result_code===2)return{resultType:"MESSAGE_DEDUPLICATED",id:BigInt(t.id)};else if(t.result_code===0)return{resultType:"MESSAGE_CREATED",id:BigInt(t.id)};else throw new Error("Unexpected result")}}var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/05-function-message-dequeue.ts";var C={name:E(__filename),sql:(e)=>{return[a`
131
136
  CREATE FUNCTION ${n(e.schema)}."message_dequeue" ()
132
137
  RETURNS JSONB AS $$
133
138
  DECLARE
134
139
  v_now TIMESTAMP;
135
- v_dequeue_id UUID;
140
+ v_dequeue_nonce UUID;
136
141
  v_channel_state RECORD;
137
142
  v_message_locked RECORD;
138
143
  v_retry_after TIMESTAMP;
@@ -141,7 +146,7 @@ var s=(e)=>({nodeType:"VALUE",value:e}),n=(e)=>({nodeType:"REF",value:e}),J=(e)=
141
146
  v_message_next_dequeue_after TIMESTAMP;
142
147
  BEGIN
143
148
  v_now := NOW();
144
- v_dequeue_id := GEN_RANDOM_UUID();
149
+ v_dequeue_nonce := GEN_RANDOM_UUID();
145
150
 
146
151
  SELECT
147
152
  "id",
@@ -164,7 +169,7 @@ var s=(e)=>({nodeType:"VALUE",value:e}),n=(e)=>({nodeType:"REF",value:e}),J=(e)=
164
169
  UPDATE ${n(e.schema)}."message" SET
165
170
  "num_attempts" = v_message_locked."num_attempts" + 1,
166
171
  "dequeue_after" = v_now + (v_message_locked."lock_ms" * INTERVAL '1 millisecond'),
167
- "dequeue_id" = v_dequeue_id
172
+ "dequeue_nonce" = v_dequeue_nonce
168
173
  WHERE "id" = v_message_locked."id";
169
174
 
170
175
  PERFORM ${n(e.schema)}."wake"(v_message_locked."lock_ms");
@@ -175,7 +180,7 @@ var s=(e)=>({nodeType:"VALUE",value:e}),n=(e)=>({nodeType:"REF",value:e}),J=(e)=
175
180
  'channel_name', v_message_locked.channel_name,
176
181
  'state', v_message_locked.state,
177
182
  'name', v_message_locked.name,
178
- 'dequeue_id', v_dequeue_id,
183
+ 'dequeue_nonce', v_dequeue_nonce,
179
184
  'content', v_message_locked.content,
180
185
  'num_attempts', v_message_locked.num_attempts
181
186
  );
@@ -184,6 +189,7 @@ var s=(e)=>({nodeType:"VALUE",value:e}),n=(e)=>({nodeType:"REF",value:e}),J=(e)=
184
189
  SELECT
185
190
  "id",
186
191
  "name",
192
+ "release_interval_ms",
187
193
  "message_next_id",
188
194
  "message_next_dequeue_after",
189
195
  "current_concurrency"
@@ -223,7 +229,7 @@ var s=(e)=>({nodeType:"VALUE",value:e}),n=(e)=>({nodeType:"REF",value:e}),J=(e)=
223
229
  UPDATE ${n(e.schema)}."message" SET
224
230
  "is_locked" = TRUE,
225
231
  "num_attempts" = v_message_dequeue."num_attempts" + 1,
226
- "dequeue_id" = v_dequeue_id,
232
+ "dequeue_nonce" = v_dequeue_nonce,
227
233
  "dequeue_after" = v_now + (v_message_dequeue."lock_ms" * INTERVAL '1 millisecond')
228
234
  WHERE "id" = v_message_dequeue."id";
229
235
 
@@ -242,7 +248,7 @@ var s=(e)=>({nodeType:"VALUE",value:e}),n=(e)=>({nodeType:"REF",value:e}),J=(e)=
242
248
  IF v_message_next_dequeue."id" IS NOT NULL THEN
243
249
  v_message_next_dequeue_after := GREATEST(
244
250
  v_message_next_dequeue."dequeue_after",
245
- v_now
251
+ v_now + (COALESCE(v_channel_state."release_interval_ms", 0) * INTERVAL '1 millisecond')
246
252
  );
247
253
 
248
254
  UPDATE ${n(e.schema)}."channel_state" SET
@@ -263,7 +269,7 @@ var s=(e)=>({nodeType:"VALUE",value:e}),n=(e)=>({nodeType:"REF",value:e}),J=(e)=
263
269
  'id', v_message_dequeue.id,
264
270
  'channel_name', v_message_dequeue.channel_name,
265
271
  'state', v_message_dequeue.state,
266
- 'dequeue_id', v_dequeue_id,
272
+ 'dequeue_nonce', v_dequeue_nonce,
267
273
  'name', v_message_dequeue.name,
268
274
  'content', v_message_dequeue.content,
269
275
  'num_attempts', v_message_dequeue.num_attempts
@@ -272,20 +278,21 @@ var s=(e)=>({nodeType:"VALUE",value:e}),n=(e)=>({nodeType:"REF",value:e}),J=(e)=
272
278
  $$ LANGUAGE plpgsql;
273
279
  `]}};class N{schema;constructor(e){this.schema=e.schema}async execute(e){let t=await e.query(a`
274
280
  SELECT ${n(this.schema)}."message_dequeue"() AS "result"
275
- `.value).then((_)=>_.rows[0].result);if(t.result_code===0)return{resultType:"MESSAGE_NOT_AVAILABLE",retryMs:t.retry_ms};else if(t.result_code===1)return{resultType:"MESSAGE_DEQUEUED",message:{id:t.id,channelName:t.channel_name,name:t.name,content:t.content,dequeueId:t.dequeue_id,state:t.state,numAttempts:t.num_attempts,lockMs:t.lock_ms}};else throw new Error("Unexpected dequeue result")}}var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/06-function-message-delete.ts";var M={name:r(__filename),sql:(e)=>{return[a`
281
+ `.value).then((_)=>_.rows[0].result);if(t.result_code===0)return{resultType:"MESSAGE_NOT_AVAILABLE",retryMs:t.retry_ms};else if(t.result_code===1)return{resultType:"MESSAGE_DEQUEUED",message:{id:BigInt(t.id),channelName:t.channel_name,name:t.name,content:t.content,dequeueNonce:t.dequeue_nonce,state:t.state,numAttempts:t.num_attempts}};else throw new Error("Unexpected dequeue result")}}var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/06-function-message-delete.ts";var $={name:E(__filename),sql:(e)=>{return[a`
276
282
  CREATE FUNCTION ${n(e.schema)}."message_delete" (
277
- p_id UUID,
278
- p_dequeue_id UUID
283
+ p_id BIGINT,
284
+ p_dequeue_nonce UUID
279
285
  )
280
286
  RETURNS JSONB AS $$
281
287
  DECLARE
288
+ v_channel_policy RECORD;
282
289
  v_channel_state RECORD;
283
290
  v_message RECORD;
284
291
  BEGIN
285
292
  SELECT
286
293
  "id",
287
294
  "channel_name",
288
- "dequeue_id"
295
+ "dequeue_nonce"
289
296
  FROM ${n(e.schema)}."message"
290
297
  WHERE "id" = p_id
291
298
  FOR UPDATE
@@ -295,12 +302,19 @@ var s=(e)=>({nodeType:"VALUE",value:e}),n=(e)=>({nodeType:"REF",value:e}),J=(e)=
295
302
  RETURN JSONB_BUILD_OBJECT(
296
303
  'result_code', ${s(0)}
297
304
  );
298
- ELSEIF v_message."dequeue_id" != p_dequeue_id THEN
305
+ ELSEIF v_message."dequeue_nonce" != p_dequeue_nonce THEN
299
306
  RETURN JSONB_BUILD_OBJECT(
300
307
  'result_code', ${s(1)}
301
308
  );
302
309
  END IF;
303
310
 
311
+ SELECT
312
+ "id"
313
+ FROM ${n(e.schema)}."channel_policy"
314
+ WHERE "name" = v_message."channel_name"
315
+ FOR SHARE
316
+ INTO v_channel_policy;
317
+
304
318
  SELECT
305
319
  "id",
306
320
  "current_size",
@@ -310,7 +324,7 @@ var s=(e)=>({nodeType:"VALUE",value:e}),n=(e)=>({nodeType:"REF",value:e}),J=(e)=
310
324
  FOR UPDATE
311
325
  INTO v_channel_state;
312
326
 
313
- IF v_channel_state."current_size" = 1 THEN
327
+ IF v_channel_policy."id" IS NULL AND v_channel_state."current_size" = 1 THEN
314
328
  DELETE FROM ${n(e.schema)}."channel_state"
315
329
  WHERE "id" = v_channel_state."id";
316
330
  ELSE
@@ -328,15 +342,15 @@ var s=(e)=>({nodeType:"VALUE",value:e}),n=(e)=>({nodeType:"REF",value:e}),J=(e)=
328
342
  );
329
343
  END;
330
344
  $$ LANGUAGE plpgsql;
331
- `]}};class T{schema;id;dequeueId;constructor(e){this.schema=e.schema,this.id=e.id,this.dequeueId=e.dequeueId}async execute(e){let t=await e.query(a`
345
+ `]}};class T{schema;id;dequeueNonce;constructor(e){this.schema=e.schema,this.id=e.id,this.dequeueNonce=e.dequeueNonce}async execute(e){let t=await e.query(a`
332
346
  SELECT ${n(this.schema)}."message_delete"( ${s(this.id)},
333
- ${s(this.dequeueId)}
347
+ ${s(this.dequeueNonce)}
334
348
  ) AS "result"
335
- `.value).then((_)=>_.rows[0].result);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")}}var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/07-function-message-defer.ts";var B={name:r(__filename),sql:(e)=>{return[a`
349
+ `.value).then((_)=>_.rows[0].result);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")}}var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/07-function-message-defer.ts";var F={name:E(__filename),sql:(e)=>{return[a`
336
350
  CREATE FUNCTION ${n(e.schema)}."message_defer" (
337
- p_id UUID,
338
- p_dequeue_id UUID,
339
- p_delay_ms BIGINT,
351
+ p_id BIGINT,
352
+ p_dequeue_nonce UUID,
353
+ p_delay_ms INTEGER,
340
354
  p_state TEXT
341
355
  )
342
356
  RETURNS JSONB AS $$
@@ -348,7 +362,7 @@ var s=(e)=>({nodeType:"VALUE",value:e}),n=(e)=>({nodeType:"REF",value:e}),J=(e)=
348
362
  SELECT
349
363
  "id",
350
364
  "channel_name",
351
- "dequeue_id"
365
+ "dequeue_nonce"
352
366
  FROM ${n(e.schema)}."message"
353
367
  WHERE "id" = p_id
354
368
  FOR UPDATE
@@ -358,7 +372,7 @@ var s=(e)=>({nodeType:"VALUE",value:e}),n=(e)=>({nodeType:"REF",value:e}),J=(e)=
358
372
  RETURN JSONB_BUILD_OBJECT(
359
373
  'result_code', ${s(0)}
360
374
  );
361
- ELSEIF v_message."dequeue_id" != p_dequeue_id THEN
375
+ ELSEIF v_message."dequeue_nonce" != p_dequeue_nonce THEN
362
376
  RETURN JSONB_BUILD_OBJECT(
363
377
  'result_code', ${s(1)}
364
378
  );
@@ -403,35 +417,37 @@ var s=(e)=>({nodeType:"VALUE",value:e}),n=(e)=>({nodeType:"REF",value:e}),J=(e)=
403
417
  );
404
418
  END;
405
419
  $$ LANGUAGE plpgsql;
406
- `]}};class S{schema;id;dequeueId;delayMs;state;constructor(e){let t;if(e.priority)t=-d;else t=e.delayMs===void 0?h:Math.max(0,e.delayMs);this.schema=e.schema,this.id=e.id,this.dequeueId=e.dequeueId,this.delayMs=t,this.state=e.state??null}async execute(e){let t=await e.query(a`
420
+ `]}};class d{schema;id;dequeueNonce;delayMs;state;constructor(e){let t=e.delayMs===void 0?h:e.delayMs;this.schema=e.schema,this.id=e.id,this.dequeueNonce=e.dequeueNonce,this.delayMs=t,this.state=e.state??null}async execute(e){let t=await e.query(a`
407
421
  SELECT ${n(this.schema)}."message_defer"(
408
422
  ${s(this.id)},
409
- ${s(this.dequeueId)},
410
- ${s(this.delayMs)}::BIGINT,
423
+ ${s(this.dequeueNonce)},
424
+ ${s(this.delayMs)},
411
425
  ${s(this.state)}
412
426
  ) AS "result"
413
- `.value).then((_)=>_.rows[0].result);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_DEFERRED"};else throw new Error("Unexpected result")}}var G=(e)=>{let t=e.split(`
414
- `),_=Number.MAX_SAFE_INTEGER;for(let c of t){if(c.trim().length===0)continue;let u=c.search(/\S/);_=Math.min(_,u)}return t.map((c)=>c.slice(_)).join(`
415
- `).trim()};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/00-table-channel-policy.ts",P={name:r(__filename),sql:(e)=>{return[a`
427
+ `.value).then((_)=>_.rows[0].result);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_DEFERRED"};else throw new Error("Unexpected result")}}var M=(e)=>{let t=e.split(`
428
+ `),_=Number.MAX_SAFE_INTEGER;for(let c of t){if(c.trim().length===0)continue;let l=c.search(/\S/);_=Math.min(_,l)}return t.map((c)=>c.slice(_)).join(`
429
+ `).trim()};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/00-table-channel-policy.ts",G={name:E(__filename),sql:(e)=>{return[a`
416
430
  CREATE TABLE ${n(e.schema)}."channel_policy" (
417
- "id" UUID NOT NULL DEFAULT GEN_RANDOM_UUID(),
431
+ "id" BIGSERIAL NOT NULL,
418
432
  "name" TEXT NOT NULL,
419
- "max_size" BIGINT,
420
- "max_concurrency" BIGINT,
433
+ "max_size" INTEGER,
434
+ "max_concurrency" INTEGER,
435
+ "release_interval_ms" INTEGER,
421
436
  PRIMARY KEY ("id")
422
437
  );
423
438
  `,a`
424
439
  CREATE UNIQUE INDEX "channel_policy_name_ux"
425
440
  ON ${n(e.schema)}."channel_policy" ("name");
426
- `]}};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/01-table-channel-state.ts",k={name:r(__filename),sql:(e)=>{return[a`
441
+ `]}};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/01-table-channel-state.ts",B={name:E(__filename),sql:(e)=>{return[a`
427
442
  CREATE TABLE ${n(e.schema)}."channel_state" (
428
- "id" UUID NOT NULL DEFAULT GEN_RANDOM_UUID(),
443
+ "id" BIGSERIAL NOT NULL,
429
444
  "name" TEXT NOT NULL,
430
- "max_size" BIGINT,
431
- "max_concurrency" BIGINT,
432
- "current_size" BIGINT NOT NULL,
433
- "current_concurrency" BIGINT NOT NULL,
434
- "message_next_id" UUID,
445
+ "max_size" INTEGER,
446
+ "max_concurrency" INTEGER,
447
+ "release_interval_ms" INTEGER,
448
+ "current_size" INTEGER NOT NULL,
449
+ "current_concurrency" INTEGER NOT NULL,
450
+ "message_next_id" BIGINT,
435
451
  "message_next_dequeue_after" TIMESTAMP,
436
452
  PRIMARY KEY ("id")
437
453
  );
@@ -444,19 +460,18 @@ var s=(e)=>({nodeType:"VALUE",value:e}),n=(e)=>({nodeType:"REF",value:e}),J=(e)=
444
460
  "message_next_dequeue_after" ASC
445
461
  ) WHERE "message_next_id" IS NOT NULL
446
462
  AND ("max_concurrency" IS NULL OR "current_concurrency" < "max_concurrency");
447
- `]}};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/02-table-message.ts",b={name:r(__filename),sql:(e)=>{return[a`
463
+ `]}};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/02-table-message.ts",P={name:E(__filename),sql:(e)=>{return[a`
448
464
  CREATE TABLE ${n(e.schema)}."message" (
449
- "id" UUID NOT NULL DEFAULT GEN_RANDOM_UUID(),
465
+ "id" BIGSERIAL NOT NULL,
450
466
  "channel_name" TEXT NOT NULL,
451
- "dequeue_id" UUID,
467
+ "dequeue_nonce" UUID,
452
468
  "name" TEXT,
453
469
  "content" TEXT NOT NULL,
454
470
  "state" TEXT,
455
- "lock_ms" BIGINT NOT NULL,
471
+ "lock_ms" INTEGER NOT NULL,
456
472
  "is_locked" BOOLEAN NOT NULL DEFAULT FALSE,
457
- "num_attempts" BIGINT NOT NULL DEFAULT 0,
473
+ "num_attempts" INTEGER NOT NULL DEFAULT 0,
458
474
  "dequeue_after" TIMESTAMP NOT NULL,
459
- "sweep_after" TIMESTAMP,
460
475
  PRIMARY KEY ("id")
461
476
  );
462
477
  `,a`
@@ -476,9 +491,9 @@ var s=(e)=>({nodeType:"VALUE",value:e}),n=(e)=>({nodeType:"REF",value:e}),J=(e)=
476
491
  ON ${n(e.schema)}."message" (
477
492
  "dequeue_after" ASC
478
493
  ) WHERE "is_locked";
479
- `]}};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/03-function-wake.ts",H={name:r(__filename),sql:(e)=>{let t=l.toString(e.schema);return[a`
494
+ `]}};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/03-function-wake.ts",H={name:E(__filename),sql:(e)=>{let t=u.toString(e.schema);return[a`
480
495
  CREATE FUNCTION ${n(e.schema)}."wake" (
481
- p_delay_ms BIGINT
496
+ p_delay_ms INTEGER
482
497
  ) RETURNS VOID AS $$
483
498
  BEGIN
484
499
  IF ${s(e.useWake)} THEN
@@ -486,47 +501,68 @@ var s=(e)=>({nodeType:"VALUE",value:e}),n=(e)=>({nodeType:"REF",value:e}),J=(e)=
486
501
  END IF;
487
502
  END;
488
503
  $$ LANGUAGE plpgsql;
489
- `]}};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/08-function-channel-policy-set.ts",z={name:r(__filename),sql:(e)=>{return[a`
504
+ `]}};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/08-function-channel-policy-set.ts",b={name:E(__filename),sql:(e)=>{return[a`
490
505
  CREATE FUNCTION ${n(e.schema)}."channel_policy_set" (
491
506
  p_name TEXT,
492
- p_max_size BIGINT,
493
- p_max_concurrency BIGINT
507
+ p_max_size INTEGER,
508
+ p_max_concurrency INTEGER,
509
+ p_release_interval_ms INTEGER
494
510
  ) RETURNS VOID AS $$
495
511
  BEGIN
496
512
  INSERT INTO ${n(e.schema)}."channel_policy" (
497
513
  "name",
498
514
  "max_size",
499
- "max_concurrency"
515
+ "max_concurrency",
516
+ "release_interval_ms"
500
517
  ) VALUES (
501
518
  p_name,
502
519
  p_max_size,
503
- p_max_concurrency
520
+ p_max_concurrency,
521
+ p_release_interval_ms
504
522
  ) ON CONFLICT ("name") DO UPDATE SET
505
523
  "max_size" = EXCLUDED."max_size",
506
- "max_concurrency" = EXCLUDED."max_concurrency";
524
+ "max_concurrency" = EXCLUDED."max_concurrency",
525
+ "release_interval_ms" = EXCLUDED."release_interval_ms";
507
526
 
508
527
  UPDATE ${n(e.schema)}."channel_state" SET
509
528
  "max_size" = p_max_size,
510
- "max_concurrency" = p_max_concurrency
529
+ "max_concurrency" = p_max_concurrency,
530
+ "release_interval_ms" = p_release_interval_ms
511
531
  WHERE "name" = p_name;
512
532
 
513
533
  PERFORM ${n(e.schema)}."wake"(0);
514
534
  END;
515
535
  $$ LANGUAGE plpgsql;
516
- `]}};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/09-function-channel-policy-clear.ts",w={name:r(__filename),sql:(e)=>{return[a`
536
+ `]}};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/09-function-channel-policy-clear.ts",k={name:E(__filename),sql:(e)=>{return[a`
517
537
  CREATE FUNCTION ${n(e.schema)}."channel_policy_clear" (
518
538
  p_name TEXT
519
539
  ) RETURNS VOID AS $$
540
+ DECLARE
541
+ v_channel_state RECORD;
520
542
  BEGIN
521
543
  DELETE FROM ${n(e.schema)}."channel_policy"
522
544
  WHERE "name" = p_name;
523
545
 
524
- UPDATE ${n(e.schema)}."channel_state" SET
525
- "max_size" = NULL,
526
- "max_concurrency" = NULL
527
- WHERE "name" = p_name;
546
+ SELECT
547
+ "id",
548
+ "current_size"
549
+ FROM ${n(e.schema)}."channel_state"
550
+ WHERE "name" = p_name
551
+ FOR UPDATE
552
+ INTO v_channel_state;
553
+
554
+ IF v_channel_state."current_size" = 0 THEN
555
+ DELETE FROM ${n(e.schema)}."channel_state"
556
+ WHERE "id" = v_channel_state."id";
557
+ ELSE
558
+ UPDATE ${n(e.schema)}."channel_state" SET
559
+ "max_size" = NULL,
560
+ "max_concurrency" = NULL,
561
+ "release_interval_ms" = NULL
562
+ WHERE "name" = p_name;
563
+ END IF;
528
564
 
529
565
  PERFORM ${n(e.schema)}."wake"(0);
530
566
  END;
531
567
  $$ LANGUAGE plpgsql;
532
- `]}};class m{value;isSet;constructor(){this.isSet=!1,this.value=null}get(){return this.isSet?{resultType:"RESULT_SET",value:this.value}:{resultType:"RESULT_NOT_SET"}}set(e){this.isSet=!0,this.value=e}}class I{schema;channelName;registerFn;constructor(e){this.schema=e.schema,this.channelName=e.channelName,this.registerFn=e.registerFn}create(e){let t=new i({schema:this.schema,channelName:this.channelName,name:e.name,content:e.content,lockMs:e.lockMs,delayMs:e.delayMs,priority:e.priority}),_=new m;return this.registerFn({sortKey:JSON.stringify([t.channelName,t.name,t.createdAt.toISOString()]),execute:(c)=>t.execute(c).then((u)=>_.set(u))}),_}}class A{schema;channelName;registerFn;constructor(e){this.schema=e.schema,this.channelName=e.channelName,this.registerFn=e.registerFn}set(e){let t=new o({schema:this.schema,channelName:this.channelName,maxConcurrency:e.maxConcurrency,maxSize:e.maxSize}),_=new m;return this.registerFn({sortKey:JSON.stringify([t.channelName,null,t.createdAt.toISOString()]),execute:(c)=>t.execute(c).then((u)=>_.set(u))}),_}clear(){let e=new E({schema:this.schema,channelName:this.channelName}),t=new m;return this.registerFn({sortKey:JSON.stringify([e.channelName,null,e.createdAt.toISOString()]),execute:(_)=>e.execute(_).then((c)=>t.set(c))}),t}}class O{policy;message;constructor(e){this.message=new I(e),this.policy=new A(e)}}var ne=(e,t)=>{return e.sortKey.localeCompare(t.sortKey)};class D{commands;schema;constructor(e){this.commands=[],this.schema=e.schema}channel(e){return new O({schema:this.schema,channelName:e,registerFn:(t)=>{this.commands.push(t)}})}async execute(e){for(let t of this.commands.sort(ne))await t.execute(e.databaseClient)}}class U{schema;channelName;constructor(e){this.schema=e.schema,this.channelName=e.channelName}async create(e){return new i({schema:this.schema,channelName:this.channelName,name:e.name,content:e.content,lockMs:e.lockMs,delayMs:e.delayMs,priority:e.priority}).execute(e.databaseClient)}}class y{schema;channelName;constructor(e){this.schema=e.schema,this.channelName=e.channelName}set(e){return new o({schema:this.schema,channelName:this.channelName,maxConcurrency:e.maxConcurrency,maxSize:e.maxSize}).execute(e.databaseClient)}clear(e){return new E({schema:this.schema,channelName:this.channelName}).execute(e.databaseClient)}}class L{policy;message;constructor(e){this.message=new U({schema:e.schema,channelName:e.channelName}),this.policy=new y({schema:e.schema,channelName:e.channelName})}}class x{schema;id;channelName;name;content;dequeueId;state;numAttempts;lockMs;constructor(e){this.schema=e.schema,this.id=e.id,this.channelName=e.channelName,this.dequeueId=e.dequeueId,this.name=e.name,this.content=e.content,this.state=e.state,this.numAttempts=e.numAttempts,this.lockMs=e.lockMs}async defer(e){return new S({schema:this.schema,id:this.id,dequeueId:this.dequeueId,delayMs:e.delayMs,state:e.state,priority:e.priority}).execute(e.databaseClient)}async delete(e){return new T({schema:this.schema,id:this.id,dequeueId:this.dequeueId}).execute(e.databaseClient)}}class W{schema;constructor(e){this.schema=e}async dequeue(e){let _=await new N({schema:this.schema}).execute(e.databaseClient);if(_.resultType==="MESSAGE_DEQUEUED")return{resultType:"MESSAGE_DEQUEUED",message:new x({schema:this.schema,id:_.message.id,dequeueId:_.message.dequeueId,channelName:_.message.channelName,name:_.message.name,content:_.message.content,state:_.message.state,numAttempts:_.message.numAttempts,lockMs:_.message.lockMs})};else return _}channel(e){return new L({schema:this.schema,channelName:e})}batch(){return new D({schema:this.schema})}migrations(e){return[P,k,b,H,$,F,M,B,z,w].map((t)=>({name:t.name,sql:t.sql({schema:this.schema,useWake:e.useWake??p}).map((_)=>G(_.value))})).sort((t,_)=>t.name.localeCompare(_.name))}wakeChannel(){return l.toString(this.schema)}}export{W as Queue,N as MessageDequeueCommand,T as MessageDeleteCommand,S as MessageDeferCommand,i as MessageCreateCommand,o as ChannelPolicySetCommand,E as ChannelPolicyClearCommand};
568
+ `]}};class i{value;isSet;constructor(){this.isSet=!1,this.value=null}get(){return this.isSet?{resultType:"RESULT_SET",value:this.value}:{resultType:"RESULT_NOT_SET"}}set(e){this.isSet=!0,this.value=e}}class S{schema;channelName;registerFn;constructor(e){this.schema=e.schema,this.channelName=e.channelName,this.registerFn=e.registerFn}create(e){let t=new m({schema:this.schema,channelName:this.channelName,name:e.name,content:e.content,lockMs:e.lockMs,delayMs:e.delayMs}),_=new i;return this.registerFn({sortKey:JSON.stringify([t.channelName,t.name,t.createdAt.toISOString()]),execute:(c)=>t.execute(c).then((l)=>_.set(l))}),_}}class g{schema;channelName;registerFn;constructor(e){this.schema=e.schema,this.channelName=e.channelName,this.registerFn=e.registerFn}set(e){let t=new o({schema:this.schema,channelName:this.channelName,maxConcurrency:e.maxConcurrency,maxSize:e.maxSize,releaseIntervalMs:e.releaseIntervalMs}),_=new i;return this.registerFn({sortKey:JSON.stringify([t.channelName,null,t.createdAt.toISOString()]),execute:(c)=>t.execute(c).then((l)=>_.set(l))}),_}clear(){let e=new r({schema:this.schema,channelName:this.channelName}),t=new i;return this.registerFn({sortKey:JSON.stringify([e.channelName,null,e.createdAt.toISOString()]),execute:(_)=>e.execute(_).then((c)=>t.set(c))}),t}}class I{policy;message;constructor(e){this.message=new S(e),this.policy=new g(e)}}var Z=(e,t)=>{return e.sortKey.localeCompare(t.sortKey)};class O{commands;schema;constructor(e){this.commands=[],this.schema=e.schema}channel(e){return new I({schema:this.schema,channelName:e,registerFn:(t)=>{this.commands.push(t)}})}async execute(e){for(let t of this.commands.sort(Z))await t.execute(e.databaseClient)}}class A{schema;channelName;constructor(e){this.schema=e.schema,this.channelName=e.channelName}async create(e){return new m({schema:this.schema,channelName:this.channelName,name:e.name,content:e.content,lockMs:e.lockMs,delayMs:e.delayMs}).execute(e.databaseClient)}}class L{schema;channelName;constructor(e){this.schema=e.schema,this.channelName=e.channelName}set(e){return new o({schema:this.schema,channelName:this.channelName,maxConcurrency:e.maxConcurrency,maxSize:e.maxSize,releaseIntervalMs:e.releaseIntervalMs}).execute(e.databaseClient)}clear(e){return new r({schema:this.schema,channelName:this.channelName}).execute(e.databaseClient)}}class v{policy;message;constructor(e){this.message=new A({schema:e.schema,channelName:e.channelName}),this.policy=new L({schema:e.schema,channelName:e.channelName})}}class D{schema;id;channelName;name;content;dequeueNonce;state;numAttempts;constructor(e){this.schema=e.schema,this.id=e.id,this.channelName=e.channelName,this.dequeueNonce=e.dequeueNonce,this.name=e.name,this.content=e.content,this.state=e.state,this.numAttempts=e.numAttempts}async defer(e){return new d({schema:this.schema,id:this.id,dequeueNonce:this.dequeueNonce,delayMs:e.delayMs,state:e.state}).execute(e.databaseClient)}async delete(e){return new T({schema:this.schema,id:this.id,dequeueNonce:this.dequeueNonce}).execute(e.databaseClient)}}class z{schema;constructor(e){this.schema=e}async dequeue(e){let _=await new N({schema:this.schema}).execute(e.databaseClient);if(_.resultType==="MESSAGE_DEQUEUED")return{resultType:"MESSAGE_DEQUEUED",message:new D({schema:this.schema,id:_.message.id,dequeueNonce:_.message.dequeueNonce,channelName:_.message.channelName,name:_.message.name,content:_.message.content,state:_.message.state,numAttempts:_.message.numAttempts})};else return _}channel(e){return new v({schema:this.schema,channelName:e})}batch(){return new O({schema:this.schema})}migrations(e){return[G,B,P,H,q,C,$,F,b,k].map((t)=>({name:t.name,sql:t.sql({schema:this.schema,useWake:e.useWake??U}).map((_)=>M(_.value))})).sort((t,_)=>t.name.localeCompare(_.name))}wakeChannel(){return u.toString(this.schema)}}export{z as Queue,N as MessageDequeueCommand,T as MessageDeleteCommand,d as MessageDeferCommand,m as MessageCreateCommand,o as ChannelPolicySetCommand,r as ChannelPolicyClearCommand};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lonnymq",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -24,4 +24,4 @@
24
24
  "peerDependencies": {
25
25
  "typescript": "^5.0.0"
26
26
  }
27
- }
27
+ }