@saga-bus/transport-inmemory 0.1.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/README.md +83 -0
- package/dist/index.cjs +186 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +68 -0
- package/dist/index.d.ts +68 -0
- package/dist/index.js +158 -0
- package/dist/index.js.map +1 -0
- package/package.json +61 -0
package/README.md
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# @saga-bus/transport-inmemory
|
|
2
|
+
|
|
3
|
+
In-memory transport implementation for testing and development.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @saga-bus/transport-inmemory
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { InMemoryTransport } from "@saga-bus/transport-inmemory";
|
|
15
|
+
import { createBus } from "@saga-bus/core";
|
|
16
|
+
|
|
17
|
+
const transport = new InMemoryTransport({
|
|
18
|
+
defaultConcurrency: 10,
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const bus = createBus({
|
|
22
|
+
transport,
|
|
23
|
+
sagas: [...],
|
|
24
|
+
});
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Features
|
|
28
|
+
|
|
29
|
+
- Zero external dependencies
|
|
30
|
+
- Configurable concurrency via semaphore
|
|
31
|
+
- Synchronous message delivery (great for tests)
|
|
32
|
+
- No network overhead
|
|
33
|
+
|
|
34
|
+
## Configuration
|
|
35
|
+
|
|
36
|
+
| Option | Type | Default | Description |
|
|
37
|
+
|--------|------|---------|-------------|
|
|
38
|
+
| `defaultConcurrency` | `number` | `10` | Max concurrent handlers |
|
|
39
|
+
|
|
40
|
+
## When to Use
|
|
41
|
+
|
|
42
|
+
**Use for:**
|
|
43
|
+
- Unit and integration tests
|
|
44
|
+
- Local development
|
|
45
|
+
- Prototyping and demos
|
|
46
|
+
|
|
47
|
+
**Do not use for:**
|
|
48
|
+
- Production deployments
|
|
49
|
+
- Distributed systems
|
|
50
|
+
- Multi-process applications
|
|
51
|
+
|
|
52
|
+
## Testing Tips
|
|
53
|
+
|
|
54
|
+
The in-memory transport processes messages synchronously within the same process, making it ideal for testing:
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
import { TestHarness } from "@saga-bus/test";
|
|
58
|
+
import { InMemoryTransport } from "@saga-bus/transport-inmemory";
|
|
59
|
+
|
|
60
|
+
// TestHarness uses InMemoryTransport internally
|
|
61
|
+
const harness = await TestHarness.create({
|
|
62
|
+
sagas: [{ definition: mySaga, store }],
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// Messages are processed immediately
|
|
66
|
+
await harness.publish({ type: "OrderSubmitted", orderId: "123" });
|
|
67
|
+
await harness.waitForIdle();
|
|
68
|
+
|
|
69
|
+
// State is immediately available
|
|
70
|
+
const state = await harness.getSagaState("OrderSaga", "123");
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Limitations
|
|
74
|
+
|
|
75
|
+
- **Single process only**: Messages are not shared between processes
|
|
76
|
+
- **No persistence**: Messages are lost if not consumed
|
|
77
|
+
- **No ordering guarantees**: Unlike production transports with FIFO support
|
|
78
|
+
|
|
79
|
+
For production, use [@saga-bus/transport-rabbitmq](../transport-rabbitmq), [@saga-bus/transport-sqs](../transport-sqs), or [@saga-bus/transport-kafka](../transport-kafka).
|
|
80
|
+
|
|
81
|
+
## License
|
|
82
|
+
|
|
83
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
InMemoryTransport: () => InMemoryTransport,
|
|
24
|
+
Semaphore: () => Semaphore
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(index_exports);
|
|
27
|
+
|
|
28
|
+
// src/InMemoryTransport.ts
|
|
29
|
+
var import_node_crypto = require("crypto");
|
|
30
|
+
|
|
31
|
+
// src/Semaphore.ts
|
|
32
|
+
var Semaphore = class {
|
|
33
|
+
permits;
|
|
34
|
+
waiting = [];
|
|
35
|
+
constructor(permits) {
|
|
36
|
+
this.permits = permits;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Acquire a permit, waiting if none are available.
|
|
40
|
+
*/
|
|
41
|
+
async acquire() {
|
|
42
|
+
if (this.permits > 0) {
|
|
43
|
+
this.permits--;
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
return new Promise((resolve) => {
|
|
47
|
+
this.waiting.push(resolve);
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Release a permit, waking a waiting acquirer if any.
|
|
52
|
+
*/
|
|
53
|
+
release() {
|
|
54
|
+
const next = this.waiting.shift();
|
|
55
|
+
if (next) {
|
|
56
|
+
next();
|
|
57
|
+
} else {
|
|
58
|
+
this.permits++;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Execute a function with a permit.
|
|
63
|
+
*/
|
|
64
|
+
async withPermit(fn) {
|
|
65
|
+
await this.acquire();
|
|
66
|
+
try {
|
|
67
|
+
return await fn();
|
|
68
|
+
} finally {
|
|
69
|
+
this.release();
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Get the number of available permits.
|
|
74
|
+
*/
|
|
75
|
+
get available() {
|
|
76
|
+
return this.permits;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Get the number of waiting acquirers.
|
|
80
|
+
*/
|
|
81
|
+
get waitingCount() {
|
|
82
|
+
return this.waiting.length;
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
// src/InMemoryTransport.ts
|
|
87
|
+
var InMemoryTransport = class {
|
|
88
|
+
subscriptions = /* @__PURE__ */ new Map();
|
|
89
|
+
pendingTimeouts = /* @__PURE__ */ new Set();
|
|
90
|
+
defaultConcurrency;
|
|
91
|
+
started = false;
|
|
92
|
+
constructor(options = {}) {
|
|
93
|
+
this.defaultConcurrency = options.defaultConcurrency ?? 1;
|
|
94
|
+
}
|
|
95
|
+
async start() {
|
|
96
|
+
this.started = true;
|
|
97
|
+
}
|
|
98
|
+
async stop() {
|
|
99
|
+
this.started = false;
|
|
100
|
+
this.pendingTimeouts.forEach((timeout) => clearTimeout(timeout));
|
|
101
|
+
this.pendingTimeouts.clear();
|
|
102
|
+
}
|
|
103
|
+
async subscribe(options, handler) {
|
|
104
|
+
const { endpoint, concurrency = this.defaultConcurrency } = options;
|
|
105
|
+
const subscription = {
|
|
106
|
+
options,
|
|
107
|
+
handler,
|
|
108
|
+
semaphore: new Semaphore(concurrency)
|
|
109
|
+
};
|
|
110
|
+
const existing = this.subscriptions.get(endpoint);
|
|
111
|
+
if (existing) {
|
|
112
|
+
existing.push(subscription);
|
|
113
|
+
} else {
|
|
114
|
+
this.subscriptions.set(endpoint, [subscription]);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
async publish(message, options) {
|
|
118
|
+
const { endpoint, headers = {}, delayMs } = options;
|
|
119
|
+
const envelope = {
|
|
120
|
+
id: (0, import_node_crypto.randomUUID)(),
|
|
121
|
+
type: message.type,
|
|
122
|
+
payload: message,
|
|
123
|
+
headers: { ...headers },
|
|
124
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
125
|
+
partitionKey: options.key
|
|
126
|
+
};
|
|
127
|
+
if (delayMs && delayMs > 0) {
|
|
128
|
+
const timeout = setTimeout(() => {
|
|
129
|
+
this.pendingTimeouts.delete(timeout);
|
|
130
|
+
void this.deliverToSubscribers(endpoint, envelope);
|
|
131
|
+
}, delayMs);
|
|
132
|
+
this.pendingTimeouts.add(timeout);
|
|
133
|
+
} else {
|
|
134
|
+
setImmediate(() => {
|
|
135
|
+
void this.deliverToSubscribers(endpoint, envelope);
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
async deliverToSubscribers(endpoint, envelope) {
|
|
140
|
+
if (!this.started) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
const subscriptions = this.subscriptions.get(endpoint);
|
|
144
|
+
if (!subscriptions || subscriptions.length === 0) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
const deliveries = subscriptions.map(async (sub) => {
|
|
148
|
+
await sub.semaphore.withPermit(async () => {
|
|
149
|
+
try {
|
|
150
|
+
await sub.handler(envelope);
|
|
151
|
+
} catch (error) {
|
|
152
|
+
console.error(
|
|
153
|
+
`[InMemoryTransport] Handler error for ${endpoint}:`,
|
|
154
|
+
error
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
await Promise.all(deliveries);
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Get the number of subscriptions for an endpoint.
|
|
163
|
+
* Useful for testing.
|
|
164
|
+
*/
|
|
165
|
+
getSubscriptionCount(endpoint) {
|
|
166
|
+
return this.subscriptions.get(endpoint)?.length ?? 0;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Check if the transport is started.
|
|
170
|
+
*/
|
|
171
|
+
get isStarted() {
|
|
172
|
+
return this.started;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Clear all subscriptions. Useful for testing.
|
|
176
|
+
*/
|
|
177
|
+
clearSubscriptions() {
|
|
178
|
+
this.subscriptions.clear();
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
182
|
+
0 && (module.exports = {
|
|
183
|
+
InMemoryTransport,
|
|
184
|
+
Semaphore
|
|
185
|
+
});
|
|
186
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/InMemoryTransport.ts","../src/Semaphore.ts"],"sourcesContent":["export { InMemoryTransport } from \"./InMemoryTransport.js\";\nexport type { InMemoryTransportOptions } from \"./InMemoryTransport.js\";\nexport { Semaphore } from \"./Semaphore.js\";\n","import { randomUUID } from \"node:crypto\";\nimport type {\n Transport,\n TransportSubscribeOptions,\n TransportPublishOptions,\n BaseMessage,\n MessageEnvelope,\n} from \"@saga-bus/core\";\nimport { Semaphore } from \"./Semaphore.js\";\n\ninterface Subscription<T extends BaseMessage = BaseMessage> {\n options: TransportSubscribeOptions;\n handler: (envelope: MessageEnvelope<T>) => Promise<void>;\n semaphore: Semaphore;\n}\n\nexport interface InMemoryTransportOptions {\n /**\n * Default concurrency for subscriptions (default: 1)\n */\n defaultConcurrency?: number;\n}\n\n/**\n * In-memory transport implementation for testing and local development.\n * Uses a simple pub/sub pattern with concurrency control.\n */\nexport class InMemoryTransport implements Transport {\n private readonly subscriptions = new Map<string, Subscription[]>();\n private readonly pendingTimeouts = new Set<ReturnType<typeof setTimeout>>();\n private readonly defaultConcurrency: number;\n private started = false;\n\n constructor(options: InMemoryTransportOptions = {}) {\n this.defaultConcurrency = options.defaultConcurrency ?? 1;\n }\n\n async start(): Promise<void> {\n this.started = true;\n }\n\n async stop(): Promise<void> {\n this.started = false;\n\n // Clear all pending delayed messages\n this.pendingTimeouts.forEach((timeout) => clearTimeout(timeout));\n this.pendingTimeouts.clear();\n }\n\n async subscribe<TMessage extends BaseMessage>(\n options: TransportSubscribeOptions,\n handler: (envelope: MessageEnvelope<TMessage>) => Promise<void>\n ): Promise<void> {\n const { endpoint, concurrency = this.defaultConcurrency } = options;\n\n const subscription: Subscription<TMessage> = {\n options,\n handler,\n semaphore: new Semaphore(concurrency),\n };\n\n const existing = this.subscriptions.get(endpoint);\n if (existing) {\n existing.push(subscription as Subscription);\n } else {\n this.subscriptions.set(endpoint, [subscription as Subscription]);\n }\n }\n\n async publish<TMessage extends BaseMessage>(\n message: TMessage,\n options: TransportPublishOptions\n ): Promise<void> {\n const { endpoint, headers = {}, delayMs } = options;\n\n const envelope: MessageEnvelope<TMessage> = {\n id: randomUUID(),\n type: message.type,\n payload: message,\n headers: { ...headers },\n timestamp: new Date(),\n partitionKey: options.key,\n };\n\n if (delayMs && delayMs > 0) {\n const timeout = setTimeout(() => {\n this.pendingTimeouts.delete(timeout);\n void this.deliverToSubscribers(endpoint, envelope);\n }, delayMs);\n this.pendingTimeouts.add(timeout);\n } else {\n // Deliver asynchronously to avoid blocking publisher\n setImmediate(() => {\n void this.deliverToSubscribers(endpoint, envelope);\n });\n }\n }\n\n private async deliverToSubscribers<TMessage extends BaseMessage>(\n endpoint: string,\n envelope: MessageEnvelope<TMessage>\n ): Promise<void> {\n if (!this.started) {\n return;\n }\n\n const subscriptions = this.subscriptions.get(endpoint);\n if (!subscriptions || subscriptions.length === 0) {\n return;\n }\n\n // Deliver to all subscriptions (fan-out)\n // Each subscription handles its own concurrency\n const deliveries = subscriptions.map(async (sub) => {\n await sub.semaphore.withPermit(async () => {\n try {\n await sub.handler(envelope as MessageEnvelope);\n } catch (error) {\n // In-memory transport doesn't handle errors - let them propagate\n // Real error handling is done by the bus runtime\n console.error(\n `[InMemoryTransport] Handler error for ${endpoint}:`,\n error\n );\n }\n });\n });\n\n await Promise.all(deliveries);\n }\n\n /**\n * Get the number of subscriptions for an endpoint.\n * Useful for testing.\n */\n getSubscriptionCount(endpoint: string): number {\n return this.subscriptions.get(endpoint)?.length ?? 0;\n }\n\n /**\n * Check if the transport is started.\n */\n get isStarted(): boolean {\n return this.started;\n }\n\n /**\n * Clear all subscriptions. Useful for testing.\n */\n clearSubscriptions(): void {\n this.subscriptions.clear();\n }\n}\n","/**\n * Simple semaphore for controlling concurrency.\n */\nexport class Semaphore {\n private permits: number;\n private waiting: Array<() => void> = [];\n\n constructor(permits: number) {\n this.permits = permits;\n }\n\n /**\n * Acquire a permit, waiting if none are available.\n */\n async acquire(): Promise<void> {\n if (this.permits > 0) {\n this.permits--;\n return;\n }\n\n return new Promise<void>((resolve) => {\n this.waiting.push(resolve);\n });\n }\n\n /**\n * Release a permit, waking a waiting acquirer if any.\n */\n release(): void {\n const next = this.waiting.shift();\n if (next) {\n next();\n } else {\n this.permits++;\n }\n }\n\n /**\n * Execute a function with a permit.\n */\n async withPermit<T>(fn: () => Promise<T>): Promise<T> {\n await this.acquire();\n try {\n return await fn();\n } finally {\n this.release();\n }\n }\n\n /**\n * Get the number of available permits.\n */\n get available(): number {\n return this.permits;\n }\n\n /**\n * Get the number of waiting acquirers.\n */\n get waitingCount(): number {\n return this.waiting.length;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,yBAA2B;;;ACGpB,IAAM,YAAN,MAAgB;AAAA,EACb;AAAA,EACA,UAA6B,CAAC;AAAA,EAEtC,YAAY,SAAiB;AAC3B,SAAK,UAAU;AAAA,EACjB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,UAAyB;AAC7B,QAAI,KAAK,UAAU,GAAG;AACpB,WAAK;AACL;AAAA,IACF;AAEA,WAAO,IAAI,QAAc,CAAC,YAAY;AACpC,WAAK,QAAQ,KAAK,OAAO;AAAA,IAC3B,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,UAAgB;AACd,UAAM,OAAO,KAAK,QAAQ,MAAM;AAChC,QAAI,MAAM;AACR,WAAK;AAAA,IACP,OAAO;AACL,WAAK;AAAA,IACP;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,WAAc,IAAkC;AACpD,UAAM,KAAK,QAAQ;AACnB,QAAI;AACF,aAAO,MAAM,GAAG;AAAA,IAClB,UAAE;AACA,WAAK,QAAQ;AAAA,IACf;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,YAAoB;AACtB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,eAAuB;AACzB,WAAO,KAAK,QAAQ;AAAA,EACtB;AACF;;;ADnCO,IAAM,oBAAN,MAA6C;AAAA,EACjC,gBAAgB,oBAAI,IAA4B;AAAA,EAChD,kBAAkB,oBAAI,IAAmC;AAAA,EACzD;AAAA,EACT,UAAU;AAAA,EAElB,YAAY,UAAoC,CAAC,GAAG;AAClD,SAAK,qBAAqB,QAAQ,sBAAsB;AAAA,EAC1D;AAAA,EAEA,MAAM,QAAuB;AAC3B,SAAK,UAAU;AAAA,EACjB;AAAA,EAEA,MAAM,OAAsB;AAC1B,SAAK,UAAU;AAGf,SAAK,gBAAgB,QAAQ,CAAC,YAAY,aAAa,OAAO,CAAC;AAC/D,SAAK,gBAAgB,MAAM;AAAA,EAC7B;AAAA,EAEA,MAAM,UACJ,SACA,SACe;AACf,UAAM,EAAE,UAAU,cAAc,KAAK,mBAAmB,IAAI;AAE5D,UAAM,eAAuC;AAAA,MAC3C;AAAA,MACA;AAAA,MACA,WAAW,IAAI,UAAU,WAAW;AAAA,IACtC;AAEA,UAAM,WAAW,KAAK,cAAc,IAAI,QAAQ;AAChD,QAAI,UAAU;AACZ,eAAS,KAAK,YAA4B;AAAA,IAC5C,OAAO;AACL,WAAK,cAAc,IAAI,UAAU,CAAC,YAA4B,CAAC;AAAA,IACjE;AAAA,EACF;AAAA,EAEA,MAAM,QACJ,SACA,SACe;AACf,UAAM,EAAE,UAAU,UAAU,CAAC,GAAG,QAAQ,IAAI;AAE5C,UAAM,WAAsC;AAAA,MAC1C,QAAI,+BAAW;AAAA,MACf,MAAM,QAAQ;AAAA,MACd,SAAS;AAAA,MACT,SAAS,EAAE,GAAG,QAAQ;AAAA,MACtB,WAAW,oBAAI,KAAK;AAAA,MACpB,cAAc,QAAQ;AAAA,IACxB;AAEA,QAAI,WAAW,UAAU,GAAG;AAC1B,YAAM,UAAU,WAAW,MAAM;AAC/B,aAAK,gBAAgB,OAAO,OAAO;AACnC,aAAK,KAAK,qBAAqB,UAAU,QAAQ;AAAA,MACnD,GAAG,OAAO;AACV,WAAK,gBAAgB,IAAI,OAAO;AAAA,IAClC,OAAO;AAEL,mBAAa,MAAM;AACjB,aAAK,KAAK,qBAAqB,UAAU,QAAQ;AAAA,MACnD,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEA,MAAc,qBACZ,UACA,UACe;AACf,QAAI,CAAC,KAAK,SAAS;AACjB;AAAA,IACF;AAEA,UAAM,gBAAgB,KAAK,cAAc,IAAI,QAAQ;AACrD,QAAI,CAAC,iBAAiB,cAAc,WAAW,GAAG;AAChD;AAAA,IACF;AAIA,UAAM,aAAa,cAAc,IAAI,OAAO,QAAQ;AAClD,YAAM,IAAI,UAAU,WAAW,YAAY;AACzC,YAAI;AACF,gBAAM,IAAI,QAAQ,QAA2B;AAAA,QAC/C,SAAS,OAAO;AAGd,kBAAQ;AAAA,YACN,yCAAyC,QAAQ;AAAA,YACjD;AAAA,UACF;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAED,UAAM,QAAQ,IAAI,UAAU;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,qBAAqB,UAA0B;AAC7C,WAAO,KAAK,cAAc,IAAI,QAAQ,GAAG,UAAU;AAAA,EACrD;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,YAAqB;AACvB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,qBAA2B;AACzB,SAAK,cAAc,MAAM;AAAA,EAC3B;AACF;","names":[]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { Transport, BaseMessage, TransportSubscribeOptions, MessageEnvelope, TransportPublishOptions } from '@saga-bus/core';
|
|
2
|
+
|
|
3
|
+
interface InMemoryTransportOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Default concurrency for subscriptions (default: 1)
|
|
6
|
+
*/
|
|
7
|
+
defaultConcurrency?: number;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* In-memory transport implementation for testing and local development.
|
|
11
|
+
* Uses a simple pub/sub pattern with concurrency control.
|
|
12
|
+
*/
|
|
13
|
+
declare class InMemoryTransport implements Transport {
|
|
14
|
+
private readonly subscriptions;
|
|
15
|
+
private readonly pendingTimeouts;
|
|
16
|
+
private readonly defaultConcurrency;
|
|
17
|
+
private started;
|
|
18
|
+
constructor(options?: InMemoryTransportOptions);
|
|
19
|
+
start(): Promise<void>;
|
|
20
|
+
stop(): Promise<void>;
|
|
21
|
+
subscribe<TMessage extends BaseMessage>(options: TransportSubscribeOptions, handler: (envelope: MessageEnvelope<TMessage>) => Promise<void>): Promise<void>;
|
|
22
|
+
publish<TMessage extends BaseMessage>(message: TMessage, options: TransportPublishOptions): Promise<void>;
|
|
23
|
+
private deliverToSubscribers;
|
|
24
|
+
/**
|
|
25
|
+
* Get the number of subscriptions for an endpoint.
|
|
26
|
+
* Useful for testing.
|
|
27
|
+
*/
|
|
28
|
+
getSubscriptionCount(endpoint: string): number;
|
|
29
|
+
/**
|
|
30
|
+
* Check if the transport is started.
|
|
31
|
+
*/
|
|
32
|
+
get isStarted(): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Clear all subscriptions. Useful for testing.
|
|
35
|
+
*/
|
|
36
|
+
clearSubscriptions(): void;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Simple semaphore for controlling concurrency.
|
|
41
|
+
*/
|
|
42
|
+
declare class Semaphore {
|
|
43
|
+
private permits;
|
|
44
|
+
private waiting;
|
|
45
|
+
constructor(permits: number);
|
|
46
|
+
/**
|
|
47
|
+
* Acquire a permit, waiting if none are available.
|
|
48
|
+
*/
|
|
49
|
+
acquire(): Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* Release a permit, waking a waiting acquirer if any.
|
|
52
|
+
*/
|
|
53
|
+
release(): void;
|
|
54
|
+
/**
|
|
55
|
+
* Execute a function with a permit.
|
|
56
|
+
*/
|
|
57
|
+
withPermit<T>(fn: () => Promise<T>): Promise<T>;
|
|
58
|
+
/**
|
|
59
|
+
* Get the number of available permits.
|
|
60
|
+
*/
|
|
61
|
+
get available(): number;
|
|
62
|
+
/**
|
|
63
|
+
* Get the number of waiting acquirers.
|
|
64
|
+
*/
|
|
65
|
+
get waitingCount(): number;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export { InMemoryTransport, type InMemoryTransportOptions, Semaphore };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { Transport, BaseMessage, TransportSubscribeOptions, MessageEnvelope, TransportPublishOptions } from '@saga-bus/core';
|
|
2
|
+
|
|
3
|
+
interface InMemoryTransportOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Default concurrency for subscriptions (default: 1)
|
|
6
|
+
*/
|
|
7
|
+
defaultConcurrency?: number;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* In-memory transport implementation for testing and local development.
|
|
11
|
+
* Uses a simple pub/sub pattern with concurrency control.
|
|
12
|
+
*/
|
|
13
|
+
declare class InMemoryTransport implements Transport {
|
|
14
|
+
private readonly subscriptions;
|
|
15
|
+
private readonly pendingTimeouts;
|
|
16
|
+
private readonly defaultConcurrency;
|
|
17
|
+
private started;
|
|
18
|
+
constructor(options?: InMemoryTransportOptions);
|
|
19
|
+
start(): Promise<void>;
|
|
20
|
+
stop(): Promise<void>;
|
|
21
|
+
subscribe<TMessage extends BaseMessage>(options: TransportSubscribeOptions, handler: (envelope: MessageEnvelope<TMessage>) => Promise<void>): Promise<void>;
|
|
22
|
+
publish<TMessage extends BaseMessage>(message: TMessage, options: TransportPublishOptions): Promise<void>;
|
|
23
|
+
private deliverToSubscribers;
|
|
24
|
+
/**
|
|
25
|
+
* Get the number of subscriptions for an endpoint.
|
|
26
|
+
* Useful for testing.
|
|
27
|
+
*/
|
|
28
|
+
getSubscriptionCount(endpoint: string): number;
|
|
29
|
+
/**
|
|
30
|
+
* Check if the transport is started.
|
|
31
|
+
*/
|
|
32
|
+
get isStarted(): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Clear all subscriptions. Useful for testing.
|
|
35
|
+
*/
|
|
36
|
+
clearSubscriptions(): void;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Simple semaphore for controlling concurrency.
|
|
41
|
+
*/
|
|
42
|
+
declare class Semaphore {
|
|
43
|
+
private permits;
|
|
44
|
+
private waiting;
|
|
45
|
+
constructor(permits: number);
|
|
46
|
+
/**
|
|
47
|
+
* Acquire a permit, waiting if none are available.
|
|
48
|
+
*/
|
|
49
|
+
acquire(): Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* Release a permit, waking a waiting acquirer if any.
|
|
52
|
+
*/
|
|
53
|
+
release(): void;
|
|
54
|
+
/**
|
|
55
|
+
* Execute a function with a permit.
|
|
56
|
+
*/
|
|
57
|
+
withPermit<T>(fn: () => Promise<T>): Promise<T>;
|
|
58
|
+
/**
|
|
59
|
+
* Get the number of available permits.
|
|
60
|
+
*/
|
|
61
|
+
get available(): number;
|
|
62
|
+
/**
|
|
63
|
+
* Get the number of waiting acquirers.
|
|
64
|
+
*/
|
|
65
|
+
get waitingCount(): number;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export { InMemoryTransport, type InMemoryTransportOptions, Semaphore };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
// src/InMemoryTransport.ts
|
|
2
|
+
import { randomUUID } from "crypto";
|
|
3
|
+
|
|
4
|
+
// src/Semaphore.ts
|
|
5
|
+
var Semaphore = class {
|
|
6
|
+
permits;
|
|
7
|
+
waiting = [];
|
|
8
|
+
constructor(permits) {
|
|
9
|
+
this.permits = permits;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Acquire a permit, waiting if none are available.
|
|
13
|
+
*/
|
|
14
|
+
async acquire() {
|
|
15
|
+
if (this.permits > 0) {
|
|
16
|
+
this.permits--;
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
return new Promise((resolve) => {
|
|
20
|
+
this.waiting.push(resolve);
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Release a permit, waking a waiting acquirer if any.
|
|
25
|
+
*/
|
|
26
|
+
release() {
|
|
27
|
+
const next = this.waiting.shift();
|
|
28
|
+
if (next) {
|
|
29
|
+
next();
|
|
30
|
+
} else {
|
|
31
|
+
this.permits++;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Execute a function with a permit.
|
|
36
|
+
*/
|
|
37
|
+
async withPermit(fn) {
|
|
38
|
+
await this.acquire();
|
|
39
|
+
try {
|
|
40
|
+
return await fn();
|
|
41
|
+
} finally {
|
|
42
|
+
this.release();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Get the number of available permits.
|
|
47
|
+
*/
|
|
48
|
+
get available() {
|
|
49
|
+
return this.permits;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Get the number of waiting acquirers.
|
|
53
|
+
*/
|
|
54
|
+
get waitingCount() {
|
|
55
|
+
return this.waiting.length;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// src/InMemoryTransport.ts
|
|
60
|
+
var InMemoryTransport = class {
|
|
61
|
+
subscriptions = /* @__PURE__ */ new Map();
|
|
62
|
+
pendingTimeouts = /* @__PURE__ */ new Set();
|
|
63
|
+
defaultConcurrency;
|
|
64
|
+
started = false;
|
|
65
|
+
constructor(options = {}) {
|
|
66
|
+
this.defaultConcurrency = options.defaultConcurrency ?? 1;
|
|
67
|
+
}
|
|
68
|
+
async start() {
|
|
69
|
+
this.started = true;
|
|
70
|
+
}
|
|
71
|
+
async stop() {
|
|
72
|
+
this.started = false;
|
|
73
|
+
this.pendingTimeouts.forEach((timeout) => clearTimeout(timeout));
|
|
74
|
+
this.pendingTimeouts.clear();
|
|
75
|
+
}
|
|
76
|
+
async subscribe(options, handler) {
|
|
77
|
+
const { endpoint, concurrency = this.defaultConcurrency } = options;
|
|
78
|
+
const subscription = {
|
|
79
|
+
options,
|
|
80
|
+
handler,
|
|
81
|
+
semaphore: new Semaphore(concurrency)
|
|
82
|
+
};
|
|
83
|
+
const existing = this.subscriptions.get(endpoint);
|
|
84
|
+
if (existing) {
|
|
85
|
+
existing.push(subscription);
|
|
86
|
+
} else {
|
|
87
|
+
this.subscriptions.set(endpoint, [subscription]);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
async publish(message, options) {
|
|
91
|
+
const { endpoint, headers = {}, delayMs } = options;
|
|
92
|
+
const envelope = {
|
|
93
|
+
id: randomUUID(),
|
|
94
|
+
type: message.type,
|
|
95
|
+
payload: message,
|
|
96
|
+
headers: { ...headers },
|
|
97
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
98
|
+
partitionKey: options.key
|
|
99
|
+
};
|
|
100
|
+
if (delayMs && delayMs > 0) {
|
|
101
|
+
const timeout = setTimeout(() => {
|
|
102
|
+
this.pendingTimeouts.delete(timeout);
|
|
103
|
+
void this.deliverToSubscribers(endpoint, envelope);
|
|
104
|
+
}, delayMs);
|
|
105
|
+
this.pendingTimeouts.add(timeout);
|
|
106
|
+
} else {
|
|
107
|
+
setImmediate(() => {
|
|
108
|
+
void this.deliverToSubscribers(endpoint, envelope);
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
async deliverToSubscribers(endpoint, envelope) {
|
|
113
|
+
if (!this.started) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
const subscriptions = this.subscriptions.get(endpoint);
|
|
117
|
+
if (!subscriptions || subscriptions.length === 0) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const deliveries = subscriptions.map(async (sub) => {
|
|
121
|
+
await sub.semaphore.withPermit(async () => {
|
|
122
|
+
try {
|
|
123
|
+
await sub.handler(envelope);
|
|
124
|
+
} catch (error) {
|
|
125
|
+
console.error(
|
|
126
|
+
`[InMemoryTransport] Handler error for ${endpoint}:`,
|
|
127
|
+
error
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
await Promise.all(deliveries);
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Get the number of subscriptions for an endpoint.
|
|
136
|
+
* Useful for testing.
|
|
137
|
+
*/
|
|
138
|
+
getSubscriptionCount(endpoint) {
|
|
139
|
+
return this.subscriptions.get(endpoint)?.length ?? 0;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Check if the transport is started.
|
|
143
|
+
*/
|
|
144
|
+
get isStarted() {
|
|
145
|
+
return this.started;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Clear all subscriptions. Useful for testing.
|
|
149
|
+
*/
|
|
150
|
+
clearSubscriptions() {
|
|
151
|
+
this.subscriptions.clear();
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
export {
|
|
155
|
+
InMemoryTransport,
|
|
156
|
+
Semaphore
|
|
157
|
+
};
|
|
158
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/InMemoryTransport.ts","../src/Semaphore.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport type {\n Transport,\n TransportSubscribeOptions,\n TransportPublishOptions,\n BaseMessage,\n MessageEnvelope,\n} from \"@saga-bus/core\";\nimport { Semaphore } from \"./Semaphore.js\";\n\ninterface Subscription<T extends BaseMessage = BaseMessage> {\n options: TransportSubscribeOptions;\n handler: (envelope: MessageEnvelope<T>) => Promise<void>;\n semaphore: Semaphore;\n}\n\nexport interface InMemoryTransportOptions {\n /**\n * Default concurrency for subscriptions (default: 1)\n */\n defaultConcurrency?: number;\n}\n\n/**\n * In-memory transport implementation for testing and local development.\n * Uses a simple pub/sub pattern with concurrency control.\n */\nexport class InMemoryTransport implements Transport {\n private readonly subscriptions = new Map<string, Subscription[]>();\n private readonly pendingTimeouts = new Set<ReturnType<typeof setTimeout>>();\n private readonly defaultConcurrency: number;\n private started = false;\n\n constructor(options: InMemoryTransportOptions = {}) {\n this.defaultConcurrency = options.defaultConcurrency ?? 1;\n }\n\n async start(): Promise<void> {\n this.started = true;\n }\n\n async stop(): Promise<void> {\n this.started = false;\n\n // Clear all pending delayed messages\n this.pendingTimeouts.forEach((timeout) => clearTimeout(timeout));\n this.pendingTimeouts.clear();\n }\n\n async subscribe<TMessage extends BaseMessage>(\n options: TransportSubscribeOptions,\n handler: (envelope: MessageEnvelope<TMessage>) => Promise<void>\n ): Promise<void> {\n const { endpoint, concurrency = this.defaultConcurrency } = options;\n\n const subscription: Subscription<TMessage> = {\n options,\n handler,\n semaphore: new Semaphore(concurrency),\n };\n\n const existing = this.subscriptions.get(endpoint);\n if (existing) {\n existing.push(subscription as Subscription);\n } else {\n this.subscriptions.set(endpoint, [subscription as Subscription]);\n }\n }\n\n async publish<TMessage extends BaseMessage>(\n message: TMessage,\n options: TransportPublishOptions\n ): Promise<void> {\n const { endpoint, headers = {}, delayMs } = options;\n\n const envelope: MessageEnvelope<TMessage> = {\n id: randomUUID(),\n type: message.type,\n payload: message,\n headers: { ...headers },\n timestamp: new Date(),\n partitionKey: options.key,\n };\n\n if (delayMs && delayMs > 0) {\n const timeout = setTimeout(() => {\n this.pendingTimeouts.delete(timeout);\n void this.deliverToSubscribers(endpoint, envelope);\n }, delayMs);\n this.pendingTimeouts.add(timeout);\n } else {\n // Deliver asynchronously to avoid blocking publisher\n setImmediate(() => {\n void this.deliverToSubscribers(endpoint, envelope);\n });\n }\n }\n\n private async deliverToSubscribers<TMessage extends BaseMessage>(\n endpoint: string,\n envelope: MessageEnvelope<TMessage>\n ): Promise<void> {\n if (!this.started) {\n return;\n }\n\n const subscriptions = this.subscriptions.get(endpoint);\n if (!subscriptions || subscriptions.length === 0) {\n return;\n }\n\n // Deliver to all subscriptions (fan-out)\n // Each subscription handles its own concurrency\n const deliveries = subscriptions.map(async (sub) => {\n await sub.semaphore.withPermit(async () => {\n try {\n await sub.handler(envelope as MessageEnvelope);\n } catch (error) {\n // In-memory transport doesn't handle errors - let them propagate\n // Real error handling is done by the bus runtime\n console.error(\n `[InMemoryTransport] Handler error for ${endpoint}:`,\n error\n );\n }\n });\n });\n\n await Promise.all(deliveries);\n }\n\n /**\n * Get the number of subscriptions for an endpoint.\n * Useful for testing.\n */\n getSubscriptionCount(endpoint: string): number {\n return this.subscriptions.get(endpoint)?.length ?? 0;\n }\n\n /**\n * Check if the transport is started.\n */\n get isStarted(): boolean {\n return this.started;\n }\n\n /**\n * Clear all subscriptions. Useful for testing.\n */\n clearSubscriptions(): void {\n this.subscriptions.clear();\n }\n}\n","/**\n * Simple semaphore for controlling concurrency.\n */\nexport class Semaphore {\n private permits: number;\n private waiting: Array<() => void> = [];\n\n constructor(permits: number) {\n this.permits = permits;\n }\n\n /**\n * Acquire a permit, waiting if none are available.\n */\n async acquire(): Promise<void> {\n if (this.permits > 0) {\n this.permits--;\n return;\n }\n\n return new Promise<void>((resolve) => {\n this.waiting.push(resolve);\n });\n }\n\n /**\n * Release a permit, waking a waiting acquirer if any.\n */\n release(): void {\n const next = this.waiting.shift();\n if (next) {\n next();\n } else {\n this.permits++;\n }\n }\n\n /**\n * Execute a function with a permit.\n */\n async withPermit<T>(fn: () => Promise<T>): Promise<T> {\n await this.acquire();\n try {\n return await fn();\n } finally {\n this.release();\n }\n }\n\n /**\n * Get the number of available permits.\n */\n get available(): number {\n return this.permits;\n }\n\n /**\n * Get the number of waiting acquirers.\n */\n get waitingCount(): number {\n return this.waiting.length;\n }\n}\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@saga-bus/transport-inmemory",
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"description": "In-memory transport for saga-bus testing and development",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"README.md"
|
|
19
|
+
],
|
|
20
|
+
"publishConfig": {
|
|
21
|
+
"access": "public"
|
|
22
|
+
},
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "https://github.com/d-e-a-n-f/saga-bus.git",
|
|
26
|
+
"directory": "packages/transport-inmemory"
|
|
27
|
+
},
|
|
28
|
+
"bugs": {
|
|
29
|
+
"url": "https://github.com/d-e-a-n-f/saga-bus/issues"
|
|
30
|
+
},
|
|
31
|
+
"homepage": "https://github.com/d-e-a-n-f/saga-bus#readme",
|
|
32
|
+
"keywords": [
|
|
33
|
+
"saga",
|
|
34
|
+
"message-bus",
|
|
35
|
+
"transport",
|
|
36
|
+
"inmemory",
|
|
37
|
+
"testing"
|
|
38
|
+
],
|
|
39
|
+
"scripts": {
|
|
40
|
+
"build": "tsup",
|
|
41
|
+
"dev": "tsup --watch",
|
|
42
|
+
"lint": "eslint src/",
|
|
43
|
+
"check-types": "tsc --noEmit",
|
|
44
|
+
"test": "vitest run",
|
|
45
|
+
"test:watch": "vitest"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"@saga-bus/core": "workspace:*"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@repo/eslint-config": "workspace:*",
|
|
52
|
+
"@repo/typescript-config": "workspace:*",
|
|
53
|
+
"@types/node": "^20.0.0",
|
|
54
|
+
"tsup": "^8.0.0",
|
|
55
|
+
"typescript": "^5.9.2",
|
|
56
|
+
"vitest": "^3.0.0"
|
|
57
|
+
},
|
|
58
|
+
"peerDependencies": {
|
|
59
|
+
"@saga-bus/core": ">=0.1.2"
|
|
60
|
+
}
|
|
61
|
+
}
|