@logto/core-kit 2.11.0 → 2.12.0

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.
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Validates an email blocklist item.
3
+ *
4
+ * An item can be either a full email address (`foo@example.com`) or a domain entry
5
+ * prefixed with `@` (`@example.com`). `*` can be used inside the local part or
6
+ * domain while keeping the same email/domain shapes.
7
+ */
8
+ export declare const isEmailBlocklistItem: (value: string) => boolean;
9
+ /**
10
+ * Checks whether an email address matches an email blocklist item.
11
+ *
12
+ * Matching is case-insensitive. Domain items (`@example.com`) match only the email
13
+ * domain, while full email items match the complete email address.
14
+ */
15
+ export declare const matchesEmailBlocklistItem: (item: string, email: string) => boolean;
@@ -0,0 +1,65 @@
1
+ import { emailOrEmailDomainRegEx } from './regex.js';
2
+ const wildcard = '*';
3
+ const emailSeparator = '@';
4
+ const domainSeparator = '.';
5
+ const whitespaceRegEx = /\s/u;
6
+ const wildcardOnlyDomainRegEx = /^[*.]+$/u;
7
+ const hasWildcard = (value) => value.includes(wildcard);
8
+ const escapeRegExp = (value) => value.replaceAll(/[.+?^${}()|[\]\\]/gu, '\\$&');
9
+ const buildWildcardRegExp = (pattern) => new RegExp(`^${escapeRegExp(pattern).replaceAll(wildcard, '.*')}$`, 'u');
10
+ const isValidWildcardLocalPart = (localPart) => localPart.length > 0 && !localPart.includes(emailSeparator) && !whitespaceRegEx.test(localPart);
11
+ const isValidWildcardDomain = (domain) => domain.length > 0 &&
12
+ domain.includes(domainSeparator) &&
13
+ !domain.includes(emailSeparator) &&
14
+ !domain.includes(`${domainSeparator}${domainSeparator}`) &&
15
+ !domain.startsWith(domainSeparator) &&
16
+ !domain.endsWith(domainSeparator) &&
17
+ !whitespaceRegEx.test(domain) &&
18
+ !wildcardOnlyDomainRegEx.test(domain);
19
+ /**
20
+ * Validates an email blocklist item.
21
+ *
22
+ * An item can be either a full email address (`foo@example.com`) or a domain entry
23
+ * prefixed with `@` (`@example.com`). `*` can be used inside the local part or
24
+ * domain while keeping the same email/domain shapes.
25
+ */
26
+ export const isEmailBlocklistItem = (value) => {
27
+ if (!hasWildcard(value)) {
28
+ return emailOrEmailDomainRegEx.test(value);
29
+ }
30
+ if (whitespaceRegEx.test(value)) {
31
+ return false;
32
+ }
33
+ if (value.startsWith(emailSeparator)) {
34
+ return isValidWildcardDomain(value.slice(1));
35
+ }
36
+ const emailParts = value.split(emailSeparator);
37
+ if (emailParts.length !== 2) {
38
+ return false;
39
+ }
40
+ const [localPart, domain] = emailParts;
41
+ return (localPart !== undefined &&
42
+ domain !== undefined &&
43
+ isValidWildcardLocalPart(localPart) &&
44
+ isValidWildcardDomain(domain));
45
+ };
46
+ /**
47
+ * Checks whether an email address matches an email blocklist item.
48
+ *
49
+ * Matching is case-insensitive. Domain items (`@example.com`) match only the email
50
+ * domain, while full email items match the complete email address.
51
+ */
52
+ export const matchesEmailBlocklistItem = (item, email) => {
53
+ const normalizedItem = item.toLowerCase();
54
+ const normalizedEmail = email.toLowerCase();
55
+ const domain = normalizedEmail.split(emailSeparator)[1];
56
+ if (normalizedItem.startsWith(emailSeparator)) {
57
+ return Boolean(domain &&
58
+ (hasWildcard(normalizedItem.slice(1))
59
+ ? buildWildcardRegExp(normalizedItem.slice(1)).test(domain)
60
+ : domain === normalizedItem.slice(1)));
61
+ }
62
+ return hasWildcard(normalizedItem)
63
+ ? buildWildcardRegExp(normalizedItem).test(normalizedEmail)
64
+ : normalizedEmail === normalizedItem;
65
+ };
package/lib/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export * from './utils/index.js';
2
2
  export * from './custom-ui-csp.js';
3
+ export * from './email-blocklist.js';
3
4
  export * from './regex.js';
4
5
  export * from './openid.js';
5
6
  export * from './models/index.js';
package/lib/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  export * from './utils/index.js';
2
2
  export * from './custom-ui-csp.js';
3
+ export * from './email-blocklist.js';
3
4
  export * from './regex.js';
4
5
  export * from './openid.js';
5
6
  export * from './models/index.js';
package/lib/regex.js CHANGED
@@ -1,6 +1,6 @@
1
1
  export const emailRegEx = /^\S+@\S+\.\S+$/;
2
2
  /** Validates full email address or email domain. */
3
- export const emailOrEmailDomainRegEx = /^\S+@\S+\.\S+|^@\S+\.\S+$/;
3
+ export const emailOrEmailDomainRegEx = /^(?:[^\s@]+@[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?(?:\.[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?)+|@[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?(?:\.[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?)+)$(?![\s\S])/u;
4
4
  export const phoneRegEx = /^\d+$/;
5
5
  export const phoneInputRegEx = /^\+?[\d-( )]+$/;
6
6
  export const usernameRegEx = /^[A-Z_a-z]\w*$/;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@logto/core-kit",
3
- "version": "2.11.0",
3
+ "version": "2.12.0",
4
4
  "author": "Silverhand Inc. <contact@silverhand.io>",
5
5
  "homepage": "https://github.com/logto-io/toolkit#readme",
6
6
  "repository": {
@@ -36,7 +36,7 @@
36
36
  "@silverhand/essentials": "^2.9.1",
37
37
  "color": "^4.2.3",
38
38
  "@logto/language-kit": "^1.3.0",
39
- "@logto/shared": "^3.4.1"
39
+ "@logto/shared": "^3.4.2"
40
40
  },
41
41
  "optionalDependencies": {
42
42
  "zod": "3.24.3"
@@ -52,7 +52,7 @@
52
52
  "@vitest/coverage-v8": "^4.1.8",
53
53
  "eslint": "^8.56.0",
54
54
  "lint-staged": "^15.0.0",
55
- "postcss": "^8.4.31",
55
+ "postcss": "^8.5.18",
56
56
  "prettier": "^3.5.3",
57
57
  "stylelint": "^15.0.0",
58
58
  "typescript": "^5.5.3",