@durable-streams/server 0.1.3 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +518 -47
- package/dist/index.d.cts +191 -15
- package/dist/index.d.ts +191 -15
- package/dist/index.js +518 -47
- package/package.json +5 -5
- package/src/file-store.ts +238 -10
- package/src/server.ts +398 -61
- package/src/store.ts +272 -7
- package/src/types.ts +46 -0
package/dist/index.d.cts
CHANGED
|
@@ -56,6 +56,11 @@ interface Stream {
|
|
|
56
56
|
* Timestamp when the stream was created.
|
|
57
57
|
*/
|
|
58
58
|
createdAt: number;
|
|
59
|
+
/**
|
|
60
|
+
* Producer states for idempotent writes.
|
|
61
|
+
* Maps producer ID to their epoch and sequence state.
|
|
62
|
+
*/
|
|
63
|
+
producers?: Map<string, ProducerState>;
|
|
59
64
|
}
|
|
60
65
|
/**
|
|
61
66
|
* Event data for stream lifecycle hooks.
|
|
@@ -131,6 +136,49 @@ interface TestServerOptions {
|
|
|
131
136
|
cursorEpoch?: Date;
|
|
132
137
|
}
|
|
133
138
|
/**
|
|
139
|
+
* Producer state for idempotent writes.
|
|
140
|
+
* Tracks epoch and sequence number per producer ID for deduplication.
|
|
141
|
+
*/
|
|
142
|
+
interface ProducerState {
|
|
143
|
+
/**
|
|
144
|
+
* Current epoch for this producer.
|
|
145
|
+
* Client-declared, server-validated monotonically increasing.
|
|
146
|
+
*/
|
|
147
|
+
epoch: number;
|
|
148
|
+
/**
|
|
149
|
+
* Last sequence number received in this epoch.
|
|
150
|
+
*/
|
|
151
|
+
lastSeq: number;
|
|
152
|
+
/**
|
|
153
|
+
* Timestamp when this producer state was last updated.
|
|
154
|
+
* Used for TTL-based cleanup.
|
|
155
|
+
*/
|
|
156
|
+
lastUpdated: number;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Result of producer validation for append operations.
|
|
160
|
+
* For 'accepted' status, includes proposedState to commit after successful append.
|
|
161
|
+
*/
|
|
162
|
+
type ProducerValidationResult = {
|
|
163
|
+
status: `accepted`;
|
|
164
|
+
isNew: boolean;
|
|
165
|
+
/** State to commit after successful append (deferred mutation) */
|
|
166
|
+
proposedState: ProducerState;
|
|
167
|
+
producerId: string;
|
|
168
|
+
} | {
|
|
169
|
+
status: `duplicate`;
|
|
170
|
+
lastSeq: number;
|
|
171
|
+
} | {
|
|
172
|
+
status: `stale_epoch`;
|
|
173
|
+
currentEpoch: number;
|
|
174
|
+
} | {
|
|
175
|
+
status: `invalid_epoch_seq`;
|
|
176
|
+
} | {
|
|
177
|
+
status: `sequence_gap`;
|
|
178
|
+
expectedSeq: number;
|
|
179
|
+
receivedSeq: number;
|
|
180
|
+
};
|
|
181
|
+
/**
|
|
134
182
|
* Pending long-poll request.
|
|
135
183
|
*/
|
|
136
184
|
interface PendingLongPoll {
|
|
@@ -155,10 +203,32 @@ interface PendingLongPoll {
|
|
|
155
203
|
/**
|
|
156
204
|
* In-memory store for durable streams.
|
|
157
205
|
*/
|
|
206
|
+
/**
|
|
207
|
+
* Options for append operations.
|
|
208
|
+
*/
|
|
209
|
+
interface AppendOptions {
|
|
210
|
+
seq?: string;
|
|
211
|
+
contentType?: string;
|
|
212
|
+
producerId?: string;
|
|
213
|
+
producerEpoch?: number;
|
|
214
|
+
producerSeq?: number;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Result of an append operation.
|
|
218
|
+
*/
|
|
219
|
+
interface AppendResult {
|
|
220
|
+
message: StreamMessage | null;
|
|
221
|
+
producerResult?: ProducerValidationResult;
|
|
222
|
+
}
|
|
158
223
|
declare class StreamStore {
|
|
159
224
|
private streams;
|
|
160
225
|
private pendingLongPolls;
|
|
161
226
|
/**
|
|
227
|
+
* Per-producer locks for serializing validation+append operations.
|
|
228
|
+
* Key: "{streamPath}:{producerId}"
|
|
229
|
+
*/
|
|
230
|
+
private producerLocks;
|
|
231
|
+
/**
|
|
162
232
|
* Check if a stream is expired based on TTL or Expires-At.
|
|
163
233
|
*/
|
|
164
234
|
private isExpired;
|
|
@@ -192,15 +262,47 @@ declare class StreamStore {
|
|
|
192
262
|
*/
|
|
193
263
|
delete(path: string): boolean;
|
|
194
264
|
/**
|
|
265
|
+
* Validate producer state WITHOUT mutating.
|
|
266
|
+
* Returns proposed state to commit after successful append.
|
|
267
|
+
* Implements Kafka-style idempotent producer validation.
|
|
268
|
+
*
|
|
269
|
+
* IMPORTANT: This function does NOT mutate producer state. The caller must
|
|
270
|
+
* call commitProducerState() after successful append to apply the mutation.
|
|
271
|
+
* This ensures atomicity: if append fails (e.g., JSON validation), producer
|
|
272
|
+
* state is not incorrectly advanced.
|
|
273
|
+
*/
|
|
274
|
+
private validateProducer;
|
|
275
|
+
/**
|
|
276
|
+
* Commit producer state after successful append.
|
|
277
|
+
* This is the only place where producer state is mutated.
|
|
278
|
+
*/
|
|
279
|
+
private commitProducerState;
|
|
280
|
+
/**
|
|
281
|
+
* Clean up expired producer states from a stream.
|
|
282
|
+
*/
|
|
283
|
+
private cleanupExpiredProducers;
|
|
284
|
+
/**
|
|
285
|
+
* Acquire a lock for serialized producer operations.
|
|
286
|
+
* Returns a release function.
|
|
287
|
+
*/
|
|
288
|
+
private acquireProducerLock;
|
|
289
|
+
/**
|
|
195
290
|
* Append data to a stream.
|
|
196
291
|
* @throws Error if stream doesn't exist or is expired
|
|
197
292
|
* @throws Error if seq is lower than lastSeq
|
|
198
293
|
* @throws Error if JSON mode and array is empty
|
|
199
294
|
*/
|
|
200
|
-
append(path: string, data: Uint8Array, options?:
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
295
|
+
append(path: string, data: Uint8Array, options?: AppendOptions): StreamMessage | AppendResult;
|
|
296
|
+
/**
|
|
297
|
+
* Append with producer serialization for concurrent request handling.
|
|
298
|
+
* This ensures that validation+append is atomic per producer.
|
|
299
|
+
*/
|
|
300
|
+
appendWithProducer(path: string, data: Uint8Array, options: AppendOptions): Promise<AppendResult>;
|
|
301
|
+
/**
|
|
302
|
+
* Get the current epoch for a producer on a stream.
|
|
303
|
+
* Returns undefined if the producer doesn't exist or stream not found.
|
|
304
|
+
*/
|
|
305
|
+
getProducerEpoch(path: string, producerId: string): number | undefined;
|
|
204
306
|
/**
|
|
205
307
|
* Read messages from a stream starting at the given offset.
|
|
206
308
|
* @throws Error if stream doesn't exist or is expired
|
|
@@ -261,6 +363,11 @@ declare class FileBackedStreamStore {
|
|
|
261
363
|
private fileHandlePool;
|
|
262
364
|
private pendingLongPolls;
|
|
263
365
|
private dataDir;
|
|
366
|
+
/**
|
|
367
|
+
* Per-producer locks for serializing validation+append operations.
|
|
368
|
+
* Key: "{streamPath}:{producerId}"
|
|
369
|
+
*/
|
|
370
|
+
private producerLocks;
|
|
264
371
|
constructor(options: FileBackedStreamStoreOptions);
|
|
265
372
|
/**
|
|
266
373
|
* Recover streams from disk on startup.
|
|
@@ -277,6 +384,25 @@ declare class FileBackedStreamStore {
|
|
|
277
384
|
*/
|
|
278
385
|
private streamMetaToStream;
|
|
279
386
|
/**
|
|
387
|
+
* Validate producer state WITHOUT mutating.
|
|
388
|
+
* Returns proposed state to commit after successful append.
|
|
389
|
+
*
|
|
390
|
+
* IMPORTANT: This function does NOT mutate producer state. The caller must
|
|
391
|
+
* commit the proposedState after successful append (file write + fsync + LMDB).
|
|
392
|
+
* This ensures atomicity: if any step fails, producer state is not advanced.
|
|
393
|
+
*/
|
|
394
|
+
private validateProducer;
|
|
395
|
+
/**
|
|
396
|
+
* Acquire a lock for serialized producer operations.
|
|
397
|
+
* Returns a release function.
|
|
398
|
+
*/
|
|
399
|
+
private acquireProducerLock;
|
|
400
|
+
/**
|
|
401
|
+
* Get the current epoch for a producer on a stream.
|
|
402
|
+
* Returns undefined if the producer doesn't exist or stream not found.
|
|
403
|
+
*/
|
|
404
|
+
getProducerEpoch(streamPath: string, producerId: string): number | undefined;
|
|
405
|
+
/**
|
|
280
406
|
* Check if a stream is expired based on TTL or Expires-At.
|
|
281
407
|
*/
|
|
282
408
|
private isExpired;
|
|
@@ -299,11 +425,14 @@ declare class FileBackedStreamStore {
|
|
|
299
425
|
get(streamPath: string): Stream | undefined;
|
|
300
426
|
has(streamPath: string): boolean;
|
|
301
427
|
delete(streamPath: string): boolean;
|
|
302
|
-
append(streamPath: string, data: Uint8Array, options?: {
|
|
303
|
-
seq?: string;
|
|
304
|
-
contentType?: string;
|
|
428
|
+
append(streamPath: string, data: Uint8Array, options?: AppendOptions & {
|
|
305
429
|
isInitialCreate?: boolean;
|
|
306
|
-
}): Promise<StreamMessage | null>;
|
|
430
|
+
}): Promise<StreamMessage | AppendResult | null>;
|
|
431
|
+
/**
|
|
432
|
+
* Append with producer serialization for concurrent request handling.
|
|
433
|
+
* This ensures that validation+append is atomic per producer.
|
|
434
|
+
*/
|
|
435
|
+
appendWithProducer(streamPath: string, data: Uint8Array, options: AppendOptions): Promise<AppendResult>;
|
|
307
436
|
read(streamPath: string, offset?: string): {
|
|
308
437
|
messages: Array<StreamMessage>;
|
|
309
438
|
upToDate: boolean;
|
|
@@ -332,6 +461,36 @@ declare class FileBackedStreamStore {
|
|
|
332
461
|
|
|
333
462
|
//#endregion
|
|
334
463
|
//#region src/server.d.ts
|
|
464
|
+
/**
|
|
465
|
+
* HTTP server for testing durable streams.
|
|
466
|
+
* Supports both in-memory and file-backed storage modes.
|
|
467
|
+
*/
|
|
468
|
+
/**
|
|
469
|
+
* Configuration for injected faults (for testing retry/resilience).
|
|
470
|
+
* Supports various fault types beyond simple HTTP errors.
|
|
471
|
+
*/
|
|
472
|
+
interface InjectedFault {
|
|
473
|
+
/** HTTP status code to return (if set, returns error response) */
|
|
474
|
+
status?: number;
|
|
475
|
+
/** Number of times to trigger this fault (decremented on each use) */
|
|
476
|
+
count: number;
|
|
477
|
+
/** Optional Retry-After header value (seconds) */
|
|
478
|
+
retryAfter?: number;
|
|
479
|
+
/** Delay in milliseconds before responding */
|
|
480
|
+
delayMs?: number;
|
|
481
|
+
/** Drop the connection after sending headers (simulates network failure) */
|
|
482
|
+
dropConnection?: boolean;
|
|
483
|
+
/** Truncate response body to this many bytes */
|
|
484
|
+
truncateBodyBytes?: number;
|
|
485
|
+
/** Probability of triggering fault (0-1, default 1.0 = always) */
|
|
486
|
+
probability?: number;
|
|
487
|
+
/** Only match specific HTTP method (GET, POST, PUT, DELETE) */
|
|
488
|
+
method?: string;
|
|
489
|
+
/** Corrupt the response body by flipping random bits */
|
|
490
|
+
corruptBody?: boolean;
|
|
491
|
+
/** Add jitter to delay (random 0-jitterMs added to delayMs) */
|
|
492
|
+
jitterMs?: number;
|
|
493
|
+
}
|
|
335
494
|
declare class DurableStreamTestServer {
|
|
336
495
|
readonly store: StreamStore | FileBackedStreamStore;
|
|
337
496
|
private server;
|
|
@@ -339,8 +498,8 @@ declare class DurableStreamTestServer {
|
|
|
339
498
|
private _url;
|
|
340
499
|
private activeSSEResponses;
|
|
341
500
|
private isShuttingDown;
|
|
342
|
-
/** Injected
|
|
343
|
-
private
|
|
501
|
+
/** Injected faults for testing retry/resilience */
|
|
502
|
+
private injectedFaults;
|
|
344
503
|
constructor(options?: TestServerOptions);
|
|
345
504
|
/**
|
|
346
505
|
* Start the server.
|
|
@@ -361,17 +520,34 @@ declare class DurableStreamTestServer {
|
|
|
361
520
|
/**
|
|
362
521
|
* Inject an error to be returned on the next N requests to a path.
|
|
363
522
|
* Used for testing retry/resilience behavior.
|
|
523
|
+
* @deprecated Use injectFault for full fault injection capabilities
|
|
364
524
|
*/
|
|
365
525
|
injectError(path: string, status: number, count?: number, retryAfter?: number): void;
|
|
366
526
|
/**
|
|
367
|
-
*
|
|
527
|
+
* Inject a fault to be triggered on the next N requests to a path.
|
|
528
|
+
* Supports various fault types: delays, connection drops, body corruption, etc.
|
|
529
|
+
*/
|
|
530
|
+
injectFault(path: string, fault: Omit<InjectedFault, `count`> & {
|
|
531
|
+
count?: number;
|
|
532
|
+
}): void;
|
|
533
|
+
/**
|
|
534
|
+
* Clear all injected faults.
|
|
535
|
+
*/
|
|
536
|
+
clearInjectedFaults(): void;
|
|
537
|
+
/**
|
|
538
|
+
* Check if there's an injected fault for this path/method and consume it.
|
|
539
|
+
* Returns the fault config if one should be triggered, null otherwise.
|
|
540
|
+
*/
|
|
541
|
+
private consumeInjectedFault;
|
|
542
|
+
/**
|
|
543
|
+
* Apply delay from fault config (including jitter).
|
|
368
544
|
*/
|
|
369
|
-
|
|
545
|
+
private applyFaultDelay;
|
|
370
546
|
/**
|
|
371
|
-
*
|
|
372
|
-
* Returns
|
|
547
|
+
* Apply body modifications from stored fault (truncation, corruption).
|
|
548
|
+
* Returns modified body, or original if no modifications needed.
|
|
373
549
|
*/
|
|
374
|
-
private
|
|
550
|
+
private applyFaultBodyModification;
|
|
375
551
|
private handleRequest;
|
|
376
552
|
/**
|
|
377
553
|
* Handle PUT - create stream
|
package/dist/index.d.ts
CHANGED
|
@@ -56,6 +56,11 @@ interface Stream {
|
|
|
56
56
|
* Timestamp when the stream was created.
|
|
57
57
|
*/
|
|
58
58
|
createdAt: number;
|
|
59
|
+
/**
|
|
60
|
+
* Producer states for idempotent writes.
|
|
61
|
+
* Maps producer ID to their epoch and sequence state.
|
|
62
|
+
*/
|
|
63
|
+
producers?: Map<string, ProducerState>;
|
|
59
64
|
}
|
|
60
65
|
/**
|
|
61
66
|
* Event data for stream lifecycle hooks.
|
|
@@ -131,6 +136,49 @@ interface TestServerOptions {
|
|
|
131
136
|
cursorEpoch?: Date;
|
|
132
137
|
}
|
|
133
138
|
/**
|
|
139
|
+
* Producer state for idempotent writes.
|
|
140
|
+
* Tracks epoch and sequence number per producer ID for deduplication.
|
|
141
|
+
*/
|
|
142
|
+
interface ProducerState {
|
|
143
|
+
/**
|
|
144
|
+
* Current epoch for this producer.
|
|
145
|
+
* Client-declared, server-validated monotonically increasing.
|
|
146
|
+
*/
|
|
147
|
+
epoch: number;
|
|
148
|
+
/**
|
|
149
|
+
* Last sequence number received in this epoch.
|
|
150
|
+
*/
|
|
151
|
+
lastSeq: number;
|
|
152
|
+
/**
|
|
153
|
+
* Timestamp when this producer state was last updated.
|
|
154
|
+
* Used for TTL-based cleanup.
|
|
155
|
+
*/
|
|
156
|
+
lastUpdated: number;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Result of producer validation for append operations.
|
|
160
|
+
* For 'accepted' status, includes proposedState to commit after successful append.
|
|
161
|
+
*/
|
|
162
|
+
type ProducerValidationResult = {
|
|
163
|
+
status: `accepted`;
|
|
164
|
+
isNew: boolean;
|
|
165
|
+
/** State to commit after successful append (deferred mutation) */
|
|
166
|
+
proposedState: ProducerState;
|
|
167
|
+
producerId: string;
|
|
168
|
+
} | {
|
|
169
|
+
status: `duplicate`;
|
|
170
|
+
lastSeq: number;
|
|
171
|
+
} | {
|
|
172
|
+
status: `stale_epoch`;
|
|
173
|
+
currentEpoch: number;
|
|
174
|
+
} | {
|
|
175
|
+
status: `invalid_epoch_seq`;
|
|
176
|
+
} | {
|
|
177
|
+
status: `sequence_gap`;
|
|
178
|
+
expectedSeq: number;
|
|
179
|
+
receivedSeq: number;
|
|
180
|
+
};
|
|
181
|
+
/**
|
|
134
182
|
* Pending long-poll request.
|
|
135
183
|
*/
|
|
136
184
|
interface PendingLongPoll {
|
|
@@ -155,10 +203,32 @@ interface PendingLongPoll {
|
|
|
155
203
|
/**
|
|
156
204
|
* In-memory store for durable streams.
|
|
157
205
|
*/
|
|
206
|
+
/**
|
|
207
|
+
* Options for append operations.
|
|
208
|
+
*/
|
|
209
|
+
interface AppendOptions {
|
|
210
|
+
seq?: string;
|
|
211
|
+
contentType?: string;
|
|
212
|
+
producerId?: string;
|
|
213
|
+
producerEpoch?: number;
|
|
214
|
+
producerSeq?: number;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Result of an append operation.
|
|
218
|
+
*/
|
|
219
|
+
interface AppendResult {
|
|
220
|
+
message: StreamMessage | null;
|
|
221
|
+
producerResult?: ProducerValidationResult;
|
|
222
|
+
}
|
|
158
223
|
declare class StreamStore {
|
|
159
224
|
private streams;
|
|
160
225
|
private pendingLongPolls;
|
|
161
226
|
/**
|
|
227
|
+
* Per-producer locks for serializing validation+append operations.
|
|
228
|
+
* Key: "{streamPath}:{producerId}"
|
|
229
|
+
*/
|
|
230
|
+
private producerLocks;
|
|
231
|
+
/**
|
|
162
232
|
* Check if a stream is expired based on TTL or Expires-At.
|
|
163
233
|
*/
|
|
164
234
|
private isExpired;
|
|
@@ -192,15 +262,47 @@ declare class StreamStore {
|
|
|
192
262
|
*/
|
|
193
263
|
delete(path: string): boolean;
|
|
194
264
|
/**
|
|
265
|
+
* Validate producer state WITHOUT mutating.
|
|
266
|
+
* Returns proposed state to commit after successful append.
|
|
267
|
+
* Implements Kafka-style idempotent producer validation.
|
|
268
|
+
*
|
|
269
|
+
* IMPORTANT: This function does NOT mutate producer state. The caller must
|
|
270
|
+
* call commitProducerState() after successful append to apply the mutation.
|
|
271
|
+
* This ensures atomicity: if append fails (e.g., JSON validation), producer
|
|
272
|
+
* state is not incorrectly advanced.
|
|
273
|
+
*/
|
|
274
|
+
private validateProducer;
|
|
275
|
+
/**
|
|
276
|
+
* Commit producer state after successful append.
|
|
277
|
+
* This is the only place where producer state is mutated.
|
|
278
|
+
*/
|
|
279
|
+
private commitProducerState;
|
|
280
|
+
/**
|
|
281
|
+
* Clean up expired producer states from a stream.
|
|
282
|
+
*/
|
|
283
|
+
private cleanupExpiredProducers;
|
|
284
|
+
/**
|
|
285
|
+
* Acquire a lock for serialized producer operations.
|
|
286
|
+
* Returns a release function.
|
|
287
|
+
*/
|
|
288
|
+
private acquireProducerLock;
|
|
289
|
+
/**
|
|
195
290
|
* Append data to a stream.
|
|
196
291
|
* @throws Error if stream doesn't exist or is expired
|
|
197
292
|
* @throws Error if seq is lower than lastSeq
|
|
198
293
|
* @throws Error if JSON mode and array is empty
|
|
199
294
|
*/
|
|
200
|
-
append(path: string, data: Uint8Array, options?:
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
295
|
+
append(path: string, data: Uint8Array, options?: AppendOptions): StreamMessage | AppendResult;
|
|
296
|
+
/**
|
|
297
|
+
* Append with producer serialization for concurrent request handling.
|
|
298
|
+
* This ensures that validation+append is atomic per producer.
|
|
299
|
+
*/
|
|
300
|
+
appendWithProducer(path: string, data: Uint8Array, options: AppendOptions): Promise<AppendResult>;
|
|
301
|
+
/**
|
|
302
|
+
* Get the current epoch for a producer on a stream.
|
|
303
|
+
* Returns undefined if the producer doesn't exist or stream not found.
|
|
304
|
+
*/
|
|
305
|
+
getProducerEpoch(path: string, producerId: string): number | undefined;
|
|
204
306
|
/**
|
|
205
307
|
* Read messages from a stream starting at the given offset.
|
|
206
308
|
* @throws Error if stream doesn't exist or is expired
|
|
@@ -261,6 +363,11 @@ declare class FileBackedStreamStore {
|
|
|
261
363
|
private fileHandlePool;
|
|
262
364
|
private pendingLongPolls;
|
|
263
365
|
private dataDir;
|
|
366
|
+
/**
|
|
367
|
+
* Per-producer locks for serializing validation+append operations.
|
|
368
|
+
* Key: "{streamPath}:{producerId}"
|
|
369
|
+
*/
|
|
370
|
+
private producerLocks;
|
|
264
371
|
constructor(options: FileBackedStreamStoreOptions);
|
|
265
372
|
/**
|
|
266
373
|
* Recover streams from disk on startup.
|
|
@@ -277,6 +384,25 @@ declare class FileBackedStreamStore {
|
|
|
277
384
|
*/
|
|
278
385
|
private streamMetaToStream;
|
|
279
386
|
/**
|
|
387
|
+
* Validate producer state WITHOUT mutating.
|
|
388
|
+
* Returns proposed state to commit after successful append.
|
|
389
|
+
*
|
|
390
|
+
* IMPORTANT: This function does NOT mutate producer state. The caller must
|
|
391
|
+
* commit the proposedState after successful append (file write + fsync + LMDB).
|
|
392
|
+
* This ensures atomicity: if any step fails, producer state is not advanced.
|
|
393
|
+
*/
|
|
394
|
+
private validateProducer;
|
|
395
|
+
/**
|
|
396
|
+
* Acquire a lock for serialized producer operations.
|
|
397
|
+
* Returns a release function.
|
|
398
|
+
*/
|
|
399
|
+
private acquireProducerLock;
|
|
400
|
+
/**
|
|
401
|
+
* Get the current epoch for a producer on a stream.
|
|
402
|
+
* Returns undefined if the producer doesn't exist or stream not found.
|
|
403
|
+
*/
|
|
404
|
+
getProducerEpoch(streamPath: string, producerId: string): number | undefined;
|
|
405
|
+
/**
|
|
280
406
|
* Check if a stream is expired based on TTL or Expires-At.
|
|
281
407
|
*/
|
|
282
408
|
private isExpired;
|
|
@@ -299,11 +425,14 @@ declare class FileBackedStreamStore {
|
|
|
299
425
|
get(streamPath: string): Stream | undefined;
|
|
300
426
|
has(streamPath: string): boolean;
|
|
301
427
|
delete(streamPath: string): boolean;
|
|
302
|
-
append(streamPath: string, data: Uint8Array, options?: {
|
|
303
|
-
seq?: string;
|
|
304
|
-
contentType?: string;
|
|
428
|
+
append(streamPath: string, data: Uint8Array, options?: AppendOptions & {
|
|
305
429
|
isInitialCreate?: boolean;
|
|
306
|
-
}): Promise<StreamMessage | null>;
|
|
430
|
+
}): Promise<StreamMessage | AppendResult | null>;
|
|
431
|
+
/**
|
|
432
|
+
* Append with producer serialization for concurrent request handling.
|
|
433
|
+
* This ensures that validation+append is atomic per producer.
|
|
434
|
+
*/
|
|
435
|
+
appendWithProducer(streamPath: string, data: Uint8Array, options: AppendOptions): Promise<AppendResult>;
|
|
307
436
|
read(streamPath: string, offset?: string): {
|
|
308
437
|
messages: Array<StreamMessage>;
|
|
309
438
|
upToDate: boolean;
|
|
@@ -332,6 +461,36 @@ declare class FileBackedStreamStore {
|
|
|
332
461
|
|
|
333
462
|
//#endregion
|
|
334
463
|
//#region src/server.d.ts
|
|
464
|
+
/**
|
|
465
|
+
* HTTP server for testing durable streams.
|
|
466
|
+
* Supports both in-memory and file-backed storage modes.
|
|
467
|
+
*/
|
|
468
|
+
/**
|
|
469
|
+
* Configuration for injected faults (for testing retry/resilience).
|
|
470
|
+
* Supports various fault types beyond simple HTTP errors.
|
|
471
|
+
*/
|
|
472
|
+
interface InjectedFault {
|
|
473
|
+
/** HTTP status code to return (if set, returns error response) */
|
|
474
|
+
status?: number;
|
|
475
|
+
/** Number of times to trigger this fault (decremented on each use) */
|
|
476
|
+
count: number;
|
|
477
|
+
/** Optional Retry-After header value (seconds) */
|
|
478
|
+
retryAfter?: number;
|
|
479
|
+
/** Delay in milliseconds before responding */
|
|
480
|
+
delayMs?: number;
|
|
481
|
+
/** Drop the connection after sending headers (simulates network failure) */
|
|
482
|
+
dropConnection?: boolean;
|
|
483
|
+
/** Truncate response body to this many bytes */
|
|
484
|
+
truncateBodyBytes?: number;
|
|
485
|
+
/** Probability of triggering fault (0-1, default 1.0 = always) */
|
|
486
|
+
probability?: number;
|
|
487
|
+
/** Only match specific HTTP method (GET, POST, PUT, DELETE) */
|
|
488
|
+
method?: string;
|
|
489
|
+
/** Corrupt the response body by flipping random bits */
|
|
490
|
+
corruptBody?: boolean;
|
|
491
|
+
/** Add jitter to delay (random 0-jitterMs added to delayMs) */
|
|
492
|
+
jitterMs?: number;
|
|
493
|
+
}
|
|
335
494
|
declare class DurableStreamTestServer {
|
|
336
495
|
readonly store: StreamStore | FileBackedStreamStore;
|
|
337
496
|
private server;
|
|
@@ -339,8 +498,8 @@ declare class DurableStreamTestServer {
|
|
|
339
498
|
private _url;
|
|
340
499
|
private activeSSEResponses;
|
|
341
500
|
private isShuttingDown;
|
|
342
|
-
/** Injected
|
|
343
|
-
private
|
|
501
|
+
/** Injected faults for testing retry/resilience */
|
|
502
|
+
private injectedFaults;
|
|
344
503
|
constructor(options?: TestServerOptions);
|
|
345
504
|
/**
|
|
346
505
|
* Start the server.
|
|
@@ -361,17 +520,34 @@ declare class DurableStreamTestServer {
|
|
|
361
520
|
/**
|
|
362
521
|
* Inject an error to be returned on the next N requests to a path.
|
|
363
522
|
* Used for testing retry/resilience behavior.
|
|
523
|
+
* @deprecated Use injectFault for full fault injection capabilities
|
|
364
524
|
*/
|
|
365
525
|
injectError(path: string, status: number, count?: number, retryAfter?: number): void;
|
|
366
526
|
/**
|
|
367
|
-
*
|
|
527
|
+
* Inject a fault to be triggered on the next N requests to a path.
|
|
528
|
+
* Supports various fault types: delays, connection drops, body corruption, etc.
|
|
529
|
+
*/
|
|
530
|
+
injectFault(path: string, fault: Omit<InjectedFault, `count`> & {
|
|
531
|
+
count?: number;
|
|
532
|
+
}): void;
|
|
533
|
+
/**
|
|
534
|
+
* Clear all injected faults.
|
|
535
|
+
*/
|
|
536
|
+
clearInjectedFaults(): void;
|
|
537
|
+
/**
|
|
538
|
+
* Check if there's an injected fault for this path/method and consume it.
|
|
539
|
+
* Returns the fault config if one should be triggered, null otherwise.
|
|
540
|
+
*/
|
|
541
|
+
private consumeInjectedFault;
|
|
542
|
+
/**
|
|
543
|
+
* Apply delay from fault config (including jitter).
|
|
368
544
|
*/
|
|
369
|
-
|
|
545
|
+
private applyFaultDelay;
|
|
370
546
|
/**
|
|
371
|
-
*
|
|
372
|
-
* Returns
|
|
547
|
+
* Apply body modifications from stored fault (truncation, corruption).
|
|
548
|
+
* Returns modified body, or original if no modifications needed.
|
|
373
549
|
*/
|
|
374
|
-
private
|
|
550
|
+
private applyFaultBodyModification;
|
|
375
551
|
private handleRequest;
|
|
376
552
|
/**
|
|
377
553
|
* Handle PUT - create stream
|