alchemy 0.7.3 → 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.
- package/lib/aws/table.js +22 -5
- package/lib/cloudflare/api-error.js +1 -1
- package/lib/cloudflare/bindings.d.ts +2 -1
- package/lib/cloudflare/bound.d.ts +2 -1
- package/lib/cloudflare/index.d.ts +2 -0
- package/lib/cloudflare/index.js +2 -0
- package/lib/cloudflare/vectorize-index.d.ts +136 -0
- package/lib/cloudflare/vectorize-index.js +170 -0
- package/lib/cloudflare/vectorize-metadata-index.d.ts +95 -0
- package/lib/cloudflare/vectorize-metadata-index.js +120 -0
- package/lib/cloudflare/worker.js +7 -0
- package/lib/cloudflare/wrangler.json.d.ts +7 -0
- package/lib/cloudflare/wrangler.json.js +10 -0
- package/lib/esbuild/bundle.d.ts +11 -14
- package/lib/esbuild/bundle.js +6 -3
- package/lib/test/bun.js +4 -1
- package/lib/web/vitepress/process-front-matter-files.js +1 -1
- package/package.json +1 -1
- package/src/aws/table.ts +34 -16
- package/src/cloudflare/api-error.ts +1 -1
- package/src/cloudflare/bindings.ts +2 -0
- package/src/cloudflare/bound.ts +8 -5
- package/src/cloudflare/index.ts +2 -0
- package/src/cloudflare/vectorize-index.ts +331 -0
- package/src/cloudflare/vectorize-metadata-index.ts +239 -0
- package/src/cloudflare/worker.ts +6 -0
- package/src/cloudflare/wrangler.json.ts +18 -0
- package/src/esbuild/bundle.ts +6 -3
- package/src/test/bun.ts +22 -14
- package/src/web/vitepress/process-front-matter-files.ts +1 -1
package/lib/esbuild/bundle.d.ts
CHANGED
|
@@ -94,14 +94,21 @@ export interface Bundle extends Resource<"esbuild::Bundle"> {
|
|
|
94
94
|
*/
|
|
95
95
|
export declare const Bundle: (((this: any, id: string, props?: {}) => never) & (new (_: never) => never)) | ((this: Context<Bundle>, id: string, props: BundleProps) => Promise<Bundle>);
|
|
96
96
|
export declare function bundle(props: BundleProps): Promise<esbuild.BuildResult<{
|
|
97
|
-
|
|
97
|
+
entryPoints: string[];
|
|
98
|
+
outfile: string;
|
|
99
|
+
bundle: true;
|
|
100
|
+
format: "iife" | "cjs" | "esm" | undefined;
|
|
101
|
+
target: string | string[] | undefined;
|
|
102
|
+
minify: boolean | undefined;
|
|
103
|
+
sourcemap: boolean | "inline" | "external" | "both" | undefined;
|
|
104
|
+
external: string[];
|
|
105
|
+
platform: "browser" | "node" | "neutral" | undefined;
|
|
106
|
+
metafile: true;
|
|
107
|
+
write: true;
|
|
98
108
|
splitting?: boolean;
|
|
99
109
|
preserveSymlinks?: boolean;
|
|
100
|
-
outfile: string;
|
|
101
|
-
metafile: boolean;
|
|
102
110
|
outdir?: string;
|
|
103
111
|
outbase?: string;
|
|
104
|
-
external: string[];
|
|
105
112
|
packages?: "bundle" | "external";
|
|
106
113
|
alias?: Record<string, string>;
|
|
107
114
|
loader?: {
|
|
@@ -110,7 +117,6 @@ export declare function bundle(props: BundleProps): Promise<esbuild.BuildResult<
|
|
|
110
117
|
resolveExtensions?: string[];
|
|
111
118
|
mainFields?: string[];
|
|
112
119
|
conditions?: string[];
|
|
113
|
-
write: boolean;
|
|
114
120
|
allowOverwrite?: boolean;
|
|
115
121
|
tsconfig?: string;
|
|
116
122
|
outExtension?: {
|
|
@@ -127,30 +133,21 @@ export declare function bundle(props: BundleProps): Promise<esbuild.BuildResult<
|
|
|
127
133
|
footer?: {
|
|
128
134
|
[type: string]: string;
|
|
129
135
|
};
|
|
130
|
-
entryPoints: string[] | Record<string, string> | {
|
|
131
|
-
in: string;
|
|
132
|
-
out: string;
|
|
133
|
-
}[];
|
|
134
136
|
stdin?: esbuild.StdinOptions;
|
|
135
137
|
plugins?: esbuild.Plugin[];
|
|
136
138
|
absWorkingDir?: string;
|
|
137
139
|
nodePaths?: string[];
|
|
138
|
-
sourcemap: boolean | "linked" | "inline" | "external" | "both";
|
|
139
140
|
legalComments?: "none" | "inline" | "eof" | "linked" | "external";
|
|
140
141
|
sourceRoot?: string;
|
|
141
142
|
sourcesContent?: boolean;
|
|
142
|
-
format: esbuild.Format;
|
|
143
143
|
globalName?: string;
|
|
144
|
-
target: string | string[];
|
|
145
144
|
supported?: Record<string, boolean>;
|
|
146
|
-
platform: esbuild.Platform;
|
|
147
145
|
mangleProps?: RegExp;
|
|
148
146
|
reserveProps?: RegExp;
|
|
149
147
|
mangleQuoted?: boolean;
|
|
150
148
|
mangleCache?: Record<string, string | false>;
|
|
151
149
|
drop?: esbuild.Drop[];
|
|
152
150
|
dropLabels?: string[];
|
|
153
|
-
minify: boolean;
|
|
154
151
|
minifyWhitespace?: boolean;
|
|
155
152
|
minifyIdentifiers?: boolean;
|
|
156
153
|
minifySyntax?: boolean;
|
package/lib/esbuild/bundle.js
CHANGED
|
@@ -47,8 +47,8 @@ export const Bundle = Resource("esbuild::Bundle", {
|
|
|
47
47
|
});
|
|
48
48
|
export async function bundle(props) {
|
|
49
49
|
const outputPath = getOutputPath(props);
|
|
50
|
-
// Build the bundle
|
|
51
50
|
return await esbuild.build({
|
|
51
|
+
...props.options,
|
|
52
52
|
entryPoints: [props.entryPoint],
|
|
53
53
|
outfile: outputPath,
|
|
54
54
|
bundle: true,
|
|
@@ -56,11 +56,14 @@ export async function bundle(props) {
|
|
|
56
56
|
target: props.target,
|
|
57
57
|
minify: props.minify,
|
|
58
58
|
sourcemap: props.sourcemap,
|
|
59
|
-
external:
|
|
59
|
+
external: [
|
|
60
|
+
"node:async_hooks",
|
|
61
|
+
...(props.external ?? []),
|
|
62
|
+
...(props.options?.external ?? []),
|
|
63
|
+
],
|
|
60
64
|
platform: props.platform,
|
|
61
65
|
metafile: true,
|
|
62
66
|
write: true,
|
|
63
|
-
...props.options,
|
|
64
67
|
});
|
|
65
68
|
}
|
|
66
69
|
function getOutputPath(props) {
|
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
|
}
|
|
@@ -35,7 +35,7 @@ export async function processFrontmatterFiles(directoryPath, linkPrefix) {
|
|
|
35
35
|
const frontmatter = frontmatterMatch[1];
|
|
36
36
|
const orderMatch = frontmatter.match(/order:\s*(\d+)/);
|
|
37
37
|
if (orderMatch) {
|
|
38
|
-
order =
|
|
38
|
+
order = parseFloat(orderMatch[1]);
|
|
39
39
|
}
|
|
40
40
|
const titleMatch = frontmatter.match(/title:\s*(.+)/);
|
|
41
41
|
if (titleMatch) {
|
package/package.json
CHANGED
package/src/aws/table.ts
CHANGED
|
@@ -143,7 +143,7 @@ export const Table = Resource(
|
|
|
143
143
|
async function (
|
|
144
144
|
this: Context<Table>,
|
|
145
145
|
id: string,
|
|
146
|
-
props: TableProps
|
|
146
|
+
props: TableProps
|
|
147
147
|
): Promise<Table> {
|
|
148
148
|
const client = new DynamoDBClient({});
|
|
149
149
|
|
|
@@ -154,15 +154,13 @@ export const Table = Resource(
|
|
|
154
154
|
client.send(
|
|
155
155
|
new DeleteTableCommand({
|
|
156
156
|
TableName: props.tableName,
|
|
157
|
-
})
|
|
158
|
-
)
|
|
157
|
+
})
|
|
158
|
+
)
|
|
159
159
|
);
|
|
160
160
|
},
|
|
161
|
-
|
|
162
|
-
error instanceof ResourceInUseException ||
|
|
163
|
-
error instanceof InternalServerError,
|
|
161
|
+
isRetryableError,
|
|
164
162
|
10, // Max attempts
|
|
165
|
-
200
|
|
163
|
+
200 // Initial delay in ms
|
|
166
164
|
);
|
|
167
165
|
|
|
168
166
|
// Wait for table to be deleted
|
|
@@ -175,7 +173,7 @@ export const Table = Resource(
|
|
|
175
173
|
await client.send(
|
|
176
174
|
new DescribeTableCommand({
|
|
177
175
|
TableName: props.tableName,
|
|
178
|
-
})
|
|
176
|
+
})
|
|
179
177
|
);
|
|
180
178
|
// If we get here, table still exists
|
|
181
179
|
retryCount++;
|
|
@@ -193,7 +191,7 @@ export const Table = Resource(
|
|
|
193
191
|
|
|
194
192
|
if (!tableDeleted) {
|
|
195
193
|
throw new Error(
|
|
196
|
-
`Timed out waiting for table ${props.tableName} to be deleted
|
|
194
|
+
`Timed out waiting for table ${props.tableName} to be deleted`
|
|
197
195
|
);
|
|
198
196
|
}
|
|
199
197
|
|
|
@@ -233,7 +231,7 @@ export const Table = Resource(
|
|
|
233
231
|
const describeResponse = await client.send(
|
|
234
232
|
new DescribeTableCommand({
|
|
235
233
|
TableName: props.tableName,
|
|
236
|
-
})
|
|
234
|
+
})
|
|
237
235
|
);
|
|
238
236
|
|
|
239
237
|
// If table exists and is ACTIVE, no need to create it
|
|
@@ -267,16 +265,16 @@ export const Table = Resource(
|
|
|
267
265
|
Value,
|
|
268
266
|
}))
|
|
269
267
|
: undefined,
|
|
270
|
-
})
|
|
268
|
+
})
|
|
271
269
|
);
|
|
272
270
|
} else {
|
|
273
271
|
throw error;
|
|
274
272
|
}
|
|
275
273
|
}
|
|
276
274
|
},
|
|
277
|
-
|
|
275
|
+
isRetryableError,
|
|
278
276
|
10, // Max attempts
|
|
279
|
-
200
|
|
277
|
+
200 // Initial delay in ms
|
|
280
278
|
);
|
|
281
279
|
|
|
282
280
|
// Wait for table to be active with timeout
|
|
@@ -290,7 +288,7 @@ export const Table = Resource(
|
|
|
290
288
|
const response = await client.send(
|
|
291
289
|
new DescribeTableCommand({
|
|
292
290
|
TableName: props.tableName,
|
|
293
|
-
})
|
|
291
|
+
})
|
|
294
292
|
);
|
|
295
293
|
|
|
296
294
|
tableActive = response.Table?.TableStatus === "ACTIVE";
|
|
@@ -313,7 +311,7 @@ export const Table = Resource(
|
|
|
313
311
|
|
|
314
312
|
if (!tableActive) {
|
|
315
313
|
throw new Error(
|
|
316
|
-
`Timed out waiting for table ${props.tableName} to become active
|
|
314
|
+
`Timed out waiting for table ${props.tableName} to become active`
|
|
317
315
|
);
|
|
318
316
|
}
|
|
319
317
|
|
|
@@ -324,5 +322,25 @@ export const Table = Resource(
|
|
|
324
322
|
tableId: tableDescription!.TableId!,
|
|
325
323
|
});
|
|
326
324
|
}
|
|
327
|
-
}
|
|
325
|
+
}
|
|
328
326
|
);
|
|
327
|
+
|
|
328
|
+
const retryableErrors = [
|
|
329
|
+
"ResourceInUseException",
|
|
330
|
+
"ResourceNotFoundException",
|
|
331
|
+
"InternalServerError",
|
|
332
|
+
"ThrottlingException",
|
|
333
|
+
"ProvisionedThroughputExceededException",
|
|
334
|
+
"LimitExceededException",
|
|
335
|
+
"RequestLimitExceeded",
|
|
336
|
+
];
|
|
337
|
+
|
|
338
|
+
function isRetryableError(error: any) {
|
|
339
|
+
return (
|
|
340
|
+
error instanceof ResourceInUseException ||
|
|
341
|
+
error instanceof InternalServerError ||
|
|
342
|
+
retryableErrors.includes(error?.name) ||
|
|
343
|
+
retryableErrors.includes(error?.code) ||
|
|
344
|
+
error?.$metadata?.httpStatusCode === 500
|
|
345
|
+
);
|
|
346
|
+
}
|
|
@@ -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
|
|
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}`;
|
|
@@ -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
|
|
|
@@ -31,6 +32,7 @@ export type Binding =
|
|
|
31
32
|
| R2Bucket
|
|
32
33
|
| Secret
|
|
33
34
|
| string
|
|
35
|
+
| VectorizeIndex
|
|
34
36
|
| Worker
|
|
35
37
|
| Workflow;
|
|
36
38
|
|
package/src/cloudflare/bound.ts
CHANGED
|
@@ -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
14
|
|
|
@@ -27,8 +28,10 @@ export type Bound<T extends Binding> = T extends _DurableObjectNamespace
|
|
|
27
28
|
? Workflow<P>
|
|
28
29
|
: T extends _D1Database
|
|
29
30
|
? D1Database
|
|
30
|
-
: T extends
|
|
31
|
-
?
|
|
32
|
-
: T extends
|
|
33
|
-
?
|
|
34
|
-
:
|
|
31
|
+
: T extends _VectorizeIndex
|
|
32
|
+
? VectorizeIndex
|
|
33
|
+
: T extends _Queue
|
|
34
|
+
? Queue
|
|
35
|
+
: T extends _Pipeline<infer R>
|
|
36
|
+
? Pipeline<R>
|
|
37
|
+
: Service;
|
package/src/cloudflare/index.ts
CHANGED
|
@@ -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,331 @@
|
|
|
1
|
+
import type { Context } from "../context";
|
|
2
|
+
import { Resource } from "../resource";
|
|
3
|
+
import {
|
|
4
|
+
CloudflareApi,
|
|
5
|
+
createCloudflareApi,
|
|
6
|
+
type CloudflareApiOptions,
|
|
7
|
+
} from "./api";
|
|
8
|
+
import { CloudflareApiError, handleApiError } from "./api-error";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Properties for creating or updating a Vectorize Index
|
|
12
|
+
*/
|
|
13
|
+
export interface VectorizeIndexProps extends CloudflareApiOptions {
|
|
14
|
+
/**
|
|
15
|
+
* Name of the index
|
|
16
|
+
*/
|
|
17
|
+
name: string;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Optional description of the index
|
|
21
|
+
*/
|
|
22
|
+
description?: string;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Dimensions of the vectors
|
|
26
|
+
*/
|
|
27
|
+
dimensions: number;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Distance metric used for vector similarity
|
|
31
|
+
*/
|
|
32
|
+
metric: "cosine" | "euclidean" | "dot_product";
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Whether to delete the index if removed
|
|
36
|
+
* If set to false, the index will remain but the resource will be removed from state
|
|
37
|
+
*
|
|
38
|
+
* @default true
|
|
39
|
+
*/
|
|
40
|
+
delete?: boolean;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Whether to adopt an existing index with the same name if it exists
|
|
44
|
+
* If true and an index with the same name exists, it will be adopted rather than creating a new one
|
|
45
|
+
*
|
|
46
|
+
* @default false
|
|
47
|
+
*/
|
|
48
|
+
adopt?: boolean;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Output returned after Vectorize Index creation/update
|
|
53
|
+
*/
|
|
54
|
+
export interface VectorizeIndex
|
|
55
|
+
extends Resource<"cloudflare::VectorizeIndex">,
|
|
56
|
+
VectorizeIndexProps {
|
|
57
|
+
type: "vectorize";
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* The unique identifier for the index (same as name)
|
|
61
|
+
*/
|
|
62
|
+
id: string;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Time at which the index was created
|
|
66
|
+
*/
|
|
67
|
+
createdAt?: number;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Creates and manages Cloudflare Vectorize Indexes.
|
|
72
|
+
*
|
|
73
|
+
* Vectorize is Cloudflare's vector database that enables vector search within Cloudflare Workers.
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* // Create a basic vector index for text embeddings
|
|
77
|
+
* const basicIndex = await VectorizeIndex("text-embeddings", {
|
|
78
|
+
* name: "text-embeddings",
|
|
79
|
+
* config: {
|
|
80
|
+
* dimensions: 768,
|
|
81
|
+
* metric: "cosine"
|
|
82
|
+
* }
|
|
83
|
+
* });
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* // Create a vector index with a description
|
|
87
|
+
* const descIndex = await VectorizeIndex("image-embeddings", {
|
|
88
|
+
* name: "image-embeddings",
|
|
89
|
+
* description: "Vector index for image embeddings using CLIP model",
|
|
90
|
+
* config: {
|
|
91
|
+
* dimensions: 512,
|
|
92
|
+
* metric: "cosine"
|
|
93
|
+
* }
|
|
94
|
+
* });
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* // Adopt an existing index if it already exists instead of failing
|
|
98
|
+
* const existingIndex = await VectorizeIndex("existing-index", {
|
|
99
|
+
* name: "existing-index",
|
|
100
|
+
* adopt: true,
|
|
101
|
+
* config: {
|
|
102
|
+
* dimensions: 1024,
|
|
103
|
+
* metric: "euclidean"
|
|
104
|
+
* }
|
|
105
|
+
* });
|
|
106
|
+
*
|
|
107
|
+
* @see https://developers.cloudflare.com/vectorize/
|
|
108
|
+
*/
|
|
109
|
+
export const VectorizeIndex = Resource(
|
|
110
|
+
"cloudflare::VectorizeIndex",
|
|
111
|
+
async function (
|
|
112
|
+
this: Context<VectorizeIndex>,
|
|
113
|
+
id: string,
|
|
114
|
+
props: VectorizeIndexProps
|
|
115
|
+
): Promise<VectorizeIndex> {
|
|
116
|
+
const api = await createCloudflareApi(props);
|
|
117
|
+
const indexName = props.name || id;
|
|
118
|
+
|
|
119
|
+
if (this.phase === "delete") {
|
|
120
|
+
console.log("Deleting Vectorize index:", indexName);
|
|
121
|
+
if (props.delete !== false) {
|
|
122
|
+
// Delete Vectorize index
|
|
123
|
+
await deleteIndex(api, indexName);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Return void (a deleted index has no content)
|
|
127
|
+
return this.destroy();
|
|
128
|
+
} else {
|
|
129
|
+
let indexData: CloudflareVectorizeResponse;
|
|
130
|
+
|
|
131
|
+
if (this.phase === "create") {
|
|
132
|
+
console.log("Creating Vectorize index:", indexName);
|
|
133
|
+
try {
|
|
134
|
+
indexData = await createIndex(api, indexName, {
|
|
135
|
+
...props,
|
|
136
|
+
name: indexName,
|
|
137
|
+
});
|
|
138
|
+
} catch (error) {
|
|
139
|
+
// Check if this is a "index already exists" error and adopt is enabled
|
|
140
|
+
if (
|
|
141
|
+
props.adopt &&
|
|
142
|
+
error instanceof CloudflareApiError &&
|
|
143
|
+
error.message.includes("already exists")
|
|
144
|
+
) {
|
|
145
|
+
console.log(`Index ${indexName} already exists, adopting it`);
|
|
146
|
+
// Find the existing index
|
|
147
|
+
indexData = await getIndex(api, indexName);
|
|
148
|
+
} else {
|
|
149
|
+
// Re-throw the error if adopt is false or it's not a "index already exists" error
|
|
150
|
+
throw error;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
} else {
|
|
154
|
+
// Update operation is not supported by Vectorize API
|
|
155
|
+
throw new Error(
|
|
156
|
+
"Updating Vectorize indexes is not supported by the Cloudflare API. " +
|
|
157
|
+
"To change an index, delete it and create a new one with the desired configuration."
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return this({
|
|
162
|
+
type: "vectorize",
|
|
163
|
+
id: indexName,
|
|
164
|
+
name: indexName,
|
|
165
|
+
description: props.description,
|
|
166
|
+
dimensions: indexData.result.config.dimensions,
|
|
167
|
+
metric: indexData.result.config.metric as
|
|
168
|
+
| "cosine"
|
|
169
|
+
| "euclidean"
|
|
170
|
+
| "dot_product",
|
|
171
|
+
accountId: api.accountId,
|
|
172
|
+
createdAt: indexData.result.created_on
|
|
173
|
+
? new Date(indexData.result.created_on).getTime()
|
|
174
|
+
: undefined,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
interface CloudflareVectorizeResponse {
|
|
181
|
+
result: {
|
|
182
|
+
name: string;
|
|
183
|
+
description?: string;
|
|
184
|
+
created_on?: string;
|
|
185
|
+
config: {
|
|
186
|
+
dimensions: number;
|
|
187
|
+
metric: string;
|
|
188
|
+
};
|
|
189
|
+
};
|
|
190
|
+
success: boolean;
|
|
191
|
+
errors: Array<{ code: number; message: string }>;
|
|
192
|
+
messages: string[];
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Create a new Vectorize index
|
|
197
|
+
*/
|
|
198
|
+
export async function createIndex(
|
|
199
|
+
api: CloudflareApi,
|
|
200
|
+
indexName: string,
|
|
201
|
+
props: VectorizeIndexProps
|
|
202
|
+
): Promise<CloudflareVectorizeResponse> {
|
|
203
|
+
// Create new Vectorize index
|
|
204
|
+
const createPayload: any = {
|
|
205
|
+
name: indexName,
|
|
206
|
+
config: {
|
|
207
|
+
dimensions: props.dimensions,
|
|
208
|
+
metric: props.metric,
|
|
209
|
+
},
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
if (props.description) {
|
|
213
|
+
createPayload.description = props.description;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const createResponse = await api.post(
|
|
217
|
+
`/accounts/${api.accountId}/vectorize/v2/indexes`,
|
|
218
|
+
createPayload
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
if (!createResponse.ok) {
|
|
222
|
+
return await handleApiError(
|
|
223
|
+
createResponse,
|
|
224
|
+
"creating",
|
|
225
|
+
"Vectorize index",
|
|
226
|
+
indexName
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return (await createResponse.json()) as CloudflareVectorizeResponse;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Get a Vectorize index
|
|
235
|
+
*/
|
|
236
|
+
export async function getIndex(
|
|
237
|
+
api: CloudflareApi,
|
|
238
|
+
indexName: string
|
|
239
|
+
): Promise<CloudflareVectorizeResponse> {
|
|
240
|
+
const response = await api.get(
|
|
241
|
+
`/accounts/${api.accountId}/vectorize/v2/indexes/${indexName}`
|
|
242
|
+
);
|
|
243
|
+
|
|
244
|
+
if (!response.ok) {
|
|
245
|
+
return await handleApiError(
|
|
246
|
+
response,
|
|
247
|
+
"getting",
|
|
248
|
+
"Vectorize index",
|
|
249
|
+
indexName
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return (await response.json()) as CloudflareVectorizeResponse;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Delete a Vectorize index
|
|
258
|
+
*/
|
|
259
|
+
export async function deleteIndex(
|
|
260
|
+
api: CloudflareApi,
|
|
261
|
+
indexName: string
|
|
262
|
+
): Promise<void> {
|
|
263
|
+
// Delete Vectorize index
|
|
264
|
+
const deleteResponse = await api.delete(
|
|
265
|
+
`/accounts/${api.accountId}/vectorize/v2/indexes/${indexName}`
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
if (!deleteResponse.ok && deleteResponse.status !== 404) {
|
|
269
|
+
const errorData: any = await deleteResponse.json().catch(() => ({
|
|
270
|
+
errors: [{ message: deleteResponse.statusText }],
|
|
271
|
+
}));
|
|
272
|
+
throw new CloudflareApiError(
|
|
273
|
+
`Error deleting Vectorize index '${indexName}': ${errorData.errors?.[0]?.message || deleteResponse.statusText}`,
|
|
274
|
+
deleteResponse
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* List all Vectorize indexes in an account
|
|
281
|
+
*/
|
|
282
|
+
export async function listIndexes(
|
|
283
|
+
api: CloudflareApi
|
|
284
|
+
): Promise<{ name: string; description?: string }[]> {
|
|
285
|
+
const response = await api.get(
|
|
286
|
+
`/accounts/${api.accountId}/vectorize/v2/indexes`
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
if (!response.ok) {
|
|
290
|
+
throw new CloudflareApiError(
|
|
291
|
+
`Failed to list indexes: ${response.statusText}`,
|
|
292
|
+
response
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const data = (await response.json()) as {
|
|
297
|
+
success: boolean;
|
|
298
|
+
errors?: Array<{ code: number; message: string }>;
|
|
299
|
+
result?: Array<{
|
|
300
|
+
name: string;
|
|
301
|
+
description?: string;
|
|
302
|
+
}>;
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
if (!data.success) {
|
|
306
|
+
const errorMessage = data.errors?.[0]?.message || "Unknown error";
|
|
307
|
+
throw new Error(`Failed to list indexes: ${errorMessage}`);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Transform API response
|
|
311
|
+
return (data.result || []).map((index) => ({
|
|
312
|
+
name: index.name,
|
|
313
|
+
description: index.description,
|
|
314
|
+
}));
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Update a Vectorize index
|
|
319
|
+
*
|
|
320
|
+
* Note: The Cloudflare Vectorize API does not support updating indexes.
|
|
321
|
+
* This function will always throw an error.
|
|
322
|
+
*/
|
|
323
|
+
export async function updateIndex(
|
|
324
|
+
api: CloudflareApi,
|
|
325
|
+
indexName: string,
|
|
326
|
+
props: VectorizeIndexProps
|
|
327
|
+
): Promise<CloudflareVectorizeResponse> {
|
|
328
|
+
throw new Error(
|
|
329
|
+
"Updating Vectorize indexes is not supported by the Cloudflare API. To change an index, delete it and create a new one."
|
|
330
|
+
);
|
|
331
|
+
}
|