@logto/core-kit 2.3.0 → 2.4.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.
package/lib/openid.d.ts CHANGED
@@ -12,7 +12,7 @@ export declare enum ReservedResource {
12
12
  */
13
13
  Organization = "urn:logto:resource:organizations"
14
14
  }
15
- export type UserClaim = 'name' | 'picture' | 'username' | 'email' | 'email_verified' | 'phone_number' | 'phone_number_verified' | 'roles' | 'organizations' | 'organization_data' | 'organization_roles' | 'custom_data' | 'identities';
15
+ export type UserClaim = 'name' | 'given_name' | 'family_name' | 'middle_name' | 'nickname' | 'preferred_username' | 'profile' | 'picture' | 'website' | 'email' | 'email_verified' | 'gender' | 'birthdate' | 'zoneinfo' | 'locale' | 'phone_number' | 'phone_number_verified' | 'address' | 'updated_at' | 'username' | 'roles' | 'organizations' | 'organization_data' | 'organization_roles' | 'custom_data' | 'identities' | 'created_at';
16
16
  /**
17
17
  * Scopes for ID Token and Userinfo Endpoint.
18
18
  */
@@ -35,6 +35,12 @@ export declare enum UserScope {
35
35
  * See {@link idTokenClaims} for mapped claims in ID Token and {@link userinfoClaims} for additional claims in Userinfo Endpoint.
36
36
  */
37
37
  Phone = "phone",
38
+ /**
39
+ * Scope for user address.
40
+ *
41
+ * See {@link idTokenClaims} for mapped claims in ID Token and {@link userinfoClaims} for additional claims in Userinfo Endpoint.
42
+ */
43
+ Address = "address",
38
44
  /**
39
45
  * Scope for user's custom data.
40
46
  *
@@ -68,6 +74,8 @@ export declare enum UserScope {
68
74
  }
69
75
  /**
70
76
  * Mapped claims that ID Token includes.
77
+ *
78
+ * @see {@link https://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims | OpenID Connect Core 1.0} for standard scope - claim mapping.
71
79
  */
72
80
  export declare const idTokenClaims: Readonly<Record<UserScope, UserClaim[]>>;
73
81
  /**
package/lib/openid.js CHANGED
@@ -37,6 +37,12 @@ export var UserScope;
37
37
  * See {@link idTokenClaims} for mapped claims in ID Token and {@link userinfoClaims} for additional claims in Userinfo Endpoint.
38
38
  */
39
39
  UserScope["Phone"] = "phone";
40
+ /**
41
+ * Scope for user address.
42
+ *
43
+ * See {@link idTokenClaims} for mapped claims in ID Token and {@link userinfoClaims} for additional claims in Userinfo Endpoint.
44
+ */
45
+ UserScope["Address"] = "address";
40
46
  /**
41
47
  * Scope for user's custom data.
42
48
  *
@@ -70,11 +76,33 @@ export var UserScope;
70
76
  })(UserScope || (UserScope = {}));
71
77
  /**
72
78
  * Mapped claims that ID Token includes.
79
+ *
80
+ * @see {@link https://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims | OpenID Connect Core 1.0} for standard scope - claim mapping.
73
81
  */
74
82
  export const idTokenClaims = Object.freeze({
75
- [UserScope.Profile]: ['name', 'picture', 'username'],
83
+ [UserScope.Profile]: [
84
+ // Standard claims
85
+ 'name',
86
+ 'family_name',
87
+ 'given_name',
88
+ 'middle_name',
89
+ 'nickname',
90
+ 'preferred_username',
91
+ 'profile',
92
+ 'picture',
93
+ 'website',
94
+ 'gender',
95
+ 'birthdate',
96
+ 'zoneinfo',
97
+ 'locale',
98
+ 'updated_at',
99
+ // Custom claims
100
+ 'username',
101
+ 'created_at',
102
+ ],
76
103
  [UserScope.Email]: ['email', 'email_verified'],
77
104
  [UserScope.Phone]: ['phone_number', 'phone_number_verified'],
105
+ [UserScope.Address]: ['address'],
78
106
  [UserScope.Roles]: ['roles'],
79
107
  [UserScope.Organizations]: ['organizations'],
80
108
  [UserScope.OrganizationRoles]: ['organization_roles'],
@@ -88,6 +116,7 @@ export const userinfoClaims = Object.freeze({
88
116
  [UserScope.Profile]: [],
89
117
  [UserScope.Email]: [],
90
118
  [UserScope.Phone]: [],
119
+ [UserScope.Address]: [],
91
120
  [UserScope.Roles]: [],
92
121
  [UserScope.Organizations]: ['organization_data'],
93
122
  [UserScope.OrganizationRoles]: [],
@@ -1,4 +1,5 @@
1
1
  import { z } from 'zod';
2
+ import { getPwnPasswordsForTest, isIntegrationTest } from './utils/integration-test.js';
2
3
  /** Password policy configuration guard. */
3
4
  export const passwordPolicyGuard = z.object({
4
5
  length: z
@@ -213,6 +214,9 @@ export class PasswordPolicyChecker {
213
214
  * @returns Whether the password has been pwned.
214
215
  */
215
216
  async hasBeenPwned(password) {
217
+ if (isIntegrationTest()) {
218
+ return getPwnPasswordsForTest().includes(password);
219
+ }
216
220
  const hash = await this.subtle.digest('SHA-1', new TextEncoder().encode(password));
217
221
  const hashHex = Array.from(new Uint8Array(hash))
218
222
  .map((binary) => binary.toString(16).padStart(2, '0'))
package/lib/regex.d.ts CHANGED
@@ -7,5 +7,5 @@ export declare const mobileUriSchemeProtocolRegEx: RegExp;
7
7
  export declare const hexColorRegEx: RegExp;
8
8
  export declare const dateRegex: RegExp;
9
9
  export declare const noSpaceRegEx: RegExp;
10
- /** Full domain that consists of at least 3 parts, e.g. foo.bar.com */
10
+ /** Full domain that consists of at least 3 parts, e.g. foo.bar.com or example-foo.bar.com */
11
11
  export declare const domainRegEx: RegExp;
package/lib/regex.js CHANGED
@@ -7,5 +7,5 @@ export const mobileUriSchemeProtocolRegEx = /^[a-z][\d+_a-z-]*(\.[\d+_a-z-]+)+:$
7
7
  export const hexColorRegEx = /^#[\da-f]{3}([\da-f]{3})?$/i;
8
8
  export const dateRegex = /^\d{4}(-\d{2}){2}/;
9
9
  export const noSpaceRegEx = /^\S+$/;
10
- /** Full domain that consists of at least 3 parts, e.g. foo.bar.com */
11
- export const domainRegEx = /^[\dA-Za-z]+(\.[\dA-Za-z]+){2,}$/;
10
+ /** Full domain that consists of at least 3 parts, e.g. foo.bar.com or example-foo.bar.com */
11
+ export const domainRegEx = /^[\dA-Za-z](?:[\dA-Za-z-]*[\dA-Za-z])?(?:\.[\dA-Za-z](?:[\dA-Za-z-]*[\dA-Za-z])?){2,}$/;
@@ -0,0 +1,2 @@
1
+ export declare const isIntegrationTest: () => boolean;
2
+ export declare const getPwnPasswordsForTest: () => readonly string[];
@@ -0,0 +1,8 @@
1
+ import { yes } from '@silverhand/essentials';
2
+ export const isIntegrationTest = () => yes(process.env.INTEGRATION_TEST);
3
+ export const getPwnPasswordsForTest = () => {
4
+ if (!isIntegrationTest()) {
5
+ throw new Error('This function should only be called in integration tests');
6
+ }
7
+ return Object.freeze(['123456aA', 'test_password']);
8
+ };
@@ -1,3 +1,7 @@
1
1
  export declare const validateRedirectUrl: (url: string, type: 'web' | 'mobile') => boolean;
2
2
  export declare const validateUriOrigin: (url: string) => boolean;
3
3
  export declare const isValidUrl: (url?: string) => boolean;
4
+ /**
5
+ * Check if the given URL is localhost
6
+ */
7
+ export declare const isLocalhost: (url: string) => boolean;
package/lib/utils/url.js CHANGED
@@ -25,3 +25,10 @@ export const isValidUrl = (url) => {
25
25
  return false;
26
26
  }
27
27
  };
28
+ /**
29
+ * Check if the given URL is localhost
30
+ */
31
+ export const isLocalhost = (url) => {
32
+ const parsedUrl = new URL(url);
33
+ return ['localhost', '127.0.0.1', '::1'].includes(parsedUrl.hostname);
34
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@logto/core-kit",
3
- "version": "2.3.0",
3
+ "version": "2.4.0",
4
4
  "author": "Silverhand Inc. <contact@silverhand.io>",
5
5
  "homepage": "https://github.com/logto-io/toolkit#readme",
6
6
  "repository": {
@@ -31,29 +31,27 @@
31
31
  "dependencies": {
32
32
  "@logto/language-kit": "^1.1.0",
33
33
  "@logto/shared": "^3.1.0",
34
+ "@silverhand/essentials": "^2.9.0",
34
35
  "color": "^4.2.3"
35
36
  },
36
37
  "optionalDependencies": {
37
38
  "zod": "^3.22.4"
38
39
  },
39
40
  "devDependencies": {
40
- "@jest/types": "^29.0.3",
41
41
  "@silverhand/eslint-config": "5.0.0",
42
- "@silverhand/essentials": "^2.9.0",
43
42
  "@silverhand/ts-config": "5.0.0",
44
43
  "@silverhand/ts-config-react": "5.0.0",
45
44
  "@types/color": "^3.0.3",
46
- "@types/jest": "^29.4.0",
47
45
  "@types/node": "^20.9.5",
48
46
  "@types/react": "^18.0.31",
47
+ "@vitest/coverage-v8": "^1.4.0",
49
48
  "eslint": "^8.44.0",
50
- "jest": "^29.7.0",
51
49
  "lint-staged": "^15.0.0",
52
50
  "postcss": "^8.4.31",
53
51
  "prettier": "^3.0.0",
54
52
  "stylelint": "^15.0.0",
55
- "tslib": "^2.4.1",
56
- "typescript": "^5.3.3"
53
+ "typescript": "^5.3.3",
54
+ "vitest": "^1.4.0"
57
55
  },
58
56
  "eslintConfig": {
59
57
  "extends": "@silverhand"
@@ -69,13 +67,11 @@
69
67
  "precommit": "lint-staged",
70
68
  "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental",
71
69
  "build": "rm -rf lib/ && tsc -p tsconfig.build.json",
72
- "build:test": "pnpm build -p tsconfig.test.json --sourcemap",
70
+ "build:test": "pnpm build",
73
71
  "lint": "eslint --ext .ts src",
74
72
  "lint:report": "pnpm lint --format json --output-file report.json",
75
73
  "stylelint": "stylelint \"scss/**/*.scss\"",
76
- "test:only": "NODE_OPTIONS=--experimental-vm-modules jest",
77
- "test": "pnpm build:test && pnpm test:only",
78
- "test:ci": "pnpm test:only",
79
- "test:coverage": "pnpm test:only --silent --coverage"
74
+ "test": "vitest src",
75
+ "test:ci": "pnpm run test --silent --coverage"
80
76
  }
81
77
  }
@@ -162,7 +162,17 @@
162
162
  --color-specific-icon-bg: #f3effa;
163
163
  --color-specific-toggle-off-enable: var(--color-neutral-90);
164
164
  --color-specific-unselected-disabled: var(--color-hover); // 8% Neutral-10
165
+ --color-specific-selected-disabled: var(--color-primary-80);
166
+ --color-specific-focused-inside: var(--color-primary-30);
167
+ --color-specific-focused-outside: var(--color-primary-40);
168
+ --color-specific-button-icon: rgba(255, 255, 255, 70%); // 70% static white
169
+ --color-overlay-primary-hover: rgba(93, 52, 242, 8%); // 8% Primary-40
170
+ --color-overlay-primary-pressed: rgba(93, 52, 242, 12%); // 12% Primary-40
165
171
  --color-function-n-overlay-primary-focused: rgba(93, 52, 242, 16%); // 16% Primary-40
172
+ --color-overlay-danger-hover: rgba(186, 27, 27, 8%); // 8% Error-40
173
+ --color-overlay-dark-bg-hover: rgba(255, 255, 255, 12%); // 12% static white
174
+ --color-overlay-dark-bg-pressed: rgba(255, 255, 255, 18%); // 18% static white
175
+ --color-overlay-dark-bg-focused: rgba(255, 255, 255, 24%); // 24% static white
166
176
 
167
177
  // Shadows
168
178
  --shadow-1: 0 4px 8px rgba(0, 0, 0, 8%);
@@ -201,6 +211,15 @@
201
211
  --color-bg-state-unselected: var(--color-neutral-90);
202
212
  --color-bg-state-disabled: rgba(25, 28, 29, 8%); // 8% --color-neutral-10
203
213
  --color-bg-info-tag: rgba(229, 225, 236, 80%); // 80% --color-neutral-variant-90
214
+
215
+ // code editor
216
+ --color-code-bg: #181133;
217
+ --color-code-bg-float: #30314e;
218
+ --color-code-white: #f7f8f8;
219
+ --color-code-grey: #adaab4;
220
+ --color-code-dark-bg-focused: rgba(255, 255, 255, 24%);
221
+ --color-code-dark-bg-hover: rgba(255, 255, 255, 12%);
222
+ --color-code-dark-bg-pressed: rgba(255, 255, 255, 18%);
204
223
  }
205
224
 
206
225
  @mixin dark {
@@ -367,7 +386,17 @@
367
386
  --color-specific-icon-bg: rgba(247, 248, 248, 12%);
368
387
  --color-specific-toggle-off-enable: var(--color-neutral-90);
369
388
  --color-specific-unselected-disabled: var(--color-hover); // 8% Neutral-10
389
+ --color-specific-selected-disabled: var(--color-primary-80);
390
+ --color-specific-focused-inside: var(--color-primary-40);
391
+ --color-specific-focused-outside: rgba(#cabeff, 32%); // 32% Primary-40
392
+ --color-specific-button-icon: rgba(255, 255, 255, 60%); // 60% static white
393
+ --color-overlay-primary-hover: rgba(202, 190, 255, 8%); // 8% Primary-40
394
+ --color-overlay-primary-pressed: rgba(202, 190, 255, 12%); // 12% Primary-40
370
395
  --color-function-n-overlay-primary-focused: rgba(202, 190, 255, 16%); // 16% Primary-40
396
+ --color-overlay-danger-hover: rgba(186, 27, 27, 8%); // 8% Error-40
397
+ --color-overlay-dark-bg-hover: rgba(255, 255, 255, 12%); // 12% static white
398
+ --color-overlay-dark-bg-pressed: rgba(255, 255, 255, 18%); // 18% static white
399
+ --color-overlay-dark-bg-focused: rgba(255, 255, 255, 24%); // 24% static white
371
400
 
372
401
  // Shadows
373
402
  --shadow-1: 0 4px 8px rgba(0, 0, 0, 8%);
@@ -409,4 +438,11 @@
409
438
  --color-bg-state-unselected: var(--color-neutral-90);
410
439
  --color-bg-state-disabled: rgba(247, 248, 248, 8%); // 8% --color-neutral-10
411
440
  --color-bg-info-tag: var(--color-neutral-variant-90);
441
+
442
+ // code editor
443
+ --color-code-bg: #090613;
444
+ --color-code-bg-float: #232439;
445
+ --color-code-white: #f7f8f8;
446
+ --color-code-grey: #adaab4;
447
+ --color-code-dark-bg-focused: rgba(255, 255, 255, 24%);
412
448
  }
package/scss/_fonts.scss CHANGED
@@ -30,4 +30,5 @@ $font-family:
30
30
  --font-body-3: 400 12px/16px #{$font-family};
31
31
  --font-section-head-1: 700 12px/16px #{$font-family};
32
32
  --font-section-head-2: 700 10px/16px #{$font-family};
33
+ --font-code: 500 14px/20px 'Roboto Mono', monospace;
33
34
  }