alchemy 0.15.12 → 0.15.13

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.
@@ -0,0 +1,52 @@
1
+ import type { Secret } from "../secret.js";
2
+ /**
3
+ * Options for Upstash API requests
4
+ */
5
+ export interface UpstashApiOptions {
6
+ /**
7
+ * API key to use (overrides environment variable)
8
+ */
9
+ apiKey?: Secret;
10
+ /**
11
+ * Email to use (overrides environment variable)
12
+ */
13
+ email?: string;
14
+ }
15
+ /**
16
+ * Minimal API client using raw fetch
17
+ */
18
+ export declare class UpstashApi {
19
+ /** Base URL for API */
20
+ readonly baseUrl: string;
21
+ /** API key */
22
+ readonly apiKey: string;
23
+ /** Email */
24
+ readonly email: string;
25
+ /**
26
+ * Create a new API client
27
+ *
28
+ * @param options API options
29
+ */
30
+ constructor(options?: UpstashApiOptions);
31
+ /**
32
+ * Make a request to the API
33
+ *
34
+ * @param path API path (without base URL)
35
+ * @param init Fetch init options
36
+ * @returns Raw Response object from fetch
37
+ */
38
+ fetch(path: string, init?: RequestInit): Promise<Response>;
39
+ /**
40
+ * Helper for GET requests
41
+ */
42
+ get(path: string, init?: RequestInit): Promise<Response>;
43
+ /**
44
+ * Helper for POST requests
45
+ */
46
+ post(path: string, body: any, init?: RequestInit): Promise<Response>;
47
+ /**
48
+ * Helper for DELETE requests
49
+ */
50
+ delete(path: string, init?: RequestInit): Promise<Response>;
51
+ }
52
+ //# sourceMappingURL=api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/upstash/api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAE3C;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,qBAAa,UAAU;IACrB,uBAAuB;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAEzB,cAAc;IACd,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAExB,YAAY;IACZ,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAEvB;;;;OAIG;gBACS,OAAO,GAAE,iBAAsB;IAe3C;;;;;;OAMG;IACG,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,WAAgB,GAAG,OAAO,CAAC,QAAQ,CAAC;IAmBpE;;OAEG;IACG,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,WAAgB,GAAG,OAAO,CAAC,QAAQ,CAAC;IAIlE;;OAEG;IACG,IAAI,CACR,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,GAAG,EACT,IAAI,GAAE,WAAgB,GACrB,OAAO,CAAC,QAAQ,CAAC;IAQpB;;OAEG;IACG,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,WAAgB,GAAG,OAAO,CAAC,QAAQ,CAAC;CAGtE"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Minimal API client using raw fetch
3
+ */
4
+ export class UpstashApi {
5
+ /** Base URL for API */
6
+ baseUrl;
7
+ /** API key */
8
+ apiKey;
9
+ /** Email */
10
+ email;
11
+ /**
12
+ * Create a new API client
13
+ *
14
+ * @param options API options
15
+ */
16
+ constructor(options = {}) {
17
+ this.baseUrl = "https://api.upstash.com/v2";
18
+ this.apiKey =
19
+ options.apiKey?.unencrypted ?? process.env.UPSTASH_API_KEY ?? "";
20
+ this.email = options.email ?? process.env.UPSTASH_EMAIL ?? "";
21
+ if (!this.apiKey) {
22
+ throw new Error("UPSTASH_API_KEY environment variable is required");
23
+ }
24
+ if (!this.email) {
25
+ throw new Error("UPSTASH_EMAIL environment variable is required");
26
+ }
27
+ }
28
+ /**
29
+ * Make a request to the API
30
+ *
31
+ * @param path API path (without base URL)
32
+ * @param init Fetch init options
33
+ * @returns Raw Response object from fetch
34
+ */
35
+ async fetch(path, init = {}) {
36
+ const headers = {
37
+ "Content-Type": "application/json",
38
+ Authorization: `Basic ${btoa(`${this.email}:${this.apiKey}`)}`,
39
+ };
40
+ if (init.headers) {
41
+ const initHeaders = init.headers;
42
+ Object.keys(initHeaders).forEach((key) => {
43
+ headers[key] = initHeaders[key];
44
+ });
45
+ }
46
+ return fetch(`${this.baseUrl}${path}`, {
47
+ ...init,
48
+ headers,
49
+ });
50
+ }
51
+ /**
52
+ * Helper for GET requests
53
+ */
54
+ async get(path, init = {}) {
55
+ return this.fetch(path, { ...init, method: "GET" });
56
+ }
57
+ /**
58
+ * Helper for POST requests
59
+ */
60
+ async post(path, body, init = {}) {
61
+ return this.fetch(path, {
62
+ ...init,
63
+ method: "POST",
64
+ body: JSON.stringify(body),
65
+ });
66
+ }
67
+ /**
68
+ * Helper for DELETE requests
69
+ */
70
+ async delete(path, init = {}) {
71
+ return this.fetch(path, { ...init, method: "DELETE" });
72
+ }
73
+ }
74
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/upstash/api.ts"],"names":[],"mappings":"AAiBA;;GAEG;AACH,MAAM,OAAO,UAAU;IACrB,uBAAuB;IACd,OAAO,CAAS;IAEzB,cAAc;IACL,MAAM,CAAS;IAExB,YAAY;IACH,KAAK,CAAS;IAEvB;;;;OAIG;IACH,YAAY,UAA6B,EAAE;QACzC,IAAI,CAAC,OAAO,GAAG,4BAA4B,CAAC;QAC5C,IAAI,CAAC,MAAM;YACT,OAAO,CAAC,MAAM,EAAE,WAAW,IAAI,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,EAAE,CAAC;QACnE,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,EAAE,CAAC;QAE9D,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAC;QACtE,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;YAChB,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;QACpE,CAAC;IACH,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,KAAK,CAAC,IAAY,EAAE,OAAoB,EAAE;QAC9C,MAAM,OAAO,GAA2B;YACtC,cAAc,EAAE,kBAAkB;YAClC,aAAa,EAAE,SAAS,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE;SAC/D,CAAC;QAEF,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,MAAM,WAAW,GAAG,IAAI,CAAC,OAAiC,CAAC;YAC3D,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE;gBACvC,OAAO,CAAC,GAAG,CAAC,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;YAClC,CAAC,CAAC,CAAC;QACL,CAAC;QAED,OAAO,KAAK,CAAC,GAAG,IAAI,CAAC,OAAO,GAAG,IAAI,EAAE,EAAE;YACrC,GAAG,IAAI;YACP,OAAO;SACR,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,GAAG,CAAC,IAAY,EAAE,OAAoB,EAAE;QAC5C,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;IACtD,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,IAAI,CACR,IAAY,EACZ,IAAS,EACT,OAAoB,EAAE;QAEtB,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE;YACtB,GAAG,IAAI;YACP,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;SAC3B,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,MAAM,CAAC,IAAY,EAAE,OAAoB,EAAE;QAC/C,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;IACzD,CAAC;CACF"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Custom error class for Upstash API errors
3
+ */
4
+ export declare class UpstashError extends Error {
5
+ /**
6
+ * HTTP status code
7
+ */
8
+ statusCode: number;
9
+ /**
10
+ * Original response object
11
+ */
12
+ response: Response;
13
+ /**
14
+ * Create a new Upstash error
15
+ *
16
+ * @param message Error message
17
+ * @param statusCode HTTP status code
18
+ * @param response Original response object
19
+ */
20
+ constructor(message: string, statusCode: number, response: Response);
21
+ }
22
+ //# sourceMappingURL=error.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../../src/upstash/error.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,qBAAa,YAAa,SAAQ,KAAK;IACrC;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,QAAQ,EAAE,QAAQ,CAAC;IAEnB;;;;;;OAMG;gBACS,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ;CAMpE"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Custom error class for Upstash API errors
3
+ */
4
+ export class UpstashError extends Error {
5
+ /**
6
+ * HTTP status code
7
+ */
8
+ statusCode;
9
+ /**
10
+ * Original response object
11
+ */
12
+ response;
13
+ /**
14
+ * Create a new Upstash error
15
+ *
16
+ * @param message Error message
17
+ * @param statusCode HTTP status code
18
+ * @param response Original response object
19
+ */
20
+ constructor(message, statusCode, response) {
21
+ super(message);
22
+ this.name = "UpstashError";
23
+ this.statusCode = statusCode;
24
+ this.response = response;
25
+ }
26
+ }
27
+ //# sourceMappingURL=error.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error.js","sourceRoot":"","sources":["../../src/upstash/error.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,OAAO,YAAa,SAAQ,KAAK;IACrC;;OAEG;IACH,UAAU,CAAS;IAEnB;;OAEG;IACH,QAAQ,CAAW;IAEnB;;;;;;OAMG;IACH,YAAY,OAAe,EAAE,UAAkB,EAAE,QAAkB;QACjE,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;QAC3B,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;CACF"}
@@ -0,0 +1,4 @@
1
+ export * from "./api.js";
2
+ export * from "./error.js";
3
+ export * from "./redis.js";
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/upstash/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC"}
@@ -0,0 +1,4 @@
1
+ export * from "./api.js";
2
+ export * from "./error.js";
3
+ export * from "./redis.js";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/upstash/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC"}
@@ -0,0 +1,202 @@
1
+ import type { Context } from "../context.js";
2
+ import { Resource } from "../resource.js";
3
+ import type { Secret } from "../secret.js";
4
+ import { UpstashApi } from "./api.js";
5
+ /**
6
+ * Available regions for Upstash Redis databases
7
+ */
8
+ export type UpstashRegion = "us-east-1" | "us-west-1" | "us-west-2" | "eu-west-1" | "eu-central-1" | "ap-southeast-1" | "ap-southeast-2" | "ap-northeast-1" | "sa-east-1";
9
+ /**
10
+ * Properties for creating or updating an UpstashRedis database
11
+ */
12
+ export interface UpstashRedisProps {
13
+ /**
14
+ * Name of the database
15
+ */
16
+ name: string;
17
+ /**
18
+ * Primary region for the database
19
+ */
20
+ primaryRegion: UpstashRegion;
21
+ /**
22
+ * Read regions for the database
23
+ */
24
+ readRegions?: UpstashRegion[];
25
+ /**
26
+ * Monthly budget for the database
27
+ */
28
+ budget?: number;
29
+ /**
30
+ * Whether to enable eviction for the database
31
+ */
32
+ eviction?: boolean;
33
+ /**
34
+ * API key to use (overrides environment variable)
35
+ */
36
+ apiKey?: Secret;
37
+ /**
38
+ * Email to use (overrides environment variable)
39
+ */
40
+ email?: string;
41
+ }
42
+ /**
43
+ * Output returned after UpstashRedis creation/update
44
+ */
45
+ export interface UpstashRedis extends Resource<"upstash::Redis">, UpstashRedisProps {
46
+ /**
47
+ * ID of the database
48
+ */
49
+ id: string;
50
+ /**
51
+ * Type of the database in terms of pricing model
52
+ */
53
+ databaseType: string;
54
+ /**
55
+ * Region where database is hosted
56
+ */
57
+ region: "global";
58
+ /**
59
+ * Database port for clients to connect
60
+ */
61
+ port: number;
62
+ /**
63
+ * Creation time of the database as Unix time
64
+ */
65
+ createdAt: number;
66
+ /**
67
+ * State of database (active or deleted)
68
+ */
69
+ state: string;
70
+ /**
71
+ * Password of the database
72
+ */
73
+ password: Secret;
74
+ /**
75
+ * Email or team id of the owner of the database
76
+ */
77
+ userEmail: string;
78
+ /**
79
+ * Endpoint URL of the database
80
+ */
81
+ endpoint: string;
82
+ /**
83
+ * Whether TLS is enabled
84
+ */
85
+ tls: boolean;
86
+ /**
87
+ * REST token for the database
88
+ */
89
+ restToken: Secret;
90
+ /**
91
+ * Read-only REST token for the database
92
+ */
93
+ readOnlyRestToken: Secret;
94
+ }
95
+ /**
96
+ * Create and manage Upstash Redis databases
97
+ *
98
+ * @example
99
+ * // Create a basic Redis database in us-east-1:
100
+ * const redis = await UpstashRedis("my-redis", {
101
+ * name: "my-redis",
102
+ * primaryRegion: "us-east-1"
103
+ * });
104
+ *
105
+ * @example
106
+ * // Create a Redis database with read replicas:
107
+ * const redis = await UpstashRedis("my-redis", {
108
+ * name: "my-redis",
109
+ * primaryRegion: "us-east-1",
110
+ * readRegions: ["us-west-1", "us-west-2"]
111
+ * });
112
+ *
113
+ * @example
114
+ * // Create a Redis database with a monthly budget:
115
+ * const redis = await UpstashRedis("my-redis", {
116
+ * name: "my-redis",
117
+ * primaryRegion: "us-east-1",
118
+ * budget: 100
119
+ * });
120
+ */
121
+ export declare const UpstashRedis: (((this: any, id: string, props?: {}) => never) & (new (_: never) => never)) | ((this: Context<UpstashRedis>, id: string, props: UpstashRedisProps) => Promise<UpstashRedis>);
122
+ /**
123
+ * Response from Upstash API for database operations
124
+ */
125
+ interface UpstashDatabaseResponse {
126
+ database_id: string;
127
+ database_name: string;
128
+ database_type: string;
129
+ region: "global";
130
+ type: string;
131
+ port: number;
132
+ creation_time: number;
133
+ state: string;
134
+ password: string;
135
+ user_email: string;
136
+ endpoint: string;
137
+ tls: boolean;
138
+ rest_token: string;
139
+ read_only_rest_token: string;
140
+ eviction: boolean;
141
+ read_regions?: UpstashRegion[];
142
+ }
143
+ /**
144
+ * Parameters for creating a Redis database
145
+ */
146
+ export interface CreateRedisDatabaseParams {
147
+ name: string;
148
+ primary_region: UpstashRegion;
149
+ read_regions?: UpstashRegion[];
150
+ region: "global";
151
+ tls: boolean;
152
+ budget?: number;
153
+ }
154
+ /**
155
+ * Delete a Redis database
156
+ *
157
+ * @param api Upstash API client
158
+ * @param databaseId ID of the database to delete
159
+ */
160
+ export declare function deleteRedisDatabase(api: UpstashApi, databaseId: string): Promise<void>;
161
+ /**
162
+ * Rename a Redis database
163
+ *
164
+ * @param api Upstash API client
165
+ * @param databaseId ID of the database to rename
166
+ * @param name New name for the database
167
+ */
168
+ export declare function renameRedisDatabase(api: UpstashApi, databaseId: string, name: string): Promise<void>;
169
+ /**
170
+ * Update read regions for a Redis database
171
+ *
172
+ * @param api Upstash API client
173
+ * @param databaseId ID of the database to update
174
+ * @param readRegions Array of read regions
175
+ */
176
+ export declare function updateRedisReadRegions(api: UpstashApi, databaseId: string, readRegions: UpstashRegion[]): Promise<void>;
177
+ /**
178
+ * Enable or disable eviction for a Redis database
179
+ *
180
+ * @param api Upstash API client
181
+ * @param databaseId ID of the database to update
182
+ * @param enable Whether to enable or disable eviction
183
+ */
184
+ export declare function setRedisEviction(api: UpstashApi, databaseId: string, enable: boolean): Promise<void>;
185
+ /**
186
+ * Get information about a Redis database
187
+ *
188
+ * @param api Upstash API client
189
+ * @param databaseId ID of the database to get information about
190
+ * @returns Database information
191
+ */
192
+ export declare function getRedisDatabase(api: UpstashApi, databaseId: string): Promise<UpstashDatabaseResponse>;
193
+ /**
194
+ * Create a new Redis database
195
+ *
196
+ * @param api Upstash API client
197
+ * @param params Parameters for the new database
198
+ * @returns Created database information
199
+ */
200
+ export declare function createRedisDatabase(api: UpstashApi, params: CreateRedisDatabaseParams): Promise<UpstashDatabaseResponse>;
201
+ export {};
202
+ //# sourceMappingURL=redis.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redis.d.ts","sourceRoot":"","sources":["../../src/upstash/redis.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,178 @@
1
+ import { alchemy } from "../alchemy.js";
2
+ import { Resource } from "../resource.js";
3
+ import { UpstashApi } from "./api.js";
4
+ import { UpstashError } from "./error.js";
5
+ /**
6
+ * Create and manage Upstash Redis databases
7
+ *
8
+ * @example
9
+ * // Create a basic Redis database in us-east-1:
10
+ * const redis = await UpstashRedis("my-redis", {
11
+ * name: "my-redis",
12
+ * primaryRegion: "us-east-1"
13
+ * });
14
+ *
15
+ * @example
16
+ * // Create a Redis database with read replicas:
17
+ * const redis = await UpstashRedis("my-redis", {
18
+ * name: "my-redis",
19
+ * primaryRegion: "us-east-1",
20
+ * readRegions: ["us-west-1", "us-west-2"]
21
+ * });
22
+ *
23
+ * @example
24
+ * // Create a Redis database with a monthly budget:
25
+ * const redis = await UpstashRedis("my-redis", {
26
+ * name: "my-redis",
27
+ * primaryRegion: "us-east-1",
28
+ * budget: 100
29
+ * });
30
+ */
31
+ export const UpstashRedis = Resource("upstash::Redis", async function (id, props) {
32
+ const api = new UpstashApi({
33
+ apiKey: props.apiKey,
34
+ email: props.email,
35
+ });
36
+ if (this.phase === "delete") {
37
+ await deleteRedisDatabase(api, this.output.id);
38
+ return this.destroy();
39
+ }
40
+ const eviction = props.eviction ?? false;
41
+ // @ts-ignore This is overridden during update/create
42
+ let database = {};
43
+ if (this.phase === "update") {
44
+ // Update name if changed
45
+ if (props.name !== this.output.name) {
46
+ await renameRedisDatabase(api, this.output.id, props.name);
47
+ }
48
+ // Update read regions if changed
49
+ if (JSON.stringify(props.readRegions) !==
50
+ JSON.stringify(this.output.readRegions)) {
51
+ await updateRedisReadRegions(api, this.output.id, props.readRegions || []);
52
+ }
53
+ // Handle eviction setting if changed
54
+ if (props.eviction !== undefined &&
55
+ props.eviction !== this.output.eviction) {
56
+ await setRedisEviction(api, this.output.id, props.eviction);
57
+ }
58
+ // Get updated database info
59
+ database = await getRedisDatabase(api, this.output.id);
60
+ }
61
+ if (this.phase === "create") {
62
+ database = await createRedisDatabase(api, {
63
+ budget: props.budget,
64
+ name: props.name,
65
+ primary_region: props.primaryRegion,
66
+ read_regions: props.readRegions,
67
+ region: "global",
68
+ tls: true,
69
+ });
70
+ if (eviction) {
71
+ await setRedisEviction(api, database.database_id, eviction);
72
+ }
73
+ }
74
+ return this.create({
75
+ id: database.database_id,
76
+ name: database.database_name,
77
+ databaseType: database.database_type,
78
+ region: database.region,
79
+ port: database.port,
80
+ createdAt: database.creation_time,
81
+ state: database.state,
82
+ password: alchemy.secret(database.password),
83
+ userEmail: database.user_email,
84
+ endpoint: database.endpoint,
85
+ tls: database.tls,
86
+ restToken: alchemy.secret(database.rest_token),
87
+ readOnlyRestToken: alchemy.secret(database.read_only_rest_token),
88
+ primaryRegion: props.primaryRegion,
89
+ readRegions: props.readRegions,
90
+ budget: props.budget,
91
+ eviction: props.eviction ?? false,
92
+ });
93
+ });
94
+ /**
95
+ * Delete a Redis database
96
+ *
97
+ * @param api Upstash API client
98
+ * @param databaseId ID of the database to delete
99
+ */
100
+ export async function deleteRedisDatabase(api, databaseId) {
101
+ const response = await api.delete(`/redis/database/${databaseId}`);
102
+ if (!response.ok && response.status !== 404) {
103
+ throw new UpstashError(`Error deleting database: ${response.statusText}`, response.status, response);
104
+ }
105
+ }
106
+ /**
107
+ * Rename a Redis database
108
+ *
109
+ * @param api Upstash API client
110
+ * @param databaseId ID of the database to rename
111
+ * @param name New name for the database
112
+ */
113
+ export async function renameRedisDatabase(api, databaseId, name) {
114
+ const response = await api.post(`/redis/rename/${databaseId}`, {
115
+ name,
116
+ });
117
+ if (!response.ok) {
118
+ throw new UpstashError(`API error updating name: ${response.statusText}`, response.status, response);
119
+ }
120
+ }
121
+ /**
122
+ * Update read regions for a Redis database
123
+ *
124
+ * @param api Upstash API client
125
+ * @param databaseId ID of the database to update
126
+ * @param readRegions Array of read regions
127
+ */
128
+ export async function updateRedisReadRegions(api, databaseId, readRegions) {
129
+ const response = await api.post(`/redis/update-regions/${databaseId}`, {
130
+ read_regions: readRegions,
131
+ });
132
+ if (!response.ok) {
133
+ throw new UpstashError(`API error updating regions: ${response.statusText}`, response.status, response);
134
+ }
135
+ }
136
+ /**
137
+ * Enable or disable eviction for a Redis database
138
+ *
139
+ * @param api Upstash API client
140
+ * @param databaseId ID of the database to update
141
+ * @param enable Whether to enable or disable eviction
142
+ */
143
+ export async function setRedisEviction(api, databaseId, enable) {
144
+ const evictionEndpoint = enable ? "enable-eviction" : "disable-eviction";
145
+ const response = await api.post(`/redis/${evictionEndpoint}/${databaseId}`, {});
146
+ if (!response.ok) {
147
+ console.warn(`API error updating eviction (status: ${response.status}): ${response.statusText}. (Eviction may already be set)`);
148
+ }
149
+ }
150
+ /**
151
+ * Get information about a Redis database
152
+ *
153
+ * @param api Upstash API client
154
+ * @param databaseId ID of the database to get information about
155
+ * @returns Database information
156
+ */
157
+ export async function getRedisDatabase(api, databaseId) {
158
+ const response = await api.get(`/redis/database/${databaseId}`);
159
+ if (!response.ok) {
160
+ throw new UpstashError(`API error: ${response.statusText}`, response.status, response);
161
+ }
162
+ return await response.json();
163
+ }
164
+ /**
165
+ * Create a new Redis database
166
+ *
167
+ * @param api Upstash API client
168
+ * @param params Parameters for the new database
169
+ * @returns Created database information
170
+ */
171
+ export async function createRedisDatabase(api, params) {
172
+ const response = await api.post("/redis/database", params);
173
+ if (!response.ok) {
174
+ throw new UpstashError(`API error creating database: ${response.statusText}`, response.status, response);
175
+ }
176
+ return await response.json();
177
+ }
178
+ //# sourceMappingURL=redis.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redis.js","sourceRoot":"","sources":["../../src/upstash/redis.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAExC,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE1C,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AA2H1C;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,QAAQ,CAClC,gBAAgB,EAChB,KAAK,WAEH,EAAU,EACV,KAAwB;IAExB,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC;QACzB,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,KAAK,EAAE,KAAK,CAAC,KAAK;KACnB,CAAC,CAAC;IAEH,IAAI,IAAI,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC5B,MAAM,mBAAmB,CAAC,GAAG,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAC/C,OAAO,IAAI,CAAC,OAAO,EAAE,CAAC;IACxB,CAAC;IAED,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC;IAEzC,qDAAqD;IACrD,IAAI,QAAQ,GAA4B,EAAE,CAAC;IAE3C,IAAI,IAAI,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC5B,yBAAyB;QACzB,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;YACpC,MAAM,mBAAmB,CAAC,GAAG,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QAC7D,CAAC;QAED,iCAAiC;QACjC,IACE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,WAAW,CAAC;YACjC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,EACvC,CAAC;YACD,MAAM,sBAAsB,CAC1B,GAAG,EACH,IAAI,CAAC,MAAM,CAAC,EAAE,EACd,KAAK,CAAC,WAAW,IAAI,EAAE,CACxB,CAAC;QACJ,CAAC;QAED,qCAAqC;QACrC,IACE,KAAK,CAAC,QAAQ,KAAK,SAAS;YAC5B,KAAK,CAAC,QAAQ,KAAK,IAAI,CAAC,MAAM,CAAC,QAAQ,EACvC,CAAC;YACD,MAAM,gBAAgB,CAAC,GAAG,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;QAC9D,CAAC;QAED,4BAA4B;QAC5B,QAAQ,GAAG,MAAM,gBAAgB,CAAC,GAAG,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACzD,CAAC;IAED,IAAI,IAAI,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC5B,QAAQ,GAAG,MAAM,mBAAmB,CAAC,GAAG,EAAE;YACxC,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,cAAc,EAAE,KAAK,CAAC,aAAa;YACnC,YAAY,EAAE,KAAK,CAAC,WAAW;YAC/B,MAAM,EAAE,QAAQ;YAChB,GAAG,EAAE,IAAI;SACV,CAAC,CAAC;QAEH,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,gBAAgB,CAAC,GAAG,EAAE,QAAQ,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;QAC9D,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC,MAAM,CAAC;QACjB,EAAE,EAAE,QAAQ,CAAC,WAAW;QACxB,IAAI,EAAE,QAAQ,CAAC,aAAa;QAC5B,YAAY,EAAE,QAAQ,CAAC,aAAa;QACpC,MAAM,EAAE,QAAQ,CAAC,MAAM;QACvB,IAAI,EAAE,QAAQ,CAAC,IAAI;QACnB,SAAS,EAAE,QAAQ,CAAC,aAAa;QACjC,KAAK,EAAE,QAAQ,CAAC,KAAK;QACrB,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAC3C,SAAS,EAAE,QAAQ,CAAC,UAAU;QAC9B,QAAQ,EAAE,QAAQ,CAAC,QAAQ;QAC3B,GAAG,EAAE,QAAQ,CAAC,GAAG;QACjB,SAAS,EAAE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC;QAC9C,iBAAiB,EAAE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,oBAAoB,CAAC;QAChE,aAAa,EAAE,KAAK,CAAC,aAAa;QAClC,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,QAAQ,EAAE,KAAK,CAAC,QAAQ,IAAI,KAAK;KAClC,CAAC,CAAC;AACL,CAAC,CACF,CAAC;AAoCF;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,GAAe,EACf,UAAkB;IAElB,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC,mBAAmB,UAAU,EAAE,CAAC,CAAC;IAEnE,IAAI,CAAC,QAAQ,CAAC,EAAE,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;QAC5C,MAAM,IAAI,YAAY,CACpB,4BAA4B,QAAQ,CAAC,UAAU,EAAE,EACjD,QAAQ,CAAC,MAAM,EACf,QAAQ,CACT,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,GAAe,EACf,UAAkB,EAClB,IAAY;IAEZ,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,iBAAiB,UAAU,EAAE,EAAE;QAC7D,IAAI;KACL,CAAC,CAAC;IAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,YAAY,CACpB,4BAA4B,QAAQ,CAAC,UAAU,EAAE,EACjD,QAAQ,CAAC,MAAM,EACf,QAAQ,CACT,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,GAAe,EACf,UAAkB,EAClB,WAA4B;IAE5B,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,yBAAyB,UAAU,EAAE,EAAE;QACrE,YAAY,EAAE,WAAW;KAC1B,CAAC,CAAC;IAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,YAAY,CACpB,+BAA+B,QAAQ,CAAC,UAAU,EAAE,EACpD,QAAQ,CAAC,MAAM,EACf,QAAQ,CACT,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,GAAe,EACf,UAAkB,EAClB,MAAe;IAEf,MAAM,gBAAgB,GAAG,MAAM,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,kBAAkB,CAAC;IAEzE,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,IAAI,CAC7B,UAAU,gBAAgB,IAAI,UAAU,EAAE,EAC1C,EAAE,CACH,CAAC;IAEF,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,OAAO,CAAC,IAAI,CACV,wCAAwC,QAAQ,CAAC,MAAM,MAAM,QAAQ,CAAC,UAAU,iCAAiC,CAClH,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,GAAe,EACf,UAAkB;IAElB,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,GAAG,CAAC,mBAAmB,UAAU,EAAE,CAAC,CAAC;IAEhE,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,YAAY,CACpB,cAAc,QAAQ,CAAC,UAAU,EAAE,EACnC,QAAQ,CAAC,MAAM,EACf,QAAQ,CACT,CAAC;IACJ,CAAC;IAED,OAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;AAC/B,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,GAAe,EACf,MAAiC;IAEjC,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC;IAE3D,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,YAAY,CACpB,gCAAgC,QAAQ,CAAC,UAAU,EAAE,EACrD,QAAQ,CAAC,MAAM,EACf,QAAQ,CACT,CAAC;IACJ,CAAC;IAED,OAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;AAC/B,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "alchemy",
3
- "version": "0.15.12",
3
+ "version": "0.15.13",
4
4
  "type": "module",
5
5
  "module": "./lib/index.js",
6
6
  "scripts": {
@@ -26,6 +26,7 @@
26
26
  "./shadcn": "./lib/shadcn/index.js",
27
27
  "./stripe": "./lib/stripe/index.js",
28
28
  "./test/bun": "./lib/test/bun.js",
29
+ "./upstash": "./lib/upstash/index.js",
29
30
  "./web/astro": "./lib/web/astro/index.js",
30
31
  "./web/tailwind": "./lib/web/tailwind/index.js",
31
32
  "./web/vite": "./lib/web/vite/index.js",
@@ -0,0 +1,105 @@
1
+ import type { Secret } from "../secret.js";
2
+
3
+ /**
4
+ * Options for Upstash API requests
5
+ */
6
+ export interface UpstashApiOptions {
7
+ /**
8
+ * API key to use (overrides environment variable)
9
+ */
10
+ apiKey?: Secret;
11
+
12
+ /**
13
+ * Email to use (overrides environment variable)
14
+ */
15
+ email?: string;
16
+ }
17
+
18
+ /**
19
+ * Minimal API client using raw fetch
20
+ */
21
+ export class UpstashApi {
22
+ /** Base URL for API */
23
+ readonly baseUrl: string;
24
+
25
+ /** API key */
26
+ readonly apiKey: string;
27
+
28
+ /** Email */
29
+ readonly email: string;
30
+
31
+ /**
32
+ * Create a new API client
33
+ *
34
+ * @param options API options
35
+ */
36
+ constructor(options: UpstashApiOptions = {}) {
37
+ this.baseUrl = "https://api.upstash.com/v2";
38
+ this.apiKey =
39
+ options.apiKey?.unencrypted ?? process.env.UPSTASH_API_KEY ?? "";
40
+ this.email = options.email ?? process.env.UPSTASH_EMAIL ?? "";
41
+
42
+ if (!this.apiKey) {
43
+ throw new Error("UPSTASH_API_KEY environment variable is required");
44
+ }
45
+
46
+ if (!this.email) {
47
+ throw new Error("UPSTASH_EMAIL environment variable is required");
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Make a request to the API
53
+ *
54
+ * @param path API path (without base URL)
55
+ * @param init Fetch init options
56
+ * @returns Raw Response object from fetch
57
+ */
58
+ async fetch(path: string, init: RequestInit = {}): Promise<Response> {
59
+ const headers: Record<string, string> = {
60
+ "Content-Type": "application/json",
61
+ Authorization: `Basic ${btoa(`${this.email}:${this.apiKey}`)}`,
62
+ };
63
+
64
+ if (init.headers) {
65
+ const initHeaders = init.headers as Record<string, string>;
66
+ Object.keys(initHeaders).forEach((key) => {
67
+ headers[key] = initHeaders[key];
68
+ });
69
+ }
70
+
71
+ return fetch(`${this.baseUrl}${path}`, {
72
+ ...init,
73
+ headers,
74
+ });
75
+ }
76
+
77
+ /**
78
+ * Helper for GET requests
79
+ */
80
+ async get(path: string, init: RequestInit = {}): Promise<Response> {
81
+ return this.fetch(path, { ...init, method: "GET" });
82
+ }
83
+
84
+ /**
85
+ * Helper for POST requests
86
+ */
87
+ async post(
88
+ path: string,
89
+ body: any,
90
+ init: RequestInit = {},
91
+ ): Promise<Response> {
92
+ return this.fetch(path, {
93
+ ...init,
94
+ method: "POST",
95
+ body: JSON.stringify(body),
96
+ });
97
+ }
98
+
99
+ /**
100
+ * Helper for DELETE requests
101
+ */
102
+ async delete(path: string, init: RequestInit = {}): Promise<Response> {
103
+ return this.fetch(path, { ...init, method: "DELETE" });
104
+ }
105
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Custom error class for Upstash API errors
3
+ */
4
+ export class UpstashError extends Error {
5
+ /**
6
+ * HTTP status code
7
+ */
8
+ statusCode: number;
9
+
10
+ /**
11
+ * Original response object
12
+ */
13
+ response: Response;
14
+
15
+ /**
16
+ * Create a new Upstash error
17
+ *
18
+ * @param message Error message
19
+ * @param statusCode HTTP status code
20
+ * @param response Original response object
21
+ */
22
+ constructor(message: string, statusCode: number, response: Response) {
23
+ super(message);
24
+ this.name = "UpstashError";
25
+ this.statusCode = statusCode;
26
+ this.response = response;
27
+ }
28
+ }
@@ -0,0 +1,3 @@
1
+ export * from "./api.js";
2
+ export * from "./error.js";
3
+ export * from "./redis.js";
@@ -0,0 +1,421 @@
1
+ import { alchemy } from "../alchemy.js";
2
+ import type { Context } from "../context.js";
3
+ import { Resource } from "../resource.js";
4
+ import type { Secret } from "../secret.js";
5
+ import { UpstashApi } from "./api.js";
6
+ import { UpstashError } from "./error.js";
7
+
8
+ /**
9
+ * Available regions for Upstash Redis databases
10
+ */
11
+ export type UpstashRegion =
12
+ | "us-east-1"
13
+ | "us-west-1"
14
+ | "us-west-2"
15
+ | "eu-west-1"
16
+ | "eu-central-1"
17
+ | "ap-southeast-1"
18
+ | "ap-southeast-2"
19
+ | "ap-northeast-1"
20
+ | "sa-east-1";
21
+
22
+ /**
23
+ * Properties for creating or updating an UpstashRedis database
24
+ */
25
+ export interface UpstashRedisProps {
26
+ /**
27
+ * Name of the database
28
+ */
29
+ name: string;
30
+
31
+ /**
32
+ * Primary region for the database
33
+ */
34
+ primaryRegion: UpstashRegion;
35
+
36
+ /**
37
+ * Read regions for the database
38
+ */
39
+ readRegions?: UpstashRegion[];
40
+
41
+ /**
42
+ * Monthly budget for the database
43
+ */
44
+ budget?: number;
45
+
46
+ /**
47
+ * Whether to enable eviction for the database
48
+ */
49
+ eviction?: boolean;
50
+
51
+ /**
52
+ * API key to use (overrides environment variable)
53
+ */
54
+ apiKey?: Secret;
55
+
56
+ /**
57
+ * Email to use (overrides environment variable)
58
+ */
59
+ email?: string;
60
+ }
61
+
62
+ /**
63
+ * Output returned after UpstashRedis creation/update
64
+ */
65
+ export interface UpstashRedis
66
+ extends Resource<"upstash::Redis">,
67
+ UpstashRedisProps {
68
+ /**
69
+ * ID of the database
70
+ */
71
+ id: string;
72
+
73
+ /**
74
+ * Type of the database in terms of pricing model
75
+ */
76
+ databaseType: string;
77
+
78
+ /**
79
+ * Region where database is hosted
80
+ */
81
+ region: "global";
82
+
83
+ /**
84
+ * Database port for clients to connect
85
+ */
86
+ port: number;
87
+
88
+ /**
89
+ * Creation time of the database as Unix time
90
+ */
91
+ createdAt: number;
92
+
93
+ /**
94
+ * State of database (active or deleted)
95
+ */
96
+ state: string;
97
+
98
+ /**
99
+ * Password of the database
100
+ */
101
+ password: Secret;
102
+
103
+ /**
104
+ * Email or team id of the owner of the database
105
+ */
106
+ userEmail: string;
107
+
108
+ /**
109
+ * Endpoint URL of the database
110
+ */
111
+ endpoint: string;
112
+
113
+ /**
114
+ * Whether TLS is enabled
115
+ */
116
+ tls: boolean;
117
+
118
+ /**
119
+ * REST token for the database
120
+ */
121
+ restToken: Secret;
122
+
123
+ /**
124
+ * Read-only REST token for the database
125
+ */
126
+ readOnlyRestToken: Secret;
127
+ }
128
+
129
+ /**
130
+ * Create and manage Upstash Redis databases
131
+ *
132
+ * @example
133
+ * // Create a basic Redis database in us-east-1:
134
+ * const redis = await UpstashRedis("my-redis", {
135
+ * name: "my-redis",
136
+ * primaryRegion: "us-east-1"
137
+ * });
138
+ *
139
+ * @example
140
+ * // Create a Redis database with read replicas:
141
+ * const redis = await UpstashRedis("my-redis", {
142
+ * name: "my-redis",
143
+ * primaryRegion: "us-east-1",
144
+ * readRegions: ["us-west-1", "us-west-2"]
145
+ * });
146
+ *
147
+ * @example
148
+ * // Create a Redis database with a monthly budget:
149
+ * const redis = await UpstashRedis("my-redis", {
150
+ * name: "my-redis",
151
+ * primaryRegion: "us-east-1",
152
+ * budget: 100
153
+ * });
154
+ */
155
+ export const UpstashRedis = Resource(
156
+ "upstash::Redis",
157
+ async function (
158
+ this: Context<UpstashRedis>,
159
+ id: string,
160
+ props: UpstashRedisProps,
161
+ ): Promise<UpstashRedis> {
162
+ const api = new UpstashApi({
163
+ apiKey: props.apiKey,
164
+ email: props.email,
165
+ });
166
+
167
+ if (this.phase === "delete") {
168
+ await deleteRedisDatabase(api, this.output.id);
169
+ return this.destroy();
170
+ }
171
+
172
+ const eviction = props.eviction ?? false;
173
+
174
+ // @ts-ignore This is overridden during update/create
175
+ let database: UpstashDatabaseResponse = {};
176
+
177
+ if (this.phase === "update") {
178
+ // Update name if changed
179
+ if (props.name !== this.output.name) {
180
+ await renameRedisDatabase(api, this.output.id, props.name);
181
+ }
182
+
183
+ // Update read regions if changed
184
+ if (
185
+ JSON.stringify(props.readRegions) !==
186
+ JSON.stringify(this.output.readRegions)
187
+ ) {
188
+ await updateRedisReadRegions(
189
+ api,
190
+ this.output.id,
191
+ props.readRegions || [],
192
+ );
193
+ }
194
+
195
+ // Handle eviction setting if changed
196
+ if (
197
+ props.eviction !== undefined &&
198
+ props.eviction !== this.output.eviction
199
+ ) {
200
+ await setRedisEviction(api, this.output.id, props.eviction);
201
+ }
202
+
203
+ // Get updated database info
204
+ database = await getRedisDatabase(api, this.output.id);
205
+ }
206
+
207
+ if (this.phase === "create") {
208
+ database = await createRedisDatabase(api, {
209
+ budget: props.budget,
210
+ name: props.name,
211
+ primary_region: props.primaryRegion,
212
+ read_regions: props.readRegions,
213
+ region: "global",
214
+ tls: true,
215
+ });
216
+
217
+ if (eviction) {
218
+ await setRedisEviction(api, database.database_id, eviction);
219
+ }
220
+ }
221
+
222
+ return this.create({
223
+ id: database.database_id,
224
+ name: database.database_name,
225
+ databaseType: database.database_type,
226
+ region: database.region,
227
+ port: database.port,
228
+ createdAt: database.creation_time,
229
+ state: database.state,
230
+ password: alchemy.secret(database.password),
231
+ userEmail: database.user_email,
232
+ endpoint: database.endpoint,
233
+ tls: database.tls,
234
+ restToken: alchemy.secret(database.rest_token),
235
+ readOnlyRestToken: alchemy.secret(database.read_only_rest_token),
236
+ primaryRegion: props.primaryRegion,
237
+ readRegions: props.readRegions,
238
+ budget: props.budget,
239
+ eviction: props.eviction ?? false,
240
+ });
241
+ },
242
+ );
243
+
244
+ /**
245
+ * Response from Upstash API for database operations
246
+ */
247
+ interface UpstashDatabaseResponse {
248
+ database_id: string;
249
+ database_name: string;
250
+ database_type: string;
251
+ region: "global";
252
+ type: string;
253
+ port: number;
254
+ creation_time: number;
255
+ state: string;
256
+ password: string;
257
+ user_email: string;
258
+ endpoint: string;
259
+ tls: boolean;
260
+ rest_token: string;
261
+ read_only_rest_token: string;
262
+ eviction: boolean;
263
+ read_regions?: UpstashRegion[];
264
+ }
265
+
266
+ /**
267
+ * Parameters for creating a Redis database
268
+ */
269
+ export interface CreateRedisDatabaseParams {
270
+ name: string;
271
+ primary_region: UpstashRegion;
272
+ read_regions?: UpstashRegion[];
273
+ region: "global";
274
+ tls: boolean;
275
+ budget?: number;
276
+ }
277
+
278
+ /**
279
+ * Delete a Redis database
280
+ *
281
+ * @param api Upstash API client
282
+ * @param databaseId ID of the database to delete
283
+ */
284
+ export async function deleteRedisDatabase(
285
+ api: UpstashApi,
286
+ databaseId: string,
287
+ ): Promise<void> {
288
+ const response = await api.delete(`/redis/database/${databaseId}`);
289
+
290
+ if (!response.ok && response.status !== 404) {
291
+ throw new UpstashError(
292
+ `Error deleting database: ${response.statusText}`,
293
+ response.status,
294
+ response,
295
+ );
296
+ }
297
+ }
298
+
299
+ /**
300
+ * Rename a Redis database
301
+ *
302
+ * @param api Upstash API client
303
+ * @param databaseId ID of the database to rename
304
+ * @param name New name for the database
305
+ */
306
+ export async function renameRedisDatabase(
307
+ api: UpstashApi,
308
+ databaseId: string,
309
+ name: string,
310
+ ): Promise<void> {
311
+ const response = await api.post(`/redis/rename/${databaseId}`, {
312
+ name,
313
+ });
314
+
315
+ if (!response.ok) {
316
+ throw new UpstashError(
317
+ `API error updating name: ${response.statusText}`,
318
+ response.status,
319
+ response,
320
+ );
321
+ }
322
+ }
323
+
324
+ /**
325
+ * Update read regions for a Redis database
326
+ *
327
+ * @param api Upstash API client
328
+ * @param databaseId ID of the database to update
329
+ * @param readRegions Array of read regions
330
+ */
331
+ export async function updateRedisReadRegions(
332
+ api: UpstashApi,
333
+ databaseId: string,
334
+ readRegions: UpstashRegion[],
335
+ ): Promise<void> {
336
+ const response = await api.post(`/redis/update-regions/${databaseId}`, {
337
+ read_regions: readRegions,
338
+ });
339
+
340
+ if (!response.ok) {
341
+ throw new UpstashError(
342
+ `API error updating regions: ${response.statusText}`,
343
+ response.status,
344
+ response,
345
+ );
346
+ }
347
+ }
348
+
349
+ /**
350
+ * Enable or disable eviction for a Redis database
351
+ *
352
+ * @param api Upstash API client
353
+ * @param databaseId ID of the database to update
354
+ * @param enable Whether to enable or disable eviction
355
+ */
356
+ export async function setRedisEviction(
357
+ api: UpstashApi,
358
+ databaseId: string,
359
+ enable: boolean,
360
+ ): Promise<void> {
361
+ const evictionEndpoint = enable ? "enable-eviction" : "disable-eviction";
362
+
363
+ const response = await api.post(
364
+ `/redis/${evictionEndpoint}/${databaseId}`,
365
+ {},
366
+ );
367
+
368
+ if (!response.ok) {
369
+ console.warn(
370
+ `API error updating eviction (status: ${response.status}): ${response.statusText}. (Eviction may already be set)`,
371
+ );
372
+ }
373
+ }
374
+
375
+ /**
376
+ * Get information about a Redis database
377
+ *
378
+ * @param api Upstash API client
379
+ * @param databaseId ID of the database to get information about
380
+ * @returns Database information
381
+ */
382
+ export async function getRedisDatabase(
383
+ api: UpstashApi,
384
+ databaseId: string,
385
+ ): Promise<UpstashDatabaseResponse> {
386
+ const response = await api.get(`/redis/database/${databaseId}`);
387
+
388
+ if (!response.ok) {
389
+ throw new UpstashError(
390
+ `API error: ${response.statusText}`,
391
+ response.status,
392
+ response,
393
+ );
394
+ }
395
+
396
+ return await response.json();
397
+ }
398
+
399
+ /**
400
+ * Create a new Redis database
401
+ *
402
+ * @param api Upstash API client
403
+ * @param params Parameters for the new database
404
+ * @returns Created database information
405
+ */
406
+ export async function createRedisDatabase(
407
+ api: UpstashApi,
408
+ params: CreateRedisDatabaseParams,
409
+ ): Promise<UpstashDatabaseResponse> {
410
+ const response = await api.post("/redis/database", params);
411
+
412
+ if (!response.ok) {
413
+ throw new UpstashError(
414
+ `API error creating database: ${response.statusText}`,
415
+ response.status,
416
+ response,
417
+ );
418
+ }
419
+
420
+ return await response.json();
421
+ }