@conorroberts/utils 0.0.22 → 0.0.23

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
@@ -38,10 +38,10 @@ var import_dayjs = __toESM(require("dayjs"), 1);
38
38
  var import_ofetch = require("ofetch");
39
39
  var ImageUtils = class {
40
40
  blacklist = ["img.clerk.com"];
41
- _accountId;
41
+ _accountHash;
42
42
  _imageIds;
43
43
  constructor(args) {
44
- this._accountId = args.accountId;
44
+ this._accountHash = args.accountHash;
45
45
  this._imageIds = args.imageIds;
46
46
  if (args.blacklist) {
47
47
  this.blacklist.push(...args.blacklist);
@@ -53,11 +53,11 @@ var ImageUtils = class {
53
53
  }
54
54
  return this._imageIds;
55
55
  }
56
- get accountId() {
57
- return this._accountId;
56
+ get accountHash() {
57
+ return this._accountHash;
58
58
  }
59
59
  url(id) {
60
- return `https://imagedelivery.net/${this.accountId}/${id}/public`;
60
+ return `https://imagedelivery.net/${this.accountHash}/${id}/public`;
61
61
  }
62
62
  isBlacklisted(url) {
63
63
  return this.blacklist.some((u) => url.includes(u));
@@ -109,7 +109,7 @@ var ImageUtils = class {
109
109
  form.append("id", id);
110
110
  form.append("expiry", (0, import_dayjs.default)().add(5, "minute").toISOString());
111
111
  const img = await (0, import_ofetch.ofetch)(
112
- `https://api.cloudflare.com/client/v4/accounts/${this.accountId}/images/v2/direct_upload`,
112
+ `https://api.cloudflare.com/client/v4/accounts/${this.accountHash}/images/v2/direct_upload`,
113
113
  { method: "POST", headers, body: form }
114
114
  );
115
115
  if (!img.success) {
@@ -145,7 +145,7 @@ var ImageUtils = class {
145
145
  const headers = new Headers();
146
146
  headers.set("Authorization", `Bearer ${args.apiKey}`);
147
147
  const response = await fetch(
148
- `https://api.cloudflare.com/client/v4/accounts/${this.accountId}/images/v1`,
148
+ `https://api.cloudflare.com/client/v4/accounts/${this.accountHash}/images/v1`,
149
149
  {
150
150
  method: "POST",
151
151
  headers,
@@ -162,7 +162,7 @@ var ImageUtils = class {
162
162
  const headers = new Headers();
163
163
  headers.set("Authorization", `Bearer ${args.apiKey}`);
164
164
  await (0, import_ofetch.ofetch)(
165
- `https://api.cloudflare.com/client/v4/accounts/${this.accountId}/images/v1/${id}`,
165
+ `https://api.cloudflare.com/client/v4/accounts/${this.accountHash}/images/v1/${id}`,
166
166
  {
167
167
  method: "POST",
168
168
  headers
@@ -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 _accountId: string;\r\n private _imageIds: ImageIds | undefined;\r\n\r\n constructor(args: {\r\n accountId: string;\r\n blacklist?: string[];\r\n imageIds?: ImageIds;\r\n }) {\r\n this._accountId = args.accountId;\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 accountId() {\r\n return this._accountId;\r\n }\r\n\r\n public url(id: string) {\r\n return `https://imagedelivery.net/${this.accountId}/${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.accountId}/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.accountId}/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.accountId}/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: 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"]}
package/dist/images.d.cts CHANGED
@@ -27,15 +27,15 @@ interface CreateImageUrlResponse {
27
27
  }
28
28
  declare class ImageUtils<ImageIds extends Record<string, any>> {
29
29
  private blacklist;
30
- private _accountId;
30
+ private _accountHash;
31
31
  private _imageIds;
32
32
  constructor(args: {
33
- accountId: string;
33
+ accountHash: string;
34
34
  blacklist?: string[];
35
35
  imageIds?: ImageIds;
36
36
  });
37
37
  get imageIds(): ImageIds;
38
- get accountId(): string;
38
+ get accountHash(): string;
39
39
  url(id: string): string;
40
40
  private isBlacklisted;
41
41
  private isProtected;
package/dist/images.d.ts CHANGED
@@ -27,15 +27,15 @@ interface CreateImageUrlResponse {
27
27
  }
28
28
  declare class ImageUtils<ImageIds extends Record<string, any>> {
29
29
  private blacklist;
30
- private _accountId;
30
+ private _accountHash;
31
31
  private _imageIds;
32
32
  constructor(args: {
33
- accountId: string;
33
+ accountHash: string;
34
34
  blacklist?: string[];
35
35
  imageIds?: ImageIds;
36
36
  });
37
37
  get imageIds(): ImageIds;
38
- get accountId(): string;
38
+ get accountHash(): string;
39
39
  url(id: string): string;
40
40
  private isBlacklisted;
41
41
  private isProtected;
package/dist/images.js CHANGED
@@ -4,10 +4,10 @@ import dayjs from "dayjs";
4
4
  import { ofetch } from "ofetch";
5
5
  var ImageUtils = class {
6
6
  blacklist = ["img.clerk.com"];
7
- _accountId;
7
+ _accountHash;
8
8
  _imageIds;
9
9
  constructor(args) {
10
- this._accountId = args.accountId;
10
+ this._accountHash = args.accountHash;
11
11
  this._imageIds = args.imageIds;
12
12
  if (args.blacklist) {
13
13
  this.blacklist.push(...args.blacklist);
@@ -19,11 +19,11 @@ var ImageUtils = class {
19
19
  }
20
20
  return this._imageIds;
21
21
  }
22
- get accountId() {
23
- return this._accountId;
22
+ get accountHash() {
23
+ return this._accountHash;
24
24
  }
25
25
  url(id) {
26
- return `https://imagedelivery.net/${this.accountId}/${id}/public`;
26
+ return `https://imagedelivery.net/${this.accountHash}/${id}/public`;
27
27
  }
28
28
  isBlacklisted(url) {
29
29
  return this.blacklist.some((u) => url.includes(u));
@@ -75,7 +75,7 @@ var ImageUtils = class {
75
75
  form.append("id", id);
76
76
  form.append("expiry", dayjs().add(5, "minute").toISOString());
77
77
  const img = await ofetch(
78
- `https://api.cloudflare.com/client/v4/accounts/${this.accountId}/images/v2/direct_upload`,
78
+ `https://api.cloudflare.com/client/v4/accounts/${this.accountHash}/images/v2/direct_upload`,
79
79
  { method: "POST", headers, body: form }
80
80
  );
81
81
  if (!img.success) {
@@ -111,7 +111,7 @@ var ImageUtils = class {
111
111
  const headers = new Headers();
112
112
  headers.set("Authorization", `Bearer ${args.apiKey}`);
113
113
  const response = await fetch(
114
- `https://api.cloudflare.com/client/v4/accounts/${this.accountId}/images/v1`,
114
+ `https://api.cloudflare.com/client/v4/accounts/${this.accountHash}/images/v1`,
115
115
  {
116
116
  method: "POST",
117
117
  headers,
@@ -128,7 +128,7 @@ var ImageUtils = class {
128
128
  const headers = new Headers();
129
129
  headers.set("Authorization", `Bearer ${args.apiKey}`);
130
130
  await ofetch(
131
- `https://api.cloudflare.com/client/v4/accounts/${this.accountId}/images/v1/${id}`,
131
+ `https://api.cloudflare.com/client/v4/accounts/${this.accountHash}/images/v1/${id}`,
132
132
  {
133
133
  method: "POST",
134
134
  headers
@@ -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 _accountId: string;\r\n private _imageIds: ImageIds | undefined;\r\n\r\n constructor(args: {\r\n accountId: string;\r\n blacklist?: string[];\r\n imageIds?: ImageIds;\r\n }) {\r\n this._accountId = args.accountId;\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 accountId() {\r\n return this._accountId;\r\n }\r\n\r\n public url(id: string) {\r\n return `https://imagedelivery.net/${this.accountId}/${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.accountId}/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.accountId}/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.accountId}/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,SAAS,gBAAgB;AACzB,OAAO,WAAW;AAClB,SAAS,cAAc;AA4ChB,IAAM,aAAN,MAAuD;AAAA,EACpD,YAAsB,CAAC,eAAe;AAAA,EACtC;AAAA,EACA;AAAA,EAER,YAAY,MAIT;AACD,SAAK,aAAa,KAAK;AAEvB,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,YAAY;AACd,WAAO,KAAK;AAAA,EACd;AAAA,EAEO,IAAI,IAAY;AACrB,WAAO,6BAA6B,KAAK,SAAS,IAAI,EAAE;AAAA,EAC1D;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,KAAK,SAAS;AACpB,eAAK,OAAO,MAAM,EAAE;AACpB,eAAK,OAAO,UAAU,MAAM,EAAE,IAAI,GAAG,QAAQ,EAAE,YAAY,CAAC;AAE5D,gBAAM,MAAM,MAAM;AAAA,YAChB,iDAAiD,KAAK,SAAS;AAAA,YAC/D,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,MAAM,OAA4B,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,MAAM,SAAS,CAAC;AAExC,UAAM,UAAU,IAAI,QAAQ;AAC5B,YAAQ,IAAI,iBAAiB,UAAU,KAAK,MAAM,EAAE;AAEpD,UAAM,WAAW,MAAM;AAAA,MACrB,iDAAiD,KAAK,SAAS;AAAA,MAC/D;AAAA,QACE,QAAQ;AAAA,QACR;AAAA,QACA,MAAM;AAAA,MACR;AAAA,IACF;AAEA,WAAO,SAAS,KAAK;AAAA,EACvB;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,YAAM;AAAA,QACJ,iDAAiD,KAAK,SAAS,cAAc,EAAE;AAAA,QAC/E;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":[]}
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":[]}
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.22",
56
+ "version": "0.0.23",
57
57
  "scripts": {
58
58
  "dev": "tsup --watch",
59
59
  "build": "tsc --noEmit && tsup",