lonnymq 0.0.0 → 0.0.2

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