0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-16 20:26:19 -05:00

refactor: bring back toolkit packages

This commit is contained in:
Gao Sun 2022-12-14 13:23:59 +08:00
parent 7bf52aa7ad
commit 32fa807d16
No known key found for this signature in database
GPG key ID: 13EBE123E4773688
97 changed files with 2411 additions and 241 deletions

View file

@ -2,16 +2,16 @@
node_modules
# testing
/packages/*/coverage
/packages/**/coverage
# report
/report.json
/packages/*/report.json
/packages/**/report.json
# production
/packages/*/build
/packages/*/lib
/packages/*/dist
/packages/**/build
/packages/**/lib
/packages/**/dist
# logs
logs

10
.gitignore vendored
View file

@ -6,16 +6,16 @@ node_modules
.pnp.js
# testing
/packages/*/coverage
/packages/**/coverage
# report
/report.json
/packages/*/report.json
/packages/**/report.json
# production
/packages/*/build
/packages/*/lib
/packages/*/dist
/packages/**/build
/packages/**/lib
/packages/**/dist
# logs
logs

View file

@ -1,7 +1,7 @@
set -eo pipefail
echo Prune dependencies
rm -rf node_modules packages/*/node_modules
rm -rf node_modules packages/**/node_modules
echo Install production dependencies
NODE_ENV=production pnpm i
@ -9,8 +9,8 @@ NODE_ENV=production pnpm i
echo Prune files
rm -rf \
.git .github .husky .vscode .parcel-cache pnpm-*.yaml *.js \
packages/*/src \
packages/*/*.config.js packages/*/*.config.ts packages/*/tsconfig*.json
packages/**/src \
packages/**/*.config.js packages/**/*.config.ts packages/**/tsconfig*.json
echo Tar
cd ..

View file

@ -8,7 +8,10 @@
"stylelint.validate": ["css", "scss"],
"eslint.workingDirectories": [
{
"pattern": "./packages/*"
"pattern": "./packages/*",
},
{
"pattern": "./packages/toolkit/*",
}
],
"eslint.validate": [

View file

@ -18,7 +18,7 @@ RUN pnpm -r build
RUN pnpm cli connector add --official -p .
# Prune dependencies for production
RUN rm -rf node_modules packages/*/node_modules
RUN rm -rf node_modules packages/**/node_modules
RUN NODE_ENV=production pnpm i
# Clean up

View file

@ -26,14 +26,6 @@
"husky": "^8.0.0",
"typescript": "^4.9.4"
},
"workspaces": {
"packages": [
"packages/*"
],
"nohoist": [
"**/module-alias"
]
},
"engines": {
"node": "^16.13.0 || ^18.12.0",
"pnpm": "^7.14.0"

View file

@ -63,7 +63,7 @@
"slonik-sql-tag-raw": "^1.1.4",
"tar": "^6.1.11",
"yargs": "^17.6.0",
"zod": "^3.19.1"
"zod": "^3.20.2"
},
"devDependencies": {
"@silverhand/eslint-config": "1.3.0",

View file

@ -19,8 +19,8 @@
},
"devDependencies": {
"@fontsource/roboto-mono": "^4.5.7",
"@logto/core-kit": "1.0.0-beta.28",
"@logto/language-kit": "1.0.0-beta.28",
"@logto/core-kit": "workspace:*",
"@logto/language-kit": "workspace:*",
"@logto/phrases": "workspace:*",
"@logto/phrases-ui": "workspace:*",
"@logto/react": "1.0.0-beta.14",
@ -88,7 +88,7 @@
"stylelint": "^14.9.1",
"swr": "^1.3.0",
"typescript": "^4.9.4",
"zod": "^3.19.1"
"zod": "^3.20.2"
},
"engines": {
"node": "^16.13.0 || ^18.12.0"

View file

@ -26,9 +26,9 @@
},
"dependencies": {
"@logto/cli": "workspace:*",
"@logto/connector-kit": "1.0.0-beta.28",
"@logto/core-kit": "1.0.0-beta.28",
"@logto/language-kit": "1.0.0-beta.28",
"@logto/connector-kit": "workspace:*",
"@logto/core-kit": "workspace:*",
"@logto/language-kit": "workspace:*",
"@logto/phrases": "workspace:*",
"@logto/phrases-ui": "workspace:*",
"@logto/schemas": "workspace:*",
@ -68,7 +68,7 @@
"slonik-sql-tag-raw": "^1.1.4",
"snake-case": "^3.0.4",
"snakecase-keys": "^5.4.4",
"zod": "^3.19.1"
"zod": "^3.20.2"
},
"devDependencies": {
"@silverhand/eslint-config": "1.3.0",

View file

@ -2,7 +2,7 @@ import type { Optional } from '@silverhand/essentials';
import { getEnv, getEnvAsStringArray } from '@silverhand/essentials';
import type { DatabasePool } from 'slonik';
import { getOidcConfigs } from '#src/lib/logto-config.js';
import { getOidcConfigs } from '#src/libraries/logto-config.js';
import { appendPath } from '#src/utils/url.js';
import { checkAlterationState } from './check-alteration-state.js';

View file

@ -44,7 +44,7 @@ mockEsm('#src/queries/custom-phrase.js', () => ({
findCustomPhraseByLanguageTag,
}));
const { getPhrase } = await import('#src/lib/phrase.js');
const { getPhrase } = await import('#src/libraries/phrase.js');
afterEach(() => {
jest.clearAllMocks();

View file

@ -47,6 +47,8 @@ export const getUserInfoByAuthCode = async (
})
);
// FIXME: @Darcy
// @ts-expect-error pending fix
return connector.getUserInfo(data);
};

View file

@ -12,7 +12,7 @@ import {
encryptUserPassword,
generateUserId,
insertUser,
} from '#src/lib/user.js';
} from '#src/libraries/user.js';
import koaGuard from '#src/middleware/koa-guard.js';
import koaPagination from '#src/middleware/koa-pagination.js';
import { revokeInstanceByUserId } from '#src/queries/oidc-model-instance.js';

View file

@ -13,8 +13,8 @@ import {
} from '#src/connectors/index.js';
import type { LogtoConnector } from '#src/connectors/types.js';
import RequestError from '#src/errors/RequestError/index.js';
import { checkSocialConnectorTargetAndPlatformUniqueness } from '#src/lib/connector.js';
import { removeUnavailableSocialConnectorTargets } from '#src/lib/sign-in-experience/index.js';
import { checkSocialConnectorTargetAndPlatformUniqueness } from '#src/libraries/connector.js';
import { removeUnavailableSocialConnectorTargets } from '#src/libraries/sign-in-experience/index.js';
import koaGuard from '#src/middleware/koa-guard.js';
import {
findConnectorById,

View file

@ -4,8 +4,8 @@ import { conditional } from '@silverhand/essentials';
import type { Provider } from 'oidc-provider';
import { getLogtoConnectorById } from '#src/connectors/index.js';
import { assignInteractionResults } from '#src/lib/session.js';
import { encryptUserPassword, generateUserId, insertUser } from '#src/lib/user.js';
import { assignInteractionResults } from '#src/libraries/session.js';
import { encryptUserPassword, generateUserId, insertUser } from '#src/libraries/user.js';
import { findUserById, updateUserById } from '#src/queries/user.js';
import type {

View file

@ -3,7 +3,7 @@ import { Event } from '@logto/schemas';
import type { Provider } from 'oidc-provider';
import RequestError from '#src/errors/RequestError/index.js';
import { assignInteractionResults } from '#src/lib/session.js';
import { assignInteractionResults } from '#src/libraries/session.js';
import koaGuard from '#src/middleware/koa-guard.js';
import assertThat from '#src/utils/assert-that.js';

View file

@ -4,7 +4,7 @@ import type { MiddlewareType } from 'koa';
import type { IRouterParamContext } from 'koa-router';
import type { Provider } from 'oidc-provider';
import { getSignInExperienceForApplication } from '#src/lib/sign-in-experience/index.js';
import { getSignInExperienceForApplication } from '#src/libraries/sign-in-experience/index.js';
import {
signInModeValidation,

View file

@ -1,7 +1,7 @@
import type { Event } from '@logto/schemas';
import { PasscodeType } from '@logto/schemas';
import { createPasscode, sendPasscode, verifyPasscode } from '#src/lib/passcode.js';
import { createPasscode, sendPasscode, verifyPasscode } from '#src/libraries/passcode.js';
import type { LogContext } from '#src/middleware/koa-log.js';
import { getPasswordlessRelatedLogType } from '#src/routes/session/utils.js';

View file

@ -3,7 +3,7 @@ import { ConnectorType } from '@logto/schemas';
import { getLogtoConnectorById } from '#src/connectors/index.js';
import type { SocialUserInfo } from '#src/connectors/types.js';
import { getUserInfoByAuthCode } from '#src/lib/social.js';
import { getUserInfoByAuthCode } from '#src/libraries/social.js';
import type { LogContext } from '#src/middleware/koa-log.js';
import assertThat from '#src/utils/assert-that.js';
@ -17,6 +17,8 @@ export const createSocialAuthorizationUrl = async (payload: SocialAuthorizationU
assertThat(connector.type === ConnectorType.Social, 'connector.unexpected_type');
// FIXME: @Darcy
// @ts-expect-error pending fix
return connector.getAuthorizationUri({ state, redirectUri });
};

View file

@ -2,7 +2,7 @@ import type { Event, SocialConnectorPayload, SocialIdentityPayload } from '@logt
import type { Provider } from 'oidc-provider';
import RequestError from '#src/errors/RequestError/index.js';
import { verifyUserPassword } from '#src/lib/user.js';
import { verifyUserPassword } from '#src/libraries/user.js';
import assertThat from '#src/utils/assert-that.js';
import type {

View file

@ -2,7 +2,7 @@ import { deduplicate } from '@silverhand/essentials';
import type { Provider } from 'oidc-provider';
import RequestError from '#src/errors/RequestError/index.js';
import { findSocialRelatedUser } from '#src/lib/social.js';
import { findSocialRelatedUser } from '#src/libraries/social.js';
import assertThat from '#src/utils/assert-that.js';
import { maskUserInfo } from '#src/utils/format.js';

View file

@ -6,7 +6,7 @@ import {
import type { Provider } from 'oidc-provider';
import detectLanguage from '#src/i18n/detect-language.js';
import { getPhrase } from '#src/lib/phrase.js';
import { getPhrase } from '#src/libraries/phrase.js';
import { findAllCustomLanguageTags } from '#src/queries/custom-phrase.js';
import { findDefaultSignInExperience } from '#src/queries/sign-in-experience.js';

View file

@ -8,9 +8,9 @@ import { object, string, unknown } from 'zod';
import { getLogtoConnectorById } from '#src/connectors/index.js';
import RequestError from '#src/errors/RequestError/index.js';
import { checkSessionHealth } from '#src/lib/session.js';
import { getUserInfoByAuthCode } from '#src/lib/social.js';
import { checkIdentifierCollision, encryptUserPassword } from '#src/lib/user.js';
import { checkSessionHealth } from '#src/libraries/session.js';
import { getUserInfoByAuthCode } from '#src/libraries/social.js';
import { checkIdentifierCollision, encryptUserPassword } from '#src/libraries/user.js';
import koaGuard from '#src/middleware/koa-guard.js';
import { deleteUserIdentity, findUserById, updateUserById } from '#src/queries/user.js';
import assertThat from '#src/utils/assert-that.js';

View file

@ -3,9 +3,12 @@ import type { Provider } from 'oidc-provider';
import { object, string } from 'zod';
import RequestError from '#src/errors/RequestError/index.js';
import { assignInteractionResults, getApplicationIdFromInteraction } from '#src/lib/session.js';
import { getSignInExperienceForApplication } from '#src/lib/sign-in-experience/index.js';
import { encryptUserPassword } from '#src/lib/user.js';
import {
assignInteractionResults,
getApplicationIdFromInteraction,
} from '#src/libraries/session.js';
import { getSignInExperienceForApplication } from '#src/libraries/sign-in-experience/index.js';
import { encryptUserPassword } from '#src/libraries/user.js';
import koaGuard from '#src/middleware/koa-guard.js';
import {
findUserById,

View file

@ -4,7 +4,7 @@ import type { Provider } from 'oidc-provider';
import { z } from 'zod';
import RequestError from '#src/errors/RequestError/index.js';
import { encryptUserPassword } from '#src/lib/user.js';
import { encryptUserPassword } from '#src/libraries/user.js';
import koaGuard from '#src/middleware/koa-guard.js';
import { findUserById, updateUserById } from '#src/queries/user.js';
import assertThat from '#src/utils/assert-that.js';

View file

@ -8,7 +8,7 @@ import type { Provider } from 'oidc-provider';
import { object, string } from 'zod';
import RequestError from '#src/errors/RequestError/index.js';
import { assignInteractionResults, saveUserFirstConsentedAppId } from '#src/lib/session.js';
import { assignInteractionResults, saveUserFirstConsentedAppId } from '#src/libraries/session.js';
import { findUserById } from '#src/queries/user.js';
import assertThat from '#src/utils/assert-that.js';

View file

@ -5,8 +5,8 @@ import type { Provider } from 'oidc-provider';
import { errors } from 'oidc-provider';
import RequestError from '#src/errors/RequestError/index.js';
import { getApplicationIdFromInteraction } from '#src/lib/session.js';
import { getSignInExperienceForApplication } from '#src/lib/sign-in-experience/index.js';
import { getApplicationIdFromInteraction } from '#src/libraries/session.js';
import { getSignInExperienceForApplication } from '#src/libraries/sign-in-experience/index.js';
import assertThat from '#src/utils/assert-that.js';
export default function koaGuardSessionAction<StateT, ContextT, ResponseBodyT>(

View file

@ -3,9 +3,12 @@ import type { MiddlewareType } from 'koa';
import type { Provider } from 'oidc-provider';
import RequestError from '#src/errors/RequestError/index.js';
import { assignInteractionResults, getApplicationIdFromInteraction } from '#src/lib/session.js';
import { getSignInExperienceForApplication } from '#src/lib/sign-in-experience/index.js';
import { generateUserId, insertUser } from '#src/lib/user.js';
import {
assignInteractionResults,
getApplicationIdFromInteraction,
} from '#src/libraries/session.js';
import { getSignInExperienceForApplication } from '#src/libraries/sign-in-experience/index.js';
import { generateUserId, insertUser } from '#src/libraries/user.js';
import type { WithLogContext } from '#src/middleware/koa-log.js';
import {
hasUserWithPhone,

View file

@ -5,9 +5,12 @@ import type { Provider } from 'oidc-provider';
import { object, string } from 'zod';
import RequestError from '#src/errors/RequestError/index.js';
import { assignInteractionResults, getApplicationIdFromInteraction } from '#src/lib/session.js';
import { getSignInExperienceForApplication } from '#src/lib/sign-in-experience/index.js';
import { encryptUserPassword, generateUserId, insertUser } from '#src/lib/user.js';
import {
assignInteractionResults,
getApplicationIdFromInteraction,
} from '#src/libraries/session.js';
import { getSignInExperienceForApplication } from '#src/libraries/sign-in-experience/index.js';
import { encryptUserPassword, generateUserId, insertUser } from '#src/libraries/user.js';
import koaGuard from '#src/middleware/koa-guard.js';
import {
findUserByEmail,

View file

@ -4,7 +4,7 @@ import type { Provider } from 'oidc-provider';
import { object, string } from 'zod';
import RequestError from '#src/errors/RequestError/index.js';
import { createPasscode, sendPasscode, verifyPasscode } from '#src/lib/passcode.js';
import { createPasscode, sendPasscode, verifyPasscode } from '#src/libraries/passcode.js';
import koaGuard from '#src/middleware/koa-guard.js';
import { findUserByEmail, findUserByPhone } from '#src/queries/user.js';
import { passcodeTypeGuard } from '#src/routes/session/types.js';

View file

@ -7,14 +7,17 @@ import { object, string, unknown } from 'zod';
import { getLogtoConnectorById } from '#src/connectors/index.js';
import RequestError from '#src/errors/RequestError/index.js';
import { assignInteractionResults, getApplicationIdFromInteraction } from '#src/lib/session.js';
import { getSignInExperienceForApplication } from '#src/lib/sign-in-experience/index.js';
import {
assignInteractionResults,
getApplicationIdFromInteraction,
} from '#src/libraries/session.js';
import { getSignInExperienceForApplication } from '#src/libraries/sign-in-experience/index.js';
import {
findSocialRelatedUser,
getUserInfoByAuthCode,
getUserInfoFromInteractionResult,
} from '#src/lib/social.js';
import { generateUserId, insertUser } from '#src/lib/user.js';
} from '#src/libraries/social.js';
import { generateUserId, insertUser } from '#src/libraries/user.js';
import koaGuard from '#src/middleware/koa-guard.js';
import {
hasUserWithIdentity,
@ -47,6 +50,8 @@ export default function socialRoutes<T extends AnonymousRouter>(router: T, provi
assertThat(state && redirectUri, 'session.insufficient_info');
const connector = await getLogtoConnectorById(connectorId);
assertThat(connector.type === ConnectorType.Social, 'connector.unexpected_type');
// FIXME: @Darcy
// @ts-expect-error pending fix
const redirectTo = await connector.getAuthorizationUri({ state, redirectUri });
ctx.body = { redirectTo };

View file

@ -9,9 +9,12 @@ import type { ZodType } from 'zod';
import { z } from 'zod';
import RequestError from '#src/errors/RequestError/index.js';
import { assignInteractionResults, getApplicationIdFromInteraction } from '#src/lib/session.js';
import { getSignInExperienceForApplication } from '#src/lib/sign-in-experience/index.js';
import { verifyUserPassword } from '#src/lib/user.js';
import {
assignInteractionResults,
getApplicationIdFromInteraction,
} from '#src/libraries/session.js';
import { getSignInExperienceForApplication } from '#src/libraries/sign-in-experience/index.js';
import { verifyUserPassword } from '#src/libraries/user.js';
import type { LogContext } from '#src/middleware/koa-log.js';
import { updateUserById } from '#src/queries/user.js';
import assertThat from '#src/utils/assert-that.js';

View file

@ -7,7 +7,7 @@ import {
validateTermsOfUse,
validateSignUp,
validateSignIn,
} from '#src/lib/sign-in-experience/index.js';
} from '#src/libraries/sign-in-experience/index.js';
import koaGuard from '#src/middleware/koa-guard.js';
import {
findDefaultSignInExperience,

View file

@ -5,8 +5,8 @@ import etag from 'etag';
import type { Provider } from 'oidc-provider';
import { getLogtoConnectors } from '#src/connectors/index.js';
import { getApplicationIdFromInteraction } from '#src/lib/session.js';
import { getSignInExperienceForApplication } from '#src/lib/sign-in-experience/index.js';
import { getApplicationIdFromInteraction } from '#src/libraries/session.js';
import { getSignInExperienceForApplication } from '#src/libraries/sign-in-experience/index.js';
import type { AnonymousRouter } from './types.js';

View file

@ -16,8 +16,8 @@
"stylelint": "stylelint \"src/**/*.scss\""
},
"devDependencies": {
"@logto/core-kit": "1.0.0-beta.28",
"@logto/language-kit": "1.0.0-beta.28",
"@logto/core-kit": "workspace:*",
"@logto/language-kit": "workspace:*",
"@logto/phrases": "workspace:*",
"@logto/react": "1.0.0-beta.14",
"@logto/schemas": "workspace:*",
@ -42,7 +42,7 @@
"react-i18next": "^11.18.3",
"stylelint": "^14.9.1",
"typescript": "^4.9.4",
"zod": "^3.19.1"
"zod": "^3.20.2"
},
"engines": {
"node": "^16.13.0 || ^18.12.0"

View file

@ -33,10 +33,10 @@
"url": "https://github.com/logto-io/logto/issues"
},
"dependencies": {
"@logto/core-kit": "1.0.0-beta.28",
"@logto/language-kit": "1.0.0-beta.28",
"@logto/core-kit": "workspace:*",
"@logto/language-kit": "workspace:*",
"@silverhand/essentials": "^1.3.0",
"zod": "^3.19.1"
"zod": "^3.20.2"
},
"devDependencies": {
"@silverhand/eslint-config": "1.3.0",

View file

@ -33,10 +33,10 @@
"url": "https://github.com/logto-io/logto/issues"
},
"dependencies": {
"@logto/core-kit": "1.0.0-beta.28",
"@logto/language-kit": "1.0.0-beta.28",
"@logto/core-kit": "workspace:*",
"@logto/language-kit": "workspace:*",
"@silverhand/essentials": "^1.3.0",
"zod": "^3.19.1"
"zod": "^3.20.2"
},
"devDependencies": {
"@silverhand/eslint-config": "1.3.0",

View file

@ -68,11 +68,11 @@
},
"prettier": "@silverhand/eslint-config/.prettierrc",
"dependencies": {
"@logto/connector-kit": "1.0.0-beta.28",
"@logto/core-kit": "1.0.0-beta.28",
"@logto/language-kit": "1.0.0-beta.28",
"@logto/connector-kit": "workspace:*",
"@logto/core-kit": "workspace:*",
"@logto/language-kit": "workspace:*",
"@logto/phrases": "workspace:*",
"@logto/phrases-ui": "workspace:*",
"zod": "^3.19.1"
"zod": "^3.20.2"
}
}

View file

@ -0,0 +1,3 @@
# Toolkit packages directory
This directory is NOT a package but a container for all toolkit packages.

View file

@ -0,0 +1,191 @@
# Change Log
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.0.0-beta.29](https://github.com/logto-io/toolkit/compare/v1.0.0-beta.28...v1.0.0-beta.29) (2022-12-07)
### Bug Fixes
* exports info and lock dep version ([#45](https://github.com/logto-io/toolkit/issues/45)) ([2ac83b4](https://github.com/logto-io/toolkit/commit/2ac83b4f0ff17579456569fb67ba018ac493c1af))
## [1.0.0-beta.28](https://github.com/logto-io/toolkit/compare/v1.0.0-beta.27...v1.0.0-beta.28) (2022-12-06)
**Note:** Version bump only for package @logto/connector-kit
## [1.0.0-beta.27](https://github.com/logto-io/toolkit/compare/v1.0.0-beta.26...v1.0.0-beta.27) (2022-11-29)
### Features
* **connector:** add continue message type ([#41](https://github.com/logto-io/toolkit/issues/41)) ([78f1eb0](https://github.com/logto-io/toolkit/commit/78f1eb06f84de2bc7601016a6bcc3c85eb4695f0))
## [1.0.0-beta.26](https://github.com/logto-io/toolkit/compare/v1.0.0-beta.25...v1.0.0-beta.26) (2022-11-23)
**Note:** Version bump only for package @logto/connector-kit
## [1.0.0-beta.25](https://github.com/logto-io/toolkit/compare/v1.0.0-beta.24...v1.0.0-beta.25) (2022-11-22)
**Note:** Version bump only for package @logto/connector-kit
## [1.0.0-beta.24](https://github.com/logto-io/toolkit/compare/v1.0.0-beta.23...v1.0.0-beta.24) (2022-11-22)
**Note:** Version bump only for package @logto/connector-kit
## [1.0.0-beta.23](https://github.com/logto-io/toolkit/compare/v1.0.0-beta.22...v1.0.0-beta.23) (2022-11-18)
### Bug Fixes
* hot fix the check on whether an element exists in array ([#37](https://github.com/logto-io/toolkit/issues/37)) ([889a577](https://github.com/logto-io/toolkit/commit/889a5773b5e95c35c4ef17db24622e7b87b723c5))
## [1.0.0-beta.22](https://github.com/logto-io/toolkit/compare/v1.0.0-beta.21...v1.0.0-beta.22) (2022-11-16)
### Bug Fixes
* make standard connectors display name configurable ([#36](https://github.com/logto-io/toolkit/issues/36)) ([da431bf](https://github.com/logto-io/toolkit/commit/da431bf318a83e0086070d49a16a8cc3d970f388))
## [1.0.0-beta.21](https://github.com/logto-io/toolkit/compare/v1.0.0-beta.20...v1.0.0-beta.21) (2022-11-11)
### Features
* update connector metadata types and add corresponding type guards ([#35](https://github.com/logto-io/toolkit/issues/35)) ([4e7cd12](https://github.com/logto-io/toolkit/commit/4e7cd12b4b4fb32f2ed5a7d66e1616e20fa395f1))
## [1.0.0-beta.20](https://github.com/logto-io/toolkit/compare/v1.0.0-beta.19...v1.0.0-beta.20) (2022-10-21)
**Note:** Version bump only for package @logto/connector-kit
## [1.0.0-beta.19](https://github.com/logto-io/toolkit/compare/v1.0.0-beta.18...v1.0.0-beta.19) (2022-10-19)
**Note:** Version bump only for package @logto/connector-kit
## [1.0.0-beta.18](https://github.com/logto-io/toolkit/compare/v1.0.0-beta.17...v1.0.0-beta.18) (2022-10-12)
**Note:** Version bump only for package @logto/connector-kit
## [1.0.0-beta.17](https://github.com/logto-io/toolkit/compare/v1.0.0-beta.16...v1.0.0-beta.17) (2022-10-12)
**Note:** Version bump only for package @logto/connector-kit
## [1.0.0-beta.16](https://github.com/logto-io/toolkit/compare/v1.0.0-beta.15...v1.0.0-beta.16) (2022-09-28)
**Note:** Version bump only for package @logto/connector-kit
## [1.0.0-beta.15](https://github.com/logto-io/toolkit/compare/v1.0.0-beta.14...v1.0.0-beta.15) (2022-09-27)
### Features
* **language,core,connector:** init language-kit package ([#14](https://github.com/logto-io/toolkit/issues/14)) ([9a74fc4](https://github.com/logto-io/toolkit/commit/9a74fc4d34c9ce277b8734ab78735549dc3a3cda))
## [1.0.0-beta.14](https://github.com/logto-io/toolkit/compare/v1.0.0-beta.13...v1.0.0-beta.14) (2022-09-23)
**Note:** Version bump only for package @logto/connector-kit
## [1.0.0-beta.13](https://github.com/logto-io/toolkit/compare/v1.0.0-beta.12...v1.0.0-beta.13) (2022-09-19)
**Note:** Version bump only for package @logto/connector-kit
## [1.0.0-beta.12](https://github.com/logto-io/toolkit/compare/v1.0.0-beta.11...v1.0.0-beta.12) (2022-09-17)
**Note:** Version bump only for package @logto/connector-kit
## [1.0.0-beta.11](https://github.com/logto-io/toolkit/compare/v1.0.0-beta.10...v1.0.0-beta.11) (2022-09-16)
**Note:** Version bump only for package @logto/connector-kit
## 1.0.0-beta.10 (2022-09-16)
### Features
* initial commit ([56a4968](https://github.com/logto-io/toolkit/commit/56a496848168a4a9ae9ac7af83d51f1b8a6afe2c))
## [1.0.0-beta.9](https://github.com/logto-io/logto/compare/v1.0.0-beta.8...v1.0.0-beta.9) (2022-09-07)
**Note:** Version bump only for package @logto/connector-core
## [1.0.0-beta.8](https://github.com/logto-io/logto/compare/v1.0.0-beta.6...v1.0.0-beta.8) (2022-09-01)
**Note:** Version bump only for package @logto/connector-core
## [1.0.0-beta.6](https://github.com/logto-io/logto/compare/v1.0.0-beta.5...v1.0.0-beta.6) (2022-08-30)
**Note:** Version bump only for package @logto/connector-core

View file

@ -0,0 +1,61 @@
{
"name": "@logto/connector-kit",
"version": "1.0.0-beta.29",
"author": "Silverhand Inc. <contact@silverhand.io>",
"homepage": "https://github.com/logto-io/toolkit#readme",
"repository": {
"type": "git",
"url": "git+https://github.com/logto-io/toolkit.git"
},
"license": "MPL-2.0",
"type": "module",
"main": "./lib/index.cjs",
"exports": {
"types": "./lib/index.d.ts",
"import": "./lib/index.js",
"require": "./lib/index.cjs"
},
"types": "./lib/index.d.ts",
"files": [
"lib"
],
"scripts": {
"precommit": "lint-staged",
"dev": "tsc --watch --preserveWatchOutput --incremental",
"build": "rm -rf lib/ && rollup -c && tsc",
"lint": "eslint --ext .ts src",
"lint:report": "pnpm lint --format json --output-file report.json",
"prepack": "pnpm build"
},
"peerDependencies": {
"zod": "^3.19.1"
},
"dependencies": {
"@logto/core-kit": "workspace:*",
"@logto/language-kit": "workspace:*",
"@silverhand/essentials": "^1.2.1"
},
"devDependencies": {
"@rollup/plugin-typescript": "^10.0.1",
"@silverhand/eslint-config": "1.3.0",
"@silverhand/ts-config": "1.2.1",
"@types/node": "^16.3.1",
"eslint": "^8.21.0",
"lint-staged": "^13.0.0",
"prettier": "^2.7.1",
"rollup": "^3.6.0",
"tslib": "^2.4.1",
"typescript": "^4.7.4",
"zod": "^3.20.2"
},
"engines": {
"node": "^16.13.0 || ^18.12.0"
},
"eslintConfig": {
"extends": "@silverhand"
},
"prettier": "@silverhand/eslint-config/.prettierrc",
"publishConfig": {
"access": "public"
}
}

View file

@ -0,0 +1,21 @@
import typescript from '@rollup/plugin-typescript';
/**
* @type {import('rollup').RollupOptions}
*/
const configs = [
{
input: ['src/index.ts'],
output: [
{
dir: 'lib',
format: 'cjs',
preserveModules: true,
entryFileNames: '[name].cjs',
},
],
plugins: [typescript()],
},
];
export default configs;

View file

@ -0,0 +1,26 @@
import type { ZodType } from 'zod';
import { ConnectorError, ConnectorErrorCodes } from './types.js';
export * from './types.js';
export function validateConfig<T>(config: unknown, guard: ZodType): asserts config is T {
const result = guard.safeParse(config);
if (!result.success) {
throw new ConnectorError(ConnectorErrorCodes.InvalidConfig, result.error);
}
}
export const parseJson = (
jsonString: string,
errorCode: ConnectorErrorCodes = ConnectorErrorCodes.InvalidResponse,
errorPayload?: unknown
) => {
try {
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return JSON.parse(jsonString);
} catch {
throw new ConnectorError(errorCode, errorPayload ?? jsonString);
}
};

View file

@ -0,0 +1,149 @@
import type { LanguageTag } from '@logto/language-kit';
import { isLanguageTag } from '@logto/language-kit';
import type { ZodType } from 'zod';
import { z } from 'zod';
// MARK: Foundation
export enum ConnectorType {
Email = 'Email',
Sms = 'Sms',
Social = 'Social',
}
export enum ConnectorPlatform {
Native = 'Native',
Universal = 'Universal',
Web = 'Web',
}
export const i18nPhrasesGuard: ZodType<I18nPhrases> = z
.object({ en: z.string() })
.and(z.record(z.string()))
.refine((i18nObject) => {
const keys = Object.keys(i18nObject);
if (!keys.includes('en')) {
return false;
}
for (const value of keys) {
if (!isLanguageTag(value)) {
return false;
}
}
return true;
});
type I18nPhrases = { en: string } & {
[K in Exclude<LanguageTag, 'en'>]?: string;
};
export enum ConnectorErrorCodes {
General = 'general',
InvalidMetadata = 'invalid_metadata',
UnexpectedType = 'unexpected_type',
InvalidConfigGuard = 'invalid_config_guard',
InvalidRequestParameters = 'invalid_request_parameters',
InsufficientRequestParameters = 'insufficient_request_parameters',
InvalidConfig = 'invalid_config',
InvalidResponse = 'invalid_response',
TemplateNotFound = 'template_not_found',
NotImplemented = 'not_implemented',
SocialAuthCodeInvalid = 'social_auth_code_invalid',
SocialAccessTokenInvalid = 'social_invalid_access_token',
SocialIdTokenInvalid = 'social_invalid_id_token',
AuthorizationFailed = 'authorization_failed',
}
export class ConnectorError extends Error {
public code: ConnectorErrorCodes;
public data: unknown;
constructor(code: ConnectorErrorCodes, data?: unknown) {
const message = typeof data === 'string' ? data : 'Connector error occurred.';
super(message);
this.code = code;
this.data = typeof data === 'string' ? { message: data } : data;
}
}
export enum MessageTypes {
SignIn = 'SignIn',
Register = 'Register',
ForgotPassword = 'ForgotPassword',
Continue = 'Continue',
Test = 'Test',
}
export const messageTypesGuard = z.nativeEnum(MessageTypes);
const connectorMetadataGuard = z.object({
id: z.string(),
target: z.string(),
platform: z.nativeEnum(ConnectorPlatform).nullable(),
name: i18nPhrasesGuard,
logo: z.string(),
logoDark: z.string().nullable(),
description: i18nPhrasesGuard,
isStandard: z.boolean().optional(),
readme: z.string(),
configTemplate: z.string(),
});
export const configurableConnectorMetadataGuard = connectorMetadataGuard
.pick({
target: true,
name: true,
logo: true,
logoDark: true,
})
.partial();
export type ConnectorMetadata = z.infer<typeof connectorMetadataGuard>;
export type ConfigurableConnectorMetadata = z.infer<typeof configurableConnectorMetadataGuard>;
export type BaseConnector<Type extends ConnectorType> = {
type: Type;
metadata: ConnectorMetadata;
configGuard: ZodType;
};
export type CreateConnector<T extends BaseConnector<ConnectorType>> = (options: {
getConfig: GetConnectorConfig;
}) => Promise<T>;
export type GetConnectorConfig = (id: string) => Promise<unknown>;
export type AllConnector = SmsConnector | EmailConnector | SocialConnector;
// MARK: SMS + Email connector
export type SmsConnector = BaseConnector<ConnectorType.Sms> & {
sendMessage: SendMessageFunction;
};
export type EmailConnector = BaseConnector<ConnectorType.Email> & {
sendMessage: SendMessageFunction;
};
export type SendMessageFunction = (
data: { to: string; type: MessageTypes; payload: { code: string } },
config?: unknown
) => Promise<unknown>;
// MARK: Social connector
export type SocialConnector = BaseConnector<ConnectorType.Social> & {
getAuthorizationUri: GetAuthorizationUri;
getUserInfo: GetUserInfo;
};
export type GetAuthorizationUri = (payload: {
state: string;
redirectUri: string;
nonce: string;
}) => Promise<string>;
export type GetUserInfo = (
data: Record<string, unknown> & { nonce: string; redirectUri: string }
) => Promise<{ id: string } & Record<string, string | boolean | number | undefined>>;

View file

@ -0,0 +1,12 @@
{
"extends": "@silverhand/ts-config/tsconfig.base",
"compilerOptions": {
"outDir": "lib",
"declaration": true,
"moduleResolution": "nodenext",
"module": "esnext"
},
"include": [
"src"
]
}

View file

@ -0,0 +1,265 @@
# Change Log
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.0.0-beta.29](https://github.com/logto-io/toolkit/compare/v1.0.0-beta.28...v1.0.0-beta.29) (2022-12-07)
### Bug Fixes
* exports info and lock dep version ([#45](https://github.com/logto-io/toolkit/issues/45)) ([2ac83b4](https://github.com/logto-io/toolkit/commit/2ac83b4f0ff17579456569fb67ba018ac493c1af))
## [1.0.0-beta.28](https://github.com/logto-io/toolkit/compare/v1.0.0-beta.27...v1.0.0-beta.28) (2022-12-06)
**Note:** Version bump only for package @logto/core-kit
## [1.0.0-beta.26](https://github.com/logto-io/toolkit/compare/v1.0.0-beta.25...v1.0.0-beta.26) (2022-11-23)
**Note:** Version bump only for package @logto/core-kit
## [1.0.0-beta.25](https://github.com/logto-io/toolkit/compare/v1.0.0-beta.24...v1.0.0-beta.25) (2022-11-22)
### Bug Fixes
* **core:** declarations ([#39](https://github.com/logto-io/toolkit/issues/39)) ([3140fdb](https://github.com/logto-io/toolkit/commit/3140fdbcc2db76bb76b4a5f5a5070de0dc12ff40))
## [1.0.0-beta.24](https://github.com/logto-io/toolkit/compare/v1.0.0-beta.23...v1.0.0-beta.24) (2022-11-22)
**Note:** Version bump only for package @logto/core-kit
## [1.0.0-beta.21](https://github.com/logto-io/toolkit/compare/v1.0.0-beta.20...v1.0.0-beta.21) (2022-11-11)
**Note:** Version bump only for package @logto/core-kit
## [1.0.0-beta.20](https://github.com/logto-io/toolkit/compare/v1.0.0-beta.19...v1.0.0-beta.20) (2022-10-21)
**Note:** Version bump only for package @logto/core-kit
## [1.0.0-beta.19](https://github.com/logto-io/toolkit/compare/v1.0.0-beta.18...v1.0.0-beta.19) (2022-10-19)
**Note:** Version bump only for package @logto/core-kit
## [1.0.0-beta.18](https://github.com/logto-io/toolkit/compare/v1.0.0-beta.17...v1.0.0-beta.18) (2022-10-12)
### Bug Fixes
* export url utilities in index file ([#23](https://github.com/logto-io/toolkit/issues/23)) ([0a7b4c8](https://github.com/logto-io/toolkit/commit/0a7b4c836fe6d566fc051fcf185df4dac352e308))
## [1.0.0-beta.17](https://github.com/logto-io/toolkit/compare/v1.0.0-beta.16...v1.0.0-beta.17) (2022-10-12)
**Note:** Version bump only for package @logto/core-kit
## [1.0.0-beta.16](https://github.com/logto-io/toolkit/compare/v1.0.0-beta.15...v1.0.0-beta.16) (2022-09-28)
### Bug Fixes
* **core,language:** avoid using path aliases ([#15](https://github.com/logto-io/toolkit/issues/15)) ([22db3ed](https://github.com/logto-io/toolkit/commit/22db3ed2daf3ee5906ffc864bb9bed1a826df842))
## [1.0.0-beta.15](https://github.com/logto-io/toolkit/compare/v1.0.0-beta.14...v1.0.0-beta.15) (2022-09-27)
### Features
* **language,core,connector:** init language-kit package ([#14](https://github.com/logto-io/toolkit/issues/14)) ([9a74fc4](https://github.com/logto-io/toolkit/commit/9a74fc4d34c9ce277b8734ab78735549dc3a3cda))
## [1.0.0-beta.14](https://github.com/logto-io/toolkit/compare/v1.0.0-beta.13...v1.0.0-beta.14) (2022-09-23)
### Bug Fixes
* remove unused file utils to avoid importing fs in browser ([#11](https://github.com/logto-io/toolkit/issues/11)) ([e1bda93](https://github.com/logto-io/toolkit/commit/e1bda93d1e95974f5e7128c48bb3a342cc9de358))
## [1.0.0-beta.13](https://github.com/logto-io/toolkit/compare/v1.0.0-beta.12...v1.0.0-beta.13) (2022-09-19)
**Note:** Version bump only for package @logto/core-kit
## [1.0.0-beta.12](https://github.com/logto-io/toolkit/compare/v1.0.0-beta.11...v1.0.0-beta.12) (2022-09-17)
**Note:** Version bump only for package @logto/core-kit
## [1.0.0-beta.11](https://github.com/logto-io/toolkit/compare/v1.0.0-beta.10...v1.0.0-beta.11) (2022-09-16)
**Note:** Version bump only for package @logto/core-kit
## 1.0.0-beta.10 (2022-09-16)
### Features
* initial commit ([56a4968](https://github.com/logto-io/toolkit/commit/56a496848168a4a9ae9ac7af83d51f1b8a6afe2c))
## [1.0.0-beta.9](https://github.com/logto-io/logto/compare/v1.0.0-beta.8...v1.0.0-beta.9) (2022-09-07)
### Features
* add Portuguese translation ([f268ecb](https://github.com/logto-io/logto/commit/f268ecb1a8d57d1e33225bec8852f3bc377dd478))
### Bug Fixes
* **console,ui:** fix locale guard issue in settings page ([e200578](https://github.com/logto-io/logto/commit/e2005780a39fa7b5f5c5e406f37805913b684c18))
## [1.0.0-beta.8](https://github.com/logto-io/logto/compare/v1.0.0-beta.6...v1.0.0-beta.8) (2022-09-01)
**Note:** Version bump only for package @logto/shared
## [1.0.0-beta.6](https://github.com/logto-io/logto/compare/v1.0.0-beta.5...v1.0.0-beta.6) (2022-08-30)
**Note:** Version bump only for package @logto/shared
## [1.0.0-beta.5](https://github.com/logto-io/logto/compare/v1.0.0-beta.4...v1.0.0-beta.5) (2022-08-19)
**Note:** Version bump only for package @logto/shared
## [1.0.0-beta.4](https://github.com/logto-io/logto/compare/v1.0.0-beta.3...v1.0.0-beta.4) (2022-08-11)
### Bug Fixes
* **shared:** fix dark color generator ([#1719](https://github.com/logto-io/logto/issues/1719)) ([3deb98c](https://github.com/logto-io/logto/commit/3deb98c18dfe54abda53e6de7592f40924e1f2f3))
## [1.0.0-beta.3](https://github.com/logto-io/logto/compare/v1.0.0-beta.2...v1.0.0-beta.3) (2022-08-01)
### Features
* **console:** add Next.js integration guide in admin console ([7d3f947](https://github.com/logto-io/logto/commit/7d3f94738f495de98464d23b6fdf18214d59005e))
## [1.0.0-beta.1](https://github.com/logto-io/logto/compare/v1.0.0-beta.0...v1.0.0-beta.1) (2022-07-19)
**Note:** Version bump only for package @logto/shared
## [1.0.0-beta.0](https://github.com/logto-io/logto/compare/v1.0.0-alpha.4...v1.0.0-beta.0) (2022-07-14)
**Note:** Version bump only for package @logto/shared
## [1.0.0-alpha.3](https://github.com/logto-io/logto/compare/v1.0.0-alpha.2...v1.0.0-alpha.3) (2022-07-07)
**Note:** Version bump only for package @logto/shared
### [0.1.2-alpha.5](https://github.com/logto-io/logto/compare/v0.1.2-alpha.4...v0.1.2-alpha.5) (2022-07-03)
**Note:** Version bump only for package @logto/shared
### [0.1.2-alpha.1](https://github.com/logto-io/logto/compare/v0.1.2-alpha.0...v0.1.2-alpha.1) (2022-07-02)
**Note:** Version bump only for package @logto/shared
### [0.1.1-alpha.0](https://github.com/logto-io/logto/compare/v0.1.0-internal...v0.1.1-alpha.0) (2022-07-01)
### Features
* **console,ui:** generate dark mode color in console ([#1231](https://github.com/logto-io/logto/issues/1231)) ([f72b21d](https://github.com/logto-io/logto/commit/f72b21d1602ab0fb35ef3e7d84f6c8ebd7e18b08))
* **console:** add details summary component in guides ([693c4f0](https://github.com/logto-io/logto/commit/693c4f0422eb312190f2c7b0673e3ceaa8c41213))
* **core,shared:** get /dashboard/users/active ([#953](https://github.com/logto-io/logto/issues/953)) ([1420bb2](https://github.com/logto-io/logto/commit/1420bb28cec9c0e20b4d0645a58e436135f87c83))
* **demo-app:** implement (part 2) ([85a055e](https://github.com/logto-io/logto/commit/85a055efa4358cfb69c0d74f7aeaeb0bade024af))
* **demo-app:** implementation ([#982](https://github.com/logto-io/logto/issues/982)) ([7f4f4f8](https://github.com/logto-io/logto/commit/7f4f4f84addf8a25c3d30f1ac3ceeef460afcf17))
* **demo-app:** init ([#979](https://github.com/logto-io/logto/issues/979)) ([ad0aa8e](https://github.com/logto-io/logto/commit/ad0aa8e0c20a8d60f095b477e942b724fb53ca7d))
* **shared,phrases-ui:** not allow hyphens in username ([#1319](https://github.com/logto-io/logto/issues/1319)) ([5e81966](https://github.com/logto-io/logto/commit/5e819665c7c1d584ff5cff25e4e0723122be78b2))
* update field check rules ([#854](https://github.com/logto-io/logto/issues/854)) ([85a407c](https://github.com/logto-io/logto/commit/85a407c5f6f76fed0513acd6fb41943413935b5a))
### Bug Fixes
* `lint:report` script ([#730](https://github.com/logto-io/logto/issues/730)) ([3b17324](https://github.com/logto-io/logto/commit/3b17324d189b2fe47985d0bee8b37b4ef1dbdd2b))
* **console:** dashboard chart style ([#1177](https://github.com/logto-io/logto/issues/1177)) ([cf47044](https://github.com/logto-io/logto/commit/cf470446e4458e748bbf6384adb96d69805a1991)), closes [#1178](https://github.com/logto-io/logto/issues/1178)
* **console:** new platform tab colors ([#1158](https://github.com/logto-io/logto/issues/1158)) ([1bb770f](https://github.com/logto-io/logto/commit/1bb770fd1fa364f12c1c56a8542d36a3cf9647fe))
* **core:** remove name regex ([#1109](https://github.com/logto-io/logto/issues/1109)) ([a790248](https://github.com/logto-io/logto/commit/a790248c091e444614652b08b05686e9934cb639))

View file

@ -0,0 +1,77 @@
// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#input_types
type InputType =
| 'button'
| 'checkbox'
| 'color'
| 'date'
| 'datetime-local'
| 'email'
| 'file'
| 'hidden'
| 'image'
| 'month'
| 'number'
| 'password'
| 'radio'
| 'range'
| 'reset'
| 'search'
| 'submit'
| 'tel'
| 'text'
| 'time'
| 'url'
| 'week';
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#autofilling-form-controls:-the-autocomplete-attribute
type AutoCompleteType =
| 'name'
| 'honorific-prefix'
| 'given-name'
| 'additional-name'
| 'family-name'
| 'honorific-suffix'
| 'nickname'
| 'username'
| 'new-password'
| 'current-password'
| 'one-time-code'
| 'organization-title'
| 'organization'
| 'street-address'
| 'address-line1'
| 'address-line2'
| 'address-line3'
| 'address-level4'
| 'address-level3'
| 'address-level2'
| 'address-level1'
| 'country'
| 'country-name'
| 'postal-code'
| 'cc-name'
| 'cc-given-name'
| 'cc-additional-name'
| 'cc-family-name'
| 'cc-number'
| 'cc-exp'
| 'cc-exp-month'
| 'cc-exp-year'
| 'cc-csc'
| 'cc-type'
| 'transaction-currency'
| 'transaction-amount'
| 'language'
| 'bday'
| 'bday-day'
| 'bday-month'
| 'bday-year'
| 'sex'
| 'url'
| 'photo'
| 'mobile';
// TO-DO: remove me
interface Body {
json<T>(): Promise<T>;
}

View file

@ -0,0 +1,4 @@
/* eslint-disable import/no-unassigned-import */
import './react-app.d';
import './dom.d';
/* eslint-enable import/no-unassigned-import */

View file

@ -0,0 +1,65 @@
// Copied from react-scripts/lib/react-app.d.ts
declare module '*.avif' {
const source: string;
export default source;
}
declare module '*.bmp' {
const source: string;
export default source;
}
declare module '*.gif' {
const source: string;
export default source;
}
declare module '*.jpg' {
const source: string;
export default source;
}
declare module '*.jpeg' {
const source: string;
export default source;
}
declare module '*.png' {
const source: string;
export default source;
}
declare module '*.webp' {
const source: string;
export default source;
}
declare module '*.svg' {
import * as React from 'react';
export const ReactComponent: React.FunctionComponent<
React.SVGProps<SVGSVGElement> & { title?: string }
>;
const source: string;
export default source;
}
declare module '*.module.css' {
const classes: Readonly<Record<string, string>>;
export default classes;
export = classes;
}
declare module '*.module.scss' {
const classes: Readonly<Record<string, string>>;
export default classes;
export = classes;
}
declare module '*.module.sass' {
const classes: Readonly<Record<string, string>>;
export default classes;
export = classes;
}

View file

@ -0,0 +1,13 @@
import type { Config } from '@jest/types';
const config: Config.InitialOptions = {
preset: 'ts-jest',
moduleNameMapper: {
'^(\\.{1,2}/.*)\\.js$': '$1',
},
collectCoverageFrom: ['src/**/*.ts'],
coverageReporters: ['lcov', 'text-summary'],
setupFilesAfterEnv: ['jest-matcher-specific-error'],
};
export default config;

View file

@ -0,0 +1,80 @@
{
"name": "@logto/core-kit",
"version": "1.0.0-beta.29",
"author": "Silverhand Inc. <contact@silverhand.io>",
"homepage": "https://github.com/logto-io/toolkit#readme",
"repository": {
"type": "git",
"url": "git+https://github.com/logto-io/toolkit.git"
},
"license": "MPL-2.0",
"type": "module",
"main": "./lib/index.cjs",
"exports": {
"types": "./lib/index.d.ts",
"import": "./lib/index.js",
"require": "./lib/index.cjs"
},
"types": "./lib/index.d.ts",
"files": [
"declaration",
"lib",
"scss"
],
"scripts": {
"precommit": "lint-staged",
"dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental",
"build": "rm -rf lib/ && rollup -c && tsc -p tsconfig.build.json",
"lint": "eslint --ext .ts src",
"lint:report": "pnpm lint --format json --output-file report.json",
"prepack": "pnpm build",
"stylelint": "stylelint \"scss/**/*.scss\"",
"test": "jest",
"test:coverage": "jest --silent --coverage"
},
"engines": {
"node": "^16.13.0 || ^18.12.0"
},
"peerDependencies": {
"zod": "^3.19.1"
},
"dependencies": {
"@logto/language-kit": "^1.0.0-beta.29",
"color": "^4.2.3",
"nanoid": "^3.1.23"
},
"devDependencies": {
"@jest/types": "^29.0.3",
"@rollup/plugin-typescript": "^10.0.1",
"@silverhand/eslint-config": "1.3.0",
"@silverhand/eslint-config-react": "1.3.0",
"@silverhand/essentials": "^1.2.1",
"@silverhand/ts-config": "1.2.1",
"@types/color": "^3.0.3",
"@types/jest": "^29.0.3",
"@types/node": "^16.3.1",
"@types/react": "^18.0.20",
"eslint": "^8.21.0",
"jest": "^29.0.3",
"jest-matcher-specific-error": "^1.0.0",
"lint-staged": "^13.0.0",
"postcss": "^8.4.6",
"prettier": "^2.7.1",
"rollup": "^3.6.0",
"stylelint": "^14.9.1",
"ts-jest": "^29.0.1",
"tslib": "^2.4.1",
"typescript": "^4.7.4",
"zod": "^3.20.2"
},
"eslintConfig": {
"extends": "@silverhand"
},
"stylelint": {
"extends": "@silverhand/eslint-config-react/.stylelintrc"
},
"prettier": "@silverhand/eslint-config/.prettierrc",
"publishConfig": {
"access": "public"
}
}

View file

@ -0,0 +1,21 @@
import typescript from '@rollup/plugin-typescript';
/**
* @type {import('rollup').RollupOptions}
*/
const configs = [
{
input: ['src/index.ts'],
output: [
{
dir: 'lib',
format: 'cjs',
preserveModules: true,
entryFileNames: '[name].cjs',
},
],
plugins: [typescript({ tsconfig: 'tsconfig.build.json' })],
},
];
export default configs;

View file

@ -0,0 +1,339 @@
@mixin light {
// tonal palettes
--color-all-0: #000;
--color-all-100: #fff;
--color-primary-10: #190064;
--color-primary-20: #2d009d;
--color-primary-30: #4300da;
--color-primary-40: #5d34f2;
--color-primary-50: #7958ff;
--color-primary-60: #947dff;
--color-primary-70: #af9eff;
--color-primary-80: #cabeff;
--color-primary-90: #e6deff;
--color-primary-95: #f5eeff;
--color-primary-99: #fffbff;
--color-secondary-10: #201c00;
--color-secondary-20: #373100;
--color-secondary-30: #4f4700;
--color-secondary-40: #695f00;
--color-secondary-50: #847900;
--color-secondary-60: #9f930d;
--color-secondary-70: #baad2d;
--color-secondary-80: #d7c947;
--color-secondary-90: #f4e560;
--color-secondary-95: #fff480;
--color-secondary-99: #fffbf7;
--color-tertiary-10: #350041;
--color-tertiary-20: #560068;
--color-tertiary-30: #7b0093;
--color-tertiary-40: #9927af;
--color-tertiary-50: #b545ca;
--color-tertiary-60: #d361e7;
--color-tertiary-70: #f07eff;
--color-tertiary-80: #faabff;
--color-tertiary-90: #ffd5ff;
--color-tertiary-95: #ffeafe;
--color-tertiary-99: #fcfcfc;
--color-error-10: #410001;
--color-error-20: #680003;
--color-error-30: #930006;
--color-error-40: #ba1b1b;
--color-error-50: #dd3730;
--color-error-60: #ff5449;
--color-error-70: #ff897a;
--color-error-80: #ffb4a9;
--color-error-90: #ffdad4;
--color-error-95: #ffede9;
--color-error-99: #fcfcfc;
--color-neutral-10: #191c1d;
--color-neutral-20: #2d3132;
--color-neutral-30: #444748;
--color-neutral-40: #5c5f60;
--color-neutral-50: #747778;
--color-neutral-60: #8e9192;
--color-neutral-70: #a9acac;
--color-neutral-80: #c4c7c7;
--color-neutral-90: #e0e3e3;
--color-neutral-95: #eff1f1;
--color-neutral-99: #f7f8f8;
--color-neutral-variant-10: #1b1b22;
--color-neutral-variant-20: #302f38;
--color-neutral-variant-30: #47464e;
--color-neutral-variant-40: #5f5d67;
--color-neutral-variant-50: #78767f;
--color-neutral-variant-60: #928f9a;
--color-neutral-variant-70: #adaab4;
--color-neutral-variant-80: #c9c5d0;
--color-neutral-variant-90: #e5e1ec;
--color-neutral-variant-95: #f3effa;
--color-neutral-variant-99: #fffbff;
--color-success-10: #002106;
--color-success-20: #00390b;
--color-success-30: #005314;
--color-success-40: #116d26;
--color-success-50: #32873d;
--color-success-60: #4ea254;
--color-success-70: #68be6c;
--color-success-80: #83da85;
--color-success-90: #9fe79f;
--color-success-95: #bdefbd;
--color-success-99: #ebf9eb;
--color-alert-10: #2b1700;
--color-alert-20: #472a00;
--color-alert-30: #663e00;
--color-alert-40: #865300;
--color-alert-50: #a76900;
--color-alert-60: #ca8000;
--color-alert-70: #eb9918;
--color-alert-80: #ffb95a;
--color-alert-90: #ffddb5;
--color-alert-95: #ffeedc;
--color-alert-99: #fff5eb;
// color aliases
--color-primary: var(--color-primary-40);
--color-on-primary: var(--color-all-100);
--color-primary-container: var(--color-primary-90);
--color-on-primary-container: var(--color-primary-10);
--color-secondary: var(--color-secondary-40);
--color-on-secondary: var(--color-all-100);
--color-secondary-container: var(--color-secondary-30);
--color-on-secondary-container: var(--color-secondary-10);
--color-tertiary: var(--color-tertiary-40);
--color-on-tertiary: var(--color-all-100);
--color-tertiary-container: var(--color-tertiary-90);
--color-on-tertiary-container: var(--color-tertiary-10);
--color-error: var(--color-error-40);
--color-on-error: var(--color-all-100);
--color-error-container: var(--color-error-90);
--color-on-error-container: var(--color-error-10);
--color-background: var(--color-neutral-99);
--color-on-background: var(--color-neutral-10);
--color-surface: var(--color-neutral-99);
--color-surface-1: #ecebf6; // Neutral-99 + 2% Neutral-Variant-50 + 5% Primary-40
--color-surface-2: #e8e6f6; // Neutral-99 + 2% Neutral-Variant-50 + 8% Primary-40
--color-surface-3: #e3e0f6; // Neutral-99 + 2% Neutral-Variant-50 + 11% Primary-40
--color-surface-4: #dfdaf5; // Neutral-99 + 2% Neutral-Variant-50 + 14% Primary-40
--color-surface-5: #dcd6f5; // Neutral-99 + 2% Neutral-Variant-50 + 16% Primary-40
--color-on-surface: var(--color-neutral-10);
--color-surface-variant: var(--color-neutral-variant-90);
--color-on-surface-variant: var(--color-neutral-variant-30);
--color-outline: var(--color-neutral-variant-50);
--color-primary-hover: var(--color-primary-50);
--color-primary-pressed: var(--color-primary-30);
--color-danger-hover: var(--color-error-50);
--color-danger-pressed: var(--color-error-30);
--color-text: var(--color-neutral-10);
--color-text-link: var(--color-primary-40);
--color-text-secondary: var(--color-neutral-50);
--color-placeholder: var(--color-neutral-70);
--color-border: var(--color-neutral-80);
--color-divider: var(--color-neutral-90);
--color-disabled: var(--color-neutral-80);
--color-base: var(--color-surface-1);
--color-layer-1: var(--color-all-100);
--color-layer-2: var(--color-neutral-95);
--color-float: var(--color-all-100);
--color-layer-light: var(--color-neutral-99);
--color-inverse-surface: var(--color-neutral-20);
--color-inverse-on-surface: var(--color-neutral-95);
--color-inverse-primary: var(--color-primary-50);
--color-shadow: var(--color-all-100);
--color-white: #fff;
--color-button-icon: var(--color-primary-80);
--color-icon-background: var(--color-neutral-variant-95);
--color-hover: rgba(25, 28, 29, 8%); // 8% Neutral-10
--color-pressed: rgba(25, 28, 29, 12%); // 12% Neutral-10
--color-focused: rgba(25, 28, 29, 16%); // 16% Neutral-10
--color-hover-variant: rgba(93, 52, 242, 8%); // 8% Primary-40
--color-pressed-variant: rgba(93, 52, 242, 12%); // 12% Primary-40
--color-focused-variant: rgba(93, 52, 242, 16%); // 16% Primary-40
// Shadows
--shadow-1: 0 4px 8px rgba(0, 0, 0, 8%);
--shadow-2: 0 4px 12px rgba(0, 0, 0, 12%);
--shadow-2-reversed: 0 -4px 12px rgba(0, 0, 0, 12%);
--shadow-3: 0 4px 16px rgba(0, 0, 0, 20%);
// Client specific variables (not available in design system)
--color-danger-toast-background: var(--color-error-95);
--color-danger-focused: rgba(186, 27, 27, 16%); // 16% Error-40
--color-tooltip-background: #34353f; // dark theme Surface-4
--color-tooltip-text: var(--color-neutral-99);
--color-overlay: rgba(0, 0, 0, 30%);
--color-drawer-overlay: rgba(0, 0, 0, 40%);
--color-guide-dropdown-background: var(--color-white);
--color-guide-dropdown-border: var(--color-border);
--color-skeleton-shimmer-rgb: 255, 255, 255; // rgb of Layer-1
}
@mixin dark {
// tonal palettes
--color-all-0: #fff;
--color-all-100: #000;
--color-primary-10: #fffbff;
--color-primary-20: #f5eeff;
--color-primary-30: #e6deff;
--color-primary-40: #cabeff;
--color-primary-50: #af9eff;
--color-primary-60: #947dff;
--color-primary-70: #7958ff;
--color-primary-80: #5d34f2;
--color-primary-90: #4300da;
--color-primary-95: #2d009d;
--color-primary-99: #190064;
--color-secondary-10: #fffbf7;
--color-secondary-20: #fff480;
--color-secondary-30: #f4e560;
--color-secondary-40: #d7c947;
--color-secondary-50: #baad2d;
--color-secondary-60: #9f930d;
--color-secondary-70: #847900;
--color-secondary-80: #695f00;
--color-secondary-90: #4f4700;
--color-secondary-95: #373100;
--color-secondary-99: #201c00;
--color-tertiary-10: #fcfcfc;
--color-tertiary-20: #ffeafe;
--color-tertiary-30: #ffd5ff;
--color-tertiary-40: #faabff;
--color-tertiary-50: #f07eff;
--color-tertiary-60: #d361e7;
--color-tertiary-70: #b545ca;
--color-tertiary-80: #9927af;
--color-tertiary-90: #7b0093;
--color-tertiary-95: #560068;
--color-tertiary-99: #350041;
--color-error-10: #fcfcfc;
--color-error-20: #ffede9;
--color-error-30: #ffdad4;
--color-error-40: #ffb4a9;
--color-error-50: #ff897a;
--color-error-60: #ff5449;
--color-error-70: #dd3730;
--color-error-80: #ba1b1b;
--color-error-90: #930006;
--color-error-95: #680003;
--color-error-99: #410001;
--color-neutral-10: #f7f8f8;
--color-neutral-20: #eff1f1;
--color-neutral-30: #e0e3e3;
--color-neutral-40: #c4c7c7;
--color-neutral-50: #a9acac;
--color-neutral-60: #8e9192;
--color-neutral-70: #747778;
--color-neutral-80: #5c5f60;
--color-neutral-90: #444748;
--color-neutral-95: #2d3132;
--color-neutral-99: #191c1d;
--color-neutral-variant-10: #fffbff;
--color-neutral-variant-20: #f3effa;
--color-neutral-variant-30: #e5e1ec;
--color-neutral-variant-40: #c9c5d0;
--color-neutral-variant-50: #adaab4;
--color-neutral-variant-60: #928f9a;
--color-neutral-variant-70: #78767f;
--color-neutral-variant-80: #5f5d67;
--color-neutral-variant-90: #47464e;
--color-neutral-variant-95: #302f38;
--color-neutral-variant-99: #1b1b22;
--color-success-10: #ebf9eb;
--color-success-20: #bdefbd;
--color-success-30: #9fe79f;
--color-success-40: #83da85;
--color-success-50: #68be6c;
--color-success-60: #4ea254;
--color-success-70: #32873d;
--color-success-80: #116d26;
--color-success-90: #005314;
--color-success-95: #00390b;
--color-success-99: #002106;
--color-alert-10: #fff5eb;
--color-alert-20: #ffeedc;
--color-alert-30: #ffddb5;
--color-alert-40: #ffb95a;
--color-alert-50: #eb9918;
--color-alert-60: #ca8000;
--color-alert-70: #a76900;
--color-alert-80: #865300;
--color-alert-90: #663e00;
--color-alert-95: #472a00;
--color-alert-99: #2b1700;
// color aliases
--color-primary: var(--color-primary-70);
--color-on-primary: var(--color-all-0);
--color-primary-container: var(--color-primary-30);
--color-on-primary-container: var(--color-primary-30);
--color-secondary: var(--color-secondary-70);
--color-on-secondary: var(--color-all-0);
--color-secondary-container: var(--color-secondary-90);
--color-on-secondary-container: var(--color-secondary-30);
--color-tertiary: var(--color-tertiary-70);
--color-on-tertiary: var(--color-all-0);
--color-tertiary-container: var(--color-tertiary-90);
--color-on-tertiary-container: var(--color-tertiary-30);
--color-error: var(--color-error-70);
--color-on-error: var(--color-all-0);
--color-error-container: var(--color-error-90);
--color-on-error-container: var(--color-error-30);
--color-background: var(--color-neutral-99);
--color-on-background: var(--color-neutral-10);
--color-surface: var(--color-neutral-99);
--color-surface-1: #25272b; // Neutral-99 + 2% Neutral-40 + 5% Primary-80
--color-surface-2: #2a2c32; // Neutral-99 + 2% Neutral-40 + 8% Primary-80
--color-surface-3: #2f3039; // Neutral-99 + 2% Neutral-40 + 11% Primary-80
--color-surface-4: #34353f; // Neutral-99 + 2% Neutral-40 + 14% Primary-80
--color-surface-5: #383844; // Neutral-99 + 2% Neutral-40 + 16% Primary-80
--color-on-surface: var(--color-neutral-10);
--color-surface-variant: var(--color-neutral-variant-90);
--color-on-surface-variant: var(--color-neutral-variant-40);
--color-outline: var(--color-neutral-variant-60);
--color-primary-hover: var(--color-primary-60);
--color-primary-pressed: var(--color-primary-80);
--color-danger-hover: var(--color-error-60);
--color-danger-pressed: var(--color-error-80);
--color-text: var(--color-neutral-10);
--color-text-link: var(--color-primary-40);
--color-text-secondary: var(--color-neutral-50);
--color-placeholder: var(--color-neutral-70);
--color-border: var(--color-neutral-80);
--color-divider: var(--color-neutral-90);
--color-disabled: var(--color-neutral-80);
--color-base: var(--color-surface);
--color-layer-1: var(--color-surface-2);
--color-layer-2: var(--color-surface-4);
--color-float: var(--color-surface-4);
--color-layer-light: var(--color-surface-4);
--color-inverse-surface: var(--color-neutral-20);
--color-inverse-on-surface: var(--color-neutral-95);
--color-inverse-primary: var(--color-primary-80);
--color-shadow: var(--color-all-100);
--color-white: #fff;
--color-button-icon: var(--color-primary-20);
--color-icon-background: #3a3b59;
--color-hover: rgba(247, 248, 248, 8%); // 8% Neutral-10
--color-pressed: rgba(247, 248, 248, 12%); // 12% Neutral-10
--color-focused: rgba(247, 248, 248, 16%); // 16% Neutral-10
--color-hover-variant: rgba(202, 190, 255, 8%); // 8% Primary-40
--color-pressed-variant: rgba(202, 190, 255, 12%); // 12% Primary-40
--color-focused-variant: rgba(202, 190, 255, 16%); // 16% Primary-40
// Shadows
--shadow-1: 0 4px 8px rgba(0, 0, 0, 8%);
--shadow-2: 0 4px 12px rgba(0, 0, 0, 12%);
--shadow-2-reversed: 0 -4px 12px rgba(0, 0, 0, 12%);
--shadow-3: 0 4px 16px rgba(0, 0, 0, 20%);
// Client specific variables (not available in design system)
--color-danger-toast-background: var(--color-error-99);
--color-danger-focused: rgba(255, 180, 169, 16%); // 16% Error-40
--color-tooltip-background: var(--color-surface-4);
--color-tooltip-text: var(--color-neutral-10);
--color-overlay: rgba(0, 0, 0, 30%);
--color-drawer-overlay: rgba(0, 0, 0, 60%);
--color-guide-dropdown-background: var(--color-neutral-variant-80);
--color-guide-dropdown-border: var(--color-neutral-variant-70);
--color-skeleton-shimmer-rgb: 42, 44, 50; // rgb of Layer-1
}

View file

@ -0,0 +1,32 @@
$font-family:
-apple-system,
system-ui,
'BlinkMacSystemFont',
'Segoe UI',
'Roboto',
'Helvetica Neue',
'Helvetica',
'Arial',
sans-serif,
'Apple Color Emoji';
:root {
--font-family: #{$font-family};
--font-headline-large: 600 32px/40px #{$font-family};
--font-headline-medium: 600 28px/36px #{$font-family};
--font-headline-small: 600 24px/32px #{$font-family};
--font-title-large: 600 20px/28px #{$font-family};
--font-title-medium: 600 16px/24px #{$font-family};
--font-title-small: 600 14px/20px #{$font-family};
--font-label-large: 500 14px/20px #{$font-family};
--font-label-medium: 500 12px/16px #{$font-family};
--font-label-small: 500 11px/16px #{$font-family};
--font-body-large: 400 16px/24px #{$font-family};
--font-body-medium: 400 14px/20px #{$font-family};
--font-body-small: 400 12px/16px #{$font-family};
--font-subhead-1: 500 16px/24px #{$font-family};
--font-subhead-2: 500 14px/20px #{$font-family};
--font-subhead-cap: 700 12px/16px #{$font-family};
--font-subhead-cap-small: 700 10px/16px #{$font-family};
--font-button: 500 14px/20px #{$font-family};
}

View file

@ -0,0 +1,3 @@
export * from './utilities/index.js';
export * from './regex.js';
export * from './scope.js';

View file

@ -0,0 +1,8 @@
export const emailRegEx = /^\S+@\S+\.\S+$/;
export const phoneRegEx = /^\d+$/;
export const usernameRegEx = /^[A-Z_a-z]\w*$/;
export const passwordRegEx = /^.{6,}$/;
export const webRedirectUriProtocolRegEx = /^https?:$/;
export const mobileUriSchemeProtocolRegEx = /^[a-z][\d_a-z]*(\.[\d_a-z]+)+:$/;
export const hexColorRegEx = /^#[\da-f]{3}([\da-f]{3})?$/i;
export const dateRegex = /^\d{4}(-\d{2}){2}/;

View file

@ -0,0 +1,85 @@
export enum ReservedScope {
OpenId = 'openid',
OfflineAccess = 'offline_access',
}
export type UserClaim =
| 'name'
| 'picture'
| 'username'
| 'role_names'
| 'email'
| 'email_verified'
| 'phone_number'
| 'phone_number_verified'
| 'custom_data'
| 'identities';
/**
* Scopes for ID Token and Userinfo Endpoint.
*/
export enum UserScope {
/**
* Scope for basic user info.
*
* See {@link idTokenClaims} for mapped claims in ID Token and {@link userinfoClaims} for additional claims in Userinfo Endpoint.
*/
Profile = 'profile',
/**
* Scope for user email address.
*
* See {@link idTokenClaims} for mapped claims in ID Token and {@link userinfoClaims} for additional claims in Userinfo Endpoint.
*/
Email = 'email',
/**
* Scope for user phone number.
*
* See {@link idTokenClaims} for mapped claims in ID Token and {@link userinfoClaims} for additional claims in Userinfo Endpoint.
*/
Phone = 'phone',
/**
* Scope for user's custom data.
*
* See {@link idTokenClaims} for mapped claims in ID Token and {@link userinfoClaims} for additional claims in Userinfo Endpoint.
*/
CustomData = 'custom_data',
/**
* Scope for user's social identity details.
*
* See {@link idTokenClaims} for mapped claims in ID Token and {@link userinfoClaims} for additional claims in Userinfo Endpoint.
*/
Identities = 'identities',
}
/**
* Mapped claims that ID Token includes.
*/
export const idTokenClaims: Readonly<Record<UserScope, UserClaim[]>> = Object.freeze({
[UserScope.Profile]: ['name', 'picture', 'username', 'role_names'],
[UserScope.Email]: ['email', 'email_verified'],
[UserScope.Phone]: ['phone_number', 'phone_number_verified'],
[UserScope.CustomData]: [],
[UserScope.Identities]: [],
});
/**
* Additional claims that Userinfo Endpoint returns.
*/
export const userinfoClaims: Readonly<Record<UserScope, UserClaim[]>> = Object.freeze({
[UserScope.Profile]: [],
[UserScope.Email]: [],
[UserScope.Phone]: [],
[UserScope.CustomData]: ['custom_data'],
[UserScope.Identities]: ['identities'],
});
export const userClaims: Readonly<Record<UserScope, UserClaim[]>> = Object.freeze(
// Hard to infer type directly, use `as` for a workaround.
// eslint-disable-next-line no-restricted-syntax
Object.fromEntries(
Object.values(UserScope).map((current) => [
current,
[...idTokenClaims[current], ...userinfoClaims[current]],
])
) as Record<UserScope, UserClaim[]>
);

View file

@ -0,0 +1,17 @@
import color from 'color';
// Color hsl lighten/darken takes percentage value only, need to implement absolute value update
export const absoluteLighten = (baseColor: color, delta: number) => {
const hslArray = baseColor.hsl().round().array();
return color([hslArray[0] ?? 0, hslArray[1] ?? 0, (hslArray[2] ?? 0) + delta], 'hsl');
};
export const absoluteDarken = (baseColor: color, delta: number) => {
const hslArray = baseColor.hsl().round().array();
return color([hslArray[0] ?? 0, hslArray[1] ?? 0, (hslArray[2] ?? 0) - delta], 'hsl');
};
export const generateDarkColor = (lightColor: string) =>
absoluteLighten(color(lightColor), 10).hex();

View file

@ -0,0 +1,4 @@
export * from './color.js';
export * from './string.js';
export * from './zod.js';
export * from './url.js';

View file

@ -0,0 +1,15 @@
import { generateRandomString } from './string.js';
describe('generateRandomString', () => {
it('should return a random string with specified length', () => {
const randomString = generateRandomString(32);
expect(randomString).toHaveLength(32);
});
it('should contain only 0-9, A-Z and a-z by default', () => {
const randomString = generateRandomString(32);
expect(() => {
/[\dA-Za-z]/.test(randomString);
}).toBeTruthy();
});
});

View file

@ -0,0 +1,6 @@
import { customAlphabet } from 'nanoid';
export const generateRandomString = (
size: number,
alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
): string => customAlphabet(alphabet, size)();

View file

@ -0,0 +1,23 @@
import { validateRedirectUrl } from './url.js';
describe('url utilities', () => {
it('should allow valid redirect URIs', () => {
expect(validateRedirectUrl('http://localhost:3001', 'web')).toBeTruthy();
expect(validateRedirectUrl('https://logto.dev/callback', 'web')).toBeTruthy();
expect(validateRedirectUrl('https://my-company.com/callback?test=123', 'web')).toBeTruthy();
expect(validateRedirectUrl('https://abc.com/callback?test=123#param=hash', 'web')).toBeTruthy();
expect(validateRedirectUrl('io.logto://my-app/callback', 'mobile')).toBeTruthy();
expect(validateRedirectUrl('com.company://myDemoApp/callback', 'mobile')).toBeTruthy();
expect(validateRedirectUrl('com.company://demo:1234', 'mobile')).toBeTruthy();
});
it('should detect invalid redirect URIs', () => {
expect(validateRedirectUrl('io.logto://my-app/callback', 'web')).toBeFalsy();
expect(validateRedirectUrl('ws://com.company://demo:1234', 'web')).toBeFalsy();
expect(validateRedirectUrl('abc.com', 'web')).toBeFalsy();
expect(validateRedirectUrl('abc.com', 'mobile')).toBeFalsy();
expect(validateRedirectUrl('http://localhost:3001', 'mobile')).toBeFalsy();
expect(validateRedirectUrl('https://logto.dev/callback', 'mobile')).toBeFalsy();
expect(validateRedirectUrl('demoApp/callback', 'mobile')).toBeFalsy();
});
});

View file

@ -0,0 +1,21 @@
import { mobileUriSchemeProtocolRegEx, webRedirectUriProtocolRegEx } from '../regex.js';
export const validateRedirectUrl = (url: string, type: 'web' | 'mobile') => {
try {
const { protocol } = new URL(url);
const protocolRegEx =
type === 'mobile' ? mobileUriSchemeProtocolRegEx : webRedirectUriProtocolRegEx;
return protocolRegEx.test(protocol);
} catch {
return false;
}
};
export const validateUriOrigin = (url: string) => {
try {
return new URL(url).origin === url;
} catch {
return false;
}
};

View file

@ -0,0 +1,13 @@
import { number, ZodError } from 'zod';
import { fallback } from './zod.js';
describe('fallback', () => {
it('should fallback to default value', () => {
const schema = number();
const tolerant = schema.or(fallback(-1));
expect(() => schema.parse('foo')).toThrow(ZodError);
expect(tolerant.parse('foo')).toBe(-1);
});
});

View file

@ -0,0 +1,15 @@
import { any } from 'zod';
/**
* https://github.com/colinhacks/zod/issues/316#issuecomment-850906479
* Create a schema matches anything and returns a value. Use it with `or`:
*
* const schema = zod.number();
* const tolerant = schema.or(fallback(-1));
*
* schema.parse('foo') // => ZodError
* tolerant.parse('foo') // -1
*/
export function fallback<T>(value: T) {
return any().transform(() => value);
}

View file

@ -0,0 +1,5 @@
{
"extends": "./tsconfig",
"include": ["src"],
"exclude": ["src/**/*.test.ts"]
}

View file

@ -0,0 +1,10 @@
{
"extends": "@silverhand/ts-config/tsconfig.base",
"compilerOptions": {
"outDir": "lib",
"baseUrl": ".",
"moduleResolution": "nodenext",
"module": "esnext"
},
"include": ["src", "declaration", "jest.config.ts"]
}

View file

@ -0,0 +1,78 @@
# Change Log
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.0.0-beta.29](https://github.com/logto-io/toolkit/compare/v1.0.0-beta.28...v1.0.0-beta.29) (2022-12-07)
### Bug Fixes
* exports info and lock dep version ([#45](https://github.com/logto-io/toolkit/issues/45)) ([2ac83b4](https://github.com/logto-io/toolkit/commit/2ac83b4f0ff17579456569fb67ba018ac493c1af))
## [1.0.0-beta.28](https://github.com/logto-io/toolkit/compare/v1.0.0-beta.27...v1.0.0-beta.28) (2022-12-06)
**Note:** Version bump only for package @logto/language-kit
## [1.0.0-beta.26](https://github.com/logto-io/toolkit/compare/v1.0.0-beta.25...v1.0.0-beta.26) (2022-11-23)
**Note:** Version bump only for package @logto/language-kit
## [1.0.0-beta.24](https://github.com/logto-io/toolkit/compare/v1.0.0-beta.23...v1.0.0-beta.24) (2022-11-22)
**Note:** Version bump only for package @logto/language-kit
## [1.0.0-beta.21](https://github.com/logto-io/toolkit/compare/v1.0.0-beta.20...v1.0.0-beta.21) (2022-11-11)
**Note:** Version bump only for package @logto/language-kit
## [1.0.0-beta.20](https://github.com/logto-io/toolkit/compare/v1.0.0-beta.19...v1.0.0-beta.20) (2022-10-21)
**Note:** Version bump only for package @logto/language-kit
## [1.0.0-beta.19](https://github.com/logto-io/toolkit/compare/v1.0.0-beta.18...v1.0.0-beta.19) (2022-10-19)
### Bug Fixes
* language tag `jp` ([#28](https://github.com/logto-io/toolkit/issues/28)) ([0f5f6e6](https://github.com/logto-io/toolkit/commit/0f5f6e6a2cd9553e1a78aa7473f56e7631c2efc4))
## [1.0.0-beta.16](https://github.com/logto-io/toolkit/compare/v1.0.0-beta.15...v1.0.0-beta.16) (2022-09-28)
### Bug Fixes
* **core,language:** avoid using path aliases ([#15](https://github.com/logto-io/toolkit/issues/15)) ([22db3ed](https://github.com/logto-io/toolkit/commit/22db3ed2daf3ee5906ffc864bb9bed1a826df842))
## [1.0.0-beta.15](https://github.com/logto-io/toolkit/compare/v1.0.0-beta.14...v1.0.0-beta.15) (2022-09-27)
### Features
* **language,core,connector:** init language-kit package ([#14](https://github.com/logto-io/toolkit/issues/14)) ([9a74fc4](https://github.com/logto-io/toolkit/commit/9a74fc4d34c9ce277b8734ab78735549dc3a3cda))

View file

@ -0,0 +1,12 @@
import type { Config } from '@jest/types';
const config: Config.InitialOptions = {
preset: 'ts-jest',
moduleNameMapper: {
'^(\\.{1,2}/.*)\\.js$': '$1',
},
collectCoverageFrom: ['src/**/*.ts'],
coverageReporters: ['lcov', 'text-summary'],
};
export default config;

View file

@ -0,0 +1,61 @@
{
"name": "@logto/language-kit",
"version": "1.0.0-beta.29",
"author": "Silverhand Inc. <contact@silverhand.io>",
"homepage": "https://github.com/logto-io/toolkit#readme",
"repository": {
"type": "git",
"url": "git+https://github.com/logto-io/toolkit.git"
},
"license": "MPL-2.0",
"type": "module",
"main": "./lib/index.cjs",
"exports": {
"types": "./lib/index.d.ts",
"import": "./lib/index.js",
"require": "./lib/index.cjs"
},
"types": "./lib/index.d.ts",
"files": [
"lib"
],
"scripts": {
"precommit": "lint-staged",
"build": "rm -rf lib/ && rollup -c && tsc -p tsconfig.build.json",
"lint": "eslint --ext .ts src",
"lint:report": "pnpm lint --format json --output-file report.json",
"prepack": "pnpm build",
"test": "jest",
"test:coverage": "jest --silent --coverage"
},
"engines": {
"node": "^16.13.0 || ^18.12.0"
},
"peerDependencies": {
"zod": "^3.19.1"
},
"devDependencies": {
"@jest/types": "^29.0.3",
"@rollup/plugin-typescript": "^10.0.1",
"@silverhand/eslint-config": "1.3.0",
"@silverhand/ts-config": "1.2.1",
"@types/jest": "^29.0.3",
"@types/node": "^16.3.1",
"eslint": "^8.21.0",
"jest": "^29.0.3",
"lint-staged": "^13.0.0",
"prettier": "^2.7.1",
"rollup": "^3.6.0",
"ts-jest": "^29.0.1",
"tslib": "^2.4.1",
"typescript": "^4.7.4",
"zod": "^3.20.2"
},
"eslintConfig": {
"extends": "@silverhand"
},
"prettier": "@silverhand/eslint-config/.prettierrc",
"publishConfig": {
"access": "public"
}
}

View file

@ -0,0 +1,21 @@
import typescript from '@rollup/plugin-typescript';
/**
* @type {import('rollup').RollupOptions}
*/
const configs = [
{
input: ['src/index.ts'],
output: [
{
dir: 'lib',
format: 'cjs',
preserveModules: true,
entryFileNames: '[name].cjs',
},
],
plugins: [typescript({ tsconfig: 'tsconfig.build.json' })],
},
];
export default configs;

View file

@ -0,0 +1,125 @@
export const languages = Object.freeze({
'af-ZA': 'Afrikaans',
'am-ET': 'አማርኛ',
'ar-AR': 'العربية',
'as-IN': 'অসমীয়া',
'az-AZ': 'Azərbaycan dili',
'be-BY': 'Беларуская',
'bg-BG': 'Български',
'bn-IN': 'বাংলা',
'br-FR': 'Brezhoneg',
'bs-BA': 'Bosanski',
'ca-ES': 'Català',
'cb-IQ': 'کوردیی ناوەندی',
'co-FR': 'Corsu',
'cs-CZ': 'Čeština',
'cx-PH': 'Bisaya',
'cy-GB': 'Cymraeg',
'da-DK': 'Dansk',
de: 'Deutsch',
'de-DE': 'Deutsch (Deutschland)',
'el-GR': 'Ελληνικά',
en: 'English',
'en-GB': 'English (UK)',
'en-US': 'English (US)',
'eo-EO': 'Esperanto',
es: 'Español',
'es-ES': 'Español (España)',
'es-419': 'Español (Latinoamérica)',
'et-EE': 'Eesti',
'eu-ES': 'Euskara',
'fa-IR': 'فارسی',
'ff-NG': 'Fula',
'fi-FI': 'Suomi',
'fo-FO': 'Føroyskt',
fr: 'Français',
'fr-CA': 'Français (Canada)',
'fr-FR': 'Français (France)',
'fy-NL': 'Frysk',
'ga-IE': 'Gaeilge',
'gl-ES': 'Galego',
'gn-PY': 'Guarani',
'gu-IN': 'ગુજરાતી',
'ha-NG': 'Hausa',
'he-IL': 'עברית',
'hi-IN': 'हिन्दी',
'hr-HR': 'Hrvatski',
'ht-HT': 'Kreyòl Ayisyen',
'hu-HU': 'Magyar',
'hy-AM': 'Հայերեն',
'id-ID': 'Bahasa Indonesia',
'ik-US': 'Iñupiatun',
'is-IS': 'Íslenska',
it: 'Italiano',
'it-IT': 'italiano (Italia)',
'iu-CA': 'Inuktitut',
ja: '日本語',
'ja-JP': '日本語',
'ja-KS': '日本語(関西)',
'jv-ID': 'Basa Jawa',
'ka-GE': 'ქართული',
'kk-KZ': 'Қазақша',
'km-KH': 'ភាសាខ្មែរ',
'kn-IN': 'ಕನ್ನಡ',
ko: '한국어',
'ko-KR': '한국어',
'ku-TR': 'Kurdî (Kurmancî)',
'ky-KG': 'кыргызча',
'lo-LA': 'ພາສາລາວ',
'lt-LT': 'Lietuvių',
'lv-LV': 'Latviešu',
'mg-MG': 'Malagasy',
'mk-MK': 'Македонски',
'ml-IN': 'മലയാളം',
'mn-MN': 'Монгол',
'mr-IN': 'मराठी',
'ms-MY': 'Bahasa Melayu',
'mt-MT': 'Malti',
'my-MM': 'မြန်မာဘာသာ',
'nb-NO': 'Norsk (bokmål)',
'ne-NP': 'नेपाली',
'nl-BE': 'Vlaams',
'nl-NL': 'Nederlands',
'nn-NO': 'Norsk (nynorsk)',
'or-IN': 'ଓଡ଼ିଆ',
'pa-IN': 'ਪੰਜਾਬੀ',
'pl-PL': 'Polski',
'ps-AF': 'پښتو',
pt: 'português',
'pt-BR': 'Português (Brasil)',
'pt-PT': 'português (Portugal)',
'ro-RO': 'Română',
ru: 'Русский',
'ru-RU': 'Русский',
'rw-RW': 'Ikinyarwanda',
'sc-IT': 'Sardu',
'si-LK': 'සිංහල',
'sk-SK': 'Slovenčina',
'sl-SI': 'Slovenščina',
'sn-ZW': 'Shona',
'sq-AL': 'Shqip',
'sr-RS': 'Српски',
'sv-SE': 'Svenska',
'sw-KE': 'Kiswahili',
'sy-SY': 'ܣܘܪܝܝܐ',
'sz-PL': 'ślōnskŏ gŏdka',
'ta-IN': 'தமிழ்',
'te-IN': 'తెలుగు',
'tg-TJ': 'Тоҷикӣ',
'th-TH': 'ภาษาไทย',
'tl-PH': 'Filipino',
tr: 'Türkçe',
'tr-TR': 'Türkçe',
'tt-RU': 'Татарча',
'tz-MA': 'ⵜⴰⵎⴰⵣⵉⵖⵜ',
'uk-UA': 'Українська',
'ur-PK': 'اردو',
'uz-UZ': "O'zbek",
'vi-VN': 'Tiếng Việt',
zh: '中文',
'zh-CN': '简体中文',
'zh-HK': '繁體中文(香港)',
'zh-MO': '繁體中文(澳門)',
'zh-TW': '繁體中文(台灣)',
'zz-TR': 'Zaza',
});

View file

@ -0,0 +1,3 @@
export * from './const.js';
export * from './type.js';
export * from './utility.js';

View file

@ -0,0 +1,3 @@
import type { languages } from './const.js';
export type LanguageTag = keyof typeof languages;

View file

@ -0,0 +1,27 @@
import { isLanguageTag, languageTagGuard } from './utility.js';
describe('isLanguageTag', () => {
it('should pass when input is a valid language key', () => {
expect(isLanguageTag('en-GB')).toBeTruthy();
expect(isLanguageTag('zh-CN')).toBeTruthy();
});
it('should fail when input is not a valid language key', () => {
for (const invalidLanguageKey of [undefined, '', 'xx-XX']) {
expect(isLanguageTag(invalidLanguageKey)).toBeFalsy();
}
});
});
describe('languageTagGuard', () => {
it('should pass when input is a valid language key', () => {
expect(languageTagGuard.safeParse('en-GB').success).toBeTruthy();
expect(languageTagGuard.safeParse('zh-CN').success).toBeTruthy();
});
it('should fail when input is not a valid language key', () => {
for (const invalidLanguageKey of [undefined, '', 'xx-XX']) {
expect(languageTagGuard.safeParse(invalidLanguageKey).success).toBeFalsy();
}
});
});

View file

@ -0,0 +1,11 @@
import { z } from 'zod';
import { languages } from './const.js';
import type { LanguageTag } from './type.js';
export const isLanguageTag = (value: unknown): value is LanguageTag =>
typeof value === 'string' && value in languages;
export const languageTagGuard: z.ZodType<LanguageTag> = z
.any()
.refine((value: unknown) => isLanguageTag(value));

View file

@ -0,0 +1,5 @@
{
"extends": "./tsconfig",
"include": ["src"],
"exclude": ["src/**/*.test.ts"]
}

View file

@ -0,0 +1,10 @@
{
"extends": "@silverhand/ts-config/tsconfig.base",
"compilerOptions": {
"outDir": "lib",
"baseUrl": ".",
"moduleResolution": "nodenext",
"module": "esnext"
},
"include": ["src", "jest.config.ts"]
}

View file

@ -17,8 +17,8 @@
"test": "jest"
},
"devDependencies": {
"@logto/core-kit": "1.0.0-beta.28",
"@logto/language-kit": "1.0.0-beta.28",
"@logto/core-kit": "workspace:*",
"@logto/language-kit": "workspace:*",
"@logto/phrases": "workspace:*",
"@logto/phrases-ui": "workspace:*",
"@logto/schemas": "workspace:*",
@ -69,7 +69,7 @@
"superstruct": "^0.16.0",
"typescript": "^4.9.4",
"use-debounced-loader": "^0.1.1",
"zod": "^3.19.1"
"zod": "^3.20.2"
},
"engines": {
"node": "^16.13.0 || ^18.12.0"

File diff suppressed because it is too large Load diff

View file

@ -1,2 +1,3 @@
packages:
- 'packages/*'
- packages/*
- packages/toolkit/*