Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 32 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ Base types and interfaces for CQRS client implementations.

CQRS client with Axios for HTTP communication and type-safe command/query handling.

#### `@leancodepl/fetch-client`

Fetch client for CQRS commands and queries.

#### `@leancodepl/react-query-cqrs-client`

TanStack Query CQRS client with hooks for queries, operations, and commands with built-in caching.
Expand Down Expand Up @@ -94,6 +98,14 @@ Dependency cruiser configuration for enforcing folder structure rules and cross-

ESLint flat config resolver for merging configuration plugins.

#### `@leancodepl/eslint-plugin`

ESLint plugin for LeanCode projects.

#### `@leancodepl/nx-plugins`

Collection of Nx plugins for LeanCode projects.

### UI & Styling

#### `@leancodepl/styled-tools`
Expand All @@ -112,7 +124,8 @@ React client for feature flag management using OpenFeature standard.

#### `@leancodepl/openfeature-posthog-provider`

OpenFeature provider that delegates feature flag evaluation to PostHog. Use PostHog feature flags through the OpenFeature standard API.
OpenFeature provider that delegates feature flag evaluation to PostHog. Use PostHog feature flags through the
OpenFeature standard API.

### Analytics

Expand Down Expand Up @@ -140,12 +153,30 @@ a preset:
- **`@leancodepl/logger/nest`** – NestJS `LoggerService` adapter: `createNestJsonLogger()` for drop-in use in Nest apps
with JSON output.

### Translation & Internationalization

#### `@leancodepl/intl`

Command-line tool for managing FormatJS translations with POEditor integration.

#### `@leancodepl/mail-translation`

Command-line tool for processing MJML and plaintext email templates with optional internationalization support.

### Utilities

#### `@leancodepl/utils`

Utility library for common development tasks including assertions, transformations, and React hooks.

#### `@leancodepl/config`

Utility for creating configuration getters that access Vite-injected values in development and environment variables in production.

#### `@leancodepl/force-update`

Library for implementing force update functionality in web applications.

#### `@leancodepl/validation`

Validation utilities for handling API responses and error management with custom error codes.
Expand Down
58 changes: 38 additions & 20 deletions examples/example/src/routes/login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,22 @@ import { useCallback } from "react"
import { dataTestIds } from "@example/e2e-ids"
import { createFileRoute, useNavigate } from "@tanstack/react-router"
import { z } from "zod"
import { loginFlow, verificationFlow } from "@leancodepl/kratos"
import type {
GetFlowErrorHandler,
GetLoginChooseMethodFormProps,
GetLoginSecondFactorEmailFormProps,
GetLoginSecondFactorFormProps,
GetVerificationEmailVerificationFormProps,
} from "@leancodepl/kratos"
import { Input } from "../components/Input"
import { useRemoveFlowFromUrl } from "../hooks/useRemoveFlowFromUrl"
import { getErrorMessage, LoginFlow, type OidcProvidersConfig, sessionManager } from "../services/kratos"
import { getErrorMessage, LoginFlow, sessionManager, VerificationFlow } from "../services/kratos"

const loginSearchSchema = z.object({
flow: z.string().optional(),
})

const handleError: loginFlow.OnLoginFlowError = ({ target, errors }) => {
const handleError: GetFlowErrorHandler<typeof LoginFlow> = ({ target, errors }) => {
if (target === "root") {
alert(`Błędy formularza: ${errors.map(e => e.id).join(", ")}`)
} else {
Expand Down Expand Up @@ -62,8 +68,14 @@ function Loader() {
return <p>Loading login methods...</p>
}

function ChooseMethodForm(props: loginFlow.ChooseMethodFormProps<OidcProvidersConfig>) {
const { errors, isSubmitting, isValidating, oidcProviders: { Google, Apple, Facebook }, isRefresh } = props
function ChooseMethodForm(props: GetLoginChooseMethodFormProps<typeof LoginFlow>) {
const {
errors,
isSubmitting,
isValidating,
oidcProviders: { Google, Apple, Facebook },
isRefresh,
} = props

if (isRefresh) {
const { passwordFields, Passkey, identifier } = props
Expand Down Expand Up @@ -124,13 +136,15 @@ function ChooseMethodForm(props: loginFlow.ChooseMethodFormProps<OidcProvidersCo
</Facebook>
)}

<Passkey>
<button
data-testid={dataTestIds.login.chooseMethodForm.passkeyButton}
disabled={isSubmitting || isValidating}>
Sign in with Passkey
</button>
</Passkey>
{Passkey && (
<Passkey>
<button
data-testid={dataTestIds.login.chooseMethodForm.passkeyButton}
disabled={isSubmitting || isValidating}>
Sign in with Passkey
</button>
</Passkey>
)}

{errors && errors.length > 0 && (
<div data-testid={dataTestIds.common.errors}>
Expand Down Expand Up @@ -205,11 +219,15 @@ function ChooseMethodForm(props: loginFlow.ChooseMethodFormProps<OidcProvidersCo
</Facebook>
)}

<Passkey>
<button data-testid={dataTestIds.login.chooseMethodForm.passkeyButton} disabled={isSubmitting || isValidating}>
Sign in with Passkey
</button>
</Passkey>
{Passkey && (
<Passkey>
<button
data-testid={dataTestIds.login.chooseMethodForm.passkeyButton}
disabled={isSubmitting || isValidating}>
Sign in with Passkey
</button>
</Passkey>
)}

{errors && errors.length > 0 && (
<div data-testid={dataTestIds.common.errors}>
Expand All @@ -229,7 +247,7 @@ function SecondFactorForm({
errors,
isSubmitting,
isValidating,
}: loginFlow.SecondFactorFormProps) {
}: GetLoginSecondFactorFormProps<typeof LoginFlow>) {
return (
<div data-testid={dataTestIds.login.secondFactorForm.wrapper}>
<Totp>
Expand Down Expand Up @@ -274,7 +292,7 @@ function SecondFactorEmailForm({
errors,
isSubmitting,
isValidating,
}: loginFlow.SecondFactorEmailFormProps) {
}: GetLoginSecondFactorEmailFormProps<typeof LoginFlow>) {
return (
<div data-testid={dataTestIds.login.secondFactorEmailForm.wrapper}>
<Code>
Expand Down Expand Up @@ -317,7 +335,7 @@ function EmailVerificationForm({
errors,
isSubmitting,
isValidating,
}: verificationFlow.EmailVerificationFormProps) {
}: GetVerificationEmailVerificationFormProps<typeof VerificationFlow>) {
return (
<div data-testid={dataTestIds.login.emailVerificationForm.wrapper}>
<div>
Expand Down
21 changes: 16 additions & 5 deletions examples/example/src/routes/recovery.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { dataTestIds } from "@example/e2e-ids"
import { createFileRoute } from "@tanstack/react-router"
import { z } from "zod"
import { recoveryFlow, settingsFlow } from "@leancodepl/kratos"
import {
GetFlowErrorHandler,
GetRecoveryCodeFormProps,
GetRecoveryEmailFormProps,
GetRecoveryNewPasswordFormProps,
} from "@leancodepl/kratos"
import { Input } from "../components/Input"
import { useRemoveFlowFromUrl } from "../hooks/useRemoveFlowFromUrl"
import { getErrorMessage, RecoveryFlow } from "../services/kratos"
Expand All @@ -10,7 +15,7 @@ const recoverySearchSchema = z.object({
flow: z.string().optional(),
})

const handleError: recoveryFlow.OnRecoveryFlowError = ({ target, errors }) => {
const handleError: GetFlowErrorHandler<typeof RecoveryFlow> = ({ target, errors }) => {
if (target === "root") {
alert(`Błędy formularza: ${errors.map(e => e.id).join(", ")}`)
} else {
Expand Down Expand Up @@ -45,7 +50,13 @@ function RouteComponent() {
)
}

function EmailForm({ errors, Email, Submit, isSubmitting, isValidating }: recoveryFlow.EmailFormProps) {
function EmailForm({
errors,
Email,
Submit,
isSubmitting,
isValidating,
}: GetRecoveryEmailFormProps<typeof RecoveryFlow>) {
return (
<div data-testid={dataTestIds.recovery.emailForm.wrapper}>
<Email>
Expand Down Expand Up @@ -73,7 +84,7 @@ function EmailForm({ errors, Email, Submit, isSubmitting, isValidating }: recove
)
}

function CodeForm({ errors, Code, Submit, isSubmitting, isValidating }: recoveryFlow.CodeFormProps) {
function CodeForm({ errors, Code, Submit, isSubmitting, isValidating }: GetRecoveryCodeFormProps<typeof RecoveryFlow>) {
return (
<div data-testid={dataTestIds.recovery.codeForm.wrapper}>
<p>Please enter the code you received in the email.</p>
Expand Down Expand Up @@ -110,7 +121,7 @@ function NewPasswordForm({
Submit,
isSubmitting,
isValidating,
}: settingsFlow.NewPasswordFormProps) {
}: GetRecoveryNewPasswordFormProps<typeof RecoveryFlow>) {
return (
<div data-testid={dataTestIds.recovery.newPasswordForm.wrapper}>
<Password>
Expand Down
21 changes: 14 additions & 7 deletions examples/example/src/routes/registration.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import { dataTestIds } from "@example/e2e-ids"
import { createFileRoute } from "@tanstack/react-router"
import { z } from "zod"
import { registrationFlow, verificationFlow } from "@leancodepl/kratos"
import type { AuthTraitsConfig, OidcProvidersConfig } from "../services/kratos"
import type {
GetFlowErrorHandler,
GetRegistrationChooseMethodFormProps,
GetRegistrationTraitsFormProps,
GetVerificationEmailVerificationFormProps,
} from "@leancodepl/kratos"
import type { VerificationFlow } from "../services/kratos"
import { Checkbox } from "../components/Checkbox"
import { Input } from "../components/Input"
import { useRemoveFlowFromUrl } from "../hooks/useRemoveFlowFromUrl"
Expand All @@ -12,7 +17,7 @@ const registrationSearchSchema = z.object({
flow: z.string().optional(),
})

const handleError: registrationFlow.OnRegistrationFlowError<AuthTraitsConfig> = ({ target, errors }) => {
const handleError: GetFlowErrorHandler<typeof RegistrationFlow> = ({ target, errors }) => {
if (target === "root") {
alert(`Błędy formularza: ${errors.map(e => e.id).join(", ")}`)
} else {
Expand Down Expand Up @@ -65,7 +70,7 @@ function TraitsForm({
traitFields: { Email, GivenName, RegulationsAccepted, Submit },
isSubmitting,
isValidating,
}: registrationFlow.TraitsFormProps<AuthTraitsConfig, OidcProvidersConfig>) {
}: GetRegistrationTraitsFormProps<typeof RegistrationFlow>) {
return (
<div data-testid={dataTestIds.registration.traitsForm.wrapper}>
<Email>
Expand Down Expand Up @@ -101,7 +106,9 @@ function TraitsForm({

{Google && (
<Google>
<button data-testid={dataTestIds.registration.traitsForm.googleButton} disabled={isSubmitting || isValidating}>
<button
data-testid={dataTestIds.registration.traitsForm.googleButton}
disabled={isSubmitting || isValidating}>
Sign up with Google
</button>
</Google>
Expand Down Expand Up @@ -143,7 +150,7 @@ function ChooseMethodForm({
passwordFields: { Password, PasswordConfirmation, Submit },
isSubmitting,
isValidating,
}: registrationFlow.ChooseMethodFormProps) {
}: GetRegistrationChooseMethodFormProps<typeof RegistrationFlow>) {
return (
<div data-testid={dataTestIds.registration.chooseMethodForm.wrapper}>
<ReturnToTraitsForm>
Expand Down Expand Up @@ -202,7 +209,7 @@ function EmailVerificationForm({
errors,
isSubmitting,
isValidating,
}: verificationFlow.EmailVerificationFormProps) {
}: GetVerificationEmailVerificationFormProps<typeof VerificationFlow>) {
return (
<div data-testid={dataTestIds.registration.emailVerificationForm.wrapper}>
<Code>
Expand Down
32 changes: 23 additions & 9 deletions examples/example/src/routes/settings.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,25 @@
import { dataTestIds } from "@example/e2e-ids"
import { createFileRoute } from "@tanstack/react-router"
import { z } from "zod"
import { settingsFlow } from "@leancodepl/kratos"
import {
GetFlowErrorHandler,
GetSettingsFormProps,
GetSettingsNewPasswordFormProps,
GetSettingsOidcFormProps,
GetSettingsPasskeysFormProps,
GetSettingsTotpFormProps,
GetSettingsTraitsFormProps,
settingsFlow,
} from "@leancodepl/kratos"
import { Input } from "../components/Input"
import { useRemoveFlowFromUrl } from "../hooks/useRemoveFlowFromUrl"
import { AuthTraitsConfig, getErrorMessage, type OidcProvidersConfig, sessionManager, SettingsFlow } from "../services/kratos"
import { getErrorMessage, sessionManager, SettingsFlow } from "../services/kratos"

const settingsSearchSchema = z.object({
flow: z.string().optional(),
})

const handleError: settingsFlow.OnSettingsFlowError<AuthTraitsConfig> = ({ target, errors }) => {
const handleError: GetFlowErrorHandler<typeof SettingsFlow> = ({ target, errors }) => {
if (target === "root") {
alert(`Błędy formularza: ${errors.map(e => e.id).join(", ")}`)
} else {
Expand Down Expand Up @@ -67,7 +76,7 @@ function SettingsForm({
passkeysForm,
totpForm,
oidcForm,
}: settingsFlow.SettingsFormProps) {
}: GetSettingsFormProps<typeof SettingsFlow>) {
if (isLoading) {
return <div>Loading settings form...</div>
}
Expand All @@ -92,7 +101,7 @@ function TraitsForm({
isSubmitting,
isValidating,
emailVerificationRequired,
}: settingsFlow.TraitsFormProps<AuthTraitsConfig>) {
}: GetSettingsTraitsFormProps<typeof SettingsFlow>) {
if (isLoading) {
return <p data-testid={dataTestIds.settings.traitsForm.loading}>Loading traits form...</p>
}
Expand Down Expand Up @@ -149,7 +158,7 @@ function NewPasswordForm({
isLoading,
isSubmitting,
isValidating,
}: settingsFlow.NewPasswordFormProps) {
}: GetSettingsNewPasswordFormProps<typeof SettingsFlow>) {
if (isLoading) {
return <p data-testid={dataTestIds.settings.newPasswordForm.loading}>Loading new password form...</p>
}
Expand Down Expand Up @@ -191,7 +200,12 @@ function NewPasswordForm({
)
}

function PasskeysForm({ addNewPasskey, existingPasskeys, isPending, isLoading }: settingsFlow.PasskeysFormProps) {
function PasskeysForm({
addNewPasskey,
existingPasskeys,
isPending,
isLoading,
}: GetSettingsPasskeysFormProps<typeof SettingsFlow>) {
if (isLoading) {
return <p data-testid={dataTestIds.settings.passkeysForm.loading}>Loading passkeys...</p>
}
Expand Down Expand Up @@ -229,7 +243,7 @@ function PasskeysForm({ addNewPasskey, existingPasskeys, isPending, isLoading }:
)
}

function TotpForm(props: settingsFlow.TotpFormProps) {
function TotpForm(props: GetSettingsTotpFormProps<typeof SettingsFlow>) {
if (props.isLoading) {
return <p data-testid={dataTestIds.settings.totpForm.loading}>Loading TOTP form...</p>
}
Expand Down Expand Up @@ -292,7 +306,7 @@ function TotpForm(props: settingsFlow.TotpFormProps) {
)
}

function OidcForm(props: settingsFlow.OidcFormProps<OidcProvidersConfig>) {
function OidcForm(props: GetSettingsOidcFormProps<typeof SettingsFlow>) {
if (props.isLoading) {
return <p>Loading OIDC providers...</p>
}
Expand Down
Loading
Loading