alepha 0.13.8 → 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.
- package/dist/api/audits/index.d.ts +418 -338
- package/dist/api/audits/index.d.ts.map +1 -0
- package/dist/api/files/index.d.ts +81 -1
- package/dist/api/files/index.d.ts.map +1 -0
- package/dist/api/jobs/index.d.ts +107 -27
- package/dist/api/jobs/index.d.ts.map +1 -0
- package/dist/api/notifications/index.d.ts +21 -1
- package/dist/api/notifications/index.d.ts.map +1 -0
- package/dist/api/parameters/index.d.ts +455 -8
- package/dist/api/parameters/index.d.ts.map +1 -0
- package/dist/api/users/index.d.ts +844 -840
- package/dist/api/users/index.d.ts.map +1 -0
- package/dist/api/verifications/index.d.ts.map +1 -0
- package/dist/batch/index.d.ts.map +1 -0
- package/dist/bucket/index.d.ts.map +1 -0
- package/dist/cache/core/index.d.ts.map +1 -0
- package/dist/cache/redis/index.d.ts.map +1 -0
- package/dist/cli/index.d.ts +254 -59
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +499 -127
- package/dist/cli/index.js.map +1 -1
- package/dist/command/index.d.ts +217 -10
- package/dist/command/index.d.ts.map +1 -0
- package/dist/command/index.js +350 -74
- package/dist/command/index.js.map +1 -1
- package/dist/core/index.browser.js +1334 -1318
- package/dist/core/index.browser.js.map +1 -1
- package/dist/core/index.d.ts +76 -72
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +1337 -1321
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.native.js +1337 -1321
- package/dist/core/index.native.js.map +1 -1
- package/dist/datetime/index.d.ts.map +1 -0
- package/dist/email/index.d.ts.map +1 -0
- package/dist/fake/index.d.ts.map +1 -0
- package/dist/file/index.d.ts.map +1 -0
- package/dist/file/index.js.map +1 -1
- package/dist/lock/core/index.d.ts.map +1 -0
- package/dist/lock/redis/index.d.ts.map +1 -0
- package/dist/logger/index.d.ts +1 -0
- package/dist/logger/index.d.ts.map +1 -0
- package/dist/mcp/index.d.ts +820 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +978 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/orm/index.d.ts +234 -107
- package/dist/orm/index.d.ts.map +1 -0
- package/dist/orm/index.js +376 -316
- package/dist/orm/index.js.map +1 -1
- package/dist/queue/core/index.d.ts +4 -4
- package/dist/queue/core/index.d.ts.map +1 -0
- package/dist/queue/redis/index.d.ts.map +1 -0
- package/dist/queue/redis/index.js +2 -4
- package/dist/queue/redis/index.js.map +1 -1
- package/dist/redis/index.d.ts +400 -29
- package/dist/redis/index.d.ts.map +1 -0
- package/dist/redis/index.js +412 -21
- package/dist/redis/index.js.map +1 -1
- package/dist/retry/index.d.ts.map +1 -0
- package/dist/router/index.d.ts.map +1 -0
- package/dist/scheduler/index.d.ts +6 -6
- package/dist/scheduler/index.d.ts.map +1 -0
- package/dist/security/index.d.ts +28 -28
- package/dist/security/index.d.ts.map +1 -0
- package/dist/server/auth/index.d.ts +155 -155
- package/dist/server/auth/index.d.ts.map +1 -0
- package/dist/server/cache/index.d.ts.map +1 -0
- package/dist/server/compress/index.d.ts.map +1 -0
- package/dist/server/cookies/index.d.ts.map +1 -0
- package/dist/server/core/index.d.ts +0 -1
- package/dist/server/core/index.d.ts.map +1 -0
- package/dist/server/core/index.js.map +1 -1
- package/dist/server/cors/index.d.ts.map +1 -0
- package/dist/server/health/index.d.ts +17 -17
- package/dist/server/health/index.d.ts.map +1 -0
- package/dist/server/helmet/index.d.ts +4 -1
- package/dist/server/helmet/index.d.ts.map +1 -0
- package/dist/server/links/index.d.ts +33 -33
- package/dist/server/links/index.d.ts.map +1 -0
- package/dist/server/metrics/index.d.ts.map +1 -0
- package/dist/server/multipart/index.d.ts.map +1 -0
- package/dist/server/multipart/index.js.map +1 -1
- package/dist/server/proxy/index.d.ts.map +1 -0
- package/dist/server/proxy/index.js.map +1 -1
- package/dist/server/rate-limit/index.d.ts.map +1 -0
- package/dist/server/security/index.d.ts +9 -9
- package/dist/server/security/index.d.ts.map +1 -0
- package/dist/server/static/index.d.ts.map +1 -0
- package/dist/server/swagger/index.d.ts.map +1 -0
- package/dist/sms/index.d.ts.map +1 -0
- package/dist/thread/index.d.ts.map +1 -0
- package/dist/topic/core/index.d.ts.map +1 -0
- package/dist/topic/redis/index.d.ts.map +1 -0
- package/dist/topic/redis/index.js +3 -3
- package/dist/topic/redis/index.js.map +1 -1
- package/dist/vite/index.d.ts +10 -2
- package/dist/vite/index.d.ts.map +1 -0
- package/dist/vite/index.js +45 -20
- package/dist/vite/index.js.map +1 -1
- package/dist/websocket/index.d.ts.map +1 -0
- package/package.json +9 -4
- package/src/cli/apps/AlephaCli.ts +10 -3
- package/src/cli/apps/AlephaPackageBuilderCli.ts +15 -8
- package/src/cli/assets/mainTs.ts +9 -10
- package/src/cli/atoms/changelogOptions.ts +45 -0
- package/src/cli/commands/ChangelogCommands.ts +259 -0
- package/src/cli/commands/DeployCommands.ts +118 -0
- package/src/cli/commands/DrizzleCommands.ts +230 -10
- package/src/cli/commands/ViteCommands.ts +47 -23
- package/src/cli/defineConfig.ts +15 -0
- package/src/cli/index.ts +3 -0
- package/src/cli/services/AlephaCliUtils.ts +10 -154
- package/src/cli/services/GitMessageParser.ts +77 -0
- package/src/command/helpers/EnvUtils.ts +37 -0
- package/src/command/index.ts +3 -1
- package/src/command/primitives/$command.ts +172 -6
- package/src/command/providers/CliProvider.ts +499 -95
- package/src/core/Alepha.ts +1 -1
- package/src/core/providers/SchemaValidator.ts +23 -1
- package/src/file/providers/NodeFileSystemProvider.ts +3 -1
- package/src/mcp/errors/McpError.ts +72 -0
- package/src/mcp/helpers/jsonrpc.ts +163 -0
- package/src/mcp/index.ts +132 -0
- package/src/mcp/interfaces/McpTypes.ts +248 -0
- package/src/mcp/primitives/$prompt.ts +188 -0
- package/src/mcp/primitives/$resource.ts +171 -0
- package/src/mcp/primitives/$tool.ts +285 -0
- package/src/mcp/providers/McpServerProvider.ts +382 -0
- package/src/mcp/transports/SseMcpTransport.ts +172 -0
- package/src/mcp/transports/StdioMcpTransport.ts +126 -0
- package/src/orm/index.ts +20 -4
- package/src/orm/interfaces/PgQueryWhere.ts +1 -26
- package/src/orm/providers/drivers/BunPostgresProvider.ts +225 -0
- package/src/orm/providers/drivers/BunSqliteProvider.ts +180 -0
- package/src/orm/providers/drivers/CloudflareD1Provider.ts +164 -0
- package/src/orm/providers/drivers/DatabaseProvider.ts +25 -0
- package/src/orm/providers/drivers/NodePostgresProvider.ts +0 -25
- package/src/orm/providers/drivers/NodeSqliteProvider.ts +3 -1
- package/src/orm/services/QueryManager.ts +10 -125
- package/src/queue/redis/providers/RedisQueueProvider.ts +2 -7
- package/src/redis/index.ts +65 -3
- package/src/redis/providers/BunRedisProvider.ts +304 -0
- package/src/redis/providers/BunRedisSubscriberProvider.ts +94 -0
- package/src/redis/providers/NodeRedisProvider.ts +280 -0
- package/src/redis/providers/NodeRedisSubscriberProvider.ts +94 -0
- package/src/redis/providers/RedisProvider.ts +134 -140
- package/src/redis/providers/RedisSubscriberProvider.ts +58 -49
- package/src/server/core/providers/BunHttpServerProvider.ts +0 -3
- package/src/server/core/providers/ServerBodyParserProvider.ts +3 -1
- package/src/server/core/providers/ServerProvider.ts +7 -4
- package/src/server/multipart/providers/ServerMultipartProvider.ts +3 -1
- package/src/server/proxy/providers/ServerProxyProvider.ts +1 -1
- package/src/topic/redis/providers/RedisTopicProvider.ts +3 -3
- package/src/vite/plugins/viteAlephaBuild.ts +8 -2
- package/src/vite/plugins/viteAlephaDev.ts +6 -2
- package/src/vite/tasks/buildServer.ts +2 -1
- package/src/vite/tasks/generateCloudflare.ts +43 -15
- package/src/vite/tasks/runAlepha.ts +1 -0
- package/src/orm/services/PgJsonQueryManager.ts +0 -511
|
@@ -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
|
|
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
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
74
|
+
* Delete one or more keys.
|
|
75
|
+
*
|
|
76
|
+
* @param keys The keys to delete.
|
|
136
77
|
*/
|
|
137
|
-
|
|
138
|
-
const url = new URL("redis://127.0.0.1:6379");
|
|
78
|
+
public abstract del(keys: string[]): Promise<void>;
|
|
139
79
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
80
|
+
// ---------------------------------------------------------
|
|
81
|
+
// Queue operations (for alepha/queue-redis)
|
|
82
|
+
// ---------------------------------------------------------
|
|
143
83
|
|
|
144
|
-
|
|
145
|
-
|
|
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
|
-
|
|
149
|
-
|
|
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
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
}).withTypeMapping({
|
|
156
|
-
[RESP_TYPES.BLOB_STRING]: Buffer,
|
|
157
|
-
});
|
|
100
|
+
// ---------------------------------------------------------
|
|
101
|
+
// Pub/Sub operations (for alepha/topic-redis)
|
|
102
|
+
// ---------------------------------------------------------
|
|
158
103
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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
|
-
|
|
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
|
}
|
|
@@ -1,56 +1,65 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
public async connect(): Promise<void> {
|
|
31
|
-
this.log.debug("Connecting...");
|
|
32
|
-
await this.client.connect();
|
|
33
|
-
this.log.info("Connection OK");
|
|
34
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Abstract Redis subscriber provider interface.
|
|
3
|
+
*
|
|
4
|
+
* This abstract class defines the common interface for Redis pub/sub subscriptions.
|
|
5
|
+
* Implementations include:
|
|
6
|
+
* - {@link NodeRedisSubscriberProvider} - Uses `@redis/client` for Node.js runtime
|
|
7
|
+
* - {@link BunRedisSubscriberProvider} - Uses Bun's native `RedisClient` for Bun runtime
|
|
8
|
+
*
|
|
9
|
+
* Redis requires separate connections for pub/sub operations, so this provider
|
|
10
|
+
* creates a dedicated connection for subscriptions.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* // Inject the abstract provider - runtime selects the implementation
|
|
15
|
+
* const subscriber = alepha.inject(RedisSubscriberProvider);
|
|
16
|
+
*
|
|
17
|
+
* // Subscribe to a channel
|
|
18
|
+
* await subscriber.subscribe("my-channel", (message, channel) => {
|
|
19
|
+
* console.log(`Received: ${message} on ${channel}`);
|
|
20
|
+
* });
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export abstract class RedisSubscriberProvider {
|
|
24
|
+
/**
|
|
25
|
+
* Whether the Redis subscriber client is ready to accept commands.
|
|
26
|
+
*/
|
|
27
|
+
public abstract readonly isReady: boolean;
|
|
35
28
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
29
|
+
/**
|
|
30
|
+
* Connect to the Redis server for subscriptions.
|
|
31
|
+
*/
|
|
32
|
+
public abstract connect(): Promise<void>;
|
|
41
33
|
|
|
42
34
|
/**
|
|
43
|
-
*
|
|
35
|
+
* Close the subscriber connection.
|
|
44
36
|
*/
|
|
45
|
-
|
|
46
|
-
const client = this.redisProvider.duplicate();
|
|
37
|
+
public abstract close(): Promise<void>;
|
|
47
38
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
39
|
+
/**
|
|
40
|
+
* Subscribe to a channel.
|
|
41
|
+
*
|
|
42
|
+
* @param channel The channel name.
|
|
43
|
+
* @param callback The callback to invoke when a message is received.
|
|
44
|
+
*/
|
|
45
|
+
public abstract subscribe(
|
|
46
|
+
channel: string,
|
|
47
|
+
callback: SubscribeCallback,
|
|
48
|
+
): Promise<void>;
|
|
53
49
|
|
|
54
|
-
|
|
55
|
-
|
|
50
|
+
/**
|
|
51
|
+
* Unsubscribe from a channel.
|
|
52
|
+
*
|
|
53
|
+
* @param channel The channel name.
|
|
54
|
+
* @param callback Optional specific callback to remove.
|
|
55
|
+
*/
|
|
56
|
+
public abstract unsubscribe(
|
|
57
|
+
channel: string,
|
|
58
|
+
callback?: SubscribeCallback,
|
|
59
|
+
): Promise<void>;
|
|
56
60
|
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Callback for subscription messages.
|
|
64
|
+
*/
|
|
65
|
+
export type SubscribeCallback = (message: string, channel: string) => void;
|
|
@@ -21,9 +21,6 @@ declare module "alepha" {
|
|
|
21
21
|
interface Env extends Partial<Static<typeof envSchema>> {}
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
// Type declaration for Bun global (only when running in Bun runtime)
|
|
25
|
-
declare const Bun: any;
|
|
26
|
-
|
|
27
24
|
export class BunHttpServerProvider extends ServerProvider {
|
|
28
25
|
protected readonly alepha = $inject(Alepha);
|
|
29
26
|
protected readonly dateTimeProvider = $inject(DateTimeProvider);
|
|
@@ -36,7 +36,9 @@ export class ServerBodyParserProvider {
|
|
|
36
36
|
} else if (request.raw.node?.req) {
|
|
37
37
|
// convert Node.js IncomingMessage to Web ReadableStream
|
|
38
38
|
// TODO: check performance impact, it's better to directly read from Node stream!
|
|
39
|
-
stream = WebStream.from(
|
|
39
|
+
stream = WebStream.from(
|
|
40
|
+
request.raw.node.req,
|
|
41
|
+
) as unknown as ReadableStream;
|
|
40
42
|
}
|
|
41
43
|
|
|
42
44
|
if (!stream) {
|
|
@@ -212,10 +212,13 @@ export class ServerProvider {
|
|
|
212
212
|
|
|
213
213
|
// if response.body is node stream
|
|
214
214
|
if (response.body instanceof Readable) {
|
|
215
|
-
ev.res = new Response(
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
215
|
+
ev.res = new Response(
|
|
216
|
+
Readable.toWeb(response.body) as unknown as ReadableStream,
|
|
217
|
+
{
|
|
218
|
+
status: response.status,
|
|
219
|
+
headers: response.headers,
|
|
220
|
+
},
|
|
221
|
+
);
|
|
219
222
|
return;
|
|
220
223
|
}
|
|
221
224
|
|
|
@@ -59,7 +59,9 @@ export class ServerMultipartProvider {
|
|
|
59
59
|
webRequest = new Request(request.url, {
|
|
60
60
|
method: request.method,
|
|
61
61
|
headers: request.headers,
|
|
62
|
-
body: WebStream.from(
|
|
62
|
+
body: WebStream.from(
|
|
63
|
+
request.raw.node.req,
|
|
64
|
+
) as unknown as ReadableStream,
|
|
63
65
|
duplex: "half",
|
|
64
66
|
} as RequestInit & { duplex: "half" });
|
|
65
67
|
}
|
|
@@ -42,7 +42,7 @@ export class RedisTopicProvider extends TopicProvider {
|
|
|
42
42
|
* Publish a message to a topic.
|
|
43
43
|
*/
|
|
44
44
|
public async publish(topic: string, message: string): Promise<void> {
|
|
45
|
-
await this.redisProvider.
|
|
45
|
+
await this.redisProvider.publish(this.prefix(topic), message);
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
/**
|
|
@@ -53,7 +53,7 @@ export class RedisTopicProvider extends TopicProvider {
|
|
|
53
53
|
callback: SubscribeCallback,
|
|
54
54
|
): Promise<UnSubscribeFn> {
|
|
55
55
|
const topic = this.prefix(name);
|
|
56
|
-
await this.redisSubscriberProvider.
|
|
56
|
+
await this.redisSubscriberProvider.subscribe(topic, callback);
|
|
57
57
|
|
|
58
58
|
return () => this.unsubscribe(name, callback);
|
|
59
59
|
}
|
|
@@ -67,6 +67,6 @@ export class RedisTopicProvider extends TopicProvider {
|
|
|
67
67
|
): Promise<void> {
|
|
68
68
|
const topic = this.prefix(name);
|
|
69
69
|
|
|
70
|
-
await this.redisSubscriberProvider.
|
|
70
|
+
await this.redisSubscriberProvider.unsubscribe(topic, callback);
|
|
71
71
|
}
|
|
72
72
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { readFile, unlink, writeFile } from "node:fs/promises";
|
|
2
|
+
import { OPTIONS } from "alepha";
|
|
2
3
|
import type { Plugin, UserConfig } from "vite";
|
|
3
4
|
import { boot } from "../helpers/boot.ts";
|
|
4
5
|
import { fileExists } from "../helpers/fileExists.ts";
|
|
@@ -14,6 +15,7 @@ import {
|
|
|
14
15
|
generateVercel,
|
|
15
16
|
prerenderPages,
|
|
16
17
|
type VercelConfig,
|
|
18
|
+
type WranglerConfig,
|
|
17
19
|
} from "../tasks/index.ts";
|
|
18
20
|
|
|
19
21
|
export interface ViteAlephaBuildOptions {
|
|
@@ -43,7 +45,7 @@ export interface ViteAlephaBuildOptions {
|
|
|
43
45
|
*
|
|
44
46
|
* @default false
|
|
45
47
|
*/
|
|
46
|
-
cloudflare?: boolean;
|
|
48
|
+
cloudflare?: boolean | WranglerConfig;
|
|
47
49
|
|
|
48
50
|
/**
|
|
49
51
|
* If true, the build will be optimized for Docker deployment.
|
|
@@ -89,8 +91,9 @@ export async function viteAlephaBuild(
|
|
|
89
91
|
let rootConfig: UserConfig = {};
|
|
90
92
|
|
|
91
93
|
return {
|
|
92
|
-
name: "alepha
|
|
94
|
+
name: "alepha:build",
|
|
93
95
|
apply: "build",
|
|
96
|
+
[OPTIONS as any]: options,
|
|
94
97
|
config(config, ctx) {
|
|
95
98
|
const buildMode = process.env.ALEPHA_BUILD_MODE as
|
|
96
99
|
| AlephaBuildMode
|
|
@@ -254,8 +257,11 @@ export async function viteAlephaBuild(
|
|
|
254
257
|
}
|
|
255
258
|
|
|
256
259
|
if (options.cloudflare) {
|
|
260
|
+
const config =
|
|
261
|
+
typeof options.cloudflare === "boolean" ? {} : options.cloudflare;
|
|
257
262
|
await generateCloudflare({
|
|
258
263
|
distDir,
|
|
264
|
+
config,
|
|
259
265
|
});
|
|
260
266
|
}
|
|
261
267
|
|
|
@@ -132,12 +132,16 @@ export async function viteAlephaDev(
|
|
|
132
132
|
});
|
|
133
133
|
|
|
134
134
|
server.config.logger.info = (msg: string) => {
|
|
135
|
-
|
|
135
|
+
console.log(msg);
|
|
136
136
|
};
|
|
137
137
|
|
|
138
138
|
server.config.logger.clearScreen = () => {};
|
|
139
139
|
|
|
140
|
-
|
|
140
|
+
// Return a function - it runs AFTER internal middlewares are set up
|
|
141
|
+
// and after buildStart has been called
|
|
142
|
+
return async () => {
|
|
143
|
+
await runner.start(server);
|
|
144
|
+
};
|
|
141
145
|
},
|
|
142
146
|
async closeBundle() {
|
|
143
147
|
// Cleanup handled by runner
|
|
@@ -91,6 +91,7 @@ export async function buildServer(
|
|
|
91
91
|
minify: true,
|
|
92
92
|
chunkSizeWarningLimit: 10000,
|
|
93
93
|
rollupOptions: {
|
|
94
|
+
external: ["bun"],
|
|
94
95
|
output: {
|
|
95
96
|
entryFileNames: "[hash].js",
|
|
96
97
|
chunkFileNames: "[hash].js",
|
|
@@ -144,7 +145,7 @@ export async function buildServer(
|
|
|
144
145
|
|
|
145
146
|
await writeFile(
|
|
146
147
|
`${opts.distDir}/index.js`,
|
|
147
|
-
`${warning}\nimport './server/${entryFile}'
|
|
148
|
+
`${warning}\nimport './server/${entryFile}';\n\n${template}`.trim(),
|
|
148
149
|
);
|
|
149
150
|
|
|
150
151
|
return { entryFile };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { writeFile } from "node:fs/promises";
|
|
1
|
+
import { access, writeFile } from "node:fs/promises";
|
|
2
2
|
import { basename, join } from "node:path";
|
|
3
3
|
|
|
4
4
|
export interface GenerateCloudflareOptions {
|
|
@@ -8,6 +8,15 @@ export interface GenerateCloudflareOptions {
|
|
|
8
8
|
* @default "dist"
|
|
9
9
|
*/
|
|
10
10
|
distDir?: string;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Additional Wrangler configuration options to merge into wrangler.jsonc.
|
|
14
|
+
*/
|
|
15
|
+
config?: WranglerConfig;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface WranglerConfig {
|
|
19
|
+
[key: string]: any;
|
|
11
20
|
}
|
|
12
21
|
|
|
13
22
|
const WARNING_COMMENT =
|
|
@@ -27,30 +36,47 @@ export async function generateCloudflare(
|
|
|
27
36
|
const distDir = opts.distDir ?? "dist";
|
|
28
37
|
const root = process.cwd();
|
|
29
38
|
const name = basename(root);
|
|
39
|
+
const hasAssets = await access(join(root, distDir, "public"))
|
|
40
|
+
.then(() => true)
|
|
41
|
+
.catch(() => false);
|
|
30
42
|
|
|
31
|
-
|
|
32
|
-
await writeWorkerEntryPoint(root, distDir);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Write the wrangler.jsonc configuration file for Cloudflare Workers
|
|
37
|
-
*/
|
|
38
|
-
async function writeWranglerConfig(
|
|
39
|
-
root: string,
|
|
40
|
-
distDir: string,
|
|
41
|
-
name: string,
|
|
42
|
-
): Promise<void> {
|
|
43
|
-
const wrangler = {
|
|
43
|
+
const wrangler: WranglerConfig = {
|
|
44
44
|
name,
|
|
45
45
|
main: "./main.cloudflare.js",
|
|
46
46
|
compatibility_flags: ["nodejs_compat"],
|
|
47
47
|
compatibility_date: "2025-11-17",
|
|
48
|
+
...opts.config,
|
|
48
49
|
};
|
|
49
50
|
|
|
51
|
+
if (hasAssets) {
|
|
52
|
+
wrangler.assets ??= {
|
|
53
|
+
directory: "./public",
|
|
54
|
+
binding: "ASSETS",
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const url = process.env.DATABASE_URL;
|
|
59
|
+
if (url?.startsWith("cloudflare-d1:")) {
|
|
60
|
+
const [name, id] = url
|
|
61
|
+
.replace("cloudflare-d1://", "")
|
|
62
|
+
.replace("cloudflare-d1:", "")
|
|
63
|
+
.split(":");
|
|
64
|
+
wrangler.d1_databases = wrangler.d1_databases || [];
|
|
65
|
+
wrangler.d1_databases.push({
|
|
66
|
+
binding: name,
|
|
67
|
+
database_name: name,
|
|
68
|
+
database_id: id,
|
|
69
|
+
});
|
|
70
|
+
wrangler.vars ??= {};
|
|
71
|
+
wrangler.vars.DATABASE_URL = `cloudflare-d1://${name}:${id}`;
|
|
72
|
+
}
|
|
73
|
+
|
|
50
74
|
await writeFile(
|
|
51
75
|
join(root, distDir, "wrangler.jsonc"),
|
|
52
76
|
JSON.stringify(wrangler, null, 2),
|
|
53
77
|
);
|
|
78
|
+
|
|
79
|
+
await writeWorkerEntryPoint(root, distDir);
|
|
54
80
|
}
|
|
55
81
|
|
|
56
82
|
/**
|
|
@@ -64,9 +90,11 @@ async function writeWorkerEntryPoint(
|
|
|
64
90
|
import "./index.js";
|
|
65
91
|
|
|
66
92
|
export default {
|
|
67
|
-
fetch: async (request) => {
|
|
93
|
+
fetch: async (request, env) => {
|
|
68
94
|
const ctx = { req: request, res: undefined };
|
|
69
95
|
|
|
96
|
+
__alepha.set("cloudflare.env", env);
|
|
97
|
+
|
|
70
98
|
await __alepha.start();
|
|
71
99
|
await __alepha.events.emit("web:request", ctx);
|
|
72
100
|
|