lonnymq 0.0.0

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.js ADDED
@@ -0,0 +1,531 @@
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`
2
+ SELECT 1 FROM ${n(this.schema)}."channel_policy_clear"(
3
+ ${s(this.channelName)}
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`
6
+ SELECT 1 FROM ${n(this.schema)}."channel_policy_set"(
7
+ ${s(this.channelName)},
8
+ ${s(this.maxSize)}::BIGINT,
9
+ ${s(this.maxConcurrency)}::BIGINT
10
+ )
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
+ CREATE FUNCTION ${n(e.schema)}."message_create" (
13
+ p_channel_name TEXT,
14
+ p_name TEXT,
15
+ p_content TEXT,
16
+ p_lock_ms BIGINT,
17
+ p_delay_ms BIGINT
18
+ ) RETURNS JSONB AS $$
19
+ DECLARE
20
+ v_now TIMESTAMP;
21
+ v_channel_policy RECORD;
22
+ v_channel_state RECORD;
23
+ v_message RECORD;
24
+ v_message_next_dequeue_after TIMESTAMP;
25
+ BEGIN
26
+ v_now := NOW() + INTERVAL '1 MILLISECOND' * p_delay_ms;
27
+
28
+ SELECT
29
+ "max_size",
30
+ "max_concurrency"
31
+ FROM ${n(e.schema)}."channel_policy"
32
+ WHERE "name" = p_channel_name
33
+ FOR SHARE
34
+ INTO v_channel_policy;
35
+
36
+ INSERT INTO ${n(e.schema)}."channel_state" (
37
+ "name",
38
+ "current_size",
39
+ "current_concurrency",
40
+ "max_size",
41
+ "max_concurrency",
42
+ "message_next_id",
43
+ "message_next_dequeue_after"
44
+ ) VALUES (
45
+ p_channel_name,
46
+ 0,
47
+ 0,
48
+ v_channel_policy."max_size",
49
+ v_channel_policy."max_concurrency",
50
+ NULL,
51
+ NULL
52
+ ) ON CONFLICT ("name")
53
+ DO UPDATE SET "id" = EXCLUDED."id"
54
+ RETURNING
55
+ "id",
56
+ "current_size",
57
+ "current_concurrency",
58
+ "max_size",
59
+ "max_concurrency",
60
+ "message_next_id",
61
+ "message_next_dequeue_after"
62
+ INTO v_channel_state;
63
+
64
+ IF v_channel_state."current_size" >= v_channel_policy."max_size" THEN
65
+ RETURN JSONB_BUILD_OBJECT(
66
+ 'result_code', ${s(1)}
67
+ );
68
+ END IF;
69
+
70
+ INSERT INTO ${n(e.schema)}."message" (
71
+ "channel_name",
72
+ "name",
73
+ "content",
74
+ "lock_ms",
75
+ "dequeue_after"
76
+ ) VALUES (
77
+ p_channel_name,
78
+ p_name,
79
+ p_content,
80
+ p_lock_ms,
81
+ v_now + INTERVAL '1 MILLISECOND' * p_delay_ms
82
+ ) ON CONFLICT ("channel_name", "name")
83
+ WHERE "num_attempts" = 0
84
+ DO UPDATE SET
85
+ "id" = EXCLUDED."id"
86
+ RETURNING
87
+ "id",
88
+ "dequeue_after"
89
+ INTO v_message;
90
+
91
+ IF v_message."id" IS NULL THEN
92
+ RETURN JSONB_BUILD_OBJECT(
93
+ 'result_code', ${s(2)},
94
+ 'id', v_message."id"
95
+ );
96
+ END IF;
97
+
98
+ IF
99
+ v_channel_state."message_next_id" IS NULL OR
100
+ v_channel_state."message_next_dequeue_after" > v_message."dequeue_after"
101
+ THEN
102
+ UPDATE ${n(e.schema)}."channel_state" SET
103
+ "current_size" = v_channel_state."current_size" + 1,
104
+ "message_next_id" = v_message."id",
105
+ "message_next_dequeue_after" = v_message."dequeue_after"
106
+ WHERE "id" = v_channel_state."id";
107
+ ELSE
108
+ UPDATE ${n(e.schema)}."channel_state" SET
109
+ "current_size" = v_channel_state."current_size" + 1
110
+ WHERE "id" = v_channel_state."id";
111
+ END IF;
112
+
113
+ PERFORM ${n(e.schema)}."wake"(GREATEST(0, p_delay_ms));
114
+
115
+ RETURN JSONB_BUILD_OBJECT(
116
+ 'result_code', ${s(0)},
117
+ 'id', v_message."id"
118
+ );
119
+ END;
120
+ $$ 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`
122
+ SELECT ${n(this.schema)}."message_create"(
123
+ ${s(this.channelName)},
124
+ ${s(this.name)},
125
+ ${s(this.content)},
126
+ ${s(this.lockMs)}::BIGINT,
127
+ ${s(this.delayMs)}::BIGINT
128
+ ) 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`
130
+ CREATE FUNCTION ${n(e.schema)}."message_dequeue" ()
131
+ RETURNS JSONB AS $$
132
+ DECLARE
133
+ v_now TIMESTAMP;
134
+ v_dequeue_id UUID;
135
+ v_channel_state RECORD;
136
+ v_message_locked RECORD;
137
+ v_retry_after TIMESTAMP;
138
+ v_message_dequeue RECORD;
139
+ v_message_next_dequeue RECORD;
140
+ v_message_next_dequeue_after TIMESTAMP;
141
+ BEGIN
142
+ v_now := NOW();
143
+ v_dequeue_id := GEN_RANDOM_UUID();
144
+
145
+ SELECT
146
+ "id",
147
+ "name",
148
+ "state",
149
+ "content",
150
+ "channel_name",
151
+ "lock_ms",
152
+ "dequeue_after",
153
+ "num_attempts"
154
+ FROM ${n(e.schema)}."message"
155
+ WHERE "is_locked"
156
+ ORDER BY "dequeue_after" ASC
157
+ FOR UPDATE
158
+ SKIP LOCKED
159
+ LIMIT 1
160
+ INTO v_message_locked;
161
+
162
+ IF v_message_locked."dequeue_after" <= v_now THEN
163
+ UPDATE ${n(e.schema)}."message" SET
164
+ "num_attempts" = v_message_locked."num_attempts" + 1,
165
+ "dequeue_after" = v_now + (v_message_locked."lock_ms" * INTERVAL '1 millisecond'),
166
+ "dequeue_id" = v_dequeue_id
167
+ WHERE "id" = v_message_locked."id";
168
+
169
+ PERFORM ${n(e.schema)}."wake"(v_message_locked."lock_ms");
170
+
171
+ RETURN JSONB_BUILD_OBJECT(
172
+ 'result_code', ${s(1)},
173
+ 'id', v_message_locked.id,
174
+ 'channel_name', v_message_locked.channel_name,
175
+ 'state', v_message_locked.state,
176
+ 'name', v_message_locked.name,
177
+ 'dequeue_id', v_dequeue_id,
178
+ 'content', v_message_locked.content,
179
+ 'num_attempts', v_message_locked.num_attempts
180
+ );
181
+ END IF;
182
+
183
+ SELECT
184
+ "id",
185
+ "name",
186
+ "message_next_id",
187
+ "message_next_dequeue_after",
188
+ "current_concurrency"
189
+ FROM ${n(e.schema)}."channel_state"
190
+ WHERE "message_next_id" IS NOT NULL
191
+ AND ("max_concurrency" IS NULL OR "current_concurrency" < "max_concurrency")
192
+ ORDER BY "message_next_dequeue_after" ASC
193
+ FOR UPDATE
194
+ SKIP LOCKED
195
+ LIMIT 1
196
+ INTO v_channel_state;
197
+
198
+ IF v_channel_state."id" IS NULL OR v_channel_state."message_next_dequeue_after" > v_now THEN
199
+ v_retry_after := LEAST(
200
+ v_channel_state."message_next_dequeue_after",
201
+ v_message_locked."dequeue_after"
202
+ );
203
+
204
+ RETURN JSONB_BUILD_OBJECT(
205
+ 'result_code', ${s(0)},
206
+ 'retry_ms', CEIL(EXTRACT(MILLISECOND FROM v_retry_after - v_now))
207
+ );
208
+ END IF;
209
+
210
+ SELECT
211
+ "id",
212
+ "name",
213
+ "channel_name",
214
+ "content",
215
+ "num_attempts",
216
+ "state",
217
+ "lock_ms"
218
+ FROM ${n(e.schema)}."message"
219
+ WHERE "id" = v_channel_state."message_next_id"
220
+ INTO v_message_dequeue;
221
+
222
+ UPDATE ${n(e.schema)}."message" SET
223
+ "is_locked" = TRUE,
224
+ "num_attempts" = v_message_dequeue."num_attempts" + 1,
225
+ "dequeue_id" = v_dequeue_id,
226
+ "dequeue_after" = v_now + (v_message_dequeue."lock_ms" * INTERVAL '1 millisecond')
227
+ WHERE "id" = v_message_dequeue."id";
228
+
229
+ PERFORM ${n(e.schema)}."wake"(v_message_dequeue."lock_ms");
230
+
231
+ SELECT
232
+ "id",
233
+ "dequeue_after"
234
+ FROM ${n(e.schema)}."message"
235
+ WHERE NOT "is_locked"
236
+ AND "channel_name" = v_message_dequeue."channel_name"
237
+ ORDER BY "dequeue_after" ASC
238
+ LIMIT 1
239
+ INTO v_message_next_dequeue;
240
+
241
+ IF v_message_next_dequeue."id" IS NOT NULL THEN
242
+ v_message_next_dequeue_after := GREATEST(
243
+ v_message_next_dequeue."dequeue_after",
244
+ v_now
245
+ );
246
+
247
+ UPDATE ${n(e.schema)}."channel_state" SET
248
+ "current_concurrency" = v_channel_state."current_concurrency" + 1,
249
+ "message_next_id" = v_message_next_dequeue."id",
250
+ "message_next_dequeue_after" = v_message_next_dequeue_after
251
+ WHERE "id" = v_channel_state."id";
252
+ ELSE
253
+ UPDATE ${n(e.schema)}."channel_state" SET
254
+ "current_concurrency" = v_channel_state."current_concurrency" + 1,
255
+ "message_next_id" = NULL
256
+ WHERE "id" = v_channel_state."id";
257
+ END IF;
258
+
259
+
260
+ RETURN JSONB_BUILD_OBJECT(
261
+ 'result_code', ${s(1)},
262
+ 'id', v_message_dequeue.id,
263
+ 'channel_name', v_message_dequeue.channel_name,
264
+ 'state', v_message_dequeue.state,
265
+ 'dequeue_id', v_dequeue_id,
266
+ 'name', v_message_dequeue.name,
267
+ 'content', v_message_dequeue.content,
268
+ 'num_attempts', v_message_dequeue.num_attempts
269
+ );
270
+ END;
271
+ $$ LANGUAGE plpgsql;
272
+ `]}};class N{schema;constructor(e){this.schema=e.schema}async execute(e){let t=await e.query(a`
273
+ 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`
275
+ CREATE FUNCTION ${n(e.schema)}."message_delete" (
276
+ p_id UUID,
277
+ p_dequeue_id UUID
278
+ )
279
+ RETURNS JSONB AS $$
280
+ DECLARE
281
+ v_channel_state RECORD;
282
+ v_message RECORD;
283
+ BEGIN
284
+ SELECT
285
+ "id",
286
+ "channel_name",
287
+ "dequeue_id"
288
+ FROM ${n(e.schema)}."message"
289
+ WHERE "id" = p_id
290
+ FOR UPDATE
291
+ INTO v_message;
292
+
293
+ IF v_message."id" IS NULL THEN
294
+ RETURN JSONB_BUILD_OBJECT(
295
+ 'result_code', ${s(0)}
296
+ );
297
+ ELSEIF v_message."dequeue_id" != p_dequeue_id THEN
298
+ RETURN JSONB_BUILD_OBJECT(
299
+ 'result_code', ${s(1)}
300
+ );
301
+ END IF;
302
+
303
+ SELECT
304
+ "id",
305
+ "current_size",
306
+ "current_concurrency"
307
+ FROM ${n(e.schema)}."channel_state"
308
+ WHERE "name" = v_message."channel_name"
309
+ FOR UPDATE
310
+ INTO v_channel_state;
311
+
312
+ IF v_channel_state."current_size" = 1 THEN
313
+ DELETE FROM ${n(e.schema)}."channel_state"
314
+ WHERE "id" = v_channel_state."id";
315
+ ELSE
316
+ UPDATE ${n(e.schema)}."channel_state" SET
317
+ "current_concurrency" = v_channel_state."current_concurrency" - 1,
318
+ "current_size" = v_channel_state."current_size" - 1
319
+ WHERE "id" = v_channel_state."id";
320
+ END IF;
321
+
322
+ DELETE FROM ${n(e.schema)}."message"
323
+ WHERE "id" = p_id;
324
+
325
+ RETURN JSONB_BUILD_OBJECT(
326
+ 'result_code', ${s(2)}
327
+ );
328
+ END;
329
+ $$ 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`
331
+ SELECT ${n(this.schema)}."message_delete"( ${s(this.id)},
332
+ ${s(this.dequeueId)}
333
+ ) 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`
335
+ CREATE FUNCTION ${n(e.schema)}."message_defer" (
336
+ p_id UUID,
337
+ p_dequeue_id UUID,
338
+ p_delay_ms BIGINT,
339
+ p_state TEXT
340
+ )
341
+ RETURNS JSONB AS $$
342
+ DECLARE
343
+ v_channel_state RECORD;
344
+ v_dequeue_after TIMESTAMP;
345
+ v_message RECORD;
346
+ BEGIN
347
+ SELECT
348
+ "id",
349
+ "channel_name",
350
+ "dequeue_id"
351
+ FROM ${n(e.schema)}."message"
352
+ WHERE "id" = p_id
353
+ FOR UPDATE
354
+ INTO v_message;
355
+
356
+ IF v_message."id" IS NULL THEN
357
+ RETURN JSONB_BUILD_OBJECT(
358
+ 'result_code', ${s(0)}
359
+ );
360
+ ELSEIF v_message."dequeue_id" != p_dequeue_id THEN
361
+ RETURN JSONB_BUILD_OBJECT(
362
+ 'result_code', ${s(1)}
363
+ );
364
+ END IF;
365
+
366
+ SELECT
367
+ "current_concurrency",
368
+ "message_next_id",
369
+ "message_next_dequeue_after"
370
+ FROM ${n(e.schema)}."channel_state"
371
+ WHERE "name" = v_message."channel_name"
372
+ FOR UPDATE
373
+ INTO v_channel_state;
374
+
375
+ v_dequeue_after := NOW() + INTERVAL '1 MILLISECOND' * p_delay_ms;
376
+
377
+ IF
378
+ v_channel_state."message_next_id" IS NULL OR
379
+ v_channel_state."message_next_dequeue_after" > v_dequeue_after
380
+ THEN
381
+ UPDATE ${n(e.schema)}."channel_state" SET
382
+ "current_concurrency" = v_channel_state."current_concurrency" - 1,
383
+ "message_next_id" = v_message."id",
384
+ "message_next_dequeue_after" = v_dequeue_after
385
+ WHERE "name" = v_message."channel_name";
386
+ ELSE
387
+ UPDATE ${n(e.schema)}."channel_state" SET
388
+ "current_concurrency" = v_channel_state."current_concurrency" - 1
389
+ WHERE "name" = v_message."channel_name";
390
+ END IF;
391
+
392
+ UPDATE ${n(e.schema)}."message" SET
393
+ "state" = p_state,
394
+ "is_locked" = FALSE,
395
+ "dequeue_after" = v_dequeue_after
396
+ WHERE "id" = p_id;
397
+
398
+ PERFORM ${n(e.schema)}."wake"(GREATEST(0, p_delay_ms));
399
+
400
+ RETURN JSONB_BUILD_OBJECT(
401
+ 'result_code', ${s(2)}
402
+ );
403
+ END;
404
+ $$ 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`
406
+ SELECT ${n(this.schema)}."message_defer"(
407
+ ${s(this.id)},
408
+ ${s(this.dequeueId)},
409
+ ${s(this.delayMs)}::BIGINT,
410
+ ${s(this.state)}
411
+ ) 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(`
414
+ `).trim()};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/00-table-channel-policy.ts",P={name:r(__filename),sql:(e)=>{return[a`
415
+ CREATE TABLE ${n(e.schema)}."channel_policy" (
416
+ "id" UUID NOT NULL DEFAULT GEN_RANDOM_UUID(),
417
+ "name" TEXT NOT NULL,
418
+ "max_size" BIGINT,
419
+ "max_concurrency" BIGINT,
420
+ PRIMARY KEY ("id")
421
+ );
422
+ `,a`
423
+ CREATE UNIQUE INDEX "channel_policy_name_ux"
424
+ 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`
426
+ CREATE TABLE ${n(e.schema)}."channel_state" (
427
+ "id" UUID NOT NULL DEFAULT GEN_RANDOM_UUID(),
428
+ "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,
434
+ "message_next_dequeue_after" TIMESTAMP,
435
+ PRIMARY KEY ("id")
436
+ );
437
+ `,a`
438
+ CREATE UNIQUE INDEX "channel_state_name_ux"
439
+ ON ${n(e.schema)}."channel_state" ("name");
440
+ `,a`
441
+ CREATE INDEX "channel_state_dequeue_ix"
442
+ ON ${n(e.schema)}."channel_state" (
443
+ "message_next_dequeue_after" ASC
444
+ ) WHERE "message_next_id" IS NOT NULL
445
+ AND ("max_concurrency" IS NULL OR "current_concurrency" < "max_concurrency");
446
+ `]}};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/02-table-message.ts",b={name:r(__filename),sql:(e)=>{return[a`
447
+ CREATE TABLE ${n(e.schema)}."message" (
448
+ "id" UUID NOT NULL DEFAULT GEN_RANDOM_UUID(),
449
+ "channel_name" TEXT NOT NULL,
450
+ "dequeue_id" UUID,
451
+ "name" TEXT,
452
+ "content" TEXT NOT NULL,
453
+ "state" TEXT,
454
+ "lock_ms" BIGINT NOT NULL,
455
+ "is_locked" BOOLEAN NOT NULL DEFAULT FALSE,
456
+ "num_attempts" BIGINT NOT NULL DEFAULT 0,
457
+ "dequeue_after" TIMESTAMP NOT NULL,
458
+ "sweep_after" TIMESTAMP,
459
+ PRIMARY KEY ("id")
460
+ );
461
+ `,a`
462
+ CREATE UNIQUE INDEX "message_name_ux"
463
+ ON ${n(e.schema)}."message" (
464
+ "channel_name",
465
+ "name"
466
+ ) WHERE "num_attempts" = 0
467
+ `,a`
468
+ CREATE INDEX "message_dequeue_ix"
469
+ ON ${n(e.schema)}."message" (
470
+ "channel_name",
471
+ "dequeue_after" ASC
472
+ ) WHERE NOT "is_locked";
473
+ `,a`
474
+ CREATE INDEX "message_locked_dequeue_ix"
475
+ ON ${n(e.schema)}."message" (
476
+ "dequeue_after" ASC
477
+ ) 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`
479
+ CREATE FUNCTION ${n(e.schema)}."wake" (
480
+ p_delay_ms BIGINT
481
+ ) RETURNS VOID AS $$
482
+ BEGIN
483
+ IF ${s(e.useWake)} THEN
484
+ PERFORM PG_NOTIFY(${s(t)}, p_delay_ms::TEXT);
485
+ END IF;
486
+ END;
487
+ $$ LANGUAGE plpgsql;
488
+ `]}};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/08-function-channel-policy-set.ts",z={name:r(__filename),sql:(e)=>{return[a`
489
+ CREATE FUNCTION ${n(e.schema)}."channel_policy_set" (
490
+ p_name TEXT,
491
+ p_max_size BIGINT,
492
+ p_max_concurrency BIGINT
493
+ ) RETURNS VOID AS $$
494
+ BEGIN
495
+ INSERT INTO ${n(e.schema)}."channel_policy" (
496
+ "name",
497
+ "max_size",
498
+ "max_concurrency"
499
+ ) VALUES (
500
+ p_name,
501
+ p_max_size,
502
+ p_max_concurrency
503
+ ) ON CONFLICT ("name") DO UPDATE SET
504
+ "max_size" = EXCLUDED."max_size",
505
+ "max_concurrency" = EXCLUDED."max_concurrency";
506
+
507
+ UPDATE ${n(e.schema)}."channel_state" SET
508
+ "max_size" = p_max_size,
509
+ "max_concurrency" = p_max_concurrency
510
+ WHERE "name" = p_name;
511
+
512
+ PERFORM ${n(e.schema)}."wake"(0);
513
+ END;
514
+ $$ 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`
516
+ CREATE FUNCTION ${n(e.schema)}."channel_policy_clear" (
517
+ p_name TEXT
518
+ ) RETURNS VOID AS $$
519
+ BEGIN
520
+ DELETE FROM ${n(e.schema)}."channel_policy"
521
+ WHERE "name" = p_name;
522
+
523
+ UPDATE ${n(e.schema)}."channel_state" SET
524
+ "max_size" = NULL,
525
+ "max_concurrency" = NULL
526
+ WHERE "name" = p_name;
527
+
528
+ PERFORM ${n(e.schema)}."wake"(0);
529
+ END;
530
+ $$ 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};
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "lonnymq",
3
+ "version": "0.0.0",
4
+ "type": "module",
5
+ "exports": {
6
+ ".": {
7
+ "types": "./dist/index.d.ts",
8
+ "import": "./dist/index.js",
9
+ "require": "./dist/index.cjs"
10
+ }
11
+ },
12
+ "files": ["dist"],
13
+ "devDependencies": {
14
+ "@stylistic/eslint-plugin-ts": "4.2.0",
15
+ "@types/bun": "latest",
16
+ "@types/pg": "^8.15.4",
17
+ "@typescript-eslint/eslint-plugin": "8.26.1",
18
+ "@typescript-eslint/parser": "8.26.1",
19
+ "bun-plugin-dts": "^0.3.0",
20
+ "eslint": "9.22.0",
21
+ "np": "^10.2.0",
22
+ "pg": "^8.16.3"
23
+ },
24
+ "peerDependencies": {
25
+ "typescript": "^5.0.0"
26
+ }
27
+ }