@conorroberts/utils 0.0.43 → 0.0.45

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.d.mts CHANGED
@@ -76,7 +76,7 @@ declare class ImageUtils<ImageIds extends Record<string, any>> {
76
76
  id: string;
77
77
  }[]>;
78
78
  serverUpload(data: Blob, args: {
79
- id: string;
79
+ id?: string;
80
80
  apiKey: string;
81
81
  }): Promise<CloudflareImagesV1Response>;
82
82
  upload(url: string, body: FormData): Promise<string>;
@@ -1 +1 @@
1
- {"version":3,"file":"images.mjs","names":[],"sources":["../src/images.ts"],"sourcesContent":["import { nanoid } from \"nanoid\";\nimport dayjs from \"dayjs\";\nimport { ofetch } from \"ofetch\";\n\nexport interface OptimizedImageOptions {\n anim?: boolean;\n background?: string;\n blur?: number;\n brightness?: number;\n compression?: \"fast\"; // faster compression = larger file size\n contrast?: number;\n dpr?: number;\n fit?: \"scale-down\" | \"contain\" | \"cover\" | \"crop\" | \"pad\";\n format?: \"webp\" | \"avif\" | \"json\";\n gamma?: number;\n width?: number;\n height?: number;\n metadata?: \"keep\" | \"copyright\" | \"none\";\n quality?: number;\n rotate?: number;\n sharpen?: number;\n}\n\nexport interface CreateImageUrlResponse {\n result: {\n id: string;\n uploadURL: string;\n };\n success: boolean;\n errors: unknown[];\n messages: unknown[];\n}\n\ninterface UploadImageResponse {\n result: {\n id: string;\n filename: string;\n uploaded: string;\n requireSignedURLs: boolean;\n variants: string[];\n };\n success: boolean;\n errors: unknown[];\n messages: unknown[];\n}\n\ninterface CloudflareImagesV1Response {\n result: {\n images: CloudflareImage[];\n };\n success: boolean;\n errors: CloudflareApiError[];\n messages: string[];\n}\n\ninterface CloudflareImage {\n id: string; // Unique image identifier\n filename: string; // Original filename\n uploaded: string; // ISO 8601 date-time string\n requireSignedURLs: boolean;\n variants: string[]; // Array of URLs for the image variants\n meta?: Record<string, any>; // User modifiable key-value store (max 1024 bytes)\n creator?: string | null; // Internal user ID (optional)\n}\n\ninterface CloudflareApiError {\n code: number;\n message: string;\n}\n\nexport class ImageUtils<ImageIds extends Record<string, any>> {\n private blacklist: string[] = [\"img.clerk.com\"];\n private accountId: string;\n private accountHash: string;\n private _imageIds: ImageIds | undefined;\n\n constructor(args: {\n accountId: string;\n accountHash: string;\n blacklist?: string[];\n imageIds?: ImageIds;\n }) {\n this.accountId = args.accountId;\n this.accountHash = args.accountHash;\n\n this._imageIds = args.imageIds;\n\n if (args.blacklist) {\n this.blacklist.push(...args.blacklist);\n }\n }\n\n get imageIds() {\n if (!this._imageIds) {\n throw new Error(\"imageIds was not supplied in constructor\");\n }\n\n return this._imageIds;\n }\n\n public url(id: string) {\n return `https://imagedelivery.net/${this.accountHash}/${id}/public`;\n }\n\n private isBlacklisted(url: string) {\n return this.blacklist.some((u) => url.includes(u));\n }\n\n private isProtected(id: string) {\n if (!this._imageIds) {\n return false;\n }\n\n return Object.values(this._imageIds).some((e) => e === id);\n }\n\n /**\n * Will only operate on images that have been uploaded via cloudflare images\n */\n public optimizeUrl(url: string, options: OptimizedImageOptions) {\n if (this.isBlacklisted(url)) {\n return url;\n }\n\n // Final format should look similar to: https://imagedelivery.net/<ACCOUNT_HASH>/<IMAGE_ID>/w=400,sharpen=3\n return url.replace(\"public\", this.createImageOptionsString(options));\n }\n\n public optimizeId(id: string, options: OptimizedImageOptions) {\n return this.optimizeUrl(this.url(id), options);\n }\n\n public createOptionsSearchParams(options: OptimizedImageOptions) {\n const params = new URLSearchParams();\n\n const pairs = Object.entries(options);\n\n for (const [key, val] of pairs) {\n if (val === undefined) {\n continue;\n }\n\n params.set(key, val.toString());\n }\n\n return params;\n }\n\n public createImageOptionsString(options: OptimizedImageOptions) {\n const params = this.createOptionsSearchParams(options);\n\n return Array.from(params.entries())\n .map(([key, val]) => `${key}=${val}`)\n .join(\",\");\n }\n\n public async createUploadUrls(count: number, args: { apiKey: string }) {\n if (count === 0) {\n return [];\n }\n\n const headers = new Headers();\n headers.set(\"Authorization\", `Bearer ${args.apiKey}`);\n\n const urls = await Promise.all(\n Array.from({ length: count }).map(async () => {\n try {\n const form = new FormData();\n const id = nanoid();\n form.append(\"id\", id);\n form.append(\"expiry\", dayjs().add(5, \"minute\").toISOString());\n\n const img = await ofetch<CreateImageUrlResponse>(\n `https://api.cloudflare.com/client/v4/accounts/${this.accountId}/images/v2/direct_upload`,\n { method: \"POST\", headers, body: form },\n );\n\n if (!img.success) {\n throw new Error(\"Error uploading image\");\n }\n\n return { url: img.result.uploadURL, id };\n } catch (e) {\n console.error(\"Error uploading image\");\n throw e;\n }\n }),\n );\n\n return urls;\n }\n\n public async serverUpload(data: Blob, args: { id: string; apiKey: string }) {\n const formData = new FormData();\n formData.append(\"file\", data, nanoid());\n\n const headers = new Headers();\n headers.set(\"Authorization\", `Bearer ${args.apiKey}`);\n\n const response = await fetch(`https://api.cloudflare.com/client/v4/accounts/${this.accountId}/images/v1`, {\n method: \"POST\",\n headers,\n body: formData,\n });\n\n const json: CloudflareImagesV1Response = await response.json();\n\n return json;\n }\n\n public async upload(url: string, body: FormData) {\n const fetchResponse = await ofetch<UploadImageResponse>(url, {\n method: \"POST\",\n body,\n });\n\n if (!fetchResponse.success) {\n throw new Error(\"Failed to upload image\");\n }\n\n const downloadUrl = fetchResponse.result.variants[0];\n\n if (!downloadUrl) {\n throw new Error(\"Could not find download URL\");\n }\n\n return downloadUrl;\n }\n\n public async delete(id: string, args: { apiKey: string }) {\n if (this.isProtected(id)) {\n return { success: true };\n }\n\n try {\n const headers = new Headers();\n headers.set(\"Authorization\", `Bearer ${args.apiKey}`);\n\n await ofetch(`https://api.cloudflare.com/client/v4/accounts/${this.accountId}/images/v1/${id}`, {\n method: \"POST\",\n headers,\n });\n return { success: true };\n } catch {\n return { success: false };\n }\n }\n\n public async batchUpload(files: { file: File; url: { id: string; value: string } }[]) {\n return await Promise.all(\n files.map(async (e) => {\n const formData = new FormData();\n formData.append(\"file\", e.file);\n\n const downloadUrl = await this.upload(e.url.value, formData);\n\n return {\n url: downloadUrl,\n id: e.url.id,\n };\n }),\n );\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"images.mjs","names":[],"sources":["../src/images.ts"],"sourcesContent":["import { nanoid } from \"nanoid\";\nimport dayjs from \"dayjs\";\nimport { ofetch } from \"ofetch\";\n\nexport interface OptimizedImageOptions {\n anim?: boolean;\n background?: string;\n blur?: number;\n brightness?: number;\n compression?: \"fast\"; // faster compression = larger file size\n contrast?: number;\n dpr?: number;\n fit?: \"scale-down\" | \"contain\" | \"cover\" | \"crop\" | \"pad\";\n format?: \"webp\" | \"avif\" | \"json\";\n gamma?: number;\n width?: number;\n height?: number;\n metadata?: \"keep\" | \"copyright\" | \"none\";\n quality?: number;\n rotate?: number;\n sharpen?: number;\n}\n\nexport interface CreateImageUrlResponse {\n result: {\n id: string;\n uploadURL: string;\n };\n success: boolean;\n errors: unknown[];\n messages: unknown[];\n}\n\ninterface UploadImageResponse {\n result: {\n id: string;\n filename: string;\n uploaded: string;\n requireSignedURLs: boolean;\n variants: string[];\n };\n success: boolean;\n errors: unknown[];\n messages: unknown[];\n}\n\ninterface CloudflareImagesV1Response {\n result: {\n images: CloudflareImage[];\n };\n success: boolean;\n errors: CloudflareApiError[];\n messages: string[];\n}\n\ninterface CloudflareImage {\n id: string; // Unique image identifier\n filename: string; // Original filename\n uploaded: string; // ISO 8601 date-time string\n requireSignedURLs: boolean;\n variants: string[]; // Array of URLs for the image variants\n meta?: Record<string, any>; // User modifiable key-value store (max 1024 bytes)\n creator?: string | null; // Internal user ID (optional)\n}\n\ninterface CloudflareApiError {\n code: number;\n message: string;\n}\n\nexport class ImageUtils<ImageIds extends Record<string, any>> {\n private blacklist: string[] = [\"img.clerk.com\"];\n private accountId: string;\n private accountHash: string;\n private _imageIds: ImageIds | undefined;\n\n constructor(args: {\n accountId: string;\n accountHash: string;\n blacklist?: string[];\n imageIds?: ImageIds;\n }) {\n this.accountId = args.accountId;\n this.accountHash = args.accountHash;\n\n this._imageIds = args.imageIds;\n\n if (args.blacklist) {\n this.blacklist.push(...args.blacklist);\n }\n }\n\n get imageIds() {\n if (!this._imageIds) {\n throw new Error(\"imageIds was not supplied in constructor\");\n }\n\n return this._imageIds;\n }\n\n public url(id: string) {\n return `https://imagedelivery.net/${this.accountHash}/${id}/public`;\n }\n\n private isBlacklisted(url: string) {\n return this.blacklist.some((u) => url.includes(u));\n }\n\n private isProtected(id: string) {\n if (!this._imageIds) {\n return false;\n }\n\n return Object.values(this._imageIds).some((e) => e === id);\n }\n\n /**\n * Will only operate on images that have been uploaded via cloudflare images\n */\n public optimizeUrl(url: string, options: OptimizedImageOptions) {\n if (this.isBlacklisted(url)) {\n return url;\n }\n\n // Final format should look similar to: https://imagedelivery.net/<ACCOUNT_HASH>/<IMAGE_ID>/w=400,sharpen=3\n return url.replace(\"public\", this.createImageOptionsString(options));\n }\n\n public optimizeId(id: string, options: OptimizedImageOptions) {\n return this.optimizeUrl(this.url(id), options);\n }\n\n public createOptionsSearchParams(options: OptimizedImageOptions) {\n const params = new URLSearchParams();\n\n const pairs = Object.entries(options);\n\n for (const [key, val] of pairs) {\n if (val === undefined) {\n continue;\n }\n\n params.set(key, val.toString());\n }\n\n return params;\n }\n\n public createImageOptionsString(options: OptimizedImageOptions) {\n const params = this.createOptionsSearchParams(options);\n\n return Array.from(params.entries())\n .map(([key, val]) => `${key}=${val}`)\n .join(\",\");\n }\n\n public async createUploadUrls(count: number, args: { apiKey: string }) {\n if (count === 0) {\n return [];\n }\n\n const headers = new Headers();\n headers.set(\"Authorization\", `Bearer ${args.apiKey}`);\n\n const urls = await Promise.all(\n Array.from({ length: count }).map(async () => {\n try {\n const form = new FormData();\n const id = nanoid();\n form.append(\"id\", id);\n form.append(\"expiry\", dayjs().add(5, \"minute\").toISOString());\n\n const img = await ofetch<CreateImageUrlResponse>(\n `https://api.cloudflare.com/client/v4/accounts/${this.accountId}/images/v2/direct_upload`,\n { method: \"POST\", headers, body: form },\n );\n\n if (!img.success) {\n throw new Error(\"Error uploading image\");\n }\n\n return { url: img.result.uploadURL, id };\n } catch (e) {\n console.error(\"Error uploading image\");\n throw e;\n }\n }),\n );\n\n return urls;\n }\n\n public async serverUpload(data: Blob, args: { id?: string; apiKey: string }) {\n const formData = new FormData();\n formData.append(\"file\", data, nanoid());\n\n const headers = new Headers();\n headers.set(\"Authorization\", `Bearer ${args.apiKey}`);\n\n const response = await fetch(`https://api.cloudflare.com/client/v4/accounts/${this.accountId}/images/v1`, {\n method: \"POST\",\n headers,\n body: formData,\n });\n\n const json: CloudflareImagesV1Response = await response.json();\n\n return json;\n }\n\n public async upload(url: string, body: FormData) {\n const fetchResponse = await ofetch<UploadImageResponse>(url, {\n method: \"POST\",\n body,\n });\n\n if (!fetchResponse.success) {\n throw new Error(\"Failed to upload image\");\n }\n\n const downloadUrl = fetchResponse.result.variants[0];\n\n if (!downloadUrl) {\n throw new Error(\"Could not find download URL\");\n }\n\n return downloadUrl;\n }\n\n public async delete(id: string, args: { apiKey: string }) {\n if (this.isProtected(id)) {\n return { success: true };\n }\n\n try {\n const headers = new Headers();\n headers.set(\"Authorization\", `Bearer ${args.apiKey}`);\n\n await ofetch(`https://api.cloudflare.com/client/v4/accounts/${this.accountId}/images/v1/${id}`, {\n method: \"POST\",\n headers,\n });\n return { success: true };\n } catch {\n return { success: false };\n }\n }\n\n public async batchUpload(files: { file: File; url: { id: string; value: string } }[]) {\n return await Promise.all(\n files.map(async (e) => {\n const formData = new FormData();\n formData.append(\"file\", e.file);\n\n const downloadUrl = await this.upload(e.url.value, formData);\n\n return {\n url: downloadUrl,\n id: e.url.id,\n };\n }),\n );\n }\n}\n"],"mappings":";;;;;AAsEA,IAAa,aAAb,MAA8D;CAC5D,AAAQ,YAAsB,CAAC,gBAAgB;CAC/C,AAAQ;CACR,AAAQ;CACR,AAAQ;CAER,YAAY,MAKT;AACD,OAAK,YAAY,KAAK;AACtB,OAAK,cAAc,KAAK;AAExB,OAAK,YAAY,KAAK;AAEtB,MAAI,KAAK,UACP,MAAK,UAAU,KAAK,GAAG,KAAK,UAAU;;CAI1C,IAAI,WAAW;AACb,MAAI,CAAC,KAAK,UACR,OAAM,IAAI,MAAM,2CAA2C;AAG7D,SAAO,KAAK;;CAGd,AAAO,IAAI,IAAY;AACrB,SAAO,6BAA6B,KAAK,YAAY,GAAG,GAAG;;CAG7D,AAAQ,cAAc,KAAa;AACjC,SAAO,KAAK,UAAU,MAAM,MAAM,IAAI,SAAS,EAAE,CAAC;;CAGpD,AAAQ,YAAY,IAAY;AAC9B,MAAI,CAAC,KAAK,UACR,QAAO;AAGT,SAAO,OAAO,OAAO,KAAK,UAAU,CAAC,MAAM,MAAM,MAAM,GAAG;;;;;CAM5D,AAAO,YAAY,KAAa,SAAgC;AAC9D,MAAI,KAAK,cAAc,IAAI,CACzB,QAAO;AAIT,SAAO,IAAI,QAAQ,UAAU,KAAK,yBAAyB,QAAQ,CAAC;;CAGtE,AAAO,WAAW,IAAY,SAAgC;AAC5D,SAAO,KAAK,YAAY,KAAK,IAAI,GAAG,EAAE,QAAQ;;CAGhD,AAAO,0BAA0B,SAAgC;EAC/D,MAAM,SAAS,IAAI,iBAAiB;EAEpC,MAAM,QAAQ,OAAO,QAAQ,QAAQ;AAErC,OAAK,MAAM,CAAC,KAAK,QAAQ,OAAO;AAC9B,OAAI,QAAQ,OACV;AAGF,UAAO,IAAI,KAAK,IAAI,UAAU,CAAC;;AAGjC,SAAO;;CAGT,AAAO,yBAAyB,SAAgC;EAC9D,MAAM,SAAS,KAAK,0BAA0B,QAAQ;AAEtD,SAAO,MAAM,KAAK,OAAO,SAAS,CAAC,CAChC,KAAK,CAAC,KAAK,SAAS,GAAG,IAAI,GAAG,MAAM,CACpC,KAAK,IAAI;;CAGd,MAAa,iBAAiB,OAAe,MAA0B;AACrE,MAAI,UAAU,EACZ,QAAO,EAAE;EAGX,MAAM,UAAU,IAAI,SAAS;AAC7B,UAAQ,IAAI,iBAAiB,UAAU,KAAK,SAAS;AA2BrD,SAzBa,MAAM,QAAQ,IACzB,MAAM,KAAK,EAAE,QAAQ,OAAO,CAAC,CAAC,IAAI,YAAY;AAC5C,OAAI;IACF,MAAM,OAAO,IAAI,UAAU;IAC3B,MAAM,KAAK,QAAQ;AACnB,SAAK,OAAO,MAAM,GAAG;AACrB,SAAK,OAAO,UAAU,OAAO,CAAC,IAAI,GAAG,SAAS,CAAC,aAAa,CAAC;IAE7D,MAAM,MAAM,MAAM,OAChB,iDAAiD,KAAK,UAAU,2BAChE;KAAE,QAAQ;KAAQ;KAAS,MAAM;KAAM,CACxC;AAED,QAAI,CAAC,IAAI,QACP,OAAM,IAAI,MAAM,wBAAwB;AAG1C,WAAO;KAAE,KAAK,IAAI,OAAO;KAAW;KAAI;YACjC,GAAG;AACV,YAAQ,MAAM,wBAAwB;AACtC,UAAM;;IAER,CACH;;CAKH,MAAa,aAAa,MAAY,MAAuC;EAC3E,MAAM,WAAW,IAAI,UAAU;AAC/B,WAAS,OAAO,QAAQ,MAAM,QAAQ,CAAC;EAEvC,MAAM,UAAU,IAAI,SAAS;AAC7B,UAAQ,IAAI,iBAAiB,UAAU,KAAK,SAAS;AAUrD,SAFyC,OANxB,MAAM,MAAM,iDAAiD,KAAK,UAAU,aAAa;GACxG,QAAQ;GACR;GACA,MAAM;GACP,CAAC,EAEsD,MAAM;;CAKhE,MAAa,OAAO,KAAa,MAAgB;EAC/C,MAAM,gBAAgB,MAAM,OAA4B,KAAK;GAC3D,QAAQ;GACR;GACD,CAAC;AAEF,MAAI,CAAC,cAAc,QACjB,OAAM,IAAI,MAAM,yBAAyB;EAG3C,MAAM,cAAc,cAAc,OAAO,SAAS;AAElD,MAAI,CAAC,YACH,OAAM,IAAI,MAAM,8BAA8B;AAGhD,SAAO;;CAGT,MAAa,OAAO,IAAY,MAA0B;AACxD,MAAI,KAAK,YAAY,GAAG,CACtB,QAAO,EAAE,SAAS,MAAM;AAG1B,MAAI;GACF,MAAM,UAAU,IAAI,SAAS;AAC7B,WAAQ,IAAI,iBAAiB,UAAU,KAAK,SAAS;AAErD,SAAM,OAAO,iDAAiD,KAAK,UAAU,aAAa,MAAM;IAC9F,QAAQ;IACR;IACD,CAAC;AACF,UAAO,EAAE,SAAS,MAAM;UAClB;AACN,UAAO,EAAE,SAAS,OAAO;;;CAI7B,MAAa,YAAY,OAA6D;AACpF,SAAO,MAAM,QAAQ,IACnB,MAAM,IAAI,OAAO,MAAM;GACrB,MAAM,WAAW,IAAI,UAAU;AAC/B,YAAS,OAAO,QAAQ,EAAE,KAAK;AAI/B,UAAO;IACL,KAHkB,MAAM,KAAK,OAAO,EAAE,IAAI,OAAO,SAAS;IAI1D,IAAI,EAAE,IAAI;IACX;IACD,CACH"}
@@ -1,20 +1,20 @@
1
- import * as oxlint8 from "oxlint";
1
+ import * as oxlint29 from "oxlint";
2
2
 
3
3
  //#region src/oxlint-plugins/jsx-component-pascal-case.d.ts
4
- declare const jsxComponentPascalCaseRule: oxlint8.Rule;
4
+ declare const jsxComponentPascalCaseRule: oxlint29.Rule;
5
5
  declare namespace _default {
6
6
  namespace meta {
7
7
  let name: string;
8
8
  }
9
9
  let rules: {
10
- "jsx-component-pascal-case": oxlint8.Rule;
10
+ "jsx-component-pascal-case": oxlint29.Rule;
11
11
  };
12
12
  }
13
- type RuleContext = oxlint8.Context;
14
- type ESTNode = oxlint8.ESTree.Node;
15
- type ESTExpression = oxlint8.ESTree.Expression;
16
- type ReturnStatementNode = oxlint8.ESTree.ReturnStatement;
17
- type FunctionLikeNode = oxlint8.ESTree.Function | oxlint8.ESTree.ArrowFunctionExpression;
13
+ type RuleContext = oxlint29.Context;
14
+ type ESTNode = oxlint29.ESTree.Node;
15
+ type ESTExpression = oxlint29.ESTree.Expression;
16
+ type ReturnStatementNode = oxlint29.ESTree.ReturnStatement;
17
+ type FunctionLikeNode = oxlint29.ESTree.Function | oxlint29.ESTree.ArrowFunctionExpression;
18
18
  type FunctionContext = {
19
19
  node: FunctionLikeNode;
20
20
  name: string;
@@ -1,20 +1,20 @@
1
- import * as oxlint35 from "oxlint";
1
+ import * as oxlint7 from "oxlint";
2
2
 
3
3
  //#region src/oxlint-plugins/no-component-date-instantiation.d.ts
4
- declare const noComponentDateInstantiationRule: oxlint35.Rule;
4
+ declare const noComponentDateInstantiationRule: oxlint7.Rule;
5
5
  declare namespace _default {
6
6
  namespace meta {
7
7
  let name: string;
8
8
  }
9
9
  let rules: {
10
- "no-component-date-instantiation": oxlint35.Rule;
10
+ "no-component-date-instantiation": oxlint7.Rule;
11
11
  };
12
12
  }
13
- type RuleContext = oxlint35.Context;
14
- type ESTNode = oxlint35.ESTree.Node;
15
- type NewExpressionNode = oxlint35.ESTree.NewExpression;
16
- type ReturnStatementNode = oxlint35.ESTree.ReturnStatement;
17
- type FunctionLikeNode = oxlint35.ESTree.Function | oxlint35.ESTree.ArrowFunctionExpression;
13
+ type RuleContext = oxlint7.Context;
14
+ type ESTNode = oxlint7.ESTree.Node;
15
+ type NewExpressionNode = oxlint7.ESTree.NewExpression;
16
+ type ReturnStatementNode = oxlint7.ESTree.ReturnStatement;
17
+ type FunctionLikeNode = oxlint7.ESTree.Function | oxlint7.ESTree.ArrowFunctionExpression;
18
18
  type FunctionContext = {
19
19
  node: FunctionLikeNode;
20
20
  parent: FunctionContext | null;
@@ -1,16 +1,16 @@
1
- import * as oxlint2 from "oxlint";
1
+ import * as oxlint4 from "oxlint";
2
2
 
3
3
  //#region src/oxlint-plugins/no-emoji.d.ts
4
- declare const noEmojiRule: oxlint2.Rule;
4
+ declare const noEmojiRule: oxlint4.Rule;
5
5
  declare namespace _default {
6
6
  namespace meta {
7
7
  let name: string;
8
8
  }
9
9
  let rules: {
10
- "no-emoji": oxlint2.Rule;
10
+ "no-emoji": oxlint4.Rule;
11
11
  };
12
12
  }
13
- type ESTNode = oxlint2.ESTree.Node;
13
+ type ESTNode = oxlint4.ESTree.Node;
14
14
  //#endregion
15
15
  export { ESTNode, _default as default, noEmojiRule };
16
16
  //# sourceMappingURL=no-emoji.d.mts.map
@@ -1,16 +1,16 @@
1
- import * as oxlint5 from "oxlint";
1
+ import * as oxlint26 from "oxlint";
2
2
 
3
3
  //#region src/oxlint-plugins/no-finally.d.ts
4
- declare const noFinallyRule: oxlint5.Rule;
4
+ declare const noFinallyRule: oxlint26.Rule;
5
5
  declare namespace _default {
6
6
  namespace meta {
7
7
  let name: string;
8
8
  }
9
9
  let rules: {
10
- "no-finally": oxlint5.Rule;
10
+ "no-finally": oxlint26.Rule;
11
11
  };
12
12
  }
13
- type ESTNode = oxlint5.ESTree.Node;
13
+ type ESTNode = oxlint26.ESTree.Node;
14
14
  //#endregion
15
15
  export { ESTNode, _default as default, noFinallyRule };
16
16
  //# sourceMappingURL=no-finally.d.mts.map
@@ -1,27 +1,27 @@
1
- import * as oxlint24 from "oxlint";
1
+ import * as oxlint15 from "oxlint";
2
2
 
3
3
  //#region src/oxlint-plugins/no-inline-components.d.ts
4
4
  declare function isComponentName(name: unknown): name is string;
5
5
  declare function isHookName(name: unknown): name is string;
6
6
  declare function getEnclosingFunction(node: ESTNode | null | undefined): FunctionLikeNode | null;
7
7
  declare function getFunctionName(node: FunctionLikeNode): string;
8
- declare const noInlineComponentsRule: oxlint24.Rule;
8
+ declare const noInlineComponentsRule: oxlint15.Rule;
9
9
  declare namespace _default {
10
10
  namespace meta {
11
11
  let name: string;
12
12
  }
13
13
  let rules: {
14
- "no-inline-components": oxlint24.Rule;
14
+ "no-inline-components": oxlint15.Rule;
15
15
  };
16
16
  }
17
- type RuleContext = oxlint24.Context;
18
- type ESTNode = oxlint24.ESTree.Node;
19
- type ESTExpression = oxlint24.ESTree.Expression;
20
- type ESTPattern = oxlint24.ESTree.Pattern;
21
- type ReturnStatementNode = oxlint24.ESTree.ReturnStatement;
22
- type VariableDeclaratorNode = oxlint24.ESTree.VariableDeclarator;
23
- type AssignmentExpressionNode = oxlint24.ESTree.AssignmentExpression;
24
- type FunctionLikeNode = oxlint24.ESTree.Function | oxlint24.ESTree.ArrowFunctionExpression;
17
+ type RuleContext = oxlint15.Context;
18
+ type ESTNode = oxlint15.ESTree.Node;
19
+ type ESTExpression = oxlint15.ESTree.Expression;
20
+ type ESTPattern = oxlint15.ESTree.Pattern;
21
+ type ReturnStatementNode = oxlint15.ESTree.ReturnStatement;
22
+ type VariableDeclaratorNode = oxlint15.ESTree.VariableDeclarator;
23
+ type AssignmentExpressionNode = oxlint15.ESTree.AssignmentExpression;
24
+ type FunctionLikeNode = oxlint15.ESTree.Function | oxlint15.ESTree.ArrowFunctionExpression;
25
25
  type RecordedAssignment = {
26
26
  node: ESTExpression;
27
27
  names: string[];
@@ -1,16 +1,16 @@
1
- import * as oxlint0 from "oxlint";
1
+ import * as oxlint40 from "oxlint";
2
2
 
3
3
  //#region src/oxlint-plugins/no-react-namespace.d.ts
4
- declare const noReactNamespaceRule: oxlint0.Rule;
4
+ declare const noReactNamespaceRule: oxlint40.Rule;
5
5
  declare namespace _default {
6
6
  namespace meta {
7
7
  let name: string;
8
8
  }
9
9
  let rules: {
10
- "no-react-namespace": oxlint0.Rule;
10
+ "no-react-namespace": oxlint40.Rule;
11
11
  };
12
12
  }
13
- type ESTNode = oxlint0.ESTree.Node;
13
+ type ESTNode = oxlint40.ESTree.Node;
14
14
  //#endregion
15
15
  export { ESTNode, _default as default, noReactNamespaceRule };
16
16
  //# sourceMappingURL=no-react-namespace.d.mts.map
@@ -1,4 +1,4 @@
1
- import * as oxlint16 from "oxlint";
1
+ import * as oxlint0 from "oxlint";
2
2
 
3
3
  //#region src/oxlint-plugins/no-switch-plugin.d.ts
4
4
  declare namespace _default {
@@ -6,10 +6,10 @@ declare namespace _default {
6
6
  let name: string;
7
7
  }
8
8
  let rules: {
9
- "no-switch": oxlint16.Rule;
9
+ "no-switch": oxlint0.Rule;
10
10
  };
11
11
  }
12
- type ESTNode = oxlint16.ESTree.Node;
12
+ type ESTNode = oxlint0.ESTree.Node;
13
13
  //#endregion
14
14
  export { ESTNode, _default as default };
15
15
  //# sourceMappingURL=no-switch-plugin.d.mts.map
@@ -1,16 +1,16 @@
1
- import * as oxlint18 from "oxlint";
1
+ import * as oxlint1 from "oxlint";
2
2
 
3
3
  //#region src/oxlint-plugins/no-top-level-let.d.ts
4
- declare const noTopLevelLetRule: oxlint18.Rule;
4
+ declare const noTopLevelLetRule: oxlint1.Rule;
5
5
  declare namespace _default {
6
6
  namespace meta {
7
7
  let name: string;
8
8
  }
9
9
  let rules: {
10
- "no-top-level-let": oxlint18.Rule;
10
+ "no-top-level-let": oxlint1.Rule;
11
11
  };
12
12
  }
13
- type ESTNode = oxlint18.ESTree.Node;
13
+ type ESTNode = oxlint1.ESTree.Node;
14
14
  //#endregion
15
15
  export { ESTNode, _default as default, noTopLevelLetRule };
16
16
  //# sourceMappingURL=no-top-level-let.d.mts.map
@@ -1,16 +1,16 @@
1
- import * as oxlint21 from "oxlint";
1
+ import * as oxlint37 from "oxlint";
2
2
 
3
3
  //#region src/oxlint-plugins/no-type-cast.d.ts
4
- declare const noTypeCastRule: oxlint21.Rule;
4
+ declare const noTypeCastRule: oxlint37.Rule;
5
5
  declare namespace _default {
6
6
  namespace meta {
7
7
  let name: string;
8
8
  }
9
9
  let rules: {
10
- "no-type-cast": oxlint21.Rule;
10
+ "no-type-cast": oxlint37.Rule;
11
11
  };
12
12
  }
13
- type ESTNode = oxlint21.ESTree.Node;
13
+ type ESTNode = oxlint37.ESTree.Node;
14
14
  //#endregion
15
15
  export { ESTNode, _default as default, noTypeCastRule };
16
16
  //# sourceMappingURL=no-type-cast.d.mts.map
@@ -0,0 +1,73 @@
1
+ //#region src/react/useStableCallback.d.ts
2
+ type AnyFunction = (...args: any[]) => any;
3
+ /**
4
+ * Creates a stable callback that always calls the latest version of the function.
5
+ * Useful for callbacks that need to be used in dependency arrays but should always
6
+ * execute the most recent version of the callback.
7
+ */
8
+ declare const useStableCallback: <T extends AnyFunction>(callback: T) => T;
9
+ //#endregion
10
+ //#region src/react/useOnce.d.ts
11
+ /**
12
+ * Runs a callback only once when a condition becomes truthy.
13
+ * The callback is stabilized internally to always reference the latest version.
14
+ *
15
+ * @param condition - When truthy (evaluated via Boolean()), the callback will be executed (only once).
16
+ * @param callback - The function to run once when the condition is met.
17
+ *
18
+ * @example
19
+ * ```tsx
20
+ * const isReady = true;
21
+ * useOnce(isReady, () => {
22
+ * console.log("Ready!");
23
+ * });
24
+ * ```
25
+ */
26
+ declare const useOnce: (condition: unknown, callback: () => void) => void;
27
+ //#endregion
28
+ //#region src/react/useLocalOnce.d.ts
29
+ /**
30
+ * Runs a callback only once when a condition becomes truthy, executing synchronously
31
+ * during render. The callback is stabilized internally to always reference the latest version.
32
+ *
33
+ * Unlike `useOnce`, this runs at the top level of the hook (not in useEffect),
34
+ * making it suitable for state updates that need to happen synchronously during render.
35
+ *
36
+ * @param condition - When truthy (evaluated via Boolean()), the callback will be executed (only once).
37
+ * @param callback - The function to run once when the condition is met.
38
+ *
39
+ * @example
40
+ * ```tsx
41
+ * const user: User | null = getUser();
42
+ * useLocalOnce(user, () => {
43
+ * setState(user.name);
44
+ * });
45
+ * ```
46
+ */
47
+ declare const useLocalOnce: (condition: unknown, callback: () => void) => void;
48
+ //#endregion
49
+ //#region src/react/useOnMount.d.ts
50
+ /**
51
+ * Calls the given callback when the component mounts.
52
+ * Uses useStableCallback internally to ensure the latest version is called.
53
+ *
54
+ * @param callback - The function to run when the component mounts.
55
+ *
56
+ * @example
57
+ * ```tsx
58
+ * useOnMount(() => {
59
+ * console.log("Component mounted!");
60
+ * });
61
+ * ```
62
+ */
63
+ declare const useOnMount: (callback: () => void) => void;
64
+ //#endregion
65
+ //#region src/react/useOnUnmount.d.ts
66
+ /**
67
+ * Calls the given callback when the component unmounts.
68
+ * Uses useStableCallback internally to ensure the latest version is called.
69
+ */
70
+ declare const useOnUnmount: (callback: () => void) => void;
71
+ //#endregion
72
+ export { useLocalOnce, useOnMount, useOnUnmount, useOnce, useStableCallback };
73
+ //# sourceMappingURL=react.d.mts.map
package/dist/react.mjs ADDED
@@ -0,0 +1,113 @@
1
+ import { useCallback, useEffect, useRef } from "react";
2
+
3
+ //#region src/react/useStableCallback.ts
4
+ /**
5
+ * Creates a stable callback that always calls the latest version of the function.
6
+ * Useful for callbacks that need to be used in dependency arrays but should always
7
+ * execute the most recent version of the callback.
8
+ */
9
+ const useStableCallback = (callback) => {
10
+ const callbackRef = useRef(callback);
11
+ callbackRef.current = callback;
12
+ return useCallback((...args) => {
13
+ return callbackRef.current(...args);
14
+ }, []);
15
+ };
16
+
17
+ //#endregion
18
+ //#region src/react/useOnce.ts
19
+ /**
20
+ * Runs a callback only once when a condition becomes truthy.
21
+ * The callback is stabilized internally to always reference the latest version.
22
+ *
23
+ * @param condition - When truthy (evaluated via Boolean()), the callback will be executed (only once).
24
+ * @param callback - The function to run once when the condition is met.
25
+ *
26
+ * @example
27
+ * ```tsx
28
+ * const isReady = true;
29
+ * useOnce(isReady, () => {
30
+ * console.log("Ready!");
31
+ * });
32
+ * ```
33
+ */
34
+ const useOnce = (condition, callback) => {
35
+ const hasRunRef = useRef(false);
36
+ const stableCallback = useStableCallback(callback);
37
+ useEffect(() => {
38
+ if (Boolean(condition) && !hasRunRef.current) {
39
+ hasRunRef.current = true;
40
+ stableCallback();
41
+ }
42
+ }, [condition, stableCallback]);
43
+ };
44
+
45
+ //#endregion
46
+ //#region src/react/useLocalOnce.ts
47
+ /**
48
+ * Runs a callback only once when a condition becomes truthy, executing synchronously
49
+ * during render. The callback is stabilized internally to always reference the latest version.
50
+ *
51
+ * Unlike `useOnce`, this runs at the top level of the hook (not in useEffect),
52
+ * making it suitable for state updates that need to happen synchronously during render.
53
+ *
54
+ * @param condition - When truthy (evaluated via Boolean()), the callback will be executed (only once).
55
+ * @param callback - The function to run once when the condition is met.
56
+ *
57
+ * @example
58
+ * ```tsx
59
+ * const user: User | null = getUser();
60
+ * useLocalOnce(user, () => {
61
+ * setState(user.name);
62
+ * });
63
+ * ```
64
+ */
65
+ const useLocalOnce = (condition, callback) => {
66
+ const hasRunRef = useRef(false);
67
+ const stableCallback = useStableCallback(callback);
68
+ if (Boolean(condition) && !hasRunRef.current) {
69
+ hasRunRef.current = true;
70
+ stableCallback();
71
+ }
72
+ };
73
+
74
+ //#endregion
75
+ //#region src/react/useOnMount.ts
76
+ /**
77
+ * Calls the given callback when the component mounts.
78
+ * Uses useStableCallback internally to ensure the latest version is called.
79
+ *
80
+ * @param callback - The function to run when the component mounts.
81
+ *
82
+ * @example
83
+ * ```tsx
84
+ * useOnMount(() => {
85
+ * console.log("Component mounted!");
86
+ * });
87
+ * ```
88
+ */
89
+ const useOnMount = (callback) => {
90
+ const stableCallback = useStableCallback(callback);
91
+ useEffect(() => {
92
+ stableCallback();
93
+ }, [stableCallback]);
94
+ };
95
+
96
+ //#endregion
97
+ //#region src/react/useOnUnmount.ts
98
+ /**
99
+ * Calls the given callback when the component unmounts.
100
+ * Uses useStableCallback internally to ensure the latest version is called.
101
+ */
102
+ const useOnUnmount = (callback) => {
103
+ const stableCallback = useStableCallback(callback);
104
+ useEffect(() => {
105
+ return () => {
106
+ stableCallback();
107
+ };
108
+ }, [stableCallback]);
109
+ };
110
+
111
+ //#endregion
112
+ export { useLocalOnce, useOnMount, useOnUnmount, useOnce, useStableCallback };
113
+ //# sourceMappingURL=react.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react.mjs","names":[],"sources":["../src/react/useStableCallback.ts","../src/react/useOnce.ts","../src/react/useLocalOnce.ts","../src/react/useOnMount.ts","../src/react/useOnUnmount.ts"],"sourcesContent":["// oxlint-disable no-explicit-any\r\n\r\nimport { useCallback, useRef } from \"react\";\r\n\r\ntype AnyFunction = (...args: any[]) => any;\r\ntype AnyArgs = any[];\r\n\r\n/**\r\n * Creates a stable callback that always calls the latest version of the function.\r\n * Useful for callbacks that need to be used in dependency arrays but should always\r\n * execute the most recent version of the callback.\r\n */\r\nexport const useStableCallback = <T extends AnyFunction>(callback: T): T => {\r\n const callbackRef = useRef(callback);\r\n\r\n // Update the ref on every render\r\n callbackRef.current = callback;\r\n\r\n return useCallback((...args: AnyArgs) => {\r\n return callbackRef.current(...args);\r\n }, []) as T;\r\n};\r\n","import { useEffect, useRef } from \"react\";\r\nimport { useStableCallback } from \"./useStableCallback\";\r\n\r\n/**\r\n * Runs a callback only once when a condition becomes truthy.\r\n * The callback is stabilized internally to always reference the latest version.\r\n *\r\n * @param condition - When truthy (evaluated via Boolean()), the callback will be executed (only once).\r\n * @param callback - The function to run once when the condition is met.\r\n *\r\n * @example\r\n * ```tsx\r\n * const isReady = true;\r\n * useOnce(isReady, () => {\r\n * console.log(\"Ready!\");\r\n * });\r\n * ```\r\n */\r\nexport const useOnce = (condition: unknown, callback: () => void): void => {\r\n const hasRunRef = useRef(false);\r\n const stableCallback = useStableCallback(callback);\r\n\r\n useEffect(() => {\r\n if (Boolean(condition) && !hasRunRef.current) {\r\n hasRunRef.current = true;\r\n stableCallback();\r\n }\r\n }, [condition, stableCallback]);\r\n};\r\n","import { useRef } from \"react\";\r\nimport { useStableCallback } from \"./useStableCallback\";\r\n\r\n/**\r\n * Runs a callback only once when a condition becomes truthy, executing synchronously\r\n * during render. The callback is stabilized internally to always reference the latest version.\r\n *\r\n * Unlike `useOnce`, this runs at the top level of the hook (not in useEffect),\r\n * making it suitable for state updates that need to happen synchronously during render.\r\n *\r\n * @param condition - When truthy (evaluated via Boolean()), the callback will be executed (only once).\r\n * @param callback - The function to run once when the condition is met.\r\n *\r\n * @example\r\n * ```tsx\r\n * const user: User | null = getUser();\r\n * useLocalOnce(user, () => {\r\n * setState(user.name);\r\n * });\r\n * ```\r\n */\r\nexport const useLocalOnce = (condition: unknown, callback: () => void): void => {\r\n const hasRunRef = useRef(false);\r\n const stableCallback = useStableCallback(callback);\r\n\r\n if (Boolean(condition) && !hasRunRef.current) {\r\n hasRunRef.current = true;\r\n stableCallback();\r\n }\r\n};\r\n","import { useEffect } from \"react\";\nimport { useStableCallback } from \"./useStableCallback\";\n\n/**\n * Calls the given callback when the component mounts.\n * Uses useStableCallback internally to ensure the latest version is called.\n *\n * @param callback - The function to run when the component mounts.\n *\n * @example\n * ```tsx\n * useOnMount(() => {\n * console.log(\"Component mounted!\");\n * });\n * ```\n */\nexport const useOnMount = (callback: () => void): void => {\n const stableCallback = useStableCallback(callback);\n\n useEffect(() => {\n stableCallback();\n }, [stableCallback]);\n};\n","import { useEffect } from \"react\";\r\nimport { useStableCallback } from \"./useStableCallback\";\r\n\r\n/**\r\n * Calls the given callback when the component unmounts.\r\n * Uses useStableCallback internally to ensure the latest version is called.\r\n */\r\nexport const useOnUnmount = (callback: () => void): void => {\r\n const stableCallback = useStableCallback(callback);\r\n\r\n useEffect(() => {\r\n return () => {\r\n stableCallback();\r\n };\r\n }, [stableCallback]);\r\n};\r\n"],"mappings":";;;;;;;;AAYA,MAAa,qBAA4C,aAAmB;CAC1E,MAAM,cAAc,OAAO,SAAS;AAGpC,aAAY,UAAU;AAEtB,QAAO,aAAa,GAAG,SAAkB;AACvC,SAAO,YAAY,QAAQ,GAAG,KAAK;IAClC,EAAE,CAAC;;;;;;;;;;;;;;;;;;;;ACFR,MAAa,WAAW,WAAoB,aAA+B;CACzE,MAAM,YAAY,OAAO,MAAM;CAC/B,MAAM,iBAAiB,kBAAkB,SAAS;AAElD,iBAAgB;AACd,MAAI,QAAQ,UAAU,IAAI,CAAC,UAAU,SAAS;AAC5C,aAAU,UAAU;AACpB,mBAAgB;;IAEjB,CAAC,WAAW,eAAe,CAAC;;;;;;;;;;;;;;;;;;;;;;;ACNjC,MAAa,gBAAgB,WAAoB,aAA+B;CAC9E,MAAM,YAAY,OAAO,MAAM;CAC/B,MAAM,iBAAiB,kBAAkB,SAAS;AAElD,KAAI,QAAQ,UAAU,IAAI,CAAC,UAAU,SAAS;AAC5C,YAAU,UAAU;AACpB,kBAAgB;;;;;;;;;;;;;;;;;;;ACXpB,MAAa,cAAc,aAA+B;CACxD,MAAM,iBAAiB,kBAAkB,SAAS;AAElD,iBAAgB;AACd,kBAAgB;IACf,CAAC,eAAe,CAAC;;;;;;;;;ACdtB,MAAa,gBAAgB,aAA+B;CAC1D,MAAM,iBAAiB,kBAAkB,SAAS;AAElD,iBAAgB;AACd,eAAa;AACX,mBAAgB;;IAEjB,CAAC,eAAe,CAAC"}
package/package.json CHANGED
@@ -15,6 +15,10 @@
15
15
  "types": "./dist/images.d.mts",
16
16
  "default": "./dist/images.mjs"
17
17
  },
18
+ "./react": {
19
+ "types": "./dist/react.d.mts",
20
+ "default": "./dist/react.mjs"
21
+ },
18
22
  "./oxlint/config": {
19
23
  "default": "./dist/oxlint/config.mjson"
20
24
  },
@@ -63,17 +67,22 @@
63
67
  "remeda": "2.32.0",
64
68
  "valibot": "1.1.0"
65
69
  },
66
- "keywords": [],
67
- "author": "",
70
+ "peerDependencies": {
71
+ "react": ">=18.0.0",
72
+ "react-dom": ">=18.0.0"
73
+ },
68
74
  "license": "ISC",
69
75
  "devDependencies": {
70
76
  "@biomejs/biome": "1.8.3",
71
- "@types/node": "24.10.0",
77
+ "@testing-library/react": "16.3.0",
78
+ "@types/node": "25.0.1",
79
+ "@types/react": "19.2.7",
80
+ "react": "19.2.3",
72
81
  "tsdown": "0.17.2",
73
82
  "typescript": "5.9.3",
74
83
  "vitest": "4.0.15"
75
84
  },
76
- "version": "0.0.43",
85
+ "version": "0.0.45",
77
86
  "scripts": {
78
87
  "dev": "tsdown --watch",
79
88
  "build": "tsc --noEmit && tsdown",