diff --git a/src/core/files/deleteFileVectors.ts b/src/core/files/deleteFileVectors.ts new file mode 100644 index 0000000..14fb28e --- /dev/null +++ b/src/core/files/deleteFileVectors.ts @@ -0,0 +1,72 @@ +import type { PinataConfig, VectorizeFileResponse } from "../types"; +import { + PinataError, + NetworkError, + AuthenticationError, + ValidationError, +} from "../../utils/custom-errors"; + +export const deleteFileVectors = async ( + config: PinataConfig | undefined, + fileId: string, +): Promise => { + if (!config) { + throw new ValidationError("Pinata configuration is missing"); + } + + let headers: Record; + + if (config.customHeaders && Object.keys(config.customHeaders).length > 0) { + headers = { + Authorization: `Bearer ${config.pinataJwt}`, + ...config.customHeaders, + }; + } else { + headers = { + Authorization: `Bearer ${config.pinataJwt}`, + Source: "sdk/vectorizeFile", + }; + } + + let endpoint: string = "https://uploads.pinata.cloud/v3"; + + if (config.endpointUrl) { + endpoint = config.endpointUrl; + } + + try { + const request = await fetch(`${endpoint}/vectorize/files/${fileId}`, { + method: "DELETE", + headers: headers, + }); + + if (!request.ok) { + const errorData = await request.text(); + if (request.status === 401 || request.status === 403) { + throw new AuthenticationError( + `Authentication failed: ${errorData}`, + request.status, + errorData, + ); + } + throw new NetworkError( + `HTTP error: ${errorData}`, + request.status, + errorData, + ); + } + + const res: VectorizeFileResponse = await request.json(); + return res; + } catch (error) { + if (error instanceof PinataError) { + throw error; + } + if (error instanceof Error) { + throw new PinataError( + `Error processing vectorize file: ${error.message}`, + ); + } + throw new PinataError("An unknown error occurred while vectorizing file"); + } +}; diff --git a/src/core/files/vectorizeFile.ts b/src/core/files/vectorizeFile.ts new file mode 100644 index 0000000..72a8c3c --- /dev/null +++ b/src/core/files/vectorizeFile.ts @@ -0,0 +1,72 @@ +import type { PinataConfig, VectorizeFileResponse } from "../types"; +import { + PinataError, + NetworkError, + AuthenticationError, + ValidationError, +} from "../../utils/custom-errors"; + +export const vectorizeFile = async ( + config: PinataConfig | undefined, + fileId: string, +): Promise => { + if (!config) { + throw new ValidationError("Pinata configuration is missing"); + } + + let headers: Record; + + if (config.customHeaders && Object.keys(config.customHeaders).length > 0) { + headers = { + Authorization: `Bearer ${config.pinataJwt}`, + ...config.customHeaders, + }; + } else { + headers = { + Authorization: `Bearer ${config.pinataJwt}`, + Source: "sdk/vectorizeFile", + }; + } + + let endpoint: string = "https://uploads.pinata.cloud/v3"; + + if (config.endpointUrl) { + endpoint = config.endpointUrl; + } + + try { + const request = await fetch(`${endpoint}/vectorize/files/${fileId}`, { + method: "POST", + headers: headers, + }); + + if (!request.ok) { + const errorData = await request.text(); + if (request.status === 401 || request.status === 403) { + throw new AuthenticationError( + `Authentication failed: ${errorData}`, + request.status, + errorData, + ); + } + throw new NetworkError( + `HTTP error: ${errorData}`, + request.status, + errorData, + ); + } + + const res: VectorizeFileResponse = await request.json(); + return res; + } catch (error) { + if (error instanceof PinataError) { + throw error; + } + if (error instanceof Error) { + throw new PinataError( + `Error processing vectorize file: ${error.message}`, + ); + } + throw new PinataError("An unknown error occurred while vectorizing file"); + } +}; diff --git a/src/core/files/vectorizeQuery.ts b/src/core/files/vectorizeQuery.ts new file mode 100644 index 0000000..c6f7820 --- /dev/null +++ b/src/core/files/vectorizeQuery.ts @@ -0,0 +1,84 @@ +import type { + PinataConfig, + VectorizeQuery, + VectorizeQueryResponse, +} from "../types"; +import { + PinataError, + NetworkError, + AuthenticationError, + ValidationError, +} from "../../utils/custom-errors"; + +export const vectorizeQuery = async ( + config: PinataConfig | undefined, + options: VectorizeQuery, +): Promise => { + if (!config) { + throw new ValidationError("Pinata configuration is missing"); + } + + let headers: Record; + + if (config.customHeaders && Object.keys(config.customHeaders).length > 0) { + headers = { + Authorization: `Bearer ${config.pinataJwt}`, + ...config.customHeaders, + }; + } else { + headers = { + Authorization: `Bearer ${config.pinataJwt}`, + Source: "sdk/vectorQuery", + }; + } + + let endpoint: string = "https://uploads.pinata.cloud/v3"; + + if (config.endpointUrl) { + endpoint = config.endpointUrl; + } + + const body = JSON.stringify({ + text: options.query, + }); + + try { + const request = await fetch( + `${endpoint}/vectorize/groups/${options.groupId}/query`, + { + method: "POST", + headers: headers, + body: body, + }, + ); + + if (!request.ok) { + const errorData = await request.text(); + if (request.status === 401 || request.status === 403) { + throw new AuthenticationError( + `Authentication failed: ${errorData}`, + request.status, + errorData, + ); + } + throw new NetworkError( + `HTTP error: ${errorData}`, + request.status, + errorData, + ); + } + const res = await request.json(); + const resData: VectorizeQueryResponse = res.data; + return resData; + } catch (error) { + if (error instanceof PinataError) { + throw error; + } + if (error instanceof Error) { + throw new PinataError( + `Error processing vectorize file: ${error.message}`, + ); + } + throw new PinataError("An unknown error occurred while vectorizing file"); + } +}; diff --git a/src/core/pinataSDK.ts b/src/core/pinataSDK.ts index c6a084c..0c638d6 100644 --- a/src/core/pinataSDK.ts +++ b/src/core/pinataSDK.ts @@ -38,6 +38,9 @@ import type { FileListResponse, UpdateGroupFilesResponse, TopAnalyticsResponse, + VectorizeFileResponse, + VectorizeQuery, + VectorizeQueryResponse, } from "./types"; import { testAuthentication } from "./authentication/testAuthentication"; import { uploadFile } from "./uploads/file"; @@ -72,6 +75,9 @@ import { swapHistory } from "./files/swapHistory"; import { deleteSwap } from "./files/deleteSwap"; import { containsCID } from "../utils/gateway-tools"; import { createSignedURL } from "./gateway/createSignedURL"; +import { vectorizeFile } from "./files/vectorizeFile"; +import { vectorizeQuery } from "./files/vectorizeQuery"; +import { deleteFileVectors } from "./files/deleteFileVectors"; const formatConfig = (config: PinataConfig | undefined) => { let gateway = config?.pinataGateway; @@ -180,6 +186,18 @@ class Files { deleteSwap(cid: string): Promise { return deleteSwap(this.config, cid); } + + vectorize(fileId: string): Promise { + return vectorizeFile(this.config, fileId); + } + + queryVector(options: VectorizeQuery): Promise { + return vectorizeQuery(this.config, options); + } + + deleteVectors(fileId: string): Promise { + return deleteFileVectors(this.config, fileId); + } } class UploadBuilder { @@ -192,6 +210,7 @@ class UploadBuilder { private metadata: PinataMetadata | undefined; private keys: string | undefined; private groupId: string | undefined; + private vector: boolean | undefined; constructor( config: PinataConfig | undefined, @@ -216,6 +235,11 @@ class UploadBuilder { return this; } + vectorize(): UploadBuilder { + this.vector = true; + return this; + } + // cidVersion(v: 0 | 1): UploadBuilder { // this.version = v; // return this; @@ -246,6 +270,9 @@ class UploadBuilder { if (this.groupId) { options.groupId = this.groupId; } + if (this.vector) { + options.vectorize = this.vector; + } this.args[this.args.length - 1] = options; return this.uploadFunction(this.config, ...this.args).then( onfulfilled, @@ -426,7 +453,6 @@ class Gateways { createSignedURL(options: SignedUrlOptions): OptimizeImageCreateSignedURL { return new OptimizeImageCreateSignedURL(this.config, options); } - } class OptimizeImageGetCid { @@ -752,7 +778,6 @@ class Analytics { this.bandwidth.updateConfig(newConfig); } - summary(options: { domain: string; start: string; @@ -1018,7 +1043,6 @@ class AnalyticsBuilder { } } - class TimeIntervalAnalyticsBuilder extends AnalyticsBuilder< TimeIntervalAnalyticsQuery, TimeIntervalAnalyticsResponse diff --git a/src/core/types.ts b/src/core/types.ts index 1fa362a..e73c31c 100644 --- a/src/core/types.ts +++ b/src/core/types.ts @@ -21,6 +21,8 @@ export type UploadResponse = { mime_type: string; user_id: string; group_id: string | null; + is_duplicate: true | null; + vectorized: true | null; }; export type FileObject = { @@ -49,7 +51,7 @@ export type UploadOptions = { //pinType?: "async" | "sync" | "cidOnly"; keys?: string; groupId?: string; - //cidVersion?: 0 | 1; + vectorize?: boolean; }; export type DeleteResponse = { @@ -380,3 +382,23 @@ export type ContainsCIDResponse = { containsCid: boolean; cid: string | null; }; + +export type VectorizeFileResponse = { + status: boolean; +}; + +export type VectorizeQuery = { + groupId: string; + query: string; +}; + +export type VectorQueryMatch = { + file_id: string; + cid: string; + score: number; +}; + +export type VectorizeQueryResponse = { + count: number; + matches: VectorQueryMatch[]; +}; diff --git a/src/core/uploads/base64.ts b/src/core/uploads/base64.ts index 86175eb..c820419 100644 --- a/src/core/uploads/base64.ts +++ b/src/core/uploads/base64.ts @@ -116,6 +116,28 @@ export const uploadBase64 = async ( const res = await request.json(); const resData: UploadResponse = res.data; + if (options?.vectorize) { + const vectorReq = await fetch( + `${endpoint}/vectorize/files/${resData.id}`, + { + method: "POST", + headers: { + Authorization: `Bearer ${jwt}`, + }, + }, + ); + if (vectorReq.ok) { + resData.vectorized = true; + return resData; + } else { + const errorData = await vectorReq.text(); + throw new NetworkError( + `HTTP error during vectorization: ${errorData}`, + vectorReq.status, + errorData, + ); + } + } return resData; } catch (error) { if (error instanceof PinataError) { diff --git a/src/core/uploads/file.ts b/src/core/uploads/file.ts index 290127d..71d9ba3 100644 --- a/src/core/uploads/file.ts +++ b/src/core/uploads/file.ts @@ -141,6 +141,28 @@ export const uploadFile = async ( }); const fileInfo = await fileInfoReq.json(); const data: UploadResponse = fileInfo.data; + if (options?.vectorize) { + const vectorReq = await fetch( + `${endpoint}/vectorize/files/${data.id}`, + { + method: "POST", + headers: { + Authorization: `Bearer ${jwt}`, + }, + }, + ); + if (vectorReq.ok) { + data.vectorized = true; + return data; + } else { + const errorData = await vectorReq.text(); + throw new NetworkError( + `HTTP error during vectorization: ${errorData}`, + vectorReq.status, + errorData, + ); + } + } return data; } } @@ -193,6 +215,28 @@ export const uploadFile = async ( } const res = await request.json(); const resData: UploadResponse = res.data; + if (options?.vectorize) { + const vectorReq = await fetch( + `${endpoint}/vectorize/files/${resData.id}`, + { + method: "POST", + headers: { + Authorization: `Bearer ${jwt}`, + }, + }, + ); + if (vectorReq.ok) { + resData.vectorized = true; + return resData; + } else { + const errorData = await vectorReq.text(); + throw new NetworkError( + `HTTP error during vectorization: ${errorData}`, + vectorReq.status, + errorData, + ); + } + } return resData; } catch (error) { if (error instanceof PinataError) { diff --git a/src/core/uploads/json.ts b/src/core/uploads/json.ts index 6763a63..9f53714 100644 --- a/src/core/uploads/json.ts +++ b/src/core/uploads/json.ts @@ -122,6 +122,28 @@ export const uploadJson = async ( const res = await request.json(); const resData: UploadResponse = res.data; + if (options?.vectorize) { + const vectorReq = await fetch( + `${endpoint}/vectorize/files/${resData.id}`, + { + method: "POST", + headers: { + Authorization: `Bearer ${jwt}`, + }, + }, + ); + if (vectorReq.ok) { + resData.vectorized = true; + return resData; + } else { + const errorData = await vectorReq.text(); + throw new NetworkError( + `HTTP error during vectorization: ${errorData}`, + vectorReq.status, + errorData, + ); + } + } return resData; } catch (error) { if (error instanceof PinataError) { diff --git a/src/core/uploads/url.ts b/src/core/uploads/url.ts index 880cb7f..3000c72 100644 --- a/src/core/uploads/url.ts +++ b/src/core/uploads/url.ts @@ -128,6 +128,28 @@ export const uploadUrl = async ( const res = await request.json(); const resData: UploadResponse = res.data; + if (options?.vectorize) { + const vectorReq = await fetch( + `${endpoint}/vectorize/files/${resData.id}`, + { + method: "POST", + headers: { + Authorization: `Bearer ${jwt}`, + }, + }, + ); + if (vectorReq.ok) { + resData.vectorized = true; + return resData; + } else { + const errorData = await vectorReq.text(); + throw new NetworkError( + `HTTP error during vectorization: ${errorData}`, + vectorReq.status, + errorData, + ); + } + } return resData; } catch (error) { if (error instanceof PinataError) {