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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion collab-server/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ if (process.argv.length !== 3) {
process.exit(1)
}

const projectDir = process.argv[2]
const projectDir = process.argv[2]!
const socketPath = path.join(process.cwd(), 'collab.sock')
const dbPath = path.join(process.cwd(), 'collab.db')

Expand Down
4 changes: 1 addition & 3 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,7 @@ const eslintConfig = defineConfig([
checksVoidReturn: { arguments: false, attributes: false },
},
],
'@typescript-eslint/no-unnecessary-condition': 'off', // actively misleading until we turn the typescript option noUncheckedIndexedAccess on
'@typescript-eslint/no-unnecessary-type-assertion': 'off', // actively misleading until we turn the typescript option noUncheckedIndexedAccess on
'@typescript-eslint/no-unnecessary-condition': 'error',
'@typescript-eslint/no-unused-vars': [
'error',
{
Expand All @@ -62,7 +61,6 @@ const eslintConfig = defineConfig([
enableAutofixRemoval: { imports: true },
},
],
'@typescript-eslint/no-unsafe-return': 'off', // noisy, temporarily disabled
'@typescript-eslint/require-await': 'off', // actively misleading in `'use server'` modules
'@typescript-eslint/restrict-template-expressions': 'off', // always allow `${x}` regardless of x's type
'@typescript-eslint/use-unknown-in-catch-callback-variable': 'error', // complements how strict works in typescript for chained promises
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ type Params = z.infer<typeof zParams>
* Access control is currently the same as for editing the project. */
export async function GET(_req: Request, { params }: { params: Promise<Params> }) {
const parsed = zParams.safeParse(await params)
if (!parsed.success) return new Response(parsed.error.issues[0].message, { status: 400 })
if (!parsed.success) return new Response(parsed.error.issues[0]!.message, { status: 400 })
const { userName, projectName, relPath: relPathSegs } = parsed.data

const session = await requireAuth()
Expand Down
6 changes: 3 additions & 3 deletions src/app/admin/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ function parseMeminfo(): Record<string, number> {
const result: Record<string, number> = {}
for (const line of text.split('\n')) {
const m = line.match(/^(\w+):\s+(\d+)/)
if (m) result[m[1]] = parseInt(m[2], 10) * 1024 // kB -> bytes
if (m) result[m[1]!] = parseInt(m[2]!, 10) * 1024 // kB -> bytes
}
return result
} catch {
Expand All @@ -151,7 +151,7 @@ export async function fetchHealth(): Promise<SystemHealth> {
const dfOut = execFileSync('df', ['-h', getDataDir()], { encoding: 'utf8' })
const lines = dfOut.trim().split('\n')
if (lines.length < 2) throw new Error('no dataVolumeDisk information')
const parts = lines[1].split(/\s+/)
const parts = lines[1]!.split(/\s+/)
dataVolumeDisk = {
total: parts[1] ?? '?',
used: parts[2] ?? '?',
Expand All @@ -170,7 +170,7 @@ export async function fetchHealth(): Promise<SystemHealth> {
try {
const text = fs.readFileSync('/proc/loadavg', 'utf8')
const parts = text.split(' ')
loadAvg = [parseFloat(parts[0]), parseFloat(parts[1]), parseFloat(parts[2])]
loadAvg = [parseFloat(parts[0]!), parseFloat(parts[1]!), parseFloat(parts[2]!)]
} catch {
loadAvg = [0, 0, 0]
}
Expand Down
2 changes: 1 addition & 1 deletion src/app/admin/components/HealthMonitor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export function HealthMonitor() {
return (
<section>
<h2>System health</h2>
<p style={{ color: '#dc2626' }}>Failed to load: {String(healthError)}</p>
<p style={{ color: '#dc2626' }}>Failed to load: {healthError.message}</p>
</section>
)
}
Expand Down
4 changes: 2 additions & 2 deletions src/app/api/auth-route/file/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ export async function GET(req: Request) {
// we can expect no trailing slash.
const match = uri.match(/^\/_file\/([^/]+)\/(.*[^/])$/)
if (!match) forbidden()
const rootDir = verifySignedDirToken(match[1])
const rootDir = verifySignedDirToken(match[1]!)
if (!rootDir) forbidden()
const realRootDir = await fs.realpath(rootDir).catch(() => null)
if (!realRootDir) forbidden()
const filePath = path.resolve(realRootDir, match[2])
const filePath = path.resolve(realRootDir, match[2]!)
// Ensure the absolute path with symlinks resolved lives under `realRootDir`.
// If resolution fails, we pass `filePath` through - Nginx will 404 it.
const realFilePath = await fs.realpath(filePath).catch(() => filePath)
Expand Down
2 changes: 1 addition & 1 deletion src/app/api/auth-route/vs/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export async function GET(req: Request) {
const uri = req.headers.get('x-auth-uri') ?? ''
const match = uri.match(/^\/_vs\/([^/]+)\/.*$/)
if (!match) forbidden()
const sessionId = match[1]
const sessionId = match[1]!
const userSession = await requireAuth()
const socketPath = getEditorSessionManager().socketPathForViewer(userSession.user.id, sessionId)
if (!socketPath) forbidden()
Expand Down
2 changes: 1 addition & 1 deletion src/app/api/setup-events/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export async function GET() {
interval = setInterval(() => {
const st = getSeedState()
while (cursor < st.events.length) {
const event = st.events[cursor++]
const event = st.events[cursor++]!
send(event)
if (event.type === 'done' || event.type === 'error') {
clearInterval(interval)
Expand Down
2 changes: 1 addition & 1 deletion src/app/components/AvatarIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export default function AvatarIcon({ user }: { user: Pick<User, 'name' | 'image'
{user.image ? (
<Image src={user.image} alt={user.name} width={28} height={28} loading='eager' />
) : (
<span className='avatar-placeholder'>{user.name[0].toUpperCase()}</span>
<span className='avatar-placeholder'>{user.name[0]!.toUpperCase()}</span>
)}
</button>
)
Expand Down
2 changes: 1 addition & 1 deletion src/app/setup/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export async function saveSetupConfig(formData: FormData): Promise<ActionRespons
clientId: formData.get('clientId'),
clientSecret: formData.get('clientSecret'),
})
if (!parsed.success) return { error: parsed.error.issues[0].message }
if (!parsed.success) return { error: parsed.error.issues[0]!.message }

const { baseUrl, ...githubAuth } = parsed.data
cfg.baseUrl = baseUrl
Expand Down
2 changes: 1 addition & 1 deletion src/lib/server/collabServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ export class CollabServerHandle implements AsyncDisposable {
if (this.disposing) return this.disposing
this.disposing = (async () => {
if (this.starting) {
await this.starting!.catch(() => {})
await this.starting.catch(() => {})
if (this.proc) {
await new Promise<void>(resolve => {
this.proc!.once('close', () => {
Expand Down
4 changes: 2 additions & 2 deletions src/lib/server/dirToken.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,11 @@ export function verifySignedDirToken(token: string): string | null {
if (parts.length !== 3) return null
const [rootDirB64, exp, sig] = parts
const expected = hmac(`${rootDirB64}.${exp}`)
if (sig.length !== expected.length || !crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) {
if (sig!.length !== expected.length || !crypto.timingSafeEqual(Buffer.from(sig!), Buffer.from(expected))) {
return null
}
if (Number(exp) <= Date.now()) return null
return Buffer.from(rootDirB64, 'base64url').toString()
return Buffer.from(rootDirB64!, 'base64url').toString()
}

/** URL at which Nginx serves {@link relPath}, a path relative to {@link token}'s root. */
Expand Down
4 changes: 2 additions & 2 deletions src/lib/server/editorSessions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -247,8 +247,8 @@ export async function initEditorSessions() {
for (const servers of m['vscServers'].values()) {
for (const s of servers) Object.setPrototypeOf(s, VscodeServerHandle.prototype)
}
await m['mounts'].forEach(mount => Object.setPrototypeOf(mount, ProjectMountHandle.prototype))
await m['collabServers'].forEach(collab => Object.setPrototypeOf(collab, CollabServerHandle.prototype))
await m['mounts'].forEach(mount => Object.setPrototypeOf(mount, ProjectMountHandle.prototype) as unknown)
await m['collabServers'].forEach(collab => Object.setPrototypeOf(collab, CollabServerHandle.prototype) as unknown)
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/lib/server/seed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export function startSeed(leanVersion: string | undefined): ActionResponse<boole
if (!line) return
const m = PROGRESS_RE.exec(line)
if (m) {
seedState.events.push({ type: 'progress', step: parseInt(m[1]), total: parseInt(m[2]), label: m[3] })
seedState.events.push({ type: 'progress', step: parseInt(m[1]!), total: parseInt(m[2]!), label: m[3]! })
} else {
seedState.events.push({ type: 'log', line })
}
Expand Down
2 changes: 1 addition & 1 deletion src/lib/server/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export async function provisionUserHome(user: User): Promise<void> {

// Git reads `$HOME/.config/git/config` as the global config.
const name = user.displayName?.trim() || user.name
const email = user.email?.trim()
const email = user.email.trim()
const userBlock = ['[user]']
if (name) userBlock.push(`\tname = ${name}`)
if (email) userBlock.push(`\temail = ${email}`)
Expand Down
2 changes: 1 addition & 1 deletion src/lib/server/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export function serverAction<S extends z.ZodType, T = void>(
): (raw: z.input<S>) => Promise<ActionResponse<T>> {
return async raw => {
const parsed = schema.safeParse(raw)
if (!parsed.success) return { error: parsed.error.issues[0].message }
if (!parsed.success) return { error: parsed.error.issues[0]!.message }
return handler(parsed.data)
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { type NextRequest, NextResponse } from 'next/server'

import { getConfig } from './lib/server/config'

export async function proxy(request: NextRequest) {
export function proxy(request: NextRequest) {
const cfg = getConfig()
if (!cfg.isSetupComplete) {
const path = request.nextUrl.pathname
Expand Down
3 changes: 2 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
},
"noFallthroughCasesInSwitch": true,
"noImplicitOverride": true,
"noImplicitReturns": true
"noImplicitReturns": true,
"noUncheckedIndexedAccess": true
},
// Subfolders of `.next/` duplicated since without that `next dev` tries to rewrite this config.
"include": [
Expand Down
2 changes: 1 addition & 1 deletion vscode-workbench/src/collabServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ export async function connectToCollabServer(
}
const color = AWARENESS_CURSOR_COLORS.reduce(
(acc, c) => (counts.get(acc)! <= counts.get(c)! ? acc : c),
AWARENESS_CURSOR_COLORS[0],
AWARENESS_CURSOR_COLORS[0]!,
)

awarenessProvider.setAwarenessField(AWARENESS_USER_KEY, {
Expand Down
2 changes: 1 addition & 1 deletion vscode-workbench/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ async function ensureProjectFolderOpen(mdata: WorkspaceMetadata, log: vs.LogOutp
const expected = bwrapProjectDir(mdata.project.name)
if (
vs.workspace.workspaceFolders?.length === 1 &&
path.resolve(vs.workspace.workspaceFolders[0].uri.fsPath) === path.resolve(expected)
path.resolve(vs.workspace.workspaceFolders[0]!.uri.fsPath) === path.resolve(expected)
)
return true

Expand Down
2 changes: 1 addition & 1 deletion vscode-workbench/src/panel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export class WorkbenchPanelProvider implements vs.TreeDataProvider<PanelItem>, v
if (!sels) continue
filePath = sels.filePath
if (0 < sels.selections.length) {
const active = sels.selections[0].active
const active = sels.selections[0]!.active
const pos = new vs.Position(active.line, active.character)
sel = new vs.Selection(pos, pos)
break
Expand Down
4 changes: 2 additions & 2 deletions vscode-workbench/src/remoteSelections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,10 @@ export class RemoteSelectionDecorator implements vs.Disposable {
const decos = this.decorationsFor(clientId, user.color)
const beforeRanges: vs.DecorationOptions[] = []
const afterRanges: vs.DecorationOptions[] = []
if (selection?.filePath === filePath) {
if (selection.filePath === filePath) {
for (const s of selection.selections) {
const range = new vs.Range(s.anchor.line, s.anchor.character, s.active.line, s.active.character)
const opts = { range, hoverMessage: user?.name }
const opts = { range, hoverMessage: user.name }
// Is `active` at the start or the end of the selection?
if (
s.active.line < s.anchor.line ||
Expand Down
Loading