Full Code of mfts/papermark for AI

main fd7d01771a5c cached
1468 files
6.2 MB
1.7M tokens
2537 symbols
1 requests
Download .txt
Showing preview only (6,783K chars total). Download the full file or copy to clipboard to get everything.
Repository: mfts/papermark
Branch: main
Commit: fd7d01771a5c
Files: 1468
Total size: 6.2 MB

Directory structure:
gitextract_qhe605p_/

├── .agents/
│   └── skills/
│       ├── frontend-design/
│       │   ├── LICENSE.txt
│       │   └── SKILL.md
│       ├── gh-cli/
│       │   └── SKILL.md
│       ├── postgres/
│       │   ├── SKILL.md
│       │   └── references/
│       │       ├── backup-recovery.md
│       │       ├── index-optimization.md
│       │       ├── indexing.md
│       │       ├── memory-management-ops.md
│       │       ├── monitoring.md
│       │       ├── mvcc-transactions.md
│       │       ├── mvcc-vacuum.md
│       │       ├── optimization-checklist.md
│       │       ├── partitioning.md
│       │       ├── process-architecture.md
│       │       ├── ps-cli-api-insights.md
│       │       ├── ps-cli-commands.md
│       │       ├── ps-connection-pooling.md
│       │       ├── ps-connections.md
│       │       ├── ps-extensions.md
│       │       ├── ps-insights.md
│       │       ├── query-patterns.md
│       │       ├── replication.md
│       │       ├── schema-design.md
│       │       ├── storage-layout.md
│       │       └── wal-operations.md
│       ├── vercel-react-best-practices/
│       │   ├── AGENTS.md
│       │   ├── SKILL.md
│       │   └── rules/
│       │       ├── advanced-event-handler-refs.md
│       │       ├── advanced-init-once.md
│       │       ├── advanced-use-latest.md
│       │       ├── async-api-routes.md
│       │       ├── async-defer-await.md
│       │       ├── async-dependencies.md
│       │       ├── async-parallel.md
│       │       ├── async-suspense-boundaries.md
│       │       ├── bundle-barrel-imports.md
│       │       ├── bundle-conditional.md
│       │       ├── bundle-defer-third-party.md
│       │       ├── bundle-dynamic-imports.md
│       │       ├── bundle-preload.md
│       │       ├── client-event-listeners.md
│       │       ├── client-localstorage-schema.md
│       │       ├── client-passive-event-listeners.md
│       │       ├── client-swr-dedup.md
│       │       ├── js-batch-dom-css.md
│       │       ├── js-cache-function-results.md
│       │       ├── js-cache-property-access.md
│       │       ├── js-cache-storage.md
│       │       ├── js-combine-iterations.md
│       │       ├── js-early-exit.md
│       │       ├── js-hoist-regexp.md
│       │       ├── js-index-maps.md
│       │       ├── js-length-check-first.md
│       │       ├── js-min-max-loop.md
│       │       ├── js-set-map-lookups.md
│       │       ├── js-tosorted-immutable.md
│       │       ├── rendering-activity.md
│       │       ├── rendering-animate-svg-wrapper.md
│       │       ├── rendering-conditional-render.md
│       │       ├── rendering-content-visibility.md
│       │       ├── rendering-hoist-jsx.md
│       │       ├── rendering-hydration-no-flicker.md
│       │       ├── rendering-hydration-suppress-warning.md
│       │       ├── rendering-svg-precision.md
│       │       ├── rendering-usetransition-loading.md
│       │       ├── rerender-defer-reads.md
│       │       ├── rerender-dependencies.md
│       │       ├── rerender-derived-state-no-effect.md
│       │       ├── rerender-derived-state.md
│       │       ├── rerender-functional-setstate.md
│       │       ├── rerender-lazy-state-init.md
│       │       ├── rerender-memo-with-default-value.md
│       │       ├── rerender-memo.md
│       │       ├── rerender-move-effect-to-event.md
│       │       ├── rerender-simple-expression-in-memo.md
│       │       ├── rerender-transitions.md
│       │       ├── rerender-use-ref-transient-values.md
│       │       ├── server-after-nonblocking.md
│       │       ├── server-auth-actions.md
│       │       ├── server-cache-lru.md
│       │       ├── server-cache-react.md
│       │       ├── server-dedup-props.md
│       │       ├── server-parallel-fetching.md
│       │       └── server-serialization.md
│       └── web-design-guidelines/
│           └── SKILL.md
├── .cursor/
│   └── rules/
│       ├── rule-claude-opus.mdc
│       └── rule-trigger-typescript.mdc
├── .cursorignore
├── .eslintrc.json
├── .github/
│   ├── CODEOWNERS
│   ├── ISSUE_TEMPLATE/
│   │   └── oss-gg-hack-template.yml
│   └── workflows/
│       └── cla.yml
├── .gitignore
├── .prettierignore
├── CLA.md
├── LICENSE
├── Pipfile
├── README.md
├── SECURITY.md
├── app/
│   ├── (auth)/
│   │   ├── auth/
│   │   │   ├── confirm-email-change/
│   │   │   │   └── [token]/
│   │   │   │       ├── page-client.tsx
│   │   │   │       ├── page.tsx
│   │   │   │       └── utils.ts
│   │   │   ├── email/
│   │   │   │   └── [[...params]]/
│   │   │   │       ├── page-client.tsx
│   │   │   │       └── page.tsx
│   │   │   └── saml/
│   │   │       ├── page-client.tsx
│   │   │       └── page.tsx
│   │   ├── layout.tsx
│   │   ├── login/
│   │   │   ├── page-client.tsx
│   │   │   └── page.tsx
│   │   ├── register/
│   │   │   ├── page-client.tsx
│   │   │   └── page.tsx
│   │   └── verify/
│   │       ├── invitation/
│   │       │   ├── AcceptInvitationButton.tsx
│   │       │   ├── InvitationStatusContent.tsx
│   │       │   ├── page.tsx
│   │       │   └── status/
│   │       │       └── ClientRedirect.tsx
│   │       └── page.tsx
│   ├── (ee)/
│   │   ├── LICENSE.md
│   │   ├── README.md
│   │   └── api/
│   │       ├── ai/
│   │       │   ├── chat/
│   │       │   │   ├── [chatId]/
│   │       │   │   │   ├── messages/
│   │       │   │   │   │   └── route.ts
│   │       │   │   │   └── route.ts
│   │       │   │   └── route.ts
│   │       │   └── store/
│   │       │       ├── runs/
│   │       │       │   └── [runId]/
│   │       │       │       └── route.ts
│   │       │       └── teams/
│   │       │           └── [teamId]/
│   │       │               ├── datarooms/
│   │       │               │   └── [dataroomId]/
│   │       │               │       └── route.ts
│   │       │               ├── documents/
│   │       │               │   └── [documentId]/
│   │       │               │       └── route.ts
│   │       │               └── route.ts
│   │       ├── auth/
│   │       │   └── saml/
│   │       │       ├── authorize/
│   │       │       │   └── route.ts
│   │       │       ├── callback/
│   │       │       │   └── route.ts
│   │       │       ├── token/
│   │       │       │   └── route.ts
│   │       │       ├── userinfo/
│   │       │       │   └── route.ts
│   │       │       └── verify/
│   │       │           └── route.ts
│   │       ├── faqs/
│   │       │   └── route.ts
│   │       ├── links/
│   │       │   └── [id]/
│   │       │       └── upload/
│   │       │           └── route.ts
│   │       ├── scim/
│   │       │   └── v2.0/
│   │       │       └── [...directory]/
│   │       │           └── route.ts
│   │       ├── teams/
│   │       │   └── [teamId]/
│   │       │       ├── directory-sync/
│   │       │       │   └── route.ts
│   │       │       └── saml/
│   │       │           └── route.ts
│   │       ├── workflow-entry/
│   │       │   ├── domains/
│   │       │   │   └── [...domainSlug]/
│   │       │   │       └── route.ts
│   │       │   └── link/
│   │       │       └── [entryLinkId]/
│   │       │           ├── access/
│   │       │           │   └── route.ts
│   │       │           └── verify/
│   │       │               └── route.ts
│   │       └── workflows/
│   │           ├── [workflowId]/
│   │           │   ├── executions/
│   │           │   │   └── route.ts
│   │           │   ├── route.ts
│   │           │   └── steps/
│   │           │       ├── [stepId]/
│   │           │       │   └── route.ts
│   │           │       └── route.ts
│   │           └── route.ts
│   ├── api/
│   │   ├── auth/
│   │   │   └── verify-code/
│   │   │       └── route.ts
│   │   ├── cron/
│   │   │   ├── dataroom-digest/
│   │   │   │   ├── daily/
│   │   │   │   │   └── route.ts
│   │   │   │   └── weekly/
│   │   │   │       └── route.ts
│   │   │   ├── domains/
│   │   │   │   ├── route.ts
│   │   │   │   └── utils.ts
│   │   │   ├── welcome-user/
│   │   │   │   └── route.ts
│   │   │   └── year-in-review/
│   │   │       └── route.ts
│   │   ├── csp-report/
│   │   │   └── route.ts
│   │   ├── feature-flags/
│   │   │   └── route.ts
│   │   ├── help/
│   │   │   └── route.ts
│   │   ├── integrations/
│   │   │   └── slack/
│   │   │       └── oauth/
│   │   │           ├── authorize/
│   │   │           │   └── route.ts
│   │   │           └── callback/
│   │   │               └── route.ts
│   │   ├── og/
│   │   │   ├── route.tsx
│   │   │   └── yir/
│   │   │       └── route.tsx
│   │   ├── verify/
│   │   │   └── login-link/
│   │   │       └── route.ts
│   │   ├── views/
│   │   │   ├── pages/
│   │   │   │   └── route.ts
│   │   │   └── route.ts
│   │   ├── views-dataroom/
│   │   │   └── route.ts
│   │   └── webhooks/
│   │       └── callback/
│   │           └── route.ts
│   ├── layout.tsx
│   └── robots.txt
├── components/
│   ├── EmailForm.tsx
│   ├── Skeleton.tsx
│   ├── account/
│   │   ├── account-header.tsx
│   │   ├── update-subscription.tsx
│   │   └── upload-avatar.tsx
│   ├── agreements/
│   │   └── agreement-card.tsx
│   ├── ai-elements/
│   │   ├── conversation.tsx
│   │   ├── message.tsx
│   │   ├── prompt-input.tsx
│   │   └── shimmer.tsx
│   ├── analytics/
│   │   ├── analytics-card.tsx
│   │   ├── dashboard-views-chart.tsx
│   │   ├── documents-table.tsx
│   │   ├── links-table.tsx
│   │   ├── time-range-select.tsx
│   │   ├── views-table.tsx
│   │   └── visitors-table.tsx
│   ├── billing/
│   │   ├── add-seat-modal.tsx
│   │   ├── plan-badge.tsx
│   │   ├── pro-annual-banner.tsx
│   │   ├── pro-banner.tsx
│   │   ├── upgrade-plan-container.tsx
│   │   ├── upgrade-plan-modal-old.tsx
│   │   ├── upgrade-plan-modal-with-discount.tsx
│   │   ├── upgrade-plan-modal.tsx
│   │   └── yearly-upgrade-banner.tsx
│   ├── blur-image.tsx
│   ├── charts/
│   │   ├── bar-chart-tooltip.tsx
│   │   ├── bar-chart.tsx
│   │   └── utils.ts
│   ├── conversations/
│   │   └── index.tsx
│   ├── datarooms/
│   │   ├── actions/
│   │   │   ├── download-dataroom.tsx
│   │   │   ├── generate-index-button.tsx
│   │   │   ├── generate-index-dialog.tsx
│   │   │   ├── rebuild-index-button.tsx
│   │   │   └── remove-document-modal.tsx
│   │   ├── add-dataroom-modal.tsx
│   │   ├── add-viewer-modal.tsx
│   │   ├── analytics/
│   │   │   ├── analytics-overview.tsx
│   │   │   ├── document-analytics-tree.tsx
│   │   │   └── mock-analytics-table.tsx
│   │   ├── dataroom-breadcrumb.tsx
│   │   ├── dataroom-card.tsx
│   │   ├── dataroom-document-card.tsx
│   │   ├── dataroom-header.tsx
│   │   ├── dataroom-items-list.tsx
│   │   ├── dataroom-navigation.tsx
│   │   ├── dataroom-trial-modal.tsx
│   │   ├── download-progress-modal.tsx
│   │   ├── edit-dataroom-document-modal.tsx
│   │   ├── empty-dataroom.tsx
│   │   ├── export-visits-modal.tsx
│   │   ├── folders/
│   │   │   ├── index.tsx
│   │   │   ├── selection-tree.tsx
│   │   │   ├── sidebar-tree.tsx
│   │   │   ├── utils.ts
│   │   │   └── view-tree.tsx
│   │   ├── groups/
│   │   │   ├── add-group-modal.tsx
│   │   │   ├── add-member-modal.tsx
│   │   │   ├── delete-group/
│   │   │   │   ├── delete-group-modal.tsx
│   │   │   │   └── index.tsx
│   │   │   ├── group-card-placeholder.tsx
│   │   │   ├── group-card.tsx
│   │   │   ├── group-header.tsx
│   │   │   ├── group-member-table.tsx
│   │   │   ├── group-navigation.tsx
│   │   │   ├── group-permissions.tsx
│   │   │   └── set-unified-permissions-modal.tsx
│   │   ├── move-dataroom-folder-modal.tsx
│   │   ├── settings/
│   │   │   ├── bulk-download-settings.tsx
│   │   │   ├── dataroom-tag-section.tsx
│   │   │   ├── delete-dataroooom/
│   │   │   │   ├── delete-dataroom-modal.tsx
│   │   │   │   └── index.tsx
│   │   │   ├── duplicate-dataroom.tsx
│   │   │   ├── introduction-settings.tsx
│   │   │   ├── notification-settings.tsx
│   │   │   ├── permission-settings.tsx
│   │   │   └── settings-tabs.tsx
│   │   ├── sortable/
│   │   │   ├── sortable-item.tsx
│   │   │   └── sortable-list.tsx
│   │   └── stats-card.tsx
│   ├── document-upload.tsx
│   ├── documents/
│   │   ├── actions/
│   │   │   ├── delete-documents-modal.tsx
│   │   │   └── delete-folder-modal.tsx
│   │   ├── add-document-modal.tsx
│   │   ├── add-document-to-dataroom-modal.tsx
│   │   ├── add-folder-to-dataroom-modal.tsx
│   │   ├── alert.tsx
│   │   ├── annotations/
│   │   │   ├── annotation-form.tsx
│   │   │   └── annotation-sheet.tsx
│   │   ├── breadcrumb.tsx
│   │   ├── delete-folder-modal.tsx
│   │   ├── document-card.tsx
│   │   ├── document-header.tsx
│   │   ├── document-preview-button.tsx
│   │   ├── document-preview-modal.tsx
│   │   ├── document-stats-placeholder.tsx
│   │   ├── documents-list.tsx
│   │   ├── drag-and-drop/
│   │   │   ├── draggable-item.tsx
│   │   │   └── droppable-folder.tsx
│   │   ├── empty-document.tsx
│   │   ├── export-visits-modal.tsx
│   │   ├── file-process-status-bar.tsx
│   │   ├── filters/
│   │   │   └── sort-button.tsx
│   │   ├── folder-card.tsx
│   │   ├── hidden-document-card.tsx
│   │   ├── hidden-documents-list.tsx
│   │   ├── hidden-folder-card.tsx
│   │   ├── link-document-indicator.tsx
│   │   ├── loading-document.tsx
│   │   ├── move-folder-modal.tsx
│   │   ├── notion-accessibility-indicator.tsx
│   │   ├── pagination.tsx
│   │   ├── preview-viewers/
│   │   │   ├── index.ts
│   │   │   ├── preview-excel-viewer.tsx
│   │   │   ├── preview-image-viewer.tsx
│   │   │   ├── preview-pages-viewer.tsx
│   │   │   └── preview-viewer.tsx
│   │   ├── stats-card.tsx
│   │   ├── stats-chart-dummy.tsx
│   │   ├── stats-chart-skeleton.tsx
│   │   ├── stats-chart.tsx
│   │   ├── stats-element.tsx
│   │   ├── stats.tsx
│   │   ├── video-analytics.tsx
│   │   ├── video-chart-placeholder.tsx
│   │   └── video-stats-placeholder.tsx
│   ├── domains/
│   │   ├── add-domain-modal.tsx
│   │   ├── delete-domain-modal.tsx
│   │   ├── domain-card.tsx
│   │   ├── domain-configuration.tsx
│   │   └── use-domain-status.ts
│   ├── emails/
│   │   ├── custom-domain-setup.tsx
│   │   ├── data-rooms-information.tsx
│   │   ├── dataroom-digest-notification.tsx
│   │   ├── dataroom-notification.tsx
│   │   ├── dataroom-trial-24h.tsx
│   │   ├── dataroom-trial-end.tsx
│   │   ├── dataroom-trial-welcome.tsx
│   │   ├── dataroom-upload-notification.tsx
│   │   ├── deleted-domain.tsx
│   │   ├── download-ready.tsx
│   │   ├── email-updated.tsx
│   │   ├── export-ready.tsx
│   │   ├── hundred-views-congrats.tsx
│   │   ├── installed-integration-notification.tsx
│   │   ├── invalid-domain.tsx
│   │   ├── onboarding-1.tsx
│   │   ├── onboarding-2.tsx
│   │   ├── onboarding-3.tsx
│   │   ├── onboarding-4.tsx
│   │   ├── otp-verification.tsx
│   │   ├── shared/
│   │   │   └── footer.tsx
│   │   ├── slack-integration.tsx
│   │   ├── team-invitation.tsx
│   │   ├── thousand-views-congrats.tsx
│   │   ├── upgrade-one-month-checkin.tsx
│   │   ├── upgrade-personal-welcome.tsx
│   │   ├── upgrade-plan.tsx
│   │   ├── upgrade-six-month-checkin.tsx
│   │   ├── verification-email-change.tsx
│   │   ├── verification-link.tsx
│   │   ├── viewed-dataroom-paused.tsx
│   │   ├── viewed-dataroom.tsx
│   │   ├── viewed-document-paused.tsx
│   │   ├── viewed-document.tsx
│   │   ├── welcome.tsx
│   │   └── year-in-review-papermark.tsx
│   ├── folders/
│   │   ├── add-folder-modal.tsx
│   │   ├── edit-folder-modal.tsx
│   │   ├── folder-color-picker.tsx
│   │   └── folder-icon-picker.tsx
│   ├── gtm-component.tsx
│   ├── hooks/
│   │   ├── use-optimistic-update.ts
│   │   └── useLastUsed.tsx
│   ├── layouts/
│   │   ├── app.tsx
│   │   ├── blocking-modal.tsx
│   │   ├── breadcrumb.tsx
│   │   └── trial-banner.tsx
│   ├── links/
│   │   ├── delete-link-modal.tsx
│   │   ├── embed-code-modal.tsx
│   │   ├── link-active-controls.tsx
│   │   ├── link-sheet/
│   │   │   ├── agreement-panel/
│   │   │   │   └── index.tsx
│   │   │   ├── agreement-section.tsx
│   │   │   ├── ai-agents-section.tsx
│   │   │   ├── allow-download-section.tsx
│   │   │   ├── allow-list-section.tsx
│   │   │   ├── allow-notification-section.tsx
│   │   │   ├── conversation-section.tsx
│   │   │   ├── custom-fields-panel/
│   │   │   │   ├── custom-field.tsx
│   │   │   │   └── index.tsx
│   │   │   ├── custom-fields-section.tsx
│   │   │   ├── dataroom-link-sheet.tsx
│   │   │   ├── deny-list-section.tsx
│   │   │   ├── domain-section.tsx
│   │   │   ├── email-authentication-section.tsx
│   │   │   ├── email-protection-section.tsx
│   │   │   ├── expiration-section.tsx
│   │   │   ├── expirationIn-section.tsx
│   │   │   ├── feedback-section.tsx
│   │   │   ├── index-file-section.tsx
│   │   │   ├── index.tsx
│   │   │   ├── link-item.tsx
│   │   │   ├── link-options.tsx
│   │   │   ├── link-success-sheet.tsx
│   │   │   ├── og-section.tsx
│   │   │   ├── password-section.tsx
│   │   │   ├── permissions-sheet.tsx
│   │   │   ├── pro-banner-section.tsx
│   │   │   ├── question-section.tsx
│   │   │   ├── screenshot-protection-section.tsx
│   │   │   ├── tags/
│   │   │   │   ├── tag-badge.tsx
│   │   │   │   ├── tag-details.tsx
│   │   │   │   └── tag-section.tsx
│   │   │   ├── upload-section/
│   │   │   │   └── index.tsx
│   │   │   ├── watermark-panel/
│   │   │   │   └── index.tsx
│   │   │   ├── watermark-section.tsx
│   │   │   └── welcome-message-section.tsx
│   │   ├── links-table.tsx
│   │   ├── links-visitors.tsx
│   │   └── preview-button.tsx
│   ├── navigation-menu.tsx
│   ├── profile-menu.tsx
│   ├── profile-search-trigger.tsx
│   ├── providers/
│   │   └── posthog-provider.tsx
│   ├── search-box.tsx
│   ├── search-command.tsx
│   ├── settings/
│   │   ├── delete-team-modal.tsx
│   │   ├── delete-team.tsx
│   │   ├── global-block-list-form.tsx
│   │   ├── ignored-domains-form.tsx
│   │   ├── og-preview.tsx
│   │   ├── settings-header.tsx
│   │   ├── slack-settings-skeleton.tsx
│   │   ├── survey-settings.tsx
│   │   └── timezone-selector.tsx
│   ├── shared/
│   │   ├── dealflow-popup.tsx
│   │   ├── icons/
│   │   │   ├── advanced-sheet.tsx
│   │   │   ├── alert-circle.tsx
│   │   │   ├── arrow-up.tsx
│   │   │   ├── badge-check.tsx
│   │   │   ├── bar-chart.tsx
│   │   │   ├── check-cirlce-2.tsx
│   │   │   ├── check.tsx
│   │   │   ├── chevron-down.tsx
│   │   │   ├── chevron-right.tsx
│   │   │   ├── chevron-up.tsx
│   │   │   ├── circle.tsx
│   │   │   ├── cloud-download-off.tsx
│   │   │   ├── copy-right.tsx
│   │   │   ├── copy.tsx
│   │   │   ├── external-link.tsx
│   │   │   ├── eye-off.tsx
│   │   │   ├── eye.tsx
│   │   │   ├── facebook.tsx
│   │   │   ├── file-up.tsx
│   │   │   ├── files/
│   │   │   │   ├── cad.tsx
│   │   │   │   ├── docs.tsx
│   │   │   │   ├── image.tsx
│   │   │   │   ├── map.tsx
│   │   │   │   ├── notion.tsx
│   │   │   │   ├── pdf.tsx
│   │   │   │   ├── sheet.tsx
│   │   │   │   ├── slides.tsx
│   │   │   │   └── video.tsx
│   │   │   ├── folder.tsx
│   │   │   ├── github.tsx
│   │   │   ├── globe.tsx
│   │   │   ├── google.tsx
│   │   │   ├── grip-vertical.tsx
│   │   │   ├── home.tsx
│   │   │   ├── index.tsx
│   │   │   ├── linkedin.tsx
│   │   │   ├── menu.tsx
│   │   │   ├── moon.tsx
│   │   │   ├── more-horizontal.tsx
│   │   │   ├── more-vertical.tsx
│   │   │   ├── papermark-sparkle.tsx
│   │   │   ├── passkey.tsx
│   │   │   ├── pie-chart.tsx
│   │   │   ├── portrait-landscape.tsx
│   │   │   ├── producthunt.tsx
│   │   │   ├── search.tsx
│   │   │   ├── settings.tsx
│   │   │   ├── slack-icon.tsx
│   │   │   ├── sparkle.tsx
│   │   │   ├── sun.tsx
│   │   │   ├── teams.tsx
│   │   │   ├── twitter.tsx
│   │   │   ├── user-round.tsx
│   │   │   ├── x-circle.tsx
│   │   │   └── x.tsx
│   │   └── logo-cloud.tsx
│   ├── sidebar/
│   │   ├── app-sidebar.tsx
│   │   ├── banners/
│   │   │   └── slack-banner.tsx
│   │   ├── nav-main.tsx
│   │   ├── nav-user.tsx
│   │   └── team-switcher.tsx
│   ├── sidebar-folders.tsx
│   ├── tab-menu.tsx
│   ├── tags/
│   │   └── add-tag-modal.tsx
│   ├── teams/
│   │   ├── add-team-member-modal.tsx
│   │   ├── add-team-modal.tsx
│   │   ├── delete-team-modal.tsx
│   │   └── select-team.tsx
│   ├── theme-provider.tsx
│   ├── theme-toggle.tsx
│   ├── ui/
│   │   ├── accordion.tsx
│   │   ├── alert-dialog.tsx
│   │   ├── alert.tsx
│   │   ├── avatar.tsx
│   │   ├── badge.tsx
│   │   ├── bar-list.tsx
│   │   ├── breadcrumb.tsx
│   │   ├── button-group.tsx
│   │   ├── button.tsx
│   │   ├── calendar.tsx
│   │   ├── card.tsx
│   │   ├── carousel.tsx
│   │   ├── checkbox.tsx
│   │   ├── collapsible.tsx
│   │   ├── command.tsx
│   │   ├── copy-button.tsx
│   │   ├── devices.tsx
│   │   ├── dialog.tsx
│   │   ├── drawer.tsx
│   │   ├── dropdown-menu.tsx
│   │   ├── feature-preview.tsx
│   │   ├── file-upload.tsx
│   │   ├── form-hook.tsx
│   │   ├── form.tsx
│   │   ├── gauge.tsx
│   │   ├── hover-card.tsx
│   │   ├── input-group.tsx
│   │   ├── input-otp.tsx
│   │   ├── input.tsx
│   │   ├── label.tsx
│   │   ├── loading-dots.module.css
│   │   ├── loading-dots.tsx
│   │   ├── loading-spinner.module.css
│   │   ├── loading-spinner.tsx
│   │   ├── modal.tsx
│   │   ├── multi-select-v2.tsx
│   │   ├── nextra-filetree.tsx
│   │   ├── pagination.tsx
│   │   ├── phone-input.tsx
│   │   ├── popover.tsx
│   │   ├── portal.tsx
│   │   ├── progress.tsx
│   │   ├── radio-group.tsx
│   │   ├── resizable.tsx
│   │   ├── responsive-button.tsx
│   │   ├── rich-text-editor.tsx
│   │   ├── scroll-area.tsx
│   │   ├── select.tsx
│   │   ├── separator.tsx
│   │   ├── sheet.tsx
│   │   ├── sidebar.tsx
│   │   ├── single-select.tsx
│   │   ├── skeleton.tsx
│   │   ├── smart-date-time-picker.tsx
│   │   ├── sonner.tsx
│   │   ├── status-badge.tsx
│   │   ├── switch.tsx
│   │   ├── tab-select.tsx
│   │   ├── table.tsx
│   │   ├── tabs.tsx
│   │   ├── textarea.tsx
│   │   ├── timestamp-tooltip.tsx
│   │   ├── toggle-group.tsx
│   │   ├── toggle.tsx
│   │   ├── tooltip.tsx
│   │   └── upgrade-button.tsx
│   ├── upload-notification.tsx
│   ├── upload-zone.tsx
│   ├── user-agent-icon.tsx
│   ├── view/
│   │   ├── ScreenProtection.tsx
│   │   ├── access-form/
│   │   │   ├── access-form-theme.tsx
│   │   │   ├── agreement-section.tsx
│   │   │   ├── custom-fields-section.tsx
│   │   │   ├── email-section.tsx
│   │   │   ├── email-verification-form.tsx
│   │   │   ├── index.tsx
│   │   │   ├── name-section.tsx
│   │   │   └── password-section.tsx
│   │   ├── annotations/
│   │   │   ├── annotation-panel.tsx
│   │   │   └── annotation-toggle.tsx
│   │   ├── conversations/
│   │   │   └── sidebar.tsx
│   │   ├── custom-metatag.tsx
│   │   ├── dataroom/
│   │   │   ├── dataroom-document-view.tsx
│   │   │   ├── dataroom-view.tsx
│   │   │   ├── document-card.tsx
│   │   │   ├── document-upload-modal.tsx
│   │   │   ├── download-otp-verification.tsx
│   │   │   ├── downloads-panel.tsx
│   │   │   ├── folder-card.tsx
│   │   │   ├── index-file-dialog.tsx
│   │   │   ├── introduction-modal.tsx
│   │   │   ├── nav-dataroom.tsx
│   │   │   ├── pending-document-card.tsx
│   │   │   └── viewer-download-progress-modal.tsx
│   │   ├── document-view.tsx
│   │   ├── link-preview.tsx
│   │   ├── nav.tsx
│   │   ├── powered-by.tsx
│   │   ├── question.tsx
│   │   ├── report-form.tsx
│   │   ├── toolbar.tsx
│   │   ├── view-data.tsx
│   │   ├── viewer/
│   │   │   ├── advanced-excel-viewer.tsx
│   │   │   ├── away-poster.tsx
│   │   │   ├── dataroom-viewer.tsx
│   │   │   ├── download-only-viewer.tsx
│   │   │   ├── excel-viewer.tsx
│   │   │   ├── image-viewer.tsx
│   │   │   ├── notion-page.tsx
│   │   │   ├── pages-horizontal-viewer.tsx
│   │   │   ├── pages-vertical-viewer.tsx
│   │   │   ├── pdf-default-viewer.tsx
│   │   │   ├── video-player.tsx
│   │   │   ├── video-viewer.tsx
│   │   │   └── viewer-surface-theme.tsx
│   │   ├── visitor-graph.tsx
│   │   └── watermark-svg.tsx
│   ├── viewer-upload-component.tsx
│   ├── viewer-upload-zone.tsx
│   ├── visitors/
│   │   ├── contacts-document-table.tsx
│   │   ├── contacts-table.tsx
│   │   ├── data-table-pagination.tsx
│   │   ├── dataroom-view-stats.tsx
│   │   ├── dataroom-viewers.tsx
│   │   ├── dataroom-visitor-custom-fields.tsx
│   │   ├── dataroom-visitor-useragent.tsx
│   │   ├── dataroom-visitors-history.tsx
│   │   ├── dataroom-visitors-table.tsx
│   │   ├── document-view-stats.tsx
│   │   ├── visitor-avatar.tsx
│   │   ├── visitor-chart.tsx
│   │   ├── visitor-clicks.tsx
│   │   ├── visitor-custom-fields.tsx
│   │   ├── visitor-group-modal.tsx
│   │   ├── visitor-groups-section.tsx
│   │   ├── visitor-useragent-base.tsx
│   │   ├── visitor-useragent-placeholder.tsx
│   │   ├── visitor-useragent.tsx
│   │   ├── visitor-video-chart.tsx
│   │   └── visitors-table.tsx
│   ├── webhooks/
│   │   └── webhook-events.tsx
│   ├── welcome/
│   │   ├── containers/
│   │   │   ├── link-option-container.tsx
│   │   │   ├── onboarding-dataroom-link-options.tsx
│   │   │   ├── onboarding-link-options.tsx
│   │   │   └── upload-container.tsx
│   │   ├── dataroom-ai-generate.tsx
│   │   ├── dataroom-choice.tsx
│   │   ├── dataroom-trial.tsx
│   │   ├── dataroom-upload.tsx
│   │   ├── dataroom.tsx
│   │   ├── intro.tsx
│   │   ├── next.tsx
│   │   ├── notion-form.tsx
│   │   ├── select.tsx
│   │   ├── special-upload.tsx
│   │   └── upload.tsx
│   └── yearly-recap/
│       ├── globe.tsx
│       ├── index.ts
│       ├── yearly-recap-banner.tsx
│       └── yearly-recap-modal.tsx
├── components.json
├── context/
│   ├── pending-uploads-context.tsx
│   └── team-context.tsx
├── ee/
│   ├── LICENSE.md
│   ├── README.md
│   ├── emails/
│   │   └── pause-resume-reminder.tsx
│   ├── features/
│   │   ├── access-notifications/
│   │   │   ├── components/
│   │   │   │   └── blocked-email-attempt.tsx
│   │   │   ├── index.ts
│   │   │   └── lib/
│   │   │       ├── report-denied-access-attempt.ts
│   │   │       └── send-blocked-email-attempt.ts
│   │   ├── ai/
│   │   │   ├── components/
│   │   │   │   ├── agents-settings-card.tsx
│   │   │   │   ├── ai-indexing-status.tsx
│   │   │   │   ├── chat-message.tsx
│   │   │   │   ├── document-ai-dialog.tsx
│   │   │   │   ├── document-context-selector.tsx
│   │   │   │   ├── viewer-chat-panel.tsx
│   │   │   │   ├── viewer-chat-provider.tsx
│   │   │   │   ├── viewer-chat-toggle.tsx
│   │   │   │   └── viewer-thread-selector.tsx
│   │   │   ├── hooks/
│   │   │   │   └── use-ai-indexing-status.ts
│   │   │   ├── lib/
│   │   │   │   ├── chat/
│   │   │   │   │   ├── create-chat.ts
│   │   │   │   │   ├── generate-chat-title.ts
│   │   │   │   │   ├── get-filtered-dataroom-document-ids.ts
│   │   │   │   │   └── send-message.ts
│   │   │   │   ├── file-processing/
│   │   │   │   │   ├── extract-document-metadata.ts
│   │   │   │   │   └── process-document-for-vector-store.ts
│   │   │   │   ├── models/
│   │   │   │   │   ├── google.ts
│   │   │   │   │   └── openai.ts
│   │   │   │   ├── permissions/
│   │   │   │   │   └── validate-chat-access.ts
│   │   │   │   ├── stream/
│   │   │   │   │   └── parse-text-stream.ts
│   │   │   │   ├── trigger/
│   │   │   │   │   ├── add-file-to-vector-store.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── process-document-for-ai.ts
│   │   │   │   │   ├── process-excel-for-ai.ts
│   │   │   │   │   ├── process-image-for-ai.ts
│   │   │   │   │   ├── process-pdf-for-ai.ts
│   │   │   │   │   └── types.ts
│   │   │   │   └── vector-stores/
│   │   │   │       ├── create-dataroom-vector-store.ts
│   │   │   │       ├── create-team-vector-store.ts
│   │   │   │       ├── delete-vector-store.ts
│   │   │   │       ├── get-vector-store-info.ts
│   │   │   │       ├── remove-file-from-vector-store.ts
│   │   │   │       └── upload-file-to-vector-store.ts
│   │   │   └── schemas/
│   │   │       └── chat.ts
│   │   ├── billing/
│   │   │   ├── cancellation/
│   │   │   │   ├── api/
│   │   │   │   │   ├── automatic-unpause-route.ts
│   │   │   │   │   ├── cancel-route.ts
│   │   │   │   │   ├── cancellation-feedback-route.ts
│   │   │   │   │   ├── pause-route.ts
│   │   │   │   │   ├── reactivate-route.ts
│   │   │   │   │   ├── retention-route.ts
│   │   │   │   │   ├── send-pause-resume-notification.ts
│   │   │   │   │   └── unpause-route.ts
│   │   │   │   ├── components/
│   │   │   │   │   ├── cancellation-modal.tsx
│   │   │   │   │   ├── confirm-cancellation-modal.tsx
│   │   │   │   │   ├── feedback-modal.tsx
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── pause-subscription-modal.tsx
│   │   │   │   │   ├── reason-base-modal.tsx
│   │   │   │   │   ├── retention-offer-modal.tsx
│   │   │   │   │   └── schedule-call-modal.tsx
│   │   │   │   ├── constants.ts
│   │   │   │   ├── emails/
│   │   │   │   │   ├── components/
│   │   │   │   │   │   └── pause-resume-reminder.tsx
│   │   │   │   │   └── lib/
│   │   │   │   │       └── send-email-pause-resume-reminder.ts
│   │   │   │   └── lib/
│   │   │   │       ├── constants.ts
│   │   │   │       ├── is-team-paused.ts
│   │   │   │       └── trigger/
│   │   │   │           ├── pause-resume-notification.ts
│   │   │   │           └── unpause-task.ts
│   │   │   └── renewal-reminder/
│   │   │       ├── emails/
│   │   │       │   └── subscription-renewal-reminder.tsx
│   │   │       └── lib/
│   │   │           └── send-subscription-renewal-reminder.ts
│   │   ├── conversations/
│   │   │   ├── api/
│   │   │   │   ├── conversations-route.ts
│   │   │   │   ├── send-conversation-new-message-notification.ts
│   │   │   │   ├── send-conversation-team-member-notification.ts
│   │   │   │   ├── team-conversations-route.ts
│   │   │   │   ├── team-faqs-route.ts
│   │   │   │   └── toggle-conversations-route.ts
│   │   │   ├── components/
│   │   │   │   ├── dashboard/
│   │   │   │   │   ├── conversation-list-item.tsx
│   │   │   │   │   ├── conversations-not-enabled-banner.tsx
│   │   │   │   │   ├── edit-faq-modal.tsx
│   │   │   │   │   ├── link-option-conversation-section.tsx
│   │   │   │   │   └── publish-faq-modal.tsx
│   │   │   │   ├── shared/
│   │   │   │   │   ├── conversation-document-context.tsx
│   │   │   │   │   └── conversation-message.tsx
│   │   │   │   └── viewer/
│   │   │   │       ├── conversation-view-sidebar.tsx
│   │   │   │       └── faq-section.tsx
│   │   │   ├── emails/
│   │   │   │   ├── components/
│   │   │   │   │   ├── conversation-notification.tsx
│   │   │   │   │   └── conversation-team-notification.tsx
│   │   │   │   └── lib/
│   │   │   │       ├── send-conversation-notification.ts
│   │   │   │       └── send-conversation-team-notification.ts
│   │   │   ├── lib/
│   │   │   │   ├── api/
│   │   │   │   │   ├── conversations/
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── messages/
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   └── notifications/
│   │   │   │   │       └── index.ts
│   │   │   │   ├── schemas/
│   │   │   │   │   └── faq.ts
│   │   │   │   └── trigger/
│   │   │   │       └── conversation-message-notification.ts
│   │   │   └── pages/
│   │   │       ├── conversation-detail.tsx
│   │   │       ├── conversation-overview.tsx
│   │   │       └── faq-overview.tsx
│   │   ├── conversions/
│   │   │   └── python/
│   │   │       └── docx-sanitizer.py
│   │   ├── dataroom-invitations/
│   │   │   ├── api/
│   │   │   │   ├── group-invite.ts
│   │   │   │   ├── link-invite.ts
│   │   │   │   └── uninvited.ts
│   │   │   ├── components/
│   │   │   │   └── invite-viewers-modal.tsx
│   │   │   ├── emails/
│   │   │   │   ├── components/
│   │   │   │   │   └── dataroom-viewer-invitation.tsx
│   │   │   │   └── lib/
│   │   │   │       └── send-dataroom-viewer-invite.ts
│   │   │   └── lib/
│   │   │       ├── schema/
│   │   │       │   └── dataroom-invitations.ts
│   │   │       └── swr/
│   │   │           └── use-dataroom-invitations.ts
│   │   ├── permissions/
│   │   │   └── components/
│   │   │       ├── dataroom-link-sheet.tsx
│   │   │       └── permissions-sheet.tsx
│   │   ├── security/
│   │   │   ├── index.ts
│   │   │   ├── lib/
│   │   │   │   ├── fraud-prevention.ts
│   │   │   │   └── ratelimit.ts
│   │   │   └── sso/
│   │   │       ├── components/
│   │   │       │   ├── directory-sync-config-modal.tsx
│   │   │       │   ├── saml-config-modal.tsx
│   │   │       │   ├── sso-enforcement-toggle.tsx
│   │   │       │   └── sso-login.tsx
│   │   │       ├── constants.ts
│   │   │       ├── index.ts
│   │   │       └── product.ts
│   │   ├── storage/
│   │   │   ├── config.ts
│   │   │   └── s3-store.ts
│   │   ├── templates/
│   │   │   ├── api/
│   │   │   │   └── datarooms/
│   │   │   │       ├── apply-template.ts
│   │   │   │       └── generate.ts
│   │   │   ├── components/
│   │   │   │   └── dataroom-templates.tsx
│   │   │   ├── constants/
│   │   │   │   └── dataroom-templates.ts
│   │   │   ├── lib/
│   │   │   │   └── prompts.ts
│   │   │   └── schemas/
│   │   │       └── dataroom-templates.ts
│   │   └── workflows/
│   │       ├── components/
│   │       │   ├── index.ts
│   │       │   ├── step-form-dialog.tsx
│   │       │   ├── step-list.tsx
│   │       │   ├── workflow-access-view.tsx
│   │       │   ├── workflow-empty-state.tsx
│   │       │   ├── workflow-header.tsx
│   │       │   └── workflow-list.tsx
│   │       ├── index.ts
│   │       ├── lib/
│   │       │   ├── engine.ts
│   │       │   ├── types.ts
│   │       │   └── validation.ts
│   │       └── pages/
│   │           ├── workflow-detail.tsx
│   │           ├── workflow-new.tsx
│   │           └── workflow-overview.tsx
│   ├── limits/
│   │   ├── constants.ts
│   │   ├── handler.ts
│   │   ├── server.ts
│   │   └── swr-handler.ts
│   └── stripe/
│       ├── client.ts
│       ├── constants.ts
│       ├── functions/
│       │   ├── get-coupon-from-plan.ts
│       │   ├── get-display-name-from-plan.ts
│       │   ├── get-price-id-from-plan.ts
│       │   ├── get-quantity-from-plan.ts
│       │   └── get-subscription-item.ts
│       ├── index.ts
│       ├── utils.ts
│       └── webhooks/
│           ├── checkout-session-completed.ts
│           ├── customer-subscription-deleted.ts
│           ├── customer-subscription-updated.ts
│           └── invoice-upcoming.ts
├── lib/
│   ├── analytics/
│   │   └── index.ts
│   ├── api/
│   │   ├── auth/
│   │   │   ├── passkey.ts
│   │   │   └── token.ts
│   │   ├── documents/
│   │   │   └── process-document.ts
│   │   ├── domains/
│   │   │   ├── clear-team-redirects.ts
│   │   │   ├── redis.ts
│   │   │   └── validate-redirect-url.ts
│   │   ├── domains.ts
│   │   ├── links/
│   │   │   └── link-data.ts
│   │   ├── notification-helper.ts
│   │   ├── teams/
│   │   │   └── is-saml-enforced-for-email-domain.ts
│   │   └── views/
│   │       └── send-webhook-event.ts
│   ├── auth/
│   │   ├── dataroom-auth.ts
│   │   ├── link-session.ts
│   │   └── preview-auth.ts
│   ├── constants/
│   │   └── folder-constants.ts
│   ├── constants.ts
│   ├── cron/
│   │   ├── index.ts
│   │   └── verify-qstash.ts
│   ├── dataroom/
│   │   ├── build-folder-hierarchy.ts
│   │   └── index-generator.ts
│   ├── documents/
│   │   ├── create-document.ts
│   │   ├── get-file-helper.ts
│   │   ├── move-dataroom-documents.ts
│   │   ├── move-dataroom-folders.ts
│   │   ├── move-documents.ts
│   │   └── move-folder.ts
│   ├── domains.ts
│   ├── dub.ts
│   ├── edge-config/
│   │   ├── blacklist.ts
│   │   ├── custom-email.ts
│   │   └── trusted-teams.ts
│   ├── emails/
│   │   ├── process-dataroom-digest.ts
│   │   ├── send-custom-domain-setup.ts
│   │   ├── send-dataroom-digest-notification.ts
│   │   ├── send-dataroom-info.ts
│   │   ├── send-dataroom-notification.ts
│   │   ├── send-dataroom-trial-24h.ts
│   │   ├── send-dataroom-trial-end.ts
│   │   ├── send-dataroom-trial.ts
│   │   ├── send-dataroom-upload-notification.ts
│   │   ├── send-deleted-domain.ts
│   │   ├── send-download-ready-email.ts
│   │   ├── send-email-otp-verification.ts
│   │   ├── send-export-ready-email.ts
│   │   ├── send-hundred-views-congrats.ts
│   │   ├── send-invalid-domain.ts
│   │   ├── send-mail-verification.ts
│   │   ├── send-onboarding.ts
│   │   ├── send-slack-integration.ts
│   │   ├── send-teammate-invite.ts
│   │   ├── send-thousand-views-congrats.ts
│   │   ├── send-upgrade-month-checkin.ts
│   │   ├── send-upgrade-personal-welcome.ts
│   │   ├── send-upgrade-plan.ts
│   │   ├── send-upgrade-six-months-checkin.ts
│   │   ├── send-verification-request.ts
│   │   ├── send-viewed-dataroom-paused.ts
│   │   ├── send-viewed-dataroom.ts
│   │   ├── send-viewed-document-paused.ts
│   │   ├── send-viewed-document.ts
│   │   └── send-welcome.ts
│   ├── errorHandler.ts
│   ├── featureFlags/
│   │   └── index.ts
│   ├── files/
│   │   ├── aws-client.ts
│   │   ├── bulk-download-presign.ts
│   │   ├── bulk-download.ts
│   │   ├── copy-file-server.ts
│   │   ├── copy-file-to-bucket-server.ts
│   │   ├── delete-file-server.ts
│   │   ├── delete-team-files-server.ts
│   │   ├── get-file.ts
│   │   ├── put-file-server.ts
│   │   ├── put-file.ts
│   │   ├── stream-file-server.ts
│   │   ├── tus-redis-locker.ts
│   │   ├── tus-upload.ts
│   │   └── viewer-tus-upload.ts
│   ├── folders/
│   │   └── create-folder.ts
│   ├── hanko.ts
│   ├── hooks/
│   │   ├── use-breakpoint.ts
│   │   ├── use-copy-to-clipboard.ts
│   │   ├── use-dataroom-permissions.ts
│   │   ├── use-disable-print.ts
│   │   ├── use-feature-flags.ts
│   │   ├── use-is-admin.ts
│   │   ├── use-lazy-pages.ts
│   │   └── use-mobile.tsx
│   ├── id-helper.ts
│   ├── incoming-webhooks/
│   │   └── index.ts
│   ├── integrations/
│   │   ├── install.ts
│   │   └── slack/
│   │       ├── client.ts
│   │       ├── env.ts
│   │       ├── events.ts
│   │       ├── install.ts
│   │       ├── templates.ts
│   │       ├── types.ts
│   │       ├── uninstall.ts
│   │       └── utils.ts
│   ├── jackson.ts
│   ├── middleware/
│   │   ├── app.ts
│   │   ├── domain.ts
│   │   ├── incoming-webhooks.ts
│   │   └── posthog.ts
│   ├── notion/
│   │   ├── config.ts
│   │   ├── index.ts
│   │   └── utils.ts
│   ├── openai.ts
│   ├── posthog.ts
│   ├── prisma.ts
│   ├── redis/
│   │   └── dataroom-notification-queue.ts
│   ├── redis-download-job-store.ts
│   ├── redis-job-store.ts
│   ├── redis.ts
│   ├── resend.ts
│   ├── sheet/
│   │   └── index.ts
│   ├── swr/
│   │   ├── use-agreements.ts
│   │   ├── use-annotations.ts
│   │   ├── use-billing.ts
│   │   ├── use-brand.ts
│   │   ├── use-dataroom-document-stats.ts
│   │   ├── use-dataroom-groups.ts
│   │   ├── use-dataroom-permission-groups.ts
│   │   ├── use-dataroom-stats.ts
│   │   ├── use-dataroom-view-document-stats.ts
│   │   ├── use-dataroom.ts
│   │   ├── use-datarooms-simple.ts
│   │   ├── use-datarooms.ts
│   │   ├── use-document-overview.ts
│   │   ├── use-document-preview.ts
│   │   ├── use-document-stats.ts
│   │   ├── use-document.ts
│   │   ├── use-documents.ts
│   │   ├── use-domains.ts
│   │   ├── use-folders.ts
│   │   ├── use-invitations.ts
│   │   ├── use-invoices.ts
│   │   ├── use-limits.ts
│   │   ├── use-link.ts
│   │   ├── use-passkeys.ts
│   │   ├── use-saml.ts
│   │   ├── use-scim.ts
│   │   ├── use-slack-channels.ts
│   │   ├── use-slack-integration.ts
│   │   ├── use-stats.ts
│   │   ├── use-tags.ts
│   │   ├── use-team-ai.ts
│   │   ├── use-team-settings.ts
│   │   ├── use-team.ts
│   │   ├── use-teams.ts
│   │   ├── use-viewer.ts
│   │   ├── use-viewers.ts
│   │   └── use-visitor-groups.ts
│   ├── team/
│   │   └── helper.ts
│   ├── tinybird/
│   │   ├── README.md
│   │   ├── datasources/
│   │   │   ├── click_events.datasource
│   │   │   ├── page_views.datasource
│   │   │   ├── pm_click_events.datasource
│   │   │   ├── video_views.datasource
│   │   │   └── webhook_events.datasource
│   │   ├── endpoints/
│   │   │   ├── get_click_events_by_view.pipe
│   │   │   ├── get_dataroom_view_document_stats.pipe
│   │   │   ├── get_document_duration_per_viewer.pipe
│   │   │   ├── get_page_duration_per_view.pipe
│   │   │   ├── get_total_average_page_duration.pipe
│   │   │   ├── get_total_dataroom_duration.pipe
│   │   │   ├── get_total_document_duration.pipe
│   │   │   ├── get_total_link_duration.pipe
│   │   │   ├── get_total_team_duration.pipe
│   │   │   ├── get_total_viewer_duration.pipe
│   │   │   ├── get_useragent_per_view.pipe
│   │   │   ├── get_video_events_by_document.pipe
│   │   │   ├── get_video_events_by_view.pipe
│   │   │   ├── get_view_completion_stats.pipe
│   │   │   └── get_webhook_events.pipe
│   │   ├── index.ts
│   │   ├── pipes.ts
│   │   └── publish.ts
│   ├── tracking/
│   │   ├── record-link-view.ts
│   │   ├── safe-page-view-tracker.ts
│   │   ├── tracking-config.ts
│   │   └── video-tracking.ts
│   ├── trigger/
│   │   ├── automatic-unpause.ts
│   │   ├── bulk-download.ts
│   │   ├── cleanup-expired-exports.ts
│   │   ├── conversation-message-notification.ts
│   │   ├── convert-files.ts
│   │   ├── dataroom-change-notification.ts
│   │   ├── dataroom-upload-notification.ts
│   │   ├── export-visits.ts
│   │   ├── optimize-video-files.ts
│   │   ├── pause-reminder-notification.ts
│   │   ├── pdf-to-image-route.ts
│   │   └── send-scheduled-email.ts
│   ├── types/
│   │   ├── document-preview.ts
│   │   └── index-file.ts
│   ├── types.ts
│   ├── unsend.ts
│   ├── utils/
│   │   ├── calculate-hierarchical-indexes.ts
│   │   ├── create-adaptive-surface-palette.ts
│   │   ├── csv.ts
│   │   ├── decode-base64url.ts
│   │   ├── determine-text-color.ts
│   │   ├── email-domain.ts
│   │   ├── generate-checksum.ts
│   │   ├── generate-jwt.ts
│   │   ├── generate-otp.ts
│   │   ├── generate-trigger-auth-token.ts
│   │   ├── generate-trigger-status.ts
│   │   ├── geo.ts
│   │   ├── get-content-type.ts
│   │   ├── get-file-icon.tsx
│   │   ├── get-file-size-limits.ts
│   │   ├── get-page-number-count.ts
│   │   ├── get-search-params.ts
│   │   ├── global-block-list.ts
│   │   ├── hierarchical-display.ts
│   │   ├── ip.ts
│   │   ├── link-url.ts
│   │   ├── reliable-tracking.ts
│   │   ├── resize-image.ts
│   │   ├── sanitize-html.ts
│   │   ├── sort-items-by-index-name.ts
│   │   ├── trigger-utils.ts
│   │   ├── unsubscribe.ts
│   │   ├── use-at-bottom.ts
│   │   ├── use-copy-to-clipboard.ts
│   │   ├── use-enter-submit.ts
│   │   ├── use-media-query.ts
│   │   ├── use-progress-status.ts
│   │   ├── user-agent.ts
│   │   └── validate-email.ts
│   ├── utils.ts
│   ├── webhook/
│   │   ├── constants.ts
│   │   ├── send-webhooks.ts
│   │   ├── signature.ts
│   │   ├── transform.ts
│   │   ├── triggers/
│   │   │   ├── document-created.ts
│   │   │   └── link-created.ts
│   │   └── types.ts
│   ├── webstorage.ts
│   ├── year-in-review/
│   │   ├── calculate-percentile.ts
│   │   ├── get-stats.ts
│   │   ├── index.ts
│   │   └── send-emails.ts
│   └── zod/
│       ├── schemas/
│       │   ├── folders.ts
│       │   ├── multipart.ts
│       │   ├── notifications.ts
│       │   ├── presets.ts
│       │   └── webhooks.ts
│       └── url-validation.ts
├── middleware.ts
├── next.config.mjs
├── package.json
├── pages/
│   ├── 404.tsx
│   ├── _app.tsx
│   ├── _document.tsx
│   ├── account/
│   │   ├── general.tsx
│   │   └── security.tsx
│   ├── api/
│   │   ├── account/
│   │   │   ├── index.ts
│   │   │   └── passkeys.ts
│   │   ├── analytics/
│   │   │   └── index.ts
│   │   ├── auth/
│   │   │   └── [...nextauth].ts
│   │   ├── conversations/
│   │   │   └── [[...conversations]].ts
│   │   ├── feedback/
│   │   │   └── index.ts
│   │   ├── file/
│   │   │   ├── browser-upload.ts
│   │   │   ├── image-upload.ts
│   │   │   ├── notion/
│   │   │   │   └── index.ts
│   │   │   ├── s3/
│   │   │   │   ├── get-presigned-get-url-proxy.ts
│   │   │   │   ├── get-presigned-get-url.ts
│   │   │   │   ├── get-presigned-post-url.ts
│   │   │   │   └── multipart.ts
│   │   │   ├── tus/
│   │   │   │   └── [[...file]].ts
│   │   │   └── tus-viewer/
│   │   │       └── [[...file]].ts
│   │   ├── health.ts
│   │   ├── internal/
│   │   │   └── billing/
│   │   │       └── automatic-unpause.ts
│   │   ├── jobs/
│   │   │   ├── get-thumbnail.ts
│   │   │   ├── process-download-batch.ts
│   │   │   ├── send-conversation-new-message-notification.ts
│   │   │   ├── send-conversation-team-member-notification.ts
│   │   │   ├── send-dataroom-new-document-notification.ts
│   │   │   ├── send-dataroom-upload-notification.ts
│   │   │   ├── send-notification.ts
│   │   │   └── send-pause-resume-notification.ts
│   │   ├── links/
│   │   │   ├── [id]/
│   │   │   │   ├── annotations.ts
│   │   │   │   ├── archive.ts
│   │   │   │   ├── documents/
│   │   │   │   │   ├── [documentId]/
│   │   │   │   │   │   └── annotations.ts
│   │   │   │   │   └── [documentId].ts
│   │   │   │   ├── duplicate.ts
│   │   │   │   ├── index.ts
│   │   │   │   └── preview.ts
│   │   │   ├── domains/
│   │   │   │   └── [...domainSlug].ts
│   │   │   ├── download/
│   │   │   │   ├── [jobId].ts
│   │   │   │   ├── bulk.ts
│   │   │   │   ├── by-email.ts
│   │   │   │   ├── dataroom-document.ts
│   │   │   │   ├── dataroom-folder.ts
│   │   │   │   ├── file/
│   │   │   │   │   └── [jobId]/
│   │   │   │   │       └── [partIndex].ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── jobs.ts
│   │   │   │   └── verify.ts
│   │   │   ├── generate-index.ts
│   │   │   └── index.ts
│   │   ├── mupdf/
│   │   │   ├── annotate-document.ts
│   │   │   ├── convert-page.ts
│   │   │   └── get-pages.ts
│   │   ├── notification-preferences/
│   │   │   └── dataroom.ts
│   │   ├── passkeys/
│   │   │   └── register.ts
│   │   ├── progress-token.ts
│   │   ├── record_click.ts
│   │   ├── record_reaction.ts
│   │   ├── record_video_view.ts
│   │   ├── record_view.ts
│   │   ├── report.ts
│   │   ├── revalidate.ts
│   │   ├── stripe/
│   │   │   ├── webhook-old.ts
│   │   │   └── webhook.ts
│   │   ├── teams/
│   │   │   ├── [teamId]/
│   │   │   │   ├── agreements/
│   │   │   │   │   ├── [agreementId]/
│   │   │   │   │   │   ├── download.ts
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   └── index.ts
│   │   │   │   ├── ai-settings.ts
│   │   │   │   ├── billing/
│   │   │   │   │   ├── cancel.ts
│   │   │   │   │   ├── cancellation-feedback.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── invoices.ts
│   │   │   │   │   ├── manage.ts
│   │   │   │   │   ├── pause.ts
│   │   │   │   │   ├── plan.ts
│   │   │   │   │   ├── reactivate.ts
│   │   │   │   │   ├── retention-offer.ts
│   │   │   │   │   ├── unpause.ts
│   │   │   │   │   └── upgrade.ts
│   │   │   │   ├── branding.ts
│   │   │   │   ├── change-role.ts
│   │   │   │   ├── datarooms/
│   │   │   │   │   ├── [id]/
│   │   │   │   │   │   ├── apply-permissions.ts
│   │   │   │   │   │   ├── apply-template.ts
│   │   │   │   │   │   ├── branding.ts
│   │   │   │   │   │   ├── calculate-indexes.ts
│   │   │   │   │   │   ├── conversations/
│   │   │   │   │   │   │   ├── [[...conversations]].ts
│   │   │   │   │   │   │   └── toggle-conversations.ts
│   │   │   │   │   │   ├── documents/
│   │   │   │   │   │   │   ├── [documentId]/
│   │   │   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   │   │   └── stats.ts
│   │   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   │   └── move.ts
│   │   │   │   │   │   ├── download/
│   │   │   │   │   │   │   ├── [jobId].ts
│   │   │   │   │   │   │   ├── bulk.ts
│   │   │   │   │   │   │   └── jobs.ts
│   │   │   │   │   │   ├── duplicate.ts
│   │   │   │   │   │   ├── export-visits.ts
│   │   │   │   │   │   ├── faqs/
│   │   │   │   │   │   │   ├── [faqId].ts
│   │   │   │   │   │   │   └── index.ts
│   │   │   │   │   │   ├── folders/
│   │   │   │   │   │   │   ├── [...name].ts
│   │   │   │   │   │   │   ├── documents/
│   │   │   │   │   │   │   │   └── [...name].ts
│   │   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   │   ├── manage/
│   │   │   │   │   │   │   │   ├── [folderId]/
│   │   │   │   │   │   │   │   │   ├── dataroom-to-dataroom.ts
│   │   │   │   │   │   │   │   │   └── index.ts
│   │   │   │   │   │   │   │   └── index.ts
│   │   │   │   │   │   │   ├── move.ts
│   │   │   │   │   │   │   └── parents/
│   │   │   │   │   │   │       └── [...name].ts
│   │   │   │   │   │   ├── generate-index.ts
│   │   │   │   │   │   ├── groups/
│   │   │   │   │   │   │   ├── [groupId]/
│   │   │   │   │   │   │   │   ├── export-visits.ts
│   │   │   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   │   │   ├── invite.ts
│   │   │   │   │   │   │   │   ├── links.ts
│   │   │   │   │   │   │   │   ├── members/
│   │   │   │   │   │   │   │   │   ├── [memberId].ts
│   │   │   │   │   │   │   │   │   └── index.ts
│   │   │   │   │   │   │   │   ├── permissions.ts
│   │   │   │   │   │   │   │   ├── uninvited.ts
│   │   │   │   │   │   │   │   └── views/
│   │   │   │   │   │   │   │       └── index.ts
│   │   │   │   │   │   │   └── index.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── links/
│   │   │   │   │   │   │   └── [linkId]/
│   │   │   │   │   │   │       └── invite.ts
│   │   │   │   │   │   ├── links.ts
│   │   │   │   │   │   ├── permission-groups/
│   │   │   │   │   │   │   ├── [permissionGroupId].ts
│   │   │   │   │   │   │   └── index.ts
│   │   │   │   │   │   ├── reorder.ts
│   │   │   │   │   │   ├── stats.ts
│   │   │   │   │   │   ├── users/
│   │   │   │   │   │   │   └── index.ts
│   │   │   │   │   │   ├── viewers/
│   │   │   │   │   │   │   └── index.ts
│   │   │   │   │   │   ├── views/
│   │   │   │   │   │   │   ├── [viewId]/
│   │   │   │   │   │   │   │   ├── custom-fields.ts
│   │   │   │   │   │   │   │   ├── document-stats.ts
│   │   │   │   │   │   │   │   ├── history.ts
│   │   │   │   │   │   │   │   └── user-agent.ts
│   │   │   │   │   │   │   └── index.ts
│   │   │   │   │   │   └── views-count.ts
│   │   │   │   │   ├── create-from-folder.ts
│   │   │   │   │   ├── generate-ai-structure.ts
│   │   │   │   │   ├── generate-ai.ts
│   │   │   │   │   ├── generate.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── trial.ts
│   │   │   │   ├── documents/
│   │   │   │   │   ├── [id]/
│   │   │   │   │   │   ├── add-to-dataroom.ts
│   │   │   │   │   │   ├── advanced-mode.ts
│   │   │   │   │   │   ├── annotations/
│   │   │   │   │   │   │   ├── [annotationId]/
│   │   │   │   │   │   │   │   └── images.ts
│   │   │   │   │   │   │   ├── [annotationId].ts
│   │   │   │   │   │   │   └── index.ts
│   │   │   │   │   │   ├── change-orientation.ts
│   │   │   │   │   │   ├── check-notion-accessibility.ts
│   │   │   │   │   │   ├── duplicate.ts
│   │   │   │   │   │   ├── export-visits.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── links.ts
│   │   │   │   │   │   ├── overview.ts
│   │   │   │   │   │   ├── preview-data.ts
│   │   │   │   │   │   ├── preview-pages.ts
│   │   │   │   │   │   ├── stats.ts
│   │   │   │   │   │   ├── toggle-dark-mode.ts
│   │   │   │   │   │   ├── toggle-download-only.ts
│   │   │   │   │   │   ├── update-link-url.ts
│   │   │   │   │   │   ├── update-name.ts
│   │   │   │   │   │   ├── update-notion-url.ts
│   │   │   │   │   │   ├── versions/
│   │   │   │   │   │   │   └── index.ts
│   │   │   │   │   │   ├── video-analytics.ts
│   │   │   │   │   │   ├── views/
│   │   │   │   │   │   │   ├── [viewId]/
│   │   │   │   │   │   │   │   ├── click-events.ts
│   │   │   │   │   │   │   │   ├── custom-fields.ts
│   │   │   │   │   │   │   │   ├── stats.ts
│   │   │   │   │   │   │   │   ├── user-agent.ts
│   │   │   │   │   │   │   │   └── video-stats.ts
│   │   │   │   │   │   │   └── index.ts
│   │   │   │   │   │   └── views-count.ts
│   │   │   │   │   ├── agreement.ts
│   │   │   │   │   ├── document-processing-status.ts
│   │   │   │   │   ├── hidden/
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── hide.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── move.ts
│   │   │   │   │   ├── search.ts
│   │   │   │   │   └── update.ts
│   │   │   │   ├── domains/
│   │   │   │   │   ├── [domain]/
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── validate.ts
│   │   │   │   │   │   └── verify.ts
│   │   │   │   │   └── index.ts
│   │   │   │   ├── enable-advanced-mode.ts
│   │   │   │   ├── export-jobs/
│   │   │   │   │   ├── [exportId]/
│   │   │   │   │   │   └── send-email.ts
│   │   │   │   │   └── [exportId].ts
│   │   │   │   ├── export-jobs.ts
│   │   │   │   ├── folders/
│   │   │   │   │   ├── [...name].ts
│   │   │   │   │   ├── documents/
│   │   │   │   │   │   └── [...name].ts
│   │   │   │   │   ├── hide.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── manage/
│   │   │   │   │   │   ├── [folderId]/
│   │   │   │   │   │   │   ├── add-to-dataroom.ts
│   │   │   │   │   │   │   └── index.ts
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── move.ts
│   │   │   │   │   └── parents/
│   │   │   │   │       └── [...name].ts
│   │   │   │   ├── global-block-list.ts
│   │   │   │   ├── ignored-domains.ts
│   │   │   │   ├── incoming-webhooks/
│   │   │   │   │   └── index.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── integrations/
│   │   │   │   │   └── slack/
│   │   │   │   │       ├── channels.ts
│   │   │   │   │       └── index.ts
│   │   │   │   ├── invitations/
│   │   │   │   │   ├── accept.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── resend.ts
│   │   │   │   ├── invite.ts
│   │   │   │   ├── limits.ts
│   │   │   │   ├── links/
│   │   │   │   │   └── [id]/
│   │   │   │   │       ├── index.ts
│   │   │   │   │       └── visits.ts
│   │   │   │   ├── presets/
│   │   │   │   │   ├── [id].ts
│   │   │   │   │   └── index.ts
│   │   │   │   ├── remove-teammate.ts
│   │   │   │   ├── settings.ts
│   │   │   │   ├── survey.ts
│   │   │   │   ├── tags/
│   │   │   │   │   ├── [id]/
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   └── index.ts
│   │   │   │   ├── tokens/
│   │   │   │   │   └── index.ts
│   │   │   │   ├── update-advanced-mode.ts
│   │   │   │   ├── update-name.ts
│   │   │   │   ├── update-replicate-folders.ts
│   │   │   │   ├── viewers/
│   │   │   │   │   ├── [id]/
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   └── index.ts
│   │   │   │   ├── views/
│   │   │   │   │   └── [id]/
│   │   │   │   │       └── archive.ts
│   │   │   │   ├── visitor-groups/
│   │   │   │   │   ├── [groupId]/
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   └── index.ts
│   │   │   │   ├── webhooks/
│   │   │   │   │   ├── [id]/
│   │   │   │   │   │   ├── events.ts
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   └── index.ts
│   │   │   │   ├── workflow-links.ts
│   │   │   │   └── yearly-recap.ts
│   │   │   └── index.ts
│   │   ├── unsubscribe/
│   │   │   ├── dataroom/
│   │   │   │   └── index.ts
│   │   │   └── yir/
│   │   │       └── index.ts
│   │   ├── user/
│   │   │   └── subscribe.ts
│   │   └── webhooks/
│   │       └── services/
│   │           └── [...path]/
│   │               └── index.ts
│   ├── branding.tsx
│   ├── dashboard.tsx
│   ├── datarooms/
│   │   ├── [id]/
│   │   │   ├── analytics/
│   │   │   │   └── index.tsx
│   │   │   ├── branding/
│   │   │   │   └── index.tsx
│   │   │   ├── conversations/
│   │   │   │   ├── [conversationId]/
│   │   │   │   │   └── index.tsx
│   │   │   │   ├── faqs/
│   │   │   │   │   └── index.tsx
│   │   │   │   └── index.tsx
│   │   │   ├── documents/
│   │   │   │   ├── [...name].tsx
│   │   │   │   └── index.tsx
│   │   │   ├── groups/
│   │   │   │   ├── [groupId]/
│   │   │   │   │   ├── group-analytics.tsx
│   │   │   │   │   ├── index.tsx
│   │   │   │   │   ├── links.tsx
│   │   │   │   │   ├── members.tsx
│   │   │   │   │   └── permissions.tsx
│   │   │   │   └── index.tsx
│   │   │   ├── index.tsx
│   │   │   ├── permissions/
│   │   │   │   └── index.tsx
│   │   │   ├── settings/
│   │   │   │   ├── downloads.tsx
│   │   │   │   ├── file-permissions.tsx
│   │   │   │   ├── index.tsx
│   │   │   │   ├── introduction.tsx
│   │   │   │   └── notifications.tsx
│   │   │   └── users/
│   │   │       └── index.tsx
│   │   └── index.tsx
│   ├── documents/
│   │   ├── [id]/
│   │   │   └── index.tsx
│   │   ├── hidden.tsx
│   │   ├── index.tsx
│   │   ├── new.tsx
│   │   └── tree/
│   │       └── [...name].tsx
│   ├── entrance_ppreview_demo.tsx
│   ├── nav_ppreview_demo.tsx
│   ├── notification-preferences.tsx
│   ├── room_ppreview_demo.tsx
│   ├── settings/
│   │   ├── agreements.tsx
│   │   ├── ai.tsx
│   │   ├── billing/
│   │   │   └── invoices.tsx
│   │   ├── billing.tsx
│   │   ├── domains.tsx
│   │   ├── general.tsx
│   │   ├── incoming-webhooks.tsx
│   │   ├── people.tsx
│   │   ├── presets/
│   │   │   ├── [id].tsx
│   │   │   ├── index.tsx
│   │   │   └── new.tsx
│   │   ├── security.tsx
│   │   ├── slack.tsx
│   │   ├── tags.tsx
│   │   ├── tokens.tsx
│   │   ├── upgrade-holiday-offer.tsx
│   │   ├── upgrade.tsx
│   │   └── webhooks/
│   │       ├── [id]/
│   │       │   └── index.tsx
│   │       ├── index.tsx
│   │       └── new.tsx
│   ├── unsubscribe.tsx
│   ├── view/
│   │   ├── [linkId]/
│   │   │   ├── d/
│   │   │   │   └── [documentId].tsx
│   │   │   ├── downloads.tsx
│   │   │   ├── embed.tsx
│   │   │   └── index.tsx
│   │   └── domains/
│   │       └── [domain]/
│   │           └── [slug]/
│   │               ├── d/
│   │               │   └── [documentId].tsx
│   │               ├── downloads.tsx
│   │               └── index.tsx
│   ├── visitors/
│   │   ├── [id]/
│   │   │   └── index.tsx
│   │   └── index.tsx
│   ├── welcome.tsx
│   └── workflows/
│       ├── [id].tsx
│       ├── index.tsx
│       └── new.tsx
├── pkgx.yaml
├── postcss.config.js
├── prettier.config.js
├── prisma/
│   ├── README.md
│   ├── add-migration.sh
│   ├── migrations/
│   │   ├── 20230912150657_initialize/
│   │   │   └── migration.sql
│   │   ├── 202310122339_NewColumnInLinkTable/
│   │   │   └── migration.sql
│   │   ├── 20231013165123_create_document_version/
│   │   │   └── migration.sql
│   │   ├── 20231014200337_create_document_pages/
│   │   │   └── migration.sql
│   │   ├── 202310311254_NewColumnEnableNotificationLinkTable/
│   │   │   └── migration.sql
│   │   ├── 20231105152632_create_team/
│   │   │   └── migration.sql
│   │   ├── 20231113051339_create_sent_email/
│   │   │   └── migration.sql
│   │   ├── 20231114054509_add_domain_to_sent_emails/
│   │   │   └── migration.sql
│   │   ├── 20231116093816_update_invitations/
│   │   │   └── migration.sql
│   │   ├── 20231127062841_add_conversation/
│   │   │   └── migration.sql
│   │   ├── 20231128064540_add_indices/
│   │   │   └── migration.sql
│   │   ├── 20231204070250_remove_trial/
│   │   │   └── migration.sql
│   │   ├── 20231207081407_add_reactions/
│   │   │   └── migration.sql
│   │   ├── 20240110233134_add_disable_feedback/
│   │   │   └── migration.sql
│   │   ├── 20240117020456_add_branding/
│   │   │   └── migration.sql
│   │   ├── 20240202052149_add_email_authentication_to_link_and_view/
│   │   │   └── migration.sql
│   │   ├── 20240205170242_embedded_links/
│   │   │   └── migration.sql
│   │   ├── 20240212081614_add_downloaded_time_to_view/
│   │   │   └── migration.sql
│   │   ├── 20240215035046_add_allow_deny_list_to_links/
│   │   │   └── migration.sql
│   │   ├── 20240221042933_add_document_storage_type_enum/
│   │   │   └── migration.sql
│   │   ├── 20240313100203_add_folders/
│   │   │   └── migration.sql
│   │   ├── 20240327102407_add_dataroom/
│   │   │   └── migration.sql
│   │   ├── 20240330062000_add_viewtype/
│   │   │   └── migration.sql
│   │   ├── 20240401000000_add_dataroom_brand/
│   │   │   └── migration.sql
│   │   ├── 20240408000000_add_viewer/
│   │   │   └── migration.sql
│   │   ├── 20240415000000_add_feedback/
│   │   │   └── migration.sql
│   │   ├── 20240424152839_add_screenprotection_to_link/
│   │   │   └── migration.sql
│   │   ├── 20240511000000_add_team_limits/
│   │   │   └── migration.sql
│   │   ├── 20240520000000_add_vertical_to_document_version/
│   │   │   └── migration.sql
│   │   ├── 20240521000000_add_manager_role/
│   │   │   └── migration.sql
│   │   ├── 20240611000000_add_agreements/
│   │   │   └── migration.sql
│   │   ├── 20240712000000_add_page_metadata/
│   │   │   └── migration.sql
│   │   ├── 20240720000000_change_owner_dependecy/
│   │   │   └── migration.sql
│   │   ├── 20240730000000_update_link_defaults/
│   │   │   └── migration.sql
│   │   ├── 20240731000000_add_link_show_banner/
│   │   │   └── migration.sql
│   │   ├── 20240809000000_add_dataroom_order_index/
│   │   │   └── migration.sql
│   │   ├── 20240821000000_add_require_name/
│   │   │   └── migration.sql
│   │   ├── 20240830000000_add_watermarks/
│   │   │   └── migration.sql
│   │   ├── 20240901000000_add_domain_default/
│   │   │   └── migration.sql
│   │   ├── 20240902000000_add_link_presets/
│   │   │   └── migration.sql
│   │   ├── 20240911000000_add_dataroom_groups_permissions/
│   │   │   └── migration.sql
│   │   ├── 20240915000000_add_advanced_mode/
│   │   │   └── migration.sql
│   │   ├── 20240916000000_add_content_type_to_document/
│   │   │   └── migration.sql
│   │   ├── 20240921000000_add_viewer_migration/
│   │   │   └── migration.sql
│   │   ├── 20241004024010_add_favicon_column/
│   │   │   └── migration.sql
│   │   ├── 20241020000000_add_teamid_to_link_and_view/
│   │   │   └── migration.sql
│   │   ├── 20241029000000_add_archived_view/
│   │   │   └── migration.sql
│   │   ├── 20241107000000_add_tokens_and_webhooks/
│   │   │   └── migration.sql
│   │   ├── 20241118000000_add_filesize_and_downloadonly/
│   │   │   └── migration.sql
│   │   ├── 20241123000000_add_viewer_notification_preferences/
│   │   │   └── migration.sql
│   │   ├── 20241126000000_add_screen_shield/
│   │   │   └── migration.sql
│   │   ├── 20241208000000_add_webhooks/
│   │   │   └── migration.sql
│   │   ├── 20241212000000_add_yir/
│   │   │   └── migration.sql
│   │   ├── 20241231_add_dataroom_allow_bulk_download/
│   │   │   └── migration.sql
│   │   ├── 20250106000000_add_download_type/
│   │   │   └── migration.sql
│   │   ├── 20250110000000_add_length_to_document_version/
│   │   │   └── migration.sql
│   │   ├── 20250113000000_add_custom_fields/
│   │   │   └── migration.sql
│   │   ├── 20250204000000_add_anonymous_group/
│   │   │   └── migration.sql
│   │   ├── 20250217000000_add_contactid_to_user/
│   │   │   └── migration.sql
│   │   ├── 20250217000000_remove_link_column/
│   │   │   └── migration.sql
│   │   ├── 20250310000000_rename_conversation_table/
│   │   │   └── migration.sql
│   │   ├── 20250404000000_add_questions_answer_conversation/
│   │   │   └── migration.sql
│   │   ├── 20250413000000_add_dataroom_upload/
│   │   │   └── migration.sql
│   │   ├── 20250425000000_update_link_presets/
│   │   │   └── migration.sql
│   │   ├── 20250428000000_add_tags/
│   │   │   └── migration.sql
│   │   ├── 20250502000000_add_additional_present_fields/
│   │   │   └── migration.sql
│   │   ├── 20250511000000_add_index_file_to_links/
│   │   │   └── migration.sql
│   │   ├── 20250513000000_add_notification_to_dataroom/
│   │   │   └── migration.sql
│   │   ├── 20250516000000_add_advanced_mode_to_team/
│   │   │   └── migration.sql
│   │   ├── 20250526000000_add_status_to_users/
│   │   │   └── migration.sql
│   │   ├── 20250607000000_add_notification_to_presets/
│   │   │   └── migration.sql
│   │   ├── 20250612000000_add_permissions_to_link/
│   │   │   └── migration.sql
│   │   ├── 20250624042156_viewers_performance_optimization/
│   │   │   └── migration.sql
│   │   ├── 20250711000000_add_ignored_domains_to_team/
│   │   │   └── migration.sql
│   │   ├── 20250715042921_add_global_block_list_to_team/
│   │   │   └── migration.sql
│   │   ├── 20250716000000_add_dataroom_permission_defaults/
│   │   │   └── migration.sql
│   │   ├── 20250725000000_add_welcome_message_to_dataroom/
│   │   │   └── migration.sql
│   │   ├── 20250730000000_change_file_size_bigint/
│   │   │   └── migration.sql
│   │   ├── 20250806000000_add_billing_details_to_team/
│   │   │   └── migration.sql
│   │   ├── 20250830000000_dataroom_faq_items_model/
│   │   │   └── migration.sql
│   │   ├── 20250903000000_add_agreement_contenttype/
│   │   │   └── migration.sql
│   │   ├── 20250905000000_add_hierarchical_index_to_dataroom/
│   │   │   └── migration.sql
│   │   ├── 20250913000000_add_integrations/
│   │   │   └── migration.sql
│   │   ├── 20250915000000_add_annotations/
│   │   │   └── migration.sql
│   │   ├── 20250915000000_add_performance_indexes/
│   │   │   └── migration.sql
│   │   ├── 20250917000000_add_performance_index_dataroom/
│   │   │   └── migration.sql
│   │   ├── 20251007000000_add_welcome_message_to_brand/
│   │   │   └── migration.sql
│   │   ├── 20251009000000_update-replicate-folders/
│   │   │   └── migration.sql
│   │   ├── 20251017000000_add_show_banner_to_presets/
│   │   │   └── migration.sql
│   │   ├── 20251021000000_add_deleted_at_to_links/
│   │   │   └── migration.sql
│   │   ├── 20251027000000_add_banner_to_brand/
│   │   │   └── migration.sql
│   │   ├── 20251031000000_add_message_to_link/
│   │   │   └── migration.sql
│   │   ├── 20251111000000_add_viewer_invitations/
│   │   │   └── migration.sql
│   │   ├── 20251116000000_add_workflow/
│   │   │   └── migration.sql
│   │   ├── 20251208000000_add_chat_model/
│   │   │   └── migration.sql
│   │   ├── 20251209000000_update_view_model/
│   │   │   └── migration.sql
│   │   ├── 20251230000000_add_timezone_to_team/
│   │   │   └── migration.sql
│   │   ├── 20260113000000_add_hidden_in_all_documents/
│   │   │   └── migration.sql
│   │   ├── 20260113000000_add_internal_name_to_dataroom/
│   │   │   └── migration.sql
│   │   ├── 20260120000000_add_folder_icon_color/
│   │   │   └── migration.sql
│   │   ├── 20260204000000_add_introduction_page/
│   │   │   └── migration.sql
│   │   ├── 20260210000000_add_saml_scim_to_team/
│   │   │   └── migration.sql
│   │   ├── 20260212000000_add_owner_to_link/
│   │   │   └── migration.sql
│   │   ├── 20260212000000_add_visitor_groups/
│   │   │   └── migration.sql
│   │   ├── 20260219171000_add_dataroom_bg_toggle_to_brand/
│   │   │   └── migration.sql
│   │   ├── 20260220000000_add_redirect_url_to_domain/
│   │   │   └── migration.sql
│   │   ├── 20260227000000_add_survey_data_to_team/
│   │   │   └── migration.sql
│   │   └── migration_lock.toml
│   └── schema/
│       ├── annotation.prisma
│       ├── conversation.prisma
│       ├── dataroom.prisma
│       ├── document.prisma
│       ├── integration.prisma
│       ├── jackson.prisma
│       ├── link.prisma
│       ├── schema.prisma
│       ├── team.prisma
│       └── workflow.prisma
├── styles/
│   ├── custom-notion-styles.css
│   ├── custom-viewer-styles.css
│   └── globals.css
├── tailwind.config.js
├── trigger.config.ts
├── tsconfig.json
└── vercel.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .agents/skills/frontend-design/LICENSE.txt
================================================

                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS


================================================
FILE: .agents/skills/frontend-design/SKILL.md
================================================
---
name: frontend-design
description: Create distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, artifacts, posters, or applications (examples include websites, landing pages, dashboards, React components, HTML/CSS layouts, or when styling/beautifying any web UI). Generates creative, polished code and UI design that avoids generic AI aesthetics.
license: Complete terms in LICENSE.txt
---

This skill guides creation of distinctive, production-grade frontend interfaces that avoid generic "AI slop" aesthetics. Implement real working code with exceptional attention to aesthetic details and creative choices.

The user provides frontend requirements: a component, page, application, or interface to build. They may include context about the purpose, audience, or technical constraints.

## Design Thinking

Before coding, understand the context and commit to a BOLD aesthetic direction:
- **Purpose**: What problem does this interface solve? Who uses it?
- **Tone**: Pick an extreme: brutally minimal, maximalist chaos, retro-futuristic, organic/natural, luxury/refined, playful/toy-like, editorial/magazine, brutalist/raw, art deco/geometric, soft/pastel, industrial/utilitarian, etc. There are so many flavors to choose from. Use these for inspiration but design one that is true to the aesthetic direction.
- **Constraints**: Technical requirements (framework, performance, accessibility).
- **Differentiation**: What makes this UNFORGETTABLE? What's the one thing someone will remember?

**CRITICAL**: Choose a clear conceptual direction and execute it with precision. Bold maximalism and refined minimalism both work - the key is intentionality, not intensity.

Then implement working code (HTML/CSS/JS, React, Vue, etc.) that is:
- Production-grade and functional
- Visually striking and memorable
- Cohesive with a clear aesthetic point-of-view
- Meticulously refined in every detail

## Frontend Aesthetics Guidelines

Focus on:
- **Typography**: Choose fonts that are beautiful, unique, and interesting. Avoid generic fonts like Arial and Inter; opt instead for distinctive choices that elevate the frontend's aesthetics; unexpected, characterful font choices. Pair a distinctive display font with a refined body font.
- **Color & Theme**: Commit to a cohesive aesthetic. Use CSS variables for consistency. Dominant colors with sharp accents outperform timid, evenly-distributed palettes.
- **Motion**: Use animations for effects and micro-interactions. Prioritize CSS-only solutions for HTML. Use Motion library for React when available. Focus on high-impact moments: one well-orchestrated page load with staggered reveals (animation-delay) creates more delight than scattered micro-interactions. Use scroll-triggering and hover states that surprise.
- **Spatial Composition**: Unexpected layouts. Asymmetry. Overlap. Diagonal flow. Grid-breaking elements. Generous negative space OR controlled density.
- **Backgrounds & Visual Details**: Create atmosphere and depth rather than defaulting to solid colors. Add contextual effects and textures that match the overall aesthetic. Apply creative forms like gradient meshes, noise textures, geometric patterns, layered transparencies, dramatic shadows, decorative borders, custom cursors, and grain overlays.

NEVER use generic AI-generated aesthetics like overused font families (Inter, Roboto, Arial, system fonts), cliched color schemes (particularly purple gradients on white backgrounds), predictable layouts and component patterns, and cookie-cutter design that lacks context-specific character.

Interpret creatively and make unexpected choices that feel genuinely designed for the context. No design should be the same. Vary between light and dark themes, different fonts, different aesthetics. NEVER converge on common choices (Space Grotesk, for example) across generations.

**IMPORTANT**: Match implementation complexity to the aesthetic vision. Maximalist designs need elaborate code with extensive animations and effects. Minimalist or refined designs need restraint, precision, and careful attention to spacing, typography, and subtle details. Elegance comes from executing the vision well.

Remember: Claude is capable of extraordinary creative work. Don't hold back, show what can truly be created when thinking outside the box and committing fully to a distinctive vision.


================================================
FILE: .agents/skills/gh-cli/SKILL.md
================================================
---
name: gh-cli
description: GitHub CLI (gh) comprehensive reference for repositories, issues, pull requests, Actions, projects, releases, gists, codespaces, organizations, extensions, and all GitHub operations from the command line.
---

# GitHub CLI (gh)

Comprehensive reference for GitHub CLI (gh) - work seamlessly with GitHub from the command line.

**Version:** 2.85.0 (current as of January 2026)

## Prerequisites

### Installation

```bash
# macOS
brew install gh

# Linux
curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null
sudo apt update
sudo apt install gh

# Windows
winget install --id GitHub.cli

# Verify installation
gh --version
```

### Authentication

```bash
# Interactive login (default: github.com)
gh auth login

# Login with specific hostname
gh auth login --hostname enterprise.internal

# Login with token
gh auth login --with-token < mytoken.txt

# Check authentication status
gh auth status

# Switch accounts
gh auth switch --hostname github.com --user username

# Logout
gh auth logout --hostname github.com --user username
```

### Setup Git Integration

```bash
# Configure git to use gh as credential helper
gh auth setup-git

# View active token
gh auth token

# Refresh authentication scopes
gh auth refresh --scopes write:org,read:public_key
```

## CLI Structure

```
gh                          # Root command
├── auth                    # Authentication
│   ├── login
│   ├── logout
│   ├── refresh
│   ├── setup-git
│   ├── status
│   ├── switch
│   └── token
├── browse                  # Open in browser
├── codespace               # GitHub Codespaces
│   ├── code
│   ├── cp
│   ├── create
│   ├── delete
│   ├── edit
│   ├── jupyter
│   ├── list
│   ├── logs
│   ├── ports
│   ├── rebuild
│   ├── ssh
│   ├── stop
│   └── view
├── gist                    # Gists
│   ├── clone
│   ├── create
│   ├── delete
│   ├── edit
│   ├── list
│   ├── rename
│   └── view
├── issue                   # Issues
│   ├── create
│   ├── list
│   ├── status
│   ├── close
│   ├── comment
│   ├── delete
│   ├── develop
│   ├── edit
│   ├── lock
│   ├── pin
│   ├── reopen
│   ├── transfer
│   ├── unlock
│   └── view
├── org                     # Organizations
│   └── list
├── pr                      # Pull Requests
│   ├── create
│   ├── list
│   ├── status
│   ├── checkout
│   ├── checks
│   ├── close
│   ├── comment
│   ├── diff
│   ├── edit
│   ├── lock
│   ├── merge
│   ├── ready
│   ├── reopen
│   ├── revert
│   ├── review
│   ├── unlock
│   ├── update-branch
│   └── view
├── project                 # Projects
│   ├── close
│   ├── copy
│   ├── create
│   ├── delete
│   ├── edit
│   ├── field-create
│   ├── field-delete
│   ├── field-list
│   ├── item-add
│   ├── item-archive
│   ├── item-create
│   ├── item-delete
│   ├── item-edit
│   ├── item-list
│   ├── link
│   ├── list
│   ├── mark-template
│   ├── unlink
│   └── view
├── release                 # Releases
│   ├── create
│   ├── list
│   ├── delete
│   ├── delete-asset
│   ├── download
│   ├── edit
│   ├── upload
│   ├── verify
│   ├── verify-asset
│   └── view
├── repo                    # Repositories
│   ├── create
│   ├── list
│   ├── archive
│   ├── autolink
│   ├── clone
│   ├── delete
│   ├── deploy-key
│   ├── edit
│   ├── fork
│   ├── gitignore
│   ├── license
│   ├── rename
│   ├── set-default
│   ├── sync
│   ├── unarchive
│   └── view
├── cache                   # Actions caches
│   ├── delete
│   └── list
├── run                     # Workflow runs
│   ├── cancel
│   ├── delete
│   ├── download
│   ├── list
│   ├── rerun
│   ├── view
│   └── watch
├── workflow                # Workflows
│   ├── disable
│   ├── enable
│   ├── list
│   ├── run
│   └── view
├── agent-task              # Agent tasks
├── alias                   # Command aliases
│   ├── delete
│   ├── import
│   ├── list
│   └── set
├── api                     # API requests
├── attestation             # Artifact attestations
│   ├── download
│   ├── trusted-root
│   └── verify
├── completion              # Shell completion
├── config                  # Configuration
│   ├── clear-cache
│   ├── get
│   ├── list
│   └── set
├── extension               # Extensions
│   ├── browse
│   ├── create
│   ├── exec
│   ├── install
│   ├── list
│   ├── remove
│   ├── search
│   └── upgrade
├── gpg-key                 # GPG keys
│   ├── add
│   ├── delete
│   └── list
├── label                   # Labels
│   ├── clone
│   ├── create
│   ├── delete
│   ├── edit
│   └── list
├── preview                 # Preview features
├── ruleset                 # Rulesets
│   ├── check
│   ├── list
│   └── view
├── search                  # Search
│   ├── code
│   ├── commits
│   ├── issues
│   ├── prs
│   └── repos
├── secret                  # Secrets
│   ├── delete
│   ├── list
│   └── set
├── ssh-key                 # SSH keys
│   ├── add
│   ├── delete
│   └── list
├── status                  # Status overview
└── variable                # Variables
    ├── delete
    ├── get
    ├── list
    └── set
```

## Configuration

### Global Configuration

```bash
# List all configuration
gh config list

# Get specific configuration value
gh config list git_protocol
gh config get editor

# Set configuration value
gh config set editor vim
gh config set git_protocol ssh
gh config set prompt disabled
gh config set pager "less -R"

# Clear configuration cache
gh config clear-cache
```

### Environment Variables

```bash
# GitHub token (for automation)
export GH_TOKEN=ghp_xxxxxxxxxxxx

# GitHub hostname
export GH_HOST=github.com

# Disable prompts
export GH_PROMPT_DISABLED=true

# Custom editor
export GH_EDITOR=vim

# Custom pager
export GH_PAGER=less

# HTTP timeout
export GH_TIMEOUT=30

# Custom repository (override default)
export GH_REPO=owner/repo

# Custom git protocol
export GH_ENTERPRISE_HOSTNAME=hostname
```

## Authentication (gh auth)

### Login

```bash
# Interactive login
gh auth login

# Web-based authentication
gh auth login --web

# With clipboard for OAuth code
gh auth login --web --clipboard

# With specific git protocol
gh auth login --git-protocol ssh

# With custom hostname (GitHub Enterprise)
gh auth login --hostname enterprise.internal

# Login with token from stdin
gh auth login --with-token < token.txt

# Insecure storage (plain text)
gh auth login --insecure-storage
```

### Status

```bash
# Show all authentication status
gh auth status

# Show active account only
gh auth status --active

# Show specific hostname
gh auth status --hostname github.com

# Show token in output
gh auth status --show-token

# JSON output
gh auth status --json hosts

# Filter with jq
gh auth status --json hosts --jq '.hosts | add'
```

### Switch Accounts

```bash
# Interactive switch
gh auth switch

# Switch to specific user/host
gh auth switch --hostname github.com --user monalisa
```

### Token

```bash
# Print authentication token
gh auth token

# Token for specific host/user
gh auth token --hostname github.com --user monalisa
```

### Refresh

```bash
# Refresh credentials
gh auth refresh

# Add scopes
gh auth refresh --scopes write:org,read:public_key

# Remove scopes
gh auth refresh --remove-scopes delete_repo

# Reset to default scopes
gh auth refresh --reset-scopes

# With clipboard
gh auth refresh --clipboard
```

### Setup Git

```bash
# Setup git credential helper
gh auth setup-git

# Setup for specific host
gh auth setup-git --hostname enterprise.internal

# Force setup even if host not known
gh auth setup-git --hostname enterprise.internal --force
```

## Browse (gh browse)

```bash
# Open repository in browser
gh browse

# Open specific path
gh browse script/
gh browse main.go:312

# Open issue or PR
gh browse 123

# Open commit
gh browse 77507cd94ccafcf568f8560cfecde965fcfa63

# Open with specific branch
gh browse main.go --branch bug-fix

# Open different repository
gh browse --repo owner/repo

# Open specific pages
gh browse --actions       # Actions tab
gh browse --projects      # Projects tab
gh browse --releases      # Releases tab
gh browse --settings      # Settings page
gh browse --wiki          # Wiki page

# Print URL instead of opening
gh browse --no-browser
```

## Repositories (gh repo)

### Create Repository

```bash
# Create new repository
gh repo create my-repo

# Create with description
gh repo create my-repo --description "My awesome project"

# Create public repository
gh repo create my-repo --public

# Create private repository
gh repo create my-repo --private

# Create with homepage
gh repo create my-repo --homepage https://example.com

# Create with license
gh repo create my-repo --license mit

# Create with gitignore
gh repo create my-repo --gitignore python

# Initialize as template repository
gh repo create my-repo --template

# Create repository in organization
gh repo create org/my-repo

# Create without cloning locally
gh repo create my-repo --source=.

# Disable issues
gh repo create my-repo --disable-issues

# Disable wiki
gh repo create my-repo --disable-wiki
```

### Clone Repository

```bash
# Clone repository
gh repo clone owner/repo

# Clone to specific directory
gh repo clone owner/repo my-directory

# Clone with different branch
gh repo clone owner/repo --branch develop
```

### List Repositories

```bash
# List all repositories
gh repo list

# List repositories for owner
gh repo list owner

# Limit results
gh repo list --limit 50

# Public repositories only
gh repo list --public

# Source repositories only (not forks)
gh repo list --source

# JSON output
gh repo list --json name,visibility,owner

# Table output
gh repo list --limit 100 | tail -n +2

# Filter with jq
gh repo list --json name --jq '.[].name'
```

### View Repository

```bash
# View repository details
gh repo view

# View specific repository
gh repo view owner/repo

# JSON output
gh repo view --json name,description,defaultBranchRef

# View in browser
gh repo view --web
```

### Edit Repository

```bash
# Edit description
gh repo edit --description "New description"

# Set homepage
gh repo edit --homepage https://example.com

# Change visibility
gh repo edit --visibility private
gh repo edit --visibility public

# Enable/disable features
gh repo edit --enable-issues
gh repo edit --disable-issues
gh repo edit --enable-wiki
gh repo edit --disable-wiki
gh repo edit --enable-projects
gh repo edit --disable-projects

# Set default branch
gh repo edit --default-branch main

# Rename repository
gh repo rename new-name

# Archive repository
gh repo archive
gh repo unarchive
```

### Delete Repository

```bash
# Delete repository
gh repo delete owner/repo

# Confirm without prompt
gh repo delete owner/repo --yes
```

### Fork Repository

```bash
# Fork repository
gh repo fork owner/repo

# Fork to organization
gh repo fork owner/repo --org org-name

# Clone after forking
gh repo fork owner/repo --clone

# Remote name for fork
gh repo fork owner/repo --remote-name upstream
```

### Sync Fork

```bash
# Sync fork with upstream
gh repo sync

# Sync specific branch
gh repo sync --branch feature

# Force sync
gh repo sync --force
```

### Set Default Repository

```bash
# Set default repository for current directory
gh repo set-default

# Set default explicitly
gh repo set-default owner/repo

# Unset default
gh repo set-default --unset
```

### Repository Autolinks

```bash
# List autolinks
gh repo autolink list

# Add autolink
gh repo autolink add \
  --key-prefix JIRA- \
  --url-template https://jira.example.com/browse/<num>

# Delete autolink
gh repo autolink delete 12345
```

### Repository Deploy Keys

```bash
# List deploy keys
gh repo deploy-key list

# Add deploy key
gh repo deploy-key add ~/.ssh/id_rsa.pub \
  --title "Production server" \
  --read-only

# Delete deploy key
gh repo deploy-key delete 12345
```

### Gitignore and License

```bash
# View gitignore template
gh repo gitignore

# View license template
gh repo license mit

# License with full name
gh repo license mit --fullname "John Doe"
```

## Issues (gh issue)

### Create Issue

```bash
# Create issue interactively
gh issue create

# Create with title
gh issue create --title "Bug: Login not working"

# Create with title and body
gh issue create \
  --title "Bug: Login not working" \
  --body "Steps to reproduce..."

# Create with body from file
gh issue create --body-file issue.md

# Create with labels
gh issue create --title "Fix bug" --labels bug,high-priority

# Create with assignees
gh issue create --title "Fix bug" --assignee user1,user2

# Create in specific repository
gh issue create --repo owner/repo --title "Issue title"

# Create issue from web
gh issue create --web
```

### List Issues

```bash
# List all open issues
gh issue list

# List all issues (including closed)
gh issue list --state all

# List closed issues
gh issue list --state closed

# Limit results
gh issue list --limit 50

# Filter by assignee
gh issue list --assignee username
gh issue list --assignee @me

# Filter by labels
gh issue list --labels bug,enhancement

# Filter by milestone
gh issue list --milestone "v1.0"

# Search/filter
gh issue list --search "is:open is:issue label:bug"

# JSON output
gh issue list --json number,title,state,author

# Table view
gh issue list --json number,title,labels --jq '.[] | [.number, .title, .labels[].name] | @tsv'

# Show comments count
gh issue list --json number,title,comments --jq '.[] | [.number, .title, .comments]'

# Sort by
gh issue list --sort created --order desc
```

### View Issue

```bash
# View issue
gh issue view 123

# View with comments
gh issue view 123 --comments

# View in browser
gh issue view 123 --web

# JSON output
gh issue view 123 --json title,body,state,labels,comments

# View specific fields
gh issue view 123 --json title --jq '.title'
```

### Edit Issue

```bash
# Edit interactively
gh issue edit 123

# Edit title
gh issue edit 123 --title "New title"

# Edit body
gh issue edit 123 --body "New description"

# Add labels
gh issue edit 123 --add-label bug,high-priority

# Remove labels
gh issue edit 123 --remove-label stale

# Add assignees
gh issue edit 123 --add-assignee user1,user2

# Remove assignees
gh issue edit 123 --remove-assignee user1

# Set milestone
gh issue edit 123 --milestone "v1.0"
```

### Close/Reopen Issue

```bash
# Close issue
gh issue close 123

# Close with comment
gh issue close 123 --comment "Fixed in PR #456"

# Reopen issue
gh issue reopen 123
```

### Comment on Issue

```bash
# Add comment
gh issue comment 123 --body "This looks good!"

# Edit comment
gh issue comment 123 --edit 456789 --body "Updated comment"

# Delete comment
gh issue comment 123 --delete 456789
```

### Issue Status

```bash
# Show issue status summary
gh issue status

# Status for specific repository
gh issue status --repo owner/repo
```

### Pin/Unpin Issues

```bash
# Pin issue (pinned to repo dashboard)
gh issue pin 123

# Unpin issue
gh issue unpin 123
```

### Lock/Unlock Issue

```bash
# Lock conversation
gh issue lock 123

# Lock with reason
gh issue lock 123 --reason off-topic

# Unlock
gh issue unlock 123
```

### Transfer Issue

```bash
# Transfer to another repository
gh issue transfer 123 --repo owner/new-repo
```

### Delete Issue

```bash
# Delete issue
gh issue delete 123

# Confirm without prompt
gh issue delete 123 --yes
```

### Develop Issue (Draft PR)

```bash
# Create draft PR from issue
gh issue develop 123

# Create in specific branch
gh issue develop 123 --branch fix/issue-123

# Create with base branch
gh issue develop 123 --base main
```

## Pull Requests (gh pr)

### Create Pull Request

```bash
# Create PR interactively
gh pr create

# Create with title
gh pr create --title "Feature: Add new functionality"

# Create with title and body
gh pr create \
  --title "Feature: Add new functionality" \
  --body "This PR adds..."

# Fill body from template
gh pr create --body-file .github/PULL_REQUEST_TEMPLATE.md

# Set base branch
gh pr create --base main

# Set head branch (default: current branch)
gh pr create --head feature-branch

# Create draft PR
gh pr create --draft

# Add assignees
gh pr create --assignee user1,user2

# Add reviewers
gh pr create --reviewer user1,user2

# Add labels
gh pr create --labels enhancement,feature

# Link to issue
gh pr create --issue 123

# Create in specific repository
gh pr create --repo owner/repo

# Open in browser after creation
gh pr create --web
```

### List Pull Requests

```bash
# List open PRs
gh pr list

# List all PRs
gh pr list --state all

# List merged PRs
gh pr list --state merged

# List closed (not merged) PRs
gh pr list --state closed

# Filter by head branch
gh pr list --head feature-branch

# Filter by base branch
gh pr list --base main

# Filter by author
gh pr list --author username
gh pr list --author @me

# Filter by assignee
gh pr list --assignee username

# Filter by labels
gh pr list --labels bug,enhancement

# Limit results
gh pr list --limit 50

# Search
gh pr list --search "is:open is:pr label:review-required"

# JSON output
gh pr list --json number,title,state,author,headRefName

# Show check status
gh pr list --json number,title,statusCheckRollup --jq '.[] | [.number, .title, .statusCheckRollup[]?.status]'

# Sort by
gh pr list --sort created --order desc
```

### View Pull Request

```bash
# View PR
gh pr view 123

# View with comments
gh pr view 123 --comments

# View in browser
gh pr view 123 --web

# JSON output
gh pr view 123 --json title,body,state,author,commits,files

# View diff
gh pr view 123 --json files --jq '.files[].path'

# View with jq query
gh pr view 123 --json title,state --jq '"\(.title): \(.state)"'
```

### Checkout Pull Request

```bash
# Checkout PR branch
gh pr checkout 123

# Checkout with specific branch name
gh pr checkout 123 --branch name-123

# Force checkout
gh pr checkout 123 --force
```

### Diff Pull Request

```bash
# View PR diff
gh pr diff 123

# View diff with color
gh pr diff 123 --color always

# Output to file
gh pr diff 123 > pr-123.patch

# View diff of specific files
gh pr diff 123 --name-only
```

### Merge Pull Request

```bash
# Merge PR
gh pr merge 123

# Merge with specific method
gh pr merge 123 --merge
gh pr merge 123 --squash
gh pr merge 123 --rebase

# Delete branch after merge
gh pr merge 123 --delete-branch

# Merge with comment
gh pr merge 123 --subject "Merge PR #123" --body "Merging feature"

# Merge draft PR
gh pr merge 123 --admin

# Force merge (skip checks)
gh pr merge 123 --admin
```

### Close Pull Request

```bash
# Close PR (as draft, not merge)
gh pr close 123

# Close with comment
gh pr close 123 --comment "Closing due to..."
```

### Reopen Pull Request

```bash
# Reopen closed PR
gh pr reopen 123
```

### Edit Pull Request

```bash
# Edit interactively
gh pr edit 123

# Edit title
gh pr edit 123 --title "New title"

# Edit body
gh pr edit 123 --body "New description"

# Add labels
gh pr edit 123 --add-label bug,enhancement

# Remove labels
gh pr edit 123 --remove-label stale

# Add assignees
gh pr edit 123 --add-assignee user1,user2

# Remove assignees
gh pr edit 123 --remove-assignee user1

# Add reviewers
gh pr edit 123 --add-reviewer user1,user2

# Remove reviewers
gh pr edit 123 --remove-reviewer user1

# Mark as ready for review
gh pr edit 123 --ready
```

### Ready for Review

```bash
# Mark draft PR as ready
gh pr ready 123
```

### Pull Request Checks

```bash
# View PR checks
gh pr checks 123

# Watch checks in real-time
gh pr checks 123 --watch

# Watch interval (seconds)
gh pr checks 123 --watch --interval 5
```

### Comment on Pull Request

```bash
# Add comment
gh pr comment 123 --body "Looks good!"

# Comment on specific line
gh pr comment 123 --body "Fix this" \
  --repo owner/repo \
  --head-owner owner --head-branch feature

# Edit comment
gh pr comment 123 --edit 456789 --body "Updated"

# Delete comment
gh pr comment 123 --delete 456789
```

### Review Pull Request

```bash
# Review PR (opens editor)
gh pr review 123

# Approve PR
gh pr review 123 --approve --body "LGTM!"

# Request changes
gh pr review 123 --request-changes \
  --body "Please fix these issues"

# Comment on PR
gh pr review 123 --comment --body "Some thoughts..."

# Dismiss review
gh pr review 123 --dismiss
```

### Update Branch

```bash
# Update PR branch with latest base branch
gh pr update-branch 123

# Force update
gh pr update-branch 123 --force

# Use merge strategy
gh pr update-branch 123 --merge
```

### Lock/Unlock Pull Request

```bash
# Lock PR conversation
gh pr lock 123

# Lock with reason
gh pr lock 123 --reason off-topic

# Unlock
gh pr unlock 123
```

### Revert Pull Request

```bash
# Revert merged PR
gh pr revert 123

# Revert with specific branch name
gh pr revert 123 --branch revert-pr-123
```

### Pull Request Status

```bash
# Show PR status summary
gh pr status

# Status for specific repository
gh pr status --repo owner/repo
```

## GitHub Actions

### Workflow Runs (gh run)

```bash
# List workflow runs
gh run list

# List for specific workflow
gh run list --workflow "ci.yml"

# List for specific branch
gh run list --branch main

# Limit results
gh run list --limit 20

# JSON output
gh run list --json databaseId,status,conclusion,headBranch

# View run details
gh run view 123456789

# View run with verbose logs
gh run view 123456789 --log

# View specific job
gh run view 123456789 --job 987654321

# View in browser
gh run view 123456789 --web

# Watch run in real-time
gh run watch 123456789

# Watch with interval
gh run watch 123456789 --interval 5

# Rerun failed run
gh run rerun 123456789

# Rerun specific job
gh run rerun 123456789 --job 987654321

# Cancel run
gh run cancel 123456789

# Delete run
gh run delete 123456789

# Download run artifacts
gh run download 123456789

# Download specific artifact
gh run download 123456789 --name build

# Download to directory
gh run download 123456789 --dir ./artifacts
```

### Workflows (gh workflow)

```bash
# List workflows
gh workflow list

# View workflow details
gh workflow view ci.yml

# View workflow YAML
gh workflow view ci.yml --yaml

# View in browser
gh workflow view ci.yml --web

# Enable workflow
gh workflow enable ci.yml

# Disable workflow
gh workflow disable ci.yml

# Run workflow manually
gh workflow run ci.yml

# Run with inputs
gh workflow run ci.yml \
  --raw-field \
  version="1.0.0" \
  environment="production"

# Run from specific branch
gh workflow run ci.yml --ref develop
```

### Action Caches (gh cache)

```bash
# List caches
gh cache list

# List for specific branch
gh cache list --branch main

# List with limit
gh cache list --limit 50

# Delete cache
gh cache delete 123456789

# Delete all caches
gh cache delete --all
```

### Action Secrets (gh secret)

```bash
# List secrets
gh secret list

# Set secret (prompts for value)
gh secret set MY_SECRET

# Set secret from environment
echo "$MY_SECRET" | gh secret set MY_SECRET

# Set secret for specific environment
gh secret set MY_SECRET --env production

# Set secret for organization
gh secret set MY_SECRET --org orgname

# Delete secret
gh secret delete MY_SECRET

# Delete from environment
gh secret delete MY_SECRET --env production
```

### Action Variables (gh variable)

```bash
# List variables
gh variable list

# Set variable
gh variable set MY_VAR "some-value"

# Set variable for environment
gh variable set MY_VAR "value" --env production

# Set variable for organization
gh variable set MY_VAR "value" --org orgname

# Get variable value
gh variable get MY_VAR

# Delete variable
gh variable delete MY_VAR

# Delete from environment
gh variable delete MY_VAR --env production
```

## Projects (gh project)

```bash
# List projects
gh project list

# List for owner
gh project list --owner owner

# Open projects
gh project list --open

# View project
gh project view 123

# View project items
gh project view 123 --format json

# Create project
gh project create --title "My Project"

# Create in organization
gh project create --title "Project" --org orgname

# Create with readme
gh project create --title "Project" --readme "Description here"

# Edit project
gh project edit 123 --title "New Title"

# Delete project
gh project delete 123

# Close project
gh project close 123

# Copy project
gh project copy 123 --owner target-owner --title "Copy"

# Mark template
gh project mark-template 123

# List fields
gh project field-list 123

# Create field
gh project field-create 123 --title "Status" --datatype single_select

# Delete field
gh project field-delete 123 --id 456

# List items
gh project item-list 123

# Create item
gh project item-create 123 --title "New item"

# Add item to project
gh project item-add 123 --owner-owner --repo repo --issue 456

# Edit item
gh project item-edit 123 --id 456 --title "Updated title"

# Delete item
gh project item-delete 123 --id 456

# Archive item
gh project item-archive 123 --id 456

# Link items
gh project link 123 --id 456 --link-id 789

# Unlink items
gh project unlink 123 --id 456 --link-id 789

# View project in browser
gh project view 123 --web
```

## Releases (gh release)

```bash
# List releases
gh release list

# View latest release
gh release view

# View specific release
gh release view v1.0.0

# View in browser
gh release view v1.0.0 --web

# Create release
gh release create v1.0.0 \
  --notes "Release notes here"

# Create release with notes from file
gh release create v1.0.0 --notes-file notes.md

# Create release with target
gh release create v1.0.0 --target main

# Create release as draft
gh release create v1.0.0 --draft

# Create pre-release
gh release create v1.0.0 --prerelease

# Create release with title
gh release create v1.0.0 --title "Version 1.0.0"

# Upload asset to release
gh release upload v1.0.0 ./file.tar.gz

# Upload multiple assets
gh release upload v1.0.0 ./file1.tar.gz ./file2.tar.gz

# Upload with label (casing sensitive)
gh release upload v1.0.0 ./file.tar.gz --casing

# Delete release
gh release delete v1.0.0

# Delete with cleanup tag
gh release delete v1.0.0 --yes

# Delete specific asset
gh release delete-asset v1.0.0 file.tar.gz

# Download release assets
gh release download v1.0.0

# Download specific asset
gh release download v1.0.0 --pattern "*.tar.gz"

# Download to directory
gh release download v1.0.0 --dir ./downloads

# Download archive (zip/tar)
gh release download v1.0.0 --archive zip

# Edit release
gh release edit v1.0.0 --notes "Updated notes"

# Verify release signature
gh release verify v1.0.0

# Verify specific asset
gh release verify-asset v1.0.0 file.tar.gz
```

## Gists (gh gist)

```bash
# List gists
gh gist list

# List all gists (including private)
gh gist list --public

# Limit results
gh gist list --limit 20

# View gist
gh gist view abc123

# View gist files
gh gist view abc123 --files

# Create gist
gh gist create script.py

# Create gist with description
gh gist create script.py --desc "My script"

# Create public gist
gh gist create script.py --public

# Create multi-file gist
gh gist create file1.py file2.py

# Create from stdin
echo "print('hello')" | gh gist create

# Edit gist
gh gist edit abc123

# Delete gist
gh gist delete abc123

# Rename gist file
gh gist rename abc123 --filename old.py new.py

# Clone gist
gh gist clone abc123

# Clone to directory
gh gist clone abc123 my-directory
```

## Codespaces (gh codespace)

```bash
# List codespaces
gh codespace list

# Create codespace
gh codespace create

# Create with specific repository
gh codespace create --repo owner/repo

# Create with branch
gh codespace create --branch develop

# Create with specific machine
gh codespace create --machine premiumLinux

# View codespace details
gh codespace view

# SSH into codespace
gh codespace ssh

# SSH with specific command
gh codespace ssh --command "cd /workspaces && ls"

# Open codespace in browser
gh codespace code

# Open in VS Code
gh codespace code --codec

# Open with specific path
gh codespace code --path /workspaces/repo

# Stop codespace
gh codespace stop

# Delete codespace
gh codespace delete

# View logs
gh codespace logs

--tail 100

# View ports
gh codespace ports

# Forward port
gh codespace cp 8080:8080

# Rebuild codespace
gh codespace rebuild

# Edit codespace
gh codespace edit --machine standardLinux

# Jupyter support
gh codespace jupyter

# Copy files to/from codespace
gh codespace cp file.txt :/workspaces/file.txt
gh codespace cp :/workspaces/file.txt ./file.txt
```

## Organizations (gh org)

```bash
# List organizations
gh org list

# List for user
gh org list --user username

# JSON output
gh org list --json login,name,description

# View organization
gh org view orgname

# View organization members
gh org view orgname --json members --jq '.members[] | .login'
```

## Search (gh search)

```bash
# Search code
gh search code "TODO"

# Search in specific repository
gh search code "TODO" --repo owner/repo

# Search commits
gh search commits "fix bug"

# Search issues
gh search issues "label:bug state:open"

# Search PRs
gh search prs "is:open is:pr review:required"

# Search repositories
gh search repos "stars:>1000 language:python"

# Limit results
gh search repos "topic:api" --limit 50

# JSON output
gh search repos "stars:>100" --json name,description,stargazers

# Order results
gh search repos "language:rust" --order desc --sort stars

# Search with extensions
gh search code "import" --extension py

# Web search (open in browser)
gh search prs "is:open" --web
```

## Labels (gh label)

```bash
# List labels
gh label list

# Create label
gh label create bug --color "d73a4a" --description "Something isn't working"

# Create with hex color
gh label create enhancement --color "#a2eeef"

# Edit label
gh label edit bug --name "bug-report" --color "ff0000"

# Delete label
gh label delete bug

# Clone labels from repository
gh label clone owner/repo

# Clone to specific repository
gh label clone owner/repo --repo target/repo
```

## SSH Keys (gh ssh-key)

```bash
# List SSH keys
gh ssh-key list

# Add SSH key
gh ssh-key add ~/.ssh/id_rsa.pub --title "My laptop"

# Add key with type
gh ssh-key add ~/.ssh/id_ed25519.pub --type "authentication"

# Delete SSH key
gh ssh-key delete 12345

# Delete by title
gh ssh-key delete --title "My laptop"
```

## GPG Keys (gh gpg-key)

```bash
# List GPG keys
gh gpg-key list

# Add GPG key
gh gpg-key add ~/.ssh/id_rsa.pub

# Delete GPG key
gh gpg-key delete 12345

# Delete by key ID
gh gpg-key delete ABCD1234
```

## Status (gh status)

```bash
# Show status overview
gh status

# Status for specific repositories
gh status --repo owner/repo

# JSON output
gh status --json
```

## Configuration (gh config)

```bash
# List all config
gh config list

# Get specific value
gh config get editor

# Set value
gh config set editor vim

# Set git protocol
gh config set git_protocol ssh

# Clear cache
gh config clear-cache

# Set prompt behavior
gh config set prompt disabled
gh config set prompt enabled
```

## Extensions (gh extension)

```bash
# List installed extensions
gh extension list

# Search extensions
gh extension search github

# Install extension
gh extension install owner/extension-repo

# Install from branch
gh extension install owner/extension-repo --branch develop

# Upgrade extension
gh extension upgrade extension-name

# Remove extension
gh extension remove extension-name

# Create new extension
gh extension create my-extension

# Browse extensions
gh extension browse

# Execute extension command
gh extension exec my-extension --arg value
```

## Aliases (gh alias)

```bash
# List aliases
gh alias list

# Set alias
gh alias set prview 'pr view --web'

# Set shell alias
gh alias set co 'pr checkout' --shell

# Delete alias
gh alias delete prview

# Import aliases
gh alias import ./aliases.sh
```

## API Requests (gh api)

```bash
# Make API request
gh api /user

# Request with method
gh api --method POST /repos/owner/repo/issues \
  --field title="Issue title" \
  --field body="Issue body"

# Request with headers
gh api /user \
  --header "Accept: application/vnd.github.v3+json"

# Request with pagination
gh api /user/repos --paginate

# Raw output (no formatting)
gh api /user --raw

# Include headers in output
gh api /user --include

# Silent mode (no progress output)
gh api /user --silent

# Input from file
gh api --input request.json

# jq query on response
gh api /user --jq '.login'

# Field from response
gh api /repos/owner/repo --jq '.stargazers_count'

# GitHub Enterprise
gh api /user --hostname enterprise.internal

# GraphQL query
gh api graphql \
  -f query='
  {
    viewer {
      login
      repositories(first: 5) {
        nodes {
          name
        }
      }
    }
  }'
```

## Rulesets (gh ruleset)

```bash
# List rulesets
gh ruleset list

# View ruleset
gh ruleset view 123

# Check ruleset
gh ruleset check --branch feature

# Check specific repository
gh ruleset check --repo owner/repo --branch main
```

## Attestations (gh attestation)

```bash
# Download attestation
gh attestation download owner/repo \
  --artifact-id 123456

# Verify attestation
gh attestation verify owner/repo

# Get trusted root
gh attestation trusted-root
```

## Completion (gh completion)

```bash
# Generate shell completion
gh completion -s bash > ~/.gh-complete.bash
gh completion -s zsh > ~/.gh-complete.zsh
gh completion -s fish > ~/.gh-complete.fish
gh completion -s powershell > ~/.gh-complete.ps1

# Shell-specific instructions
gh completion --shell=bash
gh completion --shell=zsh
```

## Preview (gh preview)

```bash
# List preview features
gh preview

# Run preview script
gh preview prompter
```

## Agent Tasks (gh agent-task)

```bash
# List agent tasks
gh agent-task list

# View agent task
gh agent-task view 123

# Create agent task
gh agent-task create --description "My task"
```

## Global Flags

| Flag                       | Description                            |
| -------------------------- | -------------------------------------- |
| `--help` / `-h`            | Show help for command                  |
| `--version`                | Show gh version                        |
| `--repo [HOST/]OWNER/REPO` | Select another repository              |
| `--hostname HOST`          | GitHub hostname                        |
| `--jq EXPRESSION`          | Filter JSON output                     |
| `--json FIELDS`            | Output JSON with specified fields      |
| `--template STRING`        | Format JSON using Go template          |
| `--web`                    | Open in browser                        |
| `--paginate`               | Make additional API calls              |
| `--verbose`                | Show verbose output                    |
| `--debug`                  | Show debug output                      |
| `--timeout SECONDS`        | Maximum API request duration           |
| `--cache CACHE`            | Cache control (default, force, bypass) |

## Output Formatting

### JSON Output

```bash
# Basic JSON
gh repo view --json name,description

# Nested fields
gh repo view --json owner,name --jq '.owner.login + "/" + .name'

# Array operations
gh pr list --json number,title --jq '.[] | select(.number > 100)'

# Complex queries
gh issue list --json number,title,labels \
  --jq '.[] | {number, title: .title, tags: [.labels[].name]}'
```

### Template Output

```bash
# Custom template
gh repo view \
  --template '{{.name}}: {{.description}}'

# Multiline template
gh pr view 123 \
  --template 'Title: {{.title}}
Author: {{.author.login}}
State: {{.state}}
'
```

## Common Workflows

### Create PR from Issue

```bash
# Create branch from issue
gh issue develop 123 --branch feature/issue-123

# Make changes, commit, push
git add .
git commit -m "Fix issue #123"
git push

# Create PR linking to issue
gh pr create --title "Fix #123" --body "Closes #123"
```

### Bulk Operations

```bash
# Close multiple issues
gh issue list --search "label:stale" \
  --json number \
  --jq '.[].number' | \
  xargs -I {} gh issue close {} --comment "Closing as stale"

# Add label to multiple PRs
gh pr list --search "review:required" \
  --json number \
  --jq '.[].number' | \
  xargs -I {} gh pr edit {} --add-label needs-review
```

### Repository Setup Workflow

```bash
# Create repository with initial setup
gh repo create my-project --public \
  --description "My awesome project" \
  --clone \
  --gitignore python \
  --license mit

cd my-project

# Set up branches
git checkout -b develop
git push -u origin develop

# Create labels
gh label create bug --color "d73a4a" --description "Bug report"
gh label create enhancement --color "a2eeef" --description "Feature request"
gh label create documentation --color "0075ca" --description "Documentation"
```

### CI/CD Workflow

```bash
# Run workflow and wait
RUN_ID=$(gh workflow run ci.yml --ref main --jq '.databaseId')

# Watch the run
gh run watch "$RUN_ID"

# Download artifacts on completion
gh run download "$RUN_ID" --dir ./artifacts
```

### Fork Sync Workflow

```bash
# Fork repository
gh repo fork original/repo --clone

cd repo

# Add upstream remote
git remote add upstream https://github.com/original/repo.git

# Sync fork
gh repo sync

# Or manual sync
git fetch upstream
git checkout main
git merge upstream/main
git push origin main
```

## Environment Setup

### Shell Integration

```bash
# Add to ~/.bashrc or ~/.zshrc
eval "$(gh completion -s bash)"  # or zsh/fish

# Create useful aliases
alias gs='gh status'
alias gpr='gh pr view --web'
alias gir='gh issue view --web'
alias gco='gh pr checkout'
```

### Git Configuration

```bash
# Use gh as credential helper
gh auth setup-git

# Set gh as default for repo operations
git config --global credential.helper 'gh !gh auth setup-git'

# Or manually
git config --global credential.helper github
```

## Best Practices

1. **Authentication**: Use environment variables for automation

   ```bash
   export GH_TOKEN=$(gh auth token)
   ```

2. **Default Repository**: Set default to avoid repetition

   ```bash
   gh repo set-default owner/repo
   ```

3. **JSON Parsing**: Use jq for complex data extraction

   ```bash
   gh pr list --json number,title --jq '.[] | select(.title | contains("fix"))'
   ```

4. **Pagination**: Use --paginate for large result sets

   ```bash
   gh issue list --state all --paginate
   ```

5. **Caching**: Use cache control for frequently accessed data
   ```bash
   gh api /user --cache force
   ```

## Getting Help

```bash
# General help
gh --help

# Command help
gh pr --help
gh issue create --help

# Help topics
gh help formatting
gh help environment
gh help exit-codes
gh help accessibility
```

## References

- Official Manual: https://cli.github.com/manual/
- GitHub Docs: https://docs.github.com/en/github-cli
- REST API: https://docs.github.com/en/rest
- GraphQL API: https://docs.github.com/en/graphql


================================================
FILE: .agents/skills/postgres/SKILL.md
================================================
---
name: postgres
description: PostgreSQL best practices, query optimization, connection troubleshooting, and performance improvement. Load when working with Postgres databases.
license: MIT
metadata:
  author: planetscale
  version: "1.0.0"
---

# PlanetScale Postgres

## Generic Postgres

| Topic                  | Reference                                                        | Use for                                                   |
| ---------------------- | ---------------------------------------------------------------- | --------------------------------------------------------- |
| Schema Design          | [references/schema-design.md](https://raw.githubusercontent.com/planetscale/database-skills/main/skills/postgres/references/schema-design.md)           | Tables, primary keys, data types, foreign keys            |
| Indexing               | [references/indexing.md](https://raw.githubusercontent.com/planetscale/database-skills/main/skills/postgres/references/indexing.md)                      | Index types, composite indexes, performance               |
| Index Optimization     | [references/index-optimization.md](https://raw.githubusercontent.com/planetscale/database-skills/main/skills/postgres/references/index-optimization.md) | Unused/duplicate index queries, index audit               |
| Partitioning           | [references/partitioning.md](https://raw.githubusercontent.com/planetscale/database-skills/main/skills/postgres/references/partitioning.md)             | Large tables, time-series, data retention                 |
| Query Patterns         | [references/query-patterns.md](https://raw.githubusercontent.com/planetscale/database-skills/main/skills/postgres/references/query-patterns.md)         | SQL anti-patterns, JOINs, pagination, batch queries       |
| Optimization Checklist | [references/optimization-checklist.md](https://raw.githubusercontent.com/planetscale/database-skills/main/skills/postgres/references/optimization-checklist.md) | Pre-optimization audit, cleanup, readiness checks  |
| MVCC and VACUUM        | [references/mvcc-vacuum.md](https://raw.githubusercontent.com/planetscale/database-skills/main/skills/postgres/references/mvcc-vacuum.md)               | Dead tuples, long transactions, xid wraparound prevention |

## Operations and Architecture

| Topic                  | Reference                                                                    | Use for                                                         |
| ---------------------- | ---------------------------------------------------------------------------- | --------------------------------------------------------------- |
| Process Architecture   | [references/process-architecture.md](https://raw.githubusercontent.com/planetscale/database-skills/main/skills/postgres/references/process-architecture.md)     | Multi-process model, connection pooling, auxiliary processes     |
| Memory Architecture    | [references/memory-management-ops.md](https://raw.githubusercontent.com/planetscale/database-skills/main/skills/postgres/references/memory-management-ops.md)   | Shared/private memory layout, OS page cache, OOM prevention     |
| MVCC Transactions      | [references/mvcc-transactions.md](https://raw.githubusercontent.com/planetscale/database-skills/main/skills/postgres/references/mvcc-transactions.md)           | Isolation levels, XID wraparound, serialization errors          |
| WAL and Checkpoints    | [references/wal-operations.md](https://raw.githubusercontent.com/planetscale/database-skills/main/skills/postgres/references/wal-operations.md)                 | WAL internals, checkpoint tuning, durability, crash recovery    |
| Replication            | [references/replication.md](https://raw.githubusercontent.com/planetscale/database-skills/main/skills/postgres/references/replication.md)                       | Streaming replication, slots, sync commit, failover             |
| Storage Layout         | [references/storage-layout.md](https://raw.githubusercontent.com/planetscale/database-skills/main/skills/postgres/references/storage-layout.md)                | PGDATA structure, TOAST, fillfactor, tablespaces, disk mgmt     |
| Monitoring             | [references/monitoring.md](https://raw.githubusercontent.com/planetscale/database-skills/main/skills/postgres/references/monitoring.md)                         | pg_stat views, logging, pg_stat_statements, host metrics        |
| Backup and Recovery    | [references/backup-recovery.md](https://raw.githubusercontent.com/planetscale/database-skills/main/skills/postgres/references/backup-recovery.md)              | pg_dump, pg_basebackup, PITR, WAL archiving, backup tools      |

## PlanetScale-Specific

| Topic              | Reference                                                                    | Use for                                               |
| ------------------ | ---------------------------------------------------------------------------- | ----------------------------------------------------- |
| Connection Pooling | [references/ps-connection-pooling.md](https://raw.githubusercontent.com/planetscale/database-skills/main/skills/postgres/references/ps-connection-pooling.md)   | PgBouncer, pool sizing, pooled vs direct              |
| Extensions         | [references/ps-extensions.md](https://raw.githubusercontent.com/planetscale/database-skills/main/skills/postgres/references/ps-extensions.md)                   | Supported extensions, compatibility                   |
| Connections        | [references/ps-connections.md](https://raw.githubusercontent.com/planetscale/database-skills/main/skills/postgres/references/ps-connections.md)                 | Connection troubleshooting, drivers, SSL              |
| Insights           | [references/ps-insights.md](https://raw.githubusercontent.com/planetscale/database-skills/main/skills/postgres/references/ps-insights.md)                       | Slow queries, MCP server, pscale CLI                  |
| CLI Commands       | [references/ps-cli-commands.md](https://raw.githubusercontent.com/planetscale/database-skills/main/skills/postgres/references/ps-cli-commands.md)               | pscale CLI reference, branches, deploy requests, auth |
| CLI API Insights   | [references/ps-cli-api-insights.md](https://raw.githubusercontent.com/planetscale/database-skills/main/skills/postgres/references/ps-cli-api-insights.md)       | Query insights via `pscale api`, schema analysis      |


================================================
FILE: .agents/skills/postgres/references/backup-recovery.md
================================================
---
title: Backup and Recovery
description: Logical/physical backups, PITR, WAL archiving, backup tools, and recovery strategies
tags: postgres, backup, recovery, pitr, pg_dump, pg_basebackup, wal-archiving, operations
---

# Backup and Recovery

**FUNDAMENTAL RULE: Backups are useless until you've successfully tested recovery.**

## Logical Backups (pg_dump)
Exports as SQL or custom format; portable across PG versions and architectures. Formats: `-Fp` (plain SQL), `-Fc` (custom compressed, selective restore), `-Fd` (directory, parallel with `-j`), `-Ft` (tar, avoid). Use `-Fd -j 4` for large DBs. Restore: `pg_restore -d dbname file.dump`; add `-j` for parallel restore. Selective table restore: `pg_restore -t tablename`. Slow for large DBs; RPO = backup frequency (typically 24h).

## Physical Backups (pg_basebackup)
Copies raw PGDATA; same major version and platform required; cross-architecture works if same endianness (e.g., x86_64 ↔ ARM64). Faster for large clusters; includes all databases. Flags: `-Ft -z -P` for compressed tar with progress. Manual alternative: `pg_backup_start()` → copy PGDATA → `pg_backup_stop()` (complex; must write returned `backup_label`).

## PITR (Point-in-Time Recovery)
Requires base backup + continuous WAL archiving. Restores to any timestamp, transaction, or named restore point. Without PITR: restore only to backup time (potentially lose hours). With PITR: RPO = minutes. `archive_command` must return 0 ONLY when file is safely stored—premature 0 = data loss risk. `wal_level` must be `replica` or `logical` (not `minimal`).

## WAL Archiving
`archive_mode=on`, `archive_command='test ! -f /archive/%f && cp %p /archive/%f'`. **Test archive command as postgres user** (not root) since permission issues are common. Monitor `pg_stat_archiver` for `failed_count`, `last_archived_time`. Archive failures prevent WAL recycling → disk fills.

## Tool Comparison
| Tool | Use case |
|------|----------|
| pg_dump | Small DBs, migrations, selective restore |
| pg_basebackup | Basic PITR, built-in |
| pgBackRest | Production—parallel, incremental, S3/GCS/Azure, retention |
| Barman | Enterprise PITR, retention policies |
| WAL-G | Cloud-native, S3/GCS/Azure |

## RPO/RTO
Logical only: RPO = backup interval (hours); RTO = hours. PITR: RPO = minutes; RTO = hours. Synchronous replication: RPO = 0; RTO = seconds to minutes (failover).

## Operational Rules
- Verify integrity with `pg_verifybackup` (PG 13+)
- Test recovery / PITR regularly
- Take backups from standby to avoid impacting primary
- Retention: 7 daily, 4 weekly, 12 monthly
- Monitor archive growth and backup age
- **Never assume backups work without testing**


================================================
FILE: .agents/skills/postgres/references/index-optimization.md
================================================
---
title: Index Optimization Queries
description: Index audit queries
tags: postgres, indexes, unused-indexes, duplicate-indexes, optimization
---

# Index Optimization

## Identify Unused Indexes

Query to find unused indexes:

```sql
-- indexes with 0 scans (check pg_stat_reset / pg_postmaster_start_time first)
SELECT
   s.schemaname,
   s.relname AS table_name,
   s.indexrelname AS index_name,
   pg_size_pretty(pg_relation_size(s.indexrelid)) AS index_size
 FROM pg_catalog.pg_stat_user_indexes s
 JOIN pg_catalog.pg_index i ON s.indexrelid = i.indexrelid
 WHERE s.idx_scan = 0
   AND 0 <> ALL (i.indkey)       -- exclude expression indexes
   AND NOT i.indisunique          -- exclude UNIQUE indexes
   AND NOT EXISTS (               -- exclude constraint-backing indexes
     SELECT 1 FROM pg_catalog.pg_constraint c
     WHERE c.conindid = s.indexrelid
   )
 ORDER BY pg_relation_size(s.indexrelid) DESC;
```

## Indexes Per Table Guidelines

- **< 5**: Normal
- **5-10**: Monitor (Verify necessity)
- **> 10**: Audit required (High write overhead)

```sql
SELECT relname AS table, count(*) as index_count
FROM pg_stat_user_indexes
GROUP BY relname
ORDER BY count(*) DESC;
```

## Identify Unused Indexes

Indexes with identical definitions (after normalizing names) on the same table are duplicates:

```sql
SELECT
  schemaname || '.' || tablename AS table,
  array_agg(indexname) AS duplicate_indexes,
  pg_size_pretty(sum(pg_relation_size((schemaname || '.' || indexname)::regclass))) AS total_size
FROM pg_indexes
WHERE schemaname NOT IN ('pg_catalog', 'information_schema')
GROUP BY schemaname, tablename,
  regexp_replace(indexdef, 'INDEX \S+ ON ', 'INDEX ON ')
HAVING count(*) > 1;
```

**Always confirm with a human before dropping or removing any indexes identified by the queries above.** Even indexes with 0 scans may be needed for infrequent but critical queries, and stats may have been reset recently.

## Per-table Index Count Guidelines

| Index Count | Recommendation                              |
| ----------- | ------------------------------------------- |
| <5          | Normal                                      |
| 5-10        | Review for unused/duplicates                |
| >10         | Audit required - significant write overhead |


================================================
FILE: .agents/skills/postgres/references/indexing.md
================================================
---
title: Indexing Best Practices
description: Index design guide
tags: postgres, indexes, composite, partial, covering, gin, brin
---

# Indexing Best Practices

## Core Rules

1. **Always index foreign key columns** — PostgreSQL does not auto-create these
2. **Index columns in WHERE, JOIN, and ORDER BY** clauses
3. **Don't over-index** — each index slows writes and uses storage
4. **Verify with EXPLAIN ANALYZE** — confirm indexes are actually used

## Composite Indexes

Put equality columns first, then range/sort columns:

```sql
-- WHERE status = 'active' AND created_at > '2026-01-01'
CREATE INDEX order_status_created_idx ON order (status, created_at);
```

A composite index on `(a, b)` supports queries on `a` + `b` and `a` alone, but not `b` alone.

## Partial Indexes

Reduce index size by filtering to common query patterns.
Only use if index size is problematic but the index is needed for performance.

```sql
CREATE INDEX order_active_idx ON order (customer_id)
  WHERE status = 'active';
```

## Covering Indexes

Consider creating covering indexes for commonly executed query patterns that return only 1 or a small number of columns.

## Index Types

| Type | Use Case | Example |
| --- | --- | --- |
| B-tree (default) | Equality, range, sorting | `WHERE id = 1`, `ORDER BY date` |
| GIN | Arrays, JSONB, full-text | `WHERE tags @> ARRAY['x']` |
| GiST | Geometric, range types, full-text | PostGIS, `tsrange`, `tsvector` |
| BRIN | Large sequential/time-series | Append-only logs, events (requires physical row order correlation) |

```sql
CREATE INDEX metadata_idx ON order USING GIN (metadata);       -- JSONB
CREATE INDEX event_created_idx ON event USING BRIN (created_at); -- time-series
```

## Guidelines

- Name indexes consistently: `{table}_{column}_idx`
- Review for unused indexes periodically
- **Always confirm with a human before removing or dropping any indexes** — even unused ones may serve a purpose not reflected in recent stats
- Use partial indexes for frequently filtered subsets
- Use covering indexes on hot read paths


================================================
FILE: .agents/skills/postgres/references/memory-management-ops.md
================================================
---
title: Memory Architecture and OOM Prevention
description: PostgreSQL shared/private memory layout, OS page cache interaction, and OOM avoidance strategies
tags: postgres, memory, shared_buffers, work_mem, oom, architecture, operations
---

# Memory Architecture and OOM Prevention

## Memory Areas

- **Shared memory**: `shared_buffers` — main data cache, all processes, requires restart to change.
- **Private per backend**: `work_mem` (sorts/hashes/joins, per-operation); `maintenance_work_mem` (VACUUM, CREATE INDEX, ALTER TABLE ADD FOREIGN KEY); `temp_buffers` (8MB default).
- **Planner hint only**: `effective_cache_size` is NOT allocated — set to ~50–75% of total RAM.
- **Hash multiplier**: `hash_mem_multiplier` (default 2.0) means hash ops use up to 2× `work_mem`.

## Memory Multiplication Danger

Maximum potential: `work_mem × operations_per_query × (parallel_workers + 1) × connections` (leader participates by default via `parallel_leader_participation = on`; hash operations use up to `hash_mem_multiplier × work_mem`, default 2.0). Example: 128MB work_mem, 3 ops (2 sorts + 1 hash join), 2 parallel workers, 100 connections → 2 sorts at 128MB = 256MB, 1 hash join at 128MB × 2.0 = 256MB, per process = 512MB, × 3 processes (2 workers + leader) = 1536MB/query, × 100 connections = **~150GB** worst case. This case is rare.
Not all queries hit limits at once, but high concurrency + large datasets approach it. This is a common cause of OOM in containerized/Kubernetes deployments. Plan capacity with a 1.5–2× safety margin.

## OS Page Cache (Double Buffering)

Data exists in both `shared_buffers` and OS page cache. A miss in shared_buffers can still hit OS cache (avoiding disk I/O). Extremely large shared_buffers can hurt performance: less OS cache, slower startup, heavier checkpoints. Optimal split depends on workload (OLTP vs OLAP).

## OOM Prevention

- Implement connection pooling to reduce total backend count.
- Reduce `work_mem` globally; use per-session overrides for heavy queries only.
- Lower `max_parallel_workers_per_gather` in high-concurrency systems.
- Set `statement_timeout` to kill runaway queries.
- Monitor: `dmesg -T | grep "killed process"` and `temp_blks_written` in pg_stat_statements.

## Operational Rules

- Tune per-session first, global last.
- Suspect OOM when memory spikes during high concurrency, dashboards, or large batch jobs.
- Increase memory only after confirming spill behavior (`temp_blks_written > 0`).
- `maintenance_work_mem` can be set much higher (1–2GB) — fewer processes use it. Cap autovacuum with `autovacuum_work_mem` to avoid `autovacuum_max_workers × maintenance_work_mem` memory spikes.
- `shared_buffers` change requires full restart; `work_mem` is per-session changeable.


================================================
FILE: .agents/skills/postgres/references/monitoring.md
================================================
---
title: Monitoring
description: Essential PostgreSQL monitoring views, pg_stat_statements, logging, host metrics, and statistics management
tags: postgres, monitoring, pg_stat_statements, logging, pgbadger, metrics, operations
---

# Monitoring

## Essential Views

- **pg_stat_activity**: First stop when something is wrong — running queries, states, wait events, locks.
- **pg_stat_statements**: Execution stats for all SQL. Requires `shared_preload_libraries = 'pg_stat_statements'` and `CREATE EXTENSION pg_stat_statements`.
- **pg_stat_database**: Cache hit ratio, temp files, deadlocks, connections per database.
- **pg_stat_user_tables**: `seq_scan` vs `idx_scan`, dead tuples, last vacuum/analyze times.
- **pg_stat_user_indexes**: Find unused indexes (`idx_scan = 0` with large size).
- **pg_stat_bgwriter**: `buffers_clean`, `maxwritten_clean`, `buffers_alloc`. Pre-PG 17 also had `buffers_checkpoint`, `buffers_backend` (high = backends bypassing bgwriter). PG 17+ moved checkpoint stats to `pg_stat_checkpointer`.
- **pg_stat_checkpointer** (PG 17+): Checkpoint frequency (`num_timed`, `num_requested`), write/sync time.

## Key Queries

```sql
-- Slow queries (with cache hit ratio)
SELECT query, calls, mean_exec_time,
  100.0 * shared_blks_hit / nullif(shared_blks_hit + shared_blks_read, 0) AS cache_hit_pct
FROM pg_stat_statements ORDER BY mean_exec_time DESC LIMIT 10;

-- Connection counts / states
SELECT state, count(*) FROM pg_stat_activity GROUP BY state;

-- Dead tuples (vacuum candidates)
SELECT relname, n_dead_tup, last_autovacuum FROM pg_stat_user_tables ORDER BY n_dead_tup DESC;
-- last_autovacuum = <null> means autovacuum has not run on this table
```

Blocking: use `pg_blocking_pids(pid)` with `pg_stat_activity` to find blocked and blocking sessions.

## Logging — First Line of Defense

PostgreSQL is extremely vocal about problems. **Always check logs first**: `tail -f /var/log/postgresql/postgresql-*.log`.

Key settings: `log_min_duration_statement` (OLTP: 1–3s, analytics: 30–60s, dev: 100–500ms). Enable `log_checkpoints=on`, `log_connections=on`, `log_disconnections=on`, `log_lock_waits=on`, `log_temp_files=0`. Use CSV log format for pgBadger analysis; pgBadger generates HTML reports with query stats and performance graphs.

## pg_activity

Interactive top-like tool (pip install pg_activity). Run on DB host for OS metrics alongside PG metrics. Combines `pg_stat_activity` with CPU/memory/I/O context.

## Host Metrics — Critical

PostgreSQL cannot report these. **Monitor them yourself:**

- **CPU**: Steal time >10% in VMs bad; load average > core count; context switches >100k/sec.
- **Memory**: Any swap = performance degradation. Check `dmesg` for OOM kills.
- **Disk I/O**: `iostat -x` — `%util=100%` means saturated; `await` >10ms = high latency.
- **Disk space**: >90% critical (VACUUM fails, writes fail). Check inode usage too.
- **Network**: Packet loss >0% = problems; high retransmits = instability.

## Statistics Management

Stats accumulate since last reset or restart; check `stats_reset` timestamp. `pg_stat_statements_reset()` clears query stats; `pg_stat_reset()` clears database stats. Reset after major maintenance, config changes, or perf testing — not routinely. Prefer snapshotting stats to external monitoring (Prometheus, Datadog) over resetting. **Always confirm with a human before resetting statistics** — resetting destroys historical performance baselines and can make it harder to identify unused indexes or regressions.


================================================
FILE: .agents/skills/postgres/references/mvcc-transactions.md
================================================
---
title: MVCC Transactions and Concurrency
description: Transaction isolation levels, XID wraparound prevention, serialization errors, and long-transaction impact
tags: postgres, mvcc, transactions, isolation, xid-wraparound, concurrency, serialization
---

# MVCC Transactions and Concurrency

## Transaction Isolation Levels

- **READ UNCOMMITTED** — treated as READ COMMITTED in PostgreSQL; no dirty reads ever.
- **READ COMMITTED** (default): new snapshot per statement; can see different data within same tx.
- **REPEATABLE READ**: snapshot at first query; can cause serialization errors on write conflicts.
- **SERIALIZABLE**: strongest; transactions appear serial; requires retry logic in app code.

Readers never block writers; writers never block readers (only writer-writer conflicts on same row). No lock escalation — row locks never degrade to table locks.

## XID Wraparound

32-bit transaction IDs wrap at ~2 billion (2^31). `VACUUM FREEZE` replaces old XIDs with FrozenXID (value 2, always visible). Without freeze: after wraparound, old rows appear "in the future" and become **invisible**. Data physically exists but is invisible to all queries — looks like total data loss. PostgreSQL emergency shutdown at 2B XIDs to prevent this. XID wraparound should be avoided at all cost.

Warning messages start at ~1.4B XIDs; shutdown at 2B. Recovery requires single-user mode VACUUM — can take hours to days on large DBs. **Never disable autovacuum** — it's your protection against wraparound.

## XID Age Monitoring

```sql
SELECT datname, age(datfrozenxid),
  ROUND(100.0 * age(datfrozenxid) / 2147483648, 2) AS pct
FROM pg_database ORDER BY age(datfrozenxid) DESC;
```

## Long Transaction Impact

A single long-running transaction blocks VACUUM from removing dead tuples across the **entire database**. Causes table bloat, increased disk, slower queries, cache pollution. `idle_in_transaction` connections are the #1 operational MVCC issue. Set `idle_in_transaction_session_timeout` (30s–5min). Dead tuples waste I/O on seq scans and cause useless heap lookups from indexes.

## Serialization Errors

Apps **must** handle "could not serialize access" with retry logic. More common in REPEATABLE READ and SERIALIZABLE. Smaller, faster transactions reduce conflict frequency.


================================================
FILE: .agents/skills/postgres/references/mvcc-vacuum.md
================================================
---
title: MVCC and VACUUM
description: MVCC internals, VACUUM/autovacuum tuning, and bloat prevention
tags: postgres, mvcc, vacuum, autovacuum, xid, bloat, dead-tuples
---

# MVCC and VACUUM

## MVCC

Every `UPDATE` creates a new tuple and marks the old one dead; `DELETE` marks tuples dead. Dead tuples accumulate until `VACUUM` reclaims space. Each transaction gets a 32-bit XID (2^32 ≈ 4B values, but modular comparison means the effective danger zone is 2^31 ≈ 2B). VACUUM must freeze old XIDs to prevent wraparound.

## VACUUM vs VACUUM FULL

`VACUUM` is non-blocking (ShareUpdateExclusive lock) and marks dead space reusable. `VACUUM FULL` rewrites the table and requires an AccessExclusive lock — use only as a last resort. For online bloat reduction prefer `pg_squeeze` or `pg_repack`.

## Autovacuum Tuning

Triggers when dead tuples > `Min(autovacuum_vacuum_max_threshold, autovacuum_vacuum_threshold + autovacuum_vacuum_scale_factor * reltuples)`. `autovacuum_vacuum_max_threshold` defaults to 100M (PG 18+), capping the threshold for very large tables. Also triggers on inserts exceeding `autovacuum_vacuum_insert_threshold + autovacuum_vacuum_insert_scale_factor * reltuples * pct_not_frozen` (ensures insert-only tables get frozen; PG 13+). For large/hot tables, set per-table overrides:

- `autovacuum_vacuum_scale_factor` — default 0.2; lower to 0.01–0.05 for large tables.
- `autovacuum_vacuum_cost_delay` — default 2 ms; set to 0 on fast storage.
- `autovacuum_vacuum_cost_limit` — default -1 (uses `vacuum_cost_limit`, effectively 200); raise to 1000–2000 on fast storage.
- `autovacuum_freeze_max_age` — default 200M; triggers anti-wraparound vacuum.
- `vacuum_failsafe_age` — default 1.6B; last-resort mode (PG 14+) that disables throttling and skips index vacuuming when wraparound is imminent.

## Key Monitoring Queries

Dead tuples: `SELECT relname, n_dead_tup, last_autovacuum FROM pg_stat_user_tables ORDER BY n_dead_tup DESC;`

XID age: `SELECT datname, age(datfrozenxid) AS xid_age FROM pg_database ORDER BY xid_age DESC;`

Long transactions: `SELECT pid, state, now() - xact_start AS tx_age FROM pg_stat_activity WHERE xact_start IS NOT NULL ORDER BY xact_start;`

## Best Practices

- Keep transactions short; set `idle_in_transaction_session_timeout` (30s–5min).
- Alert when `age(datfrozenxid)` exceeds 40–50% of wraparound (~800M–1B).
- Tune autovacuum per-table for write-heavy tables; don't change global defaults first.
- Fix application transaction scope before adjusting vacuum parameters.
- Never disable autovacuum globally.


================================================
FILE: .agents/skills/postgres/references/optimization-checklist.md
================================================
---
title: Database Optimization Checklist
description: Optimize checklist
tags: postgres, optimization, indexes, partitioning, maintenance
---

# Optimization Checklist

When optimizing performance, check the following:

- Look for unused indexes (0 scans; exclude unique/primary indexes and verify stats age first)
- Look for duplicate indexes
- Archive audit/log tables >10GB
- Review tables >500GB for partitioning (>100GB for time-series/logs)
- Verify all extensions are supported
- Check for circular foreign key dependencies
- Consider alternatives to UUID primary keys for large tables
- Configure connection pooling for OLTP workloads
- **Always confirm with a human before removing any indexes, dropping partitions, archiving tables, or performing other destructive actions**


================================================
FILE: .agents/skills/postgres/references/partitioning.md
================================================
---
title: Table Partitioning Guide
description: Partition guide
tags: postgres, partitioning, range, list, pg_partman, data-retention
---

# Table Partitioning

Plan partitioning upfront for tables expected to grow large. Retrofitting later requires a migration.

## When to Partition

Partitioning benefits maintenance (vacuum, index builds) and data retention more than pure query speed.

| Table Type | Size Threshold | Row Threshold |
| --- | --- | --- |
| General tables | >100 GB (or >RAM) | >20M rows |
| Time-series / logs | >50 GB | >10M rows |

Use the lower thresholds for append-heavy, time-ordered data with retention needs (logs, events, audit trails, metrics).

## Range Partitioning (Most Common)

```sql
-- EXAMPLE
CREATE TABLE event (
  id BIGINT GENERATED ALWAYS AS IDENTITY,
  event_type TEXT NOT NULL,
  payload JSONB,
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  PRIMARY KEY (id, created_at) -- Partition key MUST be part of PK
) PARTITION BY RANGE (created_at);

CREATE TABLE event_2026_01 PARTITION OF event
  FOR VALUES FROM ('2026-01-01') TO ('2026-02-01');

CREATE TABLE event_2026_02 PARTITION OF event
  FOR VALUES FROM ('2026-02-01') TO ('2026-03-01');
```

## List Partitioning

Useful for partitioning by region, tenant, or category:

```sql
-- EXAMPLE
CREATE TABLE order (
  id BIGINT GENERATED ALWAYS AS IDENTITY,
  region TEXT NOT NULL,
  total NUMERIC(10,2),
  PRIMARY KEY (id, region) -- Partition key MUST be part of PK
) PARTITION BY LIST (region);

CREATE TABLE order_us PARTITION OF order FOR VALUES IN ('us');
CREATE TABLE order_eu PARTITION OF order FOR VALUES IN ('eu');
CREATE TABLE order_default PARTITION OF order DEFAULT;  -- catches unmatched values
```

## Partition Management

- Use `pg_partman` (extension) to automate partition creation and cleanup.
- Use `DETACH PARTITION` to remove a partition while retaining it as a standalone table (e.g., for archiving).
- Use `DETACH PARTITION ... CONCURRENTLY` (PG 14+) to avoid `ACCESS EXCLUSIVE` locks on the parent table.
- Drop old partitions for data retention instead of `DELETE` to avoid vacuum overhead and bloat.
- Create future partitions ahead of time to avoid insert failures.
- **Always confirm with a human before detaching or dropping partitions.** These are destructive actions — detaching removes data from the partitioned table, and dropping permanently deletes the data.

```sql
-- DESTRUCTIVE: confirm with a human before executing
ALTER TABLE event DETACH PARTITION event_2025_01 CONCURRENTLY;
DROP TABLE event_2025_01;
```

## Guidelines & Limitations

- **Primary Keys**: Partition key columns MUST be included in the `PRIMARY KEY` and any `UNIQUE` constraints.
- **Global Uniqueness**: Global unique constraints on non-partition columns are NOT supported.
- **Indexes**: Indexes defined on the parent are automatically created on all partitions (and future ones).
- **Pruning**: Ensure queries filter by the partition key to enable "partition pruning" (skipping unrelated partitions).


================================================
FILE: .agents/skills/postgres/references/process-architecture.md
================================================
---
title: Process Architecture
description: PostgreSQL multi-process model, connection management, and auxiliary processes
tags: postgres, processes, connections, pooling, memory, operations
---

# Process Architecture

PostgreSQL uses a **multi-process** model, not multi-threaded: one OS process per client connection. The postmaster is the parent; it spawns backend processes per connection. Each backend has some private memory (`work_mem`, temp buffers). 1000 connections = 1000 processes (~5–10MB base + query memory each). There is also a large buffer shared amongst all.

## Auxiliary Processes

WAL Writer, Background Writer, Checkpointer, Autovacuum Launcher/Workers, Archiver, WAL Summarizer (PG 17+). These run alongside backends and are not spawned per connection.

## Memory Risk

`work_mem` is per-operation, not per-query. Estimate: `work_mem × operations_per_query × parallel_workers × connections` can grow very large at high concurrency. Scale connections and parallelism before raising `work_mem`.

## Connection Pooling (Critical)

Each connection = OS process (fork overhead, context switching, memory). PgBouncer can multiplex many app connections to fewer DB connections. Typical: 1000 app connections → pooler → 20–50 backends. Implement pooling before raising `max_connections`; `max_connections` requires a full restart to change (default 100). Note: `superuser_reserved_connections` (default 3) reserves slots for emergency superuser access, so non-superusers are rejected before `max_connections` is fully reached.

## Monitoring

```sql
SELECT state, count(*) FROM pg_stat_activity WHERE backend_type = 'client backend' GROUP BY state;
```

```sql
-- Show used and free connection slots
SELECT count(*) AS used, max(max_conn) - count(*) AS free
FROM pg_stat_activity, (SELECT setting::int AS max_conn FROM pg_settings WHERE name = 'max_connections') s
WHERE backend_type = 'client backend';
```

Use `pg_activity` for interactive top-like monitoring. Alert at 80% connection usage, critical at 95%. Count by state to find idle-in-transaction leaks — these hold locks and **block VACUUM** from reclaiming dead tuples.

## Common Problems

| Problem | Fix |
| ------- | --- |
| `too many clients already` | Implement pooling; find idle connections; check for connection leaks |
| High memory / OOM | Reduce `work_mem`; add pooling; set `statement_timeout` |
| Stuck process | `SELECT pg_cancel_backend(pid);` then `SELECT pg_terminate_backend(pid);` — **always confirm with a human before terminating backends**, as this may abort in-flight transactions and cause data issues for the application |

Prefer pooling + conservative `max_connections` over raising limits reactively.


================================================
FILE: .agents/skills/postgres/references/ps-cli-api-insights.md
================================================
---
title: CLI Query Insights API
description: CLI insights usage
tags: postgres, planetscale, cli, insights, query-patterns, api
---

# Query Insights via pscale CLI

Analyze slow queries and missing indexes using `pscale api`. Endpoints may change—see https://planetscale.com/docs/api/reference/getting-started-with-planetscale-api for current API docs.

## Using pscale api

The `pscale api` command makes authenticated API calls using your current login or service token (see [ps-cli-commands.md](ps-cli-commands.md#service-token-cicd) for auth setup). No need to manage auth headers manually.

```bash
pscale api "<endpoint>" [--method POST] [--field key=value] [--org <org>]
```

## Query Patterns Reports

```bash
# Create a new report
pscale api "organizations/{org}/databases/{db}/branches/{branch}/query-patterns-reports" \
  --method POST --org my-org

# Check status (poll until state=complete)
pscale api "organizations/{org}/databases/{db}/branches/{branch}/query-patterns-reports/{id}/status"

# Download completed report
pscale api "organizations/{org}/databases/{db}/branches/{branch}/query-patterns-reports/{id}"

# List all reports
pscale api "organizations/{org}/databases/{db}/branches/{branch}/query-patterns-reports"
```

## Schema Analysis

```bash
# Get branch schema
pscale api "organizations/{org}/databases/{db}/branches/{branch}/schema"

# Lint schema for issues
pscale api "organizations/{org}/databases/{db}/branches/{branch}/schema/lint"
```

## What to Look For

| Metric                           | Indicates             | Action                          |
| -------------------------------- | --------------------- | ------------------------------- |
| High `rows_read / rows_returned` | Missing or poor index | Add index on WHERE/JOIN columns |
| High `total_time_s`              | Heavy query           | Optimize or cache               |
| High `count` with same pattern   | N+1 queries           | Batch or eager-load             |
| `indexed: false`                 | Full table scan       | Add index                       |


================================================
FILE: .agents/skills/postgres/references/ps-cli-commands.md
================================================
---
title: PlanetScale CLI Reference
description: CLI command guide
tags: planetscale, cli, branches, deploy-requests, authentication
---

# pscale CLI Commands

Full CLI reference: https://planetscale.com/docs/cli. Use `pscale <command> --help` for subcommands and flags.

## Authentication

```bash
pscale auth login                    # Opens browser
pscale auth logout
pscale org list
pscale org switch <name>
```

### Service Token (CI/CD)

```bash
# Create and configure
pscale service-token create
pscale service-token add-access <id> read_branch --database <db>
# Use in CI/CD
export PLANETSCALE_SERVICE_TOKEN_ID="<id>"
export PLANETSCALE_SERVICE_TOKEN="<token>"
```

## Core Commands

```bash
# Databases
pscale database list
pscale database create <name>

# Branches
pscale branch list <db>
pscale branch create <db> <branch> [--from <parent>]
pscale branch delete <db> <branch>    # DESTRUCTIVE — always confirm with a human first
pscale branch schema <db> <branch>

# Deploy requests (schema changes) — Vitess only
pscale deploy-request create <db> <branch>
pscale deploy-request list <db>
pscale deploy-request deploy <db> <number>

# Connect
pscale shell <db> <branch>           # Opens psql (Postgres) or mysql (Vitess)
pscale connect <db> <branch>         # Proxy for GUI tools (secure tunnel) — Vitess only

# Credentials
pscale role create <db> <branch> <name>      # Postgres
pscale password create <db> <branch> <name>  # Vitess

# Other
pscale ping              # Check latency to regions
pscale region list       # Available regions
pscale backup list <db> <branch>
pscale backup create <db> <branch>
```

## Useful Flags

```bash
--format json    # Output as JSON (also: csv, human)
--org <name>     # Specify organization
--debug          # Debug output
```

For API calls via CLI, see [ps-cli-api-insights.md](ps-cli-api-insights.md).


================================================
FILE: .agents/skills/postgres/references/ps-connection-pooling.md
================================================
---
title: PgBouncer Connection Pooling
description: Pooling setup guide
tags: postgres, pgbouncer, connection-pooling, performance, transactions
---

# Connection Pooling with PgBouncer

PlanetScale provides PgBouncer for connection pooling. Connect on port `6432` instead of `5432`.

## When to Use PgBouncer (Port 6432)

All OLTP application workloads: web apps, APIs, high-concurrency read/write operations.

## When to Use Direct Connections (Port 5432)

- Schema changes (DDL)
- Analytics, reporting, batch processing
- Session-specific features (temp tables, session variables)
- ETL, data streaming, `pg_dump`
- Long-running admin transactions

## PgBouncer Types

PlanetScale offers three PgBouncer options. All use port `6432`.

| Type | Runs On | Routes To | Key Trait |
| ---- | ------- | --------- | --------- |
| **Local** | Same node as primary | Primary only | Included with every database; no replica routing |
| **Dedicated Primary** | Separate node | Primary | Connections persist through resizes, upgrades, and most failovers |
| **Dedicated Replica** | Separate node | Replicas | Read-only traffic; supports AZ affinity for lower latency |

- **Local PgBouncer** — use same credentials as direct, just change port to `6432`. Always routes to primary regardless of username.
- **Dedicated Primary** — runs off-server for improved HA. Use for production OLTP write traffic.
- **Dedicated Replica** — runs off-server for read-heavy workloads. Supports AZ affinity to prefer same-zone replicas. Multiple can be created for capacity or per-app isolation.

To connect to a dedicated PgBouncer, append `|pgbouncer-name` to the username (e.g., `postgres.xxx|write-pool` or `postgres.xxx|read-bouncer`).

## Transaction Pooling Limitations

PlanetScale PgBouncer uses **transaction pooling mode**. These features are unavailable:

- Prepared statements that persist across transactions
- Temporary tables
- `LISTEN`/`NOTIFY`
- Session-level advisory locks
- `SET` commands persisting beyond a transaction

## Recommended Patterns

- Size pools from observed concurrency, query memory behavior, and connection limits.
- Keep pooled app traffic on `6432` and reserve direct connections for DDL/admin/long-running jobs.

## Avoid Patterns

- Avoid setting pool size with only `CPU_cores * N` while ignoring query-memory amplification.
- Avoid running session-dependent workflows through transaction pooling.

## Connecting

```bash
# Local PgBouncer (same credentials, port 6432)
psql 'host=xxx.horizon.psdb.cloud port=6432 user=postgres.xxx password=pscale_pw_xxx dbname=mydb sslnegotiation=direct sslmode=verify-full sslrootcert=system'

# Dedicated primary PgBouncer (append |pgbouncer-name to user)
psql 'host=xxx.horizon.psdb.cloud port=6432 user=postgres.xxx|write-pool password=pscale_pw_xxx dbname=mydb sslnegotiation=direct sslmode=verify-full sslrootcert=system'

# Dedicated replica PgBouncer (append |pgbouncer-name to user)
psql 'host=xxx.horizon.psdb.cloud port=6432 user=postgres.xxx|read-bouncer password=pscale_pw_xxx dbname=mydb sslnegotiation=direct sslmode=verify-full sslrootcert=system'
```

Docs: https://planetscale.com/docs/postgres/connecting/pgbouncer


================================================
FILE: .agents/skills/postgres/references/ps-connections.md
================================================
---
title: PlanetScale Postgres Connections
description: Connection guide for PlanetScale Postgres
tags: planetscale, postgres, connections, ssl, troubleshooting
---

# PlanetScale Postgres Connections

Postgres docs: https://planetscale.com/docs/postgres/connecting

| Protocol | Standard Port | Pooled Port | SSL      |
| -------- | ------------- | ----------------------- | -------- |
| Postgres | 5432          | 6432 (PgBouncer)        | Required |

Credentials (roles) are branch-specific and cannot be recovered after creation.

## Connection String

```
postgresql://<user>:<password>@<host>.horizon.psdb.cloud:5432/<database>?sslmode=verify-full&sslrootcert=system&sslnegotiation=direct
```

Use port **6432** for PgBouncer (applications/OLTP).
Use port **5432** for DDL, admin tasks, and migrations.

## Troubleshooting

| Error | Fix |
| -------------------------------- | --------------------------------------- |
| `password authentication failed` | Check role format: `<role>.<branch_id>` |
| `too many clients already`       | Use PgBouncer (port 6432)               |
| `SSL connection is required`     | Add `sslmode=verify-full&sslrootcert=system` |

**Best practices:**
- Use the PlanetScale Postgres metrics page to monitor direct and PgBouncer connections
- Route OLTP traffic to port 6432 and reserve 5432 for admin/migrations.
- Avoid raising `max_connections` reactively instead of pooling.


================================================
FILE: .agents/skills/postgres/references/ps-extensions.md
================================================
---
title: PlanetScale PostgreSQL Extensions
description: Extension reference
tags: postgres, extensions
---

# PostgreSQL Extensions on PlanetScale

Only use PlanetScale-supported extensions. For the complete and up-to-date list of available extensions, see: https://planetscale.com/docs/postgres/extensions

Do not rely on hard-coded extension lists — always check the documentation above for current availability.

## Enabling Extensions

Some extensions must first be **enabled in the PlanetScale Dashboard** (Clusters > Extensions) before they can be created in SQL. This often requires a database restart.

Once enabled in the dashboard, create the extension in SQL:

```sql
CREATE EXTENSION IF NOT EXISTS <extension_name>;
```

## Recommended Patterns

- Always check the [PlanetScale extensions docs](https://planetscale.com/docs/postgres/extensions) before assuming an extension is available.
- Verify extension availability in PlanetScale configuration and docs before schema design depends on it.
- Enable `pg_stat_statements` early for baseline query telemetry.


================================================
FILE: .agents/skills/postgres/references/ps-insights.md
================================================
---
title: PlanetScale Query Insights
description: Query insights guide
tags: postgres, planetscale, insights, monitoring, optimization
---

# PlanetScale Insights

## Fetch current documentation first

Prefer retrieval over pre-training knowledge. Docs: https://planetscale.com/docs

## MCP Server (Preferred)

When the PlanetScale MCP server is configured in your environment, prefer it over CLI. Key tools:

- `planetscale_get_branch_schema` — Get schema for a branch
- `planetscale_execute_read_query` — Run SELECT, SHOW, DESCRIBE, EXPLAIN
- `planetscale_get_insights` — Query performance insights
- `planetscale_list_schema_recommendations` — Index and schema suggestions
- `planetscale_search_documentation` — Search PlanetScale docs

MCP setup: https://planetscale.com/docs/connect/mcp

The MCP server is the ideal way to interact with insights from an AI agent.
If not installed, prompt the user to install it to make the agent more effective.

## Query Insights (CLI)

Generating reports via CLI is a multi-step process (create → wait → download).

See [ps-cli-api-insights.md](https://raw.githubusercontent.com/planetscale/database-skills/main/skills/postgres/references/ps-cli-api-insights.md) for how to use.

What to look for:

- High `rows_read / rows_returned` ratio → missing index
- High `total_time_s` → optimization target

## Insights UI (Dashboard)

In the [PlanetScale dashboard](https://app.planetscale.com/), select your database and click **Insights**.

- **Filtering** — Pick a branch, choose primary or replica, and scroll through the last 7 days. Click-and-drag on graphs to zoom into a time window.
- **Graphs** — Four tabs: Query latency (p50/p95/p99/p99.9), Queries per second, Rows read/s, and Rows written/s.
- **Queries table** — All queries in the selected timeframe, normalized into patterns. Sortable and filterable by SQL, schema, table, latency, index usage, and more. Customizable columns (count, total time, latency percentiles, rows read/returned/affected, CPU/IO time, cache hit ratio, etc.). Enable sparklines for inline trend graphs. Orange icons flag full table scans.
- **Query deep dive** — Click any query to see per-pattern graphs, summary stats, index usage breakdown, and a table of notable executions (>1 s, >10k rows read, or errors). Use "Summarize query" for an LLM-generated plain-English description.
- **Anomalies tab** — Flags periods with elevated slow-running queries and surfaces the responsible patterns.
- **Errors tab** — Surfaces queries that produced errors.
- **pginsights settings** — `pginsights.raw_queries` enables full query text collection for notable queries; `pginsights.normalize_schema_names` groups identical patterns across schemas (useful for schema-per-tenant designs). Both configurable in the Extensions tab on the Clusters page.

More: [PlanetScale Insights docs](https://planetscale.com/docs/postgres/monitoring/query-insights)

## Optimization Checklist

- Remove unused indexes (0 scans)
- Remove duplicate indexes
- Archive audit/log tables >10 GB
- Review tables >100 GB for partitioning

**Always confirm with a human before removing indexes, dropping tables/partitions, or archiving data.** These are destructive actions that cannot be easily undone.

More: [optimization-checklist.md](https://raw.githubusercontent.com/planetscale/database-skills/main/skills/postgres/references/optimization-checklist.md)


================================================
FILE: .agents/skills/postgres/references/query-patterns.md
================================================
---
title: SQL Query Patterns
description: Common SQL anti-patterns and optimized alternatives
tags: postgres, sql, query-optimization, n-plus-one, pagination
---

# SQL Query Patterns

## Query Structure

**SELECT specific columns** — avoids fetching unnecessary data and enables covering indexes:
```sql
-- Bad:
SELECT * FROM user WHERE status = 'active';
-- Good:
SELECT id, name, email FROM user WHERE status = 'active';
```

**Subqueries → JOINs** — correlated subqueries re-execute per row:
```sql
-- Bad
SELECT id, (SELECT COUNT(*) FROM order WHERE order.user_id = user.id) FROM user;
-- Good
SELECT u.id, COUNT(o.id) FROM user u LEFT JOIN order o ON o.user_id = u.id GROUP BY u.id;
```

**Always LIMIT unbounded queries** — prevent runaway result sets:
```sql
SELECT id, message FROM log WHERE level = 'error' ORDER BY created_at DESC LIMIT 100;
```

**Avoid functions on indexed columns (SARGable)** — functions prevent index usage unless a functional index exists:
```sql
-- Bad: Full table scan
SELECT * FROM user WHERE date_trunc('day', created_at) = '2023-01-01';
-- Good: Index scan
SELECT * FROM user WHERE created_at >= '2023-01-01' AND created_at < '2023-01-02';
```

## N+1 Detection

**Queries inside loops → batch with ANY/IN:**
```python
# Bad
for uid in user_ids:
    cursor.execute("SELECT name FROM user WHERE id = %s", (uid,))
# Good (Postgres specific)
cursor.execute("SELECT id, name FROM user WHERE id = ANY(%s)", (list(user_ids),))
# Good (Standard SQL)
# cursor.execute("SELECT id, name FROM user WHERE id IN %s", (tuple(user_ids),))
```

**ORM lazy loading → eager loading:**
```python
# Bad: N+1 — each iteration fires a query
for user in User.query.all():
    print(user.posts)
# Good
users = User.query.options(joinedload(User.posts)).all()
```

## Query Rewrites

**UNION → UNION ALL** — skip deduplication when duplicates are impossible or acceptable.

**IN subquery → EXISTS** — EXISTS short-circuits on first match:
```sql
SELECT id, name FROM user u
WHERE EXISTS (SELECT 1 FROM order o WHERE o.user_id = u.id AND o.total > 100);
```

**OFFSET → cursor pagination** — OFFSET scans and discards rows, degrading at depth:
```sql
-- Bad: OFFSET 10000 scans 10020 rows
SELECT id, title FROM article ORDER BY created_at DESC LIMIT 20 OFFSET 10000;
-- Good: cursor-based (requires index on (created_at DESC, id DESC))
SELECT id, title FROM article
WHERE (created_at, id) < ('2025-06-15T12:00:00Z', 987654)
ORDER BY created_at DESC, id DESC LIMIT 20;
```


================================================
FILE: .agents/skills/postgres/references/replication.md
================================================
---
title: Replication
description: Streaming replication, replication slots, synchronous commit levels, failover, and standby management
tags: postgres, replication, streaming, slots, synchronous, failover, standby, operations
---

# Replication

## Streaming Replication for followers

Use physical (byte-for-byte) replication via WAL stream from primary to standbys. Standbys are read-only (hot standby); same major PG version and architecture required (same minor recommended). Without replication slots, the primary may recycle WAL before the standby receives it → standby needs full resync via `pg_basebackup`. Use replication slots to guarantee WAL retention for specific standbys.

## Replication Slots

Postgres supports Physical slots (streaming) and logical slots (logical replication). Slots prevent WAL deletion even if standby is offline — can exhaust `pg_wal/` disk. Use `max_slot_wal_keep_size` to cap retained WAL per slot. Use `idle_replication_slot_timeout` (PG 17+) to auto-invalidate idle slots. `wal_keep_size` is a simpler alternative to slots for WAL retention. Drop inactive slots immediately to prevent disk exhaustion.

Slot lag (MB behind): `SELECT slot_name, pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)/1024/1024 AS mb_behind FROM pg_replication_slots;`

Drop inactive slot: `SELECT pg_drop_replication_slot('slot_name');`

**Always confirm with a human before dropping replication slots.** Dropping an active or needed slot can cause downstream issues.

## Synchronous Commit Levels

| Level | Behavior | Use Case |
|-------|----------|----------|
| `off` | Returns immediately, no wait | Non-critical writes; risks losing ~600ms of commits on crash (no inconsistency) |
| `local` | Waits for local WAL fsync only | Local durability only; no standby wait |
| `remote_write` | Waits for standby OS buffer | Data loss on standby OS crash |
| `on` | Waits for standby WAL to disk when `synchronous_standby_names` is set; otherwise same as `local` | **Default. This level or higher recommended for HA** |
| `remote_apply` | Waits for standby to apply WAL | Strongest; read-your-writes |

Configure with `synchronous_standby_names`. Use `ANY N` for quorum or `FIRST N` for priority-based sync.

## Quorum and Failure

`FIRST 2 (s1, s2, s3)` is priority-based: waits for the 2 highest-priority connected standbys (s1+s2; s3 takes over only if one disconnects). `ANY 2 (s1, s2, s3)` is quorum-based: waits for any 2. With either, if only 1 is healthy, commits hang. Provision at least N+1 standbys: need 2 confirmations → provision 3. PostgreSQL never commits unless required standbys confirm — no inconsistency, but clients may timeout.

## Failover

`pg_ctl promote` or `SELECT pg_promote()` (SQL function, PG 12+) converts standby to primary. One-way: promoted standby cannot rejoin as standby without rebuild. `pg_rewind` can resync old primary to new primary (requires `wal_log_hints=on` or data checksums) — faster than full rebuild. After promotion: update connection strings, rebuild old primary as standby, reconfigure other standbys.

## Monitoring

On the primary, query `pg_stat_replication` for each connected standby's `state` (`streaming` = healthy, `catchup` = behind), `sync_state` (`sync`/`async`), and LSN positions (`sent_lsn`, `write_lsn`, `flush_lsn`, `replay_lsn`) to compute lag. On standbys, `pg_stat_wal_receiver` shows the receiver process status and `flushed_lsn`; compare `pg_last_wal_receive_lsn()` vs `pg_last_wal_replay_lsn()` for local replay lag.

Replication lag (MB): `SELECT application_name, pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn)/1024/1024 AS lag_mb FROM pg_stat_replication;`

Enable `wal_compression` (`pglz`, `lz4`, or `zstd`) to compress full page images in WAL (not all WAL data) — reduces WAL size for bandwidth-limited replication.


================================================
FILE: .agents/skills/postgres/references/schema-design.md
================================================
---
title: PostgreSQL Schema Design
description: Schema design guide
tags: postgres, schema, primary-keys, data-types, foreign-keys, naming
---

# Schema Design

## Primary Keys

Prefer `BIGINT GENERATED ALWAYS AS IDENTITY`. Avoid random UUIDs (UUIDv4) as primary keys; use `uuidv7()` when you need UUIDs.

```sql
CREATE TABLE user (
  id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  email TEXT NOT NULL UNIQUE
);
```

Random UUID PKs (v4) can cause index fragmentation; UUIDs are also larger (16 vs 8 bytes for BIGINT) and can slow joins.

## Data Types

| Use | Avoid |
| --- | --- |
| `TEXT`, `VARCHAR` | Extension-specific types |
| `JSONB` | Custom ENUMs (use CHECK instead) |
| `TIMESTAMPTZ` | `TIMESTAMP` without time zone |
| `BIGINT`, `INTEGER` | Platform-specific types |

Prefer CHECK constraints over ENUM types — they're easier to modify:

```sql
CREATE TABLE order (
  id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  status TEXT NOT NULL CHECK (status IN ('pending', 'shipped', 'delivered'))
);
```

## Foreign Keys

- Always index FK columns (PostgreSQL does not auto-create these)
- Avoid circular FK dependencies
- Suggestion: use `ON DELETE CASCADE` or `ON DELETE SET NULL` explicitly

```sql
CREATE TABLE order (
  id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  customer_id BIGINT NOT NULL REFERENCES customer(id) ON DELETE CASCADE
);
CREATE INDEX order_customer_id_idx ON order (customer_id);
```

## Naming Conventions

- Tables: singular snake_case (`user_account`, `order_item`)
- Columns: singular snake_case (`created_at`, `user_id`)
- Indexes: `{table}_{column}_idx`
- Constraints: `{table}_{column}_{type}` (e.g., `order_status_check`)

## General Guidelines

- Add `NOT NULL` to as many columns as possible
- Add `created_at TIMESTAMPTZ DEFAULT NOW()` to all tables
- Use `BIGINT` for all IDs and foreign keys, even on small tables
- Keep tables normalized; denormalize only for proven hot read paths


================================================
FILE: .agents/skills/postgres/references/storage-layout.md
================================================
---
title: Storage Layout and Tablespaces
description: PGDATA directory structure, TOAST, fillfactor, tablespaces, and disk management
tags: postgres, storage, pgdata, toast, fillfactor, tablespaces, disk, operations
---

# Storage Layout and Tablespaces

## PGDATA Structure

- **base/** — database files (one subdirectory per database, named by OID)
- **global/** — cluster-wide shared catalogs (pg_database, pg_authid, pg_tablespace)
- **pg_wal/** — WAL files
- **pg_xact/** — transaction commit status

"Cluster" in PostgreSQL = single instance with one PGDATA, not an HA cluster. Each table/index = one or more files, split into 1GB segments. Tables have companion **_fsm** (free space map) and **_vm** (visibility map); indexes have **_fsm** only (no _vm), except hash indexes.

## Visibility Map and Free Space Map

- **_vm** tracks all-visible pages — VACUUM skips these
- **_fsm** tracks free space per page — INSERT uses this to find pages with room
- Both are small files but critical for performance

## TOAST

TOAST triggers when a **row** exceeds ~2KB. Large values are compressed and/or moved out-of-line to `pg_toast.pg_toast_<oid>` tables. **Strategies:** PLAIN (no TOAST), EXTENDED (compress+out-of-line, default for text/bytea), EXTERNAL (out-of-line, no compression — use for pre-compressed data), MAIN (compress, avoid out-of-line). TOAST tables bloat like regular tables — they need VACUUM. `SELECT *` fetches all TOAST columns; always SELECT only needed columns. Move large rarely-accessed columns to separate tables.

## Fillfactor

Controls how full pages are packed (default 100%). Lower fillfactor (70–80%) leaves room for HOT (Heap-Only Tuple) updates, which avoid index entries and reduce bloat on UPDATE-heavy tables. Keep 100% for insert-only or read-mostly tables. `ALTER TABLE t SET (fillfactor = 70);`

## Tablespaces

`pg_default` (base/), `pg_global` (global/) are built-in. Custom tablespaces: symbolic links in **pg_tblspc/** to other filesystem locations. Use for separating hot data (SSD) from archives (HDD). Moving tablespaces requires exclusive lock on affected tables.

## Disk Monitoring

- `pg_database_size('dbname')`, `pg_total_relation_size('tablename')`, `pg_relation_size('tablename')`
- Monitor disk usage: >80% = at risk; >90% = critical (VACUUM may fail if disk capacity is insufficient)
- Check inode usage (`df -i`) — can run out even with free space
- `pg_wal/` suddenly large = check replication slots and archiving


================================================
FILE: .agents/skills/postgres/references/wal-operations.md
================================================
---
title: WAL and Checkpoint Operations
description: Write-ahead log internals, checkpoint tuning, durability guarantees, and WAL disk management
tags: postgres, wal, checkpoints, durability, crash-recovery, fsync, operations
---

# WAL and Checkpoint Operations

## WAL Fundamentals

Write-Ahead Logging: logs changes to `pg_wal/` **before** modifying data files. WAL segments are 16MB (fixed at initdb). On COMMIT, PostgreSQL fsyncs WAL to disk and returns SUCCESS — data files are updated lazily. WAL records are written for all changes (including uncommitted transactions and rollbacks). **Never disable `fsync` in production** — power loss without fsync risks unrecoverable data loss.

`wal_level`: `minimal` (crash recovery only), `replica` (default; replication + archiving), `logical` (logical replication).

## Dirty Pages and Checkpoints

A dirty page is modified in shared_buffers but not yet written to data files. A checkpoint flushes all dirty pages to disk and writes a checkpoint record to WAL; recovery only replays WAL since the last checkpoint.

- `checkpoint_timeout` (default 5 min) and `max_wal_size` (default 1GB) — checkpoint on whichever triggers first.
- `checkpoint_completion_target=0.9` spreads I/O over 90% of the interval; avoid spikes.
- "Checkpoints are occurring too frequently" in logs → increase `max_wal_size`.
- **Target: >90% of checkpoints should be time-based** (`num_timed` in `pg_stat_checkpointer`), not size-based (`num_requested`). If num_requested/(num_timed+num_requested) > 10%, tune `max_wal_size` up.

## WAL Disk Management

Replication slots prevent WAL deletion even when standbys are offline — they can fill disk. WAL archiving failures also block recycling. `max_wal_size` is a *soft* limit; WAL can grow beyond it under heavy load.

WAL size: `SELECT count(*) AS files, pg_size_pretty(sum(size)) AS total FROM pg_ls_waldir();`

Slot lag: `SELECT slot_name, pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn) AS lag_bytes FROM pg_replication_slots;`

## Checkpoint Monitoring

PG17+ moved checkpoint stats from `pg_stat_bgwriter` to `pg_stat_checkpointer` and renamed columns.

`SELECT num_timed, num_requested, write_time, sync_time, buffers_written FROM pg_stat_checkpointer;`

Backend-direct writes (formerly `buffers_backend` in `pg_stat_bgwriter`) are now tracked in `pg_stat_io`: `SELECT writes FROM pg_stat_io WHERE backend_type = 'client backend' AND object = 'relation';`

## Crash Recovery

On crash, PostgreSQL replays WAL from the last checkpoint. Longer checkpoint intervals → more WAL to replay → longer recovery. Trade-off: frequent checkpoints (faster recovery, more I/O) vs infrequent (less I/O, slower recovery). For most workloads, `checkpoint_timeout=5min` and `max_wal_size` tuned to keep checkpoints time-based is the right balance.


================================================
FILE: .agents/skills/vercel-react-best-practices/AGENTS.md
================================================
# React Best Practices

**Version 1.0.0**  
Vercel Engineering  
January 2026

> **Note:**  
> This document is mainly for agents and LLMs to follow when maintaining,  
> generating, or refactoring React and Next.js codebases. Humans  
> may also find it useful, but guidance here is optimized for automation  
> and consistency by AI-assisted workflows.

---

## Abstract

Comprehensive performance optimization guide for React and Next.js applications, designed for AI agents and LLMs. Contains 40+ rules across 8 categories, prioritized by impact from critical (eliminating waterfalls, reducing bundle size) to incremental (advanced patterns). Each rule includes detailed explanations, real-world examples comparing incorrect vs. correct implementations, and specific impact metrics to guide automated refactoring and code generation.

---

## Table of Contents

1. [Eliminating Waterfalls](#1-eliminating-waterfalls) — **CRITICAL**
   - 1.1 [Defer Await Until Needed](#11-defer-await-until-needed)
   - 1.2 [Dependency-Based Parallelization](#12-dependency-based-parallelization)
   - 1.3 [Prevent Waterfall Chains in API Routes](#13-prevent-waterfall-chains-in-api-routes)
   - 1.4 [Promise.all() for Independent Operations](#14-promiseall-for-independent-operations)
   - 1.5 [Strategic Suspense Boundaries](#15-strategic-suspense-boundaries)
2. [Bundle Size Optimization](#2-bundle-size-optimization) — **CRITICAL**
   - 2.1 [Avoid Barrel File Imports](#21-avoid-barrel-file-imports)
   - 2.2 [Conditional Module Loading](#22-conditional-module-loading)
   - 2.3 [Defer Non-Critical Third-Party Libraries](#23-defer-non-critical-third-party-libraries)
   - 2.4 [Dynamic Imports for Heavy Components](#24-dynamic-imports-for-heavy-components)
   - 2.5 [Preload Based on User Intent](#25-preload-based-on-user-intent)
3. [Server-Side Performance](#3-server-side-performance) — **HIGH**
   - 3.1 [Authenticate Server Actions Like API Routes](#31-authenticate-server-actions-like-api-routes)
   - 3.2 [Avoid Duplicate Serialization in RSC Props](#32-avoid-duplicate-serialization-in-rsc-props)
   - 3.3 [Cross-Request LRU Caching](#33-cross-request-lru-caching)
   - 3.4 [Minimize Serialization at RSC Boundaries](#34-minimize-serialization-at-rsc-boundaries)
   - 3.5 [Parallel Data Fetching with Component Composition](#35-parallel-data-fetching-with-component-composition)
   - 3.6 [Per-Request Deduplication with React.cache()](#36-per-request-deduplication-with-reactcache)
   - 3.7 [Use after() for Non-Blocking Operations](#37-use-after-for-non-blocking-operations)
4. [Client-Side Data Fetching](#4-client-side-data-fetching) — **MEDIUM-HIGH**
   - 4.1 [Deduplicate Global Event Listeners](#41-deduplicate-global-event-listeners)
   - 4.2 [Use Passive Event Listeners for Scrolling Performance](#42-use-passive-event-listeners-for-scrolling-performance)
   - 4.3 [Use SWR for Automatic Deduplication](#43-use-swr-for-automatic-deduplication)
   - 4.4 [Version and Minimize localStorage Data](#44-version-and-minimize-localstorage-data)
5. [Re-render Optimization](#5-re-render-optimization) — **MEDIUM**
   - 5.1 [Calculate Derived State During Rendering](#51-calculate-derived-state-during-rendering)
   - 5.2 [Defer State Reads to Usage Point](#52-defer-state-reads-to-usage-point)
   - 5.3 [Do not wrap a simple expression with a primitive result type in useMemo](#53-do-not-wrap-a-simple-expression-with-a-primitive-result-type-in-usememo)
   - 5.4 [Extract Default Non-primitive Parameter Value from Memoized Component to Constant](#54-extract-default-non-primitive-parameter-value-from-memoized-component-to-constant)
   - 5.5 [Extract to Memoized Components](#55-extract-to-memoized-components)
   - 5.6 [Narrow Effect Dependencies](#56-narrow-effect-dependencies)
   - 5.7 [Put Interaction Logic in Event Handlers](#57-put-interaction-logic-in-event-handlers)
   - 5.8 [Subscribe to Derived State](#58-subscribe-to-derived-state)
   - 5.9 [Use Functional setState Updates](#59-use-functional-setstate-updates)
   - 5.10 [Use Lazy State Initialization](#510-use-lazy-state-initialization)
   - 5.11 [Use Transitions for Non-Urgent Updates](#511-use-transitions-for-non-urgent-updates)
   - 5.12 [Use useRef for Transient Values](#512-use-useref-for-transient-values)
6. [Rendering Performance](#6-rendering-performance) — **MEDIUM**
   - 6.1 [Animate SVG Wrapper Instead of SVG Element](#61-animate-svg-wrapper-instead-of-svg-element)
   - 6.2 [CSS content-visibility for Long Lists](#62-css-content-visibility-for-long-lists)
   - 6.3 [Hoist Static JSX Elements](#63-hoist-static-jsx-elements)
   - 6.4 [Optimize SVG Precision](#64-optimize-svg-precision)
   - 6.5 [Prevent Hydration Mismatch Without Flickering](#65-prevent-hydration-mismatch-without-flickering)
   - 6.6 [Suppress Expected Hydration Mismatches](#66-suppress-expected-hydration-mismatches)
   - 6.7 [Use Activity Component for Show/Hide](#67-use-activity-component-for-showhide)
   - 6.8 [Use Explicit Conditional Rendering](#68-use-explicit-conditional-rendering)
   - 6.9 [Use useTransition Over Manual Loading States](#69-use-usetransition-over-manual-loading-states)
7. [JavaScript Performance](#7-javascript-performance) — **LOW-MEDIUM**
   - 7.1 [Avoid Layout Thrashing](#71-avoid-layout-thrashing)
   - 7.2 [Build Index Maps for Repeated Lookups](#72-build-index-maps-for-repeated-lookups)
   - 7.3 [Cache Property Access in Loops](#73-cache-property-access-in-loops)
   - 7.4 [Cache Repeated Function Calls](#74-cache-repeated-function-calls)
   - 7.5 [Cache Storage API Calls](#75-cache-storage-api-calls)
   - 7.6 [Combine Multiple Array Iterations](#76-combine-multiple-array-iterations)
   - 7.7 [Early Length Check for Array Comparisons](#77-early-length-check-for-array-comparisons)
   - 7.8 [Early Return from Functions](#78-early-return-from-functions)
   - 7.9 [Hoist RegExp Creation](#79-hoist-regexp-creation)
   - 7.10 [Use Loop for Min/Max Instead of Sort](#710-use-loop-for-minmax-instead-of-sort)
   - 7.11 [Use Set/Map for O(1) Lookups](#711-use-setmap-for-o1-lookups)
   - 7.12 [Use toSorted() Instead of sort() for Immutability](#712-use-tosorted-instead-of-sort-for-immutability)
8. [Advanced Patterns](#8-advanced-patterns) — **LOW**
   - 8.1 [Initialize App Once, Not Per Mount](#81-initialize-app-once-not-per-mount)
   - 8.2 [Store Event Handlers in Refs](#82-store-event-handlers-in-refs)
   - 8.3 [useEffectEvent for Stable Callback Refs](#83-useeffectevent-for-stable-callback-refs)

---

## 1. Eliminating Waterfalls

**Impact: CRITICAL**

Waterfalls are the #1 performance killer. Each sequential await adds full network latency. Eliminating them yields the largest gains.

### 1.1 Defer Await Until Needed

**Impact: HIGH (avoids blocking unused code paths)**

Move `await` operations into the branches where they're actually used to avoid blocking code paths that don't need them.

**Incorrect: blocks both branches**

```typescript
async function handleRequest(userId: string, skipProcessing: boolean) {
  const userData = await fetchUserData(userId)
  
  if (skipProcessing) {
    // Returns immediately but still waited for userData
    return { skipped: true }
  }
  
  // Only this branch uses userData
  return processUserData(userData)
}
```

**Correct: only blocks when needed**

```typescript
async function handleRequest(userId: string, skipProcessing: boolean) {
  if (skipProcessing) {
    // Returns immediately without waiting
    return { skipped: true }
  }
  
  // Fetch only when needed
  const userData = await fetchUserData(userId)
  return processUserData(userData)
}
```

**Another example: early return optimization**

```typescript
// Incorrect: always fetches permissions
async function updateResource(resourceId: string, userId: string) {
  const permissions = await fetchPermissions(userId)
  const resource = await getResource(resourceId)
  
  if (!resource) {
    return { error: 'Not found' }
  }
  
  if (!permissions.canEdit) {
    return { error: 'Forbidden' }
  }
  
  return await updateResourceData(resource, permissions)
}

// Correct: fetches only when needed
async function updateResource(resourceId: string, userId: string) {
  const resource = await getResource(resourceId)
  
  if (!resource) {
    return { error: 'Not found' }
  }
  
  const permissions = await fetchPermissions(userId)
  
  if (!permissions.canEdit) {
    return { error: 'Forbidden' }
  }
  
  return await updateResourceData(resource, permissions)
}
```

This optimization is especially valuable when the skipped branch is frequently taken, or when the deferred operation is expensive.

### 1.2 Dependency-Based Parallelization

**Impact: CRITICAL (2-10× improvement)**

For operations with partial dependencies, use `better-all` to maximize parallelism. It automatically starts each task at the earliest possible moment.

**Incorrect: profile waits for config unnecessarily**

```typescript
const [user, config] = await Promise.all([
  fetchUser(),
  fetchConfig()
])
const profile = await fetchProfile(user.id)
```

**Correct: config and profile run in parallel**

```typescript
import { all } from 'better-all'

const { user, config, profile } = await all({
  async user() { return fetchUser() },
  async config() { return fetchConfig() },
  async profile() {
    return fetchProfile((await this.$.user).id)
  }
})
```

**Alternative without extra dependencies:**

```typescript
const userPromise = fetchUser()
const profilePromise = userPromise.then(user => fetchProfile(user.id))

const [user, config, profile] = await Promise.all([
  userPromise,
  fetchConfig(),
  profilePromise
])
```

We can also create all the promises first, and do `Promise.all()` at the end.

Reference: [https://github.com/shuding/better-all](https://github.com/shuding/better-all)

### 1.3 Prevent Waterfall Chains in API Routes

**Impact: CRITICAL (2-10× improvement)**

In API routes and Server Actions, start independent operations immediately, even if you don't await them yet.

**Incorrect: config waits for auth, data waits for both**

```typescript
export async function GET(request: Request) {
  const session = await auth()
  const config = await fetchConfig()
  const data = await fetchData(session.user.id)
  return Response.json({ data, config })
}
```

**Correct: auth and config start immediately**

```typescript
export async function GET(request: Request) {
  const sessionPromise = auth()
  const configPromise = fetchConfig()
  const session = await sessionPromise
  const [config, data] = await Promise.all([
    configPromise,
    fetchData(session.user.id)
  ])
  return Response.json({ data, config })
}
```

For operations with more complex dependency chains, use `better-all` to automatically maximize parallelism (see Dependency-Based Parallelization).

### 1.4 Promise.all() for Independent Operations

**Impact: CRITICAL (2-10× improvement)**

When async operations have no interdependencies, execute them concurrently using `Promise.all()`.

**Incorrect: sequential execution, 3 round trips**

```typescript
const user = await fetchUser()
const posts = await fetchPosts()
const comments = await fetchComments()
```

**Correct: parallel execution, 1 round trip**

```typescript
const [user, posts, comments] = await Promise.all([
  fetchUser(),
  fetchPosts(),
  fetchComments()
])
```

### 1.5 Strategic Suspense Boundaries

**Impact: HIGH (faster initial paint)**

Instead of awaiting data in async components before returning JSX, use Suspense boundaries to show the wrapper UI faster while data loads.

**Incorrect: wrapper blocked by data fetching**

```tsx
async function Page() {
  const data = await fetchData() // Blocks entire page
  
  return (
    <div>
      <div>Sidebar</div>
      <div>Header</div>
      <div>
        <DataDisplay data={data} />
      </div>
      <div>Footer</div>
    </div>
  )
}
```

The entire layout waits for data even though only the middle section needs it.

**Correct: wrapper shows immediately, data streams in**

```tsx
function Page() {
  return (
    <div>
      <div>Sidebar</div>
      <div>Header</div>
      <div>
        <Suspense fallback={<Skeleton />}>
          <DataDisplay />
        </Suspense>
      </div>
      <div>Footer</div>
    </div>
  )
}

async function DataDisplay() {
  const data = await fetchData() // Only blocks this component
  return <div>{data.content}</div>
}
```

Sidebar, Header, and Footer render immediately. Only DataDisplay waits for data.

**Alternative: share promise across components**

```tsx
function Page() {
  // Start fetch immediately, but don't await
  const dataPromise = fetchData()
  
  return (
    <div>
      <div>Sidebar</div>
      <div>Header</div>
      <Suspense fallback={<Skeleton />}>
        <DataDisplay dataPromise={dataPromise} />
        <DataSummary dataPromise={dataPromise} />
      </Suspense>
      <div>Footer</div>
    </div>
  )
}

function DataDisplay({ dataPromise }: { dataPromise: Promise<Data> }) {
  const data = use(dataPromise) // Unwraps the promise
  return <div>{data.content}</div>
}

function DataSummary({ dataPromise }: { dataPromise: Promise<Data> }) {
  const data = use(dataPromise) // Reuses the same promise
  return <div>{data.summary}</div>
}
```

Both components share the same promise, so only one fetch occurs. Layout renders immediately while both components wait together.

**When NOT to use this pattern:**

- Critical data needed for layout decisions (affects positioning)

- SEO-critical content above the fold

- Small, fast queries where suspense overhead isn't worth it

- When you want to avoid layout shift (loading → content jump)

**Trade-off:** Faster initial paint vs potential layout shift. Choose based on your UX priorities.

---

## 2. Bundle Size Optimization

**Impact: CRITICAL**

Reducing initial bundle size improves Time to Interactive and Largest Contentful Paint.

### 2.1 Avoid Barrel File Imports

**Impact: CRITICAL (200-800ms import cost, slow builds)**

Import directly from source files instead of barrel files to avoid loading thousands of unused modules. **Barrel files** are entry points that re-export multiple modules (e.g., `index.js` that does `export * from './module'`).

Popular icon and component libraries can have **up to 10,000 re-exports** in their entry file. For many React packages, **it takes 200-800ms just to import them**, affecting both development speed and production cold starts.

**Why tree-shaking doesn't help:** When a library is marked as external (not bundled), the bundler can't optimize it. If you bundle it to enable tree-shaking, builds become substantially slower analyzing the entire module graph.

**Incorrect: imports entire library**

```tsx
import { Check, X, Menu } from 'lucide-react'
// Loads 1,583 modules, takes ~2.8s extra in dev
// Runtime cost: 200-800ms on every cold start

import { Button, TextField } from '@mui/material'
// Loads 2,225 modules, takes ~4.2s extra in dev
```

**Correct: imports only what you need**

```tsx
import Check from 'lucide-react/dist/esm/icons/check'
import X from 'lucide-react/dist/esm/icons/x'
import Menu from 'lucide-react/dist/esm/icons/menu'
// Loads only 3 modules (~2KB vs ~1MB)

import Button from '@mui/material/Button'
import TextField from '@mui/material/TextField'
// Loads only what you use
```

**Alternative: Next.js 13.5+**

```js
// next.config.js - use optimizePackageImports
module.exports = {
  experimental: {
    optimizePackageImports: ['lucide-react', '@mui/material']
  }
}

// Then you can keep the ergonomic barrel imports:
import { Check, X, Menu } from 'lucide-react'
// Automatically transformed to direct imports at build time
```

Direct imports provide 15-70% faster dev boot, 28% faster builds, 40% faster cold starts, and significantly faster HMR.

Libraries commonly affected: `lucide-react`, `@mui/material`, `@mui/icons-material`, `@tabler/icons-react`, `react-icons`, `@headlessui/react`, `@radix-ui/react-*`, `lodash`, `ramda`, `date-fns`, `rxjs`, `react-use`.

Reference: [https://vercel.com/blog/how-we-optimized-package-imports-in-next-js](https://vercel.com/blog/how-we-optimized-package-imports-in-next-js)

### 2.2 Conditional Module Loading

**Impact: HIGH (loads large data only when needed)**

Load large data or modules only when a feature is activated.

**Example: lazy-load animation frames**

```tsx
function AnimationPlayer({ enabled, setEnabled }: { enabled: boolean; setEnabled: React.Dispatch<React.SetStateAction<boolean>> }) {
  const [frames, setFrames] = useState<Frame[] | null>(null)

  useEffect(() => {
    if (enabled && !frames && typeof window !== 'undefined') {
      import('./animation-frames.js')
        .then(mod => setFrames(mod.frames))
        .catch(() => setEnabled(false))
    }
  }, [enabled, frames, setEnabled])

  if (!frames) return <Skeleton />
  return <Canvas frames={frames} />
}
```

The `typeof window !== 'undefined'` check prevents bundling this module for SSR, optimizing server bundle size and build speed.

### 2.3 Defer Non-Critical Third-Party Libraries

**Impact: MEDIUM (loads after hydration)**

Analytics, logging, and error tracking don't block user interaction. Load them after hydration.

**Incorrect: blocks initial bundle**

```tsx
import { Analytics } from '@vercel/analytics/react'

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        {children}
        <Analytics />
      </body>
    </html>
  )
}
```

**Correct: loads after hydration**

```tsx
import dynamic from 'next/dynamic'

const Analytics = dynamic(
  () => import('@vercel/analytics/react').then(m => m.Analytics),
  { ssr: false }
)

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        {children}
        <Analytics />
      </body>
    </html>
  )
}
```

### 2.4 Dynamic Imports for Heavy Components

**Impact: CRITICAL (directly affects TTI and LCP)**

Use `next/dynamic` to lazy-load large components not needed on initial render.

**Incorrect: Monaco bundles with main chunk ~300KB**

```tsx
import { MonacoEditor } from './monaco-editor'

function CodePanel({ code }: { code: string }) {
  return <MonacoEditor value={code} />
}
```

**Correct: Monaco loads on demand**

```tsx
import dynamic from 'next/dynamic'

const MonacoEditor = dynamic(
  () => import('./monaco-editor').then(m => m.MonacoEditor),
  { ssr: false }
)

function CodePanel({ code }: { code: string }) {
  return <MonacoEditor value={code} />
}
```

### 2.5 Preload Based on User Intent

**Impact: MEDIUM (reduces perceived latency)**

Preload heavy bundles before they're needed to reduce perceived latency.

**Example: preload on hover/focus**

```tsx
function EditorButton({ onClick }: { onClick: () => void }) {
  const preload = () => {
    if (typeof window !== 'undefined') {
      void import('./monaco-editor')
    }
  }

  return (
    <button
      onMouseEnter={preload}
      onFocus={preload}
      onClick={onClick}
    >
      Open Editor
    </button>
  )
}
```

**Example: preload when feature flag is enabled**

```tsx
function FlagsProvider({ children, flags }: Props) {
  useEffect(() => {
    if (flags.editorEnabled && typeof window !== 'undefined') {
      void import('./monaco-editor').then(mod => mod.init())
    }
  }, [flags.editorEnabled])

  return <FlagsContext.Provider value={flags}>
    {children}
  </FlagsContext.Provider>
}
```

The `typeof window !== 'undefined'` check prevents bundling preloaded modules for SSR, optimizing server bundle size and build speed.

---

## 3. Server-Side Performance

**Impact: HIGH**

Optimizing server-side rendering and data fetching eliminates server-side waterfalls and reduces response times.

### 3.1 Authenticate Server Actions Like API Routes

**Impact: CRITICAL (prevents unauthorized access to server mutations)**

Server Actions (functions with `"use server"`) are exposed as public endpoints, just like API routes. Always verify authentication and authorization **inside** each Server Action—do not rely solely on middleware, layout guards, or page-level checks, as Server Actions can be invoked directly.

Next.js documentation explicitly states: "Treat Server Actions with the same security considerations as public-facing API endpoints, and verify if the user is allowed to perform a mutation."

**Incorrect: no authentication check**

```typescript
'use server'

export async function deleteUser(userId: string) {
  // Anyone can call this! No auth check
  await db.user.delete({ where: { id: userId } })
  return { success: true }
}
```

**Correct: authentication inside the action**

```typescript
'use server'

import { verifySession } from '@/lib/auth'
import { unauthorized } from '@/lib/errors'

export async function deleteUser(userId: string) {
  // Always check auth inside the action
  const session = await verifySession()
  
  if (!session) {
    throw unauthorized('Must be logged in')
  }
  
  // Check authorization too
  if (session.user.role !== 'admin' && session.user.id !== userId) {
    throw unauthorized('Cannot delete other users')
  }
  
  await db.user.delete({ where: { id: userId } })
  return { success: true }
}
```

**With input validation:**

```typescript
'use server'

import { verifySession } from '@/lib/auth'
import { z } from 'zod'

const updateProfileSchema = z.object({
  userId: z.string().uuid(),
  name: z.string().min(1).max(100),
  email: z.string().email()
})

export async function updateProfile(data: unknown) {
  // Validate input first
  const validated = updateProfileSchema.parse(data)
  
  // Then authenticate
  const session = await verifySession()
  if (!session) {
    throw new Error('Unauthorized')
  }
  
  // Then authorize
  if (session.user.id !== validated.userId) {
    throw new Error('Can only update own profile')
  }
  
  // Finally perform the mutation
  await db.user.update({
    where: { id: validated.userId },
    data: {
      name: validated.name,
      email: validated.email
    }
  })
  
  return { success: true }
}
```

Reference: [https://nextjs.org/docs/app/guides/authentication](https://nextjs.org/docs/app/guides/authentication)

### 3.2 Avoid Duplicate Serialization in RSC Props

**Impact: LOW (reduces network payload by avoiding duplicate serialization)**

RSC→client serialization deduplicates by object reference, not value. Same reference = serialized once; new reference = serialized again. Do transformations (`.toSorted()`, `.filter()`, `.map()`) in client, not server.

**Incorrect: duplicates array**

```tsx
// RSC: sends 6 strings (2 arrays × 3 items)
<ClientList usernames={usernames} usernamesOrdered={usernames.toSorted()} />
```

**Correct: sends 3 strings**

```tsx
// RSC: send once
<ClientList usernames={usernames} />

// Client: transform there
'use client'
const sorted = useMemo(() => [...usernames].sort(), [usernames])
```

**Nested deduplication behavior:**

```tsx
// string[] - duplicates everything
usernames={['a','b']} sorted={usernames.toSorted()} // sends 4 strings

// object[] - duplicates array structure only
users={[{id:1},{id:2}]} sorted={users.toSorted()} // sends 2 arrays + 2 unique objects (not 4)
```

Deduplication works recursively. Impact varies by data type:

- `string[]`, `number[]`, `boolean[]`: **HIGH impact** - array + all primitives fully duplicated

- `object[]`: **LOW impact** - array duplicated, but nested objects deduplicated by reference

**Operations breaking deduplication: create new references**

- Arrays: `.toSorted()`, `.filter()`, `.map()`, `.slice()`, `[...arr]`

- Objects: `{...obj}`, `Object.assign()`, `structuredClone()`, `JSON.parse(JSON.stringify())`

**More examples:**

```tsx
// ❌ Bad
<C users={users} active={users.filter(u => u.active)} />
<C product={product} productName={product.name} />

// ✅ Good
<C users={users} />
<C product={product} />
// Do filtering/destructuring in client
```

**Exception:** Pass derived data when transformation is expensive or client doesn't need original.

### 3.3 Cross-Request LRU Caching

**Impact: HIGH (caches across requests)**

`React.cache()` only works within one request. For data shared across sequential requests (user clicks button A then button B), use an LRU cache.

**Implementation:**

```typescript
import { LRUCache } from 'lru-cache'

const cache = new LRUCache<string, any>({
  max: 1000,
  ttl: 5 * 60 * 1000  // 5 minutes
})

export async function getUser(id: string) {
  const cached = cache.get(id)
  if (cached) return cached

  const user = await db.user.findUnique({ where: { id } })
  cache.set(id, user)
  return user
}

// Request 1: DB query, result cached
// Request 2: cache hit, no DB query
```

Use when sequential user actions hit multiple endpoints needing the same data within seconds.

**With Vercel's [Fluid Compute](https://vercel.com/docs/fluid-compute):** LRU caching is especially effective because multiple concurrent requests can share the same function instance and cache. This means the cache persists across requests without needing external storage like Redis.

**In traditional serverless:** Each invocation runs in isolation, so consider Redis for cross-process caching.

Reference: [https://github.com/isaacs/node-lru-cache](https://github.com/isaacs/node-lru-cache)

### 3.4 Minimize Serialization at RSC Boundaries

**Impact: HIGH (reduces data transfer size)**

The React Server/Client boundary serializes all object properties into strings and embeds them in the HTML response and subsequent RSC requests. This serialized data directly impacts page weight and load time, so **size matters a lot**. Only pass fields that the client actually uses.

**Incorrect: serializes all 50 fields**

```tsx
async function Page() {
  const user = await fetchUser()  // 50 fields
  return <Profile user={user} />
}

'use client'
function Profile({ user }: { user: User }) {
  return <div>{user.name}</div>  // uses 1 field
}
```

**Correct: serializes only 1 field**

```tsx
async function Page() {
  const user = await fetchUser()
  return <Profile name={user.name} />
}

'use client'
function Profile({ name }: { name: string }) {
  return <div>{name}</div>
}
```

### 3.5 Parallel Data Fetching with Component Composition

**Impact: CRITICAL (eliminates server-side waterfalls)**

React Server Components execute sequentially within a tree. Restructure with composition to parallelize data fetching.

**Incorrect: Sidebar waits for Page's fetch to complete**

```tsx
export default async function Page() {
  const header = await fetchHeader()
  return (
    <div>
      <div>{header}</div>
      <Sidebar />
    </div>
  )
}

async function Sidebar() {
  const items = await fetchSidebarItems()
  return <nav>{items.map(renderItem)}</nav>
}
```

**Correct: both fetch simultaneously**

```tsx
async function Header() {
  const data = await fetchHeader()
  return <div>{data}</div>
}

async function Sidebar() {
  const items = await fetchSidebarItems()
  return <nav>{items.map(renderItem)}</nav>
}

export default function Page() {
  return (
    <div>
      <Header />
      <Sidebar />
    </div>
  )
}
```

**Alternative with children prop:**

```tsx
async function Header() {
  const data = await fetchHeader()
  return <div>{data}</div>
}

async function Sidebar() {
  const items = await fetchSidebarItems()
  return <nav>{items.map(renderItem)}</nav>
}

function Layout({ children }: { children: ReactNode }) {
  return (
    <div>
      <Header />
      {children}
    </div>
  )
}

export default function Page() {
  return (
    <Layout>
      <Sidebar />
    </Layout>
  )
}
```

### 3.6 Per-Request Deduplication with React.cache()

**Impact: MEDIUM (deduplicates within request)**

Use `React.cache()` for server-side request deduplication. Authentication and database queries benefit most.

**Usage:**

```typescript
import { cache } from 'react'

export const getCurrentUser = cache(async () => {
  const session = await auth()
  if (!session?.user?.id) return null
  return await db.user.findUnique({
    where: { id: session.user.id }
  })
})
```

Within a single request, multiple calls to `getCurrentUser()` execute the query only once.

**Avoid inline objects as arguments:**

`React.cache()` uses shallow equality (`Object.is`) to determine cache hits. Inline objects create new references each call, preventing cache hits.

**Incorrect: always cache miss**

```typescript
const getUser = cache(async (params: { uid: number }) => {
  return await db.user.findUnique({ where: { id: params.uid } })
})

// Each call creates new object, never hits cache
getUser({ uid: 1 })
getUser({ uid: 1 })  // Cache miss, runs query again
```

**Correct: cache hit**

```typescript
const params = { uid: 1 }
getUser(params)  // Query runs
getUser(params)  // Cache hit (same reference)
```

If you must pass objects, pass the same reference:

**Next.js-Specific Note:**

In Next.js, the `fetch` API is automatically extended with request memoization. Requests with the same URL and options are automatically deduplicated within a single request, so you don't need `React.cache()` for `fetch` calls. However, `React.cache()` is still essential for other async tasks:

- Database queries (Prisma, Drizzle, etc.)

- Heavy computations

- Authentication checks

- File system operations

- Any non-fetch async work

Use `React.cache()` to deduplicate these operations across your component tree.

Reference: [https://react.dev/reference/react/cache](https://react.dev/reference/react/cache)

### 3.7 Use after() for Non-Blocking Operations

**Impact: MEDIUM (faster response times)**

Use Next.js's `after()` to schedule work that should execute after a response is sent. This prevents logging, analytics, and other side effects from blocking the response.

**Incorrect: blocks response**

```tsx
import { logUserAction } from '@/app/utils'

export async function POST(request: Request) {
  // Perform mutation
  await updateDatabase(request)
  
  // Logging blocks the response
  const userAgent = request.headers.get('user-agent') || 'unknown'
  await logUserAction({ userAgent })
  
  return new Response(JSON.stringify({ status: 'success' }), {
    status: 200,
    headers: { 'Content-Type': 'application/json' }
  })
}
```

**Correct: non-blocking**

```tsx
import { after } from 'next/server'
import { headers, cookies } from 'next/headers'
import { logUserAction } from '@/app/utils'

export async function POST(request: Request) {
  // Perform mutation
  await updateDatabase(request)
  
  // Log after response is sent
  after(async () => {
    const userAgent = (await headers()).get('user-agent') || 'unknown'
    const sessionCookie = (await cookies()).get('session-id')?.value || 'anonymous'
    
    logUserAction({ sessionCookie, userAgent })
  })
  
  return new Response(JSON.stringify({ status: 'success' }), {
    status: 200,
    headers: { 'Content-Type': 'application/json' }
  })
}
```

The response is sent immediately while logging happens in the background.

**Common use cases:**

- Analytics tracking

- Audit logging

- Sending notifications

- Cache invalidation

- Cleanup tasks

**Important notes:**

- `after()` runs even if the response fails or redirects

- Works in Server Actions, Route Handlers, and Server Components

Reference: [https://nextjs.org/docs/app/api-reference/functions/after](https://nextjs.org/docs/app/api-reference/functions/after)

---

## 4. Client-Side Data Fetching

**Impact: MEDIUM-HIGH**

Automatic deduplication and efficient data fetching patterns reduce redundant network requests.

### 4.1 Deduplicate Global Event Listeners

**Impact: LOW (single listener for N components)**

Use `useSWRSubscription()` to share global event listeners across component instances.

**Incorrect: N instances = N listeners**

```tsx
function useKeyboardShortcut(key: string, callback: () => void) {
  useEffect(() => {
    const handler = (e: KeyboardEvent) => {
      if (e.metaKey && e.key === key) {
        callback()
      }
    }
    window.addEventListener('keydown', handler)
    return () => window.removeEventListener('keydown', handler)
  }, [key, callback])
}
```

When using the `useKeyboardShortcut` hook multiple times, each instance will register a new listener.

**Correct: N instances = 1 listener**

```tsx
import useSWRSubscription from 'swr/subscription'

// Module-level Map to track callbacks per key
const keyCallbacks = new Map<string, Set<() => void>>()

function useKeyboardShortcut(key: string, callback: () => void) {
  // Register this callback in the Map
  useEffect(() => {
    if (!keyCallbacks.has(key)) {
      keyCallbacks.set(key, new Set())
    }
    keyCallbacks.get(key)!.add(callback)

    return () => {
      const set = keyCallbacks.get(key)
      if (set) {
        set.delete(callback)
        if (set.size === 0) {
          keyCallbacks.delete(key)
        }
      }
    }
  }, [key, callback])

  useSWRSubscription('global-keydown', () => {
    const handler = (e: KeyboardEvent) => {
      if (e.metaKey && keyCallbacks.has(e.key)) {
        keyCallbacks.get(e.key)!.forEach(cb => cb())
      }
    }
    window.addEventListener('keydown', handler)
    return () => window.removeEventListener('keydown', handler)
  })
}

function Profile() {
  // Multiple shortcuts will share the same listener
  useKeyboardShortcut('p', () => { /* ... */ }) 
  useKeyboardShortcut('k', () => { /* ... */ })
  // ...
}
```

### 4.2 Use Passive Event Listeners for Scrolling Performance

**Impact: MEDIUM (eliminates scroll delay caused by event listeners)**

Add `{ passive: true }` to touch and wheel event listeners to enable immediate scrolling. Browsers normally wait for listeners to finish to check if `preventDefault()` is called, causing scroll delay.

**Incorrect:**

```typescript
useEffect(() => {
  const handleTouch = (e: TouchEvent) => console.log(e.touches[0].clientX)
  const handleWheel = (e: WheelEvent) => console.log(e.deltaY)
  
  document.addEventListener('touchstart', handleTouch)
  document.addEventListener('wheel', handleWheel)
  
  return () => {
    document.removeEventListener('touchstart', handleTouch)
    document.removeEventListener('wheel', handleWheel)
  }
}, [])
```

**Correct:**

```typescript
useEffect(() => {
  const handleTouch = (e: TouchEvent) => console.log(e.touches[0].clientX)
  const handleWheel = (e: WheelEvent) => console.log(e.deltaY)
  
  document.addEventListener('touchstart', handleTouch, { passive: true })
  document.addEventListener('wheel', handleWheel, { passive: true })
  
  return () => {
    document.removeEventListener('touchstart', handleTouch)
    document.removeEventListener('wheel', handleWheel)
  }
}, [])
```

**Use passive when:** tracking/analytics, logging, any listener that doesn't call `preventDefault()`.

**Don't use passive when:** implementing custom swipe gestures, custom zoom controls, or any listener that needs `preventDefault()`.

### 4.3 Use SWR for Automatic Deduplication

**Impact: MEDIUM-HIGH (automatic deduplication)**

SWR enables request deduplication, caching, and revalidation across component instances.

**Incorrect: no deduplication, each instance fetches**

```tsx
function UserList() {
  const [users, setUsers] = useState([])
  useEffect(() => {
    fetch('/api/users')
      .then(r => r.json())
      .then(setUsers)
  }, [])
}
```

**Correct: multiple instances share one request**

```tsx
import useSWR from 'swr'

function UserList() {
  const { data: users } = useSWR('/api/users', fetcher)
}
```

**For immutable data:**

```tsx
import { useImmutableSWR } from '@/lib/swr'

function StaticContent() {
  const { data } = useImmutableSWR('/api/config', fetcher)
}
```

**For mutations:**

```tsx
import { useSWRMutation } from 'swr/mutation'

function UpdateButton() {
  const { trigger } = useSWRMutation('/api/user', updateUser)
  return <button onClick={() => trigger()}>Update</button>
}
```

Reference: [https://swr.vercel.app](https://swr.vercel.app)

### 4.4 Version and Minimize localStorage Data

**Impact: MEDIUM (prevents schema conflicts, reduces storage size)**

Add version prefix to keys and store only needed fields. Prevents schema conflicts and accidental storage of sensitive data.

**Incorrect:**

```typescript
// No version, stores everything, no error handling
localStorage.setItem('userConfig', JSON.stringify(fullUserObject))
const data = localStorage.getItem('userConfig')
```

**Correct:**

```typescript
const VERSION = 'v2'

function saveConfig(config: { theme: string; language: string }) {
  try {
    localStorage.setItem(`userConfig:${VERSION}`, JSON.stringify(config))
  } catch {
    // Throws in incognito/private browsing, quota exceeded, or disabled
  }
}

function loadConfig() {
  try {
    const data = localStorage.getItem(`userConfig:${VERSION}`)
    return data ? JSON.parse(data) : null
  } catch {
    return null
  }
}

// Migration from v1 to v2
function migrate() {
  try {
    const v1 = localStorage.getItem('userConfig:v1')
    if (v1) {
      const old = JSON.parse(v1)
      saveConfig({ theme: old.darkMode ? 'dark' : 'light', language: old.lang })
      localStorage.removeItem('userConfig:v1')
    }
  } catch {}
}
```

**Store minimal fields from server responses:**

```typescript
// User object has 20+ fields, only store what UI needs
function cachePrefs(user: FullUser) {
  try {
    localStorage.setItem('prefs:v1', JSON.stringify({
      theme: user.preferences.theme,
      notifications: user.preferences.notifications
    }))
  } catch {}
}
```

**Always wrap in try-catch:** `getItem()` and `setItem()` throw in incognito/private browsing (Safari, Firefox), when quota exceeded, or when disabled.

**Benefits:** Schema evolution via versioning, reduced storage size, prevents storing tokens/PII/internal flags.

---

## 5. Re-render Optimization

**Impact: MEDIUM**

Reducing unnecessary re-renders minimizes wasted computation and improves UI responsiveness.

### 5.1 Calculate Derived State During Rendering

**Impact: MEDIUM (avoids redundant renders and state drift)**

If a value can be computed from current props/state, do not store it in state or update it in an effect. Derive it during render to avoid extra renders and state drift. Do not set state in effects solely in response to prop changes; prefer derived values or keyed resets instead.

**Incorrect: redundant state and effect**

```tsx
function Form() {
  const [firstName, setFirstName] = useState('First')
  const [lastName, setLastName] = useState('Last')
  const [fullName, setFullName] = useState('')

  useEffect(() => {
    setFullName(firstName + ' ' + lastName)
  }, [firstName, lastName])

  return <p>{fullName}</p>
}
```

**Correct: derive during render**

```tsx
function Form() {
  const [firstName, setFirstName] = useState('First')
  const [lastName, setLastName] = useState('Last')
  const fullName = firstName + ' ' + lastName

  return <p>{fullName}</p>
}
```

Reference: [https://react.dev/learn/you-might-not-need-an-effect](https://react.dev/learn/you-might-not-need-an-effect)

### 5.2 Defer State Reads to Usage Point

**Impact: MEDIUM (avoids unnecessary subscriptions)**

Don't subscribe to dynamic state (searchParams, localStorage) if you only read it inside callbacks.

**Incorrect: subscribes to all searchParams changes**

```tsx
function ShareButton({ chatId }: { chatId: string }) {
  const searchParams = useSearchParams()

  const handleShare = () => {
    const ref = searchParams.get('ref')
    shareChat(chatId, { ref })
  }

  return <button onClick={handleShare}>Share</button>
}
```

**Correct: reads on demand, no subscription**

```tsx
function ShareButton({ chatId }: { chatId: string }) {
  const handleShare = () => {
    const params = new URLSearchParams(window.location.search)
    const ref = params.get('ref')
    shareChat(chatId, { ref })
  }

  return <button onClick={handleShare}>Share</button>
}
```

### 5.3 Do not wrap a simple expression with a primitive result type in useMemo

**Impact: LOW-MEDIUM (wasted computation on every render)**

When an expression is simple (few logical or arithmetical operators) and has a primitive result type (boolean, number, string), do not wrap it in `useMemo`.

Calling `useMemo` and comparing hook dependencies may consume more resources than the expression itself.

**Incorrect:**

```tsx
function Header({ user, notifications }: Props) {
  const isLoading = useMemo(() => {
    return user.isLoading || notifications.isLoading
  }, [user.isLoading, notifications.isLoading])

  if (isLoading) return <Skeleton />
  // return some markup
}
```

**Correct:**

```tsx
function Header({ user, notifications }: Props) {
  const isLoading = user.isLoading || notifications.isLoading

  if (isLoading) return <Skeleton />
  // return some markup
}
```

### 5.4 Extract Default Non-primitive Parameter Value from Memoized Component to Constant

**Impact: MEDIUM (restores memoization by using a constant for default value)**

When memoized component has a default value for some non-primitive optional parameter, such as an array, function, or object, calling the component without that parameter results in broken memoization. This is because new value instances are created on every rerender, and they do not pass strict equality comparison in `memo()`.

To address this issue, extract the default value into a constant.

**Incorrect: `onClick` has different values on every rerender**

```tsx
const UserAvatar = memo(function UserAvatar({ onClick = () => {} }: { onClick?: () => void }) {
  // ...
})

// Used without optional onClick
<UserAvatar />
```

**Correct: stable default value**

```tsx
const NOOP = () => {};

const UserAvatar = memo(function UserAvatar({ onClick = NOOP }: { onClick?: () => void }) {
  // ...
})

// Used without optional onClick
<UserAvatar />
```

### 5.5 Extract to Memoized Components

**Impact: MEDIUM (enables early returns)**

Extract expensive work into memoized components to enable early returns before computation.

**Incorrect: computes avatar even when loading**

```tsx
function Profile({ user, loading }: Props) {
  const avatar = useMemo(() => {
    const id = computeAvatarId(user)
    return <Avatar id={id} />
  }, [user])

  if (loading) return <Skeleton />
  return <div>{avatar}</div>
}
```

**Correct: skips computation when loading**

```tsx
const UserAvatar = memo(function UserAvatar({ user }: { user: User }) {
  const id = useMemo(() => computeAvatarId(user), [user])
  return <Avatar id={id} />
})

function Profile({ user, loading }: Props) {
  if (loading) return <Skeleton />
  return (
    <div>
      <UserAvatar user={user} />
    </div>
  )
}
```

**Note:** If your project has [React Compiler](https://react.dev/learn/react-compiler) enabled, manual memoization with `memo()` and `useMemo()` is not necessary. The compiler automatically optimizes re-renders.

### 5.6 Narrow Effect Dependencies

**Impact: LOW (minimizes effect re-runs)**

Specify primitive dependencies instead of objects to minimize effect re-runs.

**Incorrect: re-runs on any user field change**

```tsx
useEffect(() => {
  console.log(user.id)
}, [user])
```

**Correct: re-runs only when id changes**

```tsx
useEffect(() => {
  console.log(user.id)
}, [user.id])
```

**For derived state, compute outside effect:**

```tsx
// Incorrect: runs on width=767, 766, 765...
useEffect(() => {
  if (width < 768) {
    enableMobileMode()
  }
}, [width])

// Correct: runs only on boolean transition
const isMobile = width < 768
useEffect(() => {
  if (isMobile) {
    enableMobileMode()
  }
}, [isMobile])
```

### 5.7 Put Interaction Logic in Event Handlers

**Impact: MEDIUM (avoids effect re-runs and duplicate side effects)**

If a side effect is triggered by a specific user action (submit, click, drag), run it in that event handler. Do not model the action as state + effect; it makes effects re-run on unrelated changes and can duplicate the action.

**Incorrect: event modeled as state + effect**

```tsx
function Form() {
  const [submitted, setSubmitted] = useState(false)
  const theme = useContext(ThemeContext)

  useEffect(() => {
    if (submitted) {
      post('/api/register')
      showToast('Registered', theme)
    }
  }, [submitted, theme])

  return <button onClick={() => setSubmitted(true)}>Submit</button>
}
```

**Correct: do it in the handler**

```tsx
function Form() {
  const theme = useContext(ThemeContext)

  function handleSubmit() {
    post('/api/register')
    showToast('Registered', theme)
  }

  return <button onClick={handleSubmit}>Submit</button>
}
```

Reference: [https://react.dev/learn/removing-effect-dependencies#should-this-code-move-to-an-event-handler](https://react.dev/learn/removing-effect-dependencies#should-this-code-move-to-an-event-handler)

### 5.8 Subscribe to Derived State

**Impact: MEDIUM (reduces re-render frequency)**

Subscribe to derived boolean state instead of continuous values to reduce re-render frequency.

**Incorrect: re-renders on every pixel change**

```tsx
function Sidebar() {
  const width = useWindowWidth()  // updates continuously
  const isMobile = width < 768
  return <nav className={isMobile ? 'mobile' : 'desktop'} />
}
```

**Correct: re-renders only when boolean changes**

```tsx
function Sidebar() {
  const isMobile = useMediaQuery('(max-width: 767px)')
  return <nav className={isMobile ? 'mobile' : 'desktop'} />
}
```

### 5.9 Use Functional setState Updates

**Impact: MEDIUM (prevents stale closures and unnecessary callback recreations)**

When updating state based on the current state value, use the functional update form of setState instead of directly referencing the state variable. This prevents stale closures, eliminates unnecessary dependencies, and creates stable callback references.

**Incorrect: requires state as dependency**

```tsx
function TodoList() {
  const [items, setItems] = useState(initialItems)
  
  // Callback must depend on items, recreated on every items change
  const addItems = useCallback((newItems: Item[]) => {
    setItems([...items, ...newItems])
  }, [items])  // ❌ items dependency causes recreations
  
  // Risk of stale closure if dependency is forgotten
  const removeItem = useCallback((id: string) => {
    setItems(items.filter(item => item.id !== id))
  }, [])  // ❌ Missing items dependency - will use stale items!
  
  return <ItemsEditor items={items} onAdd={addItems} onRemove={removeItem} />
}
```

The first callback is recreated every time `items` changes, which can cause child components to re-render unnecessarily. The second callback has a stale closure bug—it will always reference the initial `items` value.

**Correct: stable callbacks, no stale closures**

```tsx
function TodoList() {
  const [items, setItems] = useState(initialItems)
  
  // Stable callback, never recreated
  const addItems = useCallback((newItems: Item[]) => {
    setItems(curr => [...curr, ...newItems])
  }, [])  // ✅ No dependencies needed
  
  // Always uses latest state, no stale closure risk
  const removeItem = useCallback((id: string) => {
    setItems(curr => curr.filter(item => item.id !== id))
  }, [])  // ✅ Safe and stable
  
  return <ItemsEditor items={items} onAdd={addItems} onRemove={removeItem} />
}
```

**Benefits:**

1. **Stable callback references** - Callbacks don't need to be recreated when state changes

2. **No stale closures** - Always operates on the latest state value

3. **Fewer dependencies** - Simplifies dependency arrays and reduces memory leaks

4. **Prevents bugs** - Eliminates the most common source of React closure bugs

**When to use functional updates:**

- Any setState that depends on the current state value

- Inside useCallback/useMemo when state is needed

- Event handlers that reference state

- Async operations that update state

**When direct updates are fine:**

- Setting state to a static value: `setCount(0)`

- Setting state from props/arguments only: `setName(newName)`

- State doesn't depend on previous value

**Note:** If your project has [React Compiler](https://react.dev/learn/react-compiler) enabled, the compiler can automatically optimize some cases, but functional updates are still recommended for correctness and to prevent stale closure bugs.

### 5.10 Use Lazy State Initialization

**Impact: MEDIUM (wasted computation on every render)**

Pass a function to `useState` for expensive initial values. Without the function form, the initializer runs on every render even though the value is only used once.

**Incorrect: runs on every render**

```tsx
function FilteredList({ items }: { items: Item[] }) {
  // buildSearchIndex() runs on EVERY render, even after initialization
  const [searchIndex, setSearchIndex] = useState(buildSearchIndex(items))
  const [query, setQuery] = useState('')
  
  // When query changes, buildSearchIndex runs again unnecessarily
  return <SearchResults index={searchIndex} query={query} />
}

function UserProfile() {
  // JSON.parse runs on every render
  const [settings, setSettings] = useState(
    JSON.parse(localStorage.getItem('settings') || '{}')
  )
  
  return <SettingsForm settings={settings} onChange={setSettings} />
}
```

**Correct: runs only once**

```tsx
function FilteredList({ items }: { items: Item[] }) {
  // buildSearchIndex() runs ONLY on initial render
  const [searchIndex, setSearchIndex] = useState(() => buildSearchIndex(items))
  const [query, setQuery] = useState('')
  
  return <SearchResults index={searchIndex} query={query} />
}

function UserProfile() {
  // JSON.parse runs only on initial render
  const [settings, setSettings] = useState(() => {
    const stored = localStorage.getItem('settings')
    return stored ? JSON.parse(stored) : {}
  })
  
  return <SettingsForm settings={settings} onChange={setSettings} />
}
```

Use lazy initialization when computing initial values from localStorage/sessionStorage, building data structures (indexes, maps), reading from the DOM, or performing heavy transformations.

For simple primitives (`useState(0)`), direct references (`useState(props.value)`), or cheap literals (`useState({})`), the function form is unnecessary.

### 5.11 Use Transitions for Non-Urgent Updates

**Impact: MEDIUM (maintains UI responsiveness)**

Mark frequent, non-urgent state updates as transitions to maintain UI responsiveness.

**Incorrect: blocks UI on every scroll**

```tsx
function ScrollTracker() {
  const [scrollY, setScrollY] = useState(0)
  useEffect(() => {
    const handler = () => setScrollY(window.scrollY)
    window.addEventListener('scroll', handler, { passive: true })
    return () => window.removeEventListener('scroll', handler)
  }, [])
}
```

**Correct: non-blocking updates**

```tsx
import { startTransition } from 'react'

function ScrollTracker() {
  const [scrollY, setScrollY] = useState(0)
  useEffect(() => {
    const handler = () => {
      startTransition(() => setScrollY(window.scrollY))
    }
    window.addEventListener('scroll', handler, { passive: true })
    return () => window.removeEventListener('scroll', handler)
  }, [])
}
```

### 5.12 Use useRef for Transient Values

**Impact: MEDIUM (avoids unnecessary re-renders on frequent updates)**

When a value changes frequently and you don't want a re-render on every update (e.g., mouse trackers, intervals, transient flags), store it in `useRef` instead of `useState`. Keep component state for UI; use refs for temporary DOM-adjacent values. Updating a ref does not trigger a re-render.

**Incorrect: renders every update**

```tsx
function Tracker() {
  const [lastX, setLastX] = useState(0)

  useEffect(() => {
    const onMove = (e: MouseEvent) => setLastX(e.clientX)
    window.addEventListener('mousemove', onMove)
    return () => window.removeEventListener('mousemove', onMove)
  }, [])

  return (
    <div
      style={{
        position: 'fixed',
        top: 0,
        left: lastX,
        width: 8,
        height: 8,
        background: 'black',
      }}
    />
  )
}
```

**Correct: no re-render for tracking**

```tsx
function Tracker() {
  const lastXRef = useRef(0)
  const dotRef = useRef<HTMLDivElement>(null)

  useEffect(() => {
    const onMove = (e: MouseEvent) => {
      lastXRef.current = e.clientX
      const node = dotRef.current
      if (node) {
        node.style.transform = `translateX(${e.clientX}px)`
      }
    }
    window.addEventListener('mousemove', onMove)
    return () => window.removeEventListener('mousemove', onMove)
  }, [])

  return (
    <div
      ref={dotRef}
      style={{
        position: 'fixed',
        top: 0,
        left: 0,
        width: 8,
        height: 8,
        background: 'black',
        transform: 'translateX(0px)',
      }}
    />
  )
}
```

---

## 6. Rendering Performance

**Impact: MEDIUM**

Optimizing the rendering process reduces the work the browser needs to do.

### 6.1 Animate SVG Wrapper Instead of SVG Element

**Impact: LOW (enables hardware acceleration)**

Many browsers don't have hardware acceleration for CSS3 animations on SVG elements. Wrap SVG in a `<div>` and animate the wrapper instead.

**Incorrect: animating SVG directly - no hardware acceleration**

```tsx
function LoadingSpinner() {
  return (
    <svg 
      className="animate-spin"
      width="24" 
      height="24" 
      viewBox="0 0 24 24"
    >
      <circle cx="12" cy="12" r="10" stroke="currentColor" />
    </svg>
  )
}
```

**Correct: animating wrapper div - hardware accelerated**

```tsx
function LoadingSpinner() {
  return (
    <div className="animate-spin">
      <svg 
        width="24" 
        height="24" 
        viewBox="0 0 24 24"
      >
        <circle cx="12" cy="12" r="10" stroke="currentColor" />
      </svg>
    </div>
  )
}
```

This applies to all CSS transforms and transitions (`transform`, `opacity`, `translate`, `scale`, `rotate`). The wrapper div allows browsers to use GPU acceleration for smoother animations.

### 6.2 CSS content-visibility for Long Lists

**Impact: HIGH (faster initial render)**

Apply `content-visibility: auto` to defer off-screen rendering.

**CSS:**

```css
.message-item {
  content-visibility: auto;
  contain-intrinsic-size: 0 80px;
}
```

**Example:**

```tsx
function MessageList({ messages }: { messages: Message[] }) {
  return (
    <div className="overflow-y-auto h-screen">
      {messages.map(msg => (
        <div key={msg.id} className="message-item">
          <Avatar user={msg.author} />
          <div>{msg.content}</div>
        </div>
      ))}
    </div>
  )
}
```

For 1000 messages, browser skips layout/paint for ~990 off-screen items (10× faster initial render).

### 6.3 Hoist Static JSX Elements

**Impact: LOW (avoids re-creation)**

Extract static JSX outside components to avoid re-creation.

**Incorrect: recreates element every render**

```tsx
function LoadingSkeleton() {
  return <div className="animate-pulse h-20 bg-gray-200" />
}

function Container() {
  return (
    <div>
      {loading && <LoadingSkeleton />}
    </div>
  )
}
```

**Correct: reuses same element**

```tsx
const loadingSkeleton = (
  <div className="animate-pulse h-20 bg-gray-200" />
)

function Container() {
  return (
    <div>
      {loading && loadingSkeleton}
    </div>
  )
}
```

This is especially helpful for large and static SVG nodes, which can be expensive to recreate on every render.

**Note:** If your project has [React Compiler](https://react.dev/learn/react-compiler) enabled, the compiler automatically hoists static JSX elements and optimizes component re-renders, making manual hoisting unnecessary.

### 6.4 Optimize SVG Precision

**Impact: LOW (reduces file size)**

Reduce SVG coordinate precision to decrease file size. The optimal precision depends on the viewBox size, but in general reducing precision should be considered.

**Incorrect: excessive precision**

```svg
<path d="M 10.293847 20.847362 L 30.938472 40.192837" />
```

**Correct: 1 decimal place**

```svg
<path d="M 10.3 20.8 L 30.9 40.2" />
```

**Automate with SVGO:**

```bash
npx svgo --precision=1 --multipass icon.svg
```

### 6.5 Prevent Hydration Mismatch Without Flickering

**Impact: MEDIUM (avoids visual flicker and hydration errors)**

When rendering content that depends on client-side storage (localStorage, cookies), avoid both SSR breakage and post-hydration flickering by injecting a synchronous script that updates the DOM before React hydrates.

**Incorrect: breaks SSR**

```tsx
function ThemeWrapper({ children }: { children: ReactNode }) {
  // localStorage is not available on server - throws error
  const theme = localStorage.getItem('theme') || 'light'
  
  return (
    <div className={theme}>
      {children}
    </div>
  )
}
```

Server-side rendering will fail because `localStorage` is undefined.

**Incorrect: visual flickering**

```tsx
function ThemeWrapper({ children }: { children: ReactNode }) {
  const [theme, setTheme] = useState('light')
  
  useEffect(() => {
    // Runs after hydration - causes visible flash
    const stored = localStorage.getItem('theme')
    if (stored) {
      setTheme(stored)
    }
  }, [])
  
  return (
    <div className={theme}>
      {children}
    </div>
  )
}
```

Component first renders with default value (`light`), then updates after hydration, causing a visible flash of incorrect content.

**Correct: no flicker, no hydration mismatch**

```tsx
function ThemeWrapper({ children }: { children: ReactNode }) {
  return (
    <>
      <div id="theme-wrapper">
        {children}
      </div>
      <script
        dangerouslySetInnerHTML={{
          __html: `
            (function() {
              try {
                var theme = localStorage.getItem('theme') || 'light';
                var el = document.getElementById('theme-wrapper');
                if (el) el.className = theme;
              } catch (e) {}
            })();
          `,
        }}
      />
    </>
  )
}
```

The inline script executes synchronously before showing the element, ensuring the DOM already has the correct value. No flickering, no hydration mismatch.

This pattern is especially useful for theme toggles, user preferences, authentication states, and any client-only data that should render immediately without flashing default values.

### 6.6 Suppress Expected Hydration Mismatches

**Impact: LOW-MEDIUM (avoids noisy hydration warnings for known differences)**

In SSR frameworks (e.g., Next.js), some values are intentionally different on server vs client (random IDs, dates, locale/timezone formatting). For these *expected* mismatches, wrap the dynamic text in an element with `suppressHydrationWarning` to prevent noisy warnings. Do not use this to hide real bugs. Don’t overuse it.

**Incorrect: known mismatch warnings**

```tsx
function Timestamp() {
  return <span>{new Date().toLocaleString()}</span>
}
```

**Correct: suppress expected mismatch only**

```tsx
function Timestamp() {
  return (
    <span suppressHydrationWarning>
      {new Date().toLocaleString()}
    </span>
  )
}
```

### 6.7 Use Activity Component for Show/Hide

**Impact: MEDIUM (preserves state/DOM)**

Use React's `<Activity>` to preserve state/DOM for expensive components that frequently toggle visibility.

**Usage:**

```tsx
import { Activity } from 'react'

function Dropdown({ isOpen }: Props) {
  return (
    <Activity mode={isOpen ? 'visible' : 'hidden'}>
      <ExpensiveMenu />
    </Activity>
  )
}
```

Avoids expensive re-renders and state loss.

### 6.8 Use Explicit Conditional Rendering

**Impact: LOW (prevents rendering 0 or NaN)**

Use explicit ternary operators (`? :`) instead of `&&` for conditional rendering when the condition can be `0`, `NaN`, or other falsy values that render.

**Incorrect: renders "0" when count is 0**

```tsx
function Badge({ count }: { count: number }) {
  return (
    <div>
      {count && <span className="badge">{count}</span>}
    </div>
  )
}

// When count = 0, renders: <div>0</div>
// When count = 5, renders: <div><span class="badge">5</span></div>
```

**Correct: renders nothing when count is 0**

```tsx
function Badge({ count }: { count: number }) {
  return (
    <div>
      {count > 0 ? <span className="badge">{count}</span> : null}
    </div>
  )
}

// When count = 0, renders: <div></div>
// When count = 5, renders: <div><span class="badge">5</span></div>
```

### 6.9 Use useTransition Over Manual Loading States

**Impact: LOW (reduces re-renders and improves code clarity)**

Use `useTransition` instead of manual `useState` for loading states. This provides built-in `isPending` state and automatically manages transitions.

**Incorrect: manual loading state**

```tsx
function SearchResults() {
  const [query, setQuery] = useState('')
  const [results, setResults] = useState([])
  const [isLoading, setIsLoading] = useState(false)

  const handleSearch = async (value: string) => {
    setIsLoading(true)
    setQuery(value)
    const data = await fetchResults(value)
    setResults(data)
    setIsLoading(false)
  }

  return (
    <>
      <input onChange={(e) => handleSearch(e.target.value)} />
      {isLoading && <Spinner />}
      <ResultsList results={results} />
    </>
  )
}
```

**Correct: useTransition with built-in pending state**

```tsx
import { useTransition, useState } from 'react'

function SearchResults() {
  const [query, setQuery] = useState('')
  const [results, setResults] = useState([])
  const [isPending, startTransition] = useTransition()

  const handleSearch = (value: string) => {
    setQuery(value) // Update input immediately
    
    startTransition(async () => {
      // Fetch and update results
      const data = await fetchResults(value)
      setResults(data)
    })
  }

  return (
    <>
      <input onChange={(e) => handleSearch(e.target.value)} />
      {isPending && <Spinner />}
      <ResultsList results={results} />
    </>
  )
}
```

**Benefits:**

- **Automatic pending state**: No need to manually manage `setIsLoading(true/false)`

- **Error resilience**: Pending state correctly resets even if the transition throws

- **Better responsiveness**: Keeps the UI responsive during updates

- **Interrupt handling**: New transitions automatically cancel pending ones

Reference: [https://react.dev/reference/react/useTransition](https://react.dev/reference/react/useTransition)

---

## 7. JavaScript Performance

**Impact: LOW-MEDIUM**

Micro-optimizations for hot paths can add up to meaningful improvements.

### 7.1 Avoid Layout Thrashing

**Impact: MEDIUM (prevents forced synchronous layouts and reduces performance bottlenecks)**

Avoid interleaving style writes with layout reads. When you read a layout property (like `offsetWidth`, `getBoundingClientRect()`, or `getComputedStyle()`) between style changes, the browser is forced to trigger a synchronous reflow.

**This is OK: browser batches style changes**

```typescript
function updateElementStyles(element: HTMLElement) {
  // Each line invalidates style, but browser batches the recalculation
  element.style.width = '100px'
  element.style.height = '200px'
  element.style.backgroundColor = 'blue'
  element.style.border = '1px solid black'
}
```

**Incorrect: interleaved reads and writes force reflows**

```typescript
function layoutThrashing(element: HTMLElement) {
  element.style.width = '100px'
  const width = element.offsetWidth  // Forces reflow
  element.style.height = '200px'
  const height = element.offsetHeight  // Forces another reflow
}
```

**Correct: batch writes, then read once**

```typescript
function updateElementStyles(element: HTMLElement) {
  // Batch all writes together
  element.style.width = '100px'
  element.style.height = '200px'
  element.style.backgroundColor = 'blue'
  element.style.border = '1px solid black'
  
  // Read after all writes are done (single reflow)
  const { width, height } = element.getBoundingClientRect()
}
```

**Correct: batch reads, then writes**

```typescript
function updateElementStyles(element: HTMLElement) {
  element.classList.add('highlighted-box')
  
  const { width, height } = element.getBoundingClientRect()
}
```

**Better: use CSS classes**

**React example:**

```tsx
// Incorrect: interleaving style changes with layout queries
function Box({ isHighlighted }: { isHighlighted: boolean }) {
  const ref = useRef<HTMLDivElement>(null)
  
  useEffect(() => {
    if (ref.current && isHighlighted) {
      ref.current.style.width = '100px'
      const width = ref.current.offsetWidth // Forces layout
      ref.current.style.height = '200px'
    }
  }, [isHighlighted])
  
  return <div ref={ref}>Content</div>
}

// Correct: toggle class
function Box({ isHighlighted }: { isHighlighted: boolean }) {
  return (
    <div className={isHighlighted ? 'highlighted-box' : ''}>
      Content
    </div>
  )
}
```

Prefer CSS classes over inline styles when possible. CSS files are cached by the browser, and classes provide better separation of concerns and are easier to maintain.

See [this gist](https://gist.github.com/paulirish/5d52fb081b3570c81e3a) and [CSS Triggers](https://csstriggers.com/) for more information on layout-forcing operations.

### 7.2 Build Index Maps for Repeated Lookups

**Impact: LOW-MEDIUM (1M ops to 2K ops)**

Multiple `.find()` calls by the same key should use a Map.

**Incorrect (O(n) per lookup):**

```typescript
function processOrders(orders: Order[], users: User[]) {
  return orders.map(order => ({
    ...order,
    user: users.find(u => u.id === order.userId)
  }))
}
```

**Correct (O(1) per lookup):**

```typescript
function processOrders(orders: Order[], users: User[]) {
  const userById = new Map(users.map(u => [u.id, u]))

  return orders.map(order => ({
    ...order,
    user: userById.get(order.userId)
  }))
}
```

Build map once (O(n)), then all lookups are O(1).

For 1000 orders × 1000 users: 1M ops → 2K ops.

### 7.3 Cache Property Access in Loops

**Impact: LOW-MEDIUM (reduces lookups)**

Cache object property lookups in hot paths.

**Incorrect: 3 lookups × N iterations**

```typescript
for (let i = 0; i < arr.length; i++) {
  process(obj.config.settings.value)
}
```

**Correct: 1 lookup total**

```typescript
const value = obj.config.settings.value
const len = arr.length
for (let i = 0; i < len; i++) {
  process(value)
}
```

### 7.4 Cache Repeated Function Calls

**Impact: MEDIUM (avoid redundant computation)**

Use a module-level Map to cache function results when the same function is called repeatedly with the same inputs during render.

**Incorrect: redundant computation**

```typescript
function ProjectList({ projects }: { projects: Project[] }) {
  return (
    <div>
      {projects.map(project => {
        // slugify() called 100+ times for same project names
        const slug = slugify(project.name)
        
        return <ProjectCard key={project.id} slug={slug} />
      })}
    </div>
  )
}
```

**Correct: cached results**

```typescript
// Module-level cache
const slugifyCache = new Map<string, string>()

function cachedSlugify(text: string): string {
  if (slugifyCache.has(text)) {
    return slugifyCache.get(text)!
  }
  const result = slugify(text)
  slugifyCache.set(text, result)
  return result
}

function ProjectList({ projects }: { projects: Project[] }) {
  return (
    <div>
      {projects.map(project => {
        // Computed only once per unique project name
        const slug = cachedSlugify(project.name)
        
        return <ProjectCard key={project.id} slug={slug} />
      })}
    </div>
  )
}
```

**Simpler pattern for single-value functions:**

```typescript
let isLoggedInCache: boolean | null = null

function isLoggedIn(): boolean {
  if (isLoggedInCache !== null) {
    return isLoggedInCache
  }
  
  isLoggedInCache = document.cookie.includes('auth=')
  return isLoggedInCache
}

// Clear cache when auth changes
function onAuthChange() {
  isLoggedInCache = null
}
```

Use a Map (not a hook) so it works everywhere: utilities, event handlers, not just React components.

Reference: [https://vercel.com/blog/how-we-made-the-vercel-dashboard-twice-as-fast](https://vercel.com/blog/how-we-made-the-vercel-dashboard-twice-as-fast)

### 7.5 Cache Storage API Calls

**Impact: LOW-MEDIUM (reduces expensive I/O)**

`localStorage`, `sessionStorage`, and `document.cookie` are synchronous and expensive. Cache reads in memory.

**Incorrect: reads storage on every call**

```typescript
function getTheme() {
  return localStorage.getItem('theme') ?? 'light'
}
// Called 10 times = 10 storage reads
```

**Correct: Map cache**

```typescript
const storageCache = new Map<string, string | null>()

function getLocalStorage(key: string) {
  if (!storageCache.has(key)) {
    storageCache.set(key, localStorage.getItem(key))
  }
  return storageCache.get(key)
}

function setLocalStorage(key: string, value: string) {
  localStorage.setItem(key, value)
  storageCache.set(key, value)  // keep cache in sync
}
```

Use a Map (not a hook) so it works everywhere: utilities, event handlers, not just React components.

**Cookie caching:**

```typescript
let cookieCache: Record<string, string> | null = null

function getCookie(name: string) {
  if (!cookieCache) {
    cookieCache = Object.fromEntries(
      document.cookie.split('; ').map(c => c.split('='))
    )
  }
  return cookieCache[name]
}
```

**Important: invalidate on external changes**

```typescript
window.addEventListener('storage', (e) => {
  if (e.key) storageCache.delete(e.key)
})

document.addEventListener('visibilitychange', () => {
  if (document.visibilityState === 'visible') {
    storageCache.clear()
  }
})
```

If storage can change externally (another tab, server-set cookies), invalidate cache:

### 7.6 Combine Multiple Array Iterations

**Impact: LOW-MEDIUM (reduces iterations)**

Multiple `.filter()` or `.map()` calls iterate the array multiple times. Combine into one loop.

**Incorrect: 3 iterations**

```typescript
const admins = users.filter(u => u.isAdmin)
const testers = users.filter(u => u.isTester)
const inactive = users.filter(u => !u.isActive)
```

**Correct: 1 iteration**

```typescript
const admins: User[] = []
const testers: User[] = []
const inactive: User[] = []

for (const user of users) {
  if (user.isAdmin) admins.push(user)
  if (user.isTester) testers.push(user)
  if (!user.isActive) inactive.push(user)
}
```

### 7.7 Early Length Check for Array Comparisons

**Impact: MEDIUM-HIGH (avoids expensive operations when lengths differ)**

When comparing arrays with expensive operations (sorting, deep equality, serialization), check lengths first. If lengths differ, the arrays cannot be equal.

In real-world applications, this optimization is especially valuable when the comparison runs in hot paths (event handlers, render loops).

**Incorrect: always runs expensive comparison**

```typescript
function hasChanges(current: string[], original: string[]) {
  // Always sorts and joins, even when lengths differ
  return current.sort().join() !== original.sort().join()
}
```

Two O(n log n) sorts run even when `current.length` is 5 and `original.length` is 100. There is also overhead of joining the arrays and comparing the strings.

**Correct (O(1) length check first):**

```typescript
function hasChanges(current: string[], original: string[]) {
  // Early return if lengths differ
  if (current.length !== original.length) {
    return true
  }
  // Only sort when lengths match
  const currentSorted = current.toSorted()
  const originalSorted = original.toSorted()
  for (let i = 0; i < currentSorted.length; i++) {
    if (currentSorted[i] !== originalSorted[i]) {
      return true
    }
  }
  return false
}
```

This new approach is more efficient because:

- It avoids the overhead of sorting and joining the arrays when lengths differ

- It avoids consuming memory for the joined strings (especially important for large arrays)

- It avoids mutating the original arrays

- It returns early when a difference is found

### 7.8 Early Return from Functions

**Impact: LOW-MEDIUM (avoids unnecessary computation)**

Return early when result is determined to skip unnecessary processing.

**Incorrect: processes all items even after finding answer**

```typescript
function validateUsers(users: User[]) {
  let hasError = false
  let errorMessage = ''
  
  for (const user of users) {
    if (!user.email) {
      hasError = true
      errorMessage = 'Email required'
    }
    if (!user.name) {
      hasError = true
      errorMessage = 'Name required'
    }
    // Continues checking all users even after error found
  }
  
  return hasError ? { valid: false, error: errorMessage } : { valid: true }
}
```

**Correct: returns immediately on first error**

```typescript
function validateUsers(users: User[]) {
  for (const user of users) {
    if (!user.email) {
      return { valid: false, error: 'Email required' }
    }
    if (!user.name) {
      return { valid: false, error: 'Name required' }
    }
  }

  return { valid: true }
}
```

### 7.9 Hoist RegExp Creation

**Impact: LOW-MEDIUM (avoids recreation)**

Don't create RegExp inside render. Hoist to module scope or memoize with `useMemo()`.

**Incorrect: new RegExp every render**

```tsx
function Highlighter({ text, query }: Props) {
  const regex = new RegExp(`(${query})`, 'gi')
  const parts = text.split(regex)
  return <>{parts.map((part, i) => ...)}</>
}
```

**Correct: memoize or hoist**

```tsx
const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/

function Highlighter({ text, query }: Props) {
  const regex = useMemo(
    () => new RegExp(`(${escapeRegex(query)})`, 'gi'),
    [query]
  )
  const parts = text.split(regex)
  return <>{parts.map((part, i) => ...)}</>
}
```

**Warning: global regex has mutable state**

```typescript
const regex = /foo/g
regex.test('foo')  // true, lastIndex = 3
regex.test('foo')  // false, lastIndex = 0
```

Global regex (`/g`) has mutable `lastIndex` state:

### 7.10 Use Loop for Min/Max Instead of Sort

**Impact: LOW (O(n) instead of O(n log n))**

Finding the smallest or largest element only requires a single pass through the array. Sorting is wasteful and slower.

**Incorrect (O(n log n) - sort to find latest):**

```typescript
interface Project {
  id: string
  name: string
  updatedAt: number
}

function getLatestProject(projects: Project[]) {
  const sorted = [...projects].sort((a, b) => b.updatedAt - a.updatedAt)
  return sorted[0]
}
```

Sorts the entire array just to find the maximum value.

**Incorrect (O(n log n) - sort for oldest and newest):**

```typescript
function getOldestAndNewest(projects: Project[]) {
  const sorted = [...projects].sort((a, b) => a.updatedAt - b.updatedAt)
  return { oldest: sorted[0], newest: sorted[sorted.length - 1] }
}
```

Still sorts unnecessarily when only min/max are needed.

**Correct (O(n) - single loop):**

```typescript
function getLatestProject(projects: Project[]) {
  if (projects.length === 0) return null
  
  let latest = projects[0]
  
  for (let i = 1; i < projects.length; i++) {
    if (projects[i].updatedAt > latest.updatedAt) {
      latest = projects[i]
    }
  }
  
  return latest
}

function getOldestAndNewest(projects: Project[]) {
  if (projects.length === 0) return { oldest: null, newest: null }
  
  let oldest = projects[0]
  let newest = projects[0]
  
  for (let i = 1; i < projects.length; i++) {
    if (projects[i].updatedAt < oldest.updatedAt) oldest = projects[i]
    if (projects[i].updatedAt > newest.updatedAt) newest = projects[i]
  }
  
  return { oldest, newest }
}
```

Single pass through the array, no copying, no sorting.

**Alternative: Math.min/Math.max for small arrays**

```typescript
const numbers = [5, 2, 8, 1, 9]
const min = Math.min(...numbers)
const max = Math.max(...numbers)
```

This works for small arrays, but can be slower or just throw an error for very large arrays due to spread operator limitations. Maximal array length is approximately 124000 in Chrome 143 and 638000 in Safari 18; exact numbers may vary - see [the fiddle](https://jsfiddle.net/qw1jabsx/4/). Use the loop approach for reliability.

### 7.11 Use Set/Map for O(1) Lookups

**Impact: LOW-MEDIUM (O(n) to O(1))**

Convert arrays to Set/Map for repeated membership checks.

**Incorrect (O(n) per check):**

```typescript
const allowedIds = ['a', 'b', 'c', ...]
items.filter(item => allowedIds.includes(item.id))
```

**Correct (O(1) per check):**

```typescript
const allowedIds = new Set(['a', 'b', 'c', ...])
items.filter(item => allowedIds.has(item.id))
```

### 7.12 Use toSorted() Instead of sort() for Immutability

**Impact: MEDIUM-HIGH (prevents mutation bugs in React state)**

`.sort()` mutates the array in place, which can cause bugs with React state and props. Use `.toSorted()` to create a new sorted array without mutation.

**Incorrect: mutates original array**

```typescript
function UserList({ users }: { users: User[] }) {
  // Mutates the users prop array!
  const sorted = useMemo(
    () => users.sort((a, b) => a.name.localeCompare(b.name)),
    [users]
  )
  return <div>{sorted.map(renderUser)}</div>
}
```

**Correct: creates new array**

```typescript
function UserList({ users }: { users: User[] }) {
  // Creates new sorted array, original unchanged
  const sorted = useMemo(
    () => users.toSorted((a, b) => a.name.localeCompare(b.name)),
    [users]
  )
  return <div>{sorted.map(renderUser)}</div>
}
```

**Why this matters in React:**

1. Props/state mutations break React's immutability model - React expects props and state to be treated as read-only

2. Causes stale closure bugs - Mutating arrays inside closures (callbacks, effects) can lead to unexpected behavior

**Browser support: fallback for older browsers**

```typescript
// Fallback for older browsers
const sorted = [...items].sort((a, b) => a.value - b.value)
```

`.toSorted()` is available in all modern browsers (Chrome 110+, Safari 16+, Firefox 115+, Node.js 20+). For older environments, use spread operator:

**Other immutable array methods:**

- `.toSorted()` - immutable sort

- `.toReversed()` - immutable reverse

- `.toSpliced()` - immutable splice

- `.with()` - immutable element replacement

---

## 8. Advanced Patterns

**Impact: LOW**

Advanced patterns for specific cases that require careful implementation.

### 8.1 Initialize App Once, Not Per Mount

**Impact: LOW-MEDIUM (avoids duplicate init in development)**

Do not put app-wide initialization that must run once per app load inside `useEffect([])` of a component. Components can remount and effects will re-run. Use a module-level guard or top-level init in the entry module instead.

**Incorrect: runs twice in dev, re-runs on remount**

```tsx
function Comp() {
  useEffect(() => {
    loadFromStorage()
    checkAuthToken()
  }, [])

  // ...
}
```

**Correct: once per app load**

```tsx
let didInit = false

function Comp() {
  useEffect(() => {
    if (didInit) return
    didInit = true
    loadFromStorage()
    checkAuthToken()
  }, [])

  // ...
}
```

Reference: [https://react.dev/learn/you-might-not-need-an-effect#initializing-the-application](https://react.dev/learn/you-might-not-need-an-effect#initializing-the-application)

### 8.2 Store Event Handlers in Refs

**Impact: LOW (stable subscriptions)**

Store callbacks in refs when used in effects that shouldn't re-subscribe on callback changes.

**Incorrect: re-subscribes on every render**

```tsx
function useWindowEvent(event: string, handler: (e) => void) {
  useEffect(() => {
    window.addEventListener(event, handler)
    return () => window.removeEventListener(event, handler)
  }, [event, handler])
}
```

**Correct: stable subscription**

```tsx
import { useEffectEvent } from 'react'

function useWindowEvent(event: string, handler: (e) => void) {
  const onEvent = useEffectEvent(handler)

  useEffect(() => {
    window.addEventListener(event, onEvent)
    return () => window.removeEventListener(event, onEvent)
  }, [event])
}
```

**Alternative: use `useEffectEvent` if you're on latest React:**

`useEffectEvent` provides a cleaner API for the same pattern: it creates a stable function reference that always calls the latest version of the handler.

### 8.3 useEffectEvent for Stable Callback Refs

**Impact: LOW (prevents effect re-runs)**

Access latest values in callbacks without adding them to dependency arrays. Prevents effect re-runs while avoiding stale closures.

**Incorrect: effect re-runs on every callback change**

```tsx
function SearchInput({ onSearch }: { onSearch: (q: string) => void }) {
  const [query, setQuery] = useState('')

  useEffect(() => {
    const timeout = setTimeout(() => onSearch(query), 300)
    return () => clearTimeout(timeout)
  }, [query, onSearch])
}
```

**Correct: using React's useEffectEvent**

```tsx
import { useEffectEvent } from 'react';

function SearchInput({ onSearch }: { onSearch: (q: string) => void }) {
  const [query, setQuery] = useState('')
  const onSearchEvent = useEffectEvent(onSearch)

  useEffect(() => {
    const timeout = setTimeout(() => onSearchEvent(query), 300)
    return () => clearTimeout(timeout)
  }, [query])
}
```

---

## References

1. [https://react.dev](https://react.dev)
2. [https://nextjs.org](https://nextjs.org)
3. [https://swr.vercel.app](https://swr.vercel.app)
4. [https://github.com/shuding/better-all](https://github.com/shuding/better-all)
5. [https://github.com/isaacs/node-lru-cache](https://github.com/isaacs/node-lru-cache)
6. [https://vercel.com/blog/how-we-optimized-package-imports-in-next-js](https://vercel.com/blog/how-we-optimized-package-imports-in-next-js)
7. [https://vercel.com/blog/how-we-made-the-vercel-dashboard-twice-as-fast](https://vercel.com/blog/how-we-made-the-vercel-dashboard-twice-as-fast)


================================================
FILE: .agents/skills/vercel-react-best-practices/SKILL.md
================================================
---
name: vercel-react-best-practices
description: React and Next.js performance optimization guidelines from Vercel Engineering. This skill should be used when writing, reviewing, or refactoring React/Next.js code to ensure optimal performance patterns. Triggers on tasks involving React components, Next.js pages, data fetching, bundle optimization, or performance improvements.
license: MIT
metadata:
  author: vercel
  version: "1.0.0"
---

# Vercel React Best Practices

Comprehensive performance optimization guide for React and Next.js applications, maintained by Vercel. Contains 57 rules across 8 categories, prioritized by impact to guide automated refactoring and code generation.

## When to Apply

Reference these guidelines when:
- Writing new React components or Next.js pages
- Implementing data fetching (client or server-side)
- Reviewing code for performance issues
- Refactoring existing React/Next.js code
- Optimizing bundle size or load times

## Rule Categories by Priority

| Priority | Category | Impact | Prefix |
|----------|----------|--------|--------|
| 1 | Eliminating Waterfalls | CRITICAL | `async-` |
| 2 | Bundle Size Optimization | CRITICAL | `bundle-` |
| 3 | Server-Side Performance | HIGH | `server-` |
| 4 | Client-Side Data Fetching | MEDIUM-HIGH | `client-` |
| 5 | Re-render Optimization | MEDIUM | `rerender-` |
| 6 | Rendering Performance | MEDIUM | `rendering-` |
| 7 | JavaScript Performance | LOW-MEDIUM | `js-` |
| 8 | Advanced Patterns | LOW | `advanced-` |

## Quick Reference

### 1. Eliminating Waterfalls (CRITICAL)

- `async-defer-await` - Move await into branches where actually used
- `async-parallel` - Use Promise.all() for independent operations
- `async-dependencies` - Use better-all for partial dependencies
- `async-api-routes` - Start promises early, await late in API routes
- `async-suspense-boundaries` - Use Suspense to stream content

### 2. Bundle Size Optimization (CRITICAL)

- `bundle-barrel-imports` - Import directly, avoid barrel files
- `bundle-dynamic-imports` - Use next/dynamic for heavy components
- `bundle-defer-third-party` - Load analytics/logging after hydration
- `bundle-conditional` - Load modules only when feature is activated
- `bundle-preload` - Preload on hover/focus for perceived speed

### 3. Server-Side Performance (HIGH)

- `server-auth-actions` - Authenticate server actions like API routes
- `server-cache-react` - Use React.cache() for per-request deduplication
- `server-cache-lru` - Use LRU cache for cross-request caching
- `server-dedup-props` - Avoid duplicate serialization in RSC props
- `server-serialization` - Minimize data passed to client components
- `server-parallel-fetching` - Restructure components to parallelize fetches
- `server-after-nonblocking` - U
Download .txt
gitextract_qhe605p_/

├── .agents/
│   └── skills/
│       ├── frontend-design/
│       │   ├── LICENSE.txt
│       │   └── SKILL.md
│       ├── gh-cli/
│       │   └── SKILL.md
│       ├── postgres/
│       │   ├── SKILL.md
│       │   └── references/
│       │       ├── backup-recovery.md
│       │       ├── index-optimization.md
│       │       ├── indexing.md
│       │       ├── memory-management-ops.md
│       │       ├── monitoring.md
│       │       ├── mvcc-transactions.md
│       │       ├── mvcc-vacuum.md
│       │       ├── optimization-checklist.md
│       │       ├── partitioning.md
│       │       ├── process-architecture.md
│       │       ├── ps-cli-api-insights.md
│       │       ├── ps-cli-commands.md
│       │       ├── ps-connection-pooling.md
│       │       ├── ps-connections.md
│       │       ├── ps-extensions.md
│       │       ├── ps-insights.md
│       │       ├── query-patterns.md
│       │       ├── replication.md
│       │       ├── schema-design.md
│       │       ├── storage-layout.md
│       │       └── wal-operations.md
│       ├── vercel-react-best-practices/
│       │   ├── AGENTS.md
│       │   ├── SKILL.md
│       │   └── rules/
│       │       ├── advanced-event-handler-refs.md
│       │       ├── advanced-init-once.md
│       │       ├── advanced-use-latest.md
│       │       ├── async-api-routes.md
│       │       ├── async-defer-await.md
│       │       ├── async-dependencies.md
│       │       ├── async-parallel.md
│       │       ├── async-suspense-boundaries.md
│       │       ├── bundle-barrel-imports.md
│       │       ├── bundle-conditional.md
│       │       ├── bundle-defer-third-party.md
│       │       ├── bundle-dynamic-imports.md
│       │       ├── bundle-preload.md
│       │       ├── client-event-listeners.md
│       │       ├── client-localstorage-schema.md
│       │       ├── client-passive-event-listeners.md
│       │       ├── client-swr-dedup.md
│       │       ├── js-batch-dom-css.md
│       │       ├── js-cache-function-results.md
│       │       ├── js-cache-property-access.md
│       │       ├── js-cache-storage.md
│       │       ├── js-combine-iterations.md
│       │       ├── js-early-exit.md
│       │       ├── js-hoist-regexp.md
│       │       ├── js-index-maps.md
│       │       ├── js-length-check-first.md
│       │       ├── js-min-max-loop.md
│       │       ├── js-set-map-lookups.md
│       │       ├── js-tosorted-immutable.md
│       │       ├── rendering-activity.md
│       │       ├── rendering-animate-svg-wrapper.md
│       │       ├── rendering-conditional-render.md
│       │       ├── rendering-content-visibility.md
│       │       ├── rendering-hoist-jsx.md
│       │       ├── rendering-hydration-no-flicker.md
│       │       ├── rendering-hydration-suppress-warning.md
│       │       ├── rendering-svg-precision.md
│       │       ├── rendering-usetransition-loading.md
│       │       ├── rerender-defer-reads.md
│       │       ├── rerender-dependencies.md
│       │       ├── rerender-derived-state-no-effect.md
│       │       ├── rerender-derived-state.md
│       │       ├── rerender-functional-setstate.md
│       │       ├── rerender-lazy-state-init.md
│       │       ├── rerender-memo-with-default-value.md
│       │       ├── rerender-memo.md
│       │       ├── rerender-move-effect-to-event.md
│       │       ├── rerender-simple-expression-in-memo.md
│       │       ├── rerender-transitions.md
│       │       ├── rerender-use-ref-transient-values.md
│       │       ├── server-after-nonblocking.md
│       │       ├── server-auth-actions.md
│       │       ├── server-cache-lru.md
│       │       ├── server-cache-react.md
│       │       ├── server-dedup-props.md
│       │       ├── server-parallel-fetching.md
│       │       └── server-serialization.md
│       └── web-design-guidelines/
│           └── SKILL.md
├── .cursor/
│   └── rules/
│       ├── rule-claude-opus.mdc
│       └── rule-trigger-typescript.mdc
├── .cursorignore
├── .eslintrc.json
├── .github/
│   ├── CODEOWNERS
│   ├── ISSUE_TEMPLATE/
│   │   └── oss-gg-hack-template.yml
│   └── workflows/
│       └── cla.yml
├── .gitignore
├── .prettierignore
├── CLA.md
├── LICENSE
├── Pipfile
├── README.md
├── SECURITY.md
├── app/
│   ├── (auth)/
│   │   ├── auth/
│   │   │   ├── confirm-email-change/
│   │   │   │   └── [token]/
│   │   │   │       ├── page-client.tsx
│   │   │   │       ├── page.tsx
│   │   │   │       └── utils.ts
│   │   │   ├── email/
│   │   │   │   └── [[...params]]/
│   │   │   │       ├── page-client.tsx
│   │   │   │       └── page.tsx
│   │   │   └── saml/
│   │   │       ├── page-client.tsx
│   │   │       └── page.tsx
│   │   ├── layout.tsx
│   │   ├── login/
│   │   │   ├── page-client.tsx
│   │   │   └── page.tsx
│   │   ├── register/
│   │   │   ├── page-client.tsx
│   │   │   └── page.tsx
│   │   └── verify/
│   │       ├── invitation/
│   │       │   ├── AcceptInvitationButton.tsx
│   │       │   ├── InvitationStatusContent.tsx
│   │       │   ├── page.tsx
│   │       │   └── status/
│   │       │       └── ClientRedirect.tsx
│   │       └── page.tsx
│   ├── (ee)/
│   │   ├── LICENSE.md
│   │   ├── README.md
│   │   └── api/
│   │       ├── ai/
│   │       │   ├── chat/
│   │       │   │   ├── [chatId]/
│   │       │   │   │   ├── messages/
│   │       │   │   │   │   └── route.ts
│   │       │   │   │   └── route.ts
│   │       │   │   └── route.ts
│   │       │   └── store/
│   │       │       ├── runs/
│   │       │       │   └── [runId]/
│   │       │       │       └── route.ts
│   │       │       └── teams/
│   │       │           └── [teamId]/
│   │       │               ├── datarooms/
│   │       │               │   └── [dataroomId]/
│   │       │               │       └── route.ts
│   │       │               ├── documents/
│   │       │               │   └── [documentId]/
│   │       │               │       └── route.ts
│   │       │               └── route.ts
│   │       ├── auth/
│   │       │   └── saml/
│   │       │       ├── authorize/
│   │       │       │   └── route.ts
│   │       │       ├── callback/
│   │       │       │   └── route.ts
│   │       │       ├── token/
│   │       │       │   └── route.ts
│   │       │       ├── userinfo/
│   │       │       │   └── route.ts
│   │       │       └── verify/
│   │       │           └── route.ts
│   │       ├── faqs/
│   │       │   └── route.ts
│   │       ├── links/
│   │       │   └── [id]/
│   │       │       └── upload/
│   │       │           └── route.ts
│   │       ├── scim/
│   │       │   └── v2.0/
│   │       │       └── [...directory]/
│   │       │           └── route.ts
│   │       ├── teams/
│   │       │   └── [teamId]/
│   │       │       ├── directory-sync/
│   │       │       │   └── route.ts
│   │       │       └── saml/
│   │       │           └── route.ts
│   │       ├── workflow-entry/
│   │       │   ├── domains/
│   │       │   │   └── [...domainSlug]/
│   │       │   │       └── route.ts
│   │       │   └── link/
│   │       │       └── [entryLinkId]/
│   │       │           ├── access/
│   │       │           │   └── route.ts
│   │       │           └── verify/
│   │       │               └── route.ts
│   │       └── workflows/
│   │           ├── [workflowId]/
│   │           │   ├── executions/
│   │           │   │   └── route.ts
│   │           │   ├── route.ts
│   │           │   └── steps/
│   │           │       ├── [stepId]/
│   │           │       │   └── route.ts
│   │           │       └── route.ts
│   │           └── route.ts
│   ├── api/
│   │   ├── auth/
│   │   │   └── verify-code/
│   │   │       └── route.ts
│   │   ├── cron/
│   │   │   ├── dataroom-digest/
│   │   │   │   ├── daily/
│   │   │   │   │   └── route.ts
│   │   │   │   └── weekly/
│   │   │   │       └── route.ts
│   │   │   ├── domains/
│   │   │   │   ├── route.ts
│   │   │   │   └── utils.ts
│   │   │   ├── welcome-user/
│   │   │   │   └── route.ts
│   │   │   └── year-in-review/
│   │   │       └── route.ts
│   │   ├── csp-report/
│   │   │   └── route.ts
│   │   ├── feature-flags/
│   │   │   └── route.ts
│   │   ├── help/
│   │   │   └── route.ts
│   │   ├── integrations/
│   │   │   └── slack/
│   │   │       └── oauth/
│   │   │           ├── authorize/
│   │   │           │   └── route.ts
│   │   │           └── callback/
│   │   │               └── route.ts
│   │   ├── og/
│   │   │   ├── route.tsx
│   │   │   └── yir/
│   │   │       └── route.tsx
│   │   ├── verify/
│   │   │   └── login-link/
│   │   │       └── route.ts
│   │   ├── views/
│   │   │   ├── pages/
│   │   │   │   └── route.ts
│   │   │   └── route.ts
│   │   ├── views-dataroom/
│   │   │   └── route.ts
│   │   └── webhooks/
│   │       └── callback/
│   │           └── route.ts
│   ├── layout.tsx
│   └── robots.txt
├── components/
│   ├── EmailForm.tsx
│   ├── Skeleton.tsx
│   ├── account/
│   │   ├── account-header.tsx
│   │   ├── update-subscription.tsx
│   │   └── upload-avatar.tsx
│   ├── agreements/
│   │   └── agreement-card.tsx
│   ├── ai-elements/
│   │   ├── conversation.tsx
│   │   ├── message.tsx
│   │   ├── prompt-input.tsx
│   │   └── shimmer.tsx
│   ├── analytics/
│   │   ├── analytics-card.tsx
│   │   ├── dashboard-views-chart.tsx
│   │   ├── documents-table.tsx
│   │   ├── links-table.tsx
│   │   ├── time-range-select.tsx
│   │   ├── views-table.tsx
│   │   └── visitors-table.tsx
│   ├── billing/
│   │   ├── add-seat-modal.tsx
│   │   ├── plan-badge.tsx
│   │   ├── pro-annual-banner.tsx
│   │   ├── pro-banner.tsx
│   │   ├── upgrade-plan-container.tsx
│   │   ├── upgrade-plan-modal-old.tsx
│   │   ├── upgrade-plan-modal-with-discount.tsx
│   │   ├── upgrade-plan-modal.tsx
│   │   └── yearly-upgrade-banner.tsx
│   ├── blur-image.tsx
│   ├── charts/
│   │   ├── bar-chart-tooltip.tsx
│   │   ├── bar-chart.tsx
│   │   └── utils.ts
│   ├── conversations/
│   │   └── index.tsx
│   ├── datarooms/
│   │   ├── actions/
│   │   │   ├── download-dataroom.tsx
│   │   │   ├── generate-index-button.tsx
│   │   │   ├── generate-index-dialog.tsx
│   │   │   ├── rebuild-index-button.tsx
│   │   │   └── remove-document-modal.tsx
│   │   ├── add-dataroom-modal.tsx
│   │   ├── add-viewer-modal.tsx
│   │   ├── analytics/
│   │   │   ├── analytics-overview.tsx
│   │   │   ├── document-analytics-tree.tsx
│   │   │   └── mock-analytics-table.tsx
│   │   ├── dataroom-breadcrumb.tsx
│   │   ├── dataroom-card.tsx
│   │   ├── dataroom-document-card.tsx
│   │   ├── dataroom-header.tsx
│   │   ├── dataroom-items-list.tsx
│   │   ├── dataroom-navigation.tsx
│   │   ├── dataroom-trial-modal.tsx
│   │   ├── download-progress-modal.tsx
│   │   ├── edit-dataroom-document-modal.tsx
│   │   ├── empty-dataroom.tsx
│   │   ├── export-visits-modal.tsx
│   │   ├── folders/
│   │   │   ├── index.tsx
│   │   │   ├── selection-tree.tsx
│   │   │   ├── sidebar-tree.tsx
│   │   │   ├── utils.ts
│   │   │   └── view-tree.tsx
│   │   ├── groups/
│   │   │   ├── add-group-modal.tsx
│   │   │   ├── add-member-modal.tsx
│   │   │   ├── delete-group/
│   │   │   │   ├── delete-group-modal.tsx
│   │   │   │   └── index.tsx
│   │   │   ├── group-card-placeholder.tsx
│   │   │   ├── group-card.tsx
│   │   │   ├── group-header.tsx
│   │   │   ├── group-member-table.tsx
│   │   │   ├── group-navigation.tsx
│   │   │   ├── group-permissions.tsx
│   │   │   └── set-unified-permissions-modal.tsx
│   │   ├── move-dataroom-folder-modal.tsx
│   │   ├── settings/
│   │   │   ├── bulk-download-settings.tsx
│   │   │   ├── dataroom-tag-section.tsx
│   │   │   ├── delete-dataroooom/
│   │   │   │   ├── delete-dataroom-modal.tsx
│   │   │   │   └── index.tsx
│   │   │   ├── duplicate-dataroom.tsx
│   │   │   ├── introduction-settings.tsx
│   │   │   ├── notification-settings.tsx
│   │   │   ├── permission-settings.tsx
│   │   │   └── settings-tabs.tsx
│   │   ├── sortable/
│   │   │   ├── sortable-item.tsx
│   │   │   └── sortable-list.tsx
│   │   └── stats-card.tsx
│   ├── document-upload.tsx
│   ├── documents/
│   │   ├── actions/
│   │   │   ├── delete-documents-modal.tsx
│   │   │   └── delete-folder-modal.tsx
│   │   ├── add-document-modal.tsx
│   │   ├── add-document-to-dataroom-modal.tsx
│   │   ├── add-folder-to-dataroom-modal.tsx
│   │   ├── alert.tsx
│   │   ├── annotations/
│   │   │   ├── annotation-form.tsx
│   │   │   └── annotation-sheet.tsx
│   │   ├── breadcrumb.tsx
│   │   ├── delete-folder-modal.tsx
│   │   ├── document-card.tsx
│   │   ├── document-header.tsx
│   │   ├── document-preview-button.tsx
│   │   ├── document-preview-modal.tsx
│   │   ├── document-stats-placeholder.tsx
│   │   ├── documents-list.tsx
│   │   ├── drag-and-drop/
│   │   │   ├── draggable-item.tsx
│   │   │   └── droppable-folder.tsx
│   │   ├── empty-document.tsx
│   │   ├── export-visits-modal.tsx
│   │   ├── file-process-status-bar.tsx
│   │   ├── filters/
│   │   │   └── sort-button.tsx
│   │   ├── folder-card.tsx
│   │   ├── hidden-document-card.tsx
│   │   ├── hidden-documents-list.tsx
│   │   ├── hidden-folder-card.tsx
│   │   ├── link-document-indicator.tsx
│   │   ├── loading-document.tsx
│   │   ├── move-folder-modal.tsx
│   │   ├── notion-accessibility-indicator.tsx
│   │   ├── pagination.tsx
│   │   ├── preview-viewers/
│   │   │   ├── index.ts
│   │   │   ├── preview-excel-viewer.tsx
│   │   │   ├── preview-image-viewer.tsx
│   │   │   ├── preview-pages-viewer.tsx
│   │   │   └── preview-viewer.tsx
│   │   ├── stats-card.tsx
│   │   ├── stats-chart-dummy.tsx
│   │   ├── stats-chart-skeleton.tsx
│   │   ├── stats-chart.tsx
│   │   ├── stats-element.tsx
│   │   ├── stats.tsx
│   │   ├── video-analytics.tsx
│   │   ├── video-chart-placeholder.tsx
│   │   └── video-stats-placeholder.tsx
│   ├── domains/
│   │   ├── add-domain-modal.tsx
│   │   ├── delete-domain-modal.tsx
│   │   ├── domain-card.tsx
│   │   ├── domain-configuration.tsx
│   │   └── use-domain-status.ts
│   ├── emails/
│   │   ├── custom-domain-setup.tsx
│   │   ├── data-rooms-information.tsx
│   │   ├── dataroom-digest-notification.tsx
│   │   ├── dataroom-notification.tsx
│   │   ├── dataroom-trial-24h.tsx
│   │   ├── dataroom-trial-end.tsx
│   │   ├── dataroom-trial-welcome.tsx
│   │   ├── dataroom-upload-notification.tsx
│   │   ├── deleted-domain.tsx
│   │   ├── download-ready.tsx
│   │   ├── email-updated.tsx
│   │   ├── export-ready.tsx
│   │   ├── hundred-views-congrats.tsx
│   │   ├── installed-integration-notification.tsx
│   │   ├── invalid-domain.tsx
│   │   ├── onboarding-1.tsx
│   │   ├── onboarding-2.tsx
│   │   ├── onboarding-3.tsx
│   │   ├── onboarding-4.tsx
│   │   ├── otp-verification.tsx
│   │   ├── shared/
│   │   │   └── footer.tsx
│   │   ├── slack-integration.tsx
│   │   ├── team-invitation.tsx
│   │   ├── thousand-views-congrats.tsx
│   │   ├── upgrade-one-month-checkin.tsx
│   │   ├── upgrade-personal-welcome.tsx
│   │   ├── upgrade-plan.tsx
│   │   ├── upgrade-six-month-checkin.tsx
│   │   ├── verification-email-change.tsx
│   │   ├── verification-link.tsx
│   │   ├── viewed-dataroom-paused.tsx
│   │   ├── viewed-dataroom.tsx
│   │   ├── viewed-document-paused.tsx
│   │   ├── viewed-document.tsx
│   │   ├── welcome.tsx
│   │   └── year-in-review-papermark.tsx
│   ├── folders/
│   │   ├── add-folder-modal.tsx
│   │   ├── edit-folder-modal.tsx
│   │   ├── folder-color-picker.tsx
│   │   └── folder-icon-picker.tsx
│   ├── gtm-component.tsx
│   ├── hooks/
│   │   ├── use-optimistic-update.ts
│   │   └── useLastUsed.tsx
│   ├── layouts/
│   │   ├── app.tsx
│   │   ├── blocking-modal.tsx
│   │   ├── breadcrumb.tsx
│   │   └── trial-banner.tsx
│   ├── links/
│   │   ├── delete-link-modal.tsx
│   │   ├── embed-code-modal.tsx
│   │   ├── link-active-controls.tsx
│   │   ├── link-sheet/
│   │   │   ├── agreement-panel/
│   │   │   │   └── index.tsx
│   │   │   ├── agreement-section.tsx
│   │   │   ├── ai-agents-section.tsx
│   │   │   ├── allow-download-section.tsx
│   │   │   ├── allow-list-section.tsx
│   │   │   ├── allow-notification-section.tsx
│   │   │   ├── conversation-section.tsx
│   │   │   ├── custom-fields-panel/
│   │   │   │   ├── custom-field.tsx
│   │   │   │   └── index.tsx
│   │   │   ├── custom-fields-section.tsx
│   │   │   ├── dataroom-link-sheet.tsx
│   │   │   ├── deny-list-section.tsx
│   │   │   ├── domain-section.tsx
│   │   │   ├── email-authentication-section.tsx
│   │   │   ├── email-protection-section.tsx
│   │   │   ├── expiration-section.tsx
│   │   │   ├── expirationIn-section.tsx
│   │   │   ├── feedback-section.tsx
│   │   │   ├── index-file-section.tsx
│   │   │   ├── index.tsx
│   │   │   ├── link-item.tsx
│   │   │   ├── link-options.tsx
│   │   │   ├── link-success-sheet.tsx
│   │   │   ├── og-section.tsx
│   │   │   ├── password-section.tsx
│   │   │   ├── permissions-sheet.tsx
│   │   │   ├── pro-banner-section.tsx
│   │   │   ├── question-section.tsx
│   │   │   ├── screenshot-protection-section.tsx
│   │   │   ├── tags/
│   │   │   │   ├── tag-badge.tsx
│   │   │   │   ├── tag-details.tsx
│   │   │   │   └── tag-section.tsx
│   │   │   ├── upload-section/
│   │   │   │   └── index.tsx
│   │   │   ├── watermark-panel/
│   │   │   │   └── index.tsx
│   │   │   ├── watermark-section.tsx
│   │   │   └── welcome-message-section.tsx
│   │   ├── links-table.tsx
│   │   ├── links-visitors.tsx
│   │   └── preview-button.tsx
│   ├── navigation-menu.tsx
│   ├── profile-menu.tsx
│   ├── profile-search-trigger.tsx
│   ├── providers/
│   │   └── posthog-provider.tsx
│   ├── search-box.tsx
│   ├── search-command.tsx
│   ├── settings/
│   │   ├── delete-team-modal.tsx
│   │   ├── delete-team.tsx
│   │   ├── global-block-list-form.tsx
│   │   ├── ignored-domains-form.tsx
│   │   ├── og-preview.tsx
│   │   ├── settings-header.tsx
│   │   ├── slack-settings-skeleton.tsx
│   │   ├── survey-settings.tsx
│   │   └── timezone-selector.tsx
│   ├── shared/
│   │   ├── dealflow-popup.tsx
│   │   ├── icons/
│   │   │   ├── advanced-sheet.tsx
│   │   │   ├── alert-circle.tsx
│   │   │   ├── arrow-up.tsx
│   │   │   ├── badge-check.tsx
│   │   │   ├── bar-chart.tsx
│   │   │   ├── check-cirlce-2.tsx
│   │   │   ├── check.tsx
│   │   │   ├── chevron-down.tsx
│   │   │   ├── chevron-right.tsx
│   │   │   ├── chevron-up.tsx
│   │   │   ├── circle.tsx
│   │   │   ├── cloud-download-off.tsx
│   │   │   ├── copy-right.tsx
│   │   │   ├── copy.tsx
│   │   │   ├── external-link.tsx
│   │   │   ├── eye-off.tsx
│   │   │   ├── eye.tsx
│   │   │   ├── facebook.tsx
│   │   │   ├── file-up.tsx
│   │   │   ├── files/
│   │   │   │   ├── cad.tsx
│   │   │   │   ├── docs.tsx
│   │   │   │   ├── image.tsx
│   │   │   │   ├── map.tsx
│   │   │   │   ├── notion.tsx
│   │   │   │   ├── pdf.tsx
│   │   │   │   ├── sheet.tsx
│   │   │   │   ├── slides.tsx
│   │   │   │   └── video.tsx
│   │   │   ├── folder.tsx
│   │   │   ├── github.tsx
│   │   │   ├── globe.tsx
│   │   │   ├── google.tsx
│   │   │   ├── grip-vertical.tsx
│   │   │   ├── home.tsx
│   │   │   ├── index.tsx
│   │   │   ├── linkedin.tsx
│   │   │   ├── menu.tsx
│   │   │   ├── moon.tsx
│   │   │   ├── more-horizontal.tsx
│   │   │   ├── more-vertical.tsx
│   │   │   ├── papermark-sparkle.tsx
│   │   │   ├── passkey.tsx
│   │   │   ├── pie-chart.tsx
│   │   │   ├── portrait-landscape.tsx
│   │   │   ├── producthunt.tsx
│   │   │   ├── search.tsx
│   │   │   ├── settings.tsx
│   │   │   ├── slack-icon.tsx
│   │   │   ├── sparkle.tsx
│   │   │   ├── sun.tsx
│   │   │   ├── teams.tsx
│   │   │   ├── twitter.tsx
│   │   │   ├── user-round.tsx
│   │   │   ├── x-circle.tsx
│   │   │   └── x.tsx
│   │   └── logo-cloud.tsx
│   ├── sidebar/
│   │   ├── app-sidebar.tsx
│   │   ├── banners/
│   │   │   └── slack-banner.tsx
│   │   ├── nav-main.tsx
│   │   ├── nav-user.tsx
│   │   └── team-switcher.tsx
│   ├── sidebar-folders.tsx
│   ├── tab-menu.tsx
│   ├── tags/
│   │   └── add-tag-modal.tsx
│   ├── teams/
│   │   ├── add-team-member-modal.tsx
│   │   ├── add-team-modal.tsx
│   │   ├── delete-team-modal.tsx
│   │   └── select-team.tsx
│   ├── theme-provider.tsx
│   ├── theme-toggle.tsx
│   ├── ui/
│   │   ├── accordion.tsx
│   │   ├── alert-dialog.tsx
│   │   ├── alert.tsx
│   │   ├── avatar.tsx
│   │   ├── badge.tsx
│   │   ├── bar-list.tsx
│   │   ├── breadcrumb.tsx
│   │   ├── button-group.tsx
│   │   ├── button.tsx
│   │   ├── calendar.tsx
│   │   ├── card.tsx
│   │   ├── carousel.tsx
│   │   ├── checkbox.tsx
│   │   ├── collapsible.tsx
│   │   ├── command.tsx
│   │   ├── copy-button.tsx
│   │   ├── devices.tsx
│   │   ├── dialog.tsx
│   │   ├── drawer.tsx
│   │   ├── dropdown-menu.tsx
│   │   ├── feature-preview.tsx
│   │   ├── file-upload.tsx
│   │   ├── form-hook.tsx
│   │   ├── form.tsx
│   │   ├── gauge.tsx
│   │   ├── hover-card.tsx
│   │   ├── input-group.tsx
│   │   ├── input-otp.tsx
│   │   ├── input.tsx
│   │   ├── label.tsx
│   │   ├── loading-dots.module.css
│   │   ├── loading-dots.tsx
│   │   ├── loading-spinner.module.css
│   │   ├── loading-spinner.tsx
│   │   ├── modal.tsx
│   │   ├── multi-select-v2.tsx
│   │   ├── nextra-filetree.tsx
│   │   ├── pagination.tsx
│   │   ├── phone-input.tsx
│   │   ├── popover.tsx
│   │   ├── portal.tsx
│   │   ├── progress.tsx
│   │   ├── radio-group.tsx
│   │   ├── resizable.tsx
│   │   ├── responsive-button.tsx
│   │   ├── rich-text-editor.tsx
│   │   ├── scroll-area.tsx
│   │   ├── select.tsx
│   │   ├── separator.tsx
│   │   ├── sheet.tsx
│   │   ├── sidebar.tsx
│   │   ├── single-select.tsx
│   │   ├── skeleton.tsx
│   │   ├── smart-date-time-picker.tsx
│   │   ├── sonner.tsx
│   │   ├── status-badge.tsx
│   │   ├── switch.tsx
│   │   ├── tab-select.tsx
│   │   ├── table.tsx
│   │   ├── tabs.tsx
│   │   ├── textarea.tsx
│   │   ├── timestamp-tooltip.tsx
│   │   ├── toggle-group.tsx
│   │   ├── toggle.tsx
│   │   ├── tooltip.tsx
│   │   └── upgrade-button.tsx
│   ├── upload-notification.tsx
│   ├── upload-zone.tsx
│   ├── user-agent-icon.tsx
│   ├── view/
│   │   ├── ScreenProtection.tsx
│   │   ├── access-form/
│   │   │   ├── access-form-theme.tsx
│   │   │   ├── agreement-section.tsx
│   │   │   ├── custom-fields-section.tsx
│   │   │   ├── email-section.tsx
│   │   │   ├── email-verification-form.tsx
│   │   │   ├── index.tsx
│   │   │   ├── name-section.tsx
│   │   │   └── password-section.tsx
│   │   ├── annotations/
│   │   │   ├── annotation-panel.tsx
│   │   │   └── annotation-toggle.tsx
│   │   ├── conversations/
│   │   │   └── sidebar.tsx
│   │   ├── custom-metatag.tsx
│   │   ├── dataroom/
│   │   │   ├── dataroom-document-view.tsx
│   │   │   ├── dataroom-view.tsx
│   │   │   ├── document-card.tsx
│   │   │   ├── document-upload-modal.tsx
│   │   │   ├── download-otp-verification.tsx
│   │   │   ├── downloads-panel.tsx
│   │   │   ├── folder-card.tsx
│   │   │   ├── index-file-dialog.tsx
│   │   │   ├── introduction-modal.tsx
│   │   │   ├── nav-dataroom.tsx
│   │   │   ├── pending-document-card.tsx
│   │   │   └── viewer-download-progress-modal.tsx
│   │   ├── document-view.tsx
│   │   ├── link-preview.tsx
│   │   ├── nav.tsx
│   │   ├── powered-by.tsx
│   │   ├── question.tsx
│   │   ├── report-form.tsx
│   │   ├── toolbar.tsx
│   │   ├── view-data.tsx
│   │   ├── viewer/
│   │   │   ├── advanced-excel-viewer.tsx
│   │   │   ├── away-poster.tsx
│   │   │   ├── dataroom-viewer.tsx
│   │   │   ├── download-only-viewer.tsx
│   │   │   ├── excel-viewer.tsx
│   │   │   ├── image-viewer.tsx
│   │   │   ├── notion-page.tsx
│   │   │   ├── pages-horizontal-viewer.tsx
│   │   │   ├── pages-vertical-viewer.tsx
│   │   │   ├── pdf-default-viewer.tsx
│   │   │   ├── video-player.tsx
│   │   │   ├── video-viewer.tsx
│   │   │   └── viewer-surface-theme.tsx
│   │   ├── visitor-graph.tsx
│   │   └── watermark-svg.tsx
│   ├── viewer-upload-component.tsx
│   ├── viewer-upload-zone.tsx
│   ├── visitors/
│   │   ├── contacts-document-table.tsx
│   │   ├── contacts-table.tsx
│   │   ├── data-table-pagination.tsx
│   │   ├── dataroom-view-stats.tsx
│   │   ├── dataroom-viewers.tsx
│   │   ├── dataroom-visitor-custom-fields.tsx
│   │   ├── dataroom-visitor-useragent.tsx
│   │   ├── dataroom-visitors-history.tsx
│   │   ├── dataroom-visitors-table.tsx
│   │   ├── document-view-stats.tsx
│   │   ├── visitor-avatar.tsx
│   │   ├── visitor-chart.tsx
│   │   ├── visitor-clicks.tsx
│   │   ├── visitor-custom-fields.tsx
│   │   ├── visitor-group-modal.tsx
│   │   ├── visitor-groups-section.tsx
│   │   ├── visitor-useragent-base.tsx
│   │   ├── visitor-useragent-placeholder.tsx
│   │   ├── visitor-useragent.tsx
│   │   ├── visitor-video-chart.tsx
│   │   └── visitors-table.tsx
│   ├── webhooks/
│   │   └── webhook-events.tsx
│   ├── welcome/
│   │   ├── containers/
│   │   │   ├── link-option-container.tsx
│   │   │   ├── onboarding-dataroom-link-options.tsx
│   │   │   ├── onboarding-link-options.tsx
│   │   │   └── upload-container.tsx
│   │   ├── dataroom-ai-generate.tsx
│   │   ├── dataroom-choice.tsx
│   │   ├── dataroom-trial.tsx
│   │   ├── dataroom-upload.tsx
│   │   ├── dataroom.tsx
│   │   ├── intro.tsx
│   │   ├── next.tsx
│   │   ├── notion-form.tsx
│   │   ├── select.tsx
│   │   ├── special-upload.tsx
│   │   └── upload.tsx
│   └── yearly-recap/
│       ├── globe.tsx
│       ├── index.ts
│       ├── yearly-recap-banner.tsx
│       └── yearly-recap-modal.tsx
├── components.json
├── context/
│   ├── pending-uploads-context.tsx
│   └── team-context.tsx
├── ee/
│   ├── LICENSE.md
│   ├── README.md
│   ├── emails/
│   │   └── pause-resume-reminder.tsx
│   ├── features/
│   │   ├── access-notifications/
│   │   │   ├── components/
│   │   │   │   └── blocked-email-attempt.tsx
│   │   │   ├── index.ts
│   │   │   └── lib/
│   │   │       ├── report-denied-access-attempt.ts
│   │   │       └── send-blocked-email-attempt.ts
│   │   ├── ai/
│   │   │   ├── components/
│   │   │   │   ├── agents-settings-card.tsx
│   │   │   │   ├── ai-indexing-status.tsx
│   │   │   │   ├── chat-message.tsx
│   │   │   │   ├── document-ai-dialog.tsx
│   │   │   │   ├── document-context-selector.tsx
│   │   │   │   ├── viewer-chat-panel.tsx
│   │   │   │   ├── viewer-chat-provider.tsx
│   │   │   │   ├── viewer-chat-toggle.tsx
│   │   │   │   └── viewer-thread-selector.tsx
│   │   │   ├── hooks/
│   │   │   │   └── use-ai-indexing-status.ts
│   │   │   ├── lib/
│   │   │   │   ├── chat/
│   │   │   │   │   ├── create-chat.ts
│   │   │   │   │   ├── generate-chat-title.ts
│   │   │   │   │   ├── get-filtered-dataroom-document-ids.ts
│   │   │   │   │   └── send-message.ts
│   │   │   │   ├── file-processing/
│   │   │   │   │   ├── extract-document-metadata.ts
│   │   │   │   │   └── process-document-for-vector-store.ts
│   │   │   │   ├── models/
│   │   │   │   │   ├── google.ts
│   │   │   │   │   └── openai.ts
│   │   │   │   ├── permissions/
│   │   │   │   │   └── validate-chat-access.ts
│   │   │   │   ├── stream/
│   │   │   │   │   └── parse-text-stream.ts
│   │   │   │   ├── trigger/
│   │   │   │   │   ├── add-file-to-vector-store.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── process-document-for-ai.ts
│   │   │   │   │   ├── process-excel-for-ai.ts
│   │   │   │   │   ├── process-image-for-ai.ts
│   │   │   │   │   ├── process-pdf-for-ai.ts
│   │   │   │   │   └── types.ts
│   │   │   │   └── vector-stores/
│   │   │   │       ├── create-dataroom-vector-store.ts
│   │   │   │       ├── create-team-vector-store.ts
│   │   │   │       ├── delete-vector-store.ts
│   │   │   │       ├── get-vector-store-info.ts
│   │   │   │       ├── remove-file-from-vector-store.ts
│   │   │   │       └── upload-file-to-vector-store.ts
│   │   │   └── schemas/
│   │   │       └── chat.ts
│   │   ├── billing/
│   │   │   ├── cancellation/
│   │   │   │   ├── api/
│   │   │   │   │   ├── automatic-unpause-route.ts
│   │   │   │   │   ├── cancel-route.ts
│   │   │   │   │   ├── cancellation-feedback-route.ts
│   │   │   │   │   ├── pause-route.ts
│   │   │   │   │   ├── reactivate-route.ts
│   │   │   │   │   ├── retention-route.ts
│   │   │   │   │   ├── send-pause-resume-notification.ts
│   │   │   │   │   └── unpause-route.ts
│   │   │   │   ├── components/
│   │   │   │   │   ├── cancellation-modal.tsx
│   │   │   │   │   ├── confirm-cancellation-modal.tsx
│   │   │   │   │   ├── feedback-modal.tsx
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── pause-subscription-modal.tsx
│   │   │   │   │   ├── reason-base-modal.tsx
│   │   │   │   │   ├── retention-offer-modal.tsx
│   │   │   │   │   └── schedule-call-modal.tsx
│   │   │   │   ├── constants.ts
│   │   │   │   ├── emails/
│   │   │   │   │   ├── components/
│   │   │   │   │   │   └── pause-resume-reminder.tsx
│   │   │   │   │   └── lib/
│   │   │   │   │       └── send-email-pause-resume-reminder.ts
│   │   │   │   └── lib/
│   │   │   │       ├── constants.ts
│   │   │   │       ├── is-team-paused.ts
│   │   │   │       └── trigger/
│   │   │   │           ├── pause-resume-notification.ts
│   │   │   │           └── unpause-task.ts
│   │   │   └── renewal-reminder/
│   │   │       ├── emails/
│   │   │       │   └── subscription-renewal-reminder.tsx
│   │   │       └── lib/
│   │   │           └── send-subscription-renewal-reminder.ts
│   │   ├── conversations/
│   │   │   ├── api/
│   │   │   │   ├── conversations-route.ts
│   │   │   │   ├── send-conversation-new-message-notification.ts
│   │   │   │   ├── send-conversation-team-member-notification.ts
│   │   │   │   ├── team-conversations-route.ts
│   │   │   │   ├── team-faqs-route.ts
│   │   │   │   └── toggle-conversations-route.ts
│   │   │   ├── components/
│   │   │   │   ├── dashboard/
│   │   │   │   │   ├── conversation-list-item.tsx
│   │   │   │   │   ├── conversations-not-enabled-banner.tsx
│   │   │   │   │   ├── edit-faq-modal.tsx
│   │   │   │   │   ├── link-option-conversation-section.tsx
│   │   │   │   │   └── publish-faq-modal.tsx
│   │   │   │   ├── shared/
│   │   │   │   │   ├── conversation-document-context.tsx
│   │   │   │   │   └── conversation-message.tsx
│   │   │   │   └── viewer/
│   │   │   │       ├── conversation-view-sidebar.tsx
│   │   │   │       └── faq-section.tsx
│   │   │   ├── emails/
│   │   │   │   ├── components/
│   │   │   │   │   ├── conversation-notification.tsx
│   │   │   │   │   └── conversation-team-notification.tsx
│   │   │   │   └── lib/
│   │   │   │       ├── send-conversation-notification.ts
│   │   │   │       └── send-conversation-team-notification.ts
│   │   │   ├── lib/
│   │   │   │   ├── api/
│   │   │   │   │   ├── conversations/
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── messages/
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   └── notifications/
│   │   │   │   │       └── index.ts
│   │   │   │   ├── schemas/
│   │   │   │   │   └── faq.ts
│   │   │   │   └── trigger/
│   │   │   │       └── conversation-message-notification.ts
│   │   │   └── pages/
│   │   │       ├── conversation-detail.tsx
│   │   │       ├── conversation-overview.tsx
│   │   │       └── faq-overview.tsx
│   │   ├── conversions/
│   │   │   └── python/
│   │   │       └── docx-sanitizer.py
│   │   ├── dataroom-invitations/
│   │   │   ├── api/
│   │   │   │   ├── group-invite.ts
│   │   │   │   ├── link-invite.ts
│   │   │   │   └── uninvited.ts
│   │   │   ├── components/
│   │   │   │   └── invite-viewers-modal.tsx
│   │   │   ├── emails/
│   │   │   │   ├── components/
│   │   │   │   │   └── dataroom-viewer-invitation.tsx
│   │   │   │   └── lib/
│   │   │   │       └── send-dataroom-viewer-invite.ts
│   │   │   └── lib/
│   │   │       ├── schema/
│   │   │       │   └── dataroom-invitations.ts
│   │   │       └── swr/
│   │   │           └── use-dataroom-invitations.ts
│   │   ├── permissions/
│   │   │   └── components/
│   │   │       ├── dataroom-link-sheet.tsx
│   │   │       └── permissions-sheet.tsx
│   │   ├── security/
│   │   │   ├── index.ts
│   │   │   ├── lib/
│   │   │   │   ├── fraud-prevention.ts
│   │   │   │   └── ratelimit.ts
│   │   │   └── sso/
│   │   │       ├── components/
│   │   │       │   ├── directory-sync-config-modal.tsx
│   │   │       │   ├── saml-config-modal.tsx
│   │   │       │   ├── sso-enforcement-toggle.tsx
│   │   │       │   └── sso-login.tsx
│   │   │       ├── constants.ts
│   │   │       ├── index.ts
│   │   │       └── product.ts
│   │   ├── storage/
│   │   │   ├── config.ts
│   │   │   └── s3-store.ts
│   │   ├── templates/
│   │   │   ├── api/
│   │   │   │   └── datarooms/
│   │   │   │       ├── apply-template.ts
│   │   │   │       └── generate.ts
│   │   │   ├── components/
│   │   │   │   └── dataroom-templates.tsx
│   │   │   ├── constants/
│   │   │   │   └── dataroom-templates.ts
│   │   │   ├── lib/
│   │   │   │   └── prompts.ts
│   │   │   └── schemas/
│   │   │       └── dataroom-templates.ts
│   │   └── workflows/
│   │       ├── components/
│   │       │   ├── index.ts
│   │       │   ├── step-form-dialog.tsx
│   │       │   ├── step-list.tsx
│   │       │   ├── workflow-access-view.tsx
│   │       │   ├── workflow-empty-state.tsx
│   │       │   ├── workflow-header.tsx
│   │       │   └── workflow-list.tsx
│   │       ├── index.ts
│   │       ├── lib/
│   │       │   ├── engine.ts
│   │       │   ├── types.ts
│   │       │   └── validation.ts
│   │       └── pages/
│   │           ├── workflow-detail.tsx
│   │           ├── workflow-new.tsx
│   │           └── workflow-overview.tsx
│   ├── limits/
│   │   ├── constants.ts
│   │   ├── handler.ts
│   │   ├── server.ts
│   │   └── swr-handler.ts
│   └── stripe/
│       ├── client.ts
│       ├── constants.ts
│       ├── functions/
│       │   ├── get-coupon-from-plan.ts
│       │   ├── get-display-name-from-plan.ts
│       │   ├── get-price-id-from-plan.ts
│       │   ├── get-quantity-from-plan.ts
│       │   └── get-subscription-item.ts
│       ├── index.ts
│       ├── utils.ts
│       └── webhooks/
│           ├── checkout-session-completed.ts
│           ├── customer-subscription-deleted.ts
│           ├── customer-subscription-updated.ts
│           └── invoice-upcoming.ts
├── lib/
│   ├── analytics/
│   │   └── index.ts
│   ├── api/
│   │   ├── auth/
│   │   │   ├── passkey.ts
│   │   │   └── token.ts
│   │   ├── documents/
│   │   │   └── process-document.ts
│   │   ├── domains/
│   │   │   ├── clear-team-redirects.ts
│   │   │   ├── redis.ts
│   │   │   └── validate-redirect-url.ts
│   │   ├── domains.ts
│   │   ├── links/
│   │   │   └── link-data.ts
│   │   ├── notification-helper.ts
│   │   ├── teams/
│   │   │   └── is-saml-enforced-for-email-domain.ts
│   │   └── views/
│   │       └── send-webhook-event.ts
│   ├── auth/
│   │   ├── dataroom-auth.ts
│   │   ├── link-session.ts
│   │   └── preview-auth.ts
│   ├── constants/
│   │   └── folder-constants.ts
│   ├── constants.ts
│   ├── cron/
│   │   ├── index.ts
│   │   └── verify-qstash.ts
│   ├── dataroom/
│   │   ├── build-folder-hierarchy.ts
│   │   └── index-generator.ts
│   ├── documents/
│   │   ├── create-document.ts
│   │   ├── get-file-helper.ts
│   │   ├── move-dataroom-documents.ts
│   │   ├── move-dataroom-folders.ts
│   │   ├── move-documents.ts
│   │   └── move-folder.ts
│   ├── domains.ts
│   ├── dub.ts
│   ├── edge-config/
│   │   ├── blacklist.ts
│   │   ├── custom-email.ts
│   │   └── trusted-teams.ts
│   ├── emails/
│   │   ├── process-dataroom-digest.ts
│   │   ├── send-custom-domain-setup.ts
│   │   ├── send-dataroom-digest-notification.ts
│   │   ├── send-dataroom-info.ts
│   │   ├── send-dataroom-notification.ts
│   │   ├── send-dataroom-trial-24h.ts
│   │   ├── send-dataroom-trial-end.ts
│   │   ├── send-dataroom-trial.ts
│   │   ├── send-dataroom-upload-notification.ts
│   │   ├── send-deleted-domain.ts
│   │   ├── send-download-ready-email.ts
│   │   ├── send-email-otp-verification.ts
│   │   ├── send-export-ready-email.ts
│   │   ├── send-hundred-views-congrats.ts
│   │   ├── send-invalid-domain.ts
│   │   ├── send-mail-verification.ts
│   │   ├── send-onboarding.ts
│   │   ├── send-slack-integration.ts
│   │   ├── send-teammate-invite.ts
│   │   ├── send-thousand-views-congrats.ts
│   │   ├── send-upgrade-month-checkin.ts
│   │   ├── send-upgrade-personal-welcome.ts
│   │   ├── send-upgrade-plan.ts
│   │   ├── send-upgrade-six-months-checkin.ts
│   │   ├── send-verification-request.ts
│   │   ├── send-viewed-dataroom-paused.ts
│   │   ├── send-viewed-dataroom.ts
│   │   ├── send-viewed-document-paused.ts
│   │   ├── send-viewed-document.ts
│   │   └── send-welcome.ts
│   ├── errorHandler.ts
│   ├── featureFlags/
│   │   └── index.ts
│   ├── files/
│   │   ├── aws-client.ts
│   │   ├── bulk-download-presign.ts
│   │   ├── bulk-download.ts
│   │   ├── copy-file-server.ts
│   │   ├── copy-file-to-bucket-server.ts
│   │   ├── delete-file-server.ts
│   │   ├── delete-team-files-server.ts
│   │   ├── get-file.ts
│   │   ├── put-file-server.ts
│   │   ├── put-file.ts
│   │   ├── stream-file-server.ts
│   │   ├── tus-redis-locker.ts
│   │   ├── tus-upload.ts
│   │   └── viewer-tus-upload.ts
│   ├── folders/
│   │   └── create-folder.ts
│   ├── hanko.ts
│   ├── hooks/
│   │   ├── use-breakpoint.ts
│   │   ├── use-copy-to-clipboard.ts
│   │   ├── use-dataroom-permissions.ts
│   │   ├── use-disable-print.ts
│   │   ├── use-feature-flags.ts
│   │   ├── use-is-admin.ts
│   │   ├── use-lazy-pages.ts
│   │   └── use-mobile.tsx
│   ├── id-helper.ts
│   ├── incoming-webhooks/
│   │   └── index.ts
│   ├── integrations/
│   │   ├── install.ts
│   │   └── slack/
│   │       ├── client.ts
│   │       ├── env.ts
│   │       ├── events.ts
│   │       ├── install.ts
│   │       ├── templates.ts
│   │       ├── types.ts
│   │       ├── uninstall.ts
│   │       └── utils.ts
│   ├── jackson.ts
│   ├── middleware/
│   │   ├── app.ts
│   │   ├── domain.ts
│   │   ├── incoming-webhooks.ts
│   │   └── posthog.ts
│   ├── notion/
│   │   ├── config.ts
│   │   ├── index.ts
│   │   └── utils.ts
│   ├── openai.ts
│   ├── posthog.ts
│   ├── prisma.ts
│   ├── redis/
│   │   └── dataroom-notification-queue.ts
│   ├── redis-download-job-store.ts
│   ├── redis-job-store.ts
│   ├── redis.ts
│   ├── resend.ts
│   ├── sheet/
│   │   └── index.ts
│   ├── swr/
│   │   ├── use-agreements.ts
│   │   ├── use-annotations.ts
│   │   ├── use-billing.ts
│   │   ├── use-brand.ts
│   │   ├── use-dataroom-document-stats.ts
│   │   ├── use-dataroom-groups.ts
│   │   ├── use-dataroom-permission-groups.ts
│   │   ├── use-dataroom-stats.ts
│   │   ├── use-dataroom-view-document-stats.ts
│   │   ├── use-dataroom.ts
│   │   ├── use-datarooms-simple.ts
│   │   ├── use-datarooms.ts
│   │   ├── use-document-overview.ts
│   │   ├── use-document-preview.ts
│   │   ├── use-document-stats.ts
│   │   ├── use-document.ts
│   │   ├── use-documents.ts
│   │   ├── use-domains.ts
│   │   ├── use-folders.ts
│   │   ├── use-invitations.ts
│   │   ├── use-invoices.ts
│   │   ├── use-limits.ts
│   │   ├── use-link.ts
│   │   ├── use-passkeys.ts
│   │   ├── use-saml.ts
│   │   ├── use-scim.ts
│   │   ├── use-slack-channels.ts
│   │   ├── use-slack-integration.ts
│   │   ├── use-stats.ts
│   │   ├── use-tags.ts
│   │   ├── use-team-ai.ts
│   │   ├── use-team-settings.ts
│   │   ├── use-team.ts
│   │   ├── use-teams.ts
│   │   ├── use-viewer.ts
│   │   ├── use-viewers.ts
│   │   └── use-visitor-groups.ts
│   ├── team/
│   │   └── helper.ts
│   ├── tinybird/
│   │   ├── README.md
│   │   ├── datasources/
│   │   │   ├── click_events.datasource
│   │   │   ├── page_views.datasource
│   │   │   ├── pm_click_events.datasource
│   │   │   ├── video_views.datasource
│   │   │   └── webhook_events.datasource
│   │   ├── endpoints/
│   │   │   ├── get_click_events_by_view.pipe
│   │   │   ├── get_dataroom_view_document_stats.pipe
│   │   │   ├── get_document_duration_per_viewer.pipe
│   │   │   ├── get_page_duration_per_view.pipe
│   │   │   ├── get_total_average_page_duration.pipe
│   │   │   ├── get_total_dataroom_duration.pipe
│   │   │   ├── get_total_document_duration.pipe
│   │   │   ├── get_total_link_duration.pipe
│   │   │   ├── get_total_team_duration.pipe
│   │   │   ├── get_total_viewer_duration.pipe
│   │   │   ├── get_useragent_per_view.pipe
│   │   │   ├── get_video_events_by_document.pipe
│   │   │   ├── get_video_events_by_view.pipe
│   │   │   ├── get_view_completion_stats.pipe
│   │   │   └── get_webhook_events.pipe
│   │   ├── index.ts
│   │   ├── pipes.ts
│   │   └── publish.ts
│   ├── tracking/
│   │   ├── record-link-view.ts
│   │   ├── safe-page-view-tracker.ts
│   │   ├── tracking-config.ts
│   │   └── video-tracking.ts
│   ├── trigger/
│   │   ├── automatic-unpause.ts
│   │   ├── bulk-download.ts
│   │   ├── cleanup-expired-exports.ts
│   │   ├── conversation-message-notification.ts
│   │   ├── convert-files.ts
│   │   ├── dataroom-change-notification.ts
│   │   ├── dataroom-upload-notification.ts
│   │   ├── export-visits.ts
│   │   ├── optimize-video-files.ts
│   │   ├── pause-reminder-notification.ts
│   │   ├── pdf-to-image-route.ts
│   │   └── send-scheduled-email.ts
│   ├── types/
│   │   ├── document-preview.ts
│   │   └── index-file.ts
│   ├── types.ts
│   ├── unsend.ts
│   ├── utils/
│   │   ├── calculate-hierarchical-indexes.ts
│   │   ├── create-adaptive-surface-palette.ts
│   │   ├── csv.ts
│   │   ├── decode-base64url.ts
│   │   ├── determine-text-color.ts
│   │   ├── email-domain.ts
│   │   ├── generate-checksum.ts
│   │   ├── generate-jwt.ts
│   │   ├── generate-otp.ts
│   │   ├── generate-trigger-auth-token.ts
│   │   ├── generate-trigger-status.ts
│   │   ├── geo.ts
│   │   ├── get-content-type.ts
│   │   ├── get-file-icon.tsx
│   │   ├── get-file-size-limits.ts
│   │   ├── get-page-number-count.ts
│   │   ├── get-search-params.ts
│   │   ├── global-block-list.ts
│   │   ├── hierarchical-display.ts
│   │   ├── ip.ts
│   │   ├── link-url.ts
│   │   ├── reliable-tracking.ts
│   │   ├── resize-image.ts
│   │   ├── sanitize-html.ts
│   │   ├── sort-items-by-index-name.ts
│   │   ├── trigger-utils.ts
│   │   ├── unsubscribe.ts
│   │   ├── use-at-bottom.ts
│   │   ├── use-copy-to-clipboard.ts
│   │   ├── use-enter-submit.ts
│   │   ├── use-media-query.ts
│   │   ├── use-progress-status.ts
│   │   ├── user-agent.ts
│   │   └── validate-email.ts
│   ├── utils.ts
│   ├── webhook/
│   │   ├── constants.ts
│   │   ├── send-webhooks.ts
│   │   ├── signature.ts
│   │   ├── transform.ts
│   │   ├── triggers/
│   │   │   ├── document-created.ts
│   │   │   └── link-created.ts
│   │   └── types.ts
│   ├── webstorage.ts
│   ├── year-in-review/
│   │   ├── calculate-percentile.ts
│   │   ├── get-stats.ts
│   │   ├── index.ts
│   │   └── send-emails.ts
│   └── zod/
│       ├── schemas/
│       │   ├── folders.ts
│       │   ├── multipart.ts
│       │   ├── notifications.ts
│       │   ├── presets.ts
│       │   └── webhooks.ts
│       └── url-validation.ts
├── middleware.ts
├── next.config.mjs
├── package.json
├── pages/
│   ├── 404.tsx
│   ├── _app.tsx
│   ├── _document.tsx
│   ├── account/
│   │   ├── general.tsx
│   │   └── security.tsx
│   ├── api/
│   │   ├── account/
│   │   │   ├── index.ts
│   │   │   └── passkeys.ts
│   │   ├── analytics/
│   │   │   └── index.ts
│   │   ├── auth/
│   │   │   └── [...nextauth].ts
│   │   ├── conversations/
│   │   │   └── [[...conversations]].ts
│   │   ├── feedback/
│   │   │   └── index.ts
│   │   ├── file/
│   │   │   ├── browser-upload.ts
│   │   │   ├── image-upload.ts
│   │   │   ├── notion/
│   │   │   │   └── index.ts
│   │   │   ├── s3/
│   │   │   │   ├── get-presigned-get-url-proxy.ts
│   │   │   │   ├── get-presigned-get-url.ts
│   │   │   │   ├── get-presigned-post-url.ts
│   │   │   │   └── multipart.ts
│   │   │   ├── tus/
│   │   │   │   └── [[...file]].ts
│   │   │   └── tus-viewer/
│   │   │       └── [[...file]].ts
│   │   ├── health.ts
│   │   ├── internal/
│   │   │   └── billing/
│   │   │       └── automatic-unpause.ts
│   │   ├── jobs/
│   │   │   ├── get-thumbnail.ts
│   │   │   ├── process-download-batch.ts
│   │   │   ├── send-conversation-new-message-notification.ts
│   │   │   ├── send-conversation-team-member-notification.ts
│   │   │   ├── send-dataroom-new-document-notification.ts
│   │   │   ├── send-dataroom-upload-notification.ts
│   │   │   ├── send-notification.ts
│   │   │   └── send-pause-resume-notification.ts
│   │   ├── links/
│   │   │   ├── [id]/
│   │   │   │   ├── annotations.ts
│   │   │   │   ├── archive.ts
│   │   │   │   ├── documents/
│   │   │   │   │   ├── [documentId]/
│   │   │   │   │   │   └── annotations.ts
│   │   │   │   │   └── [documentId].ts
│   │   │   │   ├── duplicate.ts
│   │   │   │   ├── index.ts
│   │   │   │   └── preview.ts
│   │   │   ├── domains/
│   │   │   │   └── [...domainSlug].ts
│   │   │   ├── download/
│   │   │   │   ├── [jobId].ts
│   │   │   │   ├── bulk.ts
│   │   │   │   ├── by-email.ts
│   │   │   │   ├── dataroom-document.ts
│   │   │   │   ├── dataroom-folder.ts
│   │   │   │   ├── file/
│   │   │   │   │   └── [jobId]/
│   │   │   │   │       └── [partIndex].ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── jobs.ts
│   │   │   │   └── verify.ts
│   │   │   ├── generate-index.ts
│   │   │   └── index.ts
│   │   ├── mupdf/
│   │   │   ├── annotate-document.ts
│   │   │   ├── convert-page.ts
│   │   │   └── get-pages.ts
│   │   ├── notification-preferences/
│   │   │   └── dataroom.ts
│   │   ├── passkeys/
│   │   │   └── register.ts
│   │   ├── progress-token.ts
│   │   ├── record_click.ts
│   │   ├── record_reaction.ts
│   │   ├── record_video_view.ts
│   │   ├── record_view.ts
│   │   ├── report.ts
│   │   ├── revalidate.ts
│   │   ├── stripe/
│   │   │   ├── webhook-old.ts
│   │   │   └── webhook.ts
│   │   ├── teams/
│   │   │   ├── [teamId]/
│   │   │   │   ├── agreements/
│   │   │   │   │   ├── [agreementId]/
│   │   │   │   │   │   ├── download.ts
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   └── index.ts
│   │   │   │   ├── ai-settings.ts
│   │   │   │   ├── billing/
│   │   │   │   │   ├── cancel.ts
│   │   │   │   │   ├── cancellation-feedback.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── invoices.ts
│   │   │   │   │   ├── manage.ts
│   │   │   │   │   ├── pause.ts
│   │   │   │   │   ├── plan.ts
│   │   │   │   │   ├── reactivate.ts
│   │   │   │   │   ├── retention-offer.ts
│   │   │   │   │   ├── unpause.ts
│   │   │   │   │   └── upgrade.ts
│   │   │   │   ├── branding.ts
│   │   │   │   ├── change-role.ts
│   │   │   │   ├── datarooms/
│   │   │   │   │   ├── [id]/
│   │   │   │   │   │   ├── apply-permissions.ts
│   │   │   │   │   │   ├── apply-template.ts
│   │   │   │   │   │   ├── branding.ts
│   │   │   │   │   │   ├── calculate-indexes.ts
│   │   │   │   │   │   ├── conversations/
│   │   │   │   │   │   │   ├── [[...conversations]].ts
│   │   │   │   │   │   │   └── toggle-conversations.ts
│   │   │   │   │   │   ├── documents/
│   │   │   │   │   │   │   ├── [documentId]/
│   │   │   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   │   │   └── stats.ts
│   │   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   │   └── move.ts
│   │   │   │   │   │   ├── download/
│   │   │   │   │   │   │   ├── [jobId].ts
│   │   │   │   │   │   │   ├── bulk.ts
│   │   │   │   │   │   │   └── jobs.ts
│   │   │   │   │   │   ├── duplicate.ts
│   │   │   │   │   │   ├── export-visits.ts
│   │   │   │   │   │   ├── faqs/
│   │   │   │   │   │   │   ├── [faqId].ts
│   │   │   │   │   │   │   └── index.ts
│   │   │   │   │   │   ├── folders/
│   │   │   │   │   │   │   ├── [...name].ts
│   │   │   │   │   │   │   ├── documents/
│   │   │   │   │   │   │   │   └── [...name].ts
│   │   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   │   ├── manage/
│   │   │   │   │   │   │   │   ├── [folderId]/
│   │   │   │   │   │   │   │   │   ├── dataroom-to-dataroom.ts
│   │   │   │   │   │   │   │   │   └── index.ts
│   │   │   │   │   │   │   │   └── index.ts
│   │   │   │   │   │   │   ├── move.ts
│   │   │   │   │   │   │   └── parents/
│   │   │   │   │   │   │       └── [...name].ts
│   │   │   │   │   │   ├── generate-index.ts
│   │   │   │   │   │   ├── groups/
│   │   │   │   │   │   │   ├── [groupId]/
│   │   │   │   │   │   │   │   ├── export-visits.ts
│   │   │   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   │   │   ├── invite.ts
│   │   │   │   │   │   │   │   ├── links.ts
│   │   │   │   │   │   │   │   ├── members/
│   │   │   │   │   │   │   │   │   ├── [memberId].ts
│   │   │   │   │   │   │   │   │   └── index.ts
│   │   │   │   │   │   │   │   ├── permissions.ts
│   │   │   │   │   │   │   │   ├── uninvited.ts
│   │   │   │   │   │   │   │   └── views/
│   │   │   │   │   │   │   │       └── index.ts
│   │   │   │   │   │   │   └── index.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── links/
│   │   │   │   │   │   │   └── [linkId]/
│   │   │   │   │   │   │       └── invite.ts
│   │   │   │   │   │   ├── links.ts
│   │   │   │   │   │   ├── permission-groups/
│   │   │   │   │   │   │   ├── [permissionGroupId].ts
│   │   │   │   │   │   │   └── index.ts
│   │   │   │   │   │   ├── reorder.ts
│   │   │   │   │   │   ├── stats.ts
│   │   │   │   │   │   ├── users/
│   │   │   │   │   │   │   └── index.ts
│   │   │   │   │   │   ├── viewers/
│   │   │   │   │   │   │   └── index.ts
│   │   │   │   │   │   ├── views/
│   │   │   │   │   │   │   ├── [viewId]/
│   │   │   │   │   │   │   │   ├── custom-fields.ts
│   │   │   │   │   │   │   │   ├── document-stats.ts
│   │   │   │   │   │   │   │   ├── history.ts
│   │   │   │   │   │   │   │   └── user-agent.ts
│   │   │   │   │   │   │   └── index.ts
│   │   │   │   │   │   └── views-count.ts
│   │   │   │   │   ├── create-from-folder.ts
│   │   │   │   │   ├── generate-ai-structure.ts
│   │   │   │   │   ├── generate-ai.ts
│   │   │   │   │   ├── generate.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── trial.ts
│   │   │   │   ├── documents/
│   │   │   │   │   ├── [id]/
│   │   │   │   │   │   ├── add-to-dataroom.ts
│   │   │   │   │   │   ├── advanced-mode.ts
│   │   │   │   │   │   ├── annotations/
│   │   │   │   │   │   │   ├── [annotationId]/
│   │   │   │   │   │   │   │   └── images.ts
│   │   │   │   │   │   │   ├── [annotationId].ts
│   │   │   │   │   │   │   └── index.ts
│   │   │   │   │   │   ├── change-orientation.ts
│   │   │   │   │   │   ├── check-notion-accessibility.ts
│   │   │   │   │   │   ├── duplicate.ts
│   │   │   │   │   │   ├── export-visits.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── links.ts
│   │   │   │   │   │   ├── overview.ts
│   │   │   │   │   │   ├── preview-data.ts
│   │   │   │   │   │   ├── preview-pages.ts
│   │   │   │   │   │   ├── stats.ts
│   │   │   │   │   │   ├── toggle-dark-mode.ts
│   │   │   │   │   │   ├── toggle-download-only.ts
│   │   │   │   │   │   ├── update-link-url.ts
│   │   │   │   │   │   ├── update-name.ts
│   │   │   │   │   │   ├── update-notion-url.ts
│   │   │   │   │   │   ├── versions/
│   │   │   │   │   │   │   └── index.ts
│   │   │   │   │   │   ├── video-analytics.ts
│   │   │   │   │   │   ├── views/
│   │   │   │   │   │   │   ├── [viewId]/
│   │   │   │   │   │   │   │   ├── click-events.ts
│   │   │   │   │   │   │   │   ├── custom-fields.ts
│   │   │   │   │   │   │   │   ├── stats.ts
│   │   │   │   │   │   │   │   ├── user-agent.ts
│   │   │   │   │   │   │   │   └── video-stats.ts
│   │   │   │   │   │   │   └── index.ts
│   │   │   │   │   │   └── views-count.ts
│   │   │   │   │   ├── agreement.ts
│   │   │   │   │   ├── document-processing-status.ts
│   │   │   │   │   ├── hidden/
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── hide.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── move.ts
│   │   │   │   │   ├── search.ts
│   │   │   │   │   └── update.ts
│   │   │   │   ├── domains/
│   │   │   │   │   ├── [domain]/
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── validate.ts
│   │   │   │   │   │   └── verify.ts
│   │   │   │   │   └── index.ts
│   │   │   │   ├── enable-advanced-mode.ts
│   │   │   │   ├── export-jobs/
│   │   │   │   │   ├── [exportId]/
│   │   │   │   │   │   └── send-email.ts
│   │   │   │   │   └── [exportId].ts
│   │   │   │   ├── export-jobs.ts
│   │   │   │   ├── folders/
│   │   │   │   │   ├── [...name].ts
│   │   │   │   │   ├── documents/
│   │   │   │   │   │   └── [...name].ts
│   │   │   │   │   ├── hide.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── manage/
│   │   │   │   │   │   ├── [folderId]/
│   │   │   │   │   │   │   ├── add-to-dataroom.ts
│   │   │   │   │   │   │   └── index.ts
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── move.ts
│   │   │   │   │   └── parents/
│   │   │   │   │       └── [...name].ts
│   │   │   │   ├── global-block-list.ts
│   │   │   │   ├── ignored-domains.ts
│   │   │   │   ├── incoming-webhooks/
│   │   │   │   │   └── index.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── integrations/
│   │   │   │   │   └── slack/
│   │   │   │   │       ├── channels.ts
│   │   │   │   │       └── index.ts
│   │   │   │   ├── invitations/
│   │   │   │   │   ├── accept.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── resend.ts
│   │   │   │   ├── invite.ts
│   │   │   │   ├── limits.ts
│   │   │   │   ├── links/
│   │   │   │   │   └── [id]/
│   │   │   │   │       ├── index.ts
│   │   │   │   │       └── visits.ts
│   │   │   │   ├── presets/
│   │   │   │   │   ├── [id].ts
│   │   │   │   │   └── index.ts
│   │   │   │   ├── remove-teammate.ts
│   │   │   │   ├── settings.ts
│   │   │   │   ├── survey.ts
│   │   │   │   ├── tags/
│   │   │   │   │   ├── [id]/
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   └── index.ts
│   │   │   │   ├── tokens/
│   │   │   │   │   └── index.ts
│   │   │   │   ├── update-advanced-mode.ts
│   │   │   │   ├── update-name.ts
│   │   │   │   ├── update-replicate-folders.ts
│   │   │   │   ├── viewers/
│   │   │   │   │   ├── [id]/
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   └── index.ts
│   │   │   │   ├── views/
│   │   │   │   │   └── [id]/
│   │   │   │   │       └── archive.ts
│   │   │   │   ├── visitor-groups/
│   │   │   │   │   ├── [groupId]/
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   └── index.ts
│   │   │   │   ├── webhooks/
│   │   │   │   │   ├── [id]/
│   │   │   │   │   │   ├── events.ts
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   └── index.ts
│   │   │   │   ├── workflow-links.ts
│   │   │   │   └── yearly-recap.ts
│   │   │   └── index.ts
│   │   ├── unsubscribe/
│   │   │   ├── dataroom/
│   │   │   │   └── index.ts
│   │   │   └── yir/
│   │   │       └── index.ts
│   │   ├── user/
│   │   │   └── subscribe.ts
│   │   └── webhooks/
│   │       └── services/
│   │           └── [...path]/
│   │               └── index.ts
│   ├── branding.tsx
│   ├── dashboard.tsx
│   ├── datarooms/
│   │   ├── [id]/
│   │   │   ├── analytics/
│   │   │   │   └── index.tsx
│   │   │   ├── branding/
│   │   │   │   └── index.tsx
│   │   │   ├── conversations/
│   │   │   │   ├── [conversationId]/
│   │   │   │   │   └── index.tsx
│   │   │   │   ├── faqs/
│   │   │   │   │   └── index.tsx
│   │   │   │   └── index.tsx
│   │   │   ├── documents/
│   │   │   │   ├── [...name].tsx
│   │   │   │   └── index.tsx
│   │   │   ├── groups/
│   │   │   │   ├── [groupId]/
│   │   │   │   │   ├── group-analytics.tsx
│   │   │   │   │   ├── index.tsx
│   │   │   │   │   ├── links.tsx
│   │   │   │   │   ├── members.tsx
│   │   │   │   │   └── permissions.tsx
│   │   │   │   └── index.tsx
│   │   │   ├── index.tsx
│   │   │   ├── permissions/
│   │   │   │   └── index.tsx
│   │   │   ├── settings/
│   │   │   │   ├── downloads.tsx
│   │   │   │   ├── file-permissions.tsx
│   │   │   │   ├── index.tsx
│   │   │   │   ├── introduction.tsx
│   │   │   │   └── notifications.tsx
│   │   │   └── users/
│   │   │       └── index.tsx
│   │   └── index.tsx
│   ├── documents/
│   │   ├── [id]/
│   │   │   └── index.tsx
│   │   ├── hidden.tsx
│   │   ├── index.tsx
│   │   ├── new.tsx
│   │   └── tree/
│   │       └── [...name].tsx
│   ├── entrance_ppreview_demo.tsx
│   ├── nav_ppreview_demo.tsx
│   ├── notification-preferences.tsx
│   ├── room_ppreview_demo.tsx
│   ├── settings/
│   │   ├── agreements.tsx
│   │   ├── ai.tsx
│   │   ├── billing/
│   │   │   └── invoices.tsx
│   │   ├── billing.tsx
│   │   ├── domains.tsx
│   │   ├── general.tsx
│   │   ├── incoming-webhooks.tsx
│   │   ├── people.tsx
│   │   ├── presets/
│   │   │   ├── [id].tsx
│   │   │   ├── index.tsx
│   │   │   └── new.tsx
│   │   ├── security.tsx
│   │   ├── slack.tsx
│   │   ├── tags.tsx
│   │   ├── tokens.tsx
│   │   ├── upgrade-holiday-offer.tsx
│   │   ├── upgrade.tsx
│   │   └── webhooks/
│   │       ├── [id]/
│   │       │   └── index.tsx
│   │       ├── index.tsx
│   │       └── new.tsx
│   ├── unsubscribe.tsx
│   ├── view/
│   │   ├── [linkId]/
│   │   │   ├── d/
│   │   │   │   └── [documentId].tsx
│   │   │   ├── downloads.tsx
│   │   │   ├── embed.tsx
│   │   │   └── index.tsx
│   │   └── domains/
│   │       └── [domain]/
│   │           └── [slug]/
│   │               ├── d/
│   │               │   └── [documentId].tsx
│   │               ├── downloads.tsx
│   │               └── index.tsx
│   ├── visitors/
│   │   ├── [id]/
│   │   │   └── index.tsx
│   │   └── index.tsx
│   ├── welcome.tsx
│   └── workflows/
│       ├── [id].tsx
│       ├── index.tsx
│       └── new.tsx
├── pkgx.yaml
├── postcss.config.js
├── prettier.config.js
├── prisma/
│   ├── README.md
│   ├── add-migration.sh
│   ├── migrations/
│   │   ├── 20230912150657_initialize/
│   │   │   └── migration.sql
│   │   ├── 202310122339_NewColumnInLinkTable/
│   │   │   └── migration.sql
│   │   ├── 20231013165123_create_document_version/
│   │   │   └── migration.sql
│   │   ├── 20231014200337_create_document_pages/
│   │   │   └── migration.sql
│   │   ├── 202310311254_NewColumnEnableNotificationLinkTable/
│   │   │   └── migration.sql
│   │   ├── 20231105152632_create_team/
│   │   │   └── migration.sql
│   │   ├── 20231113051339_create_sent_email/
│   │   │   └── migration.sql
│   │   ├── 20231114054509_add_domain_to_sent_emails/
│   │   │   └── migration.sql
│   │   ├── 20231116093816_update_invitations/
│   │   │   └── migration.sql
│   │   ├── 20231127062841_add_conversation/
│   │   │   └── migration.sql
│   │   ├── 20231128064540_add_indices/
│   │   │   └── migration.sql
│   │   ├── 20231204070250_remove_trial/
│   │   │   └── migration.sql
│   │   ├── 20231207081407_add_reactions/
│   │   │   └── migration.sql
│   │   ├── 20240110233134_add_disable_feedback/
│   │   │   └── migration.sql
│   │   ├── 20240117020456_add_branding/
│   │   │   └── migration.sql
│   │   ├── 20240202052149_add_email_authentication_to_link_and_view/
│   │   │   └── migration.sql
│   │   ├── 20240205170242_embedded_links/
│   │   │   └── migration.sql
│   │   ├── 20240212081614_add_downloaded_time_to_view/
│   │   │   └── migration.sql
│   │   ├── 20240215035046_add_allow_deny_list_to_links/
│   │   │   └── migration.sql
│   │   ├── 20240221042933_add_document_storage_type_enum/
│   │   │   └── migration.sql
│   │   ├── 20240313100203_add_folders/
│   │   │   └── migration.sql
│   │   ├── 20240327102407_add_dataroom/
│   │   │   └── migration.sql
│   │   ├── 20240330062000_add_viewtype/
│   │   │   └── migration.sql
│   │   ├── 20240401000000_add_dataroom_brand/
│   │   │   └── migration.sql
│   │   ├── 20240408000000_add_viewer/
│   │   │   └── migration.sql
│   │   ├── 20240415000000_add_feedback/
│   │   │   └── migration.sql
│   │   ├── 20240424152839_add_screenprotection_to_link/
│   │   │   └── migration.sql
│   │   ├── 20240511000000_add_team_limits/
│   │   │   └── migration.sql
│   │   ├── 20240520000000_add_vertical_to_document_version/
│   │   │   └── migration.sql
│   │   ├── 20240521000000_add_manager_role/
│   │   │   └── migration.sql
│   │   ├── 20240611000000_add_agreements/
│   │   │   └── migration.sql
│   │   ├── 20240712000000_add_page_metadata/
│   │   │   └── migration.sql
│   │   ├── 20240720000000_change_owner_dependecy/
│   │   │   └── migration.sql
│   │   ├── 20240730000000_update_link_defaults/
│   │   │   └── migration.sql
│   │   ├── 20240731000000_add_link_show_banner/
│   │   │   └── migration.sql
│   │   ├── 20240809000000_add_dataroom_order_index/
│   │   │   └── migration.sql
│   │   ├── 20240821000000_add_require_name/
│   │   │   └── migration.sql
│   │   ├── 20240830000000_add_watermarks/
│   │   │   └── migration.sql
│   │   ├── 20240901000000_add_domain_default/
│   │   │   └── migration.sql
│   │   ├── 20240902000000_add_link_presets/
│   │   │   └── migration.sql
│   │   ├── 20240911000000_add_dataroom_groups_permissions/
│   │   │   └── migration.sql
│   │   ├── 20240915000000_add_advanced_mode/
│   │   │   └── migration.sql
│   │   ├── 20240916000000_add_content_type_to_document/
│   │   │   └── migration.sql
│   │   ├── 20240921000000_add_viewer_migration/
│   │   │   └── migration.sql
│   │   ├── 20241004024010_add_favicon_column/
│   │   │   └── migration.sql
│   │   ├── 20241020000000_add_teamid_to_link_and_view/
│   │   │   └── migration.sql
│   │   ├── 20241029000000_add_archived_view/
│   │   │   └── migration.sql
│   │   ├── 20241107000000_add_tokens_and_webhooks/
│   │   │   └── migration.sql
│   │   ├── 20241118000000_add_filesize_and_downloadonly/
│   │   │   └── migration.sql
│   │   ├── 20241123000000_add_viewer_notification_preferences/
│   │   │   └── migration.sql
│   │   ├── 20241126000000_add_screen_shield/
│   │   │   └── migration.sql
│   │   ├── 20241208000000_add_webhooks/
│   │   │   └── migration.sql
│   │   ├── 20241212000000_add_yir/
│   │   │   └── migration.sql
│   │   ├── 20241231_add_dataroom_allow_bulk_download/
│   │   │   └── migration.sql
│   │   ├── 20250106000000_add_download_type/
│   │   │   └── migration.sql
│   │   ├── 20250110000000_add_length_to_document_version/
│   │   │   └── migration.sql
│   │   ├── 20250113000000_add_custom_fields/
│   │   │   └── migration.sql
│   │   ├── 20250204000000_add_anonymous_group/
│   │   │   └── migration.sql
│   │   ├── 20250217000000_add_contactid_to_user/
│   │   │   └── migration.sql
│   │   ├── 20250217000000_remove_link_column/
│   │   │   └── migration.sql
│   │   ├── 20250310000000_rename_conversation_table/
│   │   │   └── migration.sql
│   │   ├── 20250404000000_add_questions_answer_conversation/
│   │   │   └── migration.sql
│   │   ├── 20250413000000_add_dataroom_upload/
│   │   │   └── migration.sql
│   │   ├── 20250425000000_update_link_presets/
│   │   │   └── migration.sql
│   │   ├── 20250428000000_add_tags/
│   │   │   └── migration.sql
│   │   ├── 20250502000000_add_additional_present_fields/
│   │   │   └── migration.sql
│   │   ├── 20250511000000_add_index_file_to_links/
│   │   │   └── migration.sql
│   │   ├── 20250513000000_add_notification_to_dataroom/
│   │   │   └── migration.sql
│   │   ├── 20250516000000_add_advanced_mode_to_team/
│   │   │   └── migration.sql
│   │   ├── 20250526000000_add_status_to_users/
│   │   │   └── migration.sql
│   │   ├── 20250607000000_add_notification_to_presets/
│   │   │   └── migration.sql
│   │   ├── 20250612000000_add_permissions_to_link/
│   │   │   └── migration.sql
│   │   ├── 20250624042156_viewers_performance_optimization/
│   │   │   └── migration.sql
│   │   ├── 20250711000000_add_ignored_domains_to_team/
│   │   │   └── migration.sql
│   │   ├── 20250715042921_add_global_block_list_to_team/
│   │   │   └── migration.sql
│   │   ├── 20250716000000_add_dataroom_permission_defaults/
│   │   │   └── migration.sql
│   │   ├── 20250725000000_add_welcome_message_to_dataroom/
│   │   │   └── migration.sql
│   │   ├── 20250730000000_change_file_size_bigint/
│   │   │   └── migration.sql
│   │   ├── 20250806000000_add_billing_details_to_team/
│   │   │   └── migration.sql
│   │   ├── 20250830000000_dataroom_faq_items_model/
│   │   │   └── migration.sql
│   │   ├── 20250903000000_add_agreement_contenttype/
│   │   │   └── migration.sql
│   │   ├── 20250905000000_add_hierarchical_index_to_dataroom/
│   │   │   └── migration.sql
│   │   ├── 20250913000000_add_integrations/
│   │   │   └── migration.sql
│   │   ├── 20250915000000_add_annotations/
│   │   │   └── migration.sql
│   │   ├── 20250915000000_add_performance_indexes/
│   │   │   └── migration.sql
│   │   ├── 20250917000000_add_performance_index_dataroom/
│   │   │   └── migration.sql
│   │   ├── 20251007000000_add_welcome_message_to_brand/
│   │   │   └── migration.sql
│   │   ├── 20251009000000_update-replicate-folders/
│   │   │   └── migration.sql
│   │   ├── 20251017000000_add_show_banner_to_presets/
│   │   │   └── migration.sql
│   │   ├── 20251021000000_add_deleted_at_to_links/
│   │   │   └── migration.sql
│   │   ├── 20251027000000_add_banner_to_brand/
│   │   │   └── migration.sql
│   │   ├── 20251031000000_add_message_to_link/
│   │   │   └── migration.sql
│   │   ├── 20251111000000_add_viewer_invitations/
│   │   │   └── migration.sql
│   │   ├── 20251116000000_add_workflow/
│   │   │   └── migration.sql
│   │   ├── 20251208000000_add_chat_model/
│   │   │   └── migration.sql
│   │   ├── 20251209000000_update_view_model/
│   │   │   └── migration.sql
│   │   ├── 20251230000000_add_timezone_to_team/
│   │   │   └── migration.sql
│   │   ├── 20260113000000_add_hidden_in_all_documents/
│   │   │   └── migration.sql
│   │   ├── 20260113000000_add_internal_name_to_dataroom/
│   │   │   └── migration.sql
│   │   ├── 20260120000000_add_folder_icon_color/
│   │   │   └── migration.sql
│   │   ├── 20260204000000_add_introduction_page/
│   │   │   └── migration.sql
│   │   ├── 20260210000000_add_saml_scim_to_team/
│   │   │   └── migration.sql
│   │   ├── 20260212000000_add_owner_to_link/
│   │   │   └── migration.sql
│   │   ├── 20260212000000_add_visitor_groups/
│   │   │   └── migration.sql
│   │   ├── 20260219171000_add_dataroom_bg_toggle_to_brand/
│   │   │   └── migration.sql
│   │   ├── 20260220000000_add_redirect_url_to_domain/
│   │   │   └── migration.sql
│   │   ├── 20260227000000_add_survey_data_to_team/
│   │   │   └── migration.sql
│   │   └── migration_lock.toml
│   └── schema/
│       ├── annotation.prisma
│       ├── conversation.prisma
│       ├── dataroom.prisma
│       ├── document.prisma
│       ├── integration.prisma
│       ├── jackson.prisma
│       ├── link.prisma
│       ├── schema.prisma
│       ├── team.prisma
│       └── workflow.prisma
├── styles/
│   ├── custom-notion-styles.css
│   ├── custom-viewer-styles.css
│   └── globals.css
├── tailwind.config.js
├── trigger.config.ts
├── tsconfig.json
└── vercel.json
Download .txt
Showing preview only (249K chars total). Download the full file or copy to clipboard to get everything.
SYMBOL INDEX (2537 symbols across 1092 files)

FILE: app/(auth)/auth/confirm-email-change/[token]/page-client.tsx
  function ConfirmEmailChangePageClient (line 12) | function ConfirmEmailChangePageClient() {

FILE: app/(auth)/auth/confirm-email-change/[token]/page.tsx
  type PageProps (line 55) | interface PageProps {
  function ConfirmEmailChangePage (line 59) | async function ConfirmEmailChangePage(props: PageProps) {

FILE: app/(auth)/auth/email/[[...params]]/page-client.tsx
  function EmailVerificationClient (line 13) | function EmailVerificationClient() {
  function TestimonialSection (line 290) | function TestimonialSection() {

FILE: app/(auth)/auth/email/[[...params]]/page.tsx
  function EmailVerificationPage (line 39) | async function EmailVerificationPage() {

FILE: app/(auth)/auth/saml/page-client.tsx
  function SAMLCallbackClient (line 20) | function SAMLCallbackClient() {

FILE: app/(auth)/auth/saml/page.tsx
  function SAMLCallbackPage (line 10) | function SAMLCallbackPage() {

FILE: app/(auth)/layout.tsx
  function Layout (line 8) | function Layout({ children }: { children: React.ReactNode }) {

FILE: app/(auth)/login/page-client.tsx
  function Login (line 27) | function Login() {

FILE: app/(auth)/login/page.tsx
  function LoginPage (line 41) | function LoginPage() {

FILE: app/(auth)/register/page-client.tsx
  function Register (line 17) | function Register() {

FILE: app/(auth)/register/page.tsx
  function RegisterPage (line 39) | function RegisterPage() {

FILE: app/(auth)/verify/invitation/AcceptInvitationButton.tsx
  type AcceptInvitationButtonProps (line 9) | interface AcceptInvitationButtonProps {
  function AcceptInvitationButton (line 13) | function AcceptInvitationButton({

FILE: app/(auth)/verify/invitation/InvitationStatusContent.tsx
  function InvitationStatusContent (line 7) | function InvitationStatusContent({

FILE: app/(auth)/verify/invitation/page.tsx
  function VerifyInvitationPage (line 49) | async function VerifyInvitationPage({

FILE: app/(auth)/verify/invitation/status/ClientRedirect.tsx
  function CleanUrlOnExpire (line 5) | function CleanUrlOnExpire({

FILE: app/(auth)/verify/page.tsx
  function VerifyPage (line 4) | async function VerifyPage() {

FILE: app/(ee)/api/ai/chat/[chatId]/messages/route.ts
  function POST (line 19) | async function POST(

FILE: app/(ee)/api/ai/chat/[chatId]/route.ts
  function GET (line 15) | async function GET(
  function DELETE (line 91) | async function DELETE(

FILE: app/(ee)/api/ai/chat/route.ts
  function POST (line 18) | async function POST(req: NextRequest) {
  function GET (line 360) | async function GET(req: NextRequest) {

FILE: app/(ee)/api/ai/store/runs/[runId]/route.ts
  function GET (line 14) | async function GET(

FILE: app/(ee)/api/ai/store/teams/[teamId]/datarooms/[dataroomId]/route.ts
  function POST (line 21) | async function POST(

FILE: app/(ee)/api/ai/store/teams/[teamId]/documents/[documentId]/route.ts
  function POST (line 22) | async function POST(
  function DELETE (line 206) | async function DELETE(

FILE: app/(ee)/api/ai/store/teams/[teamId]/route.ts
  function GET (line 15) | async function GET(

FILE: app/(ee)/api/auth/saml/authorize/route.ts
  function GET (line 7) | async function GET(req: Request) {
  function POST (line 40) | async function POST(req: Request) {

FILE: app/(ee)/api/auth/saml/callback/route.ts
  function POST (line 6) | async function POST(req: Request) {

FILE: app/(ee)/api/auth/saml/token/route.ts
  function POST (line 16) | async function POST(req: Request) {

FILE: app/(ee)/api/auth/saml/userinfo/route.ts
  function GET (line 14) | async function GET(req: Request) {

FILE: app/(ee)/api/auth/saml/verify/route.ts
  function POST (line 13) | async function POST(req: Request) {

FILE: app/(ee)/api/faqs/route.ts
  type VisitorFAQResponse (line 15) | interface VisitorFAQResponse {
  function GET (line 28) | async function GET(req: NextRequest) {

FILE: app/(ee)/api/links/[id]/upload/route.ts
  function GET (line 17) | async function GET(
  function POST (line 114) | async function POST(

FILE: app/(ee)/api/scim/v2.0/[...directory]/route.ts
  function hashEmail (line 11) | function hashEmail(email: string): string {
  function handleSCIMEvents (line 85) | async function handleSCIMEvents(event: DirectorySyncEvent) {

FILE: app/(ee)/api/teams/[teamId]/directory-sync/route.ts
  constant SSO_ELIGIBLE_PLANS (line 9) | const SSO_ELIGIBLE_PLANS = ["datarooms-premium", "datarooms-premium+old"];
  function isJacksonUnavailableError (line 11) | function isJacksonUnavailableError(error: unknown): boolean {
  function getAuthenticatedAdmin (line 20) | async function getAuthenticatedAdmin(teamId: string) {
  function GET (line 44) | async function GET(
  function POST (line 87) | async function POST(
  function DELETE (line 149) | async function DELETE(

FILE: app/(ee)/api/teams/[teamId]/saml/route.ts
  constant SSO_ELIGIBLE_PLANS (line 10) | const SSO_ELIGIBLE_PLANS = ["datarooms-premium", "datarooms-premium+old"];
  function isJacksonUnavailableError (line 12) | function isJacksonUnavailableError(error: unknown): boolean {
  function getAuthenticatedAdmin (line 21) | async function getAuthenticatedAdmin(teamId: string) {
  function GET (line 45) | async function GET(
  function POST (line 104) | async function POST(
  function PATCH (line 257) | async function PATCH(
  function DELETE (line 331) | async function DELETE(

FILE: app/(ee)/api/workflow-entry/domains/[...domainSlug]/route.ts
  function POST (line 26) | async function POST(
  function handleVerify (line 118) | async function handleVerify(req: NextRequest, link: any) {
  function handleAccess (line 190) | async function handleAccess(req: NextRequest, link: any) {

FILE: app/(ee)/api/workflow-entry/link/[entryLinkId]/access/route.ts
  function POST (line 20) | async function POST(

FILE: app/(ee)/api/workflow-entry/link/[entryLinkId]/verify/route.ts
  function POST (line 12) | async function POST(

FILE: app/(ee)/api/workflows/[workflowId]/executions/route.ts
  function GET (line 9) | async function GET(

FILE: app/(ee)/api/workflows/[workflowId]/route.ts
  function GET (line 16) | async function GET(
  function PATCH (line 115) | async function PATCH(
  function DELETE (line 225) | async function DELETE(

FILE: app/(ee)/api/workflows/[workflowId]/steps/[stepId]/route.ts
  function PATCH (line 17) | async function PATCH(
  function DELETE (line 251) | async function DELETE(

FILE: app/(ee)/api/workflows/[workflowId]/steps/route.ts
  function GET (line 17) | async function GET(
  function POST (line 133) | async function POST(
  function PUT (line 331) | async function PUT(

FILE: app/(ee)/api/workflows/route.ts
  function GET (line 16) | async function GET(req: NextRequest) {
  function POST (line 91) | async function POST(req: NextRequest) {

FILE: app/api/auth/verify-code/route.ts
  function getClientIp (line 10) | function getClientIp(request: NextRequest): string {
  function POST (line 17) | async function POST(request: NextRequest) {

FILE: app/api/cron/dataroom-digest/daily/route.ts
  function POST (line 10) | async function POST(req: Request) {

FILE: app/api/cron/dataroom-digest/weekly/route.ts
  function POST (line 10) | async function POST(req: Request) {

FILE: app/api/cron/domains/route.ts
  function POST (line 24) | async function POST(req: Request) {

FILE: app/api/cron/welcome-user/route.ts
  function POST (line 8) | async function POST(req: Request) {

FILE: app/api/cron/year-in-review/route.ts
  function POST (line 10) | async function POST(req: Request) {

FILE: app/api/csp-report/route.ts
  function POST (line 3) | async function POST(request: Request) {

FILE: app/api/feature-flags/route.ts
  function GET (line 7) | async function GET(request: Request) {

FILE: app/api/help/route.ts
  function GET (line 3) | async function GET(request: Request) {

FILE: app/api/integrations/slack/oauth/authorize/route.ts
  function GET (line 16) | async function GET(req: Request) {

FILE: app/api/og/route.tsx
  function GET (line 11) | async function GET(request: NextRequest) {

FILE: app/api/og/yir/route.tsx
  function GET (line 6) | async function GET(req: NextRequest) {

FILE: app/api/verify/login-link/route.ts
  function GET (line 4) | async function GET(request: NextRequest) {

FILE: app/api/views-dataroom/route.ts
  function POST (line 38) | async function POST(request: NextRequest) {

FILE: app/api/views/pages/route.ts
  constant MAX_PAGES_PER_REQUEST (line 11) | const MAX_PAGES_PER_REQUEST = 15;
  constant VIEW_MAX_AGE_MS (line 12) | const VIEW_MAX_AGE_MS = 23 * 60 * 60 * 1000;
  function POST (line 23) | async function POST(request: NextRequest) {

FILE: app/api/views/route.ts
  function POST (line 30) | async function POST(request: NextRequest) {

FILE: app/layout.tsx
  function RootLayout (line 43) | function RootLayout({

FILE: components/EmailForm.tsx
  function EmailForm (line 1) | function EmailForm({ onSubmitHandler, setEmail }: any) {

FILE: components/Skeleton.tsx
  function Skeleton (line 3) | function Skeleton({

FILE: components/account/account-header.tsx
  function AccountHeader (line 3) | function AccountHeader() {

FILE: components/account/upload-avatar.tsx
  type UploadAvatarProps (line 21) | interface UploadAvatarProps {

FILE: components/agreements/agreement-card.tsx
  type AgreementCardProps (line 27) | interface AgreementCardProps {
  function AgreementCard (line 32) | function AgreementCard({

FILE: components/ai-elements/conversation.tsx
  type ConversationProps (line 10) | type ConversationProps = ComponentProps<typeof StickToBottom>;
  type ConversationContentProps (line 22) | type ConversationContentProps = ComponentProps<
  type ConversationEmptyStateProps (line 36) | type ConversationEmptyStateProps = ComponentProps<"div"> & {
  type ConversationScrollButtonProps (line 71) | type ConversationScrollButtonProps = ComponentProps<typeof Button>;

FILE: components/ai-elements/message.tsx
  type MessageProps (line 26) | type MessageProps = HTMLAttributes<HTMLDivElement> & {
  type MessageContentProps (line 41) | type MessageContentProps = HTMLAttributes<HTMLDivElement>;
  type MessageActionsProps (line 61) | type MessageActionsProps = ComponentProps<"div">;
  type MessageActionProps (line 73) | type MessageActionProps = ComponentProps<typeof Button> & {
  type MessageBranchContextType (line 109) | type MessageBranchContextType = {
  type MessageBranchProps (line 134) | type MessageBranchProps = HTMLAttributes<HTMLDivElement> & {
  type MessageBranchContentProps (line 184) | type MessageBranchContentProps = HTMLAttributes<HTMLDivElement>;
  type MessageBranchSelectorProps (line 214) | type MessageBranchSelectorProps = HTMLAttributes<HTMLDivElement> & {
  type MessageBranchPreviousProps (line 239) | type MessageBranchPreviousProps = ComponentProps<typeof Button>;
  type MessageBranchNextProps (line 262) | type MessageBranchNextProps = ComponentProps<typeof Button>;
  type MessageBranchPageProps (line 286) | type MessageBranchPageProps = HTMLAttributes<HTMLSpanElement>;
  type MessageResponseProps (line 307) | type MessageResponseProps = ComponentProps<typeof Streamdown>;
  type MessageAttachmentProps (line 324) | type MessageAttachmentProps = HTMLAttributes<HTMLDivElement> & {
  function MessageAttachment (line 330) | function MessageAttachment({
  type MessageAttachmentsProps (line 408) | type MessageAttachmentsProps = ComponentProps<"div">;
  function MessageAttachments (line 410) | function MessageAttachments({
  type MessageToolbarProps (line 432) | type MessageToolbarProps = ComponentProps<"div">;

FILE: components/ai-elements/prompt-input.tsx
  type AttachmentsContext (line 77) | type AttachmentsContext = {
  type TextInputContext (line 86) | type TextInputContext = {
  type PromptInputControllerProps (line 92) | type PromptInputControllerProps = {
  type PromptInputProviderProps (line 136) | type PromptInputProviderProps = PropsWithChildren<{
  function PromptInputProvider (line 144) | function PromptInputProvider({
  type PromptInputAttachmentProps (line 279) | type PromptInputAttachmentProps = HTMLAttributes<HTMLDivElement> & {
  function PromptInputAttachment (line 284) | function PromptInputAttachment({
  type PromptInputAttachmentsProps (line 375) | type PromptInputAttachmentsProps = Omit<
  function PromptInputAttachments (line 382) | function PromptInputAttachments({
  type PromptInputActionAddAttachmentsProps (line 405) | type PromptInputActionAddAttachmentsProps = ComponentProps<
  type PromptInputMessage (line 430) | type PromptInputMessage = {
  type PromptInputProps (line 435) | type PromptInputProps = Omit<
  type PromptInputBodyProps (line 797) | type PromptInputBodyProps = HTMLAttributes<HTMLDivElement>;
  type PromptInputTextareaProps (line 806) | type PromptInputTextareaProps = ComponentProps<
  type PromptInputHeaderProps (line 907) | type PromptInputHeaderProps = Omit<
  type PromptInputFooterProps (line 923) | type PromptInputFooterProps = Omit<
  type PromptInputToolsProps (line 939) | type PromptInputToolsProps = HTMLAttributes<HTMLDivElement>;
  type PromptInputButtonProps (line 948) | type PromptInputButtonProps = ComponentProps<typeof InputGroupButton>;
  type PromptInputActionMenuProps (line 970) | type PromptInputActionMenuProps = ComponentProps<typeof DropdownMenu>;
  type PromptInputActionMenuTriggerProps (line 975) | type PromptInputActionMenuTriggerProps = PromptInputButtonProps;
  type PromptInputActionMenuContentProps (line 989) | type PromptInputActionMenuContentProps = ComponentProps<
  type PromptInputActionMenuItemProps (line 999) | type PromptInputActionMenuItemProps = ComponentProps<
  type PromptInputSubmitProps (line 1012) | type PromptInputSubmitProps = ComponentProps<typeof InputGroupButton> & {
  type SpeechRecognition (line 1048) | interface SpeechRecognition extends EventTarget {
  type SpeechRecognitionEvent (line 1064) | interface SpeechRecognitionEvent extends Event {
  type SpeechRecognitionResultList (line 1069) | type SpeechRecognitionResultList = {
  type SpeechRecognitionResult (line 1075) | type SpeechRecognitionResult = {
  type SpeechRecognitionAlternative (line 1082) | type SpeechRecognitionAlternative = {
  type SpeechRecognitionErrorEvent (line 1087) | interface SpeechRecognitionErrorEvent extends Event {
  type Window (line 1092) | interface Window {
  type PromptInputSpeechButtonProps (line 1102) | type PromptInputSpeechButtonProps = ComponentProps<
  type PromptInputSelectProps (line 1208) | type PromptInputSelectProps = ComponentProps<typeof Select>;
  type PromptInputSelectTriggerProps (line 1214) | type PromptInputSelectTriggerProps = ComponentProps<
  type PromptInputSelectContentProps (line 1232) | type PromptInputSelectContentProps = ComponentProps<
  type PromptInputSelectItemProps (line 1243) | type PromptInputSelectItemProps = ComponentProps<typeof SelectItem>;
  type PromptInputSelectValueProps (line 1252) | type PromptInputSelectValueProps = ComponentProps<typeof SelectValue>;
  type PromptInputHoverCardProps (line 1261) | type PromptInputHoverCardProps = ComponentProps<typeof HoverCard>;
  type PromptInputHoverCardTriggerProps (line 1271) | type PromptInputHoverCardTriggerProps = ComponentProps<
  type PromptInputHoverCardContentProps (line 1279) | type PromptInputHoverCardContentProps = ComponentProps<
  type PromptInputTabsListProps (line 1290) | type PromptInputTabsListProps = HTMLAttributes<HTMLDivElement>;
  type PromptInputTabProps (line 1297) | type PromptInputTabProps = HTMLAttributes<HTMLDivElement>;
  type PromptInputTabLabelProps (line 1304) | type PromptInputTabLabelProps = HTMLAttributes<HTMLHeadingElement>;
  type PromptInputTabBodyProps (line 1319) | type PromptInputTabBodyProps = HTMLAttributes<HTMLDivElement>;
  type PromptInputTabItemProps (line 1328) | type PromptInputTabItemProps = HTMLAttributes<HTMLDivElement>;
  type PromptInputCommandProps (line 1343) | type PromptInputCommandProps = ComponentProps<typeof Command>;
  type PromptInputCommandInputProps (line 1350) | type PromptInputCommandInputProps = ComponentProps<typeof CommandInput>;
  type PromptInputCommandListProps (line 1359) | type PromptInputCommandListProps = ComponentProps<typeof CommandList>;
  type PromptInputCommandEmptyProps (line 1368) | type PromptInputCommandEmptyProps = ComponentProps<typeof CommandEmpty>;
  type PromptInputCommandGroupProps (line 1377) | type PromptInputCommandGroupProps = ComponentProps<typeof CommandGroup>;
  type PromptInputCommandItemProps (line 1386) | type PromptInputCommandItemProps = ComponentProps<typeof CommandItem>;
  type PromptInputCommandSeparatorProps (line 1395) | type PromptInputCommandSeparatorProps = ComponentProps<

FILE: components/ai-elements/shimmer.tsx
  type TextShimmerProps (line 15) | type TextShimmerProps = {

FILE: components/analytics/analytics-card.tsx
  type AnalyticsCardProps (line 5) | interface AnalyticsCardProps {
  function AnalyticsCard (line 17) | function AnalyticsCard({

FILE: components/analytics/dashboard-views-chart.tsx
  type DashboardViewsChartProps (line 16) | interface DashboardViewsChartProps {
  function DashboardViewsChart (line 23) | function DashboardViewsChart({

FILE: components/analytics/documents-table.tsx
  type Document (line 46) | interface Document {
  function DocumentsTable (line 172) | function DocumentsTable({

FILE: components/analytics/links-table.tsx
  type Link (line 49) | interface Link {
  function CopyButton (line 60) | function CopyButton({ url }: { url: string }) {
  function LinksTable (line 234) | function LinksTable({

FILE: components/analytics/time-range-select.tsx
  constant TIME_RANGES (line 21) | const TIME_RANGES = [
  type TimeRange (line 28) | type TimeRange = (typeof TIME_RANGES)[number]["value"];
  type CustomRange (line 29) | interface CustomRange {
  type TimeRangeSelectProps (line 33) | interface TimeRangeSelectProps {
  function TimeRangeSelect (line 43) | function TimeRangeSelect({

FILE: components/analytics/views-table.tsx
  type View (line 56) | interface View {
  function ViewsTable (line 277) | function ViewsTable({

FILE: components/analytics/visitors-table.tsx
  type Visitor (line 49) | interface Visitor {
  function VisitorsTable (line 217) | function VisitorsTable({

FILE: components/billing/add-seat-modal.tsx
  function AddSeatModal (line 28) | function AddSeatModal({

FILE: components/billing/plan-badge.tsx
  function PlanBadge (line 5) | function PlanBadge({

FILE: components/billing/pro-annual-banner.tsx
  function ProAnnualBanner (line 15) | function ProAnnualBanner({

FILE: components/billing/pro-banner.tsx
  function ProBanner (line 11) | function ProBanner({

FILE: components/billing/upgrade-plan-container.tsx
  function UpgradePlanContainer (line 38) | function UpgradePlanContainer() {

FILE: components/billing/upgrade-plan-modal-old.tsx
  function UpgradePlanModal (line 24) | function UpgradePlanModal({

FILE: components/billing/upgrade-plan-modal-with-discount.tsx
  function UpgradePlanModalWithDiscount (line 152) | function UpgradePlanModalWithDiscount({

FILE: components/billing/upgrade-plan-modal.tsx
  function UpgradePlanModal (line 146) | function UpgradePlanModal({

FILE: components/billing/yearly-upgrade-banner.tsx
  type YearlyUpgradeBannerProps (line 22) | interface YearlyUpgradeBannerProps {
  function YearlyUpgradeBanner (line 26) | function YearlyUpgradeBanner({

FILE: components/blur-image.tsx
  function BlurImage (line 9) | function BlurImage(props: ImageProps) {

FILE: components/charts/bar-chart.tsx
  function BarChartComponent (line 67) | function BarChartComponent({

FILE: components/charts/utils.ts
  type Data (line 1) | type Data = {
  type SumData (line 9) | type SumData = {
  type TransformedData (line 14) | type TransformedData = {
  type CustomTooltipTypeBar (line 19) | type CustomTooltipTypeBar = {
  type Color (line 25) | type Color =

FILE: components/conversations/index.tsx
  function ConversationListItem (line 6) | function ConversationListItem(props: any) {
  function ConversationMessage (line 10) | function ConversationMessage(props: any) {

FILE: components/datarooms/actions/download-dataroom.tsx
  function DownloadDataroomButton (line 8) | function DownloadDataroomButton({

FILE: components/datarooms/actions/generate-index-button.tsx
  type GenerateIndexButtonProps (line 3) | interface GenerateIndexButtonProps {
  function GenerateIndexButton (line 9) | function GenerateIndexButton({

FILE: components/datarooms/actions/generate-index-dialog.tsx
  type GenerateIndexDialogProps (line 38) | interface GenerateIndexDialogProps {
  function GenerateIndexDialog (line 44) | function GenerateIndexDialog({

FILE: components/datarooms/actions/rebuild-index-button.tsx
  type RebuildIndexButtonProps (line 23) | interface RebuildIndexButtonProps {
  function RebuildIndexButton (line 29) | function RebuildIndexButton({

FILE: components/datarooms/actions/remove-document-modal.tsx
  function RemoveDataroomItemsModal (line 21) | function RemoveDataroomItemsModal({
  function useRemoveDataroomItemsModal (line 189) | function useRemoveDataroomItemsModal({

FILE: components/datarooms/add-dataroom-modal.tsx
  function AddDataroomModal (line 60) | function AddDataroomModal({

FILE: components/datarooms/add-viewer-modal.tsx
  function AddViewerModal (line 25) | function AddViewerModal({

FILE: components/datarooms/analytics/analytics-overview.tsx
  type DataroomAnalyticsOverviewProps (line 10) | interface DataroomAnalyticsOverviewProps {
  function DataroomAnalyticsOverview (line 23) | function DataroomAnalyticsOverview({

FILE: components/datarooms/analytics/document-analytics-tree.tsx
  type FileOrFolder (line 37) | type FileOrFolder = {
  type DocumentAnalyticsTreeProps (line 49) | interface DocumentAnalyticsTreeProps {
  function DocumentAnalyticsTree (line 63) | function DocumentAnalyticsTree({

FILE: components/datarooms/analytics/mock-analytics-table.tsx
  function MockAnalyticsTable (line 44) | function MockAnalyticsTable() {

FILE: components/datarooms/dataroom-breadcrumb.tsx
  function BreadcrumbComponentBase (line 96) | function BreadcrumbComponentBase({

FILE: components/datarooms/dataroom-card.tsx
  type DataroomTag (line 20) | type DataroomTag = {
  type DataroomWithDetails (line 29) | type DataroomWithDetails = {
  type DataroomCardProps (line 43) | interface DataroomCardProps {
  function DataroomCard (line 47) | function DataroomCard({ dataroom }: DataroomCardProps) {

FILE: components/datarooms/dataroom-document-card.tsx
  type DocumentsCardProps (line 47) | type DocumentsCardProps = {
  function DataroomDocumentCard (line 55) | function DataroomDocumentCard({

FILE: components/datarooms/dataroom-items-list.tsx
  type FolderOrDocument (line 62) | type FolderOrDocument =
  function DataroomItemsList (line 66) | function DataroomItemsList({

FILE: components/datarooms/dataroom-trial-modal.tsx
  function DataroomTrialModal (line 36) | function DataroomTrialModal({

FILE: components/datarooms/download-progress-modal.tsx
  type DownloadJobStatus (line 31) | interface DownloadJobStatus {
  type DownloadProgressModalProps (line 46) | interface DownloadProgressModalProps {
  function DownloadProgressModal (line 56) | function DownloadProgressModal({

FILE: components/datarooms/edit-dataroom-document-modal.tsx
  type DataroomIncludeDocumentsItem (line 27) | type DataroomIncludeDocumentsItem =
  function updateDocNameInDocuments (line 40) | function updateDocNameInDocuments(
  function updateDocNameInFolderTree (line 54) | function updateDocNameInFolderTree(
  function updateDocNameInIncludeDocumentsTree (line 75) | function updateDocNameInIncludeDocumentsTree(
  function EditDataroomDocumentModal (line 105) | function EditDataroomDocumentModal({

FILE: components/datarooms/empty-dataroom.tsx
  function EmptyDataroom (line 3) | function EmptyDataroom() {

FILE: components/datarooms/export-visits-modal.tsx
  type ExportStatus (line 10) | interface ExportStatus {
  type ExportVisitsModalProps (line 17) | interface ExportVisitsModalProps {
  function ExportVisitsModal (line 26) | function ExportVisitsModal({

FILE: components/datarooms/folders/selection-tree.tsx
  function SidebarFolderTreeSelection (line 137) | function SidebarFolderTreeSelection({

FILE: components/datarooms/folders/sidebar-tree.tsx
  type MixedItem (line 22) | type MixedItem =
  function SidebarFolderTree (line 165) | function SidebarFolderTree({ dataroomId }: { dataroomId: string }) {

FILE: components/datarooms/folders/utils.ts
  type DataroomDocumentWithVersion (line 59) | type DataroomDocumentWithVersion = {
  type DataroomFolderWithDocumentsNew (line 72) | type DataroomFolderWithDocumentsNew = DataroomFolder & {

FILE: components/datarooms/folders/view-tree.tsx
  type DataroomDocumentWithVersion (line 44) | type DataroomDocumentWithVersion = {
  type DataroomFolderWithDocuments (line 57) | type DataroomFolderWithDocuments = DataroomFolder & {
  type FolderPath (line 68) | type FolderPath = Set<string> | null;
  function findFolderPath (line 70) | function findFolderPath(
  function ViewFolderTree (line 297) | function ViewFolderTree({

FILE: components/datarooms/groups/add-group-modal.tsx
  function AddGroupModal (line 22) | function AddGroupModal({

FILE: components/datarooms/groups/add-member-modal.tsx
  function AddGroupMemberModal (line 22) | function AddGroupMemberModal({

FILE: components/datarooms/groups/delete-group/delete-group-modal.tsx
  function DeleteGroupModal (line 23) | function DeleteGroupModal({
  function useDeleteGroupModal (line 125) | function useDeleteGroupModal({

FILE: components/datarooms/groups/delete-group/index.tsx
  function DeleteGroup (line 13) | function DeleteGroup({

FILE: components/datarooms/groups/group-card-placeholder.tsx
  function GroupCardPlaceholder (line 9) | function GroupCardPlaceholder() {

FILE: components/datarooms/groups/group-card.tsx
  function GroupCard (line 19) | function GroupCard({

FILE: components/datarooms/groups/group-member-table.tsx
  function GroupMemberTable (line 46) | function GroupMemberTable({

FILE: components/datarooms/groups/group-permissions.tsx
  type FileOrFolder (line 77) | type FileOrFolder = {
  type ItemPermission (line 92) | type ItemPermission = Record<
  type ColumnExtra (line 97) | type ColumnExtra = {
  function ExpandableTable (line 348) | function ExpandableTable({

FILE: components/datarooms/groups/set-unified-permissions-modal.tsx
  type GroupPermissions (line 43) | type GroupPermissions = Record<string, { view: boolean; download: boolea...
  type LinkPermissions (line 44) | type LinkPermissions = Record<string, { view: boolean; download: boolean...
  function SetUnifiedPermissionsModal (line 46) | function SetUnifiedPermissionsModal({

FILE: components/datarooms/move-dataroom-folder-modal.tsx
  function MoveToDataroomFolderModal (line 24) | function MoveToDataroomFolderModal({

FILE: components/datarooms/settings/bulk-download-settings.tsx
  type BulkDownloadSettingsProps (line 20) | interface BulkDownloadSettingsProps {
  function BulkDownloadSettings (line 24) | function BulkDownloadSettings({

FILE: components/datarooms/settings/dataroom-tag-section.tsx
  type TagProps (line 28) | type TagProps = {
  function getTagOption (line 35) | function getTagOption(tag: TagProps) {
  type DataroomTagSectionProps (line 49) | interface DataroomTagSectionProps {
  function DataroomTagSection (line 62) | function DataroomTagSection({

FILE: components/datarooms/settings/delete-dataroooom/delete-dataroom-modal.tsx
  function DeleteDataroomModal (line 22) | function DeleteDataroomModal({
  function useDeleteDataroomModal (line 128) | function useDeleteDataroomModal({

FILE: components/datarooms/settings/delete-dataroooom/index.tsx
  function DeleteDataroom (line 13) | function DeleteDataroom({

FILE: components/datarooms/settings/duplicate-dataroom.tsx
  function DuplicateDataroom (line 24) | function DuplicateDataroom({

FILE: components/datarooms/settings/introduction-settings.tsx
  type IntroductionSettingsProps (line 41) | interface IntroductionSettingsProps {
  type FolderItem (line 45) | interface FolderItem {
  type DocumentItem (line 51) | interface DocumentItem {
  function generateIntroductionContent (line 56) | function generateIntroductionContent(
  function renderInlineContent (line 341) | function renderInlineContent(nodes: any[] | undefined): React.ReactNode {
  function renderContent (line 380) | function renderContent(content: any): React.ReactNode {
  function IntroductionSettings (line 487) | function IntroductionSettings({

FILE: components/datarooms/settings/notification-settings.tsx
  type NotificationSettingsProps (line 21) | interface NotificationSettingsProps {
  function NotificationSettings (line 25) | function NotificationSettings({

FILE: components/datarooms/settings/permission-settings.tsx
  type DefaultPermissionStrategy (line 19) | type DefaultPermissionStrategy =
  type PermissionSettingsProps (line 24) | interface PermissionSettingsProps {
  function PermissionSettings (line 28) | function PermissionSettings({

FILE: components/datarooms/settings/settings-tabs.tsx
  type SettingsTabsProps (line 14) | interface SettingsTabsProps {
  function SettingsTabs (line 18) | function SettingsTabs({ dataroomId }: SettingsTabsProps) {

FILE: components/datarooms/sortable/sortable-item.tsx
  type ItemCategory (line 6) | type ItemCategory = "folder" | "document";
  type SortableItemProps (line 8) | interface SortableItemProps {

FILE: components/datarooms/sortable/sortable-list.tsx
  type FolderOrDocument (line 38) | type FolderOrDocument =
  function DataroomSortableList (line 42) | function DataroomSortableList({

FILE: components/datarooms/stats-card.tsx
  function StatsCard (line 8) | function StatsCard() {

FILE: components/document-upload.tsx
  function DocumentUpload (line 25) | function DocumentUpload({

FILE: components/documents/actions/delete-documents-modal.tsx
  function DeleteItemsModal (line 22) | function DeleteItemsModal({
  function useDeleteDocumentsAndFoldersModal (line 278) | function useDeleteDocumentsAndFoldersModal({

FILE: components/documents/actions/delete-folder-modal.tsx
  function useDeleteFolderModal (line 11) | function useDeleteFolderModal(

FILE: components/documents/add-document-modal.tsx
  type DataroomDocument (line 50) | interface DataroomDocument {
  function AddDocumentModal (line 56) | function AddDocumentModal({

FILE: components/documents/add-document-to-dataroom-modal.tsx
  type TSelectedDataroom (line 28) | type TSelectedDataroom = { id: string; name: string } | null;
  function AddToDataroomModal (line 30) | function AddToDataroomModal({

FILE: components/documents/add-folder-to-dataroom-modal.tsx
  type TSelectedDataroom (line 29) | type TSelectedDataroom = { id: string; name: string } | null;
  function AddFolderToDataroomModal (line 31) | function AddFolderToDataroomModal({

FILE: components/documents/alert.tsx
  type AlertProps (line 10) | interface AlertProps {

FILE: components/documents/annotations/annotation-form.tsx
  type FormValues (line 38) | type FormValues = z.infer<typeof formSchema>;
  type AnnotationFormProps (line 40) | interface AnnotationFormProps {
  function AnnotationForm (line 48) | function AnnotationForm({

FILE: components/documents/annotations/annotation-sheet.tsx
  type AnnotationSheetProps (line 43) | interface AnnotationSheetProps {
  function AnnotationSheet (line 50) | function AnnotationSheet({

FILE: components/documents/breadcrumb.tsx
  function BreadcrumbComponentBase (line 17) | function BreadcrumbComponentBase({ name }: { name: string[] }) {

FILE: components/documents/delete-folder-modal.tsx
  type TSelectedDataroom (line 11) | type TSelectedDataroom = { id: string; name: string } | null;
  function DeleteFolderModal (line 13) | function DeleteFolderModal({

FILE: components/documents/document-card.tsx
  type DocumentsCardProps (line 48) | type DocumentsCardProps = {
  function DocumentsCard (line 55) | function DocumentsCard({

FILE: components/documents/document-header.tsx
  function DocumentHeader (line 71) | function DocumentHeader({

FILE: components/documents/document-preview-button.tsx
  type DocumentPreviewButtonProps (line 10) | interface DocumentPreviewButtonProps {
  function DocumentPreviewButton (line 26) | function DocumentPreviewButton({
  function isDocumentProcessing (line 117) | function isDocumentProcessing(primaryVersion?: {

FILE: components/documents/document-preview-modal.tsx
  type DocumentPreviewModalProps (line 14) | interface DocumentPreviewModalProps {
  function DocumentPreviewModal (line 20) | function DocumentPreviewModal({

FILE: components/documents/document-stats-placeholder.tsx
  type DocumentStatsPlaceholderProps (line 8) | interface DocumentStatsPlaceholderProps {
  function DocumentStatsPlaceholder (line 13) | function DocumentStatsPlaceholder({

FILE: components/documents/documents-list.tsx
  type Upload (line 59) | type Upload = {
  type File (line 65) | type File = {
  function DocumentsList (line 70) | function DocumentsList({

FILE: components/documents/drag-and-drop/draggable-item.tsx
  type DraggableItemProps (line 9) | interface DraggableItemProps {
  function DraggableItem (line 18) | function DraggableItem({

FILE: components/documents/drag-and-drop/droppable-folder.tsx
  type DroppableFolderProps (line 7) | interface DroppableFolderProps {
  function DroppableFolder (line 14) | function DroppableFolder({

FILE: components/documents/empty-document.tsx
  function EmptyDocuments (line 6) | function EmptyDocuments({

FILE: components/documents/export-visits-modal.tsx
  type ExportStatus (line 11) | interface ExportStatus {
  type ExportVisitsModalProps (line 18) | interface ExportVisitsModalProps {
  function ExportVisitsModal (line 24) | function ExportVisitsModal({

FILE: components/documents/file-process-status-bar.tsx
  constant QUEUED_MESSAGES (line 10) | const QUEUED_MESSAGES = [
  function FileProcessStatusBar (line 17) | function FileProcessStatusBar({

FILE: components/documents/filters/sort-button.tsx
  function SortButton (line 27) | function SortButton() {

FILE: components/documents/folder-card.tsx
  type FolderCardProps (line 47) | type FolderCardProps = {
  function FolderCard (line 59) | function FolderCard({

FILE: components/documents/hidden-document-card.tsx
  type HiddenDocumentCardProps (line 33) | type HiddenDocumentCardProps = {
  function HiddenDocumentCard (line 40) | function HiddenDocumentCard({

FILE: components/documents/hidden-documents-list.tsx
  function HiddenDocumentsList (line 29) | function HiddenDocumentsList({

FILE: components/documents/hidden-folder-card.tsx
  type HiddenFolderCardProps (line 29) | type HiddenFolderCardProps = {
  function HiddenFolderCard (line 37) | function HiddenFolderCard({

FILE: components/documents/link-document-indicator.tsx
  type LinkDocumentIndicatorProps (line 25) | interface LinkDocumentIndicatorProps {
  function LinkDocumentIndicator (line 31) | function LinkDocumentIndicator({

FILE: components/documents/loading-document.tsx
  function LoadingDocuments (line 3) | function LoadingDocuments({ count }: { count: number }) {

FILE: components/documents/move-folder-modal.tsx
  type TSelectedFolder (line 22) | type TSelectedFolder = {
  function MoveToFolderModal (line 28) | function MoveToFolderModal({

FILE: components/documents/notion-accessibility-indicator.tsx
  type NotionAccessibilityIndicatorProps (line 29) | interface NotionAccessibilityIndicatorProps {
  type AccessibilityStatus (line 35) | interface AccessibilityStatus {
  function NotionAccessibilityIndicator (line 43) | function NotionAccessibilityIndicator({

FILE: components/documents/pagination.tsx
  type PaginationProps (line 18) | interface PaginationProps {
  function Pagination (line 30) | function Pagination({

FILE: components/documents/preview-viewers/preview-excel-viewer.tsx
  type PreviewExcelViewerProps (line 6) | interface PreviewExcelViewerProps {
  function PreviewExcelViewer (line 11) | function PreviewExcelViewer({

FILE: components/documents/preview-viewers/preview-image-viewer.tsx
  type PreviewImageViewerProps (line 6) | interface PreviewImageViewerProps {
  function PreviewImageViewer (line 11) | function PreviewImageViewer({

FILE: components/documents/preview-viewers/preview-pages-viewer.tsx
  constant PRELOAD_RADIUS (line 10) | const PRELOAD_RADIUS = 5;
  type PreviewPagesViewerProps (line 12) | interface PreviewPagesViewerProps {
  function PreviewPagesViewer (line 18) | function PreviewPagesViewer({

FILE: components/documents/preview-viewers/preview-viewer.tsx
  type PreviewViewerProps (line 9) | interface PreviewViewerProps {
  function PreviewViewer (line 14) | function PreviewViewer({ documentData, onClose }: PreviewViewerProps) {

FILE: components/documents/stats-card.tsx
  function StatsCard (line 9) | function StatsCard({

FILE: components/documents/stats-chart-dummy.tsx
  function StatsChartDummy (line 3) | function StatsChartDummy({

FILE: components/documents/stats-chart.tsx
  function StatsChart (line 9) | function StatsChart({

FILE: components/documents/stats-element.tsx
  type Stat (line 3) | interface Stat {
  type StatsElementProps (line 9) | interface StatsElementProps {
  function StatsElement (line 14) | function StatsElement({ stat, statIdx }: StatsElementProps) {

FILE: components/documents/video-analytics.tsx
  type VideoAnalyticsProps (line 20) | interface VideoAnalyticsProps {
  function VideoAnalytics (line 26) | function VideoAnalytics({

FILE: components/documents/video-chart-placeholder.tsx
  function VideoChartPlaceholder (line 10) | function VideoChartPlaceholder({

FILE: components/documents/video-stats-placeholder.tsx
  type VideoStatsPlaceholderProps (line 5) | interface VideoStatsPlaceholderProps {
  function VideoStatsPlaceholder (line 10) | function VideoStatsPlaceholder({

FILE: components/domains/add-domain-modal.tsx
  type DomainStatus (line 40) | type DomainStatus =
  constant STATUS_CONFIG (line 48) | const STATUS_CONFIG: Record<
  function AddDomainModal (line 93) | function AddDomainModal({

FILE: components/domains/delete-domain-modal.tsx
  function DeleteDomainModal (line 19) | function DeleteDomainModal({
  function useDeleteDomainModal (line 143) | function useDeleteDomainModal({

FILE: components/domains/domain-card.tsx
  function DomainCard (line 36) | function DomainCard({

FILE: components/domains/domain-configuration.tsx
  function DomainConfiguration (line 12) | function DomainConfiguration({

FILE: components/domains/use-domain-status.ts
  function useDomainStatus (line 12) | function useDomainStatus({

FILE: components/emails/custom-domain-setup.tsx
  function CustomDomainSetup (line 19) | function CustomDomainSetup({

FILE: components/emails/dataroom-digest-notification.tsx
  type DocumentChange (line 16) | type DocumentChange = {
  function DataroomDigestNotification (line 20) | function DataroomDigestNotification({

FILE: components/emails/dataroom-notification.tsx
  function DataroomNotification (line 16) | function DataroomNotification({

FILE: components/emails/dataroom-trial-24h.tsx
  type TrialEndReminderEmail (line 19) | interface TrialEndReminderEmail {

FILE: components/emails/dataroom-trial-end.tsx
  type DataroomTrialEnd (line 19) | interface DataroomTrialEnd {

FILE: components/emails/dataroom-trial-welcome.tsx
  type WelcomeEmailProps (line 5) | interface WelcomeEmailProps {

FILE: components/emails/dataroom-upload-notification.tsx
  function DataroomUploadNotification (line 17) | function DataroomUploadNotification({

FILE: components/emails/deleted-domain.tsx
  function DomainDeleted (line 18) | function DomainDeleted({

FILE: components/emails/download-ready.tsx
  function formatExpirationTime (line 15) | function formatExpirationTime(expiresAt?: string): string {
  function DownloadReady (line 32) | function DownloadReady({

FILE: components/emails/email-updated.tsx
  function EmailUpdated (line 16) | function EmailUpdated({

FILE: components/emails/export-ready.tsx
  function ExportReady (line 16) | function ExportReady({

FILE: components/emails/hundred-views-congrats.tsx
  type HundredViewsCongratsEmailProps (line 10) | interface HundredViewsCongratsEmailProps {

FILE: components/emails/installed-integration-notification.tsx
  function SlackIntegrationNotification (line 18) | function SlackIntegrationNotification({

FILE: components/emails/invalid-domain.tsx
  function InvalidDomain (line 19) | function InvalidDomain({

FILE: components/emails/otp-verification.tsx
  function OtpEmailVerification (line 15) | function OtpEmailVerification({

FILE: components/emails/slack-integration.tsx
  type SlackIntegrationEmailProps (line 16) | interface SlackIntegrationEmailProps {

FILE: components/emails/team-invitation.tsx
  function TeamInvitation (line 18) | function TeamInvitation({

FILE: components/emails/thousand-views-congrats.tsx
  type ThousandViewsCongratsEmailProps (line 13) | interface ThousandViewsCongratsEmailProps {

FILE: components/emails/upgrade-one-month-checkin.tsx
  type UpgradeCheckinEmailProps (line 12) | interface UpgradeCheckinEmailProps {

FILE: components/emails/upgrade-personal-welcome.tsx
  type UpgradePersonalEmailProps (line 12) | interface UpgradePersonalEmailProps {

FILE: components/emails/upgrade-plan.tsx
  type UpgradePlanEmailProps (line 19) | interface UpgradePlanEmailProps {

FILE: components/emails/upgrade-six-month-checkin.tsx
  type SixMonthMilestoneEmailProps (line 12) | interface SixMonthMilestoneEmailProps {

FILE: components/emails/verification-email-change.tsx
  type ConfirmEmailChangeProps (line 16) | interface ConfirmEmailChangeProps {
  function ConfirmEmailChange (line 22) | function ConfirmEmailChange({

FILE: components/emails/viewed-dataroom-paused.tsx
  function ViewedDataroomPausedEmail (line 18) | function ViewedDataroomPausedEmail({

FILE: components/emails/viewed-dataroom.tsx
  function ViewedDataroom (line 17) | function ViewedDataroom({

FILE: components/emails/viewed-document-paused.tsx
  function ViewedDocumentPausedEmail (line 18) | function ViewedDocumentPausedEmail({

FILE: components/emails/viewed-document.tsx
  function ViewedDocument (line 17) | function ViewedDocument({

FILE: components/emails/welcome.tsx
  type WelcomeEmailProps (line 19) | interface WelcomeEmailProps {

FILE: components/emails/year-in-review-papermark.tsx
  type PapermarkYearInReviewEmailProps (line 20) | interface PapermarkYearInReviewEmailProps {
  function PapermarkYearInReviewEmail (line 35) | function PapermarkYearInReviewEmail({

FILE: components/folders/add-folder-modal.tsx
  function AddFolderModal (line 37) | function AddFolderModal({

FILE: components/folders/edit-folder-modal.tsx
  function EditFolderModal (line 31) | function EditFolderModal({

FILE: components/folders/folder-color-picker.tsx
  type FolderColorPickerProps (line 10) | interface FolderColorPickerProps {
  function FolderColorPicker (line 15) | function FolderColorPicker({ value, onChange }: FolderColorPickerProps) {
  type FolderColorDotProps (line 91) | interface FolderColorDotProps {
  function FolderColorDot (line 96) | function FolderColorDot({ colorId, className }: FolderColorDotProps) {

FILE: components/folders/folder-icon-picker.tsx
  type FolderIconPickerProps (line 20) | interface FolderIconPickerProps {
  function FolderIconPicker (line 26) | function FolderIconPicker({
  type FolderIconPreviewProps (line 71) | interface FolderIconPreviewProps {
  function FolderIconPreview (line 78) | function FolderIconPreview({
  constant COLOR_HEX_VALUES (line 101) | const COLOR_HEX_VALUES: Record<FolderColorId, string> = {
  type FolderIconColorPickerProps (line 112) | interface FolderIconColorPickerProps {
  function FolderIconColorPicker (line 119) | function FolderIconColorPicker({

FILE: components/gtm-component.tsx
  constant GTM_ID (line 3) | const GTM_ID = process.env.NEXT_PUBLIC_GTM_ID;
  function GTMComponent (line 5) | function GTMComponent() {

FILE: components/hooks/use-optimistic-update.ts
  function useOptimisticUpdate (line 6) | function useOptimisticUpdate<T>(

FILE: components/hooks/useLastUsed.tsx
  type LoginType (line 8) | type LoginType = "passkey" | "google" | "credentials" | "linkedin";
  function useLastUsed (line 10) | function useLastUsed() {

FILE: components/layouts/app.tsx
  constant DATAROOM_SIDEBAR_COOKIE_NAME (line 23) | const DATAROOM_SIDEBAR_COOKIE_NAME = "sidebar:dataroom-state";
  function getInitialSidebarState (line 26) | function getInitialSidebarState(isDataroom: boolean): boolean {
  function AppLayout (line 48) | function AppLayout({ children }: { children: React.ReactNode }) {

FILE: components/layouts/breadcrumb.tsx
  constant FOLDERS_TO_DISPLAY (line 29) | const FOLDERS_TO_DISPLAY = 1;

FILE: components/layouts/trial-banner.tsx
  function TrialBanner (line 20) | function TrialBanner() {
  function TrialBannerComponent (line 40) | function TrialBannerComponent({

FILE: components/links/delete-link-modal.tsx
  function DeleteLinkModal (line 25) | function DeleteLinkModal({
  function useDeleteLinkModal (line 199) | function useDeleteLinkModal({

FILE: components/links/embed-code-modal.tsx
  type EmbedCodeModalProps (line 14) | interface EmbedCodeModalProps {
  function EmbedCodeModal (line 21) | function EmbedCodeModal({

FILE: components/links/link-active-controls.tsx
  type LinkSetting (line 6) | type LinkSetting = {
  constant LINK_SETTINGS (line 12) | const LINK_SETTINGS: LinkSetting[] = [
  function countActiveSettings (line 71) | function countActiveSettings(link: LinkWithViews): number {
  type LinkActiveControlsProps (line 75) | interface LinkActiveControlsProps {
  function LinkActiveControls (line 80) | function LinkActiveControls({

FILE: components/links/link-sheet/agreement-panel/index.tsx
  function AgreementSheet (line 48) | function AgreementSheet({

FILE: components/links/link-sheet/agreement-section.tsx
  function AgreementSection (line 22) | function AgreementSection({

FILE: components/links/link-sheet/ai-agents-section.tsx
  function AIAgentsSection (line 10) | function AIAgentsSection({

FILE: components/links/link-sheet/allow-download-section.tsx
  function AllowDownloadSection (line 6) | function AllowDownloadSection({

FILE: components/links/link-sheet/allow-list-section.tsx
  function AllowListSection (line 24) | function AllowListSection({

FILE: components/links/link-sheet/allow-notification-section.tsx
  function AllowNotificationSection (line 6) | function AllowNotificationSection({

FILE: components/links/link-sheet/custom-fields-panel/custom-field.tsx
  type CustomFieldProps (line 19) | interface CustomFieldProps {

FILE: components/links/link-sheet/custom-fields-panel/index.tsx
  type CustomFieldData (line 22) | type CustomFieldData = Omit<
  function CustomFieldsPanel (line 29) | function CustomFieldsPanel({

FILE: components/links/link-sheet/custom-fields-section.tsx
  function CustomFieldsSection (line 17) | function CustomFieldsSection({

FILE: components/links/link-sheet/dataroom-link-sheet.tsx
  type ItemPermission (line 8) | type ItemPermission = ItemPermissionEE;
  function DataroomLinkSheet (line 10) | function DataroomLinkSheet(props: any) {

FILE: components/links/link-sheet/deny-list-section.tsx
  function DenyListSection (line 15) | function DenyListSection({

FILE: components/links/link-sheet/domain-section.tsx
  function DomainSection (line 45) | function DomainSection({

FILE: components/links/link-sheet/email-authentication-section.tsx
  function EmailAuthenticationSection (line 7) | function EmailAuthenticationSection({

FILE: components/links/link-sheet/email-protection-section.tsx
  function EmailProtectionSection (line 6) | function EmailProtectionSection({

FILE: components/links/link-sheet/expiration-section.tsx
  function ExpirationSection (line 14) | function ExpirationSection({

FILE: components/links/link-sheet/expirationIn-section.tsx
  function ExpirationInSection (line 13) | function ExpirationInSection({

FILE: components/links/link-sheet/feedback-section.tsx
  function FeedbackSection (line 6) | function FeedbackSection({

FILE: components/links/link-sheet/index-file-section.tsx
  function IndexFileSection (line 7) | function IndexFileSection({

FILE: components/links/link-sheet/index.tsx
  type DEFAULT_LINK_TYPE (line 101) | type DEFAULT_LINK_TYPE = {
  function LinkSheet (line 146) | function LinkSheet({

FILE: components/links/link-sheet/link-item.tsx
  function LinkItem (line 15) | function LinkItem({

FILE: components/links/link-sheet/link-options.tsx
  type LinkUpgradeOptions (line 42) | type LinkUpgradeOptions = {

FILE: components/links/link-sheet/link-success-sheet.tsx
  type LinkSuccessSheetProps (line 33) | interface LinkSuccessSheetProps {
  function LinkSuccessSheet (line 41) | function LinkSuccessSheet({

FILE: components/links/link-sheet/og-section.tsx
  function OGSection (line 20) | function OGSection({

FILE: components/links/link-sheet/password-section.tsx
  function PasswordSection (line 14) | function PasswordSection({

FILE: components/links/link-sheet/permissions-sheet.tsx
  function PermissionsSheet (line 5) | function PermissionsSheet(props: any) {

FILE: components/links/link-sheet/pro-banner-section.tsx
  function ProBannerSection (line 7) | function ProBannerSection({

FILE: components/links/link-sheet/question-section.tsx
  function QuestionSection (line 21) | function QuestionSection({

FILE: components/links/link-sheet/screenshot-protection-section.tsx
  function ScreenshotProtectionSection (line 7) | function ScreenshotProtectionSection({

FILE: components/links/link-sheet/tags/tag-badge.tsx
  function TagBadge (line 6) | function TagBadge({
  constant COLORS_LIST (line 54) | const COLORS_LIST: { color: TagColorProps; css: string }[] = [
  function randomBadgeColor (line 85) | function randomBadgeColor() {

FILE: components/links/link-sheet/tags/tag-details.tsx
  function useOrganizedTags (line 15) | function useOrganizedTags(tags: LinkWithViews["tags"]) {
  function TagColumn (line 37) | function TagColumn({
  function TagsTooltip (line 67) | function TagsTooltip({
  function TagButton (line 90) | function TagButton({

FILE: components/links/link-sheet/tags/tag-section.tsx
  function getTagOption (line 21) | function getTagOption(tag: TagProps) {
  function TagSection (line 35) | function TagSection({

FILE: components/links/link-sheet/upload-section/index.tsx
  function FolderSelectionModal (line 28) | function FolderSelectionModal({
  function UploadSection (line 131) | function UploadSection({

FILE: components/links/link-sheet/watermark-panel/index.tsx
  type WatermarkConfigSheetProps (line 36) | interface WatermarkConfigSheetProps {
  function WatermarkConfigSheet (line 43) | function WatermarkConfigSheet({

FILE: components/links/link-sheet/watermark-section.tsx
  function WatermarkSection (line 19) | function WatermarkSection({

FILE: components/links/link-sheet/welcome-message-section.tsx
  constant MAX_WELCOME_MESSAGE_LENGTH (line 14) | const MAX_WELCOME_MESSAGE_LENGTH = 80;
  function WelcomeMessageSection (line 16) | function WelcomeMessageSection({

FILE: components/links/links-table.tsx
  function LinksTable (line 248) | function LinksTable({

FILE: components/links/links-visitors.tsx
  function LinksVisitors (line 14) | function LinksVisitors({

FILE: components/navigation-menu.tsx
  type Props (line 17) | type Props = {

FILE: components/profile-menu.tsx
  type ProfileMenuProps (line 35) | type ProfileMenuProps = {
  type Article (line 41) | interface Article {

FILE: components/profile-search-trigger.tsx
  type ProfileSearchTriggerProps (line 5) | interface ProfileSearchTriggerProps {
  function ProfileSearchTrigger (line 9) | function ProfileSearchTrigger({ onClick }: ProfileSearchTriggerProps) {

FILE: components/search-box.tsx
  type SearchBoxProps (line 22) | type SearchBoxProps = {
  function SearchBoxPersisted (line 126) | function SearchBoxPersisted({

FILE: components/search-command.tsx
  type Article (line 18) | interface Article {
  type SearchCommandProps (line 26) | interface SearchCommandProps extends DialogProps {
  function SearchCommand (line 36) | function SearchCommand({

FILE: components/settings/delete-team-modal.tsx
  function DeleteTeamModal (line 24) | function DeleteTeamModal({
  function useDeleteTeamModal (line 162) | function useDeleteTeamModal() {

FILE: components/settings/delete-team.tsx
  function DeleteTeam (line 13) | function DeleteTeam() {

FILE: components/settings/global-block-list-form.tsx
  function GlobalBlockListForm (line 20) | function GlobalBlockListForm() {

FILE: components/settings/ignored-domains-form.tsx
  function IgnoredDomainsForm (line 20) | function IgnoredDomainsForm() {

FILE: components/settings/og-preview.tsx
  function Preview (line 12) | function Preview({

FILE: components/settings/settings-header.tsx
  function SettingsHeader (line 6) | function SettingsHeader() {

FILE: components/settings/slack-settings-skeleton.tsx
  function SlackSettingsSkeleton (line 3) | function SlackSettingsSkeleton() {

FILE: components/settings/survey-settings.tsx
  constant DEAL_TYPE_OPTIONS (line 18) | const DEAL_TYPE_OPTIONS = [
  constant DEAL_SIZE_OPTIONS (line 27) | const DEAL_SIZE_OPTIONS = [
  function SurveySettings (line 35) | function SurveySettings() {

FILE: components/settings/timezone-selector.tsx
  constant TIMEZONE_OPTIONS (line 27) | const TIMEZONE_OPTIONS = [
  function TimezoneSelector (line 73) | function TimezoneSelector() {

FILE: components/shared/dealflow-popup.tsx
  constant DEAL_TYPE_OPTIONS (line 9) | const DEAL_TYPE_OPTIONS = [
  constant DEAL_SIZE_OPTIONS (line 18) | const DEAL_SIZE_OPTIONS = [
  function DealflowPopup (line 26) | function DealflowPopup() {

FILE: components/shared/icons/advanced-sheet.tsx
  function AdvancedSheet (line 1) | function AdvancedSheet({ className }: { className?: string }) {

FILE: components/shared/icons/alert-circle.tsx
  function AlertCircle (line 1) | function AlertCircle({

FILE: components/shared/icons/arrow-up.tsx
  function ArrowUp (line 1) | function ArrowUp({ className }: { className?: string }) {

FILE: components/shared/icons/badge-check.tsx
  function BadgeCheck (line 1) | function BadgeCheck({ className }: { className?: string }) {

FILE: components/shared/icons/bar-chart.tsx
  function BarChart (line 1) | function BarChart({ className }: { className?: string }) {

FILE: components/shared/icons/check-cirlce-2.tsx
  function CheckCircle2 (line 1) | function CheckCircle2({

FILE: components/shared/icons/check.tsx
  function Check (line 1) | function Check({ className }: { className?: string }) {

FILE: components/shared/icons/chevron-down.tsx
  function ChevronDown (line 1) | function ChevronDown({ className }: { className?: string }) {

FILE: components/shared/icons/chevron-right.tsx
  function ChevronRight (line 1) | function ChevronRight({ className }: { className?: string }) {

FILE: components/shared/icons/chevron-up.tsx
  function ChevronUp (line 1) | function ChevronUp({ className }: { className?: string }) {

FILE: components/shared/icons/circle.tsx
  function Circle (line 1) | function Circle({ className }: { className?: string }) {

FILE: components/shared/icons/cloud-download-off.tsx
  function CloudDownloadOff (line 1) | function CloudDownloadOff({

FILE: components/shared/icons/copy-right.tsx
  function CopyRight (line 1) | function CopyRight({ className }: { className?: string }) {

FILE: components/shared/icons/copy.tsx
  function Copy (line 1) | function Copy({ className }: { className?: string }) {

FILE: components/shared/icons/external-link.tsx
  function CheckCircle2 (line 1) | function CheckCircle2({ className }: { className?: string }) {

FILE: components/shared/icons/eye-off.tsx
  function EyeOff (line 3) | function EyeOff({

FILE: components/shared/icons/eye.tsx
  function Eye (line 3) | function Eye({ className, ...props }: SVGProps<SVGSVGElement>) {

FILE: components/shared/icons/facebook.tsx
  function Facebook (line 1) | function Facebook({

FILE: components/shared/icons/file-up.tsx
  function FileUp (line 1) | function FileUp({ className }: { className?: string }) {

FILE: components/shared/icons/files/cad.tsx
  function CadIcon (line 1) | function CadIcon({

FILE: components/shared/icons/files/docs.tsx
  function DocsIcon (line 1) | function DocsIcon({

FILE: components/shared/icons/files/image.tsx
  function ImageFileIcon (line 1) | function ImageFileIcon({

FILE: components/shared/icons/files/map.tsx
  function MapIcon (line 1) | function MapIcon({

FILE: components/shared/icons/files/notion.tsx
  function NotionIcon (line 5) | function NotionIcon(

FILE: components/shared/icons/files/pdf.tsx
  function PdfIcon (line 1) | function PdfIcon({

FILE: components/shared/icons/files/sheet.tsx
  function SheetIcon (line 1) | function SheetIcon({

FILE: components/shared/icons/files/slides.tsx
  function SlidesIcon (line 1) | function SlidesIcon({

FILE: components/shared/icons/files/video.tsx
  function VideoIcon (line 1) | function VideoIcon({

FILE: components/shared/icons/folder.tsx
  function Folder (line 1) | function Folder({ className }: { className?: string }) {

FILE: components/shared/icons/github.tsx
  function GitHubIcon (line 3) | function GitHubIcon(

FILE: components/shared/icons/globe.tsx
  function Globe (line 1) | function Globe({ className }: { className?: string }) {

FILE: components/shared/icons/google.tsx
  function Google (line 1) | function Google({ className }: { className?: string }) {

FILE: components/shared/icons/grip-vertical.tsx
  function GripVertical (line 1) | function GripVertical({ className }: { className?: string }) {

FILE: components/shared/icons/home.tsx
  function Home (line 1) | function Home({ className }: { className?: string }) {

FILE: components/shared/icons/index.tsx
  type Icon (line 7) | type Icon = LucideIcon | ComponentType<SVGProps<SVGSVGElement>>;

FILE: components/shared/icons/linkedin.tsx
  function LinkedIn (line 1) | function LinkedIn({

FILE: components/shared/icons/menu.tsx
  function Menu (line 1) | function Menu({ className }: { className?: string }) {

FILE: components/shared/icons/moon.tsx
  function Moon (line 1) | function Moon({ className }: { className?: string }) {

FILE: components/shared/icons/more-horizontal.tsx
  function MoreHorizontal (line 1) | function MoreHorizontal({ className }: { className?: string }) {

FILE: components/shared/icons/more-vertical.tsx
  function MoreVertical (line 1) | function MoreVertical({ className }: { className?: string }) {

FILE: components/shared/icons/papermark-sparkle.tsx
  function PapermarkSparkle (line 1) | function PapermarkSparkle({

FILE: components/shared/icons/passkey.tsx
  function Passkey (line 1) | function Passkey({ className }: { className?: string }) {

FILE: components/shared/icons/pie-chart.tsx
  function PieChart (line 1) | function PieChart({ className }: { className?: string }) {

FILE: components/shared/icons/portrait-landscape.tsx
  function PortraitLandscape (line 1) | function PortraitLandscape({

FILE: components/shared/icons/producthunt.tsx
  function ProductHuntIcon (line 3) | function ProductHuntIcon(

FILE: components/shared/icons/search.tsx
  function Search (line 1) | function Search({ className }: { className?: string }) {

FILE: components/shared/icons/settings.tsx
  function Settings (line 1) | function Settings({ className }: { className?: string }) {

FILE: components/shared/icons/slack-icon.tsx
  type SlackIconProps (line 3) | interface SlackIconProps {
  function SlackIcon (line 8) | function SlackIcon({ className = "", size = 24 }: SlackIconProps) {

FILE: components/shared/icons/sparkle.tsx
  function Sparkle (line 1) | function Sparkle({ className }: { className?: string }) {

FILE: components/shared/icons/sun.tsx
  function Sun (line 1) | function Sun({ className }: { className?: string }) {

FILE: components/shared/icons/twitter.tsx
  function Twitter (line 5) | function Twitter({ className }: { className?: string }) {

FILE: components/shared/icons/user-round.tsx
  function UserRound (line 1) | function UserRound({ className }: { className?: string }) {

FILE: components/shared/icons/x-circle.tsx
  function XCircle (line 1) | function XCircle({

FILE: components/shared/icons/x.tsx
  function X (line 1) | function X({ className }: { className?: string }) {

FILE: components/sidebar-folders.tsx
  function SidebarFolderTree (line 178) | function SidebarFolderTree() {
  function SidebarFolderTreeSelection (line 230) | function SidebarFolderTreeSelection({

FILE: components/sidebar/app-sidebar.tsx
  function AppSidebar (line 50) | function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
  function UsageProgress (line 357) | function UsageProgress(data: {

FILE: components/sidebar/banners/slack-banner.tsx
  function SlackBanner (line 13) | function SlackBanner({

FILE: components/sidebar/nav-main.tsx
  type NavItem (line 32) | interface NavItem {
  function NavMain (line 49) | function NavMain({ items }: { items: NavItem[] }) {

FILE: components/sidebar/nav-user.tsx
  type Article (line 46) | interface Article {
  function NavUser (line 54) | function NavUser() {

FILE: components/sidebar/team-switcher.tsx
  function TeamSwitcher (line 33) | function TeamSwitcher({

FILE: components/tab-menu.tsx
  type Props (line 10) | type Props = {

FILE: components/tags/add-tag-modal.tsx
  function AddTagsModal (line 27) | function AddTagsModal({

FILE: components/teams/add-team-member-modal.tsx
  function AddTeamMembers (line 24) | function AddTeamMembers({

FILE: components/teams/add-team-modal.tsx
  type AddTeamModalProps (line 23) | interface AddTeamModalProps {
  function AddTeamModal (line 28) | function AddTeamModal({ children, setCurrentTeam }: AddTeamModalProps) {

FILE: components/teams/delete-team-modal.tsx
  function DeleteTeamModal (line 23) | function DeleteTeamModal({ children }: { children: React.ReactNode }) {

FILE: components/theme-provider.tsx
  function ThemeProvider (line 10) | function ThemeProvider({ children, ...props }: ThemeProviderProps) {

FILE: components/theme-toggle.tsx
  function ModeToggle (line 19) | function ModeToggle() {

FILE: components/ui/alert-dialog.tsx
  type SingleAlertDialogProps (line 138) | interface SingleAlertDialogProps {

FILE: components/ui/badge.tsx
  type BadgeProps (line 40) | interface BadgeProps
  function Badge (line 44) | function Badge({ className, variant, ...props }: BadgeProps) {

FILE: components/ui/bar-list.tsx
  type BarListProps (line 7) | interface BarListProps {
  function BarList (line 20) | function BarList({
  function LineItem (line 41) | function LineItem({

FILE: components/ui/button-group.tsx
  function ButtonGroup (line 24) | function ButtonGroup({
  function ButtonGroupText (line 40) | function ButtonGroupText({
  function ButtonGroupSeparator (line 60) | function ButtonGroupSeparator({

FILE: components/ui/button.tsx
  type ButtonProps (line 43) | interface ButtonProps

FILE: components/ui/calendar.tsx
  type CalendarProps (line 10) | type CalendarProps = React.ComponentProps<typeof DayPicker>;
  function Calendar (line 12) | function Calendar({

FILE: components/ui/carousel.tsx
  type CarouselApi (line 12) | type CarouselApi = UseEmblaCarouselType[1];
  type UseCarouselParameters (line 13) | type UseCarouselParameters = Parameters<typeof useEmblaCarousel>;
  type CarouselOptions (line 14) | type CarouselOptions = UseCarouselParameters[0];
  type CarouselPlugin (line 15) | type CarouselPlugin = UseCarouselParameters[1];
  type CarouselProps (line 17) | type CarouselProps = {
  type CarouselContextProps (line 24) | type CarouselContextProps = {
  function useCarousel (line 35) | function useCarousel() {

FILE: components/ui/command.tsx
  type CommandDialogProps (line 26) | interface CommandDialogProps extends DialogProps {}

FILE: components/ui/copy-button.tsx
  function CopyButton (line 25) | function CopyButton({

FILE: components/ui/devices.tsx
  function Chrome (line 3) | function Chrome({ className }: { className: string }) {
  function Safari (line 51) | function Safari({ className }: { className: string }) {
  function Apple (line 88) | function Apple({ className }: { className: string }) {

FILE: components/ui/feature-preview.tsx
  type FeaturePreviewProps (line 17) | interface FeaturePreviewProps {
  function FeaturePreview (line 63) | function FeaturePreview({

FILE: components/ui/file-upload.tsx
  type AcceptedFileFormats (line 10) | type AcceptedFileFormats = "any" | "images" | "csv";
  type FileUploadReadFileProps (line 42) | type FileUploadReadFileProps =
  type FileUploadProps (line 58) | type FileUploadProps = FileUploadReadFileProps & {
  function FileUpload (line 110) | function FileUpload({

FILE: components/ui/form-hook.tsx
  type FormFieldContextValue (line 22) | type FormFieldContextValue<
  type FormItemContextValue (line 69) | type FormItemContextValue = {

FILE: components/ui/form.tsx
  function Form (line 28) | function Form({

FILE: components/ui/input-group.tsx
  function InputGroup (line 9) | function InputGroup({ className, ...props }: React.ComponentProps<"div">) {
  function InputGroupAddon (line 58) | function InputGroupAddon({
  function InputGroupButton (line 98) | function InputGroupButton({
  function InputGroupText (line 117) | function InputGroupText({ className, ...props }: React.ComponentProps<"s...
  function InputGroupInput (line 129) | function InputGroupInput({
  function InputGroupTextarea (line 145) | function InputGroupTextarea({

FILE: components/ui/input.tsx
  type InputProps (line 5) | interface InputProps

FILE: components/ui/loading-spinner.tsx
  function LoadingSpinner (line 5) | function LoadingSpinner({ className }: { className?: string }) {

FILE: components/ui/modal.tsx
  function Modal (line 13) | function Modal({

FILE: components/ui/multi-select-v2.tsx
  type MultiSelectProps (line 55) | interface MultiSelectProps<
  type ComboboxOption (line 131) | type ComboboxOption<

FILE: components/ui/nextra-filetree.tsx
  function useIndent (line 31) | function useIndent() {
  function usePrefersLightText (line 35) | function usePrefersLightText() {
  type FolderProps (line 39) | interface FolderProps {
  type FileProps (line 52) | interface FileProps {
  function Tree (line 59) | function Tree({
  function Ident (line 80) | function Ident(): ReactElement {

FILE: components/ui/pagination.tsx
  type PaginationLinkProps (line 39) | type PaginationLinkProps = {

FILE: components/ui/phone-input.tsx
  type PhoneInputProps (line 12) | type PhoneInputProps = Omit<
  type CountrySelectProps (line 69) | type CountrySelectProps = {

FILE: components/ui/responsive-button.tsx
  type ResponsiveButtonProps (line 13) | interface ResponsiveButtonProps extends ButtonProps {

FILE: components/ui/rich-text-editor.tsx
  function isValidYouTubeUrl (line 44) | function isValidYouTubeUrl(url: string): boolean {
  type RichTextEditorProps (line 94) | interface RichTextEditorProps {
  function RichTextEditor (line 101) | function RichTextEditor({

FILE: components/ui/sheet.tsx
  type SheetContentProps (line 57) | interface SheetContentProps

FILE: components/ui/sidebar.tsx
  constant SIDEBAR_COOKIE_NAME (line 68) | const SIDEBAR_COOKIE_NAME = "sidebar:state";
  constant SIDEBAR_COOKIE_MAX_AGE (line 69) | const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7;
  constant SIDEBAR_WIDTH (line 70) | const SIDEBAR_WIDTH = "16rem";
  constant SIDEBAR_WIDTH_MOBILE (line 71) | const SIDEBAR_WIDTH_MOBILE = "18rem";
  constant SIDEBAR_WIDTH_ICON (line 72) | const SIDEBAR_WIDTH_ICON = "3rem";
  constant SIDEBAR_KEYBOARD_SHORTCUT (line 73) | const SIDEBAR_KEYBOARD_SHORTCUT = "[";
  type SidebarContext (line 75) | type SidebarContext = {
  function useSidebar (line 87) | function useSidebar() {

FILE: components/ui/single-select.tsx
  type SingleSelectProps (line 27) | interface SingleSelectProps<TMeta = any>
  type SingleSelectOption (line 86) | type SingleSelectOption<TMeta = any> = {

FILE: components/ui/skeleton.tsx
  function Skeleton (line 3) | function Skeleton({

FILE: components/ui/smart-date-time-picker.tsx
  type SmartDateTimePickerProps (line 10) | interface SmartDateTimePickerProps {
  function SmartDateTimePicker (line 24) | function SmartDateTimePicker({

FILE: components/ui/sonner.tsx
  type ToasterProps (line 6) | type ToasterProps = React.ComponentProps<typeof Sonner>;

FILE: components/ui/status-badge.tsx
  type BadgeProps (line 37) | interface BadgeProps
  function StatusBadge (line 43) | function StatusBadge({

FILE: components/ui/tab-select.tsx
  function TabSelect (line 7) | function TabSelect<T extends string>({

FILE: components/ui/textarea.tsx
  type TextareaProps (line 5) | interface TextareaProps

FILE: components/ui/timestamp-tooltip.tsx
  constant DAY_MS (line 23) | const DAY_MS = 24 * 60 * 60 * 1000;
  constant MONTH_MS (line 24) | const MONTH_MS = 30 * DAY_MS;
  type TimestampTooltipProps (line 26) | type TimestampTooltipProps = {
  function getLocalTimeZone (line 39) | function getLocalTimeZone(): string {
  function TimestampTooltip (line 48) | function TimestampTooltip({
  function TimestampTooltipContent (line 87) | function TimestampTooltipContent({

FILE: components/ui/upgrade-button.tsx
  type UpgradeButtonProps (line 11) | interface UpgradeButtonProps {
  function UpgradeButton (line 26) | function UpgradeButton({
  function createUpgradeButton (line 82) | function createUpgradeButton(

FILE: components/upload-notification.tsx
  type UploadNotificationDrawerProps (line 14) | interface UploadNotificationDrawerProps {
  function UploadNotificationDrawer (line 24) | function UploadNotificationDrawer({

FILE: components/upload-zone.tsx
  type FileWithPaths (line 45) | interface FileWithPaths extends File {
  type UploadState (line 51) | interface UploadState {
  type RejectedFile (line 58) | interface RejectedFile {
  type UploadZoneProps (line 63) | interface UploadZoneProps extends React.PropsWithChildren {
  function UploadZone (line 85) | function UploadZone({

FILE: components/user-agent-icon.tsx
  function UAIcon (line 13) | function UAIcon({

FILE: components/view/access-form/access-form-theme.tsx
  type AccessFormTheme (line 6) | type AccessFormTheme = ReturnType<typeof createAdaptiveSurfacePalette>;
  function createAccessFormTheme (line 12) | function createAccessFormTheme(accentColor: string | null | undefined) {
  function AccessFormThemeProvider (line 16) | function AccessFormThemeProvider({
  function useAccessFormTheme (line 30) | function useAccessFormTheme() {

FILE: components/view/access-form/agreement-section.tsx
  function AgreementSection (line 10) | function AgreementSection({

FILE: components/view/access-form/custom-fields-section.tsx
  function CustomFieldsSection (line 12) | function CustomFieldsSection({

FILE: components/view/access-form/email-section.tsx
  function EmailSection (line 19) | function EmailSection({

FILE: components/view/access-form/email-verification-form.tsx
  constant REGEXP_ONLY_DIGITS (line 16) | const REGEXP_ONLY_DIGITS = "^\\d+$";
  function EmailVerificationMessage (line 18) | function EmailVerificationMessage({

FILE: components/view/access-form/index.tsx
  constant DEFAULT_ACCESS_FORM_DATA (line 18) | const DEFAULT_ACCESS_FORM_DATA = {
  type DEFAULT_ACCESS_FORM_TYPE (line 23) | type DEFAULT_ACCESS_FORM_TYPE = {
  function AccessForm (line 31) | function AccessForm({

FILE: components/view/access-form/name-section.tsx
  function NameSection (line 9) | function NameSection({

FILE: components/view/access-form/password-section.tsx
  function PasswordSection (line 12) | function PasswordSection({

FILE: components/view/annotations/annotation-panel.tsx
  type AnnotationPanelProps (line 25) | interface AnnotationPanelProps {
  function AnnotationPanel (line 35) | function AnnotationPanel({

FILE: components/view/annotations/annotation-toggle.tsx
  type AnnotationToggleProps (line 13) | interface AnnotationToggleProps {
  function AnnotationToggle (line 19) | function AnnotationToggle({

FILE: components/view/conversations/sidebar.tsx
  function ConversationSidebar (line 8) | function ConversationSidebar(props: ConversationSidebarProps) {

FILE: components/view/dataroom/dataroom-document-view.tsx
  type RowData (line 24) | type RowData = { [key: string]: any };
  type SheetData (line 25) | type SheetData = {
  type TSupportedDocumentSimpleType (line 31) | type TSupportedDocumentSimpleType =
  type DEFAULT_DATAROOM_DOCUMENT_VIEW_TYPE (line 34) | type DEFAULT_DATAROOM_DOCUMENT_VIEW_TYPE = {
  function DataroomDocumentView (line 71) | function DataroomDocumentView({

FILE: components/view/dataroom/dataroom-view.tsx
  type TSupportedDocumentSimpleType (line 24) | type TSupportedDocumentSimpleType =
  type TDocumentData (line 27) | type TDocumentData = {
  type DEFAULT_DATAROOM_VIEW_TYPE (line 38) | type DEFAULT_DATAROOM_VIEW_TYPE = {
  function DataroomView (line 51) | function DataroomView({

FILE: components/view/dataroom/document-card.tsx
  type DRDocument (line 29) | type DRDocument = {
  type DocumentsCardProps (line 39) | type DocumentsCardProps = {
  function DocumentCard (line 50) | function DocumentCard({

FILE: components/view/dataroom/document-upload-modal.tsx
  function DocumentUploadModal (line 19) | function DocumentUploadModal({

FILE: components/view/dataroom/download-otp-verification.tsx
  constant REGEXP_ONLY_DIGITS (line 12) | const REGEXP_ONLY_DIGITS = "^\\d+$";
  type DownloadOtpVerificationProps (line 14) | interface DownloadOtpVerificationProps {
  constant RESEND_COOLDOWN_SECONDS (line 26) | const RESEND_COOLDOWN_SECONDS = 60;
  function DownloadOtpVerification (line 28) | function DownloadOtpVerification({

FILE: components/view/dataroom/downloads-panel.tsx
  type Job (line 17) | type Job = {
  constant POLL_INTERVAL_MS (line 32) | const POLL_INTERVAL_MS = 5_000;
  function DownloadsPanel (line 34) | function DownloadsPanel({ linkId }: { linkId: string }) {

FILE: components/view/dataroom/folder-card.tsx
  type FolderCardProps (line 25) | type FolderCardProps = {
  function FolderCard (line 36) | function FolderCard({

FILE: components/view/dataroom/index-file-dialog.tsx
  type IndexFileDialogProps (line 25) | interface IndexFileDialogProps {
  function IndexFileDialog (line 34) | function IndexFileDialog({

FILE: components/view/dataroom/introduction-modal.tsx
  type IntroductionModalProps (line 28) | interface IntroductionModalProps {
  type IntroductionContextType (line 39) | interface IntroductionContextType {
  function IntroductionInfoButton (line 54) | function IntroductionInfoButton() {
  function renderInlineContent (line 84) | function renderInlineContent(nodes: any[] | undefined): React.ReactNode {
  function renderContent (line 123) | function renderContent(content: any): React.ReactNode {
  type IntroductionProviderProps (line 230) | interface IntroductionProviderProps extends IntroductionModalProps {
  function IntroductionProvider (line 234) | function IntroductionProvider({
  function IntroductionModal (line 318) | function IntroductionModal({

FILE: components/view/dataroom/nav-dataroom.tsx
  constant DEFAULT_BANNER_IMAGE (line 23) | const DEFAULT_BANNER_IMAGE = "/_static/papermark-banner.png";
  function DataroomNav (line 25) | function DataroomNav({

FILE: components/view/dataroom/pending-document-card.tsx
  type FolderInfo (line 29) | type FolderInfo = {
  type PendingDocumentCardProps (line 35) | type PendingDocumentCardProps = {
  function getFolderPath (line 48) | function getFolderPath(
  function PendingDocumentCard (line 66) | function PendingDocumentCard({

FILE: components/view/dataroom/viewer-download-progress-modal.tsx
  type ViewerDownloadProgressModalProps (line 26) | type ViewerDownloadProgressModalProps = {
  type Step (line 41) | type Step = "choose" | "otp" | "progress" | "complete";
  type JobStatus (line 43) | interface JobStatus {
  function ViewerDownloadProgressModal (line 58) | function ViewerDownloadProgressModal({

FILE: components/view/document-view.tsx
  type RowData (line 23) | type RowData = { [key: string]: any };
  type SheetData (line 24) | type SheetData = {
  type DEFAULT_DOCUMENT_VIEW_TYPE (line 30) | type DEFAULT_DOCUMENT_VIEW_TYPE = {
  function DocumentView (line 57) | function DocumentView({

FILE: components/view/link-preview.tsx
  type LinkPreviewProps (line 15) | interface LinkPreviewProps {
  function LinkPreview (line 22) | function LinkPreview({

FILE: components/view/nav.tsx
  type TNavData (line 52) | type TNavData = {
  function Nav (line 71) | function Nav({

FILE: components/view/question.tsx
  function Question (line 11) | function Question({

FILE: components/view/report-form.tsx
  function ReportForm (line 17) | function ReportForm({

FILE: components/view/toolbar.tsx
  function Toolbar (line 10) | function Toolbar({

FILE: components/view/view-data.tsx
  type TViewDocumentData (line 45) | type TViewDocumentData = Document & {
  function ViewData (line 57) | function ViewData({

FILE: components/view/viewer/advanced-excel-viewer.tsx
  function AdvancedExcelViewer (line 9) | function AdvancedExcelViewer({

FILE: components/view/viewer/away-poster.tsx
  type AwayPosterProps (line 9) | interface AwayPosterProps {
  function AwayPoster (line 16) | function AwayPoster({

FILE: components/view/viewer/dataroom-viewer.tsx
  type FolderOrDocument (line 102) | type FolderOrDocument =
  type DocumentVersion (line 106) | type DocumentVersion = {
  type DataroomDocument (line 115) | type DataroomDocument = {
  function DataroomViewer (line 145) | function DataroomViewer({

FILE: components/view/viewer/download-only-viewer.tsx
  function DownloadOnlyViewer (line 20) | function DownloadOnlyViewer({

FILE: components/view/viewer/excel-viewer.tsx
  type RowData (line 19) | type RowData = { [key: string]: any };
  type SheetData (line 20) | type SheetData = {
  function ExcelViewer (line 26) | function ExcelViewer({

FILE: components/view/viewer/image-viewer.tsx
  function ImageViewer (line 19) | function ImageViewer({

FILE: components/view/viewer/pages-horizontal-viewer.tsx
  function PagesHorizontalViewer (line 34) | function PagesHorizontalViewer({

FILE: components/view/viewer/pages-vertical-viewer.tsx
  function PagesVerticalViewer (line 60) | function PagesVerticalViewer({

FILE: components/view/viewer/pdf-default-viewer.tsx
  function PDFViewer (line 16) | function PDFViewer(props: any) {

FILE: components/view/viewer/video-player.tsx
  type MediaPlayerProps (line 11) | interface MediaPlayerProps {

FILE: components/view/viewer/video-viewer.tsx
  function VideoViewer (line 12) | function VideoViewer({

FILE: components/view/viewer/viewer-surface-theme.tsx
  type ViewerSurfaceTextTone (line 9) | type ViewerSurfaceTextTone = "light" | "dark";
  type ViewerSurfaceTheme (line 11) | type ViewerSurfaceTheme = {
  constant DEFAULT_VIEWER_SURFACE_THEME (line 17) | const DEFAULT_VIEWER_SURFACE_THEME: ViewerSurfaceTheme =
  function createViewerSurfaceTheme (line 24) | function createViewerSurfaceTheme(
  function ViewerSurfaceThemeProvider (line 36) | function ViewerSurfaceThemeProvider({
  function useViewerSurfaceTheme (line 50) | function useViewerSurfaceTheme() {

FILE: components/view/visitor-graph.tsx
  function formatTotalDuration (line 17) | function formatTotalDuration(totalDuration: number | null | undefined): ...
  function sumDurationsAndFormat (line 28) | function sumDurationsAndFormat(
  function ViewDurationSummary (line 38) | function ViewDurationSummary({

FILE: components/viewer-upload-component.tsx
  function ViewerUploadComponent (line 14) | function ViewerUploadComponent({

FILE: components/viewer-upload-zone.tsx
  function ViewerUploadZone (line 18) | function ViewerUploadZone({

FILE: components/visitors/contacts-document-table.tsx
  type ViewerView (line 36) | type ViewerView = {
  function ContactsDocumentsTable (line 47) | function ContactsDocumentsTable({

FILE: components/visitors/contacts-table.tsx
  type Viewer (line 35) | type Viewer = {
  function ContactsTable (line 45) | function ContactsTable({

FILE: components/visitors/data-table-pagination.tsx
  type DataTablePaginationProps (line 18) | interface DataTablePaginationProps<TData> {
  function DataTablePagination (line 23) | function DataTablePagination<TData>({

FILE: components/visitors/dataroom-view-stats.tsx
  function DataroomViewStats (line 34) | function DataroomViewStats({
  function DocumentViewRow (line 229) | function DocumentViewRow({
  function UploadRow (line 330) | function UploadRow({ upload, timestamp }: { upload: any; timestamp: Date...
  function DownloadRow (line 366) | function DownloadRow({ view, timestamp }: { view: any; timestamp: Date }) {
  function BulkDownloadRow (line 402) | function BulkDownloadRow({

FILE: components/visitors/dataroom-viewers.tsx
  function DataroomViewersTable (line 35) | function DataroomViewersTable({

FILE: components/visitors/dataroom-visitor-custom-fields.tsx
  type CustomFieldResponse (line 7) | type CustomFieldResponse = {
  function VisitorCustomFields (line 13) | function VisitorCustomFields({

FILE: components/visitors/dataroom-visitor-useragent.tsx
  function DataroomVisitorUserAgent (line 5) | function DataroomVisitorUserAgent({ viewId }: { viewId: string }) {

FILE: components/visitors/dataroom-visitors-history.tsx
  function DataroomVisitHistory (line 24) | function DataroomVisitHistory({

FILE: components/visitors/dataroom-visitors-table.tsx
  function DataroomVisitorsTable (line 46) | function DataroomVisitorsTable({

FILE: components/visitors/document-view-stats.tsx
  function DocumentPageChart (line 10) | function DocumentPageChart({

FILE: components/visitors/visitor-chart.tsx
  function VisitorChart (line 11) | function VisitorChart({

FILE: components/visitors/visitor-clicks.tsx
  type ClickEvent (line 10) | type ClickEvent = {
  type ClickEventsResponse (line 20) | type ClickEventsResponse = {
  function VisitorClicks (line 24) | function VisitorClicks({

FILE: components/visitors/visitor-custom-fields.tsx
  type CustomFieldResponse (line 7) | type CustomFieldResponse = {
  function VisitorCustomFields (line 13) | function VisitorCustomFields({

FILE: components/visitors/visitor-group-modal.tsx
  type VisitorGroupLink (line 32) | type VisitorGroupLink = {
  type VisitorGroupDetail (line 45) | type VisitorGroupDetail = VisitorGroupWithCount & {
  function VisitorGroupModal (line 49) | function VisitorGroupModal({

FILE: components/visitors/visitor-groups-section.tsx
  type VisitorGroupLink (line 41) | type VisitorGroupLink = {
  type VisitorGroupDetail (line 54) | type VisitorGroupDetail = VisitorGroupWithCount & {
  function VisitorGroupCard (line 64) | function VisitorGroupCard({
  function VisitorGroupsSection (line 363) | function VisitorGroupsSection() {

FILE: components/visitors/visitor-useragent-base.tsx
  function decodeCity (line 5) | function decodeCity(city: string) {
  function VisitorUserAgentBase (line 14) | function VisitorUserAgentBase({

FILE: components/visitors/visitor-useragent-placeholder.tsx
  function VisitorUserAgentPlaceholder (line 8) | function VisitorUserAgentPlaceholder() {

FILE: components/visitors/visitor-useragent.tsx
  function VisitorUserAgent (line 5) | function VisitorUserAgent({ viewId }: { viewId: string }) {

FILE: components/visitors/visitor-video-chart.tsx
  function VisitorVideoChart (line 16) | function VisitorVideoChart({

FILE: components/visitors/visitors-table.tsx
  function VisitorsTable (line 69) | function VisitorsTable({

FILE: components/webhooks/webhook-events.tsx
  type EventListProps (line 23) | type EventListProps = PropsWithChildren<{
  method pre (line 60) | pre(node) {
  method code (line 71) | code(node) {

FILE: components/welcome/containers/link-option-container.tsx
  function LinkOptionContainer (line 24) | function LinkOptionContainer({

FILE: components/welcome/containers/onboarding-link-options.tsx
  type LinkUpgradeOptions (line 32) | type LinkUpgradeOptions = {

FILE: components/welcome/containers/upload-container.tsx
  function UploadContainer (line 19) | function UploadContainer({

FILE: components/welcome/dataroom-ai-generate.tsx
  function DataroomAIGenerate (line 19) | function DataroomAIGenerate() {

FILE: components/welcome/dataroom-choice.tsx
  function DataroomChoice (line 8) | function DataroomChoice({ dataroomId }: { dataroomId: string }) {

FILE: components/welcome/dataroom-trial.tsx
  function DataroomTrial (line 29) | function DataroomTrial() {

FILE: components/welcome/dataroom-upload.tsx
  function DataroomUpload (line 15) | function DataroomUpload({ dataroomId }: { dataroomId: string }) {

FILE: components/welcome/dataroom.tsx
  function Dataroom (line 9) | function Dataroom() {

FILE: components/welcome/intro.tsx
  function Intro (line 9) | function Intro() {

FILE: components/welcome/next.tsx
  function Next (line 8) | function Next() {

FILE: components/welcome/notion-form.tsx
  function NotionForm (line 35) | function NotionForm() {

FILE: components/welcome/select.tsx
  constant DEAL_TYPE_OPTIONS (line 13) | const DEAL_TYPE_OPTIONS = [
  constant DEAL_SIZE_OPTIONS (line 22) | const DEAL_SIZE_OPTIONS = [
  function Select (line 30) | function Select() {

FILE: components/welcome/special-upload.tsx
  function DeckGeneratorUpload (line 35) | function DeckGeneratorUpload() {

FILE: components/welcome/upload.tsx
  function Upload (line 14) | function Upload() {

FILE: components/yearly-recap/globe.tsx
  type GlobePoint (line 5) | interface GlobePoint {
  function Globe (line 12) | function Globe() {

FILE: components/yearly-recap/yearly-recap-banner.tsx
  function YearlyRecapBanner (line 90) | function YearlyRecapBanner() {

FILE: components/yearly-recap/yearly-recap-modal.tsx
  type YearlyRecapStats (line 38) | interface YearlyRecapStats {
  type YearlyRecapModalProps (line 63) | interface YearlyRecapModalProps {
  constant RECAP_GRADIENT (line 183) | const RECAP_GRADIENT = "";
  function YearlyRecapModal (line 195) | function YearlyRecapModal({
  function IntroSlide (line 620) | function IntroSlide({
  function GlobalFootprintSlide (line 651) | function GlobalFootprintSlide({ stats }: { stats: YearlyRecapStats }) {
  function MinutesSlide (line 688) | function MinutesSlide({ stats }: { stats: YearlyRecapStats }) {
  function ViewsStatsSlide (line 734) | function ViewsStatsSlide({ stats }: { stats: YearlyRecapStats }) {
  function MostActiveSlide (line 776) | function MostActiveSlide({ stats }: { stats: YearlyRecapStats }) {
  function SummarySlide (line 813) | function SummarySlide({ stats }: { stats: YearlyRecapStats }) {
  function ShareOfferSlide (line 853) | function ShareOfferSlide({ stats }: { stats: YearlyRecapStats }) {

FILE: context/pending-uploads-context.tsx
  type PendingUploadDocument (line 11) | type PendingUploadDocument = {
  type PendingUploadsContextType (line 28) | type PendingUploadsContextType = {

FILE: context/team-context.tsx
  type TeamContextProps (line 13) | interface TeamContextProps {
  type TeamContextType (line 17) | type TeamContextType = {

FILE: ee/emails/pause-resume-reminder.tsx
  type PauseResumeReminderEmailProps (line 15) | interface PauseResumeReminderEmailProps {
  function PauseResumeReminderEmail (line 26) | function PauseResumeReminderEmail({

FILE: ee/features/access-notifications/components/blocked-email-attempt.tsx
  function BlockedEmailAttempt (line 15) | function BlockedEmailAttempt({

FILE: ee/features/access-notifications/lib/report-denied-access-attempt.ts
  function reportDeniedAccessAttempt (line 7) | async function reportDeniedAccessAttempt(

FILE: ee/features/ai/components/agents-settings-card.tsx
  type AgentsSettingsCardProps (line 30) | interface AgentsSettingsCardProps {
  type IndexingRun (line 38) | interface IndexingRun {
  function AgentsSettingsCard (line 44) | function AgentsSettingsCard({
  function DataroomIndexingStatus (line 281) | function DataroomIndexingStatus({
  function DataroomDocumentIndexingRow (line 352) | function DataroomDocumentIndexingRow({

FILE: ee/features/ai/components/ai-indexing-status.tsx
  type AIIndexingStatusProps (line 13) | interface AIIndexingStatusProps {
  function AIIndexingStatus (line 24) | function AIIndexingStatus({

FILE: ee/features/ai/components/chat-message.tsx
  type ChatMessageProps (line 34) | interface ChatMessageProps {
  type FeedbackState (line 47) | type FeedbackState = "up" | "down" | null;
  function getViewerBasePath (line 49) | function getViewerBasePath(): string {
  function buildDocumentUrl (line 56) | function buildDocumentUrl(
  function normalizeAssistantLinkHref (line 65) | function normalizeAssistantLinkHref(href?: string): string | undefined {
  function AssistantLink (line 93) | function AssistantLink({
  function getFileIcon (line 118) | function getFileIcon(filename: string) {
  function SourcesSection (line 145) | function SourcesSection({ sources }: { sources: ChatStreamSource[] }) {
  function SuggestedQuestions (line 219) | function SuggestedQuestions({

FILE: ee/features/ai/components/document-ai-dialog.tsx
  type DocumentAIDialogProps (line 28) | interface DocumentAIDialogProps {
  function DocumentAIDialog (line 37) | function DocumentAIDialog({

FILE: ee/features/ai/components/document-context-selector.tsx
  type SelectedContextItem (line 37) | interface SelectedContextItem {
  type DocumentContextSelectorProps (line 43) | interface DocumentContextSelectorProps {
  function getDocumentsInFolder (line 60) | function getDocumentsInFolder(
  function DocumentContextSelector (line 83) | function DocumentContextSelector({
  type SelectedContextItemsProps (line 248) | interface SelectedContextItemsProps {
  function SelectedContextItems (line 253) | function SelectedContextItems({
  type ContextItemAttachmentProps (line 276) | interface ContextItemAttachmentProps {
  function ContextItemAttachment (line 282) | function ContextItemAttachment({

FILE: ee/features/ai/components/viewer-chat-panel.tsx
  type Message (line 61) | interface Message {
  function extractStreamMetadata (line 70) | function extractStreamMetadata(content: string): {
  constant REFERENCES_REGEX (line 91) | const REFERENCES_REGEX =
  constant REFERENCE_LINE_REGEX (line 93) | const REFERENCE_LINE_REGEX = /[-*]\s*\[(.+?)]\((.+?)\)(?:\s*-\s*p\.\s*(\...
  type ResolvedRef (line 95) | interface ResolvedRef {
  function parseMarkdownReferences (line 103) | function parseMarkdownReferences(content: string): {
  type ViewerChatPanelProps (line 129) | interface ViewerChatPanelProps {
  function ViewerChatPanel (line 138) | function ViewerChatPanel({ className }: ViewerChatPanelProps) {
  type ViewerChatPanelContentProps (line 173) | interface ViewerChatPanelContentProps {
  function ViewerChatPanelContent (line 203) | function ViewerChatPanelContent({

FILE: ee/features/ai/components/viewer-chat-provider.tsx
  type DataroomDocumentForChat (line 19) | interface DataroomDocumentForChat {
  type ViewerChatConfig (line 26) | interface ViewerChatConfig {
  type ViewerChatContextType (line 36) | interface ViewerChatContextType {
  function useViewerChat (line 60) | function useViewerChat() {
  function useViewerChatSafe (line 72) | function useViewerChatSafe() {
  type ViewerChatProviderProps (line 80) | interface ViewerChatProviderProps {
  function ViewerChatProvider (line 94) | function ViewerChatProvider({
  type ViewerChatLayoutProps (line 145) | interface ViewerChatLayoutProps {
  function ViewerChatLayout (line 154) | function ViewerChatLayout({

FILE: ee/features/ai/components/viewer-chat-toggle.tsx
  type ViewerChatToggleProps (line 9) | interface ViewerChatToggleProps {
  function ViewerChatToggle (line 18) | function ViewerChatToggle({ className }: ViewerChatToggleProps) {

FILE: ee/features/ai/components/viewer-thread-selector.tsx
  type Chat (line 33) | interface Chat {
  type GroupedChats (line 41) | interface GroupedChats {
  type ViewerThreadSelectorProps (line 47) | interface ViewerThreadSelectorProps {
  function groupChatsByDate (line 60) | function groupChatsByDate(chats: Chat[]): GroupedChats {
  function getChatDisplayTitle (line 84) | function getChatDisplayTitle(chat: Chat): string {
  function ViewerThreadSelector (line 95) | function ViewerThreadSelector({

FILE: ee/features/ai/hooks/use-ai-indexing-status.ts
  type UseAIIndexingStatusOptions (line 7) | interface UseAIIndexingStatusOptions {
  type UseAIIndexingStatusReturn (line 12) | interface UseAIIndexingStatusReturn {
  type RunStatusResponse (line 25) | interface RunStatusResponse {
  function useAIIndexingStatus (line 41) | function useAIIndexingStatus({
  function useAIIndexingBatchStatus (line 100) | function useAIIndexingBatchStatus() {

FILE: ee/features/ai/lib/chat/create-chat.ts
  type CreateChatOptions (line 3) | interface CreateChatOptions {
  function createChat (line 20) | async function createChat(options: CreateChatOptions) {

FILE: ee/features/ai/lib/chat/generate-chat-title.ts
  function generateChatTitle (line 9) | async function generateChatTitle(firstMessage: string): Promise<string> {

FILE: ee/features/ai/lib/chat/get-filtered-dataroom-document-ids.ts
  function getFilteredDataroomDocumentIds (line 11) | async function getFilteredDataroomDocumentIds(

FILE: ee/features/ai/lib/chat/send-message.ts
  type SendMessageOptions (line 6) | interface SendMessageOptions {
  type CitationCandidate (line 22) | interface CitationCandidate {
  type ResolvedReference (line 28) | interface ResolvedReference {
  constant CHAT_METADATA_PREFIX (line 36) | const CHAT_METADATA_PREFIX = "\n\n<!-- CHAT_METADATA:";
  constant CHAT_METADATA_SUFFIX (line 37) | const CHAT_METADATA_SUFFIX = " -->";
  type ChatStreamSource (line 39) | interface ChatStreamSource {
  type ChatStreamMetadata (line 48) | interface ChatStreamMetadata {
  function validatePageValue (line 53) | function validatePageValue(value: unknown): number | undefined {
  function normalizePageNumber (line 62) | function normalizePageNumber(value: unknown): number | undefined {
  function extractPageNumberFromRecord (line 69) | function extractPageNumberFromRecord(
  function extractFileCitations (line 108) | function extractFileCitations(response: unknown): CitationCandidate[] {
  function stripTrailingReferencesSection (line 171) | function stripTrailingReferencesSection(text: string): string {
  function escapeMarkdownLinkText (line 180) | function escapeMarkdownLinkText(value: string): string {
  function resolveReferencesFromCitations (line 187) | async function resolveReferencesFromCitations({
  function buildReferencesSection (line 365) | function buildReferencesSection(references: ResolvedReference[]): string {
  function generateSuggestedQuestions (line 382) | async function generateSuggestedQuestions(
  function buildStreamMetadataTail (line 413) | function buildStreamMetadataTail(
  function sendMessage (line 435) | async function sendMessage({

FILE: ee/features/ai/lib/file-processing/extract-document-metadata.ts
  type DocumentMetadata (line 3) | interface DocumentMetadata {
  function extractDocumentMetadata (line 18) | async function extractDocumentMetadata(

FILE: ee/features/ai/lib/file-processing/process-document-for-vector-store.ts
  function processDocumentForVectorStore (line 15) | async function processDocumentForVectorStore(

FILE: ee/features/ai/lib/permissions/validate-chat-access.ts
  type ValidateChatAccessOptions (line 3) | interface ValidateChatAccessOptions {
  function validateChatAccess (line 14) | async function validateChatAccess({

FILE: ee/features/ai/lib/stream/parse-text-stream.ts
  type TextStreamCallbacks (line 6) | interface TextStreamCallbacks {
  type TextStreamResult (line 12) | interface TextStreamResult {
  function parseTextStream (line 20) | async function parseTextStream(

FILE: ee/features/ai/lib/trigger/process-excel-for-ai.ts
  function excelToMarkdown (line 15) | function excelToMarkdown(workbook: XLSX.WorkBook): string {

FILE: ee/features/ai/lib/trigger/process-image-for-ai.ts
  constant IMAGE_ANALYSIS_PROMPT (line 13) | const IMAGE_ANALYSIS_PROMPT = `Analyze this image thoroughly and provide...

FILE: ee/features/ai/lib/trigger/types.ts
  type AIProcessingStatus (line 3) | type AIProcessingStatus =
  type AIProcessingMetadata (line 12) | type AIProcessingMetadata = {
  type ProcessDocumentPayload (line 23) | type ProcessDocumentPayload = {
  type ProcessFilePayload (line 44) | type ProcessFilePayload = {
  type AddToVectorStorePayload (line 54) | type AddToVectorStorePayload = {
  constant PDF_CONTENT_TYPES (line 70) | const PDF_CONTENT_TYPES = ["application/pdf"];
  constant EXCEL_CONTENT_TYPES (line 72) | const EXCEL_CONTENT_TYPES = [
  constant IMAGE_CONTENT_TYPES (line 78) | const IMAGE_CONTENT_TYPES = ["image/jpeg", "image/png", "image/webp"];
  constant SUPPORTED_AI_CONTENT_TYPES (line 80) | const SUPPORTED_AI_CONTENT_TYPES = [

FILE: ee/features/ai/lib/vector-stores/create-dataroom-vector-store.ts
  function createDataroomVectorStore (line 10) | async function createDataroomVectorStore({

FILE: ee/features/ai/lib/vector-stores/create-team-vector-store.ts
  function createTeamVectorStore (line 9) | async function createTeamVectorStore(

FILE: ee/features/ai/lib/vector-stores/delete-vector-store.ts
  function deleteVectorStore (line 7) | async function deleteVectorStore(vectorStoreId: string): Promise<void> {

FILE: ee/features/ai/lib/vector-stores/get-vector-store-info.ts
  function getVectorStoreInfo (line 9) | async function getVectorStoreInfo(

FILE: ee/features/ai/lib/vector-stores/remove-file-from-vector-store.ts
  function removeFileFromVectorStore (line 8) | async function removeFileFromVectorStore(

FILE: ee/features/ai/lib/vector-stores/upload-file-to-vector-store.ts
  type VectorStoreFileOptions (line 3) | interface VectorStoreFileOptions {
  function addFileToVectorStore (line 23) | async function addFileToVectorStore({

FILE: ee/features/ai/schemas/chat.ts
  type CreateChatInput (line 76) | type CreateChatInput = z.infer<typeof createChatSchema>;
  type SendMessageInput (line 77) | type SendMessageInput = z.infer<typeof sendMessageSchema>;
  type IndexDocumentInput (line 78) | type IndexDocumentInput = z.infer<typeof indexDocumentSchema>;
  type IndexDataroomInput (line 79) | type IndexDataroomInput = z.infer<typeof indexDataroomSchema>;
  type ChatQueryInput (line 80) | type ChatQueryInput = z.infer<typeof chatQuerySchema>;
  type EnableDocumentAgentsInput (line 81) | type EnableDocumentAgentsInput = z.infer<
  type EnableDataroomAgentsInput (line 84) | type EnableDataroomAgentsInput = z.infer<
  type EnableTeamAgentsInput (line 87) | type EnableTeamAgentsInput = z.infer<typeof enableTeamAgentsSchema>;

FILE: ee/features/billing/cancellation/api/automatic-unpause-route.ts
  function handleRoute (line 16) | async function handleRoute(req: NextApiRequest, res: NextApiResponse) {

FILE: ee/features/billing/cancellation/api/cancel-route.ts
  function handleRoute (line 13) | async function handleRoute(req: NextApiRequest, res: NextApiResponse) {

FILE: ee/features/billing/cancellation/api/cancellation-feedback-route.ts
  function handleRoute (line 19) | async function handleRoute(req: NextApiRequest, res: NextApiResponse) {

FILE: ee/features/billing/cancellation/api/pause-route.ts
  function handleRoute (line 20) | async function handleRoute(req: NextApiRequest, res: NextApiResponse) {

FILE: ee/features/billing/cancellation/api/reactivate-route.ts
  function handleRoute (line 13) | async function handleRoute(req: NextApiRequest, res: NextApiResponse) {

FILE: ee/features/billing/cancellation/api/retention-route.ts
  function handleRoute (line 14) | async function handleRoute(req: NextApiRequest, res: NextApiResponse) {

FILE: ee/features/billing/cancellation/api/send-pause-resume-notification.ts
  function handle (line 11) | async function handle(

FILE: ee/features/billing/cancellation/api/unpause-route.ts
  function handleRoute (line 18) | async function handleRoute(req: NextApiRequest, res: NextApiResponse) {

FILE: ee/features/billing/cancellation/components/cancellation-modal.tsx
  type CancellationStep (line 17) | type CancellationStep =
  type CancellationModalProps (line 26) | interface CancellationModalProps {
  function CancellationModal (line 31) | function CancellationModal({

FILE: ee/features/billing/cancellation/components/confirm-cancellation-modal.tsx
  type ConfirmCancellationModalProps (line 16) | interface ConfirmCancellationModalProps {
  function ConfirmCancellationModal (line 22) | function ConfirmCancellationModal({

FILE: ee/features/billing/cancellation/components/feedback-modal.tsx
  type FeedbackModalProps (line 11) | interface FeedbackModalProps {
  function FeedbackModal (line 23) | function FeedbackModal({

FILE: ee/features/billing/cancellation/components/pause-subscription-modal.tsx
  type PauseSubscriptionModalProps (line 18) | interface PauseSubscriptionModalProps {
  function PauseSubscriptionModal (line 26) | function PauseSubscriptionModal({

FILE: ee/features/billing/cancellation/components/reason-base-modal.tsx
  function CancellationBaseModal (line 7) | function CancellationBaseModal({

FILE: ee/features/billing/cancellation/components/retention-offer-modal.tsx
  type RetentionOfferModalProps (line 21) | interface RetentionOfferModalProps {
  function RetentionOfferModal (line 30) | function RetentionOfferModal({

FILE: ee/features/billing/cancellation/components/schedule-call-modal.tsx
  type ScheduleCallModalProps (line 11) | interface ScheduleCallModalProps {
  function ScheduleCallModal (line 18) | function ScheduleCallModal({

FILE: ee/features/billing/cancellation/constants.ts
  constant PAUSE_COUPON_ID (line 1) | const PAUSE_COUPON_ID = {

FILE: ee/features/billing/cancellation/emails/components/pause-resume-reminder.tsx
  type PauseResumeReminderEmailProps (line 17) | interface PauseResumeReminderEmailProps {
  function PauseResumeReminderEmail (line 27) | function PauseResumeReminderEmail({

FILE: ee/features/billing/cancellation/lib/constants.ts
  type CancellationReason (line 1) | type CancellationReason =

FILE: ee/features/billing/cancellation/lib/is-team-paused.ts
  function isTeamPaused (line 11) | function isTeamPaused(
  function isTeamPausedById (line 29) | async function isTeamPausedById(teamId: string): Promise<boolean> {

FILE: ee/features/billing/cancellation/lib/trigger/pause-resume-notification.ts
  type PauseResumeNotificationPayload (line 5) | type PauseResumeNotificationPayload = {

FILE: ee/features/billing/renewal-reminder/emails/subscription-renewal-reminder.tsx
  type SubscriptionRenewalReminderEmailProps (line 16) | interface SubscriptionRenewalReminderEmailProps {

FILE: ee/features/conversations/api/conversations-route.ts
  function handleRoute (line 276) | async function handleRoute(req: NextApiRequest, res: NextApiResponse) {

FILE: ee/features/conversations/api/send-conversation-new-message-notification.ts
  function handle (line 9) | async function handle(

FILE: ee/features/conversations/api/send-conversation-team-member-notification.ts
  function handle (line 9) | async function handle(

FILE: ee/features/conversations/api/team-conversations-route.ts
  function handleRoute (line 706) | async function handleRoute(req: NextApiRequest, res: NextApiResponse) {

FILE: ee/features/conversations/api/team-faqs-route.ts
  function handle (line 440) | async function handle(

FILE: ee/features/conversations/api/toggle-conversations-route.ts
  function toggleConversationsRoute (line 15) | async function toggleConversationsRoute(

FILE: ee/features/conversations/components/dashboard/conversation-list-item.tsx
  function ConversationListItem (line 7) | function ConversationListItem({

FILE: ee/features/conversations/components/dashboard/conversations-not-enabled-banner.tsx
  type ConversationsNotEnabledBannerProps (line 20) | interface ConversationsNotEnabledBannerProps {
  function ConversationsNotEnabledBanner (line 27) | function ConversationsNotEnabledBanner({

FILE: ee/features/conversations/components/dashboard/edit-faq-modal.tsx
  type EditFAQModalProps (line 31) | interface EditFAQModalProps {
  type EditFAQFormData (line 40) | interface EditFAQFormData {
  function EditFAQModal (line 67) | function EditFAQModal({

FILE: ee/features/conversations/components/dashboard/link-option-conversation-section.tsx
  function ConversationSection (line 7) | function ConversationSection({

FILE: ee/features/conversations/components/dashboard/publish-faq-modal.tsx
  type Message (line 29) | interface Message {
  type Conversation (line 37) | interface Conversation {
  type PublishFAQModalProps (line 54) | interface PublishFAQModalProps {
  type PublishFAQFormData (line 65) | interface PublishFAQFormData {
  function PublishFAQModal (line 94) | function PublishFAQModal({

FILE: ee/features/conversations/components/shared/conversation-document-context.tsx
  type ConversationDocumentContextProps (line 3) | interface ConversationDocumentContextProps {
  function ConversationDocumentContext (line 16) | function ConversationDocumentContext({

FILE: ee/features/conversations/components/shared/conversation-message.tsx
  function ConversationMessage (line 4) | function ConversationMessage({

FILE: ee/features/conversations/components/viewer/conversation-view-sidebar.tsx
  type Message (line 22) | interface Message {
  type Conversation (line 32) | interface Conversation {
  type CreateConversationData (line 50) | interface CreateConversationData {
  type ConversationSidebarProps (line 55) | type ConversationSidebarProps = {
  function ConversationViewSidebar (line 67) | function ConversationViewSidebar({

FILE: ee/features/conversations/components/viewer/faq-section.tsx
  type FAQ (line 17) | interface FAQ {
  type FAQSectionProps (line 30) | interface FAQSectionProps {
  function FAQSection (line 37) | function FAQSection({

FILE: ee/features/conversations/emails/components/conversation-notification.tsx
  function ConversationNotification (line 16) | function ConversationNotification({

FILE: ee/features/conversations/emails/components/conversation-team-notification.tsx
  function ConversationTeamNotification (line 16) | function ConversationTeamNotification({

FILE: ee/features/conversations/lib/api/conversations/index.ts
  type CreateConversationInput (line 6) | type CreateConversationInput = {
  method areConversationsAllowed (line 18) | async areConversationsAllowed(
  method createConversation (line 46) | async createConversation({
  method getConversationsForDataroom (line 122) | async getConversationsForDataroom({
  method toggleDataroomConversations (line 197) | async toggleDataroomConversations(dataroomId: string, enabled: boolean) {
  method toggleLinkConversations (line 205) | async toggleLinkConversations(linkId: string, enabled: boolean) {
  method getConversation (line 213) | async getConversation(
  method markConversationAsRead (line 262) | async markConversationAsRead(conversationId: string, userId: string) {
  method deleteConversation (line 301) | async deleteConversation(

FILE: ee/features/conversations/lib/api/messages/index.ts
  method addMessage (line 8) | async addMessage({
  method markMessageAsRead (line 98) | async markMessageAsRead(messageId: string) {
  method getUnreadMessages (line 106) | async getUnreadMessages(viewerId?: string, userId?: string) {

FILE: ee/features/conversations/lib/api/notifications/index.ts
  method toggleNotificationsForConversation (line 5) | async toggleNotificationsForConversation({

FILE: ee/features/conversations/lib/schemas/faq.ts
  type PublishFAQInput (line 109) | type PublishFAQInput = z.infer<typeof publishFAQSchema>;
  type UpdateFAQInput (line 110) | type UpdateFAQInput = z.infer<typeof updateFAQSchema>;
  type PublishFAQFormInput (line 111) | type PublishFAQFormInput = z.infer<typeof publishFAQFormSchema>;
  type EditFAQFormInput (line 112) | type EditFAQFormInput = z.infer<typeof editFAQFormSchema>;
  type FAQParamInput (line 113) | type FAQParamInput = z.infer<typeof faqParamSchema>;

FILE: ee/features/conversations/lib/trigger/conversation-message-notification.ts
  type NotificationPayload (line 6) | type NotificationPayload = {

FILE: ee/features/conversations/pages/conversation-detail.tsx
  type Message (line 57) | interface Message {
  type Conversation (line 67) | interface Conversation {
  type ConversationSummary (line 84) | interface ConversationSummary {
  function ConversationDetailPage (line 105) | function ConversationDetailPage() {
  function ConversationReplyForm (line 707) | function ConversationReplyForm({

FILE: ee/features/conversations/pages/conversation-overview.tsx
  type ConversationSummary (line 43) | interface ConversationSummary {
  function DataroomConversationsPage (line 64) | function DataroomConversationsPage() {

FILE: ee/features/conversations/pages/faq-overview.tsx
  type PublishedFAQ (line 67) | interface PublishedFAQ {
  function FAQOverview (line 116) | function FAQOverview() {

FILE: ee/features/conversions/python/docx-sanitizer.py
  function has_rtl_content (line 42) | def has_rtl_content(doc_content: str) -> bool:
  function downgrade_compat_mode (line 47) | def downgrade_compat_mode(content: str) -> tuple:
  function remove_glossary (line 61) | def remove_glossary(tmp_dir: str) -> bool:
  function _register_all_namespaces (line 109) | def _register_all_namespaces(path: str):
  function strip_numpages_fields_in_hf (line 119) | def strip_numpages_fields_in_hf(tmp_dir: str) -> int:
  function unwrap_sdt (line 179) | def unwrap_sdt(content: str) -> tuple:
  function repackage_docx (line 197) | def repackage_docx(tmp_dir: str, output_path: str):
  function sanitize_docx (line 217) | def sanitize_docx(input_path: str, output_path: str, mode: str = 'all') ...
  function check_rtl (line 307) | def check_rtl(input_path: str) -> bool:
  function main (line 319) | def main():

FILE: ee/features/dataroom-invitations/api/group-invite.ts
  function handle (line 15) | async function handle(

FILE: ee/features/dataroom-invitations/api/link-invite.ts
  function handle (line 18) | async function handle(

FILE: ee/features/dataroom-invitations/api/uninvited.ts
  function handle (line 9) | async function handle(

FILE: ee/features/dataroom-invitations/components/invite-viewers-modal.tsx
  type InviteViewersModalProps (line 31) | type InviteViewersModalProps = {
  type LinkOption (line 42) | type LinkOption = Pick<
  function parseRecipientInput (line 47) | function parseRecipientInput(value: string) {
  function InviteViewersModal (line 58) | function InviteViewersModal({

FILE: ee/features/dataroom-invitations/emails/components/dataroom-viewer-invitation.tsx
  function DataroomViewerInvitation (line 18) | function DataroomViewerInvitation({

FILE: ee/features/dataroom-invitations/lib/schema/dataroom-invitations.ts
  constant MAX_CUSTOM_MESSAGE_LENGTH (line 3) | const MAX_CUSTOM_MESSAGE_LENGTH = 500;
  type SendGroupInvitationInput (line 36) | type SendGroupInvitationInput = z.infer<typeof sendGroupInvitationSchema>;
  type SendLinkInvitationInput (line 37) | type SendLinkInvitationInput = z.infer<typeof sendLinkInvitationSchema>;

FILE: ee/features/dataroom-invitations/lib/swr/use-dataroom-invitations.ts
  type UninvitedMembersResponse (line 7) | type UninvitedMembersResponse = {
  function useUninvitedMembers (line 12) | function useUninvitedMembers(

FILE: ee/features/permissions/components/dataroom-link-sheet.tsx
  type ItemPermission (line 70) | type ItemPermission = Record<
  type DEFAULT_LINK_TYPE (line 75) | type DEFAULT_LINK_TYPE = BASE_DEFAULT_LINK_TYPE & {
  function DataroomLinkSheet (line 79) | function DataroomLinkSheet({

FILE: ee/features/permissions/components/permissions-sheet.tsx
  type FileOrFolder (line 88) | type FileOrFolder = {
  type ItemPermission (line 103) | type ItemPermission = Record<
  type ColumnExtra (line 108) | type ColumnExtra = {
  type PermissionsSheetProps (line 359) | interface PermissionsSheetProps {
  function PermissionsSheet (line 369) | function PermissionsSheet({

FILE: ee/features/security/lib/fraud-prevention.ts
  constant FRAUD_DECLINE_CODES (line 12) | const FRAUD_DECLINE_CODES = [
  function addEmailToStripeRadar (line 23) | async function addEmailToStripeRadar(email: string): Promise<boolean> {
  function addEmailToEdgeConfig (line 48) | async function addEmailToEdgeConfig(email: string): Promise<boolean> {
  function processPaymentFailure (line 109) | async function processPaymentFailure(

FILE: ee/features/security/lib/ratelimit.ts
  function checkRateLimit (line 31) | async function checkRateLimit(

FILE: ee/features/security/sso/components/directory-sync-config-modal.tsx
  type DirectorySyncConfigModalProps (line 31) | interface DirectorySyncConfigModalProps {
  function DirectorySyncConfigModal (line 35) | function DirectorySyncConfigModal({

FILE: ee/features/security/sso/components/saml-config-modal.tsx
  type SAMLConfigModalProps (line 33) | interface SAMLConfigModalProps {
  function SAMLConfigModal (line 37) | function SAMLConfigModal({ teamId }: SAMLConfigModalProps) {

FILE: ee/features/security/sso/components/sso-enforcement-toggle.tsx
  type SSOEnforcementToggleProps (line 21) | interface SSOEnforcementToggleProps {
  function SSOEnforcementToggle (line 25) | function SSOEnforcementToggle({ teamId }: SSOEnforcementToggleProps) {

FILE: ee/features/security/sso/components/sso-login.tsx
  type SSOLoginProps (line 15) | interface SSOLoginProps {
  function SSOLogin (line 19) | function SSOLogin({ autoExpand = false }: SSOLoginProps) {

FILE: ee/features/security/sso/constants.ts
  constant SAML_PROVIDERS (line 1) | const SAML_PROVIDERS = [
  type SAMLProviderKey (line 34) | type SAMLProviderKey = (typeof SAML_PROVIDERS)[number]["saml"];
  type SCIMProviderKey (line 35) | type SCIMProviderKey = (typeof SAML_PROVIDERS)[number]["scim"];

FILE: ee/features/security/sso/product.ts
  constant JACKSON_PRODUCT (line 6) | const JACKSON_PRODUCT = "papermark";

FILE: ee/features/storage/config.ts
  type StorageConfig (line 3) | interface StorageConfig {
  type StorageRegion (line 17) | type StorageRegion = "eu-central-1" | "us-east-2";
  function getStorageConfig (line 26) | function getStorageConfig(storageRegion?: string): StorageConfig {
  function getTeamStorageConfigById (line 90) | async function getTeamStorageConfigById(

FILE: ee/features/storage/s3-store.ts
  class MultiRegionS3Store (line 17) | class MultiRegionS3Store extends S3Store {
    method constructor (line 24) | constructor() {
    method extractTeamIdFromUploadId (line 82) | private extractTeamIdFromUploadId(uploadId: string): string | null {
    method shouldUseUSStorage (line 90) | private async shouldUseUSStorage(teamId: string): Promise<boolean> {
    method ensureCorrectRegion (line 115) | private async ensureCorrectRegion(uploadId: string): Promise<void> {
    method create (line 138) | async create(upload: Upload): Promise<Upload> {
    method write (line 147) | async write(stream: Readable, id: string, offset: number): Promise<num...
    method getUpload (line 156) | async getUpload(id: string): Promise<Upload> {
    method remove (line 165) | async remove(id: string): Promise<void> {
    method declareUploadLength (line 179) | async declareUploadLength(id: string, length: number): Promise<void> {
    method read (line 188) | async read(id: string): Promise<Readable> {

FILE: ee/features/templates/api/datarooms/apply-template.ts
  function handle (line 15) | async function handle(

FILE: ee/features/templates/api/datarooms/generate.ts
  function handle (line 18) | async function handle(

FILE: ee/features/templates/components/dataroom-templates.tsx
  constant TEMPLATES (line 24) | const TEMPLATES = [
  function DataroomTemplates (line 72) | function DataroomTemplates({

FILE: ee/features/templates/constants/dataroom-templates.ts
  type FolderTemplate (line 1) | interface FolderTemplate {
  constant DATAROOM_TEMPLATE_TYPES (line 6) | const DATAROOM_TEMPLATE_TYPES = [
  constant DATAROOM_TEMPLATES (line 18) | const DATAROOM_TEMPLATES: Record<

FILE: ee/features/templates/lib/prompts.ts
  type Prompts (line 3) | interface Prompts {
  function getPrompts (line 8) | async function getPrompts(): Promise<Prompts> {
  function getDataroomSystemPrompt (line 22) | async function getDataroomSystemPrompt(): Promise<string> {
  function getDataroomUserPrompt (line 34) | async function getDataroomUserPrompt(

FILE: ee/features/templates/schemas/dataroom-templates.ts
  type ApplyTemplateInput (line 77) | type ApplyTemplateInput = z.infer<typeof applyTemplateSchema>;
  type GenerateDataroomInput (line 78) | type GenerateDataroomInput = z.infer<typeof generateDataroomSchema>;
  type DataroomTemplateType (line 79) | type DataroomTemplateType = (typeof DATAROOM_TEMPLATE_TYPES)[number];

FILE: ee/features/workflows/components/step-form-dialog.tsx
  type WorkflowStep (line 23) | interface WorkflowStep {
  type Link (line 41) | interface Link {
  type StepFormDialogProps (line 54) | interface StepFormDialogProps {
  function StepFormDialog (line 64) | function StepFormDialog({

FILE: ee/features/workflows/components/step-list.tsx
  type WorkflowStep (line 6) | interface WorkflowStep {
  type StepListProps (line 30) | interface StepListProps {
  function StepList (line 37) | function StepList({

FILE: ee/features/workflows/components/workflow-access-view.tsx
  type WorkflowAccessViewProps (line 12) | interface WorkflowAccessViewProps {
  function WorkflowAccessView (line 19) | function WorkflowAccessView({

FILE: ee/features/workflows/components/workflow-empty-state.tsx
  type WorkflowEmptyStateProps (line 3) | interface WorkflowEmptyStateProps {
  function WorkflowEmptyState (line 8) | function WorkflowEmptyState({

FILE: ee/features/workflows/components/workflow-header.tsx
  type WorkflowHeaderProps (line 18) | interface WorkflowHeaderProps {
  function WorkflowHeader (line 28) | function WorkflowHeader({

FILE: ee/features/workflows/components/workflow-list.tsx
  type Workflow (line 7) | interface Workflow {
  type WorkflowListProps (line 24) | interface WorkflowListProps {
  function WorkflowList (line 28) | function WorkflowList({ workflows }: WorkflowListProps) {

FILE: ee/features/workflows/lib/engine.ts
  class WorkflowEngine (line 13) | class WorkflowEngine {
    method execute (line 17) | async execute(
    method evaluateConditions (line 249) | private async evaluateConditions(
    method evaluateCondition (line 271) | private async evaluateCondition(
    method evaluateEmailCondition (line 290) | private evaluateEmailCondition(
    method evaluateDomainCondition (line 325) | private evaluateDomainCondition(

FILE: ee/features/workflows/lib/types.ts
  type EmailCondition (line 22) | type EmailCondition = z.infer<typeof EmailConditionSchema>;
  type DomainCondition (line 23) | type DomainCondition = z.infer<typeof DomainConditionSchema>;
  type Condition (line 24) | type Condition = z.infer<typeof ConditionSchema>;
  type RouteAction (line 38) | type RouteAction = z.infer<typeof RouteActionSchema>;
  type Action (line 39) | type Action = z.infer<typeof ActionSchema>;
  type ConditionsConfig (line 56) | type ConditionsConfig = z.infer<typeof ConditionsConfigSchema>;
  type WorkflowStepDefinition (line 57) | type WorkflowStepDefinition = z.infer<
  type CreateWorkflowRequest (line 115) | type CreateWorkflowRequest = z.infer<typeof CreateWorkflowRequestSchema>;
  type UpdateWorkflowRequest (line 116) | type UpdateWorkflowRequest = z.infer<typeof UpdateWorkflowRequestSchema>;
  type CreateWorkflowStepRequest (line 117) | type CreateWorkflowStepRequest = z.infer<
  type UpdateWorkflowStepRequest (line 120) | type UpdateWorkflowStepRequest = z.infer<
  type ReorderStepsRequest (line 123) | type ReorderStepsRequest = z.infer<typeof ReorderStepsRequestSchema>;
  type VerifyEmailRequest (line 124) | type VerifyEmailRequest = z.infer<typeof VerifyEmailRequestSchema>;
  type AccessRequest (line 125) | type AccessRequest = z.infer<typeof AccessRequestSchema>;
  type WorkflowExecutionContext (line 129) | interface WorkflowExecutionContext {
  type WorkflowExecutionResult (line 137) | interface WorkflowExecutionResult {
  type StepExecutionResult (line 148) | interface StepExecutionResult {

FILE: ee/features/workflows/lib/validation.ts
  function validateConditions (line 28) | function validateConditions(
  function validateActions (line 50) | function validateActions(
  function validateWorkflowStep (line 75) | function validateWorkflowStep(
  function formatZodError (line 99) | function formatZodError(error: ZodError): string {

FILE: ee/features/workflows/pages/workflow-detail.tsx
  type Workflow (line 18) | interface Workflow {
  type WorkflowStep (line 31) | interface WorkflowStep {
  type Link (line 55) | interface Link {
  function WorkflowDetailPage (line 68) | function WorkflowDetailPage() {

FILE: ee/features/workflows/pages/workflow-new.tsx
  type Domain (line 20) | interface Domain {
  function NewWorkflowPage (line 25) | function NewWorkflowPage() {

FILE: ee/features/workflows/pages/workflow-overview.tsx
  type Workflow (line 18) | interface Workflow {
  function WorkflowsPage (line 35) | function WorkflowsPage() {

FILE: ee/limits/constants.ts
  type TPlanLimits (line 3) | type TPlanLimits = {
  constant FREE_PLAN_LIMITS (line 17) | const FREE_PLAN_LIMITS = {
  constant PRO_PLAN_LIMITS (line 29) | const PRO_PLAN_LIMITS = {
  constant BUSINESS_PLAN_LIMITS (line 41) | const BUSINESS_PLAN_LIMITS = {
  constant DATAROOMS_PLAN_LIMITS (line 56) | const DATAROOMS_PLAN_LIMITS = {
  constant DATAROOMS_PLUS_PLAN_LIMITS (line 71) | const DATAROOMS_PLUS_PLAN_LIMITS = {
  constant DATAROOMS_PREMIUM_PLAN_LIMITS (line 88) | const DATAROOMS_PREMIUM_PLAN_LIMITS = {
  constant PAUSED_PLAN_LIMITS (line 105) | const PAUSED_PLAN_LIMITS = {

FILE: ee/limits/handler.ts
  function handle (line 11) | async function handle(

FILE: ee/limits/server.ts
  function getLimits (line 62) | async function getLimits({

FILE: ee/limits/swr-handler.ts
  type LimitProps (line 10) | type LimitProps = z.infer<typeof configSchema> & {
  function useLimits (line 19) | function useLimits() {

FILE: ee/stripe/constants.ts
  type PlanEnum (line 1) | enum PlanEnum {
  constant PLAN_NAME_MAP (line 9) | const PLAN_NAME_MAP: Record<string, string> = {
  type PeriodType (line 19) | type PeriodType = "monthly" | "yearly";
  type Feature (line 21) | interface Feature {
  type PlanFeatures (line 33) | interface PlanFeatures {
  constant PLAN_PRICING (line 38) | const PLAN_PRICING = {
  constant BASE_FEATURES (line 71) | const BASE_FEATURES: Record<PlanEnum, PlanFeatures> = {
  type FeatureOptions (line 206) | interface FeatureOptions {
  function getPlanFeatures (line 216) | function getPlanFeatures(

FILE: ee/stripe/functions/get-coupon-from-plan.ts
  constant COUPON_MAP (line 3) | const COUPON_MAP = {

FILE: ee/stripe/functions/get-display-name-from-plan.ts
  function getDisplayNameFromPlan (line 3) | function getDisplayNameFromPlan(planSlug: string) {

FILE: ee/stripe/functions/get-price-id-from-plan.ts
  function getPriceIdFromPlan (line 3) | function getPriceIdFromPlan({

FILE: ee/stripe/functions/get-quantity-from-plan.ts
  function getQuantityFromPriceId (line 3) | function getQuantityFromPriceId(priceId?: string) {

FILE: ee/stripe/functions/get-subscription-item.ts
  type SubscriptionDiscount (line 3) | interface SubscriptionDiscount {
  function getSubscriptionItem (line 13) | async function getSubscriptionItem(

FILE: ee/stripe/index.ts
  function cancelSubscription (line 33) | async function cancelSubscription(

FILE: ee/stripe/utils.ts
  constant HISTORICAL_PRICE_IDS (line 5) | const HISTORICAL_PRICE_IDS: Record<string, Record<string, string>> = {
  function getHistoricalPlanFromPriceId (line 16) | function getHistoricalPlanFromPriceId(priceId: string, env: string) {
  function getPlanFromPriceId (line 37) | function getPlanFromPriceId(
  function isNewCustomer (line 71) | function isNewCustomer(
  function isUpgradedCustomer (line 88) | function isUpgradedCustomer(
  constant PLANS (line 105) | const PLANS = [

FILE: ee/stripe/webhooks/checkout-session-completed.ts
  function checkoutSessionCompleted (line 20) | async function checkoutSessionCompleted(

FILE: ee/stripe/webhooks/customer-subscription-deleted.ts
  function customerSubscriptionDeleted (line 11) | async function customerSubscriptionDeleted(

FILE: ee/stripe/webhooks/customer-subscription-updated.ts
  function customerSubsciptionUpdated (line 19) | async function customerSubsciptionUpdated(

FILE: ee/stripe/webhooks/invoice-upcoming.ts
  function invoiceUpcoming (line 8) | async function invoiceUpcoming(

FILE: lib/analytics/index.ts
  function useAnalytics (line 7) | function useAnalytics() {

FILE: lib/api/auth/passkey.ts
  function startServerPasskeyRegistration (line 7) | async function startServerPasskeyRegistration({
  function finishServerPasskeyRegistration (line 31) | async function finishServerPasskeyRegistration({
  function listUserPasskeys (line 52) | async function listUserPasskeys({ session }: { session: Session }) {
  function removeUserPasskey (line 94) | async function removeUserPasskey({

FILE: lib/api/documents/process-document.ts
  type ProcessDocumentParams (line 22) | type ProcessDocumentParams = {

FILE: lib/api/domains.ts
  function getDomainCount (line 6) | async function getDomainCount(domain: string) {
  function deleteDomain (line 27) | async function deleteDomain(

FILE: lib/api/domains/clear-team-redirects.ts
  function clearTeamDomainRedirects (line 10) | async function clearTeamDomainRedirects(

FILE: lib/api/domains/redis.ts
  constant DOMAIN_REDIRECT_PREFIX (line 3) | const DOMAIN_REDIRECT_PREFIX = "domain:redirect";
  constant PLANS_WITH_REDIRECTS (line 5) | const PLANS_WITH_REDIRECTS = new Set([
  function planSupportsRedirects (line 12) | function planSupportsRedirects(plan: string): boolean {
  function getRedisKey (line 17) | function getRedisKey(domain: string): string {
  function getDomainRedirectUrl (line 21) | async function getDomainRedirectUrl(
  function setDomainRedirectUrl (line 27) | async function setDomainRedirectUrl(
  function deleteDomainRedirectUrl (line 39) | async function deleteDomainRedirectUrl(domain: string): Promise<void> {

FILE: lib/api/domains/validate-redirect-url.ts
  type ValidationResult (line 7) | type ValidationResult =
  function validateRedirectUrl (line 11) | async function validateRedirectUrl(

FILE: lib/api/links/link-data.ts
  type LinkFetchStatus (line 19) | type LinkFetchStatus = "ok" | "not_found" | "archived" | "deleted" | "fr...
  type LinkFetchResult (line 21) | type LinkFetchResult =
  type LinkRecord (line 94) | type LinkRecord = Prisma.LinkGetPayload<{ select: typeof linkSelect }>;
  function getAllParentFolderIds (line 101) | async function getAllParentFolderIds(
  function fetchDataroomLinkData (line 137) | async function fetchDataroomLinkData({
  function fetchDataroomDocumentLinkData (line 341) | async function fetchDataroomDocumentLinkData({
  function fetchDocumentLinkData (line 481) | async function fetchDocumentLinkData({
  function processLinkData (line 545) | async function processLinkData(
  function fetchLinkDataById (line 740) | async function fetchLinkDataById({
  function fetchLinkDataByDomainSlug (line 771) | async function fetchLinkDataByDomainSlug({
  type CustomDomainLinkResult (line 807) | type CustomDomainLinkResult = LinkFetchResult;

FILE: lib/api/notification-helper.ts
  function sendNotification (line 3) | async function sendNotification({
  function sendViewerInvitation (line 33) | async function sendViewerInvitation({

FILE: lib/api/teams/is-saml-enforced-for-email-domain.ts
  function isSamlEnforcedForEmailDomain (line 9) | async function isSamlEnforcedForEmailDomain(

FILE: lib/api/views/send-webhook-event.ts
  function sendLinkViewWebhook (line 7) | async function sendLinkViewWebhook({

FILE: lib/auth/dataroom-auth.ts
  constant COOKIE_EXPIRATION_TIME (line 15) | const COOKIE_EXPIRATION_TIME = 23 * 60 * 60 * 1000;
  function normalizeIp (line 22) | function normalizeIp(ip: string): string {
  function generateSessionFingerprint (line 42) | function generateSessionFingerprint(headers: {
  function collectFingerprintHeaders (line 59) | function collectFingerprintHeaders(h: {
  function getFingerprintFromNextRequest (line 71) | function getFingerprintFromNextRequest(request: NextRequest): string {
  function getFingerprintFromPagesRequest (line 75) | function getFingerprintFromPagesRequest(req: NextApiRequest): string {
  type DataroomSession (line 98) | type DataroomSession = z.infer<typeof DataroomSessionSchema>;
  function createDataroomSession (line 100) | async function createDataroomSession(
  function verifyDataroomSession (line 137) | async function verifyDataroomSession(
  function verifyDataroomSessionInPagesRouter (line 190) | async function verifyDataroomSessionInPagesRouter(
  function getDataroomSessionByLinkIdInPagesRouter (line 208) | async function getDataroomSessionByLinkIdInPagesRouter(
  function updateDataroomSessionVerified (line 263) | async function updateDataroomSessionVerified(

FILE: lib/auth/link-session.ts
  constant COOKIE_EXPIRATION_TIME (line 14) | const COOKIE_EXPIRATION_TIME = 23 * 60 * 60 * 1000;
  type LinkSession (line 35) | type LinkSession = z.infer<typeof LinkSessionSchema>;
  function createLinkSession (line 37) | async function createLinkSession(
  function verifyLinkSession (line 91) | async function verifyLinkSession(
  function deleteLinkSession (line 172) | async function deleteLinkSession(
  function revokeLinkSession (line 182) | async function revokeLinkSession(linkId: string): Promise<void> {

FILE: lib/auth/preview-auth.ts
  constant PREVIEW_EXPIRATION_TIME (line 6) | const PREVIEW_EXPIRATION_TIME = 20 * 60 * 1000;
  type PreviewSession (line 14) | type PreviewSession = z.infer<typeof ZPreviewSessionSchema>;
  function createPreviewSession (line 16) | async function createPreviewSession(
  function verifyPreviewSession (line 45) | async function verifyPreviewSession(

FILE: lib/constants.ts
  constant FADE_IN_ANIMATION_SETTINGS (line 1) | const FADE_IN_ANIMATION_SETTINGS = {
  constant STAGGER_CHILD_VARIANTS (line 8) | const STAGGER_CHILD_VARIANTS = {
  constant PAPERMARK_HEADERS (line 17) | const PAPERMARK_HEADERS = {
  constant REACTIONS (line 24) | const REACTIONS = [
  constant ONE_SECOND (line 44) | const ONE_SECOND = 1000;
  constant ONE_MINUTE (line 45) | const ONE_MINUTE = ONE_SECOND * 60;
  constant TWO_MINUTES (line 46) | const TWO_MINUTES = ONE_MINUTE * 2;
  constant ONE_HOUR (line 47) | const ONE_HOUR = ONE_MINUTE * 60;
  constant ONE_DAY (line 48) | const ONE_DAY = ONE_HOUR * 24;
  constant ONE_WEEK (line 49) | const ONE_WEEK = ONE_DAY * 7;
  constant BLOCKED_PATHNAMES (line 52) | const BLOCKED_PATHNAMES = [
  constant EXCLUDED_PATHS (line 61) | const EXCLUDED_PATHS = ["/", "/register", "/privacy", "/view", "/notific...
  constant LIMITS (line 64) | const LIMITS = {
  constant SUPPORTED_DOCUMENT_MIME_TYPES (line 68) | const SUPPORTED_DOCUMENT_MIME_TYPES = [
  constant FREE_PLAN_ACCEPTED_FILE_TYPES (line 110) | const FREE_PLAN_ACCEPTED_FILE_TYPES = {
  constant FULL_PLAN_ACCEPTED_FILE_TYPES (line 121) | const FULL_PLAN_ACCEPTED_FILE_TYPES = {
  constant VIEWER_ACCEPTED_FILE_TYPES (line 162) | const VIEWER_ACCEPTED_FILE_TYPES = {
  constant SUPPORTED_DOCUMENT_SIMPLE_TYPES (line 190) | const SUPPORTED_DOCUMENT_SIMPLE_TYPES = [
  constant VIDEO_EVENT_TYPES (line 205) | const VIDEO_EVENT_TYPES = [
  constant COUNTRIES (line 227) | const COUNTRIES: { [key: string]: string } = {
  constant COUNTRY_CODES (line 480) | const COUNTRY_CODES = Object.keys(COUNTRIES) as [string, ...string[]];
  constant EU_COUNTRY_CODES (line 482) | const EU_COUNTRY_CODES = [
  constant SYSTEM_FILES (line 516) | const SYSTEM_FILES = [".DS_Store", "Thumbs.db", "node_modules"];

FILE: lib/constants/folder-constants.ts
  constant FOLDER_ICONS (line 40) | const FOLDER_ICONS = [
  type FolderIconId (line 78) | type FolderIconId = (typeof FOLDER_ICONS)[number]["id"];
  constant FOLDER_COLORS (line 81) | const FOLDER_COLORS = [
  type FolderColorId (line 140) | type FolderColorId = (typeof FOLDER_COLORS)[number]["id"];
  constant ALLOWED_FOLDER_ICONS (line 143) | const ALLOWED_FOLDER_ICONS = FOLDER_ICONS.map((icon) => icon.id);
  constant ALLOWED_FOLDER_COLORS (line 144) | const ALLOWED_FOLDER_COLORS = FOLDER_COLORS.map((color) => color.id);
  constant DEFAULT_FOLDER_ICON (line 147) | const DEFAULT_FOLDER_ICON: FolderIconId = "folder";
  constant DEFAULT_FOLDER_COLOR (line 148) | const DEFAULT_FOLDER_COLOR: FolderColorId = "gray";
  function getFolderIcon (line 151) | function getFolderIcon(iconId: string | null | undefined) {
  function getFolderColorClasses (line 157) | function getFolderColorClasses(colorId: string | null | undefined) {

FILE: lib/dataroom/build-folder-hierarchy.ts
  type FolderInput (line 3) | interface FolderInput {
  function buildFolderPathsFromHierarchy (line 19) | function buildFolderPathsFromHierarchy(
  function collectDescendantIds (line 68) | function collectDescendantIds(
  function buildFolderNameMap (line 100) | function buildFolderNameMap(

FILE: lib/dataroom/index-generator.ts
  type GenerateIndexOptions (line 11) | interface GenerateIndexOptions {
  type DataroomDocumentWithVersion (line 17) | interface DataroomDocumentWithVersion {
  function generateDataroomIndex (line 49) | async function generateDataroomIndex(

FILE: lib/documents/create-document.ts
  type DocumentData (line 4) | type DocumentData = {

FILE: lib/documents/move-documents.ts
  type Folder (line 50) | interface Folder {

FILE: lib/documents/move-folder.ts
  type Folder (line 27) | interface Folder {

FILE: lib/dub.ts
  function getDubDiscountForExternalUserId (line 7) | async function getDubDiscountForExternalUserId(externalId: string) {

FILE: lib/emails/process-dataroom-digest.ts
  function processDataroomDigest (line 11) | async function processDataroomDigest(frequency: "daily" | "weekly") {
  function processBatch (line 36) | async function processBatch(batch: DigestBatch, frequency: "daily" | "we...

FILE: lib/emails/send-dataroom-info.ts
  constant USECASE_SUBJECTS (line 7) | const USECASE_SUBJECTS = {

FILE: lib/emails/send-onboarding.ts
  type EmailType (line 11) | type EmailType =

FILE: lib/emails/send-upgrade-personal-welcome.ts
  constant PLAN_TYPE_MAP (line 7) | const PLAN_TYPE_MAP = {

FILE: lib/emails/send-upgrade-plan.ts
  constant PLAN_TYPE_MAP (line 6) | const PLAN_TYPE_MAP = {

FILE: lib/emails/send-verification-request.ts
  constant LOGIN_CODE_PREFIX (line 16) | const LOGIN_CODE_PREFIX = "login_code:";
  constant LOGIN_CODE_EMAIL_PREFIX (line 17) | const LOGIN_CODE_EMAIL_PREFIX = "login_code:email:";
  constant TOKEN_EXPIRATION_SECONDS (line 19) | const TOKEN_EXPIRATION_SECONDS = 15 * 60;
  type LoginCodeData (line 21) | interface LoginCodeData {

FILE: lib/errorHandler.ts
  function errorhandler (line 3) | function errorhandler(err: unknown, res: NextApiResponse) {
  class TeamError (line 14) | class TeamError extends Error {
    method constructor (line 16) | constructor(public message: string) {
  class DocumentError (line 21) | class DocumentError extends Error {
    method constructor (line 23) | constructor(public message: string) {

FILE: lib/featureFlags/index.ts
  type BetaFeatures (line 3) | type BetaFeatures =
  type BetaFeaturesRecord (line 21) | type BetaFeaturesRecord = Record<BetaFeatures, string[]>;

FILE: lib/files/bulk-download-presign.ts
  type S3KeyInfo (line 6) | interface S3KeyInfo {
  constant THREE_DAYS_IN_SECONDS (line 12) | const THREE_DAYS_IN_SECONDS = 3 * 24 * 60 * 60;
  function parseS3PresignedUrl (line 21) | function parseS3PresignedUrl(presignedUrl: string): S3KeyInfo {
  function generateFreshPresignedUrl (line 60) | async function generateFreshPresignedUrl(

FILE: lib/files/bulk-download.ts
  class S3DownloadService (line 7) | class S3DownloadService {
    method constructor (line 8) | constructor(private s3: S3Client) {}
    method createLazyDownloadStreamFrom (line 10) | public createLazyDownloadStreamFrom(bucket: string, key: string): Read...
    method createLazyDownloadStreamFromUrl (line 22) | public createLazyDownloadStreamFromUrl(url: string): Readable {
    method initDownloadStream (line 34) | private async initDownloadStream(
    method initUrlDownloadStream (line 65) | private async initUrlDownloadStream(url: string, stream: PassThrough) {

FILE: lib/files/copy-file-server.ts
  function copyFolder (line 96) | async function copyFolder(

FILE: lib/files/delete-file-server.ts
  type DeleteFileOptions (line 8) | type DeleteFileOptions = {

FILE: lib/files/delete-team-files-server.ts
  type DeleteFilesOptions (line 6) | type DeleteFilesOptions = {

FILE: lib/files/get-file.ts
  type GetFileOptions (line 5) | type GetFileOptions = {

FILE: lib/files/put-file-server.ts
  type File (line 14) | type File = {

FILE: lib/files/put-file.ts
  constant MULTIPART_THRESHOLD (line 84) | const MULTIPART_THRESHOLD = 10 * 1024 * 1024;
  constant PART_SIZE (line 85) | const PART_SIZE = 10 * 1024 * 1024;

FILE: lib/files/stream-file-server.ts
  type StreamFile (line 10) | type StreamFile = {

FILE: lib/files/tus-redis-locker.ts
  type RedisLockerOptions (line 25) | interface RedisLockerOptions {
  class RedisLocker (line 30) | class RedisLocker implements Locker {
    method constructor (line 34) | constructor(options: RedisLockerOptions) {
    method newLock (line 39) | newLock(id: string) {
  class RedisLock (line 44) | class RedisLock implements Lock {
    method constructor (line 45) | constructor(
    method lock (line 51) | async lock(
    method acquireLock (line 68) | protected async acquireLock(
    method unlock (line 110) | async unlock(): Promise<void> {
    method waitTimeout (line 121) | protected waitTimeout(signal: AbortSignal) {

FILE: lib/files/tus-upload.ts
  type ResumableUploadParams (line 5) | type ResumableUploadParams = {
  type UploadResult (line 15) | type UploadResult = {
  function resumableUpload (line 26) | function resumableUpload({

FILE: lib/files/viewer-tus-upload.ts
  type ViewerUploadParams (line 5) | type ViewerUploadParams = {
  type UploadResult (line 18) | type UploadResult = {
  function viewerUpload (line 30) | function viewerUpload({

FILE: lib/folders/create-folder.ts
  function isSystemFile (line 5) | function isSystemFile(name: string): boolean {
  type CreateFolderResponse (line 12) | interface CreateFolderResponse {
  function createFolderInDataroom (line 18) | async function createFolderInDataroom({
  function createFolderInMainDocs (line 51) | async function createFolderInMainDocs({
  function determineFolderPaths (line 81) | function determineFolderPaths({
  function createFolderInBoth (line 99) | async function createFolderInBoth({

FILE: lib/hooks/use-breakpoint.ts
  function useBreakpoint (line 3) | function useBreakpoint(breakpoint: number) {

FILE: lib/hooks/use-disable-print.ts
  type UseDisablePrintOptions (line 3) | interface UseDisablePrintOptions {
  function useDisablePrint (line 8) | function useDisablePrint({

FILE: lib/hooks/use-feature-flags.ts
  function useFeatureFlags (line 10) | function useFeatureFlags() {

FILE: lib/hooks/use-is-admin.ts
  function useIsAdmin (line 11) | function useIsAdmin() {

FILE: lib/hooks/use-lazy-pages.ts
  type PageData (line 3) | type PageData = {
  type FetchPagesResponse (line 16) | type FetchPagesResponse = {
  type UseLazyPagesOptions (line 20) | type UseLazyPagesOptions = {
  constant DEFAULT_PRELOAD_RADIUS (line 28) | const DEFAULT_PRELOAD_RADIUS = 5;
  function useLazyPages (line 30) | function useLazyPages({

FILE: lib/hooks/use-mobile.tsx
  constant MOBILE_BREAKPOINT (line 3) | const MOBILE_BREAKPOINT = 768
  function useIsMobile (line 5) | function useIsMobile() {

FILE: lib/id-helper.ts
  function encodeBase58 (line 3) | function encodeBase58(buf: Buffer): string {
  class IdGenerator (line 11) | class IdGenerator<TPrefixes extends string> {
    method constructor (line 18) | constructor(prefixes: Record<TPrefixes, string>) {

FILE: lib/incoming-webhooks/index.ts
  function generateBase62String (line 6) | function generateBase62String(length: number): string {
  function encodeTeamId (line 19) | function encodeTeamId(teamId: string): string {
  function decodeTeamId (line 30) | function decodeTeamId(encoded: string): string {
  function generateWebhookId (line 38) | function generateWebhookId(teamId: string): string {
  function extractTeamId (line 47) | function extractTeamId(webhookId: string): string | null {
  function isValidWebhookId (line 59) | function isValidWebhookId(webhookId: string): boolean {
  function generateWebhookSecret (line 71) | function generateWebhookSecret(): string {

FILE: lib/integrations/install.ts
  type InstallIntegration (line 8) | interface InstallIntegration {

FILE: lib/integrations/slack/client.ts
  class SlackClient (line 9) | class SlackClient {
    method constructor (line 15) | constructor() {
    method getChannels (line 144) | async getChannels(accessToken: string): Promise<SlackChannel[]> {
    method sendMessage (line 199) | async sendMessage(
  function getSlackClient (line 240) | function getSlackClient(): SlackClient {

FILE: lib/integrations/slack/env.ts
  type SlackEnv (line 10) | type SlackEnv = z.infer<typeof envSchema>;

FILE: lib/integrations/slack/events.ts
  class SlackEventManager (line 8) | class SlackEventManager {
    method constructor (line 11) | constructor() {
    method isViewerDomainIgnored (line 18) | private isViewerDomainIgnored(
    method processEvent (line 39) | async processEvent(eventData: SlackEventData): Promise<void> {
    method sendSlackNotification (line 92) | private async sendSlackNotification(
    method getNotificationChannels (line 149) | private async getNotificationChannels(
  function notifyDocumentView (line 166) | async function notifyDocumentView(
  function notifyDataroomAccess (line 172) | async function notifyDataroomAccess(
  function notifyDocumentDownload (line 181) | async function notifyDocumentDownload(

FILE: lib/integrations/slack/templates.ts
  function linkRef (line 10) | function linkRef(
  function createSlackMessage (line 19) | async function createSlackMessage(
  function createDocumentViewMessage (line 58) | async function createDocumentViewMessage(
  function createDataroomAccessMessage (line 157) | async function createDataroomAccessMessage(
  function createDocumentDownloadMessage (line 253) | async function createDocumentDownloadMessage(
  function createDocumentViewPausedMessage (line 371) | async function createDocumentViewPausedMessage(
  function createDataroomAccessPausedMessage (line 447) | async function createDataroomAccessPausedMessage(
  function getDocumentInfo (line 521) | async function getDocumentInfo(documentId: string) {
  function getDataroomInfo (line 538) | async function getDataroomInfo(dataroomId: string) {
  function getLinkInfo (line 568) | async function getLinkInfo(linkId: string) {
  function getViewInfo (line 584) | async function getViewInfo(viewId: string) {

FILE: lib/integrations/slack/types.ts
  type SlackCredential (line 3) | type SlackCredential = {
  type SlackCredentialPublic (line 13) | type SlackCredentialPublic = {
  type SlackConfiguration (line 17) | type SlackConfiguration = {
  type SlackChannel (line 22) | interface SlackChannel {
  type SlackIntegration (line 30) | type SlackIntegration = Omit<
  type SlackIntegrationServer (line 38) | type SlackIntegrationServer = Omit<
  type SlackMessage (line 46) | interface SlackMessage {
  type SlackEventData (line 55) | interface SlackEventData {
  type SlackNotificationType (line 69) | type SlackNotificationType =
  type SlackChannelConfig (line 74) | interface SlackChannelConfig {

FILE: lib/integrations/slack/utils.ts
  function deriveKey (line 8) | function deriveKey(keyMaterial: string): Buffer {
  function isTokenEncrypted (line 17) | function isTokenEncrypted(token: string): boolean {
  function encryptSlackToken (line 35) | function encryptSlackToken(token: string): string {
  function decryptSlackToken (line 70) | function decryptSlackToken(encryptedToken: string): string {

FILE: lib/jackson.ts
  function sanitizePostgresUrl (line 22) | function sanitizePostgresUrl(rawUrl: string): string {
  function getJacksonDbUrl (line 42) | function getJacksonDbUrl(): string {
  function getJacksonOptions (line 59) | function getJacksonOptions(): JacksonOption {
  function jackson (line 82) | async function jackson() {

FILE: lib/middleware/app.ts
  constant LOGIN_PATH (line 5) | const LOGIN_PATH = "/login";
  constant DEFAULT_AUTH_REDIRECT_PATH (line 6) | const DEFAULT_AUTH_REDIRECT_PATH = "/dashboard";
  function normalizeNextPath (line 8) | function normalizeNextPath(nextPath: string | null): string {
  function AppMiddleware (line 35) | async function AppMiddleware(req: NextRequest) {

FILE: lib/middleware/domain.ts
  function DomainMiddleware (line 6) | async function DomainMiddleware(req: NextRequest) {

FILE: lib/middleware/incoming-webhooks.ts
  function IncomingWebhookMiddleware (line 3) | async function IncomingWebhookMiddleware(req: NextRequest) {
  function isWebhookPath (line 20) | function isWebhookPath(host: string | null) {

FILE: lib/middleware/posthog.ts
  function PostHogMiddleware (line 3) | async function PostHogMiddleware(req: NextRequest) {

FILE: lib/notion/utils.ts
  function extractPageReferencesFromRichText (line 11) | function extractPageReferencesFromRichText(
  function normalizeRecordMap (line 59) | function normalizeRecordMap(recordMap: ExtendedRecordMap): void {
  function fetchMissingPageReferences (line 102) | async function fetchMissingPageReferences(
  function extractPageIdFromCustomNotionUrl (line 229) | function extractPageIdFromCustomNotionUrl(url: string): string | null {
  function isCustomNotionDomain (line 254) | async function isCustomNotionDomain(url: string): Promise<boolean> {
  function getNotionPageIdFromSlug (line 269) | async function getNotionPageIdFromSlug(

FILE: lib/posthog.ts
  function getPostHogConfig (line 1) | function getPostHogConfig(): { key: string; host: string } | null {

FILE: lib/redis-download-job-store.ts
  type DownloadJobStatus (line 5) | type DownloadJobStatus =
  type DownloadJob (line 11) | interface DownloadJob {
  constant JOB_PREFIX (line 42) | const JOB_PREFIX = "download_job:";
  constant TEAM_JOBS_PREFIX (line 43) | const TEAM_JOBS_PREFIX = "team_download_jobs:";
  constant VIEWER_JOBS_PREFIX (line 44) | const VIEWER_JOBS_PREFIX = "viewer_download_jobs:";
  constant JOB_TTL (line 45) | const JOB_TTL = 60 * 60 * 24 * 3;
  class RedisDownloadJobStore (line 47) | class RedisDownloadJobStore {
    method getJobKey (line 48) | private getJobKey(jobId: string): string {
    method getTeamJobsKey (line 52) | private getTeamJobsKey(teamId: string): string {
    method getViewerJobsKey (line 56) | private getViewerJobsKey(linkId: string, viewerEmail: string): string {
    method createJob (line 60) | async createJob(
    method getJob (line 96) | async getJob(jobId: string): Promise<DownloadJob | null> {
    method updateJob (line 117) | async updateJob(
    method getTeamJobs (line 138) | private async getTeamJobs(
    method getDataroomJobs (line 162) | async getDataroomJobs(
    method getViewerJobs (line 182) | async getViewerJobs(

FILE: lib/redis-job-store.ts
  type ExportJobStatus (line 5) | type ExportJobStatus = "PENDING" | "PROCESSING" | "COMPLETED" | "FAILED";
  type ExportJob (line 7) | interface ExportJob {
  type ExportJobCleanupItem (line 26) | interface ExportJobCleanupItem {
  constant JOB_PREFIX (line 32) | const JOB_PREFIX = "export_job:";
  constant USER_JOBS_PREFIX (line 33) | const USER_JOBS_PREFIX = "user_jobs:";
  constant TEAM_JOBS_PREFIX (line 34) | const TEAM_JOBS_PREFIX = "team_jobs:";
  constant CLEANUP_QUEUE_PREFIX (line 35) | const CLEANUP_QUEUE_PREFIX = "cleanup_blobs:";
  constant JOB_TTL (line 36) | const JOB_TTL = 60 * 60 * 24 * 3;
  class RedisJobStore (line 38) | class RedisJobStore {
    method getJobKey (line 39) | private getJobKey(jobId: string): string {
    method getUserJobsKey (line 43) | private getUserJobsKey(userId: string): string {
    method getTeamJobsKey (line 47) | private getTeamJobsKey(teamId: string): string {
    method getCleanupQueueKey (line 51) | private getCleanupQueueKey(): string {
    method createJob (line 55) | async createJob(
    method getJob (line 86) | async getJob(jobId: string): Promise<ExportJob | null> {
    method updateJob (line 107) | async updateJob(
    method scheduleBlobForCleanup (line 133) | async scheduleBlobForCleanup(blobUrl: string, jobId: string): Promise<...
    method getBlobsForCleanup (line 148) | async getBlobsForCleanup(
    method removeBlobFromCleanupQueue (line 180) | async removeBlobFromCleanupQueue(
    method deleteJob (line 195) | async deleteJob(jobId: string): Promise<boolean> {
    method getUserJobs (line 215) | async getUserJobs(userId: string, limit: number = 20): Promise<ExportJ...
    method getTeamJobs (line 236) | async getTeamJobs(teamId: string, limit: number = 20): Promise<ExportJ...
    method getResourceJobs (line 257) | async getResourceJobs(
    method getUserTeamJobs (line 278) | async getUserTeamJobs(
    method cleanupExpiredJobs (line 289) | async cleanupExpiredJobs(): Promise<void> {

FILE: lib/redis/dataroom-notification-queue.ts
  constant ITEM_TTL_SECONDS (line 3) | const ITEM_TTL_SECONDS = 8 * 24 * 60 * 60;
  type QueueItem (line 5) | type QueueItem = {
  type DigestViewerEntry (line 11) | type DigestViewerEntry = {
  function itemsKey (line 17) | function itemsKey(viewerId: string, dataroomId: string) {
  function viewerSetKey (line 21) | function viewerSetKey(frequency: "daily" | "weekly") {
  function encodeViewerEntry (line 25) | function encodeViewerEntry(entry: DigestViewerEntry): string {
  function decodeViewerEntry (line 29) | function decodeViewerEntry(encoded: string): DigestViewerEntry {
  function queueNotification (line 34) | async function queueNotification({
  type DigestBatch (line 66) | type DigestBatch = {
  function popDigestQueue (line 73) | async function popDigestQueue(

FILE: lib/sheet/index.ts
  type RowData (line 3) | type RowData = { [key: string]: any };
  type SheetData (line 4) | type SheetData = {

FILE: lib/swr/use-agreements.ts
  type AgreementWithLinksCount (line 7) | interface AgreementWithLinksCount extends Agreement {
  function useAgreements (line 13) | function useAgreements() {

FILE: lib/swr/use-annotations.ts
  type Annotation (line 5) | interface Annotation {
  function useAnnotations (line 28) | function useAnnotations(documentId: string, teamId: string) {
  function useViewerAnnotations (line 46) | function useViewerAnnotations(

FILE: lib/swr/use-billing.ts
  type BillingProps (line 10) | interface BillingProps {
  function useBilling (line 21) | function useBilling() {
  type BasePlan (line 39) | type BasePlan =
  type PlanWithTrial (line 49) | type PlanWithTrial = `${BasePlan}+drtrial`;
  type PlanWithOld (line 50) | type PlanWithOld = `${BasePlan}+old` | `${BasePlan}+drtrial+old`;
  type PlanResponse (line 52) | type PlanResponse = {
  type PlanDetails (line 66) | interface PlanDetails {
  function parsePlan (line 72) | function parsePlan(plan: BasePlan | PlanWithTrial | PlanWithOld): PlanDe...
  function usePlan (line 91) | function usePlan({

FILE: lib/swr/use-brand.ts
  function useBrand (line 11) | function useBrand() {
  function useDataroomBrand (line 30) | function useDataroomBrand({

FILE: lib/swr/use-dataroom-document-stats.ts
  type TDataroomDocumentStats (line 9) | type TDataroomDocumentStats = TStatsData & {
  function useDataroomDocumentStats (line 13) | function useDataroomDocumentStats(

FILE: lib/swr/use-dataroom-groups.ts
  function useDataroomGroups (line 18) | function useDataroomGroups({ documentId }: { documentId?: string } = {}) {
  function useDataroomGroupLinks (line 57) | function useDataroomGroupLinks() {
  type ViewerGroupWithMembers (line 83) | type ViewerGroupWithMembers = ViewerGroup & {
  function useDataroomGroup (line 88) | function useDataroomGroup() {
  function useDataroomGroupPermissions (line 121) | function useDataroomGroupPermissions() {

FILE: lib/swr/use-dataroom-permission-groups.ts
  function useDataroomPermissionGroups (line 9) | function useDataroomPermissionGroups() {

FILE: lib/swr/use-dataroom-stats.ts
  type TDataroomStatsData (line 10) | type TDataroomStatsData = {
  function useDataroomStats (line 20) | function useDataroomStats({
  function useDataroomVisitorUserAgent (line 84) | function useDataroomVisitorUserAgent(viewId: string) {

FILE: lib/swr/use-dataroom-view-document-stats.ts
  type DocumentViewStats (line 6) | type DocumentViewStats = {
  function useDataroomViewDocumentStats (line 15) | function useDataroomViewDocumentStats({
  type PageDurationData (line 47) | type PageDurationData = {
  function useDataroomDocumentPageStats (line 52) | function useDataroomDocumentPageStats({

FILE: lib/swr/use-dataroom.ts
  type DataroomFolderWithCount (line 14) | type DataroomFolderWithCount = DataroomFolder & {
  function useDataroom (line 21) | function useDataroom() {
  function useDataroomLinks (line 67) | function useDataroomLinks() {
  function useDataroomItems (line 90) | function useDataroomItems({
  function useDataroomDocuments (line 160) | function useDataroomDocuments() {
  function useDataroomFolders (line 186) | function useDataroomFolders({
  type DataroomFolderWithDocuments (line 220) | type DataroomFolderWithDocuments = DataroomFolder & {
  function useDataroomFoldersTree (line 235) | function useDataroomFoldersTree({
  function useDataroomFolderWithParents (line 262) | function useDataroomFolderWithParents({
  type DataroomFolderDocument (line 291) | type DataroomFolderDocument = DataroomDocument & {
  function useDataroomFolderDocuments (line 306) | function useDataroomFolderDocuments({ name }: { name: string[] }) {
  function useDataroomViewers (line 335) | function useDataroomViewers({ dataroomId }: { dataroomId: string }) {
  type DataroomVisitsResponse (line 356) | type DataroomVisitsResponse = {
  function useDataroomVisits (line 361) | function useDataroomVisits({
  type DataroomDocumentViewHistory (line 389) | type DataroomDocumentViewHistory = {
  type DataroomDocumentUploadViewHistory (line 410) | type DataroomDocumentUploadViewHistory = {
  function useDataroomVisitHistory (line 416) | function useDataroomVisitHistory({

FILE: lib/swr/use-datarooms-simple.ts
  type DataroomSimple (line 7) | type DataroomSimple = Pick<Dataroom, "id" | "name" | "internalName" | "c...
  type DataroomsSimpleResponse (line 9) | type DataroomsSimpleResponse = {
  function useDataroomsSimple (line 20) | function useDataroomsSimple() {

FILE: lib/swr/use-datarooms.ts
  type DataroomWithCount (line 9) | type DataroomWithCount = Pick<Dataroom, "id" | "name" | "createdAt"> & {
  type DataroomsResponse (line 26) | type DataroomsResponse = {
  function useDatarooms (line 31) | function useDatarooms() {

FILE: lib/swr/use-document-overview.ts
  type DocumentOverview (line 9) | interface DocumentOverview {
  function useDocumentOverview (line 33) | function useDocumentOverview() {

FILE: lib/swr/use-document-preview.ts
  function useDocumentPreview (line 7) | function useDocumentPreview(documentId: string, isOpen: boolean) {

FILE: lib/swr/use-document-stats.ts
  function useDocumentStats (line 8) | function useDocumentStats(documentId: string | null | undefined) {

FILE: lib/swr/use-document.ts
  function useDocument (line 11) | function useDocument() {
  function useDocumentLinks (line 57) | function useDocumentLinks() {
  type ViewWithDuration (line 89) | interface ViewWithDuration extends View {
  type TStatsData (line 117) | type TStatsData = {
  function useDocumentVisits (line 124) | function useDocumentVisits(page: number, limit: number) {
  type DocumentProcessingStatus (line 154) | interface DocumentProcessingStatus {
  function useDocumentProcessingStatus (line 160) | function useDocumentProcessingStatus(documentVersionId: string) {
  function useDocumentThumbnail (line 180) | function useDocumentThumbnail(

FILE: lib/swr/use-documents.ts
  function useDocuments (line 10) | function useDocuments() {
  function useFolderDocuments (line 60) | function useFolderDocuments({ name }: { name: string[] }) {
  type FolderWithCount (line 83) | type FolderWithCount = Folder & {
  type FolderWithCountAndPath (line 90) | type FolderWithCountAndPath = FolderWithCount & {
  function useFolder (line 94) | function useFolder({ name }: { name: string[] }) {
  type FolderWithDocuments (line 121) | type FolderWithDocuments = Folder & {
  function useFolders (line 130) | function useFolders() {
  function useRootFolders (line 150) | function useRootFolders() {
  function useHiddenDocuments (line 170) | function useHiddenDocuments() {

FILE: lib/swr/use-domains.ts
  function useDomains (line 7) | function useDomains({ enabled = false }: { enabled?: boolean } = {}) {

FILE: lib/swr/use-folders.ts
  type FolderWithParents (line 6) | type FolderWithParents = {
  function useFolderWithParents (line 18) | function useFolderWithParents({ name }: { name: string[] }) {

FILE: lib/swr/use-invitations.ts
  function useInvitations (line 7) | function useInvitations() {

FILE: lib/swr/use-invoices.ts
  type Invoice (line 6) | interface Invoice {
  function useInvoices (line 20) | function useInvoices() {

FILE: lib/swr/use-link.ts
  function useLink (line 11) | function useLink() {
  function useDomainLink (line 34) | function useDomainLink() {
  type ViewWithDuration (line 61) | interface ViewWithDuration extends View {
  type LinkVisitsResponse (line 69) | interface LinkVisitsResponse {
  function useLinkVisits (line 74) | function useLinkVisits(linkId: string) {

FILE: lib/swr/use-passkeys.ts
  type PasskeyCredential (line 5) | interface PasskeyCredential {
  function usePasskeys (line 16) | function usePasskeys() {

FILE: lib/swr/use-saml.ts
  function useSAML (line 7) | function useSAML() {

FILE: lib/swr/use-scim.ts
  function useSCIM (line 7) | function useSCIM() {

FILE: lib/swr/use-slack-channels.ts
  function useSlackChannels (line 7) | function useSlackChannels({ enabled = true }: { enabled?: boolean }) {

FILE: lib/swr/use-slack-integration.ts
  function useSlackIntegration (line 8) | function useSlackIntegration({ enabled = true }: { enabled?: boolean }) {

FILE: lib/swr/use-stats.ts
  type TStatsData (line 10) | type TStatsData = {
  function useStats (line 20) | function useStats({
  type StatsViewData (line 49) | interface StatsViewData {
  function useVisitorStats (line 56) | function useVisitorStats(viewId: string) {
  function useVisitorUserAgent (line 84) | function useVisitorUserAgent(viewId: string) {

FILE: lib/swr/use-tags.ts
  function useTags (line 37) | function useTags({

FILE: lib/swr/use-team-ai.ts
  type TeamAISettings (line 6) | interface TeamAISettings {
  function useTeamAI (line 18) | function useTeamAI() {

FILE: lib/swr/use-team-settings.ts
  type TeamSettings (line 5) | interface TeamSettings {
  function useTeamSettings (line 15) | function useTeamSettings(teamId: string | undefined | null) {

FILE: lib/swr/use-team.ts
  function useGetTeam (line 7) | function useGetTeam() {

FILE: lib/swr/use-teams.ts
  function useTeams (line 9) | function useTeams() {

FILE: lib/swr/use-viewer.ts
  type ViewerWithViews (line 9) | type ViewerWithViews = {
  function useViewer (line 40) | function useViewer(

FILE: lib/swr/use-viewers.ts
  type ViewerWithStats (line 9) | type ViewerWithStats = {
  type ViewersResponse (line 18) | type ViewersResponse = {
  function useViewers (line 34) | function useViewers(

FILE: lib/swr/use-visitor-groups.ts
  type VisitorGroupWithCount (line 7) | type VisitorGroupWithCount = VisitorGroup & {
  function useVisitorGroups (line 13) | function useVisitorGroups() {

FILE: lib/team/helper.ts
  type ITeamUserAndDocument (line 8) | interface ITeamUserAndDocument {
  type IDocumentWithLink (line 16) | interface IDocumentWithLink {
  function getTeamWithUsersAndDocument (line 22) | async function getTeamWithUsersAndDocument({
  function getDocumentWithTeamAndUser (line 89) | async function getDocumentWithTeamAndUser({

FILE: lib/tracking/record-link-view.ts
  function recordLinkView (line 14) | async function recordLinkView({

FILE: lib/tracking/safe-page-view-tracker.ts
  type TrackingData (line 4) | interface TrackingData {
  type TrackingOptions (line 18) | interface TrackingOptions {
  function useSafePageViewTracker (line 27) | function useSafePageViewTracker(options: TrackingOptions = {}) {

FILE: lib/tracking/tracking-config.ts
  constant TRACKING_CONFIG (line 1) | const TRACKING_CONFIG = {
  function getTrackingOptions (line 17) | function getTrackingOptions(

FILE: lib/tracking/video-tracking.ts
  type VideoTrackingEvent (line 3) | type VideoTrackingEvent = {
  type EventType (line 20) | type EventType = (typeof VIDEO_EVENT_TYPES)[number];
  function debounce (line 23) | function debounce<T extends (...args: any[]) => any>(
  class VideoTracker (line 52) | class VideoTracker {
    method constructor (line 70) | constructor(
    method addEventListenerWithCleanup (line 110) | private addEventListenerWithCleanup(
    method trackEvent (line 120) | private async trackEvent(eventType: EventType, endTime?: number, useBe...
    method setupEventListeners (line 193) | private setupEventListeners() {
    method startPeriodicTracking (line 324) | private startPeriodicTracking() {
    method stopPeriodicTracking (line 329) | private stopPeriodicTracking() {
    method updateConfig (line 333) | public updateConfig(
    method trackVisibilityChange (line 341) | public trackVisibilityChange(isVisible: boolean) {
    method cleanup (line 354) | public cleanup() {

FILE: lib/trigger/bulk-download.ts
  constant MAX_FILES_PER_BATCH (line 9) | const MAX_FILES_PER_BATCH = 500;
  constant MAX_ZIP_SIZE_BYTES (line 13) | const MAX_ZIP_SIZE_BYTES = 500 * 1024 * 1024;
  function generateTimestamp (line 19) | function generateTimestamp(): string {
  function generateZipFileName (line 31) | function generateZipFileName(
  type BulkDownloadPayload (line 45) | type BulkDownloadPayload = {
  type ProcessDownloadBatchParams (line 329) | interface ProcessDownloadBatchParams {
  type ProcessDownloadBatchResult (line 342) | interface ProcessDownloadBatchResult {
  function processDownloadBatch (line 347) | async function processDownloadBatch({
  type FileBatch (line 395) | interface FileBatch {
  type FileInfo (line 401) | interface FileInfo {
  function splitFilesIntoBatches (line 408) | function splitFilesIntoBatches(
  function buildBatchFromFiles (line 472) | function buildBatchFromFiles(
  function sendEmailNotification (line 520) | async function sendEmailNotification({

FILE: lib/trigger/convert-files.ts
  type ConvertPayload (line 15) | type ConvertPayload = {

FILE: lib/trigger/dataroom-change-notification.ts
  type NotificationPayload (line 7) | type NotificationPayload = {

FILE: lib/trigger/dataroom-upload-notification.ts
  type UploadNotificationPayload (line 5) | type UploadNotificationPayload = {

FILE: lib/trigger/export-visits.ts
  function escapeCsvField (line 15) | function escapeCsvField(field: string | number | null | undefined): stri...
  function createCsvRow (line 36) | function createCsvRow(fields: (string | number | null | undefined)[]): s...
  type ExportVisitsPayload (line 46) | type ExportVisitsPayload = {
  function isViewDuringPause (line 184) | function isViewDuringPause(
  function exportDocumentVisits (line 195) | async function exportDocumentVisits(
  function collectUniqueCustomFields (line 398) | function collectUniqueCustomFields(
  function generateCustomFieldHeaders (line 430) | function generateCustomFieldHeaders(
  function extractCustomFieldValues (line 442) | function extractCustomFieldValues(
  function exportDataroomVisits (line 493) | async function exportDataroomVisits(

FILE: lib/trigger/pdf-to-image-route.ts
  type ConvertPdfToImagePayload (line 8) | type ConvertPdfToImagePayload = {

FILE: lib/types.ts
  type CustomUser (line 17) | type CustomUser = NextAuthUser & PrismaUser;
  type CreateUserEmailProps (line 19) | interface CreateUserEmailProps {
  type DocumentWithLinksAndLinkCountAndViewCount (line 26) | interface DocumentWithLinksAndLinkCountAndViewCount extends Document {
  type DocumentWithVersion (line 41) | interface DocumentWithVersion extends Document {
  type LinkWithViews (line 61) | interface LinkWithViews extends Link {
  type LinkWithDocument (line 73) | interface LinkWithDocument extends Link {
  type LinkWithDataroomDocument (line 98) | interface LinkWithDataroomDocument extends Link {
  type LinkWithDataroom (line 126) | interface LinkWithDataroom extends Link {
  type Geo (line 166) | interface Geo {
  type DomainVerificationStatusProps (line 176) | type DomainVerificationStatusProps =
  type DomainResponse (line 185) | interface DomainResponse {
  type DomainConfigResponse (line 206) | interface DomainConfigResponse {
  type DomainVerificationResponse (line 222) | interface DomainVerificationResponse {
  type AnalyticsEvents (line 242) | type AnalyticsEvents =
  type Team (line 305) | interface Team {
  type TeamDetail (line 315) | interface TeamDetail {
  type WatermarkConfig (line 364) | type WatermarkConfig = z.infer<typeof WatermarkConfigSchema>;
  type NotionTheme (line 366) | type NotionTheme = "light" | "dark";
  type BasePlan (line 368) | type BasePlan =
  type TagColorProps (line 388) | type TagColorProps = (typeof tagColors)[number];
  type TagsWithTotalCount (line 390) | interface TagsWithTotalCount {
  type TagProps (line 395) | interface TagProps {

FILE: lib/types/document-preview.ts
  type DocumentPreviewData (line 1) | interface DocumentPreviewData {

FILE: lib/types/index-file.ts
  type DataroomIndexEntry (line 1) | interface DataroomIndexEntry {
  type DataroomIndex (line 15) | interface DataroomIndex {
  type IndexFileFormat (line 26) | type IndexFileFormat = "excel" | "csv" | "json";

FILE: lib/utils.ts
  function cn (line 17) | function cn(...inputs: ClassValue[]) {
  function classNames (line 21) | function classNames(...classes: string[]) {
  function getExtension (line 26) | function getExtension(url: string) {
  function getFileNameWithPdfExtension (line 35) | function getFileNameWithPdfExtension(filename?: string): string {
  type SWRError (line 43) | interface SWRError extends Error {
  function fetcher (line 47) | async function fetcher<JSON = any>(
  constant LOG_TIMEOUT_MS (line 89) | const LOG_TIMEOUT_MS = 2500;
  function bytesToSize (line 162) | function bytesToSize(bytes: number) {
  function capitalize (line 196) | function capitalize(str: string) {
  function nFormatter (line 241) | function nFormatter(num?: number, digits?: number) {
  function hashPassword (line 290) | async function hashPassword(password: string): Promise<string> {
  function checkPassword (line 296) | async function checkPassword(
  function copyToClipboard (line 304) | function copyToClipboard(text: string, message: string): void {
  function safeSlugify (line 360) | function safeSlugify(input: string): string {
  function constructMetadata (line 393) | function constructMetadata({
  function generateEncrpytedPassword (line 549) | async function generateEncrpytedPassword(
  function decryptEncrpytedPassword (line 578) | function decryptEncrpytedPassword(password: string): string {
  type FilterMode (line 608) | type FilterMode = "email" | "domain" | "both";
  function hexToRgb (line 630) | function hexToRgb(hex: string) {
  constant PRESET_OPTIONS (line 686) | const PRESET_OPTIONS: { label: string; value: number }[] = [
  constant WITH_CUSTOM_PRESET_OPTION (line 699) | const WITH_CUSTOM_PRESET_OPTION: {
  function safeTemplateReplace (line 804) | function safeTemplateReplace(
  function serializeFileSize (line 828) | function serializeFileSize(obj: any): any {

FILE: lib/utils/calculate-hierarchical-indexes.ts
  type DataroomItem (line 5) | interface DataroomItem {
  type HierarchicalItem (line 14) | interface HierarchicalItem extends DataroomItem {
  function sortItems (line 22) | function sortItems(items: DataroomItem[]): DataroomItem[] {
  function buildHierarchy (line 43) | function buildHierarchy(
  function assignHierarchicalIndexes (line 71) | function assignHierarchicalIndexes(
  function flattenHierarchy (line 90) | function flattenHierarchy(items: HierarchicalItem[]): Array<{
  function calculateAndUpdateHierarchicalIndexes (line 119) | async function calculateAndUpdateHierarchicalIndexes(

FILE: lib/utils/create-adaptive-surface-palette.ts
  type Rgb (line 1) | type Rgb = { r: number; g: number; b: number };
  type AdaptiveSurfacePalette (line 3) | type AdaptiveSurfacePalette = {
  constant LIGHT_TEXT (line 26) | const LIGHT_TEXT: Rgb = { r: 248, g: 250, b: 252 };
  constant DARK_TEXT (line 27) | const DARK_TEXT: Rgb = { r: 15, g: 23, b: 42 };
  constant FALLBACK_BG (line 28) | const FALLBACK_BG: Rgb = { r: 3, g: 7, b: 18 };
  function clamp (line 30) | function clamp(value: number, min: number, max: number) {
  function hexToRgb (line 34) | function hexToRgb(hex: string): Rgb | null {
  function rgbStringToRgb (line 55) | function rgbStringToRgb(input: string): Rgb | null {
  function parseToRgb (line 68) | function parseToRgb(color: string | null | undefined): Rgb {
  function toCssRgb (line 73) | function toCssRgb(rgb: Rgb) {
  function mixRgb (line 77) | function mixRgb(background: Rgb, foreground: Rgb, amount: number): Rgb {
  function toLinearChannel (line 86) | function toLinearChannel(channel: number) {
  function luminance (line 91) | function luminance(rgb: Rgb) {
  function contrastRatio (line 99) | function contrastRatio(a: Rgb, b: Rgb) {
  function saturation (line 107) | function saturation(rgb: Rgb) {
  function createAdaptiveSurfacePalette (line 119) | function createAdaptiveSurfacePalette(

FILE: lib/utils/csv.ts
  function downloadCSV (line 3) | function downloadCSV(data: any[], filename: string) {

FILE: lib/utils/decode-base64url.ts
  function decodeBase64Url (line 1) | function decodeBase64Url(base64url: string) {

FILE: lib/utils/determine-text-color.ts
  function hexToRgb (line 1) | function hexToRgb(hex: string) {
  function luminance (line 20) | function luminance(r: number, g: number, b: number) {
  function determineTextColor (line 24) | function determineTextColor(hexColor: string | null | undefined) {

FILE: lib/utils/email-domain.ts
  constant GENERIC_EMAIL_DOMAINS (line 1) | const GENERIC_EMAIL_DOMAINS = [
  function extractEmailDomain (line 49) | function extractEmailDomain(email: string): string | null {
  function normalizeListEntry (line 74) | function normalizeListEntry(entry: string): string {
  function isEmailMatched (line 81) | function isEmailMatched(email: string, entry: string): boolean {

FILE: lib/utils/generate-checksum.ts
  function generateChecksum (line 3) | function generateChecksum(url: string): string {

FILE: lib/utils/generate-jwt.ts
  constant JWT_SECRET (line 3) | const JWT_SECRET = process.env.NEXT_PRIVATE_UNSUBSCRIBE_JWT_SECRET as st...
  type JWTPayload (line 5) | type JWTPayload = {
  function generateJWT (line 16) | function generateJWT(
  function verifyJWT (line 33) | function verifyJWT<T = JWTPayload>(token: string): T | null {

FILE: lib/utils/generate-otp.ts
  function generateOTP (line 1) | function generateOTP(): string {

FILE: lib/utils/generate-trigger-auth-token.ts
  function generateTriggerPublicAccessToken (line 3) | async function generateTriggerPublicAccessToken(tag: string) {

FILE: lib/utils/generate-trigger-status.ts
  type TDocumentProgressStatus (line 9) | type TDocumentProgressStatus = z.infer<typeof ZDocumentProgressStatus>;
  type TDocumentProgressMetadata (line 15) | type TDocumentProgressMetadata = z.infer<typeof ZDocumentProgressMetadata>;
  function updateStatus (line 20) | function updateStatus(status: TDocumentProgressStatus) {
  function parseStatus (line 29) | function parseStatus(data: unknown): TDocumentProgressStatus {

FILE: lib/utils/geo.ts
  function getGeoData (line 3) | function getGeoData(headers: {
  constant LOCALHOST_GEO_DATA (line 25) | const LOCALHOST_GEO_DATA = {
  constant LOCALHOST_IP (line 34) | const LOCALHOST_IP = "127.0.0.1";

FILE: lib/utils/get-content-type.ts
  function getSupportedContentType (line 1) | function getSupportedContentType(contentType: string): string | null {
  function getExtensionFromContentType (line 55) | function getExtensionFromContentType(
  function supportsAdvancedExcelMode (line 130) | function supportsAdvancedExcelMode(

FILE: lib/utils/get-file-icon.tsx
  function fileIcon (line 13) | function fileIcon({

FILE: lib/utils/get-file-size-limits.ts
  type FileSizeLimits (line 1) | type FileSizeLimits = {
  function getFileSizeLimits (line 10) | function getFileSizeLimits({
  function getFileSizeLimit (line 46) | function getFileSizeLimit(

FILE: lib/utils/global-block-list.ts
  function checkGlobalBlockList (line 4) | function checkGlobalBlockList(

FILE: lib/utils/hierarchical-display.ts
  function useHierarchicalDisplayName (line 6) | function useHierarchicalDisplayName(
  function getHierarchicalDisplayName (line 23) | function getHierarchicalDisplayName(
  constant HIERARCHICAL_DISPLAY_STYLE (line 38) | const HIERARCHICAL_DISPLAY_STYLE = {

FILE: lib/utils/ip.ts
  function getIpAddress (line 1) | function getIpAddress(headers: {

FILE: lib/utils/link-url.ts
  function constructLinkUrl (line 1) | function constructLinkUrl(link: {

FILE: lib/utils/reliable-tracking.ts
  type TrackingData (line 1) | interface TrackingData {
  type TrackingOptions (line 12) | interface TrackingOptions {
  function trackPageViewReliably (line 17) | async function trackPageViewReliably(
  function trackPageView (line 119) | async function trackPageView(data: TrackingData): Promise<void> {

FILE: lib/utils/sanitize-html.ts
  function sanitizePlainText (line 12) | function sanitizePlainText(content: string) {
  constant MAX_MESSAGE_LENGTH (line 22) | const MAX_MESSAGE_LENGTH = 4000;
  function validateContent (line 24) | function validateContent(html: string, length: number = MAX_MESSAGE_LENG...

FILE: lib/utils/trigger-utils.ts
  type TQueueConfig (line 3) | type TQueueConfig = {

FILE: lib/utils/unsubscribe.ts
  constant JWT_SECRET (line 3) | const JWT_SECRET = process.env.NEXT_PRIVATE_UNSUBSCRIBE_JWT_SECRET as st...
  constant UNSUBSCRIBE_BASE_URL (line 4) | const UNSUBSCRIBE_BASE_URL = process.env.NEXT_PUBLIC_BASE_URL as string;
  type UnsubscribePayload (line 6) | type UnsubscribePayload = {
  function generateUnsubscribeUrl (line 13) | function generateUnsubscribeUrl(payload: UnsubscribePayload): string {
  function verifyUnsubscribeToken (line 28) | function verifyUnsubscribeToken(

FILE: lib/utils/use-at-bottom.ts
  function useAtBottom (line 3) | function useAtBottom(offset = 0) {

FILE: lib/utils/use-copy-to-clipboard.ts
  type useCopyToClipboardProps (line 7) | interface useCopyToClipboardProps {
  function useCopyToClipboard (line 11) | function useCopyToClipboard({

FILE: lib/utils/use-enter-submit.ts
  function useEnterSubmit (line 3) | function useEnterSubmit(): {

FILE: lib/utils/use-media-query.ts
  function useMediaQuery (line 3) | function useMediaQuery() {

FILE: lib/utils/use-progress-status.ts
  type IDocumentProgressStatus (line 8) | interface IDocumentProgressStatus {
  function useDocumentProgressStatus (line 14) | function useDocumentProgressStatus(

FILE: lib/utils/user-agent.ts
  function isBot (line 3) | function isBot(input: string) {
  function userAgentFromString (line 9) | function userAgentFromString(input: string | undefined): UserAgent {
  type UserAgent (line 16) | interface UserAgent {

FILE: lib/webhook/constants.ts
  constant TEAM_LEVEL_WEBHOOK_TRIGGERS (line 1) | const TEAM_LEVEL_WEBHOOK_TRIGGERS = [
  constant DOCUMENT_LEVEL_WEBHOOK_TRIGGERS (line 8) | const DOCUMENT_LEVEL_WEBHOOK_TRIGGERS = [
  constant LINK_LEVEL_WEBHOOK_TRIGGERS (line 13) | const LINK_LEVEL_WEBHOOK_TRIGGERS = [
  constant WEBHOOK_TRIGGERS (line 18) | const WEBHOOK_TRIGGERS = [
  constant WEBHOOK_TRIGGER_DESCRIPTIONS (line 24) | const WEBHOOK_TRIGGER_DESCRIPTIONS = {

FILE: lib/webhook/triggers/document-created.ts
  function sendDocumentCreatedWebhook (line 7) | async function sendDocumentCreatedWebhook({

FILE: lib/webhook/triggers/link-created.ts
  function sendLinkCreatedWebhook (line 7) | async function sendLinkCreatedWebhook({

FILE: lib/webhook/types.ts
  type WebhookTrigger (line 11) | type WebhookTrigger = keyof typeof WEBHOOK_TRIGGER_DESCRIPTIONS;
  type WebhookPayload (line 13) | type WebhookPayload =
  type EventDataProps (line 20) | type EventDataProps = WebhookPayload["data"];

FILE: lib/webstorage.ts
  method getItem (line 7) | getItem(key: string) {
  method setItem (line 16) | setItem(key: string, value: string) {

FILE: lib/year-in-review/calculate-percentile.ts
  function calculateViewPercentile (line 4) | async function calculateViewPercentile(teamTotalViews: number) {
  function getYearInReviewStats (line 43) | async function getYearInReviewStats(teamId: string) {

FILE: lib/year-in-review/get-stats.ts
  constant COUNTRY_CENTROIDS (line 9) | const COUNTRY_CENTROIDS: { [key: string]: { lat: number; lng: number } }...
  function calculateDistance (line 159) | function calculateDistance(
  constant DEFAULT_ORIGIN (line 179) | const DEFAULT_ORIGIN = { lat: 37.77, lng: -122.42 };
  function calculateTotalDistance (line 183) | function calculateTotalDistance(
  function getYearInReviewStats (line 204) | async function getYearInReviewStats(

FILE: lib/year-in-review/index.ts
  function initializeEmailQueue (line 5) | async function initializeEmailQueue() {

FILE: lib/year-in-review/send-emails.ts
  constant BATCH_SIZE (line 11) | const BATCH_SIZE = 100;
  constant MAX_ATTEMPTS (line 12) | const MAX_ATTEMPTS = 3;
  constant RATE_LIMIT_DELAY (line 13) | const RATE_LIMIT_DELAY = 10000;
  type YearReviewStats (line 15) | type YearReviewStats = {
  type EmailWithMetadata (line 33) | type EmailWithMetadata = {
  function msToMinutes (line 50) | function msToMinutes(ms: number): number {
  function processEmailQueue (line 54) | async function processEmailQueue() {

FILE: lib/zod/schemas/folders.ts
  type FolderPath (line 30) | type FolderPath = z.infer<typeof folderPathSchema>;

FILE: lib/zod/schemas/multipart.ts
  type MultipartUploadRequest (line 49) | type MultipartUploadRequest = z.infer<typeof MultipartUploadSchema>;
  type MultipartPart (line 50) | type MultipartPart = z.infer<typeof MultipartPartSchema>;
  type MultipartInitiateRequest (line 51) | type MultipartInitiateRequest = z.infer<typeof MultipartInitiateSchema>;
  type MultipartGetPartUrlsRequest (line 52) | type MultipartGetPartUrlsRequest = z.infer<
  type MultipartCompleteRequest (line 55) | type MultipartCompleteRequest = z.infer<typeof MultipartCompleteSchema>;

FILE: lib/zod/schemas/notifications.ts
  type NotificationFrequency (line 4) | type NotificationFrequency = z.infer<typeof NotificationFrequency>;

FILE: lib/zod/schemas/presets.ts
  type PresetDataSchema (line 76) | type PresetDataSchema = z.infer<typeof presetDataSchema>;

FILE: lib/zod/schemas/webhooks.ts
  type WebhookPayload (line 146) | type WebhookPayload = z.infer<typeof webhookPayloadSchema>;
  type LinkCreatedWebhookPayload (line 147) | type LinkCreatedWebhookPayload = z.infer<
  type DocumentCreatedWebhookPayload (line 150) | type DocumentCreatedWebhookPayload = z.infer<
  type DataroomCreatedWebhookPayload (line 153) | type DataroomCreatedWebhookPayload = z.infer<

FILE: middleware.ts
  function isAnalyticsPath (line 12) | function isAnalyticsPath(path: string) {
  function isCustomDomain (line 22) | function isCustomDomain(host: string) {
  function middleware (line 50) | async function middleware(req: NextRequest, ev: NextFetchEvent) {

FILE: next.config.mjs
  method redirects (line 16) | async redirects() {
  method headers (line 42) | async headers() {
  function prepareRemotePatterns (line 208) | function prepareRemotePatterns() {

FILE: pages/404.tsx
  function NotFound (line 3) | function NotFound({ message }: { message?: string }) {

FILE: pages/_app.tsx
  function App (line 22) | function App({

FILE: pages/_document.tsx
  function Document (line 3) | function Document() {

FILE: pages/account/security.tsx
  function registerPasskey (line 34) | async function registerPasskey() {
  function removePasskey (line 65) | async function removePasskey(credentialId: string) {
  function formatDate (line 85) | function formatDate(dateString: string) {

FILE: pages/api/account/index.ts
  function handle (line 25) | async function handle(

FILE: pages/api/account/passkeys.ts
  function handler (line 9) | async function handler(

FILE: pages/api/analytics/index.ts
  constant INTERVALS (line 27) | const INTERVALS = {
  function handler (line 33) | async function handler(

FILE: pages/api/auth/[...nextauth].ts
  constant VERCEL_DEPLOYMENT (line 25) | const VERCEL_DEPLOYMENT = !!process.env.VERCEL_URL;
  function getMainDomainUrl (line 27) | function getMainDomainUrl(): string {
  method profile (line 57) | profile(profile, tokens) {
  method sendVerificationRequest (line 70) | async sendVerificationRequest({ identifier, url }) {
  method authorize (line 102) | async authorize({ userId }) {
  method authorize (line 160) | async authorize(credentials) {
  method createUser (line 266) | async createUser(message) {
  function handler (line 471) | async function handler(

FILE: pages/api/conversations/[[...conversations]].ts
  function handler (line 5) | async function handler(

FILE: pages/api/feedback/index.ts
  function handle (line 5) | async function handle(

FILE: pages/api/file/browser-upload.ts
  function handler (line 11) | async function handler(

FILE: pages/api/file/image-upload.ts
  function handler (line 29) | async function handler(

FILE: pages/api/file/notion/index.ts
  function handle (line 11) | async function handle(

FILE: pages/api/file/s3/get-presigned-get-url-proxy.ts
  function handler (line 10) | async function handler(

FILE: pages/api/file/s3/get-presigned-get-url.ts
  function handler (line 11) | async function handler(

FILE: pages/api/file/s3/get-presigned-post-url.ts
  function handler (line 16) | async function handler(

FILE: pages/api/file/s3/multipart.ts
  function handler (line 22) | async function handler(

FILE: pages/api/file/tus-viewer/[[...file]].ts
  method namingFunction (line 35) | async namingFunction(req, metadata) {
  method generateUrl (line 84) | generateUrl(req, { proto, host, path, id }) {
  method getFileIdFromRequest (line 89) | getFileIdFromRequest(req) {
  method onResponseError (line 94) | onResponseError(req, res, err) {
  method onUploadCreate (line 101) | async onUploadCreate(req, res, upload) {
  method onUploadFinish (line 145) | async onUploadFinish(req, res, upload) {
  method onIncomingRequest (line 183) | async onIncomingRequest(req, res, uploadId) {
  function handler (line 249) | function handler(req: NextApiRequest, res: NextApiResponse) {

FILE: pages/api/file/tus/[[...file]].ts
  constant FREE_PLAN (line 36) | const FREE_PLAN = "free";
  constant FREE_TRIAL_PLAN (line 37) | const FREE_TRIAL_PLAN = "free+drtrial";
  constant BYTES_PER_MEGABYTE (line 38) | const BYTES_PER_MEGABYTE = 1024 * 1024;
  type TusErrorResponse (line 39) | type TusErrorResponse = { status_code: number; body: string };
  type TusAuthenticatedRequest (line 41) | type TusAuthenticatedRequest = NextApiRequest & {
  method namingFunction (line 52) | namingFunction(req, metadata) {
  method generateUrl (line 62) | generateUrl(req, { proto, host, path, id }) {
  method getFileIdFromRequest (line 67) | getFileIdFromRequest(req) {
  method onResponseError (line 72) | onResponseError(req, res, err) {
  method onIncomingRequest (line 93) | async onIncomingRequest(req, res, uploadId) {
  method onUploadCreate (line 130) | async onUploadCreate(req, res, upload) {
  method onUploadFinish (line 226) | async onUploadFinish(req, res, upload) {
  function handler (line 266) | async function handler(

FILE: pages/api/health.ts
  function handler (line 5) | async function handler(

FILE: pages/api/internal/billing/automatic-unpause.ts
  function handle (line 10) | async function handle(

FILE: pages/api/jobs/get-thumbnail.ts
  function handle (line 11) | async function handle(

FILE: pages/api/jobs/process-download-batch.ts
  function handler (line 14) | async function handler(

FILE: pages/api/jobs/send-dataroom-new-document-notification.ts
  function handle (line 12) | async function handle(

FILE: pages/api/jobs/send-dataroom-upload-notification.ts
  function handle (line 10) | async function handle(

FILE: pages/api/jobs/send-notification.ts
  function handle (line 16) | async function handle(

FILE: pages/api/links/[id]/annotations.ts
  function handle (line 10) | async function handle(

FILE: pages/api/links/[id]/archive.ts
  function handle (line 10) | async function handle(

FILE: pages/api/links/[id]/documents/[documentId].ts
  function handle (line 10) | async function handle(

FILE: pages/api/links/[id]/documents/[documentId]/annotations.ts
  function handle (line 8) | async function handle(

FILE: pages/api/links/[id]/duplicate.ts
  function handle (line 19) | async function handle(

FILE: pages/api/links/[id]/index.ts
  function handle (line 22) | async function handle(

FILE: pages/api/links/[id]/preview.ts
  function handle (line 10) | async function handle(

FILE: pages/api/links/domains/[...domainSlug].ts
  function handle (line 14) | async function handle(

FILE: pages/api/links/download/[jobId].ts
  function handler (line 7) | async function handler(

FILE: pages/api/links/download/bulk.ts
  function handle (line 21) | async function handle(

FILE: pages/api/links/download/by-email.ts
  function handler (line 8) | async function handler(

FILE: pages/api/links/download/dataroom-document.ts
  function handle (line 16) | async function handle(

FILE: pages/api/links/download/dataroom-folder.ts
  function handler (line 22) | async function handler(

FILE: pages/api/links/download/file/[jobId]/[partIndex].ts
  function handler (line 8) | async function handler(

FILE: pages/api/links/download/index.ts
  function handle (line 16) | async function handle(

FILE: pages/api/links/download/jobs.ts
  function handler (line 7) | async function handler(

FILE: pages/api/links/download/verify.ts
  constant OTP_IDENTIFIER_PREFIX (line 18) | const OTP_IDENTIFIER_PREFIX = "download-otp:";
  function handler (line 20) | async function handler(

FILE: pages/api/links/generate-index.ts
  function handle (line 11) | async function handle(

FILE: pages/api/links/index.ts
  type DomainObject (line 24) | interface DomainObject {
  function handler (line 29) | async function handler(

FILE: pages/api/mupdf/annotate-document.ts
  function validateUrl (line 22) | function validateUrl(urlString: string): URL {
  type WatermarkConfig (line 62) | interface WatermarkConfig {
  type ViewerData (line 81) | interface ViewerData {
  function getPositionCoordinates (line 89) | function getPositionCoordinates(
  function insertWatermark (line 110) | async function insertWatermark(

FILE: pages/api/notification-preferences/dataroom.ts
  function handle (line 14) | async function handle(

FILE: pages/api/passkeys/register.ts
  function handler (line 13) | async function handler(

FILE: pages/api/progress-token.ts
  function handle (line 5) | async function handle(

FILE: pages/api/record_click.ts
  function handler (line 22) | async function handler(

FILE: pages/api/record_reaction.ts
  function handle (line 5) | async function handle(

FILE: pages/api/record_video_view.ts
  function handler (line 52) | async function handler(

FILE: pages/api/record_view.ts
  function handle (line 43) | async function handle(

FILE: pages/api/report.ts
  function handler (line 21) | async function handler(

FILE: pages/api/revalidate.ts
  function handler (line 5) | async function handler(

FILE: pages/api/stripe/webhook-old.ts
  function buffer (line 20) | async function buffer(readable: Readable) {
  function webhookHandler (line 34) | async function webhookHandler(

FILE: pages/api/stripe/webhook.ts
  function buffer (line 23) | async function buffer(readable: Readable) {
  function webhookHandler (line 39) | async function webhookHandler(

FILE: pages/api/teams/[teamId]/agreements/[agreementId]/download.ts
  function handle (line 11) | async function handle(

FILE: pages/api/teams/[teamId]/agreements/[agreementId]/index.ts
  function handle (line 10) | async function handle(

FILE: pages/api/teams/[teamId]/agreements/index.ts
  function handle (line 28) | async function handle(

FILE: pages/api/teams/[teamId]/ai-settings.ts
  function handle (line 16) | async function handle(

FILE: pages/api/teams/[teamId]/billing/cancel.ts
  function handle (line 10) | async function handle(

FILE: pages/api/teams/[teamId]/billing/cancellation-feedback.ts
  function handle (line 10) | async function handle(

FILE: pages/api/teams/[teamId]/billing/index.ts
  function handle (line 11) | async function handle(

FILE: pages/api/teams/[teamId]/billing/invoices.ts
  function isOldAccount (line 10) | function isOldAccount(plan: string) {
  function handle (line 14) | async function handle(

FILE: pages/api/teams/[teamId]/billing/manage.ts
  function handle (line 23) | async function handle(

FILE: pages/api/teams/[teamId]/billing/pause.ts
  function handle (line 10) | async function handle(

FILE: pages/api/teams/[teamId]/billing/plan.ts
  function handle (line 16) | async function handle(

FILE: pages/api/teams/[teamId]/billing/reactivate.ts
  function handle (line 10) | async function handle(

FILE: pages/api/teams/[teamId]/billing/retention-offer.ts
  function handle (line 10) | async function handle(

FILE: pages/api/teams/[teamId]/billing/unpause.ts
  function handle (line 10) | async function handle(

FILE: pages/api/teams/[teamId]/billing/upgrade.ts
  function handle (line 23) | async function handle(

FILE: pages/api/teams/[teamId]/branding.ts
  function handle (line 13) | async function handle(

FILE: pages/api/teams/[teamId]/change-role.ts
  function handle (line 11) | async function handle(

FILE: pages/api/teams/[teamId]/datarooms/[id]/apply-permissions.ts
  function handler (line 11) | async function handler(
  function applyPermissionStrategy (line 119) | async function applyPermissionStrategy(
  function applyRootLevelPermissions (line 148) | async function applyRootLevelPermissions(
  function inheritFromParentFolder (line 217) | async function inheritFromParentFolder(

FILE: pages/api/teams/[teamId]/datarooms/[id]/branding.ts
  function handle (line 11) | async function handle(

FILE: pages/api/teams/[teamId]/datarooms/[id]/calculate-indexes.ts
  function handler (line 15) | async function handler(

FILE: pages/api/teams/[teamId]/datarooms/[id]/conversations/[[...conversations]].ts
  function handler (line 5) | async function handler(

FILE: pages/api/teams/[teamId]/datarooms/[id]/conversations/toggle-conversations.ts
  function handler (line 5) | async function handler(

FILE: pages/api/teams/[teamId]/datarooms/[id]/documents/[documentId]/index.ts
  function handle (line 9) | async function handle(

FILE: pages/api/teams/[teamId]/datarooms/[id]/documents/[documentId]/stats.ts
  function handle (line 15) | async function handle(

FILE: pages/api/teams/[teamId]/datarooms/[id]/documents/index.ts
  function handle (line 27) | async function handle(

FILE: pages/api/teams/[teamId]/datarooms/[id]/documents/move.ts
  function handle (line 9) | async function handle(

FILE: pages/api/teams/[teamId]/datarooms/[id]/download/[jobId].ts
  function handler (line 12) | async function handler(

FILE: pages/api/teams/[teamId]/datarooms/[id]/download/bulk.ts
  function handler (line 20) | async function handler(

FILE: pages/api/teams/[teamId]/datarooms/[id]/download/jobs.ts
  function handler (line 14) | async function handler(

FILE: pages/api/teams/[teamId]/datarooms/[id]/duplicate.ts
  type DataroomWithContents (line 18) | interface DataroomWithContents extends Dataroom {
  type DataroomFolderWithContents (line 24) | interface DataroomFolderWithContents extends DataroomFolder {
  function fetchDataroomContents (line 30) | async function fetchDataroomContents(
  function duplicateFolders (line 95) | async function duplicateFolders(
  function handle (line 131) | async function handle(

FILE: pages/api/teams/[teamId]/datarooms/[id]/export-visits.ts
  function handler (line 11) | async function handler(

FILE: pages/api/teams/[teamId]/datarooms/[id]/faqs/[faqId].ts
  function handler (line 5) | async function handler(

FILE: pages/api/teams/[teamId]/datarooms/[id]/faqs/index.ts
  function handler (line 5) | async function handler(

FILE: pages/api/teams/[teamId]/datarooms/[id]/folders/[...name].ts
  function handle (line 10) | async function handle(

FILE: pages/api/teams/[teamId]/datarooms/[id]/folders/documents/[...name].ts
  function handle (line 11) | async function handle(

FILE: pages/api/teams/[teamId]/datarooms/[id]/folders/index.ts
  function applyFolderPermissions (line 11) | async function applyFolderPermissions(
  function applyDefaultFolderPermissions (line 24) | async function applyDefaultFolderPermissions(
  function inheritFolderPermissionsFromParent (line 119) | async function inheritFolderPermissionsFromParent(
  function handle (line 213) | async function handle(

FILE: pages/api/teams/[teamId]/datarooms/[id]/folders/manage/[folderId]/dataroom-to-dataroom.ts
  type FolderWithContents (line 11) | interface FolderWithContents {
  function fetchFolderContents (line 18) | async function fetchFolderContents(
  function createDataroomStructure (line 47) | async function createDataroomStructure(
  function handle (line 82) | async function handle(

FILE: pages/api/teams/[teamId]/datarooms/[id]/folders/manage/[folderId]/index.ts
  function handle (line 10) | async function handle(
  function deleteFolderAndContents (line 97) | async function deleteFolderAndContents(folderId: string) {

FILE: pages/api/teams/[teamId]/datarooms/[id]/folders/manage/index.ts
  function handle (line 15) | async function handle(

FILE: pages/api/teams/[teamId]/datarooms/[id]/folders/move.ts
  function handle (line 10) | async function handle(

FILE: pages/api/teams/[teamId]/datarooms/[id]/folders/parents/[...name].ts
  function handle (line 10) | async function handle(

FILE: pages/api/teams/[teamId]/datarooms/[id]/generate-index.ts
  function handle (line 13) | async function handle(

FILE: pages/api/teams/[teamId]/datarooms/[id]/groups/[groupId]/export-visits.ts
  function handler (line 11) | async function handler(

FILE: pages/api/teams/[teamId]/datarooms/[id]/groups/[groupId]/index.ts
  function handle (line 11) | async function handle(

FILE: pages/api/teams/[teamId]/datarooms/[id]/groups/[groupId]/invite.ts
  function handler (line 9) | async function handler(

FILE: pages/api/teams/[teamId]/datarooms/[id]/groups/[groupId]/links.ts
  function handle (line 11) | async function handle(

FILE: pages/api/teams/[teamId]/datarooms/[id]/groups/[groupId]/members/[memberId].ts
  function handle (line 10) | async function handle(

FILE: pages/api/teams/[teamId]/datarooms/[id]/groups/[groupId]/members/index.ts
  function handle (line 9) | async function handle(

FILE: pages/api/teams/[teamId]/datarooms/[id]/groups/[groupId]/permissions.ts
  function getParentFolderIds (line 11) | async function getParentFolderIds(
  function ensureParentFoldersVisible (line 45) | async function ensureParentFoldersVisible(
  function handler (line 124) | async function handler(

FILE: pages/api/teams/[teamId]/datarooms/[id]/groups/[groupId]/uninvited.ts
  function handler (line 5) | async function handler(

FILE: pages/api/teams/[teamId]/datarooms/[id]/groups/[groupId]/views/index.ts
  function handle (line 11) | async function handle(

FILE: pages/api/teams/[teamId]/datarooms/[id]/groups/index.ts
  function handle (line 13) | async function handle(

FILE: pages/api/teams/[teamId]/datarooms/[id]/index.ts
  function handle (line 12) | async function handle(

FILE: pages/api/teams/[teamId]/datarooms/[id]/links.ts
  function handle (line 12) | async function handle(

FILE: pages/api/teams/[teamId]/datarooms/[id]/links/[linkId]/invite.ts
  function handler (line 9) | async function handler(

FILE: pages/api/teams/[teamId]/datarooms/[id]/permission-groups/[permissionGroupId].ts
  function getParentFolderIds (line 27) | async function getParentFolderIds(
  function ensureParentFoldersVisible (line 61) | async function ensureParentFoldersVisible(
  function handle (line 141) | async function handle(

FILE: pages/api/teams/[teamId]/datarooms/[id]/permission-groups/index.ts
  function handle (line 12) | async function handle(

FILE: pages/api/teams/[teamId]/datarooms/[id]/reorder.ts
  type OrderItem (line 8) | type OrderItem = {
  function handler (line 14) | async function handler(

FILE: pages/api/teams/[teamId]/datarooms/[id]/stats.ts
  function handle (line 17) | async function handle(

FILE: pages/api/teams/[teamId]/datarooms/[id]/users/index.ts
  function handle (line 11) | async function handle(

FILE: pages/api/teams/[teamId]/datarooms/[id]/viewers/index.ts
  function handle (line 11) | async function handle(

FILE: pages/api/teams/[teamId]/datarooms/[id]/views-count.ts
  function handler (line 9) | async function handler(

FILE: pages/api/teams/[teamId]/datarooms/[id]/views/[viewId]/custom-fields.ts
  function handle (line 10) | async function handle(

FILE: pages/api/teams/[teamId]/datarooms/[id]/views/[viewId]/document-stats.ts
  function handle (line 11) | async function handle(

FILE: pages/api/teams/[teamId]/datarooms/[id]/views/[viewId]/history.ts
  function handle (line 10) | async function handle(

FILE: pages/api/teams/[teamId]/datarooms/[id]/views/[viewId]/user-agent.ts
  function handle (line 11) | async function handle(

FILE: pages/api/teams/[teamId]/datarooms/[id]/views/index.ts
  function handle (line 11) | async function handle(

FILE: pages/api/teams/[teamId]/datarooms/create-from-folder.ts
  type FolderWithContents (line 13) | interface FolderWithContents extends Folder {
  function fetchFolderContents (line 19) | async function fetchFolderContents(
  function createDataroomFolders (line 68) | async function createDataroomFolders(
  function handle (line 114) | async function handle(

FILE: pages/api/teams/[teamId]/datarooms/generate-ai-structure.ts
  function handle (line 43) | async function handle(

FILE: pages/api/teams/[teamId]/datarooms/generate-ai.ts
  function handle (line 14) | async function handle(

FILE: pages/api/teams/[teamId]/datarooms/index.ts
  function handle (line 17) | async function handle(

FILE: pages/api/teams/[teamId]/datarooms/trial.ts
  function handle (line 23) | async function handle(

FILE: pages/api/teams/[teamId]/documents/[id]/add-to-dataroom.ts
  function handle (line 22) | async function handle(

FILE: pages/api/teams/[teamId]/docum
Condensed preview — 1468 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (6,896K chars).
[
  {
    "path": ".agents/skills/frontend-design/LICENSE.txt",
    "chars": 10174,
    "preview": "\n                                 Apache License\n                           Version 2.0, January 2004\n                  "
  },
  {
    "path": ".agents/skills/frontend-design/SKILL.md",
    "chars": 4440,
    "preview": "---\nname: frontend-design\ndescription: Create distinctive, production-grade frontend interfaces with high design quality"
  },
  {
    "path": ".agents/skills/gh-cli/SKILL.md",
    "chars": 39016,
    "preview": "---\nname: gh-cli\ndescription: GitHub CLI (gh) comprehensive reference for repositories, issues, pull requests, Actions, "
  },
  {
    "path": ".agents/skills/postgres/SKILL.md",
    "chars": 6493,
    "preview": "---\nname: postgres\ndescription: PostgreSQL best practices, query optimization, connection troubleshooting, and performan"
  },
  {
    "path": ".agents/skills/postgres/references/backup-recovery.md",
    "chars": 2676,
    "preview": "---\ntitle: Backup and Recovery\ndescription: Logical/physical backups, PITR, WAL archiving, backup tools, and recovery st"
  },
  {
    "path": ".agents/skills/postgres/references/index-optimization.md",
    "chars": 2275,
    "preview": "---\ntitle: Index Optimization Queries\ndescription: Index audit queries\ntags: postgres, indexes, unused-indexes, duplicat"
  },
  {
    "path": ".agents/skills/postgres/references/indexing.md",
    "chars": 2067,
    "preview": "---\ntitle: Indexing Best Practices\ndescription: Index design guide\ntags: postgres, indexes, composite, partial, covering"
  },
  {
    "path": ".agents/skills/postgres/references/memory-management-ops.md",
    "chars": 2758,
    "preview": "---\ntitle: Memory Architecture and OOM Prevention\ndescription: PostgreSQL shared/private memory layout, OS page cache in"
  },
  {
    "path": ".agents/skills/postgres/references/monitoring.md",
    "chars": 3506,
    "preview": "---\ntitle: Monitoring\ndescription: Essential PostgreSQL monitoring views, pg_stat_statements, logging, host metrics, and"
  },
  {
    "path": ".agents/skills/postgres/references/mvcc-transactions.md",
    "chars": 2290,
    "preview": "---\ntitle: MVCC Transactions and Concurrency\ndescription: Transaction isolation levels, XID wraparound prevention, seria"
  },
  {
    "path": ".agents/skills/postgres/references/mvcc-vacuum.md",
    "chars": 2567,
    "preview": "---\ntitle: MVCC and VACUUM\ndescription: MVCC internals, VACUUM/autovacuum tuning, and bloat prevention\ntags: postgres, m"
  },
  {
    "path": ".agents/skills/postgres/references/optimization-checklist.md",
    "chars": 787,
    "preview": "---\ntitle: Database Optimization Checklist\ndescription: Optimize checklist\ntags: postgres, optimization, indexes, partit"
  },
  {
    "path": ".agents/skills/postgres/references/partitioning.md",
    "chars": 3012,
    "preview": "---\ntitle: Table Partitioning Guide\ndescription: Partition guide\ntags: postgres, partitioning, range, list, pg_partman, "
  },
  {
    "path": ".agents/skills/postgres/references/process-architecture.md",
    "chars": 2708,
    "preview": "---\ntitle: Process Architecture\ndescription: PostgreSQL multi-process model, connection management, and auxiliary proces"
  },
  {
    "path": ".agents/skills/postgres/references/ps-cli-api-insights.md",
    "chars": 2066,
    "preview": "---\ntitle: CLI Query Insights API\ndescription: CLI insights usage\ntags: postgres, planetscale, cli, insights, query-patt"
  },
  {
    "path": ".agents/skills/postgres/references/ps-cli-commands.md",
    "chars": 1860,
    "preview": "---\ntitle: PlanetScale CLI Reference\ndescription: CLI command guide\ntags: planetscale, cli, branches, deploy-requests, a"
  },
  {
    "path": ".agents/skills/postgres/references/ps-connection-pooling.md",
    "chars": 3188,
    "preview": "---\ntitle: PgBouncer Connection Pooling\ndescription: Pooling setup guide\ntags: postgres, pgbouncer, connection-pooling, "
  },
  {
    "path": ".agents/skills/postgres/references/ps-connections.md",
    "chars": 1415,
    "preview": "---\ntitle: PlanetScale Postgres Connections\ndescription: Connection guide for PlanetScale Postgres\ntags: planetscale, po"
  },
  {
    "path": ".agents/skills/postgres/references/ps-extensions.md",
    "chars": 1074,
    "preview": "---\ntitle: PlanetScale PostgreSQL Extensions\ndescription: Extension reference\ntags: postgres, extensions\n---\n\n# PostgreS"
  },
  {
    "path": ".agents/skills/postgres/references/ps-insights.md",
    "chars": 3400,
    "preview": "---\ntitle: PlanetScale Query Insights\ndescription: Query insights guide\ntags: postgres, planetscale, insights, monitorin"
  },
  {
    "path": ".agents/skills/postgres/references/query-patterns.md",
    "chars": 2486,
    "preview": "---\ntitle: SQL Query Patterns\ndescription: Common SQL anti-patterns and optimized alternatives\ntags: postgres, sql, quer"
  },
  {
    "path": ".agents/skills/postgres/references/replication.md",
    "chars": 3819,
    "preview": "---\ntitle: Replication\ndescription: Streaming replication, replication slots, synchronous commit levels, failover, and s"
  },
  {
    "path": ".agents/skills/postgres/references/schema-design.md",
    "chars": 1948,
    "preview": "---\ntitle: PostgreSQL Schema Design\ndescription: Schema design guide\ntags: postgres, schema, primary-keys, data-types, f"
  },
  {
    "path": ".agents/skills/postgres/references/storage-layout.md",
    "chars": 2474,
    "preview": "---\ntitle: Storage Layout and Tablespaces\ndescription: PGDATA directory structure, TOAST, fillfactor, tablespaces, and d"
  },
  {
    "path": ".agents/skills/postgres/references/wal-operations.md",
    "chars": 2812,
    "preview": "---\ntitle: WAL and Checkpoint Operations\ndescription: Write-ahead log internals, checkpoint tuning, durability guarantee"
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/AGENTS.md",
    "chars": 81669,
    "preview": "# React Best Practices\n\n**Version 1.0.0**  \nVercel Engineering  \nJanuary 2026\n\n> **Note:**  \n> This document is mainly f"
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/SKILL.md",
    "chars": 6165,
    "preview": "---\nname: vercel-react-best-practices\ndescription: React and Next.js performance optimization guidelines from Vercel Eng"
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/advanced-event-handler-refs.md",
    "chars": 1483,
    "preview": "---\ntitle: Store Event Handlers in Refs\nimpact: LOW\nimpactDescription: stable subscriptions\ntags: advanced, hooks, refs,"
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/advanced-init-once.md",
    "chars": 958,
    "preview": "---\ntitle: Initialize App Once, Not Per Mount\nimpact: LOW-MEDIUM\nimpactDescription: avoids duplicate init in development"
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/advanced-use-latest.md",
    "chars": 1072,
    "preview": "---\ntitle: useEffectEvent for Stable Callback Refs\nimpact: LOW\nimpactDescription: prevents effect re-runs\ntags: advanced"
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/async-api-routes.md",
    "chars": 1124,
    "preview": "---\ntitle: Prevent Waterfall Chains in API Routes\nimpact: CRITICAL\nimpactDescription: 2-10× improvement\ntags: api-routes"
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/async-defer-await.md",
    "chars": 2028,
    "preview": "---\ntitle: Defer Await Until Needed\nimpact: HIGH\nimpactDescription: avoids blocking unused code paths\ntags: async, await"
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/async-dependencies.md",
    "chars": 1292,
    "preview": "---\ntitle: Dependency-Based Parallelization\nimpact: CRITICAL\nimpactDescription: 2-10× improvement\ntags: async, paralleli"
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/async-parallel.md",
    "chars": 653,
    "preview": "---\ntitle: Promise.all() for Independent Operations\nimpact: CRITICAL\nimpactDescription: 2-10× improvement\ntags: async, p"
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/async-suspense-boundaries.md",
    "chars": 2508,
    "preview": "---\ntitle: Strategic Suspense Boundaries\nimpact: HIGH\nimpactDescription: faster initial paint\ntags: async, suspense, str"
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/bundle-barrel-imports.md",
    "chars": 2370,
    "preview": "---\ntitle: Avoid Barrel File Imports\nimpact: CRITICAL\nimpactDescription: 200-800ms import cost, slow builds\ntags: bundle"
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/bundle-conditional.md",
    "chars": 949,
    "preview": "---\ntitle: Conditional Module Loading\nimpact: HIGH\nimpactDescription: loads large data only when needed\ntags: bundle, co"
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/bundle-defer-third-party.md",
    "chars": 920,
    "preview": "---\ntitle: Defer Non-Critical Third-Party Libraries\nimpact: MEDIUM\nimpactDescription: loads after hydration\ntags: bundle"
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/bundle-dynamic-imports.md",
    "chars": 791,
    "preview": "---\ntitle: Dynamic Imports for Heavy Components\nimpact: CRITICAL\nimpactDescription: directly affects TTI and LCP\ntags: b"
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/bundle-preload.md",
    "chars": 1149,
    "preview": "---\ntitle: Preload Based on User Intent\nimpact: MEDIUM\nimpactDescription: reduces perceived latency\ntags: bundle, preloa"
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/client-event-listeners.md",
    "chars": 1969,
    "preview": "---\ntitle: Deduplicate Global Event Listeners\nimpact: LOW\nimpactDescription: single listener for N components\ntags: clie"
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/client-localstorage-schema.md",
    "chars": 1950,
    "preview": "---\ntitle: Version and Minimize localStorage Data\nimpact: MEDIUM\nimpactDescription: prevents schema conflicts, reduces s"
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/client-passive-event-listeners.md",
    "chars": 1644,
    "preview": "---\ntitle: Use Passive Event Listeners for Scrolling Performance\nimpact: MEDIUM\nimpactDescription: eliminates scroll del"
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/client-swr-dedup.md",
    "chars": 1159,
    "preview": "---\ntitle: Use SWR for Automatic Deduplication\nimpact: MEDIUM-HIGH\nimpactDescription: automatic deduplication\ntags: clie"
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/js-batch-dom-css.md",
    "chars": 3266,
    "preview": "---\ntitle: Avoid Layout Thrashing\nimpact: MEDIUM\nimpactDescription: prevents forced synchronous layouts and reduces perf"
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/js-cache-function-results.md",
    "chars": 1949,
    "preview": "---\ntitle: Cache Repeated Function Calls\nimpact: MEDIUM\nimpactDescription: avoid redundant computation\ntags: javascript,"
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/js-cache-property-access.md",
    "chars": 531,
    "preview": "---\ntitle: Cache Property Access in Loops\nimpact: LOW-MEDIUM\nimpactDescription: reduces lookups\ntags: javascript, loops,"
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/js-cache-storage.md",
    "chars": 1651,
    "preview": "---\ntitle: Cache Storage API Calls\nimpact: LOW-MEDIUM\nimpactDescription: reduces expensive I/O\ntags: javascript, localSt"
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/js-combine-iterations.md",
    "chars": 753,
    "preview": "---\ntitle: Combine Multiple Array Iterations\nimpact: LOW-MEDIUM\nimpactDescription: reduces iterations\ntags: javascript, "
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/js-early-exit.md",
    "chars": 1133,
    "preview": "---\ntitle: Early Return from Functions\nimpact: LOW-MEDIUM\nimpactDescription: avoids unnecessary computation\ntags: javasc"
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/js-hoist-regexp.md",
    "chars": 1028,
    "preview": "---\ntitle: Hoist RegExp Creation\nimpact: LOW-MEDIUM\nimpactDescription: avoids recreation\ntags: javascript, regexp, optim"
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/js-index-maps.md",
    "chars": 834,
    "preview": "---\ntitle: Build Index Maps for Repeated Lookups\nimpact: LOW-MEDIUM\nimpactDescription: 1M ops to 2K ops\ntags: javascript"
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/js-length-check-first.md",
    "chars": 1747,
    "preview": "---\ntitle: Early Length Check for Array Comparisons\nimpact: MEDIUM-HIGH\nimpactDescription: avoids expensive operations w"
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/js-min-max-loop.md",
    "chars": 2290,
    "preview": "---\ntitle: Use Loop for Min/Max Instead of Sort\nimpact: LOW\nimpactDescription: O(n) instead of O(n log n)\ntags: javascri"
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/js-set-map-lookups.md",
    "chars": 532,
    "preview": "---\ntitle: Use Set/Map for O(1) Lookups\nimpact: LOW-MEDIUM\nimpactDescription: O(n) to O(1)\ntags: javascript, set, map, d"
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/js-tosorted-immutable.md",
    "chars": 1782,
    "preview": "---\ntitle: Use toSorted() Instead of sort() for Immutability\nimpact: MEDIUM-HIGH\nimpactDescription: prevents mutation bu"
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/rendering-activity.md",
    "chars": 564,
    "preview": "---\ntitle: Use Activity Component for Show/Hide\nimpact: MEDIUM\nimpactDescription: preserves state/DOM\ntags: rendering, a"
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md",
    "chars": 1185,
    "preview": "---\ntitle: Animate SVG Wrapper Instead of SVG Element\nimpact: LOW\nimpactDescription: enables hardware acceleration\ntags:"
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/rendering-conditional-render.md",
    "chars": 980,
    "preview": "---\ntitle: Use Explicit Conditional Rendering\nimpact: LOW\nimpactDescription: prevents rendering 0 or NaN\ntags: rendering"
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/rendering-content-visibility.md",
    "chars": 814,
    "preview": "---\ntitle: CSS content-visibility for Long Lists\nimpact: HIGH\nimpactDescription: faster initial render\ntags: rendering, "
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/rendering-hoist-jsx.md",
    "chars": 1039,
    "preview": "---\ntitle: Hoist Static JSX Elements\nimpact: LOW\nimpactDescription: avoids re-creation\ntags: rendering, jsx, static, opt"
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md",
    "chars": 2308,
    "preview": "---\ntitle: Prevent Hydration Mismatch Without Flickering\nimpact: MEDIUM\nimpactDescription: avoids visual flicker and hyd"
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/rendering-hydration-suppress-warning.md",
    "chars": 870,
    "preview": "---\ntitle: Suppress Expected Hydration Mismatches\nimpact: LOW-MEDIUM\nimpactDescription: avoids noisy hydration warnings "
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/rendering-svg-precision.md",
    "chars": 588,
    "preview": "---\ntitle: Optimize SVG Precision\nimpact: LOW\nimpactDescription: reduces file size\ntags: rendering, svg, optimization, s"
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/rendering-usetransition-loading.md",
    "chars": 2074,
    "preview": "---\ntitle: Use useTransition Over Manual Loading States\nimpact: LOW\nimpactDescription: reduces re-renders and improves c"
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/rerender-defer-reads.md",
    "chars": 973,
    "preview": "---\ntitle: Defer State Reads to Usage Point\nimpact: MEDIUM\nimpactDescription: avoids unnecessary subscriptions\ntags: rer"
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/rerender-dependencies.md",
    "chars": 824,
    "preview": "---\ntitle: Narrow Effect Dependencies\nimpact: LOW\nimpactDescription: minimizes effect re-runs\ntags: rerender, useEffect,"
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/rerender-derived-state-no-effect.md",
    "chars": 1201,
    "preview": "---\ntitle: Calculate Derived State During Rendering\nimpact: MEDIUM\nimpactDescription: avoids redundant renders and state"
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/rerender-derived-state.md",
    "chars": 728,
    "preview": "---\ntitle: Subscribe to Derived State\nimpact: MEDIUM\nimpactDescription: reduces re-render frequency\ntags: rerender, deri"
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/rerender-functional-setstate.md",
    "chars": 2958,
    "preview": "---\ntitle: Use Functional setState Updates\nimpact: MEDIUM\nimpactDescription: prevents stale closures and unnecessary cal"
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/rerender-lazy-state-init.md",
    "chars": 2016,
    "preview": "---\ntitle: Use Lazy State Initialization\nimpact: MEDIUM\nimpactDescription: wasted computation on every render\ntags: reac"
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/rerender-memo-with-default-value.md",
    "chars": 1173,
    "preview": "---\n\ntitle: Extract Default Non-primitive Parameter Value from Memoized Component to Constant\nimpact: MEDIUM\nimpactDescr"
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/rerender-memo.md",
    "chars": 1148,
    "preview": "---\ntitle: Extract to Memoized Components\nimpact: MEDIUM\nimpactDescription: enables early returns\ntags: rerender, memo, "
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/rerender-move-effect-to-event.md",
    "chars": 1268,
    "preview": "---\ntitle: Put Interaction Logic in Event Handlers\nimpact: MEDIUM\nimpactDescription: avoids effect re-runs and duplicate"
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/rerender-simple-expression-in-memo.md",
    "chars": 1018,
    "preview": "---\ntitle: Do not wrap a simple expression with a primitive result type in useMemo\nimpact: LOW-MEDIUM\nimpactDescription:"
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/rerender-transitions.md",
    "chars": 1055,
    "preview": "---\ntitle: Use Transitions for Non-Urgent Updates\nimpact: MEDIUM\nimpactDescription: maintains UI responsiveness\ntags: re"
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/rerender-use-ref-transient-values.md",
    "chars": 1742,
    "preview": "---\ntitle: Use useRef for Transient Values\nimpact: MEDIUM\nimpactDescription: avoids unnecessary re-renders on frequent u"
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/server-after-nonblocking.md",
    "chars": 2012,
    "preview": "---\ntitle: Use after() for Non-Blocking Operations\nimpact: MEDIUM\nimpactDescription: faster response times\ntags: server,"
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/server-auth-actions.md",
    "chars": 2647,
    "preview": "---\ntitle: Authenticate Server Actions Like API Routes\nimpact: CRITICAL\nimpactDescription: prevents unauthorized access "
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/server-cache-lru.md",
    "chars": 1353,
    "preview": "---\ntitle: Cross-Request LRU Caching\nimpact: HIGH\nimpactDescription: caches across requests\ntags: server, cache, lru, cr"
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/server-cache-react.md",
    "chars": 2228,
    "preview": "---\ntitle: Per-Request Deduplication with React.cache()\nimpact: MEDIUM\nimpactDescription: deduplicates within request\nta"
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/server-dedup-props.md",
    "chars": 2053,
    "preview": "---\ntitle: Avoid Duplicate Serialization in RSC Props\nimpact: LOW\nimpactDescription: reduces network payload by avoiding"
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/server-parallel-fetching.md",
    "chars": 1554,
    "preview": "---\ntitle: Parallel Data Fetching with Component Composition\nimpact: CRITICAL\nimpactDescription: eliminates server-side "
  },
  {
    "path": ".agents/skills/vercel-react-best-practices/rules/server-serialization.md",
    "chars": 996,
    "preview": "---\ntitle: Minimize Serialization at RSC Boundaries\nimpact: HIGH\nimpactDescription: reduces data transfer size\ntags: ser"
  },
  {
    "path": ".agents/skills/web-design-guidelines/SKILL.md",
    "chars": 1231,
    "preview": "---\nname: web-design-guidelines\ndescription: Review UI code for Web Interface Guidelines compliance. Use when asked to \""
  },
  {
    "path": ".cursor/rules/rule-claude-opus.mdc",
    "chars": 1974,
    "preview": "---\ndescription: Base Guidelines for Claude Opus 4.6 + Cursor Agent\nglobs: *,**/*\nalwaysApply: true\n---\n\n# Instructions\n"
  },
  {
    "path": ".cursor/rules/rule-trigger-typescript.mdc",
    "chars": 31855,
    "preview": "---\nglobs: **/trigger/**/*.ts, **/trigger/**/*.tsx,**/trigger/**/*.js,**/trigger/**/*.jsx\ndescription: Guidelines for wr"
  },
  {
    "path": ".cursorignore",
    "chars": 4,
    "preview": ".env"
  },
  {
    "path": ".eslintrc.json",
    "chars": 97,
    "preview": "{\n  \"extends\": \"next/core-web-vitals\",\n  \"rules\": {\n    \"@next/next/no-img-element\": \"off\"\n  }\n}\n"
  },
  {
    "path": ".github/CODEOWNERS",
    "chars": 279,
    "preview": "# Learn how to add code owners here:\n# https://help.github.com/en/articles/about-code-owners\n\n# These owners will be the"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/oss-gg-hack-template.yml",
    "chars": 985,
    "preview": "name: oss.gg hack submission 🕹️\ndescription: \"Submit your contribution for the for the oss.gg hackathon\"\ntitle: \"[🕹️]\"\nl"
  },
  {
    "path": ".github/workflows/cla.yml",
    "chars": 2557,
    "preview": "name: \"CLA Assistant\"\non:\n  issue_comment:\n    types: [created]\n  pull_request_target:\n    types: [opened,closed,synchro"
  },
  {
    "path": ".gitignore",
    "chars": 687,
    "preview": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pn"
  },
  {
    "path": ".prettierignore",
    "chars": 71,
    "preview": "node_modules/\n.next/\n.react-email/\n.vercel/\n.github/\n*.min.js\n*.min.css"
  },
  {
    "path": "CLA.md",
    "chars": 3017,
    "preview": "# Papermark Contributors License Agreement\n\nThis Contributors License Agreement (\"CLA\") is entered into between the Cont"
  },
  {
    "path": "LICENSE",
    "chars": 34906,
    "preview": "Copyright (c) 2023-present Papermark, Inc.\n\nPortions of this software are licensed as follows:\n\n- All content that resid"
  },
  {
    "path": "Pipfile",
    "chars": 158,
    "preview": "[[source]]\nurl = \"https://pypi.org/simple\"\nverify_ssl = true\nname = \"pypi\"\n\n[packages]\ntinybird-cli = \"*\"\n\n[dev-packages"
  },
  {
    "path": "README.md",
    "chars": 4220,
    "preview": "<div align=\"center\">\n  <h1 align=\"center\">Papermark</h1>\n  <h3>The open-source DocSend alternative.</h3>\n\n<a target=\"_bl"
  },
  {
    "path": "SECURITY.md",
    "chars": 377,
    "preview": "# Security Policy\n\n## Supported Versions\n\nThe latest version of Papermark is currently being supported with security upd"
  },
  {
    "path": "app/(auth)/auth/confirm-email-change/[token]/page-client.tsx",
    "chars": 935,
    "preview": "\"use client\";\n\nimport { useRouter } from \"next/navigation\";\n\nimport { useEffect, useRef } from \"react\";\n\nimport { useSes"
  },
  {
    "path": "app/(auth)/auth/confirm-email-change/[token]/page.tsx",
    "chars": 3168,
    "preview": "import { Metadata } from \"next\";\nimport { redirect } from \"next/navigation\";\n\nimport NotFound from \"@/pages/404\";\nimport"
  },
  {
    "path": "app/(auth)/auth/confirm-email-change/[token]/utils.ts",
    "chars": 192,
    "preview": "import { authOptions } from \"@/pages/api/auth/[...nextauth]\";\nimport { getServerSession } from \"next-auth\";\n\nexport cons"
  },
  {
    "path": "app/(auth)/auth/email/[[...params]]/page-client.tsx",
    "chars": 13151,
    "preview": "\"use client\";\n\nimport Link from \"next/link\";\nimport { useRouter } from \"next/navigation\";\n\nimport { useEffect, useRef, u"
  },
  {
    "path": "app/(auth)/auth/email/[[...params]]/page.tsx",
    "chars": 923,
    "preview": "import { Metadata } from \"next\";\n\nimport EmailVerificationClient from \"./page-client\";\n\nconst data = {\n  description: \"V"
  },
  {
    "path": "app/(auth)/auth/saml/page-client.tsx",
    "chars": 2432,
    "preview": "\"use client\";\n\nimport { useRouter, useSearchParams } from \"next/navigation\";\n\nimport { useEffect, useState } from \"react"
  },
  {
    "path": "app/(auth)/auth/saml/page.tsx",
    "chars": 276,
    "preview": "import { Metadata } from \"next\";\n\nimport SAMLCallbackClient from \"./page-client\";\n\nexport const metadata: Metadata = {\n "
  },
  {
    "path": "app/(auth)/layout.tsx",
    "chars": 524,
    "preview": "\"use client\";\n\nimport { SessionProvider } from \"next-auth/react\";\nimport { Toaster } from \"sonner\";\n\nimport { ThemeProvi"
  },
  {
    "path": "app/(auth)/login/page-client.tsx",
    "chars": 13326,
    "preview": "\"use client\";\n\nimport Link from \"next/link\";\nimport { useParams, useRouter, useSearchParams } from \"next/navigation\";\n\ni"
  },
  {
    "path": "app/(auth)/login/page.tsx",
    "chars": 967,
    "preview": "import { Metadata } from \"next\";\n\nimport { GTMComponent } from \"@/components/gtm-component\";\n\nimport LoginClient from \"."
  },
  {
    "path": "app/(auth)/register/page-client.tsx",
    "chars": 4112,
    "preview": "\"use client\";\n\nimport Image from \"next/image\";\nimport Link from \"next/link\";\nimport { useParams } from \"next/navigation\""
  },
  {
    "path": "app/(auth)/register/page.tsx",
    "chars": 872,
    "preview": "import { Metadata } from \"next\";\n\nimport RegisterClient from \"./page-client\";\n\nconst data = {\n  description: \"Signup to "
  },
  {
    "path": "app/(auth)/verify/invitation/AcceptInvitationButton.tsx",
    "chars": 787,
    "preview": "\"use client\";\n\nimport Link from \"next/link\";\n\nimport { useState } from \"react\";\n\nimport { Button } from \"@/components/ui"
  },
  {
    "path": "app/(auth)/verify/invitation/InvitationStatusContent.tsx",
    "chars": 1524,
    "preview": "\"use client\";\n\nimport Link from \"next/link\";\n\nimport { Button } from \"@/components/ui/button\";\n\nexport default function "
  },
  {
    "path": "app/(auth)/verify/invitation/page.tsx",
    "chars": 7574,
    "preview": "import { Metadata } from \"next\";\nimport Link from \"next/link\";\n\nimport NotFound from \"@/pages/404\";\nimport { format } fr"
  },
  {
    "path": "app/(auth)/verify/invitation/status/ClientRedirect.tsx",
    "chars": 395,
    "preview": "\"use client\";\n\nimport { useEffect } from \"react\";\n\nexport default function CleanUrlOnExpire({\n  shouldClean,\n}: {\n  shou"
  },
  {
    "path": "app/(auth)/verify/page.tsx",
    "chars": 175,
    "preview": "import { redirect } from \"next/navigation\";\n\n// Legacy verify page - redirect to new auth email flow\nexport default asyn"
  },
  {
    "path": "app/(ee)/LICENSE.md",
    "chars": 17,
    "preview": "../ee/LICENSE.md\n"
  },
  {
    "path": "app/(ee)/README.md",
    "chars": 16,
    "preview": "../ee/README.md\n"
  },
  {
    "path": "app/(ee)/api/ai/chat/[chatId]/messages/route.ts",
    "chars": 5481,
    "preview": "import { NextRequest } from \"next/server\";\n\nimport { generateChatTitle } from \"@/ee/features/ai/lib/chat/generate-chat-t"
  },
  {
    "path": "app/(ee)/api/ai/chat/[chatId]/route.ts",
    "chars": 3655,
    "preview": "import { NextRequest, NextResponse } from \"next/server\";\n\nimport { validateChatAccess } from \"@/ee/features/ai/lib/permi"
  },
  {
    "path": "app/(ee)/api/ai/chat/route.ts",
    "chars": 15618,
    "preview": "import { NextRequest, NextResponse } from \"next/server\";\n\nimport { createChat } from \"@/ee/features/ai/lib/chat/create-c"
  },
  {
    "path": "app/(ee)/api/ai/store/runs/[runId]/route.ts",
    "chars": 1597,
    "preview": "import { NextRequest, NextResponse } from \"next/server\";\n\nimport { authOptions } from \"@/pages/api/auth/[...nextauth]\";\n"
  },
  {
    "path": "app/(ee)/api/ai/store/teams/[teamId]/datarooms/[dataroomId]/route.ts",
    "chars": 7251,
    "preview": "import { NextRequest, NextResponse } from \"next/server\";\n\nimport {\n  SUPPORTED_AI_CONTENT_TYPES,\n  addFileToVectorStoreT"
  },
  {
    "path": "app/(ee)/api/ai/store/teams/[teamId]/documents/[documentId]/route.ts",
    "chars": 8062,
    "preview": "import { NextRequest, NextResponse } from \"next/server\";\n\nimport {\n  addFileToVectorStoreTask,\n  processDocumentForAITas"
  },
  {
    "path": "app/(ee)/api/ai/store/teams/[teamId]/route.ts",
    "chars": 2301,
    "preview": "import { NextRequest, NextResponse } from \"next/server\";\n\nimport { getVectorStoreInfo } from \"@/ee/features/ai/lib/vecto"
  },
  {
    "path": "app/(ee)/api/auth/saml/authorize/route.ts",
    "chars": 2176,
    "preview": "import { jackson } from \"@/lib/jackson\";\nimport type { OAuthReq } from \"@boxyhq/saml-jackson\";\nimport { NextResponse } f"
  },
  {
    "path": "app/(ee)/api/auth/saml/callback/route.ts",
    "chars": 939,
    "preview": "import { jackson } from \"@/lib/jackson\";\nimport { NextResponse } from \"next/server\";\n\nexport const dynamic = \"force-dyna"
  },
  {
    "path": "app/(ee)/api/auth/saml/token/route.ts",
    "chars": 1120,
    "preview": "import { jackson } from \"@/lib/jackson\";\n// These imports fix crypto module bundling issues with Jackson in Next.js.\n// "
  },
  {
    "path": "app/(ee)/api/auth/saml/userinfo/route.ts",
    "chars": 1138,
    "preview": "import { jackson } from \"@/lib/jackson\";\n// Force-include crypto dependencies (same workaround as token route)\nimport * "
  },
  {
    "path": "app/(ee)/api/auth/saml/verify/route.ts",
    "chars": 1847,
    "preview": "import { jackson, jacksonProduct } from \"@/lib/jackson\";\nimport prisma from \"@/lib/prisma\";\nimport { NextResponse } from"
  },
  {
    "path": "app/(ee)/api/faqs/route.ts",
    "chars": 3643,
    "preview": "import { NextRequest, NextResponse } from \"next/server\";\n\nimport { z } from \"zod\";\n\nimport { verifyDataroomSession } fro"
  },
  {
    "path": "app/(ee)/api/links/[id]/upload/route.ts",
    "chars": 10144,
    "preview": "import { NextRequest, NextResponse } from \"next/server\";\n\nimport { processDocument } from \"@/lib/api/documents/process-d"
  },
  {
    "path": "app/(ee)/api/scim/v2.0/[...directory]/route.ts",
    "chars": 8907,
    "preview": "import { jackson } from \"@/lib/jackson\";\nimport prisma from \"@/lib/prisma\";\nimport type { DirectorySyncEvent } from \"@bo"
  },
  {
    "path": "app/(ee)/api/teams/[teamId]/directory-sync/route.ts",
    "chars": 5416,
    "preview": "import { jackson, jacksonProduct } from \"@/lib/jackson\";\nimport prisma from \"@/lib/prisma\";\nimport { CustomUser } from \""
  },
  {
    "path": "app/(ee)/api/teams/[teamId]/saml/route.ts",
    "chars": 12472,
    "preview": "import { jackson, jacksonProduct, samlAudience } from \"@/lib/jackson\";\nimport prisma from \"@/lib/prisma\";\nimport { Custo"
  },
  {
    "path": "app/(ee)/api/workflow-entry/domains/[...domainSlug]/route.ts",
    "chars": 11039,
    "preview": "import { cookies } from \"next/headers\";\nimport { NextRequest, NextResponse } from \"next/server\";\n\nimport { WorkflowEngin"
  },
  {
    "path": "app/(ee)/api/workflow-entry/link/[entryLinkId]/access/route.ts",
    "chars": 8476,
    "preview": "import { cookies } from \"next/headers\";\nimport { NextRequest, NextResponse } from \"next/server\";\n\nimport { WorkflowEngin"
  },
  {
    "path": "app/(ee)/api/workflow-entry/link/[entryLinkId]/verify/route.ts",
    "chars": 4043,
    "preview": "import { NextRequest, NextResponse } from \"next/server\";\nimport { ipAddress, waitUntil } from \"@vercel/functions\";\nimpor"
  },
  {
    "path": "app/(ee)/api/workflows/[workflowId]/executions/route.ts",
    "chars": 3601,
    "preview": "import { NextRequest, NextResponse } from \"next/server\";\nimport { getServerSession } from \"next-auth\";\nimport { authOpti"
  },
  {
    "path": "app/(ee)/api/workflows/[workflowId]/route.ts",
    "chars": 8587,
    "preview": "import { NextRequest, NextResponse } from \"next/server\";\n\nimport {\n  UpdateWorkflowRequestSchema,\n  formatZodError,\n} fr"
  },
  {
    "path": "app/(ee)/api/workflows/[workflowId]/steps/[stepId]/route.ts",
    "chars": 10291,
    "preview": "import { NextRequest, NextResponse } from \"next/server\";\n\nimport {\n  UpdateWorkflowStepRequestSchema,\n  formatZodError,\n"
  },
  {
    "path": "app/(ee)/api/workflows/[workflowId]/steps/route.ts",
    "chars": 11715,
    "preview": "import { NextRequest, NextResponse } from \"next/server\";\nimport { getServerSession } from \"next-auth\";\nimport { authOpti"
  },
  {
    "path": "app/(ee)/api/workflows/route.ts",
    "chars": 7560,
    "preview": "import { NextRequest, NextResponse } from \"next/server\";\n\nimport {\n  CreateWorkflowRequestSchema,\n  formatZodError,\n} fr"
  },
  {
    "path": "app/api/auth/verify-code/route.ts",
    "chars": 2926,
    "preview": "import { NextRequest, NextResponse } from \"next/server\";\n\nimport { fetchAndDeleteLoginCodeData } from \"@/lib/emails/send"
  },
  {
    "path": "app/api/cron/dataroom-digest/daily/route.ts",
    "chars": 994,
    "preview": "import { NextResponse } from \"next/server\";\n\nimport { receiver } from \"@/lib/cron\";\nimport { processDataroomDigest } fro"
  },
  {
    "path": "app/api/cron/dataroom-digest/weekly/route.ts",
    "chars": 1007,
    "preview": "import { NextResponse } from \"next/server\";\n\nimport { receiver } from \"@/lib/cron\";\nimport { processDataroomDigest } fro"
  },
  {
    "path": "app/api/cron/domains/route.ts",
    "chars": 3319,
    "preview": "import { NextResponse } from \"next/server\";\n\nimport { receiver } from \"@/lib/cron\";\nimport {\n  getConfigResponse,\n  getD"
  },
  {
    "path": "app/api/cron/domains/utils.ts",
    "chars": 5870,
    "preview": "import { deleteDomain } from \"@/lib/api/domains\";\nimport { limiter } from \"@/lib/cron\";\nimport { sendDeletedDomainEmail "
  },
  {
    "path": "app/api/cron/welcome-user/route.ts",
    "chars": 1393,
    "preview": "import { verifyQstashSignature } from \"@/lib/cron/verify-qstash\";\nimport { sendWelcomeEmail } from \"@/lib/emails/send-we"
  },
  {
    "path": "app/api/cron/year-in-review/route.ts",
    "chars": 965,
    "preview": "import { NextResponse } from \"next/server\";\n\nimport { receiver } from \"@/lib/cron\";\nimport { log } from \"@/lib/utils\";\ni"
  },
  {
    "path": "app/api/csp-report/route.ts",
    "chars": 438,
    "preview": "import { NextResponse } from \"next/server\";\n\nexport async function POST(request: Request) {\n  const report = await reque"
  },
  {
    "path": "app/api/feature-flags/route.ts",
    "chars": 593,
    "preview": "import { NextResponse } from \"next/server\";\n\nimport { getFeatureFlags } from \"@/lib/featureFlags\";\n\nexport const runtime"
  },
  {
    "path": "app/api/help/route.ts",
    "chars": 1256,
    "preview": "import { NextResponse } from \"next/server\";\n\nexport async function GET(request: Request) {\n  const { searchParams } = ne"
  },
  {
    "path": "app/api/integrations/slack/oauth/authorize/route.ts",
    "chars": 1389,
    "preview": "import { NextResponse } from \"next/server\";\n\nimport { authOptions } from \"@/pages/api/auth/[...nextauth]\";\nimport { getS"
  },
  {
    "path": "app/api/integrations/slack/oauth/callback/route.ts",
    "chars": 3311,
    "preview": "import { redirect } from \"next/navigation\";\nimport { NextResponse } from \"next/server\";\n\nimport { authOptions } from \"@/"
  },
  {
    "path": "app/api/og/route.tsx",
    "chars": 1176,
    "preview": "/* eslint-disable @next/next/no-img-element */\nimport { ImageResponse } from \"next/og\";\nimport { NextRequest } from \"nex"
  },
  {
    "path": "app/api/og/yir/route.tsx",
    "chars": 3079,
    "preview": "import { ImageResponse } from \"next/og\";\nimport { NextRequest } from \"next/server\";\n\nexport const runtime = \"edge\";\n\nexp"
  },
  {
    "path": "app/api/verify/login-link/route.ts",
    "chars": 240,
    "preview": "import { NextRequest, NextResponse } from \"next/server\";\n\n// Legacy magic link route - redirect to new code-based flow\ne"
  },
  {
    "path": "app/api/views/pages/route.ts",
    "chars": 3445,
    "preview": "import { NextRequest, NextResponse } from \"next/server\";\n\nimport { z } from \"zod\";\n\nimport { verifyDataroomSession } fro"
  },
  {
    "path": "app/api/views/route.ts",
    "chars": 24805,
    "preview": "import { NextRequest, NextResponse } from \"next/server\";\n\nimport { reportDeniedAccessAttempt } from \"@/ee/features/acces"
  },
  {
    "path": "app/api/views-dataroom/route.ts",
    "chars": 36156,
    "preview": "import { NextRequest, NextResponse } from \"next/server\";\n\nimport { reportDeniedAccessAttempt } from \"@/ee/features/acces"
  },
  {
    "path": "app/api/webhooks/callback/route.ts",
    "chars": 1678,
    "preview": "import { z } from \"zod\";\n\nimport { verifyQstashSignature } from \"@/lib/cron/verify-qstash\";\nimport prisma from \"@/lib/pr"
  },
  {
    "path": "app/layout.tsx",
    "chars": 1242,
    "preview": "import { Metadata } from \"next\";\nimport { Inter } from \"next/font/google\";\n\nimport \"@/styles/globals.css\";\n\nconst inter "
  },
  {
    "path": "app/robots.txt",
    "chars": 93,
    "preview": "User-Agent: *\nDisallow: /register\nDisallow: /verify/\nDisallow: /auth/\nDisallow: /unsubscribe\n"
  },
  {
    "path": "components/EmailForm.tsx",
    "chars": 1791,
    "preview": "export default function EmailForm({ onSubmitHandler, setEmail }: any) {\n  return (\n    <>\n      <div className=\"flex h-s"
  },
  {
    "path": "components/Skeleton.tsx",
    "chars": 285,
    "preview": "import { classNames } from \"@/lib/utils\";\n\nfunction Skeleton({\n  className,\n  ...props\n}: React.HTMLAttributes<HTMLDivEl"
  },
  {
    "path": "components/account/account-header.tsx",
    "chars": 790,
    "preview": "import { NavMenu } from \"../navigation-menu\";\n\nexport function AccountHeader() {\n  return (\n    <header>\n      <section "
  },
  {
    "path": "components/account/update-subscription.tsx",
    "chars": 1226,
    "preview": "import { useOptimisticUpdate } from \"@/components/hooks/use-optimistic-update\";\nimport { Switch } from \"@/components/ui/"
  },
  {
    "path": "components/account/upload-avatar.tsx",
    "chars": 3181,
    "preview": "\"use client\";\n\nimport { ReactNode, useEffect, useState } from \"react\";\n\nimport { useSession } from \"next-auth/react\";\nim"
  },
  {
    "path": "components/agreements/agreement-card.tsx",
    "chars": 5720,
    "preview": "import { useState } from \"react\";\n\nimport { useTeam } from \"@/context/team-context\";\nimport { DownloadIcon, FileTextIcon"
  },
  {
    "path": "components/ai-elements/conversation.tsx",
    "chars": 2499,
    "preview": "\"use client\";\n\nimport { Button } from \"@/components/ui/button\";\nimport { cn } from \"@/lib/utils\";\nimport { ArrowDownIcon"
  },
  {
    "path": "components/ai-elements/message.tsx",
    "chars": 10878,
    "preview": "\"use client\";\n\nimport type { ComponentProps, HTMLAttributes, ReactElement } from \"react\";\nimport { createContext, memo, "
  },
  {
    "path": "components/ai-elements/prompt-input.tsx",
    "chars": 37509,
    "preview": "\"use client\";\n\nimport { Button } from \"@/components/ui/button\";\nimport {\n  Command,\n  CommandEmpty,\n  CommandGroup,\n  Co"
  },
  {
    "path": "components/ai-elements/shimmer.tsx",
    "chars": 1927,
    "preview": "\"use client\";\n\nimport {\n  type CSSProperties,\n  type ElementType,\n  type JSX,\n  memo,\n  useMemo,\n} from \"react\";\n\nimport"
  },
  {
    "path": "components/analytics/analytics-card.tsx",
    "chars": 1312,
    "preview": "import { ReactNode } from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\ninterface AnalyticsCardProps {\n  title: string;\n "
  },
  {
    "path": "components/analytics/dashboard-views-chart.tsx",
    "chars": 8786,
    "preview": "import { useMemo } from \"react\";\n\nimport { format } from \"date-fns\";\nimport {\n  Bar,\n  BarChart,\n  CartesianGrid,\n  Resp"
  },
  {
    "path": "components/analytics/documents-table.tsx",
    "chars": 9287,
    "preview": "import Link from \"next/link\";\nimport { useRouter } from \"next/router\";\n\nimport { useState } from \"react\";\n\nimport { useT"
  },
  {
    "path": "components/analytics/links-table.tsx",
    "chars": 11109,
    "preview": "import Link from \"next/link\";\nimport { useRouter } from \"next/router\";\n\nimport { useEffect, useState } from \"react\";\n\nim"
  },
  {
    "path": "components/analytics/time-range-select.tsx",
    "chars": 6820,
    "preview": "import { useEffect, useState } from \"react\";\n\nimport { PlanEnum } from \"@/ee/stripe/constants\";\nimport { differenceInDay"
  },
  {
    "path": "components/analytics/views-table.tsx",
    "chars": 15011,
    "preview": "import Link from \"next/link\";\nimport { useRouter } from \"next/router\";\n\nimport { useState } from \"react\";\n\nimport { useT"
  },
  {
    "path": "components/analytics/visitors-table.tsx",
    "chars": 11772,
    "preview": "import Link from \"next/link\";\nimport { useRouter } from \"next/router\";\n\nimport { useState } from \"react\";\n\nimport { useT"
  },
  {
    "path": "components/billing/add-seat-modal.tsx",
    "chars": 5993,
    "preview": "import Link from \"next/link\";\nimport { useRouter } from \"next/router\";\n\nimport { useEffect, useState } from \"react\";\n\nim"
  },
  {
    "path": "components/billing/plan-badge.tsx",
    "chars": 568,
    "preview": "import { CrownIcon } from \"lucide-react\";\n\nimport { cn } from \"@/lib/utils\";\n\nexport default function PlanBadge({\n  plan"
  },
  {
    "path": "components/billing/pro-annual-banner.tsx",
    "chars": 3094,
    "preview": "import { useRouter } from \"next/router\";\n\nimport { Dispatch, SetStateAction, useState } from \"react\";\n\nimport { useTeam "
  },
  {
    "path": "components/billing/pro-banner.tsx",
    "chars": 1724,
    "preview": "import { Dispatch, SetStateAction } from \"react\";\n\nimport { PlanEnum } from \"@/ee/stripe/constants\";\nimport Cookies from"
  },
  {
    "path": "components/billing/upgrade-plan-container.tsx",
    "chars": 11236,
    "preview": "import { useRouter } from \"next/router\";\n\nimport { useState } from \"react\";\n\nimport { useTeam } from \"@/context/team-con"
  },
  {
    "path": "components/billing/upgrade-plan-modal-old.tsx",
    "chars": 10053,
    "preview": "import { useRouter } from \"next/router\";\n\nimport { useEffect, useMemo, useState } from \"react\";\nimport React from \"react"
  },
  {
    "path": "components/billing/upgrade-plan-modal-with-discount.tsx",
    "chars": 22671,
    "preview": "import Link from \"next/link\";\nimport { useRouter } from \"next/router\";\n\nimport { useEffect, useMemo, useState } from \"re"
  },
  {
    "path": "components/billing/upgrade-plan-modal.tsx",
    "chars": 15659,
    "preview": "import Link from \"next/link\";\nimport { useRouter } from \"next/router\";\n\nimport { useEffect, useMemo, useState } from \"re"
  },
  {
    "path": "components/billing/yearly-upgrade-banner.tsx",
    "chars": 5749,
    "preview": "\"use client\";\n\nimport { useRouter } from \"next/router\";\n\nimport { Dispatch, SetStateAction, useState } from \"react\";\n\nim"
  },
  {
    "path": "components/blur-image.tsx",
    "chars": 1071,
    "preview": "\"use client\";\n\nimport Image, { ImageProps } from \"next/image\";\n\nimport { useEffect, useState } from \"react\";\n\nimport { c"
  },
  {
    "path": "components/charts/bar-chart-tooltip.tsx",
    "chars": 2731,
    "preview": "import { useRouter } from \"next/router\";\n\nimport { useDocumentThumbnail } from \"@/lib/swr/use-document\";\n\nimport { getCo"
  },
  {
    "path": "components/charts/bar-chart.tsx",
    "chars": 3093,
    "preview": "import { useState } from \"react\";\n\nimport { BarChart } from \"@tremor/react\";\n\nimport CustomTooltip from \"./bar-chart-too"
  },
  {
    "path": "components/charts/utils.ts",
    "chars": 2169,
    "preview": "export type Data = {\n  pageNumber: string;\n  data: {\n    versionNumber: number;\n    avg_duration: number;\n  }[];\n};\n\nexp"
  },
  {
    "path": "components/conversations/index.tsx",
    "chars": 486,
    "preview": "\"use client\";\n\nimport { ConversationListItem as ConversationListItemEE } from \"@/ee/features/conversations/components/da"
  },
  {
    "path": "components/datarooms/actions/download-dataroom.tsx",
    "chars": 1204,
    "preview": "import { useState } from \"react\";\n\nimport { DownloadIcon } from \"lucide-react\";\n\nimport { DownloadProgressModal } from \""
  },
  {
    "path": "components/datarooms/actions/generate-index-button.tsx",
    "chars": 411,
    "preview": "import GenerateIndexDialog from \"./generate-index-dialog\";\n\ninterface GenerateIndexButtonProps {\n  teamId: string;\n  dat"
  },
  {
    "path": "components/datarooms/actions/generate-index-dialog.tsx",
    "chars": 7518,
    "preview": "import { useState } from \"react\";\n\nimport { PlanEnum } from \"@/ee/stripe/constants\";\nimport {\n  FileJson,\n  FileSlidersI"
  },
  {
    "path": "components/datarooms/actions/rebuild-index-button.tsx",
    "chars": 5190,
    "preview": "import { useState } from \"react\";\n\nimport { PlanEnum } from \"@/ee/stripe/constants\";\nimport { ListOrderedIcon } from \"lu"
  },
  {
    "path": "components/datarooms/actions/remove-document-modal.tsx",
    "chars": 8066,
    "preview": "import { useRouter } from \"next/router\";\n\nimport {\n  Dispatch,\n  SetStateAction,\n  useCallback,\n  useMemo,\n  useState,\n}"
  }
]

// ... and 1268 more files (download for full content)

About this extraction

This page contains the full source code of the mfts/papermark GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 1468 files (6.2 MB), approximately 1.7M tokens, and a symbol index with 2537 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!