alchemy 0.15.11 → 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,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.11",
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",
@@ -3,12 +3,16 @@ import type { Context } from "../context.js";
3
3
  import { Resource } from "../resource.js";
4
4
  import type { Secret } from "../secret.js";
5
5
  import { sha256 } from "../util/sha256.js";
6
- import { type CloudflareApiOptions, createCloudflareApi } from "./api.js";
6
+ import { createCloudflareApi, type CloudflareApiOptions } from "./api.js";
7
+ import {
8
+ PermissionGroups,
9
+ type PermissionGroupName,
10
+ } from "./permission-groups.js";
7
11
 
8
12
  /**
9
13
  * Permission group for a token policy
10
14
  */
11
- export interface TokenPolicyPermissionGroup {
15
+ export type TokenPolicyPermissionGroup = {
12
16
  /**
13
17
  * ID of the permission group
14
18
  */
@@ -18,7 +22,7 @@ export interface TokenPolicyPermissionGroup {
18
22
  * Optional metadata for the permission group
19
23
  */
20
24
  meta?: Record<string, any>;
21
- }
25
+ };
22
26
 
23
27
  /**
24
28
  * Policy that defines what the token can access
@@ -32,14 +36,27 @@ export interface TokenPolicy {
32
36
  /**
33
37
  * Permission groups to include in the policy
34
38
  */
35
- permissionGroups: TokenPolicyPermissionGroup[];
39
+ permissionGroups: (PermissionGroupName | TokenPolicyPermissionGroup)[];
36
40
 
37
41
  /**
38
42
  * Resources the policy applies to
39
43
  */
40
- resources: Record<string, string>;
44
+ resources: {
45
+ [key in TokenPolicyResourceKey]?: string;
46
+ };
41
47
  }
42
48
 
49
+ /**
50
+ * @see https://developers.cloudflare.com/fundamentals/api/reference/permissions/
51
+ */
52
+ type TokenPolicyResourceKey =
53
+ | `com.cloudflare.api.account`
54
+ | `com.cloudflare.api.account.${string}`
55
+ // | `com.cloudflare.api.account.zone`
56
+ | `com.cloudflare.api.account.zone.${string}`
57
+ // | `com.cloudflare.api.user`
58
+ | `com.cloudflare.api.user.${string}`;
59
+
43
60
  /**
44
61
  * Condition for token usage (e.g., IP restrictions)
45
62
  */
@@ -120,8 +137,7 @@ interface CloudflareApiToken {
120
137
  * Output returned after Account API Token creation/update
121
138
  */
122
139
  export interface AccountApiToken
123
- extends Resource<"cloudflare::AccountApiToken">,
124
- AccountApiTokenProps {
140
+ extends Resource<"cloudflare::AccountApiToken"> {
125
141
  /**
126
142
  * The ID of the token
127
143
  *
@@ -129,6 +145,11 @@ export interface AccountApiToken
129
145
  */
130
146
  id: string;
131
147
 
148
+ /**
149
+ * Name of the token
150
+ */
151
+ name: string;
152
+
132
153
  /**
133
154
  * Status of the token
134
155
  */
@@ -147,7 +168,7 @@ export interface AccountApiToken
147
168
  *
148
169
  * An alias of {@link id}
149
170
  */
150
- accessKeyId: string;
171
+ accessKeyId: Secret;
151
172
 
152
173
  /**
153
174
  * Secret access key for the token
@@ -156,7 +177,7 @@ export interface AccountApiToken
156
177
  *
157
178
  * @see https://developers.cloudflare.com/r2/api/tokens/#get-s3-api-credentials-from-an-api-token
158
179
  */
159
- secretAccessKey: string;
180
+ secretAccessKey: Secret;
160
181
  }
161
182
 
162
183
  /**
@@ -169,21 +190,13 @@ export interface AccountApiToken
169
190
  * @see https://developers.cloudflare.com/api/resources/accounts/subresources/tokens/methods/create/
170
191
  *
171
192
  * @example
172
- * // First, fetch all permission groups
173
- * const permissions = await PermissionGroups("cloudflare-permissions", {
174
- * accountId: cfAccountId,
175
- * });
176
- *
177
193
  * // Create a token with read-only permissions for specific zones
178
194
  * const readOnlyToken = await AccountApiToken("readonly-token", {
179
195
  * name: "Readonly Zone Token",
180
196
  * policies: [
181
197
  * {
182
198
  * effect: "allow",
183
- * permissionGroups: [
184
- * { id: permissions["Zone Read"].id },
185
- * { id: permissions["Analytics Read"].id }
186
- * ],
199
+ * permissionGroups: ["Zone Read", "Analytics Read"],
187
200
  * resources: {
188
201
  * "com.cloudflare.api.account.zone.22b1de5f1c0e4b3ea97bb1e963b06a43": "*",
189
202
  * "com.cloudflare.api.account.zone.eb78d65290b24279ba6f44721b3ea3c4": "*"
@@ -200,9 +213,7 @@ export interface AccountApiToken
200
213
  * policies: [
201
214
  * {
202
215
  * effect: "allow",
203
- * permissionGroups: [
204
- * { id: permissions["Worker Routes Edit"].id }
205
- * ],
216
+ * permissionGroups: ["Worker Routes Edit"],
206
217
  * resources: {
207
218
  * "com.cloudflare.api.account.worker.route.*": "*"
208
219
  * }
@@ -217,6 +228,21 @@ export interface AccountApiToken
217
228
  * }
218
229
  * }
219
230
  * });
231
+ *
232
+ * @example
233
+ * // Create a token with bucket access permissions
234
+ * const storageToken = await AccountApiToken("account-access-token", {
235
+ * name: "alchemy-account-access-token",
236
+ * policies: [
237
+ * {
238
+ * effect: "allow",
239
+ * permissionGroups: ["Workers R2 Storage Write"],
240
+ * resources: {
241
+ * "com.cloudflare.api.account": "*",
242
+ * },
243
+ * },
244
+ * ],
245
+ * });
220
246
  */
221
247
  export const AccountApiToken = Resource(
222
248
  "cloudflare::AccountApiToken",
@@ -251,16 +277,34 @@ export const AccountApiToken = Resource(
251
277
  return this.destroy();
252
278
  }
253
279
 
280
+ const permissionGroups = await PermissionGroups(
281
+ "cloudflare-permission-groups",
282
+ props,
283
+ );
284
+
254
285
  // Transform our properties to API format
255
286
  const apiPayload = {
256
287
  name: props.name,
257
288
  policies: props.policies.map((policy) => ({
258
289
  effect: policy.effect,
259
- permission_groups: policy.permissionGroups.map((pg) => ({
260
- id: pg.id,
261
- meta: pg.meta || {},
262
- })),
263
- resources: policy.resources,
290
+ permission_groups: policy.permissionGroups.map((pg) =>
291
+ typeof pg === "string"
292
+ ? {
293
+ id: permissionGroups[pg].id,
294
+ }
295
+ : {
296
+ id: pg.id,
297
+ meta: pg.meta || {},
298
+ },
299
+ ),
300
+ resources: Object.fromEntries(
301
+ Object.entries(policy.resources).map(([key, value]) => {
302
+ if (key === "com.cloudflare.api.account") {
303
+ return [`com.cloudflare.api.account.${api.accountId}`, value];
304
+ }
305
+ return [key, value];
306
+ }),
307
+ ),
264
308
  })),
265
309
  // Format dates for Cloudflare API (removing milliseconds)
266
310
  ...(props.expiresOn
@@ -339,14 +383,6 @@ export const AccountApiToken = Resource(
339
383
  id: tokenData.id,
340
384
  name: tokenData.name,
341
385
  status: tokenData.status,
342
- policies: tokenData.policies.map((policy) => ({
343
- effect: policy.effect,
344
- permissionGroups: policy.permission_groups.map((pg) => ({
345
- id: pg.id,
346
- meta: pg.meta,
347
- })),
348
- resources: policy.resources,
349
- })),
350
386
  ...(tokenData.expires_on ? { expiresOn: tokenData.expires_on } : {}),
351
387
  ...(tokenData.not_before ? { notBefore: tokenData.not_before } : {}),
352
388
  ...(tokenData.condition
@@ -362,8 +398,8 @@ export const AccountApiToken = Resource(
362
398
  }
363
399
  : {}),
364
400
  value: tokenValue,
365
- accessKeyId: tokenData.id,
366
- secretAccessKey: sha256(tokenValue.unencrypted),
401
+ accessKeyId: alchemy.secret(tokenData.id),
402
+ secretAccessKey: alchemy.secret(sha256(tokenValue.unencrypted)),
367
403
  });
368
404
  },
369
405
  );
@@ -32,6 +32,14 @@ interface PermissionGroupsResponse {
32
32
  messages: any[];
33
33
  }
34
34
 
35
+ export type PermissionGroupName = R2PermissionGroups;
36
+
37
+ export type R2PermissionGroups =
38
+ | "Workers R2 Storage Write"
39
+ | "Workers R2 Storage Read"
40
+ | "Workers R2 Storage Bucket Item Write"
41
+ | "Workers R2 Storage Bucket Item Read";
42
+
35
43
  /**
36
44
  * All Cloudflare permission groups mapped by name to ID
37
45
  *