@molecule/api-queue-memory 1.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/LICENSE +115 -0
- package/dist/browser-guard.d.ts +2 -0
- package/dist/browser-guard.d.ts.map +1 -0
- package/dist/browser-guard.js +19 -0
- package/dist/browser-guard.js.map +1 -0
- package/dist/index.d.ts +61 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +61 -0
- package/dist/index.js.map +1 -0
- package/dist/provider.d.ts +25 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +88 -0
- package/dist/provider.js.map +1 -0
- package/dist/queue.d.ts +28 -0
- package/dist/queue.d.ts.map +1 -0
- package/dist/queue.js +369 -0
- package/dist/queue.js.map +1 -0
- package/dist/types.d.ts +109 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +7 -0
- package/dist/types.js.map +1 -0
- package/package.json +48 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
+
|
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
+
the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
+
other entities that control, are controlled by, or are under common
|
|
17
|
+
control with that entity. For the purposes of this definition,
|
|
18
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
+
direction or management of such entity, whether by contract or
|
|
20
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
+
|
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
+
exercising permissions granted by this License.
|
|
25
|
+
|
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
+
including but not limited to software source code, documentation
|
|
28
|
+
source, and configuration files.
|
|
29
|
+
|
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
|
31
|
+
transformation or translation of a Source form, including but
|
|
32
|
+
not limited to compiled object code, generated documentation,
|
|
33
|
+
and conversions to other media types.
|
|
34
|
+
|
|
35
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
+
Object form, made available under the License, as indicated by a
|
|
37
|
+
copyright notice that is included in or attached to the work.
|
|
38
|
+
|
|
39
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
40
|
+
form, that is based on (or derived from) the Work and for which the
|
|
41
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
42
|
+
represent, as a whole, an original work of authorship.
|
|
43
|
+
|
|
44
|
+
"Contribution" shall mean any work of authorship, including the
|
|
45
|
+
original version of the Work and any modifications or additions
|
|
46
|
+
to that Work, that is intentionally submitted to the Licensor for
|
|
47
|
+
inclusion in the Work by the copyright owner or by an individual or
|
|
48
|
+
Legal Entity authorized to submit on behalf of the copyright owner.
|
|
49
|
+
|
|
50
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
51
|
+
on behalf of whom a Contribution has been received by the Licensor and
|
|
52
|
+
subsequently incorporated within the Work.
|
|
53
|
+
|
|
54
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
55
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
56
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
57
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
58
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
59
|
+
Work and such Derivative Works in Source or Object form.
|
|
60
|
+
|
|
61
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
62
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
63
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
64
|
+
patent license to make, have made, use, offer to sell, sell, import,
|
|
65
|
+
and otherwise transfer the Work.
|
|
66
|
+
|
|
67
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
68
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
69
|
+
modifications, and in Source or Object form, provided that You
|
|
70
|
+
meet the following conditions:
|
|
71
|
+
|
|
72
|
+
(a) You must give any other recipients of the Work or
|
|
73
|
+
Derivative Works a copy of this License; and
|
|
74
|
+
|
|
75
|
+
(b) You must cause any modified files to carry prominent notices
|
|
76
|
+
stating that You changed the files; and
|
|
77
|
+
|
|
78
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
79
|
+
that You distribute, all copyright, patent, trademark, and
|
|
80
|
+
attribution notices from the Source form of the Work,
|
|
81
|
+
excluding those notices that do not pertain to any part of
|
|
82
|
+
the Derivative Works; and
|
|
83
|
+
|
|
84
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
85
|
+
distribution, then any Derivative Works that You distribute must
|
|
86
|
+
include a readable copy of the attribution notices contained
|
|
87
|
+
within such NOTICE file.
|
|
88
|
+
|
|
89
|
+
5. Submission of Contributions.
|
|
90
|
+
|
|
91
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
92
|
+
names, trademarks, service marks, or product names of the Licensor.
|
|
93
|
+
|
|
94
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
95
|
+
agreed to in writing, Licensor provides the Work on an "AS IS" BASIS,
|
|
96
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND.
|
|
97
|
+
|
|
98
|
+
8. Limitation of Liability. In no event and under no legal theory shall
|
|
99
|
+
any Contributor be liable to You for damages.
|
|
100
|
+
|
|
101
|
+
9. Accepting Warranty or Additional Liability.
|
|
102
|
+
|
|
103
|
+
Copyright 2026 Molecule Dev, Inc.
|
|
104
|
+
|
|
105
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
106
|
+
you may not use this file except in compliance with the License.
|
|
107
|
+
You may obtain a copy of the License at
|
|
108
|
+
|
|
109
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
110
|
+
|
|
111
|
+
Unless required by applicable law or agreed to in writing, software
|
|
112
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
113
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
114
|
+
See the License for the specific language governing permissions and
|
|
115
|
+
limitations under the License.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-guard.d.ts","sourceRoot":"","sources":["../src/browser-guard.ts"],"names":[],"mappings":"AAuBA,OAAO,EAAE,CAAA"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser guard — `@molecule/api-queue-memory` is SERVER-ONLY.
|
|
3
|
+
*
|
|
4
|
+
* Generated by scripts/gen-browser-guards.mjs (workspace root) — edit THAT, not this.
|
|
5
|
+
* Evaluating a server package in a browser bundle is always an import-graph mistake
|
|
6
|
+
* (node APIs, secrets); without this guard it surfaces as a cryptic downstream crash
|
|
7
|
+
* ("Buffer is not defined") far from the culprit. Throwing here names the package and
|
|
8
|
+
* the fix at the exact moment the client bundle evaluates it. jsdom tests and SSR are
|
|
9
|
+
* unaffected: the throw requires browser globals AND the absence of a node runtime.
|
|
10
|
+
*/
|
|
11
|
+
const g = globalThis;
|
|
12
|
+
if (g.window !== undefined && g.document !== undefined && !g.process?.versions?.node) {
|
|
13
|
+
throw new Error('@molecule/api-queue-memory is SERVER-ONLY: it was bundled into browser/client code. Import it only ' +
|
|
14
|
+
'from server code (a server route/function or your API), or dynamic-import it inside ' +
|
|
15
|
+
'the server handler — never from components or shared client modules, and never ' +
|
|
16
|
+
'polyfill Buffer/process to silence this.');
|
|
17
|
+
}
|
|
18
|
+
export {};
|
|
19
|
+
//# sourceMappingURL=browser-guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-guard.js","sourceRoot":"","sources":["../src/browser-guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,MAAM,CAAC,GAAG,UAIT,CAAA;AACD,IAAI,CAAC,CAAC,MAAM,KAAK,SAAS,IAAI,CAAC,CAAC,QAAQ,KAAK,SAAS,IAAI,CAAC,CAAC,CAAC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IACrF,MAAM,IAAI,KAAK,CACb,qGAAqG;QACnG,sFAAsF;QACtF,iFAAiF;QACjF,0CAA0C,CAC7C,CAAA;AACH,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory queue provider for molecule.dev.
|
|
3
|
+
*
|
|
4
|
+
* A zero-dependency, zero-configuration, in-process queue provider for
|
|
5
|
+
* development and testing — no broker, no credentials, no environment
|
|
6
|
+
* variables. Implements the full `@molecule/api-queue` contract with
|
|
7
|
+
* SQS-style semantics: named queues, at-least-once delivery via visibility
|
|
8
|
+
* leases, delayed messages (`delaySeconds`), redelivery on subscriber
|
|
9
|
+
* handler failure or `nack()`, a bounded delivery cap with optional
|
|
10
|
+
* dead-letter routing, FIFO group ordering + deduplication, long-polling
|
|
11
|
+
* `receive()`, and `maxMessages`-bounded subscriber concurrency.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* import { setProvider, queue } from '@molecule/api-queue'
|
|
16
|
+
* import { provider } from '@molecule/api-queue-memory'
|
|
17
|
+
*
|
|
18
|
+
* setProvider(provider) // no configuration, no env vars
|
|
19
|
+
*
|
|
20
|
+
* const emails = queue('emails')
|
|
21
|
+
* const unsubscribe = emails.subscribe(async (message) => {
|
|
22
|
+
* await deliver(message.body)
|
|
23
|
+
* await message.ack() // handler success also auto-acks
|
|
24
|
+
* })
|
|
25
|
+
*
|
|
26
|
+
* await emails.send({ body: { to: 'a@b.c' } })
|
|
27
|
+
* await emails.send({ body: { to: 'later@b.c' }, delaySeconds: 60 })
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* @remarks
|
|
31
|
+
* Single-process and DEV-ONLY. Messages live in this process's memory: there
|
|
32
|
+
* is NO persistence (everything is lost on restart) and NO cross-instance
|
|
33
|
+
* delivery, so it must not be used for multi-instance production — swap in
|
|
34
|
+
* `@molecule/api-queue-redis`, `@molecule/api-queue-rabbitmq`, or
|
|
35
|
+
* `@molecule/api-queue-sqs` for production workloads. Delivery is
|
|
36
|
+
* at-least-once: a message whose visibility lease expires without `ack()` is
|
|
37
|
+
* redelivered (with an incremented `receiveCount`), and a message delivered
|
|
38
|
+
* more than `maxReceiveCount` times (default 3) is routed to the queue's
|
|
39
|
+
* dead-letter queue when one was configured via `createQueue()` — otherwise
|
|
40
|
+
* it is dropped with an error log. Message bodies are `structuredClone`d on
|
|
41
|
+
* send and per delivery (like a real broker's serialization), so bodies must
|
|
42
|
+
* be structured-cloneable and post-send mutations never leak to consumers.
|
|
43
|
+
* `QueueCreateOptions.maxMessageSize` is not enforced (nothing is
|
|
44
|
+
* serialized). `close()` clears all timers, resolves pending long-polls with
|
|
45
|
+
* `[]`, and stops all delivery.
|
|
46
|
+
*
|
|
47
|
+
* Two distinct redelivery delays (both `MemoryQueueOptions`, provider-wide):
|
|
48
|
+
* an explicit `nack()` redelivers per `redeliveryDelaySeconds` (default `0`
|
|
49
|
+
* — a deliberate caller decision, honored immediately), while an UNCAUGHT
|
|
50
|
+
* `subscribe()` handler throw redelivers per
|
|
51
|
+
* `handlerFailureRedeliveryDelaySeconds` (default `1` — an unplanned failure
|
|
52
|
+
* gets a real retry window instead of burning all `maxReceiveCount` attempts
|
|
53
|
+
* within milliseconds, mirroring the Redis bond's `attempts: 3, backoff:
|
|
54
|
+
* { type: 'exponential', delay: 1000 }`).
|
|
55
|
+
*
|
|
56
|
+
* @module
|
|
57
|
+
*/
|
|
58
|
+
export * from './browser-guard.js';
|
|
59
|
+
export * from './provider.js';
|
|
60
|
+
export * from './types.js';
|
|
61
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AAEH,cAAc,oBAAoB,CAAA;AAClC,cAAc,eAAe,CAAA;AAC7B,cAAc,YAAY,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory queue provider for molecule.dev.
|
|
3
|
+
*
|
|
4
|
+
* A zero-dependency, zero-configuration, in-process queue provider for
|
|
5
|
+
* development and testing — no broker, no credentials, no environment
|
|
6
|
+
* variables. Implements the full `@molecule/api-queue` contract with
|
|
7
|
+
* SQS-style semantics: named queues, at-least-once delivery via visibility
|
|
8
|
+
* leases, delayed messages (`delaySeconds`), redelivery on subscriber
|
|
9
|
+
* handler failure or `nack()`, a bounded delivery cap with optional
|
|
10
|
+
* dead-letter routing, FIFO group ordering + deduplication, long-polling
|
|
11
|
+
* `receive()`, and `maxMessages`-bounded subscriber concurrency.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* import { setProvider, queue } from '@molecule/api-queue'
|
|
16
|
+
* import { provider } from '@molecule/api-queue-memory'
|
|
17
|
+
*
|
|
18
|
+
* setProvider(provider) // no configuration, no env vars
|
|
19
|
+
*
|
|
20
|
+
* const emails = queue('emails')
|
|
21
|
+
* const unsubscribe = emails.subscribe(async (message) => {
|
|
22
|
+
* await deliver(message.body)
|
|
23
|
+
* await message.ack() // handler success also auto-acks
|
|
24
|
+
* })
|
|
25
|
+
*
|
|
26
|
+
* await emails.send({ body: { to: 'a@b.c' } })
|
|
27
|
+
* await emails.send({ body: { to: 'later@b.c' }, delaySeconds: 60 })
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* @remarks
|
|
31
|
+
* Single-process and DEV-ONLY. Messages live in this process's memory: there
|
|
32
|
+
* is NO persistence (everything is lost on restart) and NO cross-instance
|
|
33
|
+
* delivery, so it must not be used for multi-instance production — swap in
|
|
34
|
+
* `@molecule/api-queue-redis`, `@molecule/api-queue-rabbitmq`, or
|
|
35
|
+
* `@molecule/api-queue-sqs` for production workloads. Delivery is
|
|
36
|
+
* at-least-once: a message whose visibility lease expires without `ack()` is
|
|
37
|
+
* redelivered (with an incremented `receiveCount`), and a message delivered
|
|
38
|
+
* more than `maxReceiveCount` times (default 3) is routed to the queue's
|
|
39
|
+
* dead-letter queue when one was configured via `createQueue()` — otherwise
|
|
40
|
+
* it is dropped with an error log. Message bodies are `structuredClone`d on
|
|
41
|
+
* send and per delivery (like a real broker's serialization), so bodies must
|
|
42
|
+
* be structured-cloneable and post-send mutations never leak to consumers.
|
|
43
|
+
* `QueueCreateOptions.maxMessageSize` is not enforced (nothing is
|
|
44
|
+
* serialized). `close()` clears all timers, resolves pending long-polls with
|
|
45
|
+
* `[]`, and stops all delivery.
|
|
46
|
+
*
|
|
47
|
+
* Two distinct redelivery delays (both `MemoryQueueOptions`, provider-wide):
|
|
48
|
+
* an explicit `nack()` redelivers per `redeliveryDelaySeconds` (default `0`
|
|
49
|
+
* — a deliberate caller decision, honored immediately), while an UNCAUGHT
|
|
50
|
+
* `subscribe()` handler throw redelivers per
|
|
51
|
+
* `handlerFailureRedeliveryDelaySeconds` (default `1` — an unplanned failure
|
|
52
|
+
* gets a real retry window instead of burning all `maxReceiveCount` attempts
|
|
53
|
+
* within milliseconds, mirroring the Redis bond's `attempts: 3, backoff:
|
|
54
|
+
* { type: 'exponential', delay: 1000 }`).
|
|
55
|
+
*
|
|
56
|
+
* @module
|
|
57
|
+
*/
|
|
58
|
+
export * from './browser-guard.js';
|
|
59
|
+
export * from './provider.js';
|
|
60
|
+
export * from './types.js';
|
|
61
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AAEH,cAAc,oBAAoB,CAAA;AAClC,cAAc,eAAe,CAAA;AAC7B,cAAc,YAAY,CAAA"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory queue provider implementation.
|
|
3
|
+
*
|
|
4
|
+
* A zero-dependency, zero-configuration queue provider for development and
|
|
5
|
+
* testing. No broker process, no credentials, no environment variables.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
import type { QueueProvider } from '@molecule/api-queue';
|
|
10
|
+
import type { MemoryQueueOptions } from './types.js';
|
|
11
|
+
/**
|
|
12
|
+
* Creates an in-memory queue provider. Queues are created implicitly on first
|
|
13
|
+
* access (like the Redis bond) or explicitly via `createQueue()` with FIFO,
|
|
14
|
+
* visibility-timeout, retention, and dead-letter options. All state lives in
|
|
15
|
+
* this process and is lost on restart.
|
|
16
|
+
*
|
|
17
|
+
* @param options - Optional delivery defaults (visibility timeout, delivery cap, nack redelivery delay, handler-failure redelivery delay). Everything defaults sensibly — no configuration is required.
|
|
18
|
+
* @returns A `QueueProvider` backed by in-process queues.
|
|
19
|
+
*/
|
|
20
|
+
export declare const createProvider: (options?: MemoryQueueOptions) => QueueProvider;
|
|
21
|
+
/**
|
|
22
|
+
* Lazily-initialized in-memory queue provider proxy that creates the provider on first access.
|
|
23
|
+
*/
|
|
24
|
+
export declare const provider: QueueProvider;
|
|
25
|
+
//# sourceMappingURL=provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAA6B,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAGnF,OAAO,KAAK,EAAqB,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAEvE;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,GAAI,UAAU,kBAAkB,KAAG,aA2D7D,CAAA;AAKD;;GAEG;AACH,eAAO,MAAM,QAAQ,EAAE,aAUrB,CAAA"}
|
package/dist/provider.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory queue provider implementation.
|
|
3
|
+
*
|
|
4
|
+
* A zero-dependency, zero-configuration queue provider for development and
|
|
5
|
+
* testing. No broker process, no credentials, no environment variables.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
import { createQueue } from './queue.js';
|
|
10
|
+
/**
|
|
11
|
+
* Creates an in-memory queue provider. Queues are created implicitly on first
|
|
12
|
+
* access (like the Redis bond) or explicitly via `createQueue()` with FIFO,
|
|
13
|
+
* visibility-timeout, retention, and dead-letter options. All state lives in
|
|
14
|
+
* this process and is lost on restart.
|
|
15
|
+
*
|
|
16
|
+
* @param options - Optional delivery defaults (visibility timeout, delivery cap, nack redelivery delay, handler-failure redelivery delay). Everything defaults sensibly — no configuration is required.
|
|
17
|
+
* @returns A `QueueProvider` backed by in-process queues.
|
|
18
|
+
*/
|
|
19
|
+
export const createProvider = (options) => {
|
|
20
|
+
const visibilityTimeoutSeconds = options?.visibilityTimeoutSeconds ?? 30;
|
|
21
|
+
const maxReceiveCount = options?.maxReceiveCount ?? 3;
|
|
22
|
+
const redeliveryDelaySeconds = options?.redeliveryDelaySeconds ?? 0;
|
|
23
|
+
const handlerFailureRedeliveryDelaySeconds = options?.handlerFailureRedeliveryDelaySeconds ?? 1;
|
|
24
|
+
const handles = new Map();
|
|
25
|
+
const getOrCreateQueue = (name, createOptions) => {
|
|
26
|
+
let handle = handles.get(name);
|
|
27
|
+
if (!handle) {
|
|
28
|
+
handle = createQueue(name, {
|
|
29
|
+
defaultVisibilityTimeoutSeconds: createOptions?.visibilityTimeout ?? visibilityTimeoutSeconds,
|
|
30
|
+
maxReceiveCount,
|
|
31
|
+
redeliveryDelaySeconds,
|
|
32
|
+
handlerFailureRedeliveryDelaySeconds,
|
|
33
|
+
fifo: createOptions?.fifo ?? false,
|
|
34
|
+
messageRetentionSeconds: createOptions?.messageRetentionSeconds,
|
|
35
|
+
deadLetterQueue: createOptions?.deadLetterQueue,
|
|
36
|
+
resolveQueue: (dlqName) => getOrCreateQueue(dlqName),
|
|
37
|
+
});
|
|
38
|
+
handles.set(name, handle);
|
|
39
|
+
}
|
|
40
|
+
return handle.queue;
|
|
41
|
+
};
|
|
42
|
+
const provider = {
|
|
43
|
+
queue(name) {
|
|
44
|
+
return getOrCreateQueue(name);
|
|
45
|
+
},
|
|
46
|
+
async listQueues() {
|
|
47
|
+
return Array.from(handles.keys());
|
|
48
|
+
},
|
|
49
|
+
async createQueue(name, createOptions) {
|
|
50
|
+
// Like the Redis bond: returns the existing queue when already created
|
|
51
|
+
// (creation options only apply to a queue's first creation).
|
|
52
|
+
return getOrCreateQueue(name, createOptions);
|
|
53
|
+
},
|
|
54
|
+
async deleteQueue(name) {
|
|
55
|
+
const handle = handles.get(name);
|
|
56
|
+
if (handle) {
|
|
57
|
+
handle.close();
|
|
58
|
+
handles.delete(name);
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
async close() {
|
|
62
|
+
for (const handle of handles.values()) {
|
|
63
|
+
handle.close();
|
|
64
|
+
}
|
|
65
|
+
handles.clear();
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
return provider;
|
|
69
|
+
};
|
|
70
|
+
/** Lazily-initialized in-memory queue provider, created on first property access via a Proxy. */
|
|
71
|
+
let _provider = null;
|
|
72
|
+
/**
|
|
73
|
+
* Lazily-initialized in-memory queue provider proxy that creates the provider on first access.
|
|
74
|
+
*/
|
|
75
|
+
export const provider = new Proxy({}, {
|
|
76
|
+
get(_, prop, receiver) {
|
|
77
|
+
if (!_provider)
|
|
78
|
+
_provider = createProvider();
|
|
79
|
+
return Reflect.get(_provider, prop, receiver);
|
|
80
|
+
},
|
|
81
|
+
// set trap: methods run with `this` bound to the proxy — without it, instance-state writes land on the dummy target and are lost (see api-push-notifications-web-push)
|
|
82
|
+
set(_, prop, value) {
|
|
83
|
+
if (!_provider)
|
|
84
|
+
_provider = createProvider();
|
|
85
|
+
return Reflect.set(_provider, prop, value);
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
//# sourceMappingURL=provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAGxC;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,OAA4B,EAAiB,EAAE;IAC5E,MAAM,wBAAwB,GAAG,OAAO,EAAE,wBAAwB,IAAI,EAAE,CAAA;IACxE,MAAM,eAAe,GAAG,OAAO,EAAE,eAAe,IAAI,CAAC,CAAA;IACrD,MAAM,sBAAsB,GAAG,OAAO,EAAE,sBAAsB,IAAI,CAAC,CAAA;IACnE,MAAM,oCAAoC,GAAG,OAAO,EAAE,oCAAoC,IAAI,CAAC,CAAA;IAE/F,MAAM,OAAO,GAAG,IAAI,GAAG,EAA6B,CAAA;IAEpD,MAAM,gBAAgB,GAAG,CAAC,IAAY,EAAE,aAAkC,EAAS,EAAE;QACnF,IAAI,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;QAC9B,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,GAAG,WAAW,CAAC,IAAI,EAAE;gBACzB,+BAA+B,EAC7B,aAAa,EAAE,iBAAiB,IAAI,wBAAwB;gBAC9D,eAAe;gBACf,sBAAsB;gBACtB,oCAAoC;gBACpC,IAAI,EAAE,aAAa,EAAE,IAAI,IAAI,KAAK;gBAClC,uBAAuB,EAAE,aAAa,EAAE,uBAAuB;gBAC/D,eAAe,EAAE,aAAa,EAAE,eAAe;gBAC/C,YAAY,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,gBAAgB,CAAC,OAAO,CAAC;aACrD,CAAC,CAAA;YACF,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;QAC3B,CAAC;QACD,OAAO,MAAM,CAAC,KAAK,CAAA;IACrB,CAAC,CAAA;IAED,MAAM,QAAQ,GAAkB;QAC9B,KAAK,CAAC,IAAY;YAChB,OAAO,gBAAgB,CAAC,IAAI,CAAC,CAAA;QAC/B,CAAC;QAED,KAAK,CAAC,UAAU;YACd,OAAO,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAA;QACnC,CAAC;QAED,KAAK,CAAC,WAAW,CAAC,IAAY,EAAE,aAAkC;YAChE,uEAAuE;YACvE,6DAA6D;YAC7D,OAAO,gBAAgB,CAAC,IAAI,EAAE,aAAa,CAAC,CAAA;QAC9C,CAAC;QAED,KAAK,CAAC,WAAW,CAAC,IAAY;YAC5B,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;YAChC,IAAI,MAAM,EAAE,CAAC;gBACX,MAAM,CAAC,KAAK,EAAE,CAAA;gBACd,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;YACtB,CAAC;QACH,CAAC;QAED,KAAK,CAAC,KAAK;YACT,KAAK,MAAM,MAAM,IAAI,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;gBACtC,MAAM,CAAC,KAAK,EAAE,CAAA;YAChB,CAAC;YACD,OAAO,CAAC,KAAK,EAAE,CAAA;QACjB,CAAC;KACF,CAAA;IAED,OAAO,QAAQ,CAAA;AACjB,CAAC,CAAA;AAED,iGAAiG;AACjG,IAAI,SAAS,GAAyB,IAAI,CAAA;AAE1C;;GAEG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAkB,IAAI,KAAK,CAAC,EAAmB,EAAE;IACpE,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ;QACnB,IAAI,CAAC,SAAS;YAAE,SAAS,GAAG,cAAc,EAAE,CAAA;QAC5C,OAAO,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAA;IAC/C,CAAC;IACD,uKAAuK;IACvK,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK;QAChB,IAAI,CAAC,SAAS;YAAE,SAAS,GAAG,cAAc,EAAE,CAAA;QAC5C,OAAO,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,EAAE,KAAK,CAAC,CAAA;IAC5C,CAAC;CACF,CAAC,CAAA"}
|
package/dist/queue.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory queue implementation.
|
|
3
|
+
*
|
|
4
|
+
* SQS-style visibility-lease semantics over an in-process message list:
|
|
5
|
+
* messages are leased (invisible) on delivery and return to the queue when
|
|
6
|
+
* the lease expires without an `ack()`, giving at-least-once delivery with
|
|
7
|
+
* redelivery on subscriber handler failure. A single lazily-armed
|
|
8
|
+
* `setTimeout` per queue drives delayed visibility and lease reclaim; it is
|
|
9
|
+
* tracked, `unref()`d, and cleared on `close()` so nothing leaks.
|
|
10
|
+
*
|
|
11
|
+
* @module
|
|
12
|
+
*/
|
|
13
|
+
import type { MemoryQueueConfig, MemoryQueueHandle } from './types.js';
|
|
14
|
+
/**
|
|
15
|
+
* Creates an in-memory `Queue` with SQS-style visibility-lease delivery:
|
|
16
|
+
* send/sendBatch (with `delaySeconds` and FIFO group/dedup support), pull
|
|
17
|
+
* `receive()` (with long polling via `waitTimeSeconds`), push `subscribe()`
|
|
18
|
+
* (with `maxMessages`-bounded concurrency, auto-ack on handler success, and
|
|
19
|
+
* redelivery on handler failure), `size()`, and `purge()`. Messages
|
|
20
|
+
* exceeding the delivery cap are routed to the configured dead-letter queue
|
|
21
|
+
* or dropped with an error log.
|
|
22
|
+
*
|
|
23
|
+
* @param queueName - The queue name.
|
|
24
|
+
* @param config - Resolved queue configuration (defaults, FIFO mode, DLQ, queue resolver).
|
|
25
|
+
* @returns A handle with the `Queue` and an internal `close()` for the provider.
|
|
26
|
+
*/
|
|
27
|
+
export declare const createQueue: (queueName: string, config: MemoryQueueConfig) => MemoryQueueHandle;
|
|
28
|
+
//# sourceMappingURL=queue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queue.d.ts","sourceRoot":"","sources":["../src/queue.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAWH,OAAO,KAAK,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AA8DtE;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,WAAW,GAAI,WAAW,MAAM,EAAE,QAAQ,iBAAiB,KAAG,iBAsW1E,CAAA"}
|
package/dist/queue.js
ADDED
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory queue implementation.
|
|
3
|
+
*
|
|
4
|
+
* SQS-style visibility-lease semantics over an in-process message list:
|
|
5
|
+
* messages are leased (invisible) on delivery and return to the queue when
|
|
6
|
+
* the lease expires without an `ack()`, giving at-least-once delivery with
|
|
7
|
+
* redelivery on subscriber handler failure. A single lazily-armed
|
|
8
|
+
* `setTimeout` per queue drives delayed visibility and lease reclaim; it is
|
|
9
|
+
* tracked, `unref()`d, and cleared on `close()` so nothing leaks.
|
|
10
|
+
*
|
|
11
|
+
* @module
|
|
12
|
+
*/
|
|
13
|
+
import { getLogger } from '@molecule/api-bond';
|
|
14
|
+
const logger = getLogger();
|
|
15
|
+
/**
|
|
16
|
+
* How long (ms) a FIFO `deduplicationId` suppresses duplicate sends —
|
|
17
|
+
* mirrors SQS's 5-minute deduplication window.
|
|
18
|
+
*/
|
|
19
|
+
const DEDUP_WINDOW_MS = 5 * 60_000;
|
|
20
|
+
/**
|
|
21
|
+
* Creates an in-memory `Queue` with SQS-style visibility-lease delivery:
|
|
22
|
+
* send/sendBatch (with `delaySeconds` and FIFO group/dedup support), pull
|
|
23
|
+
* `receive()` (with long polling via `waitTimeSeconds`), push `subscribe()`
|
|
24
|
+
* (with `maxMessages`-bounded concurrency, auto-ack on handler success, and
|
|
25
|
+
* redelivery on handler failure), `size()`, and `purge()`. Messages
|
|
26
|
+
* exceeding the delivery cap are routed to the configured dead-letter queue
|
|
27
|
+
* or dropped with an error log.
|
|
28
|
+
*
|
|
29
|
+
* @param queueName - The queue name.
|
|
30
|
+
* @param config - Resolved queue configuration (defaults, FIFO mode, DLQ, queue resolver).
|
|
31
|
+
* @returns A handle with the `Queue` and an internal `close()` for the provider.
|
|
32
|
+
*/
|
|
33
|
+
export const createQueue = (queueName, config) => {
|
|
34
|
+
const messages = [];
|
|
35
|
+
const subscribers = new Map();
|
|
36
|
+
const waiters = new Set();
|
|
37
|
+
const dedupIds = new Map();
|
|
38
|
+
let wakeTimer = null;
|
|
39
|
+
let closed = false;
|
|
40
|
+
const maxReceiveCount = () => config.deadLetterQueue?.maxReceiveCount ?? config.maxReceiveCount;
|
|
41
|
+
const isStored = (message) => messages.includes(message);
|
|
42
|
+
const removeMessage = (message) => {
|
|
43
|
+
const index = messages.indexOf(message);
|
|
44
|
+
if (index !== -1)
|
|
45
|
+
messages.splice(index, 1);
|
|
46
|
+
};
|
|
47
|
+
const pruneDedup = (now) => {
|
|
48
|
+
for (const [key, entry] of dedupIds) {
|
|
49
|
+
if (entry.expiresAt <= now)
|
|
50
|
+
dedupIds.delete(key);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Routes a delivery-cap-exceeded message to the dead-letter queue when one
|
|
55
|
+
* is configured (and isn't this queue itself), otherwise drops it loudly.
|
|
56
|
+
*/
|
|
57
|
+
const deadLetter = (message) => {
|
|
58
|
+
removeMessage(message);
|
|
59
|
+
const dlqName = config.deadLetterQueue?.name;
|
|
60
|
+
if (dlqName && dlqName !== queueName) {
|
|
61
|
+
logger.warn(`Memory queue "${queueName}": message ${message.id} exceeded ${maxReceiveCount()} deliveries — routing to dead-letter queue "${dlqName}"`);
|
|
62
|
+
config
|
|
63
|
+
.resolveQueue(dlqName)
|
|
64
|
+
.send({
|
|
65
|
+
id: message.id,
|
|
66
|
+
body: message.body,
|
|
67
|
+
attributes: message.attributes,
|
|
68
|
+
groupId: message.groupId,
|
|
69
|
+
})
|
|
70
|
+
.catch((error) => {
|
|
71
|
+
logger.error(`Memory queue "${queueName}": failed to dead-letter message ${message.id} to "${dlqName}"`, error);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
logger.error(`Memory queue "${queueName}": message ${message.id} exceeded ${maxReceiveCount()} deliveries and no dead-letter queue is configured — message dropped`);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* Collects and leases up to `max` deliverable messages, applying delayed
|
|
80
|
+
* visibility, retention expiry, FIFO group head-of-line blocking, and the
|
|
81
|
+
* dead-letter delivery cap. Mutates lease state (at-least-once bookkeeping).
|
|
82
|
+
*/
|
|
83
|
+
const leaseMessages = (max, visibilityTimeoutMs) => {
|
|
84
|
+
if (max <= 0)
|
|
85
|
+
return [];
|
|
86
|
+
const now = Date.now();
|
|
87
|
+
const leased = [];
|
|
88
|
+
const blockedGroups = new Set();
|
|
89
|
+
if (config.fifo) {
|
|
90
|
+
for (const message of messages) {
|
|
91
|
+
if (message.leaseExpiresAt !== null && message.leaseExpiresAt > now) {
|
|
92
|
+
blockedGroups.add(message.groupId ?? '');
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
// Iterate a copy — retention expiry and dead-lettering splice the array.
|
|
97
|
+
for (const message of [...messages]) {
|
|
98
|
+
if (leased.length >= max)
|
|
99
|
+
break;
|
|
100
|
+
if (message.leaseExpiresAt !== null && message.leaseExpiresAt > now)
|
|
101
|
+
continue;
|
|
102
|
+
if (config.messageRetentionSeconds !== undefined &&
|
|
103
|
+
message.sentAt.getTime() + config.messageRetentionSeconds * 1000 <= now) {
|
|
104
|
+
removeMessage(message);
|
|
105
|
+
logger.debug(`Memory queue "${queueName}": message ${message.id} exceeded retention (${config.messageRetentionSeconds}s) — discarded`);
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
if (config.fifo) {
|
|
109
|
+
const group = message.groupId ?? '';
|
|
110
|
+
if (blockedGroups.has(group))
|
|
111
|
+
continue;
|
|
112
|
+
// Head-of-line: later messages in this group must not overtake this
|
|
113
|
+
// one, whether it's delivered now, still delayed, or dead-lettered.
|
|
114
|
+
blockedGroups.add(group);
|
|
115
|
+
}
|
|
116
|
+
if (message.visibleAt > now)
|
|
117
|
+
continue;
|
|
118
|
+
if (message.receiveCount >= maxReceiveCount()) {
|
|
119
|
+
deadLetter(message);
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
message.leaseExpiresAt = now + visibilityTimeoutMs;
|
|
123
|
+
message.receiveCount += 1;
|
|
124
|
+
message.receiptHandle = crypto.randomUUID();
|
|
125
|
+
leased.push(message);
|
|
126
|
+
}
|
|
127
|
+
return leased;
|
|
128
|
+
};
|
|
129
|
+
/**
|
|
130
|
+
* Wraps a leased message for the consumer. `ack()`/`nack()` only act while
|
|
131
|
+
* this delivery's receipt handle is still current — once the lease expires
|
|
132
|
+
* and the message is redelivered, stale receipts become documented no-ops
|
|
133
|
+
* (making `ack()` idempotent and late acks after redelivery harmless).
|
|
134
|
+
*/
|
|
135
|
+
const toReceivedMessage = (message) => {
|
|
136
|
+
const receipt = message.receiptHandle;
|
|
137
|
+
return {
|
|
138
|
+
id: message.id,
|
|
139
|
+
body: structuredClone(message.body),
|
|
140
|
+
receiptHandle: receipt,
|
|
141
|
+
attributes: message.attributes ? { ...message.attributes } : undefined,
|
|
142
|
+
receiveCount: message.receiveCount,
|
|
143
|
+
sentTimestamp: message.sentAt,
|
|
144
|
+
async ack() {
|
|
145
|
+
if (message.receiptHandle === receipt && isStored(message)) {
|
|
146
|
+
removeMessage(message);
|
|
147
|
+
pump();
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
async nack() {
|
|
151
|
+
if (message.receiptHandle === receipt && isStored(message)) {
|
|
152
|
+
message.leaseExpiresAt = null;
|
|
153
|
+
message.visibleAt = Date.now() + config.redeliveryDelaySeconds * 1000;
|
|
154
|
+
pump();
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
};
|
|
158
|
+
};
|
|
159
|
+
/**
|
|
160
|
+
* Leases a message to a subscriber and runs its handler. Handler success
|
|
161
|
+
* auto-acks (a no-op if the handler already acked or the lease rotated);
|
|
162
|
+
* handler failure logs and returns the message for redelivery.
|
|
163
|
+
*/
|
|
164
|
+
const dispatch = (subscriber, message) => {
|
|
165
|
+
const received = toReceivedMessage(message);
|
|
166
|
+
const receipt = message.receiptHandle;
|
|
167
|
+
void (async () => {
|
|
168
|
+
try {
|
|
169
|
+
await subscriber.handler(received);
|
|
170
|
+
if (message.receiptHandle === receipt && isStored(message)) {
|
|
171
|
+
removeMessage(message);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
logger.warn(`Memory queue "${queueName}": subscriber handler failed for message ${message.id} — scheduling redelivery in ${config.handlerFailureRedeliveryDelaySeconds}s`, error);
|
|
176
|
+
if (message.receiptHandle === receipt && isStored(message)) {
|
|
177
|
+
message.leaseExpiresAt = null;
|
|
178
|
+
// An uncaught throw is an UNPLANNED failure (e.g. a transient
|
|
179
|
+
// downstream 503) — give it a real retry window instead of the
|
|
180
|
+
// instant redelivery an explicit nack() gets. With the default
|
|
181
|
+
// maxReceiveCount of 3, a 0-delay default here would burn every
|
|
182
|
+
// attempt within milliseconds and drop the message with no real
|
|
183
|
+
// chance to recover.
|
|
184
|
+
message.visibleAt = Date.now() + config.handlerFailureRedeliveryDelaySeconds * 1000;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
finally {
|
|
188
|
+
subscriber.active -= 1;
|
|
189
|
+
pump();
|
|
190
|
+
}
|
|
191
|
+
})();
|
|
192
|
+
};
|
|
193
|
+
/**
|
|
194
|
+
* Arms the single wake timer for the earliest future event (delayed
|
|
195
|
+
* visibility or lease expiry) — only while someone (subscriber or long-poll
|
|
196
|
+
* waiter) is waiting for delivery; a fully idle queue holds no timer.
|
|
197
|
+
*/
|
|
198
|
+
const scheduleNextWake = () => {
|
|
199
|
+
if (wakeTimer) {
|
|
200
|
+
clearTimeout(wakeTimer);
|
|
201
|
+
wakeTimer = null;
|
|
202
|
+
}
|
|
203
|
+
if (closed)
|
|
204
|
+
return;
|
|
205
|
+
if (subscribers.size === 0 && waiters.size === 0)
|
|
206
|
+
return;
|
|
207
|
+
const now = Date.now();
|
|
208
|
+
let next = Infinity;
|
|
209
|
+
for (const message of messages) {
|
|
210
|
+
if (message.leaseExpiresAt !== null && message.leaseExpiresAt > now) {
|
|
211
|
+
next = Math.min(next, message.leaseExpiresAt);
|
|
212
|
+
}
|
|
213
|
+
else if (message.visibleAt > now) {
|
|
214
|
+
next = Math.min(next, message.visibleAt);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
if (!Number.isFinite(next))
|
|
218
|
+
return;
|
|
219
|
+
wakeTimer = setTimeout(() => {
|
|
220
|
+
wakeTimer = null;
|
|
221
|
+
pump();
|
|
222
|
+
}, Math.max(1, next - now));
|
|
223
|
+
wakeTimer.unref?.();
|
|
224
|
+
};
|
|
225
|
+
/**
|
|
226
|
+
* Delivery engine: dispatches deliverable messages to subscribers with free
|
|
227
|
+
* capacity, satisfies pending long-poll waiters, then re-arms the wake
|
|
228
|
+
* timer. Called after every state change.
|
|
229
|
+
*/
|
|
230
|
+
const pump = () => {
|
|
231
|
+
if (closed)
|
|
232
|
+
return;
|
|
233
|
+
for (const subscriber of subscribers.values()) {
|
|
234
|
+
while (subscriber.active < subscriber.concurrency) {
|
|
235
|
+
const [message] = leaseMessages(1, subscriber.visibilityTimeoutMs);
|
|
236
|
+
if (!message)
|
|
237
|
+
break;
|
|
238
|
+
subscriber.active += 1;
|
|
239
|
+
dispatch(subscriber, message);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
for (const waiter of [...waiters]) {
|
|
243
|
+
const leased = leaseMessages(waiter.maxMessages, waiter.visibilityTimeoutMs);
|
|
244
|
+
if (leased.length > 0) {
|
|
245
|
+
waiters.delete(waiter);
|
|
246
|
+
clearTimeout(waiter.timer);
|
|
247
|
+
waiter.resolve(leased.map((message) => toReceivedMessage(message)));
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
scheduleNextWake();
|
|
251
|
+
};
|
|
252
|
+
const assertOpen = () => {
|
|
253
|
+
if (closed) {
|
|
254
|
+
throw new Error(`Memory queue "${queueName}" is closed`);
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
const queue = {
|
|
258
|
+
name: queueName,
|
|
259
|
+
async send(message) {
|
|
260
|
+
assertOpen();
|
|
261
|
+
const now = Date.now();
|
|
262
|
+
if (config.fifo && message.deduplicationId) {
|
|
263
|
+
pruneDedup(now);
|
|
264
|
+
const existing = dedupIds.get(message.deduplicationId);
|
|
265
|
+
if (existing)
|
|
266
|
+
return existing.id;
|
|
267
|
+
}
|
|
268
|
+
const id = message.id ?? crypto.randomUUID();
|
|
269
|
+
const stored = {
|
|
270
|
+
id,
|
|
271
|
+
// Clone like a real broker serializes — post-send mutations of the
|
|
272
|
+
// caller's object must not affect what consumers receive.
|
|
273
|
+
body: structuredClone(message.body),
|
|
274
|
+
attributes: message.attributes ? { ...message.attributes } : undefined,
|
|
275
|
+
groupId: message.groupId,
|
|
276
|
+
sentAt: new Date(now),
|
|
277
|
+
visibleAt: now + (message.delaySeconds ?? 0) * 1000,
|
|
278
|
+
leaseExpiresAt: null,
|
|
279
|
+
receiveCount: 0,
|
|
280
|
+
receiptHandle: '',
|
|
281
|
+
};
|
|
282
|
+
messages.push(stored);
|
|
283
|
+
if (config.fifo && message.deduplicationId) {
|
|
284
|
+
dedupIds.set(message.deduplicationId, { id, expiresAt: now + DEDUP_WINDOW_MS });
|
|
285
|
+
}
|
|
286
|
+
pump();
|
|
287
|
+
return id;
|
|
288
|
+
},
|
|
289
|
+
async sendBatch(batch) {
|
|
290
|
+
const ids = [];
|
|
291
|
+
for (const message of batch) {
|
|
292
|
+
ids.push(await this.send(message));
|
|
293
|
+
}
|
|
294
|
+
return ids;
|
|
295
|
+
},
|
|
296
|
+
async receive(options) {
|
|
297
|
+
if (closed)
|
|
298
|
+
return [];
|
|
299
|
+
const maxMessages = options?.maxMessages ?? 10;
|
|
300
|
+
const visibilityTimeoutMs = (options?.visibilityTimeout ?? config.defaultVisibilityTimeoutSeconds) * 1000;
|
|
301
|
+
const leased = leaseMessages(maxMessages, visibilityTimeoutMs);
|
|
302
|
+
if (leased.length > 0 || !options?.waitTimeSeconds) {
|
|
303
|
+
scheduleNextWake();
|
|
304
|
+
return leased.map((message) => toReceivedMessage(message));
|
|
305
|
+
}
|
|
306
|
+
// Long poll: resolve as soon as a message becomes available, or with
|
|
307
|
+
// `[]` when `waitTimeSeconds` elapses (or the queue closes).
|
|
308
|
+
const waitTimeMs = options.waitTimeSeconds * 1000;
|
|
309
|
+
return new Promise((resolve) => {
|
|
310
|
+
const waiter = {
|
|
311
|
+
maxMessages,
|
|
312
|
+
visibilityTimeoutMs,
|
|
313
|
+
resolve: resolve,
|
|
314
|
+
timer: setTimeout(() => {
|
|
315
|
+
waiters.delete(waiter);
|
|
316
|
+
resolve([]);
|
|
317
|
+
}, waitTimeMs),
|
|
318
|
+
};
|
|
319
|
+
waiter.timer.unref?.();
|
|
320
|
+
waiters.add(waiter);
|
|
321
|
+
scheduleNextWake();
|
|
322
|
+
});
|
|
323
|
+
},
|
|
324
|
+
subscribe(handler, options) {
|
|
325
|
+
assertOpen();
|
|
326
|
+
const subscriberId = crypto.randomUUID();
|
|
327
|
+
const subscriber = {
|
|
328
|
+
handler: handler,
|
|
329
|
+
concurrency: options?.maxMessages ?? 1,
|
|
330
|
+
visibilityTimeoutMs: (options?.visibilityTimeout ?? config.defaultVisibilityTimeoutSeconds) * 1000,
|
|
331
|
+
active: 0,
|
|
332
|
+
};
|
|
333
|
+
subscribers.set(subscriberId, subscriber);
|
|
334
|
+
pump();
|
|
335
|
+
return () => {
|
|
336
|
+
subscribers.delete(subscriberId);
|
|
337
|
+
scheduleNextWake();
|
|
338
|
+
};
|
|
339
|
+
},
|
|
340
|
+
async size() {
|
|
341
|
+
// Ready + delayed + in-flight, mirroring the Redis bond's
|
|
342
|
+
// waiting + active + delayed job counts.
|
|
343
|
+
return messages.length;
|
|
344
|
+
},
|
|
345
|
+
async purge() {
|
|
346
|
+
messages.length = 0;
|
|
347
|
+
scheduleNextWake();
|
|
348
|
+
},
|
|
349
|
+
};
|
|
350
|
+
const close = () => {
|
|
351
|
+
if (closed)
|
|
352
|
+
return;
|
|
353
|
+
closed = true;
|
|
354
|
+
if (wakeTimer) {
|
|
355
|
+
clearTimeout(wakeTimer);
|
|
356
|
+
wakeTimer = null;
|
|
357
|
+
}
|
|
358
|
+
for (const waiter of waiters) {
|
|
359
|
+
clearTimeout(waiter.timer);
|
|
360
|
+
waiter.resolve([]);
|
|
361
|
+
}
|
|
362
|
+
waiters.clear();
|
|
363
|
+
subscribers.clear();
|
|
364
|
+
messages.length = 0;
|
|
365
|
+
dedupIds.clear();
|
|
366
|
+
};
|
|
367
|
+
return { queue, close };
|
|
368
|
+
};
|
|
369
|
+
//# sourceMappingURL=queue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queue.js","sourceRoot":"","sources":["../src/queue.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for the in-memory queue provider.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import type { Queue, QueueCreateOptions } from '@molecule/api-queue';
|
|
7
|
+
export type { MessageHandler, Queue, QueueCreateOptions, QueueMessage, QueueProvider, ReceivedMessage, ReceiveOptions, } from '@molecule/api-queue';
|
|
8
|
+
/**
|
|
9
|
+
* Options for creating an in-memory queue provider.
|
|
10
|
+
*
|
|
11
|
+
* All options have working defaults — the provider is fully functional with
|
|
12
|
+
* zero configuration and zero environment variables.
|
|
13
|
+
*/
|
|
14
|
+
export interface MemoryQueueOptions {
|
|
15
|
+
/**
|
|
16
|
+
* Default visibility timeout in seconds applied to received/dispatched
|
|
17
|
+
* messages when `ReceiveOptions.visibilityTimeout` is not given.
|
|
18
|
+
* A leased (in-flight) message whose lease expires without an `ack()`
|
|
19
|
+
* becomes visible again and is redelivered (at-least-once delivery).
|
|
20
|
+
* Defaults to `30`.
|
|
21
|
+
*/
|
|
22
|
+
visibilityTimeoutSeconds?: number;
|
|
23
|
+
/**
|
|
24
|
+
* Maximum number of times a message may be delivered before it is routed
|
|
25
|
+
* to the queue's dead-letter queue (when configured via
|
|
26
|
+
* `QueueCreateOptions.deadLetterQueue`) or dropped with an error log.
|
|
27
|
+
* Mirrors the Redis bond's `attempts: 3`. Defaults to `3`.
|
|
28
|
+
*/
|
|
29
|
+
maxReceiveCount?: number;
|
|
30
|
+
/**
|
|
31
|
+
* Delay in seconds before a message is redelivered after an explicit
|
|
32
|
+
* `nack()` (a pull `receive()` consumer's deliberate "put this back now").
|
|
33
|
+
* Defaults to `0` (immediate redelivery) — an explicit `nack()` is a
|
|
34
|
+
* caller decision that should be honored right away, not throttled.
|
|
35
|
+
*/
|
|
36
|
+
redeliveryDelaySeconds?: number;
|
|
37
|
+
/**
|
|
38
|
+
* Delay in seconds before a message is redelivered after an UNCAUGHT
|
|
39
|
+
* `subscribe()` handler failure (a thrown error) — distinct from
|
|
40
|
+
* `redeliveryDelaySeconds` because a throw is an unplanned failure (e.g. a
|
|
41
|
+
* downstream 503) that deserves a real retry window, mirroring the Redis
|
|
42
|
+
* bond's `attempts: 3, backoff: { type: 'exponential', delay: 1000 }`.
|
|
43
|
+
* Defaults to `1`. With the default `maxReceiveCount` of `3`, a
|
|
44
|
+
* `redeliveryDelaySeconds` of `0` would burn all delivery attempts within
|
|
45
|
+
* milliseconds and drop the message with no real chance for a transient
|
|
46
|
+
* downstream failure to recover — this option exists so "retry" means a
|
|
47
|
+
* few real seconds apart, not a hot loop.
|
|
48
|
+
*/
|
|
49
|
+
handlerFailureRedeliveryDelaySeconds?: number;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Internal configuration for a single in-memory queue instance, resolved by
|
|
53
|
+
* the provider from `MemoryQueueOptions` and per-queue `QueueCreateOptions`.
|
|
54
|
+
*/
|
|
55
|
+
export interface MemoryQueueConfig {
|
|
56
|
+
/**
|
|
57
|
+
* Default visibility timeout in seconds for leases without an explicit
|
|
58
|
+
* `ReceiveOptions.visibilityTimeout`.
|
|
59
|
+
*/
|
|
60
|
+
defaultVisibilityTimeoutSeconds: number;
|
|
61
|
+
/**
|
|
62
|
+
* Maximum deliveries before dead-lettering/dropping. Overridden per queue
|
|
63
|
+
* by `deadLetterQueue.maxReceiveCount` when a dead-letter queue is set.
|
|
64
|
+
*/
|
|
65
|
+
maxReceiveCount: number;
|
|
66
|
+
/**
|
|
67
|
+
* Delay in seconds before redelivery after an explicit `nack()`.
|
|
68
|
+
*/
|
|
69
|
+
redeliveryDelaySeconds: number;
|
|
70
|
+
/**
|
|
71
|
+
* Delay in seconds before redelivery after an uncaught `subscribe()`
|
|
72
|
+
* handler failure.
|
|
73
|
+
*/
|
|
74
|
+
handlerFailureRedeliveryDelaySeconds: number;
|
|
75
|
+
/**
|
|
76
|
+
* Whether this queue enforces FIFO semantics (per-`groupId` ordered,
|
|
77
|
+
* head-of-line-blocking delivery plus `deduplicationId` deduplication).
|
|
78
|
+
*/
|
|
79
|
+
fifo: boolean;
|
|
80
|
+
/**
|
|
81
|
+
* Optional retention period in seconds; messages older than this are
|
|
82
|
+
* discarded when next scanned.
|
|
83
|
+
*/
|
|
84
|
+
messageRetentionSeconds?: number;
|
|
85
|
+
/**
|
|
86
|
+
* Optional dead-letter queue for messages exceeding the delivery cap.
|
|
87
|
+
*/
|
|
88
|
+
deadLetterQueue?: QueueCreateOptions['deadLetterQueue'];
|
|
89
|
+
/**
|
|
90
|
+
* Resolves another queue by name — used to route dead-lettered messages.
|
|
91
|
+
*/
|
|
92
|
+
resolveQueue: (name: string) => Queue;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Handle pairing a `Queue` with the internal lifecycle control the provider
|
|
96
|
+
* uses to shut it down (`close()` is not part of the core `Queue` interface).
|
|
97
|
+
*/
|
|
98
|
+
export interface MemoryQueueHandle {
|
|
99
|
+
/**
|
|
100
|
+
* The in-memory queue implementation.
|
|
101
|
+
*/
|
|
102
|
+
queue: Queue;
|
|
103
|
+
/**
|
|
104
|
+
* Stops all timers, resolves pending long-polls with `[]`, discards all
|
|
105
|
+
* messages and subscribers, and rejects further sends.
|
|
106
|
+
*/
|
|
107
|
+
close(): void;
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AAGpE,YAAY,EACV,cAAc,EACd,KAAK,EACL,kBAAkB,EAClB,YAAY,EACZ,aAAa,EACb,eAAe,EACf,cAAc,GACf,MAAM,qBAAqB,CAAA;AAE5B;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;;;;OAMG;IACH,wBAAwB,CAAC,EAAE,MAAM,CAAA;IAEjC;;;;;OAKG;IACH,eAAe,CAAC,EAAE,MAAM,CAAA;IAExB;;;;;OAKG;IACH,sBAAsB,CAAC,EAAE,MAAM,CAAA;IAE/B;;;;;;;;;;;OAWG;IACH,oCAAoC,CAAC,EAAE,MAAM,CAAA;CAC9C;AAED;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;OAGG;IACH,+BAA+B,EAAE,MAAM,CAAA;IAEvC;;;OAGG;IACH,eAAe,EAAE,MAAM,CAAA;IAEvB;;OAEG;IACH,sBAAsB,EAAE,MAAM,CAAA;IAE9B;;;OAGG;IACH,oCAAoC,EAAE,MAAM,CAAA;IAE5C;;;OAGG;IACH,IAAI,EAAE,OAAO,CAAA;IAEb;;;OAGG;IACH,uBAAuB,CAAC,EAAE,MAAM,CAAA;IAEhC;;OAEG;IACH,eAAe,CAAC,EAAE,kBAAkB,CAAC,iBAAiB,CAAC,CAAA;IAEvD;;OAEG;IACH,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,KAAK,CAAA;CACtC;AAED;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC;;OAEG;IACH,KAAK,EAAE,KAAK,CAAA;IAEZ;;;OAGG;IACH,KAAK,IAAI,IAAI,CAAA;CACd"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@molecule/api-queue-memory",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "In-memory queue provider for molecule.dev.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "tsc",
|
|
10
|
+
"test": "vitest run",
|
|
11
|
+
"test:watch": "vitest"
|
|
12
|
+
},
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"import": "./dist/index.js"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist"
|
|
21
|
+
],
|
|
22
|
+
"keywords": [
|
|
23
|
+
"molecule",
|
|
24
|
+
"queue",
|
|
25
|
+
"memory",
|
|
26
|
+
"messaging"
|
|
27
|
+
],
|
|
28
|
+
"license": "Apache-2.0",
|
|
29
|
+
"peerDependencies": {
|
|
30
|
+
"@molecule/api-bond": "^1.0.0",
|
|
31
|
+
"@molecule/api-queue": "^1.0.0"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/node": "26.1.2",
|
|
35
|
+
"typescript": "6.0.3",
|
|
36
|
+
"vitest": "4.1.10"
|
|
37
|
+
},
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "https://github.com/molecule-dev/molecule.git",
|
|
41
|
+
"directory": "packages/api/bonds/queue/memory"
|
|
42
|
+
},
|
|
43
|
+
"homepage": "https://github.com/molecule-dev/molecule/tree/main/packages/api/bonds/queue/memory",
|
|
44
|
+
"bugs": "https://github.com/molecule-dev/molecule/issues",
|
|
45
|
+
"publishConfig": {
|
|
46
|
+
"access": "public"
|
|
47
|
+
}
|
|
48
|
+
}
|