@stepflowjs/storage-nats 0.0.1
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.d.ts +44 -0
- package/dist/index.js +435 -0
- package/dist/index.js.map +1 -0
- package/package.json +60 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { StorageAdapter, QueueOperations, ExecutionOperations, EventOperations, LeaderOperations, RealtimeOperations } from '@stepflowjs/core/storage';
|
|
2
|
+
|
|
3
|
+
interface NatsStorageOptions {
|
|
4
|
+
/** NATS server URLs */
|
|
5
|
+
servers?: string | string[];
|
|
6
|
+
/** Prefix for all streams and KV buckets */
|
|
7
|
+
streamPrefix?: string;
|
|
8
|
+
/** NATS connection options */
|
|
9
|
+
connectionOptions?: {
|
|
10
|
+
user?: string;
|
|
11
|
+
pass?: string;
|
|
12
|
+
token?: string;
|
|
13
|
+
timeout?: number;
|
|
14
|
+
maxReconnectAttempts?: number;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
declare class NatsStorageAdapter implements StorageAdapter {
|
|
18
|
+
private nc?;
|
|
19
|
+
private js?;
|
|
20
|
+
private executionsKv?;
|
|
21
|
+
private stepsKv?;
|
|
22
|
+
private waitersKv?;
|
|
23
|
+
private locksKv?;
|
|
24
|
+
private readonly prefix;
|
|
25
|
+
private readonly servers;
|
|
26
|
+
private readonly connectionOptions?;
|
|
27
|
+
private jobConsumer?;
|
|
28
|
+
private realtimeSubscriptions;
|
|
29
|
+
private readonly sc;
|
|
30
|
+
private readonly jc;
|
|
31
|
+
constructor(options?: NatsStorageOptions);
|
|
32
|
+
connect(): Promise<void>;
|
|
33
|
+
disconnect(): Promise<void>;
|
|
34
|
+
healthCheck(): Promise<boolean>;
|
|
35
|
+
queue: QueueOperations;
|
|
36
|
+
execution: ExecutionOperations;
|
|
37
|
+
events: EventOperations;
|
|
38
|
+
leader: LeaderOperations;
|
|
39
|
+
realtime: RealtimeOperations;
|
|
40
|
+
private serializeExecution;
|
|
41
|
+
private deserializeExecution;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export { NatsStorageAdapter, type NatsStorageOptions, NatsStorageAdapter as default };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import {
|
|
3
|
+
connect,
|
|
4
|
+
StringCodec,
|
|
5
|
+
JSONCodec,
|
|
6
|
+
AckPolicy
|
|
7
|
+
} from "nats";
|
|
8
|
+
var NatsStorageAdapter = class {
|
|
9
|
+
nc;
|
|
10
|
+
js;
|
|
11
|
+
executionsKv;
|
|
12
|
+
stepsKv;
|
|
13
|
+
waitersKv;
|
|
14
|
+
locksKv;
|
|
15
|
+
prefix;
|
|
16
|
+
servers;
|
|
17
|
+
connectionOptions;
|
|
18
|
+
jobConsumer;
|
|
19
|
+
realtimeSubscriptions = /* @__PURE__ */ new Map();
|
|
20
|
+
sc = StringCodec();
|
|
21
|
+
jc = JSONCodec();
|
|
22
|
+
constructor(options = {}) {
|
|
23
|
+
this.servers = options.servers ?? "localhost:4222";
|
|
24
|
+
this.prefix = options.streamPrefix ?? "stepflow";
|
|
25
|
+
this.connectionOptions = options.connectionOptions;
|
|
26
|
+
}
|
|
27
|
+
// ============================================================================
|
|
28
|
+
// Connection Lifecycle
|
|
29
|
+
// ============================================================================
|
|
30
|
+
async connect() {
|
|
31
|
+
this.nc = await connect({
|
|
32
|
+
servers: this.servers,
|
|
33
|
+
...this.connectionOptions
|
|
34
|
+
});
|
|
35
|
+
this.js = this.nc.jetstream();
|
|
36
|
+
const jsm = await this.nc.jetstreamManager();
|
|
37
|
+
try {
|
|
38
|
+
this.executionsKv = await this.js.views.kv(`${this.prefix}_executions`);
|
|
39
|
+
} catch {
|
|
40
|
+
const sc = await jsm.streams.add({
|
|
41
|
+
name: `KV_${this.prefix}_executions`,
|
|
42
|
+
subjects: [`$KV.${this.prefix}_executions.>`]
|
|
43
|
+
});
|
|
44
|
+
this.executionsKv = await this.js.views.kv(`${this.prefix}_executions`);
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
this.stepsKv = await this.js.views.kv(`${this.prefix}_steps`);
|
|
48
|
+
} catch {
|
|
49
|
+
await jsm.streams.add({
|
|
50
|
+
name: `KV_${this.prefix}_steps`,
|
|
51
|
+
subjects: [`$KV.${this.prefix}_steps.>`]
|
|
52
|
+
});
|
|
53
|
+
this.stepsKv = await this.js.views.kv(`${this.prefix}_steps`);
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
this.waitersKv = await this.js.views.kv(`${this.prefix}_waiters`);
|
|
57
|
+
} catch {
|
|
58
|
+
await jsm.streams.add({
|
|
59
|
+
name: `KV_${this.prefix}_waiters`,
|
|
60
|
+
subjects: [`$KV.${this.prefix}_waiters.>`]
|
|
61
|
+
});
|
|
62
|
+
this.waitersKv = await this.js.views.kv(`${this.prefix}_waiters`);
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
this.locksKv = await this.js.views.kv(`${this.prefix}_locks`);
|
|
66
|
+
} catch {
|
|
67
|
+
await jsm.streams.add({
|
|
68
|
+
name: `KV_${this.prefix}_locks`,
|
|
69
|
+
subjects: [`$KV.${this.prefix}_locks.>`]
|
|
70
|
+
});
|
|
71
|
+
this.locksKv = await this.js.views.kv(`${this.prefix}_locks`);
|
|
72
|
+
}
|
|
73
|
+
try {
|
|
74
|
+
await jsm.streams.info(`${this.prefix}_jobs`);
|
|
75
|
+
} catch {
|
|
76
|
+
await jsm.streams.add({
|
|
77
|
+
name: `${this.prefix}_jobs`,
|
|
78
|
+
subjects: [`${this.prefix}.jobs.>`]
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
async disconnect() {
|
|
83
|
+
await this.jobConsumer?.drain();
|
|
84
|
+
for (const sub of this.realtimeSubscriptions.values()) {
|
|
85
|
+
await sub.drain();
|
|
86
|
+
}
|
|
87
|
+
this.realtimeSubscriptions.clear();
|
|
88
|
+
await this.nc?.drain();
|
|
89
|
+
await this.nc?.close();
|
|
90
|
+
}
|
|
91
|
+
async healthCheck() {
|
|
92
|
+
return this.nc !== void 0 && !this.nc.isClosed();
|
|
93
|
+
}
|
|
94
|
+
// ============================================================================
|
|
95
|
+
// Queue Operations (JetStream)
|
|
96
|
+
// ============================================================================
|
|
97
|
+
queue = {
|
|
98
|
+
push: async (job) => {
|
|
99
|
+
if (!this.js) throw new Error("Not connected");
|
|
100
|
+
const subject = `${this.prefix}.jobs.${job.workflowId}`;
|
|
101
|
+
await this.js.publish(subject, this.jc.encode(job));
|
|
102
|
+
return job.id;
|
|
103
|
+
},
|
|
104
|
+
pop: async (options) => {
|
|
105
|
+
if (!this.js) throw new Error("Not connected");
|
|
106
|
+
try {
|
|
107
|
+
const consumer = await this.js.consumers.get(
|
|
108
|
+
`${this.prefix}_jobs`,
|
|
109
|
+
`${this.prefix}_worker_${options?.workerId ?? "default"}`
|
|
110
|
+
);
|
|
111
|
+
const messages = await consumer.fetch({
|
|
112
|
+
max_messages: 1,
|
|
113
|
+
expires: 5e3
|
|
114
|
+
});
|
|
115
|
+
for await (const msg of messages) {
|
|
116
|
+
const job = this.jc.decode(msg.data);
|
|
117
|
+
if (job.scheduledFor && new Date(job.scheduledFor) > /* @__PURE__ */ new Date()) {
|
|
118
|
+
msg.nak();
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
job._msg = msg;
|
|
122
|
+
return job;
|
|
123
|
+
}
|
|
124
|
+
} catch (error) {
|
|
125
|
+
try {
|
|
126
|
+
if (!this.nc) throw new Error("Not connected");
|
|
127
|
+
const jsm = await this.nc.jetstreamManager();
|
|
128
|
+
await jsm.consumers.add(`${this.prefix}_jobs`, {
|
|
129
|
+
durable_name: `${this.prefix}_worker_${options?.workerId ?? "default"}`,
|
|
130
|
+
ack_policy: AckPolicy.Explicit,
|
|
131
|
+
filter_subject: `${this.prefix}.jobs.>`
|
|
132
|
+
});
|
|
133
|
+
return this.queue.pop(options);
|
|
134
|
+
} catch {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return null;
|
|
139
|
+
},
|
|
140
|
+
ack: async (jobId) => {
|
|
141
|
+
},
|
|
142
|
+
nack: async (jobId, options) => {
|
|
143
|
+
},
|
|
144
|
+
schedule: async (job, executeAt) => {
|
|
145
|
+
if (!this.js) throw new Error("Not connected");
|
|
146
|
+
job.scheduledFor = executeAt;
|
|
147
|
+
const subject = `${this.prefix}.jobs.${job.workflowId}`;
|
|
148
|
+
await this.js.publish(subject, this.jc.encode(job));
|
|
149
|
+
return job.id;
|
|
150
|
+
},
|
|
151
|
+
getDelayed: async () => {
|
|
152
|
+
return [];
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
// ============================================================================
|
|
156
|
+
// Execution Operations (KV Store)
|
|
157
|
+
// ============================================================================
|
|
158
|
+
execution = {
|
|
159
|
+
create: async (execution) => {
|
|
160
|
+
if (!this.executionsKv) throw new Error("Not connected");
|
|
161
|
+
await this.executionsKv.put(
|
|
162
|
+
execution.id,
|
|
163
|
+
this.jc.encode(this.serializeExecution(execution))
|
|
164
|
+
);
|
|
165
|
+
if (execution.metadata.idempotencyKey) {
|
|
166
|
+
const idempotencyKey = `${execution.workflowId}:idem:${execution.metadata.idempotencyKey}`;
|
|
167
|
+
await this.executionsKv.put(
|
|
168
|
+
idempotencyKey,
|
|
169
|
+
this.sc.encode(execution.id)
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
return execution.id;
|
|
173
|
+
},
|
|
174
|
+
get: async (executionId) => {
|
|
175
|
+
if (!this.executionsKv) throw new Error("Not connected");
|
|
176
|
+
try {
|
|
177
|
+
const entry = await this.executionsKv.get(executionId);
|
|
178
|
+
if (!entry) return null;
|
|
179
|
+
return this.deserializeExecution(this.jc.decode(entry.value));
|
|
180
|
+
} catch {
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
getByIdempotencyKey: async (workflowId, idempotencyKey) => {
|
|
185
|
+
if (!this.executionsKv) throw new Error("Not connected");
|
|
186
|
+
try {
|
|
187
|
+
const key = `${workflowId}:idem:${idempotencyKey}`;
|
|
188
|
+
const entry = await this.executionsKv.get(key);
|
|
189
|
+
if (!entry) return null;
|
|
190
|
+
const executionId = this.sc.decode(entry.value);
|
|
191
|
+
return this.execution.get(executionId);
|
|
192
|
+
} catch {
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
update: async (executionId, updates) => {
|
|
197
|
+
if (!this.executionsKv) throw new Error("Not connected");
|
|
198
|
+
const existing = await this.execution.get(executionId);
|
|
199
|
+
if (!existing) throw new Error(`Execution ${executionId} not found`);
|
|
200
|
+
const updated = { ...existing, ...updates };
|
|
201
|
+
await this.executionsKv.put(
|
|
202
|
+
executionId,
|
|
203
|
+
this.jc.encode(this.serializeExecution(updated))
|
|
204
|
+
);
|
|
205
|
+
},
|
|
206
|
+
list: async (options) => {
|
|
207
|
+
if (!this.executionsKv) throw new Error("Not connected");
|
|
208
|
+
const results = [];
|
|
209
|
+
const iter = await this.executionsKv.keys();
|
|
210
|
+
for await (const key of iter) {
|
|
211
|
+
if (key.includes(":idem:")) continue;
|
|
212
|
+
const entry = await this.executionsKv.get(key);
|
|
213
|
+
if (!entry) continue;
|
|
214
|
+
const execution = this.deserializeExecution(
|
|
215
|
+
this.jc.decode(entry.value)
|
|
216
|
+
);
|
|
217
|
+
if (options.workflowId && execution.workflowId !== options.workflowId) {
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
if (options.status && execution.status !== options.status) {
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
if (options.runId && execution.runId !== options.runId) {
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
results.push(execution);
|
|
227
|
+
}
|
|
228
|
+
results.sort((a, b) => b.startedAt.getTime() - a.startedAt.getTime());
|
|
229
|
+
const offset = options.offset ?? 0;
|
|
230
|
+
const limit = options.limit ?? 50;
|
|
231
|
+
return results.slice(offset, offset + limit);
|
|
232
|
+
},
|
|
233
|
+
getStepResult: async (executionId, stepName) => {
|
|
234
|
+
if (!this.stepsKv) throw new Error("Not connected");
|
|
235
|
+
try {
|
|
236
|
+
const key = `${executionId}:${stepName}`;
|
|
237
|
+
const entry = await this.stepsKv.get(key);
|
|
238
|
+
if (!entry) return null;
|
|
239
|
+
const result = this.jc.decode(entry.value);
|
|
240
|
+
return {
|
|
241
|
+
...result,
|
|
242
|
+
startedAt: new Date(result.startedAt),
|
|
243
|
+
completedAt: new Date(result.completedAt)
|
|
244
|
+
};
|
|
245
|
+
} catch {
|
|
246
|
+
return null;
|
|
247
|
+
}
|
|
248
|
+
},
|
|
249
|
+
saveStepResult: async (executionId, stepName, result) => {
|
|
250
|
+
if (!this.stepsKv) throw new Error("Not connected");
|
|
251
|
+
const key = `${executionId}:${stepName}`;
|
|
252
|
+
await this.stepsKv.put(key, this.jc.encode(result));
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
// ============================================================================
|
|
256
|
+
// Event Operations (KV Store)
|
|
257
|
+
// ============================================================================
|
|
258
|
+
events = {
|
|
259
|
+
publish: async (eventId, data) => {
|
|
260
|
+
if (!this.waitersKv || !this.nc) throw new Error("Not connected");
|
|
261
|
+
const waiters = [];
|
|
262
|
+
const iter = await this.waitersKv.keys();
|
|
263
|
+
for await (const key of iter) {
|
|
264
|
+
if (key.startsWith(`${eventId}:`)) {
|
|
265
|
+
const entry = await this.waitersKv.get(key);
|
|
266
|
+
if (entry) {
|
|
267
|
+
const waiter = this.jc.decode(entry.value);
|
|
268
|
+
waiters.push(waiter);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
for (const waiter of waiters) {
|
|
273
|
+
const subject = `${this.prefix}.events.${eventId}.${waiter.executionId}`;
|
|
274
|
+
this.nc.publish(subject, this.jc.encode(data));
|
|
275
|
+
await this.waitersKv.delete(`${eventId}:${waiter.executionId}`);
|
|
276
|
+
}
|
|
277
|
+
return waiters.length;
|
|
278
|
+
},
|
|
279
|
+
subscribe: async (eventId, executionId, timeout) => {
|
|
280
|
+
if (!this.waitersKv) throw new Error("Not connected");
|
|
281
|
+
const key = `${eventId}:${executionId}`;
|
|
282
|
+
const waiter = {
|
|
283
|
+
executionId,
|
|
284
|
+
timeoutAt: timeout
|
|
285
|
+
};
|
|
286
|
+
await this.waitersKv.put(key, this.jc.encode(waiter));
|
|
287
|
+
},
|
|
288
|
+
getWaiters: async (eventId) => {
|
|
289
|
+
if (!this.waitersKv) throw new Error("Not connected");
|
|
290
|
+
const waiters = [];
|
|
291
|
+
const iter = await this.waitersKv.keys();
|
|
292
|
+
for await (const key of iter) {
|
|
293
|
+
if (key.startsWith(`${eventId}:`)) {
|
|
294
|
+
const entry = await this.waitersKv.get(key);
|
|
295
|
+
if (entry) {
|
|
296
|
+
const waiter = this.jc.decode(entry.value);
|
|
297
|
+
waiters.push({
|
|
298
|
+
...waiter,
|
|
299
|
+
timeoutAt: waiter.timeoutAt ? new Date(waiter.timeoutAt) : void 0
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
return waiters;
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
// ============================================================================
|
|
308
|
+
// Leader Operations (KV with Revision)
|
|
309
|
+
// ============================================================================
|
|
310
|
+
leader = {
|
|
311
|
+
acquire: async (lockId, ttlSeconds) => {
|
|
312
|
+
if (!this.locksKv) throw new Error("Not connected");
|
|
313
|
+
try {
|
|
314
|
+
const now = /* @__PURE__ */ new Date();
|
|
315
|
+
const expiresAt = new Date(Date.now() + ttlSeconds * 1e3);
|
|
316
|
+
const owner = `${process.pid}-${Date.now()}`;
|
|
317
|
+
const lock = {
|
|
318
|
+
owner,
|
|
319
|
+
expiresAt: expiresAt.toISOString()
|
|
320
|
+
};
|
|
321
|
+
try {
|
|
322
|
+
await this.locksKv.create(lockId, this.jc.encode(lock));
|
|
323
|
+
return true;
|
|
324
|
+
} catch {
|
|
325
|
+
const entry = await this.locksKv.get(lockId);
|
|
326
|
+
if (!entry) return false;
|
|
327
|
+
const existing = this.jc.decode(entry.value);
|
|
328
|
+
const existingExpiry = new Date(existing.expiresAt);
|
|
329
|
+
if (existingExpiry <= now) {
|
|
330
|
+
await this.locksKv.put(lockId, this.jc.encode(lock));
|
|
331
|
+
return true;
|
|
332
|
+
}
|
|
333
|
+
return false;
|
|
334
|
+
}
|
|
335
|
+
} catch {
|
|
336
|
+
return false;
|
|
337
|
+
}
|
|
338
|
+
},
|
|
339
|
+
release: async (lockId) => {
|
|
340
|
+
if (!this.locksKv) throw new Error("Not connected");
|
|
341
|
+
try {
|
|
342
|
+
await this.locksKv.delete(lockId);
|
|
343
|
+
} catch {
|
|
344
|
+
}
|
|
345
|
+
},
|
|
346
|
+
renew: async (lockId, ttlSeconds) => {
|
|
347
|
+
if (!this.locksKv) throw new Error("Not connected");
|
|
348
|
+
try {
|
|
349
|
+
const entry = await this.locksKv.get(lockId);
|
|
350
|
+
if (!entry) return false;
|
|
351
|
+
const existing = this.jc.decode(entry.value);
|
|
352
|
+
const expiresAt = new Date(Date.now() + ttlSeconds * 1e3);
|
|
353
|
+
const updated = {
|
|
354
|
+
...existing,
|
|
355
|
+
expiresAt: expiresAt.toISOString()
|
|
356
|
+
};
|
|
357
|
+
await this.locksKv.put(lockId, this.jc.encode(updated));
|
|
358
|
+
return true;
|
|
359
|
+
} catch {
|
|
360
|
+
return false;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
// ============================================================================
|
|
365
|
+
// Realtime Operations (NATS Pub/Sub)
|
|
366
|
+
// ============================================================================
|
|
367
|
+
realtime = {
|
|
368
|
+
subscribe: (channel, callback) => {
|
|
369
|
+
if (!this.nc) throw new Error("Not connected");
|
|
370
|
+
const subject = `${this.prefix}.realtime.${channel}`;
|
|
371
|
+
const sub = this.nc.subscribe(subject);
|
|
372
|
+
this.realtimeSubscriptions.set(channel, sub);
|
|
373
|
+
(async () => {
|
|
374
|
+
for await (const msg of sub) {
|
|
375
|
+
try {
|
|
376
|
+
const data = this.jc.decode(msg.data);
|
|
377
|
+
callback(data);
|
|
378
|
+
} catch (error) {
|
|
379
|
+
console.error("Error processing realtime message:", error);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
})();
|
|
383
|
+
return () => {
|
|
384
|
+
sub.unsubscribe();
|
|
385
|
+
this.realtimeSubscriptions.delete(channel);
|
|
386
|
+
};
|
|
387
|
+
},
|
|
388
|
+
publish: async (channel, data) => {
|
|
389
|
+
if (!this.nc) throw new Error("Not connected");
|
|
390
|
+
const subject = `${this.prefix}.realtime.${channel}`;
|
|
391
|
+
this.nc.publish(subject, this.jc.encode(data));
|
|
392
|
+
}
|
|
393
|
+
};
|
|
394
|
+
// ============================================================================
|
|
395
|
+
// Serialization Helpers
|
|
396
|
+
// ============================================================================
|
|
397
|
+
serializeExecution(execution) {
|
|
398
|
+
return {
|
|
399
|
+
...execution,
|
|
400
|
+
startedAt: execution.startedAt.toISOString(),
|
|
401
|
+
completedAt: execution.completedAt?.toISOString(),
|
|
402
|
+
steps: execution.steps.map((step) => ({
|
|
403
|
+
...step,
|
|
404
|
+
startedAt: step.startedAt?.toISOString(),
|
|
405
|
+
completedAt: step.completedAt?.toISOString()
|
|
406
|
+
})),
|
|
407
|
+
timeline: execution.timeline.map((event) => ({
|
|
408
|
+
...event,
|
|
409
|
+
timestamp: event.timestamp.toISOString()
|
|
410
|
+
}))
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
deserializeExecution(data) {
|
|
414
|
+
return {
|
|
415
|
+
...data,
|
|
416
|
+
startedAt: new Date(data.startedAt),
|
|
417
|
+
completedAt: data.completedAt ? new Date(data.completedAt) : void 0,
|
|
418
|
+
steps: data.steps.map((step) => ({
|
|
419
|
+
...step,
|
|
420
|
+
startedAt: step.startedAt ? new Date(step.startedAt) : void 0,
|
|
421
|
+
completedAt: step.completedAt ? new Date(step.completedAt) : void 0
|
|
422
|
+
})),
|
|
423
|
+
timeline: data.timeline.map((event) => ({
|
|
424
|
+
...event,
|
|
425
|
+
timestamp: new Date(event.timestamp)
|
|
426
|
+
}))
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
};
|
|
430
|
+
var index_default = NatsStorageAdapter;
|
|
431
|
+
export {
|
|
432
|
+
NatsStorageAdapter,
|
|
433
|
+
index_default as default
|
|
434
|
+
};
|
|
435
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["// ============================================================================\n// NATS JetStream Storage Adapter\n// High-performance durable storage using NATS JetStream and KV\n// ============================================================================\n\nimport {\n connect,\n type NatsConnection,\n type JetStreamClient,\n type KV,\n type Subscription,\n type JsMsg,\n StringCodec,\n JSONCodec,\n type KvEntry,\n AckPolicy,\n} from \"nats\";\nimport type {\n StorageAdapter,\n QueueOperations,\n ExecutionOperations,\n EventOperations,\n LeaderOperations,\n RealtimeOperations,\n} from \"@stepflowjs/core/storage\";\nimport type {\n QueueJob,\n Execution,\n StepResult,\n EventWaiter,\n ListOptions,\n PopOptions,\n NackOptions,\n Unsubscribe,\n} from \"@stepflowjs/core\";\n\n// ============================================================================\n// Configuration\n// ============================================================================\n\nexport interface NatsStorageOptions {\n /** NATS server URLs */\n servers?: string | string[];\n /** Prefix for all streams and KV buckets */\n streamPrefix?: string;\n /** NATS connection options */\n connectionOptions?: {\n user?: string;\n pass?: string;\n token?: string;\n timeout?: number;\n maxReconnectAttempts?: number;\n };\n}\n\n// ============================================================================\n// NATS Storage Adapter\n// ============================================================================\n\nexport class NatsStorageAdapter implements StorageAdapter {\n private nc?: NatsConnection;\n private js?: JetStreamClient;\n private executionsKv?: KV;\n private stepsKv?: KV;\n private waitersKv?: KV;\n private locksKv?: KV;\n private readonly prefix: string;\n private readonly servers: string | string[];\n private readonly connectionOptions?: NatsStorageOptions[\"connectionOptions\"];\n private jobConsumer?: Subscription;\n private realtimeSubscriptions = new Map<string, Subscription>();\n private readonly sc = StringCodec();\n private readonly jc = JSONCodec();\n\n constructor(options: NatsStorageOptions = {}) {\n this.servers = options.servers ?? \"localhost:4222\";\n this.prefix = options.streamPrefix ?? \"stepflow\";\n this.connectionOptions = options.connectionOptions;\n }\n\n // ============================================================================\n // Connection Lifecycle\n // ============================================================================\n\n async connect(): Promise<void> {\n // Connect to NATS\n this.nc = await connect({\n servers: this.servers,\n ...this.connectionOptions,\n });\n\n // Get JetStream context\n this.js = this.nc.jetstream();\n\n // Get JetStream manager\n const jsm = await this.nc.jetstreamManager();\n\n // Executions KV bucket\n try {\n this.executionsKv = await this.js.views.kv(`${this.prefix}_executions`);\n } catch {\n const sc = await jsm.streams.add({\n name: `KV_${this.prefix}_executions`,\n subjects: [`$KV.${this.prefix}_executions.>`],\n });\n this.executionsKv = await this.js.views.kv(`${this.prefix}_executions`);\n }\n\n // Steps KV bucket\n try {\n this.stepsKv = await this.js.views.kv(`${this.prefix}_steps`);\n } catch {\n await jsm.streams.add({\n name: `KV_${this.prefix}_steps`,\n subjects: [`$KV.${this.prefix}_steps.>`],\n });\n this.stepsKv = await this.js.views.kv(`${this.prefix}_steps`);\n }\n\n // Waiters KV bucket\n try {\n this.waitersKv = await this.js.views.kv(`${this.prefix}_waiters`);\n } catch {\n await jsm.streams.add({\n name: `KV_${this.prefix}_waiters`,\n subjects: [`$KV.${this.prefix}_waiters.>`],\n });\n this.waitersKv = await this.js.views.kv(`${this.prefix}_waiters`);\n }\n\n // Locks KV bucket\n try {\n this.locksKv = await this.js.views.kv(`${this.prefix}_locks`);\n } catch {\n await jsm.streams.add({\n name: `KV_${this.prefix}_locks`,\n subjects: [`$KV.${this.prefix}_locks.>`],\n });\n this.locksKv = await this.js.views.kv(`${this.prefix}_locks`);\n }\n\n // Create/get jobs stream\n try {\n await jsm.streams.info(`${this.prefix}_jobs`);\n } catch {\n await jsm.streams.add({\n name: `${this.prefix}_jobs`,\n subjects: [`${this.prefix}.jobs.>`],\n });\n }\n }\n\n async disconnect(): Promise<void> {\n // Clean up subscriptions\n await this.jobConsumer?.drain();\n for (const sub of this.realtimeSubscriptions.values()) {\n await sub.drain();\n }\n this.realtimeSubscriptions.clear();\n\n // Close connection\n await this.nc?.drain();\n await this.nc?.close();\n }\n\n async healthCheck(): Promise<boolean> {\n return this.nc !== undefined && !this.nc.isClosed();\n }\n\n // ============================================================================\n // Queue Operations (JetStream)\n // ============================================================================\n\n queue: QueueOperations = {\n push: async (job: QueueJob): Promise<string> => {\n if (!this.js) throw new Error(\"Not connected\");\n\n const subject = `${this.prefix}.jobs.${job.workflowId}`;\n await this.js.publish(subject, this.jc.encode(job));\n return job.id;\n },\n\n pop: async (options?: PopOptions): Promise<QueueJob | null> => {\n if (!this.js) throw new Error(\"Not connected\");\n\n try {\n // Create ephemeral consumer\n const consumer = await this.js.consumers.get(\n `${this.prefix}_jobs`,\n `${this.prefix}_worker_${options?.workerId ?? \"default\"}`,\n );\n\n // Fetch one message\n const messages = await consumer.fetch({\n max_messages: 1,\n expires: 5000,\n });\n\n for await (const msg of messages) {\n const job = this.jc.decode(msg.data) as QueueJob;\n\n // Check if scheduled\n if (job.scheduledFor && new Date(job.scheduledFor) > new Date()) {\n msg.nak();\n return null;\n }\n\n // Store message for ack/nack\n (job as any)._msg = msg;\n return job;\n }\n } catch (error) {\n // Consumer doesn't exist, create it\n try {\n if (!this.nc) throw new Error(\"Not connected\");\n const jsm = await this.nc.jetstreamManager();\n await jsm.consumers.add(`${this.prefix}_jobs`, {\n durable_name: `${this.prefix}_worker_${options?.workerId ?? \"default\"}`,\n ack_policy: AckPolicy.Explicit,\n filter_subject: `${this.prefix}.jobs.>`,\n });\n return this.queue.pop(options);\n } catch {\n return null;\n }\n }\n\n return null;\n },\n\n ack: async (jobId: string): Promise<void> => {\n // Message is acked via the stored _msg reference\n // This is handled by the worker after processing\n },\n\n nack: async (jobId: string, options?: NackOptions): Promise<void> => {\n // Message is nacked via the stored _msg reference\n // Delay is handled by JetStream's nak delay\n },\n\n schedule: async (job: QueueJob, executeAt: Date): Promise<string> => {\n if (!this.js) throw new Error(\"Not connected\");\n\n job.scheduledFor = executeAt;\n const subject = `${this.prefix}.jobs.${job.workflowId}`;\n await this.js.publish(subject, this.jc.encode(job));\n return job.id;\n },\n\n getDelayed: async (): Promise<QueueJob[]> => {\n // This would require scanning the stream\n // For now, return empty array\n return [];\n },\n };\n\n // ============================================================================\n // Execution Operations (KV Store)\n // ============================================================================\n\n execution: ExecutionOperations = {\n create: async (execution: Execution): Promise<string> => {\n if (!this.executionsKv) throw new Error(\"Not connected\");\n\n await this.executionsKv.put(\n execution.id,\n this.jc.encode(this.serializeExecution(execution)),\n );\n\n // Also store by idempotency key if present\n if (execution.metadata.idempotencyKey) {\n const idempotencyKey = `${execution.workflowId}:idem:${execution.metadata.idempotencyKey}`;\n await this.executionsKv.put(\n idempotencyKey,\n this.sc.encode(execution.id),\n );\n }\n\n return execution.id;\n },\n\n get: async (executionId: string): Promise<Execution | null> => {\n if (!this.executionsKv) throw new Error(\"Not connected\");\n\n try {\n const entry = await this.executionsKv.get(executionId);\n if (!entry) return null;\n return this.deserializeExecution(this.jc.decode(entry.value) as any);\n } catch {\n return null;\n }\n },\n\n getByIdempotencyKey: async (\n workflowId: string,\n idempotencyKey: string,\n ): Promise<Execution | null> => {\n if (!this.executionsKv) throw new Error(\"Not connected\");\n\n try {\n const key = `${workflowId}:idem:${idempotencyKey}`;\n const entry = await this.executionsKv.get(key);\n if (!entry) return null;\n\n const executionId = this.sc.decode(entry.value);\n return this.execution.get(executionId);\n } catch {\n return null;\n }\n },\n\n update: async (\n executionId: string,\n updates: Partial<Execution>,\n ): Promise<void> => {\n if (!this.executionsKv) throw new Error(\"Not connected\");\n\n const existing = await this.execution.get(executionId);\n if (!existing) throw new Error(`Execution ${executionId} not found`);\n\n const updated = { ...existing, ...updates };\n await this.executionsKv.put(\n executionId,\n this.jc.encode(this.serializeExecution(updated)),\n );\n },\n\n list: async (options: ListOptions): Promise<Execution[]> => {\n if (!this.executionsKv) throw new Error(\"Not connected\");\n\n const results: Execution[] = [];\n const iter = await this.executionsKv.keys();\n\n for await (const key of iter) {\n // Skip idempotency keys\n if (key.includes(\":idem:\")) continue;\n\n const entry = await this.executionsKv.get(key);\n if (!entry) continue;\n\n const execution = this.deserializeExecution(\n this.jc.decode(entry.value) as any,\n );\n\n // Apply filters\n if (options.workflowId && execution.workflowId !== options.workflowId) {\n continue;\n }\n if (options.status && execution.status !== options.status) {\n continue;\n }\n if (options.runId && execution.runId !== options.runId) {\n continue;\n }\n\n results.push(execution);\n }\n\n // Sort by startedAt descending\n results.sort((a, b) => b.startedAt.getTime() - a.startedAt.getTime());\n\n const offset = options.offset ?? 0;\n const limit = options.limit ?? 50;\n return results.slice(offset, offset + limit);\n },\n\n getStepResult: async <T = unknown>(\n executionId: string,\n stepName: string,\n ): Promise<StepResult<T> | null> => {\n if (!this.stepsKv) throw new Error(\"Not connected\");\n\n try {\n const key = `${executionId}:${stepName}`;\n const entry = await this.stepsKv.get(key);\n if (!entry) return null;\n\n const result = this.jc.decode(entry.value) as any;\n return {\n ...result,\n startedAt: new Date(result.startedAt),\n completedAt: new Date(result.completedAt),\n } as StepResult<T>;\n } catch {\n return null;\n }\n },\n\n saveStepResult: async (\n executionId: string,\n stepName: string,\n result: StepResult,\n ): Promise<void> => {\n if (!this.stepsKv) throw new Error(\"Not connected\");\n\n const key = `${executionId}:${stepName}`;\n await this.stepsKv.put(key, this.jc.encode(result));\n },\n };\n\n // ============================================================================\n // Event Operations (KV Store)\n // ============================================================================\n\n events: EventOperations = {\n publish: async (eventId: string, data: unknown): Promise<number> => {\n if (!this.waitersKv || !this.nc) throw new Error(\"Not connected\");\n\n // Get all waiters for this event\n const waiters: EventWaiter[] = [];\n const iter = await this.waitersKv.keys();\n\n for await (const key of iter) {\n if (key.startsWith(`${eventId}:`)) {\n const entry = await this.waitersKv.get(key);\n if (entry) {\n const waiter = this.jc.decode(entry.value) as EventWaiter;\n waiters.push(waiter);\n }\n }\n }\n\n // Publish event to each waiter\n for (const waiter of waiters) {\n const subject = `${this.prefix}.events.${eventId}.${waiter.executionId}`;\n this.nc.publish(subject, this.jc.encode(data));\n\n // Delete waiter entry\n await this.waitersKv.delete(`${eventId}:${waiter.executionId}`);\n }\n\n return waiters.length;\n },\n\n subscribe: async (\n eventId: string,\n executionId: string,\n timeout: Date,\n ): Promise<void> => {\n if (!this.waitersKv) throw new Error(\"Not connected\");\n\n const key = `${eventId}:${executionId}`;\n const waiter: EventWaiter = {\n executionId,\n timeoutAt: timeout,\n };\n\n await this.waitersKv.put(key, this.jc.encode(waiter));\n },\n\n getWaiters: async (eventId: string): Promise<EventWaiter[]> => {\n if (!this.waitersKv) throw new Error(\"Not connected\");\n\n const waiters: EventWaiter[] = [];\n const iter = await this.waitersKv.keys();\n\n for await (const key of iter) {\n if (key.startsWith(`${eventId}:`)) {\n const entry = await this.waitersKv.get(key);\n if (entry) {\n const waiter = this.jc.decode(entry.value) as any;\n waiters.push({\n ...waiter,\n timeoutAt: waiter.timeoutAt\n ? new Date(waiter.timeoutAt)\n : undefined,\n });\n }\n }\n }\n\n return waiters;\n },\n };\n\n // ============================================================================\n // Leader Operations (KV with Revision)\n // ============================================================================\n\n leader: LeaderOperations = {\n acquire: async (lockId: string, ttlSeconds: number): Promise<boolean> => {\n if (!this.locksKv) throw new Error(\"Not connected\");\n\n try {\n const now = new Date();\n const expiresAt = new Date(Date.now() + ttlSeconds * 1000);\n const owner = `${process.pid}-${Date.now()}`;\n\n const lock = {\n owner,\n expiresAt: expiresAt.toISOString(),\n };\n\n // Try to create with revision 0 (only succeeds if doesn't exist)\n try {\n await this.locksKv.create(lockId, this.jc.encode(lock));\n return true;\n } catch {\n // Lock exists, check if expired\n const entry = await this.locksKv.get(lockId);\n if (!entry) return false;\n\n const existing = this.jc.decode(entry.value) as any;\n const existingExpiry = new Date(existing.expiresAt);\n\n if (existingExpiry <= now) {\n // Lock expired, try to acquire\n await this.locksKv.put(lockId, this.jc.encode(lock));\n return true;\n }\n\n return false;\n }\n } catch {\n return false;\n }\n },\n\n release: async (lockId: string): Promise<void> => {\n if (!this.locksKv) throw new Error(\"Not connected\");\n\n try {\n await this.locksKv.delete(lockId);\n } catch {\n // Ignore errors\n }\n },\n\n renew: async (lockId: string, ttlSeconds: number): Promise<boolean> => {\n if (!this.locksKv) throw new Error(\"Not connected\");\n\n try {\n const entry = await this.locksKv.get(lockId);\n if (!entry) return false;\n\n const existing = this.jc.decode(entry.value) as any;\n const expiresAt = new Date(Date.now() + ttlSeconds * 1000);\n\n const updated = {\n ...existing,\n expiresAt: expiresAt.toISOString(),\n };\n\n await this.locksKv.put(lockId, this.jc.encode(updated));\n return true;\n } catch {\n return false;\n }\n },\n };\n\n // ============================================================================\n // Realtime Operations (NATS Pub/Sub)\n // ============================================================================\n\n realtime: RealtimeOperations = {\n subscribe: (\n channel: string,\n callback: (data: unknown) => void,\n ): Unsubscribe => {\n if (!this.nc) throw new Error(\"Not connected\");\n\n const subject = `${this.prefix}.realtime.${channel}`;\n const sub = this.nc.subscribe(subject);\n\n this.realtimeSubscriptions.set(channel, sub);\n\n // Process messages\n (async () => {\n for await (const msg of sub) {\n try {\n const data = this.jc.decode(msg.data);\n callback(data);\n } catch (error) {\n console.error(\"Error processing realtime message:\", error);\n }\n }\n })();\n\n return () => {\n sub.unsubscribe();\n this.realtimeSubscriptions.delete(channel);\n };\n },\n\n publish: async (channel: string, data: unknown): Promise<void> => {\n if (!this.nc) throw new Error(\"Not connected\");\n\n const subject = `${this.prefix}.realtime.${channel}`;\n this.nc.publish(subject, this.jc.encode(data));\n },\n };\n\n // ============================================================================\n // Serialization Helpers\n // ============================================================================\n\n private serializeExecution(execution: Execution): any {\n return {\n ...execution,\n startedAt: execution.startedAt.toISOString(),\n completedAt: execution.completedAt?.toISOString(),\n steps: execution.steps.map((step: any) => ({\n ...step,\n startedAt: step.startedAt?.toISOString(),\n completedAt: step.completedAt?.toISOString(),\n })),\n timeline: execution.timeline.map((event: any) => ({\n ...event,\n timestamp: event.timestamp.toISOString(),\n })),\n };\n }\n\n private deserializeExecution(data: any): Execution {\n return {\n ...data,\n startedAt: new Date(data.startedAt),\n completedAt: data.completedAt ? new Date(data.completedAt) : undefined,\n steps: data.steps.map((step: any) => ({\n ...step,\n startedAt: step.startedAt ? new Date(step.startedAt) : undefined,\n completedAt: step.completedAt ? new Date(step.completedAt) : undefined,\n })),\n timeline: data.timeline.map((event: any) => ({\n ...event,\n timestamp: new Date(event.timestamp),\n })),\n };\n }\n}\n\n// Default export\nexport default NatsStorageAdapter;\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@stepflowjs/storage-nats",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "NATS JetStream storage adapter for Stepflow",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"types": "./dist/index.d.ts"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"nats": "^2.29.3",
|
|
20
|
+
"@stepflowjs/core": "0.0.1"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"tsup": "^8.5.1",
|
|
24
|
+
"vitest": "^4.0.17",
|
|
25
|
+
"@stepflowjs/storage-tests": "0.1.0"
|
|
26
|
+
},
|
|
27
|
+
"peerDependencies": {
|
|
28
|
+
"typescript": "^5.0.0"
|
|
29
|
+
},
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"author": "Stepflow Contributors",
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "https://stepflow-production.up.railway.app",
|
|
35
|
+
"directory": "packages/storage/nats"
|
|
36
|
+
},
|
|
37
|
+
"homepage": "https://stepflow-production.up.railway.app",
|
|
38
|
+
"bugs": {
|
|
39
|
+
"url": "https://stepflow-production.up.railway.app"
|
|
40
|
+
},
|
|
41
|
+
"keywords": [
|
|
42
|
+
"stepflow",
|
|
43
|
+
"storage",
|
|
44
|
+
"nats",
|
|
45
|
+
"jetstream",
|
|
46
|
+
"adapter",
|
|
47
|
+
"workflow",
|
|
48
|
+
"orchestration"
|
|
49
|
+
],
|
|
50
|
+
"publishConfig": {
|
|
51
|
+
"access": "public"
|
|
52
|
+
},
|
|
53
|
+
"scripts": {
|
|
54
|
+
"build": "tsup",
|
|
55
|
+
"dev": "tsup --watch",
|
|
56
|
+
"typecheck": "tsc --noEmit",
|
|
57
|
+
"test": "vitest",
|
|
58
|
+
"clean": "rm -rf dist"
|
|
59
|
+
}
|
|
60
|
+
}
|