flo-python 0.1.0__py3-none-any.whl

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.
flo/types.py ADDED
@@ -0,0 +1,804 @@
1
+ """Flo SDK Types
2
+
3
+ Core types, constants, and data classes for the Flo client SDK.
4
+ """
5
+
6
+ import struct
7
+ from dataclasses import dataclass
8
+ from enum import IntEnum
9
+ from typing import Optional
10
+
11
+ # =============================================================================
12
+ # Protocol Constants
13
+ # =============================================================================
14
+
15
+ MAGIC: int = 0x004F4C46 # "FLO\0" in little-endian
16
+ VERSION: int = 0x01
17
+ HEADER_SIZE: int = 24
18
+
19
+ # Size limits (for client-side validation)
20
+ MAX_NAMESPACE_SIZE: int = 255
21
+ MAX_KEY_SIZE: int = 64 * 1024 # 64 KB
22
+ MAX_VALUE_SIZE: int = 16 * 1024 * 1024 # 16 MB practical limit
23
+
24
+
25
+ # =============================================================================
26
+ # Enums
27
+ # =============================================================================
28
+
29
+
30
+ class OpCode(IntEnum):
31
+ """Operation codes for Flo protocol requests."""
32
+
33
+ # System Operations (0x00 - 0x0F)
34
+ PING = 0x00
35
+ PONG = 0x01
36
+ ERROR_RESPONSE = 0x02
37
+ AUTH = 0x03
38
+ SET_DURABILITY = 0x04
39
+ OK = 0x05
40
+
41
+ # Streams (0x10 - 0x1F)
42
+ STREAM_APPEND = 0x10
43
+ STREAM_READ = 0x11
44
+ STREAM_TRIM = 0x12
45
+ STREAM_INFO = 0x13
46
+ STREAM_APPEND_RESPONSE = 0x14
47
+ STREAM_READ_RESPONSE = 0x15
48
+ STREAM_EVENT = 0x16
49
+ STREAM_SUBSCRIBE = 0x17
50
+ STREAM_UNSUBSCRIBE = 0x18
51
+ STREAM_SUBSCRIBED = 0x19
52
+ STREAM_UNSUBSCRIBED = 0x1A
53
+ STREAM_LIST = 0x1B
54
+ STREAM_LIST_RESPONSE = 0x1C
55
+ STREAM_CREATE = 0x1D
56
+ STREAM_CREATE_RESPONSE = 0x1E
57
+ STREAM_ALTER = 0x1F
58
+
59
+ # Stream Consumer Groups (0x20 - 0x2F)
60
+ STREAM_GROUP_CREATE = 0x20
61
+ STREAM_GROUP_JOIN = 0x21
62
+ STREAM_GROUP_LEAVE = 0x22
63
+ STREAM_GROUP_READ = 0x23
64
+ STREAM_GROUP_ACK = 0x24
65
+ STREAM_GROUP_CLAIM = 0x25
66
+ STREAM_GROUP_PENDING = 0x26
67
+ STREAM_GROUP_CONFIGURE_SWEEPER = 0x27
68
+ STREAM_GROUP_READ_RESPONSE = 0x28
69
+ STREAM_GROUP_NACK = 0x29
70
+ STREAM_GROUP_TOUCH = 0x2A
71
+ STREAM_GROUP_INFO = 0x2B
72
+ STREAM_GROUP_DELETE = 0x2C
73
+
74
+ # KV Operations (0x30 - 0x3F)
75
+ KV_PUT = 0x30
76
+ KV_GET = 0x31
77
+ KV_DELETE = 0x32
78
+ KV_SCAN = 0x33
79
+ KV_HISTORY = 0x34
80
+ KV_GET_RESPONSE = 0x35
81
+ KV_PUT_RESPONSE = 0x36
82
+ KV_SCAN_RESPONSE = 0x37
83
+ KV_HISTORY_RESPONSE = 0x38
84
+
85
+ # Transactions (0x39 - 0x3B)
86
+ KV_BEGIN_TXN = 0x39
87
+ KV_COMMIT_TXN = 0x3A
88
+ KV_ROLLBACK_TXN = 0x3B
89
+
90
+ # Snapshots (0x3C - 0x3F)
91
+ KV_SNAPSHOT_CREATE = 0x3C
92
+ KV_SNAPSHOT_GET = 0x3D
93
+ KV_SNAPSHOT_RELEASE = 0x3E
94
+ KV_SNAPSHOT_CREATE_RESPONSE = 0x3F
95
+
96
+ # Queues (0x40 - 0x5F)
97
+ QUEUE_ENQUEUE = 0x40
98
+ QUEUE_DEQUEUE = 0x41
99
+ QUEUE_COMPLETE = 0x42
100
+ QUEUE_EXTEND_LEASE = 0x43
101
+ QUEUE_FAIL = 0x44
102
+ QUEUE_FAIL_AUTO = 0x45
103
+ QUEUE_DLQ_LIST = 0x46
104
+ QUEUE_DLQ_DELETE = 0x47
105
+ QUEUE_DLQ_REQUEUE = 0x48
106
+ QUEUE_DLQ_STATS = 0x49
107
+ QUEUE_PROMOTE_DUE = 0x4A
108
+ QUEUE_STATS = 0x4B
109
+ QUEUE_PEEK = 0x4C
110
+ QUEUE_TOUCH = 0x4D
111
+ QUEUE_BATCH_ENQUEUE = 0x4E
112
+ QUEUE_PURGE = 0x4F
113
+
114
+ # Queue responses (0x50 - 0x5F)
115
+ QUEUE_ENQUEUE_RESPONSE = 0x50
116
+ QUEUE_DEQUEUE_RESPONSE = 0x51
117
+ QUEUE_DLQ_LIST_RESPONSE = 0x52
118
+ QUEUE_STATS_RESPONSE = 0x53
119
+ QUEUE_PEEK_RESPONSE = 0x54
120
+ QUEUE_TOUCH_RESPONSE = 0x55
121
+ QUEUE_BATCH_ENQUEUE_RESPONSE = 0x56
122
+ QUEUE_PURGE_RESPONSE = 0x57
123
+
124
+ # Actions (0x60 - 0x68)
125
+ ACTION_REGISTER = 0x60
126
+ ACTION_INVOKE = 0x61
127
+ ACTION_STATUS = 0x62
128
+ ACTION_LIST = 0x63
129
+ ACTION_DELETE = 0x64
130
+ ACTION_REGISTER_RESPONSE = 0x65
131
+ ACTION_INVOKE_RESPONSE = 0x66
132
+ ACTION_STATUS_RESPONSE = 0x67
133
+ ACTION_LIST_RESPONSE = 0x68
134
+
135
+ # Workers (0x69 - 0x7F)
136
+ WORKER_REGISTER = 0x69
137
+ WORKER_TOUCH = 0x6A
138
+ WORKER_AWAIT = 0x6B
139
+ WORKER_COMPLETE = 0x6C
140
+ WORKER_FAIL = 0x6D
141
+ WORKER_LIST = 0x6E
142
+ WORKER_REGISTER_RESPONSE = 0x70
143
+ WORKER_TASK_ASSIGNMENT = 0x71
144
+ WORKER_LIST_RESPONSE = 0x72
145
+
146
+ # Workflows (0x80 - 0x91)
147
+ WORKFLOW_CREATE = 0x80
148
+ WORKFLOW_START = 0x81
149
+ WORKFLOW_SIGNAL = 0x82
150
+ WORKFLOW_CANCEL = 0x83
151
+ WORKFLOW_STATUS = 0x84
152
+ WORKFLOW_HISTORY = 0x85
153
+ WORKFLOW_LIST_RUNS = 0x86
154
+ WORKFLOW_GET_DEFINITION = 0x87
155
+ WORKFLOW_CREATE_RESPONSE = 0x88
156
+ WORKFLOW_START_RESPONSE = 0x89
157
+ WORKFLOW_STATUS_RESPONSE = 0x8A
158
+ WORKFLOW_HISTORY_RESPONSE = 0x8B
159
+ WORKFLOW_LIST_RUNS_RESPONSE = 0x8C
160
+ WORKFLOW_GET_DEFINITION_RESPONSE = 0x8D
161
+ WORKFLOW_DISABLE = 0x8E
162
+ WORKFLOW_ENABLE = 0x8F
163
+ WORKFLOW_DISABLE_RESPONSE = 0x90
164
+ WORKFLOW_ENABLE_RESPONSE = 0x91
165
+
166
+ # Cluster Management (0xA0 - 0xAF)
167
+ CLUSTER_STATUS = 0xA0
168
+ CLUSTER_MEMBERS = 0xA1
169
+ CLUSTER_JOIN = 0xA2
170
+ CLUSTER_LEAVE = 0xA3
171
+ CLUSTER_TRANSFER_LEADER = 0xA4
172
+ CLUSTER_ADD_NODE = 0xA5
173
+ CLUSTER_REMOVE_NODE = 0xA6
174
+ CLUSTER_STATUS_RESPONSE = 0xA8
175
+ CLUSTER_MEMBERS_RESPONSE = 0xA9
176
+ CLUSTER_JOIN_RESPONSE = 0xAA
177
+
178
+ # Namespace Management (0xB0 - 0xBF)
179
+ NAMESPACE_CREATE = 0xB0
180
+ NAMESPACE_DELETE = 0xB1
181
+ NAMESPACE_LIST = 0xB2
182
+ NAMESPACE_INFO = 0xB3
183
+ NAMESPACE_CREATE_RESPONSE = 0xB4
184
+ NAMESPACE_DELETE_RESPONSE = 0xB5
185
+ NAMESPACE_LIST_RESPONSE = 0xB6
186
+ NAMESPACE_INFO_RESPONSE = 0xB7
187
+
188
+ # Processing / Stream Processing (0xC0 - 0xD1)
189
+ PROCESSING_SUBMIT = 0xC0
190
+ PROCESSING_STOP = 0xC1
191
+ PROCESSING_CANCEL = 0xC2
192
+ PROCESSING_STATUS = 0xC3
193
+ PROCESSING_LIST = 0xC4
194
+ PROCESSING_SAVEPOINT = 0xC6
195
+ PROCESSING_RESTORE = 0xC7
196
+ PROCESSING_RESCALE = 0xC8
197
+ PROCESSING_SUBMIT_RESPONSE = 0xC9
198
+ PROCESSING_STOP_RESPONSE = 0xCA
199
+ PROCESSING_CANCEL_RESPONSE = 0xCB
200
+ PROCESSING_STATUS_RESPONSE = 0xCC
201
+ PROCESSING_LIST_RESPONSE = 0xCD
202
+ PROCESSING_SAVEPOINT_RESPONSE = 0xCF
203
+ PROCESSING_RESTORE_RESPONSE = 0xD0
204
+ PROCESSING_RESCALE_RESPONSE = 0xD1
205
+
206
+
207
+ class StatusCode(IntEnum):
208
+ """Status codes for Flo protocol responses."""
209
+
210
+ OK = 0
211
+ ERROR_GENERIC = 1
212
+ NOT_FOUND = 2
213
+ BAD_REQUEST = 3
214
+ CROSS_CORE_TRANSACTION = 4
215
+ NO_ACTIVE_TRANSACTION = 5
216
+ GROUP_LOCKED = 6
217
+ UNAUTHORIZED = 7
218
+ CONFLICT = 8
219
+ INTERNAL_ERROR = 9
220
+ OVERLOADED = 10
221
+ RATE_LIMITED = 11
222
+
223
+ def message(self) -> str:
224
+ """Get human-readable error message."""
225
+ messages = {
226
+ StatusCode.OK: "OK",
227
+ StatusCode.ERROR_GENERIC: "Generic error",
228
+ StatusCode.NOT_FOUND: "Not found",
229
+ StatusCode.BAD_REQUEST: "Bad request",
230
+ StatusCode.CROSS_CORE_TRANSACTION: "Cross-core transaction not supported",
231
+ StatusCode.NO_ACTIVE_TRANSACTION: "No active transaction",
232
+ StatusCode.GROUP_LOCKED: "Consumer group is locked",
233
+ StatusCode.UNAUTHORIZED: "Unauthorized",
234
+ StatusCode.CONFLICT: "Conflict",
235
+ StatusCode.INTERNAL_ERROR: "Internal server error",
236
+ StatusCode.OVERLOADED: "Server overloaded",
237
+ StatusCode.RATE_LIMITED: "Request rate limit exceeded",
238
+ }
239
+ return messages.get(self, "Unknown error")
240
+
241
+
242
+ class OptionTag(IntEnum):
243
+ """Option tags for TLV-encoded operation parameters.
244
+
245
+ Organized by feature area, matching proto.zig definitions.
246
+ """
247
+
248
+ # KV Options (0x01 - 0x0F)
249
+ TTL_SECONDS = 0x01 # u64: Time-to-live in seconds (0 = no expiration)
250
+ CAS_VERSION = 0x02 # u64: Expected version for compare-and-swap
251
+ IF_NOT_EXISTS = 0x03 # void: Only set if key doesn't exist (NX)
252
+ IF_EXISTS = 0x04 # void: Only set if key exists (XX)
253
+ LIMIT = 0x05 # u32: Maximum number of results for scan/list operations
254
+ KEYS_ONLY = 0x06 # u8: Skip values in scan response (0/1)
255
+ CURSOR = 0x07 # bytes: Pagination cursor (ShardWalker format)
256
+
257
+ # Queue Options (0x10 - 0x1F)
258
+ PRIORITY = 0x10 # u8: Message priority (0-255, higher = more urgent)
259
+ DELAY_MS = 0x11 # u64: Delay before message becomes visible
260
+ VISIBILITY_TIMEOUT_MS = 0x12 # u32: How long message is invisible after dequeue
261
+ DEDUP_KEY = 0x13 # string: Deduplication key
262
+ MAX_RETRIES = 0x14 # u8: Maximum retry attempts before DLQ
263
+ COUNT = 0x15 # u32: Number of messages to dequeue
264
+ SEND_TO_DLQ = 0x16 # u8: Whether to send failed messages to DLQ (0/1)
265
+ BLOCK_MS = 0x17 # u32: Block timeout - wait until exists (0=forever)
266
+ WAIT_MS = 0x18 # u32: Watch timeout - wait for NEXT version change (0=forever)
267
+
268
+ # Stream Options (0x20 - 0x2F) - StreamID-native ONLY
269
+ # All stream positioning uses StreamID (timestamp_ms + sequence)
270
+ # 0x20 reserved
271
+ STREAM_START = 0x21 # [16]u8: Start StreamID for reads (inclusive)
272
+ STREAM_END = 0x22 # [16]u8: End StreamID for reads (inclusive)
273
+ STREAM_TAIL = 0x23 # void: Flag indicating tail read (start from end)
274
+ PARTITION = 0x24 # u32: Explicit partition index
275
+ PARTITION_KEY = 0x25 # string: Key for partition routing
276
+ MAX_AGE_SECONDS = 0x26 # u64: Maximum age in seconds for retention
277
+ MAX_BYTES = 0x27 # u64: Maximum size in bytes for retention
278
+ DRY_RUN = 0x28 # void: Flag to preview what would be deleted
279
+ RETENTION_COUNT = 0x29 # u64: Retention policy - max event count
280
+ RETENTION_AGE = 0x2A # u64: Retention policy - max age in seconds
281
+ RETENTION_BYTES = 0x2B # u64: Retention policy - max bytes
282
+
283
+ # Consumer Group Options (0x30 - 0x3F)
284
+ ACK_TIMEOUT_MS = 0x30 # u32: Time before unacked message auto-redelivers
285
+ MAX_DELIVER = 0x31 # u8: Max delivery attempts before DLQ (default: 10)
286
+ SUBSCRIPTION_MODE = 0x32 # u8: 0=shared, 1=exclusive, 2=key_shared
287
+ REDELIVERY_DELAY_MS = 0x33 # u32: Delay before NACK'd message becomes visible
288
+ CONSUMER_TIMEOUT_MS = 0x34 # u32: Remove consumer from group if no activity
289
+ NO_ACK = 0x35 # void: Auto-ack on delivery (at-most-once)
290
+ IDLE_TIMEOUT_MS = 0x36 # u64: Min idle time for claiming stuck messages
291
+ MAX_ACK_PENDING = 0x37 # u32: Max unacked messages per consumer
292
+ EXTEND_ACK_MS = 0x38 # u32: Amount of time to extend ack deadline
293
+ MAX_STANDBYS = 0x39 # u16: Max standby consumers in exclusive mode
294
+ NUM_SLOTS = 0x3A # u16: Number of hash slots for key_shared mode
295
+
296
+ # Worker/Action Options (0x40 - 0x4F)
297
+ WORKER_ID = 0x40 # string: Worker identifier
298
+ EXTEND_MS = 0x41 # u32: Lease extension time in milliseconds
299
+ MAX_TASKS = 0x42 # u32: Maximum tasks to return in batch
300
+ RETRY = 0x43 # u8: Whether to retry on failure (0/1)
301
+
302
+ # Workflow Options (0x50 - 0x5F)
303
+ TIMEOUT_MS = 0x50 # u64: Workflow/activity timeout
304
+ RETRY_POLICY = 0x51 # bytes: Serialized retry policy
305
+ CORRELATION_ID = 0x52 # string: Correlation ID for tracing
306
+ SUBSCRIPTION_ID = 0x53 # u64: Subscription ID for stream subscriptions
307
+
308
+
309
+ # =============================================================================
310
+ # Result Types
311
+ # =============================================================================
312
+
313
+
314
+ @dataclass
315
+ class KVEntry:
316
+ """KV entry from scan results."""
317
+
318
+ key: bytes
319
+ value: bytes | None # None if keys_only=True
320
+
321
+
322
+ @dataclass
323
+ class ScanResult:
324
+ """Result of a KV scan operation."""
325
+
326
+ entries: list[KVEntry]
327
+ cursor: bytes | None # None if no more pages
328
+ has_more: bool
329
+
330
+
331
+ @dataclass
332
+ class VersionEntry:
333
+ """KV version entry from history."""
334
+
335
+ version: int
336
+ timestamp: int
337
+ value: bytes
338
+
339
+
340
+ @dataclass
341
+ class Message:
342
+ """Queue message."""
343
+
344
+ seq: int
345
+ payload: bytes
346
+
347
+
348
+ @dataclass
349
+ class DequeueResult:
350
+ """Result of a queue dequeue operation."""
351
+
352
+ messages: list[Message]
353
+
354
+
355
+ # =============================================================================
356
+ # Stream Types
357
+ # =============================================================================
358
+
359
+
360
+ @dataclass
361
+ class StreamID:
362
+ """Unique position in a stream (timestamp_ms + sequence).
363
+
364
+ The StreamID format is: [timestamp_ms: u64][sequence: u64] = 16 bytes total.
365
+ """
366
+
367
+ timestamp_ms: int = 0
368
+ sequence: int = 0
369
+
370
+ def to_bytes(self) -> bytes:
371
+ """Serialize the StreamID to 16 bytes (little-endian)."""
372
+ return struct.pack("<QQ", self.timestamp_ms, self.sequence)
373
+
374
+ @classmethod
375
+ def from_bytes(cls, data: bytes) -> "StreamID":
376
+ """Parse a StreamID from 16 bytes (little-endian)."""
377
+ if len(data) < 16:
378
+ raise ValueError(f"Invalid StreamID: expected 16 bytes, got {len(data)}")
379
+ ts, seq = struct.unpack("<QQ", data[:16])
380
+ return cls(timestamp_ms=ts, sequence=seq)
381
+
382
+ @classmethod
383
+ def from_sequence(cls, seq: int) -> "StreamID":
384
+ """Create a StreamID with just a sequence number.
385
+
386
+ Used for backwards compatibility with offset-based reads.
387
+ """
388
+ return cls(timestamp_ms=0, sequence=seq)
389
+
390
+
391
+ class StorageTier(IntEnum):
392
+ """Storage tier of a stream record."""
393
+
394
+ HOT = 0
395
+ PENDING = 1
396
+ WARM = 2
397
+ COLD = 3
398
+
399
+
400
+ @dataclass
401
+ class StreamRecord:
402
+ """A record in a stream."""
403
+
404
+ sequence: int
405
+ timestamp_ms: int
406
+ tier: StorageTier = StorageTier.HOT
407
+ payload: bytes = b""
408
+ headers: dict[str, str] | None = None
409
+
410
+
411
+ @dataclass
412
+ class StreamAppendResult:
413
+ """Result of appending to a stream."""
414
+
415
+ sequence: int
416
+ timestamp_ms: int
417
+
418
+
419
+ @dataclass
420
+ class StreamReadResult:
421
+ """Result of reading from a stream."""
422
+
423
+ records: list[StreamRecord]
424
+
425
+
426
+ @dataclass
427
+ class StreamInfo:
428
+ """Stream metadata."""
429
+
430
+ first_seq: int
431
+ last_seq: int
432
+ count: int
433
+ bytes_size: int
434
+
435
+
436
+ # =============================================================================
437
+ # Option Types (for operation parameters)
438
+ # =============================================================================
439
+
440
+
441
+ @dataclass
442
+ class GetOptions:
443
+ """Options for KV get operations."""
444
+
445
+ namespace: str | None = None
446
+ block_ms: int | None = None # Block until value available (0 = infinite)
447
+
448
+
449
+ @dataclass
450
+ class PutOptions:
451
+ """Options for KV put operations."""
452
+
453
+ namespace: str | None = None
454
+ ttl_seconds: int | None = None
455
+ cas_version: int | None = None
456
+ if_not_exists: bool = False
457
+ if_exists: bool = False
458
+
459
+
460
+ @dataclass
461
+ class DeleteOptions:
462
+ """Options for KV delete operations."""
463
+
464
+ namespace: str | None = None
465
+
466
+
467
+ @dataclass
468
+ class ScanOptions:
469
+ """Options for KV scan operations."""
470
+
471
+ namespace: str | None = None
472
+ cursor: bytes | None = None
473
+ limit: int | None = None
474
+ keys_only: bool = False
475
+
476
+
477
+ @dataclass
478
+ class HistoryOptions:
479
+ """Options for KV history operations."""
480
+
481
+ namespace: str | None = None
482
+ limit: int | None = None
483
+
484
+
485
+ @dataclass
486
+ class EnqueueOptions:
487
+ """Options for queue enqueue operations."""
488
+
489
+ namespace: str | None = None
490
+ priority: int = 0
491
+ delay_ms: int | None = None
492
+ dedup_key: str | None = None
493
+
494
+
495
+ @dataclass
496
+ class DequeueOptions:
497
+ """Options for queue dequeue operations."""
498
+
499
+ namespace: str | None = None
500
+ visibility_timeout_ms: int | None = None
501
+ block_ms: int | None = None
502
+
503
+
504
+ @dataclass
505
+ class AckOptions:
506
+ """Options for queue ack operations."""
507
+
508
+ namespace: str | None = None
509
+
510
+
511
+ @dataclass
512
+ class NackOptions:
513
+ """Options for queue nack operations."""
514
+
515
+ namespace: str | None = None
516
+ to_dlq: bool = False
517
+
518
+
519
+ @dataclass
520
+ class DlqListOptions:
521
+ """Options for DLQ list operations."""
522
+
523
+ namespace: str | None = None
524
+ limit: int = 100
525
+
526
+
527
+ @dataclass
528
+ class DlqRequeueOptions:
529
+ """Options for DLQ requeue operations."""
530
+
531
+ namespace: str | None = None
532
+
533
+
534
+ @dataclass
535
+ class PeekOptions:
536
+ """Options for queue peek operations."""
537
+
538
+ namespace: str | None = None
539
+
540
+
541
+ @dataclass
542
+ class TouchOptions:
543
+ """Options for queue touch (lease renewal) operations."""
544
+
545
+ namespace: str | None = None
546
+
547
+
548
+ # =============================================================================
549
+ # Stream Option Types
550
+ # =============================================================================
551
+
552
+
553
+ @dataclass
554
+ class StreamAppendOptions:
555
+ """Options for stream append operations."""
556
+
557
+ namespace: str | None = None
558
+ headers: dict[str, str] | None = None
559
+
560
+
561
+ @dataclass
562
+ class StreamReadOptions:
563
+ """Options for stream read operations.
564
+
565
+ Uses StreamID-native positioning (timestamp_ms + sequence).
566
+ """
567
+
568
+ namespace: str | None = None
569
+ start: Optional["StreamID"] = None # Start StreamID for reads (inclusive)
570
+ end: Optional["StreamID"] = None # End StreamID for reads (inclusive)
571
+ tail: bool = False # Start from end of stream (mutually exclusive with start)
572
+ partition: int | None = None # Explicit partition index
573
+ count: int | None = None # Maximum number of records to return
574
+ block_ms: int | None = None # Blocking timeout (0 = infinite)
575
+
576
+
577
+ @dataclass
578
+ class StreamTrimOptions:
579
+ """Options for stream trim operations."""
580
+
581
+ namespace: str | None = None
582
+ max_len: int | None = None # Retention policy - max event count
583
+ max_age_seconds: int | None = None # Retention policy - max age in seconds
584
+ max_bytes: int | None = None # Retention policy - max bytes
585
+ dry_run: bool = False # Preview what would be deleted
586
+
587
+
588
+ @dataclass
589
+ class StreamInfoOptions:
590
+ """Options for stream info operations."""
591
+
592
+ namespace: str | None = None
593
+
594
+
595
+ @dataclass
596
+ class StreamGroupJoinOptions:
597
+ """Options for joining a consumer group."""
598
+
599
+ namespace: str | None = None
600
+
601
+
602
+ @dataclass
603
+ class StreamGroupReadOptions:
604
+ """Options for reading from a consumer group."""
605
+
606
+ namespace: str | None = None
607
+ count: int | None = None # Max records to read
608
+ block_ms: int | None = None # Block waiting for records
609
+
610
+
611
+ @dataclass
612
+ class StreamGroupAckOptions:
613
+ """Options for acknowledging records in a consumer group."""
614
+
615
+ namespace: str | None = None
616
+
617
+
618
+ # =============================================================================
619
+ # Action Types
620
+ # =============================================================================
621
+
622
+
623
+ class ActionType(IntEnum):
624
+ """Type of action."""
625
+
626
+ USER = 0 # External worker-based action
627
+ WASM = 1 # WebAssembly action
628
+
629
+
630
+ @dataclass
631
+ class ActionInfo:
632
+ """Information about a registered action."""
633
+
634
+ name: str
635
+ action_type: ActionType
636
+ timeout_ms: int
637
+ max_retries: int
638
+ description: str | None = None
639
+
640
+
641
+ @dataclass
642
+ class ActionRunStatus:
643
+ """Status of an action run."""
644
+
645
+ run_id: str
646
+ status: str # "pending", "running", "completed", "failed"
647
+ result: bytes | None = None
648
+ error: str | None = None
649
+
650
+
651
+ @dataclass
652
+ class ActionInvokeResult:
653
+ """Result of invoking an action."""
654
+
655
+ run_id: str
656
+
657
+
658
+ @dataclass
659
+ class ActionListResult:
660
+ """Result of listing actions."""
661
+
662
+ actions: list[ActionInfo]
663
+ cursor: bytes | None = None
664
+
665
+
666
+ # =============================================================================
667
+ # Action Option Types
668
+ # =============================================================================
669
+
670
+
671
+ @dataclass
672
+ class ActionRegisterOptions:
673
+ """Options for registering an action."""
674
+
675
+ namespace: str | None = None
676
+ timeout_ms: int = 30000
677
+ max_retries: int = 3
678
+ description: str | None = None
679
+
680
+
681
+ @dataclass
682
+ class ActionInvokeOptions:
683
+ """Options for invoking an action."""
684
+
685
+ namespace: str | None = None
686
+ priority: int = 10
687
+ idempotency_key: str | None = None
688
+
689
+
690
+ @dataclass
691
+ class ActionStatusOptions:
692
+ """Options for getting action status."""
693
+
694
+ namespace: str | None = None
695
+
696
+
697
+ @dataclass
698
+ class ActionListOptions:
699
+ """Options for listing actions."""
700
+
701
+ namespace: str | None = None
702
+ limit: int = 100
703
+ prefix: str | None = None
704
+
705
+
706
+ @dataclass
707
+ class ActionDeleteOptions:
708
+ """Options for deleting an action."""
709
+
710
+ namespace: str | None = None
711
+
712
+
713
+ # =============================================================================
714
+ # Worker Types
715
+ # =============================================================================
716
+
717
+
718
+ @dataclass
719
+ class TaskAssignment:
720
+ """A task assigned to a worker."""
721
+
722
+ task_id: str
723
+ task_type: str
724
+ payload: bytes
725
+ created_at: int
726
+ attempt: int
727
+
728
+
729
+ # Alias for backwards compatibility
730
+ WorkerTask = TaskAssignment
731
+
732
+
733
+ @dataclass
734
+ class WorkerAwaitResult:
735
+ """Result of awaiting a task."""
736
+
737
+ task: TaskAssignment | None = None # None if no task available
738
+
739
+
740
+ @dataclass
741
+ class WorkerInfo:
742
+ """Information about a registered worker."""
743
+
744
+ worker_id: str
745
+ task_types: list[str]
746
+
747
+
748
+ @dataclass
749
+ class WorkerListResult:
750
+ """Result of listing workers."""
751
+
752
+ workers: list[WorkerInfo]
753
+
754
+
755
+ # =============================================================================
756
+ # Worker Option Types
757
+ # =============================================================================
758
+
759
+
760
+ @dataclass
761
+ class WorkerRegisterOptions:
762
+ """Options for registering a worker."""
763
+
764
+ namespace: str | None = None
765
+
766
+
767
+ @dataclass
768
+ class WorkerAwaitOptions:
769
+ """Options for awaiting a task."""
770
+
771
+ namespace: str | None = None
772
+ block_ms: int | None = None # Block waiting for task (0 = infinite)
773
+ timeout_ms: int | None = None
774
+
775
+
776
+ @dataclass
777
+ class WorkerTouchOptions:
778
+ """Options for extending task lease."""
779
+
780
+ namespace: str | None = None
781
+ extend_ms: int = 30000
782
+
783
+
784
+ @dataclass
785
+ class WorkerCompleteOptions:
786
+ """Options for completing a task."""
787
+
788
+ namespace: str | None = None
789
+
790
+
791
+ @dataclass
792
+ class WorkerFailOptions:
793
+ """Options for failing a task."""
794
+
795
+ namespace: str | None = None
796
+ retry: bool = True # Whether to retry the task
797
+
798
+
799
+ @dataclass
800
+ class WorkerListOptions:
801
+ """Options for listing workers."""
802
+
803
+ namespace: str | None = None
804
+ limit: int = 100