effect-redis 0.0.16 → 0.0.18

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 CHANGED
@@ -171,6 +171,46 @@ Gives access to the raw `RedisClient` from `redis`. The connection is shared and
171
171
 
172
172
  ---
173
173
 
174
+ ## CLI Tool - redistail
175
+
176
+ The package includes a CLI tool called `redistail` for monitoring Redis pub/sub channels and streams.
177
+
178
+ ### Version Information
179
+
180
+ To display the version and build information:
181
+
182
+ ```bash
183
+ redistail -v
184
+ # or
185
+ redistail --version
186
+ ```
187
+
188
+ Output format:
189
+ ```
190
+ redistail 0.0.17
191
+ Build: abc1234
192
+ ```
193
+
194
+ The build version is the Git commit hash of the specific build. When not in a Git repository, it will show "Build: dev".
195
+
196
+ ### Usage
197
+
198
+ ```bash
199
+ redistail <pubsub|stream> <topic-name>
200
+ ```
201
+
202
+ Options:
203
+ - `-h, --help`: Show help information
204
+ - `-v, --version`: Show version and build information
205
+
206
+ Examples:
207
+ ```bash
208
+ redistail pubsub my-channel
209
+ redistail stream my-stream
210
+ ```
211
+
212
+ ---
213
+
174
214
  ## Contributing / TODO
175
215
 
176
216
  * Expose more common redis commands (get, del, etc.)
@@ -0,0 +1,50 @@
1
+ /**
2
+ * CLI Configuration Service for redistail utility.
3
+ *
4
+ * This module provides services for parsing command-line arguments,
5
+ * loading environment configuration, and displaying help/version information.
6
+ * Built using Effect-TS patterns with proper error handling and dependency injection.
7
+ */
8
+ import { Context, Effect, Layer } from 'effect';
9
+ import { CLIConfig, RedistailConfig } from './types.js';
10
+ import type { CLIError, ConfigError } from './types.js';
11
+ /**
12
+ * CLI Configuration Service interface
13
+ */
14
+ export interface CLIConfigService {
15
+ readonly parseArgs: (args: string[]) => Effect.Effect<CLIConfig, CLIError>;
16
+ readonly loadConfig: () => Effect.Effect<RedistailConfig, ConfigError>;
17
+ readonly showHelp: () => Effect.Effect<void, never>;
18
+ readonly showVersion: () => Effect.Effect<void, never>;
19
+ }
20
+ /**
21
+ * Service tag for dependency injection
22
+ */
23
+ export declare const CLIConfigService: Context.Tag<CLIConfigService, CLIConfigService>;
24
+ /**
25
+ * Live layer for CLI Configuration Service
26
+ */
27
+ export declare const CLIConfigServiceLive: Layer.Layer<CLIConfigService>;
28
+ /**
29
+ * Parse arguments and load configuration in one operation
30
+ */
31
+ export declare const parseArgsAndLoadConfig: (args: string[]) => Effect.Effect<{
32
+ cli: CLIConfig;
33
+ config: RedistailConfig;
34
+ }, CLIError | ConfigError, CLIConfigService>;
35
+ /**
36
+ * Handle help and version flags, returning true if handled
37
+ */
38
+ export declare const handleHelpAndVersion: (cliConfig: CLIConfig) => Effect.Effect<boolean, never, CLIConfigService>;
39
+ /**
40
+ * Validate CLI configuration for runtime use
41
+ */
42
+ export declare const validateCLIConfig: (cliConfig: CLIConfig) => Effect.Effect<CLIConfig, CLIError>;
43
+ /**
44
+ * Create a complete configuration for the redistail application
45
+ */
46
+ export declare const createCompleteConfig: (args: string[]) => Effect.Effect<{
47
+ cli: CLIConfig;
48
+ config: RedistailConfig;
49
+ shouldExit: boolean;
50
+ }, CLIError | ConfigError, CLIConfigService>;
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Display service for formatting and outputting Redis messages.
3
+ *
4
+ * This service handles message formatting for both PubSub and Stream messages,
5
+ * including timestamp formatting, color support, JSON pretty-printing,
6
+ * safe handling of binary content, and proper output stream separation.
7
+ */
8
+ import { Context, Effect, Layer } from 'effect';
9
+ import type { PubSubMessage, StreamMessage, RedistailError } from './types.js';
10
+ import { RedistailConfig } from './types.js';
11
+ /**
12
+ * Service interface for message display and formatting
13
+ */
14
+ export interface DisplayService {
15
+ readonly formatPubSubMessage: (message: PubSubMessage) => Effect.Effect<string, never>;
16
+ readonly formatStreamMessage: (message: StreamMessage) => Effect.Effect<string, never>;
17
+ readonly formatError: (error: RedistailError) => Effect.Effect<string, never>;
18
+ readonly outputMessage: (formatted: string) => Effect.Effect<void, never>;
19
+ readonly outputError: (error: string) => Effect.Effect<void, never>;
20
+ }
21
+ /**
22
+ * Service tag for dependency injection
23
+ */
24
+ export declare const DisplayServiceTag: Context.Tag<DisplayService, DisplayService>;
25
+ /**
26
+ * Create a DisplayService layer with the given configuration
27
+ */
28
+ export declare const makeDisplayServiceLayer: (config: RedistailConfig) => Layer.Layer<DisplayService>;
29
+ /**
30
+ * Live layer that provides DisplayService implementation
31
+ * This is a convenience layer that uses default configuration
32
+ */
33
+ export declare const DisplayServiceLive: Layer.Layer<DisplayService>;
34
+ /**
35
+ * Create a DisplayService for testing with custom configuration
36
+ */
37
+ export declare const createTestDisplayService: (config: RedistailConfig) => DisplayService;
38
+ /**
39
+ * Helper to strip ANSI color codes from formatted output for testing
40
+ */
41
+ export declare const stripAnsiColors: (text: string) => string;
42
+ /**
43
+ * Helper to check if text contains ANSI color codes
44
+ */
45
+ export declare const hasAnsiColors: (text: string) => boolean;
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Layer composition and dependency injection for the redistail CLI utility.
3
+ *
4
+ * This module provides all service layers and their composition for the redistail
5
+ * application. It follows Effect-TS patterns for dependency injection, resource
6
+ * management, and proper error handling. The layers are composed to create a
7
+ * complete application environment with all necessary services.
8
+ *
9
+ * Requirements addressed:
10
+ * - 7.1: Integration with effect-redis package services
11
+ * - 7.2: Effect-TS patterns for error handling and resource management
12
+ * - 7.3: Service composition through Effect Layers
13
+ * - 7.4: Proper error handling and resource management
14
+ */
15
+ import { Context, Effect, Layer } from 'effect';
16
+ import { Redis, RedisPubSub, RedisStream, RedisConnectionOptions, RedisConnectionOptionsLive } from '../redis.js';
17
+ import { CLIConfigService } from './config-service.js';
18
+ import { DisplayServiceTag, type DisplayService } from './display-service.js';
19
+ import { PubSubMonitorService } from './pubsub-monitor-service.js';
20
+ import { StreamMonitorService } from './stream-monitor-service.js';
21
+ import { SignalHandlerService } from './signal-handler-service.js';
22
+ import type { RedistailConfig, ConfigError, RedistailError } from './types.js';
23
+ /**
24
+ * Context tag for RedistailConfig dependency injection
25
+ */
26
+ declare const RedistailConfigTag: Context.Tag<RedistailConfig, RedistailConfig>;
27
+ /**
28
+ * Live layer that provides RedistailConfig by loading from environment
29
+ * This layer depends on CLIConfigService to load the configuration
30
+ */
31
+ export declare const RedistailConfigLive: Layer.Layer<RedistailConfig, ConfigError, CLIConfigService>;
32
+ /**
33
+ * Live layer that provides Redis connection configuration based on RedistailConfig
34
+ * This layer creates the appropriate Redis connection options from the loaded configuration
35
+ *
36
+ * Requirement 7.1: Integrates with effect-redis RedisConnectionOptions
37
+ */
38
+ export declare const RedisConnectionLive: Layer.Layer<RedisConnectionOptions, never, RedistailConfig>;
39
+ /**
40
+ * Live layer that provides DisplayService with configuration
41
+ * This layer depends on RedistailConfig to configure display options
42
+ */
43
+ export declare const DisplayServiceLive: Layer.Layer<DisplayService, never, RedistailConfig>;
44
+ /**
45
+ * Create a Redis connection layer with custom options
46
+ *
47
+ * Requirement 7.1: Provides integration with effect-redis connection system
48
+ */
49
+ export declare const createRedisConnectionLayer: (host: string, port: number, options?: {
50
+ url?: string;
51
+ timeout?: number;
52
+ retryAttempts?: number;
53
+ retryDelay?: number;
54
+ }) => Layer.Layer<RedisConnectionOptions>;
55
+ /**
56
+ * Create a layer with custom Redis connection options
57
+ */
58
+ export declare const createRedisOptionsLayer: (options: Parameters<typeof RedisConnectionOptionsLive>[0]) => Layer.Layer<RedisConnectionOptions>;
59
+ /**
60
+ * Create a test configuration layer
61
+ */
62
+ export declare const createTestConfigLayer: (config: RedistailConfig) => Layer.Layer<RedistailConfig>;
63
+ export declare const AppLive: Layer.Layer<RedisPubSub | RedisStream | RedistailConfig | CLIConfigService | DisplayService | PubSubMonitorService | StreamMonitorService | SignalHandlerService, import("../types.js").RedisError | ConfigError, never>;
64
+ /**
65
+ * Layer for PubSub-only applications
66
+ * Provides only the services needed for PubSub monitoring
67
+ *
68
+ * Requirement 7.3: Service composition through Effect Layers
69
+ */
70
+ export declare const PubSubAppLive: Layer.Layer<RedisPubSub | RedistailConfig | CLIConfigService | DisplayService | PubSubMonitorService | SignalHandlerService, import("../types.js").RedisError | ConfigError, never>;
71
+ /**
72
+ * Layer for Stream-only applications
73
+ * Provides only the services needed for Stream monitoring
74
+ *
75
+ * Requirement 7.3: Service composition through Effect Layers
76
+ */
77
+ export declare const StreamAppLive: Layer.Layer<RedisStream | RedistailConfig | CLIConfigService | DisplayService | StreamMonitorService | SignalHandlerService, import("../types.js").RedisError | ConfigError, never>;
78
+ /**
79
+ * Create a test layer with custom configuration
80
+ * Useful for testing with specific Redis configurations
81
+ *
82
+ * Requirement 7.4: Proper resource management for testing
83
+ */
84
+ export declare const createTestAppLayer: (config: RedistailConfig) => Layer.Layer<RedisConnectionOptions | Redis | RedisPubSub | RedisStream | RedistailConfig | CLIConfigService | DisplayService | PubSubMonitorService | StreamMonitorService | SignalHandlerService, import("../types.js").RedisError | ConfigError, RedisConnectionOptions | RedisPubSub | RedisStream | RedistailConfig | CLIConfigService>;
85
+ /**
86
+ * Create a minimal test layer with mock Redis connection
87
+ * Useful for unit testing without actual Redis connection
88
+ *
89
+ * Requirement 7.4: Resource management for testing scenarios
90
+ */
91
+ export declare const createMockAppLayer: (config: RedistailConfig) => Layer.Layer<RedistailConfig | CLIConfigService | DisplayService | SignalHandlerService, never, RedistailConfig>;
92
+ /**
93
+ * Helper to handle layer initialization errors gracefully
94
+ * Provides proper error handling and resource management (Requirement 7.4)
95
+ */
96
+ export declare const handleLayerError: <E>(error: E) => Effect.Effect<never, never>;
97
+ /**
98
+ * Create a default RedistailConfig for testing and development
99
+ *
100
+ * Requirement 7.4: Proper configuration management
101
+ */
102
+ export declare const createDefaultConfig: (overrides?: Partial<RedistailConfig>) => RedistailConfig;
103
+ /**
104
+ * Validate that all required services are available in a layer
105
+ * This is a compile-time check to ensure layer completeness
106
+ *
107
+ * Requirement 7.3: Service composition validation
108
+ */
109
+ export declare const validateAppLayer: () => void;
110
+ export { CLIConfigService, DisplayServiceTag, PubSubMonitorService, StreamMonitorService, SignalHandlerService, Redis, RedisPubSub, RedisStream, };
111
+ export type { RedistailConfig, RedistailError, ConfigError };
112
+ export { RedistailConfigTag };
113
+ /**
114
+ * Example usage of the layers:
115
+ *
116
+ * ```typescript
117
+ * import { AppLive, runWithAppLayer } from './layers.js';
118
+ * import { Effect } from 'effect';
119
+ *
120
+ * const myProgram = Effect.gen(function* () {
121
+ * const cliService = yield* CLIConfigService;
122
+ * const config = yield* cliService.loadConfig();
123
+ * // ... use other services
124
+ * });
125
+ *
126
+ * // Run with complete application layer
127
+ * Effect.runPromise(Effect.provide(myProgram, AppLive));
128
+ * ```
129
+ *
130
+ * For testing:
131
+ *
132
+ * ```typescript
133
+ * import { createTestAppLayer, createDefaultConfig } from './layers.js';
134
+ *
135
+ * const testConfig = createDefaultConfig({
136
+ * redis: { host: 'localhost', port: 6380 }
137
+ * });
138
+ *
139
+ * const testLayer = createTestAppLayer(testConfig);
140
+ * Effect.runPromise(Effect.provide(myProgram, testLayer));
141
+ * ```
142
+ */
@@ -0,0 +1,52 @@
1
+ /**
2
+ * PubSub monitoring service for the redistail CLI utility.
3
+ *
4
+ * This service provides Redis Pub/Sub channel monitoring capabilities with
5
+ * automatic reconnection, exponential backoff, and proper error handling.
6
+ * It integrates with the effect-redis RedisPubSub service and follows
7
+ * Effect-TS patterns for dependency injection and resource management.
8
+ */
9
+ import { Context, Effect, Layer, Stream } from 'effect';
10
+ import { RedisPubSub } from '../redis';
11
+ import { type PubSubMessage, type MonitoringError, type ConfigError } from './types';
12
+ import { CLIConfigService } from './config-service';
13
+ type RedisPubSubService = Context.Tag.Service<RedisPubSub>;
14
+ /**
15
+ * Service interface for PubSub monitoring operations
16
+ */
17
+ export interface PubSubMonitorService {
18
+ /**
19
+ * Monitor a Redis Pub/Sub channel with automatic reconnection
20
+ * @param channel - The channel name to monitor
21
+ * @returns Stream of PubSub messages with error handling and reconnection
22
+ */
23
+ readonly monitor: (channel: string) => Stream.Stream<PubSubMessage, MonitoringError>;
24
+ /**
25
+ * Subscribe to a Redis Pub/Sub channel
26
+ * @param channel - The channel name to subscribe to
27
+ * @returns Effect that yields a stream of PubSub messages
28
+ */
29
+ readonly subscribe: (channel: string) => Effect.Effect<Stream.Stream<PubSubMessage, MonitoringError>, MonitoringError>;
30
+ }
31
+ /**
32
+ * Context tag for PubSub monitoring service dependency injection
33
+ */
34
+ export declare const PubSubMonitorService: Context.Tag<PubSubMonitorService, PubSubMonitorService>;
35
+ /**
36
+ * Live layer for PubSub monitoring service
37
+ * Depends on RedisPubSub and CLIConfigService services
38
+ */
39
+ export declare const PubSubMonitorServiceLive: Layer.Layer<PubSubMonitorService, ConfigError, RedisPubSub | CLIConfigService>;
40
+ /**
41
+ * Helper function to create a monitoring stream with custom retry configuration
42
+ */
43
+ export declare const createMonitoringStreamWithRetry: (channel: string, redisPubSubService: RedisPubSubService, maxRetries: number, baseDelaySeconds?: number) => Stream.Stream<PubSubMessage, MonitoringError>;
44
+ /**
45
+ * Helper function to validate channel names
46
+ */
47
+ export declare const validateChannelName: (channel: string) => Effect.Effect<string, MonitoringError>;
48
+ /**
49
+ * Helper function to create a monitored channel with validation
50
+ */
51
+ export declare const monitorChannelWithValidation: (channel: string, pubSubService: PubSubMonitorService) => Stream.Stream<PubSubMessage, MonitoringError>;
52
+ export {};
@@ -0,0 +1,65 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Cross-platform launcher for redistail CLI
5
+ * Detects platform and runs the appropriate binary
6
+ */
7
+
8
+ import { spawn } from 'node:child_process';
9
+ import { fileURLToPath } from 'node:url';
10
+ import { dirname, join } from 'node:path';
11
+ import { existsSync } from 'node:fs';
12
+
13
+ const __filename = fileURLToPath(import.meta.url);
14
+ const __dirname = dirname(__filename);
15
+
16
+ function launchRedistail() {
17
+ const args = process.argv.slice(2);
18
+
19
+ // Try to use the compiled executable first (better performance)
20
+ const exePath = join(__dirname, 'redistail.exe');
21
+ const jsPath = join(__dirname, 'redistail.js');
22
+
23
+ let command;
24
+ let commandArgs;
25
+
26
+ if (process.platform === 'win32' && existsSync(exePath)) {
27
+ // Use the compiled executable on Windows
28
+ command = exePath;
29
+ commandArgs = args;
30
+ } else if (existsSync(jsPath)) {
31
+ // Fall back to Node.js execution
32
+ command = 'node';
33
+ commandArgs = [jsPath, ...args];
34
+ } else {
35
+ console.error('Error: redistail binary not found');
36
+ process.exit(1);
37
+ }
38
+
39
+ // Spawn the process and pipe stdio
40
+ const child = spawn(command, commandArgs, {
41
+ stdio: 'inherit',
42
+ windowsHide: false,
43
+ });
44
+
45
+ // Handle process exit
46
+ child.on('close', (code) => {
47
+ process.exit(code || 0);
48
+ });
49
+
50
+ child.on('error', (error) => {
51
+ console.error('Error launching redistail:', error.message);
52
+ process.exit(1);
53
+ });
54
+
55
+ // Handle process signals
56
+ process.on('SIGINT', () => {
57
+ child.kill('SIGINT');
58
+ });
59
+
60
+ process.on('SIGTERM', () => {
61
+ child.kill('SIGTERM');
62
+ });
63
+ }
64
+
65
+ launchRedistail();
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Main CLI program for redistail - Redis message monitoring utility.
3
+ *
4
+ * This module implements the main program effect that combines all services
5
+ * to create a complete Redis monitoring application. It handles CLI argument
6
+ * parsing, service initialization, signal handling, and message streaming.
7
+ *
8
+ * Requirements addressed:
9
+ * - 1.5: Help and version flag support
10
+ * - 2.5: Graceful shutdown for PubSub monitoring (Ctrl+C handling)
11
+ * - 3.5: Graceful shutdown for Stream monitoring (Ctrl+C handling)
12
+ * - 6.3: Topic validation and error handling
13
+ * - 6.4: Redis error recovery and continued monitoring
14
+ */
15
+ import { Effect } from 'effect';
16
+ import { CLIConfigService } from './config-service.js';
17
+ import { type DisplayService } from './display-service.js';
18
+ import { PubSubMonitorService } from './pubsub-monitor-service.js';
19
+ import { SignalHandlerService } from './signal-handler-service.js';
20
+ import { StreamMonitorService } from './stream-monitor-service.js';
21
+ import type { RedistailError } from './types.js';
22
+ /**
23
+ * Main redistail program effect that combines all services
24
+ *
25
+ * This effect:
26
+ * 1. Parses CLI arguments and loads configuration
27
+ * 2. Handles help and version flags
28
+ * 3. Sets up signal handlers for graceful shutdown
29
+ * 4. Starts appropriate monitoring based on connection type
30
+ * 5. Streams and displays messages until interrupted
31
+ *
32
+ * Requirements 1.5, 2.5, 3.5, 6.3, 6.4
33
+ */
34
+ export declare const redistailProgram: (args: string[]) => Effect.Effect<void, RedistailError, CLIConfigService | SignalHandlerService | DisplayService | PubSubMonitorService | StreamMonitorService>;
35
+ /**
36
+ * CLI main function with comprehensive error handling and optimized layer selection
37
+ *
38
+ * This function provides the main entry point for the CLI binary with
39
+ * proper error handling, logging, and process management. It optimizes
40
+ * Redis connections by only loading the services needed for the specific mode:
41
+ *
42
+ * - PubSub mode: Only creates 2 Redis connections (publish + subscribe)
43
+ * - Stream mode: Only creates 2 Redis connections (producer + consumer)
44
+ * - Help/version: Uses minimal layer without Redis connections
45
+ * - Invalid args: Shows error without creating any connections
46
+ *
47
+ * This reduces Redis connections from 5 to just 2 per run, or 0 for help/errors.
48
+ */
49
+ export declare const cliMain: (args: string[]) => Effect.Effect<void, never, never>;
50
+ /**
51
+ * Re-export commonly used types and services for convenience
52
+ */
53
+ export { AppLive, CLIConfigService, DisplayServiceTag, PubSubAppLive, PubSubMonitorService, SignalHandlerService, StreamAppLive, StreamMonitorService, } from './layers.js';
54
+ export type { CLIConfig, RedistailConfig, RedistailError } from './types.js';
Binary file