@conorroberts/utils 0.0.23 → 0.0.24

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/dist/images.cjs CHANGED
@@ -144,7 +144,7 @@ var ImageUtils = class {
144
144
  formData.set("id", args.id ?? (0, import_cuid2.createId)());
145
145
  const headers = new Headers();
146
146
  headers.set("Authorization", `Bearer ${args.apiKey}`);
147
- const response = await fetch(
147
+ const response = await (0, import_ofetch.ofetch)(
148
148
  `https://api.cloudflare.com/client/v4/accounts/${this.accountHash}/images/v1`,
149
149
  {
150
150
  method: "POST",
@@ -152,7 +152,7 @@ var ImageUtils = class {
152
152
  body: formData
153
153
  }
154
154
  );
155
- return response.json();
155
+ return response;
156
156
  }
157
157
  async delete(id, args) {
158
158
  if (this.isProtected(id)) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/images.ts"],"sourcesContent":["import { createId } from \"@paralleldrive/cuid2\";\r\nimport dayjs from \"dayjs\";\r\nimport { ofetch } from \"ofetch\";\r\n\r\nexport interface OptimizedImageOptions {\r\n anim?: boolean;\r\n background?: string;\r\n blur?: number;\r\n brightness?: number;\r\n compression?: \"fast\"; // faster compression = larger file size\r\n contrast?: number;\r\n dpr?: number;\r\n fit?: \"scale-down\" | \"contain\" | \"cover\" | \"crop\" | \"pad\";\r\n format?: \"webp\" | \"avif\" | \"json\";\r\n gamma?: number;\r\n width?: number;\r\n height?: number;\r\n metadata?: \"keep\" | \"copyright\" | \"none\";\r\n quality?: number;\r\n rotate?: number;\r\n sharpen?: number;\r\n}\r\n\r\nexport interface CreateImageUrlResponse {\r\n result: {\r\n id: string;\r\n uploadURL: string;\r\n };\r\n success: boolean;\r\n errors: unknown[];\r\n messages: unknown[];\r\n}\r\n\r\ninterface UploadImageResponse {\r\n result: {\r\n id: string;\r\n filename: string;\r\n uploaded: string;\r\n requireSignedURLs: boolean;\r\n variants: string[];\r\n };\r\n success: boolean;\r\n errors: unknown[];\r\n messages: unknown[];\r\n}\r\n\r\nexport class ImageUtils<ImageIds extends Record<string, any>> {\r\n private blacklist: string[] = [\"img.clerk.com\"];\r\n private _accountHash: string;\r\n private _imageIds: ImageIds | undefined;\r\n\r\n constructor(args: {\r\n accountHash: string;\r\n blacklist?: string[];\r\n imageIds?: ImageIds;\r\n }) {\r\n this._accountHash = args.accountHash;\r\n\r\n this._imageIds = args.imageIds;\r\n\r\n if (args.blacklist) {\r\n this.blacklist.push(...args.blacklist);\r\n }\r\n }\r\n\r\n get imageIds() {\r\n if (!this._imageIds) {\r\n throw new Error(\"imageIds was not supplied in constructor\");\r\n }\r\n\r\n return this._imageIds;\r\n }\r\n\r\n get accountHash() {\r\n return this._accountHash;\r\n }\r\n\r\n public url(id: string) {\r\n return `https://imagedelivery.net/${this.accountHash}/${id}/public`;\r\n }\r\n\r\n private isBlacklisted(url: string) {\r\n return this.blacklist.some((u) => url.includes(u));\r\n }\r\n\r\n private isProtected(id: string) {\r\n if (!this._imageIds) {\r\n return false;\r\n }\r\n\r\n return Object.values(this._imageIds).some((e) => e === id);\r\n }\r\n\r\n /**\r\n * Will only operate on images that have been uploaded via cloudflare images\r\n */\r\n public optimizeUrl(url: string, options: OptimizedImageOptions) {\r\n if (this.isBlacklisted(url)) {\r\n return url;\r\n }\r\n\r\n // Final format should look similar to: https://imagedelivery.net/<ACCOUNT_HASH>/<IMAGE_ID>/w=400,sharpen=3\r\n return url.replace(\"public\", this.createImageOptionsString(options));\r\n }\r\n\r\n public optimizeId(id: string, options: OptimizedImageOptions) {\r\n return this.optimizeUrl(this.url(id), options);\r\n }\r\n\r\n public createOptionsSearchParams(options: OptimizedImageOptions) {\r\n const params = new URLSearchParams();\r\n\r\n const pairs = Object.entries(options);\r\n\r\n for (const [key, val] of pairs) {\r\n if (val === undefined) {\r\n continue;\r\n }\r\n\r\n params.set(key, val.toString());\r\n }\r\n\r\n return params;\r\n }\r\n\r\n public createImageOptionsString(options: OptimizedImageOptions) {\r\n const params = this.createOptionsSearchParams(options);\r\n\r\n return Array.from(params.entries())\r\n .map(([key, val]) => `${key}=${val}`)\r\n .join(\",\");\r\n }\r\n\r\n public async createUploadUrls(count: number, args: { apiKey: string }) {\r\n if (count === 0) {\r\n return [];\r\n }\r\n\r\n const headers = new Headers();\r\n headers.set(\"Authorization\", `Bearer ${args.apiKey}`);\r\n\r\n const urls = await Promise.all(\r\n Array.from({ length: count }).map(async () => {\r\n try {\r\n const form = new FormData();\r\n const id = createId();\r\n form.append(\"id\", id);\r\n form.append(\"expiry\", dayjs().add(5, \"minute\").toISOString());\r\n\r\n const img = await ofetch<CreateImageUrlResponse>(\r\n `https://api.cloudflare.com/client/v4/accounts/${this.accountHash}/images/v2/direct_upload`,\r\n { method: \"POST\", headers, body: form }\r\n );\r\n\r\n if (!img.success) {\r\n throw new Error(\"Error uploading image\");\r\n }\r\n\r\n return { url: img.result.uploadURL, id };\r\n } catch (e) {\r\n console.error(\"Error uploading image\");\r\n throw e;\r\n }\r\n })\r\n );\r\n\r\n return urls;\r\n }\r\n\r\n public async clientUpload(url: string, body: FormData) {\r\n const fetchResponse = await ofetch<UploadImageResponse>(url, {\r\n method: \"POST\",\r\n body,\r\n });\r\n\r\n if (!fetchResponse.success) {\r\n throw new Error(\"Failed to upload image\");\r\n }\r\n\r\n const downloadUrl = fetchResponse.result.variants[0];\r\n\r\n if (!downloadUrl) {\r\n throw new Error(\"Could not find download URL\");\r\n }\r\n\r\n return downloadUrl;\r\n }\r\n\r\n public async upload(data: Blob, args: { apiKey: string; id?: string }) {\r\n const formData = new FormData();\r\n formData.set(\"file\", data, \"file.png\");\r\n formData.set(\"id\", args.id ?? createId());\r\n\r\n const headers = new Headers();\r\n headers.set(\"Authorization\", `Bearer ${args.apiKey}`);\r\n\r\n const response = await fetch(\r\n `https://api.cloudflare.com/client/v4/accounts/${this.accountHash}/images/v1`,\r\n {\r\n method: \"POST\",\r\n headers,\r\n body: formData,\r\n }\r\n );\r\n\r\n return response.json();\r\n }\r\n\r\n public async delete(id: string, args: { apiKey: string }) {\r\n if (this.isProtected(id)) {\r\n return { success: true };\r\n }\r\n\r\n try {\r\n const headers = new Headers();\r\n headers.set(\"Authorization\", `Bearer ${args.apiKey}`);\r\n\r\n await ofetch(\r\n `https://api.cloudflare.com/client/v4/accounts/${this.accountHash}/images/v1/${id}`,\r\n {\r\n method: \"POST\",\r\n headers,\r\n }\r\n );\r\n return { success: true };\r\n } catch (_e) {\r\n return { success: false };\r\n }\r\n }\r\n\r\n public async batchUpload(\r\n files: { file: File; url: { id: string; value: string } }[]\r\n ) {\r\n return await Promise.all(\r\n files.map(async (e) => {\r\n const formData = new FormData();\r\n formData.append(\"file\", e.file);\r\n\r\n const downloadUrl = await this.clientUpload(e.url.value, formData);\r\n\r\n return {\r\n url: downloadUrl,\r\n id: e.url.id,\r\n };\r\n })\r\n );\r\n }\r\n}\r\n"],"mappings":"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","names":["dayjs"]}
1
+ {"version":3,"sources":["../src/images.ts"],"sourcesContent":["import { createId } from \"@paralleldrive/cuid2\";\r\nimport dayjs from \"dayjs\";\r\nimport { ofetch } from \"ofetch\";\r\n\r\nexport interface OptimizedImageOptions {\r\n anim?: boolean;\r\n background?: string;\r\n blur?: number;\r\n brightness?: number;\r\n compression?: \"fast\"; // faster compression = larger file size\r\n contrast?: number;\r\n dpr?: number;\r\n fit?: \"scale-down\" | \"contain\" | \"cover\" | \"crop\" | \"pad\";\r\n format?: \"webp\" | \"avif\" | \"json\";\r\n gamma?: number;\r\n width?: number;\r\n height?: number;\r\n metadata?: \"keep\" | \"copyright\" | \"none\";\r\n quality?: number;\r\n rotate?: number;\r\n sharpen?: number;\r\n}\r\n\r\nexport interface CreateImageUrlResponse {\r\n result: {\r\n id: string;\r\n uploadURL: string;\r\n };\r\n success: boolean;\r\n errors: { code: string; message: string }[];\r\n messages: unknown[];\r\n}\r\n\r\ninterface UploadImageResponse {\r\n result: {\r\n id: string;\r\n filename: string;\r\n uploaded: string;\r\n requireSignedURLs: boolean;\r\n variants: string[];\r\n };\r\n success: boolean;\r\n errors: { code: string; message: string }[];\r\n messages: unknown[];\r\n}\r\n\r\nexport class ImageUtils<ImageIds extends Record<string, any>> {\r\n private blacklist: string[] = [\"img.clerk.com\"];\r\n private _accountHash: string;\r\n private _imageIds: ImageIds | undefined;\r\n\r\n constructor(args: {\r\n accountHash: string;\r\n blacklist?: string[];\r\n imageIds?: ImageIds;\r\n }) {\r\n this._accountHash = args.accountHash;\r\n\r\n this._imageIds = args.imageIds;\r\n\r\n if (args.blacklist) {\r\n this.blacklist.push(...args.blacklist);\r\n }\r\n }\r\n\r\n get imageIds() {\r\n if (!this._imageIds) {\r\n throw new Error(\"imageIds was not supplied in constructor\");\r\n }\r\n\r\n return this._imageIds;\r\n }\r\n\r\n get accountHash() {\r\n return this._accountHash;\r\n }\r\n\r\n public url(id: string) {\r\n return `https://imagedelivery.net/${this.accountHash}/${id}/public`;\r\n }\r\n\r\n private isBlacklisted(url: string) {\r\n return this.blacklist.some((u) => url.includes(u));\r\n }\r\n\r\n private isProtected(id: string) {\r\n if (!this._imageIds) {\r\n return false;\r\n }\r\n\r\n return Object.values(this._imageIds).some((e) => e === id);\r\n }\r\n\r\n /**\r\n * Will only operate on images that have been uploaded via cloudflare images\r\n */\r\n public optimizeUrl(url: string, options: OptimizedImageOptions) {\r\n if (this.isBlacklisted(url)) {\r\n return url;\r\n }\r\n\r\n // Final format should look similar to: https://imagedelivery.net/<ACCOUNT_HASH>/<IMAGE_ID>/w=400,sharpen=3\r\n return url.replace(\"public\", this.createImageOptionsString(options));\r\n }\r\n\r\n public optimizeId(id: string, options: OptimizedImageOptions) {\r\n return this.optimizeUrl(this.url(id), options);\r\n }\r\n\r\n public createOptionsSearchParams(options: OptimizedImageOptions) {\r\n const params = new URLSearchParams();\r\n\r\n const pairs = Object.entries(options);\r\n\r\n for (const [key, val] of pairs) {\r\n if (val === undefined) {\r\n continue;\r\n }\r\n\r\n params.set(key, val.toString());\r\n }\r\n\r\n return params;\r\n }\r\n\r\n public createImageOptionsString(options: OptimizedImageOptions) {\r\n const params = this.createOptionsSearchParams(options);\r\n\r\n return Array.from(params.entries())\r\n .map(([key, val]) => `${key}=${val}`)\r\n .join(\",\");\r\n }\r\n\r\n public async createUploadUrls(count: number, args: { apiKey: string }) {\r\n if (count === 0) {\r\n return [];\r\n }\r\n\r\n const headers = new Headers();\r\n headers.set(\"Authorization\", `Bearer ${args.apiKey}`);\r\n\r\n const urls = await Promise.all(\r\n Array.from({ length: count }).map(async () => {\r\n try {\r\n const form = new FormData();\r\n const id = createId();\r\n form.append(\"id\", id);\r\n form.append(\"expiry\", dayjs().add(5, \"minute\").toISOString());\r\n\r\n const img = await ofetch<CreateImageUrlResponse>(\r\n `https://api.cloudflare.com/client/v4/accounts/${this.accountHash}/images/v2/direct_upload`,\r\n { method: \"POST\", headers, body: form }\r\n );\r\n\r\n if (!img.success) {\r\n throw new Error(\"Error uploading image\");\r\n }\r\n\r\n return { url: img.result.uploadURL, id };\r\n } catch (e) {\r\n console.error(\"Error uploading image\");\r\n throw e;\r\n }\r\n })\r\n );\r\n\r\n return urls;\r\n }\r\n\r\n public async clientUpload(url: string, body: FormData) {\r\n const fetchResponse = await ofetch<UploadImageResponse>(url, {\r\n method: \"POST\",\r\n body,\r\n });\r\n\r\n if (!fetchResponse.success) {\r\n throw new Error(\"Failed to upload image\");\r\n }\r\n\r\n const downloadUrl = fetchResponse.result.variants[0];\r\n\r\n if (!downloadUrl) {\r\n throw new Error(\"Could not find download URL\");\r\n }\r\n\r\n return downloadUrl;\r\n }\r\n\r\n public async upload(data: Blob, args: { apiKey: string; id?: string }) {\r\n const formData = new FormData();\r\n formData.set(\"file\", data, \"file.png\");\r\n formData.set(\"id\", args.id ?? createId());\r\n\r\n const headers = new Headers();\r\n headers.set(\"Authorization\", `Bearer ${args.apiKey}`);\r\n\r\n const response = await ofetch<UploadImageResponse>(\r\n `https://api.cloudflare.com/client/v4/accounts/${this.accountHash}/images/v1`,\r\n {\r\n method: \"POST\",\r\n headers,\r\n body: formData,\r\n }\r\n );\r\n\r\n return response;\r\n }\r\n\r\n public async delete(id: string, args: { apiKey: string }) {\r\n if (this.isProtected(id)) {\r\n return { success: true };\r\n }\r\n\r\n try {\r\n const headers = new Headers();\r\n headers.set(\"Authorization\", `Bearer ${args.apiKey}`);\r\n\r\n await ofetch(\r\n `https://api.cloudflare.com/client/v4/accounts/${this.accountHash}/images/v1/${id}`,\r\n {\r\n method: \"POST\",\r\n headers,\r\n }\r\n );\r\n return { success: true };\r\n } catch (_e) {\r\n return { success: false };\r\n }\r\n }\r\n\r\n public async batchUpload(\r\n files: { file: File; url: { id: string; value: string } }[]\r\n ) {\r\n return await Promise.all(\r\n files.map(async (e) => {\r\n const formData = new FormData();\r\n formData.append(\"file\", e.file);\r\n\r\n const downloadUrl = await this.clientUpload(e.url.value, formData);\r\n\r\n return {\r\n url: downloadUrl,\r\n id: e.url.id,\r\n };\r\n })\r\n );\r\n }\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAyB;AACzB,mBAAkB;AAClB,oBAAuB;AA4ChB,IAAM,aAAN,MAAuD;AAAA,EACpD,YAAsB,CAAC,eAAe;AAAA,EACtC;AAAA,EACA;AAAA,EAER,YAAY,MAIT;AACD,SAAK,eAAe,KAAK;AAEzB,SAAK,YAAY,KAAK;AAEtB,QAAI,KAAK,WAAW;AAClB,WAAK,UAAU,KAAK,GAAG,KAAK,SAAS;AAAA,IACvC;AAAA,EACF;AAAA,EAEA,IAAI,WAAW;AACb,QAAI,CAAC,KAAK,WAAW;AACnB,YAAM,IAAI,MAAM,0CAA0C;AAAA,IAC5D;AAEA,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,cAAc;AAChB,WAAO,KAAK;AAAA,EACd;AAAA,EAEO,IAAI,IAAY;AACrB,WAAO,6BAA6B,KAAK,WAAW,IAAI,EAAE;AAAA,EAC5D;AAAA,EAEQ,cAAc,KAAa;AACjC,WAAO,KAAK,UAAU,KAAK,CAAC,MAAM,IAAI,SAAS,CAAC,CAAC;AAAA,EACnD;AAAA,EAEQ,YAAY,IAAY;AAC9B,QAAI,CAAC,KAAK,WAAW;AACnB,aAAO;AAAA,IACT;AAEA,WAAO,OAAO,OAAO,KAAK,SAAS,EAAE,KAAK,CAAC,MAAM,MAAM,EAAE;AAAA,EAC3D;AAAA;AAAA;AAAA;AAAA,EAKO,YAAY,KAAa,SAAgC;AAC9D,QAAI,KAAK,cAAc,GAAG,GAAG;AAC3B,aAAO;AAAA,IACT;AAGA,WAAO,IAAI,QAAQ,UAAU,KAAK,yBAAyB,OAAO,CAAC;AAAA,EACrE;AAAA,EAEO,WAAW,IAAY,SAAgC;AAC5D,WAAO,KAAK,YAAY,KAAK,IAAI,EAAE,GAAG,OAAO;AAAA,EAC/C;AAAA,EAEO,0BAA0B,SAAgC;AAC/D,UAAM,SAAS,IAAI,gBAAgB;AAEnC,UAAM,QAAQ,OAAO,QAAQ,OAAO;AAEpC,eAAW,CAAC,KAAK,GAAG,KAAK,OAAO;AAC9B,UAAI,QAAQ,QAAW;AACrB;AAAA,MACF;AAEA,aAAO,IAAI,KAAK,IAAI,SAAS,CAAC;AAAA,IAChC;AAEA,WAAO;AAAA,EACT;AAAA,EAEO,yBAAyB,SAAgC;AAC9D,UAAM,SAAS,KAAK,0BAA0B,OAAO;AAErD,WAAO,MAAM,KAAK,OAAO,QAAQ,CAAC,EAC/B,IAAI,CAAC,CAAC,KAAK,GAAG,MAAM,GAAG,GAAG,IAAI,GAAG,EAAE,EACnC,KAAK,GAAG;AAAA,EACb;AAAA,EAEA,MAAa,iBAAiB,OAAe,MAA0B;AACrE,QAAI,UAAU,GAAG;AACf,aAAO,CAAC;AAAA,IACV;AAEA,UAAM,UAAU,IAAI,QAAQ;AAC5B,YAAQ,IAAI,iBAAiB,UAAU,KAAK,MAAM,EAAE;AAEpD,UAAM,OAAO,MAAM,QAAQ;AAAA,MACzB,MAAM,KAAK,EAAE,QAAQ,MAAM,CAAC,EAAE,IAAI,YAAY;AAC5C,YAAI;AACF,gBAAM,OAAO,IAAI,SAAS;AAC1B,gBAAM,SAAK,uBAAS;AACpB,eAAK,OAAO,MAAM,EAAE;AACpB,eAAK,OAAO,cAAU,aAAAA,SAAM,EAAE,IAAI,GAAG,QAAQ,EAAE,YAAY,CAAC;AAE5D,gBAAM,MAAM,UAAM;AAAA,YAChB,iDAAiD,KAAK,WAAW;AAAA,YACjE,EAAE,QAAQ,QAAQ,SAAS,MAAM,KAAK;AAAA,UACxC;AAEA,cAAI,CAAC,IAAI,SAAS;AAChB,kBAAM,IAAI,MAAM,uBAAuB;AAAA,UACzC;AAEA,iBAAO,EAAE,KAAK,IAAI,OAAO,WAAW,GAAG;AAAA,QACzC,SAAS,GAAG;AACV,kBAAQ,MAAM,uBAAuB;AACrC,gBAAM;AAAA,QACR;AAAA,MACF,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAa,aAAa,KAAa,MAAgB;AACrD,UAAM,gBAAgB,UAAM,sBAA4B,KAAK;AAAA,MAC3D,QAAQ;AAAA,MACR;AAAA,IACF,CAAC;AAED,QAAI,CAAC,cAAc,SAAS;AAC1B,YAAM,IAAI,MAAM,wBAAwB;AAAA,IAC1C;AAEA,UAAM,cAAc,cAAc,OAAO,SAAS,CAAC;AAEnD,QAAI,CAAC,aAAa;AAChB,YAAM,IAAI,MAAM,6BAA6B;AAAA,IAC/C;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAa,OAAO,MAAY,MAAuC;AACrE,UAAM,WAAW,IAAI,SAAS;AAC9B,aAAS,IAAI,QAAQ,MAAM,UAAU;AACrC,aAAS,IAAI,MAAM,KAAK,UAAM,uBAAS,CAAC;AAExC,UAAM,UAAU,IAAI,QAAQ;AAC5B,YAAQ,IAAI,iBAAiB,UAAU,KAAK,MAAM,EAAE;AAEpD,UAAM,WAAW,UAAM;AAAA,MACrB,iDAAiD,KAAK,WAAW;AAAA,MACjE;AAAA,QACE,QAAQ;AAAA,QACR;AAAA,QACA,MAAM;AAAA,MACR;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAa,OAAO,IAAY,MAA0B;AACxD,QAAI,KAAK,YAAY,EAAE,GAAG;AACxB,aAAO,EAAE,SAAS,KAAK;AAAA,IACzB;AAEA,QAAI;AACF,YAAM,UAAU,IAAI,QAAQ;AAC5B,cAAQ,IAAI,iBAAiB,UAAU,KAAK,MAAM,EAAE;AAEpD,gBAAM;AAAA,QACJ,iDAAiD,KAAK,WAAW,cAAc,EAAE;AAAA,QACjF;AAAA,UACE,QAAQ;AAAA,UACR;AAAA,QACF;AAAA,MACF;AACA,aAAO,EAAE,SAAS,KAAK;AAAA,IACzB,SAAS,IAAI;AACX,aAAO,EAAE,SAAS,MAAM;AAAA,IAC1B;AAAA,EACF;AAAA,EAEA,MAAa,YACX,OACA;AACA,WAAO,MAAM,QAAQ;AAAA,MACnB,MAAM,IAAI,OAAO,MAAM;AACrB,cAAM,WAAW,IAAI,SAAS;AAC9B,iBAAS,OAAO,QAAQ,EAAE,IAAI;AAE9B,cAAM,cAAc,MAAM,KAAK,aAAa,EAAE,IAAI,OAAO,QAAQ;AAEjE,eAAO;AAAA,UACL,KAAK;AAAA,UACL,IAAI,EAAE,IAAI;AAAA,QACZ;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AACF;","names":["dayjs"]}
package/dist/images.d.cts CHANGED
@@ -22,7 +22,25 @@ interface CreateImageUrlResponse {
22
22
  uploadURL: string;
23
23
  };
24
24
  success: boolean;
25
- errors: unknown[];
25
+ errors: {
26
+ code: string;
27
+ message: string;
28
+ }[];
29
+ messages: unknown[];
30
+ }
31
+ interface UploadImageResponse {
32
+ result: {
33
+ id: string;
34
+ filename: string;
35
+ uploaded: string;
36
+ requireSignedURLs: boolean;
37
+ variants: string[];
38
+ };
39
+ success: boolean;
40
+ errors: {
41
+ code: string;
42
+ message: string;
43
+ }[];
26
44
  messages: unknown[];
27
45
  }
28
46
  declare class ImageUtils<ImageIds extends Record<string, any>> {
@@ -56,7 +74,7 @@ declare class ImageUtils<ImageIds extends Record<string, any>> {
56
74
  upload(data: Blob, args: {
57
75
  apiKey: string;
58
76
  id?: string;
59
- }): Promise<any>;
77
+ }): Promise<UploadImageResponse>;
60
78
  delete(id: string, args: {
61
79
  apiKey: string;
62
80
  }): Promise<{
package/dist/images.d.ts CHANGED
@@ -22,7 +22,25 @@ interface CreateImageUrlResponse {
22
22
  uploadURL: string;
23
23
  };
24
24
  success: boolean;
25
- errors: unknown[];
25
+ errors: {
26
+ code: string;
27
+ message: string;
28
+ }[];
29
+ messages: unknown[];
30
+ }
31
+ interface UploadImageResponse {
32
+ result: {
33
+ id: string;
34
+ filename: string;
35
+ uploaded: string;
36
+ requireSignedURLs: boolean;
37
+ variants: string[];
38
+ };
39
+ success: boolean;
40
+ errors: {
41
+ code: string;
42
+ message: string;
43
+ }[];
26
44
  messages: unknown[];
27
45
  }
28
46
  declare class ImageUtils<ImageIds extends Record<string, any>> {
@@ -56,7 +74,7 @@ declare class ImageUtils<ImageIds extends Record<string, any>> {
56
74
  upload(data: Blob, args: {
57
75
  apiKey: string;
58
76
  id?: string;
59
- }): Promise<any>;
77
+ }): Promise<UploadImageResponse>;
60
78
  delete(id: string, args: {
61
79
  apiKey: string;
62
80
  }): Promise<{
package/dist/images.js CHANGED
@@ -110,7 +110,7 @@ var ImageUtils = class {
110
110
  formData.set("id", args.id ?? createId());
111
111
  const headers = new Headers();
112
112
  headers.set("Authorization", `Bearer ${args.apiKey}`);
113
- const response = await fetch(
113
+ const response = await ofetch(
114
114
  `https://api.cloudflare.com/client/v4/accounts/${this.accountHash}/images/v1`,
115
115
  {
116
116
  method: "POST",
@@ -118,7 +118,7 @@ var ImageUtils = class {
118
118
  body: formData
119
119
  }
120
120
  );
121
- return response.json();
121
+ return response;
122
122
  }
123
123
  async delete(id, args) {
124
124
  if (this.isProtected(id)) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/images.ts"],"sourcesContent":["import { createId } from \"@paralleldrive/cuid2\";\r\nimport dayjs from \"dayjs\";\r\nimport { ofetch } from \"ofetch\";\r\n\r\nexport interface OptimizedImageOptions {\r\n anim?: boolean;\r\n background?: string;\r\n blur?: number;\r\n brightness?: number;\r\n compression?: \"fast\"; // faster compression = larger file size\r\n contrast?: number;\r\n dpr?: number;\r\n fit?: \"scale-down\" | \"contain\" | \"cover\" | \"crop\" | \"pad\";\r\n format?: \"webp\" | \"avif\" | \"json\";\r\n gamma?: number;\r\n width?: number;\r\n height?: number;\r\n metadata?: \"keep\" | \"copyright\" | \"none\";\r\n quality?: number;\r\n rotate?: number;\r\n sharpen?: number;\r\n}\r\n\r\nexport interface CreateImageUrlResponse {\r\n result: {\r\n id: string;\r\n uploadURL: string;\r\n };\r\n success: boolean;\r\n errors: unknown[];\r\n messages: unknown[];\r\n}\r\n\r\ninterface UploadImageResponse {\r\n result: {\r\n id: string;\r\n filename: string;\r\n uploaded: string;\r\n requireSignedURLs: boolean;\r\n variants: string[];\r\n };\r\n success: boolean;\r\n errors: unknown[];\r\n messages: unknown[];\r\n}\r\n\r\nexport class ImageUtils<ImageIds extends Record<string, any>> {\r\n private blacklist: string[] = [\"img.clerk.com\"];\r\n private _accountHash: string;\r\n private _imageIds: ImageIds | undefined;\r\n\r\n constructor(args: {\r\n accountHash: string;\r\n blacklist?: string[];\r\n imageIds?: ImageIds;\r\n }) {\r\n this._accountHash = args.accountHash;\r\n\r\n this._imageIds = args.imageIds;\r\n\r\n if (args.blacklist) {\r\n this.blacklist.push(...args.blacklist);\r\n }\r\n }\r\n\r\n get imageIds() {\r\n if (!this._imageIds) {\r\n throw new Error(\"imageIds was not supplied in constructor\");\r\n }\r\n\r\n return this._imageIds;\r\n }\r\n\r\n get accountHash() {\r\n return this._accountHash;\r\n }\r\n\r\n public url(id: string) {\r\n return `https://imagedelivery.net/${this.accountHash}/${id}/public`;\r\n }\r\n\r\n private isBlacklisted(url: string) {\r\n return this.blacklist.some((u) => url.includes(u));\r\n }\r\n\r\n private isProtected(id: string) {\r\n if (!this._imageIds) {\r\n return false;\r\n }\r\n\r\n return Object.values(this._imageIds).some((e) => e === id);\r\n }\r\n\r\n /**\r\n * Will only operate on images that have been uploaded via cloudflare images\r\n */\r\n public optimizeUrl(url: string, options: OptimizedImageOptions) {\r\n if (this.isBlacklisted(url)) {\r\n return url;\r\n }\r\n\r\n // Final format should look similar to: https://imagedelivery.net/<ACCOUNT_HASH>/<IMAGE_ID>/w=400,sharpen=3\r\n return url.replace(\"public\", this.createImageOptionsString(options));\r\n }\r\n\r\n public optimizeId(id: string, options: OptimizedImageOptions) {\r\n return this.optimizeUrl(this.url(id), options);\r\n }\r\n\r\n public createOptionsSearchParams(options: OptimizedImageOptions) {\r\n const params = new URLSearchParams();\r\n\r\n const pairs = Object.entries(options);\r\n\r\n for (const [key, val] of pairs) {\r\n if (val === undefined) {\r\n continue;\r\n }\r\n\r\n params.set(key, val.toString());\r\n }\r\n\r\n return params;\r\n }\r\n\r\n public createImageOptionsString(options: OptimizedImageOptions) {\r\n const params = this.createOptionsSearchParams(options);\r\n\r\n return Array.from(params.entries())\r\n .map(([key, val]) => `${key}=${val}`)\r\n .join(\",\");\r\n }\r\n\r\n public async createUploadUrls(count: number, args: { apiKey: string }) {\r\n if (count === 0) {\r\n return [];\r\n }\r\n\r\n const headers = new Headers();\r\n headers.set(\"Authorization\", `Bearer ${args.apiKey}`);\r\n\r\n const urls = await Promise.all(\r\n Array.from({ length: count }).map(async () => {\r\n try {\r\n const form = new FormData();\r\n const id = createId();\r\n form.append(\"id\", id);\r\n form.append(\"expiry\", dayjs().add(5, \"minute\").toISOString());\r\n\r\n const img = await ofetch<CreateImageUrlResponse>(\r\n `https://api.cloudflare.com/client/v4/accounts/${this.accountHash}/images/v2/direct_upload`,\r\n { method: \"POST\", headers, body: form }\r\n );\r\n\r\n if (!img.success) {\r\n throw new Error(\"Error uploading image\");\r\n }\r\n\r\n return { url: img.result.uploadURL, id };\r\n } catch (e) {\r\n console.error(\"Error uploading image\");\r\n throw e;\r\n }\r\n })\r\n );\r\n\r\n return urls;\r\n }\r\n\r\n public async clientUpload(url: string, body: FormData) {\r\n const fetchResponse = await ofetch<UploadImageResponse>(url, {\r\n method: \"POST\",\r\n body,\r\n });\r\n\r\n if (!fetchResponse.success) {\r\n throw new Error(\"Failed to upload image\");\r\n }\r\n\r\n const downloadUrl = fetchResponse.result.variants[0];\r\n\r\n if (!downloadUrl) {\r\n throw new Error(\"Could not find download URL\");\r\n }\r\n\r\n return downloadUrl;\r\n }\r\n\r\n public async upload(data: Blob, args: { apiKey: string; id?: string }) {\r\n const formData = new FormData();\r\n formData.set(\"file\", data, \"file.png\");\r\n formData.set(\"id\", args.id ?? createId());\r\n\r\n const headers = new Headers();\r\n headers.set(\"Authorization\", `Bearer ${args.apiKey}`);\r\n\r\n const response = await fetch(\r\n `https://api.cloudflare.com/client/v4/accounts/${this.accountHash}/images/v1`,\r\n {\r\n method: \"POST\",\r\n headers,\r\n body: formData,\r\n }\r\n );\r\n\r\n return response.json();\r\n }\r\n\r\n public async delete(id: string, args: { apiKey: string }) {\r\n if (this.isProtected(id)) {\r\n return { success: true };\r\n }\r\n\r\n try {\r\n const headers = new Headers();\r\n headers.set(\"Authorization\", `Bearer ${args.apiKey}`);\r\n\r\n await ofetch(\r\n `https://api.cloudflare.com/client/v4/accounts/${this.accountHash}/images/v1/${id}`,\r\n {\r\n method: \"POST\",\r\n headers,\r\n }\r\n );\r\n return { success: true };\r\n } catch (_e) {\r\n return { success: false };\r\n }\r\n }\r\n\r\n public async batchUpload(\r\n files: { file: File; url: { id: string; value: string } }[]\r\n ) {\r\n return await Promise.all(\r\n files.map(async (e) => {\r\n const formData = new FormData();\r\n formData.append(\"file\", e.file);\r\n\r\n const downloadUrl = await this.clientUpload(e.url.value, formData);\r\n\r\n return {\r\n url: downloadUrl,\r\n id: e.url.id,\r\n };\r\n })\r\n );\r\n }\r\n}\r\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/images.ts"],"sourcesContent":["import { createId } from \"@paralleldrive/cuid2\";\r\nimport dayjs from \"dayjs\";\r\nimport { ofetch } from \"ofetch\";\r\n\r\nexport interface OptimizedImageOptions {\r\n anim?: boolean;\r\n background?: string;\r\n blur?: number;\r\n brightness?: number;\r\n compression?: \"fast\"; // faster compression = larger file size\r\n contrast?: number;\r\n dpr?: number;\r\n fit?: \"scale-down\" | \"contain\" | \"cover\" | \"crop\" | \"pad\";\r\n format?: \"webp\" | \"avif\" | \"json\";\r\n gamma?: number;\r\n width?: number;\r\n height?: number;\r\n metadata?: \"keep\" | \"copyright\" | \"none\";\r\n quality?: number;\r\n rotate?: number;\r\n sharpen?: number;\r\n}\r\n\r\nexport interface CreateImageUrlResponse {\r\n result: {\r\n id: string;\r\n uploadURL: string;\r\n };\r\n success: boolean;\r\n errors: { code: string; message: string }[];\r\n messages: unknown[];\r\n}\r\n\r\ninterface UploadImageResponse {\r\n result: {\r\n id: string;\r\n filename: string;\r\n uploaded: string;\r\n requireSignedURLs: boolean;\r\n variants: string[];\r\n };\r\n success: boolean;\r\n errors: { code: string; message: string }[];\r\n messages: unknown[];\r\n}\r\n\r\nexport class ImageUtils<ImageIds extends Record<string, any>> {\r\n private blacklist: string[] = [\"img.clerk.com\"];\r\n private _accountHash: string;\r\n private _imageIds: ImageIds | undefined;\r\n\r\n constructor(args: {\r\n accountHash: string;\r\n blacklist?: string[];\r\n imageIds?: ImageIds;\r\n }) {\r\n this._accountHash = args.accountHash;\r\n\r\n this._imageIds = args.imageIds;\r\n\r\n if (args.blacklist) {\r\n this.blacklist.push(...args.blacklist);\r\n }\r\n }\r\n\r\n get imageIds() {\r\n if (!this._imageIds) {\r\n throw new Error(\"imageIds was not supplied in constructor\");\r\n }\r\n\r\n return this._imageIds;\r\n }\r\n\r\n get accountHash() {\r\n return this._accountHash;\r\n }\r\n\r\n public url(id: string) {\r\n return `https://imagedelivery.net/${this.accountHash}/${id}/public`;\r\n }\r\n\r\n private isBlacklisted(url: string) {\r\n return this.blacklist.some((u) => url.includes(u));\r\n }\r\n\r\n private isProtected(id: string) {\r\n if (!this._imageIds) {\r\n return false;\r\n }\r\n\r\n return Object.values(this._imageIds).some((e) => e === id);\r\n }\r\n\r\n /**\r\n * Will only operate on images that have been uploaded via cloudflare images\r\n */\r\n public optimizeUrl(url: string, options: OptimizedImageOptions) {\r\n if (this.isBlacklisted(url)) {\r\n return url;\r\n }\r\n\r\n // Final format should look similar to: https://imagedelivery.net/<ACCOUNT_HASH>/<IMAGE_ID>/w=400,sharpen=3\r\n return url.replace(\"public\", this.createImageOptionsString(options));\r\n }\r\n\r\n public optimizeId(id: string, options: OptimizedImageOptions) {\r\n return this.optimizeUrl(this.url(id), options);\r\n }\r\n\r\n public createOptionsSearchParams(options: OptimizedImageOptions) {\r\n const params = new URLSearchParams();\r\n\r\n const pairs = Object.entries(options);\r\n\r\n for (const [key, val] of pairs) {\r\n if (val === undefined) {\r\n continue;\r\n }\r\n\r\n params.set(key, val.toString());\r\n }\r\n\r\n return params;\r\n }\r\n\r\n public createImageOptionsString(options: OptimizedImageOptions) {\r\n const params = this.createOptionsSearchParams(options);\r\n\r\n return Array.from(params.entries())\r\n .map(([key, val]) => `${key}=${val}`)\r\n .join(\",\");\r\n }\r\n\r\n public async createUploadUrls(count: number, args: { apiKey: string }) {\r\n if (count === 0) {\r\n return [];\r\n }\r\n\r\n const headers = new Headers();\r\n headers.set(\"Authorization\", `Bearer ${args.apiKey}`);\r\n\r\n const urls = await Promise.all(\r\n Array.from({ length: count }).map(async () => {\r\n try {\r\n const form = new FormData();\r\n const id = createId();\r\n form.append(\"id\", id);\r\n form.append(\"expiry\", dayjs().add(5, \"minute\").toISOString());\r\n\r\n const img = await ofetch<CreateImageUrlResponse>(\r\n `https://api.cloudflare.com/client/v4/accounts/${this.accountHash}/images/v2/direct_upload`,\r\n { method: \"POST\", headers, body: form }\r\n );\r\n\r\n if (!img.success) {\r\n throw new Error(\"Error uploading image\");\r\n }\r\n\r\n return { url: img.result.uploadURL, id };\r\n } catch (e) {\r\n console.error(\"Error uploading image\");\r\n throw e;\r\n }\r\n })\r\n );\r\n\r\n return urls;\r\n }\r\n\r\n public async clientUpload(url: string, body: FormData) {\r\n const fetchResponse = await ofetch<UploadImageResponse>(url, {\r\n method: \"POST\",\r\n body,\r\n });\r\n\r\n if (!fetchResponse.success) {\r\n throw new Error(\"Failed to upload image\");\r\n }\r\n\r\n const downloadUrl = fetchResponse.result.variants[0];\r\n\r\n if (!downloadUrl) {\r\n throw new Error(\"Could not find download URL\");\r\n }\r\n\r\n return downloadUrl;\r\n }\r\n\r\n public async upload(data: Blob, args: { apiKey: string; id?: string }) {\r\n const formData = new FormData();\r\n formData.set(\"file\", data, \"file.png\");\r\n formData.set(\"id\", args.id ?? createId());\r\n\r\n const headers = new Headers();\r\n headers.set(\"Authorization\", `Bearer ${args.apiKey}`);\r\n\r\n const response = await ofetch<UploadImageResponse>(\r\n `https://api.cloudflare.com/client/v4/accounts/${this.accountHash}/images/v1`,\r\n {\r\n method: \"POST\",\r\n headers,\r\n body: formData,\r\n }\r\n );\r\n\r\n return response;\r\n }\r\n\r\n public async delete(id: string, args: { apiKey: string }) {\r\n if (this.isProtected(id)) {\r\n return { success: true };\r\n }\r\n\r\n try {\r\n const headers = new Headers();\r\n headers.set(\"Authorization\", `Bearer ${args.apiKey}`);\r\n\r\n await ofetch(\r\n `https://api.cloudflare.com/client/v4/accounts/${this.accountHash}/images/v1/${id}`,\r\n {\r\n method: \"POST\",\r\n headers,\r\n }\r\n );\r\n return { success: true };\r\n } catch (_e) {\r\n return { success: false };\r\n }\r\n }\r\n\r\n public async batchUpload(\r\n files: { file: File; url: { id: string; value: string } }[]\r\n ) {\r\n return await Promise.all(\r\n files.map(async (e) => {\r\n const formData = new FormData();\r\n formData.append(\"file\", e.file);\r\n\r\n const downloadUrl = await this.clientUpload(e.url.value, formData);\r\n\r\n return {\r\n url: downloadUrl,\r\n id: e.url.id,\r\n };\r\n })\r\n );\r\n }\r\n}\r\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -53,7 +53,7 @@
53
53
  "tsup": "^8.0.1",
54
54
  "typescript": "^5.4.5"
55
55
  },
56
- "version": "0.0.23",
56
+ "version": "0.0.24",
57
57
  "scripts": {
58
58
  "dev": "tsup --watch",
59
59
  "build": "tsc --noEmit && tsup",