From 8007879c7b6344189c638f119bfacc5ddb28e697 Mon Sep 17 00:00:00 2001 From: Jason Caldwell <jaswrks@o5p.me> Date: Mon, 30 Dec 2024 01:21:47 -0500 Subject: [PATCH] Project update. [p][robotic] --- .browserslistrc | 2 +- .dockerignore | 2 +- .env.vault | 8 +-- .gitattributes | 2 +- .gitignore | 2 +- .npmignore | 2 +- .prettierignore | 2 +- .vscode/settings.json | 2 +- .vscodeignore | 2 +- dev/.files/vite/config.mjs | 2 +- dev/.files/vite/includes/rollup/config.mjs | 2 +- dev/.files/wrangler/settings.mjs | 2 +- package-lock.json | 65 +++++++++++++++++----- package.json | 2 +- src/email.ts | 33 +++++++++-- src/str.ts | 30 +++++----- src/tests/email/index.ts | 34 +++++++++++ tsconfig.json | 2 +- wrangler.toml | 4 +- 19 files changed, 146 insertions(+), 54 deletions(-) diff --git a/.browserslistrc b/.browserslistrc index b91fdab6..c0817f14 100644 --- a/.browserslistrc +++ b/.browserslistrc @@ -17,7 +17,7 @@ # Generated data. # <generated:start> -# Last generated Dec 29, 2024 6:03 AM UTC. +# Last generated Dec 30, 2024 6:09 AM UTC. [production] node >= 20.9.0 diff --git a/.dockerignore b/.dockerignore index 1848d39c..c7e6eed7 100644 --- a/.dockerignore +++ b/.dockerignore @@ -17,7 +17,7 @@ # Generated data. # <generated:start> -# Last generated Dec 29, 2024 6:03 AM UTC. +# Last generated Dec 30, 2024 6:09 AM UTC. # Locals diff --git a/.env.vault b/.env.vault index 5941a31f..a724d533 100644 --- a/.env.vault +++ b/.env.vault @@ -8,12 +8,12 @@ DOTENV_VAULT_MAIN="7GW98NfL4hM4N6HbfeDXCKreVxKvcVPupFRnZ8XJ+pomaQ==" DOTENV_VAULT_MAIN_VERSION=1 # dev -DOTENV_VAULT_DEV="TwxDnS+ApGYMAvvlyy1XL2ZHSF81VkHMf8Yj6YCMH4B3Qpg7jEOPFdVZt/I//Z4cRvmDYrr3+CZp15fx39KmzieZ/hgnf52hL3moq56IMuJQXlRej/pZFo4eV18akkxpltdVVa6qNb/qN8n5uH3dGrEwHZq8CC+zDDc4aMAEqrnqjRKOM04Y4NOCpuE5I+mqpkz+PuhmmknxgIU70eTa29La9LvaRPMTuAW6QXG5lH8izAGHHI0yylR3TtBzcZGz6A==" -DOTENV_VAULT_DEV_VERSION=561 +DOTENV_VAULT_DEV="pi+QD4XHVkzTNq/0Fo7sBxeSYxxapR8x2EPN4+RTqu47dEAmcW38KN/YoR9CeBWbqKzhsnyWMBU960reqQzNJEcLPlpQ/U9YvBlMrDjFarx22qI95mt2UVZUuxAp1Yr6k+AYqQf+ikEt+DfsRrxC79v/AKVKEUk1yoHAs3eDqCU7emqwCsS802Mp4gKQ5r9kTxJZQauIT+mw6YT4e7YQbj2rXMwP9uGVTNohBPpm59I279lP9PlCmRvK1PbYyyyyMQ==" +DOTENV_VAULT_DEV_VERSION=563 # ci -DOTENV_VAULT_CI="m73XqNmab2TEtFaq24HgWnbf01VOBtGEgpQLY8GQLzvjhDU+6UctelFDZ2OncOj33NKYJkgtVBx3frHi37rDiTjnmwDu/FD12PFxaRxG+AcJriAXDCHtlYJ5eTUtMgP8EdmRDe1NaTDYQiycRIaSMlGry7bJB5ZQyuVHvkTC7g==" -DOTENV_VAULT_CI_VERSION=561 +DOTENV_VAULT_CI="bcZ5LMMuEyegFe6gOzBH8ygDoe/CcPzzd8QIApLkeYBKjU/a0t5DxXgstL0Ym3C/LEybJ7zU7Id1qLNkHuMadgQPVTCrqk/jsnoQIn6e7SUuOGCgTc+zsX9GVygGRTruux/rrlK2D5JAMLbskKs89wBppP/7kUU4ATWjxuek2A==" +DOTENV_VAULT_CI_VERSION=563 # stage DOTENV_VAULT_STAGE="aRP8su2YV4jZu3w1HZ/SLaots0IwJDFw75TCpvXEFeNp7tw=" diff --git a/.gitattributes b/.gitattributes index f670119d..60715fcb 100644 --- a/.gitattributes +++ b/.gitattributes @@ -17,7 +17,7 @@ # Generated data. # <generated:start> -# Last generated Dec 29, 2024 6:03 AM UTC. +# Last generated Dec 30, 2024 6:09 AM UTC. # Default diff --git a/.gitignore b/.gitignore index 5da81192..1916673e 100644 --- a/.gitignore +++ b/.gitignore @@ -17,7 +17,7 @@ # Generated data. # <generated:start> -# Last generated Dec 29, 2024 6:03 AM UTC. +# Last generated Dec 30, 2024 6:09 AM UTC. # Locals diff --git a/.npmignore b/.npmignore index e857be34..a9287eeb 100644 --- a/.npmignore +++ b/.npmignore @@ -25,7 +25,7 @@ # Generated data. # <generated:start> -# Last generated Dec 29, 2024 6:03 AM UTC. +# Last generated Dec 30, 2024 6:09 AM UTC. # Locals diff --git a/.prettierignore b/.prettierignore index 567a0fb0..0def5f45 100644 --- a/.prettierignore +++ b/.prettierignore @@ -17,7 +17,7 @@ # Generated data. # <generated:start> -# Last generated Dec 29, 2024 6:03 AM UTC. +# Last generated Dec 30, 2024 6:09 AM UTC. # Packages diff --git a/.vscode/settings.json b/.vscode/settings.json index f510d6e9..8929cadf 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,7 +7,7 @@ * @note This entire file will be updated automatically. * @note Instead of editing here, please review `./settings.mjs`. * - * Last generated using `./settings.mjs` Dec 29, 2024 6:03 AM UTC. + * Last generated using `./settings.mjs` Dec 30, 2024 6:09 AM UTC. */ { "editor.formatOnType": false, diff --git a/.vscodeignore b/.vscodeignore index dbbf1bfc..93c005a3 100644 --- a/.vscodeignore +++ b/.vscodeignore @@ -17,7 +17,7 @@ # Generated data. # <generated:start> -# Last generated Dec 29, 2024 6:03 AM UTC. +# Last generated Dec 30, 2024 6:09 AM UTC. # Locals diff --git a/dev/.files/vite/config.mjs b/dev/.files/vite/config.mjs index 475a3e5a..d452e80f 100644 --- a/dev/.files/vite/config.mjs +++ b/dev/.files/vite/config.mjs @@ -323,7 +323,7 @@ export default async ({ mode, command, isSsrBuild: isSSRBuild }) => { optimizeDeps: { force: true, // Don’t use cache for optimized deps; recreate. esbuildOptions: { - external: ['cloudflare:sockets'], + external: ['cloudflare:email', 'cloudflare:sockets'], plugins: [await viteMDXESBuildConfig({ projDir })], }, // Preact is required by prefresh plugin; {@see https://o5p.me/WmuefH}. diff --git a/dev/.files/vite/includes/rollup/config.mjs b/dev/.files/vite/includes/rollup/config.mjs index 36a47126..eea14394 100644 --- a/dev/.files/vite/includes/rollup/config.mjs +++ b/dev/.files/vite/includes/rollup/config.mjs @@ -41,9 +41,9 @@ export default async ({ projDir, srcDir, distDir, a16sDir, appType, appEntries, }, // {@see https://o5p.me/7YF2NU}. }, external: [ + /^(?:node:|cloudflare:).*$/iu, ...(['lib'].includes(appType) ? [/^(?![./~#]|file:|data:|virtual:).*$/iu] : []), ...peerDepKeys.map((pkgName) => new RegExp('^' + $str.escRegExp(pkgName) + '(?:$|[/?])', 'u')), - /^(?:cloudflare:).*$/iu, ], output: { interop: 'auto', // Matches TypeScript configuration. diff --git a/dev/.files/wrangler/settings.mjs b/dev/.files/wrangler/settings.mjs index a1323a58..32777c72 100755 --- a/dev/.files/wrangler/settings.mjs +++ b/dev/.files/wrangler/settings.mjs @@ -41,7 +41,7 @@ export default async () => { defaultAccountId: brandAccountId, defaultLogpush: brandSupportsLogpush, - compatibilityDate: '2024-03-02', + compatibilityDate: '2024-09-23', compatibilityFlags: [], defaultLocalIP: '0.0.0.0', diff --git a/package-lock.json b/package-lock.json index 0dc17706..b6559300 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@clevercanyon/utilities", - "version": "1.0.966", + "version": "1.0.967", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@clevercanyon/utilities", - "version": "1.0.966", + "version": "1.0.967", "cpu": [ "x64", "arm64" @@ -847,9 +847,9 @@ } }, "node_modules/@clevercanyon/utilities": { - "version": "1.0.966", - "resolved": "https://registry.npmjs.org/@clevercanyon/utilities/-/utilities-1.0.966.tgz", - "integrity": "sha512-4Iu4buOtR8g96XQt9kxubQbRf0AqCwui6HcX3VHYnri1w2ii+ZvAiyi5kBVuti57CQD/sy86wJ/6GquZIz9tcg==", + "version": "1.0.967", + "resolved": "https://registry.npmjs.org/@clevercanyon/utilities/-/utilities-1.0.967.tgz", + "integrity": "sha512-T+m/ZvcyiaoOjqhw+I06ueWdfgY83U0NtxPCGt6vFgcQ+DHbsw6494GIXkLtC1bYqYSkzKWfVbH5u8Yt5FlqdQ==", "cpu": [ "x64", "arm64" @@ -934,9 +934,9 @@ } }, "node_modules/@clevercanyon/utilities.cfw": { - "version": "1.0.325", - "resolved": "https://registry.npmjs.org/@clevercanyon/utilities.cfw/-/utilities.cfw-1.0.325.tgz", - "integrity": "sha512-wPMVodWzM4FqtC4Wu1o5pGPHmyaNy8f/YJa2fxqb2NOrYt23dsGkj36KoRAfV1f9locw65VQ6+UJ9BiDcglcXw==", + "version": "1.0.326", + "resolved": "https://registry.npmjs.org/@clevercanyon/utilities.cfw/-/utilities.cfw-1.0.326.tgz", + "integrity": "sha512-AmLUo0ZI+WwSezTEbrq9EE8bygMlfxfMO8FnnPNhlJcEVMEN95NFzKN1PzcQnsVHdWVUIS1Y5h8bx4gTvGCOhg==", "cpu": [ "x64", "arm64" @@ -955,7 +955,7 @@ "url": "https://github.com/sponsors/clevercanyon" }, "peerDependencies": { - "@clevercanyon/utilities": "^1.0.966", + "@clevercanyon/utilities": "^1.0.967", "@cloudflare/ai": "1.0.53", "@upstash/ratelimit": "1.0.0", "@upstash/redis": "1.28.0" @@ -9700,9 +9700,9 @@ } }, "node_modules/es-abstract": { - "version": "1.23.7", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.7.tgz", - "integrity": "sha512-OygGC8kIcDhXX+6yAZRGLqwi2CmEXCbLQixeGUgYeR+Qwlppqmo7DIDr8XibtEBZp+fJcoYpoatp5qwLMEdcqQ==", + "version": "1.23.8", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.8.tgz", + "integrity": "sha512-lfab8IzDn6EpI1ibZakcgS6WsfEBiB+43cuJo+wgylx1xKXf+Sp+YR3vFuQwC/u3sxYwV8Cxe3B0DpVUu/WiJQ==", "dev": true, "dependencies": { "array-buffer-byte-length": "^1.0.2", @@ -9740,8 +9740,10 @@ "object-inspect": "^1.13.3", "object-keys": "^1.1.1", "object.assign": "^4.1.7", + "own-keys": "^1.0.0", "regexp.prototype.flags": "^1.5.3", "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", "safe-regex-test": "^1.1.0", "string.prototype.trim": "^1.2.10", "string.prototype.trimend": "^1.0.9", @@ -20996,6 +20998,23 @@ "node": ">=0.10.0" } }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/p-defer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", @@ -27068,6 +27087,22 @@ } ] }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/safe-regex-test": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", @@ -28827,9 +28862,9 @@ "dev": true }, "node_modules/tinyexec": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.1.tgz", - "integrity": "sha512-WiCJLEECkO18gwqIp6+hJg0//p23HXp4S+gGtAKu3mI2F2/sXC4FvHvXvB0zJVVaTPhx1/tOwdbRsa1sOBIKqQ==", + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", "dev": true }, "node_modules/tinypool": { diff --git a/package.json b/package.json index 07a5d7d7..a99d0f24 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "publishConfig": { "access": "public" }, - "version": "1.0.967", + "version": "1.0.968", "license": "GPL-3.0-or-later", "name": "@clevercanyon/utilities", "description": "Utilities for JavaScript apps running in any environment.", diff --git a/src/email.ts b/src/email.ts index 27047042..a72b7fc6 100644 --- a/src/email.ts +++ b/src/email.ts @@ -6,6 +6,11 @@ import '#@initialize.ts'; import { $str } from '#index.ts'; +/** + * Defines types. + */ +export type Addr = { name: string; email: string }; + /** * Gets email from an addr. * @@ -14,12 +19,28 @@ import { $str } from '#index.ts'; * * @param str String to consider. * - * @returns Email from an addr; else empty string. + * @returns Email address; else empty string. */ export const fromAddr = (str: string): string => { - if (!str) return ''; - if ($str.isEmail(str)) return str; + return parseAddr(str)?.email || ''; +}; + +/** + * Parses an addr. + * + * - `username@hostname`. + * - `"Name" <username@hostname>`. + * + * @param str String to consider. + * + * @returns Addr parts; else undefined. + */ +export const parseAddr = (str: string): Addr | undefined => { + if (!str) return; // Empty string. + if ($str.isEmail(str) /* Email only. */) { + return { name: '', email: str }; + } const parts = str.split(/(?<=")\s(?=<)/u); if ( 2 === parts.length && @@ -34,7 +55,9 @@ export const fromAddr = (str: string): string => { '>' === parts[1][parts[1].length - 1] && // Closing bracket. $str.isEmail(parts[1].slice(1, -1)) // `<email>` validation. ) { - return parts[1].slice(1, -1).toLowerCase(); + return { + name: parts[0].slice(1, -1), + email: parts[1].slice(1, -1).toLowerCase(), + }; } - return ''; // Not an addr. }; diff --git a/src/str.ts b/src/str.ts index 4038ad64..d23a9131 100644 --- a/src/str.ts +++ b/src/str.ts @@ -964,6 +964,20 @@ export const escFTSQuery = (str: string, options?: EscFTSQueryOptions): string = * Email utilities. */ +/** + * Tests if a string is an addr. + * + * - `username@hostname`. + * - `"Name" <username@hostname>`. + * + * @param str String to consider. + * + * @returns True if string is an addr. + */ +export const isAddr = (str: string): boolean => { + return $email.parseAddr(str) ? true : false; +}; + /** * Tests if a string is an email address. * @@ -983,25 +997,11 @@ export const isEmail = (str: string): boolean => { parts[1].length > 255 || // Hostname. parts[1].split('.').some((part) => part.length > 63) ) - return false; // Not an email. + return false; // Not an email address. return emailRegExp.test(str); }; -/** - * Tests if a string is an addr. - * - * - `username@hostname`. - * - `"Name" <username@hostname>`. - * - * @param str String to consider. - * - * @returns True if string is an addr. - */ -export const isAddr = (str: string): boolean => { - return $email.fromAddr(str) ? true : false; -}; - /* --- * IP utilities. */ diff --git a/src/tests/email/index.ts b/src/tests/email/index.ts index 72f9ba9e..53eefd11 100644 --- a/src/tests/email/index.ts +++ b/src/tests/email/index.ts @@ -40,4 +40,38 @@ describe('$email', async () => { expect($email.fromAddr('"X" <x@>')).toBe(''); expect($email.fromAddr('"X" <@x>')).toBe(''); }); + test('.parseAddr()', async () => { + expect($email.parseAddr('x@x')).toStrictEqual({ name: '', email: 'x@x' }); + expect($email.parseAddr('x+x@x')).toStrictEqual({ name: '', email: 'x+x@x' }); + + expect($email.parseAddr('x@localhost')).toStrictEqual({ name: '', email: 'x@localhost' }); + expect($email.parseAddr('x+x@localhost')).toStrictEqual({ name: '', email: 'x+x@localhost' }); + + expect($email.parseAddr('x@hop.gdn')).toStrictEqual({ name: '', email: 'x@hop.gdn' }); + expect($email.parseAddr('x+x@hop.gdn')).toStrictEqual({ name: '', email: 'x+x@hop.gdn' }); + + expect($email.parseAddr('"X" <x@x>')).toStrictEqual({ name: 'X', email: 'x@x' }); + expect($email.parseAddr('"X" <x+x@x>')).toStrictEqual({ name: 'X', email: 'x+x@x' }); + + expect($email.parseAddr('"X" <x@localhost>')).toStrictEqual({ name: 'X', email: 'x@localhost' }); + expect($email.parseAddr('"X" <x+x@localhost>')).toStrictEqual({ name: 'X', email: 'x+x@localhost' }); + + expect($email.parseAddr('"X" <x@hop.gdn>')).toStrictEqual({ name: 'X', email: 'x@hop.gdn' }); + expect($email.parseAddr('"X" <x+x@hop.gdn>')).toStrictEqual({ name: 'X', email: 'x+x@hop.gdn' }); + + expect($email.parseAddr('"X" <x@x>')).toStrictEqual({ name: 'X', email: 'x@x' }); + expect($email.parseAddr('"X X" <x@x>')).toStrictEqual({ name: 'X X', email: 'x@x' }); + + expect($email.parseAddr('x@x,x')).toBe(undefined); + expect($email.parseAddr('x,x@x')).toBe(undefined); + expect($email.parseAddr('<x@hop.gdn>')).toBe(undefined); + expect($email.parseAddr('x @hop.gdn')).toBe(undefined); + + expect($email.parseAddr('X x@x')).toBe(undefined); + expect($email.parseAddr('"X" x@x')).toBe(undefined); + expect($email.parseAddr('"" <x@x>')).toBe(undefined); + expect($email.parseAddr('"X" <x@x>')).toBe(undefined); + expect($email.parseAddr('"X" <x@>')).toBe(undefined); + expect($email.parseAddr('"X" <@x>')).toBe(undefined); + }); }); diff --git a/tsconfig.json b/tsconfig.json index 98dea4e5..3b3ded70 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,7 +7,7 @@ * @note This entire file will be updated automatically. * @note Instead of editing here, please review `./tsconfig.mjs`. * - * Last generated using `./tsconfig.mjs` Dec 29, 2024 6:03 AM UTC. + * Last generated using `./tsconfig.mjs` Dec 30, 2024 6:09 AM UTC. */ { "include": ["./src/**/*", "./dev-types.d.ts"], diff --git a/wrangler.toml b/wrangler.toml index 6f952930..829fac75 100644 --- a/wrangler.toml +++ b/wrangler.toml @@ -7,9 +7,9 @@ # @note This entire file will be updated automatically. # @note Instead of editing here, please review `./wrangler.mjs`. # -# Last generated using `./wrangler.mjs` Dec 29, 2024 6:03 AM UTC. +# Last generated using `./wrangler.mjs` Dec 30, 2024 6:09 AM UTC. ## send_metrics = false -compatibility_date = "2024-03-02" +compatibility_date = "2024-09-23" compatibility_flags = [ ]