first commit

This commit is contained in:
2025-04-24 13:11:28 +08:00
commit ff9c54d5e4
5960 changed files with 834111 additions and 0 deletions

View File

@@ -0,0 +1,16 @@
import { GroupPolicy } from '../subscription/dashboard/subscription'
import { SSOConfig } from '../subscription/sso'
import { TeamInvite } from '../team-invite'
export type Subscription = {
_id: string
teamInvites: TeamInvite[]
groupPolicy: GroupPolicy
admin_id: string
groupPlan: boolean
customAccount: boolean
ssoConfig: SSOConfig
managedUsersEnabled: boolean
v1_id: number
salesforce_id: string
}

View File

@@ -0,0 +1,4 @@
export type User = {
_id: string
email: string
}

View File

@@ -0,0 +1,19 @@
import { Institution } from './institution'
import { Portal } from './portal'
import { Nullable } from './utils'
export type Affiliation = {
cachedConfirmedAt: Nullable<string>
cachedEntitlement: Nullable<boolean>
cachedLastDayToReconfirm: Nullable<string>
cachedPastReconfirmDate: boolean
cachedReconfirmedAt: Nullable<string>
department: Nullable<string>
inReconfirmNotificationPeriod: boolean
inferred: boolean
institution: Institution
licence: 'free' | 'pro_plus'
pastReconfirmDate: boolean
portal: Portal
role: Nullable<string>
}

View File

@@ -0,0 +1,11 @@
export type Annotation = {
row: number
type: 'info' | 'warning' | 'error'
text: string
source?: string
ruleId?: string
id: string
entryIndex: number
firstOnLine: boolean
command?: string
}

29
services/web/types/assets.d.ts vendored Normal file
View File

@@ -0,0 +1,29 @@
declare module '*.svg' {
const src: string
export default src
}
declare module '*.png' {
const src: string
export default src
}
declare module '*.gif' {
const src: string
export default src
}
declare module '*.mp4' {
const src: string
export default src
}
declare module '*.wasm' {
const src: string
export default src
}
declare module '*.txt' {
const src: string
export default src
}

View File

@@ -0,0 +1,11 @@
export type BsStyle =
| 'primary'
| 'secondary'
| 'danger'
| 'info'
| 'default'
| 'link'
| 'warning'
| 'success'
export type BsSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl'

View File

@@ -0,0 +1,32 @@
import { ThreadId } from './review-panel/review-panel'
import { UserId } from './user'
export interface Operation {
p: number // position
}
export interface InsertOperation extends Operation {
i: string // inserted text
}
export interface DeleteOperation extends Operation {
d: string // deleted text
}
export interface CommentOperation extends Operation {
c: string // comment text
t: ThreadId // thread/comment id
}
export type EditOperation = InsertOperation | DeleteOperation
export type AnyOperation = EditOperation | CommentOperation
export type Change<T extends AnyOperation = AnyOperation> = {
id: string
metadata?: {
user_id: UserId | null
ts: Date
}
op: T
}

28
services/web/types/cms.ts Normal file
View File

@@ -0,0 +1,28 @@
export type IconElementSticker =
| 'sticker | arrow | grey | large'
| 'sticker | books | pink'
| 'sticker | books | yellow'
| 'sticker | cog-pen | grey'
| 'sticker | collaborate | purple'
| 'sticker | collaborate | yellow'
| 'sticker | curly-braces | purple'
| 'sticker | cursors | red'
| 'sticker | data | grey'
| 'sticker | formatting | green'
| 'sticker | globe | green'
| 'sticker | house-tree | grey | large'
| 'sticker | hub | tangerine'
| 'sticker | lock | grey | medium'
| 'sticker | lightning | yellow'
| 'sticker | overleaf | green | medium'
| 'sticker | pen | purple'
| 'sticker | pen | tangerine'
| 'sticker | pi | tangerine'
| 'sticker | rocket | yellow'
| 'sticker | rocket | yellow | medium'
| 'sticker | support | tangerine'
| 'sticker | support | yellow'
| 'sticker | waving-hand | purple | medium'
export type IconElement = IconElementSticker
export type IconStyle = 'Green circle' | 'Large purple text' | 'Default'

View File

@@ -0,0 +1,36 @@
export type CompileOutputFile = {
path: string
url: string
downloadURL?: string
type: string
build: string
ranges?: {
start: number
end: number
hash: string
objectId: string
}[]
contentId?: string
size?: number
// assigned by buildFileList in frontend
main?: boolean
}
export type CompileResponseData = {
fromCache?: boolean
status: string
outputFiles: CompileOutputFile[]
compileGroup?: string
clsiServerId?: string
pdfDownloadDomain?: string
pdfCachingMinChunkSize: number
validationProblems: any
stats: any
timings: any
outputFilesArchive?: CompileOutputFile
// assigned on response body by DocumentCompiler in frontend
rootDocId?: string
options: any
}

View File

@@ -0,0 +1,4 @@
export type Doc = {
_id: string
name: string
}

View File

@@ -0,0 +1,52 @@
type TemplateLink = {
name: string
url: string
trackingKey: string
}
export type ExposedSettings = {
adminEmail: string
appName: string
cookieDomain: string
dropboxAppName: string
emailConfirmationDisabled: boolean
enableSubscriptions: boolean
gaToken?: string
gaTokenV4?: string
hasAffiliationsFeature: boolean
hasLinkUrlFeature: boolean
hasLinkedProjectFileFeature: boolean
hasLinkedProjectOutputFileFeature: boolean
hasSamlBeta?: boolean
hasSamlFeature: boolean
hotjarId?: string
hotjarVersion?: string
ieeeBrandId: number
isOverleaf: boolean
maxEntitiesPerProject: number
projectUploadTimeout: number
maxUploadSize: number
recaptchaDisabled: {
invite: boolean
login: boolean
passwordReset: boolean
register: boolean
addEmail: boolean
}
recaptchaSiteKeyV3?: string
recaptchaSiteKey?: string
samlInitPath?: string
sentryAllowedOriginRegex: string
sentryDsn?: string
sentryEnvironment?: string
sentryRelease?: string
siteUrl: string
textExtensions: string[]
editableFilenames: string[]
validRootDocExtensions: string[]
fileIgnorePattern: string
templateLinks?: TemplateLink[]
labsEnabled: boolean
wikiEnabled?: boolean
templatesEnabled?: boolean
}

View File

@@ -0,0 +1,7 @@
export type FileRef = {
_id: string
name: string
created?: string
linkedFileData?: any
hash: string
}

View File

@@ -0,0 +1,5 @@
import { Folder } from './folder'
import { Doc } from './doc'
import { FileRef } from './file-ref'
export type FileTreeEntity = Folder | Doc | FileRef

View File

@@ -0,0 +1,10 @@
import { Doc } from './doc'
import { FileRef } from './file-ref'
export type Folder = {
_id: string
name: string
docs: Doc[]
folders: Folder[]
fileRefs: FileRef[]
}

2
services/web/types/globals.d.ts vendored Normal file
View File

@@ -0,0 +1,2 @@
// eslint-disable-next-line camelcase,no-unused-vars
declare let __webpack_public_path__: string

View File

@@ -0,0 +1,22 @@
export type SSOEnrollment = {
groupId: string
linkedAt: Date
primary: boolean
}
export type UserEnrollment = {
managedBy?: string
enrolledAt?: Date
sso?: SSOEnrollment[]
}
export type User = {
_id: string
email: string
first_name: string
last_name: string
invite: boolean
last_active_at: Date
enrollment?: UserEnrollment
isEntityAdmin?: boolean
}

View File

@@ -0,0 +1,3 @@
export type ValidationStatus = {
[key: string]: boolean | undefined
}

View File

@@ -0,0 +1,6 @@
// eslint-disable-next-line no-unused-vars
export enum Compare {
SORT_A_AFTER_B = 1, // eslint-disable-line no-unused-vars
SORT_A_BEFORE_B = -1, // eslint-disable-line no-unused-vars
SORT_KEEP_ORDER = 0, // eslint-disable-line no-unused-vars
}

View File

@@ -0,0 +1,3 @@
declare const brand: unique symbol
export type Brand<T, TBrand> = T & { [brand]: TBrand }

View File

@@ -0,0 +1,3 @@
import { Brand } from './brand'
export type DateString = Brand<string, 'DateString'>

View File

@@ -0,0 +1,5 @@
export type Highlight = {
cursor: { row: number; column: number }
hue: number
label: string
}

View File

@@ -0,0 +1,24 @@
import { FileDiff } from '../../frontend/js/features/history/services/types/file'
import { Nullable } from '../utils'
type Docs = Record<string, unknown>
interface Range {
fromV: Nullable<number>
toV: Nullable<number>
}
interface HoveredRange {
fromV: Nullable<unknown>
toV: Nullable<unknown>
}
export interface Selection {
docs: Docs
pathname: Nullable<string>
range: Range
hoveredRange: HoveredRange
diff: Nullable<unknown>
files: FileDiff[]
file: Nullable<unknown>
}

View File

@@ -0,0 +1,15 @@
import { IdeEvents } from '@/features/ide-react/create-ide-event-emitter'
export type ScopeEventName = keyof IdeEvents
export interface ScopeEventEmitter {
emit: <T extends ScopeEventName>(
eventName: T,
broadcast: boolean,
...detail: IdeEvents[T]
) => void
on: <T extends ScopeEventName>(
eventName: T,
listener: (event: Event, ...args: IdeEvents[T]) => void
) => () => void
}

View File

@@ -0,0 +1,7 @@
export interface ScopeValueStore {
// We can't make this generic because get() can always return undefined if
// there is no entry for the path
get: (path: string) => any
set: (path: string, value: unknown) => void
watch: <T>(path: string, callback: (newValue: T) => void) => () => void
}

View File

@@ -0,0 +1,13 @@
import { Nullable } from './utils'
export type Institution = {
commonsAccount: boolean
writefullCommonsAccount: boolean
confirmed: boolean
id: number
isUniversity: boolean
maxConfirmationMonths: Nullable<number>
name: string
ssoBeta: boolean
ssoEnabled: boolean
}

View File

@@ -0,0 +1,13 @@
export type OAuthProvider = {
name: string
descriptionKey: string
descriptionOptions: {
appName: string
link?: string
service?: string
}
hideWhenNotLinked?: boolean
linkPath: string
}
export type OAuthProviders = Record<string, OAuthProvider>

View File

@@ -0,0 +1,6 @@
export type PasswordStrengthOptions = {
length?: {
min?: number
max?: number
}
}

View File

@@ -0,0 +1,4 @@
export type PortalTemplate = {
name: string
url: string
}

View File

@@ -0,0 +1,4 @@
export type Portal = {
slug: string
templates_count: number
}

View File

@@ -0,0 +1,4 @@
export type PreviewPath = {
url: string
extension: string
}

View File

@@ -0,0 +1,31 @@
import { Brand } from './helpers/brand'
import { OverallTheme } from '@/shared/utils/styles'
export type AllowedImageName = {
imageDesc: string
imageName: string
}
export type DocId = Brand<string, 'DocId'>
export type MainDocument = {
doc: {
name: string
id: DocId
}
path: string
}
export type ProjectCompiler = 'pdflatex' | 'latex' | 'xelatex' | 'lualatex'
export type OverallThemeMeta = {
name: string
path: string
val: OverallTheme
}
export type SpellCheckLanguage = {
name: string
code: string
dic?: string
server?: false
}

View File

@@ -0,0 +1,31 @@
import { MongoUser } from './user'
import { Folder } from './folder'
export type ProjectMember = {
_id: string
type: 'user'
privileges: 'readOnly' | 'readAndWrite'
name: string
email: string
}
type ProjectInvite = {
_id: string
privileges: 'readOnly' | 'readAndWrite'
name: string
email: string
}
export type Project = {
_id: string
name: string
features: Record<string, unknown>
publicAccesLevel?: string
tokens: Record<string, unknown>
owner: MongoUser
members: ProjectMember[]
invites: ProjectInvite[]
rootDoc_id?: string
rootFolder?: Folder[]
deletedByExternalDataSource?: boolean
}

View File

@@ -0,0 +1,71 @@
import { SortingOrder } from '../../sorting-order'
import { MergeAndOverride } from '../../utils'
export type Page = {
size: number
lastId?: string
}
export type Sort = {
by: 'lastUpdated' | 'title' | 'owner'
order: SortingOrder
}
export type Filters = {
ownedByUser?: boolean
sharedWithUser?: boolean
archived?: boolean
trashed?: boolean
tag?: string | null
search?: string
}
export type GetProjectsRequestBody = {
page: Page
sort: Sort
filters: Filters
}
export type UserRef = {
id: string
email: string
firstName: string
lastName: string
}
export type ProjectApi = {
id: string
name: string
owner?: UserRef
lastUpdated: Date
lastUpdatedBy: UserRef | null
archived: boolean
trashed: boolean
accessLevel: 'owner' | 'readWrite' | 'readOnly' | 'readAndWrite'
source: 'owner' | 'invite' | 'token'
}
export type Project = MergeAndOverride<
ProjectApi,
{
lastUpdated: string
selected?: boolean
}
>
export type GetProjectsResponseBody = {
totalSize: number
projects: Project[]
}
export type ClonedProject = {
project_id: string
name: string
lastUpdated: string
owner: {
_id: string
email: string
first_name: string
last_name: string
}
}

View File

@@ -0,0 +1,115 @@
type TemplateKey =
| 'notification_project_invite'
| 'wfh_2020_upgrade_offer'
| 'notification_ip_matched_affiliation'
| 'notification_tpds_file_limit'
| 'notification_dropbox_duplicate_project_names'
| 'notification_dropbox_unlinked_due_to_lapsed_reconfirmation'
| 'notification_group_invitation'
| 'notification_personal_and_group_subscriptions'
type NotificationBase = {
_id?: number
html?: string
templateKey: TemplateKey | string
}
export interface NotificationProjectInvite extends NotificationBase {
templateKey: Extract<TemplateKey, 'notification_project_invite'>
messageOpts: {
projectName: string
userName: string
projectId: number | string
token: string
}
}
interface NotificationWFH2020UpgradeOffer extends NotificationBase {
templateKey: Extract<TemplateKey, 'wfh_2020_upgrade_offer'>
}
export interface NotificationIPMatchedAffiliation extends NotificationBase {
templateKey: Extract<TemplateKey, 'notification_ip_matched_affiliation'>
messageOpts: {
university_name: string
ssoEnabled: boolean
portalPath?: string
institutionId: string
}
}
export interface NotificationTPDSFileLimit extends NotificationBase {
templateKey: Extract<TemplateKey, 'notification_tpds_file_limit'>
messageOpts: {
projectName: string
projectId?: string
}
}
export interface NotificationDropboxDuplicateProjectNames
extends NotificationBase {
templateKey: Extract<
TemplateKey,
'notification_dropbox_duplicate_project_names'
>
messageOpts: {
projectName: string
}
}
interface NotificationDropboxUnlinkedDueToLapsedReconfirmation
extends NotificationBase {
templateKey: Extract<
TemplateKey,
'notification_dropbox_unlinked_due_to_lapsed_reconfirmation'
>
}
export interface NotificationGroupInvitation extends NotificationBase {
templateKey: Extract<TemplateKey, 'notification_group_invitation'>
messageOpts: {
token: string
inviterName: string
managedUsersEnabled: boolean
}
}
export type Notification =
| NotificationProjectInvite
| NotificationWFH2020UpgradeOffer
| NotificationIPMatchedAffiliation
| NotificationTPDSFileLimit
| NotificationDropboxDuplicateProjectNames
| NotificationDropboxUnlinkedDueToLapsedReconfirmation
| NotificationGroupInvitation
export type Institution = {
_id?: number
email: string
institutionEmail: string
institutionId: number | string
institutionName: string
requestedEmail: string
templateKey: string
error?: {
translatedMessage?: string
message?: string
tryAgain?: boolean
}
}
export type PendingGroupSubscriptionEnrollment = {
groupId: string
groupName: string
}
export const GroupsAndEnterpriseBannerVariants = ['on-premise', 'FOMO'] as const
export type GroupsAndEnterpriseBannerVariant =
(typeof GroupsAndEnterpriseBannerVariants)[number]
export const USGovBannerVariants = [
'government-purchasing',
'small-business-reseller',
'fedramp',
] as const
export type USGovBannerVariant = (typeof USGovBannerVariants)[number]

View File

@@ -0,0 +1,52 @@
type SubscriptionBase = {
featuresPageURL: string
}
export type FreePlanSubscription = {
type: 'free'
} & SubscriptionBase
type FreeSubscription = FreePlanSubscription
type RecurlyStatus = {
state: 'active' | 'canceled' | 'expired' | 'paused'
}
type PaidSubscriptionBase = {
plan: {
name: string
}
subscription: {
teamName?: string
name: string
recurlyStatus?: RecurlyStatus
}
} & SubscriptionBase
export type IndividualPlanSubscription = {
type: 'individual'
remainingTrialDays: number
} & PaidSubscriptionBase
export type GroupPlanSubscription = {
type: 'group'
remainingTrialDays: number
} & PaidSubscriptionBase
export type CommonsPlanSubscription = {
type: 'commons'
} & PaidSubscriptionBase
type PaidSubscription =
| IndividualPlanSubscription
| GroupPlanSubscription
| CommonsPlanSubscription
export type StandaloneAiAddOnSubscription = {
type: 'standalone-ai-add-on'
}
export type Subscription =
| FreeSubscription
| PaidSubscription
| StandaloneAiAddOnSubscription

View File

@@ -0,0 +1,6 @@
export type Survey = {
name: string
preText: string
linkText: string
url: string
}

View File

@@ -0,0 +1,5 @@
export type PublicAccessLevel =
| 'readOnly'
| 'readAndWrite'
| 'private'
| 'tokenBased'

View File

@@ -0,0 +1,23 @@
export interface CardElementChangeState {
brand: string
cvv: {
empty: boolean
focus: boolean
valid: boolean
}
empty: boolean
expiry: {
empty: boolean
focus: boolean
valid: boolean
}
firstSix: string
focus: boolean
lastFour: string
number: {
empty: boolean
focus: boolean
valid: boolean
}
valid: boolean
}

View File

@@ -0,0 +1,69 @@
import {
SubscriptionPricingInstance,
SubscriptionPricingState,
Address,
Tax,
} from 'recurly__recurly-js'
export interface Plan {
code: string
name: string
period: {
interval: string
length: number
}
price: Record<
string,
{
unit_amount: number
symbol: string
setup_fee: number
}
>
quantity: number
tax_code: string
tax_exempt: boolean
trial?: {
interval: string
length: number
}
}
interface Coupon {
code: string
name: string
discount: {
type: string
rate: number
}
single_use: boolean
applies_for_months: number
duration: string
temporal_unit: string
temporal_amount: number
plans: unknown[]
applies_to_non_plan_charges: boolean
applies_to_plans: boolean
applies_to_all_plans: boolean
redemption_resource: string
}
interface AddOn {
code: string
quantity: number
}
// Extending the default interface as it lacks the `items` prop
export interface SubscriptionPricingInstanceCustom
extends SubscriptionPricingInstance,
SubscriptionPricingState {
id: string
items: {
addons: AddOn[]
address?: Address
coupon?: Coupon
currency: string
plan?: Plan
tax?: Tax
}
}

View File

@@ -0,0 +1,31 @@
import {
ReviewPanelCommentThreadMessage,
ReviewPanelUser,
} from './review-panel'
import { UserId } from '../user'
import { DateString } from '../helpers/date'
export interface ReviewPanelCommentThreadBase {
messages: Array<ReviewPanelCommentThreadMessage>
submitting?: boolean // angular specific (to be made into a local state)
}
interface ReviewPanelUnresolvedCommentThread
extends ReviewPanelCommentThreadBase {
resolved?: never
resolved_at?: never
resolved_by_user_id?: never
resolved_by_user?: never
}
export interface ReviewPanelResolvedCommentThread
extends ReviewPanelCommentThreadBase {
resolved: boolean
resolved_at: DateString
resolved_by_user_id: UserId
resolved_by_user: ReviewPanelUser
}
export type ReviewPanelCommentThread =
| ReviewPanelUnresolvedCommentThread
| ReviewPanelResolvedCommentThread

View File

@@ -0,0 +1,25 @@
import { Brand } from '../helpers/brand'
import { UserId } from '../user'
export type SubView = 'cur_file' | 'overview'
export type ThreadId = Brand<string, 'ThreadId'>
export interface ReviewPanelUser {
avatar_text: string
email: string
hue: number
id: UserId
isSelf: boolean
name: string
}
export type CommentId = Brand<string, 'CommentId'>
export interface ReviewPanelCommentThreadMessage {
content: string
id: CommentId
timestamp: Date
user: ReviewPanelUser
user_id: UserId
}

View File

@@ -0,0 +1,18 @@
export type AccessToken = {
_id: string
accessTokenPartial: string
createdAt: Date
accessTokenExpiresAt: Date
lastUsedAt?: Date
}
export type SAMLError = {
translatedMessage?: string
message?: string
tryAgain?: boolean
}
export type InstitutionLink = {
universityName: string
hasEntitlement?: boolean
}

View File

@@ -0,0 +1,9 @@
import EventEmitter from 'events'
// type for the Doc class in vendor/libs/sharejs.js
export interface ShareDoc extends EventEmitter {
detach_cm6?: () => void
getText: () => string
insert: (pos: number, insert: string, fromUndo: boolean) => void
del: (pos: number, length: number, fromUndo: boolean) => void
}

1
services/web/types/sorting-order.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
export type SortingOrder = 'asc' | 'desc'

View File

@@ -0,0 +1,15 @@
export type SplitTestInfo = {
phase: string
active: boolean
archived: boolean
missing?: boolean
variants: {
name: string
rolloutPercent: number
}[]
hasOverride?: boolean
badgeInfo?: {
url?: string
tooltipText?: string
}
}

View File

@@ -0,0 +1,6 @@
export interface CreateError {
data: {
message?: string
threeDSecureActionTokenId?: string
}
}

View File

@@ -0,0 +1,22 @@
export const currencies = <const>{
USD: '$',
EUR: '€',
GBP: '£',
SEK: 'kr',
CAD: '$',
NOK: 'kr',
DKK: 'kr',
AUD: '$',
NZD: '$',
CHF: 'Fr',
SGD: '$',
INR: '₹',
BRL: 'R$',
MXN: '$',
COP: '$',
CLP: '$',
PEN: 'S/',
}
type Currency = typeof currencies
export type CurrencyCode = keyof Currency

View File

@@ -0,0 +1,7 @@
export type GroupPlans = {
plans: {
display: string
code: string
}[]
sizes: string[]
}

View File

@@ -0,0 +1,9 @@
export type ManagedInstitution = {
v1Id: number
managerIds: string[]
metricsEmail: {
optedOutUserIds: string[]
lastSent: Date
}
name: string
}

View File

@@ -0,0 +1,10 @@
export type SubscriptionDashModalIds =
| 'change-to-plan'
| 'change-to-group'
| 'keep-current-plan'
| 'leave-group'
| 'change-plan'
| 'cancel-ai-add-on'
| 'manage-on-writefull'
| 'pause-subscription'
| 'unpause-subscription'

View File

@@ -0,0 +1,6 @@
export type Publisher = {
slug: string
managerIds: string[]
name: string
partner: string
}

View File

@@ -0,0 +1,101 @@
import { CurrencyCode } from '../currency'
import { Nullable } from '../../utils'
import {
Plan,
AddOn,
PaymentProviderAddOn,
PendingPaymentProviderPlan,
} from '../plan'
import { User } from '../../user'
type SubscriptionState = 'active' | 'canceled' | 'expired' | 'paused'
// when puchasing a new add-on in recurly, we only need to provide the code
export type PurchasingAddOnCode = {
code: string
}
type PaymentProviderCoupon = {
code: string
name: string
description: string
}
type PaymentProviderRecord = {
taxRate: number
billingDetailsLink: string
accountManagementLink: string
additionalLicenses: number
addOns: PaymentProviderAddOn[]
totalLicenses: number
nextPaymentDueAt: string
nextPaymentDueDate: string
currency: CurrencyCode
state?: SubscriptionState
trialEndsAtFormatted: Nullable<string>
trialEndsAt: Nullable<string>
activeCoupons: PaymentProviderCoupon[]
accountEmail: string
hasPastDueInvoice: boolean
displayPrice: string
planOnlyDisplayPrice: string
addOnDisplayPricesWithoutAdditionalLicense: Record<string, string>
pendingAdditionalLicenses?: number
pendingTotalLicenses?: number
pausedAt?: Nullable<string>
remainingPauseCycles?: Nullable<number>
}
export type GroupPolicy = {
[policy: string]: boolean
}
export type Subscription = {
_id: string
admin_id: string
manager_ids: string[]
member_ids: string[]
invited_emails: string[]
groupPlan: boolean
groupPolicy?: GroupPolicy
membersLimit: number
teamInvites: object[]
planCode: string
recurlySubscription_id: string
plan: Plan
pendingPlan?: PendingPaymentProviderPlan
addOns?: AddOn[]
}
export type PaidSubscription = Subscription & {
payment: PaymentProviderRecord
}
export type CustomSubscription = Subscription & {
customAccount: boolean
}
export type GroupSubscription = PaidSubscription & {
teamName: string
teamNotice?: string
}
export type ManagedGroupSubscription = {
_id: string
userIsGroupMember: boolean
planLevelName: string
admin_id: {
email: string
}
features: {
groupSSO: boolean | null
managedUsers: boolean | null
}
teamName?: string
}
export type MemberGroupSubscription = Omit<GroupSubscription, 'admin_id'> & {
userIsGroupManager: boolean
planLevelName: string
admin_id: User
}

View File

@@ -0,0 +1,77 @@
import countries from '@/features/subscription/data/countries'
import { Plan } from './plan'
import { SubscriptionPricingStateTax } from 'recurly__recurly-js'
import { SubscriptionPricingInstanceCustom } from '../recurly/pricing/subscription'
import { currencies, CurrencyCode } from './currency'
export type RecurlyPrice =
| {
subtotal: string
plan: string
addons: string
setup_fee: string
discount: string
tax: string
total: string
}
| undefined
export type PricingFormState = {
first_name: string
last_name: string
postal_code: string
address1: string
address2: string
state: string
city: string
company: string
vat_number: string
country: (typeof countries)[number]['code'] | ''
coupon: string
}
export type PaymentContextValue = {
currencyCode: CurrencyCode
setCurrencyCode: React.Dispatch<
React.SetStateAction<PaymentContextValue['currencyCode']>
>
limitedCurrencies: Partial<typeof currencies>
pricingFormState: PricingFormState
setPricingFormState: React.Dispatch<
React.SetStateAction<PaymentContextValue['pricingFormState']>
>
plan: Plan
planCode: string
planName: string
planOffersFreeTrial: boolean
pricing: React.MutableRefObject<SubscriptionPricingInstanceCustom | undefined>
recurlyLoading: boolean
recurlyLoadError: boolean
recurlyPrice: RecurlyPrice
monthlyBilling: boolean | undefined
taxes: SubscriptionPricingStateTax[]
coupon:
| {
discountMonths?: number
discountRate?: number
singleUse: boolean
normalPrice: number
name: string
normalPriceWithoutTax: number
}
| undefined
couponError: string
trialLength: number | undefined
applyVatNumber: (vatNumber: PricingFormState['vat_number']) => void
addCoupon: (coupon: PricingFormState['coupon']) => void
changeCurrency: (newCurrency: CurrencyCode) => void
updateCountry: (country: PricingFormState['country']) => void
userCanNotStartRequestedTrial: boolean
showStudentConfirmation: boolean
studentConfirmationChecked: boolean
setStudentConfirmationChecked: React.Dispatch<
React.SetStateAction<PaymentContextValue['studentConfirmationChecked']>
>
updatePlan: (newPlanCode: string) => void
showNudgeToAnnualText: boolean
}

View File

@@ -0,0 +1 @@
export type PaymentMethod = 'credit_card' | 'paypal'

View File

@@ -0,0 +1,91 @@
type Features = {
collaborators: number
compileGroup: string
compileTimeout: number
dropbox: boolean
gitBridge: boolean
github: boolean
mendeley: boolean
references: boolean
referencesSearch: boolean
symbolPalette: boolean
templates: boolean
trackChanges: boolean
versioning: boolean
zotero: boolean
}
// add-ons stored on the subscription
export type AddOn = {
addOnCode: string
quantity: number
unitAmountInCents: number
}
// add-ons directly accessed through payment
export type PaymentProviderAddOn = {
code: string
name: string
quantity: number
unitPrice: number
amount?: number
displayPrice?: string
}
export type PendingPaymentProviderPlan = {
annual?: boolean
displayPrice?: string
featureDescription?: Record<string, unknown>[]
addOns?: PaymentProviderAddOn[]
features?: Features
groupPlan?: boolean
hideFromUsers?: boolean
membersLimit?: number
membersLimitAddOn?: string
name: string
planCode: string
price_in_cents: number
}
export type Plan = {
annual?: boolean
displayPrice?: string
featureDescription?: Record<string, unknown>[]
addOns?: AddOn[]
features?: Features
groupPlan?: boolean
hideFromUsers?: boolean
membersLimit?: number
membersLimitAddOn?: string
name: string
planCode: string
price_in_cents: number
}
export type PriceForDisplayData = {
totalForDisplay: string
totalAsNumber: number
subtotal: string
tax: string
includesTax: boolean
perUserDisplayPrice?: string
}
export type RecurlyPlanCode =
| 'collaborator'
| 'collaborator-annual'
| 'collaborator_free_trial_7_days'
| 'professional'
| 'professional-annual'
| 'professional_free_trial_7_days'
| 'student'
| 'student-annual'
| 'student_free_trial_7_days'
export type StripeLookupKey =
| 'collaborator_monthly'
| 'collaborator_annual'
| 'professional_monthly'
| 'professional_annual'
| 'student_monthly'
| 'student_annual'

View File

@@ -0,0 +1,23 @@
export type Certificate = {
id: string
value?: string
validFrom?: Date
validTo?: Date
}
export type SSOConfig = {
entryPoint?: string
certificates: Certificate[]
userIdAttribute?: string
userFirstNameAttribute?: string
userLastNameAttribute?: string
validated?: boolean
enabled?: boolean
}
export type GroupSSOLinkingStatus = {
groupId: string
linked?: boolean
groupName?: string
adminEmail: string
}

View File

@@ -0,0 +1,69 @@
export type SubscriptionChangePreview = {
change: SubscriptionChangeDescription
currency: string
paymentMethod: string
nextPlan: {
annual: boolean
}
immediateCharge: {
subtotal: number
tax: number
total: number
discount: number
}
nextInvoice: {
date: string
plan: {
name: string
amount: number
}
addOns: AddOn[]
subtotal: number
tax: {
rate: number
amount: number
}
total: number
}
}
type AddOn = {
code: string
name: string
quantity: number
unitAmount: number
amount: number
}
export type SubscriptionChangeDescription =
| AddOnPurchase
| AddOnUpdate
| GroupPlanUpgrade
| PremiumSubscriptionChange
export type AddOnPurchase = {
type: 'add-on-purchase'
addOn: Pick<AddOn, 'code' | 'name'>
}
export type AddOnUpdate = {
type: 'add-on-update'
addOn: Pick<AddOn, 'code' | 'quantity'> & {
prevQuantity: AddOn['quantity']
}
}
export type GroupPlanUpgrade = {
type: 'group-plan-upgrade'
prevPlan: {
name: string
}
}
export type PremiumSubscriptionChange = {
type: 'premium-subscription'
plan: {
code: string
name: string
}
}

View File

@@ -0,0 +1,11 @@
export type SystemMessage = {
_id: string
content: string
}
export type SuggestedLanguage = {
url: string
imgUrl: string
lngCode: string
lngName: string
}

View File

@@ -0,0 +1,7 @@
export type TeamInvite = {
email: string
token: string
inviterName: string
sentAt: Date
_id: string
}

View File

@@ -0,0 +1,3 @@
export type ThirdPartyId = string
export type ThirdPartyIds = Record<string, ThirdPartyId>

View File

@@ -0,0 +1,8 @@
import { CountryCode } from '../frontend/js/features/settings/data/countries-list'
export type University = {
id: number
name: string
country_code: CountryCode
departments: string[]
}

View File

@@ -0,0 +1,11 @@
import { Affiliation } from './affiliation'
export type UserEmailData = {
affiliation?: Affiliation
confirmedAt?: string
lastConfirmedAt?: string | null
email: string
default: boolean
samlProviderId?: string
emailHasInstitutionLicence?: boolean
}

View File

@@ -0,0 +1,20 @@
import { FontFamily, LineHeight, OverallTheme } from '@/shared/utils/styles'
export type Keybindings = 'none' | 'default' | 'vim' | 'emacs'
export type PdfViewer = 'pdfjs' | 'native'
export type UserSettings = {
pdfViewer: PdfViewer
autoComplete: boolean
autoPairDelimiters: boolean
syntaxValidation: boolean
editorTheme: string
overallTheme: OverallTheme
mode: Keybindings
fontSize: number
fontFamily: FontFamily
lineHeight: LineHeight
mathPreview: boolean
referencesSearchMode: 'advanced' | 'simple'
enableNewEditor: boolean
}

View File

@@ -0,0 +1,84 @@
import { Brand } from './helpers/brand'
export type RefProviders = {
mendeley?: boolean
papers?: boolean
zotero?: boolean
}
export type UserId = Brand<string, 'UserId'>
export type Features = {
aiErrorAssistant?: boolean
collaborators?: number
compileGroup?: 'standard' | 'priority'
compileTimeout?: number
dropbox?: boolean
gitBridge?: boolean
github?: boolean
mendeley?: boolean
papers?: boolean
references?: boolean
referencesSearch?: boolean
symbolPalette?: boolean
templates?: boolean
trackChanges?: boolean
versioning?: boolean
zotero?: boolean
}
export type FeatureUsage = {
[feature: string]: {
remainingUsage: number
resetDate: string // date string
}
}
export type User = {
id: UserId
isAdmin?: boolean
email: string
allowedFreeTrial?: boolean
hasRecurlySubscription?: boolean
first_name?: string
last_name?: string
alphaProgram?: boolean
betaProgram?: boolean
labsProgram?: boolean
signUpDate?: string // date string
features?: Features
refProviders?: RefProviders
writefull?: {
enabled: boolean
autoCreatedAccount: boolean
firstAutoLoad: boolean
}
aiErrorAssistant?: {
enabled: boolean
}
featureUsage?: FeatureUsage
planCode?: string
planName?: string
isAnnualPlan?: boolean
isMemberOfGroupSubscription?: boolean
hasInstitutionLicence?: boolean
}
export type LoggedOutUser = {
id: null
email?: undefined
first_name?: undefined
last_name?: undefined
signUpDate?: undefined
labsProgram?: undefined
alphaProgram?: undefined
betaProgram?: undefined
allowedFreeTrial?: undefined
features?: undefined
refProviders?: undefined
writefull?: undefined
isAdmin?: undefined
featureUsage?: undefined
}
export type MongoUser = Pick<User, Exclude<keyof User, 'id'>> & { _id: string }

View File

@@ -0,0 +1,22 @@
export type Nullable<T> = T | null
// eslint-disable-next-line @typescript-eslint/no-empty-interface, @typescript-eslint/no-empty-object-type
interface DeepReadonlyArray<T> extends ReadonlyArray<DeepReadonly<T>> {}
type DeepReadonlyObject<T> = {
readonly [P in keyof T]: DeepReadonly<T[P]>
}
export type DeepReadonly<T> = T extends (infer R)[]
? DeepReadonlyArray<R>
: T extends (...args: any[]) => void
? T
: T extends object
? DeepReadonlyObject<T>
: T
export type DeepPartial<T> = Partial<{ [P in keyof T]: DeepPartial<T[P]> }>
export type MergeAndOverride<Parent, Own> = Own & Omit<Parent, keyof Own>
export type Keys<T extends object> = (keyof T)[]

View File

@@ -0,0 +1,60 @@
type LinkedFileAgent = {
createLinkedFile: (
projectId: string,
linkedFileData: object,
name: string,
parentFolderId: string,
userId: string,
callback: () => void
) => void
refreshLinkedFile: (
projectId: string,
linkedFileData: object,
name: string,
parentFolderId: string,
userId: string,
callback: () => void
) => void
promises: {
createLinkedFile: (
projectId: string,
linkedFileData: object,
name: string,
parentFolderId: string,
userId: string
) => Promise<any>
refreshLinkedFile: (
projectId: string,
linkedFileData: object,
name: string,
parentFolderId: string,
userId: string
) => Promise<any>
}
}
export type WebModule = {
dependencies?: string[]
router?: {
apply?: (
webRouter: any,
privateApiRouter?: any,
publicApiRouter?: any
) => void
}
nonCsrfRouter?: {
apply: (webRouter: any, privateApiRouter: any, publicApiRouter: any) => void
}
hooks?: {
[name: string]: (args: any[]) => void
}
middleware?: {
[name: string]: (req: any, res: any, next: any) => void
}
sessionMiddleware?: (webRouter: any, options: any) => void
start?: () => Promise<void>
appMiddleware?: (app: any) => void
linkedFileAgents?: {
[name: string]: () => LinkedFileAgent
}
}

View File

@@ -0,0 +1,38 @@
import 'recurly__recurly-js'
import { ScopeValueStore } from './ide/scope-value-store'
import { MetaAttributesCache } from '@/utils/meta'
import { Socket } from '@/features/ide-react/connection/types/socket'
declare global {
// eslint-disable-next-line no-unused-vars
interface Window {
metaAttributesCache: MetaAttributesCache
_ide: Record<string, unknown> & {
$scope: Record<string, unknown> & {
pdf?: {
logEntryAnnotations: Record<string, unknown>
}
}
socket: Socket
}
MathJax: Record<string, any>
// For react-google-recaptcha
recaptchaOptions?: {
enterprise?: boolean
useRecaptchaNet?: boolean
}
expectingLinkedFileRefreshedSocketFor?: string | null
writefull?: {
type: 'extension' | 'integration'
}
WritefullStub?: any
io?: any
overleaf: {
unstable: {
store: ScopeValueStore
}
}
ga?: (...args: any) => void
gtag?: (...args: any) => void
}
}