From 96ea9a673f63249a51fd29a3191a93c548e8054f Mon Sep 17 00:00:00 2001 From: Sara Zanellato <47299026+peschina@users.noreply.github.com> Date: Tue, 16 Jul 2024 14:05:08 +0200 Subject: [PATCH] [19299] Update to v1.18.0 (#82) * feat(connector): add DingTalk web connector changeset (#5940) * fix(console): avoid rendering outdated role options (#5953) * refactor(console): remove redundant notification from m2m guide (#5954) * feat(core,toolkit): add new sso_identities claim (#5955) * feat(core,toolkit): add new sso_identities claim add new sso_identities claim to the userinfo endpoint * chore: update changeset update changeset * chore: update comments update comments * refactor(core): use findUserSsoIdentites query method in user library use findUserSsoIdentites query method in user library * refactor: improve user experience (#5958) * feat(console): show version number for oss (#5950) * refactor: remove service log fkey (#5959) * refactor(console): improve onboarding data and subscription fetching (#5960) * release: version packages (#5868) * chore: launch us region (#5962) * chore: update links (#5963) * fix: use correct `disabled` logic for free plan (#5964) fix: use correct disable logic for free plan * fix(console): only show m2m role notification for m2m roles (#5957) * refactor(console): make long text breakable in roles page (#5956) * refactor(console): fix plausible hostname (#5968) * chore(deps): update dependency @logto/cloud to v0.2.5-a7eedce (#5847) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * feat(console): implement account deletion (#5969) * feat(console): implement account deletion * refactor: remove unused phrases * chore: add i18n phrases * refactor: add comments and error handling * fix(console): avoid skipping m2m role assignment after switching browser tabs (#5973) * fix(core): profile avatar upload should not return 400 error (#5974) * refactor(console,phrases): update rbac-related phrases (#5975) * chore(schemas): add reserved plan ID for admin tenant (#5976) * feat(core): report oidc exceptions to the appInsights (#5978) report oidc exceptions to the appInsights * refactor(console): click console logo should navigate to root page (#5981) * fix(console): language switch should work on profile page (#5980) * refactor(core): try to fix uncaught exception (#5982) * refactor(console): use permanently delete (#5979) * refactor(core): optimize redis error handling (#5965) * chore(deps): update dependency @testing-library/react to v16 (#5984) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore(connector): improve DingTalk web connector readme (#5977) * feat: add dingtalk connector * refactor(connector): optimize codes * refactor(connector): optimize the logic of getting user phone * docs(connector): add English configuration guide for DingTalk * docs(connector): add table of contents * docs(connector): optimize format * chore(connector): update DingTalk web connector readme * chore(connector): apply suggestions from code review Co-authored-by: Darcy Ye Co-authored-by: Charles Zhao --------- Co-authored-by: aidenlu Co-authored-by: Darcy Ye Co-authored-by: Charles Zhao * chore(deps): update dependency i18next-browser-languagedetector to v8 (#5850) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore: remove dev flags and add changeset for m2m guide feature (#5983) * feat(console): add new feature content for m2m integration guide (#5947) * refactor(console): always display role creation hint in role assignment modal (#5988) * refactor(core,schemas): add user detail payload to User.Deleted webhook event (#5986) * refactor(core,schemas): add user detail payload to User.Deleted DataHook event add user detail data payload to the User.Deleted DataHook event * fix(core): fix unit test fix unit test * refactor(console,phrases): update role assignment modal phrases (#5989) * refactor(core): reorg organization routes * refactor(core): reorg organization queries * feat(schemas): init organization email domains table * feat(core): organization email domains apis * chore: add api docs * refactor: fix tests * feat(core): implement organization jit provisioning * refactor(core): trigger organization membership updated hook from jit * refactor: fix tests * feat(console): implement organization jit ui * feat(core,console): enable backchannel logout * chore: add tests and changeset * chore(console,core): launch organization jit * feat(core,console): organization mfa requirement * chore: add tests * feat: automatic social account linking (#5881) * feat: automatic social account linking * chore: add integration tests * chore: add changeset * fix(core): prevent uncaught promise rejection (#6009) * fix(core): prevent uncaught promise rejection prevent uncaught promise rejection crashing the app * refactor(core): remove inline await remove inline await statement * chore(core): update comment update comment * refactor(console): allow view and update `user.profile` in settings * refactor: apply suggestions from code review Co-authored-by: Charles Zhao * refactor(console): update jit styles * feat(core,console): organization jit roles * refactor: add organization jit role api tests * chore: update changeset * style(console): update tab item style in readme docs (#6013) * refactor(console): imporve custom phrase fetch request error handling (#6015) * refactor(console): improve webhook test request error handling (#6017) * refactor: rename method * feat(connector): google one tap * feat(core): google one tap * feat(experience): google one tap * chore: add tests * chore: add tests * refactor(console): update spring boot api protection guide (#6018) update spring boot api protection guide * refactor(console,experience,test): decouple isDevFeatureEnabled with isIntegrationTest (#6012) * refactor(console,experience,test): decouple isDevFeatureEnabled with isIntegrationTest decouple isDevFeatureEnabled with isIntegrationTest ENV variables * chore: update environment variable get method update environment variable get method * refactor(console): improve swr error handling that previously omitted (#6021) * chore: add comments * chore: add changeset (#6004) * feat: add dev feature disabled test (#6014) feat: implement dev feature disabled integration test implement dev feature desiabled integration test * chore: update README.md (#6038) * refactor(console): show sso status in jit domains (#6040) * feat(console): google one tap (#6034) * test(core): implement sso related integration tests (#6041) * test(core): implement sso related integration tests implement sso related integration tests * chore(core): remove unnecessary comments remove unnecessary comments * feat(console): add Ruby app guide * chore(deps): update docker/build-push-action action to v6 (#6042) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore(connector): update outdated links in connector readme (#6039) * refactor(console): support non-svg logos * feat(schemas): add `agree_to_terms_policy` for sie table (#6036) * feat(console,phrases): support agree to terms policy configuration (#6037) * feat(experience): support agree to terms policies (#6044) * chore(console): update guide orders (#6047) * feat(core): jit organization roles (#6049) * refactor(core): update relation queries * refactor: fix google one tap issues (#6054) * chore(schemas): add legacy-pro tag to reserved plan ID (#6061) * feat(core): init organization app apis * refactor(core): reorg organization users api docs * chore: skip tests if needed * style(experience): add margin-bottom for terms checkbox on sign-in page (#6058) * chore(test): reorg the sso connector api cleanup logic (#6053) reorg the sso connector api cleanup logic * docs(console): add the troubleshooting section in expo guide (#6052) * docs(console): add the troubleshoot section in expo guide add the troubleshoot section in expo integration guide * chore: update the words update the words * feat(core): organization jit sso apis * feat(core): init organization app role apis * chore: rename legacy pro to grandfathered pro (#6076) * refactor(console): update subscription plan ID (#6074) * feat(core): organization jit sso * feat(console): update jit config * refactor: improve code, content, and styles * chore(deps): update dependency buffer to v6 (#6060) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore(core): add dev feature tag for openapi.json (#6025) chore(core): add dev feature tag for openapi.json to indicate operation should not show up in swagger.json * chore(deps): update ikalnytskyi/action-setup-postgres action to v6 (#5815) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * fix(experience): apply agree to terms policy for sso (#6080) * chore: update changeset (#6077) * refactor(core): update naming and fix typos * refactor(core): reorg organization routes * fix: fix dev feature disabled integration tests fix dev feature disabled integration tests * feat(core): issue subject tokens (#6045) * chore: update code owners (#6081) * refactor(core): return roles in organization app get api * feat: organization role types * chore: fix tests * feat(core): get application organizations api * feat(core): update application organization role apis * fix: change special character to fix root paramter naming issue feat: add customParameters function for fixing tenantId error on `/api/.well-known/endpoints/{tenantId}` * fix: update mocks and docstrings * feat(console): show organization list for m2m apps * refactor: filter whole supplement document if needed (#6085) * refactor: update status code * feat(console): m2m pages in organizations * refactor: add changeset and improve code * refactor(core): fork client credentials grant * feat: support prompt config for some built-in connectors (#6023) * feat: support prompt config for some built-in connectors * chore: adopt code review suggestions Co-authored-by: Gao Sun --------- Co-authored-by: Gao Sun * refactor: refactor integration test ci job (#6095) * fix: remove dev feature diff check remove dev feature diff check * refactor: refactor alteration integration test ci job refactor alteration integration test ci job * fix: fix the dev feature disbaled integration tests fix the dev feature disabled integration tests * fix: fix alteration-compatibility-test fix alteration-compatibility-test * feat(console,phrases): add issuer endpoint to application form (#6094) * feat(console,phrases): add issuer endpoint to application form add issuer endpoint to the application form * chore: add changeset add changeset * fix(schemas): explicitly set search path (#6101) * refactor(console): update role-related content and components (#6091) * refactor(console): hide backchannel for m2m apps (#6075) * refactor: add brief intro in swagger.json (#6102) * fix: include `tenantId` and its root param in responses (#6092) * fix: include tenantId and its root param in responses * refactor: use shared object --------- Co-authored-by: Gao Sun * refactor(experience,phrases): update resend passcode phrases (#6103) * feat(core): add hasPassword field to custom JWT user context (#6096) * refactor: handle potential errors during ky requests in koa-auth middleware (#6112) * feat(core): issue organization token via client credentials (#6098) * feat(core): issue organization token via client credentials * refactor: fix tests * refactor(console): upgrade mdx packages * refactor(console): remove unused config * chore: fix typo (#6110) * refactor: update nuxt guide (#6114) * refactor: update nuxt guide * refactor: polish content * refactor(console): update ruby guide (#6116) * refactor(console): update ruby guide * refactor(console): support further readings * refactor(console): reorg docs * refactor(console): update next guide (#6119) * refactor(core): update grant comments (#6120) * chore: update README.mdx (#6121) Added a missing backtick that breaks layout * refactor(console): update swift guide (#6123) * refactor(console): polish ui (#6122) * refactor(console): polish ui * refactor: fix code editor title color * refactor(console): use correct array for checking enterprise sso (#6135) * refactor(console): use correct array for checking enterprise sso * refactor(console): hide add connector button when no connector available * refactor(console): fix sso connector check conditions in the organization jit section * refactor(console): update styles * refactor(console): update express guide (#6124) * refactor(console): polish android guide (#6131) * ci: refactor integration tests workflow * ci: add spaces * refactor(test): use secure random method in integration test util (#6139) * refactor(console): update python and php guide (#6136) * refactor(console): update python/php console guide * refactor(console): improve php guide * refactor(console): improve python guide --------- Co-authored-by: Gao Sun * refactor(console): polish guides * chore(console): remove unmaintained remix guide (#6137) * refactor(console): update golang guide (#6134) * refactor(console): update golang guide * refactor: use imported uris for go docs * refactor(console): fix switch styles (#6132) * refactor(console): fix php guide (#6143) * feat: demo app dev panel (#6105) * docs(console): update the sveltekit guide (#6130) * docs(console): update the sveltekit guide update the sveltekit guide * chore(console): reorg the display user section in svltekit reorg the display user section in svltekit * refactor(console): improve content --------- Co-authored-by: Gao Sun * chore: launch jit (#6127) * chore: launch m2m app for organizations (#6129) * chore: launch m2m app for organizations * chore: add changeset * chore: normalize Logto DB region role names for DB alteration CI (#6144) * docs(console): update the expo SDK integration guide (#6126) * docs(console): update the expo SDK integration guide update the expo SDK integrtion guide * chore(console): update rn guide section title update rn guide section title * refactor(console): improve content --------- Co-authored-by: Gao Sun * feat(core,schemas): token exchange grant (#6057) * docs(console): update flutter intergration guide (#6125) improve content --------- Co-authored-by: Gao Sun * fix(experience): add missing `agreeToTermsPolicy` deps (#6148) * docs(console): update the capacitor integration guide (#6128) * docs(console): update the capacitor integration guide update the capacitor integration guide * fix(console): reorg capacitor guide reorg capacitor guide * chore(console): update the section title update the section title * refactor(console): improve content --------- Co-authored-by: Gao Sun * refactor: fix mermaid in production (#6149) Use dynamic CDN import to use Mermaid as Parcel has issues on handling the static import in production. * ci: rerun integration tests on failure (#6141) * docs(console): update the java spring guide (#6133) --------- Co-authored-by: Gao Sun * refactor(console): add retry button on error (#6158) * refactor(console): update vanilla js integration guide (#6156) * refactor(console): update vanilla js integration guide * refactor(console): improve content --------- Co-authored-by: Gao Sun * refactor(console): update react integration guide (#6151) * refactor(console): update react integration guide * refactor(console): improve content --------- Co-authored-by: Gao Sun * refactor(console): update vue integration guide (#6153) * refactor(console): update vue integration guide * refactor(console): improve content --------- Co-authored-by: Gao Sun * refactor(console): update angular integration guide (#6157) * refactor(console): update angular integration guide * refactor(console): improve content --------- Co-authored-by: Gao Sun * refactor(console): load mermaid in dev (#6155) * feat(core): third-party applications are not allowed for token exchange (#6100) * feat(core,schemas): token exchange grant * feat(core): third-party applications are not allowed for token exchange * refactor: update compare DB alteration scripts (#6152) * refactor: update compare DB alteration scripts * chore: add comments * refactor: upgrade logto sdks (#6160) * fix(console): fix broken api resource guides (#6161) * feat(core): organization token for token exchange flow (#6106) * feat(core,schemas): token exchange grant * feat(core): third-party applications are not allowed for token exchange * feat(core,schemas): token exchange grant * feat(core): organization token for token exchange flow * refactor(console): optimize api resource guides (#6162) * fix(console): fix custom element swap in mdx (#6166) * refactor(console): add aggregated npm installation component (#6159) * refactor: update ci and package (#6167) * refactor: update ci and package * chore: fix tests * fix(console): hide error toast for non-existed application in audit logs (#6168) * fix(console): hide error toast for non-existed application in audit logs * chore: add changeset * feat: add `operationId` to HTTP methods on paths (#6108) * feat: add operationId to HTTP methods on paths * refactor(core): strictly handle routes for building operation id * chore: add changeset * refactor: reorg code * refactor: use get as verb for singular items --------- Co-authored-by: Gao Sun * feat(schemas): custom ui assets db update (#6010) * fix(core): issue `organization_id` claim for client credentials (#6170) * feat(core): handle oidc scopes for token exchange (#6147) * feat(core,schemas): token exchange grant * feat(core): third-party applications are not allowed for token exchange * feat(core,schemas): token exchange grant * feat(core): organization token for token exchange flow * feat(core): handle oidc scopes for token exchange * chore(deps): update dependency @rollup/plugin-commonjs to v26 (#5994) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * refactor(experience): rename `SingleSignOnContext` to `UserInteractionContext` (#6163) * chore(deps): update logto-io/actions-run-logto-integration-tests action to v4 (#6176) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore(deps): update logto-io/actions-package-logto-artifact action to v3 (#6175) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore(deps): update silverhand-io/actions-node-pnpm-run-steps action to v5 (#6174) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * refactor(console): improve dotnet guides * fix(console): fix page issues (#6181) * refactor(console): check mermaid by integration test env (#6183) * feat(core): implement new experience API routes (#5992) * feat(core): implement new interaction-session management flow implement a new interaction-session management flow for experience api use * feat(core): implement password sign-in flow implement password sign-in flow * test(core,schemas): add sign-in password tests add sign-in password tests * chore(core): update comments update comments * refactor(core): rename the password input value key rename the password input value key * refactor(core,schemas): refactor the experience API refactor the exerpience API structure * chore(test): add devFeature test add devFeature test * refactor(core): rename the path rename the path * refactor(core,schemas): refactor using the latest API design refactor using the latest API design * chore(test): replace using devFeature test statement replace using devFeature test statement * fix(core): fix lint error fix lint error * refactor(core): refactor experience API implementations refactor experience API implementations * refactor(core): replace with switch replace object map with switch * refactor: apply suggestions from code review * refactor(core): refactor the interaction class refactor the interaction class * refactor(core): update the user identification logic update the user identification logic --------- Co-authored-by: Gao Sun * feat(core): implement verification code verification API (#6001) * feat(core,schemas): implement the verification code flow implement the verification code flow * chore(core): fix rebase issue fix rebase issue * refactor(console): add chrome extension guide (#6178) * feat(core,schemas): implement social verification experience API endpoints (#6150) feat(core,schemas): implement the social verification flow implement the social verificaiton flow * release: version packages (#5987) * chore: update dependencies * chore: fix wrong conflict resolution * fix: organization and role assignment to public servant * chore: remove duplicate tab --------- Co-authored-by: Darcy Ye Co-authored-by: Xiao Yijun Co-authored-by: simeng-li Co-authored-by: Gao Sun Co-authored-by: silverhand-bot <107667382+silverhand-bot@users.noreply.github.com> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Charles Zhao Co-authored-by: aiden Co-authored-by: aidenlu Co-authored-by: wangsijie Co-authored-by: Mostafa Moradian Co-authored-by: ScreenCom User --- .github/CODEOWNERS | 13 +- ...eration-compatibility-integration-test.yml | 33 +- .github/workflows/changesets.yml | 2 +- .github/workflows/commitlint.yml | 2 +- .github/workflows/integration-test.yml | 30 +- .github/workflows/main.yml | 12 +- .github/workflows/master-codecov-report.yml | 2 +- .github/workflows/release.yml | 6 +- .github/workflows/rerun.yml | 20 + .github/workflows/upload-annotations.yml | 2 +- .scripts/compare-database.js | 37 +- .vscode/settings.json | 3 +- README.md | 10 +- package.json | 2 +- packages/cli/CHANGELOG.md | 22 + packages/cli/package.json | 10 +- .../connector-alipay-native/CHANGELOG.md | 9 + .../connector-alipay-native/README.md | 6 +- .../connector-alipay-native/package.json | 6 +- .../connector-alipay-web/CHANGELOG.md | 9 + .../connectors/connector-alipay-web/README.md | 4 +- .../connector-alipay-web/package.json | 6 +- .../connector-aliyun-dm/CHANGELOG.md | 9 + .../connectors/connector-aliyun-dm/README.md | 4 +- .../connector-aliyun-dm/package.json | 6 +- .../connector-aliyun-sms/CHANGELOG.md | 9 + .../connectors/connector-aliyun-sms/README.md | 4 +- .../connector-aliyun-sms/package.json | 6 +- .../connectors/connector-apple/CHANGELOG.md | 9 + packages/connectors/connector-apple/README.md | 2 +- .../connectors/connector-apple/package.json | 6 +- .../connectors/connector-aws-ses/CHANGELOG.md | 9 + .../connectors/connector-aws-ses/README.md | 2 +- .../connectors/connector-aws-ses/package.json | 6 +- .../connectors/connector-azuread/CHANGELOG.md | 13 + .../connectors/connector-azuread/README.md | 56 +- .../connectors/connector-azuread/package.json | 6 +- .../connector-azuread/src/constant.ts | 11 +- .../connectors/connector-azuread/src/index.ts | 3 +- .../connectors/connector-azuread/src/types.ts | 3 + .../connector-dingtalk-web/CHANGELOG.md | 9 + .../connector-dingtalk-web/README.md | 56 +- .../connector-dingtalk-web/package.json | 6 +- .../connectors/connector-discord/CHANGELOG.md | 9 + .../connectors/connector-discord/package.json | 6 +- .../connector-facebook/CHANGELOG.md | 9 + .../connector-facebook/package.json | 6 +- .../connector-feishu-web/CHANGELOG.md | 9 + .../connector-feishu-web/package.json | 6 +- .../connectors/connector-github/CHANGELOG.md | 9 + .../connectors/connector-github/README.md | 2 +- .../connectors/connector-github/package.json | 6 +- .../connectors/connector-google/CHANGELOG.md | 18 + .../connectors/connector-google/README.md | 44 +- .../connectors/connector-google/package.json | 7 +- .../connector-google/src/constant.ts | 30 +- .../connector-google/src/index.test.ts | 44 + .../connectors/connector-google/src/index.ts | 76 +- .../connectors/connector-google/src/types.ts | 16 +- .../connector-huggingface/CHANGELOG.md | 10 + .../connector-huggingface/package.json | 8 +- .../connector-huggingface/src/index.test.ts | 2 +- .../connectors/connector-kakao/CHANGELOG.md | 9 + .../connectors/connector-kakao/package.json | 6 +- .../connector-logto-email/CHANGELOG.md | 9 + .../connector-logto-email/package.json | 8 +- .../connector-logto-sms/CHANGELOG.md | 9 + .../connector-logto-sms/package.json | 6 +- .../connector-logto-social-demo/CHANGELOG.md | 9 + .../connector-logto-social-demo/package.json | 6 +- .../connectors/connector-mailgun/CHANGELOG.md | 9 + .../connectors/connector-mailgun/README.md | 2 +- .../connectors/connector-mailgun/package.json | 6 +- .../CHANGELOG.md | 9 + .../package.json | 6 +- .../connector-mock-email/CHANGELOG.md | 9 + .../connector-mock-email/package.json | 6 +- .../connector-mock-sms/CHANGELOG.md | 9 + .../connector-mock-sms/package.json | 6 +- .../connector-mock-social/CHANGELOG.md | 9 + .../connector-mock-social/package.json | 6 +- .../connector-mock-social/src/index.ts | 31 +- .../connectors/connector-mygovid/package.json | 10 +- .../connectors/connector-naver/CHANGELOG.md | 9 + .../connectors/connector-naver/package.json | 6 +- .../connectors/connector-oauth2/CHANGELOG.md | 9 + .../connectors/connector-oauth2/package.json | 6 +- .../connectors/connector-oidc/CHANGELOG.md | 10 + .../connectors/connector-oidc/package.json | 8 +- .../connectors/connector-saml/CHANGELOG.md | 9 + .../connectors/connector-saml/package.json | 6 +- .../connector-sendgrid-email/CHANGELOG.md | 9 + .../connector-sendgrid-email/README.md | 2 +- .../connector-sendgrid-email/package.json | 6 +- .../connectors/connector-smsaero/CHANGELOG.md | 9 + .../connectors/connector-smsaero/README.md | 15 +- .../connectors/connector-smsaero/package.json | 6 +- .../connectors/connector-smtp/CHANGELOG.md | 9 + packages/connectors/connector-smtp/README.md | 2 +- .../connectors/connector-smtp/package.json | 6 +- .../connector-tencent-sms/CHANGELOG.md | 9 + .../connector-tencent-sms/README.md | 2 +- .../connector-tencent-sms/package.json | 6 +- .../connector-twilio-sms/CHANGELOG.md | 9 + .../connectors/connector-twilio-sms/README.md | 2 +- .../connector-twilio-sms/package.json | 6 +- .../connector-wechat-native/CHANGELOG.md | 9 + .../connector-wechat-native/README.md | 4 +- .../connector-wechat-native/package.json | 6 +- .../connector-wechat-web/CHANGELOG.md | 9 + .../connectors/connector-wechat-web/README.md | 4 +- .../connector-wechat-web/package.json | 6 +- .../connectors/connector-wecom/CHANGELOG.md | 9 + .../connectors/connector-wecom/package.json | 6 +- packages/console/.parcelrc | 2 +- packages/console/.parcelrc.arm64 | 2 +- packages/console/CHANGELOG.md | 118 + packages/console/package.json | 32 +- packages/console/parcel-transformer-mdx2.js | 61 + packages/console/src/App.tsx | 3 + .../src/assets/docs/fragments/_checkpoint.md | 2 +- .../docs/fragments/_experience-overview.mdx | 17 + .../docs/fragments/_redirect-uris-native.mdx | 12 + .../docs/fragments/_redirect-uris-web.mdx | 22 + .../_regarding-redirect-based-sign-in.md | 9 + .../assets/docs/guides/api-express/README.mdx | 40 +- .../assets/docs/guides/api-python/README.mdx | 29 +- .../docs/guides/api-spring-boot/README.mdx | 113 +- .../assets/docs/guides/generate-metadata.js | 24 +- .../docs/guides/{index.ts => index.tsx} | 59 +- .../assets/docs/guides/m2m-general/README.mdx | 18 +- .../assets/assign-m2m-roles-modal.png | Bin 0 -> 318569 bytes .../assets/assign-m2m-roles-page.png | Bin 0 -> 50116 bytes .../assets/docs/guides/m2m-general/index.ts | 5 +- .../docs/guides/native-android/README.mdx | 208 +- .../docs/guides/native-android/index.ts | 5 +- .../docs/guides/native-capacitor/README.mdx | 58 +- .../assets/docs/guides/native-expo/README.mdx | 110 +- .../assets/docs/guides/native-expo/index.ts | 5 +- .../docs/guides/native-flutter/README.mdx | 373 ++- .../docs/guides/native-ios-swift/README.mdx | 133 +- .../docs/guides/native-ios-swift/index.ts | 2 +- .../assets/docs/guides/spa-angular/README.mdx | 81 +- .../assets/docs/guides/spa-angular/index.ts | 5 +- .../guides/spa-chrome-extension/README.mdx | 238 ++ .../guides/spa-chrome-extension/config.json | 3 + .../spa-chrome-extension/extension-popup.webp | Bin 0 -> 14648 bytes .../docs/guides/spa-chrome-extension/index.ts | 16 + .../docs/guides/spa-chrome-extension/logo.svg | 26 + .../assets/docs/guides/spa-react/README.mdx | 194 +- .../src/assets/docs/guides/spa-react/index.ts | 5 +- .../assets/docs/guides/spa-vanilla/README.mdx | 184 +- .../assets/docs/guides/spa-vanilla/index.ts | 5 +- .../src/assets/docs/guides/spa-vue/README.mdx | 191 +- .../src/assets/docs/guides/spa-vue/index.ts | 5 +- .../assets/docs/guides/spa-webflow/README.mdx | 22 +- .../console/src/assets/docs/guides/types.ts | 16 +- .../web-dotnet-core-blazor-server/README.mdx | 121 +- .../web-dotnet-core-blazor-server/index.ts | 5 +- .../web-dotnet-core-blazor-wasm/README.mdx | 80 +- .../web-dotnet-core-blazor-wasm/index.ts | 5 +- .../guides/web-dotnet-core-mvc/README.mdx | 147 +- .../fragments/_add-authentication.mdx | 23 + .../fragments/_configure-redirect-uris.mdx | 28 + .../fragments/_display-user-information.md | 12 + .../fragments/_installation.md | 5 + .../fragments/_sign-in-and-sign-out-flows.mdx | 37 + .../docs/guides/web-dotnet-core-mvc/index.ts | 5 +- .../docs/guides/web-dotnet-core/README.mdx | 148 +- .../assets/docs/guides/web-express/README.mdx | 148 +- .../assets/docs/guides/web-express/index.ts | 5 +- .../src/assets/docs/guides/web-go/README.mdx | 129 +- .../src/assets/docs/guides/web-go/index.ts | 5 +- .../docs/guides/web-gpt-plugin/config.json | 2 +- .../docs/guides/web-gpt-plugin/index.ts | 1 - .../guides/web-java-spring-boot/README.mdx | 92 +- .../guides/web-next-app-router/README.mdx | 209 +- .../docs/guides/web-next-app-router/index.ts | 5 +- .../docs/guides/web-next-auth/README.mdx | 6 +- .../assets/docs/guides/web-next/README.mdx | 195 +- .../src/assets/docs/guides/web-next/index.ts | 7 +- .../assets/docs/guides/web-nuxt/README.mdx | 100 +- .../src/assets/docs/guides/web-nuxt/index.ts | 5 +- .../assets/docs/guides/web-outline/README.mdx | 2 +- .../src/assets/docs/guides/web-php/README.mdx | 140 +- .../src/assets/docs/guides/web-php/index.ts | 5 +- .../assets/docs/guides/web-python/README.mdx | 180 +- .../assets/docs/guides/web-python/index.ts | 5 +- .../assets/docs/guides/web-remix/README.mdx | 173 -- .../assets/docs/guides/web-remix/config.json | 3 - .../src/assets/docs/guides/web-remix/logo.svg | 11 - .../assets/docs/guides/web-ruby/README.mdx | 179 ++ .../assets/docs/guides/web-ruby/config.json | 3 + .../guides/{web-remix => web-ruby}/index.ts | 10 +- .../src/assets/docs/guides/web-ruby/logo.webp | Bin 0 -> 33844 bytes .../docs/guides/web-sveltekit/README.mdx | 98 +- .../assets/docs/guides/web-sveltekit/index.ts | 5 +- .../docs/guides/web-wordpress/README.mdx | 4 +- .../assets/docs/guides/web-wordpress/index.ts | 10 +- .../icons/organization-role-feature.svg | 43 + ...er-role-dark.svg => role-feature-dark.svg} | 0 .../console/src/assets/icons/role-feature.svg | 54 +- .../console/src/assets/icons/user-role.svg | 13 - packages/console/src/assets/index.d.ts | 10 + .../src/components/AppError/index.module.scss | 1 + .../console/src/components/AppError/index.tsx | 16 +- .../ApplicationCreation/CreateForm/index.tsx | 4 + .../components/ApplicationCreation/index.tsx | 9 +- .../src/components/ApplicationName/index.tsx | 2 +- .../ConfigFormFields/index.module.scss | 5 + .../ConfigForm/ConfigFormFields/index.tsx | 17 + .../GoogleOneTapCard/figure-dark.webp | Bin 0 -> 37568 bytes .../GoogleOneTapCard/figure-light.webp | Bin 0 -> 38018 bytes .../GoogleOneTapCard/index.module.scss | 17 + .../ConnectorForm/GoogleOneTapCard/index.tsx | 111 + .../src/components/Conversion/utils.ts | 1 + .../PlanCardItem/index.tsx | 8 +- .../SelectTenantPlanModal/index.tsx | 9 + .../components/CreateTenantModal/index.tsx | 10 +- .../src/components/FeatureTag/index.tsx | 2 +- .../src/components/FormCard/index.module.scss | 1 + .../console/src/components/Guide/hooks.ts | 2 +- .../src/components/Guide/index.module.scss | 9 +- .../console/src/components/Guide/index.tsx | 58 +- .../ItemPreview/ApplicationPreview.tsx | 33 + .../components/ItemPreview/UserPreview.tsx | 19 +- .../components/ItemPreview/index.module.scss | 4 + .../MultiOptionInput/index.module.scss | 100 + .../src/components/MultiOptionInput/index.tsx | 181 ++ .../OrganizationList/index.module.scss | 13 + .../src/components/OrganizationList/index.tsx | 111 + .../index.tsx | 2 +- .../OrganizationRolesSelect/index.tsx | 13 +- .../src/components/PlanDescription/index.tsx | 1 - .../components/RoleAssignmentModal/index.tsx | 77 +- .../console/src/components/RoleIcon/index.tsx | 20 + .../components/RoleInformation/index.tsx | 17 +- .../src/components/RolesTransfer/index.tsx | 6 +- .../src/components/Topbar/index.module.scss | 1 + .../console/src/components/Topbar/index.tsx | 9 +- .../console/src/components/UserName/index.tsx | 2 +- packages/console/src/consts/env.ts | 1 - packages/console/src/consts/external-links.ts | 10 +- packages/console/src/consts/oidc.ts | 1 + packages/console/src/consts/page-tabs.ts | 1 + packages/console/src/consts/plan-quotas.ts | 1 - packages/console/src/consts/subscriptions.ts | 8 +- .../src/containers/ConsoleRoutes/index.tsx | 2 - .../src/ds-components/ActionMenu/index.tsx | 2 +- .../ds-components/Checkbox/Checkbox/index.tsx | 4 +- .../CodeEditor/index.module.scss | 8 + .../src/ds-components/CodeEditor/index.tsx | 8 +- .../ds-components/Switch/index.module.scss | 2 +- .../src/ds-components/Switch/index.tsx | 34 +- .../use-connector-form-config-parser.tsx | 4 +- .../src/hooks/use-console-routes/index.tsx | 4 +- .../hooks/use-console-routes/routes/mfa.tsx | 5 + .../routes/organizations.tsx | 5 + .../src/hooks/use-plausible-pageview.ts | 3 +- .../src/mdx-components/Anchor/index.tsx | 12 + .../console/src/mdx-components/Code/index.tsx | 31 + .../src/mdx-components/MdxProvider/index.tsx | 30 + .../src/mdx-components/Mermaid/index.tsx | 57 + .../NpmLikeInstallation/index.tsx | 30 + .../mdx-components/Steps/FurtherReadings.tsx | 17 +- .../src/mdx-components/Steps/index.tsx | 17 +- .../src/mdx-components/Tabs/index.module.scss | 32 +- .../console/src/mdx-components/Tabs/index.tsx | 11 +- .../mdx-components/UriInputField/index.tsx | 53 +- packages/console/src/onboarding/index.tsx | 2 - .../onboarding/pages/CreateTenant/index.tsx | 10 +- .../components/ApiGuide/index.tsx | 2 +- .../BackchannelLogout.tsx | 48 + .../EndpointsAndCredentials.tsx | 17 +- .../index.tsx | 27 +- .../ApplicationDetailsContent/index.tsx | 33 +- .../ApplicationDetailsContent/utils.ts | 2 + .../components/AppGuide/index.tsx | 6 +- .../components/GuideLibrary/index.tsx | 42 +- .../components/GuideLibraryModal/index.tsx | 32 +- .../console/src/pages/Applications/index.tsx | 77 +- .../ConnectorContent/index.tsx | 34 +- .../CustomizeJwtDetails/utils/config.tsx | 1 + .../SsoConnectorLogo/index.module.scss | 1 - .../Experience/DomainsInput/index.tsx | 1 + .../EnterpriseSsoDetails/SsoGuide/index.tsx | 15 +- .../index.tsx} | 28 +- .../AddAppsToOrganization.tsx | 148 + .../MachineToMachine/index.module.scss | 14 + .../MachineToMachine/index.tsx | 172 ++ .../Members/AddMembersToOrganization.tsx | 19 +- .../OrganizationDetails/Members/index.tsx | 11 +- .../Settings/JitSettings.tsx | 245 ++ .../Settings/index.module.scss | 69 + .../OrganizationDetails/Settings/index.tsx | 83 +- .../OrganizationDetails/Settings/utils.ts | 38 + .../src/pages/OrganizationDetails/index.tsx | 162 +- .../src/pages/OrganizationDetails/types.ts | 12 +- .../pages/OrganizationRoleDetails/index.tsx | 6 +- .../CreateOrganizationRoleModal.tsx | 46 +- .../OrganizationRoles/index.module.scss | 6 + .../OrganizationRoles/index.tsx | 12 +- .../DeletionConfirmationModal/index.tsx | 127 + .../FinalConfirmationModal/index.tsx | 125 + .../components/TenantsIssuesModal/index.tsx | 44 + .../components/TenantsList/index.module.scss | 6 + .../components/TenantsList/index.tsx | 24 + .../DeleteAccountModal/index.module.scss | 9 +- .../containers/DeleteAccountModal/index.tsx | 59 +- packages/console/src/pages/Profile/index.tsx | 123 +- .../RoleDetails/RoleApplications/index.tsx | 11 +- .../src/pages/RoleDetails/RoleUsers/index.tsx | 11 +- .../console/src/pages/RoleDetails/index.tsx | 31 +- .../components/AssignRoleModal/index.tsx | 20 +- .../CreateRoleForm/index.module.scss | 4 - .../Roles/components/CreateRoleForm/index.tsx | 63 +- .../components/CreateRoleModal/index.tsx | 3 +- .../console/src/pages/Roles/index.module.scss | 6 - packages/console/src/pages/Roles/index.tsx | 38 +- .../LanguageEditor/LanguageDetails.tsx | 4 +- .../PageContent/Content/TermsForm.tsx | 33 +- .../SocialSignInForm/index.tsx | 14 +- .../MauLimitExceededNotification/index.tsx | 5 +- .../TenantMembers/InviteEmailsInput/index.tsx | 1 + .../TenantMembers/Members/index.tsx | 2 +- .../index.tsx | 2 +- .../UserDetails/UserOrganizations/index.tsx | 102 +- .../src/pages/UserDetails/UserRoles/index.tsx | 11 +- .../pages/UserDetails/UserSettings/index.tsx | 46 +- .../console/src/pages/UserDetails/types.ts | 1 + .../console/src/pages/UserDetails/utils.ts | 3 +- .../WebhookSettings/TestWebhook/index.tsx | 64 +- packages/console/src/types/connector.ts | 10 +- packages/console/src/types/guide.ts | 10 +- packages/console/src/utils/connector-form.ts | 2 + packages/core/CHANGELOG.md | 163 + packages/core/package.json | 16 +- packages/core/src/__mocks__/connector.ts | 20 +- .../core/src/__mocks__/sign-in-experience.ts | 5 +- packages/core/src/caches/index.ts | 36 +- packages/core/src/constants/index.ts | 4 + packages/core/src/event-listeners/grant.ts | 1 + .../lib-keep/shared/check_resource.d.ts | 8 + packages/core/src/libraries/application.ts | 17 +- .../src/libraries/hook/context-manager.ts | 64 +- .../core/src/libraries/hook/index.test.ts | 8 +- packages/core/src/libraries/jwt-customizer.ts | 1 + packages/core/src/libraries/ogcio-user.ts | 44 +- .../src/libraries/organization-invitation.ts | 22 +- .../sign-in-experience/index.test.ts | 45 + .../src/libraries/sign-in-experience/index.ts | 40 +- .../src/libraries/sign-in-experience/types.ts | 30 - packages/core/src/libraries/social.ts | 4 +- packages/core/src/libraries/user.test.ts | 3 +- packages/core/src/libraries/user.ts | 135 +- packages/core/src/libraries/user.utils.ts | 60 + packages/core/src/main.ts | 16 + .../core/src/middleware/koa-auth/index.ts | 11 + .../koa-management-api-hooks.test.ts | 2 +- .../middleware/koa-management-api-hooks.ts | 7 +- .../src/middleware/koa-oidc-error-handler.ts | 5 + .../src/middleware/koa-pagination.test.ts | 2 +- .../core/src/middleware/koa-pagination.ts | 47 +- .../src/middleware/koa-security-headers.ts | 30 +- packages/core/src/oidc/extra-token-claims.ts | 4 +- .../oidc/grants/client-credentials.test.ts | 157 + .../src/oidc/grants/client-credentials.ts | 199 ++ packages/core/src/oidc/grants/index.ts | 25 +- .../src/oidc/grants/refresh-token.test.ts | 46 +- .../core/src/oidc/grants/refresh-token.ts | 42 +- .../src/oidc/grants/token-exchange.test.ts | 244 ++ .../core/src/oidc/grants/token-exchange.ts | 218 ++ packages/core/src/oidc/init.ts | 13 +- packages/core/src/oidc/resource.ts | 26 +- packages/core/src/oidc/utils.test.ts | 6 +- packages/core/src/oidc/utils.ts | 2 +- packages/core/src/queries/application.ts | 151 +- .../organization/application-relations.ts | 109 + .../application-role-relations.ts | 126 + .../src/queries/organization/email-domains.ts | 136 + .../core/src/queries/organization/index.ts | 55 +- .../src/queries/organization/relations.ts | 272 -- .../queries/organization/sso-connectors.ts | 45 + .../queries/organization/user-relations.ts | 146 + .../organization/user-role-relations.ts | 117 + .../core/src/queries/organization/utils.ts | 26 + .../src/queries/sign-in-experience.test.ts | 3 +- packages/core/src/queries/subject-token.ts | 28 + packages/core/src/queries/user.ts | 2 +- packages/core/src/routes-me/user-assets.ts | 6 +- packages/core/src/routes-me/user.ts | 2 +- .../core/src/routes/admin-user/basics.test.ts | 20 +- packages/core/src/routes/admin-user/basics.ts | 16 +- .../admin-user/mfa-verifications.test.ts | 11 +- .../core/src/routes/admin-user/search.test.ts | 13 +- packages/core/src/routes/admin-user/search.ts | 2 +- .../core/src/routes/admin-user/social.test.ts | 12 +- .../application-organization.openapi.json | 15 + .../applications/application-organization.ts | 40 + .../application-user-consent-organization.ts | 6 +- .../src/routes/applications/application.ts | 42 +- .../classes/experience-interaction.ts | 172 ++ .../verifications/code-verification.ts | 195 ++ .../experience/classes/verifications/index.ts | 63 + .../verifications/password-verification.ts | 102 + .../verifications/social-verification.ts | 186 ++ .../verifications/verification-record.ts | 21 + packages/core/src/routes/experience/const.ts | 7 + packages/core/src/routes/experience/index.ts | 88 + .../middleware/koa-experience-interaction.ts | 32 + packages/core/src/routes/experience/types.ts | 3 + packages/core/src/routes/experience/utils.ts | 20 + .../password-verification.ts | 41 + .../social-verification.ts | 99 + .../verification-routes/verification-code.ts | 96 + packages/core/src/routes/init.ts | 17 +- .../src/routes/interaction/actions/helpers.ts | 2 +- .../actions/submit-interaction.mfa.test.ts | 9 +- .../actions/submit-interaction.test.ts | 25 +- .../interaction/actions/submit-interaction.ts | 53 +- .../src/routes/interaction/consent/index.ts | 4 +- packages/core/src/routes/interaction/index.ts | 3 - .../middleware/koa-interaction-hooks.ts | 29 +- .../src/routes/interaction/single-sign-on.ts | 4 +- .../src/routes/interaction/types/index.ts | 6 + .../routes/interaction/utils/interaction.ts | 15 +- .../interaction/utils/single-sign-on.test.ts | 11 +- .../interaction/utils/single-sign-on.ts | 50 +- .../utils/social-verification.test.ts | 45 +- .../interaction/utils/social-verification.ts | 17 +- .../index.openapi.json} | 0 .../index.ts} | 3 +- .../index.openapi.json} | 0 .../roles.ts => organization-role/index.ts} | 18 +- .../index.openapi.json} | 0 .../scopes.ts => organization-scope/index.ts} | 3 +- .../application/index.openapi.json | 190 ++ .../routes/organization/application/index.ts | 86 + .../application/role-relations.ts | 130 + .../routes/organization/index.openapi.json | 204 -- .../core/src/routes/organization/index.ts | 195 +- .../jit/email-domains.openapi.json | 92 + .../routes/organization/jit/email-domains.ts | 85 + .../core/src/routes/organization/jit/index.ts | 18 + .../organization/jit/roles.openapi.json | 84 + .../jit/sso-connectors.openapi.json | 84 + .../organization/user/index.openapi.json | 217 ++ .../src/routes/organization/user/index.ts | 79 + .../organization/user/role-relations.ts | 146 + packages/core/src/routes/role.ts | 3 +- .../src/routes/security/index.openapi.json | 41 + packages/core/src/routes/security/index.ts | 56 + .../core/src/routes/sso-connector/index.ts | 5 +- packages/core/src/routes/swagger/consts.ts | 59 + .../core/src/routes/swagger/index.test.ts | 12 +- packages/core/src/routes/swagger/index.ts | 83 +- .../core/src/routes/swagger/utils/general.ts | 42 +- .../routes/swagger/utils/operation-id.test.ts | 53 + .../src/routes/swagger/utils/operation-id.ts | 211 ++ .../src/routes/swagger/utils/parameters.ts | 28 +- packages/core/src/routes/well-known.test.ts | 15 +- packages/core/src/routes/well-known.ts | 3 +- packages/core/src/sso/OidcConnector/index.ts | 12 +- .../core/src/sso/OidcConnector/test-utils.ts | 30 + packages/core/src/tenants/Queries.ts | 2 + packages/core/src/tenants/Tenant.ts | 22 +- packages/core/src/utils/RelationQueries.ts | 36 +- packages/core/src/utils/SchemaRouter.ts | 49 +- packages/core/src/utils/test-utils.ts | 4 +- packages/core/src/utils/zod.ts | 4 +- packages/create/CHANGELOG.md | 6 + packages/create/package.json | 4 +- packages/demo-app/CHANGELOG.md | 6 + packages/demo-app/package.json | 13 +- packages/demo-app/src/App.module.scss | 129 +- packages/demo-app/src/App.tsx | 72 +- packages/demo-app/src/DevPanel.tsx | 114 + packages/demo-app/src/utils.ts | 70 + packages/experience/CHANGELOG.md | 23 + packages/experience/package.json | 14 +- packages/experience/src/App.tsx | 6 +- .../IframeModal/index.module.scss | 2 +- .../IframeModalProvider/IframeModal/index.tsx | 3 +- .../UserInteractionContext.tsx} | 12 +- .../index.tsx | 35 +- packages/experience/src/__mocks__/logto.tsx | 7 + packages/experience/src/apis/interaction.ts | 2 +- .../src/components/GoogleOneTap/index.tsx | 43 + packages/experience/src/constants/env.ts | 4 +- .../src/containers/SocialSignInList/index.tsx | 4 +- .../containers/SocialSignInList/use-social.ts | 45 +- .../containers/TermsAndPrivacyLinks/index.tsx | 27 +- .../VerificationCode/index.test.tsx | 22 +- .../src/containers/VerificationCode/index.tsx | 36 +- .../src/hooks/use-check-single-sign-on.ts | 13 +- .../experience/src/hooks/use-connectors.ts | 8 +- packages/experience/src/hooks/use-sie.ts | 2 + .../src/hooks/use-single-sign-on-watch.ts | 40 +- packages/experience/src/hooks/use-terms.ts | 11 +- packages/experience/src/jest.setup.ts | 23 +- .../IdentifierRegisterForm/index.test.tsx | 6 +- .../Register/IdentifierRegisterForm/index.tsx | 13 +- .../src/pages/Register/index.module.scss | 7 + .../experience/src/pages/Register/index.tsx | 56 +- .../IdentifierSignInForm/index.module.scss | 5 + .../IdentifierSignInForm/index.test.tsx | 6 +- .../SignIn/IdentifierSignInForm/index.tsx | 34 +- packages/experience/src/pages/SignIn/Main.tsx | 23 +- .../PasswordSignInForm/index.module.scss | 4 + .../SignIn/PasswordSignInForm/index.test.tsx | 6 +- .../pages/SignIn/PasswordSignInForm/index.tsx | 34 +- .../src/pages/SignIn/index.module.scss | 9 +- .../src/pages/SignIn/index.test.tsx | 10 +- .../experience/src/pages/SignIn/index.tsx | 52 +- .../pages/SingleSignOnConnectors/index.tsx | 10 +- .../src/pages/SingleSignOnEmail/index.tsx | 6 +- .../use-single-sign-on-listener.ts | 18 +- .../use-social-sign-in-listener.ts | 62 +- packages/experience/src/types/index.ts | 23 +- .../experience/src/utils/social-connectors.ts | 25 +- packages/integration-tests/CHANGELOG.md | 86 + packages/integration-tests/package.json | 10 +- .../src/__mocks__/jwt-customizer.ts | 4 +- .../integration-tests/src/api/admin-user.ts | 4 + packages/integration-tests/src/api/api.ts | 2 + .../integration-tests/src/api/application.ts | 21 +- packages/integration-tests/src/api/factory.ts | 76 + packages/integration-tests/src/api/index.ts | 2 +- .../src/api/interaction-sso.ts | 24 + .../src/api/organization-jit.ts | 52 + .../src/api/organization-role.ts | 2 + .../integration-tests/src/api/organization.ts | 74 +- .../src/api/sso-connector.ts | 44 + .../src/api/subject-token.ts | 13 + .../src/client/experience/const.ts | 7 + .../src/client/experience/index.ts | 105 + .../integration-tests/src/client/index.ts | 13 +- packages/integration-tests/src/constants.ts | 6 +- .../integration-tests/src/helpers/client.ts | 13 + .../src/helpers/experience/index.ts | 66 + .../helpers/experience/social-verification.ts | 41 + .../helpers/experience/verification-code.ts | 42 + .../src/helpers/interactions.ts | 63 + .../src/helpers/organization.ts | 16 +- .../src/helpers/sign-in-experience.ts | 28 +- .../src/helpers/single-sign-on.ts | 104 + .../api/admin-user.organization-jit.test.ts | 41 + .../application.organization.test.ts | 92 + .../api/application/application.roles.test.ts | 3 +- .../tests/api/application/application.test.ts | 2 +- .../sign-in-interaction/password.test.ts | 39 + .../verification-code.test.ts | 42 + .../password-verification.test.ts | 38 + .../verifications/social-verification.test.ts | 197 ++ .../verifications/verification-code.test.ts | 208 ++ .../api/hook/hook.trigger.custom.data.test.ts | 99 +- .../tests/api/hook/hook.trigger.data.test.ts | 39 +- .../api/hook/hook.trigger.interaction.test.ts | 2 +- .../src/tests/api/hook/test-cases.ts | 37 +- .../interaction/consent/happy-path.test.ts | 16 +- .../api/interaction/organization-jit.test.ts | 226 ++ .../happy-path.test.ts | 33 +- .../single-sign-on/happy-path.test.ts | 61 +- .../api/oidc/client-credentials-grant.test.ts | 290 ++ .../tests/api/oidc/get-access-token.test.ts | 4 +- .../tests/api/oidc/organization-token.test.ts | 125 + .../src/tests/api/oidc/token-exchange.test.ts | 255 ++ .../organization-application.test.ts | 316 ++ .../api/organization/organization-jit.test.ts | 299 ++ .../organization/organization-user.test.ts | 111 +- .../src/tests/api/security.test.ts | 19 + .../src/tests/api/swagger-check.test.ts | 30 +- .../src/tests/console/applications/helpers.ts | 2 +- .../tests/console/applications/index.test.ts | 13 +- .../tests/console/backchannel-logout.test.ts | 117 + .../src/tests/console/bootstrap.test.ts | 7 + .../src/tests/console/rbac/helper.ts | 9 +- .../rbac/machime-to-machime-rbac.test.ts | 6 +- .../src/tests/console/rbac/user-rbac.test.ts | 4 +- .../sign-up-and-sign-in/helpers.ts | 10 +- .../sign-up-and-sign-in/sad-path.test.ts | 16 +- .../automatic-account-linking.test.ts | 115 + .../src/tests/experience/bootstrap.test.ts | 26 +- .../tests/experience/direct-sign-in.test.ts | 9 +- .../tests/experience/social-sign-in.test.ts | 3 +- .../src/ui-helpers/expect-console.ts | 12 +- .../src/ui-helpers/expect-organizations.ts | 2 +- packages/integration-tests/src/utils.ts | 15 +- packages/phrases-experience/CHANGELOG.md | 10 + packages/phrases-experience/package.json | 4 +- .../src/locales/de/description.ts | 5 +- .../src/locales/en/description.ts | 5 +- .../src/locales/es/description.ts | 6 +- .../src/locales/fr/description.ts | 5 +- .../src/locales/it/description.ts | 5 +- .../src/locales/ja/description.ts | 5 +- .../src/locales/ko/description.ts | 5 +- .../src/locales/pl-pl/description.ts | 6 +- .../src/locales/pt-br/description.ts | 5 +- .../src/locales/pt-pt/description.ts | 5 +- .../src/locales/ru/description.ts | 5 +- .../src/locales/tr-tr/description.ts | 5 +- .../src/locales/zh-cn/description.ts | 5 +- .../src/locales/zh-hk/description.ts | 5 +- .../src/locales/zh-tw/description.ts | 5 +- packages/phrases/CHANGELOG.md | 113 + packages/phrases/package.json | 2 +- .../phrases/src/locales/de/errors/role.ts | 2 +- .../admin-console/application-details.ts | 11 +- .../translation/admin-console/applications.ts | 7 - .../organization-role-details.ts | 1 - .../de/translation/admin-console/profile.ts | 6 - .../translation/admin-console/role-details.ts | 17 +- .../de/translation/admin-console/roles.ts | 8 - .../admin-console/sign-in-exp/content.ts | 7 + .../translation/admin-console/user-details.ts | 11 +- .../src/locales/de/translation/demo-app.ts | 1 - .../phrases/src/locales/en/errors/resource.ts | 4 +- .../phrases/src/locales/en/errors/role.ts | 2 +- .../phrases/src/locales/en/errors/session.ts | 3 + .../admin-console/api-resource-details.ts | 2 +- .../admin-console/application-details.ts | 39 +- .../admin-console/connector-details.ts | 11 + .../admin-console/enterprise-sso-details.ts | 6 +- .../admin-console/organization-details.ts | 44 +- .../organization-role-details.ts | 1 - .../admin-console/organization-template.ts | 5 +- .../admin-console/organizations.ts | 1 + .../en/translation/admin-console/profile.ts | 42 +- .../translation/admin-console/role-details.ts | 21 +- .../en/translation/admin-console/roles.ts | 17 +- .../admin-console/sign-in-exp/content.ts | 6 + .../sign-in-exp/sign-up-and-sign-in.ts | 3 + .../translation/admin-console/user-details.ts | 9 +- .../src/locales/en/translation/demo-app.ts | 2 +- .../admin-console/application-details.ts | 12 +- .../organization-role-details.ts | 1 - .../es/translation/admin-console/profile.ts | 6 - .../translation/admin-console/role-details.ts | 18 +- .../es/translation/admin-console/roles.ts | 8 - .../admin-console/sign-in-exp/content.ts | 8 + .../translation/admin-console/user-details.ts | 11 +- .../src/locales/es/translation/demo-app.ts | 1 - .../admin-console/application-details.ts | 12 +- .../translation/admin-console/applications.ts | 6 - .../organization-role-details.ts | 1 - .../fr/translation/admin-console/profile.ts | 6 - .../translation/admin-console/role-details.ts | 18 +- .../fr/translation/admin-console/roles.ts | 8 - .../admin-console/sign-in-exp/content.ts | 8 + .../translation/admin-console/user-details.ts | 11 +- .../src/locales/fr/translation/demo-app.ts | 1 - .../src/locales/it/errors/application.ts | 2 +- .../admin-console/application-details.ts | 12 +- .../organization-role-details.ts | 1 - .../it/translation/admin-console/profile.ts | 6 - .../translation/admin-console/role-details.ts | 19 +- .../it/translation/admin-console/roles.ts | 8 - .../admin-console/sign-in-exp/content.ts | 7 + .../translation/admin-console/user-details.ts | 10 +- .../src/locales/it/translation/demo-app.ts | 1 - .../admin-console/application-details.ts | 12 +- .../admin-console/enterprise-sso-details.ts | 119 +- .../organization-role-details.ts | 1 - .../ja/translation/admin-console/profile.ts | 6 - .../translation/admin-console/role-details.ts | 19 +- .../ja/translation/admin-console/roles.ts | 8 - .../admin-console/sign-in-exp/content.ts | 6 + .../translation/admin-console/user-details.ts | 10 +- .../src/locales/ja/translation/demo-app.ts | 1 - .../admin-console/application-details.ts | 12 +- .../translation/admin-console/applications.ts | 6 - .../organization-role-details.ts | 1 - .../ko/translation/admin-console/profile.ts | 6 - .../translation/admin-console/role-details.ts | 17 +- .../ko/translation/admin-console/roles.ts | 8 - .../admin-console/sign-in-exp/content.ts | 6 + .../translation/admin-console/user-details.ts | 10 +- .../src/locales/ko/translation/demo-app.ts | 1 - .../admin-console/application-details.ts | 12 +- .../organization-role-details.ts | 1 - .../translation/admin-console/profile.ts | 6 - .../translation/admin-console/role-details.ts | 6 +- .../pl-pl/translation/admin-console/roles.ts | 8 - .../admin-console/sign-in-exp/content.ts | 6 + .../translation/admin-console/user-details.ts | 11 +- .../src/locales/pl-pl/translation/demo-app.ts | 1 - .../admin-console/application-details.ts | 12 +- .../organization-role-details.ts | 1 - .../translation/admin-console/profile.ts | 6 - .../translation/admin-console/role-details.ts | 18 +- .../pt-br/translation/admin-console/roles.ts | 8 - .../admin-console/sign-in-exp/content.ts | 6 + .../translation/admin-console/user-details.ts | 11 +- .../src/locales/pt-br/translation/demo-app.ts | 1 - .../admin-console/application-details.ts | 12 +- .../organization-role-details.ts | 1 - .../translation/admin-console/profile.ts | 6 - .../translation/admin-console/role-details.ts | 18 +- .../pt-pt/translation/admin-console/roles.ts | 8 - .../admin-console/sign-in-exp/content.ts | 6 + .../translation/admin-console/user-details.ts | 11 +- .../src/locales/pt-pt/translation/demo-app.ts | 1 - .../admin-console/application-details.ts | 12 +- .../organization-role-details.ts | 1 - .../ru/translation/admin-console/profile.ts | 6 - .../translation/admin-console/role-details.ts | 18 +- .../ru/translation/admin-console/roles.ts | 8 - .../admin-console/sign-in-exp/content.ts | 6 + .../translation/admin-console/user-details.ts | 11 +- .../src/locales/ru/translation/demo-app.ts | 1 - .../admin-console/application-details.ts | 12 +- .../organization-role-details.ts | 1 - .../translation/admin-console/profile.ts | 6 - .../translation/admin-console/role-details.ts | 18 +- .../tr-tr/translation/admin-console/roles.ts | 8 - .../admin-console/sign-in-exp/content.ts | 6 + .../translation/admin-console/user-details.ts | 11 +- .../src/locales/tr-tr/translation/demo-app.ts | 1 - .../admin-console/application-details.ts | 12 +- .../organization-role-details.ts | 1 - .../translation/admin-console/profile.ts | 6 - .../translation/admin-console/role-details.ts | 18 +- .../zh-cn/translation/admin-console/roles.ts | 7 - .../admin-console/sign-in-exp/content.ts | 6 + .../translation/admin-console/user-details.ts | 10 +- .../src/locales/zh-cn/translation/demo-app.ts | 1 - .../admin-console/application-details.ts | 12 +- .../translation/admin-console/applications.ts | 5 - .../organization-role-details.ts | 1 - .../translation/admin-console/profile.ts | 6 - .../translation/admin-console/role-details.ts | 18 +- .../zh-hk/translation/admin-console/roles.ts | 7 - .../admin-console/sign-in-exp/content.ts | 6 + .../translation/admin-console/user-details.ts | 10 +- .../src/locales/zh-hk/translation/demo-app.ts | 1 - .../admin-console/application-details.ts | 12 +- .../translation/admin-console/applications.ts | 5 - .../organization-role-details.ts | 1 - .../translation/admin-console/profile.ts | 6 - .../translation/admin-console/role-details.ts | 18 +- .../zh-tw/translation/admin-console/roles.ts | 7 - .../admin-console/sign-in-exp/content.ts | 6 + .../translation/admin-console/user-details.ts | 10 +- .../src/locales/zh-tw/translation/demo-app.ts | 1 - packages/schemas/CHANGELOG.md | 121 + ....18.0-1717567857-social-sign-in-linking.ts | 18 + ...75-add-organization-email-domains-table.ts | 31 + ...1717818597-organization-mfa-requirement.ts | 18 + ...g-email-domains-and-add-jit-roles-table.ts | 56 + ....0-1718594164-add-agree-to-terms-policy.ts | 40 + ...5576-organization-application-relations.ts | 37 + ...8786576-organization-jit-sso-connectors.ts | 31 + ...organization-role-application-relations.ts | 34 + .../1.18.0-1718865814-add-subject-tokens.ts | 36 + ...18.0-1719014832-organization-role-types.ts | 35 + .../1.18.0-1719221205-fix-functions.ts | 25 + .../1.18.0-1719312694-custom-ui-assets.ts | 18 + packages/schemas/package.json | 8 +- packages/schemas/src/consts/subscriptions.ts | 10 + .../src/foundations/jsonb-types/hooks.ts | 5 +- .../foundations/jsonb-types/oidc-module.ts | 36 +- .../jsonb-types/sign-in-experience.ts | 14 + .../schemas/src/seeds/sign-in-experience.ts | 1 + packages/schemas/src/types/index.ts | 2 + packages/schemas/src/types/interactions.ts | 118 +- packages/schemas/src/types/log/token.ts | 1 + .../src/types/logto-config/jwt-customizer.ts | 39 +- packages/schemas/src/types/oidc-config.ts | 1 + packages/schemas/src/types/organization.ts | 20 +- packages/schemas/src/types/role.ts | 8 +- .../schemas/src/types/sign-in-experience.ts | 60 + packages/schemas/src/types/subject-token.ts | 8 + .../schemas/src/types/tenant-organization.ts | 3 + packages/schemas/src/utils/zod.ts | 2 +- packages/schemas/tables/applications.sql | 5 + .../organization_application_relations.sql | 14 + .../tables/organization_jit_email_domains.sql | 13 + .../schemas/tables/organization_jit_roles.sql | 14 + .../organization_jit_sso_connectors.sql | 13 + ...rganization_role_application_relations.sql | 18 + .../organization_role_user_relations.sql | 4 +- .../schemas/tables/organization_roles.sql | 9 +- packages/schemas/tables/organizations.sql | 2 + .../schemas/tables/sign_in_experiences.sql | 5 + packages/schemas/tables/subject_tokens.sql | 16 + packages/shared/src/node/env/GlobalValues.ts | 3 +- packages/toolkit/connector-kit/CHANGELOG.md | 18 + packages/toolkit/connector-kit/package.json | 2 +- .../connector-kit/src/types/config-form.ts | 6 + .../connector-kit/src/types/foundation.ts | 6 +- .../connector-kit/src/types/metadata.ts | 31 +- .../toolkit/connector-kit/src/types/social.ts | 74 +- pnpm-lock.yaml | 2655 +++++++++++------ 794 files changed, 20040 insertions(+), 7144 deletions(-) create mode 100644 .github/workflows/rerun.yml create mode 100644 packages/console/parcel-transformer-mdx2.js create mode 100644 packages/console/src/assets/docs/fragments/_experience-overview.mdx create mode 100644 packages/console/src/assets/docs/fragments/_redirect-uris-native.mdx create mode 100644 packages/console/src/assets/docs/fragments/_redirect-uris-web.mdx create mode 100644 packages/console/src/assets/docs/fragments/_regarding-redirect-based-sign-in.md rename packages/console/src/assets/docs/guides/{index.ts => index.tsx} (92%) create mode 100644 packages/console/src/assets/docs/guides/m2m-general/assets/assign-m2m-roles-modal.png create mode 100644 packages/console/src/assets/docs/guides/m2m-general/assets/assign-m2m-roles-page.png create mode 100644 packages/console/src/assets/docs/guides/spa-chrome-extension/README.mdx create mode 100644 packages/console/src/assets/docs/guides/spa-chrome-extension/config.json create mode 100644 packages/console/src/assets/docs/guides/spa-chrome-extension/extension-popup.webp create mode 100644 packages/console/src/assets/docs/guides/spa-chrome-extension/index.ts create mode 100644 packages/console/src/assets/docs/guides/spa-chrome-extension/logo.svg create mode 100644 packages/console/src/assets/docs/guides/web-dotnet-core-mvc/fragments/_add-authentication.mdx create mode 100644 packages/console/src/assets/docs/guides/web-dotnet-core-mvc/fragments/_configure-redirect-uris.mdx create mode 100644 packages/console/src/assets/docs/guides/web-dotnet-core-mvc/fragments/_display-user-information.md create mode 100644 packages/console/src/assets/docs/guides/web-dotnet-core-mvc/fragments/_installation.md create mode 100644 packages/console/src/assets/docs/guides/web-dotnet-core-mvc/fragments/_sign-in-and-sign-out-flows.mdx delete mode 100644 packages/console/src/assets/docs/guides/web-remix/README.mdx delete mode 100644 packages/console/src/assets/docs/guides/web-remix/config.json delete mode 100644 packages/console/src/assets/docs/guides/web-remix/logo.svg create mode 100644 packages/console/src/assets/docs/guides/web-ruby/README.mdx create mode 100644 packages/console/src/assets/docs/guides/web-ruby/config.json rename packages/console/src/assets/docs/guides/{web-remix => web-ruby}/index.ts (52%) create mode 100644 packages/console/src/assets/docs/guides/web-ruby/logo.webp create mode 100644 packages/console/src/assets/icons/organization-role-feature.svg rename packages/console/src/assets/icons/{user-role-dark.svg => role-feature-dark.svg} (100%) delete mode 100644 packages/console/src/assets/icons/user-role.svg create mode 100644 packages/console/src/components/ConnectorForm/GoogleOneTapCard/figure-dark.webp create mode 100644 packages/console/src/components/ConnectorForm/GoogleOneTapCard/figure-light.webp create mode 100644 packages/console/src/components/ConnectorForm/GoogleOneTapCard/index.module.scss create mode 100644 packages/console/src/components/ConnectorForm/GoogleOneTapCard/index.tsx create mode 100644 packages/console/src/components/ItemPreview/ApplicationPreview.tsx create mode 100644 packages/console/src/components/MultiOptionInput/index.module.scss create mode 100644 packages/console/src/components/MultiOptionInput/index.tsx create mode 100644 packages/console/src/components/OrganizationList/index.module.scss create mode 100644 packages/console/src/components/OrganizationList/index.tsx create mode 100644 packages/console/src/components/RoleIcon/index.tsx create mode 100644 packages/console/src/hooks/use-console-routes/routes/mfa.tsx create mode 100644 packages/console/src/mdx-components/Anchor/index.tsx create mode 100644 packages/console/src/mdx-components/Code/index.tsx create mode 100644 packages/console/src/mdx-components/MdxProvider/index.tsx create mode 100644 packages/console/src/mdx-components/Mermaid/index.tsx create mode 100644 packages/console/src/mdx-components/NpmLikeInstallation/index.tsx create mode 100644 packages/console/src/pages/ApplicationDetails/ApplicationDetailsContent/BackchannelLogout.tsx rename packages/console/src/pages/OrganizationDetails/{Members/EditOrganizationRolesModal.tsx => EditOrganizationRolesModal/index.tsx} (70%) create mode 100644 packages/console/src/pages/OrganizationDetails/MachineToMachine/AddAppsToOrganization.tsx create mode 100644 packages/console/src/pages/OrganizationDetails/MachineToMachine/index.module.scss create mode 100644 packages/console/src/pages/OrganizationDetails/MachineToMachine/index.tsx create mode 100644 packages/console/src/pages/OrganizationDetails/Settings/JitSettings.tsx create mode 100644 packages/console/src/pages/OrganizationDetails/Settings/index.module.scss create mode 100644 packages/console/src/pages/OrganizationDetails/Settings/utils.ts create mode 100644 packages/console/src/pages/Profile/containers/DeleteAccountModal/components/DeletionConfirmationModal/index.tsx create mode 100644 packages/console/src/pages/Profile/containers/DeleteAccountModal/components/FinalConfirmationModal/index.tsx create mode 100644 packages/console/src/pages/Profile/containers/DeleteAccountModal/components/TenantsIssuesModal/index.tsx create mode 100644 packages/console/src/pages/Profile/containers/DeleteAccountModal/components/TenantsList/index.module.scss create mode 100644 packages/console/src/pages/Profile/containers/DeleteAccountModal/components/TenantsList/index.tsx delete mode 100644 packages/console/src/pages/Roles/index.module.scss create mode 100644 packages/core/src/include.d/oidc-provider/lib-keep/shared/check_resource.d.ts delete mode 100644 packages/core/src/libraries/sign-in-experience/types.ts create mode 100644 packages/core/src/libraries/user.utils.ts create mode 100644 packages/core/src/oidc/grants/client-credentials.test.ts create mode 100644 packages/core/src/oidc/grants/client-credentials.ts create mode 100644 packages/core/src/oidc/grants/token-exchange.test.ts create mode 100644 packages/core/src/oidc/grants/token-exchange.ts create mode 100644 packages/core/src/queries/organization/application-relations.ts create mode 100644 packages/core/src/queries/organization/application-role-relations.ts create mode 100644 packages/core/src/queries/organization/email-domains.ts delete mode 100644 packages/core/src/queries/organization/relations.ts create mode 100644 packages/core/src/queries/organization/sso-connectors.ts create mode 100644 packages/core/src/queries/organization/user-relations.ts create mode 100644 packages/core/src/queries/organization/user-role-relations.ts create mode 100644 packages/core/src/queries/organization/utils.ts create mode 100644 packages/core/src/queries/subject-token.ts create mode 100644 packages/core/src/routes/applications/application-organization.openapi.json create mode 100644 packages/core/src/routes/applications/application-organization.ts create mode 100644 packages/core/src/routes/experience/classes/experience-interaction.ts create mode 100644 packages/core/src/routes/experience/classes/verifications/code-verification.ts create mode 100644 packages/core/src/routes/experience/classes/verifications/index.ts create mode 100644 packages/core/src/routes/experience/classes/verifications/password-verification.ts create mode 100644 packages/core/src/routes/experience/classes/verifications/social-verification.ts create mode 100644 packages/core/src/routes/experience/classes/verifications/verification-record.ts create mode 100644 packages/core/src/routes/experience/const.ts create mode 100644 packages/core/src/routes/experience/index.ts create mode 100644 packages/core/src/routes/experience/middleware/koa-experience-interaction.ts create mode 100644 packages/core/src/routes/experience/types.ts create mode 100644 packages/core/src/routes/experience/utils.ts create mode 100644 packages/core/src/routes/experience/verification-routes/password-verification.ts create mode 100644 packages/core/src/routes/experience/verification-routes/social-verification.ts create mode 100644 packages/core/src/routes/experience/verification-routes/verification-code.ts rename packages/core/src/routes/{organization/invitations.openapi.json => organization-invitation/index.openapi.json} (100%) rename packages/core/src/routes/{organization/invitations.ts => organization-invitation/index.ts} (98%) rename packages/core/src/routes/{organization/roles.openapi.json => organization-role/index.openapi.json} (100%) rename packages/core/src/routes/{organization/roles.ts => organization-role/index.ts} (90%) rename packages/core/src/routes/{organization/scopes.openapi.json => organization-scope/index.openapi.json} (100%) rename packages/core/src/routes/{organization/scopes.ts => organization-scope/index.ts} (92%) create mode 100644 packages/core/src/routes/organization/application/index.openapi.json create mode 100644 packages/core/src/routes/organization/application/index.ts create mode 100644 packages/core/src/routes/organization/application/role-relations.ts create mode 100644 packages/core/src/routes/organization/jit/email-domains.openapi.json create mode 100644 packages/core/src/routes/organization/jit/email-domains.ts create mode 100644 packages/core/src/routes/organization/jit/index.ts create mode 100644 packages/core/src/routes/organization/jit/roles.openapi.json create mode 100644 packages/core/src/routes/organization/jit/sso-connectors.openapi.json create mode 100644 packages/core/src/routes/organization/user/index.openapi.json create mode 100644 packages/core/src/routes/organization/user/index.ts create mode 100644 packages/core/src/routes/organization/user/role-relations.ts create mode 100644 packages/core/src/routes/security/index.openapi.json create mode 100644 packages/core/src/routes/security/index.ts create mode 100644 packages/core/src/routes/swagger/consts.ts create mode 100644 packages/core/src/routes/swagger/utils/operation-id.test.ts create mode 100644 packages/core/src/routes/swagger/utils/operation-id.ts create mode 100644 packages/core/src/sso/OidcConnector/test-utils.ts create mode 100644 packages/demo-app/src/DevPanel.tsx create mode 100644 packages/demo-app/src/utils.ts rename packages/experience/src/Providers/{SingleSignOnContextProvider/SingleSignOnContext.tsx => UserInteractionContextProvider/UserInteractionContext.tsx} (68%) rename packages/experience/src/Providers/{SingleSignOnContextProvider => UserInteractionContextProvider}/index.tsx (55%) create mode 100644 packages/experience/src/components/GoogleOneTap/index.tsx create mode 100644 packages/integration-tests/src/api/organization-jit.ts create mode 100644 packages/integration-tests/src/api/subject-token.ts create mode 100644 packages/integration-tests/src/client/experience/const.ts create mode 100644 packages/integration-tests/src/client/experience/index.ts create mode 100644 packages/integration-tests/src/helpers/experience/index.ts create mode 100644 packages/integration-tests/src/helpers/experience/social-verification.ts create mode 100644 packages/integration-tests/src/helpers/experience/verification-code.ts create mode 100644 packages/integration-tests/src/helpers/single-sign-on.ts create mode 100644 packages/integration-tests/src/tests/api/admin-user.organization-jit.test.ts create mode 100644 packages/integration-tests/src/tests/api/application/application.organization.test.ts create mode 100644 packages/integration-tests/src/tests/api/experience-api/sign-in-interaction/password.test.ts create mode 100644 packages/integration-tests/src/tests/api/experience-api/sign-in-interaction/verification-code.test.ts create mode 100644 packages/integration-tests/src/tests/api/experience-api/verifications/password-verification.test.ts create mode 100644 packages/integration-tests/src/tests/api/experience-api/verifications/social-verification.test.ts create mode 100644 packages/integration-tests/src/tests/api/experience-api/verifications/verification-code.test.ts create mode 100644 packages/integration-tests/src/tests/api/interaction/organization-jit.test.ts create mode 100644 packages/integration-tests/src/tests/api/oidc/client-credentials-grant.test.ts create mode 100644 packages/integration-tests/src/tests/api/oidc/organization-token.test.ts create mode 100644 packages/integration-tests/src/tests/api/oidc/token-exchange.test.ts create mode 100644 packages/integration-tests/src/tests/api/organization/organization-application.test.ts create mode 100644 packages/integration-tests/src/tests/api/organization/organization-jit.test.ts create mode 100644 packages/integration-tests/src/tests/api/security.test.ts create mode 100644 packages/integration-tests/src/tests/console/backchannel-logout.test.ts create mode 100644 packages/integration-tests/src/tests/experience/automatic-account-linking.test.ts create mode 100644 packages/schemas/alterations/1.18.0-1717567857-social-sign-in-linking.ts create mode 100644 packages/schemas/alterations/1.18.0-1717597875-add-organization-email-domains-table.ts create mode 100644 packages/schemas/alterations/1.18.0-1717818597-organization-mfa-requirement.ts create mode 100644 packages/schemas/alterations/1.18.0-1718340884-rename-org-email-domains-and-add-jit-roles-table.ts create mode 100644 packages/schemas/alterations/1.18.0-1718594164-add-agree-to-terms-policy.ts create mode 100644 packages/schemas/alterations/1.18.0-1718785576-organization-application-relations.ts create mode 100644 packages/schemas/alterations/1.18.0-1718786576-organization-jit-sso-connectors.ts create mode 100644 packages/schemas/alterations/1.18.0-1718807616-organization-role-application-relations.ts create mode 100644 packages/schemas/alterations/1.18.0-1718865814-add-subject-tokens.ts create mode 100644 packages/schemas/alterations/1.18.0-1719014832-organization-role-types.ts create mode 100644 packages/schemas/alterations/1.18.0-1719221205-fix-functions.ts create mode 100644 packages/schemas/alterations/1.18.0-1719312694-custom-ui-assets.ts create mode 100644 packages/schemas/src/types/sign-in-experience.ts create mode 100644 packages/schemas/src/types/subject-token.ts create mode 100644 packages/schemas/tables/organization_application_relations.sql create mode 100644 packages/schemas/tables/organization_jit_email_domains.sql create mode 100644 packages/schemas/tables/organization_jit_roles.sql create mode 100644 packages/schemas/tables/organization_jit_sso_connectors.sql create mode 100644 packages/schemas/tables/organization_role_application_relations.sql create mode 100644 packages/schemas/tables/subject_tokens.sql diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 33e6bb149b2..310af5c1bac 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,5 +1,10 @@ -/packages/schemas/tables @simeng-li @wangsijie @gao-sun -/packages/core @simeng-li @wangsijie @gao-sun -/packages/console @wangsijie @charIeszhao @gao-sun -/packages/ui @simeng-li @charIeszhao +/.github/ @gao-sun /.changeset @gao-sun +/packages/schemas/tables/ @simeng-li @wangsijie @gao-sun +/packages/core/ @simeng-li @wangsijie @gao-sun +/packages/console/ @wangsijie @charIeszhao @xiaoyijun @gao-sun +/packages/ui/ @simeng-li @charIeszhao +connector*/ @darcyYe @gao-sun + +# The file below should be generated by the script, just in case someone accidentally edits it +/packages/console/src/assets/docs/guides/index.ts @gao-sun diff --git a/.github/workflows/alteration-compatibility-integration-test.yml b/.github/workflows/alteration-compatibility-integration-test.yml index 2b06b877c93..4e82e5485c7 100644 --- a/.github/workflows/alteration-compatibility-integration-test.yml +++ b/.github/workflows/alteration-compatibility-integration-test.yml @@ -28,7 +28,7 @@ jobs: id: changes-detection run: | if [[ "${{ github.event_name }}" == "pull_request" ]]; then - BASE=$(git merge-base origin/${{github.base_ref}} HEAD) + BASE=$(git merge-base origin/${{ github.base_ref }} HEAD) else BASE=${{ github.event.before }} fi @@ -37,24 +37,25 @@ jobs: if [ -n "$CHANGE_FILES" ]; then echo "$CHANGE_FILES" - echo "::set-output name=has-alteration-changes::true" + echo "has-alteration-changes=true" >> $GITHUB_OUTPUT echo "Alteration changes detected" else - echo "::set-output name=has-alteration-changes::false" + echo "has-alteration-changes=false" >> $GITHUB_OUTPUT echo "No alteration changes detected" fi package: needs: check-alteration-changes runs-on: ubuntu-latest - if: ${{needs.check-alteration-changes.outputs.has-alteration-changes == 'true'}} + if: ${{ needs.check-alteration-changes.outputs.has-alteration-changes == 'true' }} env: INTEGRATION_TEST: true + DEV_FEATURES_ENABLED: false steps: - - uses: logto-io/actions-package-logto-artifact@v2 + - uses: logto-io/actions-package-logto-artifact@v3 with: artifact-name: alteration-integration-test-${{ github.sha }} - branch: ${{github.base_ref}} + branch: ${{ github.base_ref }} pnpm-version: 9 run-logto: @@ -66,13 +67,27 @@ jobs: runs-on: ubuntu-latest env: INTEGRATION_TEST: true + DEV_FEATURES_ENABLED: false DB_URL: postgres://postgres:postgres@localhost:5432/postgres steps: - - uses: logto-io/actions-run-logto-integration-tests@v3 + - uses: logto-io/actions-run-logto-integration-tests@v4 with: - branch: ${{github.base_ref}} + branch: ${{ github.base_ref }} logto-artifact: alteration-integration-test-${{ github.sha }} test-target: ${{ matrix.target }} - db-alteration-target: ${{github.head_ref}} + db-alteration-target: ${{ github.head_ref }} pnpm-version: 9 + + # Automatically rerun the workflow since the integration tests are moody + # From this genius: https://github.com/orgs/community/discussions/67654#discussioncomment-8038649 + rerun-on-failure: + needs: run-logto + if: failure() && fromJSON(github.run_attempt) < 3 + runs-on: ubuntu-latest + steps: + - env: + GH_REPO: ${{ github.repository }} + GH_TOKEN: ${{ github.token }} + GH_DEBUG: api + run: gh workflow run rerun.yml -F run_id=${{ github.run_id }} diff --git a/.github/workflows/changesets.yml b/.github/workflows/changesets.yml index 0815cc3d372..8c70dc7d811 100644 --- a/.github/workflows/changesets.yml +++ b/.github/workflows/changesets.yml @@ -18,7 +18,7 @@ jobs: token: ${{ secrets.BOT_PAT }} - name: Setup Node and pnpm - uses: silverhand-io/actions-node-pnpm-run-steps@v4 + uses: silverhand-io/actions-node-pnpm-run-steps@v5 with: pnpm-version: 9 diff --git a/.github/workflows/commitlint.yml b/.github/workflows/commitlint.yml index 4e031d6450f..2f568a9e433 100644 --- a/.github/workflows/commitlint.yml +++ b/.github/workflows/commitlint.yml @@ -21,7 +21,7 @@ jobs: fetch-depth: 0 - name: Setup Node and pnpm - uses: silverhand-io/actions-node-pnpm-run-steps@v4 + uses: silverhand-io/actions-node-pnpm-run-steps@v5 with: pnpm-version: 9 diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml index 0fd1557dbd8..076267b8795 100644 --- a/.github/workflows/integration-test.yml +++ b/.github/workflows/integration-test.yml @@ -4,7 +4,6 @@ on: push: branches: - master - - "push-action/**" pull_request: concurrency: @@ -13,14 +12,19 @@ concurrency: jobs: package: + strategy: + matrix: + # Run the integration tests with and without dev features enabled + dev-features-enabled: [true, false] runs-on: ubuntu-latest env: INTEGRATION_TEST: true + DEV_FEATURES_ENABLED: ${{ matrix.dev-features-enabled }} steps: - - uses: logto-io/actions-package-logto-artifact@v2 + - uses: logto-io/actions-package-logto-artifact@v3 with: - artifact-name: integration-test-${{ github.sha }} + artifact-name: integration-test-${{ github.sha }}-dev-features-${{ matrix.dev-features-enabled }} pnpm-version: 9 run-logto: @@ -28,15 +32,31 @@ jobs: fail-fast: false matrix: target: [api, experience, console] + # Run the integration tests with and without dev features enabled + dev-features-enabled: [true, false] needs: package runs-on: ubuntu-latest env: INTEGRATION_TEST: true + DEV_FEATURES_ENABLED: ${{ matrix.dev-features-enabled }} DB_URL: postgres://postgres:postgres@localhost:5432/postgres steps: - - uses: logto-io/actions-run-logto-integration-tests@v3 + - uses: logto-io/actions-run-logto-integration-tests@v4 with: - logto-artifact: integration-test-${{ github.sha }} + logto-artifact: integration-test-${{ github.sha }}-dev-features-${{ env.DEV_FEATURES_ENABLED }} test-target: ${{ matrix.target }} pnpm-version: 9 + + # Automatically rerun the workflow since the integration tests are moody + # From this genius: https://github.com/orgs/community/discussions/67654#discussioncomment-8038649 + rerun-on-failure: + needs: run-logto + if: failure() && fromJSON(github.run_attempt) < 3 + runs-on: ubuntu-latest + steps: + - env: + GH_REPO: ${{ github.repository }} + GH_TOKEN: ${{ github.token }} + GH_DEBUG: api + run: gh workflow run rerun.yml -r ${{ github.head_ref || github.ref_name }} -F run_id=${{ github.run_id }} diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 82d8ea6905e..b525dd8d6f1 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -19,7 +19,7 @@ jobs: - uses: actions/checkout@v4 - name: Setup Node and pnpm - uses: silverhand-io/actions-node-pnpm-run-steps@v4 + uses: silverhand-io/actions-node-pnpm-run-steps@v5 with: pnpm-version: 9 @@ -33,7 +33,7 @@ jobs: - uses: actions/checkout@v4 - name: Setup Node and pnpm - uses: silverhand-io/actions-node-pnpm-run-steps@v4 + uses: silverhand-io/actions-node-pnpm-run-steps@v5 with: pnpm-version: 9 @@ -53,7 +53,7 @@ jobs: - uses: actions/checkout@v4 - name: Setup Node and pnpm - uses: silverhand-io/actions-node-pnpm-run-steps@v4 + uses: silverhand-io/actions-node-pnpm-run-steps@v5 with: pnpm-version: 9 @@ -89,7 +89,7 @@ jobs: uses: docker/setup-buildx-action@v3 - name: Build - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . build-args: | # Test cloud build @@ -120,7 +120,7 @@ jobs: run: cp ./fresh/pnpm-lock.yaml ./ - name: Setup Node and pnpm - uses: silverhand-io/actions-node-pnpm-run-steps@v4 + uses: silverhand-io/actions-node-pnpm-run-steps@v5 with: pnpm-version: 9 run-install: false @@ -136,7 +136,7 @@ jobs: # ** End ** - name: Setup Postgres - uses: ikalnytskyi/action-setup-postgres@v5 + uses: ikalnytskyi/action-setup-postgres@v6 # ** Setup up-to-date databases and compare (test `up`) ** - name: Setup fresh database diff --git a/.github/workflows/master-codecov-report.yml b/.github/workflows/master-codecov-report.yml index 9f1efb01c04..1fe2fa12f9b 100644 --- a/.github/workflows/master-codecov-report.yml +++ b/.github/workflows/master-codecov-report.yml @@ -14,7 +14,7 @@ jobs: - uses: actions/checkout@v4 - name: Setup Node and pnpm - uses: silverhand-io/actions-node-pnpm-run-steps@v4 + uses: silverhand-io/actions-node-pnpm-run-steps@v5 with: pnpm-version: 9 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c942800e52f..bf1de731672 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -105,7 +105,7 @@ jobs: uses: docker/setup-buildx-action@v3 - name: Build and push docker image - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: platforms: linux/amd64 context: . @@ -129,7 +129,7 @@ jobs: fetch-depth: 0 - name: Setup Node and pnpm - uses: silverhand-io/actions-node-pnpm-run-steps@v4 + uses: silverhand-io/actions-node-pnpm-run-steps@v5 with: pnpm-version: 9 @@ -161,7 +161,7 @@ jobs: fetch-depth: 0 - name: Setup Node and pnpm - uses: silverhand-io/actions-node-pnpm-run-steps@v4 + uses: silverhand-io/actions-node-pnpm-run-steps@v5 with: pnpm-version: 9 diff --git a/.github/workflows/rerun.yml b/.github/workflows/rerun.yml new file mode 100644 index 00000000000..a630dafafaa --- /dev/null +++ b/.github/workflows/rerun.yml @@ -0,0 +1,20 @@ +# From this genius: https://github.com/orgs/community/discussions/67654#discussioncomment-8038649 +name: Rerun workflow + +on: + workflow_dispatch: + inputs: + run_id: + required: true +jobs: + rerun: + runs-on: ubuntu-latest + steps: + - name: rerun ${{ inputs.run_id }} + env: + GH_REPO: ${{ github.repository }} + GH_TOKEN: ${{ github.token }} + GH_DEBUG: api + run: | + gh run watch ${{ inputs.run_id }} > /dev/null 2>&1 + gh run rerun ${{ inputs.run_id }} --failed diff --git a/.github/workflows/upload-annotations.yml b/.github/workflows/upload-annotations.yml index 835c8fd07ba..d9691c6f744 100644 --- a/.github/workflows/upload-annotations.yml +++ b/.github/workflows/upload-annotations.yml @@ -22,7 +22,7 @@ jobs: - uses: actions/checkout@v4 - name: Setup Node and pnpm - uses: silverhand-io/actions-node-pnpm-run-steps@v4 + uses: silverhand-io/actions-node-pnpm-run-steps@v5 with: pnpm-version: 9 diff --git a/.scripts/compare-database.js b/.scripts/compare-database.js index 5a44b4f2f16..b959db38533 100644 --- a/.scripts/compare-database.js +++ b/.scripts/compare-database.js @@ -99,21 +99,46 @@ const queryDatabaseManifest = async (database) => { `); // This function removes the last segment of grantee since Logto will use 'logto_tenant_fresh/alteration' for the role name. - const normalizeGrantee = ({ grantee, ...rest }) => { - if (grantee.startsWith('logto_tenant_')) { - return { ...rest, grantee: 'logto_tenant' }; + const normalizeRoleName = (roleName) => { + if (roleName.startsWith('logto_tenant_')) { + return 'logto_tenant'; } - return { grantee, ...rest }; + // Removes the last segment of region grantee since Logto will use 'logto_region_xxx' for the role name for different regions. + if (roleName.startsWith('logto_region_')) { + return 'logto_region'; + } + + return roleName; }; + const normalizeGrantee = ({ grantee, ...rest }) => ({ + ...rest, + grantee: normalizeRoleName(grantee), + }); + // Ditto. const normalizeRoles = ({ roles: raw, ...rest }) => { - const roles = raw.slice(1, -1).split(',').map((name) => name.startsWith('logto_tenant_') ? 'logto_tenant' : name); + const roles = raw + .slice(1, -1) + .split(',') + .map((name) => normalizeRoleName(name)); return { roles, ...rest }; }; + const normalizePolicyname = ({ policyname, ...rest }) => { + const prefix = 'allow_'; + const suffix = '_access'; + if (policyname && policyname.startsWith(prefix) && policyname.endsWith(suffix)) { + // This is a naming convention in Logto cloud, it is formatted as `allow_{role_name}_access`, we need to normalize the role name part for the convenience of comparing DB updates. + // Ref: https://github.com/logto-io/cloud/pull/738 + return { policyname: `${prefix}${normalizeRoleName(policyname.slice(prefix.length, -suffix.length))}${suffix}`, ...rest }; + } + + return { policyname, ...rest }; + }; + // Omit generated ids and values return { tables: omitArray(tables, 'table_catalog'), @@ -144,7 +169,7 @@ const queryDatabaseManifest = async (database) => { indexes, funcs, triggers: omitArray(triggers, 'trigger_catalog', 'event_object_catalog'), - policies: policies.map(normalizeRoles), + policies: policies.map(normalizeRoles).map(normalizePolicyname), columnGrants: omitArray(columnGrants, 'table_catalog').map(normalizeGrantee), tableGrants: omitArray(tableGrants, 'table_catalog').map(normalizeGrantee), }; diff --git a/.vscode/settings.json b/.vscode/settings.json index b83dc8dfa43..805805faacf 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -54,6 +54,7 @@ "timestamptz", "topbar", "upsell", - "withtyped" + "withtyped", + "backchannel" ] } diff --git a/README.md b/README.md index 3ddb20811e3..35d06147178 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,15 @@

- - + + + + + Logto logo +

+
+ [![discord](https://img.shields.io/discord/965845662535147551?color=5865f2&label=discord)](https://discord.gg/vRvwuwgpVX) [![checks](https://img.shields.io/github/checks-status/logto-io/logto/master)](https://github.com/logto-io/logto/actions?query=branch%3Amaster) [![release](https://img.shields.io/github/v/release/logto-io/logto?color=3a3c3f)](https://github.com/logto-io/logto/releases) diff --git a/package.json b/package.json index 68e563f8f8a..28ff7ea7f52 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "typescript": "^5.0.0" }, "engines": { - "node": "^20.10.0", + "node": "^20.9.0", "pnpm": "^9.0.0" }, "pnpm": { diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index dd8c0dbbe53..c0cf24da4bb 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,27 @@ # Change Log +## 1.18.0 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] +- Updated dependencies [942780fcf] +- Updated dependencies [87615d58c] +- Updated dependencies [9f33d997b] +- Updated dependencies [061a30a87] +- Updated dependencies [ef21c7a99] +- Updated dependencies [136320584] +- Updated dependencies [b52609a1e] +- Updated dependencies [efa884c40] +- Updated dependencies [b50ba0b7e] +- Updated dependencies [d81e13d21] + - @logto/connector-kit@4.0.0 + - @logto/phrases@1.12.0 + - @logto/schemas@1.18.0 + - @logto/phrases-experience@1.7.0 + ## 1.17.0 ### Minor Changes diff --git a/packages/cli/package.json b/packages/cli/package.json index 691b1a197f4..21793fdb2e4 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@logto/cli", - "version": "1.17.0", + "version": "1.18.0", "description": "Logto CLI.", "author": "Silverhand Inc. ", "homepage": "https://github.com/logto-io/logto#readme", @@ -42,12 +42,12 @@ "url": "https://github.com/logto-io/logto/issues" }, "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@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.11.0", - "@logto/phrases-experience": "workspace:^1.6.1", - "@logto/schemas": "workspace:1.17.0", + "@logto/phrases": "workspace:^1.12.0", + "@logto/phrases-experience": "workspace:^1.7.0", + "@logto/schemas": "workspace:1.18.0", "@logto/shared": "workspace:^3.1.1", "@silverhand/essentials": "^2.9.1", "@silverhand/slonik": "31.0.0-beta.2", diff --git a/packages/connectors/connector-alipay-native/CHANGELOG.md b/packages/connectors/connector-alipay-native/CHANGELOG.md index 6e84bd0e814..f1876d9f116 100644 --- a/packages/connectors/connector-alipay-native/CHANGELOG.md +++ b/packages/connectors/connector-alipay-native/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-alipay-native +## 1.2.1 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 1.2.0 ### Minor Changes diff --git a/packages/connectors/connector-alipay-native/README.md b/packages/connectors/connector-alipay-native/README.md index d1defbf05f7..ef6ac9a20b5 100644 --- a/packages/connectors/connector-alipay-native/README.md +++ b/packages/connectors/connector-alipay-native/README.md @@ -68,7 +68,7 @@ Alipay Native connector works closely with Logto SDK on mobile platforms. It tak 3. Fill out the Logto connector settings: - Fill out the `appId` field with APPID you've got from step 1. - Fill out the `privateKey` field with contents from the private key file mentioned in step 2. Please MAKE SURE to use '\n' to replace all newline characters. You don't need to remove header and footer in private key file. - - Fill out the `signType` filed with 'RSA2' due to the `Public key` signing mode we chose in step 7 of "Create And Configure Alipay Apps". + - Fill out the `signType` field with 'RSA2' due to the `Public key` signing mode we chose in step 7 of "Create And Configure Alipay Apps". ### Config types @@ -138,7 +138,7 @@ dependencies { ### Test Alipay native connector -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-social-sign-in). +That's it. Don't forget to [Enable connector in sign-in experience](https://docs.logto.io/docs/recipes/configure-connectors/social-connector/enable-social-sign-in/). Once Alipay native connector is enabled, you can build and run your app to see if it works. @@ -258,7 +258,7 @@ dependencies { ## 测试支付宝原生连接器 -大功告成。别忘了 [在登录体验中启用社交登录](https://docs.logto.io/docs/tutorials/get-started/passwordless-sign-in-by-adding-connectors#enable-social-sign-in)。 +大功告成。别忘了 [在登录体验中启用社交登录](https://docs.logto.io/docs/recipes/configure-connectors/social-connector/enable-social-sign-in/)。 在支付宝原生连接器启用后,你可以构建并运行你的应用看看是否生效。 diff --git a/packages/connectors/connector-alipay-native/package.json b/packages/connectors/connector-alipay-native/package.json index 6cbf3aa528c..7099bda0ce0 100644 --- a/packages/connectors/connector-alipay-native/package.json +++ b/packages/connectors/connector-alipay-native/package.json @@ -1,10 +1,10 @@ { "name": "@logto/connector-alipay-native", - "version": "1.2.0", + "version": "1.2.1", "description": "Alipay Native implementation.", "author": "Silverhand Inc. ", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "dayjs": "^1.10.5", "got": "^14.0.0", @@ -13,7 +13,7 @@ "zod": "^3.22.4" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@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", diff --git a/packages/connectors/connector-alipay-web/CHANGELOG.md b/packages/connectors/connector-alipay-web/CHANGELOG.md index d2682545a91..5ffe877b82d 100644 --- a/packages/connectors/connector-alipay-web/CHANGELOG.md +++ b/packages/connectors/connector-alipay-web/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-alipay-web +## 1.3.1 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 1.3.0 ### Minor Changes diff --git a/packages/connectors/connector-alipay-web/README.md b/packages/connectors/connector-alipay-web/README.md index c4df55bb3b1..a5cbbc07a55 100644 --- a/packages/connectors/connector-alipay-web/README.md +++ b/packages/connectors/connector-alipay-web/README.md @@ -78,7 +78,7 @@ Alipay Web connector is designed for desktop Web applications. It takes advantag ## Test Alipay web connector -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-social-sign-in). +That's it. Don't forget to [Enable connector in sign-in experience](https://docs.logto.io/docs/recipes/configure-connectors/social-connector/enable-social-sign-in/). Once Alipay web connector is enabled, you can build and run your web app to see if it works. @@ -144,7 +144,7 @@ Once Alipay web connector is enabled, you can build and run your web app to see ## 测试支付宝网页连接器 -大功告成。别忘了 [在登录体验中启用社交登录](https://docs.logto.io/docs/tutorials/get-started/passwordless-sign-in-by-adding-connectors#enable-social-sign-in)。 +大功告成。别忘了 [在登录体验中启用社交登录](https://docs.logto.io/docs/recipes/configure-connectors/social-connector/enable-social-sign-in/)。 在支付宝网页连接器启用后,你可以构建并运行你的网页应用看看是否生效。 diff --git a/packages/connectors/connector-alipay-web/package.json b/packages/connectors/connector-alipay-web/package.json index 6a787a15fc3..10b5095b6b4 100644 --- a/packages/connectors/connector-alipay-web/package.json +++ b/packages/connectors/connector-alipay-web/package.json @@ -1,9 +1,9 @@ { "name": "@logto/connector-alipay-web", - "version": "1.3.0", + "version": "1.3.1", "description": "Alipay implementation.", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "dayjs": "^1.10.5", "got": "^14.0.0", @@ -12,7 +12,7 @@ "zod": "^3.22.4" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@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", diff --git a/packages/connectors/connector-aliyun-dm/CHANGELOG.md b/packages/connectors/connector-aliyun-dm/CHANGELOG.md index 425189c294e..194f3b8132c 100644 --- a/packages/connectors/connector-aliyun-dm/CHANGELOG.md +++ b/packages/connectors/connector-aliyun-dm/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-aliyun-dm +## 1.1.2 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 1.1.1 ### Patch Changes diff --git a/packages/connectors/connector-aliyun-dm/README.md b/packages/connectors/connector-aliyun-dm/README.md index 0589e1094e8..5a465d9cf11 100644 --- a/packages/connectors/connector-aliyun-dm/README.md +++ b/packages/connectors/connector-aliyun-dm/README.md @@ -65,7 +65,7 @@ After finishing setup, there are two different ways to test: 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). +That's it. Don't forget to [Enable connector in sign-in experience](https://docs.logto.io/docs/recipes/configure-connectors/email-connector/enable-email-sign-in/). ### Config types @@ -129,7 +129,7 @@ That's it. Don't forget to [Enable connector in sign-in experience](https://docs 你可以在「保存并完成」之前输入一个邮件地址并点按「发送」来测试配置是否可以正常工作。 -大功告成!快去 [启用短信或邮件验证码登录](https://docs.logto.io/docs/tutorials/get-started/passwordless-sign-in-by-adding-connectors#enable-sms-or-email-passwordless-sign-in) 吧。 +大功告成!快去 [启用邮件验证码登录](https://docs.logto.io/docs/recipes/configure-connectors/email-connector/enable-email-sign-in/) 吧。 ### 配置类型 diff --git a/packages/connectors/connector-aliyun-dm/package.json b/packages/connectors/connector-aliyun-dm/package.json index 0aac03039c5..59e41ddd8e8 100644 --- a/packages/connectors/connector-aliyun-dm/package.json +++ b/packages/connectors/connector-aliyun-dm/package.json @@ -1,9 +1,9 @@ { "name": "@logto/connector-aliyun-dm", - "version": "1.1.1", + "version": "1.1.2", "description": "Aliyun DM connector implementation.", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", "snakecase-keys": "^8.0.0", @@ -51,7 +51,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@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", diff --git a/packages/connectors/connector-aliyun-sms/CHANGELOG.md b/packages/connectors/connector-aliyun-sms/CHANGELOG.md index 952eb8f9155..dc502f55bae 100644 --- a/packages/connectors/connector-aliyun-sms/CHANGELOG.md +++ b/packages/connectors/connector-aliyun-sms/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-aliyun-sms +## 1.1.2 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 1.1.1 ### Patch Changes diff --git a/packages/connectors/connector-aliyun-sms/README.md b/packages/connectors/connector-aliyun-sms/README.md index 3a624e2a15b..c1e1b84517f 100644 --- a/packages/connectors/connector-aliyun-sms/README.md +++ b/packages/connectors/connector-aliyun-sms/README.md @@ -67,7 +67,7 @@ Go to the [Aliyun website](https://cn.aliyun.com/) and register your Aliyun acco You can type in a phone number 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). +That's it. Don't forget to [Enable connector in sign-in experience](https://docs.logto.io/docs/recipes/configure-connectors/sms-connector/enable-SMS-sign-in/). ### Config types @@ -133,7 +133,7 @@ That's it. Don't forget to [Enable connector in sign-in experience](https://docs 你可以在「保存并完成」之前输入一个手机号码并点按「发送」来测试配置是否可以正常工作。 -大功告成!快去 [启用短信或邮件验证码登录](https://docs.logto.io/docs/tutorials/get-started/passwordless-sign-in-by-adding-connectors#enable-sms-or-email-passwordless-sign-in) 吧。 +大功告成!快去 [启用短信验证码登录](https://docs.logto.io/docs/recipes/configure-connectors/sms-connector/enable-SMS-sign-in/) 吧。 ### 配置类型 diff --git a/packages/connectors/connector-aliyun-sms/package.json b/packages/connectors/connector-aliyun-sms/package.json index bf35fd641c7..9739f43eb75 100644 --- a/packages/connectors/connector-aliyun-sms/package.json +++ b/packages/connectors/connector-aliyun-sms/package.json @@ -1,9 +1,9 @@ { "name": "@logto/connector-aliyun-sms", - "version": "1.1.1", + "version": "1.1.2", "description": "Aliyun SMS connector implementation.", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", "snakecase-keys": "^8.0.0", @@ -51,7 +51,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@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", diff --git a/packages/connectors/connector-apple/CHANGELOG.md b/packages/connectors/connector-apple/CHANGELOG.md index 56904be0ab1..1e5544aa92e 100644 --- a/packages/connectors/connector-apple/CHANGELOG.md +++ b/packages/connectors/connector-apple/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-apple +## 1.3.1 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 1.3.0 ### Minor Changes diff --git a/packages/connectors/connector-apple/README.md b/packages/connectors/connector-apple/README.md index ba1e5324547..e942743d613 100644 --- a/packages/connectors/connector-apple/README.md +++ b/packages/connectors/connector-apple/README.md @@ -82,4 +82,4 @@ See developer discussion [here](https://forums.developer.apple.com/forums/thread ## Test Apple connector -That's it. The Apple connector should be available in both web and native apps. 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-social-sign-in). +That's it. The Apple connector should be available in both web and native apps. Don't forget to [Enable connector in sign-in experience](https://docs.logto.io/docs/recipes/configure-connectors/social-connector/enable-social-sign-in/). diff --git a/packages/connectors/connector-apple/package.json b/packages/connectors/connector-apple/package.json index 8aa1e61eb37..2c11467106b 100644 --- a/packages/connectors/connector-apple/package.json +++ b/packages/connectors/connector-apple/package.json @@ -1,9 +1,9 @@ { "name": "@logto/connector-apple", - "version": "1.3.0", + "version": "1.3.1", "description": "Apple web connector implementation.", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@logto/shared": "workspace:^3.1.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", @@ -53,7 +53,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@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", diff --git a/packages/connectors/connector-aws-ses/CHANGELOG.md b/packages/connectors/connector-aws-ses/CHANGELOG.md index eb0337e4ede..c1be1d0c438 100644 --- a/packages/connectors/connector-aws-ses/CHANGELOG.md +++ b/packages/connectors/connector-aws-ses/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-aws-ses +## 1.1.2 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 1.1.1 ### Patch Changes diff --git a/packages/connectors/connector-aws-ses/README.md b/packages/connectors/connector-aws-ses/README.md index 2e4c5dcb0b9..a032fca6b85 100644 --- a/packages/connectors/connector-aws-ses/README.md +++ b/packages/connectors/connector-aws-ses/README.md @@ -52,7 +52,7 @@ the following parameters are optional; parameters description can be found in th You can type in an email address and click on "Send" to see whether the settings 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). +That's it. Don't forget to [Enable connector in sign-in experience](https://docs.logto.io/docs/recipes/configure-connectors/email-connector/enable-email-sign-in/). ### Configure types diff --git a/packages/connectors/connector-aws-ses/package.json b/packages/connectors/connector-aws-ses/package.json index a9563d24b21..99f6582e302 100644 --- a/packages/connectors/connector-aws-ses/package.json +++ b/packages/connectors/connector-aws-ses/package.json @@ -1,12 +1,12 @@ { "name": "@logto/connector-aws-ses", - "version": "1.1.1", + "version": "1.1.2", "description": "Logto Connector for Amazon SES", "author": "Jeff ", "dependencies": { "@aws-sdk/client-sesv2": "^3.556.0", "@aws-sdk/types": "^3.535.0", - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", "snakecase-keys": "^8.0.0", @@ -54,7 +54,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@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", diff --git a/packages/connectors/connector-azuread/CHANGELOG.md b/packages/connectors/connector-azuread/CHANGELOG.md index f24cfea3574..6fd68879451 100644 --- a/packages/connectors/connector-azuread/CHANGELOG.md +++ b/packages/connectors/connector-azuread/CHANGELOG.md @@ -1,5 +1,18 @@ # @logto/connector-azuread +## 1.3.0 + +### Minor Changes + +- 15953609b: support config of `prompt` + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 1.2.0 ### Minor Changes diff --git a/packages/connectors/connector-azuread/README.md b/packages/connectors/connector-azuread/README.md index ac09b62863a..6bb274fa36b 100644 --- a/packages/connectors/connector-azuread/README.md +++ b/packages/connectors/connector-azuread/README.md @@ -6,9 +6,12 @@ The Microsoft Azure AD connector provides a succinct way for your application to - [Microsoft Azure AD connector](#microsoft-azure-ad-connector) - [Set up Microsoft Azure AD in the Azure Portal](#set-up-microsoft-azure-ad-in-the-azure-portal) - - [Fill in the configuration](#fill-in-the-configuration) - - [Configure your client secret](#configure-your-client-secret) - - [Config types](#config-types) + - [Fill in the configuration in Logto](#fill-in-the-configuration-in-logto) + - [Client ID](#client-id) + - [Client Secret](#client-secret) + - [Cloud Instance](#cloud-instance) + - [Tenant ID](#tenant-id) + - [Prompts](#prompts) - [References](#references) ## Set up Microsoft Azure AD in the Azure Portal @@ -21,12 +24,47 @@ The Microsoft Azure AD connector provides a succinct way for your application to ## Fill in the configuration in Logto -| Name | Type | -| ------------- | ------ | -| clientId | string | -| clientSecret | string | -| tenantId | string | -| cloudInstance | string | +| Name | Type | +| ------------- | -------- | +| clientId | string | +| clientSecret | string | +| tenantId | string | +| cloudInstance | string | +| prompts | string[] | + +### Client ID + +You may find the **Application (client) ID** in the **Overview** section of your newly created application in the Azure Portal. + +### Client Secret + +- In your newly created application, click the **Certificates & Secrets** to get a client secret, and click the **New client secret** from the top. +- Enter a description and an expiration. +- This will only show your client secret once. Fill the **value** to the Logto connector configuration and save it to a secure location. + +### Cloud Instance + +Usually, it is `https://login.microsoftonline.com/`. See [Azure AD authentication endpoints](https://learn.microsoft.com/en-us/azure/active-directory/develop/authentication-national-cloud#azure-ad-authentication-endpoints) for more information. + +### Tenant ID + +Logto will use this field to construct the authorization endpoints. This value is dependent on the **access type** you selected when creating the application in the Azure Portal. + +- If you select **Accounts in this organizational directory only** for access type then you need to enter your **{TenantID}**. You can find the tenant ID in the **Overview** section of your Azure Active Directory. +- If you select **Accounts in any organizational directory** for access type then you need to enter **organizations**. +- If you select **Accounts in any organizational directory or personal Microsoft accounts** for access type then you need to enter **common**. +- If you select **Personal Microsoft accounts only** for access type then you need to enter **consumers**. + +### Prompts + +The `prompts` field is an array of strings that specifies the type of user interaction that is required. The string can be one of the following values: + +- `prompt=login` forces the user to enter their credentials on that request, negating single-sign on. +- `prompt=none` is the opposite. It ensures that the user isn't presented with any interactive prompt. If the request can't be completed silently by using single-sign on, the Microsoft identity platform returns an `interaction_required` error. +- `prompt=consent` triggers the OAuth consent dialog after the user signs in, asking the user to grant permissions to the app. +- `prompt=select_account` interrupts single sign-on providing account selection experience listing all the accounts either in session or any remembered account or an option to choose to use a different account altogether. + +Logto will concatenate the prompts with a space as the value of `prompt` in the authorization URL. ### Client ID diff --git a/packages/connectors/connector-azuread/package.json b/packages/connectors/connector-azuread/package.json index ceb0c74cf83..773b2da57be 100644 --- a/packages/connectors/connector-azuread/package.json +++ b/packages/connectors/connector-azuread/package.json @@ -1,11 +1,11 @@ { "name": "@logto/connector-azuread", - "version": "1.2.0", + "version": "1.3.0", "description": "Microsoft Azure AD connector implementation.", "author": "Mobilist Inc. ", "dependencies": { "@azure/msal-node": "^2.0.0", - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", "snakecase-keys": "^8.0.0", @@ -53,7 +53,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@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", diff --git a/packages/connectors/connector-azuread/src/constant.ts b/packages/connectors/connector-azuread/src/constant.ts index 9e38a31f04a..0eafd7cee51 100644 --- a/packages/connectors/connector-azuread/src/constant.ts +++ b/packages/connectors/connector-azuread/src/constant.ts @@ -1,5 +1,5 @@ import type { ConnectorMetadata } from '@logto/connector-kit'; -import { ConnectorPlatform, ConnectorConfigFormItemType } from '@logto/connector-kit'; +import { ConnectorPlatform, ConnectorConfigFormItemType, OidcPrompt } from '@logto/connector-kit'; export const graphAPIEndpoint = 'https://graph.microsoft.com/v1.0/me'; export const scopes = ['User.Read']; @@ -53,6 +53,15 @@ export const defaultMetadata: ConnectorMetadata = { label: 'Tenant ID', placeholder: '', }, + { + key: 'prompts', + type: ConnectorConfigFormItemType.MultiSelect, + required: false, + label: 'Prompts', + selectItems: Object.values(OidcPrompt).map((prompt) => ({ + value: prompt, + })), + }, ], }; diff --git a/packages/connectors/connector-azuread/src/index.ts b/packages/connectors/connector-azuread/src/index.ts index eec02121a34..879df9c77cc 100644 --- a/packages/connectors/connector-azuread/src/index.ts +++ b/packages/connectors/connector-azuread/src/index.ts @@ -37,12 +37,13 @@ const getAuthorizationUri = const config = await getConfig(defaultMetadata.id); validateConfig(config, azureADConfigGuard); - const { clientId, clientSecret, cloudInstance, tenantId } = config; + const { clientId, clientSecret, cloudInstance, tenantId, prompts } = config; const defaultAuthCodeUrlParameters: AuthorizationUrlRequest = { scopes, state, redirectUri, + ...conditional(prompts && prompts.length > 0 && { prompt: prompts.join(' ') }), }; const clientApplication = new ConfidentialClientApplication({ diff --git a/packages/connectors/connector-azuread/src/types.ts b/packages/connectors/connector-azuread/src/types.ts index d2a28809ff1..9af09f1698f 100644 --- a/packages/connectors/connector-azuread/src/types.ts +++ b/packages/connectors/connector-azuread/src/types.ts @@ -1,10 +1,13 @@ import { z } from 'zod'; +import { oidcPromptsGuard } from '@logto/connector-kit'; + export const azureADConfigGuard = z.object({ clientId: z.string(), clientSecret: z.string(), cloudInstance: z.string(), tenantId: z.string(), + prompts: oidcPromptsGuard, }); export type AzureADConfig = z.infer; diff --git a/packages/connectors/connector-dingtalk-web/CHANGELOG.md b/packages/connectors/connector-dingtalk-web/CHANGELOG.md index 07a02ba542d..61d3dd9d940 100644 --- a/packages/connectors/connector-dingtalk-web/CHANGELOG.md +++ b/packages/connectors/connector-dingtalk-web/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-dingtalk-web +## 0.1.1 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 0.1.0 ### Minor Changes diff --git a/packages/connectors/connector-dingtalk-web/README.md b/packages/connectors/connector-dingtalk-web/README.md index b2df1b1380c..6e0be8350a7 100644 --- a/packages/connectors/connector-dingtalk-web/README.md +++ b/packages/connectors/connector-dingtalk-web/README.md @@ -12,6 +12,7 @@ The official Logto connector for DingTalk social sign-in in web apps. - [Register a DingTalk Developer Account](#register-a-dingtalk-developer-account) - [Create an Application](#create-an-application) - [Configure Permissions](#configure-permissions) + - [Release Application](#release-application) - [Configure Your Connector](#configure-your-connector) - [Config Types](#config-types) - [Test DingTalk Connector](#test-dingtalk-connector) @@ -22,6 +23,7 @@ The official Logto connector for DingTalk social sign-in in web apps. - [注册钉钉开发者账号](#注册钉钉开发者账号) - [创建应用](#创建应用) - [配置权限](#配置权限) + - [应用发布](#应用发布) - [配置你的连接器](#配置你的连接器) - [配置类型](#配置类型) - [测试钉钉连接器](#测试钉钉连接器) @@ -34,7 +36,6 @@ The DingTalk web connector is designed for desktop web applications. It uses the ## Create a web app in the DingTalk Open Platform > 💡 **Tip** -> > You can skip some sections if you have already finished. ### Register a DingTalk developer account @@ -43,19 +44,21 @@ If you do not have a DingTalk developer account, please register at the [DingTal ### Create an application -1. In the [DingTalk Developer Console](https://open-dev.dingtalk.com/console/index), click "Create Application" -2. Choose "Self-built Application", fill in the application name and basic information, and click "Create" -3. In the left navigation bar, select "Development Configuration" -> "Security Settings", find and configure the "Redirect URL" `${your_logto_origin}/callback/${connector_id}`. You can find the `connector_id` on the connector details page after adding the respective connector in the management console -4. In the left navigation bar, select "Basic Information" -> "Credentials and Basic Information" to get the "Client ID" and "Client Secret" -5. In the left navigation bar, select "Application Release" -> "Version Management and Release", create and release the first version to activate the "Client ID" and "Client Secret" - -> ℹ️ **Note** -> If the application does not release a version, the obtained "Client ID" and "Client Secret" cannot be used, or requests will fail. +1. In the DingTalk Open Platform "[Application Development](https://open-dev.dingtalk.com/fe/app)" > "Internal Enterprise Application" > "DingTalk Application", click "Create Application" +2. Fill in the **application name** and **description**, and click "Save" +3. In the left navigation bar, select "Development Configuration" > "Security Settings", find and configure the "Redirect URL" `${your_logto_origin}/callback/${connector_id}`. You can find the `connector_id` on the connector details page after adding the respective connector in the management console +4. In the left navigation bar, select "Basic Information" > "Credentials and Basic Information" to get the `Client ID` and `Client Secret` ### Configure permissions -1. In "Development Configuration" -> "Permission Management", select `Contact.User.Read` and `Contact.User.mobile` permissions and authorize them -2. After confirming the permission configuration, click "Save" and publish the application +In "Development Configuration" > "Permission Management", select `Contact.User.Read` and `Contact.User.mobile` permissions and authorize them + +### Release Application + +In the left navigation bar, select "Application Release" > "Version Management and Release", create and release the first version to activate the `Client ID` and `Client Secret` + +> ℹ️ **Note** +> If the application does not release a version, the obtained "Client ID" and "Client Secret" cannot be used, or requests will fail. ## Configure your connector @@ -73,7 +76,7 @@ Fill out the `clientId` and `clientSecret` field with _Client ID(formerly AppKey ## Test DingTalk connector -That's it. The DingTalk connector should be available now. 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-social-sign-in). +That's it. The DingTalk connector should be available now. Don't forget to [Enable connector in sign-in experience](https://docs.logto.io/docs/recipes/configure-connectors/social-connector/enable-social-sign-in/). Once DingTalk web connector is enabled, you can sign in to your app again to see if it works. @@ -93,7 +96,6 @@ If you have any questions or need further assistance, please visit the [DingTalk ## 在钉钉开放平台新建一个应用 > 💡 **Tip** -> > 你可以跳过已经完成的部分。 ### 注册钉钉开发者账号 @@ -102,25 +104,27 @@ If you have any questions or need further assistance, please visit the [DingTalk ### 创建应用 -1. 在 [钉钉开发者后台](https://open-dev.dingtalk.com/console/index) 中,点击「创建应用」 -2. 选择「自建应用」,填写应用名称和基本信息,点击「创建」 -3. 在左侧导航栏选择「开发配置」->「安全设置」,找到并配置「重定向 URL」 `${your_logto_origin}/callback/${connector_id}`。其中 `connector_id` 在管理控制台添加了相应的连接器之后,可以在连接器的详情页中找到 -4. 在左侧导航栏选择「基础信息」->「凭证与基础信息」中可以获取「Client ID」、「Client Secret」 -5. 在左侧导航栏选择「应用发布」->「版本管理与发布」,创建并发布第一个版本,以使「Client ID」、「Client Secret」生效 - -> ℹ️ **Note** -> 应用不发布版本,所获取的「Client ID」、「Client Secret」 均无法使用,或请求错误。 +1. 在 钉钉开放平台「[应用开发](https://open-dev.dingtalk.com/fe/app)」>「企业内部应用」>「钉钉应用」中,点击「创建应用」 +2. 填写**应用名称**和**应用描述**,点击「保存」 +3. 在左侧导航栏选择「开发配置」>「安全设置」,找到并配置「重定向 URL」 `${your_logto_origin}/callback/${connector_id}`。其中 `connector_id` 在管理控制台添加了相应的连接器之后,可以在连接器的详情页中找到 +4. 在左侧导航栏选择「基础信息」>「凭证与基础信息」中可以获取「Client ID」、「Client Secret」 ### 配置权限 -1. 在「开发配置」->「权限管理」中,选择`通讯录个人信息读权限`和`个人手机号信息`权限并进行授权 -2. 确认权限配置后,点击「保存」并发布应用 +在「开发配置」>「权限管理」中,选择`通讯录个人信息读权限`和`个人手机号信息`权限并进行授权 + +### 应用发布 + +在左侧导航栏选择「应用发布」>「版本管理与发布」,点击「创建新版本」发布第一个版本,以使「Client ID」、「Client Secret」生效 + +> ℹ️ **Note** +> 应用不发布版本,所获取的「Client ID」、「Client Secret」 均无法使用,或请求错误。 ## 配置你的连接器 -在 clientId 和 clientSecret 字段中填入你在上一个部分中提到的 OAuth 应用详情页面获取的 Client ID(原 AppKey 和 SuiteKey) 和 Client Secret(原 AppKey 和 SuiteKey) 。 +在 `clientId` 和 `clientSecret` 字段中填入你在上一个部分中提到的 OAuth 应用详情页面获取的 _Client ID_(原 AppKey 和 SuiteKey)和 _Client Secret_(原 AppKey 和 SuiteKey)。 -scope 目前支持两种值:openid 和 openid corpid。openid 授权后可以获取用户的 userid,而 openid corpid 授权后可以获取用户的 id 和登录过程中用户选择的组织 id。这些值应以空格分隔。注意:需要进行 URL 编码。 +`scope` 目前支持两种值:`openid` 和 `openid corpid`。`openid` 授权后可以获取用户的 `userid`,而 `openid corpid` 授权后可以获取用户的 `id` 和登录过程中用户选择的组织 `id`。这些值应以空格分隔。注意:需要进行 URL 编码。 ### 配置类型 @@ -132,7 +136,7 @@ scope 目前支持两种值:openid 和 openid corpid。openid 授权后可以 ## 测试钉钉连接器 -大功告成。别忘了 [在登录体验中启用本连接器](https://docs.logto.io/docs/tutorials/get-started/passwordless-sign-in-by-adding-connectors#enable-social-sign-in)。 +大功告成。别忘了 [在登录体验中启用本连接器](https://docs.logto.io/docs/recipes/configure-connectors/social-connector/enable-social-sign-in/)。 在钉钉web连接器启用后,你可以构建并运行你的应用看看是否生效。 diff --git a/packages/connectors/connector-dingtalk-web/package.json b/packages/connectors/connector-dingtalk-web/package.json index 08374ef511e..d2815d24c29 100644 --- a/packages/connectors/connector-dingtalk-web/package.json +++ b/packages/connectors/connector-dingtalk-web/package.json @@ -1,9 +1,9 @@ { "name": "@logto/connector-dingtalk-web", - "version": "0.1.0", + "version": "0.1.1", "description": "Dingtalk web connector implementation.", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "dayjs": "^1.10.5", "got": "^14.0.0", @@ -12,7 +12,7 @@ "zod": "^3.22.4" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@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", diff --git a/packages/connectors/connector-discord/CHANGELOG.md b/packages/connectors/connector-discord/CHANGELOG.md index 7a1db6bf874..a321f0c4a49 100644 --- a/packages/connectors/connector-discord/CHANGELOG.md +++ b/packages/connectors/connector-discord/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-discord +## 1.3.1 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 1.3.0 ### Minor Changes diff --git a/packages/connectors/connector-discord/package.json b/packages/connectors/connector-discord/package.json index f5abedaa2ba..eef708ea75b 100644 --- a/packages/connectors/connector-discord/package.json +++ b/packages/connectors/connector-discord/package.json @@ -1,10 +1,10 @@ { "name": "@logto/connector-discord", - "version": "1.3.0", + "version": "1.3.1", "description": "Discord connector implementation.", "author": "ZR3SYSTEMS. ", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", "snakecase-keys": "^8.0.0", @@ -52,7 +52,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@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", diff --git a/packages/connectors/connector-facebook/CHANGELOG.md b/packages/connectors/connector-facebook/CHANGELOG.md index 9e5e7bcf628..094ebede4a5 100644 --- a/packages/connectors/connector-facebook/CHANGELOG.md +++ b/packages/connectors/connector-facebook/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-facebook +## 1.3.1 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 1.3.0 ### Minor Changes diff --git a/packages/connectors/connector-facebook/package.json b/packages/connectors/connector-facebook/package.json index 3cc19cc84da..787adb612c6 100644 --- a/packages/connectors/connector-facebook/package.json +++ b/packages/connectors/connector-facebook/package.json @@ -1,10 +1,10 @@ { "name": "@logto/connector-facebook", - "version": "1.3.0", + "version": "1.3.1", "description": "Facebook web connector implementation.", "author": "Silverhand Inc. ", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", "snakecase-keys": "^8.0.0", @@ -52,7 +52,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@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", diff --git a/packages/connectors/connector-feishu-web/CHANGELOG.md b/packages/connectors/connector-feishu-web/CHANGELOG.md index 69fca21a6ae..3d0f5dbfbf8 100644 --- a/packages/connectors/connector-feishu-web/CHANGELOG.md +++ b/packages/connectors/connector-feishu-web/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-feishu-web +## 1.2.1 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 1.2.0 ### Minor Changes diff --git a/packages/connectors/connector-feishu-web/package.json b/packages/connectors/connector-feishu-web/package.json index 4d592023046..b6db69458b6 100644 --- a/packages/connectors/connector-feishu-web/package.json +++ b/packages/connectors/connector-feishu-web/package.json @@ -1,10 +1,10 @@ { "name": "@logto/connector-feishu-web", - "version": "1.2.0", + "version": "1.2.1", "description": "Feishu web connector.", "author": "Silverhand Inc. ", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", "snakecase-keys": "^8.0.0", @@ -52,7 +52,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@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", diff --git a/packages/connectors/connector-github/CHANGELOG.md b/packages/connectors/connector-github/CHANGELOG.md index 353a78a327b..3070f3df691 100644 --- a/packages/connectors/connector-github/CHANGELOG.md +++ b/packages/connectors/connector-github/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-github +## 1.4.2 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 1.4.1 ### Patch Changes diff --git a/packages/connectors/connector-github/README.md b/packages/connectors/connector-github/README.md index f3315992ae7..fabfea8a026 100644 --- a/packages/connectors/connector-github/README.md +++ b/packages/connectors/connector-github/README.md @@ -55,7 +55,7 @@ Fill out the `clientId` and `clientSecret` field with _Client ID_ and _Client Se ## Test GitHub connector -That's it. The GitHub connector should be available now. 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-social-sign-in). +That's it. The GitHub connector should be available now. Don't forget to [Enable connector in sign-in experience](https://docs.logto.io/docs/recipes/configure-connectors/social-connector/enable-social-sign-in/). ## Reference diff --git a/packages/connectors/connector-github/package.json b/packages/connectors/connector-github/package.json index b21f6d97487..1e9eeb2a4e9 100644 --- a/packages/connectors/connector-github/package.json +++ b/packages/connectors/connector-github/package.json @@ -1,10 +1,10 @@ { "name": "@logto/connector-github", - "version": "1.4.1", + "version": "1.4.2", "description": "Github web connector implementation.", "author": "Silverhand Inc. ", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "ky": "^1.2.3", "query-string": "^9.0.0", @@ -53,7 +53,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@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", diff --git a/packages/connectors/connector-google/CHANGELOG.md b/packages/connectors/connector-google/CHANGELOG.md index 2f79c263150..1525e3dea4a 100644 --- a/packages/connectors/connector-google/CHANGELOG.md +++ b/packages/connectors/connector-google/CHANGELOG.md @@ -1,5 +1,23 @@ # @logto/connector-google +## 1.4.0 + +### Minor Changes + +- 6308ee185: support Google One Tap + + - support parsing and validating Google One Tap data in `connector-google` + - add Google connector constants in `connector-kit` for reuse + +- 15953609b: support config of `prompt` + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 1.3.0 ### Minor Changes diff --git a/packages/connectors/connector-google/README.md b/packages/connectors/connector-google/README.md index 767e6b71e19..2ed9db7e7b4 100644 --- a/packages/connectors/connector-google/README.md +++ b/packages/connectors/connector-google/README.md @@ -3,18 +3,17 @@ The Google connector provides a succinct way for your application to use Google’s OAuth 2.0 authentication system. **Table of contents** -- [Google connector](#google-connector) - - [Set up a project in the Google API Console](#set-up-a-project-in-the-google-api-console) - - [Configure your consent screen](#configure-your-consent-screen) - - [Configure and register your application](#configure-and-register-your-application) - - [Edit app registration](#edit-app-registration) - - [Config OAuth consent screen](#config-oauth-consent-screen) - - [Config scopes](#config-scopes) - - [Add test users (External user type only)](#add-test-users-external-user-type-only) - - [Obtain OAuth 2.0 credentials](#obtain-oauth-20-credentials) - - [Configure your connector](#configure-your-connector) - - [Config types](#config-types) - - [References](#references) +- [Set up a project in the Google API Console](#set-up-a-project-in-the-google-api-console) +- [Configure your consent screen](#configure-your-consent-screen) + - [Configure and register your application](#configure-and-register-your-application) + - [Edit app registration](#edit-app-registration) + - [Config OAuth consent screen](#config-oauth-consent-screen) + - [Config scopes](#config-scopes) + - [Add test users (External user type only)](#add-test-users-external-user-type-only) +- [Obtain OAuth 2.0 credentials](#obtain-oauth-20-credentials) +- [Configure your connector](#configure-your-connector) + - [Config types](#config-types) +- [References](#references) ## Set up a project in the Google API Console @@ -57,8 +56,8 @@ Now you should have the Google OAuth 2.0 consent screen configured. - On the **Credentials** page, click the **+ CREATE CREDENTIALS** button on the top menu bar, and select **OAuth client ID**. - On the **Create OAuth client ID** page, select **Web application** as the application type. - Fill out the basic information for your application. -- Click **+ Add URI** to add an authorized domain to the **Authorized JavaScript origins** section. This is the domain that your logto authorization page will be served from. In our case, this will be `${your_logto_origin}`. e.g.`https://logto.dev`. -- Click **+ Add URI** in the ****Authorized redirect URIs**** section to set up the ****Authorized redirect URIs****, which redirect the user to the application after logging in. In our case, this will be `${your_logto_endpoint}/callback/${connector_id}`. e.g. `https://logto.dev/callback/${connector_id}`. The `connector_id` can be found on the top bar of the Logto Admin Console connector details page. +- Click **+ Add URI** to add an authorized domain to the **Authorized JavaScript origins** section. This is the domain that your logto authorization page will be served from. In our case, this will be `${your_logto_endpoint_origin}`. e.g.`https://foo.logto.app`. +- Click **+ Add URI** in the ****Authorized redirect URIs**** section to set up the ****Authorized redirect URIs****, which redirect the user to the application after logging in. In our case, this will be `${your_logto_endpoint}/callback/${connector_id}`. e.g. `https://foo.logto.app/callback/${connector_id}`. The `connector_id` can be found on the top bar of the Logto Admin Console connector details page. - Click **Create** to finish and then you will get the **Client ID** and **Client Secret**. ## Configure your connector @@ -67,13 +66,20 @@ Fill out the `clientId` and `clientSecret` field with _Client ID_ and _Client Se `scope` is a space-delimited list of [scopes](https://developers.google.com/identity/protocols/oauth2/scopes). If not provided, scope defaults to be `openid profile email`. +`prompts` is an array of strings that specifies the type of user interaction that is required. The string can be one of the following values: + +- `none`: The authorization server does not display any authentication or user consent screens; it will return an error if the user is not already authenticated and has not pre-configured consent for the requested scopes. You can use none to check for existing authentication and/or consent. +- `consent`: The authorization server prompts the user for consent before returning information to the client. +- `select_account`: The authorization server prompts the user to select a user account. This allows a user who has multiple accounts at the authorization server to select amongst the multiple accounts that they may have current sessions for. + ### Config types -| Name | Type | -|--------------|--------| -| clientId | string | -| clientSecret | string | -| scope | string | +| Name | Type | +|--------------|----------| +| clientId | string | +| clientSecret | string | +| scope | string | +| prompts | string[] | ## References * [Google Identity: Setting up OAuth 2.0](https://developers.google.com/identity/protocols/oauth2/openid-connect#appsetup) diff --git a/packages/connectors/connector-google/package.json b/packages/connectors/connector-google/package.json index 06479432def..6f5c8da1591 100644 --- a/packages/connectors/connector-google/package.json +++ b/packages/connectors/connector-google/package.json @@ -1,12 +1,13 @@ { "name": "@logto/connector-google", - "version": "1.3.0", + "version": "1.4.0", "description": "Google web connector implementation.", "author": "Silverhand Inc. ", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@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" }, @@ -52,7 +53,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@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", diff --git a/packages/connectors/connector-google/src/constant.ts b/packages/connectors/connector-google/src/constant.ts index 9ff63ada22a..129ec67e024 100644 --- a/packages/connectors/connector-google/src/constant.ts +++ b/packages/connectors/connector-google/src/constant.ts @@ -1,14 +1,22 @@ import type { ConnectorMetadata } from '@logto/connector-kit'; -import { ConnectorConfigFormItemType, ConnectorPlatform } from '@logto/connector-kit'; +import { + ConnectorConfigFormItemType, + ConnectorPlatform, + GoogleConnector, + OidcPrompt, +} from '@logto/connector-kit'; export const authorizationEndpoint = 'https://accounts.google.com/o/oauth2/v2/auth'; export const accessTokenEndpoint = 'https://oauth2.googleapis.com/token'; export const userInfoEndpoint = 'https://openidconnect.googleapis.com/v1/userinfo'; export const scope = 'openid profile email'; +// Instead of defining the metadata in the connector, we reuse the metadata from the connector-kit. +// This is not the normal practice, but Google One Tap is a special case. +// @see {@link GoogleConnector} for more information. export const defaultMetadata: ConnectorMetadata = { - id: 'google-universal', - target: 'google', + id: GoogleConnector.factoryId, + target: GoogleConnector.target, platform: ConnectorPlatform.Universal, name: { en: 'Google', @@ -49,7 +57,23 @@ export const defaultMetadata: ConnectorMetadata = { description: "The `scope` determines permissions granted by the user's authorization. If you are not sure what to enter, do not worry, just leave it blank.", }, + { + key: 'prompts', + type: ConnectorConfigFormItemType.MultiSelect, + required: false, + label: 'Prompts', + // Google does not support `login` prompt. + // Ref: https://developers.google.com/identity/openid-connect/openid-connect#authenticationuriparameters + selectItems: Object.values(OidcPrompt) + .filter((prompt) => prompt !== OidcPrompt.Login) + .map((prompt) => ({ + value: prompt, + })), + }, ], }; export const defaultTimeout = 5000; + +// https://developers.google.com/identity/gsi/web/guides/verify-google-id-token +export const jwksUri = 'https://www.googleapis.com/oauth2/v3/certs'; diff --git a/packages/connectors/connector-google/src/index.test.ts b/packages/connectors/connector-google/src/index.test.ts index 4b258f02144..8cdeb4c202a 100644 --- a/packages/connectors/connector-google/src/index.test.ts +++ b/packages/connectors/connector-google/src/index.test.ts @@ -8,6 +8,25 @@ import { mockedConfig } from './mock.js'; const getConfig = vi.fn().mockResolvedValue(mockedConfig); +vi.mock('jose', () => ({ + createRemoteJWKSet: vi.fn().mockReturnValue({ + getSigningKey: vi.fn().mockResolvedValue({ + publicKey: 'publicKey', + }), + }), + jwtVerify: vi.fn().mockResolvedValue({ + payload: { + sub: '1234567890', + name: 'John Wick', + given_name: 'John', + family_name: 'Wick', + email: 'john@silverhand.io', + email_verified: true, + picture: 'https://example.com/image.jpg', + }, + }), +})); + describe('google connector', () => { describe('getAuthorizationUri', () => { afterEach(() => { @@ -105,6 +124,31 @@ describe('google connector', () => { }); }); + it('should be able to decode ID token from Google One Tap', async () => { + const connector = await createConnector({ getConfig }); + const socialUserInfo = await connector.getUserInfo( + { + credential: 'credential', + }, + vi.fn() + ); + expect(socialUserInfo).toStrictEqual({ + id: '1234567890', + avatar: 'https://example.com/image.jpg', + name: 'John Wick', + email: 'john@silverhand.io', + rawData: { + sub: '1234567890', + name: 'John Wick', + given_name: 'John', + family_name: 'Wick', + email: 'john@silverhand.io', + email_verified: true, + picture: 'https://example.com/image.jpg', + }, + }); + }); + it('throws SocialAccessTokenInvalid error if remote response code is 401', async () => { nock(userInfoEndpoint).post('').reply(401); const connector = await createConnector({ getConfig }); diff --git a/packages/connectors/connector-google/src/index.ts b/packages/connectors/connector-google/src/index.ts index 19c8fd9a273..a01e506368f 100644 --- a/packages/connectors/connector-google/src/index.ts +++ b/packages/connectors/connector-google/src/index.ts @@ -11,6 +11,7 @@ import type { GetConnectorConfig, CreateConnector, SocialConnector, + GoogleConnectorConfig, } from '@logto/connector-kit'; import { ConnectorError, @@ -18,7 +19,9 @@ import { validateConfig, ConnectorType, parseJson, + GoogleConnector, } from '@logto/connector-kit'; +import { createRemoteJWKSet, jwtVerify } from 'jose'; import { accessTokenEndpoint, @@ -27,34 +30,37 @@ import { userInfoEndpoint, defaultMetadata, defaultTimeout, + jwksUri, } from './constant.js'; -import type { GoogleConfig } from './types.js'; import { - googleConfigGuard, accessTokenResponseGuard, userInfoResponseGuard, authResponseGuard, + googleOneTapDataGuard, } from './types.js'; const getAuthorizationUri = (getConfig: GetConnectorConfig): GetAuthorizationUri => async ({ state, redirectUri }) => { const config = await getConfig(defaultMetadata.id); - validateConfig(config, googleConfigGuard); + validateConfig(config, GoogleConnector.configGuard); + + const { clientId, scope, prompts } = config; const queryParameters = new URLSearchParams({ - client_id: config.clientId, + client_id: clientId, redirect_uri: redirectUri, response_type: 'code', state, - scope: config.scope ?? defaultScope, + scope: scope ?? defaultScope, + ...conditional(prompts && prompts.length > 0 && { prompt: prompts.join(' ') }), }); return `${authorizationEndpoint}?${queryParameters.toString()}`; }; export const getAccessToken = async ( - config: GoogleConfig, + config: GoogleConnectorConfig, codeObject: { code: string; redirectUri: string } ) => { const { code, redirectUri } = codeObject; @@ -86,22 +92,58 @@ export const getAccessToken = async ( return { accessToken }; }; +type Json = ReturnType; + +/** + * Get user information JSON from Google Identity Platform. It will use the following order to + * retrieve user information: + * + * 1. Google One Tap: https://developers.google.com/identity/gsi/web/guides/verify-google-id-token + * 2. Normal Google OAuth: https://developers.google.com/identity/protocols/oauth2/openid-connect + * + * @param data The data from the client. + * @param config The configuration of the connector. + * @returns A Promise that resolves to the user information JSON. + */ +const getUserInfoJson = async (data: unknown, config: GoogleConnectorConfig): Promise => { + // Google One Tap + const oneTapResult = googleOneTapDataGuard.safeParse(data); + + if (oneTapResult.success) { + const { payload } = await jwtVerify( + oneTapResult.data.credential, + createRemoteJWKSet(new URL(jwksUri)), + { + // https://developers.google.com/identity/gsi/web/guides/verify-google-id-token + issuer: ['https://accounts.google.com', 'accounts.google.com'], + audience: config.clientId, + clockTolerance: 10, + } + ); + return payload; + } + + // Normal Google OAuth + const { code, redirectUri } = await authorizationCallbackHandler(data); + const { accessToken } = await getAccessToken(config, { code, redirectUri }); + + const httpResponse = await got.post(userInfoEndpoint, { + headers: { + authorization: `Bearer ${accessToken}`, + }, + timeout: { request: defaultTimeout }, + }); + return parseJson(httpResponse.body); +}; + const getUserInfo = (getConfig: GetConnectorConfig): GetUserInfo => async (data) => { - const { code, redirectUri } = await authorizationCallbackHandler(data); const config = await getConfig(defaultMetadata.id); - validateConfig(config, googleConfigGuard); - const { accessToken } = await getAccessToken(config, { code, redirectUri }); + validateConfig(config, GoogleConnector.configGuard); try { - const httpResponse = await got.post(userInfoEndpoint, { - headers: { - authorization: `Bearer ${accessToken}`, - }, - timeout: { request: defaultTimeout }, - }); - const rawData = parseJson(httpResponse.body); + const rawData = await getUserInfoJson(data, config); const result = userInfoResponseGuard.safeParse(rawData); if (!result.success) { @@ -150,7 +192,7 @@ const createGoogleConnector: CreateConnector = async ({ getConf return { metadata: defaultMetadata, type: ConnectorType.Social, - configGuard: googleConfigGuard, + configGuard: GoogleConnector.configGuard, getAuthorizationUri: getAuthorizationUri(getConfig), getUserInfo: getUserInfo(getConfig), }; diff --git a/packages/connectors/connector-google/src/types.ts b/packages/connectors/connector-google/src/types.ts index 5837e568ed6..8141627bbbb 100644 --- a/packages/connectors/connector-google/src/types.ts +++ b/packages/connectors/connector-google/src/types.ts @@ -1,12 +1,6 @@ import { z } from 'zod'; -export const googleConfigGuard = z.object({ - clientId: z.string(), - clientSecret: z.string(), - scope: z.string().optional(), -}); - -export type GoogleConfig = z.infer; +import { GoogleConnector } from '@logto/connector-kit'; export const accessTokenResponseGuard = z.object({ access_token: z.string(), @@ -33,3 +27,11 @@ export const authResponseGuard = z.object({ code: z.string(), redirectUri: z.string(), }); + +/** + * Response payload from Google One Tap. Note the CSRF token is not included since it should be + * verified by the web server. + */ +export const googleOneTapDataGuard = z.object({ + [GoogleConnector.oneTapParams.credential]: z.string(), +}); diff --git a/packages/connectors/connector-huggingface/CHANGELOG.md b/packages/connectors/connector-huggingface/CHANGELOG.md index a64bffe3fd9..9a4e9d322cd 100644 --- a/packages/connectors/connector-huggingface/CHANGELOG.md +++ b/packages/connectors/connector-huggingface/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-huggingface +## 0.1.1 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + - @logto/connector-oauth@1.3.1 + ## 0.1.0 ### Minor Changes diff --git a/packages/connectors/connector-huggingface/package.json b/packages/connectors/connector-huggingface/package.json index b2bd8121bc0..b87196f02f6 100644 --- a/packages/connectors/connector-huggingface/package.json +++ b/packages/connectors/connector-huggingface/package.json @@ -1,11 +1,11 @@ { "name": "@logto/connector-huggingface", - "version": "0.1.0", + "version": "0.1.1", "description": "Hugging Face connector implementation.", "author": "Silverhand Inc. ", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", - "@logto/connector-oauth": "workspace:^1.3.0", + "@logto/connector-kit": "workspace:^4.0.0", + "@logto/connector-oauth": "workspace:^1.3.1", "@silverhand/essentials": "^2.9.1", "ky": "^1.2.3", "zod": "^3.22.4" @@ -52,7 +52,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@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", diff --git a/packages/connectors/connector-huggingface/src/index.test.ts b/packages/connectors/connector-huggingface/src/index.test.ts index e0b79e4904c..7c2ce197838 100644 --- a/packages/connectors/connector-huggingface/src/index.test.ts +++ b/packages/connectors/connector-huggingface/src/index.test.ts @@ -91,7 +91,7 @@ describe('Hugging Face connector', () => { nock.cleanAll(); nock(tokenEndpoint).post('').reply(200, { - invalid_filed: true, + invalid_field: true, }); const connector = await createConnector({ getConfig }); diff --git a/packages/connectors/connector-kakao/CHANGELOG.md b/packages/connectors/connector-kakao/CHANGELOG.md index 341fe977229..813a9f758ef 100644 --- a/packages/connectors/connector-kakao/CHANGELOG.md +++ b/packages/connectors/connector-kakao/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-kakao +## 1.2.1 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 1.2.0 ### Minor Changes diff --git a/packages/connectors/connector-kakao/package.json b/packages/connectors/connector-kakao/package.json index 4d41b6b817c..900f095e6c5 100644 --- a/packages/connectors/connector-kakao/package.json +++ b/packages/connectors/connector-kakao/package.json @@ -1,10 +1,10 @@ { "name": "@logto/connector-kakao", - "version": "1.2.0", + "version": "1.2.1", "description": "Kakao connector implementation.", "author": "Kyungyoon Kim. ", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", "snakecase-keys": "^8.0.0", @@ -52,7 +52,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@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", diff --git a/packages/connectors/connector-logto-email/CHANGELOG.md b/packages/connectors/connector-logto-email/CHANGELOG.md index 0742b6e2d83..2a7dbe906db 100644 --- a/packages/connectors/connector-logto-email/CHANGELOG.md +++ b/packages/connectors/connector-logto-email/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-logto-email +## 1.1.2 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 1.1.1 ### Patch Changes diff --git a/packages/connectors/connector-logto-email/package.json b/packages/connectors/connector-logto-email/package.json index 7466da480ef..d209481cfcc 100644 --- a/packages/connectors/connector-logto-email/package.json +++ b/packages/connectors/connector-logto-email/package.json @@ -1,10 +1,10 @@ { "name": "@logto/connector-logto-email", - "version": "1.1.1", + "version": "1.1.2", "description": "Logto email connector.", "author": "Silverhand Inc. ", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", "snakecase-keys": "^8.0.0", @@ -52,8 +52,8 @@ "access": "public" }, "devDependencies": { - "@logto/cloud": "0.2.5-38aae44", - "@rollup/plugin-commonjs": "^25.0.7", + "@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", diff --git a/packages/connectors/connector-logto-sms/CHANGELOG.md b/packages/connectors/connector-logto-sms/CHANGELOG.md index cdb1dcb483d..08b1d95aa47 100644 --- a/packages/connectors/connector-logto-sms/CHANGELOG.md +++ b/packages/connectors/connector-logto-sms/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-logto-sms +## 1.1.2 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 1.1.1 ### Patch Changes diff --git a/packages/connectors/connector-logto-sms/package.json b/packages/connectors/connector-logto-sms/package.json index 4b3b2ecc55e..00fa94c4f50 100644 --- a/packages/connectors/connector-logto-sms/package.json +++ b/packages/connectors/connector-logto-sms/package.json @@ -1,10 +1,10 @@ { "name": "@logto/connector-logto-sms", - "version": "1.1.1", + "version": "1.1.2", "description": "Logto SMS connector.", "author": "Silverhand Inc. ", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", "snakecase-keys": "^8.0.0", @@ -52,7 +52,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@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", diff --git a/packages/connectors/connector-logto-social-demo/CHANGELOG.md b/packages/connectors/connector-logto-social-demo/CHANGELOG.md index ba7962448b0..e641551e64a 100644 --- a/packages/connectors/connector-logto-social-demo/CHANGELOG.md +++ b/packages/connectors/connector-logto-social-demo/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-logto-social-demo +## 1.1.2 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 1.1.1 ### Patch Changes diff --git a/packages/connectors/connector-logto-social-demo/package.json b/packages/connectors/connector-logto-social-demo/package.json index 666c617ffc9..0e765182fbf 100644 --- a/packages/connectors/connector-logto-social-demo/package.json +++ b/packages/connectors/connector-logto-social-demo/package.json @@ -1,10 +1,10 @@ { "name": "@logto/connector-logto-social-demo", - "version": "1.1.1", + "version": "1.1.2", "description": "OAuth standard connector implementation.", "author": "Silverhand Inc. ", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", "snakecase-keys": "^8.0.0", @@ -52,7 +52,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@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", diff --git a/packages/connectors/connector-mailgun/CHANGELOG.md b/packages/connectors/connector-mailgun/CHANGELOG.md index 2820a1bb633..47a8407f9dc 100644 --- a/packages/connectors/connector-mailgun/CHANGELOG.md +++ b/packages/connectors/connector-mailgun/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-mailgun +## 1.2.2 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 1.2.1 ### Patch Changes diff --git a/packages/connectors/connector-mailgun/README.md b/packages/connectors/connector-mailgun/README.md index 4a493b884a9..72546840f26 100644 --- a/packages/connectors/connector-mailgun/README.md +++ b/packages/connectors/connector-mailgun/README.md @@ -99,4 +99,4 @@ The following is an example of the deliveries config: 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) +That's it. Don't forget to [Enable connector in sign-in experience](https://docs.logto.io/docs/recipes/configure-connectors/email-connector/enable-email-sign-in/) diff --git a/packages/connectors/connector-mailgun/package.json b/packages/connectors/connector-mailgun/package.json index 446d52b28ad..07cacc5bdb8 100644 --- a/packages/connectors/connector-mailgun/package.json +++ b/packages/connectors/connector-mailgun/package.json @@ -1,10 +1,10 @@ { "name": "@logto/connector-mailgun", - "version": "1.2.1", + "version": "1.2.2", "description": "Mailgun connector for Logto.", "author": "Silverhand Inc. ", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", "snakecase-keys": "^8.0.0", @@ -52,7 +52,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@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", diff --git a/packages/connectors/connector-mock-email-alternative/CHANGELOG.md b/packages/connectors/connector-mock-email-alternative/CHANGELOG.md index a1142990d04..c324ffcb4bc 100644 --- a/packages/connectors/connector-mock-email-alternative/CHANGELOG.md +++ b/packages/connectors/connector-mock-email-alternative/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-mock-standard-email +## 2.0.2 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 2.0.1 ### Patch Changes diff --git a/packages/connectors/connector-mock-email-alternative/package.json b/packages/connectors/connector-mock-email-alternative/package.json index d4207960959..9132e8f8530 100644 --- a/packages/connectors/connector-mock-email-alternative/package.json +++ b/packages/connectors/connector-mock-email-alternative/package.json @@ -1,10 +1,10 @@ { "name": "@logto/connector-mock-standard-email", - "version": "2.0.1", + "version": "2.0.2", "description": "Mock Standard Email Service connector implementation for integration tests only.", "author": "Silverhand Inc. ", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", "snakecase-keys": "^8.0.0", @@ -52,7 +52,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@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", diff --git a/packages/connectors/connector-mock-email/CHANGELOG.md b/packages/connectors/connector-mock-email/CHANGELOG.md index 4ad35a91007..9f0a121575b 100644 --- a/packages/connectors/connector-mock-email/CHANGELOG.md +++ b/packages/connectors/connector-mock-email/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-mock-email +## 2.0.2 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 2.0.1 ### Patch Changes diff --git a/packages/connectors/connector-mock-email/package.json b/packages/connectors/connector-mock-email/package.json index 66344682f55..f1b4024346a 100644 --- a/packages/connectors/connector-mock-email/package.json +++ b/packages/connectors/connector-mock-email/package.json @@ -1,10 +1,10 @@ { "name": "@logto/connector-mock-email", - "version": "2.0.1", + "version": "2.0.2", "description": "Mock Email Service connector implementation for integration tests only.", "author": "Silverhand Inc. ", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", "snakecase-keys": "^8.0.0", @@ -52,7 +52,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@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", diff --git a/packages/connectors/connector-mock-sms/CHANGELOG.md b/packages/connectors/connector-mock-sms/CHANGELOG.md index a72ac350823..db5aedaeb47 100644 --- a/packages/connectors/connector-mock-sms/CHANGELOG.md +++ b/packages/connectors/connector-mock-sms/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-mock-sms +## 2.0.2 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 2.0.1 ### Patch Changes diff --git a/packages/connectors/connector-mock-sms/package.json b/packages/connectors/connector-mock-sms/package.json index 09f82cbced3..1ae46035a40 100644 --- a/packages/connectors/connector-mock-sms/package.json +++ b/packages/connectors/connector-mock-sms/package.json @@ -1,10 +1,10 @@ { "name": "@logto/connector-mock-sms", - "version": "2.0.1", + "version": "2.0.2", "description": "Mock SMS connector implementation for integration tests only.", "author": "Silverhand Inc. ", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", "snakecase-keys": "^8.0.0", @@ -52,7 +52,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@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", diff --git a/packages/connectors/connector-mock-social/CHANGELOG.md b/packages/connectors/connector-mock-social/CHANGELOG.md index 57dcf6d62ec..44cbf2e1725 100644 --- a/packages/connectors/connector-mock-social/CHANGELOG.md +++ b/packages/connectors/connector-mock-social/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-mock-social +## 1.2.1 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 1.2.0 ### Minor Changes diff --git a/packages/connectors/connector-mock-social/package.json b/packages/connectors/connector-mock-social/package.json index 4a687efb762..902cecb18f7 100644 --- a/packages/connectors/connector-mock-social/package.json +++ b/packages/connectors/connector-mock-social/package.json @@ -1,10 +1,10 @@ { "name": "@logto/connector-mock-social", - "version": "1.2.0", + "version": "1.2.1", "description": "Social mock connector implementation.", "author": "Silverhand Inc. ", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", "snakecase-keys": "^8.0.0", @@ -52,7 +52,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@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", diff --git a/packages/connectors/connector-mock-social/src/index.ts b/packages/connectors/connector-mock-social/src/index.ts index b9c76058c35..77c24b645a3 100644 --- a/packages/connectors/connector-mock-social/src/index.ts +++ b/packages/connectors/connector-mock-social/src/index.ts @@ -2,9 +2,9 @@ import { randomUUID } from 'node:crypto'; import { z } from 'zod'; import type { + CreateConnector, GetAuthorizationUri, GetUserInfo, - CreateConnector, SocialConnector, } from '@logto/connector-kit'; import { @@ -17,11 +17,23 @@ import { import { defaultMetadata } from './constant.js'; import { mockSocialConfigGuard } from './types.js'; -const getAuthorizationUri: GetAuthorizationUri = async ({ state, redirectUri }) => { +const getAuthorizationUri: GetAuthorizationUri = async ( + { state, redirectUri, connectorId }, + setSession +) => { + try { + await setSession({ state, redirectUri, connectorId }); + } catch (error: unknown) { + // Ignore the error if the method is not implemented + if (!(error instanceof ConnectorError && error.code === ConnectorErrorCodes.NotImplemented)) { + throw error; + } + } + return `http://mock.social.com/?state=${state}&redirect_uri=${redirectUri}`; }; -const getUserInfo: GetUserInfo = async (data) => { +const getUserInfo: GetUserInfo = async (data, getSession) => { const dataGuard = z.object({ code: z.string(), userId: z.optional(z.string()), @@ -34,6 +46,19 @@ const getUserInfo: GetUserInfo = async (data) => { throw new ConnectorError(ConnectorErrorCodes.InvalidResponse, JSON.stringify(data)); } + try { + const connectorSession = await getSession(); + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (!connectorSession) { + throw new ConnectorError(ConnectorErrorCodes.AuthorizationFailed); + } + } catch (error: unknown) { + // Ignore the error if the method is not implemented + if (!(error instanceof ConnectorError && error.code === ConnectorErrorCodes.NotImplemented)) { + throw error; + } + } + const { code, userId, ...rest } = result.data; // For mock use only. Use to track the created user entity diff --git a/packages/connectors/connector-mygovid/package.json b/packages/connectors/connector-mygovid/package.json index 6caf2af0ae9..91ccf411e4d 100644 --- a/packages/connectors/connector-mygovid/package.json +++ b/packages/connectors/connector-mygovid/package.json @@ -3,10 +3,10 @@ "version": "1.3.0", "description": "MyGovId connector implementation. - A fork of the OIDC connector", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", - "@logto/connector-oauth": "workspace:^1.3.0", + "@logto/connector-kit": "workspace:^4.0.0", + "@logto/connector-oauth": "workspace:^1.3.1", "@logto/shared": "workspace:^3.1.1", - "@silverhand/essentials": "^2.9.0", + "@silverhand/essentials": "^2.9.1", "jose": "^5.0.0", "ky": "^1.2.3", "nanoid": "^5.0.1", @@ -55,7 +55,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@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", @@ -66,7 +66,7 @@ "@vitest/coverage-v8": "^1.4.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", - "nock": "14.0.0-beta.6", + "nock": "14.0.0-beta.7", "prettier": "^3.0.0", "rollup": "^4.12.0", "rollup-plugin-output-size": "^1.3.0", diff --git a/packages/connectors/connector-naver/CHANGELOG.md b/packages/connectors/connector-naver/CHANGELOG.md index 978f2a4c883..565c435eb0d 100644 --- a/packages/connectors/connector-naver/CHANGELOG.md +++ b/packages/connectors/connector-naver/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-naver +## 1.2.1 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 1.2.0 ### Minor Changes diff --git a/packages/connectors/connector-naver/package.json b/packages/connectors/connector-naver/package.json index 9202e4b1b4d..22a3e422816 100644 --- a/packages/connectors/connector-naver/package.json +++ b/packages/connectors/connector-naver/package.json @@ -1,10 +1,10 @@ { "name": "@logto/connector-naver", - "version": "1.2.0", + "version": "1.2.1", "description": "Naver connector implementation.", "author": "Kyungyoon Kim. ", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", "snakecase-keys": "^8.0.0", @@ -52,7 +52,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@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", diff --git a/packages/connectors/connector-oauth2/CHANGELOG.md b/packages/connectors/connector-oauth2/CHANGELOG.md index a1f37599889..6943b878a57 100644 --- a/packages/connectors/connector-oauth2/CHANGELOG.md +++ b/packages/connectors/connector-oauth2/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-oauth +## 1.3.1 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 1.3.0 ### Minor Changes diff --git a/packages/connectors/connector-oauth2/package.json b/packages/connectors/connector-oauth2/package.json index 4322537afe7..9fb22112f82 100644 --- a/packages/connectors/connector-oauth2/package.json +++ b/packages/connectors/connector-oauth2/package.json @@ -1,10 +1,10 @@ { "name": "@logto/connector-oauth", - "version": "1.3.0", + "version": "1.3.1", "description": "OAuth standard connector implementation.", "author": "Silverhand Inc. ", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@logto/shared": "workspace:^3.1.1", "@silverhand/essentials": "^2.9.1", "jose": "^5.0.0", @@ -56,7 +56,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@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", diff --git a/packages/connectors/connector-oidc/CHANGELOG.md b/packages/connectors/connector-oidc/CHANGELOG.md index c135a5550d1..8ff39c430b4 100644 --- a/packages/connectors/connector-oidc/CHANGELOG.md +++ b/packages/connectors/connector-oidc/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-oidc +## 1.3.1 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + - @logto/connector-oauth@1.3.1 + ## 1.3.0 ### Minor Changes diff --git a/packages/connectors/connector-oidc/package.json b/packages/connectors/connector-oidc/package.json index 85443167a45..bffa1bbc509 100644 --- a/packages/connectors/connector-oidc/package.json +++ b/packages/connectors/connector-oidc/package.json @@ -1,10 +1,10 @@ { "name": "@logto/connector-oidc", - "version": "1.3.0", + "version": "1.3.1", "description": "OIDC standard connector implementation.", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", - "@logto/connector-oauth": "workspace:^1.3.0", + "@logto/connector-kit": "workspace:^4.0.0", + "@logto/connector-oauth": "workspace:^1.3.1", "@logto/shared": "workspace:^3.1.1", "@silverhand/essentials": "^2.9.1", "jose": "^5.0.0", @@ -55,7 +55,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@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", diff --git a/packages/connectors/connector-saml/CHANGELOG.md b/packages/connectors/connector-saml/CHANGELOG.md index 3abfe03a9d0..dabc6162ef2 100644 --- a/packages/connectors/connector-saml/CHANGELOG.md +++ b/packages/connectors/connector-saml/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-saml +## 1.1.2 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 1.1.1 ### Patch Changes diff --git a/packages/connectors/connector-saml/package.json b/packages/connectors/connector-saml/package.json index 440defa1d65..7e84b900cf4 100644 --- a/packages/connectors/connector-saml/package.json +++ b/packages/connectors/connector-saml/package.json @@ -1,10 +1,10 @@ { "name": "@logto/connector-saml", - "version": "1.1.1", + "version": "1.1.2", "description": "SAML standard connector implementation.", "author": "Silverhand Inc. ", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "fast-xml-parser": "^4.3.6", "got": "^14.0.0", @@ -54,7 +54,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@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", diff --git a/packages/connectors/connector-sendgrid-email/CHANGELOG.md b/packages/connectors/connector-sendgrid-email/CHANGELOG.md index a6c4d6730db..421045fef2c 100644 --- a/packages/connectors/connector-sendgrid-email/CHANGELOG.md +++ b/packages/connectors/connector-sendgrid-email/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-sendgrid-email +## 1.1.2 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 1.1.1 ### Patch Changes diff --git a/packages/connectors/connector-sendgrid-email/README.md b/packages/connectors/connector-sendgrid-email/README.md index aa8c6b2b295..b35e1f87091 100644 --- a/packages/connectors/connector-sendgrid-email/README.md +++ b/packages/connectors/connector-sendgrid-email/README.md @@ -93,7 +93,7 @@ Here is an example of SendGrid connector template JSON. 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) +That's it. Don't forget to [Enable connector in sign-in experience](https://docs.logto.io/docs/recipes/configure-connectors/email-connector/enable-email-sign-in/) ### Config types diff --git a/packages/connectors/connector-sendgrid-email/package.json b/packages/connectors/connector-sendgrid-email/package.json index f57a58a1a91..58231c9eb00 100644 --- a/packages/connectors/connector-sendgrid-email/package.json +++ b/packages/connectors/connector-sendgrid-email/package.json @@ -1,10 +1,10 @@ { "name": "@logto/connector-sendgrid-email", - "version": "1.1.1", + "version": "1.1.2", "description": "SendGrid Email Service connector implementation.", "author": "Silverhand Inc. ", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", "snakecase-keys": "^8.0.0", @@ -52,7 +52,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@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", diff --git a/packages/connectors/connector-smsaero/CHANGELOG.md b/packages/connectors/connector-smsaero/CHANGELOG.md index 72813750cc4..fe9faafda9c 100644 --- a/packages/connectors/connector-smsaero/CHANGELOG.md +++ b/packages/connectors/connector-smsaero/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-smsaero +## 1.2.2 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 1.2.1 ### Patch Changes diff --git a/packages/connectors/connector-smsaero/README.md b/packages/connectors/connector-smsaero/README.md index 572d4697fd9..fe4720d8397 100644 --- a/packages/connectors/connector-smsaero/README.md +++ b/packages/connectors/connector-smsaero/README.md @@ -5,13 +5,12 @@ The official Logto connector for SMSAero short message service. **Table of contents** - [SMSAero short message service connector](#smsaero-short-message-service-connector) - - [Register account](#register-account) - - [Set up senders' phone numbers](#set-up-senders-phone-numbers) - - [Get account credentials](#get-account-credentials) - - [Compose the connector JSON](#compose-the-connector-json) - - [Test SMSAero connector](#test-smsaero-connector) - - [Config types](#config-types) - - [Reference](#reference) + - [Register account](#register-account) + - [Get account credentials](#get-account-credentials) + - [Compose the connector JSON](#compose-the-connector-json) + - [Test SMSAero connector](#test-smsaero-connector) + - [Config types](#config-types) + - [Reference](#reference) ## Register account @@ -43,7 +42,7 @@ You can add multiple SMS connector templates for different cases. Here is an exa You can enter a phone number 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). +to [Enable connector in sign-in experience](https://docs.logto.io/docs/recipes/configure-connectors/sms-connector/enable-SMS-sign-in/). ### Config types diff --git a/packages/connectors/connector-smsaero/package.json b/packages/connectors/connector-smsaero/package.json index fd87e00c55b..306eb38dbb3 100644 --- a/packages/connectors/connector-smsaero/package.json +++ b/packages/connectors/connector-smsaero/package.json @@ -1,10 +1,10 @@ { "name": "@logto/connector-smsaero", - "version": "1.2.1", + "version": "1.2.2", "description": "SMSAero connector implementation.", "author": "Danil Tankov ", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", "snakecase-keys": "^8.0.0", @@ -52,7 +52,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@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", diff --git a/packages/connectors/connector-smtp/CHANGELOG.md b/packages/connectors/connector-smtp/CHANGELOG.md index 2543676bcd0..999f63372ba 100644 --- a/packages/connectors/connector-smtp/CHANGELOG.md +++ b/packages/connectors/connector-smtp/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-smtp +## 1.1.3 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 1.1.2 ### Patch Changes diff --git a/packages/connectors/connector-smtp/README.md b/packages/connectors/connector-smtp/README.md index 025175c7ac5..1d659e83b59 100644 --- a/packages/connectors/connector-smtp/README.md +++ b/packages/connectors/connector-smtp/README.md @@ -61,7 +61,7 @@ To check "Sender Addresses", you can find the entrance on the left-side navigati 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). +That's it. Don't forget to [Enable connector in sign-in experience](https://docs.logto.io/docs/recipes/configure-connectors/email-connector/enable-email-sign-in/). ### Config types diff --git a/packages/connectors/connector-smtp/package.json b/packages/connectors/connector-smtp/package.json index 24c028f8aff..8fbd5f46869 100644 --- a/packages/connectors/connector-smtp/package.json +++ b/packages/connectors/connector-smtp/package.json @@ -1,10 +1,10 @@ { "name": "@logto/connector-smtp", - "version": "1.1.2", + "version": "1.1.3", "description": "SMTP connector implementation.", "author": "Silverhand Inc. ", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", "nodemailer": "^6.9.9", @@ -12,7 +12,7 @@ "zod": "^3.22.4" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@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", diff --git a/packages/connectors/connector-tencent-sms/CHANGELOG.md b/packages/connectors/connector-tencent-sms/CHANGELOG.md index 9e296ec2754..a0f03437d48 100644 --- a/packages/connectors/connector-tencent-sms/CHANGELOG.md +++ b/packages/connectors/connector-tencent-sms/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-tencent-sms +## 1.1.2 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 1.1.1 ### Patch Changes diff --git a/packages/connectors/connector-tencent-sms/README.md b/packages/connectors/connector-tencent-sms/README.md index a473705e131..a382d7615ea 100644 --- a/packages/connectors/connector-tencent-sms/README.md +++ b/packages/connectors/connector-tencent-sms/README.md @@ -72,7 +72,7 @@ The official Logto connector for Tencent short message service. 你可以在「保存并完成」之前输入一个手机号码并点按「发送」来测试配置是否可以正常工作。 -大功告成!快去 [启用短信或邮件验证码登录](https://docs.logto.io/docs/tutorials/get-started/passwordless-sign-in-by-adding-connectors#enable-sms-or-email-passwordless-sign-in) +大功告成!快去 [启用短信或邮件验证码登录](https://docs.logto.io/docs/recipes/configure-connectors/sms-connector/enable-SMS-sign-in/) 吧。 ### 配置类型 diff --git a/packages/connectors/connector-tencent-sms/package.json b/packages/connectors/connector-tencent-sms/package.json index e089a1c222b..a0da5a6bcae 100644 --- a/packages/connectors/connector-tencent-sms/package.json +++ b/packages/connectors/connector-tencent-sms/package.json @@ -1,10 +1,10 @@ { "name": "@logto/connector-tencent-sms", - "version": "1.1.1", + "version": "1.1.2", "description": "Tencent SMS connector implementation.", "author": "StringKe", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", "snakecase-keys": "^8.0.0", @@ -52,7 +52,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@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", diff --git a/packages/connectors/connector-twilio-sms/CHANGELOG.md b/packages/connectors/connector-twilio-sms/CHANGELOG.md index bafc7348638..71e7e4630f3 100644 --- a/packages/connectors/connector-twilio-sms/CHANGELOG.md +++ b/packages/connectors/connector-twilio-sms/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-twilio-sms +## 1.1.2 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 1.1.1 ### Patch Changes diff --git a/packages/connectors/connector-twilio-sms/README.md b/packages/connectors/connector-twilio-sms/README.md index bd9a9cd0d19..2d0db29ef37 100644 --- a/packages/connectors/connector-twilio-sms/README.md +++ b/packages/connectors/connector-twilio-sms/README.md @@ -62,7 +62,7 @@ You can add multiple SMS connector templates for different cases. Here is an exa You can enter a phone number 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). +That's it. Don't forget to [Enable connector in sign-in experience](https://docs.logto.io/docs/recipes/configure-connectors/sms-connector/enable-SMS-sign-in/). ### Config types diff --git a/packages/connectors/connector-twilio-sms/package.json b/packages/connectors/connector-twilio-sms/package.json index c42f2ce3766..976f3070249 100644 --- a/packages/connectors/connector-twilio-sms/package.json +++ b/packages/connectors/connector-twilio-sms/package.json @@ -1,10 +1,10 @@ { "name": "@logto/connector-twilio-sms", - "version": "1.1.1", + "version": "1.1.2", "description": "Twilio SMS connector implementation.", "author": "Silverhand Inc. ", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", "snakecase-keys": "^8.0.0", @@ -52,7 +52,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@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", diff --git a/packages/connectors/connector-wechat-native/CHANGELOG.md b/packages/connectors/connector-wechat-native/CHANGELOG.md index a8b63c8afc6..8a11df3eed8 100644 --- a/packages/connectors/connector-wechat-native/CHANGELOG.md +++ b/packages/connectors/connector-wechat-native/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-wechat-native +## 1.2.1 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 1.2.0 ### Minor Changes diff --git a/packages/connectors/connector-wechat-native/README.md b/packages/connectors/connector-wechat-native/README.md index 3c756b19447..1022e15ddb7 100644 --- a/packages/connectors/connector-wechat-native/README.md +++ b/packages/connectors/connector-wechat-native/README.md @@ -258,7 +258,7 @@ Add the following line to your `AndroidManifest.xml`: ## Test WeChat native connector -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-social-sign-in). +That's it. Don't forget to [Enable connector in sign-in experience](https://docs.logto.io/docs/recipes/configure-connectors/social-connector/enable-social-sign-in/). Once WeChat native connector is enabled, you can build and run your app to see if it works. @@ -494,7 +494,7 @@ src/main/kotlin/com/sample/app/wxapi/WXEntryActivity.kt ## 测试微信原生连接器 -大功告成。别忘了 [在登录体验中启用本连接器](https://docs.logto.io/docs/tutorials/get-started/passwordless-sign-in-by-adding-connectors#enable-social-sign-in)。 +大功告成。别忘了 [在登录体验中启用本连接器](https://docs.logto.io/docs/recipes/configure-connectors/social-connector/enable-social-sign-in/)。 在微信原生连接器启用后,你可以构建并运行你的应用看看是否生效。 diff --git a/packages/connectors/connector-wechat-native/package.json b/packages/connectors/connector-wechat-native/package.json index b249d33307b..9a68f2dc462 100644 --- a/packages/connectors/connector-wechat-native/package.json +++ b/packages/connectors/connector-wechat-native/package.json @@ -1,10 +1,10 @@ { "name": "@logto/connector-wechat-native", - "version": "1.2.0", + "version": "1.2.1", "description": "WeChat native connector implementation.", "author": "Silverhand Inc. ", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", "snakecase-keys": "^8.0.0", @@ -52,7 +52,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@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", diff --git a/packages/connectors/connector-wechat-web/CHANGELOG.md b/packages/connectors/connector-wechat-web/CHANGELOG.md index c30b52bafe5..f9206ebff28 100644 --- a/packages/connectors/connector-wechat-web/CHANGELOG.md +++ b/packages/connectors/connector-wechat-web/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-wechat-web +## 1.3.1 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 1.3.0 ### Minor Changes diff --git a/packages/connectors/connector-wechat-web/README.md b/packages/connectors/connector-wechat-web/README.md index 8881e014345..eb21d174768 100644 --- a/packages/connectors/connector-wechat-web/README.md +++ b/packages/connectors/connector-wechat-web/README.md @@ -77,7 +77,7 @@ Fill out the `scope` field with either 'snsapi_userinfo' or 'snsapi_base'. You c ### Test WeChat web connector -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-social-sign-in). +That's it. Don't forget to [Enable connector in sign-in experience](https://docs.logto.io/docs/recipes/configure-connectors/social-connector/enable-social-sign-in/). Once WeChat web connector is enabled, you can sign in to your app again to see if it works. @@ -133,6 +133,6 @@ Once WeChat web connector is enabled, you can sign in to your app again to see i ### 测试微信网页连接器 -大功告成。别忘了 [在登录体验中启用本连接器](https://docs.logto.io/docs/tutorials/get-started/passwordless-sign-in-by-adding-connectors#enable-social-sign-in)。 +大功告成。别忘了 [在登录体验中启用本连接器](https://docs.logto.io/docs/recipes/configure-connectors/social-connector/enable-social-sign-in/)。 在微信原生连接器启用后,你可以构建并运行你的应用看看是否生效。 diff --git a/packages/connectors/connector-wechat-web/package.json b/packages/connectors/connector-wechat-web/package.json index 4af7a603140..07d99ad9280 100644 --- a/packages/connectors/connector-wechat-web/package.json +++ b/packages/connectors/connector-wechat-web/package.json @@ -1,10 +1,10 @@ { "name": "@logto/connector-wechat-web", - "version": "1.3.0", + "version": "1.3.1", "description": "Wechat Web connector implementation.", "author": "Silverhand Inc. ", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", "snakecase-keys": "^8.0.0", @@ -52,7 +52,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@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", diff --git a/packages/connectors/connector-wecom/CHANGELOG.md b/packages/connectors/connector-wecom/CHANGELOG.md index b96f0187cdd..56e23dc3fb2 100644 --- a/packages/connectors/connector-wecom/CHANGELOG.md +++ b/packages/connectors/connector-wecom/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-wecom +## 0.2.1 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 0.2.0 ### Minor Changes diff --git a/packages/connectors/connector-wecom/package.json b/packages/connectors/connector-wecom/package.json index 61ec7c224c9..b06794e294c 100644 --- a/packages/connectors/connector-wecom/package.json +++ b/packages/connectors/connector-wecom/package.json @@ -1,10 +1,10 @@ { "name": "@logto/connector-wecom", - "version": "0.2.0", + "version": "0.2.1", "description": "Wecom connector implementation.", "author": "Dove fork from Wechat Web connector", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", "snakecase-keys": "^8.0.0", @@ -52,7 +52,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@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", diff --git a/packages/console/.parcelrc b/packages/console/.parcelrc index bdafd2c2bbe..eeade8d2890 100644 --- a/packages/console/.parcelrc +++ b/packages/console/.parcelrc @@ -6,7 +6,7 @@ "@parcel/transformer-svg-react" ], "*.{md,mdx}": [ - "@parcel/transformer-mdx" + "./parcel-transformer-mdx2.js" ] }, "compressors": { diff --git a/packages/console/.parcelrc.arm64 b/packages/console/.parcelrc.arm64 index 0bc273f8909..c31f639e17b 100644 --- a/packages/console/.parcelrc.arm64 +++ b/packages/console/.parcelrc.arm64 @@ -10,7 +10,7 @@ "@parcel/transformer-svg-react" ], "*.{md,mdx}": [ - "@parcel/transformer-mdx" + "./parcel-transformer-mdx2.js" ] }, "compressors": { diff --git a/packages/console/CHANGELOG.md b/packages/console/CHANGELOG.md index 379fa169d38..a07f5892216 100644 --- a/packages/console/CHANGELOG.md +++ b/packages/console/CHANGELOG.md @@ -1,5 +1,123 @@ # Change Log +## 1.16.0 + +### Minor Changes + +- eacec10ac: improve machine-to-machine application integration user experience + + - Display a role assignment modal to facilitate setting permissions for the newly created machine-to-machine app. + - In the role assignment modal, add a Logto icon to roles that carry the Logto Management API access permission, making it easier for users to select roles with Logto Management API access permission. + - Add a notification for machine-to-machine roles to guide users in using the machine-to-machine role by creating a machine-to-machine application. + - Improve machine-to-machine application integration guide. + +- 87615d58c: support machine-to-machine apps for organizations + + This feature allows machine-to-machine apps to be associated with organizations, and be assigned with organization roles. + + ### Console + + - Add a new "machine-to-machine" type to organization roles. All existing roles are now "user" type. + - You can manage machine-to-machine apps in the organization details page -> Machine-to-machine apps section. + - You can view the associated organizations in the machine-to-machine app details page. + + ### OpenID Connect grant + + The `client_credentials` grant type is now supported for organizations. You can use this grant type to obtain an access token for an organization. + + ### Management API + + A set of new endpoints are added to the Management API: + + - `/api/organizations/{id}/applications` to manage machine-to-machine apps. + - `/api/organizations/{id}/applications/{applicationId}` to manage a specific machine-to-machine app in an organization. + - `/api/applications/{id}/organizations` to view the associated organizations of a machine-to-machine app. + +- 061a30a87: support agree to terms polices for Logto’s sign-in experiences + + - Automatic: Users automatically agree to terms by continuing to use the service + - ManualRegistrationOnly: Users must agree to terms by checking a box during registration, and don't need to agree when signing in + - Manual: Users must agree to terms by checking a box during registration or signing in + +- ead51e555: add Ruby app guide +- ef21c7a99: support per-organization multi-factor authentication requirement + + An organization can now require its member to have multi-factor authentication (MFA) configured. If an organization has this requirement and a member does not have MFA configured, the member will not be able to fetch the organization access token. + +- 0ef712e4e: support Google One Tap configuration +- 15953609b: support the dynamic config rendering for connector multi-select configuration +- b52609a1e: add `hasPassword` to custom JWT user context +- efa884c40: feature: just-in-time user provisioning for organizations + + This feature allows users to automatically join the organization and be assigned roles upon their first sign-in through some authentication methods. You can set requirements to meet for just-in-time provisioning. + + ### Email domains + + New users will automatically join organizations with just-in-time provisioning if they: + + - Sign up with verified email addresses, or; + - Use social sign-in with verified email addresses. + + This applies to organizations that have the same email domain configured. + + To enable this feature, you can add email domain via the Management API or the Logto Console: + + - We added the following new endpoints to the Management API: + - `GET /organizations/{organizationId}/jit/email-domains` + - `POST /organizations/{organizationId}/jit/email-domains` + - `PUT /organizations/{organizationId}/jit/email-domains` + - `DELETE /organizations/{organizationId}/jit/email-domains/{emailDomain}` + - In the Logto Console, you can manage email domains in the organization details page -> "Just-in-time provisioning" section. + + ### SSO connectors + + New or existing users signing in through enterprise SSO for the first time will automatically join organizations that have just-in-time provisioning configured for the SSO connector. + + To enable this feature, you can add SSO connectors via the Management API or the Logto Console: + + - We added the following new endpoints to the Management API: + - `GET /organizations/{organizationId}/jit/sso-connectors` + - `POST /organizations/{organizationId}/jit/sso-connectors` + - `PUT /organizations/{organizationId}/jit/sso-connectors` + - `DELETE /organizations/{organizationId}/jit/sso-connectors/{ssoConnectorId}` + - In the Logto Console, you can manage SSO connectors in the organization details page -> "Just-in-time provisioning" section. + + ### Default organization roles + + You can also configure the default roles for users provisioned via this feature. The default roles will be assigned to the user when they are provisioned. + + To enable this feature, you can set the default roles via the Management API or the Logto Console: + + - We added the following new endpoints to the Management API: + - `GET /organizations/{organizationId}/jit/roles` + - `POST /organizations/{organizationId}/jit/roles` + - `PUT /organizations/{organizationId}/jit/roles` + - `DELETE /organizations/{organizationId}/jit/roles/{organizationRoleId}` + - In the Logto Console, you can manage default roles in the organization details page -> "Just-in-time provisioning" section. + +- b50ba0b7e: enable backchannel logout support + + Enable the support of [OpenID Connect Back-Channel Logout 1.0](https://openid.net/specs/openid-connect-backchannel-1_0.html). + + To register for backchannel logout, navigate to the application details page in the Logto Console and locate the "Backchannel logout" section. Enter the backchannel logout URL of your RP and click "Save". + + You can also enable session requirements for backchannel logout. When enabled, Logto will include the `sid` claim in the logout token. + + For programmatic registration, you can set the `backchannelLogoutUri` and `backchannelLogoutSessionRequired` properties in the application `oidcClientMetadata` object. + +### Patch Changes + +- 9f33d997b: view and update user's `profile` property in the user settings page +- 06ef19905: fix a regression bug that error toasts pop up in audit log when logs are associated with deleted applications +- af44e87eb: add Chrome extension guide +- 136320584: allow skipping manual account linking during sign-in + + You can find this configuration in Console -> Sign-in experience -> Sign-up and sign-in -> Social sign-in -> Automatic account linking. + + When switched on, if a user signs in with a social identity that is new to the system, and there is exactly one existing account with the same identifier (e.g., email), Logto will automatically link the account with the social identity instead of prompting the user for account linking. + +- d81e13d21: display OIDC issuer endpoint in the application details form + ## 1.15.0 ### Minor Changes diff --git a/packages/console/package.json b/packages/console/package.json index b99bd8554fe..cda9fc37676 100644 --- a/packages/console/package.json +++ b/packages/console/package.json @@ -1,6 +1,6 @@ { "name": "@logto/console", - "version": "1.15.0", + "version": "1.16.0", "description": "> TODO: description", "author": "Silverhand Inc. ", "homepage": "https://github.com/logto-io/logto#readme", @@ -27,21 +27,21 @@ "devDependencies": { "@fontsource/roboto-mono": "^5.0.0", "@jest/types": "^29.5.0", - "@logto/cloud": "0.2.5-38aae44", - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/cloud": "0.2.5-a7eedce", + "@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.11.0", - "@logto/phrases-experience": "workspace:^1.6.1", - "@logto/react": "^3.0.8", - "@logto/schemas": "workspace:^1.17.0", + "@logto/phrases": "workspace:^1.12.0", + "@logto/phrases-experience": "workspace:^1.7.0", + "@logto/react": "^3.0.12", + "@logto/schemas": "workspace:^1.18.0", "@logto/shared": "workspace:^3.1.1", - "@mdx-js/react": "^1.6.22", + "@mdx-js/mdx": "^3.0.1", + "@mdx-js/react": "^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-mdx": "2.9.3", "@parcel/transformer-sass": "2.9.3", "@parcel/transformer-svg-react": "2.9.3", "@silverhand/eslint-config": "6.0.1", @@ -51,12 +51,11 @@ "@silverhand/ts-config-react": "6.0.0", "@swc/core": "^1.3.52", "@swc/jest": "^0.2.26", - "@testing-library/react": "^15.0.0", + "@testing-library/react": "^16.0.0", "@types/color": "^3.0.3", "@types/debug": "^4.1.7", "@types/jest": "^29.4.0", - "@types/mdx": "^2.0.1", - "@types/mdx-js__react": "^1.5.5", + "@types/mdx": "^2.0.13", "@types/react": "^18.0.31", "@types/react-color": "^3.0.6", "@types/react-dom": "^18.0.0", @@ -64,7 +63,7 @@ "@types/react-modal": "^3.13.1", "@types/react-syntax-highlighter": "^15.5.1", "@withtyped/client": "^0.8.7", - "buffer": "^5.7.1", + "buffer": "^6.0.0", "classnames": "^2.3.1", "clean-deep": "^3.4.0", "cross-env": "^7.0.3", @@ -78,7 +77,7 @@ "eslint": "^8.56.0", "history": "^5.3.0", "i18next": "^22.4.15", - "i18next-browser-languagedetector": "^7.0.1", + "i18next-browser-languagedetector": "^8.0.0", "identity-obj-proxy": "^3.0.0", "jest": "^29.7.0", "jest-environment-jsdom": "^29.0.0", @@ -88,6 +87,7 @@ "ky": "^1.2.3", "libphonenumber-js": "^1.10.51", "lint-staged": "^15.0.0", + "mermaid": "^10.9.1", "nanoid": "^5.0.1", "overlayscrollbars": "^2.0.2", "overlayscrollbars-react": "^0.5.0", @@ -117,6 +117,7 @@ "react-syntax-highlighter": "^15.5.0", "react-timer-hook": "^3.0.5", "recharts": "^2.1.13", + "rehype-mdx-code-props": "^3.0.1", "remark-gfm": "^4.0.0", "stylelint": "^15.0.0", "swr": "^2.2.0", @@ -139,8 +140,7 @@ }, "alias": { "@/*": "./src/$1", - "@cloud/*": "./src/cloud/$1", - "@mdx/components/*": "./src/mdx-components/$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 new file mode 100644 index 00000000000..bd005897575 --- /dev/null +++ b/packages/console/parcel-transformer-mdx2.js @@ -0,0 +1,61 @@ +// 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/src/App.tsx b/packages/console/src/App.tsx index c2d8fdd49f4..96ff5ed713f 100644 --- a/packages/console/src/App.tsx +++ b/packages/console/src/App.tsx @@ -31,6 +31,7 @@ import AppConfirmModalProvider from './contexts/AppConfirmModalProvider'; import AppDataProvider, { AppDataContext } from './contexts/AppDataProvider'; import { AppThemeProvider } from './contexts/AppThemeProvider'; import TenantsProvider, { TenantsContext } from './contexts/TenantsProvider'; +import Toast from './ds-components/Toast'; import useCurrentUser from './hooks/use-current-user'; import initI18n from './i18n/init'; @@ -86,6 +87,7 @@ function Providers() { UserScope.Identities, UserScope.CustomData, UserScope.Organizations, + UserScope.OrganizationRoles, PredefinedScope.All, ...conditionalArray( isCloud && [ @@ -111,6 +113,7 @@ function Providers() { > + {/** diff --git a/packages/console/src/assets/docs/fragments/_checkpoint.md b/packages/console/src/assets/docs/fragments/_checkpoint.md index 2abf8cde580..49d2692bc90 100644 --- a/packages/console/src/assets/docs/fragments/_checkpoint.md +++ b/packages/console/src/assets/docs/fragments/_checkpoint.md @@ -2,5 +2,5 @@ Now, you can test your application: 1. Run your application, you will see the sign-in button. 2. Click the sign-in button, the SDK will init the sign-in process and redirect you to the Logto sign-in page. -3. After you signed in, you will be redirected back to your application and see user data and the sign-out button. +3. After you signed in, you will be redirected back to your application and see the sign-out button. 4. Click the sign-out button to sign out. diff --git a/packages/console/src/assets/docs/fragments/_experience-overview.mdx b/packages/console/src/assets/docs/fragments/_experience-overview.mdx new file mode 100644 index 00000000000..829bdb8d8fc --- /dev/null +++ b/packages/console/src/assets/docs/fragments/_experience-overview.mdx @@ -0,0 +1,17 @@ +import RegardingRedirectBasedSignIn from './_regarding-redirect-based-sign-in.md'; + +Before we dive into the details, here's a quick overview of the end-user experience. The sign-in process can be simplified as follows: + +```mermaid +graph LR + A(Your app) -->|1. Invoke sign-in| B(Logto) + B -->|2. Finish sign-in| A +``` + +1. Your app invokes the sign-in method. +2. The user is redirected to the Logto sign-in page. For native apps, the system browser is opened. +3. The user signs in and is redirected back to your app (configured as the redirect URI). + + + +--- diff --git a/packages/console/src/assets/docs/fragments/_redirect-uris-native.mdx b/packages/console/src/assets/docs/fragments/_redirect-uris-native.mdx new file mode 100644 index 00000000000..a9f73712888 --- /dev/null +++ b/packages/console/src/assets/docs/fragments/_redirect-uris-native.mdx @@ -0,0 +1,12 @@ +import InlineNotification from '@/ds-components/InlineNotification'; +import UriInputField from '@/mdx-components/UriInputField'; + +import ExperienceOverview from './_experience-overview.mdx'; + +export const defaultRedirectUri = 'io.logto://callback'; + + + +Now, let's configure your redirect URI. E.g. {`${props.defaultUri ?? defaultRedirectUri}`}. + + diff --git a/packages/console/src/assets/docs/fragments/_redirect-uris-web.mdx b/packages/console/src/assets/docs/fragments/_redirect-uris-web.mdx new file mode 100644 index 00000000000..5c4b30cfe47 --- /dev/null +++ b/packages/console/src/assets/docs/fragments/_redirect-uris-web.mdx @@ -0,0 +1,22 @@ +import InlineNotification from '@/ds-components/InlineNotification'; +import UriInputField from '@/mdx-components/UriInputField'; + +import ExperienceOverview from './_experience-overview.mdx'; + +export const defaultBaseUrl = 'http://localhost:3000/'; +export const defaultRedirectUri = `${defaultBaseUrl}callback`; +export const defaultPostSignOutUri = defaultBaseUrl; + + + + + In the following steps, we assume your app is running on {props.defaultBaseUrl || defaultBaseUrl}. + + +Now, let's configure your redirect URI. E.g. {`${props.defaultRedirectUri || defaultRedirectUri}`}. + + + +Just like signing in, users should be redirected to Logto for signing out of the shared session. Once finished, it would be great to redirect the user back to your website. For example, add {`${props.defaultPostSignOutUri || defaultPostSignOutUri}`} as the post sign-out redirect URI below. + + diff --git a/packages/console/src/assets/docs/fragments/_regarding-redirect-based-sign-in.md b/packages/console/src/assets/docs/fragments/_regarding-redirect-based-sign-in.md new file mode 100644 index 00000000000..8a1194e2e79 --- /dev/null +++ b/packages/console/src/assets/docs/fragments/_regarding-redirect-based-sign-in.md @@ -0,0 +1,9 @@ +
+Regarding redirect-based sign-in + +1. This authentication process follows the [OpenID Connect (OIDC)](https://openid.net/specs/openid-connect-core-1_0.html) protocol, and Logto enforces strict security measures to protect user sign-in. +2. If you have multiple apps, you can use the same identity provider (Logto). Once the user signs in to one app, Logto will automatically complete the sign-in process when the user accesses another app. + +To learn more about the rationale and benefits of redirect-based sign-in, see [Logto sign-in experience explained](../../docs/tutorials/get-started/sign-in-experience.mdx). + +
diff --git a/packages/console/src/assets/docs/guides/api-express/README.mdx b/packages/console/src/assets/docs/guides/api-express/README.mdx index 7c0b04da852..1a635c50f2a 100644 --- a/packages/console/src/assets/docs/guides/api-express/README.mdx +++ b/packages/console/src/assets/docs/guides/api-express/README.mdx @@ -1,5 +1,5 @@ -import Tabs from '@mdx/components/Tabs'; -import TabItem from '@mdx/components/TabItem'; +import Tabs from '@/mdx-components/Tabs'; +import TabItem from '@/mdx-components/TabItem'; import InlineNotification from '@/ds-components/InlineNotification'; import Steps from '@/mdx-components/Steps'; import Step from '@/mdx-components/Step'; @@ -28,8 +28,7 @@ To proceed, you'll need to integrate the Logto SDK into your client application. You'll also need to tweak the Logto SDK configuration to inform Logto that you want to request an access token for your API in this grant. Here's an example using React: -
-  
+
   {`import { LogtoProvider } from '@logto/react';
 
 const App = () => {
@@ -44,13 +43,11 @@ const App = () => {
     
   );
 };`}
-  
-
+ Once a user signs in with Logto, `isAuthenticated` within the Logto SDK will become `true`: -
-  
+
   {`import { useLogto } from '@logto/react';
 
 const Content = () => {
@@ -58,13 +55,11 @@ const Content = () => {
 
   console.log(isAuthenticated); // true
 };`}
-  
-
+ Now, you can use the `getAccessToken` method to retrieve an access token for your API: -
-  
+
   {`const Content = () => {
   const { getAccessToken, isAuthenticated } = useLogto();
 
@@ -75,13 +70,11 @@ Now, you can use the `getAccessToken` method to retrieve an access token for you
     }
   }, [isAuthenticated, getAccessToken]);
 };`}
-  
-
+ Lastly, include this access token in the `Authorization` header when making requests to your API: -
-  
+
   {`const Content = () => {
   const { getAccessToken, isAuthenticated } = useLogto();
 
@@ -97,8 +90,7 @@ Lastly, include this access token in the `Authorization` header when making requ
     }
   }, [isAuthenticated, getAccessToken]);
 };`}
-  
-
+ @@ -150,8 +142,7 @@ const extractBearerTokenFromHeaders = ({ authorization }: IncomingHttpHeaders) = Subsequently, create a middleware to verify the access token: -
-  
+
   {`import { createRemoteJWKSet, jwtVerify } from 'jose';
 
 // Generate a JWKS using jwks_uri obtained from the Logto server
@@ -181,8 +172,7 @@ export const authMiddleware = async (req, res, next) => {
 
   return next();
 };`}
-  
-
+ You can now employ this middleware to protect your API endpoints: @@ -210,8 +200,7 @@ To address this, we can employ role-based access control (RBAC). In Logto, you c After defining roles and permissions, you can add the `scopes` option to the `LogtoProvider` component: -
-  
+
   {``}
-  
-
+ Logto will then only issue an access token with the appropriate scope(s) to the user. For instance, if a user only has the `read:products` scope, the access token will solely contain that scope: diff --git a/packages/console/src/assets/docs/guides/api-python/README.mdx b/packages/console/src/assets/docs/guides/api-python/README.mdx index 621abc9275c..d166c36f691 100644 --- a/packages/console/src/assets/docs/guides/api-python/README.mdx +++ b/packages/console/src/assets/docs/guides/api-python/README.mdx @@ -7,9 +7,7 @@ import { appendPath } from '@silverhand/essentials'; -```python -"""requires-auth.py -""" +```python title="requires-auth.py" def get_auth_token(): auth = request.headers.get("Authorization", None) @@ -41,30 +39,22 @@ pip install python-jose[ecdsa] ### Retrieve Logto's OIDC configurations -

You will need a JWK public key set and the token issuer to verify the signature and source of the received JWS token. -All the latest public Logto Authorization Configurations can be found at {appendPath(props.endpoint, '/oidc/.well-known/openid-configuration')}. +All the latest public Logto Authorization Configurations can be found at {appendPath(props.endpoint, '/oidc/.well-known/openid-configuration').href}. e.g. You can locate the following two fields in the response body if you request the above endpoint. -

-
-  
+
 {`{
   "issuer": "${appendPath(props.endpoint, '/oidc')}",
   "jwks_uri": "${appendPath(props.endpoint, '/oidc/jwks')}"
 }`}
-  
-
+ ### Create the authorization validation decorator -
-  
-{`"""requires-auth.py
-"""
-
-import json
+
+{`import json
 from flask import request,  _request_ctx_stack
 from six.moves.urllib.request import urlopen
 from functools import wraps
@@ -105,11 +95,12 @@ def requires_auth(f):
 
     return f(*args, **kwargs)
   return decorated`}
-  
-
+ + +
- For 🔐 RBAC, scope validation is also required. + For 🔐 RBAC, scope validation is also required.
diff --git a/packages/console/src/assets/docs/guides/api-spring-boot/README.mdx b/packages/console/src/assets/docs/guides/api-spring-boot/README.mdx index 161da637afb..50b6ee27737 100644 --- a/packages/console/src/assets/docs/guides/api-spring-boot/README.mdx +++ b/packages/console/src/assets/docs/guides/api-spring-boot/README.mdx @@ -1,5 +1,5 @@ -import Tabs from '@mdx/components/Tabs'; -import TabItem from '@mdx/components/TabItem'; +import Tabs from '@/mdx-components/Tabs'; +import TabItem from '@/mdx-components/TabItem'; import InlineNotification from '@/ds-components/InlineNotification'; import Steps from '@/mdx-components/Steps'; import Step from '@/mdx-components/Step'; @@ -35,9 +35,10 @@ dependencies { Since Spring Boot and Spring Security have built-in support for both OAuth2 resource server and JWT validation, you DO NOT need to add additional libraries from Logto to integrate. - See [Spring Security OAuth 2.0 Resource Server](https://docs.spring.io/spring-security/reference/servlet/oauth2/resource-server/index.html) - and [Spring Security Architecture](https://spring.io/guides/topicals/spring-security-architecture) - for more details. +See [Spring Security OAuth 2.0 Resource Server](https://docs.spring.io/spring-security/reference/servlet/oauth2/resource-server/index.html) +and [Spring Security Architecture](https://spring.io/guides/topicals/spring-security-architecture) +for more details. + @@ -50,22 +51,20 @@ and signed with [JWK](https://datatracker.ietf.org/doc/html/rfc7517) Before moving on, you will need to get an issuer and a JWKS URI to verify the issuer and the signature of the Bearer Token (`access_token`). -

-All the Logto Authorization server configurations can be found by requesting {appendPath(props.endpoint, '/oidc/.well-known/openid-configuration')}, including the issuer, jwks_uri and other authorization configs. -

+All the Logto Authorization server configurations can be found by requesting{' '} +{appendPath(props.endpoint, '/oidc/.well-known/openid-configuration').href}, including the{' '} +issuer, jwks_uri and other authorization configs. An example of the response: -
-  
-{`{
+
+    {`{
   // ...
   "issuer": "${appendPath(props.endpoint, '/oidc')}",
   "jwks_uri": "${appendPath(props.endpoint, '/oidc/jwks')}"
   // ...
 }`}
-  
-
+ @@ -73,10 +72,8 @@ An example of the response: Use an `application.yml` file (instead of the default `application.properties`) to configure the server port, audience, and OAuth2 resource server. -
-
-{`# path/to/project/src/main/resources/application.yaml
-server:
+
+    {`server:
   port: 3000
 
 logto:
@@ -89,8 +86,7 @@ spring:
         jwt:
           issuer-uri: ${appendPath(props.endpoint, '/oidc')}
           jwk-set-uri: ${appendPath(props.endpoint, '/oidc/jwks')}`}
-
-
+ - `audience`: The unique API identifier of your protected API resource. - `spring.security.oauth2.resourceserver.jwt.issuer-uri`: The iss claim value and the issuer URI in the JWT issued by Logto. @@ -102,8 +98,7 @@ spring: Provide your own `AudienceValidator` class that implements the `OAuth2TokenValidator` interface to validate whether the required audience is present in the JWT. -```java -// path/to/project/src/main/java/io/logto/springboot/sample/validator/AudienceValidator.java +```java title="validator/AudienceValidator.java" package io.logto.springboot.sample.validator; import org.springframework.security.oauth2.core.OAuth2Error; @@ -113,8 +108,6 @@ import org.springframework.security.oauth2.jwt.Jwt; public class AudienceValidator implements OAuth2TokenValidator { - private final OAuth2Error oAuth2Error = new OAuth2Error("invalid_token", "Required audience not found", null); - private final String audience; public AudienceValidator(String audience) { @@ -124,18 +117,21 @@ public class AudienceValidator implements OAuth2TokenValidator { @Override public OAuth2TokenValidatorResult validate(Jwt jwt) { if (!jwt.getAudience().contains(audience)) { - return OAuth2TokenValidatorResult.failure(oAuth2Error); + return OAuth2TokenValidatorResult.failure(new OAuth2Error("invalid_token", "Required audience not found", null)); } + // Optional: For RBAC validate the scopes of the JWT. + String scopes = jwt.getClaimAsString("scope"); + if (scopes == null || !scopes.contains("read:profile")) { + return OAuth2TokenValidatorResult.failure(new OAuth2Error("invalid_token", "Insufficient permission", null)); + } + + return OAuth2TokenValidatorResult.success(); } } ``` - - For 🔐 RBAC, scope validation is also required. - - @@ -144,8 +140,7 @@ Spring Security makes it easy to configure your application as a resource server You need to provide instances of `JwtDecoder` and `SecurityFilterChain` (as Spring beans), and add the `@EnableWebSecurity` annotation. -```java -// path/to/project/src/main/java/io/logto/springboot/sample/configuration/SecurityConfiguration.java +```java title="configuration/SecurityConfiguration.java" package io.logto.springboot.sample.configuration; import com.nimbusds.jose.JOSEObjectType; @@ -154,17 +149,19 @@ import com.nimbusds.jose.proc.SecurityContext; import io.logto.springboot.sample.validator.AudienceValidator; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; +import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer; import org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator; import org.springframework.security.oauth2.core.OAuth2TokenValidator; -import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.security.oauth2.jwt.JwtDecoder; import org.springframework.security.oauth2.jwt.JwtValidators; import org.springframework.security.oauth2.jwt.NimbusJwtDecoder; -import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm; +import org.springframework.security.web.DefaultSecurityFilterChain; +@Configuration @EnableWebSecurity public class SecurityConfiguration { @@ -180,6 +177,8 @@ public class SecurityConfiguration { @Bean public JwtDecoder jwtDecoder() { NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withJwkSetUri(jwksUri) + // Logto uses the ES384 algorithm to sign the JWTs by default. + .jwsAlgorithm(ES384) // The decoder should support the token type: Access Token + JWT. .jwtProcessorCustomizer(customizer -> customizer.setJWSTypeVerifier( new DefaultJOSEObjectTypeVerifier(new JOSEObjectType("at+jwt")))) @@ -194,14 +193,17 @@ public class SecurityConfiguration { } @Bean - public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { - http.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt).cors().and() - .authorizeRequests(customizer -> customizer - // Only authenticated requests can access your protected APIs - .mvcMatchers("/", "/secret").authenticated() - // Anyone can access the public profile. - .mvcMatchers("/profile").permitAll() - ); + public DefaultSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http + .securityMatcher("/api/**") + .oauth2ResourceServer(oauth2 -> oauth2 + .jwt(Customizer.withDefaults())) + .authorizeHttpRequests(requests -> requests + // Allow all requests to the public APIs. + .requestMatchers("/api/.wellknown/**").permitAll() + // Require jwt token validation for the protected APIs. + .anyRequest().authenticated()); + return http.build(); } } @@ -213,8 +215,7 @@ public class SecurityConfiguration { Add a controller to provide the protected and public APIs: -```java -// path/to/project/src/main/java/io/logto/springboot/sample/controller/ProtectedController.java +```java title="controller/ProtectedController.java" package io.logto.springboot.sample.controller; import org.springframework.web.bind.annotation.CrossOrigin; @@ -226,20 +227,14 @@ import org.springframework.web.bind.annotation.RestController; @CrossOrigin(origins = "*") @RestController public class ProtectedController { - - @GetMapping("/") - public String protectedRoot() { - return "Protected root."; - } - - @GetMapping("/secret") - public String protectedSecret() { - return "Protected secret."; + @GetMapping("/api/profile") + public String protectedProfile() { + return "Protected profile."; } - @GetMapping("/profile") - public String publicProfile() { - return "Public profile."; + @GetMapping("/api/.wellknown/config.json") + public String publicConfig() { + return "Public config."; } } ``` @@ -272,12 +267,10 @@ gradlew.bat bootRun Request your protected API with the Access Token as the Bearer token in the Authorization header, e.g. execute the `curl` command. -
-  
-  {`curl --include '${appendPath(props.endpoint, '/secret')}' \\
+
+    {`curl --include '${appendPath(props.endpoint, '/api/profile')}' \\
 --header 'Authorization: Bearer '`}
-  
-
+ If successful, you will get a response with 200 status: @@ -298,7 +291,7 @@ WWW-Authenticate: Bearer error="invalid_token", error_description="An error occu
- + - [Protect your API](https://docs.logto.io/docs/recipes/protect-your-api/) - [Spring Security OAuth 2.0 Resource Server](https://docs.spring.io/spring-security/reference/servlet/oauth2/resource-server/index.html) diff --git a/packages/console/src/assets/docs/guides/generate-metadata.js b/packages/console/src/assets/docs/guides/generate-metadata.js index a0be81e5738..d1ca726d995 100644 --- a/packages/console/src/assets/docs/guides/generate-metadata.js +++ b/packages/console/src/assets/docs/guides/generate-metadata.js @@ -20,8 +20,7 @@ const data = await Promise.all( return; } - // Add `.png` later - const logo = ['logo.svg'].find((logo) => existsSync(`${directory}/${logo}`)); + const logo = ['logo.webp', 'logo.svg', 'logo.png'].find((logo) => existsSync(`${directory}/${logo}`)); const config = existsSync(`${directory}/config.json`) ? await import(`./${directory}/config.json`, { assert: { type: 'json' } }).then( @@ -42,20 +41,31 @@ const metadata = data .sort((a, b) => a.order - b.order); const camelCase = (value) => value.replaceAll(/-./g, (x) => x[1].toUpperCase()); -const filename = 'index.ts'; +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" ); -for (const { name } of metadata) { +for (const { name, logo } of metadata) { // eslint-disable-next-line no-await-in-loop await fs.appendFile(filename, `import ${camelCase(name)} from './${name}/index';\n`); + + if (logo && !logo.endsWith('.svg')) { + // eslint-disable-next-line no-await-in-loop + await fs.appendFile(filename, `import ${camelCase(name)}Logo from './${name}/${logo}';\n`); + } } await fs.appendFile(filename, '\n'); -await fs.appendFile(filename, 'const guides: Readonly = Object.freeze(['); +await fs.appendFile(filename, 'export const guides: Readonly = Object.freeze(['); + +const getLogo = ({ name, logo }) => { + if (!logo) return 'undefined'; + if (logo.endsWith('.svg')) return `lazy(async () => import('./${name}/${logo}'))`; + return `({ className }: { readonly className?: string }) => ${name}`; +}; for (const { name, logo, order } of metadata) { // eslint-disable-next-line no-await-in-loop @@ -65,11 +75,11 @@ for (const { name, logo, order } of metadata) { { order: ${order}, id: '${name}', - Logo: ${logo ? `lazy(async () => import('./${name}/${logo}'))` : 'undefined'}, + Logo: ${getLogo({ name, logo })}, Component: lazy(async () => import('./${name}/README.mdx')), metadata: ${camelCase(name)}, },` ); } -await fs.appendFile(filename, ']);\n\nexport default guides;\n'); +await fs.appendFile(filename, ']);\n'); diff --git a/packages/console/src/assets/docs/guides/index.ts b/packages/console/src/assets/docs/guides/index.tsx similarity index 92% rename from packages/console/src/assets/docs/guides/index.ts rename to packages/console/src/assets/docs/guides/index.tsx index a9a4f38a577..d83d09817d4 100644 --- a/packages/console/src/assets/docs/guides/index.ts +++ b/packages/console/src/assets/docs/guides/index.tsx @@ -12,6 +12,7 @@ import nativeExpo from './native-expo/index'; import nativeFlutter from './native-flutter/index'; import nativeIosSwift from './native-ios-swift/index'; import spaAngular from './spa-angular/index'; +import spaChromeExtension from './spa-chrome-extension/index'; import spaReact from './spa-react/index'; import spaVanilla from './spa-vanilla/index'; import spaVue from './spa-vue/index'; @@ -33,11 +34,19 @@ import webNuxt from './web-nuxt/index'; import webOutline from './web-outline/index'; import webPhp from './web-php/index'; import webPython from './web-python/index'; -import webRemix from './web-remix/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'; -const guides: Readonly = Object.freeze([ +export const guides: Readonly = Object.freeze([ + { + order: 1, + id: 'web-next-app-router', + Logo: lazy(async () => import('./web-next-app-router/logo.svg')), + Component: lazy(async () => import('./web-next-app-router/README.mdx')), + metadata: webNextAppRouter, + }, { order: 1.1, id: 'native-expo', @@ -52,6 +61,13 @@ const guides: Readonly = Object.freeze([ 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')), + Component: lazy(async () => import('./spa-chrome-extension/README.mdx')), + metadata: spaChromeExtension, + }, { order: 1.1, id: 'spa-react', @@ -59,13 +75,6 @@ const guides: Readonly = Object.freeze([ Component: lazy(async () => import('./spa-react/README.mdx')), metadata: spaReact, }, - { - order: 1.1, - id: 'web-next-app-router', - Logo: lazy(async () => import('./web-next-app-router/logo.svg')), - Component: lazy(async () => import('./web-next-app-router/README.mdx')), - metadata: webNextAppRouter, - }, { order: 1.2, id: 'm2m-general', @@ -115,13 +124,6 @@ const guides: Readonly = Object.freeze([ Component: lazy(async () => import('./web-java-spring-boot/README.mdx')), metadata: webJavaSpringBoot, }, - { - order: 1.5, - id: 'web-gpt-plugin', - Logo: lazy(async () => import('./web-gpt-plugin/logo.svg')), - Component: lazy(async () => import('./web-gpt-plugin/README.mdx')), - metadata: webGptPlugin, - }, { order: 1.6, id: 'spa-vue', @@ -164,6 +166,15 @@ const guides: Readonly = Object.freeze([ Component: lazy(async () => import('./web-php/README.mdx')), metadata: webPhp, }, + { + order: 2, + id: 'web-ruby', + Logo: ({ className }: { readonly className?: string }) => ( + web-ruby + ), + Component: lazy(async () => import('./web-ruby/README.mdx')), + metadata: webRuby, + }, { order: 2.1, id: 'spa-webflow', @@ -192,13 +203,6 @@ const guides: Readonly = Object.freeze([ Component: lazy(async () => import('./native-capacitor/README.mdx')), metadata: nativeCapacitor, }, - { - order: 4, - id: 'web-remix', - Logo: lazy(async () => import('./web-remix/logo.svg')), - Component: lazy(async () => import('./web-remix/README.mdx')), - metadata: webRemix, - }, { order: 5, id: 'native-flutter', @@ -241,6 +245,13 @@ const guides: Readonly = Object.freeze([ Component: lazy(async () => import('./web-outline/README.mdx')), metadata: webOutline, }, + { + order: 999, + id: 'web-gpt-plugin', + Logo: lazy(async () => import('./web-gpt-plugin/logo.svg')), + Component: lazy(async () => import('./web-gpt-plugin/README.mdx')), + metadata: webGptPlugin, + }, { order: Number.POSITIVE_INFINITY, id: 'api-express', @@ -270,5 +281,3 @@ const guides: Readonly = Object.freeze([ metadata: thirdPartyOidc, }, ]); - -export default guides; diff --git a/packages/console/src/assets/docs/guides/m2m-general/README.mdx b/packages/console/src/assets/docs/guides/m2m-general/README.mdx index 97be012e070..21392ee3b24 100644 --- a/packages/console/src/assets/docs/guides/m2m-general/README.mdx +++ b/packages/console/src/assets/docs/guides/m2m-general/README.mdx @@ -1,10 +1,12 @@ -import Tabs from '@mdx/components/Tabs'; -import TabItem from '@mdx/components/TabItem'; +import Tabs from '@/mdx-components/Tabs'; +import TabItem from '@/mdx-components/TabItem'; import InlineNotification from '@/ds-components/InlineNotification'; import Steps from '@/mdx-components/Steps'; import Step from '@/mdx-components/Step'; import ApplicationCredentials from '@/mdx-components/ApplicationCredentials'; import AppIdentifierSrc from './assets/api-identifier.png'; +import AssignM2mRolesModalSrc from './assets/assign-m2m-roles-modal.png'; +import AssignM2mRolesPageSrc from './assets/assign-m2m-roles-page.png'; import LogtoManagementApiSrc from './assets/logto-management-api.png'; @@ -19,6 +21,14 @@ There are two common use cases of using machine-to-machine apps in Logto: 1. **Accessing Logto Management API**: In this case, you need to assign a M2M role that include the `all` permission from the built-in Logto Management API to your M2M app. 2. **Accessing your API resource**: In this case, you need to assign M2M roles that include permissions from your API resources to your M2M app. +During the M2M app creation process, you’ll be directed to a page where you can assign machine-to-machine (M2M) roles to your applications: + +Assign M2M roles modal + +Or you can also assign these roles on the M2M app detail page. + +M2M app details page + @@ -67,6 +77,10 @@ The resource API indicator is in the pattern of `https://[your-tenant-id].logto. Before accessing Logto Management API, make sure your M2M app has been assigned with M2M roles that include the `all` permission from this built-in “Logto Management API” resource. + +Logto also provides a pre-configured “Logto Management API access” M2M role for new created tenants, which the Logto Management API resource’s all permission has already assigned to. You can use it directly without manually setting permissions. This pre-configured role can also be edited and deleted as needed. + + Now, compose all we have and send the request: diff --git a/packages/console/src/assets/docs/guides/m2m-general/assets/assign-m2m-roles-modal.png b/packages/console/src/assets/docs/guides/m2m-general/assets/assign-m2m-roles-modal.png new file mode 100644 index 0000000000000000000000000000000000000000..2ce64235f3569303e17c17626ca0369ea9a8af0e GIT binary patch literal 318569 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 z zg%-9Rdgf;N{Mn>9phKlfU&{yub-HI@xInVGWSik**CY+mID5TIKo2yyCsQd5VECI^ z+o##{6ax1tbhWQi_j$!U%Z2SZDC4mm;x5)8It3H0|F{=*wDD> zvH`RoL$>v-e3CMqTywC(FS>dWDE^m@s$y#L4oF_TUSV6{U110F3~I^3l*=W2^5)eJIsKSUWAr zi!aFka;z=Ovl6iZVB!D)-!P~GPYCLPu9vC>Jd(w3N60&ncy%k-v3#x&Bqb0#+RwVkPv!w2gAf6X6 zJi`O`c=Mu5dCkIsC4Y)DvFM!UI}f*vijSkX4m&{v4yiVHrK?HmTzwK zVPtOtg;r1v{?OCUV4-8M0bMIX22YUc%c&O z?L;d!lzQk$LiM0+nvrClRSAu&G_w>KjlB!s@6|iW8W>x-F?2^^xbZ7cN40<~8Lm)zB|PB}R8X93`1mz3fNcD0 z_qcw|T98+(XtnKHuToLisf5%(R(So#h;woALfr_Xms-BLEah$wS_y)wq&Kt-H-VA3 z4V|HFe+f*9d4V^SupRbbkP~0V;F7aKqP}k`w4oZUUOa!rS;U5JHY&$$7HPe{%7+U{ z1DI9ZjRx%gV`>_a45|esXQugZ+U%%`5;3Z`Shl)O&%?iR%Jzftt3Og3z3WVziQgCO ziRZ$3vTpUukq)DR|^C-9F(VLFohHg8`3wa<9R-bcl3I0g1<+zwPQq21Oo>O z0VzW=TSa9AVZSI;F{%Cz#|qGE>YKD~zt6+BbvhGy^N&)fLK7$>fU7-ZKs=y4f;FNY zHZ0;-!sE?#EK6qIuBKU+oW|2(AC39y?4`x(=Itp~CBO+8U4d?SY#8XEc>$zz!G8x& zqe@0d{7pVTN!IHlxn+3In$Co0?@h1=I1YC^DMvI_Xyz*Ihf_neFcVYfy7RoWjN{gt z^d{MN5;9?7nn+T3CHlesthYGBiqLj}B;Oe7HTM`M9k=prxJmKQxz7nypt{oYk2J!O zXCs&;?rbTWsS%dKmVoRO{>DFuEc``kPvP8&#~Kfo$>&!G@m?@l&zKK|QA7HESTt9Y z$at4U;4z7W=cOVP`wHvxvCPTIYxh?0kr8XV`1XzUBos`>!K8Z$#Wx+$C4$2G?G2gU z_RPtVt)dgMScha-h79~#s6OSHIFn92hmtemz(tdCs3B3x2Zw?!xd?r0zDV*#k_K~J zJ$yCB6kfWa!aBtV?W{7XvEx`hN^HO4X0vPvmn~3fD&Q)D8k_>bw=gFTi?P^EeE5GR~w;Jt6Zb8^oCv`>zzVr4E?t9`=}7lb^k8A+YPIfs?Y)8UUYL!c zhaV;I6f-F2*{Iz&Bv}!6RC2Z1fs!!csWn**rr$3& z3o;A|hz4eHbk9L*%7=%HhvvRKe>K;PYC#RNeRUytV5IkOaP-34v~+?lP}~a}X`a-M zT|;`{X-Fj`iX9M#tCH?)RfJT+mTPERwbrg0MaMFx}j6OqePgB^D$ar&6!q%6jLz|WumF+ zdXzWxo^Ult+<;t!Xgk!6jb30zO-B-6`?L?ywx|R={7H%kYbThPsnfxzlm=hL==Hsv zxOx#iX5xn5dkZN74C||?K6-DzkMWqc_t;4yQYnh9$;l5eCPo{^sh6G{=F`zO9iLvJ4TJp-Yv27qyv}7>=m=jTt_r*;f8>Hfwsy*bBjWz z>BcL&fJf8M(=tsslYh~49*Udhz+OGi;*!B$yObi|2wsaESGn@6)qV|Fzk26KUR^YL zje=~YF#usYHc|T`I>)HPF@}s`L~oJS23|SuWhMNeH{Qw4yXtpkA#aTt#{V792}3g^ zSzTEV>!g-%o34GUNqZ=1(lwFJLnJFf$2@J;GoTHf!pHN3S|mwI`Q4hwA`W2({jndw ziQ<^=RFz|G5y;oJTZ^lz6YBZ~du@}Zew ztqbjIXS(FW(Bn%mo^O%Nn~pCvgZRPvqMNZB`>7f2%c0>J=OXE6!k*5y4f$ToIPB|F zE4X494YH=mqsZfsSM|THZRA672Fa50e#j3BVdLP1n7TzB?ydJg(;~oIDEPRP4OK48}(e3dTV@n$rXb0&{h_@F{fh$|KFy{KDL2vXsw*Ce?7YC-=D;;YJs!4rtL;}+Cko*k zucaG^yHFZpYP{$;vXd?;#Ux02@QvQ2?*?c5?SqU-Z>PC{lqh(ih!9bi!NXEODG&X69U zro9%ga4?n!x*2@{+PgdB|3Y~_Z*P=x3pPe!Agg^JsNWMkyd{n3x7yXFPB4&DoHklf zSc697bSlH5(yl0oCnYx=Zx_mJCM8s&^JX%9Q~UD4DR8Swoonw&h1%t1Lch>B z?2kuM<7pYH2KeEP6|2g^sYvoW=VWTci8c4BdZ4R2f>4nLzD|&keW8=x)DNqSaJ}Mw z>?77hv@K5EoniJz@(@0UGJF!8XN2e3F9P&6-du_1ip|LY$9`tm6GJ{1OSE}LE7T{z zG1buroG(iGA>X&x9q=8LtXE|&!zMobLgpmIpw>LIf?>J+x@< zB3hk?zsU_#<5Z58!9q|Q0-vwmt`eTg`Lh{Be1}}k8w}Nrx-R3;BzwOu>7v%wc?74T zI1D!0i4WeFVfbwaYqVb@F0XEH@Fj(~!t@3{Mp{)|@m>{LzuJJ0oyml)=}uRjk>s^a z9SSbUve)nKcp2ml34pvTGH3L5I|@EcCAwqD@1vvYFWkTRWx1B0fpAS3Jo4%B`0>5v z!XWyv|DaMHFQU0Ks9ZjzrLhg*Ei}cY%quD@y);yd?w6}^0J}$N#7Jwp+uX{E)OEQ$ z`ARlG<1SEpQxR%vf%Dt*_JVpDKL{)X zVLt<&=P^Wni?f@ZgjpVP3McG$%?C~nM0p_fCj|=^MJDXGpBQ!>O?@wbB5iW1gBTjE zo3DHY8cxOpaO8{+SIA>>P7EtSW}Wy2;^MpC>aE^1{Ok@_tih$bDu_c*eyD;4gc8>x z>LIk|-h87-mP?<0s}fhC_4(~jj|l~5e=}Xn2HMDIbT8?3u4m6u6ERe`&5e4Z1mKqJ zz~I*z&X$bxizA=?^_~279cYTH#f4TFL=dk}>b=TnO{E1^!oS&Fv@6mbKjDyl zG8*`@ps+UJB0LMI+c~b<3)vT3k9yaFgyi_pKtA76RvSzr;S?Rlq?XW>gf`4OAs9Fy zDO84N-u`|(qC3a6V7~}u9bwc=aj{)1nIA+>OEU;6aQnAX7eT0v18q<@-JVm#&`uwm z{+&L#&eerlPQ^QE$b3XwM^vEu#PwhhZoa9XL2tB{rW?mjhnSYn_5sbC;`Vy>%BxTH z?Nj2L@$_%Kz==gruFvX`WTo3Z&~pj7*nThLuQ1N}wO8W&*-!FenAH}{kADXHP#VloKJiy5oz2~a$N`oI^$jM=r0440kLsz?0cbe z^KIj^^lu07W<#$-!WY4wHHjk@yDf>Ok4S33Rl*66M8N@234IgV*7@d^r2f(w?T)X% zw4PmPKKsIo_OIob+TgZ5dW5kPkhMR1sw?5Qkxo(1Lyw)ZEbe+sIN&~Xk5pw49xit- zfZ94&mNv&GYGVbFq`2u7;G!@^4V~aYn>V=BaB&r-#o_|zm`%_#Mvv;Z8!VyRSt~e_ z&+xycy+{3hOjD-os5XPsA^QaMvUxVwL{~wyJ!mY{f@9P8wMF|z8OF}6D z>6UH~0coT=958CYKt-fm8tLv%X^@tVF`5B`NsSnT_kMoY_5J}|W4rHj&i9kYCEOAJ zY|`alw`H`HOT)bHe((!{vuc1C$ntTKQ>A#jco6}RS97$7sVrENvxD1tM;?AFq!#*S z@1g`ebb6$MJw|g>9tUVLH(~!rhW4K(E}myV7G@+l-#GgUYnKOL!`L#bhCXE!UMxl$ z8wT_xqHZK73U_ZbNLw^L{@529og4e<;r7zs+MG7Tkx*$jZ7=TE2@h~BRwUlE{4mKX zIEfUw8I*v)kykiiSR7xOo9uBH?KJVr@Eo-e+4pjvt+$w~fTKe4c#B3QZ!R`4cM~Ss z$#EZ`NE)x+LGAM6JCt{%{ni1AmOuFxs{)XTg^fxsSw3UUm~`2Y&Jvkgk(DXz3i+`9 zq|)iiIovjWU&{mpXBjV5&q=2BPak0f>;=6@Frsz*q`>We!hefGMSvr)6UzC$FMaC! zJj0kia6PPnd4lk8X3p4=CkOr*0{=OwK09x}+-f=pH+{q2mHWt@OL0G3J=^h1gjh2~ zNY5j8Yvw<;tIc8Ikl#n>%`(Dxa?~I0^g}(H*#Clb>h@PCD*nQy1&iXFk{@~wcbeAO z<<(?&^k`BQ-Ru548)|#mFa~IU?QRDDDRg})u75;WCS0423e5I=Z8(XAk>)3ZxzVYm z745ULkwy(hz<#XWW+E1b1!sdKD6a58Z_qta)Z&~vuVGA0bYdce||0QpU)-E=W!5R*YXLEIcDC^m7ghoenz>$0@ToRGCWZG;{y8&gY{`v}~MY?F<*6!(@zC(cou;N_J_x z!=hJXr2LM^E>ufgQ{=3!+bV(Vqi6t9;d6roc_xJO=z(8m4*L4U?6xKsy_gE93vbd~ zn{`i`^Y?4%kyCRG`ta$;w1FRH4Q)-*PGOh^tYhwf}pfGxIq;BUj6(&gF^HbN8r2>D<2nRTQW* z5$7}ol|BDb0JbQ!7h>ao1{fSDLU(+2%wp1{^({UUSMI~kSZ-b*zSX-18U@}1D;Exr zoFga@A9c+NA28MVL)vEQ(<@FAq@^w0vaGEu?rS)@uaTu63%YvRW3A~35iXx6Am}2*W1XSjnct^gQ z`)(molsbp;P*3ZS)P^J1jLvFD=N9h&_if1e zsFxVZ9Q(?Q{vfG&MsR7~;PA&p2;BE49}a$dzJy2_uDpP(LEeIY|0fyH2h4YX~9til73dseDD{LhFt*lwW-^(`S5A+cnmSOLJ0z(VVJ$s zCjwj>w)7ehufS@0odw{}qw7d&=OVn8WiWuWI_E@6Y+SU0H|qd(U``iF_xmA7g_Yud z=$QQ}gvCV902;a6(a4Uu0>ob9A>)7Jfs5K0z;zov_5c6|1>j@@h3{F=l}j&CNJgk$ zH(=lyJ%GSx#MRr38fL+PS&|rIl9H@$2{bKo*VuYQYyv<7fXZ;XZRmUyv zahC#!OC(euZZ10)a(4i}Tp&mU*O1Lmb8KlYb5;wK%)pz2oTd1Xf1J1uyhYF1EFD0A z8Pu`0vIApSJ7DU!(AzZoE3Mwe;HtB=NQJGbPJ>GoyDzJ(82NIpFC}R!Dc2hLE!&0L z{?-GQ=V{ZZeyjg?%PiT8PwJVPYXx45i$}{NDxs}XGNg)4^8*$X{``pb9_Z%$WXyIJ z;g)MahBU%Km$uJGTB)N`Q~wKh=%9xy>pttWJ-gLP3B237+$K7W-%>zx`^$Xq1N_w3 z5q_5H!Z32(LC2@1_PiA_CeX-r)z~m#vbuY6Y$oJdvA+VJj@#T$JJ8vG#MR(^BDq_* zRDc+jh`!7yUBV}K;yugVlg*lmbY>wxS&VS8p>;py9TmczN%Uz=>HV>0nXPkuZCJSY zyX}?jl+cB-g}Sm5WP>HVK}P7yUo@gV)3qff15~wPV4HSHVK9B8@#NK*Vb#y1@uJ+b5|Y+bjRel@e_kBMhpR1$Gs zx@g9v+y#4Xff8*Y*m5b$sg5V$oGPWC@F>|!+YGiyhVbj1ZC}wXCF8DyFa?Dgbx=YN zsOJ8(^GRGo5|U zCn6Nn0dHGcQ(rk-`W&1IIWg@6j6~z;z#z&_Q7H8-TnIS4rPk!cSbWGM-~R2n*RoK8 zwvN^u9UJAZgx`9=h4qt6y?ns-g&NK`JRbMT{@)9b*8;qjPW7JQV8(+Gy2TG>MIW0i zGuGd@%Fr{IoxaIlQ7-K;c2>gXgXt*-P#t20_2Q$3*`d@wE)b5lwZtD5dKX5%C|&u# zT?)(_zdhA~CdVyN$!yE+KqOo`7FbQ|P^TSx2p5w*DUXV^D`Ln>_PE`2zC!sH3wJoM zP-YzkNqzFH7dD_*-f>{=LQi{Y0oku)7@vWsXG5Z)%2{(o2r=YD&*!H|`5i!m_H2Y! z4Mx837K>2DoGt*?r;#>^ZD+JdvFT;Lq{=y89nM~}p6kb!tB14jxj0wFQ9?@2G8m425_Qj4uC1Pf!GS!#Vk??6kOyUckC}>D*!tv0-8!;#4-Wr@@CzDGx&T zK44oM_8gw@!>1Ykd-kKm-&*%kfvDy}Ay|d* zu5faIu*|?)oRMb{?i#+VK6hIZ?vVyL-2FuiUt;%t98HG&JZ=)dj}>J-s2i@p(4~(Z zz^lh+T4xXFGL^HY?4S;}jR>DYp|FZqn6o6alryasrYU9eR@2LM} z@g*ncojV7F8O%|15{srwbb)_|M#Bzu#*lXCt;YdZl>D9!-Z7?BCYAelZ6dAaTE|S@ z-{k3N3IUY(ovDU7z}hV6lR2io9VrRq3jk-z3z(_%y0|Nl%|O^w)}o~$ARs66RFpTs zZdI0x8{_$|T-(dHv8)O`jJcZI1D&q8D)RC7#s-zah z(_J>7DP_iX%d26Y6W5yk{_=vQ55~qh!+k1Jp0cQs+Hu|tfSWeEawy9&xr6(N>pH(;B&+K(KgqF2U^=e}|v5Oq`8X){aS3 ziX8sR*_}S0_oMe?Oz&ul5ApqMK)5S@g+aS+tSbmLJwo^0{!?tl?PLbRGP+IC@l(i0 zEF>9IqZGTT6#*w)P8Rv(%H4&bM7&}iNORYp`TM$ro{`Vq^C?r{TAIO=eYZ8(%J7Ri z*%E!IDOznb4a3O;s|eSfS;B}NO#44oIhq!ox$j|V$ghmNpW3>R8g)VzX!s{!dpLcv z%!km!xzeSeDDo9yL0%e0XG-98b8)Pd@1Xq9{l=(MQBQu$edoqn^ykX^CK3BDbu_P9 zeP>2N>d&PDt`)y3yRqDykiGOp$`F2KI$cp7Rf^T{E_%ERpEdvj4||U`8?RWKvn3U9 zX1rQLD zs7?&q{9aYM&UHdi6I~4_@_@$qpRL3z>6vq~f=*GiCo|<=&-?@3&R^bpHbusB>UQcU z_UHBpTv?V6VkW@BN^^yi(f78fqxB+AJxsZt_gpL7<%%wFeaOq^E=%LIRB7}Shw5Kv z72Yh!4L%ARl=xvxmcIqrl)D=<Of>xd|*PxoW7~3_> zX?wT}U-C~&VF4^E72~Cymsre_5ZjnQ~XHMU+@V zs{kyv^HcTntCU=4oxqYC0M~HW?!ZKo)6XuNf+Pa2R(MM1+{>yli(F%j&cSwT@*_Vu zDlHIOhgZZIw@5}2_@)I4>{wb3up?*9MezJ+R*iUjMjt9juQ~0yGzYed zV*3#~HdVR1EYf&eBo8t{Of_dMmd8bjA|?}B58I$X{otAechLcsE=mNu3{H#RaS=1= zERx(?9LsQSZE8@}7c>s!56D3-_)ed+XDL`);zLR7A72Iy zXC>&oz?|`t_}5#=RN)e2)R8qB6(Vpb@aD5V_fT=|n8+NRYdvDuGHbSWwL5&?ce?eP zBY@HB6FPEF9?Vr32OA8K)H7%3b71y*W|D_hXChnmCObVPe|hP@5^X>lzJ(s-c$aWl z(f^t7$&qB{nP%hgsU+>#-6vAdr~prXCAVJ~w=10yw16vVGk?zq7*O+~{nb z#-q|`9}yUolDeU53W8@)myGGU9)1FIDXx@A8AC!IJJTDC_PpN(B8V9hJC&vL`^u20 z?A@6e7tayhG7nndDbdG4-zZf}to6pX>6kd)uLMZHyuJ|f3i{ceSio}^mceay&fyV| z1C|sDZf>SGvHRqQ%PXD-0xvFxP9nP%Js#@6_Fwwc9I>V?c%^7O1R}Tfw4!rMUZJ!j zZI3voPybCui3#`>f@R>z?##1O=X4$&g)g|9r~i84_Q0N5ik(vsPr6^ugLHFefoG9a zTH4`wEj9|1mmy=k$x0?>&eb!5+i@BhC?clRa!vlhxahGCDdKo0~(1tT5S?Or#5nQ{0R9 zaQP(&gg0=+XJMa*Fto*%1y`gd(L42E+EM|_G`}}e&`NpYL*R^%8ih;vErwS9Crz4V z6%->qAID1y{by<)9cw+L@(1GEztf|xOh)VG^Oroi6mqgW_Uye@)9;IIXY4hjQ(FLRMP4!EY=p=rUf}F(Brny+0#)ARiPyTj9Yf+>en#Au~ zeOEoTbxooGrla!%bsqK7=1X!xk=)`H?CGxK)2-BQ?Ws+`cy{rF3`vE51lS%0HGbuI z2mI?27TeRJ7~k#vHJd*f05m6LSR*{{w$6267he?ceDEYK#`#QTMxF4C z0DcGavG&zqa-rB$oiKS5^F7)xGdbrwyZ0L!hNBz+T-D2&iDXh8(3|40u~lVP$!_)4O`An~EkGetxNp=4qGug`^;x&#H(xTymClfH z!J`#Cbye+0e`|LnYU`%j@LEnZv4vi_)GRG`{{n#~{^MI)H0N(rNri+4_GNxVgm!2;{Wy{83{~ zIlJvRQMWpj*l{xOL!X)Lcan3(rIzvAj`2zOp7EiRU$fL3z}@Z8kvf8zr&clQ+j05B z-BzKVcx&to!^^~0P+8U{jdeHH+vd*aJ8Mw~VivPHb)HP+&=5RY6f>6Yi?r>C3~J50 zD^r_|d?1CIy+?B!z&KT0t>>t)7qtO+Ex0H9y`f0yB+N({#NF6q2o$TBA(Q$ zhdb@Z2o4%_C}?(2b++LIC#>Zz9H%Vda_z0Hvevvkn#Lo?iU!}vPp0JQt=$!wm~~!C zxqL)1=qruYVxtWe)nM;*qDFDTK5`?UriD4~a6}3Uk z#xefoWkFf%Q#O@VP{U-a=@jcs$=sVI%Mp`VlkF@Mh_$gfM^=C#iq62O;pIcH{mkA> zfoEF7r<#dH!foD7(0F`UcIn59n_fa~a4dAV!P(|t45N0JCSG(xsa6+iv7W<;`lna| zsXO+&H1jt3W??cCHvjcSI-h6BLY;l>^tcro26`L?#b~cw?U&&T2TGa7>Zq)JAVp#f zkoxJ{C>=M^i6;^E1OliK$2ru6)uDTJqjgr7?U3|#ZJF70zO{JkUG;`hhL^Jykz?;d zW9l};;1B*$(k!(5o+s7+>@c4u`B!7TFUaq3YNu28vHJU2EUy;Wp++A_%jf>{ z1yxTs+3PSm;%1*>(gRq2@yTQxVY`+C_I7R63%O15ZtCHUvMuHh=mI!H+rZdF!#xrk zdgy62P&Kt6cG#krIYicU-HT~-M3~Nk=IET3I%F{i5mH94yGmYGg-Y}Bnqb#hCVDo2 zvvw1~AS~SZcErKy>YUzOA)1o;rDG$VgjJ+m*8?|6*_0mYmqYSWaUz5BEWc9)!cgP# z;pC4<`>&7ggKEFMv$Fjmjh-(KCo$3c+S9=D!(zl;Nb50mhy4k8Y)8X>4Q+^jX5a>A z%gzb_k}JJB05c2qQy>EL;xTC3qMTRAJg%JlgxHs|CmMUhh1e^_?{VIU4x+&=aVk>F%bfEj)2GXG8n^TV>SQ%unvaG-2&# z36wsd()}vd)fC>m@B5?MtG|Z1cfRI?q(H4JW-oO#c7*JNw=r}P<7vD>vlI6POtNg@ z2t34WM3Q+W|4mgjHe$0EYIl@^1RADWB^QDoYQJkamn6JMmxO*pF<)@901^P^&0#=# z*8D3F<#h}Zkl~BH>l$YDAwBOyKf)#9SRo)~%n!3YP1Go}q8CPN_T;X41>)OyE)7L> zW&0`Q*#9xL_2qp{G|r$ZJ- zf$(mgz=Y-lGKmE$q)K)cp69Mh+g8K(o`w8m6BkiWLV3zB%JGFivGm^T{zz-fXiIuB zE^Ste01~9BblvL5`f*2VqT+j0FTs3C%u%*%Jq(#;%n!^QR(y#&Xui2G``o<*!FFxV zkK)x{J#jp$dC#!ohBVz967SazT24OyZv*zzQuyWi#NQh>;DKR_*pYp0y~e0 zYsZ=1RG9%7EUtk2l#Y`t_N(=Hd2+Ji*B2J^L?d0;#ie~?lg3J8wJ1zCjX3UE_ixEZ zn4#IM{`;#WQMwPHfAfY&^YF*+(o74->o88>7Jo=n2`POGVBHWWT_;8QKZvWqqwDLA z`PH*oYpQIV$rxMbOmeZ{MUDEhftrxH6$xg(sT7hIrO40X>5$ugVL)8{+qUwYF=pt{ zcq4Wa=%7NYjiXHC!enM2fq#+_pYlM-iE{@c65Th)%m!6n8sVgW$LU`^2ZeHyCP-Pp zP)dtGyy~DmUsWDDN|S)#iz+ou>mvQJ4OS+fGfrpquAtmHc%oLcQL<^YyARUAdS0^! z`h#8a;S|^gYCM>cY{EE0J^CEWt;G>E;kLuOiEr>z#-YOYp>P#E=ut%$ZasPZTKx-9 z1*395m0sDX)GMRuZ^ra{7RON&*D@5_-A9F~(gU-)<$4kY{!+aWtq^x-Vow8u)@#tr zodrkX+;+@q3-ygf5~l%`0D?fFJ7781QL3tNKDCQ*Z}%OQfjqMxJnHw(X>embPurb9ue-nw%k`&2NK7H=e^< z;_?PdfBZ#yhw1kx8JaY8EF6r$X)o-P_~ONR%OE=bt*dH(dFtG=B1~7HhSTZ~c6M9} znE&$;8{|RgaTo%3ov4Z>sS1;Jdx4h#ks9*0Fe7;s(?Toi6(`U|Js(o?>~82T8PY1t zGSGxCfj3y9(0K#a@kz=S-SyKpP`=8vAPDs0Zr-72zj6B0+?R3qR#(IFX8cmv%LUSW z5eDiZtw`(mJ1|vw-~f7elk$i0ODh_%kb=o~`%wLCgoJ1Yb}~F0nZZlEb*~ z_6Z@JO7tp8`M=Z6H$=&2t7_rsE1&m0*-oJ+c(bXtHdjbS0uPiP;peiY$((BKWX>KN zCyph8bwHQA7}pPI8YL;Wp=Esc1TXNV2pUq_C9H(xq;?z{(F6)IN4;?3Y6VgcU(JwVn zUD-ceZeYzWwK-Vqxa;xo>$_jyd&AJ36lw4#gA=!d&r1LPZv9DXIivoWQfo8787`9| zi$Pnz{y;P*9x4|q&ZaC2ls%Ng4inwZ^BlX*4o`*}H z+hi_d-bMikp3cgPs6&a1s_8j}(_#gkL9@?8H9On}tD_cehBpNWVS zKr!*ft&1>{Da(A${+b^Bpd*V2Z?kxLd|mqJj}G`wx};@+(t0yliLkr9tHU}g$Y7o3 zdfCbEi6F7W@yCRFBIK|!TShI$rC_E?f>X`&gLnq~Ihk(Fyg~AQMnTh8hHoM1!hcU2 z;jG&5u&U29DzQhhhGS6_=`HH1f|~m#ZYwbhs;zOC>g3mTyZm{FPC3=l_xf0Pwocn(eh3`XK|H|*n_2|J*D`g6;ekYa;6WJ0kpy#+#>(Bf>$$ev%q1{z zduppwZs-}$?bf@XM#Pa4UE5C##hxW!g?g1ShJI&Wo`o|TWnN{Q`d{`X1Daq3=|RxF z(1?%v2MoHP46pvn4aMwu+N9U8F^KdS>Y>&E*@FZziUVA=k>ZAMNAU0QhACFO2Tt=% zyxTiJrDEC-0kdhJ(suuF6kB*wnO_3FF*f4!;BhoF|Kr`Kpi#+mL3O2P+i$v9t#VmG zFhgNFxYx#bfClRaHU@s}aJMYNUFFWR`S8S2y>PXcSv@WW%4?!$`{94pNI)6Q^qQdO zZwiJaPet(z#^G2i!|sfT(vQcT0 zTB>{x!Stsc&yaqv2~CzBh>FEeC8%R-fa6fEV-n+S7stak@D@*ZsqlN5L7Yl_ki%on zBE8(6W(;rw%dB~s8OLC@!9o~#pm{=YAEQO62 zJF)BwVnWP6)QpiG$X3&9N}Q<4#ZY2R`J5I8nx$!jBE(%WA_woNS-OFKMfv=JL%$|( z`j?1gkuI@n0*`)4L2F=4(tg=Qf#)0sUeR{y3Rs;!n*BGN@I~w-ZUpW6-93Zdx)2lngO#23bJd>=43B2&d*-t^Dw|nV@L8VXwZPFr_J2Qoh$6e&jh7?w z?(tTfDvNZ1Vnd)0JH>-1bO{VJ5QH@L`-l4I;CFm@qJ9pu<`{6p0n!z;G|&IwE*Wy3 zTUGD`NV?J|G-nWY{EV;w@wMY!0o7dVhgf-$gG0X@kn~5{MrTCkzmA@7(J`LO?L3G+ zQ_`U=20T${f6LG0s1H)043RN$_=`|f9THwxiKpr}WF4H)2J2>jZ7#d)WN`R6rBzaf zgx&eek8g>;8@w_x_N)9+z!a#hfjzi<&&puumO&4589&359T1EEOdK#!df4YLqadao zG}d`@lJ(t(!+PrG)YgEx%~FUGL@V$Fv|4UbgNe)D_U`gE-=F2OnMH}%9B;m=NtTU` zZjYuWcoTR7I_VDQQHbe_9cGW#-+!DFM{`Z!u^-RaG+p|77yk#7kZ(UV#eT{hMoWc2%yQ0J zu!7#5R+(h1x&FhNAf`Kv~_j$dqjs(Wy(F#FSJj}va=-%Rap%}JA@YD(bCDNWQ3W`YrP7mI2lt? zWF7BD&nWk=TfbA(F19^_kvxD#vx5KW1InnHY|3YeB_9aqqHD(<)+A}n6o7&k#lA64 zxYOY=PH;_L286}I(8?sRv@VN`%t6Xlji`@!q9<{wE;0lPPo{)r2qN5w4;oy2TkMu9 zGK4ps^mq`DE_d+95)D1~zJv*_(Em+wx}WY+@wSLUcy;iLTZW(#bl}g#YXZEM`mfXC znww3aY0R1EF<<`MHQObhF_bBkEY3~9aVCk=5pum2q?sV0CN@`YW_C4HpKLXYPuVEb zbyEAS3H*1K7ZYp>id?$rr;*$S~bd4z)j5DAEaiG8bBubQmhBNk)r>N`|gS<@RNIP7dugwV_J=0+@Eyw-V_$9oS1pBUgk2Wp~wa$Rag$`(teYBEtJy1li zKyyH;{4&`m{#M%+W}TAxP%&I8yfpc;%SWg`V5vdGI=QUeg7|m4HVpyjkF0s6$-6MG z-;#w-=V>|aW#HaG?N#6R-t+o;rxz@7vS;SLCxtzTG}h(f9%IMT%9G9J$7naZ5o)g_ zHsqzNkVvmCZzWvRbmacpRwz#Klb|MuYJ>vkG-#Y+}z8hv7Ig5 zJeE0mU=Yl?2<$x#1T_JAWR4;9utQAb`Sp(cOv%ibQ?C&4{0Z+NK()Abo@luAaG4mmmN4bu+y4( zV{F;488YbbE}H(`l|yL<_+^%jo8ogyf#9#x$_#5+f6;%za2&4>Ys2*Ms9U$*aKfVE zB3ijmME-D)xa9!o?nm2$6l7%T+vn@cEPX@lwvkn~27@PeL+-04wcsR-q-aGTEaNAF z_u#5IY)lgZ_ayQtkv>5$8G)KdhC9k~G9MDd4i)Tsna7>UTg>i@-fOF}$&QbNMP#({ z?ySo8I&v~R?(do#O zRrqsnC&F!SeVUnRF@QT3)j(h4cNP^#(YK4#7vqwWZg+8|47MWhkPP)dYB~zz|H!r2 zh-M#i-iurCi|YzoZV_{}AVtOqDec_HADi$y3}gtdD@1$Wf3mwfsQ4b8RK~QgwdOZu z#o|5ad8&gYQ+6>bjLNbyf1gYoCSpYVm#6iam~2C+s!Ke+EZ1bPO7@Awpqr50#6+V@ zJ%y5FOI)9idbZ_KdwnbJjp6YBDl(w?Q-#* z6h`QQdiLHU4C4d6!aNP`rgc2c7n<)ThWlY6N`7V9gEBC=1wJGoTKwlis(&%^dK>*P zT4q#k$G>^v?XXsEAT;jvn7LJC&{usmtn_EX9G)lwAu_F76aExNH6GZq_KiRccqeL! zgFd>cdXNM?aL*uG{;l8I86rn?TqZ;4LB0BKokKpK+=C?Bw_B3c!X+JR6hG6X{4S9! z9Owf6_WYZ1+}G?6t_webNB%*}*psod=EG>&D0OS#o;a{V_8QB_w=NX6s+ z0DyF3Y~#u2(Y|pzr~kYHUG2&n`<9-Fs@inUip7wwNF1o`leT25YHcJn%xxS_JJi!=Ys4os(ag49+~jXkyO}_I~bo z{rh4>yGxXf5jJ-IOKtlQCbxA^Qo4ov$5Sws{2#(mBAT6;-|xre=|227$1D`vpQta5dg|EiniBCR`gF>=&6kKc0pCL5N81HPu8+@2rH1f~Yi*oitP{;_ls{!^ zB8rSu?W5H=)O%|WIP@yi!lVlWfd_nxI${3IlO9(`dR&aRw)&eztlA>+bsQj2Gy^mF z9@n~^XJE2g{Byz;7K2`<2>`}E0V>m*r1yDi67X`G_C*{yteN=Kvrj0$x1{jf-mk%z z4Smet3E_KME(^(H<8Z#c!I3WPyyG)*&yT6D-=75pR=3!8y%r|}`v+pl<<|mvm5v;Q z*L$&W)y41ilE|;^T_WlQ{=l@5GaH+IPG*HpWTzAk`F`_L*gj795?+0W6o2=CRu#|A z*kdSPx&AH%!YJQiCXc`){V41iCqo9K{IXy+mnFWG&k(iHJ;CQ7_;|qS$RP_R*Cg~w zfoaJ7cR&0Hm4My7dyJY$I&GEasMI9w^?#eOGD;w1o_J;I@!%gvP=+AoTPwS4s8~t| zDi`i`$~-}7bo*~QsQuzRS^2;iWJb0?jLG%fn+YfIt)fh+qzF_U+)*P#M$C}qtE7!f z5jU8gA1<-Iv;OiZCkylVA?E(`*Lrd9%`kq#jwI8~=3U(hgs1S<^Fi-gf$nm{>7Hl_ zm%SO+m{x?Qd8gy-@#x`JtqfoLNw74c#?uI|nCAyWV3V;@wH3x--b##HvjW!n)_Jkv zv0OI7YsZACF@vIh8~_fn6P*`iV5|wGuWV^IbV#Q_DJVx(kH6z%Amm{2xujVw8~B{Q zjiU>kZqM#VYE>S%y|9y`WQ~~^^l$xA#{&x(Yc6n~vQf4+7+{*9)X7zhR#wUu8sQ=a zczqF9R;Gzz{jvl$JkeES)m9%>C*yRZitn`)51$*Tr>4R}9;n*qFc3Z>XI6}iBXV{1 zVG90ju#XO0l{uN78vpLAdrl{@xm=GZoYrF{I6c~6Q*x`P_8zgN5^`Z$2$GRzcWqsW{>q@h=_f;_R`ZYIxvE%{RLNWk7od|#@l664gB51` zqm6k2-fPnM#JA*ZJW-DL^0>$FonW<0tN4Y$)wAFK! zgc#AB)r+u=xKCC*1R75?rz^4Q+>F*z!(Fz!HP&B)rRl{zJ~AlM$cf~o`~5p&o{088 z`bzV8dBi>Q@+(kiGh~jsIEs_lPP8gFYRsHI|Dl*Ym1O0?Y5L6UN|=Z6^j_$bp-=I) zUc`sO1GZzSwCA-kg1|dQG0?sgm3*`I`BLcnsT{=vxb zIMBpePgm=(r3hV1MQ!g1I1ui7UeEIu*N(Q;3*gn(P$`yC;)#C@^WG2Y^?Al&S;Rn! z93*+bI5DuTUHt8Xv!6H!r#Xj+DZ~@)W#(JUA5MzLupzgzZUf%WQl*0vV$E;XSGzGy z`&Qzhj57Lj6Fak)vO=zetjcMEuz-OKK1A@m$jhJY`yvhC#ZyWjh3xSZel`XXp=INM zGXd>CJf;BXwjSnha^`*s2|5dYWV9Za=5oI{Z8wZ-_u1ZS%+K?OzLOo|Sy`LzEqJ2V zPi~F|ElJVQ)9jSvGPQL`VvHLJ_sTIzWyg)ehu`=16r?T}ZePyaHDYTk5AeAr&D}#E8V{sdKeF;G z10+3(m`STA@mobg^I=zzgxxAfeLcEuKl2Nos4c;F=EmDy4h#X=HQDa~>C-pgP=3vR zZK$1la>y@*Qs~j zL?(oYpa_G{$dISRu+vt^!I(ZMBMVRO!u`2lcTpul`3WJd%Z!Q~qIAShr2Aj=yNhYZUON}_`w<(FUZ`$^9d zVE#k|ei+q~UPftG{IfvZ;0gdyj}HU?9D+g5%k>kWdr}DOp&-InW}&{5V;PfMpaq;wwsx}2fks>>T=nZQa{=*-ZOw`hc2^$<29)HtDHprc>C6i zpYffN=i-DvH)-UuUpSl8edHxHl)-%}9jM@V_V6o{KvhNM$LjZFG%86VDPd3SO&cs& z7^gV&%UJ_?%$k1`?Y{QO45UORiJy}pxnnbxy5U>W0$6dN_O@nWM+Ok;7!sw=6}`|a z?c>l@7 z^nDGy!OcJPA0mZMm(JPZsrZD~Jja^Xo7O2&eSSx1@b-xMQcCYf`6vp2nV?9o=@j2F zw)Z>i>gzg7#*FWz1IhRgfGQw^v54uZ?StX4EE|YKnB}3wj`Ty87{Q_8X2PDoKh$AqR-q~4A@>7?9Dh#ll&c8{N5$|?W?mvcv5+{ zplbTkI0a#}v!qd@DRk)2eaRf)%6PL1fz=H$gpLB)d=}h* zai4nmU5M=5Tggz;po6)jIRY zH=|jJTFN!^1ch~`v|@kOh$Coc&~4l25;u?8pt;pqJPPVL!i1+aktVwib!~Dzh;m2+Ng^*siQg@tU0>B`@In65b~Q!o{fh zg=^G~t>V1GV2rmPHP0Z8vx<4BZ1zbeWZ5%1MroG>=`uv`$NNOb+0RJ`F|aacRKV6r za9Y1F8OvY+Os}RBu0yCb3Ql9YLo7B#yCI{tom!1LTzY5uj|c+*`L=~_LalXOTB^)66;fn=0VF~-PK>_N*nY7A zO~N>HXp&9}avCqk9jMpae;*Y_TB@$#xG{xlokI`n}o6 z`Ix2fd-!>GdV>*PnHMF>T0-^G`>Upf5@1o$lZ&_ zs6m{|mTcp6v~h);jfldsTcHpYS!7ZcH&3qz|bKz?mYrS#p9elRiJXDlWFo;P!@WYDzM!fMx#h) zbvG31_<+Igz(2>#ebzv+{`&TDVA!tH9Y6~=1$M>sJ6Tlqj>@cL8}N#Z>$-+$(Y`s; zz>t`Ip%wp9t1XM$l+I!@WT3{Ir1vl~Cx`SuF;IdzPs+ ze=g*LJ=tBZ_ag@50AB*G{_!>$+0ON&O#q`0aLq@HS>MWFl3In{gW6X~kR*04x4EXh zH;lU2E-1PK9Wb;9o(a&vhee4-CQB<0v89mV11nn)`xF_%r;%MNiOBeNP~iV;bH4bG zv{|iJ1`o`%!vAaD^)r%$|C)ph@vd&5SPep0=7}ittTZ1KaaKW)GcyRnWGBk~v)|4j56_7(LiS-ar9n^eb2eqbiPAR`(gLE;gS{oG@j zc%Y;4a@F64Sl+w@_#zS^;FDt3i7t%u(o6Pagdfmf(i_+XWs!c>Sl$j*HmaqNdlm9o zzam=Yzt_EZ4N#43#?pP>CHWDaetyo8QdThf@|9w&6kdydHyls{6*GYuD^Z*n0L-sL z80g#&$&f2l$O?ZkQbs+~Usb>m)+=!f$i*7^`&9(K%~{sDQ5(@zB>{kSLJ}4g-|M*v zZt^}#)Q{Vn1N;SlV3MeW!2=3DX@;POuVEUahI3suSz!^!qiG?riZMgeZ#`I+Me1Y} zgd7*uYE0g@1J}6lghJzU_`{Sg_00k$UV}1w#2<`?()q~o<+#TQIL!~%IZghpvzvUs zxu-?kG-+fhq)Z5*Q3D)ogx77;l5%=0qGnuSC)2g#_)HT%Y{1G2`H}cq6=A@ZnMiHi z=B@m$xl8UhOkuXF9uh(Poi7-2M=m@q13HcijAjT(;<+7cyHPlD`$h(p7UX=v?^Pyj zTlRs$*=+MG#m1jIuVDecq)6am?)Z68gmFzGrwhTyBce@OG@J|!q7-(E zu_9__VY)cke5KR(?R_c>vQP4{&m?)dp>?`+p{ZePvT4-I=y8!bK$Q^uw?-wVe1}X% z$3>9$;5+l|{&MD9pC6Yy@wdt)kDLBSP=sn^XsUuT&cfrAh1=S(FK5LEXLo=rTN1d9 zFnKt;xv|s1u_TE1)xu7%K~~Xy(T9?zTlkA>J^l%9JZh9kyuLSH#_xk9&Mh^sb!ztB z*VEs-|KzPsfrfhN69}-MjS;jpI_e8|zGPmz0b0gVuO3q=q`M+dTG8!hm?+?^sd$*0 zp_%ZDir2#GkF%8Bg!xdFrjQ*!AczK7UIQorOmFd;k`OYRwRG6T5iG6PO}o1plF6rr z|5dt?>~N?K_cwnyuPsb9`@}YwRLJ>{L62~4o!yM+y5#(QC%K-o{{P%|ud*IhTgH@C zKy^V9?)O{5d$ORcj;tl20FE9OKSf&RyikbY#}aIN_8KRPp6{sM!Ncar?Z`fh)jD^W zZEN;9tGC$h3Z;)l+q%@;ERuzm=qnv(H3!&ln zGM1D6hUaT`&Nfvh9}WVHZ~j;~Bxf+lVTrmge3(izo3M-YC4rmG@#_Zjr$@I{JKF!@ zNndkKK8sB|GZ5{imqX5YMkhnJ;;%tJ!t;;+BdJP4Ttp^-OIKcp(fuVlts4aEcSz{n zZ=o#%kmQ5uS5`XvqCcDFms!Ehc|pR?<2PSP9s~a%wV)IGDJ4>Py$HNbw;ID0(kmYO zh5v5z8>T(Do$V7X;ZG(zsxnLy#WMqIPf`T1hr-qfppK)~ro1y1Mj!Kw6|^{NG3GuE zx7{8$GuL2AZM1v!K&jD8xNZYa(GY7#L+Gsfq-g;PeAMZF{H@QQkt`{xe<$1qEpi~< zmu}5?vp7eGP%ZLAwD3k3bLrx2;B}+dzqZTAm{I^6j z1Pciryj}a)e4OgoD;TT&4czOw{wqeSQs$of>v=Edgc-S-grZ&5)fnAptA4TBQ9Cr+ zAlxa_#BLzLJBTT7k_%8GzkBOVb`e2Zv$#Co&otpUTS2Q`JGuCV?<+qc%LKRzf?gr8 z2o+JgZ(06-e7$v8lxx>Mtbl|dt$=icA_z#=AV`OZ2uQa`w{&-Rhf0Vv2uO=G3?U#r zbTbSJIK(jUyV(2L&-=Z{d+hHI1@vI%p8LM8b**)-^E}s_8+0e+CEl^R&VD^m=qvlfa{8zWy=z#b&E9?scpzGNoD5@mU> zOgb9?UGzQ2BJ4NrI6W+@dIsO0(dP4O+p`7fi!4#Y9L(cII7NL$P|!up zFANJ#1?*3U(pm#*fNW=HGj(@rSf$L46Rc|!3{;+Ud9H=}6aDKGL8P+oCeNIiBoN;c= z-r^>)uu+#DZ#K7)W*nn<^qK`n%=_z?{F14o@1c>jGlnJHq)05LnaiZ+_QoZXushUw zJaHE~?-~>mt9>%<=zCd{EFx(VndRb?q9G-APA~7!kaMO}5ka+t_HwP*pXNY?36DTA z%FT3=c+-_>W7SH9NcrNr5>m{ld#G(FC@iwx zRF3?%$ixvCr`pg-$$eAs&9CK{NprWQw(4mJ&KJ(v4(^-15=IZsKXVdbiH`5hK(e3N z_T400sxj-uZWeN{)UDgx6b%nBfiZX!)D~BhZMRAxL;m!p8nvx^bPy?z_t`LGI3^si zjWMf0dKtzvWIWev`k0ZLt=dI{T7^~ow^+y2k;8hxss>P#vLf20!$gw-g)ItVB~Bu| zIdZ2xz8qHYXIA6Nq~aLcjg%&4T;nrHjnFtDH|l3UriCyvY`e0J&GL7=F4yVSoyM-V zUutLm)#hz}67`&@`fwV3e!^3Y1!A#Cb(?hE7z%m7Q`D|c4ZZwqQ6{)zY=mS6(-3br z78z{mpuGPel7F9o+*<;J_Ax@o3glAU?)Nxdks*lt4ypE%=kG#A^2`$iwpwxyr0zAi zaY(%zBI8SYj#N&C$hW~m(+$%?a6Pz9Cj{;0$hUO?#PU`th3jr&)3Cl}zTjY8$)h(| z&GYH{M*c^!RATn6P?yHs9c^1RL#RCHHEkZzjidE0!@E`0@q=85r? zlIBNE@{hsz#tqNhy-Q=fIX0F9>YjXN@H|7l<3rT2EZ{#3BxF?1wcRtX(@i$dIrLZX zWj>)f`0iQjhxP&|#}KX+ZQO)dQ3LGTCsc#o665z3dm$vvvB8TGqHscg%SEpf7zeu( zfGl3j-im~T7Y+=*CFL~n{n4{|-f{8siT&IpR_7%q&ag612Wi@6-3Udy+1`}GmL}K( zBIJ1orLjGoQC+sR8XGj@*9;rrIg0&-HPaC*1`8dh5_EuqAB_%UUPaIaKQnrGuU|2Q z?EA2xdK!en3Vp!Cf6#j3SFmNl9O|-I!84FQ0Hu>M`EZ8Vt^o*>8Q$zHLuQ>h<}qSk zdzX-ZbDv*ESp3L`B$*SMEo9Uyq=ZwsuoXU09PJ}{kgz7)IuDrU%x}3EO1&l?HHPI< zu5Rmn9Rmb;>9@*r4NS(&8rZ52u+Do`vstRweblLY%3|Subfhz>rHulcj1Sim(V1L7 z9Yq141c>Z*%(kCn4y^L{@$Ye|ocicB+C_g(1{G>PaDgr#o|JXZ4XJ8<&6N;?N(7@Z z(AdX9KGubxQN%vp;cS7Z= zAl#!YV3Hw+F8Y#x_x7r=ic5gJ2Pwnc4gdc^QGx?L$JEWd{hlH&Z;w#Gj$S{)hh2|`n>O;o1DLnnPlQ~8Vc;J6z zij18|gp~LYc1Md<&6GW+1JN$?6yNW(lM57lrc#6I$gQ3wKe65EWX3Vi@cZ>|<)KpT z=`Qe#TP3fY9?)XY76g3H>wIMWj2a0=+}&Piwugxx`3<~SaY=y5FL;i&)y6*pMi&y) zZAS?2z_(*$X&@b(>v#mz06R2+x&Ac#kw?KTfmERM`F^ z1acW9Yetoidx=rC+MDoRMw4yqw_R%4bB(z!+K<}61Suw&VH5=>h{$u2E8!&rELP<_ zNTHz5#>Q5t0ct=2_k5CUCI_mH+xQ&(S+Ej^jK_^Eo;%JP!x3O8M^2N=dh3f?m04WV zHm`~~%ii)?>(rPRg*+>H1c8;>3SeG_O@1Hq*qTLt3uxE<`Yu~=0{=O`uv)4;VAAH6 zEvTr>s-7(=@$}W;6r$3g(Qxv>ozN{dA+~Yc)!gwprubqp_0v4eHsvWCMqNv229;ug z(Yct2iX-JI_czl-BP`L7Vm?^%KU{#`?#Sfz8zgDxgKl(*ZMg5#am_=2d8WT9^3C@} zoN+{sX{r{ik=rkP$rR_wUpuK-FrKg+9v>7fHsl(y)Z>qhmMp-wE;ykQn|fKU|7^EP zyHqK}P=IlcVJrF8TITB~Czt304XpE_Dz*{l3q*@U=(-yw0qDG5$)p;((=Vi{+5+xBROda`xYLRs#1J|B&zEt ztIB|5)$5PNXDFGg1W$RfkKb0p1)A1Fa*f2933L0d4vfYdTlq(5a@ryVKMas=2Yh3N zyUM;o+@ero(+0;(o@n4mtHXSyFIHfj88B$i=XKeF#?CG}$D8%3mn8%DCTmzjEY{K4e%<~OfXA$r(jT#RQivee#E+8m z(ea*DvWj(!1NUBGpv_zC=2~YjHhQU(V9dA8V(jX(pssU6mLpTmf&AeX|k7@Wx zAn;w6FLeLn5o_)z{Z=?IS2^yO7Brrv~wzf?$wn>EnI_I``n=8;y z1EGFSEKno+W~o#&ZZ)G1NBRbSW(PNLn^(qr?XIYPz6vOXPbPw~f|n06cwv(cGYTW& zIrxlnHmLF%rGh@TF^yoG+0XUXi?u~i38b!c>f0Vj$PcjibQzdZb_{;@^fW-+iUX6m3PDyLRXoN6Gd z!uLNb%Za@Un)r$Oo1X3ml1xMPb)$@ZXZ+5H)A#YTQkJjE0S@=YhF3b*ckoPhL57&zB zPyP$1`w4e(r1wQ}oERhlR$6STDz68Zv#-IT~IbgtHZ$TQD{=L>tLbz9r?za=adriT4aza3~l9{|tM z_}F)9pXB&?oW}#^;vn0Qoyyv0|BeJ)U|kG*1P9Hz`O5jtY?<5zQkWcK#pHwDCxW#7qFP>3?d^um# z9l_LbFx*>WJD5?k_F=R2R@_&xgtMZ#C9KVC6Yh_$_MR%UScTPHFjfTZGl-9 ztB5-TFRFB!YM}-L3nTSr<#xohXIg?GZo`$@h>T9_W~ScDeo1Q5Az}r=W$4_+!^$SP zm;O^doQ(X|gXBkJ%<-~GT@;2aHm~@o;8uKr;8;Gfy!;+Y62<*0i3)>_u6Q)VgwA(0 zpQM4EE|9P{Hk-lbow=Sq$DV8pA?mtneqJJ%OphkfEUA4KkF?ptjDu>O8+y z<<;I2%ct#^piY)!)0wyvQe*Y!g~edE1b!Ty*wn>Rvbs_=@K1Ob2IkJl)hmKheqVS) zLImH%y@;)qLO#xiG4cjhdvjnUa@OW&Df`V5?spD8#3_1?$vE6Cs}GJYqIt45hSK|d zr>;8|*IXTM$311^>-jgz8XaX=UbYyybH}H$m+xTt=J^2!k>k?RK`l zc7#2+BYpl)e{oAQ8BzxsMcw$I37A5*fG?lyHqs#A>p5@WF(lT5DINOBvsJO#qfvK7SiDM|H<)WD_k;$MZajh4 zF}VngF^(p)j+r^-E;>(ng}(eQ0h$yJ*j^@yj#roA^iZHk4Xt(`5Erbr)khP4jtn?T zwSDN|tHWaMjfBQAoqT$!}}gGGtrh4=5(o>gKtj9j?HF5c^>M0^Z@3SEM~ zgjNn;uKUr#(|Qr5@Ma{FUz_h|I=8`nw{L?y7_gvi1Dad}`fKIa-TMOsAKb~K>1QDd z+dPJ~vNYd_Brg<%_vYwEP9w>_Tb@v0f!8Ca^7ar@<7u>f5W-tOD&jcKg9Yhswj9o~ zdsV4_cLQ+TI?kr%$FVE1m?+dZ6-aVC%>VGSmGa0nV8}iWUjXLWFY>~z*4V=jfDo;4 z1M1W9lJhA1VI_f7eCog>q@@$T-*>D5@t&2gBin_q13tobS>~eps4eR6qe(!3piVeO zgFNHTc}3&s#P%aj7$qM|Wp-b{o@+$1ailRfAM{qTsrXzo98y}-L428HS@&(CCnQo} z)OLQ%Th*Z}&Q*#Ky?5$OJaA^t>p4j7H1&1ud&5CfQ*AT!EF(W( zT;Q|iN9u@>kHKC(kvUbR#M{C*7`^mAL`(5Y8u@J8rJ6R!dh8C6iMuAFsSx#C$kxf` zNIr!$>D*G(cm}tWycaIf?uFuL2h#^!d3UE5BO}en_-1lE| zhX1J7^Ef(o8I}KuG^fA$quNq6&ioxHDLN6bmY#OK)gI9p-Dt04c&3?D!^TX!YPMjs z`Q9?4jJZ@;(zi3ZBo4YjHHJGjA_orpchQQOB*+awdrfS+TZoA?+ZarJN<3*Z^BnoP zGI?oA*CKxh07423j07v&Y3jA8CxiM9c;HN}uk)Bd^=L7>#3khebzWh?%1y=IIHf_K>GtLb? zc28~8d5CAqWPB}!da=M!T1k<@U(^8AVBWdO)uz_Q*NUKuS&NwxANj2Y-o*vzCXEH$}&?B4#!;x^3f5#?&Y;^ zRq%0Uf4K+ZQmuDP)?@cAX;e_5P!ooz=m$Ru3 z(|Rm1Bge26=13{ce`?zQ26}J!&1w;>!m)5?1J{}qO=F%gqZ?NQ2WnK*bJ+~8=cFCf zMab6A@>LyY#k0+BHs7uO3YTh|X93c|s%H!Lx6)+U$#Q-1uMT=5*ZO*_##*%=o*C?Z zIBI%fF*MNr?wnv)cJv{J#HWy!vdVq=4-Y{@6Yu0D-`HB>jx@^xjKCG!&4h6*o=%J{ z4*#S8s{TRFsl5b)rpCO{X*ssvuzB|_<8Uj4E`G_4_V!fiH|+aX$7EL*>HXnPk7ewa z3qoJ^>INV6WJe}{nyN@}?U3MEbvc|}%CiCtefsss;dLAJrd9G5Qd>fSgbCqm-`0jj zd*A<>rgjhd@YVSAA`g0re!x5NIwe1}bjtw%$-y_}!&l0wfnLG3H zbiOJI_z|Vv2$c6iwBJL`%2q7$chcwqc{+cl!9-bxTe5h32mPMOtl{h2d?(^{ZXag- zHUm_Up3Sv_$0ukaamrM?PRdpgUJH4s1>bUwYSzBA9w;d3@VplhdoB5~!1I_3h(t0>~)YDO|EeR4kbYeYoM6 zhvSQLq2l~htLB61lv17M_k}&22;V77M_{rD4N>68ag`giaI1v8Qa24-A<6?;L#*v8LR}HeEv#Ia6Gf7u2At4JGM52(yR2123|vFUW;zYjP{f{&HJ-d zUqV5LMJB>=eDT2h;u9iU`3~G|aA%hTw10X`$>*PMGwkLYTc1E`Z(Foze;>T${?*kM)GzdZ0=t>B0N(6mzPCy|LThaC0CThTn3M4w+# zpl<4(t!t_)vI!sMsz@N=P!#Cn2ON8DgQQrgV+J(~scKvtsG;Yf@YPDuK2=j@oAt#; zFP3>6d=gLvu?{k&Ri&$3SW?r8XivL=yNMsjF+(&00Rn}}a*~GO{ewfRpa=crY807F zW1VyAl8J4x^-c(ywIzmfLmor@-gkh*Kle_X#RETFdRehnXX8tVC~d4A@#1CVX;It?{#OM z5rtRIjqIf+#Txa4+bXh4{{*=#mX4ENl3^K7d~BBKnxk>`iht;w{9kos?B4yINy zuvAg}Rq#nJj)-4un^_Le(cB_BxPXq{ z-#)#1ek;K@)^2a1q2*$*Y{R$Z&o<{J-&~7KpBfl#5*@ATI=Xz3+0H`wKtq@KTGmya zhTxY8yOR!g%2u>jbX{MNg0%=)@|##GPBAFpW5ANOeJEJpfqZ1G#ptDZ19r2~z2-)m z`YCCmTbq%i<0%*GwLi#yLQa@R3=UhX6~5MahGkgR?M>ZSdmi<~_H)3O;DClqfvL`u zlW@8uTNMg(%<9t3Uu&#i3=Y$SlVTrl9z4=(AeFy4TfT_9D2wFxVZ)6}r^m(L5xkxJ5gd0+Q|ZW98m zuOpX#9J&M7E8Mm$kJe&heug0(mBhRvAC%Jj&(ar?ODD)!B`n`1Tu8sTEK~|WiHirL z#J^zLMFivNTH^A1FRP?MO4cD$z7%NHw{svb$H*M@Aim<~PGeQ|+HgzQ>{95b0c5}L7S3(a zz_@+6dvli5?6vgBgD>nMqIpA8Lx zAa`dZp?>ct8J_V0P0ZYy=Vk)hH)!eet1^4OKtQd=QvZM}cOwqfiacxEOU(B*MhACC zmZ&7&4kS$DHo8Yh{0A{iI%{u3J>X846ZkDug=9OxR>fQxJ-2|(H7j_c*J!Q+`$eI< zXSQ#HPafzuOAj@Rm*7V61kse+)%ud}!HxTnfWQ*Z;o0*5$LdGb)eXFeV+kqw+&j#{ zS}r5P=?Vq7gV@)M3Bf=@%i$c>t-+io%Gt{=o@xyJXU^{lEbAs=E`)oGfeL5lbU`lHfo3DtIi|2y2;-bbEUoZ~X-i%#TQSN6Ti4)W0xkI}?Zy54KaC8rPe$BP#{QWH^;49tP$GrL?3A zHwKSg&a#fTv3QvYUdID|9_S+stm`4NySFy&LVtGxUZRF%h5&QV&LVeDo!J)fI-NRO9gN3_iC zr0o8Mo~}}+u|!hj;6#a6BCpD0IW7!Z2CXRbd~H9z#b@4Y{fwk}U2lp5j(PsHx2aJd zSX{+(xo*NiCLT&ty`Rk$I3i7H`fg(Lk!8Vm_?RTnUJ(KM@wgi6b9u59``_9aMh!~+4?N=UOR!ndI!`_Bf4azvyCdj2$wWXww&B*b0kz$3SKAe^zdM%awfdZhOg(TU<2n>c5h;C`^M+vku@Q4WE z_!<_r(|fPxAEKr0nAdJ=5%T4D^iU0f(-72FLARozUA-U><5Kvw!>U-0 zkM#DedSy2!JGr9;po&l_NwjNz${wd?`_qi$1~+J;OG%72U%>^bq+*0idQ8=eie|V) zJ9=s?X&)fI%P0X1ZjrKkS)d5Mdk}@p&_tm{j#{GgwF+^{*=45cHBuaUagTf zTkqZIgH1{^V1>HSm%2R2kJ7hba;z@CbjtPm^NcQM)EQDBaXR24^M>X(O@F^>7{SHP z)up(Px?kf`K;Y)k4YM$)Uj*Mgx4u=b+pDKR)HLF*{XW9J&B8q*W3%o~^HI_M%+Zmr zUvmWklYpdZ?4y;ga6on1FUq$y`jMs(@yPJY3`;Nxvs_jSqHXkI-Rg=A;k_H-2A>fb z)CiZEwi|M-q(`5q7mfDQ#N5i^G{<{lx2R8(B3!lY;6Odz;KNR}$EutTjE!R=J>RL} z&^*DpL&p_I{q|cEdLWsmQS@!{4%=y==I7D=OWbA6BeHp1n7qJrv zWL^J^CQ9JPj4H(pe%=AxD3?b03N3Z@&{xWFxgaE;J$Mi)Ged|zw&KH)j7p|j{$SW8 z_tX^)XtqOJXOS|uFt|wqFHp4F7?%Qv9V*LD?Jqoj|4kM>w`oBoegvN8ZZMW898)_Sn@=|9GoSF%^H2SY9 z49y;Bm73ajpy*so?5S>d2;9JXF_i^C#fb5tX(NBK6PRA3^}{%U7D1DAyj4mGFBJxM z*XUOj!}b2>E;D&#l)ZkYhbg5REDr)Ti9HdE0g^`#k1!~c(~aCzyZu&6q@=_i|9MDm;+11~w-&exInDY9AVCBo4zWUSq ztJ%^Hb6;_QG^lAcR^+W|I_Lqz@%K_Z;(J4BOMGt1+V3^kvFh<9?hihM^geC)>WvTA z2U4H4!Ly*e*)~}(G1GXYkyPS&UL@#oCrLc(7DoMuFY0wJFQ7y+6Pr#NZMxXcj{`>n zmauEDK~Sf&NI!-vQrbXyZKyf%)*gwh-`bNvWdMC(7U+y!ec!8Ex(v7KEar6wmKqZF zExaR%GpyV2l9Bt^P7CcaeGnJ&XGAIQ;a=S>zkPtnD#H0#CePHxfLe+EJ@J>HnsE%H z$bG5vnw_}{J$(#X;tTmZ^X0v<$%~Cv&X+#5e!(d}Ov*z}V!=H2MK4rt$Qs+d?P(Bv zm5guSl&Phw$v>LGS&a4Om7MuFk%!}(LeM(X;9J7^8t%5EXLHiT;Y6&Ag!Po5PO|Zm z*|KlneW-}dIl5uhGGkLMFP9j}AcR!%WTb`a8>@X?i})?+wmVrEVU5&|zQ-fTs+5|x zT&ZI=I7fuMhxGwITPIZcfpl4fLuxH=X?9Ix_pK3Wh${eprpOSHvG0v$@QGERb!W3m zz3<%Js|s^_;hrKqcc^)vVsKDe)ogN7*mQdzq^Dp7@Q{^r_UB8U{$gH1x518T4%9dG z6~$cP3(qi^V09Q~QQvlDksn^|i`MqJNa629Yl{Cc4CQC?HJzrB9HKPIJ&B9#QBK1p z*+q2DI{YkKdc1k!;y;r9u`fUdN2l6q@n;4FD|gkUZ9-KMKUag7)?&}8i|v0whF3PIh06I`bIp;5|bDN_2LZj+MV z48;$?_&+m4&wYW}iNhvY_oxDlUb@k>Ip5xQAFMFNJP=XUol}G|-|FJyZ%x4?li8K6 zDSO$`8}n05E-`JaZy#bk^#7xE{=gr!t~4n7j~0DVp%&ARR#e%}6HL027IAJ)9sMmy z)swgQqFte(iR4%MP-2J)1A$-fufOAEr_Z+-hau_$ubB+@kKPH4!Cr48APvDN74|Wa zH`20e{%)k#>AH8OKF@e$SR_NXqgJ%N}w8;d@RNC|Yei#O4! za00ABVQbYdAyfLC{#LMn&2-kzgjEKx(94#K%~n7$Qoky(_XQjnW&U^lj~UZJV>Z)$ z_~;!2W>kdK?!-roZ!=XIb!nh=n=4G#Pa6KFai-%cpueMeU(Ek9XrT)Ti|}0c-dG}} zi|b6j*DTh$QRBUgq*SK&rFlcs%{=Cm(Bg8C461y%gA)F8k8pR;-432?Vx9+6BIwPy-^w)BYOXft=h(;Omi3G8A1=TWm1}a7FN)A9Vy@|%I-GqLQ+h4{ zH5UHK;qf$s%?=Lk({rM}k0-i>x_VF85A*uRahk07j>L5nEHXdaEj8O^&%B%#*%VE*>H}4P1=r>aRA3hV|hn-;=X{#rOyua{H5^ink58k zI8UOl{B`|<=kvdEj!7Cap$cyWO*?cjzd8)Ru2=0-Gk|XMniWbh%9Dx6yR^G_Oj7FC zx>_&STW?hwkhYRHY`JU!&&_)gS+O4C?=iY`+_vuc-m?J}YxAh(37IseKy|)&v{>F( z`aKA-@fD}g(+-j!`;{&FQ?E?J@m!;}Y=<2ZZhL%#6_mikY%H|$Qa4yAHbBs4xyTFK(pR`6jn|~S(Ls@@s3g`;-{e;q0 zAg%I>qp$W|bn3MtBT4DcSLiL1@7rb zca}u#=3J9uEsc}Gg!A!pZXtzF+r2I?&Mfz)y4PedR0(M!-U#_|8@95Tq}7PYTR2Ft zkNb@YE=rnR-2ld$&*w60w&aO7d)jkRzip*E+xXfX)UjtCBa=u_y|?h8fCM;hSQSrm z_T6^w%dASS;BZK@n-K7hO0O}WW)58~w;_`qbjE!&J;oP<6W95@?}ZY%`EBT}ye^yc7kI|TBQ*}@i)0}~?JyXQ z4l0l@)kZ1^=m;gg`l|I3tX=8Ql|=$2v_VpW@53WpJ?oR#$7Wm4wfQY}Uk+@mZW^?m zo@YCBH>hMi(VZJL*rE~wDqVhN`S`T99}RZ%zBYN@zX2D)+_nilk{5re?2f4EJ)PBs zJACjJ3zXN`is^|iY9sY^U+e7`2nP-w7n>^-Mt+=ETOjOGI~FTrwG&Zyx;R-LBkH$< z)pA=huF3j~;@fm1^}+ByDj6)d#zQD|FDY8meL;9uN|~NEdP%-q9Zm$RH$onlY2y%n zSodl3Ji2!l$0!pXnd`j8XXQNCb-o>R-b86+dSWF%hyGbMjpfzwhC1hnw!A(PE}VZ? z3fAgrxxCz23_MhvM;Z^ho8^t-@`W|vl#(3Rzz++ymDWq&0Rs8Yu%rcGRrW+Cj8L@< z3){OgUb`|u;?)43h5BHV_Gd1ncW=3_=Pm^g9LuZx{`T*8>cgVfl7(8pSH0t!4mnZ! zIxoFDEZKq;vXaED$N4IFza1-%X25C|!pF4Ym6ds5#>o+g z2!iUY&%HV63fJVJeIJ2{ZtedtXj5p$e&lR`d#Jlf6v#D0qL2-ax6QL76aH?$$o)AB zclZKlNbk;V2Ws-KZYLR5FqqqUMaxX6>)hR^H>^s0{inEcHgDbZ6zMzaLRKT@-G}1^ zd+R z**|3bTtOeXkC0^OfXn-lZF9p7qx~*cqoExS_qQsKVyF?B1c%JHUl|yaQI9&>?)fH^ zqW7ITJnN1oN2zKd7^Pt0Vh0D6-T}wzs(B^9qpKqcm?$mBaN9X-fYL zYL($d5(cUzRA$>|Jix7HI{j>iA}qcZQ@s=iez>{4nu0I8*~ThDS}ObzgI|Fre2rvF zsUZ9dwil@L@nx`^pS~A9O5_U6<9@+7c1;qw7OUKTwjv82=zqR9x#7(iB&neq1Ux&# zuN~}E4q%NAO11k=ipN@IvMZl#L!+J+xOBV#rgs3+h63le+bOGyptM4f?11+&b?&yZ ze(iTiitwwyW+(GR+DE!#*OPAF4qa6Vxc>6@8-;!-08G9A{NNvIILMfI%ibnW=7UTg z^H!eX;X)9fb_%ogLb>kODNzeyM4;I>?-UtYACL(UiZ>&ysa@p6L%D9`o{=A)dT;_C| zceiXk;lysPexmZWLV+gJ91o@h9Z>0fK$SY$)VcwhoH^@)nME%9Af%BZIK9)hZnXZd zyGd5?-!}kM)=$qd4F0;Mp-lH%JPri4%BFG2w);Py&!MEVu zya>oN!Eu7p*OCWP37@c=sRvfrR}dBcVB!>5BxKb#)7(dZRG(>daW1sE)p3z3g3|5~ zv_fz9ZSHL431v6h4PlVmG*L{C`uR09Z2}$^;N)9g`2F`euO9cPHZHvN zyO_uyXbL>Ky#>6!4?3O05GwH``h{i*Uw!zw^Poh7n)hD z7f)AgbAkYW=!!MBxGgWbBR9w1%Xm7E0?;`lE>~=*?dwxmGq_q*7D%2p2had-_|FCH zM1o+OIr9EFVm9zd$%Egv8`tUnwG8>M|F_BlnyvLF%lF|akWs)*Ff`n8X9c^dC-XKF zIc%>xl5F$z_nN1B2U#A9R?H7dBjv<2iYU3>X@9-INPx@W$Rdb z=>qMOeN!Ps*4SQt$y?k`2yI}gRM5hQ$O^3d@9+L|4S&B<662U%<{K#t35`3hU_XGs z>_@VVSbFrA*`y`tKmO}4vW_+BZ`kkz=EcOcq~j0og=~yjyIbEm7Vd7Mk3T1 zSr#I3Ykn}k#{AEc{?ESspD!$$m@v{UjLJ-LA_D&KRNJq<10}g&-2xYB>UTZd7QMjwk-xC>Zxfu}2VPiUniFkls|%V5I{ek}`W`JY?z zzuvo_2SXz7TA+BnP|Wv_7<9LhwJcyV*8jd2|NV(6_x?7wr#+9*&a@$jq%U4@fE|j3 z)ts*2@$m^T@jEd7GX=2D<;c&Kmlugl@3O+nuv#6$6zfoeO; zlWzR}b|%9(dbi*@tp5{I|Ib}E&w_aD<>Mqe#;5vL%3goW3%0Sdx_`aE+u-??XicGX zOU`fJKD_(zei!QqFkulQgUJPep8fxM{l6ck0y`#5{X3{KB_d*42Ai&*CDwaHx-T90 z$BN*QZ=0t7_Ni_^yUvr)NAIwjmA?MPsGQxZsT~V8FKFp*YNMPXaJ&v1|9g%9v(%Zs zJ^aoKGEhN#Y%jjqt=ISxg`|i~wlU3Nh?VibFI5l`f|b5{i>OrDwhZ~_?K;oB$M6FC zn*8mDz9C5bGq4=~bI|{vw^c}oG_H$C8HC!V%WC7DB#>@udQI8}TIGCS|8;>s zC3%8LTloV7)xxH9@py2!TNLiyO%fDVre^x1at!RM?9nb2{~89y2Ou5;E*%7}QfEkp z^(NBo_Z;>*c*o@ZN8skbaEXLa3zh|Xp0|b}{~Ch$$3Sd$j`!2u2GJmq^h z&o6Te2?W)SD|Mri{ATPbPsrHSd9#Ha1i`VVde+i#Fk=!%Ky81Hq&Jq-?U4L9TKe4l zb~quuV01c{0O-*@Iojk=IP>;9UBNl}Ic=1!Vb+`V{Dkf>w{JW{D(a#yky#!JY}$rD z)tD7swO_4-M_gT9qR1=$$8>!TEHo@=vB`#wj9m1Lc(Xw)U>OYa-LX3%}j?z zQ)GifeldshTA5dv&!&=N4#=X64X+Z#+fP^V`R!L(fcd<6ozn!H%ad65g&LQ1DnZl2 zor%VL-9`l2KH;-KiS)XI^%y>nMIRBH!YF_zXE>3u^?S9w`MvsO_l6o$T-R9Q8db5D4}c4AVwqo)y?2J^RzCrwES8BaeV!2)Ib-@Q*o7N#FMuwri#m z@(a@a`ld$2aTzkW)aY?w$bU_)I|hyUSs&DXV{w7m)<|yg>*3eMYG`cB#f!7eY=?Hm zvleV3TFcKbD=Z)QKL2^V{ZpAXED9s?kzQk#AxI^%+;+3N%#9Qt4 z2s8+*_aIC)TLpNyZlLEqb_)xGm>AvnXU%|#g7KjCXWLx?TNbQOO#ZxHa2?%_IS)7s z%l=LjQHi?HwOb=4pCVk zb3``xsyft~)B(zD@<6+S+bX3J=B;oe_o>Dh8Z+27n9F2@OLx05x%ErYM@zpt99^ zqSAK`%WLEFPw4zX`r|uw_p<>gYJRMH*E1RuhbT+!eFEp@NK3KV7Pp496n&MBNmj!f z>!=P?Ug@ik#l(`gav@gwbihHPR|JXHi79uUlIj<-y=q}q%JRB%Ka+JRZ3}huK#j+C zvH9nuLy85ElSyb6y>{b&3(e3(E0jo7a}t+l3pxl=3pu2)I*3xY`QkXY z?iyy`4e_;``VgwyvD<>i_jMujed`@nT= zN_0egSg)R&T>1_%@KZe8Y4@#_dQNUTrftydPDVYdD@u#udp$wrZETjaT&Z%wMe7-r z|Kj%uS~2&qeNrXyPl-TK`qyT;;2#Sn-md9)>F8c@Roon-@&SiEBS>rl5rk8K>aTwG zONOz{$$8~vCvWC$0MfhxYm5VD(K+?O(1|aQXn-9ukK8|mn%@9u+rvi~zAGP|u}xNF z1M`UNOc9#~g|>TPDuIaES&)q9S)M~PLz&(zyW;Q_xLb7?#YV_R|2#%P>&iu`vIGG~pg%w`PT{9MrHr@)DrT=IFO`t)aTeAM%^7w{Bw2*6Jq<@maVzYsoN3Pf^^YAVRPIl+VUq*^SCm=D#aLNlK4<8eN4%S}lc^VAoYB&~O zz7t~5Jy-bFYeuaJa~Y)C_+`RxJZ3ymYp2KWGs(eeR-ix+pz+)DtRt67|D+MJGN1+| z-3E*AgmlNAC7gA%P09f81SqrAa|Kmvdz_q|D4<4y8R(^rsk+B5YB>bS_v$-KGw_ zL`RZ6eVoYqLZ_Bys@%Zl!K+eJ;8e>Lx9rOD>uw1}pnc$k|3uoO7e_ChEJ?PPhpO7i z#hRpI59!YEYIhlgm%I*qH7hLb5=-N^0(v$b7Mpu~obGcJbKJ&m7dy`-<_CqC)X#Wl zxf``UvtaNK1#^N6a-nH+UbsFF*tXKbrQVIf7whJIT*64al?tq|Gg-#)?lbp0Z6RRJ zhMWWiTu+OLB#H43B+=&-QFZ8@I6(Tim+yP&IYL!L%)z}*k@C;kiOapBhHPqE_)GhV znoz|;u8BUv@%rpJ^ZaHj5T_`vioarOPM3|>!fXSQCE+)pr_1fa;%PD`Pax&bcw%%;21GUMjfZIG%wnDIU{4{O*4Q&nl+zQ>Tf&LxC4a^j zKId2s;KQf)y{LgxYw%==`TOY$Wb;Qtq%?u2L?`nYKJ%wQg9=Mk&eVXYxV+!D=>seK z#s7QF3Y8#3_VH%I^8kq^5U#snH;eU~{**dN+78wFPK(hdw=epFIR>Av3E*;yv^v)o zKv_?047d5g3r|%>vw#$}D`9 zOd(E|YFps}xgZ9pIE0zR-0(8J?oel5!#TAULqWcMkz~bSN?Nh`taGu+TJLo&&6JwX zfl0fyJ(&9N8M4I*h>w7kQnBAhu8DGshy3q;5!X4b^?u{q&iD1}R>>BGg*2Z$zEgXD z$fMqRY4Mm^cnoS(J?2AO!o$orW;H6fFTwPlG@fiV%*gU z0-^cET+?K$mQB;rxWy3khj3<2B(AG%ACR1?PSZ&YXE?2q%_SMMcx+9D+q3V>b{H>N zixs{MjyYhKkIN8o+03GwZBvP!vQwK|u7p8B{2flKn1>RXhB8UR%qKM|F;(@xr2JNW zPI*-4aCHgtRC<1wDO;-Kdog{|GGR*I&ZF;UcfX*s+-!zF@tUcWE%>d?R^P=Us?ez) z^r2_}74rgT!U{@^61F}hceyy_&BW)o8s!770g1nrv)|wD%&N9>sP=*@M8-S!lG=@U zJol%hCK)2c>ZCC8F##DX$EY}H6KGI^46%Sp!ae6pjX@)N>KS}BGBB_H1Kzyrwh2f@b3Em*jkmchJ9#(bnx(J ztuj-;w04UHB>m&(NTqDj$-t#ni|CyxX1z^3_iu9*AT7uMUGL1iHt%d?u4wq(nAeSV z?7lx+)Q$Kp7lhB+bg<6*9=_`Dc%*lkxbw3yf9A!M)*PU!z6a@SeIVwW%ltN>m81`I zQ#BaL0iR~2RH>DE{f5^&0x(Q_qwTPuNBtf2b8Rx=i!t7-D9=}}?Iv9=KVE(5 zsxj-Ut>4Ro^U8aEE^E8`IDE%S>gv#@!TaRlkN9Gd+sz-;=T7D>;b20d?GU8oO85$O z_U1@Pypb2d`X~O4`_*36^xG6K1taOUBHO6P^zY-JxmXh(0lBc{sGW~wub8-$MW^pX zUj2P)wy*52^7JDe@gjo@p0inf)0W_DJ~-jxokVIQ1?Rnc)816u#(lr|6qQ`kKGa~p zM6!{U|FK->OhBQ-)V~FpK#$f3{S6)M3<*Y;We7v+L#hLLZ|i=rDWV z27CXo6Ng^^hu<%e_;MevO^)0c|IfzIAz!H$6W{Vo{bnCdF`rX~<6!=S+}z<7e?R0Q zLi>S0?`ep8N(0AEc0)=B_&lY4{9~S@;Q%Jid$&8FqpfJix!(ymD7Dc7F61O6B#G1# z7!vu-ZIpqaj1I&0@|Vli&fHeC)v_u`f0j@56W5jC7^pF=sPp|Lz0EfUAAjSWnY$_T z=0{RdVtd=~{wUkNkzC%L3cj&ydMiw0ZZ&=zKE{d{3a#fck$ty?Z><|NlPzDy0LG)SM~GA&WUQ z=R=Muhfr8@97YH^n?n*|L!z9|awvzGoDVt8nav?*D~D}_#m1cdp6~DN_WAA4{juBj z+~aXST=(m`?wf6D=zl|&b-R1}t;dHMQGZ%67y#`+P?`^|wKfy7aNvkA-+6kyUJ|AW zF^Gy94%Vc{mr+N(``TdZ=3-y=SF-=Z`=B!ZU6!NC?C|J^FCbzL)U6(iOz$+eA!A;U8(kY59?v`oY?~$`yOlxj_ zx4uRViOgM(n^F8{w6f`AxyHBL*rb6J_xU)=t^LqlDqfwwFa_`Uj)O&%6R<)T= zA~}K33>E)ZyE&!YiDdvO77S^acD_pRbLdzW%S#-}sCySU%fkTlh&e$As{vtP2eW(J7<_p7IINW5d-4mcZgN2$ zUd!A{$M^7~4%&jdV*|_x><|9Wmc9ANr=uc^0uGs;L6u;tc$9LmXrUJCuI^TlZkcZL6Lw=4^ z{{qyU9kxap>cpfE^HsXE$I09Ef&|sO z_i?M15K{%2tk%HgDKD#~7&~L#QcIdq&4@4ChFi%`c?9_J{1V30@)1v$ig8vGc-rbn;bj*gqJAfOZ__Y#HCQT4E;pFC1M z_7Q!lTkMaCqB5BKMr8x7B~#9w!VCwfQh!2L%lAO_&&8G6a=zYDsm@*ubS5+mTtMDBPed$BKa zp=>oMGc8I)QS+MfR@@{46`WB1JS$|}zHG*lbIH4H+~r`7qxMzYp;$%%MB;F5Z*sbl zH0(5M6I6;+PU=rP&}-AR93ee*=kNSy2(N$IwwGOGmf?PGNqQl0=Hm12ttEn1japP7 zCd{SwzDskLcV1_TU(1W<7fzwLo^Gn$%$VO6Oob>qPKrd?clu4R*^m2YUy#W-NPZt0 z&Vo*6=TTWrg7^i}JH5S=MJc8Jj;yM*C%f%ctgbmZham^kJNOj2*@Oy(N1H8D6$k$c z5)Nf`+m{7^Oi|NaQ+JL9LVUC_=#2gGcapy6kIr%V>ten?U-)VBu<7q+Dm%Zt=l2mt zMV>yh3TyUoE@P03zQD#-y|b=Ba7iMc!t>U=Ct6xs%Q`XYBRS#LpIcQ<3BHU$s%vu> zX;EzCmiwb)VUu4xN&Us1k}|l4sPu4E*Wt`{z_C3hl0n!M9RJ;O=I-nFeAD3kdlohF z!rn?Y(^jsz@c*O~97ns~ffy3BnFSeQUOH9fbgwAsB*Yxr>jq5NFSN@MV!Mrn9fAee zH?1VwcoAvrLj!_zfB*x(rcXO(?4~_-hl>n0g9JKl*%4d3tBZ3&pn{PWSMS&h-MbF3 zRm4EFenGLAn;p)bPgWFSul@-o&5yyi2V1KC4K9kYyDu)!Z*~w(pNWyt#PqUj4seSn?fv+cny1qGNcDDOPLPt1Y!1d^f)v9vK;`F0O?z z%Jwq{pZr5Md)LGgOEulLe`jHS_7FdQjbqp9m!$M>tE<56J|F%LgQt zKBH;0gu3mlxlIl<6)bqn9BF`yj2D{O1=(VH@46zRiCwAiTP&J4WNx%qi+r+Fu*yeV=6lW)KmuSm-3{)?V0dp<{lOS#Sj+ zz##$Sc2u__wKm{5oebW1>a%fnv5Yf_rk1hLMD zy%sUk;%>^PiBmJ+$fiawwC;blgxmcnv+3geqXhFTytg>wYox{(qDMylt%YzmaDa|s z`XUE+SSjg4AkL?5kMb&>u+~xnq-HLh1h1tb8ljitSTuKRZ@rBlf{>5X5R(YhXYeHz zyhL?Lz1J8M+YSw|m$u$wuF-2|qV6ghSv1Gx*iA6r9p_Tz$-;Pr z_j<1hI%@Ew$l*!{$no^|iMW3C;tE!Q`*%SNQZljs=;N0b5P0=saJ>=eJ7Ny$LECW? zU9!#zCZ^#8g+=hW`(c{0cH9V@1pEA9|3XV%h#+bj(%FKN z=lilT)$n(v_hYBOr$Dp+$);QTk=86YmdfQnR4)R!a^RLv=M}+gLK_Z21UJKixSNgq zaA7V|vUsD0Zt|JL8Eu9v!ee8%qrSIlJREpN#R8Tu_wujcY(DH1KSDTPqE-d2chcM@ zmJ$oGu+Ab z!!>_GnA+NX9&yh1Aj#^tIDu`haS?2`K-iI2!pN<^SB#0tl9W&WhL-H`?t7MS-cF*- z4DXCw^|@sk`P+L+2QvCbtsbM}F4Jp2TnktWvWS4j`Y1D0xrU(2kX0)L6`g-fqaSe^ zJ!z|_^VQJ*PU@-+B8hw-jl)mkDUO+%vyWyw32eu)f4WAZf8U$U9cYfMVs-3JWoDpI z6drl2LY5X3wqCio?ls*t{pcoD8{*7Dsg48IWrO~o#?$dl7R}4VsPwv6jNzhm8Ti)26W~FR#q9}bRu3_GEM3sj= zq;1(XcL&OEl*+kcb>qS5R&frX1;`jAk8u@mZQ}0xLMQfr7?u5ZOR<$7dZ~HPR`@yVigr_^ z?orUHDS;Qg|2N*kMtzi3HF9s4&CjG9BjY=GBjV0A8_D)kWjw&~2E*A(DI0;lHgwJ_ zn$V%gW4w_gcFbA|6&4NB@y;dEBVtt99z>F9ddjG(Nlu!sR8Mj1Hpbo|n3HTp#jTCQzO0z9|Ifj70`ao&!DjUKO3Yv;`I>Iigv+92nL2AFpQ#cFHXMJ4<| zJJ5Rs@26Z3KERJETqf=x?BFNyHZm!}Cy@qL-Y=^6mDvtr_Gk0EKV=-wp0OGwKp69_ zO{|?G!TPkE@%z7gugy_hJ(~I5gWamLr0*OEV~3h^0PM^`oZo8NEvba-ctXd5!*-SudUQ_7Ng&DiAtpiKCHML={d%~yV}6}CW8>;9PmyQa zXCTW)9zMB`gOMHaY1&r8b?m|fE+!-2MA7wKr?b6X$=a6&rTxB}bCI>+*CL0`WfAP! zm#3;&;UuLO2Y)Lez1C`;kPKD08CYG8Kd^AT{%{>N(n(v!SY*!D!jbxoV;#3jlcacZ z2+emuGHJc+JLdW~retxt{*+zBDEd+&=$8e=L1XY-VxKA#O-uhvOriJttJtnitKa)n2cDMFQIi$LbxdP1^~9uHIQ!~J`p}h7Am@i5&>hY zIe&J2>I0)L>Hmr;T;gNjSrG87ENp<|1vD=+q+PtAZ@igPV$s^hd3^MzN1$gpyHA=; z7L3NEwjaOFV>Jm-JDl^LE_4~w&;&2$#hm1)ne9CsE6avRF_%4*JpQ9Q9jPXvCHtRm z4jomy&j%{p%$5>o{vag48;3YN3~jrjDZ2Xp{IH;|>fX&J1hHkFNw(0W%ORf^uY4eK ztcFqwzCzb|v?oZ0Po)3YQUWX5>V(mN2|fAjZKh7S1$>}K79WUK zw){m%#nV|2Jm`slNey?KDJXJS$+3#9{1X>h**jM80Tt|&v$v~7KYLt>2#dR@4HDj0 zX-)9U=e$4QUHudF%cA~ES*hB1WPm{1F(}AhD*6Cmi$z;J>?mAiQL?@OW`h$Dz>c%<=Ew1>Ny^AdF?JAd!x_1s*d2AkTpV?KO6$ z;b2zcr48N=Q^fC3kkx5jM zqAm=6b(FtH_*(?#J0Ufl{J9o9FR;>n zR;@@y=ZfQ7E_E$uniq#8wnHg|*IjZaOSZJTBt$iVP~frTUoxV#Ia*x2d*$Bf=PI_y zrwgXVgfYGr8>d|OyYD~sk(2%EBAPwXQgA`(@{zQVpr#d&N{}#(1D4Sn1W^sOF%*@sk?)+uwp+gUuC3ttJZVvSPi@@(D z(-{P9_;A@Da+@4MX$rGW81hV3ivn*<)6!%Bjz)xPf~f7xx8`s|nDLchk;LUbpIgYw z?(7u#IG1hP^!SV^^wXT6N!nCyXTbY|PRjPaL-4~4{XLa64X>jfiizYtpAVL(HzOZOntqt0=@Jv#LZdDava_tFQrGf*qUqLR4TyJf@}628M$_{ zx1jilR5#knz7#K=iWkXo`u3|VW@Ir|>Zt&;&!FvrWQ`6|J;Vs6B`Pp;OJ5Z#mB*Lu z^=thwzh!R`C7G{Geji|nWf~MqH||@?ckfS82r3jPV%`HEn1vQ2<45lc+%!-*zPceP z{EuW4I=!^erWmSh`O6}EUiv8BxeYSHY5GOshWGUawWS--|8mtf%j;pcJ#uBDI)3N3 z-nr*(lzb0z)OKsySMYPi6z!+Ksk$apWTQYfko}g)yxS)`ZSh{l@$Sq^+ z=XOpQu9IG1n>}Ln{iYJ|G?BqqR{jmui7b-QQ(X#yj~uRu)fT&@HXwejlPybh(Fo_H z)4{ku&O(stecqaM!o_5D0k>X*G_BsV?Fbp@D!=40&D8JTkQ|(P`!1CIGvyWg3 zppV|kN}7|+Ofz$w=GKkZ746+pqz7`5zhwFkc57l%4qiCrj^$wI;G@f&GCjO2T`>WE zxh)~5$8>lSM@CX$t;*r<&Yr}<+ObD7o39|$RJBTC!L%j>O&QKfh^s=g#tf6Pxx{^5 z`#j^vK6hb|gj+##M+JCKiCW{+;~vX9Ljj3lni+#SG6zLR@)@Uyqah^M^p7*S`h;?3 z8nNhSoUtyM6(HA~tZ*39O{^BJa1tN!;dD*dI3MB`kSSjaWrkOTFZ^Ss{k<25T#z5( zA-nkNPH>Smr`1JXM_%G5ddT#9JXo2wx6|TRnbFTsaB>50RzuLRr;fQ)Z%|;gpWR1; znTfj(eFwVGk!OSpwE@6))gl)W{)$?g?Xq`Ge=UGtxMsapBKy{JSsc~aPCFXxfd4#l z>ujzZqvA?;$3>LKGg6dvl>=QZX%U!obt!!L-=`)fXh#^sd$105Yi@SJovf7>Z0t@T z#x;{gIwR;a44naSc?7O_%Eb=0zZTLVQ^je*QMF4ME5?Xq2i?`T%w|h!eI=x8!|VfM z`#M|$H-6JC-XE`hXS0AAF;#QcBpY*i)cHl9v+ zGDfoXV%%#rR=LlU+3yD$CODbEFYJYh9K{{#Vath-C%C(b>d!uKf9!SzU5C|6=v=9D z&81p_|BZiXK;(IEXZd55Y^q*Aqv!it4DO1^QlPP4zCdn0@hoSnGch$Ie1|L$#T0z!Xn z_}AAD5#;UA=+D|=-O?N1!sA`qLYKS~7m{n^BJp=cQNUb3H7-Y40eu#vM@6}7HF!yC zfwD>;q6R2ze7(TRM09KHKQ!PZOoSm<@>*E$l; zq;Lq1y;*z{)%ai*o5~tdby@#+6}t#D;TG&beWmIWulLWytI79P=TSQ6cUa+)T&ixk zJ`b%v%@FLHcJ*7Iq(XeYg!AJoyy0L|U2%q%8zM&8v3^+;IOfqbUI6XSnPO7wX8Y}> zH>bHtLWy^wrv$e{vk>&$)fGe8`&iu@nK?}_chWamypHX1399y5gFOA!)LfCxEQ%(( zR*RWC$e!WpOY!J;gLD@!GkTt8l~(^nYtowwu_S`#62)*`ngA&O*+5GnhuayPp-ZJic&f-zig+q*QbqVD>N&CN!RyMTW~V2-apzt>Yj9e{1we*}Gn z+>+=p3>w{gZCAg{!MZIr2YXTaDCG)xJ88yeiqF4G<5{I3j*`Z<(*M@@M)JMH;UNrM zSEt5W_4UW|Pe6MOr1L0{qfLlanBUD6xyFI(6W|AMS@R0Y+v>MK>PX((Oh*gK9E;eK))7UVihwG##ba zQB)~NW@J|dx-r_bAA!D%ifVASEV$gB!m9M&o6k`MlFFE0qM{7p6^X@%X7WQ!=t24u zLEaR$KNc41J>T;d>@Hu+Q+;xqK_#maWoLeYXIYdg54C-KoDbO$<9TE;j_{r-H6>VW zUBO!(EHuWs|5WG{8|&V&&!uigkFL1w-o{4`G`8F;9K<1T& zPSII!_Cx7tF8r^JxgURHbE@(_4PB<07RFW1u3yv&sCG$i-5oPhYsnAo;vyIHbYa-a ztvK@ZgwvfAPn9};Q{uw2t8MT(=HZ44v#DMN7?0pP`7KoUo3M+V-`Gm6>x?g0Bi+NG zwXT}7OB04NUutHSGnjp_R~$2zgqHf~gdRHK^&wxCSoy0L%(`mwpbWi5@0`1*bFJLu z<_plcrvSW+*=k)yr4BruXK)hk$^2L0oDpQ%nj&elruf>{0lj1bx}%O6aht;ZaIyDE zFfiBNt8>eTN4%^G(*r`5?PiK!^($s0wZ~ab)CJu;Jo+5Wr)i{*v z;{LAo4I<6FdcSp89TE2y?O0QVb`SWam^vawT8e&y_~qezxUUW1_Z9w}Ig&5xzi%`9 zROFjaR|HnXEa22Bs6cD|Kh}?|)T+6B*BP%rOg|M9aQ2Yk@>pS5t@ z?`aGl7g_8cKYPzn_w^4L7FaaN%}LlkP@Wwm6RpiKd}~wor3ere(#~Dewz-kT06uFh z0gjbo1O6JrbXb3e%-+{r{i)R)lRf)))mJlZdYiqU%Y1LfECBKc||Y zoqVLF@?{^MZ{z=^Ca^^cw@>FpfMo$&20pgw*~^1>1~2)%GfuV%Hb{5z9a(Mt#_Y48 zz_ge;=16%HX(l)VnTy!jW?L|_!p^v)iCsKoVTVx)vsS%Z; zwKlNuk(n0#JWb)-uD@941^$=Wrh0`o(~D1m!UCn7>fl={F{aS%h$yOdiHcSO5iP#o zK3h`8OI^`xbRVAYUSD(PrZ?7JhAcuZcc=>}+&yXPSJxE)d)6%igJiT%=J=$&*?R|| zzB=M%Z=C4hLFgn&XYQsu51h#26vjT%Co1g5C7y*ae9z5frd3C0oJiyFs-(kf59D2U z*K3ErF4wc5|3fnjw-36?=X7M%q?RA(#`C(Q!%;$wDkJy;-6fgD=FurmuM%XU>;W!x z2J%r3r$E_hqO7s#fV|#U!In<;r<*8z@onF}=5ThW&LmG9Q#l-Rg177XG^W4*dnBNH z+wt3=SgJtgCH=q`l8lOg$?mqWW}=1j0;ag>_@Ry^EN#mWe>;RNzM@!;&tjhg7FB>sIlbeMu`3Ze%h!T;X)T^K5cH?8o%Id?2fTKCrD2MUvsWBexR zxQ?43IMm*Fsez^SX;}5*Va)U=zoJ;5E}b!T0j>?r(9NzeOhe>r;R97Z|0fe1ABY*z z^N@l`m1#J}(1s|w0aOi^2NqZfPcSq=L7qDBwaoX@X4nugk1ng>=w6Cj1C$LlS7E-J8M9fpioWt% z7zsYgLfAE@2{gq3)ybSFW02I@mQ7}wnef4nD-N$-^rWzn?=uEjoeP@gBKOHx#%kzJ zT?`iGA%oG0PN^|$OsxA)n>aP;+grGJbE^J7bkWqWt+BQ(qvDfxiX z&`P*SS*=Kz>FG$@cZJd0ph|${(51mm=##ltnrx$dOIxUE7k2S6!VFZYXIkt?A5LSZ z+hpd}!E-U+JM7_wRs1r-pnl8tcNyIhHC-6uk1pcb{L#OgX_401Y3D0~yF8?E3bX0k zKmUOTzC)fcD*DD#bz^SMH&G^{n{R%SG2RDrexaTRMr9~IEi*gr?>8+|YMXA1n=|J} z=UX1A4;y>dDSlY5LRfXvjM;Dlu|# zmI9>f7XW_K#8)o|4R3A?3v*7ReF|2?UrT<>XUY^vO<-Zxc{^qU&{%0tW>}g>-N*2Fvw*f|VtwpdS50(Dl4~xxn4b@kN-x2P z{rCjcOw}Y76I!uA*n!I`Hn)|Ntfe=R)?W4J_f}6vn*3%OTZqH6<7KN+V%|WsbQ?m4 z&KT*8Oc0f7s2gGCzzD$0O;PR;<|c(#J|x?UsKsS_XT zm(p4WgdX1r7NtW%fC~1lWvzPx<@=Lhkysgk3F7+UIE>&hzDb8F0 zRQyS7kn>HPhTGj-KKr9oNe%#8dc42_pEaWt0WEqPb~!QuV{`XDR)8UPr*U z4sUK^UDe?BCtqC9e;WOLJ@371&tZ+YGrGZEYORisA!|3pBv8w$BsAH}Fh;T_`~!ffR@ADMYy-$ePp zB_{B@13R7i)e%W&ju8{cm8?-CEMuZwUEN?&GIR}N*v^@RIG~MhAn^P2@OZt>u4H6UvpgoDJMSxsAawpVlXPZPdX7We&hBiY z4n3!Cj+=?uadHTtzLfOar?>dA_@Fc6R(TKlOsj?5!Pmd%zSB#Kzt-z|$<%MZ|I-ip zpx^`Q9DQF!P_rU3{A8$(;N+TPkpggXZZbn<)c|^z#dU{2B2Xdn&U~A#am}4ffdv}?cj|N!35`nBcKXm-4Tw_${ATbE&o4~K6>bxci+u(e+<{4d zjX(i54iz*SA0EE|;K>hU?$9CzDoQ$mD8b+ab(h{@?R*!CZhaF6J>sJe;@J}))uga} z$l@e}i{#A8$g0=AWCisf>K}VZ--DQ3!pGW(d;%c0dNk5fbEE10!X(B32J9srfN9o=`E zT>$;Y$6B1}Bc+*Yc%*`2$srEczI4oG*Vaj9EoDh_pi-vYn_us&J#2d{5B8MJ zXmd7JW^l3!)5$d*ZnoT{7C`g=e*3w;EPokF{ocznE&UlB<5SVB*?Pe=6Apz8m%0L% zI|ra}a*}D`g$R#sfWW=3+H~0N$jVX@o}K*(6D^e%b4}=zSjg35MkY*+0;ippk`=!# z1E0Gs_rF(Dx8J>5TSI&;NX?r=DmsNj-(ogQk-FDkoLo%$>bJHsU&*Yc z>>zSm`TC5l!OY5OL`#;GG@{Hyyq&&wee|7~}~gsG=x5-Wq};yM9*wr;a}V z_qVdUY(K?RQnb!*?RJ>-6{8%ZkoMlkY-Eo%Q!C=7!TZc7?bpNOiMYFnlWiq-N;3wc z3lj)YC3HL8>W|n8#;b<`j9wt<2?)ImDd8fE7-mMdzHii4oBGAOGEL--eE2U|<>%B& zOC2!A*p)K6_p8B>g?1^4GI&KmjN*an5$X~qR21mMI!Pbv%=6x{$~?^+0eRnJ_BpU- zcFoQ~YYZBH<{*kDk33fL$699kB+?=B?{r%-A(#G=~;QTO-az=#4JJb9~ zqBSC`_2_VE908EqzGBGQvKPlK|BtUg^$a=M`ouH%oSO^~5bLA7C#Ro0IDQ2K8L1_? zBe%L!Q-I<&dJb5o`=ELC<);pnFs&bKWERtUx_|58T?Q4&E_-hJZEMr|Q+Kx_8P>Fm z(w80~%B1a_KK7ma*ta&Kb~;)cK75XP5{YWcXWm446D{U`I>7UQWhPRT zWrh&K-^w2QnGGS`ZhPVrY1~-8-t(*2)Dyrks>7my+0yD6;z==v|8Ir#jKQDbv@~#S zUY!4eyJVyi+w4&CJe?++F;h?Q@v}c!sH_bS{gc3CM@(c)At9tV8O|gnYnO-RsPZvE z!8FVVOCV2+&Ba(0EAv~nG#ah4T_^yS?JV~_Wa?8%`t+7mJokJxOWNtouEzV-vIYMdhaZhT#N>y%eV=_?2cV;d7YFugz%??<3qwcMVgDx;hegW zxlrWc|Krf5;gm6%=ex{!PY(jz4i@LDQWc+w%?+q0af&+d+U8~Wo%K#qUEbu9DL8%U zcy+g1y=@NY?}LL(s_Vu??jN2AV3-=*E^|U^d}&^@{x8zb6f=W7VpNy2>ZEZl3C|~Z z*19GjxR_B#I|XK%m}&kXr|u88Ny9J!$6tnwvC1wC!y7_6-BYi|m@*1ji<<%Hx?S`y z@VlLIe6!M?1CLMml5Z_gn5muy$>3IJ_l6yE1(XB6uT)*A=`PG%flb}+FlI0x09Fd< ze4Ujy_uFj7JreKabI;et3Oj&Z{4@8CS1ASa`YXozf3k@TzK|%ZA3;00(zsRD6bYng z=&%Y#j1;TT&#I;+@UWp!lv*u=lPYlS45T~0IE*)kl@9YK@Dm4F;{H>j-jMI z-KyGCENkggQv6?0b3A{N;ToQwMNy{fy_J@nx$9q|hYA?v*H;JR6} z|MEj<;LQA%2jn)D$u$so+Ya>Od0M{1Yjtx|N~6-|7c~~2{O-lf&eAda2S08EtBzca za+&nU*W$6J0{Gjgx4xw(0%7)6b$YY$;f9A(00kd;lRH&Z3o3q9>{l$jM|DsK&CS=C zCpnlh33MFV0d=Wz<03U(f#q2e`hNC6nFh&-Bo4NyAuGj?y+sP?T`k4Q$FBS~JWStw zahnj(&HTnuVDY^$kAk0@!>bbU3!7)yV^!^QS$t-8RFLU20xF@wmb%KcNI)GPmH24d z{>eBOR#|SR7??t+6dSd<9-yO7%9eKIuDlEPQsE-IWF}(20dqB1n21r9;tfXjjq}Ic zZ8@jSKwuR^_B#yj#o#})PX{%`4MA!MT042q7LXH+{brsvoQch9ScTMYft`hDNv_U& zFF0rT!HkMK2~}4%__&L1BCwE5%=b+LcgsnF6?Aya_WWp zyx=uCj~Z+DZAL}l#BXa7{zuI@2wdxb++_W#K_Rm7v6?W-FNGr)%@pTsr@ zyK7kq7$KiK=8joQTK$qWNXs$Z^!2P9Zy`EmiT61F?+6`7uxz-f#a8uTy|=dI+Ik_J zEIz_VF73AeOef)-y3DTF|2g&nrSjG$zYh9|0zfuD@7=5&tX(6)B~Yp-+htfuRR0 zDP@3A3bHc;klwCt#)S`$;CMH7fXLlt{2zzw0k}VENn4a@K@QPDB3OlSlWFD6O42{a z-L($MNk;ke+T!8@mJ?n|GeCR7wVTYbF_mPbGQUmTSI)mNz5F5UO8@JV4yVgc@=4RF zPkJmp+HSzELihlHrka3LWqUSUq%0lu)N8wmvWRwpp8X!^dXt6bpkGv=)vhe(nUgnH*?>^KcR>JOc+AOh(mVrUaeBP;!hu_)rNL@8%tKm zF=R!CEZm2mS-7)Qp(wBPIT{&yihuSEc@%t;*++G0o}RIEvD&UIDA?T+@rCsFwxdzY z&C!}o0@h-K#+oNNQ+!0(9)`r>R`c&X#;~fERVH1y(mEaDw`~gmsdC41Y320@Y>cn> zb!?L|VSLq}4#BO`;Y-WWiB)MO6Z=?#Z``d_SG|=Z^Hu`qJKzd!R#gb`lUWx4PR9%h zF(>l`$QkC)b}z|<%8>l(BWI??y`;o~#ppZwb7rvnxJ#taO5}Fm`uenY#Hc!A>1W56 z#|0{m)>h|Fqe20qEY}Sd+SG?Oq<_s;T>>u`&-!1YDA4VNxIMjTa5S9aZ{uYITu6`F zF^Yc<`{`P1$PXS*QAw;y&5xsLy^Mv@@CClBQvTM1rXcY1yzi z>PI1=zdzi!Ydl%GR7w+T872?E_13v!anU3zoIh!RzJQ7W44f9sH)yaIp6f;o zS1?R1;@1Tc$mgvrxR#cqC$F! z5JH(RVf9CIo4V$X`9b40tEN$sEl*twcT&O)r`zgX&67nAZW#nlV8PgLoR^d(GfbvZ z{q8frnU6*iYZ8 zsT>t(u+fuA$kM)bKWf+MyKb({5!IeH5j=;)`Zlcz@`*>|o+FPR=pzTr*^cr#3|mhr9dV1 z1eEC)a}Mw_B|(wERlmM67?89-(b4@l-=K2wQ%j{^aT}o~aNq`Am6L_`bogryP8;_=QzxwzKMqc8R5f_bkT42JLc)3Aqa3CJoO4SF@4XIQ2<& zkJOHkTUH*8! z*GToatjC@U_3a3p)iUh=2|)0cWyIhQ-ds8c+Lbw>ugsl^F{{xP+RDNs+NN+IQ~q?g zp@}+l(il9;>!i&O+SDVU*?LIde@_zdeBj&nEUIrW$R&n2qLwZR)73@HC3yrWc5U0^>i31?&f|A~8{ zVfg&L%u=hJf`@o@(tf=f2usyj@vtHckP4|U_Ok>5)+dw~Q=PbjW%ZW%nR)}gqx=)}9wZ-5T^ceFe#RKYxq;k`65emcZtLd9 z?_$;816B^Y<1Sj8VC!okHeOX9dV`?7o;IdM1}K?c6xIvK=StQ=Vhc+2cMZFCefd(y z`4}lcd;Z`6Thn@f0S2!e|*kqU_E~WNjX43 zDz!Vx8%ee%J`Ci9zowmgPE?Zm*L(LP0yxMn%o+r?4gc}92^D?W{%Do}w^&V6blu@$ z06#GRZ52qEW11m4`V0)YX7T9AFgl@M^7CPA+z)Pp>(P70~olCHjO&?;lu5M-bgLdv5oafumaM^uYv z2myV*m^t*V?s&mn{}J=ndyd7|hY2+0Z0{=GhV{f0u& z(L0N)_cEG+o;6J#^Dl?lRaiqm9+n#HumZ2T`ZK>9@O|v4xfN8f4p?ilj|DP;y1LXx z(?%L3h2LwnerMvvL`I4O6FJq#jv@RxQWh+<@hDqaG;zNt9)&1$e}3@0+6`8~hB9ep=tp#= zda?NiZPpFXZmUUJ_1CuSnb0+q+}Yfjn0!mi7h3=+XW=35KQlz`SZ!kKQMjW4JF|P0 zXHU}-S3pYMv2j7?pv2#ZZA30L4cPDZ3jBL)SX%*X2@>WBdX&D@RJ%E4JX5>#KK!I7 zl4%-nk7L6^S0>htoDe910ElZ@fb7XKe~*4D=SCOiuN-YktAk`BU|$uoSQt@#BxiY#SF zzBBSRCV7ol*lVWbXJCCQ$&t$#sBlE`)31+$7ZbQ9(JzY10Lgrs+s=$gM_HcQZvuOF z93!lLL-z2Y2Q!TjswBYll^njMkvy4sm>+L1DpH=XG0H%y$r00=?Q01=+2@!B3~Vq; zjkNeHI8#iU1^391Qu~wn<{xR7^7_?J0bPr|ojkw+RwUhj6lj-8g`vd(0?BU~96RlS zWvxeo!P90LW~&*4wf{yQsr3cK96IqvgdZf?>R1l#?i3jcIaP6I`P%2DF;90<6avv3TK?+ z_=pGUTx74{eq8|s9iT5dK1Mz`c4F@HzN*((9}oTP#YEQ}_vAVT#y4DI!yh+e$CtgO zxNw>Ye_I>2{y)mzJRZuo?E@Btvi(Zgmm;KO-x-vxvV~;HR+b^MWEo=(S;|(3Y-1-0 z+4n8kml;bW3=%We!3<;fo__axKlgq2eBS%%z5X#iALF{N^E{UCaU9>{I0bj5RD<8R zcUR1f#EhgrUdzzwYr#rE`&RDN%JEC4y?t0u-QnVR?aK)CQ>(NbuvkQ`+Z6bY8^R;CK*^hL2+vvm?1Io6K|ZlRhBB$m#b``MReD zgyTq_MNXM7T>ZKqsRriZNmXhdAUu2~#2Rwio+j5WyIwHLiEBfzFSKKXM5Eo+@To9&}^*`u| z+6AnL0s#)3ER~;tRjZHfS7;X54<+!JH0f@JhE=U)Fs+FJ-h0&UYr9EXteW$Jz@SOR zpU_2xk};|{eX_sVqQ9Jijzk9ImH2eXJHTW_h$ptYw`xZE@9pLbk8@W}ugg7oe3t%2w>bTbgpU0Ah`5w$r&o8` zpbAg!ziZ{XLN%}XD=DWeyalsie9#SXQR54xr{64l#T;-PlX841t{cqb`_=@@(tW>v z*cotSsJRyLh9j;07Z?rIN$UL8tfb)r?*&Ht%EB3iS1)H}jxgy`oq54!-S)3Zpk)L2 zX;DWnc{gu=G?~qvmsGqh?a5-yjKzHRj4xH-oC308xFJkZQHpL?k4POg`B~^bRy}W4%WIyJ34=0@W zks;scjd885*0#m*&a`xSBW2#NdR!wXhPn*fA@?Nn^%+P-v*pIml{6TdM)_sP%Yey) zfYSHoRnlkP$JCpHUM#3Dy15Q5!=O>}95Y zm2`X1M8MHpK>4K`hPHf7jhkubUERkZV%gMpnT?<070V6gmk!URmTm#YkAd(o0 z0QpV^E0NpwZa>3=bhEh|`EfK;Zv6-;p{4!m>4fz56wvDHTw$o<;iB2HK8Y{?z(8dQ zC}5z0y|GL{;jK*%|~(!KRz<-4IFzaNQ!65X)3Le#ic|E z$J30h)VgKgR3Ec>CKAz5G0!Ay_oCgL0j>Y*YKoEY<7;w+m&_ZF+hrP{$TTW_POEUA zS%)0z9wLj|FWankT`awo`5a;lx8>N80B3=WkI zG3ohvbcV`=CByM*c68Zy3x+74pI@s7r^`exS7u@_lt(p@a7WTy^0Am2Ti&4VVBL!J zSs0%X2@V8!zf4slL1kAzK-k19F`f~ZuuWASq>8N*TT(n;DL1-|85a)Ssn&&tPH}XG zIZl*9+%>P#PaA-a5cYzoTUPxWo#A%MaD|?T@;ZAl1_9&LDc|Ly(z|40*^~h~X)}KQ z8|{pc4jyaWOzjPyRVPyJpZ-X+VcTgXV!x<^!i}w@eN|+=#kjuxOcj|)IIP^=-j{ck z7IZ4{t+FO(YCY~#ve*O@a|5{URXDmHO^%nJFr+$mf}2t|G5&C511Dm>9NA1cx2AZE z39$O~s@8ZEGbH7^-dc(}>?+1U589aLJI|%ai3At0!luQ<!#Bo91y^RO>ghy?t zPxmaq$T-eUwVJbWe4XVvW%-fGm};GVNhj~jQQ&FT20}bf)S&-~Q z{_U8;vEqobnWJouDM^8~)rCh+%dUrbi|UurB7v`0r5CsCVRT>|RtnqDj`UuCF|3{V zStQtzlJLl2eH;wlJf}=KC47`|`05C@!#?f@qQyM&j=V_Bqb*EP2Pf0RirRQZ8FL z747O$8l{jt^_l;ZWAyFuzY2DDG3kj*A+GDko>7LdhMgxx<7#R1hgr8I)h}i?K4W>9 z4W>ZA^E#1>H#)SF+5CRuQ@ou>1*>u!5`iqHWcz(svT8coTXsC`z!Z77Sb=* zNA;1bZU6SuLQ6=tIcv$@4Cum;xBEqysYF(l)a zPnFr{)FiVk6Y?av6xAO$sHl;O+P&+4Hct5^RF2Tvko_)jU_s}HgJjN|f?UeO;eaEg z;uEyFFEnzDX>K^(?LgDj4T`|&1aNLO%WS_lv9A)#yE4I)VgowY@WdTHDYblmQMJ`x zwh)>}lf-+ndPKmYmg!}mvcj5?=!eV?vxo+~35gVjpnb~QS@Se6XJ zIWy6tLZl|BJ-Ri@f|clirzN3Z1tPx6#}6p!Koc6b_m;=OthIaG=Jc9IMm6o1KfbmiQ{u+2XfxEKpTtl4JR$7xVaf z+5Hf>R?ZcAufCxtvUN{XgrG0!*$0Rtxj(J4dPzC*ew1F|ej6gg;9xP)+^p{X{o>6Q zPy*ilHdcvzc@$2X)wewG_KYSCmzyVUob_!l(-PAS&0;zZ?{ZN;*MQ~lGYo5q?JAL? z477#*>1DDYss5PZdGpFOOy4cfk+`#&@1Hg+-lFrR!?DffKIMweyj8)5d1cl0;O5WI zBr1FQT-!ZE)?`$V-WM@3+Qo-IM4#U}U9|3|z)406*@SsuE|tFtIX@ug=-afM=KM1a zN3A?N5jXu%fzkG|cP2tlacM>@+IRid_EOVWR&<)ouv=+ZRWP*E?s`?qBwt+yHTj*Ggb2rZu6fJ+;BTUc zdr3?kaLhY=+=>8>V)GXb@Dz&sE9GldGoW6{?~g z{k#{@{Fp0lYCn_G2i#LmPYU_zPW|Mzi9vWtRfw@cTYo-9)!b{}_E#?GYdTsXAz zVwT`vgQHVU&&FdL{b!U8J_!b&Mf&fo&(?5?UaAnK5~ksoLX>}`%SPJjlXsp)Z4VQ5 zom4XQga%bDIz;0s{5Xhx`-L~sx$MHIPgU5z*BaqOV@AO!jp*!tPa66?+n)EBpUi^{ z9v8Io_P#&?L2DQJbGq~_Uz8&1MgVNjy*#ymUPZaI#e%`6V_KijD_zyPkAH9t_s3a`TG$e~ zAJsFpOTHX$%j|-Q#!?+;I__a#_`j?|K*tGR7OwH>^~Sp9(?*`%lU*{G8)v~<2rKMJ zwU?L7$yoKRt4%6;d|u0HfCu<$zkAD&auK9x`VoeG%I&bi+9QFpBI2fcHZd3FJpKVV1d-l-;+^$HLpg~Tz}4*XmvUD^*3a$^v4oARe-oP9On zNM#-MXn)ikrmuvFQx9P;5E}-KfO^kGVda)GO6J^{@WTot1G?Jj(UL*Wq4f8Xvm17X z22kX4nSt8fk)BNI_Y(`I?V21B3GV8Tl=i%|ZqWog>+z5G)Yw0IYc00g#`jc&s<($%isjc2N*(enlcZTQeK}B{9wGbTTxK=Mh~gEY%d{v7 zb~S|66Z)kLb8LlfCw0nm{bRN+T4`da$W^kzi~rFuPZ7`*>X~t69Pu(tvk3Ti0rt$LpfE-_ z8y+<*Pkm!50lC%Z-=2X}tD+5oeQctp(%MkWcp1M=|kYUb`rt zr3cxDh0(ka!aT&%aox9F%U50~Vihpn&!Sn=m3=1fiVuIgK4IH|04JiFvnaW}l2tQX zjyXz1W@_#)`}bBnuk2BTXr$eZ$9NaI6981`#Px=!|6o9H#^{kpzMS?DLdJAs<<+$>AKs1*&U>2WM(QVd#p6>Zv)j&KK|n> z#!wQhQ!(7A-YgXOQl1MZmauBT>#Y#wqd*FI^_|lF{P{&8X+;>O*z9BuKzUL*6q@e0 zZJe$U~Mi!x`x@j_nRd}#ea86xeo?dW-s;+%yjb#1RZ>BRo~8Y>Spx6P`4tt1uYlO3k+ z8%NPfJOV;|acwvLtlGa;`ee25IMcY3y5kq%XeDF{*5EwWAg^`--X@0H9|nz%iy&vL za1OFcd+UGJ_#d|bj3YpfY$!1nM}mec(EANo#^tW&wmE!P7{Ero)0r&}JW;2AU1@nP z&Ndm0+=CIVt*T^zl|#dQa*q;?SwxJ2j^S%1A~jg$f4dfA#84n{3A!2#bg_A^&UP6@ z4@Ol!{>g|%(9C+$K=nUYjuijoG8YQZT;4lWUVAp|3sza$k>f3h(Da}nABU)mWgj_Oy^ysf{3hdt0)%UmDlg2z46tjgQX}wKeev$Bg>yzoPIznX!vH z+`m13GpK22jRCP`u9g^J1KTiMcL- zd%a8PUVN~N=~hwiGyRu7{=NG{oPbnDP4MJKHnQZo;7Y;?4c|ZN)$oxu3WUh1)txBv zzgnvbSAL7}Se%f1^}nS{>m1Sj2tEa12qmHJ;u;HB2zQ$Qlt5+y zy9_DKS%vppgM~o47AMBf#_H9$G5@w9ellcW0-YTz^W0VO@`uKZf##Ql z9mT!dC<#ZBgLqr-q*sU-C7lKM1*zNru~+98#lckEGjh&A`m=yGfHy$tVr>A2hJO-& z9=nTm`(6G@pc(m==#N-@R-m~D`QGMyZ^y7-mT z(%*A;RT0GMK&NxCe+dluwD?sCa}QJ=*a)69geRk`qCz8uk57B^6-*QkD31LmUxAE=jwm@_3Iy^2#wME0HRvwWP5&wm8qlj@L@Ej) zcA^rT?guMwX77Hn3 zQ_e@Bi6q@fwg0tbua$sBO-u_mzZ8fdDnT~u!RqGoPwqd-@cHcxfKNpUv&j|Zsl@Oodc zAC&+7VZif8;u``Hngk$2p5)$}BE4KFV9auDT;k5?J0}D8?a76T(8YGE==@as&%1Fo zMk(p=dIL`csP^KheLOV*%&tfRV$UYRhi40P_|0YUk$XE2C<)0?Opz?BJ!1yOI5ZG( zd|Xzs3e1p7vL5J>cl=!zBLdCeH8t9$;-?Kd;77+NPAl`oX;o61sAd=f8hD$_ry+2KqhM-S~$eV!t>t6Gb1Tt zC_MT9vbl*D*G%CjuLayQ{#L8OAVw`i3MgcWtv6U};DIS8f4@WA#kCW=cirpz$SM$0 zG$I(?qXHa3`w8s&Pv_hW8jjS{6Vn0!Ou%}@wP>wf#~x7sZft8gK+kO#-uE1_j?z6F z1vJc0>wkfWv7g|N0F1dqI4}T7ca5okAP!aJH)D2q+cqb}kf1Lu<;#DH6*-{acn&Cnih+T@T^ry= zOo1m8Llpv-$lchd@;m&Xjt@j%_o&Z<1G^9SpAl-_XeFK7T9|Y&vOw_Ve`2g5doZ!C zC53nukIBIF;~(l`%}>neH%+7km`Cy3zWf3$K;0@Oq9}>}6Ue~{!1e+#2s*b_8}7DH z{yGe(vEqy=@pH__pPR52mc`FYr_V}LxwP;sw*$99<%=#i3-&{12ZSPT(O0+F#A;XD;9K zqcAfNZO=j!eQN;7w$@M`2Y@In7X(&Z`$N8 zQ@_)hK8|a)=mV+wEBmV}xba$xMgcO34Dyi|EceNtD$#s6_5bzDB$lOx@e60&`ab7) zyW&xgpjq9gw&Fm9S#P)(*s(8nPQ(L1UHeI`S%02@Ll6G_xi9J921qDW`#AY<{}*z+ z+u6UX>2S$?yFbx9A@KjY6_Fo-M?%OT5oyYdf(J>5z6*|?ODUY?^cHCY+P+)L z+pA-L`T(IH$=4%=zv=iNDtj)3mCy3gYymtN*yOeGb4Yw`_u?AZyA!qOr6g!`A$M_C zD-P1tGOCg75dk((e`#1htEEuye_PmpE%{h8x&Pkm%A064z$wOEywLmlN@iWUc}jvCM$kH6``~?={L!3*v*VxcqUkY{wZEVpvX~c7{_O;Gc;^`G}ALk&KF#JU0oT%r;OqwG-rbg76W60|Rd| znF9SE;t!Hx*Z*vnT)5w3p(2A}e-&{7t5ltTTr7%=x_uIVNrvp7V8fl`18rdQM-M!! znBo3Emxsq1bhpZ=|8J=WnHSx%VYPAn27QFBo^mmN~eH1e~VSil#<7%JE}c#HN$rJk8UUsKg78rj@Wvj8ON@(CCeEbgg;y`m+Yq`ichaGVkol@g2BQ`s^8z4X%Rd&P5b0bEvJkyul8!e6_{J;C0ww8Krt?ruKeuDu4E<$>!rU;Hl;2l65LgY5) z7ah=c-p1tr)r|gA+iP4UTz(dEmX$-1(NhSwONPuPl>Z@A1)OEocqG4C)yeNBp6!xGNIe=I&bEIUUd$XT7K55U{6eFL!6VQ>3j`jS4!gIpKjI>E*^ zyBt07+)QzpHu~c=*^qDnR=O>nPp>%KPutww;_Q*TiWU+djz}SFPKQzc%fIgrbCn(%v zKXl~J#Bih~D)VFq4OLHY1`zLLY#RKlRBe66VL7TW3kAZJ;q+a?s`4zgJ2$GtPkq*g z`JdwT-^5&5l?-eTn8Od!Sh#2BU`WLiOF70H=$VDY^X}c32La&W57@;&dwJmp;=&`K zci$f52qz#EQ!kc*IW!ywsBBcJd7I|LpZW?`4KfxGMnw6a%aaW8NwI~0O9DyMjQny+ z%~}6da1h=fI#H=;a&UYAA_x}8*qshfF9R7%Gs53?P3k-bWLfLUz3XBD=KKHqKZ61X z&JTi+BfSTb>ng;8DH>2BJ_UgsBfbInvThY2G32h;D(wa}!D0ISpGrK_0SnVP5PC8( z3YkxCvH=8vk{CHj0ZZ)3bDl(q4NP&;APEu$mNF{;kC#%1Ou75Sr{=#vXTj0YflgOX zaxmz;D<8yAy#Tf2e=#`w9($gOe!Q zUGKUV`Cg^7%Acy_q%;fD>+?V_K!XLQA1LK5sS78{-sHr9Eeec=BTf)Ekf4G;er;tk z<$jP74KTOKs7~pax<*i=)A>$GkVM`txrZ_T+i5yv&>b!R+Tfg%-Re&oibywG_W3in z7MPh8mMW(Jonpnv9j?z4r(7aBldOiY9eC$ljIb^8xOCoq$X5uX@MAr zh7hPR3-aPR$5YY36r5yLiUQ4{slfRz0+iA^cK2X^BAq+v;9Xh?w2;&9y3bCl!Q)gR zb5zYwlQNgxONJXzrZuKTo#LtY>|U!=-_v%dUZofB;^F_*`&(k4zL4k{<_qZeKUgz; z|BI9TkV|f-LvFm#ht4(yF^wpi{w(7|7?c?QqN-62=Kl7sCCSLu?<11NPY!c}p(P+G zzL*NfFTdo|dGzf!T-R&L!nRs=d$BB5x20U03u zNc?zKudU%*d-Lq{=JX8~hjPa1wQdFZO;yiLFMNIR7XH6mIZLeFWz|GZ*>$JwUv*pK z%5)`N2?BoC9({JXtMbMw!75%SMS(j-+~(=~x$KVUUbSd;)_`ahDM40Qf*nZO%#L6<#N8>{BXN93kux+F%&0|F((__*o^b3Z>1?`7cIBSV|o&O#a7PNHacnU zyXyt9;J$xcmuc60>PwpNg@qhuQMPK_#?ila_EHsMC187=hTs}&o{1i2W097hS%1++ zMoaf!KFBM}Tdt(%M_a_}W_RALMl|M_h0zXer#OS3(L+sEe9uVcRpMbQL+xiGJ4d{V z-Ky6_?Kj{@>!n@(jw%z@M!{pbLXcKZ?Hs`} z-uGYol>2-I#as*P)n%afUQJs-walIhqhRB3@%sD2!Tc#MTT%kUtQBDMP3zc^=%QIc~F>D^-OM%VgbeD(k&L3jY$l~O&S4FN1F63t* zJE{S)(EF2iUUNNu<7MzfQZ&i@(TllKa+DBS?~Q*)WUc5-=Pj0T1-mYnByo6Xs+4i~yifI~blP-zKXI>z`ZYeJs8yvpADO2VN_6XTH9trP$_(%cjuj-MT@%;46>RtM}o)<7DP@jd8#DFJYHo}!uBITk3EbtUomUch1`~1o4x+SpiQ)?&W?v?vT|@uS7>Lg)Nb~T z4mG0BhP})7uS?^Va~VCv8;njobsoYkxD9ToO=s2N^Mfv#TKH<98+m(jbgQ># ziE-oIx^3(t%pLA{ig1I`xp%M)YW=mVbO@YQePhcfe*g#{ZJ7t&sn&HKQSSN5(n97Y zsc`eN%KB=Q2ig2yu2QYXx4ntO`t#{i2M+y2f0g~hzGY)l6gbY9!-~e8*yR>5TqD;y zdW!Z1$5Rz2 z;mFClBNQfeXJs?w(WuwJ)P1YFw@ZS7(th=vz7QpB^+TTjQE`^I!>CDAug)-Y-1{w( z0G0vSu2h+FhP&TBO5}P+$(98m2fMcJp0?8b6eX77w=WngXvJIbs5*0H%H#Wu{wF`_ zTQ0UlR6Ucs_wA`fZoqM1nKMy0ic}K|m+jANwdj6~MZOuI3>HeAN4pzrb$mzIf4qT+ z9#?BIqpNk@@8C{%`^lV|lj>tix^DXNNYS#zvfD7@&4Xe+vxfp^e1XJ(yX%LR&eDb} zQ*a}bDn~w<=kxDIi{$KHiUsIkOCeDtdp{&n(xqScuXGs~6=^^9+866hDE_wp_=($) z%a3w(vX;3NL$dj$5T#nL>-V)+sA9<;1zFDpT^_F#yra!|F18mM{A+1fsTR&N+hU6@ z#6^b@=a^c21=DHbZ$OLS?F4lGS?hok8*}I;_hLXNfE$@6zIICc^xmAe)^~`IZ{Q+= z#GNd4Lcd0i#QUec*X?v!|GNwQ=G*y!7|*}_RzC2TIc}IV*6=d4r89SaV#en_EZW<& zDJaQVF}!5unA;nlcf&wXR!DV@JvyhrAFUKzXdv&0Z*TAG{8=koEal5F5cnM2+nccFhq1i#*+L zWFkHp*WP6pVNaVy8j}q?e}R~7>*!&{HE`qj4)2c^qd$4SEGJjMdfre;5H!CTa*i?1 z5bdQrWYd-7ZD@MRd31IuS1uNGo57c-h`*MXDsATWR$tLtyCy4>g;6{ad|d4@?Bwxs zmLn8>N4!K)>FPAbRE17p*T%R+l9bSX?$XD5`O#Ab)$;IzAa%Hwh4sZ*)5DLZJp2!{ z;x$v`)dkb*7qJlmL*}+-+H4TMZvET@n8J>dx&M~Lw<%8EtmpixiYo1N2lnq9_Bv8F z!N{7P30$KP>*zC>s3|0b1nk%{8wuq6N+GmqiaLH`EXcZd$dY-S=Yg!@xj2*YFS**$ zT@PrL?vm(_e?{NBXbGRivz#BpoBIyAzb7;_fQ!8KE^|N1#5KL%LttIn}Uf`6;&!(I6Lf3>E_dbf-59wvQUuZM=%2zViuL%E1%`EDbGe9J~(s=bG z>eGjj3B7{NvCRcJ+;yQ#2U+1OXn)~{Jc;Rwwwpi%Y8}%$4|lL+jJD(ajD+x z%Ee0^?)lrz;6z0%>#Tc|9x3dsbKl+D;NzHAz>uZ$Xe8a!%heJB$0ENa^NG>GW>05t z?YgZWf6!-mzKtghvEIyK9%ypkZr7&*6L@|`#3~F!btSppQ;O)RQje00f_k@)_IEKn9(9DF!ZDaGi94e8l;>7Y)aIOy zvQ6K6k~2f{8`bPe>M%DF!pmt^?fqrci$<@XSa=e|A@g9T?XM(-XVF2X8;2Y{`x%T? z&eO(vIrq46ymz_ao0s13$JrKgV4cratjM?(3aD|a2oZ9GvNZhT1(K}$;#IFd{CIsh ze_#;)W6Q!{y|^!}(MUl%!88rr>y--h)7eHrx+ABrjHK0M(n#lY1=2uNw zew|IMlWEJfO>imLFs>Qtay`( z<2>CHTKULk@_m8mlkYE$g0baLfzb-v zS7%VaFf*;(JXQ+=OXDeWILuIut6RaNlDA_I^PR}-zkf*5Op(RBBDt;O*d!HXjh^I* zy`iJ9yH6!_r!BPq=sAh3<&Sy1RIdB@^H>EdTl>MXqLs-iMO{sr@1$AE#I#pQM{mGbAfdz9~MBNq;pyxD;_}({8d6@ee|6l z)XWA=q}FLNa2a%vQKP>lNGJqnhB>eh zc6`G|nVZ?KtJ9?=h%buu)N@|dg?hPna6tAu&M6%WICxev`X)$(J@{y0sF)cL2_}JP_fVEi7bMJY$L%-gi4sP?8Wz50RB zCbE$bo5yf;gA|)MF!hG5cC*XDxryhS!$`tSRv7>LL?aS+z3S$@g%p-tC4yJ&<~#AI z^ycNiSa!k&_ybynOyAOycu#NdqQOR-qLaNTDzO(`v_JoDZ2FB9+FuUvBnpSBoM)%(~<1fa5v5QwPJ*Yekc0=$A0br0`kzCyBdyXNh&moIu7)N}aEq z4r=&8v#Kn^fW;aJ-7KFSIV#k6E=H@po2!|p6~7##!%?5%(SN-0g(_CwgT=VgF&LP< zph~wN<*H<HD5yU2Qq$6wOz8 zY$#CO{+(%Cot&YvBzx%t!M8q+vB~*9^w|CS*UaoIqFp2on<>^K_sW<~;}cL&dViYK zgcss9%-0f;r+dDeUl|g%cz>g(K|l9?k{p+jXVKXN<%ytQ*tN?tWjF447N1RkXDjEx zR__-eWK8uN#s(M!t?I(AwH&PbuWrgGszv6CEjA1Nzc z`EbF+Xz!`%>1~_>?{I!!;$!6}bQyG<^5zQz=3I;e7M}e+V*`hxk3rO&`y3|oRu%<*683E6U-HM{shD7kCudlglh60tW80A# z6wP(itny@#3^Ia<)fhHN?s2l`>S!fJavlfw-6?MxiFeB-kB>(;c3!JU`Gv_>j8d`S z<2Kfbo%4n|Bzq~ihU`VvyS+<=d1XGY_Xe|h?HVN`MUP0ak(^B-`6~noJbu1=0D6xY z#xS)izces?&~H)V#U8gf_tsC7VS0ZehB@6>E_vT5gG#=IZ6_^UC4FDV{!;BUwN6{M z|LPP4gV{H%XKxn7uV!bdwPbJs+TVs6@s5@mOm9BubaS1G*_+x&VWq~(7o4}ZHY9lU zu8HBMzZ_@l2IR0_{Q+%x(Q0z6=_s_!*i>fQOt?>-c+;Q|gDlZ>v z;$EFbl|Rk7AC>H}B^NrTWz{F1YG$jM!YAW~dCPdb`}!ikiRl8;j6t8?l7-LX@dd+d9U15i(HkjeMp)SK1E!$gL5 zJU@&|R1JS8oF8tA!PyQ z{ktvENU^T%0)5-U$tyPn&tp-MBJFAkbvQ+@3knh)4q^)IDO$GPEQwp6ROthdaWyI# z>WWlz9c{V;3)igt8yYPMM&FiwyC~hF$B|qm$f@IY2|+x}?kh2Xc9%0Z*sp&a(}($4 z%Q{r+Mi+HKE9FX@qe;Cokquvgqj5fuRZ}xQ;P5cxG(;E5*RvXoRhAUxs$b^I9v<9E zHH!&Hg&oZL!OSKbZs=9*n+IfPv$Fiy@*K8sgm)JQ*DYg82W9#vvsUODp$9+fpk70> zPjcR^I@xxd5!z*8wBh+AqgoubFO%mUu#>WF!x@&3@lt#)Smo_@(M)N+64%H)Uw@BV z>WJWiQKi@(p{t;xJK_YXo$B}!BUKOHr-2srUWWTs*qt4y5aEoM zS+QMM{b23j@F-0QP7uCN%4w1z?spzCaJ)DTc~)mnbf&m>|FY$0Koylre5Z$~o2QVZ z(D3rg2*Q>|1;w0Ig7!s5Gzq0r#`0Pi;ffL`UMn4M89J=^B8(jJ5bGJ;h zl?2tOXYSJnIwgaEYC}BU;Awgcvy5?evlW$B42z_0bG>7?Nv-cv1F98BW;4# z?rL7`Meijto!u!p+U`(~iTRl(`lr+lp1TM&juO+RTMR;`1GqktDJ>8ycb1 zTHcB3mEl?u^)yUR)SeVqKn+u;xSF7L-^Q;&{8DYR@1luunO)`4WF5TP6y%Dg&v^N| zOuLSkxmK@?!;M-6tgw8I%$09MHmzn_jBdFTN4m?!9a^gTec}@kJ1q^zsH3h0Jyb}xk68}Lhf2< z{;IA%#72e92DGr)RXkqxG^~)e;@kU?70f)$P}EFe@x3#LSx0bcuyRI@q@RRVI-=s) z*j?m#>emgyhkM@#$^9$ehMPwC{U~Doj_eM)ftx5K&5mODWi3sLB1tB$GvYfaJGnOb6A@#YHVw_0E#z$B#`# zjg_83aoZrD&0pDDFbh?$c1GWTa-|wS5sghQXYyC42ujC>;op0V*TG{(dnLRR{2EZ! z(}?Uc+j+Ic2_nTixzxC>GduBW=~#i%w~&WvBY5wJU-J9nL}GJ;Hd;#@44;jIUCRBl{Izc-GNLt~^K^Sq~{`EOLNvRLOEC zms9Sw`8<+Z9rf2#F?F&Q;A-XHag zc}ElOS{AKRn1vJZ-{#sQb-T?sW7ZJ2fq=lrRq0s7Ar;3jmki{|IM?OmDb4(%dI_1L zR#ToD)sHH!>s2|+Wh^f&9|a)_#3%h8z|*(npRdO+$#;oZm$z&9aKXE&?5x@TDzT`| zuaGPYQ9xbFgycSPGWYo_+uzBW%5Bbfc7NeOx!S5k*WtYgEBjgt!m5q|vG;9Py^DND z2dQtCDA5D!a*i|W$!GzardQGpIxoil=p~yd?BOr=3QHW^BPRmIF2njp3M;m9el%n{i4&8} zC)43oLss`|$Q<|l6;YUc^nK%}=T5WNr{W@06r?E{7BReTJkZOa>$AgMW6`}&!U^l( zXp)zjSt6(C=Rz@W3T5v%`_2;gjiTx`Us8fX(<%2RXXmtfkHoui`GrUBAdSr|;oVTN zj1uX=_X0)iWpt8RB&MTSOyJJ8)47DA-e8+~g5??9sX3w(Wdi+v z@I@5tSV3ufm;TmsONTd(k2tQ1t`?GK>lSjW|{ma>b zQ0lfEcv{gKaq)%!>L&Y{S0t(tiPvs+>*YRdx!6WxX}>r$p9?GT7j)?#R(&>I%$=Md zt(+$?RI9I}r7YvKeY<-fek)4)sFMn*(XD4QBFy4@Jf_I5gpp%fteK=`l~mIo_9NIo z-^DnT^xi0G=1sqnDd8__t7>P2T4YCVup@~AFu^ze0VBqACJJ%vxj1@C|O zOwBBRPiU6OQPy`@b=hFFXu?Qn*2~Dsg-O;r-nt*5@f_1%LVkh8ey3YxAB74fa`pZ;G7%nNNN$UdRa>S-=<#47D zzBg5(UflkN9*Cgx5@E#JS-GAI{HJ^7{5vEMCM4TV>IDWQX=!ZvGN7It zM+wugN{l6MqDX_K=RV#qP0lkq&6FbV8me{;(-Wto7RiJuUC6X~(|o<$wlDFA2g|); z`Sj(zzw8g}dmiY0a1mKVA3ho{J$%vG7#!Rd{cJz?$5`dJ@91JPJ{eDJWFMmP&SHY7 z2Mc_yozZ}!B~C04X^18h4mQtWDK@Lmyb6-T^Jn{iftvV@7(hz;g6j!^EL10MWo7mq6=Mx1W9D0T>HndGHp(R8tV zs*Hwbzvbg!0zUcXQ)){D7s*gU0h8Y;Q90jX17uE&!lC$Pq(*`%k=Y~6HNLz>fo^6@ zOJi;BatQtD#YcBDk><(@wg&=*C)GZNn?{0+$jR_q#prm6LaDpG39FS05>{Y|6XT&W zz?nH-FEo7i;KdjHg|l-F623b|QTH?kk`5PGdvKt%a0kgc!0Q#!yGSpwK1CT@n276H z(5lZds~^zIvAg@u=Ggbp1J2BxA|K+rWuOY7AEYwfpAHtN_g_Si8mvyh-a5)W?`3sv zE?Pd^rod?=tjOf5MpS{)($iXBw5Wf_86{X?ET2$se79Jxd81k^kAvqs`gO7f-JCmT zPs(0p(9SCoEH&iDtQ4qy3&mVXEKSeBxE*ygpH< zm8)Y4QkSnq-`3CXd)xD_`Qa|<;jHt(`WMxESW_3LmYF4myzeB~1J0v`KF7(Lkc2X? z)qbgWp16RW%q0VW-{Nk96RtdjLSfwcgN3A|dG@cL3~+6ql5dfTZEj&4D$s|G*9JGP z3qNeo%3*%N#3Ov};`OX(8j=g1Zw64$S{rQi>fB|%8KMRu-PU``LN^{2#|Akc?$bSo z1e@Z%XVwp8f+}L!(_L~#VUcG`j{JroL*M&WV<@LLqh%DFxA1W@RqVR@fc#LF&7;Bt zWMg{vDeO85gd`VJ?M7-stdU6*?b%(?KY4n(Viai&)~^ee1>w)-2Nq9x^G}a{GO(~g z4)HkTI5l>dv?ak@$%RLc4*m6}jBE}<9zD%aZ`OIoTZGWDzIHE7F!-ceyQNNY8dr8$ z6?Cg>Tfo@u|DowDyrOEOJ$#0tQv{@2Kt#G5rCUk_DWyT_kRAaE>5_(_8y6^e!UH1=|wPwvabM}7setyr3*mZ0#*r-5-o^k6G2JWN*CI{91w@c-=90Li$ z9kq6I8ucF+N^aT>PJ~J|ok8;^)5HqoGmavusTf_g<;b4=c&&kb66a(^A<;A%7%+5Q z?Iv^C3BIT{Jomx$e%Uu{u)ZJWgL%#4VmbTcrweO0V^2X(-Tt$pXyc>L{%hrpUTYdt zJtzJqGNU@v;3TsoCe!#&{q|GWtryOsT+IRWMJ?z1;v6wd!jNX5|G&lZHos;4$kmML z%+Fj^m#S9xyPOf|5pkN!(khQlrXIcO)xRXm%}6ho`*NbKo%G1N`WJ>jUhR6;5Fu-XOs#tU?6Y0&&o!_}a~;WEN2Z<`NmoB44jywE zdzVBb+Hk3{sw=TQzZ_x?j_lv)RbHoYrvgqgpm^!!bMvt>(T){i3tZ2))3mh16RC2t zPh5&t@E5AzH~e!*ug{!3PV!T$_G6?je(Hd$JuQ3ym-Go;|L!|?Rzfk zH1$em-rVbNa15kqk&xndc%9`awgoxY+stXWXJHbgZ!f>8oMmv|6`JIaJ|UD$mmCt6 zbRU1QxV)5cb9vKs$7a!a5p+fuR2dUJ;vf8dfl?&h3AdUC8kmg>w5eXujaMjg>zjft z&V&7C?PoYd>#r~*CMaKHghyRj!{V$q`_+2)0Gxuk3DQ)RMHC;t{8edQ`~7(B>lFNt zWTj@+K9VlL5p^<}1-(C;9ny`5SikMgguzT+(&;33aZ48-KnQk1c$O zK?7U3N4BP{;6jfep}DF2IKHpVB#Xy3QCf}4pN^F?;_Cp)!9x3(4uhT=pk)hmRfJmZ z5OryDd!`7mQ?R|vM#AqUcmnBQASw4rP;}rF6n#wrfT*@@(4>gmX$9Q zwaGWk!s!}({C$Wu*`14$!jKpKPZ(dC!se*g9V?Dv=Dfn^>~Bws-Ha_f`8n6Dkr*my z$b72GzLb{?c`%BO{9W*Qzf|3O)#Al)xHGTf=HOx9n$)N3NWOq2zA711PfHG;-yezn zU2v5ay*H%5kB>+8{7u{#Gc^Ctj3Bf2QIc+zvy3IauAldXK68zi@Et{(^LAgcPKSTO zGkZ-piWS`%uC__Ut^aubwFF$?aEtB1n(lV*PeH>$N#jukt<1yC$*5|D;n^h2Qrk!q zHzkK|bBXD+y!g;(yhN2~Bf>YET$i~JqTdQZvBj9i+D+Npwpp1!6WX=d3|)CjC1i}!Tpw`q>Ho**f33$1=n!SBAXJ$m|U`EtOw(#RsF<@c5qT%pZZFDLf^Iz2uj zUPwPaZ|+3P@0}~rOujjWP(%M1bFjSQ`I&q7ld1T1AH__bkEtME&1en;Q&c;=WrDm1 z+K0@~maA-PlfLf%9FapSGe4r*2PdM4-#S4X44`0xz+JY6s))x-L)jh5J)>*B5mg6sCTKm&_`U$9o^u^?IY*KxUMJ+bhVL_KPIP<$ z6sDO;lS>d4*Ke=KmQ`})7_HxY(}moljXLZs1)Yk>Z<_T-4@r4GEPExmmTUe}(c8Ej zJE60Dgn8!W`b4!bOo^+f>cEKhA`LtX=IL>8NpTkJBW_$rM+sY);{RkfSCh>i6q#kUuk6G0BC_jr0nzk&ZxMh^%~EW7mpe|?R{5H zrlAA!N?KQ|T-2!?_NU7K630tiV{AGTI)LU9mc<-d;TbBqRZi*{G}P~mFU+x9cfZ=Z zSHo*)Un3H@I+sypNb+B#x56P-7Ke$-{8Ot3i%*qv5p;RNyvrYcsoJimbZoyO``mIZ z^9B2yznNA>ZDheqFN-~ZMLAb1A%X3;Qw4@Jq}S;Y|Ls;NeUOEt*$On_pAY9w6HKt^axg+lS8$ivDS_@m*#cK)-j*aWjxna(ZT2QeE0{t-^M= zgjj&cTzYkJ*E)9M(roV$Y81ZVHAA-+?&KVWShYa)@?rab>XLU}!#=5!SGXLL@lmYM z7~=uqDDkMmR?2b~CztgRjzbpZzvE>4zTHcu+7%(=fAU-EpeXlUL2cMiqp#0Z>}8o- zXufKoJ|m812O%T!Fm#%&$jS(l?$L&2zva40E9n)P>3n5Xce^w`ukYi(S(2IE`0ekO zr%qhWAFqkdr5+8r$tLigQ z_uDX0CdJ}VCgC1q#-kK*>z4v^kH?7oe#HE#J%Fc1NQe%Pkz3Nv7A^2g4E9_sC!g<& z4YZ1?J~jRFN^C~VovM^1vP~0uA5AYV|HY-xQX0$HH}+|`Z#>givE@$b13scmh#!=W zQ?EW_2X<6o|54?{;!lIJxWCzhvw=XsSG4LupMz`9@q87IgUGA{qw=w<8vO#DB0AhZ zUAtMQMoFroE(54Il05NBiAz1^W!2=XLA0+!Z(_6kX$rk*1>ERKMq6*Xu>SR21d*)^ z#O)uw+nwG&<9hEVcX#!Evc=)zz>hTed;@#C3^MF6U#)02Q&M#z&(`C7mW&J->ip8f z;(m?)d`2-UW97ss3=cMP4a&C!VNSaTFb_7i1ltn|Xs5bUmT}){q&e|(}2s;1j_7LHf z^sXR|yZz-_G$boTA-Tto1#Jp|StS5}AhkJ2M?fHT=JVyAA#-!&Zu{DHGqiOej zYuU%9Z_Sts(oXoAaboI;fOSBV6?FHG7jk79}Woc{_?N_~j*ohYdRmm*&TUH`02z)(98MQD~w2(zeSOi~B! zXKEf*MB|C;yoc!^U@G`}gyttx$JNqL$(JtszOQR_H!r~c>~^ec{CbGzN-RN7Is@AQ zu=&(f-Z6!s;riW;O+BhW%^H`wtSY%sx^qgv)3) z_R#l!CaamnfFj|0;V5-dg?r zl?Ac!r$+vr-iKVp<`Wx^AV&qEr=5lKvNDJQx3I6Gi z$!4mt=6ub})0y5O5XlYDc80qZfIOg{3?Z{dfy$J{-rI3ghnb(yAJfHpicvO3DSTFg zxml|RXXrW9?5mCN;}_(2fu^K!j6n*;4T4#Ru-(sU=A^@!TdslvFPC$Fwd(?zs=|Hd z+6*HgO;xCwp4az@%blg;1*a@#R3bRuKm=Y5S)7E>YAva+J3ib?m;4Nq;>`v4;?f*u zqGhX{7lp3;I~mT*8t`EQr+k{YL$g$_d%$&o^h1WpwX#-2&YUxxZzO z;t3<;HZbJALefHUYfsSO31XGzMGh*O__3`Wo`?Q+iB=!;3Cgv~Dxcn=%mqJ?Xtmic zMn8kaLy#vM4Se!uM^;9r-?=zQ*hXIS5FWYi@45}Y_~0Re*1sR}b(BKCN8z^sC%uH_XN(^o zcP8g>6Cu+Uw=W_T){@xuwO(@f)n@UkXBF)Bw*n+980%pM$rJtD18Gy~Q!uQf9&t+X zz9HDLjvgkbX|qN&jReV7Vn*Mk%2UcdeRcp<&)ch+CLn9ub%}71vBGe~91xL* zFB9lLA}|-e32CEQCPwXvk1Id+L!_;~9{-`GlPYNb<=@j?Ue9T!k{Nj1KaMS&YKytQ zf?DGP@BHpc6?{8&K>_qOztX%A?bMf0|JeDv4*KFbN`cb#iFNIIBa9}udxcv5=rgFm z>(DSwsW@)}s$Iif$_D>XwG1Bh_WS1Vm7?Z$lf?$XBte#s`0b-Vt{C;CAliE=)Ds!E zV|aNkc0i^5Ni2k2r(mjB0{_Cp>l=QbEx^V(o&L-__bmuPRo&)9v&$-S1W;h5ZwyT< zOuM#HR|4u2g`7#*lz8ybzh61lySH~s^LU=*K3ydk!I5c-+VzkihSJ}}T_tmv{<1_r zSgMFokRT~h?G?4QPkKJ(E`>PFl5C$s6k~ZZZ%Da#45O6eIj!k3N#M`7F#5Eo)6eV{ zuc-x9%xl%MkNLO9@~TwnF7e>ztt_}Zp%B@``f}mxU!5;|^v-$6fn%{VsvVQs+&*0U zYYjV_KU*XBZ)=^BvUYc{FSd(tvDLDL^qJ3v%I8Wcxt-wr41k|j(Dy4lv3)-_i>r9S zYomTbW3o=~t-Fh)kVxqLD^Eu4`DH;~K#U=4;0Mg;_NkZ4;u|H~5Dnt>xPU%4ciw~i@Sk1%?J+PQR!X}HcJK(*ANYeK{ zyXIn2zonx!4GK|rGi6p3Nd`fRs*%w1yIyaSjiIcbpSd44?8G$AyQc6wE&A*Is1|KH zIbq{9MOBxa{hUP&{H3m)9T{ITWEF%cq!_AaQ1Zjn#uSVFm2CEgt2+7uWh2X`pI@pZ zvsY~|{C*qTRc9|88_;?b7|4Vvv&EmG;pBi#AuXSm92d^6T@VLZCeoF}lrreBO6T}6 zO$e{S1Z+ft7&IYa;l&olakiOa?g^vSminS97T3YfIv+CY5C94|Lc;uGF@~X;A$%Aa zyRw)uNZup0YSNN|VSTa!->-x5LgotFb2U|g=F~F|I+Wo$^Zmr$3|jj~Ntxf}ghta_ zL>y7h zY~%}LOx63I1l04Gg&pWx??4mGoY^HzGEz#welrl;*uF+BOF*^e&|>&Db?Fmwb>*bl zomw7(RJU_;8zwE8*qL?WVjLO>^cr$^TrZ((khn+@A>)L*{CpT&v|B zYRYmyEzweLxbTE{`Xa{95ULzD=jlFS*oZ_J z{+33U5s*6YSN1!h1;k#+X@!RtYL6E{{#@Hgbz^qa-}T8O141I#!dUlG+nP7tbU*5k z20$40ss2H0b3JO6^IctnPeUFQWzp#cj7huH^URz&45V#qa5K|S`bso}pc=f5fP-(RdLErSkB#SD5^Kc?HP~x)3a0*5xmd#JMr^g`i*? zpn7EbF&fc9a7mEHuE#ztAel8YWP2Ken0km zWnnS%$Tqg$n{cI7Gm|Iv+B^*Xlpu~B`f8Gqk@w$L4e1v;5|{(lI6=s^Tn?6qAaZR& zwVPV@isZ`yJMJO~Aew~bb~-Hmcn2QVDXmwiS27MqGU{bK4#nWxZ~DQ6(p%ewZO8LS z7XN~B|MAnGP!z?g(!}!zj>rE{o-qVwXhFe?D_bgyt?t={`n_S1&SY?$DzAiUQgeuc z-*}ceYK4-^EmQnrQZ-TX`3QhADsGxw#fju4wanDDHEQNXUaNGRKLy;T@m}Y;ZqZfR zI9@Z#uT4cXxJMgS-HjWIHa;{``6u|^{xtap=zGrR5_@sGJyl6oC5yu|$}OH!OGLX{ zh%To0JD;>vhbD9Bu%1oVb2UGKjr@wS&cTSf{%*uqfsGI|B#ADz?z4!&{Jt+BxlMYp5|EsLT3mvycDgKQuIScQaz8nN(IIi zfy@Kvp7g&@hlj?PdXc%f@S-q6IQ80O7H#O+NypvHvcPYu&VjlJ&su34Oh;`zf+Bz< z)40yC;@Ry!8nVo{g8V=;p+GkjhpUs~0CbOnICzXwUf=JjJQ~?o5B|`+Nw=U%aEUd6 zceQ+fF1$e-X>O=9EyRkCqHvT}k5ifW<&HuMsu+u$ul$wgC{Q|%7h}brrg#s;4^#1T zU0a%dkYF9$xO@6NZi-f>ANkX%3s?OlhMAy%T*>^~%cY~7!Zk^6jqCHfF*0XlqZVG1 z%W<@7bQ5rj_SI-io5BtfNuW7Kgh|3d79dt|wd>f%LdKz|V@^}Duz!nBp<8>I+}tPU z@IyM1H=(Zu?A7h$G5s%pjwh1l?@?#zh#A%HR5N?3t&qS;D%Ij;_Y2)Q)?Q~OAF{(?t}lB+ z)a!)G!k^@Oya;)ZKTz?`GwccbMI&&{&G=Bp?ysyFC4oZzeAhZH6Y3F}%)fq>Y%<*@ z*Q`tPQK_R`g7T+6;huP^BdTHT6WEVA}nF<>jDJKh2ejMIz6MP z+$RSj-Fgw(C)SlaO;(qCx2e0h-Vp3KzRAVdnD=qDyR_mSGrD~ghR#_(Ka1%uL%1L1 z5ElQN2BR`53WHiZQ1erTX&2T4(9C>+4r5R-Z=EyX){&)=H9gs^a36sc=MQ6{hiUrt zPEN>krP!!vG!>WGo;OkDb1&;F(pccT4a9R6iM{FyP>{+3GY3(b8TNZ|;UtAaS70_U z^VGN9SKpG_H#`dhlQo>_xzV^LfTw%@POW)MI}A5n&1DgIwd~K;DQ(e8A?VUN=m*24 zA=_v7avpAUI%uYf4Nrt7<^=cU__67EP-eYu`*7%tQKNp!6JlY(>PMNXOgcHso8Z;zmbGcgJEWjA$lZxDz^gf^R3p7!- z;0&m-CVHQ}&VBlIgoN$IDObfd)z9qD-`$$zs_yqY?p3cy=Su2S?|3}yId_LHYV&cH z<+o&$`G~(LIL_7EHUt8!d`Vu=A55Ocev5=-uGnyFHdY$+J+V~qfZ2RRH(m8~N7gs#Y!CJ(aiPcwDWP`j2;d^BeXo5IB8F~4OB9rNQ|@F4siBn(@FCAv0;wqaK=xWT@_iMrGCn} zas!SwthkD>QRk3|$*&bRcqM+}xf{j>DoJ`rJ%mD(z@l7h0=sSR(N7h9S+-3;a%`J@ zIcN`<6lRH!@v=@oNBtu-nl%Z#^o14%13tbOjOzX_#t87d|ITgsHjirku3pxH_SC*<~ za##LBurf%ys|+^CwMQ%$q8T<$&F0H|rW+@C4VGtv1XRv<=QoGjH6pJ_;v7fJH?gHx zfRI~DX?!XH?Za1Wfy^5`YoQA*nqR=a=x$1fsPu1N zeAtM;g5vrMv`4}>5lswvSiaIqU^vZDKDkJ}XpKcsy0d0Z8ewfg?@idMCLrmZ>--`d zN2Z8ZVGml>cKltp__e|J7e?EE>1Fv*fAW0jm8s&kF+(+H+K#%ayqD2XY)Y_572vS% zSaUbR)Tg-FX5J%n{o!s+ba7V%FXb!>M@BJOo-3*9?XSUHFhM5v7`%Aa?URLwHQvo5 z(Lj?(os7G^S}1CPvKC*XvK2R)ncAp&d&kdRf%zTfn&r|g<8Wj7Sx1qWARmrmo#P+R zF>Rba-l(hH%*%Ba_h)iX{)X{5oaVose|!HC@LTQP@LukubeoBWg1fCz`;FSmyskW~ zFNP7Vs$_Vzm?Ct`Y`VLCKBey+1{ClfH3&1!c`XU}oQlH4Y?oTz5KP^d6%r+nBz0RX zE|{s41Z>b`zl%G%5yl#T3+1GZWZGOlk?*ls#@u`TCR>;bYv+WG2z@YPwF}n_+ILY* z$C^%#VSnd*y_$N7hD_yVkn+oxCVqxhkFLBHIPDXjkuAj|XP<8>VcSN{7TAl(8%2XT z49bDh)OIH1Vi3$64Vn1_4CF`?!W{0Oq*I~JpSiWlf^n!|vfO8jU?|c11>kqNa?EGn z_e#|Nw)XaX{_yeR#~_ZoABSUJ^ovri0l1Pkq!;9cpz^y||G0u`06XNxLCFh`*I0K8`i$yP z6Img9qiD#cUgC{UTn%$BoAGTegdAq?Ve<&D5ZirQ*h{Xcd!Vq@oE9kR2%FqhMqc}+d06-7&{ag6X&+&Q z5?NH-FBJlB&5lDH8R1h5fi9HFQ+^l^Cc8Pd=h*q!p`9*sDTPcTg3KJ{iG?&^<61n~ z;&O*zoD1rw*1Qjm#t|c)3wQmxm@*&{7MUvHT=*~gU){Vn3GXY?S#tSr?8e2JC25PzNh=t0k??~|H3ErD`Rm-E` zc{{Pytd~B;V;M>F2Hl%c|N8S~p9Y7y2uxF17C$VONzbkVqNHLd{%}l-&oA-_u+t`| zGEeuS=L^CIVC@KQz(ez9ES@?it>8;@5F&}HZiihnC5#$;RX)N)b^7-@Bg!kbBZ@tl zR%b(p zlKfH&PVni2I09mrpK1=tgY|mch4q44Q}|Ax>20%)79)B#hFz zjq)?`l}7-YAV#=KPg7?GF-5bIEfMf?lDIW^!AUxOMQ{_~2{XbXWs&5o)xB>b$vvYd zBYn6uuP}(tArYIqlAT%cDE>It`i|BpzCeXj(&tpK|0&c34b5sWLmpx_9=n!^(IH1Y zdgrA6Q$>K!H-z#0jofdAzGlk_uBabfJR?gC>TPq@At6){*W>7{cjY96dYJhZ9glNV zb-5H22VrJP_2Rt8G=6)_uDVZ#jSIJ{csu(AGe4>skcI687da1`4VTkfmv|BW;bXNy z7&1oi#;14G2&b@(_3eq{|C1wPZo&5>u<^LLmc>BuA_|9M4MK=$cgO8NpdoX@1FI$5 z^^AcYUajB0%NA)`{*7e0wcWG#mfKaQMplPB9;hn#_E{)yYmGkeT4byK)6#A!iTxI5 zWGUip#y)Z$nLT+)|C9VPxq>SVF+NPgEqPZW%<_}EIP5-mnoBcpd&htyiI~_;SiPp4 zrUkCmkcjGJhrWhnRUaBB#SsPQ1PP4&J>sz!0wb7BF8B50oUB0RRfd$aR{Pz#4@dR+ z#JaZ>#g%0M`9r7%?CLb>;^dVW7jyEgru3bNT>mGgv-`VHxuM(KjaP(OZ!!Y~pHltF zP`pPboH0^G-{ISY?I~#LQo3@;T?OyaAt?DA0Gh{ocq@)EuL|h3ZAdD%`V~RdB`b|)a~nd-`nsVY>}Xx`Ppr?zDzw1VU?zDdZ)gK z43?Hb<&&kiP!zpk$pxEw%0XI#aSZGnpzYfZ5!gg{T%2V4^`u4(Z@lPqh9W7_Cdf6) z5DGNsvFv@$-NvlTO?1zGr!~BhL2tv59I-G3Pke7Ck9}~lbIPRLigZ@!y+Njuq|ZNWYl~2o8`Ya^&~6p5heJ?T|H`VKKQMRX zV7WF7JvnC{c}74bpk+KmDFERHN^*RllR;wS<&x<(SUet8wy3R#U zG$wMEhj(0;?o3q}*6EAQ%|zF=`mWZCwdLF}L!Am`&#SF}izit-q7NLcnhP4H@CTpl zWzEx23)?b+z-DFhF8m8~BH{E6Q=k7tvty1A0~ERD?E9x_AEtUf4{^IBJc@LUyW8gS zMENXdF?%m>&@k>&5f5*wt+fq18>5?rW<^Y(1Lj5tO$Sl@=s=7%)gE)TNJ-uVL7C=f z&%N1!e_GY+@nY&?>hgyb3n}6-4R);R-ISKVmP!h`Q*Mm)0V9m4l@e!jwh|4?gX3cY zI7!#2!SLNS*meo|a%~fyq{W4v z{j8Ec(PM-A^}BN-8xYqG-oFZ;5uaenSU-?>#TXCoS+_{68hm^qm%UaCU0!`*vlLM& z>9Q_D%kP&Xj8sgFrx32-W%OyfaJ}D~_j@{~2V-OPh`pPrd$%=V z{W;IrB4+TG=Ximm$>SH99HHzo1YuJ4N?QLw_Ab!KW?AhP0aTlP=)e*|*JB!s zFITXoV;uOq>RBr2_D(wKH+1Ay6k*&CB=**q`S4`vY!+ZqAG8YjwLr%AUv~YMTNhTi z749!%umTpTiyg2=B@3K9fzu9Fn3NmxZPX(#r-vNX&&G{T2CP`a@x-#Mw*`P3(^n;D zB}1u5$}B44pGNs%!vb30>`ui4becVOE8-rPOynoClURGT1y3|e#3~%Ftq)AOrk#dt zzOu$b*0nn%?YP(+9`_gOfnY>_0~;TtN8r?EzCH-AHd4cMSDS|CeQ9A1TYR;7sDq0s z_t5t(X+c@#Wo)=&FmVVE*HqThZ;T{t=g0t1P?8pCz2(pdoEp~cmRvOX^Z{H}Fz);h zc~&%Zr*geNzh^B)@j{Q%ufvUAdkLF6S0y|F``@=*V!Jb=OW6 zO6l zo9DR&6fxdAWseU>bRcolKblJ@3S!{y z1qHt@OJOUEPIGM&gQYNDarb6?yRbfnC}$l@)!sP^$U8(=K$CV6p8^!lT*f07|1;R_ zm&B0&PiTJrN8#$kWpk2Wdz$EKo6R8GqJ~4xhj>&vj>oYCO?T4o7yAe-F6%m!3sePZ^+;Kh5HbQTtU-a` ztl$q*f=Xib&ThOY#$55P-}0w6`dg-ldt-~)AH0THLD&6zvTH9s3~E2Zh%l$>JGt|T zF5&9FHfSZ^&W9QO?2Dzbw7tqbaY70 z5*fzLk-j>;>M6(eG5N^5C-UT{81RW%j>sS>d|12pl|sx2I1dLm<0qM?lhX4{fA{&Q z84C1v9Vb`Ouqr`MhMe*j+IK)^TmQmuT1MOliCg2$Un`lT)fQlqLXlmwNjh{mo6oi| zP$nZ{K{WPP2rK5>mkR&rrn~)@;602aIrBu)_-{JbK@#e@%~zUlS>2N1VuD%`^Ald? z=j<*k8)2s-d$z7se9`y_DOw#QJ=e-FcT5G8;BAM?)DBgJN@3D3yf@DS;G;DcO5qw0 zM}ivY!=vf22+L87ko_zI4D8cVToMXl zTqKU!&wLC=gXeMg_BY`kM$t%V@Uz7ELE9U|5kDM&JrNLajvov3F;oM@R=7NI2Or0N z?XBeK=1x2TiZ`-845kCP?+7cu*YqR+$T3gn=eg63Y@%lCc-LZLQWqL!r(ZUZ=SLnX z?GG*f(h8F;8kMOS{scD-inLL~Ht&QEbj2|U(-unhH9lj?=wq!68(5qc1LUSY=`j%G z>gu3gDF=f+-^|>`-cM$0vW2bt2+8iO%pv0ux5FjH(YK!|z56j`6tRXtQHX+KPdc!^ z=jBG*QuEk6crzp79A$gmJpe3%+N$23RBJveseAMciRf~UW=0k^JU26h@OOZZ)^7=On5(BrcNZ+)A!BRL@grB?%W!_H!S&Q@ z%G*Vdsr@ZxoxErz{Yuq>RUpz4YYNf!7eHwSR%-Z&0jGpQfZzF#M@>$H{4m1LYH9zv zKFM0X4MlxI2~UrLaifhfKRhSQ;QJE2VgUp>sOShDf)(aa*^aa<-eA#4`v|=53!u(5 z6KDV9GlMgAf%c>9WBVuhl!WX5>b2e1frGj(x|{zvk#``+SVX9Rf!vD5k6Y;sHXQ=x zG64K0p?gth-!%Xf=8&+7X`%j$8tr1cX+8)KTV1S&;mCM`LbldN@{Eg(4%V#z>f%uH zb=O(;!>uA$#)CZ?3Xr7u+UOg^f?@-pH%w1LeAk z#4cF4`nM~t?4_u}%avwi*y6rhL2eoeR<*`RA0KH2YKcU3x1lv~Wg=3fkyQW_QJ@_! znU@?~Oy`9^cJ9Ow`E;#DMYc8orvy3PP)Ke{Sfz3@ZvvH3Nr`PP`mkq?`ENt_sW6lB z-sH4E!X8bK&-JPrM_Iwq+VU%VThbGkV2a`m2m0-u0L~+f0JFCqSjdu4T#A?9UJtDa z%yM7{zf8A=FhiX7zz;^_2^_Bc)!b2@NvYpU+JAR36wAn5V3xyV*zvLz)1$T$0GR zhppIv5(rfhAwZ4KbR<5Oeqg@kI9b6=MTt!PNit=DGQ&d7mHA!chxLV4wp||S8`QYu zS?!r+LPj9BhXecM+7hLgD6a ztmAS1;VFG^IowZF9Xg6l?|ZCpbxCp@A#VyS?iX0b#=ZK%Gurt#1-vzs^WS)d+=Ri* zXJR#la`$Ke1pPfcS150e8*l;=V0Zb5!u&6ihD%F(a16IE^m_ga1p0KPdM#fD!-fr9 zZRDD&_@fWsoAokSn1ZCqH5~QI#?{~$RYxRvSW2vy*v!@J_3RP{lzL3rstR?PV7(_ zQX@C7ABs`8lOYw_brLjofVc&!lZCp%3$Ue4qLZ(*D3(X{x1 zA=R2|Ni>Nlt&2V^D@O;0{-dy7`|#hVOXl=V%M6WmC3M|_L)pEp@;Mhff_Iv*Vl*c5 zLqvn$by*($*{HMAHRtU^>p795HdTqmc6GBAtOlml_hS!CG92W871H=RRXU};#pPPB z`0kJvOE6w-Vyss!OIr0pqF)C_9ai)F=3Mt*%@QDKKcDC{lB?>b#kKw^@N_0O1XWiTW| z0w;9yoUvEy$Z(ab&l@cmJx%>+(!f6gh;8BDr1)*H7jT}M5y}PYgnADbhvT|6Nn<_Z zXE$pWGl0Mo&N}agg|5k5=vSay(rzDRq-9>5^WG|^xm{l#t!Y|xHGzGVbBvF)_;ji7^It^#-;miqdt*ec_plvJjpntD8 z&+zAC7D4T4e>NX&KulouqwPdtP+&Gq*hi`cGo0#i2@$zm2fH;q)r_P6q;iq%L)0tBFsrX(Zw^CbEew_Un5P?l7?w zINUfuT<7X^aqP(^7~{5;SbZw0Mi(7j<>I?LPkQ7bIIP>PP?^5s%f( z_l;40LIq?tO_u7JM$<`RdNb;|7N6YSi$J5ki(nIM3heu@?CpXQ^3($D+yjJH0`#4c%nzekj#O#zHgt zwjFR&<)R(G!D&@^Kn=7X&>Tq?2v%E+=1`*jG)=v;$3l+XHEw;LpYPj_iqu`mZXD_+ zH&oaOaqmz#u=pLP^NG(w%x*3>Q*2v1FDuOFqtJz8f_m?`7oF$w%@PM8?EdNYZmnEK zx1aeBx6(aR^y{vQm*drPDT#OM)+H&=tK(cUC-?Nm4Lkqcj=d2OF%#aN6Hg@4;c2z| z<5WTBzwvQW;IPt9DeYYKi3s7W37cZ00D_ve|6F0(0zA$(R@6b^iZL=5Y+qXv`XoEU zWn!w*{*&kukWc}OpS>D`wg_QaN1+7;&$NR)V${-jIL08TnnLdBKh5BxW9xCqd-@-<3ZZH+H96`4GYTq5r!duYXJgqk=ZRr`%(g?cO7svnd68JM&3Ks%dhD-z?6D zm9pANN(uewNJucxCP~ywQuP)OT62Nf1k6gsQOBvbQSa$YctUy_(NGi@Jmaz66i^0C zR(y|^dfG@nZmyb0vit7s&iv3*ESUzoOB1A@+Mf|eF}JvD0xFJC;yHp#oE6nlOD1e^ zUdfC0XljQ*rvFIJ&^|fG%UxHtW|bMgH{TBWmW73d{BvI~M(f+-@tyKl3fLIAp8zp? zWeY4b%06s-&hyENrE^gR);?OMJn$U29Ta}O#m|)JvN=)|mqL*zAvjL^VFfCK6=_Y& zyYRyV#9$`3>SjhyG2v@Z*)6a+N^F;@mZu{qk6ATWdYnO3+$Z4Q^pD;g5w z7Jn2ei8~3PS|fD7!!ELH1|>@T>@o~iwxyvm!e-Q+w3uqwzibBRu-snJA>R^>cfF}H z(0@twIs9%XToWI5Z%|J|^$8>P8?$cW`Cyr0e`0L~E2l*3H|Go`drwL#5JtKCurS|o zQ~d9l09f=a)-uqR%^JHA18E=3iu&LRKbCMNB=JAvG7Mc|Rsk}-Ld`*U5pkqJqGR?V za;XBTKew@{USCiKiLZe{k|?qHL=Z@1?kM%WF3l|ruyl-vV7^|YFylqj?uO|m%1w6L z19ozg3{}nUyUgH&^%@BCS}PA`j?2sj@|7<#1r@nyN$l_Z$73c%G{T9r)m$B3DeCBp zaLJTQN4dW|f4qtv^Roe11?*zU@>|#fA0H9=vqePs5xgal;qGLr2ufo~Ck2t%!KAW1 z`R{t;qhkTHnlKiCpVfPq92RYk4g>R1lx$kvN6cH{!dJ3=1hDEY6MyAQrq9VGUvOZN z%&!NyhinvZG~wwTe1DbEP$*A|Hpwpl=6aqRjtf@WD*lR2RntXHAc;uB536!Mj z4xWsnhjV2DRniHo{khQ~TX|Xk8JE8Dx1<*=wtN<^e?XChY~-{<>Jb6|88e$)w)C75 z+cY-6pwkQCuGm^iCyHz)5E&JCqc0us_P9!s&}B+w)w2S>kSF@KL6bYjyGPQ zJv#@VwO%Y9If5-PMRkjY^tm`7-xwBR;LtG4pT#r^szk`pn2v4CcAz{g7`~9NI)if` zw^z$sk*E1Hv-}Pe0PV^kA$*sAhW@z>)HIS-T)Ek8n-QEa$L|Q*l}Yuaf_qP`siCo} zeIR8n26(wgS1nMbEZ|g>pWF)rIj0Yz*5DSnXJE2hG+b8XbS?6%V9Vv)iSDmO zYL^{Ew#;%a&c|=p=>ON-Z+Vp!Bs!bH?mdt zwL*}lzFI*ffO?zgBIxoSaFGA;l?|XZklz%8!q?U}2xJtOev!#+MMpD}yUK6!jy5cL zY0`4rPnNSS`&|V6qimH=O>A+n#JbG9Q#qGX@dgAYuqd&mM@^OZA+No^@PBjteaA&c z0*m%#$ulV!oYDhlRNj+>$5W zYMo8cd@K?6g*CLamZDFd4;`TT9b7T|r`fyzQA=3NAli5l3<`fWLx?D|Z2-5vYMcA# zqV9WtNiKwj%M!uIn&6PRNeDMb$FKq6={S4d{S%zEBAZ}K0pRYB zY^so7bfEeXejg4;n;T!%??~b`5(6ByUbr~SLAV{{P91gv>K_Ut(4Ot#)Ks=p)g>j7 zFL~+b@u9#fG$MZW0^EdC$i>h}G5-hq)r~)!@BT%GZsADkZZDF_V||G))6+bRafOcb zqRZpex-MkQ9=9GW519p{fl8f?CCwK~8~e5y#}K%AZ4XKT-J|Q3yn8#6_}*=~1M&Kj zATI-a8)@N!GC;1rJ`nr=<^5vM^pZsC7dD~IstMa{)me*qL7=rUMb)pGf!n_Z8McUn zYUh0qatce>(w$F?y%p&-OW-hq(B^#8=TM~-w1wI^o%Cfs@84f5%)pkm9yKhsEr5hZ zO89cyYL~@$E@`pxjKdzkynK50WIDw4NoOxWF&ygNk3zhSO{J*SuY6l&9d>8L{102| zz2i}4ijDo}u&a2qH2x_IrXvhEvvjy&Pv=PEt@ljPK$1Shk~hQ7?j-CAjf)Xal$>|T z8;vdd2|(K~Gvv&ctnls{ZSQRj^d?$0;`=AgqR`Q5(44AxAwbD{8~0$<2C0Nd83E0l z7N|c-0qFeZ^x)A z)!*&%qt=dA(j9dM3*yD1kXXd9jJYG0lBGpjhU}*>LJA6MVDVkYe+?R&5B-mbGuFTi zcZQZ=@xvqW?Bf8i!WU=&k!xshYT9u8H=t-4oN-CbUoE)b7bundQ-qxUHC2EjA^${) zq()6&zqBwVOh!@w*fR6Rq2^VwBUD&?0hU>Mo}yo^gx`M@!SaG4=Y}-VWJ1|QFPuo% zBvxOPGIro!rY`Q`BarFo#64cks4T5Lr;>{2DM@@6 z=R@T7UY#8Zgza&Jo|ih>eKkaSxtWnWoG_OFu3cKP9)Ul-zlJkW zcQh=sr*gvOO)O)ek|&yOBeMhnaJ|qJk%Z-33tos2qXWIeDNmmG8;UteBi#ypy?};< zbYj&JCav8t=@iaWv;T(FD0lgr*~`s8ewzLrUA1QvG&w(E>g%x_3&sZSqCs3u__`{C z)#yniL0l}up*gNM>9cp=F;u7Ap1@3)!CYE@zoE1$`+{OL%{Kp6pO1hW+vERh@xgH! zC?Uso^TG85CBVnDr_bONJIj>~C^vXfcZEL)r3KG*g8{<&+{uIF>toVn2Vc2 zbhucSVa^vGCmt48noi_#UqFPaoO=Hv=6eoUQmP0Fi-)ga5F;lTB>j(rSBT)c$#&{nSI_0U%!*{I^gdyZ)GqSj8xnl zLa#r#musc_f3XD1_KXWWSEDmBKswSd5&{f%?g{VzjBJ@9U64TV^Tq-!PE9Q<{(IZz z_jaG(!i0dxYZ74Su9LXJngaEB>o+*p{KAo=ndUfgJZyac8!G4kEU3pF$lFxqj^~Nz>+Ve4tB5dmnZEMQ2IrV0ET6US*a)~eZ;K_|L=sBjlbo=}1 zgyi4f&$o5WyUA_{{P(fIt12bNL}p4S@*7v)@8b0E47iNoOUqENS03{?cdjmARdF%`vYWm-R#!zOhZb?iC{(E5?B`tYuE#;V`)a{)g z4Z)Y@)chu{(@EOJsfGeh$;N39aUCVPafCXs9^{A%0R7{~b;gUAO?WoAYdW(r+v9EC zx_?fP=Zvf(Mm4MGdh2GK-H&sEozg!psCkb#gU?o+DSe~k8D|r|EtQ%pWet;PdGKlV|e^t^<`X_y=_wf~8RVWYS;8d4;9GNWOus{vV6t705IZ69fC#@%1>oV&G_ z0;h(drWIV0!w!(mUd09pEhL$>Kr@e+Pc#0^yr;) zqZcGkGiVL@)NIc%!`g0_JLEz#NJ=JY*)O9u<;OpK9RB%cy1e%P5B%}g+g7PI%PWbK z)J39(S;Z=z_4>bS)ZDqY(5ZTY$6mp8g|BXc)9*~R{1Cr_tBc`UcK_@%>$}n{BXKko zrDNaI{?9A3L#2*>myCz*K=r#Pq)oZ8wO2#9+2Xbm%3bN{LAr5v=LyQkpowyieSii3 zw;|%7B3w~&G}jwc@H<_YE0`$i?>!SE&(!}Q3;5gn=U6d(Dw_<#9Z z`qm&>XF_jPqC|$j;BirnUFd90?X#t9`;7BO+M`dk!u2NB;VHLNg6Ha|GCFygZIY+u z+;HfH8&6WPk8u}8Ug)2g|9tTl_`b|beN#s19h1jCLg9xukLjYhhVO2BqDoM3!9vtpa(k>bHR4zdS+cThLcrIS%Lw-$d50i%8ia10I5mFU=Ek)*Wwl(e zYF~_qQ!Rd_mE#e0ngO3LWMa-#pD?@WcQSTnw?+QKpMnz@u{o~ZVgCJxOLP7cHT2BB zB%c30tkm$WihK;s23=g^KA`Ly1_%5f&LzE1BI>p$ow4T)V!9k=PH110UUho!i;;b< z$2wu)=UpManbCOvn0hy}>v+s;J7b4lJ|XEOD^h)VGcR~mKmGjF&4+Sv$Gn5%ckrXGtf$1s3WQE4#!dyhIDdV&m93NY zfZG9_d6MaV>x!(;$DF%(Hf?ENE}8;^hW;efWxP*qI~&d(+9_?5_vpTH?Rrd;HIFxY!84K{ z5k1|HsjliysW_k)7tfogL0U5YBieZeU!`}6d^pJi>@KkzwY8?Eq82@H_)wLVn!)eY z;dd#f@UzGTnXF#i#ch1`3kQS_U9+!Ec3VPX`U>haxLns9d>aO{IqcS?pD%l91MVRX zJaxEn*wqewNb z2WPS$%+h8prdJhKQx1YImwpD1u0DkNrt~x%P1jRrzRBv2Cx1L83ekEp$Y`Xb1^1i^ zCn7DVSQ6t0IdSrN>JDVmw)DP|$LdbncV3C`ZTzlDTI&sCLxpPlf(6uiY`HA-i9tIM z<9|R;(Ubgv94VGMG?hz8%fpk(>IK}ymw^XuzxdSrK0QO(AEk^g;SY`fTX+s5f&;EC zKsF^Jkfnr~D2^=1m8-+*QN*JhCe;Pi=#b z5Aahx#pQz{gL5wLLA1W!Rh#xc&L6Xo4s*M|+am(%4p{(1y-gUSFu>sO!K2Vn08lS7 zWOlXJBqMiilNb5UE?B>o`6BPa+~&xYtPORb=QUr!7$vooO+>spF0TV)mDbCAxL`+= zBCID-SJvJ_4-S)UJ-+LIFY48=(gl?_01FNDn)6e47(;Tt`8#h$-+@IJSkc`4fYsZJ z#ScS-Jhsa;rq7{W2jfW>EfHos2N|A`zHKt`(`W&voK^=hIN&+fN+zYjq-9kYlU)x>HW)QxA<0LaUQGH2Mfs9 z{zocRUaq{z2^(!~#wNi|l8ajkR6i*cr~CtH;!AzJ;8?Pbf+h)*TN`j-LF5GjekP_b#?BA3me>tWfS@LF;n9 z*VcchsO1Sq{p~Gsi+h^DbR*K{z!{D)v8~V$)@RJE^u^sX+`xt-Mb)WyfKGAE5YqYQ zhvEc0=wbfQ*AamjZd|j!KW0M1HN<|fJFtmwP>wV}zLZ>Je*IE~^Go)-`X|@tjY`CR zTMv~aop?B$qrKapvQTgUnEQPATy(Mb{!{bfCC}>xsclsmc9gu9^##5TfEpfs*f@U= zd;76DGim>+fRx7Sv<~BmWtI_Eha9Ne=DHmE=pAmPWC;^*&dlnA&(W^nYsLk{K+Ad$ z2k)Ll#!ZHQ- zXj@S{KR8gL?&hb$6w6(ZuY_aKndd zDU$V&Uinp;)4H6ur95^W^FD@rhJM)8j*%bHRLtR>N;6f#MbuC4ZW1|-&yrQUmDtzU zq`&S4snw#3T;p;6+L588c$&K;h&}bGZ>Z2V0kSsjbr;MphLY6Pf6!0u>$TGYC66E? zHiWgG$dzk(7o-vQ5Bt8>wAw|WZB@)(2G2Fzchde;$aDl0E~oDlWM@aC`!TWA8Fx&b zn%XlNrz?}`5H+mx5EExV)W;uFUe7P>ANTS{MMhvw;wvW1z9y8efQw9KnUtc;>$(6r z+RHJ~c};HX#m;s_i^C;U`=FYJ{vumJdhA*D2@784KANV&UYpRs&?CioGv{5*{xv;N z@O=gcr3Gowhk1OGCs~mo;TVo7%-<{=Sdt3c@il*cD8jrCoUAFgW_S)&Q|E8Gu@e%e z$#!D5niw>79roAEo>NL5nAau{hsfT!>134nSQGl?!CvcJ$NRk;xAo4W9YkJzk!#8x z%?VvM_f0A`EV%rX&{j`zeE&x|S=*W5 zH%)17XxjCN+05=Hpw-yqcN&;sJPp-3216uWP8~HCXM{pBtT(-gHc*7fG1Ha1{@hz- z4k11p8SY(7saK|U&5M$!ynZhI)3cO(8eRO5MPYn%a|!_D3eEYBH2O$qZY*dvDh+Z- zdj297V*)fjozzj?+7|(~5()3Dg;b*b z)G;RBL6<#NAJaZ<_l4kEr<8j)*9^zPQj| z)^lgp_#VBLuF^L<_R|9G1`SNa4`+r~kQvq79N{FWR_z}7d%t@$W%K<0_OkD68iJoz z7kK?0gLk^iluz9}lYDgHzH)T`$RS@V4wXguS}wX`k|VNAs3Z#i?y5(}%5o~x_*yx~ ziSTvEFJn7)+f~{hRRp^4z&$d0^Mv-M%4&S)TH?5|gkgY%wPd_(VDNV9SywZMdntEUbWE;cYO!? zo`_#mT$1e#EA)8%+hJmwN6>+`zYII)7GV0?&x3pZRyAri;#Kaciv(OR)9`*}wk@x% zCD<7Ke&}Zq=p7k<$b+%xGb(hs6$g+z4`&_-|{mvZu z6#P3D*uSwC8veOM)!i*YG^BzIk+=K1tc?2eNc(!B6as>k_p(zv>h|zD|6~TJ*d<0# z8wjDet!S4`hlWtSHGMzHXdO(7HJ z>Xv8tuY&z3p0%OqtrL03R{YjZ}}XSt-o z`_**^Gla8Z3<0!DjA7wB7lo;(Gm-%6lda8>>R>aw-xeXYtPm2E{lplGM{-LKK;&Q!Bi zo2_vHru)?eZ$zb+$L%)613iVOjwY-k6KF-bt)V#}yBkXg(RNro$GR=P5vm*aNG#`> zh3#5@hrj9NvKeZgq$Umqu7cNNi&Yy?fHn#G(!?_iV$wHR>4?D?THz7wW>5tbvh?B- zKC`A{7eVVQNSMUsXp2&2`F@uC{onkC%hMOYEzm8CXU@a`ouiobg23RHccFcTA!=a$)Nk`i#n?- zrJ>EAAKtNhof7Vij8W0V6e*3$dQNsG)c?x^6jmZQnEeYZS>VQxuQo?0%LP06o>1bQ zTRcOu>W_CK?TIDlac^X{X*nJ6$sa)0X*i|uL#nVZH+UbKU-#9dw!l@c4^}y2u`drN z1PXSCP{ocJiy!`pCS#?)!lU$dkZ*eB{i!oQp_h5VSH-bXj|e|F{E_nnK#Fqc9*EDf zIxD&+oPJyl;&&{BC9Cv(wRti5;8PdR|J0BF@~sH%OtoGnND(Yb|7q$^x#;s=0mOmsr5; z<{Y=mHb;9eHO!`cKZqmxyB$_tQ)+XTuHMHY6v&g@HeeGCAl`_mIBn8l&ZxH<)6~`s z{=p6EIlM3$nEAjJxe;I!xw~sMpyKJu3%(dVL0eFb!}-UrkIL5|^T^(4DgTg}eUmIj zT(WNis6W?9j29_l4oe~ti9x$ZlEKnBW%iY-uW>1~TqSkKtO1{^iCcl-w<%I7ZgVSFGOsDLkoTE44gd1M$|B|HXcFC ztVX>4$Pn{}l5<9$qBE_nQ5RQ#aNNMqo?2y93~p;K4<4vyM4FBi*QZ?VNtM!`Q^ibT zK~C3(&Iy}x4xjZMd}H%%>z97I(zv>!;Sp4gE2pL*4lJ?oYid*e$&=e0+SA0v#zW&~ zH$BP0wyOttq;iKCsJ0sGZ(zrI)Hb;HIjR3IW+ZO0EsN4LEb2);2gcPneM#R;*3&CT zLGQu2**2V2;4FToi=OmG&44}Vz%#f$Y{gG|W9u0P@B90a_}y99r?Y$cb1LZ zZV!^ecQWvTl)`Y+n3^2ZdNTIc-CKci-f z^@#-9m9oC`P$-)-LTvwADrWZ`L%)4@F!)h>Hn7#%HN?1fQL66A;S1$1Nv}taSG@TVM#eqK-ovj&PAxswd zJmx;EFUq4vbs{RrBX$LnnMO{xUjd$K-l`wGh`=MKy=T_l1!x&_tKYGm`cp}H_2EC> z!e>X#J`c>DZ03iGJtBv9cbWr(A9*;$l-3F(hq+O&gNjK;EINvZ*{tC3jv$nz@cQ9N7&elXI@acm;b z@wFv`0u~*{E>R0SJ%qW|uY|JS-(vHQkPfZr2B}!!Y>q)Svv{%A$)+Nx(^$7Q$eqU< zYY_FxSsSGh)udIQp-&NNaR$%kaOup9MW_ocACNJ>rL~rnRm;!ip*y7))<hBCH8g)R$ocxNE2)kDg1J~6DWbfF4#CVxti5kY&1s_NzAwO7D#`~XOJ(<$h}` zqJ8Xj%Ba(OwDU9O<6Ew*4^NZ&U-F#OonP*yYyjY-Q5I5%7!+tz=KY9R)Mg_&<`_MlMc<>V8)QHr?>S- z2)gHbUcPrdxjl#VCwgeCa?rvMo~$v#P;W=9bEGI&O)P}oB^#`>f3nkIJhM;vq5j?W z(p-@-+-cdlI7)Hf3gEluH2qs+fLZ$WKbu#R*>kwLvj1G9YpLf<#~fm;`?bo~gxPrU z&~FH=)^$yxCkFnbjXkpp4&jFeqjD-*RagW%{;LT^`Rl{m(0zjvHWU7A3EieI)NIzY^ZNwz@Y%7^KAB8GU`zlG-AwiO4z)XG6huG0l{8E5t1W||xMwyHDG#V5I z-zxhSF;?Dsf4C0y{n|x`TcT>sF}9?XS|>3gX~j~s*aq}HKMg2NrWm@h973E-Tk2WE zwF1=-#0ejR<~Q_hVwzLt<7CTLOYY<8R3>M}=4`NS!6-ShJ74AT?2#!vdWoNQuZ@`0 zZDa=5;XFg4zBt7GOHv9S2=UTcZ5trzMy`nO_at_h5dT=f`)Ti*!&#E?ojiiN^P(+Z zp6#t40lL$rpyW#Ai5h7xAfff#%hEjFDJ8{a8o@v6-i`A1_6}gD&PnGb6-aeC$MXPR zW4L-&I>*?szvq@o`)U8{mWI;FkM=i)1r0h^sPD0{4RN*97JeS@tZ2V4TA|?PAz6hd z+!z;WT6E^YDU!NIGVoWS`ry&bAI)(iY`j82**iU=_ic1yPRjg2w>)w*OI%t}sA&25 z=!Xz|$k49pTh4s6*`^!))_iKVWjlV?NN8BCyU6>&oKnwkj<;{>Y|N~w7AAH|dcbgWTA!s4 zA~fvJsoP*%_pkZ&7v5VY4cg&bwZ7FvMH>!f-Q{4AzqmHvo*0RXCIaeTT~d z*2Xo*)r!>&u#2)yyN!Mpd+yLW^O-!s0P*{S+!L8us?mPkQa7HB5;@WA&3fV|E!|{j znddt%dTp|RX`uAw9J-?sl(cz%fyu7U)twd<3u=Dxo5gfur;Yv;U%7Ld1fnY|@_1lh zP8kWJ9hLU<|6pi0y7cY8t^2e;n zis3?1#0{@azNw-iI!x#)>}MP=D9gpoJJ2(HUt0PIm$cvNKbju$wEZAur7MN7s+r8C zmny`2el$|C1DQ7R#@avNPoZ(V^7KoBTL-@)>JOoAD&1J{RIjQDeo0hlc9dQiwZC1E zzaNIlQbUV=8)4Pk1lD>D;rE^&%&@Eu>;&ufpQ%Oz5&Z`BU-m@s*WYQwN&`0yt)&^0 z=&j}Vg7~F3=;C?dfq2TzRc3Vro(rX1`y!XpXl)EqUY#kojT~tTcK|3$4&OPIv+VE6Z zqcg(2+~XV{FZg)T1)CpI+Otou7Vy)8XgUh3C3zb&P9|gDs?`RiuYIKzTX60&e6$|? z3G(cI*a>A&E$_n6k?J9D-w?S-9CYMN@A+FElsj9iVV*W7&5V>J3a7gIDtdH) zsG>bFrW=t#7)<#6mJM>km6yVe{0`hIj*T(o-mAAt|*>8vZndX$q zw{s^m4&J#yUACkMnP=RX0X7eUUKzz!ir!Z;aQNH=_@&!T-VoAOdpxgPq-GdVk-k4$ z^V(Y9w6+nZ(O7F?h5(AiH;GEt$2MyneBhykdnfHTyDqxOIyL?ngrJQ<2sLQo(Dev68ZodN0O_GlCi`Ue zO!eB$Jws|hw%qZ^R0Ua+g2O(*hO@?QwU#&cd>)K+kZ?;8Pe$pB6J{r0Fvk8Ai%JoD zcI)DH66xDqOs@ZI9T2U#)JDTVb_Vk#6KPSnx_NU`0a{SMv#OM3OU z6`TI;O}pUkS#kjxwgt`2y&}+vR#<2@+zdFpjZ%vuM3;S;(;;~M6=N*-OX;K#YuzC0 zO09=-`xQU@9d(=Z7dI93zJE6fLm(Er*BIX>b6RkNu>H-3FA2e`@B7~LhqfS!C1ZZy zZzJANXtW@Ir1JM>kwF36HO~#Th7R(3m4*!CB#96$#U@vVF`o)&aE%+^TW`Kr=zP@9 zaUN~^st&BzNUS&2MxFyIc7G=$jz@YnvsV5j63YDJn%b%qMm3j|qAiIa|1ItJJ^6Oo zqI+Lm*-eISa9PQwofwej*)|J2gSQ@SPO;@X=l8lv8cEog1nR24eu0D4qq{sKxrqVo zje~#V(H?PDS$co_*au`2Hf-qc203C+!M{r;Ic6skbr8Wl?1MHf5v$!xO0}h?wr!9! zmA7+jr}cux+tPXzTJ)-%>rg`sg(O-HNab$9C(I=wPs{}(*l^1Bo?_c>XFAh4*ckn* zPJ3+w(7gOyqjI-MWYAPpyc^RfCROdGkIJg(8-8G&7aKk$rgqA#f_&s3L++~` z4^z_0Kac5W?E5f+p+i5y9m5?Q?tvG9w0oyPUjNgK;DSGuIMnN#Yi+14FxMaK7WilW z*f$fwxNeThkh|K!rES3dH79h3fcDx~M6L7gFmHg_U^yBTQIP#rVl5kfh;g*qmDFKL$wIJ9~-*= z(o*xt8#Qc1AKe!6;`o(8E|`Ah=%TRd(w#R*t1?ifRf_Xn zlL0~JJlh2kV~~S`-5L=O607^3DZu>?>;ZSYRVVUf!e~y$DA=91=AA7;r?wL|Ds|W` zUH+707VgY+o{8$tv6v40oqY;tOK+>$eHPO;a;Fm5dWIK?E72?l?lt&AuUCbm|Mr3Q zlq>FR@{zqgkc7K@i$xjN^tFc*8>OTYYtkw4A5w8DWJ#+{RbX}O^1>g;nVC z#$Sep40NnS9MN^IuBDV$lYF9$@F4qjEnE`cP@u;{#@{ZUiLh{&uV-R%f;BmF>Za7q zBfY;tiD?(bWV<7cAPNu%Sohe(HXOD9+BI z$xiaw;53M;w;rJj46m9b6z3<=^RrrFpFUr#-%KK}1fzg!ajCIkJ(!4_QTgSFl~;G7 zC@uyh$y1u2a}AXw%h#HRPlP_S)_vdwh<)lSdYDYmjToBVr~L2~`Gzo8d8Z!ly*)Ot zmy)?-%P9Kz*|v9~yOM(=?{8Nv6MczZ5|_HZcz}98fO|adwF3t$2)y86pv{|5rD-cF zF*(AbJDH1LFAeu9ckD2(cc2X{Rm2-e5OI}@gkT=-pmZ!J=IcIpNUG{8B1bv>ujbV7 zdbROagrF`jbInA0^s2Kge;A&0uoR1q=A@ZTOEXcVcrmXDd9NhAF zlF2Xt1wvZ)&e`gzSEow(eCs!4K9i{TS(k|LYi>u>*?lTc+)DJ{8Ejvc(pqjqGIilW z4#3`*@Bq)%*rVQenf!w(#K}zMp?JL z25!rfy~6k>$9#pC+W`m*yEheXd^8e-&wPFiXzN+IZ$6Z~W+k=*SzN}-OKSEF;eA*M zJttd@v6Js;`F<6ldusw;WOFBiD<%uEfp$Pjiz5&4wt2{rj}i*w_k1Tu{<=6SgSa|ih{YZpKAS@{J-#2V~(2~{gxmwDu` zhN+dU)oe*278&SVr`#FN{NUgnykUxOj+0d)aGnc89f0F4FWsBhu?7X)POLgl=Ofud zp^LjgGjRJA3_CqaPru!&nf&JP$t+jAbOPowPO7K}#CKz!qmj|G(6fSUbHT5K^*)tY zsy4jn7J}r?a3BnMwjRx!IsJdN5z!ew8?$P}xPE*S^$tQ!tmvlBAwo{1^h?I|=|vZq@+!^5l5kxRU7%H+M|R$1p@?gu4i- z&8O@=Ue0RL@804zv=52^2e|klM@nPfS}X$?#sD%N<&X@d9q}m!r3&F>X2g5cxFaw4 z^_jw%M&ZFq(EO*%h@z=p+%h$XV;Anwi1lwWM@Yh=%C5ROzlTNL&ws@f0d5NS{mOFF zvXl-ST0ZTuj#E*NefvAIw#_xAYe~eD$!PEA@Dh_#>b(a*_70$Si{w)A84G=G6XQyw z>{r=EH08!fNrd;5Qn;}baesc8vtYkSo3ta6Xxq?^S>ES!NT(3%VCTAuY(Io(sc?vT z*`2kc4<@r)OePmB$y^N%Brno;XHDg2zhpHTt7K05;0`mHJKgk^XT#OPuj*O#noHXyDc=#zOme(ky#voP=fe1$U9=1I zgoi`Pq4EYlFVluwL{QwrRk#DMs%(J?T+Lbk;Javn@~6w5nD{%rNU{_J8Ku2Bd1a;f zwm^*kznBDT?0Id>Fnr>%J)>}r{U?M+yx)S+qzxRJ-7K5(+T^YY`d303IT!@R2PecY z#WH5&TB3n}ULGa#R)qHy0JQ1w1>DhppzK#QgGpI!RD|1lkFI%*=+%jfcStL9ql?;$ z^!%{_sTj%GHvX}WQx>m^r}d~Ztd$AH{>viT7#F`wVrFtfyi4Tns=Y3Ca>@uoCJuSFt>Y&**3IhOB0yS-d{OeI57>7V%xVoguMH*Cso zPJK&>vfox?`EA)hu}KT<%Lj*|US7gH-YY%tQCOd`v9$}Tn1%TAq&?!nL%cbVl}&OS zt-}Q_1ZYS-D(65XQbZ0?4h2Aui#2cy(8d1BT%l1aQUwgSqX^a_uM-)&)zC0u@4FXC z`%LniZ|iREBcfOEp%tN*i*P2>I`~iA|145DZolwN@eOz2^8VN2N`he{GgpF84^PYm zRf~9Z0J*~*cNkPMZ6js7KN~T0OFJ>F$6O1Zeh$5l zc2Tx`#5P)$R1$-B79^E)+gqL|&BT2EXk_@K#%Vy>AqB>r)QacgWJ#|TJy*Q za2>wu1v1R@@`*ux=!IJFLh5XT;&bqRv zkwZ7uQJjI6AD5KYzm|pOt@UEWFO}m~ya0&o$l6%9ne}1ScHIY^h|qVKx@_#<{znL92V4#$W}RuA$>F&`tdo&uWx#DL0*WT_UZ+*L>ly+?~Jm2 z&0j7fa)!=O9iGJx4AeeqKg$dL@~Avmkk%mxp1+ePPKY`x__8m2o^U~PX7bG^;6=i% zFEtNA(^AE#p{a(5k1f*skG(q$b*GW{sFYXr zUE^Mi;-JV}kV15rQdDqKc1;kZ%5whBMnU^Xu2J4%z07INnM)!zS8E${;q9r)>h?lY znRPCp)-PX@08YaQVprw|?B^^%evCn-orjbxhFw-)D=)42BY_LPF#Ddf8nvo}aunMJ z7ID&hl~mtWumjh?xgs4>$uZB$ns1v|F=H`f3fEzrFvoygYpgVYbb%c;>W)8*GS)lG z&a^&fDn=f7XY(+4!=10b@W83gqPC%-j5&rAnRi+mw({r26{`= zcN|=eTk(##Q49KBKz2i0D<#bv%avkwnG4aBw*DQ)nV~<=pa$m&Gw7ysE@MsPd!J0H zsQ}*<{ckf)g@K`bO;+^HoQSY3x>T-^Mw7+e(%&JXrPjY4b5Pog;u=AO zh^j268e28~Gc70L+EnuYQecEt4s=M6qFhN@TqkoVo>nt6cdCWCVZP4j+tmpO_ z+H7>0{+ujIQ_2-2lpT|U9nBUmiT;WYAQYCOM7veeJmbCl@okG*QL811IYY*h*XGhh z?lrew^?i%G@`*LJCzYzKw9pDcGIA^zS{cWcY|k$9i@C?_Ok8_K)~VCQs++^zByvK$ zQm`i^a%Ox|Vlj^5pcz@r=6XG9VqJ4a2jDSmRU0HD~E6o24(414N1dV zJyx4&KO={9Vu)HLvL}E|r!M&K%xFIEw4?QFXuT&j@xsDoh(5xKCxFRpFk>E#c&ymi zKHq;t&cA+Vm+aP&rFIZ>eqEs+_5;0SymE;b{DUoA3tmjkUT+6XA$%wo&cn3o{4LG1 zvLBRw$HF_lAlC*lfNWbWWC=+$0F9{R+45}W<)iEk&*ty==k}o=^wXcjEdL3H2TF^Q zS)+Ndz&soRgC4dy1d4jHc|=!uutjJi^o3NZ%mdw&!`;KhcQ|A1-$fBVL(#j(v%im!1)EZckbw>X?8l%QSBCa>g}EtwPJa`KMcUFdhIUPF|vw?IVxUZ;F(aDET0DO=z0fY1mNjO^`Ho--!1xR<1 z?Yg}+tfu|53=8f7IuUb3Kg#UEaAM2;wPvpJn^xJC63>TZIu?s)66?o#gRSW;OxJ79 zaC@vl*~uA$53@Pb^4M6?duB_Us?+FMn}f+`kRMBKP#G0qzD|!bl?k+wBi?N4 z`a=zU@EPIsP81XrkAyYE8PmS3#rN*HP&1#*8|=^A$w*+6`^QrL_SqRJgwuq85Z2@o z%fu`Zc=)8ay?`;~qLB^|nxh1SJm^1r1q`)pEp@iL48FxM+j7!iC)6D@Kx!>MT%qhv z%^tiCU%x5oxn4=N^jJOLE{lK!JLBQ4d9So;CB&L6OW8P#AfJzLP3R+N@?Ia@^D@PD zy*k+^w)LPY*#PH8c#jNhs?O|*mUJ3*3$k0cHsH17vrhRr^@rnECwEuYx`Rr8}5)7rawR9LNkzMb1AJ+Ygp zgGUiJe>TaNHybpgyzrI#4v+$Oy@2_XhVZa0r<u;&j4rt+*xnbk~}iV z;rsL+;5Mp?jT_f_&h-P;AsEsqvt9g-^v)2yh@=!Cnwl{^Mvcu7lL8jwROanMTP}Wm zU&G*6x+nREq<2Luzi01xZIimcqQ!j8=qSz=@a@C#NLI87@7f!l^gpqU>tFmFM>Wer z!?vJC7C?UzE0&+`05436*DALdZo+<~X8$&@xRQM;W~X#i$!atO^h=YoPrhV`ps_C# zIk(ZE*s9iB&gp9N|G2+zL686I0wl6NCDgq@ob3p>{Bsx#TdLT!n8^wP@qW&OhyW zHA(9>S%!F|zbMB%ix&-4E z0GTGD?mQ*6J7a&9iVhp8fDBC=)41bF-4ynSm-cA-xz~Z{_&0$-K>59N-Ccyupb zx*V@CZz$2c>&sD?G-1-)0cgao^fVnh&jmNpya%O$!#-A2S&#cEOk;(@iD~WC`~++^M;Qw`Yakx&P>Tu$lYN9TEa>2 z>TSVf|5YP9wb`c(_XSo?eev)oDS{6FzI8rvN8j%V z-ZSTy2=&%I=r~stRyC=Qg^}%m-01e)EdLXt{m)`j)gELawEIaO0UcD<-w|!jo)|!u z<~{S}Ti6ijYxG`dxz@V;lbCbQ3&Zk#n571r1vsYw@XUIVh??hohtCdOtai#xcY8D^ zGET^6=K4%wVx@Aod5I6s#P3sh?`rU-;nbz6I$uKIOwr$^s((M>1MNWHg>E1Noi23V zRBFw8aNF~FGzK~qr!1xE`3U~V)b@Q`pg%UFo_O7&R;2r4oA!KXGR&a;XNECU3%-9A zd(q$xkwZqEd}=OKV>u&!Ad!P()KKT;8f5V8C?;?J?Z4*myU9Xg5cFId^^%|Ezl*p9kU zg5#UHeY1X+c~uAfWI5-c1$Kc{@!LSv%X1c*!D)H<1^yQAIXkU58~+SKV$T-F3<(2> zRTOP_@_$X?<#0ev4fUSaRTqCI17AcHVs@>NPKt+|t5n9DhCA1Z9F=G!ztM9`T?sXn~=jOoZ&U7E~k-9W($es}ySI zk`P5ZkXfS9@Uv*H)k!e$>sN0<3;e$+FoMVI5ge`+EAV{v>Alx=)OhAn85MYyA{(F_ z(^|uN5wqetl;3`kxz>A{g5UpYz20>N`fz&pAp3dkvI|RKGhGcY28&N5py{vhz{yzK zzG*{!(6ETr(oX2jdTaDqEZTa(1gvUy_hBXLf4Qwg>8zJD=wWW?2ELHk_2cb8B>b&` zJUfNP>VnpfretojqnwT2lp>3Y-ww1LEtKQVc4^P(pql*M+AQl3Zgi zdau5WTscb(Yii%4(Q9Mxs=+c-7_Zg|b}2>;%`4z-(h7&|=Zs)hQUVzyx9$?CKI-BQNVFYQu9Ns8I0 z{x0dpjzhqURP_Fu8k1jzQer7$C8B%Ur6oRj5R&#HQCiwodBtrjy1hD@=T~s(Mw+=ZH@se_uP|u`;i;&w`s*l znNyYh=?gQDXKNIh9%&3w_!MFsK8?tb9`~|6@-*vAM$NQG`wzZvOHKyH~9zDhVzihJiNnlnarnMQdMgpc}p z;4+IXoh#2%eOfejW{SkQTA)8}dD^d5H52PaDaVmB z@cOgi2ee0KFe{ukN2^D^Nsp(K8LkZ<3X^QV@X?aa!6m* zf$d6aqP~?uZN?N!bl{;OT|UCd2}$Rz^4tqCmzLt#J#y3KmSMTj$#7@g3>A)&Bq0H( zrehb?IegNfHzOeC8E?euc8rk=U4jR^GdK?`)NzzRMYD*;9~WGGE=BZ;MWiiT%V~-C zfF_HlF0)FvTSgs8zR#mMV&V)sG{`qVP%5>=Wvr#_fY4lx*lk*5Szb=NSCE_N>)XOf zATaYhG|++>*kcgzOHq>Ok;u0yCDmbYEre9Q2H2UpO;yEs}feD<0c&^{w^IszVm% zXDU7vFSjgLMOw^M14U@C3`anA+H_vXM1#s8!^>V>BoG(s>{AvDf(?4bZ9(_->FU3n z^hTo=w!%CdDe18jEpO$93Lq=PO{nEDY-`xPSlkiztle5Mq-v#3@%Bl$#5*RAk zr@?d5$xHqhhVC=E={O=HH1G$nGUL-nBQ=)oDf8>kys|(|Vrj=h*4U3RXB6 zad$3_i(?G$oiw9HDGrs@LHjCf?K;ToDk;I(K`j5`k1=zY^#?+T!KBpP@ECn&Y>%;( znVeVAXYK1vo|7J4CNLTq&QWu(_npkxI3>c&XYf9aKwSw4G=q1OYqfcL4@s9jMa!wi z1W4EU<}736f=!5XA}o_tsGEESgR)k@Lt1|I zS!>P9S~XFa$jKzkp_)bU)8!v=?DmxZaUB2xQkmt)b1#q zu{y80=}7R*^(J_&V8BGLUw0O111!!RX_PKYvfF(u zmLv5kmROK{ahOIaE11#Bb^t{^dCLX3$x>_2z z4l7k)PklUO{#_fo6=vpY%_#pf@s=Zn{>>v<$zj=K?7iZ~{wi{$ywfJ8H#K7^bG!Ny z(13?)(uv~l??qBqTr5l2WZNOW`N^87s}YJe*UM%8yd1<+i=4G2Jxb|Q|Ej_3J3?ci zpTc@law7*GXy!W-Q47^MOO@77f0;QD=$`2lcl%w;=+|IAXPImEHUhx`U8j#!rnqz@ z{n32@AAOY&%RfGh+8G(TW<&BmCGm^#l6GPl)qnmgS z_SIIhR~)0yjpul$sQvaP+A2|v!vBbk#6g%!Y={rLv;1xnm%%l0Eb3z}X{ADANK|{c z6J6)bkBlxKUn)>BT3a|%sXdgkW-*rUr8bK3Qq0$FQgU@#Pz_e)xgU@a*>=KQuP#QS z^L8!0fR#t85xmLA@}wfI(IYvbSL{9w4ovZ23BC7;3QGHafY^VbOte&@>mz(tXSHqE z!*!In18-=Z-7R7BQ+=@a#obh`K`cwhTO;PPMURKK1=wl##?FVG-u+l3V-;RQ>T}o_ z=%+1xG+=|}Yr-6S+RU6$!f)Y?Z3(br$K8(J4^X-^a6_|SSyKj+e9>3x9c%#j2i;Yk z%N-DWhE;&~g$Eo4;|j=c&yi{gZ4_le2Ds{ycU_U4h5urfpb`YyJL%(4UbC}vYQq%! z$A({>;oZ(td8`GX6?!DiQn1P{726+Jkcm#vgD)ui9#*B#S*I)zzQ5JMXbxf-0Sxv_ z)f0iL{}Chyz%=m~>s50VvB3=Vqtkk1N}wJsf$MQDX;G|h?TUf1UusgFB$qLn5^|Go zz<_n&&#rS_taubq8s>PZm5ge}Y;g=r*iGbN5de9h7tlV%GBIy6OU%}f9M5*J?u%4E zsCa79BI8q5jG0_MSPY&B%?nW+jz}pt=do%G$>f zwO1%Nkt>Mz2y=40;GY-!zi#bsn^qi74cB_16C2QD*#2UpV8kh zw|9Io|M>U}8Ko~V)KoM1mP)OJgNmTDy10tI^0sc`s;v|Ql>Ho79OFM;oH=j3vKUCX zc}G^9!-)6jgIKZ3{*eu3WbUss3=@7qNV&|+l`DJ5{r@F!f8B0o9h3bh1j>Pp^c3Tw z$Rua87mDV_N3ek%Dhd5J1Nwj5t&YA_Aj15IGTR^Z-1v;=u%;yh%J!F!UWi(vAh3Cc zFP>VzdHdk4fg!ks-5WRlRILBk+7K~Gh?qx-%t-M|3{d?AFlB`S3Y?0rI9hZV@hLCm zsT^e!f(Nw~mpJ$CR`Q=7AFyURpyqmGc`Pahx5mD*mWXxK#Wyfo^Y3xiS#h9p%7(pk z!D!sx>4GN<{L?3=Lx9vU`TDVu@RK-Qq5?1XX&H=ouYSt2yf7hBIRYZx`#A~Y@B*<* zMAkjbx&H-31H>ooLnU&`(ba$V811cl60@|I`pgQMz^_LLlMohi~vdCDR@o_zn zz_sw-?zYj)+kXG)pMu402H`jLrJVz zIqKQ1j7zXY`WDV&qlu#QI8lt5?Z{z||2K#NNU>$@+O&TUuiLbxq}jhfWsax=Bwj?~gb$kOJarOV^x64Zr}7osC8QYp~Yp%LJ*v=;4Pf zsQtfc0sgP3mRZK+Pa}I}^{D8VgDa>2@8saYuipR``>hWOz?yCnCi!NlT4JyeDTRGS z`T~R5&RSbS>9bw7`;(9VF=Se>Uim_;q3R7rgY0+o5%-N7+7V-?E;7{Y0|iTHo4t;D zwsS{eiD2{#*ia|`{Ykt3AJ1+_X0E7!6e|oo#}&ytKeC}D_zrfM5pJ{;jABQapQI?W z?T~R6*Ias@#BNDvjAw06{X=9}NJaQu3+Q=5NzRl9Z4{_(6~PGDp1{V;Me9}AzTIn& z1okv&BW}}Xjv-duqvtv1<3C+`HlFtb;XHl5%)XZ5qpDwylfZ;>1jc2)4q{(9pb{S( z0Jft&N>{LBbB~C5k{HTgM{lW(2&p~op8ikGfp9LJpgb8`TZUNG9sV z)Mwi3(3ytjJp=L8ZVa@hj^uOEmV^o= zomEnrKyOy1 zezSgHdx$e^b;B}}b7R?d_p#sq>0w`?^M1&ag0=clqV3P=GD0`sg9%80qWf(q3wprz zFoFZ*M-M}tQG&}a&HGv1*V+gHqdOH?Lm%6_{}4X&cN`uyj1!j2u)Fy&8d5>9ab+X{ zfN@XAO(5_5<{6p#DS7G_4EOt9#mrm;hTUj)hbTrRM`3H4V##FF*xkX?vEn5Q@{j%* zZX_Zy`vZ59c=U0@OY=m>sU5TK!?uZOmlJlay3>`iaIGJd>P#3afJDCqJndxT1#XxbESsDx&k{un&gOEb+qj& z+B(r?V+L6_jKtC%m9BjIr_^bM_k-B^06Z=sz^x9S#|v>aS3gVv-Si_HH*DER?3czv zdEoC6NB8~pM;H(g5jafYT>PiY`pkelq(wutCjd%7K$Fz&c?Q0}et6$y`@}LAtfJ*% zN(!Fn?djA-G%yc6a23;8!0Zd`;~JD{+HU;L)Zf3N<&84Bl+D-AWTD(gYmdHF^p^{! zK1cH6_K;7R`LEv)*b%lDxyr;}!iOlM1-2Eo1Mc+hg;HK3Am=a_o}R<}@z;+h9%D3v zSo#2Z4NEu~glbe4`(gj`re68U-*(w@ZwkWTO?;<}wnFLa0oK+m1tO+vG~irR?jCuf zISSg9Z~ko|``U;8jH?m?AXrA|cNOi?ju=2|sfE`tx4-}{_rI=UUpw;=(tv~vWrVRN za9}yQi|D6UUc@Z-x6gn{AA3F%;IDfDTBBB<0cB(i9+XjE_<}zH28I8=FX$H>&`L0E z?-TO7BL31jV6&f`vr`I8A5H?s76IXqVEXL~&ND!Hn`7C7%-e52B!%!}e#&ZlcPnmpk_h|9oPE#iuu$MhNL3`2N&h-JJ zCx#3`kpe)pJKS-8>~|5I1yeBAS!3(0Y33PMr31U%!L`sG}e%mA5j zpEAvhzYI`m&qwXW{dqCMoeIEd14n58pU>vS1Nf5kiRb=PiS1B}w%`xbb;w@7t4hZ> zBh-%Ic8_FGq3}enJgP}u`-J(p^6!Ev1b_q_upTevygVKB zp9)$g2qx43VpD&|gUI`bDUN?Mo#l=H^n{J%_m1cY+#z*saLE}U@ckcf1`1uR6NB4{ zrqqNsnl>;T(Q*$%4qpn`-tar({<~%*0e78!<<3VOI5fR-v8;jREr28M6hPQR>#)~f zC3Ii<5k@GWDJG+y=P>$reVSDgaq3u9w+;wC2&THQF#+WJ?LL|kFAdswko5K!yz&~Gf#wp^HLu2yMnX2Z-wryLVFg1&gEk}fALQufnooAiqq!%}`p(r-+pMu~5%rNjA&8-1T{M@+bq63e#4}u4FBSzzo-!66iz@M~q z-Ts^HT^vLjlWcb)iK+5I;D^S)Uu%aY2plA+)qDp;^Ug;w)hkVX${gq#P{?gcI(IS{ zIVtrYXq=A7{Uj0u^W!Hu!V&;T9cHZI?#0b&PW?_dTC}DhNMKlR0vNXZmKM1lY?!$~ z!gs>HU(5tBFfRKa(vC!k5*1RO?WxxnnZ2;Sr_&LlfmQ%FBa{uq*Y=1=dPwfg-=!k# z;9kV66G$!ezD}StX#4(%@4e3pZ1YogU-g7z@06qJFZ>$4GiT31=%Bv&|RQpP+mZugXDj=ZcTrV zk6~Y_=qs~K+B?|eC!yv^Nug=ZfD5EN`x^rZd;*-nIUhiodFLwtqWdNSIR!jRHH=su z0(qy5JS-X02ok(Yzd>XWH-4Sl4R|yX=%QuB`#-%_)dyNul08LWP8;Ae4}$Z5<0j$Y zls*GqjwX&Un+13-k47Ba+l=a6EDBt^9k8&!=`(m~OfUP2ZeHOBA@{zrw8%_g8^f2t zVpX)?Dx!x}*rqT3h8CBB%}{{Dgi>6!BVexkn0yhs`2WIC{4UV)zp1oN7vL`9m_2Lr zWO=XZA9)+~8)VlP4l{y$+x@AaKU5OA9@B}y=+9glzwk`2{5Jyz3U+sKy->_&z%`Y$ zL3g9@pz^0LpEho=(Yd5C;BQ8LQxu>8%*1NULDJJ_&-4y^=~bk?Q5Hi2_Hs`Q#7mMQ z!MnzOyC-02-`+&p_S=9s79iOb^YF8m#y^J<(|vviE1>~pwpvRN&c2FH{s=A{+~;=T zBs0`Lj`{fOI<_fitSfui_sVoE#l9?((um2d+)zHAliUm&_|6bD5LRfRg28~3h3iVOKU%>O4W(C;m&eexw2iUDe1wQy+Vb>W z(UQ)*%2AZx_Iy&j;Fs=b$u=f7h$DQPRARWzmhU>#rZ4h8H!logiohLP1BhlfUKEXA z3P5!%uvsP=0QB9CEEs zxs+Bw2fpRRa_Z2$r$Ypnn?Em(`cwHI*(ShHnkb1>(6Q%_%gWGqLf1HqEI8yiW#v>v zf=t=0ze3<_{H25dXV8lIfp4R($ti(mKbEe`A4z@q`J#3yi&*9GZf%E>6Ue5PFtS}A zu<1sb4G?p_gU*~Z&s$BmA2?uXODUCUn@Rr$)N~#uU%m6bdd=5A;`XFD)dgM5=Id^O z&^bSx{$Yf1VhO1PX1H0l8OJvv^WhYw`i<-oq?t}0iWMT!{6Gq|GT8aR+{0t+(TwK! zzRKy2tffw|y3B?C7aECX)T5=eo~q%?Svk(IrINCD+t1u93R!n{sf26OFF%&f)|C}_ z{J_>fpbo`&4ivO)z4#PUudPJ0lCJ=?FlZ)ZDAs1 zYgOKBPvi@n&Bk1DW=mN5NKef8z`!@n1ZQc)+DzLRpVx%7ZJbA`8h_o!sItu9;}&JR zdN64VK4wJ}QFz%{^^(~TZ-m*2H1QMvo?AtD%w_rzJ;uZytjq8J6f{3Y+Extv3{5Qc zRIPlo|DVleA`p-b9jCpG4{6ErH<}ABj{(tC+A*F2}b5y1kg${wGTZ2;lHPy zFPx#i!z^O8R#-p%0(gqy`Ea}G;gi~hB|ps2*Rb{ui@h`ARB+2#+r^28kGOwbJ_5vK z46?XY-W@N4r0S?#{Wn@?mLbg5_xpZJcS}O=C0*|*eGrDj^bS+*9mhM3mFDy^`O*z7 zN<6L&V_3iB(2L?h5kRDK`)SvK-i^MyVqquqyztuCls)kO^8JR`YmWZ zbunl!YWQ{q&?&DE;P>nyhEI<6;-p8E6Xd;eOPq#!ewf-qWqJq>gB?d@H;-x0<^~8y zc=&wWT5m!sjUqYjG1L0dN;vCZ1_MjI6a%J!U9w^C`*^f;$L970bUXF!WUPh%R!c2? zDmdxN1B~n8j5J`Cd!`9>lFvJ}HKWRw=nUn*9+BO#_=r(Ng&BgIZK%gYr`hx09tF6Nd!U{iSHQU?`E{wV3O zVBkQjzx5+qmBqo=&5eqm(O#lp{qOE%zh9eNovMBMa}TwP(DLX73*U^Aa`FS1b^Shi z7r-ei`1VH8Ynf}MV}$BXbMLhl3EdtEP39VEF1>n7q-^w-(^zHn2Dp`x&O8$3z(Kiy z!6sF**xmrCekHM)>v6ZfhVe+Yj+Q#P$Xu)VzEVN(KPBT_9UG`kSy?9g_4weZC8qwf z_czopTPsERMSU2NbjM$<%9&mE+zle{jEkllqMhwWyO`Mcp48c7WYvS|`t~{e-u1aI z!|gr&P(`uv8K%gE?R3p(iPSTR8kr2s^<5m_dK@cjr)SQzU0VWb zH~8U8IC`_b_7>UHZ$8!Y$93HHn(?G-FjihQO7JUebZbiV$Mlb}>?tr$nnkRpc)*tV zsNcj&Ek)4*6bnK7O0qN)6r1SucQV-GZ{5zN;8xn}2ua)I<;XE(WBMPA>(>Zs?;uW; zGG`~Un1~6Rh)Z45r0-&m#>m@5?S$G~wfS|>rdr431zPLf&P?u8$@aAvUA=h0Yx3CAxMX;Rs8Lq3a&M^DRQ#ymt{*HBO%`1IP9N>WJ{ zpXoD%nP*Ex?tH_`;BZl^JNCF<`YHJhfvURu4pp^}Zz-{rSS+jMT?2KFD;uicOTeJc z#cfqRdBcRwb>Y0>odYZ<(06HXiUV#XpURo#^D@Tz(;~OK1QY!2%doCd(hif2i~c{d zjV9ZUcrQy=6}X4!~^VemJbbC5|fkM)hm|Qr~?#{=k{qlKT!gXcEXrgedP($gw>df9w%(`BI+e%^$~W zlNV-NE0Vg|6gH2^bYl4x$KT6s$eLJ_rFZDt6z$mW72c`zeOMWI)P$FpfNg3WZ348q zv~@UhFYRKX6Q)a)uA)>XtvI&Z?~5;L#*W?yhS=;)TBKpI^pA=?bZRWWW$bckS!G(B z{Y0ggc39%)8?KnM!IjbHZ>>h#CKM%{NIL9MXH81*oQp191{T?#Zrs1eR%+4}k18z) z(YNrumf0iSB93I~p+Y{-$rfkl$)vD?%gH&na6PrQJDeu;vBkSg`ePf*%Rg_i5!21h znmV#_S8#RYQhI@xmJx<~_v)bNSf$;HOq%83;EqDs zsEtWon$uWG*?~i&{YlJr<`f-@@myk8vyeoLC)Q2O#B$kVr`kGQ36MxW__%B7#`Y<1 zGdz_<9lg8rW#)a^2A`Z&4zucu4g5&|y8@>&?BoL@Csr4+%bCF6+aEh_A zy>CM}MI6i5E~1=nE?;1$nY1jD0~wby(h|%UywU(miRbre){?Erb*kwu&80sh7_e=_ zOM*`EN*8jOd-v|9E8ElV`E3mvmKx^3YjQobCt-B>DN;|BC&{&KtTonY#X|^AcOy%W zXNTXA^X+;n!%wuwJ9IW#4R$rc5FU&g4E5Rhx^n_cq52)&V6f;<&3QS?~?3MLlBcbd4= zIEmHlyyb&0x%xvF0^H%5!J0DIzN1v^lyj{=Wy0$&-`%;q;=J8wV}L6P=T_Qh5W)u^k4r`>76KdAkJdy> zz}4`lNPZKR=o-J?*Tr!;$X=o;ItP5^<5;Hx>u6k|)K?s6WQ<>l(TQdF7b*m=84SpSyR(7#%@B!wMbf4xzg07~QdiW{xYtgJzEcE(D)>mA ztncL4?|7}D?~ib%A{19Y+?sg!;riD%KlHWan*=7pA-;L=4G;ZgI?a+}49wQ$Jhd*0=96U0m2AL&)#hde-mHRQJ#_!}(LwiuM65B<2$(A+q z8o8kUF6y-DQQ6Cb|mUA6Ee~iR*(A8_Yc>?^L z6~)o31*4ajmd2|nlN0A-Yj~6njr^+?ppN1*XVg9!FHh`_Cam;OsX1*;E{_${&fXSiRb1cH24!QRP=UFvD{DzM!9$k9r;ImPI+bVa1`Xn>~qA%yU z@M`4pv8ID^DQ9qo1$wCr3>=8}S5^JZD3k#34m2_9Cd8ww07Bi_8Ns0lg}d6I7<#Ng zFz)Al$aE{>$dDX)VM}&tHy8??{E&KZJl45(P!#B#$aC+!n&mU2mkj*A&vlJ_BAcJq zNu7ETD!KtXbf~A|WKrBqML<=3j~q?`-TF>6@y4rK3YK1Z)he6&0K+^Wf} zKhEq{lpXszPEhjb5&fZ$I5J!5Ha^Z`kXByR1&6S;Q}2q_HO|Dk&SE!>Yi}N~IRAW5 zddS-}e|WA?IBy+>T2v2PHSP(nIDZ)#?GjV+bbjcIERH-*9KRb!2F+K~{w@-3vXHUw@Zf>Y=8aX*s&tG>2;C5+aVU{Fwp@_)_e zIiRaZxyosq;8ok!=g&-Qu@)l+%v{ub(uUYd-1fqbM&sgU2%XoT`E@b3Q-DJ?a1^>O zlwf&%S1rN2aSol3;7UGfjyz+7n&#zq8(-T%n>eo(*sNIg{4!pzSREWXWK!zrGkGrK z#w{i)l#a_8idekqC*IQ_jpJQ~&Z%BDcm_{S5yoUx-<%M z1C(mp8iRVwDw{<&AH{9%Sc&uI&6bZa(>;k!^mX_NnJ{@A?T0*}SycAq&4XuO9s93; ztbQ;;uV9*DB`_yX;KzKAD@*u5BJi#ZP*V*=y-lYoVlwgZEiUW!=ou}!@0IS$F@m;} zZ~~O;UV9k0z45#C7f*7M*hJj|jN~_01$MsfUWhWAd+yfSWrTVxZ`WtVrE1l8*7QSA z*@S^imJAXBj0Z69g>I!|L+;Q!=6nM^t^yo{!9mc6yt= zeEn|ykh=ugf1$7C!2Y)BzV$e_rFmOa!JLSRao%K2O)>cvQemO*)Z?X_UDo)J4Pdi5 zkKq#nwo+{!&xL)ET#9u|^?MFDMz+I{v*^h8T$Lk4RQr*RdxD+MHC6lPy3LNc-Fu_4 z|MZF3nac^@OwOT^IcBB1%hRD|vTOcvoxN=kTiZCd+z41@wX{QTVLy5YH=z77Zi2p74TYDV{P(39~rAMp4@NY4kOi-=`P}osM;jmT~b%+`~eku+ckdadUZ= z<91Q2nn{&WNx03?5(mPCiQ|J-Ot9hRNlp=i(e29j z@&4-?r@J1<1?Y63Bqr`giFQ*^mTm7n&T1Z6mD{Cu=G7L0jciqT&5w2~=IRX!i|4al zKAdl%5ulcFzpAuNnB}~;2<7N{jL#QX=8fdr=Fj7Uc)|oI1Df>ekuihwC-Gs7xc0__ z9f^{Ln{hwV3(7Jc9ko>IYIrm7l;E|Z{e<7>oz8{bB!(L6tOtremVzl(JI4$1>c0C8 zO?G|>Fq~n|>#JAXRHRyJ#VhzcN4P%i&y5$8deXo-Q@=w4C25=E67~Z^4nMLhpquT< zig^+%)r7PJxQ(!3ZkI-)BvY6mt{(0MBxI}c?A!)gF>B6=70A&1vV7o=ee_DAR@BJO zxP>;f+dP}}sl;ilXPeCbb12lI2mY*mDwsOn;}@!ezMZH)|I+hl-2|Mi4s~VDd$XXe zrtjbgrI~w{Y;>l1_=J+zIJ`_uITR(vV)n*EamMJha#s->=XcFm#ip3Dr*<-6c)5kz zlNWA_qtZU1?Eixj2hXZ(Lor1kahGRMI!wN<9mITDb!t1 z#CP-KFr2h7(wWLJl5~*c{pTaC?g!-7Lzd)wVu#KM;`t!V4DZ97TLp!-V(%rojU(#M zg?=%w)wMxKFYMO?#$9QcELQF(2rPnrDSj^=2e{kiEdn5+NOLtW%N zAMQK(_ItJ20VE`E+$GEaHp zdb8yZjRLo7tI8j__vNurjzEK0akp=w^0{I>8&S5TM5SK#kpy?i>|DFL_**^W^-m7Woyk1hg_0D+Zj})*4Oq5$ zjUT?_@S`MCwSl7cKp4xR)2~lpo7JDujM|$|pHf@A=H!Q9$x{vYS{S&LFmRN`eTDIr zUH`Z4!%M+I>cxj;!IanaOdTnQ$?A3n+L9ZZ3>nq^a?81HMhkiAJ#sj82gW`vFME-b zfjr-zR`1aM*5>QYXDHKzkds8Le)K{2C!fAdx&4|ow9p(SqOJ#SZLq;;-?348>)kPB zZ#cS6UeqGB*fet!g(A-TAq-zL)Of~M`V4)d)@lrDTNX6}A7;gs@5+(wg~dD~JfQ!Z zrefL}O~lQ(AxO9WCWMpcxJC06nEExRB>XSq2F#o~tGRgkb3Q=~i|~}F4@&0{#VH;R zVJ*&AM%pB{k(JS^i1%G0)StI@AI0BWu|>fYu@qca^b9TZ_;mXxYKP)6;lV&i-Ho|9 zPP>V#8y*VbYM3*|TfF}2O8$*11pZL(pR{(=y%IJF?~x?7ZI+XG%hoX0`2~h&AUa<% zWKqT=X>*=0P5G5iE9@)-pHGj!JZ<25DNKo(O*^eHh9h_LT;3Ile+=3mU*+j>NV`i%BSx|1JANdqtW$QSrKsn6 zS^doyNjC}J+aAmev|xVw*0;{N@-uG&=SB<5&kh#kfA|RuCS~ZHg3%dCugPksbC`;R z%K@e>oT{1Z^{>f`@LuN72gB{|pwo>vu(?VsQu0-A`LxG7a#0PA~q| z80U$qvFo@5!bp3#7Khg~09g%hCN0wvisI1h>2o z2s%hBo#zXF3__wMLv$#??e(YCD~H=3B)TcpfR0OBOTm}f&X=e~h%)2HT}SQQZF_x`FH-ENUE5D< z9A(F{k2}==+wHuv|0Z606zp7yG8;u1c@iIO^;OL*-Pm7nfDvlu3K8s0j>Z8FcDcu` zhFK0Cc>GfPIf>`7*Mz?msAC5r7-Y)3@x5}3@yy`Smc+KgIHa7f?%dq z^GA<{8=#BtxjbfwcQlbuWJHAGH4e2Zb{h_ezmld2|ZunVn-^b1_3%&^dWz;IVTp`C1)rSV#nAKgBqt6s@ey)f!tn zQ_U~FZC7HyRZE|w(#b*Bn@`*q6}pgcwwS-aBB_GTU9vNr9K1+*& zC#mq&DK*7_GxeQ@-mTxXwQDys@=vT7@NTBq4~8rCr{3k_qE5b_`fw}9nZ~q1M1K!3 zQ z;y8=gXhSgH$0*lo#fxrb^uRwnCs+f#3R-V@cN}Yr_;i8g!+L)B)a(uDiIr*=QIa_Z zUALJ;q2D%DgQ3*6ibrvx3*XK3FD-)%ZyyT{S^yF+mw#bvaob zT=yBk_gRe83`+dQfoWol1e;JR`>9zd01iQ~xS z4COR0=Q9{3`nqsbnd^sm^^1;j415{yQXB?SLTEZg-{4z=Ll3rQv=szm`>3W>vIs@k zh9eibUFcfj=b4Vkw7X|8*JXU~ut6lGGL&tnl+R$is5Jg;&xuMS7WZFVp(^;T_%Osv zrr8qdpS$3dquM?r9g?;^g;AGl9H|iR4*?0a6QtVR^z}By*IT7*iuuSagSB1aQn2gH zwu&`$@W7ix=Z(i-uhxOUXmy)!DMNffRazH(mv`ubj97rGq9TzX7aJSKLGFd zyNQ}yi!$@M-#2sg+IEw6{C1uBk}jf$*2bMCfFV>;Q7L&2W4UId4Dj8Z1J@qaD?1FN z7XV#hMK{mIoNeRxCyCc3Jj`xV&?gt6*J(>2KuQR8l68J{IrL!Rh07S&#^vD$XH4f` z7W_ILG7B;*=V1sfyWOUvWm5vTN56f!=QjQQ4J3~sAavB}Td#qz-rE+%pWc$xK z$YaPXH(`@fCwT%dVp6sNi&&H09-gVDP^$=aE_B+@CI%vd>jQs>prh|UR^$VheO4a( zn)Q@V%fS^x#U?wkMsmR12(DU6AwjK|s_}1U^`Pz!sKY~`GX1K6zk26n&IL^9!yaga zGT@>p2Oj*Jd%h4t3A$EP;MhZjr+NBiD?f5j(vAb|`~w0UA6eCV+Y#$@VJcWBB$mp# z#26i5F;XbX=S#x#m~6{OC<}%X7n{paK5veE4fZH5_6$EjP#JU@hV}YKhF=PIv$YY*q>Xw;+w$2{ZeMqcPvs2Y4PE>=m>upBgHcK zef)ZBM9XOl|A>4ue{#Sbr2Ozvxy@sz_>7iLaNx}& zu#;f6S|VZ0{Qk{2-IQwyM$rdJ7rSG8>;kxUK#oK0w3}nc+E4RewW=QoUiCLNcA5F- z#`r@k_F~<6zK!LsqNcDXpBEdZ!zHZw^TFmj<=mH^W<8+i8HfJWZWkRP>RN8{)nFzB;u~!vntEqb1APZfGL&jzWr>KgxHTM^xbDyWmB^IQQq~ zE^bl7w((8^Y8TP$|QAN@KaOok{;HD#Tymp!j(q_#+@mR_LJ&LoqSNc;b~SjPZR_ zq$w@K#C}St)M0nuKZ^FffV=WlK-788Z{Hs>U9|pMaWP!%c5cfd65#7Tb53V4!V1Lf zyX5H;_*<`!j^#4cxM2N_4wLLQ+_w&kp4n2wDgT%!)608B=~G|8abn>NSS=E#&$R&mmHdn|wldFrRA*)}c|m z7G}yky_4}=drW39tIA+yGC9q{>wOt0I`Z60)ZhFpi`WG{y zhTr$sadhFumoZ(Nxn@;capn&dj-lfyVs+W0-xz9iXIe{flhj`k&-f*;d9=#UcoEeecx>m&dM8xdn~Rqi<{-$cW#jSBMon3!0u-aBR|frmdo9?6}_pAyLS2mEXFl#6k6 zq4o0UqRXn!Tc0<>Q(wxNKXoVnC5ZZF^Tf)HKIc6&V1PzkjZ)E0e+6^xB%UEE41cmt zWHu4CP82vMGzDAWvHeBXuU&b4YN-i%nfh`Ezp)D=n6yNhuOE?xU9gSr^jb>z1+$-EF=2NX?h<8}Csz@_Kov6E##EnYf zyS0zPPhncChsvL0XH&I?v5?1t#xi;qGGalWbrIo9!yUpwGspL$ohHPh~nz$ z%&Z*EG0!{5kaD9Un|C56rCpJtCUY!@8eMB8S=<$2PpNBFP&%FgPQr_q=Ct2VEIKw_ zep0)KTK)CFjpBtn+=>?MOt!gJJNPx=v=YWELMJxSvGD3+_$k!f*oz9{9Q6rg?sDs^ z7SSsYLe1U&t(<37HW;AKB6^pI20VD6q5!Bnh*l?{Mu#YmIzeB?qKsp{O?NDeQ|9@} zf*T>D24}i?r5y2n2YKG8s}DNJa_E24a`ky>s;o1yMF_C1X7kO_XF5I7rU)We#|iVO zTxCH^&Wr~1bI~-E|2O2F(>1=7^&`+isBlv?Y^${PWe=u_=(KRR>74iKdwP$`5i1i{M4kID&3 z0kvXR1W=68-P0jXheAJJR-l?r+73$(nF}IB|LujSL|zzgT2~luyi?b35rJyI+~CV4 z{YqN|lY9-+T2&z*Ty%l%)FO##e_t+o#mZm6c!l?QU_I z_wXDmELj{_n=B5uP8+{|0WdW#;q|x7WRjU+taRez(SIwWpzn|DE&@mf+ZU*uDb=;)d>dun?$K|#Jl3^WNMR4uTLe?;C zr0y@M{1 z-7nXsw;U>_@Po(Va=QCniQ258>;hoXCn?>gcLv`-y$yfNz?T_7iAURY-S^WftQQ{t zcD{?P#C`-TS~;A@F(h5i{H1DpW0A0)uod|wt>*S=5DqWFS!Cny?<>~^4hH8T_rIvE zM7uJZpXFm3x_=pTQ?3BLofg7E1bdQG$CH5wS&zHjNfbyp&h{p7kG)5sHPZ|h8-rMu zcbRu**oNl$wb;nI$+x#C$0D0vvlcZW635~gDbwIMTzhbrt}KL9{7$FVLzII zoE}Qrx$iuIN@F_mEacU`b1acz!$PGIl!Zr$Z3pW4_PoF7Y)2FYvT`udlmkynxZSu4eZ_O4;7*av zwSb13TyL81*KD(z{y5XXjkaQd4`$v+-3ax&C3SXsMbs@p+IhVBqE_6m7c6HD|8!#` zXlFc+C-&QR2}v`=A0ZGNb{j9DgtF>1GhQcq8)rQqu{Gwt22+jfu4t_CCE`JNjzm%cLh%HP?C}IlY3FzEVHy+ z&Dd)b)T(~u*0LAYqo55sM{Cr+E^R%x(sKr5ck4#LqSA6%UE75h;n$B8bdsklkGFYE zxFHj~CM((wy8j=(-aH=awtXKjyQr~*gdwsd5we7_geXdTWUG*}Z&_w6*+TZp7RE%9 zkbTRReK&*b%h-+VX3W@quetB;UF=yyytpf*Lfc2aUSP!@gY*mj|{!( zsM`CQwUm`xU9!$9G#r*tXfs;2e(-_Z5+-RbnK%ux9qeCJEL08B*tB0Rl138;xSjVx zh^vTp{m=(Fp33^g@M4Lmr}=eWq`1vSQ^aaTN+dN&U&velsl!M>95BxVx@eNJcyUQRZg6`h~Yqq z8JCRCZZ+TkI?QRAGL5HAq0X&7xN4%v(iLL`dEZQ;=B4GGS{?ajtH|5?N>C?u*hgir zg;c+lqB^v-T74Jt?=9MMB=^+WU+kcTD!!|9pWaRIk??z8WR8Yj9|U$ELMUTdu}L>Z zIeWji)+$*0u@B6t#vr2jzq9})>=z8v_Y3Ky8f0fnqbWBR zJ@y#d{~9WP zAFp|YFD;6iE(*~;YN-<|-szUTKu17vC{aw+>4-Zzw)pvBR}K{8-XD@ijF-Wc&v_8# zotB-=MN72up&vsA*s@}`+;_)FOM}yTE$3^{r{d1-zJhHGPP@7#NFf^|1I{2M^*=ES zWBeJ7cfJUuof=S&ly8&nT=4ZA7$Xe>jnb2hB-htIDbr8X)cvng>nN^vP{7KRBbA9| zQBUM?3a$2QD`_`v$Afxhp&o%gmyjMsjroU(Xr9Zl0FXzCKf zus4&v+eq+XKBY|;4~{7AQc__J?rE;_kE=u(1`GYIAH`}Ky)>z1LjX{=E455T)@+?x z|3Jpj=!8ayMO*AkgN{x3uZ0XRxJmTE*P3x?gGQX%Xwqv zl(kjF{sP+dr)2Y^`lfJLZ}}y9m!`=axA9GMAdEQLj-Do~6SiM9KJs+ENPBU-Lq%+S z>oa`QGc>CtUDYzb-A%@7@QfDs0z=D|fv6hQzyh&ir9ejpa6c+s5`~rnWfSXxr>?bPgY$ zD;DrnciU(VD!%)}$8!bn*`{A>&hGB9{0oeJV#nEZ10)K=_~*Wzr;l-vSczd_!6itW zH8Jk)hRc<=Y<1dIAhsvu%M)~!bU!GyY6?$T#2xMySLv?{8dlxc#qZOz3V%OYvo@t; zltj%Bk25`N^4?6u4vW`p=kPdfMT?tYJyv0fmprcfk`gXEf7Y=ksx@&pb1n%CLazK8 ztucPghVF#7l4%i97-uQAI)H%;bWTi z`hsyu2ViDw&`NQS0BkJ&x?Iqn>uD4HthMc!fEy4-%=#tZ)i8b8_uHk?)ux+3i`WM* zsc9a)32MZ12|nn3i~X`witLPHh9zRIDMor!ZQ?aMDiRlz?e+pZu^BU4!0B+I=XD@` z*o6W4GI8N#r#D`A(%;BfjW#rLrG{F|nd2~nq_m=S#ntNh^;c0>m=0gtmX3Lu9o|za zTZlEROgh+Lqi$5$h{j_#4l>~M2j&16`vvohyAD}!>>dR1Zg}xt3#NLOy`>x~n`*WL zv=-atl2?Pjm{h?Hr9>(^-T6!9c?0y5%(-Q(irUrL?>b=Dh9|}+7jxal#x!{iT7YmN zMu%iu+YrKPOd+sO8b$ENEsV}LICp-aHgjnRjy5?y1<2O5>G4gs8gzxLkxo;B*O=F8 z6?EEh-sGOp4WtdYb8xTUns`1}Kbo;gB3=t8fMlGxIH2-HUQ&Qxtvo{%B)l%5>y3*G zzSfnne?W9Yl%4gSEgr}=B&~>(LT(g%R>?NHOx!O+zUQwYyg9Gb&h5w(Cct%wMq`~8 zYqGERea)|0(26)cn_mZOmRam6w(h|vDp|adIG6zBB9H;uA6>Y44U#BT}MsPIrF7Ojn`Uu4}o?%Bv`^m;_r-^df{(mi`e}v%ZKf3O^crvanzNS z9svouQ?zR1Yw^LlmSKU5PUJ|Q3;jW&nN@#kAgoYkIw_@UW%Y0^OtC<_H8|&PhFyw(->iLHs{X$V3CfT^u3wPn9g=v;maABB>M^AXuAQC_TL748Zh-x^GNLQVvw7J}(m9r-l2LGkCBnL1raVKJbNA!38*vEd z`OeL3mx8PIHTau}3NqEXZb$Ajm4RC>ofc!=x0}O}jox53W{`pH{G$|aFR!ra2~T;S zu5d@o+gGJ-x67OZ(xFI(h|8j!%&QVLJ6|K&uo)W8F1lQDw$JpG?swU34=kHBd4i_O zHMBJ-0+f;O4~5^w%FVWoGGGd{k6|1&RM4K%+9ATuBz6QCe5&BwD7yqR~YX@ zf5;v^Wo7WUsvXM~#S#0RrP)4*M`JJeVuVdrD|hWG4t;5}jdzKh%l?STWpMYL zGh{0H)Wlxd!99@)rMHrfJ!cVzHN0EgAg}A`@6%A9mkwxt>uz4l*>^nN*}r^pZscGV z&%kh`r}dO&wE)yWiN#agd8wlUqP!PuNua$TRnoV||F6341tr$cRQG-zOsu>292nF` zMyX0fh!LJI5apwx59?q9R67yweAPDFPF&KiN#VRIwQ`^CezqAd>h_5ppQp>b_mqDO zukl9Qq_Uw=UeGlflDYGPv@$Xvcxouw{AW+7toR55VVuGvD3`jb;M{LhBP*sP5@BWn z`f1JW0rad@8tw(U8@faT?&jhuQibRCF)rAg&vc%L^DoE22YI*vgsbz1c@6G>)jl3g zsp!Pz8a5hO-omw-$F+6Y5f|FpQhbQxShMEv!q&SPLm!G43x+MPg)Qq6)pA!KbAQ%N zo(p9uVH-;7{o0phv++LD+f!_G+e2~hKy}l<+pM$_TcYhbO4uWwh`Y9Jnc;DBrCeE_)ANryV5~ zlW#^Q=MWlga}$emo#Ooq%Qw&2U2jftBo6hL7*|}G{q`?TnjEuQczpl-i@mK^9?HV` z`SC=_fSFYf&qmX{{t!(TzT!UzO1U%?V~=I2Ty<@g2U?uBg4gu?$3BR zJs8-D-#Bn#>F`Z=Yd^E zikG$h0NQ`C`-@!_N)?`Q*u8%5!jYBgeK;7Z8>m>es~N#Zx~^Nqjez!mxqw#0Xm^m< zzuyq}((6&GdZht~pHG@Ll^#L}0aS3um`pukO4y_7^n_81{hj0eK0j1B7FH`s z)v(>VP;#`-d72)#ya~o~^s??3g2}<5sm4$@!pCF(Drdnr1+$1@C_TCc90Hf}G$Mf> z#h0u{dCP|Z#q9k$V=6JDqQSYYMEmx*+`N~I1J;lKIv`ouNyD)=!ElXz`NJGtLs`H{8KS0jT~q4oxf;s5?LAQ1b`i7RXR zq6*Q$e^HXi4nP#ieJ=qEc>8);Mw_Z$KdrIg=zl-vGgQ#52e2%`*eMM5#LWN6pg#n! z8j71_gHA5t|L0Tk1AjSHRM4225j$Lg0{mgtbNhMM%Q)0ho9PM9w~1@lnf?!z@W(o8 zI9dR~j|!yZhoulJu1#4_8)Yxy3%xKV{%?Bf&n0g;Nl$2SUG4nFNDyoE_^OP1LZ%@8 zV^>}YPJQnX`Rt<>0+khpxI*ED_$H+t`x2Q=3oj6Jp-4O;{NL{-^`bAdC`4QrCA>A2ar0bnUs+J(RBh{wL@t;p>&W~9Sa~zrvv_R>ggwJA18%6!g4(4x% zfN=4@e%O@ABvcqpDaUCi#}kWdeH&PR!bQu{Q~3HIcvLZI3?;bzwe_|>je;QC`~O}& z5wL6y3qLRP z6fIc6sk88+07Q}oM3{LM=RFNpFOwF|Zh)J7#lS$|lYh6V8uvfe?>wg73qu(Km2U%4 zM+XU(F!W9n>;rg*9!OnORzSsnA0&dRW$5=dm)Y{O@GG1+&FFbhl(lNW^;32J`|!L` zM`=l8?t)rPH+2;!wGhHmnHhjM=8F`igngrh^MRN9k#&%&4F2CAEs0Up>!UR0{~Z*~ z(;1Jk;wCSMVusHC_x1xkzz@kg)i6vEk+TV|Nnm~(Rdpax50XLj&s@I=Q=Oj4!0DpgaNqx29+dfE&L#Mg zJ}3w>ZQImBkl+~T5Plec{^cW{kJMfWa_heCpBiHy`3?t~;oo8%GJZBSTzi*&_`_Fu za+Bxc55R&wFP!fxPH&I(ppSUD*gy2h_B7>xzX!hNr>~p>#4fE27Xo>K@|djMXjy&a zwZx|X@xBIcOXk__!|e|%+*?^l+Gb7R_%N5fJBMoiHg%t_tt30$M+F&bWxVqRnG6v5 zk0@ZFHUx*fbQWB<2@Z7U;v`J%(wf6Z3hsRI@!^tt;&;I`zpg*qII(wD_IGaMe>@n> z+@G!mNt8j9&}jY+dmT1F-nkO}AlJZ5Csu5|p+lLHrp&%haWVybrPgkW7B$>RX4yPp zUUZopV63)@F70I?xMzI2!ZZrZTn&zDTsscmK7LB7;WIjEA?x+;Z@Y5xBu?70vnTKO z_h>K%6JAI^b7xoJT-eGm0=8=pQCfwVv<;gvn!QXe0*l4*_ieuT2v?}RE6t^L;_*MLW5U+a06;=Nz_X*abo)OPyw zjdb|d+MA-_CdoVN3LsY)ovFwZBbcX;lDDRGtFoR@FNh+M$6Wj_i%cRJV3-AaWvh<( z%088By1z&w=18i)BI_qKQ2N`BZmsuMn5u9o*~I6F&o29?ae{o;#U3-CKvV&}Oc6?0 zFW{35aE=M{t#A~rT9OV76DMx`YhRcN*ZwejuscZL_xcK+{yXosgqc3b*zAUg`SBeGS`v_(Thr%@R({x|piQA&dc) zdbtqh;lWb)K*68h2fAexMLOMV94NdjSN#|tI(h+0@TNoEGyvPRctOT zu*Uw*Nx^mh`tT2}$@I0m=`ld5lB6L4yaOoyNF2Nbm+NH!imk~Efd5i|Z!7%`@H8g> z*^YA)AAyu%ngg`&z{n8jpwhslUXVrA5JKu3JgD-_k>FsuGgzVuul==$1L&gue)5i1 z@W$ku>Zr(2C;8buwUmVcC``%Ce6!d|a2G zh28id)yVrH8=F__D)i4@c6Pdf$}p@3+Z5ywZ1>POfH>!X;b4{i6Te|3Ck}WZ6tSIu z2ru!#=Daf}$MV6JXjzbY(Ao*|9tD=E*1f0xD{EiZV_ZWh*R0|ZhZtso?A2FC#edR5 zxSsV|c8$J`u69gYX8hManJFMAuX_|#d4GsvK*v4%l6H(2xi z6kJL!Up8@LBQ(c(tUzBa54645@D;z4f-8G}uBkm}>lXQ{zuv;qs|P_!14G#ju4(-Z z%cZm^yh42#SrG5&31zW;EM%G;{j6AH^H(23FsOgPY=IWFf*O|zy`O3>V3~ojurH8X zmn*G~XT@2fBn+yJQZD>H+m;8ZnzH7D7Y`9fL#oYN-?G4@{=JjVVjROJ0iGs$6onyx zn-C~^e^OSJwJ|j0v;@ZjcdI{YZmRE~{7F8}B|8{=J1tu)McE`Z{}OPC`r{`BFHNjc zHC#%-fvZ^b{j%==eKQrlJ1E)%Nij^?K01xQ79e)*5kMO}A}#>1ao(0`fD$%( zsFW1=4FIwF&z*_3#L@D8w+U6QOXk6Gj#y6k>EBgui3(r>3lIEh46sKjE4AR)_D9tI zSuo)^DmWR`QNYGJoXKjrsWxp0tsy8^Kmb2K*>x3chj>){m-xJr#`?@+0AD7a)GLLW z4~KDB1hrm;3bSN>eyj&RPDy&-@=jqD9q`L3on}DaC}l4QQvA ziudn0@;A$o>z>t16kY&Ul(;Y4S?nv#wTu=s%}>=QN&FeIMZB{0%X?@}a6a|gA(dLR z$8KZU1dsm{MY!n+?!M3gaF9U2G)X$oIMsGqqY%Te=-*|<1c1B3Vt;OFYwu@+RquV9 zp7-aF;eW-VO#MRFoEsmMh7Kd*$popYZ{crC;(uTK{ncy4&70~lXHFTz1 z`oiSR5NfcqBKoZR2QicN`?UjmnxWTiwl#k*MyqcP7cTIoVAc}>t=N{pvVU)!VM?CE zQ#POtlvahgORj1_7~*h~g-ek)jBu1cql+v(fnS#?>w(9RCe#z;CYwZp{_oQ9y|Tzy z7nYfl22vtp-LHR5{aciB&1 z8JaGaKy7#E@rP*4*bvmPq0+1{ZkyY_P@BmZUSDnDygo`8;_ojV;`@8W(j$%7aYNdK zvhn%13nBW#m7xzf%m=_FM84pV4*h?ACFHFOSmtig;oDE3v2%YYp==7fde6;5*>P%l zR}mkuJ8KGp{r0*S(MuT{#(BH5p!@?I-agj57OxOe&d!%5C8!Y}Iwn}t^W zWiRf6hp6dHk{Kb_S80$x`JCW8;eY+eEAN%f4`DrjX#qf4(;VXfQA($Rr|QGM>!2*- zqoF1_(SS?h>!~MGTspa`rsYoQ;XEqi)q6W!S!<(s;!N-C{$im-<#TiuxXmVa>&|#n z&3aWv+rzb7?7hSO*Ab_lfKl1a5R*SM+wHyo^NXqAKzL$|qMcG2Or0I6H+&*EdcTPr z+};DUpME@b2&m-@ANxH`eOlK>?Mo)^t$Rz7En@|m>kGI&prwJP_zt|pARaV!(wf3; z!91kmj?3?08uRaO;sDeJJz^XISYvZ*Rz{3oO? zdr?SnM~M}jgV_|XE~}r}7C?wN+MOZz$KTe-{3jNHjT!F?eeXa%t(x!xji({_fa_(J z?*Og!5^7S+tWDt4p56hWKMv~Kr7>D*k)?DS8oUO~*1Al*@E3MhfX)137FiUarZhG3+|~_+H2!K^Q(;yASx0 zbv2RslI7uI#hr_;*Mmbm@N+|8G|FabrnS)<+aPHVAPz9`xRD^+$>A3K69{m+*~K53 zgAvOj`gcM@Ut6j!XgshPw*j?qLAP1+@X}>x8$hU>ZaF1uI}U1Zz0#wUv^)Eyu(=l9 z3AgXH>aPbUKC17#p#V@?#;LPhIjcrdwRP@=v(xQUFst`{_Lv{aSr34?@%il;Y;;KJ zqhhO(I8$#6Q(+ca_qE68&xx57{2VTNVcGZ9%MLrRRr}p-H9rk$t==CRuB0OHvY*#8nm6iwwJiX z!w@Aeaf96^@Y%k~g9U8@7{CPwL4$Nk6EzP48U<;MtV>qUDMa zM}4Y8JmhK^_lpaUiVKLz3bxMMbrL7*wIYf>UwEV+w=!l6+KAtE1IZkS=>+Dp7s7fV zKkb4^frgpT8^#p2j^xqMecIzabzx^alI76fU03anCtg~=?DVVXg`m)mtj*wKFo%)f z)cL`nko`ioR<4P-ZUfQ35g}$b<@Pp^fn!I8zTQ&QnfU44-94Gs63}P_n~5f$u%+8P zyB~o0+Fd=B`mX&EYNA*8=?8b>FI$(~cPCyZTZ}AEwTqVmmk}>5-nW0&h6e9K0e89p za1AZ2E67S`CcIYsbl4OJ&XG(6>O`vAk95a4vjNgibUt5k|IzJ*yC{{WiQ^DMyBh;?W{su=Vk$5*|Owm zevtb_-FkaEa=b*-M&-*^Z>5Efb>HXkaBi74z$?95-KEi;GUT2B7~F*{WybW!BWc!MlC;IssDc`DylJ<0+U)lL(xKyr`Vh7fwr zfO^$*#bP+c^Sh_Xs?+uI$kQ;PJ#9QDfsN3xT<<4- zIii+GbJhwB^Heq{rz)M8St2Az=cs4gJn#tCHBECB8ewCU+3V4t=@gNW_^%u95>BIWqBEgS0HX=mYIkny+~m zf?qoX&68vDPXT4#%|T{Aj1HXv;04J=*&KIkcWpYd>Zd|oV#m5f^}*+sT}{SH;+!Ee z@Bn1VEgv^r*>bx_216+2Gjy``4Z{+Gn30CWzRA3V{^OeLN3)PG%u9ew%X4HED%0g- zO{N_lLl6laF9BNzZv~a74Fe$f5{0)bhfFwY#bOt?di5TpJ-A@X7yA{m@~oim;Wxq^ z{E+x3R?uiRKg282)}(>Sp4(|!h%BlZd4`rRpVa$p+|wA9>MG6VyTpeUZ4++ z12nnb;?gNVkzS7#5{m$%YB|PE+iWH11KL3_0&1rPbARt#-?bv4 zwke)FnU*O6a|@vd#^JozJL=sVdISJ)SK=w0&hpw*dv>&|7i#L|-EsNoYe_&jXaSOr3mDjzT4YvS_x zM&dJc)tVoVC+>-OTdRD{;tFEvqZV|t!bPO(%~&vVz-B-g0`bGSRHH9npDC6pSJ0n2rJ znHy}LSi=3oProb$ucGj^>aj|Yx92YF_Io@DOOfy9RlO@!>^$&oWLDno>RF7AGQbjs ze7+`5YVoub$s0Qho^v7Kd{eSZ7+WnXxOQZHu2JrGV{7v9CLs9-tT+%_071 zM$+&n_DSX`rhUWvzZ$rAcJ&);W5!nWB9IL-1yQy z?TMny-V8*-w)rhTH_&I0bpUjiqMeZm`xF@sS>~E}>%R9+pErAdoJ(%FavN_8S_tE$ zWYY3luU>za9xvBrr%x*PbRHbYh+?*XXk|wiV+-Bvc_GARi6a7UKGxV3KygZ6)S1?h zgaXS^8cFu)=H>`465q7uIUhJRhivY8b>TBK(Yn^deE1=|quPTBRIPs%`r>rC-SQ(~ za`|?1rMFqZWn~Fp|22Mku*jqEaHC<{@rHa2QDuK-;LUW3vf0(PH^&l=xy+1g(X(b_ zUmf*u)MjMn)rpfRO}&!T8<${~0;m99#lt3j!qDm6B!N|Q@tKHT-;F`Drijf`s4z*% z4)3yOqbXc>M^)6&&m+!#xs`7|BvtcZv_0%-sC^ktcTj)FGC^c+w=yC=V_rruzc(EuN&=`O9-_meMIalr+aTQl_2x z`s(E7?lh(BU$mP<*v#O%1D}0&PHv9mU7haQKM}V1h0J!LkskS2fa4Fn9S=^ZWx|!) z%j~x2x{)xL_%`EEycN{Aq91TY^Yb^uF5XPCcUYx0i+<4_XDf*DEjdci)IvGi1a|JH z&VXofX)El@-BRT8dUMrM68`Yn=C1)#U<2m+4X)iWUZNN~xcl0>1*TtW;)Nir3ombE zeJdf`V2M`)oARYM=fTAHYQb0FK~h7?s3$B)FV(ljEI0X#+i)1e6ISQRpsf_^F(71{ zte54p3nsdg%S*xBxhoLJZLJ%*uL}|*6Axa@wm)3cq}f}MM|5#33Q_`@aLc@@_Y0qz zg%lI$;oJ6-q*dF)?_IqUcLus!)x9Y)DFqxBgy7c)Z$XKvU4VNR4)$Xbhj5mc&M%e_jHqa%Q zyw}kb+P_s%()Z02`{9!H+*^*z(bq8ryzqGbjwl|brDrNKU~q?aj@vDfxV-=Iu;VNQ zZE*Gn8lP8bO{q5+H$$HN;M<6POn5Upe~gu3Pia0d%4+1JdQ(+cw4HV};5m2o@$F3W-A}V-c0S(Z zIUIl67>W<^XiyA25_KKa3mevT+w1VIm{Vpx!CmGXqO87MSB}q}n=!1v#ZAL-R^jEs zRBC}pRZ^?V6kL)x%B4g#YmJY+;3ej+Cp(3Vp9F^M4$VmHaIvgK+sVk?*c`)ouCxz^ zq*-eeqqamzmq!x_po*X1E zGrMOhMiMGWpmkrd1yDL=plp5Gc(9q?FV=Q=3s!f9^^<(QR(2jFfNDTl$?{C|qKwtd zZuq!j5=VZkK9kO#$}|@#q0Xq$vtzVawty*YvB{|UGIe^zwCvf78)uGWb}W_;28AH|4Ai;W~!uFW}grJo?R^rIKcG_4e`qYAfP zOS*)Ne+oH7o^iDUf({yzBT(ZTcQ~m8t;%^fi;DZ65KGNjTXH5(pjLPuEUyytz^IYd z_`?Ehbgu>PR^&?@=FEck7Eh{eg{$j)%_`AmO=Z>Rm5v#)cEBt`rIze7qLQMglJ#uf z)cy8Hiez-W78EhsRkL%u#v))aVBV+#IOA4> zvD2c$t^$>V7rZLf*rk#R`#q&*?|EalA-RxW=bFQ3 zPR9)O7EmFV6}(>r9x-#aH0^|Q8G?rOzS<+H%vY)!$Ee@F_2Pdsh<;V;ZyfKiq@)Cy zaZfsVrP_hpcDNW1G4`e@f1__HkQgh^I8oyPfquh}tJv*dJ_yIFI$ShqvA;ec@II9j z4vw_9JLD6u6u&$zvDMMpMw<^EU|94;n<6=X!mH)OjAyjLq~o-*`j;~nr)6GD9YVWA zHLt!k=>1|K?Q`59y0w9R?a|X<>(25|ZTEDA9CirHiNcJWtO0ajyj{{PTd*mp?H{ay(3x1LL8ht7@M4PYHq{9Dp zU7gP(_VDX{>N(g-Cd%?+-D!_z<#Ky)6Cc{>@M`#8{3#!HStf?y!-?+~P4*1=D$1(G zt_S-swokD^hw+aB#0M^wRfion>&%xnY1y$&4v*L~^VP^ZmHwhV7D`*|)i}sPdz?8R z)D%%OgYW2FG%jo;IZnL1aAhDU+?lZ}W$@F>tD{$rHd*0Cm zFa9-vEyxoJ*@{>6sYbKXJ(>y}pc#l5G^-KsTkOM^M}B3#{S2LG8U8`eZE^_c2T~C6 zy9zG9W*mpXM5QSs(~s>uT4R3*#{bTgIu<*WIc!n3_GHH>_N}pi>2!WtDbH1vRcpjD9&pmVXodCz@7^1()19S*3&0D4tW8B; zyUFV&g<+m-rBdj-a)8Iwi%&6+Ea8DZI}f-TRD^CZh*vj6lGX$w3*@lg&D5|0l^E5- zd|NKGiI!_&&E1$R89 z18Uvz$O8zdn}c{f-dna{cXCITzk)#|vnPygz?SI1d-}$KcO_Dj`A?2>DO;N?9W1%M z;9-HvIM;i3Q$vg6%7gU-RTg>N;K1Z$Mgm%&OxPHH}6v@p3o57U>FKkQWJ_wz;P}n%5IZ} zRC@eqr_J$qKFcWxDRF0^d`@3$eDkEH?P&H;JcjyqxxuB+{3nAJIXY0&TkU zMki{|GH@_C(fue3q(5rJ@l9a08DVj30A*j}t=u0RHJ4*pVUqw9$qW`v(ed;TwN5EM zxq_rpN8L;U6(G{tJBc=3yR~Hb4U4T?z3=5#mA?n*m{37>Yn%ECEM@m1h0-V*QK6D ztDj$ISr{`rVK+u?#}#hA=NGP~yw>Qy{Cvsrn-Oa@8rHPSf7D--!-#7|FhDYQ5~^P2 zY|jL#^{RK5dE9_reV zBeP|k=t(eOTgBa>)xgaVb&7T+S+xiZ`2bCT%k8#7)_QHXrk`lXULnJ3n}l|?l_%%} zyOI8B0Y)=P%W^d%qMLmr^p5MG!uTsBW!Ix_|z!B0w?{e1eVus|(C~w1| z#K+xaeapQq<<(AWPVCyKc2dE*2a}i`A2EdMU<@s@VbDCU!#6ukr9e7B<@CwM zG7xMR_9tTEjpv7)kJ1xtwH%KT2F5*mskaaJT^}kg{Onzd889gXuCOC(lLElJ=r;Lo zg{-xuLf7F0%kqY+EO)1rzUv3qaETb+d@bMnvcs{tBI-aWI#1lVVrMG-w93z-AYMdA zgz}0z`6`d)LEtmOxMohV?;j~T!h1-t99ae4*eT}l8vF{PKi^DvaV#U3d})G-_vZb$ zaV>7IFjA=226&Tu%06H?y=-D$W?H}(?VJRU6q2g6P$rDa8_T4=|AG$hC{IzI5m>y?3`4%}nt@OL6+0bq;6^uHF+u`?;0xZgztgbh%0e8(ca^=yhXpM&!X+( za1J7xdM9tP9+p_O=ZvmM^4TsP$T9J!0i+8pgvUd5+$Jl4UTW6;NW^PBlj^dGr(9-Bc zIk#?YEKZ(X?9uxHJHPy3R$K#5&0gVz-+<70-Wz*oW?>ac?x$c1rmBa6ZGhel5s&;Md_gj!9s85O7ub*f z3^oz;nYypHXIiOMbhZtre1o8_%#pXQJ_AP0pNM0m*nE|xV$GHmcvAxL?tB+Dv6&(> zx8~a5q!te_VQ!_`$;z!->G4Kak=Bv(w!4@Q4S8(equXLlm!B^7ohc`>&tc z!S}=E_7IiWbz$U8O=v@RxeiCgQd`zW!C$i;I#>iSp0m2UDXjw3+n-SV@gJEiinFY zSUx)4n8ZZLf;97)X)U}k9mQlD^xQcJti2*k1ZJlDZ;wrdVJV}2n5zfPkML0@@4Kva z3oJ}U+DKud+Z?vGPKIuFg!*YPZTES z(i)vJovrTJLzI7&IijG(amY0MgN!Y>Tw)V{4!BCm0$<$9Rp~=x_ttdfgG#K9#QlN< z>38UdF)9ZQHx*{@No@Wl-*k0#DyD^lsUZDNL8V_bBHNW7vct&&8TC)Jz7O>2Ne+k3 zpzU(P7rJ~uV(l9cUG%3}=4K1-Ow_6`fl)^{M=0SzhSKAbRBObmq%(ITq&m&}sncH! z`7z#ueism*rsCu^Z!vFRhWzY}&L9}iPRcncCysY79CML~8O^-g=0y$jh-PgexSuRf~rG!OJ5djq=VX-ZzL_K?rqina-nhv<0s@6$7plo_r*w2#1zf>X{y4 z3QSQ`!h4&~K2yI{m#nW|eNt%bR4?CR%F>z(kyngHP4;!+oxAidS1FhqNWnY>Xohyt z6d-91)s3(v;JOpy#DG|7CFR~wzN(S)GLwDiTY|j=<0t7|dUj)t_D{frbUH43UW(yv zRGe7ZgqrI;*V(EFBc?izh zmO7Ru{+lT02s-+?KnJ}wd8W)tWf25#HAF7k)c$U8c@7%`kiktkJGh8nr*^i>qw_6S z6xEu3eApq^y>)_0f#Dmj9(kPtbb3PAq*53{X(MPqo-T6R@*#FnwteNd9y!2w;=&XZ zr00^FlAOCPXGqR3#kX8jQ#~rE&7>(#qbm~3qI*`&>49p&+q5^Sm)_PN3=~%Ap(h=j z-Fhdh(T^-;T(F2Mecu%BZ}yKPt|uLQETbZr`t0|uCKL#_sq-Ts{tuP*UsSqq(BVco zNHrlab!o9t!_?m2Qhx*t~9HU25ZY>6~;H%6&h4*CnCvhlo!PRwB>*$r;zT+OPd zYTsX3&f_`wDo z1a~Cv*ckNg3BnXX3BoYHr-*ci(&0ijP-Y!Vbhl3s-kd=R9PoXh^^Hti`32 zIgqaaEm?B9cyY@^-g1oQc+PG{ih+U8x%`U_Q}Q3}=*QkTXnWssa`(BF_bLLVT1I$Y zZOlFeb0cc;mQSeRUQ&g-(_Vr3QSh^*)&)lk*bc4MG}q+%VJhf`f&Rt;{jy8#xzz?m zL6jzRSA#;Kj<1f&PfOwH+j&-~!cWb&HX+AwnNF<&CvShF`=ZH=VSZhW>uqjKacQvF zfC~`Tjro}a&V*fjdU89CnK^BW1DPLe`2M~qdfmkdHC>*3-G%FtO?=S4p4`m0`SSfe zXAWLw>3Wm4Rw@^yeQl~{;M7@ki%_HnF{~v-OJ%zz(_UjLl@3Q2npp>2))we+0Yg{m zEbFQE4KU&_{70qzR4i@{Q|wsrKRM~1arbq-R`>=YDSk%v%ZbWD%M2!p!a(Jy`P2)1 z`WaU31hr3%B4H<(m`|?%&@ykDqdCDHUzF0IJakrd9}^c))#JBTq?Qu+L+>Q>@T?ow zYkt|^TbemATIiUptU2O;*18x-Y_4w_DW4QMMt1`>@9Me%9^Y6(8Iaj!t@YsGjP4vU}&t0Yrq$ zmCVxTL!z^%iX7i@jGpH@qkw!B!529ZM!QqaVHCe%Tpz#n#Vm3Y5qu0+Ir80mFNDW! z>e>ORK2oX8u54Z9K{5Pz*1ht!XS6jn%MprW72MHDtC)Femu;ND$TwBh`A3f+zn<@J27cmLZR5!7_Z_-9)~E?{t+^C$uC%l*V+bc??KU{fypYI4T@uG*Gs zU3o^~E#rQZJHcvVMrVp99K!~uF27LOZdkl<+M6>{mYuk*$S~4+f_;XWkx82AxOZqk z%QN3_%*qV4mWcW(HNGor3|-6tiNdrFqDPaJid!G6A)O0Q5f1Neh;P_XyHxIk>Zs0h zFN`Ysm4_l%Yj__LX1J3WnCT;XE)G(r3T|gi)88MW!v$|=Qn~z!wQ98&=W$L`Dn6Xm{00I=ehViDkUyl zXojsB>Q9{KqWbQ7ft5nxu<5mb(g1IIcTiL$7viz6O44hTykqVb#ZjK4iubEnGG9sQ zoka=2X`r*lz3xqs$2N>>_Qw&4wz$;DCdGIW=A?JKGlg`;MS3m4+#g zdW{CqpA%4h|N2!IHssCQV@S(MKk7h=?+i@QH^dqRQFxP&#%pprD=z$fYu=x3;)L_? zJtr{>3}2kbbGk`(sg93Ge&O2S>eEan3Wd#Ugp)gvVZBR%K_aHwsuC&`WVcqB<#r&4 z=~Lkp*Y7;uKf7JmJ_dPc6RLo)g4$duhdFGT9!FVyFbvDy9zCGlA5o~@?-J?`oa+AO z)d6c~jsgEW@_;1ibCt@)DL4Z@aPg+fufb`SW4L8Ap1svtp(7`d!sE5r7TX9~h31rl zf@1pk1w{t2eeK&mkbef~aE2_uMV z?r|!JeTDq&j?8%lJ(YK*>>>A2p6W#T)(^$*tw_cY+>EBr#~0Fzd$%8QL@|}8^7ESH z0I9fJ^fA3)(LU?%4p3#SAUSM-MUCZry5WN#>hGiK=}g6J1eyW^DLF6*QWxa-NJVL= z09{DPiGv8$x*GZy?G(=kt=T^w&z-r+>y6`QDK|5~&EbRD>D0NAi0yhvsHUn`lEf;7 zg2~GTceH@^Syq@=l$6zbEjkoqeYtHbM9*OJS8p@Bxns@HF`O4Iqex|nOXX5NI=A2? zhWWhz@zZqC3xm?MFR!@-fEUM{|6IivCUDT>%~V!40EAH$UQRW^FD%adKf2yJpvnDx z13r|1NDC-21Qb*d6qMX3DMdg`P(naNlj06@=uhxn{h?ZrV0ryJq*73F(Ppt<>(-PTf0FA|PJ`3QBJD*CKa>i|K zaunuNuPaw<4{y(-L5zf&2aI#I+9V%ZWsz2yit$!Wn%y;pVUqvx!#R}$o$kq*Vri)X zT@5F;Q4B16;yl<9AL1#Mt%dqc0RMUt*1-7<= zbGBFyIvv*3P|3jJYb6*L_~ffkkf^K+topptxJE|?zv!iWLBdO5sY>5+)>SD2(^z!uUMFcAn)2u06Ll>>_j*)y<>X+eqeR>pvd3}VJJRN|J%_! zk~2_Y&!)2Yex7kyomdyQqJmebffXZ3TlygHAUnR6?LIC+$+7IXt;si$!FqvDx4KqK?R=t=pf(tuR#t;*(3)g>r7zY(|7`Y^i)@Um?sKNwBZ z7`8J49J&4!zn4XRQ`c+mL1|KA)Bv%~$LOKtOn7wSmGzJ{fwP6@V-;nsn8>ZyKOpwp zJK>$1d`E?%zun|mnQpe>3U^uRsR|bg_|^j9-Ag8Rmh=vkke71HWPfNVg*NGgy0Ev7 zr-iVuxWZ-qU!3W7s+n`bIfV;N{oC!|3M)IBCc!cW;la`kU7uuq`qE{#Xer>@OiBD_@9I@E`lYH$KX?-_hn9`pfBda@ z8eaq|s4r!HxaEX!gk1!)Ku-=z()!331Yy{@x`i_2T(c=B0)4apnCT#`@-T|YV~|X? z51oGp3BE=XQ80EowS}Ja<@_0@R0Adldi>KjrSu-HPhzAN%RqBK^5QYtAcXp1PHaf? zzCnexqz}7}Cn7!tjmGrp?73}4-o;8>S&XFjSS=T8>@Kmod5k$&d6(JqJc7pmQB^NN zK~*MV^h`vHUexKHLY4v6wNi1>>BJX-!|&t$zO+f|;InXX_S!Qv0h&TJ7j~rPdT~HG$|)S;`Ud$;mw~k7VKK4r z9jM#c!B!HkE_B2Sow`k>cbbAs>MoH%ADVkq&KB95-R~@SS?~MXIhPNz zBcC;?(2iC1bYo27`H}_-SW^pLF_dBHXsU009*`fM3wrA$BPBoX8u28s?@4yax#6c` zx-o86L4#%Rw&y$p^eo#E2~zjCd0)AU7x3{0a#(Fxm?Vr-B8H@rxyu#$+2pj0Le;WO zuhFDz*LDlZXz(ts&|kRzNUc42W-6k@&PU^8Apl|wG!K_^txmK-(&d&{jl^{(I@;my zS>}0Z!j{H(%I~&t;hxeevt;`Ci*ATOj!E8hGbX~dp0ZbXxaO%z%^u^%(%jY*oEYxI z#+`hlCg1MOF;K8_PfD65g_ui2KNd$k%2hxY_heN{62KpAc=tZerU;!lrcVOMbjbVO zaeoB{sautOuaz(Fvt$)~A#0?_f_IS4==jQw4Y|g*lj8MiKe}V#q^Jp_fRCaH)@a|Z ztRt`M%j%QuUqB~)|AxTudP~}Fx9*j2EkWjz%%@z~l|8!xn%Y(9t=Y z#2duF>{BaHady)9V~nu#O`Uv>#h`TS${-pW?5cVWxH4FccmYz|aMhrI(29x> zjgSn{dl)&wr`bF=^lm|N{o0QQfF=$@%GqMioods+sntYxIEXLk9~uP4U#W@{aB2gmT(c@AfQ(hplJq zCoFpIw8=>CU6l?!A(7xKN^SHN_MDr>Cza7*sDC#p!L=f zW}%cU!Ido`MW5WYjU9LAhoMR`%qE}kFYilAzte%b)MLY+;$LlN8bwK@bt^0*L#ran&FcqD4U!SitP%f-G zC6d;iS%zOvwA}KsvI%w{Gc`(W#LuuyOchEW#Hx6%p587jbD;MaHP?W-JAcH z?LA~bj*Dt`+cX;BSbIsonyprx$db~mH0RDjJQ~I#RuD=|O;n&sQLdjpuF@$`DWOdb zi~ISofP%7?!6S6(VNOl$Yrlqx5Oy_?e^Kma7zKym31bS9d2t3%1EPr+OouHVF2 zY%k(d4UPgh8nDqt*{WJ8n=n-DYJzr=4^1?igtJKx63d3x2PLxu*@|Yr=Y_DVKu13A zf|6WEtaF3kux;DwC%^FC-C;!;a+8WgpoiwU2R*9~T@7N%%AQdbwNE<2GgCdE<58Jr zlfNCfO4;$Wa3OCxA=ytxkIt8dYm@FOVO2Z`X_fdhVJps{_T<@BVe z6KN`-trcLZd377_p0he*K_QE0qB*>U!aQTDQ z*E?28VM~NyW!itzqdCJ$<;P;{X{A|im`O5K#VB%`Lnnq!2Q>%lqYt=#%%WAk?9Ak= z5#gpeByCc7Uyj}At9vLTI=(?${*qSJ>cwul6ojVBxT=Cqb`_yXcqnO*i1eNrA2-a; z{r_#G zwvjO+brYWa3z;0!O|in3hqw-%Iq|ZW{Jc3R_OsaIfGgO23XS%eeS(e&TT}~uBy?Zh z;VF&1^(?<{!-t^m6!UVgwY{TgKKZe4Grf(m0yp2B?kH@L9x8|Lu5ZlCC7t!0y|*5! z*fh=D8jlQ63kqb5zyx6dv$*AkocKjLjiKeTEWOw}p=7B5i68%9E{^c$Kv#R>WDr>sv zO)|#@8kpv~Mt3kr$~U~>!sgj-NoV6lw+LOuM-7ki$sflWtq4Li)nc0qfN4MCwtuYw z?&Gba?a>-#5EwBXG}^v>bfB{}IM7gwOa9Sm^c!JT;X?h;b6Ye6(rYtG?TB>}f$RMh z*@)GkpSV^|S^#=Cf~uY^eQ*hIuz7*>&~`=W9?AgbVB(u?Y@T>{!(sG`hCICW+FVyb<}P_f0{VShbz6AU+FO<;r5dO;<>c4w`Z3lt zaT}^Oyi#zs;uiM>svOB>#LA^fCB>1>iiJf?H?f=0cmH+G9J|sEQ;oQ%OWlP`leJgp z{4T|Y!^W0Vod>_hD(%s$@l8q4{A%34M(<{57xtu&R<~xgl-X5u*AIj|G zO_mEoinS|`GqC)Sk`j9$RxOe(BAt7`K0`5BGMFyarb|fUWOiSftBsY(nmY8JE1FNp znkkqWZ!IE_7uew3Dl1j&V#)L|;#|aK-+g0Wmu8ZjsT2#<_rLBwhR8K8@uk03r1waJ z(8-vDa<+!bYJNJ@qL0L;`5G5}=|*eUWa*i|Qo?dh`_gHW7yVPAYIyd_>zwXB(yb0a z)5D(mbf^!zL+DaI{K)OGci>`biWOg&rO{I3z4KleeI_gQ2uYdo1fz)=>S=6X?5U^c z1#3@c>1$n?r$dfP*m6cFI_#^R&Sw_4NTyX5@NO3CgmoKou-dB#4$8^bRpn#bM_Mm3 zk-tPInGp(QW>Mvn!}rMvEEf0(#CL&Rx?;#$6l*q5#8Y31>#^VO-jUQglxESELu4)S&ur4drm-65X-W?sG__pNEgx9@b)6TLWfie|-Y^zxhNM=nkG z$}-c?d0}8kWm|{6^`*BYep>L&px)$yp~|FC`y>9@S9{O=g#|J()28ryo##gkNKfzI z4SafF%ux98i?(~rSJ<$P;H`#pk2!>G>k@Q#489Vat5@s}%*H+J-q*|kSq!+sOi)i* zc48T3==-NwUC5d*S*3Euf!|mPFzH@AuihjbnXDCRO~n}4>j%R#5Q_hsqMatl};kPR5qjKuRWF%xWW1MYQTZE09|fEfEWc(zN^}1Ci?^S;rdu+29FHmv+xz|0G-ZoMkr*GX=~C!4fKfY z@DHk4t0)2Pez49QLm1U1-|ZU5y;(eL_Jz&DAdqC+t|mOyhdJzkQIT1z1x~CmFa_IF ziIx)s3Q47$O_VZXw)CK|+r}8Rt)w5$XK@!i`8$?*bBv+MvH|a`>@}b$FcZKZ4M-w~_&T_j>ba)T1VBT?IIUzpMT7e$!Wsk;o z61>B~mvqtPI8>T(dY3uR{z#qJC9}AH7t@S?cqZlYLcZL2-7Q-{P5p}PN|Cq+5dQTZd0T^@1wN2+39G;srq;O=qpea zo&~W>)k!j64<<-Xwb06G!q%TViaULs{c`nC1xjKoe1^Q%+cVaoV18sv$!$sMnoSbZ z#qTP%-^%Wsr@=&*8a7|ytqWm2o^bOTR{VfJ9;LQkQ=lOhmxUt4sN>RYbotIr6fsi< zTc1hasUFUI9sM~H9Y+)i6+P)*GaTT%kuDr`${n`#kAsL4d~fSxiREl`ah%?c5uZ%= zVL4wq?F}@c`?zrEw;6_AnqX1S^Fo{rM~qD`>-s+LIO5Eg%+AS^G+xP+4?(MHWVveH zA5f0df+lzdneVe$PR^W@NG(0Bj9j08DRv0a9)Uv;#!+T1t%)XH&;aZ zhlJph^gJT5-u42{mAl23Hei03d0Tv7p_G{$`s|;n4sov5THFwFi3hP(U@T=3`j1Qn zNQ=?!Po*G6zq}!%gbLn=;irfA3=HCC-U*2cxGWfKbp<0cJMBXX8?@>amz1lGgmSx% z#Z}quUr-=C>_5o0a5ea#fCe%h8oYHtc;{3flP=s!>@i%{AWx)Ko(rqbCNJ$+^y5Zx zhZhcKxmW!8RI_!w-IvFQ9sN9o3KJ;7Z3OVAIHrR1n&bWcplFg61_FfLVBfonA<{TX| z&Ao~$mZ~rc1BLhdTiMiR@2lZ~N}VmYL<6R#bE9HYU#4FPpR#1VZCQdF#+3b_?DiK^ zn$*mFmq=sb^6t}@_`dCuRrcy%$v@;J`#v@Tv$uDRuD`ct6!eW4!KK2dKbNe8;B_W- zFFCJV(I2_{EX&!t;2WSpx8t*Liwp?g9^dG;EM9l}+E%^RCWCxA2)fkwJ~_~r60<7` zvSt1qiBB@)!rm@=05ZW442yXkT*}7LN3v{MUtPwKkVeQN4%0Codr`eH)>pGi@O`a`cFp36 za`;rx^6VV?Le}#`bYrNv4t8oQ0eN|EklWf&+^Vr2J?>zkca&he-?qJA;#8ZT9hYGu zW?U%tRGEU0#_lMrg;pv+|K7(?(MI$MZ)g|yls{XV#eL8}ZWcbr5XYaJmw}iR%eq2| za80=I!Aqylc^0SO`nAk^fYJ8LW)@i_eJl0qsAF_mYZdXZzmB$%LumfwlGG~+F6~X& znlA2L$I;b)rrig*Q_t@hu0NKFC7RN!ItMT;q;9>fCeUS+y{X6U^*srE(NYvex7dQa zRtsZrJeei8XISxC>b=11@wof^g||a*ZE+9hK!x#N!9%vnJ~Q99d(=z4Y)i=}lTBdS zqF$=G`{vt$uiOXv`oD)Z$>3`n;i_BEMTstPZgs`C*^0+sU+bM%BAq7<4I({4&R1?>+KA1;dc#FVa*KUY#o`dfXLOn`V3r; zR!${}&OxvCLVP#kG}+h1Wh(FZZ3Fq%%liN^LT=_rRoyYx426$F3gm#nVI2X?Pja>ohiGfn}fRM z74JMPeqFm(Q7$a5kp}}xTv39rENBtDB7+lQZbp+qkabOv6wZi~q(%G_y$)IfylAQ( z#JrNun278g*G;Y7(UW>8`gQj5Zi0v5?NEX2`n9N&7LF&f z$g3VTE3=8Rx<@bA(Xl%w#3=Y2X5>5^m2{55@0Kg`qe5de%3YjW>T18Z-`&eMW+J8| zzgQ^d0!Lq`{}e#>H8h?wXjyE_qaI+K;1)Gt?)ogie*N{^>v+w((O^eKx_lIHt}Y~G zbihu_bcf}gk2m@J_DVnul2miY>WVA$D(|Viu6v!Y93^kOK_lPX7KZ}CFyefZa6rE2 z!-9Z-Q*F2Jx#LeA|IyL=tGhkn?C&glXE*BzgplS|9D6|>BOi){is>IsV;tiOKCgt0 z4mab*vS^c@e>T&?Gr48l%rS4Oo>fcxuI(X`F?vD&g}vtaiLRU9M2k4Ih3;@)NnP%D z*wLF-I@Kx;3dbU$Ok_T-rBS_v2XD@b@M}_M6=b7+bM%p#ZE0YF)i4fVF4S&9NH3sR zeYZp{QBY}OM4V+Whhn33r1uk+>)F*z+T0% zCiINbqs-lO)@O0O^>+e5y0K@s+X#j!;+U#^R09~+hIdElFsPHQ!qh_AOA_DeA2z@! zZSX#h1BH=uVUTroqnqkRZ^9crjx!>}(V*kybm6iQJdMK6OgKijR|RUZ1zveCVC$1t z!$gK2et$o)xe|JuwHc@1Niaf?a=k%nX@Y1`^<;tC znn8=GJiVx~LGKluE2#wtmf}BxQj|qH~^hjZ_ zW~m+O-;}Kgpn&>st97$My+N~7qSX(wqY_6ht1E3fn#R}U6yo8Y7S2G*Wdw<@43U2$ zdZswmf2*#5`F%W2pH`XQrd$d8TC-e>dM!$>9Vch;Au2f*L8sK0(lex}D7W9+*vkL+ zC);QdA9nCoG%(XWcOj5#!{{iYs7*>~j5;4U7GGCQoHYEMr3(f8Tz~C=HVrJ90#&So z5Z=?1vZhbaz{F`_NiGX3k54>{KsVC~y}rRlnvV@Qr(cZk zd}$L=ojn;@zlrHyQ3ntB0&I!HLA@zIYUz;^M1GQ?$SxR`%KCWz5qrsXV05pR{7d|r zMc6Suoj7U&2&$f)1N5zT04+{FoQO|c|NM)3N<^zn<5vOQ7iprE=mLpnM7YAgH{m=v zWhn3?S>U%(Q3McAE}oNhYd^$?O`MwIxk^6yi%u%5*`Y7D7RLEP_W+=By^(#()|U0p zcEu@CZFgG`I))MPJ<^1;&1U}?I+;{GIgZu(#=AI(;ZL zio;xQj?A>Q-mk|q?lLKHN;zd#*v#ND%}o`y%YXFyAcr<518CdLJO)|QG?MUDQ3JW# zBR>y>jSMNv2g2J$M|Y`-+SwG{`0Ct4hb?xs(m++4o=>u6%fBeALXY3h<@6pPk8DG& zdy|!+3=jX!amdqwVqk3*{0Pu1^tcxI+eR%zJd@5lFPJ+430%` zm=4>2y>0KU;YEvpa>At2!oxh62@L}NuB5&^c^{CTq4JrUR}>$@{QKoudY{Y-8i6CV z4SB@o8CNcaNDcXKSp=C0Gt7iBCS2F@!na6}3@BLBMD3pi7G_1Wlu->Fr2fMhf&^`{ zW~!WbHuA}1LAgd=3Mqd0#4l49(Sc_JnQkhv@%O&GP51DI=r)HF7k;152tXH01B-AI zVjV#{vXOEZPjJFO$tUBGhVmG$UKG(6NXKbTi|8_~@vky?akiyOPyABy{TeCrTL*bGVZ0SPX_->r z_(F;iz!m+*`lHU9Ze&$q6{lp@LuZ@$+M7L_gZmTgyB)w)z3Ev=9l{%-!X&d(pqSk;(;@rqL5HsxEYr z?tgw7Jc&9(VNed(Xc1r}vd-9;>FFv;{OBI=ocjd*J*~6UMFQGg2#hmW4if9m#-tfU+Edd`Gx^nEH1A^Rg$=tUIUO-$hTe z@W~X90IsstsQ)fkvKcS-^)McE9d=#oQMLw_aXbtn!;U!Xi&P9%d34E;_Rl(qWc+PQ zrZ^UX$CJtkZy@Eyz855%fv54|*&>%aqwk6$0IcKa;>6I=q|lwu$--)M8FyKAx&BtN z{)rBMSzKWA0GFMh&($e)qh1z}RTmkQ#(PxM_{>STzy1QKf zpP++XU8y`D&gQQN?i66@(FOb=2nALE7TEyzN83RCK=U^{l*2M zdjDh2*#M6ZwtfuPB~?!Wmnrhf&9;7JaCT0eN@Lt7^&0_&a;*IEwHzO{cg&0s#E58nergCSdD7#u$2AWV53_&QN93v4pCmIGUIyS7J;|Z2M1rUV`dY)?)AWDa z>c0Uj@W;o&0Raz{F3E`aWCES#se`eArgkMPcbMxodv!%V1HhhM>TmPEtpytNfkiJ4s5&V$AEilKDLUZ{tEW zb592M-S~XVR`SNDTwrFW5(3eNY@|zdC5gqCg(P2x(~}M|9F#{p(33`+Ptf=^b1daf zwCGowJdTC7%VN3m^myCNh9Xwi-;V z|A+OZE>Lb6D~DI^>Co*@rp1U3{%7$6QGa=dKd+=`MHlKyIWIC8ABFF_ycj9!lvrHi z{pkP%^v0bBr!zS-i7rJ*q)hxj3(x`b##GY}6nJTQ5`ROae-H_7X%r)MR3eMH1>p?9 zLsj)hgqDaP_F9%})_4*Pp%SJ`E#Us0gO6|%OT#3o)f77a)~aY_C0s5OO=Gmrh@Vmi z?pI$O)k5c|ABmp{bwTkpn*Z_M|6?YPrZs^Z?MZ7m6_hRZj$;u_OoWUuG(JBGqW#Az z!}vdOGSh#KCyJT@2%0{Gp`%789N+24x0E8FhlYR?4MTGzDl-2>k zDZWdP0M`6H6vNXAqds~^2;^L0E*~O)2mj}CYEtKHelN12G$O6UzFQn9?TFKsuckL~ z&DO7~V>B-rJ)?tJYUJ(Rz%iz&L5L%MSU1vUq`{^jiAZGIW z&*I(t`x5|rx_o;U2*HVlL$TclJL1EfQIA|u)9Q8-O#3=CVrk)jmWb(ZLl&)st$+V| z0{G48AV`l|Nhp($WE1sbqwz0&s4xKp3_NweYS%zD>)@Yvr=rnrZmNw;d_nEW6Rcnb z@u#r}>~t$QjH5vS;Qa9i9Dmk1Y!!J?ga-LJIufQ!6MG!nh)|LKc zrM3UF^fa&(L2676`cc&Nz&~8MVv^|ff@E@7^o@h0e%&(|#deG>kAOKk~0 z7&g*JhzI++4i2h<#(iGQGvMwV;{=4c?<87-Aj8a}|2?enpC}vnqJQg%8K91M4P2Ku zj01M|NMuv%s0cfWiv#-_V0$?B*}trOV1fR|dtBIf@SxwWf-81GU4{)s)Jh+W2QzF3 z8--|6hD7EMzR$|74^L5@-$5&l74sEkLo^heISB|+&C;WlzvS=0Pj!#*T>@mtKr}ufoOpb zaqTSdZuR194L=d(RVaJ78w!yMJZ0PewcsG(bDfQJBotXr3sR7X4C?i!CL|s(k#p0- z!iYX#uPi{&{ztImzbmh&j?qy^J#qy>eT!SFJ`{EAdjx<1=Z&a-6@<8dYy+sH|GPWJ z&`E11vIjj#D!DV`^B&iC>#JjAA0q+U2}*y|K$LeJ0Lj4-!vKj+XJd(R)0fP`JW7oeRm>7-URT|i*>yN@ncM+MZ zsZEnX7T?kbL1gX%?(eFv-2D_71-rjudlFhP_oi|u^_T~Juh0DWR8QH$gKhi0M^}8T zsjnBY+Vlu8Kes$RCU4(*aQoh%;@nUP%I{lMc-7{*%DOd?_eQ1@>?5Fk0L0e9eBEJL zS$`dc7Lc0Ks*HW)4DS1w7|6p_{sg4S8So>|B>>hoQfJ4jOX$)(vM^2$$#|VnpD}Tg z^^t#B&x2h&dj;_;w1OB)eKx=3Co&l$$XU0Cdz1TPd~eO%-3+$I9=JqBi@$*<=2`OwB;& zts;2;tE%tcUY&Kkx0Z?=4s)Yl`Ec&Rt*Hk;KD!n1-*}7It6fqAP0xtW1OC@O-0BcF zcr|G?9F`UF_xjqhQ_r{TikSr=uaKHVeto10O0W`v@vj~Md3+OSVxYyUlxbn@gOQ!^ zA%9==knt#|1wW(cmbrvXLC?7x1Hx?yAFomYYFRCn;`_^&AnAnOtlh%s38Nd?)<)Mq zPtz(=*tJ%3715owVEUt!rCX+k9U1b6lX)!xEFj zKk;%xF&SQA!pL3~tA~zM2jMo*l{zGB>964WwCJI5!L&?KA@Ao~x07yKoi|(gTQ?xd#3@!cIC6O1uvNs2#p6$6p6BXZq+(Ir+W<@~7O2)3E%|Y2dcc>j@kL4ZF%t1V#gpE^m11pbpQa zkEVXOQgnpg*AtoD!Xh8AxARS!RwQ5Nm&Ix22R)O8pdAdPASidDc~70BBD;gRfaH?O zzP<|*Y&@AP6CgK2?Y=azu-F_(1|Y$ojig?13Ak-Eo@(*W1pAD`70GZHFrYaDPYY1< zuY$SUm|?+(HATW97pq&(1@vtd-4^#{WFge-t|tLf_wq@Z$P;Of5d7Zkcg4Z<5|A{yBDB~y*g=rlz*mB zZc#!;27}oIB?P{enb70>RLNFdK>4y*j<-M-OyHanK}r;LcsmBxTiPOSv55KZe8(N% zDY+oZk)g>#XkWVV$z*KFXd0q@m>4xrwPF~~D4Xoudj>AqJ7jxai|wSFl@`3L_w zCx>41fRL(X>#IC(Ic({NPr+1o+f@mJ{k@EOnK!Q8_;kyjsOwUmq)=a`rf#up$$ze&*v(aDC(??zovi4Zas9K!P%9zV^YWiudw>kbrf#BKfMWCG;g5XK`MYYq*75>qpr5f)BomJg=rOV$Emf zkr%@c8#pOy>p5JjbNh|-`^O^~~uXRv)b55mQd!)qA3yn!mH?*0Ib%s$B z5|?r%PTpLBdryF=4?yZ04NVjVGC)5N<=L33mo^24H=Z}vwSf8+40{N)h($lr!gM^Q~D#0~Nv;2-v8y9X^|#bQbBLIL9!ddAiiE>5Duni1+SpWLd=8@eS1 zT9r@7qh)Fe&$Fr3t@j~?ads5C`5(Ol6}>vjQWlkc77hdL4GmRf+=$o6LuZ^Lrb=FA z1BM@_qy^y4GzmH96rqkm@q{iL-`ZEjEg7B{WG0#84h#$%H$m`TW>xo2F$0vO&5#Cm z+u?Hk_IzM~umf%(*CuSJwC&wd`QvguhgGfLd^Rik*4nE0cw1?lVB>qBU_y;zi|KKG z@}q1y*zdiqx%~C?Rq@AjUb3p+k68k(lv&kzoD-KfUbkr0rl|s25nC;N?ig)r8Gxb#250ea}*6SZP zov_?SNa-Ox)o%|Td2kZcV&`YdJsraX7A75o z7&31=)rdJ7TXl^$^pv)+tYcEVSJpiM8x~;WQ?)gMnzjVEjoP}A{mA{y5-ARpL3_LH zCPNh*m^QG9;a7-6b{fCd!JR4@TErjK45jxlex*l^%d>nE_-IndM@2c3ypiM?8=kl^ zF`bR+avLMXu3MGgznwpk$O8s)mpQ9zInvo(7amat?*ihKjtw&qt``Y7mG^t?^cq%= zaf}(!mLJz2P2Zh;n9~oY8tm}N0YQG-3#D%?aN0p-w7dL7wgDarWd@(xy|*Vu@oV=Q zvXS9yioCIQZR%v0I~$wUGHCH?qIOenw^>rAdzDsS*z9a(2!wL}vRj@X zZxuYR`d%K>ky?{x=J=`ImIlUO2g9BKz`O5^qQ#zjBq6Vqf91x2@e@B#%>@vyYXY7Q ziXtPlTOGR7{c85}-;*QGFW#lyB0S~8e!TW^u|k8hXc$=& ze_dd~`fsvEfl)QQW*mr>a8h&|(8l-#POgrz$eS>LSy^4Diz zzzT3xd5g%>$S}*#=ROHLy=&Z?ee5}JPy&+_0RktT8HTKbIVOOrz7{f&Kr7FE%7E?7 z8Vm%60E%RC4%44Zxl#Kktd$)H-QvJG{o8w+bnXB7DG0A~hY!2_JZqH$4mp!+j6NahY z7>_Q*5P^;XpU#tq1E0!t^NuIGgULDfCxDC+4q}ao#`UH1?B1NafCr)oo$6P9p>s`L zU2UB3dciqhq!#V7dV-mdaScQ-TK!?fVK?V@HjEufstZL1*+w+(I+XfG0E4o%1y;!rKt42w=29H2k;^X<%w+Q zxH91F^3U6&WLo(@Tl)9!Rd!gy91T9K_!dWFa~hQUsu=}GMYqIId%e;%;}^*~Iafa4 z(xFv$kDyX_)^|uufDY+TEzt&ks@ejdEDGLrpx0_Di@@vsEb7_{^kna2<9EO)z|$|& zJFmIjPEv%swKB!;)b`rfW0b73f!}@~YC(}L^gJdvmNbQN4?{kLA@DHDktkhUJ^m2K~8(s2p&i#Pk z*=9h~Zl`9)@%+@YK>tBziSjg`*%`Y&i|qwxiYn=}fU<+h0G_J+f$YnV2)p0>CM7KR zE`%VJbNcv3XR33=+G4zAHB{2dw{H`DpsoR6sq)+dAtS~XyNm~x-CaAWQ9@aBWIS7f zq_r5HX|bivcgKfMUXFR9qv{C}jhO3QlB5bJQB?0tx0}jaf7MG;c6{~ul2sz8(GZDN z0fT(`9~;cv zzGAviCi9E^6QBL?FmgG*UlmLi$P_uu3?C%%BsjVT8gw^OMp7x()M@mNS1=p|s9QJ- z^qP5BCEC8DRVF7s&~O7>QvNW_Z`2FDXEx^lwv@r6&ao>yt9Nggh!gNqwVu>V0zh{+ zuGSjnH&r;(?bwXnDIRxp8apQDpRL({@%U)BN8WRcNabd3k{G0Y*+?n@s_?Xmtlds?MVul?a`g;eALg>? zy8@8~p* zW945vI6?h}QVu@8ZQ3Z7D zO5Zyb=Nz_I+brx;@$pVW^Pk*1i@@OEV5icOFV2<>>t0ot(B`pKADRT}ra1+K4JZUk z@dm>z`Am&pzMq zmx0h}_NboT9ESCnA)->_3#hL6(w1d_`Ro$T1vxqVvOC8tPj+U*d9f0%sw)gbTpD{- z8_dAs2h|Eau^5SHyvH@fZF6FP(;X^mITjYv5&t2UxN6?`vW==_5gfCFe|{SlB}im% z{n*)!@ws-gu2E*!(O!pkt0gmKF^+geLnzgCU8$OR`YF(!(g@j?m=zdXD(mg!KQxKc zIn4)o5HDfW*{u4UvJ#F_$+|iaiR%xfu}~QFE>BV~&3q|m$J@CUa*l3VheR`@BVba6 zhzWosu{M5(ieXxJjxFGCRS&iW%L{z6Isk!w#S6`#!xMZ-_E6p2^#qD^(yU{_@zV-v zl(#TUs@ll*(Jijl6#TT%mu=LlUSYNFVcUZ0#P@-aMI7ZOTm4Ro#dkLj&7;#F_vrR( zk!B&^s6LX1)>Up?5Lnu0O6zujkb#HB`gM{TS)W4X%fP?krlaYw4a? zn%H8zMBW8JCw2QdhfB<>vlsMF-?MtN-fgt~CTVFHM5taH#=1ztEMNt*{W{JB2UIMx z5BNqn_j2iY&tYH`T4cEsENR(%94{byHv+j<;POblAyjgGYYshf*l3G_o6fPY$V_p4 zJ|Y~=(^7ex!_AZc&$E}~J7U^LL=H^n6qBpB`?V9z9dhG{xKbY|0OdC(ow zn1oIEJ~ZOJlbPbx`0@tep-z&u{UG!0yE7IWt2ld~D%0Vxo?OPvXng5|>4B>84*dgZPK z(J}HGZkWE%ZiX!dv+S1Nra0cld6HpOQv86=zqNs5;1KuXv`p1qyHsxrf8-!S09Ty` zb6XPjo_p>gI#8+2;w<4k;+|vl0x$Ez`PqkSE8DilkBAHTAVh1KO2p}ZzO`v>Stw|d z0HyNP0N#JE+d!uT@ae9ujt)wsB3_xh4|(1Y#M+tp&e%#H*w-bl?B$EAIvdgcCByjw z6CuG0^pjV{>hX1bzD+jZb&9&GuG>Dp*^j@mJ!1#eI*Q+^`AnJhGIaX-FuSUA7)kMS zW^Ca_Z3wWOOZ8Y3X&rh)gh6}?JO4y

(#@do>COxy%=^}0wZdL09TJjn~gn5wBpcw6m!T5qhN1MojF;k+(z#~3$JhW zwn^7~vj`xbvb@yFE1$lDpH5l`j+S2ELo+%ig`P9FDOKN&|#t=-LPN@s~OrB{P z;JIlr#ZvXs!D)bt77;VxxdS?!d)5K~2rN?v=_%3=F`dU$;iJYTpNI=2BkITwYJWYF zoC3zmyv;w57gsBxeDsS|6pa93H#TAKg|b9*srsIp^^ZA<+n=Hpo)P?0hOgP_Q6;uRT{`1ksjVHMVc$Qd`ks zI$@V(kv(+UAn0-yjkmDCBmi8EZ*@AT8<;$_tFrz(>M*YV!@yvwB3Wtyi z4+i_PE4TQ!$s*wc#VYdz&xXDOBDuo_YvzK6XPstNjcWaTTU*>5p{!9M&*w# zyi_F)u10akoBOgEmp!=7_o3H0B0zePGw9KZ0U#qOl*1@BiSe9a-@h1{H`xX*`%>FA zn-gCLs5#(|*=0TJ6mo5td~;t(tviNV2q+KK5pt{X9`Zx6XfP`@cceT71Yd)lhbX5T ziR8turoO40W>XXFU)#0hhlmB3`Pn%DKTJQ$JDNEhQ|@NYOrD)GH07k-sX?h(0gWM< zSKVR4jiplZKU0cRP{_QO>p~XfvMU}2LGq7Gn*0h{Lgd?( z|4C}^m#W(-Ld3kP9!t+ULNDyvo>xqCLYi!wAZ6N0hW%C4QU+Eee1sEYk0iejG&B|K z>rVV;>9=#Cgo_*dKF5D|8_8cOhf!^EFJfG4g>Hjf+G=&QSxL*5=_1SRyN?k~=HAFs zm^89>4w_ynw`^TjLNp{ny7^9j~=Cix!$|m~hzix+~lh_iO&%82qQkwq%q3JBc znttClF5TVIAR!$}rxGe9DkUYYf`lM7I;24a1SACs=?>}c4Mb@eFk$q-0b{UuzQ6x* zJTKr4V=s1mcis1OUgz~Wg;xL4l8+aKo|VD#$I|)PpU;OE1zmQU_c=V|RQ30G7Ri3& zk0-TALtDHWvH=j(HgG@Jp;)wM=Y@hh zu%gzxCBi9s^Ejho#i)J5^*?H5&q`fEz1J)xKjdKFv+ui#GuF-YdjZ)Y$Yik^e*Dv0 z-C->JO0CR0)N7+V29jMDagtXF1gf{I^uB}J2B!Bt{9(Q8L&h82u^zm7d^uTtoH&!N zOWvrA?Zd4AysYgKC4p-yqr4zq{mRP-V19-UY;rBApz^I~_q<6=%bk?(lqd!!_UbiG z!oa}wKc~cU;@*#8Brs;7A5=fpp-WcH=pNspQ4v)UQ>pnURV-Mj8E9VEW8;g|4O=+8 z32j;MR9C2fK(QyG13-=!L@EtE7yvj=?r+&n$EYzvvJ6RLZ&pdhv$08*h08w{SQ|s( z-1w_bT8-gwxgQQ^y>>lrq3>fv$8>f8(yecreU8GrpGh&86ofp(*0o^`{rnZT<}!cx z+dt(3vV=wrf&BqWWRU&~G%b9ymsV*Hx^R)mRC4|NxM13%xZq-q zx{mAHMa4odiyy|gZBK-DJ_V8z@v4wWGBmcK^2suA8W5fQn0DMaN5JSVEtx^5TX9&9 zE6Bk3TCs z;Rin+V`NPSOQL+$zM|G^xL59%^JC43ys!zw<@`%xsdX2s{FM1Z3R@)YPn%hSkZx1#KL0BSbhVkIxFh;XA?dD2qkGH?`0 z#`}+kCZQjR9?<7Gi`6~y$Y^^u3u)x|=>Ew|HobzDkd#Lwq$pP8;LnZ05e!OpgWor~ zTno^2U*Fw{fw6<3EkRUxuD0_&B-hJ8&6bDP1_}m3FTa1o(Sw4dj*|cJSCPfFw?+jp zI~qXidlNzbVL_SzZEv7{4BnZx$+c!cU-t#4V13qMQq=1|_*ewgsc!b8$?Kj9kYd?` zi_$=Ro~mE^s~UKv2CZz&7I)!37FTA&67A^YwR9o^+r9uhv@<5TDu1A{=LFh#wx!$0 zxd>z(zys|Cv3?;TSQojT`D#5D0KL%l@SpRQ3Mx)YGzNAr2D+oeay8W1pX-dhK95VL zp#RC(0}5(qJU|1w_|nn(qzQNi^lu#n1?XgB7D=`_fmsWVZw`EcAS-kF--l@jN zbdBjoNx$C(CUbfG`o6(oB2wi$DoGwToux#!NS9U?g{DG&Up?ubS0oT2lzyr9iJ^Q5 znfeMm?rZCPHlm@gdjyjQE@8c(4*hRO@rf5j)}Cjw)R3(<`e4Mr&<*^5`+nK(YNsuF z4dsOl>UXP&=)mo;!5}iusQ~B@iK|*5$5cZ?l=8zsHFg^&+4^T$Z+kg}czz>+&AaP0 zS$0+3RY521!X30;$~L$l_eQ2}(AfV9Zmba=cz!m9#pg|%QJw62ts-6lM?@EhOTP$z zE)PEU9|}FS!p*1?JF)FX1hTV{2!yl-?hEy4j=BWiCQ}$#`+T_Ze)YIbIOUui@crI@ z`$wu~%DWH9o4HOL|7s{BObbyJHAzj9?!5Oc)Z0Yo<$qTjB)q8$k?!S2;$*p7na{cE z%7gO2l2X9AJzj=22Kn;l>A|UDVHn#0q|LeXy?1Ye9`8zfNNjZtsNPYb+YX~uJ|YM8 zJ>R}B*WI+-{RI6Kz7<3Ui97ZwL`wfuI=Nu+6}|* zl83q1&PX*S%2^f#IgDktKhmp>J0p))lIOa38!W^EB2?k(_NCQiC zX!lqKaCv}|8RHbbIlaU4R{geTsQek`O05fVt_G^z5VLN6i=;&-q0Co@;6j=v05xm5 zXX=5DOoPiO>smb{I;SzP_k%xYs0<)bnG7x=ym_W1EVo7TdakLgC^}LF?ktCOsozle zu;K>{-J=jn`Z3#H@z1DttvdP-<>+UT|Gso}7?Vq>-M8{{V4O&Wo0gKBU9|FQ;=7$Q zMp%Y@AAH-|2jAJQBMU33T%HHzOrQARzyn*ojB4WZ`rd!rfj)k*Gg5^_$B9l zbwP3%NB_wiHN9<$J!tC9LI6ts$0&~__5KFzx&0?j%`H{btD z?S3J^r+Ij0%Jy5C)3ibMAWLqvS&SzH*C7<(~++*Pz`zTBqr`jtO0cB^`eBPa@)q7q0r`t(eg;QLq$L9 z%`o1u^S%>FmWD&ds;6n5pwRdx6)pE~mbl`k|N+uvwA zEv}dQ5Bp4E!Y4D(si8Sq;CN~U`k<)@5L|r@nD^j#O6#5H3fdlF$&wb?>_U8Oq*GoH zs_baR+YF{%3l{=ObgL&F^FL90TcPCX_8LcP%cTyp(D#KR;uijNUjgJ7+L$}-*{34L zDBaQjB5eNJ6aK8+`};K_Ri7F`ef@A$)F`j@w2>4hG-`1k2lR(;9Ks(lf8)#l;CB?E z<$dZ9hrWrGUC781y8+P;#KnfI37>t0VZr4BVee;1X^O6 zGJqXo8c#iBLu2x$up;!vauT`yW(LJk-4yu+B@aUZ>IY)yxiO@O>Igt30Rt(M>yChv z?oM2)TiBRob^xb~_YHTz^^H2^#^&^Wde0@%X^2Qs)n6y;gOHVTMC}j|kwd~}zYTX- z8|)B2i2T`DJpIEhhBxNq<%hycA5j?1Mp?4OS>2d%kirJ?Y@>|49T0z$K=p`LNbd>y z`oR4d+sW=GYxjz4#*#8mTgmaLKSx=t__=4YW_{1r=!3@3k4)FQ@Q9wNoFZj0mxO+> z@OU6q_cWZNUVeAbt`oR|eh3k~wG;D5_c!S%KA$RYs5e(!-c#UQrA_lavs1@qEHVR2 zhiUKm-DGCdwYO1PVe2>H;)zo8S`UV>F3=pGW%<{mdm5IbUIW0&qmE?^2?4hk@)Z-Y zBd?3Ds#C9a9{Gx(FXL#E$L$&`(7X@(Tx0`3aBXr+iEx*LxPk|kC^<#nit!ZEsD2A! z_#1uip^pBmx|U;Tz^Q+hu~>v5iQi-@IpLD%aicYgM@XQ!dE#T(mqtFA-Y&}k*C(UH znu*zVQqHnc-~LlabwasgMotH*;qvz$pJgRJJOjw)UW@rj3gA}QOl&;u4(B02*c=;7 z&Px#P|H`B=g<-TVSL>_*5?S7vFJg*15dAoG&~St=+rmAoP{wO*n_KoXvpGwUf+ETFtoi*dP3>dz5Y zMf!Ldr}937cv8k1glWA_ZZUpJPs=UUe{4MfUf*@plcukI1HhQEA=4(Ry~sLMkEgWV zUh%h{r;|wWKVce;I)_YGM=PU~W~`U~{&>Fr5S#0E$d-7B(a1Re?2-#?HWG=f1M@i* zGz1uHsk{b~L+%v*=fG)Y(9uQeD9=9M(FT@50hIIEoj$;wSRDZ8ZS%e)z4^I%yE=fpn<^I;VvUrx zM;9~O5CNV_lX<#EwAIewPaYfJK!-^YlyK%h>76~-UH$QD69?FCJOE0JM&y<+8g6sQ zQ&XUMJJ6rjJ897DqWQ2y>2KB zjBDfPyAYl2$GwXhJR4sMM%0)fddqdTUEtm6+`}Xma-^Cd&)n9m3!VuW0W|#=6d?^F zHP+7YpG+!PH>_juvy2#o5h7wl)kMDa(<)=Up=txD$~+8cWwSHh>U&APRUm-M)~$Oj z`X388V6{Z;Bz#l5Nc25VVUn+p*WbE3kVOE;WVyL@S|3BG!t`D^IcI1(X^xD)kDH} z@g&l$dX-G9{6u-$HbQFxi_UY=v01lHI9Htoz}t@+s_{c$Tihb+{3-Ym$*NtPX`EcMdX;-gXDM^!Pa5(BxIcyoA9Z-Te#@ z>exbb59$M7FFZ`sEiIlgMVwR(N(Zt22DEiv5b$ht zug4rFr3lNr&9qI`bx#h}z)T;hqyYp=1*uv4xi7p}gi{#)H-9J<_ECrQmRBZ&-K#)I z-^HaW4x?>9q5dA9c|VV4&o_v-9r)V2QpEur2=LSmq(>CBvTFBwVOp2|M5NN89s#yQ)~0f+A1U}6DWLgv;^1-BR2vA7 zU)$?orK_E``eBw?jyM4vy)f_G*nfW;9sm7jrB!3JZ};aRQr!ln*)OcOYnmK)sBu%H zNmQbUWK+0LY#NOzG_SvWVfS5g#d8b~*$C2FHO&fFEedapERZUB!NB72s6OhvJnC1H z-}lAY2XXW&i5ccs{C0f3N0s|Mgv&+@-u^@k31r zB%CAm?IrjAhD@89$3stDl1v&k=sVIk zkaf9QwmdqHIzcnKAav0-Ab>oNH+(H9rDvzOBs383}bJ!J1dB<>$doXTy3;V zOC_Xp{6L*}y$sogxbSy;>Cte)#c9?toOHnIPPg{xRF8@qYY3bMIlX3ux6hSOR?=-N zu6dp;s|^KjoJP@%rw=wCE08DL)w>YYIa|8)xaM&DfvT}D^Z7S1$2*`J+t+vIy14LG z&8vp3#yy|x82RXE??(i9=`7jdxqz{Jc21Fvyt{dDZ!=E)gd26b?t|+A2RsM&je)-0 z`UrdozA2Q}(67MrjoMwbJ~rb7jGX6sO0Y6 z@cKPna_dW-_QOFx38p4$yThuZyu;^>34_f@gL5N1IZ%3Oty=`;JMB`%NR${sPh||~ z>+q#kkqn`V6`YF*f36+qYB{ENl_BdibO9GE>Ep$XBc>P4JnY>!Ot=1GHK?UEv&Dn3 zR^-|DG$>akPz|Hy;s(>G!0y2KRu}ce^|8V<m&0I?-Rk8A8~3&y1hJ@B+W3z z(%(#Rf>$?{Q2#Fxy-LgHRIGWzY?E|qBG{9x3YuSt8+z^QJ{(DFFE^;cRq z?V)FbcAVgw<(+NJ%;0F@kzLdLT}52jW92>p0_?Xq9?$FbOvQXSTVEn7Cy^RWUDr!L z>(fSm@8K6xA!8`0zPoJ7zI|Q6P!&rp+nh^z+sZC%XNNs7I&dIFFuI0%SH0}Ur$eG) zR-m!sHQ|^P0fU+^Ii-t>1ye=-`n2{nk6gn`KyZfc~~dq zG~=7sg!8ToKLt!P{OmvgaDh+Zr^e48{>>?-`#Nw-Qi?t11Kax^x6TF4*Ni!uk*a|2 zMX8eCu5?6%UM6sSo=`Sh=;IzI|633xpY*It+PSWgg`gmjo0#@PX2SaS7DI`ApWWOo z3s{ufUJ|FGIg_Q9h^p8OluEkSlE8K)%AawTPW^ngL$BGK$zy!QmQ-7}Mc1)M@S#%I z!Zt=EFYiNEk0!RtKD1RW>0^(4mgiE%4#~O1i;O3PDv{N?{tSKk{bE^P>2-14mp`{6 z$#7QJ>o1Vn2S2SDW~Dn$2e0ryL_ZJrmb{gZi10zFqjY2wi+Aq)Gj;7U z)Y5;YU0G%!tDj{QfJ29;wTVW?$5*-#st8DN)I;!HhakxTjSC>mD^Y-9`&1d8zq$ij zHc-c1F>USUc_l2P%;3X8d`%<2&=vVBl9lPNbdShmR6LsG=s75E@2GQ-oJUdnok(jJ zpY_~i_q}M_+~Q74k=s%1WihvU*XoG}^H6b$T)wyWME^3EC*I}fYogOmc1g1$juO?5 zCXYDuYB6oM+ska<-8f4&b^JMP{t74Sr7Rs~M$g#XQ`bzW0@E5Pwy+oXqI-Bt0V42+ zUaF(mNy^PZiZLgpW1_G!D`Lr9`_T?Cpnj1)bt2@j)R0;{_9C`qSwmL5xX5SMDob+; zhp2Q)O|crTk#{~|9A8}|RDmML1&5pzE_+ZL7GE17*ZzABaU(%5oQa;Hon|!g$}1Vf zmJO_#XA>1gG0}AD_rOqSeWNRN=J*5a`wRUfY*u&jElkY|TGNwVs@ON@w|wbwk#2>X z%U)q_2Z6?Sbd%8?fhI=uq^OU|NjcMna#OKvcFb^ISob=uYGxrFj;1aT`O3&JN)weR zhiu6dd=(?+qx(v+$&-Okr>L>r)QTGz>1b=UcIW(h^pu zEvjE8vpiZFGd6Ak0n`V%6D$6n8hoeA8Yd8z!_D_}fv!iCNS$I2^CLpTf%v>Ne2(9- zNd0>?(^j;p(-2EV3PrfBzm4#?M!2Dq;dS*#G^QmKl61FS?G)BVkK67S-wO9oK@Fr36N5O`;u>SrXo58kZNz zj}i%qF_1@+!{rEiQs*NWUpIr%rAlp?-==QAeXKS1CSogSx^c|xwi$bavj~j(+jn~t z2!;M)c16BeAroz*2gN<=XLX!pJP%x~igs$|5E(z8 zM20ya2amrPX}B4o)FU7mRYR~Vt(i3c?~AqPzJiy}%Vz`v6nkdiQ56}SlCXFAX5jh< z)H)TB>;lw#==2z2%v~fYSN@V^wo}@pm;pAfx5>Bq(c@nJ#JQ2s4Qm{&@jba{uzr2$HHY*fbneeguks zA=j5|VHYN}7JOJcH}N^@VMjpnVPbX7?GfgDVH35vo^$)KcZ2C8!4DSreWfE|`>XSY zoR9>KBqiKS{@RC1lk}?3Aj=PFxw+)^uZf8L7IYUR2;D!+*Cs{zVzA2>dNwm z4BbS?bxzw?rTo>+1!BZ5_IcTC*vE42<9>4fBrW~BWyRNQBAN+rvzZm_W` z#a?Dt!9gB0b>QP7)Z1wX(j~?d@+b_MX@g6$Y7FqSmNe5Tx$&^ z_#m5Nk2q6^pr8n1LHXp6+n)+#aoKhx^i}(Ub=bF#1XNyG69{A)Y^j;y1(w9JW+WLg z==FE)Pz^gK1m?G<$}*9Q(3N6pG`++z%gbz`XIfg`?qf7d!DVB-htBOf^@cVA;P;|cL?`rl(1XNkL}8_7Ln zG?YZWO1f7wgj6GZ+eaNv5W(9`**}`xC>o3iRgga_N3>Ts`NMo_-Rs-K*wG=2Vy~>R zAM&oK86Ig_M~K;7Lkwi2zuY`JtgkuEXPLd5R5Vj1<+#ZMig{C>$4_d`Ow!a`FYr*4 zV((}q@3{44ta97Gx78zJ>h73*$a>UmieU%3iK|gW*k+_LL3?fl*hcV?!ZH_^SI}-mC8a*LWbZJ0=GVd=RD2HK^JhXPNQQ=E-NrktPLN)_< zFnl&lkpjyem%m-<8gs)|Cs3xuP7w{0A>Vs$MQ$ulzkL~MqzE&VZOHE579+pr;|p4e zYK;pYVs-4X_pNRH`QqR$%dTjo_8rcr{7G#BwNKq@XDIjVF_RXs4b38>qCT>@KGgaT znCig@TM}qhB(RR0`7y31Jh@tKu`0rto&dpH6<-iGnwte3=^}^HxYI&-E-T~Dc}q39 zxUI42k;V-w%WxvphKP;dW2o?uA+=~RDkb7%PKKzbei&&EEc06qh7MBfl*ltLrvP%+ABNerMwH1B4!dl z>PK96*$2LwEnxx)UMn&ua{p8!V%Ely1kecTSE5U>6D_TH@Q1JOB4l2Nhr4)R$0 zg7;N+RC}5J!#>f`q;Xu^rYw-;Kw*jIwfA!ry2?%PLQanB2U>B!rqcb}{+T4De{Ffd zY2kUE9}y!PkrZOLUbqyy4Sdo$A6(*;-Ch<|*wS%Y+uw?lrxp=$X{jRi3wC93slFrC zaa!`)(7`f8ef}t4<~>88UNSp_!FNr}X+ute4-kAlQo{zOkX5wqbjgtYGE_n>;C#RX{skoo>ng;t|Y14(3MA+21M7aL8;5P1O4P zVA{1%;LewUPQC_Md~v?@0z;%sLe7Y#JlRR_O(D?A_?;--+hgfo;l;7TWgJ5`2BIRs z_qUF^4yvjRAp6mCr9&gxb_=xA4wL6rBf5-IWH`C+XX_|D{^|$0>P+^k)q(Si}naG>QlP<`@iFsQJ3F* zP%LEgyVf2oz4#%kQms5`l7!Na$!?>Ga^>o=#ViY~`X=#A47$mc=Oi$8*e#(rgZ1yteHnmzxghf?kvENf)B`%B#l72|y!#r0uz{ zQrKCMX$cR5iHcm`h6|PP-#pQ(h@ylHoe-h+o`GdG^)l^7ehjnE$!(+akt%dx#X^cg zeI4R!MES458Dc-;GWE+f)w*MhMlE5b%;#N;OLhehfJ5JQeP|Es+#1(^VXDQ(fp1BQ zFz!4a`C=&+lJ2oYqnPOUh=rKcE)31W{io_R$`9OG05YFEZh~&xYT^m0KDrfQyQ7|R zxE1*tOC%K_BJC`IutxgC<@SHQCF?r!OGP5z^7x{DqJA1`!Pf&1epHNL8HCPwC$m&Z zM@jiP95N0pAOHO!SloG`C^Rm)4`ADT2Cb zm_j9WpzY9WR`Mpi%%`ku8Cn>yfo}8&o8zJVhX2UvnS4pQ`15rg^9OUS3YSHvG>hyX zdVYB>ZOf|HE^L`tgb4w54GE!pZlW1#$Wy#-Vl_f}X+`WW7hbi?PpG0f)!ccha`-Wt zj$`eOuQ&a4>Ty(o(rUi%$VDt4LNHBDtc_&qmp!4%4b;LeNT`m)L=CO8R!tY~95@}n z%LPAHvWtnj9Xf}2f*>IBm?RBQs) zFB(I+34}{;`cK9w_pUfDHBb8|8(ot>ho=RAD@}K@RvByVYv#PW+=G@TY5|ak$v4Cd z^qD%XX@4^gP6C4Nt@w4f2f-UiEzIuGr*91KT-Sk45_)CiX4_YND!|m|FcY=AYFX6^ zvOnfi34=|7i!BoNE+2SV4)#=(1T`NaLnu&pO2mszxYTDE#uTlbUvyl3PaEv=`k-E6 z>Xk;5U7#lH(RvPY=u6QcukTy4rRR{v%wHi))x7wTA z$qoru>X#*hz_)E|$>v{sU&`of({hrsS+|@wyOxAGVGbXN{Z1Ib+0}Ku!d!A!4Gr_) zE}bE6H?Am&GKK_?F><}ix5CBqYz>AOf=Sk2&F#P&@h3L=jj&jJR7deJD?r~@Tgq_> zQdQ7c4P~~bsnfXS-p}_L&9w#U?0sK^sGB}VUlsDP42nDlKzS*yIws{DC%}>oDNYbo z)!5#f#%n~W;OjG?>||Qre1mKoe=ET3X8t&L;K?OT(U_X9$8kbLsyq>_(8p22+~&>B zKVSQI7Y(Cd!zq2S=Wb_f8yIHlQEo$1;Juiz$jn3%Z z1x&-P)A_6ZUEG6uoOmHH32k8(FZORmV_Cl!t4(EIgmL{m@3fq|pAu_p{A-pcr_Cqt zrS}7@+VlBj^j{ZfN6?rtRr+Zu5coE~2G`+kG>4CvhDjHgxI~qDnvtjfs_Q6`k_}pZ z$v$D2$3Y`TimPguZDodhsXF;Dbx{P74!{AI3LgwO^_V`7wsQQUrp@G5;OohZo%k)_ z{k@$~#hr-Yxr;k1JYLXq!yc#7>n`em@5A4h<}fJ_3+4#|HW@GZa1cBN~9wKchw#aUMLb*W@(Lz;U&Xt9JO+HONx0`gmEkxIH1ZaR5g_h z^ii%Wx@Y+PR6YIf)!AAB3skh)PPo{Yw_0htvea=^@v=F2;0g%du_8O^bhC+OhaZj} zi=RDy<=ZgkXg{LTfB(JuVQrr_rd@6+Ak=G@cN8?BqN`h5GcyqG6u|cY_}P;)*L63r zr`%JUsmX4k`!7LfyZJ#^(}lvC4A^ufAL|JSM?G*AqynG9%H4{>jpb)sKM%bssejf- zfeb$rK_1%`*B*8`_H0`@AKSj!G28wCl+Hiq_=lFk%Pvk-LSg~6P1gtOD}Dt_uVZ4_ ztkuGJ4%txjd0!9w^tnFxb)a{@`5a4udILYmw=Mp^7XX*0aoHh*g6t;Tyx$c!8#cEL z+Q~T!x>Sbi%?17)hl4yccS+7eQP95C56<64bpIht=c-xoJhkMFWDV~AP^YvnBz_)> zVuUglR!}R@)Ct)keHiCxB3g$e-avRr!ZJO90K@gddhK1By3>O4;T(S)O#;DljN;h0 zhqQE%3&(d~mtKny^oXd>=27gq7AhV^Zm?Ow_xqBri{Z@>;CIE|Cs(Vi3^|FX5-%hy zL;n<2wG#$U>WKJaCg;auP|)LGJ4BDX0?>S?4Y^CFqyR@ma>(|)_hvco@X@(>Eb_6b z)|iscckS7wusCk(uch$(06XTNGBV>lkLd^W@UQY!GUH(`HYA+k)Cj8^Ev*U^*ESES znC4AA8SmRSf)Nb;a7X4>IVd1N?N+>C9AwmTDp6cXe8C2Ed>Sil%%E^RCA^i9EMJ@( zk-BGi=l&=KAtx<$egL~Fd0w&*EgNo_o`;w4+8vMelE^TI*vd_#?eeR2;W3Rb?i9U@OF-s!3Y)U?+?_s;#0R-a^4209k>f;*p6j~5p7=l-Ih3_(FM$F2Z8a&GmgTbW&c z_f}CB!dmez`JU4E&-3YFC&X$P&rTLc8Dkaiy*b~{pcr3vILPXLM0W%jIV}HG-Md^0 zP}t|*Jd&9jPyb_l)C>3#c*k$Y6QWz@N0j?lrZ9t#$9n!+Ie`Cl&spD>E{6Tyga!#u zHC^Osb3EC7e=rifo9p%=h{rlsIjVR@^{?u46DByxqUn8mbij$A)$JA{{tQVfuqoju zb3E+!ncZ2ljkNLImnq(OA2pu$d*4iDLau0LT#}nK!&tcNfLyDtSMf|cV%oQU81a@x>em?-U63}Pqpym9m3^=W#BqgJz}JQ~Ja) zwhz=Qt}vYo^sSxGxzaXHc3N<3H%cS=ZC?Px^7y!J$JeKT8v0i4;d#bxuR^o33aU!; ztN6<53*P^x6>6IrDP9CvUf8u=bA_8^sb(I@R2F+zyOV(ob?tgfCwY z%d&53op*b5aqMNJMijEN;+4-cPJKMbN&bAzgHXFOdsu5D{RB7*=4Ukq0#w zcyV_|XF^$Fpo@lUX+}$=B##_a@-8pSn>YCI-CP5D@`XJDs1#=p-+aa2{Iht;HanJ@ zVtT3c?fRt_8IBW9Ekq;xgz+4iM0%ly^knfhE~XZ;Uq&m*QIB^CNA; zFFPKvm9OG)tOtHu5iOFU^OM4x=^(algu5>wa@5?b<@f)#Uex%MH*bDcaHHb0_vs=gH7r(FQH7>{w_$EX-SiG`MEI^{bW;!1ZqD<&q|fq)z{75<>HO}{%2 z-vJ3W2^>O7vsctyk^e=mm+ct<@}o@Fvp@^;v$LEuSVuWL{lnc=yRzH}<68_)!+~?| zOlPIbJxZwVzBuu^ZZVa9&=W5bKicFVYBZDa+ZdRMlI&5yruJDg^Q_DJ3T_g=g?baY zx%9jNXYZ-EPH;d3%+q9x072<;&*vfkstgP)5FZwwKd8) zmi*46IZ3MbPH**6R|8a}`Wg@$I_*jPHcWx^tN@H6I4MHqk}&hB8}A3;);vlLs>?vf z+WvzTVOsJcz__m{x!grEF2ASqjc^c!VLZoPC*#R~+Q0)*j~&3CWgEP_BTIh=N)#GG zwP%l8dEo$LSJG4zh6QttnO}Nm+xoa19jpJK*o)&13v6*(df4h1;BB9qrqAo^VMCO6 zq2=bOojx;{xISkA+!hDxh{!(vs3_vMBkIiqB)mijp>J{`;|`!D9zmVr1WDKbEaCC3HF15;aurfZgoM zA(NlML^og(#m%lKbBeCiP9tA&HkkhfvdqS~*)r~qW=9y0d@?8Yt4bHb+nEsi6+V|k zPwb7=z4O;(7hE`G%X?A7oE{uJWdXcdbsC1-u(pr{6omqmcH%q+?ixCOlQ>(DX9V~?gOlVJ_YW<8_CEV z+4Y$91Y4~NXBARql|T}REI^ZAu7+;ZQgUK#!+@gsD#^b!RrzJ_9h%?QuB=8bua zIzulxewVGen>^`|M_55}MbwbBz%1UvEL8`WQsjV$dG)U-+dhF76!p2IJs$zt zz-8CN)q?14G*p7kO&0E*i4^Wl=Tz8EAt3>^{l7Q~xC1SkH2G#<0pYH+K&J8gK+nIv zvXz-VWXtJC;!J=Zc3C}D-|`+~8>}lh8*p@{KZbg-0)r)&IiCsuQ*9iS+5W(S0Gqxw zq&lVL6fk>}gA#=QK9k~{@63MC0Mme&@fFV(16-K zX-b~194Zzy&m5(uwidK9f6ISjLA+Jhba3EPaOd<(&;WazW56j#n#T2Ok7eBp*-`H{ z8i%wEXbwJHC9~)%spj zNjUr0oB$Ki3yyAJnRsa9=43I~!FYu3a%#CXt@U((jF+-2jcHDqls8N#@F)RN&1RI? zQW{a{H&z1F+p}(CI={4!guz}=E9%M?4NC?ybYVY~44UehXlBGxX>tj5ik~hxx)Tv4%TE|PaFi8By4pb&W zHb%DJd+DC<9f!9`cQTB!Qb zDYqt(+2Ni@;8UBVxda{+qMiT7?Wio-a;W1UH~L$0*kt)wG^N~Z{m<)zhP`bAowKOv zq$Usc&7VG$zK98M;N36qgDbmTR;!~L0;O1=77Oh+HaboV{9H>Ey+zZWyXv_SCaH(^ zws9(`xDT+BB9)d zKm%M_Et^-159vOj0oP4|yXs-SuL{VjX(3G-{=y#6l=4BB^8dRv0R4nkrVCa$FD-f0 z^^dhb)EAOkT+EA+aWxRuz#cWT=YQ-N!WA7&DncdL9-!-7EG%W~MrtYd$a8@DOgI3z zX;_-`&{o0MVV3+={Qx(DebG-NTdQs{?>xL-ytsJ*(RS1S8q#RO7vX(Xnl|!z@aQf@ z|CG7>8{uN6*Qnz+8F?3_uQ_HwD1Ru+g!aD3#($fL*^Y znstq?Yee|>z39^8wIXjRctL<&4pyK0xVU4n_D;Sthw^)84~|hp_%SCmxmM@;xmLyP zEmvR$g5ZQ$iX*=Ec{IJTp<}al{Neq_%;FE@>>HC2%{CFNw};Q$FGbO{wf zG<2zY%;mrKq1_uuXUtk*;SIeqtqJRdYecIAmmq#xD*@E2Qgyi>zfy(PfQPy_24t?J zNYyh3+q@+|>z=r8#<2&Wh8gErW<_{_4*p*&oSWs+3K8uMrLmndNeTHk?|oEPI;2OH zN*P{Q0pCW)Fwfp4v0#%^1*_d0?S%}=tRNzy2LUa+EX0c^*xiq7cw@f0cA*yY>Il)k z5o~&|yy$F`KW?n%ubR#V&Sie4#Hk!mA@~!|P~k=~PLpQoED??Uft!}1M5WR1pR>!I zJ6x|)TqxcE=by71A(>vt?9*(~NSmDj??xLb++CJFQiLKCIUR9=k#{@!<+ zwg<;LOLp8IKK;ajD^3063Jiffv~u9O*NFFyi~tKqkb5ON^pZuQo5ZE)oW@rm5wfA< z-=c?1cF{ri&U4#==RFE8R*l!Ng8@!Lp2CoWA5Td>Y8c-xnQRXA8*7iC4Y3dAS;pX` zpYzB#)D4Aa00#LQ~LcU|{HW=dgj9HST zL*Gq`HZ*&F(JrT8d05F9Ic~(NL#Pk8izXfM-OlaW+=f*9GpDdY6jP;lm4v*TJzCD} zx80s%1#5|1UJY;QJn-nxWfk{LW*S9wp*AcZd~z4|Ev*VBcjqS@R9Jn-YZv_Eq1JA* zrD9O3*H82=Sguc(OD@nmpSK2@S$b>&r0!k^E&gZpn)?Bj==ykzADjltekVR>sAWR% z_{X$5qSLJ?wC!2G0AFNhf5*kB@_II5Fz)Gpml8nEaw|BdARcTSZ4CRB^po<^ki4E8 z(mhrf^6!VI`x2#i9W(G)fVM=C(@eB#d&RGl{(hrm1l0j0N3yN}I1L767vn0`UeETf z2YVCU+*)wdang~xym%P5@iLgZ9*gSpId<~`x2^@w8xfSS_DT2(XMH#>Nydb#F~ z5cOz4@V#Qslk5uruNEaIEj^sQgX*dI%o9l+|HK*Qgulp+0CzrjNX9DplSw*Fj((an z6t8a!Udub}ou=CB?<#s_NF3C3zrJhh<6Z<9s(2J#gvG#ppjLN^hs5i<8BxT^_isjy z!m7!M19pjaQdO&rjB*%Hyay9fa{l1g_si+5Jvz;H!agc%-4|VX2P~3|G%ALa)C2iT zsyhoA5n)3#ikB*UpMoZvBWD8!DMZ@NRZ;8V;T~Ojvk-9C$?d5S6G}oA3Oc~E`4uoJ z31$uy`gCTQpH%)eeMRKRCRq61zSEwV(9GwrslNVAEaRS{YZxm*{*zc@nMNLNeEYwC z&}38*2#21lf5gUwkj27z7TZATZR^#CHFgtf6|es(*2da-x}DU8wNuEz4<<<^B8c4?ju$Jl=mR3o|qa9{E=PCRxep1jZM9tN(1HJ)rGV#NtVm9W0R_mk7XCYys6`)d0 zX~D-2GIW>WE>{0ZvCOj~nGv3(*TW*k70*uGXXM3>dQNV(?A!f4?%0QJHG8j}ln#BQ zI=53Rq9;@_W=^J(t77PTUhcX)P_EgvU+!`KtIt~Mc9`>%3^TjTKk<>eKad=5IPGaq z(L;_nK0MMMD;Dyu%4>KWFWI&x=lW(APOh&`%H)D_q(jgs#HZ%ghhp=}fa6l?!CZE) zipzAzu;tzDK76P;rQ46`rDE*mAtQPX?F(GdjWsTQpH88}m_}45jeXu>`HKbf(+N`2 zA16@&vmk*?3nT~8xe4m4EM|Lidbi*s`w`>fW? zV9sD`{v2835WVyWTBSQ}8?LJ|$H~wnxZY#!$%J&8$Zr!|4(q zwT?X80H#)svojLD)60@fj*iT;LA~CmPaVG;0-OwW%Ed+?tx`=xUo-z*o6@M#KoXaa zK2NBjjb>+ibxm*Zv@pbNI}PXU=eNsuFM~msLQ{^9(P>OM#(Vakja2*g6Q7iFKL^$( ziE)(*30~j4&lTv;aJ8a*ad%FEo7P2Ji>2oU$?-tH%~Eu~5T=UgZbsVbC=d7Hu?U~A zt#Nf+4AWp9$$(%!uK?D@7k@`1gIsnZ()bHIAyumR0tuI+ZkDb$^}FtUVISI=zRkg{ zgX(%3tYk4A=j9`2OK)PNnw|HYZmgAj{OeWEzK4Ea8EfAEnh*&P?^!YghCHWn|3}!H z$3wkG|Kr(1kv$1Fib^%1LK&v6Xpw2TDPa^zMV1g|jHMElrIHYhxl~%nR#}HEQN#?D zC0j@{vP_y`GM4$B_n^9;&-eTN^@rQzHs<|$z0Nt$^E~Hu&UtTcjtt!pC|WMa3n(nU zbgf(S)y1=V8O18@&tVu(g+0`p-rT(p<1)SKdi3VB#Ln_h$;ABc8;<&pnVRJwITZ;d z`}4bd)Iz3_mn=7~`L5yrRn;+YMB#{?vQUA)*r+?1C`-`h?(J($UnWm5?+Oo8XD;me z*IAIO>er<#6ezQ|wFNzG+454Qt-H-VT(yD&ccRZ~hEAmDoECen7LBoY+dOWwcYU6v z{Hehvz;riC{T8p1CDZ=c9C)k!F;~CI!c&4<%k7@Wo>;yF+v+Oc_D_29g=0V87Wm1s zQcd+~9tES-4TQWghy3C38KK$@ns`XoVN<7Hb0Af`&|uqvQ942Y_4Egic$+QOp*ubl zez~=`65biF^7JE8AlxWbv7y|(Hr`Wge1Vu+$ITO4J0taX{`=z1$g6?f0f)!S7G4dc zCwY`^-Y&w1_``TadEvX0rVZ19V;_$c6Qw4t^%8U1+o%V_GE7B$kQ=aGJM9HBwzppK zNI!nm@vd^DZr)g6H{Ox?YT4f*BHmYsPKIt9yPf&)LyoX_~Ogt+d~|bErP2 zuleXu&28HLlS0*7mc`qvabky(9&g!+X{u3=8PNLFdx*?^9U{O(Wfat|?dj%?e0tbh zQpQ6^%Y+*J-1LkxUgSP$MOyXb+_d{de_G0w%aImp%3UvrA5{I!)a3@(Y_B_y+a?+O`b^=1<5SBr>R@z_k(=kgl<9_T9u`R_qYJ-EK`Z_A(uCV<13q8x zVFirD+u;Yi`kE*fQ~K1%h3(7&r-YtK{c!b$t+s(R?DP;R;X@}pHku1h|MmIMIr4`x z4m3dod+-E#IDLV8PlPh!GI+uVWlmP|>R7cpS>{Qj7N#@1pYP+e5+LJv6L(Sl%gb#e z5zg(g3$6BJpUw>Dq!1NhUP`m)t8y{FYs{0+OlM9`Ke;HkOaxx7Vs08oHV~p*J&rqE zjn=uZ6z7>Q#;wvJYs;wKB;;@IlJ>J_c^NwMq${~B-`oDoOn(wwose`b_Cac@l*)B@kyPgZR#?GJ9_5h$JD-?vNb&B^+WO!46hDmf&SJVetK;xLEAAz9kY_SWv=sXaQcsSm6A3hDQh%%kvraK&WUvZ1*5+R>dWw$6N=4v5rx zy}BmgE$6$jV>WdwJz4koWg~3$KahM_`fcGY?}o8H2HV!{>Z*#HHOp01Ga{X__#UEP zM<98VvDCQrXCpFmGd)BurdGrz{8QCxvXHVDTkIabNIBJ8z+$k|uUk%EGa)wPknwwC z3uB&f(oSW@8lj}7NIO%`qS2(n04hZl0QqH2%J=;2s)KuXx+qxz*PVcMSiExy%CpLA z*kWa3Br#}Q*3p6_y0_jb)gRk5Iw`gO^wki#b16hll&h30nu<#N;@9PSWNMw1+0Xiy zt$JPFCgB-lQ_EgaM~mMLw9%&yAZFi}T(e;L3?nT^L^RsD*?=_g`i@=h^@Ny(Se*sQ zff>6`rt~aG|6*Lmqx~)Yntrla)i07NMXkPL>!I4^&&mu^qJ?r;sIQazrf?UN-&D9r zd3GA6KhnD>Ct1UOm-kL_OSfa)j++z+Sy<5=gSNp)IjL{MexKHKc)pMFdZzpEnWCjW!VmUSGBgej{1%JM?vLP4tD>B zK}T4{_k3FFb>E?O&>O?gn`;hMHcyE_`&b&}sWmkfq z3Oy_$MNpc%7gN@IO(!+t^`~h2@TiKQ{70(}#)Zrbc~f5xo?tdiUhy;Bp|Jk38vf_M z1t>IwR(^odPkb{L-0ym5{Aq~Q#>!k^?cHhJyz+IULAmq+!}M^cD+5y7hUIG_D3X%` z&&qaT!o$_n6gjo;w!8gP9XGm*b!D<>=8DP{NmR*d(fk#9GM8^E(F#*e{^Yq&DD*Oq zGe=FzAi18VS|514k6o>r~Xu=g;HeuUb~!dWw~vS_|P?93y@uw>S%{o z%kMq`)mM>n7kcujCN2H?+`lm{((jg)Po;elI-Gy{b;$aUdmQNNgdJliB3JJ_Oy26L zo^a^)W0Rrpnp)xls@s-sljFS|@~&6VS)3E(W2{qI0n_)E)wMdgY{>pzaW69%UdmN{ z_p`5=D=7xx!YEq%r6B8)%MCNBA;t0UWk1KVBx^#6TSJ$ubo%R))8N7FFnFJNZE)2E zA?i1z?I=a2lvyQ*m0*^Mk#dH^e8Ab5@s^+?;G!m+R(+`zYM{6)d zlcF}CN$kH9LzPVl4T+i#rJjW<li#N@uAtGL^~?xOg=1t{rlUw0B|dCpQlloqjG7PnsL@Z4+UV3R^_0r=DH9Nc`w+4~Cddl)Y z;ggOYi=mdUs5W1XB5Tqf>fK_mekvQhQ1(?%4QKcT$E0&c^xNrtau4wdtHH0bVlnQvKc1)X%=OJ z==qI)o^Bgs5-U5h23)v7n^qe+EKdsF)>Gw9zZ??Ox6hzvV#mV|0rV7yt#l@ZQ<~6U zXSCwR5wz0Mlt?8yNr`r7jScZmN=f6L zk29>!`xzsNx)v=r(gQCLEVkE9a!K|rv&Sd;s~Ntq(O!7|bK4W4Rry}H(dE=Fbi1B! zuFnSUOxTG?DJE+fi!E$#JAZ8#7nW{hn=SLWoi_SX9C*%%p3b7Pe-+pi0}uhJ8{UFxlP^{lA8C1U3e z43xHNPLH*~@T%$e0log`aqomo!s%BC?@Ry@PC&%Kk|yJ&E=Re!7Y_rdOnXpiN3!s`3a`abap+D4M>y8y)oe%^l} z^eo5C$WeSyde!^jEliYSvy4-Dp%*SvML-h5b_a&|LfUi19%H|bGxR&cY-#ftDR^Ot zot?Uo^piz@HEcA`xE^^x{=}&i%_+F?>b2X@J0PRkYp7QyzH)8tDR$&{Jn{ruONxPiwZwJ-U6l1Fk?;$Zo$i zZT;!eisjkbtCo@3Jx6X$02 zRh<)Y*0RGRD16eq4#j<^z2Yu9sBugv1?#NmR+GZ}al|X=djL|i7+9n6DKA_zgbcCE zUkP%j->O|2eOv06mOVA>b)hLMNcE|k!f+?mgx>Kpn!Yt5%{f~eOMQDHeDPy>S}ZZQ zxoA)UUKP}(S#UYvT}SyAdeDj~FJ@xh)V+5n1QkpszVx|6Vz^j3k`pp{aErP4SAi62 z*Rzai>FrmN2@*@AF4QQcM+h{v+GpdFm(iWv zJ=L?{){PJQim=t|(SGbvH>AnLOX2(WRR|%@%!mCNAG1 zrz`M#vxj*zM>=%wxt!1RZH{7vPu6xEcX-D&`%2_4|Jn)(b9FW1bHdJrf92dyyW7s` zW9nyxmcjzfv!!r|q?L4eMs0vhX=O&z`Uc=eC>O-@NMy`MgT6QLvg9Teqj};+|cZ zJ9a3dKfK*x5vjc^*C5WJ+F*DkIerX-y?ZjzA7@?KsW-A%gk8G2lSDN}!5eYGnP5fO zR~<0StV@>T>w_n8{(Yt?uf2z`Z#t^Psb8OeytFUy0PkD@)HYP8H{0n;XfKsqYVPJU zfvz&h*kahj*5L!<1c-)J)ZJ%p%R=+(+-MRU_`2zggjWd)J`GB{>(WbG(BZRXHszvQ6(& z=%WjZ2+0N~pDUG9G&L`$?B%S+wG4SzT@yx8ejbcmTWxcR6_znx5@IDPeq|A7?5=Cp zQ~BTx1)nPz>&m~c_5QM7D^`!Orjo?!F}bJ1G$RqR!~G5xS3)`qxo0jhVN1tJjnh*R0L67qLMZ-u@Z1{B2uP zP((C)>I*oQ3^YZ7mUyY-kV&jwG&-uPpYyEbN9|ALjK7mj#W-^5tEdvo1HZ305v@LL z_^#~pjyR&EOqo~o&GPcRWD;6xhA`k9@A{b9l@Mcctyi=`CV6e!ixeTcVW#}GW1CL) zm^8_T>ZP}Bj=$vM0pv&$Jd|~RQC*p?spprCHHooPCiJS!Jzq3#Tp3FHkcXD0U(K7! zFSC95etXMAM{w}h)oWv4e^9CX_AtpkniKva)UDWBn9c4=dm^%3av;&l$!xFSo*pI? z7{sAJpK*Fc2=6|zc(oh^5X;NWS<&87)r76?>m0AgWxkAdJpu%XkKC;S8L9)&-*a5L zqU#>{m9g-Jn}r3yiv0*KP)ri;5~U+i@I&9+3{>x4UAguu&ZhGcTr1Z1sP_x|BYPAI`=a>J9h zzrN%!AAbBt;p92ww!GKSakAeQH>$Stm2*sGKsvg@GNSnU`evK*>$iBVdzSCq{&pwf zISi4|E0y!w<8rAMM;I=HDsRkyqn=G7r`4)$Vq;wa$35=nUe7H|{}J`JBIo@tu2boT zy%Cx^B0f-qe)QsJ-o3{=TBZX^(%+A~`LsMxj1~RF;9a=J651M%dg`Iw;gd&2SRoT3 z1xMsR-*}Of;39n7Yz47sz^Qp1)lX+*xJ+=)$DICbb=EoMW1(Fw3!{fth*!PcF)3Od zaXj`3k?@pWM>tM$n9|_-zifA(EGT7Pj;W3mM=IBjLT6j?kde`m@g$gdd}`=O)Z#Zh z*Hc12q&_EBUD&%N_|u{Dr~s|Ux0)?G9yp6-ZqD9TkltOF86T-zDj2xBo_tVkoW0z; z{$su0i{7&_?Q;v-^*g%TWQyF!38lmw$h!W1Sr*FJ4o`KWi-jiufDJgt>morUdX zUd2%EjGX&Xe7q&6bV&lPWnAgz2yadk8ENfKPB3UZ$b zaTa18u$N`^X@}yPXDE9Kh~mY z&X|GYZU?hVdMmCld$w*YVK9B)um8Jm^z+}+JJX_e6_vIYu%;K5N2nfFejcgQ*HF&9 z9kl}O#&ED53rIG&sAN?RSulOO;J$sjPnG+ky6Jfn$|Fh^+~5gTG+qDfKZk_Y*=w3Q zXvvNer?xqlJ)_KA@wYOxV!Vhm;l*N2qP$-;7&|t6Z<|PY9g_ZGoAUOEn^+V3k)xme zl!dr)G87`rYzqH3zf(=K(II#`%75QGs-9tRL!x(Ht^ZWNtYPVkZ^wHGKXH3*be$hOH)^z^FXUEXvc<=u zkNw>0O!UboefzcFoMD{vyL?kKC8niw5AEPGO+lFC&73*9`yy3?7B-d?{B)7y(rsIC z(xw(072>Rp#{OIn2B?VZJWh?v{?}9@X13f zXtLi!(639wcwc!e=IFhjGn1nw*SfAI|BZLidshEUw{5laxL|h;!KYO-rkePnKEGob z=PmmrvnalI+tV7dLa|ViYv)*VOt0Lzn`+CNZ$7r!f`cTxeeI)%9QWLhck9Ef+H>C? z^?G)u8+UThnM(JbJn8cu{Wp7k3Cn6-`@4W{{G@8nRc%s4Dd`Dw#gfGCeNa^7#%?OUbBsRORlriUCsOa06F-+c zI{K>U^1T`Md#X>yioWrR%ugXV##^4t6>;wl_AW0U@$Y%9q|U>QyZhv z%EKAdj&6%9rk^Gn_8Th=sATs)-jDBD6>nB{%7L*l!>@$(*lM@N0S~!`YVG0GWV$|$ zrGdA$q#pU$;6k#krks#wcbz2BdE0c7p)dT7r+S$nSBDy`DC#k`J>|}EW_?ZrE#k9ZfTu+IT-Qvw(k87)te1wGe^o@nCR2pOYiMTw8yhw?zv~As2X%&`7-^s zbFHzBhPq=9|JA^BdONZ#E*RJ(!WgveL-B{l7Bto*toEq=Iej8(v+b5Wu?bbw;PJNm zVBKK+(bS1YLCw7hQF9%xC)R zOVg8s%!ew?KWu&Qh5L*EZ!(R*W!gCm+xUN8sK~&pmnT#Y=b}HJF|xv&mTIlPY?YId=P5 zkWz~6&C)A$3EenMLaDsE;}4~`CjST|?&{)Y(xAWafN0UG4i#e1BF3O}vR!m4<9hB8 z3rfv)W3b8$%hUR_$r$M_6&3eFMql<_UDr&7Jj@%7RKLWC2+N}fMZw*PnU@!r3Hz+o9NK)k`+LjqOX>EJ zY^Ny}Or4j^DjU6W2%}{qPa}xhTzP&o(Y-pcf*L&bbZN=nY6p^-deG`SC+RK&QNhLK^VO{$%OlUmgLEc@?McmK7zdI$2*gW}H#x(1I-1Fqg zW2{ICmGy>elP_Q>=by#OnLSs7=JXuYI^wNUiRZ?%r=_-;Yl}=12PCg}mNsxzE*k|I z`}sJoe-0&h&9)xHvA10G26mh&e9>Zy+L~_2C@!IriIvcnwG1iEY4tuw3z`f=LiMdV zhHyWew9)Z$sFH?;+_LeYhz>~AnYEsXw}(0^=91UL=PtDBx)oPiM1x5NbM&C|C zOYxyZVO^)HJZcz==e6B%;P#=4HR<=%;Ke^vt=_Uu2VyA0aft$yp zy8`PnD_~$$MCfK0lJAA3=YPtafP7+med~icw=K9&G1S|2;->!-vuJ zx`dmuR}D>qr;|h~{2t!haOZUThp*4|-z|N*Y^rQcsk}Z_>FDX6?BVhD@X3*4>Pn#?I01$oRNv}sRn_o>bl{bJTRPXh zGvEp8`YRO+Z71IKx=<4WEn++#6z!@Zbp%T@oYy$=5BmCr;ZU3f6kY5{p~byYk(>B8 z1g9xSIeoET#An7bb~7q9ov}oV`=$2sI}Fy?lclXIN4zw#O|NmlqI3jhvGV}MA=<0E zJZE$VGi*hBv{WTC=_g(_3Y*KBf%I1el)0!WI(*AN0j(O+W$=F6jl3O_VaU9mt?+&V z=#7C9NLU&x?y59?I2r$GAV+DdH{C#TgFIa-PD0Vv@ojef%l5FnsA8X79CoLfme`V# z?!dv!?^BYf%NFXyO-Q=B2YOtW#cvNfyVUs0chB#iD=W5#OG!y1!VM>aw3P+jTCV{IoWLHov3pUNW_ss zlb>3N$xFvZihH2VRdUcH`@~V7(Nz^wlIxcT<-NM7dsJ!L)mAzPIh;~4caW7Zp&o}Xy*a3q=0>KS@K zwPtGCXWhoNQ_ek{im!W1l^To!E&YdIk2e@+^#*&Csa{#Eu0~QZ(u>4<+-{s~l2wmV zmNPe=bXz*ZaO%nAO;h)XXlTSH{yX`}H4Ek&1?t`jE*rRG>p0FmoXkuu{(DL0O8Drt~x zs+C;C3-7s%o}4lC%95@$3MVeT(m8gsqbyNo`wee#S`7Mu0eVDeFt6boCWZmzs+CbXIUQr(0FU`ga0v&TRR2p&5%F$O(0v9rn{vM zL)DtvqSNDU#4&%c#rPF>9E}>lH4CA2$Sym5M0}9P$jH1+`~4h6T3&B}z5r#9vB6}? zzad@m{T`MJM+OAG1RCeZm~c(Fc>ur|SSe>;KfuhfRfGsIcX2P1IX<_GQBL{!d9)8BhrsyG5K) zv)nsUu!#Q!5k9}aL4sk=CvZB-j8p$Y@fG1%E``B!4rBTJBHw2M;n(6qsYjKk}n)*FKfwQ#t-L<}B;qGsA&Pf6# z;U0|o^&9du1)9qAhcbHsO8A5J^*y-xgW84uQ#N>M_7mBWQ>n-)HSvs5k6g5ml?D3nF%~GqZm0Y9J~rZ<41afN_7!EubQ~!qs^kq1 z-OV$;`qgICxLd4KZC>jv{v%#-1|x>@7DF9OQG+PIMWJ!f})}8!1 zzlEE|zeIKBh`_zH)u6#b6wATqfdu4Q6HTqt`X@1jbnnqr=b|~oe!Sz)mE|9-pgi&^ z*1)G(EXz@5pqDgV*TUg2`In35P5T(s4b0=mYBE&Tpw{-`R^I3HNGM*!Tbn_uJMn|1 zCpcUQNqI>HNkz%{Sv&aW4-Ao~)#=fUa)%$XR&~@tYq}|V*(ow-r!uGH23)Gv?I}n% zVa)I4*8KKB?Iaj&Upm1rP2d=Zho7LAK_~_WS8BZDfb?$__yZH=X;1eQK*)9~1?h)Z zZPj53t?^8)8wwoaR0rGmkj@ z8KY0m+cyhQ9&rNVLP#y!*VDVQ#qb`fk*vG(wE+55+ciBTg@(LU9rbfMBr~V1H@h0G zlo`IlY-@d59{!Oaa`$|Av?P%X0~3^-Q`=*GFf@iF`Tq_H@oO|faWS(YrVuF5-) zs&dDZd1hj~#FLcb2181;^8YVjkJ2EgZMJp>k5fp0>PnZp3JrYn->QTx-+G_{`*vaF zvMn)G4u*a_75gk3le0hPc#h{)zVLQ!o|neh84LatRh*&6DxuCHmbr843qd?egndf+ zm&6W+{lcM7_83&H$q|ePd$*~d2wM?e%?qr;G!M}fcob;Xta6-$p+tOJ0U=8=)=ZY< z+c2!xBJc?0?sfq57XromKYp5nu?V)bfcuqW&ibPz8O!o5Di^W0I^qv+!CPaf<^kV1 zZY!Z1@qb$usP9#}&AD8#l3lxCg~&LXMp@_nMY*x-uF?liHw$&jj{I-03BxR4Y!x|_SL(nYc zC-wg}HAR{cUXz>@ir|^60A*}0zs{^lz+b4YcQ``jBE z!$@?vNV5C1(ri;5k!>B!#BD*duf3^gm!df)3?h6Kafr(UK>Zi+ckQ za&$HMk!%ACO{>eLUR}7mafK!Ot%BnJ6}6K?!6;o`vraj8#ERm7yw!#Sh3Rk9+WdH! zp*ES~qPcxt&m6M|nBVL-WDqgXgO`^HO>4pH+rHfadd63P_oczGk%(Vs`ulg%LU8NI zkaKDrjKsrB&wB%KaQNMzJJj*t1~AWE*wF~>%cUCqn5GVb3n?RP0#E*)SMo#}Z}S@x z*HrvI#k>&QMNjd1IZ8hI=xFxQ0zSf<-G^dIzD>f}L*M^! zF!cB0z#loM00)7`=#Z4+ys$hJQdo($H7TF>DQo`YQvgq)tY(YRQ5k+3&D>J{BQHy~ z$@VSA<=`)f%bnlQhxB{G>{F{cuIJKNAD^%9L$tQ;mXPnB0(uy$#NS=qZ`Px}{SU~; z`hyomCk?rZbIh}z_Xsq02=ikyD6 z-YmuL1pkJ@u}60H?9RUXjciEbW#_7nylHBlg}w|rD-^$JT~Ali5G&V&(N-$hrZ>`d z)lK?;aqii0WzWxgNyg!#biVqc-++)|f1t zTFRq9!U1tk?jm+t^w!#Wc@O+LBYsSQQgkLCfqNVc`~d~`*i4uN>$|rg-ua#Zgc=eI z%dfYIzqI~;BFS|?5+4E?cr?N#aArs5S=TRO17D^`yAD46D&5%`02jU;Ekm`HX#$U; zF9Q|pv#L99fq3k;Q)Cxg3ng%w2S;Y14G9R!%GPRVLNa#@V=(V!pZwuvAq5OXKnDHv zz*@2azO#iG64gV%)uK_U$QSKeqP0a#j(9#ya{IXe1<#Z~$7PgoLQTY<@A~KO%yC z7!M#GR*HC> z-7g+5E)z^3=;Reb8WzR?w~0{>gBp5Z!{=>kWz4)k9y`;5%v?ZUW%v@vO5y|jz)KSh z>CUu13S5B?Hzi$-v3z`95B}H&q;r>P;+1283}Ho8rtu`?;XauMj! zTx|I}qEd2$cG$%Vb`yn7!BK1pp%HCXtyShm(oRo__UYi z#;@?>A3-m7QLS<{1kk27vHqkz1;50pI4`Gf@7)FQXO%lMcme#J->mCJJ8=KndH&05 z?>VW<{Fi$JPCgPto@22!8=|TGi0M(0xab#Cy}#(UzQ9nm2ZOK%om8bbkF2#l?hk%k zS=B*^4a2Q`)zgou095V%QsfX271e9Lh_Wxs^;tb!(6oWdG{M-D%sO>9Y z2}IyK`yo>PpXmYcNc>co1S1M}L=)Nylmzj4z+od|fO~0eS*19S?|<4DSmz5dJg)K` z2TlE!!Y8tvoze(LcJ3{lSN?;4XtNCl3$;b3bWVcK^8t`aU3k?xmv+eJvV8x6m8tqU z^U=f2b-&L+TR@eV1BbvRG;O-hB|#JhoCI~5HDu-r>~#@4M-IPl)?X$v<~5riyl39= zM9DT?XIp{z(KNzc-!2G_uMGf28o+9KycF9S+x{yU{ezaCf&c=hm}Q*q+M~|a+uoT5 zUHwAKBS|i)z=Gn(w*MmNU;gtnpj|Kp{6&(L#nawG(D0-!86jQLf<kHQMZq6I!~$)}Ksv=& z-KXzFE|r{Z7?y5YhI8JacK*;Zf8i&R@;MYYcnXN1I48BLXY}!lEm}E1K+*5aARRHV zt2bbV3O;`x6#gVOL!kga3PXY80bKp0g>LF>AlShtNkzqNx`exv(7jP`+i z2x)9Y0ZU+0Giy7$_V8G_YH)2i=L_`az2mp{&~JUcinWsL0u$nD3y?;cWCHjS;)?q& z+BOHI*=_?D2;`W1(1Xnn)83fPN58v>5G*4M7BUe4rATnSsso%DaIQKG)`r1enJqSF zIBMhUI+yZV!hIEnk+@-@nZ zxl@D2lQ2;LQXe|^5e~ZcetH=sNWim#Leq>A7_mD4*PIyCm|)xJP|@AUQby>>maG0d zv^N`QRlqT+o&48D+pbg$e;9Hz|BHYj;FJfmheCfBC4`8VAo>|{QyVgk(cW{(FEUUuNG#z0in+MU*Rr;A*ig8{p zj{4<^|F08)^a#i>+U8E!vm1z$+6{CA72*Tsb@h0%_xxV|;FEm+!8WzLck@A}-2KfD%(%=#sYii5~6!iFvPfd*1qg@Y;%tkJ#TD z%&wf|l_k^{W^nY#{+n(kSE*Ns23H;QyeMqtCVS%K-)_5)D7RJn?O1jueCyx42R}T1 zGV+bnS?p3o61(edUS6@NUUD?6M6EOD|~m5mQ0 z6b9pPBU7mkapFD;7K9iHZe6h8m9fBdWe6_IE2}S{(?o=QQgHf(CO$x!@Sff;S`;D$ zpF@6lro;(ySuECMb%NN1K^2DAD?go7j#eZ3?P7MKXV>+CG;#J;GCjV~l1x|X5QZ;a z5XOHKgtT_Chz0ZoGpKi}Ev zlJm7_`PK#b8u-o~=-rqyzbHaro4FjLzb;0$X&>^T{c<#c1q;4rE~hzNS|>nJXvlBQ zmt;I{yRwgry~5^($@wee6^v5o)2~d26h=Wpi9de6y2Gu=xKxZ5}9WLVGf1$|nN=Gqb z3l@-fB0m%IKS^*Jh4CE;6uM{;SQ$HCiqY@9zNf@^;&lV6Kw$({B0r~G6m^=DnG!8l zoSlEPAD873lTQ=zF+g^CYT>LeLpw0n*8^Ns=c{jQ#W0usFjVk@m(p z#aK_SnWXEcw)6y^iYoReb%16_P?V^)0+cBmT<_FD+a8b{X5Z`zk72sKg(xHpM8?hg ze6V_%=G_TYfpWp%6b}8|B6Ip@Q zw-~IWh<}V9gb_;(5X5gI*Br<3+PC3F6z%cP3%7 zwmAlrrbkNR?5-nb_wW=JJDp>ksW6gh3s;XLg7880oC=T%WiyfF z@I^0_q|RT?u2~UsB{*BjF|hs>(?bda13EKDdrXW1y6&2&gZi%VkwX?ScRV`JJiFy+ zPQ{zx)lqDHL43f(9t;X~CV59pEQ4=Xb4$<>Wm_B1Dx+E>{6^h-Nl;uxL^O|PAwrmq z+V-Vcg46XX+jr!x1g9mz45bVAq_zNugEyugygA&D3j7goGI2d+s!{|_EnyBX6h~$|%X0xSF0e z7=pvzi!bw7yeP>SYh3_`Gq`yKY^$UIQKtpyc~j{N>AJfkD;;WSp0|Z}Vp5Lw1+63( z|Ax-L$jn4c_pzQ0n16p1DGTr2wbX*VaAvMzz00e^w*gx{H5I)G!7>sdn z#b%RAn$z5!NJZ8>Xdn-M=j7;GmP z&_vka*bJecVT$0qvUEy4{P#z2fM+fa81n^NQADrj$p>`_0oi>sGLGKW`O zpA0Csja|^aWXu!S_8CnM3>!njw1wyb|RvhhyAl~D5hDJ0W$EXIDixf05(Y7fkHTC_OpF|3B8RW8J7(?t$6|J)JRW`3q=?{t zk8oaSnla647RIYKg7;n8GzdU}C?L!SLHzQAkRYZo{;7BK(+>1NK2M4?RcH9J2--2~ zhFL#u-SNbMCvc>r%=lZ}|I5Sp8l!@%L)0|6tI4jf2hxJY1i1}s*c1T@$%GHgvwrt$ zWApy3|6(wucQCv5Yhj6?xXX%vteA6|flrbTtbfM_4W~lIdX(2Ee1~ zy6~8T^AO|?cFo%3-8pqq7%?KnxRYx_N;ebT!-~b(l|3a&G+UjHzUwv>0Ren59-9S8 zK&XO=B4M^6QLPeIA=T}dUqL)Chz@$Zf*ib#hv`>;GIc2~xByOM=^P6xs-SERE#-&; zo$BDzJ&pS{1c5vQ8cUJ{iX;o@7IJvHJD8gu&l{~&FP-|xDdYXe?vg2{`ZS}z z7`_wU`fP`~MPxq68-Jtb(+mZ^yk3S#C_yG(<5ed=XXz42v3Gk$!r z*39sn1dBpQBg-FM5Ssp6JN}ajw@r6VP%(VR*}{6QzL2T66B=~zx z@YIhi-nd66Pl`{+y7ma5u_+*%c=N&+6`jI}OY<%ur7W6{#Q&re*A@!}QsuR8}mn zJ7@;QYno<42QETBlIb5uQi5j!IAwqHRbQK&FT`yg_HHz`>6v*WE&NT%byFyACPSOXiY=2bd@>b= zooUhz9*yP8`t@-}O2jD0s^vI;d0K^<~Tyy3!}UJAc7H zCHhfaa!q+>(+b;sYTqU;r|9RdTvZ<*l6H05=`1Ba>%ReE<5YwXB#xe-t zBFb6UgAVgU1RXv*Zpb-^L6Es8kjXAYSXP;zAZ6^_=QQ<^dVgLbj}RYu1N;gd~J zGL-z%&Se~kY%-4&0vQ{eBM&WnVwvpJ&&OT-({?BTDiF8rDV2F=F$kQAvN-@pDk<%h z@B@xC$QOGL#9oet2tXMDXetO2d%yl!61w#K09^~~9g5`Nh8LPRZ6O#}}2OfL+&k}3Wh_(nB% z5RjBs`>-CXN*mn97!wB7KJ)GGP;Cc=NKNkWg~`?TTfmR{(e@gmurqp9eD(32Q=j!@ z4!GH;?RDLR(GGdUK1Tn2)uk{3Y9-ERkDq6e%R57!=Izu`TmfMOcvmf&y3t|vveJmB z9fNDwpmul(N=e7W&ax*{LUF;5en?x-^az5oKqoKY$#NYrcIMddNJ@G!H(Oz3n>(3# z4}7DB|Rig2okL#?MR)D)5 z2(^6+0b$2BCNWC)?hepaszaEusVN`HJ!NL4WG%rcL{*EkyOwbf%!4TTMr&+_XUe@D zSt)2ahOZeF^^&hks&k(&4F#VT`IQpVb@raG^w>%Pq87O(cmt|!h#ioKp`!edpzJ?5 zJ#V+y|qFU!v)Y*DK!xrlC=$J>MwBQ zv5z}}O^SbmP!KOMtB`voc{_ciOh}HQB0%A=n+satMnspy z=Rqwi5baEMdH_tg2coO4#Xd6VeZ-Bx9)YxP2TbzVM~*iIX-_%w$))G~`AaaIcjc2z zOz}b#6PMxLg51XV<5YG)Bx6)$gN*&303^tb7eJe{wvu8rV97|d;o=zJ@n8~6@yMD4 z{wMhn{O9FQMcH~`xML@0@q+AJ`z)$4~RiN+`uj57P(pU z-OOkC^rLIY@hdvfKu&ga7XolQ&@} z^k}v?ev7lsC5 z%{(%_n84xS&iNm{2|eS7`rvKkfc(@%bQOoWhY{|ct;CjUwdU7S45CaV7^_n0{y0H= z5T9(>U7E$*!`ie#$dxFJ5YlN#`UZ%JT_3L7RDIvS^qT*8CQ!wDn3O0wL_pJ;sSuo6 zSm(#{)|I;H>vYVz6188-STb7VCq|K?Kcv3zkrvAE3{DG~)$6g@4M7`5V}&DueDa<Tq}_wAS>W2{9dh*1#@t_Q*bJ)B4x};*7CP+|yCTDoDI$ zYYCACh!5p(sQ?MCJlD_3ny-JpW--G}i`hPp7Mpg;-N~9_H!mG3gc^7D4~_V&zyBi+ z2ddz}6&`tlK;?AP?3`&SvI(2rL z2$@}gIN|QR3gRiQ9R@4Nwe7^j2vCgqq0KW3#I6l%Ae*&06jZY$SS`IV4EZwK3{MoQ z{d5>1;6&d_d?EI~=3qpw5pBD>14V8AQn&`mfu1&IQtSk|H9w^PnfbTY^yoymBq&Jj zFF!Omz(RtSB^~Vr=b{K)Mt6B^@R}?oTHTkzS^Bwh7U23rOo|YX5h%=lKItSNK&+AH zu1@yog!F_L?(q$Wp{a|rw!C;&z6fgow0dyjSRZm=>}gh`{#{JWAx1wR@8_)&2!KJg zzYr1VqO<@EK!XHV5Eg^t*%0-~kWwY>pdyO+2V=M)Opl#Ya zpsn}%(HhoPa?+oG2)QG)0gaGPwP=6o%_`}1e@KJ$qo2qao?B89v5FiY|2&$`U+|py z9SsnKXow4-(zK6+I4w$gnJ=eghK`3sG|!Iz-Sft5MMh#F>q2dEaX6`n`19=ve;BX+ ze?Dl#P{<<~*dQ;WN-&3@7qYLTq7Z!}&-Og`%Os4FAp-Xj8b2L2d0GPf5?k2}8QTxb zINUWT#}d9>@KL!IfuCO<4hyh`t6>$Lh;|3I05*qi4BSM-VG+Y)T~BskOnp%i;Xh(t zK}SXL%AbZik)&d)>Ari~Ivp{k!ee#MH-~l|>rvAz;wQZ49PNOO0En(hal?=f zgru7=pv9~+p5wv%+*FJp7mUQiw>Sa5x2$?Gd+B>yh;+SY`R8x*f*{U<-J~-Z(j_tQ zfOyv&t3JKW{|Cx>`8xtc%s{B*@iS|y{s(mir`{q!sbaNmvK{S+h2ZjR6k+OMny+N1 zFqhiR+uyIA!OyCBI{y|ei5@``!Y2Zi0ZDcv$-kr>VD*%z?QlggdIy6Jg^(J-QM;r$ z2Xv&M<}H#za7#$rHtS_KCC5CZI#nuJtA*eq0mMRfJ@T{e$_rt%5mS z7vwUn3v2Y>uL6EmLnW9h*9BX`Mm)GB!ao0l&{H;5x2gi#2EN?lKKL5^ri%nibZgr$iv+z-bfCEm(R0HvjKTR2Q3wY-M3&Qz! zKIiNWfQ(FYe#;?-*X+5?QyZA~BZPO0)4d;qPahkGpfcYMN!auABlEbrM}U%iwSkg@ zLtIMWOLs&Tomx|0R!>3dVQIbXU^{pLNM8r0{Z+;|68E1=m?QigIl}o${a^=8pgQf< z2!jKt>D&8Kn;ehA@bPtUmfG?;d|YptI9o`D2)7ck^nTs{xI!FM#z7>QV%rDF7YpKv zB}bH=S4=SA*2*2>I=}G2t|w>{Ic9`RxCM^6BETbxaFyOq#S@GFMC!jW9$B8K|Btfo z3~MUe{th6ZAYwxl1ni73$XEac0;f!KSg zViJD(GwpA>y}Kvf-TKS=_Oq4uGTYSCTGTgu{= zrR&O!PRnfWx#ZkF{WrrqVj2}d{@y%*DyT6)CWa0c#sJ|%7I001%dy=Aic26rolyr} zz8C&pqL}e+lx(l1$gm;7ynNzB*jZyd@@Mg=BfPc!W0Gib;TD<@xO?qgQ}4f5FNs1`%#b3!zfcZOA*C0{CeLg)=y|| z9Xy4eJbhr3nW9!xBXt1+Ww^ zo)8Z)3RZqoWGb{6uEjGMudf;V$d1Z<1)>l03o}Z-*Bx|wY!vrA3qqiNtVv&?$hPES zjI)(kc1V_BF--X8%@F;0h7&?BsKj3;MDP0p$VRPtu2C#kLFoT#%o++W9HXfZK$dB@ zP6cmya`)iNAg~<3f_((=r#5$$J&gU=N*u9(B)-tIFm1uZxgXnhitR^(dG8VwLc&LY zz7i-~F|rh!%NNBvN=;5!9@Y<}C_B8(z7x0-%yA>T5)e;%)FutiBu3p6%dx+h89%iW zfQ0?ofw{$Atjw0|z@4p|M)>x7o9)8w1-Q?MqS%(8S^Z#8ybq=_1YoQ4K_Q+HEHmnh zu~}Qm7=IiCXoRakBz`+rIEk+XhDvmUeLI>@OBi)Zf{}G%!1;qvlrGl>?ty^sB3f0$ zRsr;R17lvs$Nd}bTLQ0_iSmjN0OjP3RrFQHYWg5PghS7#5bLyWGl5h?9p1G?AmD$cIU7i1lWc5*r**m>){>_%DVhx)@P7)55%DClBDtUv2FI1n02lCkIkG}y6KFv( zNm9lr&*yK=Ds9V>=how~- z&jz@~DlT3i76+tjIfb*F3Z!$lc`>R(%jqwA0-$oBS5E^g8uRPLIHE9SEOq>gs!Qc? zpMO}Yf26Rxj-49aHroF!HCj|`7(HeeJveYab&PL49tM~4&C~&)rxqHl;D|0Da#Mja zzqO@hrjAJ*T76^Oc2{T}PW)bF~CcA@P`+IV;B_;6j-xTq>@ z+zi`45g;}pnG0BPuHq@+>!}AC+l2Jb&1~>#1BFqW|uI3vIk zS~iXFc18;aT;M0o1z$%jBRe(wYlg$dKkjsn8ljAT8>n*`JNnJi5bd&b>qWKiu2g8u4Tk{G^0@x9{@XQerwwV za12@q<97i3116dv(JrJ$(PYloV18RnTThB@Nrs~jF^Os^weU^gG@ z4^lt&ek^Et6x-t300?Huj55yy>uScz=MvV=U(8?#0A5CDSRlW0UH6<>hNB+)wFywwRkCDGOrZIz~5%?LI4!Ux1 zMZ_~4SuwN1iz^n$DTGhverYAif!A(ZV(H~eGNi)80oiHT?G**{Z6ro3D0?FX>2%Tz zPFgejFcGD5!l~o!MCcc{hWoKKmBvdjVYJP@b=mcd^>PiiB|4M49T2?2aQcwM^qGxZ zJ1{dy3(8QpMEs?vPDN&P+pwbE5Beev1w4)cXH*AeKL88C`GYg4Cyv-`7F!w^N|}oq z?7_BEGWep}*$m!ZQ#ash&43_4;(|${-2L_D+*Se|8%G38G(`z17#2W=G3O`ug9?8D z{&)oPuVW+u0FXx@{8Cs=UiF8g-!)Rb|G1X<@WsGLq4{yJ407#C|HF&U>q6YLX9%(c zP$S8S`w5nRL{z#XsD<@6NvH%=zkREE_I zPDBXlGJ&Fd0}N@%+I}eQ=Dq2Aw1lBL0c=H7epm|4#`*8R?c6>l%^9xK0juqa0TQbB&J3K`JUA&D#h5SexTv^8E#*vA|5e=iLzZsG%6Z zkktY-wkNaG5TG44#qb?29=#FvPK@)1h{>Fg)gU3Y@*^~i59_ytP2jgpAdak5;Lm14 zxD6XT(#QzJ2I&2Xocx+v&%dn*?aRr|RolDVPSe|dUGo8mimCA5=@oy7@YgKXoy4J( zB>+bndA@KQA2sPP_IRXza10EEd)7tibcgu>F_$nXQ3gy0_o*&@k-i&jtA6eRXiiH?tX$*iHOKF&-9YH)0U1 zBvw>c6e3vrT4$p9@b&laW)k}mnyypfL;5Bal7aunGXXLF$^K;5Gi(@vJ-jzdUI8FG zVZ{B~56p5llQ+XB!3VH)x?1&%=^cJC6u`GuTz9f5qkj!4DD)EVvNf zRvstb1K1+ZqtQX{`+6S+@(sYT6qPsp;RN4r&#KOq1|XUAjo}gCfEY&i!2WjvBtGZ} z?K;Ey@ASF!AtHH<0GW(kNUo6;!sr_ywUdVXEx$b!HP3*DBaRdT764Xo3=nR+p9#td zR*~XKqaTuD;A1E~&AhGgdCv2J2@jTH#V3x>T~u7dBe-k#z94(uRO5oWX;(gC8WoZ3 zNkx81C4|jDX@smAKS5SjxWOD80}vc1g9}hQI0@S_;-y5_y3IWGDuGh-5J0ocXrUZ& zw0cmM2$4m@LaaM)zgb>VeruRaD>9>FDx@f{QjH7VN$|p`JXX}Q8^r)2{b&Lho)SM)3_=os z?{H58@`EJBDNgpv2Kzt=^Ie5uqs0gEaNebDWW~k7KJ@f(e@o)M&>xwugnszDZd%|Q zTt+g$L(H?9Lk*)Wl*)^2mc*B zw5SJdXorDu3iMEDPd3UR*NAhF;_(UQET{XS(>o=B8T4=VbRcltLAW2R!zQYP?07 z8uwnj5?pQSWpn>g_pcj!syxtAk=~0%DfJHk7cYa8n^mwS>_>=QS7< zz34GQ>4#=>81S2ruD+%|#g=ZM< zOcg>1QV9mm70~%DM6Shj0f<^+h{nfQ>!sqOs8hn79Eb9;R^6|R?BXfiUl)7J^M!8N z{wPn5N*SjlhDsnowpbcMs!*X9ntucM_Q-O9ryfQo>8A*|kkk@8gW6b=K>^h{Zw!!` zO6j;yMf!2`K7?^i7*+y?6{s0AiKUP{{iS`PB>|5YH0}=(5Fm+$Z40T4sHMB3BbW}Y z5HbSkYm!(g_`w7~jHEzfWriqUX;n-tYrIKPf5=#jqytvxm!5;mu!`qF z3T*_`wO?YQT`^d>e=a&u8}yn3TC9}gIobb=QaqqnupI?-OdgkR z$2~9?M_S9nY9{e7cLxsz+)yukP`qAb1MP;F%QLVlKSAd{1_|hV^;z5guDiK#!u|8#cKv< zF|Bs!UGh5MYpr1fOtD7~04>SnIC)7OBqcE9Vii?4%6*C>@j(`33wFw&mSH=CGq?z#KDY|lWl9Dg z!1IWjK_)Usv@`0huV+xk5EEGuEeH9DNC=lXe&M7#j2o>8TYB#55Wu<<9|AhQ{+VJ&5`m1<17_MbakI%+2f=OMkj=LEF6 zh%FG>K}R^k|0}{G37byhbjE0#9f6jPM76&sK~z1i!GK?(-y#liVmk2LVo0DT-fOXh zzV5mq6*WpEP`9n?#}D7s?|^QOZSH&t6k7wrOxu7Dpmg0k7%=R&%9dDWQwBDsjIkRCe0Z6#3TDjlY^s_+XkRW0Y# z>Ll=-x1%^D{2AaEZn7Ba!!5uT7_s9DNGjj%9PSl{V#ye@h_-SNW2k`q0LNkahlC1% zPzBb&Mt)heM#+vP4EF_K1i`@qaZ{5l7YfYVcR9xPaI~h|+ zxM-rQ$WGbBD z2r7f;icxvVkr@CNo@W5Nv=IPYoaL0pwfPk%`0#hAM?!d`D+VYp);R4YC>)*Q*@C5O zC}U*@_0eOf)#P+NgU5;bEA}c;Hl!Ri(D|y0N+9d_Ax6G#Q#(07si6iMbCCIykLk@>Lli5S# zE|0C$PXS-PQvI$PDE}ZhOIxiyf@ZE8Ao7ogmciJFf;6C{SA>Jw(nSXo=wcHR{Uryf z?!N^I6S#vJC(UXDY)<+H9-MOcy6r6FLQ-I^p^Ycr#Bd7EE$;WY3P@2ll^CRC99kGP zuyL)(Z8VcRP{Mc&V63Fw{C4EJyYhI_mC_75Wh1;QYhx_NXa~1zF5X;Mux#KocVA((!1#I3EKi z9L{EK;s6B!g$J&i_FX@$n*earIr-1-f58;$O&+}a#pBj$1324MxiA??q&b0NAc#F+ zfN7eL*CnZz464(|X z&hbt=Lfv9|v=psQ=9C%@VM!0Idb;!9)XERQ3H+yGNA(a(7S}8EG{hu9J@6k4S?hgs ztv6YO5>yz@m*PVh$9PaLx9PWl$XN_Weu3ajVy8(m){@JE&={ypsVEon9|x(zfczS5(NNf0MA;{AN9}DSkc_p05vI(^t-)xv^e8PXJRE5#E~+s48#UT=dglx%9M=5dZGU42 zF>s$G1p^~%>1#I1^ZJs8MGa4U&$H376hGR|tUwU_p+wQjqaE`@)3xsaXVKYYM~As@ z)T(*3onXot+D~SG1zxaC?5;}%7!3w*2Ao@rhj3VNIs;@wsD_4O3S5r(Y*i!H_rxbs z+wNdlIa4?IEro_6$PfeY`>aijY$Sqdy_q7@`U`HCC0a~#0>xxTJ#6hk1CUM2PQc2M zz+-ng^5!*2Wf%W9I06Me1~fVl#KTkpFYt&jJ{(gkeZ>3qKqMH!f5Vw7bCU=#nyLg-h@dFw~$tcj%E=g2S-&ffzonyEReY6RF+up!7UJD z(dWQ!_W~p*_HZL|1v!K{){+0}Z%?^^0~tuvSv#Ket?H z&YN`r-2GZeHo)t3L2jq_Y~in-*>VL=NrL53Ws*(@ivVn_V{C}e{O?NAZwWF-@oy7z zA73Cp?llpHG!XGX(!y*FzGR2NRX{fs-mfG>gm695yYCzXKws$ri6}AhlqjoZ;FF-N zXg_v>3bD&}Q#kPa?&s+sOB|m9Vh7jW+Fy2*u-R}L8JN9eJBI5q%xnJSluX5B?vsqO z>}(JqQ<3cH2V4)K&iGwcKpQTu$=&x92>FlWEHAJ)6LKT~RkbCXw+AV=3WtETQs4G{ zO@;iK2}Ik_fnIU;94Q%R*>Ij1*bGJ}t)B4G;DF|@$LF#13h+BP*rQG-;a5dKM5O#l zMP)1s+QaXXUB}%2pBx=w!{<<@x$jViuFJF+J8u@RBjP1zkgx**($mBcj*`27Z%YH% z^R~WU&8@8)lsSsk!+%3&#{=tA(jtHZY?T!wjnLp4l{mxw#mz69ARMDpnb39`oNz-! zuPDztWk>k&s@(@MBqccoc$5HgJwy`!ea0gq!DtDi{R2FZUydh9%}O`{g?xw;VEb?% zg+b7w5DN|eBrT(}%y1i+Yz@aUOTEM~%oEx!1$cDq?)RSw834bN9EjT+#VO0r66fby0qxVd2qZ&Fd=v^CNgc{OXm+p?ZCD{C*or)Ozj{u zCNsME51rQaXgZ+dW}`tSNYfUh?)(oEPy!{{yM-j6gScBBhx)QEo!4G>de-<{V8AD+ zED}#IHVc1BDu=nP2@o)O+x;9gYHKNq$y!LMDp>_KgstXx(&a= zbRGz9#_bL-8L}&xbmmi_5}XqQmrSe?NLZZ#jlH&Y;El}aQ;jer%379{jfR$!XV&)& z_Eo?kBnkJILN&;(0XFzNGysR`DAUkvrQe^hmmUB}jJ$qZy<>kau1O&j^(xTdkr*Lr?8og_OC9Cl!%e|G+^zJ*;U;4*!Dm z7BUWlf;k$54p?UZlmkRn@fU9(L*fknIK|)JRw42QzbFO8utIq7C;8xUIg$F^G&Ukb zG3s_|YAV*hzc@&FI_T@m;a36CX-`V@twVfStcD{ldx;xytxiZ8K zgO*8jIX#9$eGz_(a+ojk3;bb4mEi2%w95L9!-pk=<4q zV!9IFBZhiA-fTXR5aaw}jsi0T_u&qsQ;#vq3J`^=_=v9)SN+p)vmYCszM~w;PODy}@0D|LG=qC%UN^H*Lu`S;4VjM$NT|Rp#_CE&ffd{fc$( zHpNgE{!S5f0b57LikliVkzR1>#^W`-NPWhnmvXZFn{#Lh z>w_iNbp00MDY%%A6B(}$+*t7}V89t67Zdip3ofmEJVbRQxh?mR-B^S~+sWP0fG(_LUv# zegZ$8fWtjKtAwrn=A3_JgHxA#m2v{Ywc+086D!p?6F$p$(~1j(=8m!2WBp$a^5a&v zR9_Xpfv`k;Tjv~?DsY%e!7dC<6*_McIU|4}kQefrbz6dljsaZBj-A z8%LNazWk{)btvNc&rp7P3}vr&?Y>bRZJ(^z%GHHWcV$j4e8G5uvJ^N%sCb3o^LEDM zEKciO$A`6jWf&yS>6rYl0S!K5ke(Jx4F1w}$3kGxQn)ErK{#*<2Qv(Z`)9uN&I{v7 zIk?vGHK|jf6b`EJ*Xc2WHgU6G^FFmL7)3Vmc5KG9U@ZoOQ1)kkrdi1!*XV+>@ zf!_w@GL0DUud6l)lGsge_QXmaqvWO**?Gy73%qyEk zSiM;g$4FpzeCCxX@h!+g1`-3|7za-J#h=wU@Q;oLEuF`L8wjc^j@0a<6|alYZXF>k z!R`aMVH&pLgrgHUrxiGX*)5Y8amP+&PGX#XSB-C}#5>0BkgT$#u3*)&Wh?yKE7!0H z6k960`N`F;O4ue9PG)7hJVlvQgGcTIV#Hp$091WvObHHX{W#iOU{?7M=>D zCW%vkR3$(v;^$D8bW~XYi!*_!}uD+pAb$WYFJFwOMoKOD^o^3Q^>A%xiVOwC@_)=0ieuW08*V)uEtXhM$?%M^R(lP=hAxT6aP`-m&`&f z>bAIF{3U(Q1c)nfF5h8HdJW%kc>Bz!vu=pi&QYg{l!7H&_j_9|hJgT!r*Q*%*U1%! z^cXV~I2~(kH^)d?i=0Wu_>J6%P*F^?7&Bk?+s8go7k?M^YQshQ-rzbp{@%Djcrsj-|u$s|x5VTzHBC{#&O+}FQ%gDH%(`3M)t z@@emVN>oPs&gaHzJDJQYQy>P9!eopZ4Qlw03b;9&R*R4UePwn$)l(}N+Y+aDleO*Q zp;M~nS`y?j{5NlkWE@$=RNhs>w=6cZkEP!BB^@|02Z6yhua1b4BvOH}g)aM52KEGc zM$%St8CnVX$?R8JXgC`*YjCjEr+3gr}`6E0T=wU^=TPip&iNXEcC@q_&^kKnS3AUJT7Zs6IjJu_w6 z3>teH&E308dkV7?;SsD18h44O0_>fv*qmdn9-<+S813v0blX2(2%+YM|2Qg(`vSTk z$r}7d-E#-*dFVWsn~GmQ!BFS5Qq{Wr>}YM+ZhA1-u>rE-`yAL4(^l;7a&e{bX4*=4 zKq~cVGQ=Fe79VIM$|_7iPL$*94kPJ)3jAIJIRyGln!7Un_BvGc zHIgeAhwvCMy$~U;PQaJ8G<6NQ$Ie%?eh;186tHVR^%Yw5j?!_B9D+bo{5WD(OI{Cj zB~m#o^}we)1l@&GR%k;l{snLo=7IasU9ooef*PL<`^&RK z`xOD}G#9eSRR0*RJ`>*P{Teo!q#v#LepNO!(V6ATU@JywJN=W4L9@ev&wUZRkxr@@ zAIVLxkqw55PihFrh$l_E!>D=7fmW9$Q)d6WoXAMlSOfO84j{oXX76k&R&tk9q)D95GAm_pIZ6Yn{c*22zR&o9yY zUcXY*wpFG~DHuIDXGQ!L=xA~`r{e*YdcziYZ>~Z0-ZWAC%6U>us{a@+;fuM$3Dg+A zn8U1Pz_y3RMzwM%v05J-4{!ooRq3;ET&lYfqtL8Z)5xM`jJ1qQ>|+F z0s0esqtdNfOgTyn5x;xAxIe^*+m$`-Us=?{b7b*$@D)nIT{IZx1px@gXis1=%yXu< zsy>0Qu0fAS0(Ia2RP@yBcv3pB*gmV01o)d4@*9@Dm;gZ`_6&Qc9+SJBrzU?_O z*|FKPlqKLV{QI_2fb}|q<`(?Yu!3n4TSkrZcp^RD=9(42s2{`m`|F_xtDwS9hzq*I zkh3o_JQ*b@)q_E-ts-T~m%l?RXghZZtXZ#gf#0s8-w>JupE@iLxP?(8XP6^ipxN{YW%+nF zQX^n68ZkPnVWz1nJwG{#p*)e%*|3hHz^R;mRa}neRHWksc=V`n(BXF=VD!$WlGoMv zc?82yBccU7uXr;VVJ%lm*(Gy#Awid*YJYq08Z}NtK_IBI&u(NqoMjCRf@%Cc=*PRgj90mJR1T8*2{O>tLZ90273 zyPtuzl6?lP399_65vu!(n+7Ak^?$g7CZh<~YDe=X?EO#5zLa41WdG(E?Y=x~H6pO6 zhKX-^Fwf*AqxYPuwBq+Ul!#>8KoVIQ&ql+;0R~_3EI?QSnX*dZLycmEGGoc``MFL4 z0U`l`_NLXwgE@I1UtSq8-A!Xj?90`>NIiyUhgd`Si5h9s5|pE0yZE50@@U-=L%nk3 zn@2k3$Oz}Z)|R5mAa@wbfO25GU7n~6WD(IqxK+_hyK9+`Wq(0=yGnpokl8%}$^woC zCveS@9vYB?$3APO7Hbp2@5V#z;_&9@en3l1EIWGhz5?t;lf7!hN*PmwR@6{5t|urS zNC$4vyinu1_d&_3PW%q+(5ha_l*K!Kxsn-g)`J+5f1(!x&xoFbMu0uE@68dBwJCJC z<;LouJ9N1QV3~hV9F*~|H8>qZ6EEvA3h!Y*jf;Em{Tm`wf^}%1kyy#`edd=9(DU;0 zt}i>S^}ChBm75iEb)PSI8*j3ZDRUt`$c>hulkYmsM231c0hj}}13`8H4Gtw_8}RJp$_0-b!EI^0mxxDSe#mL+x$6LOz=TST&6|P-$s$># z#i-M~#dj@b#T{EgUYp}u!V7WK6hIfwm)}Rzo%FKCrSllbiqKKg3|ijWN~M$8-*;{< z{5m#)ZX5Bc32`Wg{DBlDe)#{{NJkt>0K;d%u$8SiHx;jOD?!m7L^kuygX}Ncr%J>? zF>s)DVONw^<^+vPHGvId%6{LUt;Z{ehU3|n*YQgR>438`FpCN|@03)_n+ZD#=QQkE ziHRAjd5iR?z;wH9LDAd}TZZfbB{505KT4(D5u}k~pzlbD7&7IQHUNV_vXs&K4J$WZ zu#+t6GwG{uf^L&yc3dB%!ZOu*0Es(dU|ba!3=-1UCh3D@b4{XnNGC(-9-j^v{5$Pq zwO`^JWuX!`oztH3YIK%eEZ0Pb=ADMj8yxNjS)2&~i;1l)@D)H2px$J~w*3SBbj)ln zpa4mIoco!gd+(#|4blG7T)}ts>SwY5dibyn~m9Qi97&Y>hm-U#F z=y4lnC`@F`$N{8FwF12ge$kt3N8Kc2yC8r!P_NKWB2 zDl{4-CUxFntSk%=NM)5D8pLdxI*b&Dhj()~xhv0_3%veJbtb)LEzvPX+qWwx)>@+P z{iN2H^&h>O4XrGK>OIQyn6lM*?(at@L!sU>-#MKo>3mO*f1h^HkpT{-Ke9#=s;ok$_vIEze$^eZJ z*28p@FH@EFjPK(J`aNK}LVsu*9v#jv#)d98$W5fzf9WV~ukLFGmL{Ee;ynbSZlH&l}1B*+)Q-?0WL&&o7uh{W zf(za*W`{iS+^q+|=hiMJhF{oJ^rgk_TCEMuJI4EmB8s&VKk+T?nvEgj)lgpWi|*6& zShu*SShJCHSdP=NaF8u?y z!YoJb{t1mKzPuxZq3kpcodBl9JAmE>!IrauV3$m5N8~-~O)KGkr0= zX`Z44PqkH6EJ!*U@y2SQRaxX6AUir#M3DBZ*{LirU(e^vn!+RpaBFx0C$#-Kos|q) zLh^-?FJ@~RAO4Fx-brbA^QNF6Y!bT13(!;0|DQWEka3_r8ZOW9$=Q(MeM_@rk@8}9 zooz3NPl>Isgl@HS4;i?xcRRr3nz-V9viznpQ&H&inWFb;6rT@vE(gf#YwFt;qmSF4 zE+y0Uo5EZEnzW3PZ0eg_mvI3eLelal&|Ty!0K$P5J%b9;TJfKsHWa-#pGyOh31*Vf z!bkKe2dHwvOLK1p^w2WhHhHay&PJch*dw{z7xyE0j^R1xTl<=?T)~CS3gd*-PZZ_Yehq3osF7q^BS8?4OQ4^!I$pri(5t zEHpk;eLg$0=iF|6Ws^SUBR!S$`++puzP;%?LWa*Dy_L#-nVsmnifQhc9>wU^FWi>@?gqhwj5dZ?Km zdmok#=yvUTo^~N@i$jU-Bu1mGEPt^U?V^`*SZ>~(=TCN~a2ir)TO7SXHjBs;p^OXe z!8*wTI_V|l`Dg4X{5x$^qezGRaUf&QvlMj0atJm}VE_x-6wQm8BCKk_ntZF&>@oel7B_ksvgFdGT=IQb8S*>Y!Op5R>3fBtrJ`7S3X z-_)qF(u&bB5qJEGmzVcNp~bzx$v?eH@}}6EMhbWI^s^Tv<)t2>hpwqFIXT10BPX)E z6TWu5eV<+un|k(iuEkE6caC%C0w=QNf{;8PdT=%Wh8joX4M>(G-lvyA21W(OkMB-f z*z$5mUu$87$+-Uib#=6H_4;wmVb#A@b%cBN&-kA&+n+p6`8YPhwBnBc{jcs)I}QAi zGnpF{-wfZ0bxs^2ew^idGd2}a}&fBlk4}|z;8=07tXTOmPPBq)aS_@xPG958P z`;uZ?iYQW#K01#dJw>sbIkf^_!&J$+caQn`b5C|rQG=J4*Z<#_&zAF>{HEdU0Vk)c zMyposzg=g8#NbXnI!Mx1&87MBS+UebKsveuR}o| zGtyNKjGI4ARxLhcyf&(bhDZ*G2ds6fs()IRNTwC`wwWtI+dHXk7r3JfEMe!Yp*{l@ z>HNc9Kr;e>sTAB)3}(Y&JqFLCUAuU!bUX+=8ZdVHnk7yK_S#(@Fpp7I^j*$?BQOi2!A>O($!Q989h;N|f5AWtRRo4Nn*K2JVefb9g}rU21`UQ9I0GLR|w>ompHHqajkS&v7s%#xm_=Cc)M_BSd~bXS64KCvE@xV5I}Q@67?Hi}5l<}>d)YvPmYVFD9%5|-dnN6iOc`Ad3Kne93|UkTpPJavQ-GC$lZN?T z*y}u=0oI1#Pseo^a3E1pmTOg{hD=kZ0<2DpGis`KlKW-RKF*+tJ_otPQhi5xz{IvE zvBlMsFN-hR*jyrl&yoOJ?DIE6JZ4=hh=u^v+`T)Tdp~?UxTX>2DjnY2bIlz@!JPPV zV~NLsy=9S;oRdRAJgG&4Go}w#eN3AMNc}*1yCQ&4YfyiwO9rUPzAcc#0Bz`EXnfe+ zGw}64-{3cxJ(b}=)xcy)&Y%3jzPQ%rdM#QMjG1EJNZ2M9tdk-#U}2eR(YEK$DT zv+~>Oblj}Aslhw`$ZVPw4_10unNwmL@Pr<#;Cry)dseh_FwY*)jJ>~9pEkP`0 za-M!}*>mm4yH(`DLDsg9YjnuHf#vD!kkII<@4ii{Yxw$Gkvo<2YDXXAVNTW8O;`AD z?XrcA00~0JM`N8{^XxVGPZt^oHDuY6Ef$$@{(i_Enc(JT9QfhUG1Wu9zS$LnLuR3$ zJ+-pDi?{g<^){wV`ThKnm&u;^oNiA$=a9n0vBL#MFQWZ_e{{*Psvwi-=$dggstiB+ zV&%qG`kpCt#mRF&a<{punlnE4@2&f%%s#)=a#iD-lZnQpC5!W7BG=?SR1n<0XGLT+ z?G?PSx4Cn7)z-DPkLlK-R>fXsq}+e67W+S^Ru-R)JL3|c8ts#3C3@48R?KR04SVx0 z?7Y%=%c8+;=ke`TuU<8+bi1m$y)kIiJ3HsXzs38HSo`Lgq-GVz7IUaJZ0@1N`0+b{ zxTk8;M1!Dp({yiJWF(P5Gd--xiMOSBAG~-}Iczky@$Mm=1I616^M`a(JVaXIw7yb$ zWf99l)U-BXu9tf%iK-ICUoP@%3KkU-*{xxH=??ydnK|4;>#31PPg)2*F5R2OE@U+p zHTNIv@wgkWjO?yuUKv zZDR&J`a+gN`&qM&4J1K@;KI`&=Qo8{e9Ia?g>pKq-g)O%`PD77PWg7UcXhKMMy@y2 zd!#v$(k$eAzGbJF_*1VuxW`QQ?fbZQXV9S;7Yx{8+{0H2T3<}D{P3FzGksT00_ASa z+12$8`@Mae4|ZDj#*V3$@?5+)1$TQ>Q|T5uzM4>K*%e&k(7Vp{N@L{houc}>vStnM zoG$KdkxuwI8#_C7a_Y9PbFY%dk9J!%JNQ?$3h9k+{xrP*?RqP-e@9Pb$Hsb5`fnFEdN8UuKz8PyD>{ zHOR^Ha9cL4-Iub*?V*v;(8Ibx+ppVB{-wEiV8!26EbZLJoDV1~XZY6~T zWE=fq2i9!Cx)`cIu!Q4tia)LK#`Nb~n^jfQebhL>ruO6#be0demS>5&iuz>~o2ppO zwYK}e(uXX^MbXMd@Yh4ahA1|+h2y;?=QGbneQI=9|7%)R`mPVpGfD5tH2pl9{fa9F zxnzf4!3*ljS_=(}mbM7ClO~>6plsrBCq2D^TPm<{2x=Yd%^lzWtf=LPgZkvKst#}=G_og@Ht(&1=;IF71c|wKEdwEQIeW%-} z7nhe!jTE#Bd|tJb=D3lUxL*y*$u1LC`LJ3AciArC4=-4mE-@8c@w*p4+TS1LKQ0hi zzFU0Jx#13dxX!SuWLL;d8@nJEwG9P-aj&@6p3OIDC)PQc4Gs=&Y-a5VA!e*>*>I&* z@UG68*xhwZ$NkZXX`v6+Dwf|HOt8%M80A*hULEhc%!SL_elM(@)NY+3>`2hLIJ9g& zMfCnD#j3I0*1mC!f3d15Zd9jepPmgPFf%hV|Ezn8r|%fsWT;$i%%i#Q?)u$rb=$7K zVtQtT1q*KChnkw5$%BQq>c<)t(&pSt<&&KO|EY z^(n#AI_JX>N2RXsoDsV*B*QLgd%&#=g9{Uk-zge4lv}qh+pj#vTI(w4S)RD8*TRpd zWEPU}Z{qBxW4O_pO(tu5PK4BDx2AW6K9AOG2`2=e8NT%}r+ji`xtUCv{h9$;t>n>(v!OX)q57?* z?<)#OVdZzdO;~=Bx#Xe1j&3FR$Dwy5f6|{{(&VE{DhfCuW1q&Oyv_QH%JG8s@h4IA zT41bm+^KF>+T=6dd+qn`ef@#0W#V^V#2Ai_9#>SSS`PAG8@qkpC;FPvXlr9rad7%E zzdXZ=k-V!7_s(Ape||gXgZ+4A*6NOrdmT#xpB_pmrxi@Ke)*Cc(pS+R6}r8AXsFvg z)lJ2LzNSA?#7g!qRb|-37u|=sofj3>=2?3+{GY2~WAa0vuEcA#M=mA2o$b=+;$4!9 z{_4zWZU4`2ZZ~8KeEp8bYxlE4{rArbG3EX4Fxu|j<`o(Ha9{3tq=AB#UY&W|wq3X+roTBHS$B$kcK5N$h zA;-N{o#Gpv(v$vmJW4=fxe?B*yzR=E{b_{Zv!0t1XmF$1-`20z$I-9Wf43i-dp<97 zM^=Hhu3eK`t!;bYf`JVYx-iR znHAbNY^1jB-8Q+N-ZxtH4?7N!ZuDk!$3xzrw*1=Oq(SrS=g!et!Oz!K4d*fa@VQOP z@MZPBuE&k`ll$u1^gpiebYk~^^wsZul&#;(zq;3W;AOQO-{pu&b0(#s(I~^V$=cnn zF-W!HfK#32sRxlRQ+8G*bXPd}4!DG$Kr-_0r9cJ2`U$<9ok%KKXyO3G&Le;d|FIez zi|ZmiKA23Y#=gl~(I2)fn>os^YPd@tYH#=Jep?r>V)OQG&f(0wyyxlGsfP?Jzg-u- z4?bUXrP*a{Ox3&mvF*@@ZH8{y+Rhhlk$ZO6&h_?<;*Dpue${<<=JHC{s-0>!SNuzp zM#)_^j#D|AH4Y!%vRv}zrdT}EawmV$)OCDj=~-`Q9P#{(Th8H}{W8M!a*AaGXvMNx%5_vHA2oli=)ET|f-QQ|fYgU)oiDEH=_*)nBSxf~08 z)W~1g?Yx%N2}stQT_Qj0DWp%A-JyjYnxx3N-tM$yNAjb4<4N+Ipig5yWYJ)LRqp*e znyShBhrZ2N@55JjDQR!D(7`tuMCY^HL?zL7wZ}xqR_GfV`ZIa97fhP#C~q3KG+!w9 zS{FZGy*D+l&FM~WsiK$fM+Q-IFtj4GQafNwdVNx4%7o6SXP^x z>F`HUOB9PmZ}0#7Sg*fuTv%SxIW?&#MZ{w37`AlGbBM_y!e5H>feMo zCmTL-N8;B-?8V%XZ=D%cJT`tLIjz>KW6-AOY<{KTvq8&`)ev3drB%_{7QOv9nb87Q!^qr0*ZHikJBYuJuJKLeE*`U3>ECAFLvHRT!zoGHgYqsvFpECAI-Tgx=srIHhSY6 ztXob0MIAwhAl)ao7Vp3PdUrdonsu08;r1{}H+<*Q&}FB-woR{e9<69?H>_bVdwuV~ zH|HwOw+7PQoY8F>w9L-!--L95plbe33s22r-<}41e?(u%oZ7@CURvQ_9_GZ=FS+9D zXIQo8+lGbNRU;1yjkxPW;@KH~E{FUl-_HL$_wCzu@>}>NO;~U|fa$Fg+VN0Xj&DM}6-MdOwzEw2 zbm6xocK6@Dy{b4=c*g%qmR{1BS=jbdPc1KMM-RQ=_oSF)G-}mki7xAE1zp`1y<;@+ zb1gfBc2-8<>hb+H7p`bJH0j_k*z>$C^TATUIniBg+Ir8{2=-pdiuB8GW;veUHWG53 zH&aj{G>dK-4GdvfRUCLsf5|m%D52uF37)mk)zdp^{j89#+{C6kd|r3I!$RC3>+Qvb z8rjXe;r;~99(Ui|A7*)<<=q}_uxMzw)a>3z`h1aJ@_M;P-vh%)t#6}m>=IMIgtX1h zH{_c7GK`vfpL|K_e=WM&|235T!iAlm+!|EMv#sEaEhpzpCF z+=x{xD=eZ@8;s6%Co?O*MYHaOQH;jz8^{}KchlM2zQSiuaxJHnxqQ}6|9Znxw}QHY zIeY(~FJ4p$M64|K8@$=;`gy_#E!OAl?ECjhU>oqpx36HoBLvORR4H8c62n+I8?3Cq%AV2&70g)!=fBl zEjB1>ds5g@N!z9_8WZ)qR)r0vtTFz^tnK~l{EnilVa+eIb8`r;sboR^4)>JqhWU06 zW@p^LUtQR{uX!;4l|}857ncrIy}05|QH`iRyISZ`+}~8x?&)q;SkvuS(d^ULP^TTv zFm8=hZuX_%`-at>_xHB@mwa7f7noz)&~@%1rpse_xc7Ij_x=<<#nW zOKJT7@$~J{O#grU-$Z?+Qn?kO8@Ys1E@3FC+;f@xs3doC8L?(bl7vbq!sNcOxy@xW zD}{2ukJ%-;jWKt_jNiN8Iltc@{@CFh+upC|?Qwa&ex_Q6i(Gi7%NB7th7^|4?8LhV z$4SHeVPl>g(7Y@fGp+y9VCkI^Eo!k%8u3E+;Gu_C1(@3vZU6;p4ur+{wlfRkMOdhe z&mJBR1A5hck#$sutQ}=cV|zFwRE?u8X{O7rLPL6|Zo(GU^Hetj5>gCgJRRI)NT|vHIGb zD07IJTK$47Pl-j&*6=BKw$>|$7^ZI0|z^9KULnx~kZtpw6iEx$F|T zH7TQb=NTcj5RUXhyiT44IDThl`*U$_?M9pQVz)_+?l&s~-Xr0S#YK!FXl8yi)PLhkLpT}4saNW)b@87~ zj%lTANrct*=$d zli-s1Xa*7y+GfRhyiaOstNPFXz}dEbf?NsEmbiIj+N@8vy60dcAl@!_~S zdi0p{0MG<2(0lNZs8aa$sF$i?Q$8a*aL3zyEhlXA{hhJUJ$Uqc7=iA$D8#2bHxgbu zJME$xVlY6O`pTV7wK51O4%4PV9Q?3#yDwNp9dQz4#ko@v-RmI6Gj2I8cqSV&ktk!- z=FW1+eb!zYPKHFdM80B+_Wh4HzAu(~vVZ-V35UIrD;*Ky;l`1p<%eN2HQ`aM@JL>g z*8G>%?&_=EIvp~n8aMgBM=;<$wAl5gnOHK}4M8Ky&T9LS*1;ar@9)S$Q`J|NX%URk z>PP6&toHME%9No@%A zNLhaU{yYteSuS53*c{%(Fa1rV?Rs({mwle=M>q2m*sr9h>%|jD_?X`!yvzdtC7||b zs}2t)=Q%g_n+$A6kTYX0qCHpz5s}6j0OV6V$!&31)8Saj{mmR@U)TTAQaCx-y44M{ z(aNl%SdPO1HOWS+>=AE{8#o|C#@2^6=766;Z5IA64;@?oWeOlhAG3*G?E=evMqh02qIB#9}cK!-Tr&wW7^d0%-m>Rdp8y2DIMXosiQ0F=o8`o zp5BwrFPqy)yRVgF?MX|<)c9GJS1rnGI)gn^LC~OWJ?O}lDYuzXSm8V!?MJQT)ZL5w z9i$HqB16lZD}toMO-DT!lxQpevq&0EiFKYPtumrmv_cA~32|5;LNXq%iV3HQuH;?W zr6Z)~(?i!Ch1y;=g$FszwsuSgTL7LTSdr^eVWq(&#+enev@=m*)}9^Tk%F%BVq>BE zpz?L@b_=Z^@Tc`6Ie$4{XJpHuZ7$j8*bP;b>1b>P={FnS*s7}E*xyY(4-fc4jA)Mv zFR9(rcl6Up0~Go2fa;`^nfgUh(n85URHMEq!5$c9XO-MD*Gb_8p(+zo?mE?-S6+Ba zvkk8zVKyupxN)fd#rgtD)8^&fXQ(b-yxPzhG#f=789o`|@t|$3M|d8>LXEj3=IpIZ zDA7XKwQ=pOi2gr1HjP`4h>+lCg@jh4D;x%9%sF`ioU|=+6t;V4W9uMlvIo8^HJ2H%cJK(>ZL*t0 zDhfAkU|nZAdE^oq?;@V>j>3+xuec-1=&C{P4ZkL!ps)Qh3oa~q_mXuDZwkoNSI5<2?mCahg zA6?T4x{jq}3oXb+M4#15DP*(*3-DGS_)wpJ-yn;W#Y5^{Rt9s2tfX9?* zr_{Tiw{~TLGD;dYj~B86rDD1}GWdpOauDBT=J(#g*ZP)5dTm(w%6fwbw+;>2&F+o? zI3dYpJVgj)w|;}CQb(8Mjx)bp4+U`hKqv#f*G8Wdaony`8Ep4kwusJlD0?; ziiH;A3sZa6Xw`tX9u#=QP4?0pTE;3%UQMCiH*+rr-+V&g5i6h~4+dF8EPX$hQan*s z`%|iWuNU^`+YQypewM?ZB>*2~y2U2rP^wy#N4A7|KP`#3x3@0elC9z}Bg+Gb3*j4! zxM8;^{to}3WloxXC6vtgCJ?@jbpHNgxK8zgq3p_}ZS8<6fpQ43o2V;;E?*9c&IhfR6UDs2qI>R)>K1z!7jhuYHO)o-kT8}o zetQg>0t)EDk%O@g6L(Yc!z*A#B7hml1f$MebCO&1J9GecAz%uD zVdspc1PAD}ZIABpc(U`&iG;_H8U5~yxzWCDwLw7$;A>JH0smJLS~Sz^*tgXmJ$IT- zWb6vbw;VpTQlQJmhtoha!pilPW!xmT^r3`nk>^AN5Qn&;YfO!uT`Ofu(B9IWI$6Ag z!nZLvmbg$m#(hNFQ?xC{zjLKs8eSp~y$Ifj&~npa`1<)FsDhdgnqQXf+y2KFacBq< z!NK?D^JMhKos_!!^FhQUC}B;VJbT_3fZ)Riy6WmZ>bj#?YT{)Ux%xEKh3Kspt2-OI zi}fr^atZ#e_i1mJMq2akuVDs>?ure9;0?wr3}?`K6OMc?M^N2K)aVjH@3 zRFQ-0{my>Bm6wLD_v6qE3kDUh8I z@Cbh>X3cG>F3lnM!Kx}@F;F%|QcTy$=+1qnOVH@}9q9n6Aj+_`8Ymtkg$Nqj8o1K1 zo#`h|NO|d_M|HQK#+NH-ldtzSG2xb6te;KH0-d|yl>*w`e8?3!(yMX(PHKwi+MJih zG!Iv@THSp>iV~TvR-tw-71J1h@Nk&O{jesD2bWDneAg&zu-AOE@iNgP;UhjX0&Vx& z47ueKSTf=l06&ccPpCIVLoc4Z#yrm1EvXoR6yp(POLsV6ZW-(yACp;-d*RyF1a(#I z3Hcq}rH0}4B{IFdYI`oQ6VKc2aF_6dVYqK|T(AzDw@aJDPz-l)cr3uHt=4Z5!${|6 zW}bA~_!G;{DZf%uiJVv(>s>@h{+Mxl67!FcP2@Gw)z)<5qh&cFhbNfP#oS6vkj2R6 z(vJuBCB^JgDC-j|mU}haY_z0Kas^YH1yYtCp~F0r)=7DkIHblRY5c7cXQbHQ zVVu&ed~fmDeH{hu$N3?;!RQdeav92)FO2q8w3cxH31^Ez2yT$ZJ!p_70zY+5I`fNB zPupc!4u0TM&RvNz#chgU)kpV!h0X(n7eH*V8`eLeo(+){f7zCZL0gu=;Xk)m=USfOqk`2IM=igZp(N*3IfhtvPaAFO}=WNgi^cXdv~yK813yrd#@!}K62waMEl zAz$f4kRF=SO}pq>?CCarqllHigID5OJutGZM6-x)XykZ$kgB#A{(zm9{&#!EvTa4}K%ecl~)7H_PnNJNFcZR-dKr)j3 zYTP93#u{M7)CO`7-sAe-dV}B@U2y&XoXYoWdeg4Co>&Yku~@EU@%Pu5zx z3mw|YX|kf3s~==i5MU%itF+IeR@;LU=h5lzAjy^7FYKC;bMM$pmB+ONIbPJYv*+jf z^l^a*s2l8yjZgly_v2+sTEQ*SiAfUrjMBZ*Clh~~(0;CQA@YX|7V%}R7I%Z&@_>(` zp$}EHqN~!JZcc>m6dy4@(kpgBrirarM;Zqv6x$ga@{rseFdnJx+xhwcE7TfC2w&Ug zZLNCT@US9>aXge4Mi0)3+YuyeQH`UZx*W3^_ZthLp145&IXz#48h1%nae(`Hvn#an z-a0z~($8gcT-uzmqmYBJ;)|j_RVJrttZS9X)f=nsu(7R)@ccLrL++CeH!;mW!_E## z*}rSBS@XLx8sYS&S-*CWJyUy*s&I|CS@Ds0&3kzQnA@S)=*WB7Adx}JtZq?HA?fH2 zYvAQ)0D1QBmq@DIw|$=IN9|$i=dp}EbI2``$7~XwVj0>-eE5B2sQ;Yv z!&@BWzPiDYvPE^-_}s@u7ky{xUs;A+G+f(v*Tbj(qkI$u`YX@{b1z>7Nw-Wc}BnK*e$+gs@PN6wj{vtmHnP&jW`T3T369(A>%I!?rVQIU^v`rt-3I7n zp7H%#7Vyv3g9CO8{8NLDe?C(vie$xt1u78ko?@Ku+=`SB@K5M(1Qd!Q(1d!EeBk(x zSHcnFU-XgSKLx$p0I5@TBlaLc7=fiT-PoQY!pGOldYY8bn@IYZ4{&po)eMc4#p^U>+y+AKb3nXzJ?L;kZiNWBmw( ze~O;ys&iZ%@g?cY?F@B3I5C6eGwi8k7U_--|sOF1srmv6H|)>3dkg zj;!yir@U!>zxc^jOgr`&T~BM@4XkZj1eq3I*MWB*cU%1NkB`X{9v3sc-}B=C-w}Yt z(W8co7o}CdD7};~^v)G)Mul*`866F7+o~6_3K{B)Qe8k;C>T1lR5uHN;kSE;B`aGy z-4OpI+;9lDN+&z@thl)|2G zfN!KVzmJZfEntS4BB(wKR@!~i2muq_g{my{&%-eG)*e1I__rqwqCvX(7*vE=29y_2 zY$!A9ck7$CC$}o4ybc`^b5rylY9LR{uIq318U?=ScHgc1P~J$HV>c`k44!~dz`;Qa zy_A*F{sqTwcy>XDt4WxeDne}-q!sM6(U6}TfLo|#Hx(`yvEit`k+P02l3>e*2dhfI{I8|(#;5q z(|vGizU!N&?&p48#yBH!O zXjfX!DX-wr5e!H^-VLfYI4KS(b`HFWb)T_HJsZa8-PGS2w1pHxE3@gd=32L*ASLAB zCkL{?!VlgmLl-yshAy#AFHLw5g&5MdzWAStmHPeO^WlEjG3q)13cx0=wAF^D^;w5b zcSAkQ82QImK5eu$RAOM2q;Sz!Tob3dwK*{YV{RmG8EN7Y{V;I@J^y_dllqoDfR_(L z*gB%&d-q2wjGOzOJjbjeP57hA3G=mt_4=Y>^zC7NNbnGtt-m+3b)$?8T5a!;7`sRl zfl3Ee^tG}Z@66_q=19X&Je=E18L*!KDwCu|d$|ALk?aeJafZ^BP_6t+Dy*( zz`}=!HX;^bdr!zcf4+xvK%CL<@zPEASEzy)bNzEuWCG%Pf2z`oHA)Rr`**8<9A)QV zIjRsByaBT@?xJQipfHCLC;Qj<3&i&3TZc8sZeGGiPI~W`J>^e!iy$sEkoDOzdEv~= z8c_-V87U;g!G0P8LtF|&^nuJK#bn@~iCIy$5|f5Ncr**)5O&vO?F0)SAYt zM}2XSEyvB>#Rb6O)k)H$Ow<`M_UQ(<21o^>osBP}9H2UT2-EC5IO})gOAfuE1lWu# zJtJ*;_aT@1^fkg=W|UaLA%)pZp>aYEV)T;swc75XVeSiG2dJKB^H&ngVzcaf6y%-T zMYou^7b#=94r4#G$Ri>=-*V2@(C~x!k7H^AW+-{+ja&PiehLp{l`#s3W@ji0(vqcB zQTG-lVa!%Z0s)2_i-9@QWzlf07X)moO?0 z^dtbc&UT&Rdlu)f6R5K*DZCvakU+tow(-G&+$6Z$VN}u6vtN|8Sx`3|=&4cCk$EtH z1R2K{QA6ik=f3zF_!*^Mzm<`xR3~=MM{kZRc0!qYiS?OYtgBWY4(>g416qbZ{jo|y z2Gem8Nq8RCc8HMghZACmQhZ%OPvP6^v&$O0IK|1R)l&ROujzwIzMAt|3K8EUKFUj! zNZO~~vD=42dBr)Cp(P<^m)A&@tq`+Ael-49<^3Y^uSoQYk>F!sGe!u8Z}X#2%e4a< zqvP+#d>1O4aBRGVf&2E!(wlA%e6EzltyjljVw4E%FlfeIhHMQ4Zc|93%fTT>=D;2z z1}9Gg!fW*9NvoHj9Roq49i%C zoFkBfQ7XIIz8_j#tcDukuZ4P{?t{2;ST#$j!lgZ12x1#W!^=aXp_jsl3iT=U`ROi` zgG~H^u*2#C%sT<^j^dzYfjSpK*G)y9gvs`<0*^ePc<}@MB5(slT8r#KoCO(lE2S?m zbq!=Dttsp`q8-5V3z`hS(w|JGt_Erh8i~~HCtcEQ1&wSQ35KV)J}?9 zc?8ypQ+A%L7QwLx<+6o@8FmxNG4vlAf!);nW{Xc}ZUMp}!1UQxj$mu$dObeWTdFVo zuo_tEBXqd?-)a#*nwzf}bNgKgwSIZ_5P`&_;U>H6-#fid_R8^5-d)GC_tSy89q5i& z$N7y>2C0+7Blh3ErlU}|2I6OZlTHV)Q2&EG_^khYs41|vC0+I9*4jW%84BMH5NS^6 zA)K=(>jzw*^^oY}pU+-z_nWCT{BJ5z?Js&r)J>WH>Bi^`HGX6J`6kn2tYOY#Axr;{ z1?81@^WoGzbGx*DVA2;klsJI>x|_T z=RPK&d~ts`qONnBN{xv*us(j_I6b`lV87eY@SzrJ6cY*!y!kS^+e+si5*7S)`1fbN zup|?3BfDl*73e0sZQh$Uc<2VXH6+RK;?! zm+vFtK-U_0m<99d+NN*QpcyQiJeH~0mamX>(?wKhTx*8T{*rb_5wj*<_{s|e+3%EA zbYs@ruNX<4!0c|ltKnY&sGIFK`&<4FWwnQN|LWsz*NOu*8-TKw@#Mxwz6BD`Lyk4J z*xjNOB8CL6)~R%mE9dhEf7`CaPGE+)lH5b8>i~;Z)$x!6*pTtEo9#XI|04n_X4faB zEPr%BZKy}U&ZZzchnZ_8q8-Lltk@$luz&da>p5l?w{4gum1Qt)2exsJJtJ96DYD24 zv^spM>OS8~`I=`-(GRx zmS)(qn)#TGnKoHONI?JkHgvb9=T4p9Oma0olp4z58JFjzO zVFSHy7AVKap&{xmMX)3KYFma!Tf=H$FqT`IOknF`Qrp|nBM0AO#PN50zp$Vz_)QN( zGGLg}FV=A|*jb7N=|6hy7mwvQ;4D+n&C$z+88n_{N}-?;P~CH3i%Vt?yqmKz*Htc7 z_Q?l8?xBxGu1qRc?!#|5oo!4HirkuVphj=P`=5dp$~_-k%e%4AHiQA0irB9;75beI z{QISOQ)AezEv{Yi;#~XcuuD+G@&x{4&7{MEthz=?Jk+Ju0>_a-<)eg zAH9KZ+w9iI-{CBfdx*EC9e8f5fa0UxG+e7GE0ekm=0@@K^STA_#lHP-*%~GQgHDGr zHb6^;+*QWz0~os+>*8#jYO$sVlw6)w_Bl(vDN!xO8zQnArHa2VNaEOJ{yKoB8>RVa zMZ$=3dTX+O#T8kN zkxgqM^tR}`((DTTdWWSv`#RLkp(v~`pp$HSED@3c$vFB}>~u+H(EMv-msIo%+ZzQk zGw(Dy7W(s*7lyt=HF>PtIqy;ZnueKPNVcfe&P zV`xI{bk7hW^y$y2Db?4ol^kb@m69qOZ0Ouic?SsA1KVr0?~U`im!ruU?4dA(CA4#! zanB>Sap0XBXDdryYQr>kolrm#aMUq4^lY6-&URa9J{1>8!3N7})%UJ8@&;mXO?M`) zm~sK?`jGC^q_A)6YsA?NDoSa{&rWUusG(y+7gY{Vg7fsw+rZJspi2Q7>_&whYXxvP zBuPq_5)n}*e~mKMgE|uJsq@^=h0S-q{Hjbh7Z}B62BMYqPCp4vf^P>JMFspVwFrEs zXMmu3_s8+Uq%)Y0g!(q^Jd0az>bSb<8AhanYEheeqq@SE72Ew0p^FE!cv@6~-w4v} z2a15Xj>Y(OM;Sst8q^L8n%Ha#W;HplSfnXCla{H&vz6-TYeI_! zL@K5DRv|maRlNA8U|1GnW8mfq+#>~bA58!38@ZOr&9iCZ@gIKch^qv8!=ox;e=}{g zc2Z3f_Y=eEgT5~JklK~OBXzwfof4JjcPyYuSvvAQiPB-YtX2>`V_FA}e`U*8ChZ@C{ zdO76i8pEPqTm$ZB(&i+W>$i4Qpe~I=)WCwDb@1)eR@w0n z^B2ksHEVrSzIr8K8d@E|LLYx)xHpGX>4;Lji!qq`p|6L#Qr#(EzfC8kWrrJ11? zPU`BrKK8&qwd*)UJODV8&J4&fNB;|}>J@o}B;T2ibd?Pw83rZGQRNh09X^$=G`_`R zIKINYbkpz4nEVjfzMU!F*WvVcsjp2YbdNVtZtklzG3ee-6Jc&Ybhjy+X=io z$?E)sA^fM9mBH?4Px)eS5i#foKfcwh+4|I{+1DaW=U>5dr@Sb;7fN)Rc5hEc7g^=9 zyLI>Y2(tZxVJ(^bKLI0CLxSTPle{RRoT%!fWPSFm$RqZb>%(RvsWm7|YYDv=Oq_VT zVaENVaN4IUwf789%p>YXhucrZ)2dGx&&{axVPvSgZVhkttDLs3oi)yIeaQJ!By`#4 zZSLj1zwon8Eq`c&JsD(x6tIvRave|I+wQ4(1DRJ2?eagxmmjdI_e$vLhnA#qHHK$Iibl}+QI$u!O(Rmc@&!J zKi`LrnA}2K+3cWvSUVAsS-rCL#dh&Ym-L4|_<5z&|5{#j&pME{+|ko{4Hpj4GEh6n zV4JA}CVkKTLPEQ29U!psgkFo%%DB@X+VDAf?)ye zn4>OtCxgjE4&|zeP_azZ=KC)Kc)D5WT>bLIc?N4kmt7Ng1f{>+4KOrox+Y7S0b?W0 zk|qmxWMKXx%kz0H2QzB$)LVq<^3iIqHvK)|sX;{G@~^XkhR8bwww9H?ClAVof0EMI z!+%H?e>7YDM*OSEJH*yhhLcbj*u1xLp_;MFUf24XYs))$v}PuOZbsJe?c8CmZk1T) zmqX&nk~b57M{Ufdt@GV6_;5kVvs~>vR;RId`wh9+FavonckPX#yNf~lbo!GI^&czV ziyj7Tj1JkiJ99x^oSU3z!iwGEpCfl0OazT)lk!F4on^JY`|{wfgspB7t&G@J-F+wZZmlht_{$}X9otzfP;CIn?kg|%I-B%NJ^^>|MER`w)Ln3o; zDyPW|Ff=%ajywP(0-&Yyh)mDdwjIdlr zD(dTTF#?=^SB3JR;f0;&v4p2xR;b3xr-7TUml07ooJ23n?(75R9R}mz zjhN_NcMaK(BRAh(wgcS~x^hj89%h3oAR44s0kH@3tW(RWp4Ija9B#~QA69!3k?ry@ z)S*HkA(`_wTWEfFkQECe8%LTXsXVvw6eeQZC+>hU)Ww*Yi#1PJi86f*9CH&eG<32f zTPl_K6%KsGq&n*`brfV4iO3Q5Dv&GL1hpt|y%5I)oLxWr^C65K2Yj_jo?TTK(fZ zbHVs;({pmh^!5v9A?X@qo+=;Yq48)e@NaGU=zwzb>D; z?z?t-`Ri($r~_3wRI^OBA~#XZ&-26@0F+m82Prc0kzt@A z+r|=Y9aX68I(Kq_V&1JCquO`&yXZyKh5JKUTk$TuLG?zjp3j8efA_DG!Jna4{g2NX zsUK%L|1YG!wAfg6#TqJvi=bC9G$r~o%J4TD|9UFTSg~Y zPc}}e#ydEnJsbVyDW)fi82`}B=GuE^ecz(I)fPqEk+*cw09u?A{Hja9+S_uNwMf|8 zm(^CZ?JTh=fS>*j*ds58TN~F2^ewMu1c`dobX-jFoyoJEiuPn%WyD*kfz zYOF(x@#@plnOcCsO~d6d(rdP3!Tq->(l2my5gAJpj&4jNY^A&Sk2{Fk@5|#UZ@e8m zjciw+RsY4^Snx}PT#L?>z*X3c&O?ny6Rq4F9)2|jd4Uf!>ht5tGtPytggh>!|HL-^ zF$g7pG!zs`*j4Y1g@KDqx7fB!83Ias=EQ)K%cUC^Dv|1VB{SJaok+VqXDcM4FJIm6 zK1vi$>K2oi-doS$<4oe@Dyux_m<4IaGVc39@4nAk0ryYkDVQk4;xops-@R8F1pD~* z_bsH^0p-UZd6At!I0+tHfS8EcjT^J?&olT~$Y7@MosKlG4)!zCnW1 z=c#Bx&7Fxil&Uf3&MV^`M{*@@((`M6IbokB^kpn62Iq!bw-mjTVRB0t;dxRU)`e#F z{`m9o3!gP>+yCD1C^FqYr}hPHm~lE)bg$8DbI-s#`1|H}lhiQDc>8$qQ7(5etJ3{~ z#V@CX)zmk%wcx&BS8aLh}3%| z+0PA&%=P43Y(=&HCc`O+|puHv()|HzZafBo@ zG^CNPQATEFmaH4aiLdZ)6dN12{~gjpHrQZoj zO+JW_;-KXCdi;d4j<G3ZB@{@ByCRZec`qH zYHQOMdI6g_aFN8&okmQR@=?JHCI>x#ls49zHbmJt^Gf9QeJoY{eq6p+?%~JsA{mPH zl;yh2+)Sxj0S+sSN7Ne&g~3l@)X_xlPc_kZmq*!FV_CQE9jI}iHt8-Yz%{+JKeea5 zR#+R=X__-vuUThEetLJs?A#rhd~{|1X#26j%@R=pf&}Dg&Do22MV0S12Z_ilM*`={ zQ8^sK!ZMjs*)f%guBlMx?*Z-YyxojDiRuAVo^L)lgRh5JP8G(X;DWF#^`8e~0 zL|blHm6UF{YnffcOcU4qP07dVc6^X(42Gk}*%3PDUOXoYOm&{N$8-D{#x zg9O8HYCw`o(h4dKtDRQiahqEbRcs-_8yO^C*JQPp_=$iNgRPGVw^W?1A6Kh0LXAI| zw*m6*JB~ZI^Ee3NKvr%2%}^wNJ!1K;JRfiP2p@!FiKo+*K;?)1RgPIZ!3|RMck8>z zlpOeOBsb^*=ToZ($uvaU0@GFX@rMH@&e?vG3Jz7=`UUMT>5$ z5*8jeJC*;>PVwf%NpTMSTq=k|AIm{-=n;tF+$%eLJf=qqT8wE;jh@NIiJjG-@4LEz zlZ-VG(Ap`` z^Nbm?q{k1N{6SPhK3J1t>T9lD%3m}2Vlpf0kEP^s9`*gB_j-1(^3?lq!V6|+%jX{} zM>gJx@!WW|9@Aro^vg|gOPe!R>04o6oeCVPl1%OVzIs>QkE%6*qA7FI0~OA?Gq)RM z)J*yx%s9P25u#%i&~B4qn10_{ocJ<+d-`#fal?DVJr~mh=$>be2p_@QE$OW{7QL!2 z{jC{{byTCxLKV5nT!BSRNA!Z2^TLWm{mdXf!8P@P%eMByR* z$23Kt9<9s^g7Z#&(wRx?K>5L>>vXiiRo5M20$1uiE4kAwriX*m6NoG=R1#)#M;NTl zq&HEcizlq-yK=1q+GO5ZB`s3GL&F{ruC?*Lv|Uba@z1~WCMH_!L{qKtj->$VA5*Q! zr-f+&!mWltCJH#{eEshK+Y4~~g1yFVoj}~p*Eid=qYV42dI0DwsC=trg9Gj^cXAVn z?A*1Gt4b8|Z3d3gx%n*Ay<@M~Fq~YkEAsmER@RRh$L!ozlhJ-1Hx*QZ2=hb_esS>C zgM4nIotUDM3YwveEk}t))8VS%DI*g3nUwuskjM*AFO+xtm7T*sGcOPW8g_-TGT8Jn4LkWJl;%q? z{nsrdOOo;Q8A{UZv@iH8x7+0sGW=q2$;$5mt&ast?We74e(Qa(L@IaNPK`ma5~D=N zitrz&rJnB26{C!)QO>^47WD`t;unDAYPj0kVVI%E(L{*N-`{B94CMZ2F392MBQ}}U z-$@JVDR>DBJ;vPdT>90Ao2pNn61%jOW%WHg9z9bLc%ME48q`c>5kK;HRTbSNVa zxPZ*&Zq>~~5h%V%h0+7*}X?6ZoK%mSGb49 z5~p{AG9dF20q)PP$PJl zN5Pp|^@_LCriyHqPF!kFFH1CZ2%-VI(mlJBRl~4h(O9$FAB+P;!*(RxLww2%$yO$* z3KwG8O&q#&3`U)vWsCH4{mYM6cao!pEw%}Ut=o2ANWWlsCrM@kAHMMT#75=CoQhBN zEH;c<7UuSwGSL^p$GeJ5{ zeWmtDVQEbO8B*;-ZTA07)Em&ihbGUG&(1SD^f+@)4P$!RfI4?kJP3t37M~o{_qRp9>#u>z!AJ#f_XgdWcIeIx-9qtK|_7mGhr% z&wAOPy&%g60gX-YNYOKe7DJIv=aD}-Y|m0Ys#aCi_24cBCA;1hl;(o zF5FoCHPs?-ZBzJ(rP>Ql=M7)A1!32n1;2O&C2Pk^nUy`Os=87xx_LHkI2NR8W^gni z=d(y^;GctH7_%&OW%Sr`o;qicWJWd05O;C%N`us`cBUHU>L@uQ(FGM3d(#A@)+^5d zYfa0{O;+~Nlw9k=J?*<8a@%tDvc#&9WV01LbEMhx(w=HeVgah)Nrl!^U_LwHOiXuB z5mXDn>=pGdM~W~B5{@Y>xzPL+$loCm=7*%{m|fZ%Uv&L_xeAU7@VUW{$cpp&)v$S# z9aX^v;t}EA@gWIQS4Au5>@<~r*nN1r~z9vIRw!<~1UBDBj)hU_jqY&!nV=&Dd<&UDCRLplyw5TBg4X1zyU9Ws1a-(R|D`{g~MX&FxG9=bjVEag$nA1*6 zQn|S27s8!6*Y!>lhxdj(Nk=(pL#-5}jPyrxy~GAcWnTMPj=P=z6){bD%HU(eMBi(< z^r)yxDN$QEkl+57=`P5rm|qP$#r$uL{}glmeUa?&o}NPA5B1(GKfwqq9TUB~ZfK6b z&VvssQ(|KBohjEhw7Cyy%Cg(V(&05~Wocr{mENF;x>4~M`v3SKa=CR|KV&U*=0I2T zrdl#Sjl4VQ_XeoiGsMk-o_dQ@l_!$-xP$=n4UHPZjGA6;W)xAPKNe8K+>JiddsTvo zszR=`v}Hf}@Y^tm{(-vu_c1G=t~%0;tYL}tJD#j=JCJsP-p`M}NUp(#aXx7kW!hel z2iA?=S$JA{^H!F`m=l(n%Uf}f{{m!(z%j;NaB5Tc=R<2jO`pB^AnN!jV~MsyE_o9! zRbi!qVO%e>#tLE9tWs6*IYh6c9K7;PFVj*aV|icvD~=nI6{X(i%Qd&)9trTVKF7J8wI~(J zuiCO`;M7)z0INZYAZPyO2^hLeo#_RCfJ_u@U7-=N6C5_8CH)b%Jq>*>#UvNL7HQ*A z-fpt>XzK4`hmcta4H_8yU5nqUvhO$@B%(AtMe`XW?VrT{r zvXaMFj1pZJlc7L_CgZzjfc3Cm&(wOwTuyXHkB$`E#_=KotlKaQlcU|AsY`qitdn+i zJ1Eqsk}&)89Qqlu;TIwo7MeM s*Y*!zSG%3&zHDZJBWa0s2TR-B*Q&^h$-y97| zE0ACJsLGHUgo_P|!T1$S*v6&R`@{0M-_hcEpL+G)12&j8Tk0oo%T2OO=~gW)ex9-d z1j4s;NHfSh2Nu}R2&Aa><$s9d9j(bTE4nB-dRvfl`f4vIhVB0Qz3P65oLDAMOHC}{ z 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

tRQ%-`J;VIPC(5B%FCiDC&UM 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 literal 0 HcmV?d00001 diff --git a/packages/console/src/assets/docs/guides/m2m-general/assets/assign-m2m-roles-page.png b/packages/console/src/assets/docs/guides/m2m-general/assets/assign-m2m-roles-page.png new file mode 100644 index 0000000000000000000000000000000000000000..15ed9e768e5b7f4983904d1afab07353860d9864 GIT binary patch literal 50116 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.eY`zY* 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 literal 0 HcmV?d00001 diff --git a/packages/console/src/assets/docs/guides/m2m-general/index.ts b/packages/console/src/assets/docs/guides/m2m-general/index.ts index 00c1a10c629..4ad62a3e5b1 100644 --- a/packages/console/src/assets/docs/guides/m2m-general/index.ts +++ b/packages/console/src/assets/docs/guides/m2m-general/index.ts @@ -7,10 +7,7 @@ const metadata: Readonly = Object.freeze({ description: 'Enables direct communication between machines.', target: ApplicationType.MachineToMachine, isFeatured: true, - fullGuide: { - title: 'Full machine-to-machine integration tutorial', - url: 'https://docs.logto.io/quick-starts/m2m', - }, + fullGuide: 'm2m', }); export default metadata; diff --git a/packages/console/src/assets/docs/guides/native-android/README.mdx b/packages/console/src/assets/docs/guides/native-android/README.mdx index b6a966f5613..80020ff5ea7 100644 --- a/packages/console/src/assets/docs/guides/native-android/README.mdx +++ b/packages/console/src/assets/docs/guides/native-android/README.mdx @@ -2,6 +2,8 @@ import UriInputField from '@/mdx-components/UriInputField'; import InlineNotification from '@/ds-components/InlineNotification'; import Steps from '@/mdx-components/Steps'; import Step from '@/mdx-components/Step'; +import RedirectUrisNative from '../../fragments/_redirect-uris-native.mdx'; +import Checkpoint from '../../fragments/_checkpoint.md'; @@ -14,17 +16,17 @@ import Step from '@/mdx-components/Step'; Before you install Logto Android SDK, ensure `mavenCentral()` is added to your repository configuration in the Gradle project build file: -```kotlin -dependencyResolutionManagement { + +{`dependencyResolutionManagement { repositories { mavenCentral() } -} -``` +}`} + Add Logto Android SDK to your dependencies: -```kotlin +```kotlin title="build.gradle.kts" dependencies { implementation("io.logto.sdk:android:1.1.3") } @@ -32,7 +34,7 @@ dependencies { Since the SDK needs internet access, you need to add the following permission to your `AndroidManifest.xml` file: -```xml +```xml title="AndroidManifest.xml" @@ -46,18 +48,14 @@ Since the SDK needs internet access, you need to add the following permission to - + We use Kotlin in this example, but the concepts are the same for Java. Create a `LogtoViewModel.kt` and init `LogtoClient` in this view model: -

-  
-    {`//...with other imports
+```kotlin title="LogtoViewModel.kt"
+//...with other imports
 import io.logto.sdk.android.LogtoClient
 import io.logto.sdk.android.type.LogtoConfig
 
@@ -85,13 +83,12 @@ class LogtoViewModel(application: Application) : AndroidViewModel(application) {
             }
         }
     }
-}`}
-  
-
+} +``` then, create a `LogtoViewModel` for your `MainActivity.kt`: -```kotlin +```kotlin title="MainActivity.kt" //...with other imports class MainActivity : AppCompatActivity() { private val logtoViewModel: LogtoViewModel by viewModels { LogtoViewModel.Factory } @@ -101,12 +98,9 @@ class MainActivity : AppCompatActivity() {
- + -Before starting, you need to add a redirect URI in the Admin Console for your application. + In Android, the redirect URI follows the pattern: `$(LOGTO_REDIRECT_SCHEME)://$(YOUR_APP_PACKAGE)/callback`: @@ -115,98 +109,15 @@ In Android, the redirect URI follows the pattern: `$(LOGTO_REDIRECT_SCHEME)://$( Assuming you treat `io.logto.android` as the custom `LOGTO_REDIRECT_SCHEME`, and `io.logto.sample` is your app package name, the Redirect URI should be `io.logto.android://io.logto.sample/callback`. -You can add the redirect URI in the following input field: - - - -After the redirect URI is configured, we add a `signIn` method to your `LogtoViewModel.kt`, which will call `logtoClient.signIn` API to invoke the Logto sign-in page: - -
-  
-    {`//...with other imports
-class LogtoViewModel(application: Application) : AndroidViewModel(application) {
-    // ...other codes
-    fun signIn(context: Activity) {
-        logtoClient.signIn(context, "${props.redirectUris[0] ?? ''}") { logtoException ->
-            logtoException?.let { println(it) }
-        }
-    }
-}`}
-  
-
- -Now setup on-click listener for the sign-in button in your `MainActivity.kt` to call the `signIn` method: - -```kotlin -//...with other imports -class MainActivity : AppCompatActivity() { - override fun onCreate(savedInstanceState: Bundle?) { - //...other codes - - // Assume you have a button with id `sign_in_button` in your layout - val signInButton = findViewById
{{ userData | json }}
+

ID token: {{ idToken }}

Access token: {{ accessToken }}

@@ -160,12 +135,4 @@ And use it in the template:
- - - - - - diff --git a/packages/console/src/assets/docs/guides/spa-angular/index.ts b/packages/console/src/assets/docs/guides/spa-angular/index.ts index ef02c1ced7d..3073c73b72a 100644 --- a/packages/console/src/assets/docs/guides/spa-angular/index.ts +++ b/packages/console/src/assets/docs/guides/spa-angular/index.ts @@ -10,10 +10,7 @@ const metadata: Readonly = Object.freeze({ repo: 'js', path: 'packages/angular-sample', }, - fullGuide: { - title: 'Full Angular guide', - url: 'https://docs.logto.io/quick-starts/angular', - }, + fullGuide: 'angular', }); export default metadata; diff --git a/packages/console/src/assets/docs/guides/spa-chrome-extension/README.mdx b/packages/console/src/assets/docs/guides/spa-chrome-extension/README.mdx new file mode 100644 index 00000000000..98d22ecee13 --- /dev/null +++ b/packages/console/src/assets/docs/guides/spa-chrome-extension/README.mdx @@ -0,0 +1,238 @@ +import UriInputField from '@/mdx-components/UriInputField'; +import InlineNotification from '@/ds-components/InlineNotification'; +import Steps from '@/mdx-components/Steps'; +import Step from '@/mdx-components/Step'; +import NpmLikeInstallation from '@/mdx-components/NpmLikeInstallation'; + +import RegardingRedirectBasedSignIn from '../../fragments/_regarding-redirect-based-sign-in.md'; + +import extensionPopup from './extension-popup.webp'; + + + + + + + + + + + +Assuming you put a "Sign in" button in your Chrome extension's popup, the authentication flow will look like this: + +```mermaid +sequenceDiagram + participant A as Extension popup + participant B as Extension service worker + participant C as Logto sign-in experience + + A->>B: Invokes sign-in + B->>C: Redirects to Logto + C->>C: User signs in + C->>B: Redirects back to extension + B->>A: Notifies the popup +``` + +For other interactive pages in your extension, you just need to replace the `Extension popup` participant with the page's name. In this tutorial, we will focus on the popup page. + + + + + + + +### Update the `manifest.json` + +Logto SDK requires the following permissions in the `manifest.json`: + +```json title="manifest.json" +{ + "permissions": ["identity", "storage"], + "host_permissions": ["https://*.logto.app/*"] +} +``` + +- `permissions.identity`: Required for the Chrome Identity API, which is used to sign in and sign out. +- `permissions.storage`: Required for storing the user's session. +- `host_permissions`: Required for the Logto SDK to communicate with the Logto APIs. + + +If you are using a custom domain on Logto Cloud, you need to update the `host_permissions` to match your domain. + + +### Set up a background script (service worker) + +In your Chrome extension's background script, initialize the Logto SDK: + +```js title="service-worker.js" +import LogtoClient from '@logto/chrome-extension'; + +export const logtoClient = new LogtoClient({ + endpoint: '' + appId: '', +}); +``` + +Replace `` and `` with the actual values. You can find these values in the application page you just created in the Logto Console. + +If you don't have a background script, you can follow the [official guide](https://developer.chrome.com/docs/extensions/develop/concepts/service-workers/basics) to create one. + + +**Why do we need a background script?** + +Normal extension pages like the popup or options page can't run in the background, and they have the possibility to be closed during the authentication process. A background script ensures the authentication process can be properly handled. + + +Then, we need to listen to the message from other extension pages and handle the authentication process: + +```js title="service-worker.js" +chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { + // In the below code, since we return `true` for each action, we need to call `sendResponse` + // to notify the sender. You can also handle errors here, or use other ways to notify the sender. + + if (message.action === 'signIn') { + const redirectUri = chrome.identity.getRedirectURL('/callback'); + logtoClient.signIn(redirectUri).finally(sendResponse); + return true; + } + + if (message.action === 'signOut') { + const redirectUri = chrome.identity.getRedirectURL(); + logtoClient.signOut(redirectUri).finally(sendResponse); + return true; + } + + return false; +}); +``` + +You may notice there are two redirect URIs used in the code above. They are both created by `chrome.identity.getRedirectURL`, which is a [built-in Chrome API](https://developer.chrome.com/docs/extensions/reference/api/identity#method-getRedirectURL) to generate a redirect URL for auth flows. The two URIs will be: + +- `https://.chromiumapp.org/callback` for sign-in. +- `https://.chromiumapp.org/` for sign-out. + +Note that these URIs are not accessible, and they are only used for Chrome to trigger specific actions for the authentication process. + + + + + +As we mentioned in the previous step, we need to update the Logto application settings to allow the redirect URIs we just created (`https://.chromiumapp.org/callback`): + + + +And the post sign-out redirect URI (`https://.chromiumapp.org/`): + + + +Finally, the CORS allowed origins should include the extension's origin (`chrome-extension://`). The SDK in Chrome extension will use this origin to communicate with the Logto APIs. + + + +Don't forget to replace `` with your actual extension ID and click the "Save" button. + + + + + +We're almost there! Let's add the sign-in and sign-out buttons and other necessary logic to the popup page. + +In the `popup.html` file: + +```html title="popup.html" + +``` + +In the `popup.js` file (assuming `popup.js` is included in the `popup.html`): + +```js title="popup.js" +document.getElementById('sign-in').addEventListener('click', async () => { + await chrome.runtime.sendMessage({ action: 'signIn' }); + // Sign-in completed (or failed), you can update the UI here. +}); + +document.getElementById('sign-out').addEventListener('click', async () => { + await chrome.runtime.sendMessage({ action: 'signOut' }); + // Sign-out completed (or failed), you can update the UI here. +}); +``` + + + + + +Now you can test the authentication flow in your Chrome extension: + +1. Open the extension popup. +2. Click on the "Sign in" button. +3. You will be redirected to the Logto sign-in page. +4. Sign in with your Logto account. +5. You will be redirected back to the Chrome. + + + + + +Since Chrome provide unified storage APIs, rather than the sign-in and sign-out flow, all other Logto SDK methods can be used in the popup page directly. + +In your `popup.js`, you can reuse the `LogtoClient` instance created in the background script, or create a new one with the same configuration: + +```js title="popup.js" +import LogtoClient from '@logto/chrome-extension'; + +const logtoClient = new LogtoClient({ + endpoint: '' + appId: '', +}); + +// Or reuse the logtoClient instance created in the background script +import { logtoClient } from './service-worker.js'; +``` + +Then you can create a function to load the authentication state and user's profile: + +```js title="popup.js" +const loadAuthenticationState = async () => { + const isAuthenticated = await logtoClient.isAuthenticated(); + // Update the UI based on the authentication state + + if (isAuthenticated) { + const user = await logtoClient.getIdTokenClaims(); // { sub: '...', email: '...', ... } + // Update the UI with the user's profile + } +}; +``` + +You can also combine the `loadAuthenticationState` function with the sign-in and sign-out logic: + +```js title="popup.js" +document.getElementById('sign-in').addEventListener('click', async () => { + await chrome.runtime.sendMessage({ action: 'signIn' }); + await loadAuthenticationState(); +}); + +document.getElementById('sign-out').addEventListener('click', async () => { + await chrome.runtime.sendMessage({ action: 'signOut' }); + await loadAuthenticationState(); +}); +``` + +Here's an example of the popup page with the authentication state: + +Popup page + + + + + +- **Service worker bundling**: If you use a bundler like Webpack or Rollup, you need to explicitly set the target to `browser` or similar to avoid unnecessary bundling of Node.js modules. +- **Module resolution**: Logto Chrome extension SDK is an ESM-only module. + +See our [sample project](https://github.com/logto-io/js/tree/HEAD/packages/chrome-extension-sample) for a complete example with TypeScript, Rollup, and other configurations. + + + + diff --git a/packages/console/src/assets/docs/guides/spa-chrome-extension/config.json b/packages/console/src/assets/docs/guides/spa-chrome-extension/config.json new file mode 100644 index 00000000000..4721ad2f793 --- /dev/null +++ b/packages/console/src/assets/docs/guides/spa-chrome-extension/config.json @@ -0,0 +1,3 @@ +{ + "order": 1.1 +} diff --git a/packages/console/src/assets/docs/guides/spa-chrome-extension/extension-popup.webp b/packages/console/src/assets/docs/guides/spa-chrome-extension/extension-popup.webp new file mode 100644 index 0000000000000000000000000000000000000000..9e136c06e252ea6b86677b44130bd672f363bc5c GIT binary patch literal 14648 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�T|PLeiXi%`cH}TeIYq@wy+S!=;X}Fml15RZZ+iw}SK7)*GERf1N zsH@2sZ+_9CtZNVKD^g;@uie&RfVOgNAk6Q&^Tb6#-WatoupSe^y5D@5sah~~U%b*% zVBH`*ZN~{E^9t}BOHT*nEMrSB&&9ZAwX3sngYyM7&?By@1=;SWO~G}xYe8^>hMWG? z%OD7y(c0L__??qcpXkbewBLIK3&_*REHmFApXYMS5&+x&J^rBvy$i7I| z^pc0?-3VVlrwoMESdkqNl|*|oH3_wMgU(bl9MA=}VdVg=BYrA2bH`asjP{40>{?sr$y_7X$DB^ zO3R9LW*f&pVvGE#K9T7OZ=7hlSP~ul0Zy_98mcK$KzB)Z6Bjv4?Wb%U@A|`9uTXS9 zwkh$*GnCn=iPrD_&3}1345qVl{F89)na@AsH-02Cj(m~3=Y6MO`c%-++&7GXL=9qz zI&@f*4c2p}Aw!R$cLqKk7`mo*bRlgopdY|dA2P_+Ih@g2@YCEUS}F?Cq>XFvLt4I8 z^%(lA;DgTcHJAo%dl!~kUdOfd=equxI}6wjXH}jd%xkp`?k&IQ_jb)C=BfcMq3IfG zZ)eF%Ezxys)YZe1PrBhI5*;t|=IXsla37bQ#3rAlE^#*!k(0Efi92Ihrh4 zDT#A+yesrFi%Cjq29|!iyp2aG`==ck?I89(#MTOiMYc-l_^rO7&7z$X-l#JyA~6D< z8lgoUN{+e`*T{h75C@h+vSNoN?NQ6x$Qt4fk*Sh?ylOS!p@}>%?bRCSBC4Ewj26mo zmXFW|O#r3Gp;`f3O^xl2IgY2XyY{6#%R;|XY_$QmwE#b@ISO#*F$_0MvksrXb2^N_ zlM5BtvlkAOS>uhRv`*e}owWqltsNGyFh?60O7jF}hdOFNyVsEVO>oHJZloJ~UH zR_Hy*@upAw6vx-FIYUITotdlqQ2fEnHExXzI+bd64bAOhtUklnHSv=4Eefd}xtW~) zZ&{+X;G93)JaWcvkg6$SiZ8`T5CWFRj{BGsqRzH~+WkGNP3OB!gBU2FNCMtMgEBuq z4SIQ)gg67owh}-4XzoVthI2}Ggr^VXmT;L=O{Sm3cK+ZCN>t=DtoBbk^QS)^&L4}l z)m`Ca)P}3$-;)_rxT8KFKMK}y^EKI~tdGg1 zXZ)mWp=g(}o6U>e%|=t3VWcU}E@yKGm{LSVTMyG2*uCx T0CsYQu0Hxwol<6ie~$kL<|mb% literal 0 HcmV?d00001 diff --git a/packages/console/src/assets/docs/guides/spa-chrome-extension/index.ts b/packages/console/src/assets/docs/guides/spa-chrome-extension/index.ts new file mode 100644 index 00000000000..90ad8eb36eb --- /dev/null +++ b/packages/console/src/assets/docs/guides/spa-chrome-extension/index.ts @@ -0,0 +1,16 @@ +import { ApplicationType } from '@logto/schemas'; + +import { type GuideMetadata } from '../types'; + +const metadata: Readonly = Object.freeze({ + name: 'Chrome extension', + description: 'Build a Chrome extension with Logto.', + target: ApplicationType.SPA, + sample: { + repo: 'js', + path: 'packages/chrome-extension-sample', + }, + fullGuide: 'chrome-extension', +}); + +export default metadata; 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 new file mode 100644 index 00000000000..4fc53255e9c --- /dev/null +++ b/packages/console/src/assets/docs/guides/spa-chrome-extension/logo.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/console/src/assets/docs/guides/spa-react/README.mdx b/packages/console/src/assets/docs/guides/spa-react/README.mdx index 8f684b45e82..ce910680ca0 100644 --- a/packages/console/src/assets/docs/guides/spa-react/README.mdx +++ b/packages/console/src/assets/docs/guides/spa-react/README.mdx @@ -1,9 +1,11 @@ import UriInputField from '@/mdx-components/UriInputField'; -import Tabs from '@mdx/components/Tabs'; -import TabItem from '@mdx/components/TabItem'; import InlineNotification from '@/ds-components/InlineNotification'; import Steps from '@/mdx-components/Steps'; import Step from '@/mdx-components/Step'; +import NpmLikeInstallation from '@/mdx-components/NpmLikeInstallation'; + +import Checkpoint from '../../fragments/_checkpoint.md'; +import RedirectUrisWeb, { defaultRedirectUri, defaultPostSignOutUri } from '../../fragments/_redirect-uris-web.mdx'; @@ -11,37 +13,16 @@ import Step from '@/mdx-components/Step'; title="Installation" subtitle="Install Logto SDK for your project" > - - - -```bash -npm i @logto/react -``` - - - - -```bash -yarn add @logto/react -``` - - - -```bash -pnpm add @logto/react -``` + - - - + -Import and use `LogtoProvider` to provide a Logto context: +Import and use `LogtoProvider` to provide a Logto context to your app: -
-  
+
     {`import { LogtoProvider, LogtoConfig } from '@logto/react';
 
 const config: LogtoConfig = {
@@ -54,59 +35,23 @@ const App = () => (
     
   
 );`}
-  
-
+
- - - - In the following steps, we assume your app is running on http://localhost:3000. - - -### Configure Redirect URI + -First, let’s enter your redirect URI. E.g. `http://localhost:3000/callback`. + - - -### Implement a sign-in button - -We provide two hooks `useHandleSignInCallback()` and `useLogto()` which can help you easily manage the authentication flow. - -Go back to your IDE/editor, use the following code to implement the sign-in button: - -
-  
-    {`import { useLogto } from '@logto/react';
-
-const SignIn = () => {
-  const { signIn, isAuthenticated } = useLogto();
-
-  if (isAuthenticated) {
-    return 
Signed in
; - } - - return ( - - ); -};`} -
-
+
-### Handle redirect + -We're almost there! In the last step, we use `http://localhost:3000/callback` as the Redirect URI, and now we need to handle it properly. +After the user signs in, Logto will redirect the user back to the redirect URI configured above. However, there are still things to do to make your application work properly. -First let's create a callback component: +First let's create a callback page: -```tsx +```tsx title="pages/Callback/index.tsx" import { useHandleSignInCallback } from '@logto/react'; const Callback = () => { @@ -118,92 +63,99 @@ const Callback = () => { if (isLoading) { return
Redirecting...
; } + + return null; }; ``` -Finally insert the code below to create a `/callback` route which does NOT require authentication: +Then, insert the code below to create a `/callback` route which does NOT require authentication: -```tsx +```tsx title="App.tsx" // Assuming react-router } /> ```
- - -Calling `.signOut()` will clear all the Logto data in memory and localStorage if they exist. + -After signing out, it'll be great to redirect user back to your website. Let's add `http://localhost:3000` as the Post Sign-out URI below, and use it as the parameter when calling `.signOut()`. +We provide a hook `useLogto()` which can help you easily manage the authentication flow. - + + Before calling `.signIn()`, make sure you have correctly configured Redirect URI in Admin Console. + -### Implement a sign-out button + + {`import { useLogto } from '@logto/react'; -
-  
-    {`const SignOut = () => {
-  const { signOut } = useLogto();
+const Home = () => {
+  const { signIn, signOut, isAuthenticated } = useLogto();
 
-  return (
-    
+  return isAuthenticated ? (
+    
+  ) : (
+    
   );
 };`}
-  
-
+
+ +Calling `.signOut()` will clear all the Logto data in memory and localStorage if they exist. + +
+ + + + - + -In Logto SDK, generally we can use `logtoClient.isAuthenticated` to check the authentication status, if the user is signed in, the value will be `true`, otherwise, the value will be `false`. +To display the user's information, you can use the `getIdTokenClaims()` method. For example, in your Home page: -In Logto React SDK, the `isAuthenticated` status can be checked by using the `useLogto` hook. In the example code below, we can use it to programmatically show and hide the sign-in and sign-out buttons. And also use `getIdTokenClaims` to get the id of the currently logged-in user. +```tsx title="pages/Home/index.tsx" +import { useLogto, type IdTokenClaims } from '@logto/react'; +import { useEffect, useState } from 'react'; -```tsx const Home = () => { - const { isAuthenticated, getIdTokenClaims, signIn, signOut } = useLogto(); - const [userId, setUserId] = useState(''); + const { isAuthenticated, getIdTokenClaims } = useLogto(); + const [user, setUser] = useState(); useEffect(() => { (async () => { if (isAuthenticated) { const claims = await getIdTokenClaims(); - setUserId(claims.sub); + setUser(claims); } })(); - }, [isAuthenticated]); + }, [getIdTokenClaims, isAuthenticated]); return ( -
- {userId &&

Logged in as {userId}

} - {isAuthenticated ? ( - - ) : ( - - )} -
+ // ... + {isAuthenticated && user && ( + + + + + + + + + {Object.entries(user).map(([key, value]) => ( + + + + + ))} + +
NameValue
{key}{typeof value === 'string' ? value : JSON.stringify(value)}
+ )} ); -}; +} ```
- - -Now, you can test your application: - -1. Run your application, you will see the sign-in button. -2. Click the sign-in button, the SDK will init the sign-in process and redirect you to the Logto sign-in page. -3. After you signed in, you will be redirected back to your application and see user ID and the sign-out button. -4. Click the sign-out button to sign-out. - - -
diff --git a/packages/console/src/assets/docs/guides/spa-react/index.ts b/packages/console/src/assets/docs/guides/spa-react/index.ts index 53fdeeff520..b560d8dd4ee 100644 --- a/packages/console/src/assets/docs/guides/spa-react/index.ts +++ b/packages/console/src/assets/docs/guides/spa-react/index.ts @@ -11,10 +11,7 @@ const metadata: Readonly = Object.freeze({ path: 'packages/react-sample', }, isFeatured: true, - fullGuide: { - title: 'Full React SDK tutorial', - url: 'https://docs.logto.io/quick-starts/react', - }, + fullGuide: 'react', }); export default metadata; diff --git a/packages/console/src/assets/docs/guides/spa-vanilla/README.mdx b/packages/console/src/assets/docs/guides/spa-vanilla/README.mdx index d0848d2b299..cff41a900ec 100644 --- a/packages/console/src/assets/docs/guides/spa-vanilla/README.mdx +++ b/packages/console/src/assets/docs/guides/spa-vanilla/README.mdx @@ -1,10 +1,13 @@ import UriInputField from '@/mdx-components/UriInputField'; -import Tabs from '@mdx/components/Tabs'; -import TabItem from '@mdx/components/TabItem'; +import Tabs from '@/mdx-components/Tabs'; +import TabItem from '@/mdx-components/TabItem'; import InlineNotification from '@/ds-components/InlineNotification'; import Steps from '@/mdx-components/Steps'; import Step from '@/mdx-components/Step'; +import Checkpoint from '../../fragments/_checkpoint.md'; +import RedirectUrisWeb, { defaultRedirectUri, defaultPostSignOutUri } from '../../fragments/_redirect-uris-web.mdx'; + - + ```bash -yarn add @logto/browser +pnpm add @logto/browser ``` - + ```bash -pnpm add @logto/browser +yarn add @logto/browser ``` + + + +```html + +``` @@ -40,133 +51,108 @@ pnpm add @logto/browser Import and init `LogtoClient` with configs: -
-  
+
     {`import LogtoClient from '@logto/browser';
 
 const logtoClient = new LogtoClient({
   endpoint: '${props.endpoint}',
   appId: '${props.app.id}',
 });`}
-  
-
+ - - - - In the following steps, we assume your app is running on http://localhost:3000. - - -### Configure Redirect URI - -First, let’s enter your redirect URI. E.g. `http://localhost:3000/callback`. - - - -### Implement a sign-in button - -Go back to your IDE/editor, use the following code to implement the sign-in button: - -
-  
-    {``}
-  
-
- -### Handle redirect + -We're almost there! In the last step, we use `http://localhost:3000/callback` as the Redirect URI, and now we need to handle it properly. - -Insert the code below in your `/callback` route: - -```ts -await logtoClient.handleSignInCallback(window.location.href); - -if (!logtoClient.isAuthenticated) { - // Handle failed sign-in - alert('Failed to sign in'); - return; -} - -// Handle successful sign-in. E.g. redirect to home page. -window.location.assign('http://localhost:3000/'); -``` - -Now you can test the sign-in flow. + - - -Calling `.signOut()` will clear all the Logto data in memory and localStorage if they exist. + -After signing out, it'll be great to redirect user back to your website. Let's add `http://localhost:3000` as the Post Sign-out URI below, and use it as the parameter when calling `.signOut()`. +There are still things to do after the user is redirected back to your application from Logto. Let's handle it properly. - +```ts title="pages/Callback.js" +const callbackHandler = async (logtoClient) => { + await logtoClient.handleSignInCallback(window.location.href); -### Implement a sign-out button + if (!logtoClient.isAuthenticated) { + // Handle failed sign-in + alert('Failed to sign in'); + return; + } -
-  
-    {``}
-  
-
+ // Handle successful sign-in + window.location.assign('/'); +}; +```
- - -In Logto SDK, generally we can use `logtoClient.isAuthenticated` to check the authentication status, if the user is signed in, the value will be `true`, otherwise, the value will be `false`. + -In your vanilla JS app, you can use the `isAuthenticated` status to programmatically show and hide the sign-in and sign-out buttons. Let's see how to do it. +`logtoClient` provides `signIn` and `signOut` methods to help you easily manage the authentication flow. -```ts -const redirectUrl = 'http://localhost:3000/callback'; -const baseUrl = 'http://localhost:3000'; + + {`const isAuthenticated = await logtoClient.isAuthenticated(); -// Conditional rendering of sign-in and sign-out buttons -const isAuthenticated = await logtoClient.isAuthenticated(); - -// Assuming there's a div with id 'container' in your HTML -const container = document.querySelector('#container'); - -const onClickSignIn = () => logtoClient.signIn(redirectUrl); -const onClickSignOut = () => logtoClient.signOut(baseUrl); +const onClickSignIn = () => { + logtoClient.signIn('${props.redirectUris[0] ?? defaultRedirectUri}'); +}; +const onClickSignOut = () => { + logtoClient.signOut('${props.postLogoutRedirectUris[0] ?? defaultPostSignOutUri}'); +}; const button = document.createElement('button'); button.innerHTML = isAuthenticated ? 'Sign Out' : 'Sign In'; button.addEventListener('click', isAuthenticated ? onClickSignOut : onClickSignIn); -container.append(button); -``` +document.body.appendChild(button);`} + + +Calling `.signOut()` will clear all the Logto data in memory and `localStorage` if they exist. -Now, you can test your application: + -1. Run your application, you will see the sign-in button. -2. Click the sign-in button, the SDK will init the sign-in process and redirect you to the Logto sign-in page. -3. After you signed in, you will be redirected back to your application and see user ID and the sign-out button. -4. Click the sign-out button to sign-out. + + + + +To display the user's information, you can use the `logtoClient.getIdTokenClaims()` method. For example, in your Home page: + +```js title="pages/Home.js" +const userInfo = await logtoClient.getIdTokenClaims(); + +// Generate display table for ID token claims +const table = document.createElement('table'); +const thead = document.createElement('thead'); +const tr = document.createElement('tr'); +const thName = document.createElement('th'); +const thValue = document.createElement('th'); +thName.innerHTML = 'Name'; +thValue.innerHTML = 'Value'; +tr.append(thName, thValue); +thead.append(tr); +table.append(thead); + +const tbody = document.createElement('tbody'); + +for (const [key, value] of Object.entries(userInfo)) { + const tr = document.createElement('tr'); + const tdName = document.createElement('td'); + const tdValue = document.createElement('td'); + tdName.innerHTML = key; + tdValue.innerHTML = typeof value === 'string' ? value : JSON.stringify(value); + tr.append(tdName, tdValue); + tbody.append(tr); +} + +table.append(tbody); +``` diff --git a/packages/console/src/assets/docs/guides/spa-vanilla/index.ts b/packages/console/src/assets/docs/guides/spa-vanilla/index.ts index 48198f3cc2d..a75ff94be90 100644 --- a/packages/console/src/assets/docs/guides/spa-vanilla/index.ts +++ b/packages/console/src/assets/docs/guides/spa-vanilla/index.ts @@ -10,10 +10,7 @@ const metadata: Readonly = Object.freeze({ repo: 'js', path: 'packages/browser-sample', }, - fullGuide: { - title: 'Full vanilla JS SDK tutorial', - url: 'https://docs.logto.io/quick-starts/vanilla-js', - }, + fullGuide: 'vanilla-js', }); export default metadata; diff --git a/packages/console/src/assets/docs/guides/spa-vue/README.mdx b/packages/console/src/assets/docs/guides/spa-vue/README.mdx index b9a63997747..a522828ac43 100644 --- a/packages/console/src/assets/docs/guides/spa-vue/README.mdx +++ b/packages/console/src/assets/docs/guides/spa-vue/README.mdx @@ -1,9 +1,12 @@ import UriInputField from '@/mdx-components/UriInputField'; -import Tabs from '@mdx/components/Tabs'; -import TabItem from '@mdx/components/TabItem'; import InlineNotification from '@/ds-components/InlineNotification'; import Steps from '@/mdx-components/Steps'; import Step from '@/mdx-components/Step'; +import NpmLikeInstallation from '@/mdx-components/NpmLikeInstallation'; + +import Checkpoint from '../../fragments/_checkpoint.md'; +import RedirectUrisWeb from '../../fragments/_redirect-uris-web.mdx'; +import { defaultRedirectUri, defaultPostSignOutUri } from '../../fragments/_redirect-uris-web.mdx'; @@ -11,29 +14,9 @@ import Step from '@/mdx-components/Step'; title="Installation" subtitle="Install Logto SDK for your project" > - - - -```bash -npm i @logto/vue -``` - - - - -```bash -yarn add @logto/vue -``` - - + -```bash -pnpm add @logto/vue -``` - - - - We only support Vue 3 Composition API at this point. Will add support to Vue Options API and - possibly Vue 2 in future releases. + Logto Vue SDK is built with Vue 3 composition API. Therefore, only Vue 3 is supported at the moment. Contact us if you want to add support for Vue 2. Import and use `createLogto` to install Logto plugin: -
-  
+
     {`import { createLogto, LogtoConfig } from '@logto/vue';
+import { createApp } from 'vue';
+import App from './App.vue';
 
 const config: LogtoConfig = {
   endpoint: '${props.endpoint}',
@@ -60,69 +43,29 @@ const app = createApp(App);
 
 app.use(createLogto, config);
 app.mount("#app");`}
-  
-
+
- - - - In the following steps, we assume your app is running on http://localhost:3000. - - -### Configure Redirect URI - -First, let’s enter your redirect URI. E.g. `http://localhost:3000/callback`. + - + -### Implement a sign-in button - -We provide two composables `useHandleSignInCallback()` and `useLogto()`, which can help you easily manage the authentication flow. - -Go back to your IDE/editor, use the following code to implement the sign-in button: + -
-
-{``}
+There are still things to do after the user is redirected back to your application from Logto. Let's handle it properly.
 
-
-
+First let's create a callback page: -```html - -``` +```ts title="views/CallbackView.vue" +import { useHandleSignInCallback } from '@logto/vue'; +import router from '@/router'; -### Handle redirect - -We're almost there! In the last step, we use `http://localhost:3000/callback` as the Redirect URI, and now we need to handle it properly. - -First let's create a callback component: - -```html - - +const { isLoading } = useHandleSignInCallback(() => { + // Do something when finished, e.g. redirect to home page +}); ``` ```html @@ -132,9 +75,9 @@ First let's create a callback component: ``` -Finally insert the code below to create a `/callback` route which does NOT require authentication: +Insert the code below in your `/callback` route which does NOT require authentication: -```ts +```ts title="router/index.ts" // Assuming vue-router const router = createRouter({ routes: [ @@ -149,80 +92,70 @@ const router = createRouter({
- + -Calling `.signOut()` will clear all the Logto data in memory and localStorage if they exist. +We provide a composable `useLogto()` which can help you easily manage the authentication flow. -After signing out, it'll be great to redirect user back to your website. Let's add `http://localhost:3000` as the Post Sign-out URI below, and use it as the parameter when calling `.signOut()`. + + {`import { useLogto } from '@logto/vue'; - +const { signIn, signOut, isAuthenticated } = useLogto(); -### Implement a sign-out button +const onClickSignIn = () => signIn('${props.redirectUris[0] || defaultRedirectUri}'); +const onClickSignOut = () => signOut('${props.postLogoutRedirectUris[0] || defaultPostSignOutUri}'); +`} + -
-
-{``}
+
 
-
-
+ -```html - -``` + - - -In Logto SDK, generally we can use `logtoClient.isAuthenticated` to check the authentication status, if the user is signed in, the value will be `true`, otherwise, the value will be `false`. + -In Logto Vue SDK, the `isAuthenticated` status can be checked by using the `useLogto` composable. In the example code below, we can use it to programmatically show and hide the sign-in and sign-out buttons. Also we'll use `getIdTokenClaims` to get the ID of the currently logged-in user. +To display the user's information, you can use the `getIdTokenClaims()` method. For example, in your Home page: -```tsx -import { useLogto } from "@logto/vue"; -import { ref } from "vue"; +```ts title="views/HomeView.vue" +import { useLogto, type IdTokenClaims } from '@logto/vue'; +import { ref } from 'vue'; -const { isAuthenticated, getIdTokenClaims, signIn, signOut } = useLogto(); -const userId = ref(); +const { isAuthenticated, getIdTokenClaims } = useLogto(); +const user = ref(); if (isAuthenticated.value) { (async () => { const claims = await getIdTokenClaims(); - userId.value = claims.sub; + user.value = claims; })(); } ``` ```html ``` - - -Now, you can test your application: - -1. Run your application, you will see the sign-in button. -2. Click the sign-in button, the SDK will init the sign-in process and redirect you to the Logto sign-in page. -3. After you signed in, you will be redirected back to your application and see user ID and the sign-out button. -4. Click the sign-out button to sign-out. - - -
diff --git a/packages/console/src/assets/docs/guides/spa-vue/index.ts b/packages/console/src/assets/docs/guides/spa-vue/index.ts index 71f6553bcd8..195ceebed9e 100644 --- a/packages/console/src/assets/docs/guides/spa-vue/index.ts +++ b/packages/console/src/assets/docs/guides/spa-vue/index.ts @@ -12,10 +12,7 @@ const metadata: Readonly = Object.freeze({ path: 'packages/vue-sample', }, isFeatured: true, - fullGuide: { - title: 'Full Vue SDK tutorial', - url: 'https://docs.logto.io/quick-starts/vue', - }, + fullGuide: 'vue', }); export default metadata; diff --git a/packages/console/src/assets/docs/guides/spa-webflow/README.mdx b/packages/console/src/assets/docs/guides/spa-webflow/README.mdx index 1ad4cf7cf04..ceca5e9683c 100644 --- a/packages/console/src/assets/docs/guides/spa-webflow/README.mdx +++ b/packages/console/src/assets/docs/guides/spa-webflow/README.mdx @@ -1,6 +1,6 @@ import UriInputField from '@/mdx-components/UriInputField'; -import Tabs from '@mdx/components/Tabs'; -import TabItem from '@mdx/components/TabItem'; +import Tabs from '@/mdx-components/Tabs'; +import TabItem from '@/mdx-components/TabItem'; import InlineNotification from '@/ds-components/InlineNotification'; import Steps from '@/mdx-components/Steps'; import Step from '@/mdx-components/Step'; @@ -22,8 +22,7 @@ In this step, we'll add global-level custom code to your Webflow site. Since NPM Open the "Site settings" page, and navigate to the "Custom code" section. Add the following code to the "Head code" section. -
-  
+
     {``}
-  
-
+ @@ -59,15 +57,13 @@ First, let’s enter your redirect URI. E.g. `https://your-awesome-site.webflow. Return to your Webflow designer, drag and drop a "Sign in" button to the home page, and assign it an ID “sign-in” for later reference using `getElementById()`. -
-  
+
     {``}
-  
-
+ ### Handle redirect @@ -99,13 +95,11 @@ After signing out, it'll be great to redirect user back to your website. Let's a Return to the Webflow designer, and add a “Sign out” button on your home page. Similarly, assign an ID “sign-out” to the button, and add the following code to the page-level custom code. -
-  
+
     {`const signOutButton = document.getElementById('sign-out');
 const onClickSignOut = () => logtoClient.signOut('${props.postLogoutRedirectUris[0] ?? 'https://your-awesome-site.webflow.io'}');
 signOutButton.addEventListener('click', onClickSignOut);`}
-  
-
+ diff --git a/packages/console/src/assets/docs/guides/types.ts b/packages/console/src/assets/docs/guides/types.ts index 5f11825cd8b..0b18523c99e 100644 --- a/packages/console/src/assets/docs/guides/types.ts +++ b/packages/console/src/assets/docs/guides/types.ts @@ -31,18 +31,24 @@ export type GuideMetadata = { /** Indicate whether the application is for third-party use */ isThirdParty?: boolean; - /** The related complete guide for this guide which will be displayed in the 'Further readings' section. */ - fullGuide?: { + /** The related complete guide url relative to the quick starts page (https://docs.logto.io/quick-starts). */ + fullGuide?: string; + + /** The related URLs to add to the further readings section. */ + furtherReadings?: Array<{ title: string; - url: string; - }; + url: URL; + }>; }; /** The guide instance to build in the console. */ export type Guide = { + order: number; /** The unique identifier of the guide. */ id: string; - Logo: LazyExoticComponent; + Logo: + | 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/README.mdx b/packages/console/src/assets/docs/guides/web-dotnet-core-blazor-server/README.mdx index 1258ea8a865..20508bf92da 100644 --- a/packages/console/src/assets/docs/guides/web-dotnet-core-blazor-server/README.mdx +++ b/packages/console/src/assets/docs/guides/web-dotnet-core-blazor-server/README.mdx @@ -1,52 +1,35 @@ -import UriInputField from '@/mdx-components/UriInputField'; import Steps from '@/mdx-components/Steps'; import Step from '@/mdx-components/Step'; +import SignInAndSignOutFlows from '../web-dotnet-core-mvc/fragments/_sign-in-and-sign-out-flows.mdx'; +import ConfigureRedirectUris from '../web-dotnet-core-mvc/fragments/_configure-redirect-uris.mdx'; +import Installation from '../web-dotnet-core-mvc/fragments/_installation.md'; +import AddAuthentication from '../web-dotnet-core-mvc/fragments/_add-authentication.mdx'; +import DisplayUserInformation from '../web-dotnet-core-mvc/fragments/_display-user-information.md'; +import Checkpoint from '../../fragments/_checkpoint.md'; - + -This tutorial will show you how to use Logto ASP.NET Core authentication middleware to protect your web application. - -
    -
  • It assumes your website is hosted on {props.sampleUrls.origin}.
  • -
- -### Installation - -```bash -dotnet add package Logto.AspNetCore.Authentication -``` +
-Open `Startup.cs` (or `Program.cs`) and add the following code to register Logto authentication middleware: + -
-  
-{`using Logto.AspNetCore.Authentication;
+
 
-var builder = WebApplication.CreateBuilder(args);
+
 
-builder.Services.AddLogtoAuthentication(options =>
-{
-  options.Endpoint = "${props.endpoint}";
-  options.AppId = "${props.app.id}";
-  options.AppSecret = "${props.app.secret}";
-});
+
 
-app.UseAuthentication();`}
-  
-
+
-The `AddLogtoAuthentication` method will do the following things: + -- Set the default authentication scheme to `LogtoDefaults.CookieScheme`. -- Set the default challenge scheme to `LogtoDefaults.AuthenticationScheme`. -- Set the default sign-out scheme to `LogtoDefaults.AuthenticationScheme`. -- Add cookie and OpenID Connect authentication handlers to the authentication scheme. + @@ -56,7 +39,7 @@ Since Blazor Server uses SignalR to communicate between the server and the clien To make it right, we need to explicitly add two endpoints for sign-in and sign-out redirects: -```csharp +```csharp title="Program.cs" app.MapGet("/SignIn", async context => { if (!(context.User?.Identity?.IsAuthenticated ?? false)) @@ -82,35 +65,11 @@ Now we can redirect to these endpoints to trigger sign-in and sign-out.
- - -

-First, let's enter your redirect URI. E.g. {props.sampleUrls.origin + 'Callback'} (replace the endpoint with yours). This is where Logto will redirect users after they sign in. -

- - - -Remember to keep the path `/Callback` in the URI as it's the default value for the Logto authentication middleware. - ---- - -To clean up both ASP.NET session and Logto session, we can designate a post sign-out redierct URI. This is where Logto will redirect users after they sign out. - -

-For example, set the URI to {props.sampleUrls.origin + 'SignedOutCallback'} (replace the endpoint with yours): -

- - - -Remember to keep the path `/SignedOutCallback` in the URI as it's the default value for the Logto authentication middleware. - -
- In the Razor component, add the following code: -```cshtml +```cshtml title="Components/Pages/Index.razor" @using Microsoft.AspNetCore.Components.Authorization @using System.Security.Claims @inject AuthenticationStateProvider AuthenticationStateProvider @@ -160,41 +119,13 @@ The page will show the "Sign in" button if the user is not authenticated, and sh - - -Now you can run the web application and try to sign in and sign out with Logto: - -1. Open the web application in your browser, you should see "Is authenticated: False" and the "Sign in" button. -2. Click the "Sign in" button, and you should be redirected to the Logto sign-in page. -3. After you have signed in, you should be redirected back to the web application, and you should see "Is authenticated: True" and the "Sign out" button. -4. Click the "Sign out" button, and you should be redirected to the Logto sign-out page, and then redirected back to the web application. - - - - - -To know if the user is authenticated, you can check the `User.Identity?.IsAuthenticated` property. - -To get the user profile claims, you can use the `User.Claims` property: - -```csharp -var claims = User.Claims; - -// Get the user ID -var userId = claims.FirstOrDefault(c => c.Type == LogtoParameters.Claims.Subject)?.Value; -``` - -See the [full tutorial](https://docs.logto.io/quick-starts/dotnet-core/blazor-server/) for more details. - - - - + Alternatively, you can use the `AuthorizeView` component to conditionally render content based on the user's authentication state. This component is useful when you want to show different content to authenticated and unauthenticated users. In your Razor component, add the following code: -```cshtml +```cshtml title="Components/Pages/Index.razor" @using Microsoft.AspNetCore.Components.Authorization @* ... *@ @@ -214,7 +145,7 @@ In your Razor component, add the following code: The `AuthorizeView` component requires a cascading parameter of type `Task`. A direct way to get this parameter is to add the `` component. However, due to the nature of Blazor Server, we cannot simply add the component to the layout or the root component (it may not work as expected). Instead, we can add the following code to the builder (`Program.cs` or `Startup.cs`) to provide the cascading parameter: -```csharp +```csharp title="Program.cs" builder.Services.AddCascadingAuthenticationState(); ``` @@ -222,4 +153,16 @@ Then you can use the `AuthorizeView` component in every component that needs it. + + + + + + + + + + + +
diff --git a/packages/console/src/assets/docs/guides/web-dotnet-core-blazor-server/index.ts b/packages/console/src/assets/docs/guides/web-dotnet-core-blazor-server/index.ts index 9deb50ecb89..1e01259c368 100644 --- a/packages/console/src/assets/docs/guides/web-dotnet-core-blazor-server/index.ts +++ b/packages/console/src/assets/docs/guides/web-dotnet-core-blazor-server/index.ts @@ -10,10 +10,7 @@ const metadata: Readonly = Object.freeze({ repo: 'csharp', path: '/', }, - fullGuide: { - title: 'Full .NET Core (Blazor Server) integration tutorial', - url: 'https://docs.logto.io/quick-starts/dotnet-core/blazor-server', - }, + fullGuide: 'dotnet-core/blazor-server', }); export default metadata; diff --git a/packages/console/src/assets/docs/guides/web-dotnet-core-blazor-wasm/README.mdx b/packages/console/src/assets/docs/guides/web-dotnet-core-blazor-wasm/README.mdx index 75718996401..1e1d62c3cb0 100644 --- a/packages/console/src/assets/docs/guides/web-dotnet-core-blazor-wasm/README.mdx +++ b/packages/console/src/assets/docs/guides/web-dotnet-core-blazor-wasm/README.mdx @@ -2,15 +2,14 @@ import UriInputField from '@/mdx-components/UriInputField'; import Steps from '@/mdx-components/Steps'; import Step from '@/mdx-components/Step'; +import Checkpoint from '../../fragments/_checkpoint.md'; +import RedirectUrisWeb, { defaultRedirectUri, defaultPostSignOutUri } from '../../fragments/_redirect-uris-web.mdx'; + -This tutorial will show you how to use [Blorc.OpenIdConnect](https://github.com/WildGums/Blorc.OpenIdConnect) to add Logto authentication to a Blazor WebAssembly application. - -
    -
  • It assumes your website is hosted on {props.sampleUrls.origin}.
  • -
+This guide will show you how to use [Blorc.OpenIdConnect](https://github.com/WildGums/Blorc.OpenIdConnect) to add Logto authentication to a Blazor WebAssembly application. ### Installation @@ -26,7 +25,7 @@ dotnet add package Blorc.OpenIdConnect Include `Blorc.Core/injector.js` the `index.html` file: -```html +```html title="index.html" @@ -38,7 +37,7 @@ Include `Blorc.Core/injector.js` the `index.html` file: Add the following code to the `Program.cs` file: -```csharp +```csharp title="Program.cs" using Blorc.OpenIdConnect; using Blorc.Services; @@ -66,45 +65,29 @@ Note: There's no need to use the `Microsoft.AspNetCore.Components.WebAssembly.Au
- - -### Configure redirect URI - -

-First, let's enter your redirect URI. E.g. {props.sampleUrls.origin + 'Callback'} (replace the endpoint with yours). This is where Logto will redirect users after they sign in. -

- - - -### Configure post sign-out redirect URI - -To clean up both ASP.NET session and Logto session, we can designate a post sign-out redierct URI. This is where Logto will redirect users after they sign out. + -

-For example, set the URI to {props.sampleUrls.origin + 'SignedOutCallback'} (replace the endpoint with yours): -

+ - +
-### Configure application + Add the following code to the `appsettings.json` file: -
-  
+
 {`// ...
   IdentityServer: {
     Authority: '${props.endpoint}oidc',
     ClientId: '${props.app.id}',
-    RedirectUri: '${props.redirectUris[0] ?? props.sampleUrls.callback}',
-    PostLogoutRedirectUri: '${props.postLogoutRedirectUris[0] ?? props.sampleUrls.origin}',
+    RedirectUri: '${props.redirectUris[0] ?? defaultRedirectUri}',
+    PostLogoutRedirectUri: '${props.postLogoutRedirectUris[0] ?? defaultPostSignOutUri}',
     ResponseType: 'code',
     Scope: 'openid profile', // Add more scopes if needed
   },
 }
 `}
-    
-
+
@@ -114,7 +97,7 @@ Add the following code to the `appsettings.json` file: In the Razor pages that require authentication, add the `AuthorizeView` component. Let's assume it's the `Home.razor` page: -```cshtml +```cshtml title="Pages/Home.razor" @using Microsoft.AspNetCore.Components.Authorization @page "/" @@ -138,7 +121,7 @@ In the Razor pages that require authentication, add the `AuthorizeView` componen In the `Home.razor.cs` file (create it if it doesn't exist), add the following code: -```csharp +```csharp title="Pages/Home.razor.cs" using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Web; @@ -175,11 +158,19 @@ public partial class Home : ComponentBase Once the user is authenticated, the `User` property will be populated with the user information. -### Display user information +
+ + + + + + + + Here are some examples of how to display user information in the `Home.razor` page: -```cshtml +```cshtml title="Pages/Home.razor" @* Signed in view *@ @@ -194,23 +185,4 @@ For more properties and claims, check the `User` and `Profile` classes in the `B - - -Now you can run the web application and try to sign in and sign out with Logto: - -1. Open the web application in your browser, you should see "Is authenticated: False" and the "Sign in" button. -2. Click the "Sign in" button, and you should be redirected to the Logto sign-in page. -3. After you have signed in, you should be redirected back to the web application, and you should see "Is authenticated: True" and the "Sign out" button. -4. Click the "Sign out" button, and you should be redirected to the Logto sign-out page, and then redirected back to the web application. - - - - - -To get the user profile, you can use the `User?.Profile` property; to fetch the access token, you can use the `User?.AccessToken` property or add it to your HTTP client using `.AddAccessToken()`. - -See the [full tutorial](https://docs.logto.io/quick-starts/dotnet-core/blazor-wasm/) for more details. - - -
diff --git a/packages/console/src/assets/docs/guides/web-dotnet-core-blazor-wasm/index.ts b/packages/console/src/assets/docs/guides/web-dotnet-core-blazor-wasm/index.ts index 55770849b8c..1ad19eeeed5 100644 --- a/packages/console/src/assets/docs/guides/web-dotnet-core-blazor-wasm/index.ts +++ b/packages/console/src/assets/docs/guides/web-dotnet-core-blazor-wasm/index.ts @@ -10,10 +10,7 @@ const metadata: Readonly = Object.freeze({ repo: 'csharp', path: '/', }, - fullGuide: { - title: 'Full .NET Core (Blazor WASM) integration tutorial', - url: 'https://docs.logto.io/quick-starts/dotnet-core/blazor-wasm', - }, + fullGuide: 'dotnet-core/blazor-wasm', }); export default metadata; diff --git a/packages/console/src/assets/docs/guides/web-dotnet-core-mvc/README.mdx b/packages/console/src/assets/docs/guides/web-dotnet-core-mvc/README.mdx index 8fa99eea24c..27f31597d8f 100644 --- a/packages/console/src/assets/docs/guides/web-dotnet-core-mvc/README.mdx +++ b/packages/console/src/assets/docs/guides/web-dotnet-core-mvc/README.mdx @@ -2,143 +2,68 @@ import UriInputField from '@/mdx-components/UriInputField'; import Steps from '@/mdx-components/Steps'; import Step from '@/mdx-components/Step'; - - - +import Checkpoint from '../../fragments/_checkpoint.md'; -This tutorial will show you how to use Logto ASP.NET Core authentication middleware to protect your web application. +import SignInAndSignOutFlows from './fragments/_sign-in-and-sign-out-flows.mdx'; +import ConfigureRedirectUris from './fragments/_configure-redirect-uris.mdx'; +import Installation from './fragments/_installation.md'; +import AddAuthentication from './fragments/_add-authentication.mdx'; +import DisplayUserInformation from './fragments/_display-user-information.md'; -
    -
  • It assumes your website is hosted on {props.sampleUrls.origin}.
  • -
+ -### Installation + -```bash -dotnet add package Logto.AspNetCore.Authentication -``` + -Open `Startup.cs` (or `Program.cs`) and add the following code to register Logto authentication middleware: - -
-  
-{`using Logto.AspNetCore.Authentication;
-
-var builder = WebApplication.CreateBuilder(args);
+
 
-builder.Services.AddLogtoAuthentication(options =>
-{
-  options.Endpoint = "${props.endpoint}";
-  options.AppId = "${props.app.id}";
-  options.AppSecret = "${props.app.secret}";
-});
-
-app.UseAuthentication();`}
-  
-
+
-The `AddLogtoAuthentication` method will do the following things: + -- Set the default authentication scheme to `LogtoDefaults.CookieScheme`. -- Set the default challenge scheme to `LogtoDefaults.AuthenticationScheme`. -- Set the default sign-out scheme to `LogtoDefaults.AuthenticationScheme`. -- Add cookie and OpenID Connect authentication handlers to the authentication scheme. + - - -

-First, let's enter your redirect URI. E.g. {props.sampleUrls.origin + 'Callback'} (replace the endpoint with yours). This is where Logto will redirect users after they sign in. -

- - + -Remember to keep the path `/Callback` in the URI as it's the default value for the Logto authentication middleware. + -To sign-in with Logto, you can use the `Challenge` method of `ControllerBase`: + -```csharp -Challenge(new AuthenticationProperties -{ - // The URI below is different from the redirect URI you entered above. - // It's the URI where users will be redirected after successfully signed in. - // You can change it to any path you want. - RedirectUri = "/" -}); -``` + -For example, you can add the following code to the controller: +First, add actions methods to your `Controller`, for example: -```csharp +```csharp title="Controllers/HomeController.cs" public class HomeController : Controller { public IActionResult SignIn() { + // This will redirect the user to the Logto sign-in page. return Challenge(new AuthenticationProperties { RedirectUri = "/" }); } -} -``` - -And then add the following code to your View: - -```html -

Is authenticated: @User.Identity?.IsAuthenticated

-Sign in -``` - -
- - - -To clean up both ASP.NET session and Logto session, we can designate a post sign-out redierct URI. This is where Logto will redirect users after they sign out. - -

-For example, set the URI to {props.sampleUrls.origin + 'SignedOutCallback'} (replace the endpoint with yours): -

- - - -Remember to keep the path `/SignedOutCallback` in the URI as it's the default value for the Logto authentication middleware. - -To sign-out with Logto, you can use the `SignOut` method of `ControllerBase`: - -```csharp -SignOut(new AuthenticationProperties -{ - // The URI below is different from the post sign-out redirect URI you entered above. - // It's the URI where users will be redirected after successfully signed out. - // You can change it to any path you want. - RedirectUri = "/" -}); -``` - -The `SignOut` method will clear the authentication cookie and redirect the user to the Logto sign-out page. -For example, you can add the following code to your controller: - -```csharp -public class HomeController : Controller -{ - // ... // Use the `new` keyword to avoid conflict with the `ControllerBase.SignOut` method new public IActionResult SignOut() { + // This will clear the authentication cookie and redirect the user to the Logto sign-out page + // to clear the Logto session as well. return SignOut(new AuthenticationProperties { RedirectUri = "/" }); } } ``` -Then, update the form on your View: +Then, add the links to your View: -```html +```cshtml title="Views/Home/Index.cshtml"

Is authenticated: @User.Identity?.IsAuthenticated

-@if (User.Identity?.IsAuthenticated == true) -{ +@if (User.Identity?.IsAuthenticated == true) { Sign out } else { Sign in @@ -151,29 +76,13 @@ It will show the "Sign in" link if the user is not authenticated, and show the " -Now you can run the web application and try to sign in and sign out with Logto: - -1. Open the web application in your browser, you should see "Is authenticated: False" and the "Sign in" link. -2. Click the "Sign in" link, and you should be redirected to the Logto sign-in page. -3. After you have signed in, you should be redirected back to the web application, and you should see "Is authenticated: True" and the "Sign out" link. -4. Click the "Sign out" link, and you should be redirected to the Logto sign-out page, and then redirected back to the web application. + - - -To know if the user is authenticated, you can check the `User.Identity?.IsAuthenticated` property. - -To get the user profile claims, you can use the `User.Claims` property: - -```csharp -var claims = User.Claims; - -// Get the user ID -var userId = claims.FirstOrDefault(c => c.Type == LogtoParameters.Claims.Subject)?.Value; -``` + -See the [full tutorial](https://docs.logto.io/quick-starts/dotnet-core/mvc/) for more details. + 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 new file mode 100644 index 00000000000..34b1fe74690 --- /dev/null +++ b/packages/console/src/assets/docs/guides/web-dotnet-core-mvc/fragments/_add-authentication.mdx @@ -0,0 +1,23 @@ +Open `Startup.cs` (or `Program.cs`) and add the following code to register Logto authentication middleware: + + +{`using Logto.AspNetCore.Authentication; + +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddLogtoAuthentication(options => +{ + options.Endpoint = "${props.endpoint}"; + options.AppId = "${props.app.id}"; + options.AppSecret = "${props.app.secret}"; +}); + +app.UseAuthentication();`} + + +The `AddLogtoAuthentication` method will do the following things: + +- Set the default authentication scheme to `LogtoDefaults.CookieScheme`. +- Set the default challenge scheme to `LogtoDefaults.AuthenticationScheme`. +- Set the default sign-out scheme to `LogtoDefaults.AuthenticationScheme`. +- Add cookie and OpenID Connect authentication handlers to the authentication scheme. diff --git a/packages/console/src/assets/docs/guides/web-dotnet-core-mvc/fragments/_configure-redirect-uris.mdx b/packages/console/src/assets/docs/guides/web-dotnet-core-mvc/fragments/_configure-redirect-uris.mdx new file mode 100644 index 00000000000..6c1b0820946 --- /dev/null +++ b/packages/console/src/assets/docs/guides/web-dotnet-core-mvc/fragments/_configure-redirect-uris.mdx @@ -0,0 +1,28 @@ +import UriInputField from '@/mdx-components/UriInputField'; + +export const defaultBaseUrl = 'http://localhost:5000/'; + +First, let's configure the **Logto redirect URI**. For example, if your website is hosted on {defaultBaseUrl}, add {defaultBaseUrl + 'Callback'} as the redirect URI below: + + + +Now let's configure the **Logto post sign-out redirect URI**. For example, set the URI to {defaultBaseUrl + 'SignedOutCallback'}: + + + +#### Change the default paths + +The **Logto redirect URI** has a default path of `/Callback`, and the **Logto post sign-out redirect URI** has a default path of `/SignedOutCallback`. + +You can leave them as are if there's no special requirement. If you want to change it, you can set the `CallbackPath` and `SignedOutCallbackPath` property for `LogtoOptions`: + +```csharp title="Program.cs" +builder.Services.AddLogtoAuthentication(options => +{ + // Other configurations... + options.CallbackPath = "/Foo"; + options.SignedOutCallbackPath = "/Bar"; +}); +``` + +Remember to update the values in the redirect URI fields accordingly. diff --git a/packages/console/src/assets/docs/guides/web-dotnet-core-mvc/fragments/_display-user-information.md b/packages/console/src/assets/docs/guides/web-dotnet-core-mvc/fragments/_display-user-information.md new file mode 100644 index 00000000000..229c20ebe8f --- /dev/null +++ b/packages/console/src/assets/docs/guides/web-dotnet-core-mvc/fragments/_display-user-information.md @@ -0,0 +1,12 @@ +To know if the user is authenticated, you can check the `User.Identity?.IsAuthenticated` property. + +To get the user profile claims, you can use the `User.Claims` property: + +```csharp title="Controllers/HomeController.cs" +var claims = User.Claims; + +// Get the user ID +var userId = claims.FirstOrDefault(c => c.Type == LogtoParameters.Claims.Subject)?.Value; +``` + +See [`LogtoParameters.Claims`](https://github.com/logto-io/csharp/blob/master/src/Logto.AspNetCore.Authentication/LogtoParameters.cs) for the list of claim names and their meanings. diff --git a/packages/console/src/assets/docs/guides/web-dotnet-core-mvc/fragments/_installation.md b/packages/console/src/assets/docs/guides/web-dotnet-core-mvc/fragments/_installation.md new file mode 100644 index 00000000000..7037dce9bb0 --- /dev/null +++ b/packages/console/src/assets/docs/guides/web-dotnet-core-mvc/fragments/_installation.md @@ -0,0 +1,5 @@ +Install the Logto SDK to your project: + +```bash showLineNumbers={false} +dotnet add package Logto.AspNetCore.Authentication +``` diff --git a/packages/console/src/assets/docs/guides/web-dotnet-core-mvc/fragments/_sign-in-and-sign-out-flows.mdx b/packages/console/src/assets/docs/guides/web-dotnet-core-mvc/fragments/_sign-in-and-sign-out-flows.mdx new file mode 100644 index 00000000000..090a75062e7 --- /dev/null +++ b/packages/console/src/assets/docs/guides/web-dotnet-core-mvc/fragments/_sign-in-and-sign-out-flows.mdx @@ -0,0 +1,37 @@ +import RegardingRedirectBasedSignIn from '../../../fragments/_regarding-redirect-based-sign-in.md'; + +Before we proceed, there are two confusing terms in the .NET Core authentication middleware that we need to clarify: + +1. **CallbackPath**: The URI that Logto will redirect the user back to after the user has signed in (the "redirect URI" in Logto) +2. **RedirectUri**: The URI that will be redirected to after necessary actions have been taken in the Logto authentication middleware. + +The sign-in process can be illustrated as follows: + +```mermaid +graph LR + subgraph Your app + A + C + D + end + subgraph Logto + B + end + A(Sign-in path) -->|Redirect to| B(Logto) + B -->|Redirect to| C(CallbackPath) + C -->|Redirect to| D(RedirectUri) +``` + +
+ +Similarly, .NET Core also has **SignedOutCallbackPath** and **RedirectUri** for the sign-out flow. + +For the sack of clarity, we'll refer them as follows: + +| Term we use | .NET Core term | +| -------------------------------- | --------------------- | +| Logto redirect URI | CallbackPath | +| Logto post sign-out redirect URI | SignedOutCallbackPath | +| Application redirect URI | RedirectUri | + + diff --git a/packages/console/src/assets/docs/guides/web-dotnet-core-mvc/index.ts b/packages/console/src/assets/docs/guides/web-dotnet-core-mvc/index.ts index 315a8943e86..ac60c2b4048 100644 --- a/packages/console/src/assets/docs/guides/web-dotnet-core-mvc/index.ts +++ b/packages/console/src/assets/docs/guides/web-dotnet-core-mvc/index.ts @@ -10,10 +10,7 @@ const metadata: Readonly = Object.freeze({ repo: 'csharp', path: '/', }, - fullGuide: { - title: 'Full .NET Core (MVC) integration tutorial', - url: 'https://docs.logto.io/quick-starts/dotnet-core/mvc', - }, + fullGuide: 'dotnet-core/mvc', }); export default metadata; diff --git a/packages/console/src/assets/docs/guides/web-dotnet-core/README.mdx b/packages/console/src/assets/docs/guides/web-dotnet-core/README.mdx index 245a9fe3a51..dee9d255372 100644 --- a/packages/console/src/assets/docs/guides/web-dotnet-core/README.mdx +++ b/packages/console/src/assets/docs/guides/web-dotnet-core/README.mdx @@ -2,82 +2,46 @@ import UriInputField from '@/mdx-components/UriInputField'; import Steps from '@/mdx-components/Steps'; import Step from '@/mdx-components/Step'; - - - - -This tutorial will show you how to use Logto ASP.NET Core authentication middleware to protect your web application. +import SignInAndSignOutFlows from '../web-dotnet-core-mvc/fragments/_sign-in-and-sign-out-flows.mdx'; +import ConfigureRedirectUris from '../web-dotnet-core-mvc/fragments/_configure-redirect-uris.mdx'; +import Installation from '../web-dotnet-core-mvc/fragments/_installation.md'; +import AddAuthentication from '../web-dotnet-core-mvc/fragments/_add-authentication.mdx'; +import DisplayUserInformation from '../web-dotnet-core-mvc/fragments/_display-user-information.md'; +import Checkpoint from '../../fragments/_checkpoint.md'; -
    -
  • It assumes your website is hosted on {props.sampleUrls.origin}.
  • -
+ -### Installation + -```bash -dotnet add package Logto.AspNetCore.Authentication -``` + -Open `Startup.cs` (or `Program.cs`) and add the following code to register Logto authentication middleware: - -
-  
-{`using Logto.AspNetCore.Authentication;
+
 
-var builder = WebApplication.CreateBuilder(args);
-
-builder.Services.AddLogtoAuthentication(options =>
-{
-  options.Endpoint = "${props.endpoint}";
-  options.AppId = "${props.app.id}";
-  options.AppSecret = "${props.app.secret}";
-});
-
-app.UseAuthentication();`}
-  
-
+
-The `AddLogtoAuthentication` method will do the following things: + -- Set the default authentication scheme to `LogtoDefaults.CookieScheme`. -- Set the default challenge scheme to `LogtoDefaults.AuthenticationScheme`. -- Set the default sign-out scheme to `LogtoDefaults.AuthenticationScheme`. -- Add cookie and OpenID Connect authentication handlers to the authentication scheme. + - - -

-First, let's enter your redirect URI. E.g. {props.sampleUrls.origin + 'Callback'} (replace the endpoint with yours). This is where Logto will redirect users after they sign in. -

- - + -Remember to keep the path `/Callback` in the URI as it's the default value for the Logto authentication middleware. + -To sign-in with Logto, you can use the `ChallengeAsync` method: + -```csharp -await HttpContext.ChallengeAsync(new AuthenticationProperties -{ - // The URI below is different from the redirect URI you entered above. - // It's the URI where users will be redirected after successfully signed in. - // You can change it to any path you want. - RedirectUri = "/" -}); -``` + -For example, if you are using Razor Pages, you can add the following code to the `Index` page model: +First, add the handler methods to your `PageModel`, for example: -```csharp +```csharp title="Pages/Index.cshtml.cs" public class IndexModel : PageModel { - // ... public async Task OnPostSignInAsync() { await HttpContext.ChallengeAsync(new AuthenticationProperties @@ -85,52 +49,7 @@ public class IndexModel : PageModel RedirectUri = "/" }); } -} -``` - -And then add the following code to the `Index` page: - -```html -

Is authenticated: @User.Identity?.IsAuthenticated

-
- -
-``` -
- - - -To clean up both ASP.NET session and Logto session, we can designate a post sign-out redierct URI. This is where Logto will redirect users after they sign out. - -

-For example, set the URI to {props.sampleUrls.origin + 'SignedOutCallback'} (replace the endpoint with yours): -

- - - -Remember to keep the path `/SignedOutCallback` in the URI as it's the default value for the Logto authentication middleware. - -To sign-out with Logto, you can use the `SignOutAsync` method: - -```csharp -await HttpContext.SignOutAsync(new AuthenticationProperties -{ - // The URI below is different from the post sign-out redirect URI you entered above. - // It's the URI where users will be redirected after successfully signed out. - // You can change it to any path you want. - RedirectUri = "/" -}); -``` - -The `SignOutAsync` method will clear the authentication cookie and redirect the user to the Logto sign-out page. - -For example, if you are using Razor Pages, you can add the following code to the `Index` page model: - -```csharp -public class IndexModel : PageModel -{ - // ... public async Task OnPostSignOutAsync() { await HttpContext.SignOutAsync(new AuthenticationProperties @@ -141,13 +60,12 @@ public class IndexModel : PageModel } ``` -Then, update the form on your Razor page: +Then, add the buttons to your Razor page: -```html +```cshtml title="Pages/Index.cshtml"

Is authenticated: @User.Identity?.IsAuthenticated

- @if (User.Identity?.IsAuthenticated == true) - { + @if (User.Identity?.IsAuthenticated == true) { } else { @@ -161,29 +79,13 @@ It will show the "Sign in" button if the user is not authenticated, and show the -Now you can run the web application and try to sign in and sign out with Logto: - -1. Open the web application in your browser, you should see "Is authenticated: False" and the "Sign in" button. -2. Click the "Sign in" button, and you should be redirected to the Logto sign-in page. -3. After you have signed in, you should be redirected back to the web application, and you should see "Is authenticated: True" and the "Sign out" button. -4. Click the "Sign out" button, and you should be redirected to the Logto sign-out page, and then redirected back to the web application. + - - -To know if the user is authenticated, you can check the `User.Identity?.IsAuthenticated` property. - -To get the user profile claims, you can use the `User.Claims` property: - -```csharp -var claims = User.Claims; - -// Get the user ID -var userId = claims.FirstOrDefault(c => c.Type == LogtoParameters.Claims.Subject)?.Value; -``` + -See the [full tutorial](https://docs.logto.io/quick-starts/dotnet-core/razor/) for more details. + 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 a498ee67a05..4325a964589 100644 --- a/packages/console/src/assets/docs/guides/web-express/README.mdx +++ b/packages/console/src/assets/docs/guides/web-express/README.mdx @@ -1,167 +1,96 @@ import UriInputField from '@/mdx-components/UriInputField'; -import Tabs from '@mdx/components/Tabs'; -import TabItem from '@mdx/components/TabItem'; import InlineNotification from '@/ds-components/InlineNotification'; import { generateStandardSecret } from '@logto/shared/universal'; import Steps from '@/mdx-components/Steps'; import Step from '@/mdx-components/Step'; +import NpmLikeInstallation from '@/mdx-components/NpmLikeInstallation'; + +import Checkpoint from '../../fragments/_checkpoint.md'; +import RedirectUris from '../../fragments/_redirect-uris-web.mdx'; - - - -```bash -npm i @logto/express cookie-parser express-session -``` - - - - -```bash -yarn add @logto/express cookie-parser express-session -``` - - - - -```bash -pnpm add @logto/express cookie-parser express-session -``` + - - - - In the following steps, we assume your app is running on http://localhost:3000. - +Prepare configuration for the Logto client: -Import and initialize LogtoClient: + + {`import type { LogtoExpressConfig } from '@logto/express'; -
-  
-    {`import LogtoClient from '@logto/express';
-
-export const logtoClient = new LogtoClient({
+const config: LogtoExpressConfig = {
   endpoint: '${props.endpoint}',
   appId: '${props.app.id}',
   appSecret: '${props.app.secret}',
   baseUrl: 'http://localhost:3000', // Change to your own base URL
-});`}
-  
-
- -
- - +}; +`} + The SDK requires [express-session](https://www.npmjs.com/package/express-session) to be configured in prior. -
-  
+
     {`import cookieParser from 'cookie-parser';
 import session from 'express-session';
 
 app.use(cookieParser());
 app.use(session({ secret: '${generateStandardSecret()}', cookie: { maxAge: 14 * 24 * 60 * 60 } }));`}
-  
-
+
- - -### Configure Redirect URI - -First, let’s enter your redirect URI. E.g. `http://localhost:3000/api/logto/sign-in-callback`. - - - -### Prepare Logto routes - -Prepare routes to connect with Logto. + -Go back to your IDE/editor, use the following code to implement the API routes first: - -```ts -import { handleAuthRoutes } from '@logto/express'; - -app.use(handleAuthRoutes(config)); -``` - -This will create 3 routes automatically: +The SDK provides a helper function `handleAuthRoutes` to register 3 routes: 1. `/logto/sign-in`: Sign in with Logto. 2. `/logto/sign-in-callback`: Handle sign-in callback. 3. `/logto/sign-out`: Sign out with Logto. -### Implement sign-in +Add the following code to your app: -We're almost there! Now, create a sign-in button to redirect to the sign-in route on user click. +```ts title="app.ts" +import { handleAuthRoutes } from '@logto/express'; -```ts -app.get('/', (req, res) => { - res.setHeader('content-type', 'text/html'); - res.end(``); -}); +app.use(handleAuthRoutes(config)); ``` - -Calling `/logto/sign-out` will clear all the Logto data in memory and cookies if they exist. - -After signing out, it'll be great to redirect your user back to your website. Let's add `http://localhost:3000` as one of the Post Sign-out URIs in Admin Console (shows under Redirect URIs). + - + -In Logto SDK, you can use the `withLogto` middleware to get `req.user.isAuthenticated` to check the authentication status, if the user is signed in, the value will be `true`, otherwise, the value will be `false`. +With the routes registered, now let's implement the sign-in and sign-out buttons in the home page. We need to redirect the user to the sign-in or sign-out route when needed. To help with this, use `withLogto` to inject authentication status to `req.user`. -``ts +```ts title="app.ts" import { withLogto } from '@logto/express'; -app.use(withLogto(config)); -``` - -No, let's use this value to protect routes by creating a simple middleware: +app.get('/', withLogto(config), (req, res) => { + res.setHeader('content-type', 'text/html'); -```ts -const requireAuth = async (req: Request, res: Response, next: NextFunction) => { - if (!req.user.isAuthenticated) { - res.redirect('/logto/sign-in'); + if (req.user.isAuthenticated) { + res.end(`
Hello ${req.user.claims?.sub}, Sign Out
`); + } else { + res.end(''); } - - next(); -}; -``` - -And then use it in the route handler: - -```ts -app.get('/protected', requireAuth, (req, res) => { - res.end('protected resource'); }); ```
@@ -170,12 +99,7 @@ app.get('/protected', requireAuth, (req, res) => { title="Checkpoint: Test your application" > -Now, you can test your application: - -1. Run your application, you will see the sign-in button. -2. Click the sign-in button, and you will be redirected to the sign in route, and the SDK will then init the sign-in process and redirect to the Logto sign-in page. -3. After you signed in, you will be redirect back to your application and see the sign-out button. -4. Calling `/logto/sign-out` to sign-out. +
diff --git a/packages/console/src/assets/docs/guides/web-express/index.ts b/packages/console/src/assets/docs/guides/web-express/index.ts index 6c22e22b2e8..1f3ac22b3d0 100644 --- a/packages/console/src/assets/docs/guides/web-express/index.ts +++ b/packages/console/src/assets/docs/guides/web-express/index.ts @@ -11,10 +11,7 @@ const metadata: Readonly = Object.freeze({ repo: 'js', path: 'packages/express-sample', }, - fullGuide: { - title: 'Full Express SDK tutorial', - url: 'https://docs.logto.io/quick-starts/express', - }, + fullGuide: 'express', }); export default metadata; 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 3044cb21d9d..26e34567b4a 100644 --- a/packages/console/src/assets/docs/guides/web-go/README.mdx +++ b/packages/console/src/assets/docs/guides/web-go/README.mdx @@ -2,6 +2,8 @@ import UriInputField from '@/mdx-components/UriInputField'; import Steps from '@/mdx-components/Steps'; import Step from '@/mdx-components/Step'; import InlineNotification from '@/ds-components/InlineNotification'; +import RedirectUrisWeb, {defaultBaseUrl, defaultRedirectUri} from '../../fragments/_redirect-uris-web.mdx'; +import Checkpoint from '../../fragments/_checkpoint.md'; @@ -10,9 +12,8 @@ import InlineNotification from '@/ds-components/InlineNotification'; > - The following demonstration is built upon the Gin Web Framework. - You may also integrate Logto into other frameworks by taking the same steps. - We assume your app is running on http://localhost:8080 in this guide. + The following demonstration is built upon the Gin Web Framework. + You may also integrate Logto into other frameworks by taking the same steps. Execute in the project root directory: @@ -23,8 +24,7 @@ go get github.com/logto-io/go Add the `github.com/logto-io/go/client` package to your application code: -```go -// main.go +```go title="main.go" package main import ( @@ -38,15 +38,13 @@ func main() { router.GET("/", func(c *gin.Context) { c.String(200, "Hello Logto!") }) - router.Run(":8080") + router.Run(":3000") } ```
- + In traditional web applications, the user authentication information will be stored in the user session. @@ -60,8 +58,7 @@ Logto SDK provides a `Storage` interface, you can implement a `Storage` adapter The `Storage` type in the Logto SDK is as follows: -```go -// github.com/logto-io/client/storage.go +```go title="github.com/logto-io/client/storage.go" package client type Storage interface { @@ -74,7 +71,7 @@ We use [github.com/gin-contrib/sessions](https://github.com/gin-contrib/sessions Apply the middleware to the application, so that we can get the user session by the user request context in the route handler: -```go +```go title="main.go" package main import ( @@ -97,14 +94,13 @@ func main() { // ... ctx.String(200, "Hello Logto!") }) - router.Run(":8080") + router.Run(":3000") } ``` Create a `session_storage.go` file, define a `SessionStorage` and implement the Logto SDK's `Storage` interfaces: -```go -// session_storage.go +```go title="session_storage.go" package main import ( @@ -138,16 +134,12 @@ sessionStorage := &SessionStorage{session: session} - + First, create a Logto config: -
-  
-    {`// main.go
-func main() {
+
+{`func main() {
     // ...
 
     logtoConfig := &client.LogtoConfig{
@@ -158,13 +150,11 @@ func main() {
 
     // ...
 }`}
-  
-
+ Then, you can create a `LogtoClient` for each user request with the Logto config above: -```go -// main.go +```go title="main.go" func main() { // ... @@ -183,7 +173,7 @@ func main() { authState = "You are logged in to this website! :)" } - homePage := `

Hello Logto

` + + homePage := "

Hello Logto

" + "
" + authState + "
" ctx.Data(http.StatusOK, "text/html; charset=utf-8", []byte(homePage)) @@ -195,24 +185,18 @@ func main() {
- - -Before you start implementing the sign-in flow, you need to add a redirect URI in the Admin Console for your application. + -This allows Logto to redirect the user to the redirect URI after signing in. + -For example, if you add `http://localhost:8080/sign-in-callback` to your Redirect URI, Logto will redirect the user to the `/sign-in-callback` route of your application after signing in. + - + After the redirect URI is configured, we add a `sign-in` route to handle the sign-in request and also add an sign-in link on the home page: -
-  
-    {`//main.go
-func main() {
+
+    {`func main() {
     // ...
 
     // Add a link to perform a sign-in request on the home page
@@ -248,28 +232,24 @@ func main() {
 
     // ...
 }`}
-  
-
+ -Now, when your user visit `http://localhost:8080/sign-in`, the user will be redirected to the Logto sign-in page. +Now, when your user visit {defaultBaseUrl}sign-in, the user will be redirected to the Logto sign-in page.
- + When the user signs in successfully on the Logto sign-in page, Logto will redirect the user to the Redirect URI. -Assuming your Redirect URI is `http://localhost:8080/sign-in-callback`, then we will add the `/sign-in-callback` route to handle the callback after signing in. +Assuming your Redirect URI is {defaultRedirectUri}, then we will add the `/callback` route to handle the callback after signing in. -```go -// main.go +```go title="main.go" func main() { // ... // Add a route for handling sign-in callback requests - router.GET("/sign-in-callback", func(ctx *gin.Context) { + router.GET("/callback", func(ctx *gin.Context) { session := sessions.Default(ctx) logtoClient := client.NewLogtoClient( logtoConfig, @@ -294,22 +274,14 @@ func main() { - + Similar to the sign-in flow, when the user signs out, Logto will redirect the user to the post sign-out redirect URI. -Assuming that you add `http://localhost:8080` to the Post Sign-out Redirect URI filed, Logto will redirect the user to the home page after signing out. - - - Now, let's add the `sign-out` route to handle the sign-out request and also add a sign-out link on the home page: -
-  
-    {`//main.go
-func main() {
+
+    {`func main() {
     // ...
 
     // Add a link to perform a sign-out request on the home page
@@ -346,23 +318,40 @@ func main() {
 
     // ...
 }`}
-  
-
+ After the user makes a signing-out request, Logto will clear all user authentication information in the session.
- + + + + + + + -Now, you can test your application: +To display the user's information, you can use the `client.GetIdTokenClaims` method. For example, add a route: -1. Visit `http://localhost:8080`, you will see "You are not logged in to this website." message on the home page. -2. Click the "Sign In" link, you will be redirected to the Logto sign-in page. -3. Sign in with your Logto account, you will be redirected to the home page and see "You are logged in to this website!" message. -4. Click the "Sign Out" link, and your user authentication information will be cleared, and the home page will display "You are not logged in to this website." message again. +```go title="main.go" +func main() { + //... + + router.GET("/user-id-token-claims", func(ctx *gin.Context) { + session := sessions.Default(ctx) + logtoClient := client.NewLogtoClient(logtoConfig, &SessionStorage{session: session}) + + idTokenClaims, err := logtoClient.GetIdTokenClaims() + + if err != nil { + ctx.String(http.StatusOK, err.Error()) + } + + ctx.JSON(http.StatusOK, idTokenClaims) + }) +} +``` diff --git a/packages/console/src/assets/docs/guides/web-go/index.ts b/packages/console/src/assets/docs/guides/web-go/index.ts index 6bd10eae34c..e7190d0305b 100644 --- a/packages/console/src/assets/docs/guides/web-go/index.ts +++ b/packages/console/src/assets/docs/guides/web-go/index.ts @@ -11,10 +11,7 @@ const metadata: Readonly = Object.freeze({ repo: 'go', path: 'gin-sample', }, - fullGuide: { - title: 'Full Go SDK tutorial', - url: 'https://docs.logto.io/quick-starts/go', - }, + fullGuide: 'go', }); export default metadata; diff --git a/packages/console/src/assets/docs/guides/web-gpt-plugin/config.json b/packages/console/src/assets/docs/guides/web-gpt-plugin/config.json index cd39155a7cc..458b34b4578 100644 --- a/packages/console/src/assets/docs/guides/web-gpt-plugin/config.json +++ b/packages/console/src/assets/docs/guides/web-gpt-plugin/config.json @@ -1,3 +1,3 @@ { - "order": 1.5 + "order": 999 } diff --git a/packages/console/src/assets/docs/guides/web-gpt-plugin/index.ts b/packages/console/src/assets/docs/guides/web-gpt-plugin/index.ts index 154f6589a53..77a6a6d897a 100644 --- a/packages/console/src/assets/docs/guides/web-gpt-plugin/index.ts +++ b/packages/console/src/assets/docs/guides/web-gpt-plugin/index.ts @@ -6,7 +6,6 @@ const metadata: Readonly = Object.freeze({ name: 'ChatGPT plugin', description: 'Use Logto as an OAuth identity provider for ChatGPT plugins.', target: ApplicationType.Traditional, - isFeatured: true, }); export default metadata; 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 7019654e233..92625af90a4 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 @@ -2,23 +2,16 @@ import UriInputField from '@/mdx-components/UriInputField'; import Steps from '@/mdx-components/Steps'; import Step from '@/mdx-components/Step'; +import Checkpoint from '../../fragments/_checkpoint.md'; +import RedirectUrisWeb from '../../fragments/_redirect-uris-web.mdx'; + - This tutorial will show you how to integrate Logto into your Java Spring Boot web application. - -
    -
  • - The sample was created using the Spring Boot [securing web - starter](https://spring.io/guides/gs/securing-web). Following the instructions to bootstrap a - new web application. -
  • -
  • - The sample uses the [Spring Security - OAuth2](https://spring.io/guides/tutorials/spring-boot-oauth2) library to handle OIDC - authentication and integrate with Logto. -
  • -
+ +This tutorial will show you how to integrate Logto into your Java Spring Boot application. + +No official SDK is required to integrate Logto with your Java Spring Boot application. We will use the [Spring Security](https://spring.io/projects/spring-security) and [Spring Security OAuth2](https://spring.io/guides/tutorials/spring-boot-oauth2) libraries to handle the OIDC authentication flow with Logto. Before we begin, make sure you have went through the spring boot guides linked above. @@ -27,21 +20,21 @@ Before we begin, make sure you have went through the spring boot guides linked a Include the following dependencies in your `build.gradle` file: -```gradle +```groovy title="build.gradle" dependencies { implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' - implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-security' - implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' + implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' } ``` -The sample uses [gradle](https://spring.io/guides/gs/gradle) as the build tool. You can use +Our sample project uses [gradle](https://spring.io/guides/gs/gradle) as the build tool. You can use maven or any other build tool as well. The configurations might be slightly different. For maven, include the following dependencies in your `pom.xml` file: -```maven +```xml title="pom.xml" org.springframework.boot spring-boot-starter-thymeleaf @@ -67,8 +60,7 @@ For maven, include the following dependencies in your `pom.xml` file: Register your application with Logto to get the client credentials and IdP configurations. 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}
@@ -81,26 +73,29 @@ spring.security.oauth2.client.provider.logto.issuer-uri=${props.endpoint}oidc
 spring.security.oauth2.client.provider.logto.authorization-uri=${props.endpoint}oidc/auth
 spring.security.oauth2.client.provider.logto.jwk-set-uri=${props.endpoint}oidc/jwks
   `}
-  
-
+
-In order to redirect users back to your application after they sign in, you need to set the redirect URI using the `client.registration.logto.redirect-uri` property in the previous step. + - - -e.g. In our example, the redirect URI is `http://localhost:8080/login/oauth2/code/logto`. +Make sure the redirect URI in Logto matches the `redirect-uri` set in the `application.properties` file in the previous step. -#### Create a new class `WebSecurityConfig` in your project: +The `WebSecurityConfig` class will be used to configure the security settings for your application. It is the key class that will handle the authentication and authorization flow. Please check the [Spring Security documentation](https://spring.io/guides/topicals/spring-security-architecture) for more details. + +### Create a new class `WebSecurityConfig` in your project -```java +```java title="WebSecurityConfig.java" package com.example.securingweb; import org.springframework.context.annotation.Configuration; @@ -114,11 +109,11 @@ public class WebSecurityConfig { } ``` -#### Create a idTokenDecoderFactory bean to set the JWS algorithm to `ES384`: +### Create a idTokenDecoderFactory bean to set the JWS algorithm to `ES384` This is required because Logto uses ES384 as the default algorithm, we need to update the OidcIdTokenDecoderFactory to use the same algorithm. -```java +```java title="WebSecurityConfig.java" import org.springframework.context.annotation.Bean; import org.springframework.security.oauth2.client.oidc.authentication.OidcIdTokenDecoderFactory; import org.springframework.security.oauth2.client.registration.ClientRegistration; @@ -137,11 +132,11 @@ public class WebSecurityConfig { } ``` -#### Create a LoginSuccessHandler class to handle the login success event: +### Create a LoginSuccessHandler class to handle the login success event Redirect the user to the user page after successful login: -```java +```java title="LoginSuccessHandler.java" package com.example.securingweb; import java.io.IOException; @@ -162,11 +157,11 @@ public class CustomSuccessHandler implements AuthenticationSuccessHandler { } ``` -#### Create a LogoutSuccessHandler class to handle the logout success event: +### Create a LogoutSuccessHandler class to handle the logout success event Clear the session and redirect the user to the home page. -```java +```java title="LogoutSuccessHandler.java" package com.example.securingweb; import java.io.IOException; @@ -194,11 +189,11 @@ public class CustomLogoutHandler implements LogoutSuccessHandler { } ``` -#### Create a `securityFilterChain` bean to configure the security configuration: +#### Create a `securityFilterChain` bean to configure the security configuration Add the following code to complete the `WebSecurityConfig` class: -```java +```java title="WebSecurityConfig.java" import org.springframework.context.annotation.Bean; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.web.DefaultSecurityFilterChain; @@ -229,13 +224,12 @@ public class WebSecurityConfig { - + (You may skip this step if you already have a home page in your project) -HomeController.java: -```java +```java title="HomeController.java" package com.example.securingweb; import java.security.Principal; @@ -254,9 +248,7 @@ public class HomeController { This controller will redirect the user to the user page if the user is authenticated, otherwise, it will show the home page. -home.html: - -```html +```html title="resources/templates/home.html"

Welcome!

@@ -266,11 +258,11 @@ home.html:
- + Create a new controller to handle the user page: -```java +```java title="UserController.java" package com.example.securingweb; import java.security.Principal; @@ -306,9 +298,7 @@ public class UserController { Read the user information from the `OAuth2User` object and pass it to the `user.html` template. -user.html: - -```html +```html title="resources/templates/user.html"

User Details

@@ -327,4 +317,10 @@ user.html: + + + + + + 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 51cec13fd09..5c6196036df 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 @@ -1,54 +1,28 @@ -import UriInputField from '@/mdx-components/UriInputField'; -import Tabs from '@mdx/components/Tabs'; -import TabItem from '@mdx/components/TabItem'; -import InlineNotification from '@/ds-components/InlineNotification'; import { generateStandardSecret } from '@logto/shared/universal'; +import NpmLikeInstallation from '@/mdx-components/NpmLikeInstallation'; import Steps from '@/mdx-components/Steps'; import Step from '@/mdx-components/Step'; +import Checkpoint from '../../fragments/_checkpoint.md'; +import RedirectUrisWeb from '../../fragments/_redirect-uris-web.mdx'; - - -```bash -npm i @logto/next -``` - - - - -```bash -yarn add @logto/next -``` + - - - -```bash -pnpm add @logto/next -``` - - - - - In the following steps, we assume your app is running on http://localhost:3000. - - -Prepare configuration for the Logto client. Create a new file `app/logto.ts` and add the following code: +Prepare configuration for the Logto client: -
-  
+
     {`export const logtoConfig = {
   endpoint: '${props.endpoint}',
   appId: '${props.app.id}',
@@ -58,26 +32,25 @@ Prepare configuration for the Logto client. Create a new file `app/logto.ts` and
   cookieSecure: process.env.NODE_ENV === 'production',
 };
 `}
-  
-
+
- -### Configure Redirect URI - -First, let’s enter your redirect URI. E.g. `http://localhost:3000/callback`. + - + -### Implement callback page + -Add a callback page to your app: +Add a callback route to your app: -```tsx -// pages/callback/page.tsx +```tsx title="app/callback/route.ts" import { handleSignIn } from '@logto/next/server-actions'; import { redirect } from 'next/navigation'; import { NextRequest } from 'next/server'; @@ -91,12 +64,17 @@ export async function GET(request: NextRequest) { } ``` -### Implement sign-in button + + + + +### Implement sign-in and sign-out button -The sign-in button will call the method we just created, it is a client component: - -```tsx -// app/sign-in.tsx +In Next.js App Router, events are handled in client components, so we need to create two components first: `SignIn` and `SignOut`. + +```tsx title="app/sign-in.tsx" 'use client'; type Props = { @@ -118,53 +96,7 @@ const SignIn = ({ onSignIn }: Props) => { export default SignIn; ``` -### Add sign in button to home page - -We're almost there! Add this button to home page at `/app/page.tsx` and implement the `onSignIn` function: - -```tsx -import { signIn } from '@logto/next/server-actions'; -import SignIn from './sign-in'; -import { logtoConfig } from './logto'; - -export default async function Home() { - return ( -
-

Hello Logto.

-
- { - 'use server'; - - await signIn(logtoConfig); - }} - /> -
-
- ); -} -``` - -Now you will be navigated to Logto sign-in page when you click the button. - -
- - - -### Configure URI - -After signing out, it'll be great to redirect user back to your website. Let's add `http://localhost:3000` as the Post Sign-out URI below. - - - -### Implement a sign-out button - -The sign-out button is also a client component, so we will create it in `/app/sign-out.tsx`: - -```tsx -// app/sign-out.tsx +```tsx title="app/sign-out.tsx" 'use client'; type Props = { @@ -186,63 +118,26 @@ const SignOut = ({ onSignOut }: Props) => { export default SignOut; ``` -### Add sign out button to home page +Remember to add `'use client'` to the top of the file to indicate that these components are client components. -Then add the sign-out button to the home page in `/app/page.tsx`: +### Add buttons to home page -```tsx -import { signIn, signOut } from '@logto/next/server-actions'; -import SignIn from './sign-in'; -import SignOut from './sign-out'; -import { logtoConfig } from './logto'; - -export default async function Home() { - return ( -
-

Hello Logto.

-
- { - 'use server'; - - await signOut(logtoConfig); - }} - /> - { - 'use server'; - - await signIn(logtoConfig); - }} - /> -
-
- ); -} -``` +Now let's add the sign-in and sign-out buttons in your hoem page. We need to call the server actions in SDK when needed. To help with this, use `getLogtoContext` to fetch authentication status. -
- - - -We can call the function `getLogtoContext` to get context as the authentication state in pages, let's modify the home page: - -```tsx +```tsx title="app/page.tsx" import { getLogtoContext, signIn, signOut } from '@logto/next/server-actions'; import SignIn from './sign-in'; import SignOut from './sign-out'; import { logtoConfig } from './logto'; -export default async function Home() { - const { isAuthenticated } = await getLogtoContext(logtoConfig); +const Home = () => { + const { isAuthenticated, claims } = await getLogtoContext(logtoConfig); return ( -
-

Hello Logto.

-
- {isAuthenticated ? ( +
-
+

+ )} + ); -} +}; + +export default Home; ```
-
\ No newline at end of file + + + + + + + diff --git a/packages/console/src/assets/docs/guides/web-next-app-router/index.ts b/packages/console/src/assets/docs/guides/web-next-app-router/index.ts index 01aa121548f..492ee05158f 100644 --- a/packages/console/src/assets/docs/guides/web-next-app-router/index.ts +++ b/packages/console/src/assets/docs/guides/web-next-app-router/index.ts @@ -11,10 +11,7 @@ const metadata: Readonly = Object.freeze({ path: 'packages/next-server-actions-sample', }, isFeatured: true, - fullGuide: { - title: 'Full Next.js SDK tutorial', - url: 'https://docs.logto.io/sdk/next-app-router/', - }, + fullGuide: 'next-app-router', }); export default metadata; 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 baeeff2600b..badcf966507 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 @@ -31,8 +31,7 @@ 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';
 
 const handler = NextAuth({
@@ -64,8 +63,7 @@ const handler = NextAuth({
 });
 
 export { handler as GET, handler as POST };`}
-  
-
+ 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 0313e9dddfd..961465f691c 100644 --- a/packages/console/src/assets/docs/guides/web-next/README.mdx +++ b/packages/console/src/assets/docs/guides/web-next/README.mdx @@ -1,88 +1,58 @@ -import UriInputField from '@/mdx-components/UriInputField'; -import Tabs from '@mdx/components/Tabs'; -import TabItem from '@mdx/components/TabItem'; -import InlineNotification from '@/ds-components/InlineNotification'; +import NpmLikeInstallation from '@/mdx-components/NpmLikeInstallation'; import { generateStandardSecret } from '@logto/shared/universal'; import Steps from '@/mdx-components/Steps'; import Step from '@/mdx-components/Step'; +import Checkpoint from '../../fragments/_checkpoint.md'; +import RedirectUrisWeb, { defaultBaseUrl } from '../../fragments/_redirect-uris-web.mdx'; - - -```bash -npm i @logto/next -``` - - - - -```bash -yarn add @logto/next -``` - - - - -```bash -pnpm add @logto/next -``` + - - - - In the following steps, we assume your app is running on http://localhost:3000. - - Import and initialize LogtoClient: -
-  
-    {`// libraries/logto.js
-import LogtoClient from '@logto/next';
+
+    {`import LogtoClient from '@logto/next';
 
 export const logtoClient = new LogtoClient({
   endpoint: '${props.endpoint}',
   appId: '${props.app.id}',
   appSecret: '${props.app.secret}',
-  baseUrl: 'http://localhost:3000', // Change to your own base URL
+  baseUrl: '${defaultBaseUrl}', // Change to your own base URL
   cookieSecret: '${generateStandardSecret()}', // Auto-generated 32 digit secret
   cookieSecure: process.env.NODE_ENV === 'production',
 });`}
-  
-
+
- -### Configure Redirect URI - -First, let’s enter your redirect URI. E.g. `http://localhost:3000/api/logto/sign-in-callback`. + - + -### Prepare API routes + Prepare [API routes](https://nextjs.org/docs/api-routes/introduction) to connect with Logto. Go back to your IDE/editor, use the following code to implement the API routes first: -```ts -// pages/api/logto/[action].ts +```ts title="pages/api/logto/[action].ts" import { logtoClient } from '../../../libraries/logto'; export default logtoClient.handleAuthRoutes(); @@ -91,126 +61,65 @@ export default logtoClient.handleAuthRoutes(); This will create 4 routes automatically: 1. `/api/logto/sign-in`: Sign in with Logto. -2. `/api/logto/sign-in-callback`: Handle sign-in callback. +2. `/api/logto/sign-in-callback`: Handle sign-in callback (redirect URI). 3. `/api/logto/sign-out`: Sign out with Logto. 4. `/api/logto/user`: Check if user is authenticated with Logto, if yes, return user info. -### Implement sign-in button - -We're almost there! In the last step, we will create a sign-in button: - -```tsx -import { useRouter } from 'next/router'; - -const { push } = useRouter(); - -; -``` - -Now you will be navigated to Logto sign-in page when you click the button. - -Calling `/api/logto/sign-out` will clear all the Logto data in memory and cookies if they exist. - -After signing out, it'll be great to redirect user back to your website. Let's add `http://localhost:3000` as the Post Sign-out URI below before calling `/api/logto/sign-out`. - - +We have prepared the API routes, now let's implement the sign-in and sign-out buttons in your home page. We need to redirect the user to the sign-in or sign-out route when needed. To help with this, use `useSWR` to fetch authentication status from `/api/logto/user`. -### Implement a sign-out button - -```tsx - -``` - - - - - -### Through API request in the frontend - -You can fetch user info by calling `/api/logto/user`. +Check [this guide](https://swr.vercel.app/docs/getting-started) to learn more about `useSWR`. -```tsx -import { LogtoUser } from '@logto/next'; +```tsx title="/pages/index.tsx" +import { type LogtoContext } from '@logto/next'; import useSWR from 'swr'; const Home = () => { - const { data } = useSWR('/api/logto/user'); - - return
User ID: {data?.claims?.sub}
; + const { data } = useSWR('/api/logto/user'); + + return ( + + ); }; -export default Profile; +export default Home; ``` -Check [this guide](https://swr.vercel.app/docs/getting-started) to learn more about `useSWR`. - -### Through `getServerSideProps` in the backend - -```tsx -import { LogtoUser } from '@logto/next'; -import { logtoClient } from '../libraries/logto'; - -type Props = { - user: LogtoUser; -}; - -const Profile = ({ user }: Props) => { - return
User ID: {user.claims?.sub}
; -}; - -export default Profile; - -export const getServerSideProps = logtoClient.withLogtoSsr(({ request }) => { - const { user } = request; - - return { - props: { user }, - }; -}); -``` - -Check [Next.js documentation](https://nextjs.org/docs/basic-features/data-fetching/get-server-side-props) for more details on `getServerSideProps`. - -### Protect API routes - -Wrap your handler with `logtoClient.withLogtoApiRoute`. - -```ts -// pages/api/protected-resource.ts -import { logtoClient } from '../../libraries/logto'; - -export default logtoClient.withLogtoApiRoute((request, response) => { - if (!request.user.isAuthenticated) { - response.status(401).json({ message: 'Unauthorized' }); - - return; - } - - response.json({ - data: 'this_is_protected_resource', - }); -}); -```
-Now, you can test your application: - -1. Run your application, you will see the sign-in button. -2. Click the sign-in button, and you will be redirected to the sign in route, and the SDK will then init the sign-in process and redirect to the Logto sign-in page. -3. After you signed in, you will be redirect back to your application and see user id and the sign-out button. -4. Click the sign-out button to sign-out. + diff --git a/packages/console/src/assets/docs/guides/web-next/index.ts b/packages/console/src/assets/docs/guides/web-next/index.ts index 703b36bae68..7e60ff1e724 100644 --- a/packages/console/src/assets/docs/guides/web-next/index.ts +++ b/packages/console/src/assets/docs/guides/web-next/index.ts @@ -3,7 +3,7 @@ import { ApplicationType } from '@logto/schemas'; import { type GuideMetadata } from '../types'; const metadata: Readonly = Object.freeze({ - name: 'Next.js', + name: 'Next.js (Page Router)', description: 'Next.js is a React framework for production - it makes building fullstack React apps a breeze and ships with built-in SSR.', target: ApplicationType.Traditional, @@ -11,10 +11,7 @@ const metadata: Readonly = Object.freeze({ repo: 'js', path: 'packages/next-sample', }, - fullGuide: { - title: 'Full Next.js SDK tutorial', - url: 'https://docs.logto.io/quick-starts/next', - }, + fullGuide: 'next', }); export default metadata; 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 3ce9c63ca14..b274d4849e7 100644 --- a/packages/console/src/assets/docs/guides/web-nuxt/README.mdx +++ b/packages/console/src/assets/docs/guides/web-nuxt/README.mdx @@ -1,65 +1,35 @@ import UriInputField from '@/mdx-components/UriInputField'; -import Tabs from '@mdx/components/Tabs'; -import TabItem from '@mdx/components/TabItem'; +import NpmLikeInstallation from '@/mdx-components/NpmLikeInstallation'; import InlineNotification from '@/ds-components/InlineNotification'; import Steps from '@/mdx-components/Steps'; import Step from '@/mdx-components/Step'; import Checkpoint from '../../fragments/_checkpoint.md'; +import RedirectUrisWeb from '../../fragments/_redirect-uris-web.mdx'; import { generateStandardSecret } from '@logto/shared/universal'; export const cookieEncryptionKey = generateStandardSecret(); - - Logto Nuxt SDK only works with Nuxt 3. - - - - - -```bash -npm i @logto/nuxt -``` - - - - -```bash -yarn add @logto/nuxt -``` - - + + Logto Nuxt SDK only works with Nuxt 3. + -```bash -pnpm add @logto/nuxt -``` + - - -In your Nuxt config file (`next.config.ts`), add the Logto module: +In your Nuxt config file, add the Logto module and configure it: -```ts -export default defineNuxtConfig({ - modules: ['@logto/nuxt'], - // ...other configurations -}); -``` - -The minimal configuration for the module is as follows: - -
-  
-    {`export default defineNuxtConfig({
+
+{`export default defineNuxtConfig({
   modules: ['@logto/nuxt'],
   runtimeConfig: {
     logto: {
@@ -71,38 +41,28 @@ The minimal configuration for the module is as follows:
   },
   // ...other configurations
 });`}
-  
-
+ Since these information are sensitive, it's recommended to use environment variables (`.env`): -
-  
+
     {`NUXT_LOGTO_ENDPOINT=${props.endpoint}
 NUXT_LOGTO_APP_ID=${props.app.id}
 NUXT_LOGTO_APP_SECRET=${props.app.secret}
 NUXT_LOGTO_COOKIE_ENCRYPTION_KEY=${cookieEncryptionKey} # Random-generated
 `}
-  
-
+ See [runtime config](https://nuxt.com/docs/guide/going-further/runtime-config) for more information.
- - - - In the following steps, we assume your app is running on http://localhost:3000. - - -First, let's enter your redirect URI. E.g. `http://localhost:3000/callback`. [Redirect URI](https://www.oauth.com/oauth2-servers/redirect-uris/) is an OAuth 2.0 concept which implies the location should redirect after authentication. - - - -After signing out, it'll be great to redirect user back to your website. For example, add `http://localhost:3000` as the post sign-out redirect URI below. + - + When registering `@logto/nuxt` module, it will do the following: @@ -111,7 +71,7 @@ When registering `@logto/nuxt` module, it will do the following: These routes are configurable via `logto.pathnames` in the module options, for example: -```ts +```ts title="nuxt.config.ts" export default defineNuxtConfig({ logto: { pathnames: { @@ -126,32 +86,24 @@ export default defineNuxtConfig({ Check out the [type definition file](https://github.com/logto-io/js/blob/HEAD/packages/nuxt/src/runtime/utils/types.ts) in the `@logto/nuxt` package for more information. -Note: If you configure the callback route to a different path, you need to update the redirect URI in Logto accordingly. + +If you configure the callback route to a different path, you need to update the redirect URI in Logto accordingly. + -Since Nuxt pages will be hydrated and become a single-page application (SPA) after the initial load, we need to redirect the user to the sign-in or sign-out route when needed. - -```html -Sign in -
-Sign out -``` - -
- - - -To display the user's information, you can use the `useLogtoUser()` composable, which is available on both server and client side: +Since Nuxt pages will be hydrated and become a single-page application (SPA) after the initial load, we need to redirect the user to the sign-in or sign-out route when needed. To help with this, our SDK provides the `useLogtoUser()` composable, which can be used in both server and client side. -```html +```html title="index.vue"