Skip to content

Commit ea1bcbe

Browse files
skulidropekclaude
andcommitted
fix(lint): resolve remaining eslint-plugin-unicorn@67 violations (526 manual)
Adopt unicorn 67 fully across container/lib/terminal/app (api had none). All behavior-preserving: - no-unsafe-string-replacement: wrap template placeholder substitutions in () => value arrow replacers (also avoids $-pattern interpretation) - consistent-boolean-name: prefix booleans with is/has/can/should/did/was; wire/contract keys preserved, only local identifiers renamed - max-nested-calls: extract innermost calls into named consts - no-non-function-verb-prefix: rename non-function values to nouns - prefer-await conflicts with the project's effect-ts no-async rule -> rewritten Effect-natively (Effect.ensuring/tap/tryPromise), no async/await introduced - assorted: minimal-ternary, number-is-safe-integer, url-href, etc. Verified: all packages build + typecheck + lint(0) + lint:effect + tests green (container 66, terminal 150, lib 227, api 189, app 449). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent a304bc9 commit ea1bcbe

216 files changed

Lines changed: 1504 additions & 1390 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

packages/app/src/docker-git/api-client-auth.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ import { Effect } from "effect"
55
import {
66
authStreamMarkerExitCode,
77
type AuthStreamMarkers,
8-
authStreamSucceeded,
98
codexLoginFailureMessage,
109
codexLoginStreamMarkers,
10+
didAuthStreamSucceed,
1111
githubLoginFailureMessage,
1212
githubLoginStreamMarkers,
1313
gitlabLoginFailureMessage,
@@ -62,14 +62,14 @@ const requestMarkedAuthStream = (
6262
const output = yield* _(requestTextStream("POST", path, body, writer.writeChunk))
6363
writer.flushVisiblePending()
6464

65-
if (authStreamSucceeded(output, markers)) {
65+
if (didAuthStreamSucceed(output, markers)) {
6666
return output
6767
}
6868

69+
const exitCode = authStreamMarkerExitCode(output, markers)
70+
const message = failureMessage(output, exitCode)
6971
return yield* _(
70-
Effect.fail<ApiRequestError>(
71-
streamFailure("POST", path, failureMessage(output, authStreamMarkerExitCode(output, markers)))
72-
)
72+
Effect.fail<ApiRequestError>(streamFailure("POST", path, message))
7373
)
7474
})
7575

packages/app/src/docker-git/api-client-create.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@ export const buildCreateProjectRequest = (
2525
containerName: config.containerName,
2626
serviceName: config.serviceName,
2727
volumeName: config.volumeName,
28-
authorizedKeysPath: resolvedPaths.authorizedKeysContents === undefined
29-
? resolvedPaths.authorizedKeysPath
30-
: defaultTemplateConfig.authorizedKeysPath,
28+
authorizedKeysPath: (resolvedPaths.authorizedKeysContents === undefined
29+
? resolvedPaths
30+
: defaultTemplateConfig).authorizedKeysPath,
3131
authorizedKeysContents: resolvedPaths.authorizedKeysContents,
3232
envGlobalPath: config.envGlobalPath,
3333
envProjectPath: config.envProjectPath,

packages/app/src/docker-git/api-client-events.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -206,15 +206,14 @@ export const waitForProjectCreation = (
206206
export const startProjectEventPolling = (projectId: string, initialCursor: number) =>
207207
Effect.gen(function*(_) {
208208
const cursorRef = yield* _(Ref.make(initialCursor))
209+
const pollSchedule = Schedule.addDelay(
210+
Schedule.forever,
211+
() => projectEventPollInterval
212+
)
209213
const fiber = yield* _(
210214
pollProjectEventsOnce(projectId, cursorRef).pipe(
211215
Effect.ignore,
212-
Effect.repeat(
213-
Schedule.addDelay(
214-
Schedule.forever,
215-
() => projectEventPollInterval
216-
)
217-
),
216+
Effect.repeat(pollSchedule),
218217
Effect.fork
219218
)
220219
)

packages/app/src/docker-git/api-client.ts

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -60,17 +60,15 @@ const projectPath = (projectId: string, suffix = ""): string => `/projects/${enc
6060

6161
const decodeProjectResponse = (payload: JsonValue) => {
6262
const object = asObject(payload)
63-
return object === null
64-
? decodeProjectDetails(payload)
65-
: decodeProjectDetails(object["project"] ?? payload)
63+
return decodeProjectDetails(object === null ? payload : object["project"] ?? payload)
6664
}
6765

6866
const decodeProjectsResponse = <A>(
6967
payload: JsonValue,
7068
decode: (value: JsonValue) => A | null
7169
): ReadonlyArray<A> => {
7270
const object = asObject(payload)
73-
const items = object === null ? asArray(payload) : asArray(object["projects"])
71+
const items = asArray(object === null ? payload : object["projects"])
7472
return items
7573
.map((item) => decode(item))
7674
.filter((value): value is A => value !== null)
@@ -93,8 +91,8 @@ const createProjectAsync = (
9391
resolvedPaths: ResolvedCreateRequestPaths
9492
) =>
9593
Effect.gen(function*(_) {
96-
const createRequest = buildCreateProjectRequest(command, resolvedPaths, { async: true })
97-
const payload = yield* _(request("POST", "/projects", createRequest))
94+
const requestBody = buildCreateProjectRequest(command, resolvedPaths, { async: true })
95+
const payload = yield* _(request("POST", "/projects", requestBody))
9896
const accepted = decodeCreateProjectAccepted(payload)
9997
if (accepted === null) {
10098
return yield* _(Effect.fail(invalidCreateAcceptedResponse()))
@@ -141,8 +139,8 @@ const createProjectWithResolvedPaths = (
141139
return yield* _(createProjectAsync(command, resolvedPaths))
142140
}
143141

144-
const createRequest = buildCreateProjectRequest(command, resolvedPaths)
145-
const projectId = asString(createRequest.outDir)
142+
const requestBody = buildCreateProjectRequest(command, resolvedPaths)
143+
const projectId = asString(requestBody.outDir)
146144
const initialCursor = projectId === null
147145
? null
148146
: yield* _(
@@ -153,13 +151,12 @@ const createProjectWithResolvedPaths = (
153151
const eventPolling = projectId === null || initialCursor === null
154152
? null
155153
: yield* _(startProjectEventPolling(projectId, initialCursor))
154+
const ensureStopped = eventPolling === null
155+
? Effect.void
156+
: stopProjectEventPolling(eventPolling)
156157
const payload = yield* _(
157-
request("POST", "/projects", createRequest).pipe(
158-
Effect.ensuring(
159-
eventPolling === null
160-
? Effect.void
161-
: stopProjectEventPolling(eventPolling)
162-
)
158+
request("POST", "/projects", requestBody).pipe(
159+
Effect.ensuring(ensureStopped)
163160
)
164161
)
165162
return decodeProjectResponse(payload)
@@ -176,10 +173,9 @@ const withProjectEventPolling = <A, E, R>(
176173
)
177174
)
178175
const eventPolling = yield* _(startProjectEventPolling(projectId, initialCursor))
176+
const ensureStopped = stopProjectEventPolling(eventPolling)
179177
return yield* _(
180-
effect.pipe(
181-
Effect.ensuring(stopProjectEventPolling(eventPolling))
182-
)
178+
effect.pipe(Effect.ensuring(ensureStopped))
183179
)
184180
})
185181

@@ -236,8 +232,8 @@ export const readProjectLogs = (projectId: string) =>
236232
Effect.map((payload) => readProjectOutput(payload))
237233
)
238234

239-
export const readContainerTaskSnapshot = (projectId: string, includeDefault: boolean) =>
240-
request("GET", projectPath(projectId, `/tasks${includeDefault ? "?includeDefault=true" : ""}`)).pipe(
235+
export const readContainerTaskSnapshot = (projectId: string, shouldIncludeDefault: boolean) =>
236+
request("GET", projectPath(projectId, `/tasks${shouldIncludeDefault ? "?includeDefault=true" : ""}`)).pipe(
241237
Effect.map((payload) => decodeContainerTaskSnapshot(payload))
242238
)
243239

@@ -275,7 +271,8 @@ export const createAuthTerminalSession = (
275271

276272
export const deleteTerminalSessionByPath = (path: string) => requestVoid("DELETE", path)
277273

278-
export const applyAllProjects = (activeOnly: boolean) => requestVoid("POST", "/projects/apply-all", { activeOnly })
274+
export const applyAllProjects = (isActiveOnly: boolean) =>
275+
requestVoid("POST", "/projects/apply-all", { activeOnly: isActiveOnly })
279276

280277
export const downAllProjects = () => requestVoid("POST", "/projects/down-all")
281278

packages/app/src/docker-git/api-json.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,12 @@ export const asString = (value: JsonValue | undefined): string | null => typeof
4343
const renderGithubStatusLine = (entry: JsonObject): string | null => {
4444
const label = asString(entry["label"])
4545
const status = asString(entry["status"])
46-
const login = asString(entry["login"])
4746
if (label === null || status === null) {
4847
return null
4948
}
5049

50+
const login = asString(entry["login"])
51+
5152
if (status === "valid") {
5253
return login === null
5354
? `- ${label}: valid (owner unavailable)`

packages/app/src/docker-git/browser-frontend.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -390,7 +390,7 @@ export const runBrowserFrontendCommandWithOptions = (
390390
if (!decision.shouldStartWeb) {
391391
return Effect.log(`docker-git browser frontend is already running at http://${decision.host}:${decision.port}/`)
392392
}
393-
return options.daemon ? runBrowserFrontendDaemon(decision) : runBrowserFrontend(decision)
393+
return (options.daemon ? runBrowserFrontendDaemon : runBrowserFrontend)(decision)
394394
})
395395
)
396396

packages/app/src/docker-git/cli/parser-scrap.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,13 @@ const makeScrapExportCommand = (projectDir: string, archivePath: string, mode: "
4848
const makeScrapImportCommand = (
4949
projectDir: string,
5050
archivePath: string,
51-
wipe: boolean,
51+
shouldWipe: boolean,
5252
mode: "session"
5353
): Command => ({
5454
_tag: "ScrapImport",
5555
projectDir,
5656
archivePath,
57-
wipe,
57+
wipe: shouldWipe,
5858
mode
5959
})
6060

packages/app/src/docker-git/cli/parser-shared.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ export const parsePositiveInt = (
6262
option: string,
6363
raw: string
6464
): Either.Either<number, ParseError> => {
65-
const value = Number.parseInt(raw, 10)
65+
const value = Math.trunc(Number(raw))
6666
if (!Number.isFinite(value) || value <= 0) {
6767
const error: ParseError = {
6868
_tag: "InvalidOption",

packages/app/src/docker-git/controller-docker-diagnostics.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export type DockerProbeOutcome = {
1717

1818
const lowercase = (text: string): string => text.toLowerCase()
1919

20-
const containsAny = (haystack: string, needles: ReadonlyArray<string>): boolean =>
20+
const hasAny = (haystack: string, needles: ReadonlyArray<string>): boolean =>
2121
needles.some((needle) => haystack.includes(needle))
2222

2323
const isCliMissingExitCode = (exitCode: number): boolean => exitCode === 127
@@ -44,15 +44,15 @@ const daemonDownMarkers: ReadonlyArray<string> = [
4444
export const classifyDockerProbeFailure = (outcome: DockerProbeOutcome): DockerProbeFailureKind => {
4545
const normalized = lowercase(outcome.stderr)
4646

47-
if (containsAny(normalized, permissionMarkers)) {
47+
if (hasAny(normalized, permissionMarkers)) {
4848
return "socket-permission-denied"
4949
}
5050

51-
if (isCliMissingExitCode(outcome.exitCode) && containsAny(normalized, cliMissingMarkers)) {
51+
if (isCliMissingExitCode(outcome.exitCode) && hasAny(normalized, cliMissingMarkers)) {
5252
return "docker-cli-missing"
5353
}
5454

55-
if (containsAny(normalized, daemonDownMarkers)) {
55+
if (hasAny(normalized, daemonDownMarkers)) {
5656
return "daemon-unreachable"
5757
}
5858

packages/app/src/docker-git/controller-docker.ts

Lines changed: 23 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -107,17 +107,14 @@ export const resolveDockerCommand = (): Effect.Effect<
107107
}
108108

109109
const dockerHostRaw = process.env["DOCKER_HOST"]?.trim() ?? ""
110+
const accessDeniedMessage = renderDockerAccessDeniedMessage({
111+
directProbe,
112+
sudoProbe,
113+
apiBaseUrl: resolveConfiguredApiBaseUrl(),
114+
dockerHost: dockerHostRaw.length > 0 ? dockerHostRaw : null
115+
})
110116
return yield* _(
111-
Effect.fail(
112-
controllerBootstrapError(
113-
renderDockerAccessDeniedMessage({
114-
directProbe,
115-
sudoProbe,
116-
apiBaseUrl: resolveConfiguredApiBaseUrl(),
117-
dockerHost: dockerHostRaw.length > 0 ? dockerHostRaw : null
118-
})
119-
)
120-
)
117+
Effect.fail(controllerBootstrapError(accessDeniedMessage))
121118
)
122119
})
123120

@@ -256,7 +253,7 @@ const mapDockerCaptureError =
256253
// QUOTE(ТЗ): "комментарии ребита надо было тоже поддержать"
257254
// REF: CodeRabbit PR #344 review 4349265315
258255
// SOURCE: n/a
259-
// FORMAT THEOREM: includeOutput -> failure_with_output; !includeOutput -> base_failure
256+
// FORMAT THEOREM: shouldIncludeOutput -> failure_with_output; !shouldIncludeOutput -> base_failure
260257
// PURITY: CORE
261258
// EFFECT: n/a
262259
// INVARIANT: both modes preserve headline, command and exit code
@@ -268,14 +265,14 @@ const mapDockerCaptureError =
268265
* @param invocation - Resolved Docker invocation.
269266
* @param exitCode - Process exit code.
270267
* @param output - Combined stdout/stderr from the process.
271-
* @param includeOutput - Whether the message should include captured process output.
268+
* @param shouldIncludeOutput - Whether the message should include captured process output.
272269
* @returns Stable Docker failure message.
273270
*
274271
* @pure true
275272
* @effect n/a
276273
* @invariant Base diagnostics always include command and exit code.
277274
* @precondition `exitCode` is the observed process exit code.
278-
* @postcondition Captured output appears only when `includeOutput` is true and output is non-empty.
275+
* @postcondition Captured output appears only when `shouldIncludeOutput` is true and output is non-empty.
279276
* @complexity O(n) where n = |output|.
280277
* @throws Never
281278
*/
@@ -284,9 +281,9 @@ const formatDockerCaptureFailure = (
284281
invocation: DockerInvocation,
285282
exitCode: number,
286283
output: string,
287-
includeOutput: boolean
284+
shouldIncludeOutput: boolean
288285
): string =>
289-
includeOutput
286+
shouldIncludeOutput
290287
? formatDockerInvocationFailureWithOutput(`${label} failed.`, invocation, exitCode, output)
291288
: formatDockerInvocationFailure(`${label} failed.`, invocation, exitCode)
292289

@@ -305,7 +302,7 @@ const formatDockerCaptureFailure = (
305302
*
306303
* @param args - Docker CLI arguments after the executable.
307304
* @param label - Operation label used in diagnostics.
308-
* @param includeOutput - Whether non-zero exit diagnostics include captured stdout/stderr.
305+
* @param shouldIncludeOutput - Whether non-zero exit diagnostics include captured stdout/stderr.
309306
* @returns Effect containing stdout on success.
310307
*
311308
* @pure false
@@ -319,7 +316,7 @@ const formatDockerCaptureFailure = (
319316
const runDockerCaptureWithOutputMode = (
320317
args: ReadonlyArray<string>,
321318
label: string,
322-
includeOutput: boolean
319+
shouldIncludeOutput: boolean
323320
): Effect.Effect<string, ControllerBootstrapError, ControllerRuntime> =>
324321
resolveDockerInvocation(args).pipe(
325322
Effect.flatMap((invocation) =>
@@ -331,7 +328,7 @@ const runDockerCaptureWithOutputMode = (
331328
},
332329
[0],
333330
(exitCode, output) =>
334-
controllerBootstrapError(formatDockerCaptureFailure(label, invocation, exitCode, output, includeOutput))
331+
controllerBootstrapError(formatDockerCaptureFailure(label, invocation, exitCode, output, shouldIncludeOutput))
335332
)
336333
),
337334
Effect.mapError(mapDockerCaptureError(label))
@@ -379,10 +376,11 @@ export const runCompose = (
379376
): Effect.Effect<void, ControllerBootstrapError, ControllerRuntime> =>
380377
Effect.gen(function*(_) {
381378
const dockerCommand = yield* _(resolveDockerCommand())
379+
const composeFiles = yield* _(ControllerCompose.resolveControllerComposeFiles())
382380
const invocation = buildDockerInvocation(dockerCommand, [
383381
"compose",
384382
...ControllerCompose.controllerComposeProjectArgs,
385-
...ControllerCompose.composeFilesToArgs(yield* _(ControllerCompose.resolveControllerComposeFiles())),
383+
...ControllerCompose.composeFilesToArgs(composeFiles),
386384
...args
387385
])
388386
const exitCode = yield* _(
@@ -402,12 +400,13 @@ export const runCompose = (
402400
return
403401
}
404402

403+
const failureMessage = formatDockerInvocationFailure(
404+
"Failed to start docker-git controller.",
405+
invocation,
406+
exitCode
407+
)
405408
return yield* _(
406-
Effect.fail(
407-
controllerBootstrapError(
408-
formatDockerInvocationFailure("Failed to start docker-git controller.", invocation, exitCode)
409-
)
410-
)
409+
Effect.fail(controllerBootstrapError(failureMessage))
411410
)
412411
})
413412

0 commit comments

Comments
 (0)