alchemy 0.8.0 → 0.8.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.
@@ -39,7 +39,7 @@ export class CloudflareApiError extends Error {
39
39
  */
40
40
  export async function handleApiError(response, action, resourceType, resourceName) {
41
41
  const json = await response.json();
42
- const errors = json.errors || [
42
+ const errors = json?.errors || [
43
43
  { message: response.statusText },
44
44
  ];
45
45
  const errorMessage = `Error ${action} ${resourceType} '${resourceName}': ${errors[0]?.message || response.statusText}`;
@@ -11,6 +11,7 @@ import type { DurableObjectNamespace } from "./durable-object-namespace";
11
11
  import type { KVNamespace } from "./kv-namespace";
12
12
  import type { Pipeline } from "./pipeline";
13
13
  import type { Queue } from "./queue";
14
+ import type { VectorizeIndex } from "./vectorize-index";
14
15
  import type { Worker } from "./worker";
15
16
  import type { Workflow } from "./workflow";
16
17
  export type Bindings = {
@@ -19,7 +20,7 @@ export type Bindings = {
19
20
  /**
20
21
  * L2 Binding Resources.
21
22
  */
22
- export type Binding = Assets | D1Database | DurableObjectNamespace | KVNamespace | Pipeline | Queue | R2Bucket | Secret | string | Worker | Workflow;
23
+ export type Binding = Assets | D1Database | DurableObjectNamespace | KVNamespace | Pipeline | Queue | R2Bucket | Secret | string | VectorizeIndex | Worker | Workflow;
23
24
  /**
24
25
  * Union type for all Worker binding types (API spec)
25
26
  */
@@ -8,6 +8,7 @@ import type { DurableObjectNamespace as _DurableObjectNamespace } from "./durabl
8
8
  import type { KVNamespace as _KVNamespace } from "./kv-namespace";
9
9
  import type { Pipeline as _Pipeline } from "./pipeline";
10
10
  import type { Queue as _Queue } from "./queue";
11
+ import type { VectorizeIndex as _VectorizeIndex } from "./vectorize-index";
11
12
  import type { Worker as _Worker } from "./worker";
12
13
  import type { Workflow as _Workflow } from "./workflow";
13
- export type Bound<T extends Binding> = T extends _DurableObjectNamespace ? DurableObjectNamespace : T extends _KVNamespace ? KVNamespace : T extends _Worker ? Worker : T extends _R2Bucket ? R2Bucket : T extends Secret ? string : T extends Assets ? Service : T extends _Workflow<infer P> ? Workflow<P> : T extends _D1Database ? D1Database : T extends _Queue ? Queue : T extends _Pipeline<infer R> ? Pipeline<R> : Service;
14
+ export type Bound<T extends Binding> = T extends _DurableObjectNamespace ? DurableObjectNamespace : T extends _KVNamespace ? KVNamespace : T extends _Worker ? Worker : T extends _R2Bucket ? R2Bucket : T extends Secret ? string : T extends Assets ? Service : T extends _Workflow<infer P> ? Workflow<P> : T extends _D1Database ? D1Database : T extends _VectorizeIndex ? VectorizeIndex : T extends _Queue ? Queue : T extends _Pipeline<infer R> ? Pipeline<R> : Service;
@@ -13,6 +13,8 @@ export * from "./permission-groups";
13
13
  export * from "./pipeline";
14
14
  export * from "./queue";
15
15
  export * from "./r2-rest-state-store";
16
+ export * from "./vectorize-index";
17
+ export * from "./vectorize-metadata-index";
16
18
  export * from "./worker";
17
19
  export { Workflow } from "./workflow";
18
20
  export * from "./wrangler.json";
@@ -13,6 +13,8 @@ export * from "./permission-groups";
13
13
  export * from "./pipeline";
14
14
  export * from "./queue";
15
15
  export * from "./r2-rest-state-store";
16
+ export * from "./vectorize-index";
17
+ export * from "./vectorize-metadata-index";
16
18
  export * from "./worker";
17
19
  export { Workflow } from "./workflow";
18
20
  export * from "./wrangler.json";
@@ -0,0 +1,136 @@
1
+ import type { Context } from "../context";
2
+ import { Resource } from "../resource";
3
+ import { CloudflareApi, type CloudflareApiOptions } from "./api";
4
+ /**
5
+ * Properties for creating or updating a Vectorize Index
6
+ */
7
+ export interface VectorizeIndexProps extends CloudflareApiOptions {
8
+ /**
9
+ * Name of the index
10
+ */
11
+ name: string;
12
+ /**
13
+ * Optional description of the index
14
+ */
15
+ description?: string;
16
+ /**
17
+ * Dimensions of the vectors
18
+ */
19
+ dimensions: number;
20
+ /**
21
+ * Distance metric used for vector similarity
22
+ */
23
+ metric: "cosine" | "euclidean" | "dot_product";
24
+ /**
25
+ * Whether to delete the index if removed
26
+ * If set to false, the index will remain but the resource will be removed from state
27
+ *
28
+ * @default true
29
+ */
30
+ delete?: boolean;
31
+ /**
32
+ * Whether to adopt an existing index with the same name if it exists
33
+ * If true and an index with the same name exists, it will be adopted rather than creating a new one
34
+ *
35
+ * @default false
36
+ */
37
+ adopt?: boolean;
38
+ }
39
+ /**
40
+ * Output returned after Vectorize Index creation/update
41
+ */
42
+ export interface VectorizeIndex extends Resource<"cloudflare::VectorizeIndex">, VectorizeIndexProps {
43
+ type: "vectorize";
44
+ /**
45
+ * The unique identifier for the index (same as name)
46
+ */
47
+ id: string;
48
+ /**
49
+ * Time at which the index was created
50
+ */
51
+ createdAt?: number;
52
+ }
53
+ /**
54
+ * Creates and manages Cloudflare Vectorize Indexes.
55
+ *
56
+ * Vectorize is Cloudflare's vector database that enables vector search within Cloudflare Workers.
57
+ *
58
+ * @example
59
+ * // Create a basic vector index for text embeddings
60
+ * const basicIndex = await VectorizeIndex("text-embeddings", {
61
+ * name: "text-embeddings",
62
+ * config: {
63
+ * dimensions: 768,
64
+ * metric: "cosine"
65
+ * }
66
+ * });
67
+ *
68
+ * @example
69
+ * // Create a vector index with a description
70
+ * const descIndex = await VectorizeIndex("image-embeddings", {
71
+ * name: "image-embeddings",
72
+ * description: "Vector index for image embeddings using CLIP model",
73
+ * config: {
74
+ * dimensions: 512,
75
+ * metric: "cosine"
76
+ * }
77
+ * });
78
+ *
79
+ * @example
80
+ * // Adopt an existing index if it already exists instead of failing
81
+ * const existingIndex = await VectorizeIndex("existing-index", {
82
+ * name: "existing-index",
83
+ * adopt: true,
84
+ * config: {
85
+ * dimensions: 1024,
86
+ * metric: "euclidean"
87
+ * }
88
+ * });
89
+ *
90
+ * @see https://developers.cloudflare.com/vectorize/
91
+ */
92
+ export declare const VectorizeIndex: (((this: any, id: string, props?: {}) => never) & (new (_: never) => never)) | ((this: Context<VectorizeIndex>, id: string, props: VectorizeIndexProps) => Promise<VectorizeIndex>);
93
+ interface CloudflareVectorizeResponse {
94
+ result: {
95
+ name: string;
96
+ description?: string;
97
+ created_on?: string;
98
+ config: {
99
+ dimensions: number;
100
+ metric: string;
101
+ };
102
+ };
103
+ success: boolean;
104
+ errors: Array<{
105
+ code: number;
106
+ message: string;
107
+ }>;
108
+ messages: string[];
109
+ }
110
+ /**
111
+ * Create a new Vectorize index
112
+ */
113
+ export declare function createIndex(api: CloudflareApi, indexName: string, props: VectorizeIndexProps): Promise<CloudflareVectorizeResponse>;
114
+ /**
115
+ * Get a Vectorize index
116
+ */
117
+ export declare function getIndex(api: CloudflareApi, indexName: string): Promise<CloudflareVectorizeResponse>;
118
+ /**
119
+ * Delete a Vectorize index
120
+ */
121
+ export declare function deleteIndex(api: CloudflareApi, indexName: string): Promise<void>;
122
+ /**
123
+ * List all Vectorize indexes in an account
124
+ */
125
+ export declare function listIndexes(api: CloudflareApi): Promise<{
126
+ name: string;
127
+ description?: string;
128
+ }[]>;
129
+ /**
130
+ * Update a Vectorize index
131
+ *
132
+ * Note: The Cloudflare Vectorize API does not support updating indexes.
133
+ * This function will always throw an error.
134
+ */
135
+ export declare function updateIndex(api: CloudflareApi, indexName: string, props: VectorizeIndexProps): Promise<CloudflareVectorizeResponse>;
136
+ export {};
@@ -0,0 +1,170 @@
1
+ import { Resource } from "../resource";
2
+ import { CloudflareApi, createCloudflareApi, } from "./api";
3
+ import { CloudflareApiError, handleApiError } from "./api-error";
4
+ /**
5
+ * Creates and manages Cloudflare Vectorize Indexes.
6
+ *
7
+ * Vectorize is Cloudflare's vector database that enables vector search within Cloudflare Workers.
8
+ *
9
+ * @example
10
+ * // Create a basic vector index for text embeddings
11
+ * const basicIndex = await VectorizeIndex("text-embeddings", {
12
+ * name: "text-embeddings",
13
+ * config: {
14
+ * dimensions: 768,
15
+ * metric: "cosine"
16
+ * }
17
+ * });
18
+ *
19
+ * @example
20
+ * // Create a vector index with a description
21
+ * const descIndex = await VectorizeIndex("image-embeddings", {
22
+ * name: "image-embeddings",
23
+ * description: "Vector index for image embeddings using CLIP model",
24
+ * config: {
25
+ * dimensions: 512,
26
+ * metric: "cosine"
27
+ * }
28
+ * });
29
+ *
30
+ * @example
31
+ * // Adopt an existing index if it already exists instead of failing
32
+ * const existingIndex = await VectorizeIndex("existing-index", {
33
+ * name: "existing-index",
34
+ * adopt: true,
35
+ * config: {
36
+ * dimensions: 1024,
37
+ * metric: "euclidean"
38
+ * }
39
+ * });
40
+ *
41
+ * @see https://developers.cloudflare.com/vectorize/
42
+ */
43
+ export const VectorizeIndex = Resource("cloudflare::VectorizeIndex", async function (id, props) {
44
+ const api = await createCloudflareApi(props);
45
+ const indexName = props.name || id;
46
+ if (this.phase === "delete") {
47
+ console.log("Deleting Vectorize index:", indexName);
48
+ if (props.delete !== false) {
49
+ // Delete Vectorize index
50
+ await deleteIndex(api, indexName);
51
+ }
52
+ // Return void (a deleted index has no content)
53
+ return this.destroy();
54
+ }
55
+ else {
56
+ let indexData;
57
+ if (this.phase === "create") {
58
+ console.log("Creating Vectorize index:", indexName);
59
+ try {
60
+ indexData = await createIndex(api, indexName, {
61
+ ...props,
62
+ name: indexName,
63
+ });
64
+ }
65
+ catch (error) {
66
+ // Check if this is a "index already exists" error and adopt is enabled
67
+ if (props.adopt &&
68
+ error instanceof CloudflareApiError &&
69
+ error.message.includes("already exists")) {
70
+ console.log(`Index ${indexName} already exists, adopting it`);
71
+ // Find the existing index
72
+ indexData = await getIndex(api, indexName);
73
+ }
74
+ else {
75
+ // Re-throw the error if adopt is false or it's not a "index already exists" error
76
+ throw error;
77
+ }
78
+ }
79
+ }
80
+ else {
81
+ // Update operation is not supported by Vectorize API
82
+ throw new Error("Updating Vectorize indexes is not supported by the Cloudflare API. " +
83
+ "To change an index, delete it and create a new one with the desired configuration.");
84
+ }
85
+ return this({
86
+ type: "vectorize",
87
+ id: indexName,
88
+ name: indexName,
89
+ description: props.description,
90
+ dimensions: indexData.result.config.dimensions,
91
+ metric: indexData.result.config.metric,
92
+ accountId: api.accountId,
93
+ createdAt: indexData.result.created_on
94
+ ? new Date(indexData.result.created_on).getTime()
95
+ : undefined,
96
+ });
97
+ }
98
+ });
99
+ /**
100
+ * Create a new Vectorize index
101
+ */
102
+ export async function createIndex(api, indexName, props) {
103
+ // Create new Vectorize index
104
+ const createPayload = {
105
+ name: indexName,
106
+ config: {
107
+ dimensions: props.dimensions,
108
+ metric: props.metric,
109
+ },
110
+ };
111
+ if (props.description) {
112
+ createPayload.description = props.description;
113
+ }
114
+ const createResponse = await api.post(`/accounts/${api.accountId}/vectorize/v2/indexes`, createPayload);
115
+ if (!createResponse.ok) {
116
+ return await handleApiError(createResponse, "creating", "Vectorize index", indexName);
117
+ }
118
+ return (await createResponse.json());
119
+ }
120
+ /**
121
+ * Get a Vectorize index
122
+ */
123
+ export async function getIndex(api, indexName) {
124
+ const response = await api.get(`/accounts/${api.accountId}/vectorize/v2/indexes/${indexName}`);
125
+ if (!response.ok) {
126
+ return await handleApiError(response, "getting", "Vectorize index", indexName);
127
+ }
128
+ return (await response.json());
129
+ }
130
+ /**
131
+ * Delete a Vectorize index
132
+ */
133
+ export async function deleteIndex(api, indexName) {
134
+ // Delete Vectorize index
135
+ const deleteResponse = await api.delete(`/accounts/${api.accountId}/vectorize/v2/indexes/${indexName}`);
136
+ if (!deleteResponse.ok && deleteResponse.status !== 404) {
137
+ const errorData = await deleteResponse.json().catch(() => ({
138
+ errors: [{ message: deleteResponse.statusText }],
139
+ }));
140
+ throw new CloudflareApiError(`Error deleting Vectorize index '${indexName}': ${errorData.errors?.[0]?.message || deleteResponse.statusText}`, deleteResponse);
141
+ }
142
+ }
143
+ /**
144
+ * List all Vectorize indexes in an account
145
+ */
146
+ export async function listIndexes(api) {
147
+ const response = await api.get(`/accounts/${api.accountId}/vectorize/v2/indexes`);
148
+ if (!response.ok) {
149
+ throw new CloudflareApiError(`Failed to list indexes: ${response.statusText}`, response);
150
+ }
151
+ const data = (await response.json());
152
+ if (!data.success) {
153
+ const errorMessage = data.errors?.[0]?.message || "Unknown error";
154
+ throw new Error(`Failed to list indexes: ${errorMessage}`);
155
+ }
156
+ // Transform API response
157
+ return (data.result || []).map((index) => ({
158
+ name: index.name,
159
+ description: index.description,
160
+ }));
161
+ }
162
+ /**
163
+ * Update a Vectorize index
164
+ *
165
+ * Note: The Cloudflare Vectorize API does not support updating indexes.
166
+ * This function will always throw an error.
167
+ */
168
+ export async function updateIndex(api, indexName, props) {
169
+ throw new Error("Updating Vectorize indexes is not supported by the Cloudflare API. To change an index, delete it and create a new one.");
170
+ }
@@ -0,0 +1,95 @@
1
+ import type { Context } from "../context";
2
+ import { Resource } from "../resource";
3
+ import { CloudflareApi, type CloudflareApiOptions } from "./api";
4
+ import { VectorizeIndex } from "./vectorize-index";
5
+ /**
6
+ * Properties for creating or deleting a Vectorize Metadata Index
7
+ */
8
+ export interface VectorizeMetadataIndexProps extends CloudflareApiOptions {
9
+ /**
10
+ * Parent Vectorize Index
11
+ */
12
+ index: VectorizeIndex;
13
+ /**
14
+ * Name of the property in the metadata to create an index for
15
+ */
16
+ propertyName: string;
17
+ /**
18
+ * Type of the metadata index
19
+ */
20
+ indexType: "string" | "number" | "boolean";
21
+ }
22
+ /**
23
+ * Output returned after Vectorize Metadata Index creation/deletion
24
+ */
25
+ export interface VectorizeMetadataIndex extends Resource<"cloudflare::VectorizeMetadataIndex">, VectorizeMetadataIndexProps {
26
+ /**
27
+ * ID of this metadata index (derived from propertyName)
28
+ */
29
+ id: string;
30
+ /**
31
+ * Mutation ID returned by the API after creation
32
+ */
33
+ mutationId?: string;
34
+ }
35
+ /**
36
+ * Creates and manages Cloudflare Vectorize Metadata Indexes.
37
+ *
38
+ * Vectorize Metadata Indexes enable filtering based on metadata properties when querying vectors.
39
+ * Each Vectorize Index can have up to 10 metadata indexes.
40
+ *
41
+ * @example
42
+ * // First create a Vectorize Index
43
+ * const vectorIndex = await VectorizeIndex("documents", {
44
+ * name: "documents",
45
+ * config: {
46
+ * dimensions: 768,
47
+ * metric: "cosine"
48
+ * }
49
+ * });
50
+ *
51
+ * // Then create a metadata index for the "category" property
52
+ * const categoryIndex = await VectorizeMetadataIndex("category-index", {
53
+ * index: vectorIndex,
54
+ * propertyName: "category",
55
+ * indexType: "string"
56
+ * });
57
+ *
58
+ * @example
59
+ * // Create a metadata index for a numeric property
60
+ * const yearIndex = await VectorizeMetadataIndex("year-index", {
61
+ * index: vectorIndex,
62
+ * propertyName: "year",
63
+ * indexType: "number"
64
+ * });
65
+ *
66
+ * @see https://developers.cloudflare.com/vectorize/
67
+ */
68
+ export declare const VectorizeMetadataIndex: (((this: any, id: string, props?: {}) => never) & (new (_: never) => never)) | ((this: Context<VectorizeMetadataIndex>, id: string, props: VectorizeMetadataIndexProps) => Promise<VectorizeMetadataIndex>);
69
+ interface CloudflareMetadataIndexResponse {
70
+ result: {
71
+ mutationId: string;
72
+ };
73
+ success: boolean;
74
+ errors: Array<{
75
+ code: number;
76
+ message: string;
77
+ }>;
78
+ messages: string[];
79
+ }
80
+ /**
81
+ * Create a new Vectorize metadata index
82
+ */
83
+ export declare function createMetadataIndex(api: CloudflareApi, indexName: string, props: VectorizeMetadataIndexProps): Promise<CloudflareMetadataIndexResponse>;
84
+ /**
85
+ * Delete a Vectorize metadata index
86
+ */
87
+ export declare function deleteMetadataIndex(api: CloudflareApi, indexName: string, propertyName: string): Promise<void>;
88
+ /**
89
+ * List all metadata indexes for a Vectorize index
90
+ */
91
+ export declare function listMetadataIndexes(api: CloudflareApi, indexName: string): Promise<{
92
+ propertyName: string;
93
+ indexType: "string" | "number" | "boolean";
94
+ }[]>;
95
+ export {};
@@ -0,0 +1,120 @@
1
+ import { Resource } from "../resource";
2
+ import { CloudflareApi, createCloudflareApi, } from "./api";
3
+ import { CloudflareApiError, handleApiError } from "./api-error";
4
+ import { VectorizeIndex } from "./vectorize-index";
5
+ /**
6
+ * Creates and manages Cloudflare Vectorize Metadata Indexes.
7
+ *
8
+ * Vectorize Metadata Indexes enable filtering based on metadata properties when querying vectors.
9
+ * Each Vectorize Index can have up to 10 metadata indexes.
10
+ *
11
+ * @example
12
+ * // First create a Vectorize Index
13
+ * const vectorIndex = await VectorizeIndex("documents", {
14
+ * name: "documents",
15
+ * config: {
16
+ * dimensions: 768,
17
+ * metric: "cosine"
18
+ * }
19
+ * });
20
+ *
21
+ * // Then create a metadata index for the "category" property
22
+ * const categoryIndex = await VectorizeMetadataIndex("category-index", {
23
+ * index: vectorIndex,
24
+ * propertyName: "category",
25
+ * indexType: "string"
26
+ * });
27
+ *
28
+ * @example
29
+ * // Create a metadata index for a numeric property
30
+ * const yearIndex = await VectorizeMetadataIndex("year-index", {
31
+ * index: vectorIndex,
32
+ * propertyName: "year",
33
+ * indexType: "number"
34
+ * });
35
+ *
36
+ * @see https://developers.cloudflare.com/vectorize/
37
+ */
38
+ export const VectorizeMetadataIndex = Resource("cloudflare::VectorizeMetadataIndex", async function (id, props) {
39
+ const api = await createCloudflareApi(props);
40
+ const indexName = props.index.name;
41
+ const propertyName = props.propertyName;
42
+ if (this.phase === "delete") {
43
+ // Delete metadata index
44
+ try {
45
+ await deleteMetadataIndex(api, indexName, propertyName);
46
+ }
47
+ catch (error) {
48
+ if (error instanceof CloudflareApiError &&
49
+ error.status === 400 &&
50
+ error.message.includes("does not exist")) {
51
+ // Index doesn't exist, which is what we want
52
+ }
53
+ else {
54
+ throw error;
55
+ }
56
+ }
57
+ return this.destroy();
58
+ }
59
+ else if (this.phase === "update") {
60
+ // Update operation is not supported
61
+ throw new Error("Updating Vectorize metadata indexes is not supported by the Cloudflare API. " +
62
+ "To change a metadata index, delete it and create a new one with the desired configuration.");
63
+ }
64
+ else {
65
+ const indexData = await createMetadataIndex(api, indexName, props);
66
+ return this({
67
+ id: propertyName, // Use propertyName as ID
68
+ index: props.index,
69
+ propertyName: props.propertyName,
70
+ indexType: props.indexType,
71
+ accountId: api.accountId,
72
+ mutationId: indexData.result.mutationId,
73
+ });
74
+ }
75
+ });
76
+ /**
77
+ * Create a new Vectorize metadata index
78
+ */
79
+ export async function createMetadataIndex(api, indexName, props) {
80
+ // Create new metadata index
81
+ const createPayload = {
82
+ propertyName: props.propertyName,
83
+ indexType: props.indexType,
84
+ };
85
+ const createResponse = await api.post(`/accounts/${api.accountId}/vectorize/v2/indexes/${indexName}/metadata_index/create`, createPayload);
86
+ if (!createResponse.ok) {
87
+ return await handleApiError(createResponse, "creating", "Vectorize metadata index", props.propertyName);
88
+ }
89
+ return (await createResponse.json());
90
+ }
91
+ /**
92
+ * Delete a Vectorize metadata index
93
+ */
94
+ export async function deleteMetadataIndex(api, indexName, propertyName) {
95
+ const deleteResponse = await api.post(`/accounts/${api.accountId}/vectorize/v2/indexes/${indexName}/metadata_index/delete`, {
96
+ propertyName,
97
+ });
98
+ if (!deleteResponse.ok) {
99
+ await handleApiError(deleteResponse, "deleting", "Vectorize metadata index", propertyName);
100
+ }
101
+ }
102
+ /**
103
+ * List all metadata indexes for a Vectorize index
104
+ */
105
+ export async function listMetadataIndexes(api, indexName) {
106
+ const response = await api.get(`/accounts/${api.accountId}/vectorize/v2/indexes/${indexName}/metadata_index/list`);
107
+ if (response.status === 410) {
108
+ // Gone
109
+ return [];
110
+ }
111
+ if (!response.ok) {
112
+ throw new CloudflareApiError(`Failed to list metadata indexes: ${response.statusText}`, response);
113
+ }
114
+ const data = (await response.json());
115
+ if (!data.success) {
116
+ const errorMessage = data.errors?.[0]?.message || "Unknown error";
117
+ throw new Error(`Failed to list metadata indexes: ${errorMessage}`);
118
+ }
119
+ return data.result.metadataIndexes;
120
+ }
@@ -353,6 +353,13 @@ async function prepareWorkerMetadata(ctx, oldBindings, props, assetUploadResult)
353
353
  pipeline: binding.name,
354
354
  });
355
355
  }
356
+ else if (binding.type === "vectorize") {
357
+ meta.bindings.push({
358
+ type: "vectorize",
359
+ name: bindingName,
360
+ index_name: binding.name,
361
+ });
362
+ }
356
363
  else {
357
364
  // @ts-expect-error - we should never reach here
358
365
  throw new Error(`Unsupported binding type: ${binding.type}`);
@@ -114,6 +114,13 @@ export interface WranglerJsonSpec {
114
114
  binding: string;
115
115
  class_name: string;
116
116
  }[];
117
+ /**
118
+ * Vectorize index bindings
119
+ */
120
+ vectorize_indexes?: {
121
+ binding: string;
122
+ index_name: string;
123
+ }[];
117
124
  /**
118
125
  * Plain text bindings (vars)
119
126
  */
@@ -50,6 +50,7 @@ function processBindings(spec, bindings) {
50
50
  const workflows = [];
51
51
  const d1Databases = [];
52
52
  const queues = [];
53
+ const vectorizeIndexes = [];
53
54
  // Process each binding
54
55
  for (const [bindingName, binding] of Object.entries(bindings)) {
55
56
  if (typeof binding === "string") {
@@ -120,6 +121,12 @@ function processBindings(spec, bindings) {
120
121
  queue: binding.name,
121
122
  });
122
123
  }
124
+ else if (binding.type === "vectorize") {
125
+ vectorizeIndexes.push({
126
+ binding: bindingName,
127
+ index_name: binding.name,
128
+ });
129
+ }
123
130
  }
124
131
  // Add collected bindings to the spec
125
132
  if (kvNamespaces.length > 0) {
@@ -139,4 +146,7 @@ function processBindings(spec, bindings) {
139
146
  if (queues.length > 0) {
140
147
  spec.queues = queues;
141
148
  }
149
+ if (vectorizeIndexes.length > 0) {
150
+ spec.vectorize_indexes = vectorizeIndexes;
151
+ }
142
152
  }
package/lib/test/bun.js CHANGED
@@ -79,6 +79,9 @@ export function test(meta, defaultOptions) {
79
79
  function test(...args) {
80
80
  const testName = args[0];
81
81
  const _options = typeof args[1] === "object" ? args[1] : undefined;
82
+ const timeout = typeof args[args.length - 1] === "number"
83
+ ? args[args.length - 1]
84
+ : 120000;
82
85
  const spread = (obj) => obj && typeof obj === "object"
83
86
  ? Object.fromEntries(Object.entries(obj).flatMap(([k, v]) => v !== undefined ? [[k, v]] : []))
84
87
  : {};
@@ -110,7 +113,7 @@ export function test(meta, defaultOptions) {
110
113
  await destroy(scope);
111
114
  }
112
115
  }
113
- });
116
+ }, timeout);
114
117
  });
115
118
  }
116
119
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "alchemy",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
4
4
  "type": "module",
5
5
  "module": "./lib/index.js",
6
6
  "scripts": {
@@ -49,7 +49,7 @@ export async function handleApiError(
49
49
  resourceName: string
50
50
  ): Promise<never> {
51
51
  const json: any = await response.json();
52
- const errors: { message: string }[] = json.errors || [
52
+ const errors: { message: string }[] = json?.errors || [
53
53
  { message: response.statusText },
54
54
  ];
55
55
  const errorMessage = `Error ${action} ${resourceType} '${resourceName}': ${errors[0]?.message || response.statusText}`;