alepha 0.14.0 → 0.14.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.
Files changed (94) hide show
  1. package/dist/api/audits/index.d.ts +417 -338
  2. package/dist/api/audits/index.d.ts.map +1 -1
  3. package/dist/api/files/index.d.ts +80 -1
  4. package/dist/api/files/index.d.ts.map +1 -1
  5. package/dist/api/jobs/index.d.ts +236 -157
  6. package/dist/api/jobs/index.d.ts.map +1 -1
  7. package/dist/api/notifications/index.d.ts +21 -1
  8. package/dist/api/notifications/index.d.ts.map +1 -1
  9. package/dist/api/parameters/index.d.ts +451 -4
  10. package/dist/api/parameters/index.d.ts.map +1 -1
  11. package/dist/api/users/index.d.ts +833 -830
  12. package/dist/api/users/index.d.ts.map +1 -1
  13. package/dist/cli/index.d.ts +212 -29
  14. package/dist/cli/index.d.ts.map +1 -1
  15. package/dist/cli/index.js +320 -219
  16. package/dist/cli/index.js.map +1 -1
  17. package/dist/command/index.d.ts +206 -9
  18. package/dist/command/index.d.ts.map +1 -1
  19. package/dist/command/index.js +306 -69
  20. package/dist/command/index.js.map +1 -1
  21. package/dist/core/index.browser.js.map +1 -1
  22. package/dist/core/index.d.ts +1 -1
  23. package/dist/core/index.js.map +1 -1
  24. package/dist/core/index.native.js.map +1 -1
  25. package/dist/file/index.d.ts.map +1 -1
  26. package/dist/file/index.js.map +1 -1
  27. package/dist/orm/index.d.ts +180 -126
  28. package/dist/orm/index.d.ts.map +1 -1
  29. package/dist/orm/index.js +486 -512
  30. package/dist/orm/index.js.map +1 -1
  31. package/dist/queue/redis/index.js +2 -4
  32. package/dist/queue/redis/index.js.map +1 -1
  33. package/dist/redis/index.d.ts +400 -29
  34. package/dist/redis/index.d.ts.map +1 -1
  35. package/dist/redis/index.js +412 -21
  36. package/dist/redis/index.js.map +1 -1
  37. package/dist/scheduler/index.d.ts +6 -6
  38. package/dist/security/index.d.ts +28 -28
  39. package/dist/security/index.d.ts.map +1 -1
  40. package/dist/server/auth/index.d.ts +155 -155
  41. package/dist/server/core/index.d.ts +0 -1
  42. package/dist/server/core/index.d.ts.map +1 -1
  43. package/dist/server/core/index.js.map +1 -1
  44. package/dist/server/health/index.d.ts +17 -17
  45. package/dist/server/helmet/index.d.ts +4 -1
  46. package/dist/server/helmet/index.d.ts.map +1 -1
  47. package/dist/server/multipart/index.d.ts.map +1 -1
  48. package/dist/server/multipart/index.js.map +1 -1
  49. package/dist/server/proxy/index.js.map +1 -1
  50. package/dist/topic/redis/index.js +3 -3
  51. package/dist/topic/redis/index.js.map +1 -1
  52. package/dist/vite/index.js +9 -6
  53. package/dist/vite/index.js.map +1 -1
  54. package/package.json +3 -3
  55. package/src/cli/apps/AlephaCli.ts +8 -3
  56. package/src/cli/apps/AlephaPackageBuilderCli.ts +3 -0
  57. package/src/cli/atoms/changelogOptions.ts +45 -0
  58. package/src/cli/commands/ChangelogCommands.ts +187 -317
  59. package/src/cli/commands/DeployCommands.ts +118 -0
  60. package/src/cli/commands/DrizzleCommands.ts +28 -8
  61. package/src/cli/commands/ViteCommands.ts +23 -9
  62. package/src/cli/defineConfig.ts +15 -0
  63. package/src/cli/index.ts +3 -0
  64. package/src/cli/services/AlephaCliUtils.ts +4 -21
  65. package/src/cli/services/GitMessageParser.ts +77 -0
  66. package/src/command/helpers/EnvUtils.ts +37 -0
  67. package/src/command/index.ts +3 -1
  68. package/src/command/primitives/$command.ts +172 -6
  69. package/src/command/providers/CliProvider.ts +424 -91
  70. package/src/core/Alepha.ts +1 -1
  71. package/src/file/providers/NodeFileSystemProvider.ts +3 -1
  72. package/src/orm/index.ts +8 -4
  73. package/src/orm/interfaces/PgQueryWhere.ts +1 -26
  74. package/src/orm/providers/drivers/BunPostgresProvider.ts +225 -0
  75. package/src/orm/providers/drivers/BunSqliteProvider.ts +180 -0
  76. package/src/orm/providers/drivers/DatabaseProvider.ts +25 -0
  77. package/src/orm/providers/drivers/NodePostgresProvider.ts +0 -25
  78. package/src/orm/services/QueryManager.ts +10 -125
  79. package/src/queue/redis/providers/RedisQueueProvider.ts +2 -7
  80. package/src/redis/index.ts +65 -3
  81. package/src/redis/providers/BunRedisProvider.ts +304 -0
  82. package/src/redis/providers/BunRedisSubscriberProvider.ts +94 -0
  83. package/src/redis/providers/NodeRedisProvider.ts +280 -0
  84. package/src/redis/providers/NodeRedisSubscriberProvider.ts +94 -0
  85. package/src/redis/providers/RedisProvider.ts +134 -140
  86. package/src/redis/providers/RedisSubscriberProvider.ts +58 -49
  87. package/src/server/core/providers/BunHttpServerProvider.ts +0 -3
  88. package/src/server/core/providers/ServerBodyParserProvider.ts +3 -1
  89. package/src/server/core/providers/ServerProvider.ts +7 -4
  90. package/src/server/multipart/providers/ServerMultipartProvider.ts +3 -1
  91. package/src/server/proxy/providers/ServerProxyProvider.ts +1 -1
  92. package/src/topic/redis/providers/RedisTopicProvider.ts +3 -3
  93. package/src/vite/tasks/buildServer.ts +1 -0
  94. package/src/orm/services/PgJsonQueryManager.ts +0 -511
@@ -0,0 +1,280 @@
1
+ import {
2
+ createClient,
3
+ RESP_TYPES,
4
+ type RedisClientType,
5
+ type SetOptions,
6
+ } from "@redis/client";
7
+ import { $env, $hook, $inject, Alepha, type Static, t } from "alepha";
8
+ import { $logger } from "alepha/logger";
9
+ import { RedisProvider, type RedisSetOptions } from "./RedisProvider.ts";
10
+
11
+ const envSchema = t.object({
12
+ REDIS_URL: t.optional(t.text()),
13
+ REDIS_PORT: t.integer({
14
+ default: "6379",
15
+ }),
16
+ REDIS_HOST: t.text({
17
+ default: "localhost",
18
+ }),
19
+ REDIS_PASSWORD: t.optional(t.text()),
20
+ });
21
+
22
+ declare module "alepha" {
23
+ interface Env extends Partial<Static<typeof envSchema>> {}
24
+ }
25
+
26
+ export type NodeRedisClient = RedisClientType<
27
+ {},
28
+ {},
29
+ {},
30
+ 3,
31
+ { 36: BufferConstructor }
32
+ >;
33
+ export type NodeRedisClientOptions = Parameters<typeof createClient>[0];
34
+
35
+ /**
36
+ * Node.js Redis client provider using `@redis/client`.
37
+ *
38
+ * This provider uses the official Redis client for Node.js runtime.
39
+ *
40
+ * @example
41
+ * ```ts
42
+ * // Set REDIS_URL environment variable
43
+ * // REDIS_URL=redis://localhost:6379
44
+ *
45
+ * // Or configure via REDIS_HOST, REDIS_PORT, REDIS_PASSWORD
46
+ *
47
+ * // Or configure programmatically
48
+ * alepha.with({
49
+ * provide: RedisProvider,
50
+ * use: NodeRedisProvider,
51
+ * });
52
+ * ```
53
+ */
54
+ export class NodeRedisProvider extends RedisProvider {
55
+ protected readonly log = $logger();
56
+ protected readonly alepha = $inject(Alepha);
57
+ protected readonly env = $env(envSchema);
58
+ protected readonly client = this.createClient();
59
+
60
+ public get publisher(): NodeRedisClient {
61
+ if (!this.client.isReady) {
62
+ throw new Error("Redis client is not ready");
63
+ }
64
+
65
+ return this.client;
66
+ }
67
+
68
+ public override get isReady(): boolean {
69
+ return this.client.isReady;
70
+ }
71
+
72
+ protected readonly start = $hook({
73
+ on: "start",
74
+ handler: () => this.connect(),
75
+ });
76
+
77
+ protected readonly stop = $hook({
78
+ on: "stop",
79
+ handler: () => this.close(),
80
+ });
81
+
82
+ /**
83
+ * Connect to the Redis server.
84
+ */
85
+ public override async connect(): Promise<void> {
86
+ this.log.debug("Connecting...");
87
+ await this.client.connect();
88
+ this.log.info("Connection OK");
89
+ }
90
+
91
+ /**
92
+ * Close the connection to the Redis server.
93
+ */
94
+ public override async close(): Promise<void> {
95
+ this.log.debug("Closing connection...");
96
+ await this.client.close();
97
+ this.log.info("Connection closed");
98
+ }
99
+
100
+ public duplicate(options?: Partial<NodeRedisClientOptions>): NodeRedisClient {
101
+ return this.client
102
+ .duplicate({
103
+ ...options,
104
+ RESP: 3,
105
+ })
106
+ .withTypeMapping({
107
+ [RESP_TYPES.BLOB_STRING]: Buffer,
108
+ });
109
+ }
110
+
111
+ public override async get(key: string): Promise<Buffer | undefined> {
112
+ this.log.trace(`Getting key ${key}`);
113
+ const resp = await this.publisher.get(key);
114
+
115
+ if (resp === null) {
116
+ return undefined;
117
+ }
118
+
119
+ return Buffer.from(resp);
120
+ }
121
+
122
+ public override async set(
123
+ key: string,
124
+ value: Buffer | string,
125
+ options?: RedisSetOptions,
126
+ ): Promise<Buffer> {
127
+ const buf = Buffer.isBuffer(value) ? value : Buffer.from(value, "utf-8");
128
+
129
+ // Convert RedisSetOptions to @redis/client SetOptions
130
+ const setOptions: SetOptions = {};
131
+
132
+ // Handle expiration object format (from alepha/cache-redis, alepha/lock-redis)
133
+ if (options?.expiration) {
134
+ if (options.expiration.type === "KEEPTTL") {
135
+ setOptions.KEEPTTL = true;
136
+ } else {
137
+ setOptions[options.expiration.type] = options.expiration.value;
138
+ }
139
+ }
140
+
141
+ // Handle direct expiration properties
142
+ if (options?.EX !== undefined) {
143
+ setOptions.EX = options.EX;
144
+ }
145
+ if (options?.PX !== undefined) {
146
+ setOptions.PX = options.PX;
147
+ }
148
+ if (options?.EXAT !== undefined) {
149
+ setOptions.EXAT = options.EXAT;
150
+ }
151
+ if (options?.PXAT !== undefined) {
152
+ setOptions.PXAT = options.PXAT;
153
+ }
154
+ if (options?.KEEPTTL) {
155
+ setOptions.KEEPTTL = true;
156
+ }
157
+
158
+ // Handle condition object format
159
+ if (options?.condition === "NX") {
160
+ setOptions.NX = true;
161
+ } else if (options?.condition === "XX") {
162
+ setOptions.XX = true;
163
+ }
164
+
165
+ // Handle direct condition properties
166
+ if (options?.NX) {
167
+ setOptions.NX = true;
168
+ }
169
+ if (options?.XX) {
170
+ setOptions.XX = true;
171
+ }
172
+ if (options?.GET) {
173
+ setOptions.GET = true;
174
+ }
175
+
176
+ const resp = await this.publisher.set(
177
+ key,
178
+ buf,
179
+ Object.keys(setOptions).length > 0 ? setOptions : undefined,
180
+ );
181
+
182
+ if (resp === "OK" || !resp) {
183
+ return buf;
184
+ }
185
+
186
+ return Buffer.from(resp);
187
+ }
188
+
189
+ public override async has(key: string): Promise<boolean> {
190
+ const resp = await this.publisher.exists(key);
191
+ return resp > 0;
192
+ }
193
+
194
+ public override async keys(pattern: string): Promise<string[]> {
195
+ const keys = await this.publisher.keys(pattern);
196
+ return keys.map((key) => key.toString());
197
+ }
198
+
199
+ public override async del(keys: string[]): Promise<void> {
200
+ if (keys.length === 0) {
201
+ return;
202
+ }
203
+
204
+ await this.publisher.del(keys);
205
+ }
206
+
207
+ // ---------------------------------------------------------
208
+ // Queue operations
209
+ // ---------------------------------------------------------
210
+
211
+ public override async lpush(key: string, value: string): Promise<void> {
212
+ await this.publisher.LPUSH(key, value);
213
+ }
214
+
215
+ public override async rpop(key: string): Promise<string | undefined> {
216
+ const value = await this.publisher.RPOP(key);
217
+ if (value == null) {
218
+ return undefined;
219
+ }
220
+ return String(value);
221
+ }
222
+
223
+ // ---------------------------------------------------------
224
+ // Pub/Sub operations
225
+ // ---------------------------------------------------------
226
+
227
+ public override async publish(
228
+ channel: string,
229
+ message: string,
230
+ ): Promise<void> {
231
+ await this.publisher.publish(channel, message);
232
+ }
233
+
234
+ /**
235
+ * Get the Redis connection URL.
236
+ */
237
+ protected getUrl(): string {
238
+ // Prefer REDIS_URL if set
239
+ if (this.env.REDIS_URL) {
240
+ return this.env.REDIS_URL;
241
+ }
242
+
243
+ // Build URL from components
244
+ const url = new URL("redis://127.0.0.1:6379");
245
+
246
+ if (this.env.REDIS_PASSWORD) {
247
+ url.password = this.env.REDIS_PASSWORD;
248
+ }
249
+
250
+ if (this.env.REDIS_HOST) {
251
+ url.hostname = this.env.REDIS_HOST;
252
+ }
253
+
254
+ if (this.env.REDIS_PORT) {
255
+ url.port = String(this.env.REDIS_PORT);
256
+ }
257
+
258
+ return url.toString();
259
+ }
260
+
261
+ /**
262
+ * Redis client factory method.
263
+ */
264
+ protected createClient(): NodeRedisClient {
265
+ const client = createClient({
266
+ url: this.getUrl(),
267
+ RESP: 3,
268
+ }).withTypeMapping({
269
+ [RESP_TYPES.BLOB_STRING]: Buffer,
270
+ });
271
+
272
+ client.on("error", (error) => {
273
+ if (this.alepha.isStarted()) {
274
+ this.log.error(error);
275
+ }
276
+ });
277
+
278
+ return client;
279
+ }
280
+ }
@@ -0,0 +1,94 @@
1
+ import { $hook, $inject, Alepha } from "alepha";
2
+ import { $logger } from "alepha/logger";
3
+ import {
4
+ type NodeRedisClient,
5
+ NodeRedisProvider,
6
+ } from "./NodeRedisProvider.ts";
7
+ import {
8
+ RedisSubscriberProvider,
9
+ type SubscribeCallback,
10
+ } from "./RedisSubscriberProvider.ts";
11
+
12
+ /**
13
+ * Node.js Redis subscriber provider using `@redis/client`.
14
+ *
15
+ * This provider creates a dedicated Redis connection for subscriptions,
16
+ * as Redis requires separate connections for pub/sub operations.
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * const subscriber = alepha.inject(RedisSubscriberProvider);
21
+ * await subscriber.subscribe("channel", (message, channel) => {
22
+ * console.log(`Received: ${message} on ${channel}`);
23
+ * });
24
+ * ```
25
+ */
26
+ export class NodeRedisSubscriberProvider extends RedisSubscriberProvider {
27
+ protected readonly log = $logger();
28
+ protected readonly alepha = $inject(Alepha);
29
+ protected readonly redisProvider = $inject(NodeRedisProvider);
30
+ protected readonly client: NodeRedisClient = this.createClient();
31
+
32
+ public get subscriber(): NodeRedisClient {
33
+ if (!this.client.isReady) {
34
+ throw new Error("Redis subscriber client is not ready");
35
+ }
36
+
37
+ return this.client;
38
+ }
39
+
40
+ public override get isReady(): boolean {
41
+ return this.client.isReady;
42
+ }
43
+
44
+ protected readonly start = $hook({
45
+ on: "start",
46
+ handler: () => this.connect(),
47
+ });
48
+
49
+ protected readonly stop = $hook({
50
+ on: "stop",
51
+ handler: () => this.close(),
52
+ });
53
+
54
+ public override async connect(): Promise<void> {
55
+ this.log.debug("Connecting subscriber...");
56
+ await this.client.connect();
57
+ this.log.info("Subscriber connection OK");
58
+ }
59
+
60
+ public override async close(): Promise<void> {
61
+ this.log.debug("Closing subscriber connection...");
62
+ await this.subscriber.close();
63
+ this.log.info("Subscriber connection closed");
64
+ }
65
+
66
+ public override async subscribe(
67
+ channel: string,
68
+ callback: SubscribeCallback,
69
+ ): Promise<void> {
70
+ await this.subscriber.subscribe(channel, callback);
71
+ }
72
+
73
+ public override async unsubscribe(
74
+ channel: string,
75
+ callback?: SubscribeCallback,
76
+ ): Promise<void> {
77
+ await this.subscriber.unsubscribe(channel, callback);
78
+ }
79
+
80
+ /**
81
+ * Redis subscriber client factory method.
82
+ */
83
+ protected createClient(): NodeRedisClient {
84
+ const client = this.redisProvider.duplicate();
85
+
86
+ client.on("error", (error) => {
87
+ if (this.alepha.isStarted()) {
88
+ this.log.error(error);
89
+ }
90
+ });
91
+
92
+ return client;
93
+ }
94
+ }
@@ -1,167 +1,161 @@
1
- import {
2
- createClient,
3
- RESP_TYPES,
4
- type RedisClientType,
5
- type SetOptions,
6
- } from "@redis/client";
7
- import { $env, $hook, $inject, Alepha, type Static, t } from "alepha";
8
- import { $logger } from "alepha/logger";
9
-
10
- const envSchema = t.object({
11
- REDIS_PORT: t.integer({
12
- default: "6379",
13
- }),
14
- REDIS_HOST: t.text({
15
- default: "localhost",
16
- }),
17
- REDIS_PASSWORD: t.optional(t.text()),
18
- });
19
-
20
- declare module "alepha" {
21
- interface Env extends Partial<Static<typeof envSchema>> {}
22
- }
23
-
24
- export type RedisClient = RedisClientType<
25
- {},
26
- {},
27
- {},
28
- 3,
29
- { 36: BufferConstructor }
30
- >;
31
- export type RedisClientOptions = Parameters<typeof createClient>[0];
32
- export type RedisSetOptions = SetOptions;
33
-
34
1
  /**
35
- * Redis client provider.
2
+ * Abstract Redis provider interface.
3
+ *
4
+ * This abstract class defines the common interface for Redis operations.
5
+ * Implementations include:
6
+ * - {@link NodeRedisProvider} - Uses `@redis/client` for Node.js runtime
7
+ * - {@link BunRedisProvider} - Uses Bun's native `RedisClient` for Bun runtime
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * // Inject the abstract provider - runtime selects the implementation
12
+ * const redis = alepha.inject(RedisProvider);
13
+ *
14
+ * // Use common operations
15
+ * await redis.set("key", "value");
16
+ * const value = await redis.get("key");
17
+ * ```
36
18
  */
37
- export class RedisProvider {
38
- protected readonly log = $logger();
39
- protected readonly alepha = $inject(Alepha);
40
- protected readonly env = $env(envSchema);
41
- protected readonly client = this.createClient();
42
-
43
- public get publisher(): RedisClient {
44
- if (!this.client.isReady) {
45
- throw new Error("Redis client is not ready");
46
- }
47
-
48
- return this.client;
49
- }
50
-
51
- protected readonly start = $hook({
52
- on: "start",
53
- handler: () => this.connect(),
54
- });
55
-
56
- protected readonly stop = $hook({
57
- on: "stop",
58
- handler: () => this.close(),
59
- });
19
+ export abstract class RedisProvider {
20
+ /**
21
+ * Whether the Redis client is ready to accept commands.
22
+ */
23
+ public abstract readonly isReady: boolean;
60
24
 
61
25
  /**
62
26
  * Connect to the Redis server.
63
27
  */
64
- public async connect(): Promise<void> {
65
- this.log.debug("Connecting...");
66
- await this.client.connect();
67
- this.log.info("Connection OK");
68
- }
28
+ public abstract connect(): Promise<void>;
69
29
 
70
30
  /**
71
31
  * Close the connection to the Redis server.
72
32
  */
73
- public async close(): Promise<void> {
74
- this.log.debug("Closing connection...");
75
- await this.client.close();
76
- this.log.info("Connection closed");
77
- }
78
-
79
- public duplicate(options?: Partial<RedisClientOptions>): RedisClient {
80
- return this.client
81
- .duplicate({
82
- ...options,
83
- RESP: 3,
84
- })
85
- .withTypeMapping({
86
- [RESP_TYPES.BLOB_STRING]: Buffer,
87
- });
88
- }
89
-
90
- public async get(key: string): Promise<Buffer | undefined> {
91
- this.log.trace(`Getting key ${key}`);
92
- const resp = await this.publisher.get(key);
93
-
94
- if (resp === null) {
95
- return undefined;
96
- }
33
+ public abstract close(): Promise<void>;
97
34
 
98
- return Buffer.from(resp);
99
- }
35
+ /**
36
+ * Get the value of a key.
37
+ *
38
+ * @param key The key to get.
39
+ * @returns The value as a Buffer, or undefined if the key does not exist.
40
+ */
41
+ public abstract get(key: string): Promise<Buffer | undefined>;
100
42
 
101
- public async set(
43
+ /**
44
+ * Set the value of a key.
45
+ *
46
+ * @param key The key to set.
47
+ * @param value The value to set (Buffer or string).
48
+ * @param options Optional set options (EX, PX, NX, XX, etc.).
49
+ * @returns The value as a Buffer.
50
+ */
51
+ public abstract set(
102
52
  key: string,
103
53
  value: Buffer | string,
104
54
  options?: RedisSetOptions,
105
- ): Promise<Buffer> {
106
- const buf = Buffer.isBuffer(value) ? value : Buffer.from(value, "utf-8");
107
- const resp = await this.publisher.set(key, buf, options);
108
-
109
- if (resp === "OK" || !resp) {
110
- return buf;
111
- }
112
-
113
- return Buffer.from(resp);
114
- }
115
-
116
- public async has(key: string): Promise<boolean> {
117
- const resp = await this.publisher.exists(key);
118
- return resp > 0;
119
- }
55
+ ): Promise<Buffer>;
120
56
 
121
- public async keys(pattern: string): Promise<string[]> {
122
- const keys = await this.publisher.keys(pattern);
123
- return keys.map((key) => key.toString());
124
- }
125
-
126
- public async del(keys: string[]): Promise<void> {
127
- if (keys.length === 0) {
128
- return;
129
- }
57
+ /**
58
+ * Check if a key exists.
59
+ *
60
+ * @param key The key to check.
61
+ * @returns True if the key exists.
62
+ */
63
+ public abstract has(key: string): Promise<boolean>;
130
64
 
131
- await this.publisher.del(keys);
132
- }
65
+ /**
66
+ * Get all keys matching a pattern.
67
+ *
68
+ * @param pattern The glob-style pattern to match.
69
+ * @returns Array of matching key names.
70
+ */
71
+ public abstract keys(pattern: string): Promise<string[]>;
133
72
 
134
73
  /**
135
- * Redis subscriber client factory method.
74
+ * Delete one or more keys.
75
+ *
76
+ * @param keys The keys to delete.
136
77
  */
137
- protected createClient(): RedisClient {
138
- const url = new URL("redis://127.0.0.1:6379");
78
+ public abstract del(keys: string[]): Promise<void>;
139
79
 
140
- if (this.env.REDIS_PASSWORD) {
141
- url.password = this.env.REDIS_PASSWORD;
142
- }
80
+ // ---------------------------------------------------------
81
+ // Queue operations (for alepha/queue-redis)
82
+ // ---------------------------------------------------------
143
83
 
144
- if (this.env.REDIS_HOST) {
145
- url.hostname = this.env.REDIS_HOST;
146
- }
84
+ /**
85
+ * Push a value to the left (head) of a list.
86
+ *
87
+ * @param key The list key.
88
+ * @param value The value to push.
89
+ */
90
+ public abstract lpush(key: string, value: string): Promise<void>;
147
91
 
148
- if (this.env.REDIS_PORT) {
149
- url.port = String(this.env.REDIS_PORT);
150
- }
92
+ /**
93
+ * Pop a value from the right (tail) of a list.
94
+ *
95
+ * @param key The list key.
96
+ * @returns The value, or undefined if the list is empty.
97
+ */
98
+ public abstract rpop(key: string): Promise<string | undefined>;
151
99
 
152
- const client = createClient({
153
- url: url.toString(),
154
- RESP: 3,
155
- }).withTypeMapping({
156
- [RESP_TYPES.BLOB_STRING]: Buffer,
157
- });
100
+ // ---------------------------------------------------------
101
+ // Pub/Sub operations (for alepha/topic-redis)
102
+ // ---------------------------------------------------------
158
103
 
159
- client.on("error", (error) => {
160
- if (this.alepha.isStarted()) {
161
- this.log.error(error);
162
- }
163
- });
104
+ /**
105
+ * Publish a message to a channel.
106
+ *
107
+ * @param channel The channel name.
108
+ * @param message The message to publish.
109
+ */
110
+ public abstract publish(channel: string, message: string): Promise<void>;
111
+ }
164
112
 
165
- return client;
166
- }
113
+ /**
114
+ * Common Redis SET command options.
115
+ * Compatible with @redis/client SetOptions format.
116
+ */
117
+ export interface RedisSetOptions {
118
+ /**
119
+ * Set the specified expire time, in seconds.
120
+ */
121
+ EX?: number;
122
+ /**
123
+ * Set the specified expire time, in milliseconds.
124
+ */
125
+ PX?: number;
126
+ /**
127
+ * Set the specified Unix time at which the key will expire, in seconds.
128
+ */
129
+ EXAT?: number;
130
+ /**
131
+ * Set the specified Unix time at which the key will expire, in milliseconds.
132
+ */
133
+ PXAT?: number;
134
+ /**
135
+ * Only set the key if it does not already exist.
136
+ */
137
+ NX?: boolean;
138
+ /**
139
+ * Only set the key if it already exists.
140
+ */
141
+ XX?: boolean;
142
+ /**
143
+ * Retain the time to live associated with the key.
144
+ */
145
+ KEEPTTL?: boolean;
146
+ /**
147
+ * Return the old string stored at key, or nil if key did not exist.
148
+ */
149
+ GET?: boolean;
150
+ /**
151
+ * Alternative expiration format (compatible with @redis/client).
152
+ */
153
+ expiration?: {
154
+ type: "EX" | "PX" | "EXAT" | "PXAT" | "KEEPTTL";
155
+ value: number;
156
+ };
157
+ /**
158
+ * Alternative condition format (compatible with @redis/client).
159
+ */
160
+ condition?: "NX" | "XX";
167
161
  }