alchemy 0.30.0 → 0.30.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/lib/cloudflare/bindings.d.ts +24 -4
- package/lib/cloudflare/bindings.d.ts.map +1 -1
- package/lib/cloudflare/bindings.js.map +1 -1
- package/lib/cloudflare/bound.d.ts +3 -2
- package/lib/cloudflare/bound.d.ts.map +1 -1
- package/lib/cloudflare/index.d.ts +1 -0
- package/lib/cloudflare/index.d.ts.map +1 -1
- package/lib/cloudflare/index.js +1 -0
- package/lib/cloudflare/index.js.map +1 -1
- package/lib/cloudflare/queue.d.ts +24 -0
- package/lib/cloudflare/queue.d.ts.map +1 -1
- package/lib/cloudflare/queue.js +24 -0
- package/lib/cloudflare/queue.js.map +1 -1
- package/lib/cloudflare/secret-key.d.ts +120 -0
- package/lib/cloudflare/secret-key.d.ts.map +1 -0
- package/lib/cloudflare/secret-key.js +92 -0
- package/lib/cloudflare/secret-key.js.map +1 -0
- package/lib/cloudflare/secret.d.ts +9 -5
- package/lib/cloudflare/secret.d.ts.map +1 -1
- package/lib/cloudflare/secret.js +13 -5
- package/lib/cloudflare/secret.js.map +1 -1
- package/lib/cloudflare/secrets-store.d.ts +3 -0
- package/lib/cloudflare/secrets-store.d.ts.map +1 -1
- package/lib/cloudflare/secrets-store.js +3 -3
- package/lib/cloudflare/secrets-store.js.map +1 -1
- package/lib/cloudflare/worker-metadata.d.ts.map +1 -1
- package/lib/cloudflare/worker-metadata.js +11 -0
- package/lib/cloudflare/worker-metadata.js.map +1 -1
- package/lib/cloudflare/worker.d.ts +30 -2
- package/lib/cloudflare/worker.d.ts.map +1 -1
- package/lib/cloudflare/worker.js.map +1 -1
- package/lib/cloudflare/wrangler.json.js +3 -0
- package/lib/cloudflare/wrangler.json.js.map +1 -1
- package/lib/secret.d.ts +5 -5
- package/lib/secret.d.ts.map +1 -1
- package/lib/secret.js.map +1 -1
- package/lib/serde.d.ts +5 -1
- package/lib/serde.d.ts.map +1 -1
- package/lib/serde.js +9 -2
- package/lib/serde.js.map +1 -1
- package/package.json +1 -1
- package/src/cloudflare/bindings.ts +34 -2
- package/src/cloudflare/bound.ts +36 -33
- package/src/cloudflare/index.ts +1 -0
- package/src/cloudflare/queue.ts +24 -0
- package/src/cloudflare/secret-key.ts +143 -0
- package/src/cloudflare/secret.ts +55 -41
- package/src/cloudflare/secrets-store.ts +4 -5
- package/src/cloudflare/worker-metadata.ts +10 -0
- package/src/cloudflare/worker.ts +30 -2
- package/src/cloudflare/wrangler.json.ts +2 -0
- package/src/secret.ts +7 -7
- package/src/serde.ts +40 -20
|
@@ -15,16 +15,17 @@ import type { D1DatabaseResource } from "./d1-database.ts";
|
|
|
15
15
|
import type { DispatchNamespaceResource } from "./dispatch-namespace.ts";
|
|
16
16
|
import type { DurableObjectNamespace } from "./durable-object-namespace.ts";
|
|
17
17
|
import type { HyperdriveResource } from "./hyperdrive.ts";
|
|
18
|
+
import type { Images } from "./images.ts";
|
|
18
19
|
import type { KVNamespaceResource } from "./kv-namespace.ts";
|
|
19
20
|
import type { PipelineResource } from "./pipeline.ts";
|
|
20
21
|
import type { QueueResource } from "./queue.ts";
|
|
22
|
+
import type { SecretKey } from "./secret-key.ts";
|
|
23
|
+
import type { Secret as CloudflareSecret } from "./secret.ts";
|
|
21
24
|
import type { VectorizeIndexResource } from "./vectorize-index.ts";
|
|
22
25
|
import type { VersionMetadata } from "./version-metadata.ts";
|
|
23
26
|
import type { WorkerStub } from "./worker-stub.ts";
|
|
24
27
|
import type { Worker, WorkerRef } from "./worker.ts";
|
|
25
28
|
import type { Workflow } from "./workflow.ts";
|
|
26
|
-
import type { Images } from "./images.ts";
|
|
27
|
-
import type { Secret as CloudflareSecret } from "./secret.ts";
|
|
28
29
|
|
|
29
30
|
export type Bindings = {
|
|
30
31
|
[bindingName: string]: Binding;
|
|
@@ -59,6 +60,7 @@ export type Binding =
|
|
|
59
60
|
id: string;
|
|
60
61
|
}
|
|
61
62
|
| Secret
|
|
63
|
+
| SecretKey
|
|
62
64
|
| string
|
|
63
65
|
| VectorizeIndexResource
|
|
64
66
|
| Worker
|
|
@@ -105,6 +107,7 @@ export type WorkerBindingSpec =
|
|
|
105
107
|
| WorkerBindingPlainText
|
|
106
108
|
| WorkerBindingQueue
|
|
107
109
|
| WorkerBindingR2Bucket
|
|
110
|
+
| WorkerBindingSecretKey
|
|
108
111
|
| WorkerBindingSecretText
|
|
109
112
|
| WorkerBindingSecretsStore
|
|
110
113
|
| WorkerBindingSecretsStoreSecret
|
|
@@ -291,6 +294,35 @@ export interface WorkerBindingR2Bucket {
|
|
|
291
294
|
bucket_name: string;
|
|
292
295
|
}
|
|
293
296
|
|
|
297
|
+
/**
|
|
298
|
+
* Secret Key binding type
|
|
299
|
+
*/
|
|
300
|
+
export interface WorkerBindingSecretKey {
|
|
301
|
+
/** The name of the binding */
|
|
302
|
+
name: string;
|
|
303
|
+
/** Type identifier for Secret Key binding */
|
|
304
|
+
type: "secret_key";
|
|
305
|
+
/** Algorithm-specific key parameters */
|
|
306
|
+
algorithm: unknown;
|
|
307
|
+
/** Data format of the key */
|
|
308
|
+
format: "raw" | "pkcs8" | "spki" | "jwk";
|
|
309
|
+
/** Allowed operations with the key */
|
|
310
|
+
usages: Array<
|
|
311
|
+
| "encrypt"
|
|
312
|
+
| "decrypt"
|
|
313
|
+
| "sign"
|
|
314
|
+
| "verify"
|
|
315
|
+
| "deriveKey"
|
|
316
|
+
| "deriveBits"
|
|
317
|
+
| "wrapKey"
|
|
318
|
+
| "unwrapKey"
|
|
319
|
+
>;
|
|
320
|
+
/** Base64-encoded key data. Required if format is "raw", "pkcs8", or "spki" */
|
|
321
|
+
key_base64?: string;
|
|
322
|
+
/** Key data in JSON Web Key format. Required if format is "jwk" */
|
|
323
|
+
key_jwk?: unknown;
|
|
324
|
+
}
|
|
325
|
+
|
|
294
326
|
/**
|
|
295
327
|
* Secret Text binding type
|
|
296
328
|
*/
|
package/src/cloudflare/bound.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { Pipeline } from "cloudflare:pipelines";
|
|
2
2
|
import type { Secret } from "../secret.ts";
|
|
3
|
-
import type { Secret as CloudflareSecret } from "./secret.ts";
|
|
4
3
|
import type { AiGatewayResource as _AiGateway } from "./ai-gateway.ts";
|
|
5
4
|
import type { Ai as _Ai } from "./ai.ts";
|
|
6
5
|
import type { AnalyticsEngineDataset as _AnalyticsEngineDataset } from "./analytics-engine.ts";
|
|
@@ -15,6 +14,8 @@ import type { HyperdriveResource as _Hyperdrive } from "./hyperdrive.ts";
|
|
|
15
14
|
import type { Images as _Images } from "./images.ts";
|
|
16
15
|
import type { PipelineResource as _Pipeline } from "./pipeline.ts";
|
|
17
16
|
import type { QueueResource as _Queue } from "./queue.ts";
|
|
17
|
+
import type { SecretKey } from "./secret-key.ts";
|
|
18
|
+
import type { Secret as CloudflareSecret } from "./secret.ts";
|
|
18
19
|
import type { VectorizeIndexResource as _VectorizeIndex } from "./vectorize-index.ts";
|
|
19
20
|
import type { VersionMetadata as _VersionMetadata } from "./version-metadata.ts";
|
|
20
21
|
import type { Worker as _Worker, WorkerRef } from "./worker.ts";
|
|
@@ -46,35 +47,37 @@ export type Bound<T extends Binding> = T extends _DurableObjectNamespace<
|
|
|
46
47
|
: T extends Secret
|
|
47
48
|
? string
|
|
48
49
|
: T extends CloudflareSecret
|
|
49
|
-
?
|
|
50
|
-
: T extends
|
|
51
|
-
?
|
|
52
|
-
: T extends
|
|
53
|
-
?
|
|
54
|
-
: T extends
|
|
55
|
-
?
|
|
56
|
-
: T extends
|
|
57
|
-
?
|
|
58
|
-
: T extends
|
|
59
|
-
?
|
|
60
|
-
: T extends
|
|
61
|
-
?
|
|
62
|
-
: T extends
|
|
63
|
-
?
|
|
64
|
-
: T extends
|
|
65
|
-
?
|
|
66
|
-
: T extends
|
|
67
|
-
?
|
|
68
|
-
: T extends
|
|
69
|
-
?
|
|
70
|
-
: T extends
|
|
71
|
-
?
|
|
72
|
-
: T extends
|
|
73
|
-
?
|
|
74
|
-
: T extends
|
|
75
|
-
?
|
|
76
|
-
: T extends
|
|
77
|
-
?
|
|
78
|
-
: T extends
|
|
79
|
-
?
|
|
80
|
-
:
|
|
50
|
+
? SecretsStoreSecret
|
|
51
|
+
: T extends SecretKey
|
|
52
|
+
? CryptoKey
|
|
53
|
+
: T extends Assets
|
|
54
|
+
? Service
|
|
55
|
+
: T extends _Workflow<infer P>
|
|
56
|
+
? Workflow<P>
|
|
57
|
+
: T extends D1DatabaseResource
|
|
58
|
+
? D1Database
|
|
59
|
+
: T extends DispatchNamespaceResource
|
|
60
|
+
? { get(name: string): Fetcher }
|
|
61
|
+
: T extends _VectorizeIndex
|
|
62
|
+
? VectorizeIndex
|
|
63
|
+
: T extends _Queue<infer Body>
|
|
64
|
+
? Queue<Body>
|
|
65
|
+
: T extends _AnalyticsEngineDataset
|
|
66
|
+
? AnalyticsEngineDataset
|
|
67
|
+
: T extends _Pipeline<infer R>
|
|
68
|
+
? Pipeline<R>
|
|
69
|
+
: T extends string
|
|
70
|
+
? string
|
|
71
|
+
: T extends BrowserRendering
|
|
72
|
+
? Fetcher
|
|
73
|
+
: T extends _Ai<infer M>
|
|
74
|
+
? Ai<M>
|
|
75
|
+
: T extends _Images
|
|
76
|
+
? ImagesBinding
|
|
77
|
+
: T extends _VersionMetadata
|
|
78
|
+
? WorkerVersionMetadata
|
|
79
|
+
: T extends Self
|
|
80
|
+
? Service
|
|
81
|
+
: T extends Json<infer T>
|
|
82
|
+
? T
|
|
83
|
+
: Service;
|
package/src/cloudflare/index.ts
CHANGED
|
@@ -39,6 +39,7 @@ export * from "./react-router.ts";
|
|
|
39
39
|
export * from "./redwood.ts";
|
|
40
40
|
export * from "./route.ts";
|
|
41
41
|
export * from "./secret.ts";
|
|
42
|
+
export * from "./secret-key.ts";
|
|
42
43
|
export * from "./secrets-store.ts";
|
|
43
44
|
export * from "./sveltekit.ts";
|
|
44
45
|
export * from "./tanstack-start.ts";
|
package/src/cloudflare/queue.ts
CHANGED
|
@@ -176,6 +176,30 @@ export type Queue<Body = unknown> = QueueResource<Body> &
|
|
|
176
176
|
* dlq: dlqQueue
|
|
177
177
|
* });
|
|
178
178
|
*
|
|
179
|
+
* @example
|
|
180
|
+
* // Create a queue and configure Worker consumer with custom settings
|
|
181
|
+
* const processingQueue = await Queue("processing-queue", {
|
|
182
|
+
* name: "processing-queue"
|
|
183
|
+
* });
|
|
184
|
+
*
|
|
185
|
+
* const processingWorker = await Worker("processor", {
|
|
186
|
+
* entrypoint: "./src/processor.ts",
|
|
187
|
+
* bindings: {
|
|
188
|
+
* QUEUE: processingQueue // Producer: bind queue for sending messages
|
|
189
|
+
* },
|
|
190
|
+
* eventSources: [{ // Consumer: configure processing settings
|
|
191
|
+
* queue: processingQueue,
|
|
192
|
+
* settings: {
|
|
193
|
+
* batchSize: 25, // Process 25 messages at once
|
|
194
|
+
* maxConcurrency: 5, // Allow 5 concurrent invocations
|
|
195
|
+
* maxRetries: 3, // Retry failed messages up to 3 times
|
|
196
|
+
* maxWaitTimeMs: 1500, // Wait up to 1.5 seconds to fill a batch
|
|
197
|
+
* retryDelay: 45, // Wait 45 seconds before retrying failed messages
|
|
198
|
+
* deadLetterQueue: "failed-processing" // Send failed messages to DLQ
|
|
199
|
+
* }
|
|
200
|
+
* }]
|
|
201
|
+
* });
|
|
202
|
+
*
|
|
179
203
|
* @see https://developers.cloudflare.com/queues/
|
|
180
204
|
*/
|
|
181
205
|
export async function Queue<Body = unknown>(
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SecretKey binding for Cloudflare Workers
|
|
3
|
+
*
|
|
4
|
+
* A data class that represents a secret key binding with cryptographic properties.
|
|
5
|
+
* Keys are securely stored and exposed as CryptoKey objects for use with the Web Crypto API.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* // Create an RSA signing key from a JWK format:
|
|
9
|
+
* const keyPair = await crypto.subtle.generateKey(
|
|
10
|
+
* {
|
|
11
|
+
* name: "RSA-PSS",
|
|
12
|
+
* modulusLength: 2048,
|
|
13
|
+
* publicExponent: new Uint8Array([1, 0, 1]),
|
|
14
|
+
* hash: "SHA-256",
|
|
15
|
+
* },
|
|
16
|
+
* true,
|
|
17
|
+
* ["sign", "verify"]
|
|
18
|
+
* );
|
|
19
|
+
*
|
|
20
|
+
* const privateKeyJwk = await crypto.subtle.exportKey("jwk", keyPair.privateKey);
|
|
21
|
+
*
|
|
22
|
+
* const signingKey = new SecretKey({
|
|
23
|
+
* algorithm: { name: "RSA-PSS", hash: "SHA-256" },
|
|
24
|
+
* format: "jwk",
|
|
25
|
+
* usages: ["sign"],
|
|
26
|
+
* key_jwk: secret(privateKeyJwk),
|
|
27
|
+
* });
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* // Create an AES encryption key from raw bytes:
|
|
31
|
+
* const aesKey = await crypto.subtle.generateKey(
|
|
32
|
+
* { name: "AES-GCM", length: 256 },
|
|
33
|
+
* true,
|
|
34
|
+
* ["encrypt", "decrypt"]
|
|
35
|
+
* );
|
|
36
|
+
*
|
|
37
|
+
* const keyData = await crypto.subtle.exportKey("raw", aesKey);
|
|
38
|
+
* const keyBase64 = btoa(String.fromCharCode(...new Uint8Array(keyData)));
|
|
39
|
+
*
|
|
40
|
+
* const encryptionKey = new SecretKey({
|
|
41
|
+
* algorithm: { name: "AES-GCM" },
|
|
42
|
+
* format: "raw",
|
|
43
|
+
* usages: ["encrypt", "decrypt"],
|
|
44
|
+
* key_base64: secret(keyBase64),
|
|
45
|
+
* });
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* // Create an ECDSA key from PKCS#8 format:
|
|
49
|
+
* const ecdsaKey = new SecretKey({
|
|
50
|
+
* algorithm: { name: "ECDSA", namedCurve: "P-256" },
|
|
51
|
+
* format: "pkcs8",
|
|
52
|
+
* usages: ["sign"],
|
|
53
|
+
* key_base64: secret("MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg..."),
|
|
54
|
+
* });
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* // Create an HMAC key for message authentication:
|
|
58
|
+
* const hmacKey = await crypto.subtle.generateKey(
|
|
59
|
+
* { name: "HMAC", hash: "SHA-256" },
|
|
60
|
+
* true,
|
|
61
|
+
* ["sign", "verify"]
|
|
62
|
+
* );
|
|
63
|
+
*
|
|
64
|
+
* const keyData = await crypto.subtle.exportKey("raw", hmacKey);
|
|
65
|
+
* const keyBase64 = btoa(String.fromCharCode(...new Uint8Array(keyData)));
|
|
66
|
+
*
|
|
67
|
+
* const macKey = new SecretKey({
|
|
68
|
+
* algorithm: { name: "HMAC", hash: "SHA-256" },
|
|
69
|
+
* format: "raw",
|
|
70
|
+
* usages: ["sign", "verify"],
|
|
71
|
+
* key_base64: secret(keyBase64),
|
|
72
|
+
* });
|
|
73
|
+
*
|
|
74
|
+
* @see https://developers.cloudflare.com/api/resources/workers/subresources/scripts/methods/update/
|
|
75
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/importKey#keyusages
|
|
76
|
+
*/
|
|
77
|
+
|
|
78
|
+
import type { Secret } from "../secret.ts";
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Data format of the key
|
|
82
|
+
*/
|
|
83
|
+
export type SecretKeyFormat = "raw" | "pkcs8" | "spki" | "jwk";
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Allowed operations with the key
|
|
87
|
+
*/
|
|
88
|
+
export type SecretKeyUsage =
|
|
89
|
+
| "encrypt"
|
|
90
|
+
| "decrypt"
|
|
91
|
+
| "sign"
|
|
92
|
+
| "verify"
|
|
93
|
+
| "deriveKey"
|
|
94
|
+
| "deriveBits"
|
|
95
|
+
| "wrapKey"
|
|
96
|
+
| "unwrapKey";
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* SecretKey binding properties
|
|
100
|
+
*/
|
|
101
|
+
export interface SecretKeyProps {
|
|
102
|
+
/**
|
|
103
|
+
* Algorithm-specific key parameters
|
|
104
|
+
*/
|
|
105
|
+
algorithm: unknown;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Data format of the key
|
|
109
|
+
*/
|
|
110
|
+
format: SecretKeyFormat;
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Allowed operations with the key
|
|
114
|
+
*/
|
|
115
|
+
usages: SecretKeyUsage[];
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Base64-encoded key data. Required if format is "raw", "pkcs8", or "spki"
|
|
119
|
+
*/
|
|
120
|
+
key_base64?: Secret;
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Key data in JSON Web Key format. Required if format is "jwk"
|
|
124
|
+
*/
|
|
125
|
+
key_jwk?: Secret<JsonWebKey>;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export class SecretKey {
|
|
129
|
+
readonly type = "secret_key";
|
|
130
|
+
readonly algorithm: unknown;
|
|
131
|
+
readonly format: SecretKeyFormat;
|
|
132
|
+
readonly usages: SecretKeyUsage[];
|
|
133
|
+
readonly key_base64?: Secret;
|
|
134
|
+
readonly key_jwk?: Secret<JsonWebKey>;
|
|
135
|
+
|
|
136
|
+
constructor(props: SecretKeyProps) {
|
|
137
|
+
this.algorithm = props.algorithm;
|
|
138
|
+
this.format = props.format;
|
|
139
|
+
this.usages = props.usages;
|
|
140
|
+
this.key_base64 = props.key_base64;
|
|
141
|
+
this.key_jwk = props.key_jwk;
|
|
142
|
+
}
|
|
143
|
+
}
|
package/src/cloudflare/secret.ts
CHANGED
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
type CloudflareApi,
|
|
11
11
|
type CloudflareApiOptions,
|
|
12
12
|
} from "./api.ts";
|
|
13
|
-
import
|
|
13
|
+
import { findSecretsStoreByName, SecretsStore } from "./secrets-store.ts";
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* Properties for creating or updating a Secret in a Secrets Store (internal interface)
|
|
@@ -18,8 +18,10 @@ import type { SecretsStore } from "./secrets-store.ts";
|
|
|
18
18
|
interface _SecretProps extends CloudflareApiOptions {
|
|
19
19
|
/**
|
|
20
20
|
* The secrets store to add this secret to
|
|
21
|
+
*
|
|
22
|
+
* @default - the default-secret-store
|
|
21
23
|
*/
|
|
22
|
-
store
|
|
24
|
+
store?: SecretsStore<any>;
|
|
23
25
|
|
|
24
26
|
/**
|
|
25
27
|
* The secret value to store (must be an AlchemySecret for security)
|
|
@@ -41,8 +43,10 @@ interface _SecretProps extends CloudflareApiOptions {
|
|
|
41
43
|
export interface SecretProps extends CloudflareApiOptions {
|
|
42
44
|
/**
|
|
43
45
|
* The secrets store to add this secret to
|
|
46
|
+
*
|
|
47
|
+
* @default - the default-secret-store
|
|
44
48
|
*/
|
|
45
|
-
store
|
|
49
|
+
store?: SecretsStore<any>;
|
|
46
50
|
|
|
47
51
|
/**
|
|
48
52
|
* The secret value to store
|
|
@@ -71,39 +75,38 @@ export function isSecret(resource: Resource): resource is Secret {
|
|
|
71
75
|
return resource[ResourceKind] === "cloudflare::Secret";
|
|
72
76
|
}
|
|
73
77
|
|
|
74
|
-
export
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
}
|
|
78
|
+
export type Secret = Resource<"cloudflare::Secret"> &
|
|
79
|
+
Omit<_SecretProps, "delete"> & {
|
|
80
|
+
/**
|
|
81
|
+
* The binding type for Cloudflare Workers
|
|
82
|
+
*/
|
|
83
|
+
type: "secrets_store_secret";
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* The name of the secret
|
|
87
|
+
*/
|
|
88
|
+
name: string;
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* The unique identifier of the secrets store this secret belongs to
|
|
92
|
+
*/
|
|
93
|
+
storeId: string;
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* The secret value (as an alchemy Secret instance)
|
|
97
|
+
*/
|
|
98
|
+
value: AlchemySecret;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Timestamp when the secret was created
|
|
102
|
+
*/
|
|
103
|
+
createdAt: number;
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Timestamp when the secret was last modified
|
|
107
|
+
*/
|
|
108
|
+
modifiedAt: number;
|
|
109
|
+
};
|
|
107
110
|
|
|
108
111
|
/**
|
|
109
112
|
* A Cloudflare Secret represents an individual secret stored in a Secrets Store.
|
|
@@ -179,9 +182,20 @@ const _Secret = Resource(
|
|
|
179
182
|
): Promise<Secret> {
|
|
180
183
|
const api = await createCloudflareApi(props);
|
|
181
184
|
|
|
185
|
+
const storeId =
|
|
186
|
+
props.store?.id ??
|
|
187
|
+
(await findSecretsStoreByName(api, SecretsStore.Default))?.id ??
|
|
188
|
+
(
|
|
189
|
+
await SecretsStore("default-store", {
|
|
190
|
+
name: SecretsStore.Default,
|
|
191
|
+
adopt: true,
|
|
192
|
+
delete: false,
|
|
193
|
+
})
|
|
194
|
+
)?.id!;
|
|
195
|
+
|
|
182
196
|
if (this.phase === "delete") {
|
|
183
197
|
if (props.delete !== false) {
|
|
184
|
-
await deleteSecret(api,
|
|
198
|
+
await deleteSecret(api, storeId, name);
|
|
185
199
|
}
|
|
186
200
|
return this.destroy();
|
|
187
201
|
}
|
|
@@ -192,12 +206,12 @@ const _Secret = Resource(
|
|
|
192
206
|
: Date.now();
|
|
193
207
|
|
|
194
208
|
// Insert or update the secret
|
|
195
|
-
await insertSecret(api,
|
|
209
|
+
await insertSecret(api, storeId, name, props.value);
|
|
196
210
|
|
|
197
211
|
return this({
|
|
198
212
|
type: "secrets_store_secret",
|
|
199
213
|
name,
|
|
200
|
-
storeId
|
|
214
|
+
storeId,
|
|
201
215
|
store: props.store,
|
|
202
216
|
value: props.value,
|
|
203
217
|
createdAt,
|
|
@@ -317,10 +331,10 @@ export async function deleteSecret(
|
|
|
317
331
|
storeId: string,
|
|
318
332
|
secretName: string,
|
|
319
333
|
): Promise<void> {
|
|
334
|
+
const secretId = await getSecretId(api, storeId, secretName);
|
|
320
335
|
const response = await api.delete(
|
|
321
|
-
`/accounts/${api.accountId}/secrets_store/stores/${storeId}/secrets`,
|
|
336
|
+
`/accounts/${api.accountId}/secrets_store/stores/${storeId}/secrets/${secretId}`,
|
|
322
337
|
{
|
|
323
|
-
body: JSON.stringify([secretName]),
|
|
324
338
|
headers: {
|
|
325
339
|
"Content-Type": "application/json",
|
|
326
340
|
},
|
|
@@ -168,6 +168,10 @@ export async function SecretsStore<
|
|
|
168
168
|
return _SecretsStore(name, normalizedProps);
|
|
169
169
|
}
|
|
170
170
|
|
|
171
|
+
export namespace SecretsStore {
|
|
172
|
+
export const Default = "default_secrets_store";
|
|
173
|
+
}
|
|
174
|
+
|
|
171
175
|
const _SecretsStore = Resource("cloudflare::SecretsStore", async function <
|
|
172
176
|
S extends Record<string, Secret> | undefined = undefined,
|
|
173
177
|
>(this: Context<SecretsStore<S>>, id: string, props: SecretsStoreProps<S>): Promise<
|
|
@@ -206,17 +210,12 @@ const _SecretsStore = Resource("cloudflare::SecretsStore", async function <
|
|
|
206
210
|
} else {
|
|
207
211
|
// If adopt is true, first check if a store with this name already exists
|
|
208
212
|
if (props.adopt) {
|
|
209
|
-
console.log(`Checking for existing secrets store '${name}' to adopt`);
|
|
210
213
|
const existingStore = await findSecretsStoreByName(api, name);
|
|
211
214
|
|
|
212
215
|
if (existingStore) {
|
|
213
|
-
console.log(`Found existing secrets store '${name}', adopting it`);
|
|
214
216
|
storeId = existingStore.id;
|
|
215
217
|
createdAt = existingStore.createdAt || Date.now();
|
|
216
218
|
} else {
|
|
217
|
-
console.log(
|
|
218
|
-
`No existing secrets store '${name}' found, creating new one`,
|
|
219
|
-
);
|
|
220
219
|
const { id } = await createSecretsStore(api, {
|
|
221
220
|
...props,
|
|
222
221
|
name,
|
|
@@ -484,6 +484,16 @@ export async function prepareWorkerMetadata<B extends Bindings>(
|
|
|
484
484
|
name: bindingName,
|
|
485
485
|
namespace: binding.namespaceName,
|
|
486
486
|
});
|
|
487
|
+
} else if (binding.type === "secret_key") {
|
|
488
|
+
meta.bindings.push({
|
|
489
|
+
type: "secret_key",
|
|
490
|
+
name: bindingName,
|
|
491
|
+
algorithm: binding.algorithm,
|
|
492
|
+
format: binding.format,
|
|
493
|
+
usages: binding.usages,
|
|
494
|
+
key_base64: binding.key_base64?.unencrypted,
|
|
495
|
+
key_jwk: binding.key_jwk?.unencrypted,
|
|
496
|
+
});
|
|
487
497
|
} else {
|
|
488
498
|
// @ts-expect-error - we should never reach here
|
|
489
499
|
throw new Error(`Unsupported binding type: ${binding.type}`);
|
package/src/cloudflare/worker.ts
CHANGED
|
@@ -599,8 +599,36 @@ export function WorkerRef<
|
|
|
599
599
|
* }
|
|
600
600
|
* });
|
|
601
601
|
*
|
|
602
|
-
* @
|
|
603
|
-
*
|
|
602
|
+
* @example
|
|
603
|
+
* // Create a worker with queue event sources and custom consumer settings:
|
|
604
|
+
* const taskQueue = await Queue("task-queue", {
|
|
605
|
+
* name: "task-queue"
|
|
606
|
+
* });
|
|
607
|
+
*
|
|
608
|
+
* const dlq = await Queue("failed-tasks", {
|
|
609
|
+
* name: "failed-tasks"
|
|
610
|
+
* });
|
|
611
|
+
*
|
|
612
|
+
* const queueWorker = await Worker("queue-processor", {
|
|
613
|
+
* name: "queue-processor",
|
|
614
|
+
* entrypoint: "./src/processor.ts",
|
|
615
|
+
* bindings: {
|
|
616
|
+
* TASK_QUEUE: taskQueue // Producer: bind queue for sending messages
|
|
617
|
+
* },
|
|
618
|
+
* eventSources: [{ // Consumer: configure processing settings
|
|
619
|
+
* queue: taskQueue,
|
|
620
|
+
* settings: {
|
|
621
|
+
* batchSize: 15, // Process 15 messages at once
|
|
622
|
+
* maxConcurrency: 3, // Allow 3 concurrent invocations
|
|
623
|
+
* maxRetries: 5, // Retry failed messages up to 5 times
|
|
624
|
+
* maxWaitTimeMs: 2500, // Wait up to 2.5 seconds to fill a batch
|
|
625
|
+
* retryDelay: 60, // Wait 60 seconds before retrying failed messages
|
|
626
|
+
* deadLetterQueue: dlq // Send failed messages to dead letter queue
|
|
627
|
+
* }
|
|
628
|
+
* }]
|
|
629
|
+
* });
|
|
630
|
+
*
|
|
631
|
+
* @see https://developers.cloudflare.com/workers/
|
|
604
632
|
*/
|
|
605
633
|
export function Worker<
|
|
606
634
|
const B extends Bindings,
|
|
@@ -607,6 +607,8 @@ function processBindings(
|
|
|
607
607
|
binding: bindingName,
|
|
608
608
|
namespace: binding.namespaceName,
|
|
609
609
|
});
|
|
610
|
+
} else if (binding.type === "secret_key") {
|
|
611
|
+
// no-op
|
|
610
612
|
} else {
|
|
611
613
|
// biome-ignore lint/correctness/noVoidTypeReturn: it returns never
|
|
612
614
|
return assertNever(binding);
|
package/src/secret.ts
CHANGED
|
@@ -8,7 +8,7 @@ declare global {
|
|
|
8
8
|
|
|
9
9
|
// a global registry of all secrets that we will use when serializing an application
|
|
10
10
|
const globalSecrets: {
|
|
11
|
-
[name: string]: Secret
|
|
11
|
+
[name: string]: Secret<any>;
|
|
12
12
|
} = (globalThis.__ALCHEMY_SECRETS__ ??= {});
|
|
13
13
|
|
|
14
14
|
let i = 0;
|
|
@@ -53,19 +53,19 @@ const SecretSymbol = Symbol.for("alchemy::Secret");
|
|
|
53
53
|
* }
|
|
54
54
|
* }
|
|
55
55
|
*/
|
|
56
|
-
export class Secret {
|
|
56
|
+
export class Secret<T = string> {
|
|
57
57
|
[SecretSymbol] = true;
|
|
58
58
|
|
|
59
59
|
/**
|
|
60
60
|
* @internal
|
|
61
61
|
*/
|
|
62
|
-
public static all(): Secret[] {
|
|
62
|
+
public static all(): Secret<any>[] {
|
|
63
63
|
return Object.values(globalSecrets);
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
public readonly type = "secret";
|
|
67
67
|
constructor(
|
|
68
|
-
readonly unencrypted:
|
|
68
|
+
readonly unencrypted: T,
|
|
69
69
|
readonly name: string = nextName(),
|
|
70
70
|
) {
|
|
71
71
|
globalSecrets[name] = this;
|
|
@@ -111,10 +111,10 @@ export function isSecret(binding: any): binding is Secret {
|
|
|
111
111
|
* @throws {Error} If the value is undefined
|
|
112
112
|
* @throws {Error} If no password is set in the alchemy application options or current scope
|
|
113
113
|
*/
|
|
114
|
-
export function secret<S
|
|
115
|
-
unencrypted: S,
|
|
114
|
+
export function secret<S = string>(
|
|
115
|
+
unencrypted: S | undefined,
|
|
116
116
|
name?: string,
|
|
117
|
-
): Secret {
|
|
117
|
+
): Secret<S> {
|
|
118
118
|
if (unencrypted === undefined) {
|
|
119
119
|
throw new Error("Secret cannot be undefined");
|
|
120
120
|
}
|