From b37667f9eae3a692723921034d6bbe094712875a Mon Sep 17 00:00:00 2001 From: Real Codesiman Date: Thu, 27 Aug 2026 22:31:09 +0700 Subject: [PATCH 1/5] chore(init): scaffold ChatbotX mobile app Expo Router app with chat, contacts, conversations, and workspace features; TanStack Query API layer, Zustand stores, PartyKit realtime client, i18n for 20 locales, and themed UI component library. --- .claude/commands/audit.md | 32 + .claude/commands/commit-pr.md | 56 + .claude/commands/commit.md | 46 + .claude/commands/implement-plan.md | 157 + .claude/commands/release-check.md | 41 + .claude/settings.json | 6 + .codegraph/.gitignore | 5 + .env.example | 6 + .github/workflows/ci.yml | 31 + .gitignore | 46 + .npmrc | 1 + .prettierignore | 6 + .prettierrc.json | 6 + .vscode/extensions.json | 1 + .vscode/settings.json | 7 + AGENTS.md | 23 + CLAUDE.md | 1 + README.md | 112 + app.config.ts | 107 + assets/expo.icon/Assets/expo-symbol 2.svg | 3 + assets/expo.icon/Assets/grid.png | Bin 0 -> 53681 bytes assets/expo.icon/icon.json | 35 + assets/images/android-icon-background.png | Bin 0 -> 17549 bytes assets/images/android-icon-foreground.png | Bin 0 -> 78796 bytes assets/images/android-icon-monochrome.png | Bin 0 -> 4140 bytes assets/images/favicon.png | Bin 0 -> 1129 bytes assets/images/icon.png | Bin 0 -> 799005 bytes assets/images/splash-icon.png | Bin 0 -> 3317 bytes eas.json | 24 + eslint.config.js | 10 + jest.config.js | 15 + jest.setup.ts | 81 + package.json | 81 + pnpm-lock.yaml | 11697 +++++++++ pnpm-workspace.yaml | 28 + scripts/generate-api-client.ts | 112 + scripts/i18n-sync.ts | 64 + scripts/reset-project.js | 114 + src/api/auth-endpoints.ts | 151 + src/api/auth-token.ts | 24 + src/api/client.test.ts | 117 + src/api/client.ts | 33 + src/api/errors.test.ts | 133 + src/api/errors.ts | 91 + src/api/generated/README.md | 8 + src/api/generated/schema.ts | 20657 ++++++++++++++++ src/api/pagination.test.ts | 32 + src/api/pagination.ts | 20 + src/api/query-keys.test.ts | 53 + src/api/query-keys.ts | 60 + src/app/(app)/(tabs)/_layout.tsx | 73 + src/app/(app)/(tabs)/contacts/index.tsx | 96 + src/app/(app)/(tabs)/conversations/index.tsx | 273 + src/app/(app)/(tabs)/settings/index.tsx | 209 + src/app/(app)/_layout.tsx | 35 + src/app/(app)/contacts/[contactId].tsx | 15 + .../[conversationId]/contact.tsx | 36 + .../conversations/[conversationId]/index.tsx | 228 + src/app/(app)/profile.tsx | 56 + src/app/(app)/settings/members.tsx | 91 + src/app/(app)/workspace-picker.tsx | 73 + src/app/(auth)/_layout.tsx | 16 + src/app/(auth)/change-password.tsx | 135 + src/app/(auth)/forgot-password.tsx | 106 + src/app/(auth)/sign-in.tsx | 175 + src/app/(dev)/gallery.tsx | 435 + src/app/+not-found.tsx | 30 + src/app/_layout.tsx | 188 + src/app/camera.tsx | 165 + src/app/image-viewer.tsx | 50 + src/app/index.tsx | 44 + src/components/ui/avatar.tsx | 111 + src/components/ui/badge.tsx | 79 + src/components/ui/button.tsx | 119 + src/components/ui/chip.test.tsx | 51 + src/components/ui/chip.tsx | 127 + src/components/ui/connection-banner.tsx | 128 + src/components/ui/count-pill.test.tsx | 35 + src/components/ui/count-pill.tsx | 63 + src/components/ui/date-separator.tsx | 62 + src/components/ui/divider.tsx | 31 + src/components/ui/empty-state.tsx | 77 + src/components/ui/error-banner.tsx | 67 + src/components/ui/icon-button.test.tsx | 49 + src/components/ui/icon-button.tsx | 137 + src/components/ui/icon.tsx | 21 + src/components/ui/list-footer-spinner.tsx | 24 + src/components/ui/list-item.tsx | 144 + src/components/ui/pressable-scale.tsx | 63 + src/components/ui/relative-time.tsx | 19 + src/components/ui/screen-header.tsx | 70 + src/components/ui/screen.tsx | 40 + src/components/ui/search-bar.tsx | 69 + src/components/ui/section-header.tsx | 44 + src/components/ui/segmented-tabs.tsx | 134 + src/components/ui/sheet.tsx | 136 + src/components/ui/skeleton.tsx | 109 + src/components/ui/surface.tsx | 62 + src/components/ui/swipeable-row.tsx | 181 + src/components/ui/text-field.tsx | 117 + src/components/ui/text.tsx | 94 + src/components/ui/toast.tsx | 197 + src/components/ui/toggle-row.test.tsx | 36 + src/components/ui/toggle-row.tsx | 58 + src/components/workspace-blocked-gate.tsx | 58 + src/config/env.ts | 29 + src/config/features.ts | 12 + src/features/auth/social-buttons.tsx | 99 + .../chat/api/generate-client-id.test.ts | 15 + src/features/chat/api/generate-client-id.ts | 15 + .../chat/api/send-message-multipart.ts | 135 + src/features/chat/api/use-message-actions.ts | 129 + .../chat/api/use-messages-infinite.ts | 48 + .../chat/api/use-send-message.test.tsx | 263 + src/features/chat/api/use-send-message.ts | 306 + .../chat/components/activity-message.tsx | 34 + .../chat/components/attachment-view.tsx | 194 + src/features/chat/components/chat-header.tsx | 163 + .../components/composer-actions-sheet.tsx | 70 + src/features/chat/components/composer.tsx | 439 + .../components/conversation-actions-sheet.tsx | 143 + .../chat/components/flow-picker-sheet.tsx | 46 + .../chat/components/location-view.tsx | 58 + .../chat/components/message-actions-menu.tsx | 63 + .../chat/components/message-bubble.test.tsx | 151 + .../chat/components/message-bubble.tsx | 266 + src/features/chat/components/message-list.tsx | 224 + .../chat/components/optimistic-message.ts | 18 + src/features/chat/components/quota-banner.tsx | 31 + .../chat/components/rich-content-view.tsx | 22 + .../chat/components/saved-replies-sheet.tsx | 71 + .../chat/components/scroll-to-bottom-fab.tsx | 43 + .../chat/components/typing-indicator.tsx | 109 + src/features/chat/lib/message-groups.test.ts | 93 + src/features/chat/lib/message-groups.ts | 67 + .../chat/lib/pick-attachments.test.ts | 67 + src/features/chat/lib/pick-attachments.ts | 82 + src/features/chat/stores/use-chat-store.ts | 66 + .../contacts/api/use-contact-detail.ts | 32 + .../contacts/api/use-contact-fields.ts | 74 + .../contacts/api/use-contact-tags.test.tsx | 175 + src/features/contacts/api/use-contact-tags.ts | 110 + .../contacts/api/use-contacts-infinite.ts | 53 + src/features/contacts/contact-header.tsx | 138 + src/features/contacts/contact-panel.tsx | 168 + src/features/contacts/contact-row.tsx | 41 + src/features/contacts/info-tab.tsx | 165 + .../lib/find-contact-in-cache.test.ts | 104 + .../contacts/lib/find-contact-in-cache.ts | 39 + src/features/contacts/notes-tab.tsx | 70 + src/features/contacts/pii-mask.test.ts | 29 + src/features/contacts/pii-mask.ts | 15 + src/features/contacts/sequences-tab.tsx | 74 + src/features/contacts/tags-tab.tsx | 84 + .../api/use-conversation-actions.test.tsx | 154 + .../api/use-conversation-actions.ts | 371 + .../api/use-conversation-detail.ts | 39 + .../api/use-conversations-infinite.ts | 89 + .../api/use-inbox-unread-count.test.tsx | 167 + .../api/use-inbox-unread-count.ts | 67 + .../components/assignment-sheet.tsx | 79 + .../components/channel-badge.tsx | 46 + .../components/conversation-row.test.tsx | 111 + .../components/conversation-row.tsx | 298 + .../components/filter-chips-row.test.tsx | 32 + .../components/filter-chips-row.tsx | 115 + .../conversations/components/filter-sheet.tsx | 219 + .../conversations/components/tag-chips.tsx | 64 + .../lib/conversation-status.test.ts | 63 + .../conversations/lib/conversation-status.ts | 32 + .../lib/find-conversation-in-cache.test.ts | 113 + .../lib/find-conversation-in-cache.ts | 37 + .../stores/use-conversation-filters.ts | 85 + src/features/flows/api/use-flows.ts | 33 + .../permissions/use-permissions.test.tsx | 123 + src/features/permissions/use-permissions.ts | 108 + .../use-enroll-contact-in-sequences.test.tsx | 43 + .../api/use-enroll-contact-in-sequences.ts | 80 + src/features/sequences/api/use-sequences.ts | 37 + .../components/sequence-picker-sheet.tsx | 134 + src/features/settings/api/use-device-token.ts | 89 + src/features/settings/api/use-teams.ts | 27 + .../settings/components/language-sheet.tsx | 45 + .../api/use-switch-workspace.test.tsx | 125 + .../workspaces/api/use-switch-workspace.ts | 59 + src/features/workspaces/api/use-workspaces.ts | 27 + .../workspaces/components/workspace-pill.tsx | 56 + .../components/workspace-switcher-sheet.tsx | 73 + src/features/workspaces/workspace-row.tsx | 31 + src/hooks/use-color-scheme.ts | 1 + src/hooks/use-color-scheme.web.ts | 29 + src/i18n/apply-language.ts | 66 + src/i18n/dayjs-locales.test.ts | 31 + src/i18n/dayjs-locales.ts | 54 + src/i18n/index.ts | 61 + src/i18n/locales.test.ts | 75 + src/i18n/locales.ts | 102 + src/i18n/locales/ar.json | 264 + src/i18n/locales/da.json | 259 + src/i18n/locales/de.json | 259 + src/i18n/locales/en.json | 259 + src/i18n/locales/es.json | 259 + src/i18n/locales/fi.json | 259 + src/i18n/locales/fr.json | 259 + src/i18n/locales/he.json | 261 + src/i18n/locales/id.json | 259 + src/i18n/locales/it.json | 259 + src/i18n/locales/ja.json | 259 + src/i18n/locales/nl.json | 259 + src/i18n/locales/pt-BR.json | 259 + src/i18n/locales/pt-PT.json | 259 + src/i18n/locales/ro.json | 260 + src/i18n/locales/sv.json | 259 + src/i18n/locales/tr.json | 259 + src/i18n/locales/vi.json | 259 + src/i18n/locales/zh-CN.json | 259 + src/i18n/locales/zh-TW.json | 259 + src/i18n/namespace.ts | 1 + src/i18n/reconcile-rtl.ts | 37 + src/i18n/resources.ts | 46 + src/lib/describe-api-error.test.ts | 58 + src/lib/describe-api-error.ts | 43 + src/lib/haptics.ts | 56 + src/lib/install-crypto-polyfill.test.ts | 85 + src/lib/install-crypto-polyfill.ts | 53 + src/lib/notification-tap.ts | 50 + src/lib/notifications.ts | 97 + src/lib/pending-deep-link.ts | 43 + src/lib/query-client.ts | 37 + src/realtime/app-state.ts | 48 + .../apply-conversation-events.test.ts | 217 + src/realtime/apply-conversation-events.ts | 156 + src/realtime/apply-message-events.test.ts | 120 + src/realtime/apply-message-events.ts | 170 + src/realtime/events.ts | 140 + src/realtime/one-time-token.ts | 30 + src/realtime/parse-event.test.ts | 277 + src/realtime/parse-event.ts | 167 + src/realtime/realtime-provider.tsx | 126 + src/realtime/typing-timers.test.ts | 90 + src/realtime/typing-timers.ts | 46 + src/realtime/use-connection-store.ts | 51 + src/realtime/use-realtime-handlers.test.ts | 224 + src/realtime/use-realtime-handlers.ts | 224 + src/stores/use-auth-store.test.ts | 38 + src/stores/use-auth-store.ts | 33 + src/stores/use-settings-store.test.ts | 32 + src/stores/use-settings-store.ts | 54 + src/stores/use-workspace-store.test.ts | 22 + src/stores/use-workspace-store.ts | 30 + src/theme/color-utils.test.ts | 50 + src/theme/color-utils.ts | 37 + src/theme/motion.ts | 20 + src/theme/navigation-theme.ts | 24 + src/theme/tokens.test.ts | 179 + src/theme/tokens.ts | 317 + src/theme/typography.ts | 52 + src/theme/use-theme.ts | 83 + tsconfig.json | 12 + 259 files changed, 57169 insertions(+) create mode 100644 .claude/commands/audit.md create mode 100644 .claude/commands/commit-pr.md create mode 100644 .claude/commands/commit.md create mode 100644 .claude/commands/implement-plan.md create mode 100644 .claude/commands/release-check.md create mode 100644 .claude/settings.json create mode 100644 .codegraph/.gitignore create mode 100644 .env.example create mode 100644 .github/workflows/ci.yml create mode 100644 .gitignore create mode 100644 .npmrc create mode 100644 .prettierignore create mode 100644 .prettierrc.json create mode 100644 .vscode/extensions.json create mode 100644 .vscode/settings.json create mode 100644 AGENTS.md create mode 100644 CLAUDE.md create mode 100644 README.md create mode 100644 app.config.ts create mode 100644 assets/expo.icon/Assets/expo-symbol 2.svg create mode 100644 assets/expo.icon/Assets/grid.png create mode 100644 assets/expo.icon/icon.json create mode 100644 assets/images/android-icon-background.png create mode 100644 assets/images/android-icon-foreground.png create mode 100644 assets/images/android-icon-monochrome.png create mode 100644 assets/images/favicon.png create mode 100644 assets/images/icon.png create mode 100644 assets/images/splash-icon.png create mode 100644 eas.json create mode 100644 eslint.config.js create mode 100644 jest.config.js create mode 100644 jest.setup.ts create mode 100644 package.json create mode 100644 pnpm-lock.yaml create mode 100644 pnpm-workspace.yaml create mode 100644 scripts/generate-api-client.ts create mode 100644 scripts/i18n-sync.ts create mode 100644 scripts/reset-project.js create mode 100644 src/api/auth-endpoints.ts create mode 100644 src/api/auth-token.ts create mode 100644 src/api/client.test.ts create mode 100644 src/api/client.ts create mode 100644 src/api/errors.test.ts create mode 100644 src/api/errors.ts create mode 100644 src/api/generated/README.md create mode 100644 src/api/generated/schema.ts create mode 100644 src/api/pagination.test.ts create mode 100644 src/api/pagination.ts create mode 100644 src/api/query-keys.test.ts create mode 100644 src/api/query-keys.ts create mode 100644 src/app/(app)/(tabs)/_layout.tsx create mode 100644 src/app/(app)/(tabs)/contacts/index.tsx create mode 100644 src/app/(app)/(tabs)/conversations/index.tsx create mode 100644 src/app/(app)/(tabs)/settings/index.tsx create mode 100644 src/app/(app)/_layout.tsx create mode 100644 src/app/(app)/contacts/[contactId].tsx create mode 100644 src/app/(app)/conversations/[conversationId]/contact.tsx create mode 100644 src/app/(app)/conversations/[conversationId]/index.tsx create mode 100644 src/app/(app)/profile.tsx create mode 100644 src/app/(app)/settings/members.tsx create mode 100644 src/app/(app)/workspace-picker.tsx create mode 100644 src/app/(auth)/_layout.tsx create mode 100644 src/app/(auth)/change-password.tsx create mode 100644 src/app/(auth)/forgot-password.tsx create mode 100644 src/app/(auth)/sign-in.tsx create mode 100644 src/app/(dev)/gallery.tsx create mode 100644 src/app/+not-found.tsx create mode 100644 src/app/_layout.tsx create mode 100644 src/app/camera.tsx create mode 100644 src/app/image-viewer.tsx create mode 100644 src/app/index.tsx create mode 100644 src/components/ui/avatar.tsx create mode 100644 src/components/ui/badge.tsx create mode 100644 src/components/ui/button.tsx create mode 100644 src/components/ui/chip.test.tsx create mode 100644 src/components/ui/chip.tsx create mode 100644 src/components/ui/connection-banner.tsx create mode 100644 src/components/ui/count-pill.test.tsx create mode 100644 src/components/ui/count-pill.tsx create mode 100644 src/components/ui/date-separator.tsx create mode 100644 src/components/ui/divider.tsx create mode 100644 src/components/ui/empty-state.tsx create mode 100644 src/components/ui/error-banner.tsx create mode 100644 src/components/ui/icon-button.test.tsx create mode 100644 src/components/ui/icon-button.tsx create mode 100644 src/components/ui/icon.tsx create mode 100644 src/components/ui/list-footer-spinner.tsx create mode 100644 src/components/ui/list-item.tsx create mode 100644 src/components/ui/pressable-scale.tsx create mode 100644 src/components/ui/relative-time.tsx create mode 100644 src/components/ui/screen-header.tsx create mode 100644 src/components/ui/screen.tsx create mode 100644 src/components/ui/search-bar.tsx create mode 100644 src/components/ui/section-header.tsx create mode 100644 src/components/ui/segmented-tabs.tsx create mode 100644 src/components/ui/sheet.tsx create mode 100644 src/components/ui/skeleton.tsx create mode 100644 src/components/ui/surface.tsx create mode 100644 src/components/ui/swipeable-row.tsx create mode 100644 src/components/ui/text-field.tsx create mode 100644 src/components/ui/text.tsx create mode 100644 src/components/ui/toast.tsx create mode 100644 src/components/ui/toggle-row.test.tsx create mode 100644 src/components/ui/toggle-row.tsx create mode 100644 src/components/workspace-blocked-gate.tsx create mode 100644 src/config/env.ts create mode 100644 src/config/features.ts create mode 100644 src/features/auth/social-buttons.tsx create mode 100644 src/features/chat/api/generate-client-id.test.ts create mode 100644 src/features/chat/api/generate-client-id.ts create mode 100644 src/features/chat/api/send-message-multipart.ts create mode 100644 src/features/chat/api/use-message-actions.ts create mode 100644 src/features/chat/api/use-messages-infinite.ts create mode 100644 src/features/chat/api/use-send-message.test.tsx create mode 100644 src/features/chat/api/use-send-message.ts create mode 100644 src/features/chat/components/activity-message.tsx create mode 100644 src/features/chat/components/attachment-view.tsx create mode 100644 src/features/chat/components/chat-header.tsx create mode 100644 src/features/chat/components/composer-actions-sheet.tsx create mode 100644 src/features/chat/components/composer.tsx create mode 100644 src/features/chat/components/conversation-actions-sheet.tsx create mode 100644 src/features/chat/components/flow-picker-sheet.tsx create mode 100644 src/features/chat/components/location-view.tsx create mode 100644 src/features/chat/components/message-actions-menu.tsx create mode 100644 src/features/chat/components/message-bubble.test.tsx create mode 100644 src/features/chat/components/message-bubble.tsx create mode 100644 src/features/chat/components/message-list.tsx create mode 100644 src/features/chat/components/optimistic-message.ts create mode 100644 src/features/chat/components/quota-banner.tsx create mode 100644 src/features/chat/components/rich-content-view.tsx create mode 100644 src/features/chat/components/saved-replies-sheet.tsx create mode 100644 src/features/chat/components/scroll-to-bottom-fab.tsx create mode 100644 src/features/chat/components/typing-indicator.tsx create mode 100644 src/features/chat/lib/message-groups.test.ts create mode 100644 src/features/chat/lib/message-groups.ts create mode 100644 src/features/chat/lib/pick-attachments.test.ts create mode 100644 src/features/chat/lib/pick-attachments.ts create mode 100644 src/features/chat/stores/use-chat-store.ts create mode 100644 src/features/contacts/api/use-contact-detail.ts create mode 100644 src/features/contacts/api/use-contact-fields.ts create mode 100644 src/features/contacts/api/use-contact-tags.test.tsx create mode 100644 src/features/contacts/api/use-contact-tags.ts create mode 100644 src/features/contacts/api/use-contacts-infinite.ts create mode 100644 src/features/contacts/contact-header.tsx create mode 100644 src/features/contacts/contact-panel.tsx create mode 100644 src/features/contacts/contact-row.tsx create mode 100644 src/features/contacts/info-tab.tsx create mode 100644 src/features/contacts/lib/find-contact-in-cache.test.ts create mode 100644 src/features/contacts/lib/find-contact-in-cache.ts create mode 100644 src/features/contacts/notes-tab.tsx create mode 100644 src/features/contacts/pii-mask.test.ts create mode 100644 src/features/contacts/pii-mask.ts create mode 100644 src/features/contacts/sequences-tab.tsx create mode 100644 src/features/contacts/tags-tab.tsx create mode 100644 src/features/conversations/api/use-conversation-actions.test.tsx create mode 100644 src/features/conversations/api/use-conversation-actions.ts create mode 100644 src/features/conversations/api/use-conversation-detail.ts create mode 100644 src/features/conversations/api/use-conversations-infinite.ts create mode 100644 src/features/conversations/api/use-inbox-unread-count.test.tsx create mode 100644 src/features/conversations/api/use-inbox-unread-count.ts create mode 100644 src/features/conversations/components/assignment-sheet.tsx create mode 100644 src/features/conversations/components/channel-badge.tsx create mode 100644 src/features/conversations/components/conversation-row.test.tsx create mode 100644 src/features/conversations/components/conversation-row.tsx create mode 100644 src/features/conversations/components/filter-chips-row.test.tsx create mode 100644 src/features/conversations/components/filter-chips-row.tsx create mode 100644 src/features/conversations/components/filter-sheet.tsx create mode 100644 src/features/conversations/components/tag-chips.tsx create mode 100644 src/features/conversations/lib/conversation-status.test.ts create mode 100644 src/features/conversations/lib/conversation-status.ts create mode 100644 src/features/conversations/lib/find-conversation-in-cache.test.ts create mode 100644 src/features/conversations/lib/find-conversation-in-cache.ts create mode 100644 src/features/conversations/stores/use-conversation-filters.ts create mode 100644 src/features/flows/api/use-flows.ts create mode 100644 src/features/permissions/use-permissions.test.tsx create mode 100644 src/features/permissions/use-permissions.ts create mode 100644 src/features/sequences/api/use-enroll-contact-in-sequences.test.tsx create mode 100644 src/features/sequences/api/use-enroll-contact-in-sequences.ts create mode 100644 src/features/sequences/api/use-sequences.ts create mode 100644 src/features/sequences/components/sequence-picker-sheet.tsx create mode 100644 src/features/settings/api/use-device-token.ts create mode 100644 src/features/settings/api/use-teams.ts create mode 100644 src/features/settings/components/language-sheet.tsx create mode 100644 src/features/workspaces/api/use-switch-workspace.test.tsx create mode 100644 src/features/workspaces/api/use-switch-workspace.ts create mode 100644 src/features/workspaces/api/use-workspaces.ts create mode 100644 src/features/workspaces/components/workspace-pill.tsx create mode 100644 src/features/workspaces/components/workspace-switcher-sheet.tsx create mode 100644 src/features/workspaces/workspace-row.tsx create mode 100644 src/hooks/use-color-scheme.ts create mode 100644 src/hooks/use-color-scheme.web.ts create mode 100644 src/i18n/apply-language.ts create mode 100644 src/i18n/dayjs-locales.test.ts create mode 100644 src/i18n/dayjs-locales.ts create mode 100644 src/i18n/index.ts create mode 100644 src/i18n/locales.test.ts create mode 100644 src/i18n/locales.ts create mode 100644 src/i18n/locales/ar.json create mode 100644 src/i18n/locales/da.json create mode 100644 src/i18n/locales/de.json create mode 100644 src/i18n/locales/en.json create mode 100644 src/i18n/locales/es.json create mode 100644 src/i18n/locales/fi.json create mode 100644 src/i18n/locales/fr.json create mode 100644 src/i18n/locales/he.json create mode 100644 src/i18n/locales/id.json create mode 100644 src/i18n/locales/it.json create mode 100644 src/i18n/locales/ja.json create mode 100644 src/i18n/locales/nl.json create mode 100644 src/i18n/locales/pt-BR.json create mode 100644 src/i18n/locales/pt-PT.json create mode 100644 src/i18n/locales/ro.json create mode 100644 src/i18n/locales/sv.json create mode 100644 src/i18n/locales/tr.json create mode 100644 src/i18n/locales/vi.json create mode 100644 src/i18n/locales/zh-CN.json create mode 100644 src/i18n/locales/zh-TW.json create mode 100644 src/i18n/namespace.ts create mode 100644 src/i18n/reconcile-rtl.ts create mode 100644 src/i18n/resources.ts create mode 100644 src/lib/describe-api-error.test.ts create mode 100644 src/lib/describe-api-error.ts create mode 100644 src/lib/haptics.ts create mode 100644 src/lib/install-crypto-polyfill.test.ts create mode 100644 src/lib/install-crypto-polyfill.ts create mode 100644 src/lib/notification-tap.ts create mode 100644 src/lib/notifications.ts create mode 100644 src/lib/pending-deep-link.ts create mode 100644 src/lib/query-client.ts create mode 100644 src/realtime/app-state.ts create mode 100644 src/realtime/apply-conversation-events.test.ts create mode 100644 src/realtime/apply-conversation-events.ts create mode 100644 src/realtime/apply-message-events.test.ts create mode 100644 src/realtime/apply-message-events.ts create mode 100644 src/realtime/events.ts create mode 100644 src/realtime/one-time-token.ts create mode 100644 src/realtime/parse-event.test.ts create mode 100644 src/realtime/parse-event.ts create mode 100644 src/realtime/realtime-provider.tsx create mode 100644 src/realtime/typing-timers.test.ts create mode 100644 src/realtime/typing-timers.ts create mode 100644 src/realtime/use-connection-store.ts create mode 100644 src/realtime/use-realtime-handlers.test.ts create mode 100644 src/realtime/use-realtime-handlers.ts create mode 100644 src/stores/use-auth-store.test.ts create mode 100644 src/stores/use-auth-store.ts create mode 100644 src/stores/use-settings-store.test.ts create mode 100644 src/stores/use-settings-store.ts create mode 100644 src/stores/use-workspace-store.test.ts create mode 100644 src/stores/use-workspace-store.ts create mode 100644 src/theme/color-utils.test.ts create mode 100644 src/theme/color-utils.ts create mode 100644 src/theme/motion.ts create mode 100644 src/theme/navigation-theme.ts create mode 100644 src/theme/tokens.test.ts create mode 100644 src/theme/tokens.ts create mode 100644 src/theme/typography.ts create mode 100644 src/theme/use-theme.ts create mode 100644 tsconfig.json diff --git a/.claude/commands/audit.md b/.claude/commands/audit.md new file mode 100644 index 0000000..b4d9b3c --- /dev/null +++ b/.claude/commands/audit.md @@ -0,0 +1,32 @@ +--- +description: Audit the project's AI infrastructure (agents, skills, commands, context, rules) for gaps, drift, and security issues +allowed-tools: Bash, Read, Grep, Glob, Task +--- + +Run a focused audit of the repo's AI-assist infrastructure and report findings — analysis only, no edits. + +## Scope + +In scope: `.claude/` (agents, commands, settings), `.agents/` (skills, rules), `.github/copilot-instructions.md`, root `SKILL.md`, `AGENTS.md`, `CLAUDE.md`. **Out of scope:** editor-specific `.cursor/**` and Devin agent config. + +## Steps + +1. **Inventory** — list every in-scope file with line count and git-tracked/ignored status (`git ls-files`, `git check-ignore`). Flag untracked load-bearing config and missing referenced paths (e.g. a rules dir referenced by `AGENTS.md` that does not exist on disk). + +2. **Coverage & drift** — check that each tool's context (Claude, Copilot, and any editor-rule mirrors) carries the project invariants from `AGENTS.md`; flag any tool whose context omits them, and any invariant duplicated across files that can drift. + +3. **Skills & commands** — flag skills >400 lines (split candidates), skills/commands that duplicate a `~/.claude/` global, and prompts with no stop condition or with phantom commands. + +4. **Security** — scan `.claude/settings.local.json` for credential literals (report by location, never reproduce the value), wildcard `Bash(* )` grants, `.env`-copy/home-dir/`/etc` read grants, and machine-absolute or wrong-repo paths. + +5. **Report** — findings table `file:line | issue | severity | fix`, severity-ranked. For a deep pass, read the `security-review` skill and dispatch specialist agents (`rag-eval`, `invariant-guard`) when relevant. + +## Arguments + +$ARGUMENTS — optional surface to focus on (e.g. `skills`, `security`, `context`). + +## Guardrails + +- Analysis only — never edit infra files; output findings + recommendations. +- Never reproduce secret values; reference by `file:line` only. +- Cite real `path:line` for every finding; drop anything you cannot evidence. diff --git a/.claude/commands/commit-pr.md b/.claude/commands/commit-pr.md new file mode 100644 index 0000000..4576ff6 --- /dev/null +++ b/.claude/commands/commit-pr.md @@ -0,0 +1,56 @@ +--- +description: Commit staged changes, push branch, and open a PR in one shot +allowed-tools: Bash, Read +--- + +Run `/commit` flow then push and open PR. Follows `.agents/rules/git.md`. + +## Steps + +1. **Commit** — follow `.claude/commands/commit.md` end-to-end (analyze diff, stage specific files, conventional `(): `, HEREDOC, no `--no-verify`, no attribution). Abort if nothing to commit. + +2. **Push** — parallel: + - `git rev-parse --abbrev-ref HEAD` to get branch + - `git rev-parse --abbrev-ref --symbolic-full-name @{u} 2>/dev/null` to check upstream + - If no upstream: `git push -u origin `; else `git push` + - Never force-push. Never push to `main`/`master` directly. + +3. **PR** — parallel: + - `git log main..HEAD --oneline` for commit list + - `git diff main...HEAD --stat` for scope + - `gh pr view --json url 2>/dev/null` to detect existing PR (skip create if present, return URL) + +4. **Draft PR body**: + - Title: same as commit subject (≤ 100 chars). Add `#` if `$ARGUMENTS` contains issue ref. + - Body sections: `## Summary` (1–3 bullets, why), `## Changes` (key files/modules), `## Test plan` (markdown checklist). + +5. **Create PR** against `main` (the project's canonical PR base): + ```bash + gh pr create --base main --title "" --body "$(cat <<'EOF' + ## Summary + - ... + + ## Changes + - ... + + ## Test plan + - [ ] pnpm lint + - [ ] pnpm --filter <app> check-types + - [ ] manual verification + EOF + )" + ``` + +6. Return PR URL. + +## Arguments + +$ARGUMENTS — optional issue ref / hint (e.g. `#414 flow token`). Used in PR title/body when present. + +## Guardrails + +- No `git add -A`/`.`, no `.env`, no secrets. +- No `--no-verify`, no `--force`/`--force-with-lease` unless user explicitly asks. +- No attribution footers. +- If branch is `main`/`master`: abort and tell user to create feature branch first. +- If `pnpm lint` or typecheck obviously broken in diff, warn before pushing (do not auto-run unless asked). diff --git a/.claude/commands/commit.md b/.claude/commands/commit.md new file mode 100644 index 0000000..1c34a08 --- /dev/null +++ b/.claude/commands/commit.md @@ -0,0 +1,46 @@ +--- +description: Stage specific files and create a conventional commit following ChatbotX git rules +allowed-tools: Bash, Read +--- + +Create a git commit for the current changes following the ChatbotX conventions in `.agents/rules/git.md`. + +## Steps + +1. Run in parallel: + - `git status` (never `-uall`) + - `git diff` (staged + unstaged) + - `git log -n 10 --oneline` for style reference + +2. Analyze the diff: + - Determine commit `type`: `feat`, `fix`, `refactor`, `docs`, `style`, `test`, `chore`, `ci`, `perf`, `build`, `revert` + - Determine `scope` from touched package/app/feature when obvious (e.g. `builder`, `worker`, `whatsapp`, `database`) + - Draft subject: `<type>(<scope>): <subject>` — ≤ 100 chars, lowercase after `:`, no trailing period + - Add body only when the *why* is non-obvious + +3. Stage **specific files only** — never `git add -A` or `git add .`. + - Skip `.env*` and any file containing secrets. Warn if user explicitly requests them. + +4. Commit using a HEREDOC so formatting is preserved: + ```bash + git commit -m "$(cat <<'EOF' + <type>(<scope>): <subject> + + <optional body> + EOF + )" + ``` + - Do **not** pass `--no-verify`. Let `lefthook` commit-msg + pre-commit hooks run. + - If a hook fails, fix the underlying issue and create a **new** commit (do not `--amend`). + +5. Run `git status` after commit to confirm clean state. + +## Arguments + +$ARGUMENTS — optional. Treat as a hint about scope/intent (e.g. `flow token fallback`). Still derive type/scope from the diff. + +## Guardrails + +- No attribution / `Co-Authored-By` footers (disabled globally). +- No pushing in this command — use `/pr` for that. +- If nothing is staged and there are no changes, abort without creating an empty commit. diff --git a/.claude/commands/implement-plan.md b/.claude/commands/implement-plan.md new file mode 100644 index 0000000..47753d3 --- /dev/null +++ b/.claude/commands/implement-plan.md @@ -0,0 +1,157 @@ +--- +description: Implement approved technical plans with execution-first behavior +--- + +# Implement Plan + +You are in IMPLEMENTATION MODE. + +The implementation plan has already been approved. + +Your responsibility is execution, not planning. + +Follow the shared repository workflow in `.agents/skills/implement-plan/SKILL.md`. + +## Core Principles + +- Implement code, do not create plans. +- Follow the implementation plan exactly. +- Respect approved architecture decisions. +- Respect approved API contracts. +- Respect approved database schema. +- Reuse existing code whenever possible. +- Keep changes minimal and focused. +- Avoid speculative improvements. +- Avoid unrelated refactoring. +- Do not expand scope. + +## Getting Started + +When given a plan path: + +1. Read the plan completely. +2. Check for completed items (- [x]). +3. Read the original ticket if it is referenced or provided. +4. Read all files referenced by the plan. +5. Read files completely. +6. Understand the surrounding code before making changes. +7. Create an internal task list. +8. Begin implementation immediately. + +If no plan path is provided, ask for one. + +## Critical Execution Rules + +- DO NOT create a new implementation plan. +- DO NOT rewrite the existing implementation plan. +- Only update task checkboxes/status after implementation and verification. +- DO NOT ask for approval before coding. +- DO NOT explain what you intend to do before making changes. +- DO NOT stop to summarize before implementation. +- DO NOT pause unless blocked by ambiguity or a plan mismatch. + +Your first action should be reading the plan and relevant files. + +Your first response should never be a plan. + +## Implementation Process + +1. Read the implementation plan. +2. Find the highest-priority unfinished task. +3. Implement that task completely. +4. Verify the implementation. +5. Update task checkbox/status if applicable. +6. Stop after the task is complete. + +Only work on ONE task at a time. + +Prefer the smallest independently deliverable unfinished task. + +Do not automatically continue to the next task. + +## Plan Mismatch Handling + +If reality differs from the plan: + +STOP and explain: + +Issue in Phase [N] + +Expected: +[what the plan specifies] + +Found: +[actual implementation reality] + +Why this matters: +[technical impact] + +Recommended path: +[best option] + +Wait for guidance before proceeding. + +## Verification + +After implementation: + +- Run relevant tests. +- Run lint. +- Run typecheck. +- Fix all issues introduced by your changes. +- Ensure the project builds successfully. +- Verify success criteria defined in the plan. + +## Manual Verification + +Only pause for manual verification when: + +- The plan explicitly requires manual testing. +- The implementation cannot be fully validated automatically. +- User interaction is required. + +Otherwise continue implementation. + +Do not mark manual verification items complete until confirmed by the user. + +## Resuming Work + +If checkmarks already exist: + +- Trust completed work unless it blocks the current task, conflicts with current code, or verification fails. +- Resume from the first unfinished item. +- Only revisit completed work if necessary. + +## Output Format + +### Task Implemented + +- What was implemented +- Why it was required + +### Files Changed + +- File path +- Reason for change + +### Validation + +- Tests executed +- Test results +- Lint status +- Typecheck status +- Build status + +### Remaining Work + +- Remaining unfinished tasks from the implementation plan + +## Important + +- Implementation first. +- Code first. +- No planning. +- No redesign. +- No scope expansion. + +Your first code-changing action should be implementation, not replanning. diff --git a/.claude/commands/release-check.md b/.claude/commands/release-check.md new file mode 100644 index 0000000..3826b12 --- /dev/null +++ b/.claude/commands/release-check.md @@ -0,0 +1,41 @@ +--- +description: Run the full pre-release verification gate (lint, types, tests, coverage) and report readiness +allowed-tools: Bash, Read, Grep +--- + +Verify the repo is release-ready by running the gate sequence from the `testing-workflow` skill. CI only builds Docker images, so this gate is your real safety net — run it before tagging a release or opening a release PR. + +## Steps + +1. **Scope** — determine what changed: `git diff main...HEAD --stat` (or the given range). Identify the touched workspaces. + +2. **Lint** — `pnpm lint`. If it fails, stop and report; suggest `pnpm fix` for auto-fixable issues. + +3. **Types** — for each touched workspace: `pnpm --filter <workspace> check-types`. Report the first failure per workspace. + +4. **Tests + coverage** — run the affected packages' Vitest suites. Do **not** set `VITEST_SKIP_COVERAGE_THRESHOLDS`; the 80% threshold must hold (`packages/vitest-config/src/node.ts`). Report any suite below threshold. + +5. **Invariant scan** — dispatch the `invariant-guard` agent on the diff to catch the non-lintable invariants. + +6. **Secret scan** — grep the diff for credential patterns (`PGPASSWORD`, `DATABASE_URL=`, API keys); confirm no `.env`/secret is staged. + +## Output + +A readiness checklist: + +``` +RELEASE CHECK +- lint: PASS / FAIL (<detail>) +- types: PASS / FAIL (<workspace>) +- tests: PASS / FAIL (<suite>) +- coverage: >=80% / BELOW (<package> <n>%) +- invariants: PASS / <n> violations +- secrets: CLEAN / FOUND (<file:line>) +=> READY / NOT READY +``` + +## Guardrails + +- Read-only verification — do not edit code to make a gate pass; report the failure and let the owner fix it. +- Never report READY on a gate you did not actually run; say which gate was skipped and why. +- Never bypass coverage with the skip env var. diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000..588dcb0 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,6 @@ +{ + "enabledPlugins": { + "expo@claude-plugins-official": true, + "ui-ux-pro-max@ui-ux-pro-max-skill": true + } +} diff --git a/.codegraph/.gitignore b/.codegraph/.gitignore new file mode 100644 index 0000000..d20c0fe --- /dev/null +++ b/.codegraph/.gitignore @@ -0,0 +1,5 @@ +# CodeGraph data files — local to each machine, not for committing. +# Ignore everything in .codegraph/ except this file itself, so transient +# files (the database, daemon.pid, sockets, logs) never show up in git. +* +!.gitignore diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..0a1a12a --- /dev/null +++ b/.env.example @@ -0,0 +1,6 @@ +# Base URL of the aha.chat builder app (oRPC + REST API + OpenAPI spec host). +# Defaults to the builder app's standard local dev URL when unset. +API_BASE_URL=http://localhost:3123 + +# PartyKit realtime server URL. Defaults to the local `partykit dev` port when unset. +WS_URL=http://localhost:1999 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..3412bf3 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,31 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + +jobs: + ci: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: pnpm + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Typecheck + run: pnpm typecheck + + - name: Lint + run: pnpm lint + + - name: Test + run: pnpm test diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5103350 --- /dev/null +++ b/.gitignore @@ -0,0 +1,46 @@ +# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files + +# dependencies +node_modules/ + +# Expo +.expo/ +dist/ +web-build/ +expo-env.d.ts + +# Native +.kotlin/ +*.orig.* +*.jks +*.p8 +*.p12 +*.key +*.mobileprovision +google-services.json +GoogleService-Info.plist + +# Metro +.metro-health-check* + +# debug +npm-debug.* +yarn-debug.* +yarn-error.* + +# macOS +.DS_Store +*.pem + +# local env files +.env +.env*.local + +# typescript +*.tsbuildinfo + +example + +# generated native folders +/ios +/android diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..d67f374 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +node-linker=hoisted diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..f37dbee --- /dev/null +++ b/.prettierignore @@ -0,0 +1,6 @@ +node_modules +dist +.expo +src/generated +src/api/generated +pnpm-lock.yaml diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..47174e4 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,6 @@ +{ + "semi": true, + "singleQuote": true, + "trailingComma": "all", + "printWidth": 100 +} diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..b7ed837 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1 @@ +{ "recommendations": ["expo.vscode-expo-tools"] } diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..e2798e4 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "editor.codeActionsOnSave": { + "source.fixAll": "explicit", + "source.organizeImports": "explicit", + "source.sortMembers": "explicit" + } +} diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..17becaa --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,23 @@ +# Expo HAS CHANGED + +Read the exact versioned docs at https://docs.expo.dev/versions/v57.0.0/ before writing any code. + +# Crypto + +Never import Node's `crypto` (`crypto` / `node:crypto`) in app code. React Native runs on +**Hermes**, which has no Node standard library — the import resolves fine at build/type-check +time and even under Jest, then crashes only on a real device. Do not add `crypto-browserify` or +similar Node-crypto shims either. + +- Use `expo-crypto` directly, or the Web-Crypto-shaped `crypto` global installed by + `src/lib/install-crypto-polyfill.ts` (imported for its side effect at the top of + `src/app/_layout.tsx`). +- `expo-crypto` already covers random generation, SHA-2 digests, and AES-GCM — do not add + `react-native-get-random-values`, `expo-standard-web-crypto`, or `react-native-quick-crypto`; + they'd be redundant. +- The installed `crypto` global only provides `randomUUID` and `getRandomValues`. + **`crypto.subtle` is not implemented** — anything needing it (real hashing/HMAC/JWT + verification) must call `expo-crypto`'s `digest`/`digestStringAsync` directly instead. +- `src/features/chat/api/generate-client-id.ts` uses `Math.random` on purpose — the backend + types `clientId` as a numeric string (`zodBigintAsString()`), so a UUID would fail server + validation. Don't "upgrade" it to `crypto.randomUUID()`. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..43c994c --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +@AGENTS.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..575842c --- /dev/null +++ b/README.md @@ -0,0 +1,112 @@ +# chatbotx-mobile-app + +Expo + TypeScript mobile client for [ChatbotX](https://github.com/) — the open-source omnichannel +chatbot platform whose backend lives in the sibling repo, `../aha.chat`. This app replaces the +legacy `chatbotx-mobile` React Native project (untyped JS, old Laravel backend). + +Auth, workspace picker, and five tabs — Conversations, Contacts, Flows, Statistics, and Settings — +are implemented, along with chat with attachments, PartyKit realtime, push notifications, 20-locale +i18n (including RTL for `ar`/`he`), and a typed REST client generated from the backend's OpenAPI +spec. + +> This app talks to the `aha.chat` backend's `bearer()` better-auth plugin and the REST endpoints +> under `/api/workspaces/{workspaceId}/...`, plus the corresponding realtime events. Run +> `pnpm --filter builder dev` in `aha.chat` first so `pnpm generate:api` (see below) has a live +> spec to fetch. + +## Stack + +- [Expo](https://expo.dev) (SDK 57) + [expo-router](https://docs.expo.dev/router/introduction/) (file-based routing, `src/app/`) +- TypeScript, strict mode +- [TanStack Query](https://tanstack.com/query) for server state +- [Zustand](https://zustand-demo.pmnd.rs/) for client state +- [openapi-fetch](https://openapi-ts.dev/openapi-fetch/) + [openapi-typescript](https://openapi-ts.dev/) for a typed REST client generated from the backend's OpenAPI spec +- [i18next](https://www.i18next.com/) / [react-i18next](https://react.i18next.com/) for translations +- [expo-secure-store](https://docs.expo.dev/versions/latest/sdk/securestore/) for token storage +- ESLint (`eslint-config-expo`, flat config) + Prettier + +## Get started + +```bash +pnpm install +pnpm start +``` + +Then follow the Expo CLI prompts to open the app in a development build, Android emulator, iOS +simulator, Expo Go, or the web. + +Other useful commands: + +```bash +pnpm ios # expo start --ios +pnpm android # expo start --android +pnpm web # expo start --web +pnpm lint # expo lint (eslint-config-expo) +pnpm typecheck # tsc --noEmit +pnpm format # prettier --write . +pnpm format:check # prettier --check . +``` + +## Configuration + +The API base URL is env-driven via `app.config.ts` (not a static `app.json`, since we need +`process.env` at config-evaluation time) and exposed to the app at runtime through +`expo-constants` (see `src/config/env.ts`). + +```bash +cp .env.example .env +# edit API_BASE_URL if the builder app isn't on the default local port +``` + +Defaults to `http://localhost:3123` — the aha.chat builder app's standard local dev URL. + +## Regenerating the API client + +```bash +pnpm generate:api +``` + +This fetches `{API_BASE_URL}/api/spec.json` (the full oRPC router spec, including the +bearer/session-authenticated routes the app needs — override with `API_SPEC_PATH` to point at the +workspace-token-only `/api/public-spec.json` instead) from a running aha.chat builder instance and +runs it through `openapi-typescript` to produce `src/api/generated/schema.ts`, which +`src/api/client.ts` consumes via `openapi-fetch`. + +The script is **not** wired into the build — the backend isn't guaranteed to be running, and this +repo must build without it. If the spec endpoint isn't reachable, the script prints a clear +message and exits non-zero without touching the previously generated file. Run it from the +`aha.chat` repo first: `pnpm --filter builder dev`. + +## Realtime event types + +`src/realtime/events.ts` mirrors the realtime event type definitions from +`aha.chat/packages/partysocket-config/src/schemas.ts` **by hand**. This app lives in a separate +git repository (not a pnpm workspace member of `aha.chat`), so there's no live cross-repo import — +copying types in is the intentionally simple, non-fragile choice over a symlink or git submodule. +Only type-only shapes are mirrored; the server-side `lib.ts` in that package is never referenced. + +When the source file changes in `aha.chat`, re-copy the updated shapes by hand. The header comment +in `src/realtime/events.ts` notes which commit it was last synced against. + +## Project structure + +``` +src/ + api/ typed REST client (openapi-fetch), auth token storage, big-number-safe JSON parsing + generated/ output of `pnpm generate:api` — do not hand-edit + app/ expo-router screens (file-based routing) + config/ runtime env config (API_BASE_URL via expo-constants) + hooks/ shared hooks (e.g. useColorScheme) + i18n/ i18next setup, RTL reconciliation, and locale files (20 locales) + lib/ shared library wiring (TanStack Query client, push notifications) + realtime/ realtime event types mirrored from aha.chat (type-only, manual sync) + stores/ Zustand client-state stores + theme/ design tokens +scripts/ + generate-api-client.ts regenerates src/api/generated/schema.ts from the backend's OpenAPI spec +``` + +## Related repo + +Backend: [`aha.chat`](../aha.chat) — Next.js builder app, oRPC API, Drizzle/Postgres, BullMQ +workers, PartyKit realtime. See its `AGENTS.md` for architecture details. diff --git a/app.config.ts b/app.config.ts new file mode 100644 index 0000000..4af8482 --- /dev/null +++ b/app.config.ts @@ -0,0 +1,107 @@ +import type { ExpoConfig } from 'expo/config'; + +/** + * Using app.config.ts (instead of a static app.json) so we can read process.env at build/start + * time and expose runtime-configurable values (like the API base URL) via `expo-constants`. + * + * Values consumed by the app at runtime must be read through `expo-constants` + * (see src/config/env.ts) — process.env is only available here, in the Expo config context. + */ +const config: ExpoConfig = { + name: 'chatbotx-mobile-app', + slug: 'chatbotx-mobile-app', + version: '1.0.0', + orientation: 'portrait', + icon: './assets/images/icon.png', + scheme: 'chatbotxmobileapp', + userInterfaceStyle: 'automatic', + ios: { + icon: './assets/expo.icon', + bundleIdentifier: 'com.chatconnectx.mobile', + }, + android: { + package: 'com.chatconnectx.mobile', + adaptiveIcon: { + backgroundColor: '#E6F4FE', + foregroundImage: './assets/images/android-icon-foreground.png', + backgroundImage: './assets/images/android-icon-background.png', + monochromeImage: './assets/images/android-icon-monochrome.png', + }, + predictiveBackGestureEnabled: false, + // Required for Android FCM V1 push delivery — prerequisite: a Firebase project with this + // package name registered, its google-services.json downloaded to the repo root (gitignored, + // not committed). Builds will fail without this file once this config is active. + googleServicesFile: './google-services.json', + }, + web: { + output: 'static', + favicon: './assets/images/favicon.png', + }, + plugins: [ + 'expo-router', + 'expo-font', + [ + 'expo-splash-screen', + { + backgroundColor: '#208AEF', + image: './assets/images/splash-icon.png', + imageWidth: 76, + }, + ], + 'expo-image', + 'expo-web-browser', + [ + 'expo-image-picker', + { + photosPermission: 'Allow $(PRODUCT_NAME) to access your photos to attach them to messages.', + }, + ], + [ + 'expo-camera', + { + cameraPermission: + 'Allow $(PRODUCT_NAME) to access your camera to attach photos to messages.', + }, + ], + [ + 'expo-localization', + { + // Native RTL layout for `ar`/`he`. Requires a new dev/production build to take effect — + // Expo Go can't apply native Info.plist/strings.xml changes, so `extra.supportsRTL` below + // mirrors this for the app's own Expo-Go-safe RTL bootstrap logic (see src/i18n/reconcile-rtl.ts). + supportsRTL: true, + }, + ], + [ + 'expo-notifications', + { + // No dedicated notification icon asset exists yet (Android requires a flat white + // silhouette, distinct from the app icon) — falls back to Expo's generated default until + // one is designed. `color` matches the splash-screen brand color. + color: '#208AEF', + }, + ], + ], + experiments: { + typedRoutes: true, + reactCompiler: true, + }, + extra: { + // Falls back to the builder app's default local dev URL. Override via `API_BASE_URL` env var + // (e.g. in `.env`, loaded by `expo start` through the standard EXPO_PUBLIC_ / dotenv support, + // or exported in the shell before running Expo CLI commands). + apiBaseUrl: process.env.API_BASE_URL ?? 'http://localhost:3123', + // Realtime PartyKit server. Web reads this from tenant settings; for mobile dev we default to + // the local partykit dev port until tenant-settings discovery is wired up (see plan risk notes). + wsUrl: process.env.WS_URL ?? 'http://localhost:1999', + // Mirrors the expo-localization plugin's supportsRTL above for Expo Go, which can't read the + // native Info.plist/strings.xml values the plugin writes at build time. + supportsRTL: true, + // Written by `eas init` — required by `getExpoPushTokenAsync` in src/lib/notifications.ts. + eas: { + projectId: '8cfd58a8-15f8-4f64-b16c-abf26166e80f', + }, + }, +}; + +export default config; diff --git a/assets/expo.icon/Assets/expo-symbol 2.svg b/assets/expo.icon/Assets/expo-symbol 2.svg new file mode 100644 index 0000000..51d3676 --- /dev/null +++ b/assets/expo.icon/Assets/expo-symbol 2.svg @@ -0,0 +1,3 @@ +<svg width="652" height="606" viewBox="0 0 652 606" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M353.554 0H298.446C273.006 0 249.684 14.6347 237.962 37.9539L4.37994 502.646C-1.04325 513.435 -1.45067 526.178 3.2716 537.313L22.6123 582.918C34.6475 611.297 72.5404 614.156 88.4414 587.885L309.863 222.063C313.34 216.317 319.439 212.826 326 212.826C332.561 212.826 338.659 216.317 342.137 222.063L563.559 587.885C579.46 614.156 617.352 611.297 629.388 582.918L648.728 537.313C653.451 526.178 653.043 513.435 647.62 502.646L414.038 37.9539C402.316 14.6347 378.994 0 353.554 0Z" fill="white"/> +</svg> diff --git a/assets/expo.icon/Assets/grid.png b/assets/expo.icon/Assets/grid.png new file mode 100644 index 0000000000000000000000000000000000000000..eefea24246267f9a2446460c389a483577a3adb5 GIT binary patch literal 53681 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 literal 0 HcmV?d00001 diff --git a/assets/expo.icon/icon.json b/assets/expo.icon/icon.json new file mode 100644 index 0000000..9a26f70 --- /dev/null +++ b/assets/expo.icon/icon.json @@ -0,0 +1,35 @@ +{ + "fill": { + "automatic-gradient": "extended-srgb:0.00000,0.47843,1.00000,1.00000" + }, + "groups": [ + { + "layers": [ + { + "image-name": "expo-symbol 2.svg", + "name": "expo-symbol 2", + "position": { + "scale": 1, + "translation-in-points": [1.1008400065293245e-5, -16.046875] + } + }, + { + "image-name": "grid.png", + "name": "grid" + } + ], + "shadow": { + "kind": "neutral", + "opacity": 0.5 + }, + "translucency": { + "enabled": true, + "value": 0.5 + } + } + ], + "supported-platforms": { + "circles": ["watchOS"], + "squares": "shared" + } +} diff --git a/assets/images/android-icon-background.png b/assets/images/android-icon-background.png new file mode 100644 index 0000000000000000000000000000000000000000..5ffefc5bb57a3d7b39ec6ff4e96979226522cc49 GIT binary patch literal 17549 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 abpAhC#ukaM?vUO90000<MNUMnLSTaCFJLzS literal 0 HcmV?d00001 diff --git a/assets/images/android-icon-foreground.png b/assets/images/android-icon-foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..3a9e5016dca4804f37ba410ba49e70055e1ccad2 GIT binary patch literal 78796 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�^(}3k*O^<Rk=d$cs(slru;`)7ZX2<% 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 literal 0 HcmV?d00001 diff --git a/assets/images/android-icon-monochrome.png b/assets/images/android-icon-monochrome.png new file mode 100644 index 0000000000000000000000000000000000000000..77484ebdbca253297baea4a7d416233aa47a45c0 GIT binary patch literal 4140 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 literal 0 HcmV?d00001 diff --git a/assets/images/favicon.png b/assets/images/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..408bd746615785760b09e6f23fa8378d3e614331 GIT binary patch literal 1129 zcmV-v1eW`WP)<h;3K|Lk000e1NJLTq001xm001xu1^@s6R|5Hm00009a7bBm000o2 z000o20Vd5lGynhq0drDELIAGL9O(c600d`2O+f$vv5yP<VFdsH1O7=wK~#7F)tb*w z6G0Tm-`iHKq>_aY0~Jks(?}v7<zl^|7r}$Z6D83zKl}rPKY(9nqm(1YC^x+kE_k#i zCLX&9N$bH#tqoLcXPmcKv$Q+y?Cv1#=i)lM-M;VpF*D37f&y92%}5DGIKn*+Jc%6Q zfM9}7I26P?GXR(Y2HUY7+*J7(HoIvXz7Nx!@fqy~x5h%mG#Icxcn&SuLs9`>_xUud z^7&IRydH}ITNsLl({49i1>{P6S;TY(+6r8WMRB%aw0<C8;%g$$Q_zM8yV4h<p(*E> z;rtJIzFmzc5HNL=9nP`82+ya4@oUf#@Yz^2v_e<5fcycE^D5sGmvQI}3cr41BGHS5 zqZDE=37um+VRnU?J;~S+$dy<YI!gdyM<JXmR>t83jNjd>z;2;pUk?v<K~Ka0&6tre zjD#6tQunK$z55Q&KNOFRJ-OKf3wQcJCsknMhJkn;G_vur)L=Y8V{YR?5VqeU=Z}CU zC}ozm3ex+F;$E@LKqGMyplz@7N_fAP_im8!nxkN9OMfzav5EI<kN}-g&?Z2|c!G>q zeO3i60?7N6KB`a@Xqwf576E=dI{Wq;=pe8Fuime*W0heiTZW(n7BKrp?#Y*rF2M3c zzx4i+hFS<DfILW^bmQr56UMG~!^0a<dlh#5yoM+Q5+KXtOJ>*>P=l^%%#EN?@GW4Y z$)Z)i=b??9ER}*r!M6aJ=O|?;b?O;SL}2Vnx2Jch;7fqa`;;bgN%_uX<$k~Gwwj{g zXC@<j36Ocr&h_4~^mAmqvo@UgU7bA(Xy*L~B7IsxGty{0(lG9KJ@G6+=Ka@G1E9)W zlPGxI=y?(#^F9@Bv(%}F3fgULpm%%SZhI2o=KWTMD}!pmt)RVbx0MCRyx*#DWylKJ z>mNPJ0%YD-DO?$pM#BSL4dY&-pd{e^mwi|7(`GFw^DZY9WITPR3M8QA1nicpRJhj$ zyC5jC6?({2N=y4_(Q5S+eTj23=C^QVEZpro<|KMQeWwB$gK@sC^5hGVE;SL4Meh13 zTp7;YD+y6uQ-tlGF#nS$Ioc=FEuq}}alo`1@8om-5H5>%D<4B5PGEMAn`h5(I@~?& zUYJ*`Sky=ubjHp(Uxf}s*t|~v+OayU7}V|1cUW`sSVq`L(}gX7q98OaK!-qW*IMuR zU$s!riFH6Kk&VutaYLImSFDQeI8H)4!uWcuCzNsyHOyD^Ks!c)Y^pb&aGh|6&WC11 zPnH_W7MwQpBM{M6C7axCH~p~(wO?6<r4aG|cQJ+GqHn!=7&TTBh|(DnL6KaMk27RR vyCua%SLbAYZe!8bCIrT2ISQF(hS&H5cgDSEU!pPw00000NkvXXu0mjfi)98? literal 0 HcmV?d00001 diff --git a/assets/images/icon.png b/assets/images/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67c777a454236bef606387cbcfade762c0d3190a GIT binary patch literal 799005 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%0D6GSKUC>5e6k#&xJ-|pV#7n?o$xJn4rkXCpf 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%0D{)`)OC<E&M`sk=oyRz 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�#AeXT>->q%h`uRy{ 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 &U<9S#B-+c>y1>Zk-BJLy;7ZO=KIARW@SYej^I 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�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%0D3ugiR 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%0DF?%ivD-6-b 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�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�oJiOUI}$|tRg#K?B~xgYpiiRZI-q45yF z!1oz$hQj8F(d*ZIu9H>%>Qx2s)i1{1`=d{z^f=0hc&=fMNGjo$S8NSkiM_Y&1V0xc zXY9jMX9m<P5Ov~nhRH|CB0Ac1*AjR6XKiN`T;D%W0%C3PJm<+UP%Wdou*$QSe1N>^ zgrr)CuX{;&W>uwg;G)Lq{uQ>OE}nVrZFGx14|+Z`j-4;<{}=jy#$|f<m?v-z!#12O zJy5$pc+we-ocum{VeJ3@S+d;(mvgLs7O|*O=p!P|I_&{r!_F^L`^n_@^hIIppcsUv zqh;jJRKA1jU9^B?g9hTm7^^$$&1+?jH$Zfmu&Ke#crZTo(RhHq(VoAuZ8FJAe>S}L z|E}@&0dYrwB~u9%IdDZHOG>raApC}QTH6wE!ahYHrAQr73A8U;VBkH3Jg1DKL2$PP z)DrB`&d>!yXgwZx7<2lNm&0KovqC+5H;6+k+cCNvhT8+NGq!M!%!mn83>OSYfGfj5 zBYcfF=RfRlyABwD3_2W8#)uT)aic|BuxFj-xbUZ|1suR6BMf?r(WmQb?nzpNJrQ5c z^08quy`t!&WB|o@I;+g{G@~fQ%~cbZ!NQejb?yO;AYHX62%N0X?fk>4?p0dcaLTA) zprIfUHE0+U*1(t>Ww5jo_-KDhL4oT)r~383@fGd;=Z=Ie2oTEhBOnkkTKc5jWs9>U z_l(%f@q*O=?R2gPc&jN*@%qSWfynK#JyC`7>iHYpOb~HA>sBWJ6*FdiESk#&g`0kx zum$U*c4C_2(wOnm^6z6fgAO$mC$sjc#!%@{5-f;Z#Xc9XHruN$_@?>ldKq=e?<cF& zfOZ;YfUgq%lxBIJrYwyjys23r;^R-=#(({X-&saDalH2=A4bNtM;?z-bi<k)YtBEA zeg#bI@kp9M0t0wprWvRW8w73akxmXNq2vx&&`>VVyTu}lJ<}qEkXbW|IFUuq(22)= z-#q}c$$xp71`bJW8Yr7CnT{Bv3TJbyGgDmtxdkF*k&k5o15}ysCX!W_1RtTezTjA% zpXs>bV8)uLy>K2lY5{_jMKWtOjDtdSN=227<3YnfAajH_H~JZkUEg}z6l;eq2Ah*R z$5Drz(w2W5a2U`LGl5Z^U4B;7S7N#dSC#l&5q>Dv^%vDC<oR}H?782q*NP>p0+&FP zprCPTf(XUF$S=|iO>a6F<Y_uL>*JxVbM37(Z)?$z5f@3t(=wvcX+HAXG%b#CTN)NA z(9|fG|8OZc`L{LXh;_^!0-zdOnGMl_Mnv)2sf_DDz>lwr63j8=coNvo!CcvX(p}Nc zE?5&{Or1=*MkW<l+!$R7FcpKZ>ZEl?leOie%$_SUmq32h9hSAE@SN0G!vN<{cUF;2 zbX`HWhH6Jn>5e4-@$)&sB+3{ote}h0xoi;G4%A;^LFAd5#&72dVG&MjrRjr&dkzz? zE!h+&Kd-Ufk{Q9J=>DHOXwh34YqwjtKD32eO>`B&ul@DwoTLK@A<_^_gf9SFK%~E5 z{I#TmXN&fKI3cDYQ&6cGOqj5uY6<KW)(atmj3LlLHD$;yN%T`=H&VQb6~;oP()Nh< zS=vsuWsMM)q_zO8*D+P6cHR>dv}`!8cPRM3U1#?HUH^yaww2+Zvs^f2>K0^tVS8nz z+y85su6rypt~HEtT&7)uYSf5-e;;6iWf~;;8S87bQ2<N-&=pi$7(ew5Y!_G$D?T|- zVNEEytwk!tgkiT3!(t*vr}?{P9B;w4Rf3MWvr+(+y5auUwxlut1SBG7zatD${*w$E zs6LAupAhVz6Oy>cAo$EXa?3y%@#zY6=xH?y%_`xPSR`p$`)J9;vMr#p3(-Fl^AR|3 zi+dEB8l%B|`RIPmZC}@d9X~{+j!XccjLhjuV1br8vSWse@lUxCOA0NJV6hZ}kQ5ez z4M%Cg07ke8_fm)fmXmR6bkWv=Ah&D(S&QBoWzKlO0xp3R0D`c>Hkff}^;2N^NS=<& zvR*vSD5DhvEK@iH5QE->S&BgR7zGyV1PeI=B1Ye$HLeEguBl%j-?TZA0ok(#Nr3}{ zv=9{PvaX4q#H(VUplQf?4<CWk>zBXw<^0R<y|ZQf$)e2WuVL~Z6y@9J#=<K)&i(!H z;Z5lV_Ov%xa3Aya!&L&l6npeG0PWF*(Gu)C1}urOCk$e?9|YVIpG$@kd<J?OJ>f(} zC-p)yf_=NpsDKGe<cFCiS*|cqWS1d5S3M8@7!@}|A=Gvtd$rFQw^f+J&}#D9BnQDL z+Kk5eSKeq6M;dY=Hq9XA0q$x!NT0^l;gyB_um9kq_|Eq}N5qi<m5D4XTbeDIn18Y~ zqL?Y+t4O?7-gC_;?*7gz28bsV$tAf7!7m!=bUsi902Q4Ee5I`z3=XsSNryc3F?c9H z&C-FS$4&k-Yr{lhM+oP+uwd#w5LC`y$S~7^rWs@8VCWcY4_5qyz8g0o|0HhVzAL+Y z=!@ch{b{)G8Q!gRuERKEXP<T-1>B5y5nRQ7Ogi5FTw%?b^b=P0>HSO*7U_4r4mrBH z86G@|IwSzLMaEv#JGl;mY~i*{_adW8!W-9fOmjV_Hp}#XgF#=%)35m+hJ(p@yiorM zS_TY7r6NKG-WVE9(}1<0Q=Jvip^S5DL~i{wp4RXY^buU9l+m;f%Cx{xB+fc8=hVqc zyI2_&<I!e(&7p9;Y?WYTgEz~Hr8HSTh7Rjm1gQFBP5?!w;@}l9<htT}59j~4zww>^ zoclTyphNrRV}N#?azQDa;tr_smXl6@(%<ej;7H2)Sl5a}x}lRwy9U>9sv}~q=RfHS z<C~4uw9O$DhmfRnixU*o8sYQVb5W2uI*{5r7)gMsJfMjzr>|xWCQd;Ew#)IO`$k*f zjZ)tj&j~)NV8B{cgi*Pl)6sUtJc)?*$TPMZo+NXhZy~if*S>a0VC(x|{MX-&|Myql z&52$m)Mdm}pn^Zc8L&j#O`aD4l<3Nco9%v=H?UVkpBfLL-{s$vamGD;mICFU)vuKQ z)2pMj4}bwnogtOBxNQIAIb)RnsrZroFWo=9)mSsmsa|*cKU&XfIsr^zKgy<4zs?v6 z|DQ5<N>Q42;bRqqfobI}p@eNH_g%C<BU$kQCXgrpYBHy0oy7vu^}UXz9EXBT?nf93 zf<1D_Mb5<J+hm)UVN($qXPkW)CpqekjFp(X`5oYD;;^k^1^bxqDgW{vK|?5C+M?3M zN9BfV0z3MW9CU_-&7jo60Ep<+Ow#vNarl%d(n<iC17t9!aK9@B7ih4D7_qZa7~^PD zw4b=a2wk?S8ZD|Y^b<@1W3VmL;yQm8wWfo@3dhDb8I%;eE&%3;YK03Hv+0~~mCX)g zfgzV-N2^%G^aA@>efnc?xAB<ml#!_YKN@p0-mH^CUus8>%wooA2{|K_6!MdT4aS=2 zu>cI^^3XtuN5u@aYsxGekWz+Xd4#M)RedCIR}6qrF{RN(z@qG@1PM&Z0WKAtZ3Imj z+P1EBJx{vw*S`Lh^}J1sp1W}|3NRQ9FvWV6XPvQ?`k&C^tR`>;eAYP%xY4tXr|kW| z&C@*T7ptZbD-(g4z-Xz#g?69)o=Fatlnf2F$<A<2{vy9I7(gtkG=sq%nx3notQ>(d zFLMp%!m*51#x<~IrBPV6d|5TgJWf{Y72Eq1SJ*lZ6JV|}3|6nS1%~21!jx})_-S3` z{{<t?Il9T#PV+#`$Un#cOkl+ezf38;Ij7^KG8`nI@da=^cZtcMv*CK0c++^!NxSlz zEiV9X;Tx0(DrQao&xzDauOevTLEH!<Jl>8Yq9CIgGc!jJq{^Oz5hQ~1@6uL%>|C9c z|J?iTL)}t&A%w=KnT$Yn!1dkAUSkdTZ>kD#3l5?4$D8^E$tn>#$K1!C_V?f+hOr#` zq;-;s{#emyk3)3bbIfVgHyS0sOO%N&xjg3(I{eNxNf91Z6gil+$-fdWZNLT3U0~+l zmu7@2Lm4cm=^{HtiALdJ3ygI;<R{t`Bi!{)1;MFHShcx3_x$e!tfyI>;?C_X43hOI z0AFszPZPP$QaA!vN5n1D2xGgX%X~KA|KleA#D}=)|DvvQU!rw}Y$*f{|K30Sv-;+T zpUhPIAABc$4pSuU@N*T2^NeWr1$xZw3EOhl2lgKUx5+j>p0&Y{u!w^|mOkqaBC%nA z`akHI`?}&ctwj}r-!*L&`{f>WI>MZDQ#p+W%Q)4neMt>3i~8?Gma@+R==iq>>erfJ z`JRbCy#zWx=)_^fC%>=B`akzN$oXH8Sl1(K&vo9`v#JYXbNZz(Z=bKS<X`>z2Ysx@ z*;p{PIq{HBGPypU=CgD|15n54l^UBcC+>qlhs!y9^TxRv;Mm((?eH<BIjRXQytMx_ z=)FYP#e9lWvSP@#lq7Yz^yBs69N9r8U$_{%{lCQHnaM*N`z!W;*|vqg`Ftt=&o}#j zU5279v;V{NzXacb3u76*%Xq|mks7I8RODJUogCe+C~w`-1a#kWQyXOq6?7D8^o|cj zxC2Y6@*cER;vmQcIqjC<t^Gdqp6hXvG5VkL%b?1ffQ9(gj72s~dUDzwYv^^!7{rXO z07wm3qc`=Dns>=iq7lgdBc`6kiEqAmBl{{(4H*cbNi)DkMI5F?V~dhl)Dz?^F$aif zimwgk2qUI^f}ERT41rz{I5&tuaW3=`l0-lNp*FHb*rQ6?xgkS=$ZJY+lOj2PxIc~; zgNk@0PuhY=P*--F#uA)?DjdR7S_BfYL54uX1K}_qE&ayJZ7ks#)tOq1U^+8H;v6y% zcuK1}hEZK&te(>oM2kEHqgsgB)X06!&;|?-z;E}Y#}#6H%0lp3K@z77X=p=^rO$oU z9_7XUQY}ruFl)g)P4G%G^*4Y1z4+Pp-@$Pa419#P0IVo|?3w>_y9OtdS6I0n4%&$* z2MbrGe3Z{wTO}Pc2{OoeW<YG=Ogi}G#@?``)=}bGp*}J;rw#XHny{tonbf$>?LG)P z<uWF_M((gvm&Ir`ATaWe^@hn9EFTS@7iq$fW%QP|)A1FY;>wK8SoCmfk5w8*i_PsJ zugaJV0ezfrBYxxeKU`a@Tl6v%2CnIuMo9(be>pERx=e$hmScc!PV6I#$wiJ%;@5i$ z+d=C!fi59w$akP%K>o3WJ<8nBD3O)!6NY0POp||N<7)GGMgGB7oBS8|S@&cve4#R4 zpD(%`A~=vC${&Zes*{vu9grVKKct6vk~+s+x2;yKc)Pqb<-lPLZu2>fPqY?rB(;<{ z%&-q|I__dL+K^+__5ltEw4(Crh3vh6xL1LZJ}`{!T2R@_Y75w`U{dmsB#o=0gBu5< z!F@gUj8b!>IwR=_#biMuHvPZ&!XE1-HnXIF_RETJ5$(|Tb)KanT{t7~P9Y<oY;W~* zjDrtR*zB0FApVNITOTyk>)<KcT@5E*v#cUP2=^7t4U>OP`pj`k7!17hzu5>evb^bk z1cpccbM!UW?)rS)^VIjH$wL5-SVh#Y-ahesKId=z-iL8*1>lg+%^B8G!lab*pN;Jy zp7sFkrdVRknt%dYdL-OEk>0YI<v8*EdG&U(2RI_S2Y7el@Go1MRtNn~yj{?#)FZ<7 zQq@JJ1<v?})qS<ivae&%plQyt2%x9S0ty-we{ZLM`alI7F|Di;B*@SGGi(Q+N8ki~ zI{L8N`&dj>0aVQsJW7Fr7dB4QHNxUSa<G5lU;nB2TVH!GaQ-S}zohDy?RJy@Qp^Sz z6IPYkN|8Z2_|E+UbXq#Smp5~syZo2Sf685L|0V_(AcJepyf!HRk@f_Z932<wcs1)7 zdPFSxjj&8XoSyucGW|{d)8M#+3@}8`sZiFVyN4@B{{!E#<%f$U{hu+(9oqlTyDw9> z3YbWqH8*?mA)rb5i*c|f7)Ol4%~Tbk2<LWcg&NMA{g099ZVFQ;Q)9*wVa_;$E9ABu zxj3N8iVF`~o`u}*q(3p*q4)4_8DEBQGsy@1lEI*KU&H_tV+}a}u_s59b6w+(9LJuW zke9<tEGq2y6?|2}FVKtkQ!=JU$@l+fu@PL$!DU!XP)P%h35L^H8G|t*-jI_-oM<|U zu^Dz|l!zDjCFc^ML#br>L6%bsmgN-g@9Hq-Y-X|h8N&v>W}$MMdnhy4dsrC}m5?Xs zgU1mP3|Zm{PV5n#DBC5;dDw;lN#MR=5`=tXR=?Mwf(FEZUnnJ<g@g`Q4`sF+Ln3-u z1R1TUC3a46#0F5UQ^`~U|7s=N+_vPbtb8?CWl&n&Gz!LWjugFAFAcZo=Y2!1Wo~v& z+alL3Zys;2?Eevyp)dP4L4X075~-0qi&2{VotY3<sLv?r;{BWRUoj3@vC1GQoJABX z4Z@V+c+(kdOY&b~k;WvghHiAUb2b!)1%kRS(JL^w5KU^7Sd9gzIz|g5veE#^;LQ`u z`}E!D#}kF!Ly0u&tDFHU+UQw28hnUgg&<GHq;XJ;s3@W`nH^%j-H}^zF@EbGe<wcr z-sjh4v{;ICS(m^N;E5y%LbdwrjG~JZnm)4(?2`_AOrVABO8&DfVgpH>flB_T>IW7t zvypTz<ixxD16ObIzi2K<t(h~`CS+dh!Hfg<<EQDZI;LI4a7%NPQ6}ZiUNPSs`R|Dy zU8!E}(2e{b-8hV?rtdixG{;+G49Grh>1u;0p^s=}+E~}q*%;&qA+IV0RzRhNqXQ4e z1bcj9<cliEzmG*aku(8*99cfz^_FxuXGwo?uMTsq+A~=kdp?zP@H0wC$7H~ufvpKT ztS|<HubNoyD;E|(fDwJo!JaOmt!O&CK>K=INpm2l>;IU;tzeL{#5mk=c>I3IQHd=} zXn}y4rJt!2GxvH;{$bci{|C*MfZnJ650n3Epr-%HbCUm({GS50=7+*Iz1q1hisx#W zUZ;)o@%KK<|NfibEmn3WxIWt!gN##Uc3f9Vw{d(+9O7HU`+%C~o90BRq6PtoJ9No8 z8TQe6uv6rKR}g86q>C+Z?83CZ^y<C|D)nur`jM_p>cDAVX6NWavq8G6h!SKCK7;0f z9BcuE^H0<5H2u$U@|>Zu3D|)uLHc<Yg>#6aw<FRPxibID@kjr3-n}}2NsD!DRqW>$ z4eq#ov<@DAL^^-{uYV<;49Y38xL)+}wGeOwb(#G8>Jt2Z5<(hpLzYJVf!691eAA9@ z_gDz5DuHR)b9bCK`H!3YTS%CS7B|W`5O%K-jr|XPjM_M;v5Sr=N@xQHRnz7*`ycZe zRL-$Ut8fk?B?DGwa(oDkLN7K6(RKk#F{aj^6JYKp8B<K@8ZD<Uq^*U<#vx!yNOKN< z{4&a2_ecHDG2}#!gf`UB8-q=Nxh!&-x~FAk{Mic~Q%3VlsYfXNujWB|EP{BW!r_EP z1GW94pu;2<;P0|e-ZOZzOch<3F`d-7rS{VutVG?2ag3{mSk#5KV9+_Z`xu|u|K`~G zAx2MNe2NjqQ4rndeaPA8;5!mJHa49X8lGAZ{E&k@Yyy!R&X3?9oFvym%oYO*>`{Z) z_o?>mZyl<q4e3Zbu%r69rnL;d<1oEhZF0gHXrVjG53itMu1pUe%at+0kAtaT+!WZl zS@M9guRZ1y-18(B*@7)*2v20SI1aILVjD~%Hg)}WpiH#@I5MOSbaj|nmog>Ic4T|R z!x)2}|B5k0YiDqoaCDT*(vp|i+1b2PwnM}YTqu2gwDC6|5$vCPWd93_<AuXt^SMv0 z-Ie1iojZAuPEB+jfjMT)S;YY(WCJ9fw7h6Xa!Jv&@}C*bKe6@1!pM7kj|)~~=RMU8 zSf&iu+>`TO8H}v3vDY}G6~q0yFJZ7&gn1-nf37+BO9mr82US<F9I{zl!GkIqgr=Ma ze92bb)&x^H(=qtYnz^Qp{q{;^5QJ2he0}uE+xnevd`BnIj*T3RA<e(VjK0ydWvFPN zSmqADOa4bh1T|D_hhT|L#G=m<?xRE9Rl`R_`eoP96m6pmkn7paCI8rt!hbdLzZB;> z&X7jZl&K3d$a@^Y9FW9JN_+dz?W%~tqu282=bN>K33?Ue9|x(@qqBi0&k?|L-q)BU z|5+VqT`~)Pq|C98>%~VPu4R;#C;!MmUjZv+i334`SbYb|^JQd__n(bNuGeXJkTa$* z+>)Vb{r9(ps;3y<$=DnZZpEq*m42aWjk3Y-kD6yh2LlQ^XGV#Eu@RyJ2ytksLFzHH zmP9)SGh=336&9DFBEoe^SGQ%&Jf148s)3#=SV8)KRjx_@XNZI_q}L}pU6HiJb{bKy z4kV3D)N;_Kag6)}w*s&s`8M2z)z&h-$-kKZlK&*GB-jA*zaBNw{JH)%K4uSbm7$Em zK54k*IRe*L*F!yi_nY6zk3adGO=H@fg^s+b8*)L*IY}y<0j-O|Q}dzUuZx0}Wa-bQ zCq@w?)9fwGBTJ4>!UnV;LGw)8Ezl2g_+z$@@)~1babSd%-SY>@2l$K<S<;6$g;A~i zk`BI^iRhoYrtEW1b=UvQg!9ax+U|pGa)L=}zUOak5_Nh1GZPWTYK4Ae;4`kT^HvF< zf3^7svqhldFMse({PNe{?*L-t77R*@!BE$XR}cC3Ltjw|!Rk($`SJniWEaj$u>ef) z`l37qvhoO~lmD+jz4fWr5!Jciw;&QbklEt}Xq1@z56AYZP30AIe%trQ&QE8^pXth- zZF_3G2tHsN1I2F9UJ0%j_J6L#V?Zy%phEK}+wJ=<z0ECY=jmC6cCT~dSi^aMuh!fR zM}Pv^c4L>N-9;mn&h;=1x!(BO>0Lu>2A?Z8i;OqRMlku#C|{&*t??n}w@OPYyJK(A z@A^z_#Q-;#qzTlOhDS4(sg!Z`!s)^}g9Ca@D?lZ~*w}KvnZLDSloeXTjkQHRcrO>s zY)`E2&GEMmt%Yf(a2E(#L#X|J|3nAfKG$-XK>TJs7>aUSYm_8nSKIImbZ`$9rp9kU zgR=G(DY$qwjy<DO$*~s8fU;}f*v@6lupBgkZgcNHtxksOH<f4?9s>xVIt~lg=Zrbv zhF?ARAxjIf$AE$v1Siq}Cc;nu$GF5TECroM9VM4-SOWA%NoUicOehE`C`Ub!`!V@s zOX-GKKxX?Hq`AA4*YFKYTRclQi^)(fWy?#ZYlYJD+)FoGaa`H|Uq|-8CVk&Zt|sC> zk?;LLoo76n#T9Os)p1`Q%J72w`Fn{^^-=P`i{+?VGmN`IvK6D8fqAvl402#{G9H}# z72zd~V3u~kayQ%y#!FvTH~BX*&$`ml$}xOLs`pECP@X}65WklWSqjp$X!Ob8AMhm_ zOpL3_f`?GBhs>>RG^sTOogC8K(|+l&1e7fATI>27|L{-ana@-TIDF#8B2FX%7f2d& zcs23{*-^B<*D5wG0R`W4Ib@70NVcw_fgs0$ox!1+V8Z%=2M?`wO#X|@1f{kt!c&Ts zPI8&CLgS+Y4;|c7b<-?<B)!wHB{_9YN5u3Q+$um-IL=Ir{7Wugs+nXg7e=L)Ws@ar zbBb+!1EPWPRlK5kg~w;jx)$1RjGe4f8*~RUKd0ucai6k{NAW61R4hrYo8DE4>WM{7 zoFUNhCv!TYgC<QIus^qe@q0}EyzYO}wb*K*hGJ|a5}HOl5?Y4Gw5jCei~R%ge>0d@ z$?H^uQFq2U^6x<2O(wN*D9ca!X4cf8V)!_lpqE*2)0xASeM<|G<y5&Ovg~J>B@P&B zQ?J(3Bzhp2JA&3rH%9E(1<N5!{^O9pvde$d@&MM5M=s}~dG7J=X$<BQ;iH}!14M`9 zKRBVsHJ|?QjNkYl|2g;WCu|5q$DnryZ%hG^0B0fY6|*g;F;5)J^^tKE1lg9mL#3l3 z3$tqTnzejVc)bRd3a-{LabpkHup-RPghL4sts1v4H#Yw<?~B}ndB{rXp_X)3cpq5~ zeN|?W6H|Yb{tsYQ@!m~etq({-q$d%4KYz>HeC|VwyNm6>zo6+oeQ688j2Be^5&y~m z?=Qyt@AgwtI-xzjmt1PBHBCGvJ2o4S_5jZMsMaMTAJOtJxNp5f-QBNqDq)F*pr?Y0 ztP*R^r6zvR?!3u=Uv|nDqW$mkPch4O@^4$Yg7!bE6UgMLb!%)Zwtc)N|6I)ih8#6I z;xL}dSMC4w^UBapQV(i79%b6Ljz!SGO5g&mFWL6RVYI{{cUoqpx%9Y{grvEBqp`P4 z$_%(E{}|Qcp_9t22pr0xg3N$}aqMknVuL0su94;8!I-~l03(Bq<9e+{b1<+N$uj?Y zeA^ijSaPG=!u+sk%1%lxK@j*;N~y1}WCR~(PcK#NolGoi5s@~sUle=K+vkxm1p*3O z!EvqYivNcWC{dSqF8B^gb|1Pk^9MhYh>Xz8$gq%G1Pb%S+<P!Hrfp&G^DT@1fxhdO zhoBy1CAoBx>r|;Kfp#^pHk!y;??-kJoRWI$C<<#wcC9^+Nb``tv_p|96rjK?A!}V? z+QOb=peDwgJscTAmT_86-N~D{&gO)|`<-i}Pgpe)PFXRftxWQ?uAxU>1?s`@J(UG= z2^J}5=7yO{mN|C&vp*5*eDqL<$tr*O-~4j^EVKWym-};$<E-z$`(FkNwrk6x=A|qf z_@(haZB2)$?eCsg8#y@<;m)!yk1*qUcZ3ie7&$;t260>G^qWWz&TK)0A949uW_0|^ zf@d>+1Mw<Gt78YvRR1hVH3P5go)}(Ic|FI1Ac!z9L?Fq(P_o+$YSGLzPYko_fhdA^ zef#_W?0fac-~Keu$t<*?vS@xNfhKAdm!Rjzs(_LI1$IT5?8iycf!O80gb@gT+B!;Q z=1$9o#3KaHP`te4%BnB`(Mqj(uWB^k=#JcV7v~q0m)GPUM6Z(Gk97N9gy`=MHVo&k zd(D|19X*ul=5Ac`sqC}glNiVKV15Psba**2`jU#>Blfw;ShLl6V&h51CR*(oA*jL% zt3;sKf;Z*U%}`(*?82G7)c<K?;!^{p6Pg!;L0Oc@-=9*DzL=Lyg<v$I#^urcCh3fq zGN+cXPMcmnnok)7X}$;T$v0_Fv#^SZeiB0SCgq{|D2B?=p9MO!1%K!9=s;b<FXL9R zV$hQO%^;1-T80ilJmAvcd<;?Mfc!5<r2_#waMFj44rTTDYU{fw|Jm}7Dw$pW*ZLy) zb=)wfSkjr&?RrdLI|l_EqH;v%@vV<Ot>69T$Af>+>tp|j@5kd5?}g(9bwQMxx9oeA znQJ5e(*GITwOYGg6ghh}!%%RTUMua9L?6SwgF{gsoUz~Za#{2Dx2poY2ik@{4{ag_ zi^qn#cI&*VTVWSeiY~a$pUS|#*Y`oZ>*-9g0!fGH?QqXLR5bL|g0^8!+pbu*0|D#x z<cFUR@ZOi6;#Yp*gH;c9o;9}FEHqexS#8Qc&Ic%D#Df7-9_}psu5p+qpiq?38#X5h z*y4B^yG77>rY?wcstw35;;|ZCJN7^5&O`aI5~+MWY*7SjqDEho*+)V74`L|R8Px`7 zKNhNk16EPH{$WmM9wtm>_WxCtW>^aQKh6G+rtsXIrvKTGFn0(@zpCz>TY~`+w;ze} zDN7;%C?oVW`fu!iwq4Y#X-($5B|Xb-p=p$^zD)i_c1DjkiH)-EsQ)v<=?w*eA(W9x zu*$Wk)DtmbhK*mO0hs>h)dv;j?X`1$DB3U_g=|dE#@KoWB4TaL1LxtE-@q^9{tZW- zMl|fjg27zmk3gBZX)U#ZE2HLfyo{8De||xHX$;2ojKZNRg}Ak8kr`WcHHXLMCHq?B z#XcQIyov{^(1~Zpf+zc5*Xgw86o4`4)PHCD0RL^y69WhZVX77FY~4tMiSrb7QvE90 zcd~plr7}GWhV@fmryEA5p^c1H0^J8212m-Mvs9H(HkP*cYIN9PepyC%bQ;}&1J)$Y zZQem!hYN^>a<)o_*?Qg1_4A{bF3WxA@$I#LZSDO(%~_9l@z}pHZo1vKCr=4(P`+0I zaIe|yv14EanTMFHc3)TOo4@rk6>Hz~1>i<*LQyiNNeU>k@jB;HGib{A#l4&t83}LT zmCtvXf^kzRaQ7faZTJ(MW7u_O==&JzHYV~rBDV$v%nX39F!Ok2Ubi4ex+58Mf+si` z-~HtC`0a0ew0PhkR;mS^Xe=ckGvj?8(RcyU72AQ+yan94O>-)M?N!_>&BKLalv0cV z+GCh#|02af!=gvhYjv9Z=g{GJqI}NY8;#|FQ5S7M;aboce<9at@()*~5B*ctDp*O8 zqb~E*K(O9C>@Ws9cs;Wu;Eoh63V|2$0LOe?>4)M81pqn6IKhjb&Qtg~83&i0lRlJ; zt)<no4{GaB#5oy)F<StXR6_%)Ve_Ah=j%eLt4>d+-Z`IFWkpUnXr3B?A@^gkX6C~q z3yo6BXnRoTywwZ{?u%wP$PU3I1&*+_Y~y1DC`2!=RgAnRI;>T%e2!gJHlEDMIw060 z127wBrB&n@(2=HksT?t-(4MJ-v(7+>di12^X|-{!FSC~Iyck;{_8dm<9MWaUrdaaN z%A&%`v0Pg1^|+c-S{}%EGauul7k{abx{&dKY4QEf-mX}NQgR^~=1OOntzmQEB6Og% znCIa&Xkfys!sW!;$?1yR=@#Hc6M!5&VKj=dc`N`-S`>nzF*eSoFXBhMcKy&eJ?}wh z(`co{%6<*$-B=47AY!9YSWv>Y2EGz0$T5~H;%-Z<`B$m@f1LNL?7yTY#+k=wzPAGc z#}2AjpAc}pe&rWGh!5VKDC&~`N(Icd9%paQd@&z8)4)g>t$$H*yC7(dJe^|AYoJMz zUkBsaSId8U!Rr7#-k!FOFMN$K`=2ru!Tn>&C@JWrC%N41{~mxey6k`Qz4TJ;_W$vU z{m+44*sk-r=4_Ipos7PeFudFU2*#|-83>WTX!za?4l|*Kvj<INV)YsvFWHq#DHtHE z2+HLo_MgqhNb+0QS2M{NSS-q*2aWU_#FHNG0E%;Je_?;q^nZo<&!dCGoc@rn*nLn- zH?%bf^#ue@8@%g((4GhLr)M65^9(d4U@Ly3kV3?SKG@-8wa_tUr-i1~nj}=&Le~BT zx=_eV-!^MNnZE?s+GayyZVUE8An%cybW-?Y<gU%rXjKEx><9PlTFd!mkrz?6s*M>r zg!bAjd(q+8-Mau)k{Kukyae`?0E`XB;)*z)=@eQbfN*nXSLg^&si!1aQ7nssGYca! z@X{DrPd);xpa>ahL{@=8rsU+h&FwI;>$`(~1L<*gX{*h%7Xrppj#1U$LwQy!|Epw~ z6%{pc$zZmG(PS7R*%bg6POohW^{Wr(|7SmV2Vt{asqIS7qu?)<=W4TRs}R_gBpvVt zBE9!<fNq#wfgVuU7bT28!ap;z&MdjsQbzV&=s>3ENO{)H>AxF0YVKt#yAAIVZ{1wX zzJt$SyvS%_{Te@7uplMIuh<ULqcjr`Q}{dCpI!%LhLZgRgC?8w+hphi<!}GtcjBY( ze-?EcMU(P0Z0Yfr=)!A`k^h2&<?HM6zhs76nYtL!olYwgY<@r%Q{!(dqGe!xhm82T z8p<XQgVWLG-3a&g$CQfG3FxdSumu2EvCIE4j~p`0>+z~YK3e{l;PiH=+=QUVA;%-z z$*RMQ{G`G?9!qr0mY;<h9VBVIu#@>~3~(AkVKgJZP@xC*lumFqMsC9-Li(XrV1Yq| zYyinHJuJlm2N}P)Zi2jaDWFp%Ue*821$fA}968Qyo1z~GnUMcuc!#HHFD|8*(=rgs zq=oowc#gD%6F%$Cf!{2dYCeGiOm8Ek^T0Mwll2Za8Ah1V$1z)6u)e;A)NAV>9jvZY zxW!8u`$zB}PI_&NM4B!G0O}F&vjJt0w`wr)CjTJnc*mD)QT{tfH->k{gxzbZ9#Hi` z(m<*+4I40VSR*~GPd_{3zxl)Ot|#84@XCT@=9~equ>fT|n^bAmS`T;&+aZPTwd;fl z$O1Ftp6y0pA*d@x|Lx9u+i?4L<y+Gfz}SP^uFp2)FX>YC>p#1LKlnXDjC&6Tcz2x7 z2-8(B$HGP@pbgst^OtiLJW#lso$ZJBs>R|q#prutPW^|SvhV-51PA)w{sG0l{O(iy z?O%95o*3|3$t=qMnLKE<N1YY~BS#(dMX2aOyAx(B;clF<7^DIYWeBD+Z}J}||Ikm= zpG=S);+@zaxaMz`syX)`xtI^9JlvKU8z@>iHWqFAKbl_mzOZ<GU5@>4d}0>bEB1fI z$Up3VosW>Q2R4#eUi%m_y{fDnP#C;ilpIN>O@pyyJ5X)>XF9a(HV@$n>J>qip)Q^0 zZMgxKWFmf&-N!c7Mqn%$cX<20j`Ih~U!CRFD*e%o9rjVT)6!=aMa3BUO_<0Zr~g}% z_4oS^90f>2qipH^7uRV(3CeZJ6;IO~;fl^W7L`H-<9In!x(4NS8YL~+15M&Wj^fpo zsnA5Y0pxsb0=V8KoK49Pe}hw~+qCGlTNv}3fqr5twUI|8;y9Z&h1uMJfuN|_G%T}X zt%nFfieb|+Jk=Z>k~#P;5ap^N-fp7?fGBy*JWF(_2tz$2XxdhRK#oq%&vat26cAMj zbnNFT0y~8p^qX20z83w$QvzN_U$}m0qC-V5*05w96u`RTggW6kJNnuS$T?rvpWxy? z{N-mqcoTo?|M_0yb%msVEAfvh|EE2F3jCYwtsE!YPX)`iq;zB2thj$yRB9IGED21^ zde{6wk`GTeU+1uj2|(FDN*c~L&{B}+yd5ag!N7fO20V+3Zq~FIH#7MxseW5)D(iS5 zu{QUK1-&%(ti06a|JI<>3KxS<CiC2eUSZm8aHdeLhx$j~{v>|yPyQ?h3I<N(aLF%s zLoTO=AiH{~*QADKa1KTWOm1W3zZ!o-hDdUK&y0=U0XQFeQ>llH$0B`~&#NghI7H)@ z*+w&-@rJ|?Ig<UwZ9UzrN{w6co3k%7_}D>$+;JU*gXh#EW73mm&?7>p=_8XtN7gbx zHIJlX1p!wM{D9JYFS%}(WglgMgdVEnAnD8MDdK6ud1T<dM^CQe*c2FPtw~U_wSPsg zztI0l{p;G)mPqoC5>x8uH6c8fPy1L4GJ6gCpu%B*;C$hzi%+4iGlu?LyMWnCdeP2E z1;WHXGg-6;#bZkFskUsBm+MBw=EoJ)*$GrSxyPuq9w&6%B}d0m+g{-s`%{)r89xUD z1d#s#B(7^O6K;dY^!fA4{yR^x*>DnTQDFPkPmxL>=<_)SN4+lp{U2m!(W9Q6^9V8h z?l->^fBfMmtM0V-o|Yvj<j6aY#%r&-TVNHN0lVpnpq}xo?Um!ov_7Y#l;IuiU-}4U zR}42p)VrO%qtdpF2AFAgtESt=SN`4LtG!OhoP^-D7wzuwd2Jm9;odQ;LeA)N_>;7$ zw#0foWzN6ak3JNz;D?tbIGTs<_REW{05;zf)Vj6-y#J*G0YhMs)(gH*QT_`{gP3(p zFt~3xcip&DW+RFb4sjwf7Pl1K<R5~txcQvJOq+r^9xvs83D^Xj17jNbFDwXTuVzK4 zGh@^L8B>uK2sqZvb$yBLysVxDRz?mI4whOF(D}r+P6e=G8P125^)H+U4&zHa!q9Yg zuN4mEZ4Z82$8!3;97Uq@UH^AqY<>H_e=>zf{op<^Qxs=}C(Khph$glfs%gQ3)ee4Z zEI)@rY%FcN3B?F&6%}J?%$Om2yI@zO+k~Xa3jp{D^gk{;tnmy3pZ%2>CI`9?$|O!I zN#niUua8xCKr1@I=D5yZE*vC%#=;xRIari0*9;qR`sSn6bdKR*0$x+fH=`_SsTaSw zi7`gxE3_(^Q8F@g7L?WwUr6XAdveSVi$ta#5wcOu7>QqwLO=wK0G8YvTYOG68b|?5 zMz}M^a-^AGO5~yyFCMuxkyUM5$WnPeC^LJaY8;S-*Vy|6sF#Jj&M~;|ALk*Ae8~7; z)E%F)Mp2WXFrX7oKUS&lV}s0c6rKVw<=7W(Ix}+_HGk{pznnk&9<u-El*e&7y(Axz z@pbR)_3!BV^Erq7ZHCEKKDQWzetoGcz|VS%XBQ7)!sQD*A2g~2B?bnuF?vmv%5Zi& zwC5d|Ybpim<!@o$QxIcGBY#b>fa|wA+!@BxG}eF?-Ot|M?S=Nts6(Gsd!}gSG#Qa} zLfx<3g39`?JgCwYClQ}NQe(gQdmlz8KqE#u?<H@g?jZk5R<9-wIj>gHKwijM^n~9P zp{+6<RLpTm4h1OIRq0O=a4hL8iuzQ_uqCDqj!Q<5(xWEm6o$!v(aAY;pQ+It7}Bte zAGANe@lUu%YhwZlExT7Ab}ByYDQp2kcHxMUfQ=)nz-y^wyMo4CNx>tS$?aBd<k2_; zek?`@5BG`wSUvGfLTy$tfRTp(S`7t%O68_BcBpGy<HvaLwK+yVUjIu#&e|RcyIu#L zM4`iHcbct!c{+aG^!xHCs&x(nF#rt?sC!SMUB)fYw$?(<H`gM-Tf(;q`pl|^Rm$j| zxakUwHL#AWKUTdtA*TH_z1uz?Fpp)tas>Jjhzsk})@PV`29as&5BYCBof&r0cW&k% zFw5jWZt@?Hf85XzN4|poKSut|g@i2zLTvKS_USyXlJWSB-~V>~;IngynXxqIwdsha zH_O#cmdM}uzT{xPxO>wR`y#RZ!knpBOayAL7|F5eggXojjI+?czm$KDVf5GaIyDDS z5z&PTg>-AvNkQz&*aN``FktMZZe9LoY5}yGse)xAHr-CF@ht<qVC;<Mm*=y#gavJx z3$CB}o<ReQV%y?~vG{j%qyEB|t}1{J;wjr>588F2!6cJf#xhd+AMzh?qD?=p<aeGg z^nYJ{=1stZw%%23WjVapU+3A;bjd$3cawkG|0TUHN$c?f#&2tqmfc;!bFZ4h{1U9@ zC-BSuzwSMD`(OPsKtV{f$|wdmd(f#ZU;QKd>Aweb!qRWJN`Z$C;)H_kt78*4Qh?uc zJe!*8502mVA`EtDbH{X@PhOITt_)DG49cViAUIq|CpsdA(TjXUr25U_B|89dXw4*u zS+6<8RjDACNwof()Qcf{)&^ej<Mlr%b8Li{3dnketK0i}1xs=>3aQ`I#Mh!B1dvQy z)e0Zr)(0K`@b$`xm)HWglNQk-J=*7a8}5inwbK$v0VB#Y#zhQ)%Zw`Zj)f=(4b40G zV{VgiqXO*j1LWF<qJ#j7c0TcYHbb2_H8|wtkiEufNJaay?#2_CL&k}m4V}u(B_s0! zK(^NjATbg|gGSX&0q11YgG$|kBwilJ0}B-}fROW`rxLkOQ_KNwBJTwxi~AcVn4&1Y z!55Lhoh}a)oKE9l6!??}{&Qb>r+(>c@8Q1ge~WhWyV^_Us2rDLsmJ9^@L-fDuBoXa zc&^#bP(*>-3KdFrqxSg-C~6auVF$l->8@Ll#WEQKF{4OpzaQ`2sUE|Lufusj4kAB- zm&1(oAaBITEEU2l2IV$S$UksxARvZk+i$${d~KYH>`tf@v~MgovLa11_@5@}y6|`Y z@T2(Xlg~_yE?HqYFArejZ+&n3Vcd`GR2^ra`|mx|iFiZ*#_cee<hq0Vm7kMUBhB~E zX-?CP7O6wemO({F;c^PO7xJn~I0EudSuM+|6>!g%i%yFNeiBaZVe)T=dD)r>tqX?n zdZG%p9LW$02A3kt?9xGYpo@Od0$^I}OJt|v7;d}{V>E3}#s?kfS%7dNwR_Zc;NO;A zy53)<pT*A%pg^w9bMO<IDh8&eUo<%SI7}VNal=(F^?y}NP}nfO$2Wme;x#kBWdzI` z8mzAi+3*;;;xN4!ztS8lu7B?OKX_I}*?!bOS-Y7N1QN;E+S1WD(Ct4-{>@J5Y`sgL z@fw_Ch<p#U#-<3zj;$oPO}1oq((w$6ujFKgJqDNUB;>z!sjb#EzU`2}2AS97KVQ@T zi(nb<5RCB}TR>8b-2BoGm0vebZ}Z|&AAj%j_}zc{@%4DPd}_L-1Ea7JG(Hr}B|bx* zYo@pV1#$QH#Nhk)k$xBFWY_sc+5=C#Ovrf5_%<`>g=4a3^he$#e~hge+}iI!UWF}D z`)|@+YMQLfH{sNnH#hws5oWIA1MJ)dS8Ar`t1SUV`(%w^%Kd56oRipi?LfeB`W>43 zw+j}ovjE<I*D7-~j}!6hMek6ME~FcJN;Sy<+Aj@H9YlBiA0wBg167^pQoK<p@24pV zWtMVJ!6?0^F8|^3A471on+h@=bZmk;OOGJbR<|-P*n)vIhl0EPe~$eh#A)E1olDlg zo$@smG|+_G{wK_fZ&$^a^_8S$s2Tz~7^YAe1Y#}Qrh@I>nTuwTbS}aDLJ^s{(00I# zkWV&x)a9Sp8u8NJ>xDeek@S+qqW}m3XfE!NwK?DFJZ^XogQ61wb9u;4G_DoztNg{= z!Hj<r{eRuBoG%qy^oo$C!${H59%Upq2#ctf1nULFXUPJMcf3tF0gNmvRIr0J2#yk3 zA+1nSb7($FBL<m(jMI*PyfkodPfC+xVq_cfJa4{bQ24j!&A7|}bcMLxx*Y>26cJ2w ztgi3ZVN2dq4%dmV7!6hW9iRnavqJ!9yjTP<pPy>lWW_;C<T*WZ5CV1m=Xeq-wKjl3 z;@LdX+YHTCV&`OtY;H}$915ifWZ=Y~W0e`59>6GjcJ?jWW)=o;XYD5oGFEtoKS&;n zQZBzTCLogxc4qzh*T1q({=Z)5DHMXjeC6a@`sIsUT{Z{hTI;#G(a+Ug*?lN{Wem&$ zI%}M$Kwqd7n1#w4$EP7_iI$W)<5}|j$bW?K*52f%hIVF};qkhG)0Y41^Jxmut*J$? zV&)u=7|H?jG0ZfXyKqjImMJBq&RAlT(V|nu3G(ZLl&gbsjI#1=I(@ryh<=um|IYW` z#&7@Qj~@3NR~1PKBh46@o0mpsvT%ZCP}qIqe6>@agy5!GlPHHGVKRd(N*GAw76_~0 zW0TTO^N-H_g1@Ruzt;_CR}`&rq5{B`RYCp<Vwg`Ua1tST=bF)ha`|6Bb%0M>i0BY% z{&+7(<`5Q?ol2kH{&9?4q^q0OCp)i4#4EaBgs7c+FPCh3fxdWjT<JUk;u)aTo#ZvP zzMjux4tIw!?=cRfBO`A5IKmwi;Ny?g|EJ_10RBS$HOz<3+Jy<^Jm%>%<SCw?&>tfJ zu#_=OJD@H5pZ~_KZ9cbTHm5>msY_Q~<Iy^lX4hsS_XLl+2?WB+=VBEGTq8O!VF7l^ zL1O*OD5dO|pQ*9v`nZnU1IpyzC|u*kk34hRgf>;C(mJd-+T<S!AQYg4{M#6N9Ak*J zV!rz6@iAyPdaz;wyv`eXv{+u5-~awwAIG0{uycwsx`W}lB>79uL#W-_VueXZZUS)5 zdbtUr-3FiP&ZbC_McoCOys;*kb^-d{T8|Kq<K|Angw(MZkR+y$*gkB~Xyu`|_&hQT zXqWDEJZe9Jyg1|0-<f*}YD!9qJTC#^qlfVwGWSz<$o@HIuLfw{NIQr@gT8h<->6sx z885aKT<^bpRRR3f_wy;ASMtkvxy;WVY~$pz1K<%)prh;UaN#A##9$O1?ZUy--Cy66 z8mtcLV-=KaX~5lec&*kTiBd%!Ks<on#;_WXHHIL|_!hGy(TTT=y5p9|$3CnT<G>iW zP{J})CT)T>c2Z!y7oHuh237~hVYIJMK+G$H6_A(;-^9FT2{SgH6YjHmPcjH=!I+u0 zB7(1dO($ehz|jF$2&>_AOeP?p{wi`z=T7F%+R^@xh;RV#aZAEo_L#iVmm-|xpLR<i z5YTKki8r!J^SkI$TS6*+BK_Y(20J7j9A=p-Vk=)_zudP9BbPH_1iiT=M|*CVgzl{z z%~>Dxr)b0rI8Af7i*0d$3?gVT%GM)=-56WNFt;*=BzWCM9U<cy7@1piY>eysodw!V z++~cdUY_gLIXEg2DeJQNpkj%5p;>S!M-kw%#`)<8XfpebHDi|EjR&^X^aR+5+(Qon z3j{}dow|d48$iW?_MLvZx27_pZcfGoOxhR{Q5P)E;ag4ZLqG}CNrFA2y%jQS#5?08 zay1km2|R2XuY+C!qp-?GTv^hYZPr)cf68C_`7gIUUX%Ub4(*GO(a@3c-g;88R|=dg zZ9W-YKTXlH{JbOV%<f;0j6V%P)t7(at3ID6xt#Nx*?qw$s8jw!nFdBtJ918=zyd^f zN$Q0)+{Bx)<!6L>zu5y%W4TP9<-vp7tuY7)R2}p#11n!6jU>#3j1dWZ=%9pQicf?* z121hNm>2gx-dyYUoB!zB4}a<6y~Ss(a_0W6J@lGPkhlClm4jx%NoslHn(=y;-VL3* zA5sJ=Li($jflfc^jj#KD&Y$;#S6l3$Y-u?&V?o)@^Z-;0ZXKpDBk24&>}O8EufOk7 z$RyUCg?ab>MGY@sv+ybZ(|#W=8g0xR+&A6xc@f|&%WC92?yWH?+K4;PoiR_GEx>+* z%X=AF?X0eQ&M{0^`xp-cP65MfTDU7uBU%`(`yFDL)E)fsN9%vdKd(93c<eRDp|Uv` zzm4UpT$7Gve=?Re-W0~fFk`AH1y_r4l0m@c*Q4ywZ^*Q_A*^}16K@XG!m#HWSGegx zU$V1-#m9S57WQft2A?^Wig_<~<~YvN96GGwX<JY7uKW9LH&ENL8S(9mU6J9Jl_wmk ztStyI|H+L-nr@-!lJi3c$gwP_Bzl3FwT*W;u=3gh@EgDP?ZT5zYDO_MNMt~Vb<NHi ztSPMl{-!~=cB<UOoqx6MvVYuWfPi2bew8^yTM+X4{>1av%_yLZ(PmLqm3SwHO;{<8 z!3&MA`*=POI>VDTxH>a;{tiOUgssDv`OfHbS9vF(U?ZlH?krk|!qn2^{xr4-*(ha> z{p0uNLO?*<8Eb4Leva4z0VkC1RR!?gm)>9wCnvM|JaHCO*mhVbwwC{R&KMh<s8+*Q zuno_e1?&z`S#Vv}Tb0Zt0AE(O+0p=_R>=OBvkg7C>8_5eQ1``ICZSR!gfj}U?1~ow zP2wHr3H>cRh!~3q+bL)-+!<w)f9ij1DQKZCv;POiXU`vLgzwLr{vR1`jxoZ*sgz%k z-a*i|ufb>Pfpv@4u_nn3%~Ash_Wp(Zx39!ds?L}SIxu!|^%D~?T*Ky+K@>SpoJh?T z?9E_Ba6ZD@GW$xT9r=$J^8b_T|LglB_OGQ5$V6)r$3$NZh^C>s`;;X$2fac^t{9mS zvkuPHK45Q)yj}y0V1V^KPl1NfaW~-AluAxm-inMuh8*qGn8t0_a8$Z73z7HnZkBaw zomUs0agl+*0EE-hvTN)gWkz?p^fQBP54@KI(YRr|G$?f~MoVKdc`APoP+mri;LccJ z5X%YMXRnB64jG&04lE`2(Vlkx0)T<1DOXLKX(goxYZn!ClA$z@N#bgHB-ZWkl+2&- zRvHj;0yEKL$fSWgiX!R7J>EY(hVyHG<6nt4M*<H+Sh{d1_NewV@u1%)Gyha)+CLYl zwkcNKXz%%C%zL8Lbyc+)V-*JLTyrxF95ptKIFSiya#J5xz9w7#J?JxRz^|LvM)_2j z(YFr)3@m4M^x6a$`o=cO4wlMcUE@c{KX`Gx%7RN3qYvFy8ZvR7cCYuDuXM$b6oQTb z`%LTgy5JxEFMnQt^zHBWnc0Ql9SNb##uB>H2nVX-a{gO69)-oGLTFjze*Y|$M>^Ib z<un=eoF6J`^jB@!LCAhGn6j<s^;(MM7^8WY{}}l<Jfi#;Pxqg7(*Z^!{~QzJxmu-E zrvI|4&)&1RmcTOUwK+5qi~ts+1Hk$wBgZpN>E@UzfE-fT1M*B@rt~P37QrWdUp=$? z?W{iV`01&}`GBq<KF(muNGz!;jApDG2@*a{Kw6ae3H3js8u<FmaHn$9|G@O?d#u}$ zdw&X06T_1jJL@7n>DiFPBO>uzW3oNY4tz<+LQjq0wh63N+IqG=%v+AI$=Zf5AB{J9 zn<CkBVJytHyC^_}K(e(w$rFr`26BKVTD!cV8V&X<*wle9en0UNl;O*#4l;P{*lW+} zw(phAyWT&$k<Vb*@e0zCBpI?1!mK=u^IIQ&KY#a+{%qOJ(l05q$ncy^n$3d1Kq2q| z-G@2vm|kHf3CY_3+2udBa+eszTQPRjWV}N`o~9|i6HHjLS=L9=spd>-u0IiF7mzUY zfy|7AF%ayIy5aGndBq-2sT^_F%k}^M!w~G!|L3X2#58LQ-owZ0L<Z)1**4D>3_z2` z<U|GUtJgFCG4fjX_m6+?ed#GKTdPI+C<L_DybsF%MtFot)-+J+FI|SG3G~`X|Ifmb zC}>C3p``gu{*UH+y7P+_PpW>N9Yc+9jp=Opp8%G&birVvpC-8}b<MupHcfXJbw6<@ zf<f|=hk1gim~SzgKk^$9F8^mum?&xg9~HGp4T;wBe`p&sF3Aa2nEqeJNotL&HmwGK zF^MB?!qwf7fjVK;a9`8kD>x9u?@cyG^uVdj5+!I-^{Igf89Ka@c>)y`;=Fb|d)$5I z4YK$MY{+QUxQNrx``Pw?BKeE^dS>x2um9KC%<j~SePd?X5yxq}%z5%CVaJgs`auUh zxiZluV2(&>10#g)<t||hOQpnxWI+ODig>y1>9qlKkF>JAVTdCAi^~NlPzGwEM$>{U zyPj#>@@n&!;2GoKLj^N>hJG-?kYqB`M%uvGCYmZDcm3yHzV9ysGa6OvJDv)PgE|Bp z*MH9&E&5S2-3kn$^+Ee1EAA@^Zb$|K5)OYGJx+@n6@Q*m>M>IcE8i8=l*IV}pW!im zSr%kMq;b)%NNETkN!^A@U&tt31Pz_jDjtX{T7$<mKl}chxU&D(>$xm?ZR^f|v-e;7 z=5CM6nSFDu54!XY$}(LUTDvLQHzA5FTNS&jf{8FCQ#)!%**h~;#m({GjnP4DI^!{z zPx<$D{O;O6#@rh>!-YyxQbGe>2NbP;3jXvwa#vAosA{6GF{BeMu+AnqJ57W+_z_Oa zKmcwyVw}DtXZ3&bJKz5-|Lz}rlwJA{&5=%z&6tY;aBFa6Hu+=Su74r_%3wT|Flj4M zj&LW6j@A6iARE5NT)lzy)n(l?!)cD1GV7dQ**xpa4QCU`O8K{70W5KTH$h1evVx`j zJ<pR$VdL@SxgsZwJobQbpV-z!EHnm;aY(6lXow}5s1$`cjb~!d8s7kHK$E|?Yd)VA z(163UT?;zeRuI#>!e28K+MseDyv!r`7t;R@EZ2W79f1HwZ_(-XNMh>Q+5g6i;muvl zd#`0@&>)EoDCF<9m&g-kh8eF@#;f`uswf2?Gz|+Tn2kUT0h!TH(tyJ%XdJPl$~iJ9 z;`p|YCg$q2H6q5(GGGDEe(mAzY?zz@EXdnK!r=w)yV!@e`b=p)_Uvmt>duu<X$BA- zp&b|M#+Pj)vF)y&SbgE_h2QzcxAT+F&u!{O0aG&>6YL-P*J6cmx+z*Wb~4g4u0!@4 z`Iic#UeNc8`_4Uce+_nJn~S!@*3qnYQ-~~e*Q3O|`*`D~AJYh%aevbnx%kgrVfw!b z3^9!t`rpRv+$;OS?GARMf7MBj_F4Lre;jJJZhJ<R;<9CKc23zCYWm~-Xq(^nBK*oP zydPhE{|k?<p0g7-+;&=_GIl*GdNSXdWG`ykQt$H5#nxkOQ?OxzoZE^g<UeoAsl=6f zN+5L7e$h6dUGoV~Zr@^?6n{9;tm&=WtBgZ8{lD4&6aNyeqi*&;n|aOtcg6?lmo7)( zT@|!7_P^<WPAAPx^O>Y<D~r?d9reFh$@E4{03u1T-1UD%YRVIMocNq7cbpIAunr+* z=^pf#(>BSE|7a}ZdvdMfg`GJz?F+>p3L^ZA>i?tfW=Jd*1q$u+MdOS@Xa;b)Z9pJS z8z|+1g$c|O<ggTPjor{^WYvt2K{a~%>5-4Xdo6RQ(iXm8#uy6TAwQ#pR2i#5C4{4c zdrGw-y&`y}B6?NQpTz#$BDMR%h519Cky?8d&o-174g?})jHs<4ka0X?K+wbb(J<l_ zc6iDJ8)6pYEDe{L!QlFv!L*g$u4&-8$7w!MjpGZj%*}{nb%VGe=PmY43Z&^ktf8O% zOz+g;Yl_ZM7y^6d2eJDQq%IjtE8!fH%O-|HL#G4sa}!Di3@(Q^;@4O9p9nIgg;id{ zwnw_jUW}4^G`#eF4$0~L_Z56n0DOBX7b;lbZ_<!4Y-t}xZ)0ql#(DZQEm|SH=1tJ1 zA=->VlFe_OrKc?sa<qB#M##GTjrrX%!QR&_*ZNbG$=RMk5NC`j^tU`v5)Q&mD`aRi zcQ;IKLhPJViZYCJ)$~6>Jhjs@9l!IBKdz5I`5b+WqcDg6!I@Hf+{+jv%i(?vBKT7+ zGxIHs6*C{;9%_ahOd<7SK@Xb`@fU{@(UCyAhX*8MtnxrMd6R$JJA5au_QH312SH^W zeg(|QnhZl^ZCPjGV1IRVRnZBoDzB3ARa~TNgGT2802nsojN22^9;@mZm`u;~MQ57E zJiv9REU?Nd5f8yjFBxR^4fx2w!fst)ybb=cc*mHl<S5$kQ1z8<q4~-5znbi+T!F5j zK?`gS=+SiD%<rWsNT(c0TPb131YMgO&r9#%eGLOG`R5ugKs#Okr(MT23k8A%RP61v z@$MrE8MCcvdxgD%MZshD<E~-N;aRPt)7UXl=GpUWDLZ%hmn=IkQ~uj#EX$DfTH0Zq zj5O|wjW~#Az+<X*;Pce^R1gLox_aY)ELRu~^x87Zm9z&QA<z2chZX<LH$Li-+c*mD zYqe<w7uao@9$Vs(!(rH-xdnE)Sad{13KK&rLds!n;|S6Mjf3{uqR{j-Z!ji?TcSbQ zSVF{knOCCt^TXhGSekF)DdukXS=B_=IqlAU766IS|K&QrsJ2J{OTD3Q8Xl^Mz{J*8 z#dGvl+a$dn+Up--+a0YkGVIVm{T?susOyV2j{*JK*MDX`ordrjdZI-vd|X`1_ZhGy zS@}cCoZ_?8I;QPc=?+gP=Js2X7iJAk+RN<goZyx*Wj~@>+qfYAT<N1`+gTt$iKl7Z z?0;qETZoGKpR&S87yXSMX#NQM|AeC(KzRk!_rzV=*4V;$BVNkC)(k;xR`o7^+x7n# zpfR`{@*PVetICHgK5{&v|FI%;%g7kW_EW#b?4LBrlZr<I3nR+?M=t-^BpUH(iwsZt zta~bVo(bsWu@m=&5EQAhNJ2%rBz1WF%jy3XvOWW#o!K&-P#BrQh*IJ$_lzVV3Yv~D zeQS=SU_djsB7^PNCkGmLfn>9gVd9JQW|p7|Jc;K0I;S$tP!cb}Ab18OMwj&9HSTt3 z>E!qPVUbF_0M`Op&OR9<U~s{L%xHc6_tg60@3|W&aau|0pcGol+RJo9hEbBKxd-() zHlNC~ecO`MIPU4=@#+Z(HaWg%4D%qPB<(WXU~Ad$5~5^z57ugeQ4KNzk-*$n5Wr_J zPZwz3gg!1jiC|$w8&IW3#{hPc1Q*ef4NcJav@4h?8ai|D|JBwrl^6fjR0H(q`Rk9y z8P;m&{<%U5lCt1c0qbmlb1z90zgvGfQb%$cR>-(WjA`ZxBVe8fAI@y$ifs6vO(9v5 z;R-M97Tr^E`&aX`Mp$ZJI1`~t3S<<kjh{qU8%|i8Y?t%chqM`z+tov3H7q;{Vo*@2 zj)Izb0+liK$8Xp3u>a8?e{Y;R(#r|;BG5tueM<F4XtOGIXEJ4=@DyW}rkk_ml*lma zKS~+Ak}3sXnNx>rN(<M9`C;=v!8DCUv#bXVIIHTx9)2ALsb?#LhWG6Ovr55(<bn#9 z2b;n$O+7L-CH!(!+2S(1p2pBR$HA(>DfRD}oNgy;=Ybj(UfY-zV4yP}#JP+SM(7Qf zkuj<?m#A@&LW&Ld=`R8}NiCe9Yd88M^nd+C`k!Zx0wW;`GB;W>)3HBdgrk?NAsT$6 ziZf^RgfRy~5<ju_UMi}}6ws#osA+eC<!uWDA6Y3`o3%)nH3yV}V`0D2COIr2@d6Wc zsy8vK*<oXj(0!%Kk(>OJ_-lN}$Uj!N1Eb4WS3sbDw`!i|=&ffOz*t8M+$kV_3HVF- zUwov!vbIbBR|O6SD}MJ|-;Hm5`_l~ukI_>-hU*imP?$xmVF{D}@GOv^nv9uD;DPV2 z567%F$I93pG0AK|_A~O|L6&m8F;2Z=Xfdo~6OD<K6t0ahaMtBHI+x=-Ei>43klq`e z%<$~(?_K|kcAC?S4tB?|1#A(=BiS$=cj)r~f=P|_q@#ICK<(Qh=Q;)i{P!(cLYryL z`j@}<-oy5K2NlKF@BO41RUmt1O1V>ih%KUoL)H9DI}ubMZn?VI<fUyvbFlg|@TG>S zoFAeMRF{9KgSfr;$Xc8UqTtstz7YVb`#X2NnBa>f7AlCGFYSLDhgf;D|1+j~spd=_ z!%X1DOZ{JnZ5#|BcAwDYF@6!m@__wcLp!QO829P6(n6gM^QMx3TB=SVgc6us(=z0> zmy(k~Ov+j7(I}zrWgksa>J|Mz2W>Rd!S2Aeb&6jH*x`suz7)LRph%bh6Z)TK-Z78@ zMJV(jg9Y{+h!8o#lxm!cBIW#hJ0XT`ie=F22^j1oMwA(wrB#G-5i<m`E@M1tf^D3# z<7SvKe7Pq_Op+_dh23u?@`y($>phGjL>~a*FE!W1M13%iw5*^|_Y#Wr*-Brp1LojB zw=EG`CIEqz?UO0?XWdH+)oC;%J#bTUTsicPx{JijNPX4z%S^hB^VJ>{_5>H8xQAbY zl@~1GA}v}7!R<KXKtX|Rt3#I}0^LlBr=Ve~1lH$d22&Z4o{%A8hDR%FqrgOpFJ*B( z%J|xk_H^WnvNk>XhM!;l>r%N>2l%B$xyzL+pE-}vQ<-Kjr@xQ2P76E?gGSju|IQ65 z@`{Wp8cqS;P2S&G;sI*@Gk)uN44(om%U~z9R6;@-a}uGZ1+Rf(;|a;+DFJTzqRmJy zT^fqQ`Sc?caTLfV?}xbaO3zk#BLO1}gwj<OzwD&sbL4OS-iOPWIg;`(XE1`<epqZr z<knwLjv2vGM3VpWGy<@E9Rr}X?p%>+iq5{?4#XS=DO<c9>doPGqhN8x)5t$@vufmj z4P)eg26LzyYPF?u^u9mM83;;7+Vr1Lei(slaR1(o9qZsI8AhDXdFo&z<7OBQDJNtr zyAJ4m%=;8Or2?9jL$8Y2Z+#hf2LeecI%1_ETN$JG4jL0T`S+FDguEs|BLiWs)ce=< zfBwY!AMUwS<wO*+2qKk}F<UR5ZFA9b6+|FQ)O$mBemgCjM!oFkOk{62D6a;{(e(-l z<phx~a;&Ypx8OO}dE+HUQK#I}=6F%-K!8hjOd5z>tBPO}%aqrUG?v*%in9WUJ5K|+ z;EE*(rvE`%t><Y@<S9_<U%AyUw4&1gF?~SKInMVASD3?)-be&X{CiLt;rg>3EMfH; z&za{V+WnjV@WcG{^P*#uwP8`~T2^>jrVnzv?@QM^f1iqA3a+7Jc=U!Wez0~6!(Ff# zfPs;(YNWD8NzPG`A_Ol*Ut`O={%P8jQ@3hZE@dyr_H+OT0m}dQP|~Rym`_dXVLpn_ zr%Yd-vg<+)Utf!-1khFipSa)hd^R4^@qgxTb$e<+8J!~ba{;b@=Oq(I{HOo_e>L8D zda6lw_I~<>4d=vDl!0CJg5I26sKAG_LxGWi-5i}Jac{~Ult*XOzGsyFxx*X-sq39| zSOw3Q+H~H+LIiZG``gy58bcKHsSZR=->2+<uL1zmAV%uU-gq_gUoP49>Ig<{9CX8S z+C8V{+LvCxosG>J4)y<O4)gV$Z<QPWtr$#J!UZ~q^qnaggj^bF+j2;?!kA@aCzEx- z>&k<g5`Y|@XK=(;xX^k^-E!5-ZKfJH80=2Ro2-Emj@#&AdbRtwuTsQMqW`b!j!2d( zPBRs-W;Az3R&s#FqBb*qOc)c~7>I*zL6^PN3}X>3s`v_1z~kVPX@iU%PP7OZyJHv@ z4^!acm@ZtuPkH75Awi%SF1e4e)pRLSJ#?N>evnZrCIS<hSNGxKRYP>2PM`j~3<MgJ z*^#k6G-~Bs<^U5lgYiurHXgA=*L4XT?z?EiJv=ZzalFJ!-3P`~SEnCVhQ7Ps;&VgG zGXB~J$_QKeK12j_ESthWMfzjKrXw0r#d(Atjbes(nIOj;G@{b4dQLy6DA<}$TdQPQ zxDFW9S3l^z|JOA-b(-=K=IjTZ@wOXQr9fdn9IwNk!t2lT3I>dYP{Pq(f9<2KQN(;| z2*@KO)R^MNUo*i<{x)N;bE0Y$yPhKt-{fBr-{P1z5SpdSxZjvI?uc=if$%W~kzj*P z8=cAYpkGf(j9~pqJ|!=;y$_!!XxjIe8YmptR6`sqPw(%>P>eC;_@CeT#~;O2`X6bN ziWw~4!y!EQM5yRYKKDIe-&k;uuS3BQ4ESjo&=oV1VpMv4aR_^PMWHfhU-|SfcjMwV zb`a!X);*MYZP>&%D{N)rNy2@Qn=+c4{G-+Gg+>8pTqn!m1DFDb$;u|T?1VByq-`ur zNdyRtvxg0e)4!-Qr@0r6OlTT$K~6te+`M3cAkcB~Z1K=hu<d2&U4cXx7rXJ8G!qtp zoV0WtY=~e<`R|n+JvxIJ$FBN{Eh2|@{$=!k8T}bd$jwHgV?PO==NcYFeuCMOR#|we zgYiTQSZ(SjZF}1~8nF4J*E(r8j7=~Lg1Oy{?8U0rn^ieNXFR3<!*wJ|Z()wNUhOp! zgt9}5lnr2rK9dRZ=*4#@+dtq$*Obz0eZzRgzRUH502$Z>GfGiTMzlhnHA<7m%8Aj( zlK+?6=)iZ?!HHZ!KZFiqeDHpZATjBh)5o8@t^eVhAJYT@sdIDMD|R{Z)8->KjGw<H zzzd364W`p9tGL71rY&Z9<~?jmjFX+_fCc+dcFLCamDWd(EtP(|Cn78XH~|l@7^NTM zjk=z*dF(Ife@|9im*Eg#V8Lz_LBW_k)B(?dvZ4*~A~EvZ+Z2n{jL-B3?3(?a{_))U z>J|9Kzw*=Zi(maB{NS<wk5(oi1?~TlsZc;*Q8AryDq=Wp^>212mz46)3G22AegXWd z8KELglVogq9(UI!OYJAo{vZ9n+Sa4CndnPvpJFIqW1Mo#fe!Zz`(GCwbsNpdKd-8( z^4j=fIr?zyk0%906VCqfYv{);I&#|;)+LBt=Cc)KbrVPg1@K#J0e2`ZhTMA<YA9s3 zA-53;LDFr#9((e8amwm1fp^X~kWtbno%Z~_8Eu9`;-vxy3QiNpi)#?0TV}P3(~5sV z{g3#2Nf;*bqDXdll(QLMjl*c;@YsH{BTAH522mS3ZLeeyzyw}FiQ2LX?fi0#Fjq-% z3|zz2;#rV^E1(Zj$`o#_5HrUa*k^;wUu5<a4I<?y+GED2D^vv#GHn(ad=!)f3C6TC z;M6RKJyUv&qebOq{NUM~J(-imA%W-0wb0`L5N5Tyi8gfHXcgmK2RL0rJC4cbfD6Vj ztI>v<$OIx9mVF)KN{6oOSBnkz7&VBrA!tk<ToWH&fpv=Dn#g)K@2x&rLbV0LqxtvD z87fXx4f06JiOpc-vB!S}6@TsP%>EBHECn@dY)XyJKY<It!Qal%7J}A2Dz$58oPZCT zf|*oklY-qMt6UfXF`|r)kqJ9~SMJ-^P>GqZ*C<-h&BE_sPqwwB=umUBV&V_|75qw! za9x?ctQv!=2iyH}J3!$}$$wb;?uQUFK_hFg20JL!=y&clvUCMR-6FIm=V?P)l>^`T z-e>XKfB4a3ty5k|Dma%zpT)bKHP6_L-O!`%)}*v2xHaCwO3U(Xdk`tHJi{XJ8RJ0J zL1VWsS!edzcH{P=dCjwoS*ddWoo(dEKNsXNzQb@5nZ`BmEAuhiU`Ug@{O`R4GYAL@ zfN|P*z?lK%x1HyJ0<y8~$OBt9`H$!}OSlmpv<Sye!Ey&n6M<De!ta=KQzWQoNv9}_ zfhXJq*9tViHBHaWfa21@=#<_dhYV*_q~yQ;FVO!*`cp3>c&^}YIFtjrvUy-myzjDK zode(p&eHg`_0Qt^>5#}7uC56bGEm!w#^RrSYYi$D7A;SEd}Gaguj-vpFgqY$wta;L zx<7q_D|6<^Ed^AGhm&*$Ke7VG#;I*H+Un4}a4blAg8sk0KZWQc|4$WO!cVm4-d@9m z{u+j7ye9t{a)uSOAgVSwCI2~Cp)oga<P`w;?kAtGwhx968JL=hV@+($&{p|!mr`gl znOfGCnYF2(GW`d0BQ?ATQ=NTX#_p_a*pK$?{rx*7X2XVDx9d4h6Pf01sjE4EfbW}i zCYCQ^+~QiJJa_%ihF$+-a0*7?AMF;vVC&zDKTz_Y+%|AxuB>461CD7kbp<4X0`+R8 zWYAaK{+(SJ_p5*7XCC5p*-az=8_xuAFI4_K6tAEW5v&YN5xjLp4v%s<WaA#I6c7&f zSm)(ffyysmgSHDz4+8UNbW0b({%`wT_CJFH!W47Xm;JxsVeJ1hTfWDQEc^@mKT)Ee zhT+CFwXEohZTn&0=0R_fzCxTh{uFimKAuCy01_$fth6O!`oE!N*Z&DY8McnZdH}CL znIJ3Q^}jjd9xTDUm}JXP#E(+R%(*EX(vELy4}O4GkQS&kiJpZAC5Ma*WXAu+`hQ{Z zp>hi0HJOkO<j|_v2-+w!au^Napk=_Yb<e=;>=hy;2p8krNP)~^&PsDoiMhfU({;cJ zE=^A9NKPY+FZHOWys_E@98k_N)-389lPQ^C1C{yKmZqcj7HC|Kb&f-)5=ou@oZWD- zEY+CK$&glNDW@N=V-B`k-1Km|%S-98U*|;Wkc5<=;{tZ>l^1Dch+8N~;DUZ>4E3tC zaz8rNHHb+=um3JX>M&r6TZRys{}#}~V%iXRh+xr4Yhz<HTn+V-jIwcNl=1FxbJoxQ zrFY|(nf>R9{WZGh#rwdX|5dmVsrR6im$_ZPJ)csVEt(0(pWxPD%Pb<5E#-b05Zj)z zgknqK%wosWCRWB%9J?4j`P=S{Zgfe*>J;koza_~u@!RgecWHvoOJEe;UbN;2;)6|` zlBTsAdLtV)&QeG&XtbqZ$b`397E^kW;YF=Yj-LWO@Gx{Te)IP~ytV)+z}Y3b*Chv| zf;EoUnKq)q13QBvMR*!35iBvDi{PNksyx%AHRj10kbed~gE1XnZI_?Eclc#8{_8ix z!sgo(!>9ZYW)T;N!(6yUNA4YDAa>aSD4jOrqiJpOk4Fr)h@1=aF4@z1;>z@D24Qx; zFEh5pH#!6f#sLmADpR(~q9%Qj&P=bLuKB-}lT~f7rDQZPR?}(JIYqq`Wz_}zPVZqL z2v>>0AqyX1IsQP+b)@6}8T}vBAqD<B_^3Ify+z#Q!1od5RV=^Z#m5|AH!DYa0UFLW zQY)BE$yHraWX{@oxr0Izh%F0L%o7G?-IEjY=Wa8&6jakak0Y7+&Tw6;YYB<c|B(M< z*Z-;(T#t*?{^XndoBnT2zVSE%f?7D8;vc^HQ6A(@tY_VHf#F6o(J{CaQzeL*1D+Dt z!v5gg=ji`WK0D*T_y_-6o;4atK)wZ?dLY^BoQZ&5R+v$SUd-7pMa~CNcStvS?thDV z%>p%LeT*84G2v;7=-$|tYoH_%Xtr^$ZE9z*;AUf(9zV7pFyQ*XGxv8-|7F*GAhgzb ze%<P}trsgm#+D6N6B1L*NXcM(E|{t*Qj-Mkk^lu%l;JMiaL50{9dOIl)Zn3tfx=E8 ziQ}=IA-01pzocqpsU_L!mb&{ni|^U%^ZBgLKF`y(671GI(yza7=j@lY*IJ+Tw)ZK1 z)?CWQG%QV>HY#$%|G|ThEwcTMxHHQyPVAz%+(eBPjzx8Hy}v&N0QWx6Q;~G>`VBIx z?{v@N*7(X#|7iTxFTI1@SwA}Y48@4B<ULY%CWle7+%6qC=@aa0+QKu_mx0xzf`hF^ z#Tal+42V=xf&hqM@ZJ9JyHWYFwxOM=_#Es$%qo3Z=;$f<X<Ma;SHX2%Gd^Fn{|oki z&`r!&@ZWW01cH%RGX+^eC($$e5LOzb^t0du3#S5Ek<&~^%Kv@diF^l!rzAp@$UgpH zRG8ho(*{(xS?$C%`+u}Ve;$K!aBptZcLiLPKD@%VRIczZHc{hRV{F#PpC$eWf-fXJ z`)*L%G$V;1YEol%Bm>4zn4>$)x*O-z>>IH+f(P~+BFYir5snNu&;Ss%jn$mvk|2hR z5-3#*D-pk1i1w8r5JEi!bqVI;*Frfp09v3W=Q{Drky4;$z)I6Y<;8@s@pl}WTp??s z=V{tFwnM^9X*PPav1v$J^^7too+3&CWFUbGtl<1x^f4^+a)c2ZN)12uxK~Eg4F1Td zEfRVt5F$!6QXs-QMhwjXDd3=j9v+?Xv0RCiw)Mpwoef(?N-<1-0H3)|4LoPQd8+*1 z{0IMyF4;Ai(Bp3my!_OE0iJ<bN6LF6T7RBsIhPIqm?qB>pcDK~T*q(2P7zzYi}C`) zc5~QF5N*$)jF|FklJwi7+gNU8VWOrj1w7++M|!((FJ&0Tp0(NkmZ4u5BQ2AhU&Ads zrqb6>5Y2!v6t1n>CK9`ZmuNSE!d-c3E^ah(`v3fQzY|~k#`~cX`_<|st29uJeYYN! zeO|w#lp3!dp*XImh{OOW19;8XM>$LQsrZry1{z?N9P5W(iXIrVm%Sx{bUw!RUye-C zQHK2YIRIhuAFs>5O}StsF$}pm>ertge54ABFx?6;HNY<|wjUIAjqzkc4Dt_<mHaOd zI0tyi1PDYlZXKfG1UF0jSX1l5^*`rPu+=fq;oBA?j6Lg)l*GGeksp2>V{uVG$H*f2 z>9_<b_GgX%!Vt84;*1(8wSpkU1Q5t8g(^1igB!3?bXeyOBGu58TT4J}(``DKYA~j+ zPacZvXX;=qB3eqNNj`3(FJ-4{k8l-X60U4sKsH?Jsq~;^*vewaL(J}n+OoQazsw<l z8dl7_o`#m_za~%*Ju>pI^&}OIPP_c4p0tQjROx8}2;k>-W=Sk=k0~ww-EVxq{^{@h zQERCTv#;QTXG{<Vf^^nY>>PJp3~g0mc(!ASs}j%7&!Rcop}*MM3sXx{*<s6f=aX6K zzHZ%jXe{aD$J8_;!!xWQNbEt02(yY~ljUa39sh@5c7M+29EsY;-j~XMrtN}2Qolc& zwN)8lc;wp-^bhOv!fvXVZ2-LXIt$?O&ENa^FJb{8|L1@;)nR`m|IO1kT2CK%Ld-b4 zSXQlGbxa1NEk*Z7>Si&Ibi3B%A1j=>+y6#twN+R++5f#@@>FH__x9e2QWPo#CRVmJ z`X}1|95WM*jdguuLNA2LKkh9)IUwbPH_4A3|F2)3j^etD0VQu;P3cl^j!W*Ln;Lr7 zOZ?~5sxd2D7RAg$!jgH_<4EARl!oExA~~?Jl|KY2fP#s$3fz#Ra){wujcv3tL|k@{ zO#+z1ZvM>i|Ed7kja+YDYY-lNitDh=QZzkZh(Lwaz`SU+6}~2n<|~YW^W{A;?P-z} z(Bb8=-VupzbA1${W$UNq3~Vc4MyX}oFKLo3Xpe~qy_8&_1Q|uHnQQQ)54B2i@TvU2 ztc`2$OfZ{7WB&T@GY&>#w2!BQ;(6OtUdkTfX#q(CxK9(5!&9k9f2dqGIgfqK{CS@= z(1QDLQ6lM&t7{{f$Z_)Ik;^h1-BVLm8_hSJ^+3f!<WyZyA!<o_0!87v{(3u^odw{k zu3UU5fmZ&%)@z9a1g6W6aWhL#>r!)H`O=TXSAOc9#Rl%o2WGfMtBaqr<n%}M2@XY| z%XK)NR{`{Vl(h&dMqVRDwnq<p6o7xMXKjAu<|?*s8jbbx545&`0_AtVX9KXheea*? z;D;Ri{r=hvpj~!e^tE{Eg$<LN(=-SkmL<B4kM@JKP~5I-5NQMNux$+zAC7f8T-m8B zD0P)6e*66o;}?JHn~ycUc<h`+8RHTe<M^eQ=%aLgEd=(~1~cb#I&g9?<24ax*ptpa z>~BEVwC%y0Eszs5g4kc!p>^@X`8W}@D#(8*XgKl@C$4Ij|Ly}i4$e9cal@Duwa(nJ zzzm-ezcD(t`QVe*DG~6)T$g~N+@9f>Q$pSHpD4>D46%PKW+x%}C{BWivdAfWa!rRp z;DR2m*@}>Q9bDKLeUpEa?NrPS!%M=V)52vp|2*(N#@LA5geY*(>qX0_awl2WJfEBV zQ@{Hp=)f#pU!|zN{`iy}A2+v!cq1G+*OvTv++|_bqPMa&PI$;(8ex=ovT}ud+gyUO zep-(lBl_-j>^>HMpgIJW^C!svDfy?oVUDFfR<i+rW3WREd6R!(J)N2KZ45QZhmN`| z%x`RJx}+6=boQU>$^BWJ6@o)~9|@NHrQi5Qe(&RFjLURu-57VxCpYB}7NPFnawn9l z5&zNQxHs?LIosBXtE>Y!CAX=}ps;4ljG5(q;^7#B4!8RZOZaYG<C2ym<3-va7%p5r z1Vo!XoBQxy!C-q&h2rC&f~ozQky9>2!O`dUm~tP)iz<N%J141lAaawuYLk5r2DBZu zvd;09pZ<~fYd`hw3><uc40H}#)R5j+z7No@d5X-Qku5DtVL99GP`W$gh5ge9`5nm0 zw+*OZ>gkE{lNaP*?qT-71<iQ24lC!F#lQ5|Rn^NiaQmNqXth7d{?EyzL5B<f6ZXG? z){uV&uHk!}=(}QYp8BXDH(BO(pN9Wc!hh*4kFrTJYb0k6dPRF)np45^$B_3GEhYzo zm=y@96ywyevtx8Le7_nay_~L900keFQEh^E=7xgb;uXKQS7pTBm*>w0|NDUr0(MZ0 zk&(I^Q6!KSEfAsF)yT$FoE(^~3<(y+Cu&pbLs<xxIz&v601D4W?BhdI#Zs2}69RCb ziOr45wowE6)trm8HfCI-N9d4SMw9^vG~L`nXPIq5b5L*-W54#rYh51iLmWijKj%B} zA?M=`Z}6}XF?_fJT30iCo7!R!v1xXm?78l>a(^o!nsP_dH)F!-EvRY6SjzAlfeSm= zAfUi%6?5(ETP9IjXVNdbv=P2Ria$dNrQ!wBUN6{FdBRYU_1!Sid+fS`=@G=te{g00 zpK{r%0;kv`)v64rj$}W>5#WCR;-hh*qvL32Dj@K0f(a<|GyTW#CU_{#U`Vlw!U5Df z^BM}X=sOut2ym9^tVqum4To-WhWmt=02_FrF-~@0Go@G47RQ!0C$YdWbBKsnlqQy} zY-l!~zJJE%W0y#Tl6WD;9)jWuQ!mMYBYCB#7CDtC^^3pxUcC3gbEQ32vMus%qZF*F zL<uio+T@>KnGvmptegBt#Z>Mr%8?7IFf{-d6;C=nQ)1Uu27Sp<(m^pg=m?Ylg9jqU z?Bmwy{>Ypj$1eYCEVi#1*D;t{;?giUOH~GunRZ7C;JzNW?()y@P`A16T!$4Mm-U&# z1(aeVi&4rFSLt>bTI)#yg@A&LJVGzeJ~>Z|c*in+L^K}Cy0#mn#m~)$86zabKQ1_O zBx5(CB+?=M7Yeu_(cIknXNLb0(CjLZ!s*YeT?I56Esr3E^@MnWKaIf-X0iJW24Fa6 zhj|JvgW<xTX+GAWj~u@R0ou+mtfH=?>{l7~o{HuY-;=h9s^dbLGzPze88ZzgZTSrU z3sokC{v_pblmDR|{D)2cH~lzOffba0HNb1#Y0quVeQob;AJBGSvd%D{5fiXG&|x8m zLLTq4MLNjW@_%gw_{D$m-n#KAT*t-+H&Cp*o{t$u&VZKA$32D}c2nZfgRO$g%rXob zmNIadw>HmUF7OVIoPN*%^`S8vyOrn&gYyPzCK_3G50_3sGT5a>^2``?OQuxmpNju+ zX9!%w!vWPk*vGt#5}ziVd_H;SF+6T<Y`dtGIMNf4qJsk562PY^op_Wi?JM@tRRBkP z_2<7BZ}8L_`WTJbwPA-IiKnU=5d(Cf-QPi3N&;<BZ4IC@PeQ`gP6zI}0Ljz-<!IwT zZep_eK8gU{<zE#|L9+wx#>OCJfuI0=?Gd_RkLA1B|75Nchl^Wo8d%X)Z%s3<o#_O# z|Diu|&DeH$(h+>Q<AB4Dz<2bQWB)5jP&1Ag-Nt_7?liZ1o}(jEbLd;#M%vmcbN+jI zfQ9<)7EIN9bb*kr$QrZ8x^H9|VTuY0uk40$k(Y8uUw_v4zk>NmRYVtEF_bWeEz}qt zNiJwbZVOYS1JxheDA~m~;@Kogs3CZXK*IBvMt`U@Z#0$0i3MzN8Usd|gI)!ywc6&+ z1uW}B(Wt%>5}`JOvo^<63x|bCH!?Zy>VrX~tJd#!LUp#`JVb2%%!5c*b3mdF^TDbj zv*kemA8%$#b>#4$gddtVcN!mB8{MI?SJ=8~-b?7OUKMCSfa2u-+%6Mkb__6CYEx}R z+ZmfpgQ<lLa~m_)k$RR64EedUrWxFK%{^nMl;YFln!oikpNqfr7e51?JQ3<w%llht zHzcj(XtSRJ1ZB^;y(n>ko^1i}lW!|>3u2a(q_J5Hkk`H>LFap0LjD6gg_vAcqzqv6 zntb%osesPf&t&5oBc(Mkef68xHGJS>E_s~=A1aO2Wq{l?K;9jdus}h`kRf&%Nifc1 z3wX%iU^`@MUcc}Zp;PfUY^KctL<3*@!w>S;e(#TP`fiq<QC@VSM3NfPU+wf^U6TI^ zG-!aic9SLS4rVZq@lr!NBx8aE!yXOECjU9sBb@&p&wz5S(p^0AEL{GbC1Zf?qK}tf zpGMLp6PH8P|JK}8ZPasawM`XY8B@t5%0b56)(c{V8Kcr4^*m21fWWr=Q&*of@+>po zJU>F2h8Lh-weYU5QE}Bq=YeVr?m4%6=m4$tJH<o#jWd+4i`X+PTQGaV;+w*_XFMcu zoX(#i{!`iDn*M&aPAJ@vebDJlD_dtDV|2AMiF;S@`m}P+2xX`N#x(d78N`K!2RCyZ zRW_cYG<0xh$w`iblIaeE_R&L5P-OtFY>b$oc-<_ymK2oj<{SV>=Mb$NqUQSU$;)V? zGu~-BUAzPNPl|oknB<3yTj>|t$U*(SbVQg^7+yL(5YY?OTz!}SmcIyzVo-fUb;5z3 zz{*tM^_e7~PyCbL{!V@EoA2X9vs<Ah<g@5ZWNdIi(c%2WDj6Fj8cvm|K3p2}EnuwK zit-Sm&#s3;B;wfEH}UA$3@rW5@u|{||2a7MoWOc_LEYNp;QN%j2%Qx+{~r+lO(ggE zR>yR&w>X$M`NSvu9;a!k{ynHaliL+}NZ{4o-CpXaJHS?Fz=<lCGF9`ws{qbb1@M(G zy&DrIVW6-Dqi?ZzAsz7$9(JGdFUaqFRP>?b5rXx7;Zk6TzVR^7w&m11Dhy2i)8s!# z{=<C4)iq?7I*tB<);bk&3JZQmX1NUWWd1To7|fQ!r<e#>d%@j5by~&P($u-FcIGkM zga4p10}ZSI9=yd~e_*ie|L0U7b)>IuN|g%oE_GsNV?ap@$&3tND9I2v$G)S;K`gxW zlehlLq$AvrqdH^)G|cyU(OPotE5mSog!9h>|NGt123V6O(wyA3J_^cKOeY0Q$87l5 z>40EeH7P_QcVe0;tv?Ybw`hj9TRQ=76B7Z}r8|QpBO@>VM6I0m;?=*J2+9ivw6slv ziJE=pdb@Z^cQ&3n4VkaLo{??Xen(ae<7q`Bi>+r$BWlLeN<nn3iBX+JMUJ(k@fA{B zV8jY5Ev(ZYpW>OS^A(8*7(tNdM#o|hAxU>ze>qY`jLx`uzyi30um!XCL<lNr{5T_} z5(bID?u)KT1uxEMBj-V<N76t3{?C1Joi$K6_-_LztcwQ&F3?*C{#9rEw`_lSr2y7b zLysy0M=Ez0pCHB)*)ldIbTbB;Os_b(xVJ1*0=saO@93VEe+B~F`5rO%DTV(&?-OL+ zbkvqD$+#=r#WuVAuae9t<2e9+NsIaLGV_&23-k&ZO7>goMmo+%^GI$3q>5xv<T0+N zTw6QhKmFA|jE^g1X9hFHF|s0{q$9ifI>FwqK#k{MoVcOa6+4uF@cnA?0Cbu?UDT5` z@CdsOf`xFy(ZdLW<}%|<{sl_|`t}=`3~hxX{$UOe%bwTU(*vovg%)z$bg5a{am#|V zAyhC5kcTa}x(G5z3z)DJjGWZER<fFk!U4TSnHrt0^+Z;OxI;sVb31NhjaF&hWfI3? zvL9j6Je|-9kNaQii^3YmW*iP&m_cyE|N7U!|GIT8&g#*r%!dc^bthF;np&LC_4*8l zpZHiZDB4Mm+SWMEa>hcj6aocj82!NKGZ&Dm>C@|SXcfvxP9E*`K9JonO}N=CiMJ^K zU*ibTSOVjR`BB+o&2UZfaaytodQC?5=km!OMq?l|nCxSXt+y@o`Z16Nd<9+le2}$& zn1n%gzUR*I7Ho{#rq30rTT!R2R6r!L{;W#svG?}6YaGvyw*Qm=;=hXTe;8t)F+(r{ zYdZCB$D-UvAgqf}u(#VYMV2<$QDg|Ny7(u*pZ3!`4}!By6sj)iwutE$uk)SD6{NCE z{((urJ~MLyTDIR5Tm9F?|HyInZ}QDK_qPn}>KExTK^2|*>;FoAWUOic&*opu(FX$d zA<11eIW`rxn7n!x!14Is|LTA6u+5(Ov3Uwi483yU7f;|Fx>8|L5UVyl>yyWU(=DuH zk5!CYKpEo`XUc5XHIaZ0jo5=~6<cKy?d`Gvo1E9NW6Me>poD<`82EX!|9QB0K_i`o z;CtU&bRaQ4aZ`-_zgX#f4gY&?c_&*lKH`+bAq2m2LFkH^EyoPOi)9NCCb^0yK zew1kk4ZUlpt^IOo?#t>GPnSW0h)MqXxLWAP=Yo!pncKbFP5vFDGX4bokAGAAuRa72 zS|ADT;%H2IBz#6X(frs5BU3U_By2{0m4U<cH%{t{@yAO?e~hW!YD!&?h^%MgZC*P| z(l{)_mvs52$3<MuIVXPZEm9!|nTrh1dlSC{R6`z0M9Hw|(XX$uzh8UrdM+U_OcSak z3D%80mGgc$VDED*>o+D%SWeGGrbimo4;2IxXF7)E9Ke$}3M4~LE%~s&t8rAJq@l_Z zRsfbHN13p(8Kfp4Y+JPNNTa|TZC9RqbYD?>?qfbviwy-|G*BY3g*oD{|CP_hPk#Qb zHMBEsC>OW=X}4DeZFCPGpTiP31Y*=qZF*F%b$cZx2GcYbInp_XOH5!ue}{-1dGp@# z-ak9116)y?C&RYkKc#*l%iCC#4Y?_FMvB7bW{j{o&2s*;_h%-IHQY!5v7~lv#-aK+ zh6bB{$uW{TZj|G~*mx=5{1-4Ne(5*98Q=KshkZch0sVAER;X&b3t(cRla_?!Z;M!m zVu{zk15WJ`Tt7_gnIxz_Sn5QU5c?d%+E`V$%fP?*J5fzk*t$Fij%Cxf`&`|yT1I+p zJq~k;;1g|K`paX#kO8%2z~uWVJnocsC1$?nhx>aW^&50}RF@CKz(I-k!Bxhb#GatV zN}TX9ONdi3n|d&qz_LUV+Sd4R9?)wUuU<Cbp=gWoa9=-F5dF`_0al?3TZfG%_`_dE z)GcB*ffgKM8q-cQaxat~5rTOmkdR!*C}!}xrwZ?*4@m5JrT0&{y&_;{P71FSuL;oK zSp$D926AB6<Ms}%8|8iGdthBIq8*Djc?QNs(5W8id8PV>MH<Saal9!GUh^a`Et8%} zfHIo9{Kt}|tCTBcS5))-;s@eC*JIs3+XfOrmpBz|EC|Bt7jtc%Xt8a61>KrGQ|r9u zv3@^kK{YLxycfD-;b8Q2OThTJ--7)2c)@Sb8$||)CkX=2??|A2x=}kI&})lmVg>*| zX%Kw-yC26t{_XeHN*rOjHvC`fd~DZQy43^3CT}MH_KE-_F)FS7qG0wUYZ|f7VSjff zZhGz*V}rpm`Yq`#@b{~M<Jj)o^vH|$#lQH9QP-6EeS!bzho%p&i@G^q7V_evwPR7E z;uRPyk0uxG9HsO<_QnJM{NTU&?Up2o-R7Ep&-izK>fQLtPrd^grfSi)P^@8M@L8_B z$966&Fsj%BP&z*Y*5A!K>L<p;dc<qn@BRHr`~PX}qlZs8Tpwmr1S$)+b3AyBWanyP zDTLg)CPG04+s;P5sJqkahySvja~?Z5hB~W#a7{6BIV2oRhOYow57_^t(&=*IPe;+V z2j(8s$~MJ}5#oTQ6BEva&hu4?kg>(M;W<b3f`4B~6o}pEWg%{OlhW7a{cL~DFuUK9 z+pp7Cw``)f{XX;EUqyZIqe{2tOPwNI;4-|yFJ%H9K3QjjWIAP^RK+9;!AfNg7>x3| z@#!)IXQcd2eXS^3B10A32l+}7su&@`<Wg=4_wosab7r`JrojUNkt=FQJ=@GugGPb} zP$hjHFzWdmuk2y>!BWBIN<oh_6?G+ApPn$GWw<x<bUCOpE-w^+FSVGWI>3T*f6-Nk z^{qSJyx~Ye+he5jYU^p@4H<8;6IL`D$@$gbwpI{NBS;<yR1-->G8XX!GB!e6An$r_ z2MTt-N4=HUAFB0ZIO2x1b;nS)%e7j@ZU;Yj?+GUnbBZxTn}BC8=%s|89)ox*bL}tt zU;cYvT>BC~*0XI!sqNYS8nB8kTVIjyt{Pa)ZztOh@QJ+V=*%SO_2Z$5w_RQbURwL? zB8?_RdF{4#EkIHY6)Q&0=h8m#OP@Qql2wkKlYuX?Fq-H3CFig$+ZZ`MEE`bbz@_#j zOMVsB2{Hwmbem+f0-b7jsstu$uE%6YXrnWt!G2265)J;+EFnFFiUfQ|;|s+6*83mk zuYB#>_1U+dWSEb#Y>6Oqr<8iXUzyG&|2fuYjk;{Nwr=u&=DTkn_0F4K|MRf|AOA?T zY_&U>Qi2RdU|-*Yg!MpWBJcInQ<*&}E&>6;hO|YZpKt%%2Gc{I)sKFL_GFX)BY!R> zV*foEJguXrnrYlyhD})75c%lnI0vnz*TZd=Zu;z7k#D`#Pat^f4PpY>D!33TK*u8n z%LzMYsRTF)gVxS(2;<+i-73Hx-9NT|#5)fi|6}hSMHAvL{AlmMJ2j`nJR%uSW1ShV zgWZO_7FZ5$SO!Ft;C`6<bMG=p(^tIv)^aO@8LN&JF`yQV>fhQ6>pMa)Z+t0$4deEJ zmVVs#V{d1C=B@tj?dKkUKT_<P>Ql*ZEyIAk?G+XNU+eW0qc+Q=@fzu|)~8M$G+S&x z-hQ<4WA7ZQ%6oqIsh-OOt{AvuMsDPe6$qe)^J&TmY}Sd+dJ}_4*d;quzWHOHxlErc z;P;$w9;)N*b?RuwxCu@FTF<mh`w0AB$5E`qFYrC&e+^CaFH{$HqbCQ>WsQ92Av15k ziFKyJ6_5D%Ge^*Q%Y%oLE-hHYYDHbcjNziYWNbN4P|+cwDL&1l`K{ag7k}`b_;-K$ z^YytOdCEG?Vd5CG1Ls*`f$<WK!C#`^H1IR-PT5M1&Tmd3jGz&*l{GVe5<Qwo@cCSS z;kr#-m~y3=7mn)*r@2`WThB00@_&x)$Ig<E<%69N-k)A)tq1=(y4=5~MuxU=q79@q z(bq{#vo)h_!qd3q#9h2(9U~ipo>g0R6#?Z__rDxy`nrW>!kQ3Lt_fd3vH$IV_}_|u z@PGJPeE8gl?N&V%Z0PKBDXt2EM<DUqUh~|tvQ+L90$!{-T0y)R`5*QXp7FYIy84<+ zG~O^O|M>kgF<T=pn;OP{FC-Q!56BknSDZ<81>>N51vD*xT%1&(eaSy?y&(Q?-k5%R zydH0QSf*1aGj$B0ExH`DRiG3Q7_6kL_XG?;g%@HJJS4DfvZo^mx=RDR$@X&?02yd4 z*#Qb5)LnGLd>^u;gnhw(@v1}fmuASOCdk}~vGelC7!o((m3V2~U`mW%2%BKLDGJ(_ z^381Hyf|RsV%t8g4{Ma$w4Vr1u+Q28OEeMyv#xL=+|Paks7x(vzmlrC1p%!8F_?M) zrEE@&b4kY7+oa?#zETE{t4pmwri@V(woF^pL3N@=$sFaFJ!NN@7J-;Zu54JLa7sdk zY?+EOQRt~ad=bz=$Yhp*G*}XNB(^vT6gps_U{D&-fxtt>(B)_V?0bR{`nfJ)#C8zE z3^~$g2;Yh;l-21jE`_laoMIK@)Mey^w^-5)rSXg^g2TM703f0KdGn&K-wy^6xOUZ+ zdMDpkhM{%TE{rF~B;&HQUTW(wu%75}GhkFZGp)^<A_u}pmvXqw;zuuk{lELU_=}%= z>pGHci&PgRe9!*(69n)a+*J8$TnXCCp0biS0Q~juxn$!l&N<uITw<qLt@}>c1#K^! z6|()8gq`}_N)8c`R@wrqAQ5Yv;Bfs~QpP1aGwP-M%h`@GWQxL6ri|hy8t&Z1h)cvS z@Tou_`KX8IycI#;iQ^kNC+#z*>0*~up%KO;5w0BO`<5eQ0VkWZ0J&*<ps|Yw{vZG4 z*B9_T5$vE=x&rq!qRea)&Tzqk;*i(9#|#L;M7-zWc^Pj#42Cy|Zd~8KeRcQdFuW9T z7g>)?YPL0+dWy~twmO1><I;<v=)biIR?Aq!{cQwX&0OO^`;T!!&dvsCi`BrGIX0S_ zHl`lmm}}|T#94X*0@4gj-S^~Mk41QT>IJ>_n7+-^#TeZfRo^$jKHG_Q{T+zI<haJJ z`^h>-XG9oFsiz*-TW_)rpXfH;esip26`w~Ud7g&CP7xCX$N`XK;7yo>k)CNKe#6FK z??Zxf1vLZlp0_W3Q3vP>@oCF81NUhCWdx?29pFQ+^cw9^#~r3wd(z!@Ht?bIAAIrP z6VUOv)+VWwkd>!?$}kySn6{K_l32jrvd?SDaxKIfU;}2aN6gb^u6bU(`sT9u-{jKU zZ@+cs^Et`_oKcNAvTq<DWkKvw9A%1w3_1Z^`)LTgaPTpXhl3K!e}4NqoAO``9nG9% zyMt@eGd^hC=6X~KgIJgRiyo&805EvQRN|H8>n>mA%(t$f-i6JNKHp+&MF81ZNfYM9 zk1H;L@77wY(KK_9pwnYG<EeT-F-Z8h{-Z~v{XhTbU(f&XSN<Ywoe;1AZH6V%RJGH$ zJVp}ZT*s)07@XqZoD8xzMjuZ<kY(wvsu8#`Q{sg{uJ^#dGe-Vn;FTnjJR1h5oc<Ww z8~VBAMAF6;RQ>??Khw)CfZ}t`5kso$QsM{xQr~~=@z=qKS3B1lc5nr}g6~!w?QB2z z1R<^_&lnjmdp(6k{oM(s`H9cHy==Ar<k!B{SbWfUg{OL+IGW|`Y*%3#n4C@}t<Ex} z@fV-q5jsnxlb9Xm8Vu;FJo>Z@y`z=UIlZWglz^hF9>{KpSdlq)J?GP^t*+|3Y6mz% zcK*@0%0w!(Y81Cb(N|gGe@cFdg2cz=TR_FdNG_eH;(wEN#1Sq3aKzI%DzQCr>4dAf zr$HwLB(iw}z#L)ulB&Y?OA1xVxfLp<*cwaQZJxnUKQ3v0S$KQWnUhaculbiR{!AAL zBco=kQ1NjVvCF@;pl&4p)WTI0c!`bt5z68h^aI9SBRIKtfLTfDlcZC*?K*SPavr_M z;HfuE0fX^t)O^i)jMc3-6=TD_Bqv%RF%hBKL395bX4E_inyN*uctH&&?;1YfP=RK> z9AFpeaVOq{9FSjBXbNK;vn$aBm^gwFxuTtQ{Vh+nqg7=m?@x9J(ze3D@Blc{bDW>< z8nZnOm`z%p&M#<jOu(30-+_VqB|BJIX#@}Ydhmj3v=#bdmWro+<M}Xs(BQd%kV|wN zU^^Ol#6)KV3P0MM#Z8c%r<Ur5w1ts`D|kiaY+l)AYdt+s>3{mj{=W&Vhs~LX*hHw5 zKe$g#(~Hn)w&cqfTLbV}+XArnvnD8@U;{R}$@!n(Vw;|8sI=5#DL6*dnK_ZBy^f=x zHlsR%nwq~s1X=AeO}r-H6&}O6|2pGR_CIWHHx5RN-M@NExEom;3mW@uY}7O>^-VpJ zkovj#_3<8iAoFVAH~!Th#qYoOJ<<k7O=Aq6B^`YZ(v(3rU!2Jj!oDV8NaW}!JRhBb zB?O8zm(iIgv%)E{#j>$K5L){x{V{hbWlepDJ>2uO3<Y#sAx-&xVvqev;%-6WzV2it z4JvXZ2N9ZY1nEXOsgMOq`S1U3x~!H{f%$?vg8@H9Zc^)7!VAh^(NNgfjl?<I5-nwb zGR7*2DRtBwN{iW#b^}*Ehk;jw9*0%0aAC#hkgC!ev?^{|r|a8r0axl0pxR?2xSysy zd*Idgbf%GxrZ7(GM0AaQPPQUL>wn1=3d8ynGIf+?(}|53SG00VD=7|=d<3SyOS;`$ zmjs5l!7d62M1tXzkQ^(c)hE_Z`{{?ao}G=9g2I1}V*)_MWiBHo-w1G0w~OblurB2O zs=Pskz%wjdjJMDH(f(pK$Nk8uF#}t&Go|{Sp>@azXD~ZLN{<fh5xW9q<h-Jkyb<5m zgb@$J_~)we6yS7leV#)Va2J)+54{bv@%!(+AHVv0f3*6?7M6{utH0KdwU5<tL_;*K zt?!~3bwKMyVnUvEgKGld3mre}y2e4?@8UISI)z#9vhlEaj6Lht(rO0>%2rI@Wh__W zygsTZu~HGAfd3&`k<%UyY}=*eW8!a6uP1C7jZJ4CwS4dq$lIfj?P&mOEuZnYKqXA} zYbNQUHrfREpZ&Ej#G9v8i$7WoP-b)4*E21c!JiVkM2vE}4Wrlg?)^7R0D5|(*j4{2 z`A^CJx`eoot!dI{9RJ#`AHhRXm1QyeKMcpNMBLn1(Dxf>R=M#L_O^x(>_@S9cW3rL zEQzHhG8h2~b0#LhkH@24$1zY(>8ul?RMt$%tjL~6iK~;ek>BQjdE@G_SM*hkCl}J2 zW=&e0|HU8VKlhF-Th=}1EBAryz^jpeGPA~16Cm(E@DKx+V>gY0sZFlQ-wFa;KRtrY zWs$1@Za|U0xn8Rp1YjQwJ1R~wJdu@g^BA2ng)DO?!2k#)jT>X&fh0-g>Qx1R#G;xb z!=EB%cY#AjCUQy-B}Qe1Me2*njLIToh_%fL5m$QtnKPE+0#9XtoTwt1XJO#*FGc<= zWsx$RwZbd4o5Lh1s#=PJYeV7b>xIifo2;`QI<2^Fym_t99L!kHSO3@ZMOV-hKvz32 z1Xrn$<y=~yNYm;2=5ds=bp2NvfW~^%VYWHgkD2K?Ke8Zg1U*vY&oNGs%kcUglRTtw z$V(^HKKNe53CV0tbgiw}TA8Ac&y`-gGKcve{-qy7_P?F)F$lY-Hol?w!De_KdeI>} zp)^-@01gm*E*=cnqbzuCWF75&)1<SiPB%8hx8h$}wwP$zC1y-ysB%C7&UDe0I!)6+ z&M!2?#}Ju@oy<W8TY9ch0{x4fUcRQyfa5%F5z*_qLr#JQp8!Z#%r6YU@XumRYB5<$ zsqv^CclH@;09_xi@4f%=_1M4s;Ny_N)fZ43Wr(`Zy@jR?xsH72$;kGE6&UYZpPJFr zM;mfNCyR0Y_`%1e#Ql1t;3LykQB$$wB)Np-nfv>&!I%;4-h({!e11;t&ZUlP&Ay0o zRX%*?=@5m#|8eDq&tyzg`atGaBm{<*^lg<8Wi1q(Ecm51YT#fxCapa`GQQr%xp9|H z{r<z~c>lv%XBDir&S`3$@;S^~Ql!(B7Nl$*t%FILuFeT$>4GS=jNL~vwo8asHy?ah z79EI^4yWv3E~eLO_Fv*YEY1kblC0)4Gv-E$dauyCZ)2?=WBlNw$nSp~qX4p8MUDv3 zfdj)c`>V`CP9j1}6ZVC)e&vaK4)V<7@1qBQeD@<L1()R>I_-r$<KeWQM+z*N@`I|_ z)~J*MCiOU<&#Ox1>ZT9G`rzXWj$^JdGq7Se>YHsx>2?(yvTDqruG_m>#u&A45zIF~ zz5Vf3LDQP$`wxEk;G@DNu*MDr_oZ|ThwqlyHn>!!A)Yt~$e+RyF(AVOM1eJ{0`Hm6 z_&(};ALHDbsMzH~wwVZxW$(c}6MxxJulS)v4-3TCBGNWleOxcsIVykYCq7&6yg`%* z^BZT`0A^G8lYJXFnP1EBWyD8P50IhVJYr8<5CLlRE?Hk-DDp!+mB)>0@Hl2n<v6k^ zfvL`Smor?G!QAHqfR&AwWsC-KQN~pG`w948(`F6Cm}QP@&qCRGNlvE$UK21;kDeoN ze{EY_=V?L(tSW$kvGwm66=Q8<b=!PKOrd>B<Lc+}#b5mFTk${n?|(l2Pyg@tvOZSh zfu4+Br>}VbOWEgr>a%1eXHM&}rlEPJ@x=A@BVn}GX?=Li`6F)6EafEzzU4k#_CIjZ z9>*<J`b@nOF$W2ndc!>@;Q)`}JSUz07}J@T9bL91*cUotwhyNQAIhe%<R^5mZp|$K z8mcF3>Rk6f!Tw+B<w5t$rq4HcQiC$u0hveyKBjsDbG7v=gNX6Y3e4Gwa0T{*z}hkM zAQmYZa~GyF!lE>2Ta|)k%8ld0*(c&k=yf6p*LmxNH6~&B)`MZfL-Iy?@?duR+doCZ zXf>l;#4&xiypntqX3VOwaV7;JHL^yfMVrfR!+&$M&$=Gk0woP@mo($&Fdno<Gl8NN z)r^R2mWahMdqjO~L0{7nJ4@kL@D!f5L5-o{Xr)Q3mzo=;NgA2=2L#Q+rcDQoUE3xY z;k$&R9nBRwy`uXcKc8lFUqj#?S-gggXSH+GmWn6uQV;`NU+kBMU*Ym>;O9X<{deX^ z&z;p<YeVN-!YG$}l+|(yhKAWe4JacF4=L+LM>{#YO7_fk<Ema-WOyixTE^i7SvY1Y zuwtB}Vzf{UlXBcxj-<haeT+-2uKY_}S%^m)&mGad#{KSN82|Hs?~BW)dbX4Q?~n59 zN7%IDdM-t(b4|ZXPz$HquHP%VJg(5rGmMpvc9%?h_D6}C_XFH;5$H2I_Kt!#XItbB zN~;V6{YGNh_eJT$0xasnAydB;20*E{Cp53md#TPE1K7tlnn!bh2u=ATn>k(nW5B4U ztI~kO+?os{kBuvPZ4jxLG2|FCAvR14tasM>7k~5X`3K+mFo5=^+F({DrEVl(2z?-e zcD1&4M<!3;P6P@TgjT40KVk%<7kx4C#Z&c1A4bF$On+|q9}}i!oW>C;r)+QX+8nN; z+!yA~pXQnEZys@mw~vrs{G;zzTmb-<clIyObT&c=jltq$@o_zQoZ1H*%p#WgKLF<8 z@n<dD(^JJ~P)hZ^kFvh=VFmYr!!k#y>N7ID<h!+z5{u@6-VEDY*b95{2A_b^ABNkT z>FVQ0pz}bH{}l}Q*7u$TOR-tbhMhssf=2@SwK1@GB6^%*e7`tf_lFVr`t5?1ci!qa z(Phg1@q-@U{gBMR?oHwUa2I|64V;sA$NCfTKk&XS6Ks-r%;00-(T9&E`_A`H$v>TV zkQC!54nC*p5n)rxP&{MmUFo0_KO@HD>&rj$2Bo{}5d5RQ{lQ1s2{cUO)Y@3^11yHp z{}zq3&y8u_K+<6>**J(m1@`~0i9hat>*-v+$MycZAD;RC$Dwlzs>_4(VPiZ3<caKG zTQFKbKio?Ua6(AvY|jbgEyQ@rMKA;%J^!~l@b>Y8-hcGr$=H&qQs};HgWK#7=&V)E zCQ+zz89i+c3wC>hxpd{@n(sZL@&DvszL!7$Z-3F**#d$szf$J*$G*(5EqvB?%=OxV zz`SECg@MT%YTNYw5y+_Bp*K-QABMG&<M7jAw|iUM#n*9L&i-%~1Ix|#bxswP^ueut z_3;DXKWG$<HLxB>Y6eTs>E|%~Ph8KE?>My;j@DZ1-NN>K9$SGejB6K6%^5}s#V~Qc zF%&4t&yW9J+cy6ff9=QP|NJZ8j&Hudq71+=@-0c2B5MJ8a2D`F<~+p`u1Ae=0^)N@ zw_ce2bjkml43^z~{0vRBc={+M?1ak4_=eoWzlH}poz-)O|Hy_w=29ZF$Z_iFLQ4Rj z8p$3>t%qfBo(hgiXWRB$L?U7Ml<+UYl7Gqg819?M)>tGGVE9!}ye4EnKO!R&@B(t; zdf-NwT}YcqC&Y};DD2CffdjdP3N<84O@8DeQg2O+-JSsO)C7Ue&fKcwS@~MKd8<qS zm`65i878qo3!u<vG?|^>sM-^}q2T{!_r`XAMbzF_0%Gw*<&<C1apoyYHHRZ0Yc<gr z4rJ3Xsm^N36K4{0c_O-$<M7n3;yO6c@$b-$BsBlcm=HeV?LAHrzCt+@a938Di> z>_M?CVwqz1e;ZSjGNOc2-aKP3bT|E-SGv8Q`@7m<i9E}uMmW9UUMpmUNkBc*Gf=8q z5}+RGF<InBCMyZSdcjCpYWd`IKGUb$lEj1mbbz1n7#}~v$o>iNUniw{+$hEG9cQR@ zl6kp<52dh_XUrlPl^OMFIv4j@anxDCAjoQq!HmxK%t1oes!SipamN4XFMl?#>_5T} z8~x|>TD%W@M&-%#{GPKtAdBBM0B$91)4UlIg?XWhpcND{m)s)4>&|%<IWTNhAmjQn zxi<L^>2s@2&;?M=3SPX}(^?^cRVm=<a-jJpWv(hV`DcGorXHgUeAYZ(8hb#VmM?G& z0c0&J`>de}MTo?cv9)MhWMgE&Cqf-Q#;VC8UIzQ`f8&Gv7k}_a1)hsNBn1efIRn#+ zfUYNK1o`*e9jx+&qrIMnml1s4Jd|~sq0glMx}I%Z<2c6oN24*3DFxXQo`J`dXrwoo zQr)g!kddf^HX@vVJnk<!Mmh6K&s3MzQ!RUtBG&2Zu3|*ln&4kg>nIs_*pAfCfJdO$ zp7qW(rm!T!KBG^%?Auk%`awEx<Wn}2XQ&s2#pja$Wo+=Vhd8vbVuM?o>D6r4!(t_o z4r8W(7%S%u-9l|nc0&K5d)x*!@xOim{I3dSLgI%1WXw;-|JGyIn-lU+{LhNK6=@a9 zVS{R=1x*!fq|ky31#_XzVCZhyDOTIW@EvH_nM|@gf=)|(V5pnM+3lHOLsqGvT_f9e zb2_mKeY6fauqK25>)LbpQ;P8V{M;m-XLO^yPF8g%3rM6kn)<-KnSmYjDgbZ!OzJlv zX~iyov~}8%KkB|H$ayF(v{W3Ptb=WPus}PYcnSY0Jny^^_3f|x-goK`zWKrEpR%56 zt^=V}$}7f>m2sw!5tjz1LgMQGjjE~Ai@64LNnHz`P$9@}Cq$YYDJ*46YTfPl&!NoL z?Pw8@b6>>7sBL`BcIS!ftq%>E9rW)*z8oAL%#B(0K%wV+7MHEEe&GHp;XgwakIN=H zI|0(wj~&Ry*+<n|1IDKM7*&*QKa8#9Hd7hVoR*(`_bLANSAKjQ^nF#aQuz{Ud*rP% z&K9a1<6ttgg+Lw1%kN_Th>zrL*u>Cf6;32vzZPR`U^$q_F^JI<Pudt`2$j~HJa=|l zJsNywTUXB)RnQF1ZB?WR`yc%Tb=~ka6iiS*&({%nJXXmMFy<OPCtcPwxG<cLD9(n8 z!kG#qM)27qH~g2rW*6%p23P*w*t_&^!|XQ>wnm23QQw=!>^!i0NP+>%;WVCrc+y#t z#2FY!Y_z>iZN#61|Kr;>{-1{b`}jT?|HGCDp{I32FV+@yVnaEDW>HASmT|8cYRatZ zfxt}{^UgdGbxS&GGwybxjnJ~O=p=**SAW!)M$f{Ur<Ey75+)}hE!m#U!FYK5gIUyt zY$fe&f0OFNm?ZI$VdI67(2+ME)XD85?I<4^dtS&hJQ!zIIP(S!X%M01xz4?8B%KHV z_=K@kdgNaPA4#Ht!_aPC1j*K(C9>QaxR`o1Ph;)CDXZ0)bx@`zn}B(1#fmX<F<6C( zhesq=e-<1F&wwoB`LePYEuZXCjO=OMaOT<d#g{(+cKomZ{VxX3g<<z@5UlrnJ!9X; z7G1J;eZQW0Tl0D@AZ%}p*Ps1c-uJ?>U(&6bV&z7OlA~mgfKL+8Xk=tYp3E7p6V@H? zJM~`cTB8o~G^4ltRRb7-Ca<{<;Qy_8g`*wrpf?d`E@t1%Yr#65Tl6C>@!=W|tlW(l z%$2`;aOG448p(pNMltVeZU6KC&)1Q(l&tg_K7A1wyv)!J$UmSe5cbZe>c{Sh)KX*6 zt<=Vfov*)h-y*WeT4#=y$dUiPLV=tXsY@=p^r%dx)PFORTL@{iErA+Y1WrspsxP8$ zI?U=JIlBHY9XUNK*cYjwx19bt@?3VN2R<_^>$=zP-Szg&Sxi3!8%C)qk6^Gk$m@CN zd;p+GN<SB4ksU;U@m?6Wu(#Ax|IeA2<InlG!2lFlwj);z><GuSnpn^Hl<|MPX`YK> zdJc5!S88UQiNS<pA&49?IzPKYEpcE~B(rp9h8*(2M`Kv=zQ%lvv2;oyg*~z8Siy#E zPEvBG(*<f`@GTUi0Ka1|tnfcNqr14u9w8aTbMlwkU5ha$W#sS|R=ahl;lC+ze1T}r zK{dMiJ#X^gdc%?lF|roYQ8_A=#=XrQwoR6lY}O>Zu)-;&mQ!um<N3e+i$9F-eR!_( zfeMa|{b`M5!PAjtP&zgJ?fUJ#ITNn`Gd>{*P+|P?B9I{Ic=1}m-53mHairm&>!l6! zgp;WYrH1%g72J82Y^**3|Ko=LW-rK=IEkmVwYPg)(fqmDEnyBj5(auAQgz~k+acR# z3Z9?DWJ*1st!q2UQe$&r0X$K`vrzu-mp`9hcsCVj#bk-a(N&%Z+>*|4mew4&S0Q5Z zWUGRdSQcjFEDjhoJmLKU>&K{G;vFh?Wfq)CJ%5ekkUWg;;)kgu4o9X58gPm$L08fm z{Cd-Y)q1b({~pZJA;M4BX|ee^VO!EV+#r;|dYqF<KQKNq7VQ6;>f)R+cHo6<Zvv1u z2I;l5HI%L<jjCrBFBTzWhu-gvk^64SsjR=Oa%c-{ayc4mg1DPymCOoy{4nspsVLtj zL=cO9`BbYmgD5E5P}ta`A5hw{-5NX5V3D_Bcg#LfF8TmUFP%x%i&dbW!PrDVUp#wT zsCICh1;jHW!G_KGR+#)$xl>W5D~lzFHM%}DHX0+X*|IYnIV=RGSbKoEf6;y`8yptZ zPW8w!)(|>x-DCnHjUO{aO=8urXGa4uzjLD8FiX@{x~R%C>H9|t`VMVX;MJ_&f#DW1 zJUruPWLY$Y5wjK}_mJ!wf$bw4ehMB;h7!yel2Y<FH6nbxKL5?1`CR;4pL?^q^Mh!R zF|yC|kIwt^iX6iEuiB&5NJ92+O<+GX(ArXJ&cR;>&3q;Bt1&`8$+<0G9%HK_0Zwmt z7DONmdWu$LbIy<8Xk$_vH4ZrKeD#vqPe`!7d%R<CWZc>{<B225?KkVw+S~q0B0l5Z z=)}(u>Y8A~`hF4dG46l-+uy2hfB)l_%9u<;I})WJC5EwV135hGEC~YVo<a~!h^}R- z3H*H#6F|OxJO~X<mT(Y(GexR(kf39mjOZu#vtw<hOJoQ&yhGQl;|ZZuXHnoBN6OI& zmC&{myoOeH`Onc+C!C}8@ffVpY$`qYIISE5T;k0e1DNitc%gP<^f=&-k4NgZlFr}F z5`*&@J3s{PXXQ*yH5&uA)<2!Ou{DD@c`{_QvB}-gkcedRS_LdpP#jnkjwi!u&>#Mc z@t*-31exJgq-YFb=wrM&G{b)=MJxogKpT3vHnuF@x~$uahz-5gi&Z_W@93NtodEE~ z!pC{WcA4Q>!S*Ye<tp;q!s|1o!Neao`9E*?Z=>{uM$0XqKvrh>QML=)Cdpj1GTCN1 z*A%J+nt#>?jh6p4zoRE7@}q)BS`}-!?t}ajV4@G{!(;vsD>3|!dWrugqXo<9I*Z^t zAAB6Y`n%r_<!!>Sz;(F2Nn^haL&NxJejC`3?zU@YIm!|MtBZ0+446ZJe8o;!ta}c7 z`Pj+^k~e(Q2>1lZaJJw*G54w;E_3|p`0woa8tuZ{A{?RGV1g#4NO{eC+M(}8N(C7e zd!=OP2})}?T`5p2!0vRM%E^N07>ytQ``kNM6~Gsw&-J*=e-Bi6q$lZ6$K}X>UxHR4 z|BUC>*zXPesK}#!C@kI!{J(TTS2#(oq*2(%eG%;Cgo#f`#G<U=<YD{!1fh8kT9zr3 zV6b#ejMZ%?<iAI49`<3zM@s)L>PC(FE?6C+aR+9z=1D5-3&Ni>moQxg4U=NvdWqqY z9t5x!YT7f?AWeVG?^SysA?k%8x_&4#$d!UId<Ho}ShUNO%=&0Gof=cQeExajKgfz* znxz}1@6rs!mYxdxy$6BJ3a+Wn=n(KU84(Sd5-G7}wl2K^t&6Z?YQ}jvjW*##cb*eH zkP59#^OEf(Pxi#JpCC}%pS9`wJ%naGikYh4-?cr8zDUjwoZ@{g(+R^BM&)c<%ot-d z`%yd*C}%NaW!D~93M>zzYu5!RtqYa=UDq6klz<Tpwo6X?X7C0|h)(v$Cl?eQd7~{( zE9WCy@{pl6^af>8EXDPJVgda?9ApKo{4S`N(N3m;z?r&&(#Wi<bSB85^=m`uOJ8^s zf8!AVxW>Kq^oNz7y?Ad9{<#GQ*3J$uTU)_^KxsKk<Ib2<|AX=;`&Q-lpvq=2mdLp& zfyu1LEbn<rPKJN@CS*A1dIwvH5Y$$ui&xoZ-mg)Ghpn(L?I<^76(IlS#IJYfWF6u* z<!QCQTs<Q|QITfEwO=fQ%G={LZftD^Pz^m~2iWy8ydQl1o%cVEU;h7oGYeHbfP|9N zJD0Abikf_@{A|f{m;aMsBmdR%AIv%~;iSfl@amZmXi+^^gq06R^%H?e=i^DC4CkW* zfdi6{$1e3#*{-o@TTrO%vD&C7V=W>+@w9R{BM3r5=cXuva_|_|3wa72sJFvQm7OqL z(tbq9A4{h<ZlUq=ik=TTX$G#dj*oxW>Fh0#GB#Sdp6LwAt^!W}0GiCSP@u$SybUM8 zrU)q8b>(fn@Ekz5zU1&x;qg+v+nZhpteH<A|3T|z0K(A3Gf`W&7uNa2L9UpZmrVlN zW6MY#2Shk&ebynxR6G8Q+SUBnk88=&(d1eT$+3lsEl$yLn&Kb({=DNe)(1f^okini znu&gB+@UgW&3~29da&X({HGjH{!!-KGR*5A@18Wn(RD@L)3$BFuugioE$_nrIS(XT zH&hI;97}KUIE>s3^gj*%TTe><uPdG_uK>We-~T?&?|6Yrn~g?Yek<YEG#B<7>Bu-$ z=7aQlNHnDT!$$9z;!JPOnaLJ^6Z^mscATqQpQ8hFFx2rmIG3Djtp9bz4}$;PUBYGH zcbV#@+2w33Ww}3}Y8yuH^>v9RXtNjK+8%J-`}};JGl@#bvrr+~<^I<-AESbD5-2P7 zVcP-uio9CT&g$v^-7kM3zVPlFGD(IX+&a{bV8A`5LQ8aZ`6sx0B@uKb0+=a70g0;+ z(TA^v|EOG|{BK534*rKS>sKcI7!DL~NDP2aG4`|MyMmhxW&ficm;VEgCS3f%q-Egf z0@(khske`@<Uc3rvD$^7bF#EWe$v_f^WxJ2w=wb$tOhyD?@=OE!^T(>wCIx3kUc<N zC-0}()V#kbwvC0x91^=^k-`4}{*U6p*T!Xg^`LIcF<)yw;Hp1Y{2wx0{vZ{vOGKb# zQs0lWi%yCYlc6SylM~RSM%$zSyfpT)oq~gN=FqJfTht}O3@aNN4U-;pN3O~&<#vw@ zWa>pjwU41N|H>`eZ;L~30PUv65lN${yUC!@bb#4t<X$I$=E8Ok5zhLNTPA`GDu#AC zB@_y2p@<_G-m?wY-^r3=;s#V?WxY@+HA1Lsmxez_uD9$<NHAzAo!fQ*vBZqFJZnMa zHA+Z4*UQq>FaxOQQ8zEah}0ocQMN~GJY;j608rNw{H_1q=i(<m|EBqahyTR@O_T5T zFIa7d?EZ-e9<u!{yuB>|Y7n}&1eSD&yW|a71WZu(%x9hjAWqdXfn{nO?0_R|&K5q$ z2K<Y&&)wlo*CcZxm#*PX#sF8y43`=v8~L|3?R|NFVSHjnoRo;>Dz@lx%$mb0H8rF0 z)XOnonA+nA!>22A$90Zm3T_3;EB^2Q<=3x0i<(I(o95e(C}Hb>eHy*18<y!FFfbM$ zcGS%7Uy3<9t{7Bhm_TDN9uL*mr^)~IM*E+6kCz;#qN%3>*0e%TWwHVeMc>yM5$U&_ zU_`*lOF{>91q}nqKL;b-O6}<tK{xqtCP4ON9Ql(>kNaEv&m5hCCka@Y_poIkU^?H2 z86{0XGkoZ%f*Izfm<j<JBqR(BJq8TQf3t1^_?t5{qmTZ``tUwsO!VK3pZ`J_suL1K znEZ6{e_Db<V0-I%_eVM1soZq%7S?pApdmq~r}Bb$+h^}j)!GUWIVDrEyl(DEds2J6 z#Az>ypG<p_k2z1^U6OBS8GAfcMfbmhe+wX<XHBYQOU5@vB+~Fd4gZrwM?f*TWS=1a z9LpR8Jo4Ym=v5%6oiW5RXWrNH9|V?WgQ(D!CW=RO(7VT~V5S+zcl;mczrSiaTn7UF z!ms{eoX^iV18v8cVPLR>VO@}wZt$gb4>K|2hF>DE-8G3lHO^l!mV6Cn?qk}AfFQ)l zNUz~gZF4L$H%Fh*47%RaNx$QNypI1BgWL5Q{yTBgK7+12o2Mj-g}}Ty)RmY=;cOsx z+&kBE*@<3ZIX0G^u~fC$UIm#k+h^HgpMB>k{?3;_e;o{1@tXYm3ThE(CvrJX;P?cv zv7M*ozZf)IAQ#33CjU45SJ2DkKN);)x*YNkK;R&ePQ(9^|3E--@V%@O;S`4Ik7tX- z1~kC`p;d1y0L^IA2kCq&;33DksK<c4(A_7WFmyb<wEyvl;lZUW3)0Ks566G_2<(&s z5r+SCde_?>QVcsx9B!I{*VRy%W55(rcnG|p4I9A3UH;9l;dfIO7LlI@{tx15_jY)s zVf&-^R^mf0%;?HeJxp*C;z8<93?#`L8(oIDN#sfoRa<W}#=5=YDlJgmD8`fx(@_|J zt6teJ8bi}9U31MiPBDK9^s@a8OP{7jZ5mriQbg!t+i%kmKW4FbHo%N=^+huxIIpzd znRzb?fCQ?|cB<;L<So^Zsm4k4y|N@WpA`(FVSRV(vT_gBNTsyam_TaL$Gv6iq~sXD zV4wl>?*+)*q?2zMl?*BZ2)4E%3^~_5wvXv?*-t*Q|6l#t-uq9--odv7IslW`Kx%u> z61E0OYzbIHdRBYf4@4GA2(lC#G|SB>+1925(Pz9gFgQ0Us&WCAfsuZLwJ$>h-N@ec z`|QuxvUvX~k4bHMA1R~BTSGr8#q@wk#`q{TMjz%nw9L6L_dr0my^l3yFm*e!Z_k(G zU3~MazxPM^`|o|wcWnFS8xo|}(Lu&I+46^MpJLnh$F{9uNP<1_FhE@X>5Q$OkAq1W zIs}tViw;utTxqQL(jZ`wA>;d~*2R2eO2nwr97bSPl82Bw2np75<UizeRhZMKKUKtF zxuG+p4|)MMok7Q^oi@n&6JlU3Wb5Y6OU!i2vceMqFuh_~-6RPPI)<^M0&WfaGKn3R zE=CaHRJ24FXAHZB0v@7=0=xM_*tU)%hb}dI5mL;i1B|IZMf{)dsjZu(;Wcc!nE`MQ ztT!7?P(||Jhwz1{UgqrpqkAtpO>y^Y3T6KpVcXS$4mA#T9pOF)|6w3!oAr~HGW@p{ zpc_zX=lTxSIKpN+>(yHVv@k4TKDsJs%ompBqyOO95-$Hi3>o=f<GIQI#oF065a~6R zB5aAk&zzMGA5O_X2&LfBFjtRBW)%(r+?kIVI%F5f(~rJ?=MUeHfATxu301+9G3@4Q z255Ca*5{I@9CKbg*}%Ht9|p0F%=(c0*WNiVBx_PM3d*x;!zyBqTbE`1hh^_y%4lr; z31eHVU%~&1SMfjU^|poaIcmekI(9>eW^yc>&soNgfJ7|keC_97y0FHnf9`-C?5oa> zoI|5IL#g(8tWfm!8QAuC{X1X&LVW&RDvG1r_KeZ^*92*-W&{J*lppdVsXLWoZL6Mu zw_{5#mD~TMHRj)6Gb#2|a}Vu*=#P7~L4@|~Q)ie>-b6#P?@BBlvI`=m|6wO|c3!_D zKwGNp+Rh>Yqih%)F8?t)bxJdmfIuh-tCC#UeBfyZu`y>S&y-yZI`VDgVogH*$L7e( zp2*2bWm!OR7uXjG=N;Ae-(z3OicuLaE>N(bjAWbF((9L>I{ueG(JE`3bjs*@(ML{0 z8%`&OCUW;n4xfbjDus%jSQYz)0x21jdlV#F1Jh%juY-$|bKab(D{)j1K3~ietY}ox z51L~|+}zJ%CPV@S!W%P<z-ZnhJg^GVqBgo#&jvjekZ1$cPi)407igdJNUBzZ7LB}K z^-xu8N8sTc35|Y@*PQ>9&?na31g1)XjC4aD3fuv)sgtI3X$P0(@>6k~Iq^BA=+Tr6 zzvdH_ey-)>p@!%VB0yTrJZ(*u(s=VQbpGR?`wQ!_jHg-m4O$+pzB~JSgqk`GziFrz z)$Y^9&zVahP|#~RjxJ<oW*!3FEQ2Jz3~LohXV%0hEPkDCuSIM|0|%hHrxogJ6Fu^w z*qzz@w1&#YkBe^dZ_MH9mE+=M(NAug#1e{Tu$cIV<R8r9tSkkSqp}Na2HKSMbJ@i2 zefOjK#ozp9KW^p#crHffN%z)#y8LI13bbKl5}+g(7&*9@5pC|-MYe8ThEu=oF+c0u zB}>RQkZocN{K8g*dO^%%*ai;kWHCbHANh}wf6g_nEO?_qFOm3&pPL3C1@v4SHoMVk z*$i%rTHnzzr-4*K$1}J$Qp`U?Ql^(i#PxcZjxN({>ocEnfX4?NX@W(En0R%yy`HgZ z`V^U?z9)_2YXBv}pmhF<D`E^pUC-CnXPZ7~eJq0r<IC9b{?ow!Aj~;6`r{F%2vFv& zb!Qf+Kx4cr_)N@VmgNef=}<4y;G9gj0vFw35`fn;$*@*9uYt~h#)~nYmQaXKz<-;2 zs5@7iY4MX*O8IiDBKkfJ|CvDy(%bMKa<gtB{)ewO^$E+7&yoKMMoSg;O}vK<o<4`R z!+`(N4F`ZNRYWq=5!=R84TY3!VV%dwhy0V~k2n9%-*~UC0|CKY1!XAR-|t(m1>`Ek zy2i&t;<_(b=HS`36|DyMs&@XaeQ~8~q)LVv(*(KHR_VsnRX04=6jC~3A2+T^o5zN! z`6uCj-Tjln_!*n@q1h?jc(p~sef|Z<zlInfY0Gc#^{+0ntmdFo6_@KN(l{`A-CVd9 zYf{^GOn%2TqrLdqcaQknU;cdb6Vom`_uy4|BP<o)dfUV~)=(iyqC@{Si9D<_3Vs`7 z2mj}iHffBuy+A!uUDioI5D*vYNgplOJhLm!Ol)Cm>_g?-b?LL)|8(GG|HIp05Hn)b zzE#R`?;r-XW1-9cEs)P`0j>imSDGu=%kGi<7ftvG^8<lzL8rfpxI)ZN^8=;v9RF_` zfS56#!?Lb3cukD{7}}rj{aQ^aEO-Hd%HfP32L9)IWPNY$ER-7n$X*F&&H$-9_h`$E zFmv<9gA<Tesr13b?s<$+SFg=Gb}KNQ!k{FDpg*LX>D(#`Y(<l#^!fG=6TJ}^7w0|; zI&IZWp=1N6faLlP#!8-3s<X26Dr-awM{Zhzf<)T*qaE0H_m%B5<8d3D77yK!G11Ck zKw_2UWH9o#3>)mv3@9y?Icby?e54nRaQ&7vLC~i^QJ^L&P#Ra#YObYd?UYMi4<rY@ zb7-tn{$-%moKDJh%Lps$>b8!ujNcl=Pk!O;_>ca|XL}T94Z8_oBDm9_YOe$^hd=Nb zKtBF&B>_?Tk7Oj=CR|ft>q}0HL@7Fayc+wLbOf841RDQxvM;`(o=4QNgEx|<P5fh9 z?pAiWNy#uo?OGB#Tn)=H_j_&FR6Q9(^Vo;N?8!d98D#Q_1{yB|5YwxIk9iw&-N)X8 zO`^;7cSwvI{D0{;zL}38Ku-q_#3;$;G0vyN-_coRVG!vb((d;I?JD$$pkO9rUsf3S z)$-4Mg;Y)_<tpr2W;~Xj>P+7}a+DPVlle%C45{y<4V_c+UmE|yVF0*0zx$%cay}CG z^5pzBK5CbL>XX=+h&ujYT@wtPG{xI-fK!e+7`e7eCCh?bucrr`iB|@Di*CmlIGrsS zkD!XXzpz~Zd)<>*nCleY2^L5vMj7sQa}*AjbPCH0+$2{R!*w71f@}kx7y(`R!@+-r zFMECIknCnbD~EM&d0#ff!Wyo?j6LyzHh^T@szde(1*&&Jay|(GCAX&*IZNbV<;vYi zZ`|+iJm`dZq^?x}Eqz-<D;SrT2ru8SG326`Eqi7`-a2=DzmfpTlVrfC9{FFFJiU<r zz_>`8EjU2I+Ri31{14#&R^dbyH&#TOOnrcFqP;*6Ls-x>>a{}l@N)iF;8jYoe-;!_ ze>-4|eun%H%oNxzbCw{St<i$xn=J=#+E|kqQjDt*4`BtsoMIxYI1(yRKz`!>;8dJ3 zATOms&R{t8yx$|W`YQfU<a^eb|7rD>pkk5oQKmDbc2L!rT)v<hU-r+5siO-fLQa&% zX$wH8La&(p?{k(^IUt*ClFmb`6XlRs6~K?b^R(o^H56=0skyNQ#^H|G0)<9R%9Mzk zz1U_v(CIAFD6zH{;4?d0Omyt7L>X2w<3AK%%mrb~4iYkO8LZMX2lrL`{}|sPTE8UE zX9=plVRo%I{F{DKqv5}8gF^>3{8c-f+9P#${HJL%j=!G9qIKdZrn)Tdc4kmJ&K9)< zZmNZ{0Ld^tCmX{cuYl8F(za$H#5HpeeN^oDU!OYucdzRVfMT=+oz5d~ZUM5Qa)QpM zrcmU6zg&|q3WmI#ECZ(q8s(Oqr}NGB1AQ>Uzm038l7?M`Eg+*r0|e4Wkv31Xe^>Gm zBhgl7(%l^ndVT{}HjFQ0jB~7yIOJmPBL>(npgZ=*m$aG7YK@KrlBsEMuEumQU#NsK zJ)@We_Xha&+c^MW7C?vx37!eVoPZ8vtL*V(J<m!L+$z?kAMH<(6{DS7p`PZz1OKka zAs$;a4j6Ke9P)evfEo8zZakUlv&TA*N4gBGr^m3q`g1=XPX|i#(LN9QBa8dNf9~{~ z!@r()JNy2FkNiWaTL72`SgI%j{l4c#+YyEr$Eu(cGe@o7ILkTYBaIzj+Adfr_95Ml z5VJJMG*TGq1Ob3|{B4vIUuzt4f`9F?B=*C=UA=binpZ8yyUY*OT*}7kernBR%fSl@ zFF;5QK8;Y7+xQ05F~8sY`Um-|zyHToX7nJ|K9V$v08TpXndNe-uQI(ptc>e;ImJ&! zG>hJcU)%3Jn4}(|OsoU4&H>)$dc{3JuIsBlZ8$Y;I@zg<IPMJXbuP+V4+GBDInNfB zBVqd-#EsZH&9PN6i@|A){I74<<4{fhm$sU{{|HU4IpYj~49kK@$7?~M`(QMAyJ3Oj z;%m*?E*hW1dUbPBHm5GQN-Ckmyn^~VwGh$c##SxnX`6do1_j`_g}8OrcAbQMchcD= znT5DbEdEsRKkoQX^+Eh+;3$DD%m6{*<kPVQwxPLlf2`vi5GlR18A;*5At!wem<MC} zwBcCKmN#6$z||{(F=o?qYYlRdUJ25qr2L1iY2!cm4^JLYxB<bX70(!jioT9!U(llY zkMctL6p|%pLf?Zm^41m^^^`u@<UdXZr-d35NPvcw&A|I<WAOk%Iu7WU+`U?G`A-B! zuI){g|Ma)N6~Fh*4=}8d-4w!+N{O#8XN+@fbF5}1P1%(#9H5tNjUzKM?g@^dYPe(I z;GJ~0bWu?pdmipZU=g{NY8%83|30@l|7+mCK4je+is(a$Q;vVL7H*9_5z+ePv}%Fn zu%GOL&OWvX@KJv!%l*%Me%=oO4A_6$Hh>77jRgBi-}B$+-#y|V{15*^yg52(pkc0& z%@Y9hM4K3b?)ub>k7aiqo1xTWp|i%dWLGU0U8X<Xzl#4a?0+1x$Beovfh2X)R|5aL z;i+B%ZC<tiA+N&!ah8Mj!&*^R1z^j<b}%n!xVn4Z?f(I_#&5n454<;X<<J(iH=j$( zO23M@b7<=Y#3YF+;kbnMHaW+a6~pPgZ?`mW&i`;xhVZS&9uusQqvDY@^fCCKpE~~c zXb&I=iPr4{P@<7{2$}o#Tq8)8JJE4_=fH;7G<~);Id^W9mN;@@Ho|~_Y=6e}qW!oT zH?(P5UnLFMeV*=Ek3hpmtBg_HdhN)LVy4B67KM|et2}I4WQXcKJ|~pL^f=iJ(!|$< zjCaDQqmb9A=XEWOb`84~H;#vb-#rEz$<|J=W$PAA`Y~I4el+`0rNgy0D3k3o4LVy7 zIi6Q2Vutd|gZXC2MU1lYIqa-?U3;2Z?!BP^X3Ap`OnP>eKl!;g@i!jX|Ml1+JNR#$ ze!M4e1s(gc$^ZledF|QDz~oh_4YgMc4oqZJys7mG4iadCFfyn@XQnSYVgiUW=bJG< zP>tjY@81o|v}o!c%~mtO#J&CWFgsC-OiROI)9t%Pp2eG$&yc>~<zKgiZ1F}3f}>ql z4F1ma;SqQ{REVPA!F!+@udn~pU;FwpTn>%XX9D>zWX6)!CMdVXgv!>ssDsu|of*rW zu2_Pcp4yj1%NPfPx?;SUu)rw83>wBi+BmQ2$OCW4tib5uALKt6$tRO9ooC>-=6nsb zgAO2lYCLchMxG}FK<AA}BcnqKpu!I)l8)p1!z2CBQQ%qPmI9<rdyJ*qUI#NFsA>x_ znHxgiIXY2B`}1>BuPWnEBcH-oi|~i>Fz6J-Uydolurk9kEtH_`-hAQAi%ZETpAP=l z4gY=3isLG&PkleZyJ#nG&YbCs`e5DW+e6Vwg|nzP6Ny(D*O4~jtDR@*N{xwz;KF2X zCpi>|GHHx$NdN=uK0I+J@8?~}_{G&pQ2@W~RVcR{Rns$#C$JoeAUi;=Zt{Ps+9MEH z>E2!b;RxldR98WqGp02ST{l6w)gSgGN5j@K14v6j!#MlhiGmfufIeeX#Nw7WKH2Du z|I4rbSMj}%+W1!HRsE2o9V;e;lJk$4!bJgTBeW#c{b4P2%L?$4f78~)ocJ;fwB?VT zZlI<2;`qOf(iq{SPT$ZDzZ?Fiw@XUBg8xTy*tB`6f6ygXmsU9g!%KvEbTz#Y_wd@C z=2=Q~$R=_dh-$CxC~O5d`y2pV`pOmnjKRL+xqN@?XFeA{^|`mQPeIeR+wz62y5xU& zcj#A!$>4Wj`Cb00b0z<%jw$gqo$E6Ew{>3nsqu6klU^bFpPE|u0bV1o@NuJs3Lf_> z7PGu!{}*&2Gw<B{Z`awGV#CJ@yaNBZ=HON0|1}mKA}ISk#@SyT9dkZIY!wfi(4_b= zMu*9U&>EamqGH+gELD-=B{@!xMg6B6e3l>xIqGF&8Tq~XJBW=vZ^M7`hp*oc1OGcs zc(C&Yx@NTFG=361v`}+BxF9*0+??;y;?BZx)i|LUWZtg3sYzl?&eS~%QB8C@W>KnY zpoVQlY~O7}Ic=<|3{Dy0`&TWTOx|7Fa_$T;foW1k>y)r65aI_=)qDnb?k65cOw=cL z>JTjljixey#*U)s9<Xrr1%~cv*p>v2_27QvDX-_r-sOvj{=43d5ko5vA{(@g1SPQ+ zB4r4X4yYuE+Rh`;Jo#T9I+Edgq8E!-*vY5~AnMAc&VXF|$}Xq*tJmKDmHo%wvKUYb zu!8oVwNw|Ccu9}0eQWs)R_W+<@gTsos5RIUk=a0dFbDvOHO_n6<)<Vd=Uy1>4CJ(^ z&}L5=XGNu3gr$)y=WFmE^bsSua4ZYgvvMP4ANC>MbAJx_FDel@hOdHrZZRxnswy@u zaFewvp`P?S#j`9Uk|9E8dZf7xcd6l-e%Vk=)L`r2(3JY6-+C{;_1zC)80jh~LYC_u zojjDCIhAe%?$HPAStYm_4GJ=b<E4Z~jzd6TsGi)oDr27a<hBG()wK0V^dAb6jbXcL z@#Ksx@aDAe$|sC4`=4_-lKuorHfDk-^rcra#2Cn82KuQoppFTMtiCV3^K8h*$A=Z< z-zt|lyi>f{*VC_Ga&}}0%#X%9IrunFr7I5r2gKGk#3(J0LKqb_^e8t7z%<4Fcx_5$ zY`7)44)YCC29Sei%mjfNpA!D(4gV>N;tiAYjL{wnd~?drr!XE2HqSe!lbX{cx5aVB zYc04fjpyGONrmBIYeW0u;N@nB`XBpg+}-d$!knq0X_J{_AB-6O$6w_uQd^>dd8owy zx;gW?<3A{*)^$*B<)}j#$vhBH&gS*=DV_!n1n1fInpF%`Y%nAIPpvBOzcZz6=Ao}~ zG=eO6Y2$Bw_oKKf0p!!zy3>@i8T+hJ0YTm~o;2Rf43q!h5IXNY_(w)S0;N2N76I%$ zV_QpIJ6|QlVPLd}!0*HDWH|0EZwDO+j!;q#eFa{vPr(23h2e+`YeLe1UfIDQGP6`b z8jN-}zqm!Cv;Ip)y*=1QXXt><IpM#ybCssLQWco%S@NfnF1JGf3qggL>VT`l_V4}N z7p^A);E7ZtgX94)ooF!<V)`JDiF=U7n^^Al|1MXj$$!r0xZ(c^rjp*kILwBT)I>=O zk>|=B)1brtn*8sQF~N1{#AX%BKk%RD6X3cT3jNd8|J?7=Ol<N`Sb;6GhP3WE^M&6t zfqD6QGY=EYs(r~47@Ls)G`-#iBDx)%s#oQI^D7-$OLzx-W`zc;CBhMl!2-^mNwXgY z{=-&W6}4fUn}}9S%b@Q#&OPxlj9(ZNx+!H>g)JalfM$LXa;}#emZ88gH=h$iX=oV4 zhuquU0O__ZLOqLT8OXAE<c>1K(3~#~p%GjU7BEtz>_Afn&+(-pt4Hf$Ko%T|Hpm7` zM^Pz)g;`hJ0|enrTrwgic)<=Qblq5LS$&dz&6s=gdd(`zXKl;vqsUn&mIAKF9Pvp9 z#~8oC$;(17i0tM}qFv40o$}^-MoRC-T(NGGcP6qh4GL)aOJ8_9{>IPz*m}>BGdTpc zXaCP0?b_47BGLe-^?Hsjc}5>6Hef1vfH}33oIDtC@k?I|;G|ViC|59=7l4%Or|dz~ zb&P=;=U3beNWvYYAXtBHoi%j_a2rIDj*5Qp2gpB^X!m<=hE2C8BTx~_nTidfoZ(=2 zO@DShzH<c=E$EcTO6mibar>*y5sr`{ij38JfBaGY(r<n<drLwCnQ7eCE0MeW2O`Q0 zWXT}YGO_bErTAanD{V7I$GZKV4Id?3`Zwi#T<ECM)N$7=YEqS5BjDq>c$NV)!2Qt) z-Z8bK%YRKtW1=D=1}O0!a*Xe&(Df{Ml^+X&hHk5LyNwfrjo_MrS&==SZCi(dH1|P< zv(*>~Y^BsM_&Lx08q6m`Gz^Qt)5VeBQ&P-T<_j_LV|XM-C769MVi#M)_)4uO^|wR` z&2d0M&i(Vpf8sHbS}3myfnY;$qVQ#o?zcdLX{cF)UYm2|AHf-B)R8r>tWB0?_zZ+Q z4NeB~qJs~R--K-<ZAbi1WCv+H3ZJ0H#~~X$4P&8VrIN`+bDxaMB{KH_0bd>e%kbY` z6|8FyyQHab=L`V+Q*1X)6<gn=CYZalzyG@Y8QUcy=D^<bhW{1JzVd9L(Y-wuVmm?N z3fBMf@4Oe^{@_CjjN486&sd5WT9uP67$}GS(~?w-7}ic&m}46m5^aqP^8371&$K+J z<KEbnvEf365HBV^wmEpSE-wF^1lNyEX1u_E&e2q>;|M!q(1HWrKzr7SG_U|~_SONh zc~F;1T^F4DWhb5IJfOr)JvfMc?Rs3`*lXLr2#30L&Gq*;f9A9CSAXi=hdSW)laQX& zjPr*7pqk`=RkOjNZ#wb^+>9#o+JigY%|W9axab5Po+SLmC;?Ecq!-uJHv2!P4bm<s z{~X^CWw-xt_#dqwbg(h~ml};lTV=SM^gA>@o^5bm_CKo`=;$hu=+3R<e;VxrIJD0} zV;KIP09Ozvjk3f-a$pAacJr*aRnxas2y)4$FEDya*W@0oh?s;lRz>IsGF5zv_@7cU zEAiPzJR>!o;UkDq%TW_&#AbBUip;xlppm8NUnuZ|l+?Z;B2tooRBX9L8o3xHS`nNw z>8g)FX3vSnc+WQ`8Uw{{5h5-|u+13Dsn{PQRb(m*JU9<7O>u7u*XU&VT|{jHiMDXY zK^=BLofMU2)7b$RGPekDDBI6cevG3C!s{_%<ny$EM8$v=l~_}ikLu`1Z_W^wMs|bi zz#|Bk?r{2nWz;;rsj+L?KU+#mTea4I<^aDPySK=Rp;yM}Wur@&LX6Jzzj^%o>d#(# z|5KnrN$1{wfr<f&oeOY32O#(R3J2mqh1Zm-X@!j%EgtZP;5f#3<)wLp&zu}pmD^|g zK4??hO_Qyl;s}7(*mgB^MBST+Nwmn2-%X<vs=n_lj$MjVG6ZA!9GP}ZK_*3(0}a9? zt7(Y+nw090qt(P31+L5w=hOpL2Fc9Z!M7zcE8|e{3%~yL>siH4<s4b)X9iRE-w*Fg z2Bd6BYnVLveBo}RK;%M3nM*qd>W~6y@~>a6ln3|QMUK(6W{k{s3WsLBi~9!Mh*^O( zV9`-5g&_F@7|~50#UNMY=!%);7Pt*&Y;!QC2jO}Vj}vOX2?}ydWq@>v7%v&-#q~0I zYJvpb)H3Y)Ac5W+*@PW&I&|op(F!`1WMGLS16OSrZ^pr_RoxAf7*yPx?pa#`IgKc? zdm2eM#^U55U+y&HdPLwp75tajiUf3)2Lt3}B&KpZ5NpxbzY|Yj#d)ArNdfhmr6`-i z00wc=+P@uU%h_mr+MAnNyY4;_cmPRj$N!l5O1TO@Nw9TzZ>*<T%mi=y?{#4240+-I z=_H|U;C7U@C-~?<VzlN-nXI1YRPR9bJ_m~Hzyf=CB+3sp3qHNUvaDvmrnWI9^iQAt z&}<=<zRN$?ub)~H(tGh#?|=9l|Jkp7ebzh1GK_7MT88cWZhjnC$A{GF2n!?wMU*LF z=XP(HDCQ{Y+WXbul&Hwy(FGX{+`Fd0<>at1W01TaTosaV!+*6BFY!Ni{HF=cYpJp! z+?Ce0>=Dx8+?^(3&>1kvVHd?xn^ME;IzDj;m6WoX;P9`BpPD_-1VF;2G=`Z!zJGek z`0CGp@gXlKGcS^VP0S9nOyO{7okK|z5s~;D&9_OnWlOAed#py?@me4i!2dKm_k1b; z<aJ3o+oLYYmL_fG<6*T7p1x-PuMa8tU*q6e0_TbK%<wZz^asA9&XoPnb4<b*OdoQ1 zkja5N{>SiXQ`fM8!!?7{1x_+$9N4<Iw*G~?@ChL(FXa`A%jAK)HU4Iy;#*_rzBl<7 z&i`=nf9-4xQy+r>HW-h*qtUcQ;RK^OIz><px+G-dBnMf16bnly2CQc>Ns^fWC)5*P z5|=G`3DSsIWsX2jJcwgi9}qau{07-Ui(pWp4OK>NljS7bFVY4Pdx<claR-^-KY3|B z;IDA-(P@VTm59*v<|97Fz%FJZyP*yuBp!)XX2kR!oCrNmNPCkJ@ylId4G)ys?QzU} zTpRS4wjY;U5xbe}9qNewmBO60_EzOMOJ2%1N;$`Y0s!`MtdAKow3g!1=u-O{8ze$j zNVCvSeBn)J|7#+}oSO$BRF>A?Cqe1?3>;u%z!UpHHP5A>fkn;dvqUBM9rws-&(hdJ zA=%5g{V}_3bI@lu_!8H~D}Pdcb^C9PBEtK$h;UZbO|eUaS9d4m&>X!nELsNBjSL$P zt)b2$gh~*lRsktlHLA?uV90Tsb3$cG^KQsMl7fbh|NQem_-=je8y`G=f2!-r8O=@% zK=!`z)Zvn84b1^@9I&40jnVdNpE^u=@PP&sjX?(!vNikmA+FeSoKTCK&!(+Cqlvjl zlr!RCk|M-_Hcr+rr_(>hoY{^D-2N5DnuvR0JHs_h>Nn`1#Se$+DO)F$E|bY<K((V6 zXBs|)92d$V>4B9v$;eAL_S;1}>>KYA|AYEGwhAlh+h{!$(7Dvv$-T2_xXD}PCCIRW zwY&uj*iVjpZ_&66;cBJ@UDQvS@vx?sWwuvX$6*e++WxfgKdRi2O7bf4Kl7&ASk_Pc zKjol>cxU0x<NY&u0^2<_#<a6IMhP~Kr|-3WPN@;DIqegSVPWeN1M+>jf@`;@46MIf zrCtQFH*UW4*|Mem|0(i{zI+teN%;r<^I4RT(+rtKwYA>^LX2R?e;J%I9C6M18uFji zQJi6#hFL}i%q_+`j=}$>qmtP_dSAr@6{*K!f<b_L%0I@v;eW>0{_y?$<=_5Rye4}= zCnz`S-!4gi709*XAkV_BU@yrEFl0pbCTqOy&=ZBCn2nXK0L?x+;-nYWHg5WsB$t23 zX_Mj_9o71Pt8;JXPs9JZHQn)v(FU6>6J_`xVfWKcc;V0$faw{*|CKm_E#(Op+E`06 zu|?qddF-!~_Cq0HP;(yx3I6ZPUwSvb@>4(3^I5!iHHF88u8U4_SUa8wwh&HHMS305 zTa>pBq(jBgv`izQ+yCTg!~dkM0NHXp9HNc<=jhE)!eIjN)LBqU)6OT_{}_jztpdDK za(H{hD*Z2ATr&qAP@XZ_=Voa18Ks~<Wt&ef7<%k;AD&M6TA_1J#%JR?jIelh0I(|c zUD?<rG@LrIuzL;jK{bx#_ZAqF4nkk8!w3K4?$`d1@c;Vlm_^(7Ep%fZg&=_#c}X(a zKG{SdsFZK9k_ai5(W_<<h=~AsK!(3F?*qo6R*`ixTG&={Y%p4E!Ny=A!8IokGAv-g z>kJSI#I9a^9k6A_?V2>NDH=1$y+$^GV!kcQ=%K}<(UrbdBaR`%p=y*B43Y-f(07-S zeOi>$OK3$i?A4udur$5fNgjMyF~U%kBV!q6O;Y|U#(&*Dj}j?o*Q6|*$-Vk1Cj$WG z0f?|ry9M2d*-}_EZbbGrHu_o`7ws55vi|kepZyDKU)Tm56$8i>w?hE$!GQgFv}<o# z16+>=%g1|buxLJn_^y1xYoi5}e%$0k^W^U%XP1G3$av9`X>HtDx0Lc3TNTCuF3rtQ zm>^SDyvW$aEJi>zaA<w?CW_M=#?AF{g~c8ya~m++8LHPTw_vmXrHmUjUkVz$Fo-yQ zzP$JTNA-)p^$m)7&Vg72=E~4PS|`NI4Q0Ngu~4~>JrzsY6hqu8Hd|PN!2%wjwVVLR z|2jc=WF_WF86@iZtwD@xqSBoNo*3RLqjJyukD)n+!{9>kf6sFU#~y5mkX~6v0E`~t z4uX+1KuXw!ahAz{>>x|W<lsEu1O()t1{NNdcv+u^7zfpdN7gMA)MNC1{r778nX*aa zr!)wsLm39>Y697x96w<&F^5(mea*>t-x9l2wxG~1@M`)n<KxrB|JX!x<iEH+xL$N& zu*i%_HdD7iQTzG$?)j8aDE?D64YD?dR`W&pqLQ{9+z@6bGGlmr)s*2wH}gQ;JN|o{ zH~go2yQZu|B(bJg-)RE)uRaa`*Pga^B8rbMOp|{#lEH+7)`@ZzXPZQ&1HXKJrnbl* zG58;2K6IOg|6#li;n5t{{S11V{5L^7?rh@>$>BB6U;M3a#s?oh>)>+^t)$q(ShO(( z8B$b3B*zCIBn_DhKi_ONNurB-Qd>$yUEd+Op^j>1956=pJGO#1)wAca1^7i5{cmI7 zqjknoP1=N#KM4L4#Dgki)3r^v!K>IQP+lJwQrk*ql#l)T<3Y%1XUq6M18`TBKt{R! z)rpp}ZJRZfntZi?*L9a4`_-TQ!aDB>CF&qKDqoBcbORt06|yBy!+$^24(D#Of~?Mz zxmrWt4gZUi=DMP4?=%`Yv1tFVpb`Qvbh0fVz3l&5&m`z|KmIiPANXR2^s4HOpItRV zKJzaB;SOk7b#M(vr|BHS|J?9DDUBm#!a0nkCMpL5S@RXcmL)+!>Vz4_$0hbW1`FZb zk91XqF!Qz^shMJllLSr$l{bgvzYh2x9cWZ7NpT`I4QicfnG(pOy(i_<CyLoo&p>h| zLxWcP5i!H!0(K-NIbk~6=KN$uPDCbV>WD;@ta2Fn^@_JqC?nBWkm!g3<5>yJ-xu2Z zwy0LgKtgrE7z<-m26&!As8cF~PEW{iWj_<8GmUAQf`{^~=FU8mPHx1M-oyw@G2wog z0`A>X;6{&H^_-B2P*e_rhi6_V0gM@a%u{M}J@V&rPY!`=OlhDf6J26_R6${mV&l5C zK@)QLvLmt-gNpFAon>P-WO{1OY@_wB{q(!}w|?fc{Vo3OkNNX}K$D)CMeW^>uzhGe z@UN=`c#SFv$g9wjb8r>t-^OH~rbTt{I96a8H_+$r94jC@g6$^otW1-++~yxT7~0o3 z2PhkZBBb1#0gaR!H8=EoYy+gTPlKxmxcP_~wz!G@n$2v@1DrcLXc30h7}Fo;RUXz5 zB~p>{X%{~kO)LJ<Z+;`c{r<=NmC8LOhcsdXS%V7jJ?;Qdx&hnYOH)x1B-h!0pE=<~ z{op>i>)PuuOFn4?V>zgP3o=Q4@>EWjX`3>c5e_(<YcdNObW&0VQ<daDe0{HW4$!-p zAVr)0x5~P)PrKL#`0J$n*MMs4Fc6n~lr|p>B?<5w#=%enp0uMAG)-w-={x~CyVtc( zS#G?PbPE&^tvk*k!f}&E5a(i8H1uus9w$ckw&BjK$`~kItC@L8Z|a|6Zp^qw-+y}e zANyE#{O=%mr*^s3&G_QJ=T*<GzVMHk&fCFMF^V)PFJaNWp4XW>?~E1@<eX*z(>Vm+ zLMSTWkn^Ch4gc4F{0tCwPuL$ryV<{Tca+&sK?ofG`%tuzgJa`U__?_Jct!r#dC;Yi z(x#F8_jk$a;H7&)!NZ<6p5h~OAv8v9QB3G;4+pO=cl;L}qv^7E>KVE=Fb7xSkAL6$ zu;L&7=C^`m18wW@fN}(FeFmA55IFXUFfb7*fc(ceTHuqE5oEN4T*%D_3uSEWj_ri+ zi9;=zJ+}iYj`~>%sYnJAxVY2Mm=ui0WCDY)>(2T?@IMCsBTT+~9Nq4$Pq&TAD=FKg zBcBwh#_-mU6=vJ8-{?w!lB(>VaELOhE$80nbIxDd9Q}&8KefrF`pZB4Zv54&3gC#K z{a-te3@;Qlz)6RpHDHvmM1ke#n39}rcdsEgMZJLqk^Qfb(XC1^)0b8Eraf(@53}_^ zto;U{VrA}2`+r&v-49)GmIYmb|3QVqik=W1R`arLu3@(PXPW(g?uWE?KWDCOPtP^j zY5GtZ@k}|yp7rk+$A7(2c0&puzG4CYl}QIvR>UxRuL2$RG|UYJvkb>&UyRQ+0q}77 zAN<$fr-lE}AIGWy0Fg2#GB8FH7naL82r+(GNDp&CM2sp^(P@h^L@5Djg=HLqPG68< z0gcJAsbK{UF}@n+9khl8+)g?MfwB6O#!#W~()in|1Gunk*WxVOHJ39Y-A!DpF<O&) z&i=z|O}R%H*3DcOeNS_w#~_P7Yan93uWn>9wX!1bU4}!gmsDEX`=iKU1W&qNhBA5O zk`ZRe&|Rb0^4-e~0>DX%_8L(e8Or{b3m5K7Mqsb|4j7%BnrAt5coM*>S+4(c=9@=9 zU;WuHu1K&s^ri&DviJLGv{hYp`E2ECstP#O&e9{#a0n)d09^y}12lts=1yCtGADZ+ z=P;u{%P0w_NNVW0UD}><39%+%Kpsyn?eMhP#TEK4jl)d-Ip(+<7o-)O1yqbQeJ_ZD zxj`eT=>daEO0NNzV=jqUTOesRJH9WI1aNlk*xL6%X2Ne?i2l3peNeyt`+wZ0^YSr% z#ULAl6P-E3(znZfYIRD$CKc58Grm&le6cG&QYVJJaPI5;Ih$#ze*iMC)X&?W{B_7b z0|b6COuzNL^gzft?!`0t*Ym>1dP|*a5HaQT2<WvXQzSqow_543i9uzZ9M{U&BZ&Cq zWy9Tm*xDf-lmYplW7#TQUpi{7dvT(FKAFRIwf9WEf!@6Zo7Q@s#HyFho)aF%8dZ+g zto503D>268i$TcwD`AYAR(JTP{Ik89n?HU0k1{;A_rU+a{iUFD^b>7(qVz?lW3t0X z>&U*wtO9whkgev~^M*b1crMKcrdVR@01X2@T5AQ;5<E~*H0oO!wESC;mTT<6DH3E? z(Bw?^A1|CV+N5H?HdR{wv6^9X34cvrB>HOks{Fgd>AVnO^3N?u!BX!Kexi-_b%M?> z@&B;tCpzQ0W=#AsAP=apT5*usMsdCQ<=^>s{q8s3Z-gM8+3gWBdKzCU1OVgj25xI_ zF0^h@2#AQlz~xP~YS_64z{{4{-pt(TgqmP4+Dp9L&#x$J6nu;RM()~6n?BA@z<*xL z*8*3%E;(-aPXJY4q=MGJHn!z(k@P54Lzb#6OHWT)$&-F`+%paY<n>Uow3DVHAS?+J zvDtK6K;VpZ-qTlq{(2(7F)A(YdOCprSI|k(^o1g=fVf@u+~mIl$SV1-Zs!>M7d^<I z$zYV}C{wi@Q~9`fquKwq5{@}!r%{Pzs{t*HO5e$XTM%_6A`klxHcw&k(t;txR2aYO zr=#;->t+T^%jBPHa0YZWZ2U0&H9`R3=pZq&@|a%<!`5e$-3(cMq$jj{Y)11CGdKCq zyZnco)080}wnX$DV{p+HkK>y6Z0l3PfAsTM_Jrd*glU4&XtF577<6fqfRfR?5$%at zoW`5MPaE<O4&z8C=tN$$AVaABE(lI+8&Q-(pPGD|eY&{VE$}tM1ygNv7U=@jXp+M~ z8gXBpx5XEvN?5SSXGgt<J~!bjydSO_<cR4*fBRg)6fso?r0L1TVJooJ84f308kl7` z>CfUN;FKb&7*eD?W={l)2Be5F<NzL=t+bZLwlZ;r1N5phD@Ri?r06O_`vA|5w0S6f zk%9Y|NxMWaVZ9a7f91dZBacY%JL~J`uo8aEbpw<d@Y(|bz=WDC|7wrd#N1~DaHTxP zL2fc_x`f7f$T$E82xu`5@%CA!=*s+~h#i=k@pC!fT-UOy0qF(E|40EYwY9VkdeyUb zB^6S@V@r%032>cgV+t7hrvNP3qK-<0As7(HFx=$rB-utdszrQ_YE#ljj;^^X3Vz|g z_`}7QO&EkrX{$2ssD1aV1w_6pmEl|Cq0@i;x1;v_%|gEEMwRrg$c5E;5gh0sTUBiG z8C-{^S~24t;b)MvTy*=F1BFstG<4G1y2-vAj~N434n~DVGkQKjK@9Oh>%KlUy${f& zp-(+!&Qi?y85oL9{+T5#bMh+s1d5b16XK6WGj2B^Ke2?<_G7z4iz=qU3OJ?Ixvekn z<=;J)(Uq7(%vi$3Ls%+Vj!mFa%0!lAXGdbg%Jcw48;xTKbwK2Yga4BA8Z0;b56Azb zPmbq27vCKn2vQdOVAa66(Ny>qbaEjFkcD3JQ%pfQ^~{;CC4!!*{pi?ou=?h<5%6Hv zoCm^osZ_Hl|2YahF~3m&DVg5@;uZYAmiBl>MlC4bdbT>ykHbWpAm=XsJuc~uk$)34 zphT+Vp8AnpH~x=~&KZRTdy22^?B)cX(J-GHq`Rq8OMzj&Uc>x_U;o3pwgOO=j|l_> z^?Z@mHuxKcCW9ZWSW0{sz#y3vfpT~wIh-R;N^*JYF!@izs<ib4xBzbFdt#6;yeA2| zu~xaDvZ@U*2F`E8|7tZ%6~?af78LN&)(~~a|Cvk9BNT18jsmo^t}oAiT9rTUZ;_F| zulJAtd^}GGsOQs?D*=nWLiQ@xsLZBg-~aA93*en>99}Z6v8L;q{!L&ANqkK4H2iOL zp$#s+svGuM_PqmUj{iAJqOEqMyve^|*X?OM<)$#@Di0n23J2H@73}$Ba2u0+6rLj> z`3G$Zfr_%V4(h8K?pj@A`@m`PFMBWbj7aGJ#{cLiX+;h8&_vGg<i@;a7+Z4uex2Hh z2~vX}cu0D)3iVFk3%p&J#KZ)W7yG&br#J*SOmWZhOEyjZ!;{9J68`s?&h^DnBs3#8 z5L_Tb35;+Z2Z5l3#IDTHmZQ-38fIm9do1IC1D9Tf5L_%wE=MVa)TSaLwHpwobv<&@ z2uWgA-kp=&KhB(D7^u=5AI7^d2aKK?$X3%9=RBvpaS10CcLtv)y#Z{2F-)*ViJVwm z8W{LpCl5Ek9~RB$I?@r?GG+vqXC6e-djN|O`m_K?Xt_rzA!t%`9&koSqksho0`@eP zB!xlD6yQ>nHHCp~Lzrg_PON~GT+Uen+=~r^XHBky8gCwS|KI)f|K>Ug^E`*N1uC8f z9<%?y4caO@b(ZfV2sj<ZE^7zBO{GAvzFq1t_`Mj-9N|%9rgWnXA$;AW+;k0BX&;Ai z5!HD0v~##1=z>+9<UCi%RFrO%5itAaBa_NjQ;L)v>$jN^)uAct2s0j+{MdXm-1%R; zd6vn#W}`MBMTPZ97f&@4Qq+*Uj9>b#_v$<Ee-uiZB;}2I5{@DF-eJ5f6tM9>etvGj z;HlTted~T?%8NM0sj<XZqXY)w&@+rzY)oejv^+9K{@3*fG7Obr8AKY<eaIjkz&_KU zsnxn5v5-+S)^5X2DTni~u~<g3*B0C=$GT}!9!;o`|A=uE!VfA%CBU4-&%K$<6rWg9 z5fgJMjf1*yb#o|yi!sCi{xNi=k(tNM__c#uH|rl4Bu1O&hiGleD$w8vBSMull6bAa z95hW|ci<+kYkse>*|16o(Dh<|iuj)a-H<3Fg)H}2b8KA5?s|W6Yt4{4%=u4k6To;! zn`NmQuK5{usu*92k_#8Z&wNeiA6|JFj<%JYZk(2t;d8G&O5rB61j9F|p*j->1`^#c z@tSc}^L_^Hol*W1yny^4hX12hs{`o5_8^P3{6qI1kS)o7x>s8=q}lKK@`nG5A4o!q z<DZ28K}JoDGY0?NQD!B8<p1&E+kgCF{?p(64uXbV{U@7|0i|7P6-eIO4y^$O23?lp z1Kol!ne$uUH^0@;nS8t5>xb%{Zu4ktQq#hJ8)C~pXiz3A|0ei<25nn>FJaegcG;#l zZF4WgP-1MUlEW@lf=-j&@OmByco|rDEMRUKeH;jQLY?7&Ko20iu;qAfu?pa4zYuSp zMz6~FUbH@>hFX`x7Z5aXiKxbbJ-o>SBdD%f_+P~TQ1C5z3+BHD&lOQnvF0=6KjM`j zvjVSzjgkL#jsl(gGvfvSqTOmcTJj&EZ1SK=(w6GasJtp-FU}AmhPh6Y;o<m?Aa9!P zhM~|A0C8^1aGD=!w5AW(Yg0%8Gs>6NJ{M6oM(p~XBkh?|PnevU{BvrceT6XHEH}e{ zbL@LbKRx_!-Z&P299jU7ryE5EP(%S?CK4h+8F2}BK!OACKiz}t4zTpJRmLM}dMXP> z7-6O1jXJ}w_4c47gYo*<7djXOV0NHpnMA~DVmRb!k#G&akAQ;kYtdIRn{}XH5k-4! zuIryyr13zYV3^UIEf}?2halzJqwcI((O!U2M;%Y*UxWduGGIXijt)N1Pjl6LYJ}wM zM@jSuvZ%C@rv$I7&apdU_<&4X%z_5#+RT2!h-_LdjVM|xaw@ALecdqbR*qb_lh2G< z8nEJT{><m=OP_zM0U_eWiLuK3pJj*9ocY|}ryo#rMWLU&B>Hmx*S!0;h){dYW6xvZ zmcWqgqt%>$QZ{K9`J%vZBv@0a%>7YLeOEed)a_1qz|MfgjNRLY91OL6taM-}TMq^1 z*MxzvRtfUY>EWXErb9~t=fZCzDgqEZ(_?*S1_O&-ogGxpn{F<r0$iGP@ZbOIU%r<m zy+nDr-t)W)@?aV`UH`E%<7zAXh5W-I?*X*KvrhBY_Ype8ay8Y!Dns1l?^r>G)lbr* z!15HYk^IvR&X6I#W>m2m$g<EPO%_wsI5-A3B&s$?C}X3m7}Eoy0gxlu(eg})wV$Hi z78I&26eS@Y*wHEcVtZjhs_O*jj+RdwdC-}IfVOZ<!1Ekun1^8^-k#LgWlu*S=;~iP z5P5%3I&&JEJV5^4I4w83lOvF`?(3j{nzG~!V2z<$!;haH{!9L;mSY4SowDf)H#BQX zKl32G*|jxm*RM!_!nh23dcb6ZB4p&+_``w}J}+JK+*<*p>XuRG0d-*3(j-rV|79Yk zKJdN|!s?(v_!bhZBPtwjjyT8?*?Oi}q=9C6XsF4*#GFw)%70^O8F-9ZbouWI9FUEo zvk_E*gq1rV^x9<5p{iH#e-w2dA(<N?M{p%PFiLgPYW<)6&U-5mz-J}G4jgV9ULB5W z1KLPb!%8ujRz9af)M6Zj+iYF_yZvQv;99?ivnanez5l&kj2R2fvF9e3^>2v(2l3xX z8!v(Y8W?0MU(o8+KZxxmkoUutTW8YR54<kBNlrg6YX^TFI9Ln<Ocm$L%1s8V^f~%0 zfG>aPo$x0`&G=#OB$2D+Gth?K0$_mv0sqc(<i+uSGu=<q*{ATo!jf|*tUJsx5L_5W z4e95u-Wxszo%`N(W<YIgR^wc*)jmgQkPd_YuQ<t)e^tQYI$o>C>Eb^{S&SY3v89*^ zRQ8)A*n|H~uyVYaNY&`ojF5Jck3+^%BEapwrs2r{5TasuIwyeF{eV}%)E2+^6Yw9` za{T9~i2n#W_xh2UMA`@!zA%{-b*+b#5mw5+8-_ydh5c!u)6}jh91!|7!WCs{aTf!? z08Izx87f{0I?>}(R;tI7W|a5b-7^65Y`(%~cmPh5%RT%a6Aj`a9VhOSgKvv@EVRqn zH^@j<5g3g<91vGeP&WQ}KDK?@sh&&wRnFeIHVAusE)H7#w7ow~8VGA>x5a8S6^+z) z2rBTo<rNa-Q;KuVI3b_O0i^mf^$M^<2CAvvttA5odW>O_{-P}fKA#@#{{5f(aR%rF z3hN0>;eeDFau`4bALlpUMGF?3`|>?#&$t~&Ev}1M1z;;O@-~tP_0vLaR4}Et{b##i zl#pE;qfmL<_4&LtaALf;r2H`2uyMi=DwRqiVjD3ujfj`>PsMD;N$uk=f6beUfD%PO z9>f~O8h858d$`2>XzLh}+`jb|3|Pyw4hVi+^MC!-ugAw9!)FQ(h_S&Z3n=HTXf*T` zs!jU7m0_;7UskmW!CK?JfZqba(Yo`X@4CkgZjpaJ4o`=omC-^O#&KXLj=>T*1vZKI zT&^Y&=Gby}m;Z?H>I+&p49HA5^;T*~y~;)i$#f1b9d(jR!3d%3MnqVklg7#R7}FeE zEm`gi2XYLQnn$iOlUqhNVOmN`X;iF|V;&NMGgMwYx+!+H)S?Ym-q}`T&lGJ2ukKjA zEp*?hcYVQJ$KQcT)W;LF{`Bx)@_!fZe2M>Jd>TpUYjc8EP@H2!aJk&@X$<WrKOCh; zqE2=lBY85731_4RdC5<U3_1w6#OrDMWXXR`=GX9l&bgpo377x9LeD4#>gnwQWz3#| zeP6^^<Ubhqhr<v3aZ@rf=12Y`bx@zn|7NF6KnB+Ta7`P_p?Ey{?6E-XGqG+*o%8J2 zk^c~<uC2-4&1v!<@xk|>*H(b*6!ar#=cpMPQ_*i5$L<a}6i>d$@<$F&$Z&7+IhJmW zghesoNCu_r75QI8$<~RRDtB9;T(0@mIH-U<AIrZP{?|>zkhdB-OZeMCzw+zO(^Vc` z$RHW=kVZ~=$~*|LGye@%%<ALZCD>ZsuFhftjkiF+8D{5AlIOblssi|XKl|g?IZ&{~ za@OjuJImxBI4)M@b~=V^46D!v@PARgYIFSi56OW(H)u^_0{@vB7^#{2!c*?;30S=J z9A{h#Mvmodm!ktdZGvBl0YwOW;Sge0=&^sucI(0lJyJOB=|diLz2iU95t9FGdXeU| zDWJF-bNDJ^S^|c&YpKWjv=69xI&PW#8;#TM?LUIccCE~(+7Zq0zh2`1C&~YxC;mG> zANx&IW;4!X&@}m5+T>CiiP@D~lqj=q+9QoLFa$;ravzV@b*9M(HMK_38jk^+X^Z#u z``X69@oo{7ef2aX*MYN_nB&ZC-bGp<QasFdY8${j`x>uXP=fr;pc+X`#;pdlJKy9e zuNI<++2^zR56z_z*pwkLIe9nYJWFN~Kr^f=$q~)X*S%Bj2%$Ghj&P=ln}g{DO}eCO zH!4pA2^;9nExXCXfdb?62-^t<^~(N#Hh<!CZ#Ji1;K0Yp!0_|KXUOXQ$iD`;Qpw&G z1UOwcROk@90nYJB8D*$Z6Z}2J;x}dQitZvDD+y#Y*meAvxBGMdZ9}3X7&&bjJ$g;p zM9zP)4$;|_HHK5ab9}GLKiiI8rJMZkmM*TB+8PAGxM+o@3^Bjic!vWjC60<COzRs1 z|L4E|$ML)0_(1C*y4KMJH!_5=Mv&3sY5XYrlTj^S3E-cFa=Z=(yfEhEd1bNfA?rlk zXTDS3n#n0Xm1G+X2pgowHf%(hiDF;^?X<lxq?c|9@+PBL&>aYZ)U##nNnrjvM*bOa zEs0FAJ+7@}X+==rKrQ(sJ}d*@oOl>0BMV)X)M>0@u#<xejakBR)PZ@b&!f+EIJM}L zOy@Z$@`&gd+agW2sJq8n?p9#%cVt4sS<`rf!81mTK>K^vePldss{F=r76gt@2mjX{ zCr!ma9sd!ySG!}xAb`Vr830N=d4{-Jua6<1x5de}*O%8;?3wUFl}(-Vh%W}-LWdLr zWAFG+{d07s-aImJp2w}#At)FvwGM|4FPjyqSMVQ3_%8o_F|HARY*iwB7sOU0@h9Rx zbcf2FPh0%dSX(#kys8Wr9Iuvt%Ix?)jW-=YZG0yFTiJ0F=%Ks6o4@k4Z_!v2PSjoI zYQr~uC9+NsaNTw~N9T^w8J*Q87Te?<<hV}MhH>vZ#)yopxJMuq&>)^QJls@Cx%_Wm zzl#67`7jyJU3CbvL!{|(CWdQtgnrubzcBkUn%Aw$0cQcM;6bbW?)e<{P<H>atx$bA zTQX1b&+7pJ+F?xa{MDcSk@%Z`<;M`@S7aQD(sYHi<bhI)GskANY(F2<3wiJ0R3Cjj zc5w$c=k*xJw&^zZzgPkIU#MpBLG?|=VQbNm_|oCtShTkP!^|`;Gqc9Rf>40C?$^qe z;hm@i82LBrOJhx}`Plz^FirOOBo)Ry#^;zoLhT7zPXv`Lnvg9~A$zfAWZ`~k-&wMu z=!EOiJtF3PMNCD-3;d7q=P*$VT=?IgFaCF<E8>-hI-DDB9RYO2{1dlLSTYp@Olr#_ zEHCFs`j?3R5`1>eHAJYC!;s-G=*!#EeMLduB3y1<6hJ5#*5oC-J#hUU_vl-wOs2|| zMh^)BNnU`la-^5KKp-iwy+E)Ik?mMv@-wGYQEXR?mIVU|y5{QQIfW;veweI6jpnuc z5IF#SN(@$Rh~Qod?z(!7ValG#i<vAlglLMFoVKO!7?td`O%u+^{7Z326LH^C7_q7I zhF#u1j`-@&|M<iHirc|{^<uyN{q;ndf7|g^Zr1k{OW1Tz&^b2)CI&R+=-lwR#ItQ* zypVqJ?OeR3-23D><La*l#!RpZRIC}64M3NL$FngwznaE#Hh1K-E$9B0!SMdA!%5`j zi)#t1YA@0N!J9cL|1*yi6N4*U(MyjEJujc(!pd*G|55zI-}+{R8LBeKlG!HQBwFF5 zU<9GYI3yoqhSB@-cUtyIyD8Z`AXGi4zH%b&Vh#}=M)5HR=C$+lOcAOvk}=NGIXuu6 zS>q98OtuMI9oRSVv&K+aU}?_`Uh9bcLF=e;gMbX09ykZV*nc@!F8?`eA4%2Yfk=|i z&tJfdV&s_wOLHP;UCA(oJ^h0pcip}86ApW%{H<$`{VWygc8t29AF1<@xd=^*bU9@Z z>epA~!SD*wDMKJ8+O){>($|U3`c`<R)|%h`{ygzN9REX??_-htw`R~*i_P%mV0#a) z0$GLo767q2ZDcpOIUbM9Jra0o>pvAG168Ru)G?fgqv|#Z>iJQia~{EDkNbN#;nGQi zeMP{{xCQ=Q{(DJ|9sgAk@QVCL%;A2U{BtRfQl{^mkQr6_CjX?BDiGaOd<DY#YclVA z6@B757ij{hb4|WTTCACnPBG?o#4kN`_y-@IynOw1$auaDCqg#Paray`k1SM8%CwMZ zI3ksF17H-i0z2f+zT(L<#w$dRrNL<q^idfkk{u>pGiw#nLgL>a5dV2ino<5qf?TmX z{v-?WpDMU|o2c*u-+3x@u8q;bFM<LvY*Yd8_|Nn6JUM`mW!{1Sv47@xE?e-cKmWye z>uA6|j+w|7R3Z2C@M$N+k^>&LscJeBC&t;m+5c*RdTVrYul2Q+?Q8>U!~fdx9|=Wn zjRQxOKaD`Y9fPl4300mfjHzG>1oFnNARB}4qE(N}Kl!lVoj0q_@joK&_<zjyhLmK) zy#afN80XE2?_cIkeggjQEnCde1VeGspbzx|S9f*sgW&&+^;5-v;9)!m9s?+yvpIO0 z$c;+8u1xU<PQpbXGG^(UFm<2Eo$}=+3fo}0N8X9P85geDn5?*|sc<OrvccM{4$Nn( z2q<v5A}I257-$mBx^>Eg#uUNBE&9<?0Oykp=ceeWV7ND?Yzjnz+Qxn+_O~~&&o+V> z<)a`6-2bS0U7E@dFo^)Y18K^kmu2|!$XyE}_`)nevD|s_=%K($$1_^FVFb2d9XSni z0%k4xE-I+Rqnq3~ooo9bqD&L?$5;Q+BhvemUwBJ_nF8SjG<D1HquigT{$CmX_5K9V zbTlBg18O4Xd9|$wcxFJx3yIe92Zq21`NU5I)YbD1l-t+%^)=dfRAyN;zoaXv*i;4y z_O7t3JqOo36F!(WfXMw|cmQREY3o97Y%{%0oAkDHGbVlRL|(wVciQYMa@-tssZbkJ zrU|JGv2q%%b^AyE@|*GP?|l@^!jX>-7@ZCF?W_hTjkTfd&^+zR6c~+obYR^K2!4kE zcLopWs?j-|ProqJg;R5B&iO%vEw91_X2FISq)8juseletq6I|pZT6fg|HsbC<Cqs3 zC_aHi2N{|nk$(L&63n>ejs&t~Ne?gEy{X;cA%G3~KE_b{SlQus82$Q}+TPOiS<DCh z%Dv=pQrg3!L7q20E~dxY_omwn9H|(C+{HvZPQ$B-BT+cplo<J^at&MC*fEl(TZP#Q zKP3D&keo<#zQlhzTspD&gxYI@&#l>6|MMJW=rm~{V##Yvg$i)*lA|=|U6Dq-Ux<VC zW<Xts`GN=f$v)6A{C7MRD^wIjH%(dly7YK2hvyhQJAb`wI@oZ@e?wg}k~zYq7{W{W zH!f=)J~#R2dM$>$@<ce7q?m==<X;V@f)O~J&jXsHM-C{|f}q@u)a(1hfgSX5O}<6| z$5Zx6%y^zwAmMu-R{W#i`qsL)w*vG9M-Jv3h6ynWFfz7G4pe+0(Lmib!||`!TyDe^ zO91G>#l`|h7$*0Plki&I6Z^TS(ha=eE@)eSoZ<`o7lT9?+ehB<7(fx$CMHE&#O1d< z$iRk*#&c07L;0bVFgfQpI{by(;M}J_iOCQ7o6qIRnJx1#8u0hWzn}cvoA?`lc~t?h zf)frFr=8OElK-_dhSAuHQn4E*ga7k1i@I}sfEJ4!c<KOSgxjJ_>JLgE_CGNO_%FQ4 z)~_e={WbePmy@1-Krvb$%)#7HZp$iUIQ+4%b~6%#5rhAI{s43w&O=MzT0A~k>-RJu z*bN^i?34iA{>uEt%)Bu_D*6UHBpq%$o?*@faa8iZn{OHMlC1Fx{zFU}JKN!x{C`UL zzfb}sKAdD$Pv!GKun9&NVnv)HL7GO=HIkF8T!VuVZtO|ITw~+SCOSCr5=1}u$cQ2; z>r3tzfgv&uiT72OnNk1sN<;@_5cLXx!Lr=-(J-QnN<JT!t2U^^$%as`NY~0(m~)JM zI_9yo4v<rvp?y0x<~66|R#u1mGiY))NyvmyW<|rOHG13a#l5lB2J0St!P|3VPW-%n zfFXf{_yA*Pl?lX|TEnsuPVs+{<HHTpDU%}j)8?L%=*~z=V3$F7U2`4w`uBhCi`d`2 zk9q%B0tcN5z=-oXzd7hMTOp%bv4I+u41t%Q5ykFw7G@}3Lo;eF1v6Y*$8;X3S?+z0 zkvA+4P4yB4Ds=dS|5hFhXlkct(U+nu^X*Gj2N?F)2wEO|aZKBz2r{Hs@P5rxbmFoh zL)J!M@U`GL>Yv7b;ImDR1`h)a2e|O}zWG7?`tSX5oX;tftud9so&vSu@I!UpMX$+| zd(g(>X>AA><e%SR+W-|0N`qG?IJi#wVg(4ABu!g>P8$S@ha~@02_yfV5m5;;!&Dje zlmY0~4VTv`<OjzR#xw}vU;1P1i-$o_B}XYvorWIdCm%Iwz1ltI=>lTp19WAOG{pzh z&j0{|Je+$tlGI~qJ-0z|>iaw=OB;?G9H9wwFkr!jJZ?}bT}`JhwXS1zIj!$7oa5=# ztSbrM-j>}``@Lq#?(#nf^6BG$45o~FdJX@h9pbYqT$;|nz=H>99xIW!rhp2KRvF-P zeHY-5xQ+p51Vey<0bz->GCk`}{v-U{3&}s|OU1p$fa^;8WVtMDOjDlM-#HZutBJ#K zm&prc$ZBU#@PsKLIOCZk3&Q_WP_yN~O#b`YoTm&Sp4WECCjW6eJh3=d_>_Ue%&q{# zcr!cxpXC)*p%p0CYptkF0(p)KzdK-3FMDDIG!~>ie*ER%elLFS8{Y+{Byk}n2o(Q= zk&PkY$S-L+W4v*yY2F>fae)MN!$HYMzc0Vm?)Nd{i>1xx!VZ*4{rLD&s-I(h#R+Wu z=i&2aMli_;Sct9sW^okkm%8D9O0r;87E}ggLxBXH3uD9_EwNO~O0eKCJjD03CBXAv zEu<~`xOWc-lqGr4C#wMd-oO9F`20H&;}{xwV;siW0y21h?c1NFaSmD|9{YcSuFZ>* z588TXGAM@zK!03!bmFUZ)nUtD`x9w2!W8y0nbE>b{J->OJLMbi74Tn}#H~a#{?ZvG zG_1g8Cmw&qiY#n*I!}b*>P+g6h5v@Rg1BN(1f4ZuT`meImX<!p+@B;|^A|_l4tFw$ zK|R}J5Br{ae8+qf)_6(k{o@Tv55s@V<@_Pxe~jqG5nCrO-nASj6`Eu>?)PO$d9{Ke z>{rC*jJwfxn{Ye9rE9^t+o)PWL;2C@2$svVp-qe2=E=TtFZ-B?*4B@AN6mNr&WJq8 zng}*P!^%X|_W^)6je8sn@(U;nk+pHAVRWJ2eu<Fql6gT(jIp-})J@a##9-qc3RgdG z+YS?B39nAFCSS%_8_D8=b~gJ_MCJg9zjH{ewTkhI*ZZ@r_)O!g(^LFmDU4p<Oxjf` z?Yr8e<HWX0Q$V~R{^no)TyXC{mCF_-4s-aUGeXh$Fxv#eqsj9q)73_X8$uhi3?c=1 zUVK5vR6u(K<Sn^i4Z8d&brnNucxukWynbRPul$j{^`S|HF$Qs>$zFTo8bH%q3iS#` z9NS2m<!C^Jf@#wEg@6G2q%jTwnf@|@hkt|G>qK^@@CmRqQqt*9n%FNHDU#;e7V`M` z3%~aDexSwX(_in8UAp9CU=qR$#VeMQ9v%b*!gP3Cd^XiG7%TM%`rS19$baG>QJjQz zJ#}ESLZb}_L>WSQs)qMt(|f(QF(_&W852!~YzfK#Y5V+N`zt(^L@iMG*3BnS%;aCe zKuc6I<Iqn-Xvjonc$5W)j>!59+K2&X%nZ(s<($GKF)odyW41*xFf%?svO%bbsp%)8 zBJY$4%_R)`qT-OjD!o}|e1>NnOGHWQEx=59@2};*;y#*JfjnQl`*XwpeT=Pfnzk?T z9}x)jgZr7~T0y8(MyXe$4a^k#R4@qNx+=y{U57VT?sAa89iAc47?i$GAND|{IpJXM z#Y=poWp$Evbgatr#UnZi+a9;Yg}vJ){;1QXpDyFN6)qh8x-CuXmXZH3{Ex5$eq-dn z>7;Yfq$f(ci8`fD?)Q!1nsf&0gAzw^4lZMlu;G8}J2FsLCZ$+Axqf}1C-6T2B%C7T zAHaM-=lsI2{b79X<AI@ljF%r2Msv)ffP0^$;Rb<)HpE(1-svB)u7m<6>W2RgICifj zxJD;ZQ3G4bF_?24p8SL<_)j_g*%&%zn=cs4oS_-kk%s>wPbg=)mg=B<Q+W@+$EKNj zPPDhKzdjSH4+n%Zo}42O2ka*^t;)FMRsgROpzRjKL3H?~&%cSk`;{M$Cm>LjpJ7uu zNv0xcY74><|KU+cPi45_G4fCSC;U(0zYImJujFU>3dBeT(ZZf!b6a0NF^mgBEnctK z|HNwX(dY|cPz>o1Fx4XrYv&DXle)UwJ9XJrfmN8G+=4s)la~u?fLX0BEaAy66~8!J z5Aa9c7v}4c7vNV6PEy}pudfAFe4=P~R}#7P+Bdz#e{}W3!GC-{)aY%Fcl&9+$nARA zi%qV&Pnb-in0skJHpx^POHE9_qxTxKL(YS~+Xxg6Ml<p4S`aL(w4$cJx=~`X+&du` z?`kG#n8NYU=_7Nk33Nsb$XCqVPqZwT5S%f{BZHuZv&y1jsVx7YIni)sv_aWFiU<^x z7`ING?q5Je&T;-!=Gcz5oR%W0g#kiu_9Ea0>9;$MP=dW+Qu>t4Mgk5&p1mm}IDw5& zTXBchp<n7VPe=U6KmSGT{U<}bC=p|ZAHVZ}8T{ne)_$;zSX~tZBq0A0<c)#i9PQyY zTCcUSTUv}|6Z4S;!{R2%B`XVzH+jN>q+J|ls9;6#-NbS5rI%Cjb26nITbo_F?_Y4J z#m1XG36*voeYtI_HszqB-Cvkfj4Q`Blc6HPUtAlE6!vdCBj+Ff*0<_A?|&rgFJNfS zd^sGJ!uCX0gRLvVn{La_;&KZxSq{%Y-+^V({^i{Hgy7)7JT@URKe-+?Bx7oe%%?l8 z@N_G}yQ!S(3I=*^ywb2t`M+*HiU4Vb>ns)@S<25OsRs!a9Kd|&1eH6RCAnDdxv%jY zwgar!rZae2u_`PQ{4hM+-69Kt@@@9tO-z~MRz`Ci!XfD-F3^T3{`b<D2rvj@U@f01 zOq^!Y0!K57Fkst1rlMqSr_b;E817$wD)=9c|9kPlpNRj`sYkn2%YbepI@n3xC&54t z*K;|}SVL4LY(mC6`%HoU-Oao$i$t++*$8CMqL_KllG3y7ZN)^00`CZ7Uk(g3mIY8o zRw71Tg0dbKMC>e~(T{0F8TGiX6nWX?pR1A+y*#kmC;~{7_07|SI4rFfWVzB=DK}lB zBPL^%0@fsIz@m&+-MZ8~8!dCjMj>^Td*#zZr+*y3{5#)%%=%bPs&EzmR?&b|D<;sd zptRy!zW!p^D#ik17yk__%@~>qbidUwval#(!`?-HcffC;=<-jU-s~9l6nLbc4JB{m zx;44szrL#(%T5QX8mV{+sQlK;6gon>%>y0OwHYSYr<4Uv!DZ<w)32mW$ljWR0L3@4 z@AcPrv7H5=_h--P?|%97@r8GvVxk~)^b3nD1{?fOxU00^TW~2{q5D^JaN1*n>23YM z{zso@ZMn<QlM%4t6;h(D-vNpgxRzA)*=KlU5OX8KDxx$lV6_%6JOhF*1fP?%d}04n zZZF*+{Os>CyoiN*Ct@2nbghZk>ad1m5{tR9t&1H5VAm}XCgk0g?AsGDWT;FNqJ4at zA7+WU<`0VhH`)E6;XnAG0|3V!#cCkidrS3K;d6wDx%-PwU=dJ#_M#<Yx}ibmMjbjn z>()K}l%*SrcH}G$l*2aY*VbUZ_o!0d=1HHbM}n;fK3E~EXLcaWMT3Yp;8~bd6v>SC z&cMNPnl?_5<w73=tn`#{E6Z)xk5ghu0pdb#9d)VzCUeaG%^oDTUA$m^CTF^B6Dk>L zG)iQgWZaBiIz%A_cK!3iN6OX$89pmx3N1Mqw5=qP7I#Wms<%?+SAX`$JNqA0{?r%- z60rlq_4A(b4_$jsV4$PWl?*!KQXMMDTh=fmSt}y2pXK8<S|+O9@J#$hBuaP9cTyY~ zu5Dv%y<U~}Tl>55Podf8Hr)=2m&Q8dE|`VEe~mj4<K%-W(4X5jT5Mj&4%R)Rq^Y-| zM}`dJ^zKU9dA?&rHmOA3oS5)<|LyO7bnX3rh~-h_Q5f%8Qa|b-A?_V{Iw7N~3b3;Q z5XGks2;xN6%_C&CnP&^Asyrt;lu}<BN7*_+rpcR%kh_*kR3?N2<k;z@ClVBt!r^R8 z3$Dg~*J{@G8ok#Jz=1JBp3Q;b^<Z2m4(P!(%E==5dOAR(#hm#?h+CB95bq9*=?sU2 zA!V1s0m{5Z?xKR_@>H+c7?dJCgvyp8UP4Gz?q!WAE4O*_C(G_t5byi~sb^%yD-3}m zJj0OSl@L?@*8u;KCHg_|zYV6L)l_K|uP;+DRKB8d0E2s!FxJJ%s_bcmyl8_Ay~{tM za;#9;0!8G;Ab%8{=H>MAp1f!5j#uy>tCahdAvLKd5pdQg;DT1dD-C3C3&?TxM*cOm z3dx-7RknS{{|x(%a#W`5s5@@`LCuU7A(o`%uk>tlC~gO+@n@L_3H8aou7t{?<A3(s z-->U)|6v@^4^aHbpyEBpf!~bOqjRvPW$Sw=K8~0J?`h#Eo`)*@oxV5u7ye6ZLALVl z)7uY$3Fwj4;-4|<nK;Zu@N4*wc?<u`+COsaiWzLsLLPjJq1zb~xWM>F8MXe%X-*E$ zdWFrEJkF!2TdU7sf{m85{c?2RR_b51y$6kF3n=y2_3ts@&%SfS-~RHCFS`sFPPJ3J ziFLz&ouB3MPhF{?dCNb@=Kf+6j&hU01;yXe18bZ8A3E<Vw*CLOI8lp|&S(dMm)edz z`l_eE$M#bGk4@<#pesvN_CMR;LAsVaB`QEE4qBUjnS?-w93QZY89uWFkiBqZGc{k+ zt0}+52e;q*Y!|0N#tgTYu=c(&9z+dLmG*<+e^{$OB>e9Qtrt`POqt1|xip}UEJUEP zpCw1NgJWNk{k@!HUAGaoz|)Jh=$5($#qa4-HRfIub~L^-7Kfu0gxZvW%LcZ;O<B#} zGhV)5U6ay+24guYY-MP{+9WgAGiv)|p97%i;6;IF0{N_LIZNshWJf7;Z}SUFhj`YX zqFXer#K65?m*IiOUWLHlG9BHe9D>yIccV*m72XvA&z#soin^0DUT~GZ1gJSHK1QU^ z8qlq_ezP8}m-eVFul?+2o~}p#Es%#}zm<i}<NWX%xQ0&tqwRIzOx*U60W`XeK8y{e zY0LUuB;@BFRK?BbD|h(Az%oVP2#1|=#$*${hMAjA(Dg1Al$r|ZfaXT7M$q<(aGSB$ zeH=9#7F6!65!iwIi<10>364!35>s(?LR+|Z9;Sf^3dfdtcJZ~Sb&f%@8&EM?B-i1A zOA`OHtMvc5nm2Ros$@VbX%FX4m&jdz6o)X^zjWNwDxtWSPT7HsIA@(i;&RFnZ7brm zAV3o>Z}LRLyRmA`YFusE+jN2p;gP;4@)Rbl`8+}X%gzWP;QQb8>9p!2TbL9#90lV^ zI=qV+vJ~7r&s{e}NH-1RM3os_#eKsB5OENk36+gcd4|qtS(k-qSgf!>M`4UUmNWGk zZGr~(9K*MQcCVVCTHxC8{~d6u7$g-zcM8iGfQ-qpQR_}2Ghz+N;|Iq7`2Q3C=eqLo z=~0f6KM?*W*O5JXmMK%Il&#vOJg`hz8mB#HJs`gm*iWlYfxL^M+(B+RU&zrG>|xp1 zFdh{CJN+@nZerm-op>HJyZzAteLD0=Ow0vJ&M=b%Urz|=HSU;4Vjb&+{7YYH3r~i; z$Ta*<3)opO(iE)#FMS0R1jW-!porKO15x#wUeVaxA5GaaYxZIMfp*{j=!{?Z^{>~J zEk2%v6RWX<jfCOfxH!eREmc`Iy=fl8UDRcTQuovn5YvqeMNJ@uIIJ6{P$FVe10t~n zTWvAa=_y>0bCP#{guU@6<9~(Y^mLH2w>SL1-X3T>WR_{Z(~d)%=VI(4spxUAIHa+? zg4;8DZ#5nKdtji>J)v{Pbc%#U{GBg<KELqpQ?BQfuJQ6f%Bglqqf5k1{`EwUcBH9W z%|2cCoH}jrVnv;x4-tj^&%wuC2N3g*w%3g?3`&u}Mzpf}{UW1KzBy+2A#ALCn*fY2 z;Z%CSzV>P#E*{kPhTTc&Jr13bCgzFO;~~e2x*~R_G(IY`W}D317M|NxA!B8NatfE8 zHA|ZkqbRKjekdCTTYCln<A(n`rbmohpr%CEPsIP*-@i8g%P;J2-+1TCFf1w+Q@VV| zqXQXW1z0IPN$D7iONK>YFlk6jc`ygGMTqNU6X8<eGKd`kw|hSL)u{zsKO77(+g~3F zmS|G7u2=3ar(_9$959>vn-$gMYAX8(1qr|kXWDhm#cikUDZE7f>8Z##>v12~X~A6v zSb1$k@>BU&)NUB2Vo<1~e9;G3kyw)7CG^V8(b0g}k82L}Tm5Y2Y$85&;9{)4Wt2lX z>E1+?qlw?s9|OD2$S{oPk>g;%i^$Jpl2%LI^mUFC{F28{PnXg@T#&E7K3E8hg-IVv z1D$~rjWOf@;NSkS__w(CU#vONV1mPT`hT_kvA|`$>0A1-el*NtUfC|&rt(UhLDIkH zuBxOz76q%~uH9OE#<@>w5+O;Aj8(mEE6@SPwd=he3tM8Beg{70dzZlrXpUo)NYSD2 z>w3-E+6cQ%s4v@e%)FM2v6pJEhUFqg)dVuyZG26D#rjPLZ5mr<0OoJ}!5_!J`u6wh ztv=?zTRGSszTyh3Lv2omwbt)%9`gExr(&FMMsBV<kL)L5R9bX<jo@jm<YZB?Uv^3B zI;xUtrw#q*=<93~Wn9&Y=K<NQe8jrq?We}pOS?QiyN*r^=$Vo@koAQSAM`?~Z?w47 zSNP(7gpAZxK0U_!_M0Q<{Jry5#m65Lg9_WAy6Id8hJ##Sr%QdDdW1!8>)F~^dyu$@ zmqNA5rCJK+YX0p9%Dsh+o#$h!@4Q)o&eg^vvbV}M7tiAHNQKPnbp+B;;H&G0w~orQ zRFzUj9$70;t2Nmos+&i@vDSx&e7yUXM&FFq850E+QicpGP$-iG@ifV!c#Wow2)X-< zR_LZ_GOTc|jn;Nd9g~;}9^Qm%!A8Xf>!eHxh3gE8Xz4Tb4Nlr@ad`0STY2$l9zdiY zc?(K)?6jF;p{78ov(D3z_X|$YjuW#;l9_0EYMtCEyTv1KQnK<`tF`@0CtZ)ud$7q- zCxX+-Ak^w&GS9aDp+MEi$_kt|mrsa(6u|D$*TeCD^MI;1myDl%aMt6)RB4)FujY@& z|MRSusyjQNPpPR1h9q=M#P;~ExD?r1v(G%__w56sRZDIdtw}99J36`ckZ%S}WEiJ3 zb1~2<1nB4;Ssuq@kr&sV>^DaW4BtA~8sc)Ww-S05ffU|PJ8eCg=U=_|LHz3PekcB& z|IX(-@aw@Z7!9YSfx%c-Vy&R9l#x4Rn;-_Uene0>G%M#A*BoEFy-W(zhYYCLYYc6l z+BTF3+N&F~@-bh){IvIfGXB?&-H_jg9-EtN`0YaK>3CU5gjvWY;dLZcjh~fhr!gaz z>3RL<cxsz!v0Za<^&x@%z^%gG#}f@-I(tpN+TmA-9Uy($Jg<8{_fB3xfcW44!*90Q zY&1szD8wD5_oP(6U)A+XKXe@ZX>FB@m@*^8M2gvk>X|4BuqptdLqM=6n1?5I87O?s z>x6zdYg{+1ULxBMg`It9>S<nFEcs9BrZ^QrP3-%bs1kw!^Qb@a9o|b%=LBM#e1}S4 zD(1;92fD}T=-#yw&bk?(txXC6gkD7kZ34IeiOG?G@_bE_c}bC|$rlC8s>Ntv$O!nA z>-XFO$#LUt|I6|J>cw~ZEB+Vg5lLemgQZ~$mIW(9&WWL>PtuwNQDSHd2XbBa)V0TN zctfTS)dpHKdb0<xAh5z1^iWoJn_{VLRuFVaoG;9b97%q%aE*dO6KuF#!Ae=h7BexA z3QEni8WVH<<5?_+>QlkX0x8H!^b9EvlR=sZ;IRsavU!LUaNi+`*Vl3n;yML*VR1X* zPt>QJv`QRBgjPjx#9Lq<!+YyM5CElfbq-aSMPDBQ_yGPT*V*GZ4$u}EmO8^Wb4Lip zuRidoqJ&e7{ZD<7=Ip4Tdqe8`GKiB7RkWk?c>m@ga<`u7X}16t6l<3;G%9r}9tRS& zbSyc#e2}mH?C1M{H`|x*kL+tl9=-#3?wZnbq0|i5J;USGBmZLCXNJ*UXPW>oPl+7D zd2;^C*|P}r5w0NQ^XH~SFo)Vch6Ad}3IkO+#>}W}?~Pzx-pw`|M8kAJHukW}8^%%9 z?Y;;`&o=pw7a_TA9(XH5LQRTwrYfaqY{RNZURQR{oj(0_gZp>B_fh<_uYK#`2&LpJ z<^KkNxxwPWP$1q@FD=@9=cyf-r9a+2>PhjsHDkovq*3r4pS*!=)oGMUbloL<-RB$L zjNlrac17PG?Oe{(+i!LH?|L@!k0w}5YI?O<Ji)))fczk`Pd(SifQ5}Y09-($zb2)$ zJoGDK8giNrDrz{+?>^1SosVBXdb5<702sgqM~Lgz@=wxBGS)KK8p?t-e!mAR(EX3T zmH8HeB5ysh47O<&ZzUPDH9nrgLdPj#YIm~YnkRy`W%?QYWytnU#avc!p^c2o>3;XE z4t87@e(r6Aq7oQ$mLHx8{1E?z$K^?qz{{Am{S8eBe3v?mam}&fN8V0b)Z^iV7yFAm z?uG;dG*1Bogmac7UpdZ5ZGoZ1NBEvW;oezs4eduB^tkA9{k?g*_~Z%kWVBTz(WXcY z|I?h6gwdSF6|+8Jm1OIJW8vkx^bwm`G%1|^xgO)nz;fel2^V{gt**}#jCO#lueVR7 zb1Ga<yHbi`1SWKn_Ji+_hj;s<Z`w4j#j8g=J#njW;$WAYKv%-CX4Q+F?}#y=lXd~e zApBo@-w)XIeon$eCa+-CXRfuWK2+~|D9YtUHxoR`FpX9HmQyr@$McRK*;sC?5feSb z{8^5?f;sRAaOu;drFY&?k3H7q0qQ4|Zy_2^$DevL#(jjI40w$dzw)(j#$Wo0&&IoN zr#b%`;b2vb%~WM;7VdOY_eh(SRk^Tzab5ZB9oDv(O|$%1ODRhst)1PWNa-bg6T-&a zJ;NUW|9RyY1mz+nfs?5btY5MIZK&1;=;7wS`eJMw3T+Il99o~y5AFQ-*2~2A&j_Nx z=xOa^&8#?Yu?^aK&8OAkwE|980O0@lkKc=rJ3#zG{`F*<a-G|Fd<Fd9fEK;~<Lu{m zrORX|{5q&f{67c(xkhUt<{(-Sv7`g9ul|4J{aKT4S#li)x}Uh2H39?)0iq~M&B3T{ z>4kdGgVZwX7yW{M)bDJwwzh78v^I${43YqXKovw*0X1N*Au}thZVvZ;#}#M$TFck| zBC^J;yfjl}#*J?{XAi#J*K+qgoXan`{O7lbJM{l+#d)jymv8aVF2p=OoBSW*?J4)c z(BCxeL|?)2Cui!*XIXaJSv7_QUxX=6q8yA{s-MhqcL+I^X!#XNy!40tXipmGkVWO# zf0k={|8$sBNVCAW*wch1l_jvCb7lHm-nfiA{*Rsismp2IiC|I*{uWn`Y}S>VRl7}$ zT}9oc?on6Au^Hz4qhnW+wXnIfrp!>VVTKGgX`8~pHvbS{BWll#a2cPnI#!sn>I9d* z9h~ONFk*Coco4WIWOf_4T2>%sKQ(1F$d}<*H(tw|nPb`eK1*E!GeVsb;B4J0T!{fb z;->K~A=IQC`-cbrpt?F|R=p3R12NK5V&<(;D}$%rql06uO{V90o0QEg;lqHP)0p(E zR@$I&Rb)l6?z3lr0w*W8IoR?bMbM{9J(36e<Tl_Le5@f)Dy5&qvNB?vUpRUziHULO zl@*wfZ=eJ?R)cZmfAf#OkpJLU-wFlt<y{;-*3$lS_Kig%t)S2Sp1#lCcd|f^EB!Lq zXND)-rCauTX10TEWWe0Yqh)P=f0}yVsaWm#FKSnAp38s{WA`%CpC8!1;8L_1vRfHd zzbXdZJNkC^_pTDPgwBEMG*C`=_yMnXOOMA4V2`;8cu&DHX8D6}yn7`9;EWHyurn<) zINP^cr^w@!p5+`I%|Zn-S~GF;#1H{`c)95T+4Xy?;m_((I7Gq)N+VS@1|6Z`e@bVd zgJt#WA4`_B0MDVkC8vJh&l2bs9yV_4boF&e-edi1>1-$epcJbK2$P|JWB=>lyR9RE zHm!u?B`Tfh9~+?e9@YN*TC#gpcdbA<lJ|F8gljkiiOZ>%G)_)LRJeT+-;U+5VV0zV z67BjA9I99q%x%iKguD*66B>G$%==I;_+rLK&?pW4s*kfGt|*DV5>vRcCfJS{Q~Dnq z*KSwIw>vabNN1#|SuC+p^r2yWW5I&FHyuP7HQ&z|HwGt?j?Ggn56iFF%_D}98>Ewm zJ11ldRwz8{BIazIFm}!EFh9rZ!%jd=)JrKJJe%k2?*M>Bo7I0kdgwO}>#DY~W&hM6 z+TU#s`a5ilTfbUhT)_gvjKZ?vlHI^3p>AEjd_>@RVjMCC%&AY{^L5SWA{<Ll@VZ}q zPX^_gLmFp|B`!z(@tRxPZ0c~&QgZ8nPIb^#isKIHD16gqhE|WGWp)%MLj6oYD3cy- z)_R_Si5Il54m@pPd<6^ER@cwYO90!i@;`cg#=ri?JIiE<*%5WeQ>dGX#E7wfYhRdN z<D&GB$^Xt@TONo-x1X$!$;HqGmL-$pHNrlmRAaB)YW>;xpXP1tN&f04Ye-oS!L!5v zsQFB)+|)7M@AtjpGN_*Va=nGW#l8RBaw)&AVIJE&liLd1Z!7p{lA|VaqBi+{`SVBo z^Z)n@F_Q|E8@8)f^I@3(uhkah{}dkfDEjDf>n=Yzl_7v%S=8r{|G#|t3i|)xh=2o( za$A@2Xia7UQ@%mKXR5&=#UWvesRIe4{r{8w#X*DXpmMe)Cb?0NQBHY@ty3kX$&Po9 z2TY=#OtQxO78HrL-R&$SH{|~V$zTD9%KF-Mr;^gu-?MaJA`w!SWA+uvp)hS_5{9iE zd{;0Jp^;~ipY%w0LNgO`-eXM!Cz)x^qeC)b@_#Adlp*J+UKp4={a=Y0I&#C4>>Ud< zw#CfTylza6`#>E%><sZZhMnms1`pYt%4zOk!tOL{Af;@a&4saaTOm};rbERme9*<l z&{xuA2RCi!UKbU|1B2v#vz+~LoSv{mK{u*m9PJtF;JzI$zljJw;FNMDvUU|07#!nd z6b+w15$mxU133`VGG4Z0h@`(>r-SxPN5y0yVXa%ve%-^<%aI5GE}oJx)iR|<T^}45 z1~iu}xNH-4pPIR-kepR<gn|kM{+)zadi#(VXFRuDD<h)*n}7Oc##6S$zn(1l=h*-S zW4)Gt-fsQuStF-@)QsI%`|IMF0EOYip0m{Kc!W;=r?iOP1wrDMf@FDPV)nOc2)SZ= z4ehb3`GF5B$rb<>u)^wg);1Q<;ntrC;@um^=gh)1f3gq7<mgtv>WuM!-Tc|coKA=D zZ44z#a1U&W#8hTZWBwqVzkK(D`nP}mgC6<O{fXAPVHRXY2eb`(HK&jF{14;IlQV$q zk|jNvFrggjY@f)klRP0aX5Fhsc59o{sj?F?p1pglte>AwDCjnwqj0J3mkpfD9+%Ei zwt?A^K=^mIL)18)n`6~eD45L4oH3#e(Gkc_A{K)Pv+HqYxbmjoug49pHe$gMX3WW~ zZDMOLG@YMS&$-rPO7=-{jssXxfYa-~-i^pZ2hZ*>Gg<-6jIn#_UzZbdI<GMyu0*>j zX?>eqPq;k|wlrM$`&MIwZh@y@jciw2`jJ`l*wWmeToQ1a+2*r?_;xZq+Yb0@-7Ut( zyNs^$R@094-7)>RA^BQJQeEv_^`H;e|9YRn3HWuCOj%&Sc!KgGe!6aSo!FHIJdz)` zxb3<Q%fc_|+`xapsgv@|!3%U&9dF0+=&+w{6aOD;3klkhz}KXM>j`J+KKoT;6HLgR zF?LTEs`V9E6Feb5;ltA*?6RG!md^G>LTcah35+_?j=H6`87!II#y_CUYJj#pfZ%~; zx%8o!u%D!O%Eq(>flKrQ_V0U_*9|;;ET&G%MM*_)2v#q3LYn>&^FIjISP#bwcysC8 zAN~1z@#pWnw2W|SF+pfH9ov8GhxR5pMQj{o4*Fn05TJA)*>WpdI$kP%_@4oV{74&% zDMH*0Ng4OfxH<#h^Z%-oN9>1t3Qz%*ongL(|1^h-|0_2iBr9Tzr;2Tm4EobDX9p`T zlYEHJgx_b)y<CxbgJ0WfKdS@oeL$Cw-k%N7|NVdUcd>S<eedo#xhAyr2#GVz*sj)` zC-I=ga^Oic8k}OryDclx(4^|l#mx`RW1Ywg_})|Ys_S+pO+ZUePm4oN`u}ju@bnY_ z)K!4z6Qk5vI#wzFKN$<e;KJtrH=!!dSP9i*`2To75|IX?m;&z+Xp$tAENxC)Hl36D z3xCgRSI?QSh*TK^FJqkR+QNz^=x`v0#dbkzsPYE2*-_DpZCE@e0fkUx`f6^ee-PDK z_#W?|xrL(J_8N-fMccQ@@FC^?pSVn2oF>;I-xp|JN#*zo4KFloG7+N?FjkUknF17& z&5ZNA6Uh~0p>@rvVV!GxTigf>l2`-J_c2Rsc;6zjRQH&$L-4s;)R`U>n74$7Lg801 zMNUY;(#crPG9R}ShU6`;;!=whG*Kxf4GsX8-g9<v%_PLN;;q`8`e2XO2-tf+zLX2f zhh_U}Mrp9xP;Tl0a)%t$Qj9~7x8TMhn7NGbt=GukJifn{Kb^e=ptBGMj>ZpP?%5uA z*K=Y3yFF)N%;6j<e&k_?M_*gPa|wnn)*jXC#kF+*bx9^gBh)C<PDsrBH|y6vpZVYY zXJ1-b{?{d=2oi|T>MvsGpek?Y1oYTj7XCb05BTwSdA)ijZI_kansczU9St)^X3Ax# zxtW-)tYY_N7efMYP(ubJGcLc@18e&kI~~<-WXN$uvTB>6s2)uCBa<8;n4LB14^1`u z*R|vyx0@#O3{DC`frEBdMX<vxV+S4Hp&;lb72-Hl&YCQ|eE0v)AO3B=`vJvta`jd3 zLJc~)$R7hg8hNqNFq&~LJ$V2yfAFgEqt~=sB=ow(*7M~2`bp_RO}I=qs3bX=bx@j~ z4#1mkkbi^q7v8?*7qL~~Qo(mVKnShb1hgZ8X=0Dffo>a**U&s9-j8F%YX|d%axA5` zU$VG;spt2uLf-rEjE`^2ds+5>VEY5x*4w0Vv*9i|dGu0LoK$Ki_;3yOr`O1#R|d6w zUwyHzedepK$FYg`KiX28ke>b-J@TzdL4EK2jLQ)n76-+_frv>WlRU3C*A|mkCw-TQ zzj^;82iC&=wp~dysq)n`tW8nHsfMKLlsKHE*p398N$EU2i{9CZtr(B>z4t2WqgUqy z9|DY^ahX5H`t$+yPp!dDaM`QLfeNA}0e|Onk2>3DX4J>8<Ixt^2`(jUmu&BFpj9~# zHZH&T!dNZo6lZd;eyshksaZFeQ1wjoTaT%q*XxXvYY2||?#sty@0BoE&h@$chy$zk z$aQVbh3zoir&HWmyLx4(T`&n@_RfLH;H=wixd`3Dm#-pTUi#YG^?NOCQS%nlhzsc> zFaqn*QX03$H;SPH&ZLmVW!G6>e0@G*Nt@-h9qut#Dn~al1KHVLdV3{1>0b44r53UF z0Y6vbVR<~15+?RSwkD=u@_jfEIil<zestz5WU*Emw0|SjOeF*X76nXY<Ied1{^57x zzyH7gC-J$rL)by7Nhao~`=|bDnFtbiq$Ll`S#;W4=c@GD_aDY;Y})XDGJ(v2WKt?x z2MQ*dfRc)P%-iZA|A+S&qtZ=;hTKW0O7m6bYVQ2cHdAYtOBseWXhC;l;EOSAbS#<h zaT3u%+dJfbW5$_VJ#hxW4KwSvJ_le;vaBO>qW#mv))la;e8J1r{U^WvR=jxYsP|tY z#fVsg!f79%*2LCI6K1=!hn8Y=qd$7hIN=%=?C}qTohb|GVPrO=wOxBuAl&6U6io_X z!M3A-Bv?{`7I!Qg?xZjBGb<q~=S(XBPQ_J8e<V=kB94x5(t^{)U8W4O-PNaF>3De; z4{OQdV>0!z&9^#CUjXU136jyMpu;s-5n;)eFknavHm5&Up6n3xwR%&n*EaVeYg{EW z??xS#LWiQ23_;+UKwx`P<0v5q!cEx(WiMZ^wl!TcU;3qED3&ZR{trx#;C{$9#yOX} zo@;sBu}KFq4;rym4MuLoh@fuII6zCFb*bw0B2fYXp@i331t5WT9tLAyY2XOYe{`p; zJRk1lcy;!s<YdF`mLhRRB|tk=96%Fhf(Q{<aj?xsz@n(2NjFLV6%>K{?(MYc!g9Sp z9VOR`vtdw41kt#TNqqhEx(h6UhXKoAIHPsmQjaT&(ohFIKU2uqsJ!LN*^byU_t)PB zg?@ZkN|p^B7JsgNC(7Yu=TAB)T2~ff4>JXcx-@&T|HIh^&R%u>Z19=?{2zXPjr{_? z(oEU}{^%Eib7YFsGRBo#z;82Zyl?5w+1D-+o!2Vy?(jB42%S_W8=~upSx|}VRNqHO zq0V_f;9($i7A)3-{~X|gDX?CyfRJ2SFF&)7)COP;g}>(#iWzqes_Y;4(N3PoW<XS< z+n8UZ*m0O}kGfSDBf8K*y<CVo;>;<#orcGPfBWBkFW>#q>y081WKD*4*&Bk(>0{c{ znxiHO3gFMX6$;FRFp1dCZhr{d=S=gZ%1c=e=y&HfA_>K9Kc~mA*(-i9E5F2Z;so*z z>3cIiX52|_F-e4NAS3h`FTzK^xMmotrxP~^7HvQ}al*D{RXE3-KE=UmYxV&v0Rmfq z$eh}X934oXexg7Bq}w8A{CU`7f421L$SQHNITGwGS0k{zw7go)5>0+L4wH{BY*RSh z!F<=(2HN_LpoFW0+G5vdTBIeTO7RSE1Fd!Eq0ruDi43lYpaYoFzct>IY!4sCrA+5k z8`HMHvaBHA4F8Yw$ibqN%lMzNf*e)}vI(v{sX>T7wPv9j$*^L4F7bcW@PE(h6@s7) zMo1Ck2Vq$#bQ8sr|I2Xk(*0g)+ikx&SNG%O>LeaWIcB_-_;J|~tQkbql;24z)p3G* zYaKM`{15cT()V&NTOz{e(lrb=4&vzqwef%Lw&46f^2lJM-T_Mh2SZCDpfGivk5T4? z5tXhK%@E<aQhKq124Gt+|EQTVrz*kcYWp-WZ*`OH>AZ#Ef2`C&gmn(7!~noMKl(WS zw{N^#n7%3JIoQ{xchaXj+~n$(Gq8%sbs&WgPx&7{>It6nd&KwAt)CS%UG_P`==*9` z-B1Q+Q0+!T9M3+9|9Kk%rR3*qGs+yFJfKb$&d<8H%`t!=eVwn4;Ur4HFK0h^<`VrN zP110pr8v_c^VsGJEnv`l*aKDpa5aGZa@|w&`fBNyKK~H^{2zQSS5Vy@ga40spn$Qo zUDp}v;jmgT_-_p4ik34&yxa+dlKnRSmnEV)sSW<8!Uk+@t%_+Qt<mSe%AwW`Zq>w? z43C#ViSa)bXEpB550VmB#~y2j--56erE+<H7uH*yhx0p7AOC;-Y>(rueHDqdR`%cA z8(cw1IaL5@qlQ6p?#9^kG;DJ>zX`>p5HnV+-8W-owDEsU<{kXsaztMZ54zrSpg1XS z#-30y=vLM~Q5SdoAM(9b&=dY2Oyaf`&Rn1)_-EEI1U_#NP50~>=(%XfL2^={ENXbB zrJg$V(eg##YLjltSt79N)atH_0;9vB`)m3fN+?+2cVsNW!cnKZmP;>+aLIk?#8fzj zw9*Ras!`;?l3^+L;X7@+;|DX<?qDJKcd;ZKYsnjmq`nltZ==f?@}mPsK9?M*c#Rft z|3XW6)@?Vss5G`qst=w0t_q&WGCp08Sii*tr&foYh<G^rs1A3z8psn#tt?M6%?Ztv zN$cfG1V5%d|J#4^rD3z!X70mD$d-rIHS2S?L<~eyB%ZKy0TxNgX*Sd4GF%c@bUjQI zF`YA}Qx*WP3bo>80bIr%_+)Gepa=`z$Cl+p$lrv0A$gaod$67kgpwa<Q~F=c+{gT~ z0+Mk}-@F|8Pn2c!*enH*Y<Vx%I+r<50K_y|u<A`BRc3wzx9jJfm#^b@zW!dH0xjrS zS88@$f%}rmF_;V28H-zL4(U0V0~q_qfHXK`R$5;kTa&K}_^^wN*{%fZ-hi<)z;gbm zJ_-=mms+xG_OHJR(!yr{O{B2@YY1+YvyqbyCL9bdy5sUfPSsp*4y_Hx=4<HPMR*6v z@Ju_IT!S?UfkZ|-vFtb0j<xg=fD4%f2_!?_XWAv!!4!5B_jF@Pr{oe5a#Aw%e`8nB zisj&}D*<rf`pzE)Dm}QBY0Oog-bdNgR$$C#XBj?yqpq~3=a6!V7RUBb2MJ&e$s6MT znto6oKQsRix7An=$A06`yTlds&i1?Z_|q*i`#)wYK_Vd~4u;)Jr_${Ip8ub-e~Fm( z5B`U8SICW>TN8=3eOzH>kgNrV_QmkOK4TI-Ft9oH5B_Hcwd7zD4|{oZLH5lp?y)!4 z00yI1!@!=ATqXafBF4T&MCJA$R8ix9+><E3RqP;o8Ls6v_SbdS`7iJO=;MU0Y`mh$ zSdLKD@OO0}de*GyIOs!J_i41(pBu{N4vdW`jDs1}oH6!aW*04g*0JdY=D}iGZaxN^ za#lRc|5&)0Wq)~avbggUiNp>M0|81d;QPX7WB=}=H}RYy*^B?LFK;K-&O}d?VEl1N z=d=OIfz5Zj+4%sMU+3Eb<o}(y`GP7B4g7Ea@t5ME2UYS%5`X;IW&dS%|0J~*@r3^~ zCefZ6$0M2T8_q4x%6H{x1O4WpUBdn`i_Y`9aDPTj`l^lkssBH14IW&+T6Q}X&~gh* z@9RgO=TUw=h#5v!z-1`yRd*l#k;QWbp6CDWl5-=+jXIm+IY@JOFmhNy0kiu*oyZ<+ zPVjg)(TteM0o2Ln0a$+~{-22o@PB*)|Fi60693cCc=o(C097RQEe$hks7TNQVG@K~ zAPBxov&mF|7==Isi|Gh0Vzy4$0Mxd0E=!J{feXiux)W6fIu%X=1VAHNI4dXRj@5+S zJ{c<<2Gp0KrDlP}In!Z<?%4A-c^bU0+Lc6)<6u`$Ij}iHolFw=j7(RV0)^1td%Qnd z$IesBB3bKLPRk%G9K&YDA(dI|*-WJHF+^O(GBQ=MeN~YR*cG`cYBjo)HO3TOUpw=? z%3<2xdwdGlFe!04Qvc<D{DpqfAdrJ~&+lE3e`;li_V4zLKK9}UCd<y5b?()nLBd&M zdm~XTVv-!k7g&i=&2GaeL(Y}|v{V~cXC|OEs<XfYj>jNDGWj47nFb_ZkD}n78o<)R zQMRGRA>wd(TOo^eo|fz{EGe<+xFI!_qI#=EINY-DVQ3M-?G?4DO-$P%PmKX=hq7M! z@LT`pZ`Z!^iTi<jHHp6~v2_u0FB6*+S_=t-cBBe3>#=4FtDS+Ig=1KKg{6w!xa)Ew zO*FcZ&vT>E&U+5lGI(#i;o4bzcKwT>Sa_^9hivIIT)=2sjok*5C2hV(^cZ{WeQtCJ zqHz&2TsKN#Dv0H>f14#s!{ory!%l!^l+V_lsFO4C1QucvPGnCzp_w|MtsF%x#@Ls( zWK=5H^aM@;cL3G7lSri*IL)yJ!!wPOnEh-(P0=RHpiHLr?&LUVrCLJMLfte*Y=O+s z0CwFCZdPoc*yqLiNLs&i{#RQ|%Rhf+{`ViQ_Djyfl!DUrf@ay^!CAP0r?bxK&+%yg z2khLE=+_eVzP1^@R|cS8wnYzK8qC}I-;-u_hok$-s<H%DqJL4t|DA&f=}v?uBO;jO z<9h_mXD1_KW`xYQ>5<;vUk<*4QH^Imu2}q!Hqc+%`Vdrs!}x#ffBG7o+-XR|LxtS_ zk@UMRIM6<vjU;<tbsuE=R-Oh%m}Yf5`q0gI`SC4(>yN)nx=3<`I?&ojU!_`g>TZY% zT#uHXvDxuz3B(|qHU3}JS(qTGxbyUh4-=%L%!Fvkg~FwZnK3WGZ%nzL$p14oosd6m zTq00j#yr;8$E|-8CRr{s^<%C#?4KkA0{=wkFy|xpLw)IG{WH6NLNK$UAmyX+P-n`n z>dps@iu*)D;l8Vp|K&gYLdd@ce(_wG-g12D+zs}R+3lDKGdsZ_{I8D9q#0o^y6j!K zcsQtAB;$<}{0~X5AKO}US`d#mT(8dC&HVo|?CAG(pC~<!aZP>u^1*X_a`+$eBa(Fg zh_Ogm^fCwP$By7n_+Ru0Zs}L*6TT_pZ+95>UmFjiZ&5zsi9oxUkgGlN+3Y`5_s_@w zvGc!7@H6Ir8CB@PbR`ERhdshDYk=PXB$Om^iW*Y3lvxR-K*Gu%(Ni!5=z6rwF8O;{ zzNdSd?yh%xYwN7O<Mpv$D-67MLXp+OAf>ep{l!NRKq>2O2U{?c-&eN7iARb=JC(Hv zuNCB&8MT)4G8pVT3R-d(eN4KVSta%gPOrwnW-bLZ+o_y$vpZ{?8iE6!VKWLH5Hvft z_t1by2L)PCR1h@~j-TNU44`7h28R-!V@#5{%~Z6G^RU0t-Z9qy_rLrie)Cse_<>k* z2C<xO$RO@k1Dt*K!F-ko{3ApxB{EzI0tBjam|%^}(S|gVl&R#9rpBV&0xO2BW59e4 z!Q9A8`b^FsazFbEb2+_EH~+9qKAmuPQua%Q%9XLRHtzKm1(csVNiprl0Eg@!u;IBP zIzVBc?F~|qG~z4ke8gA&;{EvcJ0IvEGXx00rt9i;HhppB2{oNMdH^F+^m-I>$Dw88 zY)f`BE#*MC0}UGwlnvV+`s9h*IrblY4W6Cbe3Y(lrvg0uduQfm|6Ok=|5sBt{cAZc z%Gw8#0F7Y`oV(<l<nx*t$ez|sfn~DF)(brTEX@9qY(rZS$f1l?zx;u*e+C}w_;@{w z+!P?&X}caG#Mz1~sKIIl2^Ve4y5r1tM@qn^vF&|-fZB2%9G!~I@eVjw&4-n`-`u;P z(}S|+@3d1KWBshK$b{n}K8U!tj-vfky)ph*roh_$^YVWu@X61KCF(~cJ)?t5(KE;w z93KU)Ow!CL-Q#RxYs!&sU+5oo8uq8r(~KYZU$g$uS@A#Y-xDSW?BD8Q0&DQ<-f6^g z<zb9xAQAmGMd&}{d(bQj=N=r8cYoKE1-+H;+4msfNZT%qR<~*FAFw_RXgMTjFst#8 zg9YbF2d0+IrY0r}$p4a)2H`5T4{NPX4y?X#Cj8sq`yhV*oA0hRVQ+d;=p&jt{zI_^ zj!gz74z{_yXEJi`H~x3PsiCvs(Av@9o;Oc>OTK2=6vLC>XZU|Je^kBs2e3h#IB_}6 z--?a@S*P=VgiUBn9~b(r*?uhdUo-nXn=fv_ftTez9GCa&GyA<E;5wiA?zA$l0{wu% z-~5$_`1`;5q6Z6Vao`cOu-i3~vj1zFEgHsnfV~>~UtNc5e;`Ll`=3?8;{S+G@c*xm zCVAgM-<|zy!j3-~vyx_4B0bu)i0Q+*;Nfn|!GwD!JX48=G~AFIG&;p3ulS$W)KmWN zny~ao{Z~BD0Xxrk18t=|rBT9=;FyW6J%LQ==J<0AozhB5Wh`xS<QL=rc*Fc3YYRY{ zGsgmz!J{yYlqf`sC7w82a2TU>KlVg=L+AdCO+nkKtC2#*U{C4x{=$eD<bw<Kf{xn^ z0w6}7YFHC62&EBJCbM6kBO~0`=8yl$oG_g9LFNAR^?06{d60vE*|T6)FlgLuWSdQM zPx5**%izkgJzS==EpL>=ybj#60*TO_lt8<S?VI2Nvz-P8hv{mWY?wSzCX6yvMzrKN zW$cO^crL0ZuUaI?OcqxI1jmw<6EEVQ{>Izuz6tjI)YtOu0JH4>dc8!}vg{0U4GGnd z%T?s<<m?p)t01E&IlE+9UlJFnBNza$zsOGZ*G<|elOAP`$0P>=!tVb|KpWU3j<;q; zF6JZ-$JqZV15;)`CMbuVQC%?8P($oX8%`Pn?muJLpjduMEo9w=>@$X2;^KWJHIoIn zU9gR1<>nN>bn@K~K91k{+V}FDRq2{Bo<QWJD_h0K?bIZrPI+v6L}h8YT_E_lK0Kh^ zOvIs=nK`7*<eBt2#-UFo-VVbj)Tcfl_7x5nx<1i9dbfWD>M~0T29ewe`;TBSkjj)t z6Mpne9Q$ieSsSv8<rOP=qULS(KRCqV^^+W%sT==8k1B~(!N(`9=2Lf)&Ka{V?MNL> zit9vXjQ8s2I{h4Y9~^GuyhYT$YO0JQ*?A_c%4Uip476(}>#k49cb9;;xA`?0ZCL<3 z<LR$s6uuzuDKLgFtvV#H-xUAnkT2T(i}Szsxn9-N5pAFXuHKS<LC2MM_Rq%*9+;(v zE`{y&l|EM>VMHw8Z<fnk94`K+B+38AXOczh`4js$KTl)a@TA}&!yh4vSJ=DEArE?@ z(p4rYuGdFDmd|IUdc24Kb0rElcwtbHfz^p9AI^2@H1%-)PYak9c$M-n3WnH0_GxyG zumg&Di(`3krNBz)_9F~?$v%UXE9kjaME&m9-i?<ZpGA43WtMt~BV$wZDF5tqzKoEc z(b`Msj~G7*RMmP4s36JJbh@U@j7h$ujB)M@{z>fR2ih@Tgmof*EdK}VHW%Hx5WI%V zLka_5JXo8xc+xP!U4R`GLN;11v(rSw?ws@2e6rF6&aMCYc1^72jJ2h$!dC>?WROHv z8k~RfAG{scp|vOcPbH4~f%bI2`o)pLlp)>()-M6K`pKfXCAhRAjh*c8xIZ2uYG=^o zrbCLO^&FScFQ`+q(MA3*<A2%z_2!(MhW!7^7$sk;@D?UMAQ`IJgI&Fv{3kIYNI%T} zwKXxG@_)+ziQ$3}R&F*DgHO~u;d75p;dPh6q{_<L5!c6G_8g)<Vj_Zq`#JdEd-*2$ z-|Nz20FfywR%{y9SiLFT4hqtu)-nyuou^Uy3*w(;GGvX^R4&U>5KV`IS>$f92_Obx zRc}9IcKQn)y5DmWaw5vw%q(!Jz8LcwFvF4zwxysX4$g}$ZmDP5h7D3sQ4A!Q?Uthj zBgRbB%GB9oI8-24A7nc&+xG!dtk5jSx}}#U_VbeYSu_{`q$wOYObO?vlDxP#BWs+1 zQCQ6^!Pqz}AmR`0uWLgF(2f$HLP07R+opAKkky5~|MWLLH{(O1h=}bGfDG=7v;MvM zz|R5zxF~qPp_W@|$y&E~D11mILXR=CbQ867IjXB&i!c=<mr>{BHeyImW}H$~IY#D; zCxDW57Il>c_CeNt&q}x#7eMVvowolN&Oo+{?J^LrGDm29iLGlTB7($l-(-Y~XGECu zI!Op6MQR7&ZnA;5xCcMic>UhjzaQ_td|jQeZ2_0se<kT~kem*HYr>+VOU>R?t_OYW zpJNf@xwZ2>huhih)7aTp7xVa<)pBkrsP1+zdo^zz{l^mchhxqf5t0wylHj}9-QRxR zIu7xXBr&7fcd6|$GxK#fy%2t2XBzgly=N$E53QkZ8ngXIf!c{+01nEX{fF^nnXay9 zkoOmSPBR=A1zQOnUoTH)h><P7q<Cmm1N$4QD;kqrhfTL>u;a0*l-y>zStmQ9KyGqp zEu{&n7!c2p>I@X>-ktr26YLm%<dpI66aNR6cK-tWUubNh0lDqDr)fP@S|4erC4Q~7 zJ!mxwOJ0v@#QNFnfBnNi-sQ@A^CFbYyyJg$9)0B2mBq35{GY@BNTf}0OtGBI{*^Gp zMvXI6G=iSzfc+n^e~s9g6E~(m`ZW3xV3BdRQn=gVXQgm_@-Der@&}5RqY1W_UBQMr z@FyqSVNC(Q8AbP}o!_HSioQxACqsSe*KFeluU_Z>^7Zc}k~<Z<zHj4}404IifCa6< zD_~a*HieCZd`BiUEMZ>s!QgPv`cJDz=AR@~#U=T~D658><7eRi*fJn%<ZBopx2~n} z|7`EGGmT34Q-%=<?Dnk?>o1Z3H_E5jlL(uLN&0U4tbf;`&+Hrk2Ktwbxgy|Z!GCS9 zCn4}3{mNTPR#AOU0|3`w5hf28z%9L%m-fuT_WQ4&!-fx!9?Z6DCgs{RE>pNmF!anz zcH*FuuxZ>+dG}vnNx)t&k0LqY!GUc%J*jA%FuktRQ%|0=|5<?GVLMk8@G-iYvafoc z|239MB5aIfoD<HM;4a0?L3r;NDo6S0M;c#2@42;>WlGI|LH^&s_zm(urlflGX<bWj z5>SZG`BS^<;`*#<7<y<ONOo)qMW@dKO91X$B?g2}2m~c%ayG29Fo%OP!qy@St7a4Y z)XS1)sY{9UYd{Q8hp`v|8^~>9E5Wut#tc+pDGm1024(|`%2RTnOF>3E)jXNFVouYh zLS)Y}WQ1`Ph!)OGKt^PaBg?bLY-{@xM<t%Qdq~@7Y)a9cYHFF4kxkI1aq@3UUI2#K zGX;!65=CW3#W4uH?q^`Ya~c9R&>7Kx{OfPuWrr=p%hxT^hP&BA*89gW!ktN;rDugq zvc&p$1Br_J$%1N3qX8gLBGMDFO6#}GyvM%c4qoZ8)CeU(Jr0jJNn^N^<HFH6rCGB) zXeDPoLYWbkF4#WCrA&vkGsa#_&6NdieK7kUhpBduv0MgPFnW}*IA*ztIknYE%8_Lb zX8ig0U&dGd@`u`M$z{eFI>#b=viPACE3X1+A35>)^d%%IId2r;zH&zwCNuSXz#X^$ zV75<s<5^wQ5JlUEd@j=G;%F?p#i5-gf~LJCCPL@XKPCfamZe6?VE?_Epe&%U*`lI7 z2R)rZi92ZTq{=M&Uv+^Sk`Gk~%QjJ9lG%`c>G6y_coA4~bP|Plu-bEUzyyqs(m{rW zGZW0uTnPg#<4z?rv$pmW*E+gSJeZ|m)q0^ThmVrn*cYU83X^BCxf!)P3JTuM2FSmg zBheBQ&86Zl@co<Pe{UorN{-Ag$p5VEsM+#{(oU>u<AIOWd%P50$H7O`CV5)+A7=k` zZ~wc!7ZZ9_0;cst{)c_`mO75_fheUgW-T!~<x8gi6rr<&T(^I&V5%*KYg^szzsCM+ za6a7r*GW+c{>KzZIsf0;Key=JX<wUMuL@$&UP1h+^{j20j9wdvPr`#DKe#(_>_0G? zvf3_N=L8f{=@iKTkJfj;|IHsfa>NH)-ZrKMW`>b+LxLVjI4OD8gSuC()y1J<RLP(l z_$2$6vQY#(zbe^Yp6a*1LDuNYe@6aKNhpBTU8lU5%!B2*{g3E5QQ#|#*bcNyYC@u# zNdUrW!LO3vF!$D!A%Cv9dbL8^6l!1ke|xl_UJ-ymGxmw0ntN9x{_$^o9$5wOKe7IP z`F(W)okGR-nJ^TiY@xI8|Hc33&i^q<g#Pz5{+}n4Fi1{vD<%xvgTxDnclGUz-TzOw z{|Z;&<q+X;Tu?OZpZ+XkZFc%JY#1|G+bz%Le~xP>YqR5(LwY#=KNM-2MEg$#!#zx+ zwjMuiI+^~2uoxeu`KdHIn%tatF1HFQJ}v%_H^u)k?LH(3IJbdi6Ux4N&jrAx2K5pO ziDnEv75wISW5L0j`w3t4{xH~ycHI9ompY)J`?xXcZm{nD$iI*m20{1zFz->2Kt>7u zhTS3y$2z5KWx7%p9v~h$A+OWG9<%lQy5IyX5sctt20j9h#VO3jM{bG%`9wdSc$1)J zDQ=2n*31M)c0hX6w>Q6M@)27tA)!*@>@2l~gA`n$uvQFSQ*g`yB1_5P!gZ@e+s+U; zS78-50ZSL($r1(+0xQV$D(}mm>wM;U8lcx?c$o;mV`P%&09?U59uZSF-)D9z&~HLn z*E3T|FsDyOCwEM6Iwq>FM(EniH0}CIOX#IoM5bOSBbm4gvozYKm74=68C!;!L|M|a zNqEiod1sDT_NXVk!Ggx6VLR0HyPs}z4rXLU2^hD_3k{#ZPSHb0=p>DbJuRJ+L;bBk z{_Z0BgL`PPl4d+6VvaoOMZ)0gw(m@3;eF&V^0_ydFNK?_N8q#sp(hHcOV0_O?W`zD zhY3aTAk2OnnfvMjW<Qi)8Z9IcNw|e2i3wY7=RAghaR`RW?*SOKOl<Xufc>LJu26!! zIE$HD8PNmA1d(%(-_JliwqR?ydC%H^Zv#kW>x<*!c_kT1^GMoElMZ2BN@I^O>b_?Y z`>8aPuaaTi)~@=_va3S;G(lfjgYJH^Hpc<&onX;1fqAxR>uy6c+X)QG@hf1tHU^&+ z|La%d9KSICi;ZJcwuc+pK`hUu^M@#Wpvo377nAN`_7DCo7tQ)(0fGJdl>g72{|hES zCz(&>kOVIAAh>_R|FYYCtP{}ZKY5P*GqC7wII!H(BF8lEBlzgfpqUO9J>~xtm+$<K z<^DZAX*_q7`wXKTgJ=C1!SgPx#*+zZ_8ASn+}XeR4x#o@uBCj=`RFYC)*pQ*KYVp| zaxUb8KvrEWRjU|))7TS#Yl2+VGC$Ao|Bv~plTtTZr(oE<q&<UpQ`ddE{Lex>kSZg4 zG&M`*1@Nj|`ubaU{7+n!UB=iE$C5U!nSJ<gtl!UQx<A`#wsht?>N7Be3jVDh)pe}D z|KYDZr1(3){dcQhx?gjs@A~d6B<}{Z`jLFhB-HRfXk+95kv)bxp67r05%dSTx#_DG zDDO7O=ly?+U)=Xt7<kONVd<x_Fabf#82}mnzrM$$WMXBJNH(?V8>_AE{0Hzs+{JZ? zxSYYd+N^jzsX5>Zp;wf>ZhN$Q$VGufz^BRoV?}S4{|OPv=iS7BgM^h@FBJn>XFk)! z*<<3F4-xbXM!<(ia}v_N3yV8bNIjs`x6e}0CxS;?!-y9GX`~OI7z|IY24J)$utB$A z2lcd!axY`+V_(mxfzR`v;<!$Vs_tdL{mM@y1{+{KcRl8_FvsMqd(P<g7)h+<kfz3W z)imglS(s@oc}iT)gN)hwpZoV4<O;K@vj;lDRtCjn3~M<kr2&HsknPOp#8Ai}@^sM( z`KZ6~D=(HhDL8c!h-Rl=_SU2To+`$V3hudYbjs-GDW6CtNc3QZ7|^WegV@#eB34IG zzsF0-^%MhPxeekB3q_cznR&pk+F<q4V%!r_<{m|w{i{t>lL4B!UiWPl%ulPHLo{|s zb!->WA@KJ2b%GCm9AUFC^7k{nef{<Ce*K+#??<m-opU;FXks}DCcPRbt@BF~z7My! zHY;}APVXpMd2C`~GYu7Y`wu$RC54Hd`}iU~@q)f)&aAZ_;Z%08pzT}4J+mjDjyd_5 z^#~t|u7VPU31uEpq2CwAuZafkdAx9jXjR8Bx|}~&fOhFHMLX{!o{FLEeGVL>;w&sU zLgU9LpM+tdID?-b<9G%ViLS@l_?){LnF*<#45nC@I0Pb~(_zj)5S7chH;pug@S`j# zqUi+Ge2?bNU9`8I(>5ObtoeTfxO@9A$zyK++4#S8ozI*{O`Q49pj^9-HL@!qKfp%& zKV~%zmiK0OYtcYSJdOVcp7p(Mtyu)dHYLd@wqp0!;(vU!&Xg&8^jAa~|7(cr7^YbN z+M#3C1}R3}+5h@GwpkHBBmcLN_I+2ur*;4CY?Q$FItw6oVUR?P|C#krkdd%|B|Xjl zZH0L+w|=N=d)Oa*>)kFI{ZG?mG(PWPIix|lR`y@pWe`9-wg0q|X#Zm#WofC{T_{$O z-of$;e`@|O<NvXL7U?*QM6tIqP0c4njIHC~|C%~t8xW@^y`Z`=^KN>N8DE{T6rq5= z{k3{}0AObTuo{3938m|OmFV@)Z+!V7CJ~f@gff*l!h4vqJ4~6oZuT|(61`%gzi5x~ z3-N!17Po1e(&qnH4*y@xb^jkXIR7UYy~sJM`=^4?!Zkp(jsJ`Mf3*v%A*f#Tv3CAH zl>rw2hq&@y0Q18ZO2*rltk397B{{@)8VzptB+EZN{?`rQ|GcyR_)PhKNdiv}8%*B2 z#1gACx$Ei%EF1}JnU=dIun`_yBWLR1Uecr>VqhvF@7kAp8Kh#oEbFD3H=m7Ay|bS% zxY}!nlo}V}?^#M#(SMnZ%G$GV%6W{VY1QdKAUO_ptJ(l_3@nT5i%vQcsG1o#$)K<A zC9w~qNY`qBQ%a|Pw8(SvTz&we=bj7MP?AA#Hu4xlE9;v**?^8WwPVyX<w&pq_{}Bm z2tUcx{F~MCqrUuh2N{9Kz?@N&1i*6t$rk4d_}An9wkH4-zuQ(2ESIt-V;uaho0`Gv zpkMVw$M{l4kzg3v2-;en2;x}Knv)7OsjC^8*nSwzZyAX+@bzRx?YBnO7E?!+*>*Yt z?#>75l(n&M(;*PZ9^L^UO9bBz@h9LDS=0aaKSJXx@c-RE{ay|Lvd?8ReG#*?J~tua zc~rr}sdzqzPV^2Ykv-_AJpjvdWQ0PiBpvpo#{Tsvt8!?D92rRW;Z`E5^=I}A9}?@> zUOUteC1OlYWpGu5+u31_b8MnGVE@QwH)7yXNX+a9qDMSBjZ2DU7di2quO6uMx)n+k z6E$2mO<$`?u|l(0<WemE4~{vKB&=yQ34Q@r(ok%tF&N)>kM5|j1<z|U#CYc11oS+g zaULefD9Ovln)*0koD8!-D8Tn83KeXvII@htlTOi8v3CpA{WAH#jQ@obpECcWHK|8f ztwqo=!49upI5VgC^E)O20{p+6+2n6dn=~`7S#9vYGb_jK*!lmu<fOh-c;dhUEG0OE zjd^$2Cj<7{k}EC5RXZPh$=>nYAU{(e>0nIK#Lqa8mi@f^&x9RuE_VB0CpW=IQJXr! zq-_@4e(8~$R<p<z=XptXtyU+Q{jZ_z6{&yt&G+KHAHIq^^WTQ5?DO1S*Op<3r$0|W z1OK0QHd1CTWB-(**sfG2CkdROV>qYn-#&ka|L*~WD@?gupxg?jkNRlq8~KZN-6~#N zzYBG+9jz8MK2&De^8Zj9Sm->GIUyrPQ(U=;i5z&bQF>wk4{?67N}%k(=*QnLJ^GRi zYlr`j+794e#dC6vv)5(*KT&HEEXsap1kazF|Fw!}+#ciqsWX&}#|Y8?KWP7e+rUaE z)_vade`8qKYAARD!~fy@e@>$H*b;Wn@P8J!_^H=44HKjpW=?-<{|<55X8)LGO^X{+ zi3TMKqV53T7v=vETx{jO8UBBKbKdfxAy0;}(U7&{H1TMM+yma4%AW<gcG4D{Z{b6R z*=P(A>;{4?I)sQCk>WA9sMr|EMLkPXDNafZvZ|+W$38MaxhM^X!h^gS)OVnlPpa*5 znBl=Ynq&;PYeervC+C%lNMo;UM(p5H;$n8Z5cG$#Lp00<T!%T-*D1(d*XX;=D0GYI zGz2n(DQT|*NWz!pR}!$;)*un`F-Hv8JIe<`;t&-bqSEqC1QLmH=>Iq?V8BE*kZ0Zf z{QO%-2hk8Ega7Nx6EYdFFFY?cgTa4aZ}(vcxO$-Y7mDnM0m@D{aF2Uc+g@|_i;fhE zSpRRC`m*9yyfzNUU6CUh=pXmoS3E(9z&cK-i)Yo{{?$+1w9?SZ(3aJlEGO{Ttjw)# z-lx0OF1cBy`p1@U;;i~4nO@p+jq!Oc{r}E7=sh)))IENJme5l<jM*f|$Kzr4F@sO% zz}#qVwbq*b5^iP0IHSO|U{{{8fB9%eL<Kn%KZpJIp?{izZHJ&StL2t*mWjpvGnw{j z!a1`JaE4<DO6`O}YF}V~*!%3;`eDB(qXO#GoKK*~cOA%LFO%-@TeI%%6c-7%v+q3E za1Kk;xYMaWY_C7$)=xr7qw|_%P~BM%xSVy`$VNz>evhfX@HV!4)BdFfI}xj$!R=8c z!VY*dc?TjDuz!xhrd{3E*xD+~bOoGy{ypIT9_vq+|M{*ZVllDLK9)&B&MWRa4zhh? zBo35p9p9IwGXau(QqIU-?Uwxu#38qoZ6{0|aZ`*ANmhb?Ur6#b&XfE4Wf--DVi zsaE=Pt=NG;7Le2?_psI{@#GiefAb;4{}FS>(Kzmqp4~^c#o8p_z-_JZdY`hV{pT~L z9P#qwTl~wfey0~z5W5%bZ0Zs=zC`A_|IVfSNk2X6bTQ+O|C_hH^{jCHIm6}!Sy?JD zcbxor`2W1)|GIB_4gWvsFv=tUo26F8&j0g<6T`Sb+heEkCn{dZ9^;F7;Er>g%7gme z?wMHHpWC?rtWSZzefGt-GY|@ZyX@EPU%Fh<@$S-yMC!&$!awMn@j*Vz|F!dfJmG(g zChQ+XS?O~#&-(wc|A=6WA?FrOsrn-RpC_2iqlIDrrELb*rd;-~vsc9bg%wtqG~gL0 z=_?n<{yiCN(wFYWu3YJ{6lLp|JhfW#%%{)))|b8d&yxT7(XppdnPz8-B-p80F2=oL zK|!Z?aU9MT3Nsd^X(cXoqJx<=pt7MS$$6R)1Y-)l6=GKgAF#CbN)9ig-9djY)k^JE zYm;C?Ng!8ut?EuryJ~BL2oZ)bLF*{<x~k9y7{rDPcYO*t_;e7~-n{b+8jfdjt&w@2 zwv7JD07{i<<U_2e%z{ffbD21wfXk=@`<SfoQePXv;qCSHe!h?JDhA9>Izrbs*elsU z#@wdbJu54&f{uX015k`TtII%sE%!h7&#Dm1mqsgY=FXfr7W72LS`7f5;h{5ay4`q^ z2i(_s!n|pKe5@VKVvI6%MHi9T-#(^ZhTF;-sgVpXUo{^zBi5g}Ls^aIIHHXrfhX?O zgk8ma7}L)l+mMc(Jx7!fLA5UxdVklgnF-k}t?hsN;Qk{+qlDA`Cx7*ReEXf37)#7n z1+oK@IEjXPZ%>e?AF4${>xTJQl?b>y-?3{B*mN4CcF(K#uyz?sB2fHU&>@7%*c>Vx z6qG$Cy<}PI2-5&wtvq4>cl}KhbTyV3;i4_Ey-%Ew&l!in{^MCEn@EvT7Ld-_2?ib{ zw(4dAmCM<#`AJwHZc5tNeA=&8;?wXu*>^3^PM%y|`_ho?^$khI>wwg^YV>!ghn#k{ zXL_%b;^$3HJ5O<9ZkzogPDDFG!tETZpTPLeVd?(60<RBIBlT*OsMA#7b;e`6{iEe? zg#S<WavJkX;s4?s6~2~jDmi|X95JT(uQE?Kv~q+~K<Hztaf@_KReLtYq5TjhxvId% zR9Y*}Ks(pt2^&9#|BvQN5%1=QWYJk)_dIO*Kgi}M@c%LP9~R*J)cjviyTkZD?)bmY z;%QF7_%?G@xn{-q6UpiOgEYzHEX!^EosJ=yOWjYzD&5~;ncw=}2l0pBdKVq6!TcOZ zmU-`rS9dGg#0(n7h@icr4fWg%e9cIZE2m5#IB%+<inMCv8`RIh|FI{pw318wkNdax zkd2im)j!+2O4vF#k|nzF#IHF4_3TqgQc~OY-A-Xhe+xP}3!G6;X7u6barNgM+Po41 z+}2q(F;r}t*?$F5@Xr2q@#^4N)+y?+F5eFj49>bc|3^IK|F$CWe;EJcF`qRG$xzbm zH6nC4A=-?ANo)^Z7agPmj_t96&2@fCArX>6$DvXkH~VjrhY#O_gQs$`-y}fs_UU$h z^6`8_e>pNU9nFR>tQL)+w;H9G$*+wtiey6CUF@_G{q*?X!}EFr{C|M|N1u*P&Uwjc zS%`$~7dTwCM+czWXGL{WpbH~shEC1Q2A=j1=~M&+F;dLEcQN(VHV&1si<VcQ5`g=_ zcW#@OB;(HW+uZ~__MYqj1-JJxiluU$`|WeCDwuEZUd9m5{Loi$gG4(z`xaOptED;s za_M1{AOnPP?D098a^`f@vh4to?&2J%70M=6$~WQ+DS`FedonQYpK)eCB8^f2>PQTF zPVLee52&i6aBQ6!W?@Emmhhi_cVTZB0Uyi%frb4144v>#Ah=m(v!mCNGVuA+GWCkE z$VIRK$I}R)$(O_j=dzL%uN-?ls~spC?s3K4j5_Puon>Tr#}a}_Cgn^3sjmbm<Tm&i zq$MYUPr?4t=VQw95np48S&g1p(JV_}esJn=ccilJG9%*RaJbdQ`R>bC*YllMAb%Wd zLwG7KC<%--*}n~pHTzSko3O9Y(*FuL#;cgM6mD;>b=U@JF>nDIL&Q#dvuoAMn#FO` zL!4|KgQu-lbS$#M&S%E@1q`p-|GcR1J>F*jUEC19+K%r|az;${zA&Du$ZR@HE0Sd1 z9Zr#^1h(+X$9BEnPW9#LsImV5L6WEVNTFq;;u)env+rG>9(`0}R{(-QeZL_Gy;ALp zFh?X~f;u%cYN%v$E4)v%{}_KhK)__u+GI~2*?&gDh2-UcP|}HzT7hrwN_ivve~hnU z#PaFzKkQU1%X$?+j{QeUPoM`H+nI@GlQ8ad*e*HEfq3O8SH|(v52R1h4!C2Y>f9fX zIpc&+HU7Mh2bkh8yd+-M;FYW>V<sIKB&#Ho-f2RI{quR|s{7pSpZr^_2RPbD{tW-$ zk5w4|*KQ+{`|y8@<7(1FYiW}ZgR_*ZqV=)F-+g0I2K<m&s#LNA%fu^w`|ID24?kXy zZ&s{KlTrS_S*j?iy@x52w$Cidj+SY_dZcV95G?SX+Qk>%4(Uv}(2K-X+h<j=+t@}h zt?k{>x=UoW{nwp6c0CG2QYo%!&uZr(H&b<}pRm@G%uJ-N@i)pue4-sEBKz~y-|R!2 z5m;B?;6Od<8NOey47k~2=u{8G+HIuCyA0Tlx*v^-rbHJ;HzSp>B96C&W+lww17c%2 z{BI+^ntUn|5K02{_Y9qP%>Oa|f6@L+`q7C=a$CjrxKl3ESGqrU%*0BPtvmJf4U?H{ z58<V&L2{q+lGMz<l2__3f^$IYL?Nk3J(~bq04cfkJwoXCH2J@KHTM5m@&D-xbbWWb z76JIoV?_8qOGt8{P7KYguL&W$TTcbR*_rMnXICbxkq{H()yC@uh}N*&jL?KW{jW06 zsLdb+u2qJP%j%L>Q1}_7({6v5W3McJDl&~QAe>AOm}qspfCxHBsDpOO4o(2F|5XYA zCzB?Zs(SCMmguzoy7O_}>q~PezkVNda*pd!XSlwDqZaFW#;1>9*M)m&I2_y4v>FDM zWkz0h=F9ZA)Smv1Q;7p~DQiwO+p}}1d%g8T%%&x`iV6D5Rsx&>a%h{wM|{q1Kl1P7 zGPBUk{aQYDYw%Fu&$fjVQ%y=*lIe1M!C2;yTf%`FnWMjO(M3I?_%e`4(>;AXIyV(c zro2}6Za=ht&6-s02{2ZlfLBzWy4U)r?vGPJ9XQM(@x!c;obh{x4AKn$4*w2s&sFAQ z@xS-A@5j3zzKX((z8<lYF@zwpA;|VWWrV;kXm1}J%dlXYdP^)bj0Dl(5kKsS9}d(# z=(37PP6xkCw|glcl5rVw=;thghNcd(#61{~E(G?^0PDKqnBhs^FY8#l*?)WWGpoq) zF?n{s;qT0T>9PN0<uwFrv&*p?5tn+(6{3@&rOjo|Vhd49uCA;DiMSd8XC)lFO`bEE znVW4On4nWr<0}t7!MUotwt{!ISwHX|hoH*zyh2<Eg8?NZOJW1zobi7S1b^>WaLhPi z1ULH^9(<PkUx~3L2K*BFKPQLS>TKiTNqNlp!|rD^l^CdA#=2J0S<+1VR^i}f?g^J9 z|DQYmUq8JnPhn{9$rWB8km%&&@IRhXv7MI@CZEayXz~pY?mOhI9_CCQw!A(i3knn4 zctRMu;jr*jB@`a-#r@JL)l31!JS~9USJ=U%EZOb9b>-F?$Fl+=!pGL_Yxz`=NB_M7 z9m6JBPfwC0+VSDXQNR1A@AOP=2cXz98*<Yk=7S#2yC6MA7wxb35gL|v*VEq_eS^$| z6A3;{>T(PY(w4kjCahxDdtZ4FdOxeC4A07BnM{P~uJvhR`E1GGtX4vLfrt_5DGEPk z?0WTBZ*6e-#`JMbKE$lzL<b8)G_(D<_lE5N6@Cz4gl`eJnsAGlEud&uKN*TOz`0EZ zxmvjb37Y+<k`HVnVLl=z*jqq6dN>nlOk8lRfq!YMWdW^71k^~1TkL`u=17T^xX=0j z(YO#ZA%lcIq`4A)Da@+7Lt%G*YuypY`Z+um6Tlv}Z82hEA4&|@Tk_ZatJW891IFz> zK4ubgl9`n=8M|c;l!}N?oB!kA6aJSTp0fp@SW%7A5U}qQX(W!4<Xc84s+u92j$njq z&xLd=6ZOEy@8IABX)>@t|IP7`f^hJ<3S2uFDUdZnj`WNg`aJ_h=Y_?=>1R;IS$oz; z*{r`BPwCQnd)9;~Yg2Nx^L=hvGay$xAv?puF)7NCpYMBiw!{HI`DC&@0T?yQZR#|e zWhS&J*LEJ<@nXFxWTGrUp6SMu6+Fo|T8Z0<8S-c~Iu#SNGk>_TjP>gTch~I!NVK^A z_6M)E(cBGm9u3pvgh$AfpBh^&6U#2g+<W7;1mi3QiI&BmwhPBL_ZYIT``IX{Kwn$C zevbT*qB3B#L0Pq|n63%BRCf|E%ZfG8qRw)VnL%XRsLj)_cxr=HZvR#oL8~v?_BSSH z=xC+@T=k(ra|`snnN{*=<IlhUGQRQ`KdOF&<@K=<wHoH+O-Hw^yU5J17WIBxEVFAJ z)-qHU*`$C2DvS1F>|*1|CA_q&olyAMwjv<lnbNdX7*{Ib(qqoZC?+8^L`Z~0AD+}k zF!r^r0-zoc0b!%QFSf<i%L5lw1ZO#!V1S(J9R20K>g!%?Te^29!UB{hSl8w3_P>UU zj>A!-4E#BYk21<3k{%;FAI0zCEY}k<Uh*`2JLLFggfE9CuPHlEelVVCA!d#>At{}7 zG8&IYm!JYg$EeisNopl$md$iKXc&jz9q2c~|6}AaBmF7!e=z79-k$EA*4M&v)9%pd zk!<knW%S%icj?l3+l*6AVN$;phzI#W*Ox6}KaJNRmA>|5Vr9w~|Az%>=iU8b=-YTZ z7<@2b82+zGew3ZcUyw1a8kiIe-p3^%PTui<qCVTJoWe(r`U(E8;eWJq%*c|ZtFNrn zaVTT=If6KZUnEqe1GGN|a@$B^2rB>JoA1V-fA8gEekMy)-9;8JOCnzhox{r;%MSUV z8~?Ljd!4aMBbX3N@$JaOekv@W-?-EGzn<a$z7i8#JO5{F&Q`i+4R+P|EvR(TP?DAS zh^;+0_=3-Sit51)<K&!drQw!*w-ZmEs0r%lpy&I?1J*oBuzkF&+L!*X>mL94n;*W8 zP9iqXGD%gN|8V_~!w=5ijJKqqQQ*@<v=$~JpjV$LJ<U&Y(AQ^522iSb+wRws$5yN5 zM>k&_KgR!;ebqEM22WR<k`<fI_66nf3IZEoXckum2*z#6d*IQiy$XP04DrNp2JyZu zA&u^nBa;N1cZ)^A*Gd*7zX3)M{%t?MWd4sg%Kv@e5j#;d-Y;W-ixL18{3C+A3x1JF zk#jHs+3*zaV=%@ZRm?pSEVD=F2%>|7wcI;5AfY4I!A%X?%q(qafY{fm%QI3A*fQ7( z6Y?lxE5hgw2NfO2i2Fbac${o=dXk#DfV&w>8d&>a*C{87%xPElIlpVa?zs$|OCK={ z(+HqI-Ga-Iy3}OIQ91iBr4pTuFVY<>jUPd=jH?J_s_GQbbw)JDzhTx6X`Da+v*}fE z<`7xe$9q;b7p-Y9&(Nd&@O$g`3jS}<zw2Y{OI@nEUSpXvmyQ((qj*CMxbU^>HNmG$ zYL|e{jerI2fpE%Ay#GQ_so`oc&53su?eVcWNybD1EAL|?_7y={juNxILdi}dxi(Pa zCeuHC2OJL~j{PGMkSx>cVm5@!b~(Vs<Umb40d!sMd7R0*&MR1Z;{L_I`EI=C$@AVy zwrjm2IgaJ1&Lu8e?%H3*Kqn8DQ!^r_&#J5LJv5cWZ3GgGUtmtTeYS!9VxEfH?Z2%A z_J8#Agz+qC?Y<?Sj(tD1|Bc`RIv^U`{J~%{WA%c1yT<5<Y2UtNKk#@>Y4DJ;t|?v{ zys$ZH%1?vTZbuyx)LWn$fdp<U#7wM|f{j&=6=%TEw~F*;LJ+{yw!^|;rPi$sw+*Nl z95x!!H6_($8UgU&M2uo+wt<(hg5vNcuS?Am&aAI<Cmql{8RQ4fW2jN5&zS$IKU6Rn zKLPPm=YQG1JkfT*m`D)u)BewKhG}@kad^a*gKc-))hdArq{FOA*V?RD56`LZYGf`K zu|IeIuRH!HhO{wIH!yM0`T{=A-qsFB)%jmcx;|g;AApfk6gtzq4Bt)<Ysv)wC-{FG z2kL3Um%`1mS$hl^uF{zv>$ftW$~RlC<rFsGSv?SX4hK+vIN?1SC~vo@|KX3n8y|dh z&b{4EQ<=)K<i@FK8*9%Uj$-&<T0zXqq3|-9Y{`0o1hlo7T$r&4=l_#eeggmJ@PCYN zGyJd53Lr!KH+}H{w+Fk^GLd6G2t0wa;c}8_6VMvQ|K0!9%C+}DH`(-)0<xjOCUcV8 zIhTJo%lsY6w-us1G}_<)@WWfwz5PE<2rT<AAJK?#{!i+ZiCbE5@BiOvYuxic?H_=! z>;v5Gd4+Ce&lBN$u3%aA+{cCe82|r{<B5^7`Fx)L*+TKZJ(UFIkct8asg`<*Nt!aO zK06VWn`_mpJbKlDVGb>?lVsomK#{($99^928z*YU@t4W}Vf<fjj{ni#_R2DH9IdJ? zOE6uS{xIqopT3P?YfNs#Ry*Kk%m9uPQL$%EynZ`jdUql*aH<=w9m$`AnR496xnQ7( zmMsA_#xdv$CW=GXP$~4us4!>uNeIb7?7mkxV`?D#^z0Xpe)8uZB0l%{_w9!!o;9G! z6UlLJa(P~k3=S+i0x?b!qDI~!f?2Q6I(NfzL%|^)t=m@DQuH~ftUsHBvv|J<ks_V) zjgbqc$oATjKPWxNIv)n0LOR2O4s&qeFadPG@x6}+&?9CKaM&LI>LdV&p_uKvg^>nt ziU9`00ltP#MKMU_g+|^CYsFh<?ewAVYk+SV=^FYDZmFlur42wwIFn49XogHjpMs7d z&s1gtz>x8Te0E)dmB+3<%`GjqyYH&Kr7oC#OxA}A%Ir#6SNu&nCh5MsVQd$_dU^f+ z?$^E_?|kr4u4S&;r|SKp<7dw0(me~xcaW1Eq-HP~a6i62xh%KKF6>#gy}p|~&C5+< zLvGSeu=@3#*?(YWTkV>q#Miagvi`sG`HcVc*B;jIKm1i9AH_ubhtz9B2mEy#S3bWb zgyn|hiBOU<RC-?r8h#M4v|JnxvUR#!VoR8*j^=<QZHFAipF?XPN!)|wWzNz-uSj)> z7D+L4i_ImMo;3(D({Jr7N~!rKY5$IOEC(|O=7N{FIRp8nBvOeR`{Q<eG_@oELQs4+ zjBPjUI~{KNv04A@*I&ec_8&Yv{=SvJ_W6uA#{V_A7tj0@#`#m@e<Glg7&RJx>HgaD zN^Bj5S4fSe?2JGljT$^ckj2l{w^717ouG6mqu@b%>$~HyM8`e<pF98Sk$SSrJLtZ* z|J*Qcc&_We^x_}=+C%=MzxRkGzj8&Z&+-3s3p$tL{}>xtWIs*l57O*kQZW1P!5?S8 z#%IJ(FG07R&(?*_>#(l(|8`n=vY@kzaal>^kw5s+$MO5$dUpZ)0XtRhhhPQ{g59Rj zg}>*{{}_jjD=D*{u!*sM+N69Vx*U>JhGQ1SGI(z^W(YFCqtEg`+URmSOfX0iB_d2Q zVf!Fs|C!UD>t7r;_B{W`8iAtVXH`P!KtC?)=b?NjlY7x;I7Q1X`GgC6U+kV%aaPa# zUt+wSwkn|DTJf6VmeGp$KXNr68(j9^9jaM@$3)YCe)4-0AW9PdyOqM1%`pCdV*mWP z<=9|jT|wpqg7&>?)FVI1|HlNw(x$@#%4CT-vB8hw{|W`2&Lb}#@3>-{&pjk#oXA;` zBiDf2q-p;En+$R+ID;xO{UDa4!Rts1fq&axxwUl4A~w!5TlzHlA2KnEePjI3%MS%l zAsy&KGX<s~Sq(THbH0`;(WpV5v1KJn!Ua;4fCmG?pj?6pS^E$G2Vr`rUh@-z<~WR! zOirRfL8{pX+aFT<z<kMcvt@H`>Q%vZl^hDYKz&SJi?&S5)aJvS3H40w4`(@eQ^~3e zGhB4E3@i?|=rHgKc>}3O9bI)@OB^jQJFn-2uF88UUxoJtGib$bVQF7cjqKX0;N#TC z5sUB{hcNkSbZ6|PL7_I-fhRTjqw9+IKe|m8w^?K@2fG4#e=-2#`$ZzSb_oYd!IFS+ zmX`kW-X2*68xl(3p#gGlPa~phY<S3Bwg<BrrJTaN^OtI7Fb_nmnYPE=G=_UR<!K}^ zPMHJen{q%f`;RF@jff2-<xy}1W0D_k{}>>W_4@oAy=f_(%%Z|ZQ5%FzGwVPtNFc+f zmq;%Cdhg|{_}xEy2a8fb7?}uM5ctFX2NBsb20=oS3Zy%DT`9A#rS*Nh^0gP$ETe;U zNo%v`ZuoBh&6^~a(v*p09mqD@xd0Ix0q3<u@$E;Z{nFbJUw%8|^Dh{{=d>wkb0i^R zMLWP));RicJwc!X`#sOR+ai!d-Z}Qqrr&y5HSi8jU~P`UBsZ!5=Z5TQj@}bI)v~yw zw|b0#g@H;AFsXU&jtwz%NSn!9>tB+?`p!ZdXd27}W{k6dG3`$+n?E)}M@o+G*gum5 zVf?fwKjeVrrkmiY&B>g<0FCQB>iqKOGJfUp_sefzv>?TOWBgw=L3#ZW`QLGt7^Z$y zK|gYB%_}Alp|?wSt7n4G)+zQVe2*)2QlqkOH45?d^pIoxh$R<pAekr}uklYd{$~Q7 z<#Bm(b9z8dWB-_i;>_Q=?S!3B4^KIiZ#_EeUwo_Li!UO+@K&GNaz4lZ5^a`IW|VDf zygm>wSwJ+ZI=HNn?PjLrTBc{yooKA(iYz8IIyX@K9vRix0-G)b?Eua&rbzHkuLk_r z-+VXU`QTMA*+wU(MbbU!GJJ-X3Lx*t^MB=2`%iL06X#%8QAR828<WhWOwBF?nj|e! z2}N14@ju#-b~}S^vz_oDoe96jwh|ysJ128T8BD+*>J#}t6v*EDfI*Rr@Q0TrE8^f4 z=-<v+1#s5P)Vd#Nzj?cLQh*J(R+4`6aWaX{%CfPPPY0o8_-aDIqz2(`dt0}7?4NC3 z+BXvu1Yr7@ObX0!Kjr_v<=p)wY9_e)dH=t^3HkpV-?M!+K6do{3H;B_U;mxkp~CUv z!1=rO6apklg-6_CWz3b5PRS#}Y9_*3^9CF<c@*SaCf&Lg>5>9&RPxK`|JeCI-w^+& z{L7wP6maQ}W!xoTYfu`Vc;i*^VM;n~G1Vk|NtzpSS~37*K8{DJg#-%OUI{8N!!mdi zaMG=vvzjSlmlHy4;mQz3S^$SGDzS#;*Tv<JTvAe_oT28Sq6!5$XRiWKs^_E<P4i~C zU^O`Vff{Fd1TGNb0YM5d>D`$egQm(yXR&$uC+REzROE1|AOaVYDttUh>*sWV$_aGL ziu>77jLV6()8ING63DKEyEC29je;GlCI4tf{MmOuz7F);miyZy|5`$i9}w_$CcxwE zug@(>kX%J@>#?C2DTvT-jZ=#+hv?7IyYnpBg-EjS6o4+e#kWL1wg>x?x%Y&_Sic^h zXVjzjjuKQW4A5j78FyweIU@Tr@}~xwtw395jA{P~J2~wd*}B<(c{^%m2GoU+K3URa zdehty@jra!yKC8I9CkB+xb5iDbvV4qKC5dYFcq62`yZ!~y|0Lz$Ko_LwqBogcSxl} ztjm^u;JP%L0H^`_;j+{+JA?*=E%^$Ln>xO<^%OpCB1L|rUl)TkS24(^&nc-s^eke- z<6;j;inWPJ^(t9}0B2uf7=aUrCnzlJ=k$Pw_74JDS1tTSP-fYjbsd8Ol*a_?=yY%! zz`8fZalmqbiPDiZ_78p7`g$MKW{mxZlDAP>j+-bm=UN6T;$*k~9+1YLirxMLBH8r0 z<q#Vl;L%oNjhj}lpEtq(F>;GvGXIA;Sc7@dx(2=pc?0CpP6V*M$?XA0Z?lvAAGH75 zo&Co)2F1u0=Nv43z3hKE*<e33ybi6(i%;hNC3t*ZSpFH7n;x8%tXY2b89h&~flRH} z7?^xgrm$IO^z4Y!^;FD60x;Pe-{$7)E%+Zed0JVr|CGJ;n3~>W;3I;8EY2Y4&y_tu z%V7VF-AQ=_<Gc0D_4wd%LgBam=x?*PEU)4xQlK2A<K!_YoH8Tb-i`mcauofK?zkpl z%DGpy(Y6DRP3|$j=YQJZ&&B_!TQbW%X_MBLM2B^lq-=}w4YR>eo#V*?SMKdU#4msh z3At4XmU?pOA?lHj$KOdPaMoYf_-atQh5q0w`>(I_q}m5){1P^_J|`06JtharJ+q%1 zebhL_)Bm0Sk8${9moBjtH8aDJToDPI%9%;owzH(LP<3vW{fz%VX$~LfBu^f`uYVgK zP$m5TcxwNwznDm1!tY@7PmOD6nYzZrC@jSx=E`FSK~1H=%HeR(F!t-4KMU8TB7WKY z5C31u<~PLu>f3QdClFC_*r?KF#)gp&Xk^T$08KN4#O;Y0Jn&Bwent5hb*7W;BY>0* zPzII0#W=#N801~$w?5Y%%nAW;UnJf9G?}ofZA76^SPnz(BQG)fso9XGTP>S&LJ0va zT?Awb!kHj&9G+gfJQ=ip$XK?Mg4gT5gIO+Hwo@@}tC<;F*Hf3TD{yGma#{!-N%2pc zIrDf5w1@lHc7PF#)Arv2E@^bFxU+R&4jszcPDc(T+D;e>0ydg`vj0eD_V_KNTEMrm zwr2^^l?cGHFFXZ69C6xpG?ICC>{br(C(q3Wq%j7#g7T0)lmxhb*u;~Gq)>k>7%IyY zhR9k#l%v0>7X`#dBmQ%JcY`%)g+e<OVHHt#k<c(Qr%aGh<i5Tpi<w=gc@j%ZHe>(y zZR-w}eLx*sm3HzV<f!AU!~E)B{jk3M-5<fELZW2*baHRPp7oQ{U$q$^7??RifZPP1 zvi}5u13WNja3q6=dyZ5YKxNEI2h8k6kgpal@bIkuImjb?1~BgHWxuL(>>pZqre(B4 zILZ;B<<J--C9~83F1=jO9>3+--JSiH#yKi{siN6G_T{G?x<`kh`rtk-Y32-A0CPe! zmazY1-6ic`FV4o0+{d+*=Q1`f#jQ?4E{Ei>J-cpYC25WoYu2M3CbrE4SiBz!y5Ad} z!0L6i{dF=SUC^_#9HZHP@qJL&S_w?O@=5Y9N=(Owkps?YHIg5t3qMQ#S6l6iUpD`% zjRueDLGcOcUG<zu1R&sVyT#~X2CeBW<VZG2+}nT2wvv8Bz`ioZ{vU?_*^y)1(b7Sr z8~^9TYpf|JAP>?`?lXcNP|s?m8KZF|lW5Zi<{5oK`$0wG|HjCeD3SbsfMe|Be&HH? zWj!r`TQ}&lq-<$q7`eOP*PGrOoiX(f5)Ix2CLwB82Y4}79`QBfTi<&b|K?loWRepq zLaQmJH*SDMx!_%fA<Ob7$h{z&02o)M0=}p+FK77wq<*5omyX;$by0`x)!pauQ}Dl* z{xcyDyTT#-;^`^{A{sMw9)HuM$_hH{wER`h_2v1TqVCft>g|R)k14nfpjEJXG9~Q5 z=j$=gYehgrpBKOb0OjY^_=bEEz<Mgw<KJsLX$|97o37lnoB*CO=|^Ywc7$_;Rpz@& zPeZX}a3sb5qn1aGo!BH>=a%{kT~9$o?EZg5{CNMrrVou@_-yfV*K2-v7wG;!af6TO z1zT{2jyf&=U&@DrPB;D!-IJEMLuufDiU62xZRNdvK@LasV4F@eL9+Rpql+z_8q|sS zH26OheWI(MG5_=W%W#i%dr=P~JqAajjA)hRfi3<XG55qWylDYRn*-2MgSXDen&eK4 z@L)r?ho=lz*KN52*^0rqhxi*vZ$IvUFxX{zg@!TcN4?6tq{{=uN~cuJeONh1*|>+Y zQfwKVQ4aLrpT?kPwd7<3W)SX|A1Y@x&H%pU?9Vbn9w4M=W1Hi?;`nRGu{^kCv*c22 z5sVy%kw_1ZMABO+jdPOWkj+qYI~G0cY4Z1(&FjyJ=f&p$7_4AM6q(+~YcJ5gMbGT{ zGhaQZ$02|*C&iK=rd9-!!{I247E40H=L)Z=uPw1KbyGQTBwJIn?q|KBXTdJFT(}1@ zV)VO#APqJ8wgVVQ0#<!!$3tp;M^mwTN&z8B32SEFm1vsGDi}Yvnf@I6Re4(%H)q|y zEXji%PbEmgK9;}n-iNpPy|2BqbWskYUA@hk1+5j1CMyjbin9Lt*#S{8kHCWcM+As& zEoh{*t^U|DHe06abp?kt((Sec^fBEv`WZshDcMLs1Qa6Y7l^bW-l<5P4mZo#GtJb` ziy_u1oe(zn=vk?nO{EM2gmtvc;+*YWX-<mn(X#(s_y%p*?V+mepWt5i_Fo9jq95}o zxK@89?ZQ(Ml1r`K!QHa$Lh8|x0dU|z#qgoCjvV^!hwf4#l@)MUtqjU#REYLpz{pI^ z_R+{_|J`QIQg-9f$Mt!Yri5lmc7w*7<o}{d{gU}#0i;sdbH>K?%}fy4c9|p(MkkPX zKpnQ2)OpTuS)cmehq3?klkH1^zl3t}EuCY3E;)H7LNll0e|MTCzeBb^2w?UA9;Y@> z;ysx+kUUGn#G!Hfr{x{H{b&3b{*OEUhy5op+4#StII9tlK1<A4S%ovm;3v+;-hHhx z;nP4o8PM(U6<ahNZ5w-h^9SE}H$HfEhFBh0&Ef^xDQ$iV0_;`VwuIWKK}f!TB%4}+ zy+Z#Yj0D~d9uvjd_=o@Fr{I5`QACV1{+C9}URX^i%hAE4=@HeBRoJ!$LONd)Y={eG zxkFsnxH%)N>0m`;s<bcn<^I=~Jm}f?{GakWIw5jCW&{50`>!<7KKx&3LdE_xfO#SZ zbh_M5JYS`Rs*L-d|3_BJd~lh6JY7LJgYN$S#1~P+|IhmWhw*q~jRRIcRRIyW4}Bsc z@W1*0ZL3vC=8-#c!^V+VKdI@S|Ic*ybCQ=D?J+P3G&N~<uMClfEb_>i$QVBRW%Iv* z)*Iu03T|z13m0ha#vY9M!W!ixm!S+wa+IvoMW#`*4EjhJKv`=_2MJZ*W4gNu2qBYa zEgK^0jvZ_k!9co&n!zPEn#~&d#}>pF7Gh<(Bq$Gz=D6tuoYh90cG-f!0VTvryN4(B z7w)V<2b`FVTP|b7IRSin$7<HuMu!io>CGG$v|C-m)`Q|O^&^pTk{TV9b`$!PZ)O!` z84y_JJW6MU^INEPeRjA*7o8c!a$zb8XJ;|M(tapG12vQz<V@n5sz`B1z5kfB{pNQ+ zo~N1^W;qeuD(pOfs9TQ#R|I4kDV!lpNfT<wr)udmF+&2oBy=scdsK?T<8{y<g1yS$ zX5VHXk^Y8~5u?jVA}z<yFpojk!n{(pyETuPqEA*&C)3&6;aN_RovbMpX46(DuZRBe zo*F%~wl^D8!oj?*`NKbZC*S+vRZd%%I=c~PSVq}(MpU`vRPe`mJ+HB`BC^t6D#x(~ zp4IG!8~exWQ{3+P6_gi&1C@aI{l}bvRJmtRnYxXcvI+I&+U}q+Q-2cn22Cy8ZxD#F zHU}X#vfK1xBTmi`rA9h)f(_^$k84g6Sn-+W8ofT#LHd!2r+PNIaW<iR_KuFk!!d!B zA2ftsjZ&ezg&}2TpULRX$cG2b?a0JN1=|k;P!etV9CwUHw;^xb15RU~D+DuJ0<z=@ zCzLfo7vyZpS-!|zThr@p%f!DA{9m5|{}aRVzT5zrUvgM$&hcc6)z%YS2<CwVUGJF` zH(TqGuOGwz_x4}%fY|@FL_BSof2myKfAk}{oEBl|;{U^_YReqg*uUf;`fz!Le2;*g zstoF`u%zvOPxJf){zsd(;5%UdR9y5M)--$;HdClR1<q|ij&N2;QCKFBsclu&vyiC- z0h6t0`{TP09%I3O{m3<4mDw)JEKdPQu1|hE=#G)I^Zy`+&yp8lIQ98%|7rYd1JGrk zKZgIOeDQyVCJ$K5U3=;2By-Odn>e~%N)W^T!yB0M5|G7-liIp52?1KVn$4INhx>(n zRyO&w6k;Ct*Pq`!(N?^6CWZd&`ya>44_{~U5xC*tKlV~k$V3NPm-}(M0z#|?IQMbJ z82mW?_hf+1*~*iQxISOOyv-eeVt^x1XaV@M{=f4$FmGLrz5J_;)1GcZdRh0G|9>~x znvR3jbuSD!=P65UPe?7A|LHLHE{{gZUQGb|Clqx9VR>2dC7_!n2+X)QCXwQ2!T+P> z?ZvzS{+|lYK4R5ck7kjJt$!d}5e(rJ*$ZX|jZKR}NDqP<D$KB@+9aG|DME{hP`<a_ zGh-v?fo12cPwSgmiIgBm+*nT4{)y0LVhAeHWWQR<cR`#KDb3+p;z*q6DKbnU;wUv! zjSL(roW$EO9X8QM$4d_iYnC99$aULE&>5vmccRe${&O;0hL#_;vMsDn*Uy=Ily`!k z-J{GkeY7-I<3(^Of;LeVV|}${9uLGM4&~JSyS}~-E<5upfBiw{V>`>M<y`<2bWZ_V zoywL;Er(!Qj|n;f0-{<O7#dN<HcC#Uvh$*?#@I=-X3>ZNTzeUPy*r?kQNWAru5@>^ zo+6bhE;gTzyc1Nak6AI<RE|9CKW28oijR^5VX&~<vwxoK6Keq^I)o<6=w2_{`RjK- zjDPnx?+cwqpDRZ{cz>7BPn(=U>l>T2-JZ*q@K(*zHBT%|=ML1=%l8R%a!+-l?>Nzf zr_SoGQO9<LN?D7Iaq|o9T_cyfb{PR=%*`I~&1W0FWe&D<FofCka}!H&^@B=t`^O$y z$&iPWxfUK+Ci$%WciqcY(`GuWmoyS@mRu8&BI|||zh-K4Y$0l91sK;sQ<5}KS;*j! zG@yYLUIGE73$47P4?7XM?%B^VJgv}`IPSBoNs_Sw2pHf-;wA0BD@!Cuz`&a`FqiF1 zhTjnXv+d_EpZ_U?lZt@%V`m3=335J>2xv5!aS2{<XBQFA@P7{f$36eEX-9Ka>n`{3 z8>dm#FgIuR6&_@+0?*-pW*f`Q-|atqi^8^F$Rr$NyEDLdXyRjWznbl5<$u_JA;xDP zscR>soZ~-@KQ~*vQgyBw{B%2<p=S?~q%b@#K2-r%_I7}bKmLm!#9zMqa!Ye?c9Jt} zhA&paem%5Dfh;etv{}}OihE`a!(3h4?{#UKD`SC$H-_I+9(?TyoZM`qRQ8e>k~lRf zy=fnVscM<zG(5MMW0WYx?P_79uCJ~0${Nx|45TZIK3w!6&P`p~&MCO@*K4c539lQ$ z*76Y_zxe83ekcv0K9Km5J>4xP8sGZd30^q-AHi)wW%B*$`Txw}=;54*UHU;jbDdBr zczf^v*S^ZKn{g^0+1xfhX17ciRmH5pLq*t1Hc0a<|DX5v4?T*QRL1al+J1)lNmJ<@ zT+=s2)QuUML=C@e{?EA{oqi+yZ*t)(Ve5b{ot!Gk91Dh8V=OB<gtxK6-k69n>6pO9 z2_=~MQ|2=!f6_veyMVxy-yuxY0M-b<R9*-uA;igl5qfv;1li^AEO|}_>Kv0Q>qAJ9 za+c;S^2c;E+zB`Zz^{&YWn*LGlRf8b7Bh{lE6HKg&n%s(p!<;rw;yD$AP8oQ*O>O8 zc(CAx5Xh=;K|f~fkTe<Mv;fK+lO^ip{>iW*WRHhzQ$cSA>B14N%SavI($_vf);$|> zHT3ts^`m%wyOIGo01#c{(*G8DtHlZAFW}<3ZXt5(8Dob+U!G>uMJ81vw%Ue9I2+s} zwLZC)Oky(*wa8`&1P}0`7_gbMGC?R+dEJ*pcQ~U8d`8f<={tagShHam9P4uXSHIfX zWLX@NW6Bs4V_Ak)5LhEMbPCv`*8r(1d2F=~>b}VPU;Vr9ttUJL<e-E{VX0({Rh>2z zPX-3n$s^-P^ki)`sUMnwo+W+I*P%F#q!UJR?hi=`Ps(xzfHGMJ?-k21`-jdebDXkY zj)m->ytWdb+IQaviYCuO5CgXJU(%p1D=~^|ukm4Ld+-rtJfKu?m7D$hddxZ+dl>c< zA1lPMRTn1d<w;cjv<_kqEC5Rfc%52q99*$58GMYhW>k}jk}|A<=T*S3B3$1EvSX7R zKfMgdG}9xV5Sw2movN51Jdh2$AE$?jw48e7$CS5==$zszI8ApLqA^LS$Q$B+yC$PP z6aJsbh%bT~vuv=q!Pxk}FXj0X*<7aAi8!5);iJ9Y^M5;dJO7h%YYUXdp>+O7uovUR zGNC!@2i0YUJG-oFRZJ^WCplV!);PcyZH+QLhR_f2I{14eo4`j%FOC1HOU995{IBe{ z@qZfsW9cv=9jLmQ9^}e{+FJHFS70RwiK%R7<B(=QOUt~eK%IDLHZCqdj^<EP>&iYf z>x_T(C*R3euTHg0P6~t7NoSX1b3nhba%I(X#LlPJT{t2HC@jywaHm*W7_FDSs~R;s zcm9_T9K-*q=Apu=w4Dj+W?rlO#(w+Sq&i#3Rf^C2TKXL9z$iR!ix6h|Yra<>3Gy1h zHAs?!uj&l>*w_ZD!=Wqg&jHYR0de_>S^w*|KfE4hgL;hr1N@&U4$tvnp=hW#?)YDT zb;tkr_TNl$$N#w2`9DZdkW)DVVvG)oYl*4iS^vKr)bX$Csm=aR`2R-^UIPPVsV4$j zwCOy0^%9?4@niX4N^L<cnpM;fU?K0HD-iq^M}M=zv7vke{Lg@VDt<HkkI3TCEdV_4 zpn#%;oI$5fqTh!no($5~uGmbvBIJj~WHYDn%}D3~gia$*usy-YDwCkvhSCYyn^||w zi@GO3_EH54m=vVpKFKWifs|HS&^bYLU=Ke*AyDGrJ(t%so#2E`Eo)n+jjFXx>1dg@ z9<(9oIps&hJZV7RxAT7ve3jfHyxgH}I?urGq@njI#vC4SP=(JLMc9B{&FWwTu-JL4 z*bgQlQz&>K+G9`DY#~Sy@Spt6$MOD$rGZ{EigyPC_7Ko6RyT6UL%ErhDU_?rkW$@& zM9pi9U?DoZ*C=PyX2gIU$#_g27Cb{?B;D4K+jlFDJnTXdV$PHeS=;Xq>oXHWDH|O7 zck}1#W6<L15Bk{|Z)pSoILnu!CH;im=~=Ijc4V)|Gu3-9KVlXaM0OQ?iX)3#En`;q zU@R->d7uj?_PVb}qiL`UkALbhI7Jw;F^o9^1j%}`@04MM+kft4bT7!(0R<iMos5W2 zlX44iFiqaa3H#R=IL_;T?6GM@sB*NfsB?6Y^6F0Gu|a^LUfOSUNOyCRqfxTs9#t&8 zjlk^JoH*Q9<Wu_>5t#kUp(r;KQ^aDUyIvm%vP^9%9WjY%^nKTA&fpQggHA9V2rG|B zxEFw7;|$7_bJvV~VorJg#>I+$-F2o16Z<$>Vyj`{)!h4;@P8{(d&0+O%m0yM{{gT5 znhCr>^Rj<&XA~1{>(aFb@9YEqz!1CrON1lCq}(c=@_*d%|2&qlZJvh=(*6$y8l41F zcR-#TRq@q2{E{>M-2>f(Aq@rpw6z@G2aI%>pP_#{NI{;iwmuL4FNG^D!Kc)>$gdAD zn<T>on5$kst12>l!h@@+x*5j*BfPmbVi=2DQN&%=cgb}<WhD%RYjd7a$`3xc#aI5~ zy=$Aoni}n2<Iq+-dSjGL-0AB0Y!-k_|6<HJXX0Fgm-Fj<&tVL_Ps0Qr_($FEvC#N} ziy|Di(ye^Mv)J2%pY}JN;uXuvP@U|Mk^({RBqy7XV4o*3r1}xSnd-n5rq(u!cCMxT zHyn<LFmRb&e^BlRu15os#~wfU+xK6^wTcw+Qc$-#e|+i8B#K%dND!zMv-*a~bq`vV zRE*g9KkO##>Kyxz=lQ=R#`Yh>XVcp*M`1p}|KA5>g8Ld9ik#Qbb0mJ*Cp-<M%>Fz5 zB>u1XDfs`{r>C@%<n~_NM9ip&XK&RFl5mNv|Ifw$ST%6%>pW2Se91=L2g|uT^h-5} z95&c2h#87}rmc3J6tVxhw3_(K?ZRf??=u^bR;KPlR2c&lUi}7a%0%C_uSOoMQyKZP z0qc8buba?1N8l6&>o`Uo*!L#SFb4{X)bI{t^rjBtFEBXq4A5+#h^;%-sZq2r7qB$p z78wVepUi$$jRe`r7D+MT!Jr{9yO8Om3v!Rr76K?4_)RAz%l&8>&1cYukYO2snLvqz zt6MFK$4vd7{;<hnaMFT*Ib;a@2fIyh8EFQNwv-AJ7H&RGLV85)jLCRs>2bn!SQkXr zya$Y)>{saXnpx659BmQg=vRsK%lw3{W8x|N@-^*$jlnU|K&savd^ut!skh8^XzV#- zpfRP2@#nB+aCG*>Qn5G+cq!3=)dKH*@NxdZH@@FoGX-Nw;+*izkF^~}!gU@xRF4HW zqOc<A``$Q~%NqmBZi7y34^TI$;y!B+M{G(8R@xBOVt~G{FZaVfjafS92dwY}(+uc< zJMAAR-VV3=5KOKumBr&L1%IlNOYLpuuKTlZq0qiscArYdebwu6Nlo~ZH0!``oBgHz z3lC!hUIzZ+jS!BmQ`LE0)h0RGK*MuIK4B`fL-w}S+mcYG-sOP5asfOdYV1W2Yv7c* zVSj%l>(c>y#FZLuf)3Cihr91j1<#mF>?Xg6H^TpxV~aTBGv)tJ7_;G5@q8&r>n(Vv zVSOsDY$L&WIV=x{YsuM+c$WXeoFQs*h@<Gd+hKn(ctHTbnIOs8zGO)N;t4&1J_UUf zgo9)MahJjgvzt~s*uOYBH~Sx7l)-)F&Sb=Qjv?%STAO803!>+&M`(uWO*kB&%o*57 zJ;D(Z+hgr6ycEC31`(QoIX0KzlJ&p-)_d{p%a6Gvd-!gD)Tr@)g8$bTY0x>Dkq)mx zl}frd>ww+s<r(loA*WGA=*IC3R^g&{Cm892Z&O&g3d6yk>!O40>PXabQ|)&Ezh-q| z%=f(B7lj?xoEdAoFC~Y*#D=2|IYE;sD9%f8xUdpnJoh5V`WXRNfu|?sf~#vn{^3{u z>H|FKsr9^d<Q!kNAjD~XLwXAjE_HLqsvLv7^FInIxR@p3xAhl_1s#jSGB%xA6Dq5i zS;*^cV<ut{;2Hm)-?R{W`w)C4EvwPp$L{~H!FBtmPLcnMc`>xd_`g1h|HYkBk}ZkW zU5eT3#}PMZq@3;gCqHk3|MSiAzwEJzeR982kGpQ>?leKzH5xeZTQ(T_&MAc*GIB{& zp1WvcyiR8NY9FPFy$*K-GHM3qPJz7+y@J!=-_>&}13-Kt7jn3@JD~<roCC9Y*{5*E zwydrTVINcdn*Ur*>@SUNy-lKHAnQL()}unGEPOt$DfbwhdQe!BR~55`;i*8n=NWUD zU;lH;@HGobz~#M^nz!8^gN7izZ+uF|46d?3I<U6|i2vKSuWbMTsvP1Z(3kK|8RYFB ztp=!gE(JHYP>+t${X;GQ8@dAsgXkmNCyd+z<EjaM4G-3N$URH8ns<=&;c1b4jZlNm z!+|y>2$nj2I3d^E<7Uo&vk%*3NsOt75AF6f#>eb2W4N*V5S|EOKwC{GEstwl{^cKk zXC30W6&^DpHYw91G*%MRYi7*h5Z~#RX5U#-ih1$*);~BS*>)D<`&5P>1etK4=jj4$ zI_y7<E~J|UA(iaH@15&y+`Mr-lqe|FGW$oKOTy0LNF4_bG~_Cx`ya7=2T0ng)Q(TH zv#&xZd>7VmJlcrPux7<zpr&xOB{r4K?!?d74Vtxl4BCJHCND_A>qc(s)^I~a&AZ&{ zP9x=nZSXeEjzj|<>tv<ohil*}wzLqVB5{f#x*UN9iNeypEzqSwvrU&1aiC4T1hQlb zFaY#;AL!47|MLy;f9qWiKlE0iN^X~Ntst1%v%AOeve=ZFHBK&v{v`fa+oQ+Z&nKv$ z{g3l5{;v_UkPY?N%(k`Kap}(fbM9~XcF?Rc*DrzrH?l|@1jt<cpEWD=p5uQbCh|Y< zy-#;38r0bTCBNg&{=xqdwejXaKQxK>4r1(?ek3Rk_LcI=HO7kGV^&bGj_W|c-}>rz z*DzoufFK1>AaN#`HiPt3_}KWr#D(elKnr67hSQV!+BUZwftW2L)>gj0ROvvQ4!|Ru z^w8)L)N2L*p(hHh--nzO3VCXjeGO!JJ5vqGGJhnkZIXkxTMEz@QlaL8h;Z_&LF*;n zGeQxwR(3+Bg>3*qfAA##cQLoeFaKYE^xmS5Lycf3TlVq`)E=$6VJ;=i#$>(i+Y_hJ zM0IEJWNiLFtV#BtlK@kLV^1P=GU(i4>p1T0|J>}q#hl<9uDAFE{-2T^yu|<A^IR*X zl#OBH^q&9M#o+&N{*N3do%MFo()v}~QvXkAmw4$uu{~8j>DZ^v|24E(_2&3L+2}km zzgP%<%fR9;&a?2yUIm>12_f$TC9{w99h|V~glV%~PXX43m-G4FI?5x+Nxa9t&iNcp zV~t$c$=rn3yps2`2R)!Qvm|$JarbkSrx-9{hG6BqiU;G&Yy$KI2W-@k?RtTLL9(6$ z$=Ij9v}ReMz#u1g)UaFH2eP?MNCnk=<{;vLHzdW(H}*rK4U1f#o2gruq8T-s1VH34 z6rb6i?t~!2I?Me`;;p?~^RGYu_HRCnum1IiaXuyi)^lF_)Yn4FtL<<SKYf4uPHWXO z{Rjf>X^y1h0S%q;I*pcX2RJW7J?1^5L-i5k1yAO>_HG9OoP@wu%AOGq2K0PhyzV&~ z;)`1W4!FgB4}fJGrZmoLGBIoj`=Pyor1S1;C!|c&16u_+)t01i*`Qt5YdJ~Xmi*uU z>WA^|cRm!IM%Xk1$*bAH1}W)ct-qNf<6!P}mVibBI3R5f0|ZPE>MDeP132fHM}LfF ztbA+sDtTg9`Orf6M=A3zaURgbo;4Vlr40#-21le<PQbAL9r&gVcur_)tL~Pl(dgzZ zQRwTAdD?*5E&H#Y`oR?mMeru1KYk02L%3s-;0m4|jKE3bjbsgjSwl1eS?br48_@G{ z4wm<pRa+9*RN7{o+aoQre_LSNovKZ4{T?$Uh8}rGMB*v99eanEva(PmxfFGrohhsR zEi>x&PJ6~^3jOT)ACXa|jn!7`FPZ<@=NLd2|6>VRawUpAKOUEzDGt7KDmUK%(LjO2 zoBuBr9{#tX1xP;X@j0tl$ge6l$^Rx2@qgI1g8qPsp3q8yA7lT;acCLaBXPkeNp*p7 zc5oBse~AC%j{hx*n)^4Ap`ngaauj+}GSXg(b%OuZuw<p<QMgkDQ*VKt8H%MkA=0y; z?aVj+(XoeTKyqtk*>L$=-+7sT@U3@S%!9h1n+bSV2}k3~;arCS-e&5k%S5c$&7!3} zV|*A!A85D_T9I$l-Z$a}LeZH7VtZ?5^|^&`=kgpj)Cuqd{af%Kvl5^Xz%jXS$;7uP z+DrOWREo`Cxi|l4m;)V;UDvL^;9&)yby@;+`F8yXo8>e9?R&3w{-;WHO$K#o=0qLw z0GF>Xjy$t>qW%Q_4@<VKyN&;2_<s!jkUv0Qg~|M6$K}I3<Nxz+`b)i%Am))p%+jJq zC$~4j{>lGkN^lPUCqG-h0NUpGY)Sp${~?47T4S>!Ij(p;e|IFFpHjGNaoZc<|BQ*# z^tl^s7*G9j_`kt>1)=NVP67deCmLT)Y&D~g7aD*jYN`pZHH(}wp&>(VV5k_vjBu%0 zrJN5U;D<;&MiKBu+YM)ZLvWuRgcn`V#=5%zej{|t)Rb_=yH&WPHpe38_P(Eci<T3x zPn<G2RKa9OCHtuvD`wVC8{p%R()*06h?_>8+lZif2ZCTOvuiV@b6&!+XlKWqd1V}7 zFmVTv>&De_91v!*>itXnI=xw;nFon)%v?x|auI7bK;W@LI&Bync)h$m%KiWSNAJdK z%Ouj7>?L3e^im#xhCHHdKNv)0V-}XVlZ=IyT|wMuB(sCch)I7p_VlC#h!NKu<n06( z^gW8qs$y0IEDT;(>6|rw_YkuJC5zc;3z)qx>KuAJ#O%f|`(GcOu~`ranprlEv4Mu! z|3Di>!tc`R?f9=d_TGoD>bJl4{kYxQ=A=M^AtFUqX!VL(UokUs8PKDm1|mRa%`=>U zZ72x)EBsrgbeLmlqIAnZ(0QmXT#x&5L=kWz0CFe&q3mNUGI~8kpG;k>RC4Yn;4w%= z_;%-om_zG7p)_s(6^ik%^F+?tJJo~I0=e38JH_wZ*SNkv7^pyTj)x*nZmS#u_K!!L zqOnw+?hhMu;6T-bK7Bt{NWs+8EWf=jl}ko}38sJUGTckxC2lifol?IeVn25w+L@B$ zp7VJPG53ArRCLfB`_{5f=qO2ColaU^KzOqr5l2;x=smDZU<St5v@iM|dpb&DktW}R z25W99N#^I|{}Ci@E@a9-iT@{1op$`J{EzFN@c&w}OBHl|nf{Lbvp?j!icSAEo>*<_ zL;bjXTG!M53*#?6rd*ixssX&`_#faofqUNqk76RAC-xtht88J@e@HXXc4KOE1OfB) z_iV>g_|p2f?fr*h^g)2#?TiT{uUZet@N`PBc0<lcq%x7)PH&}fBeoR+YQM!CIX!N% zH}vj78E4;Qq_}lva~t=6=aGv(e03`xfOZ53_q@|A%m|_Z)5O5ic=~B2i(o37;zIJS zWglD43;ly*<l{ygB4(ih<p~IlJnl>G|Hd_k`z@@BJ*l}jH>q1vAv$@KsG8;KdrAhM z9dn=TKa@xd*I8Hlp8q;Uz@kREBH6(={McTGl0)lDzaMM)?cO>{6cx8=h6%MwFT2$R zFQ)D?loMdf{#^$qk${1sWL8ZxTi;{eImHYf%#F6N`=OOx5CK3_o%}@qKgHC2KlBmj z`s}1id_n@)R~u`q<&ouQ`Lr@TwAkwt_@DL<JJ_lu0cGz;WN_Rle~Q1|>dMQGJ3Rla z`M*AW{-4HlsZ3m`?XZ6i0*Q^w33T>2l_kRjXv8NKCW_{W1EC|nw&g*B&k}Ja>@-{9 z%o8;QM$<QttgS`~5&11<McSH&ofG|};7S>iSp|T5<<m^Rb2ydth0n-tK{xkEO=1Bn z&B{@@m&k9Xv;w_#O8?MwP_fg%BWxBj>z3S$*vr?7gCQ|VfG5Fw(0fK1Wga;^8v_Ia zeT+_8vZnMnlMADcNtGz*FRm^gv|Gka^@60c<<ho&qLs5J8|e}ZTZ3Vd(WCcVB1Udd zbWN$Fu2ld({HVv;ofk@Bz)5&fa>57@-Qpkt`4A!%(UO5hRfHov>@#PLV$Ro-El1Y< z03k-iM9^MTmwuS#qQp`entf;($z#THatM8sCjyX-?+VKNgM8AZ>3_sHigaRf&Kr>W zmJad}&g6KYDtZzfYUBEzXAb<{*Wby<B)~eTD}%{0I2DJ<bJ@o^@`&EfbzmDw3T*IM z5>r|}hnD%a$}hrNOHdQ5SV|POW-<bwBUf>xbw5>3GQZIG1y;4wsilB=WyeFd_CH*| zkNsJczuw>SO6;u9VTJ7X5P!u(C)x5vuBDsn+I0`_t?TdO=Z_U%zej-C=&WTYmbx#n zzlVhX$rS~U?RtQotuZ*5C0r=mV*&kJTY~CNr!siJsT|G2R<SI*PCD1RmiJ|bVb$UM z6M2__Jj7GVimMI541EVxiAAuHn~Ah9HEIsDlG#qX-BwU*A=z%Gl5;KW?LfP7yBGB# zq@7*O{rN)=%~}Dx7B<f;+x54oBM*=<da^FKZdmu=d)(8{i-61zewMuf>jAcMm|WBA z4d2=X6R5&x@Z_*u2@mI%xNyJ9$(RM{w9f-_IuMQ>aElIrHf42?GH8^I>&1nAF=dxP z=xh!;Z2AADUyd&v|3{r)ZCa0X!7~D4aGZF*aeRN@JkRC17!N9VZZj1;Fl-5J=l?9@ zk%^6oQ_IE^ztPLR$NppNzck*|*`SeJWulyV!0`1JwtB>u>)x!!mt%eZpjB!v!!vD) ztVlq_zO`Sx@l|p@2%I&2AB;Ma6A6$-jUOIkZNMOM=5ZWwMq6-LiPyY<Nr>a%07MF8 zmgkUL#QWjLXZ+sR-nl^Ix~CM__})J00F*#$zhKOdyMZ%=h^}LN&z$QQ^5T%?!^%MS zO38QD1)s*bG!2?K)31QL8vv}GK$ZPEHXkxJj(|RcGm~PaE-we*2u^Nv?FV|Q=|*{u za`H_=^RL-tdXjSr2*64QLmBdK8#FEPb)IF;e>~3o-}1M=@xzFqPq5p6j4mVvd<3}< z(&x&U>R`9z;AoqW#<qQe1MZOddr+m(0mKgL-&-fWmC+gh-_mB3dmdv))^?VeNW7<| zn?_sG_NZ8rzWM*^|K*n)Ojj)-FuRh2#b^#55iEb3%{lw?16zhR9&KHkXsb>)D``ki z!j9d*FJ+#<#-J}pJW%pakuZ)<Fl%h5_g=>sZ*drdz#H3lHLvdyL))cMa*J}e|B%Fj z$!4Gyyu1P|MLmgKY5(4T<JuQx&VGcXg@Hq~ldfH?Fy%j|!Ff58v*9zlFW6wx0N1-4 zX2K`}e@+{Nndu6J@l!t)hQ)ATdc+Vc&R65Xmkv{F(YEr+gLeVK!CeABAo%9liWd(V z4)U!O$I6g_OZYijaXAjiUiHQCrMF_&?Cn(}yVC6)vDNKy2JTfqH=DAFG7N2x{p66w zgU|=UUenLj&i)LOG!d7D%R?zc=)>c^wRV{D5rGkynMmjjV`>DTlRvqZx+l}S*Utl2 z{}+h+#nr6`tTafqr|-a0o&fl2jm65)E+GD&zy5>x@BVMU7RZeu3mg@8Fbp!snpult zqL0GQ5)9>#$(jL=F;;{}!cev~)w>kGwVY--j{uVzAE6{x>Wb|zw=cp-`<rVVm!kjz zIN|)Btb2k0%*M<}vii!gdNt4L9kKM^qe-~z(%|X4CJKGW=m1Ezm1S{ox2Fv`|LUEW z@$K*aFuwfOLp?ky>gQfSO}m;b9W%6Av?P~M!;-lthTx%1i718evY*R%=EyU5M=q%2 zT$X#(m)`0a;%exNN8x||RCa64R96QTDNji@b~WTo*57(p@x`}~_)mZJScB`rUxR(= z8yzmjF42*KQ2DXc&p+bGfBl~vCZ98&YEh5q@^<k6==VOX_^Th>u4>lDt-C0Lp5@MS zfye~}abf9;`qJB1c!t3K@!!{#12^b9+EE=h-4$uLx6I<<flC8rDb;D0;nwTJ<Dupu zt&v~)+z~IJc^^OO%iAviG_Ys0J(+GhHUU!D5w9nEn+|$gf)h>(^U`E|y?XnR8@})s zw41t`{+PGH2{Lv%Y_OzEUH8v^{bBVl+ur>;Gc&qC$wtp7k7NFy|K^MMFMhKxKMN)! zMB_aj1Zn2|v7=x8&g=O2R%+99EUmTI_s7`(>f;#u7hhz({m`|&ejM7jXJ;Grx=ayL zWtv~WltFEBqQli@fu#W%gAZ;0N8kA3@$Z*jWH55he4QO<9eb&(h!?Gg2l?P(1v2Y= zT|{q*v21<pEj=mkyR40x;CcLcSl@j4txhCda(W!v`1}i|Swf&VF5Ns-3$_*gh|dSj zKz;e`jQ`8ud05WMf`Ik>)Y>F<wGD>|%8ZCSS1n(7wB`T$AHKc5JDnNWva{i<-NVa9 z-&*YX-iK%Y)emZFaeC<4z8=d3xu=SY+RgflSJm0}f4N^bnD>HTts0=^;3-MPy0oQ> zva7Y#Yk5KRk(LzOh3#{UVt5)ey^s3xxwpu-N4@pvbG`M5HP?2|ul@B8;-CHfFU5cK zcfTON>xiAJV|wl!YYwbklBU1H%P_|xjPvH!+N#lOX1f+YGe<u92O7%JNBLek#Xj+C zV=E|9BZkJ&`n8g10|p9U+2FQSuZK?Ip4)%%`<&?8pekK9M+6r-t#;jFCDb|7zg%AW zcP9jK9T)Xm|N9RvtiG~bsR&~G=_UHuaB<-{?4L=gjJF<Q7Di<2NM`K?A*yXCGdD=u zfgf~+xY8_s1efs#`gs5rct*yh&sV~)pJ)=`x&lT;%-$;i7ZxxK;%cR2aMjDB&-Ex9 zXtpU^m%x0>xoowV1lW0F>Fk$YL}TqO*5iS%`42pqft^k_mh0kcY_9XX?&v8-GJT<U z&A*!X1EZQ*bJlgzmdg5F@~>DA(~HTxlJQW<K}f2j58-ehO*X+}9;a;*;8q>UA~%@? zVhwcBk{0QxW9_VgjrGN0m=C(`;T(sdKf7CkKx<_d4f7IZbxsqNm9!4Z1(`;v)&a{A zu1~q934fqr(508JZa6t@f#e04s*qW!HA)=_T78Bu2-edQO2`RmW}HHyFwSv!6PdEW z69AXd9xk|9N^C9X55m`0%0rT*Guu*K?bkl!VjE1bGlr8wTp7ZLJm#`?D^?k&G_#YA zOC2~%*G^JDi(Si7T25Lah4DyJ<<O8V@a(dBS#PDB9EEJ5rA1x*(zj09XyrZnL;J_x z;KzUe$A9;Jy#LW_;npI7;>d;e!)mKT&7sst5ZIEN30A5$u7$76UWNP+<d$-0X66#+ z_QNuWIU<j|{~WVe6yl~TR9*=BgbZQ>ys21l|Ma!KA3NMMv#dI8LvmXWf$g*EyoxU( z(6onuse3QMT?Fwz{mFONs*XO5*H#<(^QT=-A*_ma_{eYpXgcvV%w^fs*{MC2V5cfm zuhW*Qa`wV!t+m<)=(yfJjQulr+u7}h>Bq_^yjbs#BQ^VeS>?f19GJ0P<evB*t7j9z zxfUg6&+M*+DcLhM-GqM9-o@psp6l9&E5;rbrSq2)sLu+ArOJFfVFHt98D9JJCvpxm zld4(W2GUO02A4H*9FI9C1LU@5A+)3}gBaQnh*^VGRc{Zt9&oc9p(B;e>x)Vl1ZIWo z&v0(Y=sb`UMBi5OKayh~Qr$Xi)`JNS=tE`ImPf*8WB-NEsEX2IFRL{g4e3RkO9WV{ zfqwQ_Csu%T9hVwk?~ey*eDp(MlAZ4H$I9{p`b8%)0R#ufW72jlBzWyZ0BwIc=D<E` z#Xu6U<9XZVcsN8{GxG<X8jVEMj01E(fRk#IR*@uhFgH-b7gxIU%0PEpp_3Z3-W+jY z5`a3eu08e@13hg0y;iZTaXggq>3$!Y{#;|#s{xUS&)|%*QiryTl=gFADR7cki%$ar zCLNs`Z44=33ljb~dG=xH%K;gqtRX*OOzRZ&;sF8AS^;#{g22X;Q!0}1Z-|RYE3+lH zZT|@76RWJ+&XF_C5Z=Oe){rIe8mxi3Fuo6Es}WJgNDlb{xDM3$FjZXJil`hN>{Sit zuUb#!2+#PR{_S_`qmNJa5WeyqmvIaOTx$Bq>*$}jMbzZ*a4o!Y==7-qjC-?J;jx~) zOJ|uAtiW3S!CwEUbb~f04aU_;_!y=yp>f)&8MYVq*uTj^-F7Ed1=Jol%xz_LbHt16 zHU3!Q+EkvnqnCL3>K0%7+Ygt|RMWe?NsDio1ZW-T^5-0zeQ>!_sC`?E9isM-|If~7 z1zk~48Jm{}zd9}I<NR4CX)DX@zjYQE)fU+v3-#Xy2dmF)EV|7n`G0+Q&Xz~5R)Rl7 zAH8D~I$w1^?ri{o{pxd(dW9u0E3Lnji&jsuUDRQlNsRFHh2@drj$s1XN)A8$z>ROR zX$`51f+OveZS&pC<T5Z+Oxz6xU*xnURlRKX-p`-1vo^3m9A4=Ow=l;Xl5YYiD2FhJ zLrx!K;)M)3b*!h<$WcHjDE)?HC&3#-=sAIYM(myCXGmeVa1sz#>TgQOt3`>KT38Ld z2mn($NU+RSNZ=D&k`S&U$U41G0+=VtLrGZMEcn%|Sx*EyFmSwbo!q|m&eEVL8wB38 zd1fQL9b$=fC{_hiiL+H_Bu43va?$p!KV{>>4~L{3>-(#NNtmNbQyBOj%OuyOOr*nm zSy?_Yk18tu!!#44$#Mh9j4iW=tSbvEOF-kOgk%Q)M<1Q_KY#6q*K#k+V06J}^~{Z{ zxB_;(R(-{Pl_-^dm}$<{rRhZV7UL$zCpYlwy_6~=xX0Ikh9Sv9@q(iF2c_18NTO5V zNX%dbK(}v_?x)#*<&&<=2cyNKWHI)CRd?jbrJE}w+U%blj=S&tK7m!VgYyU9c;|Zl za&f;c{RYak6tqWR_oO=ySGsvjVuM*8=7kykR_H;67*~4~<!aqSv9Z`=EvDmS*2!p! z_bZXwf$p_3;OsLtR>>y_CzdUTCbri8>^?oHf1`spJ*DCdoNR1)$>{OtBQG!a?>s?h ztH4B=fzMIa!O%QH=VPEe?JWkOF<LB}#m2b{eccgQ-q$}jT*Gnc-}Yxt*!$(Yu%4Dj z=ogLZa|vL(WWj}hXJhHDGohGbxym}`z$X9&Fi#nx(^T`c+%UuW78&-&KYn-v-<<KJ zKS9$gB9hvH*Z9kFTc@J<9Odp$%LF=+?P^L4*$2^$oUU2X6A{Ffi@i>=etq`Y4y_xH z_rJ#26EEP1-XP!el-QtOg<rfrF-B+US=-Cbnq=DMFfT{UcBhGWhJc0qt(5J6PixuO zZ=#=%GkMh$M>GB%-$0dlZR=DyY_;6w^V61vsg1uqDAg->BEuao>fs3+AaJhxp?ei` zz#<+Rx+wZwpR)}K2tShteo2?Aaf7(t<={O1oCD*qM|m}M@y2mzB4#kQdEjKbPGUtV zzNP)2AcD?uqLdsDb6rd9kQ}nrp$`zjHSO6kXlRGB9e|Vepr4F6+W>VE4~zJfy7qdx znlAgV!G%2faODb2T%4M?inS?00FqqZ9n-tVa((3hFpSY|*w!TaK1ZLwg1X}eFK_Xc zzj$xGJ!Zx&CQkEC>+cp_mL(U$J+wUmAMJ9rn_d0|3x;jssn3Y|hU<xcSHIk6WJ00e z<``_4FV+guM&8$@3X5JS#MMG%%sT6d{~t4PLl<JZCwJMfe`P+r?4~RsprXpZ?*pLw z=^l8xlX{fg+FyV4=P&d7AG}^loOL(p;_Y88=E|nw$~3VtRUZC0%kf#(=fz30va%P( z^7My-8(!p<poE48qPb-0W0X}Bj0^6DDNIfpkbL5Wv7i|{VviF37q24tXVIJ(P)loK z)En);TpFD`q|L`!hNHlNw83S675`bnZFB+&_r9~nOsH2v4fJO-q1se0Cl=)-Zs+1F zp8ZMr|Ez(@|C06ubPO1Q+wRT{FigY>krXjz*tOK<yz4-9MA+U$)g;Y8TrV`9BS4Oe zNdStB1T`Q<-AQ+B`|cJFJBWMRuigTLq`?nubr7ee$~M!EhzfW{w=7q*e0BxvBZA&( z+^v?vafRM=YuiW~*!ICQN3HK?8}YB?-Sd$VA2-8iKtrxz^Eg|6_4<VRUPyfQX>C<& z&)kda<=K$<@hb#G2aPPdpXZzbKov&DuZ`=Zrb5-E=?WhE#^u6B45bvXxI|SAn9-|( zv9FQN&bs#24$`%5Gs6sKt)QbOWVOeCV&oySGY~LLLPBhEI~6alOy~-buBAUv%MAL` z-53}F4eO9{N6ur<+JXL^Kl@?)KmM=33V*e>pkg@|r#YMh53<?1506g;d{E7?za&6_ zboqXDI)#<9A2(?pRyT?ccaCc?s$kf&tf-VoO8M3``6=|YeRzbpp{<tf81o`eo&i3x z#zV-xnNgEfkC56a;X4f^C&ZwLe^A0`+~2eB`Gc3Q;<vy4PTX#hgv>R2y>_v-+xx+5 zkVsozW5;R~_d+BxStP4fK~+%a3VnO1cDd1_G*AwZ%WCE+HC(f3y#}~mJ+|Y+TX*<; z3`G-XSk!9(Lx*%+MG%;!%IW%<A3sKyAKwC@ZA$(*7}Slyz6&PU{_Yo!`1LP7tgl}^ zvh6qDz4fy0tAL|Oim}4N_Eri2+9f{De181s|NQ79*iD+FOyyj1y|sPSYfTOJtXl6& zT&oV4AbI@kN3Wt@Vee`?Ts7kZ3<7%3)b!DZp7OcYA-qkXb1b|FIh&`Jsju)H`zo($ z-dc|^%XrKK#*aQW)rI1UO+x+<ZO^Wa$!iyRSl49SIr-xQFE1uP_kvqS`tJAFxg?X= zqm04DGK$3OZQSAPeC>O;c>Cc-Ow1Tj^^|Y<YRG^ylJ#Hw=7aR@uiii7oe$6Oa~`oc zK}JgYHd(}bkL`bbqx|;1qW~%f)VE6h0E)hX`OmM*&Up36kMDof*PehJO)3yjhP90U zmj_8n={nAG5lk?^8d`k0+IeBggB>FK!;e;QIgye5G;W@g*h&}6)$dzU0ZyHhv2X^1 zT0uQ%4N1)$E%bTR)ffE`o%-+*OFnuPNZ7)5g^&fuwFt(di}m&z|Htw9@YUn*<$GY& z0;SiC2EEhXEn#N<y)S0`z0dbN{<VGQ8{dCTP^Mp^$9(#IOXIM#|J9=?UVaSzr&?&W z5XF0nK!cAB?Cz}Vu3-9sd#fM6`0#aJu2fLb`w#d(@j5%m$Eu`I@>k3PE64NMdsI&O z53NII^MedwJ^29#XH`6k^>J)^{2E4H=l}YR_u?P@#+T#oTzkSauET$UA=4<7`QD81 zAb^b|;~Bps!2dD)Tyt%WLQa`&7vqPgl|eCbX*B&xFem(r*)mmU8Df(EPbyK50{pxR zY>u*Kc9Me4EH=9Dd+IZ6aA@!4NakJCmbOa%YSjY-R>1Mj^YqHqhllo`|9^k--lPBB z(f{i2(z9Db>k&vs(bpaRr_cTRI{#vGPp_v>#Uz*1L~J3%XGtz$+t+2>I@HoC1%Zf; zuIsU#XJO2(bv>~^?fU;Mz)L3}A>XRq|JRc*&g7we<c;E)I*VZ1nLP`K@MiOWXM7H` ze~u#P?eVjRN5A65S{c->|M>L@9!RyJaqEV&6Po`!u{bHdB{>80n8aAXwqkNohgrJu zV9HG%|KG|7GSE4#*pk@hEx?`g*jOV-I|sl*bCoz?6UqO@$tU}<=sK*Aw6=n4BOl~W z|2N0RD6HF)?KQuMm`DZjRf3+xx6L(DYBV%CVgQsO-)<uIt+kaxUp22bfmXOx&=6?l zsbF0*aGGtBKRVDxAP7^4aJoZ3&^NOEiN41uMMk*ekP>=o#V0g80VoJO`oO((fiDN# z12KSBe^h-|?wV~tzt-#qor^NnH0>f`FI$H%f5NwDzoL;Zy$EVu^zF$WB(y17V<`*F zuH-szWYyY+uhhb=?>Rxfqn$kHa25g-2tQ>=^m{P;MeV^d&FX+^1yPS}c#UZNz<$aQ zT6(%8K;+~N25y}Q`0kHh$Jf5|QT@~3_&ocUCgPc~D}eyB$ei?$3um-fiXli?Loigz zG+hlC+H_Wcl|?-f(bB5QnQt_rWFOr~s_|(7*2F5~&{6@*iiFknK7QFq#NPJq-)Wgh zgoaEaY>vxaGEUA%nEcpF4B}=dS9O;u{MH|R=X&n)TK-!E-pAIyu3oNx74rI#0B`It z=xdSx*-Khik`Rs|z0dJNeRav1lM+com9>dBVLH{@Pg)vRu+8l#SD<?Z(E|Y$Vb77~ z?33e?2p$^q@uO|Md_}EGp>*tY5bZ?F3H;YS^I!jBEAW*x`<wTb(}}dXW+EOECHBb; z*h=NqSqH)W>J>~Los$%h<}JBh=Yc#XAWjkThp!%G6pSh>#aY7nzIBi|L(kzn>4?SH z|A8fs7V!qhUaOP3FK=Djp&!~y|L9Q(dM8kcAE49~a<C+4B!@Su3L(sD=J?aF-EmlB z2DeAB^5ch=&n3SrVKoVzvHy+GIC_RKLzY<|y~21w(K!WGT%7^rP6f(M3;+3l_yRkn zE8cl|#-G1`>o3NhYm-lql4DQaFtJ$@SO^W83s9p#j6u=5;n69)4vt%mdrW#Q$I!Nd zmX>`po07KLgpzT3<_G~(WoH~O!(QG0_~z|LfcW^WfuWa|mw7`K>JtOs)_*KEHy@?% z<=*e9v|v{(ukBz$B53Z}!}Msky`3&E9C=J^-T*^qdFNn~)0a|&1pdDcNkKmz^QYl! zRVFUn?SCah(dN&+coBc^3k@x|$9DYf4+He}I!SdstPnrVL?CtU{NK1sZGMLTL+zr} z7V2JhbL+NWNyU%PTfRQA5UjYmlKxV}@F?+#k6*3uCAQPo9;~xYTX}q90JW8J$@|LT zT_$|=I6Y4IC`Mfa`s#M8fAN*?<p1-(`Ue8{46O1Qe<Qa+fQb^|<;IyDLi)wlM#}fV z#l@D4Aj=9E^0@Oy7IdJDmeca>w}6BJlr*+8tI9#uVBSRC(cDXy|2${XVZnN|{SXhz z+Za?wVOE$p+|G0S>6ic}=l#_;raP~E<qM0MFUVZJ!R>nV-;d&NfADI#@Yq^1FtIvw zu;k|d0RPvVt+fL7b)1fvX+43e&RdQ*briEeRD|LGj1ZU9-f0uev<WU}3x2c&0zD5p zRK@XFH$U<J5qr#|`*fcYwyRx_-@gKXW&8B_YPJ|>YBj*@*1nx<qklKzF)7mRRq)DA zmYjmra>Ej~^aO4h<_EQyKB0g&Q;%*&+*a@na&9n8j?$iN7-M%{PD>cR%{%%;V=rTZ zHTK5N^?UNC@juT^+wQpw{?*igeOpp!fkDsWq{fp^V1SHAnG*(*hxsTixkS}wSWkcG zJBK<{2!TgBL)_M(;-J&V2#X3!LTzUuJpn=DI%mHp3x;Hq>Y@N9s6QOPy1Or`3~g0E z>mq{4x(DQ>ycOnRu<<@%3n{Cxw}ruFFFWt0t}<Yru8H8kxD;j)9cN^WvaWy5c^>iF zTPa8B=w*xo_O2&1+H1tH*J*6YLcWnw(0AmXr<qAuGD?n5#Fz@tIpAk}bf580mDMrQ zDx7gJPMbLeJpTXzUN2D|@`vnm5^`MDy_V9}zxe9AxdKIG&bUOh3XjT)31TN9&;-Sq z{G7q&!CRPsh|v*Kyt?aSu<Ryq*59YmR@hQZ#6G7wXZlm!uIg)Em$e(oq2BHwMNh2Y zRHJM;ps@R#Avd4d+O3vEW^E3|$Y4I3+gYBkvbe9uB?LKND)%RU`F?!+otM$$HT%Hr zrnNHjq<ytLpx<y*!ZCvt&XXq0!H7~8h+h!qW+g!DDe!9*-r*Ey8?0$jO7q>>f8gLS z#k_<t*niRf`}E`?H<a8?$^!b{Ws@;|)nw+KOZ5r%UutehiCrdI^4EP$d&Cu-FZ8wM z`iR*w@PNba%IzObvO!rmckX$hS*~ki(8<$&COFp!m&Pv(n6<{gFjn+L{gFW}$e5&5 z4NNj%7F~hlD=bBDrkQ#K`zXtiIgKzQndLSK_C@2_lw1R5A%{PT-;Cp2<q`Unnedj% zvad|=_Be?V!h(jJfFx{qgvV&!Q~n=dL~DKm{|lE&*!wL116-cvf37sFmcuy}1V2MN zpYZ=6pYb;K*3{<rW%#46lH2y6TiSyg8v9X+OtdhkpSX9~KU<3x3!D9;ykk}fnEg}s zd|FBG%@_aQhX2_Tjo`EPZ&_aH5#QHx@>{bGIFrZ=KFj}R|Lc_=-MicWresB2-R*y! z3DTopbyE(Z6{<H!Z1OJSe<VoH76gpD9~(-?S;=CxgBnJ+qh){ad;!4)u{vc!*T3KX z{s-{~-+Xsjx$<C7f5!Lku@0KKs}FX3arAc8DkTdvR2hc3xChr~nQG#*i5t7JBpu6N zh_*>?H(Rd*k9~wro+sjO-$r&$*o5sv3RvWG1?x0FDAb=(ln6VcDL5PD!jS*J{2@%= zqb>be(dJwTN=L7L{j0D40N(*&?*>l3XBhtz2VmPX-gfg^dguIKnl$S}SA#xnjZVeS z#{U7EhgxQO_|{fi^<(&-*O4}It18M9LihZiZuiiE&HgL;(Y>+z|5Iz&h~(fb$%sMw z5=zDxO`>H@UNK(J$P+=l@qc_0{|m<q=F{*wK7s!;c8iOljQQNBh1uMnf&U?g3TdZg z0fU9Z{5=1M^S{36C-!LWK6=DvSpol|K_uqbeVKad&~VVEd#S-oAYTISGB_AX_M{*N zyh6m0!v7I&Qi*%Gc+OSa{~X#^M=imq07nzGv&EU4e`-=vAMmciy~c<Qom!@tY3ybH zvsC2(aKKF{?){S(Ie9`Ne+o>={WnQ0icJDsg))Yh9L(+~4iK7QkJz$uR#OwzITKew zc%ct-z!Ab&n;z)kE(K3WR?w(rw-_UuAF}Y7)<%0IQt%KpI<tBdSd)FLP4CzH`gcBv zZ+!Qo&eykmMG&d64Ec(UGu<9Cy-1ED+}Fa%pw3dGLi?E39@^ld#8@*vbdW^0MJ^7+ z%9AwV%om+V{w|DT@}TY#7*`#q)N$I65t#%*jQS4A&K}x=&D9>*H$b6*yMrRNaiWg- zDK(4rjl7wMt#?0o9l!IZ@3cB_NqL0p@_KzT`I<^J<v5rn{UaCxg@A7$X9Vgw88h$h zmLzCw;2uCG#15c`Bj4(2CMQXcb>LTW>ke%;l*HtN@K_B4pinMi#3^J%BM${3{!_t; zCQ;=mD(#<a#eqAHay@T%8~f<7eTiSfa$7loUnxw_>5kh!W!O@mUdjIZY!uD9q8vCP zJ)^>Q9T?XP8a2i_#*N9zhI5t$1t2J6FF4-f2+Fxj#wQvd@>gw6M?f%%@4J)&bhQS` zmPqcrh7#5C?CJM2+ceR0B?D9<kw(!81M1@LY>*IU{`#KJkpIsPio^JLliO#)|GYoL zpz(SB--Vy+^y>BMnB|+31+Yuc)A;`Y|HJ-|B$_uy;M@VWz7I?U|D(~r82?`#I-c5p zIi{`y2qk;jf06&|XW;*123PiPc&}@95pec|HD7z7G2@ulMqoylJ(yj|_kpjc9UPy& z79!gMAjo3rnCWWEekK}%KG<QiZKo2!7e@ZhpS=?=Kf-cmbhN_a{VpVH(4Vq(|3ay( z_5V38vT1*-00iR?cUY4Xc9;pxoaK|V$tmmpb)ZE142%EIxqb!yr;?6XIo`$hiWN(6 zM{biFyk@=+<_mi3rC*C7Vi)bq3)jiAvg>4jAXnSI<2qWH^no9wBt?Dy<*okoyRRNa zb{KM`Y{tN1%EV*uc<^Mz?f}gX`9Fw@sX#4h|3M$2%3x;rzkUk-m&+shP`AWQ8~;~4 zv409Z@A<#mzVSasg5NK%>Uf}M57RUr=#_chUblbmafR_eai-F^K)yYRsB)GBjA=Ok zS8!{C_&-I9Kc4?fa{hGqKVi{tl>Y^2+?=W*3=nLQvh@!HIvrL&Qz9w@@{_YLwCN(g z7x(vVi3i@F8s54W>LClOpfeHs(uQDb%t3&BD*Vud2yS~rW~%!{*MIa#^>FN<Msjeb z4z{x>N5-S*(^B-);^8$2?es&G%H%8q!?QKDbml6**C$E%bHHE^_?G=q%lO_8m?+B< za`{R1au)^v#iT>Z?r`YW;32C#N_&=az=7bj`qVRfa8d>UTVjW)9mrfgTl?^GoS;#& z{^!){$WG7CN(BLU^hwOu{-v+{<;!cEMIY*AL_y~!om>p4q!FgH)zZ;NO_U*kMPL1v zL<&6(ux7PK)K;f{&$<iVd9YOQ%oGOyrEE9a*58dQ&+AZF>e|>8-~R^c*6OL+kTy78 zE=wjPx(jl27zf1@C<`B6*!Ayy{rmCm%UA2}L%B*M%pxJ%S-MFFi3jvV%(J>9Y^-U+ z$wxuK{+Bb&WI!0E9ov|PB?f}ds16O=ufZ7rZ8qih&si)TIMoonTGOXt1E%TemQMFp z>4%SO$}c}y);z0y!@mjqGG>`8h<#6bba;e&ak75TNOlmk2KeaqVhN-vdyTH>U^TOU zT&pLcm{!Oha9HAQ|Aql;B~=Ay0GLonBzAKfM__CS%2ArE-WZ-19Qvs3DQi>S^i7}r zzpq<s=OgJpbal7WxV*6RGVk!(Ui-|)x@h!q@o7$5SY{e0Nh4jj<f(>%c^{t*|4T19 zoHexanejjNi3VqrTI3dXSv`jFE`MS-o?tT=O*{QW{%@xYePy2ov7MN3{y%;}{`cg~ zC-8r?Pc$Q09HrLZb+Z0X!~eQpPA(qdYxX}f+WJ8qDDsZk|KX2$1z5(N{d1*a9z#h; z`o@-^XbI*WmW1p-$ln^T#@oYlv<Lv9+)J(x*V6#v_x|+z>tIfoM_*(-K*HL1p>^DE z3I?rwn-eQ=PjuOU$vmOQRgPrC?Qw=3xY|EA+<q&Z6-y(g4|4YTQ0pE$IK*FL1wpT1 zSVNDoOq_9d-yigGx*KlS%&R%+W>T7AWdX)(k`eNoPMh^*;^5jsb9>aSfAJ^puETf} zTv!yL^S_`N?-kgcS_Kr|4nZe}n&Unn5C3b3;jJe8aPt4sub;sGw+yp9RwALEb;tkD z+5bKNBPBsOi2orZP81wC44C+Y5|sG7YB|pwOoSZ8|B6$siH#gk;3QX+*;+_&qby1& zivPh`@S*BW@_&(C-XQ;vmi9wr=YtLV)C7BpAxXo#f|ewsolWV-gy*-Fo+HTyW{9gX z;3m|j^~7+Mbyh&?f{GE8Dy6efZfKo#Q!-pQqQ)2+14xJ}78J8VT0(^Zuq8Z^o@uuB z+YH$NMD#X*6OJqmms!^3NVigD&EA`;#A9)S!S-eroFVi?OEBxl?2?ip)Rn^^W%^v4 zjiDkNiS^R3Cf^e^uUU{Rd>3-eITCr7oS1-X(F|ts*ZZY21Q5y2S55#d3p1}%ZqF+I z{<nV=FF!mn?JjccQSHXbPUPz5Mp8M!IV@iK4ac`FrLeppdFi!v@r22-ZU*OEqTeTf z2WAMybd=05BjuiznPftBC%DqUPRZ5@@WHGq29@n#M_3kRF!n}O#&`6qb3_plHVIae z8;uC8nsma7s78qcU@tj;`};5BtAG8&2z|={TbZ?0dyW!FkC*iQnNB&{#+3jhzal9t zhr9PEF3U%DzjYlabxWI=G*H>01MB)`^%KDO)c&1p^A1yNnVRWvG8u-aZ?tXj7GWN3 z5W1oYp0R%hE##0BGjHcC_gB^C2yjNO?Rso{m4&^YE1gnz_OI_I+7gbrhlTQc)Ow#X zroVcXM}m4N7|ibWZ^8kznb*<wr1L;>mqR>(Ri-E)OLWi|W2UC0(_w#E6UV&OdxDBq zhr(hJ-W$WGPRONY-?%CfryJlT0MXh9zT+wb{iw%u(3bjpE7VN;Nq_mN^S`<n>2-W| z{9oqq%_K^ylSvA%Ey$34x=mN}G8Z;*c`yUrPvZZOb7=N|qI^E)g5{I{^XK7zPB_#4 zp;O(#;eVc<pDD+@#~r~!Uxu+L<A2|}ki-AHE@S6^S^ORUuiB2W|5owPl4s2DkNp#; z%ldi@|4Rq4swj-8q#<irRuSjMJ5RqTLOxm!)`-^v6FS|JKls-Bb#WLm3&j{t-TkH~ z^3&|ULT*OkTKwfHHIf_yGc>J?&l!1>H;cWg5~S$V_1Mvo*Pet4$gC>ni5Bd`eH+VH zK~5X50`L%u5ixy<Q@$khzl5D?FY+yfE(loa7}f@2C3FTwJ<8x#`OeF#-~Z<O!Ltv* z{g^1`;tC|Zsa{;6B9m^upGV$ih$q|^+<xwY?f6OjUq6BWGh|cn%eOj_tdCFh|1)^T z$y5Hved7Np9HIdJw{g1XfB6Y{Wn0#^gVXrETjBnH*9&05lLj;7gQUBS<c+kBlH$q# zumHw3n0y%fe*^r#-5YO!|NDzW<xeF&ylj?&WA>Q)S5O(i9m_OjfU1<hNCH+z%e$@) zF))bowwE~;Hc$X!V|*4_GNe$w=&_FR6PN^@#42hThDu15=IAVqt{Ll)LMk&3h*?UW zZFnm)#^rFx>?32E=B%r`=-0Z2OBate{?=_0v3gD_3ieacBKOp<nNrRy^2#$eAFr3w z6^J5eX!{Q{rP2H16o^Sil|1x}x6@%sX5WNV<3CWZkyy|;7N3dSq!VH1OKig=gHkLs zB8Bcf%JAjKQNR6-AJiFxK<$_mSNu(v%YyxwfVaO=$V;cjX8%LZX_uEqP;{yYzI<J& zU=7j!oZJdMac9u~#>jw9!Fg-I_6~J6eiBtR%0usd>>=pLO>*KY=T<^9Vsvh=?WZ<r z0*<xnBS4bjn!9HDZ;v(p%ddQIB`w<IxL>~?pQo%Y?IgTGhwQ&Cx@Xeq7;ds(Z^m&A zBW*a#Er%236!b3I{oZik!le_04AN(NR<E@+rIQK_Dh|!lgbX^9(8^>Q<cC>)9Xy2i zC3R~J2A<P?aAMCMnEd?_ee14=^$hNoZ0>fYYv4$)9%AMnGZSt9*ya?D%n#fy5tGnt zp(p=r_K##I9}mQNzb3WDmWlbS?_EjVV^%93&^Fo!-LI(}+srT|Nk;=FbNTHQ&frp0 zQ_#WmM#MNUIh@Avsz@`!xiKB$CmGZ>pnq7&Dr=!#k8L_bfcNCzOf8>1|5w{<`h~=V z&xHTOLL2_21EmaW#K!+IH6l2`v#X=OXA*2(4=45~@V}k}WBSOYziYB7o&V|BpTnCP z!JowcTs>ge_k{nmCb+<8xMPBQe3m`5H$G!l2;SL0`9F;R``fJ_Gsxh7IKc-B>PNla z+ke@;q|}1uiy1IRJ%i6xSNdUaqa;9V?F;m+C?k|vycesq3f#25J8${_|LS+Hryzt+ z@)VXu*EVC!zRq<cm-fflgUzJEB1p;zz;mA1R%YhEWP<=r<qOR;&UglGKQRb?h$a(m zr+A@zb(&woRRA?*OKT#S9Ka)4OSZ+C7d_G%<2s{&Ziv6EuAHWie^#FRp9E#lzw2AC zZvM$%{)ht!f2n4F!YV{7YoJ!-f9k`5;>b-xw`xBge!KI3-tm9j*+2antw_Xu1xbMa z3n)fQ69O>Vk8z@&KH>l8{QsYd|JyGA5Ay!K*OTnX4#gs}n)9%)FYZ4!Y{Lcw82-<I zjSKsm`1@)`vZXs3Qqnv`a-JlIO>(5su;1Oq?m8Jhxnr#rYv(yt-DGpB2>6iIb?W!a z+>dyy6%l*q&Sd<5VqDJu>Ck>CmrWK8#jkPW|LFWir}!JG<j8nFoT3#zzj1p)--So6 zqAGW{iMPf7QOQM@cgz3%)xkmbKXWc*K!6UqB3D?6*}5xjHGlvnXdn?(-bxXP3Ud3R z?l)$zXG}DQu86AwOII!^9OJM#;QrGSAt;)aLL~L3rjXJoIug8$L7vs)I9aLNn?b@B zBrAcH#um|mbm^oQg<o7TxMX-e@2a;1uqSS1v0iiZmgg95JBL=(QNG}F&psVMNUaKJ zjmJvY_&IE~m-_Ad(*rL)!z*B_2?*Ef=r|$jDv?$}7MO15BJhcg?vSLQfCOzP_H{rS z#rd%!_}w3UnxB99Ds;qW1)slH(0fCO0HXIncC0=}gn>yxGYKS_`AtYwz3LyT&O?wM z1JC=u8x&UudakbKciw<9x>es@_xx<;F#je|60aSws84zI?ERc}HB1#NVk23~f}qek z04dj2M8or;kAHvnM<3NkkCmEmtuEd3*-(Ron5w&e%*7Pktq(eQ&2`x9b<rH46^4Wq z00swPo_cn-|L_%GQ;bPbTYo<G9^v|$!JRmKQnf{RsQJIPC8DU7M_59H2&dn4ps}Cl z=#}AP_nQnty3N5!*~Hm_g2Rnb2!ab?c6z|pYx`)jPd@HjPeaZ&i1cm0s;wSo*PO#) z{(mc$5})U>pS~tYn@NnPp&C{v>n>>yGXB5)=({9=mgbsM0LoGl*|WS6Is@5p{gVDd z`d(d)nUH4~*yP{{wSvmjh)KfWFG9jzMXmvh)D`*U`uG>-|Gpmg!!9VxcpLn$!k{bt z_59xzVg#J$sa0*-9apBwfAas=@;?qmYtjX2WXv<+7vz!uA7BUMf1`YM@^$=wvL<=; zbUefV<f`#QmRKjuJiOMPG=bKX1+G)XxBszC-z{nUZ~1D8n+|$(P>++?$Ikz)p=1#W z@>H0d@_!}vwAC5EE%eY8P)$kfG|1AShSfz-$YoZBd*SAz&%TPk_~}O-Q;|YjYRp=K zm2*g<LikVJ_E=A=#n62v>Oh<NckQkqyNo$}82*rhh%JFKI7Ifj{QumOy6V|dwtOUU znofp<qcdV~b<YZch>ILW<hE`R0d6UAITlBVIrhUFY1}6Y6pNBtu;Q#M4*QcIf2;=u zb8{%#*Sz8m{tje8JfOR0_aSEmIo$j|qzpd3+Ws%^4JG}~|0>+h_X#}K??UlCu^(=@ z^M9ECb5Y>y`2PiL6I^JIvx*Z}JH!7)xe-%V4EPQBKWKtdYKD&)-;ec1-1*-oP1}1n z{0~2Yf6WZuHvh+P>tWVv@LU`iu^?2{aMS@+l~one)<?rFD7XmK6V*X5Q(+;Aa4;!y zF1e9ySp|SZ>h%pR1a~u5ejr;mJA!BVDW3jt%mBVAyb4(MY7(_mQY4*H6)PC@3Rd~7 zjGf?bUa$;_Tj$pEH8fB=$cTIPka8+vST@K5ind@)+vJc)#jf{^m|Y3iNPPRhrAA!s z*%N;wnJ#>A?gL9kZauhQq9Z*4L_0rBN<2CKO;<TNX-{2|XN%<!^JQp-qR0K&mk;qb z|Mg`&C=eK)H1CYTjV3e^RC%C-9CAEM5-lS#G3{j9mKZBiYT1>+w-Y-n;W41Q5jHwM z%kS9%&C;RR3Bdo+8$qA6a~DiR3I=B$vIDsTeJQN0RE2;mP!(>GeDwKO@uz?H0p4~S zh;(@RH{_XJMGx#gA>_796x>B@hx$bDz`HhA9ge4O8Rn)Ygn+{ik<!GwFjcjIHGXmb zm#>{#i4PRna5{FRVHHF={vSuFM5|y#b_9{9NLljX9HuBS{JZ(T{m}pUI744Pz8`_r z`A{*ff`v#J$RhXZeyhydVR=^2Q=&8SO$Nl<v(vbCGje>E#Yak{8R#J9evIS7gfibv z^M87FZ0+C=xJmwh)(E9K;b3yD+Yu;`i?FH-JBD4sUYbYSmI3xQt{g<-DzxT6&aoNY zK8bW%tsT4U=WX*pe$UC6!w34C@jqjUZ_57}u_t09w0MU9oh#R4C&D0n%Z_j!2-<%m z|D*7&#RvwMdPZ0LpYrdpV&gaF|HHV$cH#J(LaUOYvj$WKB~pt26~mcDgg)2S|1ka! z<Nvtve}Vs{9uABCe{4_{(5Lu6m?0DXQy3Iln9HDVZN*!S=e{$bKUaLFN0nGeNECq9 z<K&sMTWj@uap<4F|8ZQLb{r|bGn#?GU~IK?Gq_Z4vlwE9;sbeXg;6&}$@_QLzVp8l zuDdSH|JT29U^S;F1vD-U|Ch!ykw+J|majuY@_X4c`ov%)<gG{XTTTx*JQGHT3X%q& zwCU#mzj*(%_~5fwBRfmPpYnfh^h5rSaR1krgKBzn4U5!_7s3Boh+S{|4>{EKIPCav z+GnA}QodCqG+b7%<A0a%H{t*D#{X^(95Z49@-B3j85YCF8~8sg{vX|Q%t1*;s1O(1 zmzx`B)c9+>EB*%uBELZ~ySsd={BP4ZKvZ?FAaKT6wb{^o4f2&h9W;01mszm$q(~~` z)_TTlr%3vLT2-iKa>+_><^<-mqZooUsaqjP-ASl$Kq0A+i%^>zS~g*&#$b*(CDj2m zcMxuYUbKClQ?gK?U==m|TV8PT8Me~6<C!hBv*J96mG+L`1{n_f(6*HXo33-BQJhJz z&Tzv~ZVX4<Y-6$UQMCmI`wD$4a_xRd0gvRH*mgz^Yq^f$PT<y+RT}r$FN!kKa$i0> z2Cl~b?EO!>B%J(s*(fW-5pnY?%b?ueX^(jQOiIFW^PjC|KR@O<$s&fovJdru!b($D zANM9?{G4vKkN_4MQ~77SskiqP!1&47^4<&E(klbc*a_Id7L8O);F7nti4s@6<~oq) zU;pr*>wGvdO&%`SSGGr|`9BW6g9~P#;%~VCgFWJn(?xCmAL~HC6hxw~0}!d)sDUT{ zBDh?w;o#1(@ga4h`Pg&gn@;fd@s#;L3kbtJZJR99;reNUvM;4Xf{UnU{NH{*W~}`d z>~o!z23MP{-wM{L<&3Q|w8r<MHn29ENcz`>eS8F=%>Ow`F-9ji;+Yi24!bNZ%yYo) zGSDk!k2$0Q+X}H`(GFUtwG|rL^#`|LQ22V17sdz_D^<}K>fp_=`{@Ba_soex#)eOE zB3vaWN~J%5DP>o@&SXD0I(@<y-U<K5g!`c+{M+(>{2Khv1O@ig3x#EZM|&IpbNbK+ z&gx+hdld!Nc69`IJlyh){9iY@0TU{o;s3H=_t)b8SV8?<h`R0SWcZ)tA^xvW&OF)R z&i`EYG@={+mz$hwYvcb3WNEAsR<x1-2i^vvvD5ztdoWtGa^wFRJmEop^QDR<@Q8Cu zT!X%0c77v+E>W*u=5Q~p$mSQndKG{C{eQaN2VaNzJ-uUe7bv31S%SI*9`t*=kJyr0 zGcY7x--7?+$&cGsgyGXEjJn0EHN4KBbM8)S_%{7+>l{eu96<j`!2lnGd;<)yEO*uU zFVU`>dU)*X-~P=<aoIwz6-|=Q$Tyt--Iw@wni{^q8-(frCVEkS^M8#W{I6!|3+Ge* z$MmtogrhM0^)?JHJotK+|6})m%=(-2KM~$6=eq8C8-U0EEYjz%$N$3}GVKV9^XZ<% z@i*~*00YGTut<sLZScRb*8l+Y@HY7$=6no!c+QhEN&^}>Y+FMn1ywYXHmqU*K6e&@ zUPV8Fi75yM-BoW5HdPhsQbjh5wV{_8ZF@cfa@Cz`?~lf}1r3pV6%4v<16+bE!f1|2 zICD!pSKl%&R*VY{WkKsz)jvzM+F(*wBxX3lNjSrETM4DC)rYemAIN!20P9fwx1Gk> zbDUR<^`vlsM~Qs7YzaH>`S%D}p~Emn$33?=28FnC5e<}OR2vZ+C?=n)=E=I5@-NlQ z3R}NxvPVmf(d7Uk_O}T{TaFQUMO@$iD9?ZR?=Ryg|NJZ5-V5&-B{?99T^QwVrtNwK zQQN%^i&A}%(1R8_fL2P##u8})^s(&?0;RF3GxbZoroxrf7$@{zMTf)4RbzWiWgYov zrWHioCrXfKC|dKphv7qbwfDN@umADWc>kY2&rZhqEWhbh1sXg9*U!^yj1}YmB8Qj) z0`JSTfVMpNi~&}B*Zv<`sSAj_Q0xPt1Xgd_&*0OP#_4li|2fKEvAFynV@*r$PX;y^ z$7o?*J*HP&(eHycWjkiFUgQ5cOhGO%AB#j~8qY7c|3~uQL<xKKX8S(_8?G9`W~6`U z{CaH=e&76`->;u@4qOwb%1}9E=(h1f_`^tAk5iC1w^aZHnx}C}Df@)wl*BZKmEr@= z4A+--XO#o6?05-foMS?pQm_&GvWpZ`p`?k%bIx4#dGWfEycj?*0Ua<afnLNr;{Qut z$L~J>Pu$mBn?CL(k5h?P^Y1s{|AcL}9O#7d|9Tz&*VpmCC+an431RdFGj;x_+Dl-5 zb;)yn0+?6ZN>_{4R_C4n1N>jiZ8P{*lg3V-4T4ws#WVh2bKsg|1ph3FkpHuWCqnkt z$D#0*Wb4v1_XhZ!_H{)Uo#oQ<@R>7hw-}>P#$j(Bu;zdI!58tDKl!LlyQZy4yhfM> zu!(JsF*bhAccadTi6}z;UXUZL`<wE=JUN7P@PDn5iT?#Vq$-$?y|%9=3Bf3=-4Utc z!ew9Q+?Rtd0*zGLE+>pDO<rm4T{W2Y50A0mho8S{JQfc5pmzUH#uVz0_H>9N3LHT9 z;jW)^4a~Ue;{Vvg^Su4N?wde2D|n^=(1uwYbtzk_V6x{#7Dsw%_SyK~1_aJLpW*+; z=TSh#6aMF8(c=Fr9SIage;xjBzs{JXeEgcQ4q)a<`ZxX$VyG;mCnCwmTjGDnx9x2L zfQq-w|6QnDmmWgpz=T1t7-*nQEl^XHkV219+O}mg)uDZ5TjC}<FX2evSMn}RU=$$| zwpZbVG4nLr^A1xF@Oq#_Ro!UfP4+{$vp$1A6>daLZI!0Vz&v`ZY+>^5Smy*VkRwh# z-3&K1pMKA9_-bLDAgh|HSH}{?rLMHI36@q4R<Jly0&yZy;8-&pQ2GyE;RFA|zoD71 z#e1~Q>VMU?)fgY!XV^|Sw+eJ|y{LltUhJDrmQO${Fk{%yd=z+$V9D+}%<ljDw;wY{ zpaZPy4hD$z)CFmy9GSp{;&2K9{)9-VfjNs4jNANL_p=hEmh`$F`eTi>2=w(dkc<t4 z%BI0zl2!K{g==n_f@HM&VZ<%5jgeWeXcSv!?v*twQC5|#kG}XauV??6tHnml{VJS< zZgBX_=2wF+Azf>mXJ&)~Ph65?3*8~E8JbnmZ=0wAaRql~r1cB`Uv@ggPhYB<l}nDR zY$c4O$1aopIVVE^LnC6g^^x*n7}wbX?4z%H-v2A+oxZ&EF#pHTdiU?K_HK7VasmuO zwvPm`C9uSsv28WZ4hDpM*JtO00Z!1`Qd^>ttf`Mh)b#(cAWkaEa~`YA9%{Z?C279L z$(!*U^#Afifm}D)v-(S|hJ~#Z&|hPMhr%{g4&SsF2w2OoA%zJLHf7OBUok}=_LG6+ z)C7?u-?Z0tVU;pSJa3c#7mOZ(_5-Kezh(YsaWT>YzH~Xq&i`}24pZZNTR?h5rcM^E zNt(Fi3ID_YEpeT{<MsTHF`B)%_MQKgv^=`64*blR1OfhD6v@cKn&n#sh9b*io^%F= zY@m!ew}dLK&;h{<L(kp+d#I@`(M5riANibpgmwYg&4QqSNn^nM(ZN_J=eg;<g}Ti< z_<);mC?f0;rbA!N8B0O@XCo`wJL*q=^g(>_>Y@7G=w+r_hHot`wLearR-~G=u;<Hr zYht&FC+auge_qR@Th77AX-zz8|Az0Pv+eP~X>{V!GjrmLn!-}v#!=Q)-sU$mvQ-*( z-*Fu6WfHB&|NQ0qpIt>bJSj?>=Ymv5guekNp8PP`pI8_J#lIQ*K8gQ>Z>OT69w__x z)a^_g6l_zk`Ru-X%DE|boT(3hqr9|Z>9z4chH{O}w*G638~=xPDRca*&5i%tUx<a& z$N7!<A1n&LYhCsg$^3dp<)E*dGmkOlW=A<iF*BpL!T$&NzuqDL3uUmx#&NifLrT1< zgYhm$r*EOm5qeR1usE7p`UE$!z%-IwZ4;t1>pA_T7Mn<du#xP42lE~ab-;jR?p2$m zcDSk$F@Y-YHlsyzTue$TuII-urlQ3mSU@|~|E}0&9<z<i^{^Veh<iP6$P>)%3gQ;t zw>R$$os|J_M4vD(@<I}_em%_;s!uFlUmRn?Or$U*>2Hbcx*zXiUvnhNVu1aHRe{H+ zk}|<zC?R2H6%P^YmHWw~q~0*YFCv``nI;lJ1FO{GC@5xJRp8IR?5&6?S;02cifl@( zQ@e(;ykLXLv4KXo8h^K<Q%E45D9M&5-_xz30BlfHMw3<AfwJXAx1T9MnCW_Fn3qiv zK|)v1xBA42H=z!#a?<}P*JOYVt6;z$>wVzsw(mVA{~vzwDs(s%rX;Bd#MA3SgD>kh zJtH51SNx@d$69uN)_fn@Hr}guEOa&TFmM2<Q$&eztGEz@pHj-~XmJ9hZS&wCj63@^ zb~$s)(Lh(~@t*B%44nTcEYr?BfN!;*#d=q^1#sj4C;gve#Nmd8z~Iyh?CN!k<`&6g zg<gqzjta0!s>EHs-$la4sK)9!UB0ohfP1SA7HZfscwqhOup+2q#NA7y#?NxVY?5As z5!Zm(a>O2F!uierb3g<*1D{A65la9-$VnQQfH|?`+~M8|-oy@$>M5}(#X^vbGScc- zqodOTrn*V)Uz-1m=pC`a-CN~<_#}ON$t?}bcsFdHgTESD{;$;+_%kP?g5ntDYx#d< zDkvWa=-o80<^M8^#|(!5>Hl2^;3_!rPA(8Utd~Be14N<LTE|7N3@X#?f=X-t4_p#k zoOCdnuaV)1bq>&T|A!`Yi15Y#e9Oo-^^#m6tNr^>5d9qgvo0h5AEOM}t^D-5_}tK? z<xLm&lgTq!LT(K1*O9&a>WuIH{fFyNx)hxSLsab+h3S$G=}rj=rTyp@=@gJI>5^vX z?(Px+>F(|Z>8_!>8DN-mxj*7PXYIY7y_PA0Cx&Xx_oI#iZC5RdslL+49N@#nYSX*! zi$n73?qWnZPaTbUX`xKvw<0Gbyu<CnW)B)`-VHgP*iqf9ovo!9kKg}#Xw7%R4fCV3 z&fXfjQ3dnH4_+Q|M0|-rPxZ>TUgc8FE*DT7YH`%M%bm@7w^F(}wBT!djFq4BZ|Rvk zpy+Z7)>?AsVUFAEjs*3#9Bg+ZiWnXett1ThJ(0KEhPD)i)=K?DAbldmw<{Fj4p%Ek z3`q!g7y$-d<i_r)jp-KTEWR)ayim3$4smW`0XS-%pJ2@T`dd!wa*QEdM1F$Cbm8sn zn@t3@jY>M-`xt1HAAZWHu6D7b=0+;0f2|ejdgmE<LujQkrH0+pp{RuJ=!@`^P)4_@ z?BsW)SZ_d-W*U#ImMj<aT=>D7a*1L6KSrce55{=8&}!D*^ob`6t$5Td*$<iJ*c&;T zOn*MwC-DkTS~qJd>0*9p;+TN{nZ4rH8-TIPXK<m&(rM|7e_75U;;vylx(y|kSH2q^ zo+zLV%l1ZC#p96~ClHfD;%}<^$(;Wx3fr4AkkJyPU`gLx)wN8KQm<ay@kaKkX`gHw zmbJR)Hgi|1pOdehbh7_JtLyypXcC1&?}HXQzBTM*c`+4>N7%V<#XFnpl1{0TghAe{ z`iMMX=?JDeDGF@gLU%YtdOsQS1Bd!$h^<N}<F;8}eMxJHPW-!TQQh0sm+zZWl%?5t z%}NeTqPl-}2T-CfhOSad29lB4^8<zlRg}D>K9dEgGu*K19MSoYGET34yBQ`XyA>k1 z1QR9nruFY)YXjwV6!eHPf1_~xqu`H1>u0v5-Rm`$Q&?pCcl1aChJZph$tSky^1E&i zfbyu`M=ECbkqKRRuYg8mY1dULHG0dxWWX|pfH4VDM_Pr20MmrrFw=6?FU(rGu=9(M zy}#V_<N18+w28e6YcEzs0{TOIL2N%6g|`J;;ZuJ3bdkdzng6-Sbi4rFpMkm5Yydn7 zHN@EdH_wfX9mCwDJ8=3WnxGQ^=(1}DZlBA?X*R<(`}RJ8BHvgbyQDYgPhPNY>m;Xx zrjcG_U?ZPn?uQ^BxEF!U(DVTl0EgPy+_9jvyDok#Q^Gw4QXIAcA1vV31jJtWy0g6c z9ry-NK@m8EVt3QOk+1az)F1UVg{Lh|y@~?IvQIG7Jk%yr##gopWdeI+j4l6=rtYVm z5d(1A<j_@j_Kgi>i~Bci6}7?k)Za|wMo${=eLXl2(_21h4Jy5C9iLW*W09K06X^Nw z>zPq#;i;9tXzH2;hhX{rWuw-^C2#?5MEu;0lzHgZ0E7_}B$4>Oe%S|I!?YlKxDHUm zw@Cekt8P)H@?byFw8A~Z#lgNJ_1nL@Yv{|bq~>v>s$K2()t|n#u+2ca14EK46A7Ly zV4_*Tny#}MwDGOz?C?$bbzneHj_<J`pz*n%&|AB=LEZiev<%%m<+G?QyK*0)S)0}2 zush(;3@=9<I>QukyOim{jS*3w6)jJ3&7S{31k*z5=Vtg&<vo&h^whe#XsIIS-Kl6b zng5>x`c$UI_dAmS1o%v6)Vk5uuVZ2)%Do|P>l{>$4(*&BWiu;tn!cV75|^7zkM_+; znxdaPJ9Mfs_YMurD=R^fiM>Ol>lPOBYGtg8H{TqG2q%qvn5DL9Rv)e95ns*5bkx1# zu{cYs1kcXI*niDm3$%pYVhS>JG&Hv~(3bdTI0}X#8#mdI_`>M~Nwca{KsJ@%<i+qC zDIYEhnc~JAd1`Q~|0LrH?qH&om^f(Q?q`CGm|2~C^Lr)hBXyAVxD~)P-%=ZDY*6l! zPApfHO=Ps%+AG0aEFG#MWPYkYX$UK(j!W`omzE2+rmmB>&fjN&%3(Ef!wPN;f$co+ zUViVe8`|Nn3lMx-XZT}5kjqh6-G6+8_X3LrioAL%dPZ=UT$?srVH-suIF(8Za&G-b z;UcXyV6-)|=0JTPVlY4+faMlXHBVI?Vy;?6;^+<cn&;xhuz>SJ%kODs+C2wDg}d|q z7hc7KWYY~unJYb9Ws)Uy3j76=@i>}FQe)OqC{;c|aI;eBfCi7QPWbbdee1s49#S-2 z!71QzPi>~ht9H8Z*(BAJ#0>dd`M-(@$G*0LgnRs-A3j8IvHQF2<hlwjUVR9|>N)i# z$3(aj&>k&pll<?Y(^dM{R-vdN=|}*ziW93NIjX@kv>fDh<X$fN<L>%vkZbg#!8MSn z(!~f<{_scB9UJIJ=uQQYgH6FmNpeha7r^wDXv12^)CyXXMC~R44CfYT{O|E!_ZucC z%KmR``w@@TwgIp~il^^-*(b*1r2Z&8>Y*8R?jj+NxNg#?F_Q~nOCN|ciUF)$-kw75 z_~P$j-X{8$h{WoK)hRxmXC@`e!Mm)hq?+REE$emxW|9}VDtD{kM4@em^rpnQdTHYL z4;z;!_uR=5qR%^`hrkPPk9r+;*;$Iwfk($&j8k8mO`>kG@ll|#G$5A2meL!!-N#MH zupht!zcspWp6wCo<egnN?&nsK4{6ykzvdisndT=*a75QuR}1;$58C|nnHu}VEtGz2 zHlhg+0t4FKhQ6MQqR_`Q6S5rMfnn9pbz_45=^-HpLt~vX>WrAOP|@pI{zHHmq1L%# z=GBsw0qtYFrd60xmriy%LxfQ_5`EiE5b7;ZX1NRG*IU{50|WpLmvl!gATI^asqYjq zMGGPTKB2Z*QDyvEhx2b*%7w_lN@Dznd-Mqmx;S8$@R>0kk}zfqifl)LaHIJRS@%L{ zd2x3n?lA#_R>;=A^^&{jr}X_&a{|KFB#$|{21RkLrIP?n`9D>VzrnVAQed#`K2cKE zA2*rYVr7|+>9MD72ZLFavypx%b>U|rBx7Z_=pn?TIZBCeG=ih~ie57xha){>*`>EP zL|B!OZL8n~#6I#$$@4C6x7$o{T4^R`czwUgvy9-twVq5CeqHU#uZV8zeiqigq2|#E zz}So#dr<x6U^`%n__|=5HG4c5T$-xX3B8F1pg4K?Vt-qLO)#sBX00h9?dM*B%1dr* z!cP5ZL|!Aq48QMPUw&Jl@Fyp>88h4~77aK-UOQ$RO-S}H)EJ6Lx!bEm2nSx%OsP%k zrpKA21Z@wMChl)iD2N{3uZ~>8ApMY|dd_;Cvym~LtVC*6&xkV)`A6Tf+Yzej9`&AI z0(CN97N~XZSyb!honi)+^M<w**i{m^vD$IIT!W%y^i^ESuI@R{0(<HM^o`Y}^`wx1 z>8?WzBr2-k>(x}}VD2GL=v>z)ST%omQML}Ox{dLS6ej5+5QK}%gU7^P_b0p6MJZFr zH!Y4^Lv+nHof+U?`+}7bOv|ofh5QFa%BlNo_&mEd41Xr!c=>REL!Xr7=JGGDN8)oe z*~NPdw|j&6KP|H8wxIEaZfEy_?h+r}U6ZPJW~rzrHg36dXGV<;vpq!OqgVfAs0#h= zbv$7U+C<f<DZ|1$01a3IgfG^C2t!?*BHmceddh41A>pMncMAhBfk#{%N|N_180zj% zZct^AfL&A@1f<o{tTX<UcN^+?-%|gP-SZxeEiK3Ao=b$}PEJHP`=R19w-Iu#+(b@$ zKhzMq^@9_jJ{}&5(s*};-G#Rcy1<|Zm;((rECuzKV>!kw8zrl()`Y7V%5iuGQXLNb zNQRxbs?gJVceBCqm(Gr9CBD#=a>zIcVuc0c0@4t{xo{%M)g7rs_d)8^6x)`TjeunJ zRya`IJqR?8`gBY?=5JBp8k?i+WX)?e4yv8Rw1{_bi?%a3U-+|-zH{&Qs}Y)>R{cV5 z+Yo>7{8A099X|aV*A6mw!UhnH(L)FMHN`otu08-+&$@%Dv+F?KJ|M+D3CU@vQGJl3 zEE>fy=K)jba>4SPSYV>dh6QcLFo*K}-<OGM`~4+%<ofOJD7GKJ!$e~z<6bb!*>`qr zo`3^jA7WgW{aIvl0O-RUTCmQT(c1&&Tgao<G&V(mYMDe9#y@g3kgtAZm;zx&5xu<r zyZM2C{u3JZmjQm8##faRsp<)cYWl;pv78pDTHZ~(8>)o&V(bR6%!7~7y5s)UVacmm zOjcEhC9MBQFqKOterM~*Maz<mg^beNHR7M9`1C!#U+SN{m|U-*xU($ePbBWvz1|rU z))qfkV5U&7phYKYPDNLF-eufhm^3rv#s-EI>)8`OO8Vb26FZXSjxkyE&)vL3lBSF; z4~<LIR8Xc3BG$y3Y3{LbZn-a6%$?p6BTskoV_U$}Aq$Gvei8!r(cg1k-Zp#0l6Y$- z^8DNST9W{yH!~q5`?I{6Vi{X#T6>|=(YTYWB^KCVllVR6aXpp(271<k+4pxi7&n`- z&6m9)?-*RWbH$<Cx7zoe)V?GFh@Zyd#rmXy{Rr=qXaArW;oghl6at$MhzXn6W9R{N zv%k>0%UeIojo}I0fxe-k-{O&@*Bs5mwjN6jvm|FyTLa(!u=)w4MV&O6^#^UeOHy>o za1JB$L<tO=Sbi1dy~nPZR-cx77}gGmjzH<U9SGXPUK;%uP({MNmi*%iI5`LAQMLHe z05F3If~JEz_PaY1z?hCG7)jHrI53Xrok^<}(cXtoK0c}Hm8aE>H!&btPoO}-HZiQU zB;p%W#()56bh;hke*1xbqTOJ0t6G$~RCQ(r-+AjHjl@H;eUc9wbnE+8?tVn`7{Ck7 zv!(BB@jqQym;w@n{(ebUQBY#D6QdjRf8>Q4a(s0n^)I69iUpFA#NtV(uYl!fi0CjD zK85E@m>j#uM;+Moxl|YaR8YX{(_<aoMm-dCKkD+`w%z#*n#V(*4eweZb#M;_HNSf} z@S);81#w5nju%anp{1gFq9*r<b-$}*`O;MVox)KOkTyu&%@KiC?trI3&+@)+&NA|q zNzS{IgLJa-xm^^if7)E;5KGTRmM_$pQ=Y*=Z+j!)<It+)Zf1c}whH5>s3uGIeGrOg z8z_vgxRN)FoDg31DnRE+hIN+XT$YT%Z_sF~@fCEi4UOZt<u$;kCfh73F#9JCjYINk zB4;RuNL;-A<d*1ybl@|Or+wVx<8yY!1N?>jVu7VbZ4|#EsOuw8*r~G2|F&;K5Ozyt z%SQu*V8pVd7xQl(Cm9UY0-HukWm7XcWpm=qBEAL<w(kJ7<i`=I4-Ze&#cnNy2=W}| zR2(*y`fn=*nS2JJfhKrvOj@kZ{z?M>eU(u&ql_nm$1bwC|2mekU<F4Ehkt~n+V6N! z%%I~5C$(Lc-XwtH_2to+kTTmOKMmF02Fr?@<pUYI4C9w_HMcc3a~@o#s_eLI%*EjC zW>E9MpQ+DK)0&W#O>GHpx$-dstdQEt+Vm`Y+Dx=++5R#MCj+yQL^YwS)=D%rOu0n0 zXPU*laK^Upqu0@(O2zWR=SE>WR=83>Ok*C?w2(wbExL9vjwrwidY<M?lb<f@-Md(Q z4x$R=3nH2y@!hdOBp=p99xNk1FISbB?L$RUXRNVhTi(QQtiNx06=P;cNsduw<z0$r zy1V3KATS?@(xbrDnrQ`=nKAgNiD|)G(CxgN9oiQLG2}+UTZ<2pG==cUh=zN0f7F4c zbI#{2tLi8%Q@k6=5g!cpA8@;k8yC7F{!>IeKi9Bz4x<~RY;4P=Pw<lgaBGWwR#OQb z36~9qZe3o1*VFOrpPZ9Q;BfPmYY`r(R_S9R-Tjv{2R&*Kuk2Qq3n02Yn*%)UY$%@p zlcKu;^&e}AAy5h5?X$rRTRI;Wq+}0-=22j^UFD@_BA2&u=wNbp$i_Pfeviy63@G-t zY8RAz06)veGE4iuJPxuX)ac)UKBPi^vdd|=jEggWrY}V&5JRow&5lG9gBo5hK2b02 zXCz7DQg5LH>hHJWfe+k|pmQDFy7S$e(w%QmW!pYRebTwBJTT~J?~6-649jA9e_>En zi+rCjGQj->B%!*(*LDp|K^4T0fw$#}$Z0nB^p<*82vJqCh#al{Ta&z%@JED=!9H$i z9Hk)}JSq^D7?eP(J;&w1ohdY=e^mCZRZCC^si<J-mHY3lCUjOxjT`AMBgfwFdcPbB zriRTmnz9*`HRz%wCF@6{ORw9ukWmt@m%QxY4&bkkD!&O>OBRMgtD~%2_*cWylvl2= z-#YYavvdc={?XE3@^x6Z^Ihgm<`wWGU7Q1g?QjT3WG5vq?U%sW-2F*+l7Fy`CyCME zz{ZD;sU5&IBk>bNAKkqI_XW2;$6*%~7c~hSqrv6Ac>bU&Ih%g|jq({|c_DxL!w&cW zIBQt@sq)VB<J{i!(G-B!b+oMRRT~WU#16Y^gl<988*Fe?MuiQ>fmxTA(e_<{a$0rV z#~n&t`HE>ycT&@dfu_4!+;fVb$BiNtCen^%Z!bZ$FP|SD%^_YRs*W|&DHFk-xHg*{ zsZ>kaJ7SeI{2)w3Yy2?4rH?;57K%6~;aOpk%_p-}w{NPmY|U(3_g&}EudM>ML)33` zZ-Y!u$xbK|MDgq(qesJXCsY(_yrpG_>j7`O^b%sZNuI$7ulJojCXEknBFoI2YRC18 zo(i8~!GedHDPj0ONO0^2*~M7bx|=Db2n2WvC(WIN^hPK-gwq6?)ZL98`r+-=B)s9O zE!sAq(1*6+CuqqSGh)Tt?!FZ-E78iGV2Zj@>3I((f!kW7wx~UB13aFWphQW&(MFUP z!*E+w5!mkqAc}~kjMWtVEB@T!d8DA%xV)hk>aNc`r<(X!1rx;y`g~@L_SO|gF8dv5 zzYNyA7X6*`nwJZ>epIw!l2Qv*V+J_=VVu-nv((*@H~oxP)%4~Q;IXs-r~Y3crfsY( z=Q!o+V<GfKdPo^^N@Z*GD`O(Zz_4wv-XwliYoIIP`>H;%2`Dub&Y^@vJWL0VLQvT$ znT_BBIT~m7?$2ism||aTrxrCqqNr7i*Ev!A7Pt@fH*rqKe7B&Wh;MD06^N*jj3HB` zC(RmxsFP(9kZlpYjhKA74#ZIN1Im}bUk!Vu*8Nx>Q<>g}p=KRA1mWRGnJE;sc5A}o z$bmeE1mLhf2fzy_`d6^#I|djtN-whuhUEDYz=oAF^RF4IF9KN(UYTHrVEfOfmT%!< zAg=2_K#A?1Ep-4;PrQwOkZ}<(1bBjh+BX)jmdM$wVh`}>1EgVL`ei1U|0Q@>>A$%# z^IshU;B?ggh~>RQF{O{mv%@)WokX|vg3cq}OX_`UzzzIY^p!oExJ8c+T+;2M3%bV1 zLt4y_A|X(}!%vt2Li8F8wn@K*CE{<>{4h3&yS$?ZaC&VNoYEc3dWadu33K6Gw4LB- zQ2I&ShvOE81gVwG-8~+GiI~&E!$Eu%l-o3Thd`YJLcE2gN6^F5&r7t+8gSpmD#Y+1 z$KM>IGvSw=KSp`3Rc}Dt$-E=_N6#?;Z|!&cPFFp~ulJ9HE=G<Bp54@VnQ#rWKPJ-> z$;{XQV&pcUzQ%m)D+B~?eCoaiB4p!j6XNXZ<$faFy@`fxK#eOI&z(nJ{{9<u>-L-} z0qu!mlL(++FK<2JxD_Q6@{k_(JQuP=mh`I28`1bNNwd8tnupEK|HXI<2_i4W_<0wh z{Dh|^y!5{w9nelE)rrKAUpb*T!t*=vV9e?h`9NF-3>0o3PKKgYb!*%38=|s!?uq19 z^XOQ4F6#~@jTwQi3&v)GnxqnI@S)iD4Cq>fhm&Icz;)y(W!~h%t5d#tqnhB%Cc{?J zf!U<5b5$;bDuw18g9#UHAi6XGjiyw}&$<^ZfTKRLO!$}MCxoJQxlLqam2jHZ>rtD; z6!)(FfNL;34|-dDVoB|ztKnE^f~IPsL%=d*!6s>|*c7F{3-zv^fo)7h+<f<o;`}Oz z>;KayL(hr;iD3TnfS=9|yEQ{{Y0U%S66vGn2|SOy@r>A-&_=zYs_XH;<L8$nqoaP# zuCmh<xj%D3`TcM>x4d4)(}SV(I@7FhdRSiO(f`55ww<)@s>jd1gqK+-J0CPbvT3wM zlW^(rgh~u2O+zz<t!e&t3(0g0!TtBRa_>Vj^WB)?V7qG{P;ED};&Ptzd$paZrjyWo zPJ)U4S`pc2Ln!a}>>!6Fc11Q^J#!)0D3W&<eE<Ff%|dU*?GfaI(&se3+rR|z38hwy zTbUEv{BE}-|J#}i`~`k!KfS~S1#9hTBOYU)#+#hFdEoP8?j0q;cOs^TR;~ix?u|(X zW-i7r%CSPA=8`@?Du#?qku4EGUFH4fP6Jq8>p+r|9|0^3)>6+psM|QHCqHNdp@z<w z|4kINV>aT<Sbk~chsANh9zCZ-vHRj9S6kmstv2n3A;6&OvJ37WwR1HT7y^1JcqUD> z1ABu;eoO;Q<5^TCTC;TIV+1?fLO$s|-~-UEV360DbDPN7uHdwu|E3D&K1qtoelMMC z@_*hNkE}yzCi*i#<+kI0Kc3#uW}UoluTnEYIc@<T^5Hgi{4M#(`(%KicJxi}bzB1n z-qB7&)v3@a)j?R0)K?wKAnmV}ig(4A+xHYU9aF$s&Dpu+5tT!bxN0yDjzv1P?=`F6 zbMMzLs;zm-bCN+vY55QxeJu}W=^V8mcGF-S8yfO-v#vMq@<4U|^61F6wugCq6MLro z;Y-6za2_|uzE8631r!hh4EyYq9YjsnPVYw#fOeflgncJlbX)Dm3VVspysc~i)%o$m z$>Av^&ni!$DO@q4eT(X?Qu|Gif<aw)$2#0y-%;$#leq~dwNUB*s!B0Z(beF%xKy-B zqtwxr2RoW1vIMSm`CiB#|7`k0Nxx1?P(_|k-P@Yc8gHt60Ch3LPYOTfv0lLl;+uAy zKP6iAj9GjxwD|gK6z?8tGxRCn%dX)~ZNXp%G7&c6!?}n#L_g4XX_>>!$FK;af7iWA z{s?0b121n43irv9+VQ!B7LFd(()}H|d;Ghofvg^eSi5S!R+t{{TB9*_GdRX#o-jJ_ z$I(NXrEV`j#g<#3Aeyxsh??#fYptZllONRBPK3Wex<mA$4Z^=_HqeURIQ-^2_v?7p zUkj5&EW7=B%o`=Xv(iysCR~SKkJ#!S9r|bA;r%$uHDeIBX9gy?*E+2ZGjmFMee3lF z=jQjc;l_*)#>2NVa+0tqdlJxBY@dVl8;}C_Abh)twN>=4h5B7c5;97KJ$mrFIKv8p zQk$!h<R9cO2+11&F=nnpnM5z5T!LL;z{R5?%8(0O(idV*nsm#Agv#ITLBhu;T#m<I zl^htuM%DrGEehgeX%{}se&%JO9=m@ZppjUJ|28K9RjK?gOy%*r89&oia-L|5jMEc! zvz0da-vG&4T%;bZQ)3(glXkB}ztNLF+)xDJedTm=O)PNQ6-ezR{_h}N>*$SVJDdX! zDSV;t1$5i34w1!b){D2WfYVzvg}d`xkVrr9bb1SX472-%#qh>zKFJPr(5d{+9a6Hl z;{bzb2I~GvMtmzY;*Rir;5#e?OwHIxT!Aeu_BY#L7liS50H}2=iUv#*Euem{wfvPG z3Ba-TkDZ9#pF^6X21q)sGyrD|CGWdDZY2WD&5Z0%7OX1-!xDMNkiYJxwDcpe-@_9$ ze>Pj^PxdVTv%FB*_K6!d&q%BOvHwWbTI0F&EpE=P3Bjy-0m5@Og;vdSA>9U<gn$fW zC%$(l0FwY4--UNoH}cmYNpZbuzVy%I?>}bE#R6IGs~le18Y7>dMZezpr0_j;dxahT zI_YGRtm=(6mNZ~_19yY2-R*e7Iq(ApNbNdOfgNF1GCJ5*g|4UvqeaMnA_80Q7vFhg znVf~gKkfs$4{q%@fKanQ$tx%=2AbD3qY)(-jcFxmQ=v0)wqZ(+VnQ{24cI-u2K4^p z-viP;kn2e<Vm!Q|4%dM2K0tq*?O95IO{N*)N{c&*fo!9c&*zj7<3g;#5+1LHqhya# zhVDGm<+W!QLVh6PZ=!oLf~O^It4YjHLf;g*X1fSwbudsQs}<aYxfjL5n=(XYfAbF! zji#beuU_RS5p2L_>h7Zm?A3W3&EIGyzg_UQ9Q)T+m*$n~iP*K!t#$c44X^21gTTcy z^*0%z&#dfH`d??Sz(Zbv3n3DIF_F6NWf?7eZGcZ9T|yBobXDGG4;P)UUF{pfn5H06 zlqvhl?b8|v`lTCEHZ7*@P}H3%S5VA)snQvcFl~T}`p~oM^Y-&IvM(N_QA#-T7aL4J zM+uzk_9=bn`jx4W+O{(9lS2Ug&L{Gv7-Tsipm;u`_vZ<i20c030i2l8!Q_ECN@hfm zb&%sU%}%ryT&U&&)eV(Lk`!sVgyJBE^vjML-WgJpEV*sq!M02r7=~uV>=By4g?5m4 zZMs@*uLE-LjiovWL^cDrWdVk-H*Q8wWmbe}QSLw-IrbQ4;9ESe`iSy-;QP>m1Ct67 zV+<zmLh^)c=pD$2X0(w6?pQOV2AK*fP5Yk>wt06L-9zK~@$-Ftx`NL`aK2_l^`zgQ zcV7{L279DvW+?f?7i?rQ3Ov~j@~R&}-KfYDPBra7lvrN3s6ALWO?18Vh4My}(<G4K z{{7R|Atbo0>&=Vi$9M7ao4y5N>0Vu`+!q_)QpI2~fHt51z{_V?Nw-O34#cr__yF)G z<7>xU(<-R`{Y(@=S}=?#^nv2W26kxs9ja`w;|UYZz3Tq>3AUA_Tu65YKcoa*ojGI` zes2wxk|5q@jQt~65-Hz#j6eY|-sBe*A?gm&2M!4u2ih~-@^@|D5!y&B72X?m08Smi z__VgLo>M5y!HFy?_l{se@8~D@>jR_X*R1KKx3emUXbdozQGaFJL0pT>IM;gth$N%M z)z1!9PF_yavc7K71Yb`*Yd@4P2+{dMUxe=8l1QL)EFu^L#t`v|#As76aG_F_C~^?p zE0soOV*LIKQ@1Ap5Z9cy2MD(k4NHjjLtZ{AZ}u*IDP4Ls47**2oBu`13=x&XnqbB! zTK}Qq=o#lX3_)fpE*VOAbNO|4v>@I?NqKJ{s+%5!00QV%kY_n`Ywn418*s@K{w&^o z^w7g)a{Cw2m?ovYnjE)*3(d6&A(4=mjhDS>=^IIivu5~m6wbwG>;-ls#YWL)27STI z9nY^EbeBV*w2f~GRD^>v&uZ*LxU~4}&<(RK{vxIJ5SHL}=hy}8?CQ*s#rle}1H@l! zxJZ!H`Xo8c`qTVb`u73fg4=(_T_a50D~^+L>qV=CuYMn~E(=O|PY<}_C)ZLtIzJ0p z_eE!(cs2Ew(Msu|*)Myo?1aHFEouB+4HaBSI`@!vn4nm<pyxEqxwaJTWF54Fr+jWA zlQ;RmtBjj%0X-8%HR)sB@40&7|HxGtDRs<n?Emj9-HRrA*LV$@6uw<d8|!3S^J=r& z^*il2`$@=`XS!r2Kpv61sW2p(5jYA1lE2)o=4$@dkR*DHI2vc@IPh~m#TZ&NXpd6r zEL71v{EVW}oY92EDv(NVvR)AL{v&w><{(nv?N+NjdD>HV%p3;hYkyELS+$p$Tvy3H zmZVh0fA*hkV4Tro8$bdJAqU&a*Md<3m}q(IvR`(Ne%u#xYSc)9FAyAcL4IdDZKYj$ zbb*1AR}@VCpTpMrdBR-EGg=i7dn`=f{K*gI+8ji!Db&$SB1RMJn>-MMSN)28Zvpym zNOQ`J<a{?M#3H2H3-$x8b4Mu8u-m?ya(mFrbLA~CS^WljEIEV#rT|;$+c>5UTiQMs zdH&`w!csX93lOu+LqV<F5MW!0JNFNmK2vK5i25R|A4k;r0CF)^EE>JjGRyekztRto z67(A-zGV=U)RvK~mZ2r_F^y42^jnNI^;;RdU;@lft$eV&cQ)Wo*h{HyhExTEdgQ+d z0p+AkzghkFAyI8qJO5vrxV$kn1*n5#eOz?nmYI4o|8)3)-{Atu4Np%rUAX|Iv(xke zaPDJANdB=%>!5M2$QJptwKM;hw-}vaLJG&pk(dLg{wdES=@Fos_6yV8Q&1kLi7Lg8 zzT*iLYP;bo0L=?k7n$g?djwg@Cr;POW1Niqi^%`aSCw|v;dgZFkuvu+px6WJmBe(} zy|J+m`!*_v`x}c0hwxPUtJl42p&xBc)Qs-;qeCMK;%ExiD-_PrJAvn#k!A7c4*<@` z_n!VNiQOW=^uBVWIsl9UOZ6OAZ$bIiiIjAj%wwhwnn~e)GUpkQvdp>ONZ8UD+=#)C zA(fUOlFu#54|hK$*RCR!K-n+QflSRV{tswmC{yo@^t+z+QW(uMq<byp)ELqZcKcC@ zT@|f_ko8uoI}*KFSmb9@@yuyPy6i>yA=dewd!0N_nDjWEOP~CYNrg6C|8wu$w8^sY zZqJsu#82adU$6a7p2!>Yi?m+%U@}O;aXCGz!D3KT8eU6i2^lWAsJ%|C_lX_O+z74b z>?a*-vHp-sv-B}a?vs!0ndmEI4!WRyC`27&z=-55#f_ndBi*cZWtTD8o`-bSjhr%X zr@M}O3p?{!BpsTKD!(x|%rHh%fwWJ|4)vV~8pln)whGSxh~ObT)UkZqv-MCRDbB&G zL3z*4n@cb!RW$pmg}_Wec;z64CWGyo+*jvfyF9h5vQrK%<Uy`axD5vVM>em35h+F< zC4qh8vGz|q_Ey$87X|y5{jw>fboPmqCuQJ@bYZqb&~zk;$G@fSi0ydq2Nbi8O`%Fc zPmWw;i7^YXYzqxjWkIUqtLs7Qp7CTX)oS)#T7IEp|KZY5xgXkv$C`X#L>=AWzgD2x zKC|;z_>@*vp+_e!DEWHgf)lMcdR(C|xO)2q@z&UUBa*128nZL6VJj#F;Kxzz61M!4 zr$#g#bDxp#AAq=@Pr;fS{l|*X<?_wYBN>bIuyB(FKc(%02o&d=!60mNjPz*;-~yfg z5D!=uL_83!Ml4ttT!UOHm#9;r%2%RP$MlOO)LycHsPah4VO0w-o%7l+VXT#*CWM!V z^y2m=CxP{oi1<>!@s20aq^6+rPX}-Svy)m_I<3y|A~*)%rz8VTYb5onf{_9_M=A<B z8)_2GcV*u_SfC2eR5X0GIYvcFIpvUAQu6oDvW05X=ioEx+K$9Vg*3;?XUj!CLqnlB zGav6o#8WVv(o0{s-l&>if;_O5S8CH(%9u-_ux+lZ`-wla6ggvd==<i~tDz6JH{2r` zRQ3HI%8CKSu0Dc?p%k<GaAp+-uO@d4#i~m}AngOahofOeVQ-Xk6#jOB7_Fg+c5_bB zi7fYQ=ybSmC>;|F=NW)JPt1X`fnnt=;P7ha0C1^+WTsVlUR%JFufRF_EpU%EH=?gw z*IwXE_g>&yxChwLO6O+=hq$X*^vGUHsQkJN7n^ThKF?ufb}YP{izTEFdFj|GSe(H| zqdQ$4`uh^M?vKbKY-%a}=M46j?-D-Rj6$np8H^lwxj?Z+M3L(Q))h_s5dX<D2j2c& z8P^e&s!~tU2$RR`Ma9RjHL7kR^6Y;jHIkdrgdnR?-y9yca$hvuzKvN0$)mow_8^c} zi6SPeb5Qu75f#<NdQKC<<FonPb&~z(gt{D!-HyJ9EXz`N&aF>(4ig34v%)U&x~vf7 z2j2@2avn+sBm6j{{aMT{5t;Xxhg2kQ83c)@h*V1vFUY|(<p*q&lTv*K4dj1q@vMVo z>IhiAP~JWyJgM|#qwYM1h5U=_hOol!YT+D3YE&93u{YLMnFQ8!SN>)ElE0`<r%+q3 zRB!bn{-~lU+faYD{Uzm!8F9ksS|J2^lv+S5OYB2iFou$$v`ItTwrcg3sZK(}tEX0z z)Id1Vd0Re0`&#z)azRJX9zpA6^51rYvojs6Wl}0(ANwCIGHCoB5)#ajJ`1B*zk_oP zoMpwH^Z3Ya0Wq_H?Jis*LavM|u{`t*LhkdS-0V!#N<&-T)+hQhd*pk!0ZEdnv=UuE z;|&p=|Lma2%GD%PCRu`el!IhDr=VO1*NZfOnp~ymM;5~(d_^ZBjCeY~a;)^Qgb*l% z;*4i56mR(hlW1$ziO@WU1K=~P+b>p0pLwN}^460l1wswW;)j*oypMYu;NSseK|F`` zZ~8)+?^6Lb`{nN`@8=RhzOC2Y#0u+xHV)s%g9+Og+$qN4X@UQyc0hygvxfvD9Pn>* zZ@P;%di}VL9hA`MDU)zHBtMlpmDFh}kbGm&nmw`fxg5@$A2#%fSkSOFmSvmN_??O= z>%<$=@>Ze+;e%2u?_vv19BH9<mh3gU4aBBxXzgD0Oakxn{)2~`YB!mkYtt=7_GSkt zTF$<|8XjWBNH}8%`h`l0QhmDZxE4L5m4S`a>CBUhNcGe_i$*l)r^Gc4riML%a9<r` zk)HB;yy5@{7_)bP3Nx`H#Y0xR4jW(g>~3z4uX^vpCn$9mS)c==)n7Le<MN|P;4tf{ zI~s7JMuEOvJq?s>tqE*W0@|23wUcir_d|2C#drp1h|Bg$`vncsymEI2@Z4=(7sAR$ zeb*EDF)DMLhAl}y<yhn{4nDP`u)j*}v`nLUIn*O&?_d03h^?NklVT9Qt!m}RYmu}| zmZVbNV?O-Rca3!53>KtJ*Mlzp5(b%xnJyoS(Ok7Q=0h@Rii&q=Twb%=EYZNv7%AfT z*=2$4?9YcDZSSnjkpG;Bk{qH7U$<Us_-QUDiDSx)yo-3EIj+x<48W?l+LX=u_w%Ec zp!)7ZE$_A|a(+-Gq}<S__(}K^Q`MlTP8}`$NlBYgzPIL?#An$jH)-nn-}8~{D=+k5 z(*KfQed`HD4$UN2pb|jUwwKyd_X<v%B9Exe-)TtBgHzybJ~^IRo(3>13b|Z{OO(rP zb&>89R}6a;3QE64Cmpqy86HI1D5{6KrO#msHn0E0(BN7#4V<AXV9vKg2r<r~a(JlL z&)H>rkx3<)iswQY&8&6NoX7<QZPV{g4L1j))$4XDiP!fz5|mSC0g^+>y~}DtTpF$t zyS~)R=sS6dOxwV|k~&(;#asG<5we30Wlww4zxD`HuTOmsmryPt=9c?Q7tzmdJ~d1% zM?#qBqQ%QXc2?_6QeS^WzbK2KGYwVIJ=6ww;fcAmsCwhZ>+XQVUoJ6i6_m*Jxocpq z9-D70jNc|cFxRIVWiCE{z`hPm0|N}mQp|?@6|eVCK>F^nLmz;1nB=*4{$^x>l?J;w zQ>s0S%r%D9VR47h?xEK?k3;sLg|#<o+36qPJFk8=Og+|EbE0#bBdRdchkv8{-gb|e z#r8eD?V)=qgV$RVv^Wc2;ro2(JT_eXqK&rXjv-~-T^_vHsz>Uu#9;H(L^cg#m5gX4 z#AihGxtDWk3)O_~P{pKuO=1IfF%|lzUoTyfuWhcozwqTAf(XAu7HO^lbeCB%3<uk& zP5F$2>aQp*pF9lpkkXbd3;$w@9N?ISakZbP0(-L5!vY>+4&2bZ>Nny>HXir}g76Y~ zR0aBh;@>ck`>W3h3XgF~#bxKMJ;2j<7-!==?Q<*vqszzTv9?lo2*r8=&rbeeu^sY2 zWO5UIv!El>9nkWsC^Md5lCN_q+9(hX?;{DTb{GWafBT6VG&^JfMCV(>->(-T)Vw2K zyB^TQrb1v8$GS}UuzK{lNcEo-;^H8SQO7UU+417Ti~qgkJWbylqd$0lnto}?WG)6x zSz}d57icDw=pj~9uZ{KfSkW($vWEy#=R_0aUZ;I(y%EHYrz1xddC-)hqt(0`5u!#J z(|o7<N&%;}YSCzb3u+Wbj?V=X%2wcz5=ZZD|J*n`F@h2-UpTnW)~@ZSkfiroz92MR zWVNW3xBWd0&9{_JM_VfL{DSwg?07*(pYkR4@y&7}y##Kw^%nlijaMp4cyFMy?)d6V zrz8ZY8`Nd1_CThO8tKa>?<`CmF7(NFLF*t8wA4gN*oD@s1h}EH<bR9n-1jrl#$44S zS8$6NmAaM2%a?3qn}%}|f!!~~X+rY+uJ##9_mjUA)fz{+GPcV)HtP8MwXD29!W%*s zR-JKsZN~8;+dAo~w;|zPCnQCg{5R3|ne{cr;4-tc@}Q6CujzcsK;45oj(_L!PPYS0 z*;)C$OWZdrS23GWXx^bS=D%3+UE$|@9uJB!b8)ep17Ynfk6{|?T;GoqlLJi_Sz&n& zY8L?MD#}P@J0B35x%VYUop0jXr^vDh`U7u<T=KSw>68|cZ}ukOst}H4glU?<QO@9j z1+$F({NkAX(GSn(KtswNtrE?RNrEd%9}N<)vPKLlxu(5X<MbJZcDKb;dOrxO=bVDb zq11bzD%Sm2(c4=-NH?Nc6N2|r^cZXecxs_}=L*vSdmap(vWid{UIO96>8@3^`&`DP zz$0}f-FL2u=ixh|&-1<I1F0tEBDoep;hOSd{q=aPed=2R8hE7(F^5wC{q(@pJ6{xw zL6XCNoK<HEu^9@uxK{MWY$t!@nmI6Y#l>DoVH)U6j3bbhFwRrXO+cd@KKa%BC+bYW zHJ^a5F}9QA<LM4)s#EsS3ubsIc3)`@_Du!;%R!7_B<Kpp-~&qL6(r(Go#L$lIkQv5 zj^F**mgWvw@>C>CrkH7O8+@bD^8ki5i<!~qGnsIYzRq3~aof@P1K@k`g~WS{+nN_3 z6k+#;5ms^y@&Z@%^w{ZI<sX^0FP`V)+*VRhI|!R+eZX?jK3HxEaXSE7L>O!bep|=g zJKk<Fen0a;lKmTh-vj|3`qOXTatB^Ehi<AM0NG6K)M*cK7o%FKPV-LP^g0<s*LqOU zq2JBQdv7{V#gR_7*zL%z5;{Ufm5WFe7grv0g;~fy%Z9hOmg8CQZ_AJx@=+|6*G;X7 z59wR9Mx<Hh+@_p^b7UFGw+l?Am^dW50|S<EYl$Q~Md-Gbeda<(nk>zoO_vl%VRQR% zxHIp&D53EM<`(M#vq?-rn$Sm;zH&K(X*#SQ&LY;*0mlJsA21Vt!dZPCQY)u>^<h8> zxr|);vgz{Q9~iYCbQEDMWt2>5D7DD=2YO&}VpjSZGf(km&-8-Zpvd2&e7c2S$mMHy z6Mw6cqcKdKdi41>a~aE2btDS*)mYm1(o+Js=xGwKMJAuFa48M-A;0WwpciqX2rE(H zcipK3T7mNeJJPYsO=04nvvNM{r0J_Bd1UHS*96qIpN8q}G14#`hK*u@e~7rTwNa7? z2Q4HZ4NWY0b33sR-IiUnMC`69{wRtZLmq?X)`-&??c|E5dicgOvso-K1?_yH9v1J> zz51Qhr5E!K^+&s|`7b9fgg2j;`=8F#hb!~g&ub5pz$g3L=yZ?5chSb}RKEgyIDe}m zGTZ`7uZkRHza$(2?Nao@9^nv{6bmDW#30>RuH-?T`WeKo+d-098tD%DD!aYgCOeeB zbT7nhK%9ck`i09TC;kj-w_Y#MZh?<)Q`i359+1gbGTuB4sNCEklNj%+H`S{^y0wCE z+20WHf<7qYky5rx=L(>DR(`^dcl1wOWUG4VqG_k-V7Xouzj0;i?Aepd)dki6CG-h| zbz@S)Y9JrG1~zQC{!O@4^dcHX_2@L?s>dFQoM?%mvrF9n6+WAtN%s?rBkA5s63)5f z#AjWG{K{Q!%}D_ANWC{+J%ua~LFXgOmQOOK^+0Z`c8xaDioN9vp7TWn#x)9at)lqY zfjC|KX}ph<0s?lgSbw?Rx@8;3;@fhE_&$APL&AFqze(;*y!-9rcNJ$yc<u`Zc#Y0d zt#RKhd2S&!Xz}qmbb25|jiQ&bW|$0?Cvx<=RdeL`dD5C~#`F{^B|J4F#qyNw5MT{U zra}}3rXN7L9fn>uIxO}Hp?p9bNrK`;UPVW?mrvm}hJp_>R8+zFs1t8UZx?vZ*#UK+ z%v8cep5U%Tn42c`CJ39v(QHb1@Mohnqilp#!kr#Tj7<<i(RQ{A(D*y3F-`ipkUwZ; zAO()dfMNY&eTSNGgq(EA`NT^^W~<;I)l+Js^{)<Y9@`6_Y)+|~z%FZ35k}8vCphum zlr7x4)=>dOzbdtwi1aD~?KR=e5>eCm9#^$+pUWB~ed(B4#^Fm|@q#jg+ybXBnw215 z?wa;jj(V#2za@X*Y4YE!&Iy+P(;z~@LZVnl_t%?ujrJ@Lb~DcAX%~+0*UH}@T9rq6 zhWEYho)=@!@LnrfE1RS&uP17$PZZaS1e;44%fA%Qbe;7s)OX?Vn3F-i-dPWqI~~p= z2b|uHVl5)GA8oP6-&kev^HEgw80+2@`QzW&&!ni3cKa|i{Y~}*79q+U;_9rudL6ia zw>j#vV?mf<T`ESUNy0QP?rd>VDA&Rwo{^$);cNWHCl6tGdQL7y3M36omG6sSezrn8 zmtEn~;TkX3)k4FW?lo`tp1Te<P20N7#xf`UTVdv2xhk>Ul&FZOtHp)ImBBuW3{#Qs zc%vW8Sn4ck1pNu!`Ykw8RTlqTgWbibUrj0?84H|<UF*=&(6rt4R%dXq=ekg9qlzi( z?SD+@9$XDM{E0%%yDBz)#3#*kBva?A&RNadbPoDI>hA4su4A9N9e3pG{f42SSHSP0 zcWoPx1stL;yQ&P@89{=>qIF=TH-8sk!KB_>{7bC!c8T_L8Uk840I-;|>GM%TW|06y zl0@m3RTkEOQ^lfKjvrRShc-_Z9o(N-g*>sY`Y>sb-9nMroea6KO@#SjTl(gijKDP4 zShPL<UnCIi-+jy4L7qS7FBfd2p9vCH-A1F{>KQHoDEAZa2)gZ6*pCej?|C5SKM<<n za;)j#mG{ijJBMDzI!$aTLqz*}dHBN0)?7WW39$|&9!_C-zmF^RoqY$9lcikt?Qn+A zyB|3Twsi1zf;g#mU%DX-(;QF6UmW2YRAvsV8n|E%A0s+mL^mt`nO@hkcE}CrFFYBd zYRIh83vNtBnRw&9Om!gup9GgG1P}g3JFax<P$1tetp-i?$iWQX1926+s;@zDI1?EL z%e3LRUBp=GrVVS2pO{f(H<<v`8j-F4fO>xF+x7!M5Pz2GWFY1yWBm6pKf9{6?pJ01 zU*V7)eWVOM6eD=h`CwF)tz3SDf{YW})n+#qhMnm;iQE{8%u$9G9^O-{Nrko2k<X8a zdH^>t!`+<<RYf;p>Q4QU1u^AUPIAPw2M-t9FqW}w<)PP9*!u9;7L)v8YDrHMHG+y_ z$fopC_kY=X&e{aTKg$ZrAn{%-sP87dSH*Bh)P)iq{07UP?6*Cc9A6xqu;)NAJTW!i zVtZ4Ba3Sf%SAGX&%NCxD`ER^2@mKX>Oh*K+cQ)<WWUOdrG_}>swv(V4U&NoJd9~DY zI0t9!du!Q&gMp3^Gj9Tl;TTs`q79%Qx^<Y0W}s$O_wn%?$^u?QS>7cGQdf0LNHytZ zY{H+le9r@)7Z?H8>y;&7V&wF#6$_Qghmuv}Q{cdDbxqz7u7dZwi{%NrzU0Y=#-FNs z|9y~xuwGp3^TbyozpK}?qwB2wrVvwq5Yco8U@|STrWRe5DRmUPaCPfAT4Aat2fORN z{M1qc73^-;EyM09fbH}P*ri^Ox<Z{;+XLn3PQCd~;UQ(MFV621ngjW+?^Dj^*@@zQ z@tuc$kHMh|0s=^Dp%mxU<+t?w>(|?bua}DN%IbdNuy>@-pKpd^8wXd5HAnEY=vr9P zd`J73C^p2I|B@=}<m5h-Q-2%0(71fPZEy`D1@iDz9nyf8)bJRfxfrAe6*WB<nbHG# z-e`vXHS=f9EcA<JOE4$Q5LNGL!5~xbN`%)hUe^1*DzdlgMU;Dbv(Us$L)l$Y!&*0? zOMjwG+Ke1{5d){T1UF+#;ZVD8*Zac<U%wF<t_i}6v9~VB9HIE8!j1n49=`OjeEg*j z&X!lY3Pc*xH~?feDQ*>3r!d^_PA_GrD)L@$7$L#+?yE6RIq-q0+Fstw)hG1spZT>9 zY@S4q0Vb&-`UK;z?yA$JwKU^Y%6i#nh)Frf-Qhkw#n~d|C89K@A=pSNiP8v7)#R|d zOVNpOX|F-i!$`Yi+$}#A_ji$**oVL=YM0O!xu1UWx`>mNhIzUPy1OBc_fP@I{8|*d z<Dzd`buBgl*$Ah7@9U7wFVNG!g$HgZCooL{$hMy?$*frNs`|BjBu{EYlZRDj0p-@2 zroLE!WN|kK?2w&sa98A0Y4Z@^1_pe>pmBMo=pT_wIJU%hT!W;!#6Aq61p;zI%Ms$5 zqTm^^DTQGT*+lrz<A(oceg#-1`joc3tl)Vyo$PrHIh0QkmwWD`;-AB{2_9qbHTD{T zbYjhUAKi>c0vcwOS%@QZQku~ThcomJmy(v#glRr>2L@tedQCZsZ5{@AwLfRvdc`Ts zNzVyLxf@6pqOImO&TTF)wW4nu)fZ}rNySo|(b1H5VAlBfqwC9HefQluH{ySY;@1AJ zhO0|c<#)q=*|R5IlUp*5bWd5O+=#=!)4bQa+vl6|_-Gtwd^Z149-nwu%@wB4a06)5 zs<lC@_Gsc53V7G+^m3YdzD^OCi1eq}{B=Vl#YXFoSG3g!bnkI^n9L6*2Mf+0x1Na$ zM8Et19J+vUk9!sX>_78~R9K4&!<J)E<m&bQo*Q1bs?vL0hVhfzVvs<oES?teWR*nh zFD4boxFrf77cORMq@{{>rSA^H^*!1ohXWl5sGq_se)I|=Vfdn{z1;I|N+Q@(2`Gva zo*TcD$I6RwU@zvt<*(VxJ({Xm1>#UPu0aunv_gjZbsU{zsf;2psyl!<4t7I_s(d>C z>F+M%ip5j90z>mexLzUcSMREOo$kEg6AqvLFGlg_bSqk#*|Pp6%T+zX?MA&vTCYF6 z1SVwlW<JA+X8wgarpmJYsliyT<alyx2p5xUa#5tiQY#%wBq7yUbFi)&_3SylW|<W~ zNJb9TBICBTf9eu3jB`Qf4ecHc8YB^J(bI&+&6~21?~yr?Gate~!V@=Ck^ueb_5FzK zPQ`%&WML4H8N}l_${%39EqtP%oy556wk<;oj{|itb1Q5;CR;fjP~ZKDezClxJ`qX) z%gL;iH=zD{{NtMIF-4XxCXB2kzkRhbJ{&tvy*Tj_zinQh{@YQiE*$UB?fmyN)lB~p z-7sL?ZLA!f0vNFTkcMa-x{PjTuku3`kp83>nA2N*Md=7pFMYY<U0o0g=QsvbXzk#q zHi4F*n|#50g(uabhTuy^lLp(c8un+jH!pUF508s2SLZ0vbqMKTF}q}5U?K<CN!Z<r z=#x-aph+yc6k{^pBV&?wBQ{b+nxV)W03DH8AHnY&#d*XwDvLA%s8g$$aMFSL0GDs7 ze<-Nfc<jkby*!5g4r1=%L#$R8x>J&DuW{+fo>bC>lcB$)_?%F$pRcRqsI>vm^G;gj z{p!F*F=-Dmw7}V{yd^%!)gS^~)vcss(NxJ1K;=_(SSoT3iUZd2ry0;(++~LwNx@l5 z%G4MF)uroCqN@IDKAUFsyV+6oOHi+ZDA=^l+p~jM#Nj@cn2iTNMXqS+mBUDKzL=h| zvJxk2Dg{mg$VAHGZWVl7bX^~eXw0JMO)&WT0dc?olGWAZUn>v`C+d*%cOTDB9TRW? zn^J>$WNun_JH_Qf2ExBtIi!WIhRto#^Y7&(`{XmkakEi>A@w{qwo4kRyzQ14sn6M2 zq7$5t?Kkxjg-r`xe(KS{`PFZW&TZT{SAoB~Vc3U=<5a3t$f!DE4N2mC(yflwNY8p0 zv8m&`q6<P{$s;soJ}+wt-o4=Mnq%$#Do~;psUb|s^6{F|F1ox^N~NL0;#@VDMa|gY zV4GfEWt7E`E%BQcr5IxP#~BeXa+NGi-kT#LbOzK39touGt`3S@pAp*s0qH;%zoXD| zI9(D21QVBvLZCl%9H!*svAv`C98YxcPM4D0jv8u4{7fRG00aW+6TbT5@vjHYc2zxF zcAc<at0xBl|B5l*J62LBK<}S3-y7sJ{b%e9OcuI6KUd#{>#i<dkO9~9k72IIPro`} z#b5sH<M@C6tN)ROtCm4^yv#)_xo5^!n_AcN{efc^NEJflcwKTnL%!QRh{nB?Ldhq` zA@^hC1TxNvq4@oT6ZC+1V&`F&42*#dgts=01ZeFMi2wY@AH~Zrzsf&+!8^B_hD(Yc zfH*>6xVFY?r$>n`B0BJWag2}t{<(JV=4@<aTQAga+ulKwQE4F8(@;enr`=P)gD~%} zE0MrsOKYyb{~mz%YU~Auc|Cy1tq(<ZJ%|@Aso66B_~DDmwq`{^8g6-XQRF-sAr)DA z>GSs=`~Lm!wEv&4DqpE$oH*U4ZM5Vtd|6T~VbW$Z!9UA5QBWf*kj&qsZUC9re;)h3 z<Z_AN55F@8@|Y~Nua2KBx%MH0YeglG_f`s0y|TH&dCI;9_G2PZ!PT0BIP79I2yxBq z4<B{);zirk%AtRcyCOm%&T8+yUFHooILztj!w@OIbs7387B~Dm{KeJP>+?%DfAF28 z&g$g@g}M8Ej31+|vyJTP<U2WTfWhb|Lq!((*7fiAUi1{J^?uFhaU7^Ow`gC`CEmlm zMY%KVVcUcXLmdmsG<WW&TK#9xXXS^>l(YU<kKg^?i{}4F^~XaT1Df5o#)5ivPvt7g z9B7}rQG&IIBuUeSQOr2-z34)A@SVK;Cb5k3=r_N2t+JQt<Pa`a&@rw-oq=unYZXZW zYPMH_FtT1CtE?BT%0LxJE034|y;#BZ@Bd!v{FV0IQ=tU2ADs`h?z?_roRTPdH9oa_ zz!ncdO(K5CaPG!G@urq~6v49D%l6OX!jiK^4If>Yd|}|FsuKgd`qmBv>N3fAFw(~h z-#b`psww{-@};``4sAW!W<8E0dD!9g7TFiq&1<*h)%Jn$^TFAS*xZQk`~v?!%9(Lr z8<whJDbfQiFmCVhKE@l5m%cy^;r9Tm1=(NibOR*)Yx<1TYQpBWr(%rJk9`2yN(p0L z-1&o_e;WVuzxtmZ-LmpQd^z!8x+e-2F`0%AMtSbMQQv_7sj<;hh{4|I{#0{?;*X&7 zS18jPi-@%QW&(%|Blgrnv584EW9ujW@uOeG=P%EOo&)}w>LEIAWJ(h2-z)e}>`YA7 zMGnP^{H*YN_tN=`NmwhfXtm;!nbP?39K<<-(x?`uJ>h?8EJ6!}x!$lc{J$<ie6)Hv z$dg(u?tGQ0&U5iU3k*E)YVw}&-@#pl)qm|)JUY)=pYd#x!<w(trdGWmq+J8dof=Y; zi=#&n*ZUgarUKm@aWOXU8o5a~CSYH3Sb92!C%Gf*4C8M4lP^9dF`kJe<e!1$e!jCX z+*19V;2AQUKAY&eDy78Vll9g28C`rSj|V46SKQv+f&)&j{?~n7Yf*DoM9a5NMA0;A zDhV{+QU*lX<q)1F@k;pnYupBM&K%iruq=TE9Mcp}EAB?{im=p{uX4pf(lQXLFM{@# z640I#QF-%!XmwB<`%>0ehk*5HcEJgA`s-<JR$B~XnfjuUD}m?M-{|#`1xf_EV{Q2l zUEYqC<U3eOM-%;A(G&@Ko0*cfmE8FDA)WvQV-pl$9)cl?6Q5MCF6t2j8BdH3c{YF_ z3f2eaaH<di6TBY#dxv^bK4r-grH6Z1bTG%m%x7ZLcQ2-yQzTRIw&CNLIbBKSfBD(x z`9J@w|4nAh6bk>z9;9UHJeY$4BRELv`fU|b*)u<n*op-aH~X&e`a-t}IVnIS@g{$b z8DnaY$#`rPAIj_zM6Y`e2Eis_AE9{>SFk?%{LB1{pL|?h$k7S-Bq)v@w@-4v$SnZ; zB$xw1zph=QU-EnLq7%rg-N!`Y+I0{!(j6B<bxsgX8RoY=X6*Py&1jlj)KHGH=G@8c zRcXKM=Dn!NVGf?6ALi`Dlyd~Az&V2zb(olzD(WG!Bf2XJMN*ucX#?$d9%b+zZh3h8 z{SO-D)<eZtRt}7=7&q^M3Z<xg`?>Jr5RDN|+df_#lPtnJE*N|afF6r>0sns-*LC2G zLNsebi3dc>#R(Fd)L0f=C&9Zy3ABKAnuaHR=P?R=kI$69cR?Wzd`f{d0i5&4f8gON z#^nUvWfijI&`MS!C#n=hJ&xnCX40)+1OJ2fQUithQuI^fa;c1tIZH_{3Zg5liV)`i zxyd<UyzdFjj`}@2F`nZ<0S^3<@G;~6k+J{7+$9<CH!o(}5$p66)w0(YEu!NQgns`8 zAddW>x+lkWGZeVNk2#daz=QOXbRwj3l`yO!|F+pn6~DJ=woUB!-@6Wi9d)n}QnCQb z^$>_4i&HgOEDt5HLR7*7v!TW3>%{QK8XESNw6{O@+}jrF0ngK37s2n)6Kp=2T{Zus zLUs*k;X8=51HR0;bN8CS`)jW{;;J1z_VaLD`0BHRwR{J-_2Lj~WuH8EJ*McsHK4)* zXoY+uW`(ddoO2YzwR>UtZ}d*SYg{ju@Q6L#%#%eV9`fc-Z}2TJDCp=hD^7pi7I;$x z3pH4Q&5lcWh}?Us-^#3FYH$qBj~Kk4q8#HMR+K{qMAUD!{N+zSssHQ${(s2-@ekh% z1nD`v8A0N#c=DrAU{f+pIldYHi-YE~>8oZCx%F%lqna}m*H9vg|KnDi7#b=TFoNKF zOnC)D>U_1`m`QQ`r=NVhx_@!lAQ@+@J7?nqagDnRQM&>E<IEcYZV8JPZ7-Sepv+mh zZVRd2p>vw?uV3uYUJ3Au$+>vNQZ0Vh#2$n@FA$>|iKDeHavhv8{r}?sanSOG(p`Id z@Yx@o3K+^5xnnaV{D=Af63i8HRO)OqKO5j!iv&IM6MT<?0E>qQv^)7T?dN0<#hAhE zXJ+AlK`vlQ6?96bHzs{y$iV|WQ&HFiiubsrKLl@1A&dnF@@TS9(7{5TbC1RMtxZgC zC@yE0Id)F5QziP>e|!gq#>9D#3pXbA-v=J5#TJ4Qp8}jULhxFYCpAIDOjMLqjp*B& z|J&anz<GZxzvp+kjS>1hLR&g0)#j5kXG=P4v3jcmb(B!13&^7V?yQD7^=ScqV&qW> zUG|8Z*;<sfv*RE&5S<XLNO=2m18zA+u>iAbc`cGTMR-Pxp<Zq2)rKmtwxd;8<uFtr zzyPVPV8j$LaH0Sbh8*&>9@CI5R`Y0R3PQ96Gqp9Y0)PVB5N$G8=mXb^HRv^<Qyief zelU(FI}RiRIy&0TL8p{0;d*5ArA&QR`Yv63`Eb@RKKcq<3_C!GG#>0tcNG&HwFGDs zqvS?(e14uocw@RvxJL5O>wsL{%qE6BCC-dd>1H$5Pd}Zbod*Jsip?Bk>P@##J67Q$ z+vcyW|Ns7PKd1+*8mFRgvPjz!kT_{yRgfmZ3XgPjzQTLXNjJGm<yxiUy!c#d?IWf+ zqNmOKNS<Iis1w(={jAT99M?(yLWO8sZ%T2wtH0D%l2vm0!J(g7s#Au}h&fr!=OZ(S z#}JP&qYc}Ck_Rxcpc~;g!%Q!b^`ApN6q}WCt_l52qK9@=)NJ3mR!_10-uypF!5LUF z589cE2vPI**fn=dF?o>3#kE1d`<`9x-LhU=l|B;^5{)nCbLf{wddN2E@7gc2bl4PC za+~WjJmOJ$VmrsT7Dms?PKEK+fJqEQ0D5Z(r`a#k@j+vst|p<G#|)+eTF)VV>bwJZ z5DH$E4G?<j#y?PCardPJ4XYEOLsvSSvN5iB22yDCr%jdog1IoPU`>IqiVDW+v}O5h z_2N@)Lp6()%mQ{r4}e1z+Q#(O-Ai8wNPF!aia_qrvps4Ow+gfC)(OUl$1-3mqsIbY z)BiP;Hqo;m0XT$fJ%%1y7a4o~J2h$IiTuC24<w*5OF~0ugGe=2g8=P-|MN@`VI)>g ziFd22z5JgC7Pb#D9vJ_hXeA2qLJMMTN`>#LfW8XHOeju-tOqcu$6=6^T3I|+k}I4| zVVP#!x5#+U_Q-+h>z6sVAQ`_w+`|v>J9|tr!FdTW=cGx7y~ZK!nT_g?eZ6`t3jMpk z{_l^Rde*at{nC8G?A!R?9RKzFUlX|A`5&V*Qh54f{YsK78Z@0odacWMP0Ii6vyi@f zE;Ytky9}rAGMW6%e|=e>ygVU*Ip$0%2Y>ABZ*pSUf|}B6Gehw|xr1whed8X7(*Nnp zXW;QvR){qX-o`gqXjf2Rfhs~i$1(D6Xb6UEW)$#&cA%Wanc*Od|J^XQACM3LB$cEa zqa56|*W+CBKAZpBS9_t*cP3xhZ)>PH1omibJaAa**Z~XD&3!0@g{nBf*g<y!e8v<0 zpZr?-?7LJz?DJ|0d8?(8rHWX|e=%!v1i(eOTABrgS&IKzM5=qfSzKrC!Fmc?_Wk@Y zW2nIv@<&d)4YM#6eEuE>W`@+AvXdMUjaef(D->XgQVpU`iwPA9$Td{{=VdTl+%c<O zH0aYXs2j<;fsk#BfxheZd_C<05u8_iCK<GX1T<J9w9v8zLL)94SGQ1z5@cx;J&}nk z@xEK493B*!=;Y*S;5a<16OLR&c-moKtTHm;*XgB2L0wPJS>oOFzEbSA5tsn@s)p*9 zUmd9hinFl&i4~%%8=WlWl24;pT_wydPwD3kI%q=y09w^tQG0Hg_ZGOb#2%}Xcmm9^ zT^OKtbpK4dgKgo^6G??x=k0kw=s}Lkk^^*huIHQ9sPjMl_~ZIN{NMk#*s&Z#if|Sh z7p7-X7<quh!>0xdb}hXy!)8R8(Rjyz;fecY>$5&YIxKjiU3sVaJ!Q8eL@i{5hW=y@ z{g5l>%t8L`fBh`q|K&@*U2|=vMKWQmu?BeUut?!x@v8&4uAPYa5Rm1-%e7%XUKBCl z@tTLW)9t`6JZqN&v_hJR7kkPNJUis<*FZ{W^nV)cfdCCb#C7d^F?Rpz@#6=kjLU5I zz&YgqR)DNyum~ehR!K;%*Uxd_8Rxd?0*-(H9s|bXaiI@~AU>`bzB+@>sEDG+IZ<h# zsIYwQ#X+DLlOi$3Bsk08yKVUapX<NcKhA3h0+Ra24<4?^5w5S!DJ&*TF0m>{t3#qk zD#5A>wqT!k45ALTsTqEpV%qOqOSwmz`NJcie)UkYMVfx|>|kPOLjicFg^lbP_tP7L zTagLElL<R1TnoA1!J7Eh&MU^KTc2gZalLK&NHL}SXESAWGGq$D^!M>fJCh&v^zbN? z(*^>kTjD$s@y0{}0CY3gj7iq2-@jGvZS8~d|A{E-D;+qQb&cuO0|vNyx5vynn#sv( zOg76ea+ZwqOiO7re(dk(az&)^+11JK)#SrpJ@hl~Xy0T|&^(Qp*Ni}mfRQ2uJz9`@ zEf}bVTfoe&U#+JK8Z#~|dG+dHv`_!%V~HqCtYOF>0T&P4Jy9)lqU!2$Rv|Yt8~9l} zw;oQ$c@bYeO#F5Udx`6$e1pCl%w{tH-i^_X1*ZU{O=Ag&j{~-J5$jX2>(R!C>oL=# z{r{`$QLnQAEgf@+V6l^;cT@pgxl^JQfB;BAJ`S9O%k&MgPG#>zW#Ieqn)j+0UFXBO zus(?EvKFqGP8lz>xS-u`E;ov=?Ft*CHKq4Icp3lY{m<h6_J8^xh%)Bv^gugKtxy}v zF#qpv^m~Q><UW$&|GeD~7T>lSCmwM`t_gFN2hP+j7I8^O@TDj-qhuk#VQY-9?JMV> zE4@70`~Kz^pDx{;&;{>2bzB7q(QJo@PGVTi{}MB{$6pX5d*v{Vg=pH9rCq`2azjAE ziBNGlauF@qhk9#31l<k(bwAiqs9Z{K{Gjra9M9rI{~T-Vr{e#3691n)$BEJTXajPa zDBo;_@!$<*`aNi8D+uL^Z9fWrO#Cl&4#*RWy5#>dPNa_)3y?G=%&|7%H#e!igJ1&^ zD#m<`1AQrsPRwqE>v?BIcuc_hWCF9&6Zur|tn@%cRoLm_KJ0~@AVZY{^N1_M<LC(4 zCVd*A&-k(%XpZAuKkp%kFhEe;MEj)nm$0yynzI#<3{y<%S$W7AAXso4afr;xWb{S0 zPyen#i;3$fKd^=Tn3Kt2Um7}#1P}^R{xXu!QG|ek=rP0v18aej^gN4Ux}#OQCWMpn z$JVuuFLG^@?&RV+vh+$km$_!pA+CRMtBp?d?_&As86JLDQXXSO^vroJU)Fk<E)4Zo z7A1W?2B?LcrxUVPOXal36GpE)j#B$;o)iCFC$#qp9v>NaXu@PNfCB;Bu+e5>m-FK< z4-KNh#GbLKJPyHoNiGpQsg=w_-L@-sL8d>(qX}o2)t5q-14R9kUw#=s|KMf*AOEM{ zzu@XhW@2DhUTRSQ{w`S@f;Hw+ZSTF^UA8aBW(YgZ6)Zkm;~Im=UUlz?<f-Et8LkOf z7rJIX9XG;XB!1)+@X*gzfe$8IK-mvpeieW6w;#qAUn#*{?eQ$KP(D$D0eW)J1<C)V zkY*{fQ9Oqo{qWRxvceOeFCNzE*tx|C%kWN;3Cj?$<#QWP+j=LY)PEP@LUBkhML!5D z68o2~u>N?_1O3&j$S)s-*98S90jPnyR@z!iMFH4<5B_B0h$)gdP9}Fb@ElUy??3i? zzSjZi6~uh{6%!LF4FBME$W@%{D=vro>Oua$+@Oy-X@9wj0}rPvC*`|zu3Fqf`{-)w zdhY%TWnLWXdBtlSudYWJu0DE2fLI&$_)a>;C-HR23{kp>ZFWgi(RxEJpA+D(!F}gN zYw%Z(VDkB|5NlH&?1+X4cMC!o_Q<4)d8I3byWK=iE;7U6^4IUZ=v_(KD?RzimzOBu z6|K!=NW30ZOgPBQPwi>7qKGoGvl14CU(-P^SHWIfhdv_u`RuF9W|7bhUvnnhf(HKW z;H6^Y&EjGc=e^6S^*8)q3{x*|i9hI*h*n|dlb0QB7C58>LrgJf8w@aD43Y(66{Qse zYC&Jc5K38UW5c6v*3oWjo{y&a$*;~UiGTtlCc1*nP)srWpLhOO0A7?u+wnR)ri8;9 z*5wyR9~SrG=y`wf>dargs^R}j9!G$uA3uMAJv*nEAfRmVf2R0JGYAyQp;tK#<BLnq zuO2Hy=XbE3|FN0Rzp5Mm(~lOy!xWh;MdL0`%E1USfn%QKKO$8J<HEnw^na|C%k{KO zb^!YFtE+%Duvt{}f{h2oT(iO`<c&ThW<O+1+{XB6!P-NE8xbx$5bXmS``#}fi$pKK zI@L!?&osND+tIBM``E6<Y&|GMRUm@ZVEq42{_ey4zx?n1F#gxqVKZ@V34B@Xe@0CG zrG_7QjowUzBX68R#s6@xDw~RV6JrFSV}WLg%!Ph};|PW88h5Gk8WL}GJajNxlHi1B zDy%*Ca}D{EFCOxW)vnm}RgLD0U4gieST>(8io_#MXlMAOZUvC$D`6Q&V+GY0k2bjK zk=UYt#Q~=am^)^n=e9ZWAP=boe}$dvwUTl02Olf&(>%bZHO1LLsI+Y6s;!<y5$^Fn z#?ahcU1XfNosTwSLD2ahkz^O#x)`>)hCwhYUq-{%1@est93*#ib%0gtbbW?q{?u=* zI_Zu7l?Jr*OHm^V3e*$5l%l`$zX4>AhDm?gs=4lwt%o_H`M;1yF$l*A9dlPb1_b^h zsUX+Z)NAMe@O*t4i~B66VVaG0ueH!)6(H_%V`~SMnqf|6ld{T+gqrji>nye{6Cy}n zf#}NuxbP}g8CS7Lhd3Jvi|%98VPp~k{HW2275aD|9lRKT4@ix$!>lGv(~dwBU=6s{ zgitwlsP<V6PfF}ch9A~3()~9sWA}a#;kx|<VJ0xK@G6<hsrag&n<4TK_UaX@mq45L z!xK&ghrDgLU@!;eVl3?I%!={W9=j5$nAebW6?9gk8{7y8jUwIV39xlLNxZU$b85Mx zw40{31(<x~0PI)vVjJh=lM+-P>XVXAgVObCp3kTGm_MMon7K>z)y88$)beZDmn1(G za5Q?rb?l6Q3UiUsiJIk5vXj<!I2MQs+m9!I8Y8(1PE_<(iSMt+|0|STm}y0C!R!UN z@BaPA@!=N_Rpt*M3(}5;k<j@y$I}{CT043R)LM}SwvbORZf3dwh2K*N?%{J9Cl0af z_{!`nhQvZ7ecgAM2JzcCGzgG>cDCHt*T=@kYPabBu=_&stLD{S<9wGrKFFG4wr6er z-?HG`*@$CR&%HmHGc%G__}~dkp)F1}P=!nRe#;EQUC$>!C^=Y|_rea_;r7Rqy9h<9 znhrQX$r)gkL`jTqI8T1YNh&aiMz??sj&5UC&U>5*TPgTIfLit`OyGuoOLCKiGj|s@ zK{wIg%N`hdE&Dq0A~;X3VN=)tjss7KKqfJn1cC#zJ*6nrD<+xg31Hhvp_#K3gf*TA z_&-VjH<~xyq)h8u@jrZRohD7)7Ws{OoLc^G#Q*r}dH(N*w>c83wTVYKNldM{^Zz!+ z*7cqL1AUS&Rg68S0I>$~aQ%5$F~RxpAZ}2Qd2!i;`TxT?26`?s6(00!|8fGDe8&IX z$CqsS`CSz(vgeP(vRWr+1)YiZLM#no=4t+4i$_!Z&&T`P?oRWYBHPJ+wUrj*oN{Lt z0yg?TF|*RzK%0))>ta6p>noLcUn5xw&y)hLAI1?Cd{!P_vdOpS)qSVNkRxia7z-RS zPhrM>O)B}U{Nk&#{+GY~0CH3Hyzl6`Qo>57)E^-^FWnV)QU+|Cb1L!d38O4F!0AF* ziVx_Q=)Vko*IPf~lAz}BV1S;UAi2UAXz=H-m;Q^s(qTy=y#9qVUcUE}k746lagZYy zu)bq*E=HN%K)2$5kq7v{BBn@Sx^IvIHFqt5@$JdO9&0eQVt-|1%eM>f<o^~&tFo|Y zR=Sq)IqWA+@PGE)z~>sMpfKhC_xOLAD0i|Xg5qMl#BstGEnZYDXzi8(|7VB?kM`Hl zBY<ZCvZ%!jJ=VK~*YSUkkxBQj!~Ys-l{k|Xvfq&Z<4yb@_VXP7^YhX*-0h&*1UNB& z6aJ4I|JT>@KWY0?dY@pE3FnxtrS5oZteI*^gcl>1O%UBilJJnMmPi`at?~KN#6n4z zuWnGS5o3As>m7tEoc}sFsZ!e(R2HU`T!O7lnPG@CWha|$`LGP-=SmP(=4e@8;}C{U zkh}8FXNUu*iw`HBIe?2T7&Km-50xqxHE}Sal!IbrC=~=IclEbiX1iSIb!H7*MMFeK zuvw54xoGu&IA<)<fwA_=f$cs(bsxF#JUjK2-(D2$Ew}d}dmsuO^|MLs_$$Gi?MVsX z>o$)~(^9EMamqg7L`x7q`RA99tIVzYB+$`J%}N=rL`_A|l+fQ)L#%GS$Qf;Q5P|-2 zy|W#Vt=nZxNnZl*F}hFaRL`mnkj80I?rQ!-@KBqnPS?rl1^73hmu~xKAAAvi^^c#z zN^t1ghyo_I;@koCbI_&(ukqa$UmqY?KBkd{T`Pc<|1*f|-|_ed8crO3dYIqV4b1gh zb``Qu#_d2>e#HQTL?_?C^S3dN_H<jhb4HiVGjk3o;+W^1T&TJ)-#d2y&)xq6wi+&v zFjBLk#v03M{*Pdcr|r_MGGmgD)HhW9`bWQ#jIZ*-hbOj-@qnMr|68yC$#i)eQ3HA* zYKGoLoOe}`;JN^^JtooD$krv3>5s}mvN?%P-_S`lB@$}X<y)p>Cu1duZJ8bz_^u-q zKAacpro)LRtPrcf0GA*t$*ro*J{v4Cnt;I(&)*IIoBXly`K|N6sAPA{Z_WP;OcVUi z`Bd%wz%R)Eip*j#0|(Wy5HkK>H~+83p%mr~J_)C&KfL~u{5xVLiG*~LNC_!%nRiTI z$I<vmeodUrPyUzxGls!+5l_$XiE#ZLH~zQzFy@h6I{&jj*n1K;+3%*Rcpd+@9TKB^ zj+XBR`kQ~PYq$%-_`gCnq6E$5%mQ%G&YMR-1nZ+8{^Muy(_enk#FjDnN^F)VZ_>Z% zuek8F{Ey;+8Z7z+dIGdC?pW;B*1DN=gC39v1H7(#7y_SU*2dnu7shratI+?ga0Dsl zK-|YKAL56<_`F~T@$L9O#vUfdt#0tr)4`@V(=<d1Cbb!)I&aitfL=IDJDFa)bakjr z&xica-`jWR-`zTVvS@956t;_b-zff{q&NZ;9tQGOd>;Q#_kZnb+~a?~c_WSg3$Z#! z&)e~@nmSMYru;wmzgzwfaZQeS+xXuu|F=tBr>(G3pA?Vc6gfx9nMdLiwAsDHo+<%O zkMGDS6iz!-PMS2SWDQz^2?5#MGXeF0aM>fXd;7^IH)ro)$j2hPS;a1tVlo7(ZftDS zrl4`tI8YH>$*hlz32*kPvj8*3_HfkLt)&g4m0wx5!}t1RR@Ui^47z*8Q;`Lh-PXJk z@Al-QE+-B5v@%z^hQ%YCAXhX;J_O9wkNp~Lz)OS;egIKbz%<b{%m*b$t%?%)VaCZs z9l}Z9{x3kos5{R&=_g7?rE~s{YJuzPvsVxCpa0?Gq*kcp<n1|1A`eu!IStgv>D}_Q zvRgtKw3(FJN%g6y31h=&=itW_82G?a?Bn382irvlEKlHTgZAjtL=gtR%m1%j=8u1H z9sCzPlXZ>*#mS_94&>pylhR+m6nyD`=_5FeW1x@~^)O*83cw5)=MO^ZI~L<=?ARs( z)d<N+^cQ%YoBww(allcIl7}RVay?(#Q~UohoT9O!pPH?+vK?Hkz>W9O|25HDdrP^* z@Ync%18M7p@Akz`oOzb7GVHZ8E+?5Per!RhacNDf1M%X{A#M|9{EzQo6yr!C=cIBW zPbO>}M&Bkw3i>kx2y-*Zj{2i>PBAKsd~KrA7AzMv*lH))64p0Ik~0j8lS)$?xon=M zf>wr#xD5W_w@A=s7U0OU7%ZWPBLx*fCGj<l7rnoI{%?S*cgFwwwNk@xf&Vj<Oj;l( z9421g`M>dZq*`y_e~n&`y~prB+Vz$7>pwc^id$0G=iMWDpK$<u(KH;=FaC9&?*Djh zn52&)=<@&B3myE9iOeliYWL#-VvHl&|LD7+M-UI;2AqR_QX}`@i2nz$QJ7Csgci+$ z<DMM{{%1lHdM`q}m;!+cO=T9g{w6o9qY&zke(*tjaUD=GHaF7XqG%3fz<amES`=aY zZ#^QmBfQ{m#AUoUyb1y{Wn1XCPP9g>3A9_1MT||`T#7{=*^ek9wBUvR$Xg#j{fqZM z=>kC>Z{U9pWcroG(LMFa>=0=GLEO6B=tOcmbp<Vg$zCVXbp97i>Nh0FGP^d;8fU)! zAU9AlACmOk!YtPKABzXCjsIi!{~psoUT!0JhX47d9gJ%gHP?>+>UcSiv9&gWyq3N8 z_&>%dRxI-E_<vJu#5?7G8NKt!yW;<LrFC3S3mAh=1nXR3lB1LltU;9oqtaq9fG4<( zmsP=gB%FlMxC(^o3#EbblLSR!40)_CdXj{sKr#r<o@BCjz8Gp`PWg3EE4e+6^X#;w zN&=^h`fsAS#^+=0-?7bT5*)U%WQ+mu)X$Wd`?;Goz=Om{<H5iCl+6JkZnGUH#J0Pd z@o=}GU>W^K0i?@BmIRp{tV|EJ&zti*L}RH+V*og5Yo$A(1MBB%_bQeEus{OWZ`_^P zZT4xqd@WDfG2jFIDNrb@{Lkxo+hDr;+<8^(z*U8eKl_Kz>gAUm+++-%M#)tx^w@_8 zz#=|Q9rLh%Pat4Fw%YIJ8s|i*vxQch9Hv$Xa^b8<(wE<ya%3z3Q({%4me^fRl@&Vb zwR7RIq3{0a<LmkVF>))^6l{Xeb~l8P=BAoXvtD-7hYz4T9+1<Ikfbi=8zV4PPtZ5M zYN9shi!Lnm$h3Q+A6e1NLy3NmGoS*xB3}&^wu0R&aO|l`m;Rrd|3`A`t)7Vuu?z&? z9m)UMCh6@t{}-k#@~+tY|6J$t05peYw8N+{i1D64Wlwm)RY&l~5M~G7DQ+l@K{Onv zJ6NbE+;7D%tPDsR$(5kfKV{}hfZ3b}iC1b~8F>!`Y#{VP8TcTfFptSZ1ri?eB<7L? z5O=?@wlaixTDgr7OqirJ3+A5l35GrqS!qHDk`w?HymkUb88~-e#JlDHwk5Dn1kL5G z^MC20ZJytn{~3_!`kq`QxRLnLRAlyvZSh)Jnk-W7FaKu(Li|DyU*gPs1OM-1CWvAG zEmgBwa5(=b7mL}3-qxcD1y<gRHj&TxfA(U-a2$39OguLHfAuZy?ji?klxgq(o|;sO zQ)(SHhMvD6|EJU_0$CU6aJCF{&yF$5{J+ifP;%qNWsP-xaf~|5{|_W9ONNgV{rHQA z`pfq}T~Nz68|aQckTJ#Iz`Sq5|DO0HlF_oT0|lp$L#26^E^5QZcpS9IZKh*bQLAEV zkc|d?du=$@{#g!=eRUga)Q|q#%j9bpook=+|B}RO_@4u=&|?@?h{rLBsIg}>Z$lbX z#-fK3Z^LZ)Kcp!$m%s#A!61{^#uj>4%RT<5&ba#SMXCO*&WNY+KijVH|7aN~E-z~B z?*Gxc!kI_%f4&AZX=wd#QDKd{6;3B)+`M*!R&4(NoA7_jkHq^s#Q*Vb`G09P_;zP& zYLa0!xc$s34T*nT-HU=QY%6PF0(AW;{HS~pta3d4*@AjFRE8!64?!P}Xf3bnwQE-5 zp<K*x+Wc>G?ZK!}0#XmAT21h+-t>7>;MdP`Fs@uMSKHn-D;bV@l0d`5Y}%3L*|v56 zF^NyctdRHhU18_EMyEMW6tgOGN`7XnbRzZNmCz<7fY17Jw*R?fP~4u!Q(h&vC`o=u z@?mH-%m#EH`cIHkkw9CX400Ci)Ku?UP<-YQ%B=xpawjXJ{rh;~7hk^0AO7R#T#299 zSGsQEs*K4BN43&!ZCl*}_e|lyuXT)xn5}yyvYoc(KHw?#cul2W!_8o9Nv&6(Xm5H; zVLm6C`n8?zF(~=y^N0A}k3W6{#ba!_($LpFVA39&?uQmF=^!+fqKZmYdZi=I*~k^I z)2T2dDHIxLkL$+cR&-HghCpOoKDVcpf85CDJ$bp6PxRBzwLu^Tv0WfAQgB;W3dD09 z+V+p4&y|vV`u~{|u(!>Q|5I<U>oR#$-+U~|N1za&+<i9DR{*<HQBdD~^M7lxa3+Wc z6bRgJjAvM%ZnQak+QT3Jcha?MI<J^gxJW#2b47q4N=pVE{NbD?$z`RT-HqY)HBH_! zW)1?EnExyPGEuP)CV>4B{j@*yRRB(bPU?YLBnv9jNzw1r_xk2-^MC7P(6j0t@xRnE z5x}?R|M_pbZWY3uqU}uZP8p6f3hC<Z|IYvP|1|!O!N(FekTg()d(Qt|USN6Ww-ytU zOtdfm_f=aKkU)?nyQBPS_kUuyxS##RHXi;pSlfKS)z$utTb_BH^Z)q`_@Dh4|0l7O z<FW*wrc~oiU0g~o2Y^<Nnj|s;=N5@i>r}x8ays~X@TKnb^FRIZC-u=6U-tQm5F39P zfXVi(6Sf!Mfd3<O77vo*Jbiw;AAV~;x(wHxf|=2vuCc@s!QeXE2fkPvEL*>WG@3vk z%J_KCfBD5{`Qfj=!rYE}!sFkF|IuGqa%?+kH~xqJ&%!|ZG$X`R8F=sf@W0|5vn6;f zQKPJ=ApcME|J66+2RtGWS^&cJ;QtT<MtKq*ar6I?!E`vY+vES&`2WWLiNKSL!JWLJ z_`D*f@DLF*ffH};aijeW_`ll!VGU_k7qJ=ITi}0p&v>`|pYk8;wk&9$2Gh+#(pbt! zaZ7L@1C-OwTm^ENMkW}Tibz+@;e%7WG-2;U$*FWuK|4qXh;UzlGiA~>qqMF`;TO}a zq>><1tvvMaIoV+M&#~KqVlB3sM~V13h)1ad5(_I`>7cYe6%;G!Q9R74D4&)jjbGA_ zJ<(`#Yv+X%PsAN4<JMGtAsuaLDiJ#!L$4L0GgIHOO4W$F11mJgR4IaZlOaR(sHpcS zmq;~O7(K0G$k!~+e7h>x_etrHjONKNQEI6Hh?}vOIYHK+z5hvXN9-U6AuN}!7=jvE zh158vA0((XBcvsjelCE>^yYa7m5(R`F*^XM2cNx>js?afnyufnzbzF@N(2rt5B{~@ zC66)MY}PrI7kFH6-uvUf`tR3Cw=yXawkop`w9)o|YJrFFHL-~dup*NY2JE(d9y$YA zCyGing@%5WWJ>3kT>HEVVrK`qPQc5{l6@uQe?s@YMUG&KmHh|=a)+~Zu9NcDPru#< zU-UXB3;SjMA1u(80jA=^<s0i2K5OhJ);5(yjsJ%sWBLkT&q@ER=ls8oi<nb}C!s*q z4#NTbvA{6c+5ra+Xsj$-d7%srlNSX&gDzqv3~$Zt=r<KsOtCr{wII^Y!6fPvB+T9; zx4kX%IsePziOe>JQzBTvjg3o4v<f^lzVdnZc<r9)T>oGANbZTbWHP9pSq5vvxwS<X zPU{o>`$mk^E)Jj+NB%DOKZpN8v+sugC9|3HZ-f64luHcjV3Zu5XxKV-|6ku~7v1`| z_&;y_Pya6l@)c<oJwJ1Z+4KHyU6boOGYz89mcT~9{oMbP^0?moffc;s?k376>{(%O zLrE9~|0hReG8tZjtPq~m5FZa0e3t*qtnl~-{NLY=rL}rxVfg|+#wl=xm-5|kJ*H5# z-?P^56Ml|@1Xfd|Hsaa#`rgH_5BYEY>X+;F$cHC$@NC?i^LwzW@QwICO=6i8Ybpz@ znPikx4sLn6a~<7}k?3B#AK_%NY(kOGET$iC`RXtK!;e0}Q#2@g<?Hw#^UFK`U;lZI z|IcUnzi4hM;&V=GWl!y_g{;0Xp$OS8r;vIy?$nY_`fh{>CFuRgPUX;xUU{u(eWy6* ze?sK>8vaM$z^?KZm)`v_yn5>YlwtKseFgm=$}7e|=KpAGcAN=cafHWjk^eVJ-Y6LV z{TJr{_#NT@7@t0Rru2cjx!O~CodW0x7x*|blh#u8V^!WUpqU^SNnK#5QY757#;__X zHzV1qKALc_g4)OeV{$9NPZQKV;NhI7FGH&ek%Yh?Bs^t3(@dJ95_no>t<7ZDSji)j zQ#NH(h{kKskdF;4lI{TKNHba+c(per#>#{`LjC5G>sNw%(B?=HJ76wSSkLv<Rtk?p zB+1E7fOG@!G#SEBJBYc4_n^`h4k<GyKWaCAwmK*La+{aJ3u<nzY0n~vR|gxq13=lW z9f~AA{QOn?>EC~{#&f_IJhjkp+>66~*hGM!hBr2ra;HK%LEe8^{?&zupq7&NRE$s7 zw2k{q>(s6jh$mQa77$iAA{Oj*{o`+b@oD_*!(WAbH-dpxj^0!LA39+fIlGEp48~U% zIRH9<)<n{kd_0_6oSAN3*D^W3zCV-2En!vrV6D^LQ4QFY*F~$Cy~IWyHr}2e6M@*Z zef@ZFP$1DD$49E)H~7C@A5&X_|EGv`@G(Z5MRoO97^E&ITNm_ec}g<o_3(emgJ$CM zq2Kl+4pq(o2Lg2)@I<grq=euQa+nK*Q8R$Hc}%&4XEq{2_9SC7uB4{heo;6HagY8; z%I>n1rupj5Uw&QV%FQCQMcHY^HVYMFWcqa8Fb;R94Pmmio2uJX4EOl`H0T{2;@$B- zeLmkI|7#w!{oexrdx9~Z#7rjySLG{&(NS}hgfYmHHM_e{=>R;3dUVF-V?|dulUVhv z|Jx4>8N!#OdT)>Li2v7aMw>TR0XeCPr|j2um$CD|iyWtALqt+2%;icVj!9kR7kB=T zXZW8!4F7)v|ED;HtF9K%(SEaexLr5}RZ)|#Py&>re20g>#xt0LLl3Ekid0hf^ACTO zKe!$RIFXyi&i`SaC3TI0zY+hJjr#<?mjYN8O{Dfg`u>)Jcw8jFJ>1@d>r>ro2t7Mi zw5GZFn+q;62>XMde-<Bq@yeDc=u5L2{q_70)B>jROswbmKf{YT000Y-#^-uAl&Q<C z+qFEkj4MmpdlLUsS;o)Bg9P(gc9s7J`Jb<$v-m&Lu7?cNH)E4|Lsu*L=KnAl9%#5v zilZfjz@k^b{>X3{shj_Q8~*3_$^X0~{vQW4T6qWjPbm~b60<5^G-Ibix+4Xq#4mjg z;+1dDq`g|gAY<$;?*g7%Su0z?!nK7@g^LWETrnt+fZC9f0fz?*%;J(&kqJ~Na9{nB z5Dh59Veb~$49};=DugyfOHWc3F`qe3f|bg4kTwEb3=|GMThs#X&vLe~(4aGC%V3Xn z4k$58h2-H7c@SNS0rM7DU>J?_r;u!`uwlMt2$l9cVE;P>nv!TK8Ne5);va@li9uis zAUjcCF7BG@CSS2aLZ!FZfHPEB0pXt~mTU<8-SB_@=_m2UmnXaDjz{Qs%VH|=S)~Mx z@dOG7JCK!0E&+O{IhD?R%xQ-ecJx0r0R#jt<DO}n(N4c^*+C-bOeCXLz{g*F6@UKz zCwV<KaDWgBThiKG%XD@9J;9FDmIUG1y2+_&7S$^(iXT*%(Lig0x$D1Yh7RqsQ=~~a zBmrDkk_}dJ7S?#%^D~&_BvCnUr=X<F=jc4>s2%38Bn5=O`TyX=Y}qIL(EnlEV{EDm zulGE!cgeiy_CctAKoOT%1AwmA@xcGf4iYo+430A?!(c$@v2iHT;nvQ?$>aT5D#}bZ zI8o1F|3pffUJ&2o4L`!~(U}1x$6wa}5PIDRKW>TS-tm1L<82%7lwo-Uu@C-VGiM`x zcF7&X3?}b0EpZp_k&UNf&h4)BAd_Q$2mBwuBmAEj!zGtvR@1&M|Hl?wr~kKf9d#iB zs4)_(fU7Nn$o6b89ck#SP@KybXkR$|8vl=H{6FQd>9QNuDgmB=I!!uEjbuP;Hy_5g z#R5h=PD#Mg1!ITF{u(2ap4e)JJ1?TWo&Pfi#p;{$e@uaOPqP}tBPZEUAc)&f<0<%& z@qUc|=Vn>6Lu!hsvx1AjG5-8#pTx^o53!yR$l?DZwKr$IS~_lpfS&2=`M<)j0@vgX zoVeyA3{D+7yjAAX@68;vnPPa^ce*KY4HF^kL~IVn?bmmYiGTcupM0|3LPxdqZqzP{ z1!DP@>6`e!x}U|DN1b{vu_fJ7uAD4}|5`syb%UizyD<zvA-ZUvjXSfDqu2Ze=DzuV zXk2^zkFl2X&Lua*56Q7U7yn0COcApKA}9WL#Z(S|u2}3D|IeHMC*uF0=#KJ5!Fqz5 zF`W4=`9Jmh?+E|%t{VT3x6S|XT|f1z1vHJs?!-38Y{3Y$ssyNMZN$@F$&zDzX%!xr zU(He&`)@kB6}W%_1O)4Df(o{u%4O>|tTZ~us>9a40v5r`_{=EO0wE3o;1jBjmjQ=f z^$}A^?8(fE^U6Wa5mX?y>d7|1QCI6f=NKY`n*vB(BRj*#vCEw?df$!Ls4M_xpfZp~ zXO;-jMi7h?pzXok^w<4Qu_AH{a(Xbq=bKs(x#3mRI84&e?*R(OqC2}yEan6ir@$vB z*iu!F*Fe{N1>7U=)OUaU$-3vTZqp5lbKV`iF&jQwLZEqWuG^>#OIUD=+iGm)gzG7r zp%1U*N-%znNFO5O81AwZkR9;<KmX~c@yW|qCSurJ%(IhwMS%zJR4VRNd{&!|S_DDD z&9X$(tA~_LZt@1<t;E^3;O9PBs>AL~5;(RAj!cPnMcvWYK`?lQXC5pB==pxjY20P_ zN-PHTi~zL4-p{VeUyhsq_o}}I91$X-HPO`3+!(tS>B{||pM~9TIC6|G<o^sx(&uIV z$VDqA9>?q&Sb)frz}4QG2a>L_hbk<^GOUcdUAo<|kTl=rCKKGP6}_;*lk6vvM1!1B z7npkeq#XBci@N&&8`k~$>>M5KZaBY_?=CT)^#7c!p?p3XL;IdQqbwet@NHVh0N*A5 zx7813y=(p-oAma7J^qhp`2Pli_|1H+U%uXxIUwikRkac1$baMiBKX1QDKM<?)7RSn zDb}y?e=SB_?HtTx$A02~{~H%S$HzzrMjuyI@>KtO5tT(w;UVX|`9CZqxNUgwt;aK? zh&S+m#5d=EVg|7uSfKcaizU6fo9P)NjpnG$#**mN3z{<zjswZ-@_$ZQJ79W_k4gWp z9v2|~<BvXC(pWnYtL6&L$l{ogVB&TBA2*M+duCcKKh|=CFQQnI3RLMi6K`X7bEFm2 zx*3-c;mNB)ZEafRzFs?db@3m5^vQJwQF;VW;rjFhF5bZZ<RNtw8~>N_Kl@7Cg}Qc& z9Fcf!W$O=*IKn?>muv>#j4_zg@oVP90TwHV{9pZ(|7+s^n)pcZ|5CvD2L5+EwD_M> z^!T5ec{cw4CjJkWAwudN_`k+rlyvj|-IBjK|H~We9rJ(u4)K4i15_>86X3=(Kino! zTR>2V*tUde8%Su7Qg_5XR|Bs(*{0p+MMV!%GiFy26%D25c(;7MGHNr%d?HS*3Pj2v zYBR2G&8#t9Tqi$*Qu25@QIlx^ATq2ZN2f^163HEk1Ks<J*2OUf>D9EZ-mMX38*ue1 zEuYnoTcwT9AtBl5N_B6+_c>#~kE*5sN|5B4p5V`9E6ZP_mNm{LBA^q;7)SAx^=te% zgk^ojRZ#+K_i+|kiNIW;m=lS+3N;We9jCWQN0weYN54)jAY%nFIsX6r{%7&i4}KMz zsFGyHp-sR?SXr}^BI&B*Gl7%><H{Ro22Q+|g`@%)lBf>enbiOs_&TcY!vHrd9Y^Uc zxQ-Se9E?#FqKv=$uV2*P{kPAiGP!J&MGg8-qlM`twiuq_3P;TXLBe>+bJTgDAM^Ln z)_MKA+v?0Q{b=YGbJ{AEC$WSa78?TB4%kB+5c13dg|d+QB&+sybN>6>i9ui{(-T_7 zTxg^8|B=V+jPTnFwkUsJIzLwBhOhR_v5V!y6X>@YviNrH{*U(-_grg&p*4>Oea<=c zaDW-xc_^{P_1X3HE~bo#&^HOrNq4x2Z7Oe+8)0d#IKC*qMR_a8wt=C9mx*f-{3QCw zDU^osSV3;m=@2l#xPCdvCo|eol1UCgTkx3SiI`0_8TvevTk?|-u0*92!3OPf>7A>8 zSzCfs@fP{tMb|L%ZuwvB)^E=L#gEAULp0lu$)_N%?Ula3+vvpNzRGyD_8k9rPs9I7 zIW?<)Bmb}9n~PQ>r;4`l*PH+A{w$ohISB?xO2r5*dcd!9yuFKt#AALAtiK)`yT+09 z<=)w(`SyQviUM!q|9RbN81{ZG|A+B^c#$H^7I<H3wJx}eTn5lZ2Kjk*GAIt!WxZ2i z;MI~7MTQZ7``^B(Uwrry=tXYJ-o?Vy$*ix{<M3n<>MQgyKaDNT|As$WI|7&+6xaA& zO~23M3eL8ufrQ-0(5#(<GXT_l|CcZ02fuj9L*9n}kwfP`4I<ZT_&>gu|4Y-Ge`u`7 z*h24g#p4HYgVRk+=mWMDn_61=ECh}JQ;-Mz-}oQ5^AU%3FN^=z7|s8;iQA)!!8T_L zKEEl%@*-1@|5v_{USJsiuW74asjSy<U<CenQQn00oAbXoxqWA<))DW9|KoRr|E1}W z@xWkbQmM(X_T$FjWDO;OCpVv>v)Q&i8NFQ>x2<1wo19nfwJ^G@>(<T;FeY&5%B=$- z@HshwQq7Wy3*>P}5!^jOm7%n>V2J%JOZrLwn!8Yztq@U2q8jIbN|7KEt8sw?L(ZBi zrB1DXD$rSt6}iTwV_CH&D@}+~4WH2-n+I`KA?0By$&PaqkxLGIU2&8jbTsz01t!u7 z#*%pM&%k~vkto>KmN50QYz2a-_H2$X!?g>?32rTnEkGd7HG!{+>cbiT`iCFJ7hj$6 z;^<fQL`LluHy3JG>DMd#a(5_gFO)a!$z#$Mai46YCU0wgEJr*w-)Pyd{kZu{1s@Zz z6{Kz40{9-@$1lH%|NIXhb#*dpmhunkb{n@6{3(zu*GTR9VF3#IP~O*=lAPzDEmu^i zmK^AxS&>@cdtR~5=$9z%7%iU`dz`ro3M0%>98LA)E}zqbxvY6^mUFkm3^~xzV2Q{G z5*d>yYpWdzX$>GZqNGT)a?w-k*D8iJ*yJt$uXA^7Bixi;ZvG$U|FQdjx_r`!o1Zhd zazQvp%=JJkn)ukxd~Sgz>M<fD))at~q+(&f%&=H#iy_oD$J*?qhR-)f=|XKh6X-&> zUHY?CB?}bf|B`Or-LUXIf`Pb?VYamQ_47OP{g#ch%kKpLCx&RUeB1nw#`@FxE%HA( zM@hs6%Q1yO*vL6XpLhR{Nvx9Cbl&`5V>{CUZ{z=G{oj*XBpun4!+|Kz_`j|20;#!5 zt>xi#8{*W8V|70&Ju|(AB>u(J`nlrXO+?#UI6gk~jrc!r{69a>@&Ecr?^2K8Bbn<_ zh9`yr8>i71$AtN636cvNvok8)+Y|VmVPou~RR3!pf4d&P`*(lyL45rAS3M$9U||^` zXy^UOGv+M*k6W@wy$D*d!=QZ-ZQIj^@yyRu0^6DK;Rf`0&27~_DqZLCRDT@x;b*T_ z>~-SVcirOmumA87SPQEwY5OkpujPM-h;V)jyftBc2EUucJ7)}>=U#Uh!c#Fo^PTaS z|F<_GC%yZ>*lYt--)<k){@D4s`~Ucl@j<>J{)d5sK+@uW@pulOKlOi)l}0DL{x{`+ z$f=)rX>1;RdE5Nof_g?@5$}rsk6^$V%pF+82n#gZ4q}R+h^ji^EE#TEadGE&8Kow} ztu7F}2oub!zD>TAgjHZhQf!Nc!E~ZFp=WR<15P#ekin1vgJO616JE`vaS57o3_S;P zGeuY&vma7Z5e8s(I%kw}whZ$ciDHbQ^&288?E1Mw*MNvxIYXMyW{YtO=5#SZRLtvi zFxE9bti<<>5!6$zs#vBEDLG^m83wRQGH6*JGdJo$&;3QtO2aYzYLx*MSb%bOjBSe- z-0@zHRh46siTWL-=jIcaZsWtxzFI`|3aD|B*?J=HfGyng;GZRXU1q!a^rop%UENr6 znuD>x{wzR}*cSmL&l2d_XQ%L(KT?y;Z)t2-JJ*TB*Xi#6?r%S;YwLeOcd6$}k0;>m zz*Y!qI@r8Uc0G(cCMGa5OBDk==9d502(s_fEAwU`=M8kb5V0Y3d!$uq`QLWxc_kMy zV{K9BSd$#FF2HusbNb4>`G4`XJg&ciHu^)^E2uW3z{qM;gKyBs@||<P0Vb|3h-iiW zoKf0Z$;PdUAH>VkFfhamhs-u@<*llOVZDoBPaFU>Nl}z>!`M!neB&&=uM~K4>;>Gp zaYpg?Ql6t^c5SE0>9gf<%#JPEB&=YrD#!}~Ii1!E$Z)&5>wp(ev9H~yd9#A6-7_Nm zHr2lAP+IirKN{4w_a2Le7F^a1ynLI9I3)S5_@DM$@y_@^x;PW6U~2f^lK+|b)$|{x zTiFr&Dm-#?P;EZd0m9w?og*)duGonu;g2z0J@J3?KeT~E_Yi2j#{cQ_G5l}-zvfaQ z+~1X%;6HH&!M_Kget@4?&nA{cjIrf6<o^`^<79nda=n)Sabt*ti~3a@V&ml85R(M# zu?yVdl?n1qXKNXJ+K`gfHvMOqiwWn&#ee))AJp|mo<lOc@qdGDmW7i~_&?+Uu~p+R zBHDd`fVm?Bf=3*~8+@S)Nq&@J_GRMz?Maun@&)lq!^bBtdiwd@pL`M@em<+n?B}fU z3q%5&|A+Yh`u?;Telz}W3DM3^x%W(uef9?_-#lq+IS{^B&uk7!_Zt83|Mp|1kwTKY z|DPlHv|)QY74ZKOhol)@D-u4}X4lsXy1)w_|6_4X_lG|YnNR&cEB$<f{$+nnX~ehZ z|KW?@0sfDXMB~Ie;D3zdx?Tr}c1e#nHkc;`;TY1z^vxey4paaE3VSZ#EIq@iP@!5P zd+6~QNn_LkqcMr3L5_8sSTrHFO@)T(;WZSUWkXb58-?la29fSADS-h6q){ZLyGx{F zK)Mk`Kw7#>N_ZSPq`SMjVHk!v@4TPp2b{Cd-gED@uB!@5j<da2HxsMX5v8#2r)t+| zduheV0nLHO+X%cXXHXM?`;W2Pif6}%d+Mfx2l`RGP}grU2YH?sf9*?q8TcL48R4ov z2@L~$#d%UK!90_+2d#1})kE@K|DE}T66lg!UiSOAoYQGHY}Ed4(Sm&ovkF=%QgwUV z^2XSYy&f28WlPZTkj(M-p~|;}t$+iJHaf=9xyFe#a@#-;W_&3aH?-mdK2-ARsoL># ztL(q}IoGOIY`6|PmS9}%|C~toWTeGuS9{x*$1a`^i{MMx=4|S{Gxu7C7|(WX*0T2* zT&MKUR@NQu<}b-HE=u)SQ#}^n?#l+>WxTUJ-?~nSuEbFK!ps(QjIh3Du2`y~AzzF- z3+7@cNm@?Nq_9^l9`5zC*jZVPX?UqiqMk*#b*a%j{J~9~j-#a6s!Wb0$bJ$z-<1!P zk|Lt1GJnC8an+CObAM90$gxxhvbSLVmRBI_W;Z{Qn`<)s$`;(C{2>R-j2dZ9C28{< zApIK2dHn9Bli(V~{=M?0FLmJ?{ecmaQU-0bzjIwmPDD7x<%qyH#?8hyB8t_E9zv!Z z<p^G>#bPlpLb8>Tcycqdi%cMTXbO0}2@qthy2k^~edM_$iTsI4sq~>VTQ*oydGku$ zV?@3HXvS`#VxJtr8ReyD=vPh#B-io=NKh|>80S~eaA@l8fbFd+($(?0M}vy@GoOFe z<h@2YAw(NzHLVx?FWmtO=>qgLIFLuD0pN}g7<}=VU*+m0&oa0c`6D&)`<3u*xgQ5i zPU21b`@!A7Um1H#UO%PW8+4|Cn*efc#2_PhR+ZG&K!W$6AJ%#Wo}1UEcPtT>SALPu z@Ara;oN>>(IKvPhdnEr=K`}WLu9mOq8qd!<)2`2i*hONaAAqZPRQE$i!Jz_I1OBPK zDJ-qlcKbezN5#%4;6wQ`6x@j}qm6(doTIO7-98&GFz0n^bq3J^(Q{f7!w=DN2<w`Y zYtKT4LCtbfBt|b4x(7%}E>053O-S?sws6Ltt`7osd_bMwG2nOWQNO=(1Gw2=Bmu98 zuR#|fEATqN>>JOyLd73LO3$6^N({muu51UP1(^<<jR!3eHvQO6t_t6^BUDgG)af*j zLHs$Z`Y3viHb=JPI_NLn5LKJ(60+yvaJSpC86hff8IrMbaPe#i)D)O{y-5~eh*KVm zx}U!Su+$rZzc^Zj_KIg+HifMeW~259{o;-(9?p}cUkMYFS`VFL!Yd>ir~OnqN}MNC z`71F@pj+8`WEKz1X{M#--x1RFlfIV)Q;Yb&R6J=^a(Xs8gcEs-SLo$CBtp<kX?*xn z`0-tpm9}D$Hh&aB{EzH@du~Cmpia*J-;s<n!nd2=c*OYOJ!(9d`=MCePbsLX-TpM) z#>|P_RV^Z}xy<H=8ll3^+OXq;o3UxFWj!7Q47q;iJ!TNVIzR}nzYjaf28lea;y7}g z#QYU^33enx2VL1_Q|FesBJ1a)oWCSKiCfb(dl^%6=1`<13_U}FzZdDPeQlxUc=tS2 z8N4#gBi@==7>~a2efbSiYzvK^`Z%6Ml`C0&N@aIB`?AOf5QKsbS}=XIxtKfVS|;Ss zy5Y!M+gR)Spb;Qe=+>CgxEm-`Pw213tI@Av&T+=pEqww-S3gm(lejyiy#2{1C-z_0 zl|+)dMauvs(|CgL{Hb!$J~u5#n%XiSDT8+Q<C~+;)*O8$#m%AQMxC{LsK6K>Yv(;U zF++Eu_gpdz`n~Djw=|iuCnyv5XrHL}D5sbxLDe&0dJUdUNdDz-57#c4M}DVqzNmHJ zh&gu^)tK|6S7Lsa`NMbxSRBkuEoho^Dv>q8{2l>({w6UkdzmdD6yQB}!0z77zXL2e zz_XR+vVR^y^nUNbZ_xDE417gK)QP+|i=T1<_2;`|#5nLsj}(Z2gpRG@yl$`2hsaL= z$qkEOnF(L@I@G{#@Md0SN7gEH7ywI<AxCRwIPgA{&Fj^AZD5gV(xoO4Y*)oxt9J#0 zm=XHP9xnvh>cwu0$dpluyKlZvil5MSVzv~ixc1S(6sDAto-f34-tVtK+u1|B|1h~b z)RGl^cp26JEIsAV`+{#(x0@#mb3dn8yTQ$o-q?VON2hU=b2y`82pLWMDG|%@kLxb4 z6u88S)LnVfBWP0r!KnQRviKM(6#vcSGvet59CQvBG)rvDHPVeEv~p=e8po%9C9ugL z%)MmB04cm-0looI0Puk`GDz1}Arq=ik_s=1D@E2xfw$P$*2Z)$iFk`%r)q~M)wy|r z4Q~=u-SrZ6SAHXABYfZg`uuRrj^mIo><s=SWMQjxv**y=NbngZhc9lTjCSX;Gd>&S zUy~r}3*w_CQ7jt3f35y`H$ZAY*mbbxrLRrhDtlOLtXH?67ux+Y8E7olwXQ6`Ro+(0 z^9ALq4@YNW4a+GdI~A&tLB-WXjZ$O=G{TPmYRAUX)qDlkZ!yAGs!${G&3Es+xLGA0 zIZYd^8_%|%e%dFgX|VHW%x?F=9Uku@28bvH`)>|0DfqTG`T7Y21ib=S8mdyQ!hg*o zEQi4a+-AXduYISm4XRw{aG7xC5_8CRqXJQtQ|R4h^3N0G2S_xvm;<|Hz?U0xb--;} z@@siDX!O@Sgje-Pk3j7eHP}0BVQbnzslLAUCri6ra+k@d7}rrldD#W1`E1%8(amL( zMP(J?=`IRRV*I>?X)Za72`qF>HxZylMq|$Paf>~=(1`;+N#yu*MHqO1ZvYVh5~A{> z%tblm7{Pe^V4eD1PfP^DH%k0jMrXfc2d~(!4wz|0o4@INyc+`^{d0KkRYXlFl=rdy zOJC{xulPtHw1KS@J6?-EHo-U2P?IT(Pa(98o)`paE12Z6kB}|HIyNDcx-kAF5GJLV z;A7RYh&dtOrwS&WpVd$$IpzNbu|`t1=Iv;}W3!yD+i@}M@=B=JZ-_3=u7ELJqy^bv zRljq2AHomtmXYL9d?<U)$H^3y3wREWTI#egp}DUHtd+g+U>x90(>rDQ2Y?d}K2_dh zFq(J*DM`5INo@>Q{;10d=OEoGRG~K6zutA_AHDg`W_)z1RGVW(;JyoXInc7idM0Q& zK|ltkVrQ57$(#bV^(iQXFzqHUgJsSgakm(Xn5A%<7Pg~Gws{=lDreY6&mMeyM!Yxj z;cM@%79daxRo-}UVWVZfPtjYYnQu$q$PTqMxeUJi#<3;N9<=H8v?tdzP0L$wm`viF zfV$JXDAHhjgbXgrAl5d5zY|A3mese(UK{nY01K1uZByy+{Nd_Bf#-puE%33IkVjvu z;RPQRP3Ip+`>-aop&ptml$s;+n*p}8z<2|9>Pe&@>~~F@diu@UBV&2a3orxnu3)w` z+WV&Iiz^+K2ew8gP=vCs?#R7#n-LH+Io6HG3ZVs}hq;H;J*rxV0bkmt4&NT&&%-3^ z+jDWsR&Y}z^LsvB&H!Wc`s;t?<GCh0Sxyni`^0gTh*cWy{L>Bz31Q238vR%=%YwP( z3lzTVectCKGS2tytLHGU@TvavQdnZN2bVL(gzqS~oUds+LCte1NQx{)HA^+8wY)S< zoNc?h-hAb!MN_*JsR8Hd<7)kyLrg*nGj9EY*JyY`QT|*pkyw_cXS^sG6~>DXpZX8p ztWxCsTkgTr!N{MfSq>;g;i&4liPQ;Le*ZEiDbuLlfn{5;-um%=PdTedCZ!`pN;{84 z1~YYA)GC=8Bttd{d8{Jd!A@56PV~A<$fa<WWg@TY>nqSB>=XJxkE>b2N<The0Q(sI zh+F6&$VnPDQMmJ=0)tzEwl%Klly1wYmOLT@1E%--t`~s$Kd(SwwjurK81nK57=`M2 z&l*dlK`-8t^*B65cn0mG2L9oPQuQv=V|Xg8f5j$FeaK(0STML#e!=Pm2{YCw?dqKu zb-`9gf()N1qFTTubwQ>b!2xMqq54lk!I%4h1lToe?;J=PV##o88vOhS&Q{D5TJ|R9 zK|CTaa*oQv8T#YIOOYG^FW`osSo+P??&o-3mX)*m!MrA}x6_*GolSXjtZsRN3KHQ9 zUZ(vup)~ryVY{t}v&mxhT(6MqCDHBJ#c;HXqymy%DTb*CCHt81vjD`wk)&lc3N&2O z|9pU={Q9Avp+vjcynH+8(-@mO^37v3Ld+@qp<@%Q!@id5Vb=C~=X?WViZDndd?_&Z zOa;w9v$itv{v=^R>|grsRHJxwaXff=3_y-sR&CZ-Nu{$8Cy$_5@U!MI+VCzRBL5aV zAJ(<C$647LK*Z$Yx)bg3n5jzxn&f8TYacYhvr>*d(_FFuWCUq_QIh77~#?jr>?1 zYIF2aXsIujR-^S?!jaeWgJR+V{e7dN05|B;ZY`DW{OjrB@v`ZHyAoHQdh7)xz~yWM zeusV4n;-l~q-LjIq3h^5dI0$FTKvSfdi{YW?fl#HI}n2h<<TCIsSsDcYsHEYhJNW? zM1b{0RS7!geaN3dnqA>iSkd6(@!Z=hCh6Ye?qMAC43gE_{IooVUWez6?M?}V`4E+K znmXK_9^-jDY^mNE3M5>9C*nsK{1wI0m=x?Uox>)04I2SEfGcoi4FP}Q2&?%HFhlJ5 z0in(L88F|GbxIT7E6Ww8QA++h=&#}8oB5#Vcg4p-fTu3<!x*rGgG_$?UB?8%UyXj* zAO-5~CJ<zFRfNsrZ~pz{iI;g1Num_+;ri3fHJzhs>Pzgx>FFQgLOnUli|yG}&t67? ziSnR4E<>iYt%vl8NZdxQGH3CvkGyv_^e!(@QxiCBQC`h2tX+Hi(TnT5*rT05{v*r7 zf>F-8&`rFI$^1Yo$i~8wf{;QXVc+=}T2Gd)85%$1m!d^pkavM~l{D7QR=*~2Ak~TU ziTix6QamE@U+^EkNNJ(pvj><?p%0XAoJPm?N&MCupYkZ<bq`xPWrt0v&{<~f>oW`A zaJE?Ir+}|7sv?W<Wx?<T#<_e5uoSnFq+>(k5qi{Kb$VI*OHgTyX;@-U<40sKTs~sq zLMBbp*x}=qyHsLc00RN`ACv|IlqZt6nR`eaUlSC7U-kwqwmeIOHu9+YUq=`c2R%Yx zy|iww$))<Q>T4A~Tk}67*F}R(*o~2#+%E8)OH{ok&QSxs<NU>c+Tq5xj!n5k11VBy zzkeGxQ}9W&jHBjNB|9bMKbJ3-T;~Al!ut#A1Lwf%71KH%**SgV$0cp=SHIIni79aQ zfLHbc^p=Z^5iIAakJ7-zau-dqq+>#TChqcJRC<a#D($D{tr*=k#U;PgjKsG1Myk$( zE=UNeN%eM)(pmpKrLNY@B=3;efGZD{HjXd>oPUJ#B}N+GPuXg@daJ>RY$;zZdD3V3 zu1i2%=i8c`z~!{$me@C|(uS~r5qL62_(q$|J2<k4(3(!M1KVH$cnxhX+t-LXU7D#{ zbj3pGuM(fZE7M6I<o3XGR$YJ{jdOLGTotbeC=+8a`x$asxi0ygPTcJ6H!gYYd9SfO zORxIW>reYqe@C$P2&&nBoT(Kc@^*mt)#*xhnj+%^K>H(zu&{7{7O~oI?aVgVqC`KO z&7^S>Hn~Z`Z$Uf=UoY)hiycdhjHLWH-4}&)^L0`~%uJr$f&C)ry0%=!O(9IUS||t& z7*w!nMp-n}nhu@NiReb7MhxK;5FdX-O!%njP?%9sF~YuKqTV)lO&J4qVZh}<{sAL! zXMGL6@9y4RUu3X9dr~HT=Sj**I=E(;X99_neir9fv4nzjDWola*SL={9cKGz>C-&! zcBvdT2c)gtt@zIpA@;QlQO-yUBHcbBh&3vuBmWjI>fbN4uNxuH!)O$m&ZarvwJ^w^ zV&5W-?lP3f{gM>GU;n)M`3C&vgkqu;5JGT%3&Ka_PgJxZTOhY!6DZLm1lyOUQElH= zU|Lcti3y!|r$*}-FHA~Q&ZHJ;x?w$2<RnFz)%hxhmqogAK!WM>qT@?+*jvPoFRxWH zRuE3POU{|@kpeoUuG2Evc%!hUiEw`>A-9G0EC=q)RZA;EzIY47&w)m$T1ZaAo>5{C z7P>k1g+$+nD;^bVooZ~_c_&>IB=M?M?TKu%)w8w>=I*wSGOV1syx4+9ocUSGr#QA4 zb+k5EQPME#QGb%oMZZav#WYSPL-7hqx1Er-J>`5cL8Z@T;*7Y7K^ev@o!{BRh1wMo zAJ76L8^N7<8Ia1xTpG@B8`QU>;U|SW^m39yVVT2vg6-aO5c5aTVr>abE>77^U~qLq z)<2bf$ucw0=5F+qPw^*H7=|rJmg*)s5ICF=MSZ0pl66E_z(v=FMlHr4V~W<XxHoR` zw?ia{c40y)#0}n9*-=&{k|jEq^*9pqw8ePP_Vk}8I^QYIN>gS5<IFKPedO$wQax}g z7(xqNfPlS`%AV!qpQ*M|-6>y^?+nepyE=c>_!i_$K!So+HxTNaZd^9)mi$UG^m~ls ze*^AI#Otz)nVEg_e=omNrziUFf9*2IHJViZ_^3JgN&mW*yC{8+u_s6FsQ%D8(lv-7 zAvv)fc%p6%>OX&;{IJ4mK(-0|WhvV>_4WJ%v?7W;3iR|W?WbpVjaQ>new|v>wFF{S zusy6k=PU5XG1F3E=o(&<HM%d4Q2D8qm&cY!6Moc8)2d=&5NgL6l0r=M=jdbIggR#X zRfqjFwsU#EW{mUNXC00DW8MLz%~>)gJCE(1v$QJ%jW)C3DO6bcTLO^<n_=G%*MF&G zmQkQG_U8)Et>xBT;5{u_T>ZC!>}x;_HU~BLQuyo5iy{BVIXMWs1(txO*afErh}E&K zKf?0|0Ll<9#~0&;3UwbJCZudgUjE$%GL?6Z#(;NB3^TfO``3;kvG@@)+`AW2|5mfu zT*F|C$DTW2HcnMmY2E~YwT(p;G1$(p#`G~(278u%o3zF{0v@^M5RFbQGak44UD%!x zHE>xeE<jfpZ{#a(nSIzH?C^2#7>uBXjDqj$z_fR($pxMrf|<Ow?h0;4kOfC+JS-{| zoA)@9gZ1k8x}5?(q~DDsjZ33_cF7N$fPXg}b-3oyyyn9|bH}w1;y|HiP?|=hNSC1L zuh;gxQ471N$2XJ*Ht#`m)HFINiCPJ4B4Bbuxhx@>$1YxQFC|KtFLO`Pxqd2cVN|O9 zJ?Wq|yiV}tXKw!sIO7<w`FAOcCK+A_1vA+Crvo)WpLGk*d)1(|ixY%4dd2TQO7FSu zSRWaKRNVp$5Or1z?=J=ctUu9&b5Vo}vzdGI@`X+;G|kF4>9OuBJELVRx)l|Rs$^*x zT>SzA`gE6SOIk@O@#Vwq8VJLjlvCw=Y#wge%$`L)2DL((x+oJmzYYgMW7SQfh$Tjr z0<!lQ`NG85gW?)BQv|UXKkS-Zs)E<6^iY_v-qHJv+LWZcwGrE_^)yM7I#-FPFvP6N zf7O9WtS6D%m{MQ?S6*)Wm$z3$zi*~?0Q1zB9J^9jy$vQs85*%InTcvv8+GfDlHtu9 zj8aK9zpw7t_36~Ds>6U8T!8^<NQ^oBeuwU!yhJ7qDdOZ9Z^B?%*w7X?BttV|`>=hI zkL7TC-0dtBM4yCNCZx{?7*CrD&?oB&AjSscz_1O3X$?A1*=;g&#x>+pzIXas%7S)( zp^4?oKte+}9o;x{0ykqB+Mpp@^4Xi8`Dk5-n?#MF<D%dHai0BsClxK*Gs)*?<rcn8 z51zW6(vFPYf5!-8Q%NB2W-dnU$o)kI4D1HT`@n(OHb2d$(X16IOv~RGzXe_oT0aH8 zCvvrTH%=o%LZkPz_PwFF4Li-z+SK+alk$@*_R#Xczl-T73--Zj@|sx6P$1ZQTY9aD z5IkV|<-E`14>T>@zI`~#*PgcP)QUsg63#;-6-QQ-mO}(T3YSe4A4kbfocL$taViz` zVe<#S!9}~gGUvP8Qx2}U<-YWb5#U=dke#^^4k(e{c_Wu!5s>Zv^SlIn2?VfPOj?5u zfSvum{)I=74?^&DgPbzy4!~V<Ta$kY{7|AM=cN}f683;Xo~2E>o-1;v^Xb$JaTIU~ z%-?6+q*JvKzxxKf&D=n2*h3RSa~7^Cvu=firD%MeZ0N#|D&3Dwy<-=NAFKv<**=A_ zeb{o?c|*l5(H71pbJZK<6~B?BSNk^(N}XXYtdd`pnWK<=o-)Bi+|5|el)^5JMo#JW z;`M}1Ep<`a)yX1*Fr6*swIHgyN2}U^``;RvAg}^4bvKaK`}4*nLqD{<x_#}6WF@@X z!}=1OdqQsAT93N}sQ&zzDf&mV59{4<J6Xfm<u|^YV5`YkL-#lx;tm5AuFKd++wj;x z3LyDV16EGB_mWWz9WyC^53IMVVQuKLD+x;_tJPUxXvGxbZBTD~iZR#$$^}P&uimsC zY-`7+P>|h6<Us;>VZk6=GWyuE=SGOCe5f^lvq9Vc=fH!nBt<emMze3lU=F(7!hhlO za$wffd1R3HjNkJ&mD5nHK){bMGm_b{^BC(_*%Z~D^Gk;fZ!A*LR%9JevIQ=k?cE7& zq<CkG=h4C<q+oN)v8cmK%dhm><tAv!yR-{r20pP6v;aDCT4Ao~{T#nJDw;KQ2IeXM zE#p9IX|~B!k|INOITf-QjTrZJZ2O~QuN<&lz+}F0*OTS@|J~(8P}S^p{Ph}w#PCO= zDKcL=_l*5~|BLwIi4n2idS#A~08<0k($t%53^6W$^4u|0Ek1}$6zg<gra-ZWpqKqz zPz^K_(<pZ1nJX!3?ET6YY-TU@9u2?`PsmrM2~q*R@rCTX_Kp{c^Fz7+RjXK3{;O4) z7``_A&-AyU3w|B^ev+F3{or?d%w^Tt_$hUax^u;noeza<ki6tbcn4y|vzIJI_P~^e zz$RFz>o>A|`*o{=euI-zV{M`3=_$IeXyKU=T+=*J98R`uTRo_#!dK%VN5{e$yA01? zv28j8D95SP<ck0Puzv#arG})(yzzOr{<7-LYUzZEEc~5<l1nAKRe8@Oz$3n$)Xjh) z@LV{Y$wpsN{Mifki{bPE%{?NvUQ`+4^((A}@Ja!@K}!8Y!$h5+43?9)fHzv*v)TDP z%fPH0bxg_HQRwr)(=`NJcJlD)257;<iHD-W89B}d1*uMTZn&Ue8PN%vpujcYHP@-a zbt2zccrrYh_PZK$$?od_&KW3#x5DfJcy-(hXKXQi<)fWd$Um&+YTuTdP~KoQ3`qgJ z6aQwMf({x=egK6$N=}&KUpwk8ddO!wHi~a>*~MOO{RCP?J;f&G+}QEQh5OOeQ9eXC zkSY)2B0F`j@QBHeQ5M-B1;(2wenV9{fUoF?5#Z7HN*l-qGXmFjAsw|+^JGf*9={Q* zkdLY&w@Ghx@3@~|ZdgZ;<O0SF?I1Hz5Uf#=3^ooO$ynS0U=sibFt+g=(*HiUYmhAg z^94JfEfH{VZd&v4bu`Nmz}HCsN}j=HWm_duQ22A%*!cPTSwhOzY)X%idx<Ujy08m* z;yq07OR|;<2o=y9P5Cqf;9r5PxS(RQCeJOupbT)vElkw&7~EA>pp4;oALt{70na2; zPNpgrthZ1yILJKv9YhdlS_uLnIL+u4{i2ku_6a6FMhH7_mG!15WQ6we)LFjCz>r{M zx5N|LzRIgm*vG*oH%hy|avg}s4ycM5!mdQKw1C=B<UiG`>{L&tE-Yfa=ENZuA$UJ= zX%Rg^K&ZpyIl%oCp5`}hevj4qYex}8+(MM~FQhcNPUDEUP1c=(W%%hwb4lBQE*bM< z$@gYVbTJBJ+pYWJx5F~G{ro%L2}@BYDI*aaPNAyi*=hnf%Vn>FFNIaPqWh|glXhN~ zaiN%iz?5huSgYi{=VDn4d3MPig;O|zk>R_kB6I)D5$D!$<cV^luEqrd<v0IskhYs) zm3C>_D}?*SDK-6U%Jw_wAW7<Wkzz?&RZk=l(rsmeb)OB&xiWwDg_QHYhxWVYXVfrO z>ywo4+6a~4-2mrD^n+x~RJS^!FWB#O9$?-?tN3ZYv`1{oDLhHmHQ2i?_)%Zg5~hOw zE9<piPLh>W;S|i~jN#KY;jv0mtet@cQx51~PDu7r(S0@cmjD)IH?{zN<D)kS#%B#i zbEV(A{`6n1w!c*d&c1|0(sJb977RL7xm@}y{W~E(fPz})8K^cD7;mSS0ikDKv~c46 z5K<Q!g-1q@CyGw1a~yj}++tDKKRVO_yVqZ2C|9)<ExBD7(RQC{2XL;z&mYzk3yTTk zd`|5q8d}{vUJ(h9m*LsZ48Hp1Xo9mXmKRSk;1K`8_udVjPcHz4h!0=qNo)ZFctT7b z7Ai-BH-O#OS?%SEzC4kRepc0+55em|?8+3>F7omP;5<uXG03VGqd)E+y1!I+0~(|| zK53i^Cr|y`_VrvT=t)f`idt;v#Xa`A?iz3PiCQOn^mB2T{OTiP70uTO@d=_FZ<Uh) z;Id<f1|fLP!{*)>zTFQf85CcA6aBqP$cQ0lKR{uo>{23*vZ9n!G`hfKDY1&s&e{LU zCkK9<5Cj|C0ni&r9k39mfT)L=TW(M7?^S3oV1H=JUyP6>d4<zb_#fOnnL11&tgJKU z!$sT*hIRTvB>=t?M93kIfQO6i3-FC+Tkp7E&ySDgrmA*!nFLas6PMs_J9}(1i1MGe zytPQsfHM@b5~;L&YL9%}))7Ce`6bR?Q*n^@(Eu4l5BuIG)v3_sX$s(BrzIOehN|w> z9JZH{sJ&c(TT}%_POs?101i+h4B#Nl$Fq0@<YVKX7{P8}MkD40SO^$QJ^+4BDdFg{ zu7?3j1JR2?QJ^318Xmrt#55@o_VvVJV*a;Aeg7#FtJF#_uor%WgX@pfPS!Pw)c2XQ zp>EoC3uQzLb@LGr-Y3aH9jhwre)LU@66&Bu1?&eu;VGqe)bs7SqiLmN>a#j?GJIb7 z)(%ONgJtE%iV(dJK>duOiQ+?*E6P`uu<HfsulYyF#Ldo1qWVvQn|CxJAe^Bpa;TCm zpK$ZP{xi^CWU`)kZw$?s28-Al?^w`(C=@Xj+F1rAo!PIYn3k7hE~rg3dFll3ZVdRF z`n*EgqOB{MQ<c6Jt}-#Qfs}9wntMM9EriHf=`{ji;9TO0T2Su{*akiM!w-N%`^Pfy z^_cgfCV85?>_Ec@LV*)s(LTFh-YMA$VImn$4%ybd*uc&868^+~{_|Yj3FN8Dy#L~_ z>0WXOF3Ky@kNgX>>T=}am<;qs(|;3uuw)Nf&ZflQ@k4rq8m>PmWaBoaBRlYXm#f}4 zS&Nj5JAZ~ICmvLN53EQ$@T(&lCIgMUL7fiugb{wJFT|VONQ={vt(xOZmdr2d`44~x zaB=!QbJ}oHVft<!j|%Ny`2J=m^G7H$qJ<bK7))h@x$Xq6<9>-`t^9Ie7w`Xu6Hm~A z$dyTz^{s4`gW4^=Cy&jZyj-J%f5R{ISAmT`nMa+jHHwXx^H0Rn1|ZU`_rG4!ok3j_ zl*^ajE4^PFhleGF_Z{J2a(_Wnkk>@t@qq=3Y-=aAm!_Cn8`D~Lj(2ywQ)x#w)2FDm zsfSC~a{yc%`nkyD3G+OFP{q<_ri1R!a`%?S$E1ntT-^?!$#2nQpj#iiE`JMrCAh{< zZeJ1))G0z&TXED4bd;?7ufe^)e;2D`YFD?BW5>o8qT%S8we%AE&CKPMWD<&st^(RC zPRTURk3$uBDy_rsxa1}P*YJQ+Dj)P9MeE;0O7Ad(r&wA|wO<*_{-_}WZqB3>B<gs% zWow|Ud#?-z$VZ*XGATlOAZ%scKZ6iU5TraF0G=;)AW%?e9}@sQxGDn$A9q-w^^Vvj z=WvqP^Cx`|MK=;5ho7w!3>u*|`yC@LTYkdVT1mn8pl4|WQs@EOJ2+^GVT(-$Q$L%i zE>Bs>OmH`191xqxCP^J^4FJze3M8Z4zu3CD2WQO>fCS;nElsUYtL<Nwl+fk8Wu^Rz zUuYwNg#6C<94qJmdMU#;aEA^zFMYgz^s1Ins7FXf#tcG%Z-6V&&*XsSZLkasjABhO zW2&HOjbtHj0z1SX9`_z0HBEfHfHf2pA_k!I>P+ngUV*R?*B8C_f$Q|<cr+~dNJDG2 zNW=^7O5IG_(xF4r1ATIG(zju%nEo7Svf0<8k}DiELl2bdQ0t=S4Mf3UaxZy=j-D}- z;{6A6{;S$Hukrd|r%xR?QI9$(<OD&x{e)kF^vpgNM^YZJ%eYisly9;x`JX@dr3xL@ zFv(VEiVw(ldG+Cd=LO)&ZQ7i$I0|jnpCVm5aj3+cv8$E#6z=~#vv4sK-j=PBaj7WB zI=<r+UMtf&t(&aaVb$gpQEU`SB?oJg^z~8n|BzE77c=<qmLP>a6f{^Lr9ow5o?Ywc z_xWdeFfi?IuD=YNK%jc?DhR?9o(Ak{n<1Y05Zx1Ue7EXoYz;2AEQX7xOTI*XJJjr{ zcHQ_{W+A{@t36`L{BO!RaGMGRC&(YYHY!cs5&QjC;G1EyzD0%JIltsxovI4wW-WoO z9f~`>hQuG6t~MJ+ma#0AOZl=s)Cgy4eA}jh5hou_9f3<%9bvD-s7TB7{~VGxsmf4p zNEp7^*N26ph%H7i5lx(I*~1_TsYj&qW}LCS^)=_+z=Y~_5Ie3%dWs`xY^b+KqwF-z zr<C+=@k35n?Ky&Y2f6<X0fGZMdl4ib&l%oOI36vN5ekmw;>FQ^+9uw*>LfpHN#nIO zPR}m-jzMpdi~rk5EX5Tr9NB#I%8H^)dlAC@y^n0Y!0oLk*TBS^Mh|1UTE%S6%^zPF z$i6BQtP7(Xvwj+MaldlyGrj@03C%$Be+8%`h$-eAasqgVkY!e`EL1*^)~Y${PnlUI z_Fpmp<m@Na@(B!6JNjvP*NIff^9S}S!07yd+n)cfK=!BbiL>MU9e@cyLMK#sw=u4$ zhD|Vua~xuugqQ#f{oyFaM{uIBkz&%CJ0u)nSKop*qZ3-U(!7Chl;&KgS_yHMsEq5V z{5JcDy(An3qQ;H{QI&q%xtLs;yKUDzUl(vjP(q&X+Yyft1o2}XVqZcKpA?h~+<;-6 zSCGf0Xgw4up~7FtNTpuAq9m%2q*sX9N-SYCmod{q7-sfer%ApFBjnj{0QmuT-!X^J z)z$S!|8-47QNm$xlWh`s6eslj@5F<OLJjPVNXAA!1hDuVu9)lHX|E(X?2sSkuay(8 z@~=6yIgv1DXhCb)GV92R@5~BSXBG*p!Gm-9Ed4%=P22~z#G@qlejD-_y>0IYXzMNk z)Q*lTh~g1K5CWb97RC#j0=z4J&|AY6tc-#j5$?w4?3Z1MpO+=}fz>ecrKP}25aCw% zO8ko^zUCn-C515-U5_wZTu)@Xg-7Vx+LZOtnCEgtGjE3PEx8?jHyY)4!Oi^i-Ygl| zr;OH2E)7D6cOLraTaS`s!6<#He$B6gP))t%YqOsikIqKsCSy&CB1%FfHO)-NMm5&g z@k~7I%XjBjwwz4MXee8EwwM!qsaTlB2;r-K2__@DpfP6!>8(`$dEK2+kJB@(_mowo zRHQZL&_7+vxp6+`-lCO!Rb6)ZpA4qWmMVid(8KZDCX8=qG=*toBzEuvJf<h(Pz)Q8 z$ppj)9*eFP5qmQis*ml;U|Fim2{RQaa05bAB8EXQBLs5w9^B<420^hH#)+DEsK<Gi zy|-1qpgxzD`HgNy%}&o;0{8+1j?Bhym7rW@L!VnYkrbd5O^i4}(li&X?Mzp8Pc5$e z2I_Cu6?A+;KJ-4rUEY{fOh4yYL!mP0xW=QE-`Ei^!&~hdRo~Wiv!%4s?8EYm@o($x z-!j)RL*{+V!MYF!`L2UVV{az-r-THH$K|gNCZsG<Ft!u?k0x|k&8GI}Y$%VN=YX*1 z^B&+P!5b>8tr7N$y|z|K1`3`U4|Myaqz(msmc-HS0Ke~qhYr)DGs1#<L+2}}2QL*0 zCa)Z3x!JG1X~NeCs&|>BkSG(jt^SeEAvZ|{@6le%_1k<zkTmqa!|1Mb{J^2Ng7!N~ zJY7b)k6AK@)61rF-RZSI?|5`!SGiY&r-`Y~w1&K8=M<s#R)63nn3-|Ce#aCFIZ$<r zfvdqcaFP|JlsUuM4e@qlXhO}+Kf>3ckZCj+LIXb0q)w)E{2Cav6G(AesybTq`TEfy zo4@;WE&de`s#AGhS%#=*7*oCI@X%5MF)YJW?oLBE3gtgizC$&NjvF8fKdwDAc-EOt zq`(w8oRdyj$}c(FW?JaHf`Aim1Y38~EUFsjwW&GpO+Q6eOo>%hGj)Rt*sCv_e3!g3 zg!lFQpPxe$bFShyOoWH(wjqcS^F1&E0vN%~5mWgCppaOFfp)Hp)7;{WW$hj<3iN_L zzQf&um8j>U1sL9Il-hn6_#u9yjuHSrjU?Z#xP$QOQ~U-`lWe!>Fk4qInb>d0H!eIK zK4&QP<pRUO+cr%l1M$|fl$TwAv^rLto}1Ffwsr7pEowi{|3jzVD669xJ!o74-Gla8 zAH2~%Gs1y-C<H1lu-w&`vJnsLPywRvkn^g>3ZGb;2f&88)DyiRw)xKgKL^8ESjl5P zrqu8s@#ju{VB!Ap=@RtqypsVnQ%RXCF!d+H1Dy~a_`{6MXUY27b%(-=Urzrmtqj$d zCF2xCR~QdoWux*0&9$=8rb1h;<)d*{P`*0jp6>37U3t*&M6yO*<Hj?R=w>Dzl&RrE zyEKMANJ3U)t@QqI2^=%7OVuHoW=2L@pQy{6I%G5bw8?6|S`u(YKA{^t>-dC9hbk}G zSxg|Vo0!K=h}9@;@YUM~E2C!ozfw-4mMonU1{9XR(|&3)b7hR=w<u4!7R3>l$oi*D zG(es}TGYheiB&!>%nLE+C0LfObtW-CUD&OV5{j5~8XCc<YsG{=fS*^8e}#~n?~!&g zbOKS7kHr6gADUo;z?Jq*86#{Ke$rx%-(o#$wX06b%w`nb1xKs|sKUVcz&-WG00i~` z34VO`Z-yo)-uS7`1_l-N&{Rc$?}PB1Q3yUX4&>XV)3E8ZY?HNb=?F=(I<O>|P%{cE z2_C;$k>=demw{My;^_6-zC7t*axT?tQTvfLZV<tFAWJXroCu1cpOXrhp+!LjvG`Ad zlJ3!a6iyMyU4$DrXp#TTP2d)4W-SxxiTO2HpG5XH6**H~;x1TBlKu_J7#EN6y95u( zmi~G19S@(!f$h5xGxzSseCD{%_jzK$Ewq|j{l;#L^9Wg|uLUT?{|(8|Ff|Ca*YmKc z@~z&X_lzyZ7=sx;Yn%3>q`e}}I*650rZp$tsJe-5NUccyN_@5GLEMK)S}|erGGMxh z?RGMN!Noe}f)HwsR$-}_n@sLL0`O;@QvS2Q+RNAh7k(@$F8Gk4MsCc)`ofKIhn9B- zAPwSBzkN|FB9pDrTW|>)0p<|e8sr{b-huBFxExb~iK3Y2A%@gKq>}d$LT5CIC&Qe~ z^5HP>J;@wRLs0d*VafC&Ph89-(L6>dWX=EGipA9c6udWJ+pphr+c3sG+jrmT(*p#a z7TR2nZqdYKz!|lb7<0P>NkClpJfyikUvz)?6k3RgfL6XK;kVbAwDbrKWK79|-oZzZ zZv^6T11=DPm~lU51m11*0Y{9;04&z7wj4#)I-bO^BSy_8#pX6W(-07JPP4lKpMWGo zp63RkjEH;Ntu5k%0GB%Do)i{@ag)bfWB5zupTGoln%UTg0xhB!2*Cl~EqFe`&o9IY zOi@6)kZ*1AZ-Y`+>?ZArPO*Wsmd5qD(TRy*BDbIZPa%GA_hihFe?Y@NNoiv)aB>5m zKzm0z;p!*NiGk2|spMp%t%HL0JIwGi55DS)eZBz-Xu<x7Xm>(%2r*#o<kWHiHG@MY zZv!yokBN0GR<Uf*;um;GtH|BB6@|*qSl>pFlIvDx(?eRBR;49G&a{jOtBl{Lp!ZFE z2uM#RqrH|2<4R3QcXFbq14qyfV#q1c?k-Lf<nz>{lCNs7xb;PG#x5)jZ$=OBx>~$8 zCR^c4l5dWmZx-17*D94c=BSdZ{3_P;u%l+O2rLcm0ea{e_w}1@Hg!?A6rIS_5d9G- z4XeZd-MqX<Tj3)44QKiySh`;@6FhpaI?9Ve<9fq%BKNUCDq7jFvyS61r(oy~t936K zr@BPkt%&DT(J(1Ff~2ki;_(E6pIalMH^2!<*G)*1c+ayt7_k)~!&G+xTrvj5<b!X% zBkR%8u%md3zmN4!HJ-A>kI~6`V82@Uv)5dm!3+E6=Q>Ct_$eOLR~Pc&I&G3?iI1Ir znF`lt=3w8s&(4we+!eZ(#Ni0z3cVk;{jV2KGQrA_CT=yWwujr;f<lxeQ^QATVb8rv zoh6QP<M1z*`8H{&@#d)<jtD8U5b`Jk>(ESb7ni5pii%b%G^pY8d6T-Mp3?z>p`ENU z1jnoa+TlrwtbKz2^nb8u$*|m5R3VqOJ{5dVL!Il(*6HprvH;S8YVdP|?}f#-l94E< z`u1Pz(;N;ePfiDS?8u02lgsr!oKbM58FLI%a(<lq+p|*3wo`V8i}2(qF4`S@-hjrW zOL80%l!i*bSHhMyi!I{>TS3J?xWbu-#y|4p#8CV=-NOH8+@CJ`O5>E?+}{~tzJD9h z@Z%MC;a^e!{f3_*o~8pP0%wGpp0Oxs4<^<&vZ!gzyczc~y!<|}6HBU^Hjzxu_)v#r zMM=d;C22W!OzaAjimRQ7nj@L*6-XuM4LFg-O6O?+^HkD7_%}DBh-biOe@7*`7b@i| z^+Xwep`Zp<eo5>juLA(NB{O)18)Nyrn?1!z)9^xJY9`Z=P82BD5cr%>ts2xr!Q1JQ z6n1vN->)inhf@-_a4hnjjqJz!PRxunK=9)e^!lbQ<jEEBc!8+*!7<Bt?gc%n!jJN0 zO#4G&*r4FJ{GcJ^uhccWKy10^k7U8_{tHk@0;n?xSTPR{46cJb-L(o<J%ih}e-<2T z%cxCPbu+(7GqR`2Gq`wZK%;@c&NtLxeu=SYUnPF;-DMnHhgFLwyBsU$Fh7kNFWZI| z%kx;<*2NAQfF{q+OXX~elf?m#C$RCzqQ9J2yUYSN;KkiOU>+=l0Oi<N?RR;uKLQ$* z_bkrkgWJFuPzRE;o*~<=UVsC8bh3X?jB!XmVwtJQry{K2`M*Z1x7s3U9{1ty3Mvaz zT1DANQkk_T{H-5~{;Yz3RvHCKuXIj2Uq)I}!2~{AYiQw1{0~;;EX#&viah;iLE?Cx zIFbK_$fgqEE4)>$7CZSk>@UhuAJGsPCvC&uA5mEIYS`?zjWVk!#{ElHL0_5}ehz4U zU%W()lvDCc!^6wt0J7l<lc%O#v;izvT{28Rrx2C3aMYJ9^XpT-l=^|I{iQ|IJ|!;> z_cTxsm$hS;%r>p&-01Pi1ffYX?HZZy+k1?)_$4fquwpz*zRuqHk)<xXHX~Wq1$|({ zn5+|VUxh)M1G4cC`(v^kD@T(Nl1yJ}VEG5gs&q!&;|2m|4e#G*(Ln5GbCT{1W~_h& z*ypA9lFYgXxhv3}+Dn!cDDQ^!zz0Nf=kLSOnd@&-In%wq68xmx16G~ze7?DQiTx#X zYq5qIbQg*E^;8p0e-kz7yYfRR{qae9(qXj&Yzy$e!KZG;=j!NzKN6UpR8BV#u8anH z|Ly<rO?npB=WKWm3OZ$RVpNzb0^)gz$mAFbyDAlhh1AvrCqgcfRSn?lh31>aCvYY( z=!^h+%a#cxRd=gGr*JiPmFx&89U{25v(!9!<S38t7lq7n&tLwa685)XF5-?#=5M0w zetNS2Z9J~U8f&jHPs2bm!C072nwFFl<mJO`#@Zqn6D|2fFymDmACkVHN^pM9$ZORf z$`PUTO-|Y#d(6i(9n7S%UVqpEv#z^s{?pFe;k^W>41TIT+Rp#Y?tJ=vS)F!@Eyz^9 z-qa2mby!kbk>ozo10cy)JOoE;SM2LC^W>=vf7AEAWIl-j_LEVsX3|HmrTlAt4-|-> z=Rc{GwQh4?ZO{wm0A80NP#|a1tW8*DweFR`4zTE<;brI-l+aOj1O6v96HA^GXUNst z_N(QW*m`|vW>Exq(jdM6TiBcnuYi37gTfO5&}z7nvG5pVKyZEX|Jl|z0Ok&UL0Wkj z&AOEzbl8hiXH6j|m;d;|gHO^las2gQoke;;tywq7U1SbT(-11(A+o3@z0d4ITpfVf z_DF>KXTs+x>*6zsR19|#@fGA#<642z*x?mwUkx^0LxB(X-tLT88Vxbg8N4o;*KRA+ z`1-?l>F$wzU{KtC^W*@!ffvu*v-fjt3~0jF<c5z=zyLbpGmU5DihUmxWQovDBnY?G zb{_@ky^o4=+}1MzZb!v=^$+FAdEul#`2tLPczFuA29764xkdp$d&~{bv(lWDWsHml zT$KTFRQRQ}1Iw#FR*vNszKVesO!5k9RF3#hZ|21Ash8<!IDV`KPjCOiDRPPZOJk*> zv5u+$xgT{&a+SaD#2VnSuFDCTvLUB#(K7z!`mB0(-y~Pm`xdL7;&<@Bf$u}?vQ@l) znz1E{i?$b4;3B<uz0CfH-nJMkN(n~nf%ONGKbQ|WlW?j>wxGL?RY7xBnas^EeiIAT ziB{QXr+Rh?nAj5R^H1(6V(W6J{T(M8^3*i*#PrlwvN1xYGF!k4UU#A~3penEiocBc z3s2DtOJNHaoV(z0eXdu*TwkSjKeLa^Yz`1q;!T))zJIx!gD395>dNSP(TqxPJMFL` zl>Qp$*12;0j}chnE7f#w!;=*)L({vdUv||)8n~$ZH>ov!DdHDWFn6g59JPS7&%5%4 zi4OshM70b}RSJTQyiZ(1qK<XqoTKDSkSA~-M)&ZE5Z?pP{X6rX){=hX2>5u~_6Vlg zk(XMlMlb`}SL7(uoH4Pw1SJwOoMfT`TpU8}q7br{Qgvg5wz=y!FQs}5Vtz)#?6a>A z_i)XClzR8sW@D5XaV73NdVC6<>!xjuq|2IaQAcsg>WKpjA}g}bt58;SCI{ulULngt z@A2ryL*Gr{eh^5%1btnaF4VuegEC5>R(8jSQ}a;VXX5FcT@AAx*%#tIe7S%No{Jzr za!6?6g5_LnT;r|}G`}@Jc8yS#JvbF$wTgzdyIw(-Wt<SIORx9-Nxy8hp9`Lez5;!5 zkFu##JGEgm>2QQ6lY4#HXnQ4)wS8j3g~-r&laeAFuIU`Kf~hP@<oU|F5DTrJkCKXd z{{HZ-d5wV><R4J~3I>#1?G#`v(Dn8|3&s=qzjVI|`rF53Po0`)j)y523pA#ET!?<W zy8HPj!i^9E4-P&L@Fu54nAf<ezAs?v@$9vIN2LOd8PnTMOSrc0UEudclm)R2Fr31E z{S#sExf)pXPn>!>PiH7H3#7Mn!n?BXF{rXFU(0MPF@SIE06FUe3rP#l=8$bVDJl75 zWPLl}*Cv2W{rbK>3u(L?XhCed6(N%lZE>HqInFNr0o@pTFCQ6Mr=2AWXIxUb*Bv&6 z_!_i-v8U=^aB*kjE$3k?bEl@4c}1Iq(dyQ>Tc1X~&#OH7%4Fda8&F2rsc(B#xF#s6 z%wHa}BS!J<dJZPl_WsNM4>V17>?ukZm1(0*M_FTUxh9T?q3lTW?XQPo{2$YappG%j zgp&v6$aoSh{S}uH3dB!=EgaW=a@-@@s}xxb(=wFzgz|k!1dLU+q!{OMvaZ>wdBpDo zu?Na!T%ORlp5kKVm8#mj7Ur>Ri;Ag~D){g%l~%I~!A_9{qp>$r==#hvPcQvbQP3Ji z4Din5x_nx{k7mvE$;^9nvCZRKq+2kkjW4|3YQjwpee;qteCV6|y%}F>DmL~ZcJQda zfb=-iFve{M;C0SoK4i9uw>Bz1zDg<;I1X!<XEM~J?7mcm@}L{MCvI(F6%j2EHU#mQ z4yz|092#Rkchx@rtijGM>=e>prd@X+U46w&^u0lM$n63#0m<gyhk}ATMj$sDtJi=- z8}~yqHURni`;|8wv<f5}{d<9`xA@A6+!bCzuo$Hr3glOmNG`LmFBqBF?T!KDEP~{C zfJiJ3Bgg_W?|c0EQ@1feqP`2KlH0k<+laWY!$KzszQUieJ*dG<3l^`3otwkIB1;kH z`OHKib`DsN`XE1^kd!f%JW`&h(c>+2;9ci9ka?B75B&XeW<2#;`IT4P*~vIS*JVQD zi)gLGMl3MK@yc!_14}2#JK(e5F>61#=-84x;<dI(arCo*qVm=dR3Lni?THCk@I`XU zV-$KPx4=c{)K1nGt(tud(xH6F-sm{#0+;p*5hQg%v;sd=f$rfaqkbdw5wE55A3%Iq zw#ZSKstOzYq2tx_Mq}dD(Lpp6bkk;Q;os!Uu@Ze6Wqp$0CK5aZsQ)>=+_l(NXBcBZ zAABR_O$IKn*IQ2XLvn}!;sz;zGqB5Qcp2Kgw^s}#t7&dFgRHO-phNrtz{xH3lJ0%| zeX+xis%ZYSHBtU9aB}dmBS6IL0N_>_-^RL43-?*IJ@k<%2xu#`_kxpi3N>#!=7`7m z->q6~zXsk}Fef3v!A@6T4LF#k0$vQbGXy796+w{UqV6q~9YFgLAl3K78%4=FErUX~ z2XDY!SAzMv{~xAj1cvvwz%T$Lm}4Ujo(kXaUx~&w5w8DwKe=S4qY3w}|3NgWW}?&c zCgX2K+#g+|iq@sv;s1(Bb|i0Mr_xHdE`Ap+6dDv&{W9;Jh64IJzgF~4brTfF;*)KH zrxHU9=b4XGbH=(GCEj31HeG{WiVZ4wqm36he=A+jZPXeHC-ox0I5=rKD2&GM{I!$b zheuyjNb$&8pc;CXfT>A#TTdCEzdw{hIbFps>nS^d=<;x2NxA!T(?`aq*m@g~h(5uF z*<!wzwUSh%@q0>H`;$MBj4)?-?bhFLEUb1)m42Lx{*a*#PR$O*!LF#!l48G3{y6`A zm&#gcPK&X9;8j}@(Sl2a+OzzaE~*19&Z9ME%-J>RRK!lEvgtc@F+O0>h0MrpzSORn z4l`1U`i<-6>(XG>fnJXmn#!{N+9}qJ`^rX8!S_c(Z9|;|wz=0%w>kFp^2nqXYkI-U z)MBIsfD_hdE9h)&!PTbo%(gP$#Nlr$@+XdQ0h}*iDu}upecH-_6lSWcsnV-Arnl)M zB+PPg3I_g?+Ej{LE%?$?Ki+<!Xlbp<3MtPiaMfq247Gf_nj#)dCA}>U+Se<22$cAe zL%`609<6-2WvF0o>b~NSfQk@7fu$EDsW@hbVhgXo9uP!Mk4DKmCM+!G)%Ld4Nf4P# zV1w%35vx%qoa_Q+_?63@MBP&(ZMFM(V*S-+0}|#ku7Qv~bU7%K-I?%9LZhtK(F;ix zp^9!VqJ_-lzq>FP3awK3t*!<1bX2!=(NT#OOkZEfYITT(s&j%O_FDO~9Vq&7Y){-T z`krpYGCX}F41n&MkK}6EV5!8om^gX7|CcK(L0<cGJpwknrTKmqjn7ClBp0g6`BkVj zoGR$vZhg8<a-3R$x?k+QkI~8$!<VS)4y7ln5J8{Ih@idYu^ZG($KC-&E^DZWO+M>I zAMUS9v1MI>Nv)<#>XnZUb8dH5KkL)>90fo7HKb*DR$SY{-NkVNLmQ(2R2y|UP6Nlf z3vBt#lCZKApzgn!SDus6;imd#lGmd$o?`7u4{f^Rz7-!k@IiHzm;SYD78&4&i9zB^ zAfir;C~YNAV128$GUI7{1jzS$e=@S*MIF7+T_lu7pw8k2g<K`R{*#LOGU^Aeb=cY+ zC=d5?OUY+qg1nN!`im<Kt+CGk0J%U$zgcB?KZq;uDsnkg-G6}&@htV?YVbd^lj{K8 zBiGja(jz?yS}VpKm29J@NB-}4hN{#1;kzTmN363RinDp&@w84XY%Y9J^apJwqpk4y za)xJ!)03Ef<Nstl#2ABIO$8;j)Z5KhqZ3m$RlfMlS;qeas+J3SlXG5EIQh(YhHS6- zA2%S@O>Oa?IEy%ma2d~-(~@B?nG>58@{|n42RuM{?qdvy_K(JH4W9C{?4ZLx?g%-p zP(>RD*Wp#du@2_#IS?enA4h21t&CZ*o*137J)k}o2_p0#6gXO*_G;c`h7sm->G6Nw zw8o55$`6bG?X6{`!SODJeU8K_y69qF>;1wo9=)X?o*};UCV%yuD@b7G+F7bHPwFcf zZs@jYQTb_L*Ky)?7n}sg0o>#?8QLol_t6G}gP{=_&5SMNe?8|68h6+@N4@FuA-Dz; z9vFp+Jr-T0f9KbSv0|v-9Eb_4%iG3+q(iRcfkIT2i5e&rW-8u>M+??fNHOCU$Q2g3 z9dy`CFD%&v<>~4Xq`_*DW;3tAXrzFt)Obcn6sh~9L>WLEiHZZMHqG|1jj4c0XdA{K z7PJXS(lWjETR&B#q(Dv#KsG>NBIeF`Acjt$Vkq0eyJ91-BQo$l+?Rx=oxXKaRs{kH z(P0>cV5E9&$l+Ds3MS4xwO`VMLmfE%WhK|w>g93e7TY^VqgO#Pm|y`Q285tj1%F5@ zsnFJp0)DoMaW)`dG1gNelwNw{3QM?aXl{+0LyCAdiiU&n!{T1DlJ(X*Ogxw*CWI$s zZ%4cWpOEQZ^^_o*!^QBt8ynGFlZ)2YteJic=2Cr_@a#aKcwT<}=IBE9@y!ZEiPl}X zP}T=&WRn~13sF175h$)7k;e}1=3gS#{<Q<hD@nN$mv1g#JrF6a?Sx#tg47G|ZH`S< z0;C>rge7j-!P2r9mrYEFoHvZg`svZmlSmFeBos|GL=J{Uk98gRc?ojG92x#*kM_xw zeaAUF5Vm)3NB`buRUGw`mn8c`Kb9NkHwfg`VUJg`{sg1P^RVZ726JUT6KLcR9LGrm zI64n%|F6ynMoCw~zU=t;(8K)cPZ0P-8`SfeBn{Biv0e`)U)gLkBdmzj(nKD%^SKgv z=pB}ir1ItWpMBFqy*jzxO!%a*MnjJb=kT#J*h-lvr4pza#K0lu>L!w~w+}Dl{OO1J z{M=Z60@|*B;%AJsM1Kf)qf%@~z`&tiN}1I(YQd1_)xl3c91!$!pZw_wUyK<4&)ldj z#+d0}8(r17cCg^aLILE0U`4mYPd;2aX<b}N-6R4omy~`A_Hf3eFvLW%T4QibpnDGs zO5i6Rzg9MX_D#V5FJ<*vEV8|uO8xh`@pcE-A(yBBhk-!d+NLdDKV3FLL0*Nc4=ftx ztNJwCIZ#{@7xp#ZJgp@Ehxvc5w|IW|(EQ(<55>Q$T~s_%67KFxb3G2>R!Lxs*CC58 zKEO3&U9V@G#|LuRYii!tL2`Ady0kh@+xfJbW4}48;rxvDtAfMAlm)_yVSDV?tvE;# zP5kufN?KY(FU6}kMr~C7@V{c(Q?|>nulYZ*;7M%jn5b*Js7(TEZKKW_%H^{ko>J&@ zy;nUUw-yIPoG@3$YViMf;Qxr)!`KL0L}>dsg0DRc`p+`2fP2?E`|Sy~H_JBU{~9|B z`e(ZVPu8dXwLQ&zY5&|kc&-TX>_`%r|KaF^FRu@I7(Sh@V#5QLMNmH^_fWvB|6~5L zHOwo^8-$IW{-8Wxl$nGt>@GqO1#D#jOa==6$7%x?oZ_Bz6}uwlq%)#2-#*t099q2s zfXs%X7_2}CIbuVshU%mE2-s?8jkJNBgKPL)ug`|8^`r-V;^=ryFv*0vR5UQ~ZbyqS zJEbdutJkDXEx_T1RzT}*mIaxgDqwEMKu3K%KcmXYDZ+TjVk8YtzAbVn@prJsX`h>~ zDIc62Iw+z9f&yM!&=;oyk*@0sKA(Gj!kq@K2#|5~-XL}qwCvPkN}umi8}LjJTwz-U zF^LY8a6eFn9oN_#t}nph0>*Q1C+E8YE?mzd|7Znmhm>A%|K7M<G5V~Lrxc6s<U_rR zPV_%(3yt*yD|M$SI0Y!9rzvxec4nK^tmGAbr?z5?vI4IbF$LF|6A<)vpsAeL^1<3+ zaOzpeP2&6wGwiZyvhQ?FdjQ9I=Yr$OS^2kKt&LHZZ$Dyk04oR%YO((*Qaalq1Hhaj z`SqN9gY-N`20c;_X`cbq^~`xq(BCpwUZ}W{R&`+YDdh*$nd^BgJYvoOd%oJI%{f@P z&o1Lf?1$T|ez+J`TJ5EdIpkyNY_ep>@Y$NC`$QVjQ}ViG^2GB0;R8AhQW`=|b;w}z z3HZ_Po(<Nm+Jl?L+;&dNdhC^YNPedon!{JiW;|9aNsrIs#X%h&{VL)s8Rn?%dHLuq zz)dF!{H{4OqTxBmNH>aVfvK^@-z?%n1R3P4o<q%6J-~9>)?4%cinyKPt#|(NgA@L* z6NGbc3iGA^`}3tT<jjddc&E0p;u&8v^JDWZ)5p%<4s$Zf!&jxx+I(ddwF0(Ul(wKL z0zMcl+8tS3UEv&w<Aj`)Y+FNR(+=MT-=E@wthiZHqK&dW3n<kYjON;vl4^hMj@gC+ zc?|R<+M0WM)hHDjCNo`=AKKI$=UyMR##Z7+q~qY@mL2egc7Tm2X>&3DpEiAt5eyLf z+DUL*zW6!EZo#&T|C>B#+yhqi(B+cz?h$|`o4fzrbQ@vjne%f8@HGHVyD`G=1n#l< z+=9)Z;ng;lG(DkrTdax%w+)DM;STL{NcVZhj8j269x-YkIZB1qALRczmEe-_9=?dJ ziSH`ZDp|tZu><F@x1Q(_y1dP+x-c<53b9_h*rM`aVeG)laMhG}X=`o3<NxP8HiwvM z;br4HSMR-$ltcbcz3VbrIH6MgS^d$aXBn-@^}4^T-fehn{J)ZyUUf-s&n(0lvH?B% zwYos8av?!~-k8ap6t=MD?pu*Pq>hLO{%`G2GR==;7P&pH(1<aQanKO@(Xd`H$BK5R zP{|ls!bzH1<5-nt63Ha)Q2ehPAbNwumV#*w=bTJdgHqW6;RaARS8|PlY0C#YFL7Am z_1Ve9t>ava{WytMnLl<nit6r+EV0Y6$I68>;sorAqAb1oG~!e7{}@^pi*k)o<MT0f ziR)0uiots`+{ztM5PPM#b3^B3gOCe${<eI4q0jh25}Tb2rNtU(2qgx1LFNwJGV2~r z(N7C!5K!zHgU9R%p>2nmW{1SpHU#9sR>ZVfLk^X9nriF7{VDnwra*fhI>hAB{LpeC z45`g)i#8)&@W|Iu8Kack`iruq4vS2N&kt5zx|zaW1r$a-RmOxlXm&ndm}47A-V~<> z=#0D}l(6+AlKpf<n=`4B_D8a8V-Gxb<KqfckOe7ka%*j|N8G5+!m^YRZKWIrCD?Vz zyA^Ov%Pc@>gF%8AukFkW%AGSO8IS!dAvY!EjgM2EfINE}RX;jV23-&c)zrd>nFq;u z#(s(Psj|$urfUe30A$*xI-`RFuF8ZZv~wkmf9~o9?pn{Wv)z-GxirFC-&2Vl4|FMQ z%{w-`SS!U9<`_}4dO{Kll5toRymnCawxAs12wvNQnd(E23+UTY*LV&keyf0za2ZA( za4)X5rdB_gSQ{`Gf<AlJmc^2UmO-MMoreHuZONkD2*l#S{`iQ>Q9Qs#tw5HDqufr+ zFe!_^KXUwkXCOKVUoVB{gz@^N?yZY2kS|<|a<#k?+gl4Jrn4r{_bLp`1BpTQv=ySY z>e|yN1q9PU%2&w_*T3_$Yb9+I9eDT;GeWLX9q|7Q7r%)QPL36S9=KFypB~U&x?dG` z*`|@ic@B?v#OB@}p#&wsyuBh8nqvo4gB3ykur?;!I+LavVX6I$ImaSzZA^lIeh0(x zR{+7eUDMr{RxXmpx&E%g&dH!@6`_;&UMIkxWvleU*Sel?5Y$L^`O4TqW<Y(6xr+u% zl4y?w2dwx_5wymaFLLRhc}?Z$O+j?ub5bWY(F59N^jB#6<UY&dJtN;b=em%dOZ`!w zQE>}YiA|$Z=_BY*9lVtS%;8hci){oR)=kK<P2BVrBb#NHHREPcuQYFwZ?S-LGDz+{ z*&E-k(TUbfCk9u-z$(iS+(sdzXge(>2cZ%P?UwTFG1uDaNW13RD{Y|M&KldKC(>yB z(f)6TsxA)fT||1nyDcn~UT@0~p#^U5Z$oi_;~kvqFNVIgi%iyp_n&9<t85Favbw5_ z|3f@i+BrpjM0-cVRiNC(0e#3FcnO%Avyu#9m6j>_bxN7Q!iX&{9&_t!oc}v6&`W$A z+n9d^;x!dy59FE4i`Gt<^H|Fhm0qywSU^<X@yfAhlCSYU5|xdO87V2gLp;Uwgwu}G z>W34*On+RkK%B0}0xQG)Kc*1WAcKN8;;mVjLOZIZA+FH-1~O_M%?NwE5_9OuKAS#k zUx<ig9I5fhk2guKTDE0}g*iADg^iK%Ym9M3#i4@4?4o`wy)phjbr?zC<BMQw<75%3 z@9bHHY{N-vV7|HhEZq{h>55P?33gxVueC!wpx>-;Mowf5jh3`jMFg7pjx35{6z-c~ zyRZr#^3T3JX(L{AuOSE|rSLsc5i-?KR*}2#BV`?&7SU_Gf})ZI2JLpljhwr6RD)-% zUF!CNEui~}|Eq5(`jK{<c<u|ir|J0m(uPDfJxMo)p3tv-heSgK^VnL6F(s}DIWfUj zXhNBDXIfoP2_Xj&VbAYm$^LrcAdrlh)k8f?%|gj&3b9bqfXWmWyVFUop=yi@0mcO= zD>;y)V;TcDUcYw8L{j0;*s8uLVcme|zG{;FnCGR^pBpMw-m<i+w)*cwVQB1YbiE_% z1sIe_(&Kx~2Z6H~d^Sgh9nv!v6+#G^RvTJ}d3tUu3Oai3MKsx)gr8G!y#D_I&y_!S zg538Zs9jok?pyvasE6BWUSnqS%vB4%<2Y&sb6JAFe!_$8Q0`Nx<hW+_EpAJ0x>#!E z+g_V0uDJPFkEy^+^)6yy&Pyd0OCL4XMyD@=brl|gi06|W-iGt><^RuImu*t*b0K7$ z0;VDa>a&@-{78x=aEw~}>lkoYb2#&9ZQDKS`DJW>P@H#KThqswO|PJ&Uh`Dr|MVA{ z60l((A9dJ8t*7H9c)E6hU>n(Qzdc`)Gse{1PWpr*kOHfo4^HlHMa-)LMUm2ps_=j= z9IOYMU7_%zzGFQPdi~)KANwEQ!K2~?3ux)GK>!(eKvyHdV?A}0=>#HsMu8M_%X!xD zC(qBlg*euWqv}_e!G7DvzEgTF`D1+AP&>nkjDMxh3WK1iOlH&>RQQDO56%`Z<H(OE zyBTyf-ImCP1+lgXMbKdZ2&~2ej2=&F9!OQ!r`Nd^ebKis(cZ5=R#-$99XsQ<Bq2v4 z(F)cMl!0jhs%D{RG0Z8q)BS5SZ6iPV5b^Uih^72r{;oq7ZCEw#?MsvnCViFTyM}cf zp#^*kwn}rNXenMWY{%|TYwQ2j^Kbv~Vy{;f@{E~l4+3JnZaAlr;QP#-dvE##zG+<R zA|^kG#a`O~`A;%_+OMy=%rt)ed!`$?X?A$W^rhypS|<J{TEl-5ygN|RzLgBxv##y# z^^=GNATN#ohi@;~wfSfmjQ=VW_u?XA9qC8IrmV{25XQjNQ|s{{(;v;{zkd<+@2^+A zfD@>YP&`l;1EDsp>ehYhb(jFY%l{*;SG_OYWWQnN*MFGS=DBTaSJX?L4F9h)j4+1_ z2rMRqy&yTnDtpHTwYalu*H2h#to-e_)tkSInM4aP<31aHa4O0qcTAcm)y~h1>*!J+ z8*h(M?~?h)k7xe=BWxlSzL97_<(?;N&Vy+t@Z1)VvI5UoGnp?9F0L~w;-?H#UK;rQ zGZzE!tlt_>DIP*>WSE^mg)*tFf9V3@IFWZXwkVDO?;N4m%3VJEjM%YP#JrX;kE!DK zU|h{qjY(WM2AIbB>p5vkIx8nS+3)jjI$r#?+W&jCy~x>nJWFr&m;*S!+qOz6)r)Ee z^@qND_m|Xn;Jm16dGI`d$-$B$#XY?Uu-aNHLL7IW;j!LGYPJx}4q%JH#VZz?;ZrMF zU}3XV^op_tBTbR>r%OT~Y`64T1aXY%ZXv>45ltx>HT>tf7od)c@Es&b!lMLYGJzZa zw`SIbx!G^t-T0<Fjy3OIpP@cTO9E5gKlDQ&HaiAhXp>*ZFC_mkox{w`1~|eF>@<$^ z3~m>O)e8KR_3;B|)NFGg;2beqK{kro=l;PB5)&YdN2Ew=U4Wv0aEnAMfW>;w&jAcw zN<cn&BU7}Q%t<&r|B3b#s;>0CGg|e-Y6TLA(RnHfKve)SikZr-def~=za&}?DW}ii zrx6COv(<Yft+CY1(YOD!qwonT`T{$ft>kmNoF4Ad8*<OawJ}~^t(Ol}_~PAcL7rPq zuWyXvAoFYDNMH)~qRP`Kq%25@ej7n>1)PKys$O%omD}ykmio5;stXhdnZWlrO)~ge zsRlSbyLFCUXx0-d91fr(XKeu+bA&EmKz#}#t6CaecYa3Fa-IOc!ph9TV~B~mE19=^ z;0y!s^>vEmw8L#-`nk>z;JqK0LYKv?G5hnYfvUIFi>aF^*)w%F&uvk^vMs3Tc>x8E z_6qK%aL8?2Xt|v+2Ol*_leSh6s%JHyv?9k9>{BnR<&KfQB=O7Y;JN376n8yS+Et3@ zWEyO_5v+#}z!d=xggw;Au;LoFx*2I!$i7+7TCZum3L`Bg?m8I%Hk}Lo+v6t*#e9}F zi?2SaAt!DhM4&ZoL#NP32V&SB%L0pZ&DD${>q(^2Mnch@LtmJ@C8?YTa0N(z^$ln1 zLrd*-VI;A1G8Jrw9<LFB&DKHUBSvS5L(q-^SdA&?iTdBQ+H={~T2&>guKLl$G2MhE z0qFmnVh{jpKmk0Yx&TU9X?C&n`7-E_=Xl%4YK7jCKqab5or%qvG33;B!NwTK4W6_{ z2mIk@J+@g>xpec4UM~B5J~3X@I!6~pwWkn-14DIMa&U%Q0Ud<}{txOE*S~!J{1T}@ zul2&-fzfBXsVX>7)oL)A8)uL?yHJs|7y19`{Er4v!vGJy^gHjyUcPg(5Rb5Q;b+K4 zpjOrz7B1-+#!2?uW}a(;)d_liF3?+Q#gJ#iTm{yy|5l8jGU=c<wa%B4J7JHGsF#)h z(f<G3Sl_eO@z^E3bUsdextg6KCuJ*$5_a~gHN%pl$5*o>Vc7w0=nB=s(C5>s67uP> za`#p57-UT#5!@6oIAZ2F(M(bXCE3dV$G7^*O9GcnK3ul(A{i!kOSk7Fe2vqS{G2BX zd<>H!#$_V7!p+0}!oK)eSK&a|e%5~p2``E0>l?%D_cF31GL-WgW7^mGpSD5U_iffE z3IPWkyEldvgdQq*_tx41|IZ>_Y79L1ngyxSTw}BE!+=ofNX?^hy;GxbFx}ZWtX)JA zc+Gsxw(>J{*mFAON>1ug!J^4SCkG=LgYRabX!W$B0N5j9PsVsdfMW#1|EMJQfs^aI ziklHNl_@?ubJFiyGm;1R_d4$;Yw!xO!N{&GuMB&XA!IiAbLosPP5?3HDSX-epSj26 z)9^hI6>KPb2W*I|a`ckSZ<Bh&><spUW?4X3r6a|rgq7_bnKrl7wSj_NL}>jlhX0vI zjC99Oy`QEp1J#o3V~hBPZ^-{eVQ)n+P<+dSpJ1Vo1q;l(%LKxMjsIivMJX_XXQ<z~ z2qcECIOp;S57{S`myvSMpc459fOWE?ikO5y^?U?8A53F``;a$ALZ>aC8NQdzCuhv} zPbb@t?bjyoHVgsfOr~5I&;dtAxp9S;?_5NMt0wMuIjNBII2HX3kW4^>t)%{PXtxLn zE)iI*PqDHOEI{&r!!wZR&2wI7c3_UJTsGQ5<<Xuwnv-!6<muWV0YIn#yRLLD)=+{D z5(@Uo^uEW8clGAzceYSrb+>yK7!zQ1FjhyPnPwwROahewD3CJ1%K)*z+Hv(tMj3+6 z<dd?^(3bqM^(!Z?@N)zxtMQ{O0dumlA1MBs*)yUDj+ta>+a5E_iimWDc~^Mc-FhZ~ zw0W03KG1rWKP8j1kp@)410bWf7?%8GqQVs@WUofS&Ic>FRh}g6=u+H8;8pAzckj~( z!^cc@loaDhc*TI^Y$*;4kY>(l@{8<)DobMyzHwo-)0NLQ^)3^KBt@J5g#R<fkg;^I z*^jUcJaepGKmZ8MbBpGg@_z;s5B^{4@ml%=Wdj_GP@AD7r#p6xg^ZNMVFe*X2dJ12 zkjkD&z^>T+_W(iQ`T_YE6Wm}&5MIz)kI_s4;!u$|rXbLO6MSyq9DWo>H!Y2xKq6rV z8DU$K&F<2ImLNuJOpN(#K>^On3nc%f+Xw7DJtL3ZxMu8xz<3w_UmKZ6;qS};CgFG^ z{x^T}-uqjz>)2$<p>4`tec3!RJemKa{QuI-D$tI{d^i7hQ7y!Sl304u4O~ga-dN@j zuGUUK*!;f^<&#Txs~rO|sfgeLgg9e$q`0Uo8j9h6C{Zyxb~=?~5cS(5Uj<#g3fAC) zUU?e-V-78UZv9R|i7j*tW*d*z?E1tRJiLD$5;yQ8j78hr3*sg<|31P0W>B^9-6@Qs zeQf-Xv}*HyjDaAW=KmZ+`#<@AOW6C`<It{96liROUA2Af+@E23^j@$xd~UG5k;?4$ z8=Dz>;@hhvALy%B>6-$sA$2Ts^l3JYq|_OJGhpk8Sz!-F0EvkO$Wh+w^?&exevbb= zh8&LQR>T|~6L;mX=K;MPORxQy36p=yvUFw?{2z9iVg4U!o?5g`$eI3sZUr`J?+fxj zcld`)+R^Nd`JX;in@-=4|AU7B3J`iguuF`R*i;f|Y%1U+QSB{Fnkw*ro)nGdwQU8C z(00q5?LLLzN=TDN&vx#aZmKpr*Vxg$A@)8W>0xG2^Br?Z6kb%lDoPFVERH8fi4l&F zCcQL;v}{lrZKvY%)*S39U}jr~Cnwgvs+Z_UV7PwmRV8{?>HhUR9893EYfdCV;eBW( zzA+V&R*ZwP+KR-Nf=dg8R~rSVtle!en;gG5Z1<4f*;)hrr9{85HF_MF&xxhGXADUl zvav+sx)i8@gvE%;9lIt-NV9JP7|EGrB=Q)@#IOO{N_<C}7#i9~#l?pgzI&>CdZlL% zV{I`FK4ud3g|})1cLR}J2=;{pwsHWk|Jy;rIlh!y$cq|uW1yfSh?BePb>`kKo(fh# zwo^Dd=pRfH5Y!Y4C<JwX#(qQa7{9?V58!9@;XhNgCjMS+9dL^iymuy7o%kG9A&_ic zk37o0yPn4XYy3Y|IBGf8>;5ka<#)>0KMS)z?f7y3kDwk?TZrc{Bo8E5sosi5BXcz- zd=BpJp*<L>GXMf_(*HGh<FeY2hdQC2TkgC#6?3L0KhQW0W`@}gJUQviopiCtn3+>j zpPrOR2MRvPG4dL0r&6*jV)wDuaBRjTz9XrT82SKMfZW`lEnr;~-$7`QNtpChyd(eD z5Hkwz@5cYavt&14mH$J78SWlyQ^J#*pK$Dy9hAq5hv&lh_mA=a;pgO@)A}6$b8O(( zibwvZ|3f~EFF3B2ne%_84D&2`sJ*cXeh)IWgjM>yCD^_785|~wcVBJyOti_*E!maw z!`80El*z>CL>M>xd~@5+@;~B@mYdF300-tKV}U+npVAvkf=QVAq0r*{@P7pp>Qxm2 z{v&aRjhHMA0pMKw!UjiV{?GB}9L9(Of_)~RVAvQeVAUkYslyM)Ax^{aHOtnu{6%sN z?YfE)DvKg(@&`hda{srO4)W~6J`Sy{!LqK#RRb;-Q~Ys9#lgAj>EQTnYi@=YpenvQ z{-<^*N1AGGcA<C^{;eG0?*CD<J0VqApxAc*myOlt{}`M2|L*@^m;X_8q2At$|KZ={ zm3QU;^&ao_*a~|ZFqDfi?yqTdFDlU9%}bna%CDqy3D|~XGOCdo|3oOmEbJabo3M&e zjdJoP5RScO3}nREtG$)Gff4M>1hj2)d;Ch1O;}a>mC7<#^9E4yp70RpC8bbuu*2H0 z)n^a`<wfF+L~s>DR0JVDE2ODOUzBzt*dlqt(M;EVe<5MuQ7rtOKp#fBGmq;1z$9WJ zvOD&YWXUg?{gzu=DZpq_VLydOLi!*hj8-5!^Jc(gc4vUx`a9{!F_rnqk`5Y8*a9>^ zu(iI=D-A`$s6gXcz*|r@APq!F%ssl2%0fckWWk@##;u(B@mY4FjU?c*NIu8Oh6+^y z7Nm~iEZ4jba9K{!k^|>uyAo1cs$@GIH1M7hrnD`bbt<ZwRp;yj!45^(+;4{Qp3{Bs z%@Ta(zck+>IW0h*RfS4>uC4}**;a@gq3tB@kObVOPUxC4;5)SM{%=0ekF;p@ycl5T z&HssG*Z7ZlEJpsXq+HlD@{JT@n(o7cEh}n`V<^;|I|afK!+R$Jd6F3ux83#lC)q%= zR@M+x`4FX{-T(2agK|wU391|dhOuoy>IjAdD<*ct@aD&u@k{^_<j#EzF_cqi6XOpH z-b9DB2j-Ua@n_pXfdD1ost?lWepc=>L2YCVcV)!;@W1sR`JVi*J62=yX8fNv-{Pcm zWa|BIG9>O6WdYkVY^WdM|C<{6u;S11|4f000v?=ojB?~7$={9tbNGLiKG$UMNSf4< z)*-7aAz;uy7$Yk%nK3$uGVCq`p`4M$rpz_xemtYF=e@5!@)%2_EioRbK?JoFUDlo5 z2NO?(`0$bcLmYx;fcE`6Ub)sq;xc>{vT$d`r};l(1GFWVIkRIx<(#Af^BCJP^@{(? z=6@Lfb8$bI+((r?CYPIiq9s0>*nh{V{;)4A1W9()TpD95J<g-<KC6}Dv_}wD`^~?c zI|aldPRnM(+EjMeI{>;a1UIi6|F8Eo#>CmcyVjM<%;Nvg@c%>nFYU+f|0+a;if&9d zXv4!(vga5aIdeycvGJCB$7zQ&=LHM%{|S>LB)_k~|54+Hw#xtJ{2$a2^q{f55C2E> zi~eee43Jbt5*SfHM%>DZj(TOwCbtiI<cfu$+$O?4x;#Oh3s1MUXoPf_BJrW{!FDrZ z+eIMfGND76C_B#B3*<(qEeM03<L1#kDrqU@O@O1Xwu@8oG^%+$+htjYi^Js}bRIfh z15e>lGp#a)Ic!DL_Nt#AJCnJDfhRY^b%GOI<?VrN&r#Mb5zjC?X`)W%C=mqQn&-0R zeH05<G-u%Yb441oz_@L@{ZuWX3mt@1loMK-A_=c$;8&ct`f0-qRG4S$IYxo!uww&d zjCH+Ah+85z(4_pKQ3LI%8b;}4-VUzRO<NXZ0+L*8qF45zDPq_O2&x?WoPO4r@gB>x zG37cmr**C6d)pe>fj|>IIxL;wp9Dthrb76c+sV(+onC)e!FlKzrU5|o16g)s3Micp zu!_0L><S13yz|_6kBK_(&+SmBm|Y|xSl?EVZm;cIUV;QWp(|`t1um)K_C6&0X^&-u z&LXg{8#>DWg{2G_S%{frTloDvt6t&$ujeeSU`$+p)&H@3MldsEg<=%^i<}TfC(;|O zVGN|T91*rkAa}dDh{!Dk(vX9aAw81f#e^_S!ia=i1Dot}k}BegkEcBGOSDY`L;ynY z72z~bOozmm*yzFQiUF5(|KBfGUSMCMrPTMGh@&xYc3&Aj)Ff1YJpX%eV}p(F$N%)r z=9zcn|1MmjBQx8t&i~<CwxVL!U0W^n#4Of$xD>=%BH@pf{RscR@QtSu`YGVA@qf^K zPOWsFNZ^$e*{m`q#-VIm45$^_-%>jWV+0KW){XxgoTR4;xF#1l^=BqT!1M(FlK-LL zAjaA@+tx}DhIFvq&Hi`(pLk@qjbZ}b`rTrsNB%#=|8o1FC1d3tHc6kGtH%Fd=l=r# zBUV@!p!HQfIm`|;mwK%L_G$jV#z-CwOY~`i7*`UT&>lwXRwVAB9diN3EJwZ(VKK(o z_ra`p&5tn-lWzbG4`Sro1mY0?7Y>&h_dkGkj6tS-zREFie-G*;d5xMv>cia0{|CJE zJNUn1i~mFZALIWuHco!u{2#Fne7T0d@VG6-BR_!uuRrjNl(P8W0B(K9j?FUvcfO7I zV*Eb_6IDu?BHo4nA6@1B_`jbQt0TPnAP3}xE0oQt%ErI<zvf4bx{M?vC-FjCF7N^& zRylCODTUDcgxTH{e_Ob_?CZug2v(?!8o^t1(qm7PML4)@LGSh@<LTL08wie|{Jbef zq|ffMli8Zo3#|}F*vSrzw8W7k#dGEiwzu@)-Qytb9#4(Kr4eBij@c?Cl&-@9DexrV z(6%uq+@sTk@rf`nHjx@K2Nb=Ew$t-C=w#4T2NXOI2U;4it1#ISto+qCBuNArx1A4t zJ1gFFOpva^2gsP^>lSaehK`$cY}@5Tey(~|)Cq3E!$DX<cM!;!XWuMP;i!UJtVRml z_P)l9llu$vg8fJr6l&@)s1UIR3P{aPv)!!$7=4}gnn3x=p)oNqH+u+dH4BQMU+<E@ zlG#ocl!dLhtTA@h-}+n~S(Ul3YNs@xq0;OiIglp3BgYohW{j`IjJIWug2-tExp0Ti zxLsqQ-bRx%m?o7j(%9dvANqg9j)(FL%JFP<=)_3QmHsc%Hou12cmIdXv1>=k1w+ov zN%kpTCX*GWg3c0U&a@?5l+HgJ3D+(n;T)1N(G?Q3<d_vU=d56p8)G(B1)wJrdW4zv zSd+Z9CCU1_W4ZC3U0D?t+`(TksoL%x$_NV{&l}<Nrxrz2Iw%DL6T|&=jrO*PXZnyZ z1~9tzkL3UOG5jAhdj{g~!~drPZAka=b@{)2m;qYWraKNdIj^pf5VUMMBS;m~>&y86 zX>r0Q`JXrd&OA%Ot0+F5|4YeQgz-O$WbkDg)yQ3Z()>F-@BE*_%kJke9~oOGzUS!C z_#bjg{GKfEn`$S(iO^V?R8hxw{x9&qsfLv-$RaX6&Ho#}vHc9=#}SikjRgbx|I_FB zANt_}O^yTSDsVS?WU3gHT|melTlanA{|Y!fWlf(9<9}F87i1#i#{WrK9JZY!rSqay z7Z*aPgu^E9^32JF3fbWUcI*oVlbq&!hiw-U<Wg|bj|RZME7SPD#{ZoNdQ9lUhtZKv zIwrLo<B|V?^NjxqrQ`p{E&k_R;QxFi1sufP4>IJq`G3GRT7!W4U8kRo|GzT-b72A^ zeqa7KuCMZY2UorZ|Hmu*&()BFtCvM<8q$ExqjFJGtP&}=5>n`Ct5h94G)Uy&+q6Ci zTAEFT!9<W6B{5X+#*0v0N+iV<11y^&aK90GWp07b;QzE8y~P4;VhXn{kOhib%9V4r z;=B|o4TplK5P3`>hUa~QEFVOQq=BZ}RKg4JPeFRZMh+UtnL~8*+z|emOI)AuFd!1= z_3x0riv(RB<TYs)tEI%{1w14R8-svClpT*wmI7{&Cx&?{Az^YNn`mc3oS0~1C9W-% zrPEX#Il)p4p;0PjN~cj-qS`*{qU(fj_HJ?2MVt^Ua+Ct=mR+}ZS}>jN2eMx@8`~I3 zfSbMW3V~_d|J!zqK!?V!fQ6173d-7WX}4S<XgTVaV;(&j+v0(EJ=c(Nlq%5!Y!(0~ zS3?{iTgHqA*O;yn<^lB_u-kEvppEt|By!jVu1S$oY19gAyGS(R^+Si?10dTBTk%X= znZtpSk(8kVCG;W1Ui{0)0yK&4NPz*9ZS(*5g#V`nY`APrVW)3f(IV*oB<vWuXAll( zMlw;6a>3pQVOR@<B3y1U$xIJyER|DMvR4z&k$j1n2~(*yfsTa=P6~OaaE>(J&|<Pr zsRxteHa|{Kx}-aosV9b;IJUF`gv2=gv+pv3CZSi%jv`{+1Yh)qqL8SU;IuU+E|UiB zIXCy>(zfvBjrpGr9@D7QBdNX>|JV2~LEx+NzqO}TEN!W<37E>o8*u&fE6g9isQ(|M z@7MW1QS2oDD;OXC*MYF~m9ZnFe%pkD#ej&24Rfa$I|<t<{s-njf^9E*qH&mAO|c~N zk^cv;N=RKDLqvIy-G8h}6m>OLP>JXTD0lwnju0NSm#^}_cC}Fb)w)um#&^!fn40`e z*fB=(I{#<Q<;MRBEM~m5?LxrLyC&cwXvm2!{y#XTwhc{t+4GJ6rGDSWo{YDOr{xQ8 zlHbvP+U^qaKjjybsI_n7H!DsJ0nuh=S6~0-9Nd5}_+nFR0E!`ZY#03u2Kc{X_<yq( z^1pxmK>pVbj)P2l^M41r+p2zH)juu%zh1$)`L6jthNt*HKH>jc4uVmgMxtMp|AXdi z-1QFp-?x|J_*?M*YKBP1VIPfwhXfiCTLnx5Ew5_R?*JU&Y>_V&WdKis1eAcuH=pNb z5aPZTH25u9*a=n9GZb`^e5s4r37TLu<SeO5XcazhPV(l0gX{Q@EML{q@@scyrV?#_ z5v-DxMbe6;6p%p`Uho9!IYY&6kgNg{#NYu;V1qiswxk?@)Rru*z+PX^B8%3G+BKO1 z%**BGgu1G|t{XZ`2?*5=FJZEG4REUbGk&N*Cg#po8e?L2MOcM&WQ9Nj2teXb8dF-D z-^L%s?fRNK<$$Vrj;teg-w9S;PnvH(TS%ZQ-IHp!n>yTn%M47i50ZoaPJnz;)o4>1 z4l_*m{}RsCjAiuLU)*ATOA=X7(aJ>XQIDhvLaggQ2cHs4^=0-C-{w1^o19ee3cNXh zyAdRh)6|<0MS{zTx>Ug-wmYfExRd?5|G7q9*z#I8oltS~uNElYbdXr@;@r(=HviX; zC3_lL%3UeK*q-yc;)?%sc0^e25S#yNPBr<QZ6lTqBz)F%DBa>iC#19$?|z4x6DfkQ z;gH>+qlOO{aF{0*lfJFQ<#^NyLV>4veOBbAUoQSc!u)n0pxDN!aGlj?%Y(;VAOq8q z31)f@_W)J~QqugGW1&f*zY-@>y`vk-%6wg|w9JchkyLwlZ_EFT%qkQ@je))!|KA<T zn$pK|(jUeDN~9nBUvO00SZLQ3TM=d(ZrkdlK3RXf|FbBMolW(<_&@B6&zKkZ|J)pd z7=j7x5-Z$4lQJ022;={Z*Zd#y4sm}?A;2RiVgs@Gyz#$$v9DZ`(5|nU3p>91f6e_1 zdayz*{txm$KvA-oIg0-)9{Wp*hs6#ylZGcx<#i(o={5dGe-+06b>si4^c@{DCa}}1 z{NDp(LdunV-dhm2lGyU3TCz>fdCum+ze2MXV{(<++d-y>T`DctDn5mw`PsDAHEa+% z43ihPyr(;<^^aJP{15wBEh+xz`~<teC*tT2<bT9KFa`6g_LK+z&$z|^ls^kPeQs7@ zS7RH=x%+>?|HHpd4RM74tDO{3kN;Q1SLFZpYldX?e*E7vNj3hS{9il-P{NMkMmi}h zs1OLOmz3uOfT^lQPYm{}k#;#|LRm{H`udi3#w|&@`q6CdOJrG8u>UPeK2f`INbogt zBeHTFeaRS7LCGj6=B)rrr8fxP2;8;LNvD)#bLpQlExAi|(%{@4oS0lnkp1l_L!)hg zYl|0GJgJLlgP|>U{I`>KSLGUJhKOusH!=4dg6V=oMr{fHbTVTL>TYxnP;C6!&o+=i z<Q!Sr!j8ff{!f53TY4%QzR1L7MO#4S9~Jg(n+&(3o@%*o!m8qm4&e0GZwC~O5cecG zB7EC^_q`4hDs%7c5SNtz<Fwu-ivvZ8EQynWO(#0BM|ugOvH&n0kK(Q{e8l<KX}x;h zlKnMFq~<bP9z*Kp2A96*6x|U!S~phWrMqqJ-+4dn9ro{Jynx)E(+~GCjIS?>)}##5 z=EYvGymX#%Oq0{rAg&n)GI+u@B|8h#J#7R1n@z}!^~^1UC<L(k{I}jnd@bFVzMsXm z<NqN}%~pV&_aK?g82}pYnn&B<Rz+_zOT~K}%hA4@uSl-jsag4;fpmLRzHAuVJ5emy z6NXGT_z4CZ(~~@%u{{zYoL8UhKSHJzxlwNnku^?a2{yQTD{>zDtK58|NQ&U-4A?vU zzs7D$Nmhd&M??Q_+p29c(6t;W)y}9PX_Ho<c{l#ANieRjl^4AW|F?`P$P{nG|1742 z>C4(@$s`n{npkLmk8&#fZ4&Mu=KsU%fkDEXegOZ&veOIK5ufD$^^5Vp6)xx$(JQ?> z|Mxq{LO2vk8iW-U^USoO@zN+;4(AC!rrtuaQrXbqbQx){#w$GAzP>y$20ut9729Kb zfUV=W7fv4eKgj<mN@%xUNk-XxV{jPMygjQPvBiM<<YN3^#k_fhaCnmUI(PBs{51c+ z{GG8>C^@4o4F7LQ|Mr!eqp4l|jq~hR-yU-Fo)@v9WIMFE<T%)t7B?1-<hJW)gxCv9 zMgAvt-^R-T;LiW@3;KU;{vVbg|1kc?pArA(eb~`Oj5UKT{{IC3bKJ)Bv)D0tf_Cbb z4<;3}&Acl1c=_w{KgVZw%TdI;@ISDnF!p!h|24KV*6XUhdcn#<x}$|}IXC>Qy=pgO zNDC6id0?{Lx1yg)v3A&M(gt89J<y}smupmpQ@v`dM3&{0l+g803Tb0)nIV}<CM6Uy z^;0AVG?S5R!?qaC=rOlPa6mFLl|%Rsx9BinMb(hi!_~!6$t@<n5-Uf_*(U@8I;FQj zVAlVVbp?(gg$_aFl#@!3XU7v|_rcjZ8{d;&?L6>)<Q6@jyToQGkx8GE1>r@2F8yK~ zSP+y_749sJKSK(f0BS1LWYJl%#WP+&hAIl}K6WR_3<U+5kO5Zd8!p0kB^)XN5_pH8 zxXPsBgGj7(Z}urN>FedW>zdR3l;Ftie9~jw^`Y8II#ENOYa62cGlDx2CP3uY&#u^~ z3K~rqxgAyq7>^o*;VJ&`e;E-_MZt`)iIF3SEPbUR1fz6b1?4EhXqCtk#&dIMC!n** z!wY2I&2(DCP1%iDA~u<$SQWAzog;8sv`_!PntQ6SL#xpF?D#12|Dea2{|^!OI8}va zds*QElnrJLr!Wu-kVjU**q|Hv8%HIRO=2+ntMN<DfHDlM_{LbFa0F6ga+29Db3jA1 z^EMHhPiap4Hig6pI#BZ>i%JV~q)Fc0(3_=&#vs21Xfb2GbEiW}LYWqGw2DmoOPhW& zxTs<N=BKh*#O$buO@<RJ!~o}bR85@ow*0^DO+|9x--vhT|4uW%I{)L^lHF1xbi5Mb zAgC+p36zpywmHpGzOb<$?Ef|XzmlBq%m0CRLV-7QliT-@zZaBoZ2lj)8+Ctg{+JW^ z`(GbyTf3~yr||!7CzbqYE}BLf|C4`pIzJ1&vGISmbkrEs)raqLICBi8uLbA9fQC!> zT0Zg5r}!UsK0CD1iwBZ@daOhKmyD=`ifudpcF><L%W>u#|8tHpCiGqW-}hP3ARhTY z^o%&$pRri13KLKNbzO9z^M3QuvGzIdcNzb4m79+u97mAb2^z!yhS?p5<reS4cihc8 zlknZE^dUKZx$NsPF~0GC#2)|O_`h1Ga#izx+WPD9f5F$tuToLPYw`ca5gz|rLF4ZK zG0@g+bJ$;x|MB1EG!BUt`_}wVS&Dq#iT`6}cccf~nlRQnjWkqM4{*>!Cz~uoK>{&; z92EVe1`VI+5!A*=vgrYYnXFo?RnR~lG68IlkHP9FkYrq{#aPfAJ|Wwlp}Fm4#h??Z zjpUdt;3(9op!{MFX)6reVh@#xP>xasj)#pUW$Q-01M^mmFFX=J?QU~QLo8Gr4;t&7 z8Y;CGoegsWNb7)+>)O*+9 }w&Y1d4kYQtdH25q$aoT<CXolBFgXvvj|df(Q`%in znp?~GkS$Qn38rgM4D|!z1&Y9I(}YGDWU7iATlKvA9p|gyJJ!2_!=Q2^x#ZuL_+e|A zI9Yjv9>wJ@+w#d(S9+)2FtgVIjuB2iBu0#;7+3{m6GjO5S*Fb|1{CJ!kw$W%VoU3{ z?=ir~hz%exc3?c(SbN<Osoaxr78$vn4P&7HH+XbVd1P!$F-UF>o0pG$^xDC|8a-#@ zz?cO6d4*CW{U1rhesb^b|9g;R{vW&l18kb3g{zV+3`0_#00C{RqNDkLCr`Wo^FAY~ zCOZ1p;3;?+75PHo41W(31cQbV7zi^$%<F}`0?`*`qJa@;7~rG=e6Vv5kjX#<+p2aX z`M;9XU=cA4LrKTx2Aq2h_JUuqQcxj=DwtpvhiVu_H|s7+T-A6)KII71JpxLP3da7? zvE)Qu!mC9HZ_EFf1}#$>$?5h@`9BOtOR|fv#{ZoS@)oX$+DO`$Yp@sF)kAhuo6uW= zI}XbK^ZWb%^1WldPBdxD(nZB1{}-2@u~y;yU#dRLJUr%Xxg-O3{O_;;rN47GV=5L} zogL)0jNB5T2o~_QYTtd-6k93o#?=S@r_%>tp-T!gT%q+^>?TgrF3h9MABWGQ<A8$i zdXm?DEnb77fmx9M6jO?E^CqwAAVa`0{BPd1@&EYc`ku!BN?!PdwvHo2-?(<;&sX?A z4QDw19u<=u_UkLtX378|Jr1;*0RIQUy$!(qKW%pcC=C#P{fCcz-1xtNW7#56dE@`& z?vCramLuORAU(D+Af$84n#r&2I3E8;ABKFh*I@A^!{YxMuvdH>p~o8KX(^l8G0ozC zjG>EQ2Yur8_#X=u$$7093PpDAJZgy}P{~jG|5xPyFhnQMBer>a{&yKR{J$Un<C6n@ zgUG_+AxStpfin{u@N9!g$gpyP0wrApsFLy(1q~V3AP{HBvDAPZqDKozfWiWVl7tyB zw-8VfwipQUC;^INkzs@dIa66twMNZ^q&kULgslkZY5{n_5*J}cCn!8b4#9E+$Hc=m zFlMbxqdLT014vUt!MP7IZs0}`Je7E@&(1!eqz!3;2*NPm(m-yR*dt{ceUl|F*$G&> zyjt=VzNHV9wqj&fGp<e0sI)aX_B(Lly&2^WIf*`W*NGa=7Rcm+R>^4yJ|t_7pTq%i z&>|BK9%4ewt?dYbO<}IwpC>1y?zqh_45SG>IjaI??xCTQBwV!zPSe-C6=cb47bkY0 zw><{3`Dv=)bZGMH_nc}#0x_4~GIU5!g&q+|yr+KY<WxB_bH=x144W^>0k~31dO;$` z7Lcri07Hlk@Mh72Z5G~w5T?nt!fW+~{_kG|Q9Pqr!~@NH6s=oNT|`wQcsg&5hxvaG zuH=jE{{jD>6Q3;_qy3zt>`O@JB;X(x`&kB{ptD7?{52}*{|xFYHesc$lo>%y(qWbH z$>=H-m}oeG6=yno@^}5GHT594Lg7@t+wn!%$}_`6;|+tcuByrZIquyDO1}?*4*?S+ zqe+tfMbU=hzX|D<Qgh_POS$AYqioyg?ag6Dli`zTpGjc~gVAt`%S?SWL09nV*3*0P zf5<$?iQk<6V`LlkCj1{k|L-d@yhs|bYK}9@$4s%zP9WG!fd6sHD}g5m_lo~#`#;9s zuQ`)8)|1em=6{+-yu$xS@o?k$O32jszuMlA5JTR?y(x1i{J-Z!{_nYTcW67AECgi^ z4sE>T;dD$gdX+RsMp9buSo4vGYC1I8QspOe_}`vAue$R;_6TR-pxKDw|F+;oOngmW zc%h2O>io~4-}!(2>ljjA`ziiM&N4RFZ6Aj$p(6Jrn4g#8p?H=5o9_7cnQPA^k;Jo* z#?j0e+ToeJ884({jkfss?0Vgt#-+F})4~9J?k|f9=_fdL#!fjXh&sdIEwN>$1zurt zXyb*y4*wHB3Cd?c&iM-epX@yCAKOmfy`V7yA9k#Y*y8_0JPkhcVh2r2{+~I#NL{A$ z+s*&O$l17q1&FW3|I}j)#wQmw-<<!$7h-kkJMn+6xcLaW0vq0J*c>{1QpL#yg-LKS z`=T_{DxT?o+R7r)aYo(R$U%R$5j2>&kjbMVWMFJTGHE9)0S^Qlxvg~g_|k1vV^**v z2dSHpy1Zy8&1n{p(hx9|@`~pAI<;!p2Gs{~u#}t}xRjFCX{s8HnrF=f;g}$*xWnG< zdF1W_iFCl3L~3r3a7UR-Cnf~@6EO~xbKIB=iXV0dXyu_KFub~Q$^EP``r9iH*;cA8 zkd&(8>&Bzv7R2(qS4FEW2aaYIl5RH@eE<pq5IbB&bF!5b{3i_%vveYF)~*y=8S44g z?nP-b3(afE>d4XOn0$;bdAlWyeSNC$y7@ngERgvEzCslpe_mbTfWLzO_c;evpvloW z+f5@J!DbqF=!eCSc5S7<0e}_#d1HVTmehaC34xh{M0aHR|Byb^(Wun+MRd0N)ZS$g z)hmScaA>0WKNERH5Z7n3Y9Rj)R>OfQ-j;}?<+M$tW@kbB{{6YUkYQ%r{9lLF1#JcG zGua61x35)a%n*th5OTM$qE^&Y+h|X*iGSJ`&s0u=kaW&4;_mv{s73f^ADI6SNl!8h zbwGa+R>enawY8T+Kb7PEN%H^~1@OXTmkdN%5D7<sQuIjOZh8xhuPFS~k|43RLN+`d z7?ef5H~&}r@Ku_<wUKYc|8)~dM7#<AX9`VrwGxdwP7M^}au-dQ6(j$&F={U5<IB(Z ze?0ns&HTFU>%Zjx$oZUeeue)7$#pSEKFaN@`?>MI1?3#j37$CDzqY>hzq;|ilBXKG zDUQ9b314|<k^M@xkKE+NKl8J;lU#3}sSOCgZ<!na3lPd^1b(ymI2L(&msH;T-;-VA z^N`kM3yIi{PM_(C#UzT-SsSF;(xC*u?))EiE&Mv+hh_f%8vpB%;u`)x%VB%BZ-s7Q zdQ{!}YRVtAh?O9-DpYvOP8YKZK0f~6<2(Qc$Nsr{%7&w+j`3J(LHNvnl*L(nw#0TG zR9XAU$yRP59g_c@|K~BlypjB4{9gkSe6*wR^QYqfr5p4A2*fh4_&+dk`yAJ9{*Mbv z{vXIE(&kNb`-=RJ`-PG59{fM{;j!-f@PD^@5P(mNo;JhETZ5P;zdloV5PhGJJIU5g z1SDD|JXX06F9Nzbw-uk`kLW&8`6;`6y3avwy8y!~2@qu~t)g+fv5U2KD^X=~ydjJr zB1AB`PmCXCnv)Mu^lAbk`_hQ5y~f<b6v<$Rph7aM0S|%6RQ22Wv0|9r=JoNZ)k!U@ zn;fX_L)jD0DKHCG$Ng)T9fnioL=Q)CE3XI(2&C)Kb7RSs6$B4*s$@7ef_^BZEbGlC zBXnA<#-$PT92D5&<d!3WMNU5rL?hw`v&2eAiCE|j8H)C(-`$I2D-D*L_W>rLzACBa z!2|1~Fz)2C5SzTja_s#DYbXG{F2#t{uDKitXif!wcx@qMc`oT9=+QK}Fp}$lqZS}` zx7a~A87c@#F5LRq0lsq_2_vxLgH?-zTl;&PW|LULPDkN0@1r_+l+Pw@!T~+x|0?ug z_z<e=Xj?&orh-3n>t*{Lz;)BZ1bFvEg8tujP_p#nlm3qma!il@57DI+8=wn&#<8r} zV!?OcJHM;2rlCH5Uv|L?KSvW>DW17BK)}TF2JE#zF3@Adw31j(nd5tM>{62jzBW#G zLfFWR%WFEm8$&l+@B?Y}cu*+xpuJ>>sqaN}<jC?-h;;A>2cDg%wsBDRj&(K$hnS$> zDJXj<{<pczMBkqOgKRGge?$I9<9i_iFk|E9WZKU7CvYGFJBR-6oJm*&Pkk3he#-wX z(52mU>Q*uQf5Xc3G2Zweix+H1XX~(!;4{nJcWv$BEh|+qTQ%Wt$@n66U%w*;<UT%P z{7;`LJ1EfnsN21+KhqEkF$LNe@Al)G>-Y&gnovV+vUYL3bi~i-JB=Vc+@oeJ#Ll@- z{4dVaR%rmaKt{iE%dL<3TWtK_E5NCEqGOrf9oPKd*KDWv>pp&-7|Rmn^*i`KZv0=y z|0c*_j91~0#8GjuT~DU9%X4w7z|vx$Db!WVvy&vwQ}NS;nBHzo&Mn@d^VW=O{26y$ zGC1qT{}WRZYI=!IP-OFsSNLCcRPCtrvHV2*4_%yGIQ;JTUtH<7NqN-g{68V;<_r1% zx(l&S?)*RD)7R$zovXJ?gMWVm{x>X;Tn(3O-uA;+;(uyY_qOnB296cEW*UvXiL}D+ z6SG%#aJ`9UszSZ;GXSQ6&2*@?mW=>fT)uH^xuu3Xpnqk_DuRj%1sXEAno3`pJj_aP zSq>*-jOQ6%m4RuCE;?cJ#&xgymEoT_)JBnbBXk9}aJ5l$HIP-dd3Hww;8rPsUgF>o z{vExMkpv89sMxj+^2(gm|8Be!L$=K!;)k4*d{45!5p5#j8r>Af1JD|dN{kPnk{XoH zA;o3#gzI{H{z<?Wm`ede)(*!~(wV-3G8OShz&lDtlcQJKP${b87$)coR_EG0!sV(q zFS&uF<;W0Fp#McKmo==d@CE%9$AV&Pb#TsS%W+L@k42=~C4lV*^dJMzKBN$Z9Bt<x z5%6z)f4T23@gK%aQ?Q^bgBZHa1rMjkDkKf-le-02c`w=)&{cYXY$E3Uy=9KLzqW?J zyjnXD4JQj%=KtF&Sq4|M0a{hiQNo7h|A|7afxzWs$5;qf$|5Wihy1t=@ca4yIXDjg zN6=GV0k%LOr?L+F5$3Yd)bB2L$JQp&L`6Hw<*ga#$lAuJMK}{igl+GmuWT5WcOov? zSU51Xd!_L;f(MVq2k1RuL_anG{f#!d{fUl+LY<`}*CBw!n8BI|o6pQII3SAOf-L+= z+x!$0(EoW)C%RD+ZWgMqmSo5c+w|U;|HHmib9#IJZ(Xx9uCTo=|Lf*bH3DPOP^!kK zSm9;5U)umQ#>UO|{}|ud{2%<^+ka`_$UEdaG*9FI;yfT>6c<0o|EI6q;{2QGVfX)K zE5mI_awfN{Sy&iHV=UuZqtA?OFaF1BE~}-ZBIo~T{+F*0|4;Ct`v{)N=e{%ccxT6p z-FEw*bkg$jIjk0$fZzDP!2hQARf*tp-M1w3JNaJ)k-n*)0BGaKIYh(7tKT`4P(x-v z#{auZjSPzV5&x&ffjL}c=FE7C#i5N?0p76tPysS4g{gxl-Fl85x|rC3BHVQRpO2+p z|3Aq8q2!!@??+M2&HpR*HH~Wk$C%)E^Z#<&@i86}8L!3vl0$akCcZoVFYyGax92k8 z@+tq1Q2d`Ao3xuuK?>tZ$@+Wn|M^AuUv&TlG4I0vH9Norr{x129F%*04gL>sOyQxK zQc)d*a&zMA$Fn?mlF|)KApR8L<y?NRri`#hv8}j_Fb6IDF<$>lDkt*>EA0It;7Bv* zA)Uw&`Nae|UTx`X!xGhS@KOKCdD^{~0LjbqS%oF(0YYXa)%Kimd-axx8bI`jt*Qo~ zC9!7XAh(x0P{fv@g0R{$nNKE62i?wV`(*AKCW(Pt=};9n>A=ZBqP^`!c3$hv6t%nK zsd-fpq_Z+_CLzJz$RAtJNLVKPyzSi9*8o8Wd)coP<flmhOaP^1*%1LQ-5)9=VEBP9 zoX_65M{@u(B-n1VXtVOROF{$Xmb<2}70j3PI6yLEsi2*3Y^P&cae1A7pcr{wIP>74 zLPb{zFZvMlCqb4EVZ0>>I7c7p_5)&BToljeAdN_t1)ZL|1ijgN249mykoR3ccx}dz zO)-7+=j}u96IMWl37%V;(00w7%T`d<fqSgL^I5-a4ftTbxBoZfcM%MR**aZ*YuKC0 z&o_3<39reWq|7cwBY+}8&wusRyAxn{k$L-bkDZV61n^iQwU8`z@o)@+w5?64JwmwY zJ}g_EfQ5L*ip+fu!bWU0-r~fOz4?Dko(W}%?43Bxips{m&sbydsW5Ogxa{bzq@%JX zYd|;Y4#gaz=O#Z$Ng(5``Cn5{qI$+V@IR7pI^r0>)>nL8{%66vJ4OIapX>3aPvt=t zVOhV!LxKOz=G^~p{7=~sht^-tJO9`4e<|_(4F6Lfy((Prf<7ByW_3a)bae6-Km|cB z@7Y99gU>FN&f_+HBxPw7J%F*@i&&$=`Tsa(7mS5z3Af;idhWVL3oPPt#rbC1+wbu0 zoO$`~%l1h=dT{LgA3A_ICjJ}zh)?o=K<jBNaBaqE1JWEruD<xo+<pCUtXf~m8TP&+ zrlLYj#;Si#@x>fy94zUc3;MFKvm|3zp_0xt{zu$1GQaWvB#5Gl&<Mx=^_NR(=e^!p zD16Z#;n}5KJ7ety{-1;`6HDH&@_(@q<@3mm|M&R6$J3HFY}w{`_y3Tcq^Rzi<N5vl zzvkSE^v4a<_xI=jj4#0d64Cz(_<y){)5TojWB5w^pVJ$T#bQ!0q^3dO`=O%vCZLc} zvCVQ4y^FSzgFOpu<_4P-j0BM`yokqyjWO?ehO8_jw#o{w2_>`^<g8E5bL+(w;RdJS z(kOLxWu~vzMm~&{N{GF}1(d#$9W6ELhV^~6B58DRt}!NViy=-<ppX($S5^ylqkD^$ z#BQmE$!rQuI+MJ)EsXwaj9@*Z&&p-U<O|>Sz{|ncWUOE<@fL*SiAU*w=g%|$yLh>+ zr>~ppE#{8|@46gY_;r8#McMWGOO<V%ylqwER=vT}_4ku2w~!bs-c}<d-bHfk);=ww z+a(xW|N6OZLyPM;w>;7Um0d1<Zwkwj%t{Yxd)h>qI*2AaIq$2Ec`C3lmLNf=Nj}2; z-}x=CSJ~7npUdq5DJn)$dN02o{p?@yY=jD(AT18(;{pj1Wn!8$7ndDvaGYo={5_N! zpjj84R^;xW)&0MJ=tmTGJ#=8|y9mld$#vro^Z!;AS7l!Ff9<-#y^GK_*4iv>#z_V2 zB1-$?W@CAFW(3f<Igck%IS#|V$Nwehx~o*f-969m#T*%F_h%Q*O5gbGhBtMe_tr_M z;b0&Xds5jPW(lQaeKwfb5@Pj!Vec=!ne_WTxQK9`Vaz#m&sX@mMlP5vY}EiRZ}a_{ z{7;>f4oHo6<A2qi35D!8<E!)kK5tV$wT)r&Kk)OkAoFIRqt8uFSm9T!9=*G*!uUUk zuWnG(&3Ev>Z0I%qk5R_SA~6;m!i$r+B@_J_x2J10H+(!Uml1!!^|cbmsgz4edD?2= zSZ_W+_swUW%*TEYd|w|PoBSJeuHBT($;kh0dy~wlz`#3ia^<zhmQ03fIV@uIc59z! zCi#CvzfIz^{69885AMPjR(^w@HUE!nt$0XzI#@3+<K-v}tuY^^lk&@oE$%9b_Ay7~ zGoiW1+;>=7AB<7R0nmZQmZ?>DCLrX6l*lC{%!{;?!9Y1T6FE2k*CI^DEBqff{zs>; z@jnLiL*job@m$I5shITj_<#K9h92M3|Jfz{pRa`Mep=m43vbN-)9wZJ#O^oe|1JJS zOYg=1`1!a^t$XFo7Vwqp1J>N%_CZ2J9!HuZOg}6i*2tf2<P6Wga{oh7&Lq`jU}baZ z85lstgK`@ya)R`|(~b@DmYYN1YAMV}yag7?SVh8^?;x;e+cg6ZZ2b{n`<A&Wny~7% z&z~N{#BdRXhM2)EHxA&jPR1Pr=>)Zt2EGzdGX~7gQda+uE6v(cXhX6>Zi6wB=(Ol> zeEnFHOt-r{u`(+uE^&1ga~ZS|l*Y(M0G2Ax8<;&AT9RP);!7r9aT1cdSCLtT?hCjD zs(WO4x(gQ^2cN|<j){nL;##+^ZGJVY8mC6)#SWej%DIlC1E4^dDg`q6C0&GaX9UaK zZk7gI7lE(}*=GY(o;lBG*|zxbLtf6!|7VW25|OkGdrT!c{(-tQz41|owFAKHjt<v1 ztkau|_BLeNMl<6j8Pv9J8F*gMk`uM#0Y@+xsoElzQeeWfA*?u}UU)9Rc9K5=O!NPO z|8J!8IsZpfoB#WKxi$A-5;3b8bMt0Oq19cfGiX2dl~UIV*lO74KDLUDK=G-MQ@o=c zSv7sA9V5{@fa61%Q0g}Y96KkN3>lmefcbIc#%URQvbQ<$_H~5CB7#o~+O3<Z)7Qjf zWsI8ESy{}MLTGw}4+G9s<NNzpeJB5S^IwAh(W34<g9ma<OFnS_!x!iO1y#4g`5W=S zB?uv)=^HB?WF~0uKj*-DeB!HR;0UT=qQ7G+;2p**Yx_T*H@*(N`)>Y^@8Ex09r)`c z|62@VG1$Q*Cxs)q)@qDj+lCMDe<6NRz6|w}>u1#;^{?39PIM0cuN!fgAD%}qgdT}g zZ(5sOKoNgNDXmWOf8@^p37#wB_sC=*3;fR_%KG@Exi95!{BQA`^B{|jKaBrX?58DW zU_qieF2(=7BAzka8ZigSp}3>ZImeb;PXO002TRX$Bb@&)O@X-jSDZzpSL~GlYp{=p zDoVD*j@C6Tf%d2q@pI>Y;BN6R*by1(CvZUZ;7M%!-_I=gZjqQdejWY?nDk>=T$J!X zy%zu1?UlzrH2$Bl$p6Pm<^TM8Gyb>2g<<8}^Z)c!5?99C^S{mcK;q&BO^gDaRoO&i zzr=*Z0h7#j!8QRZ?4e613>)plD?{b|<z<jyJ9KJmkFA$kXgG<^CFnQ2Pq&7M*^R$n zSKj8QzlhdjBspW&MaAXG6X3fg(-BIN16za}OlOr(q>A<kY%QcSyCSMrapX*peMc}! z!4~}90i8o=T)limu)ep$3<iGYr<<g-NE03}u`=wdoPBjk2DCZpy%8^`c+#t>ed;=r z8g`JAY+9rNK_PME39`EzqzK6iF}woLWM$H}btsE$(b#OptzF3)R83O3PRs}9l`82x z@C}r``3Z@8jZ)Rzqs?Rq*j578wKQlt$3c>UzPnnyhO6!UlA-mvVwhU*6R|O|jTs}( zpwLQ6L6@nj{`!C6pe5LKDoS<#n<bnzgQuf#QoS&9fZbQ)TXzXxpwE;y=A*-5Rm8Fy zZiX#XR^3zIq{3rXXj-OKe3x{rqEpF{4%lo5vI+{bN%XpI#TMCr%i~El@T&hGxi8~H zng4^tiSQL#83`0BP9#kg5>Q5vI9qQVyF>@fh~b@0EIqiqle2wkXUv8yj`xpn+w#Sq zL<H3RjXmg$bsJTf-3l{?kQ=w@%5fiviGYqBaW{#<0t3%cTm)BTCvRDP!3(V2;UV+2 z{&y+}>Li_fI&Iky<U<9P`62wD5nqV^*Dt5le&e0_Kl7&0H|GCy_&<0tlc0i5ESR0~ z-v*c4lv1z7>wCu>H<xRCApc7aFRS$^1AGDgkMLxJzxU3B*$qV;71s%TMVYbrzs^Lt z3O1#rW|qW_|4mGU3b4`az#7iZ;S45+8~0XPyQ$S^xdZdC+gGIsD0;QjURewOPfPaf zu-Dkfz(N3>;5=DC;Uj>@AJ6}7epafaPKW=qj!KhpqW#%PnUOdC7hff~Q6f<+fPSrD zA+W8jsB|2@@X!P8zxNJ<3=exT)Epflm^R(C4YVzj|G9&Z$!-@uy*M$mCkEBB%kG0a z9|TD=h0HI<|15HB^SL(StQUAz+BpEQolnO9l8?rHeuw|(`2XO*$YeP6m9MYN|K|Vp z;oI~7L*E$GJMce$?+1lYZHz4jzz=A^I*8a9F~g$Su<hg)HS>2~x@{AV2EA7WYg|ZB z-v>GSMi|X*H|G808QYCD)t1>{$-f`2=LmM_u=doiRraj^>9!0p$oWN(!sHsfT4<;? zV-dV=n>Yw+5b*j8ui%S$j?7jz1!O#>yptP+E^gyPYfTz$mjDMsV4qqKt)d#;1SeHR zSfx}WFV1oQml5SEZ~3T7-?nozY_D_3H#J6({YD&9XmEnb_M{>KXT_EYc2$<7$eU90 zIk975e=adeB*p3iNovU*i6xWh{`^#t#I0#uZQU373gDm(vywcLAQReQkz5H_5u=jx z)N7Hr47PT5W0eQ9EeO~G0h0Ly<^AfW)_tF;K))oR4G3g$JeXxB>op93L8e#o&&srt z5q=dP!>Aj7><LP_C~NMGDW>HdB$<IVgZ1+f`<Dd;07d#g$a;P1C*tz?zRLjofJ?4E zaA@c5|M9y2_x%tw{C@nu)K{5p0lpI|%t#}<O`iBDdVI9q)UpWX&EzDMw-mnO^)EJ5 z<H;k4$1g?CVE)`25{^*s08D@hn$LUV$d1PUiTP9DF#o40kTJktW-^%gxt1(}Oa6|# zN&|nzxgE$<^Wi)Lh~zP6tM+_H)!6yd)K=mZb`*-s5L|X(`40TA?4sC_rd2-Pcmw`# zF`Tg#Jid$n<ID1Yl64z62THbk`aAVeld)<P_xWPi+;%VRQ@(QN|5(Bsdn>jMf&9_@ zAIASaZ16uYl9T7ZiOOL%4pQP%lhKau0ec**)jl9a6~aO1|HjfTj{3Is#+FVF6aGew z6+SoTMT8fvb5QC^pD{LI9|}&GgEH5$fBvOJyhTzjSL*CX@c(5Ci~kP<B*X}CJGEb( zFry8WiYpy?%6v}zzfA@9ql7=~5zF@fZp;e~O!l|9Zg2I0{}Xn?od>ajjXvCBgn1)@ z!X5m4?14p~5*FZ#z_|wY_pneZ<^NxX|Fx(B{>SLgd;FiDi2s@FtFHBcU7z;<)BL}b zqZDjoaY)rCDE;g4|NL$9pNjY3|J!`3-jV-t|KUQL>&^`Ki*}6%@ChT80NGl_p>p0? znsW>9*${d+v}EI@tu^&%JPltY=giyR-GQk<^2vR5g2HKl(Z{(9Ztf4p)^XM&)$pfj z?<TsrChzxBP*snBFNrHz){1z|;A*>ewzy%76oL}1Mn;mcL<p3ewmGM_$j^CwJ2*+S z`&kJ_5C|G@32tbkZUK(wF;#VrqJcS!a~H^|kQBEsww>UiwCz6F${4e)v(f{UW5%-v za#dU#;A#?n2z~$@S_HuI=cDx8eBe2^G#M~JxSI#P`8wDK4O%svmEMq?lGntLej_75 zKFDdcR(Vg_XGB@h1cSgX0tV@@9oq(9=CNX_SNv@bd#dsO<uFzd@dN9EHp{j=CrBgS zv4@yIDHO!4dCjT$kzS$h?Rc7+*>Z#9Y>8uyiG`-w(l<VE&Nzl}HYhtb8)vYJhz+DO zlP5s;fj?ylcz!ScCz|rw<L>|XIl&n|UsFLqCbdrnp$MWJOEik|igvh~=}JB6hE9&y zdLEpnG}J%GQZs4%8eX)qb~>t#@q20r;o$h~J{C7k^_yI!h|HY>Sg>o|WqAT=9LQ@0 zEa|(OX81$g9n9_EmXF`3h#20KeNVHmI!}w4>6I2x_tzWpzZQBory(P~8UGi%J?Kjn z-^u^+<@sM<{Wya4pZnxU5f93**!$D-3pwKo+g!Cj0zh2N7>v8Q?vLbuv{8elGt}(r zvM_D7l@~>hzn70AZX$@pnK1Nwt>88<<&FPiTfK~ZOua_dZ#5t<lK+p=E0&8sG#u7) zA^vl%t;{={|MPAh_&_@nl<ctVkv2|nz1=7K2Dkq4{GY@B5!;Hl`s6{nY5v9qgX0MW zAgVNQxbE<MuAO#MP&kXwecY1yyO8`bgLyLo+bOg`9d7);z1iy6{NEo5(TY7tUHqRB z40}!#7*ar%+mQ3-|Ah2{DObdo<Nu<6k$hUdlZ}VK|JC?kb0k#f11v>+m;cW>L$gcy zgk_B!-irUrzyTo(ruSa_Z?nyJ;{TfV4!dOr+Rv)W`;km&kP$90wPt~H$s1=I6D`x+ zOToYc|Dpf>KPw_Zp|FAe$jER=PD9F=ty}Y_+}+f_n4FBe*>zgw>c09rn2E;4X$n|p zN;JYXy=~46oNAjumIHmg=8oF_CZNjnEza?p0rhtC1_b3!xMC`|Jau$hn8yzu-_sg# zPs&8_u3<h`m(m|@<_ba(P?C`Bqt9)wK@+bq!bkt1oea<wZb%V+8F2CSJ2~Li$uo6@ zYAli*Kj0)iP;_Fxs|qsIwMxEBj;)jEfR-n(9;Jwr7LxpjLJ`}66swT5lOP&C(*v== zOPNqNh;s*;A?~lrW2fwkf1MR_$i*tD`XT)xGp>#V9;I0Rjka>&1ed#C_U{l*m3-{v zPJo!ChC$h5p$6)vjX);nHf|HQ+Da9XXajwCR3w6doIZjiI5IEV?3VMWsNqGoW5a^) z<^S4#Sr$Ovzb4?F0FdLbL}5z`2fMm*P#L!ZTut>+nAm^VN34GK&t1PQ@*MwX1nItJ zw_~AUe0*<{(Ycb6a?%HJYdI;lKXKj@v#Py~cPKv}-T<Fg&zJ#J<J=~<h?wA<L(bb& zZ(l6BL~al<<E+^pnA-{?E-6O#GnTjH|1$nJQ;T?8{+9~Rvfpi*Z^Zu*pW=UM$YR1` z`(FJ3*Hw7GoF#S2UEt)H9KlS7IFf<Gn&ja85&WOV|KW3y+<S$d7|kFRip1+wGPY4y zb01~8zk+YAtQt0e8i)ULc!IJk8&gm1s6po0o!172WGu#xP-qSV4Sab)7`s~RGw9@& zoI{^EBV)d+vjlV<&Ed=Qf67uQ@9~hr@8BGa$@~>Qn103-_}|Yhns~?)v<m0{Zqpxi zoHLRtobinR>-1eAGn4SRvll1aa|+`G{@~gJ{?A1nJ^DGJ2e~u%I0mc!p@WHSPJbN# zS8n{@dV`<FCy0abZ|w1ZjXWsN*xL9Z{;%^B)<Di3wBDHiTNH}-;=TC4WynA4Oo+GU zfBQOyAn33i+*ts(IU-hHbAXhSg+I+Ck;g3Ku87yFNiNixC=7G1N;@dTRu8M<wtBS@ zCa*&Kfy&(oZphe`koNOYdlk&KY){+lCno@@3HFTw7_7h;vbN~F@2{%#K)VToWYc4E z0PF7uQBG}fbgV?r6e5s3!L#Pn+rUaVn_5CUD;TVHMek6c!&i&kSF(HzIZPEwWO0~D z>TsL6r-HTxIZhyh(p#1Tn`+DDTl`v8V9A<769`+TT2s!0N|Fg_jMG97Y`f-}u>0!H zJZm@P!!4jwkp`hiAq=z-#3)%{6}&()*W6DMa<$%v@BtWl_AeP!cKZUynOp8*Fij$- zovdAtm|69~hizI%%WVUf*<yL!b{tQ$8Yo-H@oU<F0s(i7jD0m=0)cwKl$KxIQ4RsA zev2l88Z#3ZV_B>?p2#_1DnqM-$#JTm^HhM);Me2XS1u=L4D7R+P`!8MgdSUH--RXo z0ROL0K-i<rM>*}f@|FB1i)q>a)+TcZ+uKiv2>pve@z!fG=L7CO-Ur(Y;p?^yRF)r) z-R1cIwAJ$q_`n<(r92d3L!g>We2qtP_y6wyk+@s#W@EF1Oa9N*Wk$MtGTVR2EnJ*z zEofFE`3AZB?GdgI&Q*NgW6p2@>JQ$P|7-H5cu)Rs^Eg?vcnAJxhETMb)GzosZ~Dc& zH$luc*2jLx6P&{&Up`;)YxVnHF3vuJiwpT9_&<#QYxAn0kER(G4VK1s@LB!7snplG z^Z)QfsnAqd^)!witnO>4u<Iyz-BR43T((<ao3<yx{l|W+g7_X&bPcgB=N7$Ff%57O zXwN)^5Q#VBm9~w2WbTEDFU$Y*XYoJBJBuN`lE3(KVx#uo;eYl$@etn%;YqRk|C7nt z7Xl9XSKnxhE3x~7I6Y#Ei_3Kv)ZOgbm?BMV<1IufD(-K^2H8~A=6ux6m3E!R{}p&F zaQwf%C;w;N;v@RE&GYV_<=gz}{~3%idGE+I5O@C&|3ASe<RDS)+w*@QgIApWPW&G` zAG{m?n~097LMS*vC!9bsh~<`w8l{E+r{+EmD5vAue&RaJ0UeEK`kvg(UsK_ri;3n0 z+55BBwN-A~01W)mMuwR?{v(1%QqI4@?scsKV4p%T-gs@iu7J$D5kY7ZB)~Ww2+mA_ zGoAM|haXyuF!0dWK*Dljjst`omegFNe>C`BhXIL93}F2G$nEx3td8T}vh9G>&#OVA zK&>*gtQ0H)n4LJGYVTyh0H!VFS2-)leGh@N=%gtp3ucKOJX{IyN#$gq+Pcz1yd(9~ z^JrUEP$Y7V+t-Yz|Bjyt+><EN|01<>VSm)Z$RW9F!4505#GGnGC3_?J`8*_hs0B&F ztg6^}l_Z>_q!C2S!HO)(5N{Chs2Jq|?F0#_>L<O^l7psa8!wA7*8L`OdQPs=Y@<&< z6T3*LpySZC*5GzG!?V3P$bY6zdnv(0*^ZMooyXk|dm!+6{~w;@%AFM*6f}jB6X=%q zvf*RjI64?2tF+>nuFs9obL00Ek@ogO^-)`_!C-y6FR0!LjxkueIgxM2(=vcj;V*^n zuW-t|CJk-HV7c(TZ&-Fdqz<>IY21iB=f|oXq3!-}{TBO)kWy09Kq;U6L*AO?eEdHE zX5=2@AdUw5eS7|Y2mq_T2mkvHsxQm`#EeO&-=6=$b>^>{rwRxo8!MpPzF-Tur*fAG zCqPa^a(CnZm(|)jo}SvipLqz_nfH~!nZS4P|M~s+pJE=fzfLl(Nw!|$e;O0`E`!N0 zi+<fysi-v==5nlCp}4=dl8&9HVtcKs_&->riOg5T!JUSU&$&$%eT6uK1&br*u>~x` zthrqGA3%$*&;Pt{<~9X~hD1+D20ucHahgdqe@09}fMct&M=jRZ?*GI8WhB%3=-4k= zI8p(B#<NZKQ9X*6F`mGiCkp65_ygo_aWosr5U=JI3p)SjEBs&Je`ARF6#w_XiOB;6 z8u%5)h<bwOn(fb4<f6QMHw0yUp!mWV?vL_+hrD;-e{`%OM7$UOKgj4E`5&S@QVv8= z>xu12imJ|;JBg8ytV)tLNd*ZE)+28wMv@xA1*(@5+?x^fsB3p$Tpzm;l(zwc$xXR4 z7v0~}&wHh0R|plp8;V9#swl<dHP$CBBjXMdAomj|urt0PFceQA-x8jC0_`M8GxR*H zq^qm(>!i?o^{=aky?t)I)3+@EvheLc1d~+XoC^dhK$at}e+ND?Tl43(P+^Ri(pn)A zzFEK~zqY9yy(X&7nGa)jg3`3bDOWN$ulQ@<I(Hk_^J^?XrX@m3<TW-U+;|)d$FT>p zd>$`{>TV#San&Sd8GD<3=l<D7id&llm?UFfdqp=^A?M*Q=CArrJIkKa?j|iOz6Pq) z9_?X>2G`ek0Ec<A0$HFJP)-Z#_LUvln`Cv7UClw6{XJsNaiA|PqK?<1h(7m#-fEgZ z>AVbz)@~RSb@rVjg<$JM;LITNd;9-#Xl_9rLdebjdw^9#kb`+Z3+Qz4Dn(8^(Fz|8 z&*d;Jhw|$Z{mp77@wokz*9KQFtN^r=H%J<IPaQGw(L^yCY<mpeGn~UKjSul3`d8ip z3gLu&eS&Vy$>a$oC8LE=#Vw#=upjg926s`itQ+TU{$CTpSTN%N$66`p^|t(<l<-XE z-S}VJh62sI@xL|&tt2eQ4kPwhjdLrGr|x4OIp#Dz5Sk4?<bUp{%HjX(Z)bu3)A;gg zVBenH!pZITc;NqI`d|OZ{}HkAe+5rHk30XLzN*gqR742u1W+u4j4);u8s?Cn;{S~= zD-0gGU2?$5_#dO61xOUW6od5jVe<>)pa|#xB$rduHG0W>ZHznr_lfWERr&uRc7g}Z zQuAx_+>^sh$5f9eh&|ibLb@~l7mU)N1;yA27+q|to6Y7nHWmoNY%aD~I2n8Jkr?(l zoMbU`I_yRbCItdX*mV9#{^vG)^>d4F_uWd}zxCsXwO*3Rj924-#g)e&=l>je)VuM& zVT4h~JMjO^XZw0{{!f7X%V6vfYw}KD)ow~5IIN&{VhwBjRRY4)4XSg~fc@MP%DmrA zC9UzGuQP3O{H}&ZD2J^^h8)+Evt9WyWIe$H0%v(OouJ2-VS$eVUjz`PzhM=Bm*-|3 z<#I#^IvqU5$xFejN!1d4x2|&QgmQS&r|iAQlmHL_Qs8;|8DZf*{48KeBd*zw<pw&# z1fbBa^p85^s``BuX9!1$oM!3dZ3(`BAV|VB@0}&s^tQY99^X2~j(IayS=*L?XI@C5 z(UsYeKo2rHwtz9VNpw{=?!waFm}H~=9A0Vn!A5Md&{q>@_sxRJVRG~9*v9r)h|dF4 zZRGaVm5#Zzy1WkybLxYhH_=6p?cAXn|K;{Ol4bwA6$Q|RwoVcimfJxw5%+*3>A(F9 zpJxRFpuS4Q_t~b6J}1Zs3mQ)D@K|%rv?ONaQe1LdwzNH;2$GpV(?<S1{J$|g`&s6U zdGTV=mu!4$cZ3!}{5RGT#*v&ti41)yguNzj1xZFc_g*(5!`y4|E))>u1ED&8bIiBZ z>E@+Qx^qt)Z5|K)#5PJGIDtmQo#%F5+5VDD0-R4B#Ok&}+kfr;x#btHqZ|STQSbMl z(RNzt!exdP%0^wS{RiWidN=;}v20&&&HvNpd+|T4EyGszMoeS(GiD`+d9%SgkH%m` zyK5SMbvrD&f-^Z~5YOPu^~LM@v!wm#nn#yv{wKt7a$(#Q;QtwV1>%wJrM4k#+{prH z-uOS-hoZP803OETL}aAzDsi#(VVE4MiW9_K!Tfv_5ytb1r5^adZv4L)aDf90NgX(< z#YG<8{l8!XdqN%Y<M`k8Is6~Dq`pFIcN^E|{C^;%>QnKL?>@w97(G_V$LpF}aR4wB z9IqB6CblYeSs|auS<Q_IZEY;Z%RB#v_<!=w3O&|$pZmuD$B*a#RGYcQ7@U3*qx&(% zwTsn0=hzJ&Djxm$2l_vSI(mLL{s%5^^#AVsuXf*_|Lrps04OjZeNF`%1FE^k6<{Pg z@tz4O?f1?bX5g$cT3_k+@^<t~!fv?Rf`E~nP0kj)#>l9Mv?>ig%b1E4!5K*(l`;m= zqGF%~$fg(E43*WcQ1_F#y5TTZA>pJT;7)QopmB8bbtsn5`42f;s!q)=q%^*Q1fZbA zB{FCB>ImaVn9LgGZH1RXQqZJ3dEIsZY+#c+Y2h$7CVgp%)@);*DrSG1ZG8m3ey%!< zx4j}77}IoJY%d1{^lQ*oGi+kf$);=}{CT&w!wwevoweEev7~6K;X`OMul@0t@xGBS z+l<>R{P%rDZ;C2C2F%ld?>HdRa{vi*={c{<pFoHiMMEI5=z08ryg;}xcsV#tVSTM? zJ#@0}8vUj{j|&^(*8F8INxz`Y)dM8MKeqs1GQ^h0)n-CJ3I7jb+-Y(H0V3d{Db#JH zY2qCObHVSWDy&pUftX%2|Ni8A_<zD@yYEwN9q@we*8<VPWQqJ%(6d2z-R%h^l9Xv~ z_b2Q@l=n*FqOng+u-I@QqEJAFgATXk&E#?~PIz#kdm#B>*rxeM%}C3Fd7#!j8*g7Z z*OPWEP#~ww3M>Zl)VnfVKf|db)Y=klUyX<V!|X?%vwEX;OlKU+_;B=T69llzcj15H zLvkGg4*q%mudl-Y!KGFP`sn`6`5!_gXQp41Q?#htoJLnRr{G`;^XPF7PmTW%{;&JT z*XDVBb6)3bCMxv>_`jxoB)x6#XZCi5Q*yxA)5j2&d`Y-DlM?^e%=-qWF@+M&K}`+s z9+uE2uod}5+4#_)Ao7#^uS+@ogNbH_9vSS<)8fOD8D#9w&p&?sQ2tjeI#)%67iTaJ zVDwpWqg&?H(nxipe#D*w#K!-^xou2${txm0Ou3VJ9jsi>kNnR96q=a(qOqs&+U887 zZSLqfG>(yDiZIxIaA5&oQp%i*aO3}pyCo3Q3tD-F|L1SOvrs!Zi&l)0@?hWgS&Z;A zynxm5KZHH#HzWHV{?F^&72c8m*U!_&yYRoB<<260yc_>luMr)Rz7g5FsSktdgxi!u z@_6h(<+m7R6b4tCC@04@D6l(x?0*~ORu{+Wt`w2MR<%j{Uh!am#J<FhMB}27REz@X z2JFx)H^w2f-yHB9Fo6LYDN&RbM4)Xcop%E?Djy~hG6-CK4rgp~ZT%$W&p^(`$Ts+F zS=7z>i=>0G=ns7M?l5qe(ew2#W2k17m#j`tbWa5z>UL&Z<M{9{25^a>gRmp`G23A) zvTGb3Bq8M9)o8EBHKCzC?f;N`DrO5`1rCBk(n0dQexK(B3;k}1aP1Bq7EGay*a`o& zDmU5+=M~6k*>1lDl<#LP#;+Jx`U*T-J82eYOajs1TEP?aAuE&9VI*b(#`o>HUi1?- zju@hZef6AD3Rq`UXznVEY~Lr@1&*E22S}6t&x-5B@9qJzhqgl$26P2%;Xa?i1y<@g zv@8Zw#^Mh^^?m&x!b>j=nFnWV7+9F^*ty|=Y{3Jz(qc$xEUD`w5EQDakz0?oLCKa3 z?r}Kns2+GBuuZ%hz?e429S$21Nc}{sh5utU)`NoDWtQS95VAhU3^5nl9XX?Wtg=SV z)?ad`6*zae7v8zJRI3f)e{b1W-kcv%L}@Hr@JN<YAixMRMLu!o|N8p;&q$_1j=8n@ zru=W@9I^-Ma%^Eb{7U?<3!`~a^IKv{4Pw~g=0~rMXLdjAS>pKr1hfrF8&^gc?0Des zKARZa58;0a2K=A<_;VgDEPj00A!sS=u>l(YbNYHNJ~!;4Ol=_{Vmokh@Vhkr4OlI{ zs?;kLtGul9-TY7aIsa=%B?{f#T}S!Mh=KinIG%6e?=k;S{ztNI{EwdNgkR44#5!hd z933CqdCC&Z8~=m*q!&ow99UZO*or^o|B?TRog+M*zBtc_@YqTET=Ue9mvPQziyqg0 z!vDqpGuOH0m0b$!k9N@CAItyYNp7l-jg|0f#-MySd=wq{>#OlUG2yl=wzhGgt?%Ri znZ_6I#Q&hX*W&*-<$uz<79!t~|KTl%0fT^bb!Mxj!%K;Z-6$oK^Q(KH$a&Wek|;y9 zqCn)VvUt0@PelilUImKL`vm>NtpMyA13;u7wqgOm>q#)^aNyu}ND{{MUt5ya?Uc=b z#+VqR0L`b>g7S95x1J0j2Xmd6vFVO#2fT+kSr1^6Sgp0~THG$B1ZXvSUtOn95@dtQ zkGbw@=GnXt@44?j=#bK12L)%c@1DzyQ@axO)$Os-pi%SLrr9bw+rmTb?p4kngPs>d z8@i@w*NKDfap+2b$a7>FdQ1UvHMaaug1W{Nd;AEwV;GcQY=A;hw27>$_5?b>ze528 zKCj&_6Lr45YFnsuhX&AF9S{u{lHlw)a=x$;`jKMMM$-(BZdLjm)VG}_9r(Zovu-|3 zY|mL-W|zJr;=22K=Ke}}54~Q}zQ+Hp(idEX=oOjk&$cD9S}&-M*S?#h59sUh59Z)D zn0!p&Uktntxjh45l6LTsoA9lJx3<eH<GcL7$}cKA^v}LHHz?Kfg6TDhxlU9dM%yXX zlOFuy84ASTxP7?~R?HU3JEwV@T!oDY+a;E2!z=4tQV1)ecJfDW+>@Zx<0Czexn_u8 zGUBbdD+63$VgK;G$(-LzhnzcYly;Pp3#yMM_K_Fmuov%#v($%noa6Ii1Z`3Io&V#l z_`f@ko4nqf|NXg_d_Vq2Ng&(w%H|?Fp^umKQ9B>zB)E}#WGy^>&HvNzOFWnoxBBxP ziJAz;|3mm6znkrBo+<mw5&)i72%7mC|Jy3^H1rDpb6`xoS{^wE{H||j(e9DhT=IFI z1gkiAK0iKw8M*oTXZW9O*vz&1oi+S#=i*Q)VX=>7<#?W*$QK_cKwqEd|ENihBP#Y- zgcy)HssUbah=)72Id%d}2;!JO$Ny;`6yl!*DfR%Wx3vr4%?pSBBL;QgAG0H4uy2j* zI9PxyCFVIYf`D@HtM*=PE?<iOM<)C_3?H2T!^K^uAUXl!|9OlgVb?&%o1d0(Fz59> z{2!mK0~BK))XeLx_<#B$r_a|L^Z)ksPW&Iu|NUkccujYx%9?R`VmAPj(lqd7fG0nw zP*T4u8142o2rx$2uOzA=*=$v7iDLp#3Q7bU9`KuTLt|jYGg8K$VF}}zQ}Kz7{M`!N zIpQ9?pxf5zJXbgKo$*k%({u+rM*QoC(?}0n!v@f!PnYG~I1dMtX|G}@oWjCKg=aFQ z0Kcy}Mw5X^L19Y>d;ru0Om3lU2f>xFjSx6Ow^^1wQ9|LQm#j3l#_(*=ndLc2$gto{ zYwK-w0(S#aZPBys-85D_TgDizU{^=Z$Xq^%QmS87bSU<eCzosjHbRF+5}$)B>9W|W zu|go+LgMHO=Vrz&)41kV*ZlGvNWCV3?S)iTrDT}B<t>8=F%(#%?T9|nm@bBGNWH$< zTI*4DD3ffiuen5Y<j70}?tG6Vq&Fkcr-)bUvrhi6&#oN+`i4Lz`*6$llTy1yd(}>v z_NA1KHI}S)qRj3}^F5Nb6QSKNu<z^f30`xozMo&_x{3<0zm%sm2=+mnk+Ta)MbW0~ zI$D>-yePTj1mkVpCT&{5#ijR*$?O90Ml$fG4FR0DBF;oD;DnCeW{j{@$xT)_uz$>G z8e|MIx+jsx5Tf~LmBz=}(e7i4q$-%+d^=z}y&pWMea;;s2B_u8sjX#5DyX`=1!4+( zbLN15##=Ny28D5k)Ex%KhEKRZ72w=;gSnX4$3%`CePq<EI;4&Hn)?y1%hl#V{-<`i zAW$L-iO2arKEeMQN8*~cD-;8uDNpi3I0us}$;NdXH~yDf8~<;?on$+3K+{BYg|>SK z&_QSfXe5LYuke3<hX3=%|8e90_C@`Va|QS=jgXf#MkFG`h3+FTGTB#sPydfHZ?2&< zXy}mtAHbzTh*iS(mbv6##NHHk$+4sDbP_ij6fjMYKPTrmvMh--9G8;g;v$CR2MIOa z9*g_Q|0%%ABKDV?Bjk6Tf{t*YxnRA=9)S>L2Wl@~iIwvMFHlU%rDSj`L@>Yfg#i8w zhT9Zy%^x!qL#B#bJjA)us(PV9SeXA`cf7{`3^~pJyN^Dsv8G@L?_}RC(*_9^B<32a zJsCz^AD)Z<`@)xdm~=;&EfrSFsmB<bpy3ud98|?(KK;b}zb6)ob4&bv5)JzrK7Oj5 zq0lQFS2%)|_FjCLNgvjC$G<p}fSi!5zoMby+ax@Xs8I&Szcx80;gD_eT8saKUndJD zgVME@ry?HW|BCIRwq5~cc>E8n2rHV*e={~*RJF6`wukqln|g)+D~v;ibjAM@=Bz@4 z?$w1=)-D&uhMO=Bwv%zolOl{0KEeMLs!=`gzxtTrG{hT!0CXgJ+E$zA&i@7uQLpfS zk*9BR2>L$h`?_pp{%3v=%4`J?ndhpg9LI|4l>i6P$dY1pL~sI#FhEEcCy=7_dnA=9 zp+gxrMHN2{CSuVj+23?mr0=ldxD>O<Ih7o;nnb#2X%vSWLjgtOp_Wv+7E|)xK;m6n zC2bORacqSdLu#&LXf1cX5#ai|0OndfKO`^2CT%Dv{U2IYNJSh3*_H<rS?+)!qqhN` zc}Oe~wFOZtnGROVhX(q}-S8kQY-{UjpLvKd`XtIEur1Q<q)0)@8qn1z0gZY@qPAK4 zQAjKi5P{sgKwl(ecuUVk2TN&&MbbQaTYut~c*>Aoy7K%~+_Dcl9+S%16>v4<J2)8t z_3x~-2LqM`YJnFIv5lt=u3A+G-{HAPAsGN)zdvO&Ov%=s);pZX)M^j{htQDw2D?+H zcr0{yT5`M=4nDjP-V?VxE%DS%-?LIqKOcW@7fmn&n&H@CTQFm{B$FKFapm^k9BudO zujeI6ytN{bx;NkEG%!*xeMfTt;i>8Js<h$MYn3yy+uvY?i1E_dPd+GoTsCuDmGCPt zI9rdMBPiKTpG^QX8FlS_9Ji&$2G(l@C!cZ!JSsoGjQPXM1>Za^y6zRJCMZ;VnW#v~ zfTV+<UdARFL&K7>3N|4SBqc+lT~oeV{Mi$X?6TDty*$(Zuu?{x#6`pGyoT-dh*?6U zjWu&jv6A|2;o^g<gP%QJqFN|WA6~wH0Lw!E00K9(EX}`{RW2X83XO9ia3icZmRxAU zJ+*Y#+;Ch-@iLX4Jh9(&uLo?4q%gpfJd=uw^>t8>aAw$<j7#;A<)nx6bn6CkeHrFA zl|Ox|P>u`RsF6m4+vS`q(gL?M;8~VPC1hz%ZA({>+~<kN0Q+y3Jia;lhVvT!M`8!h z1vq5`_Lbwzf{sw|f%22^bffnJZO-r4{V&7FpMB``6LTuoM;Hi14PxxyT`>oQQs6u3 z1y~*YukKJRw=gE-XWy)T<FdBnMcm&Uk@vn^z^(65dDC9BCX3jWorR45m-F&9``7*d z!-cQ1lLMBFOE3RBX~7dU-h`pQ_hWHgC|xV)@W<c*XpH(7&&2jfKrX+*VYnBZeMoEs zDAZ19BDlX;M|k=FH+j8G{Kfeg=cEnoa_blVyT<dR&?2~-uL>Mq^N81rF5+o%&QZ_L z&n&`P5`kZFZx@rZtpb0hQ^oj4_6`Zfka24L37{tE>nU7g_>iK8mtW3pRd4t|Zv0<n z$I<-1is<6z7~JjT^}>!bx{c?KVc=p7Oi#-BYnkMMGn_*4Y{#04N8%7ax7f4|KWK)E z8zQY9i^h^TMVWRY{M18UezrX0dLsf7$hh;ZeIbZRJP{r8)d=Zr&nMUnWmqYyL_sHW z-kz`u0$qwP#v>jxBihM)wf(ED=UiBjO;yJe_&*W5$ki{(3G!x}89rV*Kt+;|vMS#I zUbA|v9LwT=iaOX%%<Hi4{%?}a{bv>dsNc>nZ<C%?_K?L2(gL=PJSEZI^fGxWbpm^$ z|A*&&L9XWSvjT>Sj1`m%P}>Dm_;|Z!N{zASe{^_aa5%-wuB`p}Tg#R)vk}@D3jLq_ zzrK}LUzi+U{=n3qqN-NO2gp3AXcPn@x5@$<+JmFlT!Mpy?MxD{48096%MGx0^TE{P z?x>yb5nzW6(}EjRd2-syZEju+kFX$#gkpd5b~9O6D0<Ci%)r;#Co{7#=0>)Yd8;z5 zJfS8DpePZ@jOYTG#v!bWkC2_?a|J!C&6Dw)p3!E|8pJxR26UFwY<E?#xji2sauzDb zNXA1p3GX~}033wPlU5fAbfSFlZ;<5yAUhDhHp|~j3a9*{9YqD(l_uXNudff;N$b^K zgLxQO;hn0)@eJB)`fdR>*RrPrGB_y3>mBI!9FBvhcQV;!rMk(XNTfLUj$x9?W4OO3 zmI($}@;Q)_0e+aftNPH{5#+^l00ybdPBh0w>06SR^2G$8KCJ@H+4GB$8-9XSQm;AL zRfhaMfh9-Mu2VZ1OZL2>UT_6G%&t3XIi;ejxBrE_H(RnK*^vVQkF2|NQz%VRWWHd& za(;LCFmEGG;jsHwWjH3>psG;w6Iqp8#c2`7y_Fexj=L>bQ~{W|`Ef-f*bQ*Nj!Mcs zEZ~IQWYPIo(0Snc13olf`esK5nKnX+s?K|0yx-TVfOptfZ^;SmVfn|=iTwCG#vA7R z37Lwn&oMp7i#3MhY7@F@0_g0Zm!7#@b?KzIrSD)^nxHLgr#)7ZRH?)XOi=na&1n+m zb>Ll2bdoaDD#{{Qt?rwzB&=a5N^_c-m6G7Ls)}&ija!fhPrv4U<Y3sP*q66yol#`D zVV_jnUAzgEb)#{Ss&D@g)1r7W*T+QsEUFSFj$XCGCC(XaR|VcYSjz?ZWVe}&!W4tY zN(NVQE^q>cFM&1&zO11hj#JS-UDpeN1Mx`>98xmP+3W8Y6{I+{hv1mHAdVH1j=|67 z|7)JdK@)2KU%O^1B*{tLYhn+`0SW>X;<!u6x9?l8w}UjgSuW~`LDu;sFEK&*^~a}U z9hTYUv5v9OAN`;7i{uTco7i*0*r!h@MN%*YBlX_N${QoV(2o~^S$DY#{mAQ=9;PtT z1E8G{>&#yc9Htu~|6k)ve~$ir|6kGn3x)!d%6jeovxFZFOSLfNXdj&wPt#Q>*m(2v z7sOEuzZ!!f<h9Hh?I#S5gE*T8G1PAn$CXGF4XSGb)jP=6HAhXfUSm<>ucL>u<bg-Z zy2(z?4}-dPxyA?t7-RqBf4-Uz{Eq;xxUf>em?=QyAsjfM>yrU$+f*0i+pD-h0-H@< z1Z9!`!}ve8NJIR85LFp-QnCTRDdNxC(Y98|wT1kgH_b>X!+&M^7zrUBJX&qySdFd! zWKuRUZ(@yrarm+1mj@0HPV6Ybe2-P9s#{^_w8g;g6>)2O8T!I%H)7<5m2*(1$Fc^a zB)Dd$>N$R+SozX>%S|QVr5kW`VJ^f|V#oONQVBRx=$K*j%MiH0b%>Wvv>m~f(Z~3| zYHFyQdSxm8kB9iba`F-`I8O143pwS<<rD#EnPU<b=TdfCvdaGW(T#K_Y~7)t^R}S8 zbCQ2}#FR{NP3Ls`nhUO1P$Goh1~GcG<Ud$+5UrNYg@uNAC^8ezMT87fm^*_tZ9$Yd zHgn({eBKtoXQ81vm<>VF6PU4)KoS#O0>;0?49XR;0pU1qPdpIslnG6rvro*o${a-% z8`U93(B57sP>isOPtEEFs$7%xXw`clib^WYy(pVq>B;sdx5~KvWNi1|WQOWoh8rCU zCcr^27PumHY0z0_rJukbOfo6ub=yftjmSx?MF}<6u#*npU|cy#5b=FKGhizFRp%~Q z6e7lsZ0k2)cY*n<XcR0Kbt`L&b-0{s#Jv0?O_J032|4Y+avHFVqx4^WGJIegBfYUJ z!c82huK1do2oV&XFP!FwfW}M`vE`v`sH`XfQb`7qo%Kta#G<016BOFwdfQpsw;v+J zyJ?ri*g%zwx;!D&c}{ZmaJ(Rbsc}a7(;#j?<JrM?`#<xYNGo?I02n9fZ_k(Q*aF#; zs+Mi7(`}jcG7!WVN?R+B(Q3}Ss0;rN7@!pk)~d)wc$JC)Vz!H-uV7gmNp7-&jxeSM z_=~0EDn>EkMMnlzrD+s$w@MYAbkegGPa*9xU+cghg&(a@3q$CuwG#Ssg>|uUF&d^0 zrgEJE>%RV-XIN_j)iO4gK?~EUV>{@p#;teFQ{FKkR*nJ0h8i=`H#76A&kF;P{PV++ z4FhEmaJ2K%t7oRbCR#icume&9MC~FC7SF}?ApVv^SK#7Qh?fAHF882lvdE_2&QXND zY7$J6LBf7&+0<p*NB@0&ewsd4VJmRBXCcrp@*0ENW8+co2I$;pFi{wQpL3fZIM_EQ zQ6}pLe%N6r(6-08{&fB?sXI9=<ibJucH2-5xvpD+zaKV)3{Q&A73=cVpV28-(U$ex zum)EJr(SBh`BGPDkc4H`?UiqEGC3QK7XnW&Lh89+1&NqX?R|}5bmEcpe}YTA<Nt2o zEz}geLHXHx0;h7~&#)IM0mc7wimIl@u3D*xa#Uw&xB#y%y*g;Sk~fAbGq8o-_scq1 zcCMmcnWmOKo*q|3@NGkDB7DX&p+a&Y`QKomARygiN?-gRjJ+r@a!u`7L)&5*$HLtu zSf=&`dypJBoHLF8la;AbyS?~vY<3>#DV+b;w<ik45nOc1nAhx5#!0YZo8V8y(zL{2 z>_J$uxfa`s&|{EX=!q@gv-td|aq+hAlaGH$53~+)`#kTy`a9hHQ!pb;zStO*i7Mm& zU`?CXw=c7hV`m2#|DVZNB|=HDzUS`5#17eUW!}U5VB}wS{ufx%ei`e1IsPw;|69Jf zQEV4F-iiNDCboq*H)N+hZsla+MGsd4^>D_5vE@@%INFqN|8cA_ZlLywhY@8TY(C4s z6|6Jnr~DXeNsVP8L|-WWhq;%Hede|2if!)veE6*1kwe|{S<4;Q?t`=>J~U^W@#N6+ zT^2pw#rkO~$I}?_apu#>Y3zfq={#-a>f!sS*cmZrKuc&$h;noQ53g~`6ijt>d)xYu zN!>LlYXW327-a<S^>Kl?IM&<*n_S<6@H)5XHqB6T+a<xfGj0WvTLB@b<GH?t!h#Ir zWstiME^I;3KW!nNVPr7X#<D=wyNAD~ASgNBuI{Zb2}nHf4kgh*nrkMJ0Ny|$zkDr> zWK?*a(B$SSG=RJkNzwY}k=Pd2;a!s)GK6%e&9yeyw#0S<&z~2PPy@E!eO0K$8Zg$_ zELZFU^3_(y6-0>qutOJ>ep*ff1Z9h%0D4u6&%^wiIX?rp49X}N7p_{$5`JD=zglNm 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ÉpO>stvPuLrw(j3+TqELo}QfhUqzOu)H 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 C)gYk& literal 0 HcmV?d00001 diff --git a/assets/images/splash-icon.png b/assets/images/splash-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..6b1642a0b70cd2c80d81f959ec130e5bad33489b GIT binary patch literal 3317 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 UD9cJ(z+Vc8<mTmCg%4%^2XZ{;+5i9m literal 0 HcmV?d00001 diff --git a/eas.json b/eas.json new file mode 100644 index 0000000..8ab579b --- /dev/null +++ b/eas.json @@ -0,0 +1,24 @@ +{ + "cli": { + "version": ">= 13.0.0", + "appVersionSource": "remote" + }, + "build": { + "development": { + "developmentClient": true, + "distribution": "internal", + "channel": "development" + }, + "preview": { + "distribution": "internal", + "channel": "preview" + }, + "production": { + "autoIncrement": true, + "channel": "production" + } + }, + "submit": { + "production": {} + } +} diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..5025da6 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,10 @@ +// https://docs.expo.dev/guides/using-eslint/ +const { defineConfig } = require('eslint/config'); +const expoConfig = require('eslint-config-expo/flat'); + +module.exports = defineConfig([ + expoConfig, + { + ignores: ['dist/*'], + }, +]); diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..8d17119 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,15 @@ +module.exports = { + preset: 'jest-expo', + setupFiles: ['./jest.setup.ts'], + moduleNameMapper: { + '^@/(.*)$': '<rootDir>/src/$1', + }, + // react-native-worklets (reanimated 4's underlying worklets runtime) ships a real native + // module with no Jest mock; its own package provides this resolver, which steers Jest away + // from the `.native.ts` entrypoints (which throw under Jest — see react-native-worklets' + // NativeWorklets.native.ts) toward its JS-only fallback path instead. Without this, importing + // anything that pulls in `react-native-reanimated` (e.g. any UI primitive built on + // PressableScale) crashes at import time under Jest. + resolver: 'react-native-worklets/jest/resolver.js', + collectCoverageFrom: ['src/**/*.{ts,tsx}', '!src/api/generated/**', '!src/**/*.d.ts'], +}; diff --git a/jest.setup.ts b/jest.setup.ts new file mode 100644 index 0000000..fe72a1e --- /dev/null +++ b/jest.setup.ts @@ -0,0 +1,81 @@ +jest.mock('@react-native-async-storage/async-storage', () => + require('@react-native-async-storage/async-storage/jest/async-storage-mock'), +); + +// src/config/env.ts reads Expo config at MODULE LOAD and throws when `extra` is absent, so any +// test importing a module that transitively pulls in `env` (e.g. api/client.ts) fails to load +// without this. app.config.ts is not evaluated under Jest, so supply the `extra` block here. +jest.mock('expo-constants', () => ({ + __esModule: true, + default: { + expoConfig: { + extra: { + apiBaseUrl: 'https://test.invalid', + wsUrl: 'wss://test.invalid', + }, + }, + }, +})); + +// expo-secure-store is a native module with no Jest preset mock; src/api/auth-token.ts (and +// anything transitively importing src/api/client.ts) throws at import time without this. An +// in-memory Map is enough for tests that don't specifically assert persistence behavior. +jest.mock('expo-secure-store', () => { + const store = new Map<string, string>(); + return { + __esModule: true, + getItemAsync: async (key: string) => store.get(key) ?? null, + setItemAsync: async (key: string, value: string) => { + store.set(key, value); + }, + deleteItemAsync: async (key: string) => { + store.delete(key); + }, + }; +}); + +// expo-image renders a native view manager with no Jest preset mock; any component that renders +// <Avatar> (which uses expo-image's <Image>) throws "requireNativeViewManager is not available" +// under Jest without this. A plain react-native <Image> stand-in is enough for component tests +// that don't assert on image-loading behavior specifically. +jest.mock('expo-image', () => { + // eslint-disable-next-line @typescript-eslint/no-require-imports -- jest.mock factories cannot + // reference out-of-scope imports; requiring inline is the standard Jest pattern. + const { Image: RNImage } = require('react-native'); + return { + __esModule: true, + Image: RNImage, + }; +}); + +// expo-crypto is a native module with no Jest preset mock; stub it with Node's own `crypto` so +// src/lib/install-crypto-polyfill.ts can be exercised without a device/simulator. +jest.mock('expo-crypto', () => { + // eslint-disable-next-line @typescript-eslint/no-require-imports -- jest.mock factories cannot + // reference out-of-scope imports; requiring inline is the standard Jest pattern. + const nodeCrypto = require('node:crypto'); + return { + __esModule: true, + randomUUID: () => nodeCrypto.randomUUID(), + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- mock-only; expo-crypto's + // real generic signature is asserted separately by src/lib/install-crypto-polyfill.ts. + getRandomValues: (typedArray: any) => nodeCrypto.webcrypto.getRandomValues(typedArray), + }; +}); + +// expo-image-picker and expo-document-picker are native modules with no Jest preset mock (unlike +// expo-file-system's class-based File API, which the preset does special-case) — any test +// importing src/features/chat/lib/pick-attachments.ts (composer.tsx, camera.tsx, or the module +// itself) throws "Cannot find native module 'ExponentImagePicker'"/"...'ExpoDocumentPicker'" +// without these. Bare jest.fn() stand-ins are enough; tests that need specific picker results +// override these with jest.mock(...).mockResolvedValueOnce(...) locally. +jest.mock('expo-image-picker', () => ({ + __esModule: true, + launchImageLibraryAsync: jest.fn(), + launchCameraAsync: jest.fn(), + requestMediaLibraryPermissionsAsync: jest.fn(), +})); +jest.mock('expo-document-picker', () => ({ + __esModule: true, + getDocumentAsync: jest.fn(), +})); diff --git a/package.json b/package.json new file mode 100644 index 0000000..4cbb026 --- /dev/null +++ b/package.json @@ -0,0 +1,81 @@ +{ + "name": "chatbotx-mobile-app", + "main": "expo-router/entry", + "version": "1.0.0", + "dependencies": { + "@expo-google-fonts/plus-jakarta-sans": "^0.4.2", + "@expo/vector-icons": "^15.1.1", + "@gorhom/bottom-sheet": "^5.2.14", + "@react-native-async-storage/async-storage": "2.2.0", + "@shopify/flash-list": "2.0.2", + "@tanstack/react-query": "^5.101.4", + "dayjs": "^1.11.23", + "expo": "~57.0.15", + "expo-camera": "~57.0.4", + "expo-constants": "~57.0.13", + "expo-crypto": "~57.0.2", + "expo-dev-client": "~57.0.14", + "expo-device": "~57.0.1", + "expo-document-picker": "~57.0.1", + "expo-file-system": "~57.0.6", + "expo-font": "~57.0.1", + "expo-haptics": "~57.0.2", + "expo-image": "~57.0.3", + "expo-image-picker": "~57.0.12", + "expo-linking": "~57.0.7", + "expo-localization": "^57.0.1", + "expo-notifications": "~57.0.13", + "expo-router": "~57.0.15", + "expo-secure-store": "^57.0.1", + "expo-splash-screen": "~57.0.7", + "expo-status-bar": "~57.0.1", + "expo-system-ui": "~57.0.2", + "expo-updates": "~57.0.16", + "expo-web-browser": "~57.0.2", + "i18next": "^26.3.6", + "openapi-fetch": "^0.17.0", + "partysocket": "^1.3.0", + "react": "19.2.3", + "react-dom": "19.2.3", + "react-i18next": "^17.0.11", + "react-native": "0.86.2", + "react-native-gesture-handler": "~2.32.0", + "react-native-reanimated": "4.5.1", + "react-native-safe-area-context": "~5.7.0", + "react-native-screens": "~4.26.0", + "react-native-web": "~0.21.0", + "react-native-worklets": "0.10.1", + "zustand": "^5.0.15" + }, + "devDependencies": { + "@testing-library/react-native": "^14.0.1", + "@types/jest": "^29.5.14", + "@types/react": "~19.2.2", + "cross-env": "^10.1.0", + "dotenv": "^17.4.2", + "eslint": "^9.39.5", + "eslint-config-expo": "^57.0.1", + "jest": "^29.7.0", + "jest-expo": "^57.0.4", + "openapi-typescript": "^7.13.0", + "prettier": "^3.9.6", + "test-renderer": "^1.2.0", + "tsx": "^4.23.12", + "typescript": "~6.0.3" + }, + "scripts": { + "start": "expo start", + "reset-project": "node ./scripts/reset-project.js", + "android": "expo run:android", + "ios": "expo run:ios", + "web": "expo start --web", + "lint": "expo lint", + "typecheck": "tsc --noEmit", + "format": "prettier --write .", + "format:check": "prettier --check .", + "generate:api": "tsx scripts/generate-api-client.ts", + "test": "jest", + "test:watch": "jest --watch" + }, + "private": true +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..22705fe --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,11697 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@expo-google-fonts/plus-jakarta-sans': + specifier: ^0.4.2 + version: 0.4.2 + '@expo/vector-icons': + specifier: ^15.1.1 + version: 15.1.1(expo-font@57.0.1)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + '@gorhom/bottom-sheet': + specifier: ^5.2.14 + version: 5.2.14(45a98be50302868954db6b093f845761) + '@react-native-async-storage/async-storage': + specifier: 2.2.0 + version: 2.2.0(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2)) + '@shopify/flash-list': + specifier: 2.0.2 + version: 2.0.2(@babel/runtime@7.29.7)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + '@tanstack/react-query': + specifier: ^5.101.4 + version: 5.101.4(react@19.2.3) + dayjs: + specifier: ^1.11.23 + version: 1.11.23 + expo: + specifier: ~57.0.15 + version: 57.0.15(c6665681e457b98938e269e908821e4f) + expo-camera: + specifier: ~57.0.4 + version: 57.0.4(@types/emscripten@1.41.5)(expo@57.0.15)(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + expo-constants: + specifier: ~57.0.13 + version: 57.0.13(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(supports-color@10.2.2) + expo-crypto: + specifier: ~57.0.2 + version: 57.0.2(expo@57.0.15) + expo-dev-client: + specifier: ~57.0.14 + version: 57.0.14(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2)) + expo-device: + specifier: ~57.0.1 + version: 57.0.1(expo@57.0.15) + expo-document-picker: + specifier: ~57.0.1 + version: 57.0.1(expo@57.0.15) + expo-file-system: + specifier: ~57.0.6 + version: 57.0.6(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2)) + expo-font: + specifier: ~57.0.1 + version: 57.0.1(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + expo-haptics: + specifier: ~57.0.2 + version: 57.0.2(expo@57.0.15) + expo-image: + specifier: ~57.0.3 + version: 57.0.3(expo@57.0.15)(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + expo-image-picker: + specifier: ~57.0.12 + version: 57.0.12(expo@57.0.15) + expo-linking: + specifier: ~57.0.7 + version: 57.0.7(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2) + expo-localization: + specifier: ^57.0.1 + version: 57.0.1(expo@57.0.15)(react@19.2.3) + expo-notifications: + specifier: ~57.0.13 + version: 57.0.13(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2)(typescript@6.0.3) + expo-router: + specifier: ~57.0.15 + version: 57.0.15(4297cc0b7880e6d80de6379924b45edb) + expo-secure-store: + specifier: ^57.0.1 + version: 57.0.1(expo@57.0.15) + expo-splash-screen: + specifier: ~57.0.7 + version: 57.0.7(expo@57.0.15)(supports-color@10.2.2)(typescript@6.0.3) + expo-status-bar: + specifier: ~57.0.1 + version: 57.0.1(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + expo-system-ui: + specifier: ~57.0.2 + version: 57.0.2(expo@57.0.15)(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(supports-color@10.2.2) + expo-updates: + specifier: ~57.0.16 + version: 57.0.16(expo-dev-client@57.0.14(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2)))(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2) + expo-web-browser: + specifier: ~57.0.2 + version: 57.0.2(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2)) + i18next: + specifier: ^26.3.6 + version: 26.3.6(typescript@6.0.3) + openapi-fetch: + specifier: ^0.17.0 + version: 0.17.0 + partysocket: + specifier: ^1.3.0 + version: 1.3.0(react@19.2.3) + react: + specifier: 19.2.3 + version: 19.2.3 + react-dom: + specifier: 19.2.3 + version: 19.2.3(react@19.2.3) + react-i18next: + specifier: ^17.0.11 + version: 17.0.11(i18next@26.3.6(typescript@6.0.3))(react-dom@19.2.3(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(typescript@6.0.3) + react-native: + specifier: 0.86.2 + version: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + react-native-gesture-handler: + specifier: ~2.32.0 + version: 2.32.0(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + react-native-reanimated: + specifier: 4.5.1 + version: 4.5.1(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + react-native-safe-area-context: + specifier: ~5.7.0 + version: 5.7.0(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + react-native-screens: + specifier: ~4.26.0 + version: 4.26.2(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + react-native-web: + specifier: ~0.21.0 + version: 0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react-native-worklets: + specifier: 0.10.1 + version: 0.10.1(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2) + zustand: + specifier: ^5.0.15 + version: 5.0.15(@types/react@19.2.18)(react@19.2.3)(use-sync-external-store@1.6.0(react@19.2.3)) + devDependencies: + '@testing-library/react-native': + specifier: ^14.0.1 + version: 14.0.1(jest@29.7.0(@types/node@26.2.0)(supports-color@10.2.2))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(test-renderer@1.2.0(@types/react@19.2.18)(react@19.2.3)) + '@types/jest': + specifier: ^29.5.14 + version: 29.5.14 + '@types/react': + specifier: ~19.2.2 + version: 19.2.18 + cross-env: + specifier: ^10.1.0 + version: 10.1.0 + dotenv: + specifier: ^17.4.2 + version: 17.4.2 + eslint: + specifier: ^9.39.5 + version: 9.39.5(supports-color@10.2.2) + eslint-config-expo: + specifier: ^57.0.1 + version: 57.0.1(eslint@9.39.5(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + jest: + specifier: ^29.7.0 + version: 29.7.0(@types/node@26.2.0)(supports-color@10.2.2) + jest-expo: + specifier: ^57.0.4 + version: 57.0.4(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(expo@57.0.15)(jest@29.7.0(@types/node@26.2.0)(supports-color@10.2.2))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2) + openapi-typescript: + specifier: ^7.13.0 + version: 7.13.0(typescript@6.0.3) + prettier: + specifier: ^3.9.6 + version: 3.9.6 + test-renderer: + specifier: ^1.2.0 + version: 1.2.0(@types/react@19.2.18)(react@19.2.3) + tsx: + specifier: ^4.23.12 + version: 4.23.12 + typescript: + specifier: ~6.0.3 + version: 6.0.3 + +packages: + + '@babel/code-frame@7.29.7': + resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.29.7': + resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.29.7': + resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.29.8': + resolution: {integrity: sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-annotate-as-pure@7.29.7': + resolution: {integrity: sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.29.7': + resolution: {integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-create-class-features-plugin@7.29.7': + resolution: {integrity: sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-create-regexp-features-plugin@7.29.7': + resolution: {integrity: sha512-907Uymvqgg1dwUA+7IGwFAOSYzQOuzPXKNJ1yxzwPffzkYFg2q2eHi1fIOs6sXkG9NbIUMunnUlkYsfRFNvomg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-define-polyfill-provider@0.6.8': + resolution: {integrity: sha512-47UwBLPpQi1NoWzLuHNjRoHlYXMwIJoBf7MFou6viC/sIHWYygpvr0B6IAyh5sBdA2nr2LPIRww8lfaUVQINBA==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + '@babel/helper-globals@7.29.7': + resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-member-expression-to-functions@7.29.7': + resolution: {integrity: sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.29.7': + resolution: {integrity: sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.29.7': + resolution: {integrity: sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-optimise-call-expression@7.29.7': + resolution: {integrity: sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong==} + engines: {node: '>=6.9.0'} + + '@babel/helper-plugin-utils@7.29.7': + resolution: {integrity: sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-remap-async-to-generator@7.29.7': + resolution: {integrity: sha512-16AMiW26DbXWBbr3B8wNozKM0ydMLB892vaOaJW/fPJdnT8vJk5sdkQcU/isqUxyCE0cEoa8wZOcbgDuC4b6Og==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-replace-supers@7.29.7': + resolution: {integrity: sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-skip-transparent-expression-wrappers@7.29.7': + resolution: {integrity: sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.29.7': + resolution: {integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-wrap-function@7.29.7': + resolution: {integrity: sha512-iES0Skag9ERIF68aXadpO6dbXa03mNWK3sEqJaMnLNs/eC3l0lkImdfoy6Y09/SfkpawdAB4RjQ7PVA7TcVGdw==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.29.7': + resolution: {integrity: sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.8': + resolution: {integrity: sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-proposal-decorators@7.29.7': + resolution: {integrity: sha512-EtU0Hi3GvrTqD56xKmZvV/uCXK2ZbwVNPNLAquVItcAZpUhkXwWlo3Fmj0c2LxgSf2I8IDULeAepwNP1OefLXg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-proposal-export-default-from@7.29.7': + resolution: {integrity: sha512-p+G5BNXDcy3bOXplhY4HybQ1GxH3i2Tppmdm/3epyRu2VgJJZuUlZ61MqRTg582Q7ZLBdP7fePYvsumSEkMxcQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-async-generators@7.8.4': + resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-bigint@7.8.3': + resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-class-properties@7.12.13': + resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-class-static-block@7.14.5': + resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-decorators@7.29.7': + resolution: {integrity: sha512-9MTTLbF39X6sqM92JPEsoI7++26hjZvzkxKZy64aMhWLH2mPkJ/Q3AV4QLmls3R14FpSpkOwQQfUh962JGQxxg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-dynamic-import@7.8.3': + resolution: {integrity: sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-export-default-from@7.29.7': + resolution: {integrity: sha512-foag0BB37ROhdeIX9O8G0jX7hw0UekJc04cHMrYLOnrErsnBKqJGHJ8eDRpoCFZBvEPPygmmtw4qyU97qa4oOw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-flow@7.29.7': + resolution: {integrity: sha512-ajMX6QPcyomotqwpzhkYGxcK2i/us0rs1Qo9QvUpa+Fca0FTmqrzKrctoIYLMxcOhGZldGT/BAVkRGTWBiR8gQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-attributes@7.29.7': + resolution: {integrity: sha512-zGYcYfq/WmZ4V+kBIXQon9dSSc8ircGZqw9ZaNhhGj9nZkeBu1jHLBDQqYYi5WA9uawvA2sIMbry2nCFhf5Djg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-meta@7.10.4': + resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-json-strings@7.8.3': + resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-jsx@7.29.7': + resolution: {integrity: sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-logical-assignment-operators@7.10.4': + resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3': + resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-numeric-separator@7.10.4': + resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-object-rest-spread@7.8.3': + resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-optional-catch-binding@7.8.3': + resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-optional-chaining@7.8.3': + resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-private-property-in-object@7.14.5': + resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-top-level-await@7.14.5': + resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-typescript@7.29.7': + resolution: {integrity: sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-arrow-functions@7.29.7': + resolution: {integrity: sha512-N7zArUXWzAMzm+/N0uPBeVB3Fam5lMxtUwMmDK5f/IBBS7a7p1qeUoxd/6CckXoxUdgsntq1Dh8xNW06maZbDQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-async-generator-functions@7.29.7': + resolution: {integrity: sha512-d98gXZkgswvkyohMBABkhm3GeXhYj8psWfwQ2C7gtfrKGTykQa/iOIi+JJhwMjPlZ6Vm2XN+DCf3Es1EoG4ZLA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-async-to-generator@7.29.7': + resolution: {integrity: sha512-pcUb2SS+RMo9TWVBwKGI5ShtoG7R+zBsFmCKDa6fe8c+hPr3XJlZgoE5j6i8W7gDjhyvy+85vmYexanvXh3d1w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-block-scoping@7.29.7': + resolution: {integrity: sha512-ONyr4+AZhKh8yKWInVxU9AXA9EbsyeLcL6V0dJy6M2/62vuvpGm29zzuymbTpdc451GEpDIdAyPLP3r+P61yKQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-class-properties@7.29.7': + resolution: {integrity: sha512-GtcpjFvanPfzNQi3eTitsCqtRRmmqzpy/A+yhTR1HaZo1Ly3EA8ZXxlPyHdR8/IuRMYc3E4wdGBewB2QKQjAaA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-class-static-block@7.29.7': + resolution: {integrity: sha512-kibJgmEdX2iMwsHY2tSZNDgj8PwIlCQz7FK9KuGKO8zsuoUwSEhoNnNVp/emKWrbY4HeO6kkXfdMqRKKKXBm2A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.12.0 + + '@babel/plugin-transform-classes@7.29.7': + resolution: {integrity: sha512-qV0OGGBVacduzQHE649JyCneOFI/maT+YKsO+K4Yi3xv2wTPNjM/W2o2gdzMwEAZz7fXNTHAe0NcSg30bIN69g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-destructuring@7.29.7': + resolution: {integrity: sha512-iPX8aD6H9zV5s7ZsqTdNocPN/MGQ5sSMnElKrktxjJRMnB2jN/1p2+R7GkfD6CAYoVFqy5A4XnSIUeGgJzIWpg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-export-namespace-from@7.29.7': + resolution: {integrity: sha512-24B2nOy2TeJSMheqwPD4DDQOV/elLSIlKxjZt4i05H5AgdPdWR3n18HnNrcJ+j76WJd9gbwb9jPjNYUy6RautA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-flow-strip-types@7.29.7': + resolution: {integrity: sha512-wRHeUjUjCZnMHmiO5bRgjFLcoEh7JyTdByOW11ahhwNa4V0bmeGEaIvt51yq0zQp2yWIpqfxXXPyUP6GFJZHOQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-for-of@7.29.7': + resolution: {integrity: sha512-zeSIHh0+E1Um1WJRXCFlHQYu2ieJNdivLLjlBEp+dIBu3S51n+SZZmIXjxnItw6pz56Cn+KvK68BIBVsxq2JiQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-logical-assignment-operators@7.29.7': + resolution: {integrity: sha512-A0H91hh6W8MFRkp5TqJmMr39jzGD1A1E1Ysiv2O06Sfbhkapm+XyIzxWCEh5kqwOZ1/8QZ0dY3SeQ7XBqfJd5Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-commonjs@7.29.7': + resolution: {integrity: sha512-j0vCldybPC5b5dwCQOJ21uKtHzt7hxLygJTg9eF1ScfaikEDNfzn94XoW5Fi+seBR0nCyL23xaBFFkq7dTM8XQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-named-capturing-groups-regex@7.29.7': + resolution: {integrity: sha512-vuFoLwr4qnv2xbZ16SQd6uPcH5FNrLHhk/Jzo++0XJFcaDsr4gjJVg6j398oMHiC+83k/GiBzviwF5KBJkPUtQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-nullish-coalescing-operator@7.29.7': + resolution: {integrity: sha512-idmp1dFaekP9GbcMvG24Kvw2BfhFZjHnNJCkV4WuIY4PskJzwI3f1N5OdgYke38T7rftO6ERulFRn2cFeZwRkg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-object-rest-spread@7.29.7': + resolution: {integrity: sha512-Ld98jn4c0smUywL57m7SgsHq3OpThOa6LqZJif3G6jYOovPleoFhVrBJ1WegRApSFB2wu4+RelAj9AC9G08Z4A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-optional-catch-binding@7.29.7': + resolution: {integrity: sha512-sLsyndxK2VwX6yNUOakMb7Sh553ZTe/vVM1XJ+9Z5aW1ytsc8xOIwmyk05NNjN60vkc5/KqoTH6hB4V41LJhng==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-optional-chaining@7.29.7': + resolution: {integrity: sha512-6GM1dhvK3gNODkXcEcMCOLEDCLSoZ/sBbro2Ax8HURyasQ4NshagQixkRFdh5niI6E4gmA/jYI/4aT7rRos3ZQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-parameters@7.29.7': + resolution: {integrity: sha512-ZDOBqV/qLYJI0YElr8DcENEyARsFQeESqWXH6gZlghYXuPPjvweuDhP4VyEi4BlUBlLRFZVjxoZDMjxhLW766g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-private-methods@7.29.7': + resolution: {integrity: sha512-/6Rz4DK1ETDEM/bWHsPHcaEe7ZaT1EqSXjtSP/L0DijOYuaUhiRiOKcwpZ8P7zR4xXEHc2ITdiCgBm9Tpyv9ug==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-private-property-in-object@7.29.7': + resolution: {integrity: sha512-+BNo06dnrzdNNqCm1X6YUaVv0DKk8Q+JYcoZfOkLhYWNCXzlwTSRq8zGWayT1csjcpNXV9CQTBRRbmTLZac5cA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-display-name@7.29.7': + resolution: {integrity: sha512-+1wdDMGNb4UPeY3Q4L5yLiYe6TXPXubs4NjrgRFw13hPRLJfEMw2Q5OXkee6/IfdqePIeW4Jjwe3aBh7SdKz4Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-development@7.29.7': + resolution: {integrity: sha512-Xfy3UVMF04+ypnFbkhvfqtmvwfe92qwQdbGZVonhE+6v35GzlofmOnA1szaZqzb9xYWr0nl1e5EMmzi0DNON1g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-self@7.29.7': + resolution: {integrity: sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-source@7.29.7': + resolution: {integrity: sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx@7.29.7': + resolution: {integrity: sha512-WsZulLVBUHXVj2cUcPVx6UE21TpalB6bHbSFErKT0Ib++ax24jjXe73FqlWvdylFOjiuPHYi6VCcgRad1ItN+A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-pure-annotations@7.29.7': + resolution: {integrity: sha512-H5E+HBgDpr6Q5t+Aj11tL7XkIui1jhbIoArVQnqjgXo5/3YxkN7ZEBcWF4RQlB0T4rrxJQbXS6kiFV6B7XTqUA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-regenerator@7.29.8': + resolution: {integrity: sha512-0UpIXPtdDtMXfnV2OJAVMLpj3H/92vmkA6lpSRakmycJvj3VUy6Xs1dM8tXRugupykr5WB+LpiVl0J8LMVg2mg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-runtime@7.29.7': + resolution: {integrity: sha512-xmAscdE/AsqRW7vutbPNoUmu/nF5SrLKPs7aoJgEjo35lLKA/Bc0i2rMv/hr1+Y0o1bQCiVtith3u2vdgRL39Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-shorthand-properties@7.29.7': + resolution: {integrity: sha512-I+WYbGBAiCn7nA6xBrlgPH+MB7HWb4u8pv5S0Pv7OtwNvIFvCCb24YlttKEeUFVurfBCEaOTnuhlqsb7f0Z5Dg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-template-literals@7.29.7': + resolution: {integrity: sha512-NCSEJ4sLFU2gqAub45HYh4fus2yQ36rr6ei6vpU7NdoJqCpxvEG8E6eJpscGyXP3VHD2Ny+fSXr04k1hoUrFqA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-typescript@7.29.7': + resolution: {integrity: sha512-jK52h8LaLc7JarhQV2ofeFMts4H7vnOXnqZNA6fYglBTZewRBE51KWt3BUltW1P+KoPsYkHoJeXePuz4zo2LMw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-regex@7.29.7': + resolution: {integrity: sha512-7D/x/23/d/3VqZ0QA+LGbZMlGwZjztBygSWWWsfTPoQ1oQ6Q1P6Mr3d0kk42XabyUVw+fha3LqdRsFqeKqvCyA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/preset-typescript@7.29.7': + resolution: {integrity: sha512-/Foi8vKY2EVbed/1eZx0gJEEwHAIxogrySI7rULcRIvhZzbvoE/b5qG5Ghc0WKAFKOHA9SD1x7RsFlOYdutIiQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/runtime@7.29.7': + resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.29.7': + resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.29.8': + resolution: {integrity: sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.29.8': + resolution: {integrity: sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==} + engines: {node: '>=6.9.0'} + + '@bcoe/v8-coverage@0.2.3': + resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} + + '@egjs/hammerjs@2.0.17': + resolution: {integrity: sha512-XQsZgjm2EcVUiZQf11UBJQfmZeEmOW8DpI1gsFeln6w0ae0ii4dMQEQ0kjl6DspdWX1aGY1/loyXnP0JS06e/A==} + engines: {node: '>=0.8.0'} + + '@emnapi/core@1.10.0': + resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==} + + '@emnapi/runtime@1.10.0': + resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} + + '@emnapi/wasi-threads@1.2.1': + resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} + + '@epic-web/invariant@1.0.0': + resolution: {integrity: sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==} + + '@esbuild/aix-ppc64@0.28.2': + resolution: {integrity: sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.28.2': + resolution: {integrity: sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.28.2': + resolution: {integrity: sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.28.2': + resolution: {integrity: sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.28.2': + resolution: {integrity: sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.28.2': + resolution: {integrity: sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.28.2': + resolution: {integrity: sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.28.2': + resolution: {integrity: sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.28.2': + resolution: {integrity: sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.28.2': + resolution: {integrity: sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.28.2': + resolution: {integrity: sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.28.2': + resolution: {integrity: sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.28.2': + resolution: {integrity: sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.28.2': + resolution: {integrity: sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.28.2': + resolution: {integrity: sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.28.2': + resolution: {integrity: sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.28.2': + resolution: {integrity: sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.28.2': + resolution: {integrity: sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.28.2': + resolution: {integrity: sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.28.2': + resolution: {integrity: sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.28.2': + resolution: {integrity: sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.28.2': + resolution: {integrity: sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.28.2': + resolution: {integrity: sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.28.2': + resolution: {integrity: sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.28.2': + resolution: {integrity: sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.28.2': + resolution: {integrity: sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.10.1': + resolution: {integrity: sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.21.2': + resolution: {integrity: sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.4.2': + resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.17.0': + resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.3.6': + resolution: {integrity: sha512-l2Ul9PrHsPCKcEY/ac7VgFj9D80C7S68sOKc618SyHDPK36s1XcFebXY0iTzUVn4Yq+YbwvSnDmCz9yxjX+QrA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.39.5': + resolution: {integrity: sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.7': + resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.4.1': + resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@expo-google-fonts/material-symbols@0.4.44': + resolution: {integrity: sha512-36JP9Chcy/QEVZ9ZGY4i6zInlyFPbQjkIm6gRNuWWltScIj0WR8rddcD57EIjSC5YKCe2ZKLO6eO/N5r8Jit0A==} + + '@expo-google-fonts/plus-jakarta-sans@0.4.2': + resolution: {integrity: sha512-6LYVmVGwjQvH+uzzWlVc9+oMj4lkNQ41aymVDjO+x8aFk8kCye20wOyLomYMZaMezA++Uf1mZRCw3W3Fy/hxEA==} + + '@expo/cli@57.0.17': + resolution: {integrity: sha512-PQc7if117dNh2qe+CHdlmSWpgwhFiP9CJ7XbwNF0w4KSKxFpJ2qUjDPoAcCMW1VZg1x30mEJ907bT8G1iDIZBQ==} + hasBin: true + peerDependencies: + expo: '*' + expo-router: '*' + react-native: '*' + peerDependenciesMeta: + expo-router: + optional: true + react-native: + optional: true + + '@expo/code-signing-certificates@0.0.6': + resolution: {integrity: sha512-iNe0puxwBNEcuua9gmTGzq+SuMDa0iATai1FlFTMHJ/vUmKvN/V//drXoLJkVb5i5H3iE/n/qIJxyoBnXouD0w==} + + '@expo/config-plugins@57.0.8': + resolution: {integrity: sha512-x6lx4s/19i39/+1dMPwb9tFCc4WR843dG5Yi+C64lhKL3YHX+6oKrT2Kv72PCEalnUY+usQDkB3NrLSrYOWtDg==} + + '@expo/config-types@57.0.2': + resolution: {integrity: sha512-ewW08OonrcRIsRKIlFvvcmmafE5zemb1ocu3HkNwtVPyRtj2w42pZCAkMIROYpcVBaPnc3mDT9UZDzwXWC3i6g==} + + '@expo/config@57.0.8': + resolution: {integrity: sha512-7VpAu2ZMXNZI0+Kn+nD3vQBIyST89LATNkZpnp/cXh8/aj7zUXO8d/T+hOxsvhOFwR8eQO+jKXEw8tQbidiMxA==} + + '@expo/devcert@1.2.1': + resolution: {integrity: sha512-qC4eaxmKMTmJC2ahwyui6ud8f3W60Ss7pMkpBq40Hu3zyiAaugPXnZ24145U7K36qO9UHdZUVxsCvIpz2RYYCA==} + + '@expo/devtools@57.0.1': + resolution: {integrity: sha512-GyUf+wFNkbttaX0jR7MZa9bm77U0IrLg6d2AjpxdyoXw/w4abHoXG0oFufwLMgP9zLTd5+Ct4X/ffNUTnlzZgg==} + peerDependencies: + react: '*' + react-native: '*' + peerDependenciesMeta: + react: + optional: true + react-native: + optional: true + + '@expo/dom-webview@57.0.1': + resolution: {integrity: sha512-lAKsME4SAq+8sf56oN0DX5TBYyruupoRxbWbD2xf9RnKY8y6x8eb9LCE5pxSN0qyWdqnp+0wmyWzDkKboThKAw==} + peerDependencies: + expo: '*' + react: '*' + react-native: '*' + + '@expo/env@2.4.2': + resolution: {integrity: sha512-28pqaEqwnmLduZ00Pq9HkSzE5wbj1MTwp5/n8nm8rD8MCjR9eUnVOwmNksPI3Be2ReAPO/DbPn1puy0mvoocsQ==} + engines: {node: '>=20.12.0'} + + '@expo/expo-modules-macros-plugin@0.6.1': + resolution: {integrity: sha512-cpsLZE4rqkc1Y3eZTkxB98jrqY1YXgetmtxFt8q89jBRmk3quRuk1BZo+VcnCSObZardjg99r1k5xijEMONFGA==} + + '@expo/fingerprint@0.20.9': + resolution: {integrity: sha512-h+YvPyNmeAUCCqaXvftiXklA2zGyRcIPv1B8fFS0YSIgNhwcxFGh1EyLOMsvJ2aWsBViNtIJ1HIFLzNu43/r4w==} + hasBin: true + + '@expo/image-utils@0.11.4': + resolution: {integrity: sha512-pn/4770DIEOcYZr484uazuwg20FX/qaDkeMRF6J+oxejynDmEmO8wLsCudaNShFE0BhyKGQTYrs2rsRhqrqESw==} + + '@expo/inline-modules@0.1.6': + resolution: {integrity: sha512-5f6EiOIKsFj9zlrCBet4ZIQRPEa9dBUdQgTzpjYwdZ8Z/M8W5lqMVL9dEs4HYKvEmDnqv0dQuDkFLyRSwu/DAQ==} + + '@expo/json-file@11.0.1': + resolution: {integrity: sha512-zxHWj4MKKMAL29ZQSY/Fssx4Thluk40JmuGNaeS078wy/NhlFhnVi+rHHunulE3xJAJ0CM73m8VK2+GkF9eRwQ==} + + '@expo/local-build-cache-provider@57.0.7': + resolution: {integrity: sha512-Hq5xWhXJWuyH3CWh3uqDWoHtxM0XYqLs9h2OzRWImBJahCPfePOo4kmw2uTEB6qHT5T47eqO4jtDG3MRGGZCpw==} + + '@expo/log-box@57.0.3': + resolution: {integrity: sha512-qv/cMliNax6es07Un/4IGJIcs4PUuhTKKTrJZX/0X2YRcOT5cqm/QicibZwTIbiZWi1i8P4YBRQTfFT2B17giw==} + peerDependencies: + '@expo/dom-webview': ^57.0.1 + expo: '*' + react: '*' + react-native: '*' + + '@expo/metro-config@57.0.9': + resolution: {integrity: sha512-+lalXZdoMaKTG1uKXsi2KWO0f7H3KiWrhZzAla4EmNSkcUMRx6K1rhZuBYoaJaKIp9BHZ333KbqRPron7slKQA==} + peerDependencies: + expo: '*' + peerDependenciesMeta: + expo: + optional: true + + '@expo/metro-file-map@57.0.1': + resolution: {integrity: sha512-8JXfVstZN7QnP4NianZZnlTVboOWR0sG8trUDNajOjnbGlPln29vponXM84tY+3tAHapz5/TxE53L0ixUwqPtA==} + + '@expo/metro-runtime@57.0.12': + resolution: {integrity: sha512-WpgjfRFh88B5tKJrbsypvyxx0MKuXMo+ru9Gbjq4Qwj7OboJmrQ+g555YCgiQD3uk3J1ThzgEU06yZOV43suGA==} + peerDependencies: + '@expo/log-box': ^57.0.3 + expo: '*' + react: '*' + react-dom: '*' + react-native: '*' + peerDependenciesMeta: + react-dom: + optional: true + + '@expo/metro@56.0.0': + resolution: {integrity: sha512-5gIgQHtEpjjvsjKfVtIv23a98LLRV0/y07PDShEwYSytAMlE3FSF8RHXqtHc1sUJL6dn7hnuIBpIbrLXXuVi0A==} + + '@expo/osascript@2.7.1': + resolution: {integrity: sha512-Zn03EX6In7ts2lPUW2ESUSkEhEWQN1qqsiXjadtZMJOuZRkMiAg1ZQHuvz9DjByDWNJ2pBwAGyrts9lj9k389g==} + engines: {node: '>=12'} + + '@expo/package-manager@1.13.1': + resolution: {integrity: sha512-y/K+CaYYpZpNGZhSX4HyLT/vyIunFjNfyoxNysPBCefeLKI/VCx6f9LNPzrxayr3rCYO5bl9O8H+HRQK265Nkg==} + + '@expo/plist@0.8.1': + resolution: {integrity: sha512-3gTReGIUm0oRaMClsAJYxBnVPCl6fVpsl8HS+DTVxDhW4GyVyxg9E/Znm3BvcHtUJ51RJJI14pC1wvrNilCRHw==} + + '@expo/prebuild-config@57.0.13': + resolution: {integrity: sha512-VhSySuXqOwK4fIc+9rgms7zN+c1Khj2xpuIgpVLPNyWRtpS8wB+eyV5CSohjSUBa3h+NsFdPk/VG6p0ZwlMDrg==} + + '@expo/require-utils@57.0.4': + resolution: {integrity: sha512-e7xbg/9BTQcsZE/oErafZXtI7kh5IgfasLJ97J5sFSzX2cA74pDvdlhW1KHVSaDkQyQv6h1LSLhsY7dEeOk7hw==} + peerDependencies: + typescript: ^5.0.0 || ^5.0.0-0 || ^6.0.0 || ^7.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@expo/router-server@57.0.7': + resolution: {integrity: sha512-QFHwt6V7UovmYA7+8BoMeKj3fh6WtkM9zksKjNAlZdcEI/hUhcW4uged6So5yc4tq+eA60kA7gIVZPKgBIZdrQ==} + peerDependencies: + '@expo/metro-runtime': ^57.0.12 + expo: '*' + expo-constants: ^57.0.13 + expo-font: ^57.0.1 + expo-router: '*' + expo-server: ^57.0.3 + react: '*' + react-dom: '*' + react-server-dom-webpack: ~19.0.1 || ~19.1.2 || ~19.2.1 + peerDependenciesMeta: + '@expo/metro-runtime': + optional: true + expo-router: + optional: true + react-dom: + optional: true + react-server-dom-webpack: + optional: true + + '@expo/schema-utils@57.0.2': + resolution: {integrity: sha512-fMu/jyN0l1Wzv7XkeWR4IYCx1M8ryui3FdBNGrWwbRgJ7EhxXxK8E2jxP2W3pbgUwUY0V3hG8+GyfCZwny+Lxw==} + + '@expo/sdk-runtime-versions@1.0.0': + resolution: {integrity: sha512-Doz2bfiPndXYFPMRwPyGa1k5QaKDVpY806UJj570epIiMzWaYyCtobasyfC++qfIXVb5Ocy7r3tP9d62hAQ7IQ==} + + '@expo/spawn-async@1.8.0': + resolution: {integrity: sha512-eb9xxd/LbuEGSdua4NumCu/McVB9EM+F/JxB9pWgnERw4HQ9XyTNH1KapG6oqLWR8TuRK2LQfzJlmNi94CVobw==} + engines: {node: '>=12'} + + '@expo/sudo-prompt@9.3.2': + resolution: {integrity: sha512-HHQigo3rQWKMDzYDLkubN5WQOYXJJE2eNqIQC2axC2iO3mHdwnIR7FgZVvHWtBwAdzBgAP0ECp8KqS8TiMKvgw==} + + '@expo/ui@57.0.12': + resolution: {integrity: sha512-ZkrECV6xZe0+Drq6C0hrBKvz1534/VyARNp9ziKihC6Db69W29+jBDZm8k9cyRyS1Hh23q6LP8J+2wDyUM8eAA==} + peerDependencies: + '@babel/core': '*' + expo: '*' + react: '*' + react-dom: '*' + react-native: '*' + react-native-worklets: '*' + peerDependenciesMeta: + '@babel/core': + optional: true + react-dom: + optional: true + react-native-worklets: + optional: true + + '@expo/vector-icons@15.1.1': + resolution: {integrity: sha512-Iu2VkcoI5vygbtYngm7jb4ifxElNVXQYdDrYkT7UCEIiKLeWnQY0wf2ZhHZ+Wro6Sc5TaumpKUOqDRpLi5rkvw==} + peerDependencies: + expo-font: '>=14.0.4' + react: '*' + react-native: '*' + + '@expo/ws-tunnel@2.0.0': + resolution: {integrity: sha512-j+JfTRdCk820J9dU0sA2SqshQIKFOMo7ED84w9MJFcebfbNQgsLztEY/SABDkGnjatrW4xGqnUhVRxSBVyCkXw==} + peerDependencies: + ws: ^8.0.0 + + '@expo/xcpretty@4.4.4': + resolution: {integrity: sha512-4aQzz9vgxcNXFfo/iyNgDDYfsU5XGKKxWxZopw0cVotHiW+U8IJbIxMaxsINs6bHhtkG3StKNPcOrn3eBuxKPw==} + hasBin: true + + '@gorhom/bottom-sheet@5.2.14': + resolution: {integrity: sha512-uLQFlDjp9z+jrOFcMSEldPqL5JdaXL3vXOh+juhwoNvXgTsEorJLjHTugXu+YccAG/0KJnShzKCrb71MHBsvJg==} + peerDependencies: + '@types/react': '*' + '@types/react-native': '*' + react: '*' + react-native: '*' + react-native-gesture-handler: '>=2.16.1' + react-native-reanimated: '>=3.16.0 || >=4.0.0-' + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-native': + optional: true + + '@gorhom/portal@1.0.14': + resolution: {integrity: sha512-MXyL4xvCjmgaORr/rtryDNFy3kU4qUbKlwtQqqsygd0xX3mhKjOLn6mQK8wfu0RkoE0pBE0nAasRoHua+/QZ7A==} + peerDependencies: + react: '*' + react-native: '*' + + '@humanfs/core@0.19.2': + resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.8': + resolution: {integrity: sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==} + engines: {node: '>=18.18.0'} + + '@humanfs/types@0.15.0': + resolution: {integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + + '@isaacs/ttlcache@1.4.1': + resolution: {integrity: sha512-RQgQ4uQ+pLbqXfOmieB91ejmLwvSgv9nLx6sT6sD83s7umBypgg+OIBOBbEUiJXrfpnp9j0mRhYYdzp9uqq3lA==} + engines: {node: '>=12'} + + '@istanbuljs/load-nyc-config@1.1.0': + resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} + engines: {node: '>=8'} + + '@istanbuljs/schema@0.1.6': + resolution: {integrity: sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==} + engines: {node: '>=8'} + + '@jest/console@29.7.0': + resolution: {integrity: sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/core@29.7.0': + resolution: {integrity: sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + '@jest/create-cache-key-function@29.7.0': + resolution: {integrity: sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/diff-sequences@30.4.0': + resolution: {integrity: sha512-zOpzlfUs45l6u7jm39qr87JCHUDsaeCtvL+kQe/Vn9jSnRB4/5IPXISm0h9I1vZW/o00Kn4UTJ2MOlhnUGwv3g==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/environment@29.7.0': + resolution: {integrity: sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/expect-utils@29.7.0': + resolution: {integrity: sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/expect@29.7.0': + resolution: {integrity: sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/fake-timers@29.7.0': + resolution: {integrity: sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/get-type@30.1.0': + resolution: {integrity: sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/globals@29.7.0': + resolution: {integrity: sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/reporters@29.7.0': + resolution: {integrity: sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + '@jest/schemas@29.6.3': + resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/schemas@30.4.1': + resolution: {integrity: sha512-i6b4qw5qnP8c5FEeBJg/uZQ4ddrkN6Ca8qISJh0pr7a5hfn3h3v5x60BEbOC7OYAGZNMs1LfFLwnW2CuK8F57Q==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/source-map@29.6.3': + resolution: {integrity: sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/test-result@29.7.0': + resolution: {integrity: sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/test-sequencer@29.7.0': + resolution: {integrity: sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/transform@29.7.0': + resolution: {integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/types@29.6.3': + resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/source-map@0.3.11': + resolution: {integrity: sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@napi-rs/wasm-runtime@1.2.3': + resolution: {integrity: sha512-UMduMbqO5s5zF2NkNacMT/yK5Y5QiKvWr2+50bzIIxFDwVJ2h49b+oyjaCGPhJxd2/gC2x39EHv/gHVuu36x2Q==} + engines: {node: ^20.19.0 || ^22.13.0 || >=23.5.0} + peerDependencies: + '@emnapi/core': ^1.7.1 || ^2.0.0-alpha.4 + '@emnapi/runtime': ^1.7.1 || ^2.0.0-alpha.4 + + '@nolyfill/is-core-module@1.0.39': + resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==} + engines: {node: '>=12.4.0'} + + '@radix-ui/primitive@1.1.7': + resolution: {integrity: sha512-rqWnm76nYT8HoNNqEjpgJ7Pw/DrBj5iBTrmEPo6HTX5+VJyBNOqTdv4g89G63HuR5g0AaENoAcH7Is5fF2kZ8Q==} + + '@radix-ui/react-collection@1.1.15': + resolution: {integrity: sha512-9W+B9NPF0NaaPh/1NJd3+KqsnlLqU9H7T2rvww+fp+T/evVXdNAyYcnfRQZFOjkR1ajQp3yORlqnI8soawLvNA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-compose-refs@1.1.5': + resolution: {integrity: sha512-+48PbAAbq3didjJxa+OaWY2ZwgAKsNiRGyeHKszblZMQ+kcpd9pAaT11cMkGEie0vsOi3QdeTE6d5Fe3Gn61kA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-context@1.2.2': + resolution: {integrity: sha512-RHCUGwKHDr0hDGg4X7ma4JG4/+12qxw8rkh5QKdDldlCvtja6nUx1Ef/8HVrJze81lEsgLQlqjzjGNHantgnQA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-dialog@1.1.23': + resolution: {integrity: sha512-Ksw4WeROkO4rC9k/onilX/Ao2Cr1ku1unMNH+XSCcP4jSXYu7HDsg9n4ojMjVb22XpYjAQ9qfrFlVbru1vXDUA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-direction@1.1.4': + resolution: {integrity: sha512-5pzg4FGQNpExhnhT2zlrP1wZFaYCd1K0nYWoFAdcYoYK868IEigqMX3B3f8yIoRlAhAeDWciLI6ZdCKHF9P4Vg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-dismissable-layer@1.1.19': + resolution: {integrity: sha512-8g4pfOL9HoKKLWGiypT+dphVqjFfmcXO5GBnhsG6zI+lxAx/8feQpr+1LSN8Re3hiZ+XkLNS4O9ztK11/LzQ6w==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-focus-guards@1.1.6': + resolution: {integrity: sha512-RNOJjfZMTyBM6xYmV3IVGXkPjIhcBAuv48POevAXwrGJhkWZ9p1rFoIS1JFooPuT193AZmRsCPhpoVJxx6OPoQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-focus-scope@1.1.16': + resolution: {integrity: sha512-wmRZ2WWLvmt6KHy2rNPOdPUjwq5xOHY02+m+udwJTn0aNIox/rkskAvJTyTLGhPK6KgrUjlJUJpgmx/+wFiFIQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-id@1.1.4': + resolution: {integrity: sha512-TMQp2llA+RYn7JcjnrMnz7wN4pcVttPZnRZo52PLQsoLVKzNlVwUeHmfePgTgRluXFvlD3GD5g5MOVVTJCO0qA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-portal@1.1.17': + resolution: {integrity: sha512-vKQLcWypUnwZVvfV7UkGahH2g6ySe8M8R+zYBwPrv5byZ9QAW6cQVvNKo7GgmD+p8aYb6D9JBuvy8/WhOno2wQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-presence@1.1.10': + resolution: {integrity: sha512-3wyzCQ6+ubRA+D4uv9m95JYLXxmOHp05qjrkjeA7uKHHtjpPggQzc6DAb0URl7j67oR0K2foO4ip27TiX037Bw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-primitive@2.1.10': + resolution: {integrity: sha512-MucOnzh6hR5mid6VpkbglRAMYMjKLqRnGBbjXkzjK52fuQDd1qbkx78a5P40mkcnVXJdEVxm26E9OPAiUq7nBg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-roving-focus@1.1.19': + resolution: {integrity: sha512-V9jI6hDjT7l3jsCQD9bLNvDLM3tH/gdbOTp7Tefp3hbbgCGQoK7tUvrWiRlcoBHIZ809ElXwNQwVo0B98LuTXQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-slot@1.3.3': + resolution: {integrity: sha512-qx7oqnYbxnK9kYI9m317qmFmEgo6ywqWvbTogdj7cL9p3/yx4M48p7Rnw5z3H890cL/ow/EeWJsuTykeZVXP5Q==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-tabs@1.1.21': + resolution: {integrity: sha512-UKxJlZid7FVtsk/WTxj4i4uSEgj2Au+KBbS7SQyTlzMhhn+86Cz3tISZdTa87bfEfcuvZezf2ZsxD4xuEKtkog==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-use-callback-ref@1.1.4': + resolution: {integrity: sha512-R6OUY2e2fA6Yn6s+VSx5KBV6Nx8LQEhu+cz7LCej18rQ1HLyg9PSC9jP/ZNx0o6FAIK9c0F1kHylzSxKsdlkrQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-controllable-state@1.2.6': + resolution: {integrity: sha512-uEQJGT97ZA/TgP/Hydw47lHu+/vQj6z/0jA+WeTbK1o9Rx45GImjpD0tc3W5ad3D6XTSR6e1yEO0FvGq6WQfVQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-effect-event@0.0.5': + resolution: {integrity: sha512-7cshFL8HGS/7HEiHH+9kL9HBwp2sa9yX18Knwek6KYWmXwM7pegMgta2AXMQKI+rq3JnfSj9x8wYqFMTdG1Jgg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-is-hydrated@0.1.3': + resolution: {integrity: sha512-umO/aJ+82CpOnhDZUTbILCQf7kU/g0iv+oGs/Q8jw7IkhWBzaEP4sA268PhFAJTFetbwp3ICc6ktpI4TqtxcIw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-layout-effect@1.1.4': + resolution: {integrity: sha512-K20DkRkUwDnxEYMBPcg3Y6voLkEy5p5QQmszZgLngKKiC7dzBR/aEuK3w1qlx2JWDUNH6FluahYdgR3BP+QbYw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@react-native-async-storage/async-storage@2.2.0': + resolution: {integrity: sha512-gvRvjR5JAaUZF8tv2Kcq/Gbt3JHwbKFYfmb445rhOj6NUMx3qPLixmDx5pZAyb9at1bYvJ4/eTUipU5aki45xw==} + peerDependencies: + react-native: ^0.0.0-0 || >=0.65 <1.0 + + '@react-native-masked-view/masked-view@0.3.2': + resolution: {integrity: sha512-XwuQoW7/GEgWRMovOQtX3A4PrXhyaZm0lVUiY8qJDvdngjLms9Cpdck6SmGAUNqQwcj2EadHC1HwL0bEyoa/SQ==} + peerDependencies: + react: '>=16' + react-native: '>=0.57' + + '@react-native/assets-registry@0.86.2': + resolution: {integrity: sha512-vcX/mBjWAVnWofu7KecotquI2unZ/tITwA7OGdq/mdY/zmGXIEvYhfEYyOQij/LRqi9WAL+iizInTBWnxDhK/Q==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + '@react-native/babel-plugin-codegen@0.86.2': + resolution: {integrity: sha512-NNDZqOlNbH5SzgPks1jFDYH3234Rpa5e/nhZymxhIiBH3NcE3uD+rGj/HWXhH7nHF2ToGK6XbUpqy7nmJPeh+g==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + '@react-native/babel-preset@0.86.2': + resolution: {integrity: sha512-4XKEJ6jKW9lXMB1O5o47gBoGgolde1fbX13gLW/erlcn+1ky+MHHo5UjuM3RWGdPJHIvIzekDDumSUHhB9x5iQ==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + peerDependencies: + '@babel/core': '*' + + '@react-native/codegen@0.86.2': + resolution: {integrity: sha512-xKkudsahUJ1n//55g4fXk5BStVqqmZlz8HQveL45ZxcfDnwvhuYe2GymksQANFsSN+slvrarjrfq8kIxJzbceA==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + peerDependencies: + '@babel/core': '*' + + '@react-native/community-cli-plugin@0.86.2': + resolution: {integrity: sha512-YHXNKoM6Y/HjREySZ5arET2xgiHgg67r1MdwJB//MPJAJ0Xc5g0u6UHxY9VzsHO3Y07dre6s0BinYwjt1SEWvQ==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + peerDependencies: + '@react-native-community/cli': '*' + '@react-native/metro-config': 0.86.2 + peerDependenciesMeta: + '@react-native-community/cli': + optional: true + '@react-native/metro-config': + optional: true + + '@react-native/debugger-frontend@0.86.2': + resolution: {integrity: sha512-KGS1aV5F6cIqpnoIUhLBXyVzy1oAj8jBFGau6vX4Vy0HXRJN7p+68RU7x6NuyraHvQcR14ccMGT5TkFuNjQ4gA==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + '@react-native/debugger-shell@0.86.2': + resolution: {integrity: sha512-/TaVJ2+gGajZPJGrFaObUQmHmlaxAlfmOPZicl6pNKDUjzSgFMpcLkdTOExvb+USYTVdGX1XwxXyvjQdUO2bvg==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + '@react-native/dev-middleware@0.86.2': + resolution: {integrity: sha512-B7L0vKvg+IcEElT7Vpqh1xj5yJAqWUegjbP+bQRaorJMAYnv11GkliTnZV2AdTDfZQJWgOEx8i8LGkHkUg7bnA==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + '@react-native/gradle-plugin@0.86.2': + resolution: {integrity: sha512-2F6x14NcHMpVmfTTFKfMkpV5dZedZrLiv6PE+c3vgnesV2bjleUBydr4U+NI8VkI7OwW71L0A5qQ76I9LCrfoQ==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + '@react-native/jest-preset@0.86.2': + resolution: {integrity: sha512-wneoqwKWdv6wIcWotVgp6NMlMWcJDjxDnp7fVYym2Pn6JLgAKgkbmuHGmxW0cLCGoa9wtcO680uciv+Kzx0G7w==} + engines: {node: '>= 20.19.4'} + peerDependencies: + react: ^19.2.3 + + '@react-native/js-polyfills@0.86.2': + resolution: {integrity: sha512-bIwNcGBaQ74shB5z1mRkxOpjikimuwsnOCEkZSzL67Z1FTyK1ObpENfyd2QvcvVW9Cjl+tHuw9ynpBnb2jPoJQ==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + '@react-native/metro-babel-transformer@0.86.2': + resolution: {integrity: sha512-mX1wgLErdb2hDgXJr9zM9SWLe+ZteZTFTwRWGOQ53yEJwc9DVSnxdTlAtVdMeOj2ntycKh0R8jYdat2Am43cwQ==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + peerDependencies: + '@babel/core': '*' + + '@react-native/metro-config@0.86.2': + resolution: {integrity: sha512-hJno256j+MS0b3JD1aD3ouTGZVacKNVBuXL2atMQQ8BZ060vl1ptnZ83y569aDW+/rgFSOcqn6ydKeSz4uUKQQ==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + '@react-native/normalize-colors@0.74.89': + resolution: {integrity: sha512-qoMMXddVKVhZ8PA1AbUCk83trpd6N+1nF2A6k1i6LsQObyS92fELuk8kU/lQs6M7BsMHwqyLCpQJ1uFgNvIQXg==} + + '@react-native/normalize-colors@0.86.2': + resolution: {integrity: sha512-EzPFc9Y6lzYOWeso2almwXI7f8+qReHxWvT+algsOczb2UhWXIWXDoSvkdwoSfiwwmGt/ijJgKJoeHlzPkLwRg==} + + '@react-native/virtualized-lists@0.86.2': + resolution: {integrity: sha512-uO0J72gh3EvE+1/GHRk18QRyBDTRHRB0AraAfojsRjbT7VMuJwKrZYaKGshavoaEud6aw00ZB9/8mTMIKjjcAw==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + peerDependencies: + '@types/react': ^19.2.0 + react: '*' + react-native: 0.86.2 + peerDependenciesMeta: + '@types/react': + optional: true + + '@redocly/ajv@8.11.2': + resolution: {integrity: sha512-io1JpnwtIcvojV7QKDUSIuMN/ikdOUd1ReEnUnMKGfDVridQZ31J0MmIuqwuRjWDZfmvr+Q0MqCcfHM2gTivOg==} + + '@redocly/config@0.22.0': + resolution: {integrity: sha512-gAy93Ddo01Z3bHuVdPWfCwzgfaYgMdaZPcfL7JZ7hWJoK9V0lXDbigTWkhiPFAaLWzbOJ+kbUQG1+XwIm0KRGQ==} + + '@redocly/openapi-core@1.34.19': + resolution: {integrity: sha512-o/0VgsBXgwcY1lyeqcVtSGdTQAPnVggo0fbFVPlxl5XVDKUcVH0OLRqt3CbkwByT5FU305E0iE0O7MzThjDblw==} + engines: {node: '>=18.17.0', npm: '>=9.5.0'} + + '@rtsao/scc@1.1.0': + resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} + + '@shopify/flash-list@2.0.2': + resolution: {integrity: sha512-zhlrhA9eiuEzja4wxVvotgXHtqd3qsYbXkQ3rsBfOgbFA9BVeErpDE/yEwtlIviRGEqpuFj/oU5owD6ByaNX+w==} + peerDependencies: + '@babel/runtime': '*' + react: '*' + react-native: '*' + + '@sinclair/typebox@0.27.12': + resolution: {integrity: sha512-hhyNJ+nbR6ZR7pToHvllEFun9TL0sbL+tk/ON75lo+Xas054uez98qRbsuNt7MBCyZKK4+8Yli/OAGZhmfBZ/g==} + + '@sinclair/typebox@0.34.52': + resolution: {integrity: sha512-XiMQh7qqVlxZzcVD+kkGMNGMzcTrDMLWI7S4x7z1MkCkbDPrekpZXEUK0eZqZFMuHQg2a2DZOcDIh9o5v3Gonw==} + + '@sinonjs/commons@3.0.1': + resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} + + '@sinonjs/fake-timers@10.3.0': + resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} + + '@tanstack/query-core@5.101.4': + resolution: {integrity: sha512-gNwcvOJcRbLWPOLG/2OBm+zM+Yv+MKsXKEOWC57USuZDEsI71hEErQsiEGx5wX9rzWWkfwM0fVSPoiIFSsxfiw==} + + '@tanstack/react-query@5.101.4': + resolution: {integrity: sha512-yRg2pfOCxIs4ZJW3XYYHU/WgtD04FHSnfHlpRT7h7pR77hwkdRG4wxbKe4aq6P0RvXUTBSQpQeadS1SUYUe+KA==} + peerDependencies: + react: ^18 || ^19 + + '@testing-library/react-native@14.0.1': + resolution: {integrity: sha512-2r2e2y8SkUNRWKRXlUGqDYunB+AkbdXUPn49Bs5rpv9oxRb0K3USBVugbPbAKJjfj4w5Ba91NJ8LcQCZyopUZA==} + engines: {node: ^22.13.0 || >=24} + peerDependencies: + jest: '>=29.0.0' + react: '>=19.0.0' + react-native: '>=0.78' + test-renderer: ^1.0.0 + peerDependenciesMeta: + jest: + optional: true + + '@tootallnate/once@2.0.1': + resolution: {integrity: sha512-HqmEUIGRJ5fSXchkVgR5F7qn48bDBzv0kWj/Kfu5e6uci4UlEeng4331LnBkWffb++Ei3FOVLxo8JJWMFBDMeQ==} + engines: {node: '>= 10'} + + '@tybys/wasm-util@0.10.3': + resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==} + + '@types/babel__core@7.20.5': + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + + '@types/babel__generator@7.27.0': + resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} + + '@types/babel__template@7.4.4': + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + + '@types/babel__traverse@7.28.0': + resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} + + '@types/emscripten@1.41.5': + resolution: {integrity: sha512-cMQm7pxu6BxtHyqJ7mQZ2kXWV5SLmugybFdHCBbJ5eHzOo6VhBckEgAT3//rP5FwPHNPeEiq4SmQ5ucBwsOo4Q==} + + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + + '@types/graceful-fs@4.1.9': + resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} + + '@types/hammerjs@2.0.46': + resolution: {integrity: sha512-ynRvcq6wvqexJ9brDMS4BnBLzmr0e14d6ZJTEShTBWKymQiHwlAyGu0ZPEFI2Fh1U53F7tN9ufClWM5KvqkKOw==} + + '@types/istanbul-lib-coverage@2.0.6': + resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} + + '@types/istanbul-lib-report@3.0.3': + resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==} + + '@types/istanbul-reports@3.0.4': + resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} + + '@types/jest@29.5.14': + resolution: {integrity: sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==} + + '@types/jsdom@20.0.1': + resolution: {integrity: sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/json5@0.0.29': + resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + + '@types/node@26.2.0': + resolution: {integrity: sha512-5IviulTZeRNp2vAJ514cc/HUlY5nZ9fCbq9DMyC52BrhFZACo3nI0R7qBxhQmo/d27NFe96ur/b7Wwxklda+kg==} + + '@types/react-reconciler@0.33.0': + resolution: {integrity: sha512-HZOXsKT0tGI9LlUw2LuedXsVeB88wFa536vVL0M6vE8zN63nI+sSr1ByxmPToP5K5bukaVscyeCJcF9guVNJ1g==} + peerDependencies: + '@types/react': '*' + + '@types/react-test-renderer@19.1.0': + resolution: {integrity: sha512-XD0WZrHqjNrxA/MaR9O22w/RNidWR9YZmBdRGI7wcnWGrv/3dA8wKCJ8m63Sn+tLJhcjmuhOi629N66W6kgWzQ==} + + '@types/react@19.2.18': + resolution: {integrity: sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==} + + '@types/stack-utils@2.0.3': + resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} + + '@types/tough-cookie@4.0.5': + resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==} + + '@types/yargs-parser@21.0.3': + resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} + + '@types/yargs@17.0.35': + resolution: {integrity: sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==} + + '@typescript-eslint/eslint-plugin@8.67.0': + resolution: {integrity: sha512-Un7Heoyj65NREbKAyIrFxeM143NZpExWmy1Nep4DLeQOeLlTeumPjoNKnBrU5D5moWXbPJgRa5Uwcdu0faVNGQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.67.0 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/parser@8.67.0': + resolution: {integrity: sha512-fUBfTuuEulWqX6V8+O3PtScV01tzYYRUDTAirHFKoRAt7nOzoGiPt0M/bB47wWNy0coOOcgEwAMUtBpykMxl6w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/project-service@8.67.0': + resolution: {integrity: sha512-cvE8c7ulYeXN9fYuszhCeCsbzyVEXuhrRCybnBre7TUmqb5nRmBfQAwCj0O3WJFDeyAZt4VYv51vMCC9LHSdYw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/scope-manager@8.67.0': + resolution: {integrity: sha512-EgvsleTwS4E+WzzSvem8fAUubLwatMNF1B5hHSLQxcvs7q2dtRhGyujHwLJSYlG41niJ7GP24Aha2+0mb1b2kg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.67.0': + resolution: {integrity: sha512-vV+LUSv5njUWsknE71fqKTlXUva+R76SaeORd6Zojcunk/6DvKFXONU3BrAs2H49mbygUXt6gbYunzwqNwlhdg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/type-utils@8.67.0': + resolution: {integrity: sha512-aVWDXbRmdXO9siTfX4ditQI1T9+zVcNazT48EJCD0v40/9RIFoUgZ05CmGEq9H2gixRpjUn/iplwvlcvutJW/Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/types@8.67.0': + resolution: {integrity: sha512-sBtgslww8nsMYUjhdPBiSyUqSzT8uR6g93A2QXnQC8+cGdjz0CyaOdqHDRJb1AtORbZCNUJBBeFA/tNR2uQmww==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.67.0': + resolution: {integrity: sha512-EKQBCE9yNlRJYm7jdTW5AhDacDUmSwQb0FAJAmK2EKYrNXIsa2vxcSZx6PvJ/dEdI6lS+Y9W+EXckLj0iPFGcw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/utils@8.67.0': + resolution: {integrity: sha512-U9D1FdwEWBwok3hxxSdhclMb0twvt9QnjIQ0VfQ1AiX2epnpSgv2ubVDsayOFyY8K6FX+AQ7E0FKWVG3iKsj1A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/visitor-keys@8.67.0': + resolution: {integrity: sha512-fkv8dHRDqfGtTHuJeebdrQ7cX6Ad4WAS00rgHh9UGvMycF1mjBfsxry1XsLIFhWZ6Judlh6UdzK+TYlbpCXgnA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@ungap/structured-clone@1.3.3': + resolution: {integrity: sha512-60YRaenCQcVjYEKOcG824+DRGGIQ3VKErcBoAEDJZz5bKIs2ZG+X/H9Nk+Q6EVkwJk5QNApxbrc5QtBSwtrXAg==} + + '@unrs/resolver-binding-android-arm-eabi@1.12.2': + resolution: {integrity: sha512-g5T90pqg1bo/7mytQx6F4iBNC0Wsh9cu+z9veDbFjc7HjpesJFWD7QMS0NGStXM075+7dJPPVvBbpZlnrdpi/w==} + cpu: [arm] + os: [android] + + '@unrs/resolver-binding-android-arm64@1.12.2': + resolution: {integrity: sha512-YGCRZv/9GLhwmz6mYDeTsm/92BAyR28l6c2ReweVW5pWgfsitWLY8upvfRlGdoyD8HjeTHSYJWyZGD4KJA/nFQ==} + cpu: [arm64] + os: [android] + + '@unrs/resolver-binding-darwin-arm64@1.12.2': + resolution: {integrity: sha512-u9DiNT1auQMO20A9SyTuG3wUgQWB9Z7KjAg0uFuCDR1FsAY8A0CG2S6JpHS1xwm/w1G08bjXZDcyOCjv1WAm2w==} + cpu: [arm64] + os: [darwin] + + '@unrs/resolver-binding-darwin-x64@1.12.2': + resolution: {integrity: sha512-f7rPLi/T1HVKZu/u6t87lroib16n8vrSzcyxI7lg4BGO9UF26KhQL44sd9eOUgrTYhvRXtWOIZT5PejdPyJfUA==} + cpu: [x64] + os: [darwin] + + '@unrs/resolver-binding-freebsd-x64@1.12.2': + resolution: {integrity: sha512-BpcOjWCJub6nRZUS2zA20pmLvjtqAtGejETaIyRLiZiQf++cbrjltLA5NN/xaXfqeOBOSlMFbemIl5/S5tljmg==} + cpu: [x64] + os: [freebsd] + + '@unrs/resolver-binding-linux-arm-gnueabihf@1.12.2': + resolution: {integrity: sha512-vZTDvdSISZjJx66OzJqtsOhzifbqRjbmI1Mnu49fQDwog5GtDI4QidRiEAYbZCRj9C8YZEW+3ZjqsyS9GR4k2A==} + cpu: [arm] + os: [linux] + + '@unrs/resolver-binding-linux-arm-musleabihf@1.12.2': + resolution: {integrity: sha512-BiPI+IrIlwcW4nLLMM21+B1dFPzd55yAVgVGrdgDjNef+ch03GdxrcyaIz8X9SsQirh/kCQ7mviyWlMxdh2D7g==} + cpu: [arm] + os: [linux] + + '@unrs/resolver-binding-linux-arm64-gnu@1.12.2': + resolution: {integrity: sha512-zJc0H99FEPoFfSrNpa91HYfxzfAJCr502oxNK1cfdC9hlaFI43RT+JFCann9JUgZmLzzntChHyn13Sgn9ljHNg==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@unrs/resolver-binding-linux-arm64-musl@1.12.2': + resolution: {integrity: sha512-KQ3Lki6l+Pz1k/eBipN41ES+YUK30beLGb9YqcB1O542cyLCNE6GaxrfcY3T6EezmGGk84wb5XyO9loTM9tkcA==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@unrs/resolver-binding-linux-loong64-gnu@1.12.2': + resolution: {integrity: sha512-3SJGEh1DborhG6pyxvhPzCT4bbSIVihsvgJc13P1bHG7KLdNDaF9T3gsTwFc7Jw/5Y5/iWOjkEx7Zy0NvCGX3Q==} + cpu: [loong64] + os: [linux] + libc: [glibc] + + '@unrs/resolver-binding-linux-loong64-musl@1.12.2': + resolution: {integrity: sha512-jiuG/Obbel7uw1PwHNFfrkiKhLAF6mnyZ6aWlOAVN9WqKm8v0OFGnciJIHu8+CMvXLQ8AD51LPzAoUfT21D5Ew==} + cpu: [loong64] + os: [linux] + libc: [musl] + + '@unrs/resolver-binding-linux-ppc64-gnu@1.12.2': + resolution: {integrity: sha512-q7xRvVpmcfeL+LlZg8Pbbo6QaTZwDU5BaGZbwfhkEsXJn3Was8xYfE0RBH266xZt0rM6B7i8xAYIvjthuUIWHg==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@unrs/resolver-binding-linux-riscv64-gnu@1.12.2': + resolution: {integrity: sha512-0CVdx6lcnT3Q9inOH8tsMIOJ6ImndllMjqJHg8RLVdB7Vq4SfkEXl9mCSsVNuNA4MCYycRicCUxPCabVHJRr6A==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@unrs/resolver-binding-linux-riscv64-musl@1.12.2': + resolution: {integrity: sha512-iOwlRo9vnp6R6ohHQS11n0NnfdXx/omhkocmIfaPRpQhKZ+3BDMkkdRVh53qjkFkpPddf+FETA28NwGN7l5l+w==} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@unrs/resolver-binding-linux-s390x-gnu@1.12.2': + resolution: {integrity: sha512-HYJtLfXq94q8iZNFT1lknx258wlkkWhZeUXJRqzKBBUJ00CvZ+N33zgbCqimLjsyw5Va6uUxhVa12mI+kaveEw==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@unrs/resolver-binding-linux-x64-gnu@1.12.2': + resolution: {integrity: sha512-mPsUhunKKDih5O96Y6enDQyHc1SqBPlY1E/SfMWDM3EdJ95Z9CArPeCVwCCqbP45ljvivdEk8Fxn+SIb1rDAJQ==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@unrs/resolver-binding-linux-x64-musl@1.12.2': + resolution: {integrity: sha512-azrt6+5ydLd8Vt210AAFis/lZevSfPw93EJRIJG+xPu4WCJ8K0kppCTpMyLPcKT7H15M4Jnt2tMp5bOvCkRC6A==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@unrs/resolver-binding-openharmony-arm64@1.12.2': + resolution: {integrity: sha512-YZ9hP4O0X9PQb8eO980qmLNGH4zT3I9+SZTdt0Pr0YyuGQhYKoOZkV02VzrzyOZJ5xIJ3UFIenKkUkGg8GjgWQ==} + cpu: [arm64] + os: [openharmony] + + '@unrs/resolver-binding-wasm32-wasi@1.12.2': + resolution: {integrity: sha512-tYFDIkMxSflfEc/h92ZWNsZlHSwgimbNHSO3PL2JWQHfCuC2q316jMyYU9TIWZsFK2bQwyK5VAdYgn8ygPj69A==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@unrs/resolver-binding-win32-arm64-msvc@1.12.2': + resolution: {integrity: sha512-qzNyg3xL0VPQmCaUh+N5jSitce6k+uCBfMDesWRnlULOZaqUkaJ0ybdT+UqlAWJoQjuqfIU/0Ptx9bteN4D82g==} + cpu: [arm64] + os: [win32] + + '@unrs/resolver-binding-win32-ia32-msvc@1.12.2': + resolution: {integrity: sha512-WD9sY00OfpHVGfsnHZoA8jVT+esS/Bg8z8jzxp5BnDCjjwsuKsPQrzswwpFy4J1AUJbXPRfkpcX0mXrzeXW79g==} + cpu: [ia32] + os: [win32] + + '@unrs/resolver-binding-win32-x64-msvc@1.12.2': + resolution: {integrity: sha512-nAB74NfSNKknqQ1RrYj6uz8FcXEomu/MATJZxh/x+BArzN2U3JbOYC0APYzUIGhVY3m5hRxA8VPNdPBoG8txlA==} + cpu: [x64] + os: [win32] + + '@xmldom/xmldom@0.8.14': + resolution: {integrity: sha512-T4EDRUBVZYRldYApjEJiU0e1stYWaRAX7CuSnKzrpwdZKo53zGV8/pqfzV6FfwNl9YThD2OumQYvqtvjvgG7aQ==} + engines: {node: '>=10.0.0'} + + '@xmldom/xmldom@0.9.11': + resolution: {integrity: sha512-tW8bcK3hsG0/uqSnNz6TK4BkcuZSezoU7DlnYssILmZDktPnSHHuDJJFM0AJv+13gz2r0iGdrj6qqKeUnxXEDg==} + engines: {node: '>=14.6'} + + abab@2.0.6: + resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==} + deprecated: Use your platform's native atob() and btoa() methods instead + + abort-controller@3.0.0: + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} + + accepts@1.3.8: + resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} + engines: {node: '>= 0.6'} + + accepts@2.0.0: + resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} + engines: {node: '>= 0.6'} + + acorn-globals@7.0.1: + resolution: {integrity: sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==} + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn-walk@8.3.5: + resolution: {integrity: sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==} + engines: {node: '>=0.4.0'} + + acorn@8.18.0: + resolution: {integrity: sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==} + engines: {node: '>=0.4.0'} + hasBin: true + + agent-base@6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} + + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + engines: {node: '>= 14'} + + agent-cli-detector@0.1.6: + resolution: {integrity: sha512-vKrPeEVN3upDF3GjWxsWBbwQgMtNJ8VB1cduPvK3svmmz4ENpS7yaPxbogsbe3w+xp9xp2Cu+0Ar41rjAR4+lA==} + engines: {node: '>=18.18'} + hasBin: true + + ajv@6.15.0: + resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} + + anser@1.4.10: + resolution: {integrity: sha512-hCv9AqTQ8ycjpSd3upOJd7vFwW1JaoYQ7tpham03GJ1ca8/65rqn0RpaWpItOAd6ylW9wAw6luXYPJIyPFVOww==} + + ansi-colors@4.1.3: + resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} + engines: {node: '>=6'} + + ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + + ansi-escapes@6.2.1: + resolution: {integrity: sha512-4nJ3yixlEthEJ9Rk4vPcdBRkZvQZlYyu8j4/Mqz5sgIkddmEnH2Yj2ZrnP9S3tQOvSNRUIgVNF/1yPpRAGNRig==} + engines: {node: '>=14.16'} + + ansi-regex@4.1.1: + resolution: {integrity: sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==} + engines: {node: '>=6'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.3.0: + resolution: {integrity: sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==} + engines: {node: '>=12'} + + ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + arg@4.1.3: + resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + + arg@5.0.2: + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + + argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + aria-hidden@1.2.6: + resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==} + engines: {node: '>=10'} + + array-buffer-byte-length@1.0.2: + resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} + engines: {node: '>= 0.4'} + + array-includes@3.1.9: + resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==} + engines: {node: '>= 0.4'} + + array.prototype.findlast@1.2.5: + resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} + engines: {node: '>= 0.4'} + + array.prototype.findlastindex@1.2.6: + resolution: {integrity: sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==} + engines: {node: '>= 0.4'} + + array.prototype.flat@1.3.3: + resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==} + engines: {node: '>= 0.4'} + + array.prototype.flatmap@1.3.3: + resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==} + engines: {node: '>= 0.4'} + + array.prototype.tosorted@1.1.4: + resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==} + engines: {node: '>= 0.4'} + + arraybuffer.prototype.slice@1.0.4: + resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} + engines: {node: '>= 0.4'} + + asap@2.0.6: + resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} + + async-function@1.0.0: + resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} + engines: {node: '>= 0.4'} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + + babel-jest@29.7.0: + resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@babel/core': ^7.8.0 + + babel-plugin-istanbul@6.1.1: + resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} + engines: {node: '>=8'} + + babel-plugin-jest-hoist@29.6.3: + resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + babel-plugin-polyfill-corejs2@0.4.17: + resolution: {integrity: sha512-aTyf30K/rqAsNwN76zYrdtx8obu0E4KoUME29B1xj+B3WxgvWkp943vYQ+z8Mv3lw9xHXMHpvSPOBxzAkIa94w==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-polyfill-corejs3@0.13.0: + resolution: {integrity: sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-polyfill-regenerator@0.6.8: + resolution: {integrity: sha512-M762rNHfSF1EV3SLtnCJXFoQbbIIz0OyRwnCmV0KPC7qosSfCO0QLTSuJX3ayAebubhE6oYBAYPrBA5ljowaZg==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-react-compiler@1.0.0: + resolution: {integrity: sha512-Ixm8tFfoKKIPYdCCKYTsqv+Fd4IJ0DQqMyEimo+pxUOMUR9cVPlwTrFt9Avu+3cb6Zp3mAzl+t1MrG2fxxKsxw==} + + babel-plugin-react-native-web@0.21.2: + resolution: {integrity: sha512-SPD0J6qjJn8231i0HZhlAGH6NORe+QvRSQM2mwQEzJ2Fb3E4ruWTiiicPlHjmeWShDXLcvoorOCXjeR7k/lyWA==} + + babel-plugin-syntax-hermes-parser@0.36.0: + resolution: {integrity: sha512-LhD0xdoedDw7ansQgXbB2DADLZIK/LRXuWNBPuVzMc5S2WK5GyT89tCM+cQzxFGO0mGyLK6D5TrVOJJzAoDy8Q==} + + babel-plugin-syntax-hermes-parser@0.36.1: + resolution: {integrity: sha512-ycduwJbvdvIMmVvlAZqGggS+pm5Eu4Bk9pcV9Sm2Z4PJNRVsKkv0g7vHj+LeuC1gHTeF67sJXFOq61IlqCa2hA==} + + babel-plugin-transform-flow-enums@0.0.2: + resolution: {integrity: sha512-g4aaCrDDOsWjbm0PUUeVnkcVd6AKJsVc/MbnPhEotEpkeJQP6b8nzewohQi7+QS8UyPehOhGWn0nOwjvWpmMvQ==} + + babel-preset-current-node-syntax@1.2.0: + resolution: {integrity: sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==} + peerDependencies: + '@babel/core': ^7.0.0 || ^8.0.0-0 + + babel-preset-expo@57.0.7: + resolution: {integrity: sha512-/1RLnZTJVoTNo6nCdSv27BSA2LBzM/qEkNLznwWvztg64DhsAz7ByZIiAqdbau9FK3YqF+IV5a2ZsPXFclwq4A==} + peerDependencies: + '@babel/runtime': ^7.20.0 + expo: '*' + expo-widgets: ^57.0.10 + react-refresh: '>=0.14.0 <1.0.0' + peerDependenciesMeta: + '@babel/runtime': + optional: true + expo: + optional: true + expo-widgets: + optional: true + + babel-preset-jest@29.6.3: + resolution: {integrity: sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@babel/core': ^7.0.0 + + badgin@1.2.3: + resolution: {integrity: sha512-NQGA7LcfCpSzIbGRbkgjgdWkjy7HI+Th5VLxTJfW5EeaAf3fnS+xWQaQOCYiny+q6QSvxqoSO04vCx+4u++EJw==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + + barcode-detector@3.2.2: + resolution: {integrity: sha512-/4QOrrNrCRmDSBWiiP4aC72dnkuXUEdcFRidHgbPRXUpy82XcCMvJssI6fEs6JT42LS7DvBVZO70qasJbYKyrA==} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + baseline-browser-mapping@2.11.15: + resolution: {integrity: sha512-FwMjJJ7HnyZpWe+oWxegG0fezZyBZUagI5LZEoO3GCbtbKNwRfMH9Ue5d5v01PNePBy1QSfPSDTTeVL0Hb9EzA==} + engines: {node: '>=6.0.0'} + hasBin: true + + big-integer@1.6.52: + resolution: {integrity: sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==} + engines: {node: '>=0.6'} + + bplist-creator@0.1.0: + resolution: {integrity: sha512-sXaHZicyEEmY86WyueLTQesbeoH/mquvarJaQNbjuOQO+7gbFcDEWqKmcWA4cOTLzFlfgvkiVxolk1k5bBIpmg==} + + bplist-parser@0.3.1: + resolution: {integrity: sha512-PyJxiNtA5T2PlLIeBot4lbp7rj4OadzjnMZD/G5zuBNt8ei/yCU7+wW0h2bag9vr8c+/WuRWmSxbqAl9hL1rBA==} + engines: {node: '>= 5.10.0'} + + bplist-parser@0.3.2: + resolution: {integrity: sha512-apC2+fspHGI3mMKj+dGevkGo/tCqVB8jMb6i+OX+E29p0Iposz07fABkRIfVUPNd5A5VbuOz1bZbnmkKLYF+wQ==} + engines: {node: '>= 5.10.0'} + + brace-expansion@1.1.18: + resolution: {integrity: sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==} + + brace-expansion@2.1.4: + resolution: {integrity: sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==} + + brace-expansion@5.0.9: + resolution: {integrity: sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==} + engines: {node: 20 || >=22} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browserslist@4.28.8: + resolution: {integrity: sha512-V2NpofLblG64mfOtSgDhOJESZEGogzDMBv/q+W6oc4LXWP/q75eOXoOaaOu1EOadB9U4Bwx/e0yzbvwKH8zalA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + bser@2.1.1: + resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} + + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + + bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bind@1.0.9: + resolution: {integrity: sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + camelcase@5.3.1: + resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} + engines: {node: '>=6'} + + camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + + caniuse-lite@1.0.30001809: + resolution: {integrity: sha512-xxWVywk6a6Arlk+hymeycyn/VgqEfLDxupvhH/xiY5SJ/18kmi9o6MiO320DCUzypORHLtvh0I4i04tUhCNHNQ==} + + chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + + chalk@3.0.0: + resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==} + engines: {node: '>=8'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + change-case@5.4.4: + resolution: {integrity: sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==} + + char-regex@1.0.2: + resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} + engines: {node: '>=10'} + + char-regex@2.0.2: + resolution: {integrity: sha512-cbGOjAptfM2LVmWhwRFHEKTPkLwNddVmuqYZQt895yXwAsWsXObCG+YN4DGQ/JBtT4GP1a1lPPdio2z413LmTg==} + engines: {node: '>=12.20'} + + chrome-launcher@0.15.2: + resolution: {integrity: sha512-zdLEwNo3aUVzIhKhTtXfxhdvZhUghrnmkvcAq2NoDd+LeOHKf03H5jwZ8T/STsAlzyALkBVK552iaG1fGf1xVQ==} + engines: {node: '>=12.13.0'} + hasBin: true + + chromium-edge-launcher@0.3.0: + resolution: {integrity: sha512-p03azHlGjtyRvFEee3cyvtsRYdniSkwjkzmM/KmVnqT5d7QkkwpJBhis/zCLMYdQMVJ5tt140TBNqqrZPaWeFA==} + + ci-info@2.0.0: + resolution: {integrity: sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==} + + ci-info@3.9.0: + resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} + engines: {node: '>=8'} + + cjs-module-lexer@1.4.3: + resolution: {integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==} + + cli-cursor@2.1.0: + resolution: {integrity: sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==} + engines: {node: '>=4'} + + cli-spinners@2.9.2: + resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} + engines: {node: '>=6'} + + client-only@0.0.1: + resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + clone@1.0.4: + resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} + engines: {node: '>=0.8'} + + co@4.6.0: + resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} + engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} + + collect-v8-coverage@1.0.3: + resolution: {integrity: sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==} + + color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + color-string@1.9.1: + resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + + color@4.2.3: + resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} + engines: {node: '>=12.5.0'} + + colorette@1.4.0: + resolution: {integrity: sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + commander@12.1.0: + resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} + engines: {node: '>=18'} + + commander@2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + + commander@7.2.0: + resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} + engines: {node: '>= 10'} + + compressible@2.0.18: + resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==} + engines: {node: '>= 0.6'} + + compression@1.8.1: + resolution: {integrity: sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==} + engines: {node: '>= 0.8.0'} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + connect@3.7.0: + resolution: {integrity: sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==} + engines: {node: '>= 0.10.0'} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + core-js-compat@3.50.0: + resolution: {integrity: sha512-XGpFGbMLHwSt74YLTKho7Ib242qi6O8MSX+sRokV4oz7iKXvQWGYZthjIhjRGMxjzVkAubBO512dKGYcefmX3Q==} + engines: {node: '>=6.4.0'} + + create-jest@29.7.0: + resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + + cross-env@10.1.0: + resolution: {integrity: sha512-GsYosgnACZTADcmEyJctkJIoqAhHjttw7RsFrVoJNXbsWWqaq6Ym+7kZjq6mS45O0jij6vtiReppKQEtqWy6Dw==} + engines: {node: '>=20'} + hasBin: true + + cross-fetch@3.2.0: + resolution: {integrity: sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + css-in-js-utils@3.1.0: + resolution: {integrity: sha512-fJAcud6B3rRu+KHYk+Bwf+WFL2MDCJJ1XG9x137tJQ0xYxor7XziQtuGFbWNdqrvF4Tk26O3H73nfVqXt/fW1A==} + + cssom@0.3.8: + resolution: {integrity: sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==} + + cssom@0.5.0: + resolution: {integrity: sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==} + + cssstyle@2.3.0: + resolution: {integrity: sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==} + engines: {node: '>=8'} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + data-urls@3.0.2: + resolution: {integrity: sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==} + engines: {node: '>=12'} + + data-view-buffer@1.0.2: + resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} + engines: {node: '>= 0.4'} + + data-view-byte-length@1.0.2: + resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} + engines: {node: '>= 0.4'} + + data-view-byte-offset@1.0.1: + resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} + engines: {node: '>= 0.4'} + + dayjs@1.11.23: + resolution: {integrity: sha512-QDTCU0M0MxR3hQfnlDJfwekQiaanm1ubOD231u73WBckQ/fsamwRLiE2GBz6D3a/xF1NgfiDLJjXBa1hYOYTtQ==} + + debug@2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decimal.js@10.6.0: + resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} + + decode-uri-component@0.2.2: + resolution: {integrity: sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==} + engines: {node: '>=0.10'} + + dedent@1.7.2: + resolution: {integrity: sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==} + peerDependencies: + babel-plugin-macros: ^3.1.0 + peerDependenciesMeta: + babel-plugin-macros: + optional: true + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + + defaults@1.0.4: + resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + + destroy@1.2.0: + resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + detect-newline@3.1.0: + resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} + engines: {node: '>=8'} + + detect-node-es@1.1.0: + resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} + + diff-sequences@29.6.3: + resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + dnssd-advertise@1.1.6: + resolution: {integrity: sha512-Ndrrf6BMPalkQPd/zubL+4YghH2J9NspapQ09uDXwYbvOPkP0oaqf5CkcwJ0b50kS2O3ul6yVu+jz+RY62Cejg==} + + doctrine@2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + + domexception@4.0.0: + resolution: {integrity: sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==} + engines: {node: '>=12'} + deprecated: Use your platform's native DOMException instead + + dotenv@17.4.2: + resolution: {integrity: sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==} + engines: {node: '>=12'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + + electron-to-chromium@1.5.411: + resolution: {integrity: sha512-gglkxzokjHfawpGxq75XdBV2/l3BAPzrsMs70qgaZdTW5rpV1tC4MdgJVP9fN126bODA4ZJQkn1wryEzJyQXIg==} + + emittery@0.13.1: + resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} + engines: {node: '>=12'} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + encodeurl@1.0.2: + resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} + engines: {node: '>= 0.8'} + + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + + entities@6.0.1: + resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} + engines: {node: '>=0.12'} + + error-ex@1.3.4: + resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} + + error-stack-parser@2.1.4: + resolution: {integrity: sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==} + + es-abstract-get@1.0.0: + resolution: {integrity: sha512-6PMWXpdhshVvFp+FoWYs1EvG1Nj0tvk0dZM+XcK0xMEM1czRVcP6ohqPWHy6qPagSpC8j4+p89WXlT+xXJs/fg==} + engines: {node: '>= 0.4'} + + es-abstract@1.24.2: + resolution: {integrity: sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==} + engines: {node: '>= 0.4'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-iterator-helpers@1.4.0: + resolution: {integrity: sha512-c/A0P0oxkACDc+cKWw8evLXK83oBKgn0qPOqCYT4x9uolpCIJAcYvJC9QYKNDRPsTeGyCrQ326jrvgZWdCdK5Q==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.1.2: + resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + es-shim-unscopables@1.1.0: + resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==} + engines: {node: '>= 0.4'} + + es-to-primitive@1.3.4: + resolution: {integrity: sha512-yPDz7wqpg1/mmHLmS3tcfTfbw5f1eryXvyghYBffGdERwe+mV7ZcWzTR8LR17Kvqt3qfPurjlonmnq3MKXIOXw==} + engines: {node: '>= 0.4'} + + esbuild@0.28.2: + resolution: {integrity: sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + + escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + + escape-string-regexp@2.0.0: + resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} + engines: {node: '>=8'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + escodegen@2.1.0: + resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} + engines: {node: '>=6.0'} + hasBin: true + + eslint-config-expo@57.0.1: + resolution: {integrity: sha512-OsLAReXOF3uXBwyxxw4f55sORYC+lOH4pSaGdU9MnYhRpjqPziK2uCnRxjLTGKeAKER7GSX5Wz9kNMGJT1MZEw==} + peerDependencies: + eslint: '>=8.10' + + eslint-import-resolver-node@0.3.10: + resolution: {integrity: sha512-tRrKqFyCaKict5hOd244sL6EQFNycnMQnBe+j8uqGNXYzsImGbGUU4ibtoaBmv5FLwJwcFJNeg1GeVjQfbMrDQ==} + + eslint-import-resolver-typescript@3.10.1: + resolution: {integrity: sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + eslint: '*' + eslint-plugin-import: '*' + eslint-plugin-import-x: '*' + peerDependenciesMeta: + eslint-plugin-import: + optional: true + eslint-plugin-import-x: + optional: true + + eslint-module-utils@2.14.0: + resolution: {integrity: sha512-W2WCRZ9Dqntd+2u8jJcVMV2PKulc6RdLgUUoh/yQr3uB6lo/ZOeGx11sv60/8S4QFFKNslAlWhr9u0Ef7ZW6Ig==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + + eslint-plugin-expo@1.1.0: + resolution: {integrity: sha512-vPP0EPx7IA7ZfP49dY4rq9RV5jqkFWG+Pih3/oGjzIRjMI+ogcOE8i6isYkLXAdw/yvFV2BRZkTaQaiOGQqn6Q==} + engines: {node: '>=18.0.0'} + peerDependencies: + eslint: '>=8.10' + + eslint-plugin-import@2.32.0: + resolution: {integrity: sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + + eslint-plugin-react-hooks@7.1.1: + resolution: {integrity: sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==} + engines: {node: '>=18'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0 + + eslint-plugin-react@7.37.5: + resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==} + engines: {node: '>=4'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 + + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@5.0.1: + resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + eslint@9.39.5: + resolution: {integrity: sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + esquery@1.7.0: + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + + event-target-polyfill@0.0.4: + resolution: {integrity: sha512-Gs6RLjzlLRdT8X9ZipJdIZI/Y6/HhRLyq9RdDlCsnpxr/+Nn6bU2EFGuC94GjxqhM+Nmij2Vcq98yoHrU8uNFQ==} + + event-target-shim@5.0.1: + resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} + engines: {node: '>=6'} + + execa@5.1.1: + resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} + engines: {node: '>=10'} + + exit@0.1.2: + resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} + engines: {node: '>= 0.8.0'} + + expect@29.7.0: + resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + expo-application@57.0.2: + resolution: {integrity: sha512-q31YwcXyymviAmdrtDfAg3Dld4VMxLCNAfgMHip7vZpPX4lzF/AfwsywqBJUAjQnJknzaNAkzqvMVjO5XmKYDA==} + peerDependencies: + expo: '*' + + expo-asset@57.0.13: + resolution: {integrity: sha512-RPjMcmPXRMb6UbQdTIkmSDEnQL5LKGTu7ZXatq3U67V6ldlSdQeQ9pQV7zlmk0DrbnaMEh3HYiEh2YxWLNyCzA==} + peerDependencies: + expo: '*' + react: '*' + react-native: '*' + + expo-camera@57.0.4: + resolution: {integrity: sha512-MqbbQ63O+cA8JbK3lfFHiD0f2jv8jB+EG/ILK6f5xnOPV9IR37yaG5+lGtRZNoOeYBzGypyOi17USItleMyuGw==} + peerDependencies: + expo: '*' + react: '*' + react-native: '*' + react-native-web: '*' + peerDependenciesMeta: + react-native-web: + optional: true + + expo-constants@57.0.13: + resolution: {integrity: sha512-eB5AHp7kKxsVIBjTetUgj5WQSw8joI2ekzgTlcUv+Hc0x2t0jZU6IiL4DpyOT2yZQ71n0WJZCkmOKLgxlajIzg==} + peerDependencies: + expo: '*' + react-native: '*' + + expo-crypto@57.0.2: + resolution: {integrity: sha512-OGLvHK7Hb7qsONMWGB9pJw3n+iY4OnZQmM/KwQEHRMrrOmNg0Lxj9+tp9bj46aAMMzXx0FTVA2jUKoplpXPnkA==} + peerDependencies: + expo: '*' + + expo-dev-client@57.0.14: + resolution: {integrity: sha512-0WEZJZtUZC0PgkZXm3BHWVYiOBM8hbkMP50Nk24X+K6k5+EWBmwmsSO/K4sojKyO1Pxhb/GHtSYaV8t1YI1x/w==} + peerDependencies: + expo: '*' + + expo-dev-launcher@57.0.14: + resolution: {integrity: sha512-zb1Y6cBIhniBjTpihBHaWx0O81w3aWp5ddAw+ThpfCGrMVlYCdaBb2zlo1dZ1swJna7mn09eIID97U3pPAh83g==} + peerDependencies: + expo: '*' + react-native: '*' + + expo-dev-menu-interface@57.0.0: + resolution: {integrity: sha512-F47VdzOHYc19FhI/jBgctpO8a5UskTIxG6a1E5t3W5gF8VImuvBQffdXXfLHhsuCl7dS3v3U0R45cleeVXO1Zg==} + peerDependencies: + expo: '*' + + expo-dev-menu@57.0.14: + resolution: {integrity: sha512-lTD5ErQrrH57v1HOM4RY2gA9hmvqXoZ/AArM7LQLCts3fKqaahOGg45ETRUgdvULv3K4wbKwhXwCRoANWMxGTw==} + peerDependencies: + expo: '*' + react-native: '*' + + expo-device@57.0.1: + resolution: {integrity: sha512-jyEMDUticH+dhcL3GHa2aiifOvGXJsmb3oVT2R2q4i8bN7Bddy61+NkpMmuS2VAZrvoLQwf0TJJ/1vi1ukvutA==} + peerDependencies: + expo: '*' + + expo-document-picker@57.0.1: + resolution: {integrity: sha512-qBwM5oxDZ3I9kwFD3pUE1oK/WNv9artoEKO6UpqhQgNRr0XA1ALRVWYjkF4+ge9lUNDRehjTm/jenINkzqg84g==} + peerDependencies: + expo: '*' + + expo-eas-client@57.0.1: + resolution: {integrity: sha512-4w51+zsl/ziUHQMJgLgUdgsNhRPAwHBfySpPB1hpWU21X74QS9T4SqDftaRnrDagn/DfcrXUMJvHbDQUxLPJNA==} + + expo-file-system@57.0.5: + resolution: {integrity: sha512-XjGrCClF0935y5wLB9qNQQUVptNEy+0OloBstXlCd+IeNXLo3uNOod6cX4N0hofIhNIrN1Al8fwfay7R0Z1a2A==} + peerDependencies: + expo: '*' + react-native: '*' + + expo-file-system@57.0.6: + resolution: {integrity: sha512-pm8PMYEW6BnVOCBJ7df9FcDmQtE1tqImuYphlfYe1ipQRLYtdCayRczJcbKIsnB3mqEyp4gC2gWmMbsAsAOjVg==} + peerDependencies: + expo: '*' + react-native: '*' + + expo-font@57.0.1: + resolution: {integrity: sha512-QyS9L1Kh9sKJg4gfU6rdbpxpmH+DyzBX8z6jVvXMUDoqLr1GqmkO/Wu379KCXjL///kWbhpNlbi7AgBuj4VdIQ==} + peerDependencies: + expo: '*' + react: '*' + react-native: '*' + + expo-glass-effect@57.0.1: + resolution: {integrity: sha512-m/n8maxqNcHk6ZDhuqXBfD5Kt1Iz3M8xykVgdB0iSCIXvF70IqWXmQhX8Psswhrp8eZ+3r0mAD0Jh/2gFA3QaA==} + peerDependencies: + expo: '*' + react: '*' + react-native: '*' + + expo-haptics@57.0.2: + resolution: {integrity: sha512-vPths6zTxxaGaemC7D1GKbw1iOnOAAL5oAcCFVSgVNWnuLCBefL1LkhRBaJsepiMXRh2y3vwPiXkPaf6d16blA==} + peerDependencies: + expo: '*' + + expo-image-loader@57.0.1: + resolution: {integrity: sha512-uhrZKLT/cTl2mXyR28kPpVkS5O+PK9N1QA/07IFM4f5T4g0lTW1JHT3NEWwEEsGFldPmVX4j7LwUVVZxE+woug==} + peerDependencies: + expo: '*' + + expo-image-picker@57.0.12: + resolution: {integrity: sha512-BjZLioItcDNrXwp46XZDZGDEUJ16/bK0QzxPjdNijiPYpLfQLeuFYoX9yH31sosnli0UL++x4ndYQoTMsICdlw==} + peerDependencies: + expo: '*' + + expo-image@57.0.3: + resolution: {integrity: sha512-EYfV8tIQxXLQPHhgZxc+bESUn2NcVw1U0RM28qqFeFfHyD1sBpIYJC2JTy24OtfBowYVoUyTvgf2ykWydiZVCw==} + peerDependencies: + expo: '*' + react: '*' + react-native: '*' + react-native-web: '*' + peerDependenciesMeta: + react-native-web: + optional: true + + expo-json-utils@57.0.1: + resolution: {integrity: sha512-cgTe1NqzQdYs/WN+3nIY5IZg8s0pb0xaTUbhYvxQDn137GbwRfHoGM2se3m3Vsl4Qu+B9G4RPEK5WJDEU2Do7g==} + + expo-keep-awake@57.0.1: + resolution: {integrity: sha512-28lkFImeXTS+bhAjuCFV7w7tW5bXg27BJVrxv+nC/nyYa86qEa0oFeHwqol6ha5k4pdVDQgBF09GM4A1k76Ssg==} + peerDependencies: + expo: '*' + react: '*' + + expo-linking@57.0.7: + resolution: {integrity: sha512-Ujn4kY0bd1MoJFLYuvIUWamlZH61pkahrUpug3hBtOeS89LDbPyKNjjmWef6+CPTGQfed2PlfuMZuPO1BCrg5w==} + peerDependencies: + react: '*' + react-native: '*' + + expo-localization@57.0.1: + resolution: {integrity: sha512-8Ffl4UTbOsQeGT0v5fxMbyPHyPMPnhSPDFQJa8p9rjJrthFoAtNi+fL6Ssmrvf1/7dmPq1mVY52MEt0TMEfgjA==} + peerDependencies: + expo: '*' + react: '*' + + expo-manifests@57.0.1: + resolution: {integrity: sha512-qB/mDG2dYdl+EvUeQuqP8KFYCFgFCQjJYdWIHo8SFBgDzMYmdF286DFY2M1M9Okr99wkb5M4tgA3aCcwv3aEQA==} + peerDependencies: + expo: '*' + + expo-modules-autolinking@57.0.10: + resolution: {integrity: sha512-jNqLswMx8QHN8VXRgud+xT+9q+J9U63CEGlx+k4V/IE9Qyt9HrKbWp+pIDma0fOquta5HBfSfsShcI+NkEpTiA==} + hasBin: true + + expo-modules-core@57.0.12: + resolution: {integrity: sha512-hKqCdu8+78oNKWCgM4xSeblfmD/audgNVCn6uUCJuNBS/hc4DyC0Ia1X96SSZF9w9bGTLjNS8j2b7DshiC3WJA==} + peerDependencies: + react: '*' + react-native: '*' + react-native-worklets: ^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0 + peerDependenciesMeta: + react-native-worklets: + optional: true + + expo-modules-jsi@57.0.5: + resolution: {integrity: sha512-NQF7MUF0j3rQHV8KJqETYA1SrJr3USvQ/AWEhODo+unMRFBDP9BKpn8+aNGGmx6fa2XLnqC2qulVw4DT6Gp13Q==} + peerDependencies: + react-native: '*' + + expo-notifications@57.0.13: + resolution: {integrity: sha512-4kTLfvCbpr92RWWmYGO1BP4pJ5ZhxOdhB/6zdtNs44wV7h6zKrbweccwovgZTLo4Ef/KPL3SD02Vam4Tl/xv9A==} + peerDependencies: + expo: '*' + react: '*' + react-native: '*' + + expo-router@57.0.15: + resolution: {integrity: sha512-vwr2HL2U7hqv3QTkuz0e8hQc5hkgpXztisy30VwhdXJVDPdP0/m8wSiYUm6GKyZAbcXfD4fn44ZYcJsSESY6cA==} + peerDependencies: + '@expo/log-box': ^57.0.3 + '@expo/metro-runtime': ^57.0.12 + '@testing-library/react-native': '>= 13.2.0' + expo: '*' + expo-constants: ^57.0.13 + expo-linking: ^57.0.7 + react: '*' + react-dom: '*' + react-native: '*' + react-native-gesture-handler: '*' + react-native-reanimated: '*' + react-native-safe-area-context: '>= 5.4.0' + react-native-screens: ^4.26.0 + react-native-web: '*' + react-server-dom-webpack: ~19.0.4 || ~19.1.5 || ~19.2.4 + peerDependenciesMeta: + '@testing-library/react-native': + optional: true + react-dom: + optional: true + react-native-gesture-handler: + optional: true + react-native-reanimated: + optional: true + react-native-web: + optional: true + react-server-dom-webpack: + optional: true + + expo-secure-store@57.0.1: + resolution: {integrity: sha512-tLa1VmSadOq19mA/dwkl99RbHyjLE0T1qqBYMY3/OsguZTI+rlrDy/DDJjupqlVtmr95hD7o1pYqx5aL+B4YMA==} + peerDependencies: + expo: '*' + + expo-server@57.0.3: + resolution: {integrity: sha512-aK+LdKzauHSGmsOStZtyxdzv0zWssCkxTw3m4QuOhfDSJsZaMRTd9O41d8ixU/QfELTbaJ0oRNcF7JFV/7O9YQ==} + engines: {node: '>=20.16.0'} + + expo-splash-screen@57.0.7: + resolution: {integrity: sha512-QxqfjOCTnTGnqK2BxfEtxUJOcnsJbUXO873Q342r3VkgHAG877eozsNzgYWQXcCFj/asMwx2uy9OXxBShxc5rQ==} + peerDependencies: + expo: '*' + + expo-status-bar@57.0.1: + resolution: {integrity: sha512-Xwaq1gAoVRWx5dPG5VhT5RSbnI9OilhZnO5qoPBnUaBAa5VzRzfdS8q0/bsPt0jR2DKLtGuP0bQ6efMJ4RIMDg==} + peerDependencies: + expo: '*' + react: '*' + react-native: '*' + + expo-structured-headers@57.0.0: + resolution: {integrity: sha512-//t9UNPbJSEysc2x4VKJG/u7Osvv5DYJWsET5bqt/B+qcD1by/JXvSQzX3Q/YAgA96xFPontrz6OAPLbO4JKEA==} + + expo-symbols@57.0.2: + resolution: {integrity: sha512-qZ0iqOflm5lZGwRsQ5Y8sDksw3GAUKwHSX1bJBoocXf7gu14vafIXXYWte+JT9VfXAUGyKodJhLHP/GoOrcNWg==} + peerDependencies: + expo: '*' + expo-font: '*' + react: '*' + react-native: '*' + + expo-system-ui@57.0.2: + resolution: {integrity: sha512-zABCRqFSioDBAo/RtmS0dQiGgDtDPZUVk01Y3Ti4ducobNM4HTM2sNAtq+YPpEUhaVW3hccPVsEUH4LK/ADVhA==} + peerDependencies: + expo: '*' + react-native: '*' + react-native-web: '*' + peerDependenciesMeta: + react-native-web: + optional: true + + expo-updates-interface@57.0.1: + resolution: {integrity: sha512-+LUWwJ0gf/TEKMVdQAw/Gjih4dvrk+URgy24X9qEGKuuMDZqjBRm9T4yQyBVALGL5TTdPUaB6ILxx3lshm3pwQ==} + peerDependencies: + expo: '*' + + expo-updates@57.0.16: + resolution: {integrity: sha512-eb0arz9Vp5m1RaUKO6PJxogJv8z15/Rjt5GsRcSUjrf0vhignlT7b0D/Z07Po/ACG1H7Yn7rwIc73T647JVJFA==} + hasBin: true + peerDependencies: + expo: '*' + expo-dev-client: '*' + react: '*' + react-native: '*' + peerDependenciesMeta: + expo-dev-client: + optional: true + + expo-web-browser@57.0.2: + resolution: {integrity: sha512-3vl5kvd7PB48ub6PpNIJUuPxO8xVa6D8RnIgNba6SXRwqFprOfeEZgwTgtm41kz0AAtvMOztUVNEUkwrHKjqMQ==} + peerDependencies: + expo: '*' + react-native: '*' + + expo@57.0.15: + resolution: {integrity: sha512-9UooISw8S8Gxo9wpsPcGkyRlf2UzNr+F26LSU2+M8iaiZ1WAjPR0I75NKFX8ETbrSgthZ2lQHgy5IU2KE/mpBA==} + hasBin: true + peerDependencies: + '@expo/dom-webview': '*' + '@expo/metro-runtime': '*' + react: '*' + react-dom: '*' + react-native: '*' + react-native-web: '*' + react-native-webview: '*' + peerDependenciesMeta: + '@expo/dom-webview': + optional: true + '@expo/metro-runtime': + optional: true + react-dom: + optional: true + react-native-web: + optional: true + react-native-webview: + optional: true + + exponential-backoff@3.1.3: + resolution: {integrity: sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fb-dotslash@0.5.8: + resolution: {integrity: sha512-XHYLKk9J4BupDxi9bSEhkfss0m+Vr9ChTrjhf9l2iw3jB5C7BnY4GVPoMcqbrTutsKJso6yj2nAB6BI/F2oZaA==} + engines: {node: '>=20'} + hasBin: true + + fb-watchman@2.0.2: + resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} + + fbjs-css-vars@1.0.2: + resolution: {integrity: sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==} + + fbjs@3.0.5: + resolution: {integrity: sha512-ztsSx77JBtkuMrEypfhgc3cI0+0h+svqeie7xHbh1k/IKdcydnvadp/mUaGgjAOXQmQSxsqgaRhS3q9fy+1kxg==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fetch-nodeshim@0.4.10: + resolution: {integrity: sha512-m6I8ALe4L4XpdETy7MJZWs6L1IVMbjs99bwbpIKphxX+0CTns4IKDWJY0LWfr4YsFjfg+z1TjzTMU8lKl8rG0w==} + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + filter-obj@1.1.0: + resolution: {integrity: sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==} + engines: {node: '>=0.10.0'} + + finalhandler@1.1.2: + resolution: {integrity: sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==} + engines: {node: '>= 0.8'} + + find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flatted@3.4.4: + resolution: {integrity: sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==} + + flow-enums-runtime@0.0.6: + resolution: {integrity: sha512-3PYnM29RFXwvAN6Pc/scUfkI7RwhQ/xqyLUyPNlXUp9S40zI8nup9tUSrTLSVnWGBN38FNiGWbwZOB6uR4OGdw==} + + fontfaceobserver@2.3.0: + resolution: {integrity: sha512-6FPvD/IVyT4ZlNe7Wcn5Fb/4ChigpucKYSvD6a+0iMoLn2inpo711eyIcKjmDtE5XNcgAkSH9uN/nfAeZzHEfg==} + + for-each@0.3.5: + resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} + engines: {node: '>= 0.4'} + + form-data@4.0.6: + resolution: {integrity: sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==} + engines: {node: '>= 6'} + + fresh@0.5.2: + resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} + engines: {node: '>= 0.6'} + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + function.prototype.name@1.2.0: + resolution: {integrity: sha512-jObKIik1P2QjPHP5nz5BaOtUlfgS0fWo8IUByNXkM+o+02sJOi94em77GwJKQSJ3gfPHdgzLNrHc1uokV4P/ew==} + engines: {node: '>= 0.4'} + + functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + + generator-function@2.0.1: + resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} + engines: {node: '>= 0.4'} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-nonce@1.0.1: + resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} + engines: {node: '>=6'} + + get-package-type@0.1.0: + resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} + engines: {node: '>=8.0.0'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-stream@6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + + get-symbol-description@1.1.0: + resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} + engines: {node: '>= 0.4'} + + get-tsconfig@4.14.3: + resolution: {integrity: sha512-++QEw4DIY7WGoukz+/+A/8dGYPT9l9yIadnmSgZ8Rjr3YVSVDipQSO9CdnJo9ePqFqUUqh+wk9uIaoiAwsiPkA==} + + getenv@2.0.0: + resolution: {integrity: sha512-VilgtJj/ALgGY77fiLam5iD336eSWi96Q15JSAG1zi8NRBysm3LXKdGnHb4m5cuyxvOLQQKWpBZAT6ni4FI2iQ==} + engines: {node: '>=6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + glob@13.0.6: + resolution: {integrity: sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==} + engines: {node: 18 || 20 || >=22} + + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + globals@16.5.0: + resolution: {integrity: sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==} + engines: {node: '>=18'} + + globalthis@1.0.4: + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + engines: {node: '>= 0.4'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + has-bigints@1.1.0: + resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} + engines: {node: '>= 0.4'} + + has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-proto@1.2.0: + resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} + engines: {node: '>= 0.4'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.4: + resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} + engines: {node: '>= 0.4'} + + hermes-compiler@250829098.0.16: + resolution: {integrity: sha512-xsgzk+mUyvt9t1nUbF8USBlYxajTUtPJhVZ86q85s/SEoMKCF+52YZcudb0ENSnV3T3lV9mgB3s6R7+pH90zgw==} + + hermes-estree@0.25.1: + resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==} + + hermes-estree@0.35.0: + resolution: {integrity: sha512-xVx5Opwy8Oo1I5yGpVRhCvWL/iV3M+ylksSKVNlxxD90cpDpR/AR1jLYqK8HWihm065a6UI3HeyAmYzwS8NOOg==} + + hermes-estree@0.36.0: + resolution: {integrity: sha512-A1+8zn5oss2CFP7pKsOaxorQG6FNIz1WU1VDqruLPPZl3LVgeE2C5xfFg8Ow6/Ow4mSslLLtYP1J3n38eKyW9w==} + + hermes-estree@0.36.1: + resolution: {integrity: sha512-guv1nQ6IJ7S83NRFPWc3SA7IBZrdNC9kapwOq6uXvF4wP+sDCgjzQbKPCoyYmoyZRzztF/n/c36l/rccCZSiCw==} + + hermes-parser@0.25.1: + resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==} + + hermes-parser@0.35.0: + resolution: {integrity: sha512-9JLjeHxBx8T4CAsydZR49PNZUaix+WpQJwu9p2010lu+7Kwl6D/7wYFFJxoz+aXkaaClp9Zfg6W6/zVlSJORaA==} + + hermes-parser@0.36.0: + resolution: {integrity: sha512-GdpwMmH5x6IpC1cijvcvYnlPB60Mh6kTSF/NFdYV/j56gYdi+0RIakYs+eqOV+bbO0SW7mgVVGSsTJxyPQfo3w==} + + hermes-parser@0.36.1: + resolution: {integrity: sha512-GApNk4zLHi2UWoWZZkx7LNCOSzLSc5lB55pZ/PhK7ycFeg7u5LcF88p/WbpIi1XUDtE0MpHE3uRR3u3KB7TjSQ==} + + hoist-non-react-statics@3.3.2: + resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} + + hosted-git-info@7.0.2: + resolution: {integrity: sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==} + engines: {node: ^16.14.0 || >=18.0.0} + + html-encoding-sniffer@3.0.0: + resolution: {integrity: sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==} + engines: {node: '>=12'} + + html-escaper@2.0.2: + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + + html-parse-stringify@4.0.1: + resolution: {integrity: sha512-0zHsZJrK7S3K2aucXWL6ycoYJ/iNtIcFHC/nYQgFklPtrv5LpJctIiSCroWZWeuoXvuyFdzp6KzjJQ+OT5MfFw==} + + http-errors@2.0.1: + resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} + engines: {node: '>= 0.8'} + + http-proxy-agent@5.0.0: + resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==} + engines: {node: '>= 6'} + + https-proxy-agent@5.0.1: + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + engines: {node: '>= 6'} + + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + + human-signals@2.1.0: + resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} + engines: {node: '>=10.17.0'} + + hyphenate-style-name@1.1.0: + resolution: {integrity: sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==} + + i18next@26.3.6: + resolution: {integrity: sha512-Bu5Z2nAXgfVyM8xvW3jk9EKRIuX37PudsrBViThNFx7CR7aaYTpP01cxNB/E4c4UUzTDiAZRstEhsRfPOL/8xA==} + peerDependencies: + typescript: ^5 || ^6 || ^7 + peerDependenciesMeta: + typescript: + optional: true + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ignore@7.0.6: + resolution: {integrity: sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==} + engines: {node: '>= 4'} + + image-size@1.2.1: + resolution: {integrity: sha512-rH+46sQJ2dlwfjfhCyNx5thzrv+dtmBIhPHk0zgRUukHzZ/kRueTJXoYYsclBaKcSMBWuGbOFXtioLpzTb5euw==} + engines: {node: '>=16.x'} + hasBin: true + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + import-local@3.2.0: + resolution: {integrity: sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==} + engines: {node: '>=8'} + hasBin: true + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + indent-string@4.0.0: + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} + + index-to-position@1.2.0: + resolution: {integrity: sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==} + engines: {node: '>=18'} + + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + inline-style-prefixer@7.0.1: + resolution: {integrity: sha512-lhYo5qNTQp3EvSSp3sRvXMbVQTLrvGV6DycRMJ5dm2BLMiJ30wpXKdDdgX+GmJZ5uQMucwRKHamXSst3Sj/Giw==} + + internal-slot@1.1.0: + resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} + engines: {node: '>= 0.4'} + + invariant@2.2.4: + resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} + + is-array-buffer@3.0.5: + resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} + engines: {node: '>= 0.4'} + + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + + is-arrayish@0.3.4: + resolution: {integrity: sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==} + + is-async-function@2.1.1: + resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} + engines: {node: '>= 0.4'} + + is-bigint@1.1.0: + resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} + engines: {node: '>= 0.4'} + + is-boolean-object@1.2.2: + resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} + engines: {node: '>= 0.4'} + + is-bun-module@2.0.0: + resolution: {integrity: sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==} + + is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + + is-core-module@2.16.2: + resolution: {integrity: sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==} + engines: {node: '>= 0.4'} + + is-data-view@1.0.2: + resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} + engines: {node: '>= 0.4'} + + is-date-object@1.1.0: + resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} + engines: {node: '>= 0.4'} + + is-docker@2.2.1: + resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} + engines: {node: '>=8'} + hasBin: true + + is-document.all@1.0.0: + resolution: {integrity: sha512-+XSoyS05OdBbhFuELhgTCpFNHkpBOJqtsZfUFFpe5QTw+9Sjbh8zitxhQkYAo6wV7e1Vb8cAPvpCk9jGam/82g==} + engines: {node: '>= 0.4'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-finalizationregistry@1.1.1: + resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} + engines: {node: '>= 0.4'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-generator-fn@2.1.0: + resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} + engines: {node: '>=6'} + + is-generator-function@1.1.2: + resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} + engines: {node: '>= 0.4'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-map@2.0.3: + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} + engines: {node: '>= 0.4'} + + is-negative-zero@2.0.3: + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} + engines: {node: '>= 0.4'} + + is-number-object@1.1.1: + resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} + engines: {node: '>= 0.4'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-plain-obj@2.1.0: + resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==} + engines: {node: '>=8'} + + is-potential-custom-element-name@1.0.1: + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + + is-regex@1.2.1: + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} + engines: {node: '>= 0.4'} + + is-set@2.0.3: + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} + engines: {node: '>= 0.4'} + + is-shared-array-buffer@1.0.4: + resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} + engines: {node: '>= 0.4'} + + is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + + is-string@1.1.1: + resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} + engines: {node: '>= 0.4'} + + is-symbol@1.1.1: + resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} + engines: {node: '>= 0.4'} + + is-typed-array@1.1.15: + resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} + engines: {node: '>= 0.4'} + + is-weakmap@2.0.2: + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + engines: {node: '>= 0.4'} + + is-weakref@1.1.1: + resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==} + engines: {node: '>= 0.4'} + + is-weakset@2.0.4: + resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} + engines: {node: '>= 0.4'} + + is-wsl@2.2.0: + resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} + engines: {node: '>=8'} + + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + istanbul-lib-coverage@3.2.2: + resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} + engines: {node: '>=8'} + + istanbul-lib-instrument@5.2.1: + resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} + engines: {node: '>=8'} + + istanbul-lib-instrument@6.0.3: + resolution: {integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==} + engines: {node: '>=10'} + + istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} + + istanbul-lib-source-maps@4.0.1: + resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} + engines: {node: '>=10'} + + istanbul-reports@3.2.0: + resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} + engines: {node: '>=8'} + + iterator.prototype@1.1.5: + resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} + engines: {node: '>= 0.4'} + + jest-changed-files@29.7.0: + resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-circus@29.7.0: + resolution: {integrity: sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-cli@29.7.0: + resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + jest-config@29.7.0: + resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@types/node': '*' + ts-node: '>=9.0.0' + peerDependenciesMeta: + '@types/node': + optional: true + ts-node: + optional: true + + jest-diff@29.7.0: + resolution: {integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-diff@30.4.1: + resolution: {integrity: sha512-CRpFK0RtLriVDGcPPAnR6HMVI8bSR2jnUIgralhauzYQZIb4RH9AtEInTuQr65LmmGggGcRT6HIASxwqsVsmlA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-docblock@29.7.0: + resolution: {integrity: sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-each@29.7.0: + resolution: {integrity: sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-environment-jsdom@29.7.0: + resolution: {integrity: sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + canvas: ^2.5.0 + peerDependenciesMeta: + canvas: + optional: true + + jest-environment-node@29.7.0: + resolution: {integrity: sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-expo@57.0.4: + resolution: {integrity: sha512-EpTc8zizYWepKXHdHYuYBdEP0mZLjZfB1ldb2dyLKHug0HYFGuBnQ3Qp3gmBWCj3rSRaJYP90Lq/qPozzx2Khg==} + hasBin: true + peerDependencies: + '@react-native/jest-preset': ^0.86.2 + expo: '*' + react-native: '*' + react-server-dom-webpack: ~19.0.4 || ~19.1.5 || ~19.2.4 + peerDependenciesMeta: + expo: + optional: true + react-server-dom-webpack: + optional: true + + jest-get-type@29.6.3: + resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-haste-map@29.7.0: + resolution: {integrity: sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-leak-detector@29.7.0: + resolution: {integrity: sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-matcher-utils@29.7.0: + resolution: {integrity: sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-matcher-utils@30.4.1: + resolution: {integrity: sha512-zvYfX5CaeEkFrrLS9suWe9rvJrm9J1Iv3ua8kIBv9GEPzcnsfBf0bob37la7s67fs0nlBC3EuvkOLnXQKxtx4A==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-message-util@29.7.0: + resolution: {integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-mock@29.7.0: + resolution: {integrity: sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-pnp-resolver@1.2.3: + resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} + engines: {node: '>=6'} + peerDependencies: + jest-resolve: '*' + peerDependenciesMeta: + jest-resolve: + optional: true + + jest-regex-util@29.6.3: + resolution: {integrity: sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-resolve-dependencies@29.7.0: + resolution: {integrity: sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-resolve@29.7.0: + resolution: {integrity: sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-runner@29.7.0: + resolution: {integrity: sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-runtime@29.7.0: + resolution: {integrity: sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-snapshot@29.7.0: + resolution: {integrity: sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-util@29.7.0: + resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-validate@29.7.0: + resolution: {integrity: sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-watch-select-projects@2.0.0: + resolution: {integrity: sha512-j00nW4dXc2NiCW6znXgFLF9g8PJ0zP25cpQ1xRro/HU2GBfZQFZD0SoXnAlaoKkIY4MlfTMkKGbNXFpvCdjl1w==} + + jest-watch-typeahead@2.2.1: + resolution: {integrity: sha512-jYpYmUnTzysmVnwq49TAxlmtOAwp8QIqvZyoofQFn8fiWhEDZj33ZXzg3JA4nGnzWFm1hbWf3ADpteUokvXgFA==} + engines: {node: ^14.17.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + jest: ^27.0.0 || ^28.0.0 || ^29.0.0 + + jest-watcher@29.7.0: + resolution: {integrity: sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-worker@29.7.0: + resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest@29.7.0: + resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + jimp-compact@0.16.1: + resolution: {integrity: sha512-dZ6Ra7u1G8c4Letq/B5EzAxj4tLFHL+cGtdpR+PVm4yzPDj+lCk+AbivWt1eOM+ikzkowtyV7qSqX6qr3t71Ww==} + + js-levenshtein@1.1.6: + resolution: {integrity: sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==} + engines: {node: '>=0.10.0'} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@3.15.1: + resolution: {integrity: sha512-S99WuO3HlhO3XN41EtYUNl9zzXjoJx7QvmipxsJVxtCBT0YHEFy+iOJhjSvrmV12nYhWpZaM8lPHkJm0yUMbag==} + hasBin: true + + js-yaml@4.3.1: + resolution: {integrity: sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==} + hasBin: true + + jsc-safe-url@0.2.4: + resolution: {integrity: sha512-0wM3YBWtYePOjfyXQH5MWQ8H7sdk5EXSwZvmSLKk2RboVQ2Bu239jycHDz5J/8Blf3K0Qnoy2b6xD+z10MFB+Q==} + + jsdom@20.0.3: + resolution: {integrity: sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==} + engines: {node: '>=14'} + peerDependencies: + canvas: ^2.5.0 + peerDependenciesMeta: + canvas: + optional: true + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json5@1.0.2: + resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} + hasBin: true + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsx-ast-utils@3.3.5: + resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} + engines: {node: '>=4.0'} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + kleur@3.0.3: + resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} + engines: {node: '>=6'} + + lan-network@0.2.1: + resolution: {integrity: sha512-ONPnazC96VKDntab9j9JKwIWhZ4ZUceB4A9Epu4Ssg0hYFmtHZSeQ+n15nIwTFmcBUKtExOer8WTJ4GF9MO64A==} + hasBin: true + + leven@3.1.0: + resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} + engines: {node: '>=6'} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + lighthouse-logger@1.4.2: + resolution: {integrity: sha512-gPWxznF6TKmUHrOQjlVo2UbaL2EJ71mb2CCeRs/2qBpi4L/g4LUVc9+3lKQ6DTUZwJswfM7ainGrLO1+fOqa2g==} + + lightningcss-android-arm64@1.33.0: + resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.33.0: + resolution: {integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.33.0: + resolution: {integrity: sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.33.0: + resolution: {integrity: sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.33.0: + resolution: {integrity: sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.33.0: + resolution: {integrity: sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-musl@1.33.0: + resolution: {integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-gnu@1.33.0: + resolution: {integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + lightningcss-linux-x64-musl@1.33.0: + resolution: {integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-win32-arm64-msvc@1.33.0: + resolution: {integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.33.0: + resolution: {integrity: sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.33.0: + resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==} + engines: {node: '>= 12.0.0'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.debounce@4.0.8: + resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + lodash.throttle@4.1.1: + resolution: {integrity: sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==} + + lodash@4.18.1: + resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} + + log-symbols@2.2.0: + resolution: {integrity: sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==} + engines: {node: '>=4'} + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + + lru-cache@11.5.2: + resolution: {integrity: sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==} + engines: {node: 20 || >=22} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + + makeerror@1.0.12: + resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} + + marky@1.3.0: + resolution: {integrity: sha512-ocnPZQLNpvbedwTy9kNrQEsknEfgvcLMvOtz3sFeWApDq1MXH1TqkCIx58xlpESsfwQOnuBO9beyQuNGzVvuhQ==} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + memoize-one@5.2.1: + resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==} + + memoize-one@6.0.0: + resolution: {integrity: sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==} + + merge-options@3.0.4: + resolution: {integrity: sha512-2Sug1+knBjkaMsMgf1ctR1Ujx+Ayku4EdJN4Z+C2+JzoeF7A3OZ9KM2GY0CpQS51NR61LTurMJrRKPhSs3ZRTQ==} + engines: {node: '>=10'} + + merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + + metro-babel-transformer@0.84.4: + resolution: {integrity: sha512-rvCfz8snl9h20VcvpOHxZuHP1SlAkv4HXbzw7nyyVwu6Eqo5PRerbakQ9XmUCOsRy70spJ37O+G1TK8oMzo48g==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-babel-transformer@0.84.5: + resolution: {integrity: sha512-2WbHILKMiJUzfdjmGOQOqU1bWi9//gqiclc/tkk/AIsrrVw3efhZ1uhkOwMTxUEPOzqoo091H0olLmVZH5FHGQ==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-cache-key@0.84.4: + resolution: {integrity: sha512-wVO79aGrkYImpnaVS4+d5RrRBRPX31QtvKB3wKGBuiNSznduZTQHzsrJZRroFJSwnygrzdsGUtDQPuqqFjFdvw==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-cache-key@0.84.5: + resolution: {integrity: sha512-3dPB2TnvGjjf0/9O7AXVQURKXuQNauTZE7WpTGTlR017Gh/B5y0m/2wcqxfveUguHSpu89KhVxCAlr2k/H7uhQ==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-cache@0.84.4: + resolution: {integrity: sha512-gpcFQdSLUwUCk71saKoE64jLFbx2nwTfVCcPSULMNT8QYq0p1eZZE29Jvd0HtT/UlhC3ZOutLxJME5xqD2JUZg==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-cache@0.84.5: + resolution: {integrity: sha512-WHS0n2OxQqtwEjSeQFPePNrMvEFhmQcUQM9cRJMHByWoi/GMWFBEWOf7hVkAM/0KRutAXNbDlSu/cZB6CyxgQQ==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-config@0.84.4: + resolution: {integrity: sha512-PMotGDjXcXLWo2TMRH+VR99phFNgYTwqh4OoieIKK3yTJa1Jmkl+fZJxDO0jfBvNF+WESHciHvpNuBtXaF3B0Q==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-config@0.84.5: + resolution: {integrity: sha512-zie+uN6oohscowi2S7ByU+wUw6CrT4ZxW9uAbONOObSxx86RGmnIAmjXHLkfmcdYoY7jzOPEbqcI6oeVmqyBQA==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-core@0.84.4: + resolution: {integrity: sha512-HONpWC5LGXZn3ffkd4Hu6AIrfE7j4Z0g0wMo/goV24WOB3lhuFZ40KgvaDiSw8iyQHloMYay5N/wPX+z8oN/PQ==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-core@0.84.5: + resolution: {integrity: sha512-xwm605hCi5Y6eJTTb8ZWo6pkUcoBEIyiQOfkZh5GwtDwUrP9SNhTQZhzJHrBCwwxlf3Ptl/pxWJgQ1rsNYMnrA==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-file-map@0.84.4: + resolution: {integrity: sha512-KSVDi/u60hKPx++NLu3MTIvyjzNoJnFAF8PQFxaj1jiSka/wjw+Ua6sNuJ0TDHQv+7AAoFQxeMgaRAe8Yic5wQ==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-file-map@0.84.5: + resolution: {integrity: sha512-mlm/JL8toSbSc2akpKIGmzvrVRSCgZ5vkbycI34oMLoOnLGuLyC8WTyVJ6P0hZG/usDaGwZSl/s9BCRriqjGJA==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-minify-terser@0.84.4: + resolution: {integrity: sha512-5qpbaVOMC7CPitIpuewzVeGw7E+C3ykbv2mqTjQLl85Z3annSVGlSCTcsZjqXZzjupfK4Ztj3dDc4kc44NZwtQ==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-minify-terser@0.84.5: + resolution: {integrity: sha512-BJoFwCEDsYnagPqarayInv2+diCDNDdLlaof/p6s9w4gh+gc9HXYM+pDvsKGKKUumpZswNF3Z/ftTMqKl/5IBg==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-resolver@0.84.4: + resolution: {integrity: sha512-1qLgbxQ5ZGhhutuPot1Yp348ofDsATL2WkrHF65TobqTT9K3P9qJXw38bomk7ncp5B7OYMfWwtyBZo1lCV792A==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-resolver@0.84.5: + resolution: {integrity: sha512-VSSnepg1k6LyCwtb6eirWdAWlpKwBG8Rdtsr1mU38rMelFyWgh3/QuMSiZIZAIjwg/fsa8GhW5/FO54CAUPCEA==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-runtime@0.84.4: + resolution: {integrity: sha512-Jibypds4g7AhzdRKY+kDoj51s5EXMwgyp5ddtlreDAsWefMdOx+agWqgm0H2XSZ/ueanHHVM89fnf5OJnlxa8Q==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-runtime@0.84.5: + resolution: {integrity: sha512-U1m2+d1Pr+JO2/iVXBB2OfXXityz7tqwIorxfrT15IEgaHvpJBq/OHiqnOWPKJbUl3JcxjcdviZZOKk85oK4Qg==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-source-map@0.84.4: + resolution: {integrity: sha512-jbWkPxIesVuo1IWkvezmMJld6iu8nD62GsrZiV6jP37AOdbo4OBq1FJ+qkOg8sV05wAHB//jAbziuW0SlJfW4g==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-source-map@0.84.5: + resolution: {integrity: sha512-2BtV5L9uPc49F13Gn5wiP6bX/EncqzqTIk2VL/0F/96Vo0YEOjluT/qktQjFODfqGFsucwnh5mPEAl/2jVEfeg==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-symbolicate@0.84.4: + resolution: {integrity: sha512-OnfpacxUqGPZQ27t8qK9mFa7uqHIlVWeqRqkCbvMvreEBiamEeOn8krKtcwgP5M4cYDPwuSmCTopHMVthqG4zA==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + hasBin: true + + metro-symbolicate@0.84.5: + resolution: {integrity: sha512-rQ40zYDAkaWBN9yvjUuAD0ZpzBMZSoKyGYXnb5JrfbKjun7fTvfoLHL3KXFYenBTYZkQtlp4cKSCv/1utxFyOw==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + hasBin: true + + metro-transform-plugins@0.84.4: + resolution: {integrity: sha512-kehr6HbAecqD0/a3xLXobELdPaAmRAl8bel0qagPF4vhZtux93nS8S4eq2kgKt6J2GnQpVjSoW1PXdst04mwow==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-transform-plugins@0.84.5: + resolution: {integrity: sha512-+InaSVGaOyt0DyRo4Y/zIdPI6CZwnbNho5LAL23tgmuGwv7fyfkF7kKfPjZcfxXBcoYdTLLFnCfCH/dHSiCqNg==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-transform-worker@0.84.4: + resolution: {integrity: sha512-W1IYMvvXTu4MxYr7d9h7CeG2vpIr3bmLLIavkPY4O1ilzDrvS8z/NEe6y+pC44Ff7raMXQgYSfdqDUwN/i39gg==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-transform-worker@0.84.5: + resolution: {integrity: sha512-ui1Z8x4s5RL36gMmKLaMMO7O9NNDHNdthEZSCDQHAau3JcAsTaFOK6I+2q4I/kW5u8hSEjJk9L45TXSVJw6g1A==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro@0.84.4: + resolution: {integrity: sha512-8ETTubqfD6ornDy2zYDvRcKnVDOXdFJsjetYDBsY4oAsb6NJkiwFR+FaMESyGppFmQUyBQA4H4sFGxzcQSGtFA==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + hasBin: true + + metro@0.84.5: + resolution: {integrity: sha512-r1liLkyFZMVSEMNjU1CJU5pRzs3NdkxHqXS60O25c0rCIqAR+cGk7rPydw/g0WAIKVXojIBIF45yYBPagJGcgw==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + hasBin: true + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-db@1.54.0: + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mime-types@3.0.2: + resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==} + engines: {node: '>=18'} + + mime@1.6.0: + resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} + engines: {node: '>=4'} + hasBin: true + + mimic-fn@1.2.0: + resolution: {integrity: sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==} + engines: {node: '>=4'} + + mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + + min-indent@1.0.1: + resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} + engines: {node: '>=4'} + + minimatch@10.2.6: + resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==} + engines: {node: 18 || 20 || >=22} + + minimatch@3.1.5: + resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} + + minimatch@5.1.9: + resolution: {integrity: sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==} + engines: {node: '>=10'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + minipass@7.1.3: + resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} + engines: {node: '>=16 || 14 >=14.17'} + + mkdirp@1.0.4: + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} + hasBin: true + + ms@2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + multitars@1.0.2: + resolution: {integrity: sha512-6GwVw5eLi9sThdtlS4PKwC7yRLaf45pYhIEzKBHdKxi+YOXGKFX8acIniH+Uh/+k9mS2lQOupTccjoe5r0/1IQ==} + + nanoid@3.3.18: + resolution: {integrity: sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + napi-postinstall@0.3.4: + resolution: {integrity: sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + negotiator@0.6.3: + resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} + engines: {node: '>= 0.6'} + + negotiator@0.6.4: + resolution: {integrity: sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==} + engines: {node: '>= 0.6'} + + negotiator@1.0.0: + resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} + engines: {node: '>= 0.6'} + + node-exports-info@1.6.2: + resolution: {integrity: sha512-kXs9Go0cah0qHVV2v389IXQLdLCeE1xfFtjOAF+iobu0OIoG1pje8At2vMHyaPMiPMnG/LWP50twML21eMcAag==} + engines: {node: '>= 0.4'} + + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + + node-forge@1.4.0: + resolution: {integrity: sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==} + engines: {node: '>= 6.13.0'} + + node-int64@0.4.0: + resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} + + node-releases@2.0.53: + resolution: {integrity: sha512-D9UOmYG3UH1V+ENW56t5QXBwJw1YEY18ruVeus89Rw+SyIgjPkCO84bRzO3uNIYosJbNwiabWVn48o3uJLjxFQ==} + engines: {node: '>=18'} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + npm-package-arg@11.0.3: + resolution: {integrity: sha512-sHGJy8sOC1YraBywpzQlIKBE4pBbGbiF95U6Auspzyem956E0+FtDtsx1ZxlOJkQCZ1AFXAY/yuvtFYrOxF+Bw==} + engines: {node: ^16.14.0 || >=18.0.0} + + npm-run-path@4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} + + nullthrows@1.1.1: + resolution: {integrity: sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==} + + nwsapi@2.2.24: + resolution: {integrity: sha512-7YRhZ3jS45LwmSCT4b2sVFHt/WuovaktDU07QrtOBY2PXskss5a9jfmR9jptyumwXST+rFjrmppMY1KT/yn35A==} + + ob1@0.84.4: + resolution: {integrity: sha512-eJXMpz4aQHXF/YBB9ddqZDIS+ooO91hObo9FoW/xBkr54/zCwYYCDqT/O54vNo8kOkWs5Ou/y28NgdrV0edQNA==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + ob1@0.84.5: + resolution: {integrity: sha512-aH9RkoZc7w/90HBamFxTw8ZLFr05wXS+iOnvmrgo53Ep8Pyrm5FieQSaPIVROkfFVQISeD/zo92fes26TOwe+A==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + + object.assign@4.1.7: + resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} + engines: {node: '>= 0.4'} + + object.entries@1.1.9: + resolution: {integrity: sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==} + engines: {node: '>= 0.4'} + + object.fromentries@2.0.8: + resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} + engines: {node: '>= 0.4'} + + object.groupby@1.0.3: + resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} + engines: {node: '>= 0.4'} + + object.values@1.2.1: + resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} + engines: {node: '>= 0.4'} + + on-finished@2.3.0: + resolution: {integrity: sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==} + engines: {node: '>= 0.8'} + + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + + on-headers@1.1.0: + resolution: {integrity: sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==} + engines: {node: '>= 0.8'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + onetime@2.0.1: + resolution: {integrity: sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==} + engines: {node: '>=4'} + + onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + + open@7.4.2: + resolution: {integrity: sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==} + engines: {node: '>=8'} + + openapi-fetch@0.17.0: + resolution: {integrity: sha512-PsbZR1wAPcG91eEthKhN+Zn92FMHxv+/faECIwjXdxfTODGSGegYv0sc1Olz+HYPvKOuoXfp+0pA2XVt2cI0Ig==} + + openapi-typescript-helpers@0.1.0: + resolution: {integrity: sha512-OKTGPthhivLw/fHz6c3OPtg72vi86qaMlqbJuVJ23qOvQ+53uw1n7HdmkJFibloF7QEjDrDkzJiOJuockM/ljw==} + + openapi-typescript@7.13.0: + resolution: {integrity: sha512-EFP392gcqXS7ntPvbhBzbF8TyBA+baIYEm791Hy5YkjDYKTnk/Tn5OQeKm5BIZvJihpp8Zzr4hzx0Irde1LNGQ==} + hasBin: true + peerDependencies: + typescript: ^5.x + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + ora@3.4.0: + resolution: {integrity: sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg==} + engines: {node: '>=6'} + + own-keys@1.0.2: + resolution: {integrity: sha512-19YVAg7T+WTrxggPukVq7DjTv6+PJ867TmhCvBsYwmbFCsZd344rq2Ld1p0wo8f8Qrrhgp82c6FJRqdXWtSEhg==} + engines: {node: '>= 0.4'} + + p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + + parse-json@8.3.0: + resolution: {integrity: sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==} + engines: {node: '>=18'} + + parse-png@2.1.0: + resolution: {integrity: sha512-Nt/a5SfCLiTnQAjx3fHlqp8hRgTL3z7kTQZzvIMS9uCAepnCyjpdEc6M/sz69WqMBdaDBw9sF1F1UaHROYzGkQ==} + engines: {node: '>=10'} + + parse5@7.3.0: + resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} + + parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + + partysocket@1.3.0: + resolution: {integrity: sha512-1zToNyolZFK/7nuAw/K2bZrNzFqaZyRoCEkS+9vG6WSC5ikrN6qWRe96q6ImU51uptz2r+dAwSkwhJVdQi4LiA==} + peerDependencies: + react: '>=17' + peerDependenciesMeta: + react: + optional: true + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-scurry@2.0.2: + resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} + engines: {node: 18 || 20 || >=22} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.2: + resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} + engines: {node: '>=8.6'} + + picomatch@4.0.5: + resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==} + engines: {node: '>=12'} + + pirates@4.0.7: + resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} + engines: {node: '>= 6'} + + pkg-dir@4.2.0: + resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} + engines: {node: '>=8'} + + plist@3.1.1: + resolution: {integrity: sha512-ZIfcLJC+7E7FBFnDxm9MPmt7D+DidyQ26lewieO75AdhA2ayMtsJSES0iWzqJQbcVRSrTufQoy0DR94xHue0oA==} + engines: {node: '>=10.4.0'} + + pluralize@8.0.0: + resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} + engines: {node: '>=4'} + + pngjs@3.4.0: + resolution: {integrity: sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==} + engines: {node: '>=4.0.0'} + + possible-typed-array-names@1.1.0: + resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} + engines: {node: '>= 0.4'} + + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + + postcss@8.5.26: + resolution: {integrity: sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==} + engines: {node: ^10 || ^12 || >=14} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prettier@3.9.6: + resolution: {integrity: sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==} + engines: {node: '>=14'} + hasBin: true + + pretty-format@29.7.0: + resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + pretty-format@30.4.1: + resolution: {integrity: sha512-K6KiKMHTL4jjX4u3Kir2EW07nRfcqVTXIImx50wbjHQTcZPgg+gjVeNTIT3l3L1Rd4UefxfogquC9J37SoFyyw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + proc-log@4.2.0: + resolution: {integrity: sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + progress@2.0.3: + resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} + engines: {node: '>=0.4.0'} + + promise@7.3.1: + resolution: {integrity: sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==} + + promise@8.3.0: + resolution: {integrity: sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==} + + prompts@2.4.2: + resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} + engines: {node: '>= 6'} + + prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + + psl@1.15.0: + resolution: {integrity: sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + pure-rand@6.1.0: + resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} + + query-string@7.1.3: + resolution: {integrity: sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==} + engines: {node: '>=6'} + + querystringify@2.2.0: + resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} + + queue@6.0.2: + resolution: {integrity: sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==} + + range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + + react-devtools-core@6.1.5: + resolution: {integrity: sha512-ePrwPfxAnB+7hgnEr8vpKxL9cmnp7F322t8oqcPshbIQQhDKgFDW4tjhF2wjVbdXF9O/nyuy3sQWd9JGpiLPvA==} + + react-dom@19.2.3: + resolution: {integrity: sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==} + peerDependencies: + react: ^19.2.3 + + react-fast-compare@3.2.2: + resolution: {integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==} + + react-freeze@1.0.4: + resolution: {integrity: sha512-r4F0Sec0BLxWicc7HEyo2x3/2icUTrRmDjaaRyzzn+7aDyFZliszMDOgLVwSnQnYENOlL1o569Ze2HZefk8clA==} + engines: {node: '>=10'} + peerDependencies: + react: '>=17.0.0' + + react-i18next@17.0.11: + resolution: {integrity: sha512-cDtkXgxjuFTWUH6V+aQn1Ve5vDiUztCNPWW5GtSHDccsgRXO1nE6QFWCEmc1KAutrb3OUv87wFShJL5RhUwPXg==} + peerDependencies: + i18next: '>= 26.2.0' + react: '>= 16.8.0' + react-dom: '*' + react-native: '*' + typescript: ^5 || ^6 || ^7 + peerDependenciesMeta: + react-dom: + optional: true + react-native: + optional: true + typescript: + optional: true + + react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + + react-is@18.3.1: + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + + react-is@19.2.8: + resolution: {integrity: sha512-s5un28nYxKJw5gvUHyW5PCC28CvBqLu9r3cWgzHT4Vo/5fqqkFcdRYsGcKf50WMPpjjFZS5d76fn3YCo2njKwQ==} + + react-native-drawer-layout@4.2.10: + resolution: {integrity: sha512-O6TQdZ5LSm3dqnuR4rX9KPtE+9dVg7jsezEYz1l01rkQTe4fQvYZIoPu2sZFl5X2N70uhRdjnPULySVR3sBUwA==} + peerDependencies: + react: '>= 18.2.0' + react-native: '*' + react-native-gesture-handler: '>= 2.0.0' + react-native-reanimated: '>= 2.0.0' + + react-native-gesture-handler@2.32.0: + resolution: {integrity: sha512-uYIMOKlKENORq2SABE+jIjbPU+h5I/sQKcq2v16zRq848nwEp1fWRVwML4QWqijc8UcXJC25o54S8GQd4Mf2OA==} + peerDependencies: + react: '*' + react-native: '*' + + react-native-is-edge-to-edge@1.3.1: + resolution: {integrity: sha512-NIXU/iT5+ORyCc7p0z2nnlkouYKX425vuU1OEm6bMMtWWR9yvb+Xg5AZmImTKoF9abxCPqrKC3rOZsKzUYgYZA==} + peerDependencies: + react: '*' + react-native: '*' + + react-native-reanimated@4.5.1: + resolution: {integrity: sha512-RnMvtDuR+68ig864gAvZCOdZehqhC5rFmMo0kn+ARfgVSTvFeF6IFLBVgMPUu0KwihaapEyW24WRi6nEyy1kSA==} + peerDependencies: + react: '*' + react-native: 0.83 - 0.86 + react-native-worklets: 0.10.x + + react-native-safe-area-context@5.7.0: + resolution: {integrity: sha512-/9/MtQz8ODphjsLdZ+GZAIcC/RtoqW9EeShf7Uvnfgm/pzYrJ75y3PV/J1wuAV1T5Dye5ygq4EAW20RoBq0ABQ==} + peerDependencies: + react: '*' + react-native: '*' + + react-native-screens@4.26.2: + resolution: {integrity: sha512-2XnWsZToKj76trGtEZzx5ELD/qOICFEprEeUntImmitQFVUkea27fiWdUSITArI356Y1qynpXZINW+Unbhky/A==} + peerDependencies: + react: '*' + react-native: '*' + + react-native-web@0.21.2: + resolution: {integrity: sha512-SO2t9/17zM4iEnFvlu2DA9jqNbzNhoUP+AItkoCOyFmDMOhUnBBznBDCYN92fGdfAkfQlWzPoez6+zLxFNsZEg==} + peerDependencies: + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + + react-native-worklets@0.10.1: + resolution: {integrity: sha512-62mRM19bDpfpdI8HLkEErcdOsrAPDtE9lA/sw+5lLRpzBHNhxaoj9QyY2KjXqUmirelxkX4zuPGTC3VdA0feJA==} + peerDependencies: + '@babel/core': '*' + '@react-native/metro-config': '*' + react: '*' + react-native: 0.83 - 0.86 + + react-native@0.86.2: + resolution: {integrity: sha512-zbJXGZpwfZGA79Z9ob6Atvfx4nAQL8yJBa35s58E4Oo+khPykfQP2sTeumkKbjwajFYfVayg8pj7Il9nIfTk7A==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + hasBin: true + peerDependencies: + '@react-native/jest-preset': 0.86.2 + '@types/react': ^19.1.1 + react: ^19.2.3 + peerDependenciesMeta: + '@react-native/jest-preset': + optional: true + '@types/react': + optional: true + + react-reconciler@0.33.0: + resolution: {integrity: sha512-KetWRytFv1epdpJc3J4G75I4WrplZE5jOL7Yq0p34+OVOKF4Se7WrdIdVC45XsSSmUTlht2FM/fM1FZb1mfQeA==} + engines: {node: '>=0.10.0'} + peerDependencies: + react: ^19.2.0 + + react-refresh@0.14.2: + resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} + engines: {node: '>=0.10.0'} + + react-remove-scroll-bar@2.3.8: + resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + react-remove-scroll@2.7.2: + resolution: {integrity: sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + react-style-singleton@2.2.3: + resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + react-test-renderer@19.2.3: + resolution: {integrity: sha512-TMR1LnSFiWZMJkCgNf5ATSvAheTT2NvKIwiVwdBPHxjBI7n/JbWd4gaZ16DVd9foAXdvDz+sB5yxZTwMjPRxpw==} + peerDependencies: + react: ^19.2.3 + + react@19.2.3: + resolution: {integrity: sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==} + engines: {node: '>=0.10.0'} + + redent@3.0.0: + resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} + engines: {node: '>=8'} + + reflect.getprototypeof@1.0.10: + resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} + engines: {node: '>= 0.4'} + + regenerate-unicode-properties@10.2.2: + resolution: {integrity: sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==} + engines: {node: '>=4'} + + regenerate@1.4.2: + resolution: {integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==} + + regenerator-runtime@0.13.11: + resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==} + + regexp.prototype.flags@1.5.4: + resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} + engines: {node: '>= 0.4'} + + regexpu-core@6.4.0: + resolution: {integrity: sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==} + engines: {node: '>=4'} + + regjsgen@0.8.0: + resolution: {integrity: sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==} + + regjsparser@0.13.2: + resolution: {integrity: sha512-NgRBy2Nx/bE+9F27nVHnqcN5HjyLmecqsqx2PJHu3/IEtADD4WuxuXIVExD5PoSDFVrl78dOonfcOe5O+5nbzQ==} + hasBin: true + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + requires-port@1.0.0: + resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + + resolve-cwd@3.0.0: + resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} + engines: {node: '>=8'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + resolve-workspace-root@2.0.1: + resolution: {integrity: sha512-nR23LHAvaI6aHtMg6RWoaHpdR4D881Nydkzi2CixINyg9T00KgaJdJI6Vwty+Ps8WLxZHuxsS0BseWjxSA4C+w==} + + resolve.exports@2.0.3: + resolution: {integrity: sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==} + engines: {node: '>=10'} + + resolve@1.22.12: + resolution: {integrity: sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==} + engines: {node: '>= 0.4'} + hasBin: true + + resolve@2.0.0-next.7: + resolution: {integrity: sha512-tqt+NBWwyaMgw3zDsnygx4CByWjQEJHOPMdslYhppaQSJUtL/D4JO9CcBBlhPoI8lz9oJIDXkwXfhF4aWqP8xQ==} + engines: {node: '>= 0.4'} + hasBin: true + + restore-cursor@2.0.0: + resolution: {integrity: sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==} + engines: {node: '>=4'} + + rtl-detect@1.1.2: + resolution: {integrity: sha512-PGMBq03+TTG/p/cRB7HCLKJ1MgDIi07+QU1faSjiYRfmY5UsAttV9Hs08jDAHVwcOwmVLcSJkpwyfXszVjWfIQ==} + + safe-array-concat@1.1.4: + resolution: {integrity: sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==} + engines: {node: '>=0.4'} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safe-push-apply@1.0.0: + resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} + engines: {node: '>= 0.4'} + + safe-regex-test@1.1.0: + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} + engines: {node: '>= 0.4'} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + sandbox-cli-detector@0.2.0: + resolution: {integrity: sha512-4lyHX0ZU0AZKwjgZ1InxZAa3PNpyEb8rOQ+Zss1ReYmhNzW0Q+h1zE5nvniXN0HaAWZaZE1zgVNEirb0R7LmNg==} + engines: {node: '>=18.18'} + hasBin: true + + sax@1.6.1: + resolution: {integrity: sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==} + engines: {node: '>=11.0.0'} + + saxes@6.0.0: + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} + engines: {node: '>=v12.22.7'} + + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} + engines: {node: '>=10'} + hasBin: true + + send@0.19.2: + resolution: {integrity: sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==} + engines: {node: '>= 0.8.0'} + + serialize-error@2.1.0: + resolution: {integrity: sha512-ghgmKt5o4Tly5yEG/UJp8qTd0AN7Xalw4XBtDEKP655B699qMEtra1WlXeE6WIvdEG481JvRxULKsInq/iNysw==} + engines: {node: '>=0.10.0'} + + serve-static@1.16.3: + resolution: {integrity: sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==} + engines: {node: '>= 0.8.0'} + + server-only@0.0.1: + resolution: {integrity: sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==} + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + set-function-name@2.0.2: + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} + + set-proto@1.0.0: + resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} + engines: {node: '>= 0.4'} + + setimmediate@1.0.5: + resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} + + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + + sf-symbols-typescript@2.2.0: + resolution: {integrity: sha512-TPbeg0b7ylrswdGCji8FRGFAKuqbpQlLbL8SOle3j1iHSs5Ob5mhvMAxWN2UItOjgALAB5Zp3fmMfj8mbWvXKw==} + engines: {node: '>=10'} + + shallowequal@1.1.0: + resolution: {integrity: sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + shell-quote@1.10.0: + resolution: {integrity: sha512-w1aiOKwKuRgtwAReIIj89puqg+I7GvX4IbLrvmhXbzQsj1+Zwi4VO3+fa6ZF91TWSjIxoEkKnMeHcLEODK5ZXA==} + engines: {node: '>= 0.4'} + + side-channel-list@1.0.1: + resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.1: + resolution: {integrity: sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==} + engines: {node: '>= 0.4'} + + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + simple-plist@1.3.1: + resolution: {integrity: sha512-iMSw5i0XseMnrhtIzRb7XpQEXepa9xhWxGUojHBL43SIpQuDQkh3Wpy67ZbDzZVr6EKxvwVChnVpdl8hEVLDiw==} + + simple-swizzle@0.2.4: + resolution: {integrity: sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==} + + sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + + slash@5.1.0: + resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} + engines: {node: '>=14.16'} + + slugify@1.6.9: + resolution: {integrity: sha512-vZ7rfeehZui7wQs438JXBckYLkIIdfHOXsaVEUMyS5fHo1483l1bMdo0EDSWYclY0yZKFOipDy4KHuKs6ssvdg==} + engines: {node: '>=8.0.0'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + source-map-support@0.5.13: + resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} + + source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + + source-map@0.5.6: + resolution: {integrity: sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA==} + engines: {node: '>=0.10.0'} + + source-map@0.5.7: + resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==} + engines: {node: '>=0.10.0'} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + split-on-first@1.1.0: + resolution: {integrity: sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==} + engines: {node: '>=6'} + + sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + + stable-hash@0.0.5: + resolution: {integrity: sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==} + + stack-generator@2.0.10: + resolution: {integrity: sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ==} + + stack-utils@2.0.6: + resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} + engines: {node: '>=10'} + + stackframe@1.3.4: + resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==} + + stacktrace-gps@3.1.2: + resolution: {integrity: sha512-GcUgbO4Jsqqg6RxfyTHFiPxdPqF+3LFmQhm7MgCuYQOYuWyqxo5pwRPz5d/u6/WYJdEnWfK4r+jGbyD8TSggXQ==} + + stacktrace-js@2.0.2: + resolution: {integrity: sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg==} + + stacktrace-parser@0.1.11: + resolution: {integrity: sha512-WjlahMgHmCJpqzU8bIBy4qtsZdU9lRlcZE3Lvyej6t4tuOuv1vk57OW3MBrj6hXBFx/nNoC9MPMTcr5YA7NQbg==} + engines: {node: '>=6'} + + standard-navigation@0.0.5: + resolution: {integrity: sha512-YAmzwAiiQVocZxO/VGPFiQHcu5pKiz09QIGC0MK6aRMoa3E0QkoTQgcqJr7ZZ3OMiNhu4DkaGElFI5htjOIDbw==} + + statuses@1.5.0: + resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==} + engines: {node: '>= 0.6'} + + statuses@2.0.2: + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} + engines: {node: '>= 0.8'} + + stop-iteration-iterator@1.1.0: + resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} + engines: {node: '>= 0.4'} + + stream-buffers@2.2.0: + resolution: {integrity: sha512-uyQK/mx5QjHun80FLJTfaWE7JtwfRMKBLkMne6udYOmvH0CawotVa7TfgYHzAnpphn4+TweIx1QKMnRIbipmUg==} + engines: {node: '>= 0.10.0'} + + strict-uri-encode@2.0.0: + resolution: {integrity: sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==} + engines: {node: '>=4'} + + string-length@4.0.2: + resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} + engines: {node: '>=10'} + + string-length@5.0.1: + resolution: {integrity: sha512-9Ep08KAMUn0OadnVaBuRdE2l615CQ508kr0XMadjClfYpdCyvrbFp6Taebo8yyxokQ4viUd/xPPUA4FGgUa0ow==} + engines: {node: '>=12.20'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string.prototype.matchall@4.0.12: + resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==} + engines: {node: '>= 0.4'} + + string.prototype.repeat@1.0.0: + resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==} + + string.prototype.trim@1.2.11: + resolution: {integrity: sha512-PwvK7BU+CMTJGYQCTZb5RWXIML92lftJLhQz1tBzgKiqGxJaMlBAa48POXaNAC2s4y8jr3EFqrkF9+44neS46w==} + engines: {node: '>= 0.4'} + + string.prototype.trimend@1.0.10: + resolution: {integrity: sha512-2+3aDAOmPTmuFwjDnmJG2ctEkQKVki7vOSqaxkv42Mowj1V6PnvuwFCRrR5lChUux1TBskPjfkeTOhqczDMxTw==} + engines: {node: '>= 0.4'} + + string.prototype.trimstart@1.0.8: + resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} + engines: {node: '>= 0.4'} + + strip-ansi@5.2.0: + resolution: {integrity: sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==} + engines: {node: '>=6'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.2.0: + resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} + engines: {node: '>=12'} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + strip-bom@4.0.0: + resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} + engines: {node: '>=8'} + + strip-final-newline@2.0.0: + resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} + engines: {node: '>=6'} + + strip-indent@3.0.0: + resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} + engines: {node: '>=8'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + structured-headers@0.4.1: + resolution: {integrity: sha512-0MP/Cxx5SzeeZ10p/bZI0S6MpgD+yxAhi1BOQ34jgnMXsCq3j1t6tQnZu+KdlL7dvJTLT3g9xN8tl10TqgFMcg==} + + styleq@0.1.3: + resolution: {integrity: sha512-3ZUifmCDCQanjeej1f6kyl/BeP/Vae5EYkQ9iJfUm/QwZvlgnZzyflqAsAWYURdtea8Vkvswu2GrC57h3qffcA==} + + supports-color@10.2.2: + resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==} + engines: {node: '>=18'} + + supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + + supports-hyperlinks@2.3.0: + resolution: {integrity: sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==} + engines: {node: '>=8'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + symbol-tree@3.2.4: + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + + tagged-tag@1.0.0: + resolution: {integrity: sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==} + engines: {node: '>=20'} + + terminal-link@2.1.1: + resolution: {integrity: sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==} + engines: {node: '>=8'} + + terser@5.50.0: + resolution: {integrity: sha512-CN9BVxWhgS/hRxtUMjtC2uRWSTcSfQFHMDWma6sKKfIivCD91sM+FOPfvwoaRMqCSrUpe1nv3jDamd9eEQ4y+w==} + engines: {node: '>=10'} + hasBin: true + + test-exclude@6.0.0: + resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} + engines: {node: '>=8'} + + test-renderer@1.2.0: + resolution: {integrity: sha512-JYiEGbgBGtmHAWX8Kf99gGRL1HtjcRVHLrmJNTZL4vFs9XrnWcuL45Iszw/pO4094+JR7havSrN+ds6YTOpSQA==} + peerDependencies: + react: ^19.0.0 + + throat@5.0.0: + resolution: {integrity: sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==} + + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} + engines: {node: '>=12.0.0'} + + tmpl@1.0.5: + resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + + toqr@0.1.1: + resolution: {integrity: sha512-FWAPzCIHZHnrE/5/w9MPk0kK25hSQSH2IKhYh9PyjS3SG/+IEMvlwIHbhz+oF7xl54I+ueZlVnMjyzdSwLmAwA==} + + tough-cookie@4.1.4: + resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==} + engines: {node: '>=6'} + + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + + tr46@3.0.0: + resolution: {integrity: sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==} + engines: {node: '>=12'} + + ts-api-utils@2.5.0: + resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + tsconfig-paths@3.15.0: + resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + tsx@4.23.12: + resolution: {integrity: sha512-FDf4L4sYzKtzWYhU/Xm0AQFdTjdIxNo9ElTf2mxXM6k8YMHXzYUe4yODVaXP4V9uMFbVg8c0qyBccK2OOxb45Q==} + engines: {node: '>=18.0.0'} + hasBin: true + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + type-detect@4.0.8: + resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} + engines: {node: '>=4'} + + type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + + type-fest@0.7.1: + resolution: {integrity: sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==} + engines: {node: '>=8'} + + type-fest@4.41.0: + resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} + engines: {node: '>=16'} + + type-fest@5.8.0: + resolution: {integrity: sha512-YGYEVz3Fm5iy/AybuA0oyNFq7H4CgQNfRp/qfe8nurE1kuCeNm3/vfm9X4Mtl+qLyaKJUh5xrFZwogr41SMjYA==} + engines: {node: '>=20'} + + typed-array-buffer@1.0.3: + resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} + engines: {node: '>= 0.4'} + + typed-array-byte-length@1.0.3: + resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} + engines: {node: '>= 0.4'} + + typed-array-byte-offset@1.0.4: + resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} + engines: {node: '>= 0.4'} + + typed-array-length@1.0.8: + resolution: {integrity: sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g==} + engines: {node: '>= 0.4'} + + typescript@6.0.3: + resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} + engines: {node: '>=14.17'} + hasBin: true + + ua-parser-js@0.7.41: + resolution: {integrity: sha512-O3oYyCMPYgNNHuO7Jjk3uacJWZF8loBgwrfd/5LE/HyZ3lUIOdniQ7DNXJcIgZbwioZxk0fLfI4EVnetdiX5jg==} + hasBin: true + + ua-parser-js@1.0.41: + resolution: {integrity: sha512-LbBDqdIC5s8iROCUjMbW1f5dJQTEFB1+KO9ogbvlb3nm9n4YHa5p4KTvFPWvh2Hs8gZMBuiB1/8+pdfe/tDPug==} + hasBin: true + + unbox-primitive@1.1.0: + resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} + engines: {node: '>= 0.4'} + + undici-types@8.3.0: + resolution: {integrity: sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==} + + unicode-canonical-property-names-ecmascript@2.0.1: + resolution: {integrity: sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==} + engines: {node: '>=4'} + + unicode-match-property-ecmascript@2.0.0: + resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==} + engines: {node: '>=4'} + + unicode-match-property-value-ecmascript@2.2.1: + resolution: {integrity: sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==} + engines: {node: '>=4'} + + unicode-property-aliases-ecmascript@2.2.0: + resolution: {integrity: sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==} + engines: {node: '>=4'} + + universalify@0.2.0: + resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} + engines: {node: '>= 4.0.0'} + + unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + + unrs-resolver@1.12.2: + resolution: {integrity: sha512-dmlRxBJJayXjqTwC+JtF1HhJmgf3ftQ3YejFcZrf4+KKtJv0qDsK1pjqaaVjG7wJ5NJ6UVP1OqRMQ71Z4C3rxQ==} + + update-browserslist-db@1.3.1: + resolution: {integrity: sha512-ZZ61DsRsOnakl74HAmp3oSN4aXUmEWXf+i/yv0h7tIBfICc3VdrFErQKUUKPgu3AMsTUMbcongALEN4l6GSUrQ==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js-replace@1.0.1: + resolution: {integrity: sha512-W+C9NWNLFOoBI2QWDp4UT9pv65r2w5Cx+3sTYFvtMdDBxkKt1syCqsUdSFAChbEe1uK5TfS04wt/nGwmaeIQ0g==} + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + url-parse@1.5.10: + resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} + + use-callback-ref@1.3.3: + resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + use-latest-callback@0.2.6: + resolution: {integrity: sha512-FvRG9i1HSo0wagmX63Vrm8SnlUU3LMM3WyZkQ76RnslpBrX694AdG4A0zQBx2B3ZifFA0yv/BaEHGBnEax5rZg==} + peerDependencies: + react: '>=16.8' + + use-sidecar@1.1.3: + resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + use-sync-external-store@1.6.0: + resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + utils-merge@1.0.1: + resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} + engines: {node: '>= 0.4.0'} + + uuid@7.0.3: + resolution: {integrity: sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==} + deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). + hasBin: true + + v8-to-istanbul@9.3.0: + resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} + engines: {node: '>=10.12.0'} + + validate-npm-package-name@5.0.1: + resolution: {integrity: sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + + vaul@1.1.2: + resolution: {integrity: sha512-ZFkClGpWyI2WUQjdLJ/BaGuV6AVQiJ3uELGk3OYtP+B6yCO7Cmn9vPFXVJkRaGkOJu3m8bQMgtyzNHixULceQA==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc + + vlq@1.0.1: + resolution: {integrity: sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w==} + + w3c-xmlserializer@4.0.0: + resolution: {integrity: sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==} + engines: {node: '>=14'} + + walker@1.0.8: + resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} + + warn-once@0.1.1: + resolution: {integrity: sha512-VkQZJbO8zVImzYFteBXvBOZEl1qL175WH8VmZcxF2fZAoudNhNDvHi+doCaAEdU2l2vtcIwa2zn0QK5+I1HQ3Q==} + + wcwidth@1.0.1: + resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + webidl-conversions@7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + + whatwg-encoding@2.0.0: + resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==} + engines: {node: '>=12'} + deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation + + whatwg-fetch@3.6.20: + resolution: {integrity: sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==} + + whatwg-mimetype@3.0.0: + resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==} + engines: {node: '>=12'} + + whatwg-url-minimum@0.1.2: + resolution: {integrity: sha512-XPEm0XFQWNVG292lII1PrRRJl3sItrs7CettZ4ncYxuDVpLyy+NwlGyut2hXI0JswcJUxeCH+CyOJK0ZzAXD6A==} + + whatwg-url@11.0.0: + resolution: {integrity: sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==} + engines: {node: '>=12'} + + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + + which-boxed-primitive@1.1.1: + resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} + engines: {node: '>= 0.4'} + + which-builtin-type@1.2.1: + resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} + engines: {node: '>= 0.4'} + + which-collection@1.0.2: + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + engines: {node: '>= 0.4'} + + which-typed-array@1.1.22: + resolution: {integrity: sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==} + engines: {node: '>= 0.4'} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + write-file-atomic@4.0.2: + resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + ws@7.5.13: + resolution: {integrity: sha512-rsKI6xDBFVf4r/x8XyChGK04QR/XHroxs/jUcoWvtEZM8TPU/X/uIY9B1CsSzYws9ZJb/6bbBu7dPhFW00CAoA==} + engines: {node: '>=8.3.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + ws@8.21.3: + resolution: {integrity: sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + xcode@3.0.1: + resolution: {integrity: sha512-kCz5k7J7XbJtjABOvkc5lJmkiDh8VhjVCGNiqdKCscmVpdVUpEAyXv1xmCLkQJ5dsHqx3IPO4XW+NTDhU/fatA==} + engines: {node: '>=10.0.0'} + + xml-name-validator@4.0.0: + resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} + engines: {node: '>=12'} + + xml2js@0.6.0: + resolution: {integrity: sha512-eLTh0kA8uHceqesPqSE+VvO1CDDJWMwlQfB6LuN6T8w6MaDJ8Txm8P7s5cHD0miF0V+GGTZrDQfxPZQVsur33w==} + engines: {node: '>=4.0.0'} + + xmlbuilder@11.0.1: + resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==} + engines: {node: '>=4.0'} + + xmlbuilder@15.1.1: + resolution: {integrity: sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==} + engines: {node: '>=8.0'} + + xmlchars@2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yaml-ast-parser@0.0.43: + resolution: {integrity: sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==} + + yaml@2.9.0: + resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==} + engines: {node: '>= 14.6'} + hasBin: true + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.3: + resolution: {integrity: sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==} + engines: {node: '>=12'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + zod-validation-error@4.0.2: + resolution: {integrity: sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + zod: ^3.25.0 || ^4.0.0 + + zod@3.25.76: + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + + zustand@5.0.15: + resolution: {integrity: sha512-MpSEjRiBkA9crSYeOUH32rJC7SVqAbm0Fqcqge/bUi2PPoLcBWKOsG+C8mevmpr8TwXHBVkChbbJiyvkE+i/3A==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@types/react': '>=18.0.0' + immer: '>=9.0.6' + react: '>=18.0.0' + use-sync-external-store: '>=1.2.0' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + use-sync-external-store: + optional: true + + zxing-wasm@3.1.3: + resolution: {integrity: sha512-3lC9BJk4fR5ZJxcGjb0hVnDFOW7KpLXHIebiBmVd4FDRQZVeObztoTKxRUPxlWwSgxrMRONK0u50hBD1aTYEKg==} + peerDependencies: + '@types/emscripten': '>=1.39.6' + +snapshots: + + '@babel/code-frame@7.29.7': + dependencies: + '@babel/helper-validator-identifier': 7.29.7 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.29.7': {} + + '@babel/core@7.29.7(supports-color@10.2.2)': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.8 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/helpers': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.8(supports-color@10.2.2) + '@babel/types': 7.29.8 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3(supports-color@10.2.2) + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.29.8': + dependencies: + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-annotate-as-pure@7.29.7': + dependencies: + '@babel/types': 7.29.8 + + '@babel/helper-compilation-targets@7.29.7': + dependencies: + '@babel/compat-data': 7.29.7 + '@babel/helper-validator-option': 7.29.7 + browserslist: 4.28.8 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-create-class-features-plugin@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-annotate-as-pure': 7.29.7 + '@babel/helper-member-expression-to-functions': 7.29.7(supports-color@10.2.2) + '@babel/helper-optimise-call-expression': 7.29.7 + '@babel/helper-replace-supers': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7(supports-color@10.2.2) + '@babel/traverse': 7.29.8(supports-color@10.2.2) + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/helper-create-regexp-features-plugin@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-annotate-as-pure': 7.29.7 + regexpu-core: 6.4.0 + semver: 6.3.1 + + '@babel/helper-define-polyfill-provider@0.6.8(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + debug: 4.4.3(supports-color@10.2.2) + lodash.debounce: 4.0.8 + resolve: 1.22.12 + transitivePeerDependencies: + - supports-color + + '@babel/helper-globals@7.29.7': {} + + '@babel/helper-member-expression-to-functions@7.29.7(supports-color@10.2.2)': + dependencies: + '@babel/traverse': 7.29.8(supports-color@10.2.2) + '@babel/types': 7.29.8 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-imports@7.29.7(supports-color@10.2.2)': + dependencies: + '@babel/traverse': 7.29.8(supports-color@10.2.2) + '@babel/types': 7.29.8 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-module-imports': 7.29.7(supports-color@10.2.2) + '@babel/helper-validator-identifier': 7.29.7 + '@babel/traverse': 7.29.8(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + '@babel/helper-optimise-call-expression@7.29.7': + dependencies: + '@babel/types': 7.29.8 + + '@babel/helper-plugin-utils@7.29.7': {} + + '@babel/helper-remap-async-to-generator@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-annotate-as-pure': 7.29.7 + '@babel/helper-wrap-function': 7.29.7(supports-color@10.2.2) + '@babel/traverse': 7.29.8(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + '@babel/helper-replace-supers@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-member-expression-to-functions': 7.29.7(supports-color@10.2.2) + '@babel/helper-optimise-call-expression': 7.29.7 + '@babel/traverse': 7.29.8(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + '@babel/helper-skip-transparent-expression-wrappers@7.29.7(supports-color@10.2.2)': + dependencies: + '@babel/traverse': 7.29.8(supports-color@10.2.2) + '@babel/types': 7.29.8 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.29.7': {} + + '@babel/helper-validator-identifier@7.29.7': {} + + '@babel/helper-validator-option@7.29.7': {} + + '@babel/helper-wrap-function@7.29.7(supports-color@10.2.2)': + dependencies: + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.8(supports-color@10.2.2) + '@babel/types': 7.29.8 + transitivePeerDependencies: + - supports-color + + '@babel/helpers@7.29.7': + dependencies: + '@babel/template': 7.29.7 + '@babel/types': 7.29.8 + + '@babel/parser@7.29.8': + dependencies: + '@babel/types': 7.29.8 + + '@babel/plugin-proposal-decorators@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/plugin-syntax-decorators': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-export-default-from@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-decorators@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-export-default-from@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-flow@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-import-attributes@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-arrow-functions@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-async-generator-functions@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-remap-async-to-generator': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/traverse': 7.29.8(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-async-to-generator@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-module-imports': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-remap-async-to-generator': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-block-scoping@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-class-properties@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-class-static-block@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-classes@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-annotate-as-pure': 7.29.7 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-globals': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-replace-supers': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/traverse': 7.29.8(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-destructuring@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/traverse': 7.29.8(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-export-namespace-from@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-flow-strip-types@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/plugin-syntax-flow': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + + '@babel/plugin-transform-for-of@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-logical-assignment-operators@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-modules-commonjs@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-named-capturing-groups-regex@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-nullish-coalescing-operator@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-object-rest-spread@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/plugin-transform-destructuring': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-parameters': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/traverse': 7.29.8(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-optional-catch-binding@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-optional-chaining@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-parameters@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-private-methods@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-private-property-in-object@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-annotate-as-pure': 7.29.7 + '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-react-display-name@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-react-jsx-development@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/plugin-transform-react-jsx': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-react-jsx-self@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-react-jsx-source@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-react-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-annotate-as-pure': 7.29.7 + '@babel/helper-module-imports': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/types': 7.29.8 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-react-pure-annotations@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-annotate-as-pure': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-regenerator@7.29.8(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-runtime@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-module-imports': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + babel-plugin-polyfill-corejs2: 0.4.17(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + babel-plugin-polyfill-corejs3: 0.13.0(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + babel-plugin-polyfill-regenerator: 0.6.8(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-shorthand-properties@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-template-literals@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-annotate-as-pure': 7.29.7 + '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7(supports-color@10.2.2) + '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-unicode-regex@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/preset-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-validator-option': 7.29.7 + '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-modules-commonjs': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-typescript': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + '@babel/runtime@7.29.7': {} + + '@babel/template@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 + + '@babel/traverse@7.29.8(supports-color@10.2.2)': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.8 + '@babel/helper-globals': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/template': 7.29.7 + '@babel/types': 7.29.8 + debug: 4.4.3(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + '@babel/types@7.29.8': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + + '@bcoe/v8-coverage@0.2.3': {} + + '@egjs/hammerjs@2.0.17': + dependencies: + '@types/hammerjs': 2.0.46 + + '@emnapi/core@1.10.0': + dependencies: + '@emnapi/wasi-threads': 1.2.1 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.10.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.2.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@epic-web/invariant@1.0.0': {} + + '@esbuild/aix-ppc64@0.28.2': + optional: true + + '@esbuild/android-arm64@0.28.2': + optional: true + + '@esbuild/android-arm@0.28.2': + optional: true + + '@esbuild/android-x64@0.28.2': + optional: true + + '@esbuild/darwin-arm64@0.28.2': + optional: true + + '@esbuild/darwin-x64@0.28.2': + optional: true + + '@esbuild/freebsd-arm64@0.28.2': + optional: true + + '@esbuild/freebsd-x64@0.28.2': + optional: true + + '@esbuild/linux-arm64@0.28.2': + optional: true + + '@esbuild/linux-arm@0.28.2': + optional: true + + '@esbuild/linux-ia32@0.28.2': + optional: true + + '@esbuild/linux-loong64@0.28.2': + optional: true + + '@esbuild/linux-mips64el@0.28.2': + optional: true + + '@esbuild/linux-ppc64@0.28.2': + optional: true + + '@esbuild/linux-riscv64@0.28.2': + optional: true + + '@esbuild/linux-s390x@0.28.2': + optional: true + + '@esbuild/linux-x64@0.28.2': + optional: true + + '@esbuild/netbsd-arm64@0.28.2': + optional: true + + '@esbuild/netbsd-x64@0.28.2': + optional: true + + '@esbuild/openbsd-arm64@0.28.2': + optional: true + + '@esbuild/openbsd-x64@0.28.2': + optional: true + + '@esbuild/openharmony-arm64@0.28.2': + optional: true + + '@esbuild/sunos-x64@0.28.2': + optional: true + + '@esbuild/win32-arm64@0.28.2': + optional: true + + '@esbuild/win32-ia32@0.28.2': + optional: true + + '@esbuild/win32-x64@0.28.2': + optional: true + + '@eslint-community/eslint-utils@4.10.1(eslint@9.39.5(supports-color@10.2.2))': + dependencies: + eslint: 9.39.5(supports-color@10.2.2) + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/config-array@0.21.2(supports-color@10.2.2)': + dependencies: + '@eslint/object-schema': 2.1.7 + debug: 4.4.3(supports-color@10.2.2) + minimatch: 3.1.5 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.4.2': + dependencies: + '@eslint/core': 0.17.0 + + '@eslint/core@0.17.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.6(supports-color@10.2.2)': + dependencies: + ajv: 6.15.0 + debug: 4.4.3(supports-color@10.2.2) + espree: 10.4.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.3.1 + minimatch: 3.1.5 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.39.5': {} + + '@eslint/object-schema@2.1.7': {} + + '@eslint/plugin-kit@0.4.1': + dependencies: + '@eslint/core': 0.17.0 + levn: 0.4.1 + + '@expo-google-fonts/material-symbols@0.4.44': {} + + '@expo-google-fonts/plus-jakarta-sans@0.4.2': {} + + '@expo/cli@57.0.17(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.12)(expo-constants@57.0.13)(expo-font@57.0.1)(expo-router@57.0.15)(expo@57.0.15)(react-dom@19.2.3(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2)(typescript@6.0.3)': + dependencies: + '@expo/code-signing-certificates': 0.0.6 + '@expo/config': 57.0.8(supports-color@10.2.2)(typescript@6.0.3) + '@expo/config-plugins': 57.0.8(supports-color@10.2.2)(typescript@6.0.3) + '@expo/devcert': 1.2.1(supports-color@10.2.2) + '@expo/env': 2.4.2(supports-color@10.2.2) + '@expo/image-utils': 0.11.4(supports-color@10.2.2)(typescript@6.0.3) + '@expo/inline-modules': 0.1.6(supports-color@10.2.2)(typescript@6.0.3) + '@expo/json-file': 11.0.1 + '@expo/log-box': 57.0.3(@expo/dom-webview@57.0.1)(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + '@expo/metro': 56.0.0(supports-color@10.2.2) + '@expo/metro-config': 57.0.9(expo@57.0.15)(supports-color@10.2.2)(typescript@6.0.3) + '@expo/metro-file-map': 57.0.1(supports-color@10.2.2) + '@expo/osascript': 2.7.1 + '@expo/package-manager': 1.13.1 + '@expo/plist': 0.8.1 + '@expo/prebuild-config': 57.0.13(supports-color@10.2.2)(typescript@6.0.3) + '@expo/require-utils': 57.0.4(supports-color@10.2.2)(typescript@6.0.3) + '@expo/router-server': 57.0.7(@expo/metro-runtime@57.0.12)(expo-constants@57.0.13)(expo-font@57.0.1)(expo-router@57.0.15)(expo-server@57.0.3)(expo@57.0.15)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(supports-color@10.2.2) + '@expo/schema-utils': 57.0.2 + '@expo/spawn-async': 1.8.0 + '@expo/ws-tunnel': 2.0.0(ws@8.21.3) + '@expo/xcpretty': 4.4.4 + '@react-native/dev-middleware': 0.86.2(supports-color@10.2.2) + accepts: 1.3.8 + agent-cli-detector: 0.1.6 + arg: 5.0.2 + bplist-creator: 0.1.0 + bplist-parser: 0.3.2 + chalk: 4.1.2 + ci-info: 3.9.0 + compression: 1.8.1(supports-color@10.2.2) + connect: 3.7.0(supports-color@10.2.2) + debug: 4.4.3(supports-color@10.2.2) + dnssd-advertise: 1.1.6 + expo: 57.0.15(c6665681e457b98938e269e908821e4f) + expo-server: 57.0.3 + fetch-nodeshim: 0.4.10 + getenv: 2.0.0 + glob: 13.0.6 + lan-network: 0.2.1 + multitars: 1.0.2 + node-forge: 1.4.0 + npm-package-arg: 11.0.3 + ora: 3.4.0 + picomatch: 4.0.5 + pretty-format: 29.7.0 + progress: 2.0.3 + prompts: 2.4.2 + resolve-from: 5.0.0 + sandbox-cli-detector: 0.2.0 + semver: 7.8.5 + send: 0.19.2(supports-color@10.2.2) + slugify: 1.6.9 + stacktrace-parser: 0.1.11 + structured-headers: 0.4.1 + terminal-link: 2.1.1 + toqr: 0.1.1 + wrap-ansi: 7.0.0 + ws: 8.21.3 + zod: 3.25.76 + optionalDependencies: + expo-router: 57.0.15(4297cc0b7880e6d80de6379924b45edb) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + transitivePeerDependencies: + - '@expo/dom-webview' + - '@expo/metro-runtime' + - bufferutil + - expo-constants + - expo-font + - react + - react-dom + - react-server-dom-webpack + - supports-color + - typescript + - utf-8-validate + + '@expo/code-signing-certificates@0.0.6': + dependencies: + node-forge: 1.4.0 + + '@expo/config-plugins@57.0.8(supports-color@10.2.2)(typescript@6.0.3)': + dependencies: + '@expo/config-types': 57.0.2 + '@expo/json-file': 11.0.1 + '@expo/plist': 0.8.1 + '@expo/require-utils': 57.0.4(supports-color@10.2.2)(typescript@6.0.3) + '@expo/sdk-runtime-versions': 1.0.0 + chalk: 4.1.2 + debug: 4.4.3(supports-color@10.2.2) + getenv: 2.0.0 + glob: 13.0.6 + semver: 7.8.5 + slugify: 1.6.9 + xcode: 3.0.1 + xml2js: 0.6.0 + transitivePeerDependencies: + - supports-color + - typescript + + '@expo/config-types@57.0.2': {} + + '@expo/config@57.0.8(supports-color@10.2.2)(typescript@6.0.3)': + dependencies: + '@expo/config-plugins': 57.0.8(supports-color@10.2.2)(typescript@6.0.3) + '@expo/config-types': 57.0.2 + '@expo/json-file': 11.0.1 + '@expo/require-utils': 57.0.4(supports-color@10.2.2)(typescript@6.0.3) + deepmerge: 4.3.1 + getenv: 2.0.0 + glob: 13.0.6 + resolve-workspace-root: 2.0.1 + semver: 7.8.5 + slugify: 1.6.9 + transitivePeerDependencies: + - supports-color + - typescript + + '@expo/devcert@1.2.1(supports-color@10.2.2)': + dependencies: + '@expo/sudo-prompt': 9.3.2 + debug: 3.2.7(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + '@expo/devtools@57.0.1(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)': + dependencies: + chalk: 4.1.2 + optionalDependencies: + react: 19.2.3 + react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + + '@expo/dom-webview@57.0.1(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)': + dependencies: + expo: 57.0.15(c6665681e457b98938e269e908821e4f) + react: 19.2.3 + react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + + '@expo/env@2.4.2(supports-color@10.2.2)': + dependencies: + chalk: 4.1.2 + debug: 4.4.3(supports-color@10.2.2) + getenv: 2.0.0 + transitivePeerDependencies: + - supports-color + + '@expo/expo-modules-macros-plugin@0.6.1': {} + + '@expo/fingerprint@0.20.9(supports-color@10.2.2)': + dependencies: + '@expo/env': 2.4.2(supports-color@10.2.2) + '@expo/spawn-async': 1.8.0 + arg: 5.0.2 + chalk: 4.1.2 + debug: 4.4.3(supports-color@10.2.2) + getenv: 2.0.0 + glob: 13.0.6 + ignore: 5.3.2 + minimatch: 10.2.6 + resolve-from: 5.0.0 + semver: 7.8.5 + transitivePeerDependencies: + - supports-color + + '@expo/image-utils@0.11.4(supports-color@10.2.2)(typescript@6.0.3)': + dependencies: + '@expo/require-utils': 57.0.4(supports-color@10.2.2)(typescript@6.0.3) + '@expo/spawn-async': 1.8.0 + chalk: 4.1.2 + getenv: 2.0.0 + jimp-compact: 0.16.1 + parse-png: 2.1.0 + semver: 7.8.5 + transitivePeerDependencies: + - supports-color + - typescript + + '@expo/inline-modules@0.1.6(supports-color@10.2.2)(typescript@6.0.3)': + dependencies: + '@expo/config-plugins': 57.0.8(supports-color@10.2.2)(typescript@6.0.3) + transitivePeerDependencies: + - supports-color + - typescript + + '@expo/json-file@11.0.1': + dependencies: + '@babel/code-frame': 7.29.7 + json5: 2.2.3 + + '@expo/local-build-cache-provider@57.0.7(supports-color@10.2.2)(typescript@6.0.3)': + dependencies: + '@expo/config': 57.0.8(supports-color@10.2.2)(typescript@6.0.3) + chalk: 4.1.2 + transitivePeerDependencies: + - supports-color + - typescript + + '@expo/log-box@57.0.3(@expo/dom-webview@57.0.1)(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)': + dependencies: + '@expo/dom-webview': 57.0.1(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + anser: 1.4.10 + expo: 57.0.15(c6665681e457b98938e269e908821e4f) + react: 19.2.3 + react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + stacktrace-parser: 0.1.11 + + '@expo/metro-config@57.0.9(expo@57.0.15)(supports-color@10.2.2)(typescript@6.0.3)': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/generator': 7.29.8 + '@expo/config': 57.0.8(supports-color@10.2.2)(typescript@6.0.3) + '@expo/env': 2.4.2(supports-color@10.2.2) + '@expo/json-file': 11.0.1 + '@expo/metro': 56.0.0(supports-color@10.2.2) + '@expo/require-utils': 57.0.4(supports-color@10.2.2)(typescript@6.0.3) + '@expo/spawn-async': 1.8.0 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/remapping': 2.3.5 + '@jridgewell/sourcemap-codec': 1.5.5 + browserslist: 4.28.8 + chalk: 4.1.2 + debug: 4.4.3(supports-color@10.2.2) + getenv: 2.0.0 + glob: 13.0.6 + hermes-parser: 0.36.1 + jsc-safe-url: 0.2.4 + lightningcss: 1.33.0 + picomatch: 4.0.5 + postcss: 8.5.26 + resolve-from: 5.0.0 + optionalDependencies: + expo: 57.0.15(c6665681e457b98938e269e908821e4f) + transitivePeerDependencies: + - bufferutil + - supports-color + - typescript + - utf-8-validate + + '@expo/metro-file-map@57.0.1(supports-color@10.2.2)': + dependencies: + debug: 4.4.3(supports-color@10.2.2) + fb-watchman: 2.0.2 + invariant: 2.2.4 + jest-worker: 29.7.0 + micromatch: 4.0.8 + walker: 1.0.8 + transitivePeerDependencies: + - supports-color + + '@expo/metro-runtime@57.0.12(@expo/log-box@57.0.3)(expo@57.0.15)(react-dom@19.2.3(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)': + dependencies: + '@expo/log-box': 57.0.3(@expo/dom-webview@57.0.1)(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + anser: 1.4.10 + expo: 57.0.15(c6665681e457b98938e269e908821e4f) + pretty-format: 29.7.0 + react: 19.2.3 + react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + stacktrace-parser: 0.1.11 + whatwg-fetch: 3.6.20 + optionalDependencies: + react-dom: 19.2.3(react@19.2.3) + + '@expo/metro@56.0.0(supports-color@10.2.2)': + dependencies: + metro: 0.84.4(supports-color@10.2.2) + metro-babel-transformer: 0.84.4(supports-color@10.2.2) + metro-cache: 0.84.4(supports-color@10.2.2) + metro-cache-key: 0.84.4 + metro-config: 0.84.4(supports-color@10.2.2) + metro-core: 0.84.4 + metro-file-map: 0.84.4(supports-color@10.2.2) + metro-minify-terser: 0.84.4 + metro-resolver: 0.84.4 + metro-runtime: 0.84.4 + metro-source-map: 0.84.4(supports-color@10.2.2) + metro-symbolicate: 0.84.4(supports-color@10.2.2) + metro-transform-plugins: 0.84.4(supports-color@10.2.2) + metro-transform-worker: 0.84.4(supports-color@10.2.2) + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + '@expo/osascript@2.7.1': + dependencies: + '@expo/spawn-async': 1.8.0 + + '@expo/package-manager@1.13.1': + dependencies: + '@expo/json-file': 11.0.1 + '@expo/spawn-async': 1.8.0 + chalk: 4.1.2 + npm-package-arg: 11.0.3 + ora: 3.4.0 + resolve-workspace-root: 2.0.1 + + '@expo/plist@0.8.1': + dependencies: + '@xmldom/xmldom': 0.8.14 + base64-js: 1.5.1 + xmlbuilder: 15.1.1 + + '@expo/prebuild-config@57.0.13(supports-color@10.2.2)(typescript@6.0.3)': + dependencies: + '@expo/config': 57.0.8(supports-color@10.2.2)(typescript@6.0.3) + '@expo/config-plugins': 57.0.8(supports-color@10.2.2)(typescript@6.0.3) + '@expo/config-types': 57.0.2 + '@expo/image-utils': 0.11.4(supports-color@10.2.2)(typescript@6.0.3) + '@expo/json-file': 11.0.1 + '@react-native/normalize-colors': 0.86.2 + debug: 4.4.3(supports-color@10.2.2) + expo-modules-autolinking: 57.0.10(supports-color@10.2.2)(typescript@6.0.3) + resolve-from: 5.0.0 + semver: 7.8.5 + transitivePeerDependencies: + - supports-color + - typescript + + '@expo/require-utils@57.0.4(supports-color@10.2.2)(typescript@6.0.3)': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/plugin-transform-modules-commonjs': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + optionalDependencies: + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@expo/router-server@57.0.7(@expo/metro-runtime@57.0.12)(expo-constants@57.0.13)(expo-font@57.0.1)(expo-router@57.0.15)(expo-server@57.0.3)(expo@57.0.15)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(supports-color@10.2.2)': + dependencies: + debug: 4.4.3(supports-color@10.2.2) + expo: 57.0.15(c6665681e457b98938e269e908821e4f) + expo-constants: 57.0.13(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(supports-color@10.2.2) + expo-font: 57.0.1(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + expo-server: 57.0.3 + react: 19.2.3 + optionalDependencies: + '@expo/metro-runtime': 57.0.12(@expo/log-box@57.0.3)(expo@57.0.15)(react-dom@19.2.3(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + expo-router: 57.0.15(4297cc0b7880e6d80de6379924b45edb) + react-dom: 19.2.3(react@19.2.3) + transitivePeerDependencies: + - supports-color + + '@expo/schema-utils@57.0.2': {} + + '@expo/sdk-runtime-versions@1.0.0': {} + + '@expo/spawn-async@1.8.0': + dependencies: + cross-spawn: 7.0.6 + + '@expo/sudo-prompt@9.3.2': {} + + '@expo/ui@57.0.12(ce4128d08365c77ff16d6b531794f812)': + dependencies: + expo: 57.0.15(c6665681e457b98938e269e908821e4f) + react: 19.2.3 + react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + sf-symbols-typescript: 2.2.0 + vaul: 1.1.2(@types/react@19.2.18)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + optionalDependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + react-dom: 19.2.3(react@19.2.3) + react-native-worklets: 0.10.1(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2) + transitivePeerDependencies: + - '@types/react' + - '@types/react-dom' + + '@expo/vector-icons@15.1.1(expo-font@57.0.1)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)': + dependencies: + expo-font: 57.0.1(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + react: 19.2.3 + react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + + '@expo/ws-tunnel@2.0.0(ws@8.21.3)': + dependencies: + ws: 8.21.3 + + '@expo/xcpretty@4.4.4': + dependencies: + '@babel/code-frame': 7.29.7 + chalk: 4.1.2 + js-yaml: 4.3.1 + + '@gorhom/bottom-sheet@5.2.14(45a98be50302868954db6b093f845761)': + dependencies: + '@gorhom/portal': 1.0.14(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + invariant: 2.2.4 + react: 19.2.3 + react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + react-native-gesture-handler: 2.32.0(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + react-native-reanimated: 4.5.1(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.18 + + '@gorhom/portal@1.0.14(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)': + dependencies: + nanoid: 3.3.18 + react: 19.2.3 + react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + + '@humanfs/core@0.19.2': + dependencies: + '@humanfs/types': 0.15.0 + + '@humanfs/node@0.16.8': + dependencies: + '@humanfs/core': 0.19.2 + '@humanfs/types': 0.15.0 + '@humanwhocodes/retry': 0.4.3 + + '@humanfs/types@0.15.0': {} + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@isaacs/ttlcache@1.4.1': {} + + '@istanbuljs/load-nyc-config@1.1.0': + dependencies: + camelcase: 5.3.1 + find-up: 4.1.0 + get-package-type: 0.1.0 + js-yaml: 3.15.1 + resolve-from: 5.0.0 + + '@istanbuljs/schema@0.1.6': {} + + '@jest/console@29.7.0': + dependencies: + '@jest/types': 29.6.3 + '@types/node': 26.2.0 + chalk: 4.1.2 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + slash: 3.0.0 + + '@jest/core@29.7.0(supports-color@10.2.2)': + dependencies: + '@jest/console': 29.7.0 + '@jest/reporters': 29.7.0(supports-color@10.2.2) + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0(supports-color@10.2.2) + '@jest/types': 29.6.3 + '@types/node': 26.2.0 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + ci-info: 3.9.0 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-changed-files: 29.7.0 + jest-config: 29.7.0(@types/node@26.2.0)(supports-color@10.2.2) + jest-haste-map: 29.7.0 + jest-message-util: 29.7.0 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-resolve-dependencies: 29.7.0(supports-color@10.2.2) + jest-runner: 29.7.0(supports-color@10.2.2) + jest-runtime: 29.7.0(supports-color@10.2.2) + jest-snapshot: 29.7.0(supports-color@10.2.2) + jest-util: 29.7.0 + jest-validate: 29.7.0 + jest-watcher: 29.7.0 + micromatch: 4.0.8 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-ansi: 6.0.1 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + - ts-node + + '@jest/create-cache-key-function@29.7.0': + dependencies: + '@jest/types': 29.6.3 + + '@jest/diff-sequences@30.4.0': {} + + '@jest/environment@29.7.0': + dependencies: + '@jest/fake-timers': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 26.2.0 + jest-mock: 29.7.0 + + '@jest/expect-utils@29.7.0': + dependencies: + jest-get-type: 29.6.3 + + '@jest/expect@29.7.0(supports-color@10.2.2)': + dependencies: + expect: 29.7.0 + jest-snapshot: 29.7.0(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + '@jest/fake-timers@29.7.0': + dependencies: + '@jest/types': 29.6.3 + '@sinonjs/fake-timers': 10.3.0 + '@types/node': 26.2.0 + jest-message-util: 29.7.0 + jest-mock: 29.7.0 + jest-util: 29.7.0 + + '@jest/get-type@30.1.0': {} + + '@jest/globals@29.7.0(supports-color@10.2.2)': + dependencies: + '@jest/environment': 29.7.0 + '@jest/expect': 29.7.0(supports-color@10.2.2) + '@jest/types': 29.6.3 + jest-mock: 29.7.0 + transitivePeerDependencies: + - supports-color + + '@jest/reporters@29.7.0(supports-color@10.2.2)': + dependencies: + '@bcoe/v8-coverage': 0.2.3 + '@jest/console': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0(supports-color@10.2.2) + '@jest/types': 29.6.3 + '@jridgewell/trace-mapping': 0.3.31 + '@types/node': 26.2.0 + chalk: 4.1.2 + collect-v8-coverage: 1.0.3 + exit: 0.1.2 + glob: 7.2.3 + graceful-fs: 4.2.11 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-instrument: 6.0.3(supports-color@10.2.2) + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 4.0.1(supports-color@10.2.2) + istanbul-reports: 3.2.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + jest-worker: 29.7.0 + slash: 3.0.0 + string-length: 4.0.2 + strip-ansi: 6.0.1 + v8-to-istanbul: 9.3.0 + transitivePeerDependencies: + - supports-color + + '@jest/schemas@29.6.3': + dependencies: + '@sinclair/typebox': 0.27.12 + + '@jest/schemas@30.4.1': + dependencies: + '@sinclair/typebox': 0.34.52 + + '@jest/source-map@29.6.3': + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + callsites: 3.1.0 + graceful-fs: 4.2.11 + + '@jest/test-result@29.7.0': + dependencies: + '@jest/console': 29.7.0 + '@jest/types': 29.6.3 + '@types/istanbul-lib-coverage': 2.0.6 + collect-v8-coverage: 1.0.3 + + '@jest/test-sequencer@29.7.0': + dependencies: + '@jest/test-result': 29.7.0 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + slash: 3.0.0 + + '@jest/transform@29.7.0(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@jest/types': 29.6.3 + '@jridgewell/trace-mapping': 0.3.31 + babel-plugin-istanbul: 6.1.1(supports-color@10.2.2) + chalk: 4.1.2 + convert-source-map: 2.0.0 + fast-json-stable-stringify: 2.1.0 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-regex-util: 29.6.3 + jest-util: 29.7.0 + micromatch: 4.0.8 + pirates: 4.0.7 + slash: 3.0.0 + write-file-atomic: 4.0.2 + transitivePeerDependencies: + - supports-color + + '@jest/types@29.6.3': + dependencies: + '@jest/schemas': 29.6.3 + '@types/istanbul-lib-coverage': 2.0.6 + '@types/istanbul-reports': 3.0.4 + '@types/node': 26.2.0 + '@types/yargs': 17.0.35 + chalk: 4.1.2 + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/source-map@0.3.11': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@napi-rs/wasm-runtime@1.2.3(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': + dependencies: + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@tybys/wasm-util': 0.10.3 + optional: true + + '@nolyfill/is-core-module@1.0.39': {} + + '@radix-ui/primitive@1.1.7': {} + + '@radix-ui/react-collection@1.1.15(@types/react@19.2.18)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.18)(react@19.2.3) + '@radix-ui/react-context': 1.2.2(@types/react@19.2.18)(react@19.2.3) + '@radix-ui/react-primitive': 2.1.10(@types/react@19.2.18)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-slot': 1.3.3(@types/react@19.2.18)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.18 + + '@radix-ui/react-compose-refs@1.1.5(@types/react@19.2.18)(react@19.2.3)': + dependencies: + react: 19.2.3 + optionalDependencies: + '@types/react': 19.2.18 + + '@radix-ui/react-context@1.2.2(@types/react@19.2.18)(react@19.2.3)': + dependencies: + react: 19.2.3 + optionalDependencies: + '@types/react': 19.2.18 + + '@radix-ui/react-dialog@1.1.23(@types/react@19.2.18)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.18)(react@19.2.3) + '@radix-ui/react-context': 1.2.2(@types/react@19.2.18)(react@19.2.3) + '@radix-ui/react-dismissable-layer': 1.1.19(@types/react@19.2.18)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-focus-guards': 1.1.6(@types/react@19.2.18)(react@19.2.3) + '@radix-ui/react-focus-scope': 1.1.16(@types/react@19.2.18)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-id': 1.1.4(@types/react@19.2.18)(react@19.2.3) + '@radix-ui/react-portal': 1.1.17(@types/react@19.2.18)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-presence': 1.1.10(@types/react@19.2.18)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-primitive': 2.1.10(@types/react@19.2.18)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-slot': 1.3.3(@types/react@19.2.18)(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.18)(react@19.2.3) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.18)(react@19.2.3) + aria-hidden: 1.2.6 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + react-remove-scroll: 2.7.2(@types/react@19.2.18)(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.18 + + '@radix-ui/react-direction@1.1.4(@types/react@19.2.18)(react@19.2.3)': + dependencies: + react: 19.2.3 + optionalDependencies: + '@types/react': 19.2.18 + + '@radix-ui/react-dismissable-layer@1.1.19(@types/react@19.2.18)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.18)(react@19.2.3) + '@radix-ui/react-primitive': 2.1.10(@types/react@19.2.18)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.2.18)(react@19.2.3) + '@radix-ui/react-use-effect-event': 0.0.5(@types/react@19.2.18)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.18 + + '@radix-ui/react-focus-guards@1.1.6(@types/react@19.2.18)(react@19.2.3)': + dependencies: + react: 19.2.3 + optionalDependencies: + '@types/react': 19.2.18 + + '@radix-ui/react-focus-scope@1.1.16(@types/react@19.2.18)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.18)(react@19.2.3) + '@radix-ui/react-primitive': 2.1.10(@types/react@19.2.18)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.2.18)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.18 + + '@radix-ui/react-id@1.1.4(@types/react@19.2.18)(react@19.2.3)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.18)(react@19.2.3) + react: 19.2.3 + optionalDependencies: + '@types/react': 19.2.18 + + '@radix-ui/react-portal@1.1.17(@types/react@19.2.18)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/react-primitive': 2.1.10(@types/react@19.2.18)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.18)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.18 + + '@radix-ui/react-presence@1.1.10(@types/react@19.2.18)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.18)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.18 + + '@radix-ui/react-primitive@2.1.10(@types/react@19.2.18)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/react-slot': 1.3.3(@types/react@19.2.18)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.18 + + '@radix-ui/react-roving-focus@1.1.19(@types/react@19.2.18)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-collection': 1.1.15(@types/react@19.2.18)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.18)(react@19.2.3) + '@radix-ui/react-context': 1.2.2(@types/react@19.2.18)(react@19.2.3) + '@radix-ui/react-direction': 1.1.4(@types/react@19.2.18)(react@19.2.3) + '@radix-ui/react-id': 1.1.4(@types/react@19.2.18)(react@19.2.3) + '@radix-ui/react-primitive': 2.1.10(@types/react@19.2.18)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.2.18)(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.18)(react@19.2.3) + '@radix-ui/react-use-is-hydrated': 0.1.3(@types/react@19.2.18)(react@19.2.3) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.18)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.18 + + '@radix-ui/react-slot@1.3.3(@types/react@19.2.18)(react@19.2.3)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.18)(react@19.2.3) + react: 19.2.3 + optionalDependencies: + '@types/react': 19.2.18 + + '@radix-ui/react-tabs@1.1.21(@types/react@19.2.18)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-context': 1.2.2(@types/react@19.2.18)(react@19.2.3) + '@radix-ui/react-direction': 1.1.4(@types/react@19.2.18)(react@19.2.3) + '@radix-ui/react-id': 1.1.4(@types/react@19.2.18)(react@19.2.3) + '@radix-ui/react-presence': 1.1.10(@types/react@19.2.18)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-primitive': 2.1.10(@types/react@19.2.18)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-roving-focus': 1.1.19(@types/react@19.2.18)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.18)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.18 + + '@radix-ui/react-use-callback-ref@1.1.4(@types/react@19.2.18)(react@19.2.3)': + dependencies: + react: 19.2.3 + optionalDependencies: + '@types/react': 19.2.18 + + '@radix-ui/react-use-controllable-state@1.2.6(@types/react@19.2.18)(react@19.2.3)': + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-use-effect-event': 0.0.5(@types/react@19.2.18)(react@19.2.3) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.18)(react@19.2.3) + react: 19.2.3 + optionalDependencies: + '@types/react': 19.2.18 + + '@radix-ui/react-use-effect-event@0.0.5(@types/react@19.2.18)(react@19.2.3)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.18)(react@19.2.3) + react: 19.2.3 + optionalDependencies: + '@types/react': 19.2.18 + + '@radix-ui/react-use-is-hydrated@0.1.3(@types/react@19.2.18)(react@19.2.3)': + dependencies: + react: 19.2.3 + optionalDependencies: + '@types/react': 19.2.18 + + '@radix-ui/react-use-layout-effect@1.1.4(@types/react@19.2.18)(react@19.2.3)': + dependencies: + react: 19.2.3 + optionalDependencies: + '@types/react': 19.2.18 + + '@react-native-async-storage/async-storage@2.2.0(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))': + dependencies: + merge-options: 3.0.4 + react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + + '@react-native-masked-view/masked-view@0.3.2(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)': + dependencies: + react: 19.2.3 + react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + + '@react-native/assets-registry@0.86.2': {} + + '@react-native/babel-plugin-codegen@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/traverse': 7.29.8(supports-color@10.2.2) + '@react-native/codegen': 0.86.2(@babel/core@7.29.7(supports-color@10.2.2)) + transitivePeerDependencies: + - '@babel/core' + - supports-color + + '@react-native/babel-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/plugin-proposal-export-default-from': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-syntax-export-default-from': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-async-generator-functions': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-async-to-generator': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-block-scoping': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-class-properties': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-classes': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-destructuring': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-flow-strip-types': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-for-of': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-modules-commonjs': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-named-capturing-groups-regex': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-nullish-coalescing-operator': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-optional-catch-binding': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-optional-chaining': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-private-methods': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-private-property-in-object': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-react-display-name': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-react-jsx': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-react-jsx-self': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-react-jsx-source': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-regenerator': 7.29.8(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-runtime': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-typescript': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-unicode-regex': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@react-native/babel-plugin-codegen': 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + babel-plugin-syntax-hermes-parser: 0.36.0 + babel-plugin-transform-flow-enums: 0.0.2(@babel/core@7.29.7(supports-color@10.2.2)) + react-refresh: 0.14.2 + transitivePeerDependencies: + - supports-color + + '@react-native/codegen@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/parser': 7.29.8 + hermes-parser: 0.36.0 + invariant: 2.2.4 + nullthrows: 1.1.1 + tinyglobby: 0.2.17 + yargs: 17.7.3 + + '@react-native/community-cli-plugin@0.86.2(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@react-native/dev-middleware': 0.86.2(supports-color@10.2.2) + debug: 4.4.3(supports-color@10.2.2) + invariant: 2.2.4 + metro: 0.84.5(supports-color@10.2.2) + metro-config: 0.84.5(supports-color@10.2.2) + metro-core: 0.84.5 + semver: 7.8.5 + optionalDependencies: + '@react-native/metro-config': 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + '@react-native/debugger-frontend@0.86.2': {} + + '@react-native/debugger-shell@0.86.2(supports-color@10.2.2)': + dependencies: + cross-spawn: 7.0.6 + debug: 4.4.3(supports-color@10.2.2) + fb-dotslash: 0.5.8 + transitivePeerDependencies: + - supports-color + + '@react-native/dev-middleware@0.86.2(supports-color@10.2.2)': + dependencies: + '@isaacs/ttlcache': 1.4.1 + '@react-native/debugger-frontend': 0.86.2 + '@react-native/debugger-shell': 0.86.2(supports-color@10.2.2) + chrome-launcher: 0.15.2(supports-color@10.2.2) + chromium-edge-launcher: 0.3.0(supports-color@10.2.2) + connect: 3.7.0(supports-color@10.2.2) + debug: 4.4.3(supports-color@10.2.2) + invariant: 2.2.4 + nullthrows: 1.1.1 + open: 7.4.2 + serve-static: 1.16.3(supports-color@10.2.2) + ws: 7.5.13 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + '@react-native/gradle-plugin@0.86.2': {} + + '@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2)': + dependencies: + '@jest/create-cache-key-function': 29.7.0 + '@react-native/js-polyfills': 0.86.2 + babel-jest: 29.7.0(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + jest-environment-node: 29.7.0 + react: 19.2.3 + regenerator-runtime: 0.13.11 + transitivePeerDependencies: + - '@babel/core' + - supports-color + + '@react-native/js-polyfills@0.86.2': {} + + '@react-native/metro-babel-transformer@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@react-native/babel-preset': 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + hermes-parser: 0.36.0 + nullthrows: 1.1.1 + transitivePeerDependencies: + - supports-color + + '@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@react-native/js-polyfills': 0.86.2 + '@react-native/metro-babel-transformer': 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + metro-config: 0.84.5(supports-color@10.2.2) + metro-runtime: 0.84.5 + transitivePeerDependencies: + - '@babel/core' + - bufferutil + - supports-color + - utf-8-validate + + '@react-native/normalize-colors@0.74.89': {} + + '@react-native/normalize-colors@0.86.2': {} + + '@react-native/virtualized-lists@0.86.2(@types/react@19.2.18)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)': + dependencies: + invariant: 2.2.4 + nullthrows: 1.1.1 + react: 19.2.3 + react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + optionalDependencies: + '@types/react': 19.2.18 + + '@redocly/ajv@8.11.2': + dependencies: + fast-deep-equal: 3.1.3 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + uri-js-replace: 1.0.1 + + '@redocly/config@0.22.0': {} + + '@redocly/openapi-core@1.34.19(supports-color@10.2.2)': + dependencies: + '@redocly/ajv': 8.11.2 + '@redocly/config': 0.22.0 + colorette: 1.4.0 + https-proxy-agent: 7.0.6(supports-color@10.2.2) + js-levenshtein: 1.1.6 + js-yaml: 4.3.1 + minimatch: 5.1.9 + pluralize: 8.0.0 + yaml-ast-parser: 0.0.43 + transitivePeerDependencies: + - supports-color + + '@rtsao/scc@1.1.0': {} + + '@shopify/flash-list@2.0.2(@babel/runtime@7.29.7)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)': + dependencies: + '@babel/runtime': 7.29.7 + react: 19.2.3 + react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + tslib: 2.8.1 + + '@sinclair/typebox@0.27.12': {} + + '@sinclair/typebox@0.34.52': {} + + '@sinonjs/commons@3.0.1': + dependencies: + type-detect: 4.0.8 + + '@sinonjs/fake-timers@10.3.0': + dependencies: + '@sinonjs/commons': 3.0.1 + + '@tanstack/query-core@5.101.4': {} + + '@tanstack/react-query@5.101.4(react@19.2.3)': + dependencies: + '@tanstack/query-core': 5.101.4 + react: 19.2.3 + + '@testing-library/react-native@14.0.1(jest@29.7.0(@types/node@26.2.0)(supports-color@10.2.2))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(test-renderer@1.2.0(@types/react@19.2.18)(react@19.2.3))': + dependencies: + jest-matcher-utils: 30.4.1 + picocolors: 1.1.1 + pretty-format: 30.4.1 + react: 19.2.3 + react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + redent: 3.0.0 + test-renderer: 1.2.0(@types/react@19.2.18)(react@19.2.3) + optionalDependencies: + jest: 29.7.0(@types/node@26.2.0)(supports-color@10.2.2) + + '@tootallnate/once@2.0.1': {} + + '@tybys/wasm-util@0.10.3': + dependencies: + tslib: 2.8.1 + optional: true + + '@types/babel__core@7.20.5': + dependencies: + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 + '@types/babel__generator': 7.27.0 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.28.0 + + '@types/babel__generator@7.27.0': + dependencies: + '@babel/types': 7.29.8 + + '@types/babel__template@7.4.4': + dependencies: + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 + + '@types/babel__traverse@7.28.0': + dependencies: + '@babel/types': 7.29.8 + + '@types/emscripten@1.41.5': {} + + '@types/estree@1.0.9': {} + + '@types/graceful-fs@4.1.9': + dependencies: + '@types/node': 26.2.0 + + '@types/hammerjs@2.0.46': {} + + '@types/istanbul-lib-coverage@2.0.6': {} + + '@types/istanbul-lib-report@3.0.3': + dependencies: + '@types/istanbul-lib-coverage': 2.0.6 + + '@types/istanbul-reports@3.0.4': + dependencies: + '@types/istanbul-lib-report': 3.0.3 + + '@types/jest@29.5.14': + dependencies: + expect: 29.7.0 + pretty-format: 29.7.0 + + '@types/jsdom@20.0.1': + dependencies: + '@types/node': 26.2.0 + '@types/tough-cookie': 4.0.5 + parse5: 7.3.0 + + '@types/json-schema@7.0.15': {} + + '@types/json5@0.0.29': {} + + '@types/node@26.2.0': + dependencies: + undici-types: 8.3.0 + + '@types/react-reconciler@0.33.0(@types/react@19.2.18)': + dependencies: + '@types/react': 19.2.18 + + '@types/react-test-renderer@19.1.0': + dependencies: + '@types/react': 19.2.18 + + '@types/react@19.2.18': + dependencies: + csstype: 3.2.3 + + '@types/stack-utils@2.0.3': {} + + '@types/tough-cookie@4.0.5': {} + + '@types/yargs-parser@21.0.3': {} + + '@types/yargs@17.0.35': + dependencies: + '@types/yargs-parser': 21.0.3 + + '@typescript-eslint/eslint-plugin@8.67.0(@typescript-eslint/parser@8.67.0(eslint@9.39.5(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@9.39.5(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.67.0(eslint@9.39.5(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@typescript-eslint/scope-manager': 8.67.0 + '@typescript-eslint/type-utils': 8.67.0(eslint@9.39.5(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@typescript-eslint/utils': 8.67.0(eslint@9.39.5(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@typescript-eslint/visitor-keys': 8.67.0 + eslint: 9.39.5(supports-color@10.2.2) + ignore: 7.0.6 + natural-compare: 1.4.0 + ts-api-utils: 2.5.0(typescript@6.0.3) + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.67.0(eslint@9.39.5(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.67.0 + '@typescript-eslint/types': 8.67.0 + '@typescript-eslint/typescript-estree': 8.67.0(supports-color@10.2.2)(typescript@6.0.3) + '@typescript-eslint/visitor-keys': 8.67.0 + debug: 4.4.3(supports-color@10.2.2) + eslint: 9.39.5(supports-color@10.2.2) + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.67.0(supports-color@10.2.2)(typescript@6.0.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.67.0(typescript@6.0.3) + '@typescript-eslint/types': 8.67.0 + debug: 4.4.3(supports-color@10.2.2) + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.67.0': + dependencies: + '@typescript-eslint/types': 8.67.0 + '@typescript-eslint/visitor-keys': 8.67.0 + + '@typescript-eslint/tsconfig-utils@8.67.0(typescript@6.0.3)': + dependencies: + typescript: 6.0.3 + + '@typescript-eslint/type-utils@8.67.0(eslint@9.39.5(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)': + dependencies: + '@typescript-eslint/types': 8.67.0 + '@typescript-eslint/typescript-estree': 8.67.0(supports-color@10.2.2)(typescript@6.0.3) + '@typescript-eslint/utils': 8.67.0(eslint@9.39.5(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + debug: 4.4.3(supports-color@10.2.2) + eslint: 9.39.5(supports-color@10.2.2) + ts-api-utils: 2.5.0(typescript@6.0.3) + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.67.0': {} + + '@typescript-eslint/typescript-estree@8.67.0(supports-color@10.2.2)(typescript@6.0.3)': + dependencies: + '@typescript-eslint/project-service': 8.67.0(supports-color@10.2.2)(typescript@6.0.3) + '@typescript-eslint/tsconfig-utils': 8.67.0(typescript@6.0.3) + '@typescript-eslint/types': 8.67.0 + '@typescript-eslint/visitor-keys': 8.67.0 + debug: 4.4.3(supports-color@10.2.2) + minimatch: 10.2.6 + semver: 7.8.5 + tinyglobby: 0.2.17 + ts-api-utils: 2.5.0(typescript@6.0.3) + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.67.0(eslint@9.39.5(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)': + dependencies: + '@eslint-community/eslint-utils': 4.10.1(eslint@9.39.5(supports-color@10.2.2)) + '@typescript-eslint/scope-manager': 8.67.0 + '@typescript-eslint/types': 8.67.0 + '@typescript-eslint/typescript-estree': 8.67.0(supports-color@10.2.2)(typescript@6.0.3) + eslint: 9.39.5(supports-color@10.2.2) + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.67.0': + dependencies: + '@typescript-eslint/types': 8.67.0 + eslint-visitor-keys: 5.0.1 + + '@ungap/structured-clone@1.3.3': {} + + '@unrs/resolver-binding-android-arm-eabi@1.12.2': + optional: true + + '@unrs/resolver-binding-android-arm64@1.12.2': + optional: true + + '@unrs/resolver-binding-darwin-arm64@1.12.2': + optional: true + + '@unrs/resolver-binding-darwin-x64@1.12.2': + optional: true + + '@unrs/resolver-binding-freebsd-x64@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-arm-gnueabihf@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-arm-musleabihf@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-arm64-gnu@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-arm64-musl@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-loong64-gnu@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-loong64-musl@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-ppc64-gnu@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-riscv64-gnu@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-riscv64-musl@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-s390x-gnu@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-x64-gnu@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-x64-musl@1.12.2': + optional: true + + '@unrs/resolver-binding-openharmony-arm64@1.12.2': + optional: true + + '@unrs/resolver-binding-wasm32-wasi@1.12.2': + dependencies: + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@napi-rs/wasm-runtime': 1.2.3(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + optional: true + + '@unrs/resolver-binding-win32-arm64-msvc@1.12.2': + optional: true + + '@unrs/resolver-binding-win32-ia32-msvc@1.12.2': + optional: true + + '@unrs/resolver-binding-win32-x64-msvc@1.12.2': + optional: true + + '@xmldom/xmldom@0.8.14': {} + + '@xmldom/xmldom@0.9.11': {} + + abab@2.0.6: {} + + abort-controller@3.0.0: + dependencies: + event-target-shim: 5.0.1 + + accepts@1.3.8: + dependencies: + mime-types: 2.1.35 + negotiator: 0.6.3 + + accepts@2.0.0: + dependencies: + mime-types: 3.0.2 + negotiator: 1.0.0 + + acorn-globals@7.0.1: + dependencies: + acorn: 8.18.0 + acorn-walk: 8.3.5 + + acorn-jsx@5.3.2(acorn@8.18.0): + dependencies: + acorn: 8.18.0 + + acorn-walk@8.3.5: + dependencies: + acorn: 8.18.0 + + acorn@8.18.0: {} + + agent-base@6.0.2(supports-color@10.2.2): + dependencies: + debug: 4.4.3(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + agent-base@7.1.4: {} + + agent-cli-detector@0.1.6: {} + + ajv@6.15.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + anser@1.4.10: {} + + ansi-colors@4.1.3: {} + + ansi-escapes@4.3.2: + dependencies: + type-fest: 0.21.3 + + ansi-escapes@6.2.1: {} + + ansi-regex@4.1.1: {} + + ansi-regex@5.0.1: {} + + ansi-regex@6.3.0: {} + + ansi-styles@3.2.1: + dependencies: + color-convert: 1.9.3 + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@5.2.0: {} + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.2 + + arg@4.1.3: {} + + arg@5.0.2: {} + + argparse@1.0.10: + dependencies: + sprintf-js: 1.0.3 + + argparse@2.0.1: {} + + aria-hidden@1.2.6: + dependencies: + tslib: 2.8.1 + + array-buffer-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + is-array-buffer: 3.0.5 + + array-includes@3.1.9: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-object-atoms: 1.1.2 + get-intrinsic: 1.3.0 + is-string: 1.1.1 + math-intrinsics: 1.1.0 + + array.prototype.findlast@1.2.5: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + es-shim-unscopables: 1.1.0 + + array.prototype.findlastindex@1.2.6: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + es-shim-unscopables: 1.1.0 + + array.prototype.flat@1.3.3: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-shim-unscopables: 1.1.0 + + array.prototype.flatmap@1.3.3: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-shim-unscopables: 1.1.0 + + array.prototype.tosorted@1.1.4: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-shim-unscopables: 1.1.0 + + arraybuffer.prototype.slice@1.0.4: + dependencies: + array-buffer-byte-length: 1.0.2 + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + is-array-buffer: 3.0.5 + + asap@2.0.6: {} + + async-function@1.0.0: {} + + asynckit@0.4.0: {} + + available-typed-arrays@1.0.7: + dependencies: + possible-typed-array-names: 1.1.0 + + babel-jest@29.7.0(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2): + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@jest/transform': 29.7.0(supports-color@10.2.2) + '@types/babel__core': 7.20.5 + babel-plugin-istanbul: 6.1.1(supports-color@10.2.2) + babel-preset-jest: 29.6.3(@babel/core@7.29.7(supports-color@10.2.2)) + chalk: 4.1.2 + graceful-fs: 4.2.11 + slash: 3.0.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-istanbul@6.1.1(supports-color@10.2.2): + dependencies: + '@babel/helper-plugin-utils': 7.29.7 + '@istanbuljs/load-nyc-config': 1.1.0 + '@istanbuljs/schema': 0.1.6 + istanbul-lib-instrument: 5.2.1(supports-color@10.2.2) + test-exclude: 6.0.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-jest-hoist@29.6.3: + dependencies: + '@babel/template': 7.29.7 + '@babel/types': 7.29.8 + '@types/babel__core': 7.20.5 + '@types/babel__traverse': 7.28.0 + + babel-plugin-polyfill-corejs2@0.4.17(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2): + dependencies: + '@babel/compat-data': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-corejs3@0.13.0(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2): + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + core-js-compat: 3.50.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-regenerator@0.6.8(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2): + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + babel-plugin-react-compiler@1.0.0: + dependencies: + '@babel/types': 7.29.8 + + babel-plugin-react-native-web@0.21.2: {} + + babel-plugin-syntax-hermes-parser@0.36.0: + dependencies: + hermes-parser: 0.36.0 + + babel-plugin-syntax-hermes-parser@0.36.1: + dependencies: + hermes-parser: 0.36.1 + + babel-plugin-transform-flow-enums@0.0.2(@babel/core@7.29.7(supports-color@10.2.2)): + dependencies: + '@babel/plugin-syntax-flow': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + transitivePeerDependencies: + - '@babel/core' + + babel-preset-current-node-syntax@1.2.0(@babel/core@7.29.7(supports-color@10.2.2)): + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-syntax-import-attributes': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.29.7(supports-color@10.2.2)) + + babel-preset-expo@57.0.7(@babel/core@7.29.7(supports-color@10.2.2))(@babel/runtime@7.29.7)(expo@57.0.15)(react-refresh@0.14.2)(supports-color@10.2.2): + dependencies: + '@babel/generator': 7.29.8 + '@babel/helper-module-imports': 7.29.7(supports-color@10.2.2) + '@babel/plugin-proposal-decorators': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-proposal-export-default-from': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-syntax-export-default-from': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-async-generator-functions': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-async-to-generator': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-block-scoping': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-class-properties': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-class-static-block': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-classes': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-destructuring': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-export-namespace-from': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-flow-strip-types': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-for-of': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-logical-assignment-operators': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-modules-commonjs': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-named-capturing-groups-regex': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-nullish-coalescing-operator': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-object-rest-spread': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-optional-catch-binding': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-optional-chaining': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-parameters': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-private-methods': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-private-property-in-object': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-react-display-name': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-react-jsx': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-react-jsx-development': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-react-pure-annotations': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-runtime': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-typescript': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-unicode-regex': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/preset-typescript': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@react-native/babel-plugin-codegen': 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + babel-plugin-react-compiler: 1.0.0 + babel-plugin-react-native-web: 0.21.2 + babel-plugin-syntax-hermes-parser: 0.36.1 + babel-plugin-transform-flow-enums: 0.0.2(@babel/core@7.29.7(supports-color@10.2.2)) + debug: 4.4.3(supports-color@10.2.2) + react-refresh: 0.14.2 + optionalDependencies: + '@babel/runtime': 7.29.7 + expo: 57.0.15(c6665681e457b98938e269e908821e4f) + transitivePeerDependencies: + - '@babel/core' + - supports-color + + babel-preset-jest@29.6.3(@babel/core@7.29.7(supports-color@10.2.2)): + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + babel-plugin-jest-hoist: 29.6.3 + babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.7(supports-color@10.2.2)) + + badgin@1.2.3: {} + + balanced-match@1.0.2: {} + + balanced-match@4.0.4: {} + + barcode-detector@3.2.2(@types/emscripten@1.41.5): + dependencies: + zxing-wasm: 3.1.3(@types/emscripten@1.41.5) + transitivePeerDependencies: + - '@types/emscripten' + + base64-js@1.5.1: {} + + baseline-browser-mapping@2.11.15: {} + + big-integer@1.6.52: {} + + bplist-creator@0.1.0: + dependencies: + stream-buffers: 2.2.0 + + bplist-parser@0.3.1: + dependencies: + big-integer: 1.6.52 + + bplist-parser@0.3.2: + dependencies: + big-integer: 1.6.52 + + brace-expansion@1.1.18: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.1.4: + dependencies: + balanced-match: 1.0.2 + + brace-expansion@5.0.9: + dependencies: + balanced-match: 4.0.4 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist@4.28.8: + dependencies: + baseline-browser-mapping: 2.11.15 + caniuse-lite: 1.0.30001809 + electron-to-chromium: 1.5.411 + node-releases: 2.0.53 + update-browserslist-db: 1.3.1(browserslist@4.28.8) + + bser@2.1.1: + dependencies: + node-int64: 0.4.0 + + buffer-from@1.1.2: {} + + bytes@3.1.2: {} + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bind@1.0.9: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + get-intrinsic: 1.3.0 + set-function-length: 1.2.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + callsites@3.1.0: {} + + camelcase@5.3.1: {} + + camelcase@6.3.0: {} + + caniuse-lite@1.0.30001809: {} + + chalk@2.4.2: + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + + chalk@3.0.0: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + change-case@5.4.4: {} + + char-regex@1.0.2: {} + + char-regex@2.0.2: {} + + chrome-launcher@0.15.2(supports-color@10.2.2): + dependencies: + '@types/node': 26.2.0 + escape-string-regexp: 4.0.0 + is-wsl: 2.2.0 + lighthouse-logger: 1.4.2(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + chromium-edge-launcher@0.3.0(supports-color@10.2.2): + dependencies: + '@types/node': 26.2.0 + escape-string-regexp: 4.0.0 + is-wsl: 2.2.0 + lighthouse-logger: 1.4.2(supports-color@10.2.2) + mkdirp: 1.0.4 + transitivePeerDependencies: + - supports-color + + ci-info@2.0.0: {} + + ci-info@3.9.0: {} + + cjs-module-lexer@1.4.3: {} + + cli-cursor@2.1.0: + dependencies: + restore-cursor: 2.0.0 + + cli-spinners@2.9.2: {} + + client-only@0.0.1: {} + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + clone@1.0.4: {} + + co@4.6.0: {} + + collect-v8-coverage@1.0.3: {} + + color-convert@1.9.3: + dependencies: + color-name: 1.1.3 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.3: {} + + color-name@1.1.4: {} + + color-string@1.9.1: + dependencies: + color-name: 1.1.4 + simple-swizzle: 0.2.4 + + color@4.2.3: + dependencies: + color-convert: 2.0.1 + color-string: 1.9.1 + + colorette@1.4.0: {} + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + commander@12.1.0: {} + + commander@2.20.3: {} + + commander@7.2.0: {} + + compressible@2.0.18: + dependencies: + mime-db: 1.54.0 + + compression@1.8.1(supports-color@10.2.2): + dependencies: + bytes: 3.1.2 + compressible: 2.0.18 + debug: 2.6.9(supports-color@10.2.2) + negotiator: 0.6.4 + on-headers: 1.1.0 + safe-buffer: 5.2.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + concat-map@0.0.1: {} + + connect@3.7.0(supports-color@10.2.2): + dependencies: + debug: 2.6.9(supports-color@10.2.2) + finalhandler: 1.1.2(supports-color@10.2.2) + parseurl: 1.3.3 + utils-merge: 1.0.1 + transitivePeerDependencies: + - supports-color + + convert-source-map@2.0.0: {} + + core-js-compat@3.50.0: + dependencies: + browserslist: 4.28.8 + + create-jest@29.7.0(@types/node@26.2.0)(supports-color@10.2.2): + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-config: 29.7.0(@types/node@26.2.0)(supports-color@10.2.2) + jest-util: 29.7.0 + prompts: 2.4.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + + cross-env@10.1.0: + dependencies: + '@epic-web/invariant': 1.0.0 + cross-spawn: 7.0.6 + + cross-fetch@3.2.0: + dependencies: + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + css-in-js-utils@3.1.0: + dependencies: + hyphenate-style-name: 1.1.0 + + cssom@0.3.8: {} + + cssom@0.5.0: {} + + cssstyle@2.3.0: + dependencies: + cssom: 0.3.8 + + csstype@3.2.3: {} + + data-urls@3.0.2: + dependencies: + abab: 2.0.6 + whatwg-mimetype: 3.0.0 + whatwg-url: 11.0.0 + + data-view-buffer@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-offset@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + dayjs@1.11.23: {} + + debug@2.6.9(supports-color@10.2.2): + dependencies: + ms: 2.0.0 + optionalDependencies: + supports-color: 10.2.2 + + debug@3.2.7(supports-color@10.2.2): + dependencies: + ms: 2.1.3 + optionalDependencies: + supports-color: 10.2.2 + + debug@4.4.3(supports-color@10.2.2): + dependencies: + ms: 2.1.3 + optionalDependencies: + supports-color: 10.2.2 + + decimal.js@10.6.0: {} + + decode-uri-component@0.2.2: {} + + dedent@1.7.2: {} + + deep-is@0.1.4: {} + + deepmerge@4.3.1: {} + + defaults@1.0.4: + dependencies: + clone: 1.0.4 + + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 + + define-properties@1.2.1: + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 + object-keys: 1.1.1 + + delayed-stream@1.0.0: {} + + depd@2.0.0: {} + + destroy@1.2.0: {} + + detect-libc@2.1.2: {} + + detect-newline@3.1.0: {} + + detect-node-es@1.1.0: {} + + diff-sequences@29.6.3: {} + + dnssd-advertise@1.1.6: {} + + doctrine@2.1.0: + dependencies: + esutils: 2.0.3 + + domexception@4.0.0: + dependencies: + webidl-conversions: 7.0.0 + + dotenv@17.4.2: {} + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + ee-first@1.1.1: {} + + electron-to-chromium@1.5.411: {} + + emittery@0.13.1: {} + + emoji-regex@8.0.0: {} + + encodeurl@1.0.2: {} + + encodeurl@2.0.0: {} + + entities@6.0.1: {} + + error-ex@1.3.4: + dependencies: + is-arrayish: 0.2.1 + + error-stack-parser@2.1.4: + dependencies: + stackframe: 1.3.4 + + es-abstract-get@1.0.0: + dependencies: + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + is-callable: 1.2.7 + object-inspect: 1.13.4 + + es-abstract@1.24.2: + dependencies: + array-buffer-byte-length: 1.0.2 + arraybuffer.prototype.slice: 1.0.4 + available-typed-arrays: 1.0.7 + call-bind: 1.0.9 + call-bound: 1.0.4 + data-view-buffer: 1.0.2 + data-view-byte-length: 1.0.2 + data-view-byte-offset: 1.0.1 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + es-set-tostringtag: 2.1.0 + es-to-primitive: 1.3.4 + function.prototype.name: 1.2.0 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + get-symbol-description: 1.1.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.4 + internal-slot: 1.1.0 + is-array-buffer: 3.0.5 + is-callable: 1.2.7 + is-data-view: 1.0.2 + is-negative-zero: 2.0.3 + is-regex: 1.2.1 + is-set: 2.0.3 + is-shared-array-buffer: 1.0.4 + is-string: 1.1.1 + is-typed-array: 1.1.15 + is-weakref: 1.1.1 + math-intrinsics: 1.1.0 + object-inspect: 1.13.4 + object-keys: 1.1.1 + object.assign: 4.1.7 + own-keys: 1.0.2 + regexp.prototype.flags: 1.5.4 + safe-array-concat: 1.1.4 + safe-push-apply: 1.0.0 + safe-regex-test: 1.1.0 + set-proto: 1.0.0 + stop-iteration-iterator: 1.1.0 + string.prototype.trim: 1.2.11 + string.prototype.trimend: 1.0.10 + string.prototype.trimstart: 1.0.8 + typed-array-buffer: 1.0.3 + typed-array-byte-length: 1.0.3 + typed-array-byte-offset: 1.0.4 + typed-array-length: 1.0.8 + unbox-primitive: 1.1.0 + which-typed-array: 1.1.22 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-iterator-helpers@1.4.0: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-set-tostringtag: 2.1.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + iterator.prototype: 1.1.5 + math-intrinsics: 1.1.0 + + es-object-atoms@1.1.2: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.4 + + es-shim-unscopables@1.1.0: + dependencies: + hasown: 2.0.4 + + es-to-primitive@1.3.4: + dependencies: + es-abstract-get: 1.0.0 + es-define-property: 1.0.1 + es-errors: 1.3.0 + is-callable: 1.2.7 + is-date-object: 1.1.0 + is-symbol: 1.1.1 + + esbuild@0.28.2: + optionalDependencies: + '@esbuild/aix-ppc64': 0.28.2 + '@esbuild/android-arm': 0.28.2 + '@esbuild/android-arm64': 0.28.2 + '@esbuild/android-x64': 0.28.2 + '@esbuild/darwin-arm64': 0.28.2 + '@esbuild/darwin-x64': 0.28.2 + '@esbuild/freebsd-arm64': 0.28.2 + '@esbuild/freebsd-x64': 0.28.2 + '@esbuild/linux-arm': 0.28.2 + '@esbuild/linux-arm64': 0.28.2 + '@esbuild/linux-ia32': 0.28.2 + '@esbuild/linux-loong64': 0.28.2 + '@esbuild/linux-mips64el': 0.28.2 + '@esbuild/linux-ppc64': 0.28.2 + '@esbuild/linux-riscv64': 0.28.2 + '@esbuild/linux-s390x': 0.28.2 + '@esbuild/linux-x64': 0.28.2 + '@esbuild/netbsd-arm64': 0.28.2 + '@esbuild/netbsd-x64': 0.28.2 + '@esbuild/openbsd-arm64': 0.28.2 + '@esbuild/openbsd-x64': 0.28.2 + '@esbuild/openharmony-arm64': 0.28.2 + '@esbuild/sunos-x64': 0.28.2 + '@esbuild/win32-arm64': 0.28.2 + '@esbuild/win32-ia32': 0.28.2 + '@esbuild/win32-x64': 0.28.2 + + escalade@3.2.0: {} + + escape-html@1.0.3: {} + + escape-string-regexp@1.0.5: {} + + escape-string-regexp@2.0.0: {} + + escape-string-regexp@4.0.0: {} + + escodegen@2.1.0: + dependencies: + esprima: 4.0.1 + estraverse: 5.3.0 + esutils: 2.0.3 + optionalDependencies: + source-map: 0.6.1 + + eslint-config-expo@57.0.1(eslint@9.39.5(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3): + dependencies: + '@typescript-eslint/eslint-plugin': 8.67.0(@typescript-eslint/parser@8.67.0(eslint@9.39.5(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@9.39.5(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@typescript-eslint/parser': 8.67.0(eslint@9.39.5(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + eslint: 9.39.5(supports-color@10.2.2) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.5(supports-color@10.2.2))(supports-color@10.2.2) + eslint-plugin-expo: 1.1.0(eslint@9.39.5(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.67.0(eslint@9.39.5(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.5(supports-color@10.2.2))(supports-color@10.2.2) + eslint-plugin-react: 7.37.5(eslint@9.39.5(supports-color@10.2.2)) + eslint-plugin-react-hooks: 7.1.1(eslint@9.39.5(supports-color@10.2.2))(supports-color@10.2.2) + globals: 16.5.0 + transitivePeerDependencies: + - eslint-import-resolver-webpack + - eslint-plugin-import-x + - supports-color + - typescript + + eslint-import-resolver-node@0.3.10(supports-color@10.2.2): + dependencies: + debug: 3.2.7(supports-color@10.2.2) + is-core-module: 2.16.2 + resolve: 2.0.0-next.7 + transitivePeerDependencies: + - supports-color + + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.5(supports-color@10.2.2))(supports-color@10.2.2): + dependencies: + '@nolyfill/is-core-module': 1.0.39 + debug: 4.4.3(supports-color@10.2.2) + eslint: 9.39.5(supports-color@10.2.2) + get-tsconfig: 4.14.3 + is-bun-module: 2.0.0 + stable-hash: 0.0.5 + tinyglobby: 0.2.17 + unrs-resolver: 1.12.2 + optionalDependencies: + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.67.0(eslint@9.39.5(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.5(supports-color@10.2.2))(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + eslint-module-utils@2.14.0(@typescript-eslint/parser@8.67.0(eslint@9.39.5(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint-import-resolver-node@0.3.10(supports-color@10.2.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.5(supports-color@10.2.2))(supports-color@10.2.2): + dependencies: + debug: 3.2.7(supports-color@10.2.2) + optionalDependencies: + '@typescript-eslint/parser': 8.67.0(eslint@9.39.5(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + eslint: 9.39.5(supports-color@10.2.2) + eslint-import-resolver-node: 0.3.10(supports-color@10.2.2) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.5(supports-color@10.2.2))(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + eslint-plugin-expo@1.1.0(eslint@9.39.5(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3): + dependencies: + '@typescript-eslint/types': 8.67.0 + '@typescript-eslint/utils': 8.67.0(eslint@9.39.5(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + eslint: 9.39.5(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + - typescript + + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.67.0(eslint@9.39.5(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.5(supports-color@10.2.2))(supports-color@10.2.2): + dependencies: + '@rtsao/scc': 1.1.0 + array-includes: 3.1.9 + array.prototype.findlastindex: 1.2.6 + array.prototype.flat: 1.3.3 + array.prototype.flatmap: 1.3.3 + debug: 3.2.7(supports-color@10.2.2) + doctrine: 2.1.0 + eslint: 9.39.5(supports-color@10.2.2) + eslint-import-resolver-node: 0.3.10(supports-color@10.2.2) + eslint-module-utils: 2.14.0(@typescript-eslint/parser@8.67.0(eslint@9.39.5(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint-import-resolver-node@0.3.10(supports-color@10.2.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.5(supports-color@10.2.2))(supports-color@10.2.2) + hasown: 2.0.4 + is-core-module: 2.16.2 + is-glob: 4.0.3 + minimatch: 3.1.5 + object.fromentries: 2.0.8 + object.groupby: 1.0.3 + object.values: 1.2.1 + semver: 6.3.1 + string.prototype.trimend: 1.0.10 + tsconfig-paths: 3.15.0 + optionalDependencies: + '@typescript-eslint/parser': 8.67.0(eslint@9.39.5(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + + eslint-plugin-react-hooks@7.1.1(eslint@9.39.5(supports-color@10.2.2))(supports-color@10.2.2): + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/parser': 7.29.8 + eslint: 9.39.5(supports-color@10.2.2) + hermes-parser: 0.25.1 + zod: 3.25.76 + zod-validation-error: 4.0.2(zod@3.25.76) + transitivePeerDependencies: + - supports-color + + eslint-plugin-react@7.37.5(eslint@9.39.5(supports-color@10.2.2)): + dependencies: + array-includes: 3.1.9 + array.prototype.findlast: 1.2.5 + array.prototype.flatmap: 1.3.3 + array.prototype.tosorted: 1.1.4 + doctrine: 2.1.0 + es-iterator-helpers: 1.4.0 + eslint: 9.39.5(supports-color@10.2.2) + estraverse: 5.3.0 + hasown: 2.0.4 + jsx-ast-utils: 3.3.5 + minimatch: 3.1.5 + object.entries: 1.1.9 + object.fromentries: 2.0.8 + object.values: 1.2.1 + prop-types: 15.8.1 + resolve: 2.0.0-next.7 + semver: 6.3.1 + string.prototype.matchall: 4.0.12 + string.prototype.repeat: 1.0.0 + + eslint-scope@8.4.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.1: {} + + eslint-visitor-keys@5.0.1: {} + + eslint@9.39.5(supports-color@10.2.2): + dependencies: + '@eslint-community/eslint-utils': 4.10.1(eslint@9.39.5(supports-color@10.2.2)) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.21.2(supports-color@10.2.2) + '@eslint/config-helpers': 0.4.2 + '@eslint/core': 0.17.0 + '@eslint/eslintrc': 3.3.6(supports-color@10.2.2) + '@eslint/js': 9.39.5 + '@eslint/plugin-kit': 0.4.1 + '@humanfs/node': 0.16.8 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.9 + ajv: 6.15.0 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3(supports-color@10.2.2) + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.5 + natural-compare: 1.4.0 + optionator: 0.9.4 + transitivePeerDependencies: + - supports-color + + espree@10.4.0: + dependencies: + acorn: 8.18.0 + acorn-jsx: 5.3.2(acorn@8.18.0) + eslint-visitor-keys: 4.2.1 + + esprima@4.0.1: {} + + esquery@1.7.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + esutils@2.0.3: {} + + etag@1.8.1: {} + + event-target-polyfill@0.0.4: {} + + event-target-shim@5.0.1: {} + + execa@5.1.1: + dependencies: + cross-spawn: 7.0.6 + get-stream: 6.0.1 + human-signals: 2.1.0 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + + exit@0.1.2: {} + + expect@29.7.0: + dependencies: + '@jest/expect-utils': 29.7.0 + jest-get-type: 29.6.3 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + + expo-application@57.0.2(expo@57.0.15): + dependencies: + expo: 57.0.15(c6665681e457b98938e269e908821e4f) + + expo-asset@57.0.13(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2)(typescript@6.0.3): + dependencies: + '@expo/image-utils': 0.11.4(supports-color@10.2.2)(typescript@6.0.3) + expo: 57.0.15(c6665681e457b98938e269e908821e4f) + expo-constants: 57.0.13(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(supports-color@10.2.2) + react: 19.2.3 + react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + - typescript + + expo-camera@57.0.4(@types/emscripten@1.41.5)(expo@57.0.15)(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3): + dependencies: + barcode-detector: 3.2.2(@types/emscripten@1.41.5) + expo: 57.0.15(c6665681e457b98938e269e908821e4f) + react: 19.2.3 + react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + optionalDependencies: + react-native-web: 0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + transitivePeerDependencies: + - '@types/emscripten' + + expo-constants@57.0.13(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(supports-color@10.2.2): + dependencies: + '@expo/env': 2.4.2(supports-color@10.2.2) + expo: 57.0.15(c6665681e457b98938e269e908821e4f) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + expo-crypto@57.0.2(expo@57.0.15): + dependencies: + expo: 57.0.15(c6665681e457b98938e269e908821e4f) + + expo-dev-client@57.0.14(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2)): + dependencies: + expo: 57.0.15(c6665681e457b98938e269e908821e4f) + expo-dev-launcher: 57.0.14(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2)) + expo-dev-menu: 57.0.14(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2)) + expo-dev-menu-interface: 57.0.0(expo@57.0.15) + expo-manifests: 57.0.1(expo@57.0.15) + expo-updates-interface: 57.0.1(expo@57.0.15) + transitivePeerDependencies: + - react-native + + expo-dev-launcher@57.0.14(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2)): + dependencies: + '@expo/schema-utils': 57.0.2 + expo: 57.0.15(c6665681e457b98938e269e908821e4f) + expo-dev-menu: 57.0.14(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2)) + expo-manifests: 57.0.1(expo@57.0.15) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + + expo-dev-menu-interface@57.0.0(expo@57.0.15): + dependencies: + expo: 57.0.15(c6665681e457b98938e269e908821e4f) + + expo-dev-menu@57.0.14(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2)): + dependencies: + expo: 57.0.15(c6665681e457b98938e269e908821e4f) + expo-dev-menu-interface: 57.0.0(expo@57.0.15) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + + expo-device@57.0.1(expo@57.0.15): + dependencies: + expo: 57.0.15(c6665681e457b98938e269e908821e4f) + ua-parser-js: 0.7.41 + + expo-document-picker@57.0.1(expo@57.0.15): + dependencies: + expo: 57.0.15(c6665681e457b98938e269e908821e4f) + + expo-eas-client@57.0.1: {} + + expo-file-system@57.0.5(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2)): + dependencies: + expo: 57.0.15(c6665681e457b98938e269e908821e4f) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + + expo-file-system@57.0.6(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2)): + dependencies: + expo: 57.0.15(c6665681e457b98938e269e908821e4f) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + + expo-font@57.0.1(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3): + dependencies: + expo: 57.0.15(c6665681e457b98938e269e908821e4f) + fontfaceobserver: 2.3.0 + react: 19.2.3 + react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + + expo-glass-effect@57.0.1(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3): + dependencies: + expo: 57.0.15(c6665681e457b98938e269e908821e4f) + react: 19.2.3 + react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + + expo-haptics@57.0.2(expo@57.0.15): + dependencies: + expo: 57.0.15(c6665681e457b98938e269e908821e4f) + + expo-image-loader@57.0.1(expo@57.0.15): + dependencies: + expo: 57.0.15(c6665681e457b98938e269e908821e4f) + + expo-image-picker@57.0.12(expo@57.0.15): + dependencies: + expo: 57.0.15(c6665681e457b98938e269e908821e4f) + expo-image-loader: 57.0.1(expo@57.0.15) + + expo-image@57.0.3(expo@57.0.15)(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3): + dependencies: + expo: 57.0.15(c6665681e457b98938e269e908821e4f) + react: 19.2.3 + react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + sf-symbols-typescript: 2.2.0 + optionalDependencies: + react-native-web: 0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + + expo-json-utils@57.0.1: {} + + expo-keep-awake@57.0.1(expo@57.0.15)(react@19.2.3): + dependencies: + expo: 57.0.15(c6665681e457b98938e269e908821e4f) + react: 19.2.3 + + expo-linking@57.0.7(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2): + dependencies: + expo-constants: 57.0.13(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(supports-color@10.2.2) + invariant: 2.2.4 + react: 19.2.3 + react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + transitivePeerDependencies: + - expo + - supports-color + + expo-localization@57.0.1(expo@57.0.15)(react@19.2.3): + dependencies: + expo: 57.0.15(c6665681e457b98938e269e908821e4f) + react: 19.2.3 + rtl-detect: 1.1.2 + + expo-manifests@57.0.1(expo@57.0.15): + dependencies: + expo: 57.0.15(c6665681e457b98938e269e908821e4f) + expo-json-utils: 57.0.1 + + expo-modules-autolinking@57.0.10(supports-color@10.2.2)(typescript@6.0.3): + dependencies: + '@expo/require-utils': 57.0.4(supports-color@10.2.2)(typescript@6.0.3) + '@expo/spawn-async': 1.8.0 + chalk: 4.1.2 + commander: 7.2.0 + transitivePeerDependencies: + - supports-color + - typescript + + expo-modules-core@57.0.12(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3): + dependencies: + '@expo/expo-modules-macros-plugin': 0.6.1 + expo-modules-jsi: 57.0.5(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2)) + invariant: 2.2.4 + react: 19.2.3 + react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + optionalDependencies: + react-native-worklets: 0.10.1(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2) + + expo-modules-jsi@57.0.5(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2)): + dependencies: + react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + + expo-notifications@57.0.13(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2)(typescript@6.0.3): + dependencies: + '@expo/image-utils': 0.11.4(supports-color@10.2.2)(typescript@6.0.3) + abort-controller: 3.0.0 + badgin: 1.2.3 + expo: 57.0.15(c6665681e457b98938e269e908821e4f) + expo-application: 57.0.2(expo@57.0.15) + expo-constants: 57.0.13(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(supports-color@10.2.2) + react: 19.2.3 + react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + - typescript + + expo-router@57.0.15(4297cc0b7880e6d80de6379924b45edb): + dependencies: + '@expo/log-box': 57.0.3(@expo/dom-webview@57.0.1)(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + '@expo/metro-runtime': 57.0.12(@expo/log-box@57.0.3)(expo@57.0.15)(react-dom@19.2.3(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + '@expo/schema-utils': 57.0.2 + '@expo/ui': 57.0.12(ce4128d08365c77ff16d6b531794f812) + '@radix-ui/react-slot': 1.3.3(@types/react@19.2.18)(react@19.2.3) + '@radix-ui/react-tabs': 1.1.21(@types/react@19.2.18)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-native-masked-view/masked-view': 0.3.2(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + client-only: 0.0.1 + color: 4.2.3 + debug: 4.4.3(supports-color@10.2.2) + escape-string-regexp: 4.0.0 + expo: 57.0.15(c6665681e457b98938e269e908821e4f) + expo-constants: 57.0.13(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(supports-color@10.2.2) + expo-glass-effect: 57.0.1(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + expo-linking: 57.0.7(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2) + expo-server: 57.0.3 + expo-symbols: 57.0.2(expo-font@57.0.1)(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + fast-deep-equal: 3.1.3 + invariant: 2.2.4 + nanoid: 3.3.18 + query-string: 7.1.3 + react: 19.2.3 + react-fast-compare: 3.2.2 + react-is: 19.2.8 + react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + react-native-drawer-layout: 4.2.10(d071066867e06f069216be9cfd299def) + react-native-safe-area-context: 5.7.0(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + react-native-screens: 4.26.2(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + server-only: 0.0.1 + sf-symbols-typescript: 2.2.0 + shallowequal: 1.1.0 + standard-navigation: 0.0.5 + vaul: 1.1.2(@types/react@19.2.18)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + optionalDependencies: + '@testing-library/react-native': 14.0.1(jest@29.7.0(@types/node@26.2.0)(supports-color@10.2.2))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(test-renderer@1.2.0(@types/react@19.2.18)(react@19.2.3)) + react-dom: 19.2.3(react@19.2.3) + react-native-gesture-handler: 2.32.0(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + react-native-reanimated: 4.5.1(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + react-native-web: 0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + transitivePeerDependencies: + - '@babel/core' + - '@types/react' + - '@types/react-dom' + - expo-font + - react-native-worklets + - supports-color + + expo-secure-store@57.0.1(expo@57.0.15): + dependencies: + expo: 57.0.15(c6665681e457b98938e269e908821e4f) + + expo-server@57.0.3: {} + + expo-splash-screen@57.0.7(expo@57.0.15)(supports-color@10.2.2)(typescript@6.0.3): + dependencies: + '@expo/config-plugins': 57.0.8(supports-color@10.2.2)(typescript@6.0.3) + '@expo/image-utils': 0.11.4(supports-color@10.2.2)(typescript@6.0.3) + expo: 57.0.15(c6665681e457b98938e269e908821e4f) + xml2js: 0.6.0 + transitivePeerDependencies: + - supports-color + - typescript + + expo-status-bar@57.0.1(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3): + dependencies: + expo: 57.0.15(c6665681e457b98938e269e908821e4f) + react: 19.2.3 + react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + + expo-structured-headers@57.0.0: {} + + expo-symbols@57.0.2(expo-font@57.0.1)(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3): + dependencies: + '@expo-google-fonts/material-symbols': 0.4.44 + expo: 57.0.15(c6665681e457b98938e269e908821e4f) + expo-font: 57.0.1(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + react: 19.2.3 + react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + sf-symbols-typescript: 2.2.0 + + expo-system-ui@57.0.2(expo@57.0.15)(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(supports-color@10.2.2): + dependencies: + '@react-native/normalize-colors': 0.86.2 + debug: 4.4.3(supports-color@10.2.2) + expo: 57.0.15(c6665681e457b98938e269e908821e4f) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + optionalDependencies: + react-native-web: 0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + transitivePeerDependencies: + - supports-color + + expo-updates-interface@57.0.1(expo@57.0.15): + dependencies: + expo: 57.0.15(c6665681e457b98938e269e908821e4f) + + expo-updates@57.0.16(expo-dev-client@57.0.14(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2)))(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2): + dependencies: + '@expo/code-signing-certificates': 0.0.6 + '@expo/plist': 0.8.1 + '@expo/spawn-async': 1.8.0 + arg: 4.1.3 + chalk: 4.1.2 + debug: 4.4.3(supports-color@10.2.2) + expo: 57.0.15(c6665681e457b98938e269e908821e4f) + expo-eas-client: 57.0.1 + expo-manifests: 57.0.1(expo@57.0.15) + expo-structured-headers: 57.0.0 + expo-updates-interface: 57.0.1(expo@57.0.15) + getenv: 2.0.0 + glob: 13.0.6 + ignore: 5.3.2 + nullthrows: 1.1.1 + react: 19.2.3 + react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + resolve-from: 5.0.0 + optionalDependencies: + expo-dev-client: 57.0.14(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2)) + transitivePeerDependencies: + - supports-color + + expo-web-browser@57.0.2(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2)): + dependencies: + expo: 57.0.15(c6665681e457b98938e269e908821e4f) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + + expo@57.0.15(c6665681e457b98938e269e908821e4f): + dependencies: + '@babel/runtime': 7.29.7 + '@expo/cli': 57.0.17(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.12)(expo-constants@57.0.13)(expo-font@57.0.1)(expo-router@57.0.15)(expo@57.0.15)(react-dom@19.2.3(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2)(typescript@6.0.3) + '@expo/config': 57.0.8(supports-color@10.2.2)(typescript@6.0.3) + '@expo/config-plugins': 57.0.8(supports-color@10.2.2)(typescript@6.0.3) + '@expo/devtools': 57.0.1(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + '@expo/fingerprint': 0.20.9(supports-color@10.2.2) + '@expo/local-build-cache-provider': 57.0.7(supports-color@10.2.2)(typescript@6.0.3) + '@expo/log-box': 57.0.3(@expo/dom-webview@57.0.1)(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + '@expo/metro': 56.0.0(supports-color@10.2.2) + '@expo/metro-config': 57.0.9(expo@57.0.15)(supports-color@10.2.2)(typescript@6.0.3) + '@ungap/structured-clone': 1.3.3 + babel-preset-expo: 57.0.7(@babel/core@7.29.7(supports-color@10.2.2))(@babel/runtime@7.29.7)(expo@57.0.15)(react-refresh@0.14.2)(supports-color@10.2.2) + expo-asset: 57.0.13(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2)(typescript@6.0.3) + expo-constants: 57.0.13(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(supports-color@10.2.2) + expo-file-system: 57.0.5(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2)) + expo-font: 57.0.1(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + expo-keep-awake: 57.0.1(expo@57.0.15)(react@19.2.3) + expo-modules-autolinking: 57.0.10(supports-color@10.2.2)(typescript@6.0.3) + expo-modules-core: 57.0.12(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + pretty-format: 29.7.0 + react: 19.2.3 + react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + react-refresh: 0.14.2 + whatwg-url-minimum: 0.1.2 + optionalDependencies: + '@expo/dom-webview': 57.0.1(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + '@expo/metro-runtime': 57.0.12(@expo/log-box@57.0.3)(expo@57.0.15)(react-dom@19.2.3(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + react-dom: 19.2.3(react@19.2.3) + react-native-web: 0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + transitivePeerDependencies: + - '@babel/core' + - bufferutil + - expo-router + - expo-widgets + - react-native-worklets + - react-server-dom-webpack + - supports-color + - typescript + - utf-8-validate + + exponential-backoff@3.1.3: {} + + fast-deep-equal@3.1.3: {} + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fb-dotslash@0.5.8: {} + + fb-watchman@2.0.2: + dependencies: + bser: 2.1.1 + + fbjs-css-vars@1.0.2: {} + + fbjs@3.0.5: + dependencies: + cross-fetch: 3.2.0 + fbjs-css-vars: 1.0.2 + loose-envify: 1.4.0 + object-assign: 4.1.1 + promise: 7.3.1 + setimmediate: 1.0.5 + ua-parser-js: 1.0.41 + transitivePeerDependencies: + - encoding + + fdir@6.5.0(picomatch@4.0.5): + optionalDependencies: + picomatch: 4.0.5 + + fetch-nodeshim@0.4.10: {} + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + filter-obj@1.1.0: {} + + finalhandler@1.1.2(supports-color@10.2.2): + dependencies: + debug: 2.6.9(supports-color@10.2.2) + encodeurl: 1.0.2 + escape-html: 1.0.3 + on-finished: 2.3.0 + parseurl: 1.3.3 + statuses: 1.5.0 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + + find-up@4.1.0: + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.4.4 + keyv: 4.5.4 + + flatted@3.4.4: {} + + flow-enums-runtime@0.0.6: {} + + fontfaceobserver@2.3.0: {} + + for-each@0.3.5: + dependencies: + is-callable: 1.2.7 + + form-data@4.0.6: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.4 + mime-types: 2.1.35 + + fresh@0.5.2: {} + + fs.realpath@1.0.0: {} + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + function.prototype.name@1.2.0: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + es-define-property: 1.0.1 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + hasown: 2.0.4 + is-callable: 1.2.7 + is-document.all: 1.0.0 + + functions-have-names@1.2.3: {} + + generator-function@2.0.1: {} + + gensync@1.0.0-beta.2: {} + + get-caller-file@2.0.5: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.4 + math-intrinsics: 1.1.0 + + get-nonce@1.0.1: {} + + get-package-type@0.1.0: {} + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.2 + + get-stream@6.0.1: {} + + get-symbol-description@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + + get-tsconfig@4.14.3: + dependencies: + resolve-pkg-maps: 1.0.0 + + getenv@2.0.0: {} + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + glob@13.0.6: + dependencies: + minimatch: 10.2.6 + minipass: 7.1.3 + path-scurry: 2.0.2 + + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.5 + once: 1.4.0 + path-is-absolute: 1.0.1 + + globals@14.0.0: {} + + globals@16.5.0: {} + + globalthis@1.0.4: + dependencies: + define-properties: 1.2.1 + gopd: 1.2.0 + + gopd@1.2.0: {} + + graceful-fs@4.2.11: {} + + has-bigints@1.1.0: {} + + has-flag@3.0.0: {} + + has-flag@4.0.0: {} + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.1 + + has-proto@1.2.0: + dependencies: + dunder-proto: 1.0.1 + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.4: + dependencies: + function-bind: 1.1.2 + + hermes-compiler@250829098.0.16: {} + + hermes-estree@0.25.1: {} + + hermes-estree@0.35.0: {} + + hermes-estree@0.36.0: {} + + hermes-estree@0.36.1: {} + + hermes-parser@0.25.1: + dependencies: + hermes-estree: 0.25.1 + + hermes-parser@0.35.0: + dependencies: + hermes-estree: 0.35.0 + + hermes-parser@0.36.0: + dependencies: + hermes-estree: 0.36.0 + + hermes-parser@0.36.1: + dependencies: + hermes-estree: 0.36.1 + + hoist-non-react-statics@3.3.2: + dependencies: + react-is: 16.13.1 + + hosted-git-info@7.0.2: + dependencies: + lru-cache: 10.4.3 + + html-encoding-sniffer@3.0.0: + dependencies: + whatwg-encoding: 2.0.0 + + html-escaper@2.0.2: {} + + html-parse-stringify@4.0.1: {} + + http-errors@2.0.1: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.2 + toidentifier: 1.0.1 + + http-proxy-agent@5.0.0(supports-color@10.2.2): + dependencies: + '@tootallnate/once': 2.0.1 + agent-base: 6.0.2(supports-color@10.2.2) + debug: 4.4.3(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + https-proxy-agent@5.0.1(supports-color@10.2.2): + dependencies: + agent-base: 6.0.2(supports-color@10.2.2) + debug: 4.4.3(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + https-proxy-agent@7.0.6(supports-color@10.2.2): + dependencies: + agent-base: 7.1.4 + debug: 4.4.3(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + human-signals@2.1.0: {} + + hyphenate-style-name@1.1.0: {} + + i18next@26.3.6(typescript@6.0.3): + optionalDependencies: + typescript: 6.0.3 + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + + ignore@5.3.2: {} + + ignore@7.0.6: {} + + image-size@1.2.1: + dependencies: + queue: 6.0.2 + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + import-local@3.2.0: + dependencies: + pkg-dir: 4.2.0 + resolve-cwd: 3.0.0 + + imurmurhash@0.1.4: {} + + indent-string@4.0.0: {} + + index-to-position@1.2.0: {} + + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + inherits@2.0.4: {} + + inline-style-prefixer@7.0.1: + dependencies: + css-in-js-utils: 3.1.0 + + internal-slot@1.1.0: + dependencies: + es-errors: 1.3.0 + hasown: 2.0.4 + side-channel: 1.1.1 + + invariant@2.2.4: + dependencies: + loose-envify: 1.4.0 + + is-array-buffer@3.0.5: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + is-arrayish@0.2.1: {} + + is-arrayish@0.3.4: {} + + is-async-function@2.1.1: + dependencies: + async-function: 1.0.0 + call-bound: 1.0.4 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-bigint@1.1.0: + dependencies: + has-bigints: 1.1.0 + + is-boolean-object@1.2.2: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-bun-module@2.0.0: + dependencies: + semver: 7.8.5 + + is-callable@1.2.7: {} + + is-core-module@2.16.2: + dependencies: + hasown: 2.0.4 + + is-data-view@1.0.2: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + is-typed-array: 1.1.15 + + is-date-object@1.1.0: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-docker@2.2.1: {} + + is-document.all@1.0.0: + dependencies: + call-bound: 1.0.4 + + is-extglob@2.1.1: {} + + is-finalizationregistry@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-fullwidth-code-point@3.0.0: {} + + is-generator-fn@2.1.0: {} + + is-generator-function@1.1.2: + dependencies: + call-bound: 1.0.4 + generator-function: 2.0.1 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-map@2.0.3: {} + + is-negative-zero@2.0.3: {} + + is-number-object@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-number@7.0.0: {} + + is-plain-obj@2.1.0: {} + + is-potential-custom-element-name@1.0.1: {} + + is-regex@1.2.1: + dependencies: + call-bound: 1.0.4 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + hasown: 2.0.4 + + is-set@2.0.3: {} + + is-shared-array-buffer@1.0.4: + dependencies: + call-bound: 1.0.4 + + is-stream@2.0.1: {} + + is-string@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-symbol@1.1.1: + dependencies: + call-bound: 1.0.4 + has-symbols: 1.1.0 + safe-regex-test: 1.1.0 + + is-typed-array@1.1.15: + dependencies: + which-typed-array: 1.1.22 + + is-weakmap@2.0.2: {} + + is-weakref@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-weakset@2.0.4: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + is-wsl@2.2.0: + dependencies: + is-docker: 2.2.1 + + isarray@2.0.5: {} + + isexe@2.0.0: {} + + istanbul-lib-coverage@3.2.2: {} + + istanbul-lib-instrument@5.2.1(supports-color@10.2.2): + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/parser': 7.29.8 + '@istanbuljs/schema': 0.1.6 + istanbul-lib-coverage: 3.2.2 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + istanbul-lib-instrument@6.0.3(supports-color@10.2.2): + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/parser': 7.29.8 + '@istanbuljs/schema': 0.1.6 + istanbul-lib-coverage: 3.2.2 + semver: 7.8.5 + transitivePeerDependencies: + - supports-color + + istanbul-lib-report@3.0.1: + dependencies: + istanbul-lib-coverage: 3.2.2 + make-dir: 4.0.0 + supports-color: 7.2.0 + + istanbul-lib-source-maps@4.0.1(supports-color@10.2.2): + dependencies: + debug: 4.4.3(supports-color@10.2.2) + istanbul-lib-coverage: 3.2.2 + source-map: 0.6.1 + transitivePeerDependencies: + - supports-color + + istanbul-reports@3.2.0: + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + + iterator.prototype@1.1.5: + dependencies: + define-data-property: 1.1.4 + es-object-atoms: 1.1.2 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + has-symbols: 1.1.0 + set-function-name: 2.0.2 + + jest-changed-files@29.7.0: + dependencies: + execa: 5.1.1 + jest-util: 29.7.0 + p-limit: 3.1.0 + + jest-circus@29.7.0(supports-color@10.2.2): + dependencies: + '@jest/environment': 29.7.0 + '@jest/expect': 29.7.0(supports-color@10.2.2) + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 26.2.0 + chalk: 4.1.2 + co: 4.6.0 + dedent: 1.7.2 + is-generator-fn: 2.1.0 + jest-each: 29.7.0 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-runtime: 29.7.0(supports-color@10.2.2) + jest-snapshot: 29.7.0(supports-color@10.2.2) + jest-util: 29.7.0 + p-limit: 3.1.0 + pretty-format: 29.7.0 + pure-rand: 6.1.0 + slash: 3.0.0 + stack-utils: 2.0.6 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + + jest-cli@29.7.0(@types/node@26.2.0)(supports-color@10.2.2): + dependencies: + '@jest/core': 29.7.0(supports-color@10.2.2) + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + chalk: 4.1.2 + create-jest: 29.7.0(@types/node@26.2.0)(supports-color@10.2.2) + exit: 0.1.2 + import-local: 3.2.0 + jest-config: 29.7.0(@types/node@26.2.0)(supports-color@10.2.2) + jest-util: 29.7.0 + jest-validate: 29.7.0 + yargs: 17.7.3 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + + jest-config@29.7.0(@types/node@26.2.0)(supports-color@10.2.2): + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@jest/test-sequencer': 29.7.0 + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + chalk: 4.1.2 + ci-info: 3.9.0 + deepmerge: 4.3.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-circus: 29.7.0(supports-color@10.2.2) + jest-environment-node: 29.7.0 + jest-get-type: 29.6.3 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-runner: 29.7.0(supports-color@10.2.2) + jest-util: 29.7.0 + jest-validate: 29.7.0 + micromatch: 4.0.8 + parse-json: 5.2.0 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 26.2.0 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + + jest-diff@29.7.0: + dependencies: + chalk: 4.1.2 + diff-sequences: 29.6.3 + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + + jest-diff@30.4.1: + dependencies: + '@jest/diff-sequences': 30.4.0 + '@jest/get-type': 30.1.0 + chalk: 4.1.2 + pretty-format: 30.4.1 + + jest-docblock@29.7.0: + dependencies: + detect-newline: 3.1.0 + + jest-each@29.7.0: + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + jest-get-type: 29.6.3 + jest-util: 29.7.0 + pretty-format: 29.7.0 + + jest-environment-jsdom@29.7.0(supports-color@10.2.2): + dependencies: + '@jest/environment': 29.7.0 + '@jest/fake-timers': 29.7.0 + '@jest/types': 29.6.3 + '@types/jsdom': 20.0.1 + '@types/node': 26.2.0 + jest-mock: 29.7.0 + jest-util: 29.7.0 + jsdom: 20.0.3(supports-color@10.2.2) + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + jest-environment-node@29.7.0: + dependencies: + '@jest/environment': 29.7.0 + '@jest/fake-timers': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 26.2.0 + jest-mock: 29.7.0 + jest-util: 29.7.0 + + jest-expo@57.0.4(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(expo@57.0.15)(jest@29.7.0(@types/node@26.2.0)(supports-color@10.2.2))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2): + dependencies: + '@jest/create-cache-key-function': 29.7.0 + '@jest/globals': 29.7.0(supports-color@10.2.2) + '@react-native/jest-preset': 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2) + babel-jest: 29.7.0(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + jest-environment-jsdom: 29.7.0(supports-color@10.2.2) + jest-snapshot: 29.7.0(supports-color@10.2.2) + jest-watch-select-projects: 2.0.0 + jest-watch-typeahead: 2.2.1(jest@29.7.0(@types/node@26.2.0)(supports-color@10.2.2)) + json5: 2.2.3 + lodash: 4.18.1 + react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + react-test-renderer: 19.2.3(react@19.2.3) + server-only: 0.0.1 + stacktrace-js: 2.0.2 + optionalDependencies: + expo: 57.0.15(c6665681e457b98938e269e908821e4f) + transitivePeerDependencies: + - '@babel/core' + - bufferutil + - canvas + - jest + - react + - supports-color + - utf-8-validate + + jest-get-type@29.6.3: {} + + jest-haste-map@29.7.0: + dependencies: + '@jest/types': 29.6.3 + '@types/graceful-fs': 4.1.9 + '@types/node': 26.2.0 + anymatch: 3.1.3 + fb-watchman: 2.0.2 + graceful-fs: 4.2.11 + jest-regex-util: 29.6.3 + jest-util: 29.7.0 + jest-worker: 29.7.0 + micromatch: 4.0.8 + walker: 1.0.8 + optionalDependencies: + fsevents: 2.3.3 + + jest-leak-detector@29.7.0: + dependencies: + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + + jest-matcher-utils@29.7.0: + dependencies: + chalk: 4.1.2 + jest-diff: 29.7.0 + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + + jest-matcher-utils@30.4.1: + dependencies: + '@jest/get-type': 30.1.0 + chalk: 4.1.2 + jest-diff: 30.4.1 + pretty-format: 30.4.1 + + jest-message-util@29.7.0: + dependencies: + '@babel/code-frame': 7.29.7 + '@jest/types': 29.6.3 + '@types/stack-utils': 2.0.3 + chalk: 4.1.2 + graceful-fs: 4.2.11 + micromatch: 4.0.8 + pretty-format: 29.7.0 + slash: 3.0.0 + stack-utils: 2.0.6 + + jest-mock@29.7.0: + dependencies: + '@jest/types': 29.6.3 + '@types/node': 26.2.0 + jest-util: 29.7.0 + + jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): + optionalDependencies: + jest-resolve: 29.7.0 + + jest-regex-util@29.6.3: {} + + jest-resolve-dependencies@29.7.0(supports-color@10.2.2): + dependencies: + jest-regex-util: 29.6.3 + jest-snapshot: 29.7.0(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + jest-resolve@29.7.0: + dependencies: + chalk: 4.1.2 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-pnp-resolver: 1.2.3(jest-resolve@29.7.0) + jest-util: 29.7.0 + jest-validate: 29.7.0 + resolve: 1.22.12 + resolve.exports: 2.0.3 + slash: 3.0.0 + + jest-runner@29.7.0(supports-color@10.2.2): + dependencies: + '@jest/console': 29.7.0 + '@jest/environment': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0(supports-color@10.2.2) + '@jest/types': 29.6.3 + '@types/node': 26.2.0 + chalk: 4.1.2 + emittery: 0.13.1 + graceful-fs: 4.2.11 + jest-docblock: 29.7.0 + jest-environment-node: 29.7.0 + jest-haste-map: 29.7.0 + jest-leak-detector: 29.7.0 + jest-message-util: 29.7.0 + jest-resolve: 29.7.0 + jest-runtime: 29.7.0(supports-color@10.2.2) + jest-util: 29.7.0 + jest-watcher: 29.7.0 + jest-worker: 29.7.0 + p-limit: 3.1.0 + source-map-support: 0.5.13 + transitivePeerDependencies: + - supports-color + + jest-runtime@29.7.0(supports-color@10.2.2): + dependencies: + '@jest/environment': 29.7.0 + '@jest/fake-timers': 29.7.0 + '@jest/globals': 29.7.0(supports-color@10.2.2) + '@jest/source-map': 29.6.3 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0(supports-color@10.2.2) + '@jest/types': 29.6.3 + '@types/node': 26.2.0 + chalk: 4.1.2 + cjs-module-lexer: 1.4.3 + collect-v8-coverage: 1.0.3 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-message-util: 29.7.0 + jest-mock: 29.7.0 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-snapshot: 29.7.0(supports-color@10.2.2) + jest-util: 29.7.0 + slash: 3.0.0 + strip-bom: 4.0.0 + transitivePeerDependencies: + - supports-color + + jest-snapshot@29.7.0(supports-color@10.2.2): + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/generator': 7.29.8 + '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/types': 7.29.8 + '@jest/expect-utils': 29.7.0 + '@jest/transform': 29.7.0(supports-color@10.2.2) + '@jest/types': 29.6.3 + babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.7(supports-color@10.2.2)) + chalk: 4.1.2 + expect: 29.7.0 + graceful-fs: 4.2.11 + jest-diff: 29.7.0 + jest-get-type: 29.6.3 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + natural-compare: 1.4.0 + pretty-format: 29.7.0 + semver: 7.8.5 + transitivePeerDependencies: + - supports-color + + jest-util@29.7.0: + dependencies: + '@jest/types': 29.6.3 + '@types/node': 26.2.0 + chalk: 4.1.2 + ci-info: 3.9.0 + graceful-fs: 4.2.11 + picomatch: 2.3.2 + + jest-validate@29.7.0: + dependencies: + '@jest/types': 29.6.3 + camelcase: 6.3.0 + chalk: 4.1.2 + jest-get-type: 29.6.3 + leven: 3.1.0 + pretty-format: 29.7.0 + + jest-watch-select-projects@2.0.0: + dependencies: + ansi-escapes: 4.3.2 + chalk: 3.0.0 + prompts: 2.4.2 + + jest-watch-typeahead@2.2.1(jest@29.7.0(@types/node@26.2.0)(supports-color@10.2.2)): + dependencies: + ansi-escapes: 6.2.1 + chalk: 4.1.2 + jest: 29.7.0(@types/node@26.2.0)(supports-color@10.2.2) + jest-regex-util: 29.6.3 + jest-watcher: 29.7.0 + slash: 5.1.0 + string-length: 5.0.1 + strip-ansi: 7.2.0 + + jest-watcher@29.7.0: + dependencies: + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 26.2.0 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + emittery: 0.13.1 + jest-util: 29.7.0 + string-length: 4.0.2 + + jest-worker@29.7.0: + dependencies: + '@types/node': 26.2.0 + jest-util: 29.7.0 + merge-stream: 2.0.0 + supports-color: 8.1.1 + + jest@29.7.0(@types/node@26.2.0)(supports-color@10.2.2): + dependencies: + '@jest/core': 29.7.0(supports-color@10.2.2) + '@jest/types': 29.6.3 + import-local: 3.2.0 + jest-cli: 29.7.0(@types/node@26.2.0)(supports-color@10.2.2) + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + + jimp-compact@0.16.1: {} + + js-levenshtein@1.1.6: {} + + js-tokens@4.0.0: {} + + js-yaml@3.15.1: + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 + + js-yaml@4.3.1: + dependencies: + argparse: 2.0.1 + + jsc-safe-url@0.2.4: {} + + jsdom@20.0.3(supports-color@10.2.2): + dependencies: + abab: 2.0.6 + acorn: 8.18.0 + acorn-globals: 7.0.1 + cssom: 0.5.0 + cssstyle: 2.3.0 + data-urls: 3.0.2 + decimal.js: 10.6.0 + domexception: 4.0.0 + escodegen: 2.1.0 + form-data: 4.0.6 + html-encoding-sniffer: 3.0.0 + http-proxy-agent: 5.0.0(supports-color@10.2.2) + https-proxy-agent: 5.0.1(supports-color@10.2.2) + is-potential-custom-element-name: 1.0.1 + nwsapi: 2.2.24 + parse5: 7.3.0 + saxes: 6.0.0 + symbol-tree: 3.2.4 + tough-cookie: 4.1.4 + w3c-xmlserializer: 4.0.0 + webidl-conversions: 7.0.0 + whatwg-encoding: 2.0.0 + whatwg-mimetype: 3.0.0 + whatwg-url: 11.0.0 + ws: 8.21.3 + xml-name-validator: 4.0.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + jsesc@3.1.0: {} + + json-buffer@3.0.1: {} + + json-parse-even-better-errors@2.3.1: {} + + json-schema-traverse@0.4.1: {} + + json-schema-traverse@1.0.0: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json5@1.0.2: + dependencies: + minimist: 1.2.8 + + json5@2.2.3: {} + + jsx-ast-utils@3.3.5: + dependencies: + array-includes: 3.1.9 + array.prototype.flat: 1.3.3 + object.assign: 4.1.7 + object.values: 1.2.1 + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + kleur@3.0.3: {} + + lan-network@0.2.1: {} + + leven@3.1.0: {} + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + lighthouse-logger@1.4.2(supports-color@10.2.2): + dependencies: + debug: 2.6.9(supports-color@10.2.2) + marky: 1.3.0 + transitivePeerDependencies: + - supports-color + + lightningcss-android-arm64@1.33.0: + optional: true + + lightningcss-darwin-arm64@1.33.0: + optional: true + + lightningcss-darwin-x64@1.33.0: + optional: true + + lightningcss-freebsd-x64@1.33.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.33.0: + optional: true + + lightningcss-linux-arm64-gnu@1.33.0: + optional: true + + lightningcss-linux-arm64-musl@1.33.0: + optional: true + + lightningcss-linux-x64-gnu@1.33.0: + optional: true + + lightningcss-linux-x64-musl@1.33.0: + optional: true + + lightningcss-win32-arm64-msvc@1.33.0: + optional: true + + lightningcss-win32-x64-msvc@1.33.0: + optional: true + + lightningcss@1.33.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.33.0 + lightningcss-darwin-arm64: 1.33.0 + lightningcss-darwin-x64: 1.33.0 + lightningcss-freebsd-x64: 1.33.0 + lightningcss-linux-arm-gnueabihf: 1.33.0 + lightningcss-linux-arm64-gnu: 1.33.0 + lightningcss-linux-arm64-musl: 1.33.0 + lightningcss-linux-x64-gnu: 1.33.0 + lightningcss-linux-x64-musl: 1.33.0 + lightningcss-win32-arm64-msvc: 1.33.0 + lightningcss-win32-x64-msvc: 1.33.0 + + lines-and-columns@1.2.4: {} + + locate-path@5.0.0: + dependencies: + p-locate: 4.1.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.debounce@4.0.8: {} + + lodash.merge@4.6.2: {} + + lodash.throttle@4.1.1: {} + + lodash@4.18.1: {} + + log-symbols@2.2.0: + dependencies: + chalk: 2.4.2 + + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + + lru-cache@10.4.3: {} + + lru-cache@11.5.2: {} + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + make-dir@4.0.0: + dependencies: + semver: 7.8.5 + + makeerror@1.0.12: + dependencies: + tmpl: 1.0.5 + + marky@1.3.0: {} + + math-intrinsics@1.1.0: {} + + memoize-one@5.2.1: {} + + memoize-one@6.0.0: {} + + merge-options@3.0.4: + dependencies: + is-plain-obj: 2.1.0 + + merge-stream@2.0.0: {} + + metro-babel-transformer@0.84.4(supports-color@10.2.2): + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + flow-enums-runtime: 0.0.6 + hermes-parser: 0.35.0 + metro-cache-key: 0.84.4 + nullthrows: 1.1.1 + transitivePeerDependencies: + - supports-color + + metro-babel-transformer@0.84.5(supports-color@10.2.2): + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + flow-enums-runtime: 0.0.6 + hermes-parser: 0.35.0 + metro-cache-key: 0.84.5 + nullthrows: 1.1.1 + transitivePeerDependencies: + - supports-color + + metro-cache-key@0.84.4: + dependencies: + flow-enums-runtime: 0.0.6 + + metro-cache-key@0.84.5: + dependencies: + flow-enums-runtime: 0.0.6 + + metro-cache@0.84.4(supports-color@10.2.2): + dependencies: + exponential-backoff: 3.1.3 + flow-enums-runtime: 0.0.6 + https-proxy-agent: 7.0.6(supports-color@10.2.2) + metro-core: 0.84.4 + transitivePeerDependencies: + - supports-color + + metro-cache@0.84.5(supports-color@10.2.2): + dependencies: + exponential-backoff: 3.1.3 + flow-enums-runtime: 0.0.6 + https-proxy-agent: 7.0.6(supports-color@10.2.2) + metro-core: 0.84.5 + transitivePeerDependencies: + - supports-color + + metro-config@0.84.4(supports-color@10.2.2): + dependencies: + connect: 3.7.0(supports-color@10.2.2) + flow-enums-runtime: 0.0.6 + jest-validate: 29.7.0 + metro: 0.84.4(supports-color@10.2.2) + metro-cache: 0.84.4(supports-color@10.2.2) + metro-core: 0.84.4 + metro-runtime: 0.84.4 + yaml: 2.9.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + metro-config@0.84.5(supports-color@10.2.2): + dependencies: + connect: 3.7.0(supports-color@10.2.2) + flow-enums-runtime: 0.0.6 + jest-validate: 29.7.0 + metro: 0.84.5(supports-color@10.2.2) + metro-cache: 0.84.5(supports-color@10.2.2) + metro-core: 0.84.5 + metro-runtime: 0.84.5 + yaml: 2.9.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + metro-core@0.84.4: + dependencies: + flow-enums-runtime: 0.0.6 + lodash.throttle: 4.1.1 + metro-resolver: 0.84.4 + + metro-core@0.84.5: + dependencies: + flow-enums-runtime: 0.0.6 + lodash.throttle: 4.1.1 + metro-resolver: 0.84.5 + + metro-file-map@0.84.4(supports-color@10.2.2): + dependencies: + debug: 4.4.3(supports-color@10.2.2) + fb-watchman: 2.0.2 + flow-enums-runtime: 0.0.6 + graceful-fs: 4.2.11 + invariant: 2.2.4 + jest-worker: 29.7.0 + micromatch: 4.0.8 + nullthrows: 1.1.1 + walker: 1.0.8 + transitivePeerDependencies: + - supports-color + + metro-file-map@0.84.5(supports-color@10.2.2): + dependencies: + debug: 4.4.3(supports-color@10.2.2) + fb-watchman: 2.0.2 + flow-enums-runtime: 0.0.6 + graceful-fs: 4.2.11 + invariant: 2.2.4 + jest-worker: 29.7.0 + micromatch: 4.0.8 + nullthrows: 1.1.1 + walker: 1.0.8 + transitivePeerDependencies: + - supports-color + + metro-minify-terser@0.84.4: + dependencies: + flow-enums-runtime: 0.0.6 + terser: 5.50.0 + + metro-minify-terser@0.84.5: + dependencies: + flow-enums-runtime: 0.0.6 + terser: 5.50.0 + + metro-resolver@0.84.4: + dependencies: + flow-enums-runtime: 0.0.6 + + metro-resolver@0.84.5: + dependencies: + flow-enums-runtime: 0.0.6 + + metro-runtime@0.84.4: + dependencies: + '@babel/runtime': 7.29.7 + flow-enums-runtime: 0.0.6 + + metro-runtime@0.84.5: + dependencies: + '@babel/runtime': 7.29.7 + flow-enums-runtime: 0.0.6 + + metro-source-map@0.84.4(supports-color@10.2.2): + dependencies: + '@babel/traverse': 7.29.8(supports-color@10.2.2) + '@babel/types': 7.29.8 + flow-enums-runtime: 0.0.6 + invariant: 2.2.4 + metro-symbolicate: 0.84.4(supports-color@10.2.2) + nullthrows: 1.1.1 + ob1: 0.84.4 + source-map: 0.5.7 + vlq: 1.0.1 + transitivePeerDependencies: + - supports-color + + metro-source-map@0.84.5(supports-color@10.2.2): + dependencies: + '@babel/traverse': 7.29.8(supports-color@10.2.2) + '@babel/types': 7.29.8 + flow-enums-runtime: 0.0.6 + invariant: 2.2.4 + metro-symbolicate: 0.84.5(supports-color@10.2.2) + nullthrows: 1.1.1 + ob1: 0.84.5 + source-map: 0.5.7 + vlq: 1.0.1 + transitivePeerDependencies: + - supports-color + + metro-symbolicate@0.84.4(supports-color@10.2.2): + dependencies: + flow-enums-runtime: 0.0.6 + invariant: 2.2.4 + metro-source-map: 0.84.4(supports-color@10.2.2) + nullthrows: 1.1.1 + source-map: 0.5.7 + vlq: 1.0.1 + transitivePeerDependencies: + - supports-color + + metro-symbolicate@0.84.5(supports-color@10.2.2): + dependencies: + flow-enums-runtime: 0.0.6 + invariant: 2.2.4 + metro-source-map: 0.84.5(supports-color@10.2.2) + nullthrows: 1.1.1 + source-map: 0.5.7 + vlq: 1.0.1 + transitivePeerDependencies: + - supports-color + + metro-transform-plugins@0.84.4(supports-color@10.2.2): + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/generator': 7.29.8 + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.8(supports-color@10.2.2) + flow-enums-runtime: 0.0.6 + nullthrows: 1.1.1 + transitivePeerDependencies: + - supports-color + + metro-transform-plugins@0.84.5(supports-color@10.2.2): + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/generator': 7.29.8 + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.8(supports-color@10.2.2) + flow-enums-runtime: 0.0.6 + nullthrows: 1.1.1 + transitivePeerDependencies: + - supports-color + + metro-transform-worker@0.84.4(supports-color@10.2.2): + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/generator': 7.29.8 + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 + flow-enums-runtime: 0.0.6 + metro: 0.84.4(supports-color@10.2.2) + metro-babel-transformer: 0.84.4(supports-color@10.2.2) + metro-cache: 0.84.4(supports-color@10.2.2) + metro-cache-key: 0.84.4 + metro-minify-terser: 0.84.4 + metro-source-map: 0.84.4(supports-color@10.2.2) + metro-transform-plugins: 0.84.4(supports-color@10.2.2) + nullthrows: 1.1.1 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + metro-transform-worker@0.84.5(supports-color@10.2.2): + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/generator': 7.29.8 + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 + flow-enums-runtime: 0.0.6 + metro: 0.84.5(supports-color@10.2.2) + metro-babel-transformer: 0.84.5(supports-color@10.2.2) + metro-cache: 0.84.5(supports-color@10.2.2) + metro-cache-key: 0.84.5 + metro-minify-terser: 0.84.5 + metro-source-map: 0.84.5(supports-color@10.2.2) + metro-transform-plugins: 0.84.5(supports-color@10.2.2) + nullthrows: 1.1.1 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + metro@0.84.4(supports-color@10.2.2): + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/generator': 7.29.8 + '@babel/parser': 7.29.8 + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.8(supports-color@10.2.2) + '@babel/types': 7.29.8 + accepts: 2.0.0 + ci-info: 2.0.0 + connect: 3.7.0(supports-color@10.2.2) + debug: 4.4.3(supports-color@10.2.2) + error-stack-parser: 2.1.4 + flow-enums-runtime: 0.0.6 + graceful-fs: 4.2.11 + hermes-parser: 0.35.0 + image-size: 1.2.1 + invariant: 2.2.4 + jest-worker: 29.7.0 + jsc-safe-url: 0.2.4 + lodash.throttle: 4.1.1 + metro-babel-transformer: 0.84.4(supports-color@10.2.2) + metro-cache: 0.84.4(supports-color@10.2.2) + metro-cache-key: 0.84.4 + metro-config: 0.84.4(supports-color@10.2.2) + metro-core: 0.84.4 + metro-file-map: 0.84.4(supports-color@10.2.2) + metro-resolver: 0.84.4 + metro-runtime: 0.84.4 + metro-source-map: 0.84.4(supports-color@10.2.2) + metro-symbolicate: 0.84.4(supports-color@10.2.2) + metro-transform-plugins: 0.84.4(supports-color@10.2.2) + metro-transform-worker: 0.84.4(supports-color@10.2.2) + mime-types: 3.0.2 + nullthrows: 1.1.1 + serialize-error: 2.1.0 + source-map: 0.5.7 + throat: 5.0.0 + ws: 7.5.13 + yargs: 17.7.3 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + metro@0.84.5(supports-color@10.2.2): + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/generator': 7.29.8 + '@babel/parser': 7.29.8 + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.8(supports-color@10.2.2) + '@babel/types': 7.29.8 + accepts: 2.0.0 + ci-info: 2.0.0 + connect: 3.7.0(supports-color@10.2.2) + debug: 4.4.3(supports-color@10.2.2) + error-stack-parser: 2.1.4 + flow-enums-runtime: 0.0.6 + graceful-fs: 4.2.11 + hermes-parser: 0.35.0 + invariant: 2.2.4 + jest-worker: 29.7.0 + jsc-safe-url: 0.2.4 + lodash.throttle: 4.1.1 + metro-babel-transformer: 0.84.5(supports-color@10.2.2) + metro-cache: 0.84.5(supports-color@10.2.2) + metro-cache-key: 0.84.5 + metro-config: 0.84.5(supports-color@10.2.2) + metro-core: 0.84.5 + metro-file-map: 0.84.5(supports-color@10.2.2) + metro-resolver: 0.84.5 + metro-runtime: 0.84.5 + metro-source-map: 0.84.5(supports-color@10.2.2) + metro-symbolicate: 0.84.5(supports-color@10.2.2) + metro-transform-plugins: 0.84.5(supports-color@10.2.2) + metro-transform-worker: 0.84.5(supports-color@10.2.2) + mime-types: 3.0.2 + nullthrows: 1.1.1 + serialize-error: 2.1.0 + source-map: 0.5.7 + throat: 5.0.0 + ws: 7.5.13 + yargs: 17.7.3 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.2 + + mime-db@1.52.0: {} + + mime-db@1.54.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mime-types@3.0.2: + dependencies: + mime-db: 1.54.0 + + mime@1.6.0: {} + + mimic-fn@1.2.0: {} + + mimic-fn@2.1.0: {} + + min-indent@1.0.1: {} + + minimatch@10.2.6: + dependencies: + brace-expansion: 5.0.9 + + minimatch@3.1.5: + dependencies: + brace-expansion: 1.1.18 + + minimatch@5.1.9: + dependencies: + brace-expansion: 2.1.4 + + minimist@1.2.8: {} + + minipass@7.1.3: {} + + mkdirp@1.0.4: {} + + ms@2.0.0: {} + + ms@2.1.3: {} + + multitars@1.0.2: {} + + nanoid@3.3.18: {} + + napi-postinstall@0.3.4: {} + + natural-compare@1.4.0: {} + + negotiator@0.6.3: {} + + negotiator@0.6.4: {} + + negotiator@1.0.0: {} + + node-exports-info@1.6.2: + dependencies: + array.prototype.flatmap: 1.3.3 + es-errors: 1.3.0 + object.entries: 1.1.9 + semver: 6.3.1 + + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + + node-forge@1.4.0: {} + + node-int64@0.4.0: {} + + node-releases@2.0.53: {} + + normalize-path@3.0.0: {} + + npm-package-arg@11.0.3: + dependencies: + hosted-git-info: 7.0.2 + proc-log: 4.2.0 + semver: 7.8.5 + validate-npm-package-name: 5.0.1 + + npm-run-path@4.0.1: + dependencies: + path-key: 3.1.1 + + nullthrows@1.1.1: {} + + nwsapi@2.2.24: {} + + ob1@0.84.4: + dependencies: + flow-enums-runtime: 0.0.6 + + ob1@0.84.5: + dependencies: + flow-enums-runtime: 0.0.6 + + object-assign@4.1.1: {} + + object-inspect@1.13.4: {} + + object-keys@1.1.1: {} + + object.assign@4.1.7: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.2 + has-symbols: 1.1.0 + object-keys: 1.1.1 + + object.entries@1.1.9: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.2 + + object.fromentries@2.0.8: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-object-atoms: 1.1.2 + + object.groupby@1.0.3: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + + object.values@1.2.1: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.2 + + on-finished@2.3.0: + dependencies: + ee-first: 1.1.1 + + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + + on-headers@1.1.0: {} + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + onetime@2.0.1: + dependencies: + mimic-fn: 1.2.0 + + onetime@5.1.2: + dependencies: + mimic-fn: 2.1.0 + + open@7.4.2: + dependencies: + is-docker: 2.2.1 + is-wsl: 2.2.0 + + openapi-fetch@0.17.0: + dependencies: + openapi-typescript-helpers: 0.1.0 + + openapi-typescript-helpers@0.1.0: {} + + openapi-typescript@7.13.0(typescript@6.0.3): + dependencies: + '@redocly/openapi-core': 1.34.19(supports-color@10.2.2) + ansi-colors: 4.1.3 + change-case: 5.4.4 + parse-json: 8.3.0 + supports-color: 10.2.2 + typescript: 6.0.3 + yargs-parser: 21.1.1 + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + ora@3.4.0: + dependencies: + chalk: 2.4.2 + cli-cursor: 2.1.0 + cli-spinners: 2.9.2 + log-symbols: 2.2.0 + strip-ansi: 5.2.0 + wcwidth: 1.0.1 + + own-keys@1.0.2: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + object-keys: 1.1.1 + safe-push-apply: 1.0.0 + + p-limit@2.3.0: + dependencies: + p-try: 2.2.0 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@4.1.0: + dependencies: + p-limit: 2.3.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + p-try@2.2.0: {} + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.29.7 + error-ex: 1.3.4 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + + parse-json@8.3.0: + dependencies: + '@babel/code-frame': 7.29.7 + index-to-position: 1.2.0 + type-fest: 4.41.0 + + parse-png@2.1.0: + dependencies: + pngjs: 3.4.0 + + parse5@7.3.0: + dependencies: + entities: 6.0.1 + + parseurl@1.3.3: {} + + partysocket@1.3.0(react@19.2.3): + dependencies: + event-target-polyfill: 0.0.4 + optionalDependencies: + react: 19.2.3 + + path-exists@4.0.0: {} + + path-is-absolute@1.0.1: {} + + path-key@3.1.1: {} + + path-parse@1.0.7: {} + + path-scurry@2.0.2: + dependencies: + lru-cache: 11.5.2 + minipass: 7.1.3 + + picocolors@1.1.1: {} + + picomatch@2.3.2: {} + + picomatch@4.0.5: {} + + pirates@4.0.7: {} + + pkg-dir@4.2.0: + dependencies: + find-up: 4.1.0 + + plist@3.1.1: + dependencies: + '@xmldom/xmldom': 0.9.11 + base64-js: 1.5.1 + xmlbuilder: 15.1.1 + + pluralize@8.0.0: {} + + pngjs@3.4.0: {} + + possible-typed-array-names@1.1.0: {} + + postcss-value-parser@4.2.0: {} + + postcss@8.5.26: + dependencies: + nanoid: 3.3.18 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prelude-ls@1.2.1: {} + + prettier@3.9.6: {} + + pretty-format@29.7.0: + dependencies: + '@jest/schemas': 29.6.3 + ansi-styles: 5.2.0 + react-is: 18.3.1 + + pretty-format@30.4.1: + dependencies: + '@jest/schemas': 30.4.1 + ansi-styles: 5.2.0 + react-is-18: react-is@18.3.1 + react-is-19: react-is@19.2.8 + + proc-log@4.2.0: {} + + progress@2.0.3: {} + + promise@7.3.1: + dependencies: + asap: 2.0.6 + + promise@8.3.0: + dependencies: + asap: 2.0.6 + + prompts@2.4.2: + dependencies: + kleur: 3.0.3 + sisteransi: 1.0.5 + + prop-types@15.8.1: + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + + psl@1.15.0: + dependencies: + punycode: 2.3.1 + + punycode@2.3.1: {} + + pure-rand@6.1.0: {} + + query-string@7.1.3: + dependencies: + decode-uri-component: 0.2.2 + filter-obj: 1.1.0 + split-on-first: 1.1.0 + strict-uri-encode: 2.0.0 + + querystringify@2.2.0: {} + + queue@6.0.2: + dependencies: + inherits: 2.0.4 + + range-parser@1.2.1: {} + + react-devtools-core@6.1.5: + dependencies: + shell-quote: 1.10.0 + ws: 7.5.13 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + react-dom@19.2.3(react@19.2.3): + dependencies: + react: 19.2.3 + scheduler: 0.27.0 + + react-fast-compare@3.2.2: {} + + react-freeze@1.0.4(react@19.2.3): + dependencies: + react: 19.2.3 + + react-i18next@17.0.11(i18next@26.3.6(typescript@6.0.3))(react-dom@19.2.3(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(typescript@6.0.3): + dependencies: + '@babel/runtime': 7.29.7 + html-parse-stringify: 4.0.1 + i18next: 26.3.6(typescript@6.0.3) + react: 19.2.3 + use-sync-external-store: 1.6.0(react@19.2.3) + optionalDependencies: + react-dom: 19.2.3(react@19.2.3) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + typescript: 6.0.3 + + react-is@16.13.1: {} + + react-is@18.3.1: {} + + react-is@19.2.8: {} + + react-native-drawer-layout@4.2.10(d071066867e06f069216be9cfd299def): + dependencies: + color: 4.2.3 + react: 19.2.3 + react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + react-native-gesture-handler: 2.32.0(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + react-native-reanimated: 4.5.1(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + use-latest-callback: 0.2.6(react@19.2.3) + + react-native-gesture-handler@2.32.0(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3): + dependencies: + '@egjs/hammerjs': 2.0.17 + '@types/react-test-renderer': 19.1.0 + hoist-non-react-statics: 3.3.2 + invariant: 2.2.4 + react: 19.2.3 + react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + + react-native-is-edge-to-edge@1.3.1(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3): + dependencies: + react: 19.2.3 + react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + + react-native-reanimated@4.5.1(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3): + dependencies: + react: 19.2.3 + react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + react-native-is-edge-to-edge: 1.3.1(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + react-native-worklets: 0.10.1(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2) + semver: 7.8.5 + + react-native-safe-area-context@5.7.0(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3): + dependencies: + react: 19.2.3 + react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + + react-native-screens@4.26.2(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3): + dependencies: + react: 19.2.3 + react-freeze: 1.0.4(react@19.2.3) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + warn-once: 0.1.1 + + react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3): + dependencies: + '@babel/runtime': 7.29.7 + '@react-native/normalize-colors': 0.74.89 + fbjs: 3.0.5 + inline-style-prefixer: 7.0.1 + memoize-one: 6.0.0 + nullthrows: 1.1.1 + postcss-value-parser: 4.2.0 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + styleq: 0.1.3 + transitivePeerDependencies: + - encoding + + react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2): + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/plugin-transform-arrow-functions': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-class-properties': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-classes': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-nullish-coalescing-operator': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-optional-chaining': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-shorthand-properties': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-template-literals': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-unicode-regex': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/preset-typescript': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/types': 7.29.8 + '@react-native/metro-config': 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + convert-source-map: 2.0.0 + react: 19.2.3 + react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + semver: 7.8.5 + transitivePeerDependencies: + - supports-color + + react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2): + dependencies: + '@react-native/assets-registry': 0.86.2 + '@react-native/codegen': 0.86.2(@babel/core@7.29.7(supports-color@10.2.2)) + '@react-native/community-cli-plugin': 0.86.2(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(supports-color@10.2.2) + '@react-native/gradle-plugin': 0.86.2 + '@react-native/js-polyfills': 0.86.2 + '@react-native/normalize-colors': 0.86.2 + '@react-native/virtualized-lists': 0.86.2(@types/react@19.2.18)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + abort-controller: 3.0.0 + anser: 1.4.10 + ansi-regex: 5.0.1 + babel-plugin-syntax-hermes-parser: 0.36.0 + base64-js: 1.5.1 + commander: 12.1.0 + flow-enums-runtime: 0.0.6 + hermes-compiler: 250829098.0.16 + invariant: 2.2.4 + memoize-one: 5.2.1 + metro-runtime: 0.84.5 + metro-source-map: 0.84.5(supports-color@10.2.2) + nullthrows: 1.1.1 + pretty-format: 29.7.0 + promise: 8.3.0 + react: 19.2.3 + react-devtools-core: 6.1.5 + react-refresh: 0.14.2 + regenerator-runtime: 0.13.11 + scheduler: 0.27.0 + semver: 7.8.5 + stacktrace-parser: 0.1.11 + tinyglobby: 0.2.17 + whatwg-fetch: 3.6.20 + ws: 7.5.13 + yargs: 17.7.3 + optionalDependencies: + '@react-native/jest-preset': 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2) + '@types/react': 19.2.18 + transitivePeerDependencies: + - '@babel/core' + - '@react-native-community/cli' + - '@react-native/metro-config' + - bufferutil + - supports-color + - utf-8-validate + + react-reconciler@0.33.0(react@19.2.3): + dependencies: + react: 19.2.3 + scheduler: 0.27.0 + + react-refresh@0.14.2: {} + + react-remove-scroll-bar@2.3.8(@types/react@19.2.18)(react@19.2.3): + dependencies: + react: 19.2.3 + react-style-singleton: 2.2.3(@types/react@19.2.18)(react@19.2.3) + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.18 + + react-remove-scroll@2.7.2(@types/react@19.2.18)(react@19.2.3): + dependencies: + react: 19.2.3 + react-remove-scroll-bar: 2.3.8(@types/react@19.2.18)(react@19.2.3) + react-style-singleton: 2.2.3(@types/react@19.2.18)(react@19.2.3) + tslib: 2.8.1 + use-callback-ref: 1.3.3(@types/react@19.2.18)(react@19.2.3) + use-sidecar: 1.1.3(@types/react@19.2.18)(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.18 + + react-style-singleton@2.2.3(@types/react@19.2.18)(react@19.2.3): + dependencies: + get-nonce: 1.0.1 + react: 19.2.3 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.18 + + react-test-renderer@19.2.3(react@19.2.3): + dependencies: + react: 19.2.3 + react-is: 19.2.8 + scheduler: 0.27.0 + + react@19.2.3: {} + + redent@3.0.0: + dependencies: + indent-string: 4.0.0 + strip-indent: 3.0.0 + + reflect.getprototypeof@1.0.10: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + which-builtin-type: 1.2.1 + + regenerate-unicode-properties@10.2.2: + dependencies: + regenerate: 1.4.2 + + regenerate@1.4.2: {} + + regenerator-runtime@0.13.11: {} + + regexp.prototype.flags@1.5.4: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-errors: 1.3.0 + get-proto: 1.0.1 + gopd: 1.2.0 + set-function-name: 2.0.2 + + regexpu-core@6.4.0: + dependencies: + regenerate: 1.4.2 + regenerate-unicode-properties: 10.2.2 + regjsgen: 0.8.0 + regjsparser: 0.13.2 + unicode-match-property-ecmascript: 2.0.0 + unicode-match-property-value-ecmascript: 2.2.1 + + regjsgen@0.8.0: {} + + regjsparser@0.13.2: + dependencies: + jsesc: 3.1.0 + + require-directory@2.1.1: {} + + require-from-string@2.0.2: {} + + requires-port@1.0.0: {} + + resolve-cwd@3.0.0: + dependencies: + resolve-from: 5.0.0 + + resolve-from@4.0.0: {} + + resolve-from@5.0.0: {} + + resolve-pkg-maps@1.0.0: {} + + resolve-workspace-root@2.0.1: {} + + resolve.exports@2.0.3: {} + + resolve@1.22.12: + dependencies: + es-errors: 1.3.0 + is-core-module: 2.16.2 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + resolve@2.0.0-next.7: + dependencies: + es-errors: 1.3.0 + is-core-module: 2.16.2 + node-exports-info: 1.6.2 + object-keys: 1.1.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + restore-cursor@2.0.0: + dependencies: + onetime: 2.0.1 + signal-exit: 3.0.7 + + rtl-detect@1.1.2: {} + + safe-array-concat@1.1.4: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + has-symbols: 1.1.0 + isarray: 2.0.5 + + safe-buffer@5.2.1: {} + + safe-push-apply@1.0.0: + dependencies: + es-errors: 1.3.0 + isarray: 2.0.5 + + safe-regex-test@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-regex: 1.2.1 + + safer-buffer@2.1.2: {} + + sandbox-cli-detector@0.2.0: {} + + sax@1.6.1: {} + + saxes@6.0.0: + dependencies: + xmlchars: 2.2.0 + + scheduler@0.27.0: {} + + semver@6.3.1: {} + + semver@7.8.5: {} + + send@0.19.2(supports-color@10.2.2): + dependencies: + debug: 2.6.9(supports-color@10.2.2) + depd: 2.0.0 + destroy: 1.2.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 0.5.2 + http-errors: 2.0.1 + mime: 1.6.0 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + serialize-error@2.1.0: {} + + serve-static@1.16.3(supports-color@10.2.2): + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 0.19.2(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + server-only@0.0.1: {} + + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + + set-function-name@2.0.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + + set-proto@1.0.0: + dependencies: + dunder-proto: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + + setimmediate@1.0.5: {} + + setprototypeof@1.2.0: {} + + sf-symbols-typescript@2.2.0: {} + + shallowequal@1.1.0: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + shell-quote@1.10.0: {} + + side-channel-list@1.0.1: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.1: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.1 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + signal-exit@3.0.7: {} + + simple-plist@1.3.1: + dependencies: + bplist-creator: 0.1.0 + bplist-parser: 0.3.1 + plist: 3.1.1 + + simple-swizzle@0.2.4: + dependencies: + is-arrayish: 0.3.4 + + sisteransi@1.0.5: {} + + slash@3.0.0: {} + + slash@5.1.0: {} + + slugify@1.6.9: {} + + source-map-js@1.2.1: {} + + source-map-support@0.5.13: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map-support@0.5.21: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map@0.5.6: {} + + source-map@0.5.7: {} + + source-map@0.6.1: {} + + split-on-first@1.1.0: {} + + sprintf-js@1.0.3: {} + + stable-hash@0.0.5: {} + + stack-generator@2.0.10: + dependencies: + stackframe: 1.3.4 + + stack-utils@2.0.6: + dependencies: + escape-string-regexp: 2.0.0 + + stackframe@1.3.4: {} + + stacktrace-gps@3.1.2: + dependencies: + source-map: 0.5.6 + stackframe: 1.3.4 + + stacktrace-js@2.0.2: + dependencies: + error-stack-parser: 2.1.4 + stack-generator: 2.0.10 + stacktrace-gps: 3.1.2 + + stacktrace-parser@0.1.11: + dependencies: + type-fest: 0.7.1 + + standard-navigation@0.0.5: {} + + statuses@1.5.0: {} + + statuses@2.0.2: {} + + stop-iteration-iterator@1.1.0: + dependencies: + es-errors: 1.3.0 + internal-slot: 1.1.0 + + stream-buffers@2.2.0: {} + + strict-uri-encode@2.0.0: {} + + string-length@4.0.2: + dependencies: + char-regex: 1.0.2 + strip-ansi: 6.0.1 + + string-length@5.0.1: + dependencies: + char-regex: 2.0.2 + strip-ansi: 7.2.0 + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string.prototype.matchall@4.0.12: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + regexp.prototype.flags: 1.5.4 + set-function-name: 2.0.2 + side-channel: 1.1.1 + + string.prototype.repeat@1.0.0: + dependencies: + define-properties: 1.2.1 + es-abstract: 1.24.2 + + string.prototype.trim@1.2.11: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-data-property: 1.1.4 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-object-atoms: 1.1.2 + has-property-descriptors: 1.0.2 + safe-regex-test: 1.1.0 + + string.prototype.trimend@1.0.10: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.2 + + string.prototype.trimstart@1.0.8: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-object-atoms: 1.1.2 + + strip-ansi@5.2.0: + dependencies: + ansi-regex: 4.1.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.2.0: + dependencies: + ansi-regex: 6.3.0 + + strip-bom@3.0.0: {} + + strip-bom@4.0.0: {} + + strip-final-newline@2.0.0: {} + + strip-indent@3.0.0: + dependencies: + min-indent: 1.0.1 + + strip-json-comments@3.1.1: {} + + structured-headers@0.4.1: {} + + styleq@0.1.3: {} + + supports-color@10.2.2: {} + + supports-color@5.5.0: + dependencies: + has-flag: 3.0.0 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-color@8.1.1: + dependencies: + has-flag: 4.0.0 + + supports-hyperlinks@2.3.0: + dependencies: + has-flag: 4.0.0 + supports-color: 7.2.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + symbol-tree@3.2.4: {} + + tagged-tag@1.0.0: {} + + terminal-link@2.1.1: + dependencies: + ansi-escapes: 4.3.2 + supports-hyperlinks: 2.3.0 + + terser@5.50.0: + dependencies: + '@jridgewell/source-map': 0.3.11 + acorn: 8.18.0 + commander: 2.20.3 + source-map-support: 0.5.21 + + test-exclude@6.0.0: + dependencies: + '@istanbuljs/schema': 0.1.6 + glob: 7.2.3 + minimatch: 3.1.5 + + test-renderer@1.2.0(@types/react@19.2.18)(react@19.2.3): + dependencies: + '@types/react-reconciler': 0.33.0(@types/react@19.2.18) + react: 19.2.3 + react-reconciler: 0.33.0(react@19.2.3) + transitivePeerDependencies: + - '@types/react' + + throat@5.0.0: {} + + tinyglobby@0.2.17: + dependencies: + fdir: 6.5.0(picomatch@4.0.5) + picomatch: 4.0.5 + + tmpl@1.0.5: {} + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + toidentifier@1.0.1: {} + + toqr@0.1.1: {} + + tough-cookie@4.1.4: + dependencies: + psl: 1.15.0 + punycode: 2.3.1 + universalify: 0.2.0 + url-parse: 1.5.10 + + tr46@0.0.3: {} + + tr46@3.0.0: + dependencies: + punycode: 2.3.1 + + ts-api-utils@2.5.0(typescript@6.0.3): + dependencies: + typescript: 6.0.3 + + tsconfig-paths@3.15.0: + dependencies: + '@types/json5': 0.0.29 + json5: 1.0.2 + minimist: 1.2.8 + strip-bom: 3.0.0 + + tslib@2.8.1: {} + + tsx@4.23.12: + dependencies: + esbuild: 0.28.2 + optionalDependencies: + fsevents: 2.3.3 + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + type-detect@4.0.8: {} + + type-fest@0.21.3: {} + + type-fest@0.7.1: {} + + type-fest@4.41.0: {} + + type-fest@5.8.0: + dependencies: + tagged-tag: 1.0.0 + + typed-array-buffer@1.0.3: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-typed-array: 1.1.15 + + typed-array-byte-length@1.0.3: + dependencies: + call-bind: 1.0.9 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + + typed-array-byte-offset@1.0.4: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.9 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + reflect.getprototypeof: 1.0.10 + + typed-array-length@1.0.8: + dependencies: + call-bind: 1.0.9 + for-each: 0.3.5 + gopd: 1.2.0 + is-typed-array: 1.1.15 + possible-typed-array-names: 1.1.0 + reflect.getprototypeof: 1.0.10 + + typescript@6.0.3: {} + + ua-parser-js@0.7.41: {} + + ua-parser-js@1.0.41: {} + + unbox-primitive@1.1.0: + dependencies: + call-bound: 1.0.4 + has-bigints: 1.1.0 + has-symbols: 1.1.0 + which-boxed-primitive: 1.1.1 + + undici-types@8.3.0: {} + + unicode-canonical-property-names-ecmascript@2.0.1: {} + + unicode-match-property-ecmascript@2.0.0: + dependencies: + unicode-canonical-property-names-ecmascript: 2.0.1 + unicode-property-aliases-ecmascript: 2.2.0 + + unicode-match-property-value-ecmascript@2.2.1: {} + + unicode-property-aliases-ecmascript@2.2.0: {} + + universalify@0.2.0: {} + + unpipe@1.0.0: {} + + unrs-resolver@1.12.2: + dependencies: + napi-postinstall: 0.3.4 + optionalDependencies: + '@unrs/resolver-binding-android-arm-eabi': 1.12.2 + '@unrs/resolver-binding-android-arm64': 1.12.2 + '@unrs/resolver-binding-darwin-arm64': 1.12.2 + '@unrs/resolver-binding-darwin-x64': 1.12.2 + '@unrs/resolver-binding-freebsd-x64': 1.12.2 + '@unrs/resolver-binding-linux-arm-gnueabihf': 1.12.2 + '@unrs/resolver-binding-linux-arm-musleabihf': 1.12.2 + '@unrs/resolver-binding-linux-arm64-gnu': 1.12.2 + '@unrs/resolver-binding-linux-arm64-musl': 1.12.2 + '@unrs/resolver-binding-linux-loong64-gnu': 1.12.2 + '@unrs/resolver-binding-linux-loong64-musl': 1.12.2 + '@unrs/resolver-binding-linux-ppc64-gnu': 1.12.2 + '@unrs/resolver-binding-linux-riscv64-gnu': 1.12.2 + '@unrs/resolver-binding-linux-riscv64-musl': 1.12.2 + '@unrs/resolver-binding-linux-s390x-gnu': 1.12.2 + '@unrs/resolver-binding-linux-x64-gnu': 1.12.2 + '@unrs/resolver-binding-linux-x64-musl': 1.12.2 + '@unrs/resolver-binding-openharmony-arm64': 1.12.2 + '@unrs/resolver-binding-wasm32-wasi': 1.12.2 + '@unrs/resolver-binding-win32-arm64-msvc': 1.12.2 + '@unrs/resolver-binding-win32-ia32-msvc': 1.12.2 + '@unrs/resolver-binding-win32-x64-msvc': 1.12.2 + + update-browserslist-db@1.3.1(browserslist@4.28.8): + dependencies: + browserslist: 4.28.8 + escalade: 3.2.0 + picocolors: 1.1.1 + + uri-js-replace@1.0.1: {} + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + url-parse@1.5.10: + dependencies: + querystringify: 2.2.0 + requires-port: 1.0.0 + + use-callback-ref@1.3.3(@types/react@19.2.18)(react@19.2.3): + dependencies: + react: 19.2.3 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.18 + + use-latest-callback@0.2.6(react@19.2.3): + dependencies: + react: 19.2.3 + + use-sidecar@1.1.3(@types/react@19.2.18)(react@19.2.3): + dependencies: + detect-node-es: 1.1.0 + react: 19.2.3 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.18 + + use-sync-external-store@1.6.0(react@19.2.3): + dependencies: + react: 19.2.3 + + utils-merge@1.0.1: {} + + uuid@7.0.3: {} + + v8-to-istanbul@9.3.0: + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + '@types/istanbul-lib-coverage': 2.0.6 + convert-source-map: 2.0.0 + + validate-npm-package-name@5.0.1: {} + + vary@1.1.2: {} + + vaul@1.1.2(@types/react@19.2.18)(react-dom@19.2.3(react@19.2.3))(react@19.2.3): + dependencies: + '@radix-ui/react-dialog': 1.1.23(@types/react@19.2.18)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + transitivePeerDependencies: + - '@types/react' + - '@types/react-dom' + + vlq@1.0.1: {} + + w3c-xmlserializer@4.0.0: + dependencies: + xml-name-validator: 4.0.0 + + walker@1.0.8: + dependencies: + makeerror: 1.0.12 + + warn-once@0.1.1: {} + + wcwidth@1.0.1: + dependencies: + defaults: 1.0.4 + + webidl-conversions@3.0.1: {} + + webidl-conversions@7.0.0: {} + + whatwg-encoding@2.0.0: + dependencies: + iconv-lite: 0.6.3 + + whatwg-fetch@3.6.20: {} + + whatwg-mimetype@3.0.0: {} + + whatwg-url-minimum@0.1.2: {} + + whatwg-url@11.0.0: + dependencies: + tr46: 3.0.0 + webidl-conversions: 7.0.0 + + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + + which-boxed-primitive@1.1.1: + dependencies: + is-bigint: 1.1.0 + is-boolean-object: 1.2.2 + is-number-object: 1.1.1 + is-string: 1.1.1 + is-symbol: 1.1.1 + + which-builtin-type@1.2.1: + dependencies: + call-bound: 1.0.4 + function.prototype.name: 1.2.0 + has-tostringtag: 1.0.2 + is-async-function: 2.1.1 + is-date-object: 1.1.0 + is-finalizationregistry: 1.1.1 + is-generator-function: 1.1.2 + is-regex: 1.2.1 + is-weakref: 1.1.1 + isarray: 2.0.5 + which-boxed-primitive: 1.1.1 + which-collection: 1.0.2 + which-typed-array: 1.1.22 + + which-collection@1.0.2: + dependencies: + is-map: 2.0.3 + is-set: 2.0.3 + is-weakmap: 2.0.2 + is-weakset: 2.0.4 + + which-typed-array@1.1.22: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.9 + call-bound: 1.0.4 + for-each: 0.3.5 + get-proto: 1.0.1 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + word-wrap@1.2.5: {} + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrappy@1.0.2: {} + + write-file-atomic@4.0.2: + dependencies: + imurmurhash: 0.1.4 + signal-exit: 3.0.7 + + ws@7.5.13: {} + + ws@8.21.3: {} + + xcode@3.0.1: + dependencies: + simple-plist: 1.3.1 + uuid: 7.0.3 + + xml-name-validator@4.0.0: {} + + xml2js@0.6.0: + dependencies: + sax: 1.6.1 + xmlbuilder: 11.0.1 + + xmlbuilder@11.0.1: {} + + xmlbuilder@15.1.1: {} + + xmlchars@2.2.0: {} + + y18n@5.0.8: {} + + yallist@3.1.1: {} + + yaml-ast-parser@0.0.43: {} + + yaml@2.9.0: {} + + yargs-parser@21.1.1: {} + + yargs@17.7.3: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + yocto-queue@0.1.0: {} + + zod-validation-error@4.0.2(zod@3.25.76): + dependencies: + zod: 3.25.76 + + zod@3.25.76: {} + + zustand@5.0.15(@types/react@19.2.18)(react@19.2.3)(use-sync-external-store@1.6.0(react@19.2.3)): + optionalDependencies: + '@types/react': 19.2.18 + react: 19.2.3 + use-sync-external-store: 1.6.0(react@19.2.3) + + zxing-wasm@3.1.3(@types/emscripten@1.41.5): + dependencies: + '@types/emscripten': 1.41.5 + type-fest: 5.8.0 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..6436997 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,28 @@ +allowBuilds: + esbuild: true + unrs-resolver: true +minimumReleaseAgeExclude: + - '@expo/cli@57.0.17' + - '@expo/fingerprint@0.20.9' + - '@expo/local-build-cache-provider@57.0.7' + - '@expo/metro-config@57.0.9' + - '@expo/metro-runtime@57.0.12' + - '@expo/prebuild-config@57.0.13' + - '@expo/router-server@57.0.7' + - '@expo/ui@57.0.12' + - expo-asset@57.0.13 + - expo-constants@57.0.13 + - expo-file-system@57.0.5 || 57.0.6 + - expo-linking@57.0.7 + - expo-modules-core@57.0.12 + - expo-modules-jsi@57.0.5 + - expo-router@57.0.15 + - expo@57.0.15 + - expo-camera@57.0.4 + - expo-image-picker@57.0.12 + - expo-updates@57.0.16 + - expo-dev-client@57.0.14 + - expo-dev-launcher@57.0.14 + - expo-dev-menu@57.0.14 + - expo-notifications@57.0.13 + - expo-haptics@57.0.2 diff --git a/scripts/generate-api-client.ts b/scripts/generate-api-client.ts new file mode 100644 index 0000000..390498f --- /dev/null +++ b/scripts/generate-api-client.ts @@ -0,0 +1,112 @@ +#!/usr/bin/env -S pnpm exec tsx +/** + * Regenerates the typed API client from the aha.chat builder app's OpenAPI spec. + * + * Usage: + * pnpm generate:api + * API_BASE_URL=https://staging.example.com pnpm generate:api + * API_SPEC_PATH=/api/public-spec.json pnpm generate:api + * + * Fetches `{API_BASE_URL}{API_SPEC_PATH}` (default `/api/spec.json`, the full oRPC router spec — + * includes the bearer/session-authenticated routes the app needs, unlike `/api/public-spec.json` + * which only covers the workspace-token surface) and runs it through openapi-typescript to + * produce src/api/generated/schema.ts (a `paths` type consumed by src/api/client.ts via + * openapi-fetch). + * + * This script is NOT wired into the build — the aha.chat backend is not guaranteed to be running + * locally, and this repo must be buildable without it. On any failure (spec unreachable, bad + * response, generation error) it prints a clear message and exits non-zero without touching the + * previously generated file. + */ +import { spawn } from 'node:child_process'; +import { existsSync } from 'node:fs'; +import path from 'node:path'; + +import dotenv from 'dotenv'; + +dotenv.config(); + +const DEFAULT_API_BASE_URL = 'http://localhost:3123'; +const DEFAULT_SPEC_PATH = '/api/spec.json'; +const OUTPUT_FILE = path.join(__dirname, '..', 'src', 'api', 'generated', 'schema.ts'); +const FETCH_TIMEOUT_MS = 5000; + +function resolveApiBaseUrl(): string { + return process.env.API_BASE_URL ?? DEFAULT_API_BASE_URL; +} + +function resolveSpecPath(): string { + return process.env.API_SPEC_PATH ?? DEFAULT_SPEC_PATH; +} + +async function checkSpecIsReachable(specUrl: string): Promise<void> { + const controller = new AbortController(); + const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS); + + try { + const response = await fetch(specUrl, { signal: controller.signal }); + + if (!response.ok) { + throw new Error(`Received HTTP ${response.status} ${response.statusText}`); + } + } finally { + clearTimeout(timeout); + } +} + +function runOpenApiTypescript(specUrl: string): Promise<void> { + return new Promise((resolve, reject) => { + const child = spawn('pnpm', ['exec', 'openapi-typescript', specUrl, '--output', OUTPUT_FILE], { + stdio: 'inherit', + shell: process.platform === 'win32', + }); + + child.on('error', reject); + child.on('exit', (code) => { + if (code === 0) { + resolve(); + return; + } + reject(new Error(`openapi-typescript exited with code ${code}`)); + }); + }); +} + +async function main(): Promise<void> { + const apiBaseUrl = resolveApiBaseUrl(); + const specUrl = new URL(resolveSpecPath(), apiBaseUrl).toString(); + + console.log(`[generate:api] Fetching OpenAPI spec from ${specUrl} ...`); + + try { + await checkSpecIsReachable(specUrl); + } catch (error) { + const reason = error instanceof Error ? error.message : String(error); + console.error(` +[generate:api] Could not reach the OpenAPI spec at ${specUrl}. +[generate:api] Reason: ${reason} +[generate:api] +[generate:api] This is expected if the aha.chat builder app isn't running locally, or if its +[generate:api] Phase 1 backend work (bearer auth + new REST endpoints) hasn't landed yet. +[generate:api] Start it from the aha.chat repo (pnpm --filter builder dev) and try again, or set +[generate:api] API_BASE_URL to point at a reachable environment. +[generate:api] +[generate:api] Leaving the previously generated client untouched (${ + existsSync(OUTPUT_FILE) ? 'exists' : 'does not exist yet — using the placeholder type' + }). +`); + process.exitCode = 1; + return; + } + + try { + await runOpenApiTypescript(specUrl); + console.log(`[generate:api] Wrote ${path.relative(process.cwd(), OUTPUT_FILE)}`); + } catch (error) { + const reason = error instanceof Error ? error.message : String(error); + console.error(`[generate:api] Generation failed: ${reason}`); + process.exitCode = 1; + } +} + +main(); diff --git a/scripts/i18n-sync.ts b/scripts/i18n-sync.ts new file mode 100644 index 0000000..3a4fc00 --- /dev/null +++ b/scripts/i18n-sync.ts @@ -0,0 +1,64 @@ +/** + * Adds any key present in en.json but missing from another locale file, using the English value + * as a placeholder (untranslated but functionally present — i18next falls back to showing the key + * path itself otherwise, which is worse than an English string). Never removes or overwrites an + * existing translated value. Run with `npx tsx scripts/i18n-sync.ts`. + */ +import { readFileSync, readdirSync, writeFileSync } from 'node:fs'; +import { join } from 'node:path'; + +type LocaleTree = { [key: string]: string | LocaleTree }; + +const LOCALES_DIR = join(__dirname, '..', 'src', 'i18n', 'locales'); + +function mergeMissing( + base: LocaleTree, + target: LocaleTree, +): { merged: LocaleTree; addedCount: number } { + let addedCount = 0; + const merged: LocaleTree = { ...target }; + + for (const [key, baseValue] of Object.entries(base)) { + const targetValue = target[key]; + if (targetValue === undefined) { + merged[key] = baseValue; + addedCount += typeof baseValue === 'object' ? countLeaves(baseValue) : 1; + } else if (typeof baseValue === 'object' && typeof targetValue === 'object') { + const result = mergeMissing(baseValue, targetValue); + merged[key] = result.merged; + addedCount += result.addedCount; + } + } + + return { merged, addedCount }; +} + +function countLeaves(tree: LocaleTree): number { + return Object.values(tree).reduce<number>( + (sum, value) => sum + (typeof value === 'object' ? countLeaves(value) : 1), + 0, + ); +} + +function main() { + const enPath = join(LOCALES_DIR, 'en.json'); + const en = JSON.parse(readFileSync(enPath, 'utf-8')) as LocaleTree; + + const files = readdirSync(LOCALES_DIR).filter( + (name) => name.endsWith('.json') && name !== 'en.json', + ); + + for (const file of files) { + const filePath = join(LOCALES_DIR, file); + const existing = JSON.parse(readFileSync(filePath, 'utf-8')) as LocaleTree; + const { merged, addedCount } = mergeMissing(en, existing); + + if (addedCount > 0) { + writeFileSync(filePath, `${JSON.stringify(merged, null, 2)}\n`); + // eslint-disable-next-line no-console -- CLI script output, not app runtime code. + console.log(`${file}: added ${addedCount} missing key(s)`); + } + } +} + +main(); diff --git a/scripts/reset-project.js b/scripts/reset-project.js new file mode 100644 index 0000000..6a152ea --- /dev/null +++ b/scripts/reset-project.js @@ -0,0 +1,114 @@ +#!/usr/bin/env node + +/** + * This script is used to reset the project to a blank state. + * It deletes or moves the /src and /scripts directories to /example based on user input and creates a new /src/app directory with an index.tsx and _layout.tsx file. + * You can remove the `reset-project` script from package.json and safely delete this file after running it. + */ + +const fs = require('fs'); +const path = require('path'); +const readline = require('readline'); + +const root = process.cwd(); +const oldDirs = ['src', 'scripts']; +const exampleDir = 'example'; +const newAppDir = 'src/app'; +const exampleDirPath = path.join(root, exampleDir); + +const indexContent = `import { Text, View, StyleSheet } from "react-native"; + +export default function Index() { + return ( + <View style={styles.container}> + <Text>Edit src/app/index.tsx to edit this screen.</Text> + </View> + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + alignItems: "center", + justifyContent: "center", + }, +}); +`; + +const layoutContent = `import { Stack } from "expo-router"; + +export default function RootLayout() { + return <Stack />; +} +`; + +const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, +}); + +const moveDirectories = async (userInput) => { + try { + if (userInput === 'y') { + // Create the app-example directory + await fs.promises.mkdir(exampleDirPath, { recursive: true }); + console.log(`📁 /${exampleDir} directory created.`); + } + + // Move old directories to new app-example directory or delete them + for (const dir of oldDirs) { + const oldDirPath = path.join(root, dir); + if (fs.existsSync(oldDirPath)) { + if (userInput === 'y') { + const newDirPath = path.join(root, exampleDir, dir); + await fs.promises.rename(oldDirPath, newDirPath); + console.log(`➡️ /${dir} moved to /${exampleDir}/${dir}.`); + } else { + await fs.promises.rm(oldDirPath, { recursive: true, force: true }); + console.log(`❌ /${dir} deleted.`); + } + } else { + console.log(`➡️ /${dir} does not exist, skipping.`); + } + } + + // Create new /src/app directory + const newAppDirPath = path.join(root, newAppDir); + await fs.promises.mkdir(newAppDirPath, { recursive: true }); + console.log('\n📁 New /src/app directory created.'); + + // Create index.tsx + const indexPath = path.join(newAppDirPath, 'index.tsx'); + await fs.promises.writeFile(indexPath, indexContent); + console.log('📄 src/app/index.tsx created.'); + + // Create _layout.tsx + const layoutPath = path.join(newAppDirPath, '_layout.tsx'); + await fs.promises.writeFile(layoutPath, layoutContent); + console.log('📄 src/app/_layout.tsx created.'); + + console.log('\n✅ Project reset complete. Next steps:'); + console.log( + `1. Run \`npx expo start\` to start a development server.\n2. Edit src/app/index.tsx to edit the main screen.\n3. Put all your application code in /src, only screens and layout files should be in /src/app.${ + userInput === 'y' + ? `\n4. Delete the /${exampleDir} directory when you're done referencing it.` + : '' + }`, + ); + } catch (error) { + console.error(`❌ Error during script execution: ${error.message}`); + } +}; + +rl.question( + 'Do you want to move existing files to /example instead of deleting them? (Y/n): ', + (answer) => { + const userInput = answer.trim().toLowerCase() || 'y'; + if (userInput === 'y' || userInput === 'n') { + moveDirectories(userInput).finally(() => rl.close()); + } else { + console.log("❌ Invalid input. Please enter 'Y' or 'N'."); + rl.close(); + } + }, +); diff --git a/src/api/auth-endpoints.ts b/src/api/auth-endpoints.ts new file mode 100644 index 0000000..eb6aa4a --- /dev/null +++ b/src/api/auth-endpoints.ts @@ -0,0 +1,151 @@ +import { env } from '@/config/env'; + +import { setAuthToken } from './auth-token'; + +/** + * Hand-rolled fetch calls against aha.chat's better-auth REST routes (`/api/auth/[...all]`). + * These are NOT part of the oRPC router the generated schema.ts covers, so they can't go through + * the typed apiClient — better-auth ships its own REST surface with its own error shape + * (`{ message, code }`, no `status`/`defined`, unlike oRPC's `{ code, status, message, defined }`). + */ +const AUTH_BASE_URL = `${env.apiBaseUrl}/api/auth`; + +export interface SessionUser { + id: string; + email: string; + name: string | null; + mustChangePassword: boolean; +} + +export class AuthApiError extends Error { + readonly code?: string; + + constructor(message: string, code?: string) { + super(message); + this.name = 'AuthApiError'; + this.code = code; + } +} + +interface BetterAuthErrorBody { + message?: string; + code?: string; +} + +async function throwIfError(response: Response): Promise<void> { + if (response.ok) return; + + let body: BetterAuthErrorBody = {}; + try { + body = (await response.json()) as BetterAuthErrorBody; + } catch { + // Non-JSON error body — fall back to statusText below. + } + throw new AuthApiError(body.message ?? response.statusText, body.code); +} + +/** + * Signs in with email/password. Captures the `set-auth-token` response header — the bearer() + * plugin mirrors the session cookie there for non-browser clients — and persists it via + * setAuthToken before returning the session user. + */ +export async function signInWithEmail(email: string, password: string): Promise<SessionUser> { + const response = await fetch(`${AUTH_BASE_URL}/sign-in/email`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ email, password }), + }); + + await throwIfError(response); + + const token = response.headers.get('set-auth-token'); + if (!token) { + throw new AuthApiError('Sign-in succeeded but no auth token was returned.'); + } + await setAuthToken(token); + + const body = (await response.json()) as { user: SessionUser }; + return body.user; +} + +/** Fetches the current session using the stored bearer token (attached by the caller via + * Authorization header) — returns null when there is no active session (never throws for that). */ +export async function getSession(token: string): Promise<SessionUser | null> { + const response = await fetch(`${AUTH_BASE_URL}/get-session`, { + method: 'GET', + headers: { Authorization: `Bearer ${token}` }, + }); + + if (!response.ok) { + return null; + } + + const body = (await response.json()) as { user: SessionUser } | null; + return body?.user ?? null; +} + +export async function requestPasswordReset(email: string): Promise<void> { + const response = await fetch(`${AUTH_BASE_URL}/forget-password`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ email, redirectTo: '/' }), + }); + await throwIfError(response); +} + +/** Changes the current user's password. Used for the forced mustChangePassword flow — the + * backend's session-cookie rotation (nextCookies + revokeOtherSessions) doesn't apply to bearer + * clients, so we re-fetch the session afterwards rather than relying on an updated cookie. */ +export async function changePassword( + token: string, + currentPassword: string, + newPassword: string, +): Promise<void> { + const response = await fetch(`${AUTH_BASE_URL}/change-password`, { + method: 'POST', + headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` }, + body: JSON.stringify({ currentPassword, newPassword, revokeOtherSessions: true }), + }); + await throwIfError(response); +} + +export async function signOut(token: string): Promise<void> { + const response = await fetch(`${AUTH_BASE_URL}/sign-out`, { + method: 'POST', + headers: { Authorization: `Bearer ${token}` }, + }); + // Best-effort: even if the server call fails, the caller clears local token/state. + if (!response.ok) { + return; + } +} + +export type SocialProvider = 'google' | 'facebook'; + +/** The app's custom URL scheme (app.config.ts `scheme: 'chatbotxmobileapp'`) that the OAuth + * broker relays back to once the provider flow completes. */ +export const SOCIAL_CALLBACK_URL = 'chatbotxmobileapp://auth-callback'; + +/** + * Starts a social sign-in flow. `POST /api/auth/sign-in/social` request/response shape confirmed + * directly from better-auth's own source (aha.chat's `node_modules/better-auth/dist/api/routes/ + * sign-in.mjs` — path `/sign-in/social`; `.d.mts` in the same dir — request `{ provider, + * callbackURL }`, response `{ redirect: boolean; url: string }`), NOT guessed from convention. + * + * The returned `url` sends the user through the provider, then aha.chat's fixed OAuth "broker" + * host (see SOCIAL_PROVIDERS/redirectURI in aha.chat's `packages/auth/src/server.ts`), which + * relays back to `callbackURL`. `chatbotxmobileapp://` is allowlisted in that file's + * `trustedOrigins` `staticOrigins` array alongside the broker/builder web origins. + */ +export async function startSocialSignIn(provider: SocialProvider): Promise<string> { + const response = await fetch(`${AUTH_BASE_URL}/sign-in/social`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ provider, callbackURL: SOCIAL_CALLBACK_URL }), + }); + + await throwIfError(response); + + const body = (await response.json()) as { redirect: boolean; url: string }; + return body.url; +} diff --git a/src/api/auth-token.ts b/src/api/auth-token.ts new file mode 100644 index 0000000..079284b --- /dev/null +++ b/src/api/auth-token.ts @@ -0,0 +1,24 @@ +import * as SecureStore from 'expo-secure-store'; + +/** + * Auth token storage, backed by expo-secure-store (Keychain on iOS, Keystore-backed + * EncryptedSharedPreferences on Android). + * + * This module only handles *storage* of a bearer token. The actual auth flow (sign-in, token + * exchange, refresh) is a later phase, once the backend's bearer-auth endpoints are live — see + * AGENTS.md in the aha.chat repo for the corresponding backend work. `getAuthToken` currently + * always resolves to whatever was last persisted (or null), with no refresh logic. + */ +const AUTH_TOKEN_KEY = 'chatbotx.authToken'; + +export async function getAuthToken(): Promise<string | null> { + return SecureStore.getItemAsync(AUTH_TOKEN_KEY); +} + +export async function setAuthToken(token: string): Promise<void> { + await SecureStore.setItemAsync(AUTH_TOKEN_KEY, token); +} + +export async function clearAuthToken(): Promise<void> { + await SecureStore.deleteItemAsync(AUTH_TOKEN_KEY); +} diff --git a/src/api/client.test.ts b/src/api/client.test.ts new file mode 100644 index 0000000..4852998 --- /dev/null +++ b/src/api/client.test.ts @@ -0,0 +1,117 @@ +jest.mock('expo-constants', () => ({ + __esModule: true, + default: { + expoConfig: { + extra: { apiBaseUrl: 'http://localhost:3123', wsUrl: 'ws://localhost:3123' }, + }, + }, +})); +jest.mock('expo-secure-store', () => ({ getItemAsync: jest.fn() })); + +import * as SecureStore from 'expo-secure-store'; +import type { Middleware } from 'openapi-fetch'; + +import { apiClient } from '@/api/client'; + +/** + * Stand-in for React Native's second `Response` realm (see the plan this test accompanies): + * duck-types a Response but is never `instanceof` the global `Response` openapi-fetch checks + * against, which is exactly the mismatch that produced + * "onResponse: must return new Response() when modifying the response". + */ +class ForeignResponse { + constructor( + private readonly bodyText: string, + private readonly init: ResponseInit = {}, + ) {} + get status() { + return this.init.status ?? 200; + } + get statusText() { + return this.init.statusText ?? ''; + } + get headers() { + return new Headers(this.init.headers); + } + async text() { + return this.bodyText; + } + async json() { + return JSON.parse(this.bodyText); + } +} + +describe('apiClient', () => { + let fetchMock: jest.Mock; + let serverResponse: Response; + + beforeEach(() => { + serverResponse = new Response(JSON.stringify({ id: '123' }), { + status: 200, + headers: { 'content-type': 'application/json' }, + }); + fetchMock = jest.fn().mockResolvedValue(serverResponse); + }); + + it('does not throw the RN dual-Response error, because no onResponse middleware is registered', async () => { + jest.mocked(SecureStore.getItemAsync).mockResolvedValueOnce(null); + + const result = await apiClient.GET('/analytics/broadcasts/{broadcastId}/stats', { + params: { path: { broadcastId: '1' }, query: { workspaceId: 'w1' } }, + fetch: fetchMock, + }); + + expect(result.error).toBeUndefined(); + expect(result.data).toEqual({ id: '123' }); + }); + + it('regression: a rewrapping onResponse middleware reproduces the exact RN dual-Response error', async () => { + // Proves the mechanism this suite guards against: only a middleware that reconstructs the + // response (as the deleted bigNumberSafeParsingMiddleware did) can trigger openapi-fetch's + // `instanceof Response` check. If such a middleware is ever reintroduced against a + // foreign-realm Response class, it fails exactly like the original bug. + const rewrapMiddleware: Middleware = { + async onResponse({ response }) { + const raw = await response.text(); + return new ForeignResponse(raw, { status: response.status }) as unknown as Response; + }, + }; + apiClient.use(rewrapMiddleware); + jest.mocked(SecureStore.getItemAsync).mockResolvedValueOnce(null); + + try { + await expect( + apiClient.GET('/analytics/broadcasts/{broadcastId}/stats', { + params: { path: { broadcastId: '1' }, query: { workspaceId: 'w1' } }, + fetch: fetchMock, + }), + ).rejects.toThrow('onResponse: must return new Response() when modifying the response'); + } finally { + apiClient.eject(rewrapMiddleware); + } + }); + + it('sets Authorization: Bearer <token> when getAuthToken resolves a token', async () => { + jest.mocked(SecureStore.getItemAsync).mockResolvedValueOnce('test-token'); + + await apiClient.GET('/analytics/broadcasts/{broadcastId}/stats', { + params: { path: { broadcastId: '1' }, query: { workspaceId: 'w1' } }, + fetch: fetchMock, + }); + + const request = fetchMock.mock.calls[0][0] as Request; + expect(request.headers.get('Authorization')).toBe('Bearer test-token'); + }); + + it('leaves the Authorization header unset when getAuthToken resolves null', async () => { + jest.mocked(SecureStore.getItemAsync).mockResolvedValueOnce(null); + + await apiClient.GET('/analytics/broadcasts/{broadcastId}/stats', { + params: { path: { broadcastId: '1' }, query: { workspaceId: 'w1' } }, + fetch: fetchMock, + }); + + const request = fetchMock.mock.calls[0][0] as Request; + expect(request.headers.has('Authorization')).toBe(false); + }); +}); diff --git a/src/api/client.ts b/src/api/client.ts new file mode 100644 index 0000000..c45d5b8 --- /dev/null +++ b/src/api/client.ts @@ -0,0 +1,33 @@ +import createClient, { type Middleware } from 'openapi-fetch'; + +import { getAuthToken } from '@/api/auth-token'; +import { env } from '@/config/env'; + +import type { paths } from './generated/schema'; + +/** + * Attaches `Authorization: Bearer <token>` to every outgoing request when a token is stored. + */ +const authMiddleware: Middleware = { + async onRequest({ request }) { + const token = await getAuthToken(); + + if (token) { + request.headers.set('Authorization', `Bearer ${token}`); + } + + return request; + }, +}; + +/** + * Typed fetch client for the aha.chat REST API, generated from its OpenAPI spec. + * Run `pnpm generate:api` to (re)generate src/api/generated/schema.ts against a running backend. + * + * All oRPC routes in the generated spec are served under `/api` (auth and the hand-rolled + * multipart upload route are separate surfaces under plain `/api` — see auth-endpoints.ts and + * send-message-multipart.ts). + */ +export const apiClient = createClient<paths>({ baseUrl: `${env.apiBaseUrl}/api` }); + +apiClient.use(authMiddleware); diff --git a/src/api/errors.test.ts b/src/api/errors.test.ts new file mode 100644 index 0000000..6a02d81 --- /dev/null +++ b/src/api/errors.test.ts @@ -0,0 +1,133 @@ +import { ApiError, normalizeApiError } from '@/api/errors'; + +describe('normalizeApiError', () => { + it('returns mustChangePassword for FORBIDDEN with the change-password message', () => { + const error = new ApiError({ code: 'FORBIDDEN', message: 'Password change required' }); + expect(normalizeApiError(error)).toEqual({ kind: 'mustChangePassword' }); + }); + + it('returns unauthorized for UNAUTHORIZED code', () => { + const error = new ApiError({ code: 'UNAUTHORIZED' }); + expect(normalizeApiError(error)).toEqual({ kind: 'unauthorized' }); + }); + + it('returns unauthorized for status 401', () => { + const error = new ApiError({ status: 401 }); + expect(normalizeApiError(error)).toEqual({ kind: 'unauthorized' }); + }); + + it('classifies workspaceBlocked as mac when message mentions contact limit', () => { + const error = new ApiError({ code: 'workspaceBlocked', message: 'Hit the contact limit' }); + expect(normalizeApiError(error)).toEqual({ + kind: 'workspaceBlocked', + reason: 'mac', + message: 'Hit the contact limit', + }); + }); + + it('classifies workspaceBlocked as trialExpired when message mentions trial/expired', () => { + const error = new ApiError({ status: 402, message: 'Your trial has expired' }); + expect(normalizeApiError(error)).toEqual({ + kind: 'workspaceBlocked', + reason: 'trialExpired', + message: 'Your trial has expired', + }); + }); + + it('classifies workspaceBlocked as unknown when message matches neither pattern', () => { + const error = new ApiError({ status: 402, message: 'Something else' }); + expect(normalizeApiError(error)).toEqual({ + kind: 'workspaceBlocked', + reason: 'unknown', + message: 'Something else', + }); + }); + + it('prefers a machine-readable data.reason over substring matching', () => { + const error = new ApiError({ + status: 402, + message: 'Some unrelated free-form copy that mentions nothing recognizable', + data: { reason: 'mac' }, + }); + expect(normalizeApiError(error)).toEqual({ + kind: 'workspaceBlocked', + reason: 'mac', + message: 'Some unrelated free-form copy that mentions nothing recognizable', + }); + }); + + it('uses data.reason=trialExpired even when the message would substring-match mac', () => { + const error = new ApiError({ + status: 402, + message: 'reached the contact limit', + data: { reason: 'trialExpired' }, + }); + expect(normalizeApiError(error)).toEqual({ + kind: 'workspaceBlocked', + reason: 'trialExpired', + message: 'reached the contact limit', + }); + }); + + it('falls back to substring matching when data.reason is an unrecognized value', () => { + const error = new ApiError({ + status: 402, + message: 'Your trial has expired', + data: { reason: 'somethingUnexpected' }, + }); + expect(normalizeApiError(error)).toEqual({ + kind: 'workspaceBlocked', + reason: 'trialExpired', + message: 'Your trial has expired', + }); + }); + + it('falls back to substring matching when data is absent', () => { + const error = new ApiError({ status: 402, message: 'Hit the contact limit' }); + expect(normalizeApiError(error)).toEqual({ + kind: 'workspaceBlocked', + reason: 'mac', + message: 'Hit the contact limit', + }); + }); + + it('defaults workspaceBlocked message when body has none', () => { + const error = new ApiError({ status: 402 }); + const result = normalizeApiError(error); + expect(result.kind).toBe('workspaceBlocked'); + if (result.kind === 'workspaceBlocked') { + expect(result.message).toBe('This workspace is currently unavailable.'); + } + }); + + it('returns notFound for NOT_FOUND code', () => { + const error = new ApiError({ code: 'NOT_FOUND' }); + expect(normalizeApiError(error)).toEqual({ kind: 'notFound' }); + }); + + it('returns notFound for status 404', () => { + const error = new ApiError({ status: 404 }); + expect(normalizeApiError(error)).toEqual({ kind: 'notFound' }); + }); + + it('returns unknown with body message for unrecognized ApiError', () => { + const error = new ApiError({ code: 'SOMETHING_ELSE', message: 'weird failure' }); + expect(normalizeApiError(error)).toEqual({ kind: 'unknown', message: 'weird failure' }); + }); + + it('returns unknown with default message when ApiError body has no message', () => { + const error = new ApiError({ code: 'SOMETHING_ELSE' }); + expect(normalizeApiError(error)).toEqual({ kind: 'unknown', message: 'Unknown error' }); + }); + + it('returns unknown with the Error message for a non-ApiError Error', () => { + expect(normalizeApiError(new Error('boom'))).toEqual({ kind: 'unknown', message: 'boom' }); + }); + + it('returns a generic unknown for a non-Error thrown value', () => { + expect(normalizeApiError('not an error')).toEqual({ + kind: 'unknown', + message: 'Unknown error', + }); + }); +}); diff --git a/src/api/errors.ts b/src/api/errors.ts new file mode 100644 index 0000000..b1caaed --- /dev/null +++ b/src/api/errors.ts @@ -0,0 +1,91 @@ +/** + * Normalizes the oRPC error wire shape (`{ code, status, message, defined }` — verified live + * against the aha.chat builder dev server) into a discriminated union the rest of the app can + * switch on. `openapi-fetch` doesn't throw on HTTP errors — it resolves `{ data, error }` — so + * every mutation/query hook's `queryFn`/`mutationFn` must check `error` and + * `throw new ApiError(error)` for react-query's QueryCache/MutationCache `onError` (wired in + * lib/query-client.ts) to see it. Business-rule errors (ChatbotXException) surface their own + * `code`/`status`, e.g. `workspaceBlocked` at 402 — see aha.chat apps/builder/src/orpc.ts. + */ +export interface ApiErrorBody { + code?: string; + status?: number; + message?: string; + defined?: boolean; + /** + * Machine-readable payload some business-rule errors may attach. `data.reason` is a forward- + * compatible hook for the 402 `workspaceBlocked` error: the backend does not send it today (as + * of this writing `classifyWorkspaceBlockedReason` below is the only source of truth, matched + * by English substring against `message` — see plan's "backend ask" #2), but once it does, this + * field is read first and the substring match becomes pure fallback. No schema for `data` exists + * yet in `src/api/generated/schema.ts`, so this stays loosely typed until the backend ships it. + */ + data?: { reason?: string }; +} + +export type NormalizedApiError = + | { kind: 'unauthorized' } + | { kind: 'mustChangePassword' } + | { kind: 'workspaceBlocked'; reason: 'mac' | 'trialExpired' | 'unknown'; message: string } + | { kind: 'notFound' } + | { kind: 'unknown'; message: string }; + +const MUST_CHANGE_PASSWORD_MESSAGE = 'Password change required'; + +/** Thrown by hooks when `openapi-fetch` resolves an `error` body, so react-query's error + * channel (retry, onError, `error` from useQuery) sees API failures. */ +export class ApiError extends Error { + readonly body: ApiErrorBody; + + constructor(body: ApiErrorBody) { + super(body.message ?? 'API error'); + this.name = 'ApiError'; + this.body = body; + } +} + +/** Machine-readable reason codes the backend may eventually send in `data.reason`. Any other + * string value falls through to the substring-matching fallback below rather than being trusted + * blindly, in case the backend ships a value this app doesn't recognize yet. */ +const KNOWN_REASON_CODES = new Set(['mac', 'trialExpired']); + +function classifyWorkspaceBlockedReason( + message: string, + dataReason: string | undefined, +): 'mac' | 'trialExpired' | 'unknown' { + if (dataReason && KNOWN_REASON_CODES.has(dataReason)) { + return dataReason as 'mac' | 'trialExpired'; + } + + const lowered = message.toLowerCase(); + if (lowered.includes('contact limit')) return 'mac'; + if (lowered.includes('trial') || lowered.includes('expired')) return 'trialExpired'; + return 'unknown'; +} + +export function normalizeApiError(error: unknown): NormalizedApiError { + const body: ApiErrorBody | null = error instanceof ApiError ? error.body : null; + + if (!body) { + return { kind: 'unknown', message: error instanceof Error ? error.message : 'Unknown error' }; + } + + if (body.code === 'FORBIDDEN' && body.message === MUST_CHANGE_PASSWORD_MESSAGE) { + return { kind: 'mustChangePassword' }; + } + if (body.code === 'UNAUTHORIZED' || body.status === 401) { + return { kind: 'unauthorized' }; + } + if (body.code === 'workspaceBlocked' || body.status === 402) { + const message = body.message ?? 'This workspace is currently unavailable.'; + return { + kind: 'workspaceBlocked', + reason: classifyWorkspaceBlockedReason(message, body.data?.reason), + message, + }; + } + if (body.code === 'NOT_FOUND' || body.status === 404) { + return { kind: 'notFound' }; + } + return { kind: 'unknown', message: body.message ?? 'Unknown error' }; +} diff --git a/src/api/generated/README.md b/src/api/generated/README.md new file mode 100644 index 0000000..4593836 --- /dev/null +++ b/src/api/generated/README.md @@ -0,0 +1,8 @@ +Generated by `pnpm generate:api` (scripts/generate-api-client.ts) from the aha.chat builder's +OpenAPI spec (`{API_BASE_URL}/api/spec.json`, the full oRPC router spec — override with +`API_SPEC_PATH=/api/public-spec.json` for the workspace-token-only surface instead). Do not +hand-edit files in this directory — they are overwritten on every regeneration. + +`schema.ts` is committed so the app builds without a live backend available. Re-run +`pnpm generate:api` against a running aha.chat builder instance to refresh it after backend API +changes. diff --git a/src/api/generated/schema.ts b/src/api/generated/schema.ts new file mode 100644 index 0000000..3ca0757 --- /dev/null +++ b/src/api/generated/schema.ts @@ -0,0 +1,20657 @@ +/** + * This file was auto-generated by openapi-typescript. + * Do not make direct changes to the file. + */ + +export interface paths { + "/analytics/broadcasts/{broadcastId}/stats": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get broadcast stats */ + get: operations["analyticsRoutes.getBroadcastStatsAnalyticsAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/analytics/contact-counts-per-day": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get contact counts per day */ + get: operations["analyticsRoutes.contactCountsPerDayAnalyticsAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/analytics/new-contact-counts-per-day": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get new contact counts per day */ + get: operations["analyticsRoutes.newContactCountsPerDayAnalyticsAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/analytics/blocked-contacts-per-day": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get blocked contacts per day */ + get: operations["analyticsRoutes.blockedContactsPerDayAnalyticsAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/analytics/blocked-contacts-count": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get blocked contacts count */ + get: operations["analyticsRoutes.blockedContactsCountAnalyticsAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/analytics/new-contacts-count": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get new contact counts */ + get: operations["analyticsRoutes.newContactsCountAnalyticsAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/analytics/contacts-count": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get contacts count */ + get: operations["analyticsRoutes.contactsCountAnalyticsAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/analytics/active-contacts-count": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get active contacts count */ + get: operations["analyticsRoutes.activeContactsCountAnalyticsAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/analytics/contacts-by-dimension": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get contacts by dimension */ + get: operations["analyticsRoutes.contactsByDimensionAnalyticsAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/analytics/messages-by-admin": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get messages sent by admin */ + get: operations["analyticsRoutes.messagesByAdminAnalyticsAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/analytics/human-agent-stats": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get human agent statistics */ + get: operations["analyticsRoutes.humanAgentStatsAnalyticsAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/analytics/conversation-handoffs": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get conversation handoffs */ + get: operations["analyticsRoutes.conversationHandoffsAnalyticsAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/analytics/conversation-followups": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get conversation follow-ups */ + get: operations["analyticsRoutes.conversationFollowUpsAnalyticsAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/analytics/conversation-archived": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get archived conversations */ + get: operations["analyticsRoutes.conversationArchivedAnalyticsAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/analytics/conversation-assigned": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get assigned conversations */ + get: operations["analyticsRoutes.conversationAssignedAnalyticsAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/analytics/conversation-assigned-by-admin": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get assigned conversations by admin */ + get: operations["analyticsRoutes.conversationAssignedByAdminAnalyticsAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/analytics/unique-conversations-by-admin": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get unique conversations by admin */ + get: operations["analyticsRoutes.uniqueConversationsByAdminAnalyticsAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/analytics/bot-messages-by-result": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get bot messages by result */ + get: operations["analyticsRoutes.botMessagesByResultAnalyticsAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/analytics/bot-messages-with-response": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get bot messages with response */ + get: operations["analyticsRoutes.botMessagesWithResponseAnalyticsAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/analytics/bot-messages-no-response": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get bot messages with no response */ + get: operations["analyticsRoutes.botMessagesNoResponseAnalyticsAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/analytics/bot-messages-ai-providers": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get bot messages AI providers */ + get: operations["analyticsRoutes.botMessagesAIProvidersAnalyticsAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/analytics/messages-by-sender": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get messages by sender */ + get: operations["analyticsRoutes.messagesBySenderAnalyticsAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/analytics/sequences/{sequenceId}/steps/{stepId}/stats": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get sequence step stats */ + get: operations["analyticsRoutes.getSequenceStepStatsAnalyticsAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/analytics/flows/{flowId}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get analytics sessions */ + get: operations["analyticsRoutes.getFlowAnalytics"]; + put?: never; + post?: never; + /** Delete analytics sessions */ + delete: operations["analyticsRoutes.resetFlowAnalytics"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/analytics/mac/active-count/workspace": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get current period MAC count for a workspace */ + get: operations["analyticsRoutes.macActiveContactCountByWorkspaceAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/analytics/ref-links-stats": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get ref link stats */ + get: operations["analyticsRoutes.refLinkStats"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/analytics/ref-links-contacts": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get ref link contacts */ + get: operations["analyticsRoutes.refLinkContacts"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/analytics/magic-links-stats": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get magic link stats */ + get: operations["analyticsRoutes.magicLinkStats"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/analytics/magic-links-contacts": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get magic link contacts */ + get: operations["analyticsRoutes.magicLinkContacts"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/ads/conversion-rules": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List Ads conversion rules */ + get: operations["adsAPI.listRules"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/appointment-calendars/for-flow": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List appointment calendars for flow */ + get: operations["appointmentCalendarsAPI.listAppointmentCalendarsForFlowAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/contacts/{contactId}/appointments": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List appointments for a contact */ + get: operations["appointmentsAPI.listContactAppointmentsAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/ai-mcp-servers/validate": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Validate an MCP server */ + post: operations["aiMcpServerAPIs.validateAIMcpServerAuthenticatedAPI"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/ai-mcp-servers": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List AI MCP servers */ + get: operations["aiMcpServerAPIs.listAIMcpServersAuthenticatedAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/ai-agents": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List AI agents */ + get: operations["aiAgentsAPI.listAIAgentsAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/ai-agents": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List AI agents */ + get: operations["aiAgentsAPI.listAIAgentsWorkspaceTokenAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/broadcasts": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get all broadcasts */ + get: operations["broadcastAPIs.listBroadcastsWorkspaceTokenAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/broadcasts/{idOrName}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get broadcast by id or name */ + get: operations["broadcastAPIs.getBroadcastWorkspaceTokenAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/broadcasts/{idOrName}/audience": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get broadcast audience */ + get: operations["broadcastAPIs.getBroadcastAudienceWorkspaceTokenAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/broadcasts/options": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List broadcast options */ + get: operations["broadcastAPIs.privateListBroadcastOptionsAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/broadcasts/stats": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Get batch broadcast stats */ + post: operations["broadcastAPIs.privateGetBatchBroadcastStatsAPI"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/broadcasts/{broadcastId}/template-detail": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get broadcast template detail */ + get: operations["broadcastAPIs.privateGetBroadcastTemplateDetailAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/broadcasts/{broadcastId}/contacts": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List broadcast contacts by event type */ + get: operations["broadcastAPIs.privateListBroadcastContactsAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/conversations": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** List conversations by cursor pagination */ + post: operations["conversationsAPI.listConversationsAuthenticatedAPI"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/conversations/list": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** List conversations by cursor pagination using POST request */ + post: operations["conversationsAPI.listConversationsByPOSTAuthenticatedAPI"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/conversations/{id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Find conversation by conversation id */ + get: operations["conversationsAPI.findConversationAuthenticatedAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/conversations/post-details": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get Facebook post details for a comment conversation */ + get: operations["conversationsAPI.getPostDetailsAuthenticatedAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/conversations/assign": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Assign or unassign conversations to a user or inbox team */ + post: operations["conversationsAPI.assignConversationsAuthenticatedAPI"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/conversations/archive": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Archive conversations */ + post: operations["conversationsAPI.archiveConversationsAuthenticatedAPI"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/conversations/unarchive": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Unarchive conversations */ + post: operations["conversationsAPI.unarchiveConversationsAuthenticatedAPI"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/conversations/enable-bot": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Re-enable the bot for conversations */ + post: operations["conversationsAPI.enableBotAuthenticatedAPI"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/conversations/disable-bot": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Disable the bot for conversations (hand off to a human) */ + post: operations["conversationsAPI.disableBotAuthenticatedAPI"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/conversations/{id}/read": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Mark a conversation as read */ + post: operations["conversationsAPI.readConversationAuthenticatedAPI"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/conversations/{id}/unread": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Mark a conversation as unread */ + post: operations["conversationsAPI.unreadConversationAuthenticatedAPI"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/conversations/{id}/follow": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Follow a conversation */ + post: operations["conversationsAPI.followConversationAuthenticatedAPI"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/conversations/{id}/unfollow": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Unfollow a conversation */ + post: operations["conversationsAPI.unfollowConversationAuthenticatedAPI"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/conversations": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List conversations */ + get: operations["conversationsAPI.listConversationsWorkspaceTokenAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/coupons/topics": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List coupon topics */ + get: operations["couponsAPI.listCouponTopicsAPI"]; + put?: never; + /** Create coupon topic */ + post: operations["couponsAPI.createCouponTopicAPI"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/coupons/topics/{topicId}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post?: never; + /** Delete coupon topic */ + delete: operations["couponsAPI.deleteCouponTopicAPI"]; + options?: never; + head?: never; + /** Update coupon topic */ + patch: operations["couponsAPI.updateCouponTopicAPI"]; + trace?: never; + }; + "/workspaces/{workspaceId}/coupons/topics/{topicId}/archive": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Archive coupon topic */ + post: operations["couponsAPI.archiveCouponTopicAPI"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/coupons/topics/{topicId}/unarchive": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Unarchive coupon topic */ + post: operations["couponsAPI.unarchiveCouponTopicAPI"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/coupons": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List coupons */ + get: operations["couponsAPI.listCouponsAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/coupons/topic-options": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List coupon topic options */ + get: operations["couponsAPI.listCouponTopicOptionsAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/coupons/export-count": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Count coupon export rows */ + get: operations["couponsAPI.countCouponExportAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/coupons/export-files/{fileId}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get coupon export file status */ + get: operations["couponsAPI.getCouponExportFileAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/contacts/{contactId}/coupons": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List issued coupons for a contact */ + get: operations["couponsAPI.listContactCouponsAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/users/me/device-tokens": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + /** Register a push notification device token for the current user */ + put: operations["deviceTokensAPI.registerDeviceTokenAPI"]; + post?: never; + /** Unregister a push notification device token */ + delete: operations["deviceTokensAPI.unregisterDeviceTokenAPI"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/dynamic-images/{id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get a dynamic image */ + get: operations["dynamicImagesAPI.getDynamicImageAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/email-topics": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List email topics */ + get: operations["emailTopicsAPI.privateListWorkspaceEmailTopicsAPI"]; + put?: never; + /** Create an email topic */ + post: operations["emailTopicsAPI.privateCreateWorkspaceEmailTopicAPI"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/email-topics/{id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + /** Update email topic */ + put: operations["emailTopicsAPI.privateUpdateEmailTopicAPI"]; + post?: never; + /** Delete email topic */ + delete: operations["emailTopicsAPI.privateDeleteEmailTopicAPI"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/tags": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List tags */ + get: operations["tagsAPI.privateListWorkspaceTagsAPI"]; + put?: never; + /** Create a tag */ + post: operations["tagsAPI.privateCreateWorkspaceTagAPI"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/tags/{id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + /** Update tag */ + put: operations["tagsAPI.privateUpdateTagAPI"]; + post?: never; + /** Delete tag */ + delete: operations["tagsAPI.privateDeleteTagsAPI"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/tags": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get all tags */ + get: operations["tagsAPI.listTagsWorkspaceTokenAPI"]; + put?: never; + /** Create a new tag */ + post: operations["tagsAPI.createTagWorkspaceTokenAPI"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/tags/{idOrName}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get tag by id or name */ + get: operations["tagsAPI.getTagWorkspaceTokenAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/tags/{id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + /** Update tag */ + put: operations["tagsAPI.updateTagWorkspaceTokenAPI"]; + post?: never; + /** Delete tag */ + delete: operations["tagsAPI.deleteTagWorkspaceTokenAPI"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/custom-fields": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get all custom fields */ + get: operations["customFieldsAPI.listCustomFieldsWorkspaceTokenAPI"]; + put?: never; + /** Create a custom field */ + post: operations["customFieldsAPI.createCustomFieldWorkspaceTokenAPI"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/custom-fields/{idOrName}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get custom field by id or name */ + get: operations["customFieldsAPI.getCustomFieldWorkspaceTokenAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/custom-fields/{id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + /** Update custom field */ + put: operations["customFieldsAPI.updateCustomFieldWorkspaceTokenAPI"]; + post?: never; + /** Delete custom field */ + delete: operations["customFieldsAPI.deleteCustomFieldWorkspaceTokenAPI"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/custom-fields": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List custom fields */ + get: operations["customFieldsAPI.privateListCustomFieldsAPI"]; + put?: never; + /** Create custom field */ + post: operations["customFieldsAPI.privateCreateCustomFieldAPI"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/custom-fields/{id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + /** Update custom field */ + put: operations["customFieldsAPI.privateUpdateCustomFieldAPI"]; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/custom-fields/{customFieldId}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post?: never; + /** Delete custom field */ + delete: operations["customFieldsAPI.privateDeleteCustomFieldsAPI"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/flows": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get all flows */ + get: operations["flowsAPI.listFlowsWorkspaceTokenAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/flows": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List flows */ + get: operations["flowsAPI.privateListFlowsAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/flows/{flowId}/stats": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get flow stats */ + get: operations["flowsAPI.privateGetFlowStatsAPI"]; + put?: never; + post?: never; + /** Reset flow stats */ + delete: operations["flowsAPI.privateResetFlowStatsAPI"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/flows/{flowId}/versions": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List flow versions */ + get: operations["flowsAPI.privateListFlowVersionsAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/flows/{flowId}/messenger-ads-json": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get Messenger Ads JSON */ + get: operations["flowsAPI.privateGetMessengerAdsJsonAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/flows/{flowId}/contacts": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get flow event contacts */ + get: operations["flowsAPI.privateGetFlowContactStatsAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/contacts": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List contacts */ + get: operations["contactsAPIs.listContactsWorkspaceTokenAPI"]; + put?: never; + /** Create a contact */ + post: operations["contactsAPIs.createContactWorkspaceTokenAPI"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/contacts/{identifier}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get contact by identifier (id:123, email:user@example.com, phone:+84...) */ + get: operations["contactsAPIs.getContactWorkspaceTokenAPI"]; + /** Update contact fields */ + put: operations["contactsAPIs.updateContactWorkspaceTokenAPI"]; + post?: never; + /** Delete a contact */ + delete: operations["contactsAPIs.deleteContactWorkspaceTokenAPI"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/contacts/find-by-custom-field": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * List contacts by custom field + * @description Find contacts by custom field value. It will return maximum 100 contacts. The results are sorted by the last custom field value update for a contact. + */ + get: operations["contactsAPIs.filterContactsWorkspaceTokenAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/contacts/{identifier}/tags": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get all tags added to this contact */ + get: operations["contactsAPIs.listContactTagsWorkspaceTokenAPI"]; + put?: never; + /** Add tags to the contact */ + post: operations["contactsAPIs.addContactTagsWorkspaceTokenAPI"]; + /** Remove tags from the contact */ + delete: operations["contactsAPIs.removeContactTagsWorkspaceTokenAPI"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/contacts/{identifier}/custom-fields": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get all custom fields from a contact */ + get: operations["contactsAPIs.listContactCustomFieldsWorkspaceTokenAPI"]; + /** Set multiple custom field values for a contact */ + put: operations["contactsAPIs.setContactCustomFieldsWorkspaceTokenAPI"]; + post?: never; + /** Clear all custom fields from a contact */ + delete: operations["contactsAPIs.clearContactCustomFieldsWorkspaceTokenAPI"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/contacts/{identifier}/custom-fields/{customFieldId}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get contact custom field value */ + get: operations["contactsAPIs.getContactCustomFieldWorkspaceTokenAPI"]; + put?: never; + /** Set contact custom field value */ + post: operations["contactsAPIs.setContactCustomFieldWorkspaceTokenAPI"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/contacts/{identifier}/custom-fields/{idOrName}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post?: never; + /** Delete contact custom field by id or name */ + delete: operations["contactsAPIs.clearContactCustomFieldWorkspaceTokenAPI"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/contacts/{identifier}/block": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Block a contact */ + post: operations["contactsAPIs.blockContactWorkspaceTokenAPI"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/contacts/{identifier}/unblock": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Unblock a contact */ + post: operations["contactsAPIs.unblockContactWorkspaceTokenAPI"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/contacts/{identifier}/messages": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List messages for contact */ + get: operations["contactsAPIs.listContactMessagesWorkspaceTokenAPI"]; + put?: never; + /** Send message to contact */ + post: operations["contactsAPIs.sendMessageWorkspaceTokenAPI"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/contacts/{identifier}/messages/{messageId}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get a message by ID for a contact */ + get: operations["contactsAPIs.getContactMessageWorkspaceTokenAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/contacts/{identifier}/auto-replies": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Trigger auto reply for contact */ + post: operations["contactsAPIs.triggerAutoReplyWorkspaceTokenAPI"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/contacts/{identifier}/flows": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Send flow to contact */ + post: operations["contactsAPIs.sendContactFlowWorkspaceTokenAPI"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/contacts/import": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Import contacts from a file */ + post: operations["contactsAPIs.importContactsWorkspaceTokenAPI"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/contacts/{identifier}/upsert": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Upsert a contact by identifier */ + post: operations["contactsAPIs.upsertContactWorkspaceTokenAPI"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/contacts/{contactId}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get contact */ + get: operations["contactsAPIs.getContactAuthenticatedAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/contacts": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List contacts */ + get: operations["contactsAPIs.listContactsAuthenticatedAPI"]; + put?: never; + /** Create a contact */ + post: operations["contactsAPIs.createContactAuthenticatedAPI"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/contacts/list": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** List contacts using POST request */ + post: operations["contactsAPIs.listContactsByPOSTAuthenticatedAPI"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/contacts/count": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Count contacts */ + get: operations["contactsAPIs.countContactsAuthenticatedAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/contacts/inboxes/count": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Count contact inboxes */ + get: operations["contactsAPIs.countContactInboxesAuthenticatedAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/contacts/inboxes/audience-preview": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** List audience contact inboxes preview */ + post: operations["contactsAPIs.listContactInboxesAudiencePreviewAuthenticatedAPI"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/contacts/export-files/{fileId}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get contact export file status */ + get: operations["contactsAPIs.getExportFileAuthenticatedAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/contacts/{contactId}/tags": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List contact tags */ + get: operations["contactsAPIs.listContactTagsAuthenticatedAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/contacts/tags": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Add tags to contact */ + post: operations["contactsAPIs.addContactTagAuthenticatedAPI"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/contacts/{contactId}/tags/{tagId}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post?: never; + /** Remove tag from contact */ + delete: operations["contactsAPIs.removeContactTagAuthenticatedAPI"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/contacts/{contactId}/fields": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List contact custom fields */ + get: operations["contactsAPIs.listContactFieldsAuthenticatedAPI"]; + put?: never; + /** Set contact custom field value */ + post: operations["contactsAPIs.addContactFieldAuthenticatedAPI"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/contacts/{contactId}/fields/{customFieldId}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post?: never; + /** Delete contact custom field */ + delete: operations["contactsAPIs.deleteContactFieldAuthenticatedAPI"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/bot-fields": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get all bot fields */ + get: operations["botFieldAPIs.listBotFieldsWorkspaceTokenAPI"]; + /** Set multiple bot field values */ + put: operations["botFieldAPIs.setBotFieldsWorkspaceTokenAPI"]; + /** Create a new bot field */ + post: operations["botFieldAPIs.createBotFieldWorkspaceTokenAPI"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/bot-fields/{idOrName}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get bot field by id or name */ + get: operations["botFieldAPIs.getBotFieldWorkspaceTokenAPI"]; + /** Set bot field value by id or name */ + put: operations["botFieldAPIs.setBotFieldWorkspaceTokenAPI"]; + post?: never; + /** Unset the value of the bot field by id or name */ + delete: operations["botFieldAPIs.deleteBotFieldsWorkspaceTokenAPI"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/bot-fields/bulk-update": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + /** Bulk update bot field values by id or name */ + put: operations["botFieldAPIs.bulkUpdateBotFieldsWorkspaceTokenAPI"]; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/active-campaign/lists": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List ActiveCampaign lists */ + get: operations["integrationActiveCampaignAPI.listLists"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/active-campaign/automations": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List ActiveCampaign automations */ + get: operations["integrationActiveCampaignAPI.listAutomations"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/active-campaign/tags": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List ActiveCampaign tags */ + get: operations["integrationActiveCampaignAPI.listTags"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/active-campaign/custom-fields": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List ActiveCampaign custom fields */ + get: operations["integrationActiveCampaignAPI.listCustomFields"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/facebook-ads/ad-accounts": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List Facebook ad accounts */ + get: operations["integrationFacebookAdsAPI.listAdAccounts"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/facebook-ads/custom-audiences": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List custom audiences of a Facebook ad account */ + get: operations["integrationFacebookAdsAPI.listCustomAudiences"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/facebook-ads/ad-insights": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get Facebook ad insights */ + get: operations["integrationFacebookAdsAPI.getAdInsights"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/drip/accounts": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List Drip accounts */ + get: operations["integrationDripAPI.listAccounts"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/drip/tags": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List Drip tags */ + get: operations["integrationDripAPI.listTags"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/drip/custom-fields": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List Drip custom fields */ + get: operations["integrationDripAPI.listCustomFields"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/get-response/campaigns": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List GetResponse campaigns */ + get: operations["integrationGetResponseAPI.listCampaigns"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/get-response/tags": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List GetResponse tags */ + get: operations["integrationGetResponseAPI.listTags"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/integrations/instagram/{integrationId}/coexist": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Enable or disable native Instagram coexist sync */ + post: operations["integrationInstagramAPIs.setCoexistInstagramAPI"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/klaviyo/lists": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List Klaviyo lists */ + get: operations["integrationKlaviyoAPI.listLists"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/mailchimp/audiences": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List Mailchimp audiences */ + get: operations["integrationMailchimpAPI.listAudiences"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/mailchimp/tags": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List Mailchimp tags */ + get: operations["integrationMailchimpAPI.listTags"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/mailchimp/merge-fields": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List Mailchimp merge fields */ + get: operations["integrationMailchimpAPI.listMergeFields"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/mailer-lite/groups": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List MailerLite groups */ + get: operations["integrationMailerLiteAPI.listGroups"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/mailer-lite/fields": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List MailerLite fields */ + get: operations["integrationMailerLiteAPI.listFields"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/integrations/messenger/{integrationId}/coexist": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Enable or disable Messenger coexist sync */ + post: operations["integrationMessengerAPIs.setCoexistMessengerAPI"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/moosend/mailing-lists": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List Moosend mailing lists */ + get: operations["integrationMoosendAPI.listMailingLists"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/integrations/smtp": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List SMTP integrations */ + get: operations["integrationSmtpAPI.listIntegrationSmtps"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/sendgrid/lists": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List SendGrid lists */ + get: operations["integrationSendGridAPI.listLists"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/sendgrid/custom-fields": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List SendGrid custom fields */ + get: operations["integrationSendGridAPI.listCustomFields"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/integrations/whatsapp": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List whatsapp integration */ + get: operations["integrationWhatsappAPIs.listIntegrationWhatsappInternalAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/integrations/whatsapp/{integrationId}/coexist": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Enable or disable WhatsApp coexist sync */ + post: operations["integrationWhatsappAPIs.setCoexistWhatsappAPI"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/whatsapp-message-templates": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List whatsapp message templates */ + get: operations["whatsappMessageTemplateAPIs.listWhatsappMessageTemplatesInternalAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/template-messages": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List template messages */ + get: operations["whatsappMessageTemplateAPIs.listTemplateMessagesWorkspaceTokenAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/whatsapp-flows": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List whatsapp flows */ + get: operations["whatsappFlowAPIs.listWhatsappFlowsInternalAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/whatsapp-flows/{flowId}/screens": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get whatsapp flow screens */ + get: operations["whatsappFlowAPIs.getWhatsappFlowScreensInternalAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/messenger-message-templates": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List Messenger message templates */ + get: operations["messengerMessageTemplateAPIs.listMessengerMessageTemplatesInternalAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/saved-replies": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List saved replies */ + get: operations["savedRepliesAPI.listSavedRepliesAuthorizedAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/saved-replies": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List saved replies */ + get: operations["savedRepliesAPI.listSavedRepliesWorkspaceTokenAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/fb-comments": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List FB Comment Automations */ + get: operations["fbCommentsAPI.listFbCommentsAPI"]; + put?: never; + /** Create FB Comment Automation */ + post: operations["fbCommentsAPI.createFbCommentAPI"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/fb-comments/{id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + /** Update FB Comment Automation */ + put: operations["fbCommentsAPI.updateFbCommentAPI"]; + post?: never; + /** Delete FB Comment Automation */ + delete: operations["fbCommentsAPI.deleteFbCommentAPI"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/fb-comments/facebook-posts": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List Facebook posts for FB Comment Automation */ + get: operations["fbCommentsAPI.facebookPostsAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/ig-comments": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List Instagram Comment Automations */ + get: operations["igCommentsAPI.listIgCommentsAPI"]; + put?: never; + /** Create Instagram Comment Automation */ + post: operations["igCommentsAPI.createIgCommentAPI"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/ig-comments/{id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + /** Update Instagram Comment Automation */ + put: operations["igCommentsAPI.updateIgCommentAPI"]; + post?: never; + /** Delete Instagram Comment Automation */ + delete: operations["igCommentsAPI.deleteIgCommentAPI"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/ig-comments/instagram-media": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List Instagram media for IG Comment Automation */ + get: operations["igCommentsAPI.instagramMediaAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/ig-stories": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List Instagram Story Automations */ + get: operations["igStoriesAPI.listIgStoriesAPI"]; + put?: never; + /** Create Instagram Story Automation */ + post: operations["igStoriesAPI.createIgStoryAPI"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/ig-stories/{id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + /** Update Instagram Story Automation */ + put: operations["igStoriesAPI.updateIgStoryAPI"]; + post?: never; + /** Delete Instagram Story Automation */ + delete: operations["igStoriesAPI.deleteIgStoryAPI"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/ig-stories/instagram-stories": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List Instagram stories for IG Story Automation */ + get: operations["igStoriesAPI.instagramStoriesAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/fb-lead-ads/pages": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List Messenger pages eligible for Lead Ads */ + get: operations["facebookLeadAdsAPI.listLeadAdsPagesAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/fb-lead-ads/forms": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List a page's lead forms */ + get: operations["facebookLeadAdsAPI.listLeadAdsFormsAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/sequences": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List sequences */ + get: operations["sequencesAPI.listSequencesWorkspaceAuthAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/sequences/{sequenceId}/steps/{stepId}/stats": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get sequence step stats */ + get: operations["sequencesAPI.privateGetSequenceStepStatsAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/sequences/{sequenceId}/steps/{stepId}/contacts": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List sequence step contacts by event type */ + get: operations["sequencesAPI.privateListSequenceStepContactsAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/sequences": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List sequences */ + get: operations["sequencesAPI.listSequencesWorkspaceTokenAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/sequences/{id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get sequence details */ + get: operations["sequencesAPI.getSequenceWorkspaceTokenAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/ai-files": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List AI files */ + get: operations["aiFilesAPI.listAIFilesAuthenticatedAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/inboxes": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List inboxes */ + get: operations["inboxesAPI.listInboxesAuthenticatedAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/inboxes": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * List inboxes + * @description List connected inboxes with their internal IDs. Use `id` as the `inboxId` parameter when sending messages or flows to a contact. + */ + get: operations["inboxesAPI.listInboxesWorkspaceTokenAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/channels": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List channels */ + get: operations["inboxesAPI.listChannelsWorkspaceTokenAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/spreadsheets": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List spreadsheets */ + get: operations["spreadsheetsAPI.listSpreadsheetsAuthenticatedAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/worksheets": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List worksheets */ + get: operations["spreadsheetsAPI.listWorksheetsAuthenticatedAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/worksheets/{spreadsheetId}/headers": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List worksheet headers */ + get: operations["spreadsheetsAPI.listWorksheetHeadersAuthenticatedAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/members": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List workspace members */ + get: operations["workspaceMembersAPI.listWorkspaceMembersAuthenticatedAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/members": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List workspace members */ + get: operations["workspaceMembersAPI.listMembersWorkspaceTokenAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/members/{memberId}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get workspace member by id */ + get: operations["workspaceMembersAPI.getMemberWorkspaceTokenAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/inbox-teams": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List inbox teams */ + get: operations["inboxTeamsAPI.listInboxTeamsAuthenticatedAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/teams": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List teams */ + get: operations["inboxTeamsAPI.listTeamsWorkspaceTokenAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/folders": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List folders */ + get: operations["foldersAPI.listFoldersAuthenticatedAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/messages": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List messages */ + get: operations["messagesAPI.listMessagesAuthenticatedAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/messages/{id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Find message by message id */ + get: operations["messagesAPI.findMessageAuthenticatedAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/conversations/{conversationId}/messages": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Send a message on a conversation */ + post: operations["messagesAPI.createMessageAuthenticatedAPI"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/conversations/{conversationId}/messages/{messageId}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + /** Edit a comment message */ + patch: operations["messagesAPI.editMessageAuthenticatedAPI"]; + trace?: never; + }; + "/workspaces/{workspaceId}/conversations/{conversationId}/messages/{id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post?: never; + /** Delete a comment message */ + delete: operations["messagesAPI.deleteMessageAuthenticatedAPI"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/conversations/{conversationId}/messages/{messageId}/attributes": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Change a message's liked/hidden attributes */ + post: operations["messagesAPI.changeMessageAttributesAuthenticatedAPI"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/messenger-personas": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List Messenger personas across the workspace's pages */ + get: operations["personasAPIs.listMessengerPersonasAuthenticatedAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/error-logs": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List error logs */ + get: operations["errorLogsAPI.listErrorLogsWorkspaceTokenAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/external-webhooks": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * List external webhooks + * @description List webhooks registered by external automation platforms (e.g. Make) for this workspace. + */ + get: operations["externalWebhooksAPI.listExternalWebhooksWorkspaceTokenAPI"]; + put?: never; + /** + * Register an external webhook + * @description Registers a URL to receive events for a given event name. Idempotent — registering the same (event, url) again returns the existing registration. + */ + post: operations["externalWebhooksAPI.createExternalWebhookWorkspaceTokenAPI"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/external-webhooks/{id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post?: never; + /** Unregister an external webhook */ + delete: operations["externalWebhooksAPI.deleteExternalWebhookWorkspaceTokenAPI"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/workspaces": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get workspace */ + get: operations["workspacesAPI.getWorkspaceWorkspaceTokenAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/users/me/workspaces": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List workspaces the current user is a member of */ + get: operations["workspacesAPI.listMyWorkspacesAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/ai-functions": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List AI functions */ + get: operations["aiFunctionsAPI.listAIFunctionsAuthenticatedAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/platform-credentials/giphy/api-key": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get Giphy API key */ + get: operations["platformCredentialsAPI.getGiphyApiKeyAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/products/form-options": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List product form options */ + get: operations["productsAPI.listProductFormOptionsAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/products": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List products */ + get: operations["productsAPI.listProductsAuthorizedAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/product-categories": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List product categories */ + get: operations["productCategoriesAPI.listProductCategoriesAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/questionnaires/for-flow": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List questionnaires for flow */ + get: operations["questionnairesAPI.listQuestionnairesForFlowAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/ref-links/options": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List ref link options */ + get: operations["refLinksAPI.listRefLinkOptionsAuthenticatedAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/ref-links/{id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get a specific ref link */ + get: operations["refLinksAPI.getRefLinksAuthenticatedAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/ref-links/{id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get a specific ref link */ + get: operations["refLinksAPI.getRefLinkWorkspaceTokenAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/keywords": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List keywords (automated responses) */ + get: operations["keywordsAPI.listKeywordsWorkspaceTokenAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/integrations": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List integrations */ + get: operations["integrationsAPI.listIntegrationsWorkspaceTokenAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/triggers": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List triggers */ + get: operations["triggersAPI.listTriggersWorkspaceTokenAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/workspaces/{workspaceId}/user-persistent-menus": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List user persistent menus */ + get: operations["userPersistentMenusAPI.listUserPersistentMenusAuthenticatedAPI"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/webhooks": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List webhooks */ + get: operations["webhooksAPI.listWebhooksWorkspaceTokenAPI"]; + put?: never; + /** + * Register a webhook + * @description Registers a URL to receive system events (e.g. new contact, tag applied). Automation platforms (e.g. n8n) can call this to auto-attach a webhook when a workflow is activated. + */ + post: operations["webhooksAPI.createWebhookWorkspaceTokenAPI"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/webhooks/{id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post?: never; + /** Unregister a webhook */ + delete: operations["webhooksAPI.deleteWebhookWorkspaceTokenAPI"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; +} +export type webhooks = Record<string, never>; +export interface components { + schemas: { + UndefinedError: { + /** @constant */ + defined: false; + code: string; + status: number; + message: string; + data?: unknown; + }; + }; + responses: never; + parameters: never; + requestBodies: never; + headers: never; + pathItems: never; +} +export type $defs = Record<string, never>; +export interface operations { + "analyticsRoutes.getBroadcastStatsAnalyticsAPI": { + parameters: { + query: { + workspaceId: string; + }; + header?: never; + path: { + broadcastId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + "message:sent": number; + "message:delivered": number; + "message:seen": number; + "flow:clicked": number; + "message:failed": number; + }; + }; + }; + }; + }; + "analyticsRoutes.contactCountsPerDayAnalyticsAPI": { + parameters: { + query: { + from: string; + to: string; + timezone?: string; + workspaceId: string; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + /** Format: date-time */ + date: string; + count: number; + }[]; + }; + }; + }; + }; + }; + "analyticsRoutes.newContactCountsPerDayAnalyticsAPI": { + parameters: { + query: { + from: string; + to: string; + timezone?: string; + workspaceId: string; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + /** Format: date-time */ + date: string; + count: number; + }[]; + }; + }; + }; + }; + }; + "analyticsRoutes.blockedContactsPerDayAnalyticsAPI": { + parameters: { + query: { + from: string; + to: string; + timezone?: string; + workspaceId: string; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + /** Format: date-time */ + date: string; + count: number; + }[]; + }; + }; + }; + }; + }; + "analyticsRoutes.blockedContactsCountAnalyticsAPI": { + parameters: { + query: { + from: string; + to: string; + timezone?: string; + workspaceId: string; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + count: number; + }; + }; + }; + }; + }; + }; + "analyticsRoutes.newContactsCountAnalyticsAPI": { + parameters: { + query: { + from: string; + to: string; + timezone?: string; + workspaceId: string; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + count: number; + }; + }; + }; + }; + }; + }; + "analyticsRoutes.contactsCountAnalyticsAPI": { + parameters: { + query: { + from: string; + to: string; + timezone?: string; + workspaceId: string; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + count: number; + }; + }; + }; + }; + }; + }; + "analyticsRoutes.activeContactsCountAnalyticsAPI": { + parameters: { + query: { + from: string; + to: string; + timezone?: string; + workspaceId: string; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + count: number; + }; + }; + }; + }; + }; + }; + "analyticsRoutes.contactsByDimensionAnalyticsAPI": { + parameters: { + query: { + from: string; + to: string; + timezone?: string; + workspaceId: string; + dimension: "country" | "channel" | "source"; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + count: number; + dimension: string; + uniqueContacts: number; + }[]; + }; + }; + }; + }; + }; + "analyticsRoutes.messagesByAdminAnalyticsAPI": { + parameters: { + query: { + from: string; + to: string; + timezone?: string; + workspaceId: string; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + adminId: string; + workspaceId: string; + count: number; + userEmail?: string; + userName?: string; + }[]; + }; + }; + }; + }; + }; + "analyticsRoutes.humanAgentStatsAnalyticsAPI": { + parameters: { + query: { + from: string; + to: string; + timezone?: string; + workspaceId: string; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + adminId: string; + workspaceId: string; + assignedConversations: number; + messagesSent: number; + uniqueContacts: number; + userEmail?: string; + userName?: string; + }[]; + }; + }; + }; + }; + }; + "analyticsRoutes.conversationHandoffsAnalyticsAPI": { + parameters: { + query: { + from: string; + to: string; + timezone?: string; + workspaceId: string; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + workspaceId: string; + count: number; + /** @enum {string} */ + direction: "to_human" | "to_bot"; + /** Format: date-time */ + timestamp: string; + }[]; + }; + }; + }; + }; + }; + "analyticsRoutes.conversationFollowUpsAnalyticsAPI": { + parameters: { + query: { + from: string; + to: string; + timezone?: string; + workspaceId: string; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + workspaceId: string; + count: number; + /** Format: date-time */ + timestamp: string; + }[]; + }; + }; + }; + }; + }; + "analyticsRoutes.conversationArchivedAnalyticsAPI": { + parameters: { + query: { + from: string; + to: string; + timezone?: string; + workspaceId: string; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + workspaceId: string; + count: number; + /** Format: date-time */ + timestamp: string; + }[]; + }; + }; + }; + }; + }; + "analyticsRoutes.conversationAssignedAnalyticsAPI": { + parameters: { + query: { + from: string; + to: string; + timezone?: string; + workspaceId: string; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + workspaceId: string; + count: number; + /** Format: date-time */ + timestamp: string; + }[]; + }; + }; + }; + }; + }; + "analyticsRoutes.conversationAssignedByAdminAnalyticsAPI": { + parameters: { + query: { + from: string; + to: string; + timezone?: string; + workspaceId: string; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + workspaceId: string; + toAssignee: string; + count: number; + userName?: string; + userEmail?: string; + }[]; + }; + }; + }; + }; + }; + "analyticsRoutes.uniqueConversationsByAdminAnalyticsAPI": { + parameters: { + query: { + from: string; + to: string; + timezone?: string; + workspaceId: string; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + workspaceId: string; + toAssignee: string; + count: number; + userName?: string; + userEmail?: string; + }[]; + }; + }; + }; + }; + }; + "analyticsRoutes.botMessagesByResultAnalyticsAPI": { + parameters: { + query: { + from: string; + to: string; + timezone?: string; + workspaceId: string; + granularity?: "minute" | "hour" | "day"; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + aiProvider: string; + workspaceId: string; + count: number; + hasResponse: boolean; + /** @enum {string} */ + responseType: "automated_response" | "ai_agent" | "flow" | "none"; + /** @enum {string} */ + result?: "success" | "fallback"; + /** @enum {string} */ + routeType?: "flow" | "agent" | "fallback"; + /** Format: date-time */ + timestamp: string; + }[]; + }; + }; + }; + }; + }; + "analyticsRoutes.botMessagesWithResponseAnalyticsAPI": { + parameters: { + query: { + from: string; + to: string; + timezone?: string; + workspaceId: string; + granularity?: "minute" | "hour" | "day"; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + aiProvider: string; + workspaceId: string; + count: number; + hasResponse: boolean; + /** @enum {string} */ + responseType: "automated_response" | "ai_agent" | "flow" | "none"; + /** @enum {string} */ + result?: "success" | "fallback"; + /** @enum {string} */ + routeType?: "flow" | "agent" | "fallback"; + /** Format: date-time */ + timestamp: string; + }[]; + }; + }; + }; + }; + }; + "analyticsRoutes.botMessagesNoResponseAnalyticsAPI": { + parameters: { + query: { + from: string; + to: string; + timezone?: string; + workspaceId: string; + granularity?: "minute" | "hour" | "day"; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + aiProvider: string; + workspaceId: string; + count: number; + hasResponse: boolean; + /** @enum {string} */ + responseType: "automated_response" | "ai_agent" | "flow" | "none"; + /** @enum {string} */ + result?: "success" | "fallback"; + /** @enum {string} */ + routeType?: "flow" | "agent" | "fallback"; + /** Format: date-time */ + timestamp: string; + }[]; + }; + }; + }; + }; + }; + "analyticsRoutes.botMessagesAIProvidersAnalyticsAPI": { + parameters: { + query: { + from: string; + to: string; + timezone?: string; + workspaceId: string; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + aiProvider: string; + count: number; + percentage: number; + }[]; + }; + }; + }; + }; + }; + "analyticsRoutes.messagesBySenderAnalyticsAPI": { + parameters: { + query: { + from: string; + to: string; + timezone?: string; + workspaceId: string; + granularity?: "day" | "month"; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + channel: string | null; + workspaceId: string; + count: number; + /** @enum {string} */ + senderType: "bot" | "human"; + /** Format: date-time */ + timestamp: string; + }[]; + }; + }; + }; + }; + }; + "analyticsRoutes.getSequenceStepStatsAnalyticsAPI": { + parameters: { + query: { + workspaceId: string; + }; + header?: never; + path: { + sequenceId: string; + stepId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + "message:sent": number; + "message:delivered": number; + "message:seen": number; + "flow:clicked": number; + "message:failed": number; + }; + }; + }; + }; + }; + "analyticsRoutes.getFlowAnalytics": { + parameters: { + query: { + workspaceId: string; + }; + header?: never; + path: { + flowId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + [key: string]: { + node: { + "message:sent": number; + "message:delivered": number; + "message:seen": number; + "flow:clicked": { + clicked: number; + totalUsers: number; + }; + "message:failed": number; + }; + buttons: { + [key: string]: { + buttonId: string; + clicks: number; + }; + }; + }; + }; + }; + }; + }; + }; + "analyticsRoutes.resetFlowAnalytics": { + parameters: { + query?: never; + header?: never; + path: { + flowId: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + workspaceId: string; + }; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown | unknown; + }; + }; + }; + }; + "analyticsRoutes.macActiveContactCountByWorkspaceAPI": { + parameters: { + query: { + workspaceId: string; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + macCount: number; + }; + }; + }; + }; + }; + }; + "analyticsRoutes.refLinkStats": { + parameters: { + query: { + workspaceId: string; + startDate: string; + endDate: string; + linkId: string; + timezone: string; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + dateReport: string; + count: number; + }[]; + }; + }; + }; + }; + }; + "analyticsRoutes.refLinkContacts": { + parameters: { + query: { + workspaceId: string; + linkId: string; + page: number; + perPage: number; + startDate?: string; + endDate?: string; + timezone?: string; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + contactId: string; + contactInboxId: string; + firstName: string | null; + lastName: string | null; + sourceId: string | null; + avatar: string | null; + channel: string | null; + conversationId: string; + occurredAt: string; + }[]; + total: number; + page: number; + pageCount: number; + }; + }; + }; + }; + }; + "analyticsRoutes.magicLinkStats": { + parameters: { + query: { + workspaceId: string; + startDate: string; + endDate: string; + linkId: string; + timezone: string; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + dateReport: string; + count: number; + }[]; + }; + }; + }; + }; + }; + "analyticsRoutes.magicLinkContacts": { + parameters: { + query: { + workspaceId: string; + linkId: string; + page: number; + perPage: number; + startDate?: string; + endDate?: string; + timezone?: string; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + contactId: string; + contactInboxId: string; + firstName: string | null; + lastName: string | null; + sourceId: string | null; + avatar: string | null; + channel: string | null; + conversationId: string; + occurredAt: string; + }[]; + total: number; + page: number; + pageCount: number; + }; + }; + }; + }; + }; + "adsAPI.listRules": { + parameters: { + query?: { + channel?: "whatsapp" | "facebook"; + }; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + workspaceId: string; + /** @enum {string} */ + channel: "whatsapp" | "facebook"; + integrationWhatsappId: string | null; + integrationFacebookAdsId: string | null; + adAccountId: string | null; + /** @enum {string} */ + eventType: "lead" | "purchase"; + trigger: { + /** @constant */ + type: "templateSent"; + templateIds: string[]; + } | { + /** @constant */ + type: "tagApplied"; + tagIds: string[]; + } | { + /** @constant */ + type: "keywordMatched"; + automatedResponseIds: string[]; + } | { + /** @constant */ + type: "contactReplied"; + firstReplyOnly: boolean; + }; + markAs: string | null; + enabled: boolean; + }[]; + }; + }; + }; + }; + }; + "appointmentCalendarsAPI.listAppointmentCalendarsForFlowAPI": { + parameters: { + query?: { + keyword?: string; + }; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + id: string; + name: string; + active: boolean; + }[]; + }; + }; + }; + }; + "appointmentsAPI.listContactAppointmentsAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + contactId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + id: string; + workspaceId: string; + calendarId: string; + contactId: string; + conversationId: string | null; + calendarName: string; + /** Format: date-time */ + startAt: string; + /** Format: date-time */ + endAt: string; + inviteeTimezone: string; + /** @enum {string} */ + status: "scheduled" | "cancelled"; + scheduleUrl: string; + }[]; + }; + }; + }; + }; + "aiMcpServerAPIs.validateAIMcpServerAuthenticatedAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + /** Format: uri */ + url: string; + auth: { + /** @constant */ + type: "none"; + } | { + /** @constant */ + type: "token"; + token: string; + } | { + /** @constant */ + type: "header"; + headers: { + header: string; + value: string; + }[]; + }; + }; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown | unknown; + }; + }; + }; + }; + "aiMcpServerAPIs.listAIMcpServersAuthenticatedAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string; + url: string; + auth: { + /** @constant */ + type: "none"; + } | { + /** @constant */ + type: "token"; + token: string; + } | { + /** @constant */ + type: "header"; + headers: { + header: string; + value: string; + }[]; + }; + availableTools: { + [key: string]: unknown; + }; + selectedTools: string[]; + workspaceId: string; + }[]; + }; + }; + }; + }; + }; + "aiAgentsAPI.listAIAgentsAPI": { + parameters: { + query?: { + page?: number; + perPage?: number; + sort?: { + id: string; + desc: boolean; + }[]; + name?: string; + }; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + id?: unknown; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + workspaceId?: unknown; + name: string; + prompt: string | null; + messages: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[])[]; + isDefault: boolean; + isRichResponse: boolean; + tools: string[]; + webSearchAuthorizedDomains: string[]; + models: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[])[]; + temperature: number; + maxOutputTokens: number; + }[]; + pageCount: number; + }; + }; + }; + }; + }; + "aiAgentsAPI.listAIAgentsWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + id?: unknown; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + workspaceId?: unknown; + name: string; + prompt: string | null; + messages: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[])[]; + isDefault: boolean; + isRichResponse: boolean; + tools: string[]; + webSearchAuthorizedDomains: string[]; + models: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[])[]; + temperature: number; + maxOutputTokens: number; + }[]; + pageCount: number; + }; + }; + }; + }; + }; + "broadcastAPIs.listBroadcastsWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + id?: unknown; + name: string; + /** @enum {string} */ + status: "scheduled" | "sent" | "sending" | "cancelled"; + /** @enum {string} */ + schedulesType: "now" | "future"; + /** Format: date-time */ + schedulesAt: string; + flowId?: unknown | null; + contactCount: number | null; + }[]; + }; + }; + }; + }; + }; + "broadcastAPIs.getBroadcastWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path: { + idOrName: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + id?: unknown; + name: string; + /** @enum {string} */ + status: "scheduled" | "sent" | "sending" | "cancelled"; + /** @enum {string} */ + schedulesType: "now" | "future"; + /** Format: date-time */ + schedulesAt: string; + flowId?: unknown | null; + contactCount: number | null; + }; + }; + }; + }; + }; + "broadcastAPIs.getBroadcastAudienceWorkspaceTokenAPI": { + parameters: { + query?: { + page?: number; + perPage?: number; + }; + header?: never; + path: { + idOrName: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + contactId: string; + contact: { + id: string; + firstName: string | null; + lastName: string | null; + fullName: string | null; + email: string | null; + phoneNumber: string | null; + avatar: string | null; + gender: string | null; + }; + sent: boolean; + }[]; + pageCount: number; + }; + }; + }; + }; + }; + "broadcastAPIs.privateListBroadcastOptionsAPI": { + parameters: { + query: { + channel: "omnichannel" | "webchat" | "messenger" | "whatsapp" | "zalo" | "smtp" | "telegram" | "instagram" | "tiktok"; + }; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + id: string; + name: string; + }[]; + }; + }; + }; + }; + }; + "broadcastAPIs.privateGetBatchBroadcastStatsAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + broadcastIds: string[]; + }; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + [key: string]: { + "message:sent": number; + "message:delivered": number; + "message:seen": number; + "flow:clicked": number; + "message:failed": number; + }; + }; + }; + }; + }; + }; + "broadcastAPIs.privateGetBroadcastTemplateDetailAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + broadcastId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": ({ + /** @constant */ + channel: "whatsapp"; + id: string; + name: string; + language: string; + category: string; + status: string; + components?: unknown; + integrationName: string | null; + } | { + /** @constant */ + channel: "messenger"; + id: string; + name: string; + language: string; + category: string; + status: string; + parameterFormat: string; + components?: unknown; + integrationName: string | null; + }) | null; + }; + }; + }; + }; + "broadcastAPIs.privateListBroadcastContactsAPI": { + parameters: { + query?: { + eventType?: "message:sent" | "message:delivered" | "message:seen" | "message:failed" | "message:received" | "flow:clicked" | "flow:ref"; + total?: number; + page?: number; + perPage?: number; + }; + header?: never; + path: { + workspaceId: string; + broadcastId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + contactId: string; + contactInboxId: string; + firstName: string | null; + lastName: string | null; + fullName: string | null; + sourceId: string | null; + avatar: string | null; + /** @enum {string} */ + channel: "omnichannel" | "webchat" | "messenger" | "whatsapp" | "zalo" | "smtp" | "telegram" | "instagram" | "tiktok"; + errorContent: string | null; + occurredAt: string; + conversationId: string; + }[]; + total: number; + page: number; + pageCount: number; + }; + }; + }; + }; + }; + "conversationsAPI.listConversationsAuthenticatedAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: { + content: { + "application/json": { + /** @enum {string} */ + botCategory?: "bot" | "human" | "all"; + assignedId?: string | null; + channel?: "omnichannel" | "webchat" | "messenger" | "whatsapp" | "zalo" | "smtp" | "telegram" | "instagram" | "tiktok"; + status?: ("noAdminReply" | "unread" | "followUp" | "archived" | "blocked")[]; + keyword?: string; + botEnabled?: boolean | null; + tags?: ("noAdminReply" | "unread" | "followUp" | "archived" | "blocked")[]; + contactFilter?: { + /** @enum {string} */ + operator: "and" | "or"; + conditions: (({ + /** @constant */ + field: "locale"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "language"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "fullName"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "country"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "continent"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "gender"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "subscribedToBroadcast"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "contactCreatedAt"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "source"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "conversationTransferredToHuman"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "interactedInLast24h"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "followUp"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "archived"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "blocked"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "currentChannel"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "inbox"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "timezone"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastSeen"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastSent"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastInteraction"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "contactCreatedDateMinutesAgo"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastSeenMinutesAgo"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastInteractionMinutesAgo"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastUserInput"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastComment"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastUserInputType"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "email"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "phone"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "hasContactInfo"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "tags"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "broadcastSent"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "broadcastDelivered"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "broadcastSeen"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "broadcastClicked"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "broadcastFailed"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "subscribedToDripCampaign"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "entryPointsLinks"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "questionnaireStarted"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "questionnaireInProgress"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "questionnaireFinished"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "conversationAssigned"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "unreplied"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "unread"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "existingContact"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "consecutiveAiFailures"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "emailWasVerified"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "optedInForEmail"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "fromCtwaAd"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "ctwaConversion"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + }) | { + /** @constant */ + field: "customField"; + customFieldId: string; + /** @enum {string} */ + customFieldType?: "shortText" | "email" | "phoneNumber" | "number" | "date" | "datetime" | "boolean" | "longText"; + /** @enum {string} */ + valueType: "multiSelect" | "select" | "text" | "boolean" | "datetime" | "number"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | [ + string, + string + ]; + } | { + /** @constant */ + field: "couponTopic"; + topicId: string; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string; + } | { + /** @constant */ + field: "ctwaRetarget"; + /** @enum {string} */ + segment: "conversations" | "leads" | "purchases"; + adId?: string; + integrationWhatsappId?: string; + since: string; + until: string; + })[]; + timezone?: string; + }; + cursor?: string; + perPage?: number | null; + sort?: { + id: string; + desc: boolean; + }[] | null; + }; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: ({ + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + botEnabled: boolean; + botResumeAt: string | null; + archivedAt: string | null; + additionalAttributes: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]) | null; + contactLastReadAt: string | null; + agentLastReadAt: string | null; + aiContextLastMessageId?: unknown | null; + lastActivityAt: string | null; + followed: boolean; + assignedUserId?: unknown | null; + assignedInboxTeamId?: unknown | null; + workspaceId: string; + contactId: string; + sourceId: string | null; + lastStep: string | null; + currentStep: string | null; + adminRepliedAt: string | null; + contactRepliedAt: string | null; + } & { + contactInboxes: { + id: string; + contactId: string; + inboxId: string; + channel: string; + source: string; + sourceId: string; + sourceUserId?: string | null; + sourceUsername?: string | null; + language: string | null; + lastIncomingMessageAt: string | null; + contactLastReadAt?: string | null; + inbox: { + name: string; + }; + }[]; + messages: (({ + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + conversationId: string; + contactInboxId: string; + workspaceId: string; + text: string | null; + contentAttributes: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]) | null; + /** @enum {string} */ + messageType: "incoming" | "outgoing" | "activity"; + /** @enum {string} */ + contentType: "text" | "location" | "refLink"; + /** @enum {string} */ + senderType: "bot" | "contact" | "system" | "user" | "api"; + senderId?: unknown | null; + sourceId: string | null; + deletedAt: string | null; + /** @enum {string} */ + type: "message" | "comment"; + parentId: string | null; + attributes: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]) | null; + sendError: string | null; + } & { + clientId?: string; + }) & { + attachmentCount?: number; + attachments?: ({ + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + workspaceId?: unknown; + conversationId?: unknown; + messageId?: unknown; + /** Format: date-time */ + messageCreatedAt: string; + /** @enum {string} */ + fileType: "image" | "video" | "audio" | "gif" | "file"; + sourceId: string | null; + mimeType: string; + width: number | null; + height: number | null; + size: number; + thumbnailPath: string | null; + originPath: string; + name: string | null; + } & { + url?: string | null; + })[]; + user?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string | null; + email: string; + emailVerified: boolean; + image: string | null; + isAnonymous: boolean; + mustChangePassword: boolean; + tenantId?: unknown; + }; + contact?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + avatar: string | null; + phoneNumber: string | null; + email: string | null; + emailVerified: boolean; + emailOptIn: boolean; + firstName: string | null; + lastName: string | null; + fullName: string | null; + gender: ("male" | "female" | "unknown") | null; + lastReadAt: string | null; + ref: string | null; + country: string | null; + state: string | null; + city: string | null; + location: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]) | null; + locale: string | null; + timezone: string | null; + subscribedAt: string | null; + broadcastSubscribedAt: string | null; + blockedAt: string | null; + workspaceId: string; + }; + clientId?: string; + })[]; + contact: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + avatar: string | null; + phoneNumber: string | null; + email: string | null; + emailVerified: boolean; + emailOptIn: boolean; + firstName: string | null; + lastName: string | null; + fullName: string | null; + gender: ("male" | "female" | "unknown") | null; + lastReadAt: string | null; + ref: string | null; + country: string | null; + state: string | null; + city: string | null; + location: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]) | null; + locale: string | null; + timezone: string | null; + subscribedAt: string | null; + broadcastSubscribedAt: string | null; + blockedAt: string | null; + workspaceId: string; + } | null; + assignedUser: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string | null; + email: string; + emailVerified: boolean; + image: string | null; + isAnonymous: boolean; + mustChangePassword: boolean; + tenantId?: unknown; + } | null; + assignedInboxTeam: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + workspaceId: string; + name: string; + } | null; + })[]; + nextCursor: string | null; + prevCursor: string | null; + }; + }; + }; + }; + }; + "conversationsAPI.listConversationsByPOSTAuthenticatedAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: { + content: { + "application/json": { + /** @enum {string} */ + botCategory?: "bot" | "human" | "all"; + assignedId?: string | null; + channel?: "omnichannel" | "webchat" | "messenger" | "whatsapp" | "zalo" | "smtp" | "telegram" | "instagram" | "tiktok"; + status?: ("noAdminReply" | "unread" | "followUp" | "archived" | "blocked")[]; + keyword?: string; + botEnabled?: boolean | null; + tags?: ("noAdminReply" | "unread" | "followUp" | "archived" | "blocked")[]; + contactFilter?: { + /** @enum {string} */ + operator: "and" | "or"; + conditions: (({ + /** @constant */ + field: "locale"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "language"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "fullName"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "country"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "continent"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "gender"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "subscribedToBroadcast"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "contactCreatedAt"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "source"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "conversationTransferredToHuman"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "interactedInLast24h"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "followUp"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "archived"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "blocked"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "currentChannel"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "inbox"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "timezone"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastSeen"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastSent"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastInteraction"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "contactCreatedDateMinutesAgo"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastSeenMinutesAgo"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastInteractionMinutesAgo"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastUserInput"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastComment"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastUserInputType"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "email"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "phone"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "hasContactInfo"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "tags"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "broadcastSent"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "broadcastDelivered"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "broadcastSeen"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "broadcastClicked"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "broadcastFailed"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "subscribedToDripCampaign"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "entryPointsLinks"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "questionnaireStarted"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "questionnaireInProgress"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "questionnaireFinished"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "conversationAssigned"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "unreplied"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "unread"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "existingContact"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "consecutiveAiFailures"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "emailWasVerified"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "optedInForEmail"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "fromCtwaAd"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "ctwaConversion"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + }) | { + /** @constant */ + field: "customField"; + customFieldId: string; + /** @enum {string} */ + customFieldType?: "shortText" | "email" | "phoneNumber" | "number" | "date" | "datetime" | "boolean" | "longText"; + /** @enum {string} */ + valueType: "multiSelect" | "select" | "text" | "boolean" | "datetime" | "number"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | [ + string, + string + ]; + } | { + /** @constant */ + field: "couponTopic"; + topicId: string; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string; + } | { + /** @constant */ + field: "ctwaRetarget"; + /** @enum {string} */ + segment: "conversations" | "leads" | "purchases"; + adId?: string; + integrationWhatsappId?: string; + since: string; + until: string; + })[]; + timezone?: string; + }; + cursor?: string; + perPage?: number | null; + sort?: { + id: string; + desc: boolean; + }[] | null; + }; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: ({ + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + botEnabled: boolean; + botResumeAt: string | null; + archivedAt: string | null; + additionalAttributes: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]) | null; + contactLastReadAt: string | null; + agentLastReadAt: string | null; + aiContextLastMessageId?: unknown | null; + lastActivityAt: string | null; + followed: boolean; + assignedUserId?: unknown | null; + assignedInboxTeamId?: unknown | null; + workspaceId: string; + contactId: string; + sourceId: string | null; + lastStep: string | null; + currentStep: string | null; + adminRepliedAt: string | null; + contactRepliedAt: string | null; + } & { + contactInboxes: { + id: string; + contactId: string; + inboxId: string; + channel: string; + source: string; + sourceId: string; + sourceUserId?: string | null; + sourceUsername?: string | null; + language: string | null; + lastIncomingMessageAt: string | null; + contactLastReadAt?: string | null; + inbox: { + name: string; + }; + }[]; + messages: (({ + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + conversationId: string; + contactInboxId: string; + workspaceId: string; + text: string | null; + contentAttributes: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]) | null; + /** @enum {string} */ + messageType: "incoming" | "outgoing" | "activity"; + /** @enum {string} */ + contentType: "text" | "location" | "refLink"; + /** @enum {string} */ + senderType: "bot" | "contact" | "system" | "user" | "api"; + senderId?: unknown | null; + sourceId: string | null; + deletedAt: string | null; + /** @enum {string} */ + type: "message" | "comment"; + parentId: string | null; + attributes: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]) | null; + sendError: string | null; + } & { + clientId?: string; + }) & { + attachmentCount?: number; + attachments?: ({ + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + workspaceId?: unknown; + conversationId?: unknown; + messageId?: unknown; + /** Format: date-time */ + messageCreatedAt: string; + /** @enum {string} */ + fileType: "image" | "video" | "audio" | "gif" | "file"; + sourceId: string | null; + mimeType: string; + width: number | null; + height: number | null; + size: number; + thumbnailPath: string | null; + originPath: string; + name: string | null; + } & { + url?: string | null; + })[]; + user?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string | null; + email: string; + emailVerified: boolean; + image: string | null; + isAnonymous: boolean; + mustChangePassword: boolean; + tenantId?: unknown; + }; + contact?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + avatar: string | null; + phoneNumber: string | null; + email: string | null; + emailVerified: boolean; + emailOptIn: boolean; + firstName: string | null; + lastName: string | null; + fullName: string | null; + gender: ("male" | "female" | "unknown") | null; + lastReadAt: string | null; + ref: string | null; + country: string | null; + state: string | null; + city: string | null; + location: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]) | null; + locale: string | null; + timezone: string | null; + subscribedAt: string | null; + broadcastSubscribedAt: string | null; + blockedAt: string | null; + workspaceId: string; + }; + clientId?: string; + })[]; + contact: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + avatar: string | null; + phoneNumber: string | null; + email: string | null; + emailVerified: boolean; + emailOptIn: boolean; + firstName: string | null; + lastName: string | null; + fullName: string | null; + gender: ("male" | "female" | "unknown") | null; + lastReadAt: string | null; + ref: string | null; + country: string | null; + state: string | null; + city: string | null; + location: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]) | null; + locale: string | null; + timezone: string | null; + subscribedAt: string | null; + broadcastSubscribedAt: string | null; + blockedAt: string | null; + workspaceId: string; + } | null; + assignedUser: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string | null; + email: string; + emailVerified: boolean; + image: string | null; + isAnonymous: boolean; + mustChangePassword: boolean; + tenantId?: unknown; + } | null; + assignedInboxTeam: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + workspaceId: string; + name: string; + } | null; + })[]; + nextCursor: string | null; + prevCursor: string | null; + }; + }; + }; + }; + }; + "conversationsAPI.findConversationAuthenticatedAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + botEnabled: boolean; + botResumeAt: string | null; + archivedAt: string | null; + additionalAttributes: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]) | null; + contactLastReadAt: string | null; + agentLastReadAt: string | null; + aiContextLastMessageId?: unknown | null; + lastActivityAt: string | null; + followed: boolean; + assignedUserId?: unknown | null; + assignedInboxTeamId?: unknown | null; + workspaceId: string; + contactId: string; + sourceId: string | null; + lastStep: string | null; + currentStep: string | null; + adminRepliedAt: string | null; + contactRepliedAt: string | null; + } & { + contactInboxes: { + id: string; + contactId: string; + inboxId: string; + channel: string; + source: string; + sourceId: string; + sourceUserId?: string | null; + sourceUsername?: string | null; + language: string | null; + lastIncomingMessageAt: string | null; + contactLastReadAt?: string | null; + inbox: { + name: string; + }; + }[]; + messages: (({ + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + conversationId: string; + contactInboxId: string; + workspaceId: string; + text: string | null; + contentAttributes: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]) | null; + /** @enum {string} */ + messageType: "incoming" | "outgoing" | "activity"; + /** @enum {string} */ + contentType: "text" | "location" | "refLink"; + /** @enum {string} */ + senderType: "bot" | "contact" | "system" | "user" | "api"; + senderId?: unknown | null; + sourceId: string | null; + deletedAt: string | null; + /** @enum {string} */ + type: "message" | "comment"; + parentId: string | null; + attributes: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]) | null; + sendError: string | null; + } & { + clientId?: string; + }) & { + attachmentCount?: number; + attachments?: ({ + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + workspaceId?: unknown; + conversationId?: unknown; + messageId?: unknown; + /** Format: date-time */ + messageCreatedAt: string; + /** @enum {string} */ + fileType: "image" | "video" | "audio" | "gif" | "file"; + sourceId: string | null; + mimeType: string; + width: number | null; + height: number | null; + size: number; + thumbnailPath: string | null; + originPath: string; + name: string | null; + } & { + url?: string | null; + })[]; + user?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string | null; + email: string; + emailVerified: boolean; + image: string | null; + isAnonymous: boolean; + mustChangePassword: boolean; + tenantId?: unknown; + }; + contact?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + avatar: string | null; + phoneNumber: string | null; + email: string | null; + emailVerified: boolean; + emailOptIn: boolean; + firstName: string | null; + lastName: string | null; + fullName: string | null; + gender: ("male" | "female" | "unknown") | null; + lastReadAt: string | null; + ref: string | null; + country: string | null; + state: string | null; + city: string | null; + location: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]) | null; + locale: string | null; + timezone: string | null; + subscribedAt: string | null; + broadcastSubscribedAt: string | null; + blockedAt: string | null; + workspaceId: string; + }; + clientId?: string; + })[]; + contact: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + avatar: string | null; + phoneNumber: string | null; + email: string | null; + emailVerified: boolean; + emailOptIn: boolean; + firstName: string | null; + lastName: string | null; + fullName: string | null; + gender: ("male" | "female" | "unknown") | null; + lastReadAt: string | null; + ref: string | null; + country: string | null; + state: string | null; + city: string | null; + location: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]) | null; + locale: string | null; + timezone: string | null; + subscribedAt: string | null; + broadcastSubscribedAt: string | null; + blockedAt: string | null; + workspaceId: string; + } | null; + assignedUser: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string | null; + email: string; + emailVerified: boolean; + image: string | null; + isAnonymous: boolean; + mustChangePassword: boolean; + tenantId?: unknown; + } | null; + assignedInboxTeam: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + workspaceId: string; + name: string; + } | null; + }; + }; + }; + }; + }; + }; + "conversationsAPI.getPostDetailsAuthenticatedAPI": { + parameters: { + query: { + inboxId: string; + postId: string; + channel: "omnichannel" | "webchat" | "messenger" | "whatsapp" | "zalo" | "smtp" | "telegram" | "instagram" | "tiktok"; + }; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + text?: string; + picture?: string; + from?: { + id: string; + name: string; + }; + createdAt: string; + link?: string; + }; + }; + }; + }; + }; + "conversationsAPI.assignConversationsAuthenticatedAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + contactIds: string[]; + assignedId: string | null; + }; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + success: true; + }; + }; + }; + }; + }; + "conversationsAPI.archiveConversationsAuthenticatedAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + ids: string[]; + }; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + success: true; + }; + }; + }; + }; + }; + "conversationsAPI.unarchiveConversationsAuthenticatedAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + ids: string[]; + }; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + success: true; + }; + }; + }; + }; + }; + "conversationsAPI.enableBotAuthenticatedAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + ids: string[]; + }; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + success: true; + }; + }; + }; + }; + }; + "conversationsAPI.disableBotAuthenticatedAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + ids: string[]; + }; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + success: true; + }; + }; + }; + }; + }; + "conversationsAPI.readConversationAuthenticatedAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + success: true; + }; + }; + }; + }; + }; + "conversationsAPI.unreadConversationAuthenticatedAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + success: true; + agentLastReadAt: string | null; + }; + }; + }; + }; + }; + "conversationsAPI.followConversationAuthenticatedAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + success: true; + }; + }; + }; + }; + }; + "conversationsAPI.unfollowConversationAuthenticatedAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + success: true; + }; + }; + }; + }; + }; + "conversationsAPI.listConversationsWorkspaceTokenAPI": { + parameters: { + query?: { + botCategory?: "bot" | "human" | "all"; + assignedId?: string | null; + channel?: "omnichannel" | "webchat" | "messenger" | "whatsapp" | "zalo" | "smtp" | "telegram" | "instagram" | "tiktok"; + status?: ("noAdminReply" | "unread" | "followUp" | "archived" | "blocked")[]; + keyword?: string; + botEnabled?: boolean | null; + tags?: ("noAdminReply" | "unread" | "followUp" | "archived" | "blocked")[]; + contactFilter?: { + /** @enum {string} */ + operator: "and" | "or"; + conditions: (({ + /** @constant */ + field: "locale"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "language"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "fullName"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "country"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "continent"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "gender"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "subscribedToBroadcast"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "contactCreatedAt"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "source"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "conversationTransferredToHuman"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "interactedInLast24h"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "followUp"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "archived"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "blocked"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "currentChannel"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "inbox"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "timezone"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastSeen"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastSent"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastInteraction"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "contactCreatedDateMinutesAgo"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastSeenMinutesAgo"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastInteractionMinutesAgo"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastUserInput"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastComment"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastUserInputType"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "email"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "phone"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "hasContactInfo"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "tags"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "broadcastSent"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "broadcastDelivered"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "broadcastSeen"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "broadcastClicked"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "broadcastFailed"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "subscribedToDripCampaign"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "entryPointsLinks"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "questionnaireStarted"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "questionnaireInProgress"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "questionnaireFinished"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "conversationAssigned"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "unreplied"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "unread"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "existingContact"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "consecutiveAiFailures"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "emailWasVerified"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "optedInForEmail"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "fromCtwaAd"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "ctwaConversion"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + }) | { + /** @constant */ + field: "customField"; + customFieldId: string; + /** @enum {string} */ + customFieldType?: "shortText" | "email" | "phoneNumber" | "number" | "date" | "datetime" | "boolean" | "longText"; + /** @enum {string} */ + valueType: "multiSelect" | "select" | "text" | "boolean" | "datetime" | "number"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | [ + string, + string + ]; + } | { + /** @constant */ + field: "couponTopic"; + topicId: string; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string; + } | { + /** @constant */ + field: "ctwaRetarget"; + /** @enum {string} */ + segment: "conversations" | "leads" | "purchases"; + adId?: string; + integrationWhatsappId?: string; + since: string; + until: string; + })[]; + timezone?: string; + }; + cursor?: string; + perPage?: number | null; + sort?: { + id: string; + desc: boolean; + }[] | null; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: ({ + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + botEnabled: boolean; + botResumeAt: string | null; + archivedAt: string | null; + additionalAttributes: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]) | null; + contactLastReadAt: string | null; + agentLastReadAt: string | null; + aiContextLastMessageId?: unknown | null; + lastActivityAt: string | null; + followed: boolean; + assignedUserId?: unknown | null; + assignedInboxTeamId?: unknown | null; + workspaceId: string; + contactId: string; + sourceId: string | null; + lastStep: string | null; + currentStep: string | null; + adminRepliedAt: string | null; + contactRepliedAt: string | null; + } & { + contactInboxes: { + id: string; + contactId: string; + inboxId: string; + channel: string; + source: string; + sourceId: string; + sourceUserId?: string | null; + sourceUsername?: string | null; + language: string | null; + lastIncomingMessageAt: string | null; + contactLastReadAt?: string | null; + inbox: { + name: string; + }; + }[]; + messages: (({ + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + conversationId: string; + contactInboxId: string; + workspaceId: string; + text: string | null; + contentAttributes: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]) | null; + /** @enum {string} */ + messageType: "incoming" | "outgoing" | "activity"; + /** @enum {string} */ + contentType: "text" | "location" | "refLink"; + /** @enum {string} */ + senderType: "bot" | "contact" | "system" | "user" | "api"; + senderId?: unknown | null; + sourceId: string | null; + deletedAt: string | null; + /** @enum {string} */ + type: "message" | "comment"; + parentId: string | null; + attributes: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]) | null; + sendError: string | null; + } & { + clientId?: string; + }) & { + attachmentCount?: number; + attachments?: ({ + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + workspaceId?: unknown; + conversationId?: unknown; + messageId?: unknown; + /** Format: date-time */ + messageCreatedAt: string; + /** @enum {string} */ + fileType: "image" | "video" | "audio" | "gif" | "file"; + sourceId: string | null; + mimeType: string; + width: number | null; + height: number | null; + size: number; + thumbnailPath: string | null; + originPath: string; + name: string | null; + } & { + url?: string | null; + })[]; + user?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string | null; + email: string; + emailVerified: boolean; + image: string | null; + isAnonymous: boolean; + mustChangePassword: boolean; + tenantId?: unknown; + }; + contact?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + avatar: string | null; + phoneNumber: string | null; + email: string | null; + emailVerified: boolean; + emailOptIn: boolean; + firstName: string | null; + lastName: string | null; + fullName: string | null; + gender: ("male" | "female" | "unknown") | null; + lastReadAt: string | null; + ref: string | null; + country: string | null; + state: string | null; + city: string | null; + location: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]) | null; + locale: string | null; + timezone: string | null; + subscribedAt: string | null; + broadcastSubscribedAt: string | null; + blockedAt: string | null; + workspaceId: string; + }; + clientId?: string; + })[]; + contact: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + avatar: string | null; + phoneNumber: string | null; + email: string | null; + emailVerified: boolean; + emailOptIn: boolean; + firstName: string | null; + lastName: string | null; + fullName: string | null; + gender: ("male" | "female" | "unknown") | null; + lastReadAt: string | null; + ref: string | null; + country: string | null; + state: string | null; + city: string | null; + location: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]) | null; + locale: string | null; + timezone: string | null; + subscribedAt: string | null; + broadcastSubscribedAt: string | null; + blockedAt: string | null; + workspaceId: string; + } | null; + assignedUser: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string | null; + email: string; + emailVerified: boolean; + image: string | null; + isAnonymous: boolean; + mustChangePassword: boolean; + tenantId?: unknown; + } | null; + assignedInboxTeam: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + workspaceId: string; + name: string; + } | null; + })[]; + nextCursor: string | null; + prevCursor: string | null; + }; + }; + }; + }; + }; + "couponsAPI.listCouponTopicsAPI": { + parameters: { + query?: { + archived?: boolean; + search?: string | null; + page?: number; + perPage?: number; + sort?: { + id: string; + desc: boolean; + }[]; + }; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + id: string; + workspaceId: string; + name: string; + description: string | null; + expiresAt: string | null; + /** @enum {string} */ + status: "active" | "archived"; + deletedAt: string | null; + hasEverHadCoupon: boolean; + createdById: string | null; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + couponCount: number; + }[]; + pageCount: number; + total: number; + }; + }; + }; + }; + }; + "couponsAPI.createCouponTopicAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + name: string; + description?: string | null; + expiresAt?: string | null; + }; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + id: string; + workspaceId: string; + name: string; + description: string | null; + expiresAt: string | null; + /** @enum {string} */ + status: "active" | "archived"; + deletedAt: string | null; + hasEverHadCoupon: boolean; + createdById: string | null; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + couponCount?: number; + }; + }; + }; + }; + }; + "couponsAPI.deleteCouponTopicAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + topicId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + id: string; + workspaceId: string; + name: string; + description: string | null; + expiresAt: string | null; + /** @enum {string} */ + status: "active" | "archived"; + deletedAt: string | null; + hasEverHadCoupon: boolean; + createdById: string | null; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + couponCount?: number; + }; + }; + }; + }; + }; + "couponsAPI.updateCouponTopicAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + topicId: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + name: string; + description?: string | null; + expiresAt?: string | null; + }; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + id: string; + workspaceId: string; + name: string; + description: string | null; + expiresAt: string | null; + /** @enum {string} */ + status: "active" | "archived"; + deletedAt: string | null; + hasEverHadCoupon: boolean; + createdById: string | null; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + couponCount?: number; + }; + }; + }; + }; + }; + "couponsAPI.archiveCouponTopicAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + topicId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + id: string; + workspaceId: string; + name: string; + description: string | null; + expiresAt: string | null; + /** @enum {string} */ + status: "active" | "archived"; + deletedAt: string | null; + hasEverHadCoupon: boolean; + createdById: string | null; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + couponCount?: number; + }; + }; + }; + }; + }; + "couponsAPI.unarchiveCouponTopicAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + topicId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + id: string; + workspaceId: string; + name: string; + description: string | null; + expiresAt: string | null; + /** @enum {string} */ + status: "active" | "archived"; + deletedAt: string | null; + hasEverHadCoupon: boolean; + createdById: string | null; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + couponCount?: number; + }; + }; + }; + }; + }; + "couponsAPI.listCouponsAPI": { + parameters: { + query?: { + topicId?: string | null; + issueStatus?: ("published" | "unpublished") | null; + usageStatus?: ("used" | "notUsed") | null; + search?: string | null; + page?: number; + perPage?: number; + }; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + id: string; + workspaceId: string; + topicId: string; + topicName: string; + code: string; + issuedContactId: string | null; + issuedAt: string | null; + usedAt: string | null; + /** @enum {string} */ + issueStatus: "published" | "unpublished"; + /** @enum {string} */ + usageStatus: "used" | "notUsed"; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + }[]; + pageCount: number; + total: number; + }; + }; + }; + }; + }; + "couponsAPI.listCouponTopicOptionsAPI": { + parameters: { + query?: { + keyword?: string; + issueableOnly?: boolean; + }; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + id: string; + name: string; + expiresAt: string | null; + }[]; + }; + }; + }; + }; + "couponsAPI.countCouponExportAPI": { + parameters: { + query?: { + topicId?: string | null; + issueStatus?: ("published" | "unpublished") | null; + usageStatus?: ("used" | "notUsed") | null; + search?: string | null; + page?: number; + perPage?: number; + }; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + count: number; + }; + }; + }; + }; + }; + "couponsAPI.getCouponExportFileAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + fileId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @enum {string} */ + status: "pending" | "uploaded" | "failed"; + fileName: string; + downloadUrl: string | null; + totalRecords: number | null; + }; + }; + }; + }; + }; + "couponsAPI.listContactCouponsAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + contactId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + id: string; + topicId: string; + topicName: string; + code: string; + usedAt: string | null; + issuedAt: string | null; + }[]; + }; + }; + }; + }; + "deviceTokensAPI.registerDeviceTokenAPI": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + workspaceId?: string; + /** @enum {string} */ + platform: "ios" | "android"; + token: string; + }; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + success: true; + }; + }; + }; + }; + }; + "deviceTokensAPI.unregisterDeviceTokenAPI": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + token: string; + }; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + success: true; + }; + }; + }; + }; + }; + "dynamicImagesAPI.getDynamicImageAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string; + workspaceId: string; + customFieldId: string | null; + data: { + width: number; + height: number; + elements: ({ + id: string; + x: number; + y: number; + width: number; + height: number; + /** @default false */ + priority: boolean; + /** @constant */ + type: "image"; + /** @enum {string} */ + imageType: "url" | "avatarUser" | "customField"; + url?: string; + customFieldId?: string; + /** + * @default square + * @enum {string} + */ + imageStyle: "square" | "circle"; + } | { + id: string; + x: number; + y: number; + width: number; + height: number; + /** @default false */ + priority: boolean; + /** @constant */ + type: "qrCode"; + text: string; + size: number; + color: string; + logoUrl?: string; + } | { + id: string; + x: number; + y: number; + width: number; + height: number; + /** @default false */ + priority: boolean; + /** @constant */ + type: "text"; + text: string; + fontSize: number; + /** @enum {string} */ + fontFamily: "arial" | "serif" | "roboto" | "greatVibes"; + /** @enum {string} */ + align: "left" | "center" | "right"; + color: string; + /** @default false */ + bold: boolean; + /** @default false */ + italic: boolean; + /** @default false */ + uppercase: boolean; + })[]; + }; + backgroundUrl: string | null; + enabled: boolean; + }; + }; + }; + }; + }; + "emailTopicsAPI.privateListWorkspaceEmailTopicsAPI": { + parameters: { + query?: { + page?: number | null; + perPage?: number | null; + sort?: { + id: string; + desc: boolean; + }[] | null; + name?: string | null; + folderId?: string | null; + }; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string; + workspaceId: string; + folderId: string | null; + sendsTotal: number; + deliveredsTotal: number; + seensTotal: number; + clicksTotal: number; + }[]; + pageCount: number; + }; + }; + }; + }; + }; + "emailTopicsAPI.privateCreateWorkspaceEmailTopicAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + name: string; + folderId?: string | null; + }; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + id: string; + }; + }; + }; + }; + }; + "emailTopicsAPI.privateUpdateEmailTopicAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + id: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + name: string; + }; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown | unknown; + }; + }; + }; + }; + "emailTopicsAPI.privateDeleteEmailTopicAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown | unknown; + }; + }; + }; + }; + "tagsAPI.privateListWorkspaceTagsAPI": { + parameters: { + query?: { + page?: number | null; + perPage?: number | null; + sort?: { + id: string; + desc: boolean; + }[] | null; + name?: string | null; + folderId?: string | null; + }; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string; + deletedAt: string | null; + folderId: string | null; + workspaceId: string; + }[]; + pageCount: number; + }; + }; + }; + }; + }; + "tagsAPI.privateCreateWorkspaceTagAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + name: string; + folderId?: string | null; + }; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + id: string; + }; + }; + }; + }; + }; + "tagsAPI.privateUpdateTagAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + id: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + name: string; + }; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown | unknown; + }; + }; + }; + }; + "tagsAPI.privateDeleteTagsAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown | unknown; + }; + }; + }; + }; + "tagsAPI.listTagsWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + id: string; + name: string; + }[]; + }; + }; + }; + /** @description 400 */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "businessError"; + /** @constant */ + status: 400; + /** @default An error occurred while processing your request */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + /** @description 404 */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "notFound"; + /** @constant */ + status: 404; + /** @default Resource not found */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + /** @description 429 */ + 429: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "rateLimitExceeded"; + /** @constant */ + status: 429; + /** @default Rate limit exceeded */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + }; + }; + "tagsAPI.createTagWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + name: string; + }; + }; + }; + responses: { + /** @description OK */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + id: string; + name: string; + }; + }; + }; + /** @description 400 */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "businessError"; + /** @constant */ + status: 400; + /** @default An error occurred while processing your request */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + /** @description 422 */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "invalidRequestData"; + /** @constant */ + status: 422; + /** @default Validation error */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + /** @description 429 */ + 429: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "rateLimitExceeded"; + /** @constant */ + status: 429; + /** @default Rate limit exceeded */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + }; + }; + "tagsAPI.getTagWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path: { + idOrName: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + id: string; + name: string; + }; + }; + }; + /** @description 400 */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "businessError"; + /** @constant */ + status: 400; + /** @default An error occurred while processing your request */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + /** @description 404 */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "notFound"; + /** @constant */ + status: 404; + /** @default Resource not found */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + /** @description 429 */ + 429: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "rateLimitExceeded"; + /** @constant */ + status: 429; + /** @default Rate limit exceeded */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + }; + }; + "tagsAPI.updateTagWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + name: string; + }; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + id: string; + name: string; + }; + }; + }; + /** @description 400 */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "businessError"; + /** @constant */ + status: 400; + /** @default An error occurred while processing your request */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + /** @description 422 */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "invalidRequestData"; + /** @constant */ + status: 422; + /** @default Validation error */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + /** @description 429 */ + 429: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "rateLimitExceeded"; + /** @constant */ + status: 429; + /** @default Rate limit exceeded */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + }; + }; + "tagsAPI.deleteTagWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 204: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown | unknown; + }; + }; + /** @description 400 */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "businessError"; + /** @constant */ + status: 400; + /** @default An error occurred while processing your request */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + /** @description 404 */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "notFound"; + /** @constant */ + status: 404; + /** @default Resource not found */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + /** @description 429 */ + 429: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "rateLimitExceeded"; + /** @constant */ + status: 429; + /** @default Rate limit exceeded */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + }; + }; + "customFieldsAPI.listCustomFieldsWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + id: string; + name: string; + /** @enum {string} */ + type: "shortText" | "email" | "phoneNumber" | "number" | "date" | "datetime" | "boolean" | "longText"; + description: string | null; + }[]; + }; + }; + }; + /** @description 400 */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "businessError"; + /** @constant */ + status: 400; + /** @default An error occurred while processing your request */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + /** @description 404 */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "notFound"; + /** @constant */ + status: 404; + /** @default Resource not found */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + /** @description 429 */ + 429: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "rateLimitExceeded"; + /** @constant */ + status: 429; + /** @default Rate limit exceeded */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + }; + }; + "customFieldsAPI.createCustomFieldWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + name: string; + /** @enum {string} */ + type: "shortText" | "email" | "phoneNumber" | "number" | "date" | "datetime" | "boolean" | "longText"; + }; + }; + }; + responses: { + /** @description OK */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + id: string; + name: string; + /** @enum {string} */ + type: "shortText" | "email" | "phoneNumber" | "number" | "date" | "datetime" | "boolean" | "longText"; + description: string | null; + }; + }; + }; + /** @description 400 */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "businessError"; + /** @constant */ + status: 400; + /** @default An error occurred while processing your request */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + /** @description 422 */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "invalidRequestData"; + /** @constant */ + status: 422; + /** @default Validation error */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + /** @description 429 */ + 429: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "rateLimitExceeded"; + /** @constant */ + status: 429; + /** @default Rate limit exceeded */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + }; + }; + "customFieldsAPI.getCustomFieldWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path: { + idOrName: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + id: string; + name: string; + /** @enum {string} */ + type: "shortText" | "email" | "phoneNumber" | "number" | "date" | "datetime" | "boolean" | "longText"; + description: string | null; + }; + }; + }; + /** @description 400 */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "businessError"; + /** @constant */ + status: 400; + /** @default An error occurred while processing your request */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + /** @description 404 */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "notFound"; + /** @constant */ + status: 404; + /** @default Resource not found */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + /** @description 429 */ + 429: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "rateLimitExceeded"; + /** @constant */ + status: 429; + /** @default Rate limit exceeded */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + }; + }; + "customFieldsAPI.updateCustomFieldWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + name: string; + description?: string; + folderId?: string | null; + }; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + id: string; + name: string; + /** @enum {string} */ + type: "shortText" | "email" | "phoneNumber" | "number" | "date" | "datetime" | "boolean" | "longText"; + description: string | null; + }; + }; + }; + /** @description 400 */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "businessError"; + /** @constant */ + status: 400; + /** @default An error occurred while processing your request */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + /** @description 422 */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "invalidRequestData"; + /** @constant */ + status: 422; + /** @default Validation error */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + /** @description 429 */ + 429: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "rateLimitExceeded"; + /** @constant */ + status: 429; + /** @default Rate limit exceeded */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + }; + }; + "customFieldsAPI.deleteCustomFieldWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 204: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown | unknown; + }; + }; + /** @description 400 */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "businessError"; + /** @constant */ + status: 400; + /** @default An error occurred while processing your request */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + /** @description 404 */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "notFound"; + /** @constant */ + status: 404; + /** @default Resource not found */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + /** @description 429 */ + 429: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "rateLimitExceeded"; + /** @constant */ + status: 429; + /** @default Rate limit exceeded */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + }; + }; + "customFieldsAPI.privateListCustomFieldsAPI": { + parameters: { + query?: { + page?: number | null; + perPage?: number | null; + sort?: { + id: string; + desc: boolean; + }[] | null; + name?: string | null; + folderId?: string | null; + }; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string; + /** @enum {string} */ + type: "shortText" | "email" | "phoneNumber" | "number" | "date" | "datetime" | "boolean" | "longText"; + description: string | null; + folderId: string | null; + showInInbox: boolean; + workspaceId: string; + }[]; + pageCount: number; + }; + }; + }; + }; + }; + "customFieldsAPI.privateCreateCustomFieldAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + name: string; + /** @enum {string} */ + type: "shortText" | "email" | "phoneNumber" | "number" | "date" | "datetime" | "boolean" | "longText"; + folderId?: string | null; + description?: string | null; + }; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + id: string; + }; + }; + }; + }; + }; + "customFieldsAPI.privateUpdateCustomFieldAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + id: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + name: string; + description?: string; + folderId?: string | null; + }; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown | unknown; + }; + }; + }; + }; + "customFieldsAPI.privateDeleteCustomFieldsAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + customFieldId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown | unknown; + }; + }; + }; + }; + "flowsAPI.listFlowsWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + id: string; + name: string; + }[]; + }; + }; + }; + }; + }; + "flowsAPI.privateListFlowsAPI": { + parameters: { + query?: { + page?: number | null; + perPage?: number | null; + sort?: { + id: string; + desc: boolean; + }[] | null; + name?: string | null; + folderId?: string | null; + active?: boolean | null; + startType?: string | null; + integrationWhatsappId?: string | null; + }; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: ({ + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string; + active: boolean; + enableInInbox: boolean; + currentVersionId: string | null; + draftVersionId: string | null; + workspaceId: string; + folderId: string | null; + } & { + flowVersions: { + id: string; + /** Format: date-time */ + createdAt: string; + workspaceId: string; + flowId: string; + nodes: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[])[]; + edges: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[])[]; + isDraft: boolean; + isLatest: boolean; + startNodeId: string; + }[]; + })[]; + pageCount: number; + }; + }; + }; + }; + }; + "flowsAPI.privateGetFlowStatsAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + flowId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + [key: string]: { + node: { + "message:sent": number; + "message:delivered": number; + "message:seen": number; + "flow:clicked": { + clicked: number; + totalUsers: number; + }; + "message:failed": number; + }; + buttons: { + [key: string]: { + buttonId: string; + clicks: number; + }; + }; + }; + }; + }; + }; + }; + }; + "flowsAPI.privateResetFlowStatsAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + flowId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown | unknown; + }; + }; + }; + }; + "flowsAPI.privateListFlowVersionsAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + flowId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + id: string; + /** Format: date-time */ + createdAt: string; + workspaceId: string; + flowId: string; + nodes: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[])[]; + edges: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[])[]; + isDraft: boolean; + isLatest: boolean; + startNodeId: string; + }[]; + }; + }; + }; + }; + "flowsAPI.privateGetMessengerAdsJsonAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + flowId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + status: "ok"; + json: string; + } | { + /** @constant */ + status: "error"; + /** @enum {string} */ + reason: "notPublished" | "noStartNode" | "invalidStepType" | "invalidVariable"; + }; + }; + }; + }; + }; + "flowsAPI.privateGetFlowContactStatsAPI": { + parameters: { + query?: { + eventType?: "message:sent" | "message:delivered" | "message:seen" | "message:failed" | "message:received" | "flow:clicked" | "flow:ref"; + nodeId?: string; + }; + header?: never; + path: { + workspaceId: string; + flowId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + contactId: string; + contactInboxId: string; + firstName: string | null; + lastName: string | null; + sourceId: string | null; + avatar: string | null; + channel: string | null; + conversationId: string; + occurredAt: string; + }[]; + total: number; + page: number; + pageCount: number; + }; + }; + }; + }; + }; + "contactsAPIs.listContactsWorkspaceTokenAPI": { + parameters: { + query?: { + page?: number | null; + perPage?: number | null; + sort?: { + id: string; + desc: boolean; + }[] | null; + keyword?: string; + contactFilter?: { + /** @enum {string} */ + operator: "and" | "or"; + conditions: (({ + /** @constant */ + field: "locale"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "language"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "fullName"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "country"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "continent"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "gender"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "subscribedToBroadcast"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "contactCreatedAt"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "source"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "conversationTransferredToHuman"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "interactedInLast24h"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "followUp"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "archived"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "blocked"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "currentChannel"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "inbox"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "timezone"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastSeen"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastSent"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastInteraction"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "contactCreatedDateMinutesAgo"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastSeenMinutesAgo"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastInteractionMinutesAgo"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastUserInput"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastComment"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastUserInputType"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "email"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "phone"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "hasContactInfo"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "tags"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "broadcastSent"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "broadcastDelivered"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "broadcastSeen"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "broadcastClicked"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "broadcastFailed"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "subscribedToDripCampaign"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "entryPointsLinks"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "questionnaireStarted"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "questionnaireInProgress"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "questionnaireFinished"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "conversationAssigned"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "unreplied"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "unread"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "existingContact"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "consecutiveAiFailures"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "emailWasVerified"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "optedInForEmail"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "fromCtwaAd"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "ctwaConversion"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + }) | { + /** @constant */ + field: "customField"; + customFieldId: string; + /** @enum {string} */ + customFieldType?: "shortText" | "email" | "phoneNumber" | "number" | "date" | "datetime" | "boolean" | "longText"; + /** @enum {string} */ + valueType: "multiSelect" | "select" | "text" | "boolean" | "datetime" | "number"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | [ + string, + string + ]; + } | { + /** @constant */ + field: "couponTopic"; + topicId: string; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string; + } | { + /** @constant */ + field: "ctwaRetarget"; + /** @enum {string} */ + segment: "conversations" | "leads" | "purchases"; + adId?: string; + integrationWhatsappId?: string; + since: string; + until: string; + })[]; + timezone?: string; + }; + channels?: ("omnichannel" | "webchat" | "messenger" | "whatsapp" | "zalo" | "smtp" | "telegram" | "instagram" | "tiktok")[]; + integrationWhatsappId?: string; + integrationMessengerId?: string; + inboxIds?: string[]; + subaction?: "allContacts" | "messengerActiveContacts" | "messengerTemplateMessage" | "whatsappTemplateMessage" | "whatsappWithin24Hours" | "instagramActiveContacts" | "telegramAllContacts" | "tiktokActiveContacts"; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: ({ + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + avatar: string | null; + phoneNumber: string | null; + email: string | null; + emailVerified: boolean; + emailOptIn: boolean; + firstName: string | null; + lastName: string | null; + fullName: string | null; + gender: ("male" | "female" | "unknown") | null; + lastReadAt: string | null; + ref: string | null; + country: string | null; + state: string | null; + city: string | null; + location: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]) | null; + locale: string | null; + timezone: string | null; + subscribedAt: string | null; + broadcastSubscribedAt: string | null; + blockedAt: string | null; + workspaceId: string; + } & { + contactCustomFields?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + value: string; + contactId: string; + customFieldId: string; + }[]; + tags?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string; + deletedAt: string | null; + folderId: string | null; + workspaceId: string; + }[]; + contactNotes?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + text: string; + contactId: string; + createdById: string | null; + }[]; + contactInboxes?: { + id: string; + contactId: string; + inboxId: string; + channel: string; + source: string; + sourceId: string; + sourceUserId?: string | null; + sourceUsername?: string | null; + language: string | null; + lastIncomingMessageAt: string | null; + contactLastReadAt?: string | null; + inbox: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string; + channel: string; + sourceId: string; + workspaceId: string; + status: string; + }; + }[]; + conversation?: ({ + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + botEnabled: boolean; + botResumeAt: string | null; + archivedAt: string | null; + additionalAttributes: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]) | null; + contactLastReadAt: string | null; + agentLastReadAt: string | null; + aiContextLastMessageId?: unknown | null; + lastActivityAt: string | null; + followed: boolean; + assignedUserId?: unknown | null; + assignedInboxTeamId?: unknown | null; + workspaceId: string; + contactId: string; + sourceId: string | null; + lastStep: string | null; + currentStep: string | null; + adminRepliedAt: string | null; + contactRepliedAt: string | null; + } & { + assignedUser?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string | null; + email: string; + emailVerified: boolean; + image: string | null; + isAnonymous: boolean; + mustChangePassword: boolean; + tenantId?: unknown; + } | null; + assignedInboxTeam?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + workspaceId: string; + name: string; + } | null; + inbox?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string; + channel: string; + sourceId: string; + workspaceId: string; + status: string; + } | null; + }) | null; + })[]; + pageCount: number; + totalCount: number; + totalCountCapped: boolean; + }; + }; + }; + }; + }; + "contactsAPIs.createContactWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + phoneNumber?: "" | string; + email: "" | string; + contactId?: string; + firstName?: string; + lastName?: string; + /** @enum {string} */ + gender: "male" | "female" | "unknown"; + /** @enum {string} */ + channel: "omnichannel" | "webchat" | "messenger" | "whatsapp" | "zalo" | "smtp" | "telegram" | "instagram" | "tiktok"; + inboxId: string; + }; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + avatar: string | null; + phoneNumber: string | null; + email: string | null; + emailVerified: boolean; + emailOptIn: boolean; + firstName: string | null; + lastName: string | null; + fullName: string | null; + gender: ("male" | "female" | "unknown") | null; + lastReadAt: string | null; + ref: string | null; + country: string | null; + state: string | null; + city: string | null; + location: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]) | null; + locale: string | null; + timezone: string | null; + subscribedAt: string | null; + broadcastSubscribedAt: string | null; + blockedAt: string | null; + workspaceId: string; + } & { + contactCustomFields?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + value: string; + contactId: string; + customFieldId: string; + }[]; + tags?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string; + deletedAt: string | null; + folderId: string | null; + workspaceId: string; + }[]; + contactNotes?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + text: string; + contactId: string; + createdById: string | null; + }[]; + contactInboxes?: { + id: string; + contactId: string; + inboxId: string; + channel: string; + source: string; + sourceId: string; + sourceUserId?: string | null; + sourceUsername?: string | null; + language: string | null; + lastIncomingMessageAt: string | null; + contactLastReadAt?: string | null; + inbox: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string; + channel: string; + sourceId: string; + workspaceId: string; + status: string; + }; + }[]; + conversation?: ({ + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + botEnabled: boolean; + botResumeAt: string | null; + archivedAt: string | null; + additionalAttributes: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]) | null; + contactLastReadAt: string | null; + agentLastReadAt: string | null; + aiContextLastMessageId?: unknown | null; + lastActivityAt: string | null; + followed: boolean; + assignedUserId?: unknown | null; + assignedInboxTeamId?: unknown | null; + workspaceId: string; + contactId: string; + sourceId: string | null; + lastStep: string | null; + currentStep: string | null; + adminRepliedAt: string | null; + contactRepliedAt: string | null; + } & { + assignedUser?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string | null; + email: string; + emailVerified: boolean; + image: string | null; + isAnonymous: boolean; + mustChangePassword: boolean; + tenantId?: unknown; + } | null; + assignedInboxTeam?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + workspaceId: string; + name: string; + } | null; + inbox?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string; + channel: string; + sourceId: string; + workspaceId: string; + status: string; + } | null; + }) | null; + }; + }; + }; + }; + }; + "contactsAPIs.getContactWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path: { + identifier: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + avatar: string | null; + phoneNumber: string | null; + email: string | null; + emailVerified: boolean; + emailOptIn: boolean; + firstName: string | null; + lastName: string | null; + fullName: string | null; + gender: ("male" | "female" | "unknown") | null; + lastReadAt: string | null; + ref: string | null; + country: string | null; + state: string | null; + city: string | null; + location: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]) | null; + locale: string | null; + timezone: string | null; + subscribedAt: string | null; + broadcastSubscribedAt: string | null; + blockedAt: string | null; + workspaceId: string; + } & { + contactCustomFields?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + value: string; + contactId: string; + customFieldId: string; + }[]; + tags?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string; + deletedAt: string | null; + folderId: string | null; + workspaceId: string; + }[]; + contactNotes?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + text: string; + contactId: string; + createdById: string | null; + }[]; + contactInboxes?: { + id: string; + contactId: string; + inboxId: string; + channel: string; + source: string; + sourceId: string; + sourceUserId?: string | null; + sourceUsername?: string | null; + language: string | null; + lastIncomingMessageAt: string | null; + contactLastReadAt?: string | null; + inbox: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string; + channel: string; + sourceId: string; + workspaceId: string; + status: string; + }; + }[]; + conversation?: ({ + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + botEnabled: boolean; + botResumeAt: string | null; + archivedAt: string | null; + additionalAttributes: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]) | null; + contactLastReadAt: string | null; + agentLastReadAt: string | null; + aiContextLastMessageId?: unknown | null; + lastActivityAt: string | null; + followed: boolean; + assignedUserId?: unknown | null; + assignedInboxTeamId?: unknown | null; + workspaceId: string; + contactId: string; + sourceId: string | null; + lastStep: string | null; + currentStep: string | null; + adminRepliedAt: string | null; + contactRepliedAt: string | null; + } & { + assignedUser?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string | null; + email: string; + emailVerified: boolean; + image: string | null; + isAnonymous: boolean; + mustChangePassword: boolean; + tenantId?: unknown; + } | null; + assignedInboxTeam?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + workspaceId: string; + name: string; + } | null; + inbox?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string; + channel: string; + sourceId: string; + workspaceId: string; + status: string; + } | null; + }) | null; + }; + }; + }; + }; + }; + "contactsAPIs.updateContactWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path: { + identifier: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 204: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown | unknown; + }; + }; + }; + }; + "contactsAPIs.deleteContactWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path: { + identifier: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 204: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown | unknown; + }; + }; + }; + }; + "contactsAPIs.filterContactsWorkspaceTokenAPI": { + parameters: { + query: { + customFieldId: string; + value: string; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: ({ + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + avatar: string | null; + phoneNumber: string | null; + email: string | null; + emailVerified: boolean; + emailOptIn: boolean; + firstName: string | null; + lastName: string | null; + fullName: string | null; + gender: ("male" | "female" | "unknown") | null; + lastReadAt: string | null; + ref: string | null; + country: string | null; + state: string | null; + city: string | null; + location: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]) | null; + locale: string | null; + timezone: string | null; + subscribedAt: string | null; + broadcastSubscribedAt: string | null; + blockedAt: string | null; + workspaceId: string; + } & { + contactCustomFields?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + value: string; + contactId: string; + customFieldId: string; + }[]; + tags?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string; + deletedAt: string | null; + folderId: string | null; + workspaceId: string; + }[]; + contactNotes?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + text: string; + contactId: string; + createdById: string | null; + }[]; + contactInboxes?: { + id: string; + contactId: string; + inboxId: string; + channel: string; + source: string; + sourceId: string; + sourceUserId?: string | null; + sourceUsername?: string | null; + language: string | null; + lastIncomingMessageAt: string | null; + contactLastReadAt?: string | null; + inbox: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string; + channel: string; + sourceId: string; + workspaceId: string; + status: string; + }; + }[]; + conversation?: ({ + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + botEnabled: boolean; + botResumeAt: string | null; + archivedAt: string | null; + additionalAttributes: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]) | null; + contactLastReadAt: string | null; + agentLastReadAt: string | null; + aiContextLastMessageId?: unknown | null; + lastActivityAt: string | null; + followed: boolean; + assignedUserId?: unknown | null; + assignedInboxTeamId?: unknown | null; + workspaceId: string; + contactId: string; + sourceId: string | null; + lastStep: string | null; + currentStep: string | null; + adminRepliedAt: string | null; + contactRepliedAt: string | null; + } & { + assignedUser?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string | null; + email: string; + emailVerified: boolean; + image: string | null; + isAnonymous: boolean; + mustChangePassword: boolean; + tenantId?: unknown; + } | null; + assignedInboxTeam?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + workspaceId: string; + name: string; + } | null; + inbox?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string; + channel: string; + sourceId: string; + workspaceId: string; + status: string; + } | null; + }) | null; + })[]; + }; + }; + }; + }; + }; + "contactsAPIs.listContactTagsWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path: { + identifier: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + id: string; + name: string; + }[]; + }; + }; + }; + }; + }; + "contactsAPIs.addContactTagsWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path: { + identifier: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + tagIds: string[]; + }; + }; + }; + responses: { + /** @description OK */ + 204: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown | unknown; + }; + }; + }; + }; + "contactsAPIs.removeContactTagsWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path: { + identifier: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + tagIds: string[]; + }; + }; + }; + responses: { + /** @description OK */ + 204: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown | unknown; + }; + }; + }; + }; + "contactsAPIs.listContactCustomFieldsWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path: { + identifier: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: ({ + id: string; + name: string; + /** @enum {string} */ + type: "shortText" | "email" | "phoneNumber" | "number" | "date" | "datetime" | "boolean" | "longText"; + description: string | null; + } & { + value: string; + })[]; + }; + }; + }; + }; + }; + "contactsAPIs.setContactCustomFieldsWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path: { + identifier: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + fields: { + customFieldId: string; + value: string; + }[]; + }; + }; + }; + responses: { + /** @description OK */ + 204: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown | unknown; + }; + }; + }; + }; + "contactsAPIs.clearContactCustomFieldsWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path: { + identifier: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 204: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown | unknown; + }; + }; + }; + }; + "contactsAPIs.getContactCustomFieldWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path: { + identifier: string; + customFieldId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + id: string; + name: string; + /** @enum {string} */ + type: "shortText" | "email" | "phoneNumber" | "number" | "date" | "datetime" | "boolean" | "longText"; + description: string | null; + } & { + value: string; + }; + }; + }; + }; + }; + "contactsAPIs.setContactCustomFieldWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path: { + identifier: string; + customFieldId: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + value: string; + }; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown | unknown; + }; + }; + }; + }; + "contactsAPIs.clearContactCustomFieldWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path: { + identifier: string; + idOrName: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 204: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown | unknown; + }; + }; + }; + }; + "contactsAPIs.blockContactWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path: { + identifier: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 204: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown | unknown; + }; + }; + }; + }; + "contactsAPIs.unblockContactWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path: { + identifier: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 204: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown | unknown; + }; + }; + }; + }; + "contactsAPIs.listContactMessagesWorkspaceTokenAPI": { + parameters: { + query?: { + perPage?: number; + cursor?: string; + }; + header?: never; + path: { + identifier: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: (({ + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + conversationId: string; + contactInboxId: string; + workspaceId: string; + text: string | null; + contentAttributes: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]) | null; + /** @enum {string} */ + messageType: "incoming" | "outgoing" | "activity"; + /** @enum {string} */ + contentType: "text" | "location" | "refLink"; + /** @enum {string} */ + senderType: "bot" | "contact" | "system" | "user" | "api"; + senderId?: unknown | null; + sourceId: string | null; + deletedAt: string | null; + /** @enum {string} */ + type: "message" | "comment"; + parentId: string | null; + attributes: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]) | null; + sendError: string | null; + } & { + clientId?: string; + }) & { + attachments: ({ + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + workspaceId?: unknown; + conversationId?: unknown; + messageId?: unknown; + /** Format: date-time */ + messageCreatedAt: string; + /** @enum {string} */ + fileType: "image" | "video" | "audio" | "gif" | "file"; + sourceId: string | null; + mimeType: string; + width: number | null; + height: number | null; + size: number; + thumbnailPath: string | null; + originPath: string; + name: string | null; + } & { + url?: string | null; + })[]; + user?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string | null; + email: string; + emailVerified: boolean; + image: string | null; + isAnonymous: boolean; + mustChangePassword: boolean; + tenantId?: unknown; + }; + contact?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + avatar: string | null; + phoneNumber: string | null; + email: string | null; + emailVerified: boolean; + emailOptIn: boolean; + firstName: string | null; + lastName: string | null; + fullName: string | null; + gender: ("male" | "female" | "unknown") | null; + lastReadAt: string | null; + ref: string | null; + country: string | null; + state: string | null; + city: string | null; + location: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]) | null; + locale: string | null; + timezone: string | null; + subscribedAt: string | null; + broadcastSubscribedAt: string | null; + blockedAt: string | null; + workspaceId: string; + }; + clientId?: string; + })[]; + nextCursor: string | null; + prevCursor: string | null; + }; + }; + }; + }; + }; + "contactsAPIs.sendMessageWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path: { + identifier: string; + }; + cookie?: never; + }; + requestBody?: { + content: { + "application/json": { + text?: string; + files?: unknown[]; + flowId?: string; + nodeId?: string; + /** @description ID of the channel to send the message on. null to send message on the last interacted channel (if any). */ + inboxId?: string; + clientId?: string; + replyToMessageId?: string; + /** Format: date-time */ + replyToMessageCreatedAt?: string; + }; + }; + }; + responses: { + /** @description OK */ + 204: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown | unknown; + }; + }; + }; + }; + "contactsAPIs.getContactMessageWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path: { + identifier: string; + messageId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": ({ + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + conversationId: string; + contactInboxId: string; + workspaceId: string; + text: string | null; + contentAttributes: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]) | null; + /** @enum {string} */ + messageType: "incoming" | "outgoing" | "activity"; + /** @enum {string} */ + contentType: "text" | "location" | "refLink"; + /** @enum {string} */ + senderType: "bot" | "contact" | "system" | "user" | "api"; + senderId?: unknown | null; + sourceId: string | null; + deletedAt: string | null; + /** @enum {string} */ + type: "message" | "comment"; + parentId: string | null; + attributes: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]) | null; + sendError: string | null; + } & { + clientId?: string; + }) & { + attachmentCount?: number; + attachments?: ({ + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + workspaceId?: unknown; + conversationId?: unknown; + messageId?: unknown; + /** Format: date-time */ + messageCreatedAt: string; + /** @enum {string} */ + fileType: "image" | "video" | "audio" | "gif" | "file"; + sourceId: string | null; + mimeType: string; + width: number | null; + height: number | null; + size: number; + thumbnailPath: string | null; + originPath: string; + name: string | null; + } & { + url?: string | null; + })[]; + user?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string | null; + email: string; + emailVerified: boolean; + image: string | null; + isAnonymous: boolean; + mustChangePassword: boolean; + tenantId?: unknown; + }; + contact?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + avatar: string | null; + phoneNumber: string | null; + email: string | null; + emailVerified: boolean; + emailOptIn: boolean; + firstName: string | null; + lastName: string | null; + fullName: string | null; + gender: ("male" | "female" | "unknown") | null; + lastReadAt: string | null; + ref: string | null; + country: string | null; + state: string | null; + city: string | null; + location: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]) | null; + locale: string | null; + timezone: string | null; + subscribedAt: string | null; + broadcastSubscribedAt: string | null; + blockedAt: string | null; + workspaceId: string; + }; + clientId?: string; + }; + }; + }; + }; + }; + "contactsAPIs.triggerAutoReplyWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path: { + identifier: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + keyword: string; + inboxId?: string; + }; + }; + }; + responses: { + /** @description OK */ + 204: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown | unknown; + }; + }; + }; + }; + "contactsAPIs.sendContactFlowWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path: { + identifier: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + flowId: string; + inboxId?: string; + }; + }; + }; + responses: { + /** @description OK */ + 204: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown | unknown; + }; + }; + }; + }; + "contactsAPIs.importContactsWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + fileId: string; + /** @enum {string} */ + channel: "omnichannel" | "webchat" | "messenger" | "whatsapp" | "zalo" | "smtp" | "telegram" | "instagram" | "tiktok"; + inboxId: string; + timezone?: string; + countryCode?: string; + phoneNumber?: string; + contactId?: string; + sourceUserId?: string; + email?: string; + firstName?: string; + lastName?: string; + tagId?: string; + fieldMapping?: { + column: string; + customFieldId: string; + }[]; + }; + }; + }; + responses: { + /** @description OK */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown | unknown; + }; + }; + }; + }; + "contactsAPIs.upsertContactWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path: { + identifier: string; + }; + cookie?: never; + }; + requestBody?: { + content: { + "application/json": { + firstName?: string; + lastName?: string; + email?: "" | string; + phoneNumber?: string; + avatar?: string; + /** @enum {string} */ + gender?: "male" | "female" | "unknown"; + }; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + avatar: string | null; + phoneNumber: string | null; + email: string | null; + emailVerified: boolean; + emailOptIn: boolean; + firstName: string | null; + lastName: string | null; + fullName: string | null; + gender: ("male" | "female" | "unknown") | null; + lastReadAt: string | null; + ref: string | null; + country: string | null; + state: string | null; + city: string | null; + location: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]) | null; + locale: string | null; + timezone: string | null; + subscribedAt: string | null; + broadcastSubscribedAt: string | null; + blockedAt: string | null; + workspaceId: string; + } & { + contactCustomFields?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + value: string; + contactId: string; + customFieldId: string; + }[]; + tags?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string; + deletedAt: string | null; + folderId: string | null; + workspaceId: string; + }[]; + contactNotes?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + text: string; + contactId: string; + createdById: string | null; + }[]; + contactInboxes?: { + id: string; + contactId: string; + inboxId: string; + channel: string; + source: string; + sourceId: string; + sourceUserId?: string | null; + sourceUsername?: string | null; + language: string | null; + lastIncomingMessageAt: string | null; + contactLastReadAt?: string | null; + inbox: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string; + channel: string; + sourceId: string; + workspaceId: string; + status: string; + }; + }[]; + conversation?: ({ + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + botEnabled: boolean; + botResumeAt: string | null; + archivedAt: string | null; + additionalAttributes: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]) | null; + contactLastReadAt: string | null; + agentLastReadAt: string | null; + aiContextLastMessageId?: unknown | null; + lastActivityAt: string | null; + followed: boolean; + assignedUserId?: unknown | null; + assignedInboxTeamId?: unknown | null; + workspaceId: string; + contactId: string; + sourceId: string | null; + lastStep: string | null; + currentStep: string | null; + adminRepliedAt: string | null; + contactRepliedAt: string | null; + } & { + assignedUser?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string | null; + email: string; + emailVerified: boolean; + image: string | null; + isAnonymous: boolean; + mustChangePassword: boolean; + tenantId?: unknown; + } | null; + assignedInboxTeam?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + workspaceId: string; + name: string; + } | null; + inbox?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string; + channel: string; + sourceId: string; + workspaceId: string; + status: string; + } | null; + }) | null; + }; + }; + }; + }; + }; + "contactsAPIs.getContactAuthenticatedAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + contactId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + avatar: string | null; + phoneNumber: string | null; + email: string | null; + emailVerified: boolean; + emailOptIn: boolean; + firstName: string | null; + lastName: string | null; + fullName: string | null; + gender: ("male" | "female" | "unknown") | null; + lastReadAt: string | null; + ref: string | null; + country: string | null; + state: string | null; + city: string | null; + location: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]) | null; + locale: string | null; + timezone: string | null; + subscribedAt: string | null; + broadcastSubscribedAt: string | null; + blockedAt: string | null; + workspaceId: string; + } & { + tags: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string; + deletedAt: string | null; + folderId: string | null; + workspaceId: string; + }[]; + customFields: ({ + id: string; + name: string; + /** @enum {string} */ + type: "shortText" | "email" | "phoneNumber" | "number" | "date" | "datetime" | "boolean" | "longText"; + description: string | null; + } & { + value: string; + })[]; + contactNotes: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + text: string; + contactId: string; + createdById: string | null; + }[]; + contactsOnSequences: ({ + id?: unknown; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + /** Format: date-time */ + enrolledAt: string; + completedAt: string | null; + currentStep: number; + status: string | null; + nextRunAt: string | null; + lastStepId?: unknown | null; + nextStepId?: unknown | null; + lockedAt: string | null; + lockOwner: string | null; + lastError: string | null; + contactId: string; + sequenceId: string; + workspaceId?: unknown; + } & { + sequence: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string; + folderId: string | null; + active: boolean; + subscribers: number; + messages: number; + workspaceId: string; + }; + })[]; + }; + }; + }; + }; + }; + "contactsAPIs.listContactsAuthenticatedAPI": { + parameters: { + query?: { + page?: number | null; + perPage?: number | null; + sort?: { + id: string; + desc: boolean; + }[] | null; + keyword?: string; + contactFilter?: { + /** @enum {string} */ + operator: "and" | "or"; + conditions: (({ + /** @constant */ + field: "locale"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "language"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "fullName"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "country"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "continent"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "gender"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "subscribedToBroadcast"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "contactCreatedAt"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "source"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "conversationTransferredToHuman"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "interactedInLast24h"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "followUp"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "archived"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "blocked"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "currentChannel"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "inbox"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "timezone"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastSeen"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastSent"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastInteraction"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "contactCreatedDateMinutesAgo"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastSeenMinutesAgo"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastInteractionMinutesAgo"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastUserInput"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastComment"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastUserInputType"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "email"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "phone"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "hasContactInfo"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "tags"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "broadcastSent"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "broadcastDelivered"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "broadcastSeen"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "broadcastClicked"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "broadcastFailed"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "subscribedToDripCampaign"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "entryPointsLinks"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "questionnaireStarted"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "questionnaireInProgress"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "questionnaireFinished"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "conversationAssigned"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "unreplied"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "unread"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "existingContact"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "consecutiveAiFailures"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "emailWasVerified"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "optedInForEmail"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "fromCtwaAd"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "ctwaConversion"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + }) | { + /** @constant */ + field: "customField"; + customFieldId: string; + /** @enum {string} */ + customFieldType?: "shortText" | "email" | "phoneNumber" | "number" | "date" | "datetime" | "boolean" | "longText"; + /** @enum {string} */ + valueType: "multiSelect" | "select" | "text" | "boolean" | "datetime" | "number"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | [ + string, + string + ]; + } | { + /** @constant */ + field: "couponTopic"; + topicId: string; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string; + } | { + /** @constant */ + field: "ctwaRetarget"; + /** @enum {string} */ + segment: "conversations" | "leads" | "purchases"; + adId?: string; + integrationWhatsappId?: string; + since: string; + until: string; + })[]; + timezone?: string; + }; + channels?: ("omnichannel" | "webchat" | "messenger" | "whatsapp" | "zalo" | "smtp" | "telegram" | "instagram" | "tiktok")[]; + integrationWhatsappId?: string; + integrationMessengerId?: string; + inboxIds?: string[]; + subaction?: "allContacts" | "messengerActiveContacts" | "messengerTemplateMessage" | "whatsappTemplateMessage" | "whatsappWithin24Hours" | "instagramActiveContacts" | "telegramAllContacts" | "tiktokActiveContacts"; + }; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: ({ + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + avatar: string | null; + phoneNumber: string | null; + email: string | null; + emailVerified: boolean; + emailOptIn: boolean; + firstName: string | null; + lastName: string | null; + fullName: string | null; + gender: ("male" | "female" | "unknown") | null; + lastReadAt: string | null; + ref: string | null; + country: string | null; + state: string | null; + city: string | null; + location: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]) | null; + locale: string | null; + timezone: string | null; + subscribedAt: string | null; + broadcastSubscribedAt: string | null; + blockedAt: string | null; + workspaceId: string; + } & { + contactCustomFields?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + value: string; + contactId: string; + customFieldId: string; + }[]; + tags?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string; + deletedAt: string | null; + folderId: string | null; + workspaceId: string; + }[]; + contactNotes?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + text: string; + contactId: string; + createdById: string | null; + }[]; + contactInboxes?: { + id: string; + contactId: string; + inboxId: string; + channel: string; + source: string; + sourceId: string; + sourceUserId?: string | null; + sourceUsername?: string | null; + language: string | null; + lastIncomingMessageAt: string | null; + contactLastReadAt?: string | null; + inbox: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string; + channel: string; + sourceId: string; + workspaceId: string; + status: string; + }; + }[]; + conversation?: ({ + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + botEnabled: boolean; + botResumeAt: string | null; + archivedAt: string | null; + additionalAttributes: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]) | null; + contactLastReadAt: string | null; + agentLastReadAt: string | null; + aiContextLastMessageId?: unknown | null; + lastActivityAt: string | null; + followed: boolean; + assignedUserId?: unknown | null; + assignedInboxTeamId?: unknown | null; + workspaceId: string; + contactId: string; + sourceId: string | null; + lastStep: string | null; + currentStep: string | null; + adminRepliedAt: string | null; + contactRepliedAt: string | null; + } & { + assignedUser?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string | null; + email: string; + emailVerified: boolean; + image: string | null; + isAnonymous: boolean; + mustChangePassword: boolean; + tenantId?: unknown; + } | null; + assignedInboxTeam?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + workspaceId: string; + name: string; + } | null; + inbox?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string; + channel: string; + sourceId: string; + workspaceId: string; + status: string; + } | null; + }) | null; + })[]; + pageCount: number; + totalCount: number; + totalCountCapped: boolean; + }; + }; + }; + }; + }; + "contactsAPIs.createContactAuthenticatedAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + phoneNumber?: "" | string; + email: "" | string; + contactId?: string; + firstName?: string; + lastName?: string; + /** @enum {string} */ + gender: "male" | "female" | "unknown"; + /** @enum {string} */ + channel: "omnichannel" | "webchat" | "messenger" | "whatsapp" | "zalo" | "smtp" | "telegram" | "instagram" | "tiktok"; + inboxId: string; + }; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + id: string; + }; + }; + }; + }; + }; + "contactsAPIs.listContactsByPOSTAuthenticatedAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: { + content: { + "application/json": { + page?: number | null; + perPage?: number | null; + sort?: { + id: string; + desc: boolean; + }[] | null; + keyword?: string; + contactFilter?: { + /** @enum {string} */ + operator: "and" | "or"; + conditions: (({ + /** @constant */ + field: "locale"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "language"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "fullName"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "country"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "continent"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "gender"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "subscribedToBroadcast"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "contactCreatedAt"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "source"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "conversationTransferredToHuman"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "interactedInLast24h"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "followUp"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "archived"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "blocked"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "currentChannel"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "inbox"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "timezone"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastSeen"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastSent"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastInteraction"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "contactCreatedDateMinutesAgo"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastSeenMinutesAgo"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastInteractionMinutesAgo"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastUserInput"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastComment"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastUserInputType"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "email"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "phone"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "hasContactInfo"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "tags"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "broadcastSent"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "broadcastDelivered"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "broadcastSeen"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "broadcastClicked"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "broadcastFailed"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "subscribedToDripCampaign"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "entryPointsLinks"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "questionnaireStarted"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "questionnaireInProgress"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "questionnaireFinished"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "conversationAssigned"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "unreplied"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "unread"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "existingContact"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "consecutiveAiFailures"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "emailWasVerified"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "optedInForEmail"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "fromCtwaAd"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "ctwaConversion"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + }) | { + /** @constant */ + field: "customField"; + customFieldId: string; + /** @enum {string} */ + customFieldType?: "shortText" | "email" | "phoneNumber" | "number" | "date" | "datetime" | "boolean" | "longText"; + /** @enum {string} */ + valueType: "multiSelect" | "select" | "text" | "boolean" | "datetime" | "number"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | [ + string, + string + ]; + } | { + /** @constant */ + field: "couponTopic"; + topicId: string; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string; + } | { + /** @constant */ + field: "ctwaRetarget"; + /** @enum {string} */ + segment: "conversations" | "leads" | "purchases"; + adId?: string; + integrationWhatsappId?: string; + since: string; + until: string; + })[]; + timezone?: string; + }; + channels?: ("omnichannel" | "webchat" | "messenger" | "whatsapp" | "zalo" | "smtp" | "telegram" | "instagram" | "tiktok")[]; + integrationWhatsappId?: string; + integrationMessengerId?: string; + inboxIds?: string[]; + /** @enum {string} */ + subaction?: "allContacts" | "messengerActiveContacts" | "messengerTemplateMessage" | "whatsappTemplateMessage" | "whatsappWithin24Hours" | "instagramActiveContacts" | "telegramAllContacts" | "tiktokActiveContacts"; + }; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: ({ + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + avatar: string | null; + phoneNumber: string | null; + email: string | null; + emailVerified: boolean; + emailOptIn: boolean; + firstName: string | null; + lastName: string | null; + fullName: string | null; + gender: ("male" | "female" | "unknown") | null; + lastReadAt: string | null; + ref: string | null; + country: string | null; + state: string | null; + city: string | null; + location: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]) | null; + locale: string | null; + timezone: string | null; + subscribedAt: string | null; + broadcastSubscribedAt: string | null; + blockedAt: string | null; + workspaceId: string; + } & { + contactCustomFields?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + value: string; + contactId: string; + customFieldId: string; + }[]; + tags?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string; + deletedAt: string | null; + folderId: string | null; + workspaceId: string; + }[]; + contactNotes?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + text: string; + contactId: string; + createdById: string | null; + }[]; + contactInboxes?: { + id: string; + contactId: string; + inboxId: string; + channel: string; + source: string; + sourceId: string; + sourceUserId?: string | null; + sourceUsername?: string | null; + language: string | null; + lastIncomingMessageAt: string | null; + contactLastReadAt?: string | null; + inbox: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string; + channel: string; + sourceId: string; + workspaceId: string; + status: string; + }; + }[]; + conversation?: ({ + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + botEnabled: boolean; + botResumeAt: string | null; + archivedAt: string | null; + additionalAttributes: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]) | null; + contactLastReadAt: string | null; + agentLastReadAt: string | null; + aiContextLastMessageId?: unknown | null; + lastActivityAt: string | null; + followed: boolean; + assignedUserId?: unknown | null; + assignedInboxTeamId?: unknown | null; + workspaceId: string; + contactId: string; + sourceId: string | null; + lastStep: string | null; + currentStep: string | null; + adminRepliedAt: string | null; + contactRepliedAt: string | null; + } & { + assignedUser?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string | null; + email: string; + emailVerified: boolean; + image: string | null; + isAnonymous: boolean; + mustChangePassword: boolean; + tenantId?: unknown; + } | null; + assignedInboxTeam?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + workspaceId: string; + name: string; + } | null; + inbox?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string; + channel: string; + sourceId: string; + workspaceId: string; + status: string; + } | null; + }) | null; + })[]; + pageCount: number; + totalCount: number; + totalCountCapped: boolean; + }; + }; + }; + }; + }; + "contactsAPIs.countContactsAuthenticatedAPI": { + parameters: { + query?: { + page?: number | null; + perPage?: number | null; + sort?: { + id: string; + desc: boolean; + }[] | null; + keyword?: string; + contactFilter?: { + /** @enum {string} */ + operator: "and" | "or"; + conditions: (({ + /** @constant */ + field: "locale"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "language"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "fullName"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "country"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "continent"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "gender"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "subscribedToBroadcast"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "contactCreatedAt"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "source"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "conversationTransferredToHuman"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "interactedInLast24h"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "followUp"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "archived"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "blocked"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "currentChannel"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "inbox"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "timezone"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastSeen"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastSent"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastInteraction"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "contactCreatedDateMinutesAgo"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastSeenMinutesAgo"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastInteractionMinutesAgo"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastUserInput"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastComment"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastUserInputType"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "email"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "phone"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "hasContactInfo"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "tags"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "broadcastSent"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "broadcastDelivered"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "broadcastSeen"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "broadcastClicked"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "broadcastFailed"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "subscribedToDripCampaign"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "entryPointsLinks"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "questionnaireStarted"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "questionnaireInProgress"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "questionnaireFinished"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "conversationAssigned"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "unreplied"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "unread"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "existingContact"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "consecutiveAiFailures"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "emailWasVerified"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "optedInForEmail"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "fromCtwaAd"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "ctwaConversion"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + }) | { + /** @constant */ + field: "customField"; + customFieldId: string; + /** @enum {string} */ + customFieldType?: "shortText" | "email" | "phoneNumber" | "number" | "date" | "datetime" | "boolean" | "longText"; + /** @enum {string} */ + valueType: "multiSelect" | "select" | "text" | "boolean" | "datetime" | "number"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | [ + string, + string + ]; + } | { + /** @constant */ + field: "couponTopic"; + topicId: string; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string; + } | { + /** @constant */ + field: "ctwaRetarget"; + /** @enum {string} */ + segment: "conversations" | "leads" | "purchases"; + adId?: string; + integrationWhatsappId?: string; + since: string; + until: string; + })[]; + timezone?: string; + }; + channels?: ("omnichannel" | "webchat" | "messenger" | "whatsapp" | "zalo" | "smtp" | "telegram" | "instagram" | "tiktok")[]; + integrationWhatsappId?: string; + integrationMessengerId?: string; + inboxIds?: string[]; + subaction?: "allContacts" | "messengerActiveContacts" | "messengerTemplateMessage" | "whatsappTemplateMessage" | "whatsappWithin24Hours" | "instagramActiveContacts" | "telegramAllContacts" | "tiktokActiveContacts"; + }; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + total: number; + }; + }; + }; + }; + }; + "contactsAPIs.countContactInboxesAuthenticatedAPI": { + parameters: { + query?: { + page?: number | null; + perPage?: number | null; + sort?: { + id: string; + desc: boolean; + }[] | null; + keyword?: string; + contactFilter?: { + /** @enum {string} */ + operator: "and" | "or"; + conditions: (({ + /** @constant */ + field: "locale"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "language"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "fullName"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "country"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "continent"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "gender"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "subscribedToBroadcast"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "contactCreatedAt"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "source"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "conversationTransferredToHuman"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "interactedInLast24h"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "followUp"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "archived"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "blocked"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "currentChannel"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "inbox"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "timezone"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastSeen"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastSent"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastInteraction"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "contactCreatedDateMinutesAgo"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastSeenMinutesAgo"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastInteractionMinutesAgo"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastUserInput"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastComment"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastUserInputType"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "email"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "phone"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "hasContactInfo"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "tags"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "broadcastSent"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "broadcastDelivered"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "broadcastSeen"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "broadcastClicked"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "broadcastFailed"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "subscribedToDripCampaign"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "entryPointsLinks"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "questionnaireStarted"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "questionnaireInProgress"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "questionnaireFinished"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "conversationAssigned"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "unreplied"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "unread"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "existingContact"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "consecutiveAiFailures"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "emailWasVerified"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "optedInForEmail"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "fromCtwaAd"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "ctwaConversion"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + }) | { + /** @constant */ + field: "customField"; + customFieldId: string; + /** @enum {string} */ + customFieldType?: "shortText" | "email" | "phoneNumber" | "number" | "date" | "datetime" | "boolean" | "longText"; + /** @enum {string} */ + valueType: "multiSelect" | "select" | "text" | "boolean" | "datetime" | "number"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | [ + string, + string + ]; + } | { + /** @constant */ + field: "couponTopic"; + topicId: string; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string; + } | { + /** @constant */ + field: "ctwaRetarget"; + /** @enum {string} */ + segment: "conversations" | "leads" | "purchases"; + adId?: string; + integrationWhatsappId?: string; + since: string; + until: string; + })[]; + timezone?: string; + }; + channels?: ("omnichannel" | "webchat" | "messenger" | "whatsapp" | "zalo" | "smtp" | "telegram" | "instagram" | "tiktok")[]; + integrationWhatsappId?: string; + integrationMessengerId?: string; + inboxIds?: string[]; + subaction?: "allContacts" | "messengerActiveContacts" | "messengerTemplateMessage" | "whatsappTemplateMessage" | "whatsappWithin24Hours" | "instagramActiveContacts" | "telegramAllContacts" | "tiktokActiveContacts"; + }; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + total: number; + }; + }; + }; + }; + }; + "contactsAPIs.listContactInboxesAudiencePreviewAuthenticatedAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: { + content: { + "application/json": { + page?: number | null; + perPage?: number | null; + sort?: { + id: string; + desc: boolean; + }[] | null; + keyword?: string; + contactFilter?: { + /** @enum {string} */ + operator: "and" | "or"; + conditions: (({ + /** @constant */ + field: "locale"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "language"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "fullName"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "country"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "continent"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "gender"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "subscribedToBroadcast"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "contactCreatedAt"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "source"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "conversationTransferredToHuman"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "interactedInLast24h"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "followUp"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "archived"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "blocked"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "currentChannel"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "inbox"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "timezone"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastSeen"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastSent"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastInteraction"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "contactCreatedDateMinutesAgo"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastSeenMinutesAgo"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastInteractionMinutesAgo"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastUserInput"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastComment"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "lastUserInputType"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "email"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "phone"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "hasContactInfo"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "tags"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "broadcastSent"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "broadcastDelivered"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "broadcastSeen"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "broadcastClicked"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "broadcastFailed"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "subscribedToDripCampaign"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "entryPointsLinks"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "questionnaireStarted"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "questionnaireInProgress"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "questionnaireFinished"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "conversationAssigned"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "unreplied"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "unread"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "existingContact"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "consecutiveAiFailures"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "emailWasVerified"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "optedInForEmail"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "fromCtwaAd"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + } | { + /** @constant */ + field: "ctwaConversion"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | string[] | [ + string, + string + ]; + }) | { + /** @constant */ + field: "customField"; + customFieldId: string; + /** @enum {string} */ + customFieldType?: "shortText" | "email" | "phoneNumber" | "number" | "date" | "datetime" | "boolean" | "longText"; + /** @enum {string} */ + valueType: "multiSelect" | "select" | "text" | "boolean" | "datetime" | "number"; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string | [ + string, + string + ]; + } | { + /** @constant */ + field: "couponTopic"; + topicId: string; + /** @enum {string} */ + operator: "in" | "notIn" | "isEmpty" | "isNotEmpty" | "eq" | "ne" | "startsWith" | "endsWith" | "contains" | "notContains" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "notBetween" | "used"; + value?: string; + } | { + /** @constant */ + field: "ctwaRetarget"; + /** @enum {string} */ + segment: "conversations" | "leads" | "purchases"; + adId?: string; + integrationWhatsappId?: string; + since: string; + until: string; + })[]; + timezone?: string; + }; + channels?: ("omnichannel" | "webchat" | "messenger" | "whatsapp" | "zalo" | "smtp" | "telegram" | "instagram" | "tiktok")[]; + integrationWhatsappId?: string; + integrationMessengerId?: string; + inboxIds?: string[]; + /** @enum {string} */ + subaction?: "allContacts" | "messengerActiveContacts" | "messengerTemplateMessage" | "whatsappTemplateMessage" | "whatsappWithin24Hours" | "instagramActiveContacts" | "telegramAllContacts" | "tiktokActiveContacts"; + }; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + contactId: string; + contactInboxId: string; + firstName: string | null; + lastName: string | null; + fullName: string | null; + avatar: string | null; + occurredAt: string | null; + /** @enum {string} */ + channel: "omnichannel" | "webchat" | "messenger" | "whatsapp" | "zalo" | "smtp" | "telegram" | "instagram" | "tiktok"; + conversationId: string | null; + }[]; + }; + }; + }; + }; + }; + "contactsAPIs.getExportFileAuthenticatedAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + fileId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @enum {string} */ + status: "pending" | "uploaded" | "failed"; + fileName: string; + downloadUrl: string | null; + totalRecords: number | null; + }; + }; + }; + }; + }; + "contactsAPIs.listContactTagsAuthenticatedAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + contactId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string; + deletedAt: string | null; + folderId: string | null; + workspaceId: string; + }[]; + }; + }; + }; + }; + }; + "contactsAPIs.addContactTagAuthenticatedAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + ids: string[]; + tags: string[]; + }; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown | unknown; + }; + }; + }; + }; + "contactsAPIs.removeContactTagAuthenticatedAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + contactId: string; + tagId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown | unknown; + }; + }; + }; + }; + "contactsAPIs.listContactFieldsAuthenticatedAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + contactId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: ({ + id: string; + name: string; + /** @enum {string} */ + type: "shortText" | "email" | "phoneNumber" | "number" | "date" | "datetime" | "boolean" | "longText"; + description: string | null; + } & { + value: string; + })[]; + }; + }; + }; + }; + }; + "contactsAPIs.addContactFieldAuthenticatedAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + contactId: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + customFieldId: string; + value: string; + }; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown | unknown; + }; + }; + }; + }; + "contactsAPIs.deleteContactFieldAuthenticatedAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + contactId: string; + customFieldId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown | unknown; + }; + }; + }; + }; + "botFieldAPIs.listBotFieldsWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + id: string; + name: string; + /** @enum {string} */ + type: "shortText" | "email" | "phoneNumber" | "number" | "date" | "datetime" | "boolean" | "longText"; + value: string | null; + }[]; + }; + }; + }; + /** @description 400 */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "businessError"; + /** @constant */ + status: 400; + /** @default An error occurred while processing your request */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + /** @description 404 */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "notFound"; + /** @constant */ + status: 404; + /** @default Resource not found */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + /** @description 429 */ + 429: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "rateLimitExceeded"; + /** @constant */ + status: 429; + /** @default Rate limit exceeded */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + }; + }; + "botFieldAPIs.setBotFieldsWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + fields: { + key: string; + value: string; + }[]; + }; + }; + }; + responses: { + /** @description OK */ + 204: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown | unknown; + }; + }; + /** @description 400 */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "businessError"; + /** @constant */ + status: 400; + /** @default An error occurred while processing your request */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + /** @description 422 */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "invalidRequestData"; + /** @constant */ + status: 422; + /** @default Validation error */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + /** @description 429 */ + 429: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "rateLimitExceeded"; + /** @constant */ + status: 429; + /** @default Rate limit exceeded */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + }; + }; + "botFieldAPIs.createBotFieldWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + name: string; + /** @enum {string} */ + type: "shortText" | "email" | "phoneNumber" | "number" | "date" | "datetime" | "boolean" | "longText"; + value: string | null; + description: string | null; + folderId?: string | null; + }; + }; + }; + responses: { + /** @description OK */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + id: string; + name: string; + /** @enum {string} */ + type: "shortText" | "email" | "phoneNumber" | "number" | "date" | "datetime" | "boolean" | "longText"; + value: string | null; + }; + }; + }; + /** @description 400 */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "businessError"; + /** @constant */ + status: 400; + /** @default An error occurred while processing your request */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + /** @description 422 */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "invalidRequestData"; + /** @constant */ + status: 422; + /** @default Validation error */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + /** @description 429 */ + 429: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "rateLimitExceeded"; + /** @constant */ + status: 429; + /** @default Rate limit exceeded */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + }; + }; + "botFieldAPIs.getBotFieldWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path: { + idOrName: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + id: string; + name: string; + /** @enum {string} */ + type: "shortText" | "email" | "phoneNumber" | "number" | "date" | "datetime" | "boolean" | "longText"; + value: string | null; + }; + }; + }; + /** @description 400 */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "businessError"; + /** @constant */ + status: 400; + /** @default An error occurred while processing your request */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + /** @description 404 */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "notFound"; + /** @constant */ + status: 404; + /** @default Resource not found */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + /** @description 429 */ + 429: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "rateLimitExceeded"; + /** @constant */ + status: 429; + /** @default Rate limit exceeded */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + }; + }; + "botFieldAPIs.setBotFieldWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path: { + idOrName: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + value: string; + }; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + id: string; + name: string; + /** @enum {string} */ + type: "shortText" | "email" | "phoneNumber" | "number" | "date" | "datetime" | "boolean" | "longText"; + value: string | null; + }; + }; + }; + /** @description 400 */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "businessError"; + /** @constant */ + status: 400; + /** @default An error occurred while processing your request */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + /** @description 422 */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "invalidRequestData"; + /** @constant */ + status: 422; + /** @default Validation error */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + /** @description 429 */ + 429: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "rateLimitExceeded"; + /** @constant */ + status: 429; + /** @default Rate limit exceeded */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + }; + }; + "botFieldAPIs.deleteBotFieldsWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path: { + idOrName: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 204: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown | unknown; + }; + }; + /** @description 400 */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "businessError"; + /** @constant */ + status: 400; + /** @default An error occurred while processing your request */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + /** @description 404 */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "notFound"; + /** @constant */ + status: 404; + /** @default Resource not found */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + /** @description 429 */ + 429: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "rateLimitExceeded"; + /** @constant */ + status: 429; + /** @default Rate limit exceeded */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + }; + }; + "botFieldAPIs.bulkUpdateBotFieldsWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + fields: ({ + id: number; + value: string | number; + } | { + name: string; + value: string | number; + })[]; + }; + }; + }; + responses: { + /** @description OK */ + 204: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown | unknown; + }; + }; + /** @description 400 */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "businessError"; + /** @constant */ + status: 400; + /** @default An error occurred while processing your request */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + /** @description 422 */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "invalidRequestData"; + /** @constant */ + status: 422; + /** @default Validation error */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + /** @description 429 */ + 429: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "rateLimitExceeded"; + /** @constant */ + status: 429; + /** @default Rate limit exceeded */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + }; + }; + "integrationActiveCampaignAPI.listLists": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: ({ + id?: unknown; + name?: string; + } & { + [key: string]: unknown; + })[]; + }; + }; + }; + }; + }; + "integrationActiveCampaignAPI.listAutomations": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: ({ + id?: unknown; + name?: string; + } & { + [key: string]: unknown; + })[]; + }; + }; + }; + }; + }; + "integrationActiveCampaignAPI.listTags": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: ({ + id?: unknown; + tag?: string; + name?: string; + } & { + [key: string]: unknown; + })[]; + }; + }; + }; + }; + }; + "integrationActiveCampaignAPI.listCustomFields": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: (unknown | null)[]; + }; + }; + }; + }; + }; + "integrationFacebookAdsAPI.listAdAccounts": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + id: string; + name?: string; + }[]; + }; + }; + }; + }; + }; + "integrationFacebookAdsAPI.listCustomAudiences": { + parameters: { + query: { + adAccountId: string; + }; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + id: string; + name?: string; + subtype?: string; + }[]; + }; + }; + }; + }; + }; + "integrationFacebookAdsAPI.getAdInsights": { + parameters: { + query: { + adAccountId: string; + since: string; + until: string; + }; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + ad_id: string; + ad_name?: string; + spend?: unknown; + impressions?: unknown; + clicks?: unknown; + date_start?: string; + }[]; + }; + }; + }; + }; + }; + "integrationDripAPI.listAccounts": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + id?: unknown; + name?: string; + }[]; + }; + }; + }; + }; + }; + "integrationDripAPI.listTags": { + parameters: { + query: { + accountId: string; + }; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: string[]; + }; + }; + }; + }; + }; + "integrationDripAPI.listCustomFields": { + parameters: { + query: { + accountId: string; + }; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + identifier: string; + label: string; + }[]; + }; + }; + }; + }; + }; + "integrationGetResponseAPI.listCampaigns": { + parameters: { + query?: { + page?: number; + perPage?: number; + }; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: ({ + campaignId: string; + name: string; + } & { + [key: string]: unknown; + })[]; + meta: { + currentPage: number; + lastPage: number; + perPage: number; + total: number; + }; + }; + }; + }; + }; + }; + "integrationGetResponseAPI.listTags": { + parameters: { + query?: { + page?: number; + perPage?: number; + }; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: ({ + tagId: string; + name: string; + } & { + [key: string]: unknown; + })[]; + meta: { + currentPage: number; + lastPage: number; + perPage: number; + total: number; + }; + }; + }; + }; + }; + }; + "integrationInstagramAPIs.setCoexistInstagramAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + integrationId: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + enabled: boolean; + /** @default false */ + aiReadsSyncedHistory?: boolean; + }; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + success: true; + runId?: string; + } | { + /** @constant */ + success: false; + reason?: string; + msg?: string; + }; + }; + }; + }; + }; + "integrationKlaviyoAPI.listLists": { + parameters: { + query?: { + cursor?: string; + size?: number; + }; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + id: string; + name: string; + }[]; + nextCursor: string | null; + }; + }; + }; + }; + }; + "integrationMailchimpAPI.listAudiences": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + id: string; + name: string; + }[]; + }; + }; + }; + }; + }; + "integrationMailchimpAPI.listTags": { + parameters: { + query: { + listId: string; + }; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + id: number; + name: string; + }[]; + }; + }; + }; + }; + }; + "integrationMailchimpAPI.listMergeFields": { + parameters: { + query: { + listId: string; + }; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + tag: string; + name: string; + type: string; + required?: boolean; + }[]; + }; + }; + }; + }; + }; + "integrationMailerLiteAPI.listGroups": { + parameters: { + query?: { + page?: number; + limit?: number; + }; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + id: string; + name: string; + }[]; + meta: { + currentPage: number; + lastPage: number; + perPage: number; + total: number; + }; + }; + }; + }; + }; + }; + "integrationMailerLiteAPI.listFields": { + parameters: { + query?: { + page?: number; + limit?: number; + }; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + id: string; + name: string; + key: string; + /** @enum {string} */ + type: "text" | "number" | "date"; + }[]; + meta: { + currentPage: number; + lastPage: number; + perPage: number; + total: number; + }; + }; + }; + }; + }; + }; + "integrationMessengerAPIs.setCoexistMessengerAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + integrationId: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + enabled: boolean; + /** @default false */ + aiReadsSyncedHistory?: boolean; + }; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + success: true; + } | { + /** @constant */ + success: false; + reason?: string; + msg?: string; + }; + }; + }; + }; + }; + "integrationMoosendAPI.listMailingLists": { + parameters: { + query?: { + page?: number; + pageSize?: number; + }; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + id: string; + name: string; + }[]; + meta: { + pageSize: number; + currentPage: number; + totalResults: number; + totalPageCount: number; + }; + }; + }; + }; + }; + }; + "integrationSmtpAPI.listIntegrationSmtps": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + id: string; + name: string; + fromAddress: string; + }[]; + }; + }; + }; + }; + }; + "integrationSendGridAPI.listLists": { + parameters: { + query?: { + pageSize?: number; + pageToken?: string; + }; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + id: string; + name: string; + contactCount?: number; + }[]; + nextPageToken?: string; + count?: number; + }; + }; + }; + }; + }; + "integrationSendGridAPI.listCustomFields": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + id: string; + name: string; + /** @enum {string} */ + fieldType: "Text" | "Number" | "Date"; + }[]; + }; + }; + }; + }; + }; + "integrationWhatsappAPIs.listIntegrationWhatsappInternalAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + id: string; + name: string; + inboxId: string; + displayPhoneNumber: string; + tokenRefreshError: string | null; + phoneNumberId: string; + wabaId: string; + hasCapiScope: boolean; + capiScopeCheckedAt: string | null; + datasetId: string | null; + }[]; + }; + }; + }; + }; + "integrationWhatsappAPIs.setCoexistWhatsappAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + integrationId: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + enabled: boolean; + /** @default false */ + aiReadsSyncedHistory?: boolean; + }; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + success: true; + } | { + /** @constant */ + success: false; + reason?: "already_triggered" | "window_expired" | "not_eligible" | "trigger_failed" | string; + msg?: string; + }; + }; + }; + }; + }; + "whatsappMessageTemplateAPIs.listWhatsappMessageTemplatesInternalAPI": { + parameters: { + query?: { + inboxId?: string; + integrationWhatsappId?: string; + status?: "APPROVED" | "PENDING" | "REJECTED"; + }; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + id: string; + name: string; + language: string; + category: string; + status: string; + components?: unknown; + integrationWhatsappId?: unknown; + integrationWhatsapp: { + id: string; + name: string; + inboxId: string; + displayPhoneNumber: string; + tokenRefreshError: string | null; + phoneNumberId: string; + wabaId: string; + hasCapiScope: boolean; + capiScopeCheckedAt: string | null; + datasetId: string | null; + }; + }[]; + }; + }; + }; + }; + "whatsappMessageTemplateAPIs.listTemplateMessagesWorkspaceTokenAPI": { + parameters: { + query?: { + inboxId?: string; + integrationWhatsappId?: string; + status?: "APPROVED" | "PENDING" | "REJECTED"; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + id: string; + name: string; + language: string; + category: string; + status: string; + components?: unknown; + integrationWhatsappId?: unknown; + integrationWhatsapp: { + id: string; + name: string; + inboxId: string; + displayPhoneNumber: string; + tokenRefreshError: string | null; + phoneNumberId: string; + wabaId: string; + hasCapiScope: boolean; + capiScopeCheckedAt: string | null; + datasetId: string | null; + }; + }[]; + }; + }; + }; + }; + "whatsappFlowAPIs.listWhatsappFlowsInternalAPI": { + parameters: { + query?: { + inboxId?: string; + integrationWhatsappId?: string; + }; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + id: string; + name: string; + sourceId: string; + status: string; + categories?: unknown; + validationErrors?: unknown; + completedCount: string; + integrationWhatsappId: string; + screens?: unknown; + integrationWhatsapp: { + id: string; + name: string; + inboxId: string; + displayPhoneNumber: string; + tokenRefreshError: string | null; + phoneNumberId: string; + wabaId: string; + hasCapiScope: boolean; + capiScopeCheckedAt: string | null; + datasetId: string | null; + }; + }[]; + }; + }; + }; + }; + "whatsappFlowAPIs.getWhatsappFlowScreensInternalAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + flowId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + screens: { + id: string; + title: string; + terminal: boolean; + output: { + value: string; + label: string; + }[]; + }[]; + }; + }; + }; + }; + }; + "messengerMessageTemplateAPIs.listMessengerMessageTemplatesInternalAPI": { + parameters: { + query?: { + inboxId?: string; + integrationMessengerId?: string; + status?: "APPROVED" | "PENDING" | "REJECTED"; + page?: number; + perPage?: number; + name?: string; + }; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + id: string; + name: string; + language: string; + category: string; + status: string; + components?: unknown; + parameterFormat: string; + integrationMessengerId?: unknown; + integrationMessenger: { + id: string; + name: string; + inboxId: string; + }; + }[]; + }; + }; + }; + }; + "savedRepliesAPI.listSavedRepliesAuthorizedAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + shortcut: string; + text: string; + workspaceId: string; + }[]; + }; + }; + }; + }; + }; + "savedRepliesAPI.listSavedRepliesWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + shortcut: string; + text: string; + workspaceId: string; + }[]; + }; + }; + }; + }; + }; + "fbCommentsAPI.listFbCommentsAPI": { + parameters: { + query?: { + page?: number | null; + perPage?: number | null; + sort?: { + id: string; + desc: boolean; + }[] | null; + name?: string | null; + folderId?: string | null; + isActive?: boolean | null; + }; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string; + workspaceId: string; + folderId?: string | null; + /** @enum {string} */ + type: "messenger" | "instagram" | "instagramFacebook"; + isActive: boolean; + startTime: string | null; + endTime: string | null; + repliesCount: number; + post: { + /** @enum {string} */ + type: "published" | "ads" | "reels" | "postIds" | "all"; + value: string[]; + }; + privateReply: { + /** @enum {string} */ + type: "AIAgent" | "text" | "flow" | "none"; + value: string | null; + }; + publicReply: { + /** @enum {string} */ + type: "AIAgent" | "text" | "flow" | "none"; + value: string | null; + }; + includeKeywords: { + /** @enum {string} */ + type: "all" | "equal" | "contain"; + value: string[]; + }; + excludeKeywords: string[]; + options: { + replyToNewContactsOnly: boolean; + replyOncePerUserPerPost: boolean; + likeUserComment: boolean; + replyToUsersWhoCommentedOnOtherPosts: boolean; + ignoreCommentReplies: boolean; + trackUserTags: boolean; + }; + hideComments: { + all: boolean; + hasPhoneNumber: boolean; + hasImage: boolean; + hasVideo: boolean; + hasLink: boolean; + hasKeywords: boolean; + keywords: string[]; + /** @enum {string} */ + showCommentsAfter: "none" | "6h" | "12h" | "1d" | "2d" | "3d" | "4d" | "5d" | "6d" | "7d" | "8d" | "9d" | "10d"; + }; + replyAfter: { + /** @enum {string} */ + type: "immediately" | "seconds" | "minutes" | "hours" | "randomWithin3Minutes" | "randomWithin5Minutes" | "randomWithin10Minutes" | "randomWithin20Minutes" | "randomWithin30Minutes" | "randomWithin60Minutes"; + value: number; + }; + }[]; + pageCount: number; + }; + }; + }; + }; + }; + "fbCommentsAPI.createFbCommentAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + name: string; + /** + * @default messenger + * @constant + */ + type?: "messenger"; + folderId?: string | null; + post: { + /** @enum {string} */ + type: "published" | "ads" | "reels" | "postIds" | "all"; + value: string[]; + }; + privateReply: { + /** @enum {string} */ + type: "AIAgent" | "text" | "flow" | "none"; + value: string | null; + }; + publicReply: { + /** @enum {string} */ + type: "AIAgent" | "text" | "flow" | "none"; + value: string | null; + }; + includeKeywords: { + /** @enum {string} */ + type: "all" | "equal" | "contain"; + value: string[]; + }; + excludeKeywords: string[]; + options: { + replyToNewContactsOnly: boolean; + replyOncePerUserPerPost: boolean; + likeUserComment: boolean; + replyToUsersWhoCommentedOnOtherPosts: boolean; + ignoreCommentReplies: boolean; + trackUserTags: boolean; + }; + hideComments: { + all: boolean; + hasPhoneNumber: boolean; + hasImage: boolean; + hasVideo: boolean; + hasLink: boolean; + hasKeywords: boolean; + keywords: string[]; + /** @enum {string} */ + showCommentsAfter: "none" | "6h" | "12h" | "1d" | "2d" | "3d" | "4d" | "5d" | "6d" | "7d" | "8d" | "9d" | "10d"; + }; + replyAfter: { + /** @enum {string} */ + type: "immediately" | "seconds" | "minutes" | "hours" | "randomWithin3Minutes" | "randomWithin5Minutes" | "randomWithin10Minutes" | "randomWithin20Minutes" | "randomWithin30Minutes" | "randomWithin60Minutes"; + value: number; + }; + }; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string; + workspaceId: string; + folderId?: string | null; + /** @enum {string} */ + type: "messenger" | "instagram" | "instagramFacebook"; + isActive: boolean; + startTime: string | null; + endTime: string | null; + repliesCount: number; + post: { + /** @enum {string} */ + type: "published" | "ads" | "reels" | "postIds" | "all"; + value: string[]; + }; + privateReply: { + /** @enum {string} */ + type: "AIAgent" | "text" | "flow" | "none"; + value: string | null; + }; + publicReply: { + /** @enum {string} */ + type: "AIAgent" | "text" | "flow" | "none"; + value: string | null; + }; + includeKeywords: { + /** @enum {string} */ + type: "all" | "equal" | "contain"; + value: string[]; + }; + excludeKeywords: string[]; + options: { + replyToNewContactsOnly: boolean; + replyOncePerUserPerPost: boolean; + likeUserComment: boolean; + replyToUsersWhoCommentedOnOtherPosts: boolean; + ignoreCommentReplies: boolean; + trackUserTags: boolean; + }; + hideComments: { + all: boolean; + hasPhoneNumber: boolean; + hasImage: boolean; + hasVideo: boolean; + hasLink: boolean; + hasKeywords: boolean; + keywords: string[]; + /** @enum {string} */ + showCommentsAfter: "none" | "6h" | "12h" | "1d" | "2d" | "3d" | "4d" | "5d" | "6d" | "7d" | "8d" | "9d" | "10d"; + }; + replyAfter: { + /** @enum {string} */ + type: "immediately" | "seconds" | "minutes" | "hours" | "randomWithin3Minutes" | "randomWithin5Minutes" | "randomWithin10Minutes" | "randomWithin20Minutes" | "randomWithin30Minutes" | "randomWithin60Minutes"; + value: number; + }; + }; + }; + }; + }; + }; + "fbCommentsAPI.updateFbCommentAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + id: string; + }; + cookie?: never; + }; + requestBody?: { + content: { + "application/json": { + name?: string; + /** + * @default messenger + * @constant + */ + type?: "messenger"; + folderId?: string | null; + post?: { + /** @enum {string} */ + type: "published" | "ads" | "reels" | "postIds" | "all"; + value: string[]; + }; + privateReply?: { + /** @enum {string} */ + type: "AIAgent" | "text" | "flow" | "none"; + value: string | null; + }; + publicReply?: { + /** @enum {string} */ + type: "AIAgent" | "text" | "flow" | "none"; + value: string | null; + }; + includeKeywords?: { + /** @enum {string} */ + type: "all" | "equal" | "contain"; + value: string[]; + }; + excludeKeywords?: string[]; + options?: { + replyToNewContactsOnly: boolean; + replyOncePerUserPerPost: boolean; + likeUserComment: boolean; + replyToUsersWhoCommentedOnOtherPosts: boolean; + ignoreCommentReplies: boolean; + trackUserTags: boolean; + }; + hideComments?: { + all: boolean; + hasPhoneNumber: boolean; + hasImage: boolean; + hasVideo: boolean; + hasLink: boolean; + hasKeywords: boolean; + keywords: string[]; + /** @enum {string} */ + showCommentsAfter: "none" | "6h" | "12h" | "1d" | "2d" | "3d" | "4d" | "5d" | "6d" | "7d" | "8d" | "9d" | "10d"; + }; + replyAfter?: { + /** @enum {string} */ + type: "immediately" | "seconds" | "minutes" | "hours" | "randomWithin3Minutes" | "randomWithin5Minutes" | "randomWithin10Minutes" | "randomWithin20Minutes" | "randomWithin30Minutes" | "randomWithin60Minutes"; + value: number; + }; + isActive?: boolean; + startTime?: string | null; + endTime?: string | null; + }; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string; + workspaceId: string; + folderId?: string | null; + /** @enum {string} */ + type: "messenger" | "instagram" | "instagramFacebook"; + isActive: boolean; + startTime: string | null; + endTime: string | null; + repliesCount: number; + post: { + /** @enum {string} */ + type: "published" | "ads" | "reels" | "postIds" | "all"; + value: string[]; + }; + privateReply: { + /** @enum {string} */ + type: "AIAgent" | "text" | "flow" | "none"; + value: string | null; + }; + publicReply: { + /** @enum {string} */ + type: "AIAgent" | "text" | "flow" | "none"; + value: string | null; + }; + includeKeywords: { + /** @enum {string} */ + type: "all" | "equal" | "contain"; + value: string[]; + }; + excludeKeywords: string[]; + options: { + replyToNewContactsOnly: boolean; + replyOncePerUserPerPost: boolean; + likeUserComment: boolean; + replyToUsersWhoCommentedOnOtherPosts: boolean; + ignoreCommentReplies: boolean; + trackUserTags: boolean; + }; + hideComments: { + all: boolean; + hasPhoneNumber: boolean; + hasImage: boolean; + hasVideo: boolean; + hasLink: boolean; + hasKeywords: boolean; + keywords: string[]; + /** @enum {string} */ + showCommentsAfter: "none" | "6h" | "12h" | "1d" | "2d" | "3d" | "4d" | "5d" | "6d" | "7d" | "8d" | "9d" | "10d"; + }; + replyAfter: { + /** @enum {string} */ + type: "immediately" | "seconds" | "minutes" | "hours" | "randomWithin3Minutes" | "randomWithin5Minutes" | "randomWithin10Minutes" | "randomWithin20Minutes" | "randomWithin30Minutes" | "randomWithin60Minutes"; + value: number; + }; + }; + }; + }; + }; + }; + "fbCommentsAPI.deleteFbCommentAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown | unknown; + }; + }; + }; + }; + "fbCommentsAPI.facebookPostsAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + published: { + id: string; + message?: string; + full_picture?: string; + created_time: string; + permalink_url?: string; + pageId: string; + }[]; + ads: { + id: string; + message?: string; + full_picture?: string; + created_time: string; + permalink_url?: string; + pageId: string; + }[]; + reels: { + id: string; + message?: string; + full_picture?: string; + created_time: string; + permalink_url?: string; + pageId: string; + }[]; + pages: { + id: string; + name: string; + }[]; + }; + }; + }; + }; + }; + "igCommentsAPI.listIgCommentsAPI": { + parameters: { + query?: { + page?: number | null; + perPage?: number | null; + sort?: { + id: string; + desc: boolean; + }[] | null; + name?: string | null; + folderId?: string | null; + isActive?: boolean | null; + }; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string; + workspaceId: string; + folderId?: string | null; + /** @enum {string} */ + type: "messenger" | "instagram" | "instagramFacebook"; + isActive: boolean; + startTime: string | null; + endTime: string | null; + repliesCount: number; + post: { + /** @enum {string} */ + type: "published" | "ads" | "reels" | "postIds" | "all"; + value: string[]; + }; + privateReply: { + /** @enum {string} */ + type: "AIAgent" | "text" | "flow" | "none"; + value: string | null; + }; + publicReply: { + /** @enum {string} */ + type: "AIAgent" | "text" | "flow" | "none"; + value: string | null; + }; + includeKeywords: { + /** @enum {string} */ + type: "all" | "equal" | "contain"; + value: string[]; + }; + excludeKeywords: string[]; + options: { + replyToNewContactsOnly: boolean; + replyOncePerUserPerPost: boolean; + likeUserComment: boolean; + replyToUsersWhoCommentedOnOtherPosts: boolean; + ignoreCommentReplies: boolean; + trackUserTags: boolean; + }; + hideComments: { + all: boolean; + hasPhoneNumber: boolean; + hasImage: boolean; + hasVideo: boolean; + hasLink: boolean; + hasKeywords: boolean; + keywords: string[]; + /** @enum {string} */ + showCommentsAfter: "none" | "6h" | "12h" | "1d" | "2d" | "3d" | "4d" | "5d" | "6d" | "7d" | "8d" | "9d" | "10d"; + }; + replyAfter: { + /** @enum {string} */ + type: "immediately" | "seconds" | "minutes" | "hours" | "randomWithin3Minutes" | "randomWithin5Minutes" | "randomWithin10Minutes" | "randomWithin20Minutes" | "randomWithin30Minutes" | "randomWithin60Minutes"; + value: number; + }; + }[]; + pageCount: number; + }; + }; + }; + }; + }; + "igCommentsAPI.createIgCommentAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + name: string; + /** @enum {string} */ + type: "instagram" | "instagramFacebook"; + folderId?: string | null; + post: { + /** @enum {string} */ + type: "published" | "ads" | "reels" | "postIds" | "all"; + value: string[]; + }; + privateReply: { + /** @enum {string} */ + type: "AIAgent" | "text" | "flow" | "none"; + value: string | null; + }; + publicReply: { + /** @enum {string} */ + type: "AIAgent" | "text" | "flow" | "none"; + value: string | null; + }; + includeKeywords: { + /** @enum {string} */ + type: "all" | "equal" | "contain"; + value: string[]; + }; + excludeKeywords: string[]; + options: { + replyToNewContactsOnly: boolean; + replyOncePerUserPerPost: boolean; + likeUserComment: boolean; + replyToUsersWhoCommentedOnOtherPosts: boolean; + ignoreCommentReplies: boolean; + trackUserTags: boolean; + }; + hideComments: { + all: boolean; + hasPhoneNumber: boolean; + hasImage: boolean; + hasVideo: boolean; + hasLink: boolean; + hasKeywords: boolean; + keywords: string[]; + /** @enum {string} */ + showCommentsAfter: "none" | "6h" | "12h" | "1d" | "2d" | "3d" | "4d" | "5d" | "6d" | "7d" | "8d" | "9d" | "10d"; + }; + replyAfter: { + /** @enum {string} */ + type: "immediately" | "seconds" | "minutes" | "hours" | "randomWithin3Minutes" | "randomWithin5Minutes" | "randomWithin10Minutes" | "randomWithin20Minutes" | "randomWithin30Minutes" | "randomWithin60Minutes"; + value: number; + }; + }; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string; + workspaceId: string; + folderId?: string | null; + /** @enum {string} */ + type: "messenger" | "instagram" | "instagramFacebook"; + isActive: boolean; + startTime: string | null; + endTime: string | null; + repliesCount: number; + post: { + /** @enum {string} */ + type: "published" | "ads" | "reels" | "postIds" | "all"; + value: string[]; + }; + privateReply: { + /** @enum {string} */ + type: "AIAgent" | "text" | "flow" | "none"; + value: string | null; + }; + publicReply: { + /** @enum {string} */ + type: "AIAgent" | "text" | "flow" | "none"; + value: string | null; + }; + includeKeywords: { + /** @enum {string} */ + type: "all" | "equal" | "contain"; + value: string[]; + }; + excludeKeywords: string[]; + options: { + replyToNewContactsOnly: boolean; + replyOncePerUserPerPost: boolean; + likeUserComment: boolean; + replyToUsersWhoCommentedOnOtherPosts: boolean; + ignoreCommentReplies: boolean; + trackUserTags: boolean; + }; + hideComments: { + all: boolean; + hasPhoneNumber: boolean; + hasImage: boolean; + hasVideo: boolean; + hasLink: boolean; + hasKeywords: boolean; + keywords: string[]; + /** @enum {string} */ + showCommentsAfter: "none" | "6h" | "12h" | "1d" | "2d" | "3d" | "4d" | "5d" | "6d" | "7d" | "8d" | "9d" | "10d"; + }; + replyAfter: { + /** @enum {string} */ + type: "immediately" | "seconds" | "minutes" | "hours" | "randomWithin3Minutes" | "randomWithin5Minutes" | "randomWithin10Minutes" | "randomWithin20Minutes" | "randomWithin30Minutes" | "randomWithin60Minutes"; + value: number; + }; + }; + }; + }; + }; + }; + "igCommentsAPI.updateIgCommentAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + id: string; + }; + cookie?: never; + }; + requestBody?: { + content: { + "application/json": { + name?: string; + /** @enum {string} */ + type?: "instagram" | "instagramFacebook"; + folderId?: string | null; + post?: { + /** @enum {string} */ + type: "published" | "ads" | "reels" | "postIds" | "all"; + value: string[]; + }; + privateReply?: { + /** @enum {string} */ + type: "AIAgent" | "text" | "flow" | "none"; + value: string | null; + }; + publicReply?: { + /** @enum {string} */ + type: "AIAgent" | "text" | "flow" | "none"; + value: string | null; + }; + includeKeywords?: { + /** @enum {string} */ + type: "all" | "equal" | "contain"; + value: string[]; + }; + excludeKeywords?: string[]; + options?: { + replyToNewContactsOnly: boolean; + replyOncePerUserPerPost: boolean; + likeUserComment: boolean; + replyToUsersWhoCommentedOnOtherPosts: boolean; + ignoreCommentReplies: boolean; + trackUserTags: boolean; + }; + hideComments?: { + all: boolean; + hasPhoneNumber: boolean; + hasImage: boolean; + hasVideo: boolean; + hasLink: boolean; + hasKeywords: boolean; + keywords: string[]; + /** @enum {string} */ + showCommentsAfter: "none" | "6h" | "12h" | "1d" | "2d" | "3d" | "4d" | "5d" | "6d" | "7d" | "8d" | "9d" | "10d"; + }; + replyAfter?: { + /** @enum {string} */ + type: "immediately" | "seconds" | "minutes" | "hours" | "randomWithin3Minutes" | "randomWithin5Minutes" | "randomWithin10Minutes" | "randomWithin20Minutes" | "randomWithin30Minutes" | "randomWithin60Minutes"; + value: number; + }; + isActive?: boolean; + startTime?: string | null; + endTime?: string | null; + }; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string; + workspaceId: string; + folderId?: string | null; + /** @enum {string} */ + type: "messenger" | "instagram" | "instagramFacebook"; + isActive: boolean; + startTime: string | null; + endTime: string | null; + repliesCount: number; + post: { + /** @enum {string} */ + type: "published" | "ads" | "reels" | "postIds" | "all"; + value: string[]; + }; + privateReply: { + /** @enum {string} */ + type: "AIAgent" | "text" | "flow" | "none"; + value: string | null; + }; + publicReply: { + /** @enum {string} */ + type: "AIAgent" | "text" | "flow" | "none"; + value: string | null; + }; + includeKeywords: { + /** @enum {string} */ + type: "all" | "equal" | "contain"; + value: string[]; + }; + excludeKeywords: string[]; + options: { + replyToNewContactsOnly: boolean; + replyOncePerUserPerPost: boolean; + likeUserComment: boolean; + replyToUsersWhoCommentedOnOtherPosts: boolean; + ignoreCommentReplies: boolean; + trackUserTags: boolean; + }; + hideComments: { + all: boolean; + hasPhoneNumber: boolean; + hasImage: boolean; + hasVideo: boolean; + hasLink: boolean; + hasKeywords: boolean; + keywords: string[]; + /** @enum {string} */ + showCommentsAfter: "none" | "6h" | "12h" | "1d" | "2d" | "3d" | "4d" | "5d" | "6d" | "7d" | "8d" | "9d" | "10d"; + }; + replyAfter: { + /** @enum {string} */ + type: "immediately" | "seconds" | "minutes" | "hours" | "randomWithin3Minutes" | "randomWithin5Minutes" | "randomWithin10Minutes" | "randomWithin20Minutes" | "randomWithin30Minutes" | "randomWithin60Minutes"; + value: number; + }; + }; + }; + }; + }; + }; + "igCommentsAPI.deleteIgCommentAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown | unknown; + }; + }; + }; + }; + "igCommentsAPI.instagramMediaAPI": { + parameters: { + query: { + variant: "instagram" | "instagramFacebook"; + }; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + posts: { + id: string; + message?: string; + full_picture?: string; + created_time: string; + permalink_url?: string; + media_product_type?: string; + accountId: string; + }[]; + pages: { + id: string; + name: string; + }[]; + }; + }; + }; + }; + }; + "igStoriesAPI.listIgStoriesAPI": { + parameters: { + query?: { + page?: number | null; + perPage?: number | null; + sort?: { + id: string; + desc: boolean; + }[] | null; + name?: string | null; + folderId?: string | null; + isActive?: boolean | null; + }; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string; + workspaceId: string; + folderId?: string | null; + /** @enum {string} */ + type: "instagram" | "instagramFacebook"; + isActive: boolean; + startTime: string | null; + endTime: string | null; + repliesCount: number; + story: { + /** @enum {string} */ + type: "all" | "storyIds"; + value: string[]; + }; + reply: { + /** @enum {string} */ + type: "AIAgent" | "text" | "flow" | "none"; + value: string | null; + }; + includeKeywords: { + /** @enum {string} */ + type: "all" | "equal" | "contain"; + value: string[]; + }; + }[]; + pageCount: number; + }; + }; + }; + }; + }; + "igStoriesAPI.createIgStoryAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + name: string; + /** @enum {string} */ + type: "instagram" | "instagramFacebook"; + folderId?: string | null; + story: { + /** @enum {string} */ + type: "all" | "storyIds"; + value: string[]; + }; + reply: { + /** @enum {string} */ + type: "AIAgent" | "text" | "flow" | "none"; + value: string | null; + }; + includeKeywords: { + /** @enum {string} */ + type: "all" | "equal" | "contain"; + value: string[]; + }; + }; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string; + workspaceId: string; + folderId?: string | null; + /** @enum {string} */ + type: "instagram" | "instagramFacebook"; + isActive: boolean; + startTime: string | null; + endTime: string | null; + repliesCount: number; + story: { + /** @enum {string} */ + type: "all" | "storyIds"; + value: string[]; + }; + reply: { + /** @enum {string} */ + type: "AIAgent" | "text" | "flow" | "none"; + value: string | null; + }; + includeKeywords: { + /** @enum {string} */ + type: "all" | "equal" | "contain"; + value: string[]; + }; + }; + }; + }; + }; + }; + "igStoriesAPI.updateIgStoryAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + id: string; + }; + cookie?: never; + }; + requestBody?: { + content: { + "application/json": { + name?: string; + /** @enum {string} */ + type?: "instagram" | "instagramFacebook"; + folderId?: string | null; + story?: { + /** @enum {string} */ + type: "all" | "storyIds"; + value: string[]; + }; + reply?: { + /** @enum {string} */ + type: "AIAgent" | "text" | "flow" | "none"; + value: string | null; + }; + includeKeywords?: { + /** @enum {string} */ + type: "all" | "equal" | "contain"; + value: string[]; + }; + isActive?: boolean; + startTime?: string | null; + endTime?: string | null; + }; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string; + workspaceId: string; + folderId?: string | null; + /** @enum {string} */ + type: "instagram" | "instagramFacebook"; + isActive: boolean; + startTime: string | null; + endTime: string | null; + repliesCount: number; + story: { + /** @enum {string} */ + type: "all" | "storyIds"; + value: string[]; + }; + reply: { + /** @enum {string} */ + type: "AIAgent" | "text" | "flow" | "none"; + value: string | null; + }; + includeKeywords: { + /** @enum {string} */ + type: "all" | "equal" | "contain"; + value: string[]; + }; + }; + }; + }; + }; + }; + "igStoriesAPI.deleteIgStoryAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown | unknown; + }; + }; + }; + }; + "igStoriesAPI.instagramStoriesAPI": { + parameters: { + query: { + variant: "instagram" | "instagramFacebook"; + }; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + stories: { + id: string; + message?: string; + full_picture?: string; + created_time: string; + permalink_url?: string; + accountId: string; + }[]; + pages: { + id: string; + name: string; + }[]; + }; + }; + }; + }; + }; + "facebookLeadAdsAPI.listLeadAdsPagesAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + pages: { + pageId: string; + pageName: string; + eligible: boolean; + }[]; + }; + }; + }; + }; + }; + "facebookLeadAdsAPI.listLeadAdsFormsAPI": { + parameters: { + query: { + pageId: string; + }; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + forms: { + id: string; + name: string; + status: string; + questions?: { + key: string; + label: string; + type: string; + id: string; + }[]; + }[]; + }; + }; + }; + }; + }; + "sequencesAPI.listSequencesWorkspaceAuthAPI": { + parameters: { + query?: { + page?: number | null; + perPage?: number | null; + sort?: { + id: string; + desc: boolean; + }[] | null; + name?: string | null; + folderId?: string | null; + active?: boolean | null; + }; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: ({ + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string; + folderId: string | null; + active: boolean; + subscribers: number; + messages: number; + workspaceId: string; + } & { + stepsCount: number; + subscribersCount: number; + })[]; + pageCount: number; + }; + }; + }; + }; + }; + "sequencesAPI.privateGetSequenceStepStatsAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + sequenceId: string; + stepId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + "message:sent": number; + "message:delivered": number; + "message:seen": number; + "flow:clicked": number; + "message:failed": number; + }; + }; + }; + }; + }; + "sequencesAPI.privateListSequenceStepContactsAPI": { + parameters: { + query: { + eventType: "message:sent" | "message:delivered" | "message:seen" | "message:failed" | "message:received" | "flow:clicked" | "flow:ref"; + total?: number; + page?: number; + perPage?: number; + }; + header?: never; + path: { + workspaceId: string; + sequenceId: string; + stepId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + contactId: string; + contactInboxId: string; + firstName: string | null; + lastName: string | null; + fullName: string | null; + sourceId: string | null; + avatar: string | null; + /** @enum {string} */ + channel: "omnichannel" | "webchat" | "messenger" | "whatsapp" | "zalo" | "smtp" | "telegram" | "instagram" | "tiktok"; + errorContent: string | null; + occurredAt: string; + conversationId: string; + }[]; + total: number; + page: number; + pageCount: number; + }; + }; + }; + }; + }; + "sequencesAPI.listSequencesWorkspaceTokenAPI": { + parameters: { + query?: { + page?: number | null; + perPage?: number | null; + sort?: { + id: string; + desc: boolean; + }[] | null; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: ({ + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string; + folderId: string | null; + active: boolean; + subscribers: number; + messages: number; + workspaceId: string; + } & { + stepsCount: number; + subscribersCount: number; + })[]; + pageCount: number; + }; + }; + }; + }; + }; + "sequencesAPI.getSequenceWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string; + folderId: string | null; + active: boolean; + subscribers: number; + messages: number; + workspaceId: string; + }; + }; + }; + }; + }; + "aiFilesAPI.listAIFilesAuthenticatedAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string; + path: string; + size: number; + mimeType: string; + workspaceId: string; + url: string; + chunksCount: number; + /** @enum {string} */ + processingStatus: "pending" | "success" | "error" | "processing"; + }[]; + }; + }; + }; + }; + }; + "inboxesAPI.listInboxesAuthenticatedAPI": { + parameters: { + query?: { + includes?: "integration"[]; + page?: number; + perPage?: number; + }; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string; + channel: string; + sourceId: string; + workspaceId: string; + status: string; + integrationWhatsapp?: { + id: string; + name: string; + inboxId: string; + displayPhoneNumber: string; + tokenRefreshError: string | null; + phoneNumberId: string; + wabaId: string; + hasCapiScope: boolean; + capiScopeCheckedAt: string | null; + datasetId: string | null; + } | null; + integrationWebchat?: { + id: string; + name: string; + } | null; + integrationMessenger?: { + id: string; + name: string; + inboxId: string; + } | null; + integrationZalo?: { + id: string; + name: string; + } | null; + integrationTelegram?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + auth: (string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]; + botId: string; + name: string; + workspaceId: string; + inboxId: string; + } | null; + integrationInstagram?: { + id: string; + name: string; + inboxId: string; + username: string; + } | null; + integrationSmtp?: { + id: string; + name: string; + fromAddress: string; + } | null; + }[]; + pageCount: number; + }; + }; + }; + }; + }; + "inboxesAPI.listInboxesWorkspaceTokenAPI": { + parameters: { + query?: { + includes?: "integration"[]; + page?: number; + perPage?: number; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + id: string; + name: string; + channel: string; + status: string; + }[]; + pageCount: number; + }; + }; + }; + }; + }; + "inboxesAPI.listChannelsWorkspaceTokenAPI": { + parameters: { + query?: { + includes?: "integration"[]; + page?: number; + perPage?: number; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + name: string; + channel: string; + status: string; + id: string; + }[]; + pageCount: number; + }; + }; + }; + }; + }; + "spreadsheetsAPI.listSpreadsheetsAuthenticatedAPI": { + parameters: { + query?: { + page?: number; + perPage?: number; + name?: string; + }; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + workspaceId: string; + name: string; + url: string; + spreadsheetId: string; + }[]; + pageCount: number; + }; + }; + }; + }; + }; + "spreadsheetsAPI.listWorksheetsAuthenticatedAPI": { + parameters: { + query: { + page?: number; + perPage?: number; + spreadsheetId: string; + }; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: string[]; + }; + }; + }; + }; + }; + "spreadsheetsAPI.listWorksheetHeadersAuthenticatedAPI": { + parameters: { + query: { + sheetName: string; + }; + header?: never; + path: { + workspaceId: string; + spreadsheetId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: string[]; + }; + }; + }; + }; + }; + "workspaceMembersAPI.listWorkspaceMembersAuthenticatedAPI": { + parameters: { + query?: { + page?: number | null; + perPage?: number | null; + sort?: { + id: string; + desc: boolean; + }[] | null; + keyword?: string | null; + }; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + workspaceId: string; + userId: string; + /** @enum {string} */ + role: "owner" | "agent"; + notificationChannels: (string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]; + notificationTypes: (string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]; + permissions: { + superAdmin: boolean; + analytics: boolean; + flows: boolean; + contacts: boolean; + onlyAssignedContacts: boolean; + emailAndPhone: boolean; + broadcast: boolean; + ecommerce: boolean; + }; + user: { + id: string; + name: string | null; + image: string | null; + }; + }[]; + pageCount: number; + }; + }; + }; + }; + }; + "workspaceMembersAPI.listMembersWorkspaceTokenAPI": { + parameters: { + query?: { + page?: number | null; + perPage?: number | null; + sort?: { + id: string; + desc: boolean; + }[] | null; + keyword?: string | null; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + workspaceId: string; + userId: string; + /** @enum {string} */ + role: "owner" | "agent"; + notificationChannels: (string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]; + notificationTypes: (string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]; + permissions: { + superAdmin: boolean; + analytics: boolean; + flows: boolean; + contacts: boolean; + onlyAssignedContacts: boolean; + emailAndPhone: boolean; + broadcast: boolean; + ecommerce: boolean; + }; + user: { + id: string; + name: string | null; + image: string | null; + }; + }[]; + pageCount: number; + }; + }; + }; + }; + }; + "workspaceMembersAPI.getMemberWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path: { + memberId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + workspaceId: string; + userId: string; + /** @enum {string} */ + role: "owner" | "agent"; + notificationChannels: (string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]; + notificationTypes: (string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]; + permissions: { + superAdmin: boolean; + analytics: boolean; + flows: boolean; + contacts: boolean; + onlyAssignedContacts: boolean; + emailAndPhone: boolean; + broadcast: boolean; + ecommerce: boolean; + }; + user: { + id: string; + name: string | null; + image: string | null; + }; + }; + }; + }; + }; + }; + "inboxTeamsAPI.listInboxTeamsAuthenticatedAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + workspaceId: string; + name: string; + inboxTeamMembers: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + inboxTeamId: string; + userId?: unknown; + user: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string | null; + email: string; + emailVerified: boolean; + image: string | null; + isAnonymous: boolean; + mustChangePassword: boolean; + tenantId?: unknown; + }; + }[]; + }[]; + }; + }; + }; + }; + }; + "inboxTeamsAPI.listTeamsWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + workspaceId: string; + name: string; + inboxTeamMembers: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + inboxTeamId: string; + userId?: unknown; + user: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string | null; + email: string; + emailVerified: boolean; + image: string | null; + isAnonymous: boolean; + mustChangePassword: boolean; + tenantId?: unknown; + }; + }[]; + }[]; + }; + }; + }; + }; + }; + "foldersAPI.listFoldersAuthenticatedAPI": { + parameters: { + query: { + folderType: string; + folderId?: string | null; + isTrash?: boolean | null; + }; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string; + /** @enum {string} */ + folderType: "tag" | "flow" | "customField" | "automatedResponse" | "trigger" | "webhook" | "sequence" | "emailTopic" | "fbComment" | "igComment" | "igStory" | "outboundAutomatedResponse"; + parentId?: string | null; + workspaceId: string; + isTrash: boolean; + paths: (unknown | null)[]; + }[]; + }; + }; + }; + }; + }; + "messagesAPI.listMessagesAuthenticatedAPI": { + parameters: { + query?: { + perPage?: number; + cursor?: string; + conversationId?: string; + contactInboxId?: string; + }; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: (({ + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + conversationId: string; + contactInboxId: string; + workspaceId: string; + text: string | null; + contentAttributes: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]) | null; + /** @enum {string} */ + messageType: "incoming" | "outgoing" | "activity"; + /** @enum {string} */ + contentType: "text" | "location" | "refLink"; + /** @enum {string} */ + senderType: "bot" | "contact" | "system" | "user" | "api"; + senderId?: unknown | null; + sourceId: string | null; + deletedAt: string | null; + /** @enum {string} */ + type: "message" | "comment"; + parentId: string | null; + attributes: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]) | null; + sendError: string | null; + } & { + clientId?: string; + }) & { + attachments: ({ + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + workspaceId?: unknown; + conversationId?: unknown; + messageId?: unknown; + /** Format: date-time */ + messageCreatedAt: string; + /** @enum {string} */ + fileType: "image" | "video" | "audio" | "gif" | "file"; + sourceId: string | null; + mimeType: string; + width: number | null; + height: number | null; + size: number; + thumbnailPath: string | null; + originPath: string; + name: string | null; + } & { + url?: string | null; + })[]; + user?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string | null; + email: string; + emailVerified: boolean; + image: string | null; + isAnonymous: boolean; + mustChangePassword: boolean; + tenantId?: unknown; + }; + contact?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + avatar: string | null; + phoneNumber: string | null; + email: string | null; + emailVerified: boolean; + emailOptIn: boolean; + firstName: string | null; + lastName: string | null; + fullName: string | null; + gender: ("male" | "female" | "unknown") | null; + lastReadAt: string | null; + ref: string | null; + country: string | null; + state: string | null; + city: string | null; + location: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]) | null; + locale: string | null; + timezone: string | null; + subscribedAt: string | null; + broadcastSubscribedAt: string | null; + blockedAt: string | null; + workspaceId: string; + }; + clientId?: string; + })[]; + nextCursor: string | null; + prevCursor: string | null; + }; + }; + }; + }; + }; + "messagesAPI.findMessageAuthenticatedAPI": { + parameters: { + query: { + createdAt: string; + }; + header?: never; + path: { + workspaceId: string; + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": ({ + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + conversationId: string; + contactInboxId: string; + workspaceId: string; + text: string | null; + contentAttributes: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]) | null; + /** @enum {string} */ + messageType: "incoming" | "outgoing" | "activity"; + /** @enum {string} */ + contentType: "text" | "location" | "refLink"; + /** @enum {string} */ + senderType: "bot" | "contact" | "system" | "user" | "api"; + senderId?: unknown | null; + sourceId: string | null; + deletedAt: string | null; + /** @enum {string} */ + type: "message" | "comment"; + parentId: string | null; + attributes: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]) | null; + sendError: string | null; + } & { + clientId?: string; + }) & { + attachmentCount?: number; + attachments?: ({ + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + workspaceId?: unknown; + conversationId?: unknown; + messageId?: unknown; + /** Format: date-time */ + messageCreatedAt: string; + /** @enum {string} */ + fileType: "image" | "video" | "audio" | "gif" | "file"; + sourceId: string | null; + mimeType: string; + width: number | null; + height: number | null; + size: number; + thumbnailPath: string | null; + originPath: string; + name: string | null; + } & { + url?: string | null; + })[]; + user?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string | null; + email: string; + emailVerified: boolean; + image: string | null; + isAnonymous: boolean; + mustChangePassword: boolean; + tenantId?: unknown; + }; + contact?: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + avatar: string | null; + phoneNumber: string | null; + email: string | null; + emailVerified: boolean; + emailOptIn: boolean; + firstName: string | null; + lastName: string | null; + fullName: string | null; + gender: ("male" | "female" | "unknown") | null; + lastReadAt: string | null; + ref: string | null; + country: string | null; + state: string | null; + city: string | null; + location: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]) | null; + locale: string | null; + timezone: string | null; + subscribedAt: string | null; + broadcastSubscribedAt: string | null; + blockedAt: string | null; + workspaceId: string; + }; + clientId?: string; + }; + }; + }; + }; + }; + "messagesAPI.createMessageAuthenticatedAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + conversationId: string; + }; + cookie?: never; + }; + requestBody?: { + content: { + "application/json": { + text?: string; + files?: unknown[]; + flowId?: string; + nodeId?: string; + /** @description ID of the channel to send the message on. null to send message on the last interacted channel (if any). */ + inboxId?: string; + clientId?: string; + replyToMessageId?: string; + /** Format: date-time */ + replyToMessageCreatedAt?: string; + }; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown | unknown; + }; + }; + }; + }; + "messagesAPI.editMessageAuthenticatedAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + conversationId: string; + messageId: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + /** Format: date-time */ + createdAt: string; + newText: string; + newAttachmentPath?: string; + newAttachmentPublicUrl?: string; + newAttachmentMimeType?: string; + newAttachmentName?: string; + newAttachmentSize?: number; + removeAttachment?: boolean; + }; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown | unknown; + }; + }; + }; + }; + "messagesAPI.deleteMessageAuthenticatedAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + conversationId: string; + id: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + /** Format: date-time */ + createdAt: string; + }; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown | unknown; + }; + }; + }; + }; + "messagesAPI.changeMessageAttributesAuthenticatedAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + conversationId: string; + messageId: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + /** Format: date-time */ + createdAt: string; + liked?: boolean; + hidden?: boolean; + }; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown | unknown; + }; + }; + }; + }; + "personasAPIs.listMessengerPersonasAuthenticatedAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + id: string; + name: string; + pageName: string; + }[]; + }; + }; + }; + }; + }; + "errorLogsAPI.listErrorLogsWorkspaceTokenAPI": { + parameters: { + query?: { + page?: number | null; + perPage?: number | null; + sort?: { + id: string; + desc: boolean; + }[] | null; + keyword?: string; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + action: string; + detail: string; + httpCode: string | null; + workspaceId: string; + contactId: string | null; + }[]; + pageCount: number; + }; + }; + }; + }; + }; + "externalWebhooksAPI.listExternalWebhooksWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + id: string; + provider: string; + event: string; + url: string; + }[]; + }; + }; + }; + /** @description 400 */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "businessError"; + /** @constant */ + status: 400; + /** @default An error occurred while processing your request */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + /** @description 429 */ + 429: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "rateLimitExceeded"; + /** @constant */ + status: 429; + /** @default Rate limit exceeded */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + }; + }; + "externalWebhooksAPI.createExternalWebhookWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + /** Format: uri */ + url: string; + event: string; + /** + * @default make + * @enum {string} + */ + provider?: "make" | "n8n"; + }; + }; + }; + responses: { + /** @description OK */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + id: string; + provider: string; + event: string; + url: string; + }; + }; + }; + /** @description 400 */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "businessError"; + /** @constant */ + status: 400; + /** @default An error occurred while processing your request */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + /** @description 422 */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "invalidRequestData"; + /** @constant */ + status: 422; + /** @default Validation error */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + /** @description 429 */ + 429: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "rateLimitExceeded"; + /** @constant */ + status: 429; + /** @default Rate limit exceeded */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + }; + }; + "externalWebhooksAPI.deleteExternalWebhookWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 204: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown | unknown; + }; + }; + /** @description 400 */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "businessError"; + /** @constant */ + status: 400; + /** @default An error occurred while processing your request */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + /** @description 404 */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "notFound"; + /** @constant */ + status: 404; + /** @default Resource not found */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + /** @description 429 */ + 429: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "rateLimitExceeded"; + /** @constant */ + status: 429; + /** @default Rate limit exceeded */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + }; + }; + "workspacesAPI.getWorkspaceWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string; + defaultReply: string | null; + /** @enum {string} */ + defaultReplyFrequency: "allTime" | "oncePerHour" | "oncePerDay"; + targetCountry: string | null; + language: string; + timezone: string; + brandColor: string; + developmentMode: boolean; + smartResponseDelaySeconds: number | null; + isActive: boolean; + startTime: string | null; + endTime: string | null; + logo: string | null; + scheduledDeletionAt: string | null; + ownerId?: unknown; + tenantId?: unknown; + }; + }; + }; + }; + }; + "workspacesAPI.listMyWorkspacesAPI": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + workspaces: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string; + defaultReply: string | null; + /** @enum {string} */ + defaultReplyFrequency: "allTime" | "oncePerHour" | "oncePerDay"; + targetCountry: string | null; + language: string; + timezone: string; + brandColor: string; + developmentMode: boolean; + smartResponseDelaySeconds: number | null; + isActive: boolean; + startTime: string | null; + endTime: string | null; + logo: string | null; + scheduledDeletionAt: string | null; + ownerId?: unknown; + tenantId?: unknown; + token: string | null; + }[]; + }; + }; + }; + }; + }; + "aiFunctionsAPI.listAIFunctionsAuthenticatedAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string; + purpose: string | null; + dataCollect: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]) | null; + outputMessage: string | null; + triggerFlowId?: unknown | null; + workspaceId: string; + }[]; + }; + }; + }; + }; + }; + "platformCredentialsAPI.getGiphyApiKeyAPI": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + apiKey: string | null; + }; + }; + }; + }; + }; + "productsAPI.listProductFormOptionsAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + products: { + id: string; + name: string; + }[]; + vendors: string[]; + }; + }; + }; + }; + }; + "productsAPI.listProductsAuthorizedAPI": { + parameters: { + query?: { + page?: number | null; + perPage?: number | null; + sort?: { + id: string; + desc: boolean; + }[] | null; + name?: string | null; + categoryId?: string | null; + }; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string; + shortDescription: string | null; + longDescription: string | null; + price: number; + taxes: number; + discount: number; + currency: string; + productUrl: string | null; + sku: string | null; + /** @enum {string} */ + inventoryPolicy: "dont_track" | "track"; + inventoryQuantity: number; + allowOutOfStockPurchase: boolean; + images: { + url: string; + /** @enum {string} */ + type: "link" | "file"; + }[]; + tags: string[]; + vendor: string | null; + rank: number; + categoryId: string | null; + subcategoryId: string | null; + isActive: boolean; + isSearchable: boolean; + allowSpecialRequest: boolean; + isAddonOnly: boolean; + workspaceId: string; + category: string | null; + subcategory: string | null; + }[]; + pageCount: number; + }; + }; + }; + }; + }; + "productCategoriesAPI.listProductCategoriesAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + id: string; + parentId: string | null; + name: string; + rank: number; + productCount: number; + }[]; + }; + }; + }; + }; + "questionnairesAPI.listQuestionnairesForFlowAPI": { + parameters: { + query?: { + keyword?: string; + }; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + id: string; + name: string; + }[]; + }; + }; + }; + }; + "refLinksAPI.listRefLinkOptionsAuthenticatedAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + id: string; + name: string; + }[]; + }; + }; + }; + }; + }; + "refLinksAPI.getRefLinksAuthenticatedAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string; + /** @enum {string} */ + type: "refLink" | "qrCode"; + flowId: string; + workspaceId: string; + customFieldId: string | null; + qrStyles: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]) | null; + } | unknown; + }; + }; + }; + }; + "refLinksAPI.getRefLinkWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string; + /** @enum {string} */ + type: "refLink" | "qrCode"; + flowId: string; + workspaceId: string; + customFieldId: string | null; + qrStyles: ((string | number | boolean | null) | { + [key: string]: unknown; + } | (unknown | null)[]) | null; + }; + }; + }; + }; + }; + "keywordsAPI.listKeywordsWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + workspaceId: string; + folderId: string | null; + keywords: string[]; + status: boolean; + text: string | null; + flowId: string | null; + /** @enum {string} */ + type: "inbound" | "outbound"; + }[]; + }; + }; + }; + }; + }; + "integrationsAPI.listIntegrationsWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + workspaceId: string; + integrationType: string; + }[]; + }; + }; + }; + }; + }; + "triggersAPI.listTriggersWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string; + active: boolean; + folderId: string | null; + actions: (unknown | null)[]; + workspaceId: string; + conditions: (unknown | null)[]; + }[]; + }; + }; + }; + }; + }; + "userPersistentMenusAPI.listUserPersistentMenusAuthenticatedAPI": { + parameters: { + query?: never; + header?: never; + path: { + workspaceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string; + menus: ({ + label: string; + /** @constant */ + type: "flow"; + flowId: string; + } | { + label: string; + /** @constant */ + type: "url"; + /** Format: uri */ + url: string; + })[]; + workspaceId: string; + }[]; + }; + }; + }; + }; + }; + "webhooksAPI.listWebhooksWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data: { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string; + active: boolean; + folderId: string | null; + url: string; + workspaceId: string; + }[]; + }; + }; + }; + /** @description 400 */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "businessError"; + /** @constant */ + status: 400; + /** @default An error occurred while processing your request */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + /** @description 429 */ + 429: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "rateLimitExceeded"; + /** @constant */ + status: 429; + /** @default Rate limit exceeded */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + }; + }; + "webhooksAPI.createWebhookWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + name: string; + /** Format: uri */ + url: string; + conditions: ({ + id?: string; + /** @constant */ + type: "tagApplied"; + sourceId: string; + } | { + id?: string; + /** @constant */ + type: "tagRemoved"; + sourceId: string; + } | { + id?: string; + /** @constant */ + type: "customFieldValueChanged"; + sourceId: string; + operator: string; + value?: unknown; + } | { + id?: string; + /** @constant */ + type: "contactInfoUpdated"; + /** @enum {string} */ + sourceId: "phone" | "email"; + } | { + id?: string; + /** @constant */ + type: "dateTimeBasedTrigger"; + sourceId?: string; + operator: string; + value: { + /** @enum {string} */ + triggerType: "atTheDayOf" | "before" | "after"; + timeValue?: number; + /** @enum {string} */ + timeType?: "minutes" | "hours" | "days"; + at?: string; + timezone?: string; + }; + } | { + id?: string; + /** @constant */ + type: "conversationTransferredToHuman"; + } | { + id?: string; + /** @constant */ + type: "conversationTransferredToBot"; + } | { + id?: string; + /** @constant */ + type: "newContact"; + } | { + id?: string; + /** @constant */ + type: "contactUnsubscribedFormBroadcast"; + } | { + id?: string; + /** @constant */ + type: "archived"; + } | { + id?: string; + /** @constant */ + type: "followUp"; + } | { + id?: string; + /** @constant */ + type: "conversationAssigned"; + } | { + id?: string; + /** @constant */ + type: "conversationUnassigned"; + } | { + id?: string; + /** @constant */ + type: "subscribedToSequence"; + sourceId: string; + } | { + id?: string; + /** @constant */ + type: "unsubscribedFromSequence"; + sourceId: string; + } | { + id?: string; + /** @constant */ + type: "contactReferredANewContact"; + } | { + id?: string; + /** @constant */ + type: "contactReferredExistingContact"; + })[]; + }; + }; + }; + responses: { + /** @description OK */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + name: string; + active: boolean; + folderId: string | null; + url: string; + workspaceId: string; + }; + }; + }; + /** @description 400 */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "businessError"; + /** @constant */ + status: 400; + /** @default An error occurred while processing your request */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + /** @description 422 */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "invalidRequestData"; + /** @constant */ + status: 422; + /** @default Validation error */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + /** @description 429 */ + 429: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "rateLimitExceeded"; + /** @constant */ + status: 429; + /** @default Rate limit exceeded */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + }; + }; + "webhooksAPI.deleteWebhookWorkspaceTokenAPI": { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 204: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown | unknown; + }; + }; + /** @description 400 */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "businessError"; + /** @constant */ + status: 400; + /** @default An error occurred while processing your request */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + /** @description 404 */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "notFound"; + /** @constant */ + status: 404; + /** @default Resource not found */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + /** @description 429 */ + 429: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @constant */ + defined: true; + /** @constant */ + code: "rateLimitExceeded"; + /** @constant */ + status: 429; + /** @default Rate limit exceeded */ + message: string; + data?: unknown; + } | components["schemas"]["UndefinedError"]; + }; + }; + }; + }; +} diff --git a/src/api/pagination.test.ts b/src/api/pagination.test.ts new file mode 100644 index 0000000..690fa88 --- /dev/null +++ b/src/api/pagination.test.ts @@ -0,0 +1,32 @@ +import { flattenPages, getNextPageParam } from '@/api/pagination'; +import type { CursorPage } from '@/api/pagination'; + +describe('getNextPageParam', () => { + it('returns the cursor when present', () => { + const page: CursorPage<number> = { data: [1, 2], nextCursor: 'abc' }; + expect(getNextPageParam(page)).toBe('abc'); + }); + + it('returns undefined when nextCursor is null', () => { + const page: CursorPage<number> = { data: [1, 2], nextCursor: null }; + expect(getNextPageParam(page)).toBeUndefined(); + }); +}); + +describe('flattenPages', () => { + it('flattens data across multiple pages in order', () => { + const pages: CursorPage<number>[] = [ + { data: [1, 2], nextCursor: 'a' }, + { data: [3, 4], nextCursor: null }, + ]; + expect(flattenPages(pages)).toEqual([1, 2, 3, 4]); + }); + + it('returns an empty array for undefined pages', () => { + expect(flattenPages(undefined)).toEqual([]); + }); + + it('returns an empty array for an empty pages array', () => { + expect(flattenPages([])).toEqual([]); + }); +}); diff --git a/src/api/pagination.ts b/src/api/pagination.ts new file mode 100644 index 0000000..4d01aea --- /dev/null +++ b/src/api/pagination.ts @@ -0,0 +1,20 @@ +/** + * Cursor-pagination helpers shared by every `useInfiniteQuery` hook. The generated schema's + * cursor-paginated list responses share the `{ data: T[], nextCursor: string | null, prevCursor: + * string | null }` shape — verified directly against every cursor-paginated operation in + * src/api/generated/schema.ts (conversations/list and 5 others), NOT `{ items, nextCursor }` as + * originally assumed during Phase 0 pre-flight; corrected once Phase 3 hit the real shape. + */ +export interface CursorPage<T> { + data: T[]; + nextCursor: string | null; + prevCursor?: string | null; +} + +export function getNextPageParam<T>(page: CursorPage<T>): string | undefined { + return page.nextCursor ?? undefined; +} + +export function flattenPages<T>(pages: CursorPage<T>[] | undefined): T[] { + return pages?.flatMap((page) => page.data) ?? []; +} diff --git a/src/api/query-keys.test.ts b/src/api/query-keys.test.ts new file mode 100644 index 0000000..cc5b1c1 --- /dev/null +++ b/src/api/query-keys.test.ts @@ -0,0 +1,53 @@ +import { isWorkspaceQuery, queryKeys } from '@/api/query-keys'; + +describe('queryKeys', () => { + it('builds workspace-scoped conversation list keys', () => { + expect(queryKeys.ws.conversations.list('w1', { status: 'unread' })).toEqual([ + 'ws', + 'w1', + 'conversations', + 'list', + { status: 'unread' }, + ]); + }); + + it('builds global session/workspaces keys without a workspace id', () => { + expect(queryKeys.session()).toEqual(['session']); + expect(queryKeys.workspaces()).toEqual(['workspaces']); + }); + + it('builds workspace-scoped sequences list key', () => { + expect(queryKeys.ws.sequences.list('w1')).toEqual(['ws', 'w1', 'sequences', 'list']); + }); + + it('builds workspace-scoped tags catalog key', () => { + expect(queryKeys.ws.tags.catalog('w1')).toEqual(['ws', 'w1', 'tags', 'catalog']); + }); + + it('builds workspace-scoped custom-fields catalog key', () => { + expect(queryKeys.ws.customFields.catalog('w1')).toEqual([ + 'ws', + 'w1', + 'custom-fields', + 'catalog', + ]); + }); +}); + +describe('isWorkspaceQuery', () => { + it('returns true for a matching workspace-scoped key', () => { + expect(isWorkspaceQuery(['ws', 'w1', 'conversations', 'list'], 'w1')).toBe(true); + }); + + it('returns false for a different workspace id', () => { + expect(isWorkspaceQuery(['ws', 'w1', 'conversations', 'list'], 'w2')).toBe(false); + }); + + it('returns false for a non-workspace-scoped key', () => { + expect(isWorkspaceQuery(['session'], 'w1')).toBe(false); + }); + + it('returns false for an empty key', () => { + expect(isWorkspaceQuery([], 'w1')).toBe(false); + }); +}); diff --git a/src/api/query-keys.ts b/src/api/query-keys.ts new file mode 100644 index 0000000..be04edf --- /dev/null +++ b/src/api/query-keys.ts @@ -0,0 +1,60 @@ +/** + * Central query-key factory. Every workspace-scoped key starts with `['ws', workspaceId, ...]` so + * a workspace switch can invalidate/clear everything for the old workspace with one + * `queryClient.removeQueries({ predicate: (query) => query.queryKey[0] === 'ws' && query.queryKey[1] === oldWorkspaceId })` + * call. Session/workspace-list keys are global (not workspace-scoped). + */ +export const queryKeys = { + session: () => ['session'] as const, + workspaces: () => ['workspaces'] as const, + + ws: { + conversations: { + list: (workspaceId: string, filters: Record<string, unknown>) => + ['ws', workspaceId, 'conversations', 'list', filters] as const, + detail: (workspaceId: string, conversationId: string) => + ['ws', workspaceId, 'conversations', 'detail', conversationId] as const, + }, + messages: { + list: (workspaceId: string, conversationId: string) => + ['ws', workspaceId, 'messages', 'list', conversationId] as const, + }, + contacts: { + list: (workspaceId: string, filters: Record<string, unknown>) => + ['ws', workspaceId, 'contacts', 'list', filters] as const, + detail: (workspaceId: string, contactId: string) => + ['ws', workspaceId, 'contacts', 'detail', contactId] as const, + tags: (workspaceId: string, contactId: string) => + ['ws', workspaceId, 'contacts', 'tags', contactId] as const, + sequences: (workspaceId: string, contactId: string) => + ['ws', workspaceId, 'contacts', 'sequences', contactId] as const, + }, + flows: { + list: (workspaceId: string) => ['ws', workspaceId, 'flows', 'list'] as const, + }, + sequences: { + list: (workspaceId: string) => ['ws', workspaceId, 'sequences', 'list'] as const, + }, + tags: { + catalog: (workspaceId: string) => ['ws', workspaceId, 'tags', 'catalog'] as const, + }, + customFields: { + catalog: (workspaceId: string) => ['ws', workspaceId, 'custom-fields', 'catalog'] as const, + }, + members: { + list: (workspaceId: string) => ['ws', workspaceId, 'members', 'list'] as const, + }, + teams: { + list: (workspaceId: string) => ['ws', workspaceId, 'teams', 'list'] as const, + }, + savedReplies: { + list: (workspaceId: string) => ['ws', workspaceId, 'saved-replies', 'list'] as const, + }, + }, +} as const; + +/** Predicate for `queryClient.removeQueries` on workspace switch — clears every `['ws', id, ...]` + * key for the given workspace. */ +export function isWorkspaceQuery(queryKey: readonly unknown[], workspaceId: string): boolean { + return queryKey[0] === 'ws' && queryKey[1] === workspaceId; +} diff --git a/src/app/(app)/(tabs)/_layout.tsx b/src/app/(app)/(tabs)/_layout.tsx new file mode 100644 index 0000000..f3bfc58 --- /dev/null +++ b/src/app/(app)/(tabs)/_layout.tsx @@ -0,0 +1,73 @@ +import { Redirect, Tabs } from 'expo-router'; +import { useTranslation } from 'react-i18next'; +import type { ColorValue } from 'react-native'; + +import { Icon, type IconName } from '@/components/ui/icon'; +import { useInboxUnreadCount } from '@/features/conversations/api/use-inbox-unread-count'; +import { usePushTokenRotation } from '@/features/settings/api/use-device-token'; +import { RealtimeProvider } from '@/realtime/realtime-provider'; +import { useSettingsStore } from '@/stores/use-settings-store'; +import { useWorkspaceStore } from '@/stores/use-workspace-store'; +import { useTheme } from '@/theme/use-theme'; + +/** react-navigation types tabBarIcon's `color` as `ColorValue` (includes opaque platform colors); + * our theme tokens are always plain hex strings, so this casts back for the Icon primitive. */ +function tabIcon(name: IconName) { + function TabIcon({ color, size }: { color: ColorValue; size: number }) { + return <Icon name={name} size={size} color={color as string} />; + } + return TabIcon; +} + +/** Tabs shell: guarded on having a selected workspace, wraps children in the realtime provider so + * every tab screen gets live updates. */ +export default function TabsLayout() { + const { t } = useTranslation(); + const { colors } = useTheme(); + const currentWorkspaceId = useWorkspaceStore((state) => state.currentWorkspaceId); + const pushEnabled = useSettingsStore((state) => state.pushEnabled); + const unreadCount = useInboxUnreadCount(currentWorkspaceId); + + // Lives here (not in Settings) so it re-registers the device token for whichever workspace is + // currently active, including after an inline workspace switch — the toggle itself stays a + // Settings-owned control (see settings/index.tsx's ToggleRow + its own register/unregister + // mutations for the user-initiated on/off path). + usePushTokenRotation(currentWorkspaceId ?? '', pushEnabled); + + if (!currentWorkspaceId) { + return <Redirect href="/(app)/workspace-picker" />; + } + + return ( + <RealtimeProvider> + <Tabs + screenOptions={{ + headerShown: false, + tabBarActiveTintColor: colors.primary, + tabBarInactiveTintColor: colors.textSecondary, + tabBarStyle: { backgroundColor: colors.surfaceElevated, borderTopColor: colors.border }, + }} + > + <Tabs.Screen + name="conversations/index" + options={{ + title: t('inbox.title'), + tabBarIcon: tabIcon('chatbubbles'), + // `useInboxUnreadCount` only counts CACHED list pages, not a true server-side unread + // total (no such endpoint exists yet — see the hook's own doc comment). `undefined` + // (not 0) hides the badge entirely when there's nothing unread. + tabBarBadge: unreadCount || undefined, + }} + /> + <Tabs.Screen + name="contacts/index" + options={{ title: t('contacts.title'), tabBarIcon: tabIcon('people') }} + /> + <Tabs.Screen + name="settings/index" + options={{ title: t('settings.title'), tabBarIcon: tabIcon('settings') }} + /> + </Tabs> + </RealtimeProvider> + ); +} diff --git a/src/app/(app)/(tabs)/contacts/index.tsx b/src/app/(app)/(tabs)/contacts/index.tsx new file mode 100644 index 0000000..94f90ea --- /dev/null +++ b/src/app/(app)/(tabs)/contacts/index.tsx @@ -0,0 +1,96 @@ +import { FlashList } from '@shopify/flash-list'; +import { useRouter } from 'expo-router'; +import { useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { RefreshControl, View } from 'react-native'; + +import { EmptyState } from '@/components/ui/empty-state'; +import { ErrorBanner } from '@/components/ui/error-banner'; +import { ListFooterSpinner } from '@/components/ui/list-footer-spinner'; +import { Screen } from '@/components/ui/screen'; +import { SearchBar } from '@/components/ui/search-bar'; +import { Skeleton } from '@/components/ui/skeleton'; +import { WorkspaceBlockedGate } from '@/components/workspace-blocked-gate'; +import { normalizeApiError } from '@/api/errors'; +import { describeApiError } from '@/lib/describe-api-error'; +import { ContactRow } from '@/features/contacts/contact-row'; +import { + flattenContactPages, + useContactsInfinite, +} from '@/features/contacts/api/use-contacts-infinite'; +import { useWorkspaceStore } from '@/stores/use-workspace-store'; +import { useTheme } from '@/theme/use-theme'; + +export default function ContactsScreen() { + const { t } = useTranslation(); + const { colors, spacing } = useTheme(); + const router = useRouter(); + const workspaceId = useWorkspaceStore((state) => state.currentWorkspaceId); + const [keyword, setKeyword] = useState(''); + + const query = useContactsInfinite(workspaceId, keyword); + const contacts = flattenContactPages(query.data?.pages); + const normalizedError = query.error ? normalizeApiError(query.error) : null; + + if (normalizedError?.kind === 'workspaceBlocked') { + return ( + <Screen edges={['top']}> + <WorkspaceBlockedGate reason={normalizedError.reason} message={normalizedError.message} /> + </Screen> + ); + } + + return ( + <Screen edges={['top']}> + <View style={{ padding: spacing.md, paddingBottom: spacing.sm }}> + <SearchBar value={keyword} onChangeText={setKeyword} placeholder={t('common.search')} /> + </View> + + {normalizedError ? ( + <View style={{ paddingHorizontal: spacing.md }}> + <ErrorBanner + message={describeApiError(query.error, t)} + actionLabel={t('common.retry')} + onAction={() => query.refetch()} + /> + </View> + ) : null} + + {query.isLoading ? ( + <View style={{ paddingHorizontal: spacing.md, gap: spacing.sm }}> + {Array.from({ length: 8 }).map((_, index) => ( + <Skeleton key={index} height={56} /> + ))} + </View> + ) : contacts.length === 0 ? ( + <EmptyState icon="people-outline" title={t('contacts.empty')} /> + ) : ( + <FlashList + data={contacts} + keyExtractor={(contact) => contact.id} + renderItem={({ item }) => ( + <ContactRow + contact={item} + workspaceId={workspaceId} + onPress={() => router.push(`/(app)/contacts/${item.id}`)} + /> + )} + onEndReachedThreshold={0.4} + onEndReached={() => { + if (query.hasNextPage && !query.isFetchingNextPage) { + query.fetchNextPage(); + } + }} + refreshControl={ + <RefreshControl + refreshing={query.isRefetching} + onRefresh={() => query.refetch()} + tintColor={colors.brand} + /> + } + ListFooterComponent={<ListFooterSpinner visible={query.isFetchingNextPage} />} + /> + )} + </Screen> + ); +} diff --git a/src/app/(app)/(tabs)/conversations/index.tsx b/src/app/(app)/(tabs)/conversations/index.tsx new file mode 100644 index 0000000..dd4194f --- /dev/null +++ b/src/app/(app)/(tabs)/conversations/index.tsx @@ -0,0 +1,273 @@ +import type BottomSheet from '@gorhom/bottom-sheet'; +import { FlashList } from '@shopify/flash-list'; +import { router } from 'expo-router'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { RefreshControl, StyleSheet, View } from 'react-native'; +import { useShallow } from 'zustand/react/shallow'; + +import { ConnectionBanner } from '@/components/ui/connection-banner'; +import { EmptyState } from '@/components/ui/empty-state'; +import { ErrorBanner } from '@/components/ui/error-banner'; +import { IconButton } from '@/components/ui/icon-button'; +import { ListFooterSpinner } from '@/components/ui/list-footer-spinner'; +import { Screen } from '@/components/ui/screen'; +import { SearchBar } from '@/components/ui/search-bar'; +import { SkeletonRow } from '@/components/ui/skeleton'; +import { useToast } from '@/components/ui/toast'; +import { WorkspaceBlockedGate } from '@/components/workspace-blocked-gate'; +import { normalizeApiError } from '@/api/errors'; +import { describeApiError } from '@/lib/describe-api-error'; +import { AssignmentSheet } from '@/features/conversations/components/assignment-sheet'; +import { + ConversationRow, + conversationRowItemType, +} from '@/features/conversations/components/conversation-row'; +import { FilterChipsRow } from '@/features/conversations/components/filter-chips-row'; +import { FilterSheet } from '@/features/conversations/components/filter-sheet'; +import { + flattenConversationPages, + useConversationsInfinite, + type ConversationListItem, +} from '@/features/conversations/api/use-conversations-infinite'; +import { + useArchiveConversations, + useAssignConversations, + useDisableBot, + useEnableBot, + useMarkConversationRead, + useMarkConversationUnread, + useUnarchiveConversations, +} from '@/features/conversations/api/use-conversation-actions'; +import { isBotActive, isUnread } from '@/features/conversations/lib/conversation-status'; +import { + conversationFiltersSnapshot, + useConversationFilters, +} from '@/features/conversations/stores/use-conversation-filters'; +import { WorkspaceSwitcherSheet } from '@/features/workspaces/components/workspace-switcher-sheet'; +import { WorkspacePill } from '@/features/workspaces/components/workspace-pill'; +import { useWorkspaces } from '@/features/workspaces/api/use-workspaces'; +import { useConnectionBannerState } from '@/realtime/use-connection-store'; +import { useWorkspaceStore } from '@/stores/use-workspace-store'; +import { useTheme } from '@/theme/use-theme'; + +const SEARCH_DEBOUNCE_MS = 350; +const SKELETON_ROW_COUNT = 8; + +/** Count of active filter dimensions (excluding `keyword`, which has its own search bar affordance + * and doesn't belong in the "how many filters are on" badge) — drives the filter IconButton's + * `badgeCount`. */ +function activeFilterCount(filters: ReturnType<typeof conversationFiltersSnapshot>): number { + let count = 0; + if (filters.botCategory) count += 1; + if (filters.assignedId !== undefined) count += 1; + if (filters.channel) count += 1; + if (filters.status && filters.status.length > 0) count += 1; + if (filters.botEnabled !== undefined && filters.botEnabled !== null) count += 1; + return count; +} + +export default function ConversationsScreen() { + const { t } = useTranslation(); + const { colors, spacing } = useTheme(); + const workspaceId = useWorkspaceStore((state) => state.currentWorkspaceId); + const showToast = useToast(); + const connectionState = useConnectionBannerState(); + + const [searchText, setSearchText] = useState(''); + const setKeyword = useConversationFilters((state) => state.setKeyword); + const resetFilters = useConversationFilters((state) => state.reset); + const filtersSnapshot = useConversationFilters(useShallow(conversationFiltersSnapshot)); + const filterCount = activeFilterCount(filtersSnapshot); + const [assigningConversation, setAssigningConversation] = useState<ConversationListItem | null>( + null, + ); + + const filterSheetRef = useRef<BottomSheet>(null); + const assignmentSheetRef = useRef<BottomSheet>(null); + const workspaceSwitcherSheetRef = useRef<BottomSheet>(null); + const { data: workspaces } = useWorkspaces(); + const currentWorkspace = workspaces?.find((workspace) => workspace.id === workspaceId); + + useEffect(() => { + const timeout = setTimeout(() => setKeyword(searchText), SEARCH_DEBOUNCE_MS); + return () => clearTimeout(timeout); + }, [searchText, setKeyword]); + + const query = useConversationsInfinite(workspaceId); + const conversations = flattenConversationPages(query.data?.pages); + const assignMutation = useAssignConversations(workspaceId ?? ''); + const markRead = useMarkConversationRead(workspaceId ?? ''); + const markUnread = useMarkConversationUnread(workspaceId ?? ''); + const enableBot = useEnableBot(workspaceId ?? ''); + const disableBot = useDisableBot(workspaceId ?? ''); + const archiveConversations = useArchiveConversations(workspaceId ?? ''); + const unarchiveConversations = useUnarchiveConversations(workspaceId ?? ''); + + const openAssignmentSheet = useCallback((conversation: ConversationListItem) => { + setAssigningConversation(conversation); + assignmentSheetRef.current?.expand(); + }, []); + + function handleAssign(userId: string | null) { + if (!assigningConversation?.contactId) return; + assignMutation.mutate({ contactIds: [assigningConversation.contactId], assignedId: userId }); + } + + function handleToggleRead(conversation: ConversationListItem) { + if (isUnread(conversation)) { + markRead.mutate(conversation.id); + } else { + markUnread.mutate(conversation.id); + } + } + + function handleToggleBot(conversation: ConversationListItem) { + if (isBotActive(conversation.botEnabled, conversation.botResumeAt)) { + disableBot.mutate([conversation.id]); + } else { + enableBot.mutate([conversation.id]); + } + } + + function handleArchive(conversation: ConversationListItem) { + archiveConversations.mutate([conversation.id]); + showToast({ + message: t('conversations.archive'), + tone: 'neutral', + action: { + label: t('conversations.undo'), + onPress: () => unarchiveConversations.mutate([conversation.id]), + }, + }); + } + + const listEmptyComponent = useMemo(() => { + if (filterCount > 0 || filtersSnapshot.keyword) { + return ( + <EmptyState + icon="filter-outline" + title={t('conversations.emptyFiltered')} + action={{ label: t('conversations.clearFilters'), onPress: resetFilters }} + /> + ); + } + return <EmptyState icon="chatbubbles-outline" title={t('conversations.empty')} />; + }, [filterCount, filtersSnapshot.keyword, resetFilters, t]); + + const normalizedError = query.error ? normalizeApiError(query.error) : null; + if (normalizedError?.kind === 'workspaceBlocked') { + return ( + <Screen edges={['top']}> + <WorkspaceBlockedGate reason={normalizedError.reason} message={normalizedError.message} /> + </Screen> + ); + } + + return ( + <Screen edges={['top']}> + <View style={[styles.header, { padding: spacing.md, gap: spacing.sm }]}> + {currentWorkspace ? ( + <WorkspacePill + name={currentWorkspace.name} + logo={currentWorkspace.logo} + onPress={() => workspaceSwitcherSheetRef.current?.expand()} + /> + ) : null} + <View style={styles.searchRow}> + <View style={styles.searchInput}> + <SearchBar + value={searchText} + onChangeText={setSearchText} + placeholder={t('common.search') ?? undefined} + /> + </View> + <IconButton + accessibilityLabel={t('conversations.filters')} + icon="filter" + variant="tonal" + badgeCount={filterCount} + onPress={() => filterSheetRef.current?.expand()} + /> + </View> + </View> + + <ConnectionBanner state={connectionState} /> + + <FilterChipsRow /> + + {query.isError ? ( + <View style={{ paddingHorizontal: spacing.md, paddingTop: spacing.sm }}> + <ErrorBanner + message={describeApiError(query.error, t)} + actionLabel={t('common.retry') ?? undefined} + onAction={() => query.refetch()} + /> + </View> + ) : null} + + {query.isPending ? ( + <View style={{ paddingTop: spacing.sm }}> + {Array.from({ length: SKELETON_ROW_COUNT }).map((_, index) => ( + <SkeletonRow.Conversation key={index} /> + ))} + </View> + ) : ( + <FlashList + data={conversations} + keyExtractor={(item) => item.id} + getItemType={conversationRowItemType} + renderItem={({ item }) => ( + <ConversationRow + conversation={item} + onPress={() => router.push(`/(app)/conversations/${item.id}`)} + onLongPress={() => openAssignmentSheet(item)} + onToggleRead={() => handleToggleRead(item)} + onToggleBot={() => handleToggleBot(item)} + onArchive={() => handleArchive(item)} + /> + )} + onEndReachedThreshold={0.4} + onEndReached={() => { + if (query.hasNextPage && !query.isFetchingNextPage) { + query.fetchNextPage(); + } + }} + refreshControl={ + <RefreshControl + refreshing={query.isRefetching} + onRefresh={() => query.refetch()} + tintColor={colors.brand} + /> + } + ListEmptyComponent={listEmptyComponent} + ListFooterComponent={<ListFooterSpinner visible={query.isFetchingNextPage} />} + /> + )} + + <FilterSheet sheetRef={filterSheetRef} /> + {workspaceId ? ( + <AssignmentSheet + sheetRef={assignmentSheetRef} + workspaceId={workspaceId} + onAssign={handleAssign} + /> + ) : null} + <WorkspaceSwitcherSheet ref={workspaceSwitcherSheetRef} /> + </Screen> + ); +} + +const styles = StyleSheet.create({ + header: { + flexDirection: 'column', + }, + searchRow: { + flexDirection: 'row', + alignItems: 'center', + gap: 8, + }, + searchInput: { + flex: 1, + }, +}); diff --git a/src/app/(app)/(tabs)/settings/index.tsx b/src/app/(app)/(tabs)/settings/index.tsx new file mode 100644 index 0000000..c083bfc --- /dev/null +++ b/src/app/(app)/(tabs)/settings/index.tsx @@ -0,0 +1,209 @@ +import type BottomSheet from '@gorhom/bottom-sheet'; +import Constants from 'expo-constants'; +import { router } from 'expo-router'; +import * as Notifications from 'expo-notifications'; +import { useRef } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Alert, Linking, ScrollView, View } from 'react-native'; +import Animated, { FadeIn } from 'react-native-reanimated'; + +import { signOut as signOutRequest } from '@/api/auth-endpoints'; +import { clearAuthToken, getAuthToken } from '@/api/auth-token'; +import { Avatar } from '@/components/ui/avatar'; +import { Card } from '@/components/ui/surface'; +import { Divider } from '@/components/ui/divider'; +import { ListItem } from '@/components/ui/list-item'; +import { Screen } from '@/components/ui/screen'; +import { SectionHeader } from '@/components/ui/section-header'; +import { SegmentedTabs } from '@/components/ui/segmented-tabs'; +import { Text } from '@/components/ui/text'; +import { ToggleRow } from '@/components/ui/toggle-row'; +import { + useRegisterDeviceToken, + useUnregisterDeviceToken, +} from '@/features/settings/api/use-device-token'; +import { LanguageSheet } from '@/features/settings/components/language-sheet'; +import { WorkspaceSwitcherSheet } from '@/features/workspaces/components/workspace-switcher-sheet'; +import { useWorkspaces } from '@/features/workspaces/api/use-workspaces'; +import { localeMeta } from '@/i18n/locales'; +import { queryClient } from '@/lib/query-client'; +import { useReducedMotion } from '@/theme/motion'; +import { useResolvedScheme, useTheme } from '@/theme/use-theme'; +import { useAuthStore } from '@/stores/use-auth-store'; +import type { ThemePreference } from '@/stores/use-settings-store'; +import { useSettingsStore } from '@/stores/use-settings-store'; +import { useWorkspaceStore } from '@/stores/use-workspace-store'; + +const appVersion = Constants.expoConfig?.version ?? null; + +export default function SettingsScreen() { + const { t } = useTranslation(); + const { spacing } = useTheme(); + const scheme = useResolvedScheme(); + const reducedMotion = useReducedMotion(); + + const user = useAuthStore((state) => state.user); + const setSignedOut = useAuthStore((state) => state.setSignedOut); + + const themePreference = useSettingsStore((state) => state.themePreference); + const setThemePreference = useSettingsStore((state) => state.setThemePreference); + const language = useSettingsStore((state) => state.language); + + const languageSheetRef = useRef<BottomSheet>(null); + const workspaceSwitcherSheetRef = useRef<BottomSheet>(null); + + const currentWorkspaceId = useWorkspaceStore((state) => state.currentWorkspaceId); + const setCurrentWorkspaceId = useWorkspaceStore((state) => state.setCurrentWorkspaceId); + const { data: workspaces } = useWorkspaces(); + const currentWorkspace = workspaces?.find((workspace) => workspace.id === currentWorkspaceId); + + // Persisted user *intent* — the effective toggle state also depends on OS permission, which + // can be revoked outside the app, so this isn't the only source of truth for what's displayed. + // Token rotation itself (re-registering on a new Expo push token) is handled once, workspace- + // aware, in the tabs layout — this screen only owns the on/off toggle and its mutations. + const pushEnabled = useSettingsStore((state) => state.pushEnabled); + const setPushEnabled = useSettingsStore((state) => state.setPushEnabled); + const registerDeviceToken = useRegisterDeviceToken(currentWorkspaceId ?? ''); + const unregisterDeviceToken = useUnregisterDeviceToken(); + + async function handleTogglePush(value: boolean) { + if (!value) { + setPushEnabled(false); + unregisterDeviceToken.mutate(); + return; + } + + const permission = await Notifications.getPermissionsAsync(); + if (permission.status === 'denied' && !permission.canAskAgain) { + // Denied-forever: the OS won't show the prompt again — only Settings can flip it. + Linking.openSettings(); + return; + } + + registerDeviceToken.mutate(undefined, { + onSuccess: () => setPushEnabled(true), + onError: () => setPushEnabled(false), + }); + } + + async function performSignOut() { + const token = await getAuthToken(); + if (token) { + await signOutRequest(token).catch(() => {}); + } + await clearAuthToken(); + queryClient.clear(); + setCurrentWorkspaceId(null); + setSignedOut(); + } + + function handleSignOut() { + Alert.alert(t('auth.signOutConfirmTitle'), t('auth.signOutConfirmBody'), [ + { text: t('common.cancel'), style: 'cancel' }, + { text: t('auth.signOut'), style: 'destructive', onPress: () => void performSignOut() }, + ]); + } + + return ( + <Screen edges={['top']}> + <Animated.View + key={reducedMotion ? undefined : scheme} + entering={reducedMotion ? undefined : FadeIn.duration(200)} + style={{ flex: 1 }} + > + <ScrollView contentContainerStyle={{ padding: spacing.md, gap: spacing.lg }}> + <Text variant="display">{t('settings.title')}</Text> + + <Card> + <ListItem + title={user?.name ?? user?.email ?? t('settings.profile')} + subtitle={user?.name ? user.email : undefined} + leading={<Avatar name={user?.name ?? user?.email ?? '?'} size={56} />} + onPress={() => router.push('/(app)/profile')} + showChevron + /> + </Card> + + <View> + <SectionHeader title={t('settings.workspace')} /> + <Card> + <ListItem + title={t('settings.switchWorkspace')} + value={currentWorkspace?.name} + leading={ + currentWorkspace ? ( + <Avatar uri={currentWorkspace.logo} name={currentWorkspace.name} size={36} /> + ) : undefined + } + onPress={() => workspaceSwitcherSheetRef.current?.expand()} + showChevron + /> + <Divider inset /> + <ListItem + title={t('settings.members')} + onPress={() => router.push('/(app)/settings/members')} + showChevron + /> + </Card> + <WorkspaceSwitcherSheet ref={workspaceSwitcherSheetRef} /> + </View> + + <View> + <SectionHeader title={t('settings.appearance')} /> + <Card> + <View style={{ paddingHorizontal: spacing.md, paddingVertical: spacing.ms }}> + <Text variant="body" style={{ marginBottom: spacing.xs }}> + {t('settings.theme')} + </Text> + <SegmentedTabs<ThemePreference> + options={[ + { value: 'system', label: t('settings.themeSystem') }, + { value: 'light', label: t('settings.themeLight') }, + { value: 'dark', label: t('settings.themeDark') }, + ]} + value={themePreference} + onChange={setThemePreference} + /> + </View> + <Divider inset /> + <ListItem + title={t('settings.language')} + value={localeMeta[language].nativeLabel} + onPress={() => languageSheetRef.current?.expand()} + showChevron + /> + </Card> + <LanguageSheet sheetRef={languageSheetRef} currentLanguage={language} /> + </View> + + <View> + <SectionHeader title={t('settings.notifications')} /> + <Card> + <ToggleRow + label={t('settings.pushNotifications')} + description={t('settings.pushNotificationsDescription')} + value={pushEnabled} + onValueChange={(value) => void handleTogglePush(value)} + disabled={registerDeviceToken.isPending || unregisterDeviceToken.isPending} + /> + </Card> + </View> + + <Card> + <ListItem title={t('auth.signOut')} onPress={handleSignOut} destructive /> + </Card> + + {appVersion ? ( + <Text + variant="micro" + color="tertiary" + style={{ textAlign: 'center', marginTop: spacing.sm }} + > + {t('settings.version', { version: appVersion })} + </Text> + ) : null} + </ScrollView> + </Animated.View> + </Screen> + ); +} diff --git a/src/app/(app)/_layout.tsx b/src/app/(app)/_layout.tsx new file mode 100644 index 0000000..0b9eb35 --- /dev/null +++ b/src/app/(app)/_layout.tsx @@ -0,0 +1,35 @@ +import { Redirect, Stack } from 'expo-router'; +import { useTranslation } from 'react-i18next'; + +import { useAuthStore } from '@/stores/use-auth-store'; + +/** App route group: gated on an active session. A must-change-password user is trapped in + * `(auth)/change-password` (that layout's own logic), so reaching here implies the gate is clear. */ +export default function AppLayout() { + const { t } = useTranslation(); + const status = useAuthStore((state) => state.status); + + if (status !== 'signed-in') { + return <Redirect href="/(auth)/sign-in" />; + } + + return ( + <Stack screenOptions={{ headerShown: false }}> + <Stack.Screen name="(tabs)" /> + <Stack.Screen name="workspace-picker" /> + <Stack.Screen + name="profile" + options={{ presentation: 'modal', headerShown: true, title: t('settings.profile') }} + /> + <Stack.Screen name="conversations/[conversationId]/index" /> + <Stack.Screen + name="conversations/[conversationId]/contact" + options={{ headerShown: true, title: t('contacts.contactTitle') }} + /> + <Stack.Screen + name="settings/members" + options={{ headerShown: true, title: t('settings.members') }} + /> + </Stack> + ); +} diff --git a/src/app/(app)/contacts/[contactId].tsx b/src/app/(app)/contacts/[contactId].tsx new file mode 100644 index 0000000..58418dc --- /dev/null +++ b/src/app/(app)/contacts/[contactId].tsx @@ -0,0 +1,15 @@ +import { useLocalSearchParams } from 'expo-router'; + +import { ContactPanel } from '@/features/contacts/contact-panel'; +import { useWorkspaceStore } from '@/stores/use-workspace-store'; + +/** Standalone contact detail, reached from the contacts-tab directory. Reuses the same panel as + * the conversation-nested route (contact.tsx). */ +export default function ContactDetailScreen() { + const { contactId } = useLocalSearchParams<{ contactId: string }>(); + const workspaceId = useWorkspaceStore((state) => state.currentWorkspaceId); + + return ( + <ContactPanel workspaceId={workspaceId} contactId={contactId ?? null} origin="directory" /> + ); +} diff --git a/src/app/(app)/conversations/[conversationId]/contact.tsx b/src/app/(app)/conversations/[conversationId]/contact.tsx new file mode 100644 index 0000000..d5a110b --- /dev/null +++ b/src/app/(app)/conversations/[conversationId]/contact.tsx @@ -0,0 +1,36 @@ +import { useLocalSearchParams } from 'expo-router'; + +import { Screen } from '@/components/ui/screen'; +import { Skeleton } from '@/components/ui/skeleton'; +import { useConversationDetail } from '@/features/conversations/api/use-conversation-detail'; +import { ContactPanel } from '@/features/contacts/contact-panel'; +import { useWorkspaceStore } from '@/stores/use-workspace-store'; + +/** Replaces the Phase 4 minimal placeholder. Keyed by conversationId, so resolves contactId via + * the conversation-detail fetch before handing off to the shared ContactPanel. */ +export default function ConversationContactScreen() { + const { conversationId } = useLocalSearchParams<{ conversationId: string }>(); + const workspaceId = useWorkspaceStore((state) => state.currentWorkspaceId); + const { data: conversation, isLoading } = useConversationDetail( + workspaceId, + conversationId ?? null, + ); + + if (isLoading || !conversation) { + return ( + <Screen padded> + <Skeleton height={72} borderRadius={36} style={{ alignSelf: 'center' }} /> + </Screen> + ); + } + + return ( + <ContactPanel + workspaceId={workspaceId} + contactId={conversation.contactId} + origin="conversation" + channels={conversation.contactInboxes.map((inbox) => inbox.channel)} + conversationId={conversation.id} + /> + ); +} diff --git a/src/app/(app)/conversations/[conversationId]/index.tsx b/src/app/(app)/conversations/[conversationId]/index.tsx new file mode 100644 index 0000000..3ffe735 --- /dev/null +++ b/src/app/(app)/conversations/[conversationId]/index.tsx @@ -0,0 +1,228 @@ +import { useQueryClient } from '@tanstack/react-query'; +import { useFocusEffect, useLocalSearchParams, useRouter } from 'expo-router'; +import { useCallback, useEffect, useRef, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import type BottomSheet from '@gorhom/bottom-sheet'; +import type { LayoutChangeEvent } from 'react-native'; +import { KeyboardAvoidingView, Platform, Keyboard } from 'react-native'; +import { useSafeAreaInsets } from 'react-native-safe-area-context'; + +import { ConnectionBanner } from '@/components/ui/connection-banner'; +import { ErrorBanner } from '@/components/ui/error-banner'; +import { Screen } from '@/components/ui/screen'; +import { WorkspaceBlockedGate } from '@/components/workspace-blocked-gate'; +import { normalizeApiError } from '@/api/errors'; +import { describeApiError } from '@/lib/describe-api-error'; +import { useMarkConversationRead } from '@/features/conversations/api/use-conversation-actions'; +import { findConversationInListCache } from '@/features/conversations/lib/find-conversation-in-cache'; +import { isUnread } from '@/features/conversations/lib/conversation-status'; +import { ChatHeader } from '@/features/chat/components/chat-header'; +import { Composer } from '@/features/chat/components/composer'; +import { MessageActionsMenu } from '@/features/chat/components/message-actions-menu'; +import { MessageList, type MessageListHandle } from '@/features/chat/components/message-list'; +import { + flattenMessagePages, + useMessagesInfinite, + type Message, +} from '@/features/chat/api/use-messages-infinite'; +import { useDeleteMessage, useSetMessageAttributes } from '@/features/chat/api/use-message-actions'; +import { removeOptimisticMessage, useSendMessage } from '@/features/chat/api/use-send-message'; +import { useChatStore } from '@/features/chat/stores/use-chat-store'; +import { useConnectionBannerState } from '@/realtime/use-connection-store'; +import { queryKeys } from '@/api/query-keys'; +import { useWorkspaceStore } from '@/stores/use-workspace-store'; + +/** Sibling of `(app)/(tabs)`, not nested inside it — the tab bar is hidden for this screen + * (declared as a plain Stack.Screen in (app)/_layout.tsx, no Tabs wrapper). */ +export default function ChatScreen() { + const { t } = useTranslation(); + const { conversationId } = useLocalSearchParams<{ conversationId: string }>(); + const router = useRouter(); + const workspaceId = useWorkspaceStore((state) => state.currentWorkspaceId); + const queryClient = useQueryClient(); + const insets = useSafeAreaInsets(); + + const [selectedMessage, setSelectedMessage] = useState<Message | null>(null); + const [headerHeight, setHeaderHeight] = useState(0); + const [isKeyboardVisible, setIsKeyboardVisible] = useState(false); + const actionsMenuRef = useRef<BottomSheet>(null); + const messageListRef = useRef<MessageListHandle>(null); + + const messagesQuery = useMessagesInfinite(workspaceId, conversationId ?? null); + const messages = flattenMessagePages(messagesQuery.data?.pages); + const chronologicalMessages = [...messages].reverse(); + + const markRead = useMarkConversationRead(workspaceId ?? ''); + const sendMessage = useSendMessage(workspaceId ?? '', conversationId ?? ''); + // The mutation's own resolved value carries the clientId it generated internally (see + // use-send-message.ts's mutationFn return) — reading it here (rather than threading a second + // piece of state) is enough for MessageList to know "the last message that just landed was MY + // own send" and force-scroll to it even if the user had scrolled away. + const justSentMessageId = sendMessage.isSuccess ? (sendMessage.data?.clientId ?? null) : null; + const deleteMessage = useDeleteMessage(workspaceId ?? '', conversationId ?? ''); + const setAttributes = useSetMessageAttributes(workspaceId ?? '', conversationId ?? ''); + const setComposerMode = useChatStore((state) => state.setComposerMode); + const setDraft = useChatStore((state) => state.setDraft); + const setActiveConversationId = useChatStore((state) => state.setActiveConversationId); + const connectionState = useConnectionBannerState(); + + useEffect(() => { + const showEvent = Platform.OS === 'ios' ? 'keyboardWillShow' : 'keyboardDidShow'; + const hideEvent = Platform.OS === 'ios' ? 'keyboardWillHide' : 'keyboardDidHide'; + const showSub = Keyboard.addListener(showEvent, () => setIsKeyboardVisible(true)); + const hideSub = Keyboard.addListener(hideEvent, () => setIsKeyboardVisible(false)); + return () => { + showSub.remove(); + hideSub.remove(); + }; + }, []); + + useFocusEffect( + useCallback(() => { + if (!conversationId || !workspaceId) return; + const conversation = findConversationInListCache(queryClient, workspaceId, conversationId); + // Guard: previously this fired unconditionally on every focus, generating a network call + // even when the conversation was already read. Only mark-read when there's something to + // mark — and only when we actually know the unread state (a cache miss, e.g. a cold deep + // link with no list query loaded yet, is treated as "unknown, don't guess" rather than + // either always or never firing). + if (conversation && isUnread(conversation)) { + markRead.mutate(conversationId); + } + // eslint-disable-next-line react-hooks/exhaustive-deps -- mark-read is fire-and-forget on focus, re-running per markRead identity would loop. + }, [conversationId, workspaceId]), + ); + + // Tracks which conversation is currently on-screen so `src/lib/notifications.ts`'s foreground + // notification handler (called from outside React, via `useChatStore.getState()`) can suppress + // the in-app banner for a push about the conversation the user is already viewing. Cleared on + // blur/unmount rather than left set, so navigating away (or the app backgrounding) doesn't keep + // suppressing notifications for a conversation that's no longer visible. + useFocusEffect( + useCallback(() => { + if (!conversationId) return undefined; + setActiveConversationId(conversationId); + return () => setActiveConversationId(null); + }, [conversationId, setActiveConversationId]), + ); + + function handleRetry(message: Message) { + if (!message.clientId) return; + sendMessage.mutate({ + workspaceId: workspaceId ?? '', + conversationId: conversationId ?? '', + text: message.text ?? undefined, + clientId: message.clientId, + }); + } + + function handleLongPress(message: Message) { + setSelectedMessage(message); + actionsMenuRef.current?.expand(); + } + + function handleEditAndResend(message: Message) { + if (!workspaceId || !conversationId || !message.clientId) return; + removeOptimisticMessage( + queryClient, + queryKeys.ws.messages.list(workspaceId, conversationId), + message.clientId, + ); + setDraft(conversationId, message.text ?? ''); + actionsMenuRef.current?.close(); + } + + if (!workspaceId || !conversationId) { + return <Screen />; + } + + const normalizedError = messagesQuery.error ? normalizeApiError(messagesQuery.error) : null; + if (normalizedError?.kind === 'workspaceBlocked') { + return ( + <Screen edges={['top']}> + <WorkspaceBlockedGate reason={normalizedError.reason} message={normalizedError.message} /> + </Screen> + ); + } + + function handleHeaderLayout(event: LayoutChangeEvent) { + setHeaderHeight(event.nativeEvent.layout.height); + } + + return ( + <Screen edges={['top']}> + <ChatHeader + workspaceId={workspaceId} + conversationId={conversationId} + onBack={() => router.back()} + onLayout={handleHeaderLayout} + /> + <ConnectionBanner state={connectionState} /> + <KeyboardAvoidingView + style={{ flex: 1 }} + behavior={Platform.OS === 'ios' ? 'padding' : 'height'} + keyboardVerticalOffset={headerHeight + insets.top} + > + {messagesQuery.isError ? ( + <ErrorBanner + message={describeApiError(messagesQuery.error, t)} + actionLabel={t('common.retry')} + onAction={() => messagesQuery.refetch()} + /> + ) : null} + <MessageList + ref={messageListRef} + messages={chronologicalMessages} + isLoading={messagesQuery.isPending} + hasNextPage={Boolean(messagesQuery.hasNextPage)} + isFetchingNextPage={messagesQuery.isFetchingNextPage} + conversationId={conversationId} + justSentMessageId={justSentMessageId} + onLoadOlder={() => messagesQuery.fetchNextPage()} + onLongPressMessage={handleLongPress} + onRetryMessage={handleRetry} + /> + <Composer + workspaceId={workspaceId} + conversationId={conversationId} + style={isKeyboardVisible ? undefined : { paddingBottom: insets.bottom }} + /> + </KeyboardAvoidingView> + + <MessageActionsMenu + ref={actionsMenuRef} + message={selectedMessage} + onEdit={(message) => { + setComposerMode(conversationId, { type: 'edit', message }); + setDraft(conversationId, message.text ?? ''); + actionsMenuRef.current?.close(); + }} + onReply={(message) => { + setComposerMode(conversationId, { type: 'reply', message }); + actionsMenuRef.current?.close(); + }} + onDelete={(message) => { + deleteMessage.mutate({ messageId: message.id, createdAt: message.createdAt }); + actionsMenuRef.current?.close(); + }} + onLike={(message) => { + setAttributes.mutate({ + messageId: message.id, + createdAt: message.createdAt, + liked: true, + }); + actionsMenuRef.current?.close(); + }} + onHide={(message) => { + setAttributes.mutate({ + messageId: message.id, + createdAt: message.createdAt, + hidden: true, + }); + actionsMenuRef.current?.close(); + }} + onEditAndResend={handleEditAndResend} + /> + </Screen> + ); +} diff --git a/src/app/(app)/profile.tsx b/src/app/(app)/profile.tsx new file mode 100644 index 0000000..fa81a61 --- /dev/null +++ b/src/app/(app)/profile.tsx @@ -0,0 +1,56 @@ +import { useTranslation } from 'react-i18next'; +import { View } from 'react-native'; + +import { signOut as signOutRequest } from '@/api/auth-endpoints'; +import { clearAuthToken, getAuthToken } from '@/api/auth-token'; +import { Avatar } from '@/components/ui/avatar'; +import { ListItem } from '@/components/ui/list-item'; +import { Screen } from '@/components/ui/screen'; +import { SectionHeader } from '@/components/ui/section-header'; +import { Text } from '@/components/ui/text'; +import { queryClient } from '@/lib/query-client'; +import { useAuthStore } from '@/stores/use-auth-store'; +import { useWorkspaceStore } from '@/stores/use-workspace-store'; +import { useTheme } from '@/theme/use-theme'; + +/** Profile modal: identity + plan placeholder + sign-out. Dark-mode/language live on the settings + * tab (this modal doesn't duplicate them) since there's no plan/billing API yet to justify a + * separate "account" surface beyond identity. */ +export default function ProfileScreen() { + const { t } = useTranslation(); + const { spacing } = useTheme(); + const user = useAuthStore((state) => state.user); + const setSignedOut = useAuthStore((state) => state.setSignedOut); + const setCurrentWorkspaceId = useWorkspaceStore((state) => state.setCurrentWorkspaceId); + + async function handleSignOut() { + const token = await getAuthToken(); + if (token) { + await signOutRequest(token).catch(() => {}); + } + await clearAuthToken(); + queryClient.clear(); + setCurrentWorkspaceId(null); + setSignedOut(); + } + + return ( + <Screen padded> + <View style={{ alignItems: 'center', gap: spacing.sm, marginBottom: spacing.lg }}> + <Avatar name={user?.name ?? user?.email ?? '?'} size={72} /> + <Text variant="title">{user?.name ?? user?.email}</Text> + {user?.name ? ( + <Text variant="caption" color="secondary"> + {user.email} + </Text> + ) : null} + </View> + + <SectionHeader title={t('profile.plan')} /> + <ListItem title={t('profile.plan')} subtitle="—" /> + + <SectionHeader title="" /> + <ListItem title={t('auth.signOut')} onPress={handleSignOut} /> + </Screen> + ); +} diff --git a/src/app/(app)/settings/members.tsx b/src/app/(app)/settings/members.tsx new file mode 100644 index 0000000..3fbed57 --- /dev/null +++ b/src/app/(app)/settings/members.tsx @@ -0,0 +1,91 @@ +import { useTranslation } from 'react-i18next'; +import { ScrollView, View } from 'react-native'; + +import { describeApiError } from '@/lib/describe-api-error'; +import { Avatar } from '@/components/ui/avatar'; +import { Badge } from '@/components/ui/badge'; +import { EmptyState } from '@/components/ui/empty-state'; +import { ErrorBanner } from '@/components/ui/error-banner'; +import { ListItem } from '@/components/ui/list-item'; +import { Screen } from '@/components/ui/screen'; +import { SectionHeader } from '@/components/ui/section-header'; +import { Skeleton } from '@/components/ui/skeleton'; +import { useTeamsList } from '@/features/settings/api/use-teams'; +import { useWorkspaceMembersList } from '@/features/permissions/use-permissions'; +import { useWorkspaceStore } from '@/stores/use-workspace-store'; +import { useTheme } from '@/theme/use-theme'; + +/** Read-only members + teams screen (Phase 8). Teams is enterprise-tier and gracefully hides + * itself on any fetch error (see use-teams.ts) rather than surfacing a broken state for + * workspaces without the feature. */ +export default function MembersScreen() { + const { t } = useTranslation(); + const { spacing } = useTheme(); + const workspaceId = useWorkspaceStore((state) => state.currentWorkspaceId) ?? ''; + + const membersQuery = useWorkspaceMembersList(workspaceId); + const teamsQuery = useTeamsList(workspaceId); + + const members = membersQuery.data ?? []; + const teams = teamsQuery.data ?? []; + const showTeamsSection = !teamsQuery.isError && (teamsQuery.isPending || teams.length > 0); + + return ( + <Screen> + <ScrollView> + <SectionHeader title={t('settings.members')} /> + {membersQuery.isPending ? ( + <View style={{ paddingHorizontal: spacing.md, gap: spacing.sm }}> + <Skeleton height={56} /> + <Skeleton height={56} /> + <Skeleton height={56} /> + </View> + ) : membersQuery.isError ? ( + <View style={{ paddingHorizontal: spacing.md }}> + <ErrorBanner + message={describeApiError(membersQuery.error, t)} + actionLabel={t('common.retry')} + onAction={() => membersQuery.refetch()} + /> + </View> + ) : members.length === 0 ? ( + <EmptyState icon="people-outline" title={t('settings.noMembers')} /> + ) : ( + members.map((member) => ( + <ListItem + key={member.id} + title={member.user.name ?? t('contacts.unknown')} + leading={<Avatar uri={member.user.image} name={member.user.name ?? '?'} />} + trailing={ + <Badge> + {member.role === 'owner' ? t('settings.roleOwner') : t('settings.roleAgent')} + </Badge> + } + /> + )) + )} + + {showTeamsSection ? ( + <> + <SectionHeader title={t('settings.teams')} /> + {teamsQuery.isPending ? ( + <View style={{ paddingHorizontal: spacing.md, gap: spacing.sm }}> + <Skeleton height={56} /> + </View> + ) : teams.length === 0 ? ( + <EmptyState icon="people-circle-outline" title={t('settings.noTeams')} /> + ) : ( + teams.map((team) => ( + <ListItem + key={team.id} + title={team.name} + subtitle={`${team.inboxTeamMembers.length}`} + /> + )) + )} + </> + ) : null} + </ScrollView> + </Screen> + ); +} diff --git a/src/app/(app)/workspace-picker.tsx b/src/app/(app)/workspace-picker.tsx new file mode 100644 index 0000000..922fc84 --- /dev/null +++ b/src/app/(app)/workspace-picker.tsx @@ -0,0 +1,73 @@ +import { useTranslation } from 'react-i18next'; +import { FlatList, RefreshControl, View } from 'react-native'; + +import { describeApiError } from '@/lib/describe-api-error'; +import { Card } from '@/components/ui/surface'; +import { EmptyState } from '@/components/ui/empty-state'; +import { ErrorBanner } from '@/components/ui/error-banner'; +import { Screen } from '@/components/ui/screen'; +import { SkeletonRow } from '@/components/ui/skeleton'; +import { Text } from '@/components/ui/text'; +import { useSwitchWorkspace } from '@/features/workspaces/api/use-switch-workspace'; +import { useWorkspaces, type Workspace } from '@/features/workspaces/api/use-workspaces'; +import { WorkspaceRow } from '@/features/workspaces/workspace-row'; +import { useWorkspaceStore } from '@/stores/use-workspace-store'; +import { useTheme } from '@/theme/use-theme'; + +export default function WorkspacePickerScreen() { + const { t } = useTranslation(); + const { colors, spacing } = useTheme(); + const currentWorkspaceId = useWorkspaceStore((state) => state.currentWorkspaceId); + const { data: workspaces, isPending, isError, isRefetching, error, refetch } = useWorkspaces(); + const switchWorkspace = useSwitchWorkspace(); + + function handleSelect(workspace: Workspace) { + switchWorkspace(workspace.id, { navigate: true }); + } + + return ( + <Screen padded edges={['top']}> + <Text variant="display" style={{ marginBottom: spacing.md }}> + {t('workspaces.title')} + </Text> + + {isPending ? ( + <View style={{ gap: spacing.sm }}> + <SkeletonRow.Conversation /> + <SkeletonRow.Conversation /> + <SkeletonRow.Conversation /> + </View> + ) : isError ? ( + <ErrorBanner + message={describeApiError(error, t)} + actionLabel={t('common.retry')} + onAction={() => refetch()} + /> + ) : workspaces && workspaces.length > 0 ? ( + <FlatList + data={workspaces} + keyExtractor={(item) => item.id} + contentContainerStyle={{ gap: spacing.sm }} + renderItem={({ item }) => ( + <Card> + <WorkspaceRow + workspace={item} + selected={item.id === currentWorkspaceId} + onPress={handleSelect} + /> + </Card> + )} + refreshControl={ + <RefreshControl + refreshing={isRefetching} + onRefresh={() => refetch()} + tintColor={colors.brand} + /> + } + /> + ) : ( + <EmptyState icon="business-outline" title={t('workspaces.empty')} /> + )} + </Screen> + ); +} diff --git a/src/app/(auth)/_layout.tsx b/src/app/(auth)/_layout.tsx new file mode 100644 index 0000000..cadd5e9 --- /dev/null +++ b/src/app/(auth)/_layout.tsx @@ -0,0 +1,16 @@ +import { Redirect, Stack } from 'expo-router'; + +import { useAuthStore } from '@/stores/use-auth-store'; + +/** Auth route group: signed-in users never see these screens (redirect to `/`, whose own + * redirect logic sends them to the tabs shell or mustChangePassword as appropriate). */ +export default function AuthLayout() { + const status = useAuthStore((state) => state.status); + const mustChangePassword = useAuthStore((state) => state.user?.mustChangePassword ?? false); + + if (status === 'signed-in' && !mustChangePassword) { + return <Redirect href="/" />; + } + + return <Stack screenOptions={{ headerShown: false }} />; +} diff --git a/src/app/(auth)/change-password.tsx b/src/app/(auth)/change-password.tsx new file mode 100644 index 0000000..aae1143 --- /dev/null +++ b/src/app/(auth)/change-password.tsx @@ -0,0 +1,135 @@ +import { router } from 'expo-router'; +import { useRef, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import type { TextInput } from 'react-native'; +import { KeyboardAvoidingView, Platform, ScrollView, StyleSheet, View } from 'react-native'; + +import { changePassword } from '@/api/auth-endpoints'; +import { getAuthToken } from '@/api/auth-token'; +import { Button } from '@/components/ui/button'; +import { ErrorBanner } from '@/components/ui/error-banner'; +import { Screen } from '@/components/ui/screen'; +import { Text } from '@/components/ui/text'; +import { TextField } from '@/components/ui/text-field'; +import { useAuthStore } from '@/stores/use-auth-store'; +import { useTheme } from '@/theme/use-theme'; + +/** + * Forced password-change screen — reached when the session user has `mustChangePassword: true` + * (reseller-provisioned temporary password). Every other oRPC/session-authenticated call is + * blocked server-side until this completes (see aha.chat middlewares/auth.ts), so this screen + * has no way out except a successful change. + */ +export default function ChangePasswordScreen() { + const { t } = useTranslation(); + const { spacing } = useTheme(); + const setMustChangePassword = useAuthStore((state) => state.setMustChangePassword); + + const [currentPassword, setCurrentPassword] = useState(''); + const [newPassword, setNewPassword] = useState(''); + const [isCurrentPasswordVisible, setIsCurrentPasswordVisible] = useState(false); + const [isNewPasswordVisible, setIsNewPasswordVisible] = useState(false); + const [error, setError] = useState<string | null>(null); + const [isSubmitting, setIsSubmitting] = useState(false); + + const newPasswordInputRef = useRef<TextInput>(null); + + const handleSubmit = async () => { + setError(null); + setIsSubmitting(true); + try { + const token = await getAuthToken(); + if (!token) { + throw new Error(t('errors.unauthorized')); + } + await changePassword(token, currentPassword, newPassword); + setMustChangePassword(false); + router.replace('/'); + } catch (err) { + setError(err instanceof Error ? err.message : t('errors.unknown')); + } finally { + setIsSubmitting(false); + } + }; + + const canSubmit = currentPassword.length > 0 && newPassword.length >= 8 && !isSubmitting; + + return ( + <Screen> + <KeyboardAvoidingView + behavior={Platform.OS === 'ios' ? 'padding' : 'height'} + style={styles.flex} + > + <ScrollView + contentContainerStyle={[styles.scrollContent, { padding: spacing.lg }]} + keyboardShouldPersistTaps="handled" + > + <View style={[styles.form, { gap: spacing.md }]}> + <Text variant="heading">{t('auth.changePassword')}</Text> + <Text variant="body" color="secondary"> + {t('auth.changePasswordRequired')} + </Text> + + {error ? <ErrorBanner message={error} /> : null} + + <TextField + label={t('auth.currentPassword')} + value={currentPassword} + onChangeText={setCurrentPassword} + secureTextEntry={!isCurrentPasswordVisible} + autoCapitalize="none" + textContentType="password" + returnKeyType="next" + blurOnSubmit={false} + onSubmitEditing={() => newPasswordInputRef.current?.focus()} + trailingIcon={isCurrentPasswordVisible ? 'eye-off-outline' : 'eye-outline'} + trailingIconAccessibilityLabel={ + isCurrentPasswordVisible ? t('auth.hidePassword') : t('auth.showPassword') + } + onTrailingIconPress={() => setIsCurrentPasswordVisible((visible) => !visible)} + /> + <TextField + ref={newPasswordInputRef} + label={t('auth.newPassword')} + value={newPassword} + onChangeText={setNewPassword} + secureTextEntry={!isNewPasswordVisible} + autoCapitalize="none" + textContentType="newPassword" + returnKeyType="done" + blurOnSubmit + onSubmitEditing={handleSubmit} + trailingIcon={isNewPasswordVisible ? 'eye-off-outline' : 'eye-outline'} + trailingIconAccessibilityLabel={ + isNewPasswordVisible ? t('auth.hidePassword') : t('auth.showPassword') + } + onTrailingIconPress={() => setIsNewPasswordVisible((visible) => !visible)} + /> + + <Button + label={t('auth.changePassword')} + onPress={handleSubmit} + loading={isSubmitting} + disabled={!canSubmit} + size="lg" + fullWidth + /> + </View> + </ScrollView> + </KeyboardAvoidingView> + </Screen> + ); +} + +const styles = StyleSheet.create({ + flex: { + flex: 1, + }, + scrollContent: { + flexGrow: 1, + justifyContent: 'center', + }, + form: { + width: '100%', + }, +}); diff --git a/src/app/(auth)/forgot-password.tsx b/src/app/(auth)/forgot-password.tsx new file mode 100644 index 0000000..f075db3 --- /dev/null +++ b/src/app/(auth)/forgot-password.tsx @@ -0,0 +1,106 @@ +import { router } from 'expo-router'; +import { useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { KeyboardAvoidingView, Platform, ScrollView, StyleSheet, View } from 'react-native'; + +import { requestPasswordReset } from '@/api/auth-endpoints'; +import { Button } from '@/components/ui/button'; +import { EmptyState } from '@/components/ui/empty-state'; +import { ErrorBanner } from '@/components/ui/error-banner'; +import { Screen } from '@/components/ui/screen'; +import { Text } from '@/components/ui/text'; +import { TextField } from '@/components/ui/text-field'; +import { useTheme } from '@/theme/use-theme'; + +export default function ForgotPasswordScreen() { + const { t } = useTranslation(); + const { spacing } = useTheme(); + + const [email, setEmail] = useState(''); + const [error, setError] = useState<string | null>(null); + const [isSubmitting, setIsSubmitting] = useState(false); + const [isSent, setIsSent] = useState(false); + + const handleSubmit = async () => { + setError(null); + setIsSubmitting(true); + try { + await requestPasswordReset(email.trim()); + setIsSent(true); + } catch (err) { + setError(err instanceof Error ? err.message : t('errors.unknown')); + } finally { + setIsSubmitting(false); + } + }; + + if (isSent) { + return ( + <Screen padded> + <EmptyState + icon="checkmark-circle" + title={t('auth.forgotPassword')} + description={t('auth.resetLinkSent')} + action={{ label: t('auth.backToSignIn'), onPress: () => router.back() }} + /> + </Screen> + ); + } + + return ( + <Screen> + <KeyboardAvoidingView + behavior={Platform.OS === 'ios' ? 'padding' : 'height'} + style={styles.flex} + > + <ScrollView + contentContainerStyle={[styles.scrollContent, { padding: spacing.lg }]} + keyboardShouldPersistTaps="handled" + > + <View style={[styles.form, { gap: spacing.md }]}> + <Text variant="heading">{t('auth.forgotPassword')}</Text> + <Text variant="body" color="secondary"> + {t('auth.forgotPasswordSubtitle')} + </Text> + + {error ? <ErrorBanner message={error} /> : null} + + <TextField + label={t('auth.email')} + value={email} + onChangeText={setEmail} + autoCapitalize="none" + autoComplete="email" + keyboardType="email-address" + textContentType="emailAddress" + returnKeyType="go" + blurOnSubmit + onSubmitEditing={handleSubmit} + /> + <Button + label={t('auth.sendResetLink')} + onPress={handleSubmit} + loading={isSubmitting} + disabled={email.trim().length === 0 || isSubmitting} + size="lg" + fullWidth + /> + </View> + </ScrollView> + </KeyboardAvoidingView> + </Screen> + ); +} + +const styles = StyleSheet.create({ + flex: { + flex: 1, + }, + scrollContent: { + flexGrow: 1, + justifyContent: 'center', + }, + form: { + width: '100%', + }, +}); diff --git a/src/app/(auth)/sign-in.tsx b/src/app/(auth)/sign-in.tsx new file mode 100644 index 0000000..6c309f2 --- /dev/null +++ b/src/app/(auth)/sign-in.tsx @@ -0,0 +1,175 @@ +import { Link, router } from 'expo-router'; +import { useRef, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import type { TextInput } from 'react-native'; +import { KeyboardAvoidingView, Platform, ScrollView, StyleSheet, View } from 'react-native'; + +import { signInWithEmail } from '@/api/auth-endpoints'; +import { Button } from '@/components/ui/button'; +import { ErrorBanner } from '@/components/ui/error-banner'; +import { Icon } from '@/components/ui/icon'; +import { Screen } from '@/components/ui/screen'; +import { Card } from '@/components/ui/surface'; +import { Text } from '@/components/ui/text'; +import { TextField } from '@/components/ui/text-field'; +import { SocialButtons } from '@/features/auth/social-buttons'; +import { consumePendingDeepLink } from '@/lib/pending-deep-link'; +import { useAuthStore } from '@/stores/use-auth-store'; +import { useTheme } from '@/theme/use-theme'; + +const LOGO_SIZE = 72; + +export default function SignInScreen() { + const { t } = useTranslation(); + const { colors, spacing } = useTheme(); + const setSignedIn = useAuthStore((state) => state.setSignedIn); + + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const [isPasswordVisible, setIsPasswordVisible] = useState(false); + const [error, setError] = useState<string | null>(null); + const [isSubmitting, setIsSubmitting] = useState(false); + + const passwordInputRef = useRef<TextInput>(null); + + const handleSubmit = async () => { + setError(null); + setIsSubmitting(true); + try { + const user = await signInWithEmail(email.trim(), password); + setSignedIn(user); + // If the app was cold-started via a `/conversations/:id` deep link (e.g. a push + // notification, once delivery is wired up) while signed out, return there instead of the + // default post-auth redirect — see lib/pending-deep-link.ts. A mustChangePassword user is + // still routed to '/' regardless (its own guard traps them in change-password first). + const pendingDeepLink = user.mustChangePassword ? null : consumePendingDeepLink(); + router.replace((pendingDeepLink ?? '/') as never); + } catch (err) { + setError(err instanceof Error ? err.message : t('errors.unknown')); + } finally { + setIsSubmitting(false); + } + }; + + const canSubmit = email.trim().length > 0 && password.length > 0 && !isSubmitting; + + return ( + <Screen> + <KeyboardAvoidingView + behavior={Platform.OS === 'ios' ? 'padding' : 'height'} + style={styles.flex} + > + <ScrollView + contentContainerStyle={[styles.scrollContent, { padding: spacing.lg }]} + keyboardShouldPersistTaps="handled" + > + <View style={[styles.form, { gap: spacing.md }]}> + <View style={[styles.logoWrap, { marginBottom: spacing.sm }]}> + <View + style={[ + styles.logoCircle, + { backgroundColor: colors.brandSoft, borderRadius: LOGO_SIZE / 2 }, + ]} + > + <Icon name="chatbubbles" size={32} color={colors.brand} /> + </View> + </View> + + <Text variant="display" style={styles.centered}> + {t('auth.signIn')} + </Text> + <Text variant="body" color="secondary" style={styles.centered}> + {t('auth.signInSubtitle')} + </Text> + + {error ? <ErrorBanner message={error} /> : null} + + <TextField + label={t('auth.email')} + value={email} + onChangeText={setEmail} + autoCapitalize="none" + autoComplete="email" + keyboardType="email-address" + textContentType="emailAddress" + returnKeyType="next" + blurOnSubmit={false} + onSubmitEditing={() => passwordInputRef.current?.focus()} + /> + <TextField + ref={passwordInputRef} + label={t('auth.password')} + value={password} + onChangeText={setPassword} + secureTextEntry={!isPasswordVisible} + autoCapitalize="none" + autoComplete="password" + textContentType="password" + returnKeyType="go" + blurOnSubmit + onSubmitEditing={handleSubmit} + trailingIcon={isPasswordVisible ? 'eye-off-outline' : 'eye-outline'} + trailingIconAccessibilityLabel={ + isPasswordVisible ? t('auth.hidePassword') : t('auth.showPassword') + } + onTrailingIconPress={() => setIsPasswordVisible((visible) => !visible)} + /> + + <Button + label={t('auth.signIn')} + onPress={handleSubmit} + loading={isSubmitting} + disabled={!canSubmit} + size="lg" + fullWidth + /> + + <Link href="/(auth)/forgot-password" style={styles.link}> + <Text variant="callout" color="brand"> + {t('auth.forgotPassword')} + </Text> + </Link> + + <Card style={[styles.divider, { padding: spacing.sm }]}> + <Text variant="caption" color="secondary" style={styles.centered}> + {t('auth.orContinueWith')} + </Text> + </Card> + <SocialButtons /> + </View> + </ScrollView> + </KeyboardAvoidingView> + </Screen> + ); +} + +const styles = StyleSheet.create({ + flex: { + flex: 1, + }, + scrollContent: { + flexGrow: 1, + justifyContent: 'center', + }, + form: { + width: '100%', + }, + logoWrap: { + alignItems: 'center', + }, + logoCircle: { + width: LOGO_SIZE, + height: LOGO_SIZE, + alignItems: 'center', + justifyContent: 'center', + }, + link: { + alignSelf: 'center', + }, + divider: { + alignItems: 'center', + }, + centered: { + textAlign: 'center', + }, +}); diff --git a/src/app/(dev)/gallery.tsx b/src/app/(dev)/gallery.tsx new file mode 100644 index 0000000..92fd391 --- /dev/null +++ b/src/app/(dev)/gallery.tsx @@ -0,0 +1,435 @@ +import type BottomSheet from '@gorhom/bottom-sheet'; +import { Redirect } from 'expo-router'; +import type { ReactNode } from 'react'; +import { useRef, useState } from 'react'; +import { ScrollView, StyleSheet, View } from 'react-native'; + +import { Avatar } from '@/components/ui/avatar'; +import { Badge } from '@/components/ui/badge'; +import { Button } from '@/components/ui/button'; +import { Card, Surface } from '@/components/ui/surface'; +import { Chip } from '@/components/ui/chip'; +import { ConnectionBanner, type ConnectionState } from '@/components/ui/connection-banner'; +import { CountPill } from '@/components/ui/count-pill'; +import { DateSeparator } from '@/components/ui/date-separator'; +import { Divider } from '@/components/ui/divider'; +import { EmptyState } from '@/components/ui/empty-state'; +import { ErrorBanner } from '@/components/ui/error-banner'; +import { IconButton } from '@/components/ui/icon-button'; +import { ListFooterSpinner } from '@/components/ui/list-footer-spinner'; +import { ListItem } from '@/components/ui/list-item'; +import { Screen } from '@/components/ui/screen'; +import { ScreenHeader } from '@/components/ui/screen-header'; +import { SearchBar } from '@/components/ui/search-bar'; +import { SegmentedTabs } from '@/components/ui/segmented-tabs'; +import { Sheet, SheetHeader } from '@/components/ui/sheet'; +import { Skeleton, SkeletonRow } from '@/components/ui/skeleton'; +import { Text } from '@/components/ui/text'; +import { ToggleRow } from '@/components/ui/toggle-row'; +import { useToast } from '@/components/ui/toast'; +import { useSettingsStore } from '@/stores/use-settings-store'; +import { useTheme } from '@/theme/use-theme'; + +/** + * Visual smoke-test surface for every Phase 2 UI primitive, in various states, with a light/dark + * toggle. Not a polished screen — the "storybook-lite" the plan asks for. Dev-build only: renders + * a redirect in production builds instead of registering a no-op empty screen, so the route + * still resolves (typed-routes stays happy) but is inert outside `__DEV__`. + */ +export default function GalleryScreen() { + if (!__DEV__) { + return <Redirect href="/(app)/(tabs)/conversations" />; + } + + return <GalleryContent />; +} + +function GalleryContent() { + const { colors, spacing } = useTheme(); + const themePreference = useSettingsStore((state) => state.themePreference); + const setThemePreference = useSettingsStore((state) => state.setThemePreference); + const showToast = useToast(); + + const [search, setSearch] = useState(''); + const [tab, setTab] = useState<'all' | 'unread' | 'bot'>('all'); + const [chipSelected, setChipSelected] = useState(true); + const [toggleValue, setToggleValue] = useState(true); + const [connectionState, setConnectionState] = useState<ConnectionState>('offline'); + const sheetRef = useRef<BottomSheet>(null); + // Lazy-init so `Date.now()` runs once (at first render) instead of being called fresh on every + // render, which the react-compiler lint rule flags as an impure render-time call. + const [{ todayIso, yesterdayIso }] = useState(() => { + const now = Date.now(); + return { + todayIso: new Date(now).toISOString(), + yesterdayIso: new Date(now - 86400000).toISOString(), + }; + }); + + function toggleScheme() { + setThemePreference(themePreference === 'dark' ? 'light' : 'dark'); + } + + return ( + <Screen edges={['top', 'bottom']}> + <ScrollView contentContainerStyle={{ padding: spacing.md, gap: spacing.lg }}> + <ScreenHeader + title="UI Gallery" + subtitle="Dev-only primitive smoke test" + trailing={ + <IconButton + accessibilityLabel="Toggle color scheme" + icon={themePreference === 'dark' ? 'sunny-outline' : 'moon-outline'} + variant="tonal" + onPress={toggleScheme} + /> + } + /> + + <Section title="Text"> + <Text variant="display">Display</Text> + <Text variant="heading">Heading</Text> + <Text variant="title">Title</Text> + <Text variant="subtitle">Subtitle</Text> + <Text variant="body">Body text</Text> + <Text variant="bodyStrong">Body strong</Text> + <Text variant="callout">Callout</Text> + <Text variant="caption" color="secondary"> + Caption secondary + </Text> + <Text variant="label">Label</Text> + <Text variant="micro" numeric> + 123,456 + </Text> + </Section> + + <Section title="Buttons"> + <View style={styles.row}> + <Button label="Primary" onPress={() => {}} /> + <Button label="Tonal" variant="tonal" onPress={() => {}} /> + <Button label="Secondary" variant="secondary" onPress={() => {}} /> + </View> + <View style={styles.row}> + <Button label="Danger" variant="danger" onPress={() => {}} /> + <Button label="Ghost" variant="ghost" onPress={() => {}} /> + <Button label="Icon" icon="send" onPress={() => {}} /> + </View> + <View style={styles.row}> + <Button label="Small" size="sm" onPress={() => {}} /> + <Button label="Loading" loading onPress={() => {}} /> + <Button label="Disabled" disabled onPress={() => {}} /> + </View> + </Section> + + <Section title="IconButton"> + <View style={styles.row}> + <IconButton + accessibilityLabel="Ghost" + icon="heart-outline" + variant="ghost" + onPress={() => {}} + /> + <IconButton + accessibilityLabel="Tonal" + icon="heart-outline" + variant="tonal" + onPress={() => {}} + /> + <IconButton + accessibilityLabel="Filled" + icon="heart-outline" + variant="filled" + onPress={() => {}} + /> + <IconButton + accessibilityLabel="Outline" + icon="heart-outline" + variant="outline" + onPress={() => {}} + /> + <IconButton + accessibilityLabel="Badged" + icon="notifications-outline" + badgeCount={5} + onPress={() => {}} + /> + <IconButton accessibilityLabel="Loading" icon="send" loading onPress={() => {}} /> + </View> + </Section> + + <Section title="Avatar"> + <View style={styles.row}> + <Avatar name="Ada Lovelace" size={40} /> + <Avatar name="Grace Hopper" size={52} ring="online" /> + <Avatar name="Bot Assistant" size={52} ring="bot" /> + <Avatar name="Margaret Hamilton" size={52} badge={<Badge tone="success" count={3} />} /> + </View> + </Section> + + <Section title="Chip"> + <View style={styles.row}> + <Chip label="All" selected={tab === 'all'} onPress={() => setTab('all')} /> + <Chip label="Unread" selected={tab === 'unread'} onPress={() => setTab('unread')} /> + <Chip + label="Bot" + tone="custom" + color={colors.bubbleBotAccent} + selected={chipSelected} + onPress={() => setChipSelected((prev) => !prev)} + /> + <Chip label="VIP" onRemove={() => {}} /> + </View> + </Section> + + <Section title="Badge / CountPill"> + <View style={styles.row}> + <Badge tone="brand">New</Badge> + <Badge tone="danger" count={12} /> + <CountPill count={4} /> + <CountPill count={230} tone="danger" /> + </View> + </Section> + + <Section title="Surface / Card"> + <Surface level={1} radius="md" padding="md" bordered> + <Text variant="body">Surface level 1</Text> + </Surface> + <Card padding="md" elevation={2}> + <Text variant="body">Card (level 1, bordered, elevation 2)</Text> + </Card> + </Section> + + <Section title="SearchBar / SegmentedTabs"> + <SearchBar value={search} onChangeText={setSearch} placeholder="Search…" /> + <SegmentedTabs + options={[ + { value: 'all', label: 'All' }, + { value: 'unread', label: 'Unread' }, + { value: 'bot', label: 'Bot' }, + ]} + value={tab} + onChange={setTab} + /> + </Section> + + <Section title="ListItem"> + <Card padding="xxs"> + <ListItem + title="Standard row" + subtitle="With a subtitle" + showChevron + onPress={() => {}} + /> + <Divider inset /> + <ListItem title="Row with value" value="Enabled" onPress={() => {}} /> + <Divider inset /> + <ListItem title="Destructive row" destructive onPress={() => {}} /> + <Divider inset /> + <ListItem title="Disabled row" disabled onPress={() => {}} /> + </Card> + </Section> + + <Section title="ToggleRow"> + <Card padding="xxs"> + <ToggleRow + label="Push notifications" + description="Get notified about new messages" + value={toggleValue} + onValueChange={setToggleValue} + /> + </Card> + </Section> + + <Section title="ErrorBanner"> + <ErrorBanner + tone="danger" + message="Failed to send message." + actionLabel="Retry" + onAction={() => {}} + /> + <ErrorBanner tone="warning" message="This workspace is near its plan limit." /> + <ErrorBanner tone="info" message="You're viewing a read-only flow." /> + </Section> + + <Section title="EmptyState"> + <View style={{ height: 220 }}> + <EmptyState + icon="chatbubble-ellipses-outline" + title="No conversations" + description="New conversations will show up here." + action={{ label: 'Clear filters', onPress: () => {} }} + /> + </View> + </Section> + + <Section title="Skeleton"> + <SkeletonRow.Conversation /> + <SkeletonRow.Contact /> + <SkeletonRow.Message align="start" /> + <SkeletonRow.Message align="end" /> + <Skeleton width="80%" height={14} /> + </Section> + + <Section title="ListFooterSpinner"> + <Card padding="xxs"> + <ListFooterSpinner visible /> + </Card> + </Section> + + <Section title="DateSeparator"> + <DateSeparator date={todayIso} /> + <DateSeparator date={yesterdayIso} /> + </Section> + + <Section title="ConnectionBanner"> + <ConnectionBanner state={connectionState} /> + <View style={styles.row}> + {(['online', 'connecting', 'reconnecting', 'offline'] as ConnectionState[]).map( + (state) => ( + <Button + key={state} + label={state} + size="sm" + variant="secondary" + onPress={() => setConnectionState(state)} + /> + ), + )} + </View> + </Section> + + <Section title="Toast"> + <Button + label="Show toast" + variant="tonal" + onPress={() => + showToast({ + message: 'Message sent', + tone: 'success', + action: { label: 'Undo', onPress: () => {} }, + }) + } + /> + </Section> + + <Section title="Sheet"> + <Button + label="Open sheet" + variant="secondary" + onPress={() => sheetRef.current?.expand()} + /> + </Section> + + <Section title="Fake conversation row"> + <Card padding="md"> + <View style={[styles.row, { alignItems: 'flex-start', gap: spacing.sm }]}> + <Avatar name="Jane Doe" size={52} ring="online" /> + <View style={{ flex: 1, gap: 4 }}> + <View style={[styles.row, { justifyContent: 'space-between' }]}> + <Text variant="bodyStrong" numberOfLines={1}> + Jane Doe + </Text> + <Text variant="micro" color="tertiary" numeric> + 2m + </Text> + </View> + <Text variant="callout" color="secondary" numberOfLines={1}> + Thanks for the quick reply! Let me check with the team. + </Text> + <View style={styles.row}> + <Chip + label="Bot handling" + size="sm" + tone="custom" + color={colors.bubbleBotAccent} + /> + <CountPill count={2} /> + </View> + </View> + </View> + </Card> + </Section> + + <Section title="Fake message bubble group"> + <View style={{ gap: 4 }}> + <View style={[styles.row, { justifyContent: 'flex-start' }]}> + <View + style={{ + backgroundColor: colors.bubbleIn, + borderRadius: 18, + paddingHorizontal: 14, + paddingVertical: 10, + maxWidth: '75%', + }} + > + <Text variant="body" style={{ color: colors.bubbleInText }}> + Hey, is my order still on track? + </Text> + </View> + </View> + <View style={[styles.row, { justifyContent: 'flex-end' }]}> + <View + style={{ + backgroundColor: colors.bubbleOut, + borderRadius: 18, + paddingHorizontal: 14, + paddingVertical: 10, + maxWidth: '75%', + }} + > + <Text variant="body" style={{ color: colors.bubbleOutText }}> + Yes! It shipped this morning. + </Text> + </View> + </View> + <View style={[styles.row, { justifyContent: 'flex-start' }]}> + <View + style={{ + backgroundColor: colors.bubbleBot, + borderRadius: 18, + paddingHorizontal: 14, + paddingVertical: 10, + maxWidth: '75%', + }} + > + <Text variant="body" style={{ color: colors.bubbleBotText }}> + Automated: your tracking link has been sent by email. + </Text> + </View> + </View> + </View> + </Section> + </ScrollView> + + <Sheet ref={sheetRef} snapPoints={['40%']}> + <SheetHeader title="Example sheet" onClose={() => sheetRef.current?.close()} /> + <View style={{ padding: spacing.md }}> + <Text variant="body" color="secondary"> + This is a Sheet body, rendered via BottomSheetView. + </Text> + </View> + </Sheet> + </Screen> + ); +} + +function Section({ title, children }: { title: string; children: ReactNode }) { + const { spacing } = useTheme(); + return ( + <View style={{ gap: spacing.sm }}> + <Text variant="label" color="tertiary"> + {title} + </Text> + <View style={{ gap: spacing.sm }}>{children}</View> + </View> + ); +} + +const styles = StyleSheet.create({ + row: { + flexDirection: 'row', + alignItems: 'center', + flexWrap: 'wrap', + gap: 8, + }, +}); diff --git a/src/app/+not-found.tsx b/src/app/+not-found.tsx new file mode 100644 index 0000000..bdd741d --- /dev/null +++ b/src/app/+not-found.tsx @@ -0,0 +1,30 @@ +import { router } from 'expo-router'; +import { useTranslation } from 'react-i18next'; + +import { Button } from '@/components/ui/button'; +import { EmptyState } from '@/components/ui/empty-state'; +import { Screen } from '@/components/ui/screen'; +import { useAuthStore } from '@/stores/use-auth-store'; + +/** Catch-all for unmatched routes (bad deep link, stale link, typo'd path). Routes back to the + * conversations tab if signed in, or sign-in otherwise — mirrors index.tsx's own redirect logic + * rather than assuming an authed session. */ +export default function NotFoundScreen() { + const { t } = useTranslation(); + const status = useAuthStore((state) => state.status); + + function handleGoHome() { + router.replace(status === 'signed-in' ? '/(app)/(tabs)/conversations' : '/(auth)/sign-in'); + } + + return ( + <Screen padded> + <EmptyState + icon="compass-outline" + title={t('errors.notFoundTitle')} + description={t('errors.notFoundBody')} + /> + <Button label={t('errors.goHome')} onPress={handleGoHome} /> + </Screen> + ); +} diff --git a/src/app/_layout.tsx b/src/app/_layout.tsx new file mode 100644 index 0000000..3d28444 --- /dev/null +++ b/src/app/_layout.tsx @@ -0,0 +1,188 @@ +import '@/lib/install-crypto-polyfill'; + +import { BottomSheetModalProvider } from '@gorhom/bottom-sheet'; +import { QueryClientProvider } from '@tanstack/react-query'; +import { + PlusJakartaSans_600SemiBold, + PlusJakartaSans_700Bold, + useFonts, +} from '@expo-google-fonts/plus-jakarta-sans'; +import { Stack, ThemeProvider } from 'expo-router'; +import * as SplashScreen from 'expo-splash-screen'; +import { StatusBar } from 'expo-status-bar'; +import * as SystemUI from 'expo-system-ui'; +import { useEffect, useState } from 'react'; +import { I18nManager } from 'react-native'; +import { GestureHandlerRootView } from 'react-native-gesture-handler'; + +import { getSession } from '@/api/auth-endpoints'; +import { clearAuthToken, getAuthToken } from '@/api/auth-token'; +import { ToastProvider } from '@/components/ui/toast'; +import { initI18n } from '@/i18n'; +import { applyLanguage } from '@/i18n/apply-language'; +import { reconcileRTLOnLaunch } from '@/i18n/reconcile-rtl'; +import { queryClient } from '@/lib/query-client'; +import { capturePendingDeepLink } from '@/lib/pending-deep-link'; +// Importing this (rather than only from Settings, lazily, as before) registers +// `Notifications.setNotificationHandler` unconditionally at app start — see notifications.ts. +import { ensureAndroidNotificationChannel } from '@/lib/notifications'; +import { initNotificationTapHandling } from '@/lib/notification-tap'; +import { useAuthStore } from '@/stores/use-auth-store'; +import { useSettingsStore, waitForSettingsHydration } from '@/stores/use-settings-store'; +import { getNavigationTheme } from '@/theme/navigation-theme'; +import { useResolvedScheme, useTheme } from '@/theme/use-theme'; + +SplashScreen.preventAutoHideAsync(); + +// i18next resources are static/local (bundled JSON, no network) and init() is synchronous, so +// this runs once at module load — before the first render — rather than as a render-triggered +// effect. That avoids the extra render pass a `useState` + `useEffect` combo would cause. +initI18n(); +// Allows the OS/dev-build shell to lay out RTL before any native view mounts; actually flipping +// direction still requires `forceRTL` + a reload — see reconcile-rtl.ts. +I18nManager.allowRTL(true); +// Must run before the auth guard's first redirect can fire — see pending-deep-link.ts. +capturePendingDeepLink(); + +/** + * Bootstrap gate: waits for the persisted settings store to hydrate, then applies the persisted + * language to i18next/dayjs and reconciles native RTL direction against it — before the splash + * screen can hide. Module-load `initI18n()` above already picked a device-resolved language + * synchronously; this corrects it to the user's actual saved preference once hydration completes. + */ +function useLanguageBootstrap(): boolean { + const [ready, setReady] = useState(false); + + useEffect(() => { + let cancelled = false; + + async function bootstrap() { + await waitForSettingsHydration(); + const language = useSettingsStore.getState().language; + await applyLanguage(language); + await reconcileRTLOnLaunch(language); + } + + bootstrap().finally(() => { + if (!cancelled) setReady(true); + }); + + return () => { + cancelled = true; + }; + }, []); + + return ready; +} + +/** + * Bootstrap gate: resolves the auth store's 'pending' status to 'signed-in'/'signed-out' by + * checking for a stored token and validating it against `getSession`, then hides the splash + * screen. Runs once at root-layout mount — every route's own guard reads `status` off the store + * rather than re-checking the session itself. + */ +function useAuthBootstrap(): boolean { + const [ready, setReady] = useState(false); + const setSignedIn = useAuthStore((state) => state.setSignedIn); + const setSignedOut = useAuthStore((state) => state.setSignedOut); + + useEffect(() => { + let cancelled = false; + + async function bootstrap() { + const token = await getAuthToken(); + if (!token) { + if (!cancelled) setSignedOut(); + return; + } + + const user = await getSession(token); + if (cancelled) return; + + if (user) { + setSignedIn(user); + } else { + await clearAuthToken(); + setSignedOut(); + } + } + + bootstrap().finally(() => { + if (!cancelled) setReady(true); + }); + + return () => { + cancelled = true; + }; + // eslint-disable-next-line react-hooks/exhaustive-deps -- runs once at mount; store setters are stable. + }, []); + + return ready; +} + +/** + * Provider stack (outer → inner): gesture handler root (required once at the app root by + * react-native-gesture-handler) > QueryClientProvider > BottomSheetModalProvider (so any screen + * can open a modal bottom sheet without its own provider) > ThemeProvider (our tokens, adapted for + * react-navigation via navigation-theme.ts). The auth bootstrap gate holds the splash screen until + * session status resolves, before any route's own guard runs. + */ +export default function RootLayout() { + const scheme = useResolvedScheme(); + const { colors } = useTheme(); + const authReady = useAuthBootstrap(); + const languageReady = useLanguageBootstrap(); + const [fontsLoaded] = useFonts({ + PlusJakartaSans_600SemiBold, + PlusJakartaSans_700Bold, + }); + + useEffect(() => { + if (authReady && languageReady && fontsLoaded) { + SplashScreen.hideAsync(); + } + }, [authReady, languageReady, fontsLoaded]); + + // Registers the Android notification channel (idempotent — safe to call again even though + // getPushTokenAsync also calls this lazily) and wires notification-tap → navigation, once auth + // bootstrap has resolved. `initNotificationTapHandling` itself no-ops the tap navigation when + // `useAuthStore` isn't 'signed-in' (see notification-tap.ts), so gating on `authReady` here is + // about giving `getLastNotificationResponseAsync` a resolved auth state to check against for the + // cold-start case, not a hard requirement for the listener registration itself. + useEffect(() => { + if (!authReady) return undefined; + ensureAndroidNotificationChannel(); + return initNotificationTapHandling(); + }, [authReady]); + + // Android only — iOS has no themable window/root background to set here, and + // `expo-system-ui` no-ops harmlessly on iOS/web. + useEffect(() => { + SystemUI.setBackgroundColorAsync(colors.bg); + }, [colors.bg]); + + return ( + <GestureHandlerRootView style={{ flex: 1 }}> + <QueryClientProvider client={queryClient}> + <BottomSheetModalProvider> + <ThemeProvider value={getNavigationTheme(scheme)}> + <ToastProvider> + <StatusBar style={scheme === 'dark' ? 'light' : 'dark'} /> + <Stack screenOptions={{ headerShown: false }}> + <Stack.Screen name="camera" options={{ presentation: 'fullScreenModal' }} /> + <Stack.Screen + name="image-viewer" + options={{ + presentation: 'fullScreenModal', + animation: 'fade', + contentStyle: { backgroundColor: 'transparent' }, + }} + /> + </Stack> + </ToastProvider> + </ThemeProvider> + </BottomSheetModalProvider> + </QueryClientProvider> + </GestureHandlerRootView> + ); +} diff --git a/src/app/camera.tsx b/src/app/camera.tsx new file mode 100644 index 0000000..3e5438b --- /dev/null +++ b/src/app/camera.tsx @@ -0,0 +1,165 @@ +import { CameraView, useCameraPermissions } from 'expo-camera'; +import { File } from 'expo-file-system'; +import { router, useLocalSearchParams } from 'expo-router'; +import { StatusBar } from 'expo-status-bar'; +import { useRef, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Pressable, StyleSheet, View } from 'react-native'; +import { useSafeAreaInsets } from 'react-native-safe-area-context'; + +import { Button } from '@/components/ui/button'; +import { Icon } from '@/components/ui/icon'; +import { Screen } from '@/components/ui/screen'; +import { Text } from '@/components/ui/text'; +import { + AttachmentTooLargeError, + MAX_FILE_SIZE_BYTES, + useSendMessage, +} from '@/features/chat/api/use-send-message'; +import { useWorkspaceStore } from '@/stores/use-workspace-store'; +import { useTheme } from '@/theme/use-theme'; + +/** Root-level route (outside the (app) group, per the plan's route tree) with a fullScreenModal + * presentation declared on its Stack.Screen in src/app/_layout.tsx. Captures a photo and sends it + * directly as an attachment message on the given conversation, rather than returning to the + * composer for a second step. */ +export default function CameraScreen() { + const { t } = useTranslation(); + const { conversationId } = useLocalSearchParams<{ conversationId: string }>(); + const workspaceId = useWorkspaceStore((state) => state.currentWorkspaceId); + const { colors, spacing } = useTheme(); + const insets = useSafeAreaInsets(); + const [permission, requestPermission] = useCameraPermissions(); + const cameraRef = useRef<CameraView>(null); + const [isCapturing, setIsCapturing] = useState(false); + const [error, setError] = useState<string | null>(null); + const sendMessage = useSendMessage(workspaceId ?? '', conversationId ?? ''); + + if (!permission) { + return <Screen />; + } + + if (!permission.granted) { + return ( + <Screen padded style={styles.centered}> + <Text variant="body" style={styles.centered}> + {t('chat.grantCameraPermission')} + </Text> + <Button label={t('chat.grantPermission')} onPress={requestPermission} /> + </Screen> + ); + } + + async function handleCapture() { + if (!cameraRef.current || !workspaceId || !conversationId) return; + setIsCapturing(true); + setError(null); + + try { + const photo = await cameraRef.current.takePictureAsync({ quality: 0.85 }); + if (!photo?.uri) return; + + // SDK 57 replaced the legacy `getInfoAsync` API with a class-based `File` — `new + // File(uri).size` is the current way to stat a local file (see + // https://docs.expo.dev/versions/v57.0.0/sdk/filesystem/). Guard BEFORE attempting to send, + // per the plan — previously this screen relied entirely on the backend's own size rejection + // with no client-side check at all. + const fileSize = new File(photo.uri).size; + if (typeof fileSize === 'number' && fileSize > MAX_FILE_SIZE_BYTES) { + setError(t('chat.attachmentTooLarge', { fileName: 'photo.jpg' })); + return; + } + + await sendMessage.mutateAsync({ + workspaceId, + conversationId, + attachments: [ + { uri: photo.uri, mimeType: 'image/jpeg', fileName: `photo-${Date.now()}.jpg` }, + ], + }); + router.back(); + } catch (err) { + setError(err instanceof AttachmentTooLargeError ? err.message : t('chat.failedToSendPhoto')); + } finally { + setIsCapturing(false); + } + } + + return ( + <View style={styles.container}> + <StatusBar style="light" /> + <CameraView ref={cameraRef} style={styles.camera} facing="back" /> + <View style={[styles.controls, { padding: spacing.lg }]}> + <Pressable + accessibilityRole="button" + accessibilityLabel={t('common.close', { defaultValue: 'Close' })} + onPress={() => router.back()} + style={styles.closeButton} + > + <Icon name="close" size={28} color={colors.textInverse} /> + </Pressable> + <Pressable + accessibilityRole="button" + accessibilityLabel={t('chat.camera')} + onPress={handleCapture} + disabled={isCapturing} + style={[styles.captureButton, { borderColor: colors.textInverse }]} + /> + </View> + {error ? ( + <View + style={[ + styles.errorBanner, + { backgroundColor: colors.danger, top: insets.top + spacing.lg }, + ]} + > + <Text style={{ color: colors.textInverse }}>{error}</Text> + </View> + ) : null} + </View> + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: '#000000', + }, + camera: { + flex: 1, + }, + controls: { + position: 'absolute', + bottom: 0, + left: 0, + right: 0, + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + }, + closeButton: { + padding: 8, + }, + captureButton: { + width: 68, + height: 68, + borderRadius: 34, + borderWidth: 4, + backgroundColor: '#ffffff', + alignSelf: 'center', + marginHorizontal: 'auto', + }, + errorBanner: { + position: 'absolute', + left: 16, + right: 16, + padding: 12, + borderRadius: 8, + }, + centered: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + gap: 12, + }, +}); diff --git a/src/app/image-viewer.tsx b/src/app/image-viewer.tsx new file mode 100644 index 0000000..0083bfa --- /dev/null +++ b/src/app/image-viewer.tsx @@ -0,0 +1,50 @@ +import { Image } from 'expo-image'; +import { router, useLocalSearchParams } from 'expo-router'; +import { StatusBar } from 'expo-status-bar'; +import { Pressable, StyleSheet, View } from 'react-native'; +import { useSafeAreaInsets } from 'react-native-safe-area-context'; + +import { Icon } from '@/components/ui/icon'; +import { withAlpha } from '@/theme/color-utils'; +import { useTheme } from '@/theme/use-theme'; + +/** Root-level route, transparent fullScreenModal presentation (declared on its Stack.Screen in + * src/app/_layout.tsx). Simple full-screen image + close button — pinch-zoom is skipped, it would + * need a gesture-handler-driven zoom wrapper that's not worth the complexity for this phase. + * Background is a near-opaque scrim over black (not `colors.scrim`, which is tuned at 0.5 alpha + * for overlays atop app content — this route wants the image to read as the sole subject, closer + * to opaque) so it stays consistent regardless of the active theme scheme. */ +export default function ImageViewerScreen() { + const { url } = useLocalSearchParams<{ url: string }>(); + const { colors } = useTheme(); + const insets = useSafeAreaInsets(); + + return ( + <View style={[styles.container, { backgroundColor: withAlpha('#000000', 0.92) }]}> + <StatusBar style="light" /> + <Pressable + accessibilityRole="button" + onPress={() => router.back()} + style={[styles.closeButton, { top: insets.top + 8 }]} + > + <Icon name="close" size={28} color={colors.textInverse} /> + </Pressable> + {url ? <Image source={{ uri: url }} style={styles.image} contentFit="contain" /> : null} + </View> + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + }, + image: { + flex: 1, + }, + closeButton: { + position: 'absolute', + end: 16, + zIndex: 1, + padding: 8, + }, +}); diff --git a/src/app/index.tsx b/src/app/index.tsx new file mode 100644 index 0000000..9ab7632 --- /dev/null +++ b/src/app/index.tsx @@ -0,0 +1,44 @@ +import { Redirect } from 'expo-router'; +import { ActivityIndicator } from 'react-native'; + +import { Screen } from '@/components/ui/screen'; +import { useAuthStore } from '@/stores/use-auth-store'; +import { useWorkspaceStore } from '@/stores/use-workspace-store'; +import { useTheme } from '@/theme/use-theme'; + +/** + * Root redirect gate: !authed → sign-in; mustChangePassword → change-password; no workspace → + * workspace-picker; else → tabs shell. + */ +export default function IndexRedirect() { + const status = useAuthStore((state) => state.status); + const mustChangePassword = useAuthStore((state) => state.user?.mustChangePassword ?? false); + const currentWorkspaceId = useWorkspaceStore((state) => state.currentWorkspaceId); + + if (status === 'pending') { + return <LoadingScreen />; + } + + if (status === 'signed-out') { + return <Redirect href="/(auth)/sign-in" />; + } + + if (mustChangePassword) { + return <Redirect href="/(auth)/change-password" />; + } + + if (!currentWorkspaceId) { + return <Redirect href="/(app)/workspace-picker" />; + } + + return <Redirect href="/(app)/(tabs)/conversations" />; +} + +function LoadingScreen() { + const { colors } = useTheme(); + return ( + <Screen> + <ActivityIndicator color={colors.primary} style={{ flex: 1 }} /> + </Screen> + ); +} diff --git a/src/components/ui/avatar.tsx b/src/components/ui/avatar.tsx new file mode 100644 index 0000000..fd7b3a6 --- /dev/null +++ b/src/components/ui/avatar.tsx @@ -0,0 +1,111 @@ +import { Image } from 'expo-image'; +import type { ReactNode } from 'react'; +import { StyleSheet, View } from 'react-native'; + +import { withAlpha } from '@/theme/color-utils'; +import { useTheme } from '@/theme/use-theme'; + +import { Text } from './text'; + +type Ring = 'none' | 'online' | 'bot'; + +interface AvatarProps { + uri?: string | null; + name: string; + size?: number; + /** Slot anchored bottom-end (channel icon, status dot). Absolutely positioned over the avatar. */ + badge?: ReactNode; + /** Colored ring around the avatar: 'online' (success) or 'bot' (violet accent). */ + ring?: Ring; +} + +function initialsFor(name: string): string { + const parts = name.trim().split(/\s+/).filter(Boolean); + if (parts.length === 0) return '?'; + if (parts.length === 1) return parts[0]!.slice(0, 2).toUpperCase(); + return `${parts[0]![0]}${parts[parts.length - 1]![0]}`.toUpperCase(); +} + +/** Deterministic hash of a string into a small non-negative int — used to pick a stable + * avatar-palette index per name/id, so the same contact always gets the same fallback tint. */ +function hashString(value: string): number { + let hash = 0; + for (let i = 0; i < value.length; i += 1) { + hash = (hash << 5) - hash + value.charCodeAt(i); + hash |= 0; // deliberate 32-bit hash mixing + } + return Math.abs(hash); +} + +export function Avatar({ uri, name, size = 40, badge, ring = 'none' }: AvatarProps) { + const { colors } = useTheme(); + const dimensionStyle = { width: size, height: size, borderRadius: size / 2 }; + const ringColor = + ring === 'online' ? colors.success : ring === 'bot' ? colors.bubbleBotAccent : undefined; + const ringWidth = size < 32 ? 1.5 : 2; + + const paletteIndex = hashString(name) % colors.avatarPalette.length; + const fallbackColor = colors.avatarPalette[paletteIndex]!; + + const core = uri ? ( + <Image + source={{ uri }} + style={[dimensionStyle, { backgroundColor: withAlpha(colors.textPrimary, 0.06) }]} + contentFit="cover" + transition={150} + /> + ) : ( + <View style={[styles.fallback, dimensionStyle, { backgroundColor: fallbackColor }]}> + <Text variant="caption" color="primary" style={{ fontSize: size * 0.4, fontWeight: '700' }}> + {initialsFor(name)} + </Text> + </View> + ); + + return ( + <View style={styles.wrapper}> + <View + style={ + ringColor + ? [ + styles.ring, + { + width: size + ringWidth * 2, + height: size + ringWidth * 2, + borderRadius: (size + ringWidth * 2) / 2, + borderWidth: ringWidth, + borderColor: ringColor, + }, + ] + : undefined + } + > + {core} + </View> + {badge ? ( + <View style={styles.badgeAnchor} pointerEvents="none"> + {badge} + </View> + ) : null} + </View> + ); +} + +const styles = StyleSheet.create({ + wrapper: { + alignSelf: 'flex-start', + }, + ring: { + alignItems: 'center', + justifyContent: 'center', + }, + fallback: { + alignItems: 'center', + justifyContent: 'center', + }, + badgeAnchor: { + position: 'absolute', + bottom: -2, + end: -2, + }, +}); diff --git a/src/components/ui/badge.tsx b/src/components/ui/badge.tsx new file mode 100644 index 0000000..5b722fb --- /dev/null +++ b/src/components/ui/badge.tsx @@ -0,0 +1,79 @@ +import type { PropsWithChildren } from 'react'; +import { StyleSheet, View } from 'react-native'; + +import { useTheme } from '@/theme/use-theme'; + +import { Text } from './text'; + +type Tone = 'brand' | 'neutral' | 'success' | 'warning' | 'danger' | 'info'; + +interface BadgeProps extends PropsWithChildren { + color?: string; + textColor?: string; + /** Semantic tone — resolves background/text from the theme's soft tokens. Ignored when an + * explicit `color`/`textColor` is passed. */ + tone?: Tone; + /** Numeric count mode: renders `count` (capped at `max`) instead of `children`. */ + count?: number; + max?: number; +} + +const DEFAULT_MAX = 99; + +const TONE_TOKENS: Record< + Tone, + { + bgKey: 'brandSoft' | 'surface1' | 'successSoft' | 'warningSoft' | 'dangerSoft' | 'infoSoft'; + textKey: 'brand' | 'textSecondary' | 'success' | 'warning' | 'danger' | 'info'; + } +> = { + brand: { bgKey: 'brandSoft', textKey: 'brand' }, + neutral: { bgKey: 'surface1', textKey: 'textSecondary' }, + success: { bgKey: 'successSoft', textKey: 'success' }, + warning: { bgKey: 'warningSoft', textKey: 'warning' }, + danger: { bgKey: 'dangerSoft', textKey: 'danger' }, + info: { bgKey: 'infoSoft', textKey: 'info' }, +}; + +/** Generic pill badge. Two modes: pass `children` for a label badge (explicit `color`/`textColor` + * for semantic use, or `tone` to resolve from theme tokens), or pass `count`/`max` for a numeric + * count badge (e.g. unread counts) — count above `max` renders as `"{max}+"`. */ +export function Badge({ children, color, textColor, tone, count, max = DEFAULT_MAX }: BadgeProps) { + const { colors, spacing, radius } = useTheme(); + + if (typeof count === 'number' && count <= 0 && children === undefined) return null; + + const toneTokens = tone ? TONE_TOKENS[tone] : undefined; + const backgroundColor = color ?? (toneTokens ? colors[toneTokens.bgKey] : colors.surface); + const resolvedTextColor = textColor ?? (toneTokens ? colors[toneTokens.textKey] : colors.text); + + const content = typeof count === 'number' ? (count > max ? `${max}+` : String(count)) : children; + + return ( + <View + style={[ + styles.container, + { + backgroundColor, + borderRadius: radius.full, + paddingHorizontal: spacing.xs + 2, + paddingVertical: 2, + }, + ]} + > + <Text + variant="caption" + numeric={typeof count === 'number'} + style={{ color: resolvedTextColor, fontWeight: '600' }} + > + {content} + </Text> + </View> + ); +} + +const styles = StyleSheet.create({ + container: { + alignSelf: 'flex-start', + }, +}); diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx new file mode 100644 index 0000000..d3ef8fd --- /dev/null +++ b/src/components/ui/button.tsx @@ -0,0 +1,119 @@ +import type { ComponentProps } from 'react'; +import { ActivityIndicator, StyleSheet } from 'react-native'; + +import { useTheme } from '@/theme/use-theme'; + +import { Icon, type IconName } from './icon'; +import { PressableScale } from './pressable-scale'; +import { Text } from './text'; + +type Variant = 'primary' | 'secondary' | 'danger' | 'ghost' | 'tonal'; +type Size = 'sm' | 'md' | 'lg'; + +interface ButtonProps extends Omit<ComponentProps<typeof PressableScale>, 'style'> { + label: string; + variant?: Variant; + size?: Size; + loading?: boolean; + disabled?: boolean; + icon?: IconName; + fullWidth?: boolean; +} + +const SIZE_STYLES: Record< + Size, + { minHeight: number; paddingHorizontal: number; textVariant: 'callout' | 'body' | 'subtitle' } +> = { + sm: { minHeight: 36, paddingHorizontal: 14, textVariant: 'callout' }, + md: { minHeight: 48, paddingHorizontal: 20, textVariant: 'body' }, + lg: { minHeight: 56, paddingHorizontal: 24, textVariant: 'subtitle' }, +}; + +export function Button({ + label, + variant = 'primary', + size = 'md', + loading = false, + disabled = false, + icon, + fullWidth = false, + ...rest +}: ButtonProps) { + const { colors, spacing, radius } = useTheme(); + const isDisabled = disabled || loading; + + const variantStyles: Record< + Variant, + { background: string; text: 'onBrand' | 'primary' | 'danger' | 'brand'; borderColor?: string } + > = { + primary: { background: colors.brand, text: 'onBrand' }, + danger: { background: colors.danger, text: 'onBrand' }, + secondary: { background: colors.surface1, text: 'primary', borderColor: colors.borderStrong }, + tonal: { background: colors.brandSoft, text: 'brand' }, + ghost: { background: 'transparent', text: 'primary' }, + }; + const { background, text, borderColor } = variantStyles[variant]; + const sizeStyle = SIZE_STYLES[size]; + + const iconColor = + text === 'onBrand' + ? colors.onBrand + : text === 'brand' + ? colors.brand + : text === 'danger' + ? colors.danger + : colors.textPrimary; + + return ( + <PressableScale + accessibilityRole="button" + accessibilityState={{ disabled: isDisabled }} + disabled={isDisabled} + haptic={isDisabled ? false : 'light'} + style={[ + styles.base, + { + backgroundColor: background, + borderRadius: radius.md, + minHeight: sizeStyle.minHeight, + paddingHorizontal: sizeStyle.paddingHorizontal, + gap: spacing.xs + 2, + opacity: isDisabled ? 0.5 : 1, + borderWidth: borderColor ? StyleSheet.hairlineWidth : 0, + borderColor, + alignSelf: fullWidth ? 'stretch' : 'flex-start', + width: fullWidth ? '100%' : undefined, + }, + ]} + {...rest} + > + {loading ? ( + <ActivityIndicator color={iconColor} /> + ) : ( + <> + {icon ? ( + <Icon + name={icon} + size={sizeStyle.textVariant === 'subtitle' ? 22 : 18} + color={iconColor} + /> + ) : null} + <Text variant={sizeStyle.textVariant} color={text} style={styles.label}> + {label} + </Text> + </> + )} + </PressableScale> + ); +} + +const styles = StyleSheet.create({ + base: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + }, + label: { + fontWeight: '600', + }, +}); diff --git a/src/components/ui/chip.test.tsx b/src/components/ui/chip.test.tsx new file mode 100644 index 0000000..5bab8a4 --- /dev/null +++ b/src/components/ui/chip.test.tsx @@ -0,0 +1,51 @@ +import { fireEvent, render, screen } from '@testing-library/react-native'; + +import { initI18n } from '@/i18n'; + +import { Chip } from './chip'; + +beforeAll(() => { + initI18n(); +}); + +describe('Chip', () => { + it('reflects selected state via accessibilityState', async () => { + await render(<Chip label="Unread" selected onPress={() => {}} />); + + const chip = screen.getByRole('button', { name: 'Unread' }); + expect(chip.props.accessibilityState).toMatchObject({ selected: true, disabled: false }); + }); + + it('reflects unselected state via accessibilityState', async () => { + await render(<Chip label="Unread" selected={false} onPress={() => {}} />); + + const chip = screen.getByRole('button', { name: 'Unread' }); + expect(chip.props.accessibilityState).toMatchObject({ selected: false }); + }); + + it('calls onPress when tapped', async () => { + const onPress = jest.fn(); + await render(<Chip label="Bot" onPress={onPress} />); + + fireEvent.press(screen.getByRole('button', { name: 'Bot' })); + + expect(onPress).toHaveBeenCalledTimes(1); + }); + + it('calls onRemove when the remove button is tapped, without triggering onPress', async () => { + const onPress = jest.fn(); + const onRemove = jest.fn(); + await render(<Chip label="VIP" onPress={onPress} onRemove={onRemove} />); + + fireEvent.press(screen.getByRole('button', { name: 'Remove VIP' })); + + expect(onRemove).toHaveBeenCalledTimes(1); + expect(onPress).not.toHaveBeenCalled(); + }); + + it('renders without a remove button when onRemove is omitted', async () => { + await render(<Chip label="Archived" />); + + expect(screen.queryAllByRole('button')).toHaveLength(0); + }); +}); diff --git a/src/components/ui/chip.tsx b/src/components/ui/chip.tsx new file mode 100644 index 0000000..d73621e --- /dev/null +++ b/src/components/ui/chip.tsx @@ -0,0 +1,127 @@ +import type { ReactNode } from 'react'; +import { useTranslation } from 'react-i18next'; +import { StyleSheet, View } from 'react-native'; + +import { withAlpha } from '@/theme/color-utils'; +import { useTheme } from '@/theme/use-theme'; + +import { Icon, type IconName } from './icon'; +import { IconButton } from './icon-button'; +import { PressableScale } from './pressable-scale'; +import { Text } from './text'; + +type Tone = 'neutral' | 'brand' | 'custom'; +type Size = 'sm' | 'md'; + +interface ChipProps { + label: string; + selected?: boolean; + onPress?: () => void; + onRemove?: () => void; + leading?: IconName | ReactNode; + tone?: Tone; + /** Base color for `tone="custom"` (e.g. a channel or tag color). Ignored for other tones. */ + color?: string; + size?: Size; + disabled?: boolean; +} + +const SIZE_PADDING: Record<Size, number> = { sm: 6, md: 8 }; +const ICON_SIZE: Record<Size, number> = { sm: 12, md: 14 }; + +/** Filter/tag chip: selectable pill with an optional leading icon and optional remove (x) + * button. Selection state is exposed to assistive tech via `accessibilityState`, and selecting + * fires a light 'selection' haptic. */ +export function Chip({ + label, + selected = false, + onPress, + onRemove, + leading, + tone = 'neutral', + color, + size = 'md', + disabled = false, +}: ChipProps) { + const { t } = useTranslation(); + const { colors, radius, spacing } = useTheme(); + + const baseColor = + tone === 'brand' + ? colors.brand + : tone === 'custom' + ? (color ?? colors.brand) + : colors.textSecondary; + + const backgroundColor = selected ? withAlpha(baseColor, 0.16) : colors.surface1; + const borderColor = selected ? withAlpha(baseColor, 0.4) : colors.borderSubtle; + const textColorStyle = selected ? baseColor : colors.textSecondary; + + const content = ( + <View + style={[ + styles.container, + { + backgroundColor, + borderColor, + borderRadius: radius.full, + paddingVertical: SIZE_PADDING[size], + paddingStart: spacing.ms, + paddingEnd: onRemove ? spacing.xs : spacing.ms, + gap: spacing.xs, + opacity: disabled ? 0.5 : 1, + }, + ]} + > + {typeof leading === 'string' ? ( + <Icon name={leading as IconName} size={ICON_SIZE[size]} color={textColorStyle} /> + ) : ( + leading + )} + <Text variant="caption" numeric style={{ color: textColorStyle, fontWeight: '600' }}> + {label} + </Text> + {onRemove ? ( + <IconButton + accessibilityLabel={t('common.removeItem', { defaultValue: 'Remove {{label}}', label })} + icon="close" + size="sm" + variant="ghost" + tint={textColorStyle} + haptic={false} + disabled={disabled} + onPress={onRemove} + style={styles.removeButton} + /> + ) : null} + </View> + ); + + if (!onPress) return content; + + return ( + <PressableScale + accessibilityRole="button" + accessibilityLabel={label} + accessibilityState={{ selected, disabled }} + disabled={disabled} + onPress={onPress} + haptic={disabled ? false : 'selection'} + > + {content} + </PressableScale> + ); +} + +const styles = StyleSheet.create({ + container: { + flexDirection: 'row', + alignItems: 'center', + borderWidth: StyleSheet.hairlineWidth, + alignSelf: 'flex-start', + }, + removeButton: { + width: 20, + height: 20, + }, +}); diff --git a/src/components/ui/connection-banner.tsx b/src/components/ui/connection-banner.tsx new file mode 100644 index 0000000..2211bee --- /dev/null +++ b/src/components/ui/connection-banner.tsx @@ -0,0 +1,128 @@ +import { useEffect, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { StyleSheet } from 'react-native'; +import Animated, { useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated'; + +import { useReducedMotion } from '@/theme/motion'; +import { useTheme } from '@/theme/use-theme'; + +import { Icon } from './icon'; +import { Text } from './text'; + +export type ConnectionState = 'online' | 'connecting' | 'reconnecting' | 'offline'; + +interface ConnectionBannerProps { + state: ConnectionState; +} + +const AUTO_HIDE_AFTER_ONLINE_MS = 1200; +const BANNER_HEIGHT = 36; + +/** Slide-from-top presentational banner for realtime connection state. Stays visible for any + * non-'online' state; once state transitions to 'online' it holds briefly (so the "back online" + * confirmation is readable) then auto-hides. Purely presentational — callers own translating + * actual socket state into `state`. */ +export function ConnectionBanner({ state }: ConnectionBannerProps) { + const { t } = useTranslation(); + const { colors, motion } = useTheme(); + const reducedMotion = useReducedMotion(); + const translateY = useSharedValue(-BANNER_HEIGHT); + // Whether we're still holding the banner visible after a transition to 'online', during the + // brief confirmation window before auto-hiding. Derived synchronously from `state` wherever + // possible; the one place this must be set imperatively is the hold timer below, which is a + // deliberate external-timer synchronization (exactly what effects are for), not a disguised + // derived-state computation — see the inline suppression there. + const [isHoldingAfterOnline, setIsHoldingAfterOnline] = useState(state === 'online'); + const shouldShow = state !== 'online' || isHoldingAfterOnline; + + useEffect(() => { + if (state !== 'online') { + translateY.value = reducedMotion ? 0 : withTiming(0, { duration: motion.durations.base }); + return undefined; + } + + // Just transitioned to 'online' — hold visible briefly (so the confirmation is readable), + // then slide away. This synchronizes to a real external timer (setTimeout), which is exactly + // what effects are for; the lint rule's "avoid setState in an effect body" heuristic doesn't + // have a way to distinguish that from disguised derived state. + // eslint-disable-next-line react-hooks/set-state-in-effect + setIsHoldingAfterOnline(true); + const timer = setTimeout(() => { + translateY.value = reducedMotion + ? -BANNER_HEIGHT + : withTiming(-BANNER_HEIGHT, { duration: motion.durations.base }); + setIsHoldingAfterOnline(false); + }, AUTO_HIDE_AFTER_ONLINE_MS); + return () => clearTimeout(timer); + // eslint-disable-next-line react-hooks/exhaustive-deps -- `translateY` is a stable shared-value ref. + }, [state, motion.durations.base, reducedMotion]); + + const animatedStyle = useAnimatedStyle(() => ({ + transform: [{ translateY: translateY.value }], + })); + + const config: Record< + ConnectionState, + { + icon: 'cloud-offline-outline' | 'sync-outline' | 'checkmark-circle-outline'; + background: string; + textColor: string; + label: string; + } + > = { + online: { + icon: 'checkmark-circle-outline', + background: colors.successSoft, + textColor: colors.success, + label: t('realtime.online', { defaultValue: 'Back online' }), + }, + connecting: { + icon: 'sync-outline', + background: colors.infoSoft, + textColor: colors.info, + label: t('realtime.connecting', { defaultValue: 'Connecting…' }), + }, + reconnecting: { + icon: 'sync-outline', + background: colors.warningSoft, + textColor: colors.warning, + label: t('realtime.reconnecting', { defaultValue: 'Reconnecting…' }), + }, + offline: { + icon: 'cloud-offline-outline', + background: colors.dangerSoft, + textColor: colors.danger, + label: t('realtime.offline', { defaultValue: "You're offline" }), + }, + }; + const { icon, background, textColor, label } = config[state]; + + if (!shouldShow) return null; + + return ( + <Animated.View + accessibilityLiveRegion="polite" + accessibilityRole="alert" + style={[ + styles.container, + { height: BANNER_HEIGHT, backgroundColor: background }, + reducedMotion ? undefined : animatedStyle, + ]} + > + <Icon name={icon} size={16} color={textColor} /> + <Text variant="caption" style={{ color: textColor, fontWeight: '600' }}> + {label} + </Text> + </Animated.View> + ); +} + +const styles = StyleSheet.create({ + container: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + gap: 6, + width: '100%', + }, +}); diff --git a/src/components/ui/count-pill.test.tsx b/src/components/ui/count-pill.test.tsx new file mode 100644 index 0000000..edfa34d --- /dev/null +++ b/src/components/ui/count-pill.test.tsx @@ -0,0 +1,35 @@ +import { render, screen } from '@testing-library/react-native'; + +import { initI18n } from '@/i18n'; + +import { CountPill } from './count-pill'; + +beforeAll(() => { + initI18n(); +}); + +describe('CountPill', () => { + it('renders the raw count when under the cap', async () => { + await render(<CountPill count={7} />); + + expect(screen.getByText('7')).toBeTruthy(); + }); + + it('renders "{max}+" when the count exceeds the default cap of 99', async () => { + await render(<CountPill count={140} />); + + expect(screen.getByText('99+')).toBeTruthy(); + }); + + it('respects a custom max', async () => { + await render(<CountPill count={12} max={9} />); + + expect(screen.getByText('9+')).toBeTruthy(); + }); + + it('renders nothing when count is 0', async () => { + const { toJSON } = await render(<CountPill count={0} />); + + expect(toJSON()).toBeNull(); + }); +}); diff --git a/src/components/ui/count-pill.tsx b/src/components/ui/count-pill.tsx new file mode 100644 index 0000000..70bc7bb --- /dev/null +++ b/src/components/ui/count-pill.tsx @@ -0,0 +1,63 @@ +import { StyleSheet, View } from 'react-native'; + +import { useTheme } from '@/theme/use-theme'; + +import { Text } from './text'; + +type Tone = 'brand' | 'neutral' | 'danger'; + +interface CountPillProps { + count: number; + /** Display cap — counts above this render as `"{max}+"`. Defaults to 99. */ + max?: number; + tone?: Tone; +} + +const DEFAULT_MAX = 99; +const PILL_SIZE = 18; + +/** Small numeric pill for unread counts, badge counts, etc. Renders nothing when `count` is 0 or + * less — callers don't need to guard the render themselves. */ +export function CountPill({ count, max = DEFAULT_MAX, tone = 'brand' }: CountPillProps) { + const { colors } = useTheme(); + + if (count <= 0) return null; + + const label = count > max ? `${max}+` : String(count); + + const toneStyle: Record<Tone, { background: string; text: 'onBrand' | 'inverse' }> = { + brand: { background: colors.brand, text: 'onBrand' }, + neutral: { background: colors.textTertiary, text: 'inverse' }, + danger: { background: colors.danger, text: 'onBrand' }, + }; + const { background, text } = toneStyle[tone]; + + return ( + <View + style={[ + styles.container, + { + backgroundColor: background, + minWidth: PILL_SIZE, + height: PILL_SIZE, + borderRadius: PILL_SIZE / 2, + paddingHorizontal: label.length > 1 ? 5 : 0, + }, + ]} + > + <Text variant="micro" color={text} numeric style={styles.label}> + {label} + </Text> + </View> + ); +} + +const styles = StyleSheet.create({ + container: { + alignItems: 'center', + justifyContent: 'center', + }, + label: { + fontWeight: '700', + }, +}); diff --git a/src/components/ui/date-separator.tsx b/src/components/ui/date-separator.tsx new file mode 100644 index 0000000..378697e --- /dev/null +++ b/src/components/ui/date-separator.tsx @@ -0,0 +1,62 @@ +import dayjs from 'dayjs'; +import { useTranslation } from 'react-i18next'; +import { StyleSheet, View } from 'react-native'; + +import { useTheme } from '@/theme/use-theme'; + +import { Text } from './text'; + +interface DateSeparatorProps { + /** ISO date string (or anything dayjs can parse). */ + date: string; +} + +/** Centered pill label for message-list date breaks: "Today" / "Yesterday" / a localized date for + * anything older. */ +export function DateSeparator({ date }: DateSeparatorProps) { + const { t } = useTranslation(); + const { colors, radius, spacing } = useTheme(); + + const target = dayjs(date); + const today = dayjs(); + const isToday = target.isSame(today, 'day'); + const isYesterday = target.isSame(today.subtract(1, 'day'), 'day'); + const isSameYear = target.isSame(today, 'year'); + + const label = isToday + ? t('common.today', { defaultValue: 'Today' }) + : isYesterday + ? t('common.yesterday', { defaultValue: 'Yesterday' }) + : target.format(isSameYear ? 'MMMM D' : 'MMMM D, YYYY'); + + return ( + <View style={styles.row}> + <View + style={[ + styles.pill, + { + backgroundColor: colors.surface1, + borderRadius: radius.full, + paddingHorizontal: spacing.ms, + paddingVertical: spacing.xs, + }, + ]} + > + <Text variant="micro" color="secondary" style={styles.label}> + {label} + </Text> + </View> + </View> + ); +} + +const styles = StyleSheet.create({ + row: { + alignItems: 'center', + width: '100%', + }, + pill: {}, + label: { + fontWeight: '600', + }, +}); diff --git a/src/components/ui/divider.tsx b/src/components/ui/divider.tsx new file mode 100644 index 0000000..8942e16 --- /dev/null +++ b/src/components/ui/divider.tsx @@ -0,0 +1,31 @@ +import { StyleSheet, View } from 'react-native'; + +import { useTheme } from '@/theme/use-theme'; + +interface DividerProps { + /** Insets the divider from the leading edge (e.g. to align past a leading icon/avatar in a + * list). */ + inset?: boolean; +} + +/** Hairline horizontal rule using the subtle border token. */ +export function Divider({ inset = false }: DividerProps) { + const { colors, spacing } = useTheme(); + + return ( + <View + style={[ + styles.line, + { backgroundColor: colors.borderSubtle }, + inset ? { marginStart: spacing.xl + spacing.sm } : undefined, + ]} + /> + ); +} + +const styles = StyleSheet.create({ + line: { + height: StyleSheet.hairlineWidth, + width: '100%', + }, +}); diff --git a/src/components/ui/empty-state.tsx b/src/components/ui/empty-state.tsx new file mode 100644 index 0000000..d7f324d --- /dev/null +++ b/src/components/ui/empty-state.tsx @@ -0,0 +1,77 @@ +import { StyleSheet, View } from 'react-native'; +import Animated, { FadeInUp } from 'react-native-reanimated'; + +import { useReducedMotion } from '@/theme/motion'; +import { useTheme } from '@/theme/use-theme'; + +import { Button } from './button'; +import { Icon, type IconName } from './icon'; +import { Text } from './text'; + +interface EmptyStateProps { + icon?: IconName; + title: string; + description?: string; + action?: { label: string; onPress: () => void }; +} + +const ICON_CIRCLE_SIZE = 72; + +export function EmptyState({ + icon = 'file-tray-outline', + title, + description, + action, +}: EmptyStateProps) { + const { colors, spacing } = useTheme(); + const reducedMotion = useReducedMotion(); + + return ( + <Animated.View + entering={reducedMotion ? undefined : FadeInUp.duration(280)} + style={[styles.container, { padding: spacing.xl, gap: spacing.sm }]} + > + <View + style={[ + styles.iconCircle, + { + width: ICON_CIRCLE_SIZE, + height: ICON_CIRCLE_SIZE, + borderRadius: ICON_CIRCLE_SIZE / 2, + backgroundColor: colors.brandSoft, + }, + ]} + > + <Icon name={icon} size={32} color={colors.brand} /> + </View> + <Text variant="title" style={styles.centered}> + {title} + </Text> + {description ? ( + <Text variant="body" color="secondary" style={styles.centered}> + {description} + </Text> + ) : null} + {action ? ( + <View style={{ marginTop: spacing.xs }}> + <Button label={action.label} variant="tonal" onPress={action.onPress} /> + </View> + ) : null} + </Animated.View> + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + }, + iconCircle: { + alignItems: 'center', + justifyContent: 'center', + }, + centered: { + textAlign: 'center', + }, +}); diff --git a/src/components/ui/error-banner.tsx b/src/components/ui/error-banner.tsx new file mode 100644 index 0000000..6a980a1 --- /dev/null +++ b/src/components/ui/error-banner.tsx @@ -0,0 +1,67 @@ +import { Pressable, StyleSheet, View } from 'react-native'; + +import { useTheme } from '@/theme/use-theme'; + +import { Icon } from './icon'; +import { Text } from './text'; + +interface ErrorBannerProps { + message: string; + actionLabel?: string; + onAction?: () => void; + tone?: 'danger' | 'warning' | 'info'; +} + +/** Inline banner for errors and workspace-blocked/quota states. Not a toast — stays mounted until + * the caller removes it (composer quota banner, list-level fetch error, etc). */ +export function ErrorBanner({ message, actionLabel, onAction, tone = 'danger' }: ErrorBannerProps) { + const { colors, spacing, radius } = useTheme(); + + const toneTokens = { + danger: { tint: colors.danger, soft: colors.dangerSoft }, + warning: { tint: colors.warning, soft: colors.warningSoft }, + info: { tint: colors.info, soft: colors.infoSoft }, + }[tone]; + + return ( + <View + accessibilityLiveRegion="polite" + accessibilityRole="alert" + style={[ + styles.container, + { + backgroundColor: toneTokens.soft, + borderRadius: radius.md, + padding: spacing.ms, + gap: spacing.xs, + }, + ]} + > + <Icon + name={tone === 'info' ? 'information-circle' : 'alert-circle'} + size={18} + color={toneTokens.tint} + /> + <Text variant="caption" style={styles.message}> + {message} + </Text> + {actionLabel && onAction ? ( + <Pressable onPress={onAction} accessibilityRole="button"> + <Text variant="caption" style={{ color: toneTokens.tint, fontWeight: '700' }}> + {actionLabel} + </Text> + </Pressable> + ) : null} + </View> + ); +} + +const styles = StyleSheet.create({ + container: { + flexDirection: 'row', + alignItems: 'center', + }, + message: { + flex: 1, + }, +}); diff --git a/src/components/ui/icon-button.test.tsx b/src/components/ui/icon-button.test.tsx new file mode 100644 index 0000000..77ee7a4 --- /dev/null +++ b/src/components/ui/icon-button.test.tsx @@ -0,0 +1,49 @@ +import { fireEvent, render, screen } from '@testing-library/react-native'; + +import { initI18n } from '@/i18n'; + +import { IconButton } from './icon-button'; + +beforeAll(() => { + initI18n(); +}); + +describe('IconButton', () => { + it('exposes the required accessibilityLabel and button role', async () => { + await render( + <IconButton accessibilityLabel="Close conversation" icon="close" onPress={() => {}} />, + ); + + const button = screen.getByRole('button', { name: 'Close conversation' }); + expect(button).toBeTruthy(); + }); + + it('calls onPress when pressed', async () => { + const onPress = jest.fn(); + await render( + <IconButton accessibilityLabel="Archive" icon="archive-outline" onPress={onPress} />, + ); + + fireEvent.press(screen.getByRole('button', { name: 'Archive' })); + + expect(onPress).toHaveBeenCalledTimes(1); + }); + + it('marks itself disabled via accessibilityState when loading', async () => { + await render(<IconButton accessibilityLabel="Send" icon="send" loading onPress={() => {}} />); + + const button = screen.getByRole('button', { name: 'Send' }); + expect(button.props.accessibilityState).toMatchObject({ disabled: true, busy: true }); + }); + + it('does not call onPress when disabled', async () => { + const onPress = jest.fn(); + await render( + <IconButton accessibilityLabel="Delete" icon="trash-outline" disabled onPress={onPress} />, + ); + + fireEvent.press(screen.getByRole('button', { name: 'Delete' })); + + expect(onPress).not.toHaveBeenCalled(); + }); +}); diff --git a/src/components/ui/icon-button.tsx b/src/components/ui/icon-button.tsx new file mode 100644 index 0000000..44dfb12 --- /dev/null +++ b/src/components/ui/icon-button.tsx @@ -0,0 +1,137 @@ +import type { ComponentProps } from 'react'; +import type { StyleProp, ViewStyle } from 'react-native'; +import { ActivityIndicator, StyleSheet, View } from 'react-native'; + +import { withAlpha } from '@/theme/color-utils'; +import { useTheme } from '@/theme/use-theme'; + +import { Icon, type IconName } from './icon'; +import { CountPill } from './count-pill'; +import { PressableScale } from './pressable-scale'; + +type Size = 'sm' | 'md' | 'lg'; +type Variant = 'ghost' | 'tonal' | 'filled' | 'outline'; + +const SIZE_MAP: Record<Size, number> = { sm: 32, md: 40, lg: 48 }; +const ICON_SIZE_MAP: Record<Size, number> = { sm: 16, md: 20, lg: 24 }; +const MIN_HIT_TARGET = 44; + +interface IconButtonProps extends Omit< + ComponentProps<typeof PressableScale>, + 'style' | 'children' +> { + /** Required — every icon-only control must be independently labeled for screen readers. There + * is no default: an icon glyph alone conveys no meaning to VoiceOver/TalkBack. */ + accessibilityLabel: string; + icon: IconName; + size?: Size; + variant?: Variant; + /** Overrides the icon/border tint. Defaults to a variant-appropriate color from the theme. */ + tint?: string; + badgeCount?: number; + loading?: boolean; + disabled?: boolean; + /** Overrides the pressable box's size/layout — used by composite primitives (e.g. Chip's + * inline remove button) that need a smaller box than the `size` presets provide. */ + style?: StyleProp<ViewStyle>; +} + +/** Icon-only pressable control. Visual size can be as small as 32pt (`sm`), but `hitSlop` always + * pads the touch target up to at least 44x44 — the platform-recommended minimum — regardless of + * the visual box, so small icon buttons stay comfortably tappable. */ +export function IconButton({ + accessibilityLabel, + icon, + size = 'md', + variant = 'ghost', + tint, + badgeCount, + loading = false, + disabled = false, + haptic, + style, + ...rest +}: IconButtonProps) { + const { colors, radius } = useTheme(); + const boxSize = SIZE_MAP[size]; + const iconSize = ICON_SIZE_MAP[size]; + const isDisabled = disabled || loading; + const hitSlopValue = Math.max(0, Math.ceil((MIN_HIT_TARGET - boxSize) / 2)); + + const resolvedTint = tint ?? colors.textPrimary; + + const variantStyle = (() => { + switch (variant) { + case 'filled': + return { + backgroundColor: tint ?? colors.brand, + iconColor: tint ? resolvedTint : colors.onBrand, + }; + case 'tonal': + return { backgroundColor: withAlpha(resolvedTint, 0.14), iconColor: resolvedTint }; + case 'outline': + return { + backgroundColor: 'transparent', + iconColor: resolvedTint, + borderWidth: StyleSheet.hairlineWidth, + borderColor: colors.borderStrong, + }; + case 'ghost': + default: + return { backgroundColor: 'transparent', iconColor: resolvedTint }; + } + })(); + + return ( + <View style={styles.wrapper}> + <PressableScale + accessibilityRole="button" + accessibilityLabel={accessibilityLabel} + accessibilityState={{ disabled: isDisabled, busy: loading }} + disabled={isDisabled} + hitSlop={hitSlopValue} + haptic={isDisabled ? false : haptic} + style={[ + styles.base, + { + width: boxSize, + height: boxSize, + borderRadius: radius.full, + backgroundColor: variantStyle.backgroundColor, + borderWidth: 'borderWidth' in variantStyle ? variantStyle.borderWidth : 0, + borderColor: 'borderColor' in variantStyle ? variantStyle.borderColor : undefined, + opacity: isDisabled ? 0.4 : 1, + }, + style, + ]} + {...rest} + > + {loading ? ( + <ActivityIndicator size="small" color={variantStyle.iconColor} /> + ) : ( + <Icon name={icon} size={iconSize} color={variantStyle.iconColor} flipRTL /> + )} + </PressableScale> + {typeof badgeCount === 'number' && badgeCount > 0 ? ( + <View style={styles.badgeAnchor} pointerEvents="none"> + <CountPill count={badgeCount} tone="danger" /> + </View> + ) : null} + </View> + ); +} + +const styles = StyleSheet.create({ + wrapper: { + alignSelf: 'flex-start', + }, + base: { + alignItems: 'center', + justifyContent: 'center', + }, + badgeAnchor: { + position: 'absolute', + top: -4, + end: -4, + }, +}); diff --git a/src/components/ui/icon.tsx b/src/components/ui/icon.tsx new file mode 100644 index 0000000..3de47a3 --- /dev/null +++ b/src/components/ui/icon.tsx @@ -0,0 +1,21 @@ +import { Ionicons } from '@expo/vector-icons'; +import type { ComponentProps } from 'react'; +import { I18nManager } from 'react-native'; + +export type IconName = ComponentProps<typeof Ionicons>['name']; + +interface IconProps { + name: IconName; + size?: number; + color: string; + /** Mirror this icon horizontally in RTL layouts (chevrons, send arrow, etc). `I18nManager.isRTL` + * only changes on app reload, so reading it at render time is safe — no live-toggle case exists. */ + flipRTL?: boolean; +} + +/** Single wrapper around the icon set in use (Ionicons) so swapping icon libraries later touches + * one file instead of every call site. */ +export function Icon({ name, size = 24, color, flipRTL = false }: IconProps) { + const style = flipRTL && I18nManager.isRTL ? { transform: [{ scaleX: -1 }] } : undefined; + return <Ionicons name={name} size={size} color={color} style={style} />; +} diff --git a/src/components/ui/list-footer-spinner.tsx b/src/components/ui/list-footer-spinner.tsx new file mode 100644 index 0000000..c629dce --- /dev/null +++ b/src/components/ui/list-footer-spinner.tsx @@ -0,0 +1,24 @@ +import { ActivityIndicator, View } from 'react-native'; + +import { useTheme } from '@/theme/use-theme'; + +interface ListFooterSpinnerProps { + visible: boolean; + /** Reserved height for the footer slot — kept constant whether or not the spinner is showing, + * so toggling `visible` never shifts list content (no jump on pagination end). */ + height?: number; +} + +const DEFAULT_HEIGHT = 56; + +/** FlatList/FlashList `ListFooterComponent` for infinite-scroll pagination. Always reserves + * `height`, whether or not the spinner is actually visible. */ +export function ListFooterSpinner({ visible, height = DEFAULT_HEIGHT }: ListFooterSpinnerProps) { + const { colors } = useTheme(); + + return ( + <View style={{ height, alignItems: 'center', justifyContent: 'center' }}> + {visible ? <ActivityIndicator color={colors.textSecondary} /> : null} + </View> + ); +} diff --git a/src/components/ui/list-item.tsx b/src/components/ui/list-item.tsx new file mode 100644 index 0000000..b9eb68c --- /dev/null +++ b/src/components/ui/list-item.tsx @@ -0,0 +1,144 @@ +import type { PropsWithChildren, ReactNode } from 'react'; +import type { AccessibilityState } from 'react-native'; +import { StyleSheet, View } from 'react-native'; +import Animated, { useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated'; + +import { alphaTokens, withAlpha } from '@/theme/color-utils'; +import { useReducedMotion } from '@/theme/motion'; +import { useTheme } from '@/theme/use-theme'; + +import { Icon } from './icon'; +import { PressableScale } from './pressable-scale'; +import { Text } from './text'; + +interface ListItemProps extends PropsWithChildren { + title: string; + subtitle?: string; + leading?: ReactNode; + trailing?: ReactNode; + onPress?: () => void; + showChevron?: boolean; + /** Renders title in the danger color and skips the scale-press haptic escalation — for + * destructive actions (delete, remove, leave). */ + destructive?: boolean; + /** Right-aligned secondary text shown before `trailing` (e.g. a current value/setting). */ + value?: string; + disabled?: boolean; + /** Extra accessibility state merged with `{ disabled }` (e.g. `{ selected: true }` for a row in + * a single-select list like the workspace picker). */ + accessibilityState?: AccessibilityState; +} + +const MIN_HEIGHT = 52; + +/** Generic row: leading slot (avatar/icon), title/subtitle, trailing slot. Used for settings rows, + * contact rows, member rows — anywhere the row is press-to-navigate. */ +export function ListItem({ + title, + subtitle, + leading, + trailing, + onPress, + showChevron = false, + destructive = false, + value, + disabled = false, + accessibilityState, + children, +}: ListItemProps) { + const { colors, spacing } = useTheme(); + const reducedMotion = useReducedMotion(); + const overlayOpacity = useSharedValue(0); + + const overlayStyle = useAnimatedStyle(() => ({ opacity: overlayOpacity.value })); + + const content = ( + <View style={styles.stack}> + <View + style={[ + styles.row, + { + minHeight: MIN_HEIGHT, + paddingVertical: spacing.ms, + paddingHorizontal: spacing.md, + gap: spacing.ms, + opacity: disabled ? 0.5 : 1, + }, + ]} + > + {leading} + <View style={styles.body}> + <Text variant="body" numberOfLines={1} color={destructive ? 'danger' : 'primary'}> + {title} + </Text> + {subtitle ? ( + <Text variant="caption" color="secondary" numberOfLines={1}> + {subtitle} + </Text> + ) : null} + {children} + </View> + {value ? ( + <Text variant="callout" color="secondary" numberOfLines={1}> + {value} + </Text> + ) : null} + {trailing} + {showChevron ? ( + <Icon name="chevron-forward" size={18} color={colors.textSecondary} flipRTL /> + ) : null} + </View> + <Animated.View + pointerEvents="none" + style={[ + styles.overlay, + { backgroundColor: withAlpha(colors.textPrimary, alphaTokens.pressed) }, + overlayStyle, + ]} + /> + </View> + ); + + if (!onPress) { + return content; + } + + return ( + <PressableScale + accessibilityRole="button" + accessibilityState={{ ...accessibilityState, disabled }} + disabled={disabled} + haptic={disabled ? false : destructive ? 'medium' : 'light'} + onPress={onPress} + onPressIn={() => { + // reanimated `SharedValue.value` is a mutable UI-thread ref, not React state — see + // pressable-scale.tsx for the fuller explanation of this lint false-positive. + // eslint-disable-next-line react-hooks/immutability + overlayOpacity.value = reducedMotion ? 1 : withTiming(1, { duration: 100 }); + }} + onPressOut={() => { + // eslint-disable-next-line react-hooks/immutability -- see onPressIn above. + overlayOpacity.value = reducedMotion ? 0 : withTiming(0, { duration: 150 }); + }} + > + {content} + </PressableScale> + ); +} + +const styles = StyleSheet.create({ + stack: { + position: 'relative', + }, + row: { + flexDirection: 'row', + alignItems: 'center', + }, + body: { + flex: 1, + gap: 2, + }, + overlay: { + ...StyleSheet.absoluteFill, + }, +}); diff --git a/src/components/ui/pressable-scale.tsx b/src/components/ui/pressable-scale.tsx new file mode 100644 index 0000000..deeec24 --- /dev/null +++ b/src/components/ui/pressable-scale.tsx @@ -0,0 +1,63 @@ +import type { ComponentProps } from 'react'; +import { Pressable } from 'react-native'; +import Animated, { useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated'; + +import { useReducedMotion } from '@/theme/motion'; +import { useTheme } from '@/theme/use-theme'; + +import { type HapticKind, triggerHaptic } from '@/lib/haptics'; + +const AnimatedPressable = Animated.createAnimatedComponent(Pressable); + +interface PressableScaleProps extends ComponentProps<typeof Pressable> { + /** Scale factor applied on press-in. Defaults to the `pressScale` motion token. */ + scaleTo?: number; + /** Haptic kind fired on press-in. Pass `false` to suppress. Defaults to `'light'`. */ + haptic?: HapticKind | false; +} + +/** Pressable wrapper that scales down on press via reanimated, using the shared `pressScale` + * token. Respects `useReducedMotion()` — when reduced motion is on, the scale animation is + * skipped entirely (no transform change) but the haptic still fires, since haptics aren't + * motion. */ +export function PressableScale({ + scaleTo, + haptic = 'light', + onPressIn, + onPressOut, + style, + ...rest +}: PressableScaleProps) { + const { motion } = useTheme(); + const reducedMotion = useReducedMotion(); + const scale = useSharedValue(1); + const targetScale = scaleTo ?? motion.pressScale; + + const animatedStyle = useAnimatedStyle(() => ({ + transform: [{ scale: scale.value }], + })); + + return ( + <AnimatedPressable + style={[reducedMotion ? undefined : animatedStyle, style]} + onPressIn={(event) => { + if (!reducedMotion) { + // reanimated `SharedValue.value` is a mutable UI-thread ref by design (not React + // state); the react-compiler lint rule can't tell it apart from a plain object. + // eslint-disable-next-line react-hooks/immutability + scale.value = withTiming(targetScale, { duration: motion.durations.instant }); + } + if (haptic) triggerHaptic(haptic); + onPressIn?.(event); + }} + onPressOut={(event) => { + if (!reducedMotion) { + // eslint-disable-next-line react-hooks/immutability -- see onPressIn above. + scale.value = withTiming(1, { duration: motion.durations.fast }); + } + onPressOut?.(event); + }} + {...rest} + /> + ); +} diff --git a/src/components/ui/relative-time.tsx b/src/components/ui/relative-time.tsx new file mode 100644 index 0000000..82578fe --- /dev/null +++ b/src/components/ui/relative-time.tsx @@ -0,0 +1,19 @@ +import dayjs from 'dayjs'; +import relativeTime from 'dayjs/plugin/relativeTime'; + +import { Text } from './text'; + +dayjs.extend(relativeTime); + +interface RelativeTimeProps { + date: string | Date; + variant?: 'caption' | 'body'; +} + +export function RelativeTime({ date, variant = 'caption' }: RelativeTimeProps) { + return ( + <Text variant={variant} color="secondary"> + {dayjs(date).fromNow()} + </Text> + ); +} diff --git a/src/components/ui/screen-header.tsx b/src/components/ui/screen-header.tsx new file mode 100644 index 0000000..05c6061 --- /dev/null +++ b/src/components/ui/screen-header.tsx @@ -0,0 +1,70 @@ +import type { PropsWithChildren, ReactNode } from 'react'; +import { StyleSheet, View } from 'react-native'; + +import { useTheme } from '@/theme/use-theme'; + +import { Text } from './text'; + +interface ScreenHeaderProps extends PropsWithChildren { + title?: string; + subtitle?: string; + leading?: ReactNode; + trailing?: ReactNode; + /** Renders `title` with the `display` variant instead of `title` — for top-level tab screens + * that want a large, prominent heading. */ + large?: boolean; +} + +/** Screen-level header row: optional leading slot (back button, avatar), title/subtitle stack, + * trailing slot (actions), and a `children` slot below for search bars / filter rows. */ +export function ScreenHeader({ + title, + subtitle, + leading, + trailing, + large = false, + children, +}: ScreenHeaderProps) { + const { spacing } = useTheme(); + + return ( + <View + style={[ + styles.container, + { paddingHorizontal: spacing.md, paddingTop: spacing.sm, gap: spacing.sm }, + ]} + > + <View style={[styles.row, { gap: spacing.sm }]}> + {leading} + <View style={styles.titleBlock}> + {title ? ( + <Text variant={large ? 'display' : 'heading'} numberOfLines={1}> + {title} + </Text> + ) : null} + {subtitle ? ( + <Text variant="caption" color="secondary" numberOfLines={1}> + {subtitle} + </Text> + ) : null} + </View> + {trailing ? <View style={[styles.row, { gap: spacing.xs }]}>{trailing}</View> : null} + </View> + {children} + </View> + ); +} + +const styles = StyleSheet.create({ + container: { + width: '100%', + }, + row: { + flexDirection: 'row', + alignItems: 'center', + }, + titleBlock: { + flex: 1, + gap: 2, + }, +}); diff --git a/src/components/ui/screen.tsx b/src/components/ui/screen.tsx new file mode 100644 index 0000000..5331e3a --- /dev/null +++ b/src/components/ui/screen.tsx @@ -0,0 +1,40 @@ +import type { PropsWithChildren } from 'react'; +import type { StyleProp, ViewStyle } from 'react-native'; +import { StyleSheet, View } from 'react-native'; +import { type Edge, SafeAreaView } from 'react-native-safe-area-context'; + +import { useTheme } from '@/theme/use-theme'; + +interface ScreenProps extends PropsWithChildren { + style?: StyleProp<ViewStyle>; + edges?: Edge[]; + padded?: boolean; +} + +/** Base screen wrapper: theme background + safe-area insets. Use for every top-level route. */ +export function Screen({ children, style, edges, padded = false }: ScreenProps) { + const { colors, spacing } = useTheme(); + + return ( + <SafeAreaView + edges={edges} + style={[ + styles.container, + { backgroundColor: colors.background }, + padded && { padding: spacing.md }, + style, + ]} + > + <View style={styles.content}>{children}</View> + </SafeAreaView> + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + }, + content: { + flex: 1, + }, +}); diff --git a/src/components/ui/search-bar.tsx b/src/components/ui/search-bar.tsx new file mode 100644 index 0000000..4caeaff --- /dev/null +++ b/src/components/ui/search-bar.tsx @@ -0,0 +1,69 @@ +import { useTranslation } from 'react-i18next'; +import { StyleSheet, TextInput, View } from 'react-native'; + +import { useTheme } from '@/theme/use-theme'; + +import { Icon } from './icon'; +import { IconButton } from './icon-button'; + +interface SearchBarProps { + value: string; + onChangeText: (text: string) => void; + placeholder?: string; +} + +export function SearchBar({ value, onChangeText, placeholder }: SearchBarProps) { + const { t } = useTranslation(); + const { colors, spacing, radius, scheme } = useTheme(); + + return ( + <View + accessibilityRole="search" + style={[ + styles.container, + { + backgroundColor: colors.surface, + borderRadius: radius.md, + paddingHorizontal: spacing.ms, + gap: spacing.xs, + }, + ]} + > + <Icon name="search" size={18} color={colors.textSecondary} /> + <TextInput + value={value} + onChangeText={onChangeText} + placeholder={placeholder} + placeholderTextColor={colors.textSecondary} + style={[styles.input, { color: colors.text }]} + returnKeyType="search" + autoCorrect={false} + keyboardAppearance={scheme} + /> + {value.length > 0 ? ( + <IconButton + accessibilityLabel={t('common.clear')} + icon="close-circle" + size="sm" + variant="ghost" + tint={colors.textSecondary} + haptic={false} + onPress={() => onChangeText('')} + /> + ) : null} + </View> + ); +} + +const styles = StyleSheet.create({ + container: { + flexDirection: 'row', + alignItems: 'center', + height: 40, + }, + input: { + flex: 1, + fontSize: 16, + height: '100%', + }, +}); diff --git a/src/components/ui/section-header.tsx b/src/components/ui/section-header.tsx new file mode 100644 index 0000000..c4c1ee8 --- /dev/null +++ b/src/components/ui/section-header.tsx @@ -0,0 +1,44 @@ +import { StyleSheet, View } from 'react-native'; + +import { useTheme } from '@/theme/use-theme'; + +import { Text } from './text'; + +interface SectionHeaderProps { + title: string; + action?: { label: string; onPress: () => void }; +} + +export function SectionHeader({ title, action }: SectionHeaderProps) { + const { spacing } = useTheme(); + + return ( + <View + style={[ + styles.container, + { paddingHorizontal: spacing.md, paddingTop: spacing.md, paddingBottom: spacing.xs }, + ]} + > + <Text variant="caption" color="secondary" style={styles.title}> + {title.toUpperCase()} + </Text> + {action ? ( + <Text variant="caption" color="secondary" onPress={action.onPress}> + {action.label} + </Text> + ) : null} + </View> + ); +} + +const styles = StyleSheet.create({ + container: { + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + }, + title: { + fontWeight: '700', + letterSpacing: 0.5, + }, +}); diff --git a/src/components/ui/segmented-tabs.tsx b/src/components/ui/segmented-tabs.tsx new file mode 100644 index 0000000..7227cfa --- /dev/null +++ b/src/components/ui/segmented-tabs.tsx @@ -0,0 +1,134 @@ +import { useEffect, useState } from 'react'; +import type { LayoutChangeEvent } from 'react-native'; +import { I18nManager, Pressable, StyleSheet, View } from 'react-native'; +import Animated, { useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated'; + +import { triggerHaptic } from '@/lib/haptics'; +import { useReducedMotion } from '@/theme/motion'; +import { useTheme } from '@/theme/use-theme'; + +import { Text } from './text'; + +interface SegmentedTabsProps<T extends string> { + options: { value: T; label: string }[]; + value: T; + onChange: (value: T) => void; +} + +export function SegmentedTabs<T extends string>({ + options, + value, + onChange, +}: SegmentedTabsProps<T>) { + const { colors, spacing, radius, motion } = useTheme(); + const reducedMotion = useReducedMotion(); + + const [containerWidth, setContainerWidth] = useState(0); + const indicatorX = useSharedValue(0); + const indicatorWidth = useSharedValue(0); + + const activeIndex = Math.max( + 0, + options.findIndex((option) => option.value === value), + ); + const segmentWidth = containerWidth > 0 ? containerWidth / options.length : 0; + + useEffect(() => { + if (segmentWidth <= 0) return; + // Mirror the indicator's horizontal position in RTL so it tracks the visually-active segment + // (flexDirection isn't flipped here, so the logical index must be translated manually). + const visualIndex = I18nManager.isRTL ? options.length - 1 - activeIndex : activeIndex; + const targetX = visualIndex * segmentWidth + 2; + const targetWidth = segmentWidth - 4; + + if (reducedMotion) { + indicatorX.value = targetX; + indicatorWidth.value = targetWidth; + } else { + indicatorX.value = withTiming(targetX, { duration: motion.durations.base }); + indicatorWidth.value = withTiming(targetWidth, { duration: motion.durations.base }); + } + }, [ + activeIndex, + segmentWidth, + options.length, + reducedMotion, + motion.durations.base, + indicatorX, + indicatorWidth, + ]); + + const indicatorStyle = useAnimatedStyle(() => ({ + transform: [{ translateX: indicatorX.value }], + width: indicatorWidth.value, + })); + + function handleLayout(event: LayoutChangeEvent) { + setContainerWidth(event.nativeEvent.layout.width); + } + + return ( + <View + onLayout={handleLayout} + style={[ + styles.container, + { backgroundColor: colors.surface, borderRadius: radius.md, padding: 2 }, + ]} + > + {containerWidth > 0 ? ( + <Animated.View + style={[ + styles.indicator, + indicatorStyle, + { + backgroundColor: colors.surfaceElevated, + borderRadius: radius.sm + 2, + top: 2, + bottom: 2, + }, + ]} + /> + ) : null} + {options.map((option) => { + const isActive = option.value === value; + return ( + <Pressable + key={option.value} + accessibilityRole="tab" + accessibilityState={{ selected: isActive }} + onPress={() => { + if (option.value !== value) { + triggerHaptic('selection'); + onChange(option.value); + } + }} + style={[styles.segment, { paddingVertical: spacing.xs + 2 }]} + > + <Text variant="caption" color={isActive ? 'primary' : 'secondary'} style={styles.label}> + {option.label} + </Text> + </Pressable> + ); + })} + </View> + ); +} + +const styles = StyleSheet.create({ + container: { + flexDirection: 'row', + position: 'relative', + }, + indicator: { + position: 'absolute', + left: 0, + }, + segment: { + flex: 1, + alignItems: 'center', + zIndex: 1, + }, + label: { + fontWeight: '600', + }, +}); diff --git a/src/components/ui/sheet.tsx b/src/components/ui/sheet.tsx new file mode 100644 index 0000000..ad5f67c --- /dev/null +++ b/src/components/ui/sheet.tsx @@ -0,0 +1,136 @@ +import BottomSheet, { + BottomSheetBackdrop, + type BottomSheetBackdropProps, + BottomSheetModal, + BottomSheetView, +} from '@gorhom/bottom-sheet'; +import { forwardRef, type PropsWithChildren, type ReactNode, useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; +import { StyleSheet, View } from 'react-native'; + +import { useTheme } from '@/theme/use-theme'; + +import { IconButton } from './icon-button'; +import { Text } from './text'; + +interface SheetProps extends PropsWithChildren { + snapPoints?: (string | number)[]; + onDismiss?: () => void; +} + +/** Wraps @gorhom/bottom-sheet with our theme colors and a tap-to-dismiss backdrop. Used for + * filter/saved-replies/assignment sheets — see plan's "not routes" note on these. */ +export const Sheet = forwardRef<BottomSheet, SheetProps>(function Sheet( + { children, snapPoints = ['50%', '90%'], onDismiss }, + ref, +) { + const { colors, radius } = useTheme(); + + const renderBackdrop = useCallback( + (props: BottomSheetBackdropProps) => ( + <BottomSheetBackdrop {...props} disappearsOnIndex={-1} appearsOnIndex={0} /> + ), + [], + ); + + return ( + <BottomSheet + ref={ref} + index={-1} + snapPoints={snapPoints} + enablePanDownToClose + onClose={onDismiss} + backdropComponent={renderBackdrop} + backgroundStyle={{ backgroundColor: colors.surfaceElevated, borderRadius: radius.xl }} + handleIndicatorStyle={{ backgroundColor: colors.border }} + > + <BottomSheetView style={{ flex: 1 }}>{children}</BottomSheetView> + </BottomSheet> + ); +}); + +interface SheetModalProps extends PropsWithChildren { + snapPoints?: (string | number)[]; + onDismiss?: () => void; +} + +/** Modal variant using `@gorhom/bottom-sheet`'s `BottomSheetModal` — renders in a portal above + * everything else (including the tab bar), unlike the inline `Sheet` above which is constrained + * to wherever it's mounted in the tree. Requires a `BottomSheetModalProvider` ancestor (already + * mounted once at the app root in `src/app/_layout.tsx`). Use this for sheets opened from screens + * nested under the tab navigator that need to visually overlay the tab bar (field editors, + * composer action sheets, etc). */ +export const SheetModal = forwardRef<BottomSheetModal, SheetModalProps>(function SheetModal( + { children, snapPoints = ['50%', '90%'], onDismiss }, + ref, +) { + const { colors, radius } = useTheme(); + + const renderBackdrop = useCallback( + (props: BottomSheetBackdropProps) => ( + <BottomSheetBackdrop {...props} disappearsOnIndex={-1} appearsOnIndex={0} /> + ), + [], + ); + + return ( + <BottomSheetModal + ref={ref} + index={0} + snapPoints={snapPoints} + enablePanDownToClose + onDismiss={onDismiss} + backdropComponent={renderBackdrop} + backgroundStyle={{ backgroundColor: colors.surfaceElevated, borderRadius: radius.xl }} + handleIndicatorStyle={{ backgroundColor: colors.border }} + > + <BottomSheetView style={{ flex: 1 }}>{children}</BottomSheetView> + </BottomSheetModal> + ); +}); + +interface SheetHeaderProps { + title: string; + onClose?: () => void; + trailing?: ReactNode; +} + +/** Standard sheet header row: title, optional trailing slot, optional close button. Not required + * — sheets with fully custom headers can skip it. */ +export function SheetHeader({ title, onClose, trailing }: SheetHeaderProps) { + const { t } = useTranslation(); + const { spacing } = useTheme(); + + return ( + <View + style={[ + styles.header, + { paddingHorizontal: spacing.md, paddingBottom: spacing.sm, gap: spacing.sm }, + ]} + > + <Text variant="title" style={styles.title} numberOfLines={1}> + {title} + </Text> + {trailing} + {onClose ? ( + <IconButton + accessibilityLabel={t('common.close', { defaultValue: 'Close' })} + icon="close" + size="sm" + variant="tonal" + onPress={onClose} + /> + ) : null} + </View> + ); +} + +const styles = StyleSheet.create({ + header: { + flexDirection: 'row', + alignItems: 'center', + }, + title: { + flex: 1, + }, +}); diff --git a/src/components/ui/skeleton.tsx b/src/components/ui/skeleton.tsx new file mode 100644 index 0000000..7e9654a --- /dev/null +++ b/src/components/ui/skeleton.tsx @@ -0,0 +1,109 @@ +import { useEffect } from 'react'; +import type { StyleProp, ViewStyle } from 'react-native'; +import { StyleSheet, View } from 'react-native'; +import Animated, { + cancelAnimation, + Easing, + useAnimatedStyle, + useSharedValue, + withRepeat, + withTiming, +} from 'react-native-reanimated'; + +import { useTheme } from '@/theme/use-theme'; + +interface SkeletonProps { + width?: number | `${number}%`; + height?: number; + borderRadius?: number; + style?: StyleProp<ViewStyle>; +} + +const PULSE_DURATION_MS = 800; + +/** Pulsing placeholder block for loading states. Compositor-friendly (opacity only). */ +export function Skeleton({ width = '100%', height = 16, borderRadius = 6, style }: SkeletonProps) { + const { colors } = useTheme(); + const opacity = useSharedValue(0.5); + + useEffect(() => { + opacity.value = withRepeat( + withTiming(1, { duration: PULSE_DURATION_MS, easing: Easing.inOut(Easing.ease) }), + -1, + true, + ); + return () => cancelAnimation(opacity); + }, [opacity]); + + const animatedStyle = useAnimatedStyle(() => ({ opacity: opacity.value })); + + return ( + <Animated.View + style={[ + { width, height, borderRadius, backgroundColor: colors.surface }, + animatedStyle, + style, + ]} + /> + ); +} + +const rowStyles = StyleSheet.create({ + row: { + flexDirection: 'row', + alignItems: 'center', + padding: 12, + gap: 12, + }, + body: { + flex: 1, + gap: 6, + }, +}); + +/** Loading-state row shaped like a conversation list row: avatar circle + two lines of text + + * a small trailing meta block. Used by the inbox skeleton list (Phase 3). */ +function ConversationRow() { + return ( + <View style={rowStyles.row}> + <Skeleton width={52} height={52} borderRadius={26} /> + <View style={rowStyles.body}> + <Skeleton width="60%" height={16} /> + <Skeleton width="90%" height={13} /> + </View> + <Skeleton width={32} height={11} /> + </View> + ); +} + +/** Loading-state row shaped like a contact list row: avatar circle + name + subtitle line. */ +function ContactRow() { + return ( + <View style={rowStyles.row}> + <Skeleton width={44} height={44} borderRadius={22} /> + <View style={rowStyles.body}> + <Skeleton width="50%" height={16} /> + <Skeleton width="70%" height={13} /> + </View> + </View> + ); +} + +/** Loading-state placeholder for a single chat message bubble — a rounded block roughly the size + * of a short/medium message. `align` picks which side it renders on. */ +function MessageRow({ align = 'start' }: { align?: 'start' | 'end' }) { + return ( + <View style={[rowStyles.row, { justifyContent: align === 'end' ? 'flex-end' : 'flex-start' }]}> + <Skeleton width={align === 'end' ? '45%' : '60%'} height={40} borderRadius={18} /> + </View> + ); +} + +/** Preset loading-row shapes for the three list contexts that need skeleton placeholders: + * conversations, contacts, and chat messages. Generic enough for Phase 3/4/5 list screens to + * `.map()` a handful of these while data loads. */ +export const SkeletonRow = { + Conversation: ConversationRow, + Contact: ContactRow, + Message: MessageRow, +}; diff --git a/src/components/ui/surface.tsx b/src/components/ui/surface.tsx new file mode 100644 index 0000000..523571a --- /dev/null +++ b/src/components/ui/surface.tsx @@ -0,0 +1,62 @@ +import type { PropsWithChildren } from 'react'; +import type { StyleProp, ViewStyle } from 'react-native'; +import { StyleSheet, View } from 'react-native'; + +import type { radiusTokens, spacingTokens } from '@/theme/tokens'; +import { useTheme } from '@/theme/use-theme'; + +interface SurfaceProps extends PropsWithChildren { + /** Which raised surface level to sit on — 0 is the canvas-adjacent level, 1/2 progressively + * more raised. Defaults to 0 (flush with the canvas). */ + level?: 0 | 1 | 2; + radius?: keyof typeof radiusTokens; + bordered?: boolean; + elevation?: 0 | 1 | 2 | 3; + padding?: keyof typeof spacingTokens; + style?: StyleProp<ViewStyle>; +} + +const SURFACE_COLOR_KEYS = ['surface0', 'surface1', 'surface2'] as const; + +/** Generic raised-surface container: resolves `level` to the matching `surfaceN` token, applies + * radius/border/elevation/padding from token keys instead of raw values. Use `Card` below for the + * common bordered-elevated-card shape. */ +export function Surface({ + level = 0, + radius, + bordered = false, + elevation = 0, + padding, + style, + children, +}: SurfaceProps) { + const theme = useTheme(); + const { colors, radius: radiusScale, elevation: elevationScale, spacing } = theme; + + const backgroundColor = colors[SURFACE_COLOR_KEYS[level]]; + const elevationStyle = elevationScale[elevation]; + + return ( + <View + style={[ + { + backgroundColor, + borderRadius: radius ? radiusScale[radius] : undefined, + borderWidth: bordered ? StyleSheet.hairlineWidth : undefined, + borderColor: bordered ? colors.borderSubtle : undefined, + padding: padding ? spacing[padding] : undefined, + }, + elevation > 0 ? elevationStyle : undefined, + style, + ]} + > + {children} + </View> + ); +} + +/** Preconfigured Surface for the common "card" shape used across rows/panels: level 1, bordered, + * medium radius, subtle elevation. Any prop can still be overridden by the caller. */ +export function Card(props: SurfaceProps) { + return <Surface level={1} bordered radius="md" elevation={1} {...props} />; +} diff --git a/src/components/ui/swipeable-row.tsx b/src/components/ui/swipeable-row.tsx new file mode 100644 index 0000000..a9b14ab --- /dev/null +++ b/src/components/ui/swipeable-row.tsx @@ -0,0 +1,181 @@ +import type { ReactNode } from 'react'; +import { useCallback, useRef } from 'react'; +import { I18nManager, Pressable, StyleSheet, View } from 'react-native'; +import type { SharedValue } from 'react-native-reanimated'; +import { runOnJS, useAnimatedReaction, useSharedValue } from 'react-native-reanimated'; +import Swipeable, { type SwipeableMethods } from 'react-native-gesture-handler/ReanimatedSwipeable'; + +import { triggerHaptic } from '@/lib/haptics'; +import { useTheme } from '@/theme/use-theme'; + +import { Icon, type IconName } from './icon'; +import { Text } from './text'; + +export interface SwipeAction { + icon: IconName; + label: string; + color: string; + onPress: () => void; + destructive?: boolean; +} + +interface SwipeableRowProps { + leftActions?: SwipeAction[]; + rightActions?: SwipeAction[]; + onSwipeOpen?: (side: 'left' | 'right') => void; + children: ReactNode; +} + +const ACTION_WIDTH = 76; +// Fraction of one action's width past which crossing counts as "revealed" for haptic purposes. +const HAPTIC_PROGRESS_THRESHOLD = 0.9; + +function ActionButton({ action, width }: { action: SwipeAction; width: number }) { + const { colors, spacing } = useTheme(); + + return ( + <Pressable + accessibilityRole="button" + accessibilityLabel={action.label} + onPress={action.onPress} + style={[ + styles.action, + { + width, + backgroundColor: action.destructive ? colors.danger : action.color, + gap: spacing.xxs, + }, + ]} + > + <Icon name={action.icon} size={20} color={colors.onBrand} /> + <Text variant="micro" style={{ color: colors.onBrand, fontWeight: '600' }} numberOfLines={1}> + {action.label} + </Text> + </Pressable> + ); +} + +function ActionsPanel({ actions, side }: { actions: SwipeAction[]; side: 'left' | 'right' }) { + const width = ACTION_WIDTH * actions.length; + // In RTL, flip which physical side "start" actions render on so they stay at the reading + // start of the row, matching the swap the plan calls for. + const isRTL = I18nManager.isRTL; + const renderSide = isRTL ? (side === 'left' ? 'right' : 'left') : side; + + return ( + <View + style={[ + styles.actionsPanel, + { width }, + renderSide === 'left' ? styles.actionsLeft : styles.actionsRight, + ]} + > + {actions.map((action) => ( + <ActionButton key={action.label} action={action} width={ACTION_WIDTH} /> + ))} + </View> + ); +} + +/** Row wrapper with swipe-to-reveal action panels on either edge. Wraps RNGH's + * `ReanimatedSwipeable`. `leftActions`/`rightActions` are logical (start/end in LTR); when + * `I18nManager.isRTL` is true, the rendered panel sides swap so "start" actions stay at the + * visual start of the row. Fires a 'medium' haptic once a swipe crosses the reveal threshold. */ +export function SwipeableRow({ + leftActions, + rightActions, + onSwipeOpen, + children, +}: SwipeableRowProps) { + const swipeableRef = useRef<SwipeableMethods>(null); + const hasFiredHapticRef = useRef(false); + const progressShared = useSharedValue(0); + + const fireHapticOnce = useCallback(() => { + if (!hasFiredHapticRef.current) { + hasFiredHapticRef.current = true; + triggerHaptic('medium'); + } + }, []); + + const resetHapticGuard = useCallback(() => { + hasFiredHapticRef.current = false; + }, []); + + useAnimatedReaction( + () => progressShared.value, + (progress, previous) => { + if (progress >= HAPTIC_PROGRESS_THRESHOLD && (previous ?? 0) < HAPTIC_PROGRESS_THRESHOLD) { + runOnJS(fireHapticOnce)(); + } else if ( + progress < HAPTIC_PROGRESS_THRESHOLD && + (previous ?? 0) >= HAPTIC_PROGRESS_THRESHOLD + ) { + runOnJS(resetHapticGuard)(); + } + }, + ); + + const trackProgress = useCallback( + (progress: SharedValue<number>) => { + 'worklet'; + // reanimated `SharedValue.value` is a mutable UI-thread ref, not React state — see + // pressable-scale.tsx for the fuller explanation of this lint false-positive. + // eslint-disable-next-line react-hooks/immutability + progressShared.value = progress.value; + }, + [progressShared], + ); + + const renderLeftActions = leftActions?.length + ? (progress: SharedValue<number>) => { + trackProgress(progress); + return <ActionsPanel actions={leftActions} side="left" />; + } + : undefined; + + const renderRightActions = rightActions?.length + ? (progress: SharedValue<number>) => { + trackProgress(progress); + return <ActionsPanel actions={rightActions} side="right" />; + } + : undefined; + + return ( + <Swipeable + ref={swipeableRef} + friction={2} + leftThreshold={ACTION_WIDTH / 2} + rightThreshold={ACTION_WIDTH / 2} + renderLeftActions={I18nManager.isRTL ? renderRightActions : renderLeftActions} + renderRightActions={I18nManager.isRTL ? renderLeftActions : renderRightActions} + onSwipeableOpen={(direction) => { + // RNGH reports the physically-rendered side; translate back to logical start/end. + const logicalSide = I18nManager.isRTL + ? direction === 'left' + ? 'right' + : 'left' + : direction; + onSwipeOpen?.(logicalSide); + }} + > + {children} + </Swipeable> + ); +} + +const styles = StyleSheet.create({ + actionsPanel: { + flexDirection: 'row', + }, + actionsLeft: { + justifyContent: 'flex-start', + }, + actionsRight: { + justifyContent: 'flex-end', + }, + action: { + alignItems: 'center', + justifyContent: 'center', + }, +}); diff --git a/src/components/ui/text-field.tsx b/src/components/ui/text-field.tsx new file mode 100644 index 0000000..11ad6d6 --- /dev/null +++ b/src/components/ui/text-field.tsx @@ -0,0 +1,117 @@ +import { forwardRef, useState } from 'react'; +import type { TextInput as RNTextInputType } from 'react-native'; +import { StyleSheet, TextInput, View } from 'react-native'; + +import { useTheme } from '@/theme/use-theme'; + +import { IconButton } from './icon-button'; +import type { IconName } from './icon'; +import { Text } from './text'; + +interface TextFieldProps extends React.ComponentProps<typeof TextInput> { + label?: string; + error?: string; + /** Renders an IconButton inside the field's trailing edge (e.g. a password show/hide toggle). + * Purely additive to the base field — omit both props for the original single-input layout. */ + trailingIcon?: IconName; + /** Required when `trailingIcon` is set — forwarded to the inner IconButton's + * `accessibilityLabel` so the toggle is independently announced by screen readers. */ + trailingIconAccessibilityLabel?: string; + onTrailingIconPress?: () => void; +} + +export const TextField = forwardRef<RNTextInputType, TextFieldProps>(function TextField( + { + label, + error, + trailingIcon, + trailingIconAccessibilityLabel, + onTrailingIconPress, + style, + onFocus, + onBlur, + ...rest + }, + ref, +) { + const { colors, spacing, radius, typography, scheme } = useTheme(); + const [isFocused, setIsFocused] = useState(false); + + return ( + <View style={styles.container}> + {label ? ( + <Text variant="caption" color="secondary" style={styles.label}> + {label} + </Text> + ) : null} + <View style={styles.inputRow}> + <TextInput + ref={ref} + placeholderTextColor={colors.textSecondary} + keyboardAppearance={scheme} + onFocus={(e) => { + setIsFocused(true); + onFocus?.(e); + }} + onBlur={(e) => { + setIsFocused(false); + onBlur?.(e); + }} + style={[ + styles.input, + { + backgroundColor: colors.surface, + borderColor: error ? colors.danger : isFocused ? colors.primary : colors.border, + borderRadius: radius.md, + color: colors.text, + fontSize: typography.body.fontSize, + paddingHorizontal: spacing.base, + paddingEnd: trailingIcon ? spacing.xl + spacing.sm : spacing.base, + }, + style, + ]} + {...rest} + /> + {trailingIcon ? ( + <View style={styles.trailingIcon}> + <IconButton + accessibilityLabel={trailingIconAccessibilityLabel ?? ''} + icon={trailingIcon} + size="sm" + variant="ghost" + onPress={onTrailingIconPress} + /> + </View> + ) : null} + </View> + {error ? ( + <Text variant="caption" color="danger" style={styles.error}> + {error} + </Text> + ) : null} + </View> + ); +}); + +const styles = StyleSheet.create({ + container: { + gap: 4, + }, + label: { + marginStart: 2, + }, + inputRow: { + justifyContent: 'center', + }, + input: { + height: 48, + borderWidth: StyleSheet.hairlineWidth, + }, + trailingIcon: { + position: 'absolute', + end: 4, + }, + error: { + marginStart: 2, + }, +}); diff --git a/src/components/ui/text.tsx b/src/components/ui/text.tsx new file mode 100644 index 0000000..124a5e2 --- /dev/null +++ b/src/components/ui/text.tsx @@ -0,0 +1,94 @@ +import type { ComponentProps } from 'react'; +import { Text as RNText, StyleSheet } from 'react-native'; +import { useTranslation } from 'react-i18next'; + +import { resolveDisplayFamily } from '@/theme/typography'; +import { useTheme } from '@/theme/use-theme'; +import type { typographyTokens } from '@/theme/tokens'; + +type Variant = keyof typeof typographyTokens; +type ColorRole = + | 'primary' + | 'secondary' + | 'tertiary' + | 'inverse' + | 'brand' + | 'danger' + | 'success' + | 'warning' + | 'onBrand'; + +/** Variants whose weight (600/700) qualifies as "chrome" text — these get the Plus Jakarta Sans + * display family (language permitting); body/callout/caption/micro stay on the system font since + * they render user-generated content. */ +const DISPLAY_FAMILY_VARIANTS: readonly Variant[] = [ + 'display', + 'heading', + 'title', + 'subtitle', + 'label', +]; + +interface TextProps extends ComponentProps<typeof RNText> { + variant?: Variant; + color?: ColorRole; + /** Applies tabular-nums font variant for aligned numeric columns (counts, timestamps). */ + numeric?: boolean; +} + +/** Themed text primitive: variant maps to typographyTokens, color maps to a semantic palette + * role instead of a raw hex value. Display-weight variants resolve a locale-aware display font + * family; `maxFontSizeMultiplier` is read from the variant token so Dynamic Type scaling stays + * within a sane cap per text role. */ +export function Text({ + variant = 'body', + color = 'primary', + numeric = false, + style, + ...rest +}: TextProps) { + const { colors, typography } = useTheme(); + const { i18n } = useTranslation(); + + const colorMap: Record<ColorRole, string> = { + primary: colors.textPrimary, + secondary: colors.textSecondary, + tertiary: colors.textTertiary, + inverse: colors.textInverse, + brand: colors.brand, + danger: colors.danger, + success: colors.success, + warning: colors.warning, + onBrand: colors.onBrand, + }; + + const token = typography[variant]; + const fontFamily = DISPLAY_FAMILY_VARIANTS.includes(variant) + ? resolveDisplayFamily(i18n.language, token.fontWeight === '700' ? '700' : '600') + : undefined; + + return ( + <RNText + maxFontSizeMultiplier={token.maxFontSizeMultiplier} + style={[ + styles.base, + { + fontSize: token.fontSize, + lineHeight: token.lineHeight, + fontWeight: token.fontWeight, + color: colorMap[color], + fontFamily, + textTransform: 'textTransform' in token ? token.textTransform : undefined, + letterSpacing: 'letterSpacing' in token ? token.letterSpacing : undefined, + fontVariant: numeric ? ['tabular-nums'] : undefined, + }, + style, + ]} + {...rest} + /> + ); +} + +const styles = StyleSheet.create({ + base: {}, +}); diff --git a/src/components/ui/toast.tsx b/src/components/ui/toast.tsx new file mode 100644 index 0000000..9f52807 --- /dev/null +++ b/src/components/ui/toast.tsx @@ -0,0 +1,197 @@ +import { + createContext, + type PropsWithChildren, + useCallback, + useContext, + useEffect, + useRef, + useState, +} from 'react'; +import { StyleSheet } from 'react-native'; +import Animated, { useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated'; +import { useSafeAreaInsets } from 'react-native-safe-area-context'; + +import { useReducedMotion } from '@/theme/motion'; +import { useTheme } from '@/theme/use-theme'; + +import { Icon } from './icon'; +import { Text } from './text'; + +type Tone = 'neutral' | 'success' | 'danger' | 'info'; + +interface ToastOptions { + message: string; + tone?: Tone; + action?: { label: string; onPress: () => void }; + durationMs?: number; +} + +interface QueuedToast extends ToastOptions { + id: number; +} + +type ShowToast = (opts: ToastOptions) => void; + +const ToastContext = createContext<ShowToast | null>(null); + +const DEFAULT_DURATION_MS = 3000; +const TOAST_HEIGHT = 48; + +/** Mount once near the app root. Provides `useToast()` to any descendant; renders a single toast + * at a time from a simple FIFO queue — a new toast while one is showing waits its turn rather + * than stacking or replacing. The head of `queue` (index 0) is always the currently-displayed + * toast, so there's no separate "current" state to keep in sync — dismissing just shifts the + * array, which is itself the transition to whatever's next. */ +export function ToastProvider({ children }: PropsWithChildren) { + const insets = useSafeAreaInsets(); + const { colors, radius, spacing, motion } = useTheme(); + const reducedMotion = useReducedMotion(); + + const [queue, setQueue] = useState<QueuedToast[]>([]); + const nextId = useRef(0); + const translateY = useSharedValue(TOAST_HEIGHT + 40); + const opacity = useSharedValue(0); + + const current = queue[0] ?? null; + + const show = useCallback<ShowToast>((opts) => { + nextId.current += 1; + setQueue((prev) => [...prev, { ...opts, id: nextId.current }]); + }, []); + + const dismissCurrent = useCallback(() => { + setQueue((prev) => prev.slice(1)); + }, []); + + // Animate the current toast in, then out after its duration, then dismiss it so the next + // queued toast (if any) becomes `current` on the following render. + useEffect(() => { + if (!current) return undefined; + + if (reducedMotion) { + translateY.value = 0; + opacity.value = 1; + } else { + translateY.value = withTiming(0, { duration: motion.durations.enter }); + opacity.value = withTiming(1, { duration: motion.durations.enter }); + } + + const duration = current.durationMs ?? DEFAULT_DURATION_MS; + const timer = setTimeout(() => { + if (reducedMotion) { + translateY.value = TOAST_HEIGHT + 40; + opacity.value = 0; + dismissCurrent(); + } else { + opacity.value = withTiming(0, { duration: motion.durations.exit }); + translateY.value = withTiming(TOAST_HEIGHT + 40, { duration: motion.durations.exit }); + setTimeout(dismissCurrent, motion.durations.exit); + } + }, duration); + + return () => clearTimeout(timer); + // eslint-disable-next-line react-hooks/exhaustive-deps -- shared values are stable refs, motion/reducedMotion are effectively static. + }, [current, dismissCurrent]); + + const animatedStyle = useAnimatedStyle(() => ({ + opacity: opacity.value, + transform: [{ translateY: translateY.value }], + })); + + const toneColor: Record<Tone, string> = { + neutral: colors.textInverse, + success: colors.success, + danger: colors.danger, + info: colors.info, + }; + + return ( + <ToastContext.Provider value={show}> + {children} + {current ? ( + <Animated.View + pointerEvents="box-none" + style={[ + styles.host, + { bottom: insets.bottom + spacing.md }, + reducedMotion ? undefined : animatedStyle, + ]} + > + <Animated.View + accessibilityLiveRegion="polite" + accessibilityRole="alert" + style={[ + styles.toast, + { + backgroundColor: colors.textPrimary, + borderRadius: radius.md, + paddingVertical: spacing.sm, + paddingHorizontal: spacing.md, + gap: spacing.sm, + }, + ]} + > + {current.tone && current.tone !== 'neutral' ? ( + <Icon + name={ + current.tone === 'success' + ? 'checkmark-circle' + : current.tone === 'danger' + ? 'alert-circle' + : 'information-circle' + } + size={18} + color={toneColor[current.tone]} + /> + ) : null} + <Text variant="callout" color="inverse" style={styles.message} numberOfLines={2}> + {current.message} + </Text> + {current.action ? ( + <Text + variant="callout" + color="inverse" + style={styles.action} + onPress={() => { + current.action?.onPress(); + dismissCurrent(); + }} + > + {current.action.label} + </Text> + ) : null} + </Animated.View> + </Animated.View> + ) : null} + </ToastContext.Provider> + ); +} + +/** Returns a function to enqueue a toast. Must be called under a `ToastProvider`. */ +export function useToast(): ShowToast { + const show = useContext(ToastContext); + if (!show) { + throw new Error('useToast must be used within a ToastProvider'); + } + return show; +} + +const styles = StyleSheet.create({ + host: { + position: 'absolute', + start: 16, + end: 16, + alignItems: 'center', + }, + toast: { + flexDirection: 'row', + alignItems: 'center', + width: '100%', + }, + message: { + flex: 1, + }, + action: { + fontWeight: '700', + }, +}); diff --git a/src/components/ui/toggle-row.test.tsx b/src/components/ui/toggle-row.test.tsx new file mode 100644 index 0000000..47c65a1 --- /dev/null +++ b/src/components/ui/toggle-row.test.tsx @@ -0,0 +1,36 @@ +import { fireEvent, render, screen } from '@testing-library/react-native'; + +import { initI18n } from '@/i18n'; + +import { ToggleRow } from './toggle-row'; + +beforeAll(() => { + initI18n(); +}); + +describe('ToggleRow', () => { + it('exposes switch role and checked accessibility state', async () => { + await render(<ToggleRow label="Push notifications" value onValueChange={() => {}} />); + + const control = screen.getByRole('switch', { name: 'Push notifications' }); + expect(control.props.accessibilityState).toMatchObject({ checked: true }); + }); + + it('reflects unchecked state', async () => { + await render(<ToggleRow label="Push notifications" value={false} onValueChange={() => {}} />); + + const control = screen.getByRole('switch', { name: 'Push notifications' }); + expect(control.props.accessibilityState).toMatchObject({ checked: false }); + }); + + it('calls onValueChange with the new value when toggled', async () => { + const onValueChange = jest.fn(); + await render( + <ToggleRow label="Push notifications" value={false} onValueChange={onValueChange} />, + ); + + fireEvent(screen.getByRole('switch', { name: 'Push notifications' }), 'valueChange', true); + + expect(onValueChange).toHaveBeenCalledWith(true); + }); +}); diff --git a/src/components/ui/toggle-row.tsx b/src/components/ui/toggle-row.tsx new file mode 100644 index 0000000..65d1df9 --- /dev/null +++ b/src/components/ui/toggle-row.tsx @@ -0,0 +1,58 @@ +import { StyleSheet, Switch, View } from 'react-native'; + +import { triggerHaptic } from '@/lib/haptics'; +import { useTheme } from '@/theme/use-theme'; + +import { Text } from './text'; + +interface ToggleRowProps { + label: string; + description?: string; + value: boolean; + onValueChange: (value: boolean) => void; + disabled?: boolean; +} + +export function ToggleRow({ label, description, value, onValueChange, disabled }: ToggleRowProps) { + const { colors, spacing } = useTheme(); + + function handleChange(next: boolean) { + triggerHaptic('selection'); + onValueChange(next); + } + + return ( + <View style={[styles.row, { paddingVertical: spacing.ms, paddingHorizontal: spacing.md }]}> + <View style={styles.body}> + <Text variant="body">{label}</Text> + {description ? ( + <Text variant="caption" color="secondary"> + {description} + </Text> + ) : null} + </View> + <Switch + accessibilityLabel={label} + accessibilityRole="switch" + accessibilityState={{ checked: value, disabled }} + value={value} + onValueChange={handleChange} + disabled={disabled} + trackColor={{ true: colors.primary, false: colors.border }} + /> + </View> + ); +} + +const styles = StyleSheet.create({ + row: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + }, + body: { + flex: 1, + gap: 2, + marginEnd: 12, + }, +}); diff --git a/src/components/workspace-blocked-gate.tsx b/src/components/workspace-blocked-gate.tsx new file mode 100644 index 0000000..0e9789a --- /dev/null +++ b/src/components/workspace-blocked-gate.tsx @@ -0,0 +1,58 @@ +import { useTranslation } from 'react-i18next'; +import { View } from 'react-native'; + +import { useSwitchWorkspace } from '@/features/workspaces/api/use-switch-workspace'; +import { useTheme } from '@/theme/use-theme'; + +import { Button } from './ui/button'; +import { Icon } from './ui/icon'; +import { Text } from './ui/text'; + +interface WorkspaceBlockedGateProps { + reason: 'mac' | 'trialExpired' | 'unknown'; + message: string; +} + +/** + * Full-screen state for a 402 `workspaceBlocked` error on a workspace-scoped screen query (e.g. + * the conversations list itself failing, not just a composer send — Phase 4 already handles the + * send-time case locally with quota-banner.tsx). Distinguishes MAC-limit vs trial-expired copy per + * the plan, with a "switch workspace" escape hatch mirroring the settings screen's own switch flow. + */ +export function WorkspaceBlockedGate({ reason, message }: WorkspaceBlockedGateProps) { + const { t } = useTranslation(); + const { colors, spacing } = useTheme(); + const switchWorkspace = useSwitchWorkspace(); + + const copy = + reason === 'mac' + ? t('errors.workspaceBlockedMac') + : reason === 'trialExpired' + ? t('errors.workspaceBlockedTrialExpired') + : message; + + function handleSwitchWorkspace() { + switchWorkspace(null); + } + + return ( + <View + style={{ + flex: 1, + alignItems: 'center', + justifyContent: 'center', + padding: spacing.xl, + gap: spacing.md, + }} + > + <Icon name="lock-closed-outline" size={40} color={colors.textSecondary} /> + <Text variant="title" style={{ textAlign: 'center' }}> + {t('errors.workspaceBlockedTitle')} + </Text> + <Text variant="body" color="secondary" style={{ textAlign: 'center' }}> + {copy} + </Text> + <Button label={t('settings.switchWorkspace')} onPress={handleSwitchWorkspace} /> + </View> + ); +} diff --git a/src/config/env.ts b/src/config/env.ts new file mode 100644 index 0000000..76dbd7b --- /dev/null +++ b/src/config/env.ts @@ -0,0 +1,29 @@ +import Constants from 'expo-constants'; + +interface AppExtraConfig { + apiBaseUrl: string; + wsUrl: string; +} + +function readExtra(): AppExtraConfig { + const extra = Constants.expoConfig?.extra as Partial<AppExtraConfig> | undefined; + + if (!extra?.apiBaseUrl) { + throw new Error( + 'Missing "extra.apiBaseUrl" in Expo config. Check app.config.ts and the API_BASE_URL env var.', + ); + } + if (!extra?.wsUrl) { + throw new Error( + 'Missing "extra.wsUrl" in Expo config. Check app.config.ts and the WS_URL env var.', + ); + } + + return { apiBaseUrl: extra.apiBaseUrl, wsUrl: extra.wsUrl }; +} + +/** + * Tenant-defining, env-driven app configuration. Sourced from app.config.ts `extra`, which in + * turn reads `process.env.API_BASE_URL`/`WS_URL` at Expo config evaluation time. + */ +export const env: AppExtraConfig = readExtra(); diff --git a/src/config/features.ts b/src/config/features.ts new file mode 100644 index 0000000..8ca5468 --- /dev/null +++ b/src/config/features.ts @@ -0,0 +1,12 @@ +/** + * Client-side feature flags. No env override wiring exists yet — `app.config.ts`'s `extra` shape + * has no `features` key, so this is a hardcoded constant until that's worth adding. + * + * `sendSequence`: the backend route needed to enroll a contact in a sequence + * (`POST /workspaces/{workspaceId}/contacts/sequences`) doesn't exist yet — see + * `src/features/sequences/api/use-enroll-contact-in-sequences.ts`. Flip to `true` once that route + * ships and the hand-rolled fetch there is replaced with the generated client. + */ +export const FEATURES = { + sendSequence: false, +} as const; diff --git a/src/features/auth/social-buttons.tsx b/src/features/auth/social-buttons.tsx new file mode 100644 index 0000000..6590afe --- /dev/null +++ b/src/features/auth/social-buttons.tsx @@ -0,0 +1,99 @@ +import { router } from 'expo-router'; +import * as WebBrowser from 'expo-web-browser'; +import { useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { StyleSheet, View } from 'react-native'; + +import { getSession, SOCIAL_CALLBACK_URL, startSocialSignIn } from '@/api/auth-endpoints'; +import { getAuthToken } from '@/api/auth-token'; +import { Button } from '@/components/ui/button'; +import { ErrorBanner } from '@/components/ui/error-banner'; +import { consumePendingDeepLink } from '@/lib/pending-deep-link'; +import { useAuthStore } from '@/stores/use-auth-store'; + +type Provider = 'google' | 'facebook'; + +/** + * Social sign-in buttons. Flow: `startSocialSignIn` gets the provider authorize URL from + * better-auth, `WebBrowser.openAuthSessionAsync` opens it and resolves once the broker redirects + * back to our `chatbotxmobileapp://auth-callback` scheme (see auth-endpoints.ts for the + * trustedOrigins gap that currently blocks this redirect server-side — this UI is correct but + * won't complete end-to-end until that backend change lands). + * + * TOKEN HYDRATION — real, currently-unresolved gap: unlike email/password sign-in, which reads a + * bearer token straight off the `set-auth-token` response header of a same-origin fetch, the OAuth + * dance here happens inside `WebBrowser`'s own isolated browser session/cookie jar, not this app's + * fetch client. Nothing in that flow currently hands this app a bearer token: the callback URL + * `openAuthSessionAsync` resolves with is not parsed for one here (its shape/whether it carries a + * token at all is unverified — parsing an assumed shape would be worse than not assuming), and the + * `getSession()` call below only works if a token is ALREADY in SecureStore, which it isn't yet at + * this point in the flow. So this currently cannot complete sign-in even once trustedOrigins is + * fixed, UNLESS the backend's broker relay is changed to also forward a bearer token (e.g. as a + * `token` query param on the `chatbotxmobileapp://auth-callback` redirect, mirroring what + * `set-auth-token` does for same-origin requests) — or a dedicated mobile token-exchange endpoint + * is added. Neither is implemented here; both are backend-shaped decisions outside this repo. + */ +export function SocialButtons() { + const { t } = useTranslation(); + const setSignedIn = useAuthStore((state) => state.setSignedIn); + const [error, setError] = useState<string | null>(null); + const [loadingProvider, setLoadingProvider] = useState<Provider | null>(null); + + const handleSignIn = async (provider: Provider) => { + setError(null); + setLoadingProvider(provider); + try { + const authorizeUrl = await startSocialSignIn(provider); + const result = await WebBrowser.openAuthSessionAsync(authorizeUrl, SOCIAL_CALLBACK_URL); + + if (result.type !== 'success') { + // 'cancel' / 'dismiss' — user backed out, not an error. + return; + } + + // See the token-hydration note above: this only succeeds today if a token already exists in + // SecureStore, which it won't for a fresh social sign-in. Left as the best-available + // client-side step rather than a fabricated token-parsing shortcut. + const token = await getAuthToken(); + const user = token ? await getSession(token) : null; + if (!user) { + setError(t('auth.socialSignInFailed')); + return; + } + + setSignedIn(user); + const pendingDeepLink = user.mustChangePassword ? null : consumePendingDeepLink(); + router.replace((pendingDeepLink ?? '/') as never); + } catch (err) { + setError(err instanceof Error ? err.message : t('errors.unknown')); + } finally { + setLoadingProvider(null); + } + }; + + return ( + <View style={styles.container}> + {error ? <ErrorBanner message={error} /> : null} + <Button + label={t('auth.continueWithGoogle')} + variant="secondary" + loading={loadingProvider === 'google'} + disabled={loadingProvider !== null} + onPress={() => handleSignIn('google')} + /> + <Button + label={t('auth.continueWithFacebook')} + variant="secondary" + loading={loadingProvider === 'facebook'} + disabled={loadingProvider !== null} + onPress={() => handleSignIn('facebook')} + /> + </View> + ); +} + +const styles = StyleSheet.create({ + container: { + gap: 8, + }, +}); diff --git a/src/features/chat/api/generate-client-id.test.ts b/src/features/chat/api/generate-client-id.test.ts new file mode 100644 index 0000000..a358c73 --- /dev/null +++ b/src/features/chat/api/generate-client-id.test.ts @@ -0,0 +1,15 @@ +import { generateClientId } from '@/features/chat/api/generate-client-id'; + +describe('generateClientId', () => { + it('returns a numeric string', () => { + expect(generateClientId()).toMatch(/^\d+$/); + }); + + it('generates mostly-unique values across repeated calls', () => { + // Timestamp-prefixed + 3-digit random suffix: calls within the same millisecond only draw + // from 1000 possible suffixes, so a tight synchronous loop can occasionally collide — this + // is expected per the source's own collision-tolerance comment, not a bug to chase to zero. + const ids = new Set(Array.from({ length: 50 }, () => generateClientId())); + expect(ids.size).toBeGreaterThan(40); + }); +}); diff --git a/src/features/chat/api/generate-client-id.ts b/src/features/chat/api/generate-client-id.ts new file mode 100644 index 0000000..977c3ec --- /dev/null +++ b/src/features/chat/api/generate-client-id.ts @@ -0,0 +1,15 @@ +/** + * The backend's message-create `clientId` field is typed `zodBigintAsString()` server-side (not a + * free-form UUID) — confirmed by the generated schema also carrying `clientId?: string` on message + * resources themselves. A UUID would fail that validation. This generates a timestamp-prefixed + * pseudo-snowflake: always a valid numeric string, and — since it's only ever compared within one + * client's own pending-sends map for the lifetime of the app process — collision odds are + * negligible without needing real snowflake machinery. + */ +export function generateClientId(): string { + const timestamp = Date.now().toString(); + const random = Math.floor(Math.random() * 1000) + .toString() + .padStart(3, '0'); + return `${timestamp}${random}`; +} diff --git a/src/features/chat/api/send-message-multipart.ts b/src/features/chat/api/send-message-multipart.ts new file mode 100644 index 0000000..77c0eee --- /dev/null +++ b/src/features/chat/api/send-message-multipart.ts @@ -0,0 +1,135 @@ +import { getAuthToken } from '@/api/auth-token'; +import { env } from '@/config/env'; + +/** + * Chat message attachments go through the SAME message-create endpoint as text + * (`POST /workspaces/{workspaceId}/conversations/{conversationId}/messages`), but the generated + * TS types are wrong for this case: the backend's real Zod schema + * (aha.chat apps/builder/src/features/messages/schema/mutation.ts) types `files` as + * `z.array(z.instanceof(File)).min(1)`, which openapi-typescript cannot express — it collapsed to + * `files?: unknown[]` under `application/json`. The real wire format for a request WITH files is + * `multipart/form-data` (the backend's `createMessage` handler calls `uploadMultipleFiles` on + * genuine binary uploads), not JSON. The typed `apiClient` assumes JSON everywhere, so attachment + * sends bypass it entirely and use a hand-rolled request here (same pattern as api/auth-endpoints.ts + * for other non-JSON-typed routes), building RN's standard FormData-with-file shape + * (`{ uri, type, name }`) which works directly with expo-image-picker/expo-camera result URIs. + * + * UNVERIFIED: this multipart send has not been exercised against a live signed-in session with a + * real conversation — the request shape is inferred from the backend's Zod schema and RN's FormData + * convention, not observed on the wire. Worth a real-device check before shipping. + * + * Built on XMLHttpRequest rather than `fetch` specifically so upload progress is observable: + * `fetch`'s ReadableStream request body isn't supported for uploads in RN's fetch polyfill, and + * even where it is, there's no cross-platform equivalent of `xhr.upload.onprogress`. RN's XHR + * implementation does implement `upload.onprogress` (backed by the native networking layer), so + * this is the only way to surface a progress ratio to the composer/attachment UI. The success/error + * contract (return value, thrown error types) is kept IDENTICAL to the previous fetch-based + * implementation so use-send-message.ts needs no changes beyond passing through `onProgress`. + */ +export interface MultipartAttachment { + uri: string; + mimeType: string; + fileName: string; +} + +export interface SendMultipartMessageParams { + workspaceId: string; + conversationId: string; + text?: string; + clientId: string; + attachments: MultipartAttachment[]; + inboxId?: string | null; + replyToMessageId?: string; + replyToMessageCreatedAt?: string; + /** Called with a 0-1 ratio as the upload progresses. Best-effort — some environments never + * report `lengthComputable`, in which case this simply never fires. */ + onProgress?: (ratio: number) => void; +} + +export class SendMessageError extends Error { + readonly status: number; + readonly body: unknown; + + constructor(status: number, body: unknown, message: string) { + super(message); + this.name = 'SendMessageError'; + this.status = status; + this.body = body; + } +} + +function parseResponseBody(rawBody: string): unknown { + if (!rawBody) return null; + try { + return JSON.parse(rawBody); + } catch { + // Non-JSON response — leave body null, fall back to statusText below. + return null; + } +} + +function extractMessage(body: unknown, fallback: string): string { + if (body && typeof body === 'object' && 'message' in body && typeof body.message === 'string') { + return body.message; + } + return fallback; +} + +export async function sendMultipartMessage(params: SendMultipartMessageParams): Promise<unknown> { + const token = await getAuthToken(); + const url = `${env.apiBaseUrl}/api/workspaces/${params.workspaceId}/conversations/${params.conversationId}/messages`; + + const form = new FormData(); + if (params.text) form.append('text', params.text); + form.append('clientId', params.clientId); + if (params.inboxId) form.append('inboxId', params.inboxId); + if (params.replyToMessageId) form.append('replyToMessageId', params.replyToMessageId); + if (params.replyToMessageCreatedAt) { + form.append('replyToMessageCreatedAt', params.replyToMessageCreatedAt); + } + for (const attachment of params.attachments) { + // React Native's XHR/FormData accepts this `{ uri, type, name }` object in place of a real + // Blob for file parts — the standard RN convention, distinct from web File/Blob uploads. + form.append('files', { + uri: attachment.uri, + type: attachment.mimeType, + name: attachment.fileName, + } as unknown as Blob); + } + + return new Promise<unknown>((resolve, reject) => { + const xhr = new XMLHttpRequest(); + xhr.open('POST', url, true); + if (token) { + xhr.setRequestHeader('Authorization', `Bearer ${token}`); + } + + if (xhr.upload && params.onProgress) { + xhr.upload.onprogress = (event) => { + if (event.lengthComputable && event.total > 0) { + params.onProgress?.(event.loaded / event.total); + } + }; + } + + xhr.onload = () => { + const body = parseResponseBody(xhr.responseText); + const ok = xhr.status >= 200 && xhr.status < 300; + if (!ok) { + reject(new SendMessageError(xhr.status, body, extractMessage(body, xhr.statusText))); + return; + } + resolve(body); + }; + + xhr.onerror = () => { + reject(new SendMessageError(0, null, 'Network request failed')); + }; + + xhr.onabort = () => { + reject(new SendMessageError(0, null, 'Upload aborted')); + }; + + xhr.send(form); + }); +} diff --git a/src/features/chat/api/use-message-actions.ts b/src/features/chat/api/use-message-actions.ts new file mode 100644 index 0000000..b756dc3 --- /dev/null +++ b/src/features/chat/api/use-message-actions.ts @@ -0,0 +1,129 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query'; + +import { apiClient } from '@/api/client'; +import { ApiError } from '@/api/errors'; +import { queryKeys } from '@/api/query-keys'; +import type { ListMessagesResponse, Message } from './use-messages-infinite'; + +/** + * Message edit/delete/like/hide, matching the exact request shapes read from + * `messagesAPI.{editMessage,deleteMessage,changeMessageAttributes}AuthenticatedAPI` in + * src/api/generated/schema.ts: + * - edit: PATCH .../messages/{messageId} body `{ createdAt, newText, newAttachmentPath?, + * newAttachmentPublicUrl?, newAttachmentMimeType?, newAttachmentName?, newAttachmentSize?, + * removeAttachment? }` — text-only edit here, attachment-replace is out of scope for this phase. + * - delete: DELETE .../messages/{id} body `{ createdAt }`. + * - like/hide: POST .../messages/{messageId}/attributes body `{ createdAt, liked?, hidden? }` — + * this endpoint IS the like/hide mechanism (no separate dedicated endpoints exist). + * + * All three require `createdAt` in the body alongside the path id — the messages table is + * evidently keyed/partitioned by (id, createdAt), not id alone. + */ + +function patchMessageInCache( + queryClient: ReturnType<typeof useQueryClient>, + queryKey: readonly unknown[], + messageId: string, + patch: Partial<Message>, +) { + queryClient.setQueryData<{ pages: ListMessagesResponse[]; pageParams: unknown[] }>( + queryKey, + (old) => { + if (!old) return old; + return { + ...old, + pages: old.pages.map((page) => ({ + ...page, + data: page.data.map((message) => + message.id === messageId ? { ...message, ...patch } : message, + ), + })), + }; + }, + ); +} + +function findCachedMessage( + queryClient: ReturnType<typeof useQueryClient>, + queryKey: readonly unknown[], + messageId: string, +): Message | undefined { + const data = queryClient.getQueryData<{ pages: ListMessagesResponse[] }>(queryKey); + for (const page of data?.pages ?? []) { + const found = page.data.find((message) => message.id === messageId); + if (found) return found; + } + return undefined; +} + +export function useEditMessage(workspaceId: string, conversationId: string) { + const queryClient = useQueryClient(); + const queryKey = queryKeys.ws.messages.list(workspaceId, conversationId); + + return useMutation({ + mutationFn: async (params: { messageId: string; createdAt: string; newText: string }) => { + const { error } = await apiClient.PATCH( + '/workspaces/{workspaceId}/conversations/{conversationId}/messages/{messageId}', + { + params: { path: { workspaceId, conversationId, messageId: params.messageId } }, + body: { createdAt: params.createdAt, newText: params.newText }, + }, + ); + if (error) throw new ApiError(error); + }, + onMutate: (params) => { + const previous = findCachedMessage(queryClient, queryKey, params.messageId); + patchMessageInCache(queryClient, queryKey, params.messageId, { text: params.newText }); + return { previous }; + }, + onError: (_error, params, context) => { + if (!context?.previous) return; + patchMessageInCache(queryClient, queryKey, params.messageId, { text: context.previous.text }); + }, + }); +} + +export function useDeleteMessage(workspaceId: string, conversationId: string) { + const queryClient = useQueryClient(); + const queryKey = queryKeys.ws.messages.list(workspaceId, conversationId); + + return useMutation({ + mutationFn: async (params: { messageId: string; createdAt: string }) => { + const { error } = await apiClient.DELETE( + '/workspaces/{workspaceId}/conversations/{conversationId}/messages/{id}', + { + params: { path: { workspaceId, conversationId, id: params.messageId } }, + body: { createdAt: params.createdAt }, + }, + ); + if (error) throw new ApiError(error); + }, + onMutate: (params) => + patchMessageInCache(queryClient, queryKey, params.messageId, { + deletedAt: new Date().toISOString(), + }), + }); +} + +/** No optimistic cache patch here (unlike edit/delete above) — `attributes` is a loose JSON-value + * union in the generated schema with no reliable current-liked/hidden shape to read or merge into, + * so this just fires the mutation and lets the next list refetch reflect the change. */ +export function useSetMessageAttributes(workspaceId: string, conversationId: string) { + return useMutation({ + mutationFn: async (params: { + messageId: string; + createdAt: string; + liked?: boolean; + hidden?: boolean; + }) => { + const { error } = await apiClient.POST( + '/workspaces/{workspaceId}/conversations/{conversationId}/messages/{messageId}/attributes', + { + params: { path: { workspaceId, conversationId, messageId: params.messageId } }, + body: { createdAt: params.createdAt, liked: params.liked, hidden: params.hidden }, + }, + ); + if (error) throw new ApiError(error); + }, + }); +} diff --git a/src/features/chat/api/use-messages-infinite.ts b/src/features/chat/api/use-messages-infinite.ts new file mode 100644 index 0000000..af8c4a3 --- /dev/null +++ b/src/features/chat/api/use-messages-infinite.ts @@ -0,0 +1,48 @@ +import { useInfiniteQuery } from '@tanstack/react-query'; + +import { apiClient } from '@/api/client'; +import { ApiError } from '@/api/errors'; +import { flattenPages, getNextPageParam } from '@/api/pagination'; +import { queryKeys } from '@/api/query-keys'; +import type { operations } from '@/api/generated/schema'; + +/** + * Types derived directly from the generated schema's `operations` map for + * `messagesAPI.listMessagesAuthenticatedAPI` — `GET /workspaces/{workspaceId}/messages` with + * `conversationId` as a QUERY param (not path-nested, unlike message create/edit/delete). Response + * shape matches api/pagination.ts's `CursorPage<T>` (`{ data, nextCursor, prevCursor }`). + */ +type ListMessagesOperation = operations['messagesAPI.listMessagesAuthenticatedAPI']; +export type ListMessagesResponse = + ListMessagesOperation['responses'][200]['content']['application/json']; +export type Message = ListMessagesResponse['data'][number]; + +const PER_PAGE = 30; + +/** Each page returns newest-first; `nextCursor` walks toward older messages. message-list.tsx + * reverses pages back into chronological order before rendering (see its own comment — FlashList + * v2 dropped the `inverted` prop this used to rely on). */ +export function useMessagesInfinite(workspaceId: string | null, conversationId: string | null) { + return useInfiniteQuery({ + queryKey: queryKeys.ws.messages.list(workspaceId ?? '', conversationId ?? ''), + enabled: workspaceId !== null && conversationId !== null, + initialPageParam: undefined as string | undefined, + queryFn: async ({ pageParam }): Promise<ListMessagesResponse> => { + const { data, error } = await apiClient.GET('/workspaces/{workspaceId}/messages', { + params: { + path: { workspaceId: workspaceId! }, + query: { conversationId: conversationId!, cursor: pageParam, perPage: PER_PAGE }, + }, + }); + if (error) { + throw new ApiError(error); + } + return data; + }, + getNextPageParam, + }); +} + +export function flattenMessagePages(pages: ListMessagesResponse[] | undefined): Message[] { + return flattenPages(pages); +} diff --git a/src/features/chat/api/use-send-message.test.tsx b/src/features/chat/api/use-send-message.test.tsx new file mode 100644 index 0000000..1ccdc07 --- /dev/null +++ b/src/features/chat/api/use-send-message.test.tsx @@ -0,0 +1,263 @@ +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { renderHook, waitFor } from '@testing-library/react-native'; +import { act } from 'react'; +import type { PropsWithChildren } from 'react'; + +import { apiClient } from '@/api/client'; +import { queryKeys } from '@/api/query-keys'; +import { useChatStore } from '@/features/chat/stores/use-chat-store'; +import { + createOptimisticMessage, + useSendMessage, +} from '@/features/chat/api/use-send-message'; + +jest.mock('@/api/client', () => ({ + apiClient: { + POST: jest.fn(), + }, +})); + +const mockedPost = apiClient.POST as jest.Mock; + +const BASE = { + clientId: '1712345678000123', + conversationId: 'conv-1', + workspaceId: 'ws-1', +}; + +describe('createOptimisticMessage', () => { + it('always sets attachments to an array', () => { + // Regression: the optimistic entry previously omitted `attachments` entirely, and + // message-bubble.tsx crashed with "Cannot read property 'length' of undefined" on EVERY send. + const message = createOptimisticMessage({ ...BASE, text: 'hello' }); + + expect(Array.isArray(message.attachments)).toBe(true); + expect(message.attachments).toHaveLength(0); + }); + + it('populates the fields the message bubble renders from', () => { + const message = createOptimisticMessage({ ...BASE, text: 'hello' }); + + // senderType must not be 'contact', or the bubble renders on the inbound (left) side. + expect(message.senderType).toBe('user'); + expect(message.messageType).toBe('outgoing'); + expect(message.contentType).toBe('text'); + expect(message.id).toBe(`optimistic-${BASE.clientId}`); + expect(message.clientId).toBe(BASE.clientId); + expect(message.conversationId).toBe(BASE.conversationId); + expect(typeof message.createdAt).toBe('string'); + }); + + it('maps text to null when omitted and passes it through otherwise', () => { + expect(createOptimisticMessage(BASE).text).toBeNull(); + expect(createOptimisticMessage({ ...BASE, text: 'hi' }).text).toBe('hi'); + }); + + it('renders a picked image as a local-URI image attachment', () => { + const message = createOptimisticMessage({ + ...BASE, + attachments: [ + { uri: 'file:///tmp/photo.jpg', mimeType: 'image/jpeg', fileName: 'photo.jpg' }, + ], + }); + + expect(message.attachments).toHaveLength(1); + const [attachment] = message.attachments; + expect(attachment.fileType).toBe('image'); + // AttachmentView renders the image from `url`; the local file:// URI drives the preview. + expect(attachment.url).toBe('file:///tmp/photo.jpg'); + expect(attachment.mimeType).toBe('image/jpeg'); + expect(attachment.name).toBe('photo.jpg'); + }); + + it('gives each attachment a distinct id', () => { + // AttachmentView keys on `id` — duplicates would collide as React keys. + const message = createOptimisticMessage({ + ...BASE, + attachments: [ + { uri: 'file:///tmp/a.jpg', mimeType: 'image/jpeg', fileName: 'a.jpg' }, + { uri: 'file:///tmp/b.jpg', mimeType: 'image/jpeg', fileName: 'b.jpg' }, + ], + }); + + const ids = message.attachments.map((attachment) => attachment.id); + expect(new Set(ids).size).toBe(2); + }); + + it('treats a non-image mime type as a file, not an image', () => { + // Otherwise AttachmentView's image branch would try to render a PDF as a broken image. + const message = createOptimisticMessage({ + ...BASE, + attachments: [ + { uri: 'file:///tmp/doc.pdf', mimeType: 'application/pdf', fileName: 'doc.pdf' }, + ], + }); + + expect(message.attachments[0].fileType).toBe('file'); + }); + + it('carries a replyTo messageId through as parentId', () => { + const message = createOptimisticMessage({ ...BASE, text: 'hi', parentId: 'parent-msg-1' }); + + expect(message.parentId).toBe('parent-msg-1'); + }); + + it('defaults parentId to null when no reply is set', () => { + const message = createOptimisticMessage({ ...BASE, text: 'hi' }); + + expect(message.parentId).toBeNull(); + }); +}); + +const activeQueryClients: QueryClient[] = []; + +function createTestQueryClient(): QueryClient { + const queryClient = new QueryClient(); + activeQueryClients.push(queryClient); + return queryClient; +} + +function wrapper(queryClient: QueryClient) { + return function Wrapper({ children }: PropsWithChildren) { + return <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>; + }; +} + +function messagesData(): { pages: { data: unknown[]; nextCursor: null }[]; pageParams: unknown[] } { + return { pages: [{ data: [], nextCursor: null }], pageParams: [undefined] }; +} + +describe('useSendMessage', () => { + afterEach(() => { + mockedPost.mockReset(); + useChatStore.setState({ + draftsByConversation: {}, + composerModeByConversation: {}, + uploadProgressByClientId: {}, + }); + while (activeQueryClients.length > 0) { + const queryClient = activeQueryClients.pop()!; + queryClient.clear(); + queryClient.unmount(); + } + }); + + it('skips the optimistic insert entirely for a flowId send', async () => { + mockedPost.mockResolvedValue({ data: { id: 'server-1' }, error: undefined }); + const queryClient = createTestQueryClient(); + const queryKey = queryKeys.ws.messages.list('ws-1', 'conv-1'); + queryClient.setQueryData(queryKey, messagesData()); + + const { result } = await renderHook(() => useSendMessage('ws-1', 'conv-1'), { + wrapper: wrapper(queryClient), + }); + + act(() => { + result.current.mutate({ + workspaceId: 'ws-1', + conversationId: 'conv-1', + flowId: 'flow-1', + nodeId: 'node-1', + }); + }); + + // Assert synchronously right after onMutate would have run — no optimistic entry appears at + // any point, not just after settling. + await waitFor(() => expect(result.current.isSuccess).toBe(true)); + const data = queryClient.getQueryData<ReturnType<typeof messagesData>>(queryKey); + expect(data?.pages[0]?.data).toHaveLength(0); + expect(mockedPost).toHaveBeenCalledWith( + '/workspaces/{workspaceId}/conversations/{conversationId}/messages', + expect.objectContaining({ + body: expect.objectContaining({ flowId: 'flow-1', nodeId: 'node-1' }), + }), + ); + }); + + it('populates replyToMessageId/replyToMessageCreatedAt in the request body and parentId on the optimistic entry', async () => { + mockedPost.mockResolvedValue({ data: { id: 'server-1' }, error: undefined }); + const queryClient = createTestQueryClient(); + const queryKey = queryKeys.ws.messages.list('ws-1', 'conv-1'); + queryClient.setQueryData(queryKey, messagesData()); + + const { result } = await renderHook(() => useSendMessage('ws-1', 'conv-1'), { + wrapper: wrapper(queryClient), + }); + + act(() => { + result.current.mutate({ + workspaceId: 'ws-1', + conversationId: 'conv-1', + text: 'a reply', + replyTo: { messageId: 'parent-1', createdAt: '2026-01-01T00:00:00.000Z' }, + }); + }); + + const data = queryClient.getQueryData<{ + pages: { data: { parentId: string | null }[] }[]; + }>(queryKey); + expect(data?.pages[0]?.data[0]?.parentId).toBe('parent-1'); + + await waitFor(() => expect(result.current.isSuccess).toBe(true)); + expect(mockedPost).toHaveBeenCalledWith( + '/workspaces/{workspaceId}/conversations/{conversationId}/messages', + expect.objectContaining({ + body: expect.objectContaining({ + replyToMessageId: 'parent-1', + replyToMessageCreatedAt: '2026-01-01T00:00:00.000Z', + }), + }), + ); + }); + + it('removes the optimistic bubble and restores the draft on a workspaceBlocked rejection', async () => { + mockedPost.mockResolvedValue({ + data: undefined, + error: { code: 'workspaceBlocked', status: 402, message: 'blocked' }, + }); + const queryClient = createTestQueryClient(); + const queryKey = queryKeys.ws.messages.list('ws-1', 'conv-1'); + queryClient.setQueryData(queryKey, messagesData()); + + const { result } = await renderHook(() => useSendMessage('ws-1', 'conv-1'), { + wrapper: wrapper(queryClient), + }); + + act(() => { + result.current.mutate({ workspaceId: 'ws-1', conversationId: 'conv-1', text: 'hello' }); + }); + + await waitFor(() => expect(result.current.isError).toBe(true)); + + const data = queryClient.getQueryData<ReturnType<typeof messagesData>>(queryKey); + expect(data?.pages[0]?.data).toHaveLength(0); + expect(useChatStore.getState().draftsByConversation['conv-1']).toBe('hello'); + }); + + it('keeps a failed bubble (does not remove it) on a non-terminal error', async () => { + mockedPost.mockResolvedValue({ + data: undefined, + error: { code: 'INTERNAL_SERVER_ERROR', status: 500, message: 'boom' }, + }); + const queryClient = createTestQueryClient(); + const queryKey = queryKeys.ws.messages.list('ws-1', 'conv-1'); + queryClient.setQueryData(queryKey, messagesData()); + + const { result } = await renderHook(() => useSendMessage('ws-1', 'conv-1'), { + wrapper: wrapper(queryClient), + }); + + act(() => { + result.current.mutate({ workspaceId: 'ws-1', conversationId: 'conv-1', text: 'hello' }); + }); + + await waitFor(() => expect(result.current.isError).toBe(true)); + + const data = queryClient.getQueryData<{ + pages: { data: { __optimisticStatus?: string }[] }[]; + }>(queryKey); + expect(data?.pages[0]?.data).toHaveLength(1); + expect(data?.pages[0]?.data[0]?.__optimisticStatus).toBe('failed'); + expect(useChatStore.getState().draftsByConversation['conv-1']).toBeUndefined(); + }); +}); diff --git a/src/features/chat/api/use-send-message.ts b/src/features/chat/api/use-send-message.ts new file mode 100644 index 0000000..caa2017 --- /dev/null +++ b/src/features/chat/api/use-send-message.ts @@ -0,0 +1,306 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query'; + +import { apiClient } from '@/api/client'; +import { ApiError } from '@/api/errors'; +import { queryKeys } from '@/api/query-keys'; +import { useChatStore } from '@/features/chat/stores/use-chat-store'; +import { generateClientId } from './generate-client-id'; +import type { ListMessagesResponse, Message } from './use-messages-infinite'; +import { type MultipartAttachment, sendMultipartMessage } from './send-message-multipart'; + +interface SendMessageParams { + workspaceId: string; + conversationId: string; + text?: string; + attachments?: MultipartAttachment[]; + /** Pass the same clientId back in to retry a failed send instead of creating a new pending + * bubble — see composer.tsx / message-bubble.tsx retry handler. */ + clientId?: string; + /** Send-a-flow: the server emits N messages for the flow's own steps via realtime rather than + * echoing a single created message, so `onMutate` skips the optimistic bubble entirely when this + * is set (see below). Mutually exclusive with `attachments` — flows don't take file uploads. */ + flowId?: string; + nodeId?: string; + replyTo?: { messageId: string; createdAt: string }; +} + +const MAX_FILE_SIZE_BYTES = 5 * 1000 * 1000; + +export class AttachmentTooLargeError extends Error { + constructor(fileName: string) { + super(`"${fileName}" is larger than 5MB.`); + this.name = 'AttachmentTooLargeError'; + } +} + +/** Maps a locally-picked file into the SERVER attachment shape so the optimistic bubble can render + * a preview through the normal `AttachmentView` path. `url` points at the local `file://` URI — + * expo-image renders that identically to a remote URL, so no preview-specific rendering code is + * needed. Server-only metadata we can't know yet (`size`, `width`, `height`) is zero/null and is + * not read by any render path; the real values arrive when the send is confirmed. */ +function toOptimisticAttachment( + attachment: MultipartAttachment, + clientId: string, + index: number, + now: string, +): Message['attachments'][number] { + return { + // Unique + stable: AttachmentView uses `id` as its React key, and it must not collide across + // retries of the same message (which reuse the clientId). + id: `optimistic-${clientId}-${index}`, + createdAt: now, + updatedAt: now, + messageCreatedAt: now, + fileType: attachment.mimeType.startsWith('image/') ? 'image' : 'file', + sourceId: null, + mimeType: attachment.mimeType, + width: null, + height: null, + size: 0, + thumbnailPath: null, + originPath: attachment.uri, + name: attachment.fileName, + url: attachment.uri, + }; +} + +/** Builds a STRUCTURALLY COMPLETE `Message` for the optimistic bubble. This entry goes straight + * into the query cache's real `Message[]`, so every field message-bubble.tsx reads must be present + * — notably `attachments`, whose absence previously crashed the chat screen on every send + * (`message.attachments.length` on undefined). Deliberately NOT cast: the return type is checked + * against `Message`, so a future schema change that adds a required field fails the build here + * instead of at runtime on-device. */ +export function createOptimisticMessage(params: { + clientId: string; + conversationId: string; + workspaceId: string; + text?: string; + attachments?: MultipartAttachment[]; + parentId?: string | null; +}): Message { + const now = new Date().toISOString(); + return { + id: `optimistic-${params.clientId}`, + clientId: params.clientId, + createdAt: now, + updatedAt: now, + conversationId: params.conversationId, + workspaceId: params.workspaceId, + // Not known client-side; unread by any render path. + contactInboxId: '', + text: params.text ?? null, + contentAttributes: null, + // Non-'contact' senderType keeps the bubble on the outbound (right) side — see isOutbound() + // in message-bubble.tsx. + messageType: 'outgoing', + contentType: 'text', + senderType: 'user', + sourceId: null, + deletedAt: null, + type: 'message', + parentId: params.parentId ?? null, + attributes: null, + sendError: null, + attachments: (params.attachments ?? []).map((attachment, index) => + toOptimisticAttachment(attachment, params.clientId, index, now), + ), + }; +} + +/** Response body from message-create is typed `unknown` in the generated schema (openapi-typescript + * couldn't resolve the handler's real return type) — the backend almost certainly echoes the + * created message (with server `id` and the `clientId` we sent), but this is not contractually + * guaranteed, so we narrow defensively rather than casting blindly. */ +function parseCreatedMessageId(body: unknown): string | null { + if (body && typeof body === 'object' && 'id' in body && typeof body.id === 'string') { + return body.id; + } + return null; +} + +type MessagesInfiniteData = { pages: ListMessagesResponse[]; pageParams: unknown[] }; + +function removeOptimisticMessageFromData( + old: MessagesInfiniteData | undefined, + clientId: string, +): MessagesInfiniteData | undefined { + if (!old) return old; + return { + ...old, + pages: old.pages.map((page) => ({ + ...page, + data: page.data.filter( + (message) => !('clientId' in message && message.clientId === clientId), + ), + })), + }; +} + +/** Strips an optimistic entry out of the messages cache entirely — used (unlike the "leave it as + * a failed bubble, tap to retry" default path) for rejections that mean the send fundamentally + * cannot go through in this workspace state (`workspaceBlocked`, `unauthorized`), where leaving a + * perpetually-failed bubble around would be misleading. */ +export function removeOptimisticMessage( + queryClient: ReturnType<typeof useQueryClient>, + queryKey: readonly unknown[], + clientId: string, +): void { + queryClient.setQueryData<MessagesInfiniteData>(queryKey, (old) => + removeOptimisticMessageFromData(old, clientId), + ); +} + +export function useSendMessage(workspaceId: string, conversationId: string) { + const queryClient = useQueryClient(); + const queryKey = queryKeys.ws.messages.list(workspaceId, conversationId); + const setUploadProgress = useChatStore((state) => state.setUploadProgress); + const clearUploadProgress = useChatStore((state) => state.clearUploadProgress); + const setDraft = useChatStore((state) => state.setDraft); + + return useMutation({ + mutationFn: async (params: SendMessageParams) => { + const clientId = params.clientId ?? generateClientId(); + const attachments = params.attachments ?? []; + + if (attachments.length > 0) { + const body = await sendMultipartMessage({ + workspaceId, + conversationId, + text: params.text, + clientId, + attachments, + replyToMessageId: params.replyTo?.messageId, + replyToMessageCreatedAt: params.replyTo?.createdAt, + onProgress: (ratio) => setUploadProgress(clientId, ratio), + }); + return { clientId, serverId: parseCreatedMessageId(body) }; + } + + const { data, error } = await apiClient.POST( + '/workspaces/{workspaceId}/conversations/{conversationId}/messages', + { + params: { path: { workspaceId, conversationId } }, + body: { + text: params.text, + clientId, + flowId: params.flowId, + nodeId: params.nodeId, + replyToMessageId: params.replyTo?.messageId, + replyToMessageCreatedAt: params.replyTo?.createdAt, + }, + }, + ); + if (error) { + throw new ApiError(error); + } + return { clientId, serverId: parseCreatedMessageId(data) }; + }, + + onMutate: async (params) => { + const clientId = params.clientId ?? generateClientId(); + + // Flow sends produce N server-side messages (the flow's own steps), fanned out over + // realtime — there is no single "the message that was sent" to preview optimistically, and + // inserting one would just be a bubble that never reconciles with anything. The composer + // shows a transient "Sending flow…" toast instead (see composer.tsx). + if (params.flowId) { + return { clientId, skippedOptimistic: true as const }; + } + + queryClient.setQueryData<MessagesInfiniteData>(queryKey, (old) => { + if (!old) return old; + const [firstPage, ...restPages] = old.pages; + if (!firstPage) return old; + + const existingIndex = firstPage.data.findIndex( + (message) => 'clientId' in message && message.clientId === clientId, + ); + // `__optimisticStatus` is deliberately off-schema — see optimistic-message.ts for the + // narrowing contract. The base object itself is fully type-checked by the factory. + const optimisticEntry = { + ...createOptimisticMessage({ + clientId, + conversationId, + workspaceId, + text: params.text, + attachments: params.attachments, + parentId: params.replyTo?.messageId ?? null, + }), + __optimisticStatus: 'pending', + } as Message; + + const newData = + existingIndex >= 0 + ? firstPage.data.map((message, index) => + index === existingIndex ? optimisticEntry : message, + ) + : [optimisticEntry, ...firstPage.data]; + + return { ...old, pages: [{ ...firstPage, data: newData }, ...restPages] }; + }); + + return { clientId, skippedOptimistic: false as const }; + }, + + onError: (error, params, context) => { + if (!context) return; + clearUploadProgress(context.clientId); + + if (context.skippedOptimistic) { + // Nothing was inserted for a flow send — still worth restoring the draft below on a + // blocked/unauthorized rejection so the user doesn't lose whatever they'd typed alongside + // the flow pick, but there's no cache entry to touch. + } + + const body = error instanceof ApiError ? error.body : null; + const isTerminal = + body?.code === 'workspaceBlocked' || + body?.status === 402 || + body?.code === 'UNAUTHORIZED' || + body?.status === 401; + + if (isTerminal) { + removeOptimisticMessage(queryClient, queryKey, context.clientId); + if (params.text) { + setDraft(conversationId, params.text); + } + return; + } + + if (context.skippedOptimistic) return; + + queryClient.setQueryData<MessagesInfiniteData>(queryKey, (old) => { + if (!old) return old; + return { + ...old, + pages: old.pages.map((page) => ({ + ...page, + data: page.data.map((message) => + 'clientId' in message && message.clientId === context.clientId + ? ({ + ...message, + __optimisticStatus: 'failed', + __optimisticError: error instanceof Error ? error.message : 'Failed to send', + } as unknown as Message) + : message, + ), + })), + }; + }); + }, + + onSuccess: (_data, _params, context) => { + if (context) clearUploadProgress(context.clientId); + // The sender's own optimistic bubble is reconciled here by simply refetching — the newly + // created message (with its real server id) will replace the optimistic entry once the list + // refetches, since clientId is embedded on the real message too and message-bubble.tsx keys + // by clientId when present. Realtime (Phase 6) handles the same reconciliation for OTHER + // clients viewing this conversation; this path only needs to work for the sender themself. + // Flow sends (no optimistic entry) rely on this same invalidation/refetch (or realtime) to + // surface the flow's resulting messages. + queryClient.invalidateQueries({ queryKey }); + }, + }); +} + +export { MAX_FILE_SIZE_BYTES }; diff --git a/src/features/chat/components/activity-message.tsx b/src/features/chat/components/activity-message.tsx new file mode 100644 index 0000000..d42d693 --- /dev/null +++ b/src/features/chat/components/activity-message.tsx @@ -0,0 +1,34 @@ +import { useTranslation } from 'react-i18next'; +import { StyleSheet, View } from 'react-native'; + +import { Text } from '@/components/ui/text'; +import type { Message } from '@/features/chat/api/use-messages-infinite'; +import { useTheme } from '@/theme/use-theme'; + +interface ActivityMessageProps { + message: Message; +} + +/** `messageType: "activity"` rows (assignment changes, archive, bot toggle, etc. surfaced as + * inline timeline entries) — rendered centered and de-emphasized, not as a bubble. */ +export function ActivityMessage({ message }: ActivityMessageProps) { + const { t } = useTranslation(); + const { spacing } = useTheme(); + + return ( + <View style={[styles.row, { paddingVertical: spacing.xs }]}> + <Text variant="caption" color="secondary" style={styles.text}> + {message.text ?? t('chat.activityFallback')} + </Text> + </View> + ); +} + +const styles = StyleSheet.create({ + row: { + alignItems: 'center', + }, + text: { + textAlign: 'center', + }, +}); diff --git a/src/features/chat/components/attachment-view.tsx b/src/features/chat/components/attachment-view.tsx new file mode 100644 index 0000000..d8e486c --- /dev/null +++ b/src/features/chat/components/attachment-view.tsx @@ -0,0 +1,194 @@ +import { Image } from 'expo-image'; +import { router } from 'expo-router'; +import { Pressable, StyleSheet, View } from 'react-native'; + +import { Card } from '@/components/ui/surface'; +import { Icon } from '@/components/ui/icon'; +import { Text } from '@/components/ui/text'; +import type { Message } from '@/features/chat/api/use-messages-infinite'; +import { useChatStore } from '@/features/chat/stores/use-chat-store'; +import { useTheme } from '@/theme/use-theme'; + +interface AttachmentViewProps { + attachments: Message['attachments']; + outbound: boolean; + /** clientId of the parent message, for looking up in-flight upload progress. Undefined for a + * confirmed (non-optimistic) message, which never has progress to show. */ + clientId?: string; +} + +const MAX_WIDTH = 240; +const FALLBACK_ASPECT_RATIO = 4 / 3; + +function aspectRatioFor(attachment: Message['attachments'][number]): number { + if (attachment.width && attachment.height && attachment.height > 0) { + return attachment.width / attachment.height; + } + return FALLBACK_ASPECT_RATIO; +} + +function formatBytes(bytes: number): string { + if (bytes <= 0) return ''; + const units = ['B', 'KB', 'MB', 'GB']; + const exponent = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), units.length - 1); + const value = bytes / 1024 ** exponent; + return `${value.toFixed(value >= 10 || exponent === 0 ? 0 : 1)} ${units[exponent]}`; +} + +function fileIcon(fileType: string): 'videocam' | 'musical-notes' | 'image' | 'document' { + switch (fileType) { + case 'video': + return 'videocam'; + case 'audio': + return 'musical-notes'; + case 'gif': + return 'image'; + default: + return 'document'; + } +} + +function UploadProgressOverlay({ ratio }: { ratio: number }) { + const { colors } = useTheme(); + const percent = Math.round(ratio * 100); + + return ( + <View style={[styles.progressOverlay, { backgroundColor: colors.scrim }]} pointerEvents="none"> + <Text variant="caption" color="inverse" style={styles.progressLabel}> + {`${percent}%`} + </Text> + </View> + ); +} + +function ImageAttachment({ + attachment, + width, + progress, +}: { + attachment: Message['attachments'][number]; + width: number; + progress: number | undefined; +}) { + const { radius } = useTheme(); + const ratio = aspectRatioFor(attachment); + const height = width / ratio; + + return ( + <Pressable + onPress={() => + attachment.url && + router.push(`/image-viewer?url=${encodeURIComponent(attachment.url)}` as never) + } + style={styles.imageWrap} + > + <Image + source={{ uri: attachment.url ?? undefined }} + style={{ width, height, borderRadius: radius.md }} + contentFit="cover" + /> + {typeof progress === 'number' && progress < 1 ? ( + <UploadProgressOverlay ratio={progress} /> + ) : null} + </Pressable> + ); +} + +/** Renders a message's attachments: a single full-width image, a 2-column grid for multiple + * images, and non-image files as a `Card` row (icon tile + name + size). In-flight upload + * progress (keyed by the parent message's clientId) overlays image attachments as a percentage — + * file-row attachments don't have room for a ring/bar, so progress is implicit there via the + * optimistic pending state on the bubble itself. */ +export function AttachmentView({ attachments, outbound, clientId }: AttachmentViewProps) { + const { colors, spacing } = useTheme(); + const progress = useChatStore((state) => + clientId ? state.uploadProgressByClientId[clientId] : undefined, + ); + + const images = attachments.filter( + (attachment) => attachment.fileType === 'image' && attachment.url, + ); + const files = attachments.filter( + (attachment) => !(attachment.fileType === 'image' && attachment.url), + ); + + const isGrid = images.length > 1; + const imageWidth = isGrid ? (MAX_WIDTH - spacing.xs) / 2 : MAX_WIDTH; + + return ( + <View style={{ gap: spacing.xs }}> + {images.length > 0 ? ( + <View style={[isGrid ? styles.grid : undefined, isGrid ? { gap: spacing.xs } : undefined]}> + {images.map((attachment) => ( + <ImageAttachment + key={attachment.id} + attachment={attachment} + width={imageWidth} + progress={progress} + /> + ))} + </View> + ) : null} + + {files.map((attachment) => { + const textColor = outbound ? colors.bubbleOutText : colors.bubbleInText; + return ( + <Card + key={attachment.id} + radius="sm" + padding="xs" + style={[styles.fileRow, { gap: spacing.xs }]} + > + <View style={[styles.fileIconTile, { backgroundColor: colors.surface0 }]}> + <Icon name={fileIcon(attachment.fileType)} size={20} color={textColor} /> + </View> + <View style={styles.fileMeta}> + <Text variant="caption" numberOfLines={1} style={{ color: textColor }}> + {attachment.name ?? attachment.fileType} + </Text> + {attachment.size ? ( + <Text variant="micro" style={{ color: textColor, opacity: 0.7 }}> + {formatBytes(attachment.size)} + </Text> + ) : null} + </View> + </Card> + ); + })} + </View> + ); +} + +const styles = StyleSheet.create({ + grid: { + flexDirection: 'row', + flexWrap: 'wrap', + }, + imageWrap: { + position: 'relative', + }, + progressOverlay: { + ...StyleSheet.absoluteFill, + alignItems: 'center', + justifyContent: 'center', + borderRadius: 12, + }, + progressLabel: { + fontWeight: '700', + }, + fileRow: { + flexDirection: 'row', + alignItems: 'center', + }, + fileIconTile: { + width: 36, + height: 36, + borderRadius: 8, + alignItems: 'center', + justifyContent: 'center', + }, + fileMeta: { + flex: 1, + gap: 2, + }, +}); diff --git a/src/features/chat/components/chat-header.tsx b/src/features/chat/components/chat-header.tsx new file mode 100644 index 0000000..d65f339 --- /dev/null +++ b/src/features/chat/components/chat-header.tsx @@ -0,0 +1,163 @@ +import { useQueryClient } from '@tanstack/react-query'; +import type BottomSheet from '@gorhom/bottom-sheet'; +import { router } from 'expo-router'; +import { useRef } from 'react'; +import { useTranslation } from 'react-i18next'; +import type { LayoutChangeEvent } from 'react-native'; +import { Pressable, StyleSheet, View } from 'react-native'; + +import { Avatar } from '@/components/ui/avatar'; +import { IconButton } from '@/components/ui/icon-button'; +import { Text } from '@/components/ui/text'; +import { ChannelBadge } from '@/features/conversations/components/channel-badge'; +import { useConversationDetail } from '@/features/conversations/api/use-conversation-detail'; +import { useDisableBot, useEnableBot } from '@/features/conversations/api/use-conversation-actions'; +import { isBotActive } from '@/features/conversations/lib/conversation-status'; +import { findConversationInListCache } from '@/features/conversations/lib/find-conversation-in-cache'; +import { withAlpha } from '@/theme/color-utils'; +import { useTheme } from '@/theme/use-theme'; + +import { ConversationActionsSheet } from './conversation-actions-sheet'; + +interface ChatHeaderProps { + workspaceId: string; + conversationId: string; + onBack: () => void; + onLayout?: (event: LayoutChangeEvent) => void; +} + +/** Resolves conversation identity via `useConversationDetail`, seeded with a synchronous + * `findConversationInListCache` placeholder — this is what fixes the "Conversation" fallback bug + * on a deep link straight into a conversation: the header now always has SOME data to render, + * either from the list cache the user navigated from, or (once it resolves) the dedicated detail + * fetch, rather than only ever reading the list cache and falling back to a hardcoded string when + * it's empty (e.g. cold deep link with no list query cached yet). */ +export function ChatHeader({ workspaceId, conversationId, onBack, onLayout }: ChatHeaderProps) { + const { t } = useTranslation(); + const { colors, spacing } = useTheme(); + const queryClient = useQueryClient(); + const actionsSheetRef = useRef<BottomSheet>(null); + + const { data: conversation } = useConversationDetail(workspaceId, conversationId, { + placeholderData: () => findConversationInListCache(queryClient, workspaceId, conversationId), + }); + + const enableBot = useEnableBot(workspaceId); + const disableBot = useDisableBot(workspaceId); + + const contactName = conversation?.contact?.fullName ?? t('conversations.unknownContact'); + const channel = conversation?.contactInboxes?.[0]?.channel; + const assigneeName = conversation?.assignedUser?.name; + const botActive = conversation + ? isBotActive(conversation.botEnabled, conversation.botResumeAt) + : false; + + function handleToggleBot() { + if (!conversation) return; + if (botActive) { + disableBot.mutate([conversationId]); + } else { + enableBot.mutate([conversationId]); + } + } + + const subtitleParts = [ + channel ? t(`conversations.channel${capitalize(channel)}`, { defaultValue: channel }) : null, + assigneeName ? t('conversations.assignedTo', { name: assigneeName }) : null, + ].filter(Boolean); + + return ( + <View + onLayout={onLayout} + style={[ + styles.container, + { + borderBottomColor: colors.borderSubtle, + paddingHorizontal: spacing.sm, + paddingVertical: spacing.xs, + gap: spacing.xs, + }, + ]} + > + <IconButton + accessibilityLabel={t('common.back', { defaultValue: 'Back' })} + icon="chevron-back" + variant="ghost" + onPress={onBack} + /> + + <Pressable + style={styles.identity} + accessibilityRole="button" + onPress={() => router.push(`/(app)/conversations/${conversationId}/contact` as never)} + > + <Avatar + uri={conversation?.contact?.avatar} + name={contactName} + size={36} + ring={botActive ? 'bot' : 'none'} + badge={channel ? <ChannelBadge channel={channel} size={12} /> : undefined} + /> + <View style={styles.nameColumn}> + <Text variant="bodyStrong" numberOfLines={1}> + {contactName} + </Text> + {subtitleParts.length > 0 ? ( + <Text variant="caption" color="secondary" numberOfLines={1}> + {subtitleParts.join(' · ')} + </Text> + ) : null} + </View> + </Pressable> + + <IconButton + accessibilityLabel={ + botActive ? t('conversations.disableBot') : t('conversations.enableBot') + } + icon="sparkles" + variant="tonal" + tint={botActive ? colors.bubbleBotAccent : undefined} + style={botActive ? { backgroundColor: withAlpha(colors.bubbleBotAccent, 0.16) } : undefined} + haptic="medium" + onPress={handleToggleBot} + /> + + <IconButton + accessibilityLabel={t('conversations.actions')} + icon="ellipsis-vertical" + variant="ghost" + onPress={() => actionsSheetRef.current?.expand()} + /> + + <ConversationActionsSheet + ref={actionsSheetRef} + workspaceId={workspaceId} + conversationId={conversationId} + conversation={conversation} + onClose={() => actionsSheetRef.current?.close()} + /> + </View> + ); +} + +function capitalize(value: string): string { + return value.charAt(0).toUpperCase() + value.slice(1); +} + +const styles = StyleSheet.create({ + container: { + flexDirection: 'row', + alignItems: 'center', + borderBottomWidth: StyleSheet.hairlineWidth, + }, + identity: { + flexDirection: 'row', + alignItems: 'center', + gap: 8, + flex: 1, + }, + nameColumn: { + gap: 2, + flex: 1, + }, +}); diff --git a/src/features/chat/components/composer-actions-sheet.tsx b/src/features/chat/components/composer-actions-sheet.tsx new file mode 100644 index 0000000..a0f4211 --- /dev/null +++ b/src/features/chat/components/composer-actions-sheet.tsx @@ -0,0 +1,70 @@ +import type BottomSheet from '@gorhom/bottom-sheet'; +import { forwardRef } from 'react'; +import { useTranslation } from 'react-i18next'; + +import { ListItem } from '@/components/ui/list-item'; +import { Sheet, SheetHeader } from '@/components/ui/sheet'; +import { Icon } from '@/components/ui/icon'; +import { useTheme } from '@/theme/use-theme'; + +interface ComposerActionsSheetProps { + onClose: () => void; + onCamera: () => void; + onPhotoLibrary: () => void; + onDocument: () => void; + onSendFlow: () => void; + onSavedReplies: () => void; + /** Omitted entirely (not just disabled) when the sequences feature isn't available yet — see + * the TODO in composer.tsx for why this is optional rather than always-present-but-gated. */ + onSendSequence?: () => void; +} + +/** "[+]" composer action sheet: camera, photo library (multi-select), document, send flow, send + * sequence (feature-gated — see composer.tsx), saved replies. */ +export const ComposerActionsSheet = forwardRef<BottomSheet, ComposerActionsSheetProps>( + function ComposerActionsSheet( + { onClose, onCamera, onPhotoLibrary, onDocument, onSendFlow, onSavedReplies, onSendSequence }, + ref, + ) { + const { t } = useTranslation(); + const { colors } = useTheme(); + + return ( + <Sheet ref={ref} snapPoints={['50%']} onDismiss={onClose}> + <SheetHeader title={t('chat.attach')} onClose={onClose} /> + <ListItem + title={t('chat.camera')} + leading={<Icon name="camera" size={22} color={colors.textSecondary} />} + onPress={onCamera} + /> + <ListItem + title={t('chat.photoLibrary')} + leading={<Icon name="image" size={22} color={colors.textSecondary} />} + onPress={onPhotoLibrary} + /> + <ListItem + title={t('chat.document')} + leading={<Icon name="document-attach" size={22} color={colors.textSecondary} />} + onPress={onDocument} + /> + <ListItem + title={t('chat.sendFlow')} + leading={<Icon name="git-branch" size={22} color={colors.textSecondary} />} + onPress={onSendFlow} + /> + {onSendSequence ? ( + <ListItem + title={t('chat.sendSequence')} + leading={<Icon name="layers" size={22} color={colors.textSecondary} />} + onPress={onSendSequence} + /> + ) : null} + <ListItem + title={t('chat.savedReplies')} + leading={<Icon name="flash" size={22} color={colors.textSecondary} />} + onPress={onSavedReplies} + /> + </Sheet> + ); + }, +); diff --git a/src/features/chat/components/composer.tsx b/src/features/chat/components/composer.tsx new file mode 100644 index 0000000..f40cbb7 --- /dev/null +++ b/src/features/chat/components/composer.tsx @@ -0,0 +1,439 @@ +import type BottomSheet from '@gorhom/bottom-sheet'; +import { router } from 'expo-router'; +import { useRef, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import type { + NativeSyntheticEvent, + StyleProp, + TextInputContentSizeChangeEventData, + ViewStyle, +} from 'react-native'; +import { Pressable, StyleSheet, TextInput, View } from 'react-native'; +import Animated, { useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated'; + +import { ErrorBanner } from '@/components/ui/error-banner'; +import { Icon } from '@/components/ui/icon'; +import { IconButton } from '@/components/ui/icon-button'; +import { Text } from '@/components/ui/text'; +import { useToast } from '@/components/ui/toast'; +import { normalizeApiError } from '@/api/errors'; +import { FEATURES } from '@/config/features'; +import { useConversationDetail } from '@/features/conversations/api/use-conversation-detail'; +import type { FlowListItem } from '@/features/flows/api/use-flows'; +import { useEditMessage } from '@/features/chat/api/use-message-actions'; +import { MAX_FILE_SIZE_BYTES, useSendMessage } from '@/features/chat/api/use-send-message'; +import { + partitionBySize, + pickDocuments, + pickFromLibrary, +} from '@/features/chat/lib/pick-attachments'; +import { useChatStore } from '@/features/chat/stores/use-chat-store'; +import { SequencePickerSheet } from '@/features/sequences/components/sequence-picker-sheet'; +import { triggerHaptic } from '@/lib/haptics'; +import { useTheme } from '@/theme/use-theme'; + +import { ComposerActionsSheet } from './composer-actions-sheet'; +import { FlowPickerSheet } from './flow-picker-sheet'; +import { QuotaBanner } from './quota-banner'; +import { SavedRepliesSheet } from './saved-replies-sheet'; + +interface ComposerProps { + workspaceId: string; + conversationId: string; + /** Extra style applied to the outermost container — the chat screen uses this to add + * `paddingBottom: insets.bottom` only while the keyboard is closed (open keyboard already + * pushes the composer up against it, so double-padding would leave a visible gap). */ + style?: StyleProp<ViewStyle>; +} + +const MIN_INPUT_HEIGHT = 44; +const MAX_INPUT_HEIGHT = 132; + +/** Text composer: attachment/flow/saved-reply picking via the `[+]` actions sheet, edit/reply + * mode bar, auto-grow input, morphing send button, and the quota-banner gate on 402 + * workspaceBlocked. Failed-send retry lives on the message bubble itself (tap a failed bubble) — + * the composer only starts NEW sends and edits. */ +export function Composer({ workspaceId, conversationId, style }: ComposerProps) { + const { t } = useTranslation(); + const { colors, spacing, radius, scheme } = useTheme(); + const toast = useToast(); + + const draft = useChatStore((state) => state.draftsByConversation[conversationId] ?? ''); + const setDraft = useChatStore((state) => state.setDraft); + const composerMode = useChatStore((state) => state.composerModeByConversation[conversationId]); + const setComposerMode = useChatStore((state) => state.setComposerMode); + + const sendMessage = useSendMessage(workspaceId, conversationId); + const editMessage = useEditMessage(workspaceId, conversationId); + + const actionsSheetRef = useRef<BottomSheet>(null); + const savedRepliesRef = useRef<BottomSheet>(null); + const flowPickerRef = useRef<BottomSheet>(null); + const sequencePickerRef = useRef<BottomSheet>(null); + + // `FEATURES.sendSequence` is off until the backend route ships (see src/config/features.ts) — + // fetching the conversation detail here just to resolve `contactId` would be wasted work while + // the entry is hidden, so this only fires the query once the flag is on. `useConversationDetail` + // itself already no-ops via `enabled` when either id is missing. + const { data: conversationForSequence } = useConversationDetail( + FEATURES.sendSequence ? workspaceId : null, + FEATURES.sendSequence ? conversationId : null, + ); + + const [attachmentError, setAttachmentError] = useState<string | null>(null); + const [inputHeight, setInputHeight] = useState(MIN_INPUT_HEIGHT); + const [quotaBlocked, setQuotaBlocked] = useState<Extract< + ReturnType<typeof normalizeApiError>, + { kind: 'workspaceBlocked' } + > | null>(null); + + const sendButtonScale = useSharedValue(1); + const sendButtonStyle = useAnimatedStyle(() => ({ + transform: [{ scale: sendButtonScale.value }], + })); + + const hasContent = draft.trim().length > 0; + const isEditing = composerMode?.type === 'edit'; + const isReplying = composerMode?.type === 'reply'; + + function handleChangeText(text: string) { + setDraft(conversationId, text); + } + + function handleContentSizeChange( + event: NativeSyntheticEvent<TextInputContentSizeChangeEventData>, + ) { + const nextHeight = Math.min( + MAX_INPUT_HEIGHT, + Math.max(MIN_INPUT_HEIGHT, event.nativeEvent.contentSize.height), + ); + setInputHeight(nextHeight); + } + + function dismissComposerMode() { + setComposerMode(conversationId, undefined); + } + + async function handleSend() { + const text = draft.trim(); + if (!text || quotaBlocked) return; + + // reanimated `SharedValue.value` is a mutable UI-thread ref by design (not React state); the + // react-compiler lint rule can't tell it apart from a plain object — see the matching comment + // in pressable-scale.tsx. + // eslint-disable-next-line react-hooks/immutability + sendButtonScale.value = withTiming(0.85, { duration: 80 }, () => { + sendButtonScale.value = withTiming(1, { duration: 120 }); + }); + triggerHaptic('light'); + + if (isEditing && composerMode) { + setDraft(conversationId, ''); + dismissComposerMode(); + editMessage.mutate({ + messageId: composerMode.message.id, + createdAt: composerMode.message.createdAt, + newText: text, + }); + return; + } + + const replyTo = + isReplying && composerMode + ? { + messageId: composerMode.message.id, + createdAt: composerMode.message.createdAt, + } + : undefined; + + setDraft(conversationId, ''); + dismissComposerMode(); + try { + await sendMessage.mutateAsync({ workspaceId, conversationId, text, replyTo }); + } catch (error) { + const normalized = normalizeApiError(error); + if (normalized.kind === 'workspaceBlocked') { + setQuotaBlocked(normalized); + } + // Other failures already surface on the pending bubble itself (see use-send-message.ts + // onError) — no need to re-show them here. + } + } + + async function sendAssets( + assets: { uri: string; mimeType: string; fileName: string; size?: number }[], + ) { + setAttachmentError(null); + const { ok, tooLarge } = partitionBySize(assets, MAX_FILE_SIZE_BYTES); + + if (tooLarge.length > 0) { + setAttachmentError( + t('chat.attachmentsTooLarge', { + count: tooLarge.length, + names: tooLarge.map((asset) => asset.fileName).join(', '), + }), + ); + } + if (ok.length === 0) return; + + try { + await sendMessage.mutateAsync({ + workspaceId, + conversationId, + attachments: ok.map((asset) => ({ + uri: asset.uri, + mimeType: asset.mimeType, + fileName: asset.fileName, + })), + }); + } catch (error) { + const normalized = normalizeApiError(error); + if (normalized.kind === 'workspaceBlocked') { + setQuotaBlocked(normalized); + } + } + } + + async function handleCamera() { + actionsSheetRef.current?.close(); + router.push(`/camera?conversationId=${conversationId}` as never); + } + + async function handlePhotoLibrary() { + actionsSheetRef.current?.close(); + const assets = await pickFromLibrary(); + if (assets.length === 0) return; + await sendAssets(assets); + } + + async function handleDocument() { + actionsSheetRef.current?.close(); + const assets = await pickDocuments(); + if (assets.length === 0) return; + await sendAssets(assets.map((asset) => ({ ...asset, mimeType: asset.mimeType }))); + } + + function handleOpenFlowPicker() { + actionsSheetRef.current?.close(); + flowPickerRef.current?.expand(); + } + + function handleOpenSendSequence() { + actionsSheetRef.current?.close(); + sequencePickerRef.current?.expand(); + } + + async function handleSelectFlow(flow: FlowListItem) { + flowPickerRef.current?.close(); + toast({ message: t('chat.sendingFlow', { name: flow.name }), tone: 'info' }); + try { + await sendMessage.mutateAsync({ workspaceId, conversationId, flowId: flow.id }); + } catch (error) { + const normalized = normalizeApiError(error); + if (normalized.kind === 'workspaceBlocked') { + setQuotaBlocked(normalized); + } else { + toast({ message: t('chat.flowSendFailed'), tone: 'danger' }); + } + } + } + + function handleOpenSavedReplies() { + actionsSheetRef.current?.close(); + savedRepliesRef.current?.expand(); + } + + const modeBarLabel = isEditing + ? t('chat.editingMessage') + : isReplying && composerMode + ? t('chat.replyingTo', { + name: + composerMode.message.senderType === 'contact' + ? t('chat.senderContact') + : t('chat.senderAgent'), + }) + : null; + const modeBarPreview = + composerMode?.message.text ?? (composerMode ? t('chat.repliedAttachment') : null); + + return ( + <View style={style}> + {quotaBlocked ? ( + <QuotaBanner error={quotaBlocked} onDismiss={() => setQuotaBlocked(null)} /> + ) : null} + {attachmentError ? ( + <View style={{ paddingHorizontal: spacing.sm, paddingTop: spacing.xs }}> + <ErrorBanner message={attachmentError} tone="danger" /> + </View> + ) : null} + + {modeBarLabel ? ( + <View + style={[ + styles.modeBar, + { + backgroundColor: colors.surface0, + paddingHorizontal: spacing.ms, + paddingVertical: spacing.xs, + gap: spacing.xxs, + }, + ]} + > + <Icon + name={isEditing ? 'create-outline' : 'arrow-undo-outline'} + size={16} + color={colors.brand} + /> + <View style={styles.modeBarBody}> + <Text variant="caption" style={{ color: colors.brand, fontWeight: '700' }}> + {modeBarLabel} + </Text> + {modeBarPreview ? ( + <Text variant="caption" color="secondary" numberOfLines={1}> + {modeBarPreview} + </Text> + ) : null} + </View> + <IconButton + accessibilityLabel={t('common.close', { defaultValue: 'Close' })} + icon="close" + size="sm" + variant="ghost" + onPress={dismissComposerMode} + /> + </View> + ) : null} + + <View + style={[ + styles.row, + { padding: spacing.sm, gap: spacing.xs, borderTopColor: colors.borderSubtle }, + ]} + > + <IconButton + accessibilityLabel={t('chat.attach')} + icon="add-circle" + size="lg" + variant="ghost" + onPress={() => actionsSheetRef.current?.expand()} + disabled={Boolean(quotaBlocked)} + /> + + <TextInput + value={draft} + onChangeText={handleChangeText} + onContentSizeChange={handleContentSizeChange} + placeholder={t('chat.typeAMessage')} + placeholderTextColor={colors.textSecondary} + keyboardAppearance={scheme} + multiline + editable={!quotaBlocked} + style={[ + styles.input, + { + height: inputHeight, + backgroundColor: colors.surface0, + borderRadius: radius.md, + color: colors.textPrimary, + paddingHorizontal: spacing.sm, + }, + ]} + /> + + <Animated.View style={sendButtonStyle}> + <Pressable + accessibilityRole="button" + accessibilityLabel={t('chat.send')} + onPress={handleSend} + disabled={!hasContent || Boolean(quotaBlocked)} + style={[ + styles.sendButton, + { + backgroundColor: hasContent ? colors.brand : colors.surface0, + borderRadius: radius.full, + }, + (!hasContent || quotaBlocked) && styles.sendButtonDisabled, + ]} + > + <Icon + name={isEditing ? 'checkmark' : 'send'} + size={18} + color={hasContent ? colors.onBrand : colors.textTertiary} + flipRTL={!isEditing} + /> + </Pressable> + </Animated.View> + </View> + + <ComposerActionsSheet + ref={actionsSheetRef} + onClose={() => actionsSheetRef.current?.close()} + onCamera={handleCamera} + onPhotoLibrary={handlePhotoLibrary} + onDocument={handleDocument} + onSendFlow={handleOpenFlowPicker} + onSavedReplies={handleOpenSavedReplies} + // The sequences feature (src/features/sequences/**) now exists — landed in a parallel + // workstream. `onSendSequence` is still omitted entirely (not shown disabled) unless the + // contactId for this conversation has resolved, matching the "omit rather than disable" + // convention this sheet already follows elsewhere. The picker sheet itself handles the + // FEATURES.sendSequence-off "Coming soon" state, so this stays wired even while the flag + // is off — flipping the flag in src/config/features.ts is the only thing left to do once + // the backend route ships. + onSendSequence={conversationForSequence?.contactId ? handleOpenSendSequence : undefined} + /> + + <FlowPickerSheet + ref={flowPickerRef} + workspaceId={workspaceId} + onSelect={handleSelectFlow} + onClose={() => flowPickerRef.current?.close()} + /> + + <SavedRepliesSheet + ref={savedRepliesRef} + workspaceId={workspaceId} + onSelect={(text) => { + setDraft(conversationId, text); + savedRepliesRef.current?.close(); + }} + /> + + {conversationForSequence?.contactId ? ( + <SequencePickerSheet + sheetRef={sequencePickerRef} + workspaceId={workspaceId} + contactId={conversationForSequence.contactId} + /> + ) : null} + </View> + ); +} + +const styles = StyleSheet.create({ + row: { + flexDirection: 'row', + alignItems: 'flex-end', + borderTopWidth: StyleSheet.hairlineWidth, + }, + modeBar: { + flexDirection: 'row', + alignItems: 'center', + }, + modeBarBody: { + flex: 1, + }, + input: { + flex: 1, + paddingVertical: 10, + }, + sendButton: { + width: 36, + height: 36, + alignItems: 'center', + justifyContent: 'center', + }, + sendButtonDisabled: { + opacity: 0.5, + }, +}); diff --git a/src/features/chat/components/conversation-actions-sheet.tsx b/src/features/chat/components/conversation-actions-sheet.tsx new file mode 100644 index 0000000..94b95bf --- /dev/null +++ b/src/features/chat/components/conversation-actions-sheet.tsx @@ -0,0 +1,143 @@ +import type BottomSheet from '@gorhom/bottom-sheet'; +import { router } from 'expo-router'; +import { forwardRef, useRef } from 'react'; +import { Alert } from 'react-native'; +import { useTranslation } from 'react-i18next'; + +import { Icon } from '@/components/ui/icon'; +import { ListItem } from '@/components/ui/list-item'; +import { Sheet, SheetHeader } from '@/components/ui/sheet'; +import { AssignmentSheet } from '@/features/conversations/components/assignment-sheet'; +import { + useArchiveConversations, + useAssignConversations, + useFollowConversation, + useMarkConversationUnread, + useUnarchiveConversations, + useUnfollowConversation, +} from '@/features/conversations/api/use-conversation-actions'; +import type { ConversationListItem } from '@/features/conversations/api/use-conversations-infinite'; +import { useTheme } from '@/theme/use-theme'; + +interface ConversationActionsSheetProps { + workspaceId: string; + conversationId: string; + conversation: ConversationListItem | undefined; + onClose: () => void; +} + +/** Chat header overflow menu: follow/unfollow, archive/unarchive (with confirm), mark unread, + * assign (reuses the existing assignment-sheet.tsx), view contact. */ +export const ConversationActionsSheet = forwardRef<BottomSheet, ConversationActionsSheetProps>( + function ConversationActionsSheet({ workspaceId, conversationId, conversation, onClose }, ref) { + const { t } = useTranslation(); + const { colors } = useTheme(); + const assignmentSheetRef = useRef<BottomSheet>(null); + + const followConversation = useFollowConversation(workspaceId); + const unfollowConversation = useUnfollowConversation(workspaceId); + const archiveConversations = useArchiveConversations(workspaceId); + const unarchiveConversations = useUnarchiveConversations(workspaceId); + const markUnread = useMarkConversationUnread(workspaceId); + const assignConversations = useAssignConversations(workspaceId); + + const isFollowed = Boolean(conversation?.followed); + const isArchived = Boolean(conversation?.archivedAt); + + function handleToggleFollow() { + if (isFollowed) { + unfollowConversation.mutate(conversationId); + } else { + followConversation.mutate(conversationId); + } + onClose(); + } + + function handleArchive() { + Alert.alert( + isArchived ? t('conversations.unarchive') : t('conversations.archive'), + isArchived ? t('conversations.unarchiveConfirm') : t('conversations.archiveConfirm'), + [ + { text: t('common.cancel', { defaultValue: 'Cancel' }), style: 'cancel' }, + { + text: isArchived ? t('conversations.unarchive') : t('conversations.archive'), + style: 'destructive', + onPress: () => { + if (isArchived) { + unarchiveConversations.mutate([conversationId]); + } else { + archiveConversations.mutate([conversationId]); + } + onClose(); + }, + }, + ], + ); + } + + function handleMarkUnread() { + markUnread.mutate(conversationId); + onClose(); + } + + function handleViewContact() { + onClose(); + router.push(`/(app)/conversations/${conversationId}/contact` as never); + } + + return ( + <> + <Sheet ref={ref} snapPoints={['45%']} onDismiss={onClose}> + <SheetHeader title={t('conversations.actions')} onClose={onClose} /> + <ListItem + title={isFollowed ? t('conversations.unfollow') : t('conversations.follow')} + leading={ + <Icon + name={isFollowed ? 'star' : 'star-outline'} + size={20} + color={colors.textSecondary} + /> + } + onPress={handleToggleFollow} + /> + <ListItem + title={t('conversations.assign')} + leading={<Icon name="person-add-outline" size={20} color={colors.textSecondary} />} + onPress={() => { + onClose(); + assignmentSheetRef.current?.expand(); + }} + /> + <ListItem + title={t('conversations.markUnread')} + leading={<Icon name="mail-unread-outline" size={20} color={colors.textSecondary} />} + onPress={handleMarkUnread} + /> + <ListItem + title={t('contacts.contactTitle')} + leading={<Icon name="person-outline" size={20} color={colors.textSecondary} />} + onPress={handleViewContact} + /> + <ListItem + title={isArchived ? t('conversations.unarchive') : t('conversations.archive')} + leading={<Icon name="archive-outline" size={20} color={colors.danger} />} + destructive + onPress={handleArchive} + /> + </Sheet> + <AssignmentSheet + sheetRef={assignmentSheetRef} + workspaceId={workspaceId} + onAssign={(userId) => { + if (conversation?.contactId) { + assignConversations.mutate({ + contactIds: [conversation.contactId], + assignedId: userId, + }); + } + }} + /> + </> + ); + }, +); diff --git a/src/features/chat/components/flow-picker-sheet.tsx b/src/features/chat/components/flow-picker-sheet.tsx new file mode 100644 index 0000000..39d6adb --- /dev/null +++ b/src/features/chat/components/flow-picker-sheet.tsx @@ -0,0 +1,46 @@ +import type BottomSheet from '@gorhom/bottom-sheet'; +import { BottomSheetFlatList } from '@gorhom/bottom-sheet'; +import { forwardRef, useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; + +import { EmptyState } from '@/components/ui/empty-state'; +import { ListItem } from '@/components/ui/list-item'; +import { Sheet, SheetHeader } from '@/components/ui/sheet'; +import type { FlowListItem } from '@/features/flows/api/use-flows'; +import { useFlows } from '@/features/flows/api/use-flows'; + +interface FlowPickerSheetProps { + workspaceId: string; + onSelect: (flow: FlowListItem) => void; + onClose: () => void; +} + +/** "Send flow" composer entry: lists flows filtered to `active && enableInInbox` — per the plan's + * verified backend note, only flows meeting both are valid to trigger from the inbox composer + * (the endpoint doesn't reject others itself, but the web app applies this same filter when + * building its own picker). */ +export const FlowPickerSheet = forwardRef<BottomSheet, FlowPickerSheetProps>( + function FlowPickerSheet({ workspaceId, onSelect, onClose }, ref) { + const { t } = useTranslation(); + const { data, isLoading } = useFlows(workspaceId); + + const flows = useMemo( + () => (data ?? []).filter((flow) => flow.active && flow.enableInInbox), + [data], + ); + + return ( + <Sheet ref={ref} snapPoints={['60%', '90%']} onDismiss={onClose}> + <SheetHeader title={t('chat.sendFlow')} onClose={onClose} /> + <BottomSheetFlatList + data={flows} + keyExtractor={(flow) => flow.id} + ListEmptyComponent={!isLoading ? <EmptyState title={t('flows.empty')} /> : null} + renderItem={({ item }) => ( + <ListItem title={item.name} showChevron onPress={() => onSelect(item)} /> + )} + /> + </Sheet> + ); + }, +); diff --git a/src/features/chat/components/location-view.tsx b/src/features/chat/components/location-view.tsx new file mode 100644 index 0000000..5863862 --- /dev/null +++ b/src/features/chat/components/location-view.tsx @@ -0,0 +1,58 @@ +import { useTranslation } from 'react-i18next'; +import { StyleSheet, View } from 'react-native'; + +import { Icon } from '@/components/ui/icon'; +import { Text } from '@/components/ui/text'; +import type { Message } from '@/features/chat/api/use-messages-infinite'; +import { useTheme } from '@/theme/use-theme'; + +interface LocationViewProps { + contentAttributes: Message['contentAttributes']; + outbound: boolean; +} + +/** `contentAttributes` is typed as a loose JSON-value union in the generated schema (no dedicated + * location shape) — this narrows defensively rather than assuming `{ lat, lng }` exist. Renders a + * static "location shared" indicator with coordinates when parseable, since embedding a map view + * is out of scope for this phase. */ +function parseLatLng( + contentAttributes: Message['contentAttributes'], +): { lat: number; lng: number } | null { + if ( + !contentAttributes || + typeof contentAttributes !== 'object' || + Array.isArray(contentAttributes) + ) { + return null; + } + const record = contentAttributes as Record<string, unknown>; + const lat = record.lat ?? record.latitude; + const lng = record.lng ?? record.longitude; + if (typeof lat === 'number' && typeof lng === 'number') { + return { lat, lng }; + } + return null; +} + +export function LocationView({ contentAttributes, outbound }: LocationViewProps) { + const { t } = useTranslation(); + const { colors, spacing } = useTheme(); + const coords = parseLatLng(contentAttributes); + const textColor = outbound ? colors.bubbleOutText : colors.bubbleInText; + + return ( + <View style={[styles.row, { gap: spacing.xs }]}> + <Icon name="location" size={18} color={textColor} /> + <Text variant="caption" style={{ color: textColor }}> + {coords ? `${coords.lat.toFixed(5)}, ${coords.lng.toFixed(5)}` : t('chat.locationShared')} + </Text> + </View> + ); +} + +const styles = StyleSheet.create({ + row: { + flexDirection: 'row', + alignItems: 'center', + }, +}); diff --git a/src/features/chat/components/message-actions-menu.tsx b/src/features/chat/components/message-actions-menu.tsx new file mode 100644 index 0000000..3d15afd --- /dev/null +++ b/src/features/chat/components/message-actions-menu.tsx @@ -0,0 +1,63 @@ +import type BottomSheet from '@gorhom/bottom-sheet'; +import { forwardRef } from 'react'; +import { useTranslation } from 'react-i18next'; + +import { ListItem } from '@/components/ui/list-item'; +import { Sheet } from '@/components/ui/sheet'; +import type { Message } from '@/features/chat/api/use-messages-infinite'; + +import { getOptimisticStatus } from './optimistic-message'; + +interface MessageActionsMenuProps { + message: Message | null; + onEdit: (message: Message) => void; + onReply: (message: Message) => void; + onDelete: (message: Message) => void; + onLike: (message: Message) => void; + onHide: (message: Message) => void; + /** "Edit & resend" — restores the failed bubble's text to the draft and removes the failed + * entry, distinct from the plain tap-to-retry on the bubble itself (avoids a duplicate-send if + * the user both retries AND resends the restored draft). Only relevant/shown for a failed + * optimistic bubble. */ + onEditAndResend: (message: Message) => void; +} + +/** Long-press action sheet for a message: reply, edit (text messages from us only), delete, like, + * hide, and — for a failed optimistic bubble specifically — "Edit & resend". `attributes.liked`/ + * `attributes.hidden` aren't strongly typed in the generated schema (loose JSON-value union), so + * this always shows both toggle actions rather than trying to read current state from + * `attributes` — the mutation itself is idempotent either way. */ +export const MessageActionsMenu = forwardRef<BottomSheet, MessageActionsMenuProps>( + function MessageActionsMenu( + { message, onEdit, onReply, onDelete, onLike, onHide, onEditAndResend }, + ref, + ) { + const { t } = useTranslation(); + + if (!message) { + return ( + <Sheet ref={ref} snapPoints={['1%']}> + <></> + </Sheet> + ); + } + + const optimistic = getOptimisticStatus(message); + const isFailed = optimistic?.status === 'failed'; + const canEdit = !isFailed && message.senderType !== 'contact' && message.contentType === 'text'; + const canReply = !isFailed && !message.deletedAt; + + return ( + <Sheet ref={ref} snapPoints={['40%']}> + {isFailed ? ( + <ListItem title={t('chat.editAndResend')} onPress={() => onEditAndResend(message)} /> + ) : null} + {canReply ? <ListItem title={t('chat.reply')} onPress={() => onReply(message)} /> : null} + {canEdit ? <ListItem title={t('chat.edit')} onPress={() => onEdit(message)} /> : null} + {!isFailed ? <ListItem title={t('chat.like')} onPress={() => onLike(message)} /> : null} + {!isFailed ? <ListItem title={t('chat.hide')} onPress={() => onHide(message)} /> : null} + <ListItem title={t('chat.delete')} destructive onPress={() => onDelete(message)} /> + </Sheet> + ); + }, +); diff --git a/src/features/chat/components/message-bubble.test.tsx b/src/features/chat/components/message-bubble.test.tsx new file mode 100644 index 0000000..2fdef13 --- /dev/null +++ b/src/features/chat/components/message-bubble.test.tsx @@ -0,0 +1,151 @@ +import { render, screen } from '@testing-library/react-native'; + +import { initI18n } from '@/i18n'; +import type { Message } from '@/features/chat/api/use-messages-infinite'; + +import { MessageBubble } from './message-bubble'; + +// expo-router's full module (pulled in transitively via attachment-view.tsx's `router.push` for +// the image viewer) drags in its native-stack navigator, which in turn imports expo-glass-effect's +// native view manager — unavailable under Jest ("requireNativeViewManager is not available"). +// This component only needs the imperative `router.push` call, so a minimal stand-in is enough; +// no navigation actually needs to occur in these tests. jest.mock calls are hoisted above imports +// by babel-jest regardless of source position, so this placement (after the imports, for readable +// import ordering) is functionally identical to placing it first. +jest.mock('expo-router', () => ({ + __esModule: true, + router: { push: jest.fn(), back: jest.fn() }, +})); + +beforeAll(() => { + initI18n(); +}); + +function fakeMessage(overrides: Partial<Message> = {}): Message { + return { + id: 'm-1', + createdAt: '2026-01-01T12:00:00.000Z', + updatedAt: '2026-01-01T12:00:00.000Z', + conversationId: 'conv-1', + workspaceId: 'ws-1', + contactInboxId: '', + text: 'Hello there', + contentAttributes: null, + messageType: 'outgoing', + contentType: 'text', + senderType: 'user', + sourceId: null, + deletedAt: null, + type: 'message', + parentId: null, + attributes: null, + sendError: null, + attachments: [], + ...overrides, + } as unknown as Message; +} + +const NO_OP = () => {}; + +describe('MessageBubble', () => { + it('renders message text', async () => { + await render( + <MessageBubble + message={fakeMessage({ text: 'Hello there' })} + position="single" + showMeta + messagesById={new Map()} + onLongPress={NO_OP} + onRetry={NO_OP} + />, + ); + + expect(screen.getByText('Hello there')).toBeTruthy(); + }); + + it('renders a deleted-message placeholder instead of the original text', async () => { + await render( + <MessageBubble + message={fakeMessage({ text: 'secret stuff', deletedAt: '2026-01-01T12:05:00.000Z' })} + position="single" + showMeta + messagesById={new Map()} + onLongPress={NO_OP} + onRetry={NO_OP} + />, + ); + + expect(screen.getByText('Message deleted')).toBeTruthy(); + expect(screen.queryByText('secret stuff')).toBeNull(); + }); + + it('shows the failed-to-send label for a failed optimistic bubble', async () => { + const failedMessage = { + ...fakeMessage({ text: 'oops' }), + __optimisticStatus: 'failed', + __optimisticError: 'Network error', + } as unknown as Message; + + await render( + <MessageBubble + message={failedMessage} + position="single" + showMeta + messagesById={new Map()} + onLongPress={NO_OP} + onRetry={NO_OP} + />, + ); + + expect(screen.getByText('Network error')).toBeTruthy(); + }); + + it('renders a quoted reply preview when parentId resolves in messagesById', async () => { + const parent = fakeMessage({ id: 'parent-1', text: 'original message' }); + const messagesById = new Map([['parent-1', parent]]); + + await render( + <MessageBubble + message={fakeMessage({ text: 'a reply', parentId: 'parent-1' })} + position="single" + showMeta + messagesById={messagesById} + onLongPress={NO_OP} + onRetry={NO_OP} + />, + ); + + expect(screen.getByText('original message')).toBeTruthy(); + expect(screen.getByText('a reply')).toBeTruthy(); + }); + + it('renders the "unavailable" fallback when parentId does not resolve', async () => { + await render( + <MessageBubble + message={fakeMessage({ text: 'a reply', parentId: 'missing-parent' })} + position="single" + showMeta + messagesById={new Map()} + onLongPress={NO_OP} + onRetry={NO_OP} + />, + ); + + expect(screen.getByText('Original message unavailable')).toBeTruthy(); + }); + + it('renders a centered activity chip for messageType "activity" instead of a bubble', async () => { + await render( + <MessageBubble + message={fakeMessage({ messageType: 'activity', text: 'Conversation archived' })} + position="single" + showMeta + messagesById={new Map()} + onLongPress={NO_OP} + onRetry={NO_OP} + />, + ); + + expect(screen.getByText('Conversation archived')).toBeTruthy(); + }); +}); diff --git a/src/features/chat/components/message-bubble.tsx b/src/features/chat/components/message-bubble.tsx new file mode 100644 index 0000000..78d8069 --- /dev/null +++ b/src/features/chat/components/message-bubble.tsx @@ -0,0 +1,266 @@ +import dayjs from 'dayjs'; +import { memo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Pressable, StyleSheet, View } from 'react-native'; + +import { Icon } from '@/components/ui/icon'; +import { Text } from '@/components/ui/text'; +import type { Message } from '@/features/chat/api/use-messages-infinite'; +import type { GroupPosition } from '@/features/chat/lib/message-groups'; +import { useTheme } from '@/theme/use-theme'; + +import { ActivityMessage } from './activity-message'; +import { AttachmentView } from './attachment-view'; +import { LocationView } from './location-view'; +import { getOptimisticStatus } from './optimistic-message'; +import { RichContentView } from './rich-content-view'; + +type SenderVariant = 'contact' | 'agent' | 'bot' | 'system'; + +interface MessageBubbleProps { + message: Message; + position: GroupPosition; + showMeta: boolean; + /** Lookup for resolving `parentId` into the quoted reply preview — built once by MessageList + * from the full loaded message set, not refetched per bubble. */ + messagesById: Map<string, Message>; + onLongPress?: () => void; + onRetry?: () => void; +} + +/** senderType 'contact' is inbound (left); 'bot' gets its own violet-tinted variant; 'system' is a + * centered activity chip, not a side bubble; everything else ('user'/'api') is a regular outbound + * agent bubble. There's no dedicated `isOutbound` field on the schema, so this mirrors the legacy + * app's bubble-side logic. */ +function senderVariant(message: Message): SenderVariant { + if (message.senderType === 'contact') return 'contact'; + if (message.senderType === 'bot') return 'bot'; + if (message.senderType === 'system') return 'system'; + return 'agent'; +} + +function formatTime(date: string): string { + return dayjs(date).format('HH:mm'); +} + +function MessageBubbleImpl({ + message, + position, + showMeta, + messagesById, + onLongPress, + onRetry, +}: MessageBubbleProps) { + const { t } = useTranslation(); + const { colors, spacing, radius } = useTheme(); + + if (message.messageType === 'activity') { + return <ActivityMessage message={message} />; + } + + const variant = senderVariant(message); + + if (variant === 'system') { + return <ActivityMessage message={message} />; + } + + const outbound = variant !== 'contact'; + const optimistic = getOptimisticStatus(message); + const isDeleted = Boolean(message.deletedAt); + // Defensive: this component renders whatever is in the query cache, which realtime events also + // write into. A payload missing `attachments` should degrade, not white-screen the chat. + const attachments = message.attachments ?? []; + + const bubbleColor = + variant === 'bot' ? colors.bubbleBot : outbound ? colors.bubbleOut : colors.bubbleIn; + const textColor = + variant === 'bot' + ? colors.bubbleBotText + : outbound + ? colors.bubbleOutText + : colors.bubbleInText; + + // Tails (asymmetric corner radius) only render on the terminal bubble of a group — grouped + // bubbles in the middle stay fully rounded on the "inner" side to visually chain together. + const isGroupEnd = position === 'single' || position === 'last'; + const cornerStyle = outbound + ? { borderBottomEndRadius: isGroupEnd ? radius.xs : radius.bubble } + : { borderBottomStartRadius: isGroupEnd ? radius.xs : radius.bubble }; + + const parent = message.parentId ? messagesById.get(message.parentId) : undefined; + const hasReply = Boolean(message.parentId); + + const timeLabel = formatTime(message.createdAt); + const statusLabel = + optimistic?.status === 'failed' + ? t('chat.status.failed') + : optimistic?.status === 'pending' + ? t('chat.status.pending') + : t('chat.status.sent'); + const senderLabel = + variant === 'bot' + ? t('chat.senderBot') + : variant === 'agent' + ? t('chat.senderAgent') + : t('chat.senderContact'); + const bubbleText = isDeleted ? t('chat.messageDeleted') : (message.text ?? ''); + + const accessibilityLabel = t('chat.bubbleA11yLabel', { + sender: senderLabel, + text: bubbleText, + time: timeLabel, + status: statusLabel, + }); + + return ( + <View + style={[ + styles.row, + outbound ? styles.rowOutbound : styles.rowInbound, + { marginTop: showMeta ? spacing.sm : spacing.xxs }, + ]} + > + {variant === 'bot' && showMeta ? ( + <Text + variant="micro" + style={[styles.botLabel, { color: colors.bubbleBotAccent, marginStart: spacing.xs }]} + > + {t('chat.senderBot')} + </Text> + ) : null} + + <Pressable + accessible + accessibilityLabel={accessibilityLabel} + onLongPress={optimistic || isDeleted ? undefined : onLongPress} + onPress={optimistic?.status === 'failed' ? onRetry : undefined} + style={[ + styles.bubble, + cornerStyle, + { + backgroundColor: bubbleColor, + borderRadius: radius.bubble, + padding: spacing.ms, + opacity: optimistic?.status === 'pending' ? 0.6 : 1, + }, + ]} + > + {hasReply ? ( + <View + style={[ + styles.replyQuote, + { + borderStartColor: textColor, + backgroundColor: colors.scrim, + borderRadius: radius.xs, + padding: spacing.xs, + marginBottom: spacing.xs, + }, + ]} + > + <Text variant="micro" style={{ color: textColor }} numberOfLines={2}> + {parent + ? (parent.text ?? t('chat.repliedAttachment')) + : t('chat.repliedMessageUnavailable')} + </Text> + </View> + ) : null} + + {!isDeleted && attachments.length > 0 ? ( + <AttachmentView + attachments={attachments} + outbound={outbound} + clientId={message.clientId} + /> + ) : null} + + {!isDeleted && message.contentType === 'location' ? ( + <LocationView contentAttributes={message.contentAttributes} outbound={outbound} /> + ) : !isDeleted && message.contentType === 'refLink' ? ( + <RichContentView contentType={message.contentType} outbound={outbound} /> + ) : null} + + {bubbleText ? ( + <Text + style={{ + color: isDeleted ? colors.textTertiary : textColor, + fontStyle: isDeleted ? 'italic' : 'normal', + }} + variant="body" + > + {bubbleText} + </Text> + ) : null} + + {isGroupEnd ? ( + <View style={[styles.metaRow, { gap: spacing.xxs }]}> + {optimistic?.status === 'failed' ? ( + <Icon name="alert-circle" size={12} color={colors.danger} /> + ) : optimistic?.status === 'pending' ? ( + <Icon name="time-outline" size={12} color={textColor} /> + ) : outbound ? ( + <Icon name="checkmark" size={12} color={textColor} /> + ) : null} + <Text variant="micro" numeric style={{ color: textColor, opacity: 0.75 }}> + {timeLabel} + </Text> + </View> + ) : null} + </Pressable> + {optimistic?.status === 'failed' ? ( + <Text variant="caption" color="danger" style={styles.failedLabel}> + {optimistic.errorMessage ?? t('chat.failedToSendTapRetry')} + </Text> + ) : null} + </View> + ); +} + +function messagesEqual(prev: MessageBubbleProps, next: MessageBubbleProps): boolean { + const prevOptimistic = getOptimisticStatus(prev.message); + const nextOptimistic = getOptimisticStatus(next.message); + return ( + prev.message.id === next.message.id && + prev.message.updatedAt === next.message.updatedAt && + prev.message.deletedAt === next.message.deletedAt && + prev.position === next.position && + prev.showMeta === next.showMeta && + prevOptimistic?.status === nextOptimistic?.status && + prev.messagesById === next.messagesById + ); +} + +export const MessageBubble = memo(MessageBubbleImpl, messagesEqual); + +const styles = StyleSheet.create({ + row: { + paddingHorizontal: 12, + }, + // Intentionally left as flex-start/flex-end (not left/right) so RTL auto-mirrors bubble side — + // conventional RTL chat layout (e.g. WhatsApp Arabic still puts outbound bubbles on the left). + rowOutbound: { + alignItems: 'flex-end', + }, + rowInbound: { + alignItems: 'flex-start', + }, + bubble: { + maxWidth: '78%', + gap: 4, + }, + botLabel: { + fontWeight: '700', + marginBottom: 2, + }, + replyQuote: { + borderStartWidth: 2, + }, + metaRow: { + flexDirection: 'row', + alignItems: 'center', + alignSelf: 'flex-end', + }, + failedLabel: { + marginTop: 2, + }, +}); diff --git a/src/features/chat/components/message-list.tsx b/src/features/chat/components/message-list.tsx new file mode 100644 index 0000000..cec6654 --- /dev/null +++ b/src/features/chat/components/message-list.tsx @@ -0,0 +1,224 @@ +import { FlashList, type FlashListRef } from '@shopify/flash-list'; +import dayjs from 'dayjs'; +import { forwardRef, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react'; +import type { NativeScrollEvent, NativeSyntheticEvent } from 'react-native'; +import { StyleSheet } from 'react-native'; +import { useTranslation } from 'react-i18next'; + +import { DateSeparator } from '@/components/ui/date-separator'; +import { EmptyState } from '@/components/ui/empty-state'; +import { Skeleton } from '@/components/ui/skeleton'; +import type { Message } from '@/features/chat/api/use-messages-infinite'; +import { computeMessageGroups } from '@/features/chat/lib/message-groups'; + +import { MessageBubble } from './message-bubble'; +import { ScrollToBottomFab } from './scroll-to-bottom-fab'; +import { TypingIndicator } from './typing-indicator'; + +export interface MessageListHandle { + scrollToEnd: () => void; +} + +interface MessageListProps { + messages: Message[]; + isLoading: boolean; + hasNextPage: boolean; + isFetchingNextPage: boolean; + conversationId: string; + /** id of the message most recently sent by the current user, if any — used to force + * auto-scroll-to-end even when the user had scrolled away, since a just-sent message should + * always be visible to its sender. */ + justSentMessageId?: string | null; + onLoadOlder: () => void; + onLongPressMessage: (message: Message) => void; + onRetryMessage: (message: Message) => void; +} + +type ListRow = + | { kind: 'separator'; id: string; date: string } + | { + kind: 'bubble'; + id: string; + message: Message; + position: ReturnType<typeof computeMessageGroups>[number]['position']; + showMeta: boolean; + }; + +const NEAR_BOTTOM_THRESHOLD_PX = 120; + +function buildRows(messages: Message[]): ListRow[] { + const groups = computeMessageGroups(messages); + const rows: ListRow[] = []; + let lastDay: string | null = null; + + messages.forEach((message, index) => { + const day = dayjs(message.createdAt).format('YYYY-MM-DD'); + if (day !== lastDay) { + rows.push({ kind: 'separator', id: `sep-${day}`, date: message.createdAt }); + lastDay = day; + } + const group = groups[index]!; + rows.push({ + kind: 'bubble', + id: message.clientId ?? message.id, + message, + position: group.position, + showMeta: group.showMeta, + }); + }); + + return rows; +} + +/** + * FlashList v2 (installed: 2.0.2) dropped the v1 `inverted` prop in favor of + * `maintainVisibleContentPosition` — so `messages` here is rendered in NATURAL chat order + * (oldest → newest, newest at the bottom), the opposite of the API's own cursor order + * (`nextCursor` walks toward older messages). `flattenMessagePages` in use-messages-infinite.ts + * returns pages in fetch order (newest page first, since page 1 is the most recent messages), so + * the CALLER (the chat screen) is expected to pass `messages` already reversed into chronological + * order — this component memoizes the row-building (grouping + date separators) from whatever + * chronological array it's given, but does not re-reverse on every render itself (that was a + * standing perf bug: the previous version called `[...messages].reverse()` inline in the render + * body, discarding the result every re-render for no reason since `messages` is stable per query + * update). + */ +export const MessageList = forwardRef<MessageListHandle, MessageListProps>(function MessageList( + { + messages, + isLoading, + hasNextPage, + isFetchingNextPage, + conversationId, + justSentMessageId, + onLoadOlder, + onLongPressMessage, + onRetryMessage, + }, + ref, +) { + const { t } = useTranslation(); + const listRef = useRef<FlashListRef<ListRow>>(null); + const [isNearBottom, setIsNearBottom] = useState(true); + const [hasNewMessages, setHasNewMessages] = useState(0); + const lastMessageIdRef = useRef<string | null>(null); + // Mirrors `isNearBottom` state into a ref so the new-message effect below can read it as a + // snapshot at the moment a message arrives, without needing it in the effect's dependency array + // (which would re-fire the effect — and risk a duplicate scroll/counter bump — on every scroll). + const isNearBottomRef = useRef(true); + isNearBottomRef.current = isNearBottom; + + const messagesById = useMemo(() => { + const map = new Map<string, Message>(); + for (const message of messages) { + map.set(message.id, message); + if (message.clientId) map.set(message.clientId, message); + } + return map; + }, [messages]); + + const rows = useMemo(() => buildRows(messages), [messages]); + + useImperativeHandle(ref, () => ({ + scrollToEnd: () => { + listRef.current?.scrollToEnd({ animated: true }); + setHasNewMessages(0); + }, + })); + + const lastMessage = messages[messages.length - 1]; + const lastMessageId = lastMessage?.clientId ?? lastMessage?.id ?? null; + + // Auto-scroll when a NEW message arrives (id changed since the previous commit) and either it's + // our own just-sent message or we're already near the bottom; otherwise bump the "new messages" + // counter for the FAB instead of yanking the view. Runs as an effect (not inline during render) + // since scrollToEnd is an imperative side effect on the list ref, which must not fire before the + // ref is attached / the row list has committed. + useEffect(() => { + if (!lastMessageId || lastMessageId === lastMessageIdRef.current) return; + const isFirstRender = lastMessageIdRef.current === null; + const isOwnSend = justSentMessageId != null && lastMessageId === justSentMessageId; + lastMessageIdRef.current = lastMessageId; + + if (isFirstRender) return; + + if (isOwnSend || isNearBottomRef.current) { + listRef.current?.scrollToEnd({ animated: true }); + } else { + setHasNewMessages((count) => count + 1); + } + }, [lastMessageId, justSentMessageId]); + + function handleScroll(event: NativeSyntheticEvent<NativeScrollEvent>) { + const { contentOffset, contentSize, layoutMeasurement } = event.nativeEvent; + const distanceFromEnd = contentSize.height - contentOffset.y - layoutMeasurement.height; + const nearBottom = distanceFromEnd < NEAR_BOTTOM_THRESHOLD_PX; + setIsNearBottom(nearBottom); + if (nearBottom) setHasNewMessages(0); + } + + if (isLoading) { + return ( + <FlashList + data={Array.from({ length: 8 })} + keyExtractor={(_, index) => `skeleton-${index}`} + renderItem={() => <Skeleton height={44} style={styles.skeleton} />} + /> + ); + } + + if (messages.length === 0) { + return <EmptyState icon="chatbubble-ellipses-outline" title={t('chat.noMessages')} />; + } + + return ( + <> + <FlashList + ref={listRef} + data={rows} + keyExtractor={(row) => row.id} + getItemType={(row) => (row.kind === 'separator' ? 'separator' : `bubble-${row.position}`)} + renderItem={({ item }) => + item.kind === 'separator' ? ( + <DateSeparator date={item.date} /> + ) : ( + <MessageBubble + message={item.message} + position={item.position} + showMeta={item.showMeta} + messagesById={messagesById} + onLongPress={() => onLongPressMessage(item.message)} + onRetry={() => onRetryMessage(item.message)} + /> + ) + } + maintainVisibleContentPosition={{ startRenderingFromBottom: true }} + onScroll={handleScroll} + scrollEventThrottle={100} + onStartReachedThreshold={0.4} + onStartReached={() => { + if (hasNextPage && !isFetchingNextPage) { + onLoadOlder(); + } + }} + ListFooterComponent={<TypingIndicator conversationId={conversationId} />} + /> + {!isNearBottom && hasNewMessages > 0 ? ( + <ScrollToBottomFab + newMessageCount={hasNewMessages} + onPress={() => { + listRef.current?.scrollToEnd({ animated: true }); + setHasNewMessages(0); + }} + /> + ) : null} + </> + ); +}); + +const styles = StyleSheet.create({ + skeleton: { + marginHorizontal: 12, + marginVertical: 4, + }, +}); diff --git a/src/features/chat/components/optimistic-message.ts b/src/features/chat/components/optimistic-message.ts new file mode 100644 index 0000000..46dc5fe --- /dev/null +++ b/src/features/chat/components/optimistic-message.ts @@ -0,0 +1,18 @@ +import type { Message } from '@/features/chat/api/use-messages-infinite'; + +/** use-send-message.ts stashes pending/failed status on the cached message entry under these + * `__optimistic*`-prefixed keys (not part of the real `Message` schema type) rather than + * maintaining a separate parallel list — this narrows that back out safely at render time. */ +export interface OptimisticStatus { + status: 'pending' | 'failed'; + errorMessage?: string; +} + +export function getOptimisticStatus(message: Message): OptimisticStatus | null { + const raw = message as unknown as { + __optimisticStatus?: 'pending' | 'failed'; + __optimisticError?: string; + }; + if (!raw.__optimisticStatus) return null; + return { status: raw.__optimisticStatus, errorMessage: raw.__optimisticError }; +} diff --git a/src/features/chat/components/quota-banner.tsx b/src/features/chat/components/quota-banner.tsx new file mode 100644 index 0000000..bcb2115 --- /dev/null +++ b/src/features/chat/components/quota-banner.tsx @@ -0,0 +1,31 @@ +import { useTranslation } from 'react-i18next'; + +import { ErrorBanner } from '@/components/ui/error-banner'; +import type { NormalizedApiError } from '@/api/errors'; + +interface QuotaBannerProps { + error: Extract<NormalizedApiError, { kind: 'workspaceBlocked' }>; + onDismiss: () => void; +} + +const COPY_KEY: Record<'mac' | 'trialExpired' | 'unknown', string> = { + mac: 'chat.quotaMac', + trialExpired: 'chat.quotaTrialExpired', + unknown: 'chat.quotaUnknown', +}; + +/** Shown when a send fails with a 402 workspaceBlocked error; disables the composer until + * dismissed (see chat-composer.tsx) — no resolution flow (upgrade CTA) here, that's a later + * billing-integration concern outside this phase's scope. */ +export function QuotaBanner({ error, onDismiss }: QuotaBannerProps) { + const { t } = useTranslation(); + + return ( + <ErrorBanner + message={t(COPY_KEY[error.reason])} + tone="warning" + actionLabel={t('chat.dismiss')} + onAction={onDismiss} + /> + ); +} diff --git a/src/features/chat/components/rich-content-view.tsx b/src/features/chat/components/rich-content-view.tsx new file mode 100644 index 0000000..943991e --- /dev/null +++ b/src/features/chat/components/rich-content-view.tsx @@ -0,0 +1,22 @@ +import { useTranslation } from 'react-i18next'; + +import { Text } from '@/components/ui/text'; +import type { Message } from '@/features/chat/api/use-messages-infinite'; + +interface RichContentViewProps { + contentType: Message['contentType']; + outbound: boolean; +} + +/** Fallback for `contentType: "refLink"` (and any future content type the app doesn't render a + * dedicated view for) — the schema doesn't expose a structured shape for refLink beyond + * `contentAttributes` (same loose JSON-value union as location), so this renders a plain + * "unsupported content" notice rather than guessing a structured card layout. */ +export function RichContentView({ contentType }: RichContentViewProps) { + const { t } = useTranslation(); + return ( + <Text variant="caption" color="secondary"> + {t('chat.unsupportedContent', { contentType })} + </Text> + ); +} diff --git a/src/features/chat/components/saved-replies-sheet.tsx b/src/features/chat/components/saved-replies-sheet.tsx new file mode 100644 index 0000000..f0d0c1f --- /dev/null +++ b/src/features/chat/components/saved-replies-sheet.tsx @@ -0,0 +1,71 @@ +import type BottomSheet from '@gorhom/bottom-sheet'; +import { BottomSheetFlatList } from '@gorhom/bottom-sheet'; +import { useQuery } from '@tanstack/react-query'; +import { forwardRef, useState } from 'react'; +import { useTranslation } from 'react-i18next'; + +import { EmptyState } from '@/components/ui/empty-state'; +import { ListItem } from '@/components/ui/list-item'; +import { SearchBar } from '@/components/ui/search-bar'; +import { Sheet } from '@/components/ui/sheet'; +import { apiClient } from '@/api/client'; +import { ApiError } from '@/api/errors'; +import { queryKeys } from '@/api/query-keys'; + +/** `GET /workspaces/{workspaceId}/saved-replies` — flat list, no cursor pagination (confirmed in + * the generated schema: `{ data: { id, shortcut, text }[] }`), so a plain useQuery is enough. */ +function useSavedReplies(workspaceId: string) { + return useQuery({ + queryKey: queryKeys.ws.savedReplies.list(workspaceId), + queryFn: async () => { + const { data, error } = await apiClient.GET('/workspaces/{workspaceId}/saved-replies', { + params: { path: { workspaceId } }, + }); + if (error) throw new ApiError(error); + return data.data; + }, + }); +} + +interface SavedRepliesSheetProps { + workspaceId: string; + onSelect: (text: string) => void; +} + +/** Search-then-edit-before-send per the plan: tapping a reply fills the composer with its text + * (via onSelect) rather than sending immediately, so the agent can adjust it first. */ +export const SavedRepliesSheet = forwardRef<BottomSheet, SavedRepliesSheetProps>( + function SavedRepliesSheet({ workspaceId, onSelect }, ref) { + const { t } = useTranslation(); + const [search, setSearch] = useState(''); + const { data: replies } = useSavedReplies(workspaceId); + + const filtered = (replies ?? []).filter( + (reply) => + reply.shortcut.toLowerCase().includes(search.toLowerCase()) || + reply.text.toLowerCase().includes(search.toLowerCase()), + ); + + return ( + <Sheet ref={ref} snapPoints={['60%', '90%']}> + <SearchBar + value={search} + onChangeText={setSearch} + placeholder={t('chat.searchSavedReplies')} + /> + <BottomSheetFlatList + data={filtered} + keyExtractor={(reply) => reply.id} + ListEmptyComponent={<EmptyState title={t('chat.noSavedReplies')} />} + renderItem={({ item }) => ( + <ListItem + title={item.shortcut} + subtitle={item.text} + onPress={() => onSelect(item.text)} + /> + )} + /> + </Sheet> + ); + }, +); diff --git a/src/features/chat/components/scroll-to-bottom-fab.tsx b/src/features/chat/components/scroll-to-bottom-fab.tsx new file mode 100644 index 0000000..541c33e --- /dev/null +++ b/src/features/chat/components/scroll-to-bottom-fab.tsx @@ -0,0 +1,43 @@ +import { useTranslation } from 'react-i18next'; +import { StyleSheet } from 'react-native'; +import Animated, { FadeInDown, FadeOutDown } from 'react-native-reanimated'; + +import { IconButton } from '@/components/ui/icon-button'; +import { useTheme } from '@/theme/use-theme'; + +interface ScrollToBottomFabProps { + newMessageCount: number; + onPress: () => void; +} + +/** Floating action button shown over the message list when the user has scrolled away from the + * bottom and new messages have arrived — tapping scrolls to the end and (via the parent's own + * state reset) clears the badge. */ +export function ScrollToBottomFab({ newMessageCount, onPress }: ScrollToBottomFabProps) { + const { t } = useTranslation(); + const { spacing } = useTheme(); + + return ( + <Animated.View + entering={FadeInDown} + exiting={FadeOutDown} + style={[styles.wrapper, { bottom: spacing.md, end: spacing.md }]} + > + <IconButton + accessibilityLabel={t('chat.scrollToBottom')} + icon="arrow-down" + variant="filled" + size="lg" + badgeCount={newMessageCount} + onPress={onPress} + haptic="light" + /> + </Animated.View> + ); +} + +const styles = StyleSheet.create({ + wrapper: { + position: 'absolute', + }, +}); diff --git a/src/features/chat/components/typing-indicator.tsx b/src/features/chat/components/typing-indicator.tsx new file mode 100644 index 0000000..278da9a --- /dev/null +++ b/src/features/chat/components/typing-indicator.tsx @@ -0,0 +1,109 @@ +import { useTranslation } from 'react-i18next'; +import { StyleSheet, View } from 'react-native'; +import Animated, { + Easing, + useAnimatedStyle, + useSharedValue, + withDelay, + withRepeat, + withSequence, + withTiming, +} from 'react-native-reanimated'; +import { useEffect } from 'react'; + +import { Text } from '@/components/ui/text'; +import { useChatStore } from '@/features/chat/stores/use-chat-store'; +import { useReducedMotion } from '@/theme/motion'; +import { useTheme } from '@/theme/use-theme'; + +const DOT_COUNT = 3; +const BOUNCE_HEIGHT = -4; +const CYCLE_MS = 900; + +function Dot({ delayMs }: { delayMs: number }) { + const { colors } = useTheme(); + const reducedMotion = useReducedMotion(); + const translateY = useSharedValue(0); + + useEffect(() => { + if (reducedMotion) return undefined; + + translateY.value = withDelay( + delayMs, + withRepeat( + withSequence( + withTiming(BOUNCE_HEIGHT, { duration: CYCLE_MS / 3, easing: Easing.out(Easing.quad) }), + withTiming(0, { duration: CYCLE_MS / 3, easing: Easing.in(Easing.quad) }), + withTiming(0, { duration: CYCLE_MS / 3 }), + ), + -1, + ), + ); + + return () => { + translateY.value = 0; + }; + // eslint-disable-next-line react-hooks/exhaustive-deps -- shared value ref is stable. + }, [reducedMotion, delayMs]); + + const style = useAnimatedStyle(() => ({ transform: [{ translateY: translateY.value }] })); + + return ( + <Animated.View + style={[ + styles.dot, + { backgroundColor: colors.textSecondary }, + reducedMotion ? undefined : style, + ]} + /> + ); +} + +interface TypingIndicatorProps { + conversationId: string; +} + +/** Reads `typingByConversation` from the chat store — set by the realtime `typing` event handler + * (Phase 6). Renders nothing when not typing; a `t('chat.typing')`-labeled row with 3 staggered + * bouncing dots otherwise. */ +export function TypingIndicator({ conversationId }: TypingIndicatorProps) { + const { t } = useTranslation(); + const { spacing } = useTheme(); + const isTyping = useChatStore((state) => state.typingByConversation[conversationId] ?? false); + + if (!isTyping) return null; + + return ( + <View + style={[styles.container, { paddingHorizontal: spacing.md, gap: spacing.xs }]} + accessibilityLabel={t('chat.typing')} + accessible + > + <View style={[styles.dotsRow, { gap: spacing.xxs + 1 }]}> + {Array.from({ length: DOT_COUNT }).map((_, index) => ( + <Dot key={index} delayMs={index * (CYCLE_MS / DOT_COUNT)} /> + ))} + </View> + <Text variant="caption" color="secondary"> + {t('chat.typing')} + </Text> + </View> + ); +} + +const styles = StyleSheet.create({ + container: { + flexDirection: 'row', + alignItems: 'center', + paddingVertical: 4, + }, + dotsRow: { + flexDirection: 'row', + alignItems: 'center', + }, + dot: { + width: 6, + height: 6, + borderRadius: 3, + }, +}); diff --git a/src/features/chat/lib/message-groups.test.ts b/src/features/chat/lib/message-groups.test.ts new file mode 100644 index 0000000..ca86041 --- /dev/null +++ b/src/features/chat/lib/message-groups.test.ts @@ -0,0 +1,93 @@ +import type { Message } from '@/features/chat/api/use-messages-infinite'; + +import { computeMessageGroups } from './message-groups'; + +function fakeMessage(overrides: Partial<Message> = {}): Message { + return { + id: 'm-1', + createdAt: '2026-01-01T00:00:00.000Z', + updatedAt: '2026-01-01T00:00:00.000Z', + conversationId: 'conv-1', + workspaceId: 'ws-1', + contactInboxId: '', + text: 'hello', + contentAttributes: null, + messageType: 'outgoing', + contentType: 'text', + senderType: 'user', + sourceId: null, + deletedAt: null, + type: 'message', + parentId: null, + attributes: null, + sendError: null, + attachments: [], + ...overrides, + } as unknown as Message; +} + +describe('computeMessageGroups', () => { + it('marks a single isolated message as "single"', () => { + const messages = [fakeMessage({ id: 'a' })]; + + const groups = computeMessageGroups(messages); + + expect(groups).toEqual([{ position: 'single', showMeta: true }]); + }); + + it('groups consecutive same-sender messages within the time window', () => { + const messages = [ + fakeMessage({ id: 'a', senderType: 'user', createdAt: '2026-01-01T00:00:00.000Z' }), + fakeMessage({ id: 'b', senderType: 'user', createdAt: '2026-01-01T00:01:00.000Z' }), + fakeMessage({ id: 'c', senderType: 'user', createdAt: '2026-01-01T00:02:00.000Z' }), + ]; + + const groups = computeMessageGroups(messages); + + expect(groups.map((g) => g.position)).toEqual(['first', 'middle', 'last']); + expect(groups.map((g) => g.showMeta)).toEqual([true, false, false]); + }); + + it('breaks the group when the sender changes', () => { + const messages = [ + fakeMessage({ id: 'a', senderType: 'user', createdAt: '2026-01-01T00:00:00.000Z' }), + fakeMessage({ id: 'b', senderType: 'contact', createdAt: '2026-01-01T00:00:30.000Z' }), + ]; + + const groups = computeMessageGroups(messages); + + expect(groups.map((g) => g.position)).toEqual(['single', 'single']); + }); + + it('breaks the group when messages are more than 5 minutes apart', () => { + const messages = [ + fakeMessage({ id: 'a', senderType: 'user', createdAt: '2026-01-01T00:00:00.000Z' }), + fakeMessage({ id: 'b', senderType: 'user', createdAt: '2026-01-01T00:06:00.000Z' }), + ]; + + const groups = computeMessageGroups(messages); + + expect(groups.map((g) => g.position)).toEqual(['single', 'single']); + }); + + it('never groups activity or system messages with neighbors', () => { + const messages = [ + fakeMessage({ id: 'a', senderType: 'user', createdAt: '2026-01-01T00:00:00.000Z' }), + fakeMessage({ + id: 'b', + senderType: 'system', + messageType: 'activity', + createdAt: '2026-01-01T00:00:10.000Z', + }), + fakeMessage({ id: 'c', senderType: 'user', createdAt: '2026-01-01T00:00:20.000Z' }), + ]; + + const groups = computeMessageGroups(messages); + + expect(groups.map((g) => g.position)).toEqual(['single', 'single', 'single']); + }); + + it('returns an empty array for an empty input', () => { + expect(computeMessageGroups([])).toEqual([]); + }); +}); diff --git a/src/features/chat/lib/message-groups.ts b/src/features/chat/lib/message-groups.ts new file mode 100644 index 0000000..9e47022 --- /dev/null +++ b/src/features/chat/lib/message-groups.ts @@ -0,0 +1,67 @@ +import type { Message } from '@/features/chat/api/use-messages-infinite'; + +export type GroupPosition = 'single' | 'first' | 'middle' | 'last'; + +export interface MessageGroupInfo { + position: GroupPosition; + /** Show the sender/avatar meta (name label, extra top spacing) for this item — true on the + * group's LEADING edge ('first'/'single'). The trailing edge's own meta (timestamp/status) is + * derived separately by the renderer from `position` itself ('last'/'single') rather than a + * second boolean here, since unlike the name label it applies to every group shape uniformly. */ + showMeta: boolean; +} + +const GROUP_WINDOW_MS = 5 * 60 * 1000; + +/** A message never groups with a neighbor if either is a non-bubble render (activity/system) — + * those always stand alone regardless of sender/time proximity. */ +function isGroupable(message: Message): boolean { + return message.messageType !== 'activity' && message.senderType !== 'system'; +} + +function sameSender(a: Message, b: Message): boolean { + // senderType alone isn't a unique identity for 'contact' vs 'user' sends from different agents, + // but the schema doesn't expose a stable per-sender id on every message — senderType is the best + // available signal and matches what message-bubble.tsx already uses to pick bubble side/variant. + return a.senderType === b.senderType; +} + +function withinWindow(a: Message, b: Message): boolean { + const diff = Math.abs(new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()); + return diff <= GROUP_WINDOW_MS; +} + +/** + * Computes consecutive-run grouping for a CHRONOLOGICALLY ORDERED (oldest → newest) message + * array. Messages from the same sender within `GROUP_WINDOW_MS` of the previous message in the + * run are grouped: the first message in a run is 'first', the last is 'last', everything between + * is 'middle', and a run of exactly one message is 'single'. + */ +export function computeMessageGroups(messages: Message[]): MessageGroupInfo[] { + const result: MessageGroupInfo[] = new Array(messages.length); + + let runStart = 0; + for (let i = 0; i < messages.length; i += 1) { + const message = messages[i]!; + const next = messages[i + 1]; + const continuesForward = + next !== undefined && + isGroupable(message) && + isGroupable(next) && + sameSender(message, next) && + withinWindow(message, next); + + if (!continuesForward) { + // Close out the run [runStart, i]. + const runLength = i - runStart + 1; + for (let j = runStart; j <= i; j += 1) { + const position: GroupPosition = + runLength === 1 ? 'single' : j === runStart ? 'first' : j === i ? 'last' : 'middle'; + result[j] = { position, showMeta: position === 'first' || position === 'single' }; + } + runStart = i + 1; + } + } + + return result; +} diff --git a/src/features/chat/lib/pick-attachments.test.ts b/src/features/chat/lib/pick-attachments.test.ts new file mode 100644 index 0000000..e8d11b4 --- /dev/null +++ b/src/features/chat/lib/pick-attachments.test.ts @@ -0,0 +1,67 @@ +import { partitionBySize, type PickedAsset } from './pick-attachments'; + +function fakeAsset(overrides: Partial<PickedAsset> = {}): PickedAsset { + return { + uri: 'file:///tmp/a.jpg', + mimeType: 'image/jpeg', + fileName: 'a.jpg', + ...overrides, + }; +} + +describe('partitionBySize', () => { + const MAX = 5 * 1000 * 1000; + + it('puts assets under the limit into ok', () => { + const asset = fakeAsset({ size: MAX - 1 }); + + const result = partitionBySize([asset], MAX); + + expect(result.ok).toEqual([asset]); + expect(result.tooLarge).toEqual([]); + }); + + it('puts assets over the limit into tooLarge', () => { + const asset = fakeAsset({ size: MAX + 1 }); + + const result = partitionBySize([asset], MAX); + + expect(result.tooLarge).toEqual([asset]); + expect(result.ok).toEqual([]); + }); + + it('treats an asset exactly at the limit as ok (not over)', () => { + const asset = fakeAsset({ size: MAX }); + + const result = partitionBySize([asset], MAX); + + expect(result.ok).toEqual([asset]); + }); + + it('treats assets with unknown size as ok', () => { + const asset = fakeAsset({ size: undefined }); + + const result = partitionBySize([asset], MAX); + + expect(result.ok).toEqual([asset]); + expect(result.tooLarge).toEqual([]); + }); + + it('partitions a mixed batch correctly, preserving order within each bucket', () => { + const small = fakeAsset({ fileName: 'small.jpg', size: 100 }); + const big1 = fakeAsset({ fileName: 'big1.jpg', size: MAX + 1 }); + const unknown = fakeAsset({ fileName: 'unknown.jpg', size: undefined }); + const big2 = fakeAsset({ fileName: 'big2.jpg', size: MAX + 500 }); + + const result = partitionBySize([small, big1, unknown, big2], MAX); + + expect(result.ok).toEqual([small, unknown]); + expect(result.tooLarge).toEqual([big1, big2]); + }); + + it('returns empty buckets for an empty input', () => { + const result = partitionBySize([], MAX); + + expect(result).toEqual({ ok: [], tooLarge: [] }); + }); +}); diff --git a/src/features/chat/lib/pick-attachments.ts b/src/features/chat/lib/pick-attachments.ts new file mode 100644 index 0000000..01a0854 --- /dev/null +++ b/src/features/chat/lib/pick-attachments.ts @@ -0,0 +1,82 @@ +import * as DocumentPicker from 'expo-document-picker'; +import * as ImagePicker from 'expo-image-picker'; + +/** Cap on how many assets a single multi-select library pick can return — an arbitrary but + * generous ceiling to keep one send from trying to upload an unbounded number of files. */ +export const MAX_MULTI_SELECT = 10; + +export interface PickedAsset { + uri: string; + mimeType: string; + fileName: string; + /** Bytes, when the picker reports it — undefined means "unknown, don't size-gate this one + * client-side" (the camera capture path is the main case; server-side rejection is the fallback + * per the AGENTS/plan notes on camera.tsx). */ + size?: number; +} + +/** Opens the system photo/video library in multi-select mode. Returns `[]` on cancel — callers + * don't need to branch on `result.canceled` themselves. */ +export async function pickFromLibrary(): Promise<PickedAsset[]> { + const result = await ImagePicker.launchImageLibraryAsync({ + mediaTypes: ['images', 'videos'], + allowsMultipleSelection: true, + selectionLimit: MAX_MULTI_SELECT, + quality: 0.9, + }); + + if (result.canceled) return []; + + return result.assets.map((asset, index) => ({ + uri: asset.uri, + mimeType: asset.mimeType ?? (asset.type === 'video' ? 'video/mp4' : 'image/jpeg'), + fileName: + asset.fileName ?? + `attachment-${Date.now()}-${index}.${asset.type === 'video' ? 'mp4' : 'jpg'}`, + size: asset.fileSize ?? undefined, + })); +} + +/** Opens the system document picker in multi-select mode. Returns `[]` on cancel. */ +export async function pickDocuments(): Promise<PickedAsset[]> { + const result = await DocumentPicker.getDocumentAsync({ + multiple: true, + copyToCacheDirectory: true, + }); + + if (result.canceled) return []; + + return result.assets.map((asset) => ({ + uri: asset.uri, + mimeType: asset.mimeType ?? 'application/octet-stream', + fileName: asset.name, + size: asset.size ?? undefined, + })); +} + +export interface PartitionResult<T extends PickedAsset> { + ok: T[]; + tooLarge: T[]; +} + +/** Splits a batch of picked assets by a byte-size ceiling. Assets with unknown size (`size === + * undefined`) are treated as OK — there's nothing to gate against client-side, the server is the + * final authority for those. Pure and synchronous so it's fully unit-testable without mocking any + * picker module. */ +export function partitionBySize<T extends PickedAsset>( + assets: T[], + maxBytes: number, +): PartitionResult<T> { + const ok: T[] = []; + const tooLarge: T[] = []; + + for (const asset of assets) { + if (typeof asset.size === 'number' && asset.size > maxBytes) { + tooLarge.push(asset); + } else { + ok.push(asset); + } + } + + return { ok, tooLarge }; +} diff --git a/src/features/chat/stores/use-chat-store.ts b/src/features/chat/stores/use-chat-store.ts new file mode 100644 index 0000000..d2d523d --- /dev/null +++ b/src/features/chat/stores/use-chat-store.ts @@ -0,0 +1,66 @@ +import { create } from 'zustand'; + +import type { Message } from '@/features/chat/api/use-messages-infinite'; + +export type ComposerMode = { type: 'edit'; message: Message } | { type: 'reply'; message: Message }; + +interface ChatStoreState { + /** conversationId -> is the other side typing (set by realtime; use-realtime-handlers.ts pairs + * every `setTyping(id, true, seconds)` with a typing-timers.ts expiry so this never sticks + * forever if an explicit `typing: false` follow-up is dropped — see plan Phase 6). */ + typingByConversation: Record<string, boolean>; + /** conversationId -> in-progress composer draft text, so switching away and back preserves it. */ + draftsByConversation: Record<string, string>; + /** conversationId -> the composer's current edit/reply mode, if any. Cleared on send/cancel. */ + composerModeByConversation: Record<string, ComposerMode | undefined>; + /** clientId -> upload progress ratio (0-1) for an in-flight multipart send. Set by the + * `onProgress` callback threaded through send-message-multipart.ts, cleared when the mutation + * settles (success or error) — see use-send-message.ts. */ + uploadProgressByClientId: Record<string, number>; + /** The conversationId currently focused/visible on screen, or `null` when no chat screen is + * focused. Set/cleared by the chat screen's `useFocusEffect` (see + * `(app)/conversations/[conversationId]/index.tsx`); read via `useChatStore.getState()` from + * `src/lib/notifications.ts`'s notification handler (outside React) to suppress the in-app + * banner for a push notification about the conversation the user is already looking at. */ + activeConversationId: string | null; + /** `seconds` is the realtime `typing` event's TTL (falls back to a default in the caller when + * 0/undefined) — kept optional so non-realtime callers (tests, other future call sites) aren't + * forced to pass it. Not used by this store itself; use-realtime-handlers.ts reads it back out + * to schedule the matching typing-timers.ts expiry. */ + setTyping: (conversationId: string, typing: boolean, seconds?: number) => void; + setDraft: (conversationId: string, text: string) => void; + setComposerMode: (conversationId: string, mode: ComposerMode | undefined) => void; + setUploadProgress: (clientId: string, ratio: number) => void; + clearUploadProgress: (clientId: string) => void; + setActiveConversationId: (conversationId: string | null) => void; +} + +export const useChatStore = create<ChatStoreState>((set) => ({ + typingByConversation: {}, + draftsByConversation: {}, + composerModeByConversation: {}, + uploadProgressByClientId: {}, + activeConversationId: null, + setTyping: (conversationId, typing) => + set((state) => ({ + typingByConversation: { ...state.typingByConversation, [conversationId]: typing }, + })), + setActiveConversationId: (conversationId) => set({ activeConversationId: conversationId }), + setDraft: (conversationId, text) => + set((state) => ({ + draftsByConversation: { ...state.draftsByConversation, [conversationId]: text }, + })), + setComposerMode: (conversationId, mode) => + set((state) => ({ + composerModeByConversation: { ...state.composerModeByConversation, [conversationId]: mode }, + })), + setUploadProgress: (clientId, ratio) => + set((state) => ({ + uploadProgressByClientId: { ...state.uploadProgressByClientId, [clientId]: ratio }, + })), + clearUploadProgress: (clientId) => + set((state) => { + const { [clientId]: _removed, ...rest } = state.uploadProgressByClientId; + return { uploadProgressByClientId: rest }; + }), +})); diff --git a/src/features/contacts/api/use-contact-detail.ts b/src/features/contacts/api/use-contact-detail.ts new file mode 100644 index 0000000..42c285d --- /dev/null +++ b/src/features/contacts/api/use-contact-detail.ts @@ -0,0 +1,32 @@ +import { useQuery } from '@tanstack/react-query'; + +import { apiClient } from '@/api/client'; +import { ApiError } from '@/api/errors'; +import { queryKeys } from '@/api/query-keys'; +import type { operations } from '@/api/generated/schema'; + +/** + * `contactsAPIs.getContactAuthenticatedAPI` (`GET /workspaces/{workspaceId}/contacts/{contactId}`) + * embeds `tags`, `customFields`, `contactNotes`, AND `contactsOnSequences` (each with its full + * nested `sequence` object) in ONE response — verified against the generated schema. No separate + * tags/custom-fields/sequences fetch is needed for the detail view; the panel's Info/Tags/Sequences + * tabs all read off this single query. Sequences data is clean (not "awkward" per the plan's + * fallback note), so the sequences tab is kept rather than dropped. + */ +type GetContactOperation = operations['contactsAPIs.getContactAuthenticatedAPI']; +export type ContactDetail = GetContactOperation['responses'][200]['content']['application/json']; + +export function useContactDetail(workspaceId: string | null, contactId: string | null) { + return useQuery({ + queryKey: queryKeys.ws.contacts.detail(workspaceId ?? '', contactId ?? ''), + enabled: workspaceId !== null && contactId !== null, + queryFn: async (): Promise<ContactDetail> => { + const { data, error } = await apiClient.GET( + '/workspaces/{workspaceId}/contacts/{contactId}', + { params: { path: { workspaceId: workspaceId!, contactId: contactId! } } }, + ); + if (error) throw new ApiError(error); + return data; + }, + }); +} diff --git a/src/features/contacts/api/use-contact-fields.ts b/src/features/contacts/api/use-contact-fields.ts new file mode 100644 index 0000000..31336c5 --- /dev/null +++ b/src/features/contacts/api/use-contact-fields.ts @@ -0,0 +1,74 @@ +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; + +import { apiClient } from '@/api/client'; +import { ApiError } from '@/api/errors'; +import { queryKeys } from '@/api/query-keys'; + +/** + * `customFieldsAPI.privateListCustomFieldsAPI` — `GET /workspaces/{workspaceId}/custom-fields`: + * the workspace's full custom-field catalog (id/name/type), separate from a contact's currently + * *set* field values (which come embedded on `useContactDetail`). + */ +export function useWorkspaceCustomFields(workspaceId: string | null) { + return useQuery({ + queryKey: queryKeys.ws.customFields.catalog(workspaceId ?? ''), + enabled: workspaceId !== null, + queryFn: async () => { + const { data, error } = await apiClient.GET('/workspaces/{workspaceId}/custom-fields', { + params: { path: { workspaceId: workspaceId! } }, + }); + if (error) throw new ApiError(error); + return data.data; + }, + }); +} + +/** + * `contactsAPIs.addContactFieldAuthenticatedAPI` — + * `POST /workspaces/{workspaceId}/contacts/{contactId}/fields`, body `{ customFieldId, value }`. + * Also used to *update* an already-set field's value — same endpoint upserts. Invalidates the + * contact-detail cache rather than optimistically patching it, since the response body is + * untyped in the generated schema. + */ +export function useSetContactField(workspaceId: string, contactId: string) { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: async (input: { customFieldId: string; value: string }) => { + const { error } = await apiClient.POST( + '/workspaces/{workspaceId}/contacts/{contactId}/fields', + { + params: { path: { workspaceId, contactId } }, + body: input, + }, + ); + if (error) throw new ApiError(error); + }, + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: queryKeys.ws.contacts.detail(workspaceId, contactId), + }); + }, + }); +} + +/** `contactsAPIs.deleteContactFieldAuthenticatedAPI` — + * `DELETE /workspaces/{workspaceId}/contacts/{contactId}/fields/{customFieldId}`. */ +export function useDeleteContactField(workspaceId: string, contactId: string) { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: async (customFieldId: string) => { + const { error } = await apiClient.DELETE( + '/workspaces/{workspaceId}/contacts/{contactId}/fields/{customFieldId}', + { params: { path: { workspaceId, contactId, customFieldId } } }, + ); + if (error) throw new ApiError(error); + }, + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: queryKeys.ws.contacts.detail(workspaceId, contactId), + }); + }, + }); +} diff --git a/src/features/contacts/api/use-contact-tags.test.tsx b/src/features/contacts/api/use-contact-tags.test.tsx new file mode 100644 index 0000000..0e75313 --- /dev/null +++ b/src/features/contacts/api/use-contact-tags.test.tsx @@ -0,0 +1,175 @@ +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { renderHook, waitFor } from '@testing-library/react-native'; +import { act } from 'react'; +import type { PropsWithChildren } from 'react'; + +import { apiClient } from '@/api/client'; +import { queryKeys } from '@/api/query-keys'; +import type { ContactDetail } from '@/features/contacts/api/use-contact-detail'; +import { useAddContactTag, useRemoveContactTag } from '@/features/contacts/api/use-contact-tags'; + +jest.mock('@/api/client', () => ({ + apiClient: { + POST: jest.fn(), + DELETE: jest.fn(), + }, +})); + +const mockedPost = apiClient.POST as jest.Mock; +const mockedDelete = apiClient.DELETE as jest.Mock; + +const activeQueryClients: QueryClient[] = []; + +function createTestQueryClient(): QueryClient { + const queryClient = new QueryClient(); + activeQueryClients.push(queryClient); + return queryClient; +} + +afterEach(() => { + mockedPost.mockReset(); + mockedDelete.mockReset(); + while (activeQueryClients.length > 0) { + const queryClient = activeQueryClients.pop()!; + queryClient.clear(); + queryClient.unmount(); + } +}); + +function fakeContact(overrides: Partial<ContactDetail> = {}): ContactDetail { + return { + id: 'contact-1', + createdAt: '2026-01-01T00:00:00.000Z', + updatedAt: '2026-01-01T00:00:00.000Z', + avatar: null, + phoneNumber: null, + email: null, + fullName: 'Jane Doe', + workspaceId: 'ws-1', + tags: [], + customFields: [], + contactNotes: [], + contactsOnSequences: [], + ...overrides, + } as unknown as ContactDetail; +} + +function fakeTag(id: string, name: string) { + return { + id, + name, + createdAt: '2026-01-01T00:00:00.000Z', + updatedAt: '2026-01-01T00:00:00.000Z', + deletedAt: null, + folderId: null, + workspaceId: 'ws-1', + } as unknown as ContactDetail['tags'][number]; +} + +function wrapper(queryClient: QueryClient) { + return function Wrapper({ children }: PropsWithChildren) { + return <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>; + }; +} + +describe('useAddContactTag', () => { + it('optimistically pushes the full tag object into the cached contact-detail tags array', async () => { + mockedPost.mockResolvedValue({ error: undefined }); + const queryClient = createTestQueryClient(); + const detailKey = queryKeys.ws.contacts.detail('ws-1', 'contact-1'); + queryClient.setQueryData(detailKey, fakeContact()); + + const { result } = await renderHook(() => useAddContactTag('ws-1', 'contact-1'), { + wrapper: wrapper(queryClient), + }); + + const tag = fakeTag('tag-1', 'VIP'); + act(() => { + result.current.mutate(tag); + }); + + await waitFor(() => { + const detail = queryClient.getQueryData<ContactDetail>(detailKey); + expect(detail?.tags).toEqual([tag]); + }); + }); + + it('rolls back the optimistic tag on error', async () => { + mockedPost.mockResolvedValue({ error: { message: 'boom' } }); + const queryClient = createTestQueryClient(); + const detailKey = queryKeys.ws.contacts.detail('ws-1', 'contact-1'); + queryClient.setQueryData(detailKey, fakeContact()); + + const { result } = await renderHook(() => useAddContactTag('ws-1', 'contact-1'), { + wrapper: wrapper(queryClient), + }); + + act(() => { + result.current.mutate(fakeTag('tag-1', 'VIP')); + }); + + await waitFor(() => expect(result.current.isError).toBe(true)); + expect(queryClient.getQueryData<ContactDetail>(detailKey)?.tags).toEqual([]); + }); + + it('invalidates the detail query on settle to reconcile with server truth', async () => { + mockedPost.mockResolvedValue({ error: undefined }); + const queryClient = createTestQueryClient(); + const invalidateSpy = jest.spyOn(queryClient, 'invalidateQueries'); + const detailKey = queryKeys.ws.contacts.detail('ws-1', 'contact-1'); + queryClient.setQueryData(detailKey, fakeContact()); + + const { result } = await renderHook(() => useAddContactTag('ws-1', 'contact-1'), { + wrapper: wrapper(queryClient), + }); + + act(() => { + result.current.mutate(fakeTag('tag-1', 'VIP')); + }); + + await waitFor(() => expect(result.current.isSuccess).toBe(true)); + expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: detailKey }); + }); +}); + +describe('useRemoveContactTag', () => { + it('optimistically removes the tag from the cached contact-detail tags array', async () => { + mockedDelete.mockResolvedValue({ error: undefined }); + const queryClient = createTestQueryClient(); + const detailKey = queryKeys.ws.contacts.detail('ws-1', 'contact-1'); + const tag = fakeTag('tag-1', 'VIP'); + queryClient.setQueryData(detailKey, fakeContact({ tags: [tag] })); + + const { result } = await renderHook(() => useRemoveContactTag('ws-1', 'contact-1'), { + wrapper: wrapper(queryClient), + }); + + act(() => { + result.current.mutate('tag-1'); + }); + + await waitFor(() => { + const detail = queryClient.getQueryData<ContactDetail>(detailKey); + expect(detail?.tags).toEqual([]); + }); + }); + + it('rolls back the optimistic removal on error', async () => { + mockedDelete.mockResolvedValue({ error: { message: 'boom' } }); + const queryClient = createTestQueryClient(); + const detailKey = queryKeys.ws.contacts.detail('ws-1', 'contact-1'); + const tag = fakeTag('tag-1', 'VIP'); + queryClient.setQueryData(detailKey, fakeContact({ tags: [tag] })); + + const { result } = await renderHook(() => useRemoveContactTag('ws-1', 'contact-1'), { + wrapper: wrapper(queryClient), + }); + + act(() => { + result.current.mutate('tag-1'); + }); + + await waitFor(() => expect(result.current.isError).toBe(true)); + expect(queryClient.getQueryData<ContactDetail>(detailKey)?.tags).toEqual([tag]); + }); +}); diff --git a/src/features/contacts/api/use-contact-tags.ts b/src/features/contacts/api/use-contact-tags.ts new file mode 100644 index 0000000..94dde0c --- /dev/null +++ b/src/features/contacts/api/use-contact-tags.ts @@ -0,0 +1,110 @@ +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; + +import { apiClient } from '@/api/client'; +import { ApiError } from '@/api/errors'; +import { queryKeys } from '@/api/query-keys'; +import type { ContactDetail } from '@/features/contacts/api/use-contact-detail'; + +export type WorkspaceTag = ContactDetail['tags'][number]; + +/** + * Available-tags picker source: `tagsAPI.privateListWorkspaceTagsAPI` + * (`GET /workspaces/{workspaceId}/tags`) — the workspace's full tag catalog, separate from a + * contact's assigned tags (which come embedded in useContactDetail's `tags` field). + */ +export function useWorkspaceTags(workspaceId: string | null) { + return useQuery({ + queryKey: queryKeys.ws.tags.catalog(workspaceId ?? ''), + enabled: workspaceId !== null, + queryFn: async () => { + const { data, error } = await apiClient.GET('/workspaces/{workspaceId}/tags', { + params: { path: { workspaceId: workspaceId! } }, + }); + if (error) throw new ApiError(error); + return data.data; + }, + }); +} + +/** + * `contactsAPIs.addContactTagAuthenticatedAPI` — `POST /workspaces/{workspaceId}/contacts/tags`, + * body `{ ids: string[], tags: string[] }`: batch by CONTACT id (mirrors Phase 3's assign-by-contact + * pattern), `tags` is an array of tag ids. + * + * Optimistic: takes the full tag object (not just an id) so `onMutate` can push it straight into + * the cached contact-detail's `tags` array and the UI shows the new chip immediately, instead of + * waiting on a refetch. Rolls back via the pre-mutation snapshot on error; `onSettled` always + * invalidates to reconcile with server truth (the response body is untyped in the generated + * schema, so there's nothing authoritative to merge in on success). + */ +export function useAddContactTag(workspaceId: string, contactId: string) { + const queryClient = useQueryClient(); + const detailKey = queryKeys.ws.contacts.detail(workspaceId, contactId); + + return useMutation({ + mutationFn: async (tag: WorkspaceTag) => { + const { error } = await apiClient.POST('/workspaces/{workspaceId}/contacts/tags', { + params: { path: { workspaceId } }, + body: { ids: [contactId], tags: [tag.id] }, + }); + if (error) throw new ApiError(error); + }, + onMutate: async (tag: WorkspaceTag) => { + await queryClient.cancelQueries({ queryKey: detailKey }); + const previous = queryClient.getQueryData<ContactDetail>(detailKey); + if (previous && !previous.tags.some((existing) => existing.id === tag.id)) { + queryClient.setQueryData<ContactDetail>(detailKey, { + ...previous, + tags: [...previous.tags, tag], + }); + } + return { previous }; + }, + onError: (_err, _tag, context) => { + if (context?.previous) { + queryClient.setQueryData(detailKey, context.previous); + } + }, + onSettled: () => { + queryClient.invalidateQueries({ queryKey: detailKey }); + }, + }); +} + +/** `contactsAPIs.removeContactTagAuthenticatedAPI` — + * `DELETE /workspaces/{workspaceId}/contacts/{contactId}/tags/{tagId}`, single contact/tag pair. + * Optimistic: removes the tag from the cached contact-detail's `tags` array immediately, rolls + * back on error, reconciles with a settle-time invalidation. */ +export function useRemoveContactTag(workspaceId: string, contactId: string) { + const queryClient = useQueryClient(); + const detailKey = queryKeys.ws.contacts.detail(workspaceId, contactId); + + return useMutation({ + mutationFn: async (tagId: string) => { + const { error } = await apiClient.DELETE( + '/workspaces/{workspaceId}/contacts/{contactId}/tags/{tagId}', + { params: { path: { workspaceId, contactId, tagId } } }, + ); + if (error) throw new ApiError(error); + }, + onMutate: async (tagId: string) => { + await queryClient.cancelQueries({ queryKey: detailKey }); + const previous = queryClient.getQueryData<ContactDetail>(detailKey); + if (previous) { + queryClient.setQueryData<ContactDetail>(detailKey, { + ...previous, + tags: previous.tags.filter((tag) => tag.id !== tagId), + }); + } + return { previous }; + }, + onError: (_err, _tagId, context) => { + if (context?.previous) { + queryClient.setQueryData(detailKey, context.previous); + } + }, + onSettled: () => { + queryClient.invalidateQueries({ queryKey: detailKey }); + }, + }); +} diff --git a/src/features/contacts/api/use-contacts-infinite.ts b/src/features/contacts/api/use-contacts-infinite.ts new file mode 100644 index 0000000..d937363 --- /dev/null +++ b/src/features/contacts/api/use-contacts-infinite.ts @@ -0,0 +1,53 @@ +import { useInfiniteQuery } from '@tanstack/react-query'; + +import { apiClient } from '@/api/client'; +import { ApiError } from '@/api/errors'; +import { queryKeys } from '@/api/query-keys'; +import type { operations } from '@/api/generated/schema'; + +/** + * `contactsAPIs.listContactsAuthenticatedAPI` (`GET /workspaces/{workspaceId}/contacts`) uses + * OFFSET pagination (`page`/`perPage`/`pageCount`/`totalCount`), NOT the cursor shape + * (`{ data, nextCursor }`) every other list endpoint in this app uses — verified directly against + * the generated schema. `api/pagination.ts`'s CursorPage helpers don't apply here; this hook + * drives `useInfiniteQuery` with a page-number `pageParam` instead. + * + * KNOWN LIMITATION (Phase 8): unlike conversations-list, this endpoint has no first-class + * `assignedId` field — the only way to filter contacts by assignment is the generic `contactFilter` + * condition-builder DSL, which this app deliberately doesn't expose (see filter-sheet.tsx's own + * note on the conversations side). That means `onlyAssignedContacts` (Phase 6's usePermissions) + * can be enforced on the conversations tab but NOT on this contacts directory — a restricted + * member can browse the full contact list here even though their conversation view is scoped to + * their own assignments. Closing this gap would require building the `contactFilter` DSL query, + * which is out of scope for this pass; flagging it explicitly rather than silently leaving it. + */ +type ListContactsOperation = operations['contactsAPIs.listContactsAuthenticatedAPI']; +export type ListContactsResponse = + ListContactsOperation['responses'][200]['content']['application/json']; +export type ContactListItem = ListContactsResponse['data'][number]; + +const PER_PAGE = 30; + +export function useContactsInfinite(workspaceId: string | null, keyword: string) { + return useInfiniteQuery({ + queryKey: queryKeys.ws.contacts.list(workspaceId ?? '', { keyword }), + enabled: workspaceId !== null, + initialPageParam: 1, + queryFn: async ({ pageParam }): Promise<ListContactsResponse> => { + const { data, error } = await apiClient.GET('/workspaces/{workspaceId}/contacts', { + params: { + path: { workspaceId: workspaceId! }, + query: { page: pageParam, perPage: PER_PAGE, keyword: keyword || undefined }, + }, + }); + if (error) throw new ApiError(error); + return data; + }, + getNextPageParam: (lastPage, allPages) => + allPages.length < lastPage.pageCount ? allPages.length + 1 : undefined, + }); +} + +export function flattenContactPages(pages: ListContactsResponse[] | undefined): ContactListItem[] { + return pages?.flatMap((page) => page.data) ?? []; +} diff --git a/src/features/contacts/contact-header.tsx b/src/features/contacts/contact-header.tsx new file mode 100644 index 0000000..84de3f8 --- /dev/null +++ b/src/features/contacts/contact-header.tsx @@ -0,0 +1,138 @@ +import { router } from 'expo-router'; +import { StyleSheet, View } from 'react-native'; +import { useTranslation } from 'react-i18next'; + +import { Avatar } from '@/components/ui/avatar'; +import { Badge } from '@/components/ui/badge'; +import { Button } from '@/components/ui/button'; +import { Text } from '@/components/ui/text'; +import { ChannelBadge } from '@/features/conversations/components/channel-badge'; +import type { ContactDetail } from '@/features/contacts/api/use-contact-detail'; +import { useTheme } from '@/theme/use-theme'; + +const AVATAR_SIZE = 88; + +export type ContactPanelOrigin = 'conversation' | 'directory'; + +interface ContactHeaderProps { + contact: ContactDetail; + /** Which route opened this panel — 'conversation' when reached from a conversation's own + * contact tab (the conversation is already the one "Message" would navigate to, so it goes + * back instead of forward), 'directory' when reached from the standalone contacts tab (in + * which case "Message" pushes to the contact's conversation). */ + origin: ContactPanelOrigin; + /** The contact's channel identifiers (e.g. `['whatsapp', 'email']`), for the channel chip row. + * `useContactDetail`'s response doesn't embed `contactInboxes` (only the contacts-LIST + * endpoints do — see find-contact-in-cache.ts) so the caller sources this from whichever list + * cache is available; omitted/empty renders no chips. */ + channels?: string[]; + /** The contact's active conversation id, if any — sourced the same way as `channels` (not + * present on the contact-detail response). Drives the "Message" CTA; the button is disabled + * when this is null/undefined. */ + conversationId?: string | null; + onAssign?: () => void; +} + +/** + * No block/unblock action here: `contactsAPIs.blockContactAuthenticatedAPI` doesn't exist in the + * generated schema — only `blockContactWorkspaceTokenAPI`/`unblockContactWorkspaceTokenAPI` do, + * which require workspace-token auth, not the bearer/session auth this app uses throughout. The + * header shows the current blocked state (read from `blockedAt`, kept live via the realtime + * contactBlocked/contactUnblocked appliers) but can't toggle it from mobile. + */ +export function ContactHeader({ + contact, + origin, + channels = [], + conversationId = null, + onAssign, +}: ContactHeaderProps) { + const { t } = useTranslation(); + const { colors, spacing } = useTheme(); + const name = contact.fullName ?? contact.email ?? contact.phoneNumber ?? t('contacts.unknown'); + const uniqueChannels = Array.from(new Set(channels)); + + const hasConversation = Boolean(conversationId); + + function handleMessage() { + if (!conversationId) return; + if (origin === 'conversation') { + router.back(); + return; + } + router.push(`/(app)/conversations/${conversationId}`); + } + + return ( + <View style={[styles.container, { padding: spacing.lg, gap: spacing.sm }]}> + <Avatar uri={contact.avatar} name={name} size={AVATAR_SIZE} /> + <Text variant="heading" style={styles.centered}> + {name} + </Text> + + {uniqueChannels.length > 0 || contact.blockedAt ? ( + <View style={[styles.chipRow, { gap: spacing.xs }]}> + {uniqueChannels.map((channel) => ( + <ChannelBadge key={channel} channel={channel} size={16} /> + ))} + {contact.blockedAt ? <Badge tone="danger">{t('contacts.blocked')}</Badge> : null} + </View> + ) : null} + + {contact.email ? ( + <Text variant="body" color="secondary"> + {contact.email} + </Text> + ) : null} + {contact.phoneNumber ? ( + <Text variant="body" color="secondary"> + {contact.phoneNumber} + </Text> + ) : null} + + <View style={[styles.ctaRow, { gap: spacing.sm, marginTop: spacing.xs }]}> + <Button + label={t('contacts.message')} + variant="tonal" + icon="chatbubble-outline" + disabled={!hasConversation} + onPress={handleMessage} + /> + {onAssign ? ( + <Button + label={t('contacts.assign')} + variant="tonal" + icon="person-add-outline" + onPress={onAssign} + /> + ) : null} + </View> + + <View style={[styles.divider, { backgroundColor: colors.borderSubtle }]} /> + </View> + ); +} + +const styles = StyleSheet.create({ + container: { + alignItems: 'center', + }, + centered: { + textAlign: 'center', + }, + chipRow: { + flexDirection: 'row', + alignItems: 'center', + flexWrap: 'wrap', + justifyContent: 'center', + }, + ctaRow: { + flexDirection: 'row', + justifyContent: 'center', + }, + divider: { + height: StyleSheet.hairlineWidth, + width: '100%', + marginTop: 8, + }, +}); diff --git a/src/features/contacts/contact-panel.tsx b/src/features/contacts/contact-panel.tsx new file mode 100644 index 0000000..39220b2 --- /dev/null +++ b/src/features/contacts/contact-panel.tsx @@ -0,0 +1,168 @@ +import type BottomSheet from '@gorhom/bottom-sheet'; +import { useQueryClient } from '@tanstack/react-query'; +import { useRef, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { RefreshControl, ScrollView, View } from 'react-native'; + +import { EmptyState } from '@/components/ui/empty-state'; +import { ErrorBanner } from '@/components/ui/error-banner'; +import { Screen } from '@/components/ui/screen'; +import { SegmentedTabs } from '@/components/ui/segmented-tabs'; +import { Skeleton } from '@/components/ui/skeleton'; +import { WorkspaceBlockedGate } from '@/components/workspace-blocked-gate'; +import { normalizeApiError } from '@/api/errors'; +import { describeApiError } from '@/lib/describe-api-error'; +import { AssignmentSheet } from '@/features/conversations/components/assignment-sheet'; +import { useAssignConversations } from '@/features/conversations/api/use-conversation-actions'; +import { useContactDetail } from '@/features/contacts/api/use-contact-detail'; +import { ContactHeader, type ContactPanelOrigin } from '@/features/contacts/contact-header'; +import { findContactInListCache } from '@/features/contacts/lib/find-contact-in-cache'; +import { InfoTab } from '@/features/contacts/info-tab'; +import { NotesTab } from '@/features/contacts/notes-tab'; +import { SequencesTab } from '@/features/contacts/sequences-tab'; +import { TagsTab } from '@/features/contacts/tags-tab'; +import { useTheme } from '@/theme/use-theme'; + +type Tab = 'info' | 'tags' | 'notes' | 'sequences'; + +interface ContactPanelProps { + workspaceId: string | null; + contactId: string | null; + /** Which route opened this panel — see contact-header.tsx's `ContactHeaderProps.origin` doc. + * Defaults to 'directory' (the more common entry point, and the safer default for the + * "Message" CTA's push-forward behavior). */ + origin?: ContactPanelOrigin; + /** + * Authoritative channel/conversation data the caller already has on hand (e.g. the + * conversation-nested route already fetched its own `conversation` object, which embeds + * `contactInboxes` directly — no need to fall back to a cache scan). When omitted, falls back + * to `findContactInListCache` (best-effort scan of the contacts-list cache — see that file's + * doc comment for why `useContactDetail` alone can't supply this). + */ + channels?: string[]; + conversationId?: string | null; +} + +/** Shared by both contact-detail routes ((app)/contacts/[contactId] and + * (app)/conversations/[conversationId]/contact) — same tabs, same data source. + * + * All four tabs render inline into ONE scroll container owned by this panel (with pull-to- + * refresh), rather than each tab owning its own ScrollView/FlatList — fixes the audit finding + * about inconsistent scroll behavior across Info/Tags/Sequences. */ +export function ContactPanel({ + workspaceId, + contactId, + origin = 'directory', + channels: channelsOverride, + conversationId: conversationIdOverride, +}: ContactPanelProps) { + const { t } = useTranslation(); + const { spacing } = useTheme(); + const queryClient = useQueryClient(); + const [tab, setTab] = useState<Tab>('info'); + const assignmentSheetRef = useRef<BottomSheet>(null); + const { + data: contact, + isLoading, + isRefetching, + error, + refetch, + } = useContactDetail(workspaceId, contactId); + const assignMutation = useAssignConversations(workspaceId ?? ''); + + const cachedListEntry = + !channelsOverride && workspaceId && contactId + ? findContactInListCache(queryClient, workspaceId, contactId) + : undefined; + const channels = + channelsOverride ?? cachedListEntry?.contactInboxes?.map((inbox) => inbox.channel) ?? []; + const conversationId = + conversationIdOverride !== undefined + ? conversationIdOverride + : (cachedListEntry?.conversation?.id ?? null); + + if (isLoading) { + return ( + <Screen padded> + <Skeleton height={72} borderRadius={36} style={{ alignSelf: 'center' }} /> + <View style={{ height: spacing.md }} /> + <Skeleton height={16} /> + </Screen> + ); + } + + if (error) { + const normalized = normalizeApiError(error); + if (normalized.kind === 'workspaceBlocked') { + return ( + <Screen> + <WorkspaceBlockedGate reason={normalized.reason} message={normalized.message} /> + </Screen> + ); + } + return ( + <Screen padded> + <ErrorBanner + message={describeApiError(error, t)} + actionLabel={t('common.retry')} + onAction={() => refetch()} + /> + </Screen> + ); + } + + if (!contact) { + return ( + <Screen padded> + <EmptyState title={t('errors.notFound')} /> + </Screen> + ); + } + + function handleAssign(userId: string | null) { + if (!contact) return; + assignMutation.mutate({ contactIds: [contact.id], assignedId: userId }); + assignmentSheetRef.current?.close(); + } + + return ( + <Screen> + <ScrollView + refreshControl={<RefreshControl refreshing={isRefetching} onRefresh={() => refetch()} />} + > + <ContactHeader + contact={contact} + origin={origin} + channels={channels} + conversationId={conversationId} + onAssign={workspaceId ? () => assignmentSheetRef.current?.expand() : undefined} + /> + <View style={{ paddingHorizontal: spacing.md, paddingVertical: spacing.sm }}> + <SegmentedTabs<Tab> + value={tab} + onChange={setTab} + options={[ + { value: 'info', label: t('contacts.tabInfo') }, + { value: 'tags', label: t('contacts.tabTags') }, + { value: 'notes', label: t('contacts.tabNotes') }, + { value: 'sequences', label: t('contacts.tabSequences') }, + ]} + /> + </View> + {tab === 'info' ? <InfoTab contact={contact} workspaceId={workspaceId} /> : null} + {tab === 'tags' && workspaceId ? ( + <TagsTab contact={contact} workspaceId={workspaceId} /> + ) : null} + {tab === 'notes' ? <NotesTab contact={contact} workspaceId={workspaceId} /> : null} + {tab === 'sequences' ? <SequencesTab contact={contact} workspaceId={workspaceId} /> : null} + </ScrollView> + {workspaceId ? ( + <AssignmentSheet + sheetRef={assignmentSheetRef} + workspaceId={workspaceId} + onAssign={handleAssign} + /> + ) : null} + </Screen> + ); +} diff --git a/src/features/contacts/contact-row.tsx b/src/features/contacts/contact-row.tsx new file mode 100644 index 0000000..b9e079d --- /dev/null +++ b/src/features/contacts/contact-row.tsx @@ -0,0 +1,41 @@ +import { useTranslation } from 'react-i18next'; + +import { Avatar } from '@/components/ui/avatar'; +import { ListItem } from '@/components/ui/list-item'; +import type { ContactListItem } from '@/features/contacts/api/use-contacts-infinite'; +import { usePermissions } from '@/features/permissions/use-permissions'; +import { maskEmail, maskPhone } from '@/features/contacts/pii-mask'; + +interface ContactRowProps { + contact: ContactListItem; + workspaceId: string | null; + onPress: () => void; +} + +/** PII masking is gated on `usePermissions` — a member without `emailAndPhone` sees masked + * email/phone here and in info-tab.tsx, not just a decorative blur. */ +export function ContactRow({ contact, workspaceId, onPress }: ContactRowProps) { + const { t } = useTranslation(); + const permissions = usePermissions(workspaceId); + const name = contact.fullName ?? contact.email ?? contact.phoneNumber ?? t('contacts.unknown'); + const canSeePii = permissions.canSeeEmailAndPhone; + const subtitle = contact.email + ? canSeePii + ? contact.email + : maskEmail(contact.email) + : contact.phoneNumber + ? canSeePii + ? contact.phoneNumber + : maskPhone(contact.phoneNumber) + : undefined; + + return ( + <ListItem + title={name} + subtitle={subtitle} + leading={<Avatar uri={contact.avatar} name={name} />} + onPress={onPress} + showChevron + /> + ); +} diff --git a/src/features/contacts/info-tab.tsx b/src/features/contacts/info-tab.tsx new file mode 100644 index 0000000..238673f --- /dev/null +++ b/src/features/contacts/info-tab.tsx @@ -0,0 +1,165 @@ +import type BottomSheet from '@gorhom/bottom-sheet'; +import type { BottomSheetModal } from '@gorhom/bottom-sheet'; +import { BottomSheetFlatList, BottomSheetTextInput } from '@gorhom/bottom-sheet'; +import { useRef, useState } from 'react'; +import { View } from 'react-native'; +import { useTranslation } from 'react-i18next'; + +import { Button } from '@/components/ui/button'; +import { Icon } from '@/components/ui/icon'; +import { ListItem } from '@/components/ui/list-item'; +import { SectionHeader } from '@/components/ui/section-header'; +import { Sheet, SheetHeader, SheetModal } from '@/components/ui/sheet'; +import { Text } from '@/components/ui/text'; +import type { ContactDetail } from '@/features/contacts/api/use-contact-detail'; +import { + useDeleteContactField, + useSetContactField, + useWorkspaceCustomFields, +} from '@/features/contacts/api/use-contact-fields'; +import { usePermissions } from '@/features/permissions/use-permissions'; +import { maskEmail, maskPhone } from '@/features/contacts/pii-mask'; +import { useTheme } from '@/theme/use-theme'; + +interface InfoTabProps { + contact: ContactDetail; + workspaceId: string | null; +} + +function Field({ label, value }: { label: string; value: string | null }) { + const { spacing } = useTheme(); + if (!value) return null; + return ( + <View style={{ paddingHorizontal: spacing.md, paddingVertical: spacing.xs }}> + <Text variant="caption" color="secondary"> + {label} + </Text> + <Text variant="body">{value}</Text> + </View> + ); +} + +type CustomFieldCatalogEntry = { id: string; name: string; type: string }; + +/** + * Standard fields + `customFields` (embedded on the contact-detail response — see + * use-contact-detail.ts), rendered inline (no own scroll container — `ContactPanel` owns the + * single scroll surface all tabs render into). Email/phone are masked when the signed-in member + * lacks the `emailAndPhone` permission bit. Custom-field values are editable via a `SheetModal` + * (portaled above everything, including the tab bar) rather than the previous hand-rolled + * absolutely-positioned overlay containing a `BottomSheetTextInput` outside any real sheet. + */ +export function InfoTab({ contact, workspaceId }: InfoTabProps) { + const { t } = useTranslation(); + const { colors } = useTheme(); + const permissions = usePermissions(workspaceId); + const canSeePii = permissions.canSeeEmailAndPhone; + + const { data: catalog } = useWorkspaceCustomFields(workspaceId); + const setField = useSetContactField(workspaceId ?? '', contact.id); + const deleteField = useDeleteContactField(workspaceId ?? '', contact.id); + + const catalogSheetRef = useRef<BottomSheet>(null); + const editorSheetRef = useRef<BottomSheetModal>(null); + const [editingField, setEditingField] = useState<CustomFieldCatalogEntry | null>(null); + const [draftValue, setDraftValue] = useState(''); + + const setFieldValues = new Map(contact.customFields.map((field) => [field.id, field.value])); + + function openEditor(field: CustomFieldCatalogEntry) { + setEditingField(field); + setDraftValue(setFieldValues.get(field.id) ?? ''); + catalogSheetRef.current?.close(); + editorSheetRef.current?.present(); + } + + function closeEditor() { + editorSheetRef.current?.dismiss(); + setEditingField(null); + } + + function saveDraft() { + if (!editingField) return; + const trimmed = draftValue.trim(); + if (trimmed) { + setField.mutate({ customFieldId: editingField.id, value: trimmed }); + } else if (setFieldValues.has(editingField.id)) { + deleteField.mutate(editingField.id); + } + closeEditor(); + } + + return ( + <View> + <Field + label={t('contacts.email')} + value={contact.email ? (canSeePii ? contact.email : maskEmail(contact.email)) : null} + /> + <Field + label={t('contacts.phone')} + value={ + contact.phoneNumber + ? canSeePii + ? contact.phoneNumber + : maskPhone(contact.phoneNumber) + : null + } + /> + <Field label={t('contacts.locale')} value={contact.locale} /> + <Field label={t('contacts.timezone')} value={contact.timezone} /> + <Field + label={t('contacts.location')} + value={[contact.city, contact.state, contact.country].filter(Boolean).join(', ') || null} + /> + + <SectionHeader title={t('contacts.customFields')} /> + {contact.customFields.map((field) => ( + <ListItem + key={field.id} + title={field.name} + subtitle={field.value} + trailing={<Icon name="pencil-outline" size={18} color={colors.textSecondary} />} + onPress={() => openEditor(field)} + /> + ))} + <ListItem + title={t('contacts.addCustomField')} + trailing={<Icon name="add-circle-outline" size={20} color={colors.primary} />} + onPress={() => catalogSheetRef.current?.expand()} + /> + + <Sheet ref={catalogSheetRef} snapPoints={['50%', '80%']}> + <BottomSheetFlatList + data={(catalog ?? []) as CustomFieldCatalogEntry[]} + keyExtractor={(field) => field.id} + renderItem={({ item }) => ( + <ListItem + title={item.name} + subtitle={setFieldValues.get(item.id)} + onPress={() => openEditor(item)} + /> + )} + /> + </Sheet> + + <SheetModal ref={editorSheetRef} snapPoints={['40%']} onDismiss={() => setEditingField(null)}> + <SheetHeader title={editingField?.name ?? ''} onClose={closeEditor} /> + <View style={{ paddingHorizontal: 16, gap: 12 }}> + <BottomSheetTextInput + value={draftValue} + onChangeText={setDraftValue} + autoFocus + style={{ + borderWidth: 1, + borderColor: colors.borderSubtle, + borderRadius: 8, + padding: 10, + color: colors.textPrimary, + }} + /> + <Button label={t('common.save')} onPress={saveDraft} fullWidth /> + </View> + </SheetModal> + </View> + ); +} diff --git a/src/features/contacts/lib/find-contact-in-cache.test.ts b/src/features/contacts/lib/find-contact-in-cache.test.ts new file mode 100644 index 0000000..59fbf85 --- /dev/null +++ b/src/features/contacts/lib/find-contact-in-cache.test.ts @@ -0,0 +1,104 @@ +import { QueryClient } from '@tanstack/react-query'; + +import type { + ContactListItem, + ListContactsResponse, +} from '@/features/contacts/api/use-contacts-infinite'; + +import { findContactInListCache } from './find-contact-in-cache'; + +function fakeContact(id: string, overrides: Partial<ContactListItem> = {}): ContactListItem { + return { + id, + createdAt: '2026-01-01T00:00:00.000Z', + updatedAt: '2026-01-01T00:00:00.000Z', + avatar: null, + phoneNumber: null, + email: null, + fullName: `Contact ${id}`, + blockedAt: null, + workspaceId: 'ws-1', + contactInboxes: [], + conversation: null, + ...overrides, + } as unknown as ContactListItem; +} + +function fakePage(items: ContactListItem[]): ListContactsResponse { + return { data: items, pageCount: 1, totalCount: items.length, totalCountCapped: false }; +} + +const activeQueryClients: QueryClient[] = []; + +function createTestQueryClient(): QueryClient { + const queryClient = new QueryClient(); + activeQueryClients.push(queryClient); + return queryClient; +} + +afterEach(() => { + while (activeQueryClients.length > 0) { + const queryClient = activeQueryClients.pop()!; + queryClient.clear(); + queryClient.unmount(); + } +}); + +describe('findContactInListCache', () => { + it('finds a contact cached in a list query page for the given workspace', () => { + const queryClient = createTestQueryClient(); + const target = fakeContact('contact-1', { + contactInboxes: [{ channel: 'whatsapp' }] as unknown as ContactListItem['contactInboxes'], + conversation: { id: 'conv-1' } as unknown as ContactListItem['conversation'], + }); + queryClient.setQueryData(['ws', 'ws-1', 'contacts', 'list', { keyword: '' }], { + pages: [fakePage([fakeContact('contact-0'), target])], + pageParams: [1], + }); + + const found = findContactInListCache(queryClient, 'ws-1', 'contact-1'); + + expect(found?.id).toBe('contact-1'); + expect(found?.conversation?.id).toBe('conv-1'); + }); + + it('searches across every cached list query (different keyword filters)', () => { + const queryClient = createTestQueryClient(); + const target = fakeContact('contact-2'); + queryClient.setQueryData(['ws', 'ws-1', 'contacts', 'list', { keyword: '' }], { + pages: [fakePage([fakeContact('contact-0')])], + pageParams: [1], + }); + queryClient.setQueryData(['ws', 'ws-1', 'contacts', 'list', { keyword: 'jane' }], { + pages: [fakePage([target])], + pageParams: [1], + }); + + expect(findContactInListCache(queryClient, 'ws-1', 'contact-2')?.id).toBe('contact-2'); + }); + + it('returns undefined when the contact is not cached', () => { + const queryClient = createTestQueryClient(); + queryClient.setQueryData(['ws', 'ws-1', 'contacts', 'list', {}], { + pages: [fakePage([fakeContact('contact-0')])], + pageParams: [1], + }); + + expect(findContactInListCache(queryClient, 'ws-1', 'missing')).toBeUndefined(); + }); + + it('does not match contacts cached under a different workspace', () => { + const queryClient = createTestQueryClient(); + queryClient.setQueryData(['ws', 'ws-other', 'contacts', 'list', {}], { + pages: [fakePage([fakeContact('contact-1')])], + pageParams: [1], + }); + + expect(findContactInListCache(queryClient, 'ws-1', 'contact-1')).toBeUndefined(); + }); + + it('returns undefined when no list queries are cached at all', () => { + const queryClient = createTestQueryClient(); + expect(findContactInListCache(queryClient, 'ws-1', 'contact-1')).toBeUndefined(); + }); +}); diff --git a/src/features/contacts/lib/find-contact-in-cache.ts b/src/features/contacts/lib/find-contact-in-cache.ts new file mode 100644 index 0000000..caf550a --- /dev/null +++ b/src/features/contacts/lib/find-contact-in-cache.ts @@ -0,0 +1,39 @@ +import type { QueryClient } from '@tanstack/react-query'; + +import type { + ContactListItem, + ListContactsResponse, +} from '@/features/contacts/api/use-contacts-infinite'; + +/** + * Scans every cached contacts-list infinite query (any keyword) for a row matching `contactId`. + * + * `useContactDetail`'s `getContactAuthenticatedAPI` response does NOT embed `contactInboxes` or + * `conversation` (verified directly against the generated schema — only the contacts-LIST + * operations, `listContactsAuthenticatedAPI`/`listContactsByPOSTAuthenticatedAPI`, embed those). + * So the contact-header's channel chips and "Message" CTA source `contactInboxes`/`conversation` + * from whichever list page the user navigated from, mirroring + * `find-conversation-in-cache.ts`'s approach on the conversations side. Falls back to `undefined` + * on a cache miss (e.g. a cold deep link straight to a contact) — the header degrades gracefully + * in that case (no channel chips, "Message" disabled). + */ +export function findContactInListCache( + queryClient: QueryClient, + workspaceId: string, + contactId: string, +): ContactListItem | undefined { + return queryClient + .getQueryCache() + .findAll({ + predicate: (query) => + query.queryKey[0] === 'ws' && + query.queryKey[1] === workspaceId && + query.queryKey[2] === 'contacts' && + query.queryKey[3] === 'list', + }) + .flatMap((query) => { + const data = query.state.data as { pages: ListContactsResponse[] } | undefined; + return data?.pages.flatMap((page) => page.data) ?? []; + }) + .find((item) => item.id === contactId); +} diff --git a/src/features/contacts/notes-tab.tsx b/src/features/contacts/notes-tab.tsx new file mode 100644 index 0000000..711317d --- /dev/null +++ b/src/features/contacts/notes-tab.tsx @@ -0,0 +1,70 @@ +import { useTranslation } from 'react-i18next'; +import { StyleSheet, View } from 'react-native'; + +import { Card } from '@/components/ui/surface'; +import { EmptyState } from '@/components/ui/empty-state'; +import { RelativeTime } from '@/components/ui/relative-time'; +import { Text } from '@/components/ui/text'; +import type { ContactDetail } from '@/features/contacts/api/use-contact-detail'; +import { useWorkspaceMembersList } from '@/features/permissions/use-permissions'; +import { useTheme } from '@/theme/use-theme'; + +interface NotesTabProps { + contact: ContactDetail; + workspaceId: string | null; +} + +/** Read-only — `contactNotes` (`{ id, text, createdAt, createdById }`) is embedded on the + * contact-detail response, so no separate fetch is needed. Author name is resolved by matching + * `createdById` against the workspace members list (shared with assignment-sheet's query), with a + * generic fallback label for notes whose author is no longer a member or wasn't found. */ +export function NotesTab({ contact, workspaceId }: NotesTabProps) { + const { t } = useTranslation(); + const { spacing } = useTheme(); + const { data: members } = useWorkspaceMembersList(workspaceId ?? ''); + + function authorName(createdById: string | null): string { + if (!createdById) return t('contacts.unknownAuthor'); + const member = members?.find((m) => m.userId === createdById); + return member?.user.name ?? t('contacts.unknownAuthor'); + } + + if (contact.contactNotes.length === 0) { + return <EmptyState icon="document-text-outline" title={t('contacts.noNotes')} />; + } + + const sortedNotes = [...contact.contactNotes].sort( + (a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(), + ); + + return ( + <View style={{ padding: spacing.md, gap: spacing.sm }}> + {sortedNotes.map((note) => ( + <Card key={note.id} padding="md" style={styles.card}> + <View style={[styles.header, { gap: spacing.xs }]}> + <Text variant="bodyStrong" numberOfLines={1} style={styles.authorName}> + {authorName(note.createdById)} + </Text> + <RelativeTime date={note.createdAt} /> + </View> + <Text variant="body" color="secondary"> + {note.text} + </Text> + </Card> + ))} + </View> + ); +} + +const styles = StyleSheet.create({ + card: { + gap: 4, + }, + header: { + flexDirection: 'row', + alignItems: 'center', + }, + authorName: { + flex: 1, + }, +}); diff --git a/src/features/contacts/pii-mask.test.ts b/src/features/contacts/pii-mask.test.ts new file mode 100644 index 0000000..b5def88 --- /dev/null +++ b/src/features/contacts/pii-mask.test.ts @@ -0,0 +1,29 @@ +import { maskEmail, maskPhone } from '@/features/contacts/pii-mask'; + +describe('maskEmail', () => { + it('keeps the first 2 local-part chars and the full domain', () => { + expect(maskEmail('johndoe@example.com')).toBe('jo*****@example.com'); + }); + + it('returns *** when there is no domain', () => { + expect(maskEmail('notanemail')).toBe('***'); + }); + + it('pads short local parts to at least 3 asterisks', () => { + expect(maskEmail('ab@example.com')).toBe('ab***@example.com'); + }); +}); + +describe('maskPhone', () => { + it('keeps only the last 2 digits visible', () => { + expect(maskPhone('+15551234567')).toBe('**********67'); + }); + + it('masks the whole string when there are 2 or fewer digits', () => { + expect(maskPhone('12')).toBe('**'); + }); + + it('strips non-digit characters before counting the last 2 visible digits', () => { + expect(maskPhone('(555) 123-4567')).toBe('************67'); + }); +}); diff --git a/src/features/contacts/pii-mask.ts b/src/features/contacts/pii-mask.ts new file mode 100644 index 0000000..66200fe --- /dev/null +++ b/src/features/contacts/pii-mask.ts @@ -0,0 +1,15 @@ +/** Masks an email as `ab***@domain.com` — keeps the first 2 local-part chars and the domain so a + * restricted member can still recognize a contact without seeing the full address. */ +export function maskEmail(email: string): string { + const [local, domain] = email.split('@'); + if (!domain) return '***'; + const visible = local.slice(0, 2); + return `${visible}${'*'.repeat(Math.max(local.length - visible.length, 3))}@${domain}`; +} + +/** Masks a phone number, keeping only the last 2 digits visible. */ +export function maskPhone(phone: string): string { + const digits = phone.replace(/\D/g, ''); + if (digits.length <= 2) return '*'.repeat(phone.length); + return `${'*'.repeat(phone.length - 2)}${digits.slice(-2)}`; +} diff --git a/src/features/contacts/sequences-tab.tsx b/src/features/contacts/sequences-tab.tsx new file mode 100644 index 0000000..dd06698 --- /dev/null +++ b/src/features/contacts/sequences-tab.tsx @@ -0,0 +1,74 @@ +import type BottomSheet from '@gorhom/bottom-sheet'; +import { useRef } from 'react'; +import { View } from 'react-native'; +import { useTranslation } from 'react-i18next'; + +import { Badge } from '@/components/ui/badge'; +import { Button } from '@/components/ui/button'; +import { EmptyState } from '@/components/ui/empty-state'; +import { ListItem } from '@/components/ui/list-item'; +import { RelativeTime } from '@/components/ui/relative-time'; +import type { ContactDetail } from '@/features/contacts/api/use-contact-detail'; +import { SequencePickerSheet } from '@/features/sequences/components/sequence-picker-sheet'; +import { useTheme } from '@/theme/use-theme'; + +interface SequencesTabProps { + contact: ContactDetail; + workspaceId: string | null; +} + +/** + * `contactsOnSequences` embeds the full `sequence` object per enrollment, so no separate fetch is + * needed for the read-only enrollment list below. No longer read-only overall, though: an "Add to + * sequence" button opens `SequencePickerSheet` (flag-gated — see `src/config/features.ts` and + * `sequence-picker-sheet.tsx`'s own "Coming soon" behavior while `FEATURES.sendSequence` is off). + * Renders inline (no own scroll container — `ContactPanel` owns the single scroll surface). + */ +export function SequencesTab({ contact, workspaceId }: SequencesTabProps) { + const { t } = useTranslation(); + const { colors, spacing } = useTheme(); + const pickerSheetRef = useRef<BottomSheet>(null); + + return ( + <View style={{ paddingBottom: spacing.md }}> + <View style={{ paddingHorizontal: spacing.md, paddingTop: spacing.md }}> + <Button + label={t('contacts.addToSequence')} + variant="tonal" + icon="add-circle-outline" + onPress={() => pickerSheetRef.current?.expand()} + disabled={!workspaceId} + /> + </View> + + {contact.contactsOnSequences.length === 0 ? ( + <EmptyState icon="git-network-outline" title={t('contacts.noSequences')} /> + ) : ( + contact.contactsOnSequences.map((enrollment) => ( + <View key={enrollment.sequenceId}> + <ListItem + title={enrollment.sequence.name} + subtitle={t('contacts.sequenceStep', { step: enrollment.currentStep })} + trailing={ + <Badge color={enrollment.completedAt ? colors.success : colors.surface}> + {enrollment.completedAt + ? t('contacts.completed') + : (enrollment.status ?? t('contacts.active'))} + </Badge> + } + /> + <RelativeTime date={enrollment.enrolledAt} /> + </View> + )) + )} + + {workspaceId ? ( + <SequencePickerSheet + sheetRef={pickerSheetRef} + workspaceId={workspaceId} + contactId={contact.id} + /> + ) : null} + </View> + ); +} diff --git a/src/features/contacts/tags-tab.tsx b/src/features/contacts/tags-tab.tsx new file mode 100644 index 0000000..be7894a --- /dev/null +++ b/src/features/contacts/tags-tab.tsx @@ -0,0 +1,84 @@ +import { StyleSheet, View } from 'react-native'; +import { useTranslation } from 'react-i18next'; + +import { Chip } from '@/components/ui/chip'; +import { EmptyState } from '@/components/ui/empty-state'; +import { Icon } from '@/components/ui/icon'; +import { ListItem } from '@/components/ui/list-item'; +import { SectionHeader } from '@/components/ui/section-header'; +import type { ContactDetail } from '@/features/contacts/api/use-contact-detail'; +import { + useAddContactTag, + useRemoveContactTag, + useWorkspaceTags, +} from '@/features/contacts/api/use-contact-tags'; +import { useTheme } from '@/theme/use-theme'; + +interface TagsTabProps { + contact: ContactDetail; + workspaceId: string; +} + +/** + * `contact.tags` comes embedded from useContactDetail — these are real workspace tags, unrelated + * to the conversations feature's `tag-chips.tsx` (which renders derived status chips like + * unread/followUp, not contact tags — the conversations-list response has no `tags` field at all, + * confirmed against the schema). There is nothing to keep in sync with the inbox there. + * + * Renders inline (no own scroll/list container — `ContactPanel` owns the single scroll surface). + * Assigned tags render as removable `Chip`s (optimistic add/remove — see use-contact-tags.ts). + */ +export function TagsTab({ contact, workspaceId }: TagsTabProps) { + const { t } = useTranslation(); + const { colors, spacing } = useTheme(); + const { data: catalog } = useWorkspaceTags(workspaceId); + const addTag = useAddContactTag(workspaceId, contact.id); + const removeTag = useRemoveContactTag(workspaceId, contact.id); + + const assignedIds = new Set(contact.tags.map((tag) => tag.id)); + const available = (catalog ?? []).filter((tag) => !assignedIds.has(tag.id)); + + return ( + <View style={styles.container}> + <SectionHeader title={t('contacts.assignedTags')} /> + {contact.tags.length === 0 ? ( + <EmptyState icon="pricetag-outline" title={t('contacts.noTags')} /> + ) : ( + <View style={[styles.chipRow, { paddingHorizontal: spacing.md, gap: spacing.xs }]}> + {contact.tags.map((tag) => ( + <Chip + key={tag.id} + label={tag.name} + tone="brand" + onRemove={() => removeTag.mutate(tag.id)} + /> + ))} + </View> + )} + + {available.length > 0 ? ( + <> + <SectionHeader title={t('contacts.addTag')} /> + {available.map((item) => ( + <ListItem + key={item.id} + title={item.name} + trailing={<Icon name="add-circle-outline" size={20} color={colors.primary} />} + onPress={() => addTag.mutate(item)} + /> + ))} + </> + ) : null} + </View> + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + }, + chipRow: { + flexDirection: 'row', + flexWrap: 'wrap', + }, +}); diff --git a/src/features/conversations/api/use-conversation-actions.test.tsx b/src/features/conversations/api/use-conversation-actions.test.tsx new file mode 100644 index 0000000..68af9bf --- /dev/null +++ b/src/features/conversations/api/use-conversation-actions.test.tsx @@ -0,0 +1,154 @@ +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { renderHook, waitFor } from '@testing-library/react-native'; +import { act } from 'react'; +import type { PropsWithChildren } from 'react'; + +import { apiClient } from '@/api/client'; +import { queryKeys } from '@/api/query-keys'; +import { + useDisableBot, + useMarkConversationRead, +} from '@/features/conversations/api/use-conversation-actions'; +import type { ConversationListItem } from '@/features/conversations/api/use-conversations-infinite'; + +jest.mock('@/api/client', () => ({ + apiClient: { + POST: jest.fn(), + }, +})); + +const mockedPost = apiClient.POST as jest.Mock; + +const activeQueryClients: QueryClient[] = []; + +function createTestQueryClient(): QueryClient { + const queryClient = new QueryClient(); + activeQueryClients.push(queryClient); + return queryClient; +} + +afterEach(() => { + mockedPost.mockReset(); + while (activeQueryClients.length > 0) { + const queryClient = activeQueryClients.pop()!; + queryClient.clear(); + queryClient.unmount(); + } +}); + +function fakeConversation(overrides: Partial<ConversationListItem> = {}): ConversationListItem { + return { + id: 'conv-1', + createdAt: '2026-01-01T00:00:00.000Z', + updatedAt: '2026-01-01T00:00:00.000Z', + botEnabled: true, + botResumeAt: null, + archivedAt: null, + additionalAttributes: null, + contactLastReadAt: null, + agentLastReadAt: null, + lastActivityAt: '2026-01-01T00:00:00.000Z', + followed: false, + workspaceId: 'ws-1', + contactId: 'contact-1', + sourceId: null, + lastStep: null, + currentStep: null, + adminRepliedAt: null, + contactRepliedAt: null, + contactInboxes: [], + messages: [], + contact: null, + assignedUser: null, + assignedInboxTeam: null, + ...overrides, + } as unknown as ConversationListItem; +} + +function wrapper(queryClient: QueryClient) { + return function Wrapper({ children }: PropsWithChildren) { + return <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>; + }; +} + +describe('patchConversationDetail (via useMarkConversationRead)', () => { + it('optimistically patches a cached conversation-detail query alongside the list cache', async () => { + mockedPost.mockResolvedValue({ data: {}, error: undefined }); + const queryClient = createTestQueryClient(); + const detailKey = queryKeys.ws.conversations.detail('ws-1', 'conv-1'); + queryClient.setQueryData(detailKey, fakeConversation({ agentLastReadAt: null })); + queryClient.setQueryData(['ws', 'ws-1', 'conversations', 'list', {}], { + pages: [{ data: [fakeConversation({ agentLastReadAt: null })], nextCursor: null }], + pageParams: [undefined], + }); + + const { result } = await renderHook(() => useMarkConversationRead('ws-1'), { + wrapper: wrapper(queryClient), + }); + + act(() => { + result.current.mutate('conv-1'); + }); + + await waitFor(() => { + const detail = queryClient.getQueryData<ConversationListItem>(detailKey); + expect(detail?.agentLastReadAt).not.toBeNull(); + }); + }); + + it('leaves the detail cache untouched when nothing is cached for that conversation', async () => { + mockedPost.mockResolvedValue({ data: {}, error: undefined }); + const queryClient = createTestQueryClient(); + const detailKey = queryKeys.ws.conversations.detail('ws-1', 'conv-1'); + + const { result } = await renderHook(() => useMarkConversationRead('ws-1'), { + wrapper: wrapper(queryClient), + }); + + act(() => { + result.current.mutate('conv-1'); + }); + + await waitFor(() => expect(result.current.isSuccess).toBe(true)); + expect(queryClient.getQueryData(detailKey)).toBeUndefined(); + }); +}); + +describe('useDisableBot', () => { + it('invalidates the detail query for each disabled conversation on success', async () => { + mockedPost.mockResolvedValue({ data: {}, error: undefined }); + const queryClient = createTestQueryClient(); + const invalidateSpy = jest.spyOn(queryClient, 'invalidateQueries'); + const detailKey = queryKeys.ws.conversations.detail('ws-1', 'conv-1'); + queryClient.setQueryData(detailKey, fakeConversation()); + + const { result } = await renderHook(() => useDisableBot('ws-1'), { + wrapper: wrapper(queryClient), + }); + + act(() => { + result.current.mutate(['conv-1']); + }); + + await waitFor(() => expect(result.current.isSuccess).toBe(true)); + expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: detailKey }); + }); + + it('rolls back the optimistic botEnabled patch on error', async () => { + mockedPost.mockResolvedValue({ data: undefined, error: { message: 'boom' } }); + const queryClient = createTestQueryClient(); + const detailKey = queryKeys.ws.conversations.detail('ws-1', 'conv-1'); + queryClient.setQueryData(detailKey, fakeConversation({ botEnabled: true })); + + const { result } = await renderHook(() => useDisableBot('ws-1'), { + wrapper: wrapper(queryClient), + }); + + act(() => { + result.current.mutate(['conv-1']); + }); + + await waitFor(() => expect(result.current.isError).toBe(true)); + expect(queryClient.getQueryData<ConversationListItem>(detailKey)?.botEnabled).toBe(true); + }); +}); diff --git a/src/features/conversations/api/use-conversation-actions.ts b/src/features/conversations/api/use-conversation-actions.ts new file mode 100644 index 0000000..1a0b38e --- /dev/null +++ b/src/features/conversations/api/use-conversation-actions.ts @@ -0,0 +1,371 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query'; + +import { apiClient } from '@/api/client'; +import { ApiError } from '@/api/errors'; +import { isWorkspaceQuery, queryKeys } from '@/api/query-keys'; +import type { + ConversationListItem, + ListConversationsResponse, +} from '@/features/conversations/api/use-conversations-infinite'; + +/** + * Conversation action mutations, exactly matching the request shapes read from + * `conversationsAPI.{assignConversations,archiveConversations,unarchiveConversations,enableBot, + * disableBot,readConversation,unreadConversation,followConversation,unfollowConversation} + * AuthenticatedAPI` in src/api/generated/schema.ts: + * + * - assign: POST /conversations/assign body `{ contactIds: string[], assignedId: string | null }` + * — keyed by CONTACT id, not conversation id (per the real schema). + * - archive/unarchive/enableBot/disableBot: POST .../{action} body `{ ids: string[] }` — batch, + * conversation ids. + * - read/unread/follow/unfollow: POST .../{id}/{action}, path param, no body, single conversation. + * + * All apply an optimistic patch across every cached infinite-query page for the conversations + * list (any filter combination), rolling back on error via the snapshot each mutation captures. + */ + +type InfiniteData = { pages: ListConversationsResponse[]; pageParams: unknown[] }; + +function patchConversationInCache( + queryClient: ReturnType<typeof useQueryClient>, + workspaceId: string, + conversationId: string, + patch: Partial<ConversationListItem>, +) { + const previous = new Map<readonly unknown[], InfiniteData | undefined>(); + + queryClient + .getQueryCache() + .findAll({ + // `queryKey[3] === 'list'` narrows this to the paginated list queries specifically — the + // detail query lives under the same `['ws', id, 'conversations', ...]` prefix but as + // `['ws', id, 'conversations', 'detail', conversationId]` with a single-object cache shape + // (`{ pages, data }` doesn't apply), so without this check the `.pages.map(...)` below + // throws whenever a detail query happens to be cached for the same workspace. + predicate: (query) => + isWorkspaceQuery(query.queryKey, workspaceId) && + query.queryKey[2] === 'conversations' && + query.queryKey[3] === 'list', + }) + .forEach((query) => { + previous.set(query.queryKey, query.state.data as InfiniteData | undefined); + queryClient.setQueryData<InfiniteData>(query.queryKey, (old) => { + if (!old) return old; + return { + ...old, + pages: old.pages.map((page) => ({ + ...page, + data: page.data.map((item) => + item.id === conversationId ? { ...item, ...patch } : item, + ), + })), + }; + }); + }); + + return previous; +} + +function rollback( + queryClient: ReturnType<typeof useQueryClient>, + snapshot: Map<readonly unknown[], InfiniteData | undefined>, +) { + snapshot.forEach((data, key) => { + queryClient.setQueryData(key, data); + }); +} + +/** The conversation-detail query (`useConversationDetail`) caches a single object under + * `queryKeys.ws.conversations.detail(...)` — a different shape from the paginated list queries + * above (`{ pages, pageParams }`). Every list-cache mutation here should also patch the detail + * query when it's cached for the same conversation, so a screen reading the detail query (chat + * header, contact-nested route) doesn't go stale relative to the list the user came from. Returns + * the previous detail data (or undefined if nothing was cached) so callers can roll it back. */ +function patchConversationDetail( + queryClient: ReturnType<typeof useQueryClient>, + workspaceId: string, + conversationId: string, + patch: Partial<ConversationListItem>, +) { + const detailKey = queryKeys.ws.conversations.detail(workspaceId, conversationId); + const previous = queryClient.getQueryData<ConversationListItem>(detailKey); + + if (previous) { + queryClient.setQueryData<ConversationListItem>(detailKey, { ...previous, ...patch }); + } + + return { key: detailKey, previous }; +} + +function rollbackDetail( + queryClient: ReturnType<typeof useQueryClient>, + snapshot: { key: readonly unknown[]; previous: ConversationListItem | undefined } | undefined, +) { + if (!snapshot || !snapshot.previous) return; + queryClient.setQueryData(snapshot.key, snapshot.previous); +} + +interface ActionContext { + workspaceId: string; +} + +interface SingleActionSnapshot { + list: Map<readonly unknown[], InfiniteData | undefined>; + detail: { key: readonly unknown[]; previous: ConversationListItem | undefined }; +} + +/** Patches both the list cache (every filter combination) and the detail cache for a single + * conversation id, returning a combined snapshot `onError` can roll back with `rollbackSingle`. */ +function patchSingleConversation( + queryClient: ReturnType<typeof useQueryClient>, + workspaceId: string, + conversationId: string, + patch: Partial<ConversationListItem>, +): SingleActionSnapshot { + return { + list: patchConversationInCache(queryClient, workspaceId, conversationId, patch), + detail: patchConversationDetail(queryClient, workspaceId, conversationId, patch), + }; +} + +function rollbackSingle( + queryClient: ReturnType<typeof useQueryClient>, + snapshot: SingleActionSnapshot | undefined, +) { + if (!snapshot) return; + rollback(queryClient, snapshot.list); + rollbackDetail(queryClient, snapshot.detail); +} + +export function useMarkConversationRead(workspaceId: string) { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: async (conversationId: string) => { + const { error } = await apiClient.POST('/workspaces/{workspaceId}/conversations/{id}/read', { + params: { path: { workspaceId, id: conversationId } }, + }); + if (error) throw new ApiError(error); + }, + onMutate: (conversationId) => + patchSingleConversation(queryClient, workspaceId, conversationId, { + agentLastReadAt: new Date().toISOString(), + }), + onError: (_err, _vars, snapshot) => rollbackSingle(queryClient, snapshot), + }); +} + +export function useMarkConversationUnread(workspaceId: string) { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: async (conversationId: string) => { + const { error } = await apiClient.POST( + '/workspaces/{workspaceId}/conversations/{id}/unread', + { + params: { path: { workspaceId, id: conversationId } }, + }, + ); + if (error) throw new ApiError(error); + }, + onMutate: (conversationId) => + patchSingleConversation(queryClient, workspaceId, conversationId, { agentLastReadAt: null }), + onError: (_err, _vars, snapshot) => rollbackSingle(queryClient, snapshot), + }); +} + +export function useFollowConversation(workspaceId: string) { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: async (conversationId: string) => { + const { error } = await apiClient.POST( + '/workspaces/{workspaceId}/conversations/{id}/follow', + { + params: { path: { workspaceId, id: conversationId } }, + }, + ); + if (error) throw new ApiError(error); + }, + onMutate: (conversationId) => + patchSingleConversation(queryClient, workspaceId, conversationId, { followed: true }), + onError: (_err, _vars, snapshot) => rollbackSingle(queryClient, snapshot), + }); +} + +export function useUnfollowConversation(workspaceId: string) { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: async (conversationId: string) => { + const { error } = await apiClient.POST( + '/workspaces/{workspaceId}/conversations/{id}/unfollow', + { + params: { path: { workspaceId, id: conversationId } }, + }, + ); + if (error) throw new ApiError(error); + }, + onMutate: (conversationId) => + patchSingleConversation(queryClient, workspaceId, conversationId, { followed: false }), + onError: (_err, _vars, snapshot) => rollbackSingle(queryClient, snapshot), + }); +} + +/** Batch actions (archive/unarchive/bot toggle) patch every affected id in cache — both the list + * cache (every filter combination) and the detail cache — since the backend accepts a batch but + * the UI here always calls with a single-id array (swipe/long-press actions) — batching UI is a + * later concern if ever needed. `onSettledIds` runs after the mutation settles (success or + * error) with the affected ids, for callers that need a side effect beyond the optimistic patch + * (e.g. `useDisableBot` invalidating the detail query so the server-computed `botResumeAt` + * arrives). */ +function useBatchConversationAction( + workspaceId: string, + path: + | '/workspaces/{workspaceId}/conversations/archive' + | '/workspaces/{workspaceId}/conversations/unarchive' + | '/workspaces/{workspaceId}/conversations/enable-bot' + | '/workspaces/{workspaceId}/conversations/disable-bot', + patch: Partial<ConversationListItem>, + onSuccessIds?: (queryClient: ReturnType<typeof useQueryClient>, ids: string[]) => void, +) { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: async (ids: string[]) => { + const { error } = await apiClient.POST(path, { + params: { path: { workspaceId } }, + body: { ids }, + }); + if (error) throw new ApiError(error); + }, + onMutate: (ids: string[]) => + ids.map((id) => patchSingleConversation(queryClient, workspaceId, id, patch)), + onError: (_err, _vars, snapshots) => { + snapshots?.forEach((snapshot) => rollbackSingle(queryClient, snapshot)); + }, + onSuccess: (_data, ids) => onSuccessIds?.(queryClient, ids), + }); +} + +export function useArchiveConversations(workspaceId: string) { + return useBatchConversationAction( + workspaceId, + '/workspaces/{workspaceId}/conversations/archive', + { + archivedAt: new Date().toISOString(), + }, + ); +} + +export function useUnarchiveConversations(workspaceId: string) { + return useBatchConversationAction( + workspaceId, + '/workspaces/{workspaceId}/conversations/unarchive', + { + archivedAt: null, + }, + ); +} + +export function useEnableBot(workspaceId: string) { + return useBatchConversationAction( + workspaceId, + '/workspaces/{workspaceId}/conversations/enable-bot', + { + botEnabled: true, + }, + ); +} + +/** Disabling the bot triggers a 24h server-side pause (`botResumeAt`) — the optimistic patch above + * only knows `botEnabled: false`, it can't predict the server-computed resume timestamp. Once the + * mutation succeeds, invalidate the detail query for each affected conversation so a refetch + * brings in the real `botResumeAt` (used by `isBotActive` to show "paused until…" rather than + * just "off"). The list cache is left as-is here — it already shows a reasonable "not handled by + * bot" state from the optimistic patch, and invalidating N list queries on every disable is + * unnecessary churn; the detail query is what chat-header and contact screens read closely. */ +export function useDisableBot(workspaceId: string) { + return useBatchConversationAction( + workspaceId, + '/workspaces/{workspaceId}/conversations/disable-bot', + { + botEnabled: false, + }, + (queryClient, ids) => { + ids.forEach((id) => { + queryClient.invalidateQueries({ + queryKey: queryKeys.ws.conversations.detail(workspaceId, id), + }); + }); + }, + ); +} + +/** Patches `assignedUserId` on every cached conversation row whose `contactId` is in the given + * set — assign is keyed by contact, not conversation, so a contact can back multiple + * conversations (e.g. across channels) that all need the same optimistic update. */ +function patchAssignedUserByContact( + queryClient: ReturnType<typeof useQueryClient>, + workspaceId: string, + contactIds: string[], + assignedId: string | null, +) { + const contactIdSet = new Set(contactIds); + const previous = new Map<readonly unknown[], InfiniteData | undefined>(); + + queryClient + .getQueryCache() + .findAll({ + // See the matching comment in `patchConversationInCache` above — `queryKey[3] === 'list'` + // excludes the differently-shaped detail query from this list-only patch. + predicate: (query) => + isWorkspaceQuery(query.queryKey, workspaceId) && + query.queryKey[2] === 'conversations' && + query.queryKey[3] === 'list', + }) + .forEach((query) => { + previous.set(query.queryKey, query.state.data as InfiniteData | undefined); + queryClient.setQueryData<InfiniteData>(query.queryKey, (old) => { + if (!old) return old; + return { + ...old, + pages: old.pages.map((page) => ({ + ...page, + data: page.data.map((item) => + contactIdSet.has(item.contactId) ? { ...item, assignedUserId: assignedId } : item, + ), + })), + }; + }); + }); + + return previous; +} + +/** Assign is keyed by CONTACT id (not conversation id) per the real schema — pass the + * conversation's `contactId` field, and `assignedId: null` to unassign. */ +export function useAssignConversations(workspaceId: string) { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: async ({ + contactIds, + assignedId, + }: { + contactIds: string[]; + assignedId: string | null; + }) => { + const { error } = await apiClient.POST('/workspaces/{workspaceId}/conversations/assign', { + params: { path: { workspaceId } }, + body: { contactIds, assignedId }, + }); + if (error) throw new ApiError(error); + }, + onMutate: ({ contactIds, assignedId }) => + patchAssignedUserByContact(queryClient, workspaceId, contactIds, assignedId), + onError: (_err, _vars, snapshot) => snapshot && rollback(queryClient, snapshot), + }); +} + +export type { ActionContext }; diff --git a/src/features/conversations/api/use-conversation-detail.ts b/src/features/conversations/api/use-conversation-detail.ts new file mode 100644 index 0000000..dae4b3d --- /dev/null +++ b/src/features/conversations/api/use-conversation-detail.ts @@ -0,0 +1,39 @@ +import { useQuery } from '@tanstack/react-query'; + +import { apiClient } from '@/api/client'; +import { ApiError } from '@/api/errors'; +import { queryKeys } from '@/api/query-keys'; +import type { ConversationListItem } from '@/features/conversations/api/use-conversations-infinite'; + +interface UseConversationDetailOptions { + /** Synchronous placeholder resolver — e.g. `findConversationInListCache` — so a screen that + * navigated here from a list already showing this conversation can render instantly instead of + * flashing a "Conversation" fallback while the detail fetch is in flight (the bug this option + * exists to fix: a deep link straight into a conversation had no list cache to fall back on, + * and previously showed a hardcoded fallback name for the whole first fetch). */ + placeholderData?: () => ConversationListItem | undefined; +} + +/** `conversationsAPI.findConversationAuthenticatedAPI` — `GET /workspaces/{workspaceId}/conversations/{id}`. + * Used by the conversation-nested contact route to resolve `contactId` before rendering the shared + * ContactPanel (that route is keyed by conversationId, not contactId), and by the chat header to + * render conversation identity (name/channel/assignee/bot state) with an optional list-cache + * placeholder while the real fetch is in flight. */ +export function useConversationDetail( + workspaceId: string | null, + conversationId: string | null, + options: UseConversationDetailOptions = {}, +) { + return useQuery({ + queryKey: queryKeys.ws.conversations.detail(workspaceId ?? '', conversationId ?? ''), + enabled: workspaceId !== null && conversationId !== null, + placeholderData: options.placeholderData, + queryFn: async () => { + const { data, error } = await apiClient.GET('/workspaces/{workspaceId}/conversations/{id}', { + params: { path: { workspaceId: workspaceId!, id: conversationId! } }, + }); + if (error) throw new ApiError(error); + return data.data; + }, + }); +} diff --git a/src/features/conversations/api/use-conversations-infinite.ts b/src/features/conversations/api/use-conversations-infinite.ts new file mode 100644 index 0000000..a15529e --- /dev/null +++ b/src/features/conversations/api/use-conversations-infinite.ts @@ -0,0 +1,89 @@ +import { useInfiniteQuery } from '@tanstack/react-query'; +import { useMemo } from 'react'; +import { useShallow } from 'zustand/react/shallow'; + +import { apiClient } from '@/api/client'; +import { ApiError } from '@/api/errors'; +import { flattenPages, getNextPageParam } from '@/api/pagination'; +import { queryKeys } from '@/api/query-keys'; +import type { operations } from '@/api/generated/schema'; +import { + conversationFiltersSnapshot, + useConversationFilters, +} from '@/features/conversations/stores/use-conversation-filters'; +import { usePermissions } from '@/features/permissions/use-permissions'; +import { useAuthStore } from '@/stores/use-auth-store'; + +/** + * Request/response types derived directly from the generated schema's `operations` map, so they + * always match `conversationsAPI.listConversationsByPOSTAuthenticatedAPI` exactly (verified by + * reading src/api/generated/schema.ts — do not hand-declare these, the operation gets regenerated + * whenever `pnpm generate:api` reruns). The response shape matches api/pagination.ts's + * `CursorPage<T>` (`{ data, nextCursor, prevCursor }`), so this hook uses the shared helpers. + */ +type ListConversationsOperation = + operations['conversationsAPI.listConversationsByPOSTAuthenticatedAPI']; +export type ListConversationsResponse = + ListConversationsOperation['responses'][200]['content']['application/json']; +export type ConversationListItem = ListConversationsResponse['data'][number]; + +const PER_PAGE = 30; + +/** + * `onlyAssignedContacts` (Phase 6's usePermissions) forces `assignedId` to the current user's own + * id, overriding any manual "assigned to" filter — a restricted member must not be able to browse + * other agents' conversations by picking a different filter value. Unrestricted members keep + * whatever the filter store holds (including "unassigned"/other-user values). + */ +export function useConversationsInfinite(workspaceId: string | null) { + // `conversationFiltersSnapshot` builds a fresh object per call, and zustand v5's + // useSyncExternalStore compares selector results with Object.is (no shallow fallback, + // unlike v4) — without useShallow this re-renders on every render and infinite-loops. + const filters = useConversationFilters(useShallow(conversationFiltersSnapshot)); + const currentUserId = useAuthStore((state) => state.user?.id); + const { onlyAssignedContacts, isLoading: isPermissionsLoading } = usePermissions(workspaceId); + const effectiveAssignedId = onlyAssignedContacts + ? (currentUserId ?? filters.assignedId) + : filters.assignedId; + const effectiveFilters = useMemo( + () => ({ ...filters, assignedId: effectiveAssignedId }), + [filters, effectiveAssignedId], + ); + + return useInfiniteQuery({ + queryKey: queryKeys.ws.conversations.list(workspaceId ?? '', effectiveFilters), + // Waits for permissions to settle before firing the first fetch — `usePermissions` fails + // CLOSED (onlyAssignedContacts: true) while loading, but that fail-closed default would + // otherwise still be baked into this query's very first request/cache-key if it ran before + // the real permission bits were known, letting a restricted member's first page briefly use + // the wrong scope (or an unrestricted member's first page be needlessly scoped down). + enabled: workspaceId !== null && !isPermissionsLoading, + initialPageParam: undefined as string | undefined, + queryFn: async ({ pageParam }): Promise<ListConversationsResponse> => { + const { data, error } = await apiClient.POST('/workspaces/{workspaceId}/conversations/list', { + params: { path: { workspaceId: workspaceId! } }, + body: { + botCategory: effectiveFilters.botCategory, + assignedId: effectiveFilters.assignedId, + channel: effectiveFilters.channel, + status: effectiveFilters.status, + keyword: effectiveFilters.keyword || undefined, + botEnabled: effectiveFilters.botEnabled, + cursor: pageParam, + perPage: PER_PAGE, + }, + }); + if (error) { + throw new ApiError(error); + } + return data; + }, + getNextPageParam, + }); +} + +export function flattenConversationPages( + pages: ListConversationsResponse[] | undefined, +): ConversationListItem[] { + return flattenPages(pages); +} diff --git a/src/features/conversations/api/use-inbox-unread-count.test.tsx b/src/features/conversations/api/use-inbox-unread-count.test.tsx new file mode 100644 index 0000000..55fe447 --- /dev/null +++ b/src/features/conversations/api/use-inbox-unread-count.test.tsx @@ -0,0 +1,167 @@ +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { renderHook, act, waitFor } from '@testing-library/react-native'; +import type { PropsWithChildren } from 'react'; + +import type { ConversationListItem } from '@/features/conversations/api/use-conversations-infinite'; +import { useInboxUnreadCount } from './use-inbox-unread-count'; + +const WORKSPACE_ID = 'ws-1'; + +function fakeConversation(overrides: Partial<ConversationListItem> = {}): ConversationListItem { + return { + id: 'conv-1', + createdAt: '2026-01-01T00:00:00.000Z', + updatedAt: '2026-01-01T00:00:00.000Z', + botEnabled: true, + botResumeAt: null, + archivedAt: null, + additionalAttributes: null, + contactLastReadAt: null, + agentLastReadAt: null, + lastActivityAt: '2026-01-01T00:00:00.000Z', + followed: false, + workspaceId: WORKSPACE_ID, + contactId: 'contact-1', + sourceId: null, + lastStep: null, + currentStep: null, + adminRepliedAt: null, + contactRepliedAt: null, + contactInboxes: [], + messages: [], + contact: null, + assignedUser: null, + assignedInboxTeam: null, + ...overrides, + } as unknown as ConversationListItem; +} + +function unreadConversation(id: string): ConversationListItem { + return fakeConversation({ + id, + lastActivityAt: '2026-01-02T00:00:00.000Z', + agentLastReadAt: '2026-01-01T00:00:00.000Z', + }); +} + +function readConversation(id: string): ConversationListItem { + return fakeConversation({ + id, + lastActivityAt: '2026-01-01T00:00:00.000Z', + agentLastReadAt: '2026-01-02T00:00:00.000Z', + }); +} + +function wrapper(queryClient: QueryClient) { + return function Wrapper({ children }: PropsWithChildren) { + return <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>; + }; +} + +const activeQueryClients: QueryClient[] = []; + +function createTestQueryClient(): QueryClient { + const queryClient = new QueryClient(); + activeQueryClients.push(queryClient); + return queryClient; +} + +afterEach(() => { + while (activeQueryClients.length > 0) { + const queryClient = activeQueryClients.pop()!; + queryClient.clear(); + queryClient.unmount(); + } +}); + +describe('useInboxUnreadCount', () => { + it('returns 0 when nothing is cached', async () => { + const queryClient = createTestQueryClient(); + const { result } = await renderHook(() => useInboxUnreadCount(WORKSPACE_ID), { + wrapper: wrapper(queryClient), + }); + expect(result.current).toBe(0); + }); + + it('counts unread conversations across a single cached list page', async () => { + const queryClient = createTestQueryClient(); + queryClient.setQueryData(['ws', WORKSPACE_ID, 'conversations', 'list', {}], { + pages: [ + { + data: [unreadConversation('a'), readConversation('b'), unreadConversation('c')], + nextCursor: null, + }, + ], + pageParams: [undefined], + }); + + const { result } = await renderHook(() => useInboxUnreadCount(WORKSPACE_ID), { + wrapper: wrapper(queryClient), + }); + expect(result.current).toBe(2); + }); + + it('deduplicates conversations cached under multiple filter combinations', async () => { + const queryClient = createTestQueryClient(); + queryClient.setQueryData(['ws', WORKSPACE_ID, 'conversations', 'list', { keyword: '' }], { + pages: [{ data: [unreadConversation('a')], nextCursor: null }], + pageParams: [undefined], + }); + queryClient.setQueryData( + ['ws', WORKSPACE_ID, 'conversations', 'list', { status: ['unread'] }], + { + pages: [{ data: [unreadConversation('a')], nextCursor: null }], + pageParams: [undefined], + }, + ); + + const { result } = await renderHook(() => useInboxUnreadCount(WORKSPACE_ID), { + wrapper: wrapper(queryClient), + }); + expect(result.current).toBe(1); + }); + + it('ignores conversations cached under a different workspace', async () => { + const queryClient = createTestQueryClient(); + queryClient.setQueryData(['ws', 'other-ws', 'conversations', 'list', {}], { + pages: [{ data: [unreadConversation('a')], nextCursor: null }], + pageParams: [undefined], + }); + + const { result } = await renderHook(() => useInboxUnreadCount(WORKSPACE_ID), { + wrapper: wrapper(queryClient), + }); + expect(result.current).toBe(0); + }); + + it('recomputes when the cache changes', async () => { + const queryClient = createTestQueryClient(); + const key = ['ws', WORKSPACE_ID, 'conversations', 'list', {}]; + queryClient.setQueryData(key, { + pages: [{ data: [readConversation('a')], nextCursor: null }], + pageParams: [undefined], + }); + + const { result } = await renderHook(() => useInboxUnreadCount(WORKSPACE_ID), { + wrapper: wrapper(queryClient), + }); + expect(result.current).toBe(0); + + await act(async () => { + queryClient.setQueryData(key, { + pages: [{ data: [unreadConversation('a')], nextCursor: null }], + pageParams: [undefined], + }); + }); + + await waitFor(() => expect(result.current).toBe(1)); + }); + + it('returns 0 for a null workspaceId', async () => { + const queryClient = createTestQueryClient(); + const { result } = await renderHook(() => useInboxUnreadCount(null), { + wrapper: wrapper(queryClient), + }); + await waitFor(() => expect(result.current).toBe(0)); + }); +}); diff --git a/src/features/conversations/api/use-inbox-unread-count.ts b/src/features/conversations/api/use-inbox-unread-count.ts new file mode 100644 index 0000000..100cca1 --- /dev/null +++ b/src/features/conversations/api/use-inbox-unread-count.ts @@ -0,0 +1,67 @@ +import { useQueryClient } from '@tanstack/react-query'; +import { useSyncExternalStore } from 'react'; + +import { isUnread } from '@/features/conversations/lib/conversation-status'; +import type { + ConversationListItem, + ListConversationsResponse, +} from '@/features/conversations/api/use-conversations-infinite'; + +/** + * Counts unread conversations across every currently CACHED conversations-list page for the given + * workspace, deduplicated by conversation id (the same conversation can appear in more than one + * cached filter combination — e.g. "All" and "Unread" — and must only count once). + * + * LIMITATION: this only sees pages TanStack Query already has in cache (whatever's been scrolled + * into view / fetched this session) — it is NOT a true server-side unread total. A conversation + * that's unread but was never fetched into any cached list page (e.g. page 3 the user hasn't + * scrolled to yet) is invisible to this count. There is no unread-count endpoint on the backend + * yet (see plan's "Backend asks" #3) — this is the best available approximation until one exists. + * `Badge`'s own count-mode caps display at "99+", so no separate capping is needed here. + */ +function countUnreadFromCache( + queryClient: ReturnType<typeof useQueryClient>, + workspaceId: string, +): number { + if (!workspaceId) return 0; + + const seen = new Map<string, ConversationListItem>(); + + queryClient + .getQueryCache() + .findAll({ + predicate: (query) => + query.queryKey[0] === 'ws' && + query.queryKey[1] === workspaceId && + query.queryKey[2] === 'conversations' && + query.queryKey[3] === 'list', + }) + .forEach((query) => { + const data = query.state.data as { pages: ListConversationsResponse[] } | undefined; + data?.pages.forEach((page) => { + page.data.forEach((item) => { + seen.set(item.id, item); + }); + }); + }); + + let count = 0; + seen.forEach((conversation) => { + if (isUnread(conversation)) count += 1; + }); + return count; +} + +/** Subscribes to the query cache and recomputes the unread count on every cache change — used for + * the Inbox tab's `tabBarBadge`. `useSyncExternalStore` (rather than a `useQuery`/`useEffect` + * combo) is the correct primitive here since the "data" being read isn't a query result itself, + * it's a derived aggregate over however many list queries currently happen to be cached. */ +export function useInboxUnreadCount(workspaceId: string | null): number { + const queryClient = useQueryClient(); + + return useSyncExternalStore( + (onStoreChange) => queryClient.getQueryCache().subscribe(onStoreChange), + () => countUnreadFromCache(queryClient, workspaceId ?? ''), + () => 0, + ); +} diff --git a/src/features/conversations/components/assignment-sheet.tsx b/src/features/conversations/components/assignment-sheet.tsx new file mode 100644 index 0000000..3bd6a48 --- /dev/null +++ b/src/features/conversations/components/assignment-sheet.tsx @@ -0,0 +1,79 @@ +import type BottomSheet from '@gorhom/bottom-sheet'; +import { BottomSheetFlatList } from '@gorhom/bottom-sheet'; +import { useQuery } from '@tanstack/react-query'; +import type { RefObject } from 'react'; +import { useTranslation } from 'react-i18next'; + +import { Avatar } from '@/components/ui/avatar'; +import { EmptyState } from '@/components/ui/empty-state'; +import { ListItem } from '@/components/ui/list-item'; +import { Sheet } from '@/components/ui/sheet'; +import { apiClient } from '@/api/client'; +import { ApiError } from '@/api/errors'; +import { queryKeys } from '@/api/query-keys'; + +/** `GET /workspaces/{workspaceId}/members` — confirmed live in the generated schema, so this is a + * real (not stubbed) member list, not the deferred Phase 8 members *management* screen. */ +function useWorkspaceMembers(workspaceId: string) { + return useQuery({ + queryKey: queryKeys.ws.members.list(workspaceId), + queryFn: async () => { + const { data, error } = await apiClient.GET('/workspaces/{workspaceId}/members', { + params: { path: { workspaceId } }, + }); + if (error) throw new ApiError(error); + return data.data; + }, + }); +} + +interface AssignmentSheetProps { + sheetRef: RefObject<BottomSheet | null>; + workspaceId: string; + onAssign: (userId: string | null) => void; +} + +export function AssignmentSheet({ sheetRef, workspaceId, onAssign }: AssignmentSheetProps) { + const { t } = useTranslation(); + const { data: members } = useWorkspaceMembers(workspaceId); + + return ( + <Sheet ref={sheetRef} snapPoints={['50%', '80%']}> + <BottomSheetFlatList + data={members ?? []} + keyExtractor={(member) => member.id} + ListHeaderComponent={ + <ListItem + title={t('conversations.unassign')} + onPress={() => { + onAssign(null); + sheetRef.current?.close(); + }} + /> + } + ListEmptyComponent={ + <EmptyState + title={t('settings.noMembers')} + description={t('conversations.noOtherMembers')} + /> + } + renderItem={({ item }) => ( + <ListItem + title={item.user.name ?? t('conversations.unnamed')} + leading={ + <Avatar + uri={item.user.image} + name={item.user.name ?? t('conversations.unnamed')} + size={36} + /> + } + onPress={() => { + onAssign(item.userId); + sheetRef.current?.close(); + }} + /> + )} + /> + </Sheet> + ); +} diff --git a/src/features/conversations/components/channel-badge.tsx b/src/features/conversations/components/channel-badge.tsx new file mode 100644 index 0000000..2989a56 --- /dev/null +++ b/src/features/conversations/components/channel-badge.tsx @@ -0,0 +1,46 @@ +import { StyleSheet, View } from 'react-native'; + +import { Icon, type IconName } from '@/components/ui/icon'; +import { useTheme } from '@/theme/use-theme'; + +/** Every contactInbox `channel` string the backend can send (see schema.ts `contactInboxes[].channel` + * and the list-filter `channel` enum) — wider than `colors.channel`'s keys, so unmapped channels + * fall back to a neutral color instead of crashing on an undefined lookup. */ +const CHANNEL_ICONS: Record<string, IconName> = { + messenger: 'logo-facebook', + instagram: 'logo-instagram', + whatsapp: 'logo-whatsapp', + webchat: 'chatbubble', + email: 'mail', + smtp: 'mail', + sms: 'chatbox', + zalo: 'chatbubbles', + telegram: 'paper-plane', + tiktok: 'logo-tiktok', + omnichannel: 'apps', +}; + +interface ChannelBadgeProps { + channel: string; + size?: number; +} + +export function ChannelBadge({ channel, size = 16 }: ChannelBadgeProps) { + const { colors } = useTheme(); + const color = (colors.channel as Record<string, string>)[channel] ?? colors.textSecondary; + const icon = CHANNEL_ICONS[channel] ?? 'ellipse'; + + return ( + <View style={[styles.container, { backgroundColor: color, width: size + 6, height: size + 6 }]}> + <Icon name={icon} size={size - 4} color={colors.primaryForeground} /> + </View> + ); +} + +const styles = StyleSheet.create({ + container: { + borderRadius: 999, + alignItems: 'center', + justifyContent: 'center', + }, +}); diff --git a/src/features/conversations/components/conversation-row.test.tsx b/src/features/conversations/components/conversation-row.test.tsx new file mode 100644 index 0000000..b24decb --- /dev/null +++ b/src/features/conversations/components/conversation-row.test.tsx @@ -0,0 +1,111 @@ +import { fireEvent, render, screen } from '@testing-library/react-native'; + +import { initI18n } from '@/i18n'; +import type { ConversationListItem } from '@/features/conversations/api/use-conversations-infinite'; + +import { ConversationRow } from './conversation-row'; + +beforeAll(() => { + initI18n(); +}); + +function fakeConversation(overrides: Partial<ConversationListItem> = {}): ConversationListItem { + return { + id: 'conv-1', + createdAt: '2026-01-01T00:00:00.000Z', + updatedAt: '2026-01-01T00:00:00.000Z', + botEnabled: false, + botResumeAt: null, + archivedAt: null, + additionalAttributes: null, + contactLastReadAt: null, + agentLastReadAt: '2026-01-01T00:00:00.000Z', + lastActivityAt: '2026-01-01T00:00:00.000Z', + followed: false, + workspaceId: 'ws-1', + contactId: 'contact-1', + sourceId: null, + lastStep: null, + currentStep: null, + adminRepliedAt: null, + contactRepliedAt: null, + contactInboxes: [{ channel: 'whatsapp' }], + messages: [{ text: 'Hello there', attachmentCount: 0 }], + contact: { fullName: 'Jane Doe', avatar: null, blockedAt: null }, + assignedUser: null, + assignedInboxTeam: null, + ...overrides, + } as unknown as ConversationListItem; +} + +describe('ConversationRow', () => { + it('renders the contact name and message preview', async () => { + await render(<ConversationRow conversation={fakeConversation()} onPress={() => {}} />); + + expect(screen.getByText('Jane Doe')).toBeTruthy(); + expect(screen.getByText('Hello there')).toBeTruthy(); + }); + + it('falls back to a translated "Unknown contact" label when the contact has no name', async () => { + await render( + <ConversationRow conversation={fakeConversation({ contact: null })} onPress={() => {}} />, + ); + + expect(screen.getByText('Unknown contact')).toBeTruthy(); + }); + + it('renders a translated attachment preview when the last message has no text', async () => { + await render( + <ConversationRow + conversation={fakeConversation({ + messages: [ + { text: null, attachmentCount: 3 }, + ] as unknown as ConversationListItem['messages'], + })} + onPress={() => {}} + />, + ); + + expect(screen.getByText('📎 3 attachment(s)')).toBeTruthy(); + }); + + it('calls onPress when the row is tapped', async () => { + const onPress = jest.fn(); + await render(<ConversationRow conversation={fakeConversation()} onPress={onPress} />); + + fireEvent.press(screen.getByRole('button')); + + expect(onPress).toHaveBeenCalledTimes(1); + }); + + it('exposes accessibilityActions mirroring the wired swipe callbacks', async () => { + const onToggleRead = jest.fn(); + const onToggleBot = jest.fn(); + const onArchive = jest.fn(); + await render( + <ConversationRow + conversation={fakeConversation()} + onPress={() => {}} + onToggleRead={onToggleRead} + onToggleBot={onToggleBot} + onArchive={onArchive} + />, + ); + + const button = screen.getByLabelText(/Jane Doe/); + const actionNames = (button.props.accessibilityActions as { name: string }[]).map( + (action) => action.name, + ); + expect(actionNames).toEqual(['toggleRead', 'toggleBot', 'archive']); + + fireEvent(button, 'accessibilityAction', { nativeEvent: { actionName: 'archive' } }); + expect(onArchive).toHaveBeenCalledTimes(1); + }); + + it('omits accessibilityActions that have no wired callback', async () => { + await render(<ConversationRow conversation={fakeConversation()} onPress={() => {}} />); + + const button = screen.getByLabelText(/Jane Doe/); + expect(button.props.accessibilityActions).toEqual([]); + }); +}); diff --git a/src/features/conversations/components/conversation-row.tsx b/src/features/conversations/components/conversation-row.tsx new file mode 100644 index 0000000..f5a4edd --- /dev/null +++ b/src/features/conversations/components/conversation-row.tsx @@ -0,0 +1,298 @@ +import { memo } from 'react'; +import type { TFunction } from 'i18next'; +import { useTranslation } from 'react-i18next'; +import { StyleSheet, View } from 'react-native'; + +import { Avatar } from '@/components/ui/avatar'; +import { PressableScale } from '@/components/ui/pressable-scale'; +import { RelativeTime } from '@/components/ui/relative-time'; +import type { SwipeAction } from '@/components/ui/swipeable-row'; +import { SwipeableRow } from '@/components/ui/swipeable-row'; +import { Text } from '@/components/ui/text'; +import type { ConversationListItem } from '@/features/conversations/api/use-conversations-infinite'; +import { isBotActive, isUnread } from '@/features/conversations/lib/conversation-status'; +import { withAlpha } from '@/theme/color-utils'; +import { useTheme } from '@/theme/use-theme'; + +import { ChannelBadge } from './channel-badge'; +import type { ConversationStatusChip } from './tag-chips'; +import { TagChips } from './tag-chips'; + +interface ConversationRowProps { + conversation: ConversationListItem; + onPress: () => void; + onLongPress?: () => void; + onToggleRead?: () => void; + onToggleBot?: () => void; + onArchive?: () => void; +} + +function lastMessagePreview(conversation: ConversationListItem, t: TFunction): string { + const lastMessage = conversation.messages[conversation.messages.length - 1]; + if (!lastMessage) return ''; + if (lastMessage.text) return lastMessage.text; + if (lastMessage.attachmentCount) { + return t('conversations.attachmentPreview', { count: lastMessage.attachmentCount }); + } + return ''; +} + +function statusChips(conversation: ConversationListItem): ConversationStatusChip[] { + const chips: ConversationStatusChip[] = []; + if (conversation.followed) chips.push('followUp'); + if (conversation.archivedAt) chips.push('archived'); + if (conversation.contact?.blockedAt) chips.push('blocked'); + return chips; +} + +/** + * Presentational conversation row: renders from `conversation` and calls back to the parent + * screen for every action — it never calls a mutation hook directly. `onToggleRead`/`onToggleBot`/ + * `onArchive` are optional so this component still renders sensibly in contexts that don't wire + * swipe actions (e.g. a future preview/gallery use). + * + * Swipe: start (left in LTR) reveals a read/unread toggle; end (right in LTR) reveals bot toggle + + * archive. `SwipeableRow` itself mirrors these for RTL. Archive does not confirm inline — the + * screen is expected to show an "Archived — Undo" toast (toast+undo is less friction than a modal + * confirm and is what `onArchive` is wired to in conversations/index.tsx). + */ +export const ConversationRow = memo(function ConversationRow({ + conversation, + onPress, + onLongPress, + onToggleRead, + onToggleBot, + onArchive, +}: ConversationRowProps) { + const { t } = useTranslation(); + const { colors, spacing } = useTheme(); + const contactName = conversation.contact?.fullName ?? t('conversations.unknownContact'); + const channel = conversation.contactInboxes[0]?.channel ?? 'omnichannel'; + const preview = lastMessagePreview(conversation, t); + const unread = isUnread(conversation); + const botActive = isBotActive(conversation.botEnabled, conversation.botResumeAt); + const botPaused = !conversation.botEnabled && !botActive; + const assigneeName = conversation.assignedUser?.name; + const relativeTimeLabel = conversation.lastActivityAt + ? new Date(conversation.lastActivityAt).toLocaleString() + : ''; + + const chips = statusChips(conversation); + + const accessibilityLabel = [ + contactName, + channel, + unread ? t('conversations.a11yUnread') : null, + preview || null, + relativeTimeLabel || null, + ] + .filter(Boolean) + .join(', '); + + const leftActions: SwipeAction[] | undefined = onToggleRead + ? [ + { + icon: unread ? 'mail-open-outline' : 'mail-unread-outline', + label: unread ? t('conversations.markRead') : t('conversations.markUnread'), + color: colors.brand, + onPress: onToggleRead, + }, + ] + : undefined; + + const rightActions: SwipeAction[] = []; + if (onToggleBot) { + rightActions.push({ + icon: botActive ? 'sparkles' : 'sparkles-outline', + label: botActive ? t('conversations.disableBot') : t('conversations.enableBot'), + color: colors.bubbleBotAccent, + onPress: onToggleBot, + }); + } + if (onArchive) { + rightActions.push({ + icon: 'archive-outline', + label: t('conversations.archive'), + color: colors.textSecondary, + destructive: true, + onPress: onArchive, + }); + } + + const accessibilityActions = [ + ...(onToggleRead + ? [ + { + name: 'toggleRead', + label: unread ? t('conversations.markRead') : t('conversations.markUnread'), + }, + ] + : []), + ...(onToggleBot + ? [ + { + name: 'toggleBot', + label: botActive ? t('conversations.disableBot') : t('conversations.enableBot'), + }, + ] + : []), + ...(onArchive ? [{ name: 'archive', label: t('conversations.archive') }] : []), + ]; + + function handleAccessibilityAction(event: { nativeEvent: { actionName: string } }) { + switch (event.nativeEvent.actionName) { + case 'toggleRead': + onToggleRead?.(); + return; + case 'toggleBot': + onToggleBot?.(); + return; + case 'archive': + onArchive?.(); + return; + } + } + + return ( + <SwipeableRow leftActions={leftActions} rightActions={rightActions}> + <PressableScale + accessibilityRole="button" + accessibilityLabel={accessibilityLabel} + accessibilityActions={accessibilityActions} + onAccessibilityAction={handleAccessibilityAction} + onPress={onPress} + onLongPress={onLongPress} + haptic={false} + style={[ + styles.row, + { + paddingVertical: spacing.ms, + paddingHorizontal: spacing.md, + gap: spacing.ms, + backgroundColor: colors.surface1, + }, + ]} + > + <View style={styles.avatarWrap}> + <Avatar + uri={conversation.contact?.avatar} + name={contactName} + size={52} + ring={botActive ? 'bot' : 'none'} + badge={<ChannelBadge channel={channel} size={14} />} + /> + </View> + + <View style={styles.body}> + <View style={styles.headerRow}> + <Text variant={unread ? 'bodyStrong' : 'body'} numberOfLines={1} style={styles.name}> + {contactName} + </Text> + {conversation.lastActivityAt ? ( + <RelativeTime date={conversation.lastActivityAt} /> + ) : null} + </View> + <View style={styles.previewRow}> + <Text + variant="callout" + color={unread ? 'primary' : 'secondary'} + numberOfLines={1} + style={[styles.preview, unread && styles.previewUnread]} + > + {preview} + </Text> + {unread ? <View style={[styles.unreadDot, { backgroundColor: colors.brand }]} /> : null} + </View> + + <View style={[styles.metaRow, { gap: spacing.xs }]}> + {botActive || botPaused ? ( + <View + style={[ + styles.metaChip, + { + backgroundColor: botPaused + ? colors.warningSoft + : withAlpha(colors.bubbleBotAccent, 0.16), + }, + ]} + > + <Text + variant="micro" + style={{ + color: botPaused ? colors.warning : colors.bubbleBotAccent, + fontWeight: '600', + }} + > + {botPaused ? t('conversations.botPaused') : t('conversations.botActive')} + </Text> + </View> + ) : null} + {assigneeName ? ( + <Text variant="micro" color="tertiary" numberOfLines={1}> + {t('conversations.assignedTo', { name: assigneeName })} + </Text> + ) : null} + </View> + + <TagChips chips={chips} /> + </View> + </PressableScale> + </SwipeableRow> + ); +}); + +const styles = StyleSheet.create({ + row: { + flexDirection: 'row', + alignItems: 'flex-start', + }, + avatarWrap: { + position: 'relative', + }, + body: { + flex: 1, + gap: 3, + }, + headerRow: { + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + gap: 8, + }, + name: { + flex: 1, + }, + previewRow: { + flexDirection: 'row', + alignItems: 'center', + gap: 6, + }, + preview: { + flex: 1, + }, + previewUnread: { + fontWeight: '600', + }, + unreadDot: { + width: 8, + height: 8, + borderRadius: 4, + }, + metaRow: { + flexDirection: 'row', + alignItems: 'center', + flexWrap: 'wrap', + }, + metaChip: { + borderRadius: 999, + paddingHorizontal: 6, + paddingVertical: 2, + }, +}); + +/** FlashList `getItemType` resolver — every row renders the same shape today, so a single + * constant type is correct; kept as a named export so the parent screen's FlashList can wire it + * without guessing at a magic string. */ +export function conversationRowItemType(): string { + return 'conversation-row'; +} diff --git a/src/features/conversations/components/filter-chips-row.test.tsx b/src/features/conversations/components/filter-chips-row.test.tsx new file mode 100644 index 0000000..532528b --- /dev/null +++ b/src/features/conversations/components/filter-chips-row.test.tsx @@ -0,0 +1,32 @@ +import { render, screen } from '@testing-library/react-native'; +import { StyleSheet } from 'react-native'; + +import { initI18n } from '@/i18n'; + +import { FilterChipsRow } from './filter-chips-row'; + +beforeAll(() => { + initI18n(); +}); + +describe('FilterChipsRow', () => { + it('renders every quick-filter chip with "All" selected by default', async () => { + await render(<FilterChipsRow />); + + for (const label of ['All', 'Unread', 'Mine', 'Bot handling', 'Following']) { + expect(screen.getByText(label)).toBeTruthy(); + } + expect(screen.getByRole('button', { name: 'All' }).props.accessibilityState).toMatchObject({ + selected: true, + }); + }); + + it('does not flex-grow, so it hugs its content instead of splitting the screen with the list', async () => { + // RN's ScrollView base style is `flexGrow: 1`; inside the Inbox flex column that made the chips + // row take half the screen (regression seen on device). + await render(<FilterChipsRow />); + + const style = StyleSheet.flatten(screen.getByTestId('filter-chips-row').props.style); + expect(style.flexGrow).toBe(0); + }); +}); diff --git a/src/features/conversations/components/filter-chips-row.tsx b/src/features/conversations/components/filter-chips-row.tsx new file mode 100644 index 0000000..c27619b --- /dev/null +++ b/src/features/conversations/components/filter-chips-row.tsx @@ -0,0 +1,115 @@ +import { useTranslation } from 'react-i18next'; +import { ScrollView, StyleSheet } from 'react-native'; + +import { Chip } from '@/components/ui/chip'; +import { useConversationFilters } from '@/features/conversations/stores/use-conversation-filters'; +import { useAuthStore } from '@/stores/use-auth-store'; +import { useTheme } from '@/theme/use-theme'; + +/** + * Quick-filter chip: All / Unread / Mine / Bot / Following. These are single-select shortcuts + * layered on top of the same store the full filter sheet reads (`use-conversation-filters.ts`) — + * there's no separate "quick filter" concept on the backend, so each chip just sets the store + * fields that already exist: + * - Unread → `status: ['unread']` (a real server-side status filter) + * - Following → `status: ['followUp']` (ditto) + * - Mine → `assignedId: currentUserId` (ditto) + * - Bot → `botCategory: 'bot'` (ditto) + * - All → clears all four quick-filter dimensions (channel/keyword from the sheet/search bar + * are left untouched — "All" only resets the facets this row itself can set). + * + * Only one quick filter is active at a time (chips are mutually exclusive here, matching the + * "quick shortcut" mental model) — picking a second chip replaces whichever one was active, + * rather than combining them. The full filter sheet still supports combining `status` values + * freely; this row is intentionally simpler. + */ +type QuickFilter = 'all' | 'unread' | 'mine' | 'bot' | 'following'; + +export function FilterChipsRow() { + const { t } = useTranslation(); + const { spacing } = useTheme(); + const currentUserId = useAuthStore((state) => state.user?.id); + + const status = useConversationFilters((state) => state.status); + const assignedId = useConversationFilters((state) => state.assignedId); + const botCategory = useConversationFilters((state) => state.botCategory); + const setStatus = useConversationFilters((state) => state.setStatus); + const setAssignedId = useConversationFilters((state) => state.setAssignedId); + const setBotCategory = useConversationFilters((state) => state.setBotCategory); + + const active: QuickFilter = (() => { + if (status?.includes('unread')) return 'unread'; + if (status?.includes('followUp')) return 'following'; + if (botCategory === 'bot') return 'bot'; + if (currentUserId && assignedId === currentUserId) return 'mine'; + return 'all'; + })(); + + function selectQuickFilter(next: QuickFilter) { + // Clear the other quick-filter dimensions first so only one is ever active. + setStatus(undefined); + setAssignedId(undefined); + setBotCategory(undefined); + + switch (next) { + case 'unread': + setStatus(['unread']); + return; + case 'following': + setStatus(['followUp']); + return; + case 'bot': + setBotCategory('bot'); + return; + case 'mine': + if (currentUserId) setAssignedId(currentUserId); + return; + case 'all': + return; + } + } + + const chips: { value: QuickFilter; label: string }[] = [ + { value: 'all', label: t('conversations.filterAll') }, + { value: 'unread', label: t('conversations.statusUnread') }, + { value: 'mine', label: t('conversations.quickFilterMine') }, + { value: 'bot', label: t('conversations.handledByBot') }, + { value: 'following', label: t('conversations.statusFollowUp') }, + ]; + + return ( + <ScrollView + testID="filter-chips-row" + horizontal + showsHorizontalScrollIndicator={false} + style={styles.scroll} + contentContainerStyle={[ + styles.row, + { paddingHorizontal: spacing.md, paddingBottom: spacing.sm, gap: spacing.xs }, + ]} + > + {chips.map((chip) => ( + <Chip + key={chip.value} + label={chip.label} + selected={active === chip.value} + tone="brand" + onPress={() => selectQuickFilter(chip.value)} + /> + ))} + </ScrollView> + ); +} + +const styles = StyleSheet.create({ + // ScrollView's base style is `flexGrow: 1`; in the Inbox flex column that made this row split the + // remaining height 50/50 with the conversation FlashList. Hug the chips' own height instead. + scroll: { + flexGrow: 0, + flexShrink: 0, + }, + row: { + flexDirection: 'row', + alignItems: 'center', + }, +}); diff --git a/src/features/conversations/components/filter-sheet.tsx b/src/features/conversations/components/filter-sheet.tsx new file mode 100644 index 0000000..716b7cd --- /dev/null +++ b/src/features/conversations/components/filter-sheet.tsx @@ -0,0 +1,219 @@ +import type BottomSheet from '@gorhom/bottom-sheet'; +import { BottomSheetScrollView } from '@gorhom/bottom-sheet'; +import type { RefObject } from 'react'; +import { Pressable, StyleSheet, View } from 'react-native'; +import { useTranslation } from 'react-i18next'; + +import { Button } from '@/components/ui/button'; +import { SegmentedTabs } from '@/components/ui/segmented-tabs'; +import { Sheet } from '@/components/ui/sheet'; +import { Text } from '@/components/ui/text'; +import type { + BotCategoryFilter, + ConversationChannelFilter, + ConversationStatusFilter, +} from '@/features/conversations/stores/use-conversation-filters'; +import { useConversationFilters } from '@/features/conversations/stores/use-conversation-filters'; +import { useTheme } from '@/theme/use-theme'; + +/** All 9 channel enum values the backend accepts for this filter (see + * `use-conversation-filters.ts`'s `ConversationChannelFilter`), plus a leading "all" — previously + * truncated to the first 4 via `.slice(0, 4)`, silently hiding zalo/telegram/smtp/tiktok/ + * omnichannel from the filter sheet. Channel/platform brand names (Messenger, Instagram, + * WhatsApp, Zalo, Telegram, TikTok) stay literal — they're proper nouns, not translatable UI + * copy. "Webchat", "Email", and "Omnichannel" are ours. Rendered as a wrapping chip row rather + * than `SegmentedTabs` — 10 options don't fit a single-row segmented control. */ +const CHANNEL_OPTIONS: { value: ConversationChannelFilter | 'all'; labelKey: string }[] = [ + { value: 'all', labelKey: 'conversations.filterAll' }, + { value: 'omnichannel', labelKey: 'conversations.channelOmnichannel' }, + { value: 'messenger', labelKey: 'conversations.channelMessenger' }, + { value: 'instagram', labelKey: 'conversations.channelInstagram' }, + { value: 'whatsapp', labelKey: 'conversations.channelWhatsapp' }, + { value: 'webchat', labelKey: 'conversations.channelWebchat' }, + { value: 'zalo', labelKey: 'conversations.channelZalo' }, + { value: 'telegram', labelKey: 'conversations.channelTelegram' }, + { value: 'smtp', labelKey: 'conversations.channelEmail' }, + { value: 'tiktok', labelKey: 'conversations.channelTiktok' }, +]; + +const STATUS_OPTIONS: { value: ConversationStatusFilter; labelKey: string }[] = [ + { value: 'unread', labelKey: 'conversations.statusUnread' }, + { value: 'noAdminReply', labelKey: 'conversations.statusNoAdminReply' }, + { value: 'followUp', labelKey: 'conversations.statusFollowUp' }, + { value: 'archived', labelKey: 'conversations.statusArchived' }, + { value: 'blocked', labelKey: 'conversations.statusBlocked' }, +]; + +const BOT_CATEGORY_OPTIONS: { value: BotCategoryFilter; labelKey: string }[] = [ + { value: 'all', labelKey: 'conversations.filterAll' }, + { value: 'bot', labelKey: 'conversations.handledByBot' }, + { value: 'human', labelKey: 'conversations.handledByHuman' }, +]; + +type BotEnabledOption = 'all' | 'on' | 'off'; + +const BOT_ENABLED_OPTIONS: { value: BotEnabledOption; labelKey: string }[] = [ + { value: 'all', labelKey: 'conversations.filterAll' }, + { value: 'on', labelKey: 'conversations.botOn' }, + { value: 'off', labelKey: 'conversations.botOff' }, +]; + +interface FilterSheetProps { + sheetRef: RefObject<BottomSheet | null>; +} + +/** Filter sheet for the conversations list — form controls map 1:1 to the top-level fields + * `listConversationsByPOSTAuthenticatedAPI` accepts (see use-conversation-filters.ts). The + * advanced `contactFilter` condition-builder from that schema is not exposed here — it's a + * large audience-builder DSL that belongs to a dedicated "advanced search" surface, not this + * quick filter sheet (mirrors the legacy app's AdvancedSearchModal vs QuickFilter split). */ +export function FilterSheet({ sheetRef }: FilterSheetProps) { + const { t } = useTranslation(); + const { spacing } = useTheme(); + const channel = useConversationFilters((state) => state.channel); + const setChannel = useConversationFilters((state) => state.setChannel); + const status = useConversationFilters((state) => state.status); + const setStatus = useConversationFilters((state) => state.setStatus); + const botCategory = useConversationFilters((state) => state.botCategory); + const setBotCategory = useConversationFilters((state) => state.setBotCategory); + const botEnabled = useConversationFilters((state) => state.botEnabled); + const setBotEnabled = useConversationFilters((state) => state.setBotEnabled); + const reset = useConversationFilters((state) => state.reset); + + function toggleStatus(value: ConversationStatusFilter) { + const current = status ?? []; + const next = current.includes(value) ? current.filter((s) => s !== value) : [...current, value]; + setStatus(next.length > 0 ? next : undefined); + } + + const botEnabledValue: BotEnabledOption = + botEnabled === true ? 'on' : botEnabled === false ? 'off' : 'all'; + + function handleBotEnabledChange(value: BotEnabledOption) { + setBotEnabled(value === 'all' ? undefined : value === 'on'); + } + + return ( + <Sheet ref={sheetRef} snapPoints={['60%', '90%']}> + <BottomSheetScrollView contentContainerStyle={{ padding: spacing.md, gap: spacing.md }}> + <Text variant="title">{t('conversations.filters')}</Text> + + <View style={styles.section}> + <Text variant="caption" color="secondary"> + {t('conversations.channel')} + </Text> + <View style={styles.chipRow}> + {CHANNEL_OPTIONS.map((option) => { + const active = (channel ?? 'all') === option.value; + return ( + <StatusChip + key={option.value} + label={t(option.labelKey)} + active={active} + onPress={() => setChannel(option.value === 'all' ? undefined : option.value)} + /> + ); + })} + </View> + </View> + + <View style={styles.section}> + <Text variant="caption" color="secondary"> + {t('conversations.handledBy')} + </Text> + <SegmentedTabs + options={BOT_CATEGORY_OPTIONS.map((option) => ({ + value: option.value, + label: t(option.labelKey), + }))} + value={botCategory ?? 'all'} + onChange={(value) => setBotCategory(value === 'all' ? undefined : value)} + /> + </View> + + <View style={styles.section}> + <Text variant="caption" color="secondary"> + {t('conversations.botOnOffLabel')} + </Text> + <SegmentedTabs + options={BOT_ENABLED_OPTIONS.map((option) => ({ + value: option.value, + label: t(option.labelKey), + }))} + value={botEnabledValue} + onChange={handleBotEnabledChange} + /> + </View> + + <View style={styles.section}> + <Text variant="caption" color="secondary"> + {t('conversations.status')} + </Text> + <View style={styles.chipRow}> + {STATUS_OPTIONS.map((option) => { + const active = (status ?? []).includes(option.value); + return ( + <StatusChip + key={option.value} + label={t(option.labelKey)} + active={active} + onPress={() => toggleStatus(option.value)} + /> + ); + })} + </View> + </View> + + <Button label={t('conversations.clearFilters')} variant="secondary" onPress={reset} /> + <Button + label={t('common.done')} + variant="primary" + onPress={() => sheetRef.current?.close()} + /> + </BottomSheetScrollView> + </Sheet> + ); +} + +function StatusChip({ + label, + active, + onPress, +}: { + label: string; + active: boolean; + onPress: () => void; +}) { + const { colors, radius, spacing } = useTheme(); + return ( + <Pressable + onPress={onPress} + style={[ + styles.statusChip, + { + backgroundColor: active ? colors.primary : colors.surface, + borderRadius: radius.full, + paddingHorizontal: spacing.ms, + }, + ]} + > + <Text variant="caption" color={active ? 'inverse' : 'primary'}> + {label} + </Text> + </Pressable> + ); +} + +const styles = StyleSheet.create({ + section: { + gap: 8, + }, + chipRow: { + flexDirection: 'row', + flexWrap: 'wrap', + gap: 8, + }, + statusChip: { + paddingVertical: 6, + }, +}); diff --git a/src/features/conversations/components/tag-chips.tsx b/src/features/conversations/components/tag-chips.tsx new file mode 100644 index 0000000..c2b7185 --- /dev/null +++ b/src/features/conversations/components/tag-chips.tsx @@ -0,0 +1,64 @@ +import { useTranslation } from 'react-i18next'; +import { StyleSheet, View } from 'react-native'; + +import { Badge } from '@/components/ui/badge'; +import { useTheme } from '@/theme/use-theme'; + +/** + * The conversation-list response (schema.ts) has no dedicated free-form `tags` array on the + * conversation item itself — the `status`/`tags` request filters share one enum + * (noAdminReply|unread|followUp|archived|blocked). This component renders derived status chips + * from that same vocabulary (computed by the caller from the conversation's own fields — e.g. + * `followed`, `archivedAt`, `botEnabled` — see conversation-row.tsx), not a separate tags API. + */ +export type ConversationStatusChip = + 'noAdminReply' | 'unread' | 'followUp' | 'archived' | 'blocked'; + +const CHIP_LABEL_KEYS: Record<ConversationStatusChip, string> = { + noAdminReply: 'conversations.statusNoAdminReply', + unread: 'conversations.statusUnread', + followUp: 'conversations.statusFollowUp', + archived: 'conversations.statusArchived', + blocked: 'conversations.statusBlocked', +}; + +interface TagChipsProps { + chips: ConversationStatusChip[]; +} + +export function TagChips({ chips }: TagChipsProps) { + const { t } = useTranslation(); + const { colors } = useTheme(); + + if (chips.length === 0) { + return null; + } + + return ( + <View style={styles.row}> + {chips.map((chip) => ( + <Badge + key={chip} + color={ + chip === 'blocked' ? colors.danger : chip === 'unread' ? colors.primary : colors.surface + } + textColor={ + chip === 'blocked' || chip === 'unread' + ? colors.primaryForeground + : colors.textSecondary + } + > + {t(CHIP_LABEL_KEYS[chip])} + </Badge> + ))} + </View> + ); +} + +const styles = StyleSheet.create({ + row: { + flexDirection: 'row', + gap: 4, + flexWrap: 'wrap', + }, +}); diff --git a/src/features/conversations/lib/conversation-status.test.ts b/src/features/conversations/lib/conversation-status.test.ts new file mode 100644 index 0000000..5bbdfbc --- /dev/null +++ b/src/features/conversations/lib/conversation-status.test.ts @@ -0,0 +1,63 @@ +import { isBotActive, isUnread } from './conversation-status'; + +describe('isBotActive', () => { + it('returns true when the bot is enabled, regardless of botResumeAt', () => { + expect(isBotActive(true, null)).toBe(true); + expect(isBotActive(true, new Date(Date.now() + 60_000).toISOString())).toBe(true); + }); + + it('returns false (paused) when disabled and botResumeAt is in the future', () => { + const future = new Date(Date.now() + 60_000).toISOString(); + expect(isBotActive(false, future)).toBe(false); + }); + + it('returns true (auto-resumed) when disabled and botResumeAt is in the past', () => { + const past = new Date(Date.now() - 60_000).toISOString(); + expect(isBotActive(false, past)).toBe(true); + }); + + it('returns false when disabled with no botResumeAt', () => { + expect(isBotActive(false, null)).toBe(false); + }); + + it('respects an explicit `now` for deterministic testing', () => { + const resumeAt = new Date('2026-01-01T00:00:00.000Z').toISOString(); + const beforeResume = new Date('2025-12-31T00:00:00.000Z').getTime(); + const afterResume = new Date('2026-01-02T00:00:00.000Z').getTime(); + expect(isBotActive(false, resumeAt, beforeResume)).toBe(false); + expect(isBotActive(false, resumeAt, afterResume)).toBe(true); + }); +}); + +describe('isUnread', () => { + it('returns true when lastActivityAt is newer than agentLastReadAt', () => { + expect( + isUnread({ + lastActivityAt: '2026-01-02T00:00:00.000Z', + agentLastReadAt: '2026-01-01T00:00:00.000Z', + }), + ).toBe(true); + }); + + it('returns false when agentLastReadAt is newer than or equal to lastActivityAt', () => { + expect( + isUnread({ + lastActivityAt: '2026-01-01T00:00:00.000Z', + agentLastReadAt: '2026-01-02T00:00:00.000Z', + }), + ).toBe(false); + }); + + it('returns true when there is activity but agentLastReadAt is null', () => { + expect(isUnread({ lastActivityAt: '2026-01-01T00:00:00.000Z', agentLastReadAt: null })).toBe( + true, + ); + }); + + it('returns false when there is no lastActivityAt at all', () => { + expect(isUnread({ lastActivityAt: null, agentLastReadAt: null })).toBe(false); + expect(isUnread({ lastActivityAt: null, agentLastReadAt: '2026-01-01T00:00:00.000Z' })).toBe( + false, + ); + }); +}); diff --git a/src/features/conversations/lib/conversation-status.ts b/src/features/conversations/lib/conversation-status.ts new file mode 100644 index 0000000..bb7aedf --- /dev/null +++ b/src/features/conversations/lib/conversation-status.ts @@ -0,0 +1,32 @@ +import type { ConversationListItem } from '@/features/conversations/api/use-conversations-infinite'; + +/** + * Bot-active derivation, matching the backend's own rule (see plan Context: `isBotActive = + * botEnabled || botResumeAt < now`): the bot is considered "active" either because it's + * explicitly enabled, or because a previous 24h disable-pause (`botResumeAt`) has already + * elapsed — the backend auto-resumes the bot at that point even though `botEnabled` itself only + * flips back via the next `enable-bot` call or scheduled job. A `botResumeAt` in the future means + * the bot is still paused. + */ +export function isBotActive( + botEnabled: boolean, + botResumeAt: string | null, + now: number = Date.now(), +): boolean { + if (botEnabled) return true; + if (!botResumeAt) return false; + return new Date(botResumeAt).getTime() < now; +} + +/** Unread = the contact's last incoming activity is newer than the agent's last read timestamp. + * There's no direct `unreadCount` field on the schema — this derives a boolean from the two + * timestamps the response does carry (`lastActivityAt`, `agentLastReadAt`). Extracted verbatim + * from the previous inline `isUnread` in conversation-row.tsx — same semantics, same edge cases + * (no activity yet → read; activity but never read → unread). */ +export function isUnread( + conversation: Pick<ConversationListItem, 'lastActivityAt' | 'agentLastReadAt'>, +): boolean { + if (!conversation.lastActivityAt) return false; + if (!conversation.agentLastReadAt) return true; + return new Date(conversation.lastActivityAt) > new Date(conversation.agentLastReadAt); +} diff --git a/src/features/conversations/lib/find-conversation-in-cache.test.ts b/src/features/conversations/lib/find-conversation-in-cache.test.ts new file mode 100644 index 0000000..98a7dca --- /dev/null +++ b/src/features/conversations/lib/find-conversation-in-cache.test.ts @@ -0,0 +1,113 @@ +import { QueryClient } from '@tanstack/react-query'; + +import type { + ConversationListItem, + ListConversationsResponse, +} from '@/features/conversations/api/use-conversations-infinite'; + +import { findConversationInListCache } from './find-conversation-in-cache'; + +function fakeConversation(id: string): ConversationListItem { + return { + id, + createdAt: '2026-01-01T00:00:00.000Z', + updatedAt: '2026-01-01T00:00:00.000Z', + botEnabled: true, + botResumeAt: null, + archivedAt: null, + additionalAttributes: null, + contactLastReadAt: null, + agentLastReadAt: null, + lastActivityAt: '2026-01-01T00:00:00.000Z', + followed: false, + workspaceId: 'ws-1', + contactId: 'contact-1', + sourceId: null, + lastStep: null, + currentStep: null, + adminRepliedAt: null, + contactRepliedAt: null, + contactInboxes: [], + messages: [], + contact: null, + assignedUser: null, + assignedInboxTeam: null, + } as unknown as ConversationListItem; +} + +function fakePage(items: ConversationListItem[]): ListConversationsResponse { + return { data: items, nextCursor: null, prevCursor: null }; +} + +const activeQueryClients: QueryClient[] = []; + +function createTestQueryClient(): QueryClient { + const queryClient = new QueryClient(); + activeQueryClients.push(queryClient); + return queryClient; +} + +afterEach(() => { + while (activeQueryClients.length > 0) { + const queryClient = activeQueryClients.pop()!; + queryClient.clear(); + queryClient.unmount(); + } +}); + +describe('findConversationInListCache', () => { + it('finds a conversation cached in a list query page for the given workspace', () => { + const queryClient = createTestQueryClient(); + const target = fakeConversation('conv-1'); + queryClient.setQueryData(['ws', 'ws-1', 'conversations', 'list', { keyword: '' }], { + pages: [fakePage([fakeConversation('conv-0'), target])], + pageParams: [undefined], + }); + + const found = findConversationInListCache(queryClient, 'ws-1', 'conv-1'); + + expect(found?.id).toBe('conv-1'); + }); + + it('searches across every cached list query (different filter combinations)', () => { + const queryClient = createTestQueryClient(); + const target = fakeConversation('conv-2'); + queryClient.setQueryData(['ws', 'ws-1', 'conversations', 'list', { keyword: '' }], { + pages: [fakePage([fakeConversation('conv-0')])], + pageParams: [undefined], + }); + queryClient.setQueryData(['ws', 'ws-1', 'conversations', 'list', { status: ['unread'] }], { + pages: [fakePage([target])], + pageParams: [undefined], + }); + + const found = findConversationInListCache(queryClient, 'ws-1', 'conv-2'); + + expect(found?.id).toBe('conv-2'); + }); + + it('returns undefined when the conversation is not cached', () => { + const queryClient = createTestQueryClient(); + queryClient.setQueryData(['ws', 'ws-1', 'conversations', 'list', {}], { + pages: [fakePage([fakeConversation('conv-0')])], + pageParams: [undefined], + }); + + expect(findConversationInListCache(queryClient, 'ws-1', 'missing')).toBeUndefined(); + }); + + it('does not match conversations cached under a different workspace', () => { + const queryClient = createTestQueryClient(); + queryClient.setQueryData(['ws', 'ws-other', 'conversations', 'list', {}], { + pages: [fakePage([fakeConversation('conv-1')])], + pageParams: [undefined], + }); + + expect(findConversationInListCache(queryClient, 'ws-1', 'conv-1')).toBeUndefined(); + }); + + it('returns undefined when no list queries are cached at all', () => { + const queryClient = createTestQueryClient(); + expect(findConversationInListCache(queryClient, 'ws-1', 'conv-1')).toBeUndefined(); + }); +}); diff --git a/src/features/conversations/lib/find-conversation-in-cache.ts b/src/features/conversations/lib/find-conversation-in-cache.ts new file mode 100644 index 0000000..f552f29 --- /dev/null +++ b/src/features/conversations/lib/find-conversation-in-cache.ts @@ -0,0 +1,37 @@ +import type { QueryClient } from '@tanstack/react-query'; + +import type { + ConversationListItem, + ListConversationsResponse, +} from '@/features/conversations/api/use-conversations-infinite'; + +/** + * Scans every cached conversations-list infinite query (any filter combination) for a row + * matching `conversationId`. Extracted from the chat screen's previous inline cache scan so it can + * be reused as `placeholderData` for a conversation-detail query without duplicating the + * predicate/flatMap logic — both `[conversationId]/index.tsx` (mark-read guard) and + * `chat-header.tsx` (identity placeholder) call this directly today. + * + * Conversation header/preview data in this app generally comes from whichever list page the user + * navigated from, rather than a dedicated detail fetch — this is the reusable form of that lookup. + */ +export function findConversationInListCache( + queryClient: QueryClient, + workspaceId: string, + conversationId: string, +): ConversationListItem | undefined { + return queryClient + .getQueryCache() + .findAll({ + predicate: (query) => + query.queryKey[0] === 'ws' && + query.queryKey[1] === workspaceId && + query.queryKey[2] === 'conversations' && + query.queryKey[3] === 'list', + }) + .flatMap((query) => { + const data = query.state.data as { pages: ListConversationsResponse[] } | undefined; + return data?.pages.flatMap((page) => page.data) ?? []; + }) + .find((item) => item.id === conversationId); +} diff --git a/src/features/conversations/stores/use-conversation-filters.ts b/src/features/conversations/stores/use-conversation-filters.ts new file mode 100644 index 0000000..9f273ec --- /dev/null +++ b/src/features/conversations/stores/use-conversation-filters.ts @@ -0,0 +1,85 @@ +import AsyncStorage from '@react-native-async-storage/async-storage'; +import { create } from 'zustand'; +import { createJSONStorage, persist } from 'zustand/middleware'; + +/** Channel enum values from `conversationsAPI.listConversationsByPOSTAuthenticatedAPI` (generated + * schema.ts) — the single-channel filter field, not the per-conversation contactInbox channel. */ +export type ConversationChannelFilter = + | 'omnichannel' + | 'webchat' + | 'messenger' + | 'whatsapp' + | 'zalo' + | 'smtp' + | 'telegram' + | 'instagram' + | 'tiktok'; + +/** Status enum values — reused by the schema for both `status` and `tags` request fields. */ +export type ConversationStatusFilter = + 'noAdminReply' | 'unread' | 'followUp' | 'archived' | 'blocked'; + +export type BotCategoryFilter = 'bot' | 'human' | 'all'; + +/** Persisted conversation-list filters, matching exactly the top-level fields + * `listConversationsByPOSTAuthenticatedAPI` accepts (excluding `contactFilter`, `sort`, `cursor`, + * `perPage` — those are pagination/advanced-audience concerns, not user-facing filters here). */ +interface ConversationFiltersState { + botCategory: BotCategoryFilter | undefined; + assignedId: string | null | undefined; + channel: ConversationChannelFilter | undefined; + status: ConversationStatusFilter[] | undefined; + keyword: string; + botEnabled: boolean | null | undefined; + setBotCategory: (value: BotCategoryFilter | undefined) => void; + setAssignedId: (value: string | null | undefined) => void; + setChannel: (value: ConversationChannelFilter | undefined) => void; + setStatus: (value: ConversationStatusFilter[] | undefined) => void; + setKeyword: (value: string) => void; + setBotEnabled: (value: boolean | null | undefined) => void; + reset: () => void; +} + +const defaultFilters = { + botCategory: undefined, + assignedId: undefined, + channel: undefined, + status: undefined, + keyword: '', + botEnabled: undefined, +} as const; + +export const useConversationFilters = create<ConversationFiltersState>()( + persist( + (set) => ({ + ...defaultFilters, + setBotCategory: (botCategory) => set({ botCategory }), + setAssignedId: (assignedId) => set({ assignedId }), + setChannel: (channel) => set({ channel }), + setStatus: (status) => set({ status }), + setKeyword: (keyword) => set({ keyword }), + setBotEnabled: (botEnabled) => set({ botEnabled }), + reset: () => set(defaultFilters), + }), + { + name: 'conversation-filters-store', + storage: createJSONStorage(() => AsyncStorage), + }, + ), +); + +/** Plain-object snapshot of the active filters, safe to embed in a query key (persist's store API + * carries functions too, which don't belong in a key). Returns a new object every call, so any + * caller passing this straight to `useConversationFilters(...)` MUST wrap it in `useShallow` from + * `zustand/react/shallow` — zustand v5's useSyncExternalStore has no shallow-equality fallback + * (v4 did), so an unwrapped call re-renders on every render and trips React's infinite-loop guard. */ +export function conversationFiltersSnapshot(state: ConversationFiltersState) { + return { + botCategory: state.botCategory, + assignedId: state.assignedId, + channel: state.channel, + status: state.status, + keyword: state.keyword, + botEnabled: state.botEnabled, + }; +} diff --git a/src/features/flows/api/use-flows.ts b/src/features/flows/api/use-flows.ts new file mode 100644 index 0000000..22375fa --- /dev/null +++ b/src/features/flows/api/use-flows.ts @@ -0,0 +1,33 @@ +import { useQuery } from '@tanstack/react-query'; + +import { apiClient } from '@/api/client'; +import { ApiError } from '@/api/errors'; +import { queryKeys } from '@/api/query-keys'; +import type { operations } from '@/api/generated/schema'; + +/** + * `flowsAPI.privateListFlowsAPI` (`GET /workspaces/{workspaceId}/flows`) is the session-authenticated + * flows list (the `WorkspaceTokenAPI` sibling requires a workspace token, not a bearer session). + * Offset-paginated (`page`/`perPage`/`pageCount`), like contacts and members — NOT the cursor shape. + * Flow lists are typically small, so this fetches a single large page rather than driving + * useInfiniteQuery — revisit if a workspace has enough flows to need real pagination. + */ +type ListFlowsOperation = operations['flowsAPI.privateListFlowsAPI']; +export type ListFlowsResponse = ListFlowsOperation['responses'][200]['content']['application/json']; +export type FlowListItem = ListFlowsResponse['data'][number]; + +const PER_PAGE = 100; + +export function useFlows(workspaceId: string | null) { + return useQuery({ + queryKey: queryKeys.ws.flows.list(workspaceId ?? ''), + enabled: workspaceId !== null, + queryFn: async (): Promise<FlowListItem[]> => { + const { data, error } = await apiClient.GET('/workspaces/{workspaceId}/flows', { + params: { path: { workspaceId: workspaceId! }, query: { page: 1, perPage: PER_PAGE } }, + }); + if (error) throw new ApiError(error); + return data.data; + }, + }); +} diff --git a/src/features/permissions/use-permissions.test.tsx b/src/features/permissions/use-permissions.test.tsx new file mode 100644 index 0000000..c4ddd2a --- /dev/null +++ b/src/features/permissions/use-permissions.test.tsx @@ -0,0 +1,123 @@ +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { renderHook, waitFor } from '@testing-library/react-native'; +import type { PropsWithChildren } from 'react'; + +import { apiClient } from '@/api/client'; +import { usePermissions } from '@/features/permissions/use-permissions'; +import { useAuthStore } from '@/stores/use-auth-store'; + +jest.mock('@/api/client', () => ({ + apiClient: { + GET: jest.fn(), + }, +})); + +const mockedGet = apiClient.GET as jest.Mock; + +const activeQueryClients: QueryClient[] = []; + +function createTestQueryClient(): QueryClient { + const queryClient = new QueryClient(); + activeQueryClients.push(queryClient); + return queryClient; +} + +function renderPermissions(queryClient: QueryClient, workspaceId: string | null) { + function wrapper({ children }: PropsWithChildren) { + return <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>; + } + return renderHook(() => usePermissions(workspaceId), { wrapper }); +} + +function member(overrides: Partial<Record<string, unknown>> = {}) { + return { + id: 'member-1', + userId: 'user-1', + permissions: { + superAdmin: false, + analytics: true, + flows: true, + contacts: true, + onlyAssignedContacts: false, + emailAndPhone: true, + broadcast: true, + ecommerce: true, + }, + ...overrides, + }; +} + +describe('usePermissions', () => { + beforeEach(() => { + mockedGet.mockReset(); + useAuthStore.setState({ + status: 'signed-in', + user: { id: 'user-1', email: 'a@b.com', name: 'A', mustChangePassword: false }, + }); + }); + + afterEach(() => { + while (activeQueryClients.length > 0) { + const queryClient = activeQueryClients.pop()!; + queryClient.clear(); + queryClient.unmount(); + } + }); + + it('fails closed on PII-masking bits while the members query is loading', async () => { + // Never resolves during this assertion window — simulates the loading state. + mockedGet.mockReturnValue(new Promise(() => {})); + const queryClient = createTestQueryClient(); + + const { result } = await renderPermissions(queryClient, 'ws-1'); + + expect(result.current.isLoading).toBe(true); + expect(result.current.canSeeEmailAndPhone).toBe(false); + expect(result.current.onlyAssignedContacts).toBe(true); + }); + + it('fails closed on PII-masking bits when the member row is not found', async () => { + mockedGet.mockResolvedValue({ data: { data: [member({ userId: 'someone-else' })] } }); + const queryClient = createTestQueryClient(); + + const { result } = await renderPermissions(queryClient, 'ws-1'); + + await waitFor(() => expect(result.current.isLoading).toBe(false)); + expect(result.current.canSeeEmailAndPhone).toBe(false); + expect(result.current.onlyAssignedContacts).toBe(true); + }); + + it('resolves the real permission bits once the member row loads', async () => { + mockedGet.mockResolvedValue({ + data: { data: [member({ permissions: { ...member().permissions, emailAndPhone: false } })] }, + }); + const queryClient = createTestQueryClient(); + + const { result } = await renderPermissions(queryClient, 'ws-1'); + + await waitFor(() => expect(result.current.isLoading).toBe(false)); + expect(result.current.canSeeEmailAndPhone).toBe(false); + expect(result.current.onlyAssignedContacts).toBe(false); + }); + + it('super admins bypass every restriction once loaded', async () => { + mockedGet.mockResolvedValue({ + data: { data: [member({ permissions: { ...member().permissions, superAdmin: true } })] }, + }); + const queryClient = createTestQueryClient(); + + const { result } = await renderPermissions(queryClient, 'ws-1'); + + await waitFor(() => expect(result.current.isLoading).toBe(false)); + expect(result.current.isSuperAdmin).toBe(true); + expect(result.current.canSeeEmailAndPhone).toBe(true); + }); + + it('fails closed while workspaceId is null', async () => { + const queryClient = createTestQueryClient(); + const { result } = await renderPermissions(queryClient, null); + + expect(result.current.isLoading).toBe(true); + expect(result.current.canSeeEmailAndPhone).toBe(false); + }); +}); diff --git a/src/features/permissions/use-permissions.ts b/src/features/permissions/use-permissions.ts new file mode 100644 index 0000000..99ab498 --- /dev/null +++ b/src/features/permissions/use-permissions.ts @@ -0,0 +1,108 @@ +import { useQuery } from '@tanstack/react-query'; + +import { apiClient } from '@/api/client'; +import { ApiError } from '@/api/errors'; +import { queryKeys } from '@/api/query-keys'; +import { useAuthStore } from '@/stores/use-auth-store'; + +/** + * `GET /workspaces/{workspaceId}/members` (`workspaceMembersAPI.listWorkspaceMembersAuthenticatedAPI` + * in src/api/generated/schema.ts) embeds a `permissions` object on every member row with EXACTLY + * these field names — no mapping needed against the plan's shorthand: + * `{ superAdmin, analytics, flows, contacts, onlyAssignedContacts, emailAndPhone, broadcast, ecommerce }`. + * This hook finds the current signed-in user's own member row (matched by `userId` against the + * auth store) and exposes its permission bits. Shares the same members-list query the Phase 3 + * assignment-sheet already fetches (`queryKeys.ws.members.list`), so no extra request in practice. + */ +export interface WorkspacePermissions { + isSuperAdmin: boolean; + onlyAssignedContacts: boolean; + canSeeEmailAndPhone: boolean; + canSeeAnalytics: boolean; + canSeeFlows: boolean; + canSeeContacts: boolean; + canSeeBroadcast: boolean; + canSeeEcommerce: boolean; + /** True while the underlying members-list query hasn't resolved yet (or the current user's own + * member row hasn't been found in it). Callers that gate a query's `enabled` on permissions + * being settled (e.g. `useConversationsInfinite`'s `onlyAssignedContacts` enforcement) should + * wait on this rather than firing against the fail-closed defaults below. */ + isLoading: boolean; +} + +/** Super admins bypass every restriction, mirroring the backend's own authorization checks. */ +const DEFAULT_PERMISSIONS: Omit<WorkspacePermissions, 'isLoading'> = { + isSuperAdmin: false, + onlyAssignedContacts: false, + canSeeEmailAndPhone: true, + canSeeAnalytics: true, + canSeeFlows: true, + canSeeContacts: true, + canSeeBroadcast: true, + canSeeEcommerce: true, +}; + +/** + * PII-masking-relevant defaults used ONLY while the member row is loading or can't be found. + * Unlike `DEFAULT_PERMISSIONS` (which stays open so the app doesn't lock up other UI on a fetch + * hiccup), `canSeeEmailAndPhone` fails CLOSED here — masking is the one bit where "briefly show + * unmasked PII on every mount" is an actual data-exposure bug (see contact-row.tsx / info-tab.tsx + * consumers), not just a UX inconvenience. `onlyAssignedContacts` also fails closed (scoped to + * "assigned to me") for the same reason: a restricted member's conversation list must never + * briefly show everyone else's conversations while permissions are still loading. + */ +const LOADING_PERMISSIONS: Omit<WorkspacePermissions, 'isLoading'> = { + ...DEFAULT_PERMISSIONS, + onlyAssignedContacts: true, + canSeeEmailAndPhone: false, +}; + +export function useWorkspaceMembersList(workspaceId: string) { + return useQuery({ + queryKey: queryKeys.ws.members.list(workspaceId), + queryFn: async () => { + const { data, error } = await apiClient.GET('/workspaces/{workspaceId}/members', { + params: { path: { workspaceId } }, + }); + if (error) throw new ApiError(error); + return data.data; + }, + }); +} + +/** Returns the signed-in user's own permission bits for `workspaceId`. While the member row is + * loading or can't be found, PII-masking-relevant bits (`canSeeEmailAndPhone`, + * `onlyAssignedContacts`) fail CLOSED (see LOADING_PERMISSIONS) so a fetch hiccup can't briefly + * expose unmasked contact data or another agent's conversations; other bits stay open by default + * so unrelated UI (flows/analytics/etc. visibility) doesn't lock up on every mount. The backend + * still enforces real authorization server-side regardless of this UI gate. */ +export function usePermissions(workspaceId: string | null): WorkspacePermissions { + const currentUserId = useAuthStore((state) => state.user?.id); + const { data: members, isLoading: isMembersLoading } = useWorkspaceMembersList(workspaceId ?? ''); + + if (!workspaceId || !currentUserId || isMembersLoading || !members) { + return { ...LOADING_PERMISSIONS, isLoading: true }; + } + + const ownMember = members.find((member) => member.userId === currentUserId); + if (!ownMember) { + return { ...LOADING_PERMISSIONS, isLoading: false }; + } + + const { permissions } = ownMember; + if (permissions.superAdmin) { + return { ...DEFAULT_PERMISSIONS, isSuperAdmin: true, isLoading: false }; + } + + return { + isSuperAdmin: false, + onlyAssignedContacts: permissions.onlyAssignedContacts, + canSeeEmailAndPhone: permissions.emailAndPhone, + canSeeAnalytics: permissions.analytics, + canSeeFlows: permissions.flows, + canSeeContacts: permissions.contacts, + canSeeBroadcast: permissions.broadcast, + canSeeEcommerce: permissions.ecommerce, + isLoading: false, + }; +} diff --git a/src/features/sequences/api/use-enroll-contact-in-sequences.test.tsx b/src/features/sequences/api/use-enroll-contact-in-sequences.test.tsx new file mode 100644 index 0000000..758dc87 --- /dev/null +++ b/src/features/sequences/api/use-enroll-contact-in-sequences.test.tsx @@ -0,0 +1,43 @@ +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { renderHook, waitFor } from '@testing-library/react-native'; +import { act } from 'react'; +import type { PropsWithChildren } from 'react'; + +import { useEnrollContactInSequences } from './use-enroll-contact-in-sequences'; + +// `FEATURES.sendSequence` is hardcoded `false` in src/config/features.ts — this hook must refuse +// to execute (and never reach `fetch`) while that's the case, per its own doc comment. +const originalFetch = global.fetch; + +afterEach(() => { + global.fetch = originalFetch; +}); + +function wrapper(queryClient: QueryClient) { + return function Wrapper({ children }: PropsWithChildren) { + return <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>; + }; +} + +describe('useEnrollContactInSequences', () => { + it('throws before any network call while FEATURES.sendSequence is off', async () => { + const fetchSpy = jest.fn(); + global.fetch = fetchSpy as unknown as typeof fetch; + const queryClient = new QueryClient(); + + const { result } = await renderHook(() => useEnrollContactInSequences('ws-1', 'contact-1'), { + wrapper: wrapper(queryClient), + }); + + act(() => { + result.current.mutate(['seq-1']); + }); + + await waitFor(() => expect(result.current.isError).toBe(true)); + expect(fetchSpy).not.toHaveBeenCalled(); + expect(result.current.error).toBeInstanceOf(Error); + + queryClient.clear(); + queryClient.unmount(); + }); +}); diff --git a/src/features/sequences/api/use-enroll-contact-in-sequences.ts b/src/features/sequences/api/use-enroll-contact-in-sequences.ts new file mode 100644 index 0000000..2d494f1 --- /dev/null +++ b/src/features/sequences/api/use-enroll-contact-in-sequences.ts @@ -0,0 +1,80 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query'; + +import { getAuthToken } from '@/api/auth-token'; +import { queryKeys } from '@/api/query-keys'; +import { env } from '@/config/env'; +import { FEATURES } from '@/config/features'; + +/** + * Hand-rolled fetch — no generated-client route exists yet for this endpoint (backend needs to + * add `POST /workspaces/{workspaceId}/contacts/sequences`, bearer-auth, mirroring the existing + * `POST /contacts/tags` convention). Replace with the generated client + delete this file once + * shipped and `pnpm generate:api` has been re-run. + * + * Request shape mirrors the web app's Next.js server action body exactly: `{ ids, sequences }`, + * where `ids` are contact ids and `sequences` are sequence ids — matching the + * `POST /workspaces/{workspaceId}/contacts/tags` convention's `{ ids, tags }` shape. + */ +interface EnrollContactsInSequencesRequest { + ids: string[]; + sequences: string[]; +} + +class EnrollSequencesError extends Error {} + +async function postEnrollContactsInSequences( + workspaceId: string, + body: EnrollContactsInSequencesRequest, +): Promise<void> { + const token = await getAuthToken(); + const response = await fetch( + `${env.apiBaseUrl}/api/workspaces/${workspaceId}/contacts/sequences`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + ...(token ? { Authorization: `Bearer ${token}` } : {}), + }, + body: JSON.stringify(body), + }, + ); + + if (!response.ok) { + let message = response.statusText; + try { + const errorBody = (await response.json()) as { message?: string }; + message = errorBody.message ?? message; + } catch { + // Non-JSON error body — fall back to statusText. + } + throw new EnrollSequencesError(message); + } +} + +/** + * Enrolls a single contact into one or more sequences. Safely importable/callable even while + * `FEATURES.sendSequence` is off — `mutationFn` throws immediately, before any network call, so + * nothing reaches the (nonexistent) backend route by accident. + */ +export function useEnrollContactInSequences(workspaceId: string, contactId: string) { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: async (sequenceIds: string[]) => { + if (!FEATURES.sendSequence) { + throw new EnrollSequencesError( + 'Send sequence is not yet available — the backend enroll endpoint has not shipped.', + ); + } + await postEnrollContactsInSequences(workspaceId, { + ids: [contactId], + sequences: sequenceIds, + }); + }, + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: queryKeys.ws.contacts.detail(workspaceId, contactId), + }); + }, + }); +} diff --git a/src/features/sequences/api/use-sequences.ts b/src/features/sequences/api/use-sequences.ts new file mode 100644 index 0000000..0f093ca --- /dev/null +++ b/src/features/sequences/api/use-sequences.ts @@ -0,0 +1,37 @@ +import { useQuery } from '@tanstack/react-query'; + +import { apiClient } from '@/api/client'; +import { ApiError } from '@/api/errors'; +import { queryKeys } from '@/api/query-keys'; +import type { operations } from '@/api/generated/schema'; + +/** + * `sequencesAPI.listSequencesWorkspaceAuthAPI` (`GET /workspaces/{workspaceId}/sequences`) is the + * session-authenticated sequences list (the `WorkspaceTokenAPI` sibling requires a workspace + * token, not a bearer session). Offset-paginated (`page`/`perPage`/`pageCount`), like contacts and + * flows — not the cursor shape. Filters to `active: true` server-side so the picker only ever + * shows sequences that can currently be enrolled into. + */ +type ListSequencesOperation = operations['sequencesAPI.listSequencesWorkspaceAuthAPI']; +export type ListSequencesResponse = + ListSequencesOperation['responses'][200]['content']['application/json']; +export type SequenceListItem = ListSequencesResponse['data'][number]; + +const PER_PAGE = 100; + +export function useSequences(workspaceId: string | null) { + return useQuery({ + queryKey: queryKeys.ws.sequences.list(workspaceId ?? ''), + enabled: workspaceId !== null, + queryFn: async (): Promise<SequenceListItem[]> => { + const { data, error } = await apiClient.GET('/workspaces/{workspaceId}/sequences', { + params: { + path: { workspaceId: workspaceId! }, + query: { page: 1, perPage: PER_PAGE, active: true }, + }, + }); + if (error) throw new ApiError(error); + return data.data; + }, + }); +} diff --git a/src/features/sequences/components/sequence-picker-sheet.tsx b/src/features/sequences/components/sequence-picker-sheet.tsx new file mode 100644 index 0000000..3122f35 --- /dev/null +++ b/src/features/sequences/components/sequence-picker-sheet.tsx @@ -0,0 +1,134 @@ +import type BottomSheet from '@gorhom/bottom-sheet'; +import { BottomSheetFlatList } from '@gorhom/bottom-sheet'; +import type { RefObject } from 'react'; +import { useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { StyleSheet, View } from 'react-native'; + +import { Badge } from '@/components/ui/badge'; +import { Button } from '@/components/ui/button'; +import { EmptyState } from '@/components/ui/empty-state'; +import { Icon } from '@/components/ui/icon'; +import { ListItem } from '@/components/ui/list-item'; +import { Sheet, SheetHeader } from '@/components/ui/sheet'; +import { useToast } from '@/components/ui/toast'; +import { FEATURES } from '@/config/features'; +import { useEnrollContactInSequences } from '@/features/sequences/api/use-enroll-contact-in-sequences'; +import { useSequences, type SequenceListItem } from '@/features/sequences/api/use-sequences'; +import { useTheme } from '@/theme/use-theme'; + +interface SequencePickerSheetProps { + sheetRef: RefObject<BottomSheet | null>; + workspaceId: string; + contactId: string; + onEnrolled?: () => void; +} + +/** + * Multi-select sequence picker, opened from the contact detail Sequences tab's "Add to sequence" + * button. When `FEATURES.sendSequence` is off, the sheet still opens (so the feature stays + * discoverable) but every row renders disabled with a "Coming soon" badge, and selection/submit + * are both no-ops. + */ +export function SequencePickerSheet({ + sheetRef, + workspaceId, + contactId, + onEnrolled, +}: SequencePickerSheetProps) { + const { t } = useTranslation(); + const { colors, spacing } = useTheme(); + const showToast = useToast(); + const [selectedIds, setSelectedIds] = useState<string[]>([]); + + const { data: sequences, isLoading } = useSequences(workspaceId); + const enroll = useEnrollContactInSequences(workspaceId, contactId); + + const activeSequences = (sequences ?? []).filter((sequence) => sequence.active); + + function toggle(sequenceId: string) { + if (!FEATURES.sendSequence) return; + setSelectedIds((prev) => + prev.includes(sequenceId) ? prev.filter((id) => id !== sequenceId) : [...prev, sequenceId], + ); + } + + function handleConfirm() { + if (!FEATURES.sendSequence || selectedIds.length === 0) return; + enroll.mutate(selectedIds, { + onSuccess: () => { + showToast({ message: t('contacts.sequenceEnrolled'), tone: 'success' }); + setSelectedIds([]); + onEnrolled?.(); + sheetRef.current?.close(); + }, + onError: () => { + showToast({ message: t('contacts.sequenceEnrollFailed'), tone: 'danger' }); + }, + }); + } + + return ( + <Sheet ref={sheetRef} snapPoints={['60%', '90%']}> + <SheetHeader title={t('contacts.addToSequence')} onClose={() => sheetRef.current?.close()} /> + {!FEATURES.sendSequence ? ( + <View style={{ paddingHorizontal: spacing.md, paddingBottom: spacing.sm }}> + <Badge tone="warning">{t('common.comingSoon')}</Badge> + </View> + ) : null} + <BottomSheetFlatList + data={activeSequences} + keyExtractor={(sequence) => sequence.id} + ListEmptyComponent={ + !isLoading ? ( + <EmptyState icon="git-network-outline" title={t('contacts.noActiveSequences')} /> + ) : null + } + renderItem={({ item }: { item: SequenceListItem }) => { + const selected = selectedIds.includes(item.id); + return ( + <ListItem + title={item.name} + subtitle={t('contacts.sequenceSubscribers', { count: item.subscribersCount })} + disabled={!FEATURES.sendSequence} + leading={ + <Icon + name={selected ? 'checkbox' : 'square-outline'} + size={22} + color={selected ? colors.brand : colors.textSecondary} + /> + } + trailing={ + !FEATURES.sendSequence ? ( + <Badge tone="warning">{t('common.comingSoon')}</Badge> + ) : undefined + } + accessibilityState={{ selected }} + onPress={() => toggle(item.id)} + /> + ); + }} + /> + <View + style={[ + styles.footer, + { padding: spacing.md, gap: spacing.sm, borderTopColor: colors.borderSubtle }, + ]} + > + <Button + label={t('contacts.enrollSelected', { count: selectedIds.length })} + onPress={handleConfirm} + disabled={!FEATURES.sendSequence || selectedIds.length === 0} + loading={enroll.isPending} + fullWidth + /> + </View> + </Sheet> + ); +} + +const styles = StyleSheet.create({ + footer: { + borderTopWidth: StyleSheet.hairlineWidth, + }, +}); diff --git a/src/features/settings/api/use-device-token.ts b/src/features/settings/api/use-device-token.ts new file mode 100644 index 0000000..cb7f67a --- /dev/null +++ b/src/features/settings/api/use-device-token.ts @@ -0,0 +1,89 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import * as Notifications from 'expo-notifications'; +import * as SecureStore from 'expo-secure-store'; +import { useEffect } from 'react'; +import { Platform } from 'react-native'; + +import { apiClient } from '@/api/client'; +import { ApiError } from '@/api/errors'; +import { getPushTokenAsync } from '@/lib/notifications'; + +/** + * Real Expo push token registration against `PUT/DELETE /users/me/device-tokens` (verified in + * src/api/generated/schema.ts: PUT body `{ workspaceId?, platform: "ios"|"android", token }`, + * DELETE body `{ token }`). The last-registered token is cached in SecureStore so unregister (and + * re-registration after a token rotation) always targets the token the backend actually has on + * file, not whatever `getExpoPushTokenAsync` returns *now* if it has since changed. + */ +const LAST_REGISTERED_TOKEN_KEY = 'chatbotx.lastRegisteredPushToken'; + +function resolvePlatform(): 'ios' | 'android' | null { + if (Platform.OS === 'ios') return 'ios'; + if (Platform.OS === 'android') return 'android'; + return null; // web / other — the backend enum has no matching value, don't attempt registration. +} + +async function registerToken(workspaceId: string, token: string): Promise<void> { + const platform = resolvePlatform(); + if (!platform) throw new Error('Push registration is only supported on iOS and Android.'); + + const { error } = await apiClient.PUT('/users/me/device-tokens', { + body: { workspaceId, platform, token }, + }); + if (error) throw new ApiError(error); + await SecureStore.setItemAsync(LAST_REGISTERED_TOKEN_KEY, token); +} + +async function unregisterToken(token: string): Promise<void> { + const { error } = await apiClient.DELETE('/users/me/device-tokens', { body: { token } }); + if (error) throw new ApiError(error); +} + +export function useRegisterDeviceToken(workspaceId: string) { + return useMutation({ + mutationFn: async () => { + const result = await getPushTokenAsync(); + if (result.status !== 'granted') { + throw new Error(`Push permission ${result.status}`); + } + await registerToken(workspaceId, result.token); + return result.token; + }, + }); +} + +export function useUnregisterDeviceToken() { + return useMutation({ + mutationFn: async () => { + const token = await SecureStore.getItemAsync(LAST_REGISTERED_TOKEN_KEY); + if (!token) return; + await unregisterToken(token); + await SecureStore.deleteItemAsync(LAST_REGISTERED_TOKEN_KEY); + }, + }); +} + +/** + * Expo can issue a new push token at any time (token rotation) — this re-registers automatically + * whenever that happens, but only while the user has push enabled (tracked by the caller via + * `enabled`), and unregisters the stale token first so the backend never holds two tokens for one + * install. + */ +export function usePushTokenRotation(workspaceId: string, enabled: boolean): void { + const queryClient = useQueryClient(); + + useEffect(() => { + if (!enabled) return; + + const subscription = Notifications.addPushTokenListener(async ({ data: newToken }) => { + const previousToken = await SecureStore.getItemAsync(LAST_REGISTERED_TOKEN_KEY); + if (previousToken && previousToken !== newToken) { + await unregisterToken(previousToken).catch(() => {}); + } + await registerToken(workspaceId, newToken); + queryClient.invalidateQueries({ queryKey: ['device-token'] }); + }); + + return () => subscription.remove(); + }, [enabled, workspaceId, queryClient]); +} diff --git a/src/features/settings/api/use-teams.ts b/src/features/settings/api/use-teams.ts new file mode 100644 index 0000000..a6692ce --- /dev/null +++ b/src/features/settings/api/use-teams.ts @@ -0,0 +1,27 @@ +import { useQuery } from '@tanstack/react-query'; + +import { apiClient } from '@/api/client'; +import { ApiError } from '@/api/errors'; +import { queryKeys } from '@/api/query-keys'; + +/** + * `GET /workspaces/{workspaceId}/inbox-teams` (`inboxTeamsAPI.listInboxTeamsAuthenticatedAPI`) — + * an enterprise-tier feature (mounted from `@/enterprise/features/inbox-teams` in aha.chat's + * router). Not every workspace/plan has this — the members screen treats any error here as "no + * teams" (hides the section) rather than showing a broken error state, since a 403/404 from an + * unlicensed workspace is an expected outcome, not a real failure to surface to the user. + */ +export function useTeamsList(workspaceId: string) { + return useQuery({ + queryKey: queryKeys.ws.teams.list(workspaceId), + queryFn: async () => { + const { data, error } = await apiClient.GET('/workspaces/{workspaceId}/inbox-teams', { + params: { path: { workspaceId } }, + }); + if (error) throw new ApiError(error); + return data.data; + }, + enabled: workspaceId.length > 0, + retry: false, // an unlicensed workspace's 403 shouldn't retry — resolve to "hidden" quickly. + }); +} diff --git a/src/features/settings/components/language-sheet.tsx b/src/features/settings/components/language-sheet.tsx new file mode 100644 index 0000000..6b7d312 --- /dev/null +++ b/src/features/settings/components/language-sheet.tsx @@ -0,0 +1,45 @@ +import type BottomSheet from '@gorhom/bottom-sheet'; +import { BottomSheetFlatList } from '@gorhom/bottom-sheet'; +import type { RefObject } from 'react'; +import { View } from 'react-native'; + +import { Icon } from '@/components/ui/icon'; +import { ListItem } from '@/components/ui/list-item'; +import { Sheet } from '@/components/ui/sheet'; +import { changeAppLanguage } from '@/i18n/apply-language'; +import { localeMeta, supportedLanguages, type SupportedLanguage } from '@/i18n/locales'; +import { useTheme } from '@/theme/use-theme'; + +interface LanguageSheetProps { + sheetRef: RefObject<BottomSheet | null>; + currentLanguage: SupportedLanguage; +} + +export function LanguageSheet({ sheetRef, currentLanguage }: LanguageSheetProps) { + const { colors } = useTheme(); + + return ( + <Sheet ref={sheetRef} snapPoints={['60%', '90%']}> + <BottomSheetFlatList + data={supportedLanguages} + keyExtractor={(language) => language} + renderItem={({ item: language }) => ( + <ListItem + title={localeMeta[language].nativeLabel} + trailing={ + language === currentLanguage ? ( + <View> + <Icon name="checkmark" size={20} color={colors.primary} /> + </View> + ) : undefined + } + onPress={() => { + sheetRef.current?.close(); + void changeAppLanguage(language); + }} + /> + )} + /> + </Sheet> + ); +} diff --git a/src/features/workspaces/api/use-switch-workspace.test.tsx b/src/features/workspaces/api/use-switch-workspace.test.tsx new file mode 100644 index 0000000..80f5d35 --- /dev/null +++ b/src/features/workspaces/api/use-switch-workspace.test.tsx @@ -0,0 +1,125 @@ +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { renderHook } from '@testing-library/react-native'; +import { act } from 'react'; +import type { PropsWithChildren } from 'react'; + +import { useSwitchWorkspace } from '@/features/workspaces/api/use-switch-workspace'; +import { useConversationFilters } from '@/features/conversations/stores/use-conversation-filters'; +import { useWorkspaceStore } from '@/stores/use-workspace-store'; + +const mockReplace = jest.fn(); + +jest.mock('expo-router', () => ({ + useRouter: () => ({ replace: mockReplace }), +})); + +function seedQueries(queryClient: QueryClient, workspaceId: string) { + queryClient.setQueryData(['ws', workspaceId, 'conversations', 'list', {}], { pages: [] }); + queryClient.setQueryData(['ws', workspaceId, 'contacts', 'list', {}], { pages: [] }); +} + +// `renderHook` in this @testing-library/react-native version is async (it awaits an internal +// `render()` call) — every call site must `await` it or `result` resolves to a Promise instead of +// the ref object, and `result.current` throws. +function renderSwitchWorkspace(queryClient: QueryClient) { + function wrapper({ children }: PropsWithChildren) { + return <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>; + } + return renderHook(() => useSwitchWorkspace(), { wrapper }); +} + +const activeQueryClients: QueryClient[] = []; + +/** Tracks every QueryClient created in a test so it can be torn down afterward — a live + * QueryClient schedules per-query `gcTime` timers (and focus/online listeners via `unmount()`) + * that otherwise keep the Jest process alive past the test run. */ +function createTestQueryClient(): QueryClient { + const queryClient = new QueryClient(); + activeQueryClients.push(queryClient); + return queryClient; +} + +describe('useSwitchWorkspace', () => { + beforeEach(() => { + mockReplace.mockClear(); + useWorkspaceStore.setState({ currentWorkspaceId: null }); + useConversationFilters.getState().reset(); + }); + + afterEach(() => { + while (activeQueryClients.length > 0) { + const queryClient = activeQueryClients.pop()!; + queryClient.clear(); + queryClient.unmount(); + } + }); + + it('removes cached queries for the previous workspace only', async () => { + const queryClient = createTestQueryClient(); + seedQueries(queryClient, 'ws-old'); + seedQueries(queryClient, 'ws-new'); + useWorkspaceStore.setState({ currentWorkspaceId: 'ws-old' }); + + const { result } = await renderSwitchWorkspace(queryClient); + act(() => { + result.current('ws-new', { navigate: true }); + }); + + expect(queryClient.getQueryData(['ws', 'ws-old', 'conversations', 'list', {}])).toBeUndefined(); + expect(queryClient.getQueryData(['ws', 'ws-new', 'conversations', 'list', {}])).toBeDefined(); + }); + + it('sets the new workspace id and navigates to conversations when opts.navigate is true', async () => { + const queryClient = createTestQueryClient(); + useWorkspaceStore.setState({ currentWorkspaceId: 'ws-old' }); + + const { result } = await renderSwitchWorkspace(queryClient); + act(() => { + result.current('ws-new', { navigate: true }); + }); + + expect(useWorkspaceStore.getState().currentWorkspaceId).toBe('ws-new'); + expect(mockReplace).toHaveBeenCalledWith('/(app)/(tabs)/conversations'); + }); + + it('does not navigate when opts.navigate is omitted for a non-null workspace', async () => { + const queryClient = createTestQueryClient(); + useWorkspaceStore.setState({ currentWorkspaceId: 'ws-old' }); + + const { result } = await renderSwitchWorkspace(queryClient); + act(() => { + result.current('ws-new'); + }); + + expect(useWorkspaceStore.getState().currentWorkspaceId).toBe('ws-new'); + expect(mockReplace).not.toHaveBeenCalled(); + }); + + it('navigates to the workspace picker and clears the workspace id when next is null', async () => { + const queryClient = createTestQueryClient(); + useWorkspaceStore.setState({ currentWorkspaceId: 'ws-old' }); + + const { result } = await renderSwitchWorkspace(queryClient); + act(() => { + result.current(null); + }); + + expect(useWorkspaceStore.getState().currentWorkspaceId).toBeNull(); + expect(mockReplace).toHaveBeenCalledWith('/(app)/workspace-picker'); + }); + + it('resets the conversation filters store on switch', async () => { + const queryClient = createTestQueryClient(); + useWorkspaceStore.setState({ currentWorkspaceId: 'ws-old' }); + useConversationFilters.getState().setKeyword('hello'); + useConversationFilters.getState().setAssignedId('user-1'); + + const { result } = await renderSwitchWorkspace(queryClient); + act(() => { + result.current('ws-new'); + }); + + expect(useConversationFilters.getState().keyword).toBe(''); + expect(useConversationFilters.getState().assignedId).toBeUndefined(); + }); +}); diff --git a/src/features/workspaces/api/use-switch-workspace.ts b/src/features/workspaces/api/use-switch-workspace.ts new file mode 100644 index 0000000..505a84f --- /dev/null +++ b/src/features/workspaces/api/use-switch-workspace.ts @@ -0,0 +1,59 @@ +import { useQueryClient } from '@tanstack/react-query'; +import { useRouter } from 'expo-router'; +import { useCallback } from 'react'; + +import { isWorkspaceQuery } from '@/api/query-keys'; +import { useConversationFilters } from '@/features/conversations/stores/use-conversation-filters'; +import { useWorkspaceStore } from '@/stores/use-workspace-store'; + +interface SwitchWorkspaceOptions { + /** When true, navigates to the conversations tab after switching (used by an inline switch from + * the workspace switcher sheet). When `next` is null, this is ignored — the workspace picker is + * always the destination for "no workspace selected". */ + navigate?: boolean; +} + +/** + * Single source of truth for the "switch workspace" flow, replacing the three near-identical + * blocks that used to live in settings/index.tsx, workspace-blocked-gate.tsx, and + * workspace-picker.tsx. Steps: drop every cached query for the OLD workspace (so a re-entered + * workspace never shows stale data from a previous session), reset the conversation filters store + * (filters are workspace-agnostic UI state but shouldn't leak a stale "assigned to me" filter into + * a workspace where that user id is meaningless), select the new workspace, then navigate. + * + * `next: null` clears the selection entirely and always routes to the workspace picker (sign-out + * style / "switch workspace" from a blocked gate). A non-null `next` optionally navigates to the + * inbox when `opts.navigate` is set — the workspace switcher sheet uses this for an inline switch + * that doesn't require leaving the current tab stack; callers that already own their own navigation + * (e.g. the picker screen selecting a workspace for the first time) can omit it. + */ +export function useSwitchWorkspace() { + const queryClient = useQueryClient(); + const router = useRouter(); + const currentWorkspaceId = useWorkspaceStore((state) => state.currentWorkspaceId); + const setCurrentWorkspaceId = useWorkspaceStore((state) => state.setCurrentWorkspaceId); + const resetConversationFilters = useConversationFilters((state) => state.reset); + + return useCallback( + (next: string | null, opts?: SwitchWorkspaceOptions) => { + if (currentWorkspaceId) { + queryClient.removeQueries({ + predicate: (query) => isWorkspaceQuery(query.queryKey, currentWorkspaceId), + }); + } + + resetConversationFilters(); + setCurrentWorkspaceId(next); + + if (next === null) { + router.replace('/(app)/workspace-picker'); + return; + } + + if (opts?.navigate) { + router.replace('/(app)/(tabs)/conversations'); + } + }, + [currentWorkspaceId, queryClient, resetConversationFilters, setCurrentWorkspaceId, router], + ); +} diff --git a/src/features/workspaces/api/use-workspaces.ts b/src/features/workspaces/api/use-workspaces.ts new file mode 100644 index 0000000..488c6f6 --- /dev/null +++ b/src/features/workspaces/api/use-workspaces.ts @@ -0,0 +1,27 @@ +import { useQuery } from '@tanstack/react-query'; + +import { apiClient } from '@/api/client'; +import { ApiError } from '@/api/errors'; +import { queryKeys } from '@/api/query-keys'; + +export interface Workspace { + id: string; + name: string; + logo: string | null; + brandColor: string; +} + +/** Lists workspaces the current user is a member of (`GET /users/me/workspaces`, Phase 0 backend + * addition). Global key — not workspace-scoped, since it lists across workspaces. */ +export function useWorkspaces() { + return useQuery({ + queryKey: queryKeys.workspaces(), + queryFn: async (): Promise<Workspace[]> => { + const { data, error } = await apiClient.GET('/users/me/workspaces'); + if (error) { + throw new ApiError(error); + } + return data.workspaces; + }, + }); +} diff --git a/src/features/workspaces/components/workspace-pill.tsx b/src/features/workspaces/components/workspace-pill.tsx new file mode 100644 index 0000000..43641ee --- /dev/null +++ b/src/features/workspaces/components/workspace-pill.tsx @@ -0,0 +1,56 @@ +import { Pressable, StyleSheet } from 'react-native'; + +import { Avatar } from '@/components/ui/avatar'; +import { Icon } from '@/components/ui/icon'; +import { Text } from '@/components/ui/text'; +import { useTheme } from '@/theme/use-theme'; + +interface WorkspacePillProps { + name: string; + logo?: string | null; + onPress: () => void; +} + +/** Inbox header affordance: current workspace's avatar + name, tapping opens the workspace + * switcher sheet. Kept as a small standalone component (rather than inlined in the conversations + * screen) so Settings' "Switch workspace" row and any future header reuse it identically. */ +export function WorkspacePill({ name, logo, onPress }: WorkspacePillProps) { + const { colors, spacing, radius } = useTheme(); + + return ( + <Pressable + accessibilityRole="button" + accessibilityLabel={name} + onPress={onPress} + style={({ pressed }) => [ + styles.pill, + { + backgroundColor: colors.surface, + borderRadius: radius.full, + paddingVertical: spacing.xs, + paddingHorizontal: spacing.ms, + gap: spacing.xs, + opacity: pressed ? 0.7 : 1, + }, + ]} + > + <Avatar uri={logo} name={name} size={24} /> + <Text variant="caption" numberOfLines={1} style={styles.name}> + {name} + </Text> + <Icon name="chevron-down" size={14} color={colors.textSecondary} /> + </Pressable> + ); +} + +const styles = StyleSheet.create({ + pill: { + flexDirection: 'row', + alignItems: 'center', + alignSelf: 'flex-start', + maxWidth: 200, + }, + name: { + flexShrink: 1, + }, +}); diff --git a/src/features/workspaces/components/workspace-switcher-sheet.tsx b/src/features/workspaces/components/workspace-switcher-sheet.tsx new file mode 100644 index 0000000..562735f --- /dev/null +++ b/src/features/workspaces/components/workspace-switcher-sheet.tsx @@ -0,0 +1,73 @@ +import type BottomSheet from '@gorhom/bottom-sheet'; +import { BottomSheetFlatList } from '@gorhom/bottom-sheet'; +import { forwardRef } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Alert } from 'react-native'; + +import { EmptyState } from '@/components/ui/empty-state'; +import { Sheet } from '@/components/ui/sheet'; +import { useSwitchWorkspace } from '@/features/workspaces/api/use-switch-workspace'; +import { useWorkspaces, type Workspace } from '@/features/workspaces/api/use-workspaces'; +import { WorkspaceRow } from '@/features/workspaces/workspace-row'; +import { useWorkspaceStore } from '@/stores/use-workspace-store'; + +/** + * Inline workspace switcher, opened from the inbox header pill and from Settings' "Switch + * workspace" row — replaces navigating away to the full-screen picker for the common case of + * hopping between a small number of workspaces. Picking a DIFFERENT workspace confirms first + * (switching drops every cached query for the current one), then switches and closes; picking the + * already-active workspace just closes the sheet. + */ +export const WorkspaceSwitcherSheet = forwardRef<BottomSheet>( + function WorkspaceSwitcherSheet(_props, ref) { + const { t } = useTranslation(); + const { data: workspaces, isPending } = useWorkspaces(); + const currentWorkspaceId = useWorkspaceStore((state) => state.currentWorkspaceId); + const switchWorkspace = useSwitchWorkspace(); + + function closeSheet() { + if (ref && 'current' in ref) { + ref.current?.close(); + } + } + + function handleSelect(workspace: Workspace) { + if (workspace.id === currentWorkspaceId) { + closeSheet(); + return; + } + + Alert.alert( + t('workspaces.switchConfirmTitle'), + t('workspaces.switchConfirmBody', { name: workspace.name }), + [ + { text: t('common.cancel'), style: 'cancel' }, + { + text: t('workspaces.switch'), + onPress: () => { + switchWorkspace(workspace.id, { navigate: true }); + closeSheet(); + }, + }, + ], + ); + } + + return ( + <Sheet ref={ref} snapPoints={['50%', '80%']}> + <BottomSheetFlatList + data={isPending ? [] : (workspaces ?? [])} + keyExtractor={(item) => item.id} + ListEmptyComponent={<EmptyState icon="business-outline" title={t('workspaces.empty')} />} + renderItem={({ item }) => ( + <WorkspaceRow + workspace={item} + selected={item.id === currentWorkspaceId} + onPress={handleSelect} + /> + )} + /> + </Sheet> + ); + }, +); diff --git a/src/features/workspaces/workspace-row.tsx b/src/features/workspaces/workspace-row.tsx new file mode 100644 index 0000000..1179116 --- /dev/null +++ b/src/features/workspaces/workspace-row.tsx @@ -0,0 +1,31 @@ +import { Avatar } from '@/components/ui/avatar'; +import { Icon } from '@/components/ui/icon'; +import { ListItem } from '@/components/ui/list-item'; +import { useTheme } from '@/theme/use-theme'; + +import type { Workspace } from './api/use-workspaces'; + +interface WorkspaceRowProps { + workspace: Workspace; + onPress: (workspace: Workspace) => void; + /** Marks this row as the currently active workspace — shows a checkmark instead of the + * chevron. */ + selected?: boolean; +} + +export function WorkspaceRow({ workspace, onPress, selected = false }: WorkspaceRowProps) { + const { colors } = useTheme(); + + return ( + <ListItem + title={workspace.name} + leading={<Avatar uri={workspace.logo} name={workspace.name} size={44} />} + trailing={ + selected ? <Icon name="checkmark-circle" size={22} color={colors.brand} /> : undefined + } + onPress={() => onPress(workspace)} + showChevron={!selected} + accessibilityState={{ selected }} + /> + ); +} diff --git a/src/hooks/use-color-scheme.ts b/src/hooks/use-color-scheme.ts new file mode 100644 index 0000000..17e3c63 --- /dev/null +++ b/src/hooks/use-color-scheme.ts @@ -0,0 +1 @@ +export { useColorScheme } from 'react-native'; diff --git a/src/hooks/use-color-scheme.web.ts b/src/hooks/use-color-scheme.web.ts new file mode 100644 index 0000000..4843764 --- /dev/null +++ b/src/hooks/use-color-scheme.web.ts @@ -0,0 +1,29 @@ +import { useSyncExternalStore } from 'react'; +import { useColorScheme as useRNColorScheme } from 'react-native'; + +const subscribeNoop = () => () => undefined; + +/** + * To support static rendering, this value needs to be re-calculated on the client side for web. + * useSyncExternalStore's getServerSnapshot returns a fixed value during SSR/hydration, then the + * client snapshot takes over post-hydration without triggering the cascading-render pattern that + * an effect-based setState would. + */ +function useHasHydrated() { + return useSyncExternalStore( + subscribeNoop, + () => true, + () => false, + ); +} + +export function useColorScheme() { + const hasHydrated = useHasHydrated(); + const colorScheme = useRNColorScheme(); + + if (hasHydrated) { + return colorScheme; + } + + return 'light'; +} diff --git a/src/i18n/apply-language.ts b/src/i18n/apply-language.ts new file mode 100644 index 0000000..b8ec168 --- /dev/null +++ b/src/i18n/apply-language.ts @@ -0,0 +1,66 @@ +import { Alert, DevSettings, I18nManager } from 'react-native'; +import * as Updates from 'expo-updates'; + +import { i18next } from '@/i18n'; +import { setDayjsLocale } from '@/i18n/dayjs-locales'; +import { isRTL, type SupportedLanguage } from '@/i18n/locales'; +import { useSettingsStore } from '@/stores/use-settings-store'; + +/** Applies a language to the running app's i18next + dayjs instances. Does not persist it or + * reconcile RTL — see `changeAppLanguage` for the full user-facing flow. */ +export async function applyLanguage(language: SupportedLanguage): Promise<void> { + await i18next.changeLanguage(language); + setDayjsLocale(language); +} + +/** + * Reloads the app JS bundle. `Updates.reloadAsync` is a native-production API that throws in + * `__DEV__`/Expo Go (no update runtime available there), so we use `DevSettings.reload()` + * instead in those environments. `Updates.isEnabled` reflects whether the `expo-updates` native + * module is actually linked into *this* build — it can be false in a production build if the + * app was built before the `expo-updates` package/plugin were added (needs a fresh build to pick + * up the native module), independent of whether EAS Update hosting is configured. In that case + * there is no reload mechanism available at all; the RTL/language change still takes effect on + * the *next* natural app relaunch, so we surface an alert rather than silently doing nothing. + */ +export async function reloadApp(): Promise<void> { + if (__DEV__) { + DevSettings.reload(); + return; + } + if (!Updates.isEnabled) { + Alert.alert( + i18next.t('settings.restartRequiredTitle'), + i18next.t('settings.restartManuallyMessage'), + ); + return; + } + await Updates.reloadAsync(); +} + +/** + * Full user-initiated language change: persist the choice, apply it live, and — if the new + * language's writing direction differs from the current `I18nManager` direction — force the RTL + * flag and prompt the user to restart (native layout direction cannot flip without a reload). + */ +export async function changeAppLanguage(language: SupportedLanguage): Promise<void> { + useSettingsStore.getState().setLanguage(language); + await applyLanguage(language); + + const targetIsRTL = isRTL(language); + if (targetIsRTL === I18nManager.isRTL) { + return; + } + + I18nManager.allowRTL(true); + I18nManager.forceRTL(targetIsRTL); + + Alert.alert( + i18next.t('settings.restartRequiredTitle'), + i18next.t('settings.restartRequiredMessage'), + [ + { text: i18next.t('settings.restartLater'), style: 'cancel' }, + { text: i18next.t('settings.restartNow'), onPress: () => void reloadApp() }, + ], + ); +} diff --git a/src/i18n/dayjs-locales.test.ts b/src/i18n/dayjs-locales.test.ts new file mode 100644 index 0000000..222b58d --- /dev/null +++ b/src/i18n/dayjs-locales.test.ts @@ -0,0 +1,31 @@ +import dayjs from 'dayjs'; + +import { setDayjsLocale } from '@/i18n/dayjs-locales'; + +afterEach(() => { + dayjs.locale('en'); +}); + +describe('setDayjsLocale', () => { + it('maps pt-BR to the pt-br dayjs locale', () => { + setDayjsLocale('pt-BR'); + expect(dayjs.locale()).toBe('pt-br'); + }); + + it('maps pt-PT to the pt dayjs locale (dayjs has no distinct pt-pt)', () => { + setDayjsLocale('pt-PT'); + expect(dayjs.locale()).toBe('pt'); + }); + + it('maps zh-CN and zh-TW to their lowercase dayjs equivalents', () => { + setDayjsLocale('zh-CN'); + expect(dayjs.locale()).toBe('zh-cn'); + setDayjsLocale('zh-TW'); + expect(dayjs.locale()).toBe('zh-tw'); + }); + + it('maps a simple language like vi to its identity lowercase locale', () => { + setDayjsLocale('vi'); + expect(dayjs.locale()).toBe('vi'); + }); +}); diff --git a/src/i18n/dayjs-locales.ts b/src/i18n/dayjs-locales.ts new file mode 100644 index 0000000..e692c47 --- /dev/null +++ b/src/i18n/dayjs-locales.ts @@ -0,0 +1,54 @@ +import 'dayjs/locale/ar'; +import 'dayjs/locale/da'; +import 'dayjs/locale/de'; +import 'dayjs/locale/en'; +import 'dayjs/locale/es'; +import 'dayjs/locale/fi'; +import 'dayjs/locale/fr'; +import 'dayjs/locale/he'; +import 'dayjs/locale/id'; +import 'dayjs/locale/it'; +import 'dayjs/locale/ja'; +import 'dayjs/locale/nl'; +import 'dayjs/locale/pt-br'; +import 'dayjs/locale/pt'; +import 'dayjs/locale/ro'; +import 'dayjs/locale/sv'; +import 'dayjs/locale/tr'; +import 'dayjs/locale/vi'; +import 'dayjs/locale/zh-cn'; +import 'dayjs/locale/zh-tw'; +import dayjs from 'dayjs'; + +import type { SupportedLanguage } from '@/i18n/locales'; + +/** + * dayjs has no `pt-pt` locale — `pt` IS European Portuguese there, and Brazilian Portuguese is + * the separate `pt-br` locale. This mapping must stay in sync with that dayjs convention. + */ +const dayjsLocaleNames: Record<SupportedLanguage, string> = { + ar: 'ar', + da: 'da', + de: 'de', + en: 'en', + es: 'es', + fi: 'fi', + fr: 'fr', + he: 'he', + id: 'id', + it: 'it', + ja: 'ja', + nl: 'nl', + 'pt-BR': 'pt-br', + 'pt-PT': 'pt', + ro: 'ro', + sv: 'sv', + tr: 'tr', + vi: 'vi', + 'zh-CN': 'zh-cn', + 'zh-TW': 'zh-tw', +}; + +export function setDayjsLocale(language: SupportedLanguage): void { + dayjs.locale(dayjsLocaleNames[language]); +} diff --git a/src/i18n/index.ts b/src/i18n/index.ts new file mode 100644 index 0000000..772e6d9 --- /dev/null +++ b/src/i18n/index.ts @@ -0,0 +1,61 @@ +import * as Localization from 'expo-localization'; +import i18next from 'i18next'; +import { initReactI18next } from 'react-i18next'; + +import { defaultLanguage, resolveLocale } from '@/i18n/locales'; +import { defaultNamespace } from '@/i18n/namespace'; +import { resources } from '@/i18n/resources'; + +export { + defaultLanguage, + isRTL, + isSupportedLanguage, + localeMeta, + resolveLocale, + supportedLanguages, +} from '@/i18n/locales'; +export type { SupportedLanguage } from '@/i18n/locales'; +export { defaultNamespace } from '@/i18n/namespace'; + +function resolveDeviceLanguage() { + // `languageTag` is the full BCP-47 tag (e.g. `zh-Hant-TW`, `pt-PT`) — `languageCode` would only + // give the primary subtag and lose the region info resolveLocale needs for zh/pt normalization. + return resolveLocale(Localization.getLocales()[0]?.languageTag); +} + +let initialized = false; + +/** + * Initializes i18next once. Safe to call multiple times (e.g. across fast-refresh reloads) — + * only the first call performs setup. Uses the device-resolved language; the persisted user + * preference (if different) is applied afterwards via `applyLanguage` in the app bootstrap gate, + * since settings-store hydration is async and can't block this synchronous module-load init. + */ +export function initI18n(): typeof i18next { + if (initialized) { + return i18next; + } + + // i18next's default export intentionally also carries `.use()`/`.init()` as instance methods — + // this is the library's documented API shape, not an accidental default-vs-named mismatch. + // eslint-disable-next-line import/no-named-as-default-member + i18next.use(initReactI18next).init({ + resources, + lng: resolveDeviceLanguage(), + fallbackLng: defaultLanguage, + defaultNS: defaultNamespace, + interpolation: { + escapeValue: false, + }, + // React Native has no XSS/HTML-injection concerns from interpolation, and Suspense adds + // complexity we don't need for static locale files loaded synchronously at startup. + react: { + useSuspense: false, + }, + }); + + initialized = true; + return i18next; +} + +export { i18next }; diff --git a/src/i18n/locales.test.ts b/src/i18n/locales.test.ts new file mode 100644 index 0000000..f8bec25 --- /dev/null +++ b/src/i18n/locales.test.ts @@ -0,0 +1,75 @@ +import { isRTL, isSupportedLanguage, resolveLocale } from '@/i18n/locales'; + +describe('resolveLocale', () => { + it('returns en for undefined', () => { + expect(resolveLocale(undefined)).toBe('en'); + }); + + it('passes through an exact supported tag', () => { + expect(resolveLocale('en')).toBe('en'); + expect(resolveLocale('vi')).toBe('vi'); + expect(resolveLocale('pt-PT')).toBe('pt-PT'); + }); + + it('normalizes bare pt to pt-BR', () => { + expect(resolveLocale('pt')).toBe('pt-BR'); + }); + + it('normalizes any pt-* region tag to pt-BR (pt-PT only passes through exactly)', () => { + expect(resolveLocale('pt-MZ')).toBe('pt-BR'); + }); + + it('normalizes zh-Hant-HK to zh-TW', () => { + expect(resolveLocale('zh-Hant-HK')).toBe('zh-TW'); + }); + + it('normalizes zh-TW-flavored tags to zh-TW', () => { + expect(resolveLocale('zh-TW')).toBe('zh-TW'); + expect(resolveLocale('zh-HK')).toBe('zh-TW'); + expect(resolveLocale('zh-MO')).toBe('zh-TW'); + }); + + it('normalizes zh-CN/zh-Hans to zh-CN', () => { + expect(resolveLocale('zh-CN')).toBe('zh-CN'); + expect(resolveLocale('zh-Hans')).toBe('zh-CN'); + }); + + it('normalizes bare zh to zh-CN', () => { + expect(resolveLocale('zh')).toBe('zh-CN'); + }); + + it('matches on primary subtag for a region variant not otherwise special-cased', () => { + expect(resolveLocale('de-AT')).toBe('de'); + }); + + it('falls back to en for an unsupported language', () => { + expect(resolveLocale('xx-YY')).toBe('en'); + }); + + it('falls back to en for an empty string', () => { + expect(resolveLocale('')).toBe('en'); + }); +}); + +describe('isSupportedLanguage', () => { + it('returns true for supported tags', () => { + expect(isSupportedLanguage('en')).toBe(true); + expect(isSupportedLanguage('zh-TW')).toBe(true); + }); + + it('returns false for unsupported tags', () => { + expect(isSupportedLanguage('xx')).toBe(false); + }); +}); + +describe('isRTL', () => { + it('returns true for ar and he', () => { + expect(isRTL('ar')).toBe(true); + expect(isRTL('he')).toBe(true); + }); + + it('returns false for ltr languages', () => { + expect(isRTL('en')).toBe(false); + expect(isRTL('vi')).toBe(false); + }); +}); diff --git a/src/i18n/locales.ts b/src/i18n/locales.ts new file mode 100644 index 0000000..e0b276b --- /dev/null +++ b/src/i18n/locales.ts @@ -0,0 +1,102 @@ +export const supportedLanguages = [ + 'ar', + 'da', + 'de', + 'en', + 'es', + 'fi', + 'fr', + 'he', + 'id', + 'it', + 'ja', + 'nl', + 'pt-BR', + 'pt-PT', + 'ro', + 'sv', + 'tr', + 'vi', + 'zh-CN', + 'zh-TW', +] as const; + +export type SupportedLanguage = (typeof supportedLanguages)[number]; +export const defaultLanguage: SupportedLanguage = 'en'; + +export const localeMeta: Record<SupportedLanguage, { nativeLabel: string; dir: 'ltr' | 'rtl' }> = { + ar: { nativeLabel: 'العربية', dir: 'rtl' }, + da: { nativeLabel: 'Dansk', dir: 'ltr' }, + de: { nativeLabel: 'Deutsch', dir: 'ltr' }, + en: { nativeLabel: 'English', dir: 'ltr' }, + es: { nativeLabel: 'Español', dir: 'ltr' }, + fi: { nativeLabel: 'Suomi', dir: 'ltr' }, + fr: { nativeLabel: 'Français', dir: 'ltr' }, + he: { nativeLabel: 'עברית', dir: 'rtl' }, + id: { nativeLabel: 'Bahasa Indonesia', dir: 'ltr' }, + it: { nativeLabel: 'Italiano', dir: 'ltr' }, + ja: { nativeLabel: '日本語', dir: 'ltr' }, + nl: { nativeLabel: 'Nederlands', dir: 'ltr' }, + 'pt-BR': { nativeLabel: 'Português (Brasil)', dir: 'ltr' }, + 'pt-PT': { nativeLabel: 'Português (Portugal)', dir: 'ltr' }, + ro: { nativeLabel: 'Română', dir: 'ltr' }, + sv: { nativeLabel: 'Svenska', dir: 'ltr' }, + tr: { nativeLabel: 'Türkçe', dir: 'ltr' }, + vi: { nativeLabel: 'Tiếng Việt', dir: 'ltr' }, + 'zh-CN': { nativeLabel: '简体中文', dir: 'ltr' }, + 'zh-TW': { nativeLabel: '繁體中文', dir: 'ltr' }, +}; + +export function isSupportedLanguage(value: string): value is SupportedLanguage { + return (supportedLanguages as readonly string[]).includes(value); +} + +export function isRTL(language: SupportedLanguage): boolean { + return localeMeta[language].dir === 'rtl'; +} + +function matchesLocaleTag(normalizedValue: string, localeTag: string) { + return normalizedValue === localeTag || normalizedValue.startsWith(`${localeTag}-`); +} + +/** + * Verbatim port of the web app's resolveLocale (aha.chat apps/builder/src/i18n/config.ts) so + * device-language resolution matches the web product exactly (pt/zh normalization, RTL locales). + */ +export function resolveLocale(value: string | undefined): SupportedLanguage { + if (!value) { + return defaultLanguage; + } + + const normalizedValue = value.trim(); + if (isSupportedLanguage(normalizedValue)) { + return normalizedValue; + } + + const normalizedLower = normalizedValue.toLowerCase(); + const language = normalizedLower.split('-')[0]; + if (!language) { + return defaultLanguage; + } + if (language === 'pt') { + return 'pt-BR'; + } + if ( + ['zh-hant', 'zh-hk', 'zh-mo', 'zh-tw'].some((localeTag) => + matchesLocaleTag(normalizedLower, localeTag), + ) + ) { + return 'zh-TW'; + } + if (['zh-cn', 'zh-hans'].some((localeTag) => matchesLocaleTag(normalizedLower, localeTag))) { + return 'zh-CN'; + } + if (language === 'zh') { + return 'zh-CN'; + } + + return ( + supportedLanguages.find((locale) => locale.toLowerCase().split('-')[0] === language) ?? + defaultLanguage + ); +} diff --git a/src/i18n/locales/ar.json b/src/i18n/locales/ar.json new file mode 100644 index 0000000..ace3ef7 --- /dev/null +++ b/src/i18n/locales/ar.json @@ -0,0 +1,264 @@ +{ + "common": { + "loading": "جارٍ التحميل…", + "error": "حدث خطأ ما", + "retry": "إعادة المحاولة", + "cancel": "إلغاء", + "save": "حفظ", + "done": "تم", + "search": "بحث", + "confirm": "تأكيد", + "delete": "حذف", + "clear": "Clear", + "close": "Close", + "today": "Today", + "yesterday": "Yesterday", + "removeItem": "Remove {{label}}", + "comingSoon": "Coming soon", + "back": "Back" + }, + "auth": { + "signIn": "تسجيل الدخول", + "email": "البريد الإلكتروني", + "password": "كلمة المرور", + "forgotPassword": "هل نسيت كلمة المرور؟", + "signOut": "تسجيل الخروج", + "changePassword": "تغيير كلمة المرور", + "changePasswordRequired": "يجب عليك تعيين كلمة مرور جديدة قبل المتابعة.", + "currentPassword": "كلمة المرور الحالية", + "newPassword": "كلمة المرور الجديدة", + "sendResetLink": "إرسال رابط إعادة التعيين", + "resetLinkSent": "إذا كان هناك حساب مرتبط بهذا البريد الإلكتروني، فقد تم إرسال رابط إعادة التعيين.", + "orContinueWith": "أو تابع باستخدام", + "continueWithGoogle": "المتابعة باستخدام Google", + "continueWithFacebook": "المتابعة باستخدام Facebook", + "socialSignInFailed": "لم يكتمل تسجيل الدخول. يرجى المحاولة مرة أخرى.", + "signOutConfirmTitle": "Sign out?", + "signOutConfirmBody": "You'll need to sign in again to access your workspaces.", + "showPassword": "Show password", + "hidePassword": "Hide password", + "signInSubtitle": "Sign in to keep every conversation moving.", + "forgotPasswordSubtitle": "Enter your email and we'll send you a link to reset your password.", + "backToSignIn": "Back to sign in" + }, + "workspaces": { + "title": "مساحات العمل", + "empty": "لا توجد مساحات عمل بعد", + "current": "Current workspace", + "switch": "Switch", + "switchConfirmTitle": "Switch workspace?", + "switchConfirmBody": "Switch to {{name}}? You'll leave your current workspace." + }, + "inbox": { + "title": "Inbox" + }, + "conversations": { + "title": "المحادثات", + "empty": "لا توجد محادثات", + "filters": "التصفية", + "channel": "القناة", + "handledBy": "تمت معالجتها بواسطة", + "status": "الحالة", + "clearFilters": "مسح التصفية", + "filterAll": "الكل", + "channelMessenger": "Messenger", + "channelInstagram": "Instagram", + "channelWhatsapp": "WhatsApp", + "channelWebchat": "الدردشة عبر الموقع", + "channelZalo": "Zalo", + "channelTelegram": "Telegram", + "channelEmail": "البريد الإلكتروني", + "channelTiktok": "TikTok", + "statusUnread": "غير مقروءة", + "statusNoAdminReply": "لا يوجد رد من الفريق", + "statusFollowUp": "قيد المتابعة", + "statusArchived": "مؤرشفة", + "statusBlocked": "محظورة", + "handledByBot": "يتولاها الروبوت", + "handledByHuman": "يتولاها شخص", + "unassign": "إلغاء الإسناد", + "noOtherMembers": "لا يوجد أعضاء آخرون في مساحة العمل هذه بعد.", + "unnamed": "بدون اسم", + "unknownContact": "Unknown contact", + "attachmentPreview": "📎 {{count}} attachment(s)", + "a11yUnread": "unread", + "markRead": "Mark as read", + "markUnread": "Mark as unread", + "enableBot": "Enable bot", + "disableBot": "Disable bot", + "archive": "Archive", + "archiveUndone": "Conversation restored", + "undo": "Undo", + "botActive": "Bot handling", + "botPaused": "Bot paused", + "assignedTo": "Assigned to {{name}}", + "quickFilterMine": "Mine", + "emptyFiltered": "No conversations match your filters", + "botOn": "On", + "botOff": "Off", + "channelOmnichannel": "Omnichannel", + "botOnOffLabel": "Bot", + "actions": "Conversation actions", + "follow": "Follow", + "unfollow": "Unfollow", + "assign": "Assign", + "unarchive": "Unarchive", + "archiveConfirm": "Archive this conversation? You can find it again later in archived conversations.", + "unarchiveConfirm": "Restore this conversation from the archive?" + }, + "chat": { + "typeAMessage": "اكتب رسالة", + "send": "إرسال", + "failedToSend": "فشل الإرسال", + "failedToSendTapRetry": "فشل الإرسال. اضغط لإعادة المحاولة.", + "retrySend": "إعادة المحاولة", + "noMessages": "لا توجد رسائل بعد", + "edit": "تعديل", + "like": "إعجاب", + "hide": "إخفاء", + "delete": "حذف", + "searchSavedReplies": "البحث في الردود المحفوظة", + "noSavedReplies": "لا توجد ردود محفوظة", + "attachmentTooLarge": "حجم {{fileName}} أكبر من 5 ميغابايت.", + "quotaMac": "وصلت مساحة العمل هذه إلى حد جهات الاتصال في الخطة الحالية. قم بالترقية لمتابعة إرسال الرسائل.", + "quotaTrialExpired": "انتهت الفترة التجريبية أو خطة مساحة العمل هذه. قم بالترقية لمتابعة إرسال الرسائل.", + "quotaUnknown": "مساحة العمل هذه غير متاحة حاليًا.", + "dismiss": "تجاهل", + "grantCameraPermission": "الوصول إلى الكاميرا مطلوب لالتقاط صورة.", + "grantPermission": "منح الإذن", + "failedToSendPhoto": "فشل إرسال الصورة.", + "typing": "Typing…", + "attach": "Attach", + "camera": "Camera", + "photoLibrary": "Photo library", + "document": "Document", + "sendFlow": "Send flow", + "sendSequence": "Send sequence", + "sendingFlow": "Sending {{name}}…", + "flowSendFailed": "Couldn't send flow. Please try again.", + "savedReplies": "Saved replies", + "attachmentsTooLarge": "{{count}} file(s) are larger than 5MB and weren't sent: {{names}}", + "editingMessage": "Editing message", + "replyingTo": "Replying to {{name}}", + "reply": "Reply", + "editAndResend": "Edit & resend", + "repliedAttachment": "Attachment", + "repliedMessageUnavailable": "Original message unavailable", + "messageDeleted": "Message deleted", + "senderAgent": "You", + "senderBot": "Bot", + "senderContact": "Contact", + "scrollToBottom": "Scroll to latest messages", + "bubbleA11yLabel": "{{sender}} said {{text}}, {{time}}, {{status}}", + "status": { + "pending": "Sending", + "sent": "Sent", + "failed": "Failed to send" + }, + "locationShared": "Location shared", + "unsupportedContent": "[{{contentType}}] Unsupported on mobile", + "activityFallback": "Activity" + }, + "contacts": { + "title": "جهات الاتصال", + "contactTitle": "جهة الاتصال", + "empty": "لا توجد جهات اتصال", + "unknown": "غير معروف", + "blocked": "محظور", + "email": "البريد الإلكتروني", + "phone": "الهاتف", + "locale": "اللغة", + "timezone": "المنطقة الزمنية", + "location": "الموقع", + "customFields": "الحقول المخصصة", + "addCustomField": "إضافة حقل مخصص", + "assignedTags": "الوسوم", + "noTags": "لا توجد وسوم", + "addTag": "إضافة وسم", + "noSequences": "غير مسجل في أي تسلسل", + "sequenceStep": "الخطوة {{step}}", + "completed": "مكتمل", + "active": "نشط", + "tabInfo": "معلومات", + "tabTags": "الوسوم", + "tabSequences": "التسلسلات", + "tabNotes": "Notes", + "noNotes": "No notes yet", + "unknownAuthor": "Unknown", + "message": "Message", + "assign": "Assign", + "addToSequence": "Add to sequence", + "noActiveSequences": "No active sequences", + "sequenceSubscribers": "{{count}} subscriber", + "sequenceSubscribers_other": "{{count}} subscribers", + "enrollSelected": "Add ({{count}})", + "sequenceEnrolled": "Contact enrolled in sequence", + "sequenceEnrollFailed": "Couldn't enroll contact in sequence" + }, + "flows": { + "title": "المسارات", + "empty": "لا توجد مسارات", + "readOnlyNote": "المسارات للقراءة فقط على الجوال.", + "active": "نشط", + "inactive": "غير نشط", + "nodes": "{{count}} عقدة", + "nodes_zero": "لا توجد عقد", + "nodes_one": "عقدة واحدة", + "nodes_two": "عقدتان", + "nodes_few": "{{count}} عقد", + "nodes_many": "{{count}} عقدة", + "nodes_other": "{{count}} عقدة" + }, + "settings": { + "title": "الإعدادات", + "darkMode": "الوضع الداكن", + "theme": "المظهر", + "themeSystem": "النظام", + "themeLight": "فاتح", + "themeDark": "داكن", + "language": "اللغة", + "members": "الأعضاء", + "profile": "الملف الشخصي", + "switchWorkspace": "تبديل مساحة العمل", + "pushNotifications": "الإشعارات الفورية", + "pushNotificationsDescription": "احصل على إشعارات بالرسائل الجديدة على هذا الجهاز.", + "teams": "الفرق", + "noMembers": "لا يوجد أعضاء", + "noTeams": "لا توجد فرق", + "roleOwner": "مالك", + "roleAgent": "وكيل", + "restartRequiredTitle": "إعادة التشغيل مطلوبة", + "restartRequiredMessage": "التبديل إلى هذه اللغة يغيّر اتجاه تخطيط التطبيق. هل تريد إعادة التشغيل الآن لتطبيقه؟", + "restartManuallyMessage": "التبديل إلى هذه اللغة يغيّر اتجاه تخطيط التطبيق. أغلق التطبيق وأعد فتحه لتطبيقه.", + "restartNow": "إعادة التشغيل الآن", + "restartLater": "لاحقًا", + "notifications": "Notifications", + "appearance": "Appearance", + "workspace": "Workspace", + "version": "Version {{version}}" + }, + "profile": { + "plan": "الخطة" + }, + "errors": { + "unauthorized": "انتهت صلاحية جلستك. يرجى تسجيل الدخول مرة أخرى.", + "workspaceBlocked": "مساحة العمل هذه غير متاحة حاليًا.", + "workspaceBlockedTitle": "مساحة العمل غير متاحة", + "workspaceBlockedMac": "وصلت مساحة العمل هذه إلى حد جهات الاتصال في الخطة الحالية. قم بالترقية للمتابعة.", + "workspaceBlockedTrialExpired": "انتهت الفترة التجريبية أو خطة مساحة العمل هذه. قم بالترقية للمتابعة.", + "notFound": "غير موجود", + "notFoundTitle": "الصفحة غير موجودة", + "notFoundBody": "الصفحة التي تبحث عنها غير موجودة.", + "goHome": "الانتقال إلى المحادثات", + "unknown": "حدث خطأ ما. يرجى المحاولة مرة أخرى." + }, + "realtime": { + "online": "Back online", + "connecting": "Connecting…", + "reconnecting": "Reconnecting…", + "offline": "You're offline" + }, + "notifications": { + "androidChannelName": "Default" + } +} diff --git a/src/i18n/locales/da.json b/src/i18n/locales/da.json new file mode 100644 index 0000000..71ec66f --- /dev/null +++ b/src/i18n/locales/da.json @@ -0,0 +1,259 @@ +{ + "common": { + "loading": "Indlæser…", + "error": "Noget gik galt", + "retry": "Prøv igen", + "cancel": "Annuller", + "save": "Gem", + "done": "Færdig", + "search": "Søg", + "confirm": "Bekræft", + "delete": "Slet", + "clear": "Clear", + "close": "Close", + "today": "Today", + "yesterday": "Yesterday", + "removeItem": "Remove {{label}}", + "comingSoon": "Coming soon", + "back": "Back" + }, + "auth": { + "signIn": "Log ind", + "email": "E-mail", + "password": "Adgangskode", + "forgotPassword": "Glemt adgangskode?", + "signOut": "Log ud", + "changePassword": "Skift adgangskode", + "changePasswordRequired": "Du skal angive en ny adgangskode, før du fortsætter.", + "currentPassword": "Nuværende adgangskode", + "newPassword": "Ny adgangskode", + "sendResetLink": "Send nulstillingslink", + "resetLinkSent": "Hvis der findes en konto med denne e-mail, er der sendt et nulstillingslink.", + "orContinueWith": "Eller fortsæt med", + "continueWithGoogle": "Fortsæt med Google", + "continueWithFacebook": "Fortsæt med Facebook", + "socialSignInFailed": "Login blev ikke gennemført. Prøv igen.", + "signOutConfirmTitle": "Sign out?", + "signOutConfirmBody": "You'll need to sign in again to access your workspaces.", + "showPassword": "Show password", + "hidePassword": "Hide password", + "signInSubtitle": "Sign in to keep every conversation moving.", + "forgotPasswordSubtitle": "Enter your email and we'll send you a link to reset your password.", + "backToSignIn": "Back to sign in" + }, + "workspaces": { + "title": "Workspaces", + "empty": "Ingen workspaces endnu", + "current": "Current workspace", + "switch": "Switch", + "switchConfirmTitle": "Switch workspace?", + "switchConfirmBody": "Switch to {{name}}? You'll leave your current workspace." + }, + "inbox": { + "title": "Inbox" + }, + "conversations": { + "title": "Samtaler", + "empty": "Ingen samtaler", + "filters": "Filtre", + "channel": "Kanal", + "handledBy": "Håndteret af", + "status": "Status", + "clearFilters": "Ryd filtre", + "filterAll": "Alle", + "channelMessenger": "Messenger", + "channelInstagram": "Instagram", + "channelWhatsapp": "WhatsApp", + "channelWebchat": "Webchat", + "channelZalo": "Zalo", + "channelTelegram": "Telegram", + "channelEmail": "E-mail", + "channelTiktok": "TikTok", + "statusUnread": "Ulæst", + "statusNoAdminReply": "Intet svar fra teamet", + "statusFollowUp": "Under opfølgning", + "statusArchived": "Arkiveret", + "statusBlocked": "Blokeret", + "handledByBot": "Håndteret af bot", + "handledByHuman": "Håndteret af menneske", + "unassign": "Fjern tildeling", + "noOtherMembers": "Dette workspace har endnu ingen andre medlemmer.", + "unnamed": "Unavngivet", + "unknownContact": "Unknown contact", + "attachmentPreview": "📎 {{count}} attachment(s)", + "a11yUnread": "unread", + "markRead": "Mark as read", + "markUnread": "Mark as unread", + "enableBot": "Enable bot", + "disableBot": "Disable bot", + "archive": "Archive", + "archiveUndone": "Conversation restored", + "undo": "Undo", + "botActive": "Bot handling", + "botPaused": "Bot paused", + "assignedTo": "Assigned to {{name}}", + "quickFilterMine": "Mine", + "emptyFiltered": "No conversations match your filters", + "botOn": "On", + "botOff": "Off", + "channelOmnichannel": "Omnichannel", + "botOnOffLabel": "Bot", + "actions": "Conversation actions", + "follow": "Follow", + "unfollow": "Unfollow", + "assign": "Assign", + "unarchive": "Unarchive", + "archiveConfirm": "Archive this conversation? You can find it again later in archived conversations.", + "unarchiveConfirm": "Restore this conversation from the archive?" + }, + "chat": { + "typeAMessage": "Skriv en besked", + "send": "Send", + "failedToSend": "Afsendelse mislykkedes", + "failedToSendTapRetry": "Afsendelse mislykkedes. Tryk for at prøve igen.", + "retrySend": "Prøv igen", + "noMessages": "Ingen beskeder endnu", + "edit": "Rediger", + "like": "Synes godt om", + "hide": "Skjul", + "delete": "Slet", + "searchSavedReplies": "Søg i gemte svar", + "noSavedReplies": "Ingen gemte svar", + "attachmentTooLarge": "{{fileName}} er større end 5 MB.", + "quotaMac": "Dette workspace har nået sin kontaktgrænse på den nuværende plan. Opgrader for at fortsætte med at sende beskeder.", + "quotaTrialExpired": "Prøveperioden eller planen for dette workspace er udløbet. Opgrader for at fortsætte med at sende beskeder.", + "quotaUnknown": "Dette workspace er i øjeblikket utilgængeligt.", + "dismiss": "Afvis", + "grantCameraPermission": "Kameraadgang er nødvendig for at tage et billede.", + "grantPermission": "Giv tilladelse", + "failedToSendPhoto": "Kunne ikke sende billedet.", + "typing": "Typing…", + "attach": "Attach", + "camera": "Camera", + "photoLibrary": "Photo library", + "document": "Document", + "sendFlow": "Send flow", + "sendSequence": "Send sequence", + "sendingFlow": "Sending {{name}}…", + "flowSendFailed": "Couldn't send flow. Please try again.", + "savedReplies": "Saved replies", + "attachmentsTooLarge": "{{count}} file(s) are larger than 5MB and weren't sent: {{names}}", + "editingMessage": "Editing message", + "replyingTo": "Replying to {{name}}", + "reply": "Reply", + "editAndResend": "Edit & resend", + "repliedAttachment": "Attachment", + "repliedMessageUnavailable": "Original message unavailable", + "messageDeleted": "Message deleted", + "senderAgent": "You", + "senderBot": "Bot", + "senderContact": "Contact", + "scrollToBottom": "Scroll to latest messages", + "bubbleA11yLabel": "{{sender}} said {{text}}, {{time}}, {{status}}", + "status": { + "pending": "Sending", + "sent": "Sent", + "failed": "Failed to send" + }, + "locationShared": "Location shared", + "unsupportedContent": "[{{contentType}}] Unsupported on mobile", + "activityFallback": "Activity" + }, + "contacts": { + "title": "Kontakter", + "contactTitle": "Kontakt", + "empty": "Ingen kontakter", + "unknown": "Ukendt", + "blocked": "Blokeret", + "email": "E-mail", + "phone": "Telefon", + "locale": "Sprog", + "timezone": "Tidszone", + "location": "Placering", + "customFields": "Brugerdefinerede felter", + "addCustomField": "Tilføj brugerdefineret felt", + "assignedTags": "Tags", + "noTags": "Ingen tags", + "addTag": "Tilføj tag", + "noSequences": "Ikke tilmeldt nogen sekvens", + "sequenceStep": "Trin {{step}}", + "completed": "Afsluttet", + "active": "Aktiv", + "tabInfo": "Info", + "tabTags": "Tags", + "tabSequences": "Sekvenser", + "tabNotes": "Notes", + "noNotes": "No notes yet", + "unknownAuthor": "Unknown", + "message": "Message", + "assign": "Assign", + "addToSequence": "Add to sequence", + "noActiveSequences": "No active sequences", + "sequenceSubscribers": "{{count}} subscriber", + "sequenceSubscribers_other": "{{count}} subscribers", + "enrollSelected": "Add ({{count}})", + "sequenceEnrolled": "Contact enrolled in sequence", + "sequenceEnrollFailed": "Couldn't enroll contact in sequence" + }, + "flows": { + "title": "Flows", + "empty": "Ingen flows", + "readOnlyNote": "Flows er skrivebeskyttede på mobil.", + "active": "Aktiv", + "inactive": "Inaktiv", + "nodes": "{{count}} knude", + "nodes_other": "{{count}} knuder" + }, + "settings": { + "title": "Indstillinger", + "darkMode": "Mørk tilstand", + "theme": "Tema", + "themeSystem": "System", + "themeLight": "Lys", + "themeDark": "Mørk", + "language": "Sprog", + "members": "Medlemmer", + "profile": "Profil", + "switchWorkspace": "Skift workspace", + "pushNotifications": "Push-notifikationer", + "pushNotificationsDescription": "Få besked om nye beskeder på denne enhed.", + "teams": "Teams", + "noMembers": "Ingen medlemmer", + "noTeams": "Ingen teams", + "roleOwner": "Ejer", + "roleAgent": "Agent", + "restartRequiredTitle": "Genstart påkrævet", + "restartRequiredMessage": "Skift til dette sprog ændrer appens layoutretning. Genstart nu for at anvende det?", + "restartManuallyMessage": "Skift til dette sprog ændrer appens layoutretning. Luk og genåbn appen for at anvende det.", + "restartNow": "Genstart nu", + "restartLater": "Senere", + "notifications": "Notifications", + "appearance": "Appearance", + "workspace": "Workspace", + "version": "Version {{version}}" + }, + "profile": { + "plan": "Plan" + }, + "errors": { + "unauthorized": "Din session er udløbet. Log ind igen.", + "workspaceBlocked": "Dette workspace er i øjeblikket utilgængeligt.", + "workspaceBlockedTitle": "Workspace utilgængeligt", + "workspaceBlockedMac": "Dette workspace har nået sin kontaktgrænse på den nuværende plan. Opgrader for at fortsætte.", + "workspaceBlockedTrialExpired": "Prøveperioden eller planen for dette workspace er udløbet. Opgrader for at fortsætte.", + "notFound": "Ikke fundet", + "notFoundTitle": "Siden blev ikke fundet", + "notFoundBody": "Siden, du leder efter, findes ikke.", + "goHome": "Gå til samtaler", + "unknown": "Noget gik galt. Prøv igen." + }, + "realtime": { + "online": "Back online", + "connecting": "Connecting…", + "reconnecting": "Reconnecting…", + "offline": "You're offline" + }, + "notifications": { + "androidChannelName": "Default" + } +} diff --git a/src/i18n/locales/de.json b/src/i18n/locales/de.json new file mode 100644 index 0000000..14afe67 --- /dev/null +++ b/src/i18n/locales/de.json @@ -0,0 +1,259 @@ +{ + "common": { + "loading": "Wird geladen…", + "error": "Etwas ist schiefgelaufen", + "retry": "Erneut versuchen", + "cancel": "Abbrechen", + "save": "Speichern", + "done": "Fertig", + "search": "Suchen", + "confirm": "Bestätigen", + "delete": "Löschen", + "clear": "Clear", + "close": "Close", + "today": "Today", + "yesterday": "Yesterday", + "removeItem": "Remove {{label}}", + "comingSoon": "Coming soon", + "back": "Back" + }, + "auth": { + "signIn": "Anmelden", + "email": "E-Mail", + "password": "Passwort", + "forgotPassword": "Passwort vergessen?", + "signOut": "Abmelden", + "changePassword": "Passwort ändern", + "changePasswordRequired": "Sie müssen ein neues Passwort festlegen, bevor Sie fortfahren.", + "currentPassword": "Aktuelles Passwort", + "newPassword": "Neues Passwort", + "sendResetLink": "Link zum Zurücksetzen senden", + "resetLinkSent": "Falls ein Konto mit dieser E-Mail existiert, wurde ein Link zum Zurücksetzen gesendet.", + "orContinueWith": "Oder fortfahren mit", + "continueWithGoogle": "Mit Google fortfahren", + "continueWithFacebook": "Mit Facebook fortfahren", + "socialSignInFailed": "Anmeldung nicht abgeschlossen. Bitte versuchen Sie es erneut.", + "signOutConfirmTitle": "Sign out?", + "signOutConfirmBody": "You'll need to sign in again to access your workspaces.", + "showPassword": "Show password", + "hidePassword": "Hide password", + "signInSubtitle": "Sign in to keep every conversation moving.", + "forgotPasswordSubtitle": "Enter your email and we'll send you a link to reset your password.", + "backToSignIn": "Back to sign in" + }, + "workspaces": { + "title": "Workspaces", + "empty": "Noch keine Workspaces", + "current": "Current workspace", + "switch": "Switch", + "switchConfirmTitle": "Switch workspace?", + "switchConfirmBody": "Switch to {{name}}? You'll leave your current workspace." + }, + "inbox": { + "title": "Inbox" + }, + "conversations": { + "title": "Unterhaltungen", + "empty": "Keine Unterhaltungen", + "filters": "Filter", + "channel": "Kanal", + "handledBy": "Bearbeitet von", + "status": "Status", + "clearFilters": "Filter zurücksetzen", + "filterAll": "Alle", + "channelMessenger": "Messenger", + "channelInstagram": "Instagram", + "channelWhatsapp": "WhatsApp", + "channelWebchat": "Webchat", + "channelZalo": "Zalo", + "channelTelegram": "Telegram", + "channelEmail": "E-Mail", + "channelTiktok": "TikTok", + "statusUnread": "Ungelesen", + "statusNoAdminReply": "Keine Antwort vom Team", + "statusFollowUp": "In Nachverfolgung", + "statusArchived": "Archiviert", + "statusBlocked": "Blockiert", + "handledByBot": "Bot übernimmt", + "handledByHuman": "Mensch übernimmt", + "unassign": "Zuweisung aufheben", + "noOtherMembers": "Dieser Workspace hat noch keine weiteren Mitglieder.", + "unnamed": "Unbenannt", + "unknownContact": "Unknown contact", + "attachmentPreview": "📎 {{count}} attachment(s)", + "a11yUnread": "unread", + "markRead": "Mark as read", + "markUnread": "Mark as unread", + "enableBot": "Enable bot", + "disableBot": "Disable bot", + "archive": "Archive", + "archiveUndone": "Conversation restored", + "undo": "Undo", + "botActive": "Bot handling", + "botPaused": "Bot paused", + "assignedTo": "Assigned to {{name}}", + "quickFilterMine": "Mine", + "emptyFiltered": "No conversations match your filters", + "botOn": "On", + "botOff": "Off", + "channelOmnichannel": "Omnichannel", + "botOnOffLabel": "Bot", + "actions": "Conversation actions", + "follow": "Follow", + "unfollow": "Unfollow", + "assign": "Assign", + "unarchive": "Unarchive", + "archiveConfirm": "Archive this conversation? You can find it again later in archived conversations.", + "unarchiveConfirm": "Restore this conversation from the archive?" + }, + "chat": { + "typeAMessage": "Nachricht eingeben", + "send": "Senden", + "failedToSend": "Senden fehlgeschlagen", + "failedToSendTapRetry": "Senden fehlgeschlagen. Zum Wiederholen tippen.", + "retrySend": "Erneut versuchen", + "noMessages": "Noch keine Nachrichten", + "edit": "Bearbeiten", + "like": "Gefällt mir", + "hide": "Ausblenden", + "delete": "Löschen", + "searchSavedReplies": "Gespeicherte Antworten durchsuchen", + "noSavedReplies": "Keine gespeicherten Antworten", + "attachmentTooLarge": "{{fileName}} ist größer als 5 MB.", + "quotaMac": "Dieser Workspace hat sein Kontaktlimit im aktuellen Plan erreicht. Upgraden, um weiter zu senden.", + "quotaTrialExpired": "Die Testphase oder der Plan dieses Workspace ist abgelaufen. Upgraden, um weiter zu senden.", + "quotaUnknown": "Dieser Workspace ist derzeit nicht verfügbar.", + "dismiss": "Verwerfen", + "grantCameraPermission": "Für ein Foto ist Kamerazugriff erforderlich.", + "grantPermission": "Zugriff erlauben", + "failedToSendPhoto": "Foto konnte nicht gesendet werden.", + "typing": "Typing…", + "attach": "Attach", + "camera": "Camera", + "photoLibrary": "Photo library", + "document": "Document", + "sendFlow": "Send flow", + "sendSequence": "Send sequence", + "sendingFlow": "Sending {{name}}…", + "flowSendFailed": "Couldn't send flow. Please try again.", + "savedReplies": "Saved replies", + "attachmentsTooLarge": "{{count}} file(s) are larger than 5MB and weren't sent: {{names}}", + "editingMessage": "Editing message", + "replyingTo": "Replying to {{name}}", + "reply": "Reply", + "editAndResend": "Edit & resend", + "repliedAttachment": "Attachment", + "repliedMessageUnavailable": "Original message unavailable", + "messageDeleted": "Message deleted", + "senderAgent": "You", + "senderBot": "Bot", + "senderContact": "Contact", + "scrollToBottom": "Scroll to latest messages", + "bubbleA11yLabel": "{{sender}} said {{text}}, {{time}}, {{status}}", + "status": { + "pending": "Sending", + "sent": "Sent", + "failed": "Failed to send" + }, + "locationShared": "Location shared", + "unsupportedContent": "[{{contentType}}] Unsupported on mobile", + "activityFallback": "Activity" + }, + "contacts": { + "title": "Kontakte", + "contactTitle": "Kontakt", + "empty": "Keine Kontakte", + "unknown": "Unbekannt", + "blocked": "Blockiert", + "email": "E-Mail", + "phone": "Telefon", + "locale": "Sprache", + "timezone": "Zeitzone", + "location": "Standort", + "customFields": "Benutzerdefinierte Felder", + "addCustomField": "Feld hinzufügen", + "assignedTags": "Tags", + "noTags": "Keine Tags", + "addTag": "Tag hinzufügen", + "noSequences": "In keine Sequenz eingeschrieben", + "sequenceStep": "Schritt {{step}}", + "completed": "Abgeschlossen", + "active": "Aktiv", + "tabInfo": "Info", + "tabTags": "Tags", + "tabSequences": "Sequenzen", + "tabNotes": "Notes", + "noNotes": "No notes yet", + "unknownAuthor": "Unknown", + "message": "Message", + "assign": "Assign", + "addToSequence": "Add to sequence", + "noActiveSequences": "No active sequences", + "sequenceSubscribers": "{{count}} subscriber", + "sequenceSubscribers_other": "{{count}} subscribers", + "enrollSelected": "Add ({{count}})", + "sequenceEnrolled": "Contact enrolled in sequence", + "sequenceEnrollFailed": "Couldn't enroll contact in sequence" + }, + "flows": { + "title": "Flows", + "empty": "Keine Flows", + "readOnlyNote": "Flows sind auf Mobilgeräten schreibgeschützt.", + "active": "Aktiv", + "inactive": "Inaktiv", + "nodes": "{{count}} Knoten", + "nodes_other": "{{count}} Knoten" + }, + "settings": { + "title": "Einstellungen", + "darkMode": "Dunkelmodus", + "theme": "Design", + "themeSystem": "System", + "themeLight": "Hell", + "themeDark": "Dunkel", + "language": "Sprache", + "members": "Mitglieder", + "profile": "Profil", + "switchWorkspace": "Workspace wechseln", + "pushNotifications": "Push-Benachrichtigungen", + "pushNotificationsDescription": "Erhalten Sie Benachrichtigungen über neue Nachrichten auf diesem Gerät.", + "teams": "Teams", + "noMembers": "Keine Mitglieder", + "noTeams": "Keine Teams", + "roleOwner": "Inhaber", + "roleAgent": "Agent", + "restartRequiredTitle": "Neustart erforderlich", + "restartRequiredMessage": "Der Wechsel zu dieser Sprache ändert die Layoutrichtung der App. Jetzt neu starten, um sie anzuwenden?", + "restartManuallyMessage": "Der Wechsel zu dieser Sprache ändert die Layoutrichtung der App. Schließen und öffnen Sie die App erneut, um sie anzuwenden.", + "restartNow": "Jetzt neu starten", + "restartLater": "Später", + "notifications": "Notifications", + "appearance": "Appearance", + "workspace": "Workspace", + "version": "Version {{version}}" + }, + "profile": { + "plan": "Plan" + }, + "errors": { + "unauthorized": "Ihre Sitzung ist abgelaufen. Bitte melden Sie sich erneut an.", + "workspaceBlocked": "Dieser Workspace ist derzeit nicht verfügbar.", + "workspaceBlockedTitle": "Workspace nicht verfügbar", + "workspaceBlockedMac": "Dieser Workspace hat sein Kontaktlimit im aktuellen Plan erreicht. Upgraden, um fortzufahren.", + "workspaceBlockedTrialExpired": "Die Testphase oder der Plan dieses Workspace ist abgelaufen. Upgraden, um fortzufahren.", + "notFound": "Nicht gefunden", + "notFoundTitle": "Seite nicht gefunden", + "notFoundBody": "Die gesuchte Seite existiert nicht.", + "goHome": "Zu den Unterhaltungen", + "unknown": "Etwas ist schiefgelaufen. Bitte versuchen Sie es erneut." + }, + "realtime": { + "online": "Back online", + "connecting": "Connecting…", + "reconnecting": "Reconnecting…", + "offline": "You're offline" + }, + "notifications": { + "androidChannelName": "Default" + } +} diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json new file mode 100644 index 0000000..15c1a07 --- /dev/null +++ b/src/i18n/locales/en.json @@ -0,0 +1,259 @@ +{ + "common": { + "loading": "Loading…", + "error": "Something went wrong", + "retry": "Retry", + "cancel": "Cancel", + "save": "Save", + "done": "Done", + "search": "Search", + "confirm": "Confirm", + "delete": "Delete", + "clear": "Clear", + "close": "Close", + "back": "Back", + "today": "Today", + "yesterday": "Yesterday", + "removeItem": "Remove {{label}}", + "comingSoon": "Coming soon" + }, + "realtime": { + "online": "Back online", + "connecting": "Connecting…", + "reconnecting": "Reconnecting…", + "offline": "You're offline" + }, + "auth": { + "signIn": "Sign in", + "email": "Email", + "password": "Password", + "forgotPassword": "Forgot password?", + "signOut": "Sign out", + "changePassword": "Change password", + "changePasswordRequired": "You need to set a new password before continuing.", + "currentPassword": "Current password", + "newPassword": "New password", + "sendResetLink": "Send reset link", + "resetLinkSent": "If an account exists for that email, a reset link has been sent.", + "orContinueWith": "Or continue with", + "continueWithGoogle": "Continue with Google", + "continueWithFacebook": "Continue with Facebook", + "socialSignInFailed": "Sign-in didn't complete. Please try again.", + "signOutConfirmTitle": "Sign out?", + "signOutConfirmBody": "You'll need to sign in again to access your workspaces.", + "showPassword": "Show password", + "hidePassword": "Hide password", + "signInSubtitle": "Sign in to keep every conversation moving.", + "forgotPasswordSubtitle": "Enter your email and we'll send you a link to reset your password.", + "backToSignIn": "Back to sign in" + }, + "workspaces": { + "title": "Workspaces", + "empty": "No workspaces yet", + "current": "Current workspace", + "switch": "Switch", + "switchConfirmTitle": "Switch workspace?", + "switchConfirmBody": "Switch to {{name}}? You'll leave your current workspace." + }, + "inbox": { + "title": "Inbox" + }, + "conversations": { + "title": "Conversations", + "empty": "No conversations", + "filters": "Filters", + "channel": "Channel", + "handledBy": "Handled by", + "status": "Status", + "clearFilters": "Clear filters", + "filterAll": "All", + "channelMessenger": "Messenger", + "channelInstagram": "Instagram", + "channelWhatsapp": "WhatsApp", + "channelWebchat": "Webchat", + "channelZalo": "Zalo", + "channelTelegram": "Telegram", + "channelEmail": "Email", + "channelTiktok": "TikTok", + "statusUnread": "Unread", + "statusNoAdminReply": "No admin reply", + "statusFollowUp": "Following", + "statusArchived": "Archived", + "statusBlocked": "Blocked", + "handledByBot": "Bot handling", + "handledByHuman": "Human handling", + "unassign": "Unassign", + "noOtherMembers": "This workspace has no other members yet.", + "unnamed": "Unnamed", + "unknownContact": "Unknown contact", + "attachmentPreview": "📎 {{count}} attachment(s)", + "a11yUnread": "unread", + "markRead": "Mark as read", + "markUnread": "Mark as unread", + "enableBot": "Enable bot", + "disableBot": "Disable bot", + "archive": "Archive", + "archiveUndone": "Conversation restored", + "undo": "Undo", + "botActive": "Bot handling", + "botPaused": "Bot paused", + "assignedTo": "Assigned to {{name}}", + "quickFilterMine": "Mine", + "emptyFiltered": "No conversations match your filters", + "botOn": "On", + "botOff": "Off", + "channelOmnichannel": "Omnichannel", + "botOnOffLabel": "Bot", + "actions": "Conversation actions", + "follow": "Follow", + "unfollow": "Unfollow", + "assign": "Assign", + "unarchive": "Unarchive", + "archiveConfirm": "Archive this conversation? You can find it again later in archived conversations.", + "unarchiveConfirm": "Restore this conversation from the archive?" + }, + "chat": { + "typeAMessage": "Type a message", + "send": "Send", + "failedToSend": "Failed to send", + "failedToSendTapRetry": "Failed to send. Tap to retry.", + "retrySend": "Retry", + "noMessages": "No messages yet", + "edit": "Edit", + "like": "Like", + "hide": "Hide", + "delete": "Delete", + "searchSavedReplies": "Search saved replies", + "noSavedReplies": "No saved replies", + "attachmentTooLarge": "{{fileName}} is larger than 5MB.", + "quotaMac": "This workspace has reached its contact limit on the current plan. Upgrade to keep messaging.", + "quotaTrialExpired": "This workspace's trial or plan has expired. Upgrade to keep messaging.", + "quotaUnknown": "This workspace is currently unavailable.", + "dismiss": "Dismiss", + "grantCameraPermission": "Camera access is required to take a photo.", + "grantPermission": "Grant permission", + "failedToSendPhoto": "Failed to send photo.", + "typing": "Typing…", + "attach": "Attach", + "camera": "Camera", + "photoLibrary": "Photo library", + "document": "Document", + "sendFlow": "Send flow", + "sendSequence": "Send sequence", + "sendingFlow": "Sending {{name}}…", + "flowSendFailed": "Couldn't send flow. Please try again.", + "savedReplies": "Saved replies", + "attachmentsTooLarge": "{{count}} file(s) are larger than 5MB and weren't sent: {{names}}", + "editingMessage": "Editing message", + "replyingTo": "Replying to {{name}}", + "reply": "Reply", + "editAndResend": "Edit & resend", + "repliedAttachment": "Attachment", + "repliedMessageUnavailable": "Original message unavailable", + "messageDeleted": "Message deleted", + "senderAgent": "You", + "senderBot": "Bot", + "senderContact": "Contact", + "scrollToBottom": "Scroll to latest messages", + "bubbleA11yLabel": "{{sender}} said {{text}}, {{time}}, {{status}}", + "locationShared": "Location shared", + "unsupportedContent": "[{{contentType}}] Unsupported on mobile", + "activityFallback": "Activity", + "status": { + "pending": "Sending", + "sent": "Sent", + "failed": "Failed to send" + } + }, + "contacts": { + "title": "Contacts", + "contactTitle": "Contact", + "empty": "No contacts", + "unknown": "Unknown", + "blocked": "Blocked", + "email": "Email", + "phone": "Phone", + "locale": "Locale", + "timezone": "Timezone", + "location": "Location", + "customFields": "Custom fields", + "addCustomField": "Add custom field", + "assignedTags": "Tags", + "noTags": "No tags", + "addTag": "Add a tag", + "noSequences": "Not enrolled in any sequences", + "sequenceStep": "Step {{step}}", + "completed": "Completed", + "active": "Active", + "tabInfo": "Info", + "tabTags": "Tags", + "tabSequences": "Sequences", + "tabNotes": "Notes", + "noNotes": "No notes yet", + "unknownAuthor": "Unknown", + "message": "Message", + "assign": "Assign", + "addToSequence": "Add to sequence", + "noActiveSequences": "No active sequences", + "sequenceSubscribers": "{{count}} subscriber", + "sequenceSubscribers_other": "{{count}} subscribers", + "enrollSelected": "Add ({{count}})", + "sequenceEnrolled": "Contact enrolled in sequence", + "sequenceEnrollFailed": "Couldn't enroll contact in sequence" + }, + "flows": { + "title": "Flows", + "empty": "No flows", + "readOnlyNote": "Flows are read-only on mobile.", + "active": "Active", + "inactive": "Inactive", + "nodes": "{{count}} node", + "nodes_other": "{{count}} nodes" + }, + "settings": { + "title": "Settings", + "darkMode": "Dark mode", + "theme": "Theme", + "themeSystem": "System", + "themeLight": "Light", + "themeDark": "Dark", + "language": "Language", + "members": "Members", + "profile": "Profile", + "switchWorkspace": "Switch workspace", + "pushNotifications": "Push notifications", + "pushNotificationsDescription": "Get notified about new messages on this device.", + "teams": "Teams", + "noMembers": "No members", + "noTeams": "No teams", + "roleOwner": "Owner", + "roleAgent": "Agent", + "restartRequiredTitle": "Restart required", + "restartRequiredMessage": "Switching to this language changes the app's layout direction. Restart now to apply it?", + "restartManuallyMessage": "Switching to this language changes the app's layout direction. Close and reopen the app to apply it.", + "restartNow": "Restart now", + "restartLater": "Later", + "notifications": "Notifications", + "appearance": "Appearance", + "workspace": "Workspace", + "version": "Version {{version}}" + }, + "profile": { + "plan": "Plan" + }, + "notifications": { + "androidChannelName": "Default" + }, + "errors": { + "unauthorized": "Your session has expired. Please sign in again.", + "workspaceBlocked": "This workspace is currently unavailable.", + "workspaceBlockedTitle": "Workspace unavailable", + "workspaceBlockedMac": "This workspace has reached its contact limit on the current plan. Upgrade to continue.", + "workspaceBlockedTrialExpired": "This workspace's trial or plan has expired. Upgrade to continue.", + "notFound": "Not found", + "notFoundTitle": "Page not found", + "notFoundBody": "The page you're looking for doesn't exist.", + "goHome": "Go to conversations", + "unknown": "Something went wrong. Please try again." + } +} diff --git a/src/i18n/locales/es.json b/src/i18n/locales/es.json new file mode 100644 index 0000000..be0699b --- /dev/null +++ b/src/i18n/locales/es.json @@ -0,0 +1,259 @@ +{ + "common": { + "loading": "Cargando…", + "error": "Algo salió mal", + "retry": "Reintentar", + "cancel": "Cancelar", + "save": "Guardar", + "done": "Listo", + "search": "Buscar", + "confirm": "Confirmar", + "delete": "Eliminar", + "clear": "Clear", + "close": "Close", + "today": "Today", + "yesterday": "Yesterday", + "removeItem": "Remove {{label}}", + "comingSoon": "Coming soon", + "back": "Back" + }, + "auth": { + "signIn": "Iniciar sesión", + "email": "Correo electrónico", + "password": "Contraseña", + "forgotPassword": "¿Olvidaste tu contraseña?", + "signOut": "Cerrar sesión", + "changePassword": "Cambiar contraseña", + "changePasswordRequired": "Debes establecer una nueva contraseña antes de continuar.", + "currentPassword": "Contraseña actual", + "newPassword": "Nueva contraseña", + "sendResetLink": "Enviar enlace de restablecimiento", + "resetLinkSent": "Si existe una cuenta con ese correo, se ha enviado un enlace de restablecimiento.", + "orContinueWith": "O continúa con", + "continueWithGoogle": "Continuar con Google", + "continueWithFacebook": "Continuar con Facebook", + "socialSignInFailed": "El inicio de sesión no se completó. Inténtalo de nuevo.", + "signOutConfirmTitle": "Sign out?", + "signOutConfirmBody": "You'll need to sign in again to access your workspaces.", + "showPassword": "Show password", + "hidePassword": "Hide password", + "signInSubtitle": "Sign in to keep every conversation moving.", + "forgotPasswordSubtitle": "Enter your email and we'll send you a link to reset your password.", + "backToSignIn": "Back to sign in" + }, + "workspaces": { + "title": "Espacios de trabajo", + "empty": "Aún no hay espacios de trabajo", + "current": "Current workspace", + "switch": "Switch", + "switchConfirmTitle": "Switch workspace?", + "switchConfirmBody": "Switch to {{name}}? You'll leave your current workspace." + }, + "inbox": { + "title": "Inbox" + }, + "conversations": { + "title": "Conversaciones", + "empty": "No hay conversaciones", + "filters": "Filtros", + "channel": "Canal", + "handledBy": "Atendido por", + "status": "Estado", + "clearFilters": "Borrar filtros", + "filterAll": "Todos", + "channelMessenger": "Messenger", + "channelInstagram": "Instagram", + "channelWhatsapp": "WhatsApp", + "channelWebchat": "Chat web", + "channelZalo": "Zalo", + "channelTelegram": "Telegram", + "channelEmail": "Correo electrónico", + "channelTiktok": "TikTok", + "statusUnread": "No leído", + "statusNoAdminReply": "Sin respuesta del equipo", + "statusFollowUp": "En seguimiento", + "statusArchived": "Archivado", + "statusBlocked": "Bloqueado", + "handledByBot": "Atendido por el bot", + "handledByHuman": "Atendido por una persona", + "unassign": "Quitar asignación", + "noOtherMembers": "Este espacio de trabajo aún no tiene otros miembros.", + "unnamed": "Sin nombre", + "unknownContact": "Unknown contact", + "attachmentPreview": "📎 {{count}} attachment(s)", + "a11yUnread": "unread", + "markRead": "Mark as read", + "markUnread": "Mark as unread", + "enableBot": "Enable bot", + "disableBot": "Disable bot", + "archive": "Archive", + "archiveUndone": "Conversation restored", + "undo": "Undo", + "botActive": "Bot handling", + "botPaused": "Bot paused", + "assignedTo": "Assigned to {{name}}", + "quickFilterMine": "Mine", + "emptyFiltered": "No conversations match your filters", + "botOn": "On", + "botOff": "Off", + "channelOmnichannel": "Omnichannel", + "botOnOffLabel": "Bot", + "actions": "Conversation actions", + "follow": "Follow", + "unfollow": "Unfollow", + "assign": "Assign", + "unarchive": "Unarchive", + "archiveConfirm": "Archive this conversation? You can find it again later in archived conversations.", + "unarchiveConfirm": "Restore this conversation from the archive?" + }, + "chat": { + "typeAMessage": "Escribe un mensaje", + "send": "Enviar", + "failedToSend": "Error al enviar", + "failedToSendTapRetry": "Error al enviar. Toca para reintentar.", + "retrySend": "Reintentar", + "noMessages": "Aún no hay mensajes", + "edit": "Editar", + "like": "Me gusta", + "hide": "Ocultar", + "delete": "Eliminar", + "searchSavedReplies": "Buscar respuestas guardadas", + "noSavedReplies": "No hay respuestas guardadas", + "attachmentTooLarge": "{{fileName}} supera los 5 MB.", + "quotaMac": "Este espacio de trabajo alcanzó su límite de contactos en el plan actual. Mejora tu plan para seguir enviando mensajes.", + "quotaTrialExpired": "La prueba o el plan de este espacio de trabajo ha vencido. Mejora tu plan para seguir enviando mensajes.", + "quotaUnknown": "Este espacio de trabajo no está disponible actualmente.", + "dismiss": "Descartar", + "grantCameraPermission": "Se requiere acceso a la cámara para tomar una foto.", + "grantPermission": "Conceder permiso", + "failedToSendPhoto": "No se pudo enviar la foto.", + "typing": "Typing…", + "attach": "Attach", + "camera": "Camera", + "photoLibrary": "Photo library", + "document": "Document", + "sendFlow": "Send flow", + "sendSequence": "Send sequence", + "sendingFlow": "Sending {{name}}…", + "flowSendFailed": "Couldn't send flow. Please try again.", + "savedReplies": "Saved replies", + "attachmentsTooLarge": "{{count}} file(s) are larger than 5MB and weren't sent: {{names}}", + "editingMessage": "Editing message", + "replyingTo": "Replying to {{name}}", + "reply": "Reply", + "editAndResend": "Edit & resend", + "repliedAttachment": "Attachment", + "repliedMessageUnavailable": "Original message unavailable", + "messageDeleted": "Message deleted", + "senderAgent": "You", + "senderBot": "Bot", + "senderContact": "Contact", + "scrollToBottom": "Scroll to latest messages", + "bubbleA11yLabel": "{{sender}} said {{text}}, {{time}}, {{status}}", + "status": { + "pending": "Sending", + "sent": "Sent", + "failed": "Failed to send" + }, + "locationShared": "Location shared", + "unsupportedContent": "[{{contentType}}] Unsupported on mobile", + "activityFallback": "Activity" + }, + "contacts": { + "title": "Contactos", + "contactTitle": "Contacto", + "empty": "No hay contactos", + "unknown": "Desconocido", + "blocked": "Bloqueado", + "email": "Correo electrónico", + "phone": "Teléfono", + "locale": "Idioma", + "timezone": "Zona horaria", + "location": "Ubicación", + "customFields": "Campos personalizados", + "addCustomField": "Añadir campo personalizado", + "assignedTags": "Etiquetas", + "noTags": "Sin etiquetas", + "addTag": "Añadir etiqueta", + "noSequences": "No está inscrito en ninguna secuencia", + "sequenceStep": "Paso {{step}}", + "completed": "Completado", + "active": "Activo", + "tabInfo": "Información", + "tabTags": "Etiquetas", + "tabSequences": "Secuencias", + "tabNotes": "Notes", + "noNotes": "No notes yet", + "unknownAuthor": "Unknown", + "message": "Message", + "assign": "Assign", + "addToSequence": "Add to sequence", + "noActiveSequences": "No active sequences", + "sequenceSubscribers": "{{count}} subscriber", + "sequenceSubscribers_other": "{{count}} subscribers", + "enrollSelected": "Add ({{count}})", + "sequenceEnrolled": "Contact enrolled in sequence", + "sequenceEnrollFailed": "Couldn't enroll contact in sequence" + }, + "flows": { + "title": "Flujos", + "empty": "No hay flujos", + "readOnlyNote": "Los flujos son de solo lectura en el móvil.", + "active": "Activo", + "inactive": "Inactivo", + "nodes": "{{count}} nodo", + "nodes_other": "{{count}} nodos" + }, + "settings": { + "title": "Configuración", + "darkMode": "Modo oscuro", + "theme": "Tema", + "themeSystem": "Sistema", + "themeLight": "Claro", + "themeDark": "Oscuro", + "language": "Idioma", + "members": "Miembros", + "profile": "Perfil", + "switchWorkspace": "Cambiar espacio de trabajo", + "pushNotifications": "Notificaciones push", + "pushNotificationsDescription": "Recibe notificaciones de mensajes nuevos en este dispositivo.", + "teams": "Equipos", + "noMembers": "Sin miembros", + "noTeams": "Sin equipos", + "roleOwner": "Propietario", + "roleAgent": "Agente", + "restartRequiredTitle": "Reinicio requerido", + "restartRequiredMessage": "Cambiar a este idioma cambia la dirección del diseño de la app. ¿Reiniciar ahora para aplicarlo?", + "restartManuallyMessage": "Cambiar a este idioma cambia la dirección del diseño de la app. Cierra y vuelve a abrir la app para aplicarlo.", + "restartNow": "Reiniciar ahora", + "restartLater": "Más tarde", + "notifications": "Notifications", + "appearance": "Appearance", + "workspace": "Workspace", + "version": "Version {{version}}" + }, + "profile": { + "plan": "Plan" + }, + "errors": { + "unauthorized": "Tu sesión ha expirado. Vuelve a iniciar sesión.", + "workspaceBlocked": "Este espacio de trabajo no está disponible actualmente.", + "workspaceBlockedTitle": "Espacio de trabajo no disponible", + "workspaceBlockedMac": "Este espacio de trabajo alcanzó su límite de contactos en el plan actual. Mejora tu plan para continuar.", + "workspaceBlockedTrialExpired": "La prueba o el plan de este espacio de trabajo ha vencido. Mejora tu plan para continuar.", + "notFound": "No encontrado", + "notFoundTitle": "Página no encontrada", + "notFoundBody": "La página que buscas no existe.", + "goHome": "Ir a conversaciones", + "unknown": "Algo salió mal. Inténtalo de nuevo." + }, + "realtime": { + "online": "Back online", + "connecting": "Connecting…", + "reconnecting": "Reconnecting…", + "offline": "You're offline" + }, + "notifications": { + "androidChannelName": "Default" + } +} diff --git a/src/i18n/locales/fi.json b/src/i18n/locales/fi.json new file mode 100644 index 0000000..be669f8 --- /dev/null +++ b/src/i18n/locales/fi.json @@ -0,0 +1,259 @@ +{ + "common": { + "loading": "Ladataan…", + "error": "Jokin meni pieleen", + "retry": "Yritä uudelleen", + "cancel": "Peruuta", + "save": "Tallenna", + "done": "Valmis", + "search": "Haku", + "confirm": "Vahvista", + "delete": "Poista", + "clear": "Clear", + "close": "Close", + "today": "Today", + "yesterday": "Yesterday", + "removeItem": "Remove {{label}}", + "comingSoon": "Coming soon", + "back": "Back" + }, + "auth": { + "signIn": "Kirjaudu sisään", + "email": "Sähköposti", + "password": "Salasana", + "forgotPassword": "Unohditko salasanan?", + "signOut": "Kirjaudu ulos", + "changePassword": "Vaihda salasana", + "changePasswordRequired": "Sinun täytyy asettaa uusi salasana ennen kuin voit jatkaa.", + "currentPassword": "Nykyinen salasana", + "newPassword": "Uusi salasana", + "sendResetLink": "Lähetä palautuslinkki", + "resetLinkSent": "Jos tälle sähköpostille on tili, palautuslinkki on lähetetty.", + "orContinueWith": "Tai jatka käyttäen", + "continueWithGoogle": "Jatka Googlella", + "continueWithFacebook": "Jatka Facebookilla", + "socialSignInFailed": "Kirjautuminen ei onnistunut. Yritä uudelleen.", + "signOutConfirmTitle": "Sign out?", + "signOutConfirmBody": "You'll need to sign in again to access your workspaces.", + "showPassword": "Show password", + "hidePassword": "Hide password", + "signInSubtitle": "Sign in to keep every conversation moving.", + "forgotPasswordSubtitle": "Enter your email and we'll send you a link to reset your password.", + "backToSignIn": "Back to sign in" + }, + "workspaces": { + "title": "Työtilat", + "empty": "Ei vielä työtiloja", + "current": "Current workspace", + "switch": "Switch", + "switchConfirmTitle": "Switch workspace?", + "switchConfirmBody": "Switch to {{name}}? You'll leave your current workspace." + }, + "inbox": { + "title": "Inbox" + }, + "conversations": { + "title": "Keskustelut", + "empty": "Ei keskusteluja", + "filters": "Suodattimet", + "channel": "Kanava", + "handledBy": "Käsittelijä", + "status": "Tila", + "clearFilters": "Tyhjennä suodattimet", + "filterAll": "Kaikki", + "channelMessenger": "Messenger", + "channelInstagram": "Instagram", + "channelWhatsapp": "WhatsApp", + "channelWebchat": "Verkkochat", + "channelZalo": "Zalo", + "channelTelegram": "Telegram", + "channelEmail": "Sähköposti", + "channelTiktok": "TikTok", + "statusUnread": "Lukematon", + "statusNoAdminReply": "Ei vastausta tiimiltä", + "statusFollowUp": "Seurannassa", + "statusArchived": "Arkistoitu", + "statusBlocked": "Estetty", + "handledByBot": "Botti käsittelee", + "handledByHuman": "Ihminen käsittelee", + "unassign": "Poista vastuuhenkilö", + "noOtherMembers": "Tässä työtilassa ei vielä ole muita jäseniä.", + "unnamed": "Nimetön", + "unknownContact": "Unknown contact", + "attachmentPreview": "📎 {{count}} attachment(s)", + "a11yUnread": "unread", + "markRead": "Mark as read", + "markUnread": "Mark as unread", + "enableBot": "Enable bot", + "disableBot": "Disable bot", + "archive": "Archive", + "archiveUndone": "Conversation restored", + "undo": "Undo", + "botActive": "Bot handling", + "botPaused": "Bot paused", + "assignedTo": "Assigned to {{name}}", + "quickFilterMine": "Mine", + "emptyFiltered": "No conversations match your filters", + "botOn": "On", + "botOff": "Off", + "channelOmnichannel": "Omnichannel", + "botOnOffLabel": "Bot", + "actions": "Conversation actions", + "follow": "Follow", + "unfollow": "Unfollow", + "assign": "Assign", + "unarchive": "Unarchive", + "archiveConfirm": "Archive this conversation? You can find it again later in archived conversations.", + "unarchiveConfirm": "Restore this conversation from the archive?" + }, + "chat": { + "typeAMessage": "Kirjoita viesti", + "send": "Lähetä", + "failedToSend": "Lähetys epäonnistui", + "failedToSendTapRetry": "Lähetys epäonnistui. Yritä uudelleen napauttamalla.", + "retrySend": "Yritä uudelleen", + "noMessages": "Ei vielä viestejä", + "edit": "Muokkaa", + "like": "Tykkää", + "hide": "Piilota", + "delete": "Poista", + "searchSavedReplies": "Hae tallennettuja vastauksia", + "noSavedReplies": "Ei tallennettuja vastauksia", + "attachmentTooLarge": "{{fileName}} on suurempi kuin 5 Mt.", + "quotaMac": "Tämä työtila on saavuttanut nykyisen suunnitelman yhteystietorajan. Päivitä jatkaaksesi viestien lähettämistä.", + "quotaTrialExpired": "Tämän työtilan kokeilu tai suunnitelma on päättynyt. Päivitä jatkaaksesi viestien lähettämistä.", + "quotaUnknown": "Tämä työtila ei ole tällä hetkellä käytettävissä.", + "dismiss": "Hylkää", + "grantCameraPermission": "Kameran käyttöoikeus vaaditaan kuvan ottamiseen.", + "grantPermission": "Myönnä käyttöoikeus", + "failedToSendPhoto": "Kuvan lähetys epäonnistui.", + "typing": "Typing…", + "attach": "Attach", + "camera": "Camera", + "photoLibrary": "Photo library", + "document": "Document", + "sendFlow": "Send flow", + "sendSequence": "Send sequence", + "sendingFlow": "Sending {{name}}…", + "flowSendFailed": "Couldn't send flow. Please try again.", + "savedReplies": "Saved replies", + "attachmentsTooLarge": "{{count}} file(s) are larger than 5MB and weren't sent: {{names}}", + "editingMessage": "Editing message", + "replyingTo": "Replying to {{name}}", + "reply": "Reply", + "editAndResend": "Edit & resend", + "repliedAttachment": "Attachment", + "repliedMessageUnavailable": "Original message unavailable", + "messageDeleted": "Message deleted", + "senderAgent": "You", + "senderBot": "Bot", + "senderContact": "Contact", + "scrollToBottom": "Scroll to latest messages", + "bubbleA11yLabel": "{{sender}} said {{text}}, {{time}}, {{status}}", + "status": { + "pending": "Sending", + "sent": "Sent", + "failed": "Failed to send" + }, + "locationShared": "Location shared", + "unsupportedContent": "[{{contentType}}] Unsupported on mobile", + "activityFallback": "Activity" + }, + "contacts": { + "title": "Yhteystiedot", + "contactTitle": "Yhteystieto", + "empty": "Ei yhteystietoja", + "unknown": "Tuntematon", + "blocked": "Estetty", + "email": "Sähköposti", + "phone": "Puhelin", + "locale": "Kieli", + "timezone": "Aikavyöhyke", + "location": "Sijainti", + "customFields": "Mukautetut kentät", + "addCustomField": "Lisää mukautettu kenttä", + "assignedTags": "Tunnisteet", + "noTags": "Ei tunnisteita", + "addTag": "Lisää tunniste", + "noSequences": "Ei liitetty mihinkään sarjaan", + "sequenceStep": "Vaihe {{step}}", + "completed": "Valmis", + "active": "Aktiivinen", + "tabInfo": "Tiedot", + "tabTags": "Tunnisteet", + "tabSequences": "Sarjat", + "tabNotes": "Notes", + "noNotes": "No notes yet", + "unknownAuthor": "Unknown", + "message": "Message", + "assign": "Assign", + "addToSequence": "Add to sequence", + "noActiveSequences": "No active sequences", + "sequenceSubscribers": "{{count}} subscriber", + "sequenceSubscribers_other": "{{count}} subscribers", + "enrollSelected": "Add ({{count}})", + "sequenceEnrolled": "Contact enrolled in sequence", + "sequenceEnrollFailed": "Couldn't enroll contact in sequence" + }, + "flows": { + "title": "Flow't", + "empty": "Ei flow'ta", + "readOnlyNote": "Flow't ovat vain luku -tilassa mobiilissa.", + "active": "Aktiivinen", + "inactive": "Ei aktiivinen", + "nodes": "{{count}} solmu", + "nodes_other": "{{count}} solmua" + }, + "settings": { + "title": "Asetukset", + "darkMode": "Tumma tila", + "theme": "Teema", + "themeSystem": "Järjestelmä", + "themeLight": "Vaalea", + "themeDark": "Tumma", + "language": "Kieli", + "members": "Jäsenet", + "profile": "Profiili", + "switchWorkspace": "Vaihda työtilaa", + "pushNotifications": "Push-ilmoitukset", + "pushNotificationsDescription": "Saat ilmoituksia uusista viesteistä tällä laitteella.", + "teams": "Tiimit", + "noMembers": "Ei jäseniä", + "noTeams": "Ei tiimejä", + "roleOwner": "Omistaja", + "roleAgent": "Agentti", + "restartRequiredTitle": "Uudelleenkäynnistys vaaditaan", + "restartRequiredMessage": "Tähän kieleen vaihtaminen muuttaa sovelluksen asettelusuuntaa. Käynnistetäänkö uudelleen nyt sen ottamiseksi käyttöön?", + "restartManuallyMessage": "Tähän kieleen vaihtaminen muuttaa sovelluksen asettelusuuntaa. Sulje ja avaa sovellus uudelleen ottaaksesi sen käyttöön.", + "restartNow": "Käynnistä uudelleen nyt", + "restartLater": "Myöhemmin", + "notifications": "Notifications", + "appearance": "Appearance", + "workspace": "Workspace", + "version": "Version {{version}}" + }, + "profile": { + "plan": "Paketti" + }, + "errors": { + "unauthorized": "Istuntosi on vanhentunut. Kirjaudu sisään uudelleen.", + "workspaceBlocked": "Tämä työtila ei ole tällä hetkellä käytettävissä.", + "workspaceBlockedTitle": "Työtila ei ole käytettävissä", + "workspaceBlockedMac": "Tämä työtila on saavuttanut nykyisen suunnitelman yhteystietorajan. Päivitä jatkaaksesi.", + "workspaceBlockedTrialExpired": "Tämän työtilan kokeilu tai suunnitelma on päättynyt. Päivitä jatkaaksesi.", + "notFound": "Ei löytynyt", + "notFoundTitle": "Sivua ei löytynyt", + "notFoundBody": "Etsimääsi sivua ei ole olemassa.", + "goHome": "Siirry keskusteluihin", + "unknown": "Jokin meni pieleen. Yritä uudelleen." + }, + "realtime": { + "online": "Back online", + "connecting": "Connecting…", + "reconnecting": "Reconnecting…", + "offline": "You're offline" + }, + "notifications": { + "androidChannelName": "Default" + } +} diff --git a/src/i18n/locales/fr.json b/src/i18n/locales/fr.json new file mode 100644 index 0000000..878072a --- /dev/null +++ b/src/i18n/locales/fr.json @@ -0,0 +1,259 @@ +{ + "common": { + "loading": "Chargement…", + "error": "Une erreur s'est produite", + "retry": "Réessayer", + "cancel": "Annuler", + "save": "Enregistrer", + "done": "Terminé", + "search": "Rechercher", + "confirm": "Confirmer", + "delete": "Supprimer", + "clear": "Clear", + "close": "Close", + "today": "Today", + "yesterday": "Yesterday", + "removeItem": "Remove {{label}}", + "comingSoon": "Coming soon", + "back": "Back" + }, + "auth": { + "signIn": "Se connecter", + "email": "E-mail", + "password": "Mot de passe", + "forgotPassword": "Mot de passe oublié ?", + "signOut": "Se déconnecter", + "changePassword": "Changer le mot de passe", + "changePasswordRequired": "Vous devez définir un nouveau mot de passe avant de continuer.", + "currentPassword": "Mot de passe actuel", + "newPassword": "Nouveau mot de passe", + "sendResetLink": "Envoyer le lien de réinitialisation", + "resetLinkSent": "Si un compte existe pour cet e-mail, un lien de réinitialisation a été envoyé.", + "orContinueWith": "Ou continuer avec", + "continueWithGoogle": "Continuer avec Google", + "continueWithFacebook": "Continuer avec Facebook", + "socialSignInFailed": "La connexion n'a pas abouti. Veuillez réessayer.", + "signOutConfirmTitle": "Sign out?", + "signOutConfirmBody": "You'll need to sign in again to access your workspaces.", + "showPassword": "Show password", + "hidePassword": "Hide password", + "signInSubtitle": "Sign in to keep every conversation moving.", + "forgotPasswordSubtitle": "Enter your email and we'll send you a link to reset your password.", + "backToSignIn": "Back to sign in" + }, + "workspaces": { + "title": "Espaces de travail", + "empty": "Aucun espace de travail pour le moment", + "current": "Current workspace", + "switch": "Switch", + "switchConfirmTitle": "Switch workspace?", + "switchConfirmBody": "Switch to {{name}}? You'll leave your current workspace." + }, + "inbox": { + "title": "Inbox" + }, + "conversations": { + "title": "Conversations", + "empty": "Aucune conversation", + "filters": "Filtres", + "channel": "Canal", + "handledBy": "Pris en charge par", + "status": "Statut", + "clearFilters": "Effacer les filtres", + "filterAll": "Tous", + "channelMessenger": "Messenger", + "channelInstagram": "Instagram", + "channelWhatsapp": "WhatsApp", + "channelWebchat": "Chat web", + "channelZalo": "Zalo", + "channelTelegram": "Telegram", + "channelEmail": "E-mail", + "channelTiktok": "TikTok", + "statusUnread": "Non lu", + "statusNoAdminReply": "Aucune réponse de l'équipe", + "statusFollowUp": "En suivi", + "statusArchived": "Archivé", + "statusBlocked": "Bloqué", + "handledByBot": "Pris en charge par le bot", + "handledByHuman": "Pris en charge par une personne", + "unassign": "Annuler l'attribution", + "noOtherMembers": "Cet espace de travail n'a pas encore d'autres membres.", + "unnamed": "Sans nom", + "unknownContact": "Unknown contact", + "attachmentPreview": "📎 {{count}} attachment(s)", + "a11yUnread": "unread", + "markRead": "Mark as read", + "markUnread": "Mark as unread", + "enableBot": "Enable bot", + "disableBot": "Disable bot", + "archive": "Archive", + "archiveUndone": "Conversation restored", + "undo": "Undo", + "botActive": "Bot handling", + "botPaused": "Bot paused", + "assignedTo": "Assigned to {{name}}", + "quickFilterMine": "Mine", + "emptyFiltered": "No conversations match your filters", + "botOn": "On", + "botOff": "Off", + "channelOmnichannel": "Omnichannel", + "botOnOffLabel": "Bot", + "actions": "Conversation actions", + "follow": "Follow", + "unfollow": "Unfollow", + "assign": "Assign", + "unarchive": "Unarchive", + "archiveConfirm": "Archive this conversation? You can find it again later in archived conversations.", + "unarchiveConfirm": "Restore this conversation from the archive?" + }, + "chat": { + "typeAMessage": "Écrire un message", + "send": "Envoyer", + "failedToSend": "Échec de l'envoi", + "failedToSendTapRetry": "Échec de l'envoi. Appuyez pour réessayer.", + "retrySend": "Réessayer", + "noMessages": "Aucun message pour le moment", + "edit": "Modifier", + "like": "J'aime", + "hide": "Masquer", + "delete": "Supprimer", + "searchSavedReplies": "Rechercher des réponses enregistrées", + "noSavedReplies": "Aucune réponse enregistrée", + "attachmentTooLarge": "{{fileName}} dépasse 5 Mo.", + "quotaMac": "Cet espace de travail a atteint sa limite de contacts pour le plan actuel. Passez à un plan supérieur pour continuer à envoyer des messages.", + "quotaTrialExpired": "L'essai ou le plan de cet espace de travail a expiré. Passez à un plan supérieur pour continuer à envoyer des messages.", + "quotaUnknown": "Cet espace de travail est actuellement indisponible.", + "dismiss": "Ignorer", + "grantCameraPermission": "L'accès à la caméra est requis pour prendre une photo.", + "grantPermission": "Autoriser l'accès", + "failedToSendPhoto": "Échec de l'envoi de la photo.", + "typing": "Typing…", + "attach": "Attach", + "camera": "Camera", + "photoLibrary": "Photo library", + "document": "Document", + "sendFlow": "Send flow", + "sendSequence": "Send sequence", + "sendingFlow": "Sending {{name}}…", + "flowSendFailed": "Couldn't send flow. Please try again.", + "savedReplies": "Saved replies", + "attachmentsTooLarge": "{{count}} file(s) are larger than 5MB and weren't sent: {{names}}", + "editingMessage": "Editing message", + "replyingTo": "Replying to {{name}}", + "reply": "Reply", + "editAndResend": "Edit & resend", + "repliedAttachment": "Attachment", + "repliedMessageUnavailable": "Original message unavailable", + "messageDeleted": "Message deleted", + "senderAgent": "You", + "senderBot": "Bot", + "senderContact": "Contact", + "scrollToBottom": "Scroll to latest messages", + "bubbleA11yLabel": "{{sender}} said {{text}}, {{time}}, {{status}}", + "status": { + "pending": "Sending", + "sent": "Sent", + "failed": "Failed to send" + }, + "locationShared": "Location shared", + "unsupportedContent": "[{{contentType}}] Unsupported on mobile", + "activityFallback": "Activity" + }, + "contacts": { + "title": "Contacts", + "contactTitle": "Contact", + "empty": "Aucun contact", + "unknown": "Inconnu", + "blocked": "Bloqué", + "email": "E-mail", + "phone": "Téléphone", + "locale": "Langue", + "timezone": "Fuseau horaire", + "location": "Localisation", + "customFields": "Champs personnalisés", + "addCustomField": "Ajouter un champ personnalisé", + "assignedTags": "Étiquettes", + "noTags": "Aucune étiquette", + "addTag": "Ajouter une étiquette", + "noSequences": "N'est inscrit à aucune séquence", + "sequenceStep": "Étape {{step}}", + "completed": "Terminée", + "active": "Active", + "tabInfo": "Infos", + "tabTags": "Étiquettes", + "tabSequences": "Séquences", + "tabNotes": "Notes", + "noNotes": "No notes yet", + "unknownAuthor": "Unknown", + "message": "Message", + "assign": "Assign", + "addToSequence": "Add to sequence", + "noActiveSequences": "No active sequences", + "sequenceSubscribers": "{{count}} subscriber", + "sequenceSubscribers_other": "{{count}} subscribers", + "enrollSelected": "Add ({{count}})", + "sequenceEnrolled": "Contact enrolled in sequence", + "sequenceEnrollFailed": "Couldn't enroll contact in sequence" + }, + "flows": { + "title": "Flux", + "empty": "Aucun flux", + "readOnlyNote": "Les flux sont en lecture seule sur mobile.", + "active": "Actif", + "inactive": "Inactif", + "nodes": "{{count}} nœud", + "nodes_other": "{{count}} nœuds" + }, + "settings": { + "title": "Paramètres", + "darkMode": "Mode sombre", + "theme": "Thème", + "themeSystem": "Système", + "themeLight": "Clair", + "themeDark": "Sombre", + "language": "Langue", + "members": "Membres", + "profile": "Profil", + "switchWorkspace": "Changer d'espace de travail", + "pushNotifications": "Notifications push", + "pushNotificationsDescription": "Recevez des notifications pour les nouveaux messages sur cet appareil.", + "teams": "Équipes", + "noMembers": "Aucun membre", + "noTeams": "Aucune équipe", + "roleOwner": "Propriétaire", + "roleAgent": "Agent", + "restartRequiredTitle": "Redémarrage requis", + "restartRequiredMessage": "Passer à cette langue change le sens de la mise en page de l'application. Redémarrer maintenant pour l'appliquer ?", + "restartManuallyMessage": "Passer à cette langue change le sens de la mise en page de l'application. Fermez et rouvrez l'application pour l'appliquer.", + "restartNow": "Redémarrer maintenant", + "restartLater": "Plus tard", + "notifications": "Notifications", + "appearance": "Appearance", + "workspace": "Workspace", + "version": "Version {{version}}" + }, + "profile": { + "plan": "Forfait" + }, + "errors": { + "unauthorized": "Votre session a expiré. Veuillez vous reconnecter.", + "workspaceBlocked": "Cet espace de travail est actuellement indisponible.", + "workspaceBlockedTitle": "Espace de travail indisponible", + "workspaceBlockedMac": "Cet espace de travail a atteint sa limite de contacts pour le plan actuel. Passez à un plan supérieur pour continuer.", + "workspaceBlockedTrialExpired": "L'essai ou le plan de cet espace de travail a expiré. Passez à un plan supérieur pour continuer.", + "notFound": "Introuvable", + "notFoundTitle": "Page introuvable", + "notFoundBody": "La page que vous recherchez n'existe pas.", + "goHome": "Aller aux conversations", + "unknown": "Une erreur s'est produite. Veuillez réessayer." + }, + "realtime": { + "online": "Back online", + "connecting": "Connecting…", + "reconnecting": "Reconnecting…", + "offline": "You're offline" + }, + "notifications": { + "androidChannelName": "Default" + } +} diff --git a/src/i18n/locales/he.json b/src/i18n/locales/he.json new file mode 100644 index 0000000..e859882 --- /dev/null +++ b/src/i18n/locales/he.json @@ -0,0 +1,261 @@ +{ + "common": { + "loading": "טוען…", + "error": "משהו השתבש", + "retry": "נסה שוב", + "cancel": "ביטול", + "save": "שמירה", + "done": "בוצע", + "search": "חיפוש", + "confirm": "אישור", + "delete": "מחיקה", + "clear": "Clear", + "close": "Close", + "today": "Today", + "yesterday": "Yesterday", + "removeItem": "Remove {{label}}", + "comingSoon": "Coming soon", + "back": "Back" + }, + "auth": { + "signIn": "התחברות", + "email": "אימייל", + "password": "סיסמה", + "forgotPassword": "שכחת סיסמה?", + "signOut": "התנתקות", + "changePassword": "שינוי סיסמה", + "changePasswordRequired": "עליך להגדיר סיסמה חדשה לפני שתוכל להמשיך.", + "currentPassword": "סיסמה נוכחית", + "newPassword": "סיסמה חדשה", + "sendResetLink": "שליחת קישור לאיפוס", + "resetLinkSent": "אם קיים חשבון עבור אימייל זה, נשלח קישור לאיפוס.", + "orContinueWith": "או המשך עם", + "continueWithGoogle": "המשך עם Google", + "continueWithFacebook": "המשך עם Facebook", + "socialSignInFailed": "ההתחברות לא הושלמה. נסה שוב.", + "signOutConfirmTitle": "Sign out?", + "signOutConfirmBody": "You'll need to sign in again to access your workspaces.", + "showPassword": "Show password", + "hidePassword": "Hide password", + "signInSubtitle": "Sign in to keep every conversation moving.", + "forgotPasswordSubtitle": "Enter your email and we'll send you a link to reset your password.", + "backToSignIn": "Back to sign in" + }, + "workspaces": { + "title": "סביבות עבודה", + "empty": "אין עדיין סביבות עבודה", + "current": "Current workspace", + "switch": "Switch", + "switchConfirmTitle": "Switch workspace?", + "switchConfirmBody": "Switch to {{name}}? You'll leave your current workspace." + }, + "inbox": { + "title": "Inbox" + }, + "conversations": { + "title": "שיחות", + "empty": "אין שיחות", + "filters": "מסננים", + "channel": "ערוץ", + "handledBy": "מטופלת על ידי", + "status": "סטטוס", + "clearFilters": "ניקוי מסננים", + "filterAll": "הכול", + "channelMessenger": "Messenger", + "channelInstagram": "Instagram", + "channelWhatsapp": "WhatsApp", + "channelWebchat": "צ'אט באתר", + "channelZalo": "Zalo", + "channelTelegram": "Telegram", + "channelEmail": "אימייל", + "channelTiktok": "TikTok", + "statusUnread": "לא נקראה", + "statusNoAdminReply": "אין תגובה מהצוות", + "statusFollowUp": "במעקב", + "statusArchived": "בארכיון", + "statusBlocked": "חסומה", + "handledByBot": "מטופלת על ידי בוט", + "handledByHuman": "מטופלת על ידי אדם", + "unassign": "ביטול שיוך", + "noOtherMembers": "לסביבת עבודה זו אין עדיין חברים נוספים.", + "unnamed": "ללא שם", + "unknownContact": "Unknown contact", + "attachmentPreview": "📎 {{count}} attachment(s)", + "a11yUnread": "unread", + "markRead": "Mark as read", + "markUnread": "Mark as unread", + "enableBot": "Enable bot", + "disableBot": "Disable bot", + "archive": "Archive", + "archiveUndone": "Conversation restored", + "undo": "Undo", + "botActive": "Bot handling", + "botPaused": "Bot paused", + "assignedTo": "Assigned to {{name}}", + "quickFilterMine": "Mine", + "emptyFiltered": "No conversations match your filters", + "botOn": "On", + "botOff": "Off", + "channelOmnichannel": "Omnichannel", + "botOnOffLabel": "Bot", + "actions": "Conversation actions", + "follow": "Follow", + "unfollow": "Unfollow", + "assign": "Assign", + "unarchive": "Unarchive", + "archiveConfirm": "Archive this conversation? You can find it again later in archived conversations.", + "unarchiveConfirm": "Restore this conversation from the archive?" + }, + "chat": { + "typeAMessage": "הקלד הודעה", + "send": "שליחה", + "failedToSend": "השליחה נכשלה", + "failedToSendTapRetry": "השליחה נכשלה. הקש כדי לנסות שוב.", + "retrySend": "נסה שוב", + "noMessages": "אין עדיין הודעות", + "edit": "עריכה", + "like": "לייק", + "hide": "הסתרה", + "delete": "מחיקה", + "searchSavedReplies": "חיפוש תגובות שמורות", + "noSavedReplies": "אין תגובות שמורות", + "attachmentTooLarge": "{{fileName}} גדול מ-5MB.", + "quotaMac": "סביבת עבודה זו הגיעה למגבלת אנשי הקשר בתוכנית הנוכחית. שדרג כדי להמשיך לשלוח הודעות.", + "quotaTrialExpired": "תקופת הניסיון או התוכנית של סביבת עבודה זו פגה. שדרג כדי להמשיך לשלוח הודעות.", + "quotaUnknown": "סביבת עבודה זו אינה זמינה כרגע.", + "dismiss": "סגירה", + "grantCameraPermission": "נדרשת גישה למצלמה כדי לצלם תמונה.", + "grantPermission": "הענקת הרשאה", + "failedToSendPhoto": "שליחת התמונה נכשלה.", + "typing": "Typing…", + "attach": "Attach", + "camera": "Camera", + "photoLibrary": "Photo library", + "document": "Document", + "sendFlow": "Send flow", + "sendSequence": "Send sequence", + "sendingFlow": "Sending {{name}}…", + "flowSendFailed": "Couldn't send flow. Please try again.", + "savedReplies": "Saved replies", + "attachmentsTooLarge": "{{count}} file(s) are larger than 5MB and weren't sent: {{names}}", + "editingMessage": "Editing message", + "replyingTo": "Replying to {{name}}", + "reply": "Reply", + "editAndResend": "Edit & resend", + "repliedAttachment": "Attachment", + "repliedMessageUnavailable": "Original message unavailable", + "messageDeleted": "Message deleted", + "senderAgent": "You", + "senderBot": "Bot", + "senderContact": "Contact", + "scrollToBottom": "Scroll to latest messages", + "bubbleA11yLabel": "{{sender}} said {{text}}, {{time}}, {{status}}", + "status": { + "pending": "Sending", + "sent": "Sent", + "failed": "Failed to send" + }, + "locationShared": "Location shared", + "unsupportedContent": "[{{contentType}}] Unsupported on mobile", + "activityFallback": "Activity" + }, + "contacts": { + "title": "אנשי קשר", + "contactTitle": "איש קשר", + "empty": "אין אנשי קשר", + "unknown": "לא ידוע", + "blocked": "חסום", + "email": "אימייל", + "phone": "טלפון", + "locale": "שפה", + "timezone": "אזור זמן", + "location": "מיקום", + "customFields": "שדות מותאמים אישית", + "addCustomField": "הוספת שדה מותאם אישית", + "assignedTags": "תגיות", + "noTags": "אין תגיות", + "addTag": "הוספת תגית", + "noSequences": "לא רשום לאף רצף", + "sequenceStep": "שלב {{step}}", + "completed": "הושלם", + "active": "פעיל", + "tabInfo": "מידע", + "tabTags": "תגיות", + "tabSequences": "רצפים", + "tabNotes": "Notes", + "noNotes": "No notes yet", + "unknownAuthor": "Unknown", + "message": "Message", + "assign": "Assign", + "addToSequence": "Add to sequence", + "noActiveSequences": "No active sequences", + "sequenceSubscribers": "{{count}} subscriber", + "sequenceSubscribers_other": "{{count}} subscribers", + "enrollSelected": "Add ({{count}})", + "sequenceEnrolled": "Contact enrolled in sequence", + "sequenceEnrollFailed": "Couldn't enroll contact in sequence" + }, + "flows": { + "title": "תהליכים", + "empty": "אין תהליכים", + "readOnlyNote": "תהליכים הם לקריאה בלבד בנייד.", + "active": "פעיל", + "inactive": "לא פעיל", + "nodes": "{{count}} צומת", + "nodes_one": "צומת אחד", + "nodes_two": "שני צמתים", + "nodes_other": "{{count}} צמתים" + }, + "settings": { + "title": "הגדרות", + "darkMode": "מצב כהה", + "theme": "ערכת נושא", + "themeSystem": "מערכת", + "themeLight": "בהיר", + "themeDark": "כהה", + "language": "שפה", + "members": "חברים", + "profile": "פרופיל", + "switchWorkspace": "החלפת סביבת עבודה", + "pushNotifications": "התראות דחיפה", + "pushNotificationsDescription": "קבל התראות על הודעות חדשות במכשיר זה.", + "teams": "צוותים", + "noMembers": "אין חברים", + "noTeams": "אין צוותים", + "roleOwner": "בעלים", + "roleAgent": "נציג", + "restartRequiredTitle": "נדרשת הפעלה מחדש", + "restartRequiredMessage": "מעבר לשפה זו משנה את כיוון הפריסה של האפליקציה. להפעיל מחדש עכשיו כדי להחיל אותה?", + "restartManuallyMessage": "מעבר לשפה זו משנה את כיוון הפריסה של האפליקציה. סגור ופתח מחדש את האפליקציה כדי להחיל אותה.", + "restartNow": "הפעל מחדש עכשיו", + "restartLater": "מאוחר יותר", + "notifications": "Notifications", + "appearance": "Appearance", + "workspace": "Workspace", + "version": "Version {{version}}" + }, + "profile": { + "plan": "תוכנית" + }, + "errors": { + "unauthorized": "פג תוקף ההפעלה שלך. אנא התחבר שוב.", + "workspaceBlocked": "סביבת עבודה זו אינה זמינה כרגע.", + "workspaceBlockedTitle": "סביבת עבודה אינה זמינה", + "workspaceBlockedMac": "סביבת עבודה זו הגיעה למגבלת אנשי הקשר בתוכנית הנוכחית. שדרג כדי להמשיך.", + "workspaceBlockedTrialExpired": "תקופת הניסיון או התוכנית של סביבת עבודה זו פגה. שדרג כדי להמשיך.", + "notFound": "לא נמצא", + "notFoundTitle": "הדף לא נמצא", + "notFoundBody": "הדף שאתה מחפש אינו קיים.", + "goHome": "מעבר לשיחות", + "unknown": "משהו השתבש. נסה שוב." + }, + "realtime": { + "online": "Back online", + "connecting": "Connecting…", + "reconnecting": "Reconnecting…", + "offline": "You're offline" + }, + "notifications": { + "androidChannelName": "Default" + } +} diff --git a/src/i18n/locales/id.json b/src/i18n/locales/id.json new file mode 100644 index 0000000..de5fd76 --- /dev/null +++ b/src/i18n/locales/id.json @@ -0,0 +1,259 @@ +{ + "common": { + "loading": "Memuat…", + "error": "Terjadi kesalahan", + "retry": "Coba lagi", + "cancel": "Batal", + "save": "Simpan", + "done": "Selesai", + "search": "Cari", + "confirm": "Konfirmasi", + "delete": "Hapus", + "clear": "Clear", + "close": "Close", + "today": "Today", + "yesterday": "Yesterday", + "removeItem": "Remove {{label}}", + "comingSoon": "Coming soon", + "back": "Back" + }, + "auth": { + "signIn": "Masuk", + "email": "Email", + "password": "Kata sandi", + "forgotPassword": "Lupa kata sandi?", + "signOut": "Keluar", + "changePassword": "Ubah kata sandi", + "changePasswordRequired": "Anda perlu membuat kata sandi baru sebelum melanjutkan.", + "currentPassword": "Kata sandi saat ini", + "newPassword": "Kata sandi baru", + "sendResetLink": "Kirim tautan reset", + "resetLinkSent": "Jika akun dengan email ini ada, tautan reset telah dikirim.", + "orContinueWith": "Atau lanjutkan dengan", + "continueWithGoogle": "Lanjutkan dengan Google", + "continueWithFacebook": "Lanjutkan dengan Facebook", + "socialSignInFailed": "Masuk tidak berhasil. Silakan coba lagi.", + "signOutConfirmTitle": "Sign out?", + "signOutConfirmBody": "You'll need to sign in again to access your workspaces.", + "showPassword": "Show password", + "hidePassword": "Hide password", + "signInSubtitle": "Sign in to keep every conversation moving.", + "forgotPasswordSubtitle": "Enter your email and we'll send you a link to reset your password.", + "backToSignIn": "Back to sign in" + }, + "workspaces": { + "title": "Ruang kerja", + "empty": "Belum ada ruang kerja", + "current": "Current workspace", + "switch": "Switch", + "switchConfirmTitle": "Switch workspace?", + "switchConfirmBody": "Switch to {{name}}? You'll leave your current workspace." + }, + "inbox": { + "title": "Inbox" + }, + "conversations": { + "title": "Percakapan", + "empty": "Tidak ada percakapan", + "filters": "Filter", + "channel": "Saluran", + "handledBy": "Ditangani oleh", + "status": "Status", + "clearFilters": "Hapus filter", + "filterAll": "Semua", + "channelMessenger": "Messenger", + "channelInstagram": "Instagram", + "channelWhatsapp": "WhatsApp", + "channelWebchat": "Obrolan web", + "channelZalo": "Zalo", + "channelTelegram": "Telegram", + "channelEmail": "Email", + "channelTiktok": "TikTok", + "statusUnread": "Belum dibaca", + "statusNoAdminReply": "Belum ada balasan dari tim", + "statusFollowUp": "Sedang ditindaklanjuti", + "statusArchived": "Diarsipkan", + "statusBlocked": "Diblokir", + "handledByBot": "Ditangani oleh bot", + "handledByHuman": "Ditangani oleh manusia", + "unassign": "Batalkan penugasan", + "noOtherMembers": "Ruang kerja ini belum memiliki anggota lain.", + "unnamed": "Tanpa nama", + "unknownContact": "Unknown contact", + "attachmentPreview": "📎 {{count}} attachment(s)", + "a11yUnread": "unread", + "markRead": "Mark as read", + "markUnread": "Mark as unread", + "enableBot": "Enable bot", + "disableBot": "Disable bot", + "archive": "Archive", + "archiveUndone": "Conversation restored", + "undo": "Undo", + "botActive": "Bot handling", + "botPaused": "Bot paused", + "assignedTo": "Assigned to {{name}}", + "quickFilterMine": "Mine", + "emptyFiltered": "No conversations match your filters", + "botOn": "On", + "botOff": "Off", + "channelOmnichannel": "Omnichannel", + "botOnOffLabel": "Bot", + "actions": "Conversation actions", + "follow": "Follow", + "unfollow": "Unfollow", + "assign": "Assign", + "unarchive": "Unarchive", + "archiveConfirm": "Archive this conversation? You can find it again later in archived conversations.", + "unarchiveConfirm": "Restore this conversation from the archive?" + }, + "chat": { + "typeAMessage": "Ketik pesan", + "send": "Kirim", + "failedToSend": "Gagal mengirim", + "failedToSendTapRetry": "Gagal mengirim. Ketuk untuk mencoba lagi.", + "retrySend": "Coba lagi", + "noMessages": "Belum ada pesan", + "edit": "Edit", + "like": "Suka", + "hide": "Sembunyikan", + "delete": "Hapus", + "searchSavedReplies": "Cari balasan tersimpan", + "noSavedReplies": "Tidak ada balasan tersimpan", + "attachmentTooLarge": "{{fileName}} lebih besar dari 5MB.", + "quotaMac": "Ruang kerja ini telah mencapai batas kontak pada paket saat ini. Tingkatkan paket untuk terus mengirim pesan.", + "quotaTrialExpired": "Masa percobaan atau paket ruang kerja ini telah berakhir. Tingkatkan paket untuk terus mengirim pesan.", + "quotaUnknown": "Ruang kerja ini saat ini tidak tersedia.", + "dismiss": "Tutup", + "grantCameraPermission": "Akses kamera diperlukan untuk mengambil foto.", + "grantPermission": "Berikan izin", + "failedToSendPhoto": "Gagal mengirim foto.", + "typing": "Typing…", + "attach": "Attach", + "camera": "Camera", + "photoLibrary": "Photo library", + "document": "Document", + "sendFlow": "Send flow", + "sendSequence": "Send sequence", + "sendingFlow": "Sending {{name}}…", + "flowSendFailed": "Couldn't send flow. Please try again.", + "savedReplies": "Saved replies", + "attachmentsTooLarge": "{{count}} file(s) are larger than 5MB and weren't sent: {{names}}", + "editingMessage": "Editing message", + "replyingTo": "Replying to {{name}}", + "reply": "Reply", + "editAndResend": "Edit & resend", + "repliedAttachment": "Attachment", + "repliedMessageUnavailable": "Original message unavailable", + "messageDeleted": "Message deleted", + "senderAgent": "You", + "senderBot": "Bot", + "senderContact": "Contact", + "scrollToBottom": "Scroll to latest messages", + "bubbleA11yLabel": "{{sender}} said {{text}}, {{time}}, {{status}}", + "status": { + "pending": "Sending", + "sent": "Sent", + "failed": "Failed to send" + }, + "locationShared": "Location shared", + "unsupportedContent": "[{{contentType}}] Unsupported on mobile", + "activityFallback": "Activity" + }, + "contacts": { + "title": "Kontak", + "contactTitle": "Kontak", + "empty": "Tidak ada kontak", + "unknown": "Tidak diketahui", + "blocked": "Diblokir", + "email": "Email", + "phone": "Telepon", + "locale": "Bahasa", + "timezone": "Zona waktu", + "location": "Lokasi", + "customFields": "Bidang khusus", + "addCustomField": "Tambahkan bidang khusus", + "assignedTags": "Tag", + "noTags": "Tidak ada tag", + "addTag": "Tambahkan tag", + "noSequences": "Tidak terdaftar dalam sekuens apa pun", + "sequenceStep": "Langkah {{step}}", + "completed": "Selesai", + "active": "Aktif", + "tabInfo": "Info", + "tabTags": "Tag", + "tabSequences": "Sekuens", + "tabNotes": "Notes", + "noNotes": "No notes yet", + "unknownAuthor": "Unknown", + "message": "Message", + "assign": "Assign", + "addToSequence": "Add to sequence", + "noActiveSequences": "No active sequences", + "sequenceSubscribers": "{{count}} subscriber", + "sequenceSubscribers_other": "{{count}} subscribers", + "enrollSelected": "Add ({{count}})", + "sequenceEnrolled": "Contact enrolled in sequence", + "sequenceEnrollFailed": "Couldn't enroll contact in sequence" + }, + "flows": { + "title": "Alur", + "empty": "Tidak ada alur", + "readOnlyNote": "Alur bersifat hanya-baca di perangkat seluler.", + "active": "Aktif", + "inactive": "Tidak aktif", + "nodes": "{{count}} node", + "nodes_other": "{{count}} node" + }, + "settings": { + "title": "Pengaturan", + "darkMode": "Mode gelap", + "theme": "Tema", + "themeSystem": "Sistem", + "themeLight": "Terang", + "themeDark": "Gelap", + "language": "Bahasa", + "members": "Anggota", + "profile": "Profil", + "switchWorkspace": "Ganti ruang kerja", + "pushNotifications": "Notifikasi push", + "pushNotificationsDescription": "Dapatkan notifikasi tentang pesan baru di perangkat ini.", + "teams": "Tim", + "noMembers": "Tidak ada anggota", + "noTeams": "Tidak ada tim", + "roleOwner": "Pemilik", + "roleAgent": "Agen", + "restartRequiredTitle": "Perlu memulai ulang", + "restartRequiredMessage": "Beralih ke bahasa ini mengubah arah tata letak aplikasi. Mulai ulang sekarang untuk menerapkannya?", + "restartManuallyMessage": "Beralih ke bahasa ini mengubah arah tata letak aplikasi. Tutup dan buka kembali aplikasi untuk menerapkannya.", + "restartNow": "Mulai ulang sekarang", + "restartLater": "Nanti", + "notifications": "Notifications", + "appearance": "Appearance", + "workspace": "Workspace", + "version": "Version {{version}}" + }, + "profile": { + "plan": "Paket" + }, + "errors": { + "unauthorized": "Sesi Anda telah berakhir. Silakan masuk kembali.", + "workspaceBlocked": "Ruang kerja ini saat ini tidak tersedia.", + "workspaceBlockedTitle": "Ruang kerja tidak tersedia", + "workspaceBlockedMac": "Ruang kerja ini telah mencapai batas kontak pada paket saat ini. Tingkatkan paket untuk melanjutkan.", + "workspaceBlockedTrialExpired": "Masa percobaan atau paket ruang kerja ini telah berakhir. Tingkatkan paket untuk melanjutkan.", + "notFound": "Tidak ditemukan", + "notFoundTitle": "Halaman tidak ditemukan", + "notFoundBody": "Halaman yang Anda cari tidak ada.", + "goHome": "Ke percakapan", + "unknown": "Terjadi kesalahan. Silakan coba lagi." + }, + "realtime": { + "online": "Back online", + "connecting": "Connecting…", + "reconnecting": "Reconnecting…", + "offline": "You're offline" + }, + "notifications": { + "androidChannelName": "Default" + } +} diff --git a/src/i18n/locales/it.json b/src/i18n/locales/it.json new file mode 100644 index 0000000..6f3725a --- /dev/null +++ b/src/i18n/locales/it.json @@ -0,0 +1,259 @@ +{ + "common": { + "loading": "Caricamento…", + "error": "Qualcosa è andato storto", + "retry": "Riprova", + "cancel": "Annulla", + "save": "Salva", + "done": "Fatto", + "search": "Cerca", + "confirm": "Conferma", + "delete": "Elimina", + "clear": "Clear", + "close": "Close", + "today": "Today", + "yesterday": "Yesterday", + "removeItem": "Remove {{label}}", + "comingSoon": "Coming soon", + "back": "Back" + }, + "auth": { + "signIn": "Accedi", + "email": "E-mail", + "password": "Password", + "forgotPassword": "Password dimenticata?", + "signOut": "Esci", + "changePassword": "Cambia password", + "changePasswordRequired": "Devi impostare una nuova password prima di continuare.", + "currentPassword": "Password attuale", + "newPassword": "Nuova password", + "sendResetLink": "Invia link di reimpostazione", + "resetLinkSent": "Se esiste un account con questa e-mail, è stato inviato un link di reimpostazione.", + "orContinueWith": "Oppure continua con", + "continueWithGoogle": "Continua con Google", + "continueWithFacebook": "Continua con Facebook", + "socialSignInFailed": "Accesso non riuscito. Riprova.", + "signOutConfirmTitle": "Sign out?", + "signOutConfirmBody": "You'll need to sign in again to access your workspaces.", + "showPassword": "Show password", + "hidePassword": "Hide password", + "signInSubtitle": "Sign in to keep every conversation moving.", + "forgotPasswordSubtitle": "Enter your email and we'll send you a link to reset your password.", + "backToSignIn": "Back to sign in" + }, + "workspaces": { + "title": "Workspace", + "empty": "Nessun workspace ancora", + "current": "Current workspace", + "switch": "Switch", + "switchConfirmTitle": "Switch workspace?", + "switchConfirmBody": "Switch to {{name}}? You'll leave your current workspace." + }, + "inbox": { + "title": "Inbox" + }, + "conversations": { + "title": "Conversazioni", + "empty": "Nessuna conversazione", + "filters": "Filtri", + "channel": "Canale", + "handledBy": "Gestita da", + "status": "Stato", + "clearFilters": "Cancella filtri", + "filterAll": "Tutti", + "channelMessenger": "Messenger", + "channelInstagram": "Instagram", + "channelWhatsapp": "WhatsApp", + "channelWebchat": "Chat web", + "channelZalo": "Zalo", + "channelTelegram": "Telegram", + "channelEmail": "E-mail", + "channelTiktok": "TikTok", + "statusUnread": "Non letto", + "statusNoAdminReply": "Nessuna risposta del team", + "statusFollowUp": "In monitoraggio", + "statusArchived": "Archiviata", + "statusBlocked": "Bloccata", + "handledByBot": "Gestita dal bot", + "handledByHuman": "Gestita da una persona", + "unassign": "Rimuovi assegnazione", + "noOtherMembers": "Questo workspace non ha ancora altri membri.", + "unnamed": "Senza nome", + "unknownContact": "Unknown contact", + "attachmentPreview": "📎 {{count}} attachment(s)", + "a11yUnread": "unread", + "markRead": "Mark as read", + "markUnread": "Mark as unread", + "enableBot": "Enable bot", + "disableBot": "Disable bot", + "archive": "Archive", + "archiveUndone": "Conversation restored", + "undo": "Undo", + "botActive": "Bot handling", + "botPaused": "Bot paused", + "assignedTo": "Assigned to {{name}}", + "quickFilterMine": "Mine", + "emptyFiltered": "No conversations match your filters", + "botOn": "On", + "botOff": "Off", + "channelOmnichannel": "Omnichannel", + "botOnOffLabel": "Bot", + "actions": "Conversation actions", + "follow": "Follow", + "unfollow": "Unfollow", + "assign": "Assign", + "unarchive": "Unarchive", + "archiveConfirm": "Archive this conversation? You can find it again later in archived conversations.", + "unarchiveConfirm": "Restore this conversation from the archive?" + }, + "chat": { + "typeAMessage": "Scrivi un messaggio", + "send": "Invia", + "failedToSend": "Invio non riuscito", + "failedToSendTapRetry": "Invio non riuscito. Tocca per riprovare.", + "retrySend": "Riprova", + "noMessages": "Nessun messaggio ancora", + "edit": "Modifica", + "like": "Mi piace", + "hide": "Nascondi", + "delete": "Elimina", + "searchSavedReplies": "Cerca risposte salvate", + "noSavedReplies": "Nessuna risposta salvata", + "attachmentTooLarge": "{{fileName}} supera i 5 MB.", + "quotaMac": "Questo workspace ha raggiunto il limite di contatti del piano attuale. Esegui l'upgrade per continuare a inviare messaggi.", + "quotaTrialExpired": "La prova o il piano di questo workspace è scaduto. Esegui l'upgrade per continuare a inviare messaggi.", + "quotaUnknown": "Questo workspace non è attualmente disponibile.", + "dismiss": "Ignora", + "grantCameraPermission": "È richiesto l'accesso alla fotocamera per scattare una foto.", + "grantPermission": "Concedi il permesso", + "failedToSendPhoto": "Invio della foto non riuscito.", + "typing": "Typing…", + "attach": "Attach", + "camera": "Camera", + "photoLibrary": "Photo library", + "document": "Document", + "sendFlow": "Send flow", + "sendSequence": "Send sequence", + "sendingFlow": "Sending {{name}}…", + "flowSendFailed": "Couldn't send flow. Please try again.", + "savedReplies": "Saved replies", + "attachmentsTooLarge": "{{count}} file(s) are larger than 5MB and weren't sent: {{names}}", + "editingMessage": "Editing message", + "replyingTo": "Replying to {{name}}", + "reply": "Reply", + "editAndResend": "Edit & resend", + "repliedAttachment": "Attachment", + "repliedMessageUnavailable": "Original message unavailable", + "messageDeleted": "Message deleted", + "senderAgent": "You", + "senderBot": "Bot", + "senderContact": "Contact", + "scrollToBottom": "Scroll to latest messages", + "bubbleA11yLabel": "{{sender}} said {{text}}, {{time}}, {{status}}", + "status": { + "pending": "Sending", + "sent": "Sent", + "failed": "Failed to send" + }, + "locationShared": "Location shared", + "unsupportedContent": "[{{contentType}}] Unsupported on mobile", + "activityFallback": "Activity" + }, + "contacts": { + "title": "Contatti", + "contactTitle": "Contatto", + "empty": "Nessun contatto", + "unknown": "Sconosciuto", + "blocked": "Bloccato", + "email": "E-mail", + "phone": "Telefono", + "locale": "Lingua", + "timezone": "Fuso orario", + "location": "Posizione", + "customFields": "Campi personalizzati", + "addCustomField": "Aggiungi campo personalizzato", + "assignedTags": "Tag", + "noTags": "Nessun tag", + "addTag": "Aggiungi tag", + "noSequences": "Non iscritto ad alcuna sequenza", + "sequenceStep": "Passaggio {{step}}", + "completed": "Completata", + "active": "Attiva", + "tabInfo": "Info", + "tabTags": "Tag", + "tabSequences": "Sequenze", + "tabNotes": "Notes", + "noNotes": "No notes yet", + "unknownAuthor": "Unknown", + "message": "Message", + "assign": "Assign", + "addToSequence": "Add to sequence", + "noActiveSequences": "No active sequences", + "sequenceSubscribers": "{{count}} subscriber", + "sequenceSubscribers_other": "{{count}} subscribers", + "enrollSelected": "Add ({{count}})", + "sequenceEnrolled": "Contact enrolled in sequence", + "sequenceEnrollFailed": "Couldn't enroll contact in sequence" + }, + "flows": { + "title": "Flussi", + "empty": "Nessun flusso", + "readOnlyNote": "I flussi sono di sola lettura su mobile.", + "active": "Attivo", + "inactive": "Inattivo", + "nodes": "{{count}} nodo", + "nodes_other": "{{count}} nodi" + }, + "settings": { + "title": "Impostazioni", + "darkMode": "Modalità scura", + "theme": "Tema", + "themeSystem": "Sistema", + "themeLight": "Chiaro", + "themeDark": "Scuro", + "language": "Lingua", + "members": "Membri", + "profile": "Profilo", + "switchWorkspace": "Cambia workspace", + "pushNotifications": "Notifiche push", + "pushNotificationsDescription": "Ricevi notifiche per i nuovi messaggi su questo dispositivo.", + "teams": "Team", + "noMembers": "Nessun membro", + "noTeams": "Nessun team", + "roleOwner": "Proprietario", + "roleAgent": "Agente", + "restartRequiredTitle": "Riavvio richiesto", + "restartRequiredMessage": "Passare a questa lingua cambia la direzione del layout dell'app. Riavviare ora per applicarla?", + "restartManuallyMessage": "Passare a questa lingua cambia la direzione del layout dell'app. Chiudi e riapri l'app per applicarla.", + "restartNow": "Riavvia ora", + "restartLater": "Più tardi", + "notifications": "Notifications", + "appearance": "Appearance", + "workspace": "Workspace", + "version": "Version {{version}}" + }, + "profile": { + "plan": "Piano" + }, + "errors": { + "unauthorized": "La sessione è scaduta. Accedi di nuovo.", + "workspaceBlocked": "Questo workspace non è attualmente disponibile.", + "workspaceBlockedTitle": "Workspace non disponibile", + "workspaceBlockedMac": "Questo workspace ha raggiunto il limite di contatti del piano attuale. Esegui l'upgrade per continuare.", + "workspaceBlockedTrialExpired": "La prova o il piano di questo workspace è scaduto. Esegui l'upgrade per continuare.", + "notFound": "Non trovato", + "notFoundTitle": "Pagina non trovata", + "notFoundBody": "La pagina cercata non esiste.", + "goHome": "Vai alle conversazioni", + "unknown": "Qualcosa è andato storto. Riprova." + }, + "realtime": { + "online": "Back online", + "connecting": "Connecting…", + "reconnecting": "Reconnecting…", + "offline": "You're offline" + }, + "notifications": { + "androidChannelName": "Default" + } +} diff --git a/src/i18n/locales/ja.json b/src/i18n/locales/ja.json new file mode 100644 index 0000000..64bcf17 --- /dev/null +++ b/src/i18n/locales/ja.json @@ -0,0 +1,259 @@ +{ + "common": { + "loading": "読み込み中…", + "error": "問題が発生しました", + "retry": "再試行", + "cancel": "キャンセル", + "save": "保存", + "done": "完了", + "search": "検索", + "confirm": "確認", + "delete": "削除", + "clear": "Clear", + "close": "Close", + "today": "Today", + "yesterday": "Yesterday", + "removeItem": "Remove {{label}}", + "comingSoon": "Coming soon", + "back": "Back" + }, + "auth": { + "signIn": "サインイン", + "email": "メールアドレス", + "password": "パスワード", + "forgotPassword": "パスワードをお忘れですか?", + "signOut": "サインアウト", + "changePassword": "パスワードを変更", + "changePasswordRequired": "続行する前に新しいパスワードを設定してください。", + "currentPassword": "現在のパスワード", + "newPassword": "新しいパスワード", + "sendResetLink": "リセットリンクを送信", + "resetLinkSent": "このメールアドレスのアカウントが存在する場合、リセットリンクが送信されました。", + "orContinueWith": "または次で続行", + "continueWithGoogle": "Googleで続行", + "continueWithFacebook": "Facebookで続行", + "socialSignInFailed": "サインインが完了しませんでした。もう一度お試しください。", + "signOutConfirmTitle": "Sign out?", + "signOutConfirmBody": "You'll need to sign in again to access your workspaces.", + "showPassword": "Show password", + "hidePassword": "Hide password", + "signInSubtitle": "Sign in to keep every conversation moving.", + "forgotPasswordSubtitle": "Enter your email and we'll send you a link to reset your password.", + "backToSignIn": "Back to sign in" + }, + "workspaces": { + "title": "ワークスペース", + "empty": "ワークスペースはまだありません", + "current": "Current workspace", + "switch": "Switch", + "switchConfirmTitle": "Switch workspace?", + "switchConfirmBody": "Switch to {{name}}? You'll leave your current workspace." + }, + "inbox": { + "title": "Inbox" + }, + "conversations": { + "title": "会話", + "empty": "会話がありません", + "filters": "フィルター", + "channel": "チャネル", + "handledBy": "対応者", + "status": "ステータス", + "clearFilters": "フィルターをクリア", + "filterAll": "すべて", + "channelMessenger": "Messenger", + "channelInstagram": "Instagram", + "channelWhatsapp": "WhatsApp", + "channelWebchat": "ウェブチャット", + "channelZalo": "Zalo", + "channelTelegram": "Telegram", + "channelEmail": "メール", + "channelTiktok": "TikTok", + "statusUnread": "未読", + "statusNoAdminReply": "チームからの返信なし", + "statusFollowUp": "フォローアップ中", + "statusArchived": "アーカイブ済み", + "statusBlocked": "ブロック済み", + "handledByBot": "ボットが対応中", + "handledByHuman": "担当者が対応中", + "unassign": "割り当て解除", + "noOtherMembers": "このワークスペースには他のメンバーがまだいません。", + "unnamed": "名前なし", + "unknownContact": "Unknown contact", + "attachmentPreview": "📎 {{count}} attachment(s)", + "a11yUnread": "unread", + "markRead": "Mark as read", + "markUnread": "Mark as unread", + "enableBot": "Enable bot", + "disableBot": "Disable bot", + "archive": "Archive", + "archiveUndone": "Conversation restored", + "undo": "Undo", + "botActive": "Bot handling", + "botPaused": "Bot paused", + "assignedTo": "Assigned to {{name}}", + "quickFilterMine": "Mine", + "emptyFiltered": "No conversations match your filters", + "botOn": "On", + "botOff": "Off", + "channelOmnichannel": "Omnichannel", + "botOnOffLabel": "Bot", + "actions": "Conversation actions", + "follow": "Follow", + "unfollow": "Unfollow", + "assign": "Assign", + "unarchive": "Unarchive", + "archiveConfirm": "Archive this conversation? You can find it again later in archived conversations.", + "unarchiveConfirm": "Restore this conversation from the archive?" + }, + "chat": { + "typeAMessage": "メッセージを入力", + "send": "送信", + "failedToSend": "送信に失敗しました", + "failedToSendTapRetry": "送信に失敗しました。タップして再試行してください。", + "retrySend": "再試行", + "noMessages": "メッセージはまだありません", + "edit": "編集", + "like": "いいね", + "hide": "非表示", + "delete": "削除", + "searchSavedReplies": "保存済み返信を検索", + "noSavedReplies": "保存済み返信がありません", + "attachmentTooLarge": "{{fileName}} は5MBを超えています。", + "quotaMac": "このワークスペースは現在のプランの連絡先上限に達しました。メッセージの送信を続けるにはアップグレードしてください。", + "quotaTrialExpired": "このワークスペースの試用期間またはプランが終了しました。メッセージの送信を続けるにはアップグレードしてください。", + "quotaUnknown": "このワークスペースは現在利用できません。", + "dismiss": "閉じる", + "grantCameraPermission": "写真を撮るにはカメラへのアクセスが必要です。", + "grantPermission": "許可する", + "failedToSendPhoto": "写真の送信に失敗しました。", + "typing": "Typing…", + "attach": "Attach", + "camera": "Camera", + "photoLibrary": "Photo library", + "document": "Document", + "sendFlow": "Send flow", + "sendSequence": "Send sequence", + "sendingFlow": "Sending {{name}}…", + "flowSendFailed": "Couldn't send flow. Please try again.", + "savedReplies": "Saved replies", + "attachmentsTooLarge": "{{count}} file(s) are larger than 5MB and weren't sent: {{names}}", + "editingMessage": "Editing message", + "replyingTo": "Replying to {{name}}", + "reply": "Reply", + "editAndResend": "Edit & resend", + "repliedAttachment": "Attachment", + "repliedMessageUnavailable": "Original message unavailable", + "messageDeleted": "Message deleted", + "senderAgent": "You", + "senderBot": "Bot", + "senderContact": "Contact", + "scrollToBottom": "Scroll to latest messages", + "bubbleA11yLabel": "{{sender}} said {{text}}, {{time}}, {{status}}", + "status": { + "pending": "Sending", + "sent": "Sent", + "failed": "Failed to send" + }, + "locationShared": "Location shared", + "unsupportedContent": "[{{contentType}}] Unsupported on mobile", + "activityFallback": "Activity" + }, + "contacts": { + "title": "連絡先", + "contactTitle": "連絡先", + "empty": "連絡先がありません", + "unknown": "不明", + "blocked": "ブロック済み", + "email": "メール", + "phone": "電話番号", + "locale": "言語", + "timezone": "タイムゾーン", + "location": "所在地", + "customFields": "カスタムフィールド", + "addCustomField": "カスタムフィールドを追加", + "assignedTags": "タグ", + "noTags": "タグなし", + "addTag": "タグを追加", + "noSequences": "どのシーケンスにも登録されていません", + "sequenceStep": "ステップ {{step}}", + "completed": "完了", + "active": "有効", + "tabInfo": "情報", + "tabTags": "タグ", + "tabSequences": "シーケンス", + "tabNotes": "Notes", + "noNotes": "No notes yet", + "unknownAuthor": "Unknown", + "message": "Message", + "assign": "Assign", + "addToSequence": "Add to sequence", + "noActiveSequences": "No active sequences", + "sequenceSubscribers": "{{count}} subscriber", + "sequenceSubscribers_other": "{{count}} subscribers", + "enrollSelected": "Add ({{count}})", + "sequenceEnrolled": "Contact enrolled in sequence", + "sequenceEnrollFailed": "Couldn't enroll contact in sequence" + }, + "flows": { + "title": "フロー", + "empty": "フローがありません", + "readOnlyNote": "フローはモバイルでは読み取り専用です。", + "active": "有効", + "inactive": "無効", + "nodes": "{{count}} 個のノード", + "nodes_other": "{{count}} 個のノード" + }, + "settings": { + "title": "設定", + "darkMode": "ダークモード", + "theme": "テーマ", + "themeSystem": "システム", + "themeLight": "ライト", + "themeDark": "ダーク", + "language": "言語", + "members": "メンバー", + "profile": "プロフィール", + "switchWorkspace": "ワークスペースを切り替え", + "pushNotifications": "プッシュ通知", + "pushNotificationsDescription": "このデバイスで新しいメッセージの通知を受け取ります。", + "teams": "チーム", + "noMembers": "メンバーがいません", + "noTeams": "チームがありません", + "roleOwner": "オーナー", + "roleAgent": "エージェント", + "restartRequiredTitle": "再起動が必要です", + "restartRequiredMessage": "この言語に切り替えると、アプリのレイアウトの方向が変わります。今すぐ再起動して適用しますか?", + "restartManuallyMessage": "この言語に切り替えると、アプリのレイアウトの方向が変わります。アプリを閉じて再度開くと適用されます。", + "restartNow": "今すぐ再起動", + "restartLater": "後で", + "notifications": "Notifications", + "appearance": "Appearance", + "workspace": "Workspace", + "version": "Version {{version}}" + }, + "profile": { + "plan": "プラン" + }, + "errors": { + "unauthorized": "セッションの有効期限が切れました。再度サインインしてください。", + "workspaceBlocked": "このワークスペースは現在利用できません。", + "workspaceBlockedTitle": "ワークスペースを利用できません", + "workspaceBlockedMac": "このワークスペースは現在のプランの連絡先上限に達しました。続けるにはアップグレードしてください。", + "workspaceBlockedTrialExpired": "このワークスペースの試用期間またはプランが終了しました。続けるにはアップグレードしてください。", + "notFound": "見つかりません", + "notFoundTitle": "ページが見つかりません", + "notFoundBody": "お探しのページは存在しません。", + "goHome": "会話に戻る", + "unknown": "問題が発生しました。もう一度お試しください。" + }, + "realtime": { + "online": "Back online", + "connecting": "Connecting…", + "reconnecting": "Reconnecting…", + "offline": "You're offline" + }, + "notifications": { + "androidChannelName": "Default" + } +} diff --git a/src/i18n/locales/nl.json b/src/i18n/locales/nl.json new file mode 100644 index 0000000..f4227ec --- /dev/null +++ b/src/i18n/locales/nl.json @@ -0,0 +1,259 @@ +{ + "common": { + "loading": "Laden…", + "error": "Er is iets misgegaan", + "retry": "Opnieuw proberen", + "cancel": "Annuleren", + "save": "Opslaan", + "done": "Klaar", + "search": "Zoeken", + "confirm": "Bevestigen", + "delete": "Verwijderen", + "clear": "Clear", + "close": "Close", + "today": "Today", + "yesterday": "Yesterday", + "removeItem": "Remove {{label}}", + "comingSoon": "Coming soon", + "back": "Back" + }, + "auth": { + "signIn": "Inloggen", + "email": "E-mail", + "password": "Wachtwoord", + "forgotPassword": "Wachtwoord vergeten?", + "signOut": "Uitloggen", + "changePassword": "Wachtwoord wijzigen", + "changePasswordRequired": "Je moet een nieuw wachtwoord instellen voordat je verdergaat.", + "currentPassword": "Huidig wachtwoord", + "newPassword": "Nieuw wachtwoord", + "sendResetLink": "Reset-link versturen", + "resetLinkSent": "Als er een account bestaat voor dit e-mailadres, is er een reset-link verzonden.", + "orContinueWith": "Of ga verder met", + "continueWithGoogle": "Doorgaan met Google", + "continueWithFacebook": "Doorgaan met Facebook", + "socialSignInFailed": "Inloggen niet voltooid. Probeer het opnieuw.", + "signOutConfirmTitle": "Sign out?", + "signOutConfirmBody": "You'll need to sign in again to access your workspaces.", + "showPassword": "Show password", + "hidePassword": "Hide password", + "signInSubtitle": "Sign in to keep every conversation moving.", + "forgotPasswordSubtitle": "Enter your email and we'll send you a link to reset your password.", + "backToSignIn": "Back to sign in" + }, + "workspaces": { + "title": "Workspaces", + "empty": "Nog geen workspaces", + "current": "Current workspace", + "switch": "Switch", + "switchConfirmTitle": "Switch workspace?", + "switchConfirmBody": "Switch to {{name}}? You'll leave your current workspace." + }, + "inbox": { + "title": "Inbox" + }, + "conversations": { + "title": "Gesprekken", + "empty": "Geen gesprekken", + "filters": "Filters", + "channel": "Kanaal", + "handledBy": "Behandeld door", + "status": "Status", + "clearFilters": "Filters wissen", + "filterAll": "Alle", + "channelMessenger": "Messenger", + "channelInstagram": "Instagram", + "channelWhatsapp": "WhatsApp", + "channelWebchat": "Webchat", + "channelZalo": "Zalo", + "channelTelegram": "Telegram", + "channelEmail": "E-mail", + "channelTiktok": "TikTok", + "statusUnread": "Ongelezen", + "statusNoAdminReply": "Geen reactie van het team", + "statusFollowUp": "In opvolging", + "statusArchived": "Gearchiveerd", + "statusBlocked": "Geblokkeerd", + "handledByBot": "Behandeld door bot", + "handledByHuman": "Behandeld door mens", + "unassign": "Toewijzing opheffen", + "noOtherMembers": "Deze workspace heeft nog geen andere leden.", + "unnamed": "Naamloos", + "unknownContact": "Unknown contact", + "attachmentPreview": "📎 {{count}} attachment(s)", + "a11yUnread": "unread", + "markRead": "Mark as read", + "markUnread": "Mark as unread", + "enableBot": "Enable bot", + "disableBot": "Disable bot", + "archive": "Archive", + "archiveUndone": "Conversation restored", + "undo": "Undo", + "botActive": "Bot handling", + "botPaused": "Bot paused", + "assignedTo": "Assigned to {{name}}", + "quickFilterMine": "Mine", + "emptyFiltered": "No conversations match your filters", + "botOn": "On", + "botOff": "Off", + "channelOmnichannel": "Omnichannel", + "botOnOffLabel": "Bot", + "actions": "Conversation actions", + "follow": "Follow", + "unfollow": "Unfollow", + "assign": "Assign", + "unarchive": "Unarchive", + "archiveConfirm": "Archive this conversation? You can find it again later in archived conversations.", + "unarchiveConfirm": "Restore this conversation from the archive?" + }, + "chat": { + "typeAMessage": "Typ een bericht", + "send": "Verstuur", + "failedToSend": "Verzenden mislukt", + "failedToSendTapRetry": "Verzenden mislukt. Tik om opnieuw te proberen.", + "retrySend": "Opnieuw proberen", + "noMessages": "Nog geen berichten", + "edit": "Bewerken", + "like": "Vind ik leuk", + "hide": "Verbergen", + "delete": "Verwijderen", + "searchSavedReplies": "Opgeslagen antwoorden zoeken", + "noSavedReplies": "Geen opgeslagen antwoorden", + "attachmentTooLarge": "{{fileName}} is groter dan 5 MB.", + "quotaMac": "Deze workspace heeft de contactlimiet van het huidige plan bereikt. Upgrade om te blijven berichten sturen.", + "quotaTrialExpired": "De proefperiode of het plan van deze workspace is verlopen. Upgrade om te blijven berichten sturen.", + "quotaUnknown": "Deze workspace is momenteel niet beschikbaar.", + "dismiss": "Sluiten", + "grantCameraPermission": "Cameratoegang is vereist om een foto te maken.", + "grantPermission": "Toegang verlenen", + "failedToSendPhoto": "Foto verzenden mislukt.", + "typing": "Typing…", + "attach": "Attach", + "camera": "Camera", + "photoLibrary": "Photo library", + "document": "Document", + "sendFlow": "Send flow", + "sendSequence": "Send sequence", + "sendingFlow": "Sending {{name}}…", + "flowSendFailed": "Couldn't send flow. Please try again.", + "savedReplies": "Saved replies", + "attachmentsTooLarge": "{{count}} file(s) are larger than 5MB and weren't sent: {{names}}", + "editingMessage": "Editing message", + "replyingTo": "Replying to {{name}}", + "reply": "Reply", + "editAndResend": "Edit & resend", + "repliedAttachment": "Attachment", + "repliedMessageUnavailable": "Original message unavailable", + "messageDeleted": "Message deleted", + "senderAgent": "You", + "senderBot": "Bot", + "senderContact": "Contact", + "scrollToBottom": "Scroll to latest messages", + "bubbleA11yLabel": "{{sender}} said {{text}}, {{time}}, {{status}}", + "status": { + "pending": "Sending", + "sent": "Sent", + "failed": "Failed to send" + }, + "locationShared": "Location shared", + "unsupportedContent": "[{{contentType}}] Unsupported on mobile", + "activityFallback": "Activity" + }, + "contacts": { + "title": "Contacten", + "contactTitle": "Contact", + "empty": "Geen contacten", + "unknown": "Onbekend", + "blocked": "Geblokkeerd", + "email": "E-mail", + "phone": "Telefoon", + "locale": "Taal", + "timezone": "Tijdzone", + "location": "Locatie", + "customFields": "Aangepaste velden", + "addCustomField": "Aangepast veld toevoegen", + "assignedTags": "Tags", + "noTags": "Geen tags", + "addTag": "Tag toevoegen", + "noSequences": "Niet ingeschreven voor een reeks", + "sequenceStep": "Stap {{step}}", + "completed": "Voltooid", + "active": "Actief", + "tabInfo": "Info", + "tabTags": "Tags", + "tabSequences": "Reeksen", + "tabNotes": "Notes", + "noNotes": "No notes yet", + "unknownAuthor": "Unknown", + "message": "Message", + "assign": "Assign", + "addToSequence": "Add to sequence", + "noActiveSequences": "No active sequences", + "sequenceSubscribers": "{{count}} subscriber", + "sequenceSubscribers_other": "{{count}} subscribers", + "enrollSelected": "Add ({{count}})", + "sequenceEnrolled": "Contact enrolled in sequence", + "sequenceEnrollFailed": "Couldn't enroll contact in sequence" + }, + "flows": { + "title": "Flows", + "empty": "Geen flows", + "readOnlyNote": "Flows zijn alleen-lezen op mobiel.", + "active": "Actief", + "inactive": "Inactief", + "nodes": "{{count}} node", + "nodes_other": "{{count}} nodes" + }, + "settings": { + "title": "Instellingen", + "darkMode": "Donkere modus", + "theme": "Thema", + "themeSystem": "Systeem", + "themeLight": "Licht", + "themeDark": "Donker", + "language": "Taal", + "members": "Leden", + "profile": "Profiel", + "switchWorkspace": "Workspace wisselen", + "pushNotifications": "Pushmeldingen", + "pushNotificationsDescription": "Ontvang meldingen voor nieuwe berichten op dit apparaat.", + "teams": "Teams", + "noMembers": "Geen leden", + "noTeams": "Geen teams", + "roleOwner": "Eigenaar", + "roleAgent": "Agent", + "restartRequiredTitle": "Herstart vereist", + "restartRequiredMessage": "Overschakelen naar deze taal verandert de lay-outrichting van de app. Nu herstarten om toe te passen?", + "restartManuallyMessage": "Overschakelen naar deze taal verandert de lay-outrichting van de app. Sluit en open de app opnieuw om toe te passen.", + "restartNow": "Nu herstarten", + "restartLater": "Later", + "notifications": "Notifications", + "appearance": "Appearance", + "workspace": "Workspace", + "version": "Version {{version}}" + }, + "profile": { + "plan": "Abonnement" + }, + "errors": { + "unauthorized": "Je sessie is verlopen. Log opnieuw in.", + "workspaceBlocked": "Deze workspace is momenteel niet beschikbaar.", + "workspaceBlockedTitle": "Workspace niet beschikbaar", + "workspaceBlockedMac": "Deze workspace heeft de contactlimiet van het huidige plan bereikt. Upgrade om door te gaan.", + "workspaceBlockedTrialExpired": "De proefperiode of het plan van deze workspace is verlopen. Upgrade om door te gaan.", + "notFound": "Niet gevonden", + "notFoundTitle": "Pagina niet gevonden", + "notFoundBody": "De pagina die je zoekt bestaat niet.", + "goHome": "Naar gesprekken", + "unknown": "Er is iets misgegaan. Probeer het opnieuw." + }, + "realtime": { + "online": "Back online", + "connecting": "Connecting…", + "reconnecting": "Reconnecting…", + "offline": "You're offline" + }, + "notifications": { + "androidChannelName": "Default" + } +} diff --git a/src/i18n/locales/pt-BR.json b/src/i18n/locales/pt-BR.json new file mode 100644 index 0000000..fe8cc78 --- /dev/null +++ b/src/i18n/locales/pt-BR.json @@ -0,0 +1,259 @@ +{ + "common": { + "loading": "Carregando…", + "error": "Algo deu errado", + "retry": "Tentar novamente", + "cancel": "Cancelar", + "save": "Salvar", + "done": "Concluído", + "search": "Buscar", + "confirm": "Confirmar", + "delete": "Excluir", + "clear": "Clear", + "close": "Close", + "today": "Today", + "yesterday": "Yesterday", + "removeItem": "Remove {{label}}", + "comingSoon": "Coming soon", + "back": "Back" + }, + "auth": { + "signIn": "Entrar", + "email": "E-mail", + "password": "Senha", + "forgotPassword": "Esqueceu a senha?", + "signOut": "Sair", + "changePassword": "Alterar senha", + "changePasswordRequired": "Você precisa definir uma nova senha antes de continuar.", + "currentPassword": "Senha atual", + "newPassword": "Nova senha", + "sendResetLink": "Enviar link de redefinição", + "resetLinkSent": "Se existir uma conta com esse e-mail, um link de redefinição foi enviado.", + "orContinueWith": "Ou continue com", + "continueWithGoogle": "Continuar com o Google", + "continueWithFacebook": "Continuar com o Facebook", + "socialSignInFailed": "O login não foi concluído. Tente novamente.", + "signOutConfirmTitle": "Sign out?", + "signOutConfirmBody": "You'll need to sign in again to access your workspaces.", + "showPassword": "Show password", + "hidePassword": "Hide password", + "signInSubtitle": "Sign in to keep every conversation moving.", + "forgotPasswordSubtitle": "Enter your email and we'll send you a link to reset your password.", + "backToSignIn": "Back to sign in" + }, + "workspaces": { + "title": "Workspaces", + "empty": "Ainda não há workspaces", + "current": "Current workspace", + "switch": "Switch", + "switchConfirmTitle": "Switch workspace?", + "switchConfirmBody": "Switch to {{name}}? You'll leave your current workspace." + }, + "inbox": { + "title": "Inbox" + }, + "conversations": { + "title": "Conversas", + "empty": "Nenhuma conversa", + "filters": "Filtros", + "channel": "Canal", + "handledBy": "Atendida por", + "status": "Status", + "clearFilters": "Limpar filtros", + "filterAll": "Todos", + "channelMessenger": "Messenger", + "channelInstagram": "Instagram", + "channelWhatsapp": "WhatsApp", + "channelWebchat": "Chat do site", + "channelZalo": "Zalo", + "channelTelegram": "Telegram", + "channelEmail": "E-mail", + "channelTiktok": "TikTok", + "statusUnread": "Não lida", + "statusNoAdminReply": "Sem resposta do time", + "statusFollowUp": "Em acompanhamento", + "statusArchived": "Arquivada", + "statusBlocked": "Bloqueada", + "handledByBot": "Atendida pelo bot", + "handledByHuman": "Atendida por uma pessoa", + "unassign": "Remover atribuição", + "noOtherMembers": "Este workspace ainda não tem outros membros.", + "unnamed": "Sem nome", + "unknownContact": "Unknown contact", + "attachmentPreview": "📎 {{count}} attachment(s)", + "a11yUnread": "unread", + "markRead": "Mark as read", + "markUnread": "Mark as unread", + "enableBot": "Enable bot", + "disableBot": "Disable bot", + "archive": "Archive", + "archiveUndone": "Conversation restored", + "undo": "Undo", + "botActive": "Bot handling", + "botPaused": "Bot paused", + "assignedTo": "Assigned to {{name}}", + "quickFilterMine": "Mine", + "emptyFiltered": "No conversations match your filters", + "botOn": "On", + "botOff": "Off", + "channelOmnichannel": "Omnichannel", + "botOnOffLabel": "Bot", + "actions": "Conversation actions", + "follow": "Follow", + "unfollow": "Unfollow", + "assign": "Assign", + "unarchive": "Unarchive", + "archiveConfirm": "Archive this conversation? You can find it again later in archived conversations.", + "unarchiveConfirm": "Restore this conversation from the archive?" + }, + "chat": { + "typeAMessage": "Digite uma mensagem", + "send": "Enviar", + "failedToSend": "Falha ao enviar", + "failedToSendTapRetry": "Falha ao enviar. Toque para tentar novamente.", + "retrySend": "Tentar novamente", + "noMessages": "Ainda não há mensagens", + "edit": "Editar", + "like": "Curtir", + "hide": "Ocultar", + "delete": "Excluir", + "searchSavedReplies": "Buscar respostas salvas", + "noSavedReplies": "Nenhuma resposta salva", + "attachmentTooLarge": "{{fileName}} tem mais de 5 MB.", + "quotaMac": "Este workspace atingiu o limite de contatos do plano atual. Faça upgrade para continuar enviando mensagens.", + "quotaTrialExpired": "O período de teste ou o plano deste workspace expirou. Faça upgrade para continuar enviando mensagens.", + "quotaUnknown": "Este workspace está indisponível no momento.", + "dismiss": "Dispensar", + "grantCameraPermission": "É necessário acesso à câmera para tirar uma foto.", + "grantPermission": "Conceder permissão", + "failedToSendPhoto": "Falha ao enviar a foto.", + "typing": "Typing…", + "attach": "Attach", + "camera": "Camera", + "photoLibrary": "Photo library", + "document": "Document", + "sendFlow": "Send flow", + "sendSequence": "Send sequence", + "sendingFlow": "Sending {{name}}…", + "flowSendFailed": "Couldn't send flow. Please try again.", + "savedReplies": "Saved replies", + "attachmentsTooLarge": "{{count}} file(s) are larger than 5MB and weren't sent: {{names}}", + "editingMessage": "Editing message", + "replyingTo": "Replying to {{name}}", + "reply": "Reply", + "editAndResend": "Edit & resend", + "repliedAttachment": "Attachment", + "repliedMessageUnavailable": "Original message unavailable", + "messageDeleted": "Message deleted", + "senderAgent": "You", + "senderBot": "Bot", + "senderContact": "Contact", + "scrollToBottom": "Scroll to latest messages", + "bubbleA11yLabel": "{{sender}} said {{text}}, {{time}}, {{status}}", + "status": { + "pending": "Sending", + "sent": "Sent", + "failed": "Failed to send" + }, + "locationShared": "Location shared", + "unsupportedContent": "[{{contentType}}] Unsupported on mobile", + "activityFallback": "Activity" + }, + "contacts": { + "title": "Contatos", + "contactTitle": "Contato", + "empty": "Nenhum contato", + "unknown": "Desconhecido", + "blocked": "Bloqueado", + "email": "E-mail", + "phone": "Telefone", + "locale": "Idioma", + "timezone": "Fuso horário", + "location": "Localização", + "customFields": "Campos personalizados", + "addCustomField": "Adicionar campo personalizado", + "assignedTags": "Tags", + "noTags": "Sem tags", + "addTag": "Adicionar tag", + "noSequences": "Não inscrito em nenhuma sequência", + "sequenceStep": "Etapa {{step}}", + "completed": "Concluída", + "active": "Ativa", + "tabInfo": "Info", + "tabTags": "Tags", + "tabSequences": "Sequências", + "tabNotes": "Notes", + "noNotes": "No notes yet", + "unknownAuthor": "Unknown", + "message": "Message", + "assign": "Assign", + "addToSequence": "Add to sequence", + "noActiveSequences": "No active sequences", + "sequenceSubscribers": "{{count}} subscriber", + "sequenceSubscribers_other": "{{count}} subscribers", + "enrollSelected": "Add ({{count}})", + "sequenceEnrolled": "Contact enrolled in sequence", + "sequenceEnrollFailed": "Couldn't enroll contact in sequence" + }, + "flows": { + "title": "Fluxos", + "empty": "Nenhum fluxo", + "readOnlyNote": "Os fluxos são somente leitura no celular.", + "active": "Ativo", + "inactive": "Inativo", + "nodes": "{{count}} nó", + "nodes_other": "{{count}} nós" + }, + "settings": { + "title": "Configurações", + "darkMode": "Modo escuro", + "theme": "Tema", + "themeSystem": "Sistema", + "themeLight": "Claro", + "themeDark": "Escuro", + "language": "Idioma", + "members": "Membros", + "profile": "Perfil", + "switchWorkspace": "Trocar workspace", + "pushNotifications": "Notificações push", + "pushNotificationsDescription": "Receba notificações de novas mensagens neste dispositivo.", + "teams": "Times", + "noMembers": "Nenhum membro", + "noTeams": "Nenhum time", + "roleOwner": "Proprietário", + "roleAgent": "Agente", + "restartRequiredTitle": "Reinício necessário", + "restartRequiredMessage": "Mudar para este idioma altera a direção do layout do app. Reiniciar agora para aplicar?", + "restartManuallyMessage": "Mudar para este idioma altera a direção do layout do app. Feche e reabra o app para aplicar.", + "restartNow": "Reiniciar agora", + "restartLater": "Mais tarde", + "notifications": "Notifications", + "appearance": "Appearance", + "workspace": "Workspace", + "version": "Version {{version}}" + }, + "profile": { + "plan": "Plano" + }, + "errors": { + "unauthorized": "Sua sessão expirou. Faça login novamente.", + "workspaceBlocked": "Este workspace está indisponível no momento.", + "workspaceBlockedTitle": "Workspace indisponível", + "workspaceBlockedMac": "Este workspace atingiu o limite de contatos do plano atual. Faça upgrade para continuar.", + "workspaceBlockedTrialExpired": "O período de teste ou o plano deste workspace expirou. Faça upgrade para continuar.", + "notFound": "Não encontrado", + "notFoundTitle": "Página não encontrada", + "notFoundBody": "A página que você procura não existe.", + "goHome": "Ir para conversas", + "unknown": "Algo deu errado. Tente novamente." + }, + "realtime": { + "online": "Back online", + "connecting": "Connecting…", + "reconnecting": "Reconnecting…", + "offline": "You're offline" + }, + "notifications": { + "androidChannelName": "Default" + } +} diff --git a/src/i18n/locales/pt-PT.json b/src/i18n/locales/pt-PT.json new file mode 100644 index 0000000..7065e01 --- /dev/null +++ b/src/i18n/locales/pt-PT.json @@ -0,0 +1,259 @@ +{ + "common": { + "loading": "A carregar…", + "error": "Algo correu mal", + "retry": "Tentar novamente", + "cancel": "Cancelar", + "save": "Guardar", + "done": "Concluído", + "search": "Pesquisar", + "confirm": "Confirmar", + "delete": "Eliminar", + "clear": "Clear", + "close": "Close", + "today": "Today", + "yesterday": "Yesterday", + "removeItem": "Remove {{label}}", + "comingSoon": "Coming soon", + "back": "Back" + }, + "auth": { + "signIn": "Iniciar sessão", + "email": "E-mail", + "password": "Palavra-passe", + "forgotPassword": "Esqueceu-se da palavra-passe?", + "signOut": "Terminar sessão", + "changePassword": "Alterar palavra-passe", + "changePasswordRequired": "Tem de definir uma nova palavra-passe antes de continuar.", + "currentPassword": "Palavra-passe atual", + "newPassword": "Nova palavra-passe", + "sendResetLink": "Enviar link de reposição", + "resetLinkSent": "Se existir uma conta com este e-mail, foi enviado um link de reposição.", + "orContinueWith": "Ou continue com", + "continueWithGoogle": "Continuar com o Google", + "continueWithFacebook": "Continuar com o Facebook", + "socialSignInFailed": "O início de sessão não foi concluído. Tente novamente.", + "signOutConfirmTitle": "Sign out?", + "signOutConfirmBody": "You'll need to sign in again to access your workspaces.", + "showPassword": "Show password", + "hidePassword": "Hide password", + "signInSubtitle": "Sign in to keep every conversation moving.", + "forgotPasswordSubtitle": "Enter your email and we'll send you a link to reset your password.", + "backToSignIn": "Back to sign in" + }, + "workspaces": { + "title": "Espaços de trabalho", + "empty": "Ainda não existem espaços de trabalho", + "current": "Current workspace", + "switch": "Switch", + "switchConfirmTitle": "Switch workspace?", + "switchConfirmBody": "Switch to {{name}}? You'll leave your current workspace." + }, + "inbox": { + "title": "Inbox" + }, + "conversations": { + "title": "Conversas", + "empty": "Sem conversas", + "filters": "Filtros", + "channel": "Canal", + "handledBy": "Atendida por", + "status": "Estado", + "clearFilters": "Limpar filtros", + "filterAll": "Todas", + "channelMessenger": "Messenger", + "channelInstagram": "Instagram", + "channelWhatsapp": "WhatsApp", + "channelWebchat": "Chat do site", + "channelZalo": "Zalo", + "channelTelegram": "Telegram", + "channelEmail": "E-mail", + "channelTiktok": "TikTok", + "statusUnread": "Não lida", + "statusNoAdminReply": "Sem resposta da equipa", + "statusFollowUp": "Em acompanhamento", + "statusArchived": "Arquivada", + "statusBlocked": "Bloqueada", + "handledByBot": "Atendida pelo bot", + "handledByHuman": "Atendida por uma pessoa", + "unassign": "Remover atribuição", + "noOtherMembers": "Este espaço de trabalho ainda não tem outros membros.", + "unnamed": "Sem nome", + "unknownContact": "Unknown contact", + "attachmentPreview": "📎 {{count}} attachment(s)", + "a11yUnread": "unread", + "markRead": "Mark as read", + "markUnread": "Mark as unread", + "enableBot": "Enable bot", + "disableBot": "Disable bot", + "archive": "Archive", + "archiveUndone": "Conversation restored", + "undo": "Undo", + "botActive": "Bot handling", + "botPaused": "Bot paused", + "assignedTo": "Assigned to {{name}}", + "quickFilterMine": "Mine", + "emptyFiltered": "No conversations match your filters", + "botOn": "On", + "botOff": "Off", + "channelOmnichannel": "Omnichannel", + "botOnOffLabel": "Bot", + "actions": "Conversation actions", + "follow": "Follow", + "unfollow": "Unfollow", + "assign": "Assign", + "unarchive": "Unarchive", + "archiveConfirm": "Archive this conversation? You can find it again later in archived conversations.", + "unarchiveConfirm": "Restore this conversation from the archive?" + }, + "chat": { + "typeAMessage": "Escreva uma mensagem", + "send": "Enviar", + "failedToSend": "Falha ao enviar", + "failedToSendTapRetry": "Falha ao enviar. Toque para tentar novamente.", + "retrySend": "Tentar novamente", + "noMessages": "Ainda não existem mensagens", + "edit": "Editar", + "like": "Gosto", + "hide": "Ocultar", + "delete": "Eliminar", + "searchSavedReplies": "Pesquisar respostas guardadas", + "noSavedReplies": "Sem respostas guardadas", + "attachmentTooLarge": "{{fileName}} tem mais de 5 MB.", + "quotaMac": "Este espaço de trabalho atingiu o limite de contactos do plano atual. Faça upgrade para continuar a enviar mensagens.", + "quotaTrialExpired": "O período de teste ou o plano deste espaço de trabalho expirou. Faça upgrade para continuar a enviar mensagens.", + "quotaUnknown": "Este espaço de trabalho está indisponível de momento.", + "dismiss": "Dispensar", + "grantCameraPermission": "É necessário acesso à câmara para tirar uma fotografia.", + "grantPermission": "Conceder permissão", + "failedToSendPhoto": "Falha ao enviar a fotografia.", + "typing": "Typing…", + "attach": "Attach", + "camera": "Camera", + "photoLibrary": "Photo library", + "document": "Document", + "sendFlow": "Send flow", + "sendSequence": "Send sequence", + "sendingFlow": "Sending {{name}}…", + "flowSendFailed": "Couldn't send flow. Please try again.", + "savedReplies": "Saved replies", + "attachmentsTooLarge": "{{count}} file(s) are larger than 5MB and weren't sent: {{names}}", + "editingMessage": "Editing message", + "replyingTo": "Replying to {{name}}", + "reply": "Reply", + "editAndResend": "Edit & resend", + "repliedAttachment": "Attachment", + "repliedMessageUnavailable": "Original message unavailable", + "messageDeleted": "Message deleted", + "senderAgent": "You", + "senderBot": "Bot", + "senderContact": "Contact", + "scrollToBottom": "Scroll to latest messages", + "bubbleA11yLabel": "{{sender}} said {{text}}, {{time}}, {{status}}", + "status": { + "pending": "Sending", + "sent": "Sent", + "failed": "Failed to send" + }, + "locationShared": "Location shared", + "unsupportedContent": "[{{contentType}}] Unsupported on mobile", + "activityFallback": "Activity" + }, + "contacts": { + "title": "Contactos", + "contactTitle": "Contacto", + "empty": "Sem contactos", + "unknown": "Desconhecido", + "blocked": "Bloqueado", + "email": "E-mail", + "phone": "Telefone", + "locale": "Idioma", + "timezone": "Fuso horário", + "location": "Localização", + "customFields": "Campos personalizados", + "addCustomField": "Adicionar campo personalizado", + "assignedTags": "Etiquetas", + "noTags": "Sem etiquetas", + "addTag": "Adicionar etiqueta", + "noSequences": "Não inscrito em nenhuma sequência", + "sequenceStep": "Passo {{step}}", + "completed": "Concluída", + "active": "Ativa", + "tabInfo": "Info", + "tabTags": "Etiquetas", + "tabSequences": "Sequências", + "tabNotes": "Notes", + "noNotes": "No notes yet", + "unknownAuthor": "Unknown", + "message": "Message", + "assign": "Assign", + "addToSequence": "Add to sequence", + "noActiveSequences": "No active sequences", + "sequenceSubscribers": "{{count}} subscriber", + "sequenceSubscribers_other": "{{count}} subscribers", + "enrollSelected": "Add ({{count}})", + "sequenceEnrolled": "Contact enrolled in sequence", + "sequenceEnrollFailed": "Couldn't enroll contact in sequence" + }, + "flows": { + "title": "Fluxos", + "empty": "Sem fluxos", + "readOnlyNote": "Os fluxos são apenas de leitura no telemóvel.", + "active": "Ativo", + "inactive": "Inativo", + "nodes": "{{count}} nó", + "nodes_other": "{{count}} nós" + }, + "settings": { + "title": "Definições", + "darkMode": "Modo escuro", + "theme": "Tema", + "themeSystem": "Sistema", + "themeLight": "Claro", + "themeDark": "Escuro", + "language": "Idioma", + "members": "Membros", + "profile": "Perfil", + "switchWorkspace": "Mudar de espaço de trabalho", + "pushNotifications": "Notificações push", + "pushNotificationsDescription": "Receba notificações de novas mensagens neste dispositivo.", + "teams": "Equipas", + "noMembers": "Sem membros", + "noTeams": "Sem equipas", + "roleOwner": "Proprietário", + "roleAgent": "Agente", + "restartRequiredTitle": "Reinício necessário", + "restartRequiredMessage": "Mudar para este idioma altera a direção do esquema da aplicação. Reiniciar agora para aplicar?", + "restartManuallyMessage": "Mudar para este idioma altera a direção do esquema da aplicação. Feche e reabra a aplicação para aplicar.", + "restartNow": "Reiniciar agora", + "restartLater": "Mais tarde", + "notifications": "Notifications", + "appearance": "Appearance", + "workspace": "Workspace", + "version": "Version {{version}}" + }, + "profile": { + "plan": "Plano" + }, + "errors": { + "unauthorized": "A sua sessão expirou. Inicie sessão novamente.", + "workspaceBlocked": "Este espaço de trabalho está indisponível de momento.", + "workspaceBlockedTitle": "Espaço de trabalho indisponível", + "workspaceBlockedMac": "Este espaço de trabalho atingiu o limite de contactos do plano atual. Faça upgrade para continuar.", + "workspaceBlockedTrialExpired": "O período de teste ou o plano deste espaço de trabalho expirou. Faça upgrade para continuar.", + "notFound": "Não encontrado", + "notFoundTitle": "Página não encontrada", + "notFoundBody": "A página que procura não existe.", + "goHome": "Ir para as conversas", + "unknown": "Algo correu mal. Tente novamente." + }, + "realtime": { + "online": "Back online", + "connecting": "Connecting…", + "reconnecting": "Reconnecting…", + "offline": "You're offline" + }, + "notifications": { + "androidChannelName": "Default" + } +} diff --git a/src/i18n/locales/ro.json b/src/i18n/locales/ro.json new file mode 100644 index 0000000..4a55005 --- /dev/null +++ b/src/i18n/locales/ro.json @@ -0,0 +1,260 @@ +{ + "common": { + "loading": "Se încarcă…", + "error": "Ceva nu a mers bine", + "retry": "Reîncearcă", + "cancel": "Anulează", + "save": "Salvează", + "done": "Gata", + "search": "Caută", + "confirm": "Confirmă", + "delete": "Șterge", + "clear": "Clear", + "close": "Close", + "today": "Today", + "yesterday": "Yesterday", + "removeItem": "Remove {{label}}", + "comingSoon": "Coming soon", + "back": "Back" + }, + "auth": { + "signIn": "Conectare", + "email": "E-mail", + "password": "Parolă", + "forgotPassword": "Ai uitat parola?", + "signOut": "Deconectare", + "changePassword": "Schimbă parola", + "changePasswordRequired": "Trebuie să setezi o parolă nouă înainte de a continua.", + "currentPassword": "Parola curentă", + "newPassword": "Parolă nouă", + "sendResetLink": "Trimite link de resetare", + "resetLinkSent": "Dacă există un cont pentru acest e-mail, a fost trimis un link de resetare.", + "orContinueWith": "Sau continuă cu", + "continueWithGoogle": "Continuă cu Google", + "continueWithFacebook": "Continuă cu Facebook", + "socialSignInFailed": "Conectarea nu s-a finalizat. Te rugăm să încerci din nou.", + "signOutConfirmTitle": "Sign out?", + "signOutConfirmBody": "You'll need to sign in again to access your workspaces.", + "showPassword": "Show password", + "hidePassword": "Hide password", + "signInSubtitle": "Sign in to keep every conversation moving.", + "forgotPasswordSubtitle": "Enter your email and we'll send you a link to reset your password.", + "backToSignIn": "Back to sign in" + }, + "workspaces": { + "title": "Spații de lucru", + "empty": "Niciun spațiu de lucru încă", + "current": "Current workspace", + "switch": "Switch", + "switchConfirmTitle": "Switch workspace?", + "switchConfirmBody": "Switch to {{name}}? You'll leave your current workspace." + }, + "inbox": { + "title": "Inbox" + }, + "conversations": { + "title": "Conversații", + "empty": "Nicio conversație", + "filters": "Filtre", + "channel": "Canal", + "handledBy": "Gestionată de", + "status": "Stare", + "clearFilters": "Șterge filtrele", + "filterAll": "Toate", + "channelMessenger": "Messenger", + "channelInstagram": "Instagram", + "channelWhatsapp": "WhatsApp", + "channelWebchat": "Chat web", + "channelZalo": "Zalo", + "channelTelegram": "Telegram", + "channelEmail": "E-mail", + "channelTiktok": "TikTok", + "statusUnread": "Necitit", + "statusNoAdminReply": "Fără răspuns din partea echipei", + "statusFollowUp": "În urmărire", + "statusArchived": "Arhivată", + "statusBlocked": "Blocată", + "handledByBot": "Gestionată de bot", + "handledByHuman": "Gestionată de o persoană", + "unassign": "Anulează atribuirea", + "noOtherMembers": "Acest spațiu de lucru nu are încă alți membri.", + "unnamed": "Fără nume", + "unknownContact": "Unknown contact", + "attachmentPreview": "📎 {{count}} attachment(s)", + "a11yUnread": "unread", + "markRead": "Mark as read", + "markUnread": "Mark as unread", + "enableBot": "Enable bot", + "disableBot": "Disable bot", + "archive": "Archive", + "archiveUndone": "Conversation restored", + "undo": "Undo", + "botActive": "Bot handling", + "botPaused": "Bot paused", + "assignedTo": "Assigned to {{name}}", + "quickFilterMine": "Mine", + "emptyFiltered": "No conversations match your filters", + "botOn": "On", + "botOff": "Off", + "channelOmnichannel": "Omnichannel", + "botOnOffLabel": "Bot", + "actions": "Conversation actions", + "follow": "Follow", + "unfollow": "Unfollow", + "assign": "Assign", + "unarchive": "Unarchive", + "archiveConfirm": "Archive this conversation? You can find it again later in archived conversations.", + "unarchiveConfirm": "Restore this conversation from the archive?" + }, + "chat": { + "typeAMessage": "Scrie un mesaj", + "send": "Trimite", + "failedToSend": "Trimiterea a eșuat", + "failedToSendTapRetry": "Trimiterea a eșuat. Atinge pentru a reîncerca.", + "retrySend": "Reîncearcă", + "noMessages": "Niciun mesaj încă", + "edit": "Editează", + "like": "Apreciază", + "hide": "Ascunde", + "delete": "Șterge", + "searchSavedReplies": "Caută răspunsuri salvate", + "noSavedReplies": "Niciun răspuns salvat", + "attachmentTooLarge": "{{fileName}} depășește 5 MB.", + "quotaMac": "Acest spațiu de lucru a atins limita de contacte a planului curent. Fă upgrade pentru a continua să trimiți mesaje.", + "quotaTrialExpired": "Perioada de probă sau planul acestui spațiu de lucru a expirat. Fă upgrade pentru a continua să trimiți mesaje.", + "quotaUnknown": "Acest spațiu de lucru nu este disponibil momentan.", + "dismiss": "Închide", + "grantCameraPermission": "Este necesar accesul la cameră pentru a face o fotografie.", + "grantPermission": "Acordă permisiune", + "failedToSendPhoto": "Trimiterea fotografiei a eșuat.", + "typing": "Typing…", + "attach": "Attach", + "camera": "Camera", + "photoLibrary": "Photo library", + "document": "Document", + "sendFlow": "Send flow", + "sendSequence": "Send sequence", + "sendingFlow": "Sending {{name}}…", + "flowSendFailed": "Couldn't send flow. Please try again.", + "savedReplies": "Saved replies", + "attachmentsTooLarge": "{{count}} file(s) are larger than 5MB and weren't sent: {{names}}", + "editingMessage": "Editing message", + "replyingTo": "Replying to {{name}}", + "reply": "Reply", + "editAndResend": "Edit & resend", + "repliedAttachment": "Attachment", + "repliedMessageUnavailable": "Original message unavailable", + "messageDeleted": "Message deleted", + "senderAgent": "You", + "senderBot": "Bot", + "senderContact": "Contact", + "scrollToBottom": "Scroll to latest messages", + "bubbleA11yLabel": "{{sender}} said {{text}}, {{time}}, {{status}}", + "status": { + "pending": "Sending", + "sent": "Sent", + "failed": "Failed to send" + }, + "locationShared": "Location shared", + "unsupportedContent": "[{{contentType}}] Unsupported on mobile", + "activityFallback": "Activity" + }, + "contacts": { + "title": "Contacte", + "contactTitle": "Contact", + "empty": "Niciun contact", + "unknown": "Necunoscut", + "blocked": "Blocat", + "email": "E-mail", + "phone": "Telefon", + "locale": "Limbă", + "timezone": "Fus orar", + "location": "Locație", + "customFields": "Câmpuri personalizate", + "addCustomField": "Adaugă câmp personalizat", + "assignedTags": "Etichete", + "noTags": "Fără etichete", + "addTag": "Adaugă etichetă", + "noSequences": "Nu este înscris în nicio secvență", + "sequenceStep": "Pasul {{step}}", + "completed": "Finalizată", + "active": "Activă", + "tabInfo": "Info", + "tabTags": "Etichete", + "tabSequences": "Secvențe", + "tabNotes": "Notes", + "noNotes": "No notes yet", + "unknownAuthor": "Unknown", + "message": "Message", + "assign": "Assign", + "addToSequence": "Add to sequence", + "noActiveSequences": "No active sequences", + "sequenceSubscribers": "{{count}} subscriber", + "sequenceSubscribers_other": "{{count}} subscribers", + "enrollSelected": "Add ({{count}})", + "sequenceEnrolled": "Contact enrolled in sequence", + "sequenceEnrollFailed": "Couldn't enroll contact in sequence" + }, + "flows": { + "title": "Fluxuri", + "empty": "Niciun flux", + "readOnlyNote": "Fluxurile sunt needitabile pe mobil.", + "active": "Activ", + "inactive": "Inactiv", + "nodes": "{{count}} nod", + "nodes_few": "{{count}} noduri", + "nodes_other": "{{count}} de noduri" + }, + "settings": { + "title": "Setări", + "darkMode": "Mod întunecat", + "theme": "Temă", + "themeSystem": "Sistem", + "themeLight": "Deschisă", + "themeDark": "Întunecată", + "language": "Limbă", + "members": "Membri", + "profile": "Profil", + "switchWorkspace": "Schimbă spațiul de lucru", + "pushNotifications": "Notificări push", + "pushNotificationsDescription": "Primești notificări despre mesajele noi pe acest dispozitiv.", + "teams": "Echipe", + "noMembers": "Niciun membru", + "noTeams": "Nicio echipă", + "roleOwner": "Proprietar", + "roleAgent": "Agent", + "restartRequiredTitle": "Este necesară repornirea", + "restartRequiredMessage": "Trecerea la această limbă schimbă direcția aspectului aplicației. Repornești acum pentru a o aplica?", + "restartManuallyMessage": "Trecerea la această limbă schimbă direcția aspectului aplicației. Închide și redeschide aplicația pentru a o aplica.", + "restartNow": "Repornește acum", + "restartLater": "Mai târziu", + "notifications": "Notifications", + "appearance": "Appearance", + "workspace": "Workspace", + "version": "Version {{version}}" + }, + "profile": { + "plan": "Plan" + }, + "errors": { + "unauthorized": "Sesiunea ta a expirat. Te rugăm să te conectezi din nou.", + "workspaceBlocked": "Acest spațiu de lucru nu este disponibil momentan.", + "workspaceBlockedTitle": "Spațiu de lucru indisponibil", + "workspaceBlockedMac": "Acest spațiu de lucru a atins limita de contacte a planului curent. Fă upgrade pentru a continua.", + "workspaceBlockedTrialExpired": "Perioada de probă sau planul acestui spațiu de lucru a expirat. Fă upgrade pentru a continua.", + "notFound": "Negăsit", + "notFoundTitle": "Pagina nu a fost găsită", + "notFoundBody": "Pagina pe care o cauți nu există.", + "goHome": "Mergi la conversații", + "unknown": "Ceva nu a mers bine. Te rugăm să încerci din nou." + }, + "realtime": { + "online": "Back online", + "connecting": "Connecting…", + "reconnecting": "Reconnecting…", + "offline": "You're offline" + }, + "notifications": { + "androidChannelName": "Default" + } +} diff --git a/src/i18n/locales/sv.json b/src/i18n/locales/sv.json new file mode 100644 index 0000000..ac9f12f --- /dev/null +++ b/src/i18n/locales/sv.json @@ -0,0 +1,259 @@ +{ + "common": { + "loading": "Läser in…", + "error": "Något gick fel", + "retry": "Försök igen", + "cancel": "Avbryt", + "save": "Spara", + "done": "Klar", + "search": "Sök", + "confirm": "Bekräfta", + "delete": "Ta bort", + "clear": "Clear", + "close": "Close", + "today": "Today", + "yesterday": "Yesterday", + "removeItem": "Remove {{label}}", + "comingSoon": "Coming soon", + "back": "Back" + }, + "auth": { + "signIn": "Logga in", + "email": "E-post", + "password": "Lösenord", + "forgotPassword": "Glömt lösenordet?", + "signOut": "Logga ut", + "changePassword": "Byt lösenord", + "changePasswordRequired": "Du måste ange ett nytt lösenord innan du fortsätter.", + "currentPassword": "Nuvarande lösenord", + "newPassword": "Nytt lösenord", + "sendResetLink": "Skicka återställningslänk", + "resetLinkSent": "Om ett konto finns för den e-postadressen har en återställningslänk skickats.", + "orContinueWith": "Eller fortsätt med", + "continueWithGoogle": "Fortsätt med Google", + "continueWithFacebook": "Fortsätt med Facebook", + "socialSignInFailed": "Inloggningen slutfördes inte. Försök igen.", + "signOutConfirmTitle": "Sign out?", + "signOutConfirmBody": "You'll need to sign in again to access your workspaces.", + "showPassword": "Show password", + "hidePassword": "Hide password", + "signInSubtitle": "Sign in to keep every conversation moving.", + "forgotPasswordSubtitle": "Enter your email and we'll send you a link to reset your password.", + "backToSignIn": "Back to sign in" + }, + "workspaces": { + "title": "Arbetsytor", + "empty": "Inga arbetsytor än", + "current": "Current workspace", + "switch": "Switch", + "switchConfirmTitle": "Switch workspace?", + "switchConfirmBody": "Switch to {{name}}? You'll leave your current workspace." + }, + "inbox": { + "title": "Inbox" + }, + "conversations": { + "title": "Konversationer", + "empty": "Inga konversationer", + "filters": "Filter", + "channel": "Kanal", + "handledBy": "Hanteras av", + "status": "Status", + "clearFilters": "Rensa filter", + "filterAll": "Alla", + "channelMessenger": "Messenger", + "channelInstagram": "Instagram", + "channelWhatsapp": "WhatsApp", + "channelWebchat": "Webbchatt", + "channelZalo": "Zalo", + "channelTelegram": "Telegram", + "channelEmail": "E-post", + "channelTiktok": "TikTok", + "statusUnread": "Oläst", + "statusNoAdminReply": "Inget svar från teamet", + "statusFollowUp": "Under uppföljning", + "statusArchived": "Arkiverad", + "statusBlocked": "Blockerad", + "handledByBot": "Hanteras av bot", + "handledByHuman": "Hanteras av person", + "unassign": "Ta bort tilldelning", + "noOtherMembers": "Den här arbetsytan har inga andra medlemmar än.", + "unnamed": "Namnlös", + "unknownContact": "Unknown contact", + "attachmentPreview": "📎 {{count}} attachment(s)", + "a11yUnread": "unread", + "markRead": "Mark as read", + "markUnread": "Mark as unread", + "enableBot": "Enable bot", + "disableBot": "Disable bot", + "archive": "Archive", + "archiveUndone": "Conversation restored", + "undo": "Undo", + "botActive": "Bot handling", + "botPaused": "Bot paused", + "assignedTo": "Assigned to {{name}}", + "quickFilterMine": "Mine", + "emptyFiltered": "No conversations match your filters", + "botOn": "On", + "botOff": "Off", + "channelOmnichannel": "Omnichannel", + "botOnOffLabel": "Bot", + "actions": "Conversation actions", + "follow": "Follow", + "unfollow": "Unfollow", + "assign": "Assign", + "unarchive": "Unarchive", + "archiveConfirm": "Archive this conversation? You can find it again later in archived conversations.", + "unarchiveConfirm": "Restore this conversation from the archive?" + }, + "chat": { + "typeAMessage": "Skriv ett meddelande", + "send": "Skicka", + "failedToSend": "Det gick inte att skicka", + "failedToSendTapRetry": "Det gick inte att skicka. Tryck för att försöka igen.", + "retrySend": "Försök igen", + "noMessages": "Inga meddelanden än", + "edit": "Redigera", + "like": "Gilla", + "hide": "Dölj", + "delete": "Ta bort", + "searchSavedReplies": "Sök sparade svar", + "noSavedReplies": "Inga sparade svar", + "attachmentTooLarge": "{{fileName}} är större än 5 MB.", + "quotaMac": "Den här arbetsytan har nått sin kontaktgräns på den aktuella planen. Uppgradera för att fortsätta skicka meddelanden.", + "quotaTrialExpired": "Provperioden eller planen för den här arbetsytan har gått ut. Uppgradera för att fortsätta skicka meddelanden.", + "quotaUnknown": "Den här arbetsytan är för tillfället inte tillgänglig.", + "dismiss": "Avfärda", + "grantCameraPermission": "Kameraåtkomst krävs för att ta en bild.", + "grantPermission": "Ge behörighet", + "failedToSendPhoto": "Det gick inte att skicka bilden.", + "typing": "Typing…", + "attach": "Attach", + "camera": "Camera", + "photoLibrary": "Photo library", + "document": "Document", + "sendFlow": "Send flow", + "sendSequence": "Send sequence", + "sendingFlow": "Sending {{name}}…", + "flowSendFailed": "Couldn't send flow. Please try again.", + "savedReplies": "Saved replies", + "attachmentsTooLarge": "{{count}} file(s) are larger than 5MB and weren't sent: {{names}}", + "editingMessage": "Editing message", + "replyingTo": "Replying to {{name}}", + "reply": "Reply", + "editAndResend": "Edit & resend", + "repliedAttachment": "Attachment", + "repliedMessageUnavailable": "Original message unavailable", + "messageDeleted": "Message deleted", + "senderAgent": "You", + "senderBot": "Bot", + "senderContact": "Contact", + "scrollToBottom": "Scroll to latest messages", + "bubbleA11yLabel": "{{sender}} said {{text}}, {{time}}, {{status}}", + "status": { + "pending": "Sending", + "sent": "Sent", + "failed": "Failed to send" + }, + "locationShared": "Location shared", + "unsupportedContent": "[{{contentType}}] Unsupported on mobile", + "activityFallback": "Activity" + }, + "contacts": { + "title": "Kontakter", + "contactTitle": "Kontakt", + "empty": "Inga kontakter", + "unknown": "Okänd", + "blocked": "Blockerad", + "email": "E-post", + "phone": "Telefon", + "locale": "Språk", + "timezone": "Tidszon", + "location": "Plats", + "customFields": "Anpassade fält", + "addCustomField": "Lägg till anpassat fält", + "assignedTags": "Taggar", + "noTags": "Inga taggar", + "addTag": "Lägg till tagg", + "noSequences": "Inte inskriven i någon sekvens", + "sequenceStep": "Steg {{step}}", + "completed": "Avslutad", + "active": "Aktiv", + "tabInfo": "Info", + "tabTags": "Taggar", + "tabSequences": "Sekvenser", + "tabNotes": "Notes", + "noNotes": "No notes yet", + "unknownAuthor": "Unknown", + "message": "Message", + "assign": "Assign", + "addToSequence": "Add to sequence", + "noActiveSequences": "No active sequences", + "sequenceSubscribers": "{{count}} subscriber", + "sequenceSubscribers_other": "{{count}} subscribers", + "enrollSelected": "Add ({{count}})", + "sequenceEnrolled": "Contact enrolled in sequence", + "sequenceEnrollFailed": "Couldn't enroll contact in sequence" + }, + "flows": { + "title": "Flöden", + "empty": "Inga flöden", + "readOnlyNote": "Flöden är skrivskyddade på mobil.", + "active": "Aktiv", + "inactive": "Inaktiv", + "nodes": "{{count}} nod", + "nodes_other": "{{count}} noder" + }, + "settings": { + "title": "Inställningar", + "darkMode": "Mörkt läge", + "theme": "Tema", + "themeSystem": "System", + "themeLight": "Ljust", + "themeDark": "Mörkt", + "language": "Språk", + "members": "Medlemmar", + "profile": "Profil", + "switchWorkspace": "Byt arbetsyta", + "pushNotifications": "Push-notiser", + "pushNotificationsDescription": "Få aviseringar om nya meddelanden på den här enheten.", + "teams": "Team", + "noMembers": "Inga medlemmar", + "noTeams": "Inga team", + "roleOwner": "Ägare", + "roleAgent": "Agent", + "restartRequiredTitle": "Omstart krävs", + "restartRequiredMessage": "Att byta till det här språket ändrar appens layoutriktning. Starta om nu för att tillämpa det?", + "restartManuallyMessage": "Att byta till det här språket ändrar appens layoutriktning. Stäng och öppna appen igen för att tillämpa det.", + "restartNow": "Starta om nu", + "restartLater": "Senare", + "notifications": "Notifications", + "appearance": "Appearance", + "workspace": "Workspace", + "version": "Version {{version}}" + }, + "profile": { + "plan": "Plan" + }, + "errors": { + "unauthorized": "Din session har gått ut. Logga in igen.", + "workspaceBlocked": "Den här arbetsytan är för tillfället inte tillgänglig.", + "workspaceBlockedTitle": "Arbetsytan är inte tillgänglig", + "workspaceBlockedMac": "Den här arbetsytan har nått sin kontaktgräns på den aktuella planen. Uppgradera för att fortsätta.", + "workspaceBlockedTrialExpired": "Provperioden eller planen för den här arbetsytan har gått ut. Uppgradera för att fortsätta.", + "notFound": "Hittades inte", + "notFoundTitle": "Sidan hittades inte", + "notFoundBody": "Sidan du letar efter finns inte.", + "goHome": "Gå till konversationer", + "unknown": "Något gick fel. Försök igen." + }, + "realtime": { + "online": "Back online", + "connecting": "Connecting…", + "reconnecting": "Reconnecting…", + "offline": "You're offline" + }, + "notifications": { + "androidChannelName": "Default" + } +} diff --git a/src/i18n/locales/tr.json b/src/i18n/locales/tr.json new file mode 100644 index 0000000..044c4a2 --- /dev/null +++ b/src/i18n/locales/tr.json @@ -0,0 +1,259 @@ +{ + "common": { + "loading": "Yükleniyor…", + "error": "Bir şeyler ters gitti", + "retry": "Tekrar dene", + "cancel": "İptal", + "save": "Kaydet", + "done": "Tamam", + "search": "Ara", + "confirm": "Onayla", + "delete": "Sil", + "clear": "Clear", + "close": "Close", + "today": "Today", + "yesterday": "Yesterday", + "removeItem": "Remove {{label}}", + "comingSoon": "Coming soon", + "back": "Back" + }, + "auth": { + "signIn": "Giriş yap", + "email": "E-posta", + "password": "Şifre", + "forgotPassword": "Şifrenizi mi unuttunuz?", + "signOut": "Çıkış yap", + "changePassword": "Şifreyi değiştir", + "changePasswordRequired": "Devam etmeden önce yeni bir şifre belirlemeniz gerekiyor.", + "currentPassword": "Mevcut şifre", + "newPassword": "Yeni şifre", + "sendResetLink": "Sıfırlama bağlantısı gönder", + "resetLinkSent": "Bu e-posta için bir hesap varsa, sıfırlama bağlantısı gönderildi.", + "orContinueWith": "Veya şununla devam et", + "continueWithGoogle": "Google ile devam et", + "continueWithFacebook": "Facebook ile devam et", + "socialSignInFailed": "Giriş tamamlanamadı. Lütfen tekrar deneyin.", + "signOutConfirmTitle": "Sign out?", + "signOutConfirmBody": "You'll need to sign in again to access your workspaces.", + "showPassword": "Show password", + "hidePassword": "Hide password", + "signInSubtitle": "Sign in to keep every conversation moving.", + "forgotPasswordSubtitle": "Enter your email and we'll send you a link to reset your password.", + "backToSignIn": "Back to sign in" + }, + "workspaces": { + "title": "Çalışma alanları", + "empty": "Henüz çalışma alanı yok", + "current": "Current workspace", + "switch": "Switch", + "switchConfirmTitle": "Switch workspace?", + "switchConfirmBody": "Switch to {{name}}? You'll leave your current workspace." + }, + "inbox": { + "title": "Inbox" + }, + "conversations": { + "title": "Konuşmalar", + "empty": "Konuşma yok", + "filters": "Filtreler", + "channel": "Kanal", + "handledBy": "İşleyen", + "status": "Durum", + "clearFilters": "Filtreleri temizle", + "filterAll": "Tümü", + "channelMessenger": "Messenger", + "channelInstagram": "Instagram", + "channelWhatsapp": "WhatsApp", + "channelWebchat": "Web sohbeti", + "channelZalo": "Zalo", + "channelTelegram": "Telegram", + "channelEmail": "E-posta", + "channelTiktok": "TikTok", + "statusUnread": "Okunmadı", + "statusNoAdminReply": "Ekipten yanıt yok", + "statusFollowUp": "Takipte", + "statusArchived": "Arşivlendi", + "statusBlocked": "Engellendi", + "handledByBot": "Bot işliyor", + "handledByHuman": "Kişi işliyor", + "unassign": "Atamayı kaldır", + "noOtherMembers": "Bu çalışma alanında henüz başka üye yok.", + "unnamed": "İsimsiz", + "unknownContact": "Unknown contact", + "attachmentPreview": "📎 {{count}} attachment(s)", + "a11yUnread": "unread", + "markRead": "Mark as read", + "markUnread": "Mark as unread", + "enableBot": "Enable bot", + "disableBot": "Disable bot", + "archive": "Archive", + "archiveUndone": "Conversation restored", + "undo": "Undo", + "botActive": "Bot handling", + "botPaused": "Bot paused", + "assignedTo": "Assigned to {{name}}", + "quickFilterMine": "Mine", + "emptyFiltered": "No conversations match your filters", + "botOn": "On", + "botOff": "Off", + "channelOmnichannel": "Omnichannel", + "botOnOffLabel": "Bot", + "actions": "Conversation actions", + "follow": "Follow", + "unfollow": "Unfollow", + "assign": "Assign", + "unarchive": "Unarchive", + "archiveConfirm": "Archive this conversation? You can find it again later in archived conversations.", + "unarchiveConfirm": "Restore this conversation from the archive?" + }, + "chat": { + "typeAMessage": "Bir mesaj yazın", + "send": "Gönder", + "failedToSend": "Gönderilemedi", + "failedToSendTapRetry": "Gönderilemedi. Yeniden denemek için dokunun.", + "retrySend": "Tekrar dene", + "noMessages": "Henüz mesaj yok", + "edit": "Düzenle", + "like": "Beğen", + "hide": "Gizle", + "delete": "Sil", + "searchSavedReplies": "Kayıtlı yanıtlarda ara", + "noSavedReplies": "Kayıtlı yanıt yok", + "attachmentTooLarge": "{{fileName}} 5 MB'tan büyük.", + "quotaMac": "Bu çalışma alanı mevcut plandaki kişi sınırına ulaştı. Mesajlaşmaya devam etmek için yükseltin.", + "quotaTrialExpired": "Bu çalışma alanının denemesi veya planı sona erdi. Mesajlaşmaya devam etmek için yükseltin.", + "quotaUnknown": "Bu çalışma alanı şu anda kullanılamıyor.", + "dismiss": "Kapat", + "grantCameraPermission": "Fotoğraf çekmek için kamera erişimi gerekiyor.", + "grantPermission": "İzin ver", + "failedToSendPhoto": "Fotoğraf gönderilemedi.", + "typing": "Typing…", + "attach": "Attach", + "camera": "Camera", + "photoLibrary": "Photo library", + "document": "Document", + "sendFlow": "Send flow", + "sendSequence": "Send sequence", + "sendingFlow": "Sending {{name}}…", + "flowSendFailed": "Couldn't send flow. Please try again.", + "savedReplies": "Saved replies", + "attachmentsTooLarge": "{{count}} file(s) are larger than 5MB and weren't sent: {{names}}", + "editingMessage": "Editing message", + "replyingTo": "Replying to {{name}}", + "reply": "Reply", + "editAndResend": "Edit & resend", + "repliedAttachment": "Attachment", + "repliedMessageUnavailable": "Original message unavailable", + "messageDeleted": "Message deleted", + "senderAgent": "You", + "senderBot": "Bot", + "senderContact": "Contact", + "scrollToBottom": "Scroll to latest messages", + "bubbleA11yLabel": "{{sender}} said {{text}}, {{time}}, {{status}}", + "status": { + "pending": "Sending", + "sent": "Sent", + "failed": "Failed to send" + }, + "locationShared": "Location shared", + "unsupportedContent": "[{{contentType}}] Unsupported on mobile", + "activityFallback": "Activity" + }, + "contacts": { + "title": "Kişiler", + "contactTitle": "Kişi", + "empty": "Kişi yok", + "unknown": "Bilinmiyor", + "blocked": "Engellendi", + "email": "E-posta", + "phone": "Telefon", + "locale": "Dil", + "timezone": "Saat dilimi", + "location": "Konum", + "customFields": "Özel alanlar", + "addCustomField": "Özel alan ekle", + "assignedTags": "Etiketler", + "noTags": "Etiket yok", + "addTag": "Etiket ekle", + "noSequences": "Herhangi bir diziye kayıtlı değil", + "sequenceStep": "Adım {{step}}", + "completed": "Tamamlandı", + "active": "Aktif", + "tabInfo": "Bilgi", + "tabTags": "Etiketler", + "tabSequences": "Diziler", + "tabNotes": "Notes", + "noNotes": "No notes yet", + "unknownAuthor": "Unknown", + "message": "Message", + "assign": "Assign", + "addToSequence": "Add to sequence", + "noActiveSequences": "No active sequences", + "sequenceSubscribers": "{{count}} subscriber", + "sequenceSubscribers_other": "{{count}} subscribers", + "enrollSelected": "Add ({{count}})", + "sequenceEnrolled": "Contact enrolled in sequence", + "sequenceEnrollFailed": "Couldn't enroll contact in sequence" + }, + "flows": { + "title": "Akışlar", + "empty": "Akış yok", + "readOnlyNote": "Akışlar mobilde salt okunurdur.", + "active": "Aktif", + "inactive": "Pasif", + "nodes": "{{count}} düğüm", + "nodes_other": "{{count}} düğüm" + }, + "settings": { + "title": "Ayarlar", + "darkMode": "Karanlık mod", + "theme": "Tema", + "themeSystem": "Sistem", + "themeLight": "Açık", + "themeDark": "Koyu", + "language": "Dil", + "members": "Üyeler", + "profile": "Profil", + "switchWorkspace": "Çalışma alanını değiştir", + "pushNotifications": "Push bildirimleri", + "pushNotificationsDescription": "Bu cihazda yeni mesajlar hakkında bildirim alın.", + "teams": "Ekipler", + "noMembers": "Üye yok", + "noTeams": "Ekip yok", + "roleOwner": "Sahip", + "roleAgent": "Temsilci", + "restartRequiredTitle": "Yeniden başlatma gerekli", + "restartRequiredMessage": "Bu dile geçmek uygulamanın düzen yönünü değiştirir. Uygulamak için şimdi yeniden başlatılsın mı?", + "restartManuallyMessage": "Bu dile geçmek uygulamanın düzen yönünü değiştirir. Uygulamak için uygulamayı kapatıp yeniden açın.", + "restartNow": "Şimdi yeniden başlat", + "restartLater": "Daha sonra", + "notifications": "Notifications", + "appearance": "Appearance", + "workspace": "Workspace", + "version": "Version {{version}}" + }, + "profile": { + "plan": "Plan" + }, + "errors": { + "unauthorized": "Oturumunuzun süresi doldu. Lütfen tekrar giriş yapın.", + "workspaceBlocked": "Bu çalışma alanı şu anda kullanılamıyor.", + "workspaceBlockedTitle": "Çalışma alanı kullanılamıyor", + "workspaceBlockedMac": "Bu çalışma alanı mevcut plandaki kişi sınırına ulaştı. Devam etmek için yükseltin.", + "workspaceBlockedTrialExpired": "Bu çalışma alanının denemesi veya planı sona erdi. Devam etmek için yükseltin.", + "notFound": "Bulunamadı", + "notFoundTitle": "Sayfa bulunamadı", + "notFoundBody": "Aradığınız sayfa mevcut değil.", + "goHome": "Konuşmalara git", + "unknown": "Bir şeyler ters gitti. Lütfen tekrar deneyin." + }, + "realtime": { + "online": "Back online", + "connecting": "Connecting…", + "reconnecting": "Reconnecting…", + "offline": "You're offline" + }, + "notifications": { + "androidChannelName": "Default" + } +} diff --git a/src/i18n/locales/vi.json b/src/i18n/locales/vi.json new file mode 100644 index 0000000..7c48d47 --- /dev/null +++ b/src/i18n/locales/vi.json @@ -0,0 +1,259 @@ +{ + "common": { + "loading": "Đang tải…", + "error": "Đã xảy ra lỗi", + "retry": "Thử lại", + "cancel": "Hủy", + "save": "Lưu", + "done": "Xong", + "search": "Tìm kiếm", + "confirm": "Xác nhận", + "delete": "Xóa", + "clear": "Clear", + "close": "Close", + "today": "Today", + "yesterday": "Yesterday", + "removeItem": "Remove {{label}}", + "comingSoon": "Coming soon", + "back": "Back" + }, + "auth": { + "signIn": "Đăng nhập", + "email": "Email", + "password": "Mật khẩu", + "forgotPassword": "Quên mật khẩu?", + "signOut": "Đăng xuất", + "changePassword": "Đổi mật khẩu", + "changePasswordRequired": "Bạn cần đặt mật khẩu mới trước khi tiếp tục.", + "currentPassword": "Mật khẩu hiện tại", + "newPassword": "Mật khẩu mới", + "sendResetLink": "Gửi liên kết đặt lại", + "resetLinkSent": "Nếu email tồn tại, liên kết đặt lại mật khẩu đã được gửi.", + "orContinueWith": "Hoặc tiếp tục với", + "continueWithGoogle": "Tiếp tục với Google", + "continueWithFacebook": "Tiếp tục với Facebook", + "socialSignInFailed": "Đăng nhập chưa hoàn tất. Vui lòng thử lại.", + "signOutConfirmTitle": "Sign out?", + "signOutConfirmBody": "You'll need to sign in again to access your workspaces.", + "showPassword": "Show password", + "hidePassword": "Hide password", + "signInSubtitle": "Sign in to keep every conversation moving.", + "forgotPasswordSubtitle": "Enter your email and we'll send you a link to reset your password.", + "backToSignIn": "Back to sign in" + }, + "workspaces": { + "title": "Không gian làm việc", + "empty": "Chưa có không gian làm việc nào", + "current": "Current workspace", + "switch": "Switch", + "switchConfirmTitle": "Switch workspace?", + "switchConfirmBody": "Switch to {{name}}? You'll leave your current workspace." + }, + "inbox": { + "title": "Inbox" + }, + "conversations": { + "title": "Hội thoại", + "empty": "Không có hội thoại", + "filters": "Bộ lọc", + "channel": "Kênh", + "handledBy": "Xử lý bởi", + "status": "Trạng thái", + "clearFilters": "Xóa bộ lọc", + "filterAll": "Tất cả", + "channelMessenger": "Messenger", + "channelInstagram": "Instagram", + "channelWhatsapp": "WhatsApp", + "channelWebchat": "Webchat", + "channelZalo": "Zalo", + "channelTelegram": "Telegram", + "channelEmail": "Email", + "channelTiktok": "TikTok", + "statusUnread": "Chưa đọc", + "statusNoAdminReply": "Chưa có phản hồi từ quản trị", + "statusFollowUp": "Đang theo dõi", + "statusArchived": "Đã lưu trữ", + "statusBlocked": "Đã chặn", + "handledByBot": "Bot xử lý", + "handledByHuman": "Người xử lý", + "unassign": "Bỏ phân công", + "noOtherMembers": "Không gian làm việc này chưa có thành viên nào khác.", + "unnamed": "Chưa đặt tên", + "unknownContact": "Unknown contact", + "attachmentPreview": "📎 {{count}} attachment(s)", + "a11yUnread": "unread", + "markRead": "Mark as read", + "markUnread": "Mark as unread", + "enableBot": "Enable bot", + "disableBot": "Disable bot", + "archive": "Archive", + "archiveUndone": "Conversation restored", + "undo": "Undo", + "botActive": "Bot handling", + "botPaused": "Bot paused", + "assignedTo": "Assigned to {{name}}", + "quickFilterMine": "Mine", + "emptyFiltered": "No conversations match your filters", + "botOn": "On", + "botOff": "Off", + "channelOmnichannel": "Omnichannel", + "botOnOffLabel": "Bot", + "actions": "Conversation actions", + "follow": "Follow", + "unfollow": "Unfollow", + "assign": "Assign", + "unarchive": "Unarchive", + "archiveConfirm": "Archive this conversation? You can find it again later in archived conversations.", + "unarchiveConfirm": "Restore this conversation from the archive?" + }, + "chat": { + "typeAMessage": "Nhập tin nhắn", + "send": "Gửi", + "failedToSend": "Gửi thất bại", + "failedToSendTapRetry": "Gửi thất bại. Nhấn để thử lại.", + "retrySend": "Thử lại", + "noMessages": "Chưa có tin nhắn", + "edit": "Sửa", + "like": "Thích", + "hide": "Ẩn", + "delete": "Xóa", + "searchSavedReplies": "Tìm mẫu trả lời nhanh", + "noSavedReplies": "Không có mẫu trả lời nhanh", + "attachmentTooLarge": "{{fileName}} lớn hơn 5MB.", + "quotaMac": "Không gian làm việc này đã đạt giới hạn liên hệ của gói hiện tại. Nâng cấp để tiếp tục nhắn tin.", + "quotaTrialExpired": "Gói dùng thử hoặc gói hiện tại của không gian làm việc này đã hết hạn. Nâng cấp để tiếp tục nhắn tin.", + "quotaUnknown": "Không gian làm việc này hiện không khả dụng.", + "dismiss": "Đóng", + "grantCameraPermission": "Cần quyền truy cập máy ảnh để chụp ảnh.", + "grantPermission": "Cấp quyền", + "failedToSendPhoto": "Gửi ảnh thất bại.", + "typing": "Typing…", + "attach": "Attach", + "camera": "Camera", + "photoLibrary": "Photo library", + "document": "Document", + "sendFlow": "Send flow", + "sendSequence": "Send sequence", + "sendingFlow": "Sending {{name}}…", + "flowSendFailed": "Couldn't send flow. Please try again.", + "savedReplies": "Saved replies", + "attachmentsTooLarge": "{{count}} file(s) are larger than 5MB and weren't sent: {{names}}", + "editingMessage": "Editing message", + "replyingTo": "Replying to {{name}}", + "reply": "Reply", + "editAndResend": "Edit & resend", + "repliedAttachment": "Attachment", + "repliedMessageUnavailable": "Original message unavailable", + "messageDeleted": "Message deleted", + "senderAgent": "You", + "senderBot": "Bot", + "senderContact": "Contact", + "scrollToBottom": "Scroll to latest messages", + "bubbleA11yLabel": "{{sender}} said {{text}}, {{time}}, {{status}}", + "status": { + "pending": "Sending", + "sent": "Sent", + "failed": "Failed to send" + }, + "locationShared": "Location shared", + "unsupportedContent": "[{{contentType}}] Unsupported on mobile", + "activityFallback": "Activity" + }, + "contacts": { + "title": "Liên hệ", + "contactTitle": "Liên hệ", + "empty": "Không có liên hệ", + "unknown": "Không xác định", + "blocked": "Đã chặn", + "email": "Email", + "phone": "Số điện thoại", + "locale": "Ngôn ngữ", + "timezone": "Múi giờ", + "location": "Vị trí", + "customFields": "Trường tùy chỉnh", + "addCustomField": "Thêm trường tùy chỉnh", + "assignedTags": "Nhãn", + "noTags": "Không có nhãn", + "addTag": "Thêm nhãn", + "noSequences": "Chưa tham gia chuỗi kịch bản nào", + "sequenceStep": "Bước {{step}}", + "completed": "Hoàn thành", + "active": "Đang hoạt động", + "tabInfo": "Thông tin", + "tabTags": "Nhãn", + "tabSequences": "Chuỗi kịch bản", + "tabNotes": "Notes", + "noNotes": "No notes yet", + "unknownAuthor": "Unknown", + "message": "Message", + "assign": "Assign", + "addToSequence": "Add to sequence", + "noActiveSequences": "No active sequences", + "sequenceSubscribers": "{{count}} subscriber", + "sequenceSubscribers_other": "{{count}} subscribers", + "enrollSelected": "Add ({{count}})", + "sequenceEnrolled": "Contact enrolled in sequence", + "sequenceEnrollFailed": "Couldn't enroll contact in sequence" + }, + "flows": { + "title": "Kịch bản", + "empty": "Không có kịch bản", + "readOnlyNote": "Kịch bản chỉ xem trên thiết bị di động.", + "active": "Đang bật", + "inactive": "Đang tắt", + "nodes": "{{count}} nút", + "nodes_other": "{{count}} nút" + }, + "settings": { + "title": "Cài đặt", + "darkMode": "Chế độ tối", + "theme": "Giao diện", + "themeSystem": "Hệ thống", + "themeLight": "Sáng", + "themeDark": "Tối", + "language": "Ngôn ngữ", + "members": "Thành viên", + "profile": "Hồ sơ", + "switchWorkspace": "Đổi không gian làm việc", + "pushNotifications": "Thông báo đẩy", + "pushNotificationsDescription": "Nhận thông báo về tin nhắn mới trên thiết bị này.", + "teams": "Nhóm", + "noMembers": "Không có thành viên", + "noTeams": "Không có nhóm", + "roleOwner": "Chủ sở hữu", + "roleAgent": "Nhân viên", + "restartRequiredTitle": "Cần khởi động lại", + "restartRequiredMessage": "Chuyển sang ngôn ngữ này sẽ thay đổi hướng bố cục của ứng dụng. Khởi động lại ngay để áp dụng?", + "restartManuallyMessage": "Chuyển sang ngôn ngữ này sẽ thay đổi hướng bố cục của ứng dụng. Hãy đóng và mở lại ứng dụng để áp dụng.", + "restartNow": "Khởi động lại ngay", + "restartLater": "Để sau", + "notifications": "Notifications", + "appearance": "Appearance", + "workspace": "Workspace", + "version": "Version {{version}}" + }, + "profile": { + "plan": "Gói" + }, + "errors": { + "unauthorized": "Phiên làm việc đã hết hạn. Vui lòng đăng nhập lại.", + "workspaceBlocked": "Không gian làm việc này hiện không khả dụng.", + "workspaceBlockedTitle": "Không gian làm việc không khả dụng", + "workspaceBlockedMac": "Không gian làm việc này đã đạt giới hạn liên hệ của gói hiện tại. Nâng cấp để tiếp tục.", + "workspaceBlockedTrialExpired": "Gói dùng thử hoặc gói hiện tại của không gian làm việc này đã hết hạn. Nâng cấp để tiếp tục.", + "notFound": "Không tìm thấy", + "notFoundTitle": "Không tìm thấy trang", + "notFoundBody": "Trang bạn tìm không tồn tại.", + "goHome": "Về trang hội thoại", + "unknown": "Đã xảy ra lỗi. Vui lòng thử lại." + }, + "realtime": { + "online": "Back online", + "connecting": "Connecting…", + "reconnecting": "Reconnecting…", + "offline": "You're offline" + }, + "notifications": { + "androidChannelName": "Default" + } +} diff --git a/src/i18n/locales/zh-CN.json b/src/i18n/locales/zh-CN.json new file mode 100644 index 0000000..76dd444 --- /dev/null +++ b/src/i18n/locales/zh-CN.json @@ -0,0 +1,259 @@ +{ + "common": { + "loading": "加载中…", + "error": "出错了", + "retry": "重试", + "cancel": "取消", + "save": "保存", + "done": "完成", + "search": "搜索", + "confirm": "确认", + "delete": "删除", + "clear": "Clear", + "close": "Close", + "today": "Today", + "yesterday": "Yesterday", + "removeItem": "Remove {{label}}", + "comingSoon": "Coming soon", + "back": "Back" + }, + "auth": { + "signIn": "登录", + "email": "邮箱", + "password": "密码", + "forgotPassword": "忘记密码?", + "signOut": "退出登录", + "changePassword": "修改密码", + "changePasswordRequired": "继续之前需要设置新密码。", + "currentPassword": "当前密码", + "newPassword": "新密码", + "sendResetLink": "发送重置链接", + "resetLinkSent": "如果该邮箱存在账号,重置链接已发送。", + "orContinueWith": "或使用以下方式继续", + "continueWithGoogle": "使用 Google 继续", + "continueWithFacebook": "使用 Facebook 继续", + "socialSignInFailed": "登录未完成,请重试。", + "signOutConfirmTitle": "Sign out?", + "signOutConfirmBody": "You'll need to sign in again to access your workspaces.", + "showPassword": "Show password", + "hidePassword": "Hide password", + "signInSubtitle": "Sign in to keep every conversation moving.", + "forgotPasswordSubtitle": "Enter your email and we'll send you a link to reset your password.", + "backToSignIn": "Back to sign in" + }, + "workspaces": { + "title": "工作区", + "empty": "暂无工作区", + "current": "Current workspace", + "switch": "Switch", + "switchConfirmTitle": "Switch workspace?", + "switchConfirmBody": "Switch to {{name}}? You'll leave your current workspace." + }, + "inbox": { + "title": "Inbox" + }, + "conversations": { + "title": "对话", + "empty": "暂无对话", + "filters": "筛选", + "channel": "渠道", + "handledBy": "处理人", + "status": "状态", + "clearFilters": "清除筛选", + "filterAll": "全部", + "channelMessenger": "Messenger", + "channelInstagram": "Instagram", + "channelWhatsapp": "WhatsApp", + "channelWebchat": "网页聊天", + "channelZalo": "Zalo", + "channelTelegram": "Telegram", + "channelEmail": "邮箱", + "channelTiktok": "TikTok", + "statusUnread": "未读", + "statusNoAdminReply": "团队未回复", + "statusFollowUp": "跟进中", + "statusArchived": "已归档", + "statusBlocked": "已屏蔽", + "handledByBot": "机器人处理中", + "handledByHuman": "人工处理中", + "unassign": "取消分配", + "noOtherMembers": "此工作区还没有其他成员。", + "unnamed": "未命名", + "unknownContact": "Unknown contact", + "attachmentPreview": "📎 {{count}} attachment(s)", + "a11yUnread": "unread", + "markRead": "Mark as read", + "markUnread": "Mark as unread", + "enableBot": "Enable bot", + "disableBot": "Disable bot", + "archive": "Archive", + "archiveUndone": "Conversation restored", + "undo": "Undo", + "botActive": "Bot handling", + "botPaused": "Bot paused", + "assignedTo": "Assigned to {{name}}", + "quickFilterMine": "Mine", + "emptyFiltered": "No conversations match your filters", + "botOn": "On", + "botOff": "Off", + "channelOmnichannel": "Omnichannel", + "botOnOffLabel": "Bot", + "actions": "Conversation actions", + "follow": "Follow", + "unfollow": "Unfollow", + "assign": "Assign", + "unarchive": "Unarchive", + "archiveConfirm": "Archive this conversation? You can find it again later in archived conversations.", + "unarchiveConfirm": "Restore this conversation from the archive?" + }, + "chat": { + "typeAMessage": "输入消息", + "send": "发送", + "failedToSend": "发送失败", + "failedToSendTapRetry": "发送失败,点击重试。", + "retrySend": "重试", + "noMessages": "暂无消息", + "edit": "编辑", + "like": "点赞", + "hide": "隐藏", + "delete": "删除", + "searchSavedReplies": "搜索保存的回复", + "noSavedReplies": "没有保存的回复", + "attachmentTooLarge": "{{fileName}} 超过 5MB。", + "quotaMac": "此工作区已达到当前套餐的联系人上限,请升级以继续发送消息。", + "quotaTrialExpired": "此工作区的试用期或套餐已过期,请升级以继续发送消息。", + "quotaUnknown": "此工作区当前不可用。", + "dismiss": "关闭", + "grantCameraPermission": "拍照需要相机权限。", + "grantPermission": "授予权限", + "failedToSendPhoto": "照片发送失败。", + "typing": "Typing…", + "attach": "Attach", + "camera": "Camera", + "photoLibrary": "Photo library", + "document": "Document", + "sendFlow": "Send flow", + "sendSequence": "Send sequence", + "sendingFlow": "Sending {{name}}…", + "flowSendFailed": "Couldn't send flow. Please try again.", + "savedReplies": "Saved replies", + "attachmentsTooLarge": "{{count}} file(s) are larger than 5MB and weren't sent: {{names}}", + "editingMessage": "Editing message", + "replyingTo": "Replying to {{name}}", + "reply": "Reply", + "editAndResend": "Edit & resend", + "repliedAttachment": "Attachment", + "repliedMessageUnavailable": "Original message unavailable", + "messageDeleted": "Message deleted", + "senderAgent": "You", + "senderBot": "Bot", + "senderContact": "Contact", + "scrollToBottom": "Scroll to latest messages", + "bubbleA11yLabel": "{{sender}} said {{text}}, {{time}}, {{status}}", + "status": { + "pending": "Sending", + "sent": "Sent", + "failed": "Failed to send" + }, + "locationShared": "Location shared", + "unsupportedContent": "[{{contentType}}] Unsupported on mobile", + "activityFallback": "Activity" + }, + "contacts": { + "title": "联系人", + "contactTitle": "联系人", + "empty": "暂无联系人", + "unknown": "未知", + "blocked": "已屏蔽", + "email": "邮箱", + "phone": "电话", + "locale": "语言", + "timezone": "时区", + "location": "位置", + "customFields": "自定义字段", + "addCustomField": "添加自定义字段", + "assignedTags": "标签", + "noTags": "暂无标签", + "addTag": "添加标签", + "noSequences": "未加入任何序列", + "sequenceStep": "第 {{step}} 步", + "completed": "已完成", + "active": "进行中", + "tabInfo": "信息", + "tabTags": "标签", + "tabSequences": "序列", + "tabNotes": "Notes", + "noNotes": "No notes yet", + "unknownAuthor": "Unknown", + "message": "Message", + "assign": "Assign", + "addToSequence": "Add to sequence", + "noActiveSequences": "No active sequences", + "sequenceSubscribers": "{{count}} subscriber", + "sequenceSubscribers_other": "{{count}} subscribers", + "enrollSelected": "Add ({{count}})", + "sequenceEnrolled": "Contact enrolled in sequence", + "sequenceEnrollFailed": "Couldn't enroll contact in sequence" + }, + "flows": { + "title": "流程", + "empty": "暂无流程", + "readOnlyNote": "流程在移动端为只读。", + "active": "已启用", + "inactive": "未启用", + "nodes": "{{count}} 个节点", + "nodes_other": "{{count}} 个节点" + }, + "settings": { + "title": "设置", + "darkMode": "深色模式", + "theme": "主题", + "themeSystem": "跟随系统", + "themeLight": "浅色", + "themeDark": "深色", + "language": "语言", + "members": "成员", + "profile": "个人资料", + "switchWorkspace": "切换工作区", + "pushNotifications": "推送通知", + "pushNotificationsDescription": "在此设备上接收新消息通知。", + "teams": "团队", + "noMembers": "暂无成员", + "noTeams": "暂无团队", + "roleOwner": "所有者", + "roleAgent": "客服", + "restartRequiredTitle": "需要重启", + "restartRequiredMessage": "切换到此语言会更改应用的布局方向。是否立即重启以应用更改?", + "restartManuallyMessage": "切换到此语言会更改应用的布局方向。请关闭并重新打开应用以应用更改。", + "restartNow": "立即重启", + "restartLater": "稍后", + "notifications": "Notifications", + "appearance": "Appearance", + "workspace": "Workspace", + "version": "Version {{version}}" + }, + "profile": { + "plan": "套餐" + }, + "errors": { + "unauthorized": "您的会话已过期,请重新登录。", + "workspaceBlocked": "此工作区当前不可用。", + "workspaceBlockedTitle": "工作区不可用", + "workspaceBlockedMac": "此工作区已达到当前套餐的联系人上限,请升级以继续。", + "workspaceBlockedTrialExpired": "此工作区的试用期或套餐已过期,请升级以继续。", + "notFound": "未找到", + "notFoundTitle": "页面未找到", + "notFoundBody": "您访问的页面不存在。", + "goHome": "返回对话", + "unknown": "出错了,请重试。" + }, + "realtime": { + "online": "Back online", + "connecting": "Connecting…", + "reconnecting": "Reconnecting…", + "offline": "You're offline" + }, + "notifications": { + "androidChannelName": "Default" + } +} diff --git a/src/i18n/locales/zh-TW.json b/src/i18n/locales/zh-TW.json new file mode 100644 index 0000000..e4f0835 --- /dev/null +++ b/src/i18n/locales/zh-TW.json @@ -0,0 +1,259 @@ +{ + "common": { + "loading": "載入中…", + "error": "發生錯誤", + "retry": "重試", + "cancel": "取消", + "save": "儲存", + "done": "完成", + "search": "搜尋", + "confirm": "確認", + "delete": "刪除", + "clear": "Clear", + "close": "Close", + "today": "Today", + "yesterday": "Yesterday", + "removeItem": "Remove {{label}}", + "comingSoon": "Coming soon", + "back": "Back" + }, + "auth": { + "signIn": "登入", + "email": "電子郵件", + "password": "密碼", + "forgotPassword": "忘記密碼?", + "signOut": "登出", + "changePassword": "變更密碼", + "changePasswordRequired": "繼續之前需要設定新密碼。", + "currentPassword": "目前密碼", + "newPassword": "新密碼", + "sendResetLink": "傳送重設連結", + "resetLinkSent": "如果該電子郵件存在帳號,重設連結已傳送。", + "orContinueWith": "或使用以下方式繼續", + "continueWithGoogle": "使用 Google 繼續", + "continueWithFacebook": "使用 Facebook 繼續", + "socialSignInFailed": "登入未完成,請再試一次。", + "signOutConfirmTitle": "Sign out?", + "signOutConfirmBody": "You'll need to sign in again to access your workspaces.", + "showPassword": "Show password", + "hidePassword": "Hide password", + "signInSubtitle": "Sign in to keep every conversation moving.", + "forgotPasswordSubtitle": "Enter your email and we'll send you a link to reset your password.", + "backToSignIn": "Back to sign in" + }, + "workspaces": { + "title": "工作區", + "empty": "尚無工作區", + "current": "Current workspace", + "switch": "Switch", + "switchConfirmTitle": "Switch workspace?", + "switchConfirmBody": "Switch to {{name}}? You'll leave your current workspace." + }, + "inbox": { + "title": "Inbox" + }, + "conversations": { + "title": "對話", + "empty": "沒有對話", + "filters": "篩選", + "channel": "頻道", + "handledBy": "處理人", + "status": "狀態", + "clearFilters": "清除篩選", + "filterAll": "全部", + "channelMessenger": "Messenger", + "channelInstagram": "Instagram", + "channelWhatsapp": "WhatsApp", + "channelWebchat": "網頁聊天", + "channelZalo": "Zalo", + "channelTelegram": "Telegram", + "channelEmail": "電子郵件", + "channelTiktok": "TikTok", + "statusUnread": "未讀", + "statusNoAdminReply": "團隊尚未回覆", + "statusFollowUp": "跟進中", + "statusArchived": "已封存", + "statusBlocked": "已封鎖", + "handledByBot": "機器人處理中", + "handledByHuman": "真人處理中", + "unassign": "取消指派", + "noOtherMembers": "此工作區尚無其他成員。", + "unnamed": "未命名", + "unknownContact": "Unknown contact", + "attachmentPreview": "📎 {{count}} attachment(s)", + "a11yUnread": "unread", + "markRead": "Mark as read", + "markUnread": "Mark as unread", + "enableBot": "Enable bot", + "disableBot": "Disable bot", + "archive": "Archive", + "archiveUndone": "Conversation restored", + "undo": "Undo", + "botActive": "Bot handling", + "botPaused": "Bot paused", + "assignedTo": "Assigned to {{name}}", + "quickFilterMine": "Mine", + "emptyFiltered": "No conversations match your filters", + "botOn": "On", + "botOff": "Off", + "channelOmnichannel": "Omnichannel", + "botOnOffLabel": "Bot", + "actions": "Conversation actions", + "follow": "Follow", + "unfollow": "Unfollow", + "assign": "Assign", + "unarchive": "Unarchive", + "archiveConfirm": "Archive this conversation? You can find it again later in archived conversations.", + "unarchiveConfirm": "Restore this conversation from the archive?" + }, + "chat": { + "typeAMessage": "輸入訊息", + "send": "傳送", + "failedToSend": "傳送失敗", + "failedToSendTapRetry": "傳送失敗,點一下重試。", + "retrySend": "重試", + "noMessages": "尚無訊息", + "edit": "編輯", + "like": "按讚", + "hide": "隱藏", + "delete": "刪除", + "searchSavedReplies": "搜尋已儲存的回覆", + "noSavedReplies": "沒有已儲存的回覆", + "attachmentTooLarge": "{{fileName}} 超過 5MB。", + "quotaMac": "此工作區已達到目前方案的聯絡人上限,請升級以繼續傳送訊息。", + "quotaTrialExpired": "此工作區的試用期或方案已過期,請升級以繼續傳送訊息。", + "quotaUnknown": "此工作區目前無法使用。", + "dismiss": "關閉", + "grantCameraPermission": "拍照需要相機權限。", + "grantPermission": "授予權限", + "failedToSendPhoto": "照片傳送失敗。", + "typing": "Typing…", + "attach": "Attach", + "camera": "Camera", + "photoLibrary": "Photo library", + "document": "Document", + "sendFlow": "Send flow", + "sendSequence": "Send sequence", + "sendingFlow": "Sending {{name}}…", + "flowSendFailed": "Couldn't send flow. Please try again.", + "savedReplies": "Saved replies", + "attachmentsTooLarge": "{{count}} file(s) are larger than 5MB and weren't sent: {{names}}", + "editingMessage": "Editing message", + "replyingTo": "Replying to {{name}}", + "reply": "Reply", + "editAndResend": "Edit & resend", + "repliedAttachment": "Attachment", + "repliedMessageUnavailable": "Original message unavailable", + "messageDeleted": "Message deleted", + "senderAgent": "You", + "senderBot": "Bot", + "senderContact": "Contact", + "scrollToBottom": "Scroll to latest messages", + "bubbleA11yLabel": "{{sender}} said {{text}}, {{time}}, {{status}}", + "status": { + "pending": "Sending", + "sent": "Sent", + "failed": "Failed to send" + }, + "locationShared": "Location shared", + "unsupportedContent": "[{{contentType}}] Unsupported on mobile", + "activityFallback": "Activity" + }, + "contacts": { + "title": "聯絡人", + "contactTitle": "聯絡人", + "empty": "沒有聯絡人", + "unknown": "未知", + "blocked": "已封鎖", + "email": "電子郵件", + "phone": "電話", + "locale": "語言", + "timezone": "時區", + "location": "地點", + "customFields": "自訂欄位", + "addCustomField": "新增自訂欄位", + "assignedTags": "標籤", + "noTags": "沒有標籤", + "addTag": "新增標籤", + "noSequences": "未加入任何序列", + "sequenceStep": "第 {{step}} 步", + "completed": "已完成", + "active": "進行中", + "tabInfo": "資訊", + "tabTags": "標籤", + "tabSequences": "序列", + "tabNotes": "Notes", + "noNotes": "No notes yet", + "unknownAuthor": "Unknown", + "message": "Message", + "assign": "Assign", + "addToSequence": "Add to sequence", + "noActiveSequences": "No active sequences", + "sequenceSubscribers": "{{count}} subscriber", + "sequenceSubscribers_other": "{{count}} subscribers", + "enrollSelected": "Add ({{count}})", + "sequenceEnrolled": "Contact enrolled in sequence", + "sequenceEnrollFailed": "Couldn't enroll contact in sequence" + }, + "flows": { + "title": "流程", + "empty": "沒有流程", + "readOnlyNote": "流程在行動裝置上為唯讀。", + "active": "已啟用", + "inactive": "未啟用", + "nodes": "{{count}} 個節點", + "nodes_other": "{{count}} 個節點" + }, + "settings": { + "title": "設定", + "darkMode": "深色模式", + "theme": "主題", + "themeSystem": "系統", + "themeLight": "淺色", + "themeDark": "深色", + "language": "語言", + "members": "成員", + "profile": "個人資料", + "switchWorkspace": "切換工作區", + "pushNotifications": "推播通知", + "pushNotificationsDescription": "在此裝置上接收新訊息通知。", + "teams": "團隊", + "noMembers": "沒有成員", + "noTeams": "沒有團隊", + "roleOwner": "擁有者", + "roleAgent": "客服人員", + "restartRequiredTitle": "需要重新啟動", + "restartRequiredMessage": "切換到此語言會變更應用程式的版面方向。是否立即重新啟動以套用?", + "restartManuallyMessage": "切換到此語言會變更應用程式的版面方向。請關閉並重新開啟應用程式以套用。", + "restartNow": "立即重新啟動", + "restartLater": "稍後", + "notifications": "Notifications", + "appearance": "Appearance", + "workspace": "Workspace", + "version": "Version {{version}}" + }, + "profile": { + "plan": "方案" + }, + "errors": { + "unauthorized": "您的工作階段已過期,請重新登入。", + "workspaceBlocked": "此工作區目前無法使用。", + "workspaceBlockedTitle": "工作區無法使用", + "workspaceBlockedMac": "此工作區已達到目前方案的聯絡人上限,請升級以繼續。", + "workspaceBlockedTrialExpired": "此工作區的試用期或方案已過期,請升級以繼續。", + "notFound": "找不到", + "notFoundTitle": "找不到頁面", + "notFoundBody": "您要尋找的頁面不存在。", + "goHome": "回到對話", + "unknown": "發生錯誤,請再試一次。" + }, + "realtime": { + "online": "Back online", + "connecting": "Connecting…", + "reconnecting": "Reconnecting…", + "offline": "You're offline" + }, + "notifications": { + "androidChannelName": "Default" + } +} diff --git a/src/i18n/namespace.ts b/src/i18n/namespace.ts new file mode 100644 index 0000000..2c64c0d --- /dev/null +++ b/src/i18n/namespace.ts @@ -0,0 +1 @@ +export const defaultNamespace = 'translation'; diff --git a/src/i18n/reconcile-rtl.ts b/src/i18n/reconcile-rtl.ts new file mode 100644 index 0000000..9c746eb --- /dev/null +++ b/src/i18n/reconcile-rtl.ts @@ -0,0 +1,37 @@ +import { Platform, I18nManager } from 'react-native'; + +import { reloadApp } from '@/i18n/apply-language'; +import { isRTL, type SupportedLanguage } from '@/i18n/locales'; + +/** + * Called once at app bootstrap (splash still up) to make the native `I18nManager` direction match + * the persisted language's writing direction. Unlike `changeAppLanguage`'s user-initiated flow, + * this reloads silently (no dialog) since nothing has rendered yet. + * + * Skipped on web (I18nManager.forceRTL has no effect there — CSS `dir` handles it) and in Expo Go + * `__DEV__`, where `forceRTL` is known not to persist across the JS reload it triggers, which would + * otherwise cause a reload loop; logs a warning there instead. + */ +export async function reconcileRTLOnLaunch(language: SupportedLanguage): Promise<void> { + if (Platform.OS === 'web') { + return; + } + + const targetIsRTL = isRTL(language); + if (targetIsRTL === I18nManager.isRTL) { + return; + } + + I18nManager.allowRTL(true); + I18nManager.forceRTL(targetIsRTL); + + if (__DEV__) { + console.warn( + '[i18n] RTL direction mismatch detected in dev — forceRTL may not persist in Expo Go. ' + + 'Use a development build to verify RTL layout.', + ); + return; + } + + await reloadApp(); +} diff --git a/src/i18n/resources.ts b/src/i18n/resources.ts new file mode 100644 index 0000000..86625d2 --- /dev/null +++ b/src/i18n/resources.ts @@ -0,0 +1,46 @@ +// Metro requires static `require`/`import` paths to bundle JSON — this file exists solely to +// gather every locale's translation JSON into one typed resources record for i18next.init(). +import ar from '@/i18n/locales/ar.json'; +import da from '@/i18n/locales/da.json'; +import de from '@/i18n/locales/de.json'; +import en from '@/i18n/locales/en.json'; +import es from '@/i18n/locales/es.json'; +import fi from '@/i18n/locales/fi.json'; +import fr from '@/i18n/locales/fr.json'; +import he from '@/i18n/locales/he.json'; +import id from '@/i18n/locales/id.json'; +import it from '@/i18n/locales/it.json'; +import ja from '@/i18n/locales/ja.json'; +import nl from '@/i18n/locales/nl.json'; +import ptBR from '@/i18n/locales/pt-BR.json'; +import ptPT from '@/i18n/locales/pt-PT.json'; +import ro from '@/i18n/locales/ro.json'; +import sv from '@/i18n/locales/sv.json'; +import tr from '@/i18n/locales/tr.json'; +import vi from '@/i18n/locales/vi.json'; +import zhCN from '@/i18n/locales/zh-CN.json'; +import zhTW from '@/i18n/locales/zh-TW.json'; +import type { SupportedLanguage } from '@/i18n/locales'; + +export const resources: Record<SupportedLanguage, { translation: typeof en }> = { + ar: { translation: ar }, + da: { translation: da }, + de: { translation: de }, + en: { translation: en }, + es: { translation: es }, + fi: { translation: fi }, + fr: { translation: fr }, + he: { translation: he }, + id: { translation: id }, + it: { translation: it }, + ja: { translation: ja }, + nl: { translation: nl }, + 'pt-BR': { translation: ptBR }, + 'pt-PT': { translation: ptPT }, + ro: { translation: ro }, + sv: { translation: sv }, + tr: { translation: tr }, + vi: { translation: vi }, + 'zh-CN': { translation: zhCN }, + 'zh-TW': { translation: zhTW }, +}; diff --git a/src/lib/describe-api-error.test.ts b/src/lib/describe-api-error.test.ts new file mode 100644 index 0000000..dcc8840 --- /dev/null +++ b/src/lib/describe-api-error.test.ts @@ -0,0 +1,58 @@ +import type { TFunction } from 'i18next'; + +import { ApiError } from '@/api/errors'; +import { describeApiError } from '@/lib/describe-api-error'; + +/** Identity-style mock: returns the key itself so assertions can check which key was resolved, + * matching the pattern the rest of the app's i18n-aware tests use. */ +const t = ((key: string) => key) as TFunction; + +describe('describeApiError', () => { + it('maps unauthorized to errors.unauthorized', () => { + const error = new ApiError({ code: 'UNAUTHORIZED' }); + expect(describeApiError(error, t)).toBe('errors.unauthorized'); + }); + + it('maps mustChangePassword to auth.changePasswordRequired', () => { + const error = new ApiError({ code: 'FORBIDDEN', message: 'Password change required' }); + expect(describeApiError(error, t)).toBe('auth.changePasswordRequired'); + }); + + it('maps notFound to errors.notFound', () => { + const error = new ApiError({ code: 'NOT_FOUND' }); + expect(describeApiError(error, t)).toBe('errors.notFound'); + }); + + it('maps workspaceBlocked/mac to errors.workspaceBlockedMac', () => { + const error = new ApiError({ code: 'workspaceBlocked', message: 'Hit the contact limit' }); + expect(describeApiError(error, t)).toBe('errors.workspaceBlockedMac'); + }); + + it('maps workspaceBlocked/trialExpired to errors.workspaceBlockedTrialExpired', () => { + const error = new ApiError({ status: 402, message: 'Your trial has expired' }); + expect(describeApiError(error, t)).toBe('errors.workspaceBlockedTrialExpired'); + }); + + it('falls back to the server message for workspaceBlocked/unknown', () => { + const error = new ApiError({ status: 402, message: 'Something else' }); + expect(describeApiError(error, t)).toBe('Something else'); + }); + + it("uses normalizeApiError's default message when workspaceBlocked/unknown has no server message", () => { + const error = new ApiError({ status: 402 }); + expect(describeApiError(error, t)).toBe('This workspace is currently unavailable.'); + }); + + it('returns the server message for unknown ApiError with a message', () => { + const error = new ApiError({ code: 'SOMETHING_ELSE', message: 'weird failure' }); + expect(describeApiError(error, t)).toBe('weird failure'); + }); + + it('falls back to errors.unknown for a non-ApiError Error', () => { + expect(describeApiError(new Error(''), t)).toBe('errors.unknown'); + }); + + it("uses normalizeApiError's default message for a non-Error thrown value", () => { + expect(describeApiError('not an error', t)).toBe('Unknown error'); + }); +}); diff --git a/src/lib/describe-api-error.ts b/src/lib/describe-api-error.ts new file mode 100644 index 0000000..c43db0e --- /dev/null +++ b/src/lib/describe-api-error.ts @@ -0,0 +1,43 @@ +import type { TFunction } from 'i18next'; + +import { normalizeApiError } from '@/api/errors'; + +/** + * Centralizes the `NormalizedApiError` → user-facing string mapping duplicated across several + * screens (conversations list, workspace picker, contact panel, contacts directory, members, + * chat screen) — each previously re-implemented its own ternary chain over `normalizeApiError`. + * + * For `workspaceBlocked`, this returns the flat `errors.workspaceBlocked*` copy suitable for a + * single-line `ErrorBanner`. Screens that render a full-screen gate for that kind should keep + * branching on `normalizeApiError(error).kind === 'workspaceBlocked'` themselves (see + * `WorkspaceBlockedGate`, which needs the `reason` to pick MAC-limit vs trial-expired copy, not + * just a string) — `describeApiError` is for the inline-banner case, not a replacement for that + * branch. + */ +export function describeApiError(error: unknown, t: TFunction): string { + const normalized = normalizeApiError(error); + + switch (normalized.kind) { + case 'unauthorized': + return t('errors.unauthorized'); + case 'mustChangePassword': + return t('auth.changePasswordRequired'); + case 'notFound': + return t('errors.notFound'); + case 'workspaceBlocked': + if (normalized.reason === 'mac') return t('errors.workspaceBlockedMac'); + if (normalized.reason === 'trialExpired') return t('errors.workspaceBlockedTrialExpired'); + return normalized.message || t('errors.workspaceBlocked'); + case 'unknown': + return normalized.message || t('errors.unknown'); + default: + return exhaustiveFallback(normalized, t); + } +} + +/** Compile-time exhaustiveness guard: if `NormalizedApiError` ever grows a new `kind`, this fails + * to typecheck rather than silently falling through at runtime. */ +function exhaustiveFallback(value: never, t: TFunction): string { + void value; + return t('errors.unknown'); +} diff --git a/src/lib/haptics.ts b/src/lib/haptics.ts new file mode 100644 index 0000000..c110b04 --- /dev/null +++ b/src/lib/haptics.ts @@ -0,0 +1,56 @@ +/** + * Thin wrapper around `expo-haptics`. Every UI primitive that fires a haptic goes through here + * instead of importing `expo-haptics` directly, so the "no-op on web + never throw" guard lives + * in exactly one place. + * + * - Web has no haptics engine; `expo-haptics` methods are unimplemented there, so we short-circuit + * on `Platform.OS === 'web'` rather than relying on the module to no-op gracefully. + * - Haptics can throw *or reject* on devices/simulators without a Taptic Engine (or with OS-level + * haptics disabled, or under Jest where the native module isn't linked at all) — every call is + * both wrapped in try/catch (for synchronous throws) and given a no-op `.catch()` (for async + * rejections, which a bare try/catch around a fire-and-forget promise does NOT catch) so a + * haptic failure never surfaces as a crash or an unhandled promise rejection. + */ +import * as Haptics from 'expo-haptics'; +import { Platform } from 'react-native'; + +export type HapticKind = + 'selection' | 'light' | 'medium' | 'heavy' | 'success' | 'warning' | 'error'; + +function swallow(): void { + // Haptics are a nice-to-have — never worth surfacing to the caller or crashing over. +} + +/** Fires a haptic feedback pulse for the given semantic kind. Fire-and-forget: callers don't need + * to await this, and it never rejects. */ +export function triggerHaptic(kind: HapticKind): void { + if (Platform.OS === 'web') return; + + try { + switch (kind) { + case 'selection': + Haptics.selectionAsync().catch(swallow); + return; + case 'light': + Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light).catch(swallow); + return; + case 'medium': + Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium).catch(swallow); + return; + case 'heavy': + Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Heavy).catch(swallow); + return; + case 'success': + Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success).catch(swallow); + return; + case 'warning': + Haptics.notificationAsync(Haptics.NotificationFeedbackType.Warning).catch(swallow); + return; + case 'error': + Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error).catch(swallow); + return; + } + } catch { + swallow(); + } +} diff --git a/src/lib/install-crypto-polyfill.test.ts b/src/lib/install-crypto-polyfill.test.ts new file mode 100644 index 0000000..10b8de0 --- /dev/null +++ b/src/lib/install-crypto-polyfill.test.ts @@ -0,0 +1,85 @@ +/** + * `install-crypto-polyfill.ts` runs its polyfill as a module-load side effect, so each case must + * `jest.resetModules()` and re-`require()` it to get a fresh evaluation, and must control + * `globalThis.crypto` itself rather than relying on the ambient jsdom/node environment. + */ +function setGlobalCrypto(value: unknown): void { + Object.defineProperty(globalThis, 'crypto', { + value, + configurable: true, + writable: true, + enumerable: true, + }); +} + +/** Re-imports the polyfill fresh (after `jest.resetModules()`) so its side effect re-runs. */ +function loadPolyfill(): void { + // A static top-level import would only ever evaluate once; a dynamic require after + // resetModules() is required to re-run the module's side effect for each case below. + // eslint-disable-next-line @typescript-eslint/no-require-imports + require('./install-crypto-polyfill'); +} + +describe('install-crypto-polyfill', () => { + const originalCrypto = globalThis.crypto; + + afterEach(() => { + jest.resetModules(); + setGlobalCrypto(originalCrypto); + }); + + test('installs a working crypto global when none exists', () => { + // Arrange + setGlobalCrypto(undefined); + jest.resetModules(); + + // Act + loadPolyfill(); + + // Assert + expect(globalThis.crypto).toBeDefined(); + expect(typeof globalThis.crypto.randomUUID).toBe('function'); + expect(typeof globalThis.crypto.getRandomValues).toBe('function'); + }); + + test('randomUUID returns an RFC4122 v4-shaped string', () => { + // Arrange + setGlobalCrypto(undefined); + jest.resetModules(); + loadPolyfill(); + + // Act + const id = globalThis.crypto.randomUUID(); + + // Assert + expect(id).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i); + }); + + test('getRandomValues fills the provided typed array in place', () => { + // Arrange + setGlobalCrypto(undefined); + jest.resetModules(); + loadPolyfill(); + const buffer = new Uint8Array(16); + + // Act + const result = globalThis.crypto.getRandomValues(buffer); + + // Assert + expect(result).toBe(buffer); + expect(buffer.some((byte) => byte !== 0)).toBe(true); + }); + + test('does not overwrite an existing crypto global', () => { + // Arrange + const existingCrypto = { randomUUID: () => 'existing', getRandomValues: jest.fn() }; + setGlobalCrypto(existingCrypto); + jest.resetModules(); + + // Act + loadPolyfill(); + + // Assert + expect(globalThis.crypto).toBe(existingCrypto); + }); +}); diff --git a/src/lib/install-crypto-polyfill.ts b/src/lib/install-crypto-polyfill.ts new file mode 100644 index 0000000..77352d2 --- /dev/null +++ b/src/lib/install-crypto-polyfill.ts @@ -0,0 +1,53 @@ +import * as ExpoCrypto from 'expo-crypto'; + +/** + * Installs a Web-Crypto-shaped `crypto` global backed by `expo-crypto`. + * + * Why this exists: neither Expo SDK 57's winter runtime (`expo/src/winter`, which installs + * `fetch`/`FormData`/`TextDecoder`/`URL`/`AbortSignal`/`structuredClone`) nor React Native 0.86 + * core defines a global `crypto`. `partysocket` (used by src/realtime/realtime-provider.tsx) + * reads it unconditionally in its `generateUUID()` helper: + * + * if (crypto?.randomUUID) { return crypto.randomUUID(); } + * + * Optional chaining only guards a `null`/`undefined` *value* — it does not guard an undeclared + * *binding*. On Hermes, the bare `crypto` identifier lookup throws + * `ReferenceError: Property 'crypto' doesn't exist` before `?.` can short-circuit, so partysocket's + * own `Math.random()` fallback is never reached. This module makes the global exist so that read + * succeeds, and fixes the same gap for anything else that expects a standard `crypto`. + * + * Imported for its side effect only — see the first import in src/app/_layout.tsx. + * + * This only shapes `randomUUID` and `getRandomValues` — `crypto.subtle` is deliberately not + * implemented. Anything needing real hashing/HMAC (e.g. SHA-256) should call `expo-crypto`'s + * `digest`/`digestStringAsync` directly rather than expecting it on this global. See AGENTS.md. + */ +function installCryptoPolyfill(): void { + if (typeof globalThis.crypto !== 'undefined') { + return; + } + + // expo-crypto's `getRandomValues` is generic over int/uint TypedArrays specifically, narrower + // than DOM's `Crypto.getRandomValues<T extends ArrayBufferView | null>`. Cast once at this + // boundary rather than assigned directly, so callers still get the standard DOM signature; the + // cast is safe because every concrete TypedArray callers pass (partysocket, any future caller) + // satisfies expo-crypto's constraint at runtime. + const getRandomValues = ExpoCrypto.getRandomValues as unknown as Crypto['getRandomValues']; + + const cryptoPolyfill: Pick<Crypto, 'randomUUID' | 'getRandomValues'> = { + randomUUID: () => + ExpoCrypto.randomUUID() as `${string}-${string}-${string}-${string}-${string}`, + getRandomValues, + }; + + // Plain assignment can silently no-op if the engine defines `crypto` as a non-writable + // accessor rather than a normal data property; `defineProperty` guarantees the global sticks. + Object.defineProperty(globalThis, 'crypto', { + value: cryptoPolyfill, + configurable: true, + writable: true, + enumerable: true, + }); +} + +installCryptoPolyfill(); diff --git a/src/lib/notification-tap.ts b/src/lib/notification-tap.ts new file mode 100644 index 0000000..5346b94 --- /dev/null +++ b/src/lib/notification-tap.ts @@ -0,0 +1,50 @@ +import * as Notifications from 'expo-notifications'; +import { router } from 'expo-router'; + +import { useAuthStore } from '@/stores/use-auth-store'; +import { useWorkspaceStore } from '@/stores/use-workspace-store'; + +/** + * Data payload the backend attaches to a push notification (see plan Workstream 4.2 — the worker + * sends `{ workspaceId, conversationId, messageId }` alongside title/body). All optional because + * this same handler also has to tolerate a malformed/older payload without throwing. + */ +interface NotificationTapData { + workspaceId?: string; + conversationId?: string; + messageId?: string; +} + +function navigateToNotification(data: NotificationTapData): void { + if (!data.conversationId) return; + + if (data.workspaceId && data.workspaceId !== useWorkspaceStore.getState().currentWorkspaceId) { + useWorkspaceStore.getState().setCurrentWorkspaceId(data.workspaceId); + } + + router.push(`/(app)/conversations/${data.conversationId}` as never); +} + +/** Handles a notification tap once the user is authenticated. If auth is still pending (tapped + * from a cold start before sign-in resolves), stashes nothing here — `pending-deep-link.ts` + * already covers the cold-start-URL case; this covers the warm/background-tap case where + * `useAuthStore` is already resolved by the time the tap arrives. */ +function handleResponse(response: Notifications.NotificationResponse): void { + if (useAuthStore.getState().status !== 'signed-in') return; + navigateToNotification(response.notification.request.content.data as NotificationTapData); +} + +/** + * Wires notification-tap → in-app navigation for both cold start (app launched by tapping a + * notification) and warm/background taps. Call once at root layout mount, after auth bootstrap + * has had a chance to resolve for the cold-start case — `getLastNotificationResponseAsync` + * result stays available until consumed, so a short delay here doesn't lose it. + */ +export function initNotificationTapHandling(): () => void { + Notifications.getLastNotificationResponseAsync().then((response) => { + if (response) handleResponse(response); + }); + + const subscription = Notifications.addNotificationResponseReceivedListener(handleResponse); + return () => subscription.remove(); +} diff --git a/src/lib/notifications.ts b/src/lib/notifications.ts new file mode 100644 index 0000000..77c711e --- /dev/null +++ b/src/lib/notifications.ts @@ -0,0 +1,97 @@ +import Constants from 'expo-constants'; +import * as Device from 'expo-device'; +import * as Notifications from 'expo-notifications'; +import { Platform } from 'react-native'; + +import { i18next } from '@/i18n'; +import { useChatStore } from '@/features/chat/stores/use-chat-store'; + +/** Shape of the push `data` payload possibly carrying a `conversationId` — mirrors + * `NotificationTapData` in notification-tap.ts (kept as a separate minimal type here since this + * module only reads the one field it needs). */ +interface NotificationConversationData { + conversationId?: string; +} + +// Foreground behavior: show banner + play sound even while the app is open, matching what the +// OS would do if the app were backgrounded — the app has no in-app "toast" for incoming messages. +// EXCEPTION: suppress the banner/list/sound entirely when the notification is about the +// conversation the user is already looking at — `useChatStore.getState()` (not a hook: this +// handler runs outside React, invoked directly by expo-notifications) reads `activeConversationId`, +// set by the chat screen's own `useFocusEffect` (see `(app)/conversations/[conversationId]/index.tsx`). +Notifications.setNotificationHandler({ + handleNotification: async (notification) => { + const data = notification.request.content.data as NotificationConversationData; + const activeConversationId = useChatStore.getState().activeConversationId; + const isForActiveConversation = + Boolean(data?.conversationId) && data.conversationId === activeConversationId; + + if (isForActiveConversation) { + return { + shouldShowBanner: false, + shouldShowList: false, + shouldPlaySound: false, + shouldSetBadge: false, + }; + } + + return { + shouldShowBanner: true, + shouldShowList: true, + shouldPlaySound: true, + shouldSetBadge: false, + }; + }, +}); + +const ANDROID_DEFAULT_CHANNEL_ID = 'default'; + +/** Android requires a notification channel to exist before any notification can display on it — + * call once at startup. No-op on iOS/web. */ +export async function ensureAndroidNotificationChannel(): Promise<void> { + if (Platform.OS !== 'android') return; + + await Notifications.setNotificationChannelAsync(ANDROID_DEFAULT_CHANNEL_ID, { + name: i18next.t('notifications.androidChannelName'), + importance: Notifications.AndroidImportance.DEFAULT, + }); +} + +export type PushTokenResult = + { status: 'granted'; token: string } | { status: 'denied' | 'unsupported' }; + +/** + * Requests notification permission (if not already granted) and returns an Expo push token. + * `unsupported` covers both simulators/emulators (`Device.isDevice` is false — remote push + * doesn't work there) and web. Requires `Constants.expoConfig.extra.eas.projectId`, which only + * exists after `eas init` has run — see plan Workstream 5.2. + */ +export async function getPushTokenAsync(): Promise<PushTokenResult> { + if (!Device.isDevice) { + return { status: 'unsupported' }; + } + + const existing = await Notifications.getPermissionsAsync(); + let finalStatus = existing.status; + + if (finalStatus !== 'granted') { + const requested = await Notifications.requestPermissionsAsync(); + finalStatus = requested.status; + } + + if (finalStatus !== 'granted') { + return { status: 'denied' }; + } + + await ensureAndroidNotificationChannel(); + + const projectId = Constants.expoConfig?.extra?.eas?.projectId as string | undefined; + if (!projectId) { + throw new Error( + 'Missing extra.eas.projectId in app config — run `eas init` before requesting a push token.', + ); + } + + const { data: token } = await Notifications.getExpoPushTokenAsync({ projectId }); + return { status: 'granted', token }; +} diff --git a/src/lib/pending-deep-link.ts b/src/lib/pending-deep-link.ts new file mode 100644 index 0000000..6881a58 --- /dev/null +++ b/src/lib/pending-deep-link.ts @@ -0,0 +1,43 @@ +import * as Linking from 'expo-linking'; + +/** + * Cold-start deep link to a conversation (`chatbotxmobileapp://conversations/:id`, e.g. from a + * push notification once delivery lands) arrives before the auth bootstrap gate resolves. + * `(app)/_layout.tsx` redirects an unauthenticated visitor to sign-in, which drops the original + * target — expo-router has no built-in "return to this deep link after auth" mechanism. This + * module stashes the conversation path in memory at launch (read once, before any redirect can + * fire) so sign-in can navigate there instead of the default `/` after a successful login. + * + * In-memory only (not persisted): a cold-start deep link is only meaningful for THIS launch: if + * the process is killed before sign-in completes, the notification (once real push delivery + * exists) would redeliver or the user re-opens the link. + */ +let pendingConversationPath: string | null = null; +let captured = false; + +/** Call once, as early as possible (root layout module scope) — captures the launch URL before + * any navigation/redirect can consume it. Safe to call multiple times; only the first call has an + * effect, matching the "cold start only" semantics above. */ +export function capturePendingDeepLink(): void { + if (captured) return; + captured = true; + + const url = Linking.getLinkingURL(); + if (!url) return; + + const parsed = Linking.parse(url); + // expo-router path for the chat screen is `conversations/[conversationId]` — Linking.parse + // gives us the path with the scheme/host stripped, e.g. "conversations/12345". + const match = /^\/?conversations\/([^/]+)\/?$/.exec(parsed.path ?? ''); + if (match) { + pendingConversationPath = `/(app)/conversations/${match[1]}`; + } +} + +/** Consume the stashed deep link (if any) — returns it once, then clears it, so a later normal + * sign-in (e.g. after a manual sign-out) doesn't replay a stale deep link from a previous launch. */ +export function consumePendingDeepLink(): string | null { + const path = pendingConversationPath; + pendingConversationPath = null; + return path; +} diff --git a/src/lib/query-client.ts b/src/lib/query-client.ts new file mode 100644 index 0000000..d417a56 --- /dev/null +++ b/src/lib/query-client.ts @@ -0,0 +1,37 @@ +import { MutationCache, QueryCache, QueryClient } from '@tanstack/react-query'; + +import { normalizeApiError } from '@/api/errors'; +import { clearAuthToken } from '@/api/auth-token'; +import { useAuthStore } from '@/stores/use-auth-store'; +import { useWorkspaceStore } from '@/stores/use-workspace-store'; + +/** + * Global error normalization: FORBIDDEN mustChangePassword and 401 route through the auth store so + * the root layout's redirect guard reacts everywhere, not just on the request that triggered it. + * 402 workspaceBlocked is left for the calling screen (composer banner, etc.) to read via + * `normalizeApiError` on its own `error` — this handler only manages the auth-affecting codes. + */ +function handleGlobalError(error: unknown): void { + const normalized = normalizeApiError(error); + + if (normalized.kind === 'mustChangePassword') { + useAuthStore.getState().setMustChangePassword(true); + return; + } + if (normalized.kind === 'unauthorized') { + clearAuthToken().catch(() => {}); + useAuthStore.getState().setSignedOut(); + useWorkspaceStore.getState().setCurrentWorkspaceId(null); + } +} + +export const queryClient = new QueryClient({ + defaultOptions: { + queries: { + retry: 1, + staleTime: 30_000, + }, + }, + queryCache: new QueryCache({ onError: handleGlobalError }), + mutationCache: new MutationCache({ onError: handleGlobalError }), +}); diff --git a/src/realtime/app-state.ts b/src/realtime/app-state.ts new file mode 100644 index 0000000..e013ae1 --- /dev/null +++ b/src/realtime/app-state.ts @@ -0,0 +1,48 @@ +import type { QueryClient } from '@tanstack/react-query'; +import { useEffect, useRef } from 'react'; +import { AppState, type AppStateStatus } from 'react-native'; + +import { isWorkspaceQuery } from '@/api/query-keys'; + +interface UseRealtimeAppStateOptions { + onBackground: () => void; + onForeground: () => void; +} + +/** + * Wraps AppState so the realtime provider can close its socket on background (saves battery/data, + * avoids a stale connection accumulating missed events indefinitely) and reconnect + refresh on + * return to foreground. `onForeground` is expected to both reconnect the socket AND invalidate the + * active workspace's queries (any events missed while backgrounded are caught by the resulting + * refetch, since there's no event-replay/backfill mechanism on the wire protocol). + */ +export function useRealtimeAppState({ onBackground, onForeground }: UseRealtimeAppStateOptions) { + const previousState = useRef<AppStateStatus>(AppState.currentState); + + useEffect(() => { + const subscription = AppState.addEventListener('change', (nextState) => { + const wasBackground = previousState.current.match(/inactive|background/); + const isNowActive = nextState === 'active'; + const isNowBackground = nextState.match(/inactive|background/); + + if (isNowBackground && !previousState.current.match(/inactive|background/)) { + onBackground(); + } else if (isNowActive && wasBackground) { + onForeground(); + } + + previousState.current = nextState; + }); + + return () => subscription.remove(); + }, [onBackground, onForeground]); +} + +/** Invalidates every cached query for the given workspace — used as the "refresh on foreground" + * half of the app-state handling, so a reconnect always heals against whatever changed while + * backgrounded. */ +export function invalidateWorkspaceQueries(queryClient: QueryClient, workspaceId: string) { + queryClient.invalidateQueries({ + predicate: (query) => isWorkspaceQuery(query.queryKey, workspaceId), + }); +} diff --git a/src/realtime/apply-conversation-events.test.ts b/src/realtime/apply-conversation-events.test.ts new file mode 100644 index 0000000..1e4aa4f --- /dev/null +++ b/src/realtime/apply-conversation-events.test.ts @@ -0,0 +1,217 @@ +import { + applyConversationAssigned, + applyConversationMessageCreated, + applyConversationUpdated, + applyContactBlockState, + applyContactDetailBlockState, + type InfiniteConversationsData, +} from './apply-conversation-events'; +import { RealtimeEventType } from './events'; +import type { ConversationListItem } from '@/features/conversations/api/use-conversations-infinite'; +import type { Message } from '@/features/chat/api/use-messages-infinite'; + +function fakeConversation(overrides: Partial<ConversationListItem> = {}): ConversationListItem { + return { + id: 'conv-1', + createdAt: '2026-01-01T00:00:00.000Z', + updatedAt: '2026-01-01T00:00:00.000Z', + botEnabled: true, + botResumeAt: null, + archivedAt: null, + additionalAttributes: null, + contactLastReadAt: null, + agentLastReadAt: null, + lastActivityAt: '2026-01-01T00:00:00.000Z', + followed: false, + workspaceId: 'ws-1', + contactId: 'contact-1', + sourceId: null, + lastStep: null, + currentStep: null, + adminRepliedAt: null, + contactRepliedAt: null, + contactInboxes: [], + messages: [], + contact: null, + assignedUser: null, + assignedInboxTeam: null, + ...overrides, + } as unknown as ConversationListItem; +} + +function cacheWith(conversations: ConversationListItem[]): InfiniteConversationsData { + return { + pages: [{ data: conversations, nextCursor: null, prevCursor: null }], + pageParams: [undefined], + } as InfiniteConversationsData; +} + +function firstPage(result: InfiniteConversationsData): ConversationListItem[] { + return result!.pages[0]!.data; +} + +function createdEvent(data: unknown) { + return { eventType: RealtimeEventType.messageCreated, data } as const; +} + +describe('applyConversationAssigned', () => { + it('patches assignedUserId/assignedInboxTeamId on matching rows only', () => { + const cache = cacheWith([ + fakeConversation({ id: 'conv-1', assignedUserId: null }), + fakeConversation({ id: 'conv-2', assignedUserId: null }), + ]); + const result = applyConversationAssigned(cache, { + eventType: RealtimeEventType.conversationAssigned, + data: { conversationIds: ['conv-1'], assignedUserId: 'user-9', assignedInboxTeamId: null }, + }); + + const data = firstPage(result); + expect(data.find((c) => c.id === 'conv-1')?.assignedUserId).toBe('user-9'); + expect(data.find((c) => c.id === 'conv-2')?.assignedUserId).toBeNull(); + }); +}); + +describe('applyConversationUpdated', () => { + it('applies the changes object to matching rows', () => { + const cache = cacheWith([fakeConversation({ id: 'conv-1', botEnabled: true })]); + const result = applyConversationUpdated(cache, { + eventType: RealtimeEventType.conversationUpdated, + data: { conversationIds: ['conv-1'], changes: { botEnabled: false } }, + }); + + expect(firstPage(result)[0]!.botEnabled).toBe(false); + }); +}); + +describe('applyContactBlockState', () => { + it('patches the embedded contact.blockedAt for matching contactId rows', () => { + const cache = cacheWith([ + fakeConversation({ + id: 'conv-1', + contactId: 'contact-1', + contact: { blockedAt: null } as never, + }), + ]); + const result = applyContactBlockState( + cache, + { eventType: RealtimeEventType.contactBlocked, data: { contactId: 'contact-1' } }, + true, + ); + + expect(firstPage(result)[0]!.contact?.blockedAt).not.toBeNull(); + }); +}); + +describe('applyContactDetailBlockState', () => { + it('sets blockedAt when blocked', () => { + const result = applyContactDetailBlockState({ blockedAt: null }, true); + expect(result?.blockedAt).not.toBeNull(); + }); + + it('clears blockedAt when unblocked', () => { + const result = applyContactDetailBlockState({ blockedAt: '2026-01-01T00:00:00.000Z' }, false); + expect(result?.blockedAt).toBeNull(); + }); + + it('returns undefined unchanged when nothing is cached', () => { + expect(applyContactDetailBlockState(undefined, true)).toBeUndefined(); + }); +}); + +describe('applyConversationMessageCreated', () => { + it('patches lastActivityAt and appends to the messages preview array on the matching row', () => { + const cache = cacheWith([ + fakeConversation({ + id: 'conv-1', + lastActivityAt: '2026-01-01T00:00:00.000Z', + messages: [{ id: 'old-msg', text: 'hi' } as Message], + }), + ]); + + const result = applyConversationMessageCreated( + cache, + createdEvent({ + id: 'new-msg', + conversationId: 'conv-1', + createdAt: '2026-01-02T00:00:00.000Z', + text: 'new message', + attachments: [], + }), + ); + + expect(result.found).toBe(true); + const row = firstPage(result.data)[0]!; + expect(row.lastActivityAt).toBe('2026-01-02T00:00:00.000Z'); + expect(row.messages.map((m) => m.id)).toEqual(['old-msg', 'new-msg']); + }); + + it('moves the patched row to the top of its page', () => { + const cache = cacheWith([ + fakeConversation({ id: 'conv-1', messages: [] }), + fakeConversation({ id: 'conv-2', messages: [] }), + fakeConversation({ id: 'conv-3', messages: [] }), + ]); + + const result = applyConversationMessageCreated( + cache, + createdEvent({ + id: 'new-msg', + conversationId: 'conv-3', + createdAt: '2026-01-02T00:00:00.000Z', + attachments: [], + }), + ); + + expect(firstPage(result.data).map((c) => c.id)).toEqual(['conv-3', 'conv-1', 'conv-2']); + }); + + it('reports found=false and leaves the cache unchanged when the conversation is not cached', () => { + const cache = cacheWith([fakeConversation({ id: 'conv-1' })]); + + const result = applyConversationMessageCreated( + cache, + createdEvent({ + id: 'new-msg', + conversationId: 'not-cached', + createdAt: '2026-01-02T00:00:00.000Z', + attachments: [], + }), + ); + + expect(result.found).toBe(false); + expect(firstPage(result.data).map((c) => c.id)).toEqual(['conv-1']); + }); + + it('reports found=false when there is nothing cached at all', () => { + const result = applyConversationMessageCreated(undefined, createdEvent({ id: 'm1' })); + expect(result.found).toBe(false); + expect(result.data).toBeUndefined(); + }); + + it('reports found=false for a malformed/un-narrowable payload', () => { + const cache = cacheWith([fakeConversation({ id: 'conv-1' })]); + const result = applyConversationMessageCreated(cache, createdEvent(null)); + expect(result.found).toBe(false); + expect(result.data).toBe(cache); + }); + + it('caps the preview messages array at the max length', () => { + const existing = Array.from({ length: 20 }, (_, i) => ({ id: `msg-${i}` }) as Message); + const cache = cacheWith([fakeConversation({ id: 'conv-1', messages: existing })]); + + const result = applyConversationMessageCreated( + cache, + createdEvent({ + id: 'new-msg', + conversationId: 'conv-1', + createdAt: '2026-01-02T00:00:00.000Z', + attachments: [], + }), + ); + + const row = firstPage(result.data)[0]!; + expect(row.messages).toHaveLength(20); + expect(row.messages[row.messages.length - 1]!.id).toBe('new-msg'); + expect(row.messages[0]!.id).toBe('msg-1'); + }); +}); diff --git a/src/realtime/apply-conversation-events.ts b/src/realtime/apply-conversation-events.ts new file mode 100644 index 0000000..66752c7 --- /dev/null +++ b/src/realtime/apply-conversation-events.ts @@ -0,0 +1,156 @@ +import type { + RealtimeEventConversationAssigned, + RealtimeEventConversationUpdated, + RealtimeEventContactCommon, + RealtimeEventCreateMessage, +} from '@/realtime/events'; +import { narrowIncomingMessage } from '@/realtime/apply-message-events'; +import type { + ConversationListItem, + ListConversationsResponse, +} from '@/features/conversations/api/use-conversations-infinite'; + +/** Pure functions patching the conversations-list infinite-query cache shape — see + * apply-message-events.ts for the same design rationale (pure, immutable, testable later). */ +export type InfiniteConversationsData = + { pages: ListConversationsResponse[]; pageParams: unknown[] } | undefined; + +function patchMatching( + current: InfiniteConversationsData, + matches: (item: ConversationListItem) => boolean, + patch: Partial<ConversationListItem>, +): InfiniteConversationsData { + if (!current) return current; + return { + ...current, + pages: current.pages.map((page) => ({ + ...page, + data: page.data.map((item) => (matches(item) ? { ...item, ...patch } : item)), + })), + }; +} + +export function applyConversationAssigned( + current: InfiniteConversationsData, + event: RealtimeEventConversationAssigned, +): InfiniteConversationsData { + const idSet = new Set(event.data.conversationIds); + return patchMatching(current, (item) => idSet.has(item.id), { + assignedUserId: event.data.assignedUserId, + assignedInboxTeamId: event.data.assignedInboxTeamId, + }); +} + +export function applyConversationUpdated( + current: InfiniteConversationsData, + event: RealtimeEventConversationUpdated, +): InfiniteConversationsData { + const idSet = new Set(event.data.conversationIds); + return patchMatching(current, (item) => idSet.has(item.id), event.data.changes); +} + +/** `contactBlocked`/`contactUnblocked` patch the embedded `contact.blockedAt` field on every + * conversation row for that contact — a contact can back multiple conversations (see Phase 3's + * assign-by-contact applier for the same one-contact-many-conversations pattern). */ +export function applyContactBlockState( + current: InfiniteConversationsData, + event: RealtimeEventContactCommon, + blocked: boolean, +): InfiniteConversationsData { + if (!current) return current; + const { contactId } = event.data; + + return { + ...current, + pages: current.pages.map((page) => ({ + ...page, + data: page.data.map((item) => + item.contactId === contactId && item.contact + ? { + ...item, + contact: { ...item.contact, blockedAt: blocked ? new Date().toISOString() : null }, + } + : item, + ), + })), + }; +} + +/** Patches a standalone `queryKeys.ws.contacts.detail` cache entry's `blockedAt` field — added in + * Phase 6 alongside the contacts feature. `applyContactBlockState` above only reaches the + * conversations-list cache's embedded contact object; this covers the separate contact-detail + * query the contact panel reads (use-contact-detail.ts), which has its own `blockedAt` field at + * the top level (not nested under `contact`). Callers scan for a query keyed + * `['ws', workspaceId, 'contacts', 'detail', contactId]` and setQueryData through this. */ +export function applyContactDetailBlockState<T extends { blockedAt: string | null }>( + current: T | undefined, + blocked: boolean, +): T | undefined { + if (!current) return current; + return { ...current, blockedAt: blocked ? new Date().toISOString() : null }; +} + +export interface ApplyConversationMessageCreatedResult { + data: InfiniteConversationsData; + /** Whether the incoming message's conversation was found (and patched) in ANY cached list page. + * `false` means no row exists in cache to patch — use-realtime-handlers.ts uses this signal to + * fall back to a debounced `invalidateQueries` instead of fabricating a new row from the + * partial/untyped message payload. */ + found: boolean; +} + +/** + * Companion to `applyMessageCreated` in apply-message-events.ts: when a `messageCreated` event + * arrives, the messages-list cache for the open conversation gets the new message (that applier), + * and this one ALSO patches the matching conversation-list ROW so the inbox preview stays live + * without a refetch — updates `lastActivityAt`, splices the message onto the row's `messages[]` + * preview array (capped to the last few entries, matching `conversation-row.tsx`'s + * `lastMessagePreview` which only ever reads the LAST entry), and moves that row to the top of + * whichever page it was found on (conventional "most recent conversation first" ordering). + * + * Deliberately does NOT attempt to fabricate a brand-new row when the conversation isn't cached + * anywhere — the event payload is a single message, not a full conversation row, so there's no + * contact/channel/assignee data to build one from. Callers should invalidate instead (see `found` + * on the return value). + */ +const MAX_PREVIEW_MESSAGES = 20; + +export function applyConversationMessageCreated( + current: InfiniteConversationsData, + event: RealtimeEventCreateMessage, +): ApplyConversationMessageCreatedResult { + if (!current) return { data: current, found: false }; + + const incoming = narrowIncomingMessage(event.data); + if (!incoming) return { data: current, found: false }; + + let found = false; + + const pages = current.pages.map((page) => { + const matchIndex = page.data.findIndex((item) => item.id === incoming.conversationId); + if (matchIndex < 0) return page; + + found = true; + const matched = page.data[matchIndex]!; + const patchedRow: ConversationListItem = { + ...matched, + lastActivityAt: incoming.createdAt, + messages: [...matched.messages, incoming].slice(-MAX_PREVIEW_MESSAGES), + }; + + // Move the patched row to the front of this page — mirrors "most recently active + // conversation first" ordering without waiting on a refetch. + const rest = page.data.filter((_, index) => index !== matchIndex); + return { ...page, data: [patchedRow, ...rest] }; + }); + + return { data: { ...current, pages }, found }; +} + +// `conversationCreated` carries the full new conversation row as `data: unknown` (the source +// package deliberately keeps this untyped to avoid a database-schema dependency — see events.ts +// comment). Splicing an unknown-shaped row into the strongly-typed `ConversationListItem[]` cache +// risks silently corrupting it with missing fields the UI assumes exist, so there's no applier +// function for it here — use-realtime-handlers.ts calls `queryClient.invalidateQueries` directly +// for the active conversations-list keys instead: one extra network round-trip on a relatively +// rare event, in exchange for never rendering a malformed row. diff --git a/src/realtime/apply-message-events.test.ts b/src/realtime/apply-message-events.test.ts new file mode 100644 index 0000000..f2e160d --- /dev/null +++ b/src/realtime/apply-message-events.test.ts @@ -0,0 +1,120 @@ +import { applyMessageCreated, type InfiniteMessagesData } from '@/realtime/apply-message-events'; +import type { Message } from '@/features/chat/api/use-messages-infinite'; +import { RealtimeEventType } from '@/realtime/events'; + +function cacheWith(messages: Partial<Message>[]): InfiniteMessagesData { + return { + pages: [{ data: messages as Message[], nextCursor: null, prevCursor: null }], + pageParams: [undefined], + } as InfiniteMessagesData; +} + +function createdEvent(data: unknown) { + return { eventType: RealtimeEventType.messageCreated, data } as const; +} + +function firstPage(result: InfiniteMessagesData): Message[] { + return result!.pages[0]!.data; +} + +const CONVERSATION_A = 'conv-a'; +const CONVERSATION_B = 'conv-b'; + +describe('applyMessageCreated', () => { + it('defaults a missing attachments field to an empty array', () => { + // A wire payload without `attachments` would otherwise land in the cache and crash + // message-bubble.tsx on `message.attachments.length` — the same bug as the optimistic entry, + // reached from the websocket side instead. + const result = applyMessageCreated( + cacheWith([]), + createdEvent({ id: 'srv-1', text: 'hi', conversationId: CONVERSATION_A }), + CONVERSATION_A, + ); + + expect(firstPage(result)[0].attachments).toEqual([]); + }); + + it('passes existing attachments through untouched', () => { + const attachments = [{ id: 'att-1', fileType: 'image', url: 'https://cdn/x.jpg' }]; + const result = applyMessageCreated( + cacheWith([]), + createdEvent({ id: 'srv-1', text: 'hi', attachments, conversationId: CONVERSATION_A }), + CONVERSATION_A, + ); + + expect(firstPage(result)[0].attachments).toEqual(attachments); + }); + + it('ignores a payload with no usable id', () => { + const cache = cacheWith([{ id: 'existing' }]); + + expect(applyMessageCreated(cache, createdEvent({ text: 'no id' }), CONVERSATION_A)).toBe(cache); + expect(applyMessageCreated(cache, createdEvent(null), CONVERSATION_A)).toBe(cache); + }); + + it('replaces a pending optimistic entry that shares the clientId', () => { + const cache = cacheWith([{ id: 'optimistic-99', clientId: '99', text: 'pending' }]); + + const result = applyMessageCreated( + cache, + createdEvent({ + id: 'srv-1', + clientId: '99', + text: 'confirmed', + attachments: [], + conversationId: CONVERSATION_A, + }), + CONVERSATION_A, + ); + + const data = firstPage(result); + expect(data).toHaveLength(1); + expect(data[0].id).toBe('srv-1'); + expect(data[0].text).toBe('confirmed'); + }); + + it('prepends a message that is not already cached', () => { + const result = applyMessageCreated( + cacheWith([{ id: 'older' }]), + createdEvent({ id: 'srv-1', attachments: [], conversationId: CONVERSATION_A }), + CONVERSATION_A, + ); + + expect(firstPage(result).map((message) => message.id)).toEqual(['srv-1', 'older']); + }); + + it('does not duplicate a message already present by id', () => { + const result = applyMessageCreated( + cacheWith([{ id: 'srv-1' }]), + createdEvent({ id: 'srv-1', attachments: [], conversationId: CONVERSATION_A }), + CONVERSATION_A, + ); + + expect(firstPage(result)).toHaveLength(1); + }); + + it('no-ops when the incoming message belongs to a different conversation (cross-conversation leak guard)', () => { + const cache = cacheWith([{ id: 'existing-b' }]); + + const result = applyMessageCreated( + cache, + createdEvent({ id: 'srv-from-a', attachments: [], conversationId: CONVERSATION_A }), + CONVERSATION_B, + ); + + expect(result).toBe(cache); + expect(firstPage(result).map((message) => message.id)).toEqual(['existing-b']); + }); + + it('applies when the incoming message conversationId matches the target', () => { + const cache = cacheWith([{ id: 'existing-a' }]); + + const result = applyMessageCreated( + cache, + createdEvent({ id: 'srv-from-a', attachments: [], conversationId: CONVERSATION_A }), + CONVERSATION_A, + ); + + expect(firstPage(result).map((message) => message.id)).toEqual(['srv-from-a', 'existing-a']); + }); +}); diff --git a/src/realtime/apply-message-events.ts b/src/realtime/apply-message-events.ts new file mode 100644 index 0000000..0933c4f --- /dev/null +++ b/src/realtime/apply-message-events.ts @@ -0,0 +1,170 @@ +import type { + RealtimeEventCreateMessage, + RealtimeEventMessageDeleted, + RealtimeEventMessageFailed, + RealtimeEventMessageIdAssigned, + RealtimeEventMessageUpdated, +} from '@/realtime/events'; +import type { ListMessagesResponse, Message } from '@/features/chat/api/use-messages-infinite'; + +/** + * Pure functions: `(currentData, event) => newData`, never mutating the input — called from + * use-realtime-handlers.ts via `queryClient.setQueryData`. Kept pure/side-effect-free so they stay + * easy to unit test later (the plan notes jest-expo coverage for exactly this kind of pure cache + * applier, even though no test infra exists yet this phase). + */ +export type InfiniteMessagesData = + { pages: ListMessagesResponse[]; pageParams: unknown[] } | undefined; + +/** Normalizes as well as narrows: `attachments` is required by the `Message` type and dereferenced + * unguarded by message-bubble.tsx, but nothing guarantees a wire payload includes it. Defaulting it + * here (rather than only guarding at render) keeps the cache well-formed for every consumer. + * Exported so `apply-conversation-events.ts` and other realtime helpers can reuse the same + * narrowing when they only need the conversationId/id out of an untyped `messageCreated` payload. */ +export function narrowIncomingMessage(data: unknown): Message | null { + if (!data || typeof data !== 'object') return null; + if (!('id' in data) || typeof data.id !== 'string') return null; + const message = data as Message; + return Array.isArray(message.attachments) ? message : { ...message, attachments: [] }; +} + +/** + * messageCreated: if the event carries a `clientId` that matches a pending optimistic entry in + * cache (our own send, echoed back), replace that entry with the confirmed message — this is the + * SAME reconciliation use-send-message.ts already does from its own mutation response, so this + * path mainly matters for messages that arrive from OTHER clients/channels. If no match, prepend + * as a new message (guarding against a duplicate by id, in case the same event is delivered twice + * or the sender's own mutation-response reconciliation already inserted it). + * + * `targetConversationId` guards against the cross-conversation cache leak: the caller + * (use-realtime-handlers.ts) is expected to only route a `messageCreated` event to the ONE + * matching conversation's messages-list query key in the first place, but this guard is kept as + * defense-in-depth (e.g. against a future caller that fans this out more broadly again) — a + * message whose own `conversationId` doesn't match the query it's being applied to is a no-op. + */ +export function applyMessageCreated( + current: InfiniteMessagesData, + event: RealtimeEventCreateMessage, + targetConversationId: string, +): InfiniteMessagesData { + if (!current) return current; + const incoming = narrowIncomingMessage(event.data); + if (!incoming) return current; + if (incoming.conversationId !== targetConversationId) return current; + + const [firstPage, ...restPages] = current.pages; + if (!firstPage) return current; + + const clientId = 'clientId' in incoming ? incoming.clientId : undefined; + const pendingIndex = clientId + ? firstPage.data.findIndex((message) => 'clientId' in message && message.clientId === clientId) + : -1; + const alreadyPresent = firstPage.data.some((message) => message.id === incoming.id); + + let newData: Message[]; + if (pendingIndex >= 0) { + newData = firstPage.data.map((message, index) => (index === pendingIndex ? incoming : message)); + } else if (alreadyPresent) { + newData = firstPage.data; + } else { + newData = [incoming, ...firstPage.data]; + } + + return { ...current, pages: [{ ...firstPage, data: newData }, ...restPages] }; +} + +export function applyMessageDeleted( + current: InfiniteMessagesData, + event: RealtimeEventMessageDeleted, +): InfiniteMessagesData { + if (!current) return current; + const deletedIds = new Set(event.data.messageIds); + + return { + ...current, + pages: current.pages.map((page) => ({ + ...page, + data: page.data.map((message) => + deletedIds.has(message.id) ? { ...message, deletedAt: new Date().toISOString() } : message, + ), + })), + }; +} + +/** + * Patches the edited text only. The web app's equivalent handler (`updateMessageText` in + * ../aha.chat apps/builder/src/features/chat/store/chat-store.ts) also reconstructs a full + * attachment object from `newAttachmentPath`/`newAttachmentMimeType`/etc when an attachment was + * added/replaced/removed during the edit — deliberately NOT mirrored here, since Phase 4's own + * edit-message UI is itself a stub (no attachment-edit affordance exists yet to trigger this + * server-side), so building the full attachment-reconstruction logic now would have nothing to + * exercise it. Revisit once message edit UI grows an attachment-replace flow. + */ +export function applyMessageUpdated( + current: InfiniteMessagesData, + event: RealtimeEventMessageUpdated, +): InfiniteMessagesData { + if (!current) return current; + const { messageId, newText } = event.data; + + return { + ...current, + pages: current.pages.map((page) => ({ + ...page, + data: page.data.map((message) => + message.id === messageId ? { ...message, text: newText } : message, + ), + })), + }; +} + +/** Mirrors the `__optimisticStatus`/`__optimisticError` shape use-send-message.ts's onError + * already writes, so message-bubble.tsx's existing rendering (via getOptimisticStatus) picks this + * up unchanged — this is the realtime-driven version of the same failure signal, for cases where + * the failure is detected server-side/async rather than by the sender's own mutation rejecting. */ +export function applyMessageFailed( + current: InfiniteMessagesData, + event: RealtimeEventMessageFailed, +): InfiniteMessagesData { + if (!current) return current; + const { messageId, clientId, error } = event.data; + + return { + ...current, + pages: current.pages.map((page) => ({ + ...page, + data: page.data.map((message) => { + const matches = clientId + ? 'clientId' in message && message.clientId === clientId + : message.id === messageId; + if (!matches) return message; + return { + ...message, + __optimisticStatus: 'failed', + __optimisticError: error ?? 'Failed to send', + } as unknown as Message; + }), + })), + }; +} + +/** Mirrors the web app's `assignMessageCommentId` handler exactly (../aha.chat + * apps/builder/src/features/chat/store/chat-store.ts) — the comment id lands on `sourceId`, not + * `parentId` (parentId means something different: thread/reply linkage). */ +export function applyMessageIdAssigned( + current: InfiniteMessagesData, + event: RealtimeEventMessageIdAssigned, +): InfiniteMessagesData { + if (!current) return current; + const { messageId, commentId } = event.data; + + return { + ...current, + pages: current.pages.map((page) => ({ + ...page, + data: page.data.map((message) => + message.id === messageId ? { ...message, sourceId: commentId } : message, + ), + })), + }; +} diff --git a/src/realtime/events.ts b/src/realtime/events.ts new file mode 100644 index 0000000..d87ccf1 --- /dev/null +++ b/src/realtime/events.ts @@ -0,0 +1,140 @@ +/** + * MIRRORED TYPES — kept in manual sync with `aha.chat/packages/partysocket-config/src/schemas.ts`. + * + * This app lives in a separate git repo from aha.chat (not a pnpm workspace member), so there is + * no live cross-repo import. Do NOT import aha.chat's `lib.ts` (server-side realtime wiring) — + * only these type-only definitions, mirrored by hand. + * + * When the source file changes, re-copy its `RealtimeEventType` and `RealtimeEvent*` shapes here. + * Last synced against aha.chat's current `packages/partysocket-config/src/schemas.ts` (Phase 5, + * 2026-08-21) — added conversationCreated/conversationUpdated, per the plan's pre-flight note. + */ + +export const RealtimeEventType = { + messageCreated: 'messageCreated', + messageDeleted: 'messageDeleted', + messageUpdated: 'messageUpdated', + messageIdAssigned: 'messageIdAssigned', + messageFailed: 'messageFailed', + typing: 'typing', + contactBlocked: 'contactBlocked', + contactUnblocked: 'contactUnblocked', + conversationAssigned: 'conversationAssigned', + notifyExportResult: 'notifyExportResult', + conversationCreated: 'conversationCreated', + conversationUpdated: 'conversationUpdated', +} as const; + +export type RealtimeEventCreateMessage = { + eventType: typeof RealtimeEventType.messageCreated; + data: unknown; +}; + +export type RealtimeEventMessageDeleted = { + eventType: typeof RealtimeEventType.messageDeleted; + data: { + messageIds: string[]; + }; +}; + +export type RealtimeEventMessageIdAssigned = { + eventType: typeof RealtimeEventType.messageIdAssigned; + data: { + messageId: string; + commentId: string; + }; +}; + +export type RealtimeEventMessageUpdated = { + eventType: typeof RealtimeEventType.messageUpdated; + data: { + messageId: string; + newText: string; + newAttachmentPath?: string | null; + newAttachmentPublicUrl?: string | null; + newAttachmentMimeType?: string | null; + newAttachmentWidth?: number; + newAttachmentHeight?: number; + removedAttachment?: boolean; + }; +}; + +export type RealtimeEventMessageFailed = { + eventType: typeof RealtimeEventType.messageFailed; + data: { + messageId: string; + clientId?: string; + error: string | null; + }; +}; + +export type RealtimeEventTyping = { + eventType: typeof RealtimeEventType.typing; + data: { + conversationId: string; + typing: boolean; + seconds: number; + }; +}; + +export type RealtimeEventContactCommon = { + eventType: typeof RealtimeEventType.contactBlocked | typeof RealtimeEventType.contactUnblocked; + data: { + contactId: string; + }; +}; + +export type RealtimeEventConversationAssigned = { + eventType: typeof RealtimeEventType.conversationAssigned; + data: { + conversationIds: string[]; + assignedUserId: string | null; + assignedInboxTeamId: string | null; + }; +}; + +export type RealtimeEventNotifyExportResult = { + eventType: typeof RealtimeEventType.notifyExportResult; + data: { + outputPath: string; + status: 'pending' | 'processing' | 'completed' | 'failed'; + error?: string; + }; +}; + +export type RealtimeEventConversationCreated = { + eventType: typeof RealtimeEventType.conversationCreated; + // Full conversation row — shape owned by @chatbotx.io/business's ConversationModel; kept as + // `unknown` here to match the source package (avoids a dependency on the database schema). + data: unknown; +}; + +export type RealtimeEventConversationUpdatedChanges = { + archivedAt?: string | null; + assignedUserId?: string | null; + assignedInboxTeamId?: string | null; + followed?: boolean; + agentLastReadAt?: string | null; + botEnabled?: boolean; +}; + +export type RealtimeEventConversationUpdated = { + eventType: typeof RealtimeEventType.conversationUpdated; + data: { + conversationIds: string[]; + changes: RealtimeEventConversationUpdatedChanges; + }; +}; + +export type RealtimeEventData = + | RealtimeEventCreateMessage + | RealtimeEventMessageDeleted + | RealtimeEventMessageIdAssigned + | RealtimeEventMessageUpdated + | RealtimeEventMessageFailed + | RealtimeEventContactCommon + | RealtimeEventConversationAssigned + | RealtimeEventTyping + | RealtimeEventNotifyExportResult + | RealtimeEventConversationCreated + | RealtimeEventConversationUpdated; diff --git a/src/realtime/one-time-token.ts b/src/realtime/one-time-token.ts new file mode 100644 index 0000000..68822c1 --- /dev/null +++ b/src/realtime/one-time-token.ts @@ -0,0 +1,30 @@ +import { env } from '@/config/env'; + +/** + * `GET /api/auth/one-time-token/generate` — better-auth's `oneTimeToken()` plugin (enabled + * server-side, confirmed in ../aha.chat packages/auth/src/server.ts). Hand-rolled fetch, same + * pattern as api/auth-endpoints.ts, since this isn't part of the oRPC-generated apiClient. + */ +export class OneTimeTokenError extends Error { + constructor(message: string) { + super(message); + this.name = 'OneTimeTokenError'; + } +} + +export async function generateOneTimeToken(bearerToken: string): Promise<string> { + const response = await fetch(`${env.apiBaseUrl}/api/auth/one-time-token/generate`, { + headers: { Authorization: `Bearer ${bearerToken}` }, + }); + + if (!response.ok) { + throw new OneTimeTokenError(`Failed to generate one-time token (${response.status})`); + } + + const body = (await response.json()) as { token?: string } | null; + if (!body?.token) { + throw new OneTimeTokenError('One-time token response missing "token" field.'); + } + + return body.token; +} diff --git a/src/realtime/parse-event.test.ts b/src/realtime/parse-event.test.ts new file mode 100644 index 0000000..2050697 --- /dev/null +++ b/src/realtime/parse-event.test.ts @@ -0,0 +1,277 @@ +import { parseRealtimeEvent } from './parse-event'; +import { RealtimeEventType } from './events'; + +describe('parseRealtimeEvent', () => { + describe('malformed input', () => { + it('returns null for garbage (non-JSON) text', () => { + expect(parseRealtimeEvent('not json{{')).toBeNull(); + }); + + it('returns null for a JSON value that is not an object', () => { + expect(parseRealtimeEvent('42')).toBeNull(); + expect(parseRealtimeEvent('"a string"')).toBeNull(); + expect(parseRealtimeEvent('[1,2,3]')).toBeNull(); + expect(parseRealtimeEvent('null')).toBeNull(); + }); + + it('returns null when eventType is missing', () => { + expect(parseRealtimeEvent(JSON.stringify({ data: {} }))).toBeNull(); + }); + + it('returns null when eventType is not a recognized value', () => { + expect(parseRealtimeEvent(JSON.stringify({ eventType: 'bogusEvent', data: {} }))).toBeNull(); + }); + + it('returns null when eventType is not a string', () => { + expect(parseRealtimeEvent(JSON.stringify({ eventType: 123, data: {} }))).toBeNull(); + }); + }); + + describe('messageCreated', () => { + it('accepts any present data payload (untyped by design)', () => { + const raw = JSON.stringify({ + eventType: RealtimeEventType.messageCreated, + data: { id: 'm1', text: 'hi' }, + }); + const result = parseRealtimeEvent(raw); + expect(result?.eventType).toBe(RealtimeEventType.messageCreated); + }); + }); + + describe('messageDeleted', () => { + it('accepts a valid messageIds array', () => { + const raw = JSON.stringify({ + eventType: RealtimeEventType.messageDeleted, + data: { messageIds: ['a', 'b'] }, + }); + expect(parseRealtimeEvent(raw)).not.toBeNull(); + }); + + it('rejects a non-array messageIds', () => { + const raw = JSON.stringify({ + eventType: RealtimeEventType.messageDeleted, + data: { messageIds: 'a' }, + }); + expect(parseRealtimeEvent(raw)).toBeNull(); + }); + + it('rejects a messageIds array with non-string elements', () => { + const raw = JSON.stringify({ + eventType: RealtimeEventType.messageDeleted, + data: { messageIds: [1, 2] }, + }); + expect(parseRealtimeEvent(raw)).toBeNull(); + }); + }); + + describe('messageIdAssigned', () => { + it('accepts a valid payload', () => { + const raw = JSON.stringify({ + eventType: RealtimeEventType.messageIdAssigned, + data: { messageId: 'm1', commentId: 'c1' }, + }); + expect(parseRealtimeEvent(raw)).not.toBeNull(); + }); + + it('rejects a missing commentId', () => { + const raw = JSON.stringify({ + eventType: RealtimeEventType.messageIdAssigned, + data: { messageId: 'm1' }, + }); + expect(parseRealtimeEvent(raw)).toBeNull(); + }); + }); + + describe('messageUpdated', () => { + it('accepts a minimal valid payload', () => { + const raw = JSON.stringify({ + eventType: RealtimeEventType.messageUpdated, + data: { messageId: 'm1', newText: 'edited' }, + }); + expect(parseRealtimeEvent(raw)).not.toBeNull(); + }); + + it('accepts a full payload with optional attachment fields', () => { + const raw = JSON.stringify({ + eventType: RealtimeEventType.messageUpdated, + data: { + messageId: 'm1', + newText: 'edited', + newAttachmentPath: 'path.jpg', + newAttachmentPublicUrl: 'https://x/y.jpg', + newAttachmentMimeType: 'image/jpeg', + newAttachmentWidth: 100, + newAttachmentHeight: 200, + removedAttachment: false, + }, + }); + expect(parseRealtimeEvent(raw)).not.toBeNull(); + }); + + it('rejects a wrong-typed newText', () => { + const raw = JSON.stringify({ + eventType: RealtimeEventType.messageUpdated, + data: { messageId: 'm1', newText: 123 }, + }); + expect(parseRealtimeEvent(raw)).toBeNull(); + }); + + it('rejects a wrong-typed newAttachmentWidth', () => { + const raw = JSON.stringify({ + eventType: RealtimeEventType.messageUpdated, + data: { messageId: 'm1', newText: 'x', newAttachmentWidth: 'wide' }, + }); + expect(parseRealtimeEvent(raw)).toBeNull(); + }); + }); + + describe('messageFailed', () => { + it('accepts a valid payload with a null error', () => { + const raw = JSON.stringify({ + eventType: RealtimeEventType.messageFailed, + data: { messageId: 'm1', error: null }, + }); + expect(parseRealtimeEvent(raw)).not.toBeNull(); + }); + + it('accepts an optional clientId', () => { + const raw = JSON.stringify({ + eventType: RealtimeEventType.messageFailed, + data: { messageId: 'm1', clientId: 'c1', error: 'boom' }, + }); + expect(parseRealtimeEvent(raw)).not.toBeNull(); + }); + + it('rejects a missing messageId', () => { + const raw = JSON.stringify({ + eventType: RealtimeEventType.messageFailed, + data: { error: null }, + }); + expect(parseRealtimeEvent(raw)).toBeNull(); + }); + }); + + describe('typing', () => { + it('accepts a valid payload', () => { + const raw = JSON.stringify({ + eventType: RealtimeEventType.typing, + data: { conversationId: 'c1', typing: true, seconds: 5 }, + }); + expect(parseRealtimeEvent(raw)).not.toBeNull(); + }); + + it('rejects a missing seconds field', () => { + const raw = JSON.stringify({ + eventType: RealtimeEventType.typing, + data: { conversationId: 'c1', typing: true }, + }); + expect(parseRealtimeEvent(raw)).toBeNull(); + }); + + it('rejects a non-boolean typing field', () => { + const raw = JSON.stringify({ + eventType: RealtimeEventType.typing, + data: { conversationId: 'c1', typing: 'yes', seconds: 5 }, + }); + expect(parseRealtimeEvent(raw)).toBeNull(); + }); + }); + + describe('contactBlocked / contactUnblocked', () => { + it('accepts a valid payload for both types', () => { + expect( + parseRealtimeEvent( + JSON.stringify({ + eventType: RealtimeEventType.contactBlocked, + data: { contactId: 'c1' }, + }), + ), + ).not.toBeNull(); + expect( + parseRealtimeEvent( + JSON.stringify({ + eventType: RealtimeEventType.contactUnblocked, + data: { contactId: 'c1' }, + }), + ), + ).not.toBeNull(); + }); + + it('rejects a missing contactId', () => { + const raw = JSON.stringify({ eventType: RealtimeEventType.contactBlocked, data: {} }); + expect(parseRealtimeEvent(raw)).toBeNull(); + }); + }); + + describe('conversationAssigned', () => { + it('accepts a valid payload', () => { + const raw = JSON.stringify({ + eventType: RealtimeEventType.conversationAssigned, + data: { conversationIds: ['c1'], assignedUserId: 'u1', assignedInboxTeamId: null }, + }); + expect(parseRealtimeEvent(raw)).not.toBeNull(); + }); + + it('rejects a non-array conversationIds', () => { + const raw = JSON.stringify({ + eventType: RealtimeEventType.conversationAssigned, + data: { conversationIds: 'c1', assignedUserId: null, assignedInboxTeamId: null }, + }); + expect(parseRealtimeEvent(raw)).toBeNull(); + }); + }); + + describe('notifyExportResult', () => { + it('accepts a valid payload', () => { + const raw = JSON.stringify({ + eventType: RealtimeEventType.notifyExportResult, + data: { outputPath: '/out.csv', status: 'completed' }, + }); + expect(parseRealtimeEvent(raw)).not.toBeNull(); + }); + + it('rejects an invalid status value', () => { + const raw = JSON.stringify({ + eventType: RealtimeEventType.notifyExportResult, + data: { outputPath: '/out.csv', status: 'bogus' }, + }); + expect(parseRealtimeEvent(raw)).toBeNull(); + }); + }); + + describe('conversationCreated', () => { + it('accepts any present data payload (untyped by design)', () => { + const raw = JSON.stringify({ + eventType: RealtimeEventType.conversationCreated, + data: { id: 'conv1' }, + }); + expect(parseRealtimeEvent(raw)).not.toBeNull(); + }); + }); + + describe('conversationUpdated', () => { + it('accepts a valid payload', () => { + const raw = JSON.stringify({ + eventType: RealtimeEventType.conversationUpdated, + data: { conversationIds: ['c1'], changes: { botEnabled: true, followed: false } }, + }); + expect(parseRealtimeEvent(raw)).not.toBeNull(); + }); + + it('rejects a missing changes object', () => { + const raw = JSON.stringify({ + eventType: RealtimeEventType.conversationUpdated, + data: { conversationIds: ['c1'] }, + }); + expect(parseRealtimeEvent(raw)).toBeNull(); + }); + + it('rejects a wrong-typed field inside changes', () => { + const raw = JSON.stringify({ + eventType: RealtimeEventType.conversationUpdated, + data: { conversationIds: ['c1'], changes: { botEnabled: 'yes' } }, + }); + expect(parseRealtimeEvent(raw)).toBeNull(); + }); + }); +}); diff --git a/src/realtime/parse-event.ts b/src/realtime/parse-event.ts new file mode 100644 index 0000000..86bdd33 --- /dev/null +++ b/src/realtime/parse-event.ts @@ -0,0 +1,167 @@ +import { RealtimeEventType, type RealtimeEventData } from './events'; + +/** + * Hand-rolled runtime type guards for every `RealtimeEventData` variant in events.ts — no zod + * dependency in this project (see AGENTS.md/plan), so each event shape is validated by hand. + * `parseRealtimeEvent` is the single entry point: parses the raw websocket text, validates the + * envelope (`eventType` + `data` object), then dispatches to the per-type guard. Returns `null` + * on ANY malformation (invalid JSON, non-object, unrecognized eventType, wrong-shaped data) + * rather than throwing — realtime-provider.tsx's onMessage handler is expected to `__DEV__ warn` + * and drop the event rather than crash the socket handler. + */ + +function isObject(value: unknown): value is Record<string, unknown> { + return typeof value === 'object' && value !== null && !Array.isArray(value); +} + +function isString(value: unknown): value is string { + return typeof value === 'string'; +} + +function isStringArray(value: unknown): value is string[] { + return Array.isArray(value) && value.every((item) => typeof item === 'string'); +} + +/** True when `value` is absent, `null`, or a string — the common shape for the optional + * nullable-string fields scattered across these event payloads. */ +function isOptionalNullableString(value: unknown): boolean { + return value === undefined || value === null || typeof value === 'string'; +} + +function validateMessageCreated(data: unknown): data is { data: unknown } { + // The source package deliberately keeps this payload untyped (see events.ts comment) — any + // object-or-not value is "valid" at this layer; downstream `narrowIncomingMessage` in + // apply-message-events.ts does the real per-field narrowing before it touches the cache. + return data !== undefined; +} + +function validateMessageDeleted(data: unknown): boolean { + return isObject(data) && isStringArray(data.messageIds); +} + +function validateMessageIdAssigned(data: unknown): boolean { + return isObject(data) && isString(data.messageId) && isString(data.commentId); +} + +function validateMessageUpdated(data: unknown): boolean { + if (!isObject(data)) return false; + if (!isString(data.messageId) || !isString(data.newText)) return false; + if (!isOptionalNullableString(data.newAttachmentPath)) return false; + if (!isOptionalNullableString(data.newAttachmentPublicUrl)) return false; + if (!isOptionalNullableString(data.newAttachmentMimeType)) return false; + if (data.newAttachmentWidth !== undefined && typeof data.newAttachmentWidth !== 'number') + return false; + if (data.newAttachmentHeight !== undefined && typeof data.newAttachmentHeight !== 'number') + return false; + if (data.removedAttachment !== undefined && typeof data.removedAttachment !== 'boolean') + return false; + return true; +} + +function validateMessageFailed(data: unknown): boolean { + if (!isObject(data)) return false; + if (!isString(data.messageId)) return false; + if (data.clientId !== undefined && typeof data.clientId !== 'string') return false; + if (data.error !== null && typeof data.error !== 'string') return false; + return true; +} + +function validateTyping(data: unknown): boolean { + return ( + isObject(data) && + isString(data.conversationId) && + typeof data.typing === 'boolean' && + typeof data.seconds === 'number' + ); +} + +function validateContactCommon(data: unknown): boolean { + return isObject(data) && isString(data.contactId); +} + +function validateConversationAssigned(data: unknown): boolean { + if (!isObject(data)) return false; + if (!isStringArray(data.conversationIds)) return false; + if (typeof data.assignedUserId !== 'string' && data.assignedUserId !== null) return false; + if (typeof data.assignedInboxTeamId !== 'string' && data.assignedInboxTeamId !== null) + return false; + return true; +} + +const NOTIFY_EXPORT_STATUSES = new Set(['pending', 'processing', 'completed', 'failed']); + +function validateNotifyExportResult(data: unknown): boolean { + if (!isObject(data)) return false; + if (!isString(data.outputPath)) return false; + if (!isString(data.status) || !NOTIFY_EXPORT_STATUSES.has(data.status)) return false; + if (data.error !== undefined && typeof data.error !== 'string') return false; + return true; +} + +function validateConversationCreated(data: unknown): boolean { + // Untyped payload by design (see events.ts) — any presence is "valid" here. + return data !== undefined; +} + +function validateConversationUpdatedChanges(changes: unknown): boolean { + if (!isObject(changes)) return false; + if (!isOptionalNullableString(changes.archivedAt)) return false; + if (!isOptionalNullableString(changes.assignedUserId)) return false; + if (!isOptionalNullableString(changes.assignedInboxTeamId)) return false; + if (changes.followed !== undefined && typeof changes.followed !== 'boolean') return false; + if (!isOptionalNullableString(changes.agentLastReadAt)) return false; + if (changes.botEnabled !== undefined && typeof changes.botEnabled !== 'boolean') return false; + return true; +} + +function validateConversationUpdated(data: unknown): boolean { + return ( + isObject(data) && + isStringArray(data.conversationIds) && + validateConversationUpdatedChanges(data.changes) + ); +} + +/** Parses and validates a raw websocket text payload into a `RealtimeEventData`, or `null` if the + * payload is malformed JSON, not an object, missing/unrecognized `eventType`, or has a `data` + * shape that doesn't match the event type. Never throws. */ +export function parseRealtimeEvent(raw: string): RealtimeEventData | null { + let parsed: unknown; + try { + parsed = JSON.parse(raw); + } catch { + return null; + } + + if (!isObject(parsed)) return null; + const { eventType, data } = parsed; + if (typeof eventType !== 'string') return null; + + switch (eventType) { + case RealtimeEventType.messageCreated: + return validateMessageCreated(data) ? (parsed as RealtimeEventData) : null; + case RealtimeEventType.messageDeleted: + return validateMessageDeleted(data) ? (parsed as RealtimeEventData) : null; + case RealtimeEventType.messageIdAssigned: + return validateMessageIdAssigned(data) ? (parsed as RealtimeEventData) : null; + case RealtimeEventType.messageUpdated: + return validateMessageUpdated(data) ? (parsed as RealtimeEventData) : null; + case RealtimeEventType.messageFailed: + return validateMessageFailed(data) ? (parsed as RealtimeEventData) : null; + case RealtimeEventType.typing: + return validateTyping(data) ? (parsed as RealtimeEventData) : null; + case RealtimeEventType.contactBlocked: + case RealtimeEventType.contactUnblocked: + return validateContactCommon(data) ? (parsed as RealtimeEventData) : null; + case RealtimeEventType.conversationAssigned: + return validateConversationAssigned(data) ? (parsed as RealtimeEventData) : null; + case RealtimeEventType.notifyExportResult: + return validateNotifyExportResult(data) ? (parsed as RealtimeEventData) : null; + case RealtimeEventType.conversationCreated: + return validateConversationCreated(data) ? (parsed as RealtimeEventData) : null; + case RealtimeEventType.conversationUpdated: + return validateConversationUpdated(data) ? (parsed as RealtimeEventData) : null; + default: + return null; + } +} diff --git a/src/realtime/realtime-provider.tsx b/src/realtime/realtime-provider.tsx new file mode 100644 index 0000000..63880ba --- /dev/null +++ b/src/realtime/realtime-provider.tsx @@ -0,0 +1,126 @@ +import { useQueryClient } from '@tanstack/react-query'; +import { usePartySocket } from 'partysocket/react'; +import type { PropsWithChildren } from 'react'; +import { useCallback, useEffect } from 'react'; + +import { getAuthToken } from '@/api/auth-token'; +import { env } from '@/config/env'; +import { useWorkspaceStore } from '@/stores/use-workspace-store'; + +import { invalidateWorkspaceQueries, useRealtimeAppState } from './app-state'; +import { generateOneTimeToken } from './one-time-token'; +import { parseRealtimeEvent } from './parse-event'; +import { clearAllTypingTimers } from './typing-timers'; +import { resetConnectionStore, useConnectionStore } from './use-connection-store'; +import { useRealtimeHandlers } from './use-realtime-handlers'; + +/** + * Real implementation, replacing the Phase 0-4 passthrough stub. Connects to the `workspaces` + * PartyKit party, room = workspaceId (mirrors ../aha.chat apps/builder/src/features/chat/chat-realtime.tsx + * and apps/realtime/src/parties/workspaces.ts). + * + * `?domain=` is REQUIRED on every mobile connection: the realtime server verifies the one-time + * token by resolving a tenant origin from either the WebSocket request's `Origin` header (browsers + * send this automatically) or a `?domain=<https url>` query param — React Native's WebSocket + * implementation sends no `Origin` header at all, so without `domain` the server has nothing to + * verify the token against and rejects the connection (verified by reading + * ../aha.chat apps/realtime/src/lib/auth.ts `resolveVerificationOrigin`). The value must be the + * SAME origin that serves `/api/auth/one-time-token/verify` — i.e. `env.apiBaseUrl` — not the + * realtime server's own host. + */ +function ConnectedRealtimeProvider({ + children, + workspaceId, +}: PropsWithChildren<{ workspaceId: string }>) { + const queryClient = useQueryClient(); + const handleEvent = useRealtimeHandlers(queryClient, workspaceId); + const setStatus = useConnectionStore((state) => state.setStatus); + const markOpened = useConnectionStore((state) => state.markOpened); + + const socket = usePartySocket({ + host: env.wsUrl, + party: 'workspaces', + room: workspaceId, + query: async () => { + const authToken = await getAuthToken(); + if (!authToken) { + return {}; + } + const oneTimeToken = await generateOneTimeToken(authToken); + return { token: oneTimeToken, domain: env.apiBaseUrl }; + }, + onMessage(event) { + const parsed = parseRealtimeEvent(event.data); + if (!parsed) { + if (__DEV__) { + console.warn('[realtime] dropped malformed event payload', event.data); + } + return; + } + handleEvent(parsed); + }, + onOpen() { + // `hasEverOpened` is read BEFORE `markOpened` flips it, so this correctly distinguishes the + // very first connect (no reconciliation needed — the initial query fetches are already + // fresh) from a reconnect after a drop (where events may have been missed while + // disconnected, so the active workspace's queries need to be reconciled against the server). + if (useConnectionStore.getState().hasEverOpened) { + invalidateWorkspaceQueries(queryClient, workspaceId); + } + markOpened(); + }, + onClose() { + setStatus('closed'); + }, + onError() { + setStatus('closed'); + }, + }); + + // Reset to 'connecting' whenever this component (re)mounts (fresh workspace, per the `key` on + // RealtimeProvider below) — usePartySocket starts connecting immediately, so this reflects that + // without waiting on the first onOpen/onClose callback. + useEffect(() => { + resetConnectionStore(); + return () => { + clearAllTypingTimers(); + }; + // Runs once per mount — workspaceId is this component's whole identity via the parent's + // `key={workspaceId}`, so a dependency array is unnecessary; teardown clears timers for the + // room this instance owned. + }, []); + + const handleBackground = useCallback(() => { + socket.close(); + }, [socket]); + + const handleForeground = useCallback(() => { + socket.reconnect(); + invalidateWorkspaceQueries(queryClient, workspaceId); + }, [socket, queryClient, workspaceId]); + + useRealtimeAppState({ onBackground: handleBackground, onForeground: handleForeground }); + + return children; +} + +/** Only connects once a workspace is selected — defensive, since the tabs layout already guards + * on `currentWorkspaceId` before mounting this provider, but a null id here should never attempt + * a connection rather than crash on an empty room name. */ +export function RealtimeProvider({ children }: PropsWithChildren) { + const workspaceId = useWorkspaceStore((state) => state.currentWorkspaceId); + + if (!workspaceId) { + return children; + } + + return ( + // `key={workspaceId}` forces a full remount on workspace switch, tearing down the old + // PartySocket connection (and its usePartySocket internal state) rather than reusing the + // component instance with a new `room` prop — cleaner than relying on the socket library to + // detect and rejoin a different room on its own. + <ConnectedRealtimeProvider key={workspaceId} workspaceId={workspaceId}> + {children} + </ConnectedRealtimeProvider> + ); +} diff --git a/src/realtime/typing-timers.test.ts b/src/realtime/typing-timers.test.ts new file mode 100644 index 0000000..f6c51e1 --- /dev/null +++ b/src/realtime/typing-timers.test.ts @@ -0,0 +1,90 @@ +import { clearAllTypingTimers, clearTypingTimer, scheduleTypingExpiry } from './typing-timers'; + +describe('typing-timers', () => { + beforeEach(() => { + jest.useFakeTimers(); + clearAllTypingTimers(); + }); + + afterEach(() => { + clearAllTypingTimers(); + jest.useRealTimers(); + }); + + it('calls onExpire after the given number of seconds', () => { + const onExpire = jest.fn(); + scheduleTypingExpiry('conv-1', 5, onExpire); + + jest.advanceTimersByTime(4999); + expect(onExpire).not.toHaveBeenCalled(); + + jest.advanceTimersByTime(1); + expect(onExpire).toHaveBeenCalledTimes(1); + }); + + it('resets the countdown when scheduled again for the same conversation', () => { + const onExpire = jest.fn(); + scheduleTypingExpiry('conv-1', 5, onExpire); + + jest.advanceTimersByTime(3000); + scheduleTypingExpiry('conv-1', 5, onExpire); + + jest.advanceTimersByTime(4000); + expect(onExpire).not.toHaveBeenCalled(); + + jest.advanceTimersByTime(1000); + expect(onExpire).toHaveBeenCalledTimes(1); + }); + + it('clearTypingTimer prevents onExpire from firing', () => { + const onExpire = jest.fn(); + scheduleTypingExpiry('conv-1', 5, onExpire); + + clearTypingTimer('conv-1'); + jest.advanceTimersByTime(10_000); + + expect(onExpire).not.toHaveBeenCalled(); + }); + + it('clearTypingTimer is a no-op for an unknown conversationId', () => { + expect(() => clearTypingTimer('does-not-exist')).not.toThrow(); + }); + + it('tracks timers independently per conversation', () => { + const onExpireA = jest.fn(); + const onExpireB = jest.fn(); + scheduleTypingExpiry('conv-a', 2, onExpireA); + scheduleTypingExpiry('conv-b', 5, onExpireB); + + jest.advanceTimersByTime(2000); + expect(onExpireA).toHaveBeenCalledTimes(1); + expect(onExpireB).not.toHaveBeenCalled(); + + jest.advanceTimersByTime(3000); + expect(onExpireB).toHaveBeenCalledTimes(1); + }); + + it('clearAllTypingTimers cancels every pending timer', () => { + const onExpireA = jest.fn(); + const onExpireB = jest.fn(); + scheduleTypingExpiry('conv-a', 2, onExpireA); + scheduleTypingExpiry('conv-b', 5, onExpireB); + + clearAllTypingTimers(); + jest.advanceTimersByTime(10_000); + + expect(onExpireA).not.toHaveBeenCalled(); + expect(onExpireB).not.toHaveBeenCalled(); + }); + + it('defaults seconds=0 to firing immediately when advanced by 0ms (TTL math handled by caller)', () => { + // Note: the "default to 5s when seconds is 0/undefined" fallback lives in the CALLER + // (use-realtime-handlers.ts), not this scheduler — scheduleTypingExpiry always honors + // whatever `seconds` it's given, including 0. + const onExpire = jest.fn(); + scheduleTypingExpiry('conv-1', 0, onExpire); + + jest.advanceTimersByTime(0); + expect(onExpire).toHaveBeenCalledTimes(1); + }); +}); diff --git a/src/realtime/typing-timers.ts b/src/realtime/typing-timers.ts new file mode 100644 index 0000000..8522d0c --- /dev/null +++ b/src/realtime/typing-timers.ts @@ -0,0 +1,46 @@ +/** + * Expiry timers for realtime `typing` events. The `typing` payload carries a `seconds` TTL (see + * events.ts's `RealtimeEventTyping`) that was previously ignored entirely — once + * `setTyping(conversationId, true)` fired, nothing ever cleared it if the sender's own + * `typing: false` follow-up was dropped/delayed, so "Typing…" could stick forever. This module is + * a tiny scheduler keyed by conversationId, backed by a module-level timer map (not React state — + * it's driven from use-realtime-handlers.ts, outside any component's render). + */ + +const timers = new Map<string, ReturnType<typeof setTimeout>>(); + +/** Schedules `onExpire` to run after `seconds` (falling back to a default TTL for 0/undefined — + * see use-realtime-handlers.ts's call site), clearing any existing timer for the same + * conversationId first so a fresh `typing: true` event resets the countdown rather than stacking + * timers. */ +export function scheduleTypingExpiry( + conversationId: string, + seconds: number, + onExpire: () => void, +): void { + clearTypingTimer(conversationId); + const timer = setTimeout(() => { + timers.delete(conversationId); + onExpire(); + }, seconds * 1000); + timers.set(conversationId, timer); +} + +/** Clears the pending expiry timer for one conversation, if any — called when an explicit + * `typing: false` event arrives (the sender stopped before the TTL elapsed) so it doesn't fire a + * redundant `setTyping(id, false)` later. */ +export function clearTypingTimer(conversationId: string): void { + const existing = timers.get(conversationId); + if (existing) { + clearTimeout(existing); + timers.delete(conversationId); + } +} + +/** Clears every pending expiry timer — called on socket teardown (workspace switch/unmount) so no + * stale timer from a torn-down connection fires after a new one has taken over the same + * conversationId keys. */ +export function clearAllTypingTimers(): void { + timers.forEach((timer) => clearTimeout(timer)); + timers.clear(); +} diff --git a/src/realtime/use-connection-store.ts b/src/realtime/use-connection-store.ts new file mode 100644 index 0000000..342a628 --- /dev/null +++ b/src/realtime/use-connection-store.ts @@ -0,0 +1,51 @@ +import { create } from 'zustand'; + +/** + * Client-state store mirroring the PartySocket connection's lifecycle (see realtime-provider.tsx, + * which wires `usePartySocket`'s `onOpen`/`onClose`/`onError` into this). Purely reflects socket + * state — it does not own reconnect logic (partysocket handles retry itself). Consumed by + * `ConnectionBanner` call sites (Inbox + chat screen) to render a real connecting/reconnecting/ + * offline indicator instead of the previous hardcoded 'online'. + */ +export type ConnectionStatus = 'connecting' | 'open' | 'closed'; + +interface ConnectionStoreState { + status: ConnectionStatus; + /** Whether the socket has ever successfully reached `open` at least once during this app + * session. Distinguishes the FIRST connect (status 'connecting' with `hasEverOpened: false` → + * banner shows "Connecting…") from a later drop-and-retry (status 'connecting'/'closed' with + * `hasEverOpened: true` → banner shows "Reconnecting…"/"You're offline"). Reset to `false` on + * every workspace switch since realtime-provider.tsx remounts (`key={workspaceId}`) and this + * store is a plain singleton, not scoped per-provider-instance — see `resetConnectionStore`. */ + hasEverOpened: boolean; + setStatus: (status: ConnectionStatus) => void; + markOpened: () => void; +} + +export const useConnectionStore = create<ConnectionStoreState>((set) => ({ + status: 'connecting', + hasEverOpened: false, + setStatus: (status) => set({ status }), + markOpened: () => set({ status: 'open', hasEverOpened: true }), +})); + +/** Resets the store back to its initial "never connected" state — call when the realtime provider + * itself is torn down for a reason that should NOT be read as "reconnecting" (e.g. workspace + * switch, where the next connect is a fresh first-connect from the new provider's point of view, + * not a resume of the old one). */ +export function resetConnectionStore(): void { + useConnectionStore.setState({ status: 'connecting', hasEverOpened: false }); +} + +/** Maps the store's `status`/`hasEverOpened` pair to `ConnectionBanner`'s four-state prop, so both + * mount sites (Inbox header, chat screen) render identical banner text/color for the same + * underlying socket state instead of each hand-rolling the mapping. `open` intentionally still + * renders as `ConnectionBanner`'s `'online'` state briefly (see that component's own auto-hide + * behavior) rather than being hidden here — the banner owns "when to disappear". */ +export function useConnectionBannerState(): 'online' | 'connecting' | 'reconnecting' | 'offline' { + return useConnectionStore((state) => { + if (state.status === 'open') return 'online'; + if (!state.hasEverOpened) return 'connecting'; + return state.status === 'connecting' ? 'reconnecting' : 'offline'; + }); +} diff --git a/src/realtime/use-realtime-handlers.test.ts b/src/realtime/use-realtime-handlers.test.ts new file mode 100644 index 0000000..caeae85 --- /dev/null +++ b/src/realtime/use-realtime-handlers.test.ts @@ -0,0 +1,224 @@ +import { QueryClient } from '@tanstack/react-query'; +import { renderHook } from '@testing-library/react-native'; + +import { queryKeys } from '@/api/query-keys'; +import { useChatStore } from '@/features/chat/stores/use-chat-store'; +import type { Message } from '@/features/chat/api/use-messages-infinite'; +import type { ConversationListItem } from '@/features/conversations/api/use-conversations-infinite'; +import { RealtimeEventType } from './events'; +import { useRealtimeHandlers } from './use-realtime-handlers'; + +const WORKSPACE_ID = 'ws-1'; + +function messagesCache(messages: Partial<Message>[]) { + return { + pages: [{ data: messages as Message[], nextCursor: null, prevCursor: null }], + pageParams: [undefined], + }; +} + +function fakeConversation(overrides: Partial<ConversationListItem> = {}): ConversationListItem { + return { + id: 'conv-a', + createdAt: '2026-01-01T00:00:00.000Z', + updatedAt: '2026-01-01T00:00:00.000Z', + botEnabled: true, + botResumeAt: null, + archivedAt: null, + additionalAttributes: null, + contactLastReadAt: null, + agentLastReadAt: null, + lastActivityAt: '2026-01-01T00:00:00.000Z', + followed: false, + workspaceId: WORKSPACE_ID, + contactId: 'contact-1', + sourceId: null, + lastStep: null, + currentStep: null, + adminRepliedAt: null, + contactRepliedAt: null, + contactInboxes: [], + messages: [], + contact: null, + assignedUser: null, + assignedInboxTeam: null, + ...overrides, + } as unknown as ConversationListItem; +} + +function conversationsCache(conversations: ConversationListItem[]) { + return { + pages: [{ data: conversations, nextCursor: null, prevCursor: null }], + pageParams: [undefined], + }; +} + +describe('useRealtimeHandlers', () => { + beforeEach(() => { + jest.useFakeTimers(); + useChatStore.setState({ typingByConversation: {} }); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + it('routes messageCreated to only the matching conversation messages cache, leaving others untouched', async () => { + const queryClient = new QueryClient(); + const keyA = queryKeys.ws.messages.list(WORKSPACE_ID, 'conv-a'); + const keyB = queryKeys.ws.messages.list(WORKSPACE_ID, 'conv-b'); + queryClient.setQueryData(keyA, messagesCache([{ id: 'existing-a' }])); + queryClient.setQueryData(keyB, messagesCache([{ id: 'existing-b' }])); + + const { result } = await renderHook(() => useRealtimeHandlers(queryClient, WORKSPACE_ID)); + + result.current({ + eventType: RealtimeEventType.messageCreated, + data: { id: 'new-msg', conversationId: 'conv-a', attachments: [] }, + }); + + const dataA = queryClient.getQueryData<ReturnType<typeof messagesCache>>(keyA); + const dataB = queryClient.getQueryData<ReturnType<typeof messagesCache>>(keyB); + + expect(dataA!.pages[0]!.data.map((m) => m.id)).toEqual(['new-msg', 'existing-a']); + expect(dataB!.pages[0]!.data.map((m) => m.id)).toEqual(['existing-b']); + }); + + it('patches the conversations-list row for the matching conversation on messageCreated', async () => { + const queryClient = new QueryClient(); + const listKey = queryKeys.ws.conversations.list(WORKSPACE_ID, {}); + queryClient.setQueryData( + listKey, + conversationsCache([fakeConversation({ id: 'conv-a', messages: [] })]), + ); + + const { result } = await renderHook(() => useRealtimeHandlers(queryClient, WORKSPACE_ID)); + + result.current({ + eventType: RealtimeEventType.messageCreated, + data: { + id: 'new-msg', + conversationId: 'conv-a', + createdAt: '2026-01-05T00:00:00.000Z', + attachments: [], + }, + }); + + const data = queryClient.getQueryData<ReturnType<typeof conversationsCache>>(listKey); + const row = data!.pages[0]!.data[0]!; + expect(row.lastActivityAt).toBe('2026-01-05T00:00:00.000Z'); + expect(row.messages.map((m) => m.id)).toEqual(['new-msg']); + }); + + it('debounces an invalidation of the conversations list when the conversation is not cached', async () => { + const queryClient = new QueryClient(); + const invalidateSpy = jest.spyOn(queryClient, 'invalidateQueries'); + const listKey = queryKeys.ws.conversations.list(WORKSPACE_ID, {}); + queryClient.setQueryData( + listKey, + conversationsCache([fakeConversation({ id: 'some-other-conv' })]), + ); + + const { result } = await renderHook(() => useRealtimeHandlers(queryClient, WORKSPACE_ID)); + + result.current({ + eventType: RealtimeEventType.messageCreated, + data: { id: 'new-msg', conversationId: 'not-cached', attachments: [] }, + }); + + expect(invalidateSpy).not.toHaveBeenCalled(); + jest.advanceTimersByTime(500); + expect(invalidateSpy).toHaveBeenCalledWith( + expect.objectContaining({ queryKey: ['ws', WORKSPACE_ID, 'conversations', 'list'] }), + ); + }); + + it('clears the typing flag automatically after the event TTL elapses', async () => { + const queryClient = new QueryClient(); + const { result } = await renderHook(() => useRealtimeHandlers(queryClient, WORKSPACE_ID)); + + result.current({ + eventType: RealtimeEventType.typing, + data: { conversationId: 'conv-a', typing: true, seconds: 1 }, + }); + + expect(useChatStore.getState().typingByConversation['conv-a']).toBe(true); + + jest.advanceTimersByTime(1000); + + expect(useChatStore.getState().typingByConversation['conv-a']).toBe(false); + }); + + it('does not clear typing early when an explicit typing:false event has not arrived', async () => { + const queryClient = new QueryClient(); + const { result } = await renderHook(() => useRealtimeHandlers(queryClient, WORKSPACE_ID)); + + result.current({ + eventType: RealtimeEventType.typing, + data: { conversationId: 'conv-a', typing: true, seconds: 5 }, + }); + + jest.advanceTimersByTime(4000); + expect(useChatStore.getState().typingByConversation['conv-a']).toBe(true); + }); + + it('clears the timer when an explicit typing:false event arrives before the TTL', async () => { + const queryClient = new QueryClient(); + const { result } = await renderHook(() => useRealtimeHandlers(queryClient, WORKSPACE_ID)); + + result.current({ + eventType: RealtimeEventType.typing, + data: { conversationId: 'conv-a', typing: true, seconds: 5 }, + }); + result.current({ + eventType: RealtimeEventType.typing, + data: { conversationId: 'conv-a', typing: false, seconds: 0 }, + }); + + expect(useChatStore.getState().typingByConversation['conv-a']).toBe(false); + + // The set-to-false spy check: advancing well past the original TTL should not throw or + // re-trigger anything odd (timer was cleared, nothing scheduled for false events). + jest.advanceTimersByTime(10_000); + expect(useChatStore.getState().typingByConversation['conv-a']).toBe(false); + }); + + it('defaults to a 5s TTL when seconds is 0', async () => { + const queryClient = new QueryClient(); + const { result } = await renderHook(() => useRealtimeHandlers(queryClient, WORKSPACE_ID)); + + result.current({ + eventType: RealtimeEventType.typing, + data: { conversationId: 'conv-a', typing: true, seconds: 0 }, + }); + + jest.advanceTimersByTime(4999); + expect(useChatStore.getState().typingByConversation['conv-a']).toBe(true); + jest.advanceTimersByTime(1); + expect(useChatStore.getState().typingByConversation['conv-a']).toBe(false); + }); + + it('conversationUpdated patches only the matching conversations-list row', async () => { + const queryClient = new QueryClient(); + const listKey = queryKeys.ws.conversations.list(WORKSPACE_ID, {}); + queryClient.setQueryData( + listKey, + conversationsCache([ + fakeConversation({ id: 'conv-a', botEnabled: true }), + fakeConversation({ id: 'conv-b', botEnabled: true }), + ]), + ); + + const { result } = await renderHook(() => useRealtimeHandlers(queryClient, WORKSPACE_ID)); + + result.current({ + eventType: RealtimeEventType.conversationUpdated, + data: { conversationIds: ['conv-a'], changes: { botEnabled: false } }, + }); + + const data = queryClient.getQueryData<ReturnType<typeof conversationsCache>>(listKey); + const rows = data!.pages[0]!.data; + expect(rows.find((r) => r.id === 'conv-a')?.botEnabled).toBe(false); + expect(rows.find((r) => r.id === 'conv-b')?.botEnabled).toBe(true); + }); +}); diff --git a/src/realtime/use-realtime-handlers.ts b/src/realtime/use-realtime-handlers.ts new file mode 100644 index 0000000..5cee479 --- /dev/null +++ b/src/realtime/use-realtime-handlers.ts @@ -0,0 +1,224 @@ +import type { QueryClient } from '@tanstack/react-query'; +import { useCallback } from 'react'; + +import { queryKeys } from '@/api/query-keys'; +import { useChatStore } from '@/features/chat/stores/use-chat-store'; +import { narrowIncomingMessage, type InfiniteMessagesData } from './apply-message-events'; +import { + applyMessageCreated, + applyMessageDeleted, + applyMessageFailed, + applyMessageIdAssigned, + applyMessageUpdated, +} from './apply-message-events'; +import type { InfiniteConversationsData } from './apply-conversation-events'; +import { + applyConversationMessageCreated, + applyContactBlockState, + applyContactDetailBlockState, + applyConversationAssigned, + applyConversationUpdated, +} from './apply-conversation-events'; +import { scheduleTypingExpiry, clearTypingTimer } from './typing-timers'; +import { RealtimeEventType, type RealtimeEventData } from './events'; + +/** Default typing-indicator TTL (seconds) when the event's own `seconds` field is 0/undefined — + * per the plan, a payload that doesn't specify a TTL still shouldn't stick forever. */ +const DEFAULT_TYPING_TTL_SECONDS = 5; + +/** How long to wait, after a `messageCreated` event whose conversation isn't found in ANY cached + * list page, before invalidating the conversations list — debounced (rather than firing per + * event) so a burst of messages for the same not-yet-cached conversation doesn't trigger a + * refetch storm. Keyed by workspaceId since this hook is itself workspace-scoped. */ +const CONVERSATION_INVALIDATE_DEBOUNCE_MS = 500; +const pendingConversationInvalidate = new Map<string, ReturnType<typeof setTimeout>>(); + +function debounceInvalidateConversationsList(queryClient: QueryClient, workspaceId: string) { + const existing = pendingConversationInvalidate.get(workspaceId); + if (existing) clearTimeout(existing); + + const timer = setTimeout(() => { + pendingConversationInvalidate.delete(workspaceId); + queryClient.invalidateQueries({ queryKey: ['ws', workspaceId, 'conversations', 'list'] }); + }, CONVERSATION_INVALIDATE_DEBOUNCE_MS); + pendingConversationInvalidate.set(workspaceId, timer); +} + +/** + * Dispatches a parsed realtime event to the right cache applier. + * + * `messageCreated` is special-cased: it's routed ONLY to the one messages-list query key matching + * its own `conversationId` (rather than fanning out to every cached messages query like the other + * message events below) — this is the actual fix for the cross-conversation leak where a message + * from conversation A could get prepended into conversation B's cache if both happened to be + * cached simultaneously. `applyMessageCreated`'s own `targetConversationId` guard is + * defense-in-depth on top of this. It also patches the conversations-list ROW for the same + * conversation via `applyConversationMessageCreated` so the inbox preview updates live. + * + * The other message events (`messageDeleted`/`messageUpdated`/`messageFailed`/ + * `messageIdAssigned`) match by message id already, so fanning out to every cached messages query + * and letting each applier no-op on a non-matching cache is safe and is left unchanged. + * + * Conversation events patch every cached conversations-list query (any filter combination) for + * this workspace, same as Phase 3's action-mutation cache patches. + */ +export function useRealtimeHandlers(queryClient: QueryClient, workspaceId: string) { + return useCallback( + (event: RealtimeEventData) => { + switch (event.eventType) { + case RealtimeEventType.messageCreated: { + const incoming = narrowIncomingMessage(event.data); + if (!incoming) break; + + queryClient.setQueryData<InfiniteMessagesData>( + queryKeys.ws.messages.list(workspaceId, incoming.conversationId), + (old) => applyMessageCreated(old, event, incoming.conversationId), + ); + + let foundInAnyPage = false; + queryClient + .getQueryCache() + .findAll({ + predicate: (query) => + query.queryKey[0] === 'ws' && + query.queryKey[1] === workspaceId && + query.queryKey[2] === 'conversations' && + query.queryKey[3] === 'list', + }) + .forEach((query) => { + queryClient.setQueryData<InfiniteConversationsData>(query.queryKey, (old) => { + const result = applyConversationMessageCreated(old, event); + if (result.found) foundInAnyPage = true; + return result.data; + }); + }); + + if (!foundInAnyPage) { + debounceInvalidateConversationsList(queryClient, workspaceId); + } + break; + } + + case RealtimeEventType.messageDeleted: + case RealtimeEventType.messageUpdated: + case RealtimeEventType.messageFailed: + case RealtimeEventType.messageIdAssigned: { + queryClient + .getQueryCache() + .findAll({ + predicate: (query) => + query.queryKey[0] === 'ws' && + query.queryKey[1] === workspaceId && + query.queryKey[2] === 'messages', + }) + .forEach((query) => { + queryClient.setQueryData<InfiniteMessagesData>(query.queryKey, (old) => { + switch (event.eventType) { + case RealtimeEventType.messageDeleted: + return applyMessageDeleted(old, event); + case RealtimeEventType.messageUpdated: + return applyMessageUpdated(old, event); + case RealtimeEventType.messageFailed: + return applyMessageFailed(old, event); + case RealtimeEventType.messageIdAssigned: + return applyMessageIdAssigned(old, event); + default: + return old; + } + }); + }); + break; + } + + case RealtimeEventType.typing: { + const { conversationId, typing, seconds } = event.data; + // Read via `getState()` rather than a hook-selected value: this callback is memoized by + // `useCallback` below (deps: queryClient/workspaceId only) and also invoked from the + // typing-timer's own expiry closure, so it needs the CURRENT store action each time it + // runs rather than one captured at a particular render — `getState()` always returns the + // live store, zustand action references are stable, so this is safe outside React. + useChatStore.getState().setTyping(conversationId, typing, seconds); + + if (typing) { + scheduleTypingExpiry(conversationId, seconds || DEFAULT_TYPING_TTL_SECONDS, () => + useChatStore.getState().setTyping(conversationId, false), + ); + } else { + clearTypingTimer(conversationId); + } + break; + } + + case RealtimeEventType.conversationAssigned: + case RealtimeEventType.conversationUpdated: + case RealtimeEventType.contactBlocked: + case RealtimeEventType.contactUnblocked: { + queryClient + .getQueryCache() + .findAll({ + predicate: (query) => + query.queryKey[0] === 'ws' && + query.queryKey[1] === workspaceId && + query.queryKey[2] === 'conversations' && + query.queryKey[3] === 'list', + }) + .forEach((query) => { + queryClient.setQueryData<InfiniteConversationsData>(query.queryKey, (old) => { + switch (event.eventType) { + case RealtimeEventType.conversationAssigned: + return applyConversationAssigned(old, event); + case RealtimeEventType.conversationUpdated: + return applyConversationUpdated(old, event); + case RealtimeEventType.contactBlocked: + return applyContactBlockState(old, event, true); + case RealtimeEventType.contactUnblocked: + return applyContactBlockState(old, event, false); + default: + return old; + } + }); + }); + + if ( + event.eventType === RealtimeEventType.contactBlocked || + event.eventType === RealtimeEventType.contactUnblocked + ) { + const blocked = event.eventType === RealtimeEventType.contactBlocked; + queryClient + .getQueryCache() + .findAll({ + predicate: (query) => + query.queryKey[0] === 'ws' && + query.queryKey[1] === workspaceId && + query.queryKey[2] === 'contacts' && + query.queryKey[3] === 'detail' && + query.queryKey[4] === event.data.contactId, + }) + .forEach((query) => { + queryClient.setQueryData( + query.queryKey, + (old: { blockedAt: string | null } | undefined) => + applyContactDetailBlockState(old, blocked), + ); + }); + } + break; + } + + case RealtimeEventType.conversationCreated: { + // Untyped payload — invalidate rather than splice (see apply-conversation-events.ts). + queryClient.invalidateQueries({ queryKey: ['ws', workspaceId, 'conversations', 'list'] }); + break; + } + + case RealtimeEventType.notifyExportResult: + // No UI surface for export jobs on mobile yet — intentionally ignored. + break; + + default: + break; + } + }, + [queryClient, workspaceId], + ); +} diff --git a/src/stores/use-auth-store.test.ts b/src/stores/use-auth-store.test.ts new file mode 100644 index 0000000..7f143cf --- /dev/null +++ b/src/stores/use-auth-store.test.ts @@ -0,0 +1,38 @@ +import { useAuthStore } from '@/stores/use-auth-store'; + +const testUser = { id: 'u1', email: 'a@b.com', name: 'A B', mustChangePassword: false }; + +beforeEach(() => { + useAuthStore.setState({ status: 'pending', user: null }); +}); + +describe('useAuthStore', () => { + it('starts pending with no user', () => { + expect(useAuthStore.getState().status).toBe('pending'); + expect(useAuthStore.getState().user).toBeNull(); + }); + + it('setSignedIn sets status signed-in and stores the user', () => { + useAuthStore.getState().setSignedIn(testUser); + expect(useAuthStore.getState().status).toBe('signed-in'); + expect(useAuthStore.getState().user).toEqual(testUser); + }); + + it('setSignedOut clears the user and sets status signed-out', () => { + useAuthStore.getState().setSignedIn(testUser); + useAuthStore.getState().setSignedOut(); + expect(useAuthStore.getState().status).toBe('signed-out'); + expect(useAuthStore.getState().user).toBeNull(); + }); + + it('setMustChangePassword updates the flag on an existing user', () => { + useAuthStore.getState().setSignedIn(testUser); + useAuthStore.getState().setMustChangePassword(true); + expect(useAuthStore.getState().user?.mustChangePassword).toBe(true); + }); + + it('setMustChangePassword is a no-op when there is no signed-in user', () => { + useAuthStore.getState().setMustChangePassword(true); + expect(useAuthStore.getState().user).toBeNull(); + }); +}); diff --git a/src/stores/use-auth-store.ts b/src/stores/use-auth-store.ts new file mode 100644 index 0000000..b4ca00c --- /dev/null +++ b/src/stores/use-auth-store.ts @@ -0,0 +1,33 @@ +import { create } from 'zustand'; + +export interface AuthUser { + id: string; + email: string; + name: string | null; + mustChangePassword: boolean; +} + +export type AuthStatus = 'pending' | 'signed-out' | 'signed-in'; + +/** + * Client-state store for the auth session. `status` starts 'pending' so the root layout's + * bootstrap gate can hold the splash screen until `getSession` resolves — see api/auth-endpoints.ts. + * Token storage lives in SecureStore (api/auth-token.ts), not here; this store only holds the + * resolved session shape the UI reads from. + */ +interface AuthStoreState { + status: AuthStatus; + user: AuthUser | null; + setSignedIn: (user: AuthUser) => void; + setSignedOut: () => void; + setMustChangePassword: (mustChangePassword: boolean) => void; +} + +export const useAuthStore = create<AuthStoreState>((set) => ({ + status: 'pending', + user: null, + setSignedIn: (user) => set({ status: 'signed-in', user }), + setSignedOut: () => set({ status: 'signed-out', user: null }), + setMustChangePassword: (mustChangePassword) => + set((state) => (state.user ? { user: { ...state.user, mustChangePassword } } : state)), +})); diff --git a/src/stores/use-settings-store.test.ts b/src/stores/use-settings-store.test.ts new file mode 100644 index 0000000..ea63afd --- /dev/null +++ b/src/stores/use-settings-store.test.ts @@ -0,0 +1,32 @@ +import { useSettingsStore } from '@/stores/use-settings-store'; + +// Hoisted above the import above by babel-jest regardless of source order. +jest.mock('expo-localization', () => ({ + getLocales: () => [{ languageTag: 'en-US' }], +})); + +beforeEach(() => { + useSettingsStore.setState({ themePreference: 'system', language: 'en', pushEnabled: false }); +}); + +describe('useSettingsStore', () => { + it('defaults themePreference to system and pushEnabled to false', () => { + expect(useSettingsStore.getState().themePreference).toBe('system'); + expect(useSettingsStore.getState().pushEnabled).toBe(false); + }); + + it('setThemePreference updates the preference', () => { + useSettingsStore.getState().setThemePreference('dark'); + expect(useSettingsStore.getState().themePreference).toBe('dark'); + }); + + it('setLanguage is a pure setter with no side effects', () => { + useSettingsStore.getState().setLanguage('vi'); + expect(useSettingsStore.getState().language).toBe('vi'); + }); + + it('setPushEnabled updates the flag', () => { + useSettingsStore.getState().setPushEnabled(true); + expect(useSettingsStore.getState().pushEnabled).toBe(true); + }); +}); diff --git a/src/stores/use-settings-store.ts b/src/stores/use-settings-store.ts new file mode 100644 index 0000000..a7425c8 --- /dev/null +++ b/src/stores/use-settings-store.ts @@ -0,0 +1,54 @@ +import AsyncStorage from '@react-native-async-storage/async-storage'; +import * as Localization from 'expo-localization'; +import { create } from 'zustand'; +import { createJSONStorage, persist } from 'zustand/middleware'; + +import { resolveLocale } from '@/i18n/locales'; +import type { SupportedLanguage } from '@/i18n/locales'; + +export type ThemePreference = 'system' | 'light' | 'dark'; + +interface SettingsStoreState { + themePreference: ThemePreference; + language: SupportedLanguage; + /** Persisted user *intent* for push notifications — not the effective permission state, which + * can be revoked in OS Settings independently of this flag. See settings screen for how the two + * combine. */ + pushEnabled: boolean; + setThemePreference: (preference: ThemePreference) => void; + setLanguage: (language: SupportedLanguage) => void; + setPushEnabled: (enabled: boolean) => void; +} + +export const useSettingsStore = create<SettingsStoreState>()( + persist( + (set) => ({ + themePreference: 'system', + language: resolveLocale(Localization.getLocales()[0]?.languageTag), + pushEnabled: false, + setThemePreference: (themePreference) => set({ themePreference }), + // Pure setter — i18next/dayjs/RTL side effects live in `changeAppLanguage` + // (src/i18n/apply-language.ts), which calls this and then applies them. + setLanguage: (language) => set({ language }), + setPushEnabled: (pushEnabled) => set({ pushEnabled }), + }), + { + name: 'settings-store', + storage: createJSONStorage(() => AsyncStorage), + }, + ), +); + +/** Resolves once the persisted settings-store snapshot has been read from AsyncStorage (or + * immediately, if hydration already finished before this was called). */ +export function waitForSettingsHydration(): Promise<void> { + if (useSettingsStore.persist.hasHydrated()) { + return Promise.resolve(); + } + return new Promise((resolve) => { + const unsubscribe = useSettingsStore.persist.onFinishHydration(() => { + unsubscribe(); + resolve(); + }); + }); +} diff --git a/src/stores/use-workspace-store.test.ts b/src/stores/use-workspace-store.test.ts new file mode 100644 index 0000000..f780070 --- /dev/null +++ b/src/stores/use-workspace-store.test.ts @@ -0,0 +1,22 @@ +import { useWorkspaceStore } from '@/stores/use-workspace-store'; + +beforeEach(() => { + useWorkspaceStore.setState({ currentWorkspaceId: null }); +}); + +describe('useWorkspaceStore', () => { + it('starts with no current workspace', () => { + expect(useWorkspaceStore.getState().currentWorkspaceId).toBeNull(); + }); + + it('setCurrentWorkspaceId updates the id', () => { + useWorkspaceStore.getState().setCurrentWorkspaceId('w1'); + expect(useWorkspaceStore.getState().currentWorkspaceId).toBe('w1'); + }); + + it('setCurrentWorkspaceId can clear back to null', () => { + useWorkspaceStore.getState().setCurrentWorkspaceId('w1'); + useWorkspaceStore.getState().setCurrentWorkspaceId(null); + expect(useWorkspaceStore.getState().currentWorkspaceId).toBeNull(); + }); +}); diff --git a/src/stores/use-workspace-store.ts b/src/stores/use-workspace-store.ts new file mode 100644 index 0000000..8393185 --- /dev/null +++ b/src/stores/use-workspace-store.ts @@ -0,0 +1,30 @@ +import AsyncStorage from '@react-native-async-storage/async-storage'; +import { create } from 'zustand'; +import { createJSONStorage, persist } from 'zustand/middleware'; + +/** + * Client-state store for the currently selected workspace. This is deliberately client-only + * state (not server state) — the actual list of workspaces the user belongs to comes from + * TanStack Query (`features/workspaces/api/use-workspaces.ts`). + * + * `currentWorkspaceId` mirrors the bigint-as-string id convention used throughout the ChatbotX + * API (see AGENTS.md invariant #10 in the aha.chat repo). Persisted so the app reopens on the + * last-selected workspace instead of forcing the picker every launch. + */ +interface WorkspaceStoreState { + currentWorkspaceId: string | null; + setCurrentWorkspaceId: (workspaceId: string | null) => void; +} + +export const useWorkspaceStore = create<WorkspaceStoreState>()( + persist( + (set) => ({ + currentWorkspaceId: null, + setCurrentWorkspaceId: (workspaceId) => set({ currentWorkspaceId: workspaceId }), + }), + { + name: 'workspace-store', + storage: createJSONStorage(() => AsyncStorage), + }, + ), +); diff --git a/src/theme/color-utils.test.ts b/src/theme/color-utils.test.ts new file mode 100644 index 0000000..6e895b4 --- /dev/null +++ b/src/theme/color-utils.test.ts @@ -0,0 +1,50 @@ +import { alphaTokens, withAlpha } from '@/theme/color-utils'; + +describe('withAlpha', () => { + it('converts a 6-digit hex color into an rgba string', () => { + expect(withAlpha('#3c6df0', 0.5)).toBe('rgba(60, 109, 240, 0.5)'); + }); + + it('handles pure black and pure white', () => { + expect(withAlpha('#000000', 1)).toBe('rgba(0, 0, 0, 1)'); + expect(withAlpha('#ffffff', 0)).toBe('rgba(255, 255, 255, 0)'); + }); + + it('is case-insensitive on hex digits', () => { + expect(withAlpha('#3C6DF0', 0.5)).toBe(withAlpha('#3c6df0', 0.5)); + }); + + it('clamps alpha above 1 down to 1', () => { + expect(withAlpha('#112233', 2)).toBe('rgba(17, 34, 51, 1)'); + }); + + it('clamps alpha below 0 up to 0', () => { + expect(withAlpha('#112233', -1)).toBe('rgba(17, 34, 51, 0)'); + }); + + it('throws on a 3-digit shorthand hex', () => { + expect(() => withAlpha('#fff', 0.5)).toThrow(); + }); + + it('throws on an 8-digit hex-with-alpha string', () => { + expect(() => withAlpha('#3c6df0ff', 0.5)).toThrow(); + }); + + it('throws on a named color', () => { + expect(() => withAlpha('blue', 0.5)).toThrow(); + }); +}); + +describe('alphaTokens', () => { + it('defines every documented alpha intent', () => { + expect(alphaTokens).toEqual({ + hover: 0.06, + pressed: 0.1, + soft: 0.12, + softDark: 0.18, + disabled: 0.4, + scrim: 0.4, + scrimDark: 0.6, + }); + }); +}); diff --git a/src/theme/color-utils.ts b/src/theme/color-utils.ts new file mode 100644 index 0000000..e7ba956 --- /dev/null +++ b/src/theme/color-utils.ts @@ -0,0 +1,37 @@ +/** + * Color helpers for the token system. `withAlpha` replaces ad-hoc `${hex}1a`-style string + * concatenation with a real alpha channel, so overlay/soft/scrim tokens can be derived from a + * single base hex value instead of hand-picking a second hex per scheme. + */ + +const HEX_PATTERN = /^#([0-9a-fA-F]{6})$/; + +/** Parses a 6-digit `#rrggbb` hex string and returns an `rgba(r, g, b, alpha)` string. Throws on + * anything else (3-digit shorthand, 8-digit hex-with-alpha, named colors) — callers only ever + * feed this our own token hex values, so a loud failure beats silently returning a wrong color. */ +export function withAlpha(hex: string, alpha: number): string { + const match = HEX_PATTERN.exec(hex); + if (!match) { + throw new Error(`withAlpha: expected a 6-digit hex color like "#3c6df0", got "${hex}"`); + } + + const intValue = parseInt(match[1], 16); + const r = (intValue >> 16) & 255; + const g = (intValue >> 8) & 255; + const b = intValue & 255; + const clampedAlpha = Math.min(1, Math.max(0, alpha)); + + return `rgba(${r}, ${g}, ${b}, ${clampedAlpha})`; +} + +/** Shared alpha values for overlay-style states (hover/press/soft fills/scrims), so call sites + * pick a named intent instead of a magic decimal. */ +export const alphaTokens = { + hover: 0.06, + pressed: 0.1, + soft: 0.12, + softDark: 0.18, + disabled: 0.4, + scrim: 0.4, + scrimDark: 0.6, +} as const; diff --git a/src/theme/motion.ts b/src/theme/motion.ts new file mode 100644 index 0000000..424d1ca --- /dev/null +++ b/src/theme/motion.ts @@ -0,0 +1,20 @@ +/** + * Motion tokens re-exported from tokens.ts under friendlier names, plus a `useReducedMotion` + * hook so components can short-circuit springs/entrance animations for users with the OS + * "reduce motion" accessibility setting on, without importing reanimated directly everywhere. + */ +import { useReducedMotion as useReanimatedReducedMotion } from 'react-native-reanimated'; + +import { motionDurations, motionEasings, motionSprings, pressScale } from './tokens'; + +export const durations = motionDurations; +export const easings = motionEasings; +export const springs = motionSprings; +export { pressScale }; + +/** Wraps reanimated's device-level reduced-motion signal. Components should check this before + * kicking off a spring/entrance animation and fall back to an instant state change (or a plain + * opacity fade at `durations.instant`) when it's `true`. */ +export function useReducedMotion(): boolean { + return useReanimatedReducedMotion(); +} diff --git a/src/theme/navigation-theme.ts b/src/theme/navigation-theme.ts new file mode 100644 index 0000000..4983032 --- /dev/null +++ b/src/theme/navigation-theme.ts @@ -0,0 +1,24 @@ +import { DarkTheme, DefaultTheme, type Theme } from 'expo-router'; + +import { colorTokens } from './tokens'; +import type { ResolvedScheme } from './use-theme'; + +/** Feeds our design tokens into react-navigation's ThemeProvider so headers, tab bars, and + * native-stack chrome match the app's palette instead of react-navigation's defaults. */ +export function getNavigationTheme(scheme: ResolvedScheme): Theme { + const base = scheme === 'dark' ? DarkTheme : DefaultTheme; + const colors = colorTokens[scheme]; + + return { + ...base, + colors: { + ...base.colors, + primary: colors.brand, + background: colors.bg, + card: colors.surface1, + text: colors.textPrimary, + border: colors.borderSubtle, + notification: colors.danger, + }, + }; +} diff --git a/src/theme/tokens.test.ts b/src/theme/tokens.test.ts new file mode 100644 index 0000000..49ab67f --- /dev/null +++ b/src/theme/tokens.test.ts @@ -0,0 +1,179 @@ +import { colorTokens, radiusTokens, spacingTokens, typographyTokens } from '@/theme/tokens'; + +const NEW_SEMANTIC_COLOR_KEYS = [ + 'bg', + 'surface0', + 'surface1', + 'surface2', + 'borderSubtle', + 'borderStrong', + 'textPrimary', + 'textSecondary', + 'textTertiary', + 'textInverse', + 'brand', + 'brandStrong', + 'brandSoft', + 'onBrand', + 'bubbleIn', + 'bubbleInText', + 'bubbleOut', + 'bubbleOutText', + 'bubbleBot', + 'bubbleBotText', + 'bubbleBotAccent', + 'success', + 'successSoft', + 'warning', + 'warningSoft', + 'danger', + 'dangerSoft', + 'info', + 'infoSoft', + 'scrim', +] as const; + +// Phase 7 removed `unreadDot`, `bubbleOutbound(Text)`, `bubbleInbound(Text)` once their last call +// sites migrated to the new semantic keys — see tokens.ts's doc comment. The rest still have live +// call sites and stay as compatibility aliases. +const DEPRECATED_ALIAS_KEYS = [ + 'background', + 'surface', + 'surfaceElevated', + 'border', + 'text', + 'primary', + 'primaryForeground', +] as const; + +const CHANNEL_KEYS = [ + 'messenger', + 'instagram', + 'whatsapp', + 'webchat', + 'email', + 'smtp', + 'sms', + 'zalo', + 'telegram', + 'tiktok', + 'omnichannel', +] as const; + +describe('colorTokens', () => { + it.each(['light', 'dark'] as const)('defines every new semantic key for %s', (scheme) => { + for (const key of NEW_SEMANTIC_COLOR_KEYS) { + expect(colorTokens[scheme]).toHaveProperty(key); + expect(typeof colorTokens[scheme][key]).toBe('string'); + } + }); + + it.each(['light', 'dark'] as const)('keeps every deprecated alias for %s', (scheme) => { + for (const key of DEPRECATED_ALIAS_KEYS) { + expect(colorTokens[scheme]).toHaveProperty(key); + expect(typeof colorTokens[scheme][key]).toBe('string'); + } + }); + + it.each(['light', 'dark'] as const)('defines exactly the 11 channel keys for %s', (scheme) => { + expect(Object.keys(colorTokens[scheme].channel).sort()).toEqual([...CHANNEL_KEYS].sort()); + }); + + it('aliases smtp to email and webchat to brand in both schemes', () => { + for (const scheme of ['light', 'dark'] as const) { + expect(colorTokens[scheme].channel.smtp).toBe(colorTokens[scheme].channel.email); + expect(colorTokens[scheme].channel.webchat).toBe(colorTokens[scheme].brand); + } + }); + + it.each(['light', 'dark'] as const)('defines a 6-color avatar palette for %s', (scheme) => { + expect(colorTokens[scheme].avatarPalette).toHaveLength(6); + }); + + it('deprecated aliases point at their new-key equivalents', () => { + for (const scheme of ['light', 'dark'] as const) { + const tokens = colorTokens[scheme]; + expect(tokens.background).toBe(tokens.bg); + expect(tokens.surface).toBe(tokens.surface2); + expect(tokens.surfaceElevated).toBe(tokens.surface1); + expect(tokens.border).toBe(tokens.borderSubtle); + expect(tokens.text).toBe(tokens.textPrimary); + expect(tokens.primary).toBe(tokens.brand); + expect(tokens.primaryForeground).toBe(tokens.onBrand); + } + }); + + it.each(['light', 'dark'] as const)( + 'no longer exposes the fully-migrated aliases removed in Phase 7 for %s', + (scheme) => { + const tokens = colorTokens[scheme] as Record<string, unknown>; + expect(tokens.unreadDot).toBeUndefined(); + expect(tokens.bubbleOutbound).toBeUndefined(); + expect(tokens.bubbleOutboundText).toBeUndefined(); + expect(tokens.bubbleInbound).toBeUndefined(); + expect(tokens.bubbleInboundText).toBeUndefined(); + }, + ); +}); + +describe('spacingTokens', () => { + it('defines the full spacing scale', () => { + expect(spacingTokens).toEqual({ + xxs: 2, + xs: 4, + sm: 8, + ms: 10, + base: 12, + md: 16, + ml: 20, + lg: 24, + xl: 32, + xxl: 48, + }); + }); +}); + +describe('radiusTokens', () => { + it('defines the full radius scale', () => { + expect(radiusTokens).toEqual({ + xs: 6, + sm: 8, + md: 12, + lg: 16, + xl: 20, + bubble: 18, + full: 999, + }); + }); +}); + +describe('typographyTokens', () => { + it('defines every variant with a maxFontSizeMultiplier', () => { + const variants = [ + 'display', + 'heading', + 'title', + 'subtitle', + 'body', + 'bodyStrong', + 'callout', + 'caption', + 'label', + 'micro', + ] as const; + + for (const variant of variants) { + expect(typographyTokens[variant].maxFontSizeMultiplier).toBeGreaterThan(1); + } + }); + + it('sizes display largest and micro smallest', () => { + expect(typographyTokens.display.fontSize).toBeGreaterThan(typographyTokens.heading.fontSize); + expect(typographyTokens.micro.fontSize).toBeLessThan(typographyTokens.caption.fontSize); + }); + + it('marks label as uppercase with letter spacing', () => { + expect(typographyTokens.label.textTransform).toBe('uppercase'); + expect(typographyTokens.label.letterSpacing).toBeGreaterThan(0); + }); +}); diff --git a/src/theme/tokens.ts b/src/theme/tokens.ts new file mode 100644 index 0000000..4328e53 --- /dev/null +++ b/src/theme/tokens.ts @@ -0,0 +1,317 @@ +/** + * Design tokens — single source of truth for color/spacing/radius/elevation/typography/motion. + * See src/theme/use-theme.ts for the hook that resolves these against the active scheme, and + * src/theme/navigation-theme.ts for the react-navigation adapter. + * + * Style direction: "Layered Workbench" — a tinted-neutral canvas with two raised surface levels, + * hairline borders instead of heavy shadows, a strong type hierarchy, and color reserved for + * meaning (channel identity, unread state, bot state, semantic status) rather than decoration. + * + * `colorTokens.light`/`colorTokens.dark` also carry a handful of OLD flat keys (`background`, + * `surface`, `surfaceElevated`, `border`, `text`, `primary`, `primaryForeground`) as DEPRECATED + * ALIASES of the new semantic keys below, kept because Phase 7's audit found ~35 live call sites + * still on the old names — removing them in that pass would have been a risky mass-rename for a + * final-phase sweep. `unreadDot`, `bubbleOutbound(Text)`, `bubbleInbound(Text)` were removed in + * Phase 7 once their last call sites migrated to `brand`/`bubbleOut(Text)`/`bubbleIn(Text)`. New + * code should use the new keys directly; the remaining aliases are still slated for eventual + * removal once every call site migrates. + */ + +import { Easing } from 'react-native'; + +import { withAlpha } from './color-utils'; + +// --------------------------------------------------------------------------- +// Colors +// --------------------------------------------------------------------------- + +const lightBase = { + bg: '#f6f5f2', + surface0: '#efeee9', + surface1: '#ffffff', + surface2: '#fbfaf8', + borderSubtle: '#e5e2da', + borderStrong: '#cfcabd', + textPrimary: '#1c1b17', + textSecondary: '#615c4f', + textTertiary: '#948d7c', + textInverse: '#ffffff', + brand: '#3c6df0', + brandStrong: '#2851c9', + onBrand: '#ffffff', + bubbleIn: '#ffffff', + bubbleInText: '#1c1b17', + bubbleOut: '#3c6df0', + bubbleOutText: '#ffffff', + bubbleBot: '#f1ecff', + bubbleBotText: '#2f1f66', + bubbleBotAccent: '#7c4dff', + success: '#12855a', + warning: '#b4650a', + danger: '#d92d20', + info: '#2464c9', +} as const; + +const darkBase = { + bg: '#131210', + surface0: '#1b1a17', + surface1: '#211f1b', + surface2: '#28261f', + borderSubtle: '#332f27', + borderStrong: '#4a4536', + textPrimary: '#f4f2ec', + textSecondary: '#b9b3a2', + textTertiary: '#847d6b', + textInverse: '#1c1b17', + brand: '#7d9eff', + brandStrong: '#a4bcff', + onBrand: '#0e152b', + bubbleIn: '#28261f', + bubbleInText: '#f4f2ec', + bubbleOut: '#3c5fd9', + bubbleOutText: '#ffffff', + bubbleBot: '#2a2145', + bubbleBotText: '#e4d9ff', + bubbleBotAccent: '#a184ff', + success: '#4fd39a', + warning: '#f2a93c', + danger: '#f97066', + info: '#7db2ff', +} as const; + +/** 11-key channel identity map — every contactInbox `channel` value the backend can send. `smtp` + * aliases `email`'s color (same medium, different transport), `webchat` aliases `brand` (it *is* + * our own product surface, so it borrows the brand color rather than getting an arbitrary one). */ +const lightChannel = { + messenger: '#0866ff', + instagram: '#e1306c', + whatsapp: '#25d366', + webchat: lightBase.brand, + email: '#6b6355', + smtp: '#6b6355', + sms: '#12855a', + zalo: '#0068ff', + telegram: '#26a5e4', + tiktok: '#010101', + omnichannel: '#7c4dff', +} as const; + +const darkChannel = { + messenger: '#4a9eff', + instagram: '#f472a3', + whatsapp: '#4ade80', + webchat: darkBase.brand, + email: '#b9b3a2', + smtp: '#b9b3a2', + sms: '#4fd39a', + zalo: '#5c9dff', + telegram: '#6fc3ef', + tiktok: '#f4f2ec', + omnichannel: '#a184ff', +} as const; + +/** Deterministic pastel fills for avatar-fallback tinting (initials on a color picked by hashing + * the contact id/name — see avatar.tsx in Phase 2) — same 6 hues in both schemes, just retuned + * for contrast against each canvas. */ +const lightAvatarPalette = [ + '#ffd6d1', // coral + '#ffe8b8', // amber + '#d7f0d0', // sage + '#c9ecec', // teal + '#cfe0ff', // periwinkle + '#ead6ff', // lilac +] as const; + +const darkAvatarPalette = [ + '#5c2e29', // coral + '#5c4a1f', // amber + '#2c4a26', // sage + '#204a4a', // teal + '#26355c', // periwinkle + '#43285c', // lilac +] as const; + +type ColorBase = Record<keyof typeof lightBase, string>; +type ChannelMap = Record<keyof typeof lightChannel, string>; + +function buildColorTokens(base: ColorBase, channel: ChannelMap, avatarPalette: readonly string[]) { + return { + ...base, + brandSoft: withAlpha(base.brand, 0.14), + successSoft: withAlpha(base.success, 0.14), + warningSoft: withAlpha(base.warning, 0.14), + dangerSoft: withAlpha(base.danger, 0.14), + infoSoft: withAlpha(base.info, 0.14), + scrim: withAlpha('#000000', 0.5), + channel, + avatarPalette, + + // --- deprecated aliases (Phase 1 compatibility shim) --- + // Phase 7 removed `unreadDot`, `bubbleOutbound(Text)`, `bubbleInbound(Text)` — every call site + // had already migrated to the new semantic keys (`brand`, `bubbleOut(Text)`, `bubbleIn(Text)`). + // The remaining aliases below still have live call sites (35 across ~20 files as of Phase 7's + // audit) — removing them now would be a risky mass-rename for a final-phase pass, so they stay + // until a future cleanup migrates the rest. New code should use the new keys directly. + background: base.bg, + surface: base.surface2, + surfaceElevated: base.surface1, + border: base.borderSubtle, + text: base.textPrimary, + textSecondary: base.textSecondary, + primary: base.brand, + primaryForeground: base.onBrand, + }; +} + +export const colorTokens = { + light: buildColorTokens(lightBase, lightChannel, lightAvatarPalette), + dark: buildColorTokens(darkBase, darkChannel, darkAvatarPalette), +} as const; + +// --------------------------------------------------------------------------- +// Spacing +// --------------------------------------------------------------------------- + +export const spacingTokens = { + xxs: 2, + xs: 4, + sm: 8, + ms: 10, + base: 12, + md: 16, + ml: 20, + lg: 24, + xl: 32, + xxl: 48, +} as const; + +// --------------------------------------------------------------------------- +// Radius +// --------------------------------------------------------------------------- + +/** + * NOTE on the `sm/md/lg/full` rename: the old scale was `sm:4, md:8, lg:16, full:999`. Existing + * callers using `radius.sm + 2` (segmented-tabs pill, badge padding-adjacent math) or bare + * `radius.md`/`radius.sm` (message bubbles, buttons, inputs, search bar) all read reasonably + * with the new, slightly larger values below — nothing goes from a hairline radius to a full + * pill or vice versa. `radius.full` is unchanged. See report for the full grep audit. + */ +export const radiusTokens = { + xs: 6, + sm: 8, + md: 12, + lg: 16, + xl: 20, + bubble: 18, + full: 999, +} as const; + +// --------------------------------------------------------------------------- +// Elevation +// --------------------------------------------------------------------------- + +interface ElevationToken { + shadowColor: string; + shadowOpacity: number; + shadowRadius: number; + shadowOffset: { width: number; height: number }; + elevation: number; +} + +function buildElevationTokens(shadowColor: string, opacityMultiplier: number) { + const levels: readonly [number, number, number][] = [ + [0, 0, 0], // 0 — flush with the canvas, no shadow + [0.06, 3, 1], // 1 — subtle lift (rows, chips) + [0.1, 8, 2], // 2 — floating surfaces (cards, sheets at rest) + [0.16, 16, 4], // 3 — overlays (modals, active sheets) + ]; + + return levels.map(([opacity, shadowRadius, elevation]): ElevationToken => ({ + shadowColor, + shadowOpacity: opacity * opacityMultiplier, + shadowRadius, + shadowOffset: { width: 0, height: elevation > 0 ? Math.ceil(elevation / 2) : 0 }, + elevation, + })) as [ElevationToken, ElevationToken, ElevationToken, ElevationToken]; +} + +export const elevationTokens = { + light: buildElevationTokens('#1c1b17', 1), + // Dark surfaces already sit on a dark canvas, so shadows read as noise unless softened — + // halve the opacity relative to light. + dark: buildElevationTokens('#000000', 0.5), +} as const; + +// --------------------------------------------------------------------------- +// Typography +// --------------------------------------------------------------------------- + +interface TypographyToken { + fontSize: number; + lineHeight: number; + fontWeight: '400' | '500' | '600' | '700'; + maxFontSizeMultiplier: number; + textTransform?: 'uppercase'; + letterSpacing?: number; + fontVariant?: readonly ['tabular-nums']; +} + +/** + * NOTE on `heading`: the old spec was 28/34/700. The new redesign spec sizes `heading` at + * 24/30/700 (one step down, `display` now owns the 32/38/700 slot old `heading` was closest to). + * Any screen currently relying on `heading` for a large page title will render ~4pt smaller — + * intentional per the new hierarchy (display > heading), flagged in the report rather than + * silently left as a surprise. + */ +export const typographyTokens = { + display: { fontSize: 32, lineHeight: 38, fontWeight: '700', maxFontSizeMultiplier: 1.3 }, + heading: { fontSize: 24, lineHeight: 30, fontWeight: '700', maxFontSizeMultiplier: 1.3 }, + title: { fontSize: 20, lineHeight: 26, fontWeight: '600', maxFontSizeMultiplier: 1.4 }, + subtitle: { fontSize: 17, lineHeight: 22, fontWeight: '600', maxFontSizeMultiplier: 1.4 }, + body: { fontSize: 16, lineHeight: 22, fontWeight: '400', maxFontSizeMultiplier: 1.6 }, + bodyStrong: { fontSize: 16, lineHeight: 22, fontWeight: '600', maxFontSizeMultiplier: 1.6 }, + callout: { fontSize: 15, lineHeight: 20, fontWeight: '400', maxFontSizeMultiplier: 1.6 }, + caption: { fontSize: 13, lineHeight: 18, fontWeight: '400', maxFontSizeMultiplier: 1.6 }, + label: { + fontSize: 12, + lineHeight: 16, + fontWeight: '600', + maxFontSizeMultiplier: 1.6, + textTransform: 'uppercase', + letterSpacing: 0.4, + }, + micro: { + fontSize: 11, + lineHeight: 14, + fontWeight: '500', + maxFontSizeMultiplier: 1.6, + fontVariant: ['tabular-nums'], + }, +} as const satisfies Record<string, TypographyToken>; + +// --------------------------------------------------------------------------- +// Motion +// --------------------------------------------------------------------------- + +export const motionDurations = { + instant: 80, + fast: 120, + base: 200, + slow: 320, + enter: 240, + exit: 160, +} as const; + +export const motionEasings = { + standard: Easing.bezier(0.4, 0, 0.2, 1), + decelerate: Easing.bezier(0, 0, 0.2, 1), + accelerate: Easing.bezier(0.4, 0, 1, 1), +} as const; + +export const motionSprings = { + snappy: { damping: 20, stiffness: 300, mass: 0.6 }, + gentle: { damping: 18, stiffness: 160, mass: 0.8 }, + bouncy: { damping: 12, stiffness: 220, mass: 0.7 }, +} as const; + +export const pressScale = 0.97; diff --git a/src/theme/typography.ts b/src/theme/typography.ts new file mode 100644 index 0000000..524a462 --- /dev/null +++ b/src/theme/typography.ts @@ -0,0 +1,52 @@ +/** + * Display-family font resolution. Chrome text (headings, titles, labels — anything using the + * `display`/`heading`/`title`/`subtitle` typography variants at weight 600/700) uses Plus Jakarta + * Sans; body/user-generated content stays on the system font so CJK/Arabic/Hebrew glyphs never + * mix a Latin-only webfont with a system CJK/RTL fallback mid-string. + * + * Fonts must be loaded via `useFonts` from `@expo-google-fonts/plus-jakarta-sans` (see + * src/app/_layout.tsx) before this is used to render — `resolveDisplayFamily` only picks the + * *name*, it doesn't load anything itself. + */ +import { + PlusJakartaSans_600SemiBold, + PlusJakartaSans_700Bold, +} from '@expo-google-fonts/plus-jakarta-sans'; + +/** Language prefixes (BCP-47 primary subtag) whose scripts should never be forced onto a + * Latin-only display face — Arabic, Hebrew, Japanese, Chinese. Returns `undefined` for these so + * callers fall back to the OS system font, which already ships full script coverage. */ +const SYSTEM_FONT_LANGUAGE_PREFIXES = ['ar', 'he', 'ja', 'zh'] as const; + +export type DisplayFontWeight = '600' | '700'; + +const DISPLAY_FONT_FAMILIES: Record<DisplayFontWeight, string> = { + '600': 'PlusJakartaSans_600SemiBold', + '700': 'PlusJakartaSans_700Bold', +}; + +/** Ensures the font-loading side effect above is retained (and the values are the actual + * `useFonts` map keys) — never referenced directly, `useFonts({ PlusJakartaSans_600SemiBold, ... + * })` in _layout.tsx uses the named imports instead. */ +void PlusJakartaSans_600SemiBold; +void PlusJakartaSans_700Bold; + +/** + * Resolves the font-family name to use for display-weight chrome text in the given language. + * Returns `undefined` (meaning: don't override `fontFamily`, let the system font render) for + * languages whose scripts Plus Jakarta Sans doesn't cover well; otherwise returns the loaded + * Plus Jakarta Sans family name for the given weight. + */ +export function resolveDisplayFamily( + language: string, + weight: DisplayFontWeight = '700', +): string | undefined { + const primarySubtag = language.toLowerCase().split('-')[0]; + const isSystemFontLanguage = SYSTEM_FONT_LANGUAGE_PREFIXES.some( + (prefix) => primarySubtag === prefix, + ); + + if (isSystemFontLanguage) return undefined; + + return DISPLAY_FONT_FAMILIES[weight]; +} diff --git a/src/theme/use-theme.ts b/src/theme/use-theme.ts new file mode 100644 index 0000000..c1c04a5 --- /dev/null +++ b/src/theme/use-theme.ts @@ -0,0 +1,83 @@ +import { useColorScheme } from 'react-native'; +import { useMemo } from 'react'; + +import { useSettingsStore } from '@/stores/use-settings-store'; + +import { + colorTokens, + elevationTokens, + motionDurations, + motionEasings, + motionSprings, + pressScale, + radiusTokens, + spacingTokens, + typographyTokens, +} from './tokens'; + +export type ResolvedScheme = 'light' | 'dark'; + +/** Resolves the settings-store theme preference against the OS scheme ('system' ⊕ override). */ +export function useResolvedScheme(): ResolvedScheme { + const themePreference = useSettingsStore((state) => state.themePreference); + const systemScheme = useColorScheme(); + + if (themePreference === 'system') { + return systemScheme === 'dark' ? 'dark' : 'light'; + } + return themePreference; +} + +/** Builds the full resolved theme object for one scheme. Colors/radius/spacing/typography are + * already fully static per scheme in tokens.ts (soft/alpha variants are precomputed there via + * `withAlpha`), so this is just an assembly step — but it's a function (not an inline literal) so + * `THEMES` below can build both variants with one call each. */ +function buildTheme(scheme: ResolvedScheme) { + return { + scheme, + colors: colorTokens[scheme], + spacing: spacingTokens, + radius: radiusTokens, + elevation: elevationTokens[scheme], + typography: typographyTokens, + motion: { + durations: motionDurations, + easings: motionEasings, + springs: motionSprings, + pressScale, + }, + }; +} + +/** Built once at module load — not per render, not per hook call — so `useTheme()` can return a + * stable reference per scheme instead of a fresh object every render (which would break + * `useMemo`/`useCallback` dependency arrays keyed on the theme object). */ +const THEMES = { + light: buildTheme('light'), + dark: buildTheme('dark'), +} as const; + +export type Theme = (typeof THEMES)[ResolvedScheme]; + +/** Resolves the full token set for the active scheme. Use this in components instead of + * importing colorTokens directly, so theme overrides are respected everywhere. Returns the same + * object reference across renders as long as the resolved scheme doesn't change. */ +export function useTheme(): Theme { + const scheme = useResolvedScheme(); + + return THEMES[scheme]; +} + +/** + * Memoizes a derived-styles object per (scheme, factory identity). Correct as long as callers + * either define `factory` at module scope (preferred — it's a pure function of `theme` with no + * closure captures) or wrap it in `useCallback` with its own stable dependencies; an inline + * arrow function recreated every render defeats the memoization (a fresh factory reference each + * render means `useMemo`'s dependency array never matches, so it just recomputes every time — + * functionally correct, just not memoized). + */ +export function useThemedStyles<T>(factory: (theme: Theme) => T): T { + const theme = useTheme(); + + return useMemo(() => factory(theme), [theme, factory]); +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..7ff4f8d --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "expo/tsconfig.base", + "compilerOptions": { + "strict": true, + "types": ["jest"], + "paths": { + "@/*": ["./src/*"], + "@/assets/*": ["./assets/*"] + } + }, + "include": ["**/*.ts", "**/*.tsx", ".expo/types/**/*.ts", "expo-env.d.ts"] +} From ae66de338bdb47bad5a8ca6e9069f16d97dcd79a Mon Sep 17 00:00:00 2001 From: Real Codesiman <realcodesiman@gmail.com> Date: Fri, 28 Aug 2026 08:32:44 +0700 Subject: [PATCH 2/5] =?UTF-8?q?fix(scaffold):=20address=20PR=20#1=20review?= =?UTF-8?q?=20=E2=80=94=20optimistic-send=20ids,=20hydration=20gate,=20con?= =?UTF-8?q?fig,=20dead=20code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Full fix pass from the code review of the initial Expo scaffold: the duplicate-clientId optimistic-send bug, workspace hydration gate, splash/auth bootstrap hardening, message-actions rollback/invalidation, permissions guard, retry/401 handling, multipart error normalization, PII-mask fixes, unified sign-out flow, realtime timer/hoisting/validation bugs, dead code removal (unused deps, hooks, motion tokens, deprecated color aliases), perf memoization fixes, typed router pushes, UI/UX polish (touch targets, contrast, haptics, loading skeletons), and CI/tooling updates. See .claude/PRPs/reviews/pr-1-review.md for the full findings-by-severity breakdown and verification results (typecheck/lint/format/test all green; 293 tests, up from 279). --- .claude/PRPs/reviews/pr-1-review.md | 360 +++++++++++++++ .github/workflows/ci.yml | 8 +- .gitignore | 2 - README.md | 48 +- app.config.ts | 27 +- eas.json | 18 +- package.json | 7 +- pnpm-lock.yaml | 67 ++- scripts/reset-project.js | 114 ----- src/api/client.test.ts | 13 +- src/api/query-keys.ts | 4 - src/app/(app)/(tabs)/_layout.tsx | 4 +- src/app/(app)/(tabs)/contacts/index.tsx | 6 +- src/app/(app)/(tabs)/conversations/index.tsx | 4 +- src/app/(app)/(tabs)/settings/index.tsx | 29 +- src/app/(app)/_layout.tsx | 7 + .../[conversationId]/contact.tsx | 8 + .../conversations/[conversationId]/index.tsx | 53 ++- src/app/(app)/profile.tsx | 34 +- src/app/(app)/settings/members.tsx | 14 +- src/app/(auth)/sign-in.tsx | 37 +- src/app/(dev)/gallery.tsx | 432 +----------------- src/app/+not-found.tsx | 3 +- src/app/_layout.tsx | 64 ++- src/app/camera.tsx | 17 +- src/app/index.tsx | 62 ++- src/components/ui/badge.tsx | 5 +- src/components/ui/button.tsx | 25 +- src/components/ui/connection-banner.tsx | 59 +-- src/components/ui/error-banner.tsx | 24 +- src/components/ui/list-item.tsx | 6 +- src/components/ui/screen.tsx | 9 +- src/components/ui/search-bar.tsx | 4 +- src/components/ui/segmented-tabs.tsx | 8 +- src/components/ui/sheet.tsx | 8 +- src/components/ui/skeleton.tsx | 10 +- src/components/ui/swipeable-row.tsx | 61 +-- src/components/ui/text-field.tsx | 6 +- src/components/ui/text.tsx | 28 +- src/components/ui/toast.tsx | 23 +- src/components/ui/toggle-row.tsx | 2 +- src/dev/gallery-content.tsx | 427 +++++++++++++++++ src/features/auth/social-buttons.tsx | 7 +- src/features/auth/use-sign-out.ts | 38 ++ .../chat/api/send-message-multipart.ts | 38 +- .../chat/api/use-message-actions.test.tsx | 167 +++++++ src/features/chat/api/use-message-actions.ts | 31 +- .../chat/api/use-messages-infinite.ts | 6 +- .../chat/api/use-send-message.test.tsx | 52 ++- src/features/chat/api/use-send-message.ts | 44 +- .../chat/components/attachment-view.tsx | 2 +- src/features/chat/components/chat-header.tsx | 7 +- src/features/chat/components/composer.tsx | 6 +- .../components/conversation-actions-sheet.tsx | 5 +- src/features/chat/lib/pick-attachments.ts | 2 +- src/features/chat/stores/use-chat-store.ts | 17 + src/features/contacts/info-tab.tsx | 2 +- src/features/contacts/pii-mask.test.ts | 16 +- src/features/contacts/pii-mask.ts | 17 +- src/features/contacts/sequences-tab.tsx | 2 +- src/features/contacts/tags-tab.tsx | 2 +- .../api/use-conversation-actions.ts | 82 ++-- .../api/use-conversations-infinite.ts | 8 +- .../components/assignment-sheet.tsx | 22 +- .../components/channel-badge.tsx | 2 +- .../conversations/components/filter-sheet.tsx | 2 +- .../conversations/components/tag-chips.tsx | 6 +- src/features/permissions/use-permissions.ts | 1 + src/features/settings/api/use-device-token.ts | 19 +- .../settings/components/language-sheet.tsx | 2 +- .../workspaces/components/workspace-pill.tsx | 2 +- src/hooks/use-color-scheme.ts | 1 - src/hooks/use-color-scheme.web.ts | 29 -- src/i18n/apply-language.ts | 16 +- src/i18n/dayjs-locales.ts | 66 ++- src/i18n/reconcile-rtl.ts | 8 +- src/lib/notification-tap.ts | 23 +- src/lib/pending-deep-link.test.ts | 80 ++++ src/lib/pending-deep-link.ts | 49 +- src/lib/query-client.ts | 28 +- .../apply-conversation-events.test.ts | 76 +++ src/realtime/apply-conversation-events.ts | 29 +- src/realtime/events.ts | 2 +- src/realtime/parse-event.ts | 4 +- src/realtime/realtime-provider.tsx | 29 +- src/realtime/use-realtime-handlers.ts | 13 +- src/stores/use-workspace-store.ts | 16 + src/theme/motion.ts | 13 +- src/theme/tokens.test.ts | 48 +- src/theme/tokens.ts | 43 +- src/theme/use-theme.ts | 19 - 91 files changed, 2145 insertions(+), 1201 deletions(-) create mode 100644 .claude/PRPs/reviews/pr-1-review.md delete mode 100644 scripts/reset-project.js create mode 100644 src/dev/gallery-content.tsx create mode 100644 src/features/auth/use-sign-out.ts create mode 100644 src/features/chat/api/use-message-actions.test.tsx delete mode 100644 src/hooks/use-color-scheme.ts delete mode 100644 src/hooks/use-color-scheme.web.ts create mode 100644 src/lib/pending-deep-link.test.ts diff --git a/.claude/PRPs/reviews/pr-1-review.md b/.claude/PRPs/reviews/pr-1-review.md new file mode 100644 index 0000000..8b43857 --- /dev/null +++ b/.claude/PRPs/reviews/pr-1-review.md @@ -0,0 +1,360 @@ +# PR #1 review — `chore(init): scaffold ChatbotX mobile app` + +**Decision:** COMMENT (author can't self-approve their own PR). Full fix pass applied on the same +branch, left **unstaged** per review-mode convention — see commit message suggestion at the bottom. + +**Scope reviewed:** 259 files, +57k lines. Initial Expo SDK 57 / expo-router scaffold: auth, +workspace picker, 3 tabs (Conversations/Contacts/Settings), chat with attachments, PartyKit +realtime, push notifications, 20-locale i18n (RTL for ar/he), typed REST client from OpenAPI spec. + +**Overall assessment:** high-quality scaffold — zero `any`, SecureStore for tokens, FlashList v2 +done right, full dark-mode token parity, RTL-aware primitives, 20-locale key parity. Findings +clustered in the optimistic-send lifecycle, store hydration, config/tooling gaps, and template +leftovers — all addressed below. + +--- + +## Findings by severity + +### CRITICAL + +- **`src/features/chat/api/use-send-message.ts:161,199` (A1)** — `mutationFn` and `onMutate` each + called `generateClientId()` independently, so the optimistic bubble and the outgoing request body + carried *different* clientIds — a duplicate bubble until refetch, an orphaned upload-progress + entry, and the retry-scroll signal never firing. **Fix:** normalize `clientId` once in a + `mutate`/`mutateAsync` wrapper before React Query hands params to `onMutate`/`mutationFn`. Added a + regression test asserting request-body clientId === cached optimistic row's clientId. + +- **`src/stores/use-workspace-store.ts` + `src/app/_layout.tsx` (A2)** — no hydration gate on the + workspace store meant a cold start could read `currentWorkspaceId === null` before the persisted + value loaded, bouncing an already-workspace-selected signed-in user to the picker. **Fix:** added + `waitForWorkspaceHydration()` (mirrors `waitForSettingsHydration`), awaited in the auth-bootstrap + effect before splash hides. + +### HIGH + +- **`src/app/_layout.tsx` (A3)** — `preventAutoHideAsync()` had no `.catch`; `useFonts` didn't + destructure `fontError`, so a font-load failure (e.g. offline on first launch) held the splash + screen forever; `useAuthBootstrap` had no try/catch around `getSession`, so a network failure + during session validation looked identical to an invalid token (no distinction between "can't + verify" and "verified invalid"). **Fix:** added `.catch`, `fontsLoaded || fontError` gate, and a + try/catch that treats a validation *failure* as signed-out-for-this-launch without clearing + SecureStore (so the next successful bootstrap can still sign back in with the same token). Also + added `SplashScreen.setOptions({ fade: true, duration: 300 })` and a root `ErrorBoundary` export + (expo-router convention) so an uncaught render error doesn't white-screen the app. + +- **`src/app/(app)/_layout.tsx:12` (A4)** — `status === 'pending'` (bootstrap not yet resolved) fell + into the `!== 'signed-in'` branch and redirected to sign-in, which could bounce a + still-being-verified signed-in user mid-bootstrap. **Fix:** render `null` while pending instead. + +- **`src/features/chat/api/use-message-actions.ts` (A5)** — `useDeleteMessage` had no rollback on + error (a failed delete left the row permanently marked deleted); `useSetMessageAttributes` had no + cache invalidation at all (like/hide never reflected without a manual refetch); no mutation here + or in `use-send-message.ts`/`use-conversation-actions.ts` cancelled in-flight queries before + patching, so a refetch resolving mid-mutation could clobber the optimistic patch. **Fix:** added + snapshot+rollback to delete, `onSettled` invalidation to attributes and edit, and + `await queryClient.cancelQueries(...)` at the top of every optimistic `onMutate` across all three + files (a shared `cancelConversationQueries` helper in `use-conversation-actions.ts` since those + patches span every cached filter combination). Also fixed a latent bug found while doing this: + `useArchiveConversations`/`useUnarchiveConversations` built their patch object + (`{ archivedAt: new Date().toISOString() }`) once at hook-definition time rather than per + mutation, so every archive used a stale timestamp from whenever the component last rendered — + moved to a `buildPatch()` factory invoked inside `onMutate`. Added `use-message-actions.test.tsx` + (didn't exist before) covering delete rollback and attributes invalidation. + +- **`src/features/permissions/use-permissions.ts:60-71` (A6)** — `useWorkspaceMembersList` had no + `enabled` guard, so a `null`/empty workspaceId fired `GET /workspaces//members`, which could + trigger the global 401→sign-out handler. **Fix:** `enabled: workspaceId.length > 0`. Also deleted + a byte-for-byte duplicate `useWorkspaceMembers` in `assignment-sheet.tsx` and repointed it at the + shared hook (same query key, so no extra request in practice — this was pure duplication). + +- **`src/lib/query-client.ts:21-31` (A7)** — the global `retry: 1` retried every error including + 4xx (bad input, unauthorized, not found — retrying identically can't change those outcomes), and + the 401→sign-out handler fired unconditionally, without checking whether the failing request had + actually carried a token (so a 401 raced against bootstrap, or from a request that never had a + token, could force-sign-out a user who was never actually rejected). **Fix:** `retry` is now a + predicate that skips 4xx via `ApiError.body.status`; the 401 handler now checks `getAuthToken()` + before clearing anything. + +- **`src/features/chat/api/send-message-multipart.ts:49,119` (A8)** — the hand-rolled XHR upload + path threw its own `SendMessageError` class, which `normalizeApiError` didn't recognize at all — + a 402 quota rejection on an attachment send silently fell through as "unknown error" instead of + showing the quota banner. **Fix:** replaced `SendMessageError` with `ApiError` built from the + parsed XHR response body (same `{ code, status, message, data }` shape the typed `apiClient` path + produces), so `normalizeApiError` classifies it identically regardless of send path. + +- **`src/features/contacts/pii-mask.ts` (A9)** — `maskPhone` sized its asterisk run off the raw + string length (including formatting characters like `-`/`(`/`)`/spaces) instead of the actual + digit count, so a formatted number like `(555) 123-4567` masked with 4 extra asterisks it didn't + need; `maskEmail` split on the *first* `@`, which breaks for a (rare but legal) quoted local-part + containing `@`, and didn't guard an empty local-part. **Fix:** `maskPhone` sizes off `digits`; + `maskEmail` splits on `lastIndexOf('@')` and guards `atIndex <= 0`. Updated + `pii-mask.test.ts` with the corrected expectations plus new empty-local-part and + quoted-local-part cases. + +- **`src/app/(app)/profile.tsx` + `settings/index.tsx` (A10)** — duplicate sign-out logic (fetch + token, best-effort revoke, clear token, clear query cache, clear workspace, flip auth store); the + profile-modal copy had **no confirmation and wasn't marked destructive**, so a stray tap + immediately signed the user out. **Fix:** extracted `useSignOut()` (`src/features/auth/use-sign-out.ts`) + with the confirm-then-clear-everything flow from settings/index.tsx; both screens now share it. + Folded in the adjacent Phase D ask while touching profile.tsx: removed the "Plan / —" placeholder + section (no plan/billing API exists yet to back it) and the empty trailing `SectionHeader`. + +### MEDIUM + +- **`src/lib/notification-tap.ts:43-50` (A11)** — `getLastNotificationResponseAsync().then(...)` + had no cancellation guard and no `.catch`; a slow cold-start resolution after the listener's + cleanup ran could still call `handleResponse` against a torn-down state, and a rejected promise + was unhandled. **Fix:** `cancelled` flag + `.catch`. + +- **`src/realtime/use-realtime-handlers.ts:34-42` (A12)** — the debounced + conversations-list-invalidate timers are module-scope (`Map<workspaceId, Timeout>`), so nothing + ever cleared them when the realtime connection tore down — a leaked `setTimeout` per + not-yet-cached conversation, and the source of the Jest "worker failed to exit gracefully" + warning noted in the baseline. **Fix:** exported `clearPendingConversationInvalidates()`, called + alongside `clearAllTypingTimers()` in `realtime-provider.tsx`'s unmount effect. Verified: a full + suite run right after this fix showed **no** exit warning. (See "Known issues" below — a + *different*, apparently pre-existing leak resurfaced later, isolated to + `use-send-message.test.tsx`; not chased further, see notes.) + +- **`src/realtime/use-realtime-handlers.ts:88-94` (A13)** — `messageCreated` for the conversation + the user is actively viewing didn't patch `agentLastReadAt`, so the inbox unread badge could drift + stale while the user was reading the conversation live over the socket. **Fix:** + `applyConversationMessageCreated` now takes `activeConversationId` and bumps `agentLastReadAt` + when the incoming message's conversation matches it. Added tests for both the bump and the + not-active case. + +- **`src/realtime/apply-conversation-events.ts:141-144` (A14)** — hoisted the matched conversation + row to the front of *whichever page it was found on*, not just page 0 — for a row on a later + page, this duplicated it across the client's merged view and corrupted cursor pagination. **Fix:** + only hoist when `pageIndex === 0`; a later-page match is patched in place. Added a multi-page + regression test. + +- **`src/realtime/realtime-provider.tsx:83-91` (A15)** — `resetConnectionStore()` ran in a + post-mount `useEffect`, but `usePartySocket` starts connecting immediately on render — a fast + `onOpen` could fire and flip the store to `'open'` *before* the effect's reset ran, clobbering it + back to `'connecting'` and leaving the connection banner stuck. **Fix:** reset moved to run + exactly once per mounted instance via `useState`'s lazy initializer (not a ref read/write during + render — the project's `reactCompiler: true` setting disallows that pattern; confirmed by ESLint + during this review, see "Caught during review" below). + +- **`src/realtime/parse-event.ts:65` (A16)** — `messageFailed`'s validator rejected `error === + undefined` (only `null` or a string passed), so an event that *omitted* the field entirely (valid + per the type, which already allowed optional) failed validation and the whole event was dropped + silently. **Fix:** `isOptionalNullableString`; also widened `RealtimeEventMessageFailed.error` to + `string | null | undefined` in `events.ts` to match. + +- **`app.config.ts:34` (A17)** — `googleServicesFile` was referenced unconditionally, but the file + is gitignored (per-dev/CI secret) — every clean checkout's Android build/prebuild would fail + before the secret is provisioned. **Fix:** conditional on `existsSync`; documented the EAS + file-secret setup in README. + +- **`app.config.ts` + `src/i18n/reconcile-rtl.ts` (A18)** — expo-updates was installed but not + wired up (no `runtimeVersion`/`updates`/plugin entry); the RTL bootstrap called `forceRTL` + *before* checking `__DEV__`, doing an unnecessary native call it then discarded; and the silent + bootstrap reload path could pop a blocking `Alert` if `!Updates.isEnabled`, from a code path with + nothing rendered yet to explain the alert's context to the user. **Fix:** added + `runtimeVersion: { policy: 'appVersion' }`, `updates.url`, and the `'expo-updates'` plugin; + reordered the `__DEV__` check before `forceRTL`; `reloadApp` now takes a `{ silent }` option, used + by the bootstrap path to fall back to silence instead of alerting. + +- **`eas.json` (A19)** — no per-profile `env`, so a preview/production build had no + `API_BASE_URL`/`WS_URL` baked in — worst case, shipping a build that silently points at + `localhost`. **Fix:** added `env` blocks per profile (dev points at local ports; preview/ + production carry placeholder URLs the team should replace with real values before first use). + +### Dead code / template leftovers (no user-facing behavior change) + +- Deleted `scripts/reset-project.js`, its `reset-project` script, and the `example` line in + `.gitignore` (destructive create-expo-app leftover with zero purpose in a real app). +- Deleted `src/hooks/` (`use-color-scheme.ts` + `.web.ts`) — zero importers anywhere. +- Deleted `useThemedStyles` (`use-theme.ts`) — zero callers; deleted `motionEasings`/`motionSprings` + from `tokens.ts` and the corresponding `durations`/`easings`/`springs`/`pressScale` re-exports from + `theme/motion.ts` (kept only `useReducedMotion`, the only thing anything actually imports from + that module) — confirmed zero consumers of `theme.motion.easings`/`.springs` anywhere in the tree. +- Removed the empty `if (context.skippedOptimistic) {}` block in `use-send-message.ts` (folded into + the existing comment); `MAX_FILE_SIZE_BYTES` is now `export const` at its definition instead of a + bottom re-export statement. +- Deleted `ActionContext` (`use-conversation-actions.ts`) — declared, exported, never imported + anywhere. +- Removed the no-op `['device-token']` query invalidation in `use-device-token.ts` — nothing ever + caches under that key; also added a cancellation flag to the push-token-rotation listener + (previously no guard against firing register/unregister calls after unmount). +- Made `MAX_MULTI_SELECT` module-private in `pick-attachments.ts` (only used within its own file). +- Removed the identity `.map` and no-`await` `async` in `composer.tsx`'s `handleCamera`/ + `handleDocument`. +- **Deps removed** (and `pnpm install` re-run to refresh the lockfile): `react-dom`, + `react-native-web`, `test-renderer` (wrong package name — not `react-test-renderer`; nothing + imported it), `cross-env` (nothing referenced it). Removed the `web` config block + + `favicon.png` reference from `app.config.ts`, and the `pnpm web` script. +- **Dev gallery**: moved `(dev)/gallery.tsx`'s ~380-line body (24 UI-primitive imports) to + `src/dev/gallery-content.tsx`, `require()`d lazily inside the route's `__DEV__` branch — those + imports no longer ship in production bundles, while the route itself still resolves so typed + routes stay valid in every build. +- **Deprecated color aliases**: codemodded all ~28 remaining call sites off + `colors.{background,surface,surfaceElevated,border,text,primary,primaryForeground}` onto the + semantic keys (`bg`/`surface2`/`surface1`/`borderSubtle`/`textPrimary`/`brand`/`onBrand`), deleted + the alias block from `tokens.ts` and its doc comment, updated `tokens.test.ts` to assert the + aliases are now `undefined` instead of asserting they exist. +- Consolidated `flattenMessagePages`/`flattenConversationPages` (each a one-line wrapper around + `flattenPages`) — deleted both, call sites now use `flattenPages` directly. +- Removed stale plan-artifact comments ("Phase N", references to a plan/report) opportunistically + in every file this review otherwise touched; a repo-wide sweep of files *not* otherwise touched + was out of scope for this pass — grep `Phase [0-9]\|see report\|see plan` in `src/` for the + remainder if a follow-up wants to finish that sweep. +- `isWorkspaceListQuery`-style consolidation (query-keys.ts helper unifying the + `queryKey[2]==='conversations' && queryKey[3]==='list'` predicate scattered across + `use-conversation-actions.ts`/`use-realtime-handlers.ts`/`find-conversation-in-cache.ts`) was + **not done** in this pass — the individual predicates were left as-is since consolidating them + touches several files for a pure readability win with no behavior change; flagged as a good + follow-up, not blocking. +- Removed `queryKeys.ws.contacts.tags`/`.sequences` — zero callers anywhere. + +### UI/UX polish + +- Touch targets: `error-banner.tsx`'s retry action now has `hitSlop={8}` + + `accessibilityLabel`; `segmented-tabs.tsx` segments now have `minHeight: 44`, and its indicator's + `left: 0` → `start: 0` (was breaking in RTL). +- Contrast: light-mode `textTertiary` `#948d7c` (~3.0:1 against `bg`) → `#767063` (~4.51:1, meets + WCAG AA) — dark mode was already compliant (~4.57:1), left unchanged. +- Haptics: `ListItem`'s default flipped from `'light'` on every navigation tap to `false` — + navigating to a screen doesn't need a haptic beyond the transition itself; destructive rows keep + `'medium'`. Chips/toggles/segmented-tabs already correctly used `'selection'`, unchanged. + `camera.tsx` now fires a `'light'` haptic on capture, `'success'`/`'error'` on + settle, and dims the shutter button with an `ActivityIndicator` while capturing (previously no + visual feedback at all beyond `disabled`). +- `+not-found.tsx`: the "go home" button now uses `EmptyState`'s own `action` prop instead of + floating below it as a separate `Button`. +- `sign-in.tsx`: replaced the `Card`-as-divider hack with a proper rule-with-label row (`Divider` + + centered caption); the logo block now fades in (`FadeInUp`, reduced-motion aware). +- `index.tsx`'s `LoadingScreen`: bare `ActivityIndicator` → branded pulsing logo mark (matches the + sign-in screen's icon treatment), reduced-motion aware. +- Loading fidelity: `contacts/index.tsx` → `SkeletonRow.Contact` (was a flat generic block); + `settings/members.tsx` → `SkeletonRow.Conversation` shape (closer match to its avatar+title+badge + rows); `[conversationId]/contact.tsx`'s outer gate skeleton now matches `ContactPanel`'s own + loading shape exactly, instead of a bare single circle. +- **Not done in this pass:** empty-state descriptions/actions for `conversations.empty` / + `contacts.empty` / `workspaces.empty` (needs new i18n keys across 20 locales + an + `scripts/i18n-sync.ts` run — scoped out given the size of this pass already); the Settings screen + profile-row-as-branded-header-card / theme-picker-as-mini-previews / `tabBarBadge`-matching- + `CountPill` redesign asks (larger visual redesign work, not a bug fix); `settings.tsx` push-toggle + distinct-copy-for-denied-vs-failure; `image-viewer.tsx` accessibility label + pinch-zoom. + +### Performance / correctness (Phase C) + +- `[conversationId]/index.tsx:52-53` — `[...messages].reverse()` was recomputed inline every + render, invalidating `message-list.tsx`'s own `messagesById`/`rows` `useMemo`s on every parent + re-render. **Fix:** `flattenPages` result and the reversed array are both memoized now. +- `justSentMessageId` in the same file was derived from `sendMessage.isSuccess` — which stays + `true` for the mutation's entire lifetime until the next `mutate()` call, so it pointed at a + stale clientId indefinitely between sends. **More severe than the plan's phrasing suggested**: + this `useSendMessage(...)` instance is used *only* for the retry path in this screen — the + composer instantiates its **own separate** `useSendMessage(...)` for normal sends, so + `sendMessage.data` here never reflected a composer send at all; the force-scroll-to-own-message + behavior was effectively broken for every non-retry send. **Fix:** added + `justSentClientIdByConversation` to the shared `useChatStore`, set once in `useSendMessage`'s + `onSuccess` (skipped for flow sends), read by the chat screen regardless of which hook instance + performed the send. +- `handleRetry` only re-sent `text`, silently dropping any attachments or reply context — retrying + a failed attachment/reply send produced a plain text message. **Fix:** rebuilds + `attachments`/`replyTo` from the failed optimistic row (parent `createdAt` recovered from the + already-loaded messages list, since the row itself only carries `parentId`). +- Hoisted per-render color-map/variant-map object literals behind `useMemo` in `text.tsx`, + `button.tsx`, `connection-banner.tsx`, `error-banner.tsx` — each rebuilt a lookup object every + render just to read one entry out of it; `text.tsx`/`button.tsx` in particular render on every + message/list row. +- `toast.tsx:68-94` — the exit-animation's inner `setTimeout(dismissCurrent, ...)` was never + tracked or cleared, only the outer one; if `current` changed mid-exit-animation the inner timer + still fired later against a stale render. **Fix:** both timers tracked, both cleared in cleanup; + also filled in the effect's real dependencies (`reducedMotion`, `motion.durations.*`, + `translateY`, `opacity`) instead of a blanket exhaustive-deps disable — `reducedMotion` in + particular is genuinely not static (OS accessibility toggle). +- `swipeable-row.tsx:119-142` — `trackProgress` mutated a shared value (`progressShared.value`) as + a side effect of a render-phase function (`renderLeftActions`/`renderRightActions`, invoked by + RNGH during render to build the action panels) — exactly the pattern React/reanimated warn + against, even though it "worked." **Fix:** removed `progressShared`/`useAnimatedReaction` + entirely; the reveal haptic now fires from RNGH's own `onSwipeableWillOpen` callback. +- `skeleton.tsx:29-36` now respects `useReducedMotion()` (static opacity instead of a repeating + pulse). +- `screen.tsx` — dropped the redundant inner `flex: 1` `View` (the outer `SafeAreaView` already + provides it; pure passthrough wrapper). +- `dayjs-locales.ts` — was 20 static side-effect imports at module load; now each locale is loaded + lazily inside `setDayjsLocale`. Metro needs a **static, literal** `require()` argument to bundle a + module (a template-literal path can't be resolved at bundle time, unlike webpack's context + modules) — used a per-locale literal-`require()` lookup table rather than one dynamically-built + call. +- `use-conversation-actions.ts:255,266` — `new Date().toISOString()` was called once at + hook-definition time (inside `useArchiveConversations`/`useUnarchiveConversations`'s call to the + shared `useBatchConversationAction`), not per-mutate — every archive optimistically patched with a + timestamp from whenever the component last rendered, not the actual archive time. **Fix:** the + patch is now built by a `buildPatch()` factory invoked inside `onMutate`. +- `use-device-token.ts:78-87` — added a cancellation flag to the push-token-rotation listener (see + "Dead code" section above for the paired no-op-invalidation removal in the same file). +- Replaced all 7 `router.push/replace(... as never)` sites with the typed object form + (`{ pathname, params }`) where the target is a route this app's typed-routes manifest knows + statically; the 2 sites where the target is a genuinely arbitrary runtime string (captured deep + links in `sign-in.tsx`/`social-buttons.tsx`) use expo-router's own `Href` type instead of `never` + — a more accurate cast for "any valid path string," not a workaround. +- Added `export function ErrorBoundary` in `src/app/_layout.tsx` (see A3). +- `pending-deep-link.ts` now accepts both a bare `conversations/:id`/`contacts/:id` deep link and + one that already embeds the `(app)` route-group prefix — previously only the bare form matched. + Added `pending-deep-link.test.ts` (didn't exist before) covering both shapes plus the + unrecognized-path and consume-once cases. + +### Tooling / CI (Phase E) + +- `.github/workflows/ci.yml`: added `pnpm format:check` and `npx expo-doctor` steps; bumped + `node-version` to 22. +- `package.json`: added `"packageManager": "pnpm@11.15.1"` so `pnpm/action-setup@v4` in CI resolves + a pinned version automatically instead of drifting to whatever's latest. +- Fixed the `import/first` warnings in `src/api/client.test.ts` (the two `jest.mock()` calls were + above the imports they mock — Jest hoists `jest.mock` regardless of source position, so this was + purely a lint-order fix, not a behavior change). +- Ran `pnpm format` across the repo (also reformatted two `.claude/commands/*.md` files that were + already out of Prettier's style — incidental, not hand-edited). +- **Not done** (blocked by this repo's own config-protection hook, which exists specifically to + stop agents from silently weakening lint/format rules): `no-console` rule in `eslint.config.js`, + `endOfLine: 'lf'` in `.prettierrc.json`. Both are legitimate, plan-requested additive changes, not + a weakening — but the hook can't distinguish that, and bypassing a guard rail the user + deliberately installed isn't this review's call to make. **Action needed from a human:** apply + these two one-line config changes directly, or explicitly disable the hook for this edit. +- `app.config.ts`: `newArchEnabled`/`edgeToEdgeEnabled` from the original plan **don't exist** as + config properties in Expo SDK 57 — both New Architecture and edge-to-edge are mandatory/default + with no opt-out knob in this SDK version (confirmed against the versioned SDK 57 docs). Left + explanatory comments in place of the properties rather than setting nonexistent config keys. +- **Not done:** `scripts/i18n-sync.ts` marking backfilled English placeholders — real work (a + `*.todo.json` sidecar or CI count check) that's independent of everything else in this pass; + scoped out. + +--- + +## Verification + +| Check | Result | +|---|---| +| `pnpm typecheck` | ✅ clean | +| `pnpm lint` | ✅ 0 errors (8 pre-existing warnings in `jest.setup.ts`/`scripts/i18n-sync.ts`/`.expo/types`, none in touched files) — **1 real error caught and fixed during this review**: `realtime-provider.tsx`'s ref-read-during-render (see A15) violated `reactCompiler: true`'s stricter ref-access rule; fixed via `useState`'s lazy initializer instead of a ref | +| `pnpm format:check` | ✅ clean | +| `pnpm test` | ✅ 41 suites / 293 tests passing (up from 39/279 at baseline — new tests for A1's clientId-parity, A5's delete-rollback/attributes-invalidate, A9's pii-mask cases, A14's page-0-only hoist, pending-deep-link's path matching) | +| `npx expo-doctor` | 2 pre-existing, unrelated failures: 14 packages with patch-version drift from the exact SDK-pinned versions, and a duplicate `expo-file-system` resolution — neither touched by this pass, not silently bumped (native-build-affecting, out of scope for a review-fix pass) | +| `npx expo config --type public` | shows `updates`/`runtimeVersion`, no `web` block | +| `git status` | only unstaged modifications, no staged changes, no unexpected untracked files beyond the 4 new files this pass added | + +**Known issue not resolved:** the Jest "worker failed to exit gracefully" warning is back after a +clean run right after A12's fix showed it gone. Isolated it to `use-send-message.test.tsx` +specifically (running that file alone, including its *pre-existing, untouched* tests, reproduces it +with `--forceExit` needed) — this points at something inherent to that test file's setup (likely a +native-module mock or `jest-expo` environment characteristic) rather than a regression from this +pass's changes, but I could not get a definitive stack trace: `--detectOpenHandles` took several +minutes to return in this sandbox (worker-pool disabled, real Node handle timeouts), well past what +was practical to wait out repeatedly. All 293 tests pass correctly regardless — this only affects +the test *process's* exit, not test correctness. Flagging for a follow-up with a faster local +environment to run `--detectOpenHandles` to completion. + +--- + +## Suggested commit message + +``` +fix(scaffold): address PR #1 review — optimistic-send ids, hydration gate, config, dead code +``` diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3412bf3..e3a8157 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ jobs: - uses: actions/setup-node@v4 with: - node-version: 20 + node-version: 22 cache: pnpm - name: Install dependencies @@ -27,5 +27,11 @@ jobs: - name: Lint run: pnpm lint + - name: Format check + run: pnpm format:check + - name: Test run: pnpm test + + - name: Expo config/dependency doctor + run: npx expo-doctor diff --git a/.gitignore b/.gitignore index 5103350..641dc92 100644 --- a/.gitignore +++ b/.gitignore @@ -39,8 +39,6 @@ yarn-error.* # typescript *.tsbuildinfo -example - # generated native folders /ios /android diff --git a/README.md b/README.md index 575842c..8725d76 100644 --- a/README.md +++ b/README.md @@ -4,10 +4,9 @@ Expo + TypeScript mobile client for [ChatbotX](https://github.com/) — the open chatbot platform whose backend lives in the sibling repo, `../aha.chat`. This app replaces the legacy `chatbotx-mobile` React Native project (untyped JS, old Laravel backend). -Auth, workspace picker, and five tabs — Conversations, Contacts, Flows, Statistics, and Settings — -are implemented, along with chat with attachments, PartyKit realtime, push notifications, 20-locale -i18n (including RTL for `ar`/`he`), and a typed REST client generated from the backend's OpenAPI -spec. +Auth, workspace picker, and three tabs — Conversations, Contacts, and Settings — are implemented, +along with chat with attachments, PartyKit realtime, push notifications, 20-locale i18n (including +RTL for `ar`/`he`), and a typed REST client generated from the backend's OpenAPI spec. > This app talks to the `aha.chat` backend's `bearer()` better-auth plugin and the REST endpoints > under `/api/workspaces/{workspaceId}/...`, plus the corresponding realtime events. Run @@ -33,20 +32,22 @@ pnpm start ``` Then follow the Expo CLI prompts to open the app in a development build, Android emulator, iOS -simulator, Expo Go, or the web. +simulator, or Expo Go. Other useful commands: ```bash -pnpm ios # expo start --ios -pnpm android # expo start --android -pnpm web # expo start --web +pnpm ios # expo run:ios +pnpm android # expo run:android pnpm lint # expo lint (eslint-config-expo) pnpm typecheck # tsc --noEmit pnpm format # prettier --write . pnpm format:check # prettier --check . ``` +There is no web target — this app ships iOS/Android only (see `app.config.ts`; `react-dom` / +`react-native-web` are not dependencies). + ## Configuration The API base URL is env-driven via `app.config.ts` (not a static `app.json`, since we need @@ -60,6 +61,24 @@ cp .env.example .env Defaults to `http://localhost:3123` — the aha.chat builder app's standard local dev URL. +### Android push notifications (`google-services.json`) + +Android FCM V1 push delivery requires a Firebase project with this app's package name +(`com.chatconnectx.mobile`) registered, and its `google-services.json` downloaded to the repo +root — the file is gitignored (per-developer/CI secret, never committed). `app.config.ts` only +references it conditionally (`existsSync` check), so a clean checkout without the file still +builds; Android push registration simply won't work until it's provisioned. For EAS builds, +upload it as a [file-type secret](https://docs.expo.dev/eas/environment-variables/#file-environment-variables) +via `eas env:create` (or the EAS dashboard) referencing the path `./google-services.json`, rather +than committing it. + +### EAS Update + +This app ships with `expo-updates` configured (`app.config.ts`'s `updates`/`runtimeVersion`), so +production/preview builds can receive OTA JS-bundle updates via `eas update` without an app-store +resubmission. `runtimeVersion` uses the `appVersion` policy — a build only receives updates +published against the same `version` in `app.config.ts`. + ## Regenerating the API client ```bash @@ -95,15 +114,20 @@ src/ api/ typed REST client (openapi-fetch), auth token storage, big-number-safe JSON parsing generated/ output of `pnpm generate:api` — do not hand-edit app/ expo-router screens (file-based routing) + components/ui/ design-system primitives (Button, Text, ListItem, Sheet, Skeleton, etc.) config/ runtime env config (API_BASE_URL via expo-constants) - hooks/ shared hooks (e.g. useColorScheme) + dev/ dev-build-only screens' content (e.g. the UI gallery), lazily required so their + imports don't ship in production bundles + features/ feature modules (chat, contacts, conversations, workspaces, settings, ...) — + each owns its own api/ (TanStack Query hooks), components/, and stores/ i18n/ i18next setup, RTL reconciliation, and locale files (20 locales) - lib/ shared library wiring (TanStack Query client, push notifications) + lib/ shared library wiring (TanStack Query client, push notifications, deep links) realtime/ realtime event types mirrored from aha.chat (type-only, manual sync) - stores/ Zustand client-state stores - theme/ design tokens + stores/ app-wide Zustand client-state stores (auth, workspace, settings) + theme/ design tokens, the useTheme() hook, and motion/reduced-motion helpers scripts/ generate-api-client.ts regenerates src/api/generated/schema.ts from the backend's OpenAPI spec + i18n-sync.ts backfills missing translation keys across all 20 locales from en.json ``` ## Related repo diff --git a/app.config.ts b/app.config.ts index 4af8482..574c2c4 100644 --- a/app.config.ts +++ b/app.config.ts @@ -1,3 +1,5 @@ +import { existsSync } from 'node:fs'; + import type { ExpoConfig } from 'expo/config'; /** @@ -8,13 +10,15 @@ import type { ExpoConfig } from 'expo/config'; * (see src/config/env.ts) — process.env is only available here, in the Expo config context. */ const config: ExpoConfig = { - name: 'chatbotx-mobile-app', + name: 'ChatbotX', slug: 'chatbotx-mobile-app', version: '1.0.0', orientation: 'portrait', icon: './assets/images/icon.png', scheme: 'chatbotxmobileapp', userInterfaceStyle: 'automatic', + // New Architecture is mandatory (no opt-out) as of this SDK — no `newArchEnabled` property + // exists in the config schema to set. ios: { icon: './assets/expo.icon', bundleIdentifier: 'com.chatconnectx.mobile', @@ -28,14 +32,14 @@ const config: ExpoConfig = { monochromeImage: './assets/images/android-icon-monochrome.png', }, predictiveBackGestureEnabled: false, + // Edge-to-edge display is mandatory (no opt-out) as of this SDK — no `edgeToEdgeEnabled` knob + // exists in the Android config schema to set. // Required for Android FCM V1 push delivery — prerequisite: a Firebase project with this // package name registered, its google-services.json downloaded to the repo root (gitignored, - // not committed). Builds will fail without this file once this config is active. - googleServicesFile: './google-services.json', - }, - web: { - output: 'static', - favicon: './assets/images/favicon.png', + // not committed). Conditional on the file actually existing locally: it's a per-developer/CI + // secret (see README for the EAS file-secret setup), and an unconditional reference here fails + // every clean checkout's Android build/prebuild before that secret is provisioned. + googleServicesFile: existsSync('./google-services.json') ? './google-services.json' : undefined, }, plugins: [ 'expo-router', @@ -81,11 +85,20 @@ const config: ExpoConfig = { color: '#208AEF', }, ], + 'expo-updates', ], experiments: { typedRoutes: true, reactCompiler: true, }, + // EAS Update: OTA JS-bundle updates without an app-store resubmission. `runtimeVersion` gates + // which updates a given build can receive — 'appVersion' policy means any build sharing the + // same `version` above can receive the same update (no native code change assumed between + // updates to the same version). + runtimeVersion: { policy: 'appVersion' }, + updates: { + url: 'https://u.expo.dev/8cfd58a8-15f8-4f64-b16c-abf26166e80f', + }, extra: { // Falls back to the builder app's default local dev URL. Override via `API_BASE_URL` env var // (e.g. in `.env`, loaded by `expo start` through the standard EXPO_PUBLIC_ / dotenv support, diff --git a/eas.json b/eas.json index 8ab579b..c9b1494 100644 --- a/eas.json +++ b/eas.json @@ -7,15 +7,27 @@ "development": { "developmentClient": true, "distribution": "internal", - "channel": "development" + "channel": "development", + "env": { + "API_BASE_URL": "http://localhost:3123", + "WS_URL": "http://localhost:1999" + } }, "preview": { "distribution": "internal", - "channel": "preview" + "channel": "preview", + "env": { + "API_BASE_URL": "https://preview.example.com", + "WS_URL": "https://preview-realtime.example.com" + } }, "production": { "autoIncrement": true, - "channel": "production" + "channel": "production", + "env": { + "API_BASE_URL": "https://api.example.com", + "WS_URL": "https://realtime.example.com" + } } }, "submit": { diff --git a/package.json b/package.json index 4cbb026..0afd430 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,7 @@ "name": "chatbotx-mobile-app", "main": "expo-router/entry", "version": "1.0.0", + "packageManager": "pnpm@11.15.1", "dependencies": { "@expo-google-fonts/plus-jakarta-sans": "^0.4.2", "@expo/vector-icons": "^15.1.1", @@ -36,14 +37,12 @@ "openapi-fetch": "^0.17.0", "partysocket": "^1.3.0", "react": "19.2.3", - "react-dom": "19.2.3", "react-i18next": "^17.0.11", "react-native": "0.86.2", "react-native-gesture-handler": "~2.32.0", "react-native-reanimated": "4.5.1", "react-native-safe-area-context": "~5.7.0", "react-native-screens": "~4.26.0", - "react-native-web": "~0.21.0", "react-native-worklets": "0.10.1", "zustand": "^5.0.15" }, @@ -51,7 +50,6 @@ "@testing-library/react-native": "^14.0.1", "@types/jest": "^29.5.14", "@types/react": "~19.2.2", - "cross-env": "^10.1.0", "dotenv": "^17.4.2", "eslint": "^9.39.5", "eslint-config-expo": "^57.0.1", @@ -59,16 +57,13 @@ "jest-expo": "^57.0.4", "openapi-typescript": "^7.13.0", "prettier": "^3.9.6", - "test-renderer": "^1.2.0", "tsx": "^4.23.12", "typescript": "~6.0.3" }, "scripts": { "start": "expo start", - "reset-project": "node ./scripts/reset-project.js", "android": "expo run:android", "ios": "expo run:ios", - "web": "expo start --web", "lint": "expo lint", "typecheck": "tsc --noEmit", "format": "prettier --write .", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 22705fe..0b6acd3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -107,9 +107,6 @@ importers: react: specifier: 19.2.3 version: 19.2.3 - react-dom: - specifier: 19.2.3 - version: 19.2.3(react@19.2.3) react-i18next: specifier: ^17.0.11 version: 17.0.11(i18next@26.3.6(typescript@6.0.3))(react-dom@19.2.3(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(typescript@6.0.3) @@ -128,9 +125,6 @@ importers: react-native-screens: specifier: ~4.26.0 version: 4.26.2(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) - react-native-web: - specifier: ~0.21.0 - version: 0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) react-native-worklets: specifier: 0.10.1 version: 0.10.1(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2) @@ -147,9 +141,6 @@ importers: '@types/react': specifier: ~19.2.2 version: 19.2.18 - cross-env: - specifier: ^10.1.0 - version: 10.1.0 dotenv: specifier: ^17.4.2 version: 17.4.2 @@ -171,9 +162,6 @@ importers: prettier: specifier: ^3.9.6 version: 3.9.6 - test-renderer: - specifier: ^1.2.0 - version: 1.2.0(@types/react@19.2.18)(react@19.2.3) tsx: specifier: ^4.23.12 version: 4.23.12 @@ -653,9 +641,6 @@ packages: '@emnapi/wasi-threads@1.2.1': resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} - '@epic-web/invariant@1.0.0': - resolution: {integrity: sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==} - '@esbuild/aix-ppc64@0.28.2': resolution: {integrity: sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==} engines: {node: '>=18'} @@ -1843,10 +1828,12 @@ packages: '@xmldom/xmldom@0.8.14': resolution: {integrity: sha512-T4EDRUBVZYRldYApjEJiU0e1stYWaRAX7CuSnKzrpwdZKo53zGV8/pqfzV6FfwNl9YThD2OumQYvqtvjvgG7aQ==} engines: {node: '>=10.0.0'} + deprecated: this version has critical issues, please update to the latest version '@xmldom/xmldom@0.9.11': resolution: {integrity: sha512-tW8bcK3hsG0/uqSnNz6TK4BkcuZSezoU7DlnYssILmZDktPnSHHuDJJFM0AJv+13gz2r0iGdrj6qqKeUnxXEDg==} engines: {node: '>=14.6'} + deprecated: this version has critical issues, please update to the latest version abab@2.0.6: resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==} @@ -2296,11 +2283,6 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true - cross-env@10.1.0: - resolution: {integrity: sha512-GsYosgnACZTADcmEyJctkJIoqAhHjttw7RsFrVoJNXbsWWqaq6Ym+7kZjq6mS45O0jij6vtiReppKQEtqWy6Dw==} - engines: {node: '>=20'} - hasBin: true - cross-fetch@3.2.0: resolution: {integrity: sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==} @@ -5947,8 +5929,6 @@ snapshots: tslib: 2.8.1 optional: true - '@epic-web/invariant@1.0.0': {} - '@esbuild/aix-ppc64@0.28.2': optional: true @@ -7052,7 +7032,8 @@ snapshots: - supports-color - utf-8-validate - '@react-native/normalize-colors@0.74.89': {} + '@react-native/normalize-colors@0.74.89': + optional: true '@react-native/normalize-colors@0.86.2': {} @@ -7942,16 +7923,12 @@ snapshots: - supports-color - ts-node - cross-env@10.1.0: - dependencies: - '@epic-web/invariant': 1.0.0 - cross-spawn: 7.0.6 - cross-fetch@3.2.0: dependencies: node-fetch: 2.7.0 transitivePeerDependencies: - encoding + optional: true cross-spawn@7.0.6: dependencies: @@ -7962,6 +7939,7 @@ snapshots: css-in-js-utils@3.1.0: dependencies: hyphenate-style-name: 1.1.0 + optional: true cssom@0.3.8: {} @@ -8847,7 +8825,8 @@ snapshots: dependencies: bser: 2.1.1 - fbjs-css-vars@1.0.2: {} + fbjs-css-vars@1.0.2: + optional: true fbjs@3.0.5: dependencies: @@ -8860,6 +8839,7 @@ snapshots: ua-parser-js: 1.0.41 transitivePeerDependencies: - encoding + optional: true fdir@6.5.0(picomatch@4.0.5): optionalDependencies: @@ -9117,7 +9097,8 @@ snapshots: human-signals@2.1.0: {} - hyphenate-style-name@1.1.0: {} + hyphenate-style-name@1.1.0: + optional: true i18next@26.3.6(typescript@6.0.3): optionalDependencies: @@ -9161,6 +9142,7 @@ snapshots: inline-style-prefixer@7.0.1: dependencies: css-in-js-utils: 3.1.0 + optional: true internal-slot@1.1.0: dependencies: @@ -9937,7 +9919,8 @@ snapshots: memoize-one@5.2.1: {} - memoize-one@6.0.0: {} + memoize-one@6.0.0: + optional: true merge-options@3.0.4: dependencies: @@ -10363,6 +10346,7 @@ snapshots: node-fetch@2.7.0: dependencies: whatwg-url: 5.0.0 + optional: true node-forge@1.4.0: {} @@ -10593,7 +10577,8 @@ snapshots: possible-typed-array-names@1.1.0: {} - postcss-value-parser@4.2.0: {} + postcss-value-parser@4.2.0: + optional: true postcss@8.5.26: dependencies: @@ -10625,6 +10610,7 @@ snapshots: promise@7.3.1: dependencies: asap: 2.0.6 + optional: true promise@8.3.0: dependencies: @@ -10758,6 +10744,7 @@ snapshots: styleq: 0.1.3 transitivePeerDependencies: - encoding + optional: true react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2): dependencies: @@ -11049,7 +11036,8 @@ snapshots: es-errors: 1.3.0 es-object-atoms: 1.1.2 - setimmediate@1.0.5: {} + setimmediate@1.0.5: + optional: true setprototypeof@1.2.0: {} @@ -11264,7 +11252,8 @@ snapshots: structured-headers@0.4.1: {} - styleq@0.1.3: {} + styleq@0.1.3: + optional: true supports-color@10.2.2: {} @@ -11341,7 +11330,8 @@ snapshots: universalify: 0.2.0 url-parse: 1.5.10 - tr46@0.0.3: {} + tr46@0.0.3: + optional: true tr46@3.0.0: dependencies: @@ -11419,7 +11409,8 @@ snapshots: ua-parser-js@0.7.41: {} - ua-parser-js@1.0.41: {} + ua-parser-js@1.0.41: + optional: true unbox-primitive@1.1.0: dependencies: @@ -11551,7 +11542,8 @@ snapshots: dependencies: defaults: 1.0.4 - webidl-conversions@3.0.1: {} + webidl-conversions@3.0.1: + optional: true webidl-conversions@7.0.0: {} @@ -11574,6 +11566,7 @@ snapshots: dependencies: tr46: 0.0.3 webidl-conversions: 3.0.1 + optional: true which-boxed-primitive@1.1.1: dependencies: diff --git a/scripts/reset-project.js b/scripts/reset-project.js deleted file mode 100644 index 6a152ea..0000000 --- a/scripts/reset-project.js +++ /dev/null @@ -1,114 +0,0 @@ -#!/usr/bin/env node - -/** - * This script is used to reset the project to a blank state. - * It deletes or moves the /src and /scripts directories to /example based on user input and creates a new /src/app directory with an index.tsx and _layout.tsx file. - * You can remove the `reset-project` script from package.json and safely delete this file after running it. - */ - -const fs = require('fs'); -const path = require('path'); -const readline = require('readline'); - -const root = process.cwd(); -const oldDirs = ['src', 'scripts']; -const exampleDir = 'example'; -const newAppDir = 'src/app'; -const exampleDirPath = path.join(root, exampleDir); - -const indexContent = `import { Text, View, StyleSheet } from "react-native"; - -export default function Index() { - return ( - <View style={styles.container}> - <Text>Edit src/app/index.tsx to edit this screen.</Text> - </View> - ); -} - -const styles = StyleSheet.create({ - container: { - flex: 1, - alignItems: "center", - justifyContent: "center", - }, -}); -`; - -const layoutContent = `import { Stack } from "expo-router"; - -export default function RootLayout() { - return <Stack />; -} -`; - -const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout, -}); - -const moveDirectories = async (userInput) => { - try { - if (userInput === 'y') { - // Create the app-example directory - await fs.promises.mkdir(exampleDirPath, { recursive: true }); - console.log(`📁 /${exampleDir} directory created.`); - } - - // Move old directories to new app-example directory or delete them - for (const dir of oldDirs) { - const oldDirPath = path.join(root, dir); - if (fs.existsSync(oldDirPath)) { - if (userInput === 'y') { - const newDirPath = path.join(root, exampleDir, dir); - await fs.promises.rename(oldDirPath, newDirPath); - console.log(`➡️ /${dir} moved to /${exampleDir}/${dir}.`); - } else { - await fs.promises.rm(oldDirPath, { recursive: true, force: true }); - console.log(`❌ /${dir} deleted.`); - } - } else { - console.log(`➡️ /${dir} does not exist, skipping.`); - } - } - - // Create new /src/app directory - const newAppDirPath = path.join(root, newAppDir); - await fs.promises.mkdir(newAppDirPath, { recursive: true }); - console.log('\n📁 New /src/app directory created.'); - - // Create index.tsx - const indexPath = path.join(newAppDirPath, 'index.tsx'); - await fs.promises.writeFile(indexPath, indexContent); - console.log('📄 src/app/index.tsx created.'); - - // Create _layout.tsx - const layoutPath = path.join(newAppDirPath, '_layout.tsx'); - await fs.promises.writeFile(layoutPath, layoutContent); - console.log('📄 src/app/_layout.tsx created.'); - - console.log('\n✅ Project reset complete. Next steps:'); - console.log( - `1. Run \`npx expo start\` to start a development server.\n2. Edit src/app/index.tsx to edit the main screen.\n3. Put all your application code in /src, only screens and layout files should be in /src/app.${ - userInput === 'y' - ? `\n4. Delete the /${exampleDir} directory when you're done referencing it.` - : '' - }`, - ); - } catch (error) { - console.error(`❌ Error during script execution: ${error.message}`); - } -}; - -rl.question( - 'Do you want to move existing files to /example instead of deleting them? (Y/n): ', - (answer) => { - const userInput = answer.trim().toLowerCase() || 'y'; - if (userInput === 'y' || userInput === 'n') { - moveDirectories(userInput).finally(() => rl.close()); - } else { - console.log("❌ Invalid input. Please enter 'Y' or 'N'."); - rl.close(); - } - }, -); diff --git a/src/api/client.test.ts b/src/api/client.test.ts index 4852998..ffe859d 100644 --- a/src/api/client.test.ts +++ b/src/api/client.test.ts @@ -1,3 +1,11 @@ +import * as SecureStore from 'expo-secure-store'; +import type { Middleware } from 'openapi-fetch'; + +import { apiClient } from '@/api/client'; + +// Jest hoists `jest.mock` calls above these imports at compile time regardless of source +// position, so this ordering (imports first, mocks after) is purely to satisfy `import/first` — +// it does not change which runs first. jest.mock('expo-constants', () => ({ __esModule: true, default: { @@ -8,11 +16,6 @@ jest.mock('expo-constants', () => ({ })); jest.mock('expo-secure-store', () => ({ getItemAsync: jest.fn() })); -import * as SecureStore from 'expo-secure-store'; -import type { Middleware } from 'openapi-fetch'; - -import { apiClient } from '@/api/client'; - /** * Stand-in for React Native's second `Response` realm (see the plan this test accompanies): * duck-types a Response but is never `instanceof` the global `Response` openapi-fetch checks diff --git a/src/api/query-keys.ts b/src/api/query-keys.ts index be04edf..9692eca 100644 --- a/src/api/query-keys.ts +++ b/src/api/query-keys.ts @@ -24,10 +24,6 @@ export const queryKeys = { ['ws', workspaceId, 'contacts', 'list', filters] as const, detail: (workspaceId: string, contactId: string) => ['ws', workspaceId, 'contacts', 'detail', contactId] as const, - tags: (workspaceId: string, contactId: string) => - ['ws', workspaceId, 'contacts', 'tags', contactId] as const, - sequences: (workspaceId: string, contactId: string) => - ['ws', workspaceId, 'contacts', 'sequences', contactId] as const, }, flows: { list: (workspaceId: string) => ['ws', workspaceId, 'flows', 'list'] as const, diff --git a/src/app/(app)/(tabs)/_layout.tsx b/src/app/(app)/(tabs)/_layout.tsx index f3bfc58..b1efe96 100644 --- a/src/app/(app)/(tabs)/_layout.tsx +++ b/src/app/(app)/(tabs)/_layout.tsx @@ -43,9 +43,9 @@ export default function TabsLayout() { <Tabs screenOptions={{ headerShown: false, - tabBarActiveTintColor: colors.primary, + tabBarActiveTintColor: colors.brand, tabBarInactiveTintColor: colors.textSecondary, - tabBarStyle: { backgroundColor: colors.surfaceElevated, borderTopColor: colors.border }, + tabBarStyle: { backgroundColor: colors.surface1, borderTopColor: colors.borderSubtle }, }} > <Tabs.Screen diff --git a/src/app/(app)/(tabs)/contacts/index.tsx b/src/app/(app)/(tabs)/contacts/index.tsx index 94f90ea..ba92abb 100644 --- a/src/app/(app)/(tabs)/contacts/index.tsx +++ b/src/app/(app)/(tabs)/contacts/index.tsx @@ -9,7 +9,7 @@ import { ErrorBanner } from '@/components/ui/error-banner'; import { ListFooterSpinner } from '@/components/ui/list-footer-spinner'; import { Screen } from '@/components/ui/screen'; import { SearchBar } from '@/components/ui/search-bar'; -import { Skeleton } from '@/components/ui/skeleton'; +import { SkeletonRow } from '@/components/ui/skeleton'; import { WorkspaceBlockedGate } from '@/components/workspace-blocked-gate'; import { normalizeApiError } from '@/api/errors'; import { describeApiError } from '@/lib/describe-api-error'; @@ -57,9 +57,9 @@ export default function ContactsScreen() { ) : null} {query.isLoading ? ( - <View style={{ paddingHorizontal: spacing.md, gap: spacing.sm }}> + <View> {Array.from({ length: 8 }).map((_, index) => ( - <Skeleton key={index} height={56} /> + <SkeletonRow.Contact key={index} /> ))} </View> ) : contacts.length === 0 ? ( diff --git a/src/app/(app)/(tabs)/conversations/index.tsx b/src/app/(app)/(tabs)/conversations/index.tsx index dd4194f..0fd14f5 100644 --- a/src/app/(app)/(tabs)/conversations/index.tsx +++ b/src/app/(app)/(tabs)/conversations/index.tsx @@ -6,6 +6,7 @@ import { useTranslation } from 'react-i18next'; import { RefreshControl, StyleSheet, View } from 'react-native'; import { useShallow } from 'zustand/react/shallow'; +import { flattenPages } from '@/api/pagination'; import { ConnectionBanner } from '@/components/ui/connection-banner'; import { EmptyState } from '@/components/ui/empty-state'; import { ErrorBanner } from '@/components/ui/error-banner'; @@ -26,7 +27,6 @@ import { import { FilterChipsRow } from '@/features/conversations/components/filter-chips-row'; import { FilterSheet } from '@/features/conversations/components/filter-sheet'; import { - flattenConversationPages, useConversationsInfinite, type ConversationListItem, } from '@/features/conversations/api/use-conversations-infinite'; @@ -95,7 +95,7 @@ export default function ConversationsScreen() { }, [searchText, setKeyword]); const query = useConversationsInfinite(workspaceId); - const conversations = flattenConversationPages(query.data?.pages); + const conversations = flattenPages(query.data?.pages); const assignMutation = useAssignConversations(workspaceId ?? ''); const markRead = useMarkConversationRead(workspaceId ?? ''); const markUnread = useMarkConversationUnread(workspaceId ?? ''); diff --git a/src/app/(app)/(tabs)/settings/index.tsx b/src/app/(app)/(tabs)/settings/index.tsx index c083bfc..57474fd 100644 --- a/src/app/(app)/(tabs)/settings/index.tsx +++ b/src/app/(app)/(tabs)/settings/index.tsx @@ -4,11 +4,9 @@ import { router } from 'expo-router'; import * as Notifications from 'expo-notifications'; import { useRef } from 'react'; import { useTranslation } from 'react-i18next'; -import { Alert, Linking, ScrollView, View } from 'react-native'; +import { Linking, ScrollView, View } from 'react-native'; import Animated, { FadeIn } from 'react-native-reanimated'; -import { signOut as signOutRequest } from '@/api/auth-endpoints'; -import { clearAuthToken, getAuthToken } from '@/api/auth-token'; import { Avatar } from '@/components/ui/avatar'; import { Card } from '@/components/ui/surface'; import { Divider } from '@/components/ui/divider'; @@ -18,6 +16,7 @@ import { SectionHeader } from '@/components/ui/section-header'; import { SegmentedTabs } from '@/components/ui/segmented-tabs'; import { Text } from '@/components/ui/text'; import { ToggleRow } from '@/components/ui/toggle-row'; +import { useSignOut } from '@/features/auth/use-sign-out'; import { useRegisterDeviceToken, useUnregisterDeviceToken, @@ -26,7 +25,6 @@ import { LanguageSheet } from '@/features/settings/components/language-sheet'; import { WorkspaceSwitcherSheet } from '@/features/workspaces/components/workspace-switcher-sheet'; import { useWorkspaces } from '@/features/workspaces/api/use-workspaces'; import { localeMeta } from '@/i18n/locales'; -import { queryClient } from '@/lib/query-client'; import { useReducedMotion } from '@/theme/motion'; import { useResolvedScheme, useTheme } from '@/theme/use-theme'; import { useAuthStore } from '@/stores/use-auth-store'; @@ -43,7 +41,7 @@ export default function SettingsScreen() { const reducedMotion = useReducedMotion(); const user = useAuthStore((state) => state.user); - const setSignedOut = useAuthStore((state) => state.setSignedOut); + const signOut = useSignOut(); const themePreference = useSettingsStore((state) => state.themePreference); const setThemePreference = useSettingsStore((state) => state.setThemePreference); @@ -53,7 +51,6 @@ export default function SettingsScreen() { const workspaceSwitcherSheetRef = useRef<BottomSheet>(null); const currentWorkspaceId = useWorkspaceStore((state) => state.currentWorkspaceId); - const setCurrentWorkspaceId = useWorkspaceStore((state) => state.setCurrentWorkspaceId); const { data: workspaces } = useWorkspaces(); const currentWorkspace = workspaces?.find((workspace) => workspace.id === currentWorkspaceId); @@ -86,24 +83,6 @@ export default function SettingsScreen() { }); } - async function performSignOut() { - const token = await getAuthToken(); - if (token) { - await signOutRequest(token).catch(() => {}); - } - await clearAuthToken(); - queryClient.clear(); - setCurrentWorkspaceId(null); - setSignedOut(); - } - - function handleSignOut() { - Alert.alert(t('auth.signOutConfirmTitle'), t('auth.signOutConfirmBody'), [ - { text: t('common.cancel'), style: 'cancel' }, - { text: t('auth.signOut'), style: 'destructive', onPress: () => void performSignOut() }, - ]); - } - return ( <Screen edges={['top']}> <Animated.View @@ -190,7 +169,7 @@ export default function SettingsScreen() { </View> <Card> - <ListItem title={t('auth.signOut')} onPress={handleSignOut} destructive /> + <ListItem title={t('auth.signOut')} onPress={signOut} destructive /> </Card> {appVersion ? ( diff --git a/src/app/(app)/_layout.tsx b/src/app/(app)/_layout.tsx index 0b9eb35..59ff9d4 100644 --- a/src/app/(app)/_layout.tsx +++ b/src/app/(app)/_layout.tsx @@ -9,6 +9,13 @@ export default function AppLayout() { const { t } = useTranslation(); const status = useAuthStore((state) => state.status); + // 'pending' means the root bootstrap gate hasn't resolved the session yet — the splash screen + // is normally still up at that point, but render nothing rather than redirecting, so a re-mount + // of this layout mid-bootstrap never bounces a still-being-verified signed-in user to sign-in. + if (status === 'pending') { + return null; + } + if (status !== 'signed-in') { return <Redirect href="/(auth)/sign-in" />; } diff --git a/src/app/(app)/conversations/[conversationId]/contact.tsx b/src/app/(app)/conversations/[conversationId]/contact.tsx index d5a110b..857d623 100644 --- a/src/app/(app)/conversations/[conversationId]/contact.tsx +++ b/src/app/(app)/conversations/[conversationId]/contact.tsx @@ -1,25 +1,33 @@ import { useLocalSearchParams } from 'expo-router'; +import { View } from 'react-native'; import { Screen } from '@/components/ui/screen'; import { Skeleton } from '@/components/ui/skeleton'; import { useConversationDetail } from '@/features/conversations/api/use-conversation-detail'; import { ContactPanel } from '@/features/contacts/contact-panel'; import { useWorkspaceStore } from '@/stores/use-workspace-store'; +import { useTheme } from '@/theme/use-theme'; /** Replaces the Phase 4 minimal placeholder. Keyed by conversationId, so resolves contactId via * the conversation-detail fetch before handing off to the shared ContactPanel. */ export default function ConversationContactScreen() { const { conversationId } = useLocalSearchParams<{ conversationId: string }>(); const workspaceId = useWorkspaceStore((state) => state.currentWorkspaceId); + const { spacing } = useTheme(); const { data: conversation, isLoading } = useConversationDetail( workspaceId, conversationId ?? null, ); if (isLoading || !conversation) { + // Matches ContactPanel's own loading shape (see contact-panel.tsx) — this gate sits in front + // of it, resolving `contactId` via the conversation-detail fetch, so the two loading states + // should look identical rather than swap shapes mid-load. return ( <Screen padded> <Skeleton height={72} borderRadius={36} style={{ alignSelf: 'center' }} /> + <View style={{ height: spacing.md }} /> + <Skeleton height={16} /> </Screen> ); } diff --git a/src/app/(app)/conversations/[conversationId]/index.tsx b/src/app/(app)/conversations/[conversationId]/index.tsx index 3ffe735..6baa310 100644 --- a/src/app/(app)/conversations/[conversationId]/index.tsx +++ b/src/app/(app)/conversations/[conversationId]/index.tsx @@ -1,12 +1,13 @@ import { useQueryClient } from '@tanstack/react-query'; import { useFocusEffect, useLocalSearchParams, useRouter } from 'expo-router'; -import { useCallback, useEffect, useRef, useState } from 'react'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import type BottomSheet from '@gorhom/bottom-sheet'; import type { LayoutChangeEvent } from 'react-native'; import { KeyboardAvoidingView, Platform, Keyboard } from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; +import { flattenPages } from '@/api/pagination'; import { ConnectionBanner } from '@/components/ui/connection-banner'; import { ErrorBanner } from '@/components/ui/error-banner'; import { Screen } from '@/components/ui/screen'; @@ -20,11 +21,7 @@ import { ChatHeader } from '@/features/chat/components/chat-header'; import { Composer } from '@/features/chat/components/composer'; import { MessageActionsMenu } from '@/features/chat/components/message-actions-menu'; import { MessageList, type MessageListHandle } from '@/features/chat/components/message-list'; -import { - flattenMessagePages, - useMessagesInfinite, - type Message, -} from '@/features/chat/api/use-messages-infinite'; +import { useMessagesInfinite, type Message } from '@/features/chat/api/use-messages-infinite'; import { useDeleteMessage, useSetMessageAttributes } from '@/features/chat/api/use-message-actions'; import { removeOptimisticMessage, useSendMessage } from '@/features/chat/api/use-send-message'; import { useChatStore } from '@/features/chat/stores/use-chat-store'; @@ -49,16 +46,23 @@ export default function ChatScreen() { const messageListRef = useRef<MessageListHandle>(null); const messagesQuery = useMessagesInfinite(workspaceId, conversationId ?? null); - const messages = flattenMessagePages(messagesQuery.data?.pages); - const chronologicalMessages = [...messages].reverse(); + const messages = useMemo(() => flattenPages(messagesQuery.data?.pages), [messagesQuery.data]); + // `messages` is newest-first (see use-messages-infinite.ts); MessageList wants chronological + // order. Memoized on `messages` itself (not recomputed inline) so downstream `messagesById`/ + // `rows` memos that depend on this array's identity don't invalidate on every render. + const chronologicalMessages = useMemo(() => [...messages].reverse(), [messages]); const markRead = useMarkConversationRead(workspaceId ?? ''); const sendMessage = useSendMessage(workspaceId ?? '', conversationId ?? ''); - // The mutation's own resolved value carries the clientId it generated internally (see - // use-send-message.ts's mutationFn return) — reading it here (rather than threading a second - // piece of state) is enough for MessageList to know "the last message that just landed was MY - // own send" and force-scroll to it even if the user had scrolled away. - const justSentMessageId = sendMessage.isSuccess ? (sendMessage.data?.clientId ?? null) : null; + // Read from the shared chat store (set by `useSendMessage`'s `onSuccess`) rather than this + // screen's own `sendMessage.data` — that mutation instance is used ONLY for the retry path + // here; the composer instantiates its own separate `useSendMessage(...)` for normal sends, so + // `sendMessage.data` never reflected a composer send at all. The store is the one place both + // instances can report to. + const justSentMessageId = + useChatStore((state) => + conversationId ? state.justSentClientIdByConversation[conversationId] : undefined, + ) ?? null; const deleteMessage = useDeleteMessage(workspaceId ?? '', conversationId ?? ''); const setAttributes = useSetMessageAttributes(workspaceId ?? '', conversationId ?? ''); const setComposerMode = useChatStore((state) => state.setComposerMode); @@ -108,10 +112,33 @@ export default function ChatScreen() { function handleRetry(message: Message) { if (!message.clientId) return; + + // Rebuild the ORIGINAL send params from the failed optimistic row — previously this only + // re-sent `text`, silently dropping any attachments or reply context, so retrying a failed + // attachment/reply send produced a plain text message instead of the thing the user actually + // tried to send. + const attachments = + message.attachments.length > 0 + ? message.attachments.map((attachment) => ({ + uri: attachment.originPath, + mimeType: attachment.mimeType, + fileName: attachment.name ?? attachment.originPath.split('/').pop() ?? 'attachment', + })) + : undefined; + + // `parentId` only carries the parent's id, not its `createdAt` (which `replyTo` needs) — + // recover it from the already-loaded messages list rather than a fresh cache lookup. + const parent = message.parentId + ? messages.find((candidate) => candidate.id === message.parentId) + : undefined; + const replyTo = parent ? { messageId: parent.id, createdAt: parent.createdAt } : undefined; + sendMessage.mutate({ workspaceId: workspaceId ?? '', conversationId: conversationId ?? '', text: message.text ?? undefined, + attachments, + replyTo, clientId: message.clientId, }); } diff --git a/src/app/(app)/profile.tsx b/src/app/(app)/profile.tsx index fa81a61..b80fed0 100644 --- a/src/app/(app)/profile.tsx +++ b/src/app/(app)/profile.tsx @@ -1,38 +1,22 @@ -import { useTranslation } from 'react-i18next'; import { View } from 'react-native'; -import { signOut as signOutRequest } from '@/api/auth-endpoints'; -import { clearAuthToken, getAuthToken } from '@/api/auth-token'; import { Avatar } from '@/components/ui/avatar'; import { ListItem } from '@/components/ui/list-item'; import { Screen } from '@/components/ui/screen'; -import { SectionHeader } from '@/components/ui/section-header'; import { Text } from '@/components/ui/text'; -import { queryClient } from '@/lib/query-client'; +import { useSignOut } from '@/features/auth/use-sign-out'; import { useAuthStore } from '@/stores/use-auth-store'; -import { useWorkspaceStore } from '@/stores/use-workspace-store'; import { useTheme } from '@/theme/use-theme'; +import { useTranslation } from 'react-i18next'; -/** Profile modal: identity + plan placeholder + sign-out. Dark-mode/language live on the settings - * tab (this modal doesn't duplicate them) since there's no plan/billing API yet to justify a - * separate "account" surface beyond identity. */ +/** Profile modal: identity + sign-out. Dark-mode/language live on the settings tab (this modal + * doesn't duplicate them) since there's no plan/billing API yet to justify a separate "account" + * surface beyond identity. */ export default function ProfileScreen() { const { t } = useTranslation(); const { spacing } = useTheme(); const user = useAuthStore((state) => state.user); - const setSignedOut = useAuthStore((state) => state.setSignedOut); - const setCurrentWorkspaceId = useWorkspaceStore((state) => state.setCurrentWorkspaceId); - - async function handleSignOut() { - const token = await getAuthToken(); - if (token) { - await signOutRequest(token).catch(() => {}); - } - await clearAuthToken(); - queryClient.clear(); - setCurrentWorkspaceId(null); - setSignedOut(); - } + const signOut = useSignOut(); return ( <Screen padded> @@ -46,11 +30,7 @@ export default function ProfileScreen() { ) : null} </View> - <SectionHeader title={t('profile.plan')} /> - <ListItem title={t('profile.plan')} subtitle="—" /> - - <SectionHeader title="" /> - <ListItem title={t('auth.signOut')} onPress={handleSignOut} /> + <ListItem title={t('auth.signOut')} onPress={signOut} destructive /> </Screen> ); } diff --git a/src/app/(app)/settings/members.tsx b/src/app/(app)/settings/members.tsx index 3fbed57..ec20098 100644 --- a/src/app/(app)/settings/members.tsx +++ b/src/app/(app)/settings/members.tsx @@ -9,7 +9,7 @@ import { ErrorBanner } from '@/components/ui/error-banner'; import { ListItem } from '@/components/ui/list-item'; import { Screen } from '@/components/ui/screen'; import { SectionHeader } from '@/components/ui/section-header'; -import { Skeleton } from '@/components/ui/skeleton'; +import { SkeletonRow } from '@/components/ui/skeleton'; import { useTeamsList } from '@/features/settings/api/use-teams'; import { useWorkspaceMembersList } from '@/features/permissions/use-permissions'; import { useWorkspaceStore } from '@/stores/use-workspace-store'; @@ -35,10 +35,10 @@ export default function MembersScreen() { <ScrollView> <SectionHeader title={t('settings.members')} /> {membersQuery.isPending ? ( - <View style={{ paddingHorizontal: spacing.md, gap: spacing.sm }}> - <Skeleton height={56} /> - <Skeleton height={56} /> - <Skeleton height={56} /> + <View> + <SkeletonRow.Conversation /> + <SkeletonRow.Conversation /> + <SkeletonRow.Conversation /> </View> ) : membersQuery.isError ? ( <View style={{ paddingHorizontal: spacing.md }}> @@ -69,8 +69,8 @@ export default function MembersScreen() { <> <SectionHeader title={t('settings.teams')} /> {teamsQuery.isPending ? ( - <View style={{ paddingHorizontal: spacing.md, gap: spacing.sm }}> - <Skeleton height={56} /> + <View> + <SkeletonRow.Conversation /> </View> ) : teams.length === 0 ? ( <EmptyState icon="people-circle-outline" title={t('settings.noTeams')} /> diff --git a/src/app/(auth)/sign-in.tsx b/src/app/(auth)/sign-in.tsx index 6c309f2..81978c7 100644 --- a/src/app/(auth)/sign-in.tsx +++ b/src/app/(auth)/sign-in.tsx @@ -1,20 +1,22 @@ -import { Link, router } from 'expo-router'; +import { Link, router, type Href } from 'expo-router'; import { useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import type { TextInput } from 'react-native'; import { KeyboardAvoidingView, Platform, ScrollView, StyleSheet, View } from 'react-native'; +import Animated, { FadeInUp } from 'react-native-reanimated'; import { signInWithEmail } from '@/api/auth-endpoints'; import { Button } from '@/components/ui/button'; +import { Divider } from '@/components/ui/divider'; import { ErrorBanner } from '@/components/ui/error-banner'; import { Icon } from '@/components/ui/icon'; import { Screen } from '@/components/ui/screen'; -import { Card } from '@/components/ui/surface'; import { Text } from '@/components/ui/text'; import { TextField } from '@/components/ui/text-field'; import { SocialButtons } from '@/features/auth/social-buttons'; import { consumePendingDeepLink } from '@/lib/pending-deep-link'; import { useAuthStore } from '@/stores/use-auth-store'; +import { useReducedMotion } from '@/theme/motion'; import { useTheme } from '@/theme/use-theme'; const LOGO_SIZE = 72; @@ -22,6 +24,7 @@ const LOGO_SIZE = 72; export default function SignInScreen() { const { t } = useTranslation(); const { colors, spacing } = useTheme(); + const reducedMotion = useReducedMotion(); const setSignedIn = useAuthStore((state) => state.setSignedIn); const [email, setEmail] = useState(''); @@ -43,7 +46,10 @@ export default function SignInScreen() { // default post-auth redirect — see lib/pending-deep-link.ts. A mustChangePassword user is // still routed to '/' regardless (its own guard traps them in change-password first). const pendingDeepLink = user.mustChangePassword ? null : consumePendingDeepLink(); - router.replace((pendingDeepLink ?? '/') as never); + // A captured deep link is an arbitrary runtime string, not a route this app's typed-routes + // manifest can know statically — `Href` (rather than `as never`) is expo-router's own type + // for exactly this "any valid path string" case. + router.replace((pendingDeepLink ?? '/') as Href); } catch (err) { setError(err instanceof Error ? err.message : t('errors.unknown')); } finally { @@ -64,7 +70,10 @@ export default function SignInScreen() { keyboardShouldPersistTaps="handled" > <View style={[styles.form, { gap: spacing.md }]}> - <View style={[styles.logoWrap, { marginBottom: spacing.sm }]}> + <Animated.View + entering={reducedMotion ? undefined : FadeInUp.duration(400)} + style={[styles.logoWrap, { marginBottom: spacing.sm }]} + > <View style={[ styles.logoCircle, @@ -73,7 +82,7 @@ export default function SignInScreen() { > <Icon name="chatbubbles" size={32} color={colors.brand} /> </View> - </View> + </Animated.View> <Text variant="display" style={styles.centered}> {t('auth.signIn')} @@ -130,11 +139,17 @@ export default function SignInScreen() { </Text> </Link> - <Card style={[styles.divider, { padding: spacing.sm }]}> - <Text variant="caption" color="secondary" style={styles.centered}> + <View style={[styles.dividerRow, { gap: spacing.sm }]}> + <View style={styles.dividerLine}> + <Divider /> + </View> + <Text variant="caption" color="secondary"> {t('auth.orContinueWith')} </Text> - </Card> + <View style={styles.dividerLine}> + <Divider /> + </View> + </View> <SocialButtons /> </View> </ScrollView> @@ -166,9 +181,13 @@ const styles = StyleSheet.create({ link: { alignSelf: 'center', }, - divider: { + dividerRow: { + flexDirection: 'row', alignItems: 'center', }, + dividerLine: { + flex: 1, + }, centered: { textAlign: 'center', }, diff --git a/src/app/(dev)/gallery.tsx b/src/app/(dev)/gallery.tsx index 92fd391..eb4f7cf 100644 --- a/src/app/(dev)/gallery.tsx +++ b/src/app/(dev)/gallery.tsx @@ -1,435 +1,19 @@ -import type BottomSheet from '@gorhom/bottom-sheet'; import { Redirect } from 'expo-router'; -import type { ReactNode } from 'react'; -import { useRef, useState } from 'react'; -import { ScrollView, StyleSheet, View } from 'react-native'; - -import { Avatar } from '@/components/ui/avatar'; -import { Badge } from '@/components/ui/badge'; -import { Button } from '@/components/ui/button'; -import { Card, Surface } from '@/components/ui/surface'; -import { Chip } from '@/components/ui/chip'; -import { ConnectionBanner, type ConnectionState } from '@/components/ui/connection-banner'; -import { CountPill } from '@/components/ui/count-pill'; -import { DateSeparator } from '@/components/ui/date-separator'; -import { Divider } from '@/components/ui/divider'; -import { EmptyState } from '@/components/ui/empty-state'; -import { ErrorBanner } from '@/components/ui/error-banner'; -import { IconButton } from '@/components/ui/icon-button'; -import { ListFooterSpinner } from '@/components/ui/list-footer-spinner'; -import { ListItem } from '@/components/ui/list-item'; -import { Screen } from '@/components/ui/screen'; -import { ScreenHeader } from '@/components/ui/screen-header'; -import { SearchBar } from '@/components/ui/search-bar'; -import { SegmentedTabs } from '@/components/ui/segmented-tabs'; -import { Sheet, SheetHeader } from '@/components/ui/sheet'; -import { Skeleton, SkeletonRow } from '@/components/ui/skeleton'; -import { Text } from '@/components/ui/text'; -import { ToggleRow } from '@/components/ui/toggle-row'; -import { useToast } from '@/components/ui/toast'; -import { useSettingsStore } from '@/stores/use-settings-store'; -import { useTheme } from '@/theme/use-theme'; /** - * Visual smoke-test surface for every Phase 2 UI primitive, in various states, with a light/dark - * toggle. Not a polished screen — the "storybook-lite" the plan asks for. Dev-build only: renders - * a redirect in production builds instead of registering a no-op empty screen, so the route - * still resolves (typed-routes stays happy) but is inert outside `__DEV__`. + * Dev-build-only "storybook-lite" route — see `src/dev/gallery-content.tsx` for the actual + * screen. That module is `require()`d lazily, inside the `__DEV__` branch, rather than imported + * statically at the top of this file: its 24 UI-primitive imports would otherwise ship in every + * production bundle even though the screen itself redirects away immediately outside `__DEV__`. + * The route file itself stays registered unconditionally so expo-router's typed routes keep + * resolving `/gallery` in every build. */ export default function GalleryScreen() { if (!__DEV__) { return <Redirect href="/(app)/(tabs)/conversations" />; } + // eslint-disable-next-line @typescript-eslint/no-require-imports -- intentionally lazy, see above + const { GalleryContent } = require('@/dev/gallery-content'); return <GalleryContent />; } - -function GalleryContent() { - const { colors, spacing } = useTheme(); - const themePreference = useSettingsStore((state) => state.themePreference); - const setThemePreference = useSettingsStore((state) => state.setThemePreference); - const showToast = useToast(); - - const [search, setSearch] = useState(''); - const [tab, setTab] = useState<'all' | 'unread' | 'bot'>('all'); - const [chipSelected, setChipSelected] = useState(true); - const [toggleValue, setToggleValue] = useState(true); - const [connectionState, setConnectionState] = useState<ConnectionState>('offline'); - const sheetRef = useRef<BottomSheet>(null); - // Lazy-init so `Date.now()` runs once (at first render) instead of being called fresh on every - // render, which the react-compiler lint rule flags as an impure render-time call. - const [{ todayIso, yesterdayIso }] = useState(() => { - const now = Date.now(); - return { - todayIso: new Date(now).toISOString(), - yesterdayIso: new Date(now - 86400000).toISOString(), - }; - }); - - function toggleScheme() { - setThemePreference(themePreference === 'dark' ? 'light' : 'dark'); - } - - return ( - <Screen edges={['top', 'bottom']}> - <ScrollView contentContainerStyle={{ padding: spacing.md, gap: spacing.lg }}> - <ScreenHeader - title="UI Gallery" - subtitle="Dev-only primitive smoke test" - trailing={ - <IconButton - accessibilityLabel="Toggle color scheme" - icon={themePreference === 'dark' ? 'sunny-outline' : 'moon-outline'} - variant="tonal" - onPress={toggleScheme} - /> - } - /> - - <Section title="Text"> - <Text variant="display">Display</Text> - <Text variant="heading">Heading</Text> - <Text variant="title">Title</Text> - <Text variant="subtitle">Subtitle</Text> - <Text variant="body">Body text</Text> - <Text variant="bodyStrong">Body strong</Text> - <Text variant="callout">Callout</Text> - <Text variant="caption" color="secondary"> - Caption secondary - </Text> - <Text variant="label">Label</Text> - <Text variant="micro" numeric> - 123,456 - </Text> - </Section> - - <Section title="Buttons"> - <View style={styles.row}> - <Button label="Primary" onPress={() => {}} /> - <Button label="Tonal" variant="tonal" onPress={() => {}} /> - <Button label="Secondary" variant="secondary" onPress={() => {}} /> - </View> - <View style={styles.row}> - <Button label="Danger" variant="danger" onPress={() => {}} /> - <Button label="Ghost" variant="ghost" onPress={() => {}} /> - <Button label="Icon" icon="send" onPress={() => {}} /> - </View> - <View style={styles.row}> - <Button label="Small" size="sm" onPress={() => {}} /> - <Button label="Loading" loading onPress={() => {}} /> - <Button label="Disabled" disabled onPress={() => {}} /> - </View> - </Section> - - <Section title="IconButton"> - <View style={styles.row}> - <IconButton - accessibilityLabel="Ghost" - icon="heart-outline" - variant="ghost" - onPress={() => {}} - /> - <IconButton - accessibilityLabel="Tonal" - icon="heart-outline" - variant="tonal" - onPress={() => {}} - /> - <IconButton - accessibilityLabel="Filled" - icon="heart-outline" - variant="filled" - onPress={() => {}} - /> - <IconButton - accessibilityLabel="Outline" - icon="heart-outline" - variant="outline" - onPress={() => {}} - /> - <IconButton - accessibilityLabel="Badged" - icon="notifications-outline" - badgeCount={5} - onPress={() => {}} - /> - <IconButton accessibilityLabel="Loading" icon="send" loading onPress={() => {}} /> - </View> - </Section> - - <Section title="Avatar"> - <View style={styles.row}> - <Avatar name="Ada Lovelace" size={40} /> - <Avatar name="Grace Hopper" size={52} ring="online" /> - <Avatar name="Bot Assistant" size={52} ring="bot" /> - <Avatar name="Margaret Hamilton" size={52} badge={<Badge tone="success" count={3} />} /> - </View> - </Section> - - <Section title="Chip"> - <View style={styles.row}> - <Chip label="All" selected={tab === 'all'} onPress={() => setTab('all')} /> - <Chip label="Unread" selected={tab === 'unread'} onPress={() => setTab('unread')} /> - <Chip - label="Bot" - tone="custom" - color={colors.bubbleBotAccent} - selected={chipSelected} - onPress={() => setChipSelected((prev) => !prev)} - /> - <Chip label="VIP" onRemove={() => {}} /> - </View> - </Section> - - <Section title="Badge / CountPill"> - <View style={styles.row}> - <Badge tone="brand">New</Badge> - <Badge tone="danger" count={12} /> - <CountPill count={4} /> - <CountPill count={230} tone="danger" /> - </View> - </Section> - - <Section title="Surface / Card"> - <Surface level={1} radius="md" padding="md" bordered> - <Text variant="body">Surface level 1</Text> - </Surface> - <Card padding="md" elevation={2}> - <Text variant="body">Card (level 1, bordered, elevation 2)</Text> - </Card> - </Section> - - <Section title="SearchBar / SegmentedTabs"> - <SearchBar value={search} onChangeText={setSearch} placeholder="Search…" /> - <SegmentedTabs - options={[ - { value: 'all', label: 'All' }, - { value: 'unread', label: 'Unread' }, - { value: 'bot', label: 'Bot' }, - ]} - value={tab} - onChange={setTab} - /> - </Section> - - <Section title="ListItem"> - <Card padding="xxs"> - <ListItem - title="Standard row" - subtitle="With a subtitle" - showChevron - onPress={() => {}} - /> - <Divider inset /> - <ListItem title="Row with value" value="Enabled" onPress={() => {}} /> - <Divider inset /> - <ListItem title="Destructive row" destructive onPress={() => {}} /> - <Divider inset /> - <ListItem title="Disabled row" disabled onPress={() => {}} /> - </Card> - </Section> - - <Section title="ToggleRow"> - <Card padding="xxs"> - <ToggleRow - label="Push notifications" - description="Get notified about new messages" - value={toggleValue} - onValueChange={setToggleValue} - /> - </Card> - </Section> - - <Section title="ErrorBanner"> - <ErrorBanner - tone="danger" - message="Failed to send message." - actionLabel="Retry" - onAction={() => {}} - /> - <ErrorBanner tone="warning" message="This workspace is near its plan limit." /> - <ErrorBanner tone="info" message="You're viewing a read-only flow." /> - </Section> - - <Section title="EmptyState"> - <View style={{ height: 220 }}> - <EmptyState - icon="chatbubble-ellipses-outline" - title="No conversations" - description="New conversations will show up here." - action={{ label: 'Clear filters', onPress: () => {} }} - /> - </View> - </Section> - - <Section title="Skeleton"> - <SkeletonRow.Conversation /> - <SkeletonRow.Contact /> - <SkeletonRow.Message align="start" /> - <SkeletonRow.Message align="end" /> - <Skeleton width="80%" height={14} /> - </Section> - - <Section title="ListFooterSpinner"> - <Card padding="xxs"> - <ListFooterSpinner visible /> - </Card> - </Section> - - <Section title="DateSeparator"> - <DateSeparator date={todayIso} /> - <DateSeparator date={yesterdayIso} /> - </Section> - - <Section title="ConnectionBanner"> - <ConnectionBanner state={connectionState} /> - <View style={styles.row}> - {(['online', 'connecting', 'reconnecting', 'offline'] as ConnectionState[]).map( - (state) => ( - <Button - key={state} - label={state} - size="sm" - variant="secondary" - onPress={() => setConnectionState(state)} - /> - ), - )} - </View> - </Section> - - <Section title="Toast"> - <Button - label="Show toast" - variant="tonal" - onPress={() => - showToast({ - message: 'Message sent', - tone: 'success', - action: { label: 'Undo', onPress: () => {} }, - }) - } - /> - </Section> - - <Section title="Sheet"> - <Button - label="Open sheet" - variant="secondary" - onPress={() => sheetRef.current?.expand()} - /> - </Section> - - <Section title="Fake conversation row"> - <Card padding="md"> - <View style={[styles.row, { alignItems: 'flex-start', gap: spacing.sm }]}> - <Avatar name="Jane Doe" size={52} ring="online" /> - <View style={{ flex: 1, gap: 4 }}> - <View style={[styles.row, { justifyContent: 'space-between' }]}> - <Text variant="bodyStrong" numberOfLines={1}> - Jane Doe - </Text> - <Text variant="micro" color="tertiary" numeric> - 2m - </Text> - </View> - <Text variant="callout" color="secondary" numberOfLines={1}> - Thanks for the quick reply! Let me check with the team. - </Text> - <View style={styles.row}> - <Chip - label="Bot handling" - size="sm" - tone="custom" - color={colors.bubbleBotAccent} - /> - <CountPill count={2} /> - </View> - </View> - </View> - </Card> - </Section> - - <Section title="Fake message bubble group"> - <View style={{ gap: 4 }}> - <View style={[styles.row, { justifyContent: 'flex-start' }]}> - <View - style={{ - backgroundColor: colors.bubbleIn, - borderRadius: 18, - paddingHorizontal: 14, - paddingVertical: 10, - maxWidth: '75%', - }} - > - <Text variant="body" style={{ color: colors.bubbleInText }}> - Hey, is my order still on track? - </Text> - </View> - </View> - <View style={[styles.row, { justifyContent: 'flex-end' }]}> - <View - style={{ - backgroundColor: colors.bubbleOut, - borderRadius: 18, - paddingHorizontal: 14, - paddingVertical: 10, - maxWidth: '75%', - }} - > - <Text variant="body" style={{ color: colors.bubbleOutText }}> - Yes! It shipped this morning. - </Text> - </View> - </View> - <View style={[styles.row, { justifyContent: 'flex-start' }]}> - <View - style={{ - backgroundColor: colors.bubbleBot, - borderRadius: 18, - paddingHorizontal: 14, - paddingVertical: 10, - maxWidth: '75%', - }} - > - <Text variant="body" style={{ color: colors.bubbleBotText }}> - Automated: your tracking link has been sent by email. - </Text> - </View> - </View> - </View> - </Section> - </ScrollView> - - <Sheet ref={sheetRef} snapPoints={['40%']}> - <SheetHeader title="Example sheet" onClose={() => sheetRef.current?.close()} /> - <View style={{ padding: spacing.md }}> - <Text variant="body" color="secondary"> - This is a Sheet body, rendered via BottomSheetView. - </Text> - </View> - </Sheet> - </Screen> - ); -} - -function Section({ title, children }: { title: string; children: ReactNode }) { - const { spacing } = useTheme(); - return ( - <View style={{ gap: spacing.sm }}> - <Text variant="label" color="tertiary"> - {title} - </Text> - <View style={{ gap: spacing.sm }}>{children}</View> - </View> - ); -} - -const styles = StyleSheet.create({ - row: { - flexDirection: 'row', - alignItems: 'center', - flexWrap: 'wrap', - gap: 8, - }, -}); diff --git a/src/app/+not-found.tsx b/src/app/+not-found.tsx index bdd741d..4d543d1 100644 --- a/src/app/+not-found.tsx +++ b/src/app/+not-found.tsx @@ -1,7 +1,6 @@ import { router } from 'expo-router'; import { useTranslation } from 'react-i18next'; -import { Button } from '@/components/ui/button'; import { EmptyState } from '@/components/ui/empty-state'; import { Screen } from '@/components/ui/screen'; import { useAuthStore } from '@/stores/use-auth-store'; @@ -23,8 +22,8 @@ export default function NotFoundScreen() { icon="compass-outline" title={t('errors.notFoundTitle')} description={t('errors.notFoundBody')} + action={{ label: t('errors.goHome'), onPress: handleGoHome }} /> - <Button label={t('errors.goHome')} onPress={handleGoHome} /> </Screen> ); } diff --git a/src/app/_layout.tsx b/src/app/_layout.tsx index 3d28444..d28ef41 100644 --- a/src/app/_layout.tsx +++ b/src/app/_layout.tsx @@ -7,6 +7,7 @@ import { PlusJakartaSans_700Bold, useFonts, } from '@expo-google-fonts/plus-jakarta-sans'; +import type { ErrorBoundaryProps } from 'expo-router'; import { Stack, ThemeProvider } from 'expo-router'; import * as SplashScreen from 'expo-splash-screen'; import { StatusBar } from 'expo-status-bar'; @@ -14,9 +15,11 @@ import * as SystemUI from 'expo-system-ui'; import { useEffect, useState } from 'react'; import { I18nManager } from 'react-native'; import { GestureHandlerRootView } from 'react-native-gesture-handler'; +import { useTranslation } from 'react-i18next'; import { getSession } from '@/api/auth-endpoints'; import { clearAuthToken, getAuthToken } from '@/api/auth-token'; +import { EmptyState } from '@/components/ui/empty-state'; import { ToastProvider } from '@/components/ui/toast'; import { initI18n } from '@/i18n'; import { applyLanguage } from '@/i18n/apply-language'; @@ -29,10 +32,15 @@ import { ensureAndroidNotificationChannel } from '@/lib/notifications'; import { initNotificationTapHandling } from '@/lib/notification-tap'; import { useAuthStore } from '@/stores/use-auth-store'; import { useSettingsStore, waitForSettingsHydration } from '@/stores/use-settings-store'; +import { waitForWorkspaceHydration } from '@/stores/use-workspace-store'; import { getNavigationTheme } from '@/theme/navigation-theme'; import { useResolvedScheme, useTheme } from '@/theme/use-theme'; -SplashScreen.preventAutoHideAsync(); +SplashScreen.preventAutoHideAsync().catch(() => { + // Only fails if the splash screen was already hidden or never registered — either way there's + // nothing to recover, and the bootstrap gate below still hides it once ready. +}); +SplashScreen.setOptions({ duration: 300, fade: true }); // i18next resources are static/local (bundled JSON, no network) and init() is synchronous, so // this runs once at module load — before the first render — rather than as a render-triggered @@ -78,8 +86,10 @@ function useLanguageBootstrap(): boolean { /** * Bootstrap gate: resolves the auth store's 'pending' status to 'signed-in'/'signed-out' by * checking for a stored token and validating it against `getSession`, then hides the splash - * screen. Runs once at root-layout mount — every route's own guard reads `status` off the store - * rather than re-checking the session itself. + * screen. Also waits for the persisted workspace-store snapshot to hydrate, so a cold start never + * reads `currentWorkspaceId === null` before the real (persisted) value has loaded and bounces an + * already-workspace-selected user to the picker. Runs once at root-layout mount — every route's + * own guard reads `status` off the store rather than re-checking the session itself. */ function useAuthBootstrap(): boolean { const [ready, setReady] = useState(false); @@ -90,20 +100,29 @@ function useAuthBootstrap(): boolean { let cancelled = false; async function bootstrap() { + await waitForWorkspaceHydration(); + const token = await getAuthToken(); if (!token) { if (!cancelled) setSignedOut(); return; } - const user = await getSession(token); - if (cancelled) return; - - if (user) { - setSignedIn(user); - } else { - await clearAuthToken(); - setSignedOut(); + try { + const user = await getSession(token); + if (cancelled) return; + + if (user) { + setSignedIn(user); + } else { + await clearAuthToken(); + setSignedOut(); + } + } catch { + // A network/server failure validating the session is not proof the token is invalid — + // treat it as signed-out for this launch without clearing SecureStore, so the next + // successful bootstrap can still sign the user back in with the same token. + if (!cancelled) setSignedOut(); } } @@ -132,16 +151,18 @@ export default function RootLayout() { const { colors } = useTheme(); const authReady = useAuthBootstrap(); const languageReady = useLanguageBootstrap(); - const [fontsLoaded] = useFonts({ + const [fontsLoaded, fontError] = useFonts({ PlusJakartaSans_600SemiBold, PlusJakartaSans_700Bold, }); useEffect(() => { - if (authReady && languageReady && fontsLoaded) { + // A font failing to load (e.g. offline on first launch before assets are cached) must not + // hold the splash screen forever — proceed with the system fallback font instead. + if (authReady && languageReady && (fontsLoaded || fontError)) { SplashScreen.hideAsync(); } - }, [authReady, languageReady, fontsLoaded]); + }, [authReady, languageReady, fontsLoaded, fontError]); // Registers the Android notification channel (idempotent — safe to call again even though // getPushTokenAsync also calls this lazily) and wires notification-tap → navigation, once auth @@ -186,3 +207,18 @@ export default function RootLayout() { </GestureHandlerRootView> ); } + +/** expo-router renders this in place of the whole navigation tree when a route throws during + * render — without it, an uncaught error white-screens the app with no way back in. */ +export function ErrorBoundary({ error, retry }: ErrorBoundaryProps) { + const { t } = useTranslation(); + + return ( + <EmptyState + icon="warning-outline" + title={t('common.error')} + description={error.message || t('errors.unknown')} + action={{ label: t('common.retry'), onPress: retry }} + /> + ); +} diff --git a/src/app/camera.tsx b/src/app/camera.tsx index 3e5438b..278f20c 100644 --- a/src/app/camera.tsx +++ b/src/app/camera.tsx @@ -4,7 +4,7 @@ import { router, useLocalSearchParams } from 'expo-router'; import { StatusBar } from 'expo-status-bar'; import { useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { Pressable, StyleSheet, View } from 'react-native'; +import { ActivityIndicator, Pressable, StyleSheet, View } from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { Button } from '@/components/ui/button'; @@ -16,6 +16,7 @@ import { MAX_FILE_SIZE_BYTES, useSendMessage, } from '@/features/chat/api/use-send-message'; +import { triggerHaptic } from '@/lib/haptics'; import { useWorkspaceStore } from '@/stores/use-workspace-store'; import { useTheme } from '@/theme/use-theme'; @@ -52,6 +53,7 @@ export default function CameraScreen() { async function handleCapture() { if (!cameraRef.current || !workspaceId || !conversationId) return; + triggerHaptic('light'); setIsCapturing(true); setError(null); @@ -77,8 +79,10 @@ export default function CameraScreen() { { uri: photo.uri, mimeType: 'image/jpeg', fileName: `photo-${Date.now()}.jpg` }, ], }); + triggerHaptic('success'); router.back(); } catch (err) { + triggerHaptic('error'); setError(err instanceof AttachmentTooLargeError ? err.message : t('chat.failedToSendPhoto')); } finally { setIsCapturing(false); @@ -103,8 +107,13 @@ export default function CameraScreen() { accessibilityLabel={t('chat.camera')} onPress={handleCapture} disabled={isCapturing} - style={[styles.captureButton, { borderColor: colors.textInverse }]} - /> + style={[ + styles.captureButton, + { borderColor: colors.textInverse, opacity: isCapturing ? 0.6 : 1 }, + ]} + > + {isCapturing ? <ActivityIndicator color={colors.textPrimary} /> : null} + </Pressable> </View> {error ? ( <View @@ -148,6 +157,8 @@ const styles = StyleSheet.create({ backgroundColor: '#ffffff', alignSelf: 'center', marginHorizontal: 'auto', + alignItems: 'center', + justifyContent: 'center', }, errorBanner: { position: 'absolute', diff --git a/src/app/index.tsx b/src/app/index.tsx index 9ab7632..5603edd 100644 --- a/src/app/index.tsx +++ b/src/app/index.tsx @@ -1,11 +1,24 @@ import { Redirect } from 'expo-router'; -import { ActivityIndicator } from 'react-native'; +import { useEffect } from 'react'; +import { StyleSheet, View } from 'react-native'; +import Animated, { + Easing, + useAnimatedStyle, + useSharedValue, + withRepeat, + withTiming, +} from 'react-native-reanimated'; +import { Icon } from '@/components/ui/icon'; import { Screen } from '@/components/ui/screen'; import { useAuthStore } from '@/stores/use-auth-store'; import { useWorkspaceStore } from '@/stores/use-workspace-store'; +import { useReducedMotion } from '@/theme/motion'; import { useTheme } from '@/theme/use-theme'; +const LOGO_SIZE = 72; +const PULSE_DURATION_MS = 900; + /** * Root redirect gate: !authed → sign-in; mustChangePassword → change-password; no workspace → * workspace-picker; else → tabs shell. @@ -34,11 +47,56 @@ export default function IndexRedirect() { return <Redirect href="/(app)/(tabs)/conversations" />; } +/** Brand mark with a gentle pulse instead of a bare spinner — this screen normally shows only for + * the brief window between the splash screen hiding and the auth/workspace bootstrap gates + * resolving, so it's the first thing a returning user sees post-splash. */ function LoadingScreen() { const { colors } = useTheme(); + const reducedMotion = useReducedMotion(); + const scale = useSharedValue(1); + + useEffect(() => { + if (reducedMotion) { + scale.value = 1; + return undefined; + } + scale.value = withRepeat( + withTiming(1.08, { duration: PULSE_DURATION_MS, easing: Easing.inOut(Easing.ease) }), + -1, + true, + ); + return undefined; + }, [reducedMotion, scale]); + + const animatedStyle = useAnimatedStyle(() => ({ transform: [{ scale: scale.value }] })); + return ( <Screen> - <ActivityIndicator color={colors.primary} style={{ flex: 1 }} /> + <View style={styles.center}> + <Animated.View + style={[ + styles.logoCircle, + { backgroundColor: colors.brandSoft, borderRadius: LOGO_SIZE / 2 }, + animatedStyle, + ]} + > + <Icon name="chatbubbles" size={32} color={colors.brand} /> + </Animated.View> + </View> </Screen> ); } + +const styles = StyleSheet.create({ + center: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + }, + logoCircle: { + width: LOGO_SIZE, + height: LOGO_SIZE, + alignItems: 'center', + justifyContent: 'center', + }, +}); diff --git a/src/components/ui/badge.tsx b/src/components/ui/badge.tsx index 5b722fb..a8c705c 100644 --- a/src/components/ui/badge.tsx +++ b/src/components/ui/badge.tsx @@ -44,8 +44,9 @@ export function Badge({ children, color, textColor, tone, count, max = DEFAULT_M if (typeof count === 'number' && count <= 0 && children === undefined) return null; const toneTokens = tone ? TONE_TOKENS[tone] : undefined; - const backgroundColor = color ?? (toneTokens ? colors[toneTokens.bgKey] : colors.surface); - const resolvedTextColor = textColor ?? (toneTokens ? colors[toneTokens.textKey] : colors.text); + const backgroundColor = color ?? (toneTokens ? colors[toneTokens.bgKey] : colors.surface2); + const resolvedTextColor = + textColor ?? (toneTokens ? colors[toneTokens.textKey] : colors.textPrimary); const content = typeof count === 'number' ? (count > max ? `${max}+` : String(count)) : children; diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx index d3ef8fd..32c4a18 100644 --- a/src/components/ui/button.tsx +++ b/src/components/ui/button.tsx @@ -1,4 +1,5 @@ import type { ComponentProps } from 'react'; +import { useMemo } from 'react'; import { ActivityIndicator, StyleSheet } from 'react-native'; import { useTheme } from '@/theme/use-theme'; @@ -42,16 +43,26 @@ export function Button({ const { colors, spacing, radius } = useTheme(); const isDisabled = disabled || loading; + // Hoisted behind useMemo — Button renders on every list row/action bar, so rebuilding this + // object every render (to read a single variant's entry out of it) was needless per-render + // churn. const variantStyles: Record< Variant, { background: string; text: 'onBrand' | 'primary' | 'danger' | 'brand'; borderColor?: string } - > = { - primary: { background: colors.brand, text: 'onBrand' }, - danger: { background: colors.danger, text: 'onBrand' }, - secondary: { background: colors.surface1, text: 'primary', borderColor: colors.borderStrong }, - tonal: { background: colors.brandSoft, text: 'brand' }, - ghost: { background: 'transparent', text: 'primary' }, - }; + > = useMemo( + () => ({ + primary: { background: colors.brand, text: 'onBrand' }, + danger: { background: colors.danger, text: 'onBrand' }, + secondary: { + background: colors.surface1, + text: 'primary', + borderColor: colors.borderStrong, + }, + tonal: { background: colors.brandSoft, text: 'brand' }, + ghost: { background: 'transparent', text: 'primary' }, + }), + [colors], + ); const { background, text, borderColor } = variantStyles[variant]; const sizeStyle = SIZE_STYLES[size]; diff --git a/src/components/ui/connection-banner.tsx b/src/components/ui/connection-banner.tsx index 2211bee..2d9402c 100644 --- a/src/components/ui/connection-banner.tsx +++ b/src/components/ui/connection-banner.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from 'react'; +import { useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { StyleSheet } from 'react-native'; import Animated, { useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated'; @@ -61,6 +61,8 @@ export function ConnectionBanner({ state }: ConnectionBannerProps) { transform: [{ translateY: translateY.value }], })); + // Hoisted behind useMemo — this banner mounts on both the Inbox and chat screens and rebuilding + // all four states' config every render (to read one) was needless per-render churn. const config: Record< ConnectionState, { @@ -69,32 +71,35 @@ export function ConnectionBanner({ state }: ConnectionBannerProps) { textColor: string; label: string; } - > = { - online: { - icon: 'checkmark-circle-outline', - background: colors.successSoft, - textColor: colors.success, - label: t('realtime.online', { defaultValue: 'Back online' }), - }, - connecting: { - icon: 'sync-outline', - background: colors.infoSoft, - textColor: colors.info, - label: t('realtime.connecting', { defaultValue: 'Connecting…' }), - }, - reconnecting: { - icon: 'sync-outline', - background: colors.warningSoft, - textColor: colors.warning, - label: t('realtime.reconnecting', { defaultValue: 'Reconnecting…' }), - }, - offline: { - icon: 'cloud-offline-outline', - background: colors.dangerSoft, - textColor: colors.danger, - label: t('realtime.offline', { defaultValue: "You're offline" }), - }, - }; + > = useMemo( + () => ({ + online: { + icon: 'checkmark-circle-outline', + background: colors.successSoft, + textColor: colors.success, + label: t('realtime.online', { defaultValue: 'Back online' }), + }, + connecting: { + icon: 'sync-outline', + background: colors.infoSoft, + textColor: colors.info, + label: t('realtime.connecting', { defaultValue: 'Connecting…' }), + }, + reconnecting: { + icon: 'sync-outline', + background: colors.warningSoft, + textColor: colors.warning, + label: t('realtime.reconnecting', { defaultValue: 'Reconnecting…' }), + }, + offline: { + icon: 'cloud-offline-outline', + background: colors.dangerSoft, + textColor: colors.danger, + label: t('realtime.offline', { defaultValue: "You're offline" }), + }, + }), + [colors, t], + ); const { icon, background, textColor, label } = config[state]; if (!shouldShow) return null; diff --git a/src/components/ui/error-banner.tsx b/src/components/ui/error-banner.tsx index 6a980a1..2c6521b 100644 --- a/src/components/ui/error-banner.tsx +++ b/src/components/ui/error-banner.tsx @@ -1,3 +1,4 @@ +import { useMemo } from 'react'; import { Pressable, StyleSheet, View } from 'react-native'; import { useTheme } from '@/theme/use-theme'; @@ -17,11 +18,17 @@ interface ErrorBannerProps { export function ErrorBanner({ message, actionLabel, onAction, tone = 'danger' }: ErrorBannerProps) { const { colors, spacing, radius } = useTheme(); - const toneTokens = { - danger: { tint: colors.danger, soft: colors.dangerSoft }, - warning: { tint: colors.warning, soft: colors.warningSoft }, - info: { tint: colors.info, soft: colors.infoSoft }, - }[tone]; + // Hoisted behind useMemo — rebuilding all three tones every render (to read one) was needless + // per-render churn on a component that can sit mounted for a whole screen's lifetime. + const toneMap = useMemo( + () => ({ + danger: { tint: colors.danger, soft: colors.dangerSoft }, + warning: { tint: colors.warning, soft: colors.warningSoft }, + info: { tint: colors.info, soft: colors.infoSoft }, + }), + [colors], + ); + const toneTokens = toneMap[tone]; return ( <View @@ -46,7 +53,12 @@ export function ErrorBanner({ message, actionLabel, onAction, tone = 'danger' }: {message} </Text> {actionLabel && onAction ? ( - <Pressable onPress={onAction} accessibilityRole="button"> + <Pressable + onPress={onAction} + accessibilityRole="button" + accessibilityLabel={actionLabel} + hitSlop={8} + > <Text variant="caption" style={{ color: toneTokens.tint, fontWeight: '700' }}> {actionLabel} </Text> diff --git a/src/components/ui/list-item.tsx b/src/components/ui/list-item.tsx index b9eb68c..3fa9258 100644 --- a/src/components/ui/list-item.tsx +++ b/src/components/ui/list-item.tsx @@ -108,7 +108,11 @@ export function ListItem({ accessibilityRole="button" accessibilityState={{ ...accessibilityState, disabled }} disabled={disabled} - haptic={disabled ? false : destructive ? 'medium' : 'light'} + // Plain navigation rows (the overwhelming majority of ListItem uses — settings, contacts, + // members) get no haptic: a tap that just pushes a screen doesn't need feedback beyond the + // screen transition itself. Destructive rows keep the 'medium' haptic as extra confirmation + // weight for an action that can't be casually undone. + haptic={disabled ? false : destructive ? 'medium' : false} onPress={onPress} onPressIn={() => { // reanimated `SharedValue.value` is a mutable UI-thread ref, not React state — see diff --git a/src/components/ui/screen.tsx b/src/components/ui/screen.tsx index 5331e3a..ad1f6cf 100644 --- a/src/components/ui/screen.tsx +++ b/src/components/ui/screen.tsx @@ -1,6 +1,6 @@ import type { PropsWithChildren } from 'react'; import type { StyleProp, ViewStyle } from 'react-native'; -import { StyleSheet, View } from 'react-native'; +import { StyleSheet } from 'react-native'; import { type Edge, SafeAreaView } from 'react-native-safe-area-context'; import { useTheme } from '@/theme/use-theme'; @@ -20,12 +20,12 @@ export function Screen({ children, style, edges, padded = false }: ScreenProps) edges={edges} style={[ styles.container, - { backgroundColor: colors.background }, + { backgroundColor: colors.bg }, padded && { padding: spacing.md }, style, ]} > - <View style={styles.content}>{children}</View> + {children} </SafeAreaView> ); } @@ -34,7 +34,4 @@ const styles = StyleSheet.create({ container: { flex: 1, }, - content: { - flex: 1, - }, }); diff --git a/src/components/ui/search-bar.tsx b/src/components/ui/search-bar.tsx index 4caeaff..b8cae22 100644 --- a/src/components/ui/search-bar.tsx +++ b/src/components/ui/search-bar.tsx @@ -22,7 +22,7 @@ export function SearchBar({ value, onChangeText, placeholder }: SearchBarProps) style={[ styles.container, { - backgroundColor: colors.surface, + backgroundColor: colors.surface2, borderRadius: radius.md, paddingHorizontal: spacing.ms, gap: spacing.xs, @@ -35,7 +35,7 @@ export function SearchBar({ value, onChangeText, placeholder }: SearchBarProps) onChangeText={onChangeText} placeholder={placeholder} placeholderTextColor={colors.textSecondary} - style={[styles.input, { color: colors.text }]} + style={[styles.input, { color: colors.textPrimary }]} returnKeyType="search" autoCorrect={false} keyboardAppearance={scheme} diff --git a/src/components/ui/segmented-tabs.tsx b/src/components/ui/segmented-tabs.tsx index 7227cfa..af6b643 100644 --- a/src/components/ui/segmented-tabs.tsx +++ b/src/components/ui/segmented-tabs.tsx @@ -72,7 +72,7 @@ export function SegmentedTabs<T extends string>({ onLayout={handleLayout} style={[ styles.container, - { backgroundColor: colors.surface, borderRadius: radius.md, padding: 2 }, + { backgroundColor: colors.surface2, borderRadius: radius.md, padding: 2 }, ]} > {containerWidth > 0 ? ( @@ -81,7 +81,7 @@ export function SegmentedTabs<T extends string>({ styles.indicator, indicatorStyle, { - backgroundColor: colors.surfaceElevated, + backgroundColor: colors.surface1, borderRadius: radius.sm + 2, top: 2, bottom: 2, @@ -102,7 +102,7 @@ export function SegmentedTabs<T extends string>({ onChange(option.value); } }} - style={[styles.segment, { paddingVertical: spacing.xs + 2 }]} + style={[styles.segment, { paddingVertical: spacing.xs + 2, minHeight: 44 }]} > <Text variant="caption" color={isActive ? 'primary' : 'secondary'} style={styles.label}> {option.label} @@ -121,7 +121,7 @@ const styles = StyleSheet.create({ }, indicator: { position: 'absolute', - left: 0, + start: 0, }, segment: { flex: 1, diff --git a/src/components/ui/sheet.tsx b/src/components/ui/sheet.tsx index ad5f67c..a56c494 100644 --- a/src/components/ui/sheet.tsx +++ b/src/components/ui/sheet.tsx @@ -41,8 +41,8 @@ export const Sheet = forwardRef<BottomSheet, SheetProps>(function Sheet( enablePanDownToClose onClose={onDismiss} backdropComponent={renderBackdrop} - backgroundStyle={{ backgroundColor: colors.surfaceElevated, borderRadius: radius.xl }} - handleIndicatorStyle={{ backgroundColor: colors.border }} + backgroundStyle={{ backgroundColor: colors.surface1, borderRadius: radius.xl }} + handleIndicatorStyle={{ backgroundColor: colors.borderSubtle }} > <BottomSheetView style={{ flex: 1 }}>{children}</BottomSheetView> </BottomSheet> @@ -81,8 +81,8 @@ export const SheetModal = forwardRef<BottomSheetModal, SheetModalProps>(function enablePanDownToClose onDismiss={onDismiss} backdropComponent={renderBackdrop} - backgroundStyle={{ backgroundColor: colors.surfaceElevated, borderRadius: radius.xl }} - handleIndicatorStyle={{ backgroundColor: colors.border }} + backgroundStyle={{ backgroundColor: colors.surface1, borderRadius: radius.xl }} + handleIndicatorStyle={{ backgroundColor: colors.borderSubtle }} > <BottomSheetView style={{ flex: 1 }}>{children}</BottomSheetView> </BottomSheetModal> diff --git a/src/components/ui/skeleton.tsx b/src/components/ui/skeleton.tsx index 7e9654a..3689aa6 100644 --- a/src/components/ui/skeleton.tsx +++ b/src/components/ui/skeleton.tsx @@ -10,6 +10,7 @@ import Animated, { withTiming, } from 'react-native-reanimated'; +import { useReducedMotion } from '@/theme/motion'; import { useTheme } from '@/theme/use-theme'; interface SkeletonProps { @@ -24,23 +25,28 @@ const PULSE_DURATION_MS = 800; /** Pulsing placeholder block for loading states. Compositor-friendly (opacity only). */ export function Skeleton({ width = '100%', height = 16, borderRadius = 6, style }: SkeletonProps) { const { colors } = useTheme(); + const reducedMotion = useReducedMotion(); const opacity = useSharedValue(0.5); useEffect(() => { + if (reducedMotion) { + opacity.value = 0.7; + return undefined; + } opacity.value = withRepeat( withTiming(1, { duration: PULSE_DURATION_MS, easing: Easing.inOut(Easing.ease) }), -1, true, ); return () => cancelAnimation(opacity); - }, [opacity]); + }, [opacity, reducedMotion]); const animatedStyle = useAnimatedStyle(() => ({ opacity: opacity.value })); return ( <Animated.View style={[ - { width, height, borderRadius, backgroundColor: colors.surface }, + { width, height, borderRadius, backgroundColor: colors.surface2 }, animatedStyle, style, ]} diff --git a/src/components/ui/swipeable-row.tsx b/src/components/ui/swipeable-row.tsx index a9b14ab..a8c46cc 100644 --- a/src/components/ui/swipeable-row.tsx +++ b/src/components/ui/swipeable-row.tsx @@ -1,8 +1,7 @@ import type { ReactNode } from 'react'; -import { useCallback, useRef } from 'react'; +import { useRef } from 'react'; import { I18nManager, Pressable, StyleSheet, View } from 'react-native'; import type { SharedValue } from 'react-native-reanimated'; -import { runOnJS, useAnimatedReaction, useSharedValue } from 'react-native-reanimated'; import Swipeable, { type SwipeableMethods } from 'react-native-gesture-handler/ReanimatedSwipeable'; import { triggerHaptic } from '@/lib/haptics'; @@ -27,8 +26,6 @@ interface SwipeableRowProps { } const ACTION_WIDTH = 76; -// Fraction of one action's width past which crossing counts as "revealed" for haptic purposes. -const HAPTIC_PROGRESS_THRESHOLD = 0.9; function ActionButton({ action, width }: { action: SwipeAction; width: number }) { const { colors, spacing } = useTheme(); @@ -80,7 +77,12 @@ function ActionsPanel({ actions, side }: { actions: SwipeAction[]; side: 'left' /** Row wrapper with swipe-to-reveal action panels on either edge. Wraps RNGH's * `ReanimatedSwipeable`. `leftActions`/`rightActions` are logical (start/end in LTR); when * `I18nManager.isRTL` is true, the rendered panel sides swap so "start" actions stay at the - * visual start of the row. Fires a 'medium' haptic once a swipe crosses the reveal threshold. */ + * visual start of the row. Fires a 'medium' haptic once a swipe commits to opening — driven by + * RNGH's own `onSwipeableWillOpen` callback (fired once, off the UI thread via `runOnJS` + * internally) rather than a `useAnimatedReaction` tracking a shared value that + * `renderLeftActions`/`renderRightActions` mutated during render — mutating a shared value as a + * side effect of a render-phase function is exactly the kind of thing React (and reanimated) call + * out as unsafe, even though it "worked" in practice here. */ export function SwipeableRow({ leftActions, rightActions, @@ -88,57 +90,13 @@ export function SwipeableRow({ children, }: SwipeableRowProps) { const swipeableRef = useRef<SwipeableMethods>(null); - const hasFiredHapticRef = useRef(false); - const progressShared = useSharedValue(0); - - const fireHapticOnce = useCallback(() => { - if (!hasFiredHapticRef.current) { - hasFiredHapticRef.current = true; - triggerHaptic('medium'); - } - }, []); - - const resetHapticGuard = useCallback(() => { - hasFiredHapticRef.current = false; - }, []); - - useAnimatedReaction( - () => progressShared.value, - (progress, previous) => { - if (progress >= HAPTIC_PROGRESS_THRESHOLD && (previous ?? 0) < HAPTIC_PROGRESS_THRESHOLD) { - runOnJS(fireHapticOnce)(); - } else if ( - progress < HAPTIC_PROGRESS_THRESHOLD && - (previous ?? 0) >= HAPTIC_PROGRESS_THRESHOLD - ) { - runOnJS(resetHapticGuard)(); - } - }, - ); - - const trackProgress = useCallback( - (progress: SharedValue<number>) => { - 'worklet'; - // reanimated `SharedValue.value` is a mutable UI-thread ref, not React state — see - // pressable-scale.tsx for the fuller explanation of this lint false-positive. - // eslint-disable-next-line react-hooks/immutability - progressShared.value = progress.value; - }, - [progressShared], - ); const renderLeftActions = leftActions?.length - ? (progress: SharedValue<number>) => { - trackProgress(progress); - return <ActionsPanel actions={leftActions} side="left" />; - } + ? (progress: SharedValue<number>) => <ActionsPanel actions={leftActions} side="left" /> : undefined; const renderRightActions = rightActions?.length - ? (progress: SharedValue<number>) => { - trackProgress(progress); - return <ActionsPanel actions={rightActions} side="right" />; - } + ? (progress: SharedValue<number>) => <ActionsPanel actions={rightActions} side="right" /> : undefined; return ( @@ -149,6 +107,7 @@ export function SwipeableRow({ rightThreshold={ACTION_WIDTH / 2} renderLeftActions={I18nManager.isRTL ? renderRightActions : renderLeftActions} renderRightActions={I18nManager.isRTL ? renderLeftActions : renderRightActions} + onSwipeableWillOpen={() => triggerHaptic('medium')} onSwipeableOpen={(direction) => { // RNGH reports the physically-rendered side; translate back to logical start/end. const logicalSide = I18nManager.isRTL diff --git a/src/components/ui/text-field.tsx b/src/components/ui/text-field.tsx index 11ad6d6..0725ce3 100644 --- a/src/components/ui/text-field.tsx +++ b/src/components/ui/text-field.tsx @@ -60,10 +60,10 @@ export const TextField = forwardRef<RNTextInputType, TextFieldProps>(function Te style={[ styles.input, { - backgroundColor: colors.surface, - borderColor: error ? colors.danger : isFocused ? colors.primary : colors.border, + backgroundColor: colors.surface2, + borderColor: error ? colors.danger : isFocused ? colors.brand : colors.borderSubtle, borderRadius: radius.md, - color: colors.text, + color: colors.textPrimary, fontSize: typography.body.fontSize, paddingHorizontal: spacing.base, paddingEnd: trailingIcon ? spacing.xl + spacing.sm : spacing.base, diff --git a/src/components/ui/text.tsx b/src/components/ui/text.tsx index 124a5e2..2d833d7 100644 --- a/src/components/ui/text.tsx +++ b/src/components/ui/text.tsx @@ -1,4 +1,5 @@ import type { ComponentProps } from 'react'; +import { useMemo } from 'react'; import { Text as RNText, StyleSheet } from 'react-native'; import { useTranslation } from 'react-i18next'; @@ -50,17 +51,22 @@ export function Text({ const { colors, typography } = useTheme(); const { i18n } = useTranslation(); - const colorMap: Record<ColorRole, string> = { - primary: colors.textPrimary, - secondary: colors.textSecondary, - tertiary: colors.textTertiary, - inverse: colors.textInverse, - brand: colors.brand, - danger: colors.danger, - success: colors.success, - warning: colors.warning, - onBrand: colors.onBrand, - }; + // Hoisted behind useMemo — this component renders on every message/list row, so rebuilding a + // fresh object every render (to read a single value out of it) was needless per-render churn. + const colorMap: Record<ColorRole, string> = useMemo( + () => ({ + primary: colors.textPrimary, + secondary: colors.textSecondary, + tertiary: colors.textTertiary, + inverse: colors.textInverse, + brand: colors.brand, + danger: colors.danger, + success: colors.success, + warning: colors.warning, + onBrand: colors.onBrand, + }), + [colors], + ); const token = typography[variant]; const fontFamily = DISPLAY_FAMILY_VARIANTS.includes(variant) diff --git a/src/components/ui/toast.tsx b/src/components/ui/toast.tsx index 9f52807..b1b3a1f 100644 --- a/src/components/ui/toast.tsx +++ b/src/components/ui/toast.tsx @@ -77,6 +77,11 @@ export function ToastProvider({ children }: PropsWithChildren) { } const duration = current.durationMs ?? DEFAULT_DURATION_MS; + // Both timers are tracked (not just the outer one) so cleanup can cancel the exit-animation + // dismiss too — previously only the outer timer was cleared, so if `current` changed (or this + // unmounted) mid-exit-animation, the inner `setTimeout` still fired later and called + // `dismissCurrent` against a stale render. + let exitTimer: ReturnType<typeof setTimeout> | undefined; const timer = setTimeout(() => { if (reducedMotion) { translateY.value = TOAST_HEIGHT + 40; @@ -85,13 +90,23 @@ export function ToastProvider({ children }: PropsWithChildren) { } else { opacity.value = withTiming(0, { duration: motion.durations.exit }); translateY.value = withTiming(TOAST_HEIGHT + 40, { duration: motion.durations.exit }); - setTimeout(dismissCurrent, motion.durations.exit); + exitTimer = setTimeout(dismissCurrent, motion.durations.exit); } }, duration); - return () => clearTimeout(timer); - // eslint-disable-next-line react-hooks/exhaustive-deps -- shared values are stable refs, motion/reducedMotion are effectively static. - }, [current, dismissCurrent]); + return () => { + clearTimeout(timer); + clearTimeout(exitTimer); + }; + }, [ + current, + dismissCurrent, + reducedMotion, + motion.durations.enter, + motion.durations.exit, + translateY, + opacity, + ]); const animatedStyle = useAnimatedStyle(() => ({ opacity: opacity.value, diff --git a/src/components/ui/toggle-row.tsx b/src/components/ui/toggle-row.tsx index 65d1df9..b549646 100644 --- a/src/components/ui/toggle-row.tsx +++ b/src/components/ui/toggle-row.tsx @@ -38,7 +38,7 @@ export function ToggleRow({ label, description, value, onValueChange, disabled } value={value} onValueChange={handleChange} disabled={disabled} - trackColor={{ true: colors.primary, false: colors.border }} + trackColor={{ true: colors.brand, false: colors.borderSubtle }} /> </View> ); diff --git a/src/dev/gallery-content.tsx b/src/dev/gallery-content.tsx new file mode 100644 index 0000000..fa87f59 --- /dev/null +++ b/src/dev/gallery-content.tsx @@ -0,0 +1,427 @@ +import type BottomSheet from '@gorhom/bottom-sheet'; +import type { ReactNode } from 'react'; +import { useRef, useState } from 'react'; +import { ScrollView, StyleSheet, View } from 'react-native'; + +import { Avatar } from '@/components/ui/avatar'; +import { Badge } from '@/components/ui/badge'; +import { Button } from '@/components/ui/button'; +import { Card, Surface } from '@/components/ui/surface'; +import { Chip } from '@/components/ui/chip'; +import { ConnectionBanner, type ConnectionState } from '@/components/ui/connection-banner'; +import { CountPill } from '@/components/ui/count-pill'; +import { DateSeparator } from '@/components/ui/date-separator'; +import { Divider } from '@/components/ui/divider'; +import { EmptyState } from '@/components/ui/empty-state'; +import { ErrorBanner } from '@/components/ui/error-banner'; +import { IconButton } from '@/components/ui/icon-button'; +import { ListFooterSpinner } from '@/components/ui/list-footer-spinner'; +import { ListItem } from '@/components/ui/list-item'; +import { Screen } from '@/components/ui/screen'; +import { ScreenHeader } from '@/components/ui/screen-header'; +import { SearchBar } from '@/components/ui/search-bar'; +import { SegmentedTabs } from '@/components/ui/segmented-tabs'; +import { Sheet, SheetHeader } from '@/components/ui/sheet'; +import { Skeleton, SkeletonRow } from '@/components/ui/skeleton'; +import { Text } from '@/components/ui/text'; +import { ToggleRow } from '@/components/ui/toggle-row'; +import { useToast } from '@/components/ui/toast'; +import { useSettingsStore } from '@/stores/use-settings-store'; +import { useTheme } from '@/theme/use-theme'; + +/** + * Visual smoke-test surface for every Phase 2 UI primitive, in various states, with a light/dark + * toggle. Not a polished screen — the "storybook-lite" the plan asks for. Split into its own + * module (rather than living inline in the `(dev)/gallery` route file) so its 24 primitive imports + * are only pulled into the bundle via the lazy `require()` in that route's `__DEV__` branch, + * instead of shipping in every production build's static import graph. + */ +export function GalleryContent() { + const { colors, spacing } = useTheme(); + const themePreference = useSettingsStore((state) => state.themePreference); + const setThemePreference = useSettingsStore((state) => state.setThemePreference); + const showToast = useToast(); + + const [search, setSearch] = useState(''); + const [tab, setTab] = useState<'all' | 'unread' | 'bot'>('all'); + const [chipSelected, setChipSelected] = useState(true); + const [toggleValue, setToggleValue] = useState(true); + const [connectionState, setConnectionState] = useState<ConnectionState>('offline'); + const sheetRef = useRef<BottomSheet>(null); + // Lazy-init so `Date.now()` runs once (at first render) instead of being called fresh on every + // render, which the react-compiler lint rule flags as an impure render-time call. + const [{ todayIso, yesterdayIso }] = useState(() => { + const now = Date.now(); + return { + todayIso: new Date(now).toISOString(), + yesterdayIso: new Date(now - 86400000).toISOString(), + }; + }); + + function toggleScheme() { + setThemePreference(themePreference === 'dark' ? 'light' : 'dark'); + } + + return ( + <Screen edges={['top', 'bottom']}> + <ScrollView contentContainerStyle={{ padding: spacing.md, gap: spacing.lg }}> + <ScreenHeader + title="UI Gallery" + subtitle="Dev-only primitive smoke test" + trailing={ + <IconButton + accessibilityLabel="Toggle color scheme" + icon={themePreference === 'dark' ? 'sunny-outline' : 'moon-outline'} + variant="tonal" + onPress={toggleScheme} + /> + } + /> + + <Section title="Text"> + <Text variant="display">Display</Text> + <Text variant="heading">Heading</Text> + <Text variant="title">Title</Text> + <Text variant="subtitle">Subtitle</Text> + <Text variant="body">Body text</Text> + <Text variant="bodyStrong">Body strong</Text> + <Text variant="callout">Callout</Text> + <Text variant="caption" color="secondary"> + Caption secondary + </Text> + <Text variant="label">Label</Text> + <Text variant="micro" numeric> + 123,456 + </Text> + </Section> + + <Section title="Buttons"> + <View style={styles.row}> + <Button label="Primary" onPress={() => {}} /> + <Button label="Tonal" variant="tonal" onPress={() => {}} /> + <Button label="Secondary" variant="secondary" onPress={() => {}} /> + </View> + <View style={styles.row}> + <Button label="Danger" variant="danger" onPress={() => {}} /> + <Button label="Ghost" variant="ghost" onPress={() => {}} /> + <Button label="Icon" icon="send" onPress={() => {}} /> + </View> + <View style={styles.row}> + <Button label="Small" size="sm" onPress={() => {}} /> + <Button label="Loading" loading onPress={() => {}} /> + <Button label="Disabled" disabled onPress={() => {}} /> + </View> + </Section> + + <Section title="IconButton"> + <View style={styles.row}> + <IconButton + accessibilityLabel="Ghost" + icon="heart-outline" + variant="ghost" + onPress={() => {}} + /> + <IconButton + accessibilityLabel="Tonal" + icon="heart-outline" + variant="tonal" + onPress={() => {}} + /> + <IconButton + accessibilityLabel="Filled" + icon="heart-outline" + variant="filled" + onPress={() => {}} + /> + <IconButton + accessibilityLabel="Outline" + icon="heart-outline" + variant="outline" + onPress={() => {}} + /> + <IconButton + accessibilityLabel="Badged" + icon="notifications-outline" + badgeCount={5} + onPress={() => {}} + /> + <IconButton accessibilityLabel="Loading" icon="send" loading onPress={() => {}} /> + </View> + </Section> + + <Section title="Avatar"> + <View style={styles.row}> + <Avatar name="Ada Lovelace" size={40} /> + <Avatar name="Grace Hopper" size={52} ring="online" /> + <Avatar name="Bot Assistant" size={52} ring="bot" /> + <Avatar name="Margaret Hamilton" size={52} badge={<Badge tone="success" count={3} />} /> + </View> + </Section> + + <Section title="Chip"> + <View style={styles.row}> + <Chip label="All" selected={tab === 'all'} onPress={() => setTab('all')} /> + <Chip label="Unread" selected={tab === 'unread'} onPress={() => setTab('unread')} /> + <Chip + label="Bot" + tone="custom" + color={colors.bubbleBotAccent} + selected={chipSelected} + onPress={() => setChipSelected((prev) => !prev)} + /> + <Chip label="VIP" onRemove={() => {}} /> + </View> + </Section> + + <Section title="Badge / CountPill"> + <View style={styles.row}> + <Badge tone="brand">New</Badge> + <Badge tone="danger" count={12} /> + <CountPill count={4} /> + <CountPill count={230} tone="danger" /> + </View> + </Section> + + <Section title="Surface / Card"> + <Surface level={1} radius="md" padding="md" bordered> + <Text variant="body">Surface level 1</Text> + </Surface> + <Card padding="md" elevation={2}> + <Text variant="body">Card (level 1, bordered, elevation 2)</Text> + </Card> + </Section> + + <Section title="SearchBar / SegmentedTabs"> + <SearchBar value={search} onChangeText={setSearch} placeholder="Search…" /> + <SegmentedTabs + options={[ + { value: 'all', label: 'All' }, + { value: 'unread', label: 'Unread' }, + { value: 'bot', label: 'Bot' }, + ]} + value={tab} + onChange={setTab} + /> + </Section> + + <Section title="ListItem"> + <Card padding="xxs"> + <ListItem + title="Standard row" + subtitle="With a subtitle" + showChevron + onPress={() => {}} + /> + <Divider inset /> + <ListItem title="Row with value" value="Enabled" onPress={() => {}} /> + <Divider inset /> + <ListItem title="Destructive row" destructive onPress={() => {}} /> + <Divider inset /> + <ListItem title="Disabled row" disabled onPress={() => {}} /> + </Card> + </Section> + + <Section title="ToggleRow"> + <Card padding="xxs"> + <ToggleRow + label="Push notifications" + description="Get notified about new messages" + value={toggleValue} + onValueChange={setToggleValue} + /> + </Card> + </Section> + + <Section title="ErrorBanner"> + <ErrorBanner + tone="danger" + message="Failed to send message." + actionLabel="Retry" + onAction={() => {}} + /> + <ErrorBanner tone="warning" message="This workspace is near its plan limit." /> + <ErrorBanner tone="info" message="You're viewing a read-only flow." /> + </Section> + + <Section title="EmptyState"> + <View style={{ height: 220 }}> + <EmptyState + icon="chatbubble-ellipses-outline" + title="No conversations" + description="New conversations will show up here." + action={{ label: 'Clear filters', onPress: () => {} }} + /> + </View> + </Section> + + <Section title="Skeleton"> + <SkeletonRow.Conversation /> + <SkeletonRow.Contact /> + <SkeletonRow.Message align="start" /> + <SkeletonRow.Message align="end" /> + <Skeleton width="80%" height={14} /> + </Section> + + <Section title="ListFooterSpinner"> + <Card padding="xxs"> + <ListFooterSpinner visible /> + </Card> + </Section> + + <Section title="DateSeparator"> + <DateSeparator date={todayIso} /> + <DateSeparator date={yesterdayIso} /> + </Section> + + <Section title="ConnectionBanner"> + <ConnectionBanner state={connectionState} /> + <View style={styles.row}> + {(['online', 'connecting', 'reconnecting', 'offline'] as ConnectionState[]).map( + (state) => ( + <Button + key={state} + label={state} + size="sm" + variant="secondary" + onPress={() => setConnectionState(state)} + /> + ), + )} + </View> + </Section> + + <Section title="Toast"> + <Button + label="Show toast" + variant="tonal" + onPress={() => + showToast({ + message: 'Message sent', + tone: 'success', + action: { label: 'Undo', onPress: () => {} }, + }) + } + /> + </Section> + + <Section title="Sheet"> + <Button + label="Open sheet" + variant="secondary" + onPress={() => sheetRef.current?.expand()} + /> + </Section> + + <Section title="Fake conversation row"> + <Card padding="md"> + <View style={[styles.row, { alignItems: 'flex-start', gap: spacing.sm }]}> + <Avatar name="Jane Doe" size={52} ring="online" /> + <View style={{ flex: 1, gap: 4 }}> + <View style={[styles.row, { justifyContent: 'space-between' }]}> + <Text variant="bodyStrong" numberOfLines={1}> + Jane Doe + </Text> + <Text variant="micro" color="tertiary" numeric> + 2m + </Text> + </View> + <Text variant="callout" color="secondary" numberOfLines={1}> + Thanks for the quick reply! Let me check with the team. + </Text> + <View style={styles.row}> + <Chip + label="Bot handling" + size="sm" + tone="custom" + color={colors.bubbleBotAccent} + /> + <CountPill count={2} /> + </View> + </View> + </View> + </Card> + </Section> + + <Section title="Fake message bubble group"> + <View style={{ gap: 4 }}> + <View style={[styles.row, { justifyContent: 'flex-start' }]}> + <View + style={{ + backgroundColor: colors.bubbleIn, + borderRadius: 18, + paddingHorizontal: 14, + paddingVertical: 10, + maxWidth: '75%', + }} + > + <Text variant="body" style={{ color: colors.bubbleInText }}> + Hey, is my order still on track? + </Text> + </View> + </View> + <View style={[styles.row, { justifyContent: 'flex-end' }]}> + <View + style={{ + backgroundColor: colors.bubbleOut, + borderRadius: 18, + paddingHorizontal: 14, + paddingVertical: 10, + maxWidth: '75%', + }} + > + <Text variant="body" style={{ color: colors.bubbleOutText }}> + Yes! It shipped this morning. + </Text> + </View> + </View> + <View style={[styles.row, { justifyContent: 'flex-start' }]}> + <View + style={{ + backgroundColor: colors.bubbleBot, + borderRadius: 18, + paddingHorizontal: 14, + paddingVertical: 10, + maxWidth: '75%', + }} + > + <Text variant="body" style={{ color: colors.bubbleBotText }}> + Automated: your tracking link has been sent by email. + </Text> + </View> + </View> + </View> + </Section> + </ScrollView> + + <Sheet ref={sheetRef} snapPoints={['40%']}> + <SheetHeader title="Example sheet" onClose={() => sheetRef.current?.close()} /> + <View style={{ padding: spacing.md }}> + <Text variant="body" color="secondary"> + This is a Sheet body, rendered via BottomSheetView. + </Text> + </View> + </Sheet> + </Screen> + ); +} + +function Section({ title, children }: { title: string; children: ReactNode }) { + const { spacing } = useTheme(); + return ( + <View style={{ gap: spacing.sm }}> + <Text variant="label" color="tertiary"> + {title} + </Text> + <View style={{ gap: spacing.sm }}>{children}</View> + </View> + ); +} + +const styles = StyleSheet.create({ + row: { + flexDirection: 'row', + alignItems: 'center', + flexWrap: 'wrap', + gap: 8, + }, +}); diff --git a/src/features/auth/social-buttons.tsx b/src/features/auth/social-buttons.tsx index 6590afe..157b4f6 100644 --- a/src/features/auth/social-buttons.tsx +++ b/src/features/auth/social-buttons.tsx @@ -1,4 +1,4 @@ -import { router } from 'expo-router'; +import { router, type Href } from 'expo-router'; import * as WebBrowser from 'expo-web-browser'; import { useState } from 'react'; import { useTranslation } from 'react-i18next'; @@ -63,7 +63,10 @@ export function SocialButtons() { setSignedIn(user); const pendingDeepLink = user.mustChangePassword ? null : consumePendingDeepLink(); - router.replace((pendingDeepLink ?? '/') as never); + // A captured deep link is an arbitrary runtime string, not a route this app's typed-routes + // manifest can know statically — `Href` (rather than `as never`) is expo-router's own type + // for exactly this "any valid path string" case. + router.replace((pendingDeepLink ?? '/') as Href); } catch (err) { setError(err instanceof Error ? err.message : t('errors.unknown')); } finally { diff --git a/src/features/auth/use-sign-out.ts b/src/features/auth/use-sign-out.ts new file mode 100644 index 0000000..5d9622a --- /dev/null +++ b/src/features/auth/use-sign-out.ts @@ -0,0 +1,38 @@ +import { Alert } from 'react-native'; +import { useTranslation } from 'react-i18next'; + +import { signOut as signOutRequest } from '@/api/auth-endpoints'; +import { clearAuthToken, getAuthToken } from '@/api/auth-token'; +import { queryClient } from '@/lib/query-client'; +import { useAuthStore } from '@/stores/use-auth-store'; +import { useWorkspaceStore } from '@/stores/use-workspace-store'; + +/** Single source of truth for signing out, replacing the two near-identical blocks that used to + * live in profile.tsx (no confirmation, not destructive) and settings/index.tsx (confirmed, + * destructive). Both call sites now get the same confirm-then-clear-everything behavior: + * best-effort server-side session revoke, clear the stored token, drop every cached query (it's + * all workspace/session-scoped), clear the selected workspace, then flip the auth store to + * signed-out. */ +export function useSignOut() { + const { t } = useTranslation(); + const setSignedOut = useAuthStore((state) => state.setSignedOut); + const setCurrentWorkspaceId = useWorkspaceStore((state) => state.setCurrentWorkspaceId); + + async function performSignOut() { + const token = await getAuthToken(); + if (token) { + await signOutRequest(token).catch(() => {}); + } + await clearAuthToken(); + queryClient.clear(); + setCurrentWorkspaceId(null); + setSignedOut(); + } + + return function signOut() { + Alert.alert(t('auth.signOutConfirmTitle'), t('auth.signOutConfirmBody'), [ + { text: t('common.cancel'), style: 'cancel' }, + { text: t('auth.signOut'), style: 'destructive', onPress: () => void performSignOut() }, + ]); + }; +} diff --git a/src/features/chat/api/send-message-multipart.ts b/src/features/chat/api/send-message-multipart.ts index 77c0eee..d8fa66a 100644 --- a/src/features/chat/api/send-message-multipart.ts +++ b/src/features/chat/api/send-message-multipart.ts @@ -1,4 +1,5 @@ import { getAuthToken } from '@/api/auth-token'; +import { ApiError } from '@/api/errors'; import { env } from '@/config/env'; /** @@ -46,18 +47,6 @@ export interface SendMultipartMessageParams { onProgress?: (ratio: number) => void; } -export class SendMessageError extends Error { - readonly status: number; - readonly body: unknown; - - constructor(status: number, body: unknown, message: string) { - super(message); - this.name = 'SendMessageError'; - this.status = status; - this.body = body; - } -} - function parseResponseBody(rawBody: string): unknown { if (!rawBody) return null; try { @@ -68,11 +57,20 @@ function parseResponseBody(rawBody: string): unknown { } } -function extractMessage(body: unknown, fallback: string): string { - if (body && typeof body === 'object' && 'message' in body && typeof body.message === 'string') { - return body.message; - } - return fallback; +/** Builds an `ApiError` from a raw XHR response body, matching the shape + * `normalizeApiError`/`ApiError` expect from the typed `apiClient` path (`{ code, status, + * message }`) — so a 402/401/etc. from an attachment send is classified identically to a + * JSON-body send instead of silently falling through as an unrecognized error type. */ +function toApiError(status: number, body: unknown, fallbackMessage: string): ApiError { + const parsedBody = body && typeof body === 'object' ? (body as Record<string, unknown>) : {}; + const code = typeof parsedBody.code === 'string' ? parsedBody.code : undefined; + const message = typeof parsedBody.message === 'string' ? parsedBody.message : fallbackMessage; + const data = + parsedBody.data && typeof parsedBody.data === 'object' + ? (parsedBody.data as { reason?: string }) + : undefined; + + return new ApiError({ code, status, message, data }); } export async function sendMultipartMessage(params: SendMultipartMessageParams): Promise<unknown> { @@ -116,18 +114,18 @@ export async function sendMultipartMessage(params: SendMultipartMessageParams): const body = parseResponseBody(xhr.responseText); const ok = xhr.status >= 200 && xhr.status < 300; if (!ok) { - reject(new SendMessageError(xhr.status, body, extractMessage(body, xhr.statusText))); + reject(toApiError(xhr.status, body, xhr.statusText)); return; } resolve(body); }; xhr.onerror = () => { - reject(new SendMessageError(0, null, 'Network request failed')); + reject(toApiError(0, null, 'Network request failed')); }; xhr.onabort = () => { - reject(new SendMessageError(0, null, 'Upload aborted')); + reject(toApiError(0, null, 'Upload aborted')); }; xhr.send(form); diff --git a/src/features/chat/api/use-message-actions.test.tsx b/src/features/chat/api/use-message-actions.test.tsx new file mode 100644 index 0000000..3fe612a --- /dev/null +++ b/src/features/chat/api/use-message-actions.test.tsx @@ -0,0 +1,167 @@ +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { renderHook, waitFor } from '@testing-library/react-native'; +import { act } from 'react'; +import type { PropsWithChildren } from 'react'; + +import { apiClient } from '@/api/client'; +import { queryKeys } from '@/api/query-keys'; +import { useDeleteMessage, useSetMessageAttributes } from '@/features/chat/api/use-message-actions'; +import type { Message } from '@/features/chat/api/use-messages-infinite'; + +jest.mock('@/api/client', () => ({ + apiClient: { + DELETE: jest.fn(), + POST: jest.fn(), + PATCH: jest.fn(), + }, +})); + +const mockedDelete = apiClient.DELETE as jest.Mock; +const mockedPost = apiClient.POST as jest.Mock; + +const activeQueryClients: QueryClient[] = []; + +function createTestQueryClient(): QueryClient { + const queryClient = new QueryClient(); + activeQueryClients.push(queryClient); + return queryClient; +} + +afterEach(() => { + mockedDelete.mockReset(); + mockedPost.mockReset(); + while (activeQueryClients.length > 0) { + const queryClient = activeQueryClients.pop()!; + queryClient.clear(); + queryClient.unmount(); + } +}); + +function fakeMessage(overrides: Partial<Message> = {}): Message { + return { + id: 'msg-1', + clientId: null, + createdAt: '2026-01-01T00:00:00.000Z', + updatedAt: '2026-01-01T00:00:00.000Z', + conversationId: 'conv-1', + workspaceId: 'ws-1', + contactInboxId: '', + text: 'hello', + contentAttributes: null, + messageType: 'outgoing', + contentType: 'text', + senderType: 'user', + sourceId: null, + deletedAt: null, + type: 'message', + parentId: null, + attributes: null, + sendError: null, + attachments: [], + ...overrides, + } as unknown as Message; +} + +function wrapper(queryClient: QueryClient) { + return function Wrapper({ children }: PropsWithChildren) { + return <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>; + }; +} + +function messagesData(messages: Message[]) { + return { pages: [{ data: messages, nextCursor: null }], pageParams: [undefined] }; +} + +describe('useDeleteMessage', () => { + it('optimistically sets deletedAt and rolls it back on error', async () => { + mockedDelete.mockResolvedValue({ data: undefined, error: { message: 'boom' } }); + const queryClient = createTestQueryClient(); + const queryKey = queryKeys.ws.messages.list('ws-1', 'conv-1'); + queryClient.setQueryData(queryKey, messagesData([fakeMessage({ deletedAt: null })])); + + const { result } = await renderHook(() => useDeleteMessage('ws-1', 'conv-1'), { + wrapper: wrapper(queryClient), + }); + + act(() => { + result.current.mutate({ messageId: 'msg-1', createdAt: '2026-01-01T00:00:00.000Z' }); + }); + + // Not asserting the transient optimistic (non-null deletedAt) state here: `onMutate` now + // awaits `cancelQueries` before patching, so by the time a `waitFor` poll could observe it, + // the mutation may have already settled and rolled back — a real race, not a test timing + // nuance. The end-to-end contract that matters is covered below: the row must NOT be left + // permanently deleted after an error. + await waitFor(() => expect(result.current.isError).toBe(true)); + + const rolledBack = queryClient.getQueryData<ReturnType<typeof messagesData>>(queryKey); + expect(rolledBack?.pages[0]?.data[0]?.deletedAt).toBeNull(); + }); + + it('keeps deletedAt set and invalidates the list on success', async () => { + mockedDelete.mockResolvedValue({ data: undefined, error: undefined }); + const queryClient = createTestQueryClient(); + const queryKey = queryKeys.ws.messages.list('ws-1', 'conv-1'); + const invalidateSpy = jest.spyOn(queryClient, 'invalidateQueries'); + queryClient.setQueryData(queryKey, messagesData([fakeMessage({ deletedAt: null })])); + + const { result } = await renderHook(() => useDeleteMessage('ws-1', 'conv-1'), { + wrapper: wrapper(queryClient), + }); + + act(() => { + result.current.mutate({ messageId: 'msg-1', createdAt: '2026-01-01T00:00:00.000Z' }); + }); + + await waitFor(() => expect(result.current.isSuccess).toBe(true)); + expect(invalidateSpy).toHaveBeenCalledWith({ queryKey }); + }); +}); + +describe('useSetMessageAttributes', () => { + it('invalidates the messages list once the mutation settles', async () => { + mockedPost.mockResolvedValue({ data: undefined, error: undefined }); + const queryClient = createTestQueryClient(); + const queryKey = queryKeys.ws.messages.list('ws-1', 'conv-1'); + const invalidateSpy = jest.spyOn(queryClient, 'invalidateQueries'); + queryClient.setQueryData(queryKey, messagesData([fakeMessage()])); + + const { result } = await renderHook(() => useSetMessageAttributes('ws-1', 'conv-1'), { + wrapper: wrapper(queryClient), + }); + + act(() => { + result.current.mutate({ + messageId: 'msg-1', + createdAt: '2026-01-01T00:00:00.000Z', + liked: true, + }); + }); + + await waitFor(() => expect(result.current.isSuccess).toBe(true)); + expect(invalidateSpy).toHaveBeenCalledWith({ queryKey }); + }); + + it('invalidates the messages list even when the mutation fails', async () => { + mockedPost.mockResolvedValue({ data: undefined, error: { message: 'boom' } }); + const queryClient = createTestQueryClient(); + const queryKey = queryKeys.ws.messages.list('ws-1', 'conv-1'); + const invalidateSpy = jest.spyOn(queryClient, 'invalidateQueries'); + queryClient.setQueryData(queryKey, messagesData([fakeMessage()])); + + const { result } = await renderHook(() => useSetMessageAttributes('ws-1', 'conv-1'), { + wrapper: wrapper(queryClient), + }); + + act(() => { + result.current.mutate({ + messageId: 'msg-1', + createdAt: '2026-01-01T00:00:00.000Z', + hidden: true, + }); + }); + + await waitFor(() => expect(result.current.isError).toBe(true)); + expect(invalidateSpy).toHaveBeenCalledWith({ queryKey }); + }); +}); diff --git a/src/features/chat/api/use-message-actions.ts b/src/features/chat/api/use-message-actions.ts index b756dc3..fb85689 100644 --- a/src/features/chat/api/use-message-actions.ts +++ b/src/features/chat/api/use-message-actions.ts @@ -71,7 +71,8 @@ export function useEditMessage(workspaceId: string, conversationId: string) { ); if (error) throw new ApiError(error); }, - onMutate: (params) => { + onMutate: async (params) => { + await queryClient.cancelQueries({ queryKey }); const previous = findCachedMessage(queryClient, queryKey, params.messageId); patchMessageInCache(queryClient, queryKey, params.messageId, { text: params.newText }); return { previous }; @@ -80,6 +81,9 @@ export function useEditMessage(workspaceId: string, conversationId: string) { if (!context?.previous) return; patchMessageInCache(queryClient, queryKey, params.messageId, { text: context.previous.text }); }, + onSettled: () => { + queryClient.invalidateQueries({ queryKey }); + }, }); } @@ -98,17 +102,33 @@ export function useDeleteMessage(workspaceId: string, conversationId: string) { ); if (error) throw new ApiError(error); }, - onMutate: (params) => + onMutate: async (params) => { + await queryClient.cancelQueries({ queryKey }); + const previous = findCachedMessage(queryClient, queryKey, params.messageId); patchMessageInCache(queryClient, queryKey, params.messageId, { deletedAt: new Date().toISOString(), - }), + }); + return { previous }; + }, + onError: (_error, params, context) => { + if (!context?.previous) return; + patchMessageInCache(queryClient, queryKey, params.messageId, { + deletedAt: context.previous.deletedAt, + }); + }, + onSettled: () => { + queryClient.invalidateQueries({ queryKey }); + }, }); } /** No optimistic cache patch here (unlike edit/delete above) — `attributes` is a loose JSON-value * union in the generated schema with no reliable current-liked/hidden shape to read or merge into, - * so this just fires the mutation and lets the next list refetch reflect the change. */ + * so this just fires the mutation and lets `onSettled` invalidation reflect the change. */ export function useSetMessageAttributes(workspaceId: string, conversationId: string) { + const queryClient = useQueryClient(); + const queryKey = queryKeys.ws.messages.list(workspaceId, conversationId); + return useMutation({ mutationFn: async (params: { messageId: string; @@ -125,5 +145,8 @@ export function useSetMessageAttributes(workspaceId: string, conversationId: str ); if (error) throw new ApiError(error); }, + onSettled: () => { + queryClient.invalidateQueries({ queryKey }); + }, }); } diff --git a/src/features/chat/api/use-messages-infinite.ts b/src/features/chat/api/use-messages-infinite.ts index af8c4a3..e329674 100644 --- a/src/features/chat/api/use-messages-infinite.ts +++ b/src/features/chat/api/use-messages-infinite.ts @@ -2,7 +2,7 @@ import { useInfiniteQuery } from '@tanstack/react-query'; import { apiClient } from '@/api/client'; import { ApiError } from '@/api/errors'; -import { flattenPages, getNextPageParam } from '@/api/pagination'; +import { getNextPageParam } from '@/api/pagination'; import { queryKeys } from '@/api/query-keys'; import type { operations } from '@/api/generated/schema'; @@ -42,7 +42,3 @@ export function useMessagesInfinite(workspaceId: string | null, conversationId: getNextPageParam, }); } - -export function flattenMessagePages(pages: ListMessagesResponse[] | undefined): Message[] { - return flattenPages(pages); -} diff --git a/src/features/chat/api/use-send-message.test.tsx b/src/features/chat/api/use-send-message.test.tsx index 1ccdc07..bd12912 100644 --- a/src/features/chat/api/use-send-message.test.tsx +++ b/src/features/chat/api/use-send-message.test.tsx @@ -6,10 +6,7 @@ import type { PropsWithChildren } from 'react'; import { apiClient } from '@/api/client'; import { queryKeys } from '@/api/query-keys'; import { useChatStore } from '@/features/chat/stores/use-chat-store'; -import { - createOptimisticMessage, - useSendMessage, -} from '@/features/chat/api/use-send-message'; +import { createOptimisticMessage, useSendMessage } from '@/features/chat/api/use-send-message'; jest.mock('@/api/client', () => ({ apiClient: { @@ -193,10 +190,14 @@ describe('useSendMessage', () => { }); }); - const data = queryClient.getQueryData<{ - pages: { data: { parentId: string | null }[] }[]; - }>(queryKey); - expect(data?.pages[0]?.data[0]?.parentId).toBe('parent-1'); + // `onMutate` awaits `cancelQueries` before patching, so the optimistic entry lands a + // microtask after `mutate()` returns rather than perfectly synchronously — wait for it. + await waitFor(() => { + const data = queryClient.getQueryData<{ + pages: { data: { parentId: string | null }[] }[]; + }>(queryKey); + expect(data?.pages[0]?.data[0]?.parentId).toBe('parent-1'); + }); await waitFor(() => expect(result.current.isSuccess).toBe(true)); expect(mockedPost).toHaveBeenCalledWith( @@ -260,4 +261,39 @@ describe('useSendMessage', () => { expect(data?.pages[0]?.data[0]?.__optimisticStatus).toBe('failed'); expect(useChatStore.getState().draftsByConversation['conv-1']).toBeUndefined(); }); + + it('sends the same clientId in the request body as the cached optimistic row', async () => { + // Regression: `mutationFn` and `onMutate` used to each call `generateClientId()` + // independently, so the optimistic bubble and the outgoing request carried different ids — + // producing a duplicate bubble until refetch and an orphaned upload-progress entry. + mockedPost.mockResolvedValue({ data: { id: 'server-1' }, error: undefined }); + const queryClient = createTestQueryClient(); + const queryKey = queryKeys.ws.messages.list('ws-1', 'conv-1'); + queryClient.setQueryData(queryKey, messagesData()); + + const { result } = await renderHook(() => useSendMessage('ws-1', 'conv-1'), { + wrapper: wrapper(queryClient), + }); + + act(() => { + result.current.mutate({ workspaceId: 'ws-1', conversationId: 'conv-1', text: 'hello' }); + }); + + // `onMutate` awaits `cancelQueries` before patching, so the optimistic entry lands a + // microtask after `mutate()` returns rather than perfectly synchronously — wait for it. + let cachedClientId: string | undefined; + await waitFor(() => { + const data = queryClient.getQueryData<{ pages: { data: { clientId?: string }[] }[] }>( + queryKey, + ); + cachedClientId = data?.pages[0]?.data[0]?.clientId; + expect(cachedClientId).toEqual(expect.any(String)); + }); + + await waitFor(() => expect(result.current.isSuccess).toBe(true)); + expect(mockedPost).toHaveBeenCalledWith( + '/workspaces/{workspaceId}/conversations/{conversationId}/messages', + expect.objectContaining({ body: expect.objectContaining({ clientId: cachedClientId }) }), + ); + }); }); diff --git a/src/features/chat/api/use-send-message.ts b/src/features/chat/api/use-send-message.ts index caa2017..1124403 100644 --- a/src/features/chat/api/use-send-message.ts +++ b/src/features/chat/api/use-send-message.ts @@ -24,7 +24,7 @@ interface SendMessageParams { replyTo?: { messageId: string; createdAt: string }; } -const MAX_FILE_SIZE_BYTES = 5 * 1000 * 1000; +export const MAX_FILE_SIZE_BYTES = 5 * 1000 * 1000; export class AttachmentTooLargeError extends Error { constructor(fileName: string) { @@ -156,10 +156,11 @@ export function useSendMessage(workspaceId: string, conversationId: string) { const setUploadProgress = useChatStore((state) => state.setUploadProgress); const clearUploadProgress = useChatStore((state) => state.clearUploadProgress); const setDraft = useChatStore((state) => state.setDraft); + const setJustSentClientId = useChatStore((state) => state.setJustSentClientId); - return useMutation({ - mutationFn: async (params: SendMessageParams) => { - const clientId = params.clientId ?? generateClientId(); + const mutation = useMutation({ + mutationFn: async (params: SendMessageParams & { clientId: string }) => { + const { clientId } = params; const attachments = params.attachments ?? []; if (attachments.length > 0) { @@ -197,7 +198,8 @@ export function useSendMessage(workspaceId: string, conversationId: string) { }, onMutate: async (params) => { - const clientId = params.clientId ?? generateClientId(); + const { clientId } = params; + await queryClient.cancelQueries({ queryKey }); // Flow sends produce N server-side messages (the flow's own steps), fanned out over // realtime — there is no single "the message that was sent" to preview optimistically, and @@ -246,11 +248,8 @@ export function useSendMessage(workspaceId: string, conversationId: string) { if (!context) return; clearUploadProgress(context.clientId); - if (context.skippedOptimistic) { - // Nothing was inserted for a flow send — still worth restoring the draft below on a - // blocked/unauthorized rejection so the user doesn't lose whatever they'd typed alongside - // the flow pick, but there's no cache entry to touch. - } + // context.skippedOptimistic (flow sends): nothing was inserted, so there's no cache entry + // to touch — but a blocked/unauthorized rejection below still restores the draft. const body = error instanceof ApiError ? error.body : null; const isTerminal = @@ -299,8 +298,29 @@ export function useSendMessage(workspaceId: string, conversationId: string) { // Flow sends (no optimistic entry) rely on this same invalidation/refetch (or realtime) to // surface the flow's resulting messages. queryClient.invalidateQueries({ queryKey }); + + // Recorded in the shared chat store (not local mutation state) so the chat screen's + // force-scroll-to-own-send logic sees it regardless of which `useSendMessage(...)` instance + // performed the send — the screen's own instance (retry only) and the composer's instance + // are separate mutations. Skipped for flow sends: there's no single bubble to scroll to. + if (context && !context.skippedOptimistic) { + setJustSentClientId(conversationId, context.clientId); + } }, }); -} -export { MAX_FILE_SIZE_BYTES }; + // Normalizes `clientId` once, before React Query hands params to `onMutate` and `mutationFn` + // independently — otherwise each side generates its own id and the optimistic bubble never + // matches the sent request (duplicate bubble, orphaned upload-progress entry, retry-scroll never + // fires). Callers that pass their own `clientId` (retry) are left untouched. + return { + ...mutation, + mutate: (params: SendMessageParams, options?: Parameters<typeof mutation.mutate>[1]) => + mutation.mutate({ ...params, clientId: params.clientId ?? generateClientId() }, options), + mutateAsync: ( + params: SendMessageParams, + options?: Parameters<typeof mutation.mutateAsync>[1], + ) => + mutation.mutateAsync({ ...params, clientId: params.clientId ?? generateClientId() }, options), + }; +} diff --git a/src/features/chat/components/attachment-view.tsx b/src/features/chat/components/attachment-view.tsx index d8e486c..a8335ca 100644 --- a/src/features/chat/components/attachment-view.tsx +++ b/src/features/chat/components/attachment-view.tsx @@ -78,7 +78,7 @@ function ImageAttachment({ <Pressable onPress={() => attachment.url && - router.push(`/image-viewer?url=${encodeURIComponent(attachment.url)}` as never) + router.push({ pathname: '/image-viewer', params: { url: attachment.url } }) } style={styles.imageWrap} > diff --git a/src/features/chat/components/chat-header.tsx b/src/features/chat/components/chat-header.tsx index d65f339..13e5b85 100644 --- a/src/features/chat/components/chat-header.tsx +++ b/src/features/chat/components/chat-header.tsx @@ -89,7 +89,12 @@ export function ChatHeader({ workspaceId, conversationId, onBack, onLayout }: Ch <Pressable style={styles.identity} accessibilityRole="button" - onPress={() => router.push(`/(app)/conversations/${conversationId}/contact` as never)} + onPress={() => + router.push({ + pathname: '/(app)/conversations/[conversationId]/contact', + params: { conversationId }, + }) + } > <Avatar uri={conversation?.contact?.avatar} diff --git a/src/features/chat/components/composer.tsx b/src/features/chat/components/composer.tsx index f40cbb7..f4c85ae 100644 --- a/src/features/chat/components/composer.tsx +++ b/src/features/chat/components/composer.tsx @@ -194,9 +194,9 @@ export function Composer({ workspaceId, conversationId, style }: ComposerProps) } } - async function handleCamera() { + function handleCamera() { actionsSheetRef.current?.close(); - router.push(`/camera?conversationId=${conversationId}` as never); + router.push({ pathname: '/camera', params: { conversationId } }); } async function handlePhotoLibrary() { @@ -210,7 +210,7 @@ export function Composer({ workspaceId, conversationId, style }: ComposerProps) actionsSheetRef.current?.close(); const assets = await pickDocuments(); if (assets.length === 0) return; - await sendAssets(assets.map((asset) => ({ ...asset, mimeType: asset.mimeType }))); + await sendAssets(assets); } function handleOpenFlowPicker() { diff --git a/src/features/chat/components/conversation-actions-sheet.tsx b/src/features/chat/components/conversation-actions-sheet.tsx index 94b95bf..82ae265 100644 --- a/src/features/chat/components/conversation-actions-sheet.tsx +++ b/src/features/chat/components/conversation-actions-sheet.tsx @@ -82,7 +82,10 @@ export const ConversationActionsSheet = forwardRef<BottomSheet, ConversationActi function handleViewContact() { onClose(); - router.push(`/(app)/conversations/${conversationId}/contact` as never); + router.push({ + pathname: '/(app)/conversations/[conversationId]/contact', + params: { conversationId }, + }); } return ( diff --git a/src/features/chat/lib/pick-attachments.ts b/src/features/chat/lib/pick-attachments.ts index 01a0854..3f2611c 100644 --- a/src/features/chat/lib/pick-attachments.ts +++ b/src/features/chat/lib/pick-attachments.ts @@ -3,7 +3,7 @@ import * as ImagePicker from 'expo-image-picker'; /** Cap on how many assets a single multi-select library pick can return — an arbitrary but * generous ceiling to keep one send from trying to upload an unbounded number of files. */ -export const MAX_MULTI_SELECT = 10; +const MAX_MULTI_SELECT = 10; export interface PickedAsset { uri: string; diff --git a/src/features/chat/stores/use-chat-store.ts b/src/features/chat/stores/use-chat-store.ts index d2d523d..ef1aef6 100644 --- a/src/features/chat/stores/use-chat-store.ts +++ b/src/features/chat/stores/use-chat-store.ts @@ -23,6 +23,14 @@ interface ChatStoreState { * `src/lib/notifications.ts`'s notification handler (outside React) to suppress the in-app * banner for a push notification about the conversation the user is already looking at. */ activeConversationId: string | null; + /** conversationId -> the clientId of the most recently successfully-sent message, set once by + * `useSendMessage`'s `onSuccess` (use-send-message.ts). Lives here — not as component state in + * the chat screen — because the screen's own `useSendMessage(...)` instance (used only for the + * retry path) is a SEPARATE mutation from the one `Composer` instantiates for normal sends; a + * `sendMessage.data` read in the screen would never see the composer's own sends. The chat + * screen reads this to force-scroll to a message the user just sent, even if they'd scrolled + * away — see MessageList's `justSentMessageId` prop. */ + justSentClientIdByConversation: Record<string, string | undefined>; /** `seconds` is the realtime `typing` event's TTL (falls back to a default in the caller when * 0/undefined) — kept optional so non-realtime callers (tests, other future call sites) aren't * forced to pass it. Not used by this store itself; use-realtime-handlers.ts reads it back out @@ -33,6 +41,7 @@ interface ChatStoreState { setUploadProgress: (clientId: string, ratio: number) => void; clearUploadProgress: (clientId: string) => void; setActiveConversationId: (conversationId: string | null) => void; + setJustSentClientId: (conversationId: string, clientId: string) => void; } export const useChatStore = create<ChatStoreState>((set) => ({ @@ -41,6 +50,7 @@ export const useChatStore = create<ChatStoreState>((set) => ({ composerModeByConversation: {}, uploadProgressByClientId: {}, activeConversationId: null, + justSentClientIdByConversation: {}, setTyping: (conversationId, typing) => set((state) => ({ typingByConversation: { ...state.typingByConversation, [conversationId]: typing }, @@ -63,4 +73,11 @@ export const useChatStore = create<ChatStoreState>((set) => ({ const { [clientId]: _removed, ...rest } = state.uploadProgressByClientId; return { uploadProgressByClientId: rest }; }), + setJustSentClientId: (conversationId, clientId) => + set((state) => ({ + justSentClientIdByConversation: { + ...state.justSentClientIdByConversation, + [conversationId]: clientId, + }, + })), })); diff --git a/src/features/contacts/info-tab.tsx b/src/features/contacts/info-tab.tsx index 238673f..1c2c6f3 100644 --- a/src/features/contacts/info-tab.tsx +++ b/src/features/contacts/info-tab.tsx @@ -124,7 +124,7 @@ export function InfoTab({ contact, workspaceId }: InfoTabProps) { ))} <ListItem title={t('contacts.addCustomField')} - trailing={<Icon name="add-circle-outline" size={20} color={colors.primary} />} + trailing={<Icon name="add-circle-outline" size={20} color={colors.brand} />} onPress={() => catalogSheetRef.current?.expand()} /> diff --git a/src/features/contacts/pii-mask.test.ts b/src/features/contacts/pii-mask.test.ts index b5def88..1b55f5a 100644 --- a/src/features/contacts/pii-mask.test.ts +++ b/src/features/contacts/pii-mask.test.ts @@ -12,11 +12,20 @@ describe('maskEmail', () => { it('pads short local parts to at least 3 asterisks', () => { expect(maskEmail('ab@example.com')).toBe('ab***@example.com'); }); + + it('returns *** for an empty local part', () => { + expect(maskEmail('@example.com')).toBe('***'); + }); + + it('splits on the last @ so a quoted local part containing @ is not mistaken for the domain', () => { + expect(maskEmail('"john@doe"@example.com')).toBe('"j********@example.com'); + }); }); describe('maskPhone', () => { - it('keeps only the last 2 digits visible', () => { - expect(maskPhone('+15551234567')).toBe('**********67'); + it('keeps only the last 2 digits visible, sized off the digit count not the raw string', () => { + // "+15551234567" has 11 digits — the asterisk count must reflect that, not the 12-char string. + expect(maskPhone('+15551234567')).toBe('*********67'); }); it('masks the whole string when there are 2 or fewer digits', () => { @@ -24,6 +33,7 @@ describe('maskPhone', () => { }); it('strips non-digit characters before counting the last 2 visible digits', () => { - expect(maskPhone('(555) 123-4567')).toBe('************67'); + // "(555) 123-4567" has 10 digits — 8 asterisks + the last 2, not sized off the 14-char string. + expect(maskPhone('(555) 123-4567')).toBe('********67'); }); }); diff --git a/src/features/contacts/pii-mask.ts b/src/features/contacts/pii-mask.ts index 66200fe..de15e57 100644 --- a/src/features/contacts/pii-mask.ts +++ b/src/features/contacts/pii-mask.ts @@ -1,15 +1,20 @@ /** Masks an email as `ab***@domain.com` — keeps the first 2 local-part chars and the domain so a - * restricted member can still recognize a contact without seeing the full address. */ + * restricted member can still recognize a contact without seeing the full address. Splits on the + * LAST `@` (not the first) since a quoted local-part can itself legally contain `@`. */ export function maskEmail(email: string): string { - const [local, domain] = email.split('@'); - if (!domain) return '***'; + const atIndex = email.lastIndexOf('@'); + if (atIndex <= 0) return '***'; + const local = email.slice(0, atIndex); + const domain = email.slice(atIndex + 1); const visible = local.slice(0, 2); return `${visible}${'*'.repeat(Math.max(local.length - visible.length, 3))}@${domain}`; } -/** Masks a phone number, keeping only the last 2 digits visible. */ +/** Masks a phone number, keeping only the last 2 digits visible. Asterisk count is derived from + * `digits` (not the raw string) so formatting characters (`-`, spaces, parens) don't inflate the + * mask length beyond the actual digit count. */ export function maskPhone(phone: string): string { const digits = phone.replace(/\D/g, ''); - if (digits.length <= 2) return '*'.repeat(phone.length); - return `${'*'.repeat(phone.length - 2)}${digits.slice(-2)}`; + if (digits.length <= 2) return '*'.repeat(digits.length); + return `${'*'.repeat(digits.length - 2)}${digits.slice(-2)}`; } diff --git a/src/features/contacts/sequences-tab.tsx b/src/features/contacts/sequences-tab.tsx index dd06698..655b1d6 100644 --- a/src/features/contacts/sequences-tab.tsx +++ b/src/features/contacts/sequences-tab.tsx @@ -50,7 +50,7 @@ export function SequencesTab({ contact, workspaceId }: SequencesTabProps) { title={enrollment.sequence.name} subtitle={t('contacts.sequenceStep', { step: enrollment.currentStep })} trailing={ - <Badge color={enrollment.completedAt ? colors.success : colors.surface}> + <Badge color={enrollment.completedAt ? colors.success : colors.surface2}> {enrollment.completedAt ? t('contacts.completed') : (enrollment.status ?? t('contacts.active'))} diff --git a/src/features/contacts/tags-tab.tsx b/src/features/contacts/tags-tab.tsx index be7894a..8cdd08b 100644 --- a/src/features/contacts/tags-tab.tsx +++ b/src/features/contacts/tags-tab.tsx @@ -63,7 +63,7 @@ export function TagsTab({ contact, workspaceId }: TagsTabProps) { <ListItem key={item.id} title={item.name} - trailing={<Icon name="add-circle-outline" size={20} color={colors.primary} />} + trailing={<Icon name="add-circle-outline" size={20} color={colors.brand} />} onPress={() => addTag.mutate(item)} /> ))} diff --git a/src/features/conversations/api/use-conversation-actions.ts b/src/features/conversations/api/use-conversation-actions.ts index 1a0b38e..34f02e3 100644 --- a/src/features/conversations/api/use-conversation-actions.ts +++ b/src/features/conversations/api/use-conversation-actions.ts @@ -75,6 +75,21 @@ function rollback( }); } +/** Cancels every in-flight conversations-list query for this workspace (any filter combination) + * before an optimistic patch runs, so a refetch that resolves mid-mutation can't clobber the + * patch with stale server data. */ +function cancelConversationQueries( + queryClient: ReturnType<typeof useQueryClient>, + workspaceId: string, +): Promise<void> { + return queryClient.cancelQueries({ + predicate: (query) => + isWorkspaceQuery(query.queryKey, workspaceId) && + query.queryKey[2] === 'conversations' && + query.queryKey[3] === 'list', + }); +} + /** The conversation-detail query (`useConversationDetail`) caches a single object under * `queryKeys.ws.conversations.detail(...)` — a different shape from the paginated list queries * above (`{ pages, pageParams }`). Every list-cache mutation here should also patch the detail @@ -105,10 +120,6 @@ function rollbackDetail( queryClient.setQueryData(snapshot.key, snapshot.previous); } -interface ActionContext { - workspaceId: string; -} - interface SingleActionSnapshot { list: Map<readonly unknown[], InfiniteData | undefined>; detail: { key: readonly unknown[]; previous: ConversationListItem | undefined }; @@ -147,10 +158,12 @@ export function useMarkConversationRead(workspaceId: string) { }); if (error) throw new ApiError(error); }, - onMutate: (conversationId) => - patchSingleConversation(queryClient, workspaceId, conversationId, { + onMutate: async (conversationId) => { + await cancelConversationQueries(queryClient, workspaceId); + return patchSingleConversation(queryClient, workspaceId, conversationId, { agentLastReadAt: new Date().toISOString(), - }), + }); + }, onError: (_err, _vars, snapshot) => rollbackSingle(queryClient, snapshot), }); } @@ -168,8 +181,12 @@ export function useMarkConversationUnread(workspaceId: string) { ); if (error) throw new ApiError(error); }, - onMutate: (conversationId) => - patchSingleConversation(queryClient, workspaceId, conversationId, { agentLastReadAt: null }), + onMutate: async (conversationId) => { + await cancelConversationQueries(queryClient, workspaceId); + return patchSingleConversation(queryClient, workspaceId, conversationId, { + agentLastReadAt: null, + }); + }, onError: (_err, _vars, snapshot) => rollbackSingle(queryClient, snapshot), }); } @@ -187,8 +204,10 @@ export function useFollowConversation(workspaceId: string) { ); if (error) throw new ApiError(error); }, - onMutate: (conversationId) => - patchSingleConversation(queryClient, workspaceId, conversationId, { followed: true }), + onMutate: async (conversationId) => { + await cancelConversationQueries(queryClient, workspaceId); + return patchSingleConversation(queryClient, workspaceId, conversationId, { followed: true }); + }, onError: (_err, _vars, snapshot) => rollbackSingle(queryClient, snapshot), }); } @@ -206,8 +225,12 @@ export function useUnfollowConversation(workspaceId: string) { ); if (error) throw new ApiError(error); }, - onMutate: (conversationId) => - patchSingleConversation(queryClient, workspaceId, conversationId, { followed: false }), + onMutate: async (conversationId) => { + await cancelConversationQueries(queryClient, workspaceId); + return patchSingleConversation(queryClient, workspaceId, conversationId, { + followed: false, + }); + }, onError: (_err, _vars, snapshot) => rollbackSingle(queryClient, snapshot), }); } @@ -226,7 +249,7 @@ function useBatchConversationAction( | '/workspaces/{workspaceId}/conversations/unarchive' | '/workspaces/{workspaceId}/conversations/enable-bot' | '/workspaces/{workspaceId}/conversations/disable-bot', - patch: Partial<ConversationListItem>, + buildPatch: () => Partial<ConversationListItem>, onSuccessIds?: (queryClient: ReturnType<typeof useQueryClient>, ids: string[]) => void, ) { const queryClient = useQueryClient(); @@ -239,8 +262,11 @@ function useBatchConversationAction( }); if (error) throw new ApiError(error); }, - onMutate: (ids: string[]) => - ids.map((id) => patchSingleConversation(queryClient, workspaceId, id, patch)), + onMutate: async (ids: string[]) => { + await cancelConversationQueries(queryClient, workspaceId); + const patch = buildPatch(); + return ids.map((id) => patchSingleConversation(queryClient, workspaceId, id, patch)); + }, onError: (_err, _vars, snapshots) => { snapshots?.forEach((snapshot) => rollbackSingle(queryClient, snapshot)); }, @@ -252,9 +278,7 @@ export function useArchiveConversations(workspaceId: string) { return useBatchConversationAction( workspaceId, '/workspaces/{workspaceId}/conversations/archive', - { - archivedAt: new Date().toISOString(), - }, + () => ({ archivedAt: new Date().toISOString() }), ); } @@ -262,9 +286,7 @@ export function useUnarchiveConversations(workspaceId: string) { return useBatchConversationAction( workspaceId, '/workspaces/{workspaceId}/conversations/unarchive', - { - archivedAt: null, - }, + () => ({ archivedAt: null }), ); } @@ -272,9 +294,7 @@ export function useEnableBot(workspaceId: string) { return useBatchConversationAction( workspaceId, '/workspaces/{workspaceId}/conversations/enable-bot', - { - botEnabled: true, - }, + () => ({ botEnabled: true }), ); } @@ -289,9 +309,7 @@ export function useDisableBot(workspaceId: string) { return useBatchConversationAction( workspaceId, '/workspaces/{workspaceId}/conversations/disable-bot', - { - botEnabled: false, - }, + () => ({ botEnabled: false }), (queryClient, ids) => { ids.forEach((id) => { queryClient.invalidateQueries({ @@ -362,10 +380,10 @@ export function useAssignConversations(workspaceId: string) { }); if (error) throw new ApiError(error); }, - onMutate: ({ contactIds, assignedId }) => - patchAssignedUserByContact(queryClient, workspaceId, contactIds, assignedId), + onMutate: async ({ contactIds, assignedId }) => { + await cancelConversationQueries(queryClient, workspaceId); + return patchAssignedUserByContact(queryClient, workspaceId, contactIds, assignedId); + }, onError: (_err, _vars, snapshot) => snapshot && rollback(queryClient, snapshot), }); } - -export type { ActionContext }; diff --git a/src/features/conversations/api/use-conversations-infinite.ts b/src/features/conversations/api/use-conversations-infinite.ts index a15529e..4c9b270 100644 --- a/src/features/conversations/api/use-conversations-infinite.ts +++ b/src/features/conversations/api/use-conversations-infinite.ts @@ -4,7 +4,7 @@ import { useShallow } from 'zustand/react/shallow'; import { apiClient } from '@/api/client'; import { ApiError } from '@/api/errors'; -import { flattenPages, getNextPageParam } from '@/api/pagination'; +import { getNextPageParam } from '@/api/pagination'; import { queryKeys } from '@/api/query-keys'; import type { operations } from '@/api/generated/schema'; import { @@ -81,9 +81,3 @@ export function useConversationsInfinite(workspaceId: string | null) { getNextPageParam, }); } - -export function flattenConversationPages( - pages: ListConversationsResponse[] | undefined, -): ConversationListItem[] { - return flattenPages(pages); -} diff --git a/src/features/conversations/components/assignment-sheet.tsx b/src/features/conversations/components/assignment-sheet.tsx index 3bd6a48..f365990 100644 --- a/src/features/conversations/components/assignment-sheet.tsx +++ b/src/features/conversations/components/assignment-sheet.tsx @@ -1,6 +1,5 @@ import type BottomSheet from '@gorhom/bottom-sheet'; import { BottomSheetFlatList } from '@gorhom/bottom-sheet'; -import { useQuery } from '@tanstack/react-query'; import type { RefObject } from 'react'; import { useTranslation } from 'react-i18next'; @@ -8,24 +7,7 @@ import { Avatar } from '@/components/ui/avatar'; import { EmptyState } from '@/components/ui/empty-state'; import { ListItem } from '@/components/ui/list-item'; import { Sheet } from '@/components/ui/sheet'; -import { apiClient } from '@/api/client'; -import { ApiError } from '@/api/errors'; -import { queryKeys } from '@/api/query-keys'; - -/** `GET /workspaces/{workspaceId}/members` — confirmed live in the generated schema, so this is a - * real (not stubbed) member list, not the deferred Phase 8 members *management* screen. */ -function useWorkspaceMembers(workspaceId: string) { - return useQuery({ - queryKey: queryKeys.ws.members.list(workspaceId), - queryFn: async () => { - const { data, error } = await apiClient.GET('/workspaces/{workspaceId}/members', { - params: { path: { workspaceId } }, - }); - if (error) throw new ApiError(error); - return data.data; - }, - }); -} +import { useWorkspaceMembersList } from '@/features/permissions/use-permissions'; interface AssignmentSheetProps { sheetRef: RefObject<BottomSheet | null>; @@ -35,7 +17,7 @@ interface AssignmentSheetProps { export function AssignmentSheet({ sheetRef, workspaceId, onAssign }: AssignmentSheetProps) { const { t } = useTranslation(); - const { data: members } = useWorkspaceMembers(workspaceId); + const { data: members } = useWorkspaceMembersList(workspaceId); return ( <Sheet ref={sheetRef} snapPoints={['50%', '80%']}> diff --git a/src/features/conversations/components/channel-badge.tsx b/src/features/conversations/components/channel-badge.tsx index 2989a56..1897b96 100644 --- a/src/features/conversations/components/channel-badge.tsx +++ b/src/features/conversations/components/channel-badge.tsx @@ -32,7 +32,7 @@ export function ChannelBadge({ channel, size = 16 }: ChannelBadgeProps) { return ( <View style={[styles.container, { backgroundColor: color, width: size + 6, height: size + 6 }]}> - <Icon name={icon} size={size - 4} color={colors.primaryForeground} /> + <Icon name={icon} size={size - 4} color={colors.onBrand} /> </View> ); } diff --git a/src/features/conversations/components/filter-sheet.tsx b/src/features/conversations/components/filter-sheet.tsx index 716b7cd..937b7c1 100644 --- a/src/features/conversations/components/filter-sheet.tsx +++ b/src/features/conversations/components/filter-sheet.tsx @@ -191,7 +191,7 @@ function StatusChip({ style={[ styles.statusChip, { - backgroundColor: active ? colors.primary : colors.surface, + backgroundColor: active ? colors.brand : colors.surface2, borderRadius: radius.full, paddingHorizontal: spacing.ms, }, diff --git a/src/features/conversations/components/tag-chips.tsx b/src/features/conversations/components/tag-chips.tsx index c2b7185..93aecc9 100644 --- a/src/features/conversations/components/tag-chips.tsx +++ b/src/features/conversations/components/tag-chips.tsx @@ -40,12 +40,10 @@ export function TagChips({ chips }: TagChipsProps) { <Badge key={chip} color={ - chip === 'blocked' ? colors.danger : chip === 'unread' ? colors.primary : colors.surface + chip === 'blocked' ? colors.danger : chip === 'unread' ? colors.brand : colors.surface2 } textColor={ - chip === 'blocked' || chip === 'unread' - ? colors.primaryForeground - : colors.textSecondary + chip === 'blocked' || chip === 'unread' ? colors.onBrand : colors.textSecondary } > {t(CHIP_LABEL_KEYS[chip])} diff --git a/src/features/permissions/use-permissions.ts b/src/features/permissions/use-permissions.ts index 99ab498..339aea9 100644 --- a/src/features/permissions/use-permissions.ts +++ b/src/features/permissions/use-permissions.ts @@ -67,6 +67,7 @@ export function useWorkspaceMembersList(workspaceId: string) { if (error) throw new ApiError(error); return data.data; }, + enabled: workspaceId.length > 0, }); } diff --git a/src/features/settings/api/use-device-token.ts b/src/features/settings/api/use-device-token.ts index cb7f67a..4959203 100644 --- a/src/features/settings/api/use-device-token.ts +++ b/src/features/settings/api/use-device-token.ts @@ -1,4 +1,4 @@ -import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { useMutation } from '@tanstack/react-query'; import * as Notifications from 'expo-notifications'; import * as SecureStore from 'expo-secure-store'; import { useEffect } from 'react'; @@ -70,20 +70,25 @@ export function useUnregisterDeviceToken() { * install. */ export function usePushTokenRotation(workspaceId: string, enabled: boolean): void { - const queryClient = useQueryClient(); - useEffect(() => { - if (!enabled) return; + if (!enabled) return undefined; + + let cancelled = false; const subscription = Notifications.addPushTokenListener(async ({ data: newToken }) => { const previousToken = await SecureStore.getItemAsync(LAST_REGISTERED_TOKEN_KEY); + if (cancelled) return; + if (previousToken && previousToken !== newToken) { await unregisterToken(previousToken).catch(() => {}); + if (cancelled) return; } await registerToken(workspaceId, newToken); - queryClient.invalidateQueries({ queryKey: ['device-token'] }); }); - return () => subscription.remove(); - }, [enabled, workspaceId, queryClient]); + return () => { + cancelled = true; + subscription.remove(); + }; + }, [enabled, workspaceId]); } diff --git a/src/features/settings/components/language-sheet.tsx b/src/features/settings/components/language-sheet.tsx index 6b7d312..c10df02 100644 --- a/src/features/settings/components/language-sheet.tsx +++ b/src/features/settings/components/language-sheet.tsx @@ -29,7 +29,7 @@ export function LanguageSheet({ sheetRef, currentLanguage }: LanguageSheetProps) trailing={ language === currentLanguage ? ( <View> - <Icon name="checkmark" size={20} color={colors.primary} /> + <Icon name="checkmark" size={20} color={colors.brand} /> </View> ) : undefined } diff --git a/src/features/workspaces/components/workspace-pill.tsx b/src/features/workspaces/components/workspace-pill.tsx index 43641ee..336c083 100644 --- a/src/features/workspaces/components/workspace-pill.tsx +++ b/src/features/workspaces/components/workspace-pill.tsx @@ -25,7 +25,7 @@ export function WorkspacePill({ name, logo, onPress }: WorkspacePillProps) { style={({ pressed }) => [ styles.pill, { - backgroundColor: colors.surface, + backgroundColor: colors.surface2, borderRadius: radius.full, paddingVertical: spacing.xs, paddingHorizontal: spacing.ms, diff --git a/src/hooks/use-color-scheme.ts b/src/hooks/use-color-scheme.ts deleted file mode 100644 index 17e3c63..0000000 --- a/src/hooks/use-color-scheme.ts +++ /dev/null @@ -1 +0,0 @@ -export { useColorScheme } from 'react-native'; diff --git a/src/hooks/use-color-scheme.web.ts b/src/hooks/use-color-scheme.web.ts deleted file mode 100644 index 4843764..0000000 --- a/src/hooks/use-color-scheme.web.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { useSyncExternalStore } from 'react'; -import { useColorScheme as useRNColorScheme } from 'react-native'; - -const subscribeNoop = () => () => undefined; - -/** - * To support static rendering, this value needs to be re-calculated on the client side for web. - * useSyncExternalStore's getServerSnapshot returns a fixed value during SSR/hydration, then the - * client snapshot takes over post-hydration without triggering the cascading-render pattern that - * an effect-based setState would. - */ -function useHasHydrated() { - return useSyncExternalStore( - subscribeNoop, - () => true, - () => false, - ); -} - -export function useColorScheme() { - const hasHydrated = useHasHydrated(); - const colorScheme = useRNColorScheme(); - - if (hasHydrated) { - return colorScheme; - } - - return 'light'; -} diff --git a/src/i18n/apply-language.ts b/src/i18n/apply-language.ts index b8ec168..553d70a 100644 --- a/src/i18n/apply-language.ts +++ b/src/i18n/apply-language.ts @@ -21,18 +21,22 @@ export async function applyLanguage(language: SupportedLanguage): Promise<void> * app was built before the `expo-updates` package/plugin were added (needs a fresh build to pick * up the native module), independent of whether EAS Update hosting is configured. In that case * there is no reload mechanism available at all; the RTL/language change still takes effect on - * the *next* natural app relaunch, so we surface an alert rather than silently doing nothing. + * the *next* natural app relaunch. `silent: true` (the bootstrap path, before anything has + * rendered) falls back to that quietly instead of alerting — there's no user-initiated action to + * report back on yet. */ -export async function reloadApp(): Promise<void> { +export async function reloadApp(options?: { silent?: boolean }): Promise<void> { if (__DEV__) { DevSettings.reload(); return; } if (!Updates.isEnabled) { - Alert.alert( - i18next.t('settings.restartRequiredTitle'), - i18next.t('settings.restartManuallyMessage'), - ); + if (!options?.silent) { + Alert.alert( + i18next.t('settings.restartRequiredTitle'), + i18next.t('settings.restartManuallyMessage'), + ); + } return; } await Updates.reloadAsync(); diff --git a/src/i18n/dayjs-locales.ts b/src/i18n/dayjs-locales.ts index e692c47..cd27ef8 100644 --- a/src/i18n/dayjs-locales.ts +++ b/src/i18n/dayjs-locales.ts @@ -1,23 +1,3 @@ -import 'dayjs/locale/ar'; -import 'dayjs/locale/da'; -import 'dayjs/locale/de'; -import 'dayjs/locale/en'; -import 'dayjs/locale/es'; -import 'dayjs/locale/fi'; -import 'dayjs/locale/fr'; -import 'dayjs/locale/he'; -import 'dayjs/locale/id'; -import 'dayjs/locale/it'; -import 'dayjs/locale/ja'; -import 'dayjs/locale/nl'; -import 'dayjs/locale/pt-br'; -import 'dayjs/locale/pt'; -import 'dayjs/locale/ro'; -import 'dayjs/locale/sv'; -import 'dayjs/locale/tr'; -import 'dayjs/locale/vi'; -import 'dayjs/locale/zh-cn'; -import 'dayjs/locale/zh-tw'; import dayjs from 'dayjs'; import type { SupportedLanguage } from '@/i18n/locales'; @@ -49,6 +29,50 @@ const dayjsLocaleNames: Record<SupportedLanguage, string> = { 'zh-TW': 'zh-tw', }; +// Metro (unlike webpack) needs a STATIC, literal string argument to `require()` to resolve and +// bundle a module — a template-literal path like `require(\`dayjs/locale/${name}\`)` can't be +// analyzed at bundle time, so each locale needs its own literal `require()` call here rather than +// a single dynamically-built one. Still lazy: nothing runs until `setDayjsLocale` actually picks +// this branch, so a session only pays for the locale(s) it uses instead of importing all 20 at +// module load. +/* eslint-disable @typescript-eslint/no-require-imports -- intentionally lazy per-locale requires, see above */ +const dayjsLocaleLoaders: Record<string, () => void> = { + ar: () => require('dayjs/locale/ar'), + da: () => require('dayjs/locale/da'), + de: () => require('dayjs/locale/de'), + en: () => require('dayjs/locale/en'), + es: () => require('dayjs/locale/es'), + fi: () => require('dayjs/locale/fi'), + fr: () => require('dayjs/locale/fr'), + he: () => require('dayjs/locale/he'), + id: () => require('dayjs/locale/id'), + it: () => require('dayjs/locale/it'), + ja: () => require('dayjs/locale/ja'), + nl: () => require('dayjs/locale/nl'), + 'pt-br': () => require('dayjs/locale/pt-br'), + pt: () => require('dayjs/locale/pt'), + ro: () => require('dayjs/locale/ro'), + sv: () => require('dayjs/locale/sv'), + tr: () => require('dayjs/locale/tr'), + vi: () => require('dayjs/locale/vi'), + 'zh-cn': () => require('dayjs/locale/zh-cn'), + 'zh-tw': () => require('dayjs/locale/zh-tw'), +}; +/* eslint-enable @typescript-eslint/no-require-imports */ + +// Tracks which locale modules have already been loaded — dayjs locale side-effect modules are +// idempotent to re-require, but there's no reason to re-run the loader for a locale already +// loaded this session. +const loadedLocales = new Set<string>(); + +function ensureDayjsLocaleLoaded(dayjsLocaleName: string): void { + if (loadedLocales.has(dayjsLocaleName)) return; + loadedLocales.add(dayjsLocaleName); + dayjsLocaleLoaders[dayjsLocaleName]?.(); +} + export function setDayjsLocale(language: SupportedLanguage): void { - dayjs.locale(dayjsLocaleNames[language]); + const dayjsLocaleName = dayjsLocaleNames[language]; + ensureDayjsLocaleLoaded(dayjsLocaleName); + dayjs.locale(dayjsLocaleName); } diff --git a/src/i18n/reconcile-rtl.ts b/src/i18n/reconcile-rtl.ts index 9c746eb..0bcbf91 100644 --- a/src/i18n/reconcile-rtl.ts +++ b/src/i18n/reconcile-rtl.ts @@ -22,9 +22,6 @@ export async function reconcileRTLOnLaunch(language: SupportedLanguage): Promise return; } - I18nManager.allowRTL(true); - I18nManager.forceRTL(targetIsRTL); - if (__DEV__) { console.warn( '[i18n] RTL direction mismatch detected in dev — forceRTL may not persist in Expo Go. ' + @@ -33,5 +30,8 @@ export async function reconcileRTLOnLaunch(language: SupportedLanguage): Promise return; } - await reloadApp(); + I18nManager.allowRTL(true); + I18nManager.forceRTL(targetIsRTL); + + await reloadApp({ silent: true }); } diff --git a/src/lib/notification-tap.ts b/src/lib/notification-tap.ts index 5346b94..c697353 100644 --- a/src/lib/notification-tap.ts +++ b/src/lib/notification-tap.ts @@ -22,7 +22,10 @@ function navigateToNotification(data: NotificationTapData): void { useWorkspaceStore.getState().setCurrentWorkspaceId(data.workspaceId); } - router.push(`/(app)/conversations/${data.conversationId}` as never); + router.push({ + pathname: '/(app)/conversations/[conversationId]', + params: { conversationId: data.conversationId }, + }); } /** Handles a notification tap once the user is authenticated. If auth is still pending (tapped @@ -41,10 +44,20 @@ function handleResponse(response: Notifications.NotificationResponse): void { * result stays available until consumed, so a short delay here doesn't lose it. */ export function initNotificationTapHandling(): () => void { - Notifications.getLastNotificationResponseAsync().then((response) => { - if (response) handleResponse(response); - }); + let cancelled = false; + + Notifications.getLastNotificationResponseAsync() + .then((response) => { + if (!cancelled && response) handleResponse(response); + }) + .catch(() => { + // Best-effort cold-start check — a failure here just means the cold-start tap (if any) is + // missed; the warm/background listener below still covers every subsequent tap. + }); const subscription = Notifications.addNotificationResponseReceivedListener(handleResponse); - return () => subscription.remove(); + return () => { + cancelled = true; + subscription.remove(); + }; } diff --git a/src/lib/pending-deep-link.test.ts b/src/lib/pending-deep-link.test.ts new file mode 100644 index 0000000..efde8f5 --- /dev/null +++ b/src/lib/pending-deep-link.test.ts @@ -0,0 +1,80 @@ +// `capturePendingDeepLink` is guarded by a module-scope `captured` flag (only the FIRST call +// across the whole process does anything — see the module's own doc comment) — so each case here +// needs a genuinely fresh module instance to test capture in isolation. `jest.resetModules()` + +// `jest.doMock` + a scoped `require()` (not a top-level `import`) gives each case its own +// instance of both `pending-deep-link.ts` AND the `expo-linking` mock it imports — a plain +// `jest.mock` at the top of the file doesn't survive `resetModules()` for the freshly-required +// module's own import of `expo-linking`. +function freshCapture(url: string | undefined, path: string): string | null { + jest.resetModules(); + jest.doMock('expo-linking', () => ({ + getLinkingURL: jest.fn().mockReturnValue(url), + parse: jest.fn().mockReturnValue({ path }), + })); + // eslint-disable-next-line @typescript-eslint/no-require-imports -- deliberate fresh re-require for module-state isolation + const fresh: typeof import('@/lib/pending-deep-link') = require('@/lib/pending-deep-link'); + fresh.capturePendingDeepLink(); + return fresh.consumePendingDeepLink(); +} + +afterEach(() => { + jest.dontMock('expo-linking'); +}); + +describe('capturePendingDeepLink / consumePendingDeepLink', () => { + it('resolves a bare conversations/:id path to the (app) conversation route', () => { + const result = freshCapture('chatbotxmobileapp://conversations/12345', 'conversations/12345'); + expect(result).toBe('/(app)/conversations/12345'); + }); + + it('resolves a path that already embeds the (app) group prefix', () => { + const result = freshCapture( + 'https://example.com/(app)/conversations/12345', + '(app)/conversations/12345', + ); + expect(result).toBe('/(app)/conversations/12345'); + }); + + it('resolves a bare contacts/:id path to the (app) contact route', () => { + const result = freshCapture('chatbotxmobileapp://contacts/999', 'contacts/999'); + expect(result).toBe('/(app)/contacts/999'); + }); + + it('does nothing for an unrecognized path', () => { + const result = freshCapture('chatbotxmobileapp://settings', 'settings'); + expect(result).toBeNull(); + }); + + it('does nothing when there is no launch URL at all', () => { + const result = freshCapture(undefined, ''); + expect(result).toBeNull(); + }); + + it('consuming clears the stashed path so a later call returns null', () => { + jest.resetModules(); + jest.doMock('expo-linking', () => ({ + getLinkingURL: jest.fn().mockReturnValue('chatbotxmobileapp://conversations/1'), + parse: jest.fn().mockReturnValue({ path: 'conversations/1' }), + })); + // eslint-disable-next-line @typescript-eslint/no-require-imports -- deliberate fresh re-require for module-state isolation + const fresh: typeof import('@/lib/pending-deep-link') = require('@/lib/pending-deep-link'); + fresh.capturePendingDeepLink(); + + expect(fresh.consumePendingDeepLink()).toBe('/(app)/conversations/1'); + expect(fresh.consumePendingDeepLink()).toBeNull(); + }); + + it('only captures once per module instance — a second call is a no-op', () => { + jest.resetModules(); + jest.doMock('expo-linking', () => ({ + getLinkingURL: jest.fn().mockReturnValue('chatbotxmobileapp://conversations/1'), + parse: jest.fn().mockReturnValue({ path: 'conversations/1' }), + })); + // eslint-disable-next-line @typescript-eslint/no-require-imports -- deliberate fresh re-require for module-state isolation + const fresh: typeof import('@/lib/pending-deep-link') = require('@/lib/pending-deep-link'); + fresh.capturePendingDeepLink(); + fresh.capturePendingDeepLink(); // second call: no-op, even though nothing else changed + + expect(fresh.consumePendingDeepLink()).toBe('/(app)/conversations/1'); + }); +}); diff --git a/src/lib/pending-deep-link.ts b/src/lib/pending-deep-link.ts index 6881a58..b5f9ab2 100644 --- a/src/lib/pending-deep-link.ts +++ b/src/lib/pending-deep-link.ts @@ -1,20 +1,38 @@ import * as Linking from 'expo-linking'; /** - * Cold-start deep link to a conversation (`chatbotxmobileapp://conversations/:id`, e.g. from a - * push notification once delivery lands) arrives before the auth bootstrap gate resolves. - * `(app)/_layout.tsx` redirects an unauthenticated visitor to sign-in, which drops the original - * target — expo-router has no built-in "return to this deep link after auth" mechanism. This - * module stashes the conversation path in memory at launch (read once, before any redirect can - * fire) so sign-in can navigate there instead of the default `/` after a successful login. + * Cold-start deep link to a conversation or contact (`chatbotxmobileapp://conversations/:id` or + * `.../contacts/:id`, e.g. from a push notification once delivery lands) arrives before the auth + * bootstrap gate resolves. `(app)/_layout.tsx` redirects an unauthenticated visitor to sign-in, + * which drops the original target — expo-router has no built-in "return to this deep link after + * auth" mechanism. This module stashes the resolved route path in memory at launch (read once, + * before any redirect can fire) so sign-in can navigate there instead of the default `/` after a + * successful login. * * In-memory only (not persisted): a cold-start deep link is only meaningful for THIS launch: if * the process is killed before sign-in completes, the notification (once real push delivery * exists) would redeliver or the user re-opens the link. */ -let pendingConversationPath: string | null = null; +let pendingDeepLinkPath: string | null = null; let captured = false; +/** Deep-link path shapes this app knows how to resolve to a route, in order of priority. Each + * pattern is matched against the URL's path with the scheme/host stripped (what `Linking.parse` + * gives us) — accepting both a bare `conversations/:id` / `contacts/:id` link (the natural shape + * for a push-notification deep link, e.g. `chatbotxmobileapp://conversations/12345`) and one that + * already embeds the `(app)` route-group prefix (e.g. from a universal link that mirrors the + * app's own internal route paths verbatim). */ +const DEEP_LINK_PATTERNS: { regex: RegExp; buildPath: (id: string) => string }[] = [ + { + regex: /^\/?(?:\(app\)\/)?conversations\/([^/]+)\/?$/, + buildPath: (id) => `/(app)/conversations/${id}`, + }, + { + regex: /^\/?(?:\(app\)\/)?contacts\/([^/]+)\/?$/, + buildPath: (id) => `/(app)/contacts/${id}`, + }, +]; + /** Call once, as early as possible (root layout module scope) — captures the launch URL before * any navigation/redirect can consume it. Safe to call multiple times; only the first call has an * effect, matching the "cold start only" semantics above. */ @@ -26,18 +44,21 @@ export function capturePendingDeepLink(): void { if (!url) return; const parsed = Linking.parse(url); - // expo-router path for the chat screen is `conversations/[conversationId]` — Linking.parse - // gives us the path with the scheme/host stripped, e.g. "conversations/12345". - const match = /^\/?conversations\/([^/]+)\/?$/.exec(parsed.path ?? ''); - if (match) { - pendingConversationPath = `/(app)/conversations/${match[1]}`; + const path = parsed.path ?? ''; + + for (const pattern of DEEP_LINK_PATTERNS) { + const match = pattern.regex.exec(path); + if (match) { + pendingDeepLinkPath = pattern.buildPath(match[1]!); + return; + } } } /** Consume the stashed deep link (if any) — returns it once, then clears it, so a later normal * sign-in (e.g. after a manual sign-out) doesn't replay a stale deep link from a previous launch. */ export function consumePendingDeepLink(): string | null { - const path = pendingConversationPath; - pendingConversationPath = null; + const path = pendingDeepLinkPath; + pendingDeepLinkPath = null; return path; } diff --git a/src/lib/query-client.ts b/src/lib/query-client.ts index d417a56..2c69548 100644 --- a/src/lib/query-client.ts +++ b/src/lib/query-client.ts @@ -1,7 +1,7 @@ import { MutationCache, QueryCache, QueryClient } from '@tanstack/react-query'; -import { normalizeApiError } from '@/api/errors'; -import { clearAuthToken } from '@/api/auth-token'; +import { ApiError, normalizeApiError } from '@/api/errors'; +import { clearAuthToken, getAuthToken } from '@/api/auth-token'; import { useAuthStore } from '@/stores/use-auth-store'; import { useWorkspaceStore } from '@/stores/use-workspace-store'; @@ -11,7 +11,7 @@ import { useWorkspaceStore } from '@/stores/use-workspace-store'; * 402 workspaceBlocked is left for the calling screen (composer banner, etc.) to read via * `normalizeApiError` on its own `error` — this handler only manages the auth-affecting codes. */ -function handleGlobalError(error: unknown): void { +async function handleGlobalError(error: unknown): Promise<void> { const normalized = normalizeApiError(error); if (normalized.kind === 'mustChangePassword') { @@ -19,16 +19,36 @@ function handleGlobalError(error: unknown): void { return; } if (normalized.kind === 'unauthorized') { + // A 401 fired before auth bootstrap resolved (or from a request that never carried a token in + // the first place) doesn't mean the stored token is invalid — only force a sign-out when one + // was actually present, otherwise this races the bootstrap and immediately re-signs-out a + // user who was never actually rejected. + const token = await getAuthToken(); + if (!token) return; + clearAuthToken().catch(() => {}); useAuthStore.getState().setSignedOut(); useWorkspaceStore.getState().setCurrentWorkspaceId(null); } } +/** Skip retrying 4xx responses — they're the server telling us the request itself is wrong + * (bad input, unauthorized, not found, blocked), and retrying identically can't change that + * outcome. Only retry once for everything else (network blips, 5xx). */ +function shouldRetry(failureCount: number, error: unknown): boolean { + if (error instanceof ApiError) { + const status = error.body.status; + if (status !== undefined && status >= 400 && status < 500) { + return false; + } + } + return failureCount < 1; +} + export const queryClient = new QueryClient({ defaultOptions: { queries: { - retry: 1, + retry: shouldRetry, staleTime: 30_000, }, }, diff --git a/src/realtime/apply-conversation-events.test.ts b/src/realtime/apply-conversation-events.test.ts index 1e4aa4f..fc19180 100644 --- a/src/realtime/apply-conversation-events.test.ts +++ b/src/realtime/apply-conversation-events.test.ts @@ -46,10 +46,21 @@ function cacheWith(conversations: ConversationListItem[]): InfiniteConversations } as InfiniteConversationsData; } +function cacheWithPages(pages: ConversationListItem[][]): InfiniteConversationsData { + return { + pages: pages.map((data) => ({ data, nextCursor: null, prevCursor: null })), + pageParams: pages.map(() => undefined), + } as InfiniteConversationsData; +} + function firstPage(result: InfiniteConversationsData): ConversationListItem[] { return result!.pages[0]!.data; } +function pageAt(result: InfiniteConversationsData, index: number): ConversationListItem[] { + return result!.pages[index]!.data; +} + function createdEvent(data: unknown) { return { eventType: RealtimeEventType.messageCreated, data } as const; } @@ -214,4 +225,69 @@ describe('applyConversationMessageCreated', () => { expect(row.messages[row.messages.length - 1]!.id).toBe('new-msg'); expect(row.messages[0]!.id).toBe('msg-1'); }); + + it('patches a matching row on a LATER page in place instead of hoisting it to page 0', () => { + // Regression: hoisting across a page boundary duplicated the row in the client's merged view + // and desynced every subsequent page's cursor — a row found on page 1+ must stay on its own + // page, patched in place. + const cache = cacheWithPages([ + [fakeConversation({ id: 'conv-1' }), fakeConversation({ id: 'conv-2' })], + [fakeConversation({ id: 'conv-3' }), fakeConversation({ id: 'conv-4' })], + ]); + + const result = applyConversationMessageCreated( + cache, + createdEvent({ + id: 'new-msg', + conversationId: 'conv-4', + createdAt: '2026-01-02T00:00:00.000Z', + attachments: [], + }), + ); + + expect(result.found).toBe(true); + // Page 0 is untouched — the row was not hoisted onto it. + expect(pageAt(result.data, 0).map((c) => c.id)).toEqual(['conv-1', 'conv-2']); + // Page 1 keeps its own row order — only patched in place, not moved to the front. + expect(pageAt(result.data, 1).map((c) => c.id)).toEqual(['conv-3', 'conv-4']); + expect(pageAt(result.data, 1)[1]!.lastActivityAt).toBe('2026-01-02T00:00:00.000Z'); + }); + + it('bumps agentLastReadAt when the message belongs to the currently-active conversation', () => { + const cache = cacheWith([ + fakeConversation({ id: 'conv-1', agentLastReadAt: '2025-01-01T00:00:00.000Z' }), + ]); + + const result = applyConversationMessageCreated( + cache, + createdEvent({ + id: 'new-msg', + conversationId: 'conv-1', + createdAt: '2026-01-02T00:00:00.000Z', + attachments: [], + }), + 'conv-1', + ); + + expect(firstPage(result.data)[0]!.agentLastReadAt).toBe('2026-01-02T00:00:00.000Z'); + }); + + it('leaves agentLastReadAt untouched when the message is for a different conversation than the active one', () => { + const cache = cacheWith([ + fakeConversation({ id: 'conv-1', agentLastReadAt: '2025-01-01T00:00:00.000Z' }), + ]); + + const result = applyConversationMessageCreated( + cache, + createdEvent({ + id: 'new-msg', + conversationId: 'conv-1', + createdAt: '2026-01-02T00:00:00.000Z', + attachments: [], + }), + 'conv-2', + ); + + expect(firstPage(result.data)[0]!.agentLastReadAt).toBe('2025-01-01T00:00:00.000Z'); + }); }); diff --git a/src/realtime/apply-conversation-events.ts b/src/realtime/apply-conversation-events.ts index 66752c7..7ccd9a2 100644 --- a/src/realtime/apply-conversation-events.ts +++ b/src/realtime/apply-conversation-events.ts @@ -105,8 +105,16 @@ export interface ApplyConversationMessageCreatedResult { * and this one ALSO patches the matching conversation-list ROW so the inbox preview stays live * without a refetch — updates `lastActivityAt`, splices the message onto the row's `messages[]` * preview array (capped to the last few entries, matching `conversation-row.tsx`'s - * `lastMessagePreview` which only ever reads the LAST entry), and moves that row to the top of - * whichever page it was found on (conventional "most recent conversation first" ordering). + * `lastMessagePreview` which only ever reads the LAST entry). When the matched row is on the + * FIRST page, it's also moved to the front of that page (conventional "most recent conversation + * first" ordering) — a row found on a later page is patched in place instead, since the list is + * cursor-paginated and splicing a row from page N to the front of page 0 would duplicate it + * across pages and desync every subsequent page's cursor. + * + * `activeConversationId` — when it matches the incoming message's conversation — also bumps + * `agentLastReadAt` on the patched row, so the inbox unread badge doesn't drift stale while the + * user is actively looking at that conversation's chat screen (the realtime event is the only + * signal here; the user isn't calling the read-receipt mutation just by having the screen open). * * Deliberately does NOT attempt to fabricate a brand-new row when the conversation isn't cached * anywhere — the event payload is a single message, not a full conversation row, so there's no @@ -118,6 +126,7 @@ const MAX_PREVIEW_MESSAGES = 20; export function applyConversationMessageCreated( current: InfiniteConversationsData, event: RealtimeEventCreateMessage, + activeConversationId: string | null = null, ): ApplyConversationMessageCreatedResult { if (!current) return { data: current, found: false }; @@ -126,7 +135,7 @@ export function applyConversationMessageCreated( let found = false; - const pages = current.pages.map((page) => { + const pages = current.pages.map((page, pageIndex) => { const matchIndex = page.data.findIndex((item) => item.id === incoming.conversationId); if (matchIndex < 0) return page; @@ -136,9 +145,21 @@ export function applyConversationMessageCreated( ...matched, lastActivityAt: incoming.createdAt, messages: [...matched.messages, incoming].slice(-MAX_PREVIEW_MESSAGES), + ...(incoming.conversationId === activeConversationId + ? { agentLastReadAt: incoming.createdAt } + : {}), }; - // Move the patched row to the front of this page — mirrors "most recently active + if (pageIndex !== 0) { + // Not the first page — patch in place. Hoisting here would move the row across a page + // boundary, duplicating it in the client's merged view and corrupting cursor pagination. + return { + ...page, + data: page.data.map((item, index) => (index === matchIndex ? patchedRow : item)), + }; + } + + // First page only: move the patched row to the front — mirrors "most recently active // conversation first" ordering without waiting on a refetch. const rest = page.data.filter((_, index) => index !== matchIndex); return { ...page, data: [patchedRow, ...rest] }; diff --git a/src/realtime/events.ts b/src/realtime/events.ts index d87ccf1..5104c14 100644 --- a/src/realtime/events.ts +++ b/src/realtime/events.ts @@ -64,7 +64,7 @@ export type RealtimeEventMessageFailed = { data: { messageId: string; clientId?: string; - error: string | null; + error?: string | null; }; }; diff --git a/src/realtime/parse-event.ts b/src/realtime/parse-event.ts index 86bdd33..fdd65b6 100644 --- a/src/realtime/parse-event.ts +++ b/src/realtime/parse-event.ts @@ -62,7 +62,9 @@ function validateMessageFailed(data: unknown): boolean { if (!isObject(data)) return false; if (!isString(data.messageId)) return false; if (data.clientId !== undefined && typeof data.clientId !== 'string') return false; - if (data.error !== null && typeof data.error !== 'string') return false; + // `error` is optional — a payload that omits it entirely (rather than sending `null`) must not + // fail validation and drop the whole event. + if (!isOptionalNullableString(data.error)) return false; return true; } diff --git a/src/realtime/realtime-provider.tsx b/src/realtime/realtime-provider.tsx index 63880ba..d5d06fe 100644 --- a/src/realtime/realtime-provider.tsx +++ b/src/realtime/realtime-provider.tsx @@ -1,7 +1,7 @@ import { useQueryClient } from '@tanstack/react-query'; import { usePartySocket } from 'partysocket/react'; import type { PropsWithChildren } from 'react'; -import { useCallback, useEffect } from 'react'; +import { useCallback, useEffect, useState } from 'react'; import { getAuthToken } from '@/api/auth-token'; import { env } from '@/config/env'; @@ -12,7 +12,7 @@ import { generateOneTimeToken } from './one-time-token'; import { parseRealtimeEvent } from './parse-event'; import { clearAllTypingTimers } from './typing-timers'; import { resetConnectionStore, useConnectionStore } from './use-connection-store'; -import { useRealtimeHandlers } from './use-realtime-handlers'; +import { clearPendingConversationInvalidates, useRealtimeHandlers } from './use-realtime-handlers'; /** * Real implementation, replacing the Phase 0-4 passthrough stub. Connects to the `workspaces` @@ -37,6 +37,19 @@ function ConnectedRealtimeProvider({ const setStatus = useConnectionStore((state) => state.setStatus); const markOpened = useConnectionStore((state) => state.markOpened); + // Resets the connection store synchronously during this component's FIRST render — not in a + // post-mount effect. `usePartySocket` below starts connecting immediately on mount, so an + // effect-based reset can run AFTER `onOpen` has already fired and flipped the store to 'open', + // clobbering it back to 'connecting' and leaving the banner stuck. `useState`'s lazy initializer + // is the React-compiler-safe way to run this exactly once per mounted instance — reading/writing + // a ref's `.current` during render (the more obvious "one-shot" pattern) is disallowed under + // `reactCompiler: true` (see app.config.ts), since the compiler may re-execute render bodies. + // The parent's `key={workspaceId}` already guarantees a fresh instance per workspace. + useState(() => { + resetConnectionStore(); + return null; + }); + const socket = usePartySocket({ host: env.wsUrl, party: 'workspaces', @@ -77,17 +90,15 @@ function ConnectedRealtimeProvider({ }, }); - // Reset to 'connecting' whenever this component (re)mounts (fresh workspace, per the `key` on - // RealtimeProvider below) — usePartySocket starts connecting immediately, so this reflects that - // without waiting on the first onOpen/onClose callback. + // Runs once per mount — workspaceId is this component's whole identity via the parent's + // `key={workspaceId}`, so a dependency array is unnecessary; teardown clears both the typing + // timers and the debounced conversations-list invalidation timers this instance's realtime + // handlers may have scheduled, so neither leaks past the connection they belong to. useEffect(() => { - resetConnectionStore(); return () => { clearAllTypingTimers(); + clearPendingConversationInvalidates(); }; - // Runs once per mount — workspaceId is this component's whole identity via the parent's - // `key={workspaceId}`, so a dependency array is unnecessary; teardown clears timers for the - // room this instance owned. }, []); const handleBackground = useCallback(() => { diff --git a/src/realtime/use-realtime-handlers.ts b/src/realtime/use-realtime-handlers.ts index 5cee479..9f7f7c1 100644 --- a/src/realtime/use-realtime-handlers.ts +++ b/src/realtime/use-realtime-handlers.ts @@ -44,6 +44,16 @@ function debounceInvalidateConversationsList(queryClient: QueryClient, workspace pendingConversationInvalidate.set(workspaceId, timer); } +/** Clears every pending debounced conversations-list invalidation across all workspaces — + * these are module-scope timers (not tied to any component's lifecycle), so without an explicit + * teardown they outlive the realtime connection that scheduled them, leaking a timer per + * not-yet-cached conversation and holding the Jest process open after tests finish. Call this + * alongside `clearAllTypingTimers()` wherever the realtime connection itself is torn down. */ +export function clearPendingConversationInvalidates(): void { + pendingConversationInvalidate.forEach((timer) => clearTimeout(timer)); + pendingConversationInvalidate.clear(); +} + /** * Dispatches a parsed realtime event to the right cache applier. * @@ -76,6 +86,7 @@ export function useRealtimeHandlers(queryClient: QueryClient, workspaceId: strin ); let foundInAnyPage = false; + const activeConversationId = useChatStore.getState().activeConversationId; queryClient .getQueryCache() .findAll({ @@ -87,7 +98,7 @@ export function useRealtimeHandlers(queryClient: QueryClient, workspaceId: strin }) .forEach((query) => { queryClient.setQueryData<InfiniteConversationsData>(query.queryKey, (old) => { - const result = applyConversationMessageCreated(old, event); + const result = applyConversationMessageCreated(old, event, activeConversationId); if (result.found) foundInAnyPage = true; return result.data; }); diff --git a/src/stores/use-workspace-store.ts b/src/stores/use-workspace-store.ts index 8393185..1f2cc92 100644 --- a/src/stores/use-workspace-store.ts +++ b/src/stores/use-workspace-store.ts @@ -28,3 +28,19 @@ export const useWorkspaceStore = create<WorkspaceStoreState>()( }, ), ); + +/** Resolves once the persisted workspace-store snapshot has been read from AsyncStorage (or + * immediately, if hydration already finished before this was called). Without gating on this, + * cold start reads `currentWorkspaceId === null` before hydration completes and bounces an + * already-signed-in, already-workspace-selected user to the workspace picker. */ +export function waitForWorkspaceHydration(): Promise<void> { + if (useWorkspaceStore.persist.hasHydrated()) { + return Promise.resolve(); + } + return new Promise((resolve) => { + const unsubscribe = useWorkspaceStore.persist.onFinishHydration(() => { + unsubscribe(); + resolve(); + }); + }); +} diff --git a/src/theme/motion.ts b/src/theme/motion.ts index 424d1ca..f107f6b 100644 --- a/src/theme/motion.ts +++ b/src/theme/motion.ts @@ -1,17 +1,10 @@ /** - * Motion tokens re-exported from tokens.ts under friendlier names, plus a `useReducedMotion` - * hook so components can short-circuit springs/entrance animations for users with the OS - * "reduce motion" accessibility setting on, without importing reanimated directly everywhere. + * A `useReducedMotion` hook so components can short-circuit springs/entrance animations for + * users with the OS "reduce motion" accessibility setting on, without importing reanimated + * directly everywhere. */ import { useReducedMotion as useReanimatedReducedMotion } from 'react-native-reanimated'; -import { motionDurations, motionEasings, motionSprings, pressScale } from './tokens'; - -export const durations = motionDurations; -export const easings = motionEasings; -export const springs = motionSprings; -export { pressScale }; - /** Wraps reanimated's device-level reduced-motion signal. Components should check this before * kicking off a spring/entrance animation and fall back to an instant state change (or a plain * opacity fade at `durations.instant`) when it's `true`. */ diff --git a/src/theme/tokens.test.ts b/src/theme/tokens.test.ts index 49ab67f..32be708 100644 --- a/src/theme/tokens.test.ts +++ b/src/theme/tokens.test.ts @@ -33,19 +33,10 @@ const NEW_SEMANTIC_COLOR_KEYS = [ 'scrim', ] as const; -// Phase 7 removed `unreadDot`, `bubbleOutbound(Text)`, `bubbleInbound(Text)` once their last call -// sites migrated to the new semantic keys — see tokens.ts's doc comment. The rest still have live -// call sites and stay as compatibility aliases. -const DEPRECATED_ALIAS_KEYS = [ - 'background', - 'surface', - 'surfaceElevated', - 'border', - 'text', - 'primary', - 'primaryForeground', -] as const; - +// Phase 7 removed `unreadDot`, `bubbleOutbound(Text)`, `bubbleInbound(Text)`; a later cleanup +// removed the remaining flat-key aliases (`background`, `surface`, `surfaceElevated`, `border`, +// `text`, `primary`, `primaryForeground`) once every call site migrated to the semantic keys +// above — see tokens.ts's doc comment. const CHANNEL_KEYS = [ 'messenger', 'instagram', @@ -68,13 +59,6 @@ describe('colorTokens', () => { } }); - it.each(['light', 'dark'] as const)('keeps every deprecated alias for %s', (scheme) => { - for (const key of DEPRECATED_ALIAS_KEYS) { - expect(colorTokens[scheme]).toHaveProperty(key); - expect(typeof colorTokens[scheme][key]).toBe('string'); - } - }); - it.each(['light', 'dark'] as const)('defines exactly the 11 channel keys for %s', (scheme) => { expect(Object.keys(colorTokens[scheme].channel).sort()).toEqual([...CHANNEL_KEYS].sort()); }); @@ -90,28 +74,24 @@ describe('colorTokens', () => { expect(colorTokens[scheme].avatarPalette).toHaveLength(6); }); - it('deprecated aliases point at their new-key equivalents', () => { - for (const scheme of ['light', 'dark'] as const) { - const tokens = colorTokens[scheme]; - expect(tokens.background).toBe(tokens.bg); - expect(tokens.surface).toBe(tokens.surface2); - expect(tokens.surfaceElevated).toBe(tokens.surface1); - expect(tokens.border).toBe(tokens.borderSubtle); - expect(tokens.text).toBe(tokens.textPrimary); - expect(tokens.primary).toBe(tokens.brand); - expect(tokens.primaryForeground).toBe(tokens.onBrand); - } - }); - it.each(['light', 'dark'] as const)( - 'no longer exposes the fully-migrated aliases removed in Phase 7 for %s', + 'no longer exposes any deprecated flat-key alias for %s', (scheme) => { const tokens = colorTokens[scheme] as Record<string, unknown>; + // Phase 7 removals: expect(tokens.unreadDot).toBeUndefined(); expect(tokens.bubbleOutbound).toBeUndefined(); expect(tokens.bubbleOutboundText).toBeUndefined(); expect(tokens.bubbleInbound).toBeUndefined(); expect(tokens.bubbleInboundText).toBeUndefined(); + // Later cleanup removed the rest once every call site migrated to the semantic keys above: + expect(tokens.background).toBeUndefined(); + expect(tokens.surface).toBeUndefined(); + expect(tokens.surfaceElevated).toBeUndefined(); + expect(tokens.border).toBeUndefined(); + expect(tokens.text).toBeUndefined(); + expect(tokens.primary).toBeUndefined(); + expect(tokens.primaryForeground).toBeUndefined(); }, ); }); diff --git a/src/theme/tokens.ts b/src/theme/tokens.ts index 4328e53..e1a46dc 100644 --- a/src/theme/tokens.ts +++ b/src/theme/tokens.ts @@ -6,19 +6,8 @@ * Style direction: "Layered Workbench" — a tinted-neutral canvas with two raised surface levels, * hairline borders instead of heavy shadows, a strong type hierarchy, and color reserved for * meaning (channel identity, unread state, bot state, semantic status) rather than decoration. - * - * `colorTokens.light`/`colorTokens.dark` also carry a handful of OLD flat keys (`background`, - * `surface`, `surfaceElevated`, `border`, `text`, `primary`, `primaryForeground`) as DEPRECATED - * ALIASES of the new semantic keys below, kept because Phase 7's audit found ~35 live call sites - * still on the old names — removing them in that pass would have been a risky mass-rename for a - * final-phase sweep. `unreadDot`, `bubbleOutbound(Text)`, `bubbleInbound(Text)` were removed in - * Phase 7 once their last call sites migrated to `brand`/`bubbleOut(Text)`/`bubbleIn(Text)`. New - * code should use the new keys directly; the remaining aliases are still slated for eventual - * removal once every call site migrates. */ -import { Easing } from 'react-native'; - import { withAlpha } from './color-utils'; // --------------------------------------------------------------------------- @@ -34,7 +23,10 @@ const lightBase = { borderStrong: '#cfcabd', textPrimary: '#1c1b17', textSecondary: '#615c4f', - textTertiary: '#948d7c', + // WCAG AA (4.5:1) against `bg` (#f6f5f2) requires darkening past the original #948d7c + // (~3.0:1) — #767063 is the same warm-neutral hue, darkened just enough to clear 4.5:1 + // (measured ~4.51:1). + textTertiary: '#767063', textInverse: '#ffffff', brand: '#3c6df0', brandStrong: '#2851c9', @@ -145,21 +137,6 @@ function buildColorTokens(base: ColorBase, channel: ChannelMap, avatarPalette: r scrim: withAlpha('#000000', 0.5), channel, avatarPalette, - - // --- deprecated aliases (Phase 1 compatibility shim) --- - // Phase 7 removed `unreadDot`, `bubbleOutbound(Text)`, `bubbleInbound(Text)` — every call site - // had already migrated to the new semantic keys (`brand`, `bubbleOut(Text)`, `bubbleIn(Text)`). - // The remaining aliases below still have live call sites (35 across ~20 files as of Phase 7's - // audit) — removing them now would be a risky mass-rename for a final-phase pass, so they stay - // until a future cleanup migrates the rest. New code should use the new keys directly. - background: base.bg, - surface: base.surface2, - surfaceElevated: base.surface1, - border: base.borderSubtle, - text: base.textPrimary, - textSecondary: base.textSecondary, - primary: base.brand, - primaryForeground: base.onBrand, }; } @@ -302,16 +279,4 @@ export const motionDurations = { exit: 160, } as const; -export const motionEasings = { - standard: Easing.bezier(0.4, 0, 0.2, 1), - decelerate: Easing.bezier(0, 0, 0.2, 1), - accelerate: Easing.bezier(0.4, 0, 1, 1), -} as const; - -export const motionSprings = { - snappy: { damping: 20, stiffness: 300, mass: 0.6 }, - gentle: { damping: 18, stiffness: 160, mass: 0.8 }, - bouncy: { damping: 12, stiffness: 220, mass: 0.7 }, -} as const; - export const pressScale = 0.97; diff --git a/src/theme/use-theme.ts b/src/theme/use-theme.ts index c1c04a5..19971f8 100644 --- a/src/theme/use-theme.ts +++ b/src/theme/use-theme.ts @@ -1,5 +1,4 @@ import { useColorScheme } from 'react-native'; -import { useMemo } from 'react'; import { useSettingsStore } from '@/stores/use-settings-store'; @@ -7,8 +6,6 @@ import { colorTokens, elevationTokens, motionDurations, - motionEasings, - motionSprings, pressScale, radiusTokens, spacingTokens, @@ -42,8 +39,6 @@ function buildTheme(scheme: ResolvedScheme) { typography: typographyTokens, motion: { durations: motionDurations, - easings: motionEasings, - springs: motionSprings, pressScale, }, }; @@ -67,17 +62,3 @@ export function useTheme(): Theme { return THEMES[scheme]; } - -/** - * Memoizes a derived-styles object per (scheme, factory identity). Correct as long as callers - * either define `factory` at module scope (preferred — it's a pure function of `theme` with no - * closure captures) or wrap it in `useCallback` with its own stable dependencies; an inline - * arrow function recreated every render defeats the memoization (a fresh factory reference each - * render means `useMemo`'s dependency array never matches, so it just recomputes every time — - * functionally correct, just not memoized). - */ -export function useThemedStyles<T>(factory: (theme: Theme) => T): T { - const theme = useTheme(); - - return useMemo(() => factory(theme), [theme, factory]); -} From 93d9b3f8417436ca184c9f38a121b191de3844b8 Mon Sep 17 00:00:00 2001 From: Real Codesiman <realcodesiman@gmail.com> Date: Fri, 28 Aug 2026 09:44:49 +0700 Subject: [PATCH 3/5] docs: remove sibling-repo references and split README into docs/ Comments and README content referenced the backend's sibling repo path (../aha.chat) and internal planning artifacts ("Phase N", "the plan"), neither of which make sense once this repo stands alone. Reword them to describe backend behavior directly, and split the README's deep-dive sections (API client generation, project structure, push notifications, EAS Update, realtime event sync) into docs/*.md so the README stays a quick-start overview. --- .claude/commands/commit-pr.md | 1 + .claude/commands/commit.md | 3 +- .env.example | 2 +- README.md | 141 ++++-------------- app.config.ts | 4 +- docs/api-client.md | 15 ++ docs/eas-updates.md | 6 + docs/project-structure.md | 22 +++ docs/push-notifications.md | 12 ++ docs/realtime-events.md | 9 ++ scripts/generate-api-client.ts | 9 +- src/api/auth-endpoints.ts | 15 +- src/api/auth-token.ts | 6 +- src/api/client.ts | 2 +- src/api/errors.ts | 11 +- src/api/generated/README.md | 4 +- src/api/pagination.ts | 4 +- .../[conversationId]/contact.tsx | 4 +- src/app/(app)/settings/members.tsx | 2 +- src/app/(auth)/change-password.tsx | 4 +- src/components/ui/sheet.tsx | 2 +- src/components/ui/skeleton.tsx | 4 +- src/components/workspace-blocked-gate.tsx | 6 +- src/dev/gallery-content.tsx | 4 +- .../chat/api/send-message-multipart.ts | 3 +- src/features/chat/api/use-send-message.ts | 2 +- .../chat/components/message-bubble.tsx | 4 +- .../chat/components/typing-indicator.tsx | 4 +- src/features/chat/stores/use-chat-store.ts | 2 +- src/features/contacts/api/use-contact-tags.ts | 4 +- .../contacts/api/use-contacts-infinite.ts | 4 +- .../api/use-conversations-infinite.ts | 2 +- .../api/use-inbox-unread-count.ts | 2 +- .../conversations/components/filter-sheet.tsx | 2 +- .../conversations/lib/conversation-status.ts | 2 +- src/features/permissions/use-permissions.ts | 4 +- src/features/settings/api/use-teams.ts | 8 +- src/features/workspaces/api/use-workspaces.ts | 4 +- src/i18n/locales.ts | 4 +- src/lib/notification-tap.ts | 4 +- src/lib/notifications.ts | 2 +- src/realtime/apply-conversation-events.ts | 6 +- src/realtime/apply-message-events.ts | 12 +- src/realtime/events.ts | 12 +- src/realtime/one-time-token.ts | 6 +- src/realtime/realtime-provider.tsx | 8 +- src/realtime/use-realtime-handlers.ts | 2 +- src/stores/use-workspace-store.ts | 6 +- src/theme/tokens.test.ts | 4 +- src/theme/tokens.ts | 2 +- 50 files changed, 191 insertions(+), 216 deletions(-) create mode 100644 docs/api-client.md create mode 100644 docs/eas-updates.md create mode 100644 docs/project-structure.md create mode 100644 docs/push-notifications.md create mode 100644 docs/realtime-events.md diff --git a/.claude/commands/commit-pr.md b/.claude/commands/commit-pr.md index 4576ff6..9f9db75 100644 --- a/.claude/commands/commit-pr.md +++ b/.claude/commands/commit-pr.md @@ -25,6 +25,7 @@ Run `/commit` flow then push and open PR. Follows `.agents/rules/git.md`. - Body sections: `## Summary` (1–3 bullets, why), `## Changes` (key files/modules), `## Test plan` (markdown checklist). 5. **Create PR** against `main` (the project's canonical PR base): + ```bash gh pr create --base main --title "<title>" --body "$(cat <<'EOF' ## Summary diff --git a/.claude/commands/commit.md b/.claude/commands/commit.md index 1c34a08..bafa467 100644 --- a/.claude/commands/commit.md +++ b/.claude/commands/commit.md @@ -16,12 +16,13 @@ Create a git commit for the current changes following the ChatbotX conventions i - Determine commit `type`: `feat`, `fix`, `refactor`, `docs`, `style`, `test`, `chore`, `ci`, `perf`, `build`, `revert` - Determine `scope` from touched package/app/feature when obvious (e.g. `builder`, `worker`, `whatsapp`, `database`) - Draft subject: `<type>(<scope>): <subject>` — ≤ 100 chars, lowercase after `:`, no trailing period - - Add body only when the *why* is non-obvious + - Add body only when the _why_ is non-obvious 3. Stage **specific files only** — never `git add -A` or `git add .`. - Skip `.env*` and any file containing secrets. Warn if user explicitly requests them. 4. Commit using a HEREDOC so formatting is preserved: + ```bash git commit -m "$(cat <<'EOF' <type>(<scope>): <subject> diff --git a/.env.example b/.env.example index 0a1a12a..ff699c0 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,4 @@ -# Base URL of the aha.chat builder app (oRPC + REST API + OpenAPI spec host). +# Base URL of the ChatbotX builder app (oRPC + REST API + OpenAPI spec host). # Defaults to the builder app's standard local dev URL when unset. API_BASE_URL=http://localhost:3123 diff --git a/README.md b/README.md index 8725d76..5192119 100644 --- a/README.md +++ b/README.md @@ -1,136 +1,59 @@ # chatbotx-mobile-app -Expo + TypeScript mobile client for [ChatbotX](https://github.com/) — the open-source omnichannel -chatbot platform whose backend lives in the sibling repo, `../aha.chat`. This app replaces the -legacy `chatbotx-mobile` React Native project (untyped JS, old Laravel backend). +Expo + TypeScript mobile client for [ChatbotX](https://chatbotx.io/) — the open-source omnichannel +chatbot platform. -Auth, workspace picker, and three tabs — Conversations, Contacts, and Settings — are implemented, -along with chat with attachments, PartyKit realtime, push notifications, 20-locale i18n (including -RTL for `ar`/`he`), and a typed REST client generated from the backend's OpenAPI spec. +## ✨ Features -> This app talks to the `aha.chat` backend's `bearer()` better-auth plugin and the REST endpoints -> under `/api/workspaces/{workspaceId}/...`, plus the corresponding realtime events. Run -> `pnpm --filter builder dev` in `aha.chat` first so `pnpm generate:api` (see below) has a live -> spec to fetch. +- **Auth & workspaces:** sign in, switch workspaces, workspace picker +- **Live Chat:** Conversations tab with attachments, replies, edit/delete, and read state +- **Contact CRM:** Contacts tab with tags, custom fields, and PII masking for restricted roles +- **Realtime:** PartyKit-backed live updates for messages and conversation state +- **Push notifications:** Expo push + Android FCM, with deep-linking into a conversation/contact +- **i18n:** 20 locales, including RTL support for `ar`/`he` +- **Typed API client:** REST client generated from the backend's OpenAPI spec -## Stack +## Tech Stack -- [Expo](https://expo.dev) (SDK 57) + [expo-router](https://docs.expo.dev/router/introduction/) (file-based routing, `src/app/`) +- [Expo](https://expo.dev) (SDK 57) + [expo-router](https://docs.expo.dev/router/introduction/) - TypeScript, strict mode - [TanStack Query](https://tanstack.com/query) for server state - [Zustand](https://zustand-demo.pmnd.rs/) for client state -- [openapi-fetch](https://openapi-ts.dev/openapi-fetch/) + [openapi-typescript](https://openapi-ts.dev/) for a typed REST client generated from the backend's OpenAPI spec +- [openapi-fetch](https://openapi-ts.dev/openapi-fetch/) + [openapi-typescript](https://openapi-ts.dev/) for the typed REST client - [i18next](https://www.i18next.com/) / [react-i18next](https://react.i18next.com/) for translations - [expo-secure-store](https://docs.expo.dev/versions/latest/sdk/securestore/) for token storage -- ESLint (`eslint-config-expo`, flat config) + Prettier -## Get started +There is no web target — this app ships iOS/Android only. + +## Quick Start ```bash pnpm install +cp .env.example .env +# edit API_BASE_URL if the backend isn't on the default local port (http://localhost:3123) pnpm start ``` Then follow the Expo CLI prompts to open the app in a development build, Android emulator, iOS simulator, or Expo Go. -Other useful commands: +## Development Commands ```bash pnpm ios # expo run:ios -pnpm android # expo run:android -pnpm lint # expo lint (eslint-config-expo) -pnpm typecheck # tsc --noEmit -pnpm format # prettier --write . -pnpm format:check # prettier --check . -``` - -There is no web target — this app ships iOS/Android only (see `app.config.ts`; `react-dom` / -`react-native-web` are not dependencies). - -## Configuration - -The API base URL is env-driven via `app.config.ts` (not a static `app.json`, since we need -`process.env` at config-evaluation time) and exposed to the app at runtime through -`expo-constants` (see `src/config/env.ts`). - -```bash -cp .env.example .env -# edit API_BASE_URL if the builder app isn't on the default local port -``` - -Defaults to `http://localhost:3123` — the aha.chat builder app's standard local dev URL. - -### Android push notifications (`google-services.json`) - -Android FCM V1 push delivery requires a Firebase project with this app's package name -(`com.chatconnectx.mobile`) registered, and its `google-services.json` downloaded to the repo -root — the file is gitignored (per-developer/CI secret, never committed). `app.config.ts` only -references it conditionally (`existsSync` check), so a clean checkout without the file still -builds; Android push registration simply won't work until it's provisioned. For EAS builds, -upload it as a [file-type secret](https://docs.expo.dev/eas/environment-variables/#file-environment-variables) -via `eas env:create` (or the EAS dashboard) referencing the path `./google-services.json`, rather -than committing it. - -### EAS Update - -This app ships with `expo-updates` configured (`app.config.ts`'s `updates`/`runtimeVersion`), so -production/preview builds can receive OTA JS-bundle updates via `eas update` without an app-store -resubmission. `runtimeVersion` uses the `appVersion` policy — a build only receives updates -published against the same `version` in `app.config.ts`. - -## Regenerating the API client - -```bash -pnpm generate:api -``` - -This fetches `{API_BASE_URL}/api/spec.json` (the full oRPC router spec, including the -bearer/session-authenticated routes the app needs — override with `API_SPEC_PATH` to point at the -workspace-token-only `/api/public-spec.json` instead) from a running aha.chat builder instance and -runs it through `openapi-typescript` to produce `src/api/generated/schema.ts`, which -`src/api/client.ts` consumes via `openapi-fetch`. - -The script is **not** wired into the build — the backend isn't guaranteed to be running, and this -repo must build without it. If the spec endpoint isn't reachable, the script prints a clear -message and exits non-zero without touching the previously generated file. Run it from the -`aha.chat` repo first: `pnpm --filter builder dev`. - -## Realtime event types - -`src/realtime/events.ts` mirrors the realtime event type definitions from -`aha.chat/packages/partysocket-config/src/schemas.ts` **by hand**. This app lives in a separate -git repository (not a pnpm workspace member of `aha.chat`), so there's no live cross-repo import — -copying types in is the intentionally simple, non-fragile choice over a symlink or git submodule. -Only type-only shapes are mirrored; the server-side `lib.ts` in that package is never referenced. - -When the source file changes in `aha.chat`, re-copy the updated shapes by hand. The header comment -in `src/realtime/events.ts` notes which commit it was last synced against. - -## Project structure - -``` -src/ - api/ typed REST client (openapi-fetch), auth token storage, big-number-safe JSON parsing - generated/ output of `pnpm generate:api` — do not hand-edit - app/ expo-router screens (file-based routing) - components/ui/ design-system primitives (Button, Text, ListItem, Sheet, Skeleton, etc.) - config/ runtime env config (API_BASE_URL via expo-constants) - dev/ dev-build-only screens' content (e.g. the UI gallery), lazily required so their - imports don't ship in production bundles - features/ feature modules (chat, contacts, conversations, workspaces, settings, ...) — - each owns its own api/ (TanStack Query hooks), components/, and stores/ - i18n/ i18next setup, RTL reconciliation, and locale files (20 locales) - lib/ shared library wiring (TanStack Query client, push notifications, deep links) - realtime/ realtime event types mirrored from aha.chat (type-only, manual sync) - stores/ app-wide Zustand client-state stores (auth, workspace, settings) - theme/ design tokens, the useTheme() hook, and motion/reduced-motion helpers -scripts/ - generate-api-client.ts regenerates src/api/generated/schema.ts from the backend's OpenAPI spec - i18n-sync.ts backfills missing translation keys across all 20 locales from en.json +pnpm android # expo run:android +pnpm lint # expo lint (eslint-config-expo) +pnpm typecheck # tsc --noEmit +pnpm test # jest +pnpm format # prettier --write . +pnpm format:check # prettier --check . +pnpm generate:api # regenerate the typed API client from a running backend instance ``` -## Related repo +## Docs -Backend: [`aha.chat`](../aha.chat) — Next.js builder app, oRPC API, Drizzle/Postgres, BullMQ -workers, PartyKit realtime. See its `AGENTS.md` for architecture details. +- [Project structure](docs/project-structure.md) +- [Regenerating the API client](docs/api-client.md) +- [Realtime event types](docs/realtime-events.md) +- [Android push notifications](docs/push-notifications.md) +- [EAS Update](docs/eas-updates.md) diff --git a/app.config.ts b/app.config.ts index 574c2c4..76572d6 100644 --- a/app.config.ts +++ b/app.config.ts @@ -100,12 +100,12 @@ const config: ExpoConfig = { url: 'https://u.expo.dev/8cfd58a8-15f8-4f64-b16c-abf26166e80f', }, extra: { - // Falls back to the builder app's default local dev URL. Override via `API_BASE_URL` env var + // Falls back to the backend's default local dev URL. Override via `API_BASE_URL` env var // (e.g. in `.env`, loaded by `expo start` through the standard EXPO_PUBLIC_ / dotenv support, // or exported in the shell before running Expo CLI commands). apiBaseUrl: process.env.API_BASE_URL ?? 'http://localhost:3123', // Realtime PartyKit server. Web reads this from tenant settings; for mobile dev we default to - // the local partykit dev port until tenant-settings discovery is wired up (see plan risk notes). + // the local partykit dev port until tenant-settings discovery is wired up. wsUrl: process.env.WS_URL ?? 'http://localhost:1999', // Mirrors the expo-localization plugin's supportsRTL above for Expo Go, which can't read the // native Info.plist/strings.xml values the plugin writes at build time. diff --git a/docs/api-client.md b/docs/api-client.md new file mode 100644 index 0000000..d31a33f --- /dev/null +++ b/docs/api-client.md @@ -0,0 +1,15 @@ +# Regenerating the API client + +```bash +pnpm generate:api +``` + +This fetches `{API_BASE_URL}/api/spec.json` (the full oRPC router spec, including the +bearer/session-authenticated routes the app needs — override with `API_SPEC_PATH` to point at the +workspace-token-only `/api/public-spec.json` instead) from a running backend instance and runs it +through `openapi-typescript` to produce `src/api/generated/schema.ts`, which `src/api/client.ts` +consumes via `openapi-fetch`. + +The script is **not** wired into the build — the backend isn't guaranteed to be running, and this +repo must build without it. If the spec endpoint isn't reachable, the script prints a clear +message and exits non-zero without touching the previously generated file. diff --git a/docs/eas-updates.md b/docs/eas-updates.md new file mode 100644 index 0000000..d9ea242 --- /dev/null +++ b/docs/eas-updates.md @@ -0,0 +1,6 @@ +# EAS Update + +This app ships with `expo-updates` configured (`app.config.ts`'s `updates`/`runtimeVersion`), so +production/preview builds can receive OTA JS-bundle updates via `eas update` without an app-store +resubmission. `runtimeVersion` uses the `appVersion` policy — a build only receives updates +published against the same `version` in `app.config.ts`. diff --git a/docs/project-structure.md b/docs/project-structure.md new file mode 100644 index 0000000..132a42d --- /dev/null +++ b/docs/project-structure.md @@ -0,0 +1,22 @@ +# Project structure + +``` +src/ + api/ typed REST client (openapi-fetch), auth token storage, big-number-safe JSON parsing + generated/ output of `pnpm generate:api` — do not hand-edit + app/ expo-router screens (file-based routing) + components/ui/ design-system primitives (Button, Text, ListItem, Sheet, Skeleton, etc.) + config/ runtime env config (API_BASE_URL via expo-constants) + dev/ dev-build-only screens' content (e.g. the UI gallery), lazily required so their + imports don't ship in production bundles + features/ feature modules (chat, contacts, conversations, workspaces, settings, ...) — + each owns its own api/ (TanStack Query hooks), components/, and stores/ + i18n/ i18next setup, RTL reconciliation, and locale files (20 locales) + lib/ shared library wiring (TanStack Query client, push notifications, deep links) + realtime/ realtime event types mirrored from the backend (type-only, manual sync) + stores/ app-wide Zustand client-state stores (auth, workspace, settings) + theme/ design tokens, the useTheme() hook, and motion/reduced-motion helpers +scripts/ + generate-api-client.ts regenerates src/api/generated/schema.ts from the backend's OpenAPI spec + i18n-sync.ts backfills missing translation keys across all 20 locales from en.json +``` diff --git a/docs/push-notifications.md b/docs/push-notifications.md new file mode 100644 index 0000000..8dd8cac --- /dev/null +++ b/docs/push-notifications.md @@ -0,0 +1,12 @@ +# Android push notifications (`google-services.json`) + +Android FCM V1 push delivery requires a Firebase project with this app's package name +(`com.chatconnectx.mobile`) registered, and its `google-services.json` downloaded to the repo +root — the file is gitignored (per-developer/CI secret, never committed). `app.config.ts` only +references it conditionally (`existsSync` check), so a clean checkout without the file still +builds; Android push registration simply won't work until it's provisioned. + +For EAS builds, upload it as a +[file-type secret](https://docs.expo.dev/eas/environment-variables/#file-environment-variables) +via `eas env:create` (or the EAS dashboard) referencing the path `./google-services.json`, rather +than committing it. diff --git a/docs/realtime-events.md b/docs/realtime-events.md new file mode 100644 index 0000000..e037250 --- /dev/null +++ b/docs/realtime-events.md @@ -0,0 +1,9 @@ +# Realtime event types + +`src/realtime/events.ts` mirrors the backend's realtime event type definitions **by hand**. This +app lives in a separate git repository from the backend (not a shared pnpm workspace), so there's +no live cross-repo import — copying types in is the intentionally simple, non-fragile choice over +a symlink or git submodule. Only type-only shapes are mirrored; no server-side code is referenced. + +When the backend's event schemas change, re-copy the updated shapes by hand. The header comment in +`src/realtime/events.ts` notes which commit it was last synced against. diff --git a/scripts/generate-api-client.ts b/scripts/generate-api-client.ts index 390498f..b48f333 100644 --- a/scripts/generate-api-client.ts +++ b/scripts/generate-api-client.ts @@ -1,6 +1,6 @@ #!/usr/bin/env -S pnpm exec tsx /** - * Regenerates the typed API client from the aha.chat builder app's OpenAPI spec. + * Regenerates the typed API client from the ChatbotX backend's OpenAPI spec. * * Usage: * pnpm generate:api @@ -13,7 +13,7 @@ * produce src/api/generated/schema.ts (a `paths` type consumed by src/api/client.ts via * openapi-fetch). * - * This script is NOT wired into the build — the aha.chat backend is not guaranteed to be running + * This script is NOT wired into the build — the ChatbotX backend is not guaranteed to be running * locally, and this repo must be buildable without it. On any failure (spec unreachable, bad * response, generation error) it prints a clear message and exits non-zero without touching the * previously generated file. @@ -86,9 +86,8 @@ async function main(): Promise<void> { [generate:api] Could not reach the OpenAPI spec at ${specUrl}. [generate:api] Reason: ${reason} [generate:api] -[generate:api] This is expected if the aha.chat builder app isn't running locally, or if its -[generate:api] Phase 1 backend work (bearer auth + new REST endpoints) hasn't landed yet. -[generate:api] Start it from the aha.chat repo (pnpm --filter builder dev) and try again, or set +[generate:api] This is expected if the ChatbotX backend isn't running locally. +[generate:api] Start the backend locally and try again, or set [generate:api] API_BASE_URL to point at a reachable environment. [generate:api] [generate:api] Leaving the previously generated client untouched (${ diff --git a/src/api/auth-endpoints.ts b/src/api/auth-endpoints.ts index eb6aa4a..1030fae 100644 --- a/src/api/auth-endpoints.ts +++ b/src/api/auth-endpoints.ts @@ -3,7 +3,7 @@ import { env } from '@/config/env'; import { setAuthToken } from './auth-token'; /** - * Hand-rolled fetch calls against aha.chat's better-auth REST routes (`/api/auth/[...all]`). + * Hand-rolled fetch calls against the backend's better-auth REST routes (`/api/auth/[...all]`). * These are NOT part of the oRPC router the generated schema.ts covers, so they can't go through * the typed apiClient — better-auth ships its own REST surface with its own error shape * (`{ message, code }`, no `status`/`defined`, unlike oRPC's `{ code, status, message, defined }`). @@ -127,15 +127,12 @@ export type SocialProvider = 'google' | 'facebook'; export const SOCIAL_CALLBACK_URL = 'chatbotxmobileapp://auth-callback'; /** - * Starts a social sign-in flow. `POST /api/auth/sign-in/social` request/response shape confirmed - * directly from better-auth's own source (aha.chat's `node_modules/better-auth/dist/api/routes/ - * sign-in.mjs` — path `/sign-in/social`; `.d.mts` in the same dir — request `{ provider, - * callbackURL }`, response `{ redirect: boolean; url: string }`), NOT guessed from convention. + * Starts a social sign-in flow. Shape per better-auth's `/sign-in/social` route: request + * `{ provider, callbackURL }`, response `{ redirect: boolean; url: string }`. * - * The returned `url` sends the user through the provider, then aha.chat's fixed OAuth "broker" - * host (see SOCIAL_PROVIDERS/redirectURI in aha.chat's `packages/auth/src/server.ts`), which - * relays back to `callbackURL`. `chatbotxmobileapp://` is allowlisted in that file's - * `trustedOrigins` `staticOrigins` array alongside the broker/builder web origins. + * The returned `url` sends the user through the provider, then the backend's fixed OAuth + * "broker" host relays back to `callbackURL`. `chatbotxmobileapp://` must be in the backend's + * better-auth `trustedOrigins`. */ export async function startSocialSignIn(provider: SocialProvider): Promise<string> { const response = await fetch(`${AUTH_BASE_URL}/sign-in/social`, { diff --git a/src/api/auth-token.ts b/src/api/auth-token.ts index 079284b..631fbe5 100644 --- a/src/api/auth-token.ts +++ b/src/api/auth-token.ts @@ -5,9 +5,9 @@ import * as SecureStore from 'expo-secure-store'; * EncryptedSharedPreferences on Android). * * This module only handles *storage* of a bearer token. The actual auth flow (sign-in, token - * exchange, refresh) is a later phase, once the backend's bearer-auth endpoints are live — see - * AGENTS.md in the aha.chat repo for the corresponding backend work. `getAuthToken` currently - * always resolves to whatever was last persisted (or null), with no refresh logic. + * exchange, refresh) is a later phase, once the backend's bearer-auth endpoints are live. + * `getAuthToken` currently always resolves to whatever was last persisted (or null), with no + * refresh logic. */ const AUTH_TOKEN_KEY = 'chatbotx.authToken'; diff --git a/src/api/client.ts b/src/api/client.ts index c45d5b8..00055b3 100644 --- a/src/api/client.ts +++ b/src/api/client.ts @@ -21,7 +21,7 @@ const authMiddleware: Middleware = { }; /** - * Typed fetch client for the aha.chat REST API, generated from its OpenAPI spec. + * Typed fetch client for the ChatbotX REST API, generated from its OpenAPI spec. * Run `pnpm generate:api` to (re)generate src/api/generated/schema.ts against a running backend. * * All oRPC routes in the generated spec are served under `/api` (auth and the hand-rolled diff --git a/src/api/errors.ts b/src/api/errors.ts index b1caaed..446cbcd 100644 --- a/src/api/errors.ts +++ b/src/api/errors.ts @@ -1,11 +1,10 @@ /** - * Normalizes the oRPC error wire shape (`{ code, status, message, defined }` — verified live - * against the aha.chat builder dev server) into a discriminated union the rest of the app can - * switch on. `openapi-fetch` doesn't throw on HTTP errors — it resolves `{ data, error }` — so + * Normalizes the oRPC error wire shape (`{ code, status, message, defined }` — verified against + * the running backend) into a discriminated union the rest of the app can switch on. `openapi-fetch` doesn't throw on HTTP errors — it resolves `{ data, error }` — so * every mutation/query hook's `queryFn`/`mutationFn` must check `error` and * `throw new ApiError(error)` for react-query's QueryCache/MutationCache `onError` (wired in * lib/query-client.ts) to see it. Business-rule errors (ChatbotXException) surface their own - * `code`/`status`, e.g. `workspaceBlocked` at 402 — see aha.chat apps/builder/src/orpc.ts. + * `code`/`status`, e.g. `workspaceBlocked` at 402. */ export interface ApiErrorBody { code?: string; @@ -16,8 +15,8 @@ export interface ApiErrorBody { * Machine-readable payload some business-rule errors may attach. `data.reason` is a forward- * compatible hook for the 402 `workspaceBlocked` error: the backend does not send it today (as * of this writing `classifyWorkspaceBlockedReason` below is the only source of truth, matched - * by English substring against `message` — see plan's "backend ask" #2), but once it does, this - * field is read first and the substring match becomes pure fallback. No schema for `data` exists + * by English substring against `message`), but once it does, this field is read first and the + * substring match becomes pure fallback. No schema for `data` exists * yet in `src/api/generated/schema.ts`, so this stays loosely typed until the backend ships it. */ data?: { reason?: string }; diff --git a/src/api/generated/README.md b/src/api/generated/README.md index 4593836..983817a 100644 --- a/src/api/generated/README.md +++ b/src/api/generated/README.md @@ -1,8 +1,8 @@ -Generated by `pnpm generate:api` (scripts/generate-api-client.ts) from the aha.chat builder's +Generated by `pnpm generate:api` (scripts/generate-api-client.ts) from the ChatbotX builder's OpenAPI spec (`{API_BASE_URL}/api/spec.json`, the full oRPC router spec — override with `API_SPEC_PATH=/api/public-spec.json` for the workspace-token-only surface instead). Do not hand-edit files in this directory — they are overwritten on every regeneration. `schema.ts` is committed so the app builds without a live backend available. Re-run -`pnpm generate:api` against a running aha.chat builder instance to refresh it after backend API +`pnpm generate:api` against a running ChatbotX builder instance to refresh it after backend API changes. diff --git a/src/api/pagination.ts b/src/api/pagination.ts index 4d01aea..07a511e 100644 --- a/src/api/pagination.ts +++ b/src/api/pagination.ts @@ -2,8 +2,8 @@ * Cursor-pagination helpers shared by every `useInfiniteQuery` hook. The generated schema's * cursor-paginated list responses share the `{ data: T[], nextCursor: string | null, prevCursor: * string | null }` shape — verified directly against every cursor-paginated operation in - * src/api/generated/schema.ts (conversations/list and 5 others), NOT `{ items, nextCursor }` as - * originally assumed during Phase 0 pre-flight; corrected once Phase 3 hit the real shape. + * src/api/generated/schema.ts (conversations/list and 5 others); the real wire shape differs + * from the initial assumption of `{ items, nextCursor }`. */ export interface CursorPage<T> { data: T[]; diff --git a/src/app/(app)/conversations/[conversationId]/contact.tsx b/src/app/(app)/conversations/[conversationId]/contact.tsx index 857d623..4c7276f 100644 --- a/src/app/(app)/conversations/[conversationId]/contact.tsx +++ b/src/app/(app)/conversations/[conversationId]/contact.tsx @@ -8,8 +8,8 @@ import { ContactPanel } from '@/features/contacts/contact-panel'; import { useWorkspaceStore } from '@/stores/use-workspace-store'; import { useTheme } from '@/theme/use-theme'; -/** Replaces the Phase 4 minimal placeholder. Keyed by conversationId, so resolves contactId via - * the conversation-detail fetch before handing off to the shared ContactPanel. */ +/** Keyed by conversationId, so resolves contactId via the conversation-detail fetch before + * handing off to the shared ContactPanel. */ export default function ConversationContactScreen() { const { conversationId } = useLocalSearchParams<{ conversationId: string }>(); const workspaceId = useWorkspaceStore((state) => state.currentWorkspaceId); diff --git a/src/app/(app)/settings/members.tsx b/src/app/(app)/settings/members.tsx index ec20098..15c78d1 100644 --- a/src/app/(app)/settings/members.tsx +++ b/src/app/(app)/settings/members.tsx @@ -15,7 +15,7 @@ import { useWorkspaceMembersList } from '@/features/permissions/use-permissions' import { useWorkspaceStore } from '@/stores/use-workspace-store'; import { useTheme } from '@/theme/use-theme'; -/** Read-only members + teams screen (Phase 8). Teams is enterprise-tier and gracefully hides +/** Read-only members + teams screen. Teams is enterprise-tier and gracefully hides * itself on any fetch error (see use-teams.ts) rather than surfacing a broken state for * workspaces without the feature. */ export default function MembersScreen() { diff --git a/src/app/(auth)/change-password.tsx b/src/app/(auth)/change-password.tsx index aae1143..9506147 100644 --- a/src/app/(auth)/change-password.tsx +++ b/src/app/(auth)/change-password.tsx @@ -17,8 +17,8 @@ import { useTheme } from '@/theme/use-theme'; /** * Forced password-change screen — reached when the session user has `mustChangePassword: true` * (reseller-provisioned temporary password). Every other oRPC/session-authenticated call is - * blocked server-side until this completes (see aha.chat middlewares/auth.ts), so this screen - * has no way out except a successful change. + * blocked server-side until this completes, so this screen has no way out except a successful + * change. */ export default function ChangePasswordScreen() { const { t } = useTranslation(); diff --git a/src/components/ui/sheet.tsx b/src/components/ui/sheet.tsx index a56c494..94e6eb0 100644 --- a/src/components/ui/sheet.tsx +++ b/src/components/ui/sheet.tsx @@ -19,7 +19,7 @@ interface SheetProps extends PropsWithChildren { } /** Wraps @gorhom/bottom-sheet with our theme colors and a tap-to-dismiss backdrop. Used for - * filter/saved-replies/assignment sheets — see plan's "not routes" note on these. */ + * filter/saved-replies/assignment sheets, which are modal overlays rather than routes. */ export const Sheet = forwardRef<BottomSheet, SheetProps>(function Sheet( { children, snapPoints = ['50%', '90%'], onDismiss }, ref, diff --git a/src/components/ui/skeleton.tsx b/src/components/ui/skeleton.tsx index 3689aa6..75be8c5 100644 --- a/src/components/ui/skeleton.tsx +++ b/src/components/ui/skeleton.tsx @@ -68,7 +68,7 @@ const rowStyles = StyleSheet.create({ }); /** Loading-state row shaped like a conversation list row: avatar circle + two lines of text + - * a small trailing meta block. Used by the inbox skeleton list (Phase 3). */ + * a small trailing meta block. Used by the inbox skeleton list. */ function ConversationRow() { return ( <View style={rowStyles.row}> @@ -106,7 +106,7 @@ function MessageRow({ align = 'start' }: { align?: 'start' | 'end' }) { } /** Preset loading-row shapes for the three list contexts that need skeleton placeholders: - * conversations, contacts, and chat messages. Generic enough for Phase 3/4/5 list screens to + * conversations, contacts, and chat messages. Generic enough for any list screen to * `.map()` a handful of these while data loads. */ export const SkeletonRow = { Conversation: ConversationRow, diff --git a/src/components/workspace-blocked-gate.tsx b/src/components/workspace-blocked-gate.tsx index 0e9789a..0ea461a 100644 --- a/src/components/workspace-blocked-gate.tsx +++ b/src/components/workspace-blocked-gate.tsx @@ -15,9 +15,9 @@ interface WorkspaceBlockedGateProps { /** * Full-screen state for a 402 `workspaceBlocked` error on a workspace-scoped screen query (e.g. - * the conversations list itself failing, not just a composer send — Phase 4 already handles the - * send-time case locally with quota-banner.tsx). Distinguishes MAC-limit vs trial-expired copy per - * the plan, with a "switch workspace" escape hatch mirroring the settings screen's own switch flow. + * the conversations list itself failing, not just a composer send — quota-banner.tsx already + * handles the send-time case locally). Distinguishes MAC-limit vs trial-expired copy, with a + * "switch workspace" escape hatch mirroring the settings screen's own switch flow. */ export function WorkspaceBlockedGate({ reason, message }: WorkspaceBlockedGateProps) { const { t } = useTranslation(); diff --git a/src/dev/gallery-content.tsx b/src/dev/gallery-content.tsx index fa87f59..6e5d057 100644 --- a/src/dev/gallery-content.tsx +++ b/src/dev/gallery-content.tsx @@ -30,8 +30,8 @@ import { useSettingsStore } from '@/stores/use-settings-store'; import { useTheme } from '@/theme/use-theme'; /** - * Visual smoke-test surface for every Phase 2 UI primitive, in various states, with a light/dark - * toggle. Not a polished screen — the "storybook-lite" the plan asks for. Split into its own + * Visual smoke-test surface for every UI primitive, in various states, with a light/dark + * toggle. Not a polished screen — a "storybook-lite". Split into its own * module (rather than living inline in the `(dev)/gallery` route file) so its 24 primitive imports * are only pulled into the bundle via the lazy `require()` in that route's `__DEV__` branch, * instead of shipping in every production build's static import graph. diff --git a/src/features/chat/api/send-message-multipart.ts b/src/features/chat/api/send-message-multipart.ts index d8fa66a..5153778 100644 --- a/src/features/chat/api/send-message-multipart.ts +++ b/src/features/chat/api/send-message-multipart.ts @@ -5,8 +5,7 @@ import { env } from '@/config/env'; /** * Chat message attachments go through the SAME message-create endpoint as text * (`POST /workspaces/{workspaceId}/conversations/{conversationId}/messages`), but the generated - * TS types are wrong for this case: the backend's real Zod schema - * (aha.chat apps/builder/src/features/messages/schema/mutation.ts) types `files` as + * TS types are wrong for this case: the backend's real Zod schema types `files` as * `z.array(z.instanceof(File)).min(1)`, which openapi-typescript cannot express — it collapsed to * `files?: unknown[]` under `application/json`. The real wire format for a request WITH files is * `multipart/form-data` (the backend's `createMessage` handler calls `uploadMultipleFiles` on diff --git a/src/features/chat/api/use-send-message.ts b/src/features/chat/api/use-send-message.ts index 1124403..303b5af 100644 --- a/src/features/chat/api/use-send-message.ts +++ b/src/features/chat/api/use-send-message.ts @@ -293,7 +293,7 @@ export function useSendMessage(workspaceId: string, conversationId: string) { // The sender's own optimistic bubble is reconciled here by simply refetching — the newly // created message (with its real server id) will replace the optimistic entry once the list // refetches, since clientId is embedded on the real message too and message-bubble.tsx keys - // by clientId when present. Realtime (Phase 6) handles the same reconciliation for OTHER + // by clientId when present. Realtime handlers handle the same reconciliation for OTHER // clients viewing this conversation; this path only needs to work for the sender themself. // Flow sends (no optimistic entry) rely on this same invalidation/refetch (or realtime) to // surface the flow's resulting messages. diff --git a/src/features/chat/components/message-bubble.tsx b/src/features/chat/components/message-bubble.tsx index 78d8069..928800a 100644 --- a/src/features/chat/components/message-bubble.tsx +++ b/src/features/chat/components/message-bubble.tsx @@ -30,8 +30,8 @@ interface MessageBubbleProps { /** senderType 'contact' is inbound (left); 'bot' gets its own violet-tinted variant; 'system' is a * centered activity chip, not a side bubble; everything else ('user'/'api') is a regular outbound - * agent bubble. There's no dedicated `isOutbound` field on the schema, so this mirrors the legacy - * app's bubble-side logic. */ + * agent bubble. There's no dedicated `isOutbound` field on the schema, so this is derived from + * senderType instead. */ function senderVariant(message: Message): SenderVariant { if (message.senderType === 'contact') return 'contact'; if (message.senderType === 'bot') return 'bot'; diff --git a/src/features/chat/components/typing-indicator.tsx b/src/features/chat/components/typing-indicator.tsx index 278da9a..cd61b29 100644 --- a/src/features/chat/components/typing-indicator.tsx +++ b/src/features/chat/components/typing-indicator.tsx @@ -63,8 +63,8 @@ interface TypingIndicatorProps { conversationId: string; } -/** Reads `typingByConversation` from the chat store — set by the realtime `typing` event handler - * (Phase 6). Renders nothing when not typing; a `t('chat.typing')`-labeled row with 3 staggered +/** Reads `typingByConversation` from the chat store — set by the realtime `typing` event handler. + * Renders nothing when not typing; a `t('chat.typing')`-labeled row with 3 staggered * bouncing dots otherwise. */ export function TypingIndicator({ conversationId }: TypingIndicatorProps) { const { t } = useTranslation(); diff --git a/src/features/chat/stores/use-chat-store.ts b/src/features/chat/stores/use-chat-store.ts index ef1aef6..b98ae79 100644 --- a/src/features/chat/stores/use-chat-store.ts +++ b/src/features/chat/stores/use-chat-store.ts @@ -7,7 +7,7 @@ export type ComposerMode = { type: 'edit'; message: Message } | { type: 'reply'; interface ChatStoreState { /** conversationId -> is the other side typing (set by realtime; use-realtime-handlers.ts pairs * every `setTyping(id, true, seconds)` with a typing-timers.ts expiry so this never sticks - * forever if an explicit `typing: false` follow-up is dropped — see plan Phase 6). */ + * forever if an explicit `typing: false` follow-up is dropped). */ typingByConversation: Record<string, boolean>; /** conversationId -> in-progress composer draft text, so switching away and back preserves it. */ draftsByConversation: Record<string, string>; diff --git a/src/features/contacts/api/use-contact-tags.ts b/src/features/contacts/api/use-contact-tags.ts index 94dde0c..de3d451 100644 --- a/src/features/contacts/api/use-contact-tags.ts +++ b/src/features/contacts/api/use-contact-tags.ts @@ -28,8 +28,8 @@ export function useWorkspaceTags(workspaceId: string | null) { /** * `contactsAPIs.addContactTagAuthenticatedAPI` — `POST /workspaces/{workspaceId}/contacts/tags`, - * body `{ ids: string[], tags: string[] }`: batch by CONTACT id (mirrors Phase 3's assign-by-contact - * pattern), `tags` is an array of tag ids. + * body `{ ids: string[], tags: string[] }`: batch by CONTACT id (mirrors the assign-by-contact + * pattern used elsewhere), `tags` is an array of tag ids. * * Optimistic: takes the full tag object (not just an id) so `onMutate` can push it straight into * the cached contact-detail's `tags` array and the UI shows the new chip immediately, instead of diff --git a/src/features/contacts/api/use-contacts-infinite.ts b/src/features/contacts/api/use-contacts-infinite.ts index d937363..d32ae07 100644 --- a/src/features/contacts/api/use-contacts-infinite.ts +++ b/src/features/contacts/api/use-contacts-infinite.ts @@ -12,10 +12,10 @@ import type { operations } from '@/api/generated/schema'; * the generated schema. `api/pagination.ts`'s CursorPage helpers don't apply here; this hook * drives `useInfiniteQuery` with a page-number `pageParam` instead. * - * KNOWN LIMITATION (Phase 8): unlike conversations-list, this endpoint has no first-class + * KNOWN LIMITATION: unlike conversations-list, this endpoint has no first-class * `assignedId` field — the only way to filter contacts by assignment is the generic `contactFilter` * condition-builder DSL, which this app deliberately doesn't expose (see filter-sheet.tsx's own - * note on the conversations side). That means `onlyAssignedContacts` (Phase 6's usePermissions) + * note on the conversations side). That means `onlyAssignedContacts` (from `usePermissions`) * can be enforced on the conversations tab but NOT on this contacts directory — a restricted * member can browse the full contact list here even though their conversation view is scoped to * their own assignments. Closing this gap would require building the `contactFilter` DSL query, diff --git a/src/features/conversations/api/use-conversations-infinite.ts b/src/features/conversations/api/use-conversations-infinite.ts index 4c9b270..e1c30b0 100644 --- a/src/features/conversations/api/use-conversations-infinite.ts +++ b/src/features/conversations/api/use-conversations-infinite.ts @@ -30,7 +30,7 @@ export type ConversationListItem = ListConversationsResponse['data'][number]; const PER_PAGE = 30; /** - * `onlyAssignedContacts` (Phase 6's usePermissions) forces `assignedId` to the current user's own + * `onlyAssignedContacts` (from `usePermissions`) forces `assignedId` to the current user's own * id, overriding any manual "assigned to" filter — a restricted member must not be able to browse * other agents' conversations by picking a different filter value. Unrestricted members keep * whatever the filter store holds (including "unassigned"/other-user values). diff --git a/src/features/conversations/api/use-inbox-unread-count.ts b/src/features/conversations/api/use-inbox-unread-count.ts index 100cca1..66aaa1b 100644 --- a/src/features/conversations/api/use-inbox-unread-count.ts +++ b/src/features/conversations/api/use-inbox-unread-count.ts @@ -16,7 +16,7 @@ import type { * into view / fetched this session) — it is NOT a true server-side unread total. A conversation * that's unread but was never fetched into any cached list page (e.g. page 3 the user hasn't * scrolled to yet) is invisible to this count. There is no unread-count endpoint on the backend - * yet (see plan's "Backend asks" #3) — this is the best available approximation until one exists. + * yet — this is the best available approximation until one exists. * `Badge`'s own count-mode caps display at "99+", so no separate capping is needed here. */ function countUnreadFromCache( diff --git a/src/features/conversations/components/filter-sheet.tsx b/src/features/conversations/components/filter-sheet.tsx index 937b7c1..4fa5ec9 100644 --- a/src/features/conversations/components/filter-sheet.tsx +++ b/src/features/conversations/components/filter-sheet.tsx @@ -66,7 +66,7 @@ interface FilterSheetProps { * `listConversationsByPOSTAuthenticatedAPI` accepts (see use-conversation-filters.ts). The * advanced `contactFilter` condition-builder from that schema is not exposed here — it's a * large audience-builder DSL that belongs to a dedicated "advanced search" surface, not this - * quick filter sheet (mirrors the legacy app's AdvancedSearchModal vs QuickFilter split). */ + * quick filter sheet. */ export function FilterSheet({ sheetRef }: FilterSheetProps) { const { t } = useTranslation(); const { spacing } = useTheme(); diff --git a/src/features/conversations/lib/conversation-status.ts b/src/features/conversations/lib/conversation-status.ts index bb7aedf..0da3eb8 100644 --- a/src/features/conversations/lib/conversation-status.ts +++ b/src/features/conversations/lib/conversation-status.ts @@ -1,7 +1,7 @@ import type { ConversationListItem } from '@/features/conversations/api/use-conversations-infinite'; /** - * Bot-active derivation, matching the backend's own rule (see plan Context: `isBotActive = + * Bot-active derivation, matching the backend's own rule (`isBotActive = * botEnabled || botResumeAt < now`): the bot is considered "active" either because it's * explicitly enabled, or because a previous 24h disable-pause (`botResumeAt`) has already * elapsed — the backend auto-resumes the bot at that point even though `botEnabled` itself only diff --git a/src/features/permissions/use-permissions.ts b/src/features/permissions/use-permissions.ts index 339aea9..7500716 100644 --- a/src/features/permissions/use-permissions.ts +++ b/src/features/permissions/use-permissions.ts @@ -8,10 +8,10 @@ import { useAuthStore } from '@/stores/use-auth-store'; /** * `GET /workspaces/{workspaceId}/members` (`workspaceMembersAPI.listWorkspaceMembersAuthenticatedAPI` * in src/api/generated/schema.ts) embeds a `permissions` object on every member row with EXACTLY - * these field names — no mapping needed against the plan's shorthand: + * these field names: * `{ superAdmin, analytics, flows, contacts, onlyAssignedContacts, emailAndPhone, broadcast, ecommerce }`. * This hook finds the current signed-in user's own member row (matched by `userId` against the - * auth store) and exposes its permission bits. Shares the same members-list query the Phase 3 + * auth store) and exposes its permission bits. Shares the same members-list query the * assignment-sheet already fetches (`queryKeys.ws.members.list`), so no extra request in practice. */ export interface WorkspacePermissions { diff --git a/src/features/settings/api/use-teams.ts b/src/features/settings/api/use-teams.ts index a6692ce..4c44384 100644 --- a/src/features/settings/api/use-teams.ts +++ b/src/features/settings/api/use-teams.ts @@ -6,10 +6,10 @@ import { queryKeys } from '@/api/query-keys'; /** * `GET /workspaces/{workspaceId}/inbox-teams` (`inboxTeamsAPI.listInboxTeamsAuthenticatedAPI`) — - * an enterprise-tier feature (mounted from `@/enterprise/features/inbox-teams` in aha.chat's - * router). Not every workspace/plan has this — the members screen treats any error here as "no - * teams" (hides the section) rather than showing a broken error state, since a 403/404 from an - * unlicensed workspace is an expected outcome, not a real failure to surface to the user. + * an enterprise-tier feature; not every workspace/plan has it — the members screen treats any + * error here as "no teams" (hides the section) rather than showing a broken error state, since a + * 403/404 from an unlicensed workspace is an expected outcome, not a real failure to surface to + * the user. */ export function useTeamsList(workspaceId: string) { return useQuery({ diff --git a/src/features/workspaces/api/use-workspaces.ts b/src/features/workspaces/api/use-workspaces.ts index 488c6f6..71d6d18 100644 --- a/src/features/workspaces/api/use-workspaces.ts +++ b/src/features/workspaces/api/use-workspaces.ts @@ -11,8 +11,8 @@ export interface Workspace { brandColor: string; } -/** Lists workspaces the current user is a member of (`GET /users/me/workspaces`, Phase 0 backend - * addition). Global key — not workspace-scoped, since it lists across workspaces. */ +/** Lists workspaces the current user is a member of (`GET /users/me/workspaces`). + * Global key — not workspace-scoped, since it lists across workspaces. */ export function useWorkspaces() { return useQuery({ queryKey: queryKeys.workspaces(), diff --git a/src/i18n/locales.ts b/src/i18n/locales.ts index e0b276b..1b3df78 100644 --- a/src/i18n/locales.ts +++ b/src/i18n/locales.ts @@ -60,8 +60,8 @@ function matchesLocaleTag(normalizedValue: string, localeTag: string) { } /** - * Verbatim port of the web app's resolveLocale (aha.chat apps/builder/src/i18n/config.ts) so - * device-language resolution matches the web product exactly (pt/zh normalization, RTL locales). + * Verbatim port of the web app's resolveLocale so device-language resolution matches the web + * product exactly (pt/zh normalization, RTL locales). */ export function resolveLocale(value: string | undefined): SupportedLanguage { if (!value) { diff --git a/src/lib/notification-tap.ts b/src/lib/notification-tap.ts index c697353..c2c7185 100644 --- a/src/lib/notification-tap.ts +++ b/src/lib/notification-tap.ts @@ -5,8 +5,8 @@ import { useAuthStore } from '@/stores/use-auth-store'; import { useWorkspaceStore } from '@/stores/use-workspace-store'; /** - * Data payload the backend attaches to a push notification (see plan Workstream 4.2 — the worker - * sends `{ workspaceId, conversationId, messageId }` alongside title/body). All optional because + * Data payload the backend attaches to a push notification (the worker sends + * `{ workspaceId, conversationId, messageId }` alongside title/body). All optional because * this same handler also has to tolerate a malformed/older payload without throwing. */ interface NotificationTapData { diff --git a/src/lib/notifications.ts b/src/lib/notifications.ts index 77c711e..dd7520f 100644 --- a/src/lib/notifications.ts +++ b/src/lib/notifications.ts @@ -64,7 +64,7 @@ export type PushTokenResult = * Requests notification permission (if not already granted) and returns an Expo push token. * `unsupported` covers both simulators/emulators (`Device.isDevice` is false — remote push * doesn't work there) and web. Requires `Constants.expoConfig.extra.eas.projectId`, which only - * exists after `eas init` has run — see plan Workstream 5.2. + * exists after `eas init` has run. */ export async function getPushTokenAsync(): Promise<PushTokenResult> { if (!Device.isDevice) { diff --git a/src/realtime/apply-conversation-events.ts b/src/realtime/apply-conversation-events.ts index 7ccd9a2..8171a34 100644 --- a/src/realtime/apply-conversation-events.ts +++ b/src/realtime/apply-conversation-events.ts @@ -50,7 +50,7 @@ export function applyConversationUpdated( } /** `contactBlocked`/`contactUnblocked` patch the embedded `contact.blockedAt` field on every - * conversation row for that contact — a contact can back multiple conversations (see Phase 3's + * conversation row for that contact — a contact can back multiple conversations (see the * assign-by-contact applier for the same one-contact-many-conversations pattern). */ export function applyContactBlockState( current: InfiniteConversationsData, @@ -76,8 +76,8 @@ export function applyContactBlockState( }; } -/** Patches a standalone `queryKeys.ws.contacts.detail` cache entry's `blockedAt` field — added in - * Phase 6 alongside the contacts feature. `applyContactBlockState` above only reaches the +/** Patches a standalone `queryKeys.ws.contacts.detail` cache entry's `blockedAt` field — added + * alongside the contacts feature. `applyContactBlockState` above only reaches the * conversations-list cache's embedded contact object; this covers the separate contact-detail * query the contact panel reads (use-contact-detail.ts), which has its own `blockedAt` field at * the top level (not nested under `contact`). Callers scan for a query keyed diff --git a/src/realtime/apply-message-events.ts b/src/realtime/apply-message-events.ts index 0933c4f..5c39dd9 100644 --- a/src/realtime/apply-message-events.ts +++ b/src/realtime/apply-message-events.ts @@ -92,10 +92,9 @@ export function applyMessageDeleted( } /** - * Patches the edited text only. The web app's equivalent handler (`updateMessageText` in - * ../aha.chat apps/builder/src/features/chat/store/chat-store.ts) also reconstructs a full - * attachment object from `newAttachmentPath`/`newAttachmentMimeType`/etc when an attachment was - * added/replaced/removed during the edit — deliberately NOT mirrored here, since Phase 4's own + * Patches the edited text only. The web app's equivalent handler (`updateMessageText`) also + * reconstructs a full attachment object from `newAttachmentPath`/`newAttachmentMimeType`/etc when an attachment was + * added/replaced/removed during the edit — deliberately NOT mirrored here, since this app's own * edit-message UI is itself a stub (no attachment-edit affordance exists yet to trigger this * server-side), so building the full attachment-reconstruction logic now would have nothing to * exercise it. Revisit once message edit UI grows an attachment-replace flow. @@ -148,9 +147,8 @@ export function applyMessageFailed( }; } -/** Mirrors the web app's `assignMessageCommentId` handler exactly (../aha.chat - * apps/builder/src/features/chat/store/chat-store.ts) — the comment id lands on `sourceId`, not - * `parentId` (parentId means something different: thread/reply linkage). */ +/** Mirrors the web app's `assignMessageCommentId` handler exactly — the comment id lands on + * `sourceId`, not `parentId` (parentId means something different: thread/reply linkage). */ export function applyMessageIdAssigned( current: InfiniteMessagesData, event: RealtimeEventMessageIdAssigned, diff --git a/src/realtime/events.ts b/src/realtime/events.ts index 5104c14..394c073 100644 --- a/src/realtime/events.ts +++ b/src/realtime/events.ts @@ -1,13 +1,9 @@ /** - * MIRRORED TYPES — kept in manual sync with `aha.chat/packages/partysocket-config/src/schemas.ts`. + * MIRRORED TYPES — mirrors the backend's realtime event schema (no shared package; this repo is + * standalone, so there is no live cross-repo import). * - * This app lives in a separate git repo from aha.chat (not a pnpm workspace member), so there is - * no live cross-repo import. Do NOT import aha.chat's `lib.ts` (server-side realtime wiring) — - * only these type-only definitions, mirrored by hand. - * - * When the source file changes, re-copy its `RealtimeEventType` and `RealtimeEvent*` shapes here. - * Last synced against aha.chat's current `packages/partysocket-config/src/schemas.ts` (Phase 5, - * 2026-08-21) — added conversationCreated/conversationUpdated, per the plan's pre-flight note. + * When the backend's event schema changes, re-copy `RealtimeEventType` / `RealtimeEvent*` here. + * Last synced 2026-08-21 (added conversationCreated/conversationUpdated). */ export const RealtimeEventType = { diff --git a/src/realtime/one-time-token.ts b/src/realtime/one-time-token.ts index 68822c1..c8f1c16 100644 --- a/src/realtime/one-time-token.ts +++ b/src/realtime/one-time-token.ts @@ -1,9 +1,9 @@ import { env } from '@/config/env'; /** - * `GET /api/auth/one-time-token/generate` — better-auth's `oneTimeToken()` plugin (enabled - * server-side, confirmed in ../aha.chat packages/auth/src/server.ts). Hand-rolled fetch, same - * pattern as api/auth-endpoints.ts, since this isn't part of the oRPC-generated apiClient. + * `GET /api/auth/one-time-token/generate` — better-auth's `oneTimeToken()` plugin (enabled on the + * backend). Hand-rolled fetch, same pattern as api/auth-endpoints.ts, since this isn't part of + * the oRPC-generated apiClient. */ export class OneTimeTokenError extends Error { constructor(message: string) { diff --git a/src/realtime/realtime-provider.tsx b/src/realtime/realtime-provider.tsx index d5d06fe..bb32027 100644 --- a/src/realtime/realtime-provider.tsx +++ b/src/realtime/realtime-provider.tsx @@ -15,16 +15,14 @@ import { resetConnectionStore, useConnectionStore } from './use-connection-store import { clearPendingConversationInvalidates, useRealtimeHandlers } from './use-realtime-handlers'; /** - * Real implementation, replacing the Phase 0-4 passthrough stub. Connects to the `workspaces` - * PartyKit party, room = workspaceId (mirrors ../aha.chat apps/builder/src/features/chat/chat-realtime.tsx - * and apps/realtime/src/parties/workspaces.ts). + * Connects to the `workspaces` PartyKit party, room = workspaceId (mirrors the web app's + * realtime connection). * * `?domain=` is REQUIRED on every mobile connection: the realtime server verifies the one-time * token by resolving a tenant origin from either the WebSocket request's `Origin` header (browsers * send this automatically) or a `?domain=<https url>` query param — React Native's WebSocket * implementation sends no `Origin` header at all, so without `domain` the server has nothing to - * verify the token against and rejects the connection (verified by reading - * ../aha.chat apps/realtime/src/lib/auth.ts `resolveVerificationOrigin`). The value must be the + * verify the token against and rejects the connection. The value must be the * SAME origin that serves `/api/auth/one-time-token/verify` — i.e. `env.apiBaseUrl` — not the * realtime server's own host. */ diff --git a/src/realtime/use-realtime-handlers.ts b/src/realtime/use-realtime-handlers.ts index 9f7f7c1..2af0f51 100644 --- a/src/realtime/use-realtime-handlers.ts +++ b/src/realtime/use-realtime-handlers.ts @@ -70,7 +70,7 @@ export function clearPendingConversationInvalidates(): void { * and letting each applier no-op on a non-matching cache is safe and is left unchanged. * * Conversation events patch every cached conversations-list query (any filter combination) for - * this workspace, same as Phase 3's action-mutation cache patches. + * this workspace, same as the conversation-action mutations' cache patches. */ export function useRealtimeHandlers(queryClient: QueryClient, workspaceId: string) { return useCallback( diff --git a/src/stores/use-workspace-store.ts b/src/stores/use-workspace-store.ts index 1f2cc92..1963185 100644 --- a/src/stores/use-workspace-store.ts +++ b/src/stores/use-workspace-store.ts @@ -7,9 +7,9 @@ import { createJSONStorage, persist } from 'zustand/middleware'; * state (not server state) — the actual list of workspaces the user belongs to comes from * TanStack Query (`features/workspaces/api/use-workspaces.ts`). * - * `currentWorkspaceId` mirrors the bigint-as-string id convention used throughout the ChatbotX - * API (see AGENTS.md invariant #10 in the aha.chat repo). Persisted so the app reopens on the - * last-selected workspace instead of forcing the picker every launch. + * `currentWorkspaceId` mirrors the bigint-as-string id convention — ids are bigint-as-string + * throughout the API. Persisted so the app reopens on the last-selected workspace instead of + * forcing the picker every launch. */ interface WorkspaceStoreState { currentWorkspaceId: string | null; diff --git a/src/theme/tokens.test.ts b/src/theme/tokens.test.ts index 32be708..ae31759 100644 --- a/src/theme/tokens.test.ts +++ b/src/theme/tokens.test.ts @@ -33,7 +33,7 @@ const NEW_SEMANTIC_COLOR_KEYS = [ 'scrim', ] as const; -// Phase 7 removed `unreadDot`, `bubbleOutbound(Text)`, `bubbleInbound(Text)`; a later cleanup +// Removed tokens: `unreadDot`, `bubbleOutbound(Text)`, `bubbleInbound(Text)`; a later cleanup // removed the remaining flat-key aliases (`background`, `surface`, `surfaceElevated`, `border`, // `text`, `primary`, `primaryForeground`) once every call site migrated to the semantic keys // above — see tokens.ts's doc comment. @@ -78,7 +78,7 @@ describe('colorTokens', () => { 'no longer exposes any deprecated flat-key alias for %s', (scheme) => { const tokens = colorTokens[scheme] as Record<string, unknown>; - // Phase 7 removals: + // Removed tokens: expect(tokens.unreadDot).toBeUndefined(); expect(tokens.bubbleOutbound).toBeUndefined(); expect(tokens.bubbleOutboundText).toBeUndefined(); diff --git a/src/theme/tokens.ts b/src/theme/tokens.ts index e1a46dc..d8f2d57 100644 --- a/src/theme/tokens.ts +++ b/src/theme/tokens.ts @@ -103,7 +103,7 @@ const darkChannel = { } as const; /** Deterministic pastel fills for avatar-fallback tinting (initials on a color picked by hashing - * the contact id/name — see avatar.tsx in Phase 2) — same 6 hues in both schemes, just retuned + * the contact id/name — see avatar.tsx) — same 6 hues in both schemes, just retuned * for contrast against each canvas. */ const lightAvatarPalette = [ '#ffd6d1', // coral From 2e9f5d182c2164148963b7834c0084bbd6a77972 Mon Sep 17 00:00:00 2001 From: Real Codesiman <realcodesiman@gmail.com> Date: Fri, 28 Aug 2026 09:55:47 +0700 Subject: [PATCH 4/5] fix(ci): format pr-1-review.md with prettier --- .claude/PRPs/reviews/pr-1-review.md | 42 ++++++++++++++--------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/.claude/PRPs/reviews/pr-1-review.md b/.claude/PRPs/reviews/pr-1-review.md index 8b43857..74bef80 100644 --- a/.claude/PRPs/reviews/pr-1-review.md +++ b/.claude/PRPs/reviews/pr-1-review.md @@ -20,7 +20,7 @@ leftovers — all addressed below. - **`src/features/chat/api/use-send-message.ts:161,199` (A1)** — `mutationFn` and `onMutate` each called `generateClientId()` independently, so the optimistic bubble and the outgoing request body - carried *different* clientIds — a duplicate bubble until refetch, an orphaned upload-progress + carried _different_ clientIds — a duplicate bubble until refetch, an orphaned upload-progress entry, and the retry-scroll signal never firing. **Fix:** normalize `clientId` once in a `mutate`/`mutateAsync` wrapper before React Query hands params to `onMutate`/`mutationFn`. Added a regression test asserting request-body clientId === cached optimistic row's clientId. @@ -38,7 +38,7 @@ leftovers — all addressed below. screen forever; `useAuthBootstrap` had no try/catch around `getSession`, so a network failure during session validation looked identical to an invalid token (no distinction between "can't verify" and "verified invalid"). **Fix:** added `.catch`, `fontsLoaded || fontError` gate, and a - try/catch that treats a validation *failure* as signed-out-for-this-launch without clearing + try/catch that treats a validation _failure_ as signed-out-for-this-launch without clearing SecureStore (so the next successful bootstrap can still sign back in with the same token). Also added `SplashScreen.setOptions({ fade: true, duration: 300 })` and a root `ErrorBoundary` export (expo-router convention) so an uncaught render error doesn't white-screen the app. @@ -86,7 +86,7 @@ leftovers — all addressed below. - **`src/features/contacts/pii-mask.ts` (A9)** — `maskPhone` sized its asterisk run off the raw string length (including formatting characters like `-`/`(`/`)`/spaces) instead of the actual digit count, so a formatted number like `(555) 123-4567` masked with 4 extra asterisks it didn't - need; `maskEmail` split on the *first* `@`, which breaks for a (rare but legal) quoted local-part + need; `maskEmail` split on the _first_ `@`, which breaks for a (rare but legal) quoted local-part containing `@`, and didn't guard an empty local-part. **Fix:** `maskPhone` sizes off `digits`; `maskEmail` splits on `lastIndexOf('@')` and guards `atIndex <= 0`. Updated `pii-mask.test.ts` with the corrected expectations plus new empty-local-part and @@ -114,7 +114,7 @@ leftovers — all addressed below. warning noted in the baseline. **Fix:** exported `clearPendingConversationInvalidates()`, called alongside `clearAllTypingTimers()` in `realtime-provider.tsx`'s unmount effect. Verified: a full suite run right after this fix showed **no** exit warning. (See "Known issues" below — a - *different*, apparently pre-existing leak resurfaced later, isolated to + _different_, apparently pre-existing leak resurfaced later, isolated to `use-send-message.test.tsx`; not chased further, see notes.) - **`src/realtime/use-realtime-handlers.ts:88-94` (A13)** — `messageCreated` for the conversation @@ -125,21 +125,21 @@ leftovers — all addressed below. not-active case. - **`src/realtime/apply-conversation-events.ts:141-144` (A14)** — hoisted the matched conversation - row to the front of *whichever page it was found on*, not just page 0 — for a row on a later + row to the front of _whichever page it was found on_, not just page 0 — for a row on a later page, this duplicated it across the client's merged view and corrupted cursor pagination. **Fix:** only hoist when `pageIndex === 0`; a later-page match is patched in place. Added a multi-page regression test. - **`src/realtime/realtime-provider.tsx:83-91` (A15)** — `resetConnectionStore()` ran in a post-mount `useEffect`, but `usePartySocket` starts connecting immediately on render — a fast - `onOpen` could fire and flip the store to `'open'` *before* the effect's reset ran, clobbering it + `onOpen` could fire and flip the store to `'open'` _before_ the effect's reset ran, clobbering it back to `'connecting'` and leaving the connection banner stuck. **Fix:** reset moved to run exactly once per mounted instance via `useState`'s lazy initializer (not a ref read/write during render — the project's `reactCompiler: true` setting disallows that pattern; confirmed by ESLint during this review, see "Caught during review" below). - **`src/realtime/parse-event.ts:65` (A16)** — `messageFailed`'s validator rejected `error === - undefined` (only `null` or a string passed), so an event that *omitted* the field entirely (valid +undefined` (only `null` or a string passed), so an event that _omitted_ the field entirely (valid per the type, which already allowed optional) failed validation and the whole event was dropped silently. **Fix:** `isOptionalNullableString`; also widened `RealtimeEventMessageFailed.error` to `string | null | undefined` in `events.ts` to match. @@ -151,7 +151,7 @@ leftovers — all addressed below. - **`app.config.ts` + `src/i18n/reconcile-rtl.ts` (A18)** — expo-updates was installed but not wired up (no `runtimeVersion`/`updates`/plugin entry); the RTL bootstrap called `forceRTL` - *before* checking `__DEV__`, doing an unnecessary native call it then discarded; and the silent + _before_ checking `__DEV__`, doing an unnecessary native call it then discarded; and the silent bootstrap reload path could pop a blocking `Alert` if `!Updates.isEnabled`, from a code path with nothing rendered yet to explain the alert's context to the user. **Fix:** added `runtimeVersion: { policy: 'appVersion' }`, `updates.url`, and the `'expo-updates'` plugin; @@ -199,7 +199,7 @@ leftovers — all addressed below. - Consolidated `flattenMessagePages`/`flattenConversationPages` (each a one-line wrapper around `flattenPages`) — deleted both, call sites now use `flattenPages` directly. - Removed stale plan-artifact comments ("Phase N", references to a plan/report) opportunistically - in every file this review otherwise touched; a repo-wide sweep of files *not* otherwise touched + in every file this review otherwise touched; a repo-wide sweep of files _not_ otherwise touched was out of scope for this pass — grep `Phase [0-9]\|see report\|see plan` in `src/` for the remainder if a follow-up wants to finish that sweep. - `isWorkspaceListQuery`-style consolidation (query-keys.ts helper unifying the @@ -248,7 +248,7 @@ leftovers — all addressed below. - `justSentMessageId` in the same file was derived from `sendMessage.isSuccess` — which stays `true` for the mutation's entire lifetime until the next `mutate()` call, so it pointed at a stale clientId indefinitely between sends. **More severe than the plan's phrasing suggested**: - this `useSendMessage(...)` instance is used *only* for the retry path in this screen — the + this `useSendMessage(...)` instance is used _only_ for the retry path in this screen — the composer instantiates its **own separate** `useSendMessage(...)` for normal sends, so `sendMessage.data` here never reflected a composer send at all; the force-scroll-to-own-message behavior was effectively broken for every non-retry send. **Fix:** added @@ -330,25 +330,25 @@ leftovers — all addressed below. ## Verification -| Check | Result | -|---|---| -| `pnpm typecheck` | ✅ clean | -| `pnpm lint` | ✅ 0 errors (8 pre-existing warnings in `jest.setup.ts`/`scripts/i18n-sync.ts`/`.expo/types`, none in touched files) — **1 real error caught and fixed during this review**: `realtime-provider.tsx`'s ref-read-during-render (see A15) violated `reactCompiler: true`'s stricter ref-access rule; fixed via `useState`'s lazy initializer instead of a ref | -| `pnpm format:check` | ✅ clean | -| `pnpm test` | ✅ 41 suites / 293 tests passing (up from 39/279 at baseline — new tests for A1's clientId-parity, A5's delete-rollback/attributes-invalidate, A9's pii-mask cases, A14's page-0-only hoist, pending-deep-link's path matching) | -| `npx expo-doctor` | 2 pre-existing, unrelated failures: 14 packages with patch-version drift from the exact SDK-pinned versions, and a duplicate `expo-file-system` resolution — neither touched by this pass, not silently bumped (native-build-affecting, out of scope for a review-fix pass) | -| `npx expo config --type public` | shows `updates`/`runtimeVersion`, no `web` block | -| `git status` | only unstaged modifications, no staged changes, no unexpected untracked files beyond the 4 new files this pass added | +| Check | Result | +| ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `pnpm typecheck` | ✅ clean | +| `pnpm lint` | ✅ 0 errors (8 pre-existing warnings in `jest.setup.ts`/`scripts/i18n-sync.ts`/`.expo/types`, none in touched files) — **1 real error caught and fixed during this review**: `realtime-provider.tsx`'s ref-read-during-render (see A15) violated `reactCompiler: true`'s stricter ref-access rule; fixed via `useState`'s lazy initializer instead of a ref | +| `pnpm format:check` | ✅ clean | +| `pnpm test` | ✅ 41 suites / 293 tests passing (up from 39/279 at baseline — new tests for A1's clientId-parity, A5's delete-rollback/attributes-invalidate, A9's pii-mask cases, A14's page-0-only hoist, pending-deep-link's path matching) | +| `npx expo-doctor` | 2 pre-existing, unrelated failures: 14 packages with patch-version drift from the exact SDK-pinned versions, and a duplicate `expo-file-system` resolution — neither touched by this pass, not silently bumped (native-build-affecting, out of scope for a review-fix pass) | +| `npx expo config --type public` | shows `updates`/`runtimeVersion`, no `web` block | +| `git status` | only unstaged modifications, no staged changes, no unexpected untracked files beyond the 4 new files this pass added | **Known issue not resolved:** the Jest "worker failed to exit gracefully" warning is back after a clean run right after A12's fix showed it gone. Isolated it to `use-send-message.test.tsx` -specifically (running that file alone, including its *pre-existing, untouched* tests, reproduces it +specifically (running that file alone, including its _pre-existing, untouched_ tests, reproduces it with `--forceExit` needed) — this points at something inherent to that test file's setup (likely a native-module mock or `jest-expo` environment characteristic) rather than a regression from this pass's changes, but I could not get a definitive stack trace: `--detectOpenHandles` took several minutes to return in this sandbox (worker-pool disabled, real Node handle timeouts), well past what was practical to wait out repeatedly. All 293 tests pass correctly regardless — this only affects -the test *process's* exit, not test correctness. Flagging for a follow-up with a faster local +the test _process's_ exit, not test correctness. Flagging for a follow-up with a faster local environment to run `--detectOpenHandles` to completion. --- From 1db43ede7ff8821b84cf39073f66ab3d373feea6 Mon Sep 17 00:00:00 2001 From: Real Codesiman <realcodesiman@gmail.com> Date: Fri, 28 Aug 2026 10:10:49 +0700 Subject: [PATCH 5/5] fix(ci): bump Expo SDK 57 deps to expected patch versions expo-doctor was failing on two checks: a duplicate expo-file-system install caused by stale patch versions, and 14 packages behind what SDK 57 expects. Ran `expo install --fix` to align versions, and added the missing expo-secure-store plugin entry to app.config.ts that the fix required. --- app.config.ts | 1 + package.json | 28 +- pnpm-lock.yaml | 1229 +++++++++++++++++++----------------------------- 3 files changed, 496 insertions(+), 762 deletions(-) diff --git a/app.config.ts b/app.config.ts index 76572d6..3593f1c 100644 --- a/app.config.ts +++ b/app.config.ts @@ -54,6 +54,7 @@ const config: ExpoConfig = { ], 'expo-image', 'expo-web-browser', + 'expo-secure-store', [ 'expo-image-picker', { diff --git a/package.json b/package.json index 0afd430..cd2e309 100644 --- a/package.json +++ b/package.json @@ -11,34 +11,34 @@ "@shopify/flash-list": "2.0.2", "@tanstack/react-query": "^5.101.4", "dayjs": "^1.11.23", - "expo": "~57.0.15", + "expo": "~57.0.17", "expo-camera": "~57.0.4", - "expo-constants": "~57.0.13", + "expo-constants": "~57.0.15", "expo-crypto": "~57.0.2", - "expo-dev-client": "~57.0.14", + "expo-dev-client": "~57.0.16", "expo-device": "~57.0.1", "expo-document-picker": "~57.0.1", "expo-file-system": "~57.0.6", "expo-font": "~57.0.1", "expo-haptics": "~57.0.2", "expo-image": "~57.0.3", - "expo-image-picker": "~57.0.12", - "expo-linking": "~57.0.7", + "expo-image-picker": "~57.0.14", + "expo-linking": "~57.0.8", "expo-localization": "^57.0.1", - "expo-notifications": "~57.0.13", - "expo-router": "~57.0.15", - "expo-secure-store": "^57.0.1", - "expo-splash-screen": "~57.0.7", + "expo-notifications": "~57.0.15", + "expo-router": "~57.0.17", + "expo-secure-store": "^57.0.2", + "expo-splash-screen": "~57.0.8", "expo-status-bar": "~57.0.1", - "expo-system-ui": "~57.0.2", - "expo-updates": "~57.0.16", + "expo-system-ui": "~57.0.3", + "expo-updates": "~57.0.18", "expo-web-browser": "~57.0.2", "i18next": "^26.3.6", "openapi-fetch": "^0.17.0", "partysocket": "^1.3.0", "react": "19.2.3", "react-i18next": "^17.0.11", - "react-native": "0.86.2", + "react-native": "0.86.3", "react-native-gesture-handler": "~2.32.0", "react-native-reanimated": "4.5.1", "react-native-safe-area-context": "~5.7.0", @@ -52,9 +52,9 @@ "@types/react": "~19.2.2", "dotenv": "^17.4.2", "eslint": "^9.39.5", - "eslint-config-expo": "^57.0.1", + "eslint-config-expo": "^57.0.2", "jest": "^29.7.0", - "jest-expo": "^57.0.4", + "jest-expo": "^57.0.5", "openapi-typescript": "^7.13.0", "prettier": "^3.9.6", "tsx": "^4.23.12", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0b6acd3..6a89ff7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13,16 +13,16 @@ importers: version: 0.4.2 '@expo/vector-icons': specifier: ^15.1.1 - version: 15.1.1(expo-font@57.0.1)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + version: 15.1.1(expo-font@57.0.1)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) '@gorhom/bottom-sheet': specifier: ^5.2.14 - version: 5.2.14(45a98be50302868954db6b093f845761) + version: 5.2.14(c57327f6cff0368afd765304bb73382e) '@react-native-async-storage/async-storage': specifier: 2.2.0 - version: 2.2.0(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2)) + version: 2.2.0(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2)) '@shopify/flash-list': specifier: 2.0.2 - version: 2.0.2(@babel/runtime@7.29.7)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + version: 2.0.2(@babel/runtime@7.29.7)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) '@tanstack/react-query': specifier: ^5.101.4 version: 5.101.4(react@19.2.3) @@ -30,71 +30,71 @@ importers: specifier: ^1.11.23 version: 1.11.23 expo: - specifier: ~57.0.15 - version: 57.0.15(c6665681e457b98938e269e908821e4f) + specifier: ~57.0.17 + version: 57.0.17(4c67990895f3488afb1e1e80811ccb84) expo-camera: specifier: ~57.0.4 - version: 57.0.4(@types/emscripten@1.41.5)(expo@57.0.15)(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + version: 57.0.4(@types/emscripten@1.41.5)(expo@57.0.17)(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) expo-constants: - specifier: ~57.0.13 - version: 57.0.13(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(supports-color@10.2.2) + specifier: ~57.0.15 + version: 57.0.15(expo@57.0.17)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(supports-color@10.2.2) expo-crypto: specifier: ~57.0.2 - version: 57.0.2(expo@57.0.15) + version: 57.0.2(expo@57.0.17) expo-dev-client: - specifier: ~57.0.14 - version: 57.0.14(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2)) + specifier: ~57.0.16 + version: 57.0.16(expo@57.0.17)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2)) expo-device: specifier: ~57.0.1 - version: 57.0.1(expo@57.0.15) + version: 57.0.1(expo@57.0.17) expo-document-picker: specifier: ~57.0.1 - version: 57.0.1(expo@57.0.15) + version: 57.0.1(expo@57.0.17) expo-file-system: specifier: ~57.0.6 - version: 57.0.6(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2)) + version: 57.0.6(expo@57.0.17)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2)) expo-font: specifier: ~57.0.1 - version: 57.0.1(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + version: 57.0.1(expo@57.0.17)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) expo-haptics: specifier: ~57.0.2 - version: 57.0.2(expo@57.0.15) + version: 57.0.2(expo@57.0.17) expo-image: specifier: ~57.0.3 - version: 57.0.3(expo@57.0.15)(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + version: 57.0.3(expo@57.0.17)(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) expo-image-picker: - specifier: ~57.0.12 - version: 57.0.12(expo@57.0.15) + specifier: ~57.0.14 + version: 57.0.14(expo@57.0.17) expo-linking: - specifier: ~57.0.7 - version: 57.0.7(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2) + specifier: ~57.0.8 + version: 57.0.8(expo@57.0.17)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2) expo-localization: specifier: ^57.0.1 - version: 57.0.1(expo@57.0.15)(react@19.2.3) + version: 57.0.1(expo@57.0.17)(react@19.2.3) expo-notifications: - specifier: ~57.0.13 - version: 57.0.13(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2)(typescript@6.0.3) - expo-router: specifier: ~57.0.15 - version: 57.0.15(4297cc0b7880e6d80de6379924b45edb) + version: 57.0.15(expo@57.0.17)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2)(typescript@6.0.3) + expo-router: + specifier: ~57.0.17 + version: 57.0.17(d6f39c1ccc936eb2d412636bfc344e3d) expo-secure-store: - specifier: ^57.0.1 - version: 57.0.1(expo@57.0.15) + specifier: ^57.0.2 + version: 57.0.2(expo@57.0.17) expo-splash-screen: - specifier: ~57.0.7 - version: 57.0.7(expo@57.0.15)(supports-color@10.2.2)(typescript@6.0.3) + specifier: ~57.0.8 + version: 57.0.8(expo@57.0.17)(supports-color@10.2.2)(typescript@6.0.3) expo-status-bar: specifier: ~57.0.1 - version: 57.0.1(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + version: 57.0.1(expo@57.0.17)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) expo-system-ui: - specifier: ~57.0.2 - version: 57.0.2(expo@57.0.15)(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(supports-color@10.2.2) + specifier: ~57.0.3 + version: 57.0.3(expo@57.0.17)(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(supports-color@10.2.2) expo-updates: - specifier: ~57.0.16 - version: 57.0.16(expo-dev-client@57.0.14(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2)))(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2) + specifier: ~57.0.18 + version: 57.0.18(expo-dev-client@57.0.16(expo@57.0.17)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2)))(expo@57.0.17)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2) expo-web-browser: specifier: ~57.0.2 - version: 57.0.2(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2)) + version: 57.0.2(expo@57.0.17)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2)) i18next: specifier: ^26.3.6 version: 26.3.6(typescript@6.0.3) @@ -109,32 +109,32 @@ importers: version: 19.2.3 react-i18next: specifier: ^17.0.11 - version: 17.0.11(i18next@26.3.6(typescript@6.0.3))(react-dom@19.2.3(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(typescript@6.0.3) + version: 17.0.11(i18next@26.3.6(typescript@6.0.3))(react-dom@19.2.3(react@19.2.3))(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(typescript@6.0.3) react-native: - specifier: 0.86.2 - version: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + specifier: 0.86.3 + version: 0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) react-native-gesture-handler: specifier: ~2.32.0 - version: 2.32.0(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + version: 2.32.0(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) react-native-reanimated: specifier: 4.5.1 - version: 4.5.1(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + version: 4.5.1(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) react-native-safe-area-context: specifier: ~5.7.0 - version: 5.7.0(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + version: 5.7.0(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) react-native-screens: specifier: ~4.26.0 - version: 4.26.2(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + version: 4.26.2(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) react-native-worklets: specifier: 0.10.1 - version: 0.10.1(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2) + version: 0.10.1(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2) zustand: specifier: ^5.0.15 version: 5.0.15(@types/react@19.2.18)(react@19.2.3)(use-sync-external-store@1.6.0(react@19.2.3)) devDependencies: '@testing-library/react-native': specifier: ^14.0.1 - version: 14.0.1(jest@29.7.0(@types/node@26.2.0)(supports-color@10.2.2))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(test-renderer@1.2.0(@types/react@19.2.18)(react@19.2.3)) + version: 14.0.1(jest@29.7.0(@types/node@26.2.0)(supports-color@10.2.2))(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(test-renderer@1.2.0(@types/react@19.2.18)(react@19.2.3)) '@types/jest': specifier: ^29.5.14 version: 29.5.14 @@ -148,14 +148,14 @@ importers: specifier: ^9.39.5 version: 9.39.5(supports-color@10.2.2) eslint-config-expo: - specifier: ^57.0.1 - version: 57.0.1(eslint@9.39.5(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + specifier: ^57.0.2 + version: 57.0.2(eslint@9.39.5(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) jest: specifier: ^29.7.0 version: 29.7.0(@types/node@26.2.0)(supports-color@10.2.2) jest-expo: - specifier: ^57.0.4 - version: 57.0.4(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(expo@57.0.15)(jest@29.7.0(@types/node@26.2.0)(supports-color@10.2.2))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2) + specifier: ^57.0.5 + version: 57.0.5(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(expo@57.0.17)(jest@29.7.0(@types/node@26.2.0)(supports-color@10.2.2))(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2) openapi-typescript: specifier: ^7.13.0 version: 7.13.0(typescript@6.0.3) @@ -841,8 +841,8 @@ packages: '@expo-google-fonts/plus-jakarta-sans@0.4.2': resolution: {integrity: sha512-6LYVmVGwjQvH+uzzWlVc9+oMj4lkNQ41aymVDjO+x8aFk8kCye20wOyLomYMZaMezA++Uf1mZRCw3W3Fy/hxEA==} - '@expo/cli@57.0.17': - resolution: {integrity: sha512-PQc7if117dNh2qe+CHdlmSWpgwhFiP9CJ7XbwNF0w4KSKxFpJ2qUjDPoAcCMW1VZg1x30mEJ907bT8G1iDIZBQ==} + '@expo/cli@57.0.19': + resolution: {integrity: sha512-HcvVmm0FNL1P3Lf23z1L/sLZtdcWfKW8P5L/doQbICWVG/PL7uI5+G2Ttxnj9QGNmpYgJ89E7+LJefS5jJMUlQ==} hasBin: true peerDependencies: expo: '*' @@ -857,14 +857,14 @@ packages: '@expo/code-signing-certificates@0.0.6': resolution: {integrity: sha512-iNe0puxwBNEcuua9gmTGzq+SuMDa0iATai1FlFTMHJ/vUmKvN/V//drXoLJkVb5i5H3iE/n/qIJxyoBnXouD0w==} - '@expo/config-plugins@57.0.8': - resolution: {integrity: sha512-x6lx4s/19i39/+1dMPwb9tFCc4WR843dG5Yi+C64lhKL3YHX+6oKrT2Kv72PCEalnUY+usQDkB3NrLSrYOWtDg==} + '@expo/config-plugins@57.0.9': + resolution: {integrity: sha512-hHgfL1avkCdEvDSw7IwlKwRYYNgcxzbNNMIk6W6lTkJpY0MajinAfeJUS0J+wPCsjUfGbVqOJM+XhPaO5ulUxg==} '@expo/config-types@57.0.2': resolution: {integrity: sha512-ewW08OonrcRIsRKIlFvvcmmafE5zemb1ocu3HkNwtVPyRtj2w42pZCAkMIROYpcVBaPnc3mDT9UZDzwXWC3i6g==} - '@expo/config@57.0.8': - resolution: {integrity: sha512-7VpAu2ZMXNZI0+Kn+nD3vQBIyST89LATNkZpnp/cXh8/aj7zUXO8d/T+hOxsvhOFwR8eQO+jKXEw8tQbidiMxA==} + '@expo/config@57.0.9': + resolution: {integrity: sha512-dmzlKraIFxa7wLwV6K7WzI8jp6QZpW6Mc5mGjLimJUFjzh4uQdYaT3m3plEutM5yxBoBEwqzks7l+I/ljCbxAQ==} '@expo/devcert@1.2.1': resolution: {integrity: sha512-qC4eaxmKMTmJC2ahwyui6ud8f3W60Ss7pMkpBq40Hu3zyiAaugPXnZ24145U7K36qO9UHdZUVxsCvIpz2RYYCA==} @@ -894,45 +894,45 @@ packages: '@expo/expo-modules-macros-plugin@0.6.1': resolution: {integrity: sha512-cpsLZE4rqkc1Y3eZTkxB98jrqY1YXgetmtxFt8q89jBRmk3quRuk1BZo+VcnCSObZardjg99r1k5xijEMONFGA==} - '@expo/fingerprint@0.20.9': - resolution: {integrity: sha512-h+YvPyNmeAUCCqaXvftiXklA2zGyRcIPv1B8fFS0YSIgNhwcxFGh1EyLOMsvJ2aWsBViNtIJ1HIFLzNu43/r4w==} + '@expo/fingerprint@0.20.10': + resolution: {integrity: sha512-Ox/smKlpPpyTUwZAXS927xuJ8qICwNxHOtZElv0anp6xJ80FzwkfvCdC/NjCxlALS2Hp3IsmwyKd975gYgW12Q==} hasBin: true - '@expo/image-utils@0.11.4': - resolution: {integrity: sha512-pn/4770DIEOcYZr484uazuwg20FX/qaDkeMRF6J+oxejynDmEmO8wLsCudaNShFE0BhyKGQTYrs2rsRhqrqESw==} + '@expo/image-utils@0.11.5': + resolution: {integrity: sha512-KPQBTpmpAfy/Vu9y4wPW808/qtZxjYmyJg8cm2QCPAupp+qEWA3b5zmk0ulOwQ9OgeHxuCPgUqWgkwHFo7UsrQ==} - '@expo/inline-modules@0.1.6': - resolution: {integrity: sha512-5f6EiOIKsFj9zlrCBet4ZIQRPEa9dBUdQgTzpjYwdZ8Z/M8W5lqMVL9dEs4HYKvEmDnqv0dQuDkFLyRSwu/DAQ==} + '@expo/inline-modules@0.1.7': + resolution: {integrity: sha512-Bz/khd1gIJqDkje7t5ejD5e9jFbm4xEJzWSwRKadRo6gruepbw1xJ3Eb+e58OS8fY1ZNQYjzZbspnuph67sN0g==} '@expo/json-file@11.0.1': resolution: {integrity: sha512-zxHWj4MKKMAL29ZQSY/Fssx4Thluk40JmuGNaeS078wy/NhlFhnVi+rHHunulE3xJAJ0CM73m8VK2+GkF9eRwQ==} - '@expo/local-build-cache-provider@57.0.7': - resolution: {integrity: sha512-Hq5xWhXJWuyH3CWh3uqDWoHtxM0XYqLs9h2OzRWImBJahCPfePOo4kmw2uTEB6qHT5T47eqO4jtDG3MRGGZCpw==} + '@expo/local-build-cache-provider@57.0.8': + resolution: {integrity: sha512-SEdE0pAQrr90bRh3MNR0ZuwoIBw390cYdFgbn7Vk0Mtm9EHaBfP7kYj+2hXnXZJgOEm3/JMtfoD3rV7rWA9FGg==} - '@expo/log-box@57.0.3': - resolution: {integrity: sha512-qv/cMliNax6es07Un/4IGJIcs4PUuhTKKTrJZX/0X2YRcOT5cqm/QicibZwTIbiZWi1i8P4YBRQTfFT2B17giw==} + '@expo/log-box@57.0.4': + resolution: {integrity: sha512-IxwS9s1L2muj8mj8AQSuiy7u8OFJdc02NRFo2me/Tj6DiaeG5SREqmpBE4rQpR2cadqSg5jl8Qab8Cjie616dg==} peerDependencies: '@expo/dom-webview': ^57.0.1 expo: '*' react: '*' react-native: '*' - '@expo/metro-config@57.0.9': - resolution: {integrity: sha512-+lalXZdoMaKTG1uKXsi2KWO0f7H3KiWrhZzAla4EmNSkcUMRx6K1rhZuBYoaJaKIp9BHZ333KbqRPron7slKQA==} + '@expo/metro-config@57.0.11': + resolution: {integrity: sha512-aIKcCAQALafeIf+HOoHgjj7hOb771VQRFZARDjm+GJRT74Lb3bYHjkATtS4Wv2zqVx/aFYkNqYNqPF/Py3kSlw==} peerDependencies: expo: '*' peerDependenciesMeta: expo: optional: true - '@expo/metro-file-map@57.0.1': - resolution: {integrity: sha512-8JXfVstZN7QnP4NianZZnlTVboOWR0sG8trUDNajOjnbGlPln29vponXM84tY+3tAHapz5/TxE53L0ixUwqPtA==} + '@expo/metro-file-map@57.0.2': + resolution: {integrity: sha512-tb50nSIWwKpRufSkGuivOK0FbUxv1Uwqptb0SzFTk0bkmYmcy3gIwnCbOHTfmrQDVndRhPEiu2SYgh9Z0PCvGg==} - '@expo/metro-runtime@57.0.12': - resolution: {integrity: sha512-WpgjfRFh88B5tKJrbsypvyxx0MKuXMo+ru9Gbjq4Qwj7OboJmrQ+g555YCgiQD3uk3J1ThzgEU06yZOV43suGA==} + '@expo/metro-runtime@57.0.14': + resolution: {integrity: sha512-LVpsfF0QOzlLSKlHYpxgW1cP1NKcYeaoalLO18jYHgqseSveEkb9VOffFpicTQ7AN03odZ4fgkUF+p5YpoY4yw==} peerDependencies: - '@expo/log-box': ^57.0.3 + '@expo/log-box': ^57.0.4 expo: '*' react: '*' react-dom: '*' @@ -941,8 +941,8 @@ packages: react-dom: optional: true - '@expo/metro@56.0.0': - resolution: {integrity: sha512-5gIgQHtEpjjvsjKfVtIv23a98LLRV0/y07PDShEwYSytAMlE3FSF8RHXqtHc1sUJL6dn7hnuIBpIbrLXXuVi0A==} + '@expo/metro@56.0.2': + resolution: {integrity: sha512-Ld5AeYMCCDa8bLeWhfuLbZFFjlV3f6ORqyPz2glGh6RltIngMuLf9BTC2yvHFjkKuGxL5SynijmA8xmNNWn5iA==} '@expo/osascript@2.7.1': resolution: {integrity: sha512-Zn03EX6In7ts2lPUW2ESUSkEhEWQN1qqsiXjadtZMJOuZRkMiAg1ZQHuvz9DjByDWNJ2pBwAGyrts9lj9k389g==} @@ -954,23 +954,23 @@ packages: '@expo/plist@0.8.1': resolution: {integrity: sha512-3gTReGIUm0oRaMClsAJYxBnVPCl6fVpsl8HS+DTVxDhW4GyVyxg9E/Znm3BvcHtUJ51RJJI14pC1wvrNilCRHw==} - '@expo/prebuild-config@57.0.13': - resolution: {integrity: sha512-VhSySuXqOwK4fIc+9rgms7zN+c1Khj2xpuIgpVLPNyWRtpS8wB+eyV5CSohjSUBa3h+NsFdPk/VG6p0ZwlMDrg==} + '@expo/prebuild-config@57.0.15': + resolution: {integrity: sha512-xTbWHroj0PDmlbqvmU+zF9ZZxveJkiuyiPoeRJYRGruFHebRAWnoTdw5S7d/UCzDBI8ropGGu9g2eb2nMxtvAw==} - '@expo/require-utils@57.0.4': - resolution: {integrity: sha512-e7xbg/9BTQcsZE/oErafZXtI7kh5IgfasLJ97J5sFSzX2cA74pDvdlhW1KHVSaDkQyQv6h1LSLhsY7dEeOk7hw==} + '@expo/require-utils@57.0.5': + resolution: {integrity: sha512-kTAXj9lDFEIPMsbAOGCGbjBbMF0oi7CqkYM79KOX0DDD9wSwXmlKL1z2h8OwsrBf7mbOo2DjlRvZu4BEjrIxGw==} peerDependencies: typescript: ^5.0.0 || ^5.0.0-0 || ^6.0.0 || ^7.0.0 peerDependenciesMeta: typescript: optional: true - '@expo/router-server@57.0.7': - resolution: {integrity: sha512-QFHwt6V7UovmYA7+8BoMeKj3fh6WtkM9zksKjNAlZdcEI/hUhcW4uged6So5yc4tq+eA60kA7gIVZPKgBIZdrQ==} + '@expo/router-server@57.0.8': + resolution: {integrity: sha512-lEPGYoDmh97yyEcY/MFWQsfE7yIgMRnGDcnsBN7B6AiWBFSJIMEcGnO0aYPgY0ZjNWyJDi7Ee8QZRGqB+LhI6w==} peerDependencies: - '@expo/metro-runtime': ^57.0.12 + '@expo/metro-runtime': ^57.0.14 expo: '*' - expo-constants: ^57.0.13 + expo-constants: ^57.0.15 expo-font: ^57.0.1 expo-router: '*' expo-server: ^57.0.3 @@ -1000,8 +1000,8 @@ packages: '@expo/sudo-prompt@9.3.2': resolution: {integrity: sha512-HHQigo3rQWKMDzYDLkubN5WQOYXJJE2eNqIQC2axC2iO3mHdwnIR7FgZVvHWtBwAdzBgAP0ECp8KqS8TiMKvgw==} - '@expo/ui@57.0.12': - resolution: {integrity: sha512-ZkrECV6xZe0+Drq6C0hrBKvz1534/VyARNp9ziKihC6Db69W29+jBDZm8k9cyRyS1Hh23q6LP8J+2wDyUM8eAA==} + '@expo/ui@57.0.14': + resolution: {integrity: sha512-m8Z5dCplkOOQ70lu/xfB67qhk00Muw4U1T9frHBIW3LxxJeJZQ+jOmV6vCk6O6zVgoG/ib1et7PeX6etxI4Rsw==} peerDependencies: '@babel/core': '*' expo: '*' @@ -1428,87 +1428,87 @@ packages: react: '>=16' react-native: '>=0.57' - '@react-native/assets-registry@0.86.2': - resolution: {integrity: sha512-vcX/mBjWAVnWofu7KecotquI2unZ/tITwA7OGdq/mdY/zmGXIEvYhfEYyOQij/LRqi9WAL+iizInTBWnxDhK/Q==} + '@react-native/assets-registry@0.86.3': + resolution: {integrity: sha512-TDhgCZA4wjJg84d5A9swiOQYPIWSKEEVdg9IwMFZDupQzW/F3QoLUrfAJOcalgqTDA9/buTB8awhE3Whwg6u9Q==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - '@react-native/babel-plugin-codegen@0.86.2': - resolution: {integrity: sha512-NNDZqOlNbH5SzgPks1jFDYH3234Rpa5e/nhZymxhIiBH3NcE3uD+rGj/HWXhH7nHF2ToGK6XbUpqy7nmJPeh+g==} + '@react-native/babel-plugin-codegen@0.86.3': + resolution: {integrity: sha512-O6Xza4JBGPIU8J7YbKTyBoYL4thpy8jMW/oaLDWdAyOwYHKIjK47pAL5HUEbOe2bWz2PEKjbYRF2ApkJv1ottQ==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - '@react-native/babel-preset@0.86.2': - resolution: {integrity: sha512-4XKEJ6jKW9lXMB1O5o47gBoGgolde1fbX13gLW/erlcn+1ky+MHHo5UjuM3RWGdPJHIvIzekDDumSUHhB9x5iQ==} + '@react-native/babel-preset@0.86.3': + resolution: {integrity: sha512-/eqs/Hy9RZRcjdcs4wj3Cqmxvtb3NM5g+Uuh1RIvsjynMO8PRsrVWWLWgBcZL/jYUo+ogXd2NFB0W8L5Bg89xw==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} peerDependencies: '@babel/core': '*' - '@react-native/codegen@0.86.2': - resolution: {integrity: sha512-xKkudsahUJ1n//55g4fXk5BStVqqmZlz8HQveL45ZxcfDnwvhuYe2GymksQANFsSN+slvrarjrfq8kIxJzbceA==} + '@react-native/codegen@0.86.3': + resolution: {integrity: sha512-Ux4jHi0fh+bdtVEcL0gaPLbY56V+SvFUDl/8sRAE1jdb4k+o7fT/4Nc29yz4X+qfjstkSqObQTMBGhdzxH9JvA==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} peerDependencies: '@babel/core': '*' - '@react-native/community-cli-plugin@0.86.2': - resolution: {integrity: sha512-YHXNKoM6Y/HjREySZ5arET2xgiHgg67r1MdwJB//MPJAJ0Xc5g0u6UHxY9VzsHO3Y07dre6s0BinYwjt1SEWvQ==} + '@react-native/community-cli-plugin@0.86.3': + resolution: {integrity: sha512-qSDL9LQc5mZSZPNczT95WU9YQuPzxBklgON9vLhhqfI0yWIwKInqFx88dQ/uiEXBtf0yossthaQIqA4Ml6bF6g==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} peerDependencies: '@react-native-community/cli': '*' - '@react-native/metro-config': 0.86.2 + '@react-native/metro-config': 0.86.3 peerDependenciesMeta: '@react-native-community/cli': optional: true '@react-native/metro-config': optional: true - '@react-native/debugger-frontend@0.86.2': - resolution: {integrity: sha512-KGS1aV5F6cIqpnoIUhLBXyVzy1oAj8jBFGau6vX4Vy0HXRJN7p+68RU7x6NuyraHvQcR14ccMGT5TkFuNjQ4gA==} + '@react-native/debugger-frontend@0.86.3': + resolution: {integrity: sha512-TQmeofQ0PcuylhhlleOeuzHYZfbrgm3gayXzowqUEzgRisTm1D40/J3ggqs7XkQi5HP5ZA3n8dHmKL9vIzPcsw==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - '@react-native/debugger-shell@0.86.2': - resolution: {integrity: sha512-/TaVJ2+gGajZPJGrFaObUQmHmlaxAlfmOPZicl6pNKDUjzSgFMpcLkdTOExvb+USYTVdGX1XwxXyvjQdUO2bvg==} + '@react-native/debugger-shell@0.86.3': + resolution: {integrity: sha512-O4ds+J7xZfxkbih9T+cAGegBdvKSPKYJm/lDgC9CpEjFMkmzWTpVLU3Qsv9sqZuo58z+sGhIcJfPsCFFWHpqbQ==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - '@react-native/dev-middleware@0.86.2': - resolution: {integrity: sha512-B7L0vKvg+IcEElT7Vpqh1xj5yJAqWUegjbP+bQRaorJMAYnv11GkliTnZV2AdTDfZQJWgOEx8i8LGkHkUg7bnA==} + '@react-native/dev-middleware@0.86.3': + resolution: {integrity: sha512-LiEPTqTg/63bYUnrPyHLfjTDCNhA/+CUqI1+DsA9tYyewtSbULd5awsva6SgE10I+2iMhgKXS3ymkhU/kSCrGA==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - '@react-native/gradle-plugin@0.86.2': - resolution: {integrity: sha512-2F6x14NcHMpVmfTTFKfMkpV5dZedZrLiv6PE+c3vgnesV2bjleUBydr4U+NI8VkI7OwW71L0A5qQ76I9LCrfoQ==} + '@react-native/gradle-plugin@0.86.3': + resolution: {integrity: sha512-lxmx0GqLEWRIpZfpYFXlYVIs3ENQwaW6Vmp6oi29l2GoQJ1wZfFZRdMimDWlGEk8LKfHar3QH3iaPMkTcK9lEQ==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - '@react-native/jest-preset@0.86.2': - resolution: {integrity: sha512-wneoqwKWdv6wIcWotVgp6NMlMWcJDjxDnp7fVYym2Pn6JLgAKgkbmuHGmxW0cLCGoa9wtcO680uciv+Kzx0G7w==} + '@react-native/jest-preset@0.86.3': + resolution: {integrity: sha512-oMmBwXUpTgJvGlcXQQPuiQLykoM8jsWYp6RwdJ2NWyIWzJcGp1rmSr3XV7istoIcUt0wdOVLtQs0A0nDm9ND+A==} engines: {node: '>= 20.19.4'} peerDependencies: react: ^19.2.3 - '@react-native/js-polyfills@0.86.2': - resolution: {integrity: sha512-bIwNcGBaQ74shB5z1mRkxOpjikimuwsnOCEkZSzL67Z1FTyK1ObpENfyd2QvcvVW9Cjl+tHuw9ynpBnb2jPoJQ==} + '@react-native/js-polyfills@0.86.3': + resolution: {integrity: sha512-eYIJ0es967+tePBFQDnl/gidVFxLns3fnbiK6rxscQrGodvuUO6hwxpQnfNynJ8MjbbndImXihXjcnJdc7SzJg==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - '@react-native/metro-babel-transformer@0.86.2': - resolution: {integrity: sha512-mX1wgLErdb2hDgXJr9zM9SWLe+ZteZTFTwRWGOQ53yEJwc9DVSnxdTlAtVdMeOj2ntycKh0R8jYdat2Am43cwQ==} + '@react-native/metro-babel-transformer@0.86.3': + resolution: {integrity: sha512-0nlwVkG0uT9o72nrGTKUz/IqDnCkM4zE0ihe4r+OJcxH93yBdDm58AXJ3EXZeSjv3osHEFXU7ceajdZiS34mTw==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} peerDependencies: '@babel/core': '*' - '@react-native/metro-config@0.86.2': - resolution: {integrity: sha512-hJno256j+MS0b3JD1aD3ouTGZVacKNVBuXL2atMQQ8BZ060vl1ptnZ83y569aDW+/rgFSOcqn6ydKeSz4uUKQQ==} + '@react-native/metro-config@0.86.3': + resolution: {integrity: sha512-qdzDMepV2xdUhsO4XC7idnnbt8K6+Hd59AjeK9y4KS86fxSQq5oL0TAbqUuajDGR7beNQoYTuS6AowN/zP0oaQ==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} '@react-native/normalize-colors@0.74.89': resolution: {integrity: sha512-qoMMXddVKVhZ8PA1AbUCk83trpd6N+1nF2A6k1i6LsQObyS92fELuk8kU/lQs6M7BsMHwqyLCpQJ1uFgNvIQXg==} - '@react-native/normalize-colors@0.86.2': - resolution: {integrity: sha512-EzPFc9Y6lzYOWeso2almwXI7f8+qReHxWvT+algsOczb2UhWXIWXDoSvkdwoSfiwwmGt/ijJgKJoeHlzPkLwRg==} + '@react-native/normalize-colors@0.86.3': + resolution: {integrity: sha512-Cv3CDkprb67GrzuaS9BGbBJC/6G4lIw3nyKOHRKTqTTum4bn37y5+R0Z04L8mcbQN85eEohNrRwb7IOM4j6uvg==} - '@react-native/virtualized-lists@0.86.2': - resolution: {integrity: sha512-uO0J72gh3EvE+1/GHRk18QRyBDTRHRB0AraAfojsRjbT7VMuJwKrZYaKGshavoaEud6aw00ZB9/8mTMIKjjcAw==} + '@react-native/virtualized-lists@0.86.3': + resolution: {integrity: sha512-1j44NEyNn05Ut40vHAmoSWbsIcybFkMAOBTwQt1PrESyfSS+qBoyU1LGIogNva0VIa0rQyEC5PzbA7R4/7Nhyw==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} peerDependencies: '@types/react': ^19.2.0 react: '*' - react-native: 0.86.2 + react-native: 0.86.3 peerDependenciesMeta: '@types/react': optional: true @@ -2038,12 +2038,12 @@ packages: peerDependencies: '@babel/core': ^7.0.0 || ^8.0.0-0 - babel-preset-expo@57.0.7: - resolution: {integrity: sha512-/1RLnZTJVoTNo6nCdSv27BSA2LBzM/qEkNLznwWvztg64DhsAz7ByZIiAqdbau9FK3YqF+IV5a2ZsPXFclwq4A==} + babel-preset-expo@57.0.9: + resolution: {integrity: sha512-T19biGOBTnMp161PyBqWOoSIEeZug8/EjtuUdVob8JPQKZMTalHUQaQt+qtQQE7XsEkZJ6erb3k0CohneTFzQg==} peerDependencies: '@babel/runtime': ^7.20.0 expo: '*' - expo-widgets: ^57.0.10 + expo-widgets: ^57.0.13 react-refresh: '>=0.14.0 <1.0.0' peerDependenciesMeta: '@babel/runtime': @@ -2526,8 +2526,8 @@ packages: engines: {node: '>=6.0'} hasBin: true - eslint-config-expo@57.0.1: - resolution: {integrity: sha512-OsLAReXOF3uXBwyxxw4f55sORYC+lOH4pSaGdU9MnYhRpjqPziK2uCnRxjLTGKeAKER7GSX5Wz9kNMGJT1MZEw==} + eslint-config-expo@57.0.2: + resolution: {integrity: sha512-dOWkx+MWclLVnYpPPBzas4sfnPaAjk+fe/dbvJSzbWtHUR4fGGaTT/lTcOpxqhwRIlTKKBUYGKude0tcTBsZHg==} peerDependencies: eslint: '>=8.10' @@ -2676,8 +2676,8 @@ packages: peerDependencies: expo: '*' - expo-asset@57.0.13: - resolution: {integrity: sha512-RPjMcmPXRMb6UbQdTIkmSDEnQL5LKGTu7ZXatq3U67V6ldlSdQeQ9pQV7zlmk0DrbnaMEh3HYiEh2YxWLNyCzA==} + expo-asset@57.0.15: + resolution: {integrity: sha512-ZoiUftQb1Nn1jvL6l1kZoUXvgRCx57tMQYxSsp+g4ZvY+jgVWp/THruXOtPtZVdC/WWZxPUTct53bfAm2EPDAQ==} peerDependencies: expo: '*' react: '*' @@ -2694,8 +2694,8 @@ packages: react-native-web: optional: true - expo-constants@57.0.13: - resolution: {integrity: sha512-eB5AHp7kKxsVIBjTetUgj5WQSw8joI2ekzgTlcUv+Hc0x2t0jZU6IiL4DpyOT2yZQ71n0WJZCkmOKLgxlajIzg==} + expo-constants@57.0.15: + resolution: {integrity: sha512-rLME1H/K+/uFMR/jEpe/p1nribvCEZZmQegzqxu/R1HTyvYweq4E+B38zB0baie+SEwmeIPLaM4KLVMnejhm5A==} peerDependencies: expo: '*' react-native: '*' @@ -2705,13 +2705,13 @@ packages: peerDependencies: expo: '*' - expo-dev-client@57.0.14: - resolution: {integrity: sha512-0WEZJZtUZC0PgkZXm3BHWVYiOBM8hbkMP50Nk24X+K6k5+EWBmwmsSO/K4sojKyO1Pxhb/GHtSYaV8t1YI1x/w==} + expo-dev-client@57.0.16: + resolution: {integrity: sha512-Txss9sgzEFI0E+4I34E76vPiukeVDHnqCj0ZXy8SDijVM31/P6MCNniVbEZk7MBPQclD0aopfuaCFtkV8+r1sw==} peerDependencies: expo: '*' - expo-dev-launcher@57.0.14: - resolution: {integrity: sha512-zb1Y6cBIhniBjTpihBHaWx0O81w3aWp5ddAw+ThpfCGrMVlYCdaBb2zlo1dZ1swJna7mn09eIID97U3pPAh83g==} + expo-dev-launcher@57.0.16: + resolution: {integrity: sha512-jaBr6q4A5js/r465m2mRxN8A87YOi8I6PiIt8XIt4qfQWDgw6hqGovaF1AVFGT1Z5jxkjtgK8WNz/rAeypjqPQ==} peerDependencies: expo: '*' react-native: '*' @@ -2721,8 +2721,8 @@ packages: peerDependencies: expo: '*' - expo-dev-menu@57.0.14: - resolution: {integrity: sha512-lTD5ErQrrH57v1HOM4RY2gA9hmvqXoZ/AArM7LQLCts3fKqaahOGg45ETRUgdvULv3K4wbKwhXwCRoANWMxGTw==} + expo-dev-menu@57.0.16: + resolution: {integrity: sha512-KzMHtAmnr0GqyU6MCrKLzIo/AfOjcToGhzK+pFeXlcqSPOrPJbTGlZqQ3E1o2G2jZMs+M1XvXh/VgnRyJUaRGw==} peerDependencies: expo: '*' react-native: '*' @@ -2737,14 +2737,8 @@ packages: peerDependencies: expo: '*' - expo-eas-client@57.0.1: - resolution: {integrity: sha512-4w51+zsl/ziUHQMJgLgUdgsNhRPAwHBfySpPB1hpWU21X74QS9T4SqDftaRnrDagn/DfcrXUMJvHbDQUxLPJNA==} - - expo-file-system@57.0.5: - resolution: {integrity: sha512-XjGrCClF0935y5wLB9qNQQUVptNEy+0OloBstXlCd+IeNXLo3uNOod6cX4N0hofIhNIrN1Al8fwfay7R0Z1a2A==} - peerDependencies: - expo: '*' - react-native: '*' + expo-eas-client@57.0.2: + resolution: {integrity: sha512-EfFiqUr0o9TvTOgMbqDiV1oIdG/d7kirhqtwa7roGmm9wF+CpXUz20d9YGzO6KzJWUYgdUgu4B6Ocv0jNJUdnQ==} expo-file-system@57.0.6: resolution: {integrity: sha512-pm8PMYEW6BnVOCBJ7df9FcDmQtE1tqImuYphlfYe1ipQRLYtdCayRczJcbKIsnB3mqEyp4gC2gWmMbsAsAOjVg==} @@ -2776,8 +2770,8 @@ packages: peerDependencies: expo: '*' - expo-image-picker@57.0.12: - resolution: {integrity: sha512-BjZLioItcDNrXwp46XZDZGDEUJ16/bK0QzxPjdNijiPYpLfQLeuFYoX9yH31sosnli0UL++x4ndYQoTMsICdlw==} + expo-image-picker@57.0.14: + resolution: {integrity: sha512-NK9XBQqOtscbB/uRts1Gm7Oki6odMN3FQPWD6fSKmNUfKM69O6kcgU25lEXFTSLZ7sLl+AopHa2X0uILd8rjaQ==} peerDependencies: expo: '*' @@ -2801,8 +2795,8 @@ packages: expo: '*' react: '*' - expo-linking@57.0.7: - resolution: {integrity: sha512-Ujn4kY0bd1MoJFLYuvIUWamlZH61pkahrUpug3hBtOeS89LDbPyKNjjmWef6+CPTGQfed2PlfuMZuPO1BCrg5w==} + expo-linking@57.0.8: + resolution: {integrity: sha512-gv0ZNaF7tILbcvX8xinNbQr6GIGQStxeUAJseweP+LjC9ped7Wl6EeyIMBlDR83U6Da+8drVeqBQSMvwAXgytA==} peerDependencies: react: '*' react-native: '*' @@ -2818,12 +2812,12 @@ packages: peerDependencies: expo: '*' - expo-modules-autolinking@57.0.10: - resolution: {integrity: sha512-jNqLswMx8QHN8VXRgud+xT+9q+J9U63CEGlx+k4V/IE9Qyt9HrKbWp+pIDma0fOquta5HBfSfsShcI+NkEpTiA==} + expo-modules-autolinking@57.0.12: + resolution: {integrity: sha512-Q8KAlq37nLKsQ+HsS9NpQVpd5jCgqtu694TDUNHBUBpV9ViD82mRBh8Uug/h68RG9xnLS+kuL4nYaCuFRghHjg==} hasBin: true - expo-modules-core@57.0.12: - resolution: {integrity: sha512-hKqCdu8+78oNKWCgM4xSeblfmD/audgNVCn6uUCJuNBS/hc4DyC0Ia1X96SSZF9w9bGTLjNS8j2b7DshiC3WJA==} + expo-modules-core@57.0.14: + resolution: {integrity: sha512-Cg3aQnVsQhZcMOF/RFgPGzMuhIak5W9eR9ecwmFSeQ813r4/49ut6RMA6PqUuUKuPCUIRqnBAHfSyTm1W9nfzg==} peerDependencies: react: '*' react-native: '*' @@ -2832,27 +2826,27 @@ packages: react-native-worklets: optional: true - expo-modules-jsi@57.0.5: - resolution: {integrity: sha512-NQF7MUF0j3rQHV8KJqETYA1SrJr3USvQ/AWEhODo+unMRFBDP9BKpn8+aNGGmx6fa2XLnqC2qulVw4DT6Gp13Q==} + expo-modules-jsi@57.0.6: + resolution: {integrity: sha512-WK0xFEe0FdZTCLCdfXK+HVNYfAmEbA6goecRxqjr0gh3XYRKAr9tikxqcM0ItLQoEPKntscPZhWcPLGvr97Tfw==} peerDependencies: react-native: '*' - expo-notifications@57.0.13: - resolution: {integrity: sha512-4kTLfvCbpr92RWWmYGO1BP4pJ5ZhxOdhB/6zdtNs44wV7h6zKrbweccwovgZTLo4Ef/KPL3SD02Vam4Tl/xv9A==} + expo-notifications@57.0.15: + resolution: {integrity: sha512-H91z4WSFukSQP3dThgh6PCPFrT6VHeR+J2f7CuCOE72JV7YEGcBCir103QY58ocKsdgkodQpnkTU2m8LTHkWLg==} peerDependencies: expo: '*' react: '*' react-native: '*' - expo-router@57.0.15: - resolution: {integrity: sha512-vwr2HL2U7hqv3QTkuz0e8hQc5hkgpXztisy30VwhdXJVDPdP0/m8wSiYUm6GKyZAbcXfD4fn44ZYcJsSESY6cA==} + expo-router@57.0.17: + resolution: {integrity: sha512-zKAfGLxrbdkUFzzW1xHezLXPSa0V4DlYKEWweDUUFGgnPWPNy3OY0EKYAqOhlBGSDebAvtnJXn+2zyyJjYKwyg==} peerDependencies: - '@expo/log-box': ^57.0.3 - '@expo/metro-runtime': ^57.0.12 + '@expo/log-box': ^57.0.4 + '@expo/metro-runtime': ^57.0.14 '@testing-library/react-native': '>= 13.2.0' expo: '*' - expo-constants: ^57.0.13 - expo-linking: ^57.0.7 + expo-constants: ^57.0.15 + expo-linking: ^57.0.8 react: '*' react-dom: '*' react-native: '*' @@ -2876,8 +2870,8 @@ packages: react-server-dom-webpack: optional: true - expo-secure-store@57.0.1: - resolution: {integrity: sha512-tLa1VmSadOq19mA/dwkl99RbHyjLE0T1qqBYMY3/OsguZTI+rlrDy/DDJjupqlVtmr95hD7o1pYqx5aL+B4YMA==} + expo-secure-store@57.0.2: + resolution: {integrity: sha512-PhrPMKnI7YSObLEQZOoP9evB2ZOCv9kFuG2L7cZfNlOwWjGbir711PlppkXVoM3JE8vamDrTqzMLGv266FhJJg==} peerDependencies: expo: '*' @@ -2885,8 +2879,8 @@ packages: resolution: {integrity: sha512-aK+LdKzauHSGmsOStZtyxdzv0zWssCkxTw3m4QuOhfDSJsZaMRTd9O41d8ixU/QfELTbaJ0oRNcF7JFV/7O9YQ==} engines: {node: '>=20.16.0'} - expo-splash-screen@57.0.7: - resolution: {integrity: sha512-QxqfjOCTnTGnqK2BxfEtxUJOcnsJbUXO873Q342r3VkgHAG877eozsNzgYWQXcCFj/asMwx2uy9OXxBShxc5rQ==} + expo-splash-screen@57.0.8: + resolution: {integrity: sha512-BEsrKg4niYBZa5AFzWRyGqxA4ZemtBOPwbCy+C336iGYYPuZYLEgWJGmm2xD1gRfCdrM/TP+l2ONd86Zvk3KyA==} peerDependencies: expo: '*' @@ -2908,8 +2902,8 @@ packages: react: '*' react-native: '*' - expo-system-ui@57.0.2: - resolution: {integrity: sha512-zABCRqFSioDBAo/RtmS0dQiGgDtDPZUVk01Y3Ti4ducobNM4HTM2sNAtq+YPpEUhaVW3hccPVsEUH4LK/ADVhA==} + expo-system-ui@57.0.3: + resolution: {integrity: sha512-3N3BoagGojEvQw0h7kL0Lvv2LrVjqEvODSNS+Nc9lj/kzaTaVx4K1HI8qqxeALMf5jui3rLP2b3Ai2pXotoNuA==} peerDependencies: expo: '*' react-native: '*' @@ -2923,8 +2917,8 @@ packages: peerDependencies: expo: '*' - expo-updates@57.0.16: - resolution: {integrity: sha512-eb0arz9Vp5m1RaUKO6PJxogJv8z15/Rjt5GsRcSUjrf0vhignlT7b0D/Z07Po/ACG1H7Yn7rwIc73T647JVJFA==} + expo-updates@57.0.18: + resolution: {integrity: sha512-guTp5U6RbMOivi3GWXsflZA6M/4oTZKj1/M69sEzVW9MII/FQt+lPgbbEHwcIGrpXBr4RuJIYVrh492kkptJvQ==} hasBin: true peerDependencies: expo: '*' @@ -2941,8 +2935,8 @@ packages: expo: '*' react-native: '*' - expo@57.0.15: - resolution: {integrity: sha512-9UooISw8S8Gxo9wpsPcGkyRlf2UzNr+F26LSU2+M8iaiZ1WAjPR0I75NKFX8ETbrSgthZ2lQHgy5IU2KE/mpBA==} + expo@57.0.17: + resolution: {integrity: sha512-RG9BItgEObzSafkJKHPUh5iCnaPCFXJTDwfEJe3t7uhmqipVhcsf6gAJEtME0xaGaMp4Z3C+HLbPW9DMea8G0w==} hasBin: true peerDependencies: '@expo/dom-webview': '*' @@ -3174,8 +3168,8 @@ packages: resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} engines: {node: '>= 0.4'} - hermes-compiler@250829098.0.16: - resolution: {integrity: sha512-xsgzk+mUyvt9t1nUbF8USBlYxajTUtPJhVZ86q85s/SEoMKCF+52YZcudb0ENSnV3T3lV9mgB3s6R7+pH90zgw==} + hermes-compiler@250829098.0.17: + resolution: {integrity: sha512-qG1PXzTEtriF6oQLZF3vyHhSMxOdW5h2TqqLri0rdpstPustd2fSvRZQMVAPdlhgFwBfYnj3OUZtiO6LjYsEFw==} hermes-estree@0.25.1: resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==} @@ -3261,11 +3255,6 @@ packages: resolution: {integrity: sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==} engines: {node: '>= 4'} - image-size@1.2.1: - resolution: {integrity: sha512-rH+46sQJ2dlwfjfhCyNx5thzrv+dtmBIhPHk0zgRUukHzZ/kRueTJXoYYsclBaKcSMBWuGbOFXtioLpzTb5euw==} - engines: {node: '>=16.x'} - hasBin: true - import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} @@ -3538,11 +3527,11 @@ packages: resolution: {integrity: sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-expo@57.0.4: - resolution: {integrity: sha512-EpTc8zizYWepKXHdHYuYBdEP0mZLjZfB1ldb2dyLKHug0HYFGuBnQ3Qp3gmBWCj3rSRaJYP90Lq/qPozzx2Khg==} + jest-expo@57.0.5: + resolution: {integrity: sha512-U08fwjaDBwnnbKnZcOA0WQbHfKqOWTCGku+IdCaekrLZmfbbZr3P1WqEsXHo6ntidQ3DmW+TT6G1aOp+ChYfKA==} hasBin: true peerDependencies: - '@react-native/jest-preset': ^0.86.2 + '@react-native/jest-preset': ^0.86.3 expo: '*' react-native: '*' react-server-dom-webpack: ~19.0.4 || ~19.1.5 || ~19.2.4 @@ -3875,117 +3864,59 @@ packages: merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - metro-babel-transformer@0.84.4: - resolution: {integrity: sha512-rvCfz8snl9h20VcvpOHxZuHP1SlAkv4HXbzw7nyyVwu6Eqo5PRerbakQ9XmUCOsRy70spJ37O+G1TK8oMzo48g==} - engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro-babel-transformer@0.84.5: resolution: {integrity: sha512-2WbHILKMiJUzfdjmGOQOqU1bWi9//gqiclc/tkk/AIsrrVw3efhZ1uhkOwMTxUEPOzqoo091H0olLmVZH5FHGQ==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro-cache-key@0.84.4: - resolution: {integrity: sha512-wVO79aGrkYImpnaVS4+d5RrRBRPX31QtvKB3wKGBuiNSznduZTQHzsrJZRroFJSwnygrzdsGUtDQPuqqFjFdvw==} - engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro-cache-key@0.84.5: resolution: {integrity: sha512-3dPB2TnvGjjf0/9O7AXVQURKXuQNauTZE7WpTGTlR017Gh/B5y0m/2wcqxfveUguHSpu89KhVxCAlr2k/H7uhQ==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro-cache@0.84.4: - resolution: {integrity: sha512-gpcFQdSLUwUCk71saKoE64jLFbx2nwTfVCcPSULMNT8QYq0p1eZZE29Jvd0HtT/UlhC3ZOutLxJME5xqD2JUZg==} - engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro-cache@0.84.5: resolution: {integrity: sha512-WHS0n2OxQqtwEjSeQFPePNrMvEFhmQcUQM9cRJMHByWoi/GMWFBEWOf7hVkAM/0KRutAXNbDlSu/cZB6CyxgQQ==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro-config@0.84.4: - resolution: {integrity: sha512-PMotGDjXcXLWo2TMRH+VR99phFNgYTwqh4OoieIKK3yTJa1Jmkl+fZJxDO0jfBvNF+WESHciHvpNuBtXaF3B0Q==} - engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro-config@0.84.5: resolution: {integrity: sha512-zie+uN6oohscowi2S7ByU+wUw6CrT4ZxW9uAbONOObSxx86RGmnIAmjXHLkfmcdYoY7jzOPEbqcI6oeVmqyBQA==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro-core@0.84.4: - resolution: {integrity: sha512-HONpWC5LGXZn3ffkd4Hu6AIrfE7j4Z0g0wMo/goV24WOB3lhuFZ40KgvaDiSw8iyQHloMYay5N/wPX+z8oN/PQ==} - engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro-core@0.84.5: resolution: {integrity: sha512-xwm605hCi5Y6eJTTb8ZWo6pkUcoBEIyiQOfkZh5GwtDwUrP9SNhTQZhzJHrBCwwxlf3Ptl/pxWJgQ1rsNYMnrA==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro-file-map@0.84.4: - resolution: {integrity: sha512-KSVDi/u60hKPx++NLu3MTIvyjzNoJnFAF8PQFxaj1jiSka/wjw+Ua6sNuJ0TDHQv+7AAoFQxeMgaRAe8Yic5wQ==} - engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro-file-map@0.84.5: resolution: {integrity: sha512-mlm/JL8toSbSc2akpKIGmzvrVRSCgZ5vkbycI34oMLoOnLGuLyC8WTyVJ6P0hZG/usDaGwZSl/s9BCRriqjGJA==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro-minify-terser@0.84.4: - resolution: {integrity: sha512-5qpbaVOMC7CPitIpuewzVeGw7E+C3ykbv2mqTjQLl85Z3annSVGlSCTcsZjqXZzjupfK4Ztj3dDc4kc44NZwtQ==} - engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro-minify-terser@0.84.5: resolution: {integrity: sha512-BJoFwCEDsYnagPqarayInv2+diCDNDdLlaof/p6s9w4gh+gc9HXYM+pDvsKGKKUumpZswNF3Z/ftTMqKl/5IBg==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro-resolver@0.84.4: - resolution: {integrity: sha512-1qLgbxQ5ZGhhutuPot1Yp348ofDsATL2WkrHF65TobqTT9K3P9qJXw38bomk7ncp5B7OYMfWwtyBZo1lCV792A==} - engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro-resolver@0.84.5: resolution: {integrity: sha512-VSSnepg1k6LyCwtb6eirWdAWlpKwBG8Rdtsr1mU38rMelFyWgh3/QuMSiZIZAIjwg/fsa8GhW5/FO54CAUPCEA==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro-runtime@0.84.4: - resolution: {integrity: sha512-Jibypds4g7AhzdRKY+kDoj51s5EXMwgyp5ddtlreDAsWefMdOx+agWqgm0H2XSZ/ueanHHVM89fnf5OJnlxa8Q==} - engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro-runtime@0.84.5: resolution: {integrity: sha512-U1m2+d1Pr+JO2/iVXBB2OfXXityz7tqwIorxfrT15IEgaHvpJBq/OHiqnOWPKJbUl3JcxjcdviZZOKk85oK4Qg==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro-source-map@0.84.4: - resolution: {integrity: sha512-jbWkPxIesVuo1IWkvezmMJld6iu8nD62GsrZiV6jP37AOdbo4OBq1FJ+qkOg8sV05wAHB//jAbziuW0SlJfW4g==} - engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro-source-map@0.84.5: resolution: {integrity: sha512-2BtV5L9uPc49F13Gn5wiP6bX/EncqzqTIk2VL/0F/96Vo0YEOjluT/qktQjFODfqGFsucwnh5mPEAl/2jVEfeg==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro-symbolicate@0.84.4: - resolution: {integrity: sha512-OnfpacxUqGPZQ27t8qK9mFa7uqHIlVWeqRqkCbvMvreEBiamEeOn8krKtcwgP5M4cYDPwuSmCTopHMVthqG4zA==} - engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - hasBin: true - metro-symbolicate@0.84.5: resolution: {integrity: sha512-rQ40zYDAkaWBN9yvjUuAD0ZpzBMZSoKyGYXnb5JrfbKjun7fTvfoLHL3KXFYenBTYZkQtlp4cKSCv/1utxFyOw==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} hasBin: true - metro-transform-plugins@0.84.4: - resolution: {integrity: sha512-kehr6HbAecqD0/a3xLXobELdPaAmRAl8bel0qagPF4vhZtux93nS8S4eq2kgKt6J2GnQpVjSoW1PXdst04mwow==} - engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro-transform-plugins@0.84.5: resolution: {integrity: sha512-+InaSVGaOyt0DyRo4Y/zIdPI6CZwnbNho5LAL23tgmuGwv7fyfkF7kKfPjZcfxXBcoYdTLLFnCfCH/dHSiCqNg==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro-transform-worker@0.84.4: - resolution: {integrity: sha512-W1IYMvvXTu4MxYr7d9h7CeG2vpIr3bmLLIavkPY4O1ilzDrvS8z/NEe6y+pC44Ff7raMXQgYSfdqDUwN/i39gg==} - engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro-transform-worker@0.84.5: resolution: {integrity: sha512-ui1Z8x4s5RL36gMmKLaMMO7O9NNDHNdthEZSCDQHAau3JcAsTaFOK6I+2q4I/kW5u8hSEjJk9L45TXSVJw6g1A==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro@0.84.4: - resolution: {integrity: sha512-8ETTubqfD6ornDy2zYDvRcKnVDOXdFJsjetYDBsY4oAsb6NJkiwFR+FaMESyGppFmQUyBQA4H4sFGxzcQSGtFA==} - engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - hasBin: true - metro@0.84.5: resolution: {integrity: sha512-r1liLkyFZMVSEMNjU1CJU5pRzs3NdkxHqXS60O25c0rCIqAR+cGk7rPydw/g0WAIKVXojIBIF45yYBPagJGcgw==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} @@ -4127,10 +4058,6 @@ packages: nwsapi@2.2.24: resolution: {integrity: sha512-7YRhZ3jS45LwmSCT4b2sVFHt/WuovaktDU07QrtOBY2PXskss5a9jfmR9jptyumwXST+rFjrmppMY1KT/yn35A==} - ob1@0.84.4: - resolution: {integrity: sha512-eJXMpz4aQHXF/YBB9ddqZDIS+ooO91hObo9FoW/xBkr54/zCwYYCDqT/O54vNo8kOkWs5Ou/y28NgdrV0edQNA==} - engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - ob1@0.84.5: resolution: {integrity: sha512-aH9RkoZc7w/90HBamFxTw8ZLFr05wXS+iOnvmrgo53Ep8Pyrm5FieQSaPIVROkfFVQISeD/zo92fes26TOwe+A==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} @@ -4385,9 +4312,6 @@ packages: querystringify@2.2.0: resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} - queue@6.0.2: - resolution: {integrity: sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==} - range-parser@1.2.1: resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} engines: {node: '>= 0.6'} @@ -4487,12 +4411,12 @@ packages: react: '*' react-native: 0.83 - 0.86 - react-native@0.86.2: - resolution: {integrity: sha512-zbJXGZpwfZGA79Z9ob6Atvfx4nAQL8yJBa35s58E4Oo+khPykfQP2sTeumkKbjwajFYfVayg8pj7Il9nIfTk7A==} + react-native@0.86.3: + resolution: {integrity: sha512-JR5s3bM9ezud+Mw24GlNXNfthqPIKwrQgPPJcam+L97t2sKjjEavhCzBn+fyqZZRcM5+XlhYxpTxVkK7e1n38Q==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} hasBin: true peerDependencies: - '@react-native/jest-preset': 0.86.2 + '@react-native/jest-preset': 0.86.3 '@types/react': ^19.1.1 react: ^19.2.3 peerDependenciesMeta: @@ -6057,31 +5981,31 @@ snapshots: '@expo-google-fonts/plus-jakarta-sans@0.4.2': {} - '@expo/cli@57.0.17(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.12)(expo-constants@57.0.13)(expo-font@57.0.1)(expo-router@57.0.15)(expo@57.0.15)(react-dom@19.2.3(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2)(typescript@6.0.3)': + '@expo/cli@57.0.19(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.14)(expo-constants@57.0.15)(expo-font@57.0.1)(expo-router@57.0.17)(expo@57.0.17)(react-dom@19.2.3(react@19.2.3))(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2)(typescript@6.0.3)': dependencies: '@expo/code-signing-certificates': 0.0.6 - '@expo/config': 57.0.8(supports-color@10.2.2)(typescript@6.0.3) - '@expo/config-plugins': 57.0.8(supports-color@10.2.2)(typescript@6.0.3) + '@expo/config': 57.0.9(supports-color@10.2.2)(typescript@6.0.3) + '@expo/config-plugins': 57.0.9(supports-color@10.2.2)(typescript@6.0.3) '@expo/devcert': 1.2.1(supports-color@10.2.2) '@expo/env': 2.4.2(supports-color@10.2.2) - '@expo/image-utils': 0.11.4(supports-color@10.2.2)(typescript@6.0.3) - '@expo/inline-modules': 0.1.6(supports-color@10.2.2)(typescript@6.0.3) + '@expo/image-utils': 0.11.5(supports-color@10.2.2)(typescript@6.0.3) + '@expo/inline-modules': 0.1.7(supports-color@10.2.2)(typescript@6.0.3) '@expo/json-file': 11.0.1 - '@expo/log-box': 57.0.3(@expo/dom-webview@57.0.1)(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) - '@expo/metro': 56.0.0(supports-color@10.2.2) - '@expo/metro-config': 57.0.9(expo@57.0.15)(supports-color@10.2.2)(typescript@6.0.3) - '@expo/metro-file-map': 57.0.1(supports-color@10.2.2) + '@expo/log-box': 57.0.4(@expo/dom-webview@57.0.1)(expo@57.0.17)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + '@expo/metro': 56.0.2(supports-color@10.2.2) + '@expo/metro-config': 57.0.11(expo@57.0.17)(supports-color@10.2.2)(typescript@6.0.3) + '@expo/metro-file-map': 57.0.2(supports-color@10.2.2) '@expo/osascript': 2.7.1 '@expo/package-manager': 1.13.1 '@expo/plist': 0.8.1 - '@expo/prebuild-config': 57.0.13(supports-color@10.2.2)(typescript@6.0.3) - '@expo/require-utils': 57.0.4(supports-color@10.2.2)(typescript@6.0.3) - '@expo/router-server': 57.0.7(@expo/metro-runtime@57.0.12)(expo-constants@57.0.13)(expo-font@57.0.1)(expo-router@57.0.15)(expo-server@57.0.3)(expo@57.0.15)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(supports-color@10.2.2) + '@expo/prebuild-config': 57.0.15(supports-color@10.2.2)(typescript@6.0.3) + '@expo/require-utils': 57.0.5(supports-color@10.2.2)(typescript@6.0.3) + '@expo/router-server': 57.0.8(@expo/metro-runtime@57.0.14)(expo-constants@57.0.15)(expo-font@57.0.1)(expo-router@57.0.17)(expo-server@57.0.3)(expo@57.0.17)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(supports-color@10.2.2) '@expo/schema-utils': 57.0.2 '@expo/spawn-async': 1.8.0 '@expo/ws-tunnel': 2.0.0(ws@8.21.3) '@expo/xcpretty': 4.4.4 - '@react-native/dev-middleware': 0.86.2(supports-color@10.2.2) + '@react-native/dev-middleware': 0.86.3(supports-color@10.2.2) accepts: 1.3.8 agent-cli-detector: 0.1.6 arg: 5.0.2 @@ -6093,7 +6017,7 @@ snapshots: connect: 3.7.0(supports-color@10.2.2) debug: 4.4.3(supports-color@10.2.2) dnssd-advertise: 1.1.6 - expo: 57.0.15(c6665681e457b98938e269e908821e4f) + expo: 57.0.17(4c67990895f3488afb1e1e80811ccb84) expo-server: 57.0.3 fetch-nodeshim: 0.4.10 getenv: 2.0.0 @@ -6120,8 +6044,8 @@ snapshots: ws: 8.21.3 zod: 3.25.76 optionalDependencies: - expo-router: 57.0.15(4297cc0b7880e6d80de6379924b45edb) - react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + expo-router: 57.0.17(d6f39c1ccc936eb2d412636bfc344e3d) + react-native: 0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) transitivePeerDependencies: - '@expo/dom-webview' - '@expo/metro-runtime' @@ -6139,12 +6063,12 @@ snapshots: dependencies: node-forge: 1.4.0 - '@expo/config-plugins@57.0.8(supports-color@10.2.2)(typescript@6.0.3)': + '@expo/config-plugins@57.0.9(supports-color@10.2.2)(typescript@6.0.3)': dependencies: '@expo/config-types': 57.0.2 '@expo/json-file': 11.0.1 '@expo/plist': 0.8.1 - '@expo/require-utils': 57.0.4(supports-color@10.2.2)(typescript@6.0.3) + '@expo/require-utils': 57.0.5(supports-color@10.2.2)(typescript@6.0.3) '@expo/sdk-runtime-versions': 1.0.0 chalk: 4.1.2 debug: 4.4.3(supports-color@10.2.2) @@ -6160,12 +6084,12 @@ snapshots: '@expo/config-types@57.0.2': {} - '@expo/config@57.0.8(supports-color@10.2.2)(typescript@6.0.3)': + '@expo/config@57.0.9(supports-color@10.2.2)(typescript@6.0.3)': dependencies: - '@expo/config-plugins': 57.0.8(supports-color@10.2.2)(typescript@6.0.3) + '@expo/config-plugins': 57.0.9(supports-color@10.2.2)(typescript@6.0.3) '@expo/config-types': 57.0.2 '@expo/json-file': 11.0.1 - '@expo/require-utils': 57.0.4(supports-color@10.2.2)(typescript@6.0.3) + '@expo/require-utils': 57.0.5(supports-color@10.2.2)(typescript@6.0.3) deepmerge: 4.3.1 getenv: 2.0.0 glob: 13.0.6 @@ -6183,18 +6107,18 @@ snapshots: transitivePeerDependencies: - supports-color - '@expo/devtools@57.0.1(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)': + '@expo/devtools@57.0.1(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)': dependencies: chalk: 4.1.2 optionalDependencies: react: 19.2.3 - react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + react-native: 0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) - '@expo/dom-webview@57.0.1(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)': + '@expo/dom-webview@57.0.1(expo@57.0.17)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)': dependencies: - expo: 57.0.15(c6665681e457b98938e269e908821e4f) + expo: 57.0.17(4c67990895f3488afb1e1e80811ccb84) react: 19.2.3 - react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + react-native: 0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) '@expo/env@2.4.2(supports-color@10.2.2)': dependencies: @@ -6206,7 +6130,7 @@ snapshots: '@expo/expo-modules-macros-plugin@0.6.1': {} - '@expo/fingerprint@0.20.9(supports-color@10.2.2)': + '@expo/fingerprint@0.20.10(supports-color@10.2.2)': dependencies: '@expo/env': 2.4.2(supports-color@10.2.2) '@expo/spawn-async': 1.8.0 @@ -6222,9 +6146,9 @@ snapshots: transitivePeerDependencies: - supports-color - '@expo/image-utils@0.11.4(supports-color@10.2.2)(typescript@6.0.3)': + '@expo/image-utils@0.11.5(supports-color@10.2.2)(typescript@6.0.3)': dependencies: - '@expo/require-utils': 57.0.4(supports-color@10.2.2)(typescript@6.0.3) + '@expo/require-utils': 57.0.5(supports-color@10.2.2)(typescript@6.0.3) '@expo/spawn-async': 1.8.0 chalk: 4.1.2 getenv: 2.0.0 @@ -6235,9 +6159,9 @@ snapshots: - supports-color - typescript - '@expo/inline-modules@0.1.6(supports-color@10.2.2)(typescript@6.0.3)': + '@expo/inline-modules@0.1.7(supports-color@10.2.2)(typescript@6.0.3)': dependencies: - '@expo/config-plugins': 57.0.8(supports-color@10.2.2)(typescript@6.0.3) + '@expo/config-plugins': 57.0.9(supports-color@10.2.2)(typescript@6.0.3) transitivePeerDependencies: - supports-color - typescript @@ -6247,33 +6171,33 @@ snapshots: '@babel/code-frame': 7.29.7 json5: 2.2.3 - '@expo/local-build-cache-provider@57.0.7(supports-color@10.2.2)(typescript@6.0.3)': + '@expo/local-build-cache-provider@57.0.8(supports-color@10.2.2)(typescript@6.0.3)': dependencies: - '@expo/config': 57.0.8(supports-color@10.2.2)(typescript@6.0.3) + '@expo/config': 57.0.9(supports-color@10.2.2)(typescript@6.0.3) chalk: 4.1.2 transitivePeerDependencies: - supports-color - typescript - '@expo/log-box@57.0.3(@expo/dom-webview@57.0.1)(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)': + '@expo/log-box@57.0.4(@expo/dom-webview@57.0.1)(expo@57.0.17)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)': dependencies: - '@expo/dom-webview': 57.0.1(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + '@expo/dom-webview': 57.0.1(expo@57.0.17)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) anser: 1.4.10 - expo: 57.0.15(c6665681e457b98938e269e908821e4f) + expo: 57.0.17(4c67990895f3488afb1e1e80811ccb84) react: 19.2.3 - react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + react-native: 0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) stacktrace-parser: 0.1.11 - '@expo/metro-config@57.0.9(expo@57.0.15)(supports-color@10.2.2)(typescript@6.0.3)': + '@expo/metro-config@57.0.11(expo@57.0.17)(supports-color@10.2.2)(typescript@6.0.3)': dependencies: '@babel/code-frame': 7.29.7 '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/generator': 7.29.8 - '@expo/config': 57.0.8(supports-color@10.2.2)(typescript@6.0.3) + '@expo/config': 57.0.9(supports-color@10.2.2)(typescript@6.0.3) '@expo/env': 2.4.2(supports-color@10.2.2) '@expo/json-file': 11.0.1 - '@expo/metro': 56.0.0(supports-color@10.2.2) - '@expo/require-utils': 57.0.4(supports-color@10.2.2)(typescript@6.0.3) + '@expo/metro': 56.0.2(supports-color@10.2.2) + '@expo/require-utils': 57.0.5(supports-color@10.2.2)(typescript@6.0.3) '@expo/spawn-async': 1.8.0 '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/remapping': 2.3.5 @@ -6290,14 +6214,14 @@ snapshots: postcss: 8.5.26 resolve-from: 5.0.0 optionalDependencies: - expo: 57.0.15(c6665681e457b98938e269e908821e4f) + expo: 57.0.17(4c67990895f3488afb1e1e80811ccb84) transitivePeerDependencies: - bufferutil - supports-color - typescript - utf-8-validate - '@expo/metro-file-map@57.0.1(supports-color@10.2.2)': + '@expo/metro-file-map@57.0.2(supports-color@10.2.2)': dependencies: debug: 4.4.3(supports-color@10.2.2) fb-watchman: 2.0.2 @@ -6308,35 +6232,35 @@ snapshots: transitivePeerDependencies: - supports-color - '@expo/metro-runtime@57.0.12(@expo/log-box@57.0.3)(expo@57.0.15)(react-dom@19.2.3(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)': + '@expo/metro-runtime@57.0.14(@expo/log-box@57.0.4)(expo@57.0.17)(react-dom@19.2.3(react@19.2.3))(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)': dependencies: - '@expo/log-box': 57.0.3(@expo/dom-webview@57.0.1)(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + '@expo/log-box': 57.0.4(@expo/dom-webview@57.0.1)(expo@57.0.17)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) anser: 1.4.10 - expo: 57.0.15(c6665681e457b98938e269e908821e4f) + expo: 57.0.17(4c67990895f3488afb1e1e80811ccb84) pretty-format: 29.7.0 react: 19.2.3 - react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + react-native: 0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) stacktrace-parser: 0.1.11 whatwg-fetch: 3.6.20 optionalDependencies: react-dom: 19.2.3(react@19.2.3) - '@expo/metro@56.0.0(supports-color@10.2.2)': - dependencies: - metro: 0.84.4(supports-color@10.2.2) - metro-babel-transformer: 0.84.4(supports-color@10.2.2) - metro-cache: 0.84.4(supports-color@10.2.2) - metro-cache-key: 0.84.4 - metro-config: 0.84.4(supports-color@10.2.2) - metro-core: 0.84.4 - metro-file-map: 0.84.4(supports-color@10.2.2) - metro-minify-terser: 0.84.4 - metro-resolver: 0.84.4 - metro-runtime: 0.84.4 - metro-source-map: 0.84.4(supports-color@10.2.2) - metro-symbolicate: 0.84.4(supports-color@10.2.2) - metro-transform-plugins: 0.84.4(supports-color@10.2.2) - metro-transform-worker: 0.84.4(supports-color@10.2.2) + '@expo/metro@56.0.2(supports-color@10.2.2)': + dependencies: + metro: 0.84.5(supports-color@10.2.2) + metro-babel-transformer: 0.84.5(supports-color@10.2.2) + metro-cache: 0.84.5(supports-color@10.2.2) + metro-cache-key: 0.84.5 + metro-config: 0.84.5(supports-color@10.2.2) + metro-core: 0.84.5 + metro-file-map: 0.84.5(supports-color@10.2.2) + metro-minify-terser: 0.84.5 + metro-resolver: 0.84.5 + metro-runtime: 0.84.5 + metro-source-map: 0.84.5(supports-color@10.2.2) + metro-symbolicate: 0.84.5(supports-color@10.2.2) + metro-transform-plugins: 0.84.5(supports-color@10.2.2) + metro-transform-worker: 0.84.5(supports-color@10.2.2) transitivePeerDependencies: - bufferutil - supports-color @@ -6361,23 +6285,23 @@ snapshots: base64-js: 1.5.1 xmlbuilder: 15.1.1 - '@expo/prebuild-config@57.0.13(supports-color@10.2.2)(typescript@6.0.3)': + '@expo/prebuild-config@57.0.15(supports-color@10.2.2)(typescript@6.0.3)': dependencies: - '@expo/config': 57.0.8(supports-color@10.2.2)(typescript@6.0.3) - '@expo/config-plugins': 57.0.8(supports-color@10.2.2)(typescript@6.0.3) + '@expo/config': 57.0.9(supports-color@10.2.2)(typescript@6.0.3) + '@expo/config-plugins': 57.0.9(supports-color@10.2.2)(typescript@6.0.3) '@expo/config-types': 57.0.2 - '@expo/image-utils': 0.11.4(supports-color@10.2.2)(typescript@6.0.3) + '@expo/image-utils': 0.11.5(supports-color@10.2.2)(typescript@6.0.3) '@expo/json-file': 11.0.1 - '@react-native/normalize-colors': 0.86.2 + '@react-native/normalize-colors': 0.86.3 debug: 4.4.3(supports-color@10.2.2) - expo-modules-autolinking: 57.0.10(supports-color@10.2.2)(typescript@6.0.3) + expo-modules-autolinking: 57.0.12(supports-color@10.2.2)(typescript@6.0.3) resolve-from: 5.0.0 semver: 7.8.5 transitivePeerDependencies: - supports-color - typescript - '@expo/require-utils@57.0.4(supports-color@10.2.2)(typescript@6.0.3)': + '@expo/require-utils@57.0.5(supports-color@10.2.2)(typescript@6.0.3)': dependencies: '@babel/code-frame': 7.29.7 '@babel/core': 7.29.7(supports-color@10.2.2) @@ -6387,17 +6311,17 @@ snapshots: transitivePeerDependencies: - supports-color - '@expo/router-server@57.0.7(@expo/metro-runtime@57.0.12)(expo-constants@57.0.13)(expo-font@57.0.1)(expo-router@57.0.15)(expo-server@57.0.3)(expo@57.0.15)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(supports-color@10.2.2)': + '@expo/router-server@57.0.8(@expo/metro-runtime@57.0.14)(expo-constants@57.0.15)(expo-font@57.0.1)(expo-router@57.0.17)(expo-server@57.0.3)(expo@57.0.17)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(supports-color@10.2.2)': dependencies: debug: 4.4.3(supports-color@10.2.2) - expo: 57.0.15(c6665681e457b98938e269e908821e4f) - expo-constants: 57.0.13(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(supports-color@10.2.2) - expo-font: 57.0.1(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + expo: 57.0.17(4c67990895f3488afb1e1e80811ccb84) + expo-constants: 57.0.15(expo@57.0.17)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(supports-color@10.2.2) + expo-font: 57.0.1(expo@57.0.17)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) expo-server: 57.0.3 react: 19.2.3 optionalDependencies: - '@expo/metro-runtime': 57.0.12(@expo/log-box@57.0.3)(expo@57.0.15)(react-dom@19.2.3(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) - expo-router: 57.0.15(4297cc0b7880e6d80de6379924b45edb) + '@expo/metro-runtime': 57.0.14(@expo/log-box@57.0.4)(expo@57.0.17)(react-dom@19.2.3(react@19.2.3))(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + expo-router: 57.0.17(d6f39c1ccc936eb2d412636bfc344e3d) react-dom: 19.2.3(react@19.2.3) transitivePeerDependencies: - supports-color @@ -6412,26 +6336,26 @@ snapshots: '@expo/sudo-prompt@9.3.2': {} - '@expo/ui@57.0.12(ce4128d08365c77ff16d6b531794f812)': + '@expo/ui@57.0.14(a28db61b81538200eb1a9563fb519989)': dependencies: - expo: 57.0.15(c6665681e457b98938e269e908821e4f) + expo: 57.0.17(4c67990895f3488afb1e1e80811ccb84) react: 19.2.3 - react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + react-native: 0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) sf-symbols-typescript: 2.2.0 vaul: 1.1.2(@types/react@19.2.18)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) optionalDependencies: '@babel/core': 7.29.7(supports-color@10.2.2) react-dom: 19.2.3(react@19.2.3) - react-native-worklets: 0.10.1(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2) + react-native-worklets: 0.10.1(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2) transitivePeerDependencies: - '@types/react' - '@types/react-dom' - '@expo/vector-icons@15.1.1(expo-font@57.0.1)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)': + '@expo/vector-icons@15.1.1(expo-font@57.0.1)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)': dependencies: - expo-font: 57.0.1(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + expo-font: 57.0.1(expo@57.0.17)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) react: 19.2.3 - react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + react-native: 0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) '@expo/ws-tunnel@2.0.0(ws@8.21.3)': dependencies: @@ -6443,22 +6367,22 @@ snapshots: chalk: 4.1.2 js-yaml: 4.3.1 - '@gorhom/bottom-sheet@5.2.14(45a98be50302868954db6b093f845761)': + '@gorhom/bottom-sheet@5.2.14(c57327f6cff0368afd765304bb73382e)': dependencies: - '@gorhom/portal': 1.0.14(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + '@gorhom/portal': 1.0.14(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) invariant: 2.2.4 react: 19.2.3 - react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) - react-native-gesture-handler: 2.32.0(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) - react-native-reanimated: 4.5.1(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + react-native: 0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + react-native-gesture-handler: 2.32.0(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + react-native-reanimated: 4.5.1(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) optionalDependencies: '@types/react': 19.2.18 - '@gorhom/portal@1.0.14(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)': + '@gorhom/portal@1.0.14(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)': dependencies: nanoid: 3.3.18 react: 19.2.3 - react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + react-native: 0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) '@humanfs/core@0.19.2': dependencies: @@ -6882,27 +6806,27 @@ snapshots: optionalDependencies: '@types/react': 19.2.18 - '@react-native-async-storage/async-storage@2.2.0(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))': + '@react-native-async-storage/async-storage@2.2.0(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))': dependencies: merge-options: 3.0.4 - react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + react-native: 0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) - '@react-native-masked-view/masked-view@0.3.2(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)': + '@react-native-masked-view/masked-view@0.3.2(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)': dependencies: react: 19.2.3 - react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + react-native: 0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) - '@react-native/assets-registry@0.86.2': {} + '@react-native/assets-registry@0.86.3': {} - '@react-native/babel-plugin-codegen@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + '@react-native/babel-plugin-codegen@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: '@babel/traverse': 7.29.8(supports-color@10.2.2) - '@react-native/codegen': 0.86.2(@babel/core@7.29.7(supports-color@10.2.2)) + '@react-native/codegen': 0.86.3(@babel/core@7.29.7(supports-color@10.2.2)) transitivePeerDependencies: - '@babel/core' - supports-color - '@react-native/babel-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + '@react-native/babel-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/plugin-proposal-export-default-from': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) @@ -6933,14 +6857,14 @@ snapshots: '@babel/plugin-transform-runtime': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) '@babel/plugin-transform-typescript': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) '@babel/plugin-transform-unicode-regex': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) - '@react-native/babel-plugin-codegen': 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@react-native/babel-plugin-codegen': 0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) babel-plugin-syntax-hermes-parser: 0.36.0 babel-plugin-transform-flow-enums: 0.0.2(@babel/core@7.29.7(supports-color@10.2.2)) react-refresh: 0.14.2 transitivePeerDependencies: - supports-color - '@react-native/codegen@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))': + '@react-native/codegen@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/parser': 7.29.8 @@ -6950,9 +6874,9 @@ snapshots: tinyglobby: 0.2.17 yargs: 17.7.3 - '@react-native/community-cli-plugin@0.86.2(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(supports-color@10.2.2)': + '@react-native/community-cli-plugin@0.86.3(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@react-native/dev-middleware': 0.86.2(supports-color@10.2.2) + '@react-native/dev-middleware': 0.86.3(supports-color@10.2.2) debug: 4.4.3(supports-color@10.2.2) invariant: 2.2.4 metro: 0.84.5(supports-color@10.2.2) @@ -6960,15 +6884,15 @@ snapshots: metro-core: 0.84.5 semver: 7.8.5 optionalDependencies: - '@react-native/metro-config': 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@react-native/metro-config': 0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - '@react-native/debugger-frontend@0.86.2': {} + '@react-native/debugger-frontend@0.86.3': {} - '@react-native/debugger-shell@0.86.2(supports-color@10.2.2)': + '@react-native/debugger-shell@0.86.3(supports-color@10.2.2)': dependencies: cross-spawn: 7.0.6 debug: 4.4.3(supports-color@10.2.2) @@ -6976,11 +6900,11 @@ snapshots: transitivePeerDependencies: - supports-color - '@react-native/dev-middleware@0.86.2(supports-color@10.2.2)': + '@react-native/dev-middleware@0.86.3(supports-color@10.2.2)': dependencies: '@isaacs/ttlcache': 1.4.1 - '@react-native/debugger-frontend': 0.86.2 - '@react-native/debugger-shell': 0.86.2(supports-color@10.2.2) + '@react-native/debugger-frontend': 0.86.3 + '@react-native/debugger-shell': 0.86.3(supports-color@10.2.2) chrome-launcher: 0.15.2(supports-color@10.2.2) chromium-edge-launcher: 0.3.0(supports-color@10.2.2) connect: 3.7.0(supports-color@10.2.2) @@ -6995,12 +6919,12 @@ snapshots: - supports-color - utf-8-validate - '@react-native/gradle-plugin@0.86.2': {} + '@react-native/gradle-plugin@0.86.3': {} - '@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2)': + '@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2)': dependencies: '@jest/create-cache-key-function': 29.7.0 - '@react-native/js-polyfills': 0.86.2 + '@react-native/js-polyfills': 0.86.3 babel-jest: 29.7.0(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) jest-environment-node: 29.7.0 react: 19.2.3 @@ -7009,21 +6933,21 @@ snapshots: - '@babel/core' - supports-color - '@react-native/js-polyfills@0.86.2': {} + '@react-native/js-polyfills@0.86.3': {} - '@react-native/metro-babel-transformer@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + '@react-native/metro-babel-transformer@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: '@babel/core': 7.29.7(supports-color@10.2.2) - '@react-native/babel-preset': 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@react-native/babel-preset': 0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) hermes-parser: 0.36.0 nullthrows: 1.1.1 transitivePeerDependencies: - supports-color - '@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + '@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@react-native/js-polyfills': 0.86.2 - '@react-native/metro-babel-transformer': 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@react-native/js-polyfills': 0.86.3 + '@react-native/metro-babel-transformer': 0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) metro-config: 0.84.5(supports-color@10.2.2) metro-runtime: 0.84.5 transitivePeerDependencies: @@ -7035,14 +6959,14 @@ snapshots: '@react-native/normalize-colors@0.74.89': optional: true - '@react-native/normalize-colors@0.86.2': {} + '@react-native/normalize-colors@0.86.3': {} - '@react-native/virtualized-lists@0.86.2(@types/react@19.2.18)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)': + '@react-native/virtualized-lists@0.86.3(@types/react@19.2.18)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)': dependencies: invariant: 2.2.4 nullthrows: 1.1.1 react: 19.2.3 - react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + react-native: 0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) optionalDependencies: '@types/react': 19.2.18 @@ -7071,11 +6995,11 @@ snapshots: '@rtsao/scc@1.1.0': {} - '@shopify/flash-list@2.0.2(@babel/runtime@7.29.7)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)': + '@shopify/flash-list@2.0.2(@babel/runtime@7.29.7)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)': dependencies: '@babel/runtime': 7.29.7 react: 19.2.3 - react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + react-native: 0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) tslib: 2.8.1 '@sinclair/typebox@0.27.12': {} @@ -7097,13 +7021,13 @@ snapshots: '@tanstack/query-core': 5.101.4 react: 19.2.3 - '@testing-library/react-native@14.0.1(jest@29.7.0(@types/node@26.2.0)(supports-color@10.2.2))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(test-renderer@1.2.0(@types/react@19.2.18)(react@19.2.3))': + '@testing-library/react-native@14.0.1(jest@29.7.0(@types/node@26.2.0)(supports-color@10.2.2))(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(test-renderer@1.2.0(@types/react@19.2.18)(react@19.2.3))': dependencies: jest-matcher-utils: 30.4.1 picocolors: 1.1.1 pretty-format: 30.4.1 react: 19.2.3 - react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + react-native: 0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) redent: 3.0.0 test-renderer: 1.2.0(@types/react@19.2.18)(react@19.2.3) optionalDependencies: @@ -7628,7 +7552,7 @@ snapshots: '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.29.7(supports-color@10.2.2)) '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.29.7(supports-color@10.2.2)) - babel-preset-expo@57.0.7(@babel/core@7.29.7(supports-color@10.2.2))(@babel/runtime@7.29.7)(expo@57.0.15)(react-refresh@0.14.2)(supports-color@10.2.2): + babel-preset-expo@57.0.9(@babel/core@7.29.7(supports-color@10.2.2))(@babel/runtime@7.29.7)(expo@57.0.17)(react-refresh@0.14.2)(supports-color@10.2.2): dependencies: '@babel/generator': 7.29.8 '@babel/helper-module-imports': 7.29.7(supports-color@10.2.2) @@ -7666,7 +7590,7 @@ snapshots: '@babel/plugin-transform-typescript': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) '@babel/plugin-transform-unicode-regex': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) '@babel/preset-typescript': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) - '@react-native/babel-plugin-codegen': 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@react-native/babel-plugin-codegen': 0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) babel-plugin-react-compiler: 1.0.0 babel-plugin-react-native-web: 0.21.2 babel-plugin-syntax-hermes-parser: 0.36.1 @@ -7675,7 +7599,7 @@ snapshots: react-refresh: 0.14.2 optionalDependencies: '@babel/runtime': 7.29.7 - expo: 57.0.15(c6665681e457b98938e269e908821e4f) + expo: 57.0.17(4c67990895f3488afb1e1e80811ccb84) transitivePeerDependencies: - '@babel/core' - supports-color @@ -8233,7 +8157,7 @@ snapshots: optionalDependencies: source-map: 0.6.1 - eslint-config-expo@57.0.1(eslint@9.39.5(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3): + eslint-config-expo@57.0.2(eslint@9.39.5(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3): dependencies: '@typescript-eslint/eslint-plugin': 8.67.0(@typescript-eslint/parser@8.67.0(eslint@9.39.5(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@9.39.5(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) '@typescript-eslint/parser': 8.67.0(eslint@9.39.5(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) @@ -8453,160 +8377,155 @@ snapshots: jest-message-util: 29.7.0 jest-util: 29.7.0 - expo-application@57.0.2(expo@57.0.15): + expo-application@57.0.2(expo@57.0.17): dependencies: - expo: 57.0.15(c6665681e457b98938e269e908821e4f) + expo: 57.0.17(4c67990895f3488afb1e1e80811ccb84) - expo-asset@57.0.13(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2)(typescript@6.0.3): + expo-asset@57.0.15(expo@57.0.17)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2)(typescript@6.0.3): dependencies: - '@expo/image-utils': 0.11.4(supports-color@10.2.2)(typescript@6.0.3) - expo: 57.0.15(c6665681e457b98938e269e908821e4f) - expo-constants: 57.0.13(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(supports-color@10.2.2) + '@expo/image-utils': 0.11.5(supports-color@10.2.2)(typescript@6.0.3) + expo: 57.0.17(4c67990895f3488afb1e1e80811ccb84) + expo-constants: 57.0.15(expo@57.0.17)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(supports-color@10.2.2) react: 19.2.3 - react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + react-native: 0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) transitivePeerDependencies: - supports-color - typescript - expo-camera@57.0.4(@types/emscripten@1.41.5)(expo@57.0.15)(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3): + expo-camera@57.0.4(@types/emscripten@1.41.5)(expo@57.0.17)(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3): dependencies: barcode-detector: 3.2.2(@types/emscripten@1.41.5) - expo: 57.0.15(c6665681e457b98938e269e908821e4f) + expo: 57.0.17(4c67990895f3488afb1e1e80811ccb84) react: 19.2.3 - react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + react-native: 0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) optionalDependencies: react-native-web: 0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) transitivePeerDependencies: - '@types/emscripten' - expo-constants@57.0.13(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(supports-color@10.2.2): + expo-constants@57.0.15(expo@57.0.17)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(supports-color@10.2.2): dependencies: '@expo/env': 2.4.2(supports-color@10.2.2) - expo: 57.0.15(c6665681e457b98938e269e908821e4f) - react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + expo: 57.0.17(4c67990895f3488afb1e1e80811ccb84) + react-native: 0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) transitivePeerDependencies: - supports-color - expo-crypto@57.0.2(expo@57.0.15): + expo-crypto@57.0.2(expo@57.0.17): dependencies: - expo: 57.0.15(c6665681e457b98938e269e908821e4f) + expo: 57.0.17(4c67990895f3488afb1e1e80811ccb84) - expo-dev-client@57.0.14(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2)): + expo-dev-client@57.0.16(expo@57.0.17)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2)): dependencies: - expo: 57.0.15(c6665681e457b98938e269e908821e4f) - expo-dev-launcher: 57.0.14(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2)) - expo-dev-menu: 57.0.14(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2)) - expo-dev-menu-interface: 57.0.0(expo@57.0.15) - expo-manifests: 57.0.1(expo@57.0.15) - expo-updates-interface: 57.0.1(expo@57.0.15) + expo: 57.0.17(4c67990895f3488afb1e1e80811ccb84) + expo-dev-launcher: 57.0.16(expo@57.0.17)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2)) + expo-dev-menu: 57.0.16(expo@57.0.17)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2)) + expo-dev-menu-interface: 57.0.0(expo@57.0.17) + expo-manifests: 57.0.1(expo@57.0.17) + expo-updates-interface: 57.0.1(expo@57.0.17) transitivePeerDependencies: - react-native - expo-dev-launcher@57.0.14(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2)): + expo-dev-launcher@57.0.16(expo@57.0.17)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2)): dependencies: '@expo/schema-utils': 57.0.2 - expo: 57.0.15(c6665681e457b98938e269e908821e4f) - expo-dev-menu: 57.0.14(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2)) - expo-manifests: 57.0.1(expo@57.0.15) - react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + expo: 57.0.17(4c67990895f3488afb1e1e80811ccb84) + expo-dev-menu: 57.0.16(expo@57.0.17)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2)) + expo-manifests: 57.0.1(expo@57.0.17) + react-native: 0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) - expo-dev-menu-interface@57.0.0(expo@57.0.15): + expo-dev-menu-interface@57.0.0(expo@57.0.17): dependencies: - expo: 57.0.15(c6665681e457b98938e269e908821e4f) + expo: 57.0.17(4c67990895f3488afb1e1e80811ccb84) - expo-dev-menu@57.0.14(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2)): + expo-dev-menu@57.0.16(expo@57.0.17)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2)): dependencies: - expo: 57.0.15(c6665681e457b98938e269e908821e4f) - expo-dev-menu-interface: 57.0.0(expo@57.0.15) - react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + expo: 57.0.17(4c67990895f3488afb1e1e80811ccb84) + expo-dev-menu-interface: 57.0.0(expo@57.0.17) + react-native: 0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) - expo-device@57.0.1(expo@57.0.15): + expo-device@57.0.1(expo@57.0.17): dependencies: - expo: 57.0.15(c6665681e457b98938e269e908821e4f) + expo: 57.0.17(4c67990895f3488afb1e1e80811ccb84) ua-parser-js: 0.7.41 - expo-document-picker@57.0.1(expo@57.0.15): + expo-document-picker@57.0.1(expo@57.0.17): dependencies: - expo: 57.0.15(c6665681e457b98938e269e908821e4f) - - expo-eas-client@57.0.1: {} + expo: 57.0.17(4c67990895f3488afb1e1e80811ccb84) - expo-file-system@57.0.5(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2)): - dependencies: - expo: 57.0.15(c6665681e457b98938e269e908821e4f) - react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + expo-eas-client@57.0.2: {} - expo-file-system@57.0.6(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2)): + expo-file-system@57.0.6(expo@57.0.17)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2)): dependencies: - expo: 57.0.15(c6665681e457b98938e269e908821e4f) - react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + expo: 57.0.17(4c67990895f3488afb1e1e80811ccb84) + react-native: 0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) - expo-font@57.0.1(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3): + expo-font@57.0.1(expo@57.0.17)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3): dependencies: - expo: 57.0.15(c6665681e457b98938e269e908821e4f) + expo: 57.0.17(4c67990895f3488afb1e1e80811ccb84) fontfaceobserver: 2.3.0 react: 19.2.3 - react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + react-native: 0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) - expo-glass-effect@57.0.1(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3): + expo-glass-effect@57.0.1(expo@57.0.17)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3): dependencies: - expo: 57.0.15(c6665681e457b98938e269e908821e4f) + expo: 57.0.17(4c67990895f3488afb1e1e80811ccb84) react: 19.2.3 - react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + react-native: 0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) - expo-haptics@57.0.2(expo@57.0.15): + expo-haptics@57.0.2(expo@57.0.17): dependencies: - expo: 57.0.15(c6665681e457b98938e269e908821e4f) + expo: 57.0.17(4c67990895f3488afb1e1e80811ccb84) - expo-image-loader@57.0.1(expo@57.0.15): + expo-image-loader@57.0.1(expo@57.0.17): dependencies: - expo: 57.0.15(c6665681e457b98938e269e908821e4f) + expo: 57.0.17(4c67990895f3488afb1e1e80811ccb84) - expo-image-picker@57.0.12(expo@57.0.15): + expo-image-picker@57.0.14(expo@57.0.17): dependencies: - expo: 57.0.15(c6665681e457b98938e269e908821e4f) - expo-image-loader: 57.0.1(expo@57.0.15) + expo: 57.0.17(4c67990895f3488afb1e1e80811ccb84) + expo-image-loader: 57.0.1(expo@57.0.17) - expo-image@57.0.3(expo@57.0.15)(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3): + expo-image@57.0.3(expo@57.0.17)(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3): dependencies: - expo: 57.0.15(c6665681e457b98938e269e908821e4f) + expo: 57.0.17(4c67990895f3488afb1e1e80811ccb84) react: 19.2.3 - react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + react-native: 0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) sf-symbols-typescript: 2.2.0 optionalDependencies: react-native-web: 0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) expo-json-utils@57.0.1: {} - expo-keep-awake@57.0.1(expo@57.0.15)(react@19.2.3): + expo-keep-awake@57.0.1(expo@57.0.17)(react@19.2.3): dependencies: - expo: 57.0.15(c6665681e457b98938e269e908821e4f) + expo: 57.0.17(4c67990895f3488afb1e1e80811ccb84) react: 19.2.3 - expo-linking@57.0.7(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2): + expo-linking@57.0.8(expo@57.0.17)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2): dependencies: - expo-constants: 57.0.13(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(supports-color@10.2.2) + expo-constants: 57.0.15(expo@57.0.17)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(supports-color@10.2.2) invariant: 2.2.4 react: 19.2.3 - react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + react-native: 0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) transitivePeerDependencies: - expo - supports-color - expo-localization@57.0.1(expo@57.0.15)(react@19.2.3): + expo-localization@57.0.1(expo@57.0.17)(react@19.2.3): dependencies: - expo: 57.0.15(c6665681e457b98938e269e908821e4f) + expo: 57.0.17(4c67990895f3488afb1e1e80811ccb84) react: 19.2.3 rtl-detect: 1.1.2 - expo-manifests@57.0.1(expo@57.0.15): + expo-manifests@57.0.1(expo@57.0.17): dependencies: - expo: 57.0.15(c6665681e457b98938e269e908821e4f) + expo: 57.0.17(4c67990895f3488afb1e1e80811ccb84) expo-json-utils: 57.0.1 - expo-modules-autolinking@57.0.10(supports-color@10.2.2)(typescript@6.0.3): + expo-modules-autolinking@57.0.12(supports-color@10.2.2)(typescript@6.0.3): dependencies: - '@expo/require-utils': 57.0.4(supports-color@10.2.2)(typescript@6.0.3) + '@expo/require-utils': 57.0.5(supports-color@10.2.2)(typescript@6.0.3) '@expo/spawn-async': 1.8.0 chalk: 4.1.2 commander: 7.2.0 @@ -8614,53 +8533,53 @@ snapshots: - supports-color - typescript - expo-modules-core@57.0.12(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3): + expo-modules-core@57.0.14(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3): dependencies: '@expo/expo-modules-macros-plugin': 0.6.1 - expo-modules-jsi: 57.0.5(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2)) + expo-modules-jsi: 57.0.6(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2)) invariant: 2.2.4 react: 19.2.3 - react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + react-native: 0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) optionalDependencies: - react-native-worklets: 0.10.1(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2) + react-native-worklets: 0.10.1(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2) - expo-modules-jsi@57.0.5(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2)): + expo-modules-jsi@57.0.6(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2)): dependencies: - react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + react-native: 0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) - expo-notifications@57.0.13(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2)(typescript@6.0.3): + expo-notifications@57.0.15(expo@57.0.17)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2)(typescript@6.0.3): dependencies: - '@expo/image-utils': 0.11.4(supports-color@10.2.2)(typescript@6.0.3) + '@expo/image-utils': 0.11.5(supports-color@10.2.2)(typescript@6.0.3) abort-controller: 3.0.0 badgin: 1.2.3 - expo: 57.0.15(c6665681e457b98938e269e908821e4f) - expo-application: 57.0.2(expo@57.0.15) - expo-constants: 57.0.13(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(supports-color@10.2.2) + expo: 57.0.17(4c67990895f3488afb1e1e80811ccb84) + expo-application: 57.0.2(expo@57.0.17) + expo-constants: 57.0.15(expo@57.0.17)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(supports-color@10.2.2) react: 19.2.3 - react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + react-native: 0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) transitivePeerDependencies: - supports-color - typescript - expo-router@57.0.15(4297cc0b7880e6d80de6379924b45edb): + expo-router@57.0.17(d6f39c1ccc936eb2d412636bfc344e3d): dependencies: - '@expo/log-box': 57.0.3(@expo/dom-webview@57.0.1)(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) - '@expo/metro-runtime': 57.0.12(@expo/log-box@57.0.3)(expo@57.0.15)(react-dom@19.2.3(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + '@expo/log-box': 57.0.4(@expo/dom-webview@57.0.1)(expo@57.0.17)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + '@expo/metro-runtime': 57.0.14(@expo/log-box@57.0.4)(expo@57.0.17)(react-dom@19.2.3(react@19.2.3))(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) '@expo/schema-utils': 57.0.2 - '@expo/ui': 57.0.12(ce4128d08365c77ff16d6b531794f812) + '@expo/ui': 57.0.14(a28db61b81538200eb1a9563fb519989) '@radix-ui/react-slot': 1.3.3(@types/react@19.2.18)(react@19.2.3) '@radix-ui/react-tabs': 1.1.21(@types/react@19.2.18)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@react-native-masked-view/masked-view': 0.3.2(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + '@react-native-masked-view/masked-view': 0.3.2(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) client-only: 0.0.1 color: 4.2.3 debug: 4.4.3(supports-color@10.2.2) escape-string-regexp: 4.0.0 - expo: 57.0.15(c6665681e457b98938e269e908821e4f) - expo-constants: 57.0.13(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(supports-color@10.2.2) - expo-glass-effect: 57.0.1(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) - expo-linking: 57.0.7(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2) + expo: 57.0.17(4c67990895f3488afb1e1e80811ccb84) + expo-constants: 57.0.15(expo@57.0.17)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(supports-color@10.2.2) + expo-glass-effect: 57.0.1(expo@57.0.17)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + expo-linking: 57.0.8(expo@57.0.17)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2) expo-server: 57.0.3 - expo-symbols: 57.0.2(expo-font@57.0.1)(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + expo-symbols: 57.0.2(expo-font@57.0.1)(expo@57.0.17)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) fast-deep-equal: 3.1.3 invariant: 2.2.4 nanoid: 3.3.18 @@ -8668,20 +8587,20 @@ snapshots: react: 19.2.3 react-fast-compare: 3.2.2 react-is: 19.2.8 - react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) - react-native-drawer-layout: 4.2.10(d071066867e06f069216be9cfd299def) - react-native-safe-area-context: 5.7.0(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) - react-native-screens: 4.26.2(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + react-native: 0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + react-native-drawer-layout: 4.2.10(b4989148c3d1358ee81abc9680ef4d80) + react-native-safe-area-context: 5.7.0(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + react-native-screens: 4.26.2(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) server-only: 0.0.1 sf-symbols-typescript: 2.2.0 shallowequal: 1.1.0 standard-navigation: 0.0.5 vaul: 1.1.2(@types/react@19.2.18)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) optionalDependencies: - '@testing-library/react-native': 14.0.1(jest@29.7.0(@types/node@26.2.0)(supports-color@10.2.2))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(test-renderer@1.2.0(@types/react@19.2.18)(react@19.2.3)) + '@testing-library/react-native': 14.0.1(jest@29.7.0(@types/node@26.2.0)(supports-color@10.2.2))(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(test-renderer@1.2.0(@types/react@19.2.18)(react@19.2.3)) react-dom: 19.2.3(react@19.2.3) - react-native-gesture-handler: 2.32.0(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) - react-native-reanimated: 4.5.1(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + react-native-gesture-handler: 2.32.0(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + react-native-reanimated: 4.5.1(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) react-native-web: 0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) transitivePeerDependencies: - '@babel/core' @@ -8691,55 +8610,55 @@ snapshots: - react-native-worklets - supports-color - expo-secure-store@57.0.1(expo@57.0.15): + expo-secure-store@57.0.2(expo@57.0.17): dependencies: - expo: 57.0.15(c6665681e457b98938e269e908821e4f) + expo: 57.0.17(4c67990895f3488afb1e1e80811ccb84) expo-server@57.0.3: {} - expo-splash-screen@57.0.7(expo@57.0.15)(supports-color@10.2.2)(typescript@6.0.3): + expo-splash-screen@57.0.8(expo@57.0.17)(supports-color@10.2.2)(typescript@6.0.3): dependencies: - '@expo/config-plugins': 57.0.8(supports-color@10.2.2)(typescript@6.0.3) - '@expo/image-utils': 0.11.4(supports-color@10.2.2)(typescript@6.0.3) - expo: 57.0.15(c6665681e457b98938e269e908821e4f) + '@expo/config-plugins': 57.0.9(supports-color@10.2.2)(typescript@6.0.3) + '@expo/image-utils': 0.11.5(supports-color@10.2.2)(typescript@6.0.3) + expo: 57.0.17(4c67990895f3488afb1e1e80811ccb84) xml2js: 0.6.0 transitivePeerDependencies: - supports-color - typescript - expo-status-bar@57.0.1(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3): + expo-status-bar@57.0.1(expo@57.0.17)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3): dependencies: - expo: 57.0.15(c6665681e457b98938e269e908821e4f) + expo: 57.0.17(4c67990895f3488afb1e1e80811ccb84) react: 19.2.3 - react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + react-native: 0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) expo-structured-headers@57.0.0: {} - expo-symbols@57.0.2(expo-font@57.0.1)(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3): + expo-symbols@57.0.2(expo-font@57.0.1)(expo@57.0.17)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3): dependencies: '@expo-google-fonts/material-symbols': 0.4.44 - expo: 57.0.15(c6665681e457b98938e269e908821e4f) - expo-font: 57.0.1(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + expo: 57.0.17(4c67990895f3488afb1e1e80811ccb84) + expo-font: 57.0.1(expo@57.0.17)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) react: 19.2.3 - react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + react-native: 0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) sf-symbols-typescript: 2.2.0 - expo-system-ui@57.0.2(expo@57.0.15)(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(supports-color@10.2.2): + expo-system-ui@57.0.3(expo@57.0.17)(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(supports-color@10.2.2): dependencies: - '@react-native/normalize-colors': 0.86.2 + '@react-native/normalize-colors': 0.86.3 debug: 4.4.3(supports-color@10.2.2) - expo: 57.0.15(c6665681e457b98938e269e908821e4f) - react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + expo: 57.0.17(4c67990895f3488afb1e1e80811ccb84) + react-native: 0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) optionalDependencies: react-native-web: 0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) transitivePeerDependencies: - supports-color - expo-updates-interface@57.0.1(expo@57.0.15): + expo-updates-interface@57.0.1(expo@57.0.17): dependencies: - expo: 57.0.15(c6665681e457b98938e269e908821e4f) + expo: 57.0.17(4c67990895f3488afb1e1e80811ccb84) - expo-updates@57.0.16(expo-dev-client@57.0.14(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2)))(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2): + expo-updates@57.0.18(expo-dev-client@57.0.16(expo@57.0.17)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2)))(expo@57.0.17)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2): dependencies: '@expo/code-signing-certificates': 0.0.6 '@expo/plist': 0.8.1 @@ -8747,57 +8666,57 @@ snapshots: arg: 4.1.3 chalk: 4.1.2 debug: 4.4.3(supports-color@10.2.2) - expo: 57.0.15(c6665681e457b98938e269e908821e4f) - expo-eas-client: 57.0.1 - expo-manifests: 57.0.1(expo@57.0.15) + expo: 57.0.17(4c67990895f3488afb1e1e80811ccb84) + expo-eas-client: 57.0.2 + expo-manifests: 57.0.1(expo@57.0.17) expo-structured-headers: 57.0.0 - expo-updates-interface: 57.0.1(expo@57.0.15) + expo-updates-interface: 57.0.1(expo@57.0.17) getenv: 2.0.0 glob: 13.0.6 ignore: 5.3.2 nullthrows: 1.1.1 react: 19.2.3 - react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + react-native: 0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) resolve-from: 5.0.0 optionalDependencies: - expo-dev-client: 57.0.14(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2)) + expo-dev-client: 57.0.16(expo@57.0.17)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2)) transitivePeerDependencies: - supports-color - expo-web-browser@57.0.2(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2)): + expo-web-browser@57.0.2(expo@57.0.17)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2)): dependencies: - expo: 57.0.15(c6665681e457b98938e269e908821e4f) - react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + expo: 57.0.17(4c67990895f3488afb1e1e80811ccb84) + react-native: 0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) - expo@57.0.15(c6665681e457b98938e269e908821e4f): + expo@57.0.17(4c67990895f3488afb1e1e80811ccb84): dependencies: '@babel/runtime': 7.29.7 - '@expo/cli': 57.0.17(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.12)(expo-constants@57.0.13)(expo-font@57.0.1)(expo-router@57.0.15)(expo@57.0.15)(react-dom@19.2.3(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2)(typescript@6.0.3) - '@expo/config': 57.0.8(supports-color@10.2.2)(typescript@6.0.3) - '@expo/config-plugins': 57.0.8(supports-color@10.2.2)(typescript@6.0.3) - '@expo/devtools': 57.0.1(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) - '@expo/fingerprint': 0.20.9(supports-color@10.2.2) - '@expo/local-build-cache-provider': 57.0.7(supports-color@10.2.2)(typescript@6.0.3) - '@expo/log-box': 57.0.3(@expo/dom-webview@57.0.1)(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) - '@expo/metro': 56.0.0(supports-color@10.2.2) - '@expo/metro-config': 57.0.9(expo@57.0.15)(supports-color@10.2.2)(typescript@6.0.3) + '@expo/cli': 57.0.19(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.14)(expo-constants@57.0.15)(expo-font@57.0.1)(expo-router@57.0.17)(expo@57.0.17)(react-dom@19.2.3(react@19.2.3))(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2)(typescript@6.0.3) + '@expo/config': 57.0.9(supports-color@10.2.2)(typescript@6.0.3) + '@expo/config-plugins': 57.0.9(supports-color@10.2.2)(typescript@6.0.3) + '@expo/devtools': 57.0.1(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + '@expo/fingerprint': 0.20.10(supports-color@10.2.2) + '@expo/local-build-cache-provider': 57.0.8(supports-color@10.2.2)(typescript@6.0.3) + '@expo/log-box': 57.0.4(@expo/dom-webview@57.0.1)(expo@57.0.17)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + '@expo/metro': 56.0.2(supports-color@10.2.2) + '@expo/metro-config': 57.0.11(expo@57.0.17)(supports-color@10.2.2)(typescript@6.0.3) '@ungap/structured-clone': 1.3.3 - babel-preset-expo: 57.0.7(@babel/core@7.29.7(supports-color@10.2.2))(@babel/runtime@7.29.7)(expo@57.0.15)(react-refresh@0.14.2)(supports-color@10.2.2) - expo-asset: 57.0.13(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2)(typescript@6.0.3) - expo-constants: 57.0.13(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(supports-color@10.2.2) - expo-file-system: 57.0.5(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2)) - expo-font: 57.0.1(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) - expo-keep-awake: 57.0.1(expo@57.0.15)(react@19.2.3) - expo-modules-autolinking: 57.0.10(supports-color@10.2.2)(typescript@6.0.3) - expo-modules-core: 57.0.12(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + babel-preset-expo: 57.0.9(@babel/core@7.29.7(supports-color@10.2.2))(@babel/runtime@7.29.7)(expo@57.0.17)(react-refresh@0.14.2)(supports-color@10.2.2) + expo-asset: 57.0.15(expo@57.0.17)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2)(typescript@6.0.3) + expo-constants: 57.0.15(expo@57.0.17)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(supports-color@10.2.2) + expo-file-system: 57.0.6(expo@57.0.17)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2)) + expo-font: 57.0.1(expo@57.0.17)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + expo-keep-awake: 57.0.1(expo@57.0.17)(react@19.2.3) + expo-modules-autolinking: 57.0.12(supports-color@10.2.2)(typescript@6.0.3) + expo-modules-core: 57.0.14(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) pretty-format: 29.7.0 react: 19.2.3 - react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + react-native: 0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) react-refresh: 0.14.2 whatwg-url-minimum: 0.1.2 optionalDependencies: - '@expo/dom-webview': 57.0.1(expo@57.0.15)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) - '@expo/metro-runtime': 57.0.12(@expo/log-box@57.0.3)(expo@57.0.15)(react-dom@19.2.3(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + '@expo/dom-webview': 57.0.1(expo@57.0.17)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + '@expo/metro-runtime': 57.0.14(@expo/log-box@57.0.4)(expo@57.0.17)(react-dom@19.2.3(react@19.2.3))(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) react-dom: 19.2.3(react@19.2.3) react-native-web: 0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) transitivePeerDependencies: @@ -9023,7 +8942,7 @@ snapshots: dependencies: function-bind: 1.1.2 - hermes-compiler@250829098.0.16: {} + hermes-compiler@250829098.0.17: {} hermes-estree@0.25.1: {} @@ -9112,10 +9031,6 @@ snapshots: ignore@7.0.6: {} - image-size@1.2.1: - dependencies: - queue: 6.0.2 - import-fresh@3.3.1: dependencies: parent-module: 1.0.1 @@ -9475,11 +9390,11 @@ snapshots: jest-mock: 29.7.0 jest-util: 29.7.0 - jest-expo@57.0.4(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(expo@57.0.15)(jest@29.7.0(@types/node@26.2.0)(supports-color@10.2.2))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2): + jest-expo@57.0.5(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(expo@57.0.17)(jest@29.7.0(@types/node@26.2.0)(supports-color@10.2.2))(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2): dependencies: '@jest/create-cache-key-function': 29.7.0 '@jest/globals': 29.7.0(supports-color@10.2.2) - '@react-native/jest-preset': 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2) + '@react-native/jest-preset': 0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2) babel-jest: 29.7.0(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) jest-environment-jsdom: 29.7.0(supports-color@10.2.2) jest-snapshot: 29.7.0(supports-color@10.2.2) @@ -9487,12 +9402,12 @@ snapshots: jest-watch-typeahead: 2.2.1(jest@29.7.0(@types/node@26.2.0)(supports-color@10.2.2)) json5: 2.2.3 lodash: 4.18.1 - react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + react-native: 0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) react-test-renderer: 19.2.3(react@19.2.3) server-only: 0.0.1 stacktrace-js: 2.0.2 optionalDependencies: - expo: 57.0.15(c6665681e457b98938e269e908821e4f) + expo: 57.0.17(4c67990895f3488afb1e1e80811ccb84) transitivePeerDependencies: - '@babel/core' - bufferutil @@ -9928,16 +9843,6 @@ snapshots: merge-stream@2.0.0: {} - metro-babel-transformer@0.84.4(supports-color@10.2.2): - dependencies: - '@babel/core': 7.29.7(supports-color@10.2.2) - flow-enums-runtime: 0.0.6 - hermes-parser: 0.35.0 - metro-cache-key: 0.84.4 - nullthrows: 1.1.1 - transitivePeerDependencies: - - supports-color - metro-babel-transformer@0.84.5(supports-color@10.2.2): dependencies: '@babel/core': 7.29.7(supports-color@10.2.2) @@ -9948,23 +9853,10 @@ snapshots: transitivePeerDependencies: - supports-color - metro-cache-key@0.84.4: - dependencies: - flow-enums-runtime: 0.0.6 - metro-cache-key@0.84.5: dependencies: flow-enums-runtime: 0.0.6 - metro-cache@0.84.4(supports-color@10.2.2): - dependencies: - exponential-backoff: 3.1.3 - flow-enums-runtime: 0.0.6 - https-proxy-agent: 7.0.6(supports-color@10.2.2) - metro-core: 0.84.4 - transitivePeerDependencies: - - supports-color - metro-cache@0.84.5(supports-color@10.2.2): dependencies: exponential-backoff: 3.1.3 @@ -9974,21 +9866,6 @@ snapshots: transitivePeerDependencies: - supports-color - metro-config@0.84.4(supports-color@10.2.2): - dependencies: - connect: 3.7.0(supports-color@10.2.2) - flow-enums-runtime: 0.0.6 - jest-validate: 29.7.0 - metro: 0.84.4(supports-color@10.2.2) - metro-cache: 0.84.4(supports-color@10.2.2) - metro-core: 0.84.4 - metro-runtime: 0.84.4 - yaml: 2.9.0 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - metro-config@0.84.5(supports-color@10.2.2): dependencies: connect: 3.7.0(supports-color@10.2.2) @@ -10004,32 +9881,12 @@ snapshots: - supports-color - utf-8-validate - metro-core@0.84.4: - dependencies: - flow-enums-runtime: 0.0.6 - lodash.throttle: 4.1.1 - metro-resolver: 0.84.4 - metro-core@0.84.5: dependencies: flow-enums-runtime: 0.0.6 lodash.throttle: 4.1.1 metro-resolver: 0.84.5 - metro-file-map@0.84.4(supports-color@10.2.2): - dependencies: - debug: 4.4.3(supports-color@10.2.2) - fb-watchman: 2.0.2 - flow-enums-runtime: 0.0.6 - graceful-fs: 4.2.11 - invariant: 2.2.4 - jest-worker: 29.7.0 - micromatch: 4.0.8 - nullthrows: 1.1.1 - walker: 1.0.8 - transitivePeerDependencies: - - supports-color - metro-file-map@0.84.5(supports-color@10.2.2): dependencies: debug: 4.4.3(supports-color@10.2.2) @@ -10044,48 +9901,20 @@ snapshots: transitivePeerDependencies: - supports-color - metro-minify-terser@0.84.4: - dependencies: - flow-enums-runtime: 0.0.6 - terser: 5.50.0 - metro-minify-terser@0.84.5: dependencies: flow-enums-runtime: 0.0.6 terser: 5.50.0 - metro-resolver@0.84.4: - dependencies: - flow-enums-runtime: 0.0.6 - metro-resolver@0.84.5: dependencies: flow-enums-runtime: 0.0.6 - metro-runtime@0.84.4: - dependencies: - '@babel/runtime': 7.29.7 - flow-enums-runtime: 0.0.6 - metro-runtime@0.84.5: dependencies: '@babel/runtime': 7.29.7 flow-enums-runtime: 0.0.6 - metro-source-map@0.84.4(supports-color@10.2.2): - dependencies: - '@babel/traverse': 7.29.8(supports-color@10.2.2) - '@babel/types': 7.29.8 - flow-enums-runtime: 0.0.6 - invariant: 2.2.4 - metro-symbolicate: 0.84.4(supports-color@10.2.2) - nullthrows: 1.1.1 - ob1: 0.84.4 - source-map: 0.5.7 - vlq: 1.0.1 - transitivePeerDependencies: - - supports-color - metro-source-map@0.84.5(supports-color@10.2.2): dependencies: '@babel/traverse': 7.29.8(supports-color@10.2.2) @@ -10100,17 +9929,6 @@ snapshots: transitivePeerDependencies: - supports-color - metro-symbolicate@0.84.4(supports-color@10.2.2): - dependencies: - flow-enums-runtime: 0.0.6 - invariant: 2.2.4 - metro-source-map: 0.84.4(supports-color@10.2.2) - nullthrows: 1.1.1 - source-map: 0.5.7 - vlq: 1.0.1 - transitivePeerDependencies: - - supports-color - metro-symbolicate@0.84.5(supports-color@10.2.2): dependencies: flow-enums-runtime: 0.0.6 @@ -10122,17 +9940,6 @@ snapshots: transitivePeerDependencies: - supports-color - metro-transform-plugins@0.84.4(supports-color@10.2.2): - dependencies: - '@babel/core': 7.29.7(supports-color@10.2.2) - '@babel/generator': 7.29.8 - '@babel/template': 7.29.7 - '@babel/traverse': 7.29.8(supports-color@10.2.2) - flow-enums-runtime: 0.0.6 - nullthrows: 1.1.1 - transitivePeerDependencies: - - supports-color - metro-transform-plugins@0.84.5(supports-color@10.2.2): dependencies: '@babel/core': 7.29.7(supports-color@10.2.2) @@ -10144,26 +9951,6 @@ snapshots: transitivePeerDependencies: - supports-color - metro-transform-worker@0.84.4(supports-color@10.2.2): - dependencies: - '@babel/core': 7.29.7(supports-color@10.2.2) - '@babel/generator': 7.29.8 - '@babel/parser': 7.29.8 - '@babel/types': 7.29.8 - flow-enums-runtime: 0.0.6 - metro: 0.84.4(supports-color@10.2.2) - metro-babel-transformer: 0.84.4(supports-color@10.2.2) - metro-cache: 0.84.4(supports-color@10.2.2) - metro-cache-key: 0.84.4 - metro-minify-terser: 0.84.4 - metro-source-map: 0.84.4(supports-color@10.2.2) - metro-transform-plugins: 0.84.4(supports-color@10.2.2) - nullthrows: 1.1.1 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - metro-transform-worker@0.84.5(supports-color@10.2.2): dependencies: '@babel/core': 7.29.7(supports-color@10.2.2) @@ -10184,52 +9971,6 @@ snapshots: - supports-color - utf-8-validate - metro@0.84.4(supports-color@10.2.2): - dependencies: - '@babel/code-frame': 7.29.7 - '@babel/core': 7.29.7(supports-color@10.2.2) - '@babel/generator': 7.29.8 - '@babel/parser': 7.29.8 - '@babel/template': 7.29.7 - '@babel/traverse': 7.29.8(supports-color@10.2.2) - '@babel/types': 7.29.8 - accepts: 2.0.0 - ci-info: 2.0.0 - connect: 3.7.0(supports-color@10.2.2) - debug: 4.4.3(supports-color@10.2.2) - error-stack-parser: 2.1.4 - flow-enums-runtime: 0.0.6 - graceful-fs: 4.2.11 - hermes-parser: 0.35.0 - image-size: 1.2.1 - invariant: 2.2.4 - jest-worker: 29.7.0 - jsc-safe-url: 0.2.4 - lodash.throttle: 4.1.1 - metro-babel-transformer: 0.84.4(supports-color@10.2.2) - metro-cache: 0.84.4(supports-color@10.2.2) - metro-cache-key: 0.84.4 - metro-config: 0.84.4(supports-color@10.2.2) - metro-core: 0.84.4 - metro-file-map: 0.84.4(supports-color@10.2.2) - metro-resolver: 0.84.4 - metro-runtime: 0.84.4 - metro-source-map: 0.84.4(supports-color@10.2.2) - metro-symbolicate: 0.84.4(supports-color@10.2.2) - metro-transform-plugins: 0.84.4(supports-color@10.2.2) - metro-transform-worker: 0.84.4(supports-color@10.2.2) - mime-types: 3.0.2 - nullthrows: 1.1.1 - serialize-error: 2.1.0 - source-map: 0.5.7 - throat: 5.0.0 - ws: 7.5.13 - yargs: 17.7.3 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - metro@0.84.5(supports-color@10.2.2): dependencies: '@babel/code-frame': 7.29.7 @@ -10371,10 +10112,6 @@ snapshots: nwsapi@2.2.24: {} - ob1@0.84.4: - dependencies: - flow-enums-runtime: 0.0.6 - ob1@0.84.5: dependencies: flow-enums-runtime: 0.0.6 @@ -10644,10 +10381,6 @@ snapshots: querystringify@2.2.0: {} - queue@6.0.2: - dependencies: - inherits: 2.0.4 - range-parser@1.2.1: {} react-devtools-core@6.1.5: @@ -10669,7 +10402,7 @@ snapshots: dependencies: react: 19.2.3 - react-i18next@17.0.11(i18next@26.3.6(typescript@6.0.3))(react-dom@19.2.3(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(typescript@6.0.3): + react-i18next@17.0.11(i18next@26.3.6(typescript@6.0.3))(react-dom@19.2.3(react@19.2.3))(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(typescript@6.0.3): dependencies: '@babel/runtime': 7.29.7 html-parse-stringify: 4.0.1 @@ -10678,7 +10411,7 @@ snapshots: use-sync-external-store: 1.6.0(react@19.2.3) optionalDependencies: react-dom: 19.2.3(react@19.2.3) - react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + react-native: 0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) typescript: 6.0.3 react-is@16.13.1: {} @@ -10687,47 +10420,47 @@ snapshots: react-is@19.2.8: {} - react-native-drawer-layout@4.2.10(d071066867e06f069216be9cfd299def): + react-native-drawer-layout@4.2.10(b4989148c3d1358ee81abc9680ef4d80): dependencies: color: 4.2.3 react: 19.2.3 - react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) - react-native-gesture-handler: 2.32.0(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) - react-native-reanimated: 4.5.1(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + react-native: 0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + react-native-gesture-handler: 2.32.0(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + react-native-reanimated: 4.5.1(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) use-latest-callback: 0.2.6(react@19.2.3) - react-native-gesture-handler@2.32.0(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3): + react-native-gesture-handler@2.32.0(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3): dependencies: '@egjs/hammerjs': 2.0.17 '@types/react-test-renderer': 19.1.0 hoist-non-react-statics: 3.3.2 invariant: 2.2.4 react: 19.2.3 - react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + react-native: 0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) - react-native-is-edge-to-edge@1.3.1(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3): + react-native-is-edge-to-edge@1.3.1(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3): dependencies: react: 19.2.3 - react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + react-native: 0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) - react-native-reanimated@4.5.1(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3): + react-native-reanimated@4.5.1(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3): dependencies: react: 19.2.3 - react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) - react-native-is-edge-to-edge: 1.3.1(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) - react-native-worklets: 0.10.1(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2) + react-native: 0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + react-native-is-edge-to-edge: 1.3.1(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + react-native-worklets: 0.10.1(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2) semver: 7.8.5 - react-native-safe-area-context@5.7.0(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3): + react-native-safe-area-context@5.7.0(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3): dependencies: react: 19.2.3 - react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + react-native: 0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) - react-native-screens@4.26.2(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3): + react-native-screens@4.26.2(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3): dependencies: react: 19.2.3 react-freeze: 1.0.4(react@19.2.3) - react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + react-native: 0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) warn-once: 0.1.1 react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3): @@ -10746,7 +10479,7 @@ snapshots: - encoding optional: true - react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2): + react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2): dependencies: '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/plugin-transform-arrow-functions': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) @@ -10759,23 +10492,23 @@ snapshots: '@babel/plugin-transform-unicode-regex': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) '@babel/preset-typescript': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) '@babel/types': 7.29.8 - '@react-native/metro-config': 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@react-native/metro-config': 0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) convert-source-map: 2.0.0 react: 19.2.3 - react-native: 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) + react-native: 0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2) semver: 7.8.5 transitivePeerDependencies: - supports-color - react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2): + react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2): dependencies: - '@react-native/assets-registry': 0.86.2 - '@react-native/codegen': 0.86.2(@babel/core@7.29.7(supports-color@10.2.2)) - '@react-native/community-cli-plugin': 0.86.2(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(supports-color@10.2.2) - '@react-native/gradle-plugin': 0.86.2 - '@react-native/js-polyfills': 0.86.2 - '@react-native/normalize-colors': 0.86.2 - '@react-native/virtualized-lists': 0.86.2(@types/react@19.2.18)(react-native@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + '@react-native/assets-registry': 0.86.3 + '@react-native/codegen': 0.86.3(@babel/core@7.29.7(supports-color@10.2.2)) + '@react-native/community-cli-plugin': 0.86.3(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(supports-color@10.2.2) + '@react-native/gradle-plugin': 0.86.3 + '@react-native/js-polyfills': 0.86.3 + '@react-native/normalize-colors': 0.86.3 + '@react-native/virtualized-lists': 0.86.3(@types/react@19.2.18)(react-native@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/jest-preset@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.2.18)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) abort-controller: 3.0.0 anser: 1.4.10 ansi-regex: 5.0.1 @@ -10783,7 +10516,7 @@ snapshots: base64-js: 1.5.1 commander: 12.1.0 flow-enums-runtime: 0.0.6 - hermes-compiler: 250829098.0.16 + hermes-compiler: 250829098.0.17 invariant: 2.2.4 memoize-one: 5.2.1 metro-runtime: 0.84.5 @@ -10803,7 +10536,7 @@ snapshots: ws: 7.5.13 yargs: 17.7.3 optionalDependencies: - '@react-native/jest-preset': 0.86.2(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2) + '@react-native/jest-preset': 0.86.3(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2) '@types/react': 19.2.18 transitivePeerDependencies: - '@babel/core'