From 6dcd51631d15cd9783746e9664ccc3cd29f05b2c Mon Sep 17 00:00:00 2001 From: SamSalvatico <40636569+SamSalvatico@users.noreply.github.com> Date: Tue, 3 Sep 2024 08:37:36 +0200 Subject: [PATCH] Feature(OGCIO): merge to v1.19.0 (#124) * refactor(console): reorg logo uploads * refactor(experience): add label for input field (#6200) * feat(core): add quota guard for subject tokens (#6205) * style(experience): update floating label position (#6211) * refactor(core): update cache key * refactor(console): rename file * refactor(console): update all logo uploaders (#6209) * refactor(experience): show dark favicon (#6210) * feat(core): implement TOTP verification routes (#6201) * feat(core): implmenent totp verification routes implement totp verification routes * fix(core): update comments update comments * feat(core,schemas): implement backup codes verification (#6207) implement the backup code verification flow * refactor: fix experience branding fallback * fix(experience): use forgot password identifier in related flow (#6221) * refactor(console): improve branding experience * feat(core): handle dpop and client certificate for token exchange (#6199) * refactor: fix third-party app experience branding (#6223) * refactor(core): refactor organizations in grants (#6208) * test: add resource test cases for token exchange (#6216) * feat(core): handle dpop and client certificate for token exchange * refactor(core): refactor organizations in grants * test: add resource test cases for token exchange * feat(core,schemas): introduce new PUT experience API (#6212) * feat(core,schemas): introduce new PUT experience API introduce new PUT experience API * fix(core): fix some comments fix some comments * refactor: experience ssr (#6229) * refactor: experience ssr * refactor: fix parameter issue * chore(deps): upgrade packages * chore(deps): upgrade zod * feat(experience): support loading state for buttons (#6232) * refactor: patch type issues * chore: add changesets (#6239) * chore(deps): update vitest monorepo to v2 (major) (#6202) * chore(deps): update vitest monorepo to v2 * refactor: remove unused lint ignorings --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Gao Sun * feat(core,schemas): implement the sie settings guard (#6215) * feat(core,schemas): implement the sie settings guard implement the sie settings guard * fix(test): fix integration test fix integration test * test(core): add sie guard ut add sie guard ut * chore(core): add some comment add some comment * refactor(core): rename the sign-in-experience-settings class rename the sign-in-experience-settings class * feat: init elements * refactor(core): remove subject token api prefix (#6235) * feat(core): add get available sso connectors endpoint (#6224) feat(core): implement get sso connectors implement get sso connectors endpoint * feat(elements): init i18n * feat(core,schemas): implement the register flow (#6237) * feat(core,schemas): implement the register flow implement the register flow * refactor(core,schemas): relocate the profile type defs relocate the profile type defs * fix(core): fix the validation guard logic fix the validation guard logic * fix(core): fix social and sso identity not created bug fix social and sso identity not created bug * fix(core): fix social identities profile key fix social identities profile key * fix(core): fix sso query method fix sso query method * feat(core,schemas): add post custom ui assets api (#6118) * feat(core,schemas): add post custom ui assets api * test(core): add register integration tests (#6248) * test(core): add register integration tests add register integration tests * test: add enterprise sso integration tests add enterprise sso integration tests * feat(elements): add components * feat(core,schemas): implement the username password registration flow (#6249) * feat(core,schemas): implement the username password registration flow implement the username password registration flow * chore(core): update some comments update some comments * fix(test): fix integration tests fix integration tests * fix(test): fix lint fix lint * fix(experience): correct active state for input field (#6255) * refactor(console): use button loading in experience flow if possible (#6234) * refactor(experience): support and apply modal loading state (#6236) * refactor(experience): support and apply modal loading state * feat(experience): support cancel loading for modal * chore(elements): update readme * feat(core): add the new user provision (#6253) add the new user provision * feat(connector): enable custom headers for SMTP connector (#6256) * fix(console): fix Google connector `scope` field can not be unset bug (#6254) * style(experience): improve input filed style (#6260) * feat(core): set up proxy to host custom ui assets if available (#6214) * feat(core): set up proxy to host custom ui assets if available * refactor: use object param for koa spa proxy middleware * refactor: make queries param mandatory * style(experience): remove autofill style from input component (#6261) * fix(console): fix image upload in onboarding process (#6266) * fix(console): fix grant data card height (#6264) * fix(console): fix passwordless connector tester send failed bug (#6268) * feat(console): implement custom ui assets upload component (#6217) * ci: always set conclusion for alteration tests (#6276) * style(experience): add transition for notched border (#6265) * refactor(experience): avoid disabled button for continue button (#6271) * feat(core): add api quota guard for bring your ui feature (#6273) * fix(console): should not toast invitation sent message when creating tenant w/o invitee (#6270) fix(console): should not toast invitation sent message when creating tenant without invitee * feat(console): add impersonation price item (#6269) * fix(experience): shrink input field when autofilled by the browser (#6280) * feat(console): add impersonation tag to audit log (#6267) * feat(core,schemas): implement social/sso link and sync logic (#6257) * feat(core,schemas): implement social/sso link and sync logic implement social/sso link and sync logic * test(core): add intergration tests add integration tests * feat(core): add mfa verification guard (#6262) add mfa verification guard * chore: remove feature guard for token exchange (#6246) * chore: add changeset for impersonation (#6251) * chore(elements): move check to build * chore(deps): upgrade typescript * chore(elements): add locale changes * chore(deps): upgrade react * chore(elements): check git existence * feat(schemas): init app_secrets table * feat(core): multiple app secrets * refactor(core,schemas): refactor `CodeVerification` (#6277) * refactor(core,schemas): refactor the CodeVerification class split the CodeVerification class into EmailCodeVerification and PhoneCodeVerification * refactor(core,schemas): split CodeVerification type split CodeVerification type * fix(core): code review updates code review updates * feat: add content schema to HTTP 201 CREATED messages (#6244) feat: add content schema to 201 messages * feat(console,phrases): add bring your ui quota item to pricing table (#6274) * refactor(console,phrases,schemas): increase file upload size limit to 10mb (#6258) refactor(console,phrases,schemas): increase file upload size limit to 10 mb * feat(elements): init modal and input * refactor: fix phrases * feat(elements): init user provider * feat(elements): update name * feat(console,phrases): add bring your UI feature paywall (#6275) feat(console,phrases): add bring your ui feature paywall * chore: update README.md (#6297) * chore: update README.md * chore: add awesome list * style(experience): improve notched border animation (#6296) * fix(console): sidebar width should not be shrunk (#6299) * refactor(core): extract password-validator (#6282) * refactor(core): extract password-validator extract password validator * fix(core): update comments and rename method name update comment and rename method name * refactor(console): update file uploader component to 80px fixed height * fix(core): should not sync registered identifier from social (#6283) should not sync registered identifier from social * style(experience): use brand loading color for buttons (#6302) * chore(console): remove dev feature guard (#6303) * refactor(core): extract helpers and provision methods (#6285) extract helpers and provision methods * feat(console): support multiple app secrets * refactor(phrases): improve bring your ui field description * fix(console): add cloud guard to bring your ui form field * refactor(schemas): increase max upload file size limit to 20MB * fix(core): disable bring your ui feature for admin tenant (#6300) * fix(console): should be able to remove the zip on upload error (#6306) * refactor: generate application secret on creation * fix(console): fix loading and error handling for org details page (#6313) * refactor(console): keep button loading before redirecting to sign-in success page (#6305) * refactor: use vite for demo app * refactor(core): log app secret name * chore(phrases): sync keys and translate (#6315) * refactor(core): implement verification records map (#6289) * refactor(core): implement verificaiton records map implement verification records map * fix(core): fix invalid verification type error fix invalid verificaiton type error * fix(core): update the verification record map update the verification record map * fix(core): update some comments update some comments * refactor(core): polish promise dependency polish promise dependency * fix(core): fix the social/sso syncing profile logic fix the social/sso syncing profile logic * refactor(core): optimize the verification records map optimize the verification records map * fix(core): fix set method of VerificationRecord map fix set method of VerificationRecord map * refactor(experience): use button loading for social sign-in (#6316) * chore: add comment * chore: add comment * refactor(console): use vite * refactor(experience): use vite * refactor(console): use local mermaid import * fix(console): use correct public url (#6325) * refactor(console, experience): optimize bundling (#6326) * refactor(console, experience): optimize bundling * fix: use correct favicon paths * chore: use dynamic react dependency checking in bundling * refactor(core): rename some file names and methods (#6321) * refactor(core): rename some files name and methods rename some files name and methods, fix some comments * chore: update comments update comments * chore: update comments update comments * chore: polish the words polish the words * fix(console): check scope only when data is ready (#6329) * feat(core,schemas): implement profile fulfillment flow (#6293) * feat(core,schemas): implement profile fulfillment flow implement profile fulfillment flow * fix(test): fix integration tests fix integration tests * fix(core): fix rebase issue fix rebase issue * refactor(core): refactor the interaction set profile flow refactor the interaction set profile flow * test(core): add profile fulfillment integration tests (#6294) * test(core): add profile fufillment integration tests add profile fufillment integration tests * fix: fix integration tests fix integration tests * refactor(test): rebase and update the latest profile api rebase and update the latest profile api * fix(console): css loaded svg should be rendered properly (#6333) * fix(core): fix some webhook api body status 404 bug (#6311) * fix(core): fix some webhook api body status 404 bug fix some webhook api body status 404 bug * fix(core): improve the webhook trigger logic improve the webhook trigger logic * chore: add changeset add changeset * chore: update the changeset update the changeset * feat(core): implement the WebAuthn verification (#6308) feat(core): implement the webauthn verification implement the webauthn verification * feat(schemas): add custom data to application (#6309) * feat(core,schemas): add application custom data add application custom data * test(core): add update application with new custom data test add update application with new custom data test * refactor(console): increase custom ui assets upload timeout to 5 mins (#6319) refactor(console): increase custom ui assets upload timeout to 5mins * refactor: update logto/core cloud API usage * refactor: update code according to CR * refactor(console): update admin console using new pricing model (#6295) * refactor(console): update cloud API calls * refactor: update code according to CR * refactor: correct component usage * refactor(console): safely lazy load pages (#6332) * refactor(console): safely lazy load pages * chore(console): use react-safe-lazy * feat(core): implement the missing mfa bind and guard flow (#6320) * feat(core): implement the mfa binding flow implment the mfa binding flow * fix(test): fix integration tests fix integration tests * fix(core): fix the wrong status code fix the wrong status code * refactor(core): refactor bind backup codes refactor bind backup codes * refactor(core): extract isNewMfaVerification property (#6338) extract isNewMfaVerifrication property * refactor(core): refactor backup code generates flow (#6339) refactor(core): refactor backup code generate flow refactor backup code generate flow * fix(console): dragging anchor in the color picker on application branding page (#6340) * test(core): add the mfa binding integration tests (#6330) * refactor(core): refactor backup code generate flow refactor backup code generate flow * fix(core): fix api payload fix api payload * test(core): implement the mfa binding integration tests implement the mfa binding integration tests * test(core): rebase backup code refactor rebase backup code refactor * style(console): fix custom jwt guide card style (#6343) * refactor(console): block page navigation when uploading custom ui assets (#6342) * chore(console): update bring your ui documentation link (#6317) chore(console): add bring your ui documentation link * fix(elements): fix user context tag name (#6346) * chore: launch multiple app secrets * chore: launch multiple app secrets * refactor(core): use tsup for building * refactor: use tsup for building * refactor(console): improve ux * chore: fix failed tests * refactor(connector): use tsup for building * ci: add check job * feat(console): remove beta tag for protected app (#6341) * feat(console): add passport.js guide (#6344) * chore: update plausible urls (#6349) * refactor(console, experience): solve sass deprecations (#6356) * fix(console): fix the plan title for subscription plan selector (#6348) * refactor(core): refactor openapi docs for protected app (#6331) * refactor: update per review * feat: allow app secret edit (#6352) * fix(console): add dev guard on new pricing model subscription hooks (#6363) * feat(core): migrate register flow affiliate report logic (#6334) Migrate the new user affiliate flow from interaction API. - `postAffiliateLogs` is forked from `routes/interaction/actions/helpers.ts` * refactor(core): extract verified interaction guard middleware (#6336) * refactor(core): refactor backup code generate flow refactor backup code generate flow * fix(core): fix api payload fix api payload * fix(core): fix rebase issue fix rebase issue * refactor(core): extract verified interaction guard middleware extract verified interaction guard middleware * refactor(console): fix text overflow issue (#6366) * refactor(core): make the interaction event mandatory (#6337) * refactor(core): refactor backup code generate flow refactor backup code generate flow * fix(core): fix api payload fix api payload * fix(core): fix rebase issue fix rebase issue * refactor(core): make the interaction event mandatory make the interaction event mandatory * test: update integration tests update integration tests * fix(core): fix the middleware apply bug fix the koaExperienceInteraction middleware apply bug * feat(core): add webhooks middleware to experience api (#6357) * refactor(core): refactor backup code generate flow refactor backup code generate flow * fix(core): fix api payload fix api payload * fix(core): fix rebase issue fix rebase issue * feat(core): add hooks middleware to experience APIs add interaction hooks to experience APIs * refactor(core): refactor experience API context type refactor experience API context type * feat(connector): added postmark connector * chore: remove unused deps (#6372) * chore: remove unused deps * chore: fix version * refactor(core): improve swagger auth description (#6367) * feat(core,schemas): add auditLogs to experience API (#6361) * refactor(core): refactor backup code generate flow refactor backup code generate flow * fix(core): fix api payload fix api payload * fix(core): fix rebase issue fix rebase issue * feat(core,schemas): add auditLogs to experience API add auditLogs to experience API * refactor(core): allow cloudflare insights origin in csp (#6375) refactor(core): allow cloudflare csp * feat(core,schemas): add mandatory password guard on register (#6368) * refactor(core): refactor backup code generate flow refactor backup code generate flow * fix(core): fix api payload fix api payload * fix(core): fix rebase issue fix rebase issue * feat(core,schemas): add mandatory password guard on register add mandatory password guard on register * feat: add advanced search params to all supported endpoints (#6358) * feat: add search params to list users endpoint * feat: implement advanced search for all supported endpoints * chore(deps): update dependency nock to v14.0.0-beta.9 (#6243) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * feat(cli): add cli command to setup custom ui local debugging proxy (#6365) * feat(cli): add proxy * refactor(cli): polish code per comments * refactor(cli): polish code * refactor(cli): support serving static files * chore: add changeset * refactor: polish code * refactor(cli): polish code * refactor(cli): make json parse safer * feat(core,console,phrases): add custom data editor to application details page (#6370) * feat(core,console,phrases): add custom data editor to application details page add custom data editor to application details page * chore: add changeset add changeset * fix(core): fix input params bug fix input params bug * fix(test): fix the integration tests fix the integration tests * fix(console): use the form controller element use the form controller element * fix(core,console): remove deepPartial statement remove deepPartial statement from the patch application API payload guard * fix(test): fix backchannel integration test fix backchannel integration test * fix(core): allow non-json body type when parsing (#6379) * refactor(core): make password optional in NewPasswordIdentity (#6377) refactor(core): make password optional in NewPasswordIdentity verification make password optioanl in NewPasswordIdentity verification * refactor(console): get and check `skuId` from checkout session (#6369) * refactor(console): get and check skuId from checkout session * chore: update @logto/cloud dependency * refactor: add tests for content-type in oidc apis (#6380) * refactor(console): delay module loading suspense component display by 500ms (#6345) * chore(console): remove redunant login hint usage for invitation (#6385) * fix(core): error data bug fixing (#6382) fix(core): error code bug fixing error code bug fixing * refactor(console): update billing info showed in subscription details page (#6384) * fix(console): add in-line error message (#6386) * fix(console): add in-line error message add in-line error message * refactor(console): remove old validation logic remove old validation logic * fix(console): create tenant button should stretch to full width (#6381) * fix(console): manual update subscription data when add/delete resources (#6360) * fix(console): add post response hook to update subscription info for useApi hook * refactor: wrap sync subscription data method * chore(phrases): update content (#6392) chore: update content * fix(console): fix the subscription plan display in tenant dropdown (#6393) * refactor(core): should not guard sso authentication flow (#6394) should not guard mfa and profile fulfillment for the sso authentication flow * fix(core): should not throw when not adding any new roles to a user (#6387) * fix(console): should not call cloud API when tenant ID is not valid (#6399) * refactor(console): improve guide logo and contact us logo display (#6391) * feat(core,schemas): add support for argon2d and argon2id (#6404) * feat(console): support next auth v5 (#6397) * feat: add add-on feature notice/tag * chore: define add on unit price temporarily * refactor: produce br outputs (#6376) * refactor: produce br outputs * refactor: fix favicon url * refactor: add `report:subscription:updates` Cloud scope (#6403) * Revert "refactor: add `report:subscription:updates` Cloud scope" (#6412) Revert "refactor: add `report:subscription:updates` Cloud scope (#6403)" This reverts commit e1922e9afbb8f9a0ce4b9fea4b154e9266bcedcf. * fix(console): fix unexpected 401 error toast (#6416) * feat(core): add Sentinel guard (#6374) feat(core): add sentinel protection add sentinel protection * feat(core): support google one tap (#6395) * feat(core): support google one tap support google one tap verification * fix(core): fix google one tap verification error fix google one tap verification error * fix(test): optimize social verification test optimize social verificaiton tests * fix(test): update social verification ut update social verification util unit test * refactor(core,schemas): refactor the register flow (#6401) * refactor(core,schemas): refactor the registration flow refactor the registraction flow * fix(core): remove unused method remove unused method * fix(test): remove legacy test remove legacy test * fix(core): fix webauthn verificaiton api fix webauthn verification api * feat(console): add new usage display for pro subscription plan (#6413) * release: version packages (#6197) * feat(cli): added ogcio folder * chore(cli): added ref to ogcio command * feat(core): added env sample * chore(cli): added port collision fix * chore(core): updated docker compose * chore(cli): fixed dockerfile * chore(cli): added makefile * chore(core): updated package json * chore(core): updated run logto remote * chore(core): updated pr request template * chore(cli): ogcio connectors * chore(phrases): updated errors * chore(core): added lot of stuffs * fix(cli): fixed port * workflow main * dockerignore * readme * basics * basics * basics * basics * eof * eof * eof * chore(core): synced --------- Co-authored-by: Gao Sun Co-authored-by: Xiao Yijun Co-authored-by: wangsijie Co-authored-by: simeng-li Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Charles Zhao Co-authored-by: Darcy Ye Co-authored-by: Mostafa Moradian Co-authored-by: Sten Roger Sandvik Co-authored-by: silverhand-bot <107667382+silverhand-bot@users.noreply.github.com> --- .dockerignore | 1 - ...eration-compatibility-integration-test.yml | 8 + .github/workflows/main.yml | 22 +- .npmrc | 6 - .scripts/compare-database.js | 4 +- .scripts/package.sh | 2 +- .vscode/settings.json | 8 +- .vscode/tsx.code-snippets | 2 +- AWESOME.md | 11 + Dockerfile | 3 +- README.md | 7 +- commitlint.config.ts | 2 +- package.json | 8 +- packages/app-insights/package.json | 6 +- packages/cli/CHANGELOG.md | 48 + packages/cli/package.json | 22 +- packages/cli/src/commands/proxy/index.ts | 106 + packages/cli/src/commands/proxy/types.ts | 18 + packages/cli/src/commands/proxy/utils.ts | 157 + packages/cli/src/index.ts | 2 + packages/connectors/.gitignore | 3 +- .../connector-alipay-native/CHANGELOG.md | 10 + .../connector-alipay-native/package.json | 25 +- .../connector-alipay-web/CHANGELOG.md | 10 + .../connector-alipay-web/package.json | 25 +- .../connector-aliyun-dm/CHANGELOG.md | 10 + .../connector-aliyun-dm/package.json | 25 +- .../src/single-send-mail.test.ts | 1 - .../connector-aliyun-dm/src/utils.test.ts | 1 - .../connector-aliyun-sms/CHANGELOG.md | 10 + .../connector-aliyun-sms/package.json | 25 +- .../src/single-send-text.test.ts | 1 - .../connector-aliyun-sms/src/utils.test.ts | 1 - .../connectors/connector-apple/CHANGELOG.md | 10 + .../connectors/connector-apple/package.json | 27 +- .../connectors/connector-aws-ses/CHANGELOG.md | 10 + .../connectors/connector-aws-ses/package.json | 25 +- .../connectors/connector-azuread/CHANGELOG.md | 10 + .../connectors/connector-azuread/package.json | 25 +- .../connector-dingtalk-web/CHANGELOG.md | 10 + .../connector-dingtalk-web/package.json | 25 +- .../connectors/connector-discord/CHANGELOG.md | 10 + .../connectors/connector-discord/package.json | 25 +- .../connector-facebook/CHANGELOG.md | 10 + .../connector-facebook/package.json | 25 +- .../connector-feishu-web/CHANGELOG.md | 10 + .../connector-feishu-web/package.json | 25 +- .../connectors/connector-github/CHANGELOG.md | 10 + .../connectors/connector-github/package.json | 27 +- .../connectors/connector-google/CHANGELOG.md | 10 + .../connectors/connector-google/package.json | 27 +- .../connector-huggingface/CHANGELOG.md | 15 + .../connector-huggingface/package.json | 27 +- .../connectors/connector-kakao/CHANGELOG.md | 10 + .../connectors/connector-kakao/package.json | 25 +- .../connector-logto-email/CHANGELOG.md | 10 + .../connector-logto-email/package.json | 27 +- .../connector-logto-email/src/constant.ts | 2 +- .../connector-logto-sms/CHANGELOG.md | 10 + .../connector-logto-sms/package.json | 25 +- .../connector-logto-social-demo/CHANGELOG.md | 10 + .../connector-logto-social-demo/package.json | 25 +- .../connectors/connector-mailgun/CHANGELOG.md | 10 + .../connectors/connector-mailgun/package.json | 25 +- .../CHANGELOG.md | 10 + .../package.json | 25 +- .../connector-mock-email/CHANGELOG.md | 10 + .../connector-mock-email/package.json | 25 +- .../connector-mock-sms/CHANGELOG.md | 10 + .../connector-mock-sms/package.json | 25 +- .../connector-mock-social/CHANGELOG.md | 10 + .../connector-mock-social/package.json | 25 +- .../connector-mock-social/src/index.ts | 4 +- .../connectors/connector-mygovid/package.json | 6 +- .../connectors/connector-naver/CHANGELOG.md | 10 + .../connectors/connector-naver/package.json | 25 +- .../connectors/connector-oauth2/CHANGELOG.md | 10 + .../connectors/connector-oauth2/package.json | 32 +- .../connector-ogcio-entraid/package.json | 6 +- .../connectors/connector-oidc/CHANGELOG.md | 15 + .../connectors/connector-oidc/package.json | 31 +- .../connector-postmark/CHANGELOG.md | 7 + .../connectors/connector-postmark/README.md | 61 + .../connectors/connector-postmark/logo.svg | 17 + .../connector-postmark/package.json | 68 + .../connector-postmark/src/constant.ts | 57 + .../connector-postmark/src/index.test.ts | 42 + .../connector-postmark/src/index.ts | 65 + .../connectors/connector-postmark/src/mock.ts | 26 + .../connector-postmark/src/types.ts | 32 + .../connectors/connector-saml/CHANGELOG.md | 10 + .../connectors/connector-saml/package.json | 25 +- .../connector-sendgrid-email/CHANGELOG.md | 10 + .../connector-sendgrid-email/package.json | 25 +- .../connectors/connector-smsaero/CHANGELOG.md | 10 + .../connectors/connector-smsaero/package.json | 25 +- .../connectors/connector-smtp/CHANGELOG.md | 11 + .../connectors/connector-smtp/package.json | 25 +- .../connectors/connector-smtp/src/constant.ts | 9 + .../connector-smtp/src/index.test.ts | 22 + .../connectors/connector-smtp/src/index.ts | 11 +- .../connectors/connector-smtp/src/mock.ts | 1 + .../connectors/connector-smtp/src/types.ts | 1 + .../connector-tencent-sms/CHANGELOG.md | 10 + .../connector-tencent-sms/package.json | 25 +- .../connector-twilio-sms/CHANGELOG.md | 10 + .../connector-twilio-sms/package.json | 25 +- .../connector-wechat-native/CHANGELOG.md | 10 + .../connector-wechat-native/package.json | 25 +- .../connector-wechat-web/CHANGELOG.md | 10 + .../connector-wechat-web/package.json | 25 +- .../connectors/connector-wecom/CHANGELOG.md | 10 + .../connectors/connector-wecom/package.json | 25 +- packages/connectors/templates/package.json | 6 +- .../templates/preset/rollup.config.js | 25 - .../templates/preset/tsconfig.base.json | 9 - .../templates/preset/tsconfig.build.json | 5 - .../connectors/templates/preset/tsconfig.json | 15 +- .../templates/preset/tsconfig.test.json | 7 - .../templates/preset/tsup.config.ts | 5 + packages/connectors/templates/sync-preset.js | 2 +- packages/console/.eslintrc.cjs | 9 + packages/console/.parcelrc | 19 - packages/console/.parcelrc.arm64 | 23 - packages/console/CHANGELOG.md | 62 + packages/console/{src => }/index.html | 6 +- packages/console/package.json | 70 +- packages/console/parcel-transformer-mdx2.js | 61 - ...generate-jwt-customizer-type-definition.ts | 9 + packages/console/src/App.tsx | 2 + .../console/src/assets/docs/guides/README.md | 3 + .../docs/guides/api-express/logo-dark.svg | 10 + .../assets/docs/guides/api-express/logo.svg | 11 +- .../assets/docs/guides/api-python/logo.svg | 15 +- .../docs/guides/api-spring-boot/logo.svg | 10 +- .../assets/docs/guides/generate-metadata.js | 28 +- .../console/src/assets/docs/guides/index.tsx | 116 +- .../docs/guides/m2m-general/logo-dark.svg | 35 + .../assets/docs/guides/m2m-general/logo.svg | 52 +- .../docs/guides/native-android/logo.svg | 13 +- .../docs/guides/native-capacitor/logo.svg | 21 +- .../docs/guides/native-expo/logo-dark.svg | 3 + .../assets/docs/guides/native-expo/logo.svg | 4 +- .../docs/guides/native-flutter/logo.svg | 28 +- .../docs/guides/native-ios-swift/logo.svg | 13 +- .../assets/docs/guides/spa-angular/logo.svg | 28 +- .../docs/guides/spa-chrome-extension/logo.svg | 23 +- .../src/assets/docs/guides/spa-react/logo.svg | 11 +- .../assets/docs/guides/spa-vanilla/logo.svg | 14 +- .../src/assets/docs/guides/spa-vue/logo.svg | 6 +- .../assets/docs/guides/spa-webflow/logo.svg | 11 +- .../docs/guides/third-party-oidc/logo.svg | 21 +- .../console/src/assets/docs/guides/types.ts | 3 + .../web-dotnet-core-blazor-server/logo.svg | 8 +- .../web-dotnet-core-blazor-wasm/logo.svg | 8 +- .../fragments/_add-authentication.mdx | 2 +- .../docs/guides/web-dotnet-core-mvc/logo.svg | 14 +- .../docs/guides/web-dotnet-core/logo.svg | 14 +- .../assets/docs/guides/web-express/README.mdx | 2 +- .../docs/guides/web-express/logo-dark.svg | 10 + .../assets/docs/guides/web-express/logo.svg | 11 +- .../src/assets/docs/guides/web-go/README.mdx | 2 +- .../src/assets/docs/guides/web-go/logo.svg | 10 +- .../components/AlwaysIssueRefreshToken.tsx | 1 + .../components/ClientBasics/index.tsx | 2 +- .../docs/guides/web-gpt-plugin/logo.svg | 12 +- .../guides/web-java-spring-boot/README.mdx | 2 +- .../docs/guides/web-java-spring-boot/logo.svg | 11 +- .../guides/web-next-app-router/README.mdx | 2 +- .../guides/web-next-app-router/logo-dark.svg | 10 + .../docs/guides/web-next-app-router/logo.svg | 11 +- .../docs/guides/web-next-auth/README.mdx | 43 +- .../assets/docs/guides/web-next-auth/logo.svg | 40 +- .../assets/docs/guides/web-next/README.mdx | 2 +- .../assets/docs/guides/web-next/logo-dark.svg | 10 + .../src/assets/docs/guides/web-next/logo.svg | 11 +- .../assets/docs/guides/web-nuxt/README.mdx | 4 +- .../src/assets/docs/guides/web-nuxt/logo.svg | 4 +- .../docs/guides/web-outline/logo-dark.svg | 10 + .../assets/docs/guides/web-outline/logo.svg | 13 +- .../docs/guides/web-passport/README.mdx | 163 + .../docs/guides/web-passport/config.json | 3 + .../assets/docs/guides/web-passport/index.ts | 11 + .../docs/guides/web-passport/logo-dark.svg | 6 + .../assets/docs/guides/web-passport/logo.svg | 6 + .../src/assets/docs/guides/web-php/README.mdx | 2 +- .../src/assets/docs/guides/web-php/logo.svg | 15 +- .../assets/docs/guides/web-python/README.mdx | 2 +- .../assets/docs/guides/web-python/logo.svg | 15 +- .../assets/docs/guides/web-ruby/README.mdx | 4 +- .../src/assets/docs/guides/web-ruby/logo.svg | 199 + .../src/assets/docs/guides/web-ruby/logo.webp | Bin 33844 -> 0 bytes .../docs/guides/web-sveltekit/README.mdx | 2 +- .../assets/docs/guides/web-sveltekit/logo.svg | 5 +- .../assets/docs/guides/web-wordpress/logo.svg | 8 +- .../src/assets/icons/calendar-dark.svg | 9 + .../console/src/assets/icons/calendar.svg | 2 +- packages/console/src/assets/icons/email.svg | 2 +- packages/console/src/assets/icons/github.svg | 2 +- .../src/assets/images/blur-preview.svg | 102 + packages/console/src/cloud/AppRoutes.tsx | 2 +- .../cloud/pages/Main/InvitationList/index.tsx | 4 +- .../TenantLandingPageContent/index.tsx | 8 +- .../pages/Main/TenantLandingPage/index.tsx | 2 +- .../cloud/pages/SocialDemoCallback/index.tsx | 6 +- packages/console/src/cloud/types/router.ts | 20 + .../src/components/ActionBar/index.tsx | 2 +- .../src/components/ActionsButton/index.tsx | 8 +- .../AddOnNoticeFooter/index.module.scss | 17 + .../components/AddOnNoticeFooter/index.tsx | 30 + .../console/src/components/AppError/index.tsx | 10 +- .../src/components/AppLoading/index.tsx | 4 +- .../CreateForm/Footer/index.module.scss | 3 + .../CreateForm/Footer/index.tsx | 45 +- .../ApplicationCreation/CreateForm/index.tsx | 17 +- .../src/components/ApplicationName/index.tsx | 2 +- .../components/EventName/index.module.scss | 1 + .../components/EventName/index.tsx | 8 +- .../src/components/AuditLogTable/index.tsx | 2 +- .../src/components/BasicWebhookForm/index.tsx | 2 +- .../console/src/components/BillInfo/index.tsx | 4 +- .../src/components/Breakable/index.tsx | 2 +- .../components/ChargeNotification/index.tsx | 5 +- .../ConnectorForm/BasicForm/index.tsx | 78 +- .../ConfigForm/ConfigFormFields/index.tsx | 2 +- .../ConnectorForm/ConfigForm/index.tsx | 2 +- .../ConnectorForm/GoogleOneTapCard/index.tsx | 2 +- .../src/components/ConnectorLogo/index.tsx | 2 +- .../src/components/ConnectorTester/index.tsx | 2 +- .../src/components/Conversion/index.tsx | 3 +- .../ConnectorRadio/index.tsx | 2 +- .../ConnectorRadioGroup/index.tsx | 2 +- .../CreateConnectorForm/Footer/index.tsx | 68 +- .../PlatformSelector/index.tsx | 2 +- .../Skeleton/index.module.scss | 6 +- .../CreateConnectorForm/Skeleton/index.tsx | 6 +- .../components/CreateConnectorForm/index.tsx | 4 +- .../EnvTagOptionContent/index.tsx | 2 +- .../FeaturedPlanContent/index.tsx | 6 +- .../PlanCardItem/index.tsx | 4 +- .../SkuCardItem/FeaturedSkuContent/index.tsx | 35 + .../use-featured-sku-content.ts | 77 + .../SkuCardItem/index.tsx | 100 + .../SelectTenantPlanModal/index.tsx | 86 +- .../components/CreateTenantModal/index.tsx | 8 +- .../console/src/components/DateTime/index.tsx | 34 +- .../src/components/DetailsForm/index.tsx | 2 +- .../DetailsPage/DetailsPageHeader/index.tsx | 4 +- .../DetailsPage/Skeleton/index.module.scss | 8 +- .../components/DetailsPage/Skeleton/index.tsx | 2 +- .../src/components/DetailsPage/index.tsx | 4 +- .../console/src/components/Drawer/index.tsx | 4 +- .../src/components/EditScopeModal/index.tsx | 2 +- .../components/EmptyDataPlaceholder/index.tsx | 6 +- .../components/EntityItem/index.tsx | 2 +- .../components/SourceEntitiesBox/index.tsx | 6 +- .../components/SourceEntityItem/index.tsx | 2 +- .../components/TargetEntitiesBox/index.tsx | 4 +- .../components/TargetEntityItem/index.tsx | 4 +- .../src/components/EntitiesTransfer/index.tsx | 4 +- .../src/components/FeatureTag/AddOnTag.tsx | 19 + .../src/components/FeatureTag/BetaTag.tsx | 2 +- .../src/components/FeatureTag/index.tsx | 17 +- .../console/src/components/FileIcon/index.tsx | 20 + .../FormCard/FormCardLayout/index.module.scss | 2 +- .../FormCard/FormCardLayout/index.tsx | 2 +- .../FormCard/Skeleton/index.module.scss | 14 +- .../components/FormCard/Skeleton/index.tsx | 9 +- .../console/src/components/FormCard/index.tsx | 2 +- .../Guide/GuideCard/index.module.scss | 9 +- .../src/components/Guide/GuideCard/index.tsx | 11 +- .../components/Guide/GuideCardGroup/index.tsx | 2 +- .../components/Guide/ModalFooter/index.tsx | 2 +- .../Guide/ModalHeader/RequestForm.tsx | 2 +- .../components/Guide/ModalHeader/index.tsx | 4 +- .../Guide/StepsSkeleton/index.module.scss | 6 +- .../components/Guide/StepsSkeleton/index.tsx | 2 +- .../console/src/components/Guide/index.tsx | 5 +- .../components/ImageInputs/LogoAndFavicon.tsx | 62 + .../ImageInputs}/index.module.scss | 17 +- .../src/components/ImageInputs/index.tsx | 169 + .../console/src/components/Index/index.tsx | 4 +- .../src/components/InlineUpsell/index.tsx | 2 +- .../ItemPreview/ApplicationPreview.tsx | 2 +- .../src/components/ItemPreview/index.tsx | 2 +- .../console/src/components/ListPage/index.tsx | 4 +- .../components/LivePreviewButton/index.tsx | 4 +- .../index.tsx | 2 +- .../console/src/components/Markdown/index.tsx | 4 +- .../src/components/MauExceededModal/index.tsx | 20 +- .../src/components/MfaFactorTitle/index.tsx | 10 +- .../src/components/MultiOptionInput/index.tsx | 4 +- .../components/MultiTextInputField/index.tsx | 2 +- .../src/components/OpenExternalLink/index.tsx | 2 +- .../src/components/OrganizationList/index.tsx | 6 +- .../OrganizationRolesSelect/index.tsx | 4 +- .../components/PaymentOverdueModal/index.tsx | 4 +- .../src/components/PermissionsTable/index.tsx | 8 +- .../src/components/PlanDescription/index.tsx | 14 +- .../console/src/components/PlanName/index.tsx | 22 +- .../ProPlanUsageCard/index.module.scss | 35 + .../PlanUsage/ProPlanUsageCard/index.tsx | 78 + .../components/PlanUsage/index.module.scss | 17 + .../src/components/PlanUsage/index.tsx | 64 +- .../console/src/components/PlanUsage/utils.ts | 77 + .../src/components/ProgressBar/index.tsx | 2 +- .../src/components/QuotaGuardFooter/index.tsx | 2 +- .../console/src/components/Region/index.tsx | 2 +- .../src/components/RequestDataError/index.tsx | 6 +- .../components/RoleAssignmentModal/index.tsx | 4 +- .../console/src/components/RoleIcon/index.tsx | 4 +- .../components/ResourceItem/index.tsx | 6 +- .../components/SourceScopeItem/index.tsx | 2 +- .../components/SourceScopesBox/index.tsx | 6 +- .../components/TargetScopeItem/index.tsx | 4 +- .../components/TargetScopesBox/index.tsx | 4 +- .../components/RoleScopesTransfer/index.tsx | 4 +- .../SourceRolesBox/SourceRoleItem/index.tsx | 2 +- .../RolesTransfer/SourceRolesBox/index.tsx | 6 +- .../TargetRolesBox/TargetRoleItem/index.tsx | 4 +- .../RolesTransfer/TargetRolesBox/index.tsx | 4 +- .../components/RoleInformation/index.tsx | 4 +- .../src/components/RolesTransfer/index.tsx | 6 +- .../components/ToggleUiThemeButton/index.tsx | 6 +- .../SignInExperiencePreview/index.module.scss | 24 + .../SignInExperiencePreview/index.tsx | 78 +- .../SubmitFormChangesActionBar/index.tsx | 5 +- .../src/components/TemplateTable/index.tsx | 6 +- .../src/components/TenantEnvTag/index.tsx | 2 +- .../src/components/ThemedIcon/index.tsx | 2 +- .../components/Topbar/ContactModal/hook.tsx | 17 +- .../components/Topbar/ContactModal/index.tsx | 4 +- .../TenantDropdownItem/TenantStatusTag.tsx | 29 +- .../TenantDropdownItem/index.tsx | 22 +- .../TenantInvitationDropdownItem/index.tsx | 2 +- .../Topbar/TenantSelector/index.module.scss | 7 +- .../Topbar/TenantSelector/index.tsx | 6 +- .../Topbar/UserInfo/SubMenu/index.tsx | 6 +- .../UserInfoSkeleton/index.module.scss | 4 +- .../UserInfo/UserInfoSkeleton/index.tsx | 2 +- .../src/components/Topbar/UserInfo/index.tsx | 12 +- .../src/components/Topbar/index.module.scss | 6 +- .../console/src/components/Topbar/index.tsx | 12 +- .../UnsavedChangesAlertModal/index.tsx | 2 +- .../UserAccountInformation/index.tsx | 6 +- .../src/components/UserAvatar/index.tsx | 4 +- .../src/components/UserInfoCard/index.tsx | 2 +- .../console/src/components/UserName/index.tsx | 2 +- .../VerificationCodeInput/index.tsx | 2 +- packages/console/src/consts/applications.ts | 24 +- packages/console/src/consts/connectors.ts | 4 +- packages/console/src/consts/env.ts | 11 +- packages/console/src/consts/external-links.ts | 4 + packages/console/src/consts/logs.ts | 1 + packages/console/src/consts/plan-quotas.ts | 29 + .../console/src/consts/quota-item-phrases.ts | 123 + packages/console/src/consts/subscriptions.ts | 10 + packages/console/src/consts/tenants.ts | 92 +- packages/console/src/consts/user-assets.ts | 17 - .../CreateProductionTenantBanner/index.tsx | 2 +- .../TenantEnvMigrationModal/index.tsx | 10 +- .../AppContent/TenantSuspendedPage/index.tsx | 6 +- .../src/containers/AppContent/index.tsx | 27 +- .../Sidebar/components/Item/index.tsx | 2 +- .../Sidebar/components/Section/index.tsx | 2 +- .../ConsoleContent/Sidebar/hook.tsx | 34 +- .../ConsoleContent/Sidebar/index.module.scss | 7 + .../ConsoleContent/Sidebar/index.tsx | 6 +- .../src/containers/ConsoleContent/hooks.ts | 10 +- .../ConsoleContent/index.module.scss | 11 +- .../src/containers/ConsoleContent/index.tsx | 36 +- .../src/containers/ConsoleRoutes/index.tsx | 65 +- .../src/containers/ConsoleRoutes/internal.ts | 14 + .../src/contexts/AppThemeProvider/index.tsx | 8 +- .../SubscriptionDataProvider/index.tsx | 22 +- .../SubscriptionDataProvider/types.ts | 24 +- .../use-new-subscription-data.ts | 64 + .../src/ds-components/ActionMenu/index.tsx | 2 +- .../src/ds-components/Button/index.tsx | 2 +- .../console/src/ds-components/Card/index.tsx | 2 +- .../src/ds-components/CardTitle/index.tsx | 2 +- .../CategorizedCheckboxGroup/index.tsx | 2 +- .../Checkbox/Checkbox/index.module.scss | 1 + .../ds-components/Checkbox/Checkbox/index.tsx | 2 +- .../Checkbox/CheckboxGroup/index.tsx | 2 +- .../src/ds-components/CodeEditor/index.tsx | 2 +- .../ColorPicker/index.module.scss | 4 + .../src/ds-components/ColorPicker/index.tsx | 23 +- .../src/ds-components/ConfirmModal/index.tsx | 4 +- .../CopyToClipboard/index.module.scss | 22 +- .../ds-components/CopyToClipboard/index.tsx | 18 +- .../DataTransferBox/SourceDataItem/index.tsx | 2 +- .../DataTransferBox/SourceGroupItem/index.tsx | 6 +- .../DataTransferBox/SourcePanel/index.tsx | 6 +- .../DataTransferBox/TargetDataItem/index.tsx | 4 +- .../DataTransferBox/TargetPanel/index.tsx | 4 +- .../ds-components/DataTransferBox/index.tsx | 4 +- .../src/ds-components/Divider/index.tsx | 2 +- .../DragDrop/DragDropProvider.tsx | 2 +- .../ds-components/Dropdown/DropdownItem.tsx | 2 +- .../src/ds-components/Dropdown/index.tsx | 2 +- .../FormField/Skeleton.module.scss | 11 + .../src/ds-components/FormField/Skeleton.tsx | 18 + .../ds-components/FormField/index.module.scss | 4 + .../src/ds-components/FormField/index.tsx | 8 +- .../src/ds-components/IconButton/index.tsx | 2 +- .../ImageWithErrorFallback/index.tsx | 4 +- .../InlineNotification/index.tsx | 10 +- .../KeyValueInputField/index.tsx | 6 +- .../src/ds-components/ModalHeader/index.tsx | 4 +- .../src/ds-components/ModalLayout/index.tsx | 6 +- .../ds-components/MultiTextInput/index.tsx | 6 +- .../src/ds-components/Pagination/index.tsx | 2 +- .../src/ds-components/RadioGroup/Radio.tsx | 2 +- .../src/ds-components/RadioGroup/index.tsx | 2 +- .../src/ds-components/Search/index.tsx | 4 +- .../src/ds-components/Select/MultiSelect.tsx | 4 +- .../src/ds-components/Select/index.tsx | 10 +- .../src/ds-components/Spacer/index.tsx | 2 +- .../src/ds-components/Spinner/index.tsx | 2 +- .../src/ds-components/Switch/index.tsx | 2 +- .../src/ds-components/TabNav/TabNavItem.tsx | 2 +- .../src/ds-components/TabNav/index.tsx | 2 +- .../src/ds-components/TabWrapper/index.tsx | 2 +- .../Table/Skeleton/index.module.scss | 8 +- .../ds-components/Table/Skeleton/index.tsx | 2 +- .../ds-components/Table/TableEmptyWrapper.tsx | 2 +- .../src/ds-components/Table/TableError.tsx | 6 +- .../ds-components/Table/TablePlaceholder.tsx | 2 +- .../console/src/ds-components/Table/index.tsx | 2 +- .../console/src/ds-components/Tag/index.tsx | 6 +- .../ds-components/TextInput/NumericInput.tsx | 6 +- .../ds-components/TextInput/index.module.scss | 2 +- .../src/ds-components/TextInput/index.tsx | 6 +- .../src/ds-components/TextLink/index.tsx | 2 +- .../src/ds-components/Textarea/index.tsx | 2 +- .../src/ds-components/Tip/TipBubble/index.tsx | 2 +- .../src/ds-components/Tip/TipBubble/utils.ts | 4 +- .../src/ds-components/Tip/ToggleTip/index.tsx | 4 +- .../src/ds-components/Tip/Tooltip/index.tsx | 2 +- .../console/src/ds-components/Toast/index.tsx | 6 +- .../Uploader/FileUploader/index.module.scss | 24 +- .../Uploader/FileUploader/index.tsx | 54 +- .../Uploader/ImageUploader/index.tsx | 8 +- .../Uploader/ImageUploaderField/index.tsx | 6 +- .../src/hooks/use-api-resources-usage.ts | 55 +- packages/console/src/hooks/use-api.ts | 32 +- .../src/hooks/use-applications-usage.ts | 118 +- .../use-connector-form-config-parser.tsx | 8 +- .../src/hooks/use-console-routes/index.tsx | 8 +- .../routes/api-resources.tsx | 14 +- .../routes/applications.tsx | 8 +- .../use-console-routes/routes/audit-logs.tsx | 5 +- .../use-console-routes/routes/connectors.tsx | 6 +- .../routes/customize-jwt.tsx | 5 +- .../routes/enterprise-sso.tsx | 6 +- .../hooks/use-console-routes/routes/mfa.tsx | 3 +- .../routes/organization-template.tsx | 18 +- .../routes/organizations.tsx | 14 +- .../use-console-routes/routes/profile.tsx | 15 +- .../hooks/use-console-routes/routes/roles.tsx | 14 +- .../routes/sign-in-experience.tsx | 4 +- .../routes/tenant-settings.tsx | 24 +- .../hooks/use-console-routes/routes/users.tsx | 16 +- .../use-console-routes/routes/webhooks.tsx | 12 +- packages/console/src/hooks/use-logto-skus.ts | 52 + .../src/hooks/use-new-subscription-quota.ts | 19 + .../use-new-subscription-scopes-usage.ts | 39 + .../src/hooks/use-new-subscription-usage.ts | 19 + packages/console/src/hooks/use-subscribe.ts | 58 +- .../src/hooks/use-subscription-plans.ts | 1 + .../src/hooks/use-subscription-usage.ts | 1 + .../src/hooks/use-user-assets-service.ts | 13 + .../console/src/hooks/use-user-preferences.ts | 1 + .../src/icons/ConnectorPlatformIcon.tsx | 6 +- packages/console/src/include.d/react-app.d.ts | 65 - .../src/include.d/react-router-dom.d.ts | 1 - packages/console/src/include.d/vite-env.d.ts | 6 + .../ApplicationCredentials/index.tsx | 10 +- .../mdx-components/DetailsSummary/index.tsx | 4 +- .../src/mdx-components/Mermaid/index.tsx | 29 +- .../mdx-components/OidcCallbackUri/index.tsx | 2 +- .../src/mdx-components/Sample/index.tsx | 2 +- .../SsoSamlSpMetadata/index.tsx | 2 +- .../console/src/mdx-components/Step/index.tsx | 2 +- .../src/mdx-components/Steps/index.tsx | 2 +- .../console/src/mdx-components/Tabs/index.tsx | 2 +- .../mdx-components/UriInputField/index.tsx | 5 +- .../MultiCardSelector/CardItem.tsx | 2 +- .../CardSelector/MultiCardSelector/index.tsx | 2 +- .../components/DemoConnectorNotice/index.tsx | 2 +- .../onboarding/components/Topbar/index.tsx | 4 +- packages/console/src/onboarding/index.tsx | 2 +- .../onboarding/pages/CreateTenant/index.tsx | 34 +- .../InspireMe/index.module.scss | 2 +- .../SignInExperience/InspireMe/index.tsx | 6 +- .../Preview/PlatformTabs/PlatformTab.tsx | 2 +- .../Preview/PlatformTabs/index.tsx | 6 +- .../pages/SignInExperience/Preview/index.tsx | 2 +- .../Skeleton/index.module.scss | 4 +- .../pages/SignInExperience/Skeleton/index.tsx | 6 +- .../pages/SignInExperience/index.tsx | 13 +- .../pages/SignInExperience/options.tsx | 18 +- .../src/onboarding/pages/Welcome/index.tsx | 6 +- .../src/onboarding/pages/Welcome/options.tsx | 4 +- .../AcceptInvitation/SwitchAccount/index.tsx | 4 +- .../src/pages/AcceptInvitation/index.tsx | 5 +- .../CreatePermissionModal/index.tsx | 36 +- .../components/GuideDrawer/index.tsx | 6 +- .../components/GuideModal/index.tsx | 4 +- .../src/pages/ApiResourceDetails/index.tsx | 14 +- .../components/CreateForm/Footer.tsx | 40 +- .../components/CreateForm/index.module.scss | 3 + .../components/CreateForm/index.tsx | 14 +- .../components/GuideLibrary/index.tsx | 2 +- .../components/GuideLibraryModal/index.tsx | 4 +- .../console/src/pages/ApiResources/index.tsx | 10 +- .../Branding/LogoUploader.module.scss | 34 - .../Branding/LogoUploader.tsx | 79 - .../Branding/NonThirdPartyBrandingForm.tsx | 94 + .../Branding/index.module.scss | 13 + .../Branding/index.tsx | 155 +- .../Branding/utils.ts | 42 +- .../CreateSecretModal.tsx | 150 + .../EditSecretModal.tsx | 86 + .../EndpointsAndCredentials/index.module.scss | 35 + .../index.tsx} | 146 +- .../use-secret-table-columns.tsx | 108 + .../GuideDrawer/index.tsx | 11 +- .../MachineLogs/index.tsx | 2 +- .../index.tsx | 6 +- .../index.tsx | 2 +- .../Permissions/PermissionsCard/index.tsx | 2 +- .../PermissionsCard/use-scopes-table.tsx | 4 +- .../Permissions/index.tsx | 2 +- .../components/SessionForm.tsx | 2 +- .../ProtectedAppSettings/index.module.scss | 2 +- .../ProtectedAppSettings/index.tsx | 6 +- .../ApplicationDetailsContent/Settings.tsx | 29 +- .../index.module.scss | 15 - .../ApplicationDetailsContent/index.tsx | 78 +- .../ApplicationDetailsContent/utils.ts | 13 +- .../ApplicationDetails/GuideModal/index.tsx | 18 +- .../components/AppGuide/index.tsx | 31 +- .../src/pages/ApplicationDetails/index.tsx | 30 +- .../components/GuideLibrary/index.tsx | 4 +- .../components/GuideLibraryModal/index.tsx | 4 +- .../components/ProtectedAppCard/index.tsx | 8 +- .../components/ProtectedAppForm/index.tsx | 22 +- .../components/ProtectedAppModal/index.tsx | 2 +- .../components/TypeDescription/index.tsx | 2 +- .../console/src/pages/Applications/index.tsx | 6 +- .../components/EventIcon/index.tsx | 6 +- .../pages/AuditLogDetails/index.module.scss | 3 + .../src/pages/AuditLogDetails/index.tsx | 10 +- .../console/src/pages/AuditLogs/index.tsx | 2 +- .../pages/CheckoutSuccessCallback/index.tsx | 48 +- .../EmailServiceConnectorForm/index.tsx | 6 +- .../ConnectorContent/index.tsx | 12 +- .../ConnectorDetails/ConnectorTabs/index.tsx | 2 +- .../ConnectorDetails/EmailUsage/index.tsx | 8 +- .../src/pages/ConnectorDetails/index.tsx | 8 +- .../ConnectorDeleteButton/index.tsx | 2 +- .../pages/Connectors/ConnectorName/index.tsx | 2 +- .../Connectors/ConnectorStatusField/index.tsx | 4 +- .../src/pages/Connectors/Guide/index.tsx | 6 +- .../SignInExperienceSetupNotice/index.tsx | 2 +- .../console/src/pages/Connectors/index.tsx | 8 +- .../pages/CustomizeJwt/CreateButton/index.tsx | 18 +- .../CustomizeJwt/CustomizerItem/index.tsx | 6 +- .../UpsellNotice/index.module.scss | 52 + .../pages/CustomizeJwt/UpsellNotice/index.tsx | 48 + .../src/pages/CustomizeJwt/index.module.scss | 4 + .../console/src/pages/CustomizeJwt/index.tsx | 29 +- .../ActionButton/CodeRestoreButton.tsx | 4 +- .../MonacoCodeEditor/Dashboard/index.tsx | 4 +- .../MainContent/MonacoCodeEditor/index.tsx | 2 +- .../ScriptSection/ErrorContent/index.tsx | 2 +- .../MainContent/ScriptSection/index.tsx | 4 +- .../GuideCard/index.module.scss | 2 +- .../InstructionTab/GuideCard/index.tsx | 5 +- .../SettingsSection/InstructionTab/index.tsx | 23 +- .../SettingsSection/TestTab/index.tsx | 4 +- .../MainContent/SettingsSection/index.tsx | 6 +- .../CustomizeJwtDetails/MainContent/index.tsx | 2 +- .../PageLoadingSkeleton/index.module.scss | 5 +- .../PageLoadingSkeleton/index.tsx | 2 +- .../src/pages/CustomizeJwtDetails/index.tsx | 2 +- .../CustomizeJwtDetails/utils/config.tsx | 25 +- .../utils/type-definitions.ts | 4 + .../src/pages/Dashboard/components/Block.tsx | 8 +- .../components/ChartTooltip/index.tsx | 2 +- .../components/Skeleton/index.module.scss | 6 +- .../Dashboard/components/Skeleton/index.tsx | 2 +- .../console/src/pages/Dashboard/index.tsx | 2 +- .../EnterpriseSso/SsoConnectorLogo/index.tsx | 2 +- .../SsoConnectorRadio/index.tsx | 2 +- .../SsoConnectorRadioGroup/index.tsx | 2 +- .../SsoCreationModal/index.module.scss | 4 + .../EnterpriseSso/SsoCreationModal/index.tsx | 51 +- .../console/src/pages/EnterpriseSso/index.tsx | 26 +- .../Connection/FileReader/index.tsx | 10 +- .../ParsedConfigPreview/index.tsx | 2 +- .../Connection/OidcMetadataForm/index.tsx | 2 +- .../Connection/SamlAttributeMapping/index.tsx | 2 +- .../Connection/SamlConnectorForm.tsx | 2 +- .../ParsedConfigPreview/index.tsx | 2 +- .../SwitchFormatButton/index.tsx | 6 +- .../Connection/SamlMetadataForm/index.tsx | 2 +- .../Experience/DomainsInput/index.tsx | 4 +- .../Experience/LogosUploader/index.tsx | 10 +- .../EnterpriseSsoDetails/Experience/index.tsx | 2 +- .../EnterpriseSsoDetails/SsoGuide/index.tsx | 2 +- .../src/pages/EnterpriseSsoDetails/index.tsx | 6 +- .../index.module.scss | 2 +- .../ProtectedAppCreationForm/index.tsx | 2 +- .../console/src/pages/GetStarted/index.tsx | 14 +- .../FactorLabel/WebAuthnTipContent/index.tsx | 2 +- .../pages/Mfa/MfaForm/FactorLabel/index.tsx | 2 +- .../pages/Mfa/MfaForm/UpsellNotice/index.tsx | 51 + .../src/pages/Mfa/MfaForm/index.module.scss | 4 + .../console/src/pages/Mfa/MfaForm/index.tsx | 12 +- .../src/pages/Mfa/PageWrapper/index.tsx | 19 +- .../console/src/pages/Mfa/Skeleton/index.tsx | 2 +- packages/console/src/pages/NotFound/index.tsx | 6 +- .../EditOrganizationRolesModal/index.tsx | 2 +- .../AddAppsToOrganization.tsx | 2 +- .../MachineToMachine/index.tsx | 4 +- .../Members/AddMembersToOrganization.tsx | 2 +- .../OrganizationDetails/Members/index.tsx | 8 +- .../Settings/JitSettings.tsx | 15 +- .../Settings/index.module.scss | 6 +- .../OrganizationDetails/Settings/index.tsx | 35 +- .../OrganizationDetails/Settings/utils.ts | 14 +- .../src/pages/OrganizationDetails/index.tsx | 22 +- .../Permissions/ResourceName/index.tsx | 4 +- .../Permissions/index.tsx | 4 +- .../pages/OrganizationRoleDetails/index.tsx | 6 +- .../OrganizationPermissions/index.tsx | 8 +- .../CreateOrganizationRoleModal.tsx | 4 +- .../OrganizationRoles/index.tsx | 10 +- .../src/pages/OrganizationTemplate/index.tsx | 28 +- .../CreateOrganizationModal/index.module.scss | 3 + .../CreateOrganizationModal/index.tsx | 52 +- .../Introduction/components/FlexBox/index.tsx | 2 +- .../components/InteractiveDiagram/index.tsx | 2 +- .../Introduction/components/Panel/index.tsx | 2 +- .../components/Permission/index.tsx | 2 +- .../Introduction/components/Role/index.tsx | 2 +- .../Introduction/components/Section/index.tsx | 2 +- .../Introduction/components/User/index.tsx | 4 +- .../Introduction/components/Vector/John.tsx | 2 +- .../Introduction/components/Vector/Sarah.tsx | 2 +- .../Organizations/Introduction/index.tsx | 6 +- .../EmptyDataPlaceholder/index.tsx | 8 +- .../OrganizationsTable/index.tsx | 4 +- .../console/src/pages/Organizations/index.tsx | 26 +- .../Profile/components/CardContent/index.tsx | 2 +- .../components/ExperienceLikeModal/index.tsx | 6 +- .../components/LinkAccountSection/index.tsx | 4 +- .../pages/Profile/components/NotSet/index.tsx | 2 +- .../components/Skeleton/index.module.scss | 6 +- .../Profile/components/Skeleton/index.tsx | 2 +- .../BasicUserInfoUpdateModal/index.tsx | 2 +- .../containers/ChangePasswordModal/index.tsx | 2 +- .../DeletionConfirmationModal/index.tsx | 2 +- .../FinalConfirmationModal/index.tsx | 4 +- .../components/TenantsIssuesModal/index.tsx | 2 +- .../components/TenantsList/index.tsx | 2 +- .../containers/DeleteAccountModal/index.tsx | 2 +- .../VerificationCodeModal/index.tsx | 4 +- .../containers/VerifyPasswordModal/index.tsx | 8 +- packages/console/src/pages/Profile/index.tsx | 118 +- .../RoleDetails/RoleApplications/index.tsx | 6 +- .../AssignPermissionsModal/index.tsx | 32 +- .../src/pages/RoleDetails/RoleUsers/index.tsx | 10 +- .../console/src/pages/RoleDetails/index.tsx | 4 +- .../components/AssignRoleModal/index.tsx | 2 +- .../components/AssignedEntities/index.tsx | 2 +- .../components/CreateRoleForm/Footer.tsx | 49 +- .../Roles/components/CreateRoleForm/index.tsx | 2 +- .../components/CreateRoleModal/index.tsx | 2 +- packages/console/src/pages/Roles/index.tsx | 6 +- .../LogoAndFaviconUploader/index.tsx | 75 - .../Branding/BrandingForm/index.tsx | 103 +- .../Branding/CustomCssForm/index.tsx | 66 - .../index.module.scss | 6 + .../Branding/CustomUiForm/index.tsx | 117 + .../PageContent/Branding/index.tsx | 4 +- .../LanguageEditor/AddLanguageSelector.tsx | 6 +- .../LanguageEditor/LanguageDetails.tsx | 6 +- .../LanguageEditor/LanguageItem.tsx | 2 +- .../LanguageEditor/LanguageNav.tsx | 2 +- .../ManageLanguage/LanguageEditor/index.tsx | 4 +- .../Content/LanguagesForm/index.tsx | 2 +- .../PageContent/PasswordPolicy/index.tsx | 2 +- .../SignUpAndSignIn/AdvancedOptions/index.tsx | 2 +- .../SignInMethodEditBox/AddButton.tsx | 6 +- .../SignInMethodEditBox/SignInMethodItem.tsx | 8 +- .../SignInForm/SignInMethodEditBox/index.tsx | 2 +- .../SignUpAndSignIn/SignUpForm/index.tsx | 2 +- .../AddButton/index.tsx | 6 +- .../SelectedConnectorItem/index.tsx | 6 +- .../SocialConnectorEditBox/index.tsx | 2 +- .../ConnectorSetupWarning/index.tsx | 2 +- .../DiffSegment.tsx | 2 +- .../SignInDiffSection.tsx | 2 +- .../SignUpDiffSection.tsx | 2 +- .../SocialTargetsDiffSection.tsx | 2 +- .../SignUpAndSignInChangePreview/index.tsx | 2 +- .../components/FormFieldDescription/index.tsx | 2 +- .../components/FormSectionTitle/index.tsx | 2 +- .../SignInExperienceTabWrapper/index.tsx | 2 +- .../SignInExperience/PageContent/index.tsx | 22 +- .../PageContent/utils/parser.ts | 18 +- .../Skeleton/index.module.scss | 4 +- .../pages/SignInExperience/Skeleton/index.tsx | 6 +- .../SignInExperience/Welcome/GuideModal.tsx | 6 +- .../pages/SignInExperience/Welcome/index.tsx | 6 +- .../CustomUiAssetsUploader/index.module.scss | 62 + .../CustomUiAssetsUploader/index.tsx | 114 + .../components/Preview/index.tsx | 5 +- .../SignInExperienceContextProvider/index.tsx | 48 + .../src/pages/SignInExperience/index.tsx | 21 +- .../SigningKeyFormCard/index.module.scss | 2 +- .../SigningKeys/SigningKeyFormCard/index.tsx | 4 +- .../console/src/pages/SigningKeys/index.tsx | 2 +- .../MauLimitExceededNotification/index.tsx | 50 +- .../Subscription/CurrentPlan/index.tsx | 54 +- .../DiffQuotaItem/SkuQuotaItemPhrase.tsx | 43 + .../PlanQuotaList/DiffQuotaItem/index.tsx | 45 +- .../PlanQuotaDiffCard/PlanQuotaList/index.tsx | 34 +- .../PlanQuotaDiffCard/index.tsx | 29 +- .../DowngradeConfirmModalContent/index.tsx | 39 +- .../TableDataContent/index.tsx | 4 +- .../TableDataWrapper/index.tsx | 4 +- .../PlanComparisonTable/index.tsx | 8 +- .../SwitchPlanActionBar/index.tsx | 133 +- .../TenantSettings/Subscription/index.tsx | 17 +- .../TenantBasicSettings/DeleteCard/index.tsx | 2 +- .../TenantBasicSettings/DeleteModal/index.tsx | 2 +- .../TenantBasicSettings/LeaveCard/index.tsx | 2 +- .../ProfileForm/TenantEnvironment/index.tsx | 2 +- .../ProfileForm/TenantRegion/index.tsx | 2 +- .../TenantBasicSettings/index.tsx | 2 +- .../AddDomainForm/index.tsx | 2 +- .../DnsRecordsTable/index.tsx | 2 +- .../ActivationProcess/Step/index.tsx | 6 +- .../CustomDomain/ActivationProcess/index.tsx | 2 +- .../CustomDomain/CustomDomainHeader/index.tsx | 6 +- .../CustomDomain/index.tsx | 2 +- .../DefaultDomain/index.tsx | 2 +- .../TenantDomainSettings/index.tsx | 2 +- .../TenantMembers/EditMemberModal/index.tsx | 4 +- .../TenantMembers/Invitations/index.tsx | 14 +- .../TenantMembers/InviteEmailsInput/index.tsx | 4 +- .../InviteMemberModal/Footer/index.tsx | 12 +- .../TenantMembers/InviteMemberModal/index.tsx | 63 +- .../TenantSettings/TenantMembers/hooks.ts | 57 +- .../TenantMembers/index.module.scss | 4 + .../TenantSettings/TenantMembers/index.tsx | 8 +- .../index.tsx | 105 +- .../components/Skeleton/index.tsx | 2 +- .../src/pages/TenantSettings/index.tsx | 2 +- .../src/pages/UserDetails/UserLogs/index.tsx | 2 +- .../src/pages/UserDetails/UserRoles/index.tsx | 6 +- .../UserMfaVerifications/index.tsx | 2 +- .../components/UserSocialIdentities/index.tsx | 2 +- .../components/UserSsoIdentities/index.tsx | 2 +- .../console/src/pages/UserDetails/index.tsx | 12 +- .../Users/components/CreateForm/index.tsx | 4 +- packages/console/src/pages/Users/index.tsx | 10 +- .../WebhookDetails/WebhookLogs/index.tsx | 2 +- .../WebhookSettings/SigningKeyField/index.tsx | 4 +- .../WebhookSettings/TestWebhook/index.tsx | 2 +- .../src/pages/WebhookDetails/index.tsx | 12 +- .../Webhooks/CreateFormModal/CreateForm.tsx | 31 +- .../pages/Webhooks/CreateFormModal/index.tsx | 2 +- packages/console/src/pages/Webhooks/index.tsx | 12 +- .../src/pages/Welcome/index.module.scss | 4 +- packages/console/src/pages/Welcome/index.tsx | 4 +- .../console/src/types/sign-in-experience.ts | 11 + packages/console/src/types/skus.ts | 14 + packages/console/src/types/subscriptions.ts | 8 + packages/console/src/utils/connector-form.ts | 9 +- packages/console/src/utils/json.ts | 8 +- packages/console/src/utils/object.ts | 2 + packages/console/src/utils/quota.ts | 42 + packages/console/src/utils/subscription.ts | 54 +- packages/console/src/utils/uploader.ts | 19 +- packages/console/svgo.config.json | 13 - packages/console/tsconfig.json | 6 +- packages/console/vite.config.ts | 84 + packages/core/CHANGELOG.md | 139 + packages/core/nodemon.json | 17 - packages/core/package.json | 47 +- packages/core/src/__mocks__/index.ts | 19 +- .../core/src/__mocks__/sign-in-experience.ts | 2 +- packages/core/src/app/init.ts | 7 +- packages/core/src/caches/well-known.ts | 5 + packages/core/src/event-listeners/grant.ts | 3 +- packages/core/src/event-listeners/index.ts | 9 +- packages/core/src/event-listeners/utils.ts | 4 +- packages/core/src/libraries/ogcio-user.ts | 8 +- packages/core/src/libraries/quota.ts | 134 +- .../sign-in-experience/index.test.ts | 20 +- .../src/libraries/sign-in-experience/index.ts | 90 +- packages/core/src/libraries/user.test.ts | 39 +- packages/core/src/libraries/user.ts | 5 +- packages/core/src/libraries/user.utils.ts | 2 +- .../koa-app-secret-transpilation.test.ts | 154 + .../koa-app-secret-transpilation.ts | 141 + packages/core/src/middleware/koa-cors.ts | 4 + .../src/middleware/koa-experience-ssr.test.ts | 81 + .../core/src/middleware/koa-experience-ssr.ts | 67 + .../middleware/koa-interaction-details.ts | 0 .../src/middleware/koa-oidc-error-handler.ts | 14 +- .../core/src/middleware/koa-quota-guard.ts | 22 +- .../src/middleware/koa-security-headers.ts | 17 +- .../koa-serve-custom-ui-assets.test.ts | 91 + .../middleware/koa-serve-custom-ui-assets.ts | 40 + .../core/src/middleware/koa-serve-static.ts | 30 +- .../middleware/koa-slonik-error-handler.ts | 15 + .../core/src/middleware/koa-spa-proxy.test.ts | 34 +- packages/core/src/middleware/koa-spa-proxy.ts | 45 +- packages/core/src/oidc/extra-token-claims.ts | 52 +- .../src/oidc/grants/client-credentials.ts | 51 +- packages/core/src/oidc/grants/index.ts | 2 +- .../src/oidc/grants/refresh-token.test.ts | 40 +- .../core/src/oidc/grants/refresh-token.ts | 156 +- .../grants/token-exchange/actor-token.test.ts | 69 + .../oidc/grants/token-exchange/actor-token.ts | 38 + .../index.test.ts} | 12 +- .../index.ts} | 63 +- .../src/oidc/grants/token-exchange/types.ts | 13 + packages/core/src/oidc/grants/utils.ts | 191 + packages/core/src/oidc/init.ts | 106 +- packages/core/src/oidc/utils.test.ts | 18 +- packages/core/src/oidc/utils.ts | 24 +- .../core/src/queries/application-secrets.ts | 59 + .../queries/application-sign-in-experience.ts | 26 +- packages/core/src/queries/application.ts | 9 +- .../src/queries/sign-in-experience.test.ts | 3 +- packages/core/src/queries/users-roles.ts | 9 +- packages/core/src/routes-me/user-assets.ts | 2 +- .../application-custom-data.openapi.json | 24 + .../applications/application-custom-data.ts | 31 + ...cation-protected-app-metadata.openapi.json | 16 +- .../application-secret.openapi.json | 104 + .../routes/applications/application-secret.ts | 130 + .../application-sign-in-experience.ts | 6 +- .../applications/application.openapi.json | 64 +- .../routes/applications/application.test.ts | 2 +- .../src/routes/applications/application.ts | 124 +- .../core/src/routes/applications/types.ts | 1 - packages/core/src/routes/connector/index.ts | 5 +- packages/core/src/routes/domain.ts | 8 +- .../classes/experience-interaction.test.ts | 128 + .../classes/experience-interaction.ts | 513 +- .../src/routes/experience/classes/helpers.ts | 162 + .../classes/libraries/mfa-validator.ts | 131 + .../classes/libraries/password-validator.ts | 73 + .../classes/libraries/profile-validator.ts | 187 + .../classes/libraries/provision-library.ts | 288 + .../classes/libraries/sentinel-guard.ts | 72 + .../sign-in-experience-validator.test.ts | 399 + .../libraries/sign-in-experience-validator.ts | 253 + .../core/src/routes/experience/classes/mfa.ts | 330 + .../src/routes/experience/classes/profile.ts | 205 + .../routes/experience/classes/utils.test.ts | 38 + .../src/routes/experience/classes/utils.ts | 66 + .../verifications/backup-code-verification.ts | 152 + .../verifications/code-verification.ts | 287 +- .../enterprise-sso-verification.ts | 278 + .../experience/classes/verifications/index.ts | 101 +- .../new-password-identity-verification.ts | 150 + .../verifications/password-verification.ts | 63 +- .../verifications/social-verification.ts | 166 +- .../verifications/totp-verification.ts | 207 + .../verifications/verification-record.ts | 44 +- .../verifications/verification-records-map.ts | 36 + .../verifications/web-authn-verification.ts | 291 + packages/core/src/routes/experience/const.ts | 2 + packages/core/src/routes/experience/index.ts | 158 +- .../koa-experience-interaction-hooks.ts | 83 + .../middleware/koa-experience-interaction.ts | 41 +- .../koa-experience-verifications-audit-log.ts | 36 + .../src/routes/experience/profile-routes.ts | 222 + packages/core/src/routes/experience/types.ts | 86 + packages/core/src/routes/experience/utils.ts | 20 - .../backup-code-verification.ts | 104 + .../enterprise-sso-verification.ts | 163 + .../new-password-identity-verification.ts | 68 + .../password-verification.ts | 49 +- .../social-verification.ts | 79 +- .../verification-routes/totp-verification.ts | 135 + .../verification-routes/verification-code.ts | 88 +- .../web-authn-verification.ts | 224 + packages/core/src/routes/hook.ts | 7 +- packages/core/src/routes/init.ts | 8 +- .../interaction/actions/submit-interaction.ts | 2 +- .../core/src/routes/interaction/additional.ts | 4 +- .../src/routes/interaction/consent/index.ts | 8 +- packages/core/src/routes/interaction/index.ts | 20 +- packages/core/src/routes/interaction/mfa.ts | 2 +- .../middleware/koa-interaction-hooks.ts | 11 +- .../src/routes/interaction/single-sign-on.ts | 2 +- .../src/routes/interaction/types/index.ts | 1 - .../utils/single-sign-on-session.ts | 29 +- .../interaction/utils/single-sign-on.test.ts | 6 +- .../interaction/utils/single-sign-on.ts | 77 +- .../utils/social-verification.test.ts | 19 +- .../interaction/utils/social-verification.ts | 6 +- .../mandatory-user-profile-validation.ts | 6 +- .../verifications/mfa-payload-verification.ts | 20 +- .../verifications/mfa-verification.ts | 6 +- .../src/routes/logto-config/jwt-customizer.ts | 17 +- .../src/routes/organization-role/index.ts | 9 +- .../src/routes/organization-scope/index.ts | 9 +- .../application/index.openapi.json | 16 +- .../core/src/routes/organization/index.ts | 9 +- packages/core/src/routes/resource.scope.ts | 5 +- packages/core/src/routes/resource.ts | 7 +- packages/core/src/routes/role.scope.ts | 5 +- packages/core/src/routes/role.ts | 23 +- .../custom-ui-assets/index.openapi.json | 42 + .../custom-ui-assets/index.test.ts | 133 + .../custom-ui-assets/index.ts | 127 + .../src/routes/sign-in-experience/index.ts | 15 +- .../core/src/routes/sso-connector/index.ts | 7 +- ...penapi.json => subject-token.openapi.json} | 9 +- .../{security/index.ts => subject-token.ts} | 24 +- packages/core/src/routes/swagger/consts.ts | 20 +- packages/core/src/routes/swagger/index.ts | 42 +- .../core/src/routes/swagger/utils/general.ts | 31 +- .../src/routes/swagger/utils/operation-id.ts | 6 +- .../src/routes/swagger/utils/parameters.ts | 14 + packages/core/src/routes/user-assets.ts | 2 +- packages/core/src/routes/well-known.ts | 30 +- packages/core/src/sso/types/session.ts | 53 +- packages/core/src/tenants/Libraries.ts | 3 +- packages/core/src/tenants/Queries.ts | 2 + packages/core/src/tenants/SystemContext.ts | 16 + packages/core/src/tenants/Tenant.ts | 22 +- packages/core/src/test-utils/quota.ts | 2 + packages/core/src/utils/SchemaRouter.ts | 14 +- packages/core/src/utils/file.test.ts | 16 + packages/core/src/utils/file.ts | 17 + packages/core/src/utils/i18n.ts | 31 + .../core/src/utils/storage/azure-storage.ts | 23 +- packages/core/src/utils/storage/consts.ts | 8 - packages/core/src/utils/subscription/index.ts | 46 +- packages/core/src/utils/subscription/types.ts | 22 + packages/core/tsconfig.base.json | 13 - packages/core/tsconfig.build.json | 10 - packages/core/tsconfig.json | 22 +- packages/core/tsconfig.test.json | 5 +- packages/core/tsup.config.ts | 11 + packages/core/tsup.dev.config.ts | 9 + packages/create/CHANGELOG.md | 7 + packages/create/package.json | 4 +- packages/demo-app/.parcelrc.arm64 | 7 - packages/demo-app/CHANGELOG.md | 12 + packages/demo-app/{src => }/index.html | 6 +- packages/demo-app/package.json | 47 +- packages/demo-app/src/App.tsx | 25 +- packages/demo-app/src/DevPanel.tsx | 30 +- packages/demo-app/src/include.d/vite-end.d.ts | 2 + packages/demo-app/src/index.tsx | 2 - packages/demo-app/src/utils.ts | 28 +- packages/demo-app/tsconfig.json | 1 + packages/demo-app/vite.config.ts | 22 + packages/elements/.gitignore | 1 + packages/elements/README.md | 41 + packages/elements/index.html | 19 + packages/elements/lit-localize.json | 15 + packages/elements/package.json | 89 + .../elements/src/components/logto-avatar.ts | 53 + .../src/components/logto-button.styles.ts | 106 + .../elements/src/components/logto-button.ts | 58 + .../src/components/logto-card-section.ts | 33 + .../elements/src/components/logto-card.ts | 33 + .../src/components/logto-form-card.ts | 78 + .../src/components/logto-icon-button.ts | 51 + .../elements/src/components/logto-list-row.ts | 55 + .../elements/src/components/logto-list.ts | 23 + .../src/components/logto-modal-layout.ts | 75 + .../src/components/logto-modal.context.ts | 16 + .../elements/src/components/logto-modal.ts | 88 + .../src/components/logto-text-input.ts | 85 + .../src/elements/logto-profile-card.ts | 129 + packages/elements/src/icons/close.svg | 5 + packages/elements/src/icons/index.d.ts | 4 + packages/elements/src/index.ts | 18 + packages/elements/src/phrases/index.ts | 6 + .../src/providers/logto-theme-provider.ts | 22 + .../src/providers/logto-user-provider.ts | 61 + packages/elements/src/react.ts | 48 + packages/elements/src/utils/api.ts | 22 + packages/elements/src/utils/css.ts | 37 + packages/elements/src/utils/locale.ts | 11 + packages/elements/src/utils/string.ts | 19 + packages/elements/src/utils/theme.ts | 174 + packages/elements/tsconfig.json | 12 + packages/elements/tsup.config.ts | 23 + packages/elements/web-dev-server.config.js | 33 + packages/elements/xliff/de.xlf | 50 + packages/elements/xliff/es.xlf | 50 + packages/elements/xliff/fr.xlf | 50 + packages/elements/xliff/it.xlf | 50 + packages/elements/xliff/ja.xlf | 50 + packages/elements/xliff/ko.xlf | 50 + packages/elements/xliff/pl-PL.xlf | 50 + packages/elements/xliff/pt-BR.xlf | 50 + packages/elements/xliff/pt-PT.xlf | 50 + packages/elements/xliff/ru.xlf | 50 + packages/elements/xliff/tr-TR.xlf | 50 + packages/elements/xliff/zh-CN.xlf | 50 + packages/elements/xliff/zh-HK.xlf | 50 + packages/elements/xliff/zh-TW.xlf | 50 + packages/experience/.eslintrc.cjs | 22 + packages/experience/.parcelrc | 16 - packages/experience/.parcelrc.arm64 | 20 - packages/experience/CHANGELOG.md | 53 + packages/experience/index.html | 20 + packages/experience/jest.config.ts | 2 +- packages/experience/package.json | 74 +- .../experience/src/Layout/AppLayout/index.tsx | 2 +- .../src/Layout/LandingPageLayout/index.tsx | 2 +- .../src/Layout/SecondaryPageLayout/index.tsx | 2 +- .../src/Layout/SectionLayout/index.tsx | 2 +- .../src/Layout/StaticPageLayout/index.tsx | 2 +- .../src/Providers/AppBoundary/AppMeta.tsx | 19 +- .../Providers/AppBoundary/use-color-theme.ts | 2 + .../Providers/ConfirmModalProvider/index.tsx | 119 +- .../ConfirmModalProvider/indext.test.tsx | 210 +- .../IframeModalProvider/IframeModal/index.tsx | 2 +- .../Providers/LoadingLayerProvider/index.tsx | 6 +- .../UserInteractionContext.tsx | 38 + .../UserInteractionContextProvider/index.tsx | 47 +- packages/experience/src/__mocks__/logto.tsx | 4 +- packages/experience/src/apis/settings.ts | 16 +- .../src/assets/icons/loading-ring.svg | 3 + .../src/components/BrandingHeader/index.tsx | 4 +- .../src/components/Button/IconButton.tsx | 2 +- .../src/components/Button/MfaFactorButton.tsx | 12 +- .../Button/RotatingRingIcon.module.scss | 14 + .../components/Button/RotatingRingIcon.tsx | 7 + .../Button/SocialLinkButton.module.scss | 9 + .../components/Button/SocialLinkButton.tsx | 28 +- .../src/components/Button/index.module.scss | 34 +- .../src/components/Button/index.tsx | 24 +- .../src/components/Checkbox/index.tsx | 4 +- .../src/components/ConfirmModal/AcModal.tsx | 10 +- .../components/ConfirmModal/MobileModal.tsx | 14 +- .../src/components/ConfirmModal/type.ts | 2 + .../src/components/Divider/index.tsx | 2 +- .../src/components/ErrorMessage/index.tsx | 2 +- .../NotchedBorder/index.module.scss | 131 + .../InputField/NotchedBorder/index.tsx | 54 + .../InputFields/InputField/index.module.scss | 176 +- .../InputFields/InputField/index.tsx | 117 +- .../InputFields/PasswordInputField/index.tsx | 4 +- .../SmartInputField/AnimatedPrefix/index.tsx | 2 +- .../CountryCodeDropdown/index.module.scss | 4 +- .../CountryCodeDropdown/index.test.tsx | 8 +- .../CountryCodeDropdown/index.tsx | 7 +- .../CountryCodeSelector/index.module.scss | 8 + .../CountryCodeSelector/index.tsx | 6 +- .../InputFields/SmartInputField/index.tsx | 5 +- .../SmartInputField/use-smart-input-field.ts | 9 +- .../InputFields/SmartInputField/utils.test.ts | 10 +- .../InputFields/SmartInputField/utils.ts | 10 +- .../components/LoadingLayer/LoadingIcon.tsx | 4 +- .../components/LoadingLayer/index.module.scss | 7 - .../src/components/LoadingLayer/index.tsx | 8 +- .../components/LoadingMask/index.module.scss | 8 + .../src/components/LoadingMask/index.tsx | 13 + .../LogtoSignature/index.module.scss | 4 + .../src/components/LogtoSignature/index.tsx | 8 +- .../src/components/NavBar/index.tsx | 6 +- .../Notification/AppNotification/index.tsx | 4 +- .../Notification/InlineNotification/index.tsx | 2 +- .../components/SwitchMfaFactorsLink/index.tsx | 2 +- .../src/components/TermsLinks/index.tsx | 2 +- .../src/components/TextLink/index.tsx | 2 +- .../experience/src/components/Toast/index.tsx | 2 +- .../src/components/VerificationCode/index.tsx | 2 +- packages/experience/src/constants/env.ts | 4 - .../DevelopmentTenantNotification/index.tsx | 2 +- .../containers/ForgotPasswordLink/index.tsx | 32 +- .../src/containers/MfaFactorList/index.tsx | 2 +- .../SetPassword/HiddenIdentifierInput.tsx | 25 + .../src/containers/SetPassword/Lite.tsx | 21 +- .../containers/SetPassword/SetPassword.tsx | 25 +- .../containers/SetPassword/TogglePassword.tsx | 2 +- .../src/containers/SetPassword/index.tsx | 2 +- .../src/containers/SocialLanding/index.tsx | 2 +- .../SocialLinkAccount/index.module.scss | 6 +- .../SocialLinkAccount/index.test.tsx | 6 +- .../containers/SocialLinkAccount/index.tsx | 52 +- .../use-social-link-related-user.ts | 2 +- .../src/containers/SocialSignInList/index.tsx | 14 +- .../containers/SocialSignInList/use-social.ts | 16 +- .../TermsAndPrivacyCheckbox/index.tsx | 2 +- .../TotpCodeVerification/index.module.scss | 4 + .../containers/TotpCodeVerification/index.tsx | 77 +- .../use-totp-code-verification.ts | 4 +- .../VerificationCode/PasswordSignInLink.tsx | 9 +- .../VerificationCode/index.module.scss | 3 +- .../src/containers/VerificationCode/index.tsx | 78 +- .../use-continue-flow-code-verification.ts | 2 +- .../use-identifier-error-alert.ts | 4 +- .../use-link-social-confirm-modal.ts | 16 +- .../use-register-flow-code-verification.ts | 38 +- .../use-sign-in-flow-code-verification.ts | 42 +- .../src/hooks/use-check-single-sign-on.ts | 10 +- .../experience/src/hooks/use-confirm-modal.ts | 47 +- .../experience/src/hooks/use-error-handler.ts | 1 + .../src/hooks/use-global-redirect-to.ts | 74 +- .../src/hooks/use-password-sign-in.ts | 2 +- .../src/hooks/use-send-mfa-payload.ts | 2 +- .../src/hooks/use-send-verification-code.ts | 5 +- .../src/hooks/use-session-storages.ts | 6 +- .../src/hooks/use-single-sign-on.ts | 16 +- packages/experience/src/hooks/use-skip-mfa.ts | 2 +- .../src/hooks/use-social-link-account.ts | 2 +- .../src/hooks/use-social-register.ts | 2 +- packages/experience/src/hooks/use-terms.ts | 4 +- packages/experience/src/i18n/utils.ts | 41 +- packages/experience/src/include.d/global.d.ts | 14 +- .../experience/src/include.d/react-app.d.ts | 65 - .../src/include.d/react-router-dom.d.ts | 1 - .../experience/src/include.d/vite-env.d.ts | 2 + packages/experience/src/index.html | 34 - packages/experience/src/jest.setup.ts | 22 +- .../experience/src/pages/Callback/index.tsx | 2 +- .../OrganizationItem/index.module.scss | 2 +- .../OrganizationItem/index.tsx | 6 +- .../OrganizationSelectorModal/index.tsx | 2 +- .../Consent/OrganizationSelector/index.tsx | 4 +- .../src/pages/Consent/ScopeGroup/index.tsx | 6 +- .../pages/Consent/ScopesListCard/index.tsx | 2 +- .../src/pages/Consent/UserProfile/index.tsx | 4 +- .../experience/src/pages/Consent/index.tsx | 10 +- .../Continue/IdentifierProfileForm/index.tsx | 8 +- .../SocialIdentityNotification.tsx | 2 +- .../pages/Continue/SetEmailOrPhone/index.tsx | 5 + .../src/pages/Continue/SetPassword/index.tsx | 8 +- .../src/pages/Continue/SetUsername/index.tsx | 6 + .../Continue/SetUsername/use-set-username.ts | 2 +- .../src/pages/DirectSignIn/index.test.tsx | 31 +- .../experience/src/pages/ErrorPage/index.tsx | 6 +- .../ForgotPasswordForm/index.test.tsx | 23 +- .../ForgotPasswordForm/index.tsx | 17 +- .../src/pages/ForgotPassword/index.test.tsx | 111 +- .../src/pages/ForgotPassword/index.tsx | 17 +- .../MfaBinding/BackupCodeBinding/index.tsx | 11 +- .../TotpBinding/SecretSection/index.tsx | 2 +- .../pages/MfaBinding/TotpBinding/index.tsx | 2 +- .../MfaBinding/WebAuthnBinding/index.tsx | 11 +- .../BackupCodeVerification/index.tsx | 14 +- .../TotpVerification/index.tsx | 2 +- .../WebAuthnVerification/index.tsx | 11 +- .../IdentifierRegisterForm/index.test.tsx | 19 +- .../Register/IdentifierRegisterForm/index.tsx | 17 +- .../experience/src/pages/Register/index.tsx | 2 +- .../src/pages/RegisterPassword/index.tsx | 8 +- .../src/pages/ResetPassword/index.tsx | 14 +- .../IdentifierSignInForm/index.test.tsx | 35 +- .../SignIn/IdentifierSignInForm/index.tsx | 16 +- .../IdentifierSignInForm/use-on-submit.ts | 33 +- packages/experience/src/pages/SignIn/Main.tsx | 2 +- .../pages/SignIn/PasswordSignInForm/index.tsx | 16 +- .../experience/src/pages/SignIn/index.tsx | 2 +- .../PasswordForm/VerificationCodeLink.tsx | 2 +- .../PasswordForm/index.test.tsx | 1 - .../SignInPassword/PasswordForm/index.tsx | 16 +- .../src/pages/SignInPassword/index.test.tsx | 26 +- .../src/pages/SignInPassword/index.tsx | 18 +- .../pages/SingleSignOnConnectors/index.tsx | 17 +- .../src/pages/SingleSignOnEmail/index.tsx | 13 +- .../src/pages/SocialLanding/index.test.tsx | 19 +- .../src/pages/SocialLanding/index.tsx | 2 +- .../pages/SocialLinkAccount/index.test.tsx | 16 +- .../use-single-sign-on-listener.ts | 4 +- .../src/pages/VerificationCode/index.test.tsx | 31 +- .../src/pages/VerificationCode/index.tsx | 42 +- packages/experience/src/scss/_colors.scss | 3 + packages/experience/src/types/guard.ts | 35 +- .../experience/src/utils/search-parameters.ts | 33 +- .../src/utils/sign-in-experience.ts | 17 +- packages/experience/tsconfig.json | 2 +- packages/experience/vite.config.ts | 55 + packages/integration-tests/CHANGELOG.md | 61 + packages/integration-tests/jest.setup.api.js | 6 + packages/integration-tests/jest.setup.js | 3 - packages/integration-tests/package.json | 17 +- .../src/__mocks__/jwt-customizer.ts | 2 +- .../integration-tests/src/api/admin-user.ts | 3 + .../integration-tests/src/api/application.ts | 52 +- .../src/api/sso-connector.ts | 1 + .../src/api/subject-token.ts | 2 +- .../src/client/experience/const.ts | 2 + .../src/client/experience/index.ts | 134 +- packages/integration-tests/src/constants.ts | 2 +- .../integration-tests/src/helpers/client.ts | 3 + .../src/helpers/connector.ts | 1 + .../experience/enterprise-sso-verification.ts | 41 + .../src/helpers/experience/index.ts | 233 +- .../helpers/experience/totp-verification.ts | 39 + .../src/include.d/global.d.ts | 4 + .../src/tests/api/admin-user.test.ts | 4 +- .../application-custom-data.test.ts | 52 + .../application-sign-in-experience.test.ts | 13 - .../application/application.secrets.test.ts | 180 + .../tests/api/application/application.test.ts | 9 +- .../bind-mfa/happpy-path.test.ts | 272 + .../experience-api/bind-mfa/sad-path.test.ts | 179 + .../api/experience-api/interaction.test.ts | 65 + .../profile/fulfill-user-profiles.test.ts | 180 + .../profile/reset-password.test.ts | 150 + .../organization-jti.test.ts | 201 + .../username-password.test.ts | 85 + .../verification-code.test.ts | 191 + .../enterprise-sso.test.ts | 191 + .../mfa-verification.test.ts | 97 + .../sign-in-interaction/password.test.ts | 77 +- .../sign-in-interaction/social.test.ts | 351 + .../verification-code.test.ts | 75 +- .../backup-code-verification.test.ts | 90 + .../enterprise-sso-verification.test.ts | 247 + ...new-password-identity-verification.test.ts | 129 + .../password-verification.test.ts | 4 +- .../verifications/social-verification.test.ts | 7 +- .../verifications/totp-verification.test.ts | 163 + .../verifications/verification-code.test.ts | 6 +- .../src/tests/api/hook/WebhookMockServer.ts | 1 + .../tests/api/hook/hook.trigger.data.test.ts | 2 + .../api/hook/hook.trigger.experience.test.ts | 336 + .../src/tests/api/hook/utils.ts | 1 + .../tests/api/interaction/mfa/totp.test.ts | 4 +- .../api/interaction/organization-jit.test.ts | 2 +- .../api/oidc/client-authentication.test.ts | 242 + .../tests/api/oidc/content-type-json.test.ts | 35 + .../src/tests/api/oidc/id-token.test.ts | 4 +- .../api/oidc/refresh-token-grant.test.ts | 18 +- .../src/tests/api/oidc/token-exchange.test.ts | 257 +- .../src/tests/api/security.test.ts | 3 - .../src/tests/api/sign-in-experience.test.ts | 8 +- .../tests/console/backchannel-logout.test.ts | 9 +- .../src/tests/console/error-handling.test.ts | 46 + .../console/sign-in-experience/helpers.ts | 2 +- .../automatic-account-linking.test.ts | 2 +- .../experience/mfa/totp/social-flow.test.ts | 2 +- .../src/tests/experience/overrides.test.ts | 304 + .../experience/server-side-rendering.test.ts | 111 + .../src/ui-helpers/expect-experience.ts | 12 + .../integration-tests/src/ui-helpers/trace.ts | 54 + packages/integration-tests/tsup.config.ts | 10 + packages/phrases-experience/package.json | 5 +- .../src/locales/de/action.ts | 2 +- .../src/locales/de/description.ts | 3 +- .../src/locales/de/error/index.ts | 2 +- .../src/locales/en/action.ts | 2 +- .../src/locales/en/description.ts | 4 +- .../src/locales/en/error/index.ts | 2 +- .../src/locales/es/action.ts | 2 +- .../src/locales/es/description.ts | 3 +- .../src/locales/es/error/index.ts | 2 +- .../src/locales/fr/action.ts | 2 +- .../src/locales/fr/description.ts | 3 +- .../src/locales/fr/error/index.ts | 2 +- .../src/locales/it/action.ts | 2 +- .../src/locales/it/description.ts | 4 +- .../src/locales/it/error/index.ts | 2 +- .../src/locales/ja/action.ts | 2 +- .../src/locales/ja/description.ts | 4 +- .../src/locales/ja/error/index.ts | 2 +- .../src/locales/ko/action.ts | 2 +- .../src/locales/ko/description.ts | 3 +- .../src/locales/pl-pl/action.ts | 2 +- .../src/locales/pl-pl/description.ts | 4 +- .../src/locales/pl-pl/error/index.ts | 2 +- .../src/locales/pt-br/action.ts | 2 +- .../src/locales/pt-br/description.ts | 3 +- .../src/locales/pt-br/error/index.ts | 2 +- .../src/locales/pt-pt/action.ts | 2 +- .../src/locales/pt-pt/description.ts | 3 +- .../src/locales/ru/action.ts | 2 +- .../src/locales/ru/description.ts | 3 +- .../src/locales/ru/error/index.ts | 2 +- .../src/locales/tr-tr/action.ts | 2 +- .../src/locales/tr-tr/description.ts | 3 +- .../src/locales/tr-tr/error/index.ts | 2 +- .../src/locales/zh-cn/action.ts | 8 +- .../src/locales/zh-cn/description.ts | 16 +- .../src/locales/zh-cn/error/index.ts | 2 +- .../src/locales/zh-cn/user-scopes.ts | 27 +- .../src/locales/zh-hk/action.ts | 2 +- .../src/locales/zh-hk/description.ts | 3 +- .../src/locales/zh-hk/error/index.ts | 2 +- .../src/locales/zh-tw/action.ts | 2 +- .../src/locales/zh-tw/description.ts | 3 +- .../src/locales/zh-tw/error/index.ts | 2 +- packages/phrases-experience/src/types.ts | 10 +- packages/phrases/CHANGELOG.md | 44 + packages/phrases/package.json | 6 +- .../phrases/src/locales/de/errors/session.ts | 3 + .../admin-console/application-details.ts | 28 +- .../de/translation/admin-console/cloud.ts | 7 + .../translation/admin-console/components.ts | 2 +- .../admin-console/connector-details.ts | 18 +- .../translation/admin-console/connectors.ts | 12 +- .../translation/admin-console/jwt-claims.ts | 5 + .../admin-console/organization-details.ts | 50 +- .../admin-console/organization-template.ts | 5 +- .../admin-console/organizations.ts | 1 + .../de/translation/admin-console/profile.ts | 38 + .../translation/admin-console/role-details.ts | 3 +- .../de/translation/admin-console/roles.ts | 6 + .../admin-console/sign-in-exp/index.ts | 62 +- .../sign-in-exp/sign-up-and-sign-in.ts | 3 + .../admin-console/subscription/quota-item.ts | 12 + .../admin-console/subscription/quota-table.ts | 6 +- .../de/translation/admin-console/tenants.ts | 11 + .../admin-console/upsell/add-on.ts | 24 + .../admin-console/upsell/paywall.ts | 2 + .../translation/admin-console/user-details.ts | 7 + .../src/locales/de/translation/demo-app.ts | 1 + .../src/locales/en/errors/application.ts | 2 + .../phrases/src/locales/en/errors/guard.ts | 1 + .../phrases/src/locales/en/errors/session.ts | 5 + .../admin-console/application-details.ts | 47 +- .../en/translation/admin-console/cloud.ts | 2 +- .../translation/admin-console/components.ts | 2 +- .../admin-console/connector-details.ts | 4 +- .../translation/admin-console/connectors.ts | 12 +- .../translation/admin-console/jwt-claims.ts | 5 + .../admin-console/organization-details.ts | 5 + .../admin-console/sign-in-exp/index.ts | 58 +- .../admin-console/subscription/index.ts | 2 + .../admin-console/subscription/quota-item.ts | 12 + .../admin-console/subscription/quota-table.ts | 4 +- .../admin-console/subscription/usage.ts | 59 + .../admin-console/upsell/add-on.ts | 18 + .../translation/admin-console/upsell/index.ts | 2 + .../admin-console/upsell/paywall.ts | 10 +- .../phrases/src/locales/es/errors/session.ts | 3 + .../admin-console/application-details.ts | 25 +- .../es/translation/admin-console/cloud.ts | 7 + .../translation/admin-console/components.ts | 2 +- .../admin-console/connector-details.ts | 18 +- .../translation/admin-console/connectors.ts | 12 +- .../translation/admin-console/jwt-claims.ts | 5 + .../admin-console/organization-details.ts | 50 +- .../admin-console/organization-template.ts | 5 +- .../admin-console/organizations.ts | 1 + .../es/translation/admin-console/profile.ts | 38 + .../es/translation/admin-console/roles.ts | 5 + .../admin-console/sign-in-exp/index.ts | 60 +- .../sign-in-exp/sign-up-and-sign-in.ts | 3 + .../admin-console/subscription/quota-item.ts | 12 + .../admin-console/subscription/quota-table.ts | 4 +- .../es/translation/admin-console/tenants.ts | 11 + .../admin-console/upsell/add-on.ts | 24 + .../admin-console/upsell/paywall.ts | 2 + .../translation/admin-console/user-details.ts | 7 + .../src/locales/es/translation/demo-app.ts | 1 + .../phrases/src/locales/fr/errors/session.ts | 3 + .../admin-console/application-details.ts | 27 +- .../fr/translation/admin-console/cloud.ts | 7 + .../translation/admin-console/components.ts | 2 +- .../admin-console/connector-details.ts | 16 +- .../translation/admin-console/connectors.ts | 12 +- .../translation/admin-console/jwt-claims.ts | 5 + .../admin-console/organization-details.ts | 50 +- .../admin-console/organization-template.ts | 5 +- .../admin-console/organizations.ts | 1 + .../fr/translation/admin-console/profile.ts | 38 + .../fr/translation/admin-console/roles.ts | 6 + .../admin-console/sign-in-exp/index.ts | 60 +- .../sign-in-exp/sign-up-and-sign-in.ts | 3 + .../admin-console/subscription/quota-item.ts | 36 +- .../admin-console/subscription/quota-table.ts | 4 +- .../fr/translation/admin-console/tenants.ts | 11 + .../admin-console/upsell/add-on.ts | 24 + .../admin-console/upsell/paywall.ts | 14 +- .../translation/admin-console/user-details.ts | 7 + .../src/locales/fr/translation/demo-app.ts | 1 + .../phrases/src/locales/it/errors/session.ts | 3 + .../admin-console/application-details.ts | 25 +- .../it/translation/admin-console/cloud.ts | 7 + .../translation/admin-console/components.ts | 2 +- .../admin-console/connector-details.ts | 17 +- .../translation/admin-console/connectors.ts | 12 +- .../translation/admin-console/jwt-claims.ts | 7 +- .../admin-console/organization-details.ts | 49 +- .../admin-console/organization-template.ts | 5 +- .../admin-console/organizations.ts | 1 + .../it/translation/admin-console/profile.ts | 38 + .../it/translation/admin-console/roles.ts | 6 + .../admin-console/sign-in-exp/index.ts | 62 +- .../sign-in-exp/sign-up-and-sign-in.ts | 3 + .../admin-console/subscription/quota-item.ts | 12 + .../admin-console/subscription/quota-table.ts | 4 +- .../it/translation/admin-console/tenants.ts | 11 + .../admin-console/upsell/add-on.ts | 24 + .../admin-console/upsell/paywall.ts | 2 + .../translation/admin-console/user-details.ts | 7 + .../src/locales/it/translation/demo-app.ts | 1 + .../phrases/src/locales/ja/errors/session.ts | 36 +- .../admin-console/application-details.ts | 79 +- .../ja/translation/admin-console/cloud.ts | 17 +- .../translation/admin-console/components.ts | 2 +- .../admin-console/connector-details.ts | 30 +- .../translation/admin-console/connectors.ts | 43 +- .../translation/admin-console/jwt-claims.ts | 5 + .../admin-console/organization-details.ts | 48 +- .../admin-console/organization-template.ts | 7 +- .../admin-console/organizations.ts | 25 +- .../ja/translation/admin-console/profile.ts | 33 + .../ja/translation/admin-console/roles.ts | 10 +- .../admin-console/sign-in-exp/index.ts | 72 +- .../sign-in-exp/sign-up-and-sign-in.ts | 9 +- .../admin-console/subscription/quota-item.ts | 28 +- .../admin-console/subscription/quota-table.ts | 14 +- .../ja/translation/admin-console/tenants.ts | 15 +- .../admin-console/upsell/add-on.ts | 24 + .../admin-console/upsell/paywall.ts | 18 +- .../translation/admin-console/user-details.ts | 13 +- .../src/locales/ja/translation/demo-app.ts | 1 + .../phrases/src/locales/ko/errors/session.ts | 28 +- .../admin-console/application-details.ts | 24 +- .../ko/translation/admin-console/cloud.ts | 7 + .../translation/admin-console/components.ts | 2 +- .../admin-console/connector-details.ts | 18 +- .../translation/admin-console/connectors.ts | 11 +- .../translation/admin-console/jwt-claims.ts | 5 + .../admin-console/organization-details.ts | 48 +- .../admin-console/organization-template.ts | 5 +- .../admin-console/organizations.ts | 1 + .../ko/translation/admin-console/profile.ts | 33 + .../ko/translation/admin-console/roles.ts | 5 + .../admin-console/sign-in-exp/index.ts | 62 +- .../sign-in-exp/sign-up-and-sign-in.ts | 3 + .../admin-console/subscription/quota-item.ts | 12 + .../admin-console/subscription/quota-table.ts | 20 +- .../ko/translation/admin-console/tenants.ts | 10 + .../admin-console/upsell/add-on.ts | 24 + .../admin-console/upsell/paywall.ts | 17 +- .../translation/admin-console/user-details.ts | 7 + .../src/locales/ko/translation/demo-app.ts | 1 + .../src/locales/pl-pl/errors/session.ts | 3 + .../admin-console/application-details.ts | 24 +- .../pl-pl/translation/admin-console/cloud.ts | 7 + .../translation/admin-console/components.ts | 2 +- .../admin-console/connector-details.ts | 18 +- .../translation/admin-console/connectors.ts | 12 +- .../translation/admin-console/jwt-claims.ts | 5 + .../admin-console/organization-details.ts | 49 +- .../admin-console/organization-template.ts | 5 +- .../admin-console/organizations.ts | 1 + .../translation/admin-console/profile.ts | 34 + .../pl-pl/translation/admin-console/roles.ts | 6 + .../admin-console/sign-in-exp/index.ts | 58 +- .../sign-in-exp/sign-up-and-sign-in.ts | 3 + .../admin-console/subscription/quota-item.ts | 12 + .../admin-console/subscription/quota-table.ts | 4 +- .../translation/admin-console/tenants.ts | 17 +- .../admin-console/upsell/add-on.ts | 24 + .../admin-console/upsell/paywall.ts | 2 + .../translation/admin-console/user-details.ts | 7 + .../src/locales/pl-pl/translation/demo-app.ts | 1 + .../src/locales/pt-br/errors/session.ts | 3 + .../admin-console/application-details.ts | 25 +- .../pt-br/translation/admin-console/cloud.ts | 7 + .../translation/admin-console/components.ts | 2 +- .../admin-console/connector-details.ts | 18 +- .../translation/admin-console/connectors.ts | 11 +- .../translation/admin-console/jwt-claims.ts | 5 + .../admin-console/organization-details.ts | 49 +- .../admin-console/organization-template.ts | 5 +- .../admin-console/organizations.ts | 3 +- .../translation/admin-console/profile.ts | 36 + .../pt-br/translation/admin-console/roles.ts | 5 + .../admin-console/sign-in-exp/index.ts | 58 +- .../sign-in-exp/sign-up-and-sign-in.ts | 3 + .../admin-console/subscription/quota-item.ts | 12 + .../admin-console/subscription/quota-table.ts | 4 +- .../translation/admin-console/tenants.ts | 11 + .../admin-console/upsell/add-on.ts | 24 + .../admin-console/upsell/paywall.ts | 2 + .../translation/admin-console/user-details.ts | 7 + .../src/locales/pt-br/translation/demo-app.ts | 1 + .../src/locales/pt-pt/errors/session.ts | 3 + .../admin-console/application-details.ts | 27 +- .../pt-pt/translation/admin-console/cloud.ts | 7 + .../translation/admin-console/components.ts | 2 +- .../admin-console/connector-details.ts | 18 +- .../translation/admin-console/connectors.ts | 12 +- .../translation/admin-console/jwt-claims.ts | 5 + .../admin-console/organization-details.ts | 49 +- .../admin-console/organization-template.ts | 5 +- .../admin-console/organizations.ts | 1 + .../translation/admin-console/profile.ts | 36 + .../pt-pt/translation/admin-console/roles.ts | 5 + .../admin-console/sign-in-exp/index.ts | 60 +- .../sign-in-exp/sign-up-and-sign-in.ts | 3 + .../admin-console/subscription/quota-item.ts | 14 +- .../admin-console/subscription/quota-table.ts | 4 +- .../translation/admin-console/tenants.ts | 13 +- .../admin-console/upsell/add-on.ts | 24 + .../admin-console/upsell/paywall.ts | 2 + .../translation/admin-console/user-details.ts | 7 + .../src/locales/pt-pt/translation/demo-app.ts | 1 + .../phrases/src/locales/ru/errors/session.ts | 3 + .../admin-console/application-details.ts | 25 +- .../ru/translation/admin-console/cloud.ts | 7 + .../translation/admin-console/components.ts | 2 +- .../admin-console/connector-details.ts | 18 +- .../translation/admin-console/connectors.ts | 12 +- .../translation/admin-console/jwt-claims.ts | 5 + .../admin-console/organization-details.ts | 49 +- .../admin-console/organization-template.ts | 5 +- .../admin-console/organizations.ts | 1 + .../ru/translation/admin-console/profile.ts | 37 + .../ru/translation/admin-console/roles.ts | 6 + .../admin-console/sign-in-exp/index.ts | 63 +- .../sign-in-exp/sign-up-and-sign-in.ts | 3 + .../admin-console/subscription/quota-item.ts | 12 + .../admin-console/subscription/quota-table.ts | 4 +- .../ru/translation/admin-console/tenants.ts | 13 +- .../admin-console/upsell/add-on.ts | 24 + .../admin-console/upsell/paywall.ts | 4 +- .../translation/admin-console/user-details.ts | 7 + .../src/locales/ru/translation/demo-app.ts | 1 + .../src/locales/tr-tr/errors/session.ts | 29 +- .../admin-console/application-details.ts | 26 +- .../tr-tr/translation/admin-console/cloud.ts | 7 + .../translation/admin-console/components.ts | 3 +- .../admin-console/connector-details.ts | 18 +- .../translation/admin-console/connectors.ts | 11 +- .../translation/admin-console/jwt-claims.ts | 5 + .../admin-console/organization-details.ts | 49 +- .../admin-console/organization-template.ts | 5 +- .../admin-console/organizations.ts | 1 + .../translation/admin-console/profile.ts | 35 + .../tr-tr/translation/admin-console/roles.ts | 5 + .../admin-console/sign-in-exp/index.ts | 64 +- .../sign-in-exp/sign-up-and-sign-in.ts | 3 + .../admin-console/subscription/quota-item.ts | 12 + .../admin-console/subscription/quota-table.ts | 4 +- .../translation/admin-console/tenants.ts | 11 + .../admin-console/upsell/add-on.ts | 24 + .../admin-console/upsell/paywall.ts | 6 +- .../translation/admin-console/user-details.ts | 7 + .../src/locales/tr-tr/translation/demo-app.ts | 1 + .../src/locales/zh-cn/errors/session.ts | 29 +- .../admin-console/application-details.ts | 25 +- .../zh-cn/translation/admin-console/cloud.ts | 6 + .../translation/admin-console/components.ts | 2 +- .../admin-console/connector-details.ts | 15 +- .../translation/admin-console/connectors.ts | 10 +- .../translation/admin-console/jwt-claims.ts | 6 +- .../admin-console/organization-details.ts | 47 +- .../admin-console/organization-template.ts | 5 +- .../admin-console/organizations.ts | 23 +- .../translation/admin-console/profile.ts | 27 + .../zh-cn/translation/admin-console/roles.ts | 10 +- .../admin-console/sign-in-exp/index.ts | 60 +- .../sign-in-exp/sign-up-and-sign-in.ts | 5 +- .../admin-console/subscription/quota-item.ts | 12 + .../admin-console/subscription/quota-table.ts | 24 +- .../translation/admin-console/tenants.ts | 9 + .../admin-console/upsell/add-on.ts | 24 + .../admin-console/upsell/paywall.ts | 1 + .../translation/admin-console/user-details.ts | 19 +- .../src/locales/zh-cn/translation/demo-app.ts | 1 + .../src/locales/zh-hk/errors/session.ts | 2 + .../admin-console/application-details.ts | 24 +- .../zh-hk/translation/admin-console/cloud.ts | 6 + .../translation/admin-console/components.ts | 2 +- .../admin-console/connector-details.ts | 15 +- .../translation/admin-console/connectors.ts | 10 +- .../translation/admin-console/jwt-claims.ts | 12 +- .../admin-console/organization-details.ts | 47 +- .../admin-console/organization-template.ts | 5 +- .../admin-console/organizations.ts | 5 +- .../translation/admin-console/profile.ts | 27 + .../zh-hk/translation/admin-console/roles.ts | 10 +- .../admin-console/sign-in-exp/index.ts | 68 +- .../sign-in-exp/sign-up-and-sign-in.ts | 5 +- .../admin-console/subscription/quota-item.ts | 12 + .../admin-console/subscription/quota-table.ts | 4 +- .../translation/admin-console/tenants.ts | 39 +- .../admin-console/upsell/add-on.ts | 24 + .../admin-console/upsell/paywall.ts | 37 +- .../translation/admin-console/user-details.ts | 15 +- .../src/locales/zh-hk/translation/demo-app.ts | 1 + .../src/locales/zh-tw/errors/session.ts | 2 + .../admin-console/application-details.ts | 26 +- .../zh-tw/translation/admin-console/cloud.ts | 6 + .../translation/admin-console/components.ts | 2 +- .../admin-console/connector-details.ts | 15 +- .../translation/admin-console/connectors.ts | 10 +- .../translation/admin-console/jwt-claims.ts | 8 +- .../admin-console/organization-details.ts | 51 +- .../admin-console/organization-template.ts | 5 +- .../admin-console/organizations.ts | 63 +- .../translation/admin-console/profile.ts | 27 + .../zh-tw/translation/admin-console/roles.ts | 10 +- .../admin-console/sign-in-exp/index.ts | 60 +- .../sign-in-exp/sign-up-and-sign-in.ts | 3 + .../admin-console/subscription/quota-item.ts | 12 + .../admin-console/subscription/quota-table.ts | 20 +- .../translation/admin-console/tenants.ts | 9 + .../admin-console/upsell/add-on.ts | 24 + .../admin-console/upsell/paywall.ts | 37 +- .../translation/admin-console/user-details.ts | 13 +- .../src/locales/zh-tw/translation/demo-app.ts | 1 + packages/schemas/CHANGELOG.md | 78 + ....0-1720253939-add-organization-branding.ts | 18 + .../1.19.0-1720345784-add-color-to-app-sie.ts | 18 + ...19.0-1720505152-update-custom-ui-assets.ts | 20 + .../1.19.0-1721483240-multiple-app-secrets.ts | 77 + ...5392-add-application-custom-data-column.ts | 18 + .../1.19.0-1722926389-argon2d-argon2id.ts | 35 + packages/schemas/package.json | 14 +- packages/schemas/src/consts/oidc.ts | 16 +- packages/schemas/src/consts/system.ts | 3 + .../jsonb-types/sign-in-experience.ts | 31 +- packages/schemas/src/seeds/application.ts | 1 + .../schemas/src/seeds/sign-in-experience.ts | 4 +- packages/schemas/src/types/cookie.ts | 11 +- packages/schemas/src/types/index.ts | 1 + packages/schemas/src/types/interactions.ts | 114 +- packages/schemas/src/types/log/interaction.ts | 9 +- .../src/types/logto-config/jwt-customizer.ts | 15 +- .../schemas/src/types/sign-in-experience.ts | 2 +- packages/schemas/src/types/ssr.ts | 28 + packages/schemas/src/types/system.ts | 6 + packages/schemas/src/types/user-assets.ts | 27 +- packages/schemas/src/utils/application.ts | 9 + packages/schemas/src/utils/index.ts | 1 + .../schemas/tables/application_secrets.sql | 17 + .../application_sign_in_experiences.sql | 2 +- packages/schemas/tables/applications.sql | 9 +- packages/schemas/tables/organizations.sql | 2 + .../schemas/tables/sign_in_experiences.sql | 2 +- packages/schemas/tables/users.sql | 2 +- .../schemas/tsconfig.build.alterations.json | 5 +- packages/schemas/tsconfig.build.gen.json | 7 +- packages/shared/package.json | 8 +- packages/toolkit/connector-kit/package.json | 8 +- .../toolkit/core-kit/declaration/index.ts | 1 - .../core-kit/declaration/react-app.d.ts | 65 - packages/toolkit/core-kit/package.json | 10 +- packages/toolkit/language-kit/package.json | 8 +- pnpm-lock.yaml | 7895 ++++++++--------- tsup.shared.config.ts | 10 + vite.shared.config.ts | 52 + 1662 files changed, 36073 insertions(+), 10736 deletions(-) delete mode 100644 .npmrc create mode 100644 AWESOME.md create mode 100644 packages/cli/src/commands/proxy/index.ts create mode 100644 packages/cli/src/commands/proxy/types.ts create mode 100644 packages/cli/src/commands/proxy/utils.ts create mode 100644 packages/connectors/connector-postmark/CHANGELOG.md create mode 100644 packages/connectors/connector-postmark/README.md create mode 100644 packages/connectors/connector-postmark/logo.svg create mode 100644 packages/connectors/connector-postmark/package.json create mode 100644 packages/connectors/connector-postmark/src/constant.ts create mode 100644 packages/connectors/connector-postmark/src/index.test.ts create mode 100644 packages/connectors/connector-postmark/src/index.ts create mode 100644 packages/connectors/connector-postmark/src/mock.ts create mode 100644 packages/connectors/connector-postmark/src/types.ts delete mode 100644 packages/connectors/templates/preset/rollup.config.js delete mode 100644 packages/connectors/templates/preset/tsconfig.base.json delete mode 100644 packages/connectors/templates/preset/tsconfig.build.json delete mode 100644 packages/connectors/templates/preset/tsconfig.test.json create mode 100644 packages/connectors/templates/preset/tsup.config.ts delete mode 100644 packages/console/.parcelrc delete mode 100644 packages/console/.parcelrc.arm64 rename packages/console/{src => }/index.html (51%) delete mode 100644 packages/console/parcel-transformer-mdx2.js create mode 100644 packages/console/src/assets/docs/guides/api-express/logo-dark.svg create mode 100644 packages/console/src/assets/docs/guides/m2m-general/logo-dark.svg create mode 100644 packages/console/src/assets/docs/guides/native-expo/logo-dark.svg create mode 100644 packages/console/src/assets/docs/guides/web-express/logo-dark.svg create mode 100644 packages/console/src/assets/docs/guides/web-next-app-router/logo-dark.svg create mode 100644 packages/console/src/assets/docs/guides/web-next/logo-dark.svg create mode 100644 packages/console/src/assets/docs/guides/web-outline/logo-dark.svg create mode 100644 packages/console/src/assets/docs/guides/web-passport/README.mdx create mode 100644 packages/console/src/assets/docs/guides/web-passport/config.json create mode 100644 packages/console/src/assets/docs/guides/web-passport/index.ts create mode 100644 packages/console/src/assets/docs/guides/web-passport/logo-dark.svg create mode 100644 packages/console/src/assets/docs/guides/web-passport/logo.svg create mode 100644 packages/console/src/assets/docs/guides/web-ruby/logo.svg delete mode 100644 packages/console/src/assets/docs/guides/web-ruby/logo.webp create mode 100644 packages/console/src/assets/icons/calendar-dark.svg create mode 100644 packages/console/src/assets/images/blur-preview.svg create mode 100644 packages/console/src/components/AddOnNoticeFooter/index.module.scss create mode 100644 packages/console/src/components/AddOnNoticeFooter/index.tsx create mode 100644 packages/console/src/components/ApplicationCreation/CreateForm/Footer/index.module.scss create mode 100644 packages/console/src/components/CreateTenantModal/SelectTenantPlanModal/SkuCardItem/FeaturedSkuContent/index.tsx create mode 100644 packages/console/src/components/CreateTenantModal/SelectTenantPlanModal/SkuCardItem/FeaturedSkuContent/use-featured-sku-content.ts create mode 100644 packages/console/src/components/CreateTenantModal/SelectTenantPlanModal/SkuCardItem/index.tsx create mode 100644 packages/console/src/components/FeatureTag/AddOnTag.tsx create mode 100644 packages/console/src/components/FileIcon/index.tsx create mode 100644 packages/console/src/components/ImageInputs/LogoAndFavicon.tsx rename packages/console/src/{pages/SignInExperience/PageContent/Branding/BrandingForm/LogoAndFaviconUploader => components/ImageInputs}/index.module.scss (69%) create mode 100644 packages/console/src/components/ImageInputs/index.tsx create mode 100644 packages/console/src/components/PlanUsage/ProPlanUsageCard/index.module.scss create mode 100644 packages/console/src/components/PlanUsage/ProPlanUsageCard/index.tsx create mode 100644 packages/console/src/components/PlanUsage/utils.ts delete mode 100644 packages/console/src/consts/user-assets.ts create mode 100644 packages/console/src/containers/ConsoleRoutes/internal.ts create mode 100644 packages/console/src/contexts/SubscriptionDataProvider/use-new-subscription-data.ts create mode 100644 packages/console/src/ds-components/FormField/Skeleton.module.scss create mode 100644 packages/console/src/ds-components/FormField/Skeleton.tsx create mode 100644 packages/console/src/hooks/use-logto-skus.ts create mode 100644 packages/console/src/hooks/use-new-subscription-quota.ts create mode 100644 packages/console/src/hooks/use-new-subscription-scopes-usage.ts create mode 100644 packages/console/src/hooks/use-new-subscription-usage.ts delete mode 100644 packages/console/src/include.d/react-app.d.ts create mode 100644 packages/console/src/include.d/vite-env.d.ts create mode 100644 packages/console/src/pages/ApiResources/components/CreateForm/index.module.scss delete mode 100644 packages/console/src/pages/ApplicationDetails/ApplicationDetailsContent/Branding/LogoUploader.module.scss delete mode 100644 packages/console/src/pages/ApplicationDetails/ApplicationDetailsContent/Branding/LogoUploader.tsx create mode 100644 packages/console/src/pages/ApplicationDetails/ApplicationDetailsContent/Branding/NonThirdPartyBrandingForm.tsx create mode 100644 packages/console/src/pages/ApplicationDetails/ApplicationDetailsContent/Branding/index.module.scss create mode 100644 packages/console/src/pages/ApplicationDetails/ApplicationDetailsContent/CreateSecretModal.tsx create mode 100644 packages/console/src/pages/ApplicationDetails/ApplicationDetailsContent/EditSecretModal.tsx create mode 100644 packages/console/src/pages/ApplicationDetails/ApplicationDetailsContent/EndpointsAndCredentials/index.module.scss rename packages/console/src/pages/ApplicationDetails/ApplicationDetailsContent/{EndpointsAndCredentials.tsx => EndpointsAndCredentials/index.tsx} (55%) create mode 100644 packages/console/src/pages/ApplicationDetails/ApplicationDetailsContent/EndpointsAndCredentials/use-secret-table-columns.tsx create mode 100644 packages/console/src/pages/CustomizeJwt/UpsellNotice/index.module.scss create mode 100644 packages/console/src/pages/CustomizeJwt/UpsellNotice/index.tsx create mode 100644 packages/console/src/pages/Mfa/MfaForm/UpsellNotice/index.tsx create mode 100644 packages/console/src/pages/Organizations/CreateOrganizationModal/index.module.scss delete mode 100644 packages/console/src/pages/SignInExperience/PageContent/Branding/BrandingForm/LogoAndFaviconUploader/index.tsx delete mode 100644 packages/console/src/pages/SignInExperience/PageContent/Branding/CustomCssForm/index.tsx rename packages/console/src/pages/SignInExperience/PageContent/Branding/{CustomCssForm => CustomUiForm}/index.module.scss (63%) create mode 100644 packages/console/src/pages/SignInExperience/PageContent/Branding/CustomUiForm/index.tsx create mode 100644 packages/console/src/pages/SignInExperience/components/CustomUiAssetsUploader/index.module.scss create mode 100644 packages/console/src/pages/SignInExperience/components/CustomUiAssetsUploader/index.tsx create mode 100644 packages/console/src/pages/SignInExperience/contexts/SignInExperienceContextProvider/index.tsx create mode 100644 packages/console/src/pages/TenantSettings/Subscription/DowngradeConfirmModalContent/PlanQuotaDiffCard/PlanQuotaList/DiffQuotaItem/SkuQuotaItemPhrase.tsx create mode 100644 packages/console/src/types/sign-in-experience.ts create mode 100644 packages/console/src/types/skus.ts create mode 100644 packages/console/src/utils/object.ts delete mode 100644 packages/console/svgo.config.json create mode 100644 packages/console/vite.config.ts delete mode 100644 packages/core/nodemon.json create mode 100644 packages/core/src/middleware/koa-app-secret-transpilation.test.ts create mode 100644 packages/core/src/middleware/koa-app-secret-transpilation.ts create mode 100644 packages/core/src/middleware/koa-experience-ssr.test.ts create mode 100644 packages/core/src/middleware/koa-experience-ssr.ts rename packages/core/src/{routes/interaction => }/middleware/koa-interaction-details.ts (100%) create mode 100644 packages/core/src/middleware/koa-serve-custom-ui-assets.test.ts create mode 100644 packages/core/src/middleware/koa-serve-custom-ui-assets.ts create mode 100644 packages/core/src/oidc/grants/token-exchange/actor-token.test.ts create mode 100644 packages/core/src/oidc/grants/token-exchange/actor-token.ts rename packages/core/src/oidc/grants/{token-exchange.test.ts => token-exchange/index.test.ts} (95%) rename packages/core/src/oidc/grants/{token-exchange.ts => token-exchange/index.ts} (82%) create mode 100644 packages/core/src/oidc/grants/token-exchange/types.ts create mode 100644 packages/core/src/oidc/grants/utils.ts create mode 100644 packages/core/src/queries/application-secrets.ts create mode 100644 packages/core/src/routes/applications/application-custom-data.openapi.json create mode 100644 packages/core/src/routes/applications/application-custom-data.ts create mode 100644 packages/core/src/routes/applications/application-secret.openapi.json create mode 100644 packages/core/src/routes/applications/application-secret.ts create mode 100644 packages/core/src/routes/experience/classes/experience-interaction.test.ts create mode 100644 packages/core/src/routes/experience/classes/helpers.ts create mode 100644 packages/core/src/routes/experience/classes/libraries/mfa-validator.ts create mode 100644 packages/core/src/routes/experience/classes/libraries/password-validator.ts create mode 100644 packages/core/src/routes/experience/classes/libraries/profile-validator.ts create mode 100644 packages/core/src/routes/experience/classes/libraries/provision-library.ts create mode 100644 packages/core/src/routes/experience/classes/libraries/sentinel-guard.ts create mode 100644 packages/core/src/routes/experience/classes/libraries/sign-in-experience-validator.test.ts create mode 100644 packages/core/src/routes/experience/classes/libraries/sign-in-experience-validator.ts create mode 100644 packages/core/src/routes/experience/classes/mfa.ts create mode 100644 packages/core/src/routes/experience/classes/profile.ts create mode 100644 packages/core/src/routes/experience/classes/utils.test.ts create mode 100644 packages/core/src/routes/experience/classes/utils.ts create mode 100644 packages/core/src/routes/experience/classes/verifications/backup-code-verification.ts create mode 100644 packages/core/src/routes/experience/classes/verifications/enterprise-sso-verification.ts create mode 100644 packages/core/src/routes/experience/classes/verifications/new-password-identity-verification.ts create mode 100644 packages/core/src/routes/experience/classes/verifications/totp-verification.ts create mode 100644 packages/core/src/routes/experience/classes/verifications/verification-records-map.ts create mode 100644 packages/core/src/routes/experience/classes/verifications/web-authn-verification.ts create mode 100644 packages/core/src/routes/experience/middleware/koa-experience-interaction-hooks.ts create mode 100644 packages/core/src/routes/experience/middleware/koa-experience-verifications-audit-log.ts create mode 100644 packages/core/src/routes/experience/profile-routes.ts delete mode 100644 packages/core/src/routes/experience/utils.ts create mode 100644 packages/core/src/routes/experience/verification-routes/backup-code-verification.ts create mode 100644 packages/core/src/routes/experience/verification-routes/enterprise-sso-verification.ts create mode 100644 packages/core/src/routes/experience/verification-routes/new-password-identity-verification.ts create mode 100644 packages/core/src/routes/experience/verification-routes/totp-verification.ts create mode 100644 packages/core/src/routes/experience/verification-routes/web-authn-verification.ts create mode 100644 packages/core/src/routes/sign-in-experience/custom-ui-assets/index.openapi.json create mode 100644 packages/core/src/routes/sign-in-experience/custom-ui-assets/index.test.ts create mode 100644 packages/core/src/routes/sign-in-experience/custom-ui-assets/index.ts rename packages/core/src/routes/{security/index.openapi.json => subject-token.openapi.json} (82%) rename packages/core/src/routes/{security/index.ts => subject-token.ts} (73%) create mode 100644 packages/core/src/utils/file.test.ts create mode 100644 packages/core/src/utils/file.ts delete mode 100644 packages/core/src/utils/storage/consts.ts delete mode 100644 packages/core/tsconfig.base.json delete mode 100644 packages/core/tsconfig.build.json create mode 100644 packages/core/tsup.config.ts create mode 100644 packages/core/tsup.dev.config.ts delete mode 100644 packages/demo-app/.parcelrc.arm64 rename packages/demo-app/{src => }/index.html (55%) create mode 100644 packages/demo-app/src/include.d/vite-end.d.ts create mode 100644 packages/demo-app/vite.config.ts create mode 100644 packages/elements/.gitignore create mode 100644 packages/elements/README.md create mode 100644 packages/elements/index.html create mode 100644 packages/elements/lit-localize.json create mode 100644 packages/elements/package.json create mode 100644 packages/elements/src/components/logto-avatar.ts create mode 100644 packages/elements/src/components/logto-button.styles.ts create mode 100644 packages/elements/src/components/logto-button.ts create mode 100644 packages/elements/src/components/logto-card-section.ts create mode 100644 packages/elements/src/components/logto-card.ts create mode 100644 packages/elements/src/components/logto-form-card.ts create mode 100644 packages/elements/src/components/logto-icon-button.ts create mode 100644 packages/elements/src/components/logto-list-row.ts create mode 100644 packages/elements/src/components/logto-list.ts create mode 100644 packages/elements/src/components/logto-modal-layout.ts create mode 100644 packages/elements/src/components/logto-modal.context.ts create mode 100644 packages/elements/src/components/logto-modal.ts create mode 100644 packages/elements/src/components/logto-text-input.ts create mode 100644 packages/elements/src/elements/logto-profile-card.ts create mode 100644 packages/elements/src/icons/close.svg create mode 100644 packages/elements/src/icons/index.d.ts create mode 100644 packages/elements/src/index.ts create mode 100644 packages/elements/src/phrases/index.ts create mode 100644 packages/elements/src/providers/logto-theme-provider.ts create mode 100644 packages/elements/src/providers/logto-user-provider.ts create mode 100644 packages/elements/src/react.ts create mode 100644 packages/elements/src/utils/api.ts create mode 100644 packages/elements/src/utils/css.ts create mode 100644 packages/elements/src/utils/locale.ts create mode 100644 packages/elements/src/utils/string.ts create mode 100644 packages/elements/src/utils/theme.ts create mode 100644 packages/elements/tsconfig.json create mode 100644 packages/elements/tsup.config.ts create mode 100644 packages/elements/web-dev-server.config.js create mode 100644 packages/elements/xliff/de.xlf create mode 100644 packages/elements/xliff/es.xlf create mode 100644 packages/elements/xliff/fr.xlf create mode 100644 packages/elements/xliff/it.xlf create mode 100644 packages/elements/xliff/ja.xlf create mode 100644 packages/elements/xliff/ko.xlf create mode 100644 packages/elements/xliff/pl-PL.xlf create mode 100644 packages/elements/xliff/pt-BR.xlf create mode 100644 packages/elements/xliff/pt-PT.xlf create mode 100644 packages/elements/xliff/ru.xlf create mode 100644 packages/elements/xliff/tr-TR.xlf create mode 100644 packages/elements/xliff/zh-CN.xlf create mode 100644 packages/elements/xliff/zh-HK.xlf create mode 100644 packages/elements/xliff/zh-TW.xlf create mode 100644 packages/experience/.eslintrc.cjs delete mode 100644 packages/experience/.parcelrc delete mode 100644 packages/experience/.parcelrc.arm64 create mode 100644 packages/experience/index.html create mode 100644 packages/experience/src/assets/icons/loading-ring.svg create mode 100644 packages/experience/src/components/Button/RotatingRingIcon.module.scss create mode 100644 packages/experience/src/components/Button/RotatingRingIcon.tsx create mode 100644 packages/experience/src/components/InputFields/InputField/NotchedBorder/index.module.scss create mode 100644 packages/experience/src/components/InputFields/InputField/NotchedBorder/index.tsx create mode 100644 packages/experience/src/components/LoadingMask/index.module.scss create mode 100644 packages/experience/src/components/LoadingMask/index.tsx delete mode 100644 packages/experience/src/constants/env.ts create mode 100644 packages/experience/src/containers/SetPassword/HiddenIdentifierInput.tsx delete mode 100644 packages/experience/src/include.d/react-app.d.ts create mode 100644 packages/experience/src/include.d/vite-env.d.ts delete mode 100644 packages/experience/src/index.html create mode 100644 packages/experience/vite.config.ts create mode 100644 packages/integration-tests/src/helpers/experience/enterprise-sso-verification.ts create mode 100644 packages/integration-tests/src/helpers/experience/totp-verification.ts create mode 100644 packages/integration-tests/src/include.d/global.d.ts create mode 100644 packages/integration-tests/src/tests/api/application/application-custom-data.test.ts create mode 100644 packages/integration-tests/src/tests/api/application/application.secrets.test.ts create mode 100644 packages/integration-tests/src/tests/api/experience-api/bind-mfa/happpy-path.test.ts create mode 100644 packages/integration-tests/src/tests/api/experience-api/bind-mfa/sad-path.test.ts create mode 100644 packages/integration-tests/src/tests/api/experience-api/interaction.test.ts create mode 100644 packages/integration-tests/src/tests/api/experience-api/profile/fulfill-user-profiles.test.ts create mode 100644 packages/integration-tests/src/tests/api/experience-api/profile/reset-password.test.ts create mode 100644 packages/integration-tests/src/tests/api/experience-api/register-interaction/organization-jti.test.ts create mode 100644 packages/integration-tests/src/tests/api/experience-api/register-interaction/username-password.test.ts create mode 100644 packages/integration-tests/src/tests/api/experience-api/register-interaction/verification-code.test.ts create mode 100644 packages/integration-tests/src/tests/api/experience-api/sign-in-interaction/enterprise-sso.test.ts create mode 100644 packages/integration-tests/src/tests/api/experience-api/sign-in-interaction/mfa-verification.test.ts create mode 100644 packages/integration-tests/src/tests/api/experience-api/sign-in-interaction/social.test.ts create mode 100644 packages/integration-tests/src/tests/api/experience-api/verifications/backup-code-verification.test.ts create mode 100644 packages/integration-tests/src/tests/api/experience-api/verifications/enterprise-sso-verification.test.ts create mode 100644 packages/integration-tests/src/tests/api/experience-api/verifications/new-password-identity-verification.test.ts create mode 100644 packages/integration-tests/src/tests/api/experience-api/verifications/totp-verification.test.ts create mode 100644 packages/integration-tests/src/tests/api/hook/hook.trigger.experience.test.ts create mode 100644 packages/integration-tests/src/tests/api/oidc/client-authentication.test.ts create mode 100644 packages/integration-tests/src/tests/console/error-handling.test.ts create mode 100644 packages/integration-tests/src/tests/experience/overrides.test.ts create mode 100644 packages/integration-tests/src/tests/experience/server-side-rendering.test.ts create mode 100644 packages/integration-tests/src/ui-helpers/trace.ts create mode 100644 packages/integration-tests/tsup.config.ts create mode 100644 packages/phrases/src/locales/de/translation/admin-console/upsell/add-on.ts create mode 100644 packages/phrases/src/locales/en/translation/admin-console/subscription/usage.ts create mode 100644 packages/phrases/src/locales/en/translation/admin-console/upsell/add-on.ts create mode 100644 packages/phrases/src/locales/es/translation/admin-console/upsell/add-on.ts create mode 100644 packages/phrases/src/locales/fr/translation/admin-console/upsell/add-on.ts create mode 100644 packages/phrases/src/locales/it/translation/admin-console/upsell/add-on.ts create mode 100644 packages/phrases/src/locales/ja/translation/admin-console/upsell/add-on.ts create mode 100644 packages/phrases/src/locales/ko/translation/admin-console/upsell/add-on.ts create mode 100644 packages/phrases/src/locales/pl-pl/translation/admin-console/upsell/add-on.ts create mode 100644 packages/phrases/src/locales/pt-br/translation/admin-console/upsell/add-on.ts create mode 100644 packages/phrases/src/locales/pt-pt/translation/admin-console/upsell/add-on.ts create mode 100644 packages/phrases/src/locales/ru/translation/admin-console/upsell/add-on.ts create mode 100644 packages/phrases/src/locales/tr-tr/translation/admin-console/upsell/add-on.ts create mode 100644 packages/phrases/src/locales/zh-cn/translation/admin-console/upsell/add-on.ts create mode 100644 packages/phrases/src/locales/zh-hk/translation/admin-console/upsell/add-on.ts create mode 100644 packages/phrases/src/locales/zh-tw/translation/admin-console/upsell/add-on.ts create mode 100644 packages/schemas/alterations/1.19.0-1720253939-add-organization-branding.ts create mode 100644 packages/schemas/alterations/1.19.0-1720345784-add-color-to-app-sie.ts create mode 100644 packages/schemas/alterations/1.19.0-1720505152-update-custom-ui-assets.ts create mode 100644 packages/schemas/alterations/1.19.0-1721483240-multiple-app-secrets.ts create mode 100644 packages/schemas/alterations/1.19.0-1721645392-add-application-custom-data-column.ts create mode 100644 packages/schemas/alterations/1.19.0-1722926389-argon2d-argon2id.ts create mode 100644 packages/schemas/src/types/ssr.ts create mode 100644 packages/schemas/src/utils/application.ts create mode 100644 packages/schemas/tables/application_secrets.sql delete mode 100644 packages/toolkit/core-kit/declaration/react-app.d.ts create mode 100644 tsup.shared.config.ts create mode 100644 vite.shared.config.ts diff --git a/.dockerignore b/.dockerignore index 8b6ac882710..26fc4427468 100644 --- a/.dockerignore +++ b/.dockerignore @@ -33,5 +33,4 @@ dump.rdb .devcontainer .github .husky -.parcel-cache .vscode \ No newline at end of file diff --git a/.github/workflows/alteration-compatibility-integration-test.yml b/.github/workflows/alteration-compatibility-integration-test.yml index 4e82e5485c7..27d1454786c 100644 --- a/.github/workflows/alteration-compatibility-integration-test.yml +++ b/.github/workflows/alteration-compatibility-integration-test.yml @@ -91,3 +91,11 @@ jobs: GH_TOKEN: ${{ github.token }} GH_DEBUG: api run: gh workflow run rerun.yml -F run_id=${{ github.run_id }} + + alteration-compatibility-conclusion: + needs: run-logto + runs-on: ubuntu-latest + if: always() && (needs.run-logto.result == 'success' || needs.run-logto.result == 'skipped') + steps: + - name: Conclusion + run: echo "Alteration compatibility integration test completed successfully" diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8e51aadc1d9..1afaf44a0dc 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -20,12 +20,25 @@ jobs: - name: Setup Node and pnpm uses: silverhand-io/actions-node-pnpm-run-steps@v5 - with: - pnpm-version: 9 - name: Build run: pnpm ci:build + main-check: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Setup Node and pnpm + uses: silverhand-io/actions-node-pnpm-run-steps@v5 + + - name: Prepack + run: pnpm prepack + + - name: Check + run: pnpm -r check + main-lint: runs-on: ubuntu-latest @@ -34,8 +47,6 @@ jobs: - name: Setup Node and pnpm uses: silverhand-io/actions-node-pnpm-run-steps@v5 - with: - pnpm-version: 9 - name: Prepack run: pnpm prepack @@ -54,8 +65,6 @@ jobs: - name: Setup Node and pnpm uses: silverhand-io/actions-node-pnpm-run-steps@v5 - with: - pnpm-version: 9 - name: Build for test run: pnpm -r build:test @@ -122,7 +131,6 @@ jobs: - name: Setup Node and pnpm uses: silverhand-io/actions-node-pnpm-run-steps@v5 with: - pnpm-version: 9 run-install: false # ** Prepack packages ** diff --git a/.npmrc b/.npmrc deleted file mode 100644 index 573e44e096e..00000000000 --- a/.npmrc +++ /dev/null @@ -1,6 +0,0 @@ -# Hoist for Parcel -public-hoist-pattern[]=@parcel/* -public-hoist-pattern[]=postcss -public-hoist-pattern[]=process -public-hoist-pattern[]=*eslint* -public-hoist-pattern[]=buffer diff --git a/.scripts/compare-database.js b/.scripts/compare-database.js index b959db38533..f7afeaab692 100644 --- a/.scripts/compare-database.js +++ b/.scripts/compare-database.js @@ -42,14 +42,14 @@ const queryDatabaseManifest = async (database) => { `); const { rows: constraints } = await pool.query(/* sql */` - select conrelid::regclass AS table, con.*, pg_get_constraintdef(con.oid) + select conrelid::regclass as r_table, con.*, pg_get_constraintdef(con.oid) as def from pg_catalog.pg_constraint con inner join pg_catalog.pg_class rel on rel.oid = con.conrelid inner join pg_catalog.pg_namespace nsp on nsp.oid = connamespace where nsp.nspname = 'public' - order by conname asc; + order by conname asc, def asc; `); const { rows: indexes } = await pool.query(/* sql */` diff --git a/.scripts/package.sh b/.scripts/package.sh index c7654930ab8..7e2720b1840 100755 --- a/.scripts/package.sh +++ b/.scripts/package.sh @@ -18,7 +18,7 @@ fi # Some node packages use `src` as their dist folder, so ignore them from the rm list in the end find \ -.git .changeset .devcontainer .github .husky .parcel-cache .scripts .vscode pnpm-*.yaml *.js \ +.git .changeset .devcontainer .github .husky .scripts .vscode pnpm-*.yaml *.js \ packages/**/src \ packages/**/*.config.js packages/**/*.config.ts packages/**/tsconfig*.json \ ! -path '**/node_modules/**' \ diff --git a/.vscode/settings.json b/.vscode/settings.json index a647d0cd09f..9df9bb7b150 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -52,7 +52,7 @@ "topbar", "upsell", "withtyped", - "backchannel" - ], - "eslint.codeActionsOnSave.rules": null -} + "backchannel", + "deepmerge" + ] +} \ No newline at end of file diff --git a/.vscode/tsx.code-snippets b/.vscode/tsx.code-snippets index 2f1caf11c1b..be2b52bab50 100644 --- a/.vscode/tsx.code-snippets +++ b/.vscode/tsx.code-snippets @@ -10,7 +10,7 @@ "scope": "javascriptreact,typescriptreact", "prefix": "isc", "body": [ - "import * as styles from './index.module.scss';", + "import styles from './index.module.scss';", "$0" ], "description": "Import SCSS styles from the same directory." diff --git a/AWESOME.md b/AWESOME.md new file mode 100644 index 00000000000..5b4a0dfb104 --- /dev/null +++ b/AWESOME.md @@ -0,0 +1,11 @@ +# Logto awesome + +Here's the list of awesome community-contributed resources for Logto. Feel free to add yours by submitting a pull request. + +## Account + +- [Logto Account Dashboard](https://github.com/t2vee/Logto-Account-Dashboard) by @t2vee + +## API + +- [Go API client for logto](https://github.com/mostafa/go-api-client) by @mostafa diff --git a/Dockerfile b/Dockerfile index d01d28175ab..18ac74d02ca 100644 --- a/Dockerfile +++ b/Dockerfile @@ -19,7 +19,6 @@ RUN apk add --no-cache python3 make g++ rsync COPY . . ### Install dependencies and build ### -RUN node .scripts/update-parcelrc.js RUN pnpm i ### Set if dev features enabled ### @@ -40,7 +39,7 @@ RUN rm -rf node_modules packages/**/node_modules RUN NODE_ENV=production pnpm i ### Clean up ### -RUN rm -rf .scripts .parcel-cache pnpm-*.yaml packages/cloud +RUN rm -rf .scripts pnpm-*.yaml packages/cloud ###### [STAGE] Seal ###### FROM node:20-alpine as app diff --git a/README.md b/README.md index 35d06147178..889aa2d71a3 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ Logto[^info] is an Auth0 alternative designed for modern apps and SaaS products. - Enables OIDC-based authentication with Logto SDKs. - Supports passwordless sign-in, along with various options like email, phone number, username, Google, Facebook, and other social sign-in methods. - Offers beautiful UI components with customizable CSS to suit your business needs. +- Has an open community with many warm-hearted contributors and users. Check out our [awesome list](./AWESOME.md) of community-contributed resources. 📦 **Out-of-the-box infrastructure** @@ -37,12 +38,12 @@ Logto[^info] is an Auth0 alternative designed for modern apps and SaaS products. - Implements role-based access control (RBAC) for scalable role authorization, catering to a wide range of use cases. - Facilitates user management and provides audit logs for understanding identity-related user information and maintaining security. - Enables single sign-on (SSO) and multi-factor authentication (MFA) without extra coding. -- Leverages Logto Organizations to build multi-tenancy apps with ease. +- Leverages Logto organizations to build multi-tenancy apps with ease. In a more approachable way, we refer to this solution as "[Customer Identity Access Management (CIAM)](https://en.wikipedia.org/wiki/Customer_identity_access_management)" or simply, the "Customer Identity Solution." -[Subscribe to us](https://logto.io/subscribe/?utm_source=github&utm_medium=repo_logto) now to stay updated with the latest information about the Logto Cloud (SaaS) and receive feature updates in real-time. - +> [!IMPORTANT] +> [Subscribe to us](https://logto.io/subscribe/?utm_source=github&utm_medium=repo_logto) now to stay updated with the latest information about the Logto Cloud (SaaS) and receive feature updates in real-time. ## Get started diff --git a/commitlint.config.ts b/commitlint.config.ts index 5f44c75e5e8..c89743462dc 100644 --- a/commitlint.config.ts +++ b/commitlint.config.ts @@ -7,7 +7,7 @@ const config: UserConfig = { extends: ['@commitlint/config-conventional'], rules: { 'type-enum': [2, 'always', [...conventional.rules['type-enum'][2], 'api', 'release']], - 'scope-enum': [2, 'always', ['connector', 'console', 'core', 'demo-app', 'test', 'phrases', 'schemas', 'shared', 'experience', 'deps', 'deps-dev', 'cli', 'toolkit', 'cloud', 'app-insights']], + 'scope-enum': [2, 'always', ['connector', 'console', 'core', 'demo-app', 'test', 'phrases', 'schemas', 'shared', 'experience', 'deps', 'deps-dev', 'cli', 'toolkit', 'cloud', 'app-insights', 'elements']], // Slightly increase the tolerance to allow the appending PR number ...(isCi && { 'header-max-length': [2, 'always', 110] }), 'body-max-line-length': [2, 'always', 110], diff --git a/package.json b/package.json index 28ff7ea7f52..527d8d94110 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,9 @@ "@types/pg": "^8.6.6", "husky": "^9.0.0", "pg": "^8.8.0", - "typescript": "^5.0.0" + "tsup": "^8.1.0", + "typescript": "^5.0.0", + "vite": "^5.3.4" }, "engines": { "node": "^20.9.0", @@ -52,9 +54,5 @@ }, "dependencies": { "@logto/cli": "workspace:^1.1.0" - }, - "//": "@see https://parceljs.org/features/dependency-resolution/#package-exports", - "@parcel/resolver-default": { - "packageExports": true } } diff --git a/packages/app-insights/package.json b/packages/app-insights/package.json index 8016dc02794..9591063d8af 100644 --- a/packages/app-insights/package.json +++ b/packages/app-insights/package.json @@ -32,12 +32,12 @@ "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.9.5", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.0", "prettier": "^3.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "typescript": "^5.5.3", + "vitest": "^2.0.0" }, "engines": { "node": "^20.9.0" diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index c0cf24da4bb..3dfb5f2e8fc 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,53 @@ # Change Log +## 1.19.0 + +### Minor Changes + +- 2d0502a42: add new cli command to setup proxy for developing and debugging custom ui locally + + This command will establish a proxy tunnel between the following 3 entities together: your Logto cloud auth services, your application, and your custom sign-in UI. + + Assuming you have a custom sign-in page running on `http://localhost:4000`. + Then you can execute the command this way: + + ```bash + npm cli proxy --endpoint https://.logto.app --port 9000 --experience-uri http://localhost:4000 + ``` + + Or if you don't have your custom UI pages hosted on a dev server, you can use the `--experience-path` option to specify the path to your static files: + + ```bash + npm cli proxy --endpoint https://.logto.app --port 9000 --experience-path /path/to/your/custom/ui + ``` + + This command also works if you have enabled custom domain in your Logto tenant. E.g.: + + ```bash + npm cli proxy --endpoint https://your-custom-domain.com --port 9000 --experience-path /path/to/your/custom/ui + ``` + + This should set up the proxy and it will be running on your local machine at `http://localhost:9000/`. + + Finally, run your application and set its Logto endpoint to the proxy address `http://localhost:9000/` instead. + + If all set up correctly, when you click the "sign-in" button in your application, you should be navigated to your custom sign-in page instead of Logto's built-in UI, along with valid session (cookies) that allows you to further interact with Logto experience API. + + Happy coding! + +### Patch Changes + +- Updated dependencies [6477c6dee] +- Updated dependencies [3a839f6d6] +- Updated dependencies [b91ec0cd6] +- Updated dependencies [d203c8d2f] +- Updated dependencies [b188bb161] +- Updated dependencies [62f5e5e0c] +- Updated dependencies [d56bc2f73] +- Updated dependencies [510f681fa] + - @logto/schemas@1.19.0 + - @logto/phrases@1.13.0 + ## 1.18.0 ### Patch Changes diff --git a/packages/cli/package.json b/packages/cli/package.json index 21793fdb2e4..10c7ea85d7a 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@logto/cli", - "version": "1.18.0", + "version": "1.19.0", "description": "Logto CLI.", "author": "Silverhand Inc. ", "homepage": "https://github.com/logto-io/logto#readme", @@ -45,30 +45,32 @@ "@logto/connector-kit": "workspace:^4.0.0", "@logto/core-kit": "workspace:^2.5.0", "@logto/language-kit": "workspace:^1.1.0", - "@logto/phrases": "workspace:^1.12.0", + "@logto/phrases": "workspace:^1.13.0", "@logto/phrases-experience": "workspace:^1.7.0", - "@logto/schemas": "workspace:1.18.0", + "@logto/schemas": "workspace:1.19.0", "@logto/shared": "workspace:^3.1.1", "@silverhand/essentials": "^2.9.1", "@silverhand/slonik": "31.0.0-beta.2", - "chalk": "^5.0.0", + "chalk": "^5.3.0", "decamelize": "^6.0.0", - "dotenv": "^16.0.0", + "dotenv": "^16.4.5", "got": "^14.0.0", "hpagent": "^1.2.0", + "http-proxy-middleware": "^3.0.0", "inquirer": "^9.0.0", + "mime": "^4.0.4", "nanoid": "^5.0.1", "ora": "^8.0.1", - "p-limit": "^5.0.0", + "p-limit": "^6.0.0", "p-queue": "^8.0.0", "p-retry": "^6.0.0", "pg-protocol": "^1.6.0", "roarr": "^7.11.0", "semver": "^7.3.8", "tar": "^7.0.0", - "typescript": "^5.3.3", + "typescript": "^5.5.3", "yargs": "^17.6.0", - "zod": "^3.22.4" + "zod": "^3.23.8" }, "devDependencies": { "@silverhand/eslint-config": "6.0.1", @@ -79,13 +81,13 @@ "@types/sinon": "^17.0.0", "@types/tar": "^6.1.12", "@types/yargs": "^17.0.13", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "@withtyped/server": "^0.13.6", "eslint": "^8.56.0", "lint-staged": "^15.0.0", "prettier": "^3.0.0", "sinon": "^18.0.0", - "vitest": "^1.4.0" + "vitest": "^2.0.0" }, "eslintConfig": { "extends": "@silverhand", diff --git a/packages/cli/src/commands/proxy/index.ts b/packages/cli/src/commands/proxy/index.ts new file mode 100644 index 00000000000..a12428b3ef3 --- /dev/null +++ b/packages/cli/src/commands/proxy/index.ts @@ -0,0 +1,106 @@ +import http from 'node:http'; + +import { isValidUrl } from '@logto/core-kit'; +import { conditional } from '@silverhand/essentials'; +import chalk from 'chalk'; +import type { CommandModule } from 'yargs'; + +import { consoleLog } from '../../utils.js'; + +import { type ProxyCommandArgs } from './types.js'; +import { + checkExperienceInput, + createLogtoResponseHandler, + createProxy, + createStaticFileProxy, + isLogtoRequestPath, +} from './utils.js'; + +const proxy: CommandModule = { + command: ['proxy'], + describe: 'Command for Logto proxy', + builder: (yargs) => + yargs + .options({ + 'experience-uri': { + alias: ['x'], + describe: 'The URI of your custom sign-in experience page.', + type: 'string', + }, + 'experience-path': { + alias: ['xp'], + describe: 'The local folder path of your custom sign-in experience assets.', + type: 'string', + }, + endpoint: { + alias: 'ep', + describe: + 'Logto endpoint URI, which can be found in Logto Console. E.g.: https://.logto.app/', + type: 'string', + }, + port: { + alias: 'p', + describe: 'The port number where the proxy server will be running on. Defaults to 9000.', + type: 'number', + default: 9000, + }, + verbose: { + alias: 'v', + describe: 'Show verbose output.', + type: 'boolean', + default: false, + }, + }) + .global('e'), + handler: async ({ 'experience-uri': url, 'experience-path': path, endpoint, port, verbose }) => { + checkExperienceInput(url, path); + + if (!endpoint || !isValidUrl(endpoint)) { + consoleLog.fatal('A valid Logto endpoint URI must be provided.'); + } + const logtoEndpointUrl = new URL(endpoint); + const proxyUrl = new URL(`http://localhost:${port}`); + + const proxyLogtoRequest = createProxy( + logtoEndpointUrl.href, + async (proxyResponse, request, response) => + createLogtoResponseHandler({ + proxyResponse, + request, + response, + logtoEndpointUrl, + proxyUrl, + verbose, + }) + ); + const proxyExperienceServerRequest = conditional(url && createProxy(url)); + const proxyExperienceStaticFileRequest = conditional(path && createStaticFileProxy(path)); + + const server = http.createServer((request, response) => { + if (verbose) { + consoleLog.info(`Incoming request: ${chalk.blue(request.method, request.url)}`); + } + + // Proxy the requests to Logto endpoint + if (isLogtoRequestPath(request.url)) { + void proxyLogtoRequest(request, response); + return; + } + + if (proxyExperienceServerRequest) { + void proxyExperienceServerRequest(request, response); + return; + } + + if (proxyExperienceStaticFileRequest) { + void proxyExperienceStaticFileRequest(request, response); + } + }); + + server.listen(port, () => { + consoleLog.info(`Proxy server is running on ${chalk.blue(proxyUrl.href)}`); + }); + }, +}; + +export default proxy; diff --git a/packages/cli/src/commands/proxy/types.ts b/packages/cli/src/commands/proxy/types.ts new file mode 100644 index 00000000000..af80b479254 --- /dev/null +++ b/packages/cli/src/commands/proxy/types.ts @@ -0,0 +1,18 @@ +import type * as http from 'node:http'; + +export type ProxyCommandArgs = { + 'experience-uri'?: string; + 'experience-path'?: string; + endpoint?: string; + port: number; + verbose: boolean; +}; + +export type ProxyResponseHandler = { + proxyResponse: http.IncomingMessage; + request: http.IncomingMessage; + response: http.ServerResponse; + logtoEndpointUrl: URL; + proxyUrl: URL; + verbose: boolean; +}; diff --git a/packages/cli/src/commands/proxy/utils.ts b/packages/cli/src/commands/proxy/utils.ts new file mode 100644 index 00000000000..28283be84c3 --- /dev/null +++ b/packages/cli/src/commands/proxy/utils.ts @@ -0,0 +1,157 @@ +import { existsSync } from 'node:fs'; +import fs from 'node:fs/promises'; +import type http from 'node:http'; +import path from 'node:path'; + +import { isValidUrl } from '@logto/core-kit'; +import { conditional, trySafe } from '@silverhand/essentials'; +import chalk from 'chalk'; +import { createProxyMiddleware, responseInterceptor } from 'http-proxy-middleware'; +import { type OnProxyEvent } from 'http-proxy-middleware/dist/types.js'; +import mime from 'mime'; + +import { consoleLog } from '../../utils.js'; + +import { type ProxyResponseHandler } from './types.js'; + +export const createProxy = (targetUrl: string, onProxyResponse?: OnProxyEvent['proxyRes']) => { + const hasResponseHandler = Boolean(onProxyResponse); + return createProxyMiddleware({ + target: targetUrl, + changeOrigin: true, + selfHandleResponse: hasResponseHandler, + ...conditional( + hasResponseHandler && { + on: { + proxyRes: onProxyResponse, + error: (error) => { + consoleLog.error(chalk.red(error)); + }, + }, + } + ), + }); +}; + +const index = 'index.html'; +const indexContentType = 'text/html; charset=utf-8'; +const noCache = 'no-cache, no-store, must-revalidate'; +const maxAgeSevenDays = 'max-age=604_800_000'; + +export const createStaticFileProxy = + (staticPath: string) => async (request: http.IncomingMessage, response: http.ServerResponse) => { + if (!request.url) { + response.writeHead(400).end(); + return; + } + + if (request.method === 'HEAD' || request.method === 'GET') { + const fallBackToIndex = !isFileAssetPath(request.url); + const requestPath = path.join(staticPath, fallBackToIndex ? index : request.url); + try { + const content = await fs.readFile(requestPath, 'utf8'); + response.setHeader('cache-control', fallBackToIndex ? noCache : maxAgeSevenDays); + response.setHeader('content-type', getMimeType(request.url)); + response.writeHead(200); + response.end(content); + } catch (error: unknown) { + consoleLog.error(chalk.red(error)); + response.setHeader('content-type', getMimeType(request.url)); + response.writeHead(existsSync(request.url) ? 500 : 404); + response.end(); + } + } + }; + +/** + * Intercept the response from Logto endpoint and replace Logto endpoint URLs in the response with the + * proxy URL. The string replace happens in the following cases: + * - The response is a redirect response, and the `location` property in response header may contain Logto + * endpoint URI. + * - The response body is JSON, which consists of properties such as `**_endpoint` and `redirectTo`. These + * properties may contain Logto endpoint URI. + * - The response is HTML content that contains a form. The form action URL may contain Logto endpoint URI. + * + * Note: the `issuer` and `jwks_uri` properties in the `/oidc/.well-known` response should not be replaced, + * even they also contain the Logto endpoint URI. + */ +export const createLogtoResponseHandler = async ({ + proxyResponse, + request, + response, + logtoEndpointUrl, + proxyUrl, + verbose, +}: ProxyResponseHandler) => { + const { location } = proxyResponse.headers; + if (location) { + // eslint-disable-next-line @silverhand/fp/no-mutation + proxyResponse.headers.location = location.replace(logtoEndpointUrl.href, proxyUrl.href); + } + + void responseInterceptor(async (responseBuffer, proxyResponse) => { + const responseBody = responseBuffer.toString(); + if (verbose) { + consoleLog.info(`Response received: ${chalk.green(responseBody)}`); + } + + if (proxyResponse.headers['content-type']?.includes('text/html')) { + return responseBody.replace(`action="${logtoEndpointUrl.href}`, `action="${proxyUrl.href}`); + } + + if (proxyResponse.headers['content-type']?.includes('application/json')) { + const jsonData = trySafe(() => JSON.parse(responseBody)); + + if (jsonData && typeof jsonData === 'object') { + const updatedEntries: Array<[string, unknown]> = Object.entries(jsonData).map( + ([key, value]) => { + if ((key === 'redirectTo' || key.endsWith('_endpoint')) && typeof value === 'string') { + return [key, value.replace(logtoEndpointUrl.href, proxyUrl.href)]; + } + return [key, value]; + } + ); + + return JSON.stringify(Object.fromEntries(updatedEntries)); + } + } + return responseBody; + })(proxyResponse, request, response); +}; + +export const checkExperienceInput = (url?: string, staticPath?: string) => { + if (staticPath && url) { + consoleLog.fatal('Only one of the experience URI or path can be provided.'); + } + if (!staticPath && !url) { + consoleLog.fatal('Either a sign-in experience URI or local path must be provided.'); + } + if (url && !isValidUrl(url)) { + consoleLog.fatal( + 'A valid sign-in experience URI must be provided. E.g.: http://localhost:4000' + ); + } + if (staticPath && !existsSync(path.join(staticPath, index))) { + consoleLog.fatal('The provided path does not contain a valid index.html file.'); + } +}; + +/** + * Check if the request path is a Logto request path. + * @example isLogtoRequestPath('/oidc/.well-known/openid-configuration') // true + * @example isLogtoRequestPath('/oidc/auth') // true + * @example isLogtoRequestPath('/api/interaction/submit') // true + * @example isLogtoRequestPath('/consent') // true + */ +export const isLogtoRequestPath = (requestPath?: string) => + ['/oidc/', '/api/'].some((path) => requestPath?.startsWith(path)) || requestPath === '/consent'; + +const isFileAssetPath = (url: string) => url.split('/').at(-1)?.includes('.'); + +const getMimeType = (requestPath: string) => { + const fallBackToIndex = !isFileAssetPath(requestPath); + if (fallBackToIndex) { + return indexContentType; + } + return mime.getType(requestPath) ?? 'application/octet-stream'; +}; diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 13de2f79785..7afd7c9ec79 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -6,6 +6,7 @@ import { hideBin } from 'yargs/helpers'; import connector from './commands/connector/index.js'; import database from './commands/database/index.js'; import install from './commands/install/index.js'; +import proxy from './commands/proxy/index.js'; import translate from './commands/translate/index.js'; import { packageJson } from './package-json.js'; import { cliConfig, ConfigKey, consoleLog } from './utils.js'; @@ -48,6 +49,7 @@ void yargs(hideBin(process.argv)) .command(database) .command(connector) .command(translate) + .command(proxy) .demandCommand(1) .showHelpOnFail(false, `Specify ${chalk.green('--help')} for available options`) .strict() diff --git a/packages/connectors/.gitignore b/packages/connectors/.gitignore index 65245801929..533db69aa78 100644 --- a/packages/connectors/.gitignore +++ b/packages/connectors/.gitignore @@ -1,7 +1,6 @@ # generated files /*/tsconfig.* -/*/rollup.config.* -/*/vitest.config.* +/*/*.config.* # keep templates !/templates/** diff --git a/packages/connectors/connector-alipay-native/CHANGELOG.md b/packages/connectors/connector-alipay-native/CHANGELOG.md index f1876d9f116..827cf921a07 100644 --- a/packages/connectors/connector-alipay-native/CHANGELOG.md +++ b/packages/connectors/connector-alipay-native/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-alipay-native +## 1.3.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 1.2.1 ### Patch Changes diff --git a/packages/connectors/connector-alipay-native/package.json b/packages/connectors/connector-alipay-native/package.json index 7099bda0ce0..f9693c27791 100644 --- a/packages/connectors/connector-alipay-native/package.json +++ b/packages/connectors/connector-alipay-native/package.json @@ -1,6 +1,6 @@ { "name": "@logto/connector-alipay-native", - "version": "1.2.1", + "version": "1.3.0", "description": "Alipay Native implementation.", "author": "Silverhand Inc. ", "dependencies": { @@ -9,29 +9,24 @@ "dayjs": "^1.10.5", "got": "^14.0.0", "iconv-lite": "^0.6.3", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "devDependencies": { - "@rollup/plugin-commonjs": "^26.0.0", - "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^15.2.3", - "@rollup/plugin-typescript": "^11.1.6", "@shopify/jest-koa-mocks": "^5.0.0", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -46,9 +41,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", diff --git a/packages/connectors/connector-alipay-web/CHANGELOG.md b/packages/connectors/connector-alipay-web/CHANGELOG.md index 5ffe877b82d..afa9b87c27b 100644 --- a/packages/connectors/connector-alipay-web/CHANGELOG.md +++ b/packages/connectors/connector-alipay-web/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-alipay-web +## 1.4.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 1.3.1 ### Patch Changes diff --git a/packages/connectors/connector-alipay-web/package.json b/packages/connectors/connector-alipay-web/package.json index 10b5095b6b4..c567bb78973 100644 --- a/packages/connectors/connector-alipay-web/package.json +++ b/packages/connectors/connector-alipay-web/package.json @@ -1,6 +1,6 @@ { "name": "@logto/connector-alipay-web", - "version": "1.3.1", + "version": "1.4.0", "description": "Alipay implementation.", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", @@ -8,29 +8,24 @@ "dayjs": "^1.10.5", "got": "^14.0.0", "iconv-lite": "^0.6.3", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "devDependencies": { - "@rollup/plugin-commonjs": "^26.0.0", - "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^15.2.3", - "@rollup/plugin-typescript": "^11.1.6", "@shopify/jest-koa-mocks": "^5.0.0", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -45,9 +40,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", diff --git a/packages/connectors/connector-aliyun-dm/CHANGELOG.md b/packages/connectors/connector-aliyun-dm/CHANGELOG.md index 194f3b8132c..cf64e5b303d 100644 --- a/packages/connectors/connector-aliyun-dm/CHANGELOG.md +++ b/packages/connectors/connector-aliyun-dm/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-aliyun-dm +## 1.2.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 1.1.2 ### Patch Changes diff --git a/packages/connectors/connector-aliyun-dm/package.json b/packages/connectors/connector-aliyun-dm/package.json index 59e41ddd8e8..6e2d9901923 100644 --- a/packages/connectors/connector-aliyun-dm/package.json +++ b/packages/connectors/connector-aliyun-dm/package.json @@ -1,13 +1,13 @@ { "name": "@logto/connector-aliyun-dm", - "version": "1.1.2", + "version": "1.2.0", "description": "Aliyun DM connector implementation.", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -22,9 +22,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -51,23 +51,18 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^26.0.0", - "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^15.2.3", - "@rollup/plugin-typescript": "^11.1.6", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-aliyun-dm/src/single-send-mail.test.ts b/packages/connectors/connector-aliyun-dm/src/single-send-mail.test.ts index 1162b5d3210..cf4f13e33db 100644 --- a/packages/connectors/connector-aliyun-dm/src/single-send-mail.test.ts +++ b/packages/connectors/connector-aliyun-dm/src/single-send-mail.test.ts @@ -21,7 +21,6 @@ describe('singleSendMail', () => { }, '' ); - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const calledData = request.mock.calls[0]; expect(calledData).not.toBeUndefined(); // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment diff --git a/packages/connectors/connector-aliyun-dm/src/utils.test.ts b/packages/connectors/connector-aliyun-dm/src/utils.test.ts index 24da02e7a88..ac40456fe08 100644 --- a/packages/connectors/connector-aliyun-dm/src/utils.test.ts +++ b/packages/connectors/connector-aliyun-dm/src/utils.test.ts @@ -24,7 +24,6 @@ describe('request', () => { it('should call got.post with extended params', async () => { const parameters = mockedParameters; await request('http://test.endpoint.com', parameters, 'testsecret'); - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const calledData = post.mock.calls[0]; expect(calledData).not.toBeUndefined(); const payload = calledData?.[0].form as Record; diff --git a/packages/connectors/connector-aliyun-sms/CHANGELOG.md b/packages/connectors/connector-aliyun-sms/CHANGELOG.md index dc502f55bae..d8cf480ef0d 100644 --- a/packages/connectors/connector-aliyun-sms/CHANGELOG.md +++ b/packages/connectors/connector-aliyun-sms/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-aliyun-sms +## 1.2.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 1.1.2 ### Patch Changes diff --git a/packages/connectors/connector-aliyun-sms/package.json b/packages/connectors/connector-aliyun-sms/package.json index 9739f43eb75..bf66618112f 100644 --- a/packages/connectors/connector-aliyun-sms/package.json +++ b/packages/connectors/connector-aliyun-sms/package.json @@ -1,13 +1,13 @@ { "name": "@logto/connector-aliyun-sms", - "version": "1.1.2", + "version": "1.2.0", "description": "Aliyun SMS connector implementation.", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -22,9 +22,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -51,23 +51,18 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^26.0.0", - "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^15.2.3", - "@rollup/plugin-typescript": "^11.1.6", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-aliyun-sms/src/single-send-text.test.ts b/packages/connectors/connector-aliyun-sms/src/single-send-text.test.ts index ec7da921964..142628c87c6 100644 --- a/packages/connectors/connector-aliyun-sms/src/single-send-text.test.ts +++ b/packages/connectors/connector-aliyun-sms/src/single-send-text.test.ts @@ -20,7 +20,6 @@ describe('sendSms', () => { }, '' ); - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const calledData = request.mock.calls[0]; expect(calledData).not.toBeUndefined(); // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment diff --git a/packages/connectors/connector-aliyun-sms/src/utils.test.ts b/packages/connectors/connector-aliyun-sms/src/utils.test.ts index 24da02e7a88..ac40456fe08 100644 --- a/packages/connectors/connector-aliyun-sms/src/utils.test.ts +++ b/packages/connectors/connector-aliyun-sms/src/utils.test.ts @@ -24,7 +24,6 @@ describe('request', () => { it('should call got.post with extended params', async () => { const parameters = mockedParameters; await request('http://test.endpoint.com', parameters, 'testsecret'); - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const calledData = post.mock.calls[0]; expect(calledData).not.toBeUndefined(); const payload = calledData?.[0].form as Record; diff --git a/packages/connectors/connector-apple/CHANGELOG.md b/packages/connectors/connector-apple/CHANGELOG.md index 1e5544aa92e..3b7a9534c53 100644 --- a/packages/connectors/connector-apple/CHANGELOG.md +++ b/packages/connectors/connector-apple/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-apple +## 1.4.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 1.3.1 ### Patch Changes diff --git a/packages/connectors/connector-apple/package.json b/packages/connectors/connector-apple/package.json index 2c11467106b..3ddbbe02385 100644 --- a/packages/connectors/connector-apple/package.json +++ b/packages/connectors/connector-apple/package.json @@ -1,15 +1,15 @@ { "name": "@logto/connector-apple", - "version": "1.3.1", + "version": "1.4.0", "description": "Apple web connector implementation.", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", "@logto/shared": "workspace:^3.1.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", - "jose": "^5.0.0", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "jose": "^5.6.3", + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -24,9 +24,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -53,23 +53,18 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^26.0.0", - "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^15.2.3", - "@rollup/plugin-typescript": "^11.1.6", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-aws-ses/CHANGELOG.md b/packages/connectors/connector-aws-ses/CHANGELOG.md index c1be1d0c438..eaa603c95b1 100644 --- a/packages/connectors/connector-aws-ses/CHANGELOG.md +++ b/packages/connectors/connector-aws-ses/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-aws-ses +## 1.2.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 1.1.2 ### Patch Changes diff --git a/packages/connectors/connector-aws-ses/package.json b/packages/connectors/connector-aws-ses/package.json index 99f6582e302..9c9f32c0cb6 100644 --- a/packages/connectors/connector-aws-ses/package.json +++ b/packages/connectors/connector-aws-ses/package.json @@ -1,6 +1,6 @@ { "name": "@logto/connector-aws-ses", - "version": "1.1.2", + "version": "1.2.0", "description": "Logto Connector for Amazon SES", "author": "Jeff ", "dependencies": { @@ -9,8 +9,8 @@ "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -25,9 +25,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -54,23 +54,18 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^26.0.0", - "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^15.2.3", - "@rollup/plugin-typescript": "^11.1.6", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-azuread/CHANGELOG.md b/packages/connectors/connector-azuread/CHANGELOG.md index 6fd68879451..67bbedd0686 100644 --- a/packages/connectors/connector-azuread/CHANGELOG.md +++ b/packages/connectors/connector-azuread/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-azuread +## 1.4.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 1.3.0 ### Minor Changes diff --git a/packages/connectors/connector-azuread/package.json b/packages/connectors/connector-azuread/package.json index 773b2da57be..d95be30e90e 100644 --- a/packages/connectors/connector-azuread/package.json +++ b/packages/connectors/connector-azuread/package.json @@ -1,6 +1,6 @@ { "name": "@logto/connector-azuread", - "version": "1.3.0", + "version": "1.4.0", "description": "Microsoft Azure AD connector implementation.", "author": "Mobilist Inc. ", "dependencies": { @@ -8,8 +8,8 @@ "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -24,9 +24,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -53,23 +53,18 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^26.0.0", - "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^15.2.3", - "@rollup/plugin-typescript": "^11.1.6", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-dingtalk-web/CHANGELOG.md b/packages/connectors/connector-dingtalk-web/CHANGELOG.md index 61d3dd9d940..e00eb0f6673 100644 --- a/packages/connectors/connector-dingtalk-web/CHANGELOG.md +++ b/packages/connectors/connector-dingtalk-web/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-dingtalk-web +## 0.2.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 0.1.1 ### Patch Changes diff --git a/packages/connectors/connector-dingtalk-web/package.json b/packages/connectors/connector-dingtalk-web/package.json index d2815d24c29..b099ca2d0c5 100644 --- a/packages/connectors/connector-dingtalk-web/package.json +++ b/packages/connectors/connector-dingtalk-web/package.json @@ -1,6 +1,6 @@ { "name": "@logto/connector-dingtalk-web", - "version": "0.1.1", + "version": "0.2.0", "description": "Dingtalk web connector implementation.", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", @@ -8,29 +8,24 @@ "dayjs": "^1.10.5", "got": "^14.0.0", "iconv-lite": "^0.6.3", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "devDependencies": { - "@rollup/plugin-commonjs": "^26.0.0", - "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^15.2.3", - "@rollup/plugin-typescript": "^11.1.6", "@shopify/jest-koa-mocks": "^5.0.0", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -45,9 +40,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", diff --git a/packages/connectors/connector-discord/CHANGELOG.md b/packages/connectors/connector-discord/CHANGELOG.md index a321f0c4a49..f102353eefa 100644 --- a/packages/connectors/connector-discord/CHANGELOG.md +++ b/packages/connectors/connector-discord/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-discord +## 1.4.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 1.3.1 ### Patch Changes diff --git a/packages/connectors/connector-discord/package.json b/packages/connectors/connector-discord/package.json index eef708ea75b..0b7c856ddaf 100644 --- a/packages/connectors/connector-discord/package.json +++ b/packages/connectors/connector-discord/package.json @@ -1,14 +1,14 @@ { "name": "@logto/connector-discord", - "version": "1.3.1", + "version": "1.4.0", "description": "Discord connector implementation.", "author": "ZR3SYSTEMS. ", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -23,9 +23,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -52,23 +52,18 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^26.0.0", - "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^15.2.3", - "@rollup/plugin-typescript": "^11.1.6", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-facebook/CHANGELOG.md b/packages/connectors/connector-facebook/CHANGELOG.md index 094ebede4a5..90a43257788 100644 --- a/packages/connectors/connector-facebook/CHANGELOG.md +++ b/packages/connectors/connector-facebook/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-facebook +## 1.4.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 1.3.1 ### Patch Changes diff --git a/packages/connectors/connector-facebook/package.json b/packages/connectors/connector-facebook/package.json index 787adb612c6..823c7644e73 100644 --- a/packages/connectors/connector-facebook/package.json +++ b/packages/connectors/connector-facebook/package.json @@ -1,14 +1,14 @@ { "name": "@logto/connector-facebook", - "version": "1.3.1", + "version": "1.4.0", "description": "Facebook web connector implementation.", "author": "Silverhand Inc. ", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -23,9 +23,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -52,23 +52,18 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^26.0.0", - "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^15.2.3", - "@rollup/plugin-typescript": "^11.1.6", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-feishu-web/CHANGELOG.md b/packages/connectors/connector-feishu-web/CHANGELOG.md index 3d0f5dbfbf8..caeb8d362d9 100644 --- a/packages/connectors/connector-feishu-web/CHANGELOG.md +++ b/packages/connectors/connector-feishu-web/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-feishu-web +## 1.3.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 1.2.1 ### Patch Changes diff --git a/packages/connectors/connector-feishu-web/package.json b/packages/connectors/connector-feishu-web/package.json index b6db69458b6..77f01bd255b 100644 --- a/packages/connectors/connector-feishu-web/package.json +++ b/packages/connectors/connector-feishu-web/package.json @@ -1,14 +1,14 @@ { "name": "@logto/connector-feishu-web", - "version": "1.2.1", + "version": "1.3.0", "description": "Feishu web connector.", "author": "Silverhand Inc. ", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -23,9 +23,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -52,23 +52,18 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^26.0.0", - "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^15.2.3", - "@rollup/plugin-typescript": "^11.1.6", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-github/CHANGELOG.md b/packages/connectors/connector-github/CHANGELOG.md index 3070f3df691..05ad1af36e9 100644 --- a/packages/connectors/connector-github/CHANGELOG.md +++ b/packages/connectors/connector-github/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-github +## 1.5.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 1.4.2 ### Patch Changes diff --git a/packages/connectors/connector-github/package.json b/packages/connectors/connector-github/package.json index 1e9eeb2a4e9..4df0c375e89 100644 --- a/packages/connectors/connector-github/package.json +++ b/packages/connectors/connector-github/package.json @@ -1,6 +1,6 @@ { "name": "@logto/connector-github", - "version": "1.4.2", + "version": "1.5.0", "description": "Github web connector implementation.", "author": "Silverhand Inc. ", "dependencies": { @@ -8,8 +8,8 @@ "@silverhand/essentials": "^2.9.1", "ky": "^1.2.3", "query-string": "^9.0.0", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -24,9 +24,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -53,23 +53,18 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^26.0.0", - "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^15.2.3", - "@rollup/plugin-typescript": "^11.1.6", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", - "nock": "14.0.0-beta.7", + "nock": "14.0.0-beta.9", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-google/CHANGELOG.md b/packages/connectors/connector-google/CHANGELOG.md index 1525e3dea4a..81ff42db4f6 100644 --- a/packages/connectors/connector-google/CHANGELOG.md +++ b/packages/connectors/connector-google/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-google +## 1.5.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 1.4.0 ### Minor Changes diff --git a/packages/connectors/connector-google/package.json b/packages/connectors/connector-google/package.json index 6f5c8da1591..fa995562f88 100644 --- a/packages/connectors/connector-google/package.json +++ b/packages/connectors/connector-google/package.json @@ -1,15 +1,15 @@ { "name": "@logto/connector-google", - "version": "1.4.0", + "version": "1.5.0", "description": "Google web connector implementation.", "author": "Silverhand Inc. ", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", - "jose": "^5.0.0", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "jose": "^5.6.3", + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -24,9 +24,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -53,23 +53,18 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^26.0.0", - "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^15.2.3", - "@rollup/plugin-typescript": "^11.1.6", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-huggingface/CHANGELOG.md b/packages/connectors/connector-huggingface/CHANGELOG.md index 9a4e9d322cd..20c3e36594d 100644 --- a/packages/connectors/connector-huggingface/CHANGELOG.md +++ b/packages/connectors/connector-huggingface/CHANGELOG.md @@ -1,5 +1,20 @@ # @logto/connector-huggingface +## 0.2.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + +### Patch Changes + +- Updated dependencies [510f681fa] + - @logto/connector-oauth@1.4.0 + ## 0.1.1 ### Patch Changes diff --git a/packages/connectors/connector-huggingface/package.json b/packages/connectors/connector-huggingface/package.json index b87196f02f6..5cd070f240c 100644 --- a/packages/connectors/connector-huggingface/package.json +++ b/packages/connectors/connector-huggingface/package.json @@ -1,14 +1,14 @@ { "name": "@logto/connector-huggingface", - "version": "0.1.1", + "version": "0.2.0", "description": "Hugging Face connector implementation.", "author": "Silverhand Inc. ", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", - "@logto/connector-oauth": "workspace:^1.3.1", + "@logto/connector-oauth": "workspace:^1.4.0", "@silverhand/essentials": "^2.9.1", "ky": "^1.2.3", - "zod": "^3.22.4" + "zod": "^3.23.8" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -23,9 +23,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -52,23 +52,18 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^26.0.0", - "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^15.2.3", - "@rollup/plugin-typescript": "^11.1.6", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", - "nock": "14.0.0-beta.7", + "nock": "14.0.0-beta.9", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-kakao/CHANGELOG.md b/packages/connectors/connector-kakao/CHANGELOG.md index 813a9f758ef..15c2b083ef6 100644 --- a/packages/connectors/connector-kakao/CHANGELOG.md +++ b/packages/connectors/connector-kakao/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-kakao +## 1.3.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 1.2.1 ### Patch Changes diff --git a/packages/connectors/connector-kakao/package.json b/packages/connectors/connector-kakao/package.json index 900f095e6c5..3c0091e4ea2 100644 --- a/packages/connectors/connector-kakao/package.json +++ b/packages/connectors/connector-kakao/package.json @@ -1,14 +1,14 @@ { "name": "@logto/connector-kakao", - "version": "1.2.1", + "version": "1.3.0", "description": "Kakao connector implementation.", "author": "Kyungyoon Kim. ", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -23,9 +23,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -52,23 +52,18 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^26.0.0", - "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^15.2.3", - "@rollup/plugin-typescript": "^11.1.6", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-logto-email/CHANGELOG.md b/packages/connectors/connector-logto-email/CHANGELOG.md index 2a7dbe906db..727a8e00f84 100644 --- a/packages/connectors/connector-logto-email/CHANGELOG.md +++ b/packages/connectors/connector-logto-email/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-logto-email +## 1.2.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 1.1.2 ### Patch Changes diff --git a/packages/connectors/connector-logto-email/package.json b/packages/connectors/connector-logto-email/package.json index d209481cfcc..ff9df9cd506 100644 --- a/packages/connectors/connector-logto-email/package.json +++ b/packages/connectors/connector-logto-email/package.json @@ -1,14 +1,14 @@ { "name": "@logto/connector-logto-email", - "version": "1.1.2", + "version": "1.2.0", "description": "Logto email connector.", "author": "Silverhand Inc. ", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -23,9 +23,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -52,24 +52,19 @@ "access": "public" }, "devDependencies": { - "@logto/cloud": "0.2.5-a7eedce", - "@rollup/plugin-commonjs": "^26.0.0", - "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^15.2.3", - "@rollup/plugin-typescript": "^11.1.6", + "@logto/cloud": "0.2.5-3b703da", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-logto-email/src/constant.ts b/packages/connectors/connector-logto-email/src/constant.ts index 82a6157580d..a9f94c13e8f 100644 --- a/packages/connectors/connector-logto-email/src/constant.ts +++ b/packages/connectors/connector-logto-email/src/constant.ts @@ -89,7 +89,7 @@ export const defaultMetadata: ConnectorMetadata = { }, { key: 'appLogo', - label: 'App Logo', + label: 'Email logo', type: ConnectorConfigFormItemType.Text, }, ], diff --git a/packages/connectors/connector-logto-sms/CHANGELOG.md b/packages/connectors/connector-logto-sms/CHANGELOG.md index 08b1d95aa47..79ef1f9cb8d 100644 --- a/packages/connectors/connector-logto-sms/CHANGELOG.md +++ b/packages/connectors/connector-logto-sms/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-logto-sms +## 1.2.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 1.1.2 ### Patch Changes diff --git a/packages/connectors/connector-logto-sms/package.json b/packages/connectors/connector-logto-sms/package.json index 00fa94c4f50..7ad0abc5343 100644 --- a/packages/connectors/connector-logto-sms/package.json +++ b/packages/connectors/connector-logto-sms/package.json @@ -1,14 +1,14 @@ { "name": "@logto/connector-logto-sms", - "version": "1.1.2", + "version": "1.2.0", "description": "Logto SMS connector.", "author": "Silverhand Inc. ", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -23,9 +23,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -52,23 +52,18 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^26.0.0", - "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^15.2.3", - "@rollup/plugin-typescript": "^11.1.6", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-logto-social-demo/CHANGELOG.md b/packages/connectors/connector-logto-social-demo/CHANGELOG.md index e641551e64a..4b77f04322b 100644 --- a/packages/connectors/connector-logto-social-demo/CHANGELOG.md +++ b/packages/connectors/connector-logto-social-demo/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-logto-social-demo +## 1.2.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 1.1.2 ### Patch Changes diff --git a/packages/connectors/connector-logto-social-demo/package.json b/packages/connectors/connector-logto-social-demo/package.json index 0e765182fbf..7ec2d21aaad 100644 --- a/packages/connectors/connector-logto-social-demo/package.json +++ b/packages/connectors/connector-logto-social-demo/package.json @@ -1,14 +1,14 @@ { "name": "@logto/connector-logto-social-demo", - "version": "1.1.2", + "version": "1.2.0", "description": "OAuth standard connector implementation.", "author": "Silverhand Inc. ", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -23,9 +23,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -52,23 +52,18 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^26.0.0", - "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^15.2.3", - "@rollup/plugin-typescript": "^11.1.6", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-mailgun/CHANGELOG.md b/packages/connectors/connector-mailgun/CHANGELOG.md index 47a8407f9dc..f97a2c5e96a 100644 --- a/packages/connectors/connector-mailgun/CHANGELOG.md +++ b/packages/connectors/connector-mailgun/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-mailgun +## 1.3.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 1.2.2 ### Patch Changes diff --git a/packages/connectors/connector-mailgun/package.json b/packages/connectors/connector-mailgun/package.json index 07cacc5bdb8..77fd114c99b 100644 --- a/packages/connectors/connector-mailgun/package.json +++ b/packages/connectors/connector-mailgun/package.json @@ -1,14 +1,14 @@ { "name": "@logto/connector-mailgun", - "version": "1.2.2", + "version": "1.3.0", "description": "Mailgun connector for Logto.", "author": "Silverhand Inc. ", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -23,9 +23,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -52,23 +52,18 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^26.0.0", - "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^15.2.3", - "@rollup/plugin-typescript": "^11.1.6", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-mock-email-alternative/CHANGELOG.md b/packages/connectors/connector-mock-email-alternative/CHANGELOG.md index c324ffcb4bc..e731249f313 100644 --- a/packages/connectors/connector-mock-email-alternative/CHANGELOG.md +++ b/packages/connectors/connector-mock-email-alternative/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-mock-standard-email +## 2.1.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 2.0.2 ### Patch Changes diff --git a/packages/connectors/connector-mock-email-alternative/package.json b/packages/connectors/connector-mock-email-alternative/package.json index 9132e8f8530..123de9a5780 100644 --- a/packages/connectors/connector-mock-email-alternative/package.json +++ b/packages/connectors/connector-mock-email-alternative/package.json @@ -1,20 +1,20 @@ { "name": "@logto/connector-mock-standard-email", - "version": "2.0.2", + "version": "2.1.0", "description": "Mock Standard Email Service connector implementation for integration tests only.", "author": "Silverhand Inc. ", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -52,23 +52,18 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^26.0.0", - "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^15.2.3", - "@rollup/plugin-typescript": "^11.1.6", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-mock-email/CHANGELOG.md b/packages/connectors/connector-mock-email/CHANGELOG.md index 9f0a121575b..ef4375a6f7c 100644 --- a/packages/connectors/connector-mock-email/CHANGELOG.md +++ b/packages/connectors/connector-mock-email/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-mock-email +## 2.1.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 2.0.2 ### Patch Changes diff --git a/packages/connectors/connector-mock-email/package.json b/packages/connectors/connector-mock-email/package.json index f1b4024346a..9f746fe443e 100644 --- a/packages/connectors/connector-mock-email/package.json +++ b/packages/connectors/connector-mock-email/package.json @@ -1,20 +1,20 @@ { "name": "@logto/connector-mock-email", - "version": "2.0.2", + "version": "2.1.0", "description": "Mock Email Service connector implementation for integration tests only.", "author": "Silverhand Inc. ", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -52,23 +52,18 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^26.0.0", - "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^15.2.3", - "@rollup/plugin-typescript": "^11.1.6", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-mock-sms/CHANGELOG.md b/packages/connectors/connector-mock-sms/CHANGELOG.md index db5aedaeb47..6167208e2ab 100644 --- a/packages/connectors/connector-mock-sms/CHANGELOG.md +++ b/packages/connectors/connector-mock-sms/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-mock-sms +## 2.1.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 2.0.2 ### Patch Changes diff --git a/packages/connectors/connector-mock-sms/package.json b/packages/connectors/connector-mock-sms/package.json index 1ae46035a40..fcc57351ddf 100644 --- a/packages/connectors/connector-mock-sms/package.json +++ b/packages/connectors/connector-mock-sms/package.json @@ -1,20 +1,20 @@ { "name": "@logto/connector-mock-sms", - "version": "2.0.2", + "version": "2.1.0", "description": "Mock SMS connector implementation for integration tests only.", "author": "Silverhand Inc. ", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -52,23 +52,18 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^26.0.0", - "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^15.2.3", - "@rollup/plugin-typescript": "^11.1.6", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-mock-social/CHANGELOG.md b/packages/connectors/connector-mock-social/CHANGELOG.md index 44cbf2e1725..1e80ef4dabe 100644 --- a/packages/connectors/connector-mock-social/CHANGELOG.md +++ b/packages/connectors/connector-mock-social/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-mock-social +## 1.3.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 1.2.1 ### Patch Changes diff --git a/packages/connectors/connector-mock-social/package.json b/packages/connectors/connector-mock-social/package.json index 902cecb18f7..700247542c4 100644 --- a/packages/connectors/connector-mock-social/package.json +++ b/packages/connectors/connector-mock-social/package.json @@ -1,20 +1,20 @@ { "name": "@logto/connector-mock-social", - "version": "1.2.1", + "version": "1.3.0", "description": "Social mock connector implementation.", "author": "Silverhand Inc. ", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -52,23 +52,18 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^26.0.0", - "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^15.2.3", - "@rollup/plugin-typescript": "^11.1.6", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-mock-social/src/index.ts b/packages/connectors/connector-mock-social/src/index.ts index 77c24b645a3..f9751ba22e5 100644 --- a/packages/connectors/connector-mock-social/src/index.ts +++ b/packages/connectors/connector-mock-social/src/index.ts @@ -30,7 +30,7 @@ const getAuthorizationUri: GetAuthorizationUri = async ( } } - return `http://mock.social.com/?state=${state}&redirect_uri=${redirectUri}`; + return `http://mock-social/?state=${state}&redirect_uri=${redirectUri}`; }; const getUserInfo: GetUserInfo = async (data, getSession) => { @@ -39,6 +39,8 @@ const getUserInfo: GetUserInfo = async (data, getSession) => { userId: z.optional(z.string()), email: z.string().optional(), phone: z.string().optional(), + name: z.string().optional(), + avatar: z.string().optional(), }); const result = dataGuard.safeParse(data); diff --git a/packages/connectors/connector-mygovid/package.json b/packages/connectors/connector-mygovid/package.json index 91ccf411e4d..7a086836dbf 100644 --- a/packages/connectors/connector-mygovid/package.json +++ b/packages/connectors/connector-mygovid/package.json @@ -26,9 +26,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", diff --git a/packages/connectors/connector-naver/CHANGELOG.md b/packages/connectors/connector-naver/CHANGELOG.md index 565c435eb0d..bda3cffdf43 100644 --- a/packages/connectors/connector-naver/CHANGELOG.md +++ b/packages/connectors/connector-naver/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-naver +## 1.3.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 1.2.1 ### Patch Changes diff --git a/packages/connectors/connector-naver/package.json b/packages/connectors/connector-naver/package.json index 22a3e422816..8789957438f 100644 --- a/packages/connectors/connector-naver/package.json +++ b/packages/connectors/connector-naver/package.json @@ -1,14 +1,14 @@ { "name": "@logto/connector-naver", - "version": "1.2.1", + "version": "1.3.0", "description": "Naver connector implementation.", "author": "Kyungyoon Kim. ", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -23,9 +23,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -52,23 +52,18 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^26.0.0", - "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^15.2.3", - "@rollup/plugin-typescript": "^11.1.6", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-oauth2/CHANGELOG.md b/packages/connectors/connector-oauth2/CHANGELOG.md index 6943b878a57..fa90526996a 100644 --- a/packages/connectors/connector-oauth2/CHANGELOG.md +++ b/packages/connectors/connector-oauth2/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-oauth +## 1.4.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 1.3.1 ### Patch Changes diff --git a/packages/connectors/connector-oauth2/package.json b/packages/connectors/connector-oauth2/package.json index 9fb22112f82..097b6073f97 100644 --- a/packages/connectors/connector-oauth2/package.json +++ b/packages/connectors/connector-oauth2/package.json @@ -1,17 +1,17 @@ { "name": "@logto/connector-oauth", - "version": "1.3.1", + "version": "1.4.0", "description": "OAuth standard connector implementation.", "author": "Silverhand Inc. ", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", "@logto/shared": "workspace:^3.1.1", "@silverhand/essentials": "^2.9.1", - "jose": "^5.0.0", + "jose": "^5.6.3", "ky": "^1.2.3", "query-string": "^9.0.0", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -26,15 +26,16 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup && tsc --declaration --emitDeclarationOnly", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", "test:ci": "pnpm run test --silent --coverage", "prepublishOnly": "pnpm build", - "prepack": "pnpm build" + "prepack": "pnpm build", + "build:test": "pnpm build" }, "engines": { "node": "^20.9.0" @@ -56,23 +57,18 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^26.0.0", - "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^15.2.3", - "@rollup/plugin-typescript": "^11.1.6", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", - "nock": "14.0.0-beta.7", + "nock": "14.0.0-beta.9", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-ogcio-entraid/package.json b/packages/connectors/connector-ogcio-entraid/package.json index 4e944a87e27..214f3af96b8 100644 --- a/packages/connectors/connector-ogcio-entraid/package.json +++ b/packages/connectors/connector-ogcio-entraid/package.json @@ -23,9 +23,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", diff --git a/packages/connectors/connector-oidc/CHANGELOG.md b/packages/connectors/connector-oidc/CHANGELOG.md index 8ff39c430b4..e90805f28d1 100644 --- a/packages/connectors/connector-oidc/CHANGELOG.md +++ b/packages/connectors/connector-oidc/CHANGELOG.md @@ -1,5 +1,20 @@ # @logto/connector-oidc +## 1.4.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + +### Patch Changes + +- Updated dependencies [510f681fa] + - @logto/connector-oauth@1.4.0 + ## 1.3.1 ### Patch Changes diff --git a/packages/connectors/connector-oidc/package.json b/packages/connectors/connector-oidc/package.json index bffa1bbc509..432e3ccd0ca 100644 --- a/packages/connectors/connector-oidc/package.json +++ b/packages/connectors/connector-oidc/package.json @@ -1,17 +1,17 @@ { "name": "@logto/connector-oidc", - "version": "1.3.1", + "version": "1.4.0", "description": "OIDC standard connector implementation.", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", - "@logto/connector-oauth": "workspace:^1.3.1", + "@logto/connector-oauth": "workspace:^1.4.0", "@logto/shared": "workspace:^3.1.1", "@silverhand/essentials": "^2.9.1", - "jose": "^5.0.0", + "jose": "^5.6.3", "ky": "^1.2.3", "nanoid": "^5.0.1", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -26,9 +26,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -55,23 +55,18 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^26.0.0", - "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^15.2.3", - "@rollup/plugin-typescript": "^11.1.6", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", - "nock": "14.0.0-beta.7", + "nock": "14.0.0-beta.9", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-postmark/CHANGELOG.md b/packages/connectors/connector-postmark/CHANGELOG.md new file mode 100644 index 00000000000..6e153566253 --- /dev/null +++ b/packages/connectors/connector-postmark/CHANGELOG.md @@ -0,0 +1,7 @@ +# @logto/connector-postmark + +## 1.0.0 + +### Major Changes + +- e9581d8b4: add postmark connector diff --git a/packages/connectors/connector-postmark/README.md b/packages/connectors/connector-postmark/README.md new file mode 100644 index 00000000000..f456741d78b --- /dev/null +++ b/packages/connectors/connector-postmark/README.md @@ -0,0 +1,61 @@ +# Postmark connector + +Logto connector for Postmark email service. + +## Get started + +Postmark is a mail platform for transactional and marketing email. We can use its email sending function to send a _verification code_. + +## Register Postmark account + +Create a new account at [Postmark website](https://postmark.com/). You may skip this step if you've already got an account. + +## Configure your connector + +Fill out the `serverToken` field with the Server Token you find under settings for your +server in Postmark. + +Fill out the `fromEmail` field with the senders' _From Address_. + +In order to enable full user flows, templates with usageType `Register`, `SignIn`, `ForgotPassword` and `Generic` are required + +Here is an example of Postmark connector template JSON. + +```jsonc +[ + { + "usageType": "Register", + "templateAlias": "logto-register" + }, + { + "usageType": "SignIn", + "templateAlias": "logto-sign-in" + }, + { + "usageType": "ForgotPassword", + "templateAlias": "logto-forgot-password" + }, + { + "usageType": "Generic", + "templateAlias": "logto-generic" + }, +] +``` + +## Test Postmark email connector + +You can type in an email address and click on "Send" to see whether the settings can work before "Save and Done". + +That's it. Don't forget to [Enable connector in sign-in experience](https://docs.logto.io/docs/tutorials/get-started/passwordless-sign-in-by-adding-connectors#enable-sms-or-email-passwordless-sign-in) + +## Config types + +| Name | Type | +|-------------|-------------------| +| serverToken | string | +| fromEmail | string | + +| Template Properties | Type | Enum values | +|---------------------|-------------|------------------------------------------------------| +| usageType | enum string | 'Register' \| 'SignIn' \| 'ForgotPassword' \| 'Generic' | +| templateAlias | string | N/A | diff --git a/packages/connectors/connector-postmark/logo.svg b/packages/connectors/connector-postmark/logo.svg new file mode 100644 index 00000000000..7826748742a --- /dev/null +++ b/packages/connectors/connector-postmark/logo.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/packages/connectors/connector-postmark/package.json b/packages/connectors/connector-postmark/package.json new file mode 100644 index 00000000000..ab8fd454f22 --- /dev/null +++ b/packages/connectors/connector-postmark/package.json @@ -0,0 +1,68 @@ +{ + "name": "@logto/connector-postmark", + "version": "1.0.0", + "description": "Postmark connector implementation.", + "author": "Sten Sandvik ", + "dependencies": { + "@logto/connector-kit": "workspace:^4.0.0", + "@silverhand/essentials": "^2.9.0", + "postmark": "^4.0.2", + "zod": "^3.22.4" + }, + "devDependencies": { + "@silverhand/eslint-config": "6.0.1", + "@silverhand/ts-config": "6.0.0", + "@types/node": "^20.11.20", + "@types/supertest": "^6.0.2", + "@vitest/coverage-v8": "^2.0.0", + "eslint": "^8.56.0", + "lint-staged": "^15.0.2", + "nock": "14.0.0-beta.9", + "prettier": "^3.0.0", + "supertest": "^7.0.0", + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" + }, + "main": "./lib/index.js", + "module": "./lib/index.js", + "exports": "./lib/index.js", + "license": "MPL-2.0", + "type": "module", + "files": [ + "lib", + "docs", + "logo.svg", + "logo-dark.svg" + ], + "scripts": { + "precommit": "lint-staged", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", + "lint": "eslint --ext .ts src", + "lint:report": "pnpm lint --format json --output-file report.json", + "test": "vitest src", + "test:ci": "pnpm run test --silent --coverage", + "prepublishOnly": "pnpm build" + }, + "engines": { + "node": "^20.9.0" + }, + "eslintConfig": { + "extends": "@silverhand", + "settings": { + "import/core-modules": [ + "@silverhand/essentials", + "got", + "nock", + "snakecase-keys", + "zod" + ] + } + }, + "prettier": "@silverhand/eslint-config/.prettierrc", + "publishConfig": { + "access": "public" + } +} diff --git a/packages/connectors/connector-postmark/src/constant.ts b/packages/connectors/connector-postmark/src/constant.ts new file mode 100644 index 00000000000..694012e0934 --- /dev/null +++ b/packages/connectors/connector-postmark/src/constant.ts @@ -0,0 +1,57 @@ +import type { ConnectorMetadata } from '@logto/connector-kit'; +import { ConnectorConfigFormItemType } from '@logto/connector-kit'; + +export const defaultMetadata: ConnectorMetadata = { + id: 'postmark-mail', + target: 'postmark-mail', + platform: null, + name: { + en: 'Postmark Mail', + }, + logo: './logo.svg', + logoDark: null, + description: { + en: 'Postmark is a mail sending platform.', + }, + readme: './README.md', + formItems: [ + { + key: 'serverToken', + label: 'Server Token', + type: ConnectorConfigFormItemType.Text, + required: true, + placeholder: '', + }, + { + key: 'fromEmail', + label: 'From Email', + type: ConnectorConfigFormItemType.Text, + required: true, + placeholder: '', + }, + { + key: 'templates', + label: 'Templates', + type: ConnectorConfigFormItemType.Json, + required: true, + defaultValue: [ + { + usageType: 'SignIn', + templateAlias: 'logto-sign-in', + }, + { + usageType: 'Register', + templateAlias: 'logto-register', + }, + { + usageType: 'ForgotPassword', + templateAlias: 'logto-forgot-password', + }, + { + usageType: 'Generic', + templateAlias: 'logto-generic', + }, + ], + }, + ], +}; diff --git a/packages/connectors/connector-postmark/src/index.test.ts b/packages/connectors/connector-postmark/src/index.test.ts new file mode 100644 index 00000000000..634a0bddaa5 --- /dev/null +++ b/packages/connectors/connector-postmark/src/index.test.ts @@ -0,0 +1,42 @@ +import { TemplateType } from '@logto/connector-kit'; + +import { mockedConfig } from './mock.js'; + +const getConfig = vi.fn().mockResolvedValue(mockedConfig); +const sendEmailWithTemplate = vi.fn(); +vi.mock('postmark', () => ({ + ServerClient: vi.fn(() => ({ + sendEmailWithTemplate, + })), +})); + +const { default: createConnector } = await import('./index.js'); + +describe('Postmark connector', () => { + it('init without throwing errors', async () => { + await expect(createConnector({ getConfig })).resolves.not.toThrow(); + }); + + describe('sendMessage()', () => { + afterEach(() => { + vi.clearAllMocks(); + }); + + it('should call sendEmailWithTemplate() with correct template and content', async () => { + const connector = await createConnector({ getConfig }); + await connector.sendMessage({ + to: 'to@email.com', + type: TemplateType.SignIn, + payload: { code: '1234' }, + }); + expect(sendEmailWithTemplate).toHaveBeenCalledWith( + expect.objectContaining({ + From: mockedConfig.fromEmail, + TemplateAlias: 'logto-sign-in', + To: 'to@email.com', + TemplateModel: { code: '1234' }, + }) + ); + }); + }); +}); diff --git a/packages/connectors/connector-postmark/src/index.ts b/packages/connectors/connector-postmark/src/index.ts new file mode 100644 index 00000000000..6039796cdab --- /dev/null +++ b/packages/connectors/connector-postmark/src/index.ts @@ -0,0 +1,65 @@ +import { assert } from '@silverhand/essentials'; + +import type { + GetConnectorConfig, + CreateConnector, + EmailConnector, + SendMessageFunction, +} from '@logto/connector-kit'; +import { + ConnectorError, + ConnectorErrorCodes, + validateConfig, + ConnectorType, +} from '@logto/connector-kit'; +import { ServerClient } from 'postmark'; + +import { defaultMetadata } from './constant.js'; +import { postmarkConfigGuard } from './types.js'; + +const sendMessage = + (getConfig: GetConnectorConfig): SendMessageFunction => + async (data, inputConfig) => { + const { to, type, payload } = data; + + const config = inputConfig ?? (await getConfig(defaultMetadata.id)); + validateConfig(config, postmarkConfigGuard); + + const { serverToken, fromEmail, templates } = config; + const template = templates.find((template) => template.usageType === type); + + assert( + template, + new ConnectorError( + ConnectorErrorCodes.TemplateNotFound, + `Template not found for type: ${type}` + ) + ); + + const client = new ServerClient(serverToken); + + try { + await client.sendEmailWithTemplate({ + From: fromEmail, + TemplateAlias: template.templateAlias, + To: to, + TemplateModel: payload, + }); + } catch (error: unknown) { + throw new ConnectorError( + ConnectorErrorCodes.General, + error instanceof Error ? error.message : '' + ); + } + }; + +const createPostmarkConnector: CreateConnector = async ({ getConfig }) => { + return { + metadata: defaultMetadata, + type: ConnectorType.Email, + configGuard: postmarkConfigGuard, + sendMessage: sendMessage(getConfig), + }; +}; + +export default createPostmarkConnector; diff --git a/packages/connectors/connector-postmark/src/mock.ts b/packages/connectors/connector-postmark/src/mock.ts new file mode 100644 index 00000000000..457b541ccd1 --- /dev/null +++ b/packages/connectors/connector-postmark/src/mock.ts @@ -0,0 +1,26 @@ +import type { PostmarkConfig } from './types.js'; + +export const mockedServerToken = 'serverToken'; + +export const mockedConfig: PostmarkConfig = { + serverToken: mockedServerToken, + fromEmail: 'noreply@logto.test.io', + templates: [ + { + usageType: 'SignIn', + templateAlias: 'logto-sign-in', + }, + { + usageType: 'Register', + templateAlias: 'logto-register', + }, + { + usageType: 'ForgotPassword', + templateAlias: 'logto-forgot-password', + }, + { + usageType: 'Generic', + templateAlias: 'logto-generic', + }, + ], +}; diff --git a/packages/connectors/connector-postmark/src/types.ts b/packages/connectors/connector-postmark/src/types.ts new file mode 100644 index 00000000000..ff5a06cd636 --- /dev/null +++ b/packages/connectors/connector-postmark/src/types.ts @@ -0,0 +1,32 @@ +import { z } from 'zod'; + +/** + * UsageType here is used to specify the use case of the template, can be either + * 'Register', 'SignIn', 'ForgotPassword', 'Generic'. + */ +const requiredTemplateUsageTypes = ['Register', 'SignIn', 'ForgotPassword', 'Generic']; + +const templateGuard = z.object({ + usageType: z.string(), + templateAlias: z.string(), +}); + +export const postmarkConfigGuard = z.object({ + serverToken: z.string(), + fromEmail: z.string(), + templates: z.array(templateGuard).refine( + (templates) => + requiredTemplateUsageTypes.every((requiredType) => + templates.map((template) => template.usageType).includes(requiredType) + ), + (templates) => ({ + message: `Template with UsageType (${requiredTemplateUsageTypes + .filter( + (requiredType) => !templates.map((template) => template.usageType).includes(requiredType) + ) + .join(', ')}) should be provided!`, + }) + ), +}); + +export type PostmarkConfig = z.infer; diff --git a/packages/connectors/connector-saml/CHANGELOG.md b/packages/connectors/connector-saml/CHANGELOG.md index dabc6162ef2..ac9bf904c10 100644 --- a/packages/connectors/connector-saml/CHANGELOG.md +++ b/packages/connectors/connector-saml/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-saml +## 1.2.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 1.1.2 ### Patch Changes diff --git a/packages/connectors/connector-saml/package.json b/packages/connectors/connector-saml/package.json index 7e84b900cf4..af99429f3ca 100644 --- a/packages/connectors/connector-saml/package.json +++ b/packages/connectors/connector-saml/package.json @@ -1,6 +1,6 @@ { "name": "@logto/connector-saml", - "version": "1.1.2", + "version": "1.2.0", "description": "SAML standard connector implementation.", "author": "Silverhand Inc. ", "dependencies": { @@ -9,8 +9,8 @@ "fast-xml-parser": "^4.3.6", "got": "^14.0.0", "samlify": "2.8.11", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -25,9 +25,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -54,23 +54,18 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^26.0.0", - "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^15.2.3", - "@rollup/plugin-typescript": "^11.1.6", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-sendgrid-email/CHANGELOG.md b/packages/connectors/connector-sendgrid-email/CHANGELOG.md index 421045fef2c..2b86deb802d 100644 --- a/packages/connectors/connector-sendgrid-email/CHANGELOG.md +++ b/packages/connectors/connector-sendgrid-email/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-sendgrid-email +## 1.2.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 1.1.2 ### Patch Changes diff --git a/packages/connectors/connector-sendgrid-email/package.json b/packages/connectors/connector-sendgrid-email/package.json index 58231c9eb00..68700420171 100644 --- a/packages/connectors/connector-sendgrid-email/package.json +++ b/packages/connectors/connector-sendgrid-email/package.json @@ -1,14 +1,14 @@ { "name": "@logto/connector-sendgrid-email", - "version": "1.1.2", + "version": "1.2.0", "description": "SendGrid Email Service connector implementation.", "author": "Silverhand Inc. ", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -23,9 +23,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -52,23 +52,18 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^26.0.0", - "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^15.2.3", - "@rollup/plugin-typescript": "^11.1.6", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-smsaero/CHANGELOG.md b/packages/connectors/connector-smsaero/CHANGELOG.md index fe9faafda9c..77f31884710 100644 --- a/packages/connectors/connector-smsaero/CHANGELOG.md +++ b/packages/connectors/connector-smsaero/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-smsaero +## 1.3.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 1.2.2 ### Patch Changes diff --git a/packages/connectors/connector-smsaero/package.json b/packages/connectors/connector-smsaero/package.json index 306eb38dbb3..9fcc20f83d6 100644 --- a/packages/connectors/connector-smsaero/package.json +++ b/packages/connectors/connector-smsaero/package.json @@ -1,14 +1,14 @@ { "name": "@logto/connector-smsaero", - "version": "1.2.2", + "version": "1.3.0", "description": "SMSAero connector implementation.", "author": "Danil Tankov ", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -23,9 +23,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -52,23 +52,18 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^26.0.0", - "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^15.2.3", - "@rollup/plugin-typescript": "^11.1.6", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-smtp/CHANGELOG.md b/packages/connectors/connector-smtp/CHANGELOG.md index 999f63372ba..0d068c29e28 100644 --- a/packages/connectors/connector-smtp/CHANGELOG.md +++ b/packages/connectors/connector-smtp/CHANGELOG.md @@ -1,5 +1,16 @@ # @logto/connector-smtp +## 1.2.0 + +### Minor Changes + +- 6fca3fe3c: enable static custom headers for SMTP connector +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 1.1.3 ### Patch Changes diff --git a/packages/connectors/connector-smtp/package.json b/packages/connectors/connector-smtp/package.json index 8fbd5f46869..1edc7fad725 100644 --- a/packages/connectors/connector-smtp/package.json +++ b/packages/connectors/connector-smtp/package.json @@ -1,6 +1,6 @@ { "name": "@logto/connector-smtp", - "version": "1.1.3", + "version": "1.2.0", "description": "SMTP connector implementation.", "author": "Silverhand Inc. ", "dependencies": { @@ -8,29 +8,24 @@ "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", "nodemailer": "^6.9.9", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "devDependencies": { - "@rollup/plugin-commonjs": "^26.0.0", - "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^15.2.3", - "@rollup/plugin-typescript": "^11.1.6", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/nodemailer": "^6.4.7", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -45,9 +40,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", diff --git a/packages/connectors/connector-smtp/src/constant.ts b/packages/connectors/connector-smtp/src/constant.ts index d2078b048c0..28d9a42761f 100644 --- a/packages/connectors/connector-smtp/src/constant.ts +++ b/packages/connectors/connector-smtp/src/constant.ts @@ -198,5 +198,14 @@ export const defaultMetadata: ConnectorMetadata = { type: ConnectorConfigFormItemType.Switch, required: false, }, + { + key: 'customHeaders', + label: 'Custom Headers', + type: ConnectorConfigFormItemType.Json, + required: false, + defaultValue: {}, + description: + 'Custom headers to be added to original email headers when sending messages. Both keys and values should be string-typed.', + }, ], }; diff --git a/packages/connectors/connector-smtp/src/index.test.ts b/packages/connectors/connector-smtp/src/index.test.ts index a28137dd32d..b57ad02e133 100644 --- a/packages/connectors/connector-smtp/src/index.test.ts +++ b/packages/connectors/connector-smtp/src/index.test.ts @@ -79,6 +79,28 @@ describe('SMTP connector', () => { to: 'baz', }); }); + + it('should send mail with customer headers', async () => { + const connector = await createConnector({ + getConfig: vi.fn().mockResolvedValue({ + ...mockedConfig, + customHeaders: { 'X-Test': 'test', 'X-Test-Another': ['test1', 'test2', 'test3'] }, + }), + }); + await connector.sendMessage({ + to: 'baz', + type: TemplateType.OrganizationInvitation, + payload: { code: '345678', link: 'https://example.com' }, + }); + + expect(sendMail).toHaveBeenCalledWith({ + from: '', + subject: 'Organization invitation', + text: 'This is for organization invitation. Your link is https://example.com.', + to: 'baz', + headers: { 'X-Test': 'test', 'X-Test-Another': ['test1', 'test2', 'test3'] }, + }); + }); }); describe('Test config guard', () => { diff --git a/packages/connectors/connector-smtp/src/index.ts b/packages/connectors/connector-smtp/src/index.ts index 8aed0e05dd6..f447f420e65 100644 --- a/packages/connectors/connector-smtp/src/index.ts +++ b/packages/connectors/connector-smtp/src/index.ts @@ -1,4 +1,4 @@ -import { assert } from '@silverhand/essentials'; +import { assert, conditional } from '@silverhand/essentials'; import type { GetConnectorConfig, @@ -14,6 +14,7 @@ import { replaceSendMessageHandlebars, } from '@logto/connector-kit'; import nodemailer from 'nodemailer'; +import type Mail from 'nodemailer/lib/mailer'; import type SMTPTransport from 'nodemailer/lib/smtp-transport'; import { defaultMetadata } from './constant.js'; @@ -44,11 +45,17 @@ const sendMessage = template.contentType ); - const mailOptions = { + const mailOptions: Mail.Options = { to, from: config.fromEmail, replyTo: config.replyTo, subject: replaceSendMessageHandlebars(template.subject, payload), + ...conditional( + config.customHeaders && + Object.entries(config.customHeaders).length > 0 && { + headers: config.customHeaders, + } + ), ...contentsObject, }; diff --git a/packages/connectors/connector-smtp/src/mock.ts b/packages/connectors/connector-smtp/src/mock.ts index 87f2017d3b5..bef4a38b1b1 100644 --- a/packages/connectors/connector-smtp/src/mock.ts +++ b/packages/connectors/connector-smtp/src/mock.ts @@ -35,6 +35,7 @@ export const mockedConfig = { usageType: 'OrganizationInvitation', }, ], + customHeaders: {}, }; export const mockedOauth2AuthWithToken = { diff --git a/packages/connectors/connector-smtp/src/types.ts b/packages/connectors/connector-smtp/src/types.ts index 263fe802807..cf18622105f 100644 --- a/packages/connectors/connector-smtp/src/types.ts +++ b/packages/connectors/connector-smtp/src/types.ts @@ -125,6 +125,7 @@ export const smtpConfigGuard = z.object({ servername: z.string().optional(), ignoreTLS: z.boolean().optional(), requireTLS: z.boolean().optional(), + customHeaders: z.record(z.string().or(z.string().array())).optional(), }); export type SmtpConfig = z.infer; diff --git a/packages/connectors/connector-tencent-sms/CHANGELOG.md b/packages/connectors/connector-tencent-sms/CHANGELOG.md index a0f03437d48..aee270d8a4f 100644 --- a/packages/connectors/connector-tencent-sms/CHANGELOG.md +++ b/packages/connectors/connector-tencent-sms/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-tencent-sms +## 1.2.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 1.1.2 ### Patch Changes diff --git a/packages/connectors/connector-tencent-sms/package.json b/packages/connectors/connector-tencent-sms/package.json index a0da5a6bcae..ae37f2c376d 100644 --- a/packages/connectors/connector-tencent-sms/package.json +++ b/packages/connectors/connector-tencent-sms/package.json @@ -1,14 +1,14 @@ { "name": "@logto/connector-tencent-sms", - "version": "1.1.2", + "version": "1.2.0", "description": "Tencent SMS connector implementation.", "author": "StringKe", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -23,9 +23,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -52,23 +52,18 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^26.0.0", - "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^15.2.3", - "@rollup/plugin-typescript": "^11.1.6", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-twilio-sms/CHANGELOG.md b/packages/connectors/connector-twilio-sms/CHANGELOG.md index 71e7e4630f3..b9642803401 100644 --- a/packages/connectors/connector-twilio-sms/CHANGELOG.md +++ b/packages/connectors/connector-twilio-sms/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-twilio-sms +## 1.2.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 1.1.2 ### Patch Changes diff --git a/packages/connectors/connector-twilio-sms/package.json b/packages/connectors/connector-twilio-sms/package.json index 976f3070249..99f9d94d014 100644 --- a/packages/connectors/connector-twilio-sms/package.json +++ b/packages/connectors/connector-twilio-sms/package.json @@ -1,14 +1,14 @@ { "name": "@logto/connector-twilio-sms", - "version": "1.1.2", + "version": "1.2.0", "description": "Twilio SMS connector implementation.", "author": "Silverhand Inc. ", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -23,9 +23,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -52,23 +52,18 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^26.0.0", - "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^15.2.3", - "@rollup/plugin-typescript": "^11.1.6", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-wechat-native/CHANGELOG.md b/packages/connectors/connector-wechat-native/CHANGELOG.md index 8a11df3eed8..ebdfaff229e 100644 --- a/packages/connectors/connector-wechat-native/CHANGELOG.md +++ b/packages/connectors/connector-wechat-native/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-wechat-native +## 1.3.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 1.2.1 ### Patch Changes diff --git a/packages/connectors/connector-wechat-native/package.json b/packages/connectors/connector-wechat-native/package.json index 9a68f2dc462..c4890f9f638 100644 --- a/packages/connectors/connector-wechat-native/package.json +++ b/packages/connectors/connector-wechat-native/package.json @@ -1,14 +1,14 @@ { "name": "@logto/connector-wechat-native", - "version": "1.2.1", + "version": "1.3.0", "description": "WeChat native connector implementation.", "author": "Silverhand Inc. ", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -23,9 +23,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -52,23 +52,18 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^26.0.0", - "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^15.2.3", - "@rollup/plugin-typescript": "^11.1.6", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-wechat-web/CHANGELOG.md b/packages/connectors/connector-wechat-web/CHANGELOG.md index f9206ebff28..246624d6d66 100644 --- a/packages/connectors/connector-wechat-web/CHANGELOG.md +++ b/packages/connectors/connector-wechat-web/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-wechat-web +## 1.4.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 1.3.1 ### Patch Changes diff --git a/packages/connectors/connector-wechat-web/package.json b/packages/connectors/connector-wechat-web/package.json index 07d99ad9280..c2d7b409923 100644 --- a/packages/connectors/connector-wechat-web/package.json +++ b/packages/connectors/connector-wechat-web/package.json @@ -1,14 +1,14 @@ { "name": "@logto/connector-wechat-web", - "version": "1.3.1", + "version": "1.4.0", "description": "Wechat Web connector implementation.", "author": "Silverhand Inc. ", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -23,9 +23,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -52,23 +52,18 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^26.0.0", - "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^15.2.3", - "@rollup/plugin-typescript": "^11.1.6", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-wecom/CHANGELOG.md b/packages/connectors/connector-wecom/CHANGELOG.md index 56e23dc3fb2..571d5b687d0 100644 --- a/packages/connectors/connector-wecom/CHANGELOG.md +++ b/packages/connectors/connector-wecom/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-wecom +## 0.3.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 0.2.1 ### Patch Changes diff --git a/packages/connectors/connector-wecom/package.json b/packages/connectors/connector-wecom/package.json index b06794e294c..15c3aa3555a 100644 --- a/packages/connectors/connector-wecom/package.json +++ b/packages/connectors/connector-wecom/package.json @@ -1,14 +1,14 @@ { "name": "@logto/connector-wecom", - "version": "0.2.1", + "version": "0.3.0", "description": "Wecom connector implementation.", "author": "Dove fork from Wechat Web connector", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -23,9 +23,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -52,23 +52,18 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^26.0.0", - "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^15.2.3", - "@rollup/plugin-typescript": "^11.1.6", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.10.4", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/templates/package.json b/packages/connectors/templates/package.json index ede58013e61..44870bc7b36 100644 --- a/packages/connectors/templates/package.json +++ b/packages/connectors/templates/package.json @@ -12,9 +12,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", diff --git a/packages/connectors/templates/preset/rollup.config.js b/packages/connectors/templates/preset/rollup.config.js deleted file mode 100644 index df98e6a1c1b..00000000000 --- a/packages/connectors/templates/preset/rollup.config.js +++ /dev/null @@ -1,25 +0,0 @@ -import commonjs from '@rollup/plugin-commonjs'; -import json from '@rollup/plugin-json'; -import { nodeResolve } from '@rollup/plugin-node-resolve'; -import typescript from '@rollup/plugin-typescript'; -import outputSize from 'rollup-plugin-output-size'; - -/** - * @type {import('rollup').RollupOptions} - */ -const configs = [ - { - input: ['src/index.ts'], - output: [{ dir: 'lib' }], - external: ['zod', 'got', '@logto/connector-kit'], - plugins: [ - typescript({ tsconfig: 'tsconfig.build.json' }), - nodeResolve({ exportConditions: ['node'], preferBuiltins: true }), - commonjs(), - json(), - outputSize(), - ], - }, -]; - -export default configs; diff --git a/packages/connectors/templates/preset/tsconfig.base.json b/packages/connectors/templates/preset/tsconfig.base.json deleted file mode 100644 index ebab46568f2..00000000000 --- a/packages/connectors/templates/preset/tsconfig.base.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "@silverhand/ts-config/tsconfig.base", - "compilerOptions": { - "moduleResolution": "nodenext", - "module": "nodenext", - "outDir": "lib", - "baseUrl": "." - } -} diff --git a/packages/connectors/templates/preset/tsconfig.build.json b/packages/connectors/templates/preset/tsconfig.build.json deleted file mode 100644 index d42923dd38c..00000000000 --- a/packages/connectors/templates/preset/tsconfig.build.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.base", - "include": ["src"], - "exclude": ["src/**/*.test.ts"] -} diff --git a/packages/connectors/templates/preset/tsconfig.json b/packages/connectors/templates/preset/tsconfig.json index 4fa2dd684aa..126a50cb0d9 100644 --- a/packages/connectors/templates/preset/tsconfig.json +++ b/packages/connectors/templates/preset/tsconfig.json @@ -1,7 +1,16 @@ { - "extends": "./tsconfig.base", + "extends": "@silverhand/ts-config/tsconfig.base", "compilerOptions": { - "types": ["node", "vitest/globals"] + "moduleResolution": "nodenext", + "module": "nodenext", + "outDir": "lib", + "baseUrl": ".", + "types": [ + "node", + "vitest/globals" + ] }, - "include": ["src", "types"] + "include": [ + "src" + ] } diff --git a/packages/connectors/templates/preset/tsconfig.test.json b/packages/connectors/templates/preset/tsconfig.test.json deleted file mode 100644 index 1424a155592..00000000000 --- a/packages/connectors/templates/preset/tsconfig.test.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "./tsconfig", - "compilerOptions": { - "isolatedModules": false, - "allowJs": true, - } -} diff --git a/packages/connectors/templates/preset/tsup.config.ts b/packages/connectors/templates/preset/tsup.config.ts new file mode 100644 index 00000000000..8e6ef8edb83 --- /dev/null +++ b/packages/connectors/templates/preset/tsup.config.ts @@ -0,0 +1,5 @@ +import { defineConfig } from 'tsup'; + +import { defaultConfig } from '../../../tsup.shared.config.js'; + +export default defineConfig(defaultConfig); diff --git a/packages/connectors/templates/sync-preset.js b/packages/connectors/templates/sync-preset.js index 32460417652..aa12dafb118 100644 --- a/packages/connectors/templates/sync-preset.js +++ b/packages/connectors/templates/sync-preset.js @@ -20,7 +20,7 @@ const templateKeys = Object.keys(templateJson); * Value format: `{ "": [""] }` * Example: `{ "connector-oauth2": ["prepack"] }` */ -const scriptExceptions = { 'connector-oauth2': ['prepack'] }; +const scriptExceptions = { 'connector-oauth2': ['prepack', 'build', 'build:test'] }; const sync = async () => { const packagesDirectory = './'; diff --git a/packages/console/.eslintrc.cjs b/packages/console/.eslintrc.cjs index f2cffeab3db..0997dc7409c 100644 --- a/packages/console/.eslintrc.cjs +++ b/packages/console/.eslintrc.cjs @@ -15,6 +15,7 @@ module.exports = { unnamedComponents: 'arrow-function', }, ], + 'react/jsx-pascal-case': ['error', { ignore: ['__Internal__*'] }], 'import/no-unused-modules': [ 'error', { @@ -30,6 +31,8 @@ module.exports = { '**/assets/docs/guides/*/index.ts', '**/assets/docs/guides/*/components/**/*.tsx', '**/mdx-components*/*/index.tsx', + '*.config.js', + '*.config.ts', ], rules: { 'import/no-unused-modules': 'off', @@ -49,5 +52,11 @@ module.exports = { ], }, }, + { + files: ['*.d.ts'], + rules: { + 'import/no-unassigned-import': 'off', + }, + }, ], }; diff --git a/packages/console/.parcelrc b/packages/console/.parcelrc deleted file mode 100644 index eeade8d2890..00000000000 --- a/packages/console/.parcelrc +++ /dev/null @@ -1,19 +0,0 @@ -{ - "extends": "@parcel/config-default", - "transformers": { - "raw:*": ["@parcel/transformer-raw"], - "**/assets/**/*.svg": [ - "@parcel/transformer-svg-react" - ], - "*.{md,mdx}": [ - "./parcel-transformer-mdx2.js" - ] - }, - "compressors": { - "*.{html,css,js,svg,map}": [ - "...", - "@parcel/compressor-gzip", - "@parcel/compressor-brotli" - ] - } -} diff --git a/packages/console/.parcelrc.arm64 b/packages/console/.parcelrc.arm64 deleted file mode 100644 index c31f639e17b..00000000000 --- a/packages/console/.parcelrc.arm64 +++ /dev/null @@ -1,23 +0,0 @@ -{ - "extends": "@parcel/config-default", - "optimizers": { - // Disable optimizers in arm64 arch https://github.com/parcel-bundler/parcel/issues/7402 - "*.{jpg,jpeg,png}": [] - }, - "transformers": { - "raw:*": ["@parcel/transformer-raw"], - "**/assets/**/*.svg": [ - "@parcel/transformer-svg-react" - ], - "*.{md,mdx}": [ - "./parcel-transformer-mdx2.js" - ] - }, - "compressors": { - "*.{html,css,js,svg,map}": [ - "...", - "@parcel/compressor-gzip", - "@parcel/compressor-brotli" - ] - } -} diff --git a/packages/console/CHANGELOG.md b/packages/console/CHANGELOG.md index a07f5892216..7898a683d93 100644 --- a/packages/console/CHANGELOG.md +++ b/packages/console/CHANGELOG.md @@ -1,5 +1,67 @@ # Change Log +## 1.17.0 + +### Minor Changes + +- 3a839f6d6: support organization logo and sign-in experience override + + Now it's able to set light and dark logos for organizations. You can upload the logos in the organization settings page. + + Also, it's possible to override the sign-in experience logo from an organization. Simply add the `organization_id` parameter to the authentication request. In most Logto SDKs, it can be done by using the `extraParams` field in the `signIn` method. + + For example, in the JavaScript SDK: + + ```ts + import LogtoClient from "@logto/client"; + + const logtoClient = new LogtoClient(/* your configuration */); + + logtoClient.signIn({ + redirectUri: "https://your-app.com/callback", + extraParams: { + organization_id: "", + }, + }); + ``` + + The value `` can be found in the organization settings page. + + If you could not find the `extraParams` field in the SDK you are using, please let us know. + +- b91ec0cd6: add the application `custom_data` field editor to the application details page in console +- 62f5e5e0c: support app-level branding + + You can now set logos, favicons, and colors for your app. These settings will be used in the sign-in experience when the app initiates the authentication flow. For apps that have no branding settings, the omni sign-in experience branding will be used. + + If `organization_id` is provided in the authentication request, the app-level branding settings will be overridden by the organization's branding settings, if available. + +- 3bf756f2b: use Vite for transpilation and bundling + + Removed ParcelJS and replaced with Vite. No breaking changes should be expected, but use a minor version bump to catch your attention. + + > [!Important] + > The browserlist configuration for `@logto/experience` and been synced with what is stated in README.md. + +- b188bb161: support multiple app secrets with expiration + + Now secure apps (machine-to-machine, traditional web, Protected) can have multiple app secrets with expiration. This allows for secret rotation and provides an even safer experience. + + To manage your application secrets, go to Logto Console -> Applications -> Application Details -> Endpoints & Credentials. + + We've also added a set of Management APIs (`/api/applications/{id}/secrets`) for this purpose. + + > [!Important] + > You can still use existing app secrets for client authentication, but it is recommended to delete the old ones and create new secrets with expiration for enhanced security. + +- 62f5e5e0c: support dark favicon + + The favicon for the dark theme now can be set in the sign-in experience branding settings. + +### Patch Changes + +- 3aa7e57b3: fix Google connector `scope` field can not be reset bug + ## 1.16.0 ### Minor Changes diff --git a/packages/console/src/index.html b/packages/console/index.html similarity index 51% rename from packages/console/src/index.html rename to packages/console/index.html index 91e6965ac8c..a5603fdf399 100644 --- a/packages/console/src/index.html +++ b/packages/console/index.html @@ -3,14 +3,14 @@ - - + +
- + diff --git a/packages/console/package.json b/packages/console/package.json index cda9fc37676..b93ee45baf9 100644 --- a/packages/console/package.json +++ b/packages/console/package.json @@ -1,6 +1,6 @@ { "name": "@logto/console", - "version": "1.16.0", + "version": "1.17.0", "description": "> TODO: description", "author": "Silverhand Inc. ", "homepage": "https://github.com/logto-io/logto#readme", @@ -14,10 +14,10 @@ "prepack": "pnpm generate", "generate": "./generate.sh", "precommit": "lint-staged", - "start": "parcel src/index.html", - "dev": "cross-env PORT=5002 parcel src/index.html --public-url ${CONSOLE_PUBLIC_URL:-/console} --no-cache --hmr-port 6002", + "start": "vite", + "dev": "vite", "check": "tsc --noEmit", - "build": "pnpm generate && pnpm check && rm -rf dist && parcel build src/index.html --no-autoinstall --no-cache --public-url ${CONSOLE_PUBLIC_URL:-/console}", + "build": "pnpm generate && vite build", "lint": "eslint --ext .ts --ext .tsx src", "lint:report": "pnpm lint --format json --output-file report.json", "stylelint": "stylelint \"src/**/*.scss\"", @@ -27,23 +27,19 @@ "devDependencies": { "@fontsource/roboto-mono": "^5.0.0", "@jest/types": "^29.5.0", - "@logto/cloud": "0.2.5-a7eedce", + "@logto/cloud": "0.2.5-923c26f", "@logto/connector-kit": "workspace:^4.0.0", "@logto/core-kit": "workspace:^2.5.0", + "@logto/elements": "workspace:^0.0.0", "@logto/language-kit": "workspace:^1.1.0", - "@logto/phrases": "workspace:^1.12.0", + "@logto/phrases": "workspace:^1.13.0", "@logto/phrases-experience": "workspace:^1.7.0", "@logto/react": "^3.0.12", - "@logto/schemas": "workspace:^1.18.0", + "@logto/schemas": "workspace:^1.19.0", "@logto/shared": "workspace:^3.1.1", - "@mdx-js/mdx": "^3.0.1", "@mdx-js/react": "^3.0.1", + "@mdx-js/rollup": "^3.0.1", "@monaco-editor/react": "^4.6.0", - "@parcel/compressor-brotli": "2.9.3", - "@parcel/compressor-gzip": "2.9.3", - "@parcel/core": "2.9.3", - "@parcel/transformer-sass": "2.9.3", - "@parcel/transformer-svg-react": "2.9.3", "@silverhand/eslint-config": "6.0.1", "@silverhand/eslint-config-react": "6.0.2", "@silverhand/essentials": "^2.9.1", @@ -56,31 +52,30 @@ "@types/debug": "^4.1.7", "@types/jest": "^29.4.0", "@types/mdx": "^2.0.13", - "@types/react": "^18.0.31", + "@types/react": "^18.3.3", "@types/react-color": "^3.0.6", - "@types/react-dom": "^18.0.0", + "@types/react-dom": "^18.3.0", "@types/react-helmet": "^6.1.6", "@types/react-modal": "^3.13.1", "@types/react-syntax-highlighter": "^15.5.1", + "@vitejs/plugin-react": "^4.3.1", "@withtyped/client": "^0.8.7", - "buffer": "^6.0.0", "classnames": "^2.3.1", "clean-deep": "^3.4.0", - "cross-env": "^7.0.3", - "csstype": "^3.0.11", "date-fns": "^2.29.3", "dayjs": "^1.10.5", "debug": "^4.3.4", "deep-object-diff": "^1.1.9", "deepmerge": "^4.2.2", "dnd-core": "^16.0.0", + "dotenv": "^16.4.5", "eslint": "^8.56.0", - "history": "^5.3.0", + "find-up": "^7.0.0", "i18next": "^22.4.15", "i18next-browser-languagedetector": "^8.0.0", "identity-obj-proxy": "^3.0.0", "jest": "^29.7.0", - "jest-environment-jsdom": "^29.0.0", + "jest-environment-jsdom": "^29.7.0", "jest-transform-stub": "^2.0.0", "jest-transformer-svg": "^2.0.0", "just-kebab-case": "^4.2.0", @@ -91,20 +86,18 @@ "nanoid": "^5.0.1", "overlayscrollbars": "^2.0.2", "overlayscrollbars-react": "^0.5.0", - "parcel": "2.9.3", - "postcss": "^8.4.31", + "postcss": "^8.4.39", "postcss-modules": "^4.3.0", "prettier": "^3.0.0", - "process": "^0.11.10", "prop-types": "^15.8.1", "property-information": "^6.2.0", - "react": "^18.0.0", + "react": "^18.3.1", "react-animate-height": "^3.0.4", - "react-color": "^2.19.3", + "react-color-palette": "^7.2.1", "react-confetti": "^6.1.0", "react-dnd": "^16.0.0", "react-dnd-html5-backend": "^16.0.0", - "react-dom": "^18.0.0", + "react-dom": "^18.3.1", "react-dropzone": "^14.2.3", "react-helmet": "^6.1.0", "react-hook-form": "^7.43.9", @@ -113,7 +106,8 @@ "react-markdown": "^9.0.0", "react-modal": "^3.15.1", "react-paginate": "^8.1.3", - "react-router-dom": "^6.10.0", + "react-router-dom": "^6.25.1", + "react-safe-lazy": "^0.1.0", "react-syntax-highlighter": "^15.5.0", "react-timer-hook": "^3.0.5", "recharts": "^2.1.13", @@ -121,27 +115,17 @@ "remark-gfm": "^4.0.0", "stylelint": "^15.0.0", "swr": "^2.2.0", - "ts-node": "^10.9.2", - "tslib": "^2.4.1", - "typescript": "^5.3.3", - "zod": "^3.22.4", + "typescript": "^5.5.3", + "vite": "^5.3.4", + "vite-plugin-compression": "^0.5.1", + "vite-plugin-prebundle": "^0.0.4", + "vite-plugin-svgr": "^4.2.0", + "zod": "^3.23.8", "zod-to-ts": "^1.2.0" }, "engines": { "node": "^20.9.0" }, - "//": "https://github.com/parcel-bundler/parcel/issues/7636", - "targets": { - "default": { - "engines": { - "browsers": "defaults" - } - } - }, - "alias": { - "@/*": "./src/$1", - "@cloud/*": "./src/cloud/$1" - }, "stylelint": { "extends": "@silverhand/eslint-config-react/.stylelintrc" }, diff --git a/packages/console/parcel-transformer-mdx2.js b/packages/console/parcel-transformer-mdx2.js deleted file mode 100644 index bd005897575..00000000000 --- a/packages/console/parcel-transformer-mdx2.js +++ /dev/null @@ -1,61 +0,0 @@ -// https://github.com/parcel-bundler/parcel/pull/7922#issuecomment-1750704973 - -import { compile } from '@mdx-js/mdx'; -import { default as ThrowableDiagnostic } from '@parcel/diagnostic'; -import { Transformer } from '@parcel/plugin'; -import rehypeMdxCodeProps from 'rehype-mdx-code-props'; -import remarkGfm from 'remark-gfm'; - -export default new Transformer({ - async transform({ asset }) { - const source = await asset.getCode(); - - let codeVFile; - - try { - codeVFile = await compile(source, { - development: true, - jsx: true, - providerImportSource: '@mdx-js/react', - remarkPlugins: [remarkGfm], - rehypePlugins: [[rehypeMdxCodeProps, { tagName: 'code' }]], - }); - } catch (error) { - const { start, end } = error.position; - - const highlight = { - message: error.reason, - start, - end, - }; - - if (!(end.line && end.column)) { - highlight.end = { ...start }; - } - - // Adjust for parser and reporter differences - highlight.start.column -= 1; - highlight.end.column -= 1; - - throw new ThrowableDiagnostic({ - diagnostic: { - message: 'Unable to compile MDX', - codeFrames: [ - { - filePath: asset.filePath, - code: source, - codeHighlights: [highlight], - }, - ], - }, - }); - } - - const code = String(codeVFile); - - asset.type = 'jsx'; - asset.setCode(code); - - return [asset]; - }, -}); diff --git a/packages/console/scripts/generate-jwt-customizer-type-definition.ts b/packages/console/scripts/generate-jwt-customizer-type-definition.ts index 9b25077a20b..e7f9af524d2 100644 --- a/packages/console/scripts/generate-jwt-customizer-type-definition.ts +++ b/packages/console/scripts/generate-jwt-customizer-type-definition.ts @@ -3,6 +3,7 @@ import fs from 'node:fs'; import { accessTokenPayloadGuard, clientCredentialsPayloadGuard, + jwtCustomizerGrantContextGuard, jwtCustomizerUserContextGuard, } from '@logto/schemas'; import prettier from 'prettier'; @@ -13,6 +14,7 @@ const filePath = 'src/consts/jwt-customizer-type-definition.ts'; const typeIdentifiers = `export enum JwtCustomizerTypeDefinitionKey { JwtCustomizerUserContext = 'JwtCustomizerUserContext', + JwtCustomizerGrantContext = 'JwtCustomizerGrantContext', AccessTokenPayload = 'AccessTokenPayload', ClientCredentialsPayload = 'ClientCredentialsPayload', EnvironmentVariables = 'EnvironmentVariables', @@ -43,6 +45,11 @@ const createJwtCustomizerTypeDefinitions = async () => { 'JwtCustomizerUserContext' ); + const jwtCustomizerGrantContextTypeDefinition = inferTsDefinitionFromZod( + jwtCustomizerGrantContextGuard, + 'JwtCustomizerGrantContext' + ); + const accessTokenPayloadTypeDefinition = inferTsDefinitionFromZod( accessTokenPayloadGuard, 'AccessTokenPayload' @@ -58,6 +65,8 @@ ${typeIdentifiers} export const jwtCustomizerUserContextTypeDefinition = \`${jwtCustomizerUserContextTypeDefinition}\`; +export const jwtCustomizerGrantContextTypeDefinition = \`${jwtCustomizerGrantContextTypeDefinition}\`; + export const accessTokenPayloadTypeDefinition = \`${accessTokenPayloadTypeDefinition}\`; export const clientCredentialsPayloadTypeDefinition = \`${clientCredentialsPayloadTypeDefinition}\`; diff --git a/packages/console/src/App.tsx b/packages/console/src/App.tsx index 96ff5ed713f..30830308c53 100644 --- a/packages/console/src/App.tsx +++ b/packages/console/src/App.tsx @@ -16,6 +16,8 @@ import './scss/normalized.scss'; import './scss/overlayscrollbars.scss'; // eslint-disable-next-line import/no-unassigned-import import '@fontsource/roboto-mono'; +// eslint-disable-next-line import/no-unassigned-import +import 'react-color-palette/css'; import CloudAppRoutes from '@/cloud/AppRoutes'; import AppLoading from '@/components/AppLoading'; diff --git a/packages/console/src/assets/docs/guides/README.md b/packages/console/src/assets/docs/guides/README.md index 29f0f0dea24..c2517133e50 100644 --- a/packages/console/src/assets/docs/guides/README.md +++ b/packages/console/src/assets/docs/guides/README.md @@ -52,6 +52,9 @@ Images and other assets (if any) should be placed in the `assets` directory of t ### Update metadata +> [!Note] +> This section is outdated and we should test if it's still necessary. + Since Parcel doesn't support dynamic import (see [#112](https://github.com/parcel-bundler/parcel/issues/112) [#125](https://github.com/parcel-bundler/parcel/issues/125)), we need to run `node generate-metadata.js` to update the metadata in `index.ts`, thus we can use it in the guide components with React lazy loading. This may be fixed by replacing Parcel with something else. diff --git a/packages/console/src/assets/docs/guides/api-express/logo-dark.svg b/packages/console/src/assets/docs/guides/api-express/logo-dark.svg new file mode 100644 index 00000000000..8ed50c5f2ab --- /dev/null +++ b/packages/console/src/assets/docs/guides/api-express/logo-dark.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/packages/console/src/assets/docs/guides/api-express/logo.svg b/packages/console/src/assets/docs/guides/api-express/logo.svg index bda5f80dabc..db674128014 100644 --- a/packages/console/src/assets/docs/guides/api-express/logo.svg +++ b/packages/console/src/assets/docs/guides/api-express/logo.svg @@ -1,3 +1,10 @@ - - + + + + + + + + + diff --git a/packages/console/src/assets/docs/guides/api-python/logo.svg b/packages/console/src/assets/docs/guides/api-python/logo.svg index c33c339ecb7..1d2ba15af8d 100644 --- a/packages/console/src/assets/docs/guides/api-python/logo.svg +++ b/packages/console/src/assets/docs/guides/api-python/logo.svg @@ -1,14 +1,19 @@ - - - + + + + + - + - + + + + diff --git a/packages/console/src/assets/docs/guides/api-spring-boot/logo.svg b/packages/console/src/assets/docs/guides/api-spring-boot/logo.svg index 55b425680de..2d4fe64ea1a 100644 --- a/packages/console/src/assets/docs/guides/api-spring-boot/logo.svg +++ b/packages/console/src/assets/docs/guides/api-spring-boot/logo.svg @@ -1,10 +1,10 @@ - - - + + + - - + + diff --git a/packages/console/src/assets/docs/guides/generate-metadata.js b/packages/console/src/assets/docs/guides/generate-metadata.js index d1ca726d995..adc18258106 100644 --- a/packages/console/src/assets/docs/guides/generate-metadata.js +++ b/packages/console/src/assets/docs/guides/generate-metadata.js @@ -1,4 +1,4 @@ -// This script generates the `index.ts` file for all the guides. +// This script generates the `index.tsx` file for all the guides. // It should be run from the `packages/console/src/assets/docs/guides` (current) directory. // For conventions and specifications, see the `README.md` file in this directory. @@ -21,6 +21,9 @@ const data = await Promise.all( } const logo = ['logo.webp', 'logo.svg', 'logo.png'].find((logo) => existsSync(`${directory}/${logo}`)); + const darkLogo = ['logo-dark.webp', 'logo-dark.svg', 'logo-dark.png'].find((logo) => + existsSync(`${directory}/${logo}`) + ); const config = existsSync(`${directory}/config.json`) ? await import(`./${directory}/config.json`, { assert: { type: 'json' } }).then( @@ -31,6 +34,7 @@ const data = await Promise.all( return { name: directory, logo, + darkLogo, order: config?.order ?? Number.POSITIVE_INFINITY, }; }) @@ -45,10 +49,11 @@ const filename = 'index.tsx'; await fs.writeFile( filename, - "// This is a generated file, don't update manually.\n\nimport { lazy } from 'react';\n\nimport { type Guide } from './types';\n" + `/* eslint-disable max-lines */ +// This is a generated file, don't update manually.\n\nimport { lazy } from 'react';\n\nimport { type Guide } from './types';\n` ); -for (const { name, logo } of metadata) { +for (const { name, logo, darkLogo } of metadata) { // eslint-disable-next-line no-await-in-loop await fs.appendFile(filename, `import ${camelCase(name)} from './${name}/index';\n`); @@ -56,18 +61,23 @@ for (const { name, logo } of metadata) { // eslint-disable-next-line no-await-in-loop await fs.appendFile(filename, `import ${camelCase(name)}Logo from './${name}/${logo}';\n`); } + + if (darkLogo && !darkLogo.endsWith('.svg')) { + // eslint-disable-next-line no-await-in-loop + await fs.appendFile(filename, `import ${camelCase(name)}LogoDark from './${name}/${darkLogo}';\n`); + } } await fs.appendFile(filename, '\n'); await fs.appendFile(filename, 'export const guides: Readonly = Object.freeze(['); -const getLogo = ({ name, logo }) => { +const getLogo = ({ name, logo, isDark }) => { if (!logo) return 'undefined'; - if (logo.endsWith('.svg')) return `lazy(async () => import('./${name}/${logo}'))`; - return `({ className }: { readonly className?: string }) => ${name}`; + if (logo.endsWith('.svg')) return `lazy(async () => import('./${name}/${logo}?react'))`; + return `({ className }: { readonly className?: string }) => ${name}`; }; -for (const { name, logo, order } of metadata) { +for (const { name, logo, darkLogo, order } of metadata) { // eslint-disable-next-line no-await-in-loop await fs.appendFile( filename, @@ -76,10 +86,12 @@ for (const { name, logo, order } of metadata) { order: ${order}, id: '${name}', Logo: ${getLogo({ name, logo })}, + DarkLogo: ${getLogo({ name, logo: darkLogo, isDark: true })}, Component: lazy(async () => import('./${name}/README.mdx')), metadata: ${camelCase(name)}, },` ); } -await fs.appendFile(filename, ']);\n'); +await fs.appendFile(filename, `]); +/* eslint-enable max-lines */\n`); diff --git a/packages/console/src/assets/docs/guides/index.tsx b/packages/console/src/assets/docs/guides/index.tsx index d83d09817d4..d66e9699d27 100644 --- a/packages/console/src/assets/docs/guides/index.tsx +++ b/packages/console/src/assets/docs/guides/index.tsx @@ -1,3 +1,4 @@ +/* eslint-disable max-lines */ // This is a generated file, don't update manually. import { lazy } from 'react'; @@ -32,10 +33,10 @@ import webNextAppRouter from './web-next-app-router/index'; import webNextAuth from './web-next-auth/index'; import webNuxt from './web-nuxt/index'; import webOutline from './web-outline/index'; +import webPassport from './web-passport/index'; import webPhp from './web-php/index'; import webPython from './web-python/index'; import webRuby from './web-ruby/index'; -import webRubyLogo from './web-ruby/logo.webp'; import webSveltekit from './web-sveltekit/index'; import webWordpress from './web-wordpress/index'; @@ -43,241 +44,282 @@ export const guides: Readonly = Object.freeze([ { order: 1, id: 'web-next-app-router', - Logo: lazy(async () => import('./web-next-app-router/logo.svg')), + Logo: lazy(async () => import('./web-next-app-router/logo.svg?react')), + DarkLogo: lazy(async () => import('./web-next-app-router/logo-dark.svg?react')), Component: lazy(async () => import('./web-next-app-router/README.mdx')), metadata: webNextAppRouter, }, { order: 1.1, id: 'native-expo', - Logo: lazy(async () => import('./native-expo/logo.svg')), + Logo: lazy(async () => import('./native-expo/logo.svg?react')), + DarkLogo: lazy(async () => import('./native-expo/logo-dark.svg?react')), Component: lazy(async () => import('./native-expo/README.mdx')), metadata: nativeExpo, }, { order: 1.1, id: 'spa-angular', - Logo: lazy(async () => import('./spa-angular/logo.svg')), + Logo: lazy(async () => import('./spa-angular/logo.svg?react')), + DarkLogo: undefined, Component: lazy(async () => import('./spa-angular/README.mdx')), metadata: spaAngular, }, { order: 1.1, id: 'spa-chrome-extension', - Logo: lazy(async () => import('./spa-chrome-extension/logo.svg')), + Logo: lazy(async () => import('./spa-chrome-extension/logo.svg?react')), + DarkLogo: undefined, Component: lazy(async () => import('./spa-chrome-extension/README.mdx')), metadata: spaChromeExtension, }, { order: 1.1, id: 'spa-react', - Logo: lazy(async () => import('./spa-react/logo.svg')), + Logo: lazy(async () => import('./spa-react/logo.svg?react')), + DarkLogo: undefined, Component: lazy(async () => import('./spa-react/README.mdx')), metadata: spaReact, }, { order: 1.2, id: 'm2m-general', - Logo: lazy(async () => import('./m2m-general/logo.svg')), + Logo: lazy(async () => import('./m2m-general/logo.svg?react')), + DarkLogo: lazy(async () => import('./m2m-general/logo-dark.svg?react')), Component: lazy(async () => import('./m2m-general/README.mdx')), metadata: m2mGeneral, }, { order: 1.2, id: 'web-express', - Logo: lazy(async () => import('./web-express/logo.svg')), + Logo: lazy(async () => import('./web-express/logo.svg?react')), + DarkLogo: lazy(async () => import('./web-express/logo-dark.svg?react')), Component: lazy(async () => import('./web-express/README.mdx')), metadata: webExpress, }, { order: 1.2, id: 'web-next', - Logo: lazy(async () => import('./web-next/logo.svg')), + Logo: lazy(async () => import('./web-next/logo.svg?react')), + DarkLogo: lazy(async () => import('./web-next/logo-dark.svg?react')), Component: lazy(async () => import('./web-next/README.mdx')), metadata: webNext, }, { order: 1.2, id: 'web-sveltekit', - Logo: lazy(async () => import('./web-sveltekit/logo.svg')), + Logo: lazy(async () => import('./web-sveltekit/logo.svg?react')), + DarkLogo: undefined, Component: lazy(async () => import('./web-sveltekit/README.mdx')), metadata: webSveltekit, }, { order: 1.3, id: 'web-go', - Logo: lazy(async () => import('./web-go/logo.svg')), + Logo: lazy(async () => import('./web-go/logo.svg?react')), + DarkLogo: undefined, Component: lazy(async () => import('./web-go/README.mdx')), metadata: webGo, }, { order: 1.3, id: 'web-next-auth', - Logo: lazy(async () => import('./web-next-auth/logo.svg')), + Logo: lazy(async () => import('./web-next-auth/logo.svg?react')), + DarkLogo: undefined, Component: lazy(async () => import('./web-next-auth/README.mdx')), metadata: webNextAuth, }, { order: 1.4, id: 'web-java-spring-boot', - Logo: lazy(async () => import('./web-java-spring-boot/logo.svg')), + Logo: lazy(async () => import('./web-java-spring-boot/logo.svg?react')), + DarkLogo: undefined, Component: lazy(async () => import('./web-java-spring-boot/README.mdx')), metadata: webJavaSpringBoot, }, { order: 1.6, id: 'spa-vue', - Logo: lazy(async () => import('./spa-vue/logo.svg')), + Logo: lazy(async () => import('./spa-vue/logo.svg?react')), + DarkLogo: undefined, Component: lazy(async () => import('./spa-vue/README.mdx')), metadata: spaVue, }, { order: 1.7, id: 'native-ios-swift', - Logo: lazy(async () => import('./native-ios-swift/logo.svg')), + Logo: lazy(async () => import('./native-ios-swift/logo.svg?react')), + DarkLogo: undefined, Component: lazy(async () => import('./native-ios-swift/README.mdx')), metadata: nativeIosSwift, }, { order: 2, id: 'native-android', - Logo: lazy(async () => import('./native-android/logo.svg')), + Logo: lazy(async () => import('./native-android/logo.svg?react')), + DarkLogo: undefined, Component: lazy(async () => import('./native-android/README.mdx')), metadata: nativeAndroid, }, { order: 2, id: 'spa-vanilla', - Logo: lazy(async () => import('./spa-vanilla/logo.svg')), + Logo: lazy(async () => import('./spa-vanilla/logo.svg?react')), + DarkLogo: undefined, Component: lazy(async () => import('./spa-vanilla/README.mdx')), metadata: spaVanilla, }, { order: 2, id: 'web-nuxt', - Logo: lazy(async () => import('./web-nuxt/logo.svg')), + Logo: lazy(async () => import('./web-nuxt/logo.svg?react')), + DarkLogo: undefined, Component: lazy(async () => import('./web-nuxt/README.mdx')), metadata: webNuxt, }, { order: 2, id: 'web-php', - Logo: lazy(async () => import('./web-php/logo.svg')), + Logo: lazy(async () => import('./web-php/logo.svg?react')), + DarkLogo: undefined, Component: lazy(async () => import('./web-php/README.mdx')), metadata: webPhp, }, { order: 2, id: 'web-ruby', - Logo: ({ className }: { readonly className?: string }) => ( - web-ruby - ), + Logo: lazy(async () => import('./web-ruby/logo.svg?react')), + DarkLogo: undefined, Component: lazy(async () => import('./web-ruby/README.mdx')), metadata: webRuby, }, { order: 2.1, id: 'spa-webflow', - Logo: lazy(async () => import('./spa-webflow/logo.svg')), + Logo: lazy(async () => import('./spa-webflow/logo.svg?react')), + DarkLogo: undefined, Component: lazy(async () => import('./spa-webflow/README.mdx')), metadata: spaWebflow, }, { order: 2.2, id: 'web-wordpress', - Logo: lazy(async () => import('./web-wordpress/logo.svg')), + Logo: lazy(async () => import('./web-wordpress/logo.svg?react')), + DarkLogo: undefined, Component: lazy(async () => import('./web-wordpress/README.mdx')), metadata: webWordpress, }, { order: 3, id: 'web-python', - Logo: lazy(async () => import('./web-python/logo.svg')), + Logo: lazy(async () => import('./web-python/logo.svg?react')), + DarkLogo: undefined, Component: lazy(async () => import('./web-python/README.mdx')), metadata: webPython, }, { order: 4, id: 'native-capacitor', - Logo: lazy(async () => import('./native-capacitor/logo.svg')), + Logo: lazy(async () => import('./native-capacitor/logo.svg?react')), + DarkLogo: undefined, Component: lazy(async () => import('./native-capacitor/README.mdx')), metadata: nativeCapacitor, }, { order: 5, id: 'native-flutter', - Logo: lazy(async () => import('./native-flutter/logo.svg')), + Logo: lazy(async () => import('./native-flutter/logo.svg?react')), + DarkLogo: undefined, Component: lazy(async () => import('./native-flutter/README.mdx')), metadata: nativeFlutter, }, { order: 5, id: 'web-dotnet-core', - Logo: lazy(async () => import('./web-dotnet-core/logo.svg')), + Logo: lazy(async () => import('./web-dotnet-core/logo.svg?react')), + DarkLogo: undefined, Component: lazy(async () => import('./web-dotnet-core/README.mdx')), metadata: webDotnetCore, }, { order: 5.1, id: 'web-dotnet-core-mvc', - Logo: lazy(async () => import('./web-dotnet-core-mvc/logo.svg')), + Logo: lazy(async () => import('./web-dotnet-core-mvc/logo.svg?react')), + DarkLogo: undefined, Component: lazy(async () => import('./web-dotnet-core-mvc/README.mdx')), metadata: webDotnetCoreMvc, }, { order: 5.2, id: 'web-dotnet-core-blazor-server', - Logo: lazy(async () => import('./web-dotnet-core-blazor-server/logo.svg')), + Logo: lazy(async () => import('./web-dotnet-core-blazor-server/logo.svg?react')), + DarkLogo: undefined, Component: lazy(async () => import('./web-dotnet-core-blazor-server/README.mdx')), metadata: webDotnetCoreBlazorServer, }, { order: 5.3, id: 'web-dotnet-core-blazor-wasm', - Logo: lazy(async () => import('./web-dotnet-core-blazor-wasm/logo.svg')), + Logo: lazy(async () => import('./web-dotnet-core-blazor-wasm/logo.svg?react')), + DarkLogo: undefined, Component: lazy(async () => import('./web-dotnet-core-blazor-wasm/README.mdx')), metadata: webDotnetCoreBlazorWasm, }, { order: 6, id: 'web-outline', - Logo: lazy(async () => import('./web-outline/logo.svg')), + Logo: lazy(async () => import('./web-outline/logo.svg?react')), + DarkLogo: lazy(async () => import('./web-outline/logo-dark.svg?react')), Component: lazy(async () => import('./web-outline/README.mdx')), metadata: webOutline, }, + { + order: 6.1, + id: 'web-passport', + Logo: lazy(async () => import('./web-passport/logo.svg?react')), + DarkLogo: lazy(async () => import('./web-passport/logo-dark.svg?react')), + Component: lazy(async () => import('./web-passport/README.mdx')), + metadata: webPassport, + }, { order: 999, id: 'web-gpt-plugin', - Logo: lazy(async () => import('./web-gpt-plugin/logo.svg')), + Logo: lazy(async () => import('./web-gpt-plugin/logo.svg?react')), + DarkLogo: undefined, Component: lazy(async () => import('./web-gpt-plugin/README.mdx')), metadata: webGptPlugin, }, { order: Number.POSITIVE_INFINITY, id: 'api-express', - Logo: lazy(async () => import('./api-express/logo.svg')), + Logo: lazy(async () => import('./api-express/logo.svg?react')), + DarkLogo: lazy(async () => import('./api-express/logo-dark.svg?react')), Component: lazy(async () => import('./api-express/README.mdx')), metadata: apiExpress, }, { order: Number.POSITIVE_INFINITY, id: 'api-python', - Logo: lazy(async () => import('./api-python/logo.svg')), + Logo: lazy(async () => import('./api-python/logo.svg?react')), + DarkLogo: undefined, Component: lazy(async () => import('./api-python/README.mdx')), metadata: apiPython, }, { order: Number.POSITIVE_INFINITY, id: 'api-spring-boot', - Logo: lazy(async () => import('./api-spring-boot/logo.svg')), + Logo: lazy(async () => import('./api-spring-boot/logo.svg?react')), + DarkLogo: undefined, Component: lazy(async () => import('./api-spring-boot/README.mdx')), metadata: apiSpringBoot, }, { order: Number.POSITIVE_INFINITY, id: 'third-party-oidc', - Logo: lazy(async () => import('./third-party-oidc/logo.svg')), + Logo: lazy(async () => import('./third-party-oidc/logo.svg?react')), + DarkLogo: undefined, Component: lazy(async () => import('./third-party-oidc/README.mdx')), metadata: thirdPartyOidc, }, ]); +/* eslint-enable max-lines */ diff --git a/packages/console/src/assets/docs/guides/m2m-general/logo-dark.svg b/packages/console/src/assets/docs/guides/m2m-general/logo-dark.svg new file mode 100644 index 00000000000..7aaf3ec6f52 --- /dev/null +++ b/packages/console/src/assets/docs/guides/m2m-general/logo-dark.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/console/src/assets/docs/guides/m2m-general/logo.svg b/packages/console/src/assets/docs/guides/m2m-general/logo.svg index a2a63b7f663..5ef0d8edd3b 100644 --- a/packages/console/src/assets/docs/guides/m2m-general/logo.svg +++ b/packages/console/src/assets/docs/guides/m2m-general/logo.svg @@ -1,35 +1,35 @@ - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - + - + - + diff --git a/packages/console/src/assets/docs/guides/native-android/logo.svg b/packages/console/src/assets/docs/guides/native-android/logo.svg index 63418a92e23..04c799220d0 100644 --- a/packages/console/src/assets/docs/guides/native-android/logo.svg +++ b/packages/console/src/assets/docs/guides/native-android/logo.svg @@ -1,4 +1,11 @@ - - - + + + + + + + + + + diff --git a/packages/console/src/assets/docs/guides/native-capacitor/logo.svg b/packages/console/src/assets/docs/guides/native-capacitor/logo.svg index e8c04da0996..84f1647a659 100644 --- a/packages/console/src/assets/docs/guides/native-capacitor/logo.svg +++ b/packages/console/src/assets/docs/guides/native-capacitor/logo.svg @@ -1,8 +1,15 @@ - - - - - - - + + + + + + + + + + + + + + diff --git a/packages/console/src/assets/docs/guides/native-expo/logo-dark.svg b/packages/console/src/assets/docs/guides/native-expo/logo-dark.svg new file mode 100644 index 00000000000..78413920df1 --- /dev/null +++ b/packages/console/src/assets/docs/guides/native-expo/logo-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/console/src/assets/docs/guides/native-expo/logo.svg b/packages/console/src/assets/docs/guides/native-expo/logo.svg index 785dbbd164f..df2edaf0de0 100644 --- a/packages/console/src/assets/docs/guides/native-expo/logo.svg +++ b/packages/console/src/assets/docs/guides/native-expo/logo.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + diff --git a/packages/console/src/assets/docs/guides/native-flutter/logo.svg b/packages/console/src/assets/docs/guides/native-flutter/logo.svg index ad50e5eb043..90e5276fc46 100644 --- a/packages/console/src/assets/docs/guides/native-flutter/logo.svg +++ b/packages/console/src/assets/docs/guides/native-flutter/logo.svg @@ -1,18 +1,28 @@ - - - - - - - + + + + + + + + + + + - + - + + + + + + + diff --git a/packages/console/src/assets/docs/guides/native-ios-swift/logo.svg b/packages/console/src/assets/docs/guides/native-ios-swift/logo.svg index 1f273709d07..5bc1e96e120 100644 --- a/packages/console/src/assets/docs/guides/native-ios-swift/logo.svg +++ b/packages/console/src/assets/docs/guides/native-ios-swift/logo.svg @@ -1,4 +1,11 @@ - - - + + + + + + + + + + diff --git a/packages/console/src/assets/docs/guides/spa-angular/logo.svg b/packages/console/src/assets/docs/guides/spa-angular/logo.svg index 96301efe1b4..e53c94dfce2 100644 --- a/packages/console/src/assets/docs/guides/spa-angular/logo.svg +++ b/packages/console/src/assets/docs/guides/spa-angular/logo.svg @@ -1,16 +1,12 @@ - - - - - - - - - - + + + + + + + + + + + + diff --git a/packages/console/src/assets/docs/guides/spa-chrome-extension/logo.svg b/packages/console/src/assets/docs/guides/spa-chrome-extension/logo.svg index 4fc53255e9c..e658a11e462 100644 --- a/packages/console/src/assets/docs/guides/spa-chrome-extension/logo.svg +++ b/packages/console/src/assets/docs/guides/spa-chrome-extension/logo.svg @@ -1,26 +1,21 @@ - - - - - - - - + + + + + + - + - + - + - - - diff --git a/packages/console/src/assets/docs/guides/spa-react/logo.svg b/packages/console/src/assets/docs/guides/spa-react/logo.svg index eccfaa961b4..7d1653573a7 100644 --- a/packages/console/src/assets/docs/guides/spa-react/logo.svg +++ b/packages/console/src/assets/docs/guides/spa-react/logo.svg @@ -1,3 +1,10 @@ - - + + + + + + + + + diff --git a/packages/console/src/assets/docs/guides/spa-vanilla/logo.svg b/packages/console/src/assets/docs/guides/spa-vanilla/logo.svg index 2b0ce5c72c2..ce73eb180b0 100644 --- a/packages/console/src/assets/docs/guides/spa-vanilla/logo.svg +++ b/packages/console/src/assets/docs/guides/spa-vanilla/logo.svg @@ -1,12 +1,12 @@ - - - - - + + + + + - - + + diff --git a/packages/console/src/assets/docs/guides/spa-vue/logo.svg b/packages/console/src/assets/docs/guides/spa-vue/logo.svg index d6a24d4938e..a71f46129a5 100644 --- a/packages/console/src/assets/docs/guides/spa-vue/logo.svg +++ b/packages/console/src/assets/docs/guides/spa-vue/logo.svg @@ -1,4 +1,4 @@ - - - + + + diff --git a/packages/console/src/assets/docs/guides/spa-webflow/logo.svg b/packages/console/src/assets/docs/guides/spa-webflow/logo.svg index e5df00dfe65..c4ffbd645e9 100644 --- a/packages/console/src/assets/docs/guides/spa-webflow/logo.svg +++ b/packages/console/src/assets/docs/guides/spa-webflow/logo.svg @@ -1,10 +1,3 @@ - - - - - - - - - + + diff --git a/packages/console/src/assets/docs/guides/third-party-oidc/logo.svg b/packages/console/src/assets/docs/guides/third-party-oidc/logo.svg index 0958f5836e9..3ef36eaf00c 100644 --- a/packages/console/src/assets/docs/guides/third-party-oidc/logo.svg +++ b/packages/console/src/assets/docs/guides/third-party-oidc/logo.svg @@ -1,12 +1,11 @@ - - - - - - - - - - + + + + + + + + + + diff --git a/packages/console/src/assets/docs/guides/types.ts b/packages/console/src/assets/docs/guides/types.ts index 0b18523c99e..4e6f0b29433 100644 --- a/packages/console/src/assets/docs/guides/types.ts +++ b/packages/console/src/assets/docs/guides/types.ts @@ -49,6 +49,9 @@ export type Guide = { Logo: | LazyExoticComponent | ((props: { readonly className?: string }) => JSX.Element); + DarkLogo?: + | LazyExoticComponent + | ((props: { readonly className?: string }) => JSX.Element); Component: LazyExoticComponent>; metadata: Readonly; }; diff --git a/packages/console/src/assets/docs/guides/web-dotnet-core-blazor-server/logo.svg b/packages/console/src/assets/docs/guides/web-dotnet-core-blazor-server/logo.svg index 29dc61d07a8..16f6b42fd71 100644 --- a/packages/console/src/assets/docs/guides/web-dotnet-core-blazor-server/logo.svg +++ b/packages/console/src/assets/docs/guides/web-dotnet-core-blazor-server/logo.svg @@ -1,6 +1,4 @@ - - - - - + + + diff --git a/packages/console/src/assets/docs/guides/web-dotnet-core-blazor-wasm/logo.svg b/packages/console/src/assets/docs/guides/web-dotnet-core-blazor-wasm/logo.svg index 29dc61d07a8..16f6b42fd71 100644 --- a/packages/console/src/assets/docs/guides/web-dotnet-core-blazor-wasm/logo.svg +++ b/packages/console/src/assets/docs/guides/web-dotnet-core-blazor-wasm/logo.svg @@ -1,6 +1,4 @@ - - - - - + + + diff --git a/packages/console/src/assets/docs/guides/web-dotnet-core-mvc/fragments/_add-authentication.mdx b/packages/console/src/assets/docs/guides/web-dotnet-core-mvc/fragments/_add-authentication.mdx index 34b1fe74690..62a183eab90 100644 --- a/packages/console/src/assets/docs/guides/web-dotnet-core-mvc/fragments/_add-authentication.mdx +++ b/packages/console/src/assets/docs/guides/web-dotnet-core-mvc/fragments/_add-authentication.mdx @@ -9,7 +9,7 @@ builder.Services.AddLogtoAuthentication(options => { options.Endpoint = "${props.endpoint}"; options.AppId = "${props.app.id}"; - options.AppSecret = "${props.app.secret}"; + options.AppSecret = "${props.secrets[0]?.value ?? props.app.secret}"; }); app.UseAuthentication();`} diff --git a/packages/console/src/assets/docs/guides/web-dotnet-core-mvc/logo.svg b/packages/console/src/assets/docs/guides/web-dotnet-core-mvc/logo.svg index d04bf0477bf..3aa3a41c122 100644 --- a/packages/console/src/assets/docs/guides/web-dotnet-core-mvc/logo.svg +++ b/packages/console/src/assets/docs/guides/web-dotnet-core-mvc/logo.svg @@ -1,2 +1,12 @@ - -logo_NETcore \ No newline at end of file + + + + + + + + + + + + diff --git a/packages/console/src/assets/docs/guides/web-dotnet-core/logo.svg b/packages/console/src/assets/docs/guides/web-dotnet-core/logo.svg index d04bf0477bf..3aa3a41c122 100644 --- a/packages/console/src/assets/docs/guides/web-dotnet-core/logo.svg +++ b/packages/console/src/assets/docs/guides/web-dotnet-core/logo.svg @@ -1,2 +1,12 @@ - -logo_NETcore \ No newline at end of file + + + + + + + + + + + + diff --git a/packages/console/src/assets/docs/guides/web-express/README.mdx b/packages/console/src/assets/docs/guides/web-express/README.mdx index 4325a964589..9fcb5f7a336 100644 --- a/packages/console/src/assets/docs/guides/web-express/README.mdx +++ b/packages/console/src/assets/docs/guides/web-express/README.mdx @@ -31,7 +31,7 @@ Prepare configuration for the Logto client: const config: LogtoExpressConfig = { endpoint: '${props.endpoint}', appId: '${props.app.id}', - appSecret: '${props.app.secret}', + appSecret: '${props.secrets[0]?.value ?? props.app.secret}', baseUrl: 'http://localhost:3000', // Change to your own base URL }; `} diff --git a/packages/console/src/assets/docs/guides/web-express/logo-dark.svg b/packages/console/src/assets/docs/guides/web-express/logo-dark.svg new file mode 100644 index 00000000000..8ed50c5f2ab --- /dev/null +++ b/packages/console/src/assets/docs/guides/web-express/logo-dark.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/packages/console/src/assets/docs/guides/web-express/logo.svg b/packages/console/src/assets/docs/guides/web-express/logo.svg index bda5f80dabc..db674128014 100644 --- a/packages/console/src/assets/docs/guides/web-express/logo.svg +++ b/packages/console/src/assets/docs/guides/web-express/logo.svg @@ -1,3 +1,10 @@ - - + + + + + + + + + diff --git a/packages/console/src/assets/docs/guides/web-go/README.mdx b/packages/console/src/assets/docs/guides/web-go/README.mdx index 26e34567b4a..478f88527a2 100644 --- a/packages/console/src/assets/docs/guides/web-go/README.mdx +++ b/packages/console/src/assets/docs/guides/web-go/README.mdx @@ -145,7 +145,7 @@ First, create a Logto config: logtoConfig := &client.LogtoConfig{ Endpoint: "${props.endpoint}", AppId: "${props.app.id}", - AppSecret: "${props.app.secret}", + AppSecret: "${props.secrets[0]?.value ?? props.app.secret}", } // ... diff --git a/packages/console/src/assets/docs/guides/web-go/logo.svg b/packages/console/src/assets/docs/guides/web-go/logo.svg index 35afcc5967c..ba1ff3e67aa 100644 --- a/packages/console/src/assets/docs/guides/web-go/logo.svg +++ b/packages/console/src/assets/docs/guides/web-go/logo.svg @@ -1,6 +1,6 @@ - - - - - + + + + + diff --git a/packages/console/src/assets/docs/guides/web-gpt-plugin/components/AlwaysIssueRefreshToken.tsx b/packages/console/src/assets/docs/guides/web-gpt-plugin/components/AlwaysIssueRefreshToken.tsx index 858c8fe0f8d..9a446392cda 100644 --- a/packages/console/src/assets/docs/guides/web-gpt-plugin/components/AlwaysIssueRefreshToken.tsx +++ b/packages/console/src/assets/docs/guides/web-gpt-plugin/components/AlwaysIssueRefreshToken.tsx @@ -23,6 +23,7 @@ export default function AlwaysIssueRefreshToken() { await api.patch(`api/applications/${app.id}`, { json: { customClientMetadata: { + ...app.customClientMetadata, alwaysIssueRefreshToken: value, }, }, diff --git a/packages/console/src/assets/docs/guides/web-gpt-plugin/components/ClientBasics/index.tsx b/packages/console/src/assets/docs/guides/web-gpt-plugin/components/ClientBasics/index.tsx index c7fae219f79..b5ab4e70ba7 100644 --- a/packages/console/src/assets/docs/guides/web-gpt-plugin/components/ClientBasics/index.tsx +++ b/packages/console/src/assets/docs/guides/web-gpt-plugin/components/ClientBasics/index.tsx @@ -4,7 +4,7 @@ import { GuideContext } from '@/components/Guide'; import CopyToClipboard from '@/ds-components/CopyToClipboard'; import FormField from '@/ds-components/FormField'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; export default function ClientBasics() { const { app } = useContext(GuideContext); diff --git a/packages/console/src/assets/docs/guides/web-gpt-plugin/logo.svg b/packages/console/src/assets/docs/guides/web-gpt-plugin/logo.svg index b153d5dee60..0c89371dfdf 100644 --- a/packages/console/src/assets/docs/guides/web-gpt-plugin/logo.svg +++ b/packages/console/src/assets/docs/guides/web-gpt-plugin/logo.svg @@ -1,11 +1,11 @@ - - - - + + + + - - + + diff --git a/packages/console/src/assets/docs/guides/web-java-spring-boot/README.mdx b/packages/console/src/assets/docs/guides/web-java-spring-boot/README.mdx index 92625af90a4..80f83361dc6 100644 --- a/packages/console/src/assets/docs/guides/web-java-spring-boot/README.mdx +++ b/packages/console/src/assets/docs/guides/web-java-spring-boot/README.mdx @@ -63,7 +63,7 @@ Add the following configuration to your `application.properties` file: {`spring.security.oauth2.client.registration.logto.client-name=logto spring.security.oauth2.client.registration.logto.client-id=${props.app.id} -spring.security.oauth2.client.registration.logto.client-secret=${props.app.secret} +spring.security.oauth2.client.registration.logto.client-secret=${props.secrets[0]?.value ?? props.app.secret} spring.security.oauth2.client.registration.logto.redirect-uri={baseUrl}/login/oauth2/code/{registrationId} spring.security.oauth2.client.registration.logto.authorization-grant-type=authorization_code spring.security.oauth2.client.registration.logto.scope=openid,profile,email,offline_access diff --git a/packages/console/src/assets/docs/guides/web-java-spring-boot/logo.svg b/packages/console/src/assets/docs/guides/web-java-spring-boot/logo.svg index d7256ddcf41..2d4fe64ea1a 100644 --- a/packages/console/src/assets/docs/guides/web-java-spring-boot/logo.svg +++ b/packages/console/src/assets/docs/guides/web-java-spring-boot/logo.svg @@ -1 +1,10 @@ - + + + + + + + + + + diff --git a/packages/console/src/assets/docs/guides/web-next-app-router/README.mdx b/packages/console/src/assets/docs/guides/web-next-app-router/README.mdx index 5c6196036df..1ca7e409158 100644 --- a/packages/console/src/assets/docs/guides/web-next-app-router/README.mdx +++ b/packages/console/src/assets/docs/guides/web-next-app-router/README.mdx @@ -26,7 +26,7 @@ Prepare configuration for the Logto client: {`export const logtoConfig = { endpoint: '${props.endpoint}', appId: '${props.app.id}', - appSecret: '${props.app.secret}', + appSecret: '${props.secrets[0]?.value ?? props.app.secret}', baseUrl: 'http://localhost:3000', // Change to your own base URL cookieSecret: '${generateStandardSecret()}', // Auto-generated 32 digit secret cookieSecure: process.env.NODE_ENV === 'production', diff --git a/packages/console/src/assets/docs/guides/web-next-app-router/logo-dark.svg b/packages/console/src/assets/docs/guides/web-next-app-router/logo-dark.svg new file mode 100644 index 00000000000..69d2a9cd56d --- /dev/null +++ b/packages/console/src/assets/docs/guides/web-next-app-router/logo-dark.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/packages/console/src/assets/docs/guides/web-next-app-router/logo.svg b/packages/console/src/assets/docs/guides/web-next-app-router/logo.svg index c1db01c06b7..252c045953d 100644 --- a/packages/console/src/assets/docs/guides/web-next-app-router/logo.svg +++ b/packages/console/src/assets/docs/guides/web-next-app-router/logo.svg @@ -1,10 +1,3 @@ - - - - - - - - - + + diff --git a/packages/console/src/assets/docs/guides/web-next-auth/README.mdx b/packages/console/src/assets/docs/guides/web-next-auth/README.mdx index badcf966507..2911141745d 100644 --- a/packages/console/src/assets/docs/guides/web-next-auth/README.mdx +++ b/packages/console/src/assets/docs/guides/web-next-auth/README.mdx @@ -2,6 +2,8 @@ import InlineNotification from '@/ds-components/InlineNotification'; import UriInputField from '@/mdx-components/UriInputField'; import Steps from '@/mdx-components/Steps'; import Step from '@/mdx-components/Step'; +import TabItem from '@/mdx-components/TabItem'; +import Tabs from '@/mdx-components/Tabs'; @@ -31,9 +33,43 @@ Modify your API route config of Next Auth, if you are using Pages Router, the fi The following is an example of App Router: - + + + + {`import NextAuth from 'next-auth'; +export const { handlers, signIn, signOut, auth } = NextAuth({ + providers: [ + { + id: 'logto', + name: 'Logto', + type: 'oidc', + issuer: '${props.endpoint}oidc', + clientId: '${props.app.id}', + clientSecret: '${props.secrets[0]?.value ?? props.app.secret}', + authorization: { + params: { scope: 'openid offline_access profile email' }, + }, + profile(profile) { + // You can customize the user profile mapping here + return { + id: profile.sub, + name: profile.name ?? profile.username, + email: profile.email, + image: profile.picture, + }; + }, + }, + ], +});`} + + + + + +{`import NextAuth from 'next-auth'; + const handler = NextAuth({ providers: [ { @@ -45,7 +81,7 @@ const handler = NextAuth({ wellKnown: '${props.endpoint}oidc/.well-known/openid-configuration', authorization: { params: { scope: 'openid offline_access profile email' } }, clientId: '${props.app.id}'', - clientSecret: '${props.app.secret}', + clientSecret: '${props.secrets[0]?.value ?? props.app.secret}', client: { id_token_signed_response_alg: 'ES384', }, @@ -64,6 +100,9 @@ const handler = NextAuth({ export { handler as GET, handler as POST };`} + + + diff --git a/packages/console/src/assets/docs/guides/web-next-auth/logo.svg b/packages/console/src/assets/docs/guides/web-next-auth/logo.svg index 47b9c27d79a..5bd92bb887f 100644 --- a/packages/console/src/assets/docs/guides/web-next-auth/logo.svg +++ b/packages/console/src/assets/docs/guides/web-next-auth/logo.svg @@ -1,25 +1,27 @@ - - - - - - - - - - + + + + + + - - - + + + - - - + + + + - - - + + + + + + + + diff --git a/packages/console/src/assets/docs/guides/web-next/README.mdx b/packages/console/src/assets/docs/guides/web-next/README.mdx index 961465f691c..6cb14c8c16f 100644 --- a/packages/console/src/assets/docs/guides/web-next/README.mdx +++ b/packages/console/src/assets/docs/guides/web-next/README.mdx @@ -28,7 +28,7 @@ Import and initialize LogtoClient: export const logtoClient = new LogtoClient({ endpoint: '${props.endpoint}', appId: '${props.app.id}', - appSecret: '${props.app.secret}', + appSecret: '${props.secrets[0]?.value ?? props.app.secret}', baseUrl: '${defaultBaseUrl}', // Change to your own base URL cookieSecret: '${generateStandardSecret()}', // Auto-generated 32 digit secret cookieSecure: process.env.NODE_ENV === 'production', diff --git a/packages/console/src/assets/docs/guides/web-next/logo-dark.svg b/packages/console/src/assets/docs/guides/web-next/logo-dark.svg new file mode 100644 index 00000000000..69d2a9cd56d --- /dev/null +++ b/packages/console/src/assets/docs/guides/web-next/logo-dark.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/packages/console/src/assets/docs/guides/web-next/logo.svg b/packages/console/src/assets/docs/guides/web-next/logo.svg index c1db01c06b7..252c045953d 100644 --- a/packages/console/src/assets/docs/guides/web-next/logo.svg +++ b/packages/console/src/assets/docs/guides/web-next/logo.svg @@ -1,10 +1,3 @@ - - - - - - - - - + + diff --git a/packages/console/src/assets/docs/guides/web-nuxt/README.mdx b/packages/console/src/assets/docs/guides/web-nuxt/README.mdx index b274d4849e7..29c58fa6663 100644 --- a/packages/console/src/assets/docs/guides/web-nuxt/README.mdx +++ b/packages/console/src/assets/docs/guides/web-nuxt/README.mdx @@ -35,7 +35,7 @@ In your Nuxt config file, add the Logto module and configure it: logto: { endpoint: '${props.endpoint}', appId: '${props.app.id}', - appSecret: '${props.app.secret}', + appSecret: '${props.secrets[0]?.value ?? props.app.secret}', cookieEncryptionKey: '${cookieEncryptionKey}', // Random-generated }, }, @@ -48,7 +48,7 @@ Since these information are sensitive, it's recommended to use environment varia {`NUXT_LOGTO_ENDPOINT=${props.endpoint} NUXT_LOGTO_APP_ID=${props.app.id} -NUXT_LOGTO_APP_SECRET=${props.app.secret} +NUXT_LOGTO_APP_SECRET=${props.secrets[0]?.value ?? props.app.secret} NUXT_LOGTO_COOKIE_ENCRYPTION_KEY=${cookieEncryptionKey} # Random-generated `} diff --git a/packages/console/src/assets/docs/guides/web-nuxt/logo.svg b/packages/console/src/assets/docs/guides/web-nuxt/logo.svg index ead151fc7ad..a2f1522d636 100644 --- a/packages/console/src/assets/docs/guides/web-nuxt/logo.svg +++ b/packages/console/src/assets/docs/guides/web-nuxt/logo.svg @@ -1,3 +1,3 @@ - - + + diff --git a/packages/console/src/assets/docs/guides/web-outline/logo-dark.svg b/packages/console/src/assets/docs/guides/web-outline/logo-dark.svg new file mode 100644 index 00000000000..34cb2b54aee --- /dev/null +++ b/packages/console/src/assets/docs/guides/web-outline/logo-dark.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/packages/console/src/assets/docs/guides/web-outline/logo.svg b/packages/console/src/assets/docs/guides/web-outline/logo.svg index eadcc194278..15f815789be 100644 --- a/packages/console/src/assets/docs/guides/web-outline/logo.svg +++ b/packages/console/src/assets/docs/guides/web-outline/logo.svg @@ -1,5 +1,10 @@ - - + + + + + + + + + diff --git a/packages/console/src/assets/docs/guides/web-passport/README.mdx b/packages/console/src/assets/docs/guides/web-passport/README.mdx new file mode 100644 index 00000000000..70d294ae5df --- /dev/null +++ b/packages/console/src/assets/docs/guides/web-passport/README.mdx @@ -0,0 +1,163 @@ +import InlineNotification from '@/ds-components/InlineNotification'; +import UriInputField from '@/mdx-components/UriInputField'; +import Steps from '@/mdx-components/Steps'; +import Step from '@/mdx-components/Step'; +import NpmLikeInstallation from '@/mdx-components/NpmLikeInstallation'; + + + + + +In this guide, we assume you have set up Express with session in you project. If you haven't, check out the [Express.js website](https://expressjs.com/) to get started. + + + + + +Install `passport` and the OIDC strategy plugin, `passport-openidconnect`: + + + + + + + + + In the following steps, we assume your app is running on http://localhost:3000. + + +First, let’s enter your redirect URI. + + + +Don't forget to click the **Save** button. + + + + + + + {`import passport from 'passport'; +import OpenIDConnectStrategy, { type Profile, type VerifyCallback } from 'passport-openidconnect'; + +const endpoint = '${props.endpoint}'; +const appId = '${props.app.id}'; +const appSecret = '${props.app.secret}'; + +export default function initPassport() { + passport.use( + new OpenIDConnectStrategy( + { + issuer: \`\${endpoint}/oidc\`, + authorizationURL: \`\${endpoint}/oidc/auth\`, + tokenURL: \`\${endpoint}/oidc/token\`, + userInfoURL: \`\${endpoint}/oidc/me\`, + clientID: appId, + clientSecret: appSecret, + callbackURL: '/callback', + scope: ['profile', 'offline_access'], + }, + (issuer: string, profile: Profile, callback: VerifyCallback) => { + callback(null, profile); + } + ) + ); + + passport.serializeUser((user, callback) => { + callback(null, user); + }); + + passport.deserializeUser(function (user, callback) { + callback(null, user as Express.User); + }); +}`} + + +This code initializes Passport with the **`OpenIDConnectStrategy`**. The serialize and deserialize methods are set for demonstration purposes. + +Ensure to initialize and attach Passport middleware in your application: + +```tsx +import initPassport from 'src/passport'; + +// ... other code +initPassport(); +// ... other code +app.use(passport.authenticate('session')); +// ... other code +``` + + + + + +We'll now create specific routes for authentication processes: + +### Sign in: `/sign-in` + +```tsx +app.get('/sign-in', passport.authenticate('openidconnect')); +``` + +This route builds and redirects to an OIDC auth route. + +### Handle sign in callback: `/callback` + +```tsx +app.get( + '/callback', + passport.authenticate('openidconnect', { + successReturnToOrRedirect: '/', + }) +); +``` + +This handles the OIDC sign-in callback, stores tokens, and redirects to the homepage. + +### Sign out: `/sign-out` + +```tsx +app.get('/sign-out', (request, response, next) => { + request.logout((error) => { + if (error) { + next(error); + return; + } + response.redirect(`${endpoint}/oidc/session/end?client_id=${appId}`); + }); +}); +``` + +This redirects to Logto's session end URL, then back to the homepage. + +### Add to the homepage + +```tsx +app.get('/', (request: Request, response) => { + const { user } = request; + response.setHeader('content-type', 'text/html'); + + if (user) { + response.end( + `

Hello Logto

Signed in as ${JSON.stringify( + user + )}, Sign Out

` + ); + } else { + response.end(`

Hello Logto

Sign In

`); + } +}); +``` + +
+ + + +Now, you can test your application to see if the authentication works as expected. + + + + diff --git a/packages/console/src/assets/docs/guides/web-passport/config.json b/packages/console/src/assets/docs/guides/web-passport/config.json new file mode 100644 index 00000000000..c81c3a59b30 --- /dev/null +++ b/packages/console/src/assets/docs/guides/web-passport/config.json @@ -0,0 +1,3 @@ +{ + "order": 6.1 +} diff --git a/packages/console/src/assets/docs/guides/web-passport/index.ts b/packages/console/src/assets/docs/guides/web-passport/index.ts new file mode 100644 index 00000000000..bc3dfd037bc --- /dev/null +++ b/packages/console/src/assets/docs/guides/web-passport/index.ts @@ -0,0 +1,11 @@ +import { ApplicationType } from '@logto/schemas'; + +import { type GuideMetadata } from '../types'; + +const metadata: Readonly = Object.freeze({ + name: 'Passport', + description: 'Passport is authentication middleware for Node.js.', + target: ApplicationType.Traditional, +}); + +export default metadata; diff --git a/packages/console/src/assets/docs/guides/web-passport/logo-dark.svg b/packages/console/src/assets/docs/guides/web-passport/logo-dark.svg new file mode 100644 index 00000000000..a9337f4c359 --- /dev/null +++ b/packages/console/src/assets/docs/guides/web-passport/logo-dark.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/packages/console/src/assets/docs/guides/web-passport/logo.svg b/packages/console/src/assets/docs/guides/web-passport/logo.svg new file mode 100644 index 00000000000..f11c4f4d8a2 --- /dev/null +++ b/packages/console/src/assets/docs/guides/web-passport/logo.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/packages/console/src/assets/docs/guides/web-php/README.mdx b/packages/console/src/assets/docs/guides/web-php/README.mdx index 6dfe1d3d7af..32ea660d4fa 100644 --- a/packages/console/src/assets/docs/guides/web-php/README.mdx +++ b/packages/console/src/assets/docs/guides/web-php/README.mdx @@ -35,7 +35,7 @@ $client = new LogtoClient( new LogtoConfig( endpoint: "${props.endpoint}", appId: "${props.app.id}", - appSecret: "${props.app.secret}", + appSecret: "${props.secrets[0]?.value ?? props.app.secret}", ), );`}
diff --git a/packages/console/src/assets/docs/guides/web-php/logo.svg b/packages/console/src/assets/docs/guides/web-php/logo.svg index e9f2a895255..8ca1c32c63f 100644 --- a/packages/console/src/assets/docs/guides/web-php/logo.svg +++ b/packages/console/src/assets/docs/guides/web-php/logo.svg @@ -1,5 +1,12 @@ - - - - + + + + + + + + + + + diff --git a/packages/console/src/assets/docs/guides/web-python/README.mdx b/packages/console/src/assets/docs/guides/web-python/README.mdx index 52fd2300a3b..1616da8d390 100644 --- a/packages/console/src/assets/docs/guides/web-python/README.mdx +++ b/packages/console/src/assets/docs/guides/web-python/README.mdx @@ -32,7 +32,7 @@ client = LogtoClient( LogtoConfig( endpoint="${props.endpoint}", appId="${props.app.id}", - appSecret="${props.app.secret}", + appSecret="${props.secrets[0]?.value ?? props.app.secret}", ) )`}
diff --git a/packages/console/src/assets/docs/guides/web-python/logo.svg b/packages/console/src/assets/docs/guides/web-python/logo.svg index c33c339ecb7..1d2ba15af8d 100644 --- a/packages/console/src/assets/docs/guides/web-python/logo.svg +++ b/packages/console/src/assets/docs/guides/web-python/logo.svg @@ -1,14 +1,19 @@ - - - + + + + + - + - + + + + diff --git a/packages/console/src/assets/docs/guides/web-ruby/README.mdx b/packages/console/src/assets/docs/guides/web-ruby/README.mdx index 63d9e9e5470..89f05318152 100644 --- a/packages/console/src/assets/docs/guides/web-ruby/README.mdx +++ b/packages/console/src/assets/docs/guides/web-ruby/README.mdx @@ -41,7 +41,7 @@ In the file where you want to initialize the Logto client (e.g. a base controlle config: LogtoClient::Config.new( endpoint: "${props.endpoint}", app_id: "${props.app.id}", - app_secret: "${props.app.secret}" + app_secret: "${props.secrets[0]?.value ?? props.app.secret}" ), navigate: ->(uri) { a_redirect_method(uri) }, storage: LogtoClient::SessionStorage.new(the_session_object) @@ -64,7 +64,7 @@ class SampleController < ApplicationController config: LogtoClient::Config.new( endpoint: "${props.endpoint}", app_id: "${props.app.id}", - app_secret: "${props.app.secret}" + app_secret: "${props.secrets[0]?.value ?? props.app.secret}" ), # Allow the client to redirect to other hosts (i.e. your Logto tenant) navigate: ->(uri) { redirect_to(uri, allow_other_host: true) }, diff --git a/packages/console/src/assets/docs/guides/web-ruby/logo.svg b/packages/console/src/assets/docs/guides/web-ruby/logo.svg new file mode 100644 index 00000000000..99dbe45db06 --- /dev/null +++ b/packages/console/src/assets/docs/guides/web-ruby/logo.svg @@ -0,0 +1,199 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/console/src/assets/docs/guides/web-ruby/logo.webp b/packages/console/src/assets/docs/guides/web-ruby/logo.webp deleted file mode 100644 index 991a3aa740358db07ea776a35e844dec3c20e870..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33844 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 diff --git a/packages/console/src/assets/docs/guides/web-sveltekit/README.mdx b/packages/console/src/assets/docs/guides/web-sveltekit/README.mdx index bd361427691..d3b1cbff7b8 100644 --- a/packages/console/src/assets/docs/guides/web-sveltekit/README.mdx +++ b/packages/console/src/assets/docs/guides/web-sveltekit/README.mdx @@ -40,7 +40,7 @@ export const handle = handleLogto( { endpoint: '${props.endpoint}', appId: '${props.app.id}', - appSecret: '${props.app.secret}', + appSecret: '${props.secrets[0]?.value ?? props.app.secret}', }, { encryptionKey: '${cookieEncryptionKey}' } // Random-generated key );`} diff --git a/packages/console/src/assets/docs/guides/web-sveltekit/logo.svg b/packages/console/src/assets/docs/guides/web-sveltekit/logo.svg index 0ab1ab0c3c0..09759b47f8a 100644 --- a/packages/console/src/assets/docs/guides/web-sveltekit/logo.svg +++ b/packages/console/src/assets/docs/guides/web-sveltekit/logo.svg @@ -1 +1,4 @@ - \ No newline at end of file + + + + diff --git a/packages/console/src/assets/docs/guides/web-wordpress/logo.svg b/packages/console/src/assets/docs/guides/web-wordpress/logo.svg index 72183e90424..30eb694c1ee 100644 --- a/packages/console/src/assets/docs/guides/web-wordpress/logo.svg +++ b/packages/console/src/assets/docs/guides/web-wordpress/logo.svg @@ -1 +1,7 @@ - \ No newline at end of file + + + + + + + diff --git a/packages/console/src/assets/icons/calendar-dark.svg b/packages/console/src/assets/icons/calendar-dark.svg new file mode 100644 index 00000000000..d63565f701f --- /dev/null +++ b/packages/console/src/assets/icons/calendar-dark.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/packages/console/src/assets/icons/calendar.svg b/packages/console/src/assets/icons/calendar.svg index b8ffd462547..59e439e9150 100644 --- a/packages/console/src/assets/icons/calendar.svg +++ b/packages/console/src/assets/icons/calendar.svg @@ -1,5 +1,5 @@ - + diff --git a/packages/console/src/assets/icons/email.svg b/packages/console/src/assets/icons/email.svg index 6d156693a7f..36cdd5c31d8 100644 --- a/packages/console/src/assets/icons/email.svg +++ b/packages/console/src/assets/icons/email.svg @@ -1,4 +1,4 @@ - + diff --git a/packages/console/src/assets/icons/github.svg b/packages/console/src/assets/icons/github.svg index 52779b17747..3e30da2e97b 100644 --- a/packages/console/src/assets/icons/github.svg +++ b/packages/console/src/assets/icons/github.svg @@ -1,4 +1,4 @@ - + diff --git a/packages/console/src/assets/images/blur-preview.svg b/packages/console/src/assets/images/blur-preview.svg new file mode 100644 index 00000000000..9d07235496a --- /dev/null +++ b/packages/console/src/assets/images/blur-preview.svg @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/console/src/cloud/AppRoutes.tsx b/packages/console/src/cloud/AppRoutes.tsx index 9870c709ac6..2e49878cfa8 100644 --- a/packages/console/src/cloud/AppRoutes.tsx +++ b/packages/console/src/cloud/AppRoutes.tsx @@ -9,7 +9,7 @@ import CheckoutSuccessCallback from '@/pages/CheckoutSuccessCallback'; import Profile from '@/pages/Profile'; import HandleSocialCallback from '@/pages/Profile/containers/HandleSocialCallback'; -import * as styles from './AppRoutes.module.scss'; +import styles from './AppRoutes.module.scss'; import Main from './pages/Main'; import SocialDemoCallback from './pages/SocialDemoCallback'; diff --git a/packages/console/src/cloud/pages/Main/InvitationList/index.tsx b/packages/console/src/cloud/pages/Main/InvitationList/index.tsx index e86b71147fe..13e17b42a47 100644 --- a/packages/console/src/cloud/pages/Main/InvitationList/index.tsx +++ b/packages/console/src/cloud/pages/Main/InvitationList/index.tsx @@ -2,7 +2,7 @@ import { OrganizationInvitationStatus, getTenantIdFromOrganizationId } from '@lo import { useContext, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import OrganizationIcon from '@/assets/icons/organization-preview.svg'; +import OrganizationIcon from '@/assets/icons/organization-preview.svg?react'; import { useCloudApi } from '@/cloud/hooks/use-cloud-api'; import { type InvitationListResponse } from '@/cloud/types/router'; import TenantEnvTag from '@/components/TenantEnvTag'; @@ -13,7 +13,7 @@ import Spacer from '@/ds-components/Spacer'; import useTenantPathname from '@/hooks/use-tenant-pathname'; import useUserOnboardingData from '@/onboarding/hooks/use-user-onboarding-data'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly invitations: InvitationListResponse; diff --git a/packages/console/src/cloud/pages/Main/TenantLandingPage/TenantLandingPageContent/index.tsx b/packages/console/src/cloud/pages/Main/TenantLandingPage/TenantLandingPageContent/index.tsx index 6978b899366..e51c9c24c4b 100644 --- a/packages/console/src/cloud/pages/Main/TenantLandingPage/TenantLandingPageContent/index.tsx +++ b/packages/console/src/cloud/pages/Main/TenantLandingPage/TenantLandingPageContent/index.tsx @@ -2,9 +2,9 @@ import { Theme } from '@logto/schemas'; import classNames from 'classnames'; import { useContext, useState } from 'react'; -import Plus from '@/assets/icons/plus.svg'; -import TenantLandingPageImageDark from '@/assets/images/tenant-landing-page-dark.svg'; -import TenantLandingPageImage from '@/assets/images/tenant-landing-page.svg'; +import Plus from '@/assets/icons/plus.svg?react'; +import TenantLandingPageImageDark from '@/assets/images/tenant-landing-page-dark.svg?react'; +import TenantLandingPageImage from '@/assets/images/tenant-landing-page.svg?react'; import { type TenantResponse } from '@/cloud/types/router'; import CreateTenantModal from '@/components/CreateTenantModal'; import { TenantsContext } from '@/contexts/TenantsProvider'; @@ -12,7 +12,7 @@ import Button from '@/ds-components/Button'; import DynamicT from '@/ds-components/DynamicT'; import useTheme from '@/hooks/use-theme'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly className?: string; diff --git a/packages/console/src/cloud/pages/Main/TenantLandingPage/index.tsx b/packages/console/src/cloud/pages/Main/TenantLandingPage/index.tsx index fb99b70dcc4..d5fd24d6c70 100644 --- a/packages/console/src/cloud/pages/Main/TenantLandingPage/index.tsx +++ b/packages/console/src/cloud/pages/Main/TenantLandingPage/index.tsx @@ -1,7 +1,7 @@ import Topbar from '@/components/Topbar'; import TenantLandingPageContent from './TenantLandingPageContent'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; function TenantLandingPage() { return ( diff --git a/packages/console/src/cloud/pages/SocialDemoCallback/index.tsx b/packages/console/src/cloud/pages/SocialDemoCallback/index.tsx index 5c18de94f1c..b83cd0c46a5 100644 --- a/packages/console/src/cloud/pages/SocialDemoCallback/index.tsx +++ b/packages/console/src/cloud/pages/SocialDemoCallback/index.tsx @@ -3,12 +3,12 @@ import { useEffect } from 'react'; import { useTranslation } from 'react-i18next'; import { useSearchParams } from 'react-router-dom'; -import CongratsDark from '@/assets/images/congrats-dark.svg'; -import Congrats from '@/assets/images/congrats.svg'; +import CongratsDark from '@/assets/images/congrats-dark.svg?react'; +import Congrats from '@/assets/images/congrats.svg?react'; import Card from '@/ds-components/Card'; import useTheme from '@/hooks/use-theme'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; function SocialDemoCallback() { const theme = useTheme(); diff --git a/packages/console/src/cloud/types/router.ts b/packages/console/src/cloud/types/router.ts index ed175b7afda..7d6cf2f10bc 100644 --- a/packages/console/src/cloud/types/router.ts +++ b/packages/console/src/cloud/types/router.ts @@ -11,10 +11,30 @@ export type SubscriptionPlanResponse = GuardedResponse< GetRoutes['/api/subscription-plans'] >[number]; +export type LogtoSkuResponse = GetArrayElementType>; + export type Subscription = GuardedResponse; +/** @deprecated */ export type SubscriptionUsage = GuardedResponse; +/* ===== Use `New` in the naming to avoid confusion with legacy types ===== */ +/** The response of `GET /api/tenants/my/subscription/quota` has the same response type. */ +export type NewSubscriptionQuota = GuardedResponse< + GetRoutes['/api/tenants/:tenantId/subscription/quota'] +>; + +/** The response of `GET /api/tenants/my/subscription/usage` has the same response type. */ +export type NewSubscriptionUsage = GuardedResponse< + GetRoutes['/api/tenants/:tenantId/subscription/usage'] +>; + +/** The response of `GET /api/tenants/my/subscription/usage/:entityName/scopes` has the same response type. */ +export type NewSubscriptionScopeUsage = GuardedResponse< + GetRoutes['/api/tenants/:tenantId/subscription/usage/:entityName/scopes'] +>; +/* ===== Use `New` in the naming to avoid confusion with legacy types ===== */ + export type InvoicesResponse = GuardedResponse; export type InvitationResponse = GuardedResponse; diff --git a/packages/console/src/components/ActionBar/index.tsx b/packages/console/src/components/ActionBar/index.tsx index 11d09537973..403f3a981cc 100644 --- a/packages/console/src/components/ActionBar/index.tsx +++ b/packages/console/src/components/ActionBar/index.tsx @@ -2,7 +2,7 @@ import type { ReactNode } from 'react'; import ProgressBar from '../ProgressBar'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly step: number; diff --git a/packages/console/src/components/ActionsButton/index.tsx b/packages/console/src/components/ActionsButton/index.tsx index 575b05f1bd6..00a27113274 100644 --- a/packages/console/src/components/ActionsButton/index.tsx +++ b/packages/console/src/components/ActionsButton/index.tsx @@ -2,15 +2,15 @@ import { type AdminConsoleKey } from '@logto/phrases'; import { useCallback, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import Delete from '@/assets/icons/delete.svg'; -import Edit from '@/assets/icons/edit.svg'; -import More from '@/assets/icons/more.svg'; +import Delete from '@/assets/icons/delete.svg?react'; +import Edit from '@/assets/icons/edit.svg?react'; +import More from '@/assets/icons/more.svg?react'; import ActionMenu, { ActionMenuItem } from '@/ds-components/ActionMenu'; import ConfirmModal from '@/ds-components/ConfirmModal'; import DynamicT from '@/ds-components/DynamicT'; import useActionTranslation from '@/hooks/use-action-translation'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { /** A function that will be called when the user confirms the deletion. If not provided, diff --git a/packages/console/src/components/AddOnNoticeFooter/index.module.scss b/packages/console/src/components/AddOnNoticeFooter/index.module.scss new file mode 100644 index 00000000000..4a3dc3faa6e --- /dev/null +++ b/packages/console/src/components/AddOnNoticeFooter/index.module.scss @@ -0,0 +1,17 @@ +@use '@/scss/underscore' as _; + +.container { + display: flex; + align-items: center; + gap: _.unit(6); + padding: _.unit(6); + background-color: var(--color-info-container); + margin: 0 _.unit(-6) _.unit(-6); + flex: 1; // Should display in full width + + .description { + flex: 1; + flex-shrink: 0; + font: var(--font-body-2); + } +} diff --git a/packages/console/src/components/AddOnNoticeFooter/index.tsx b/packages/console/src/components/AddOnNoticeFooter/index.tsx new file mode 100644 index 00000000000..6f40c55f4e5 --- /dev/null +++ b/packages/console/src/components/AddOnNoticeFooter/index.tsx @@ -0,0 +1,30 @@ +import { type AdminConsoleKey } from '@logto/phrases'; +import { type ReactNode } from 'react'; + +import Button from '@/ds-components/Button'; + +import styles from './index.module.scss'; + +type Props = { + readonly children: ReactNode; + readonly isLoading?: boolean; + readonly buttonTitle?: AdminConsoleKey; + readonly onClick: () => void; +}; + +function AddOnNoticeFooter({ children, isLoading, onClick, buttonTitle }: Props) { + return ( +
+
{children}
+
+ ); +} + +export default AddOnNoticeFooter; diff --git a/packages/console/src/components/AppError/index.tsx b/packages/console/src/components/AppError/index.tsx index 49a2bfdbb89..468aa8d80fe 100644 --- a/packages/console/src/components/AppError/index.tsx +++ b/packages/console/src/components/AppError/index.tsx @@ -3,15 +3,15 @@ import { Theme } from '@logto/schemas'; import { useState } from 'react'; import { useTranslation } from 'react-i18next'; -import KeyboardArrowDown from '@/assets/icons/keyboard-arrow-down.svg'; -import KeyboardArrowUp from '@/assets/icons/keyboard-arrow-up.svg'; -import ErrorDark from '@/assets/images/error-dark.svg'; -import Error from '@/assets/images/error.svg'; +import KeyboardArrowDown from '@/assets/icons/keyboard-arrow-down.svg?react'; +import KeyboardArrowUp from '@/assets/icons/keyboard-arrow-up.svg?react'; +import ErrorDark from '@/assets/images/error-dark.svg?react'; +import Error from '@/assets/images/error.svg?react'; import Button from '@/ds-components/Button'; import useTheme from '@/hooks/use-theme'; import { onKeyDownHandler } from '@/utils/a11y'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly title?: string; diff --git a/packages/console/src/components/AppLoading/index.tsx b/packages/console/src/components/AppLoading/index.tsx index 036e8efa270..c0db14cb006 100644 --- a/packages/console/src/components/AppLoading/index.tsx +++ b/packages/console/src/components/AppLoading/index.tsx @@ -1,7 +1,7 @@ -import Logo from '@/assets/images/logo.svg'; +import Logo from '@/assets/images/logo.svg?react'; import { Daisy as Spinner } from '@/ds-components/Spinner'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; function AppLoading() { return ( diff --git a/packages/console/src/components/ApplicationCreation/CreateForm/Footer/index.module.scss b/packages/console/src/components/ApplicationCreation/CreateForm/Footer/index.module.scss new file mode 100644 index 00000000000..c9daaa78419 --- /dev/null +++ b/packages/console/src/components/ApplicationCreation/CreateForm/Footer/index.module.scss @@ -0,0 +1,3 @@ +.strong { + font-weight: 500; +} diff --git a/packages/console/src/components/ApplicationCreation/CreateForm/Footer/index.tsx b/packages/console/src/components/ApplicationCreation/CreateForm/Footer/index.tsx index 0928e6cf980..1f5845f4a6b 100644 --- a/packages/console/src/components/ApplicationCreation/CreateForm/Footer/index.tsx +++ b/packages/console/src/components/ApplicationCreation/CreateForm/Footer/index.tsx @@ -2,13 +2,19 @@ import { ApplicationType, ReservedPlanId } from '@logto/schemas'; import { useContext } from 'react'; import { Trans, useTranslation } from 'react-i18next'; +import AddOnNoticeFooter from '@/components/AddOnNoticeFooter'; import ContactUsPhraseLink from '@/components/ContactUsPhraseLink'; import PlanName from '@/components/PlanName'; import QuotaGuardFooter from '@/components/QuotaGuardFooter'; +import { isDevFeaturesEnabled } from '@/consts/env'; +import { machineToMachineAddOnUnitPrice } from '@/consts/subscriptions'; import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider'; import Button from '@/ds-components/Button'; +import TextLink from '@/ds-components/TextLink'; import useApplicationsUsage from '@/hooks/use-applications-usage'; +import styles from './index.module.scss'; + type Props = { readonly selectedType?: ApplicationType; readonly isLoading: boolean; @@ -17,8 +23,8 @@ type Props = { }; function Footer({ selectedType, isLoading, onClickCreate, isThirdParty }: Props) { - const { currentPlan } = useContext(SubscriptionDataContext); - const { t } = useTranslation(undefined, { keyPrefix: 'admin_console.upsell.paywall' }); + const { currentPlan, currentSku } = useContext(SubscriptionDataContext); + const { t } = useTranslation(undefined, { keyPrefix: 'admin_console.upsell' }); const { hasAppsReachedLimit, hasMachineToMachineAppsReachedLimit, @@ -28,11 +34,36 @@ function Footer({ selectedType, isLoading, onClickCreate, isThirdParty }: Props) if (selectedType) { const { id: planId, name: planName, quota } = currentPlan; + if ( + selectedType === ApplicationType.MachineToMachine && + isDevFeaturesEnabled && + planId === ReservedPlanId.Pro + ) { + return ( + + , + a: , + }} + > + {t('add_on.footer.machine_to_machine_app', { + price: machineToMachineAddOnUnitPrice, + })} + + + ); + } + if ( selectedType === ApplicationType.MachineToMachine && hasMachineToMachineAppsReachedLimit && // For paid plan (pro plan), we don't guard the m2m app creation since it's an add-on feature. - planId === ReservedPlanId.Free + (isDevFeaturesEnabled ? currentSku.id : planId) === ReservedPlanId.Free ) { return ( @@ -41,7 +72,7 @@ function Footer({ selectedType, isLoading, onClickCreate, isThirdParty }: Props) a: , }} > - {t('machine_to_machine_feature')} + {t('paywall.machine_to_machine_feature')} ); @@ -56,7 +87,7 @@ function Footer({ selectedType, isLoading, onClickCreate, isThirdParty }: Props) a: , }} > - {t('third_party_apps')} + {t('paywall.third_party_apps')} ); @@ -68,10 +99,10 @@ function Footer({ selectedType, isLoading, onClickCreate, isThirdParty }: Props) , - planName: , + planName: , }} > - {t('applications', { count: quota.applicationsLimit ?? 0 })} + {t('paywall.applications', { count: quota.applicationsLimit ?? 0 })} ); diff --git a/packages/console/src/components/ApplicationCreation/CreateForm/index.tsx b/packages/console/src/components/ApplicationCreation/CreateForm/index.tsx index 21c88536a07..097c7cf7010 100644 --- a/packages/console/src/components/ApplicationCreation/CreateForm/index.tsx +++ b/packages/console/src/components/ApplicationCreation/CreateForm/index.tsx @@ -1,7 +1,8 @@ import { type AdminConsoleKey } from '@logto/phrases'; import type { Application } from '@logto/schemas'; -import { ApplicationType } from '@logto/schemas'; -import { type ReactElement, useMemo } from 'react'; +import { ApplicationType, ReservedPlanId } from '@logto/schemas'; +import { conditional } from '@silverhand/essentials'; +import { type ReactElement, useContext, useMemo } from 'react'; import { useController, useForm } from 'react-hook-form'; import { toast } from 'react-hot-toast'; import { useTranslation } from 'react-i18next'; @@ -9,6 +10,8 @@ import Modal from 'react-modal'; import { useSWRConfig } from 'swr'; import { GtagConversionId, reportConversion } from '@/components/Conversion/utils'; +import { isDevFeaturesEnabled } from '@/consts/env'; +import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider'; import DynamicT from '@/ds-components/DynamicT'; import FormField from '@/ds-components/FormField'; import ModalLayout from '@/ds-components/ModalLayout'; @@ -17,12 +20,12 @@ import TextInput from '@/ds-components/TextInput'; import useApi from '@/hooks/use-api'; import useCurrentUser from '@/hooks/use-current-user'; import TypeDescription from '@/pages/Applications/components/TypeDescription'; -import * as modalStyles from '@/scss/modal.module.scss'; +import modalStyles from '@/scss/modal.module.scss'; import { applicationTypeI18nKey } from '@/types/applications'; import { trySubmitSafe } from '@/utils/form'; import Footer from './Footer'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type FormData = { type: ApplicationType; @@ -52,6 +55,9 @@ function CreateForm({ } = useForm({ defaultValues: { type: defaultCreateType, isThirdParty: isDefaultCreateThirdParty }, }); + const { + currentSubscription: { planId }, + } = useContext(SubscriptionDataContext); const { user } = useCurrentUser(); const { mutate: mutateGlobal } = useSWRConfig(); @@ -115,6 +121,9 @@ function CreateForm({ )} {!to &&
{title}
} + {eventKey === 'ExchangeTokenBy.TokenExchange' && Impersonation} ); } diff --git a/packages/console/src/components/AuditLogTable/index.tsx b/packages/console/src/components/AuditLogTable/index.tsx index 6901f5fa3a6..8cd31e744b3 100644 --- a/packages/console/src/components/AuditLogTable/index.tsx +++ b/packages/console/src/components/AuditLogTable/index.tsx @@ -19,7 +19,7 @@ import EmptyDataPlaceholder from '../EmptyDataPlaceholder'; import ApplicationSelector from './components/ApplicationSelector'; import EventName from './components/EventName'; import EventSelector from './components/EventSelector'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; const auditLogEventOptions = Object.entries(auditLogEventTitle).map(([value, title]) => ({ value, diff --git a/packages/console/src/components/BasicWebhookForm/index.tsx b/packages/console/src/components/BasicWebhookForm/index.tsx index f110c7b852c..98ba5b7097e 100644 --- a/packages/console/src/components/BasicWebhookForm/index.tsx +++ b/packages/console/src/components/BasicWebhookForm/index.tsx @@ -14,7 +14,7 @@ import FormField from '@/ds-components/FormField'; import TextInput from '@/ds-components/TextInput'; import { uriValidator } from '@/utils/validator'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; const hookEventGroups: Array> = [ ...schemaGroupedDataHookEvents.map(([schema, events]) => ({ diff --git a/packages/console/src/components/BillInfo/index.tsx b/packages/console/src/components/BillInfo/index.tsx index c2a3a25f2df..85ec6946fed 100644 --- a/packages/console/src/components/BillInfo/index.tsx +++ b/packages/console/src/components/BillInfo/index.tsx @@ -1,7 +1,7 @@ import { useContext, useState } from 'react'; import { Trans, useTranslation } from 'react-i18next'; -import Tip from '@/assets/icons/tip.svg'; +import Tip from '@/assets/icons/tip.svg?react'; import { newPlansBlogLink } from '@/consts'; import { TenantsContext } from '@/contexts/TenantsProvider'; import Button from '@/ds-components/Button'; @@ -11,7 +11,7 @@ import TextLink from '@/ds-components/TextLink'; import { ToggleTip } from '@/ds-components/Tip'; import useSubscribe from '@/hooks/use-subscribe'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly cost: number; diff --git a/packages/console/src/components/Breakable/index.tsx b/packages/console/src/components/Breakable/index.tsx index ca5bf216797..45de5954c8c 100644 --- a/packages/console/src/components/Breakable/index.tsx +++ b/packages/console/src/components/Breakable/index.tsx @@ -1,4 +1,4 @@ -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly children: React.ReactNode; diff --git a/packages/console/src/components/ChargeNotification/index.tsx b/packages/console/src/components/ChargeNotification/index.tsx index 2fd62a5f4df..e2ce6ffe3ce 100644 --- a/packages/console/src/components/ChargeNotification/index.tsx +++ b/packages/console/src/components/ChargeNotification/index.tsx @@ -5,6 +5,7 @@ import { useContext } from 'react'; import { Trans, useTranslation } from 'react-i18next'; import { newPlansBlogLink } from '@/consts'; +import { isDevFeaturesEnabled } from '@/consts/env'; import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider'; import InlineNotification from '@/ds-components/InlineNotification'; import TextLink from '@/ds-components/TextLink'; @@ -38,7 +39,7 @@ function ChargeNotification({ checkedFlagKey, }: Props) { const { t } = useTranslation(undefined, { keyPrefix: 'admin_console.upsell' }); - const { currentPlan } = useContext(SubscriptionDataContext); + const { currentPlan, currentSku } = useContext(SubscriptionDataContext); const { configs, updateConfigs } = useConfigs(); // Display null when loading @@ -52,7 +53,7 @@ function ChargeNotification({ Boolean(checkedChargeNotification?.[checkedFlagKey]) || !hasSurpassedLimit || // No charge notification for free plan - currentPlan.id === ReservedPlanId.Free + (isDevFeaturesEnabled ? currentSku.id : currentPlan.id) === ReservedPlanId.Free ) { return null; } diff --git a/packages/console/src/components/ConnectorForm/BasicForm/index.tsx b/packages/console/src/components/ConnectorForm/BasicForm/index.tsx index 2a72ed192cb..0711588a6a2 100644 --- a/packages/console/src/components/ConnectorForm/BasicForm/index.tsx +++ b/packages/console/src/components/ConnectorForm/BasicForm/index.tsx @@ -1,12 +1,10 @@ -import { useState } from 'react'; +import { Theme } from '@logto/schemas'; import { Controller, useFormContext } from 'react-hook-form'; import { Trans, useTranslation } from 'react-i18next'; -import CaretDown from '@/assets/icons/caret-down.svg'; -import CaretUp from '@/assets/icons/caret-up.svg'; -import Error from '@/assets/icons/toast-error.svg'; +import Error from '@/assets/icons/toast-error.svg?react'; +import ImageInputs from '@/components/ImageInputs'; import UnnamedTrans from '@/components/UnnamedTrans'; -import Button from '@/ds-components/Button'; import FormField from '@/ds-components/FormField'; import Select from '@/ds-components/Select'; import TextInput from '@/ds-components/TextInput'; @@ -14,23 +12,21 @@ import TextLink from '@/ds-components/TextLink'; import useDocumentationUrl from '@/hooks/use-documentation-url'; import type { ConnectorFormType } from '@/types/connector'; import { SyncProfileMode } from '@/types/connector'; -import { uriValidator } from '@/utils/validator'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; + +const themeToField = Object.freeze({ + [Theme.Light]: 'logo', + [Theme.Dark]: 'logoDark', +} as const satisfies Record); type Props = { readonly isAllowEditTarget?: boolean; - readonly isDarkDefaultVisible?: boolean; readonly isStandard?: boolean; readonly conflictConnectorName?: Record; }; -function BasicForm({ - isAllowEditTarget, - isDarkDefaultVisible, - isStandard, - conflictConnectorName, -}: Props) { +function BasicForm({ isAllowEditTarget, isStandard, conflictConnectorName }: Props) { const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); const { getDocumentationUrl } = useDocumentationUrl(); const { @@ -38,17 +34,6 @@ function BasicForm({ register, formState: { errors }, } = useFormContext(); - const [darkVisible, setDarkVisible] = useState(Boolean(isDarkDefaultVisible)); - - const toggleDarkVisible = () => { - setDarkVisible((previous) => !previous); - }; - - const toggleVisibleButtonTitle = darkVisible - ? 'connectors.guide.logo_dark_collapse' - : 'connectors.guide.logo_dark_show'; - - const ToggleVisibleCaretIcon = darkVisible ? CaretUp : CaretDown; const syncProfileOptions = [ { @@ -72,37 +57,18 @@ function BasicForm({ {...register('name', { required: true })} /> - - - !value || uriValidator(value) || t('errors.invalid_uri_format'), - })} - /> - - {darkVisible && ( - - - !value || uriValidator(value) || t('errors.invalid_uri_format'), - })} - /> - - )} -
-
+ ({ + name: themeToField[theme], + error: errors[themeToField[theme]], + type: 'connector_logo', + theme, + }))} + /> )} diff --git a/packages/console/src/components/CreateConnectorForm/ConnectorRadioGroup/ConnectorRadio/index.tsx b/packages/console/src/components/CreateConnectorForm/ConnectorRadioGroup/ConnectorRadio/index.tsx index b722f3f99bb..fc2293ab59d 100644 --- a/packages/console/src/components/CreateConnectorForm/ConnectorRadioGroup/ConnectorRadio/index.tsx +++ b/packages/console/src/components/CreateConnectorForm/ConnectorRadioGroup/ConnectorRadio/index.tsx @@ -5,7 +5,7 @@ import ConnectorLogo from '@/components/ConnectorLogo'; import UnnamedTrans from '@/components/UnnamedTrans'; import { type ConnectorGroup } from '@/types/connector'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly data: ConnectorGroup; diff --git a/packages/console/src/components/CreateConnectorForm/ConnectorRadioGroup/index.tsx b/packages/console/src/components/CreateConnectorForm/ConnectorRadioGroup/index.tsx index f682f929e8a..38a81ebe01b 100644 --- a/packages/console/src/components/CreateConnectorForm/ConnectorRadioGroup/index.tsx +++ b/packages/console/src/components/CreateConnectorForm/ConnectorRadioGroup/index.tsx @@ -5,7 +5,7 @@ import RadioGroup, { Radio } from '@/ds-components/RadioGroup'; import { type ConnectorGroup } from '@/types/connector'; import ConnectorRadio from './ConnectorRadio'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; export type ConnectorRadioGroupSize = 'medium' | 'large' | 'xlarge'; diff --git a/packages/console/src/components/CreateConnectorForm/Footer/index.tsx b/packages/console/src/components/CreateConnectorForm/Footer/index.tsx index 53c7b4032cb..8c86cce555c 100644 --- a/packages/console/src/components/CreateConnectorForm/Footer/index.tsx +++ b/packages/console/src/components/CreateConnectorForm/Footer/index.tsx @@ -10,10 +10,11 @@ import { Trans, useTranslation } from 'react-i18next'; import ContactUsPhraseLink from '@/components/ContactUsPhraseLink'; import PlanName from '@/components/PlanName'; import QuotaGuardFooter from '@/components/QuotaGuardFooter'; +import { isDevFeaturesEnabled } from '@/consts/env'; import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider'; import Button from '@/ds-components/Button'; import { type ConnectorGroup } from '@/types/connector'; -import { hasReachedQuotaLimit } from '@/utils/quota'; +import { hasReachedQuotaLimit, hasReachedSubscriptionQuotaLimit } from '@/utils/quota'; type Props = { readonly isCreatingSocialConnector: boolean; @@ -31,35 +32,51 @@ function Footer({ onClickCreateButton, }: Props) { const { t } = useTranslation(undefined, { keyPrefix: 'admin_console.upsell.paywall' }); - const { currentPlan } = useContext(SubscriptionDataContext); + const { currentPlan, currentSku, currentSubscriptionUsage, currentSubscriptionQuota } = + useContext(SubscriptionDataContext); const standardConnectorCount = useMemo( () => - existingConnectors.filter( - ({ isStandard, isDemo, type }) => isStandard && !isDemo && type === ConnectorType.Social - ).length, + isDevFeaturesEnabled + ? // No more standard connector limit in new pricing model. + 0 + : existingConnectors.filter( + ({ isStandard, isDemo, type }) => isStandard && !isDemo && type === ConnectorType.Social + ).length, [existingConnectors] ); const socialConnectorCount = useMemo( () => - existingConnectors.filter( - ({ isStandard, isDemo, type }) => !isStandard && !isDemo && type === ConnectorType.Social - ).length, - [existingConnectors] + isDevFeaturesEnabled + ? currentSubscriptionUsage.socialConnectorsLimit + : existingConnectors.filter( + ({ isStandard, isDemo, type }) => + !isStandard && !isDemo && type === ConnectorType.Social + ).length, + [existingConnectors, currentSubscriptionUsage.socialConnectorsLimit] ); - const isStandardConnectorsReachLimit = hasReachedQuotaLimit({ - quotaKey: 'standardConnectorsLimit', - plan: currentPlan, - usage: standardConnectorCount, - }); + const isStandardConnectorsReachLimit = isDevFeaturesEnabled + ? // No more standard connector limit in new pricing model. + false + : hasReachedQuotaLimit({ + quotaKey: 'standardConnectorsLimit', + plan: currentPlan, + usage: standardConnectorCount, + }); - const isSocialConnectorsReachLimit = hasReachedQuotaLimit({ - quotaKey: 'socialConnectorsLimit', - plan: currentPlan, - usage: socialConnectorCount, - }); + const isSocialConnectorsReachLimit = isDevFeaturesEnabled + ? hasReachedSubscriptionQuotaLimit({ + quotaKey: 'socialConnectorsLimit', + usage: currentSubscriptionUsage.socialConnectorsLimit, + quota: currentSubscriptionQuota, + }) + : hasReachedQuotaLimit({ + quotaKey: 'socialConnectorsLimit', + plan: currentPlan, + usage: socialConnectorCount, + }); if (isCreatingSocialConnector && selectedConnectorGroup) { const { id: planId, name: planName, quota } = currentPlan; @@ -70,13 +87,15 @@ function Footer({ , - planName: , + planName: , }} > {quota.standardConnectorsLimit === 0 ? t('standard_connectors_feature') : t( - planId === ReservedPlanId.Pro ? 'standard_connectors_pro' : 'standard_connectors', + (isDevFeaturesEnabled ? currentSku.id : planId) === ReservedPlanId.Pro + ? 'standard_connectors_pro' + : 'standard_connectors', { count: quota.standardConnectorsLimit ?? 0, } @@ -92,11 +111,14 @@ function Footer({ , - planName: , + planName: , }} > {t('social_connectors', { - count: quota.socialConnectorsLimit ?? 0, + count: + (isDevFeaturesEnabled + ? currentSubscriptionQuota.socialConnectorsLimit + : quota.socialConnectorsLimit) ?? 0, })} diff --git a/packages/console/src/components/CreateConnectorForm/PlatformSelector/index.tsx b/packages/console/src/components/CreateConnectorForm/PlatformSelector/index.tsx index 658db4e286d..01a9eac6eab 100644 --- a/packages/console/src/components/CreateConnectorForm/PlatformSelector/index.tsx +++ b/packages/console/src/components/CreateConnectorForm/PlatformSelector/index.tsx @@ -6,7 +6,7 @@ import { connectorPlatformLabel } from '@/consts/connectors'; import RadioGroup, { Radio } from '@/ds-components/RadioGroup'; import type { ConnectorGroup } from '@/types/connector'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly connectorGroup: ConnectorGroup; diff --git a/packages/console/src/components/CreateConnectorForm/Skeleton/index.module.scss b/packages/console/src/components/CreateConnectorForm/Skeleton/index.module.scss index dbe6a3872a4..e53ce9df54d 100644 --- a/packages/console/src/components/CreateConnectorForm/Skeleton/index.module.scss +++ b/packages/console/src/components/CreateConnectorForm/Skeleton/index.module.scss @@ -7,23 +7,23 @@ } .logo { - @include _.shimmering-animation; width: 40px; height: 40px; border-radius: 8px; + @include _.shimmering-animation; } .name { - @include _.shimmering-animation; width: 50px; height: 16px; margin-bottom: _.unit(1); + @include _.shimmering-animation; } .description { - @include _.shimmering-animation; height: 14px; margin-bottom: _.unit(0.5); + @include _.shimmering-animation; &.shortDescription { width: 50%; diff --git a/packages/console/src/components/CreateConnectorForm/Skeleton/index.tsx b/packages/console/src/components/CreateConnectorForm/Skeleton/index.tsx index 9ecdb68975f..c305f630c42 100644 --- a/packages/console/src/components/CreateConnectorForm/Skeleton/index.tsx +++ b/packages/console/src/components/CreateConnectorForm/Skeleton/index.tsx @@ -1,9 +1,9 @@ import classNames from 'classnames'; -import * as radioStyles from '../ConnectorRadioGroup/ConnectorRadio/index.module.scss'; -import * as radioGroupStyles from '../ConnectorRadioGroup/index.module.scss'; +import radioStyles from '../ConnectorRadioGroup/ConnectorRadio/index.module.scss'; +import radioGroupStyles from '../ConnectorRadioGroup/index.module.scss'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly numberOfLoadingConnectors?: number; diff --git a/packages/console/src/components/CreateConnectorForm/index.tsx b/packages/console/src/components/CreateConnectorForm/index.tsx index 5ed33692ef9..db935cd198a 100644 --- a/packages/console/src/components/CreateConnectorForm/index.tsx +++ b/packages/console/src/components/CreateConnectorForm/index.tsx @@ -10,7 +10,7 @@ import useSWR from 'swr'; import DynamicT from '@/ds-components/DynamicT'; import ModalLayout from '@/ds-components/ModalLayout'; import type { RequestError } from '@/hooks/use-api'; -import * as modalStyles from '@/scss/modal.module.scss'; +import modalStyles from '@/scss/modal.module.scss'; import { getConnectorGroups } from '../../pages/Connectors/utils'; @@ -18,7 +18,7 @@ import ConnectorRadioGroup from './ConnectorRadioGroup'; import Footer from './Footer'; import PlatformSelector from './PlatformSelector'; import Skeleton from './Skeleton'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; import { compareConnectors, getConnectorRadioGroupSize, getModalTitle } from './utils'; type Props = { diff --git a/packages/console/src/components/CreateTenantModal/EnvTagOptionContent/index.tsx b/packages/console/src/components/CreateTenantModal/EnvTagOptionContent/index.tsx index f9d6a10549d..48b2f75e2d7 100644 --- a/packages/console/src/components/CreateTenantModal/EnvTagOptionContent/index.tsx +++ b/packages/console/src/components/CreateTenantModal/EnvTagOptionContent/index.tsx @@ -7,7 +7,7 @@ import DynamicT from '@/ds-components/DynamicT'; import Tag from '@/ds-components/Tag'; import { ReservedPlanName } from '@/types/subscriptions'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly tag: TenantTag; diff --git a/packages/console/src/components/CreateTenantModal/SelectTenantPlanModal/PlanCardItem/FeaturedPlanContent/index.tsx b/packages/console/src/components/CreateTenantModal/SelectTenantPlanModal/PlanCardItem/FeaturedPlanContent/index.tsx index 8e95a6a2a3a..9007d706273 100644 --- a/packages/console/src/components/CreateTenantModal/SelectTenantPlanModal/PlanCardItem/FeaturedPlanContent/index.tsx +++ b/packages/console/src/components/CreateTenantModal/SelectTenantPlanModal/PlanCardItem/FeaturedPlanContent/index.tsx @@ -1,9 +1,9 @@ import classNames from 'classnames'; -import Failed from '@/assets/icons/failed.svg'; -import Success from '@/assets/icons/success.svg'; +import Failed from '@/assets/icons/failed.svg?react'; +import Success from '@/assets/icons/success.svg?react'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; import useFeaturedPlanContent from './use-featured-plan-content'; type Props = { diff --git a/packages/console/src/components/CreateTenantModal/SelectTenantPlanModal/PlanCardItem/index.tsx b/packages/console/src/components/CreateTenantModal/SelectTenantPlanModal/PlanCardItem/index.tsx index f6836d4041c..417d5d77a66 100644 --- a/packages/console/src/components/CreateTenantModal/SelectTenantPlanModal/PlanCardItem/index.tsx +++ b/packages/console/src/components/CreateTenantModal/SelectTenantPlanModal/PlanCardItem/index.tsx @@ -3,7 +3,7 @@ import classNames from 'classnames'; import { useContext, useMemo } from 'react'; import { Trans, useTranslation } from 'react-i18next'; -import ArrowRight from '@/assets/icons/arrow-right.svg'; +import ArrowRight from '@/assets/icons/arrow-right.svg?react'; import PlanDescription from '@/components/PlanDescription'; import PlanName from '@/components/PlanName'; import { pricingLink } from '@/consts'; @@ -15,7 +15,7 @@ import TextLink from '@/ds-components/TextLink'; import { type SubscriptionPlan } from '@/types/subscriptions'; import FeaturedPlanContent from './FeaturedPlanContent'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly plan: SubscriptionPlan; diff --git a/packages/console/src/components/CreateTenantModal/SelectTenantPlanModal/SkuCardItem/FeaturedSkuContent/index.tsx b/packages/console/src/components/CreateTenantModal/SelectTenantPlanModal/SkuCardItem/FeaturedSkuContent/index.tsx new file mode 100644 index 00000000000..6ecbbeead51 --- /dev/null +++ b/packages/console/src/components/CreateTenantModal/SelectTenantPlanModal/SkuCardItem/FeaturedSkuContent/index.tsx @@ -0,0 +1,35 @@ +import classNames from 'classnames'; + +import Failed from '@/assets/icons/failed.svg?react'; +import Success from '@/assets/icons/success.svg?react'; + +import styles from '../../PlanCardItem/FeaturedPlanContent/index.module.scss'; + +import useFeaturedSkuContent from './use-featured-sku-content'; + +type Props = { + readonly skuId: string; +}; + +function FeaturedSkuContent({ skuId }: Props) { + const contentData = useFeaturedSkuContent(skuId); + + return ( +
    + {contentData.map(({ title, isAvailable }) => { + return ( +
  • + {isAvailable ? ( + + ) : ( + + )} + {title} +
  • + ); + })} +
+ ); +} + +export default FeaturedSkuContent; diff --git a/packages/console/src/components/CreateTenantModal/SelectTenantPlanModal/SkuCardItem/FeaturedSkuContent/use-featured-sku-content.ts b/packages/console/src/components/CreateTenantModal/SelectTenantPlanModal/SkuCardItem/FeaturedSkuContent/use-featured-sku-content.ts new file mode 100644 index 00000000000..70662e19b03 --- /dev/null +++ b/packages/console/src/components/CreateTenantModal/SelectTenantPlanModal/SkuCardItem/FeaturedSkuContent/use-featured-sku-content.ts @@ -0,0 +1,77 @@ +import { ReservedPlanId } from '@logto/schemas'; +import { cond } from '@silverhand/essentials'; +import { useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; + +import { + freePlanAuditLogsRetentionDays, + freePlanM2mLimit, + freePlanMauLimit, + freePlanPermissionsLimit, + freePlanRoleLimit, + proPlanAuditLogsRetentionDays, +} from '@/consts/subscriptions'; + +type ContentData = { + readonly title: string; + readonly isAvailable: boolean; +}; + +const useFeaturedSkuContent = (skuId: string) => { + const { t } = useTranslation(undefined, { + keyPrefix: 'admin_console.upsell.featured_plan_content', + }); + + const contentData: ContentData[] = useMemo(() => { + const isFreePlan = skuId === ReservedPlanId.Free; + const planPhraseKey = isFreePlan ? 'free_plan' : 'pro_plan'; + + return [ + { + title: t(`mau.${planPhraseKey}`, { ...cond(isFreePlan && { count: freePlanMauLimit }) }), + isAvailable: true, + }, + { + title: t(`m2m.${planPhraseKey}`, { ...cond(isFreePlan && { count: freePlanM2mLimit }) }), + isAvailable: true, + }, + { + title: t('third_party_apps'), + isAvailable: !isFreePlan, + }, + { + title: t('mfa'), + isAvailable: !isFreePlan, + }, + { + title: t('sso'), + isAvailable: !isFreePlan, + }, + { + title: t(`role_and_permissions.${planPhraseKey}`, { + ...cond( + isFreePlan && { + roleCount: freePlanRoleLimit, + permissionCount: freePlanPermissionsLimit, + } + ), + }), + isAvailable: true, + }, + { + title: t('organizations'), + isAvailable: !isFreePlan, + }, + { + title: t('audit_logs', { + count: isFreePlan ? freePlanAuditLogsRetentionDays : proPlanAuditLogsRetentionDays, + }), + isAvailable: true, + }, + ]; + }, [t, skuId]); + + return contentData; +}; + +export default useFeaturedSkuContent; diff --git a/packages/console/src/components/CreateTenantModal/SelectTenantPlanModal/SkuCardItem/index.tsx b/packages/console/src/components/CreateTenantModal/SelectTenantPlanModal/SkuCardItem/index.tsx new file mode 100644 index 00000000000..81fbcb7c64e --- /dev/null +++ b/packages/console/src/components/CreateTenantModal/SelectTenantPlanModal/SkuCardItem/index.tsx @@ -0,0 +1,100 @@ +import { maxFreeTenantLimit, adminTenantId, ReservedPlanId } from '@logto/schemas'; +import classNames from 'classnames'; +import { useContext, useMemo } from 'react'; +import { Trans, useTranslation } from 'react-i18next'; + +import ArrowRight from '@/assets/icons/arrow-right.svg?react'; +import { type LogtoSkuResponse } from '@/cloud/types/router'; +import PlanDescription from '@/components/PlanDescription'; +import PlanName from '@/components/PlanName'; +import { pricingLink } from '@/consts'; +import { TenantsContext } from '@/contexts/TenantsProvider'; +import Button, { type Props as ButtonProps } from '@/ds-components/Button'; +import DangerousRaw from '@/ds-components/DangerousRaw'; +import DynamicT from '@/ds-components/DynamicT'; +import TextLink from '@/ds-components/TextLink'; + +import styles from '../PlanCardItem/index.module.scss'; + +import FeaturedSkuContent from './FeaturedSkuContent'; + +type Props = { + readonly sku: LogtoSkuResponse; + readonly onSelect: () => void; + readonly buttonProps?: Partial; +}; + +function SkuCardItem({ sku, onSelect, buttonProps }: Props) { + const { t } = useTranslation(undefined, { keyPrefix: 'admin_console.upsell.create_tenant' }); + const { tenants } = useContext(TenantsContext); + const { unitPrice: basePrice, id: skuId } = sku; + + const isFreeSku = skuId === ReservedPlanId.Free; + + const isFreeTenantExceeded = useMemo( + () => + /** Should not block admin tenant owners from creating more than three tenants */ + !tenants.some(({ id }) => id === adminTenantId) && + tenants.filter(({ planId }) => planId === ReservedPlanId.Free).length >= maxFreeTenantLimit, + [tenants] + ); + + return ( +
+
+
+ +
+
+
{t('base_price')}
+
+ ${t('monthly_price', { value: (basePrice ?? 0) / 100 })} +
+
+
+ +
+
+
+ + {isFreeSku && isFreeTenantExceeded && ( +
+ {t('free_tenants_limit', { count: maxFreeTenantLimit })} +
+ )} + {!isFreeSku && ( +
+ } + className={styles.link} + > + + +
+ )} +
+ {skuId === ReservedPlanId.Pro && ( +
{t('most_popular')}
+ )} +
+ ); +} + +export default SkuCardItem; diff --git a/packages/console/src/components/CreateTenantModal/SelectTenantPlanModal/index.tsx b/packages/console/src/components/CreateTenantModal/SelectTenantPlanModal/index.tsx index 38d4260599f..f7bfbfdbbd0 100644 --- a/packages/console/src/components/CreateTenantModal/SelectTenantPlanModal/index.tsx +++ b/packages/console/src/components/CreateTenantModal/SelectTenantPlanModal/index.tsx @@ -5,22 +5,25 @@ import { Trans, useTranslation } from 'react-i18next'; import Modal from 'react-modal'; import { useCloudApi, toastResponseError } from '@/cloud/hooks/use-cloud-api'; -import { type TenantResponse } from '@/cloud/types/router'; +import { type TenantResponse, type LogtoSkuResponse } from '@/cloud/types/router'; import { GtagConversionId, reportToGoogle } from '@/components/Conversion/utils'; import { pricingLink } from '@/consts'; +import { isDevFeaturesEnabled } from '@/consts/env'; import DangerousRaw from '@/ds-components/DangerousRaw'; import ModalLayout from '@/ds-components/ModalLayout'; import TextLink from '@/ds-components/TextLink'; +import useLogtoSkus from '@/hooks/use-logto-skus'; import useSubscribe from '@/hooks/use-subscribe'; import useSubscriptionPlans from '@/hooks/use-subscription-plans'; -import * as modalStyles from '@/scss/modal.module.scss'; +import modalStyles from '@/scss/modal.module.scss'; import { type SubscriptionPlan } from '@/types/subscriptions'; -import { pickupFeaturedPlans } from '@/utils/subscription'; +import { pickupFeaturedPlans, pickupFeaturedLogtoSkus } from '@/utils/subscription'; import { type CreateTenantData } from '../types'; import PlanCardItem from './PlanCardItem'; -import * as styles from './index.module.scss'; +import SkuCardItem from './SkuCardItem'; +import styles from './index.module.scss'; type Props = { readonly tenantData?: CreateTenantData; @@ -28,21 +31,27 @@ type Props = { }; function SelectTenantPlanModal({ tenantData, onClose }: Props) { - const [isSubmitting, setIsSubmitting] = useState(); + const [processingPlanId, setProcessingPlanId] = useState(); + const [processingSkuId, setProcessingSkuId] = useState(); const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); + const { data: subscriptionPlans } = useSubscriptionPlans(); + const { data: logtoSkus } = useLogtoSkus(); + const { subscribe } = useSubscribe(); const cloudApi = useCloudApi({ hideErrorToast: true }); + const reservedPlans = conditional(subscriptionPlans && pickupFeaturedPlans(subscriptionPlans)); + const reservedBasicLogtoSkus = conditional(logtoSkus && pickupFeaturedLogtoSkus(logtoSkus)); - if (!reservedPlans || !tenantData) { + if (!reservedPlans || !reservedBasicLogtoSkus || !tenantData) { return null; } const handleSelectPlan = async (plan: SubscriptionPlan) => { const { id: planId } = plan; try { - setIsSubmitting(planId); + setProcessingPlanId(planId); if (planId === ReservedPlanId.Free) { const { name, tag, regionName } = tenantData; const newTenant = await cloudApi.post('/api/tenants', { body: { name, tag, regionName } }); @@ -56,7 +65,28 @@ function SelectTenantPlanModal({ tenantData, onClose }: Props) { } catch (error: unknown) { void toastResponseError(error); } finally { - setIsSubmitting(undefined); + setProcessingPlanId(undefined); + } + }; + + const handleSelectSku = async (logtoSku: LogtoSkuResponse) => { + const { id: skuId } = logtoSku; + try { + setProcessingSkuId(skuId); + if (skuId === ReservedPlanId.Free) { + const { name, tag, regionName } = tenantData; + const newTenant = await cloudApi.post('/api/tenants', { body: { name, tag, regionName } }); + + reportToGoogle(GtagConversionId.CreateProductionTenant, { transactionId: newTenant.id }); + onClose(newTenant); + return; + } + + await subscribe({ skuId, planId: skuId, tenantData }); + } catch (error: unknown) { + void toastResponseError(error); + } finally { + setProcessingSkuId(undefined); } }; @@ -83,19 +113,33 @@ function SelectTenantPlanModal({ tenantData, onClose }: Props) { onClose={onClose} >
- {reservedPlans.map((plan) => ( - { - void handleSelectPlan(plan); - }} - /> - ))} + {isDevFeaturesEnabled + ? reservedBasicLogtoSkus.map((logtoSku) => ( + { + void handleSelectSku(logtoSku); + }} + /> + )) + : reservedPlans.map((plan) => ( + { + void handleSelectPlan(plan); + }} + /> + ))}
diff --git a/packages/console/src/components/CreateTenantModal/index.tsx b/packages/console/src/components/CreateTenantModal/index.tsx index d6525cc5162..3dc2fd67878 100644 --- a/packages/console/src/components/CreateTenantModal/index.tsx +++ b/packages/console/src/components/CreateTenantModal/index.tsx @@ -5,8 +5,8 @@ import { toast } from 'react-hot-toast'; import { useTranslation } from 'react-i18next'; import Modal from 'react-modal'; -import CreateTenantHeaderIconDark from '@/assets/icons/create-tenant-header-dark.svg'; -import CreateTenantHeaderIcon from '@/assets/icons/create-tenant-header.svg'; +import CreateTenantHeaderIconDark from '@/assets/icons/create-tenant-header-dark.svg?react'; +import CreateTenantHeaderIcon from '@/assets/icons/create-tenant-header.svg?react'; import { useCloudApi } from '@/cloud/hooks/use-cloud-api'; import { type TenantResponse } from '@/cloud/types/router'; import Region, { RegionName } from '@/components/Region'; @@ -17,11 +17,11 @@ import ModalLayout from '@/ds-components/ModalLayout'; import RadioGroup, { Radio } from '@/ds-components/RadioGroup'; import TextInput from '@/ds-components/TextInput'; import useTheme from '@/hooks/use-theme'; -import * as modalStyles from '@/scss/modal.module.scss'; +import modalStyles from '@/scss/modal.module.scss'; import EnvTagOptionContent from './EnvTagOptionContent'; import SelectTenantPlanModal from './SelectTenantPlanModal'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; import { type CreateTenantData } from './types'; type Props = { diff --git a/packages/console/src/components/DateTime/index.tsx b/packages/console/src/components/DateTime/index.tsx index 766d94d28d7..c40b39bfd4a 100644 --- a/packages/console/src/components/DateTime/index.tsx +++ b/packages/console/src/components/DateTime/index.tsx @@ -1,18 +1,30 @@ -import type { Nullable } from '@silverhand/essentials'; +import { type Nullable } from '@silverhand/essentials'; import { isValid } from 'date-fns'; -type Props = { - readonly children: Nullable; -}; +const parseDate = (date: Nullable) => { + if (!date) { + return; + } -function DateTime({ children }: Props) { - const date = children && new Date(children); + const parsed = new Date(date); + return isValid(parsed) ? parsed : undefined; +}; - if (!date || !isValid(date)) { - return -; - } +type Props = { + readonly children: Nullable; +}; - return {date.toLocaleDateString()}; +/** + * Safely display a date in the user's locale. If the date is invalid, it will display a dash. + */ +export function LocaleDate({ children }: Props) { + return {parseDate(children)?.toLocaleDateString() ?? '-'}; } -export default DateTime; +/** + * Safely display a date and time in the user's locale. If the date is invalid, it will display a + * dash. + */ +export function LocaleDateTime({ children }: Props) { + return {parseDate(children)?.toLocaleString() ?? '-'}; +} diff --git a/packages/console/src/components/DetailsForm/index.tsx b/packages/console/src/components/DetailsForm/index.tsx index c19161d60c4..fb2b8ef231a 100644 --- a/packages/console/src/components/DetailsForm/index.tsx +++ b/packages/console/src/components/DetailsForm/index.tsx @@ -3,7 +3,7 @@ import type { ReactNode } from 'react'; import SubmitFormChangesActionBar from '../SubmitFormChangesActionBar'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly autoComplete?: string; diff --git a/packages/console/src/components/DetailsPage/DetailsPageHeader/index.tsx b/packages/console/src/components/DetailsPage/DetailsPageHeader/index.tsx index b4b431a33a9..d72e741f6bf 100644 --- a/packages/console/src/components/DetailsPage/DetailsPageHeader/index.tsx +++ b/packages/console/src/components/DetailsPage/DetailsPageHeader/index.tsx @@ -10,7 +10,7 @@ import { } from 'react'; import { useTranslation } from 'react-i18next'; -import More from '@/assets/icons/more.svg'; +import More from '@/assets/icons/more.svg?react'; import ActionMenu, { ActionMenuItem } from '@/ds-components/ActionMenu'; import Button from '@/ds-components/Button'; import Card from '@/ds-components/Card'; @@ -20,7 +20,7 @@ import DynamicT from '@/ds-components/DynamicT'; import Tag, { type Props as TagProps } from '@/ds-components/Tag'; import useWindowResize from '@/hooks/use-window-resize'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type StatusTag = { status: TagProps['status']; diff --git a/packages/console/src/components/DetailsPage/Skeleton/index.module.scss b/packages/console/src/components/DetailsPage/Skeleton/index.module.scss index f01e5691a71..d7a86f5d7c4 100644 --- a/packages/console/src/components/DetailsPage/Skeleton/index.module.scss +++ b/packages/console/src/components/DetailsPage/Skeleton/index.module.scss @@ -18,11 +18,11 @@ background-color: var(--color-layer-1); .icon { - @include _.shimmering-animation; width: 60px; height: 60px; border-radius: 12px; margin-right: _.unit(6); + @include _.shimmering-animation; } .wrapper { @@ -30,23 +30,23 @@ flex-direction: column; .title { - @include _.shimmering-animation; width: 113px; height: 28px; + @include _.shimmering-animation; } .tags { - @include _.shimmering-animation; width: 453px; height: 20px; margin-top: _.unit(3); + @include _.shimmering-animation; } } .button { - @include _.shimmering-animation; width: 158px; height: 44px; + @include _.shimmering-animation; } } diff --git a/packages/console/src/components/DetailsPage/Skeleton/index.tsx b/packages/console/src/components/DetailsPage/Skeleton/index.tsx index b796c621e91..58bfcb30b24 100644 --- a/packages/console/src/components/DetailsPage/Skeleton/index.tsx +++ b/packages/console/src/components/DetailsPage/Skeleton/index.tsx @@ -1,7 +1,7 @@ import { FormCardSkeleton } from '@/components/FormCard'; import Spacer from '@/ds-components/Spacer'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; function Skeleton() { return ( diff --git a/packages/console/src/components/DetailsPage/index.tsx b/packages/console/src/components/DetailsPage/index.tsx index c6083122d0f..2f4f8677413 100644 --- a/packages/console/src/components/DetailsPage/index.tsx +++ b/packages/console/src/components/DetailsPage/index.tsx @@ -3,7 +3,7 @@ import classNames from 'classnames'; import type { ReactElement, ReactNode } from 'react'; import { type To } from 'react-router-dom'; -import Back from '@/assets/icons/back.svg'; +import Back from '@/assets/icons/back.svg?react'; import type DangerousRaw from '@/ds-components/DangerousRaw'; import DynamicT from '@/ds-components/DynamicT'; import TextLink from '@/ds-components/TextLink'; @@ -12,7 +12,7 @@ import type { RequestError } from '@/hooks/use-api'; import RequestDataError from '../RequestDataError'; import Skeleton from './Skeleton'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly backLink: To; diff --git a/packages/console/src/components/Drawer/index.tsx b/packages/console/src/components/Drawer/index.tsx index 99e004b664d..bf920ab5d79 100644 --- a/packages/console/src/components/Drawer/index.tsx +++ b/packages/console/src/components/Drawer/index.tsx @@ -1,12 +1,12 @@ import type { AdminConsoleKey } from '@logto/phrases'; import ReactModal from 'react-modal'; -import Close from '@/assets/icons/close.svg'; +import Close from '@/assets/icons/close.svg?react'; import CardTitle from '@/ds-components/CardTitle'; import IconButton from '@/ds-components/IconButton'; import Spacer from '@/ds-components/Spacer'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly title?: AdminConsoleKey; diff --git a/packages/console/src/components/EditScopeModal/index.tsx b/packages/console/src/components/EditScopeModal/index.tsx index 1ba3bc37bee..5e8a3cc1fda 100644 --- a/packages/console/src/components/EditScopeModal/index.tsx +++ b/packages/console/src/components/EditScopeModal/index.tsx @@ -8,7 +8,7 @@ import Button from '@/ds-components/Button'; import FormField from '@/ds-components/FormField'; import ModalLayout from '@/ds-components/ModalLayout'; import TextInput from '@/ds-components/TextInput'; -import * as modalStyles from '@/scss/modal.module.scss'; +import modalStyles from '@/scss/modal.module.scss'; import { trySubmitSafe } from '@/utils/form'; export type EditScopeData = { diff --git a/packages/console/src/components/EmptyDataPlaceholder/index.tsx b/packages/console/src/components/EmptyDataPlaceholder/index.tsx index bdf1706507b..4b7d12ad7b0 100644 --- a/packages/console/src/components/EmptyDataPlaceholder/index.tsx +++ b/packages/console/src/components/EmptyDataPlaceholder/index.tsx @@ -3,11 +3,11 @@ import classNames from 'classnames'; import { type ReactNode } from 'react'; import { useTranslation } from 'react-i18next'; -import EmptyDark from '@/assets/images/table-empty-dark.svg'; -import Empty from '@/assets/images/table-empty.svg'; +import EmptyDark from '@/assets/images/table-empty-dark.svg?react'; +import Empty from '@/assets/images/table-empty.svg?react'; import useTheme from '@/hooks/use-theme'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly title?: ReactNode; diff --git a/packages/console/src/components/EntitiesTransfer/components/EntityItem/index.tsx b/packages/console/src/components/EntitiesTransfer/components/EntityItem/index.tsx index 0b8ccaa9db2..7646f9f66ee 100644 --- a/packages/console/src/components/EntitiesTransfer/components/EntityItem/index.tsx +++ b/packages/console/src/components/EntitiesTransfer/components/EntityItem/index.tsx @@ -5,7 +5,7 @@ import UserAvatar from '@/components/UserAvatar'; import SuspendedTag from '@/pages/Users/components/SuspendedTag'; import { getUserTitle } from '@/utils/user'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type UserItemProps = { readonly entity: User; diff --git a/packages/console/src/components/EntitiesTransfer/components/SourceEntitiesBox/index.tsx b/packages/console/src/components/EntitiesTransfer/components/SourceEntitiesBox/index.tsx index e8402edf490..01715df9161 100644 --- a/packages/console/src/components/EntitiesTransfer/components/SourceEntitiesBox/index.tsx +++ b/packages/console/src/components/EntitiesTransfer/components/SourceEntitiesBox/index.tsx @@ -6,7 +6,7 @@ import { useState } from 'react'; import { useTranslation } from 'react-i18next'; import useSWR from 'swr'; -import Search from '@/assets/icons/search.svg'; +import Search from '@/assets/icons/search.svg?react'; import EmptyDataPlaceholder from '@/components/EmptyDataPlaceholder'; import { defaultPageSize } from '@/consts'; import DynamicT from '@/ds-components/DynamicT'; @@ -14,13 +14,13 @@ import Pagination from '@/ds-components/Pagination'; import TextInput from '@/ds-components/TextInput'; import type { RequestError } from '@/hooks/use-api'; import useDebounce from '@/hooks/use-debounce'; -import * as transferLayout from '@/scss/transfer.module.scss'; +import transferLayout from '@/scss/transfer.module.scss'; import { type Identifiable } from '@/types/general'; import { buildUrl, formatSearchKeyword } from '@/utils/url'; import SourceEntityItem from '../SourceEntityItem'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type SearchProps = { pathname: string; diff --git a/packages/console/src/components/EntitiesTransfer/components/SourceEntityItem/index.tsx b/packages/console/src/components/EntitiesTransfer/components/SourceEntityItem/index.tsx index b26f110b3ae..1c3dc0d1820 100644 --- a/packages/console/src/components/EntitiesTransfer/components/SourceEntityItem/index.tsx +++ b/packages/console/src/components/EntitiesTransfer/components/SourceEntityItem/index.tsx @@ -4,7 +4,7 @@ import Checkbox from '@/ds-components/Checkbox'; import { type Identifiable } from '@/types/general'; import { onKeyDownHandler } from '@/utils/a11y'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly entity: T; diff --git a/packages/console/src/components/EntitiesTransfer/components/TargetEntitiesBox/index.tsx b/packages/console/src/components/EntitiesTransfer/components/TargetEntitiesBox/index.tsx index 145cbbfb31b..7c19d6e69cf 100644 --- a/packages/console/src/components/EntitiesTransfer/components/TargetEntitiesBox/index.tsx +++ b/packages/console/src/components/EntitiesTransfer/components/TargetEntitiesBox/index.tsx @@ -1,11 +1,11 @@ import { useTranslation } from 'react-i18next'; -import * as transferLayout from '@/scss/transfer.module.scss'; +import transferLayout from '@/scss/transfer.module.scss'; import { type Identifiable } from '@/types/general'; import TargetEntityItem from '../TargetEntityItem'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly renderEntity: (entity: T) => React.ReactNode; diff --git a/packages/console/src/components/EntitiesTransfer/components/TargetEntityItem/index.tsx b/packages/console/src/components/EntitiesTransfer/components/TargetEntityItem/index.tsx index 8c71be5d205..47c4f05b19b 100644 --- a/packages/console/src/components/EntitiesTransfer/components/TargetEntityItem/index.tsx +++ b/packages/console/src/components/EntitiesTransfer/components/TargetEntityItem/index.tsx @@ -1,10 +1,10 @@ import { type ReactNode } from 'react'; -import Close from '@/assets/icons/close.svg'; +import Close from '@/assets/icons/close.svg?react'; import IconButton from '@/ds-components/IconButton'; import { type Identifiable } from '@/types/general'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly entity: T; diff --git a/packages/console/src/components/EntitiesTransfer/index.tsx b/packages/console/src/components/EntitiesTransfer/index.tsx index ffec093f2ef..87d4ca74344 100644 --- a/packages/console/src/components/EntitiesTransfer/index.tsx +++ b/packages/console/src/components/EntitiesTransfer/index.tsx @@ -1,11 +1,11 @@ import classNames from 'classnames'; -import * as transferLayout from '@/scss/transfer.module.scss'; +import transferLayout from '@/scss/transfer.module.scss'; import { type Identifiable } from '@/types/general'; import SourceEntitiesBox, { type Props as SourceProps } from './components/SourceEntitiesBox'; import TargetEntitiesBox from './components/TargetEntitiesBox'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = SourceProps & { readonly errorMessage?: string; diff --git a/packages/console/src/components/FeatureTag/AddOnTag.tsx b/packages/console/src/components/FeatureTag/AddOnTag.tsx new file mode 100644 index 00000000000..68112af7ab2 --- /dev/null +++ b/packages/console/src/components/FeatureTag/AddOnTag.tsx @@ -0,0 +1,19 @@ +import classNames from 'classnames'; + +/** + * AddOnTag static component + * + * Used to indicate that a feature is add-on feature and will be charged according to usage. + */ + +import styles from './index.module.scss'; + +type Props = { + readonly className?: string; +}; + +function AddOnTag({ className }: Props) { + return
Add-on
; +} + +export default AddOnTag; diff --git a/packages/console/src/components/FeatureTag/BetaTag.tsx b/packages/console/src/components/FeatureTag/BetaTag.tsx index 4bbe194a095..452eab39d3b 100644 --- a/packages/console/src/components/FeatureTag/BetaTag.tsx +++ b/packages/console/src/components/FeatureTag/BetaTag.tsx @@ -6,7 +6,7 @@ import classNames from 'classnames'; * Used to indicate that a new released feature is in beta. */ -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly className?: string; diff --git a/packages/console/src/components/FeatureTag/index.tsx b/packages/console/src/components/FeatureTag/index.tsx index 6363a58518a..152ae00f037 100644 --- a/packages/console/src/components/FeatureTag/index.tsx +++ b/packages/console/src/components/FeatureTag/index.tsx @@ -1,14 +1,17 @@ -import { type ReservedPlanId } from '@logto/schemas'; +import { ReservedPlanId } from '@logto/schemas'; import classNames from 'classnames'; import { useContext } from 'react'; +import { isDevFeaturesEnabled } from '@/consts/env'; +import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider'; import { TenantsContext } from '@/contexts/TenantsProvider'; -import * as styles from './index.module.scss'; +import AddOnTag from './AddOnTag'; +import styles from './index.module.scss'; export { default as BetaTag } from './BetaTag'; -type Props = { +export type Props = { /** * Whether the tag should be visible. It should be `true` if the tenant's subscription * plan has NO access to the feature (paywall), but it will always be visible for dev @@ -50,6 +53,9 @@ type Props = { function FeatureTag(props: Props) { const { className } = props; const { isDevTenant } = useContext(TenantsContext); + const { + currentSubscription: { planId }, + } = useContext(SubscriptionDataContext); const { isVisible, plan } = props; @@ -59,6 +65,11 @@ function FeatureTag(props: Props) { return null; } + // Show the add-on tag for Pro plan when dev features are enabled. + if (isDevFeaturesEnabled && planId === ReservedPlanId.Pro) { + return ; + } + return
{plan}
; } diff --git a/packages/console/src/components/FileIcon/index.tsx b/packages/console/src/components/FileIcon/index.tsx new file mode 100644 index 00000000000..29c8585ff8b --- /dev/null +++ b/packages/console/src/components/FileIcon/index.tsx @@ -0,0 +1,20 @@ +import { Theme } from '@logto/schemas'; +import { type ReactNode } from 'react'; + +import FileIconDark from '@/assets/icons/file-icon-dark.svg?react'; +import FileIconLight from '@/assets/icons/file-icon.svg?react'; +import useTheme from '@/hooks/use-theme'; + +const themeToRoleIcon = Object.freeze({ + [Theme.Light]: , + [Theme.Dark]: , +} satisfies Record); + +/** Render a role icon according to the current theme. */ +const FileIcon = () => { + const theme = useTheme(); + + return themeToRoleIcon[theme]; +}; + +export default FileIcon; diff --git a/packages/console/src/components/FormCard/FormCardLayout/index.module.scss b/packages/console/src/components/FormCard/FormCardLayout/index.module.scss index a929b9f9f85..cb10864dbc3 100644 --- a/packages/console/src/components/FormCard/FormCardLayout/index.module.scss +++ b/packages/console/src/components/FormCard/FormCardLayout/index.module.scss @@ -25,7 +25,7 @@ $column-width: calc((100% - 23 * $gutter-width) / 24); width: calc($column-width * 16 + $gutter-width * 15); } - @container (max-width: 600px) { + @container (max-width: 800px) { .container { flex-direction: column; justify-content: unset; diff --git a/packages/console/src/components/FormCard/FormCardLayout/index.tsx b/packages/console/src/components/FormCard/FormCardLayout/index.tsx index a19c3c66343..1622ed74cf7 100644 --- a/packages/console/src/components/FormCard/FormCardLayout/index.tsx +++ b/packages/console/src/components/FormCard/FormCardLayout/index.tsx @@ -2,7 +2,7 @@ import { type ReactNode } from 'react'; import Card from '@/ds-components/Card'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly introduction: ReactNode; diff --git a/packages/console/src/components/FormCard/Skeleton/index.module.scss b/packages/console/src/components/FormCard/Skeleton/index.module.scss index 35406566d05..6a11b36f3c9 100644 --- a/packages/console/src/components/FormCard/Skeleton/index.module.scss +++ b/packages/console/src/components/FormCard/Skeleton/index.module.scss @@ -1,27 +1,17 @@ @use '@/scss/underscore' as _; .title { - @include _.shimmering-animation; height: 16px; width: 80px; + @include _.shimmering-animation; } .text { - @include _.shimmering-animation; width: 100%; height: 10px; + @include _.shimmering-animation; } .text + .text { margin-top: _.unit(2); } - -.field { - @include _.shimmering-animation; - width: 100%; - height: 44px; -} - -.field + .field { - margin-top: _.unit(6); -} diff --git a/packages/console/src/components/FormCard/Skeleton/index.tsx b/packages/console/src/components/FormCard/Skeleton/index.tsx index cafb1f21940..c9fc9f5a216 100644 --- a/packages/console/src/components/FormCard/Skeleton/index.tsx +++ b/packages/console/src/components/FormCard/Skeleton/index.tsx @@ -1,6 +1,8 @@ +import FormFieldSkeleton from '@/ds-components/FormField/Skeleton'; + import FormCardLayout from '../FormCardLayout'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly formFieldCount?: number; @@ -21,10 +23,7 @@ function Skeleton({ formFieldCount = 4 }: Props) { } > - {Array.from({ length: formFieldCount }).map((_, index) => ( - // eslint-disable-next-line react/no-array-index-key -
- ))} + ); } diff --git a/packages/console/src/components/FormCard/index.tsx b/packages/console/src/components/FormCard/index.tsx index 565a3615a49..6befab89d5c 100644 --- a/packages/console/src/components/FormCard/index.tsx +++ b/packages/console/src/components/FormCard/index.tsx @@ -6,7 +6,7 @@ import TextLink from '@/ds-components/TextLink'; import type { Props as TextLinkProps } from '@/ds-components/TextLink'; import FormCardLayout from './FormCardLayout'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; export type Props = { readonly title: AdminConsoleKey; diff --git a/packages/console/src/components/Guide/GuideCard/index.module.scss b/packages/console/src/components/Guide/GuideCard/index.module.scss index c962c7593e1..bf6890d3348 100644 --- a/packages/console/src/components/Guide/GuideCard/index.module.scss +++ b/packages/console/src/components/Guide/GuideCard/index.module.scss @@ -32,9 +32,14 @@ } .logo { - width: 48px; - height: 48px; + padding: _.unit(1.5); flex-shrink: 0; + + > svg, + > img { + width: 36px; + height: 36px; + } } .tagWrapper { diff --git a/packages/console/src/components/Guide/GuideCard/index.tsx b/packages/console/src/components/Guide/GuideCard/index.tsx index fef3471ecf8..c785aaf1326 100644 --- a/packages/console/src/components/Guide/GuideCard/index.tsx +++ b/packages/console/src/components/Guide/GuideCard/index.tsx @@ -1,4 +1,4 @@ -import { ReservedPlanId } from '@logto/schemas'; +import { ReservedPlanId, Theme } from '@logto/schemas'; import classNames from 'classnames'; import { Suspense, useCallback, useContext } from 'react'; @@ -7,9 +7,10 @@ import FeatureTag, { BetaTag } from '@/components/FeatureTag'; import { isCloud } from '@/consts/env'; import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider'; import Button from '@/ds-components/Button'; +import useTheme from '@/hooks/use-theme'; import { onKeyDownHandler } from '@/utils/a11y'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; export type SelectedGuide = { id: Guide['id']; @@ -29,11 +30,13 @@ function GuideCard({ data, onClick, hasBorder, hasButton }: Props) { const { id, Logo, + DarkLogo, metadata: { target, name, description, isThirdParty }, } = data; const buttonText = target === 'API' ? 'guide.get_started' : 'guide.start_building'; const { currentPlan } = useContext(SubscriptionDataContext); + const theme = useTheme(); const showPaywallTag = isCloud && isThirdParty; const showBetaTag = isCloud && isThirdParty; @@ -58,7 +61,9 @@ function GuideCard({ data, onClick, hasBorder, hasButton }: Props) { >
}> - +
+ {theme === Theme.Dark && DarkLogo ? : } +
diff --git a/packages/console/src/components/Guide/GuideCardGroup/index.tsx b/packages/console/src/components/Guide/GuideCardGroup/index.tsx index e23fb721fef..a979d68e5d0 100644 --- a/packages/console/src/components/Guide/GuideCardGroup/index.tsx +++ b/packages/console/src/components/Guide/GuideCardGroup/index.tsx @@ -5,7 +5,7 @@ import { type Guide } from '@/assets/docs/guides/types'; import GuideCard, { type SelectedGuide } from '../GuideCard'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly className?: string; diff --git a/packages/console/src/components/Guide/ModalFooter/index.tsx b/packages/console/src/components/Guide/ModalFooter/index.tsx index d43a8960221..42862ea5d18 100644 --- a/packages/console/src/components/Guide/ModalFooter/index.tsx +++ b/packages/console/src/components/Guide/ModalFooter/index.tsx @@ -4,7 +4,7 @@ import classNames from 'classnames'; import Button from '@/ds-components/Button'; import DynamicT from '@/ds-components/DynamicT'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly wrapperClassName?: string; diff --git a/packages/console/src/components/Guide/ModalHeader/RequestForm.tsx b/packages/console/src/components/Guide/ModalHeader/RequestForm.tsx index afa1a94c72b..7232c3b6cd8 100644 --- a/packages/console/src/components/Guide/ModalHeader/RequestForm.tsx +++ b/packages/console/src/components/Guide/ModalHeader/RequestForm.tsx @@ -10,7 +10,7 @@ import FormField from '@/ds-components/FormField'; import ModalLayout from '@/ds-components/ModalLayout'; import TextInput from '@/ds-components/TextInput'; import useCurrentUser from '@/hooks/use-current-user'; -import * as modalStyles from '@/scss/modal.module.scss'; +import modalStyles from '@/scss/modal.module.scss'; type Props = { readonly title: AdminConsoleKey; diff --git a/packages/console/src/components/Guide/ModalHeader/index.tsx b/packages/console/src/components/Guide/ModalHeader/index.tsx index dbef8c32208..99f40efdb20 100644 --- a/packages/console/src/components/Guide/ModalHeader/index.tsx +++ b/packages/console/src/components/Guide/ModalHeader/index.tsx @@ -1,14 +1,14 @@ import { type AdminConsoleKey } from '@logto/phrases'; import { useCallback, useState } from 'react'; -import Box from '@/assets/icons/box.svg'; +import Box from '@/assets/icons/box.svg?react'; import { githubIssuesLink } from '@/consts'; import { isCloud } from '@/consts/env'; import Button from '@/ds-components/Button'; import DsModalHeader from '@/ds-components/ModalHeader'; import RequestForm from './RequestForm'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly title: AdminConsoleKey; diff --git a/packages/console/src/components/Guide/StepsSkeleton/index.module.scss b/packages/console/src/components/Guide/StepsSkeleton/index.module.scss index e221c516409..e9c657a4ac3 100644 --- a/packages/console/src/components/Guide/StepsSkeleton/index.module.scss +++ b/packages/console/src/components/Guide/StepsSkeleton/index.module.scss @@ -11,11 +11,11 @@ margin: 0 auto; .index { - @include _.shimmering-animation; width: 28px; height: 28px; border-radius: 50%; margin-right: _.unit(4); + @include _.shimmering-animation; } .wrapper { @@ -24,16 +24,16 @@ flex-direction: column; .title { - @include _.shimmering-animation; width: 140px; height: 24px; + @include _.shimmering-animation; } .subtitle { - @include _.shimmering-animation; width: 400px; height: 20px; margin-top: _.unit(1); + @include _.shimmering-animation; } } } diff --git a/packages/console/src/components/Guide/StepsSkeleton/index.tsx b/packages/console/src/components/Guide/StepsSkeleton/index.tsx index 7d3ed4e75d9..5557f532f57 100644 --- a/packages/console/src/components/Guide/StepsSkeleton/index.tsx +++ b/packages/console/src/components/Guide/StepsSkeleton/index.tsx @@ -1,4 +1,4 @@ -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; function StepsSkeleton() { return ( diff --git a/packages/console/src/components/Guide/index.tsx b/packages/console/src/components/Guide/index.tsx index be312df981e..d68f7613928 100644 --- a/packages/console/src/components/Guide/index.tsx +++ b/packages/console/src/components/Guide/index.tsx @@ -7,10 +7,11 @@ import { type GuideMetadata } from '@/assets/docs/guides/types'; import Button from '@/ds-components/Button'; import OverlayScrollbar from '@/ds-components/OverlayScrollbar'; import MdxProvider from '@/mdx-components/MdxProvider'; +import { type ApplicationSecretRow } from '@/pages/ApplicationDetails/ApplicationDetailsContent/EndpointsAndCredentials'; import NotFound from '@/pages/NotFound'; import StepsSkeleton from './StepsSkeleton'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; export type GuideContextType = { metadata: Readonly; @@ -19,6 +20,7 @@ export type GuideContextType = { | ((props: { readonly className?: string }) => JSX.Element); isCompact: boolean; app?: ApplicationResponse; + secrets?: ApplicationSecretRow[]; endpoint?: string; redirectUris?: string[]; postLogoutRedirectUris?: string[]; @@ -40,6 +42,7 @@ export const GuideContext = createContext({ metadata: {} as GuideMetadata, // eslint-disable-next-line @typescript-eslint/consistent-type-assertions, no-restricted-syntax app: {} as ApplicationResponse, + secrets: [], endpoint: '', redirectUris: [], postLogoutRedirectUris: [], diff --git a/packages/console/src/components/ImageInputs/LogoAndFavicon.tsx b/packages/console/src/components/ImageInputs/LogoAndFavicon.tsx new file mode 100644 index 00000000000..b60af048eb6 --- /dev/null +++ b/packages/console/src/components/ImageInputs/LogoAndFavicon.tsx @@ -0,0 +1,62 @@ +import { type Theme } from '@logto/schemas'; +import { type FieldValues, type Control, type UseFormRegister } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; + +import ImageInputs, { type ImageField } from '.'; + +type Field = Pick, 'name' | 'error'>; + +type Props = { + readonly theme: Theme; + readonly control: Control; + readonly register: UseFormRegister; + /** + * Form-related data of the logo input, including the name (field path) and error in the form. + */ + readonly logo: Field; + /** + * Form-related data of the favicon input, including the name (field path) and error in the form. + */ + readonly favicon: Field; + /** The type of the logo. It will affect the translation key. */ + readonly type: 'app_logo' | 'company_logo'; +}; + +/** + * A component that renders the logo and favicon inputs for a form. + * + * When user assets service is available, it will render two image uploader components side-by-side; + * otherwise, it will render two text inputs. + * + * @see {@link ImageInputs} for the implementation of the inner components. + */ +function LogoAndFavicon({ + theme, + control, + register, + logo, + favicon, + type, +}: Props) { + const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); + + return ( + + {t(`sign_in_exp.branding.with_${theme}`, { + value: t(`sign_in_exp.branding.${type}_and_favicon`), + })} + + } + control={control} + register={register} + fields={[ + { ...logo, theme, type }, + { ...favicon, theme, type: 'favicon' }, + ]} + /> + ); +} + +export default LogoAndFavicon; diff --git a/packages/console/src/pages/SignInExperience/PageContent/Branding/BrandingForm/LogoAndFaviconUploader/index.module.scss b/packages/console/src/components/ImageInputs/index.module.scss similarity index 69% rename from packages/console/src/pages/SignInExperience/PageContent/Branding/BrandingForm/LogoAndFaviconUploader/index.module.scss rename to packages/console/src/components/ImageInputs/index.module.scss index 1f8c81e86ab..4e24be27ac4 100644 --- a/packages/console/src/pages/SignInExperience/PageContent/Branding/BrandingForm/LogoAndFaviconUploader/index.module.scss +++ b/packages/console/src/components/ImageInputs/index.module.scss @@ -1,20 +1,19 @@ @use '@/scss/underscore' as _; .container { - display: flex; - flex-direction: column; -} - -.uploader { display: flex; gap: _.unit(2); - .logoUploader { - flex: 2 0; + > * { + flex: 1; + + &.dark { + background-color: #111; + } } - .faviconUploader { - flex: 1; + .logo { + flex: 3; } } diff --git a/packages/console/src/components/ImageInputs/index.tsx b/packages/console/src/components/ImageInputs/index.tsx new file mode 100644 index 00000000000..6dea4890cba --- /dev/null +++ b/packages/console/src/components/ImageInputs/index.tsx @@ -0,0 +1,169 @@ +import { type LocalePhrase } from '@logto/phrases'; +import { Theme } from '@logto/schemas'; +import { cond, noop } from '@silverhand/essentials'; +import classNames from 'classnames'; +import type React from 'react'; +import { useMemo, useState } from 'react'; +import { + Controller, + type FieldPath, + type FieldValues, + type Control, + type UseFormRegister, + type FieldError, +} from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; + +import FormField from '@/ds-components/FormField'; +import Skeleton from '@/ds-components/FormField/Skeleton'; +import TextInput from '@/ds-components/TextInput'; +import ImageUploader from '@/ds-components/Uploader/ImageUploader'; +import useImageMimeTypes from '@/hooks/use-image-mime-types'; +import useUserAssetsService from '@/hooks/use-user-assets-service'; +import { uriValidator } from '@/utils/validator'; + +import styles from './index.module.scss'; + +export const themeToLogoName = Object.freeze({ + [Theme.Light]: 'logoUrl', + [Theme.Dark]: 'darkLogoUrl', +} as const satisfies Record); + +export type ImageField = { + /** The name (field path) of the field in the form. */ + name: FieldPath; + /** + * The type of the field. It should match the existing structure in the translation file to get + * the correct translations. + */ + type: keyof LocalePhrase['translation']['admin_console']['sign_in_exp']['branding_uploads']; + theme: Theme; + /** The error message of the field in the form. */ + error?: FieldError; +}; + +type Props = { + /** The condensed title when user assets service is available. */ + readonly uploadTitle: React.ComponentProps['title']; + /** + * When user assets service is available, the tip will be displayed for the `uploadTitle`; + * otherwise, it will be displayed for each text input. + */ + readonly tip?: React.ComponentProps['tip']; + readonly control: Control; + readonly register: UseFormRegister; + readonly fields: Array>; +}; + +/** + * A component that renders the logo inputs for a form. + * + * When user assets service is available, it will render the image uploader components side-by-side; + * otherwise, it will render the text inputs. + */ +function ImageInputs({ + uploadTitle, + tip, + control, + register, + fields, +}: Props) { + const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); + const [uploadErrors, setUploadErrors] = useState>>({}); + const { description } = useImageMimeTypes(); + const { isReady: isUserAssetsServiceReady, isLoading } = useUserAssetsService(); + const uploadErrorChangeHandlers = useMemo( + () => + Object.fromEntries( + fields.map((field) => [ + field.name, + (message?: string) => { + setUploadErrors((previous) => ({ ...previous, [field.name]: message })); + }, + ]) + ), + [fields] + ); + + if (isLoading) { + return ; + } + + if (!isUserAssetsServiceReady) { + return ( + <> + {fields.map((field) => ( + + {t(`sign_in_exp.branding.with_${field.theme}`, { + value: t(`sign_in_exp.branding_uploads.${field.type}.url`), + })} + + } + > + + !value || uriValidator(value) || t('errors.invalid_uri_format'), + shouldUnregister: true, + })} + placeholder={t(`sign_in_exp.branding_uploads.${field.type}.url_placeholder`)} + error={field.error?.message} + /> + + ))} + + ); + } + + return ( + +
+ {fields.map((field) => ( + ( + { + onChange(url); + }} + // Noop fallback should not be necessary, but for TypeScript to be happy + onUploadErrorChange={uploadErrorChangeHandlers[field.name] ?? noop} + onDelete={() => { + onChange(''); + }} + /> + )} + /> + ))} +
+ {fields.map( + (field) => + uploadErrors[field.name] && ( +
+ {t(`sign_in_exp.branding_uploads.${field.type}.error`, { + error: uploadErrors[field.name], + })} +
+ ) + )} + +
{description}
+
+ ); +} + +export default ImageInputs; diff --git a/packages/console/src/components/Index/index.tsx b/packages/console/src/components/Index/index.tsx index d9584593dca..4c9036e0e44 100644 --- a/packages/console/src/components/Index/index.tsx +++ b/packages/console/src/components/Index/index.tsx @@ -1,8 +1,8 @@ import classNames from 'classnames'; -import Tick from '@/assets/icons/tick.svg'; +import Tick from '@/assets/icons/tick.svg?react'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly className?: string; diff --git a/packages/console/src/components/InlineUpsell/index.tsx b/packages/console/src/components/InlineUpsell/index.tsx index 1f57a042c43..ca2fd0eb152 100644 --- a/packages/console/src/components/InlineUpsell/index.tsx +++ b/packages/console/src/components/InlineUpsell/index.tsx @@ -8,7 +8,7 @@ import useTenantPathname from '@/hooks/use-tenant-pathname'; import ContactUsPhraseLink from '../ContactUsPhraseLink'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly className?: string; diff --git a/packages/console/src/components/ItemPreview/ApplicationPreview.tsx b/packages/console/src/components/ItemPreview/ApplicationPreview.tsx index e284a5894c6..438d322834d 100644 --- a/packages/console/src/components/ItemPreview/ApplicationPreview.tsx +++ b/packages/console/src/components/ItemPreview/ApplicationPreview.tsx @@ -5,7 +5,7 @@ import ApplicationIcon from '@/components/ApplicationIcon'; import { applicationTypeI18nKey } from '@/types/applications'; import ItemPreview from '.'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; const applicationsPathname = '/applications'; const buildDetailsPathname = (id: string) => `${applicationsPathname}/${id}`; diff --git a/packages/console/src/components/ItemPreview/index.tsx b/packages/console/src/components/ItemPreview/index.tsx index e7447b76e06..2728e184848 100644 --- a/packages/console/src/components/ItemPreview/index.tsx +++ b/packages/console/src/components/ItemPreview/index.tsx @@ -5,7 +5,7 @@ import { Link } from 'react-router-dom'; import useTenantPathname from '@/hooks/use-tenant-pathname'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly title: ReactNode; diff --git a/packages/console/src/components/ListPage/index.tsx b/packages/console/src/components/ListPage/index.tsx index c59d75fea1d..26abe9e481b 100644 --- a/packages/console/src/components/ListPage/index.tsx +++ b/packages/console/src/components/ListPage/index.tsx @@ -2,14 +2,14 @@ import classNames from 'classnames'; import { type ReactNode } from 'react'; import { type FieldValues, type FieldPath } from 'react-hook-form'; -import Plus from '@/assets/icons/plus.svg'; +import Plus from '@/assets/icons/plus.svg?react'; import PageMeta, { type Props as PageMetaProps } from '@/components/PageMeta'; import { type Props as ButtonProps } from '@/ds-components/Button'; import Button from '@/ds-components/Button'; import { type Props as CardTitleProps } from '@/ds-components/CardTitle'; import CardTitle from '@/ds-components/CardTitle'; import Table, { type Props as TableProps } from '@/ds-components/Table'; -import * as pageLayout from '@/scss/page-layout.module.scss'; +import pageLayout from '@/scss/page-layout.module.scss'; type CreateButtonProps = { title: ButtonProps['title']; diff --git a/packages/console/src/components/LivePreviewButton/index.tsx b/packages/console/src/components/LivePreviewButton/index.tsx index 6a1ec955d3f..c773cfaac34 100644 --- a/packages/console/src/components/LivePreviewButton/index.tsx +++ b/packages/console/src/components/LivePreviewButton/index.tsx @@ -3,13 +3,13 @@ import classNames from 'classnames'; import { useContext } from 'react'; import { useTranslation } from 'react-i18next'; -import ExternalLinkIcon from '@/assets/icons/external-link.svg'; +import ExternalLinkIcon from '@/assets/icons/external-link.svg?react'; import { AppDataContext } from '@/contexts/AppDataProvider'; import type { Props as ButtonProps, ButtonType } from '@/ds-components/Button'; import Button from '@/ds-components/Button'; import { Tooltip } from '@/ds-components/Tip'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly size?: ButtonProps['size']; diff --git a/packages/console/src/components/ManageOrganizationPermissionModal/index.tsx b/packages/console/src/components/ManageOrganizationPermissionModal/index.tsx index bca34f4503a..153802ff340 100644 --- a/packages/console/src/components/ManageOrganizationPermissionModal/index.tsx +++ b/packages/console/src/components/ManageOrganizationPermissionModal/index.tsx @@ -10,7 +10,7 @@ import FormField from '@/ds-components/FormField'; import ModalLayout from '@/ds-components/ModalLayout'; import TextInput from '@/ds-components/TextInput'; import useApi from '@/hooks/use-api'; -import * as modalStyles from '@/scss/modal.module.scss'; +import modalStyles from '@/scss/modal.module.scss'; import { trySubmitSafe } from '@/utils/form'; type Props = { diff --git a/packages/console/src/components/Markdown/index.tsx b/packages/console/src/components/Markdown/index.tsx index c8cf7c6eb97..7fb575a7474 100644 --- a/packages/console/src/components/Markdown/index.tsx +++ b/packages/console/src/components/Markdown/index.tsx @@ -4,6 +4,8 @@ import classNames from 'classnames'; import { memo, useRef } from 'react'; import ReactMarkdown from 'react-markdown'; import remarkGfm from 'remark-gfm'; + +// TODO: @charles double check if this is still needed /** * Workaround for the markdown crash issue in the parcel dev build. It seems parcel does * something clever in dev mode and messing up the `hastToReact` module. Manually adding @@ -17,7 +19,7 @@ import 'property-information'; import CodeEditor from '@/ds-components/CodeEditor'; import GithubRawImage from './components/GithubRawImage'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly className?: string; diff --git a/packages/console/src/components/MauExceededModal/index.tsx b/packages/console/src/components/MauExceededModal/index.tsx index 3d44dddf692..835a14b4007 100644 --- a/packages/console/src/components/MauExceededModal/index.tsx +++ b/packages/console/src/components/MauExceededModal/index.tsx @@ -4,6 +4,7 @@ import ReactModal from 'react-modal'; import PlanUsage from '@/components/PlanUsage'; import { contactEmailLink } from '@/consts'; +import { isDevFeaturesEnabled } from '@/consts/env'; import { subscriptionPage } from '@/consts/pages'; import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider'; import { TenantsContext } from '@/contexts/TenantsProvider'; @@ -12,16 +13,22 @@ import FormField from '@/ds-components/FormField'; import InlineNotification from '@/ds-components/InlineNotification'; import ModalLayout from '@/ds-components/ModalLayout'; import useTenantPathname from '@/hooks/use-tenant-pathname'; -import * as modalStyles from '@/scss/modal.module.scss'; +import modalStyles from '@/scss/modal.module.scss'; import PlanName from '../PlanName'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; function MauExceededModal() { const { currentTenant } = useContext(TenantsContext); const { usage } = currentTenant ?? {}; - const { currentPlan, currentSubscription } = useContext(SubscriptionDataContext); + const { + currentPlan, + currentSubscription, + currentSku, + currentSubscriptionQuota, + currentSubscriptionUsage, + } = useContext(SubscriptionDataContext); const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); const { navigate } = useTenantPathname(); @@ -40,7 +47,10 @@ function MauExceededModal() { name: planName, } = currentPlan; - const isMauExceeded = mauLimit !== null && usage.activeUsers >= mauLimit; + const isMauExceeded = isDevFeaturesEnabled + ? currentSubscriptionQuota.mauLimit !== null && + currentSubscriptionUsage.mauLimit >= currentSubscriptionQuota.mauLimit + : mauLimit !== null && usage.activeUsers >= mauLimit; if (!isMauExceeded) { return null; @@ -76,7 +86,7 @@ function MauExceededModal() { , + planName: , }} > {t('upsell.mau_exceeded_modal.notification')} diff --git a/packages/console/src/components/MfaFactorTitle/index.tsx b/packages/console/src/components/MfaFactorTitle/index.tsx index a32c32339f5..8f75cafb8b4 100644 --- a/packages/console/src/components/MfaFactorTitle/index.tsx +++ b/packages/console/src/components/MfaFactorTitle/index.tsx @@ -1,16 +1,16 @@ import { MfaFactor } from '@logto/schemas'; import { type ReactNode } from 'react'; -import FactorBackupCode from '@/assets/icons/factor-backup-code.svg'; -import FactorTotp from '@/assets/icons/factor-totp.svg'; -import FactorWebAuthn from '@/assets/icons/factor-webauthn.svg'; -import Tip from '@/assets/icons/tip.svg'; +import FactorBackupCode from '@/assets/icons/factor-backup-code.svg?react'; +import FactorTotp from '@/assets/icons/factor-totp.svg?react'; +import FactorWebAuthn from '@/assets/icons/factor-webauthn.svg?react'; +import Tip from '@/assets/icons/tip.svg?react'; import IconButton from '@/ds-components/IconButton'; import { ToggleTip } from '@/ds-components/Tip'; import MfaFactorName, { type Props as MfaFactorNameProps } from '../MfaFactorName'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; const factorIcon: Record = { [MfaFactor.TOTP]: FactorTotp, diff --git a/packages/console/src/components/MultiOptionInput/index.tsx b/packages/console/src/components/MultiOptionInput/index.tsx index 68d5db09eb7..47cc0f85c79 100644 --- a/packages/console/src/components/MultiOptionInput/index.tsx +++ b/packages/console/src/components/MultiOptionInput/index.tsx @@ -2,12 +2,12 @@ import { isKeyInObject, type Nullable } from '@silverhand/essentials'; import classNames from 'classnames'; import { type ReactNode, useRef, useState, useCallback } from 'react'; -import Close from '@/assets/icons/close.svg'; +import Close from '@/assets/icons/close.svg?react'; import IconButton from '@/ds-components/IconButton'; import Tag from '@/ds-components/Tag'; import { onKeyDownHandler } from '@/utils/a11y'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type CanBePromise = T | Promise; diff --git a/packages/console/src/components/MultiTextInputField/index.tsx b/packages/console/src/components/MultiTextInputField/index.tsx index 69791ecaeff..d797a4e1891 100644 --- a/packages/console/src/components/MultiTextInputField/index.tsx +++ b/packages/console/src/components/MultiTextInputField/index.tsx @@ -5,7 +5,7 @@ import FormField from '@/ds-components/FormField'; import type { Props as MultiTextInputProps } from '@/ds-components/MultiTextInput'; import MultiTextInput from '@/ds-components/MultiTextInput'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = MultiTextInputProps & Pick & { diff --git a/packages/console/src/components/OpenExternalLink/index.tsx b/packages/console/src/components/OpenExternalLink/index.tsx index c6e93e6c345..649fbde7fd3 100644 --- a/packages/console/src/components/OpenExternalLink/index.tsx +++ b/packages/console/src/components/OpenExternalLink/index.tsx @@ -1,6 +1,6 @@ import { useTranslation } from 'react-i18next'; -import ExternalLinkIcon from '@/assets/icons/external-link.svg'; +import ExternalLinkIcon from '@/assets/icons/external-link.svg?react'; import IconButton from '@/ds-components/IconButton'; import { Tooltip } from '@/ds-components/Tip'; diff --git a/packages/console/src/components/OrganizationList/index.tsx b/packages/console/src/components/OrganizationList/index.tsx index a924e830646..aaef30d4f74 100644 --- a/packages/console/src/components/OrganizationList/index.tsx +++ b/packages/console/src/components/OrganizationList/index.tsx @@ -3,8 +3,8 @@ import { type ReactNode, useState } from 'react'; import { useTranslation } from 'react-i18next'; import useSWR from 'swr'; -import OrganizationIcon from '@/assets/icons/organization-preview.svg'; -import Tip from '@/assets/icons/tip.svg'; +import OrganizationIcon from '@/assets/icons/organization-preview.svg?react'; +import Tip from '@/assets/icons/tip.svg?react'; import EmptyDataPlaceholder from '@/components/EmptyDataPlaceholder'; import ItemPreview from '@/components/ItemPreview'; import { RoleOption } from '@/components/OrganizationRolesSelect'; @@ -18,7 +18,7 @@ import { ToggleTip } from '@/ds-components/Tip'; import { type RequestError } from '@/hooks/use-api'; import useTenantPathname from '@/hooks/use-tenant-pathname'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly type: 'user' | 'application'; diff --git a/packages/console/src/components/OrganizationRolesSelect/index.tsx b/packages/console/src/components/OrganizationRolesSelect/index.tsx index 452100f8980..f78a09f0876 100644 --- a/packages/console/src/components/OrganizationRolesSelect/index.tsx +++ b/packages/console/src/components/OrganizationRolesSelect/index.tsx @@ -1,14 +1,14 @@ import { type OrganizationRole, type RoleType } from '@logto/schemas'; import classNames from 'classnames'; -import RoleIcon from '@/assets/icons/organization-role-feature.svg'; +import RoleIcon from '@/assets/icons/organization-role-feature.svg?react'; import MultiSelect, { type Option } from '@/ds-components/Select/MultiSelect'; import useSearchValues from '@/hooks/use-search-values'; import Breakable from '../Breakable'; import ThemedIcon from '../ThemedIcon'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type RoleOptionProps = { readonly title?: string; diff --git a/packages/console/src/components/PaymentOverdueModal/index.tsx b/packages/console/src/components/PaymentOverdueModal/index.tsx index 78029388510..73e31ff27fe 100644 --- a/packages/console/src/components/PaymentOverdueModal/index.tsx +++ b/packages/console/src/components/PaymentOverdueModal/index.tsx @@ -10,11 +10,11 @@ import FormField from '@/ds-components/FormField'; import InlineNotification from '@/ds-components/InlineNotification'; import ModalLayout from '@/ds-components/ModalLayout'; import useSubscribe from '@/hooks/use-subscribe'; -import * as modalStyles from '@/scss/modal.module.scss'; +import modalStyles from '@/scss/modal.module.scss'; import BillInfo from '../BillInfo'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; function PaymentOverdueModal() { const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); diff --git a/packages/console/src/components/PermissionsTable/index.tsx b/packages/console/src/components/PermissionsTable/index.tsx index 3097491f369..0a2b62fab26 100644 --- a/packages/console/src/components/PermissionsTable/index.tsx +++ b/packages/console/src/components/PermissionsTable/index.tsx @@ -5,9 +5,9 @@ import { useState } from 'react'; import { toast } from 'react-hot-toast'; import { useTranslation } from 'react-i18next'; -import Plus from '@/assets/icons/plus.svg'; -import PermissionsEmptyDark from '@/assets/images/permissions-empty-dark.svg'; -import PermissionsEmpty from '@/assets/images/permissions-empty.svg'; +import Plus from '@/assets/icons/plus.svg?react'; +import PermissionsEmptyDark from '@/assets/images/permissions-empty-dark.svg?react'; +import PermissionsEmpty from '@/assets/images/permissions-empty.svg?react'; import { ApiResourceDetailsTabs } from '@/consts/page-tabs'; import Button from '@/ds-components/Button'; import type { Props as PaginationProps } from '@/ds-components/Pagination'; @@ -24,7 +24,7 @@ import ActionsButton from '../ActionsButton'; import EditScopeModal, { type EditScopeData } from '../EditScopeModal'; import EmptyDataPlaceholder from '../EmptyDataPlaceholder'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type SearchProps = { keyword: string; diff --git a/packages/console/src/components/PlanDescription/index.tsx b/packages/console/src/components/PlanDescription/index.tsx index f39a94c1b66..1db3e078542 100644 --- a/packages/console/src/components/PlanDescription/index.tsx +++ b/packages/console/src/components/PlanDescription/index.tsx @@ -1,4 +1,5 @@ import { ReservedPlanId } from '@logto/schemas'; +import { conditional } from '@silverhand/essentials'; import { type TFuncKey } from 'i18next'; import DynamicT from '@/ds-components/DynamicT'; @@ -11,10 +12,17 @@ const registeredPlanDescriptionPhrasesMap: Record< [ReservedPlanId.Pro]: 'pro_plan_description', }; -type Props = { readonly planId: string }; +type Props = { + /** Temporarily mark as optional. */ + readonly skuId?: string; + /** @deprecated */ + readonly planId: string; +}; -function PlanDescription({ planId }: Props) { - const description = registeredPlanDescriptionPhrasesMap[planId]; +function PlanDescription({ skuId, planId }: Props) { + const description = + conditional(skuId && registeredPlanDescriptionPhrasesMap[skuId]) ?? + registeredPlanDescriptionPhrasesMap[planId]; if (!description) { return null; diff --git a/packages/console/src/components/PlanName/index.tsx b/packages/console/src/components/PlanName/index.tsx index 2dc5f8e77b4..45cdc98d5ff 100644 --- a/packages/console/src/components/PlanName/index.tsx +++ b/packages/console/src/components/PlanName/index.tsx @@ -1,7 +1,8 @@ +import { conditional } from '@silverhand/essentials'; import { type TFuncKey } from 'i18next'; import { useTranslation } from 'react-i18next'; -import { ReservedPlanName } from '@/types/subscriptions'; +import { ReservedPlanName, ReservedSkuId } from '@/types/subscriptions'; const registeredPlanNamePhraseMap: Record< string, @@ -14,13 +15,28 @@ const registeredPlanNamePhraseMap: Record< [ReservedPlanName.Enterprise]: 'enterprise', }; +const registeredSkuIdNamePhraseMap: Record< + string, + TFuncKey<'translation', 'admin_console.subscription'> | undefined +> = { + quotaKey: undefined, + [ReservedSkuId.Free]: 'free_plan', + [ReservedSkuId.Pro]: 'pro_plan', + [ReservedSkuId.Enterprise]: 'enterprise', +}; + type Props = { + /** Temporarily use optional for backward compatibility. */ + readonly skuId?: string; + /** @deprecated */ readonly name: string; }; -function PlanName({ name }: Props) { +// TODO: rename the component once new pricing model is ready, should be `SkuName`. +function PlanName({ skuId, name }: Props) { const { t } = useTranslation(undefined, { keyPrefix: 'admin_console.subscription' }); - const planNamePhrase = registeredPlanNamePhraseMap[name]; + const planNamePhrase = + conditional(skuId && registeredSkuIdNamePhraseMap[skuId]) ?? registeredPlanNamePhraseMap[name]; /** * Note: fallback to the plan name if the phrase is not registered. diff --git a/packages/console/src/components/PlanUsage/ProPlanUsageCard/index.module.scss b/packages/console/src/components/PlanUsage/ProPlanUsageCard/index.module.scss new file mode 100644 index 00000000000..3a5a84e5344 --- /dev/null +++ b/packages/console/src/components/PlanUsage/ProPlanUsageCard/index.module.scss @@ -0,0 +1,35 @@ +@use '@/scss/underscore' as _; + +.card { + display: flex; + flex-direction: column; + justify-content: space-between; + align-items: left; + border-radius: 12px; + border: 1px solid var(--color-divider); + background: var(--color-layer-1); + padding: _.unit(5.5) _.unit(6); + gap: _.unit(6); +} + +.title { + font: var(--font-title-3); + color: var(--color-text-secondary); + display: flex; + align-items: center; +} + +.description { + font: var(--font-title-3); + color: var(--color-text); +} + +.usageTip { + font: var(--font-body-2); + color: var(--color-text-secondary); +} + +.tag { + padding-top: 1px; + padding-bottom: 1px; +} diff --git a/packages/console/src/components/PlanUsage/ProPlanUsageCard/index.tsx b/packages/console/src/components/PlanUsage/ProPlanUsageCard/index.tsx new file mode 100644 index 00000000000..70b60b8876e --- /dev/null +++ b/packages/console/src/components/PlanUsage/ProPlanUsageCard/index.tsx @@ -0,0 +1,78 @@ +import { type AdminConsoleKey } from '@logto/phrases'; +import classNames from 'classnames'; +import { Trans, useTranslation } from 'react-i18next'; + +import Tip from '@/assets/icons/tip.svg?react'; +import DynamicT from '@/ds-components/DynamicT'; +import IconButton from '@/ds-components/IconButton'; +import Tag from '@/ds-components/Tag'; +import TextLink from '@/ds-components/TextLink'; +import { ToggleTip } from '@/ds-components/Tip'; + +import { formatNumber } from '../utils'; + +import styles from './index.module.scss'; + +export type Props = { + readonly usage: number | boolean; + readonly quota?: number; + readonly usageKey: AdminConsoleKey; + readonly titleKey: AdminConsoleKey; + readonly tooltipKey: AdminConsoleKey; + readonly className?: string; +}; + +function ProPlanUsageCard({ usage, quota, usageKey, titleKey, tooltipKey, className }: Props) { + const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); + + return ( +
+
+ + + + , + }} + > + {t(tooltipKey)} + + } + > + + + + +
+ {typeof usage === 'number' ? ( +
+ , + }} + > + {t(usageKey, { + usage: + quota && typeof quota === 'number' + ? `${formatNumber(usage)} / ${formatNumber(quota)}` + : formatNumber(usage), + })} + +
+ ) : ( +
+ + + +
+ )} +
+ ); +} + +export default ProPlanUsageCard; diff --git a/packages/console/src/components/PlanUsage/index.module.scss b/packages/console/src/components/PlanUsage/index.module.scss index 49bf92bd922..c01e37937d6 100644 --- a/packages/console/src/components/PlanUsage/index.module.scss +++ b/packages/console/src/components/PlanUsage/index.module.scss @@ -18,10 +18,27 @@ align-items: center; } +.newPricingModelUsage { + margin-top: _.unit(1); + display: flex; + flex-wrap: wrap; + gap: _.unit(2); +} + +.cardItem { + flex: 0 0 calc(33.333% - _.unit(2) * 2); + max-width: calc(33.333% - _.unit(2) * 2); + max-height: 112px; +} + .planCycle { font: var(--font-body-2); } +.planCycleNewPricingModel { + color: var(--color-text-secondary); +} + .usageBar { border-radius: 4px; background-color: var(--color-layer-2); diff --git a/packages/console/src/components/PlanUsage/index.tsx b/packages/console/src/components/PlanUsage/index.tsx index 96fb6345b54..6714d41f0c1 100644 --- a/packages/console/src/components/PlanUsage/index.tsx +++ b/packages/console/src/components/PlanUsage/index.tsx @@ -1,30 +1,80 @@ -import { conditional } from '@silverhand/essentials'; +import { ReservedPlanId } from '@logto/schemas'; +import { cond, conditional } from '@silverhand/essentials'; import classNames from 'classnames'; import dayjs from 'dayjs'; +import { useContext } from 'react'; import { type SubscriptionUsage, type Subscription } from '@/cloud/types/router'; +import { isDevFeaturesEnabled } from '@/consts/env'; +import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider'; import DynamicT from '@/ds-components/DynamicT'; import { type SubscriptionPlan } from '@/types/subscriptions'; import { formatPeriod } from '@/utils/subscription'; -import * as styles from './index.module.scss'; +import ProPlanUsageCard, { type Props as ProPlanUsageCardProps } from './ProPlanUsageCard'; +import styles from './index.module.scss'; +import { usageKeys, usageKeyMap, titleKeyMap, tooltipKeyMap } from './utils'; type Props = { + /** @deprecated */ readonly subscriptionUsage: SubscriptionUsage; + /** @deprecated */ readonly currentSubscription: Subscription; + /** @deprecated */ readonly currentPlan: SubscriptionPlan; }; function PlanUsage({ subscriptionUsage, currentSubscription, currentPlan }: Props) { - const { currentPeriodStart, currentPeriodEnd } = currentSubscription; - const { activeUsers } = subscriptionUsage; const { - quota: { mauLimit }, - } = currentPlan; + currentSubscriptionQuota, + currentSubscriptionUsage, + currentSubscription: currentSubscriptionFromNewPricingModel, + } = useContext(SubscriptionDataContext); + + const { currentPeriodStart, currentPeriodEnd } = isDevFeaturesEnabled + ? currentSubscriptionFromNewPricingModel + : currentSubscription; + + const [activeUsers, mauLimit] = isDevFeaturesEnabled + ? [currentSubscriptionUsage.mauLimit, currentSubscriptionQuota.mauLimit] + : [subscriptionUsage.activeUsers, currentPlan.quota.mauLimit]; const usagePercent = conditional(mauLimit && activeUsers / mauLimit); - return ( + const usages: ProPlanUsageCardProps[] = usageKeys.map((key) => ({ + usage: currentSubscriptionUsage[key], + usageKey: `subscription.usage.${usageKeyMap[key]}`, + titleKey: `subscription.usage.${titleKeyMap[key]}`, + tooltipKey: `subscription.usage.${tooltipKeyMap[key]}`, + ...cond( + key === 'tokenLimit' && + currentSubscriptionQuota.tokenLimit && { quota: currentSubscriptionQuota.tokenLimit } + ), + })); + + return isDevFeaturesEnabled && + currentSubscriptionFromNewPricingModel.planId === ReservedPlanId.Pro ? ( +
+
+ +
+
+ {usages.map((props, index) => ( + // eslint-disable-next-line react/no-array-index-key + + ))} +
+
+ ) : (
{`${activeUsers} / `} diff --git a/packages/console/src/components/PlanUsage/utils.ts b/packages/console/src/components/PlanUsage/utils.ts new file mode 100644 index 00000000000..4234148079c --- /dev/null +++ b/packages/console/src/components/PlanUsage/utils.ts @@ -0,0 +1,77 @@ +import { type TFuncKey } from 'i18next'; + +import { type NewSubscriptionQuota } from '@/cloud/types/router'; + +type UsageKey = Pick< + NewSubscriptionQuota, + | 'mauLimit' + | 'organizationsEnabled' + | 'mfaEnabled' + | 'enterpriseSsoLimit' + | 'resourcesLimit' + | 'machineToMachineLimit' + | 'tenantMembersLimit' + | 'tokenLimit' + | 'hooksLimit' +>; + +export const usageKeys: Array = [ + 'mauLimit', + 'organizationsEnabled', + 'mfaEnabled', + 'enterpriseSsoLimit', + 'resourcesLimit', + 'machineToMachineLimit', + 'tenantMembersLimit', + 'tokenLimit', + 'hooksLimit', +]; + +export const usageKeyMap: Record< + keyof UsageKey, + TFuncKey<'translation', 'admin_console.subscription.usage'> +> = { + mauLimit: 'mau.description', + organizationsEnabled: 'organizations.description', + mfaEnabled: 'mfa.description', + enterpriseSsoLimit: 'enterprise_sso.description', + resourcesLimit: 'api_resources.description', + machineToMachineLimit: 'machine_to_machine.description', + tenantMembersLimit: 'tenant_members.description', + tokenLimit: 'tokens.description', + hooksLimit: 'hooks.description', +}; + +export const titleKeyMap: Record< + keyof UsageKey, + TFuncKey<'translation', 'admin_console.subscription.usage'> +> = { + mauLimit: 'mau.title', + organizationsEnabled: 'organizations.title', + mfaEnabled: 'mfa.title', + enterpriseSsoLimit: 'enterprise_sso.title', + resourcesLimit: 'api_resources.title', + machineToMachineLimit: 'machine_to_machine.title', + tenantMembersLimit: 'tenant_members.title', + tokenLimit: 'tokens.title', + hooksLimit: 'hooks.title', +}; + +export const tooltipKeyMap: Record< + keyof UsageKey, + TFuncKey<'translation', 'admin_console.subscription.usage'> +> = { + mauLimit: 'mau.tooltip', + organizationsEnabled: 'organizations.tooltip', + mfaEnabled: 'mfa.tooltip', + enterpriseSsoLimit: 'enterprise_sso.tooltip', + resourcesLimit: 'api_resources.tooltip', + machineToMachineLimit: 'machine_to_machine.tooltip', + tenantMembersLimit: 'tenant_members.tooltip', + tokenLimit: 'tokens.tooltip', + hooksLimit: 'hooks.tooltip', +}; + +export const formatNumber = (number: number): string => { + return number.toString().replaceAll(/\B(?=(\d{3})+(?!\d))/g, ','); +}; diff --git a/packages/console/src/components/ProgressBar/index.tsx b/packages/console/src/components/ProgressBar/index.tsx index 25af66bc818..5977e5fc00e 100644 --- a/packages/console/src/components/ProgressBar/index.tsx +++ b/packages/console/src/components/ProgressBar/index.tsx @@ -1,6 +1,6 @@ import classNames from 'classnames'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly currentStep: number; diff --git a/packages/console/src/components/QuotaGuardFooter/index.tsx b/packages/console/src/components/QuotaGuardFooter/index.tsx index c5d57f0ded6..ecd9408dc3a 100644 --- a/packages/console/src/components/QuotaGuardFooter/index.tsx +++ b/packages/console/src/components/QuotaGuardFooter/index.tsx @@ -3,7 +3,7 @@ import { type ReactNode } from 'react'; import Button from '@/ds-components/Button'; import useTenantPathname from '@/hooks/use-tenant-pathname'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly children: ReactNode; diff --git a/packages/console/src/components/Region/index.tsx b/packages/console/src/components/Region/index.tsx index 9a50cb97ed2..ff4164aa88f 100644 --- a/packages/console/src/components/Region/index.tsx +++ b/packages/console/src/components/Region/index.tsx @@ -1,7 +1,7 @@ import classNames from 'classnames'; import { useTranslation } from 'react-i18next'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; // TODO: This is a copy from `@logto/cloud-models`, make a SSoT for this later export enum RegionName { diff --git a/packages/console/src/components/RequestDataError/index.tsx b/packages/console/src/components/RequestDataError/index.tsx index daed530f2c5..b0134d6f37f 100644 --- a/packages/console/src/components/RequestDataError/index.tsx +++ b/packages/console/src/components/RequestDataError/index.tsx @@ -2,14 +2,14 @@ import { Theme } from '@logto/schemas'; import classNames from 'classnames'; import { useTranslation } from 'react-i18next'; -import RequestErrorDarkImage from '@/assets/images/request-error-dark.svg'; -import RequestErrorImage from '@/assets/images/request-error.svg'; +import RequestErrorDarkImage from '@/assets/images/request-error-dark.svg?react'; +import RequestErrorImage from '@/assets/images/request-error.svg?react'; import Button from '@/ds-components/Button'; import Card from '@/ds-components/Card'; import type { RequestError } from '@/hooks/use-api'; import useTheme from '@/hooks/use-theme'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly error: RequestError; diff --git a/packages/console/src/components/RoleAssignmentModal/index.tsx b/packages/console/src/components/RoleAssignmentModal/index.tsx index cd2e9fed36b..baa1056ecfe 100644 --- a/packages/console/src/components/RoleAssignmentModal/index.tsx +++ b/packages/console/src/components/RoleAssignmentModal/index.tsx @@ -11,10 +11,10 @@ import DangerousRaw from '@/ds-components/DangerousRaw'; import ModalLayout from '@/ds-components/ModalLayout'; import TextLink from '@/ds-components/TextLink'; import useApi from '@/hooks/use-api'; -import * as modalStyles from '@/scss/modal.module.scss'; +import modalStyles from '@/scss/modal.module.scss'; import { getUserTitle } from '@/utils/user'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = ( | { diff --git a/packages/console/src/components/RoleIcon/index.tsx b/packages/console/src/components/RoleIcon/index.tsx index 10914329113..878a790de51 100644 --- a/packages/console/src/components/RoleIcon/index.tsx +++ b/packages/console/src/components/RoleIcon/index.tsx @@ -1,8 +1,8 @@ import { Theme } from '@logto/schemas'; import { type ReactNode } from 'react'; -import UserRoleIconDark from '@/assets/icons/role-feature-dark.svg'; -import UserRoleIcon from '@/assets/icons/role-feature.svg'; +import UserRoleIconDark from '@/assets/icons/role-feature-dark.svg?react'; +import UserRoleIcon from '@/assets/icons/role-feature.svg?react'; import useTheme from '@/hooks/use-theme'; const themeToRoleIcon = Object.freeze({ diff --git a/packages/console/src/components/RoleScopesTransfer/components/ResourceItem/index.tsx b/packages/console/src/components/RoleScopesTransfer/components/ResourceItem/index.tsx index a0280753fbe..ee93f79ce1b 100644 --- a/packages/console/src/components/RoleScopesTransfer/components/ResourceItem/index.tsx +++ b/packages/console/src/components/RoleScopesTransfer/components/ResourceItem/index.tsx @@ -3,8 +3,8 @@ import classNames from 'classnames'; import { useState } from 'react'; import { useTranslation } from 'react-i18next'; -import CaretExpanded from '@/assets/icons/caret-expanded.svg'; -import CaretFolded from '@/assets/icons/caret-folded.svg'; +import CaretExpanded from '@/assets/icons/caret-expanded.svg?react'; +import CaretFolded from '@/assets/icons/caret-folded.svg?react'; import Checkbox from '@/ds-components/Checkbox'; import IconButton from '@/ds-components/IconButton'; import { onKeyDownHandler } from '@/utils/a11y'; @@ -12,7 +12,7 @@ import { onKeyDownHandler } from '@/utils/a11y'; import type { DetailedResourceResponse } from '../../types'; import SourceScopeItem from '../SourceScopeItem'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly resource: DetailedResourceResponse; diff --git a/packages/console/src/components/RoleScopesTransfer/components/SourceScopeItem/index.tsx b/packages/console/src/components/RoleScopesTransfer/components/SourceScopeItem/index.tsx index 04f84802ae0..869b746a28a 100644 --- a/packages/console/src/components/RoleScopesTransfer/components/SourceScopeItem/index.tsx +++ b/packages/console/src/components/RoleScopesTransfer/components/SourceScopeItem/index.tsx @@ -3,7 +3,7 @@ import type { ScopeResponse } from '@logto/schemas'; import Checkbox from '@/ds-components/Checkbox'; import { onKeyDownHandler } from '@/utils/a11y'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly scope: ScopeResponse; diff --git a/packages/console/src/components/RoleScopesTransfer/components/SourceScopesBox/index.tsx b/packages/console/src/components/RoleScopesTransfer/components/SourceScopesBox/index.tsx index a67684e135c..aea029d47b3 100644 --- a/packages/console/src/components/RoleScopesTransfer/components/SourceScopesBox/index.tsx +++ b/packages/console/src/components/RoleScopesTransfer/components/SourceScopesBox/index.tsx @@ -7,16 +7,16 @@ import { useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import useSWR from 'swr'; -import Search from '@/assets/icons/search.svg'; +import Search from '@/assets/icons/search.svg?react'; import EmptyDataPlaceholder from '@/components/EmptyDataPlaceholder'; import type { DetailedResourceResponse } from '@/components/RoleScopesTransfer/types'; import TextInput from '@/ds-components/TextInput'; import type { RequestError } from '@/hooks/use-api'; -import * as transferLayout from '@/scss/transfer.module.scss'; +import transferLayout from '@/scss/transfer.module.scss'; import ResourceItem from '../ResourceItem'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly roleId?: string; diff --git a/packages/console/src/components/RoleScopesTransfer/components/TargetScopeItem/index.tsx b/packages/console/src/components/RoleScopesTransfer/components/TargetScopeItem/index.tsx index 65793d4f2f5..41fbec835aa 100644 --- a/packages/console/src/components/RoleScopesTransfer/components/TargetScopeItem/index.tsx +++ b/packages/console/src/components/RoleScopesTransfer/components/TargetScopeItem/index.tsx @@ -1,9 +1,9 @@ import type { ScopeResponse } from '@logto/schemas'; -import Close from '@/assets/icons/close.svg'; +import Close from '@/assets/icons/close.svg?react'; import IconButton from '@/ds-components/IconButton'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly scope: ScopeResponse; diff --git a/packages/console/src/components/RoleScopesTransfer/components/TargetScopesBox/index.tsx b/packages/console/src/components/RoleScopesTransfer/components/TargetScopesBox/index.tsx index c7edebe2562..bc0319fdd90 100644 --- a/packages/console/src/components/RoleScopesTransfer/components/TargetScopesBox/index.tsx +++ b/packages/console/src/components/RoleScopesTransfer/components/TargetScopesBox/index.tsx @@ -1,11 +1,11 @@ import type { ScopeResponse } from '@logto/schemas'; import { useTranslation } from 'react-i18next'; -import * as transferLayout from '@/scss/transfer.module.scss'; +import transferLayout from '@/scss/transfer.module.scss'; import TargetScopeItem from '../TargetScopeItem'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly selectedScopes: ScopeResponse[]; diff --git a/packages/console/src/components/RoleScopesTransfer/index.tsx b/packages/console/src/components/RoleScopesTransfer/index.tsx index 5baf6c1205e..ce1610c9e57 100644 --- a/packages/console/src/components/RoleScopesTransfer/index.tsx +++ b/packages/console/src/components/RoleScopesTransfer/index.tsx @@ -1,11 +1,11 @@ import type { ScopeResponse, RoleType } from '@logto/schemas'; import classNames from 'classnames'; -import * as transferLayout from '@/scss/transfer.module.scss'; +import transferLayout from '@/scss/transfer.module.scss'; import SourceScopesBox from './components/SourceScopesBox'; import TargetScopesBox from './components/TargetScopesBox'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; /** * @deprecated Use `@/ds-component/DataTransferBox` instead. diff --git a/packages/console/src/components/RolesTransfer/SourceRolesBox/SourceRoleItem/index.tsx b/packages/console/src/components/RolesTransfer/SourceRolesBox/SourceRoleItem/index.tsx index c943633cc2e..9a128849bdf 100644 --- a/packages/console/src/components/RolesTransfer/SourceRolesBox/SourceRoleItem/index.tsx +++ b/packages/console/src/components/RolesTransfer/SourceRolesBox/SourceRoleItem/index.tsx @@ -5,7 +5,7 @@ import { onKeyDownHandler } from '@/utils/a11y'; import RoleInformation from '../../components/RoleInformation'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly role: RoleResponse; diff --git a/packages/console/src/components/RolesTransfer/SourceRolesBox/index.tsx b/packages/console/src/components/RolesTransfer/SourceRolesBox/index.tsx index 1da49e842e6..ccc485c3764 100644 --- a/packages/console/src/components/RolesTransfer/SourceRolesBox/index.tsx +++ b/packages/console/src/components/RolesTransfer/SourceRolesBox/index.tsx @@ -7,18 +7,18 @@ import { useState } from 'react'; import { useTranslation } from 'react-i18next'; import useSWR from 'swr'; -import Search from '@/assets/icons/search.svg'; +import Search from '@/assets/icons/search.svg?react'; import EmptyDataPlaceholder from '@/components/EmptyDataPlaceholder'; import { defaultPageSize } from '@/consts'; import Pagination from '@/ds-components/Pagination'; import TextInput from '@/ds-components/TextInput'; import type { RequestError } from '@/hooks/use-api'; import useDebounce from '@/hooks/use-debounce'; -import * as transferLayout from '@/scss/transfer.module.scss'; +import transferLayout from '@/scss/transfer.module.scss'; import { buildUrl } from '@/utils/url'; import SourceRoleItem from './SourceRoleItem'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly entityId: string; diff --git a/packages/console/src/components/RolesTransfer/TargetRolesBox/TargetRoleItem/index.tsx b/packages/console/src/components/RolesTransfer/TargetRolesBox/TargetRoleItem/index.tsx index af35c72caa1..ec1690c3e68 100644 --- a/packages/console/src/components/RolesTransfer/TargetRolesBox/TargetRoleItem/index.tsx +++ b/packages/console/src/components/RolesTransfer/TargetRolesBox/TargetRoleItem/index.tsx @@ -1,11 +1,11 @@ import { type RoleResponse } from '@logto/schemas'; -import Close from '@/assets/icons/close.svg'; +import Close from '@/assets/icons/close.svg?react'; import IconButton from '@/ds-components/IconButton'; import RoleInformation from '../../components/RoleInformation'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly role: RoleResponse; diff --git a/packages/console/src/components/RolesTransfer/TargetRolesBox/index.tsx b/packages/console/src/components/RolesTransfer/TargetRolesBox/index.tsx index ea9bcc2e4ea..56c82584ca0 100644 --- a/packages/console/src/components/RolesTransfer/TargetRolesBox/index.tsx +++ b/packages/console/src/components/RolesTransfer/TargetRolesBox/index.tsx @@ -1,10 +1,10 @@ import type { RoleResponse } from '@logto/schemas'; import { useTranslation } from 'react-i18next'; -import * as transferLayout from '@/scss/transfer.module.scss'; +import transferLayout from '@/scss/transfer.module.scss'; import TargetRoleItem from './TargetRoleItem'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly selectedRoles: RoleResponse[]; diff --git a/packages/console/src/components/RolesTransfer/components/RoleInformation/index.tsx b/packages/console/src/components/RolesTransfer/components/RoleInformation/index.tsx index 4602f85758d..93bd4751218 100644 --- a/packages/console/src/components/RolesTransfer/components/RoleInformation/index.tsx +++ b/packages/console/src/components/RolesTransfer/components/RoleInformation/index.tsx @@ -1,11 +1,11 @@ import { RoleType, type ScopeResponse, isManagementApi, type RoleResponse } from '@logto/schemas'; import useSWR from 'swr'; -import ManagementApiAccessFlag from '@/assets/icons/management-api-access.svg'; +import ManagementApiAccessFlag from '@/assets/icons/management-api-access.svg?react'; import DynamicT from '@/ds-components/DynamicT'; import { Tooltip } from '@/ds-components/Tip'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly role: RoleResponse; diff --git a/packages/console/src/components/RolesTransfer/index.tsx b/packages/console/src/components/RolesTransfer/index.tsx index 152aa6fd41c..c642f628959 100644 --- a/packages/console/src/components/RolesTransfer/index.tsx +++ b/packages/console/src/components/RolesTransfer/index.tsx @@ -2,15 +2,15 @@ import { type RoleResponse, RoleType } from '@logto/schemas'; import classNames from 'classnames'; import { Trans, useTranslation } from 'react-i18next'; -import ManagementApiAccessFlag from '@/assets/icons/management-api-access.svg'; +import ManagementApiAccessFlag from '@/assets/icons/management-api-access.svg?react'; import FormField from '@/ds-components/FormField'; import InlineNotification from '@/ds-components/InlineNotification'; import useUserPreferences from '@/hooks/use-user-preferences'; -import * as transferLayout from '@/scss/transfer.module.scss'; +import transferLayout from '@/scss/transfer.module.scss'; import SourceRolesBox from './SourceRolesBox'; import TargetRolesBox from './TargetRolesBox'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly entityId: string; diff --git a/packages/console/src/components/SignInExperiencePreview/components/ToggleUiThemeButton/index.tsx b/packages/console/src/components/SignInExperiencePreview/components/ToggleUiThemeButton/index.tsx index c0e9dbce37f..ffc859394e8 100644 --- a/packages/console/src/components/SignInExperiencePreview/components/ToggleUiThemeButton/index.tsx +++ b/packages/console/src/components/SignInExperiencePreview/components/ToggleUiThemeButton/index.tsx @@ -1,12 +1,12 @@ import { Theme } from '@logto/schemas'; import classNames from 'classnames'; -import Moon from '@/assets/icons/moon.svg'; -import Sun from '@/assets/icons/sun.svg'; +import Moon from '@/assets/icons/moon.svg?react'; +import Sun from '@/assets/icons/sun.svg?react'; import Button from '@/ds-components/Button'; import type { Props as ButtonProps } from '@/ds-components/Button'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly value: Theme; diff --git a/packages/console/src/components/SignInExperiencePreview/index.module.scss b/packages/console/src/components/SignInExperiencePreview/index.module.scss index 7f945749665..c8d4d83a7ea 100644 --- a/packages/console/src/components/SignInExperiencePreview/index.module.scss +++ b/packages/console/src/components/SignInExperiencePreview/index.module.scss @@ -85,4 +85,28 @@ } } } + + &.disabled { + background: url('../../assets/images/blur-preview.svg') 0 0 / 100% auto no-repeat; + } + + .placeholder { + width: 100%; + height: calc(_screenSize.$web-height + _.unit(20)); + padding: _.unit(10); + backdrop-filter: blur(25px); + display: flex; + flex-direction: column; + color: var(--color-static-white); + + .title { + font: var(--font-label-2); + } + + .description { + margin-top: _.unit(1.5); + font: var(--font-body-2); + white-space: pre-wrap; + } + } } diff --git a/packages/console/src/components/SignInExperiencePreview/index.tsx b/packages/console/src/components/SignInExperiencePreview/index.tsx index ca0a61ac8a1..cf20714f359 100644 --- a/packages/console/src/components/SignInExperiencePreview/index.tsx +++ b/packages/console/src/components/SignInExperiencePreview/index.tsx @@ -4,16 +4,24 @@ import type { ConnectorMetadata, SignInExperience, ConnectorResponse } from '@lo import { conditional } from '@silverhand/essentials'; import classNames from 'classnames'; import { format } from 'date-fns'; -import { useContext, useRef, useMemo, useCallback, useEffect, useState } from 'react'; +import { + useContext, + useRef, + useMemo, + useCallback, + useEffect, + useState, + type ReactNode, +} from 'react'; import { useTranslation } from 'react-i18next'; import useSWR from 'swr'; -import PhoneInfo from '@/assets/images/phone-info.svg'; +import PhoneInfo from '@/assets/images/phone-info.svg?react'; import { AppDataContext } from '@/contexts/AppDataProvider'; import type { RequestError } from '@/hooks/use-api'; import useUiLanguages from '@/hooks/use-ui-languages'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; import { PreviewPlatform } from './types'; export { default as ToggleUiThemeButton } from './components/ToggleUiThemeButton'; @@ -28,6 +36,16 @@ type Props = { * the `AppDataContext` will be used. */ readonly endpoint?: URL; + /** + * Whether the preview is disabled. If `true`, the preview will be disabled and a placeholder will + * be shown instead. Defaults to `false`. + */ + // eslint-disable-next-line react/boolean-prop-naming + readonly disabled?: boolean; + /** + * The placeholder to show when the preview is disabled. + */ + readonly disabledPlaceholder?: ReactNode; }; function SignInExperiencePreview({ @@ -36,6 +54,8 @@ function SignInExperiencePreview({ language = 'en', signInExperience, endpoint: endpointInput, + disabled = false, + disabledPlaceholder, }: Props) { const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); @@ -97,6 +117,10 @@ function SignInExperiencePreview({ }, []); useEffect(() => { + if (disabled) { + setIframeLoaded(false); + return; + } const iframe = previewRef.current; iframe?.addEventListener('load', iframeOnLoadEventHandler); @@ -104,7 +128,7 @@ function SignInExperiencePreview({ return () => { iframe?.removeEventListener('load', iframeOnLoadEventHandler); }; - }, [iframeLoaded, iframeOnLoadEventHandler]); + }, [iframeLoaded, disabled, iframeOnLoadEventHandler]); useEffect(() => { if (!iframeLoaded) { @@ -122,7 +146,8 @@ function SignInExperiencePreview({
-
-
- {platform !== PreviewPlatform.DesktopWeb && ( -
-
{format(Date.now(), 'HH:mm')}
- -
- )} -