diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 827344f1f96..00000000000 --- a/.eslintignore +++ /dev/null @@ -1,4 +0,0 @@ -*/**.js -*/**.d.ts -packages/*/dist -packages/*/lib \ No newline at end of file diff --git a/.github/labeler.yml b/.github/labeler.yml index 279bab91a79..6760c37bcd0 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -9,4 +9,4 @@ - any: ["**/*.md"] "πŸ“Œ area: ci": - - any: [".github/**/*"] \ No newline at end of file + - any: [".github/**/*"] diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml new file mode 100644 index 00000000000..6fd2f73b4ab --- /dev/null +++ b/.github/workflows/code-quality.yml @@ -0,0 +1,38 @@ +name: "🎨 Format & Lint" + +on: + workflow_call: + +permissions: + contents: read + +jobs: + code-quality: + runs-on: ubuntu-latest + + steps: + - name: ⬇️ Checkout repo + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + persist-credentials: false + + - name: βŽ” Setup pnpm + uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0 + with: + version: 10.33.2 + + - name: βŽ” Setup node + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + with: + node-version: 20.20.2 + cache: "pnpm" + + - name: πŸ“₯ Download deps + run: pnpm install --frozen-lockfile + + - name: πŸ’… Check formatting + run: pnpm exec oxfmt --check . + + - name: πŸ”Ž Lint + run: pnpm exec oxlint . diff --git a/.github/workflows/dependabot-critical-alerts.yml b/.github/workflows/dependabot-critical-alerts.yml index e69e9c0af7b..9a61387324d 100644 --- a/.github/workflows/dependabot-critical-alerts.yml +++ b/.github/workflows/dependabot-critical-alerts.yml @@ -2,7 +2,7 @@ name: Dependabot Critical Alerts on: schedule: - - cron: "0 8 * * *" # Daily 08:00 UTC + - cron: "0 8 * * *" # Daily 08:00 UTC workflow_dispatch: inputs: severity: diff --git a/.github/workflows/dependabot-weekly-summary.yml b/.github/workflows/dependabot-weekly-summary.yml index 43463b488e4..d7b489d55da 100644 --- a/.github/workflows/dependabot-weekly-summary.yml +++ b/.github/workflows/dependabot-weekly-summary.yml @@ -2,7 +2,7 @@ name: Dependabot Weekly Summary on: schedule: - - cron: "0 8 * * 1" # Mon 08:00 UTC + - cron: "0 8 * * 1" # Mon 08:00 UTC workflow_dispatch: # Single-purpose monitoring workflow; serialise on workflow name only - we never @@ -12,9 +12,9 @@ concurrency: cancel-in-progress: false permissions: - contents: read # gh CLI baseline - pull-requests: read # gh pr list (open dependabot PRs) - actions: read # gh run list / view (parse latest dependabot run logs) + contents: read # gh CLI baseline + pull-requests: read # gh pr list (open dependabot PRs) + actions: read # gh run list / view (parse latest dependabot run logs) jobs: summary: diff --git a/.github/workflows/pr_checks.yml b/.github/workflows/pr_checks.yml index 95805539807..1fb3a47a45c 100644 --- a/.github/workflows/pr_checks.yml +++ b/.github/workflows/pr_checks.yml @@ -102,6 +102,11 @@ jobs: - 'pnpm-workspace.yaml' - 'turbo.json' + code-quality: + needs: changes + if: needs.changes.outputs.code == 'true' + uses: ./.github/workflows/code-quality.yml + typecheck: needs: changes if: needs.changes.outputs.code == 'true' || needs.changes.outputs.typecheck_self == 'true' @@ -155,6 +160,7 @@ jobs: name: All PR Checks needs: - changes + - code-quality - typecheck - webapp - e2e-webapp diff --git a/.github/workflows/release-helm.yml b/.github/workflows/release-helm.yml index 6dbfee7d0c8..d21c62de8be 100644 --- a/.github/workflows/release-helm.yml +++ b/.github/workflows/release-helm.yml @@ -3,17 +3,17 @@ name: 🧭 Helm Chart Release on: push: tags: - - 'helm-v*' + - "helm-v*" workflow_call: inputs: chart_version: - description: 'Chart version to release' + description: "Chart version to release" required: true type: string workflow_dispatch: inputs: chart_version: - description: 'Chart version to release' + description: "Chart version to release" required: true type: string @@ -58,7 +58,7 @@ jobs: - name: Validate manifests uses: docker://ghcr.io/yannh/kubeconform:v0.7.0@sha256:85dbef6b4b312b99133decc9c6fc9495e9fc5f92293d4ff3b7e1b30f5611823c with: - entrypoint: '/kubeconform' + entrypoint: "/kubeconform" args: "-summary -output json ./helm-output" release: @@ -134,7 +134,7 @@ jobs: run: | VERSION="${STEPS_VERSION_OUTPUTS_VERSION}" CHART_PACKAGE="/tmp/${{ env.CHART_NAME }}-${VERSION}.tgz" - + # Push to GHCR OCI registry helm push "$CHART_PACKAGE" "oci://${{ env.REGISTRY }}/${{ github.repository_owner }}/charts" env: @@ -153,7 +153,7 @@ jobs: oci://${{ env.REGISTRY }}/${{ github.repository_owner }}/charts/${{ env.CHART_NAME }} \ --version "${{ steps.version.outputs.version }}" ``` - + ### Changes See commit history for detailed changes in this release. files: | diff --git a/.github/workflows/workflow-checks.yml b/.github/workflows/workflow-checks.yml index e99a4d33427..62406671fc5 100644 --- a/.github/workflows/workflow-checks.yml +++ b/.github/workflows/workflow-checks.yml @@ -4,14 +4,14 @@ on: push: branches: [main] paths: - - '.github/workflows/**' - - '.github/actions/**' - - '.github/zizmor.yml' + - ".github/workflows/**" + - ".github/actions/**" + - ".github/zizmor.yml" pull_request: paths: - - '.github/workflows/**' - - '.github/actions/**' - - '.github/zizmor.yml' + - ".github/workflows/**" + - ".github/actions/**" + - ".github/zizmor.yml" permissions: {} diff --git a/.github/zizmor.yml b/.github/zizmor.yml index 2fcbb540127..e7920e8cf17 100644 --- a/.github/zizmor.yml +++ b/.github/zizmor.yml @@ -2,4 +2,4 @@ rules: unpinned-uses: config: policies: - '*': hash-pin + "*": hash-pin diff --git a/.oxfmtrc.json b/.oxfmtrc.json new file mode 100644 index 00000000000..71809f50ef4 --- /dev/null +++ b/.oxfmtrc.json @@ -0,0 +1,30 @@ +{ + "$schema": "./node_modules/oxfmt/configuration_schema.json", + "semi": true, + "singleQuote": false, + "jsxSingleQuote": false, + "trailingComma": "es5", + "bracketSpacing": true, + "bracketSameLine": false, + "printWidth": 100, + "tabWidth": 2, + "useTabs": false, + "sortPackageJson": false, + "ignorePatterns": [ + "node_modules", + ".env", + ".env.local", + "pnpm-lock.yaml", + "tailwind.css", + ".babelrc.json", + "**/.react-email/", + "**/storybook-static/", + "**/.changeset/", + "**/dist/", + "internal-packages/tsql/src/grammar/", + // Maybe turn these on in the future + "**/*.yaml", + "**/*.mdx", + "**/*.md" + ] +} diff --git a/.oxlintrc.json b/.oxlintrc.json new file mode 100644 index 00000000000..99babf86a17 --- /dev/null +++ b/.oxlintrc.json @@ -0,0 +1,46 @@ +{ + "$schema": "./node_modules/oxlint/configuration_schema.json", + "plugins": ["typescript", "import", "react"], + "ignorePatterns": [ + "**/dist/**", + "**/build/**", + "**/*.d.ts", + "**/seed.js", + "**/seedCloud.ts", + "**/populate.js" + ], + // All rules below are disabled because they currently fire on the existing + // codebase (1748 warnings across these 20 rules as of this commit). Disabled + // to keep the lint baseline green; re-enable and fix incrementally. + "rules": { + // eslint + "no-unused-vars": "off", + "no-unused-expressions": "off", + "no-control-regex": "off", + "no-empty-pattern": "off", + "no-unused-private-class-members": "off", + "no-useless-catch": "off", + "no-unsafe-optional-chaining": "off", + "no-unreachable": "off", + "require-yield": "off", + "no-async-promise-executor": "off", + "no-unsafe-finally": "off", + "no-useless-escape": "off", + // typescript + "typescript/consistent-type-imports": "off", + "typescript/no-this-alias": "off", + "typescript/no-non-null-asserted-optional-chain": "off", + "typescript/no-unnecessary-parameter-property-assignment": "off", + // import + "import/no-duplicates": "off", + "import/namespace": "off", + // react + "react/jsx-key": "off", + "react/no-children-prop": "off", + // react-hooks + "react-hooks/exhaustive-deps": "off", + // oxlint treats any use*()/use() call as a React hook, producing false + // positives in async/test code (testcontainers, cli-v3 e2e). + "react-hooks/rules-of-hooks": "off" + } +} diff --git a/.prettierignore b/.prettierignore deleted file mode 100644 index a34447dd45d..00000000000 --- a/.prettierignore +++ /dev/null @@ -1,10 +0,0 @@ -node_modules -.env -.env.local -pnpm-lock.yaml -tailwind.css -.babelrc.json -**/.react-email/ -**/storybook-static/ -**/.changeset/ -**/dist/ \ No newline at end of file diff --git a/apps/coordinator/package.json b/apps/coordinator/package.json index 3b4240bd37d..446606a44c8 100644 --- a/apps/coordinator/package.json +++ b/apps/coordinator/package.json @@ -27,4 +27,4 @@ "esbuild": "^0.19.11", "tsx": "^4.7.0" } -} \ No newline at end of file +} diff --git a/apps/docker-provider/package.json b/apps/docker-provider/package.json index f3e4015ef08..aa812c68d4a 100644 --- a/apps/docker-provider/package.json +++ b/apps/docker-provider/package.json @@ -24,4 +24,4 @@ "esbuild": "^0.19.11", "tsx": "^4.7.0" } -} \ No newline at end of file +} diff --git a/apps/kubernetes-provider/package.json b/apps/kubernetes-provider/package.json index 6cb26e2c70f..b4a4307abbb 100644 --- a/apps/kubernetes-provider/package.json +++ b/apps/kubernetes-provider/package.json @@ -25,4 +25,4 @@ "esbuild": "^0.19.11", "tsx": "^4.7.0" } -} \ No newline at end of file +} diff --git a/apps/supervisor/src/backpressure/k8sPodCountSignalSource.test.ts b/apps/supervisor/src/backpressure/k8sPodCountSignalSource.test.ts index 8857372a042..8c7104cfb14 100644 --- a/apps/supervisor/src/backpressure/k8sPodCountSignalSource.test.ts +++ b/apps/supervisor/src/backpressure/k8sPodCountSignalSource.test.ts @@ -73,9 +73,9 @@ describe("K8sPodCountSignalSource", () => { engageThreshold: 10000, releaseThreshold: 5000, }); - expect((await source.read()).engaged).toBe(true); // engage + expect((await source.read()).engaged).toBe(true); // engage count = 7000; - expect((await source.read()).engaged).toBe(true); // band -> still engaged + expect((await source.read()).engaged).toBe(true); // band -> still engaged count = 4999; expect((await source.read()).engaged).toBe(false); // below release -> off count = 7000; diff --git a/apps/supervisor/src/backpressure/redisBackpressureSignalSource.test.ts b/apps/supervisor/src/backpressure/redisBackpressureSignalSource.test.ts index 77a7457b13c..dc8040a4c5e 100644 --- a/apps/supervisor/src/backpressure/redisBackpressureSignalSource.test.ts +++ b/apps/supervisor/src/backpressure/redisBackpressureSignalSource.test.ts @@ -49,14 +49,17 @@ describe("RedisBackpressureSignalSource", () => { } }); - redisTest("returns null for valid JSON of the wrong shape (fail-open)", async ({ redisOptions }) => { - const redis = new Redis(redisOptions); - try { - await redis.set(KEY, JSON.stringify({ foo: "bar" })); - const source = new RedisBackpressureSignalSource(redis, KEY); - expect(await source.read()).toBeNull(); - } finally { - await redis.quit(); + redisTest( + "returns null for valid JSON of the wrong shape (fail-open)", + async ({ redisOptions }) => { + const redis = new Redis(redisOptions); + try { + await redis.set(KEY, JSON.stringify({ foo: "bar" })); + const source = new RedisBackpressureSignalSource(redis, KEY); + expect(await source.read()).toBeNull(); + } finally { + await redis.quit(); + } } - }); + ); }); diff --git a/apps/supervisor/src/env.ts b/apps/supervisor/src/env.ts index 10e8f9b62b3..2cb1bfb9a74 100644 --- a/apps/supervisor/src/env.ts +++ b/apps/supervisor/src/env.ts @@ -75,9 +75,21 @@ export const Env = z TRIGGER_DEQUEUE_BACKPRESSURE_REDIS_TLS_DISABLED: BoolEnv.default(false), TRIGGER_DEQUEUE_BACKPRESSURE_POD_COUNT_ENABLED: BoolEnv.default(false), TRIGGER_DEQUEUE_BACKPRESSURE_POD_COUNT_DRY_RUN: BoolEnv.default(true), - TRIGGER_DEQUEUE_BACKPRESSURE_POD_COUNT_ENGAGE: z.coerce.number().int().positive().default(10_000), - TRIGGER_DEQUEUE_BACKPRESSURE_POD_COUNT_RELEASE: z.coerce.number().int().positive().default(5_000), - TRIGGER_DEQUEUE_BACKPRESSURE_POD_COUNT_REFRESH_MS: z.coerce.number().int().positive().default(5_000), + TRIGGER_DEQUEUE_BACKPRESSURE_POD_COUNT_ENGAGE: z.coerce + .number() + .int() + .positive() + .default(10_000), + TRIGGER_DEQUEUE_BACKPRESSURE_POD_COUNT_RELEASE: z.coerce + .number() + .int() + .positive() + .default(5_000), + TRIGGER_DEQUEUE_BACKPRESSURE_POD_COUNT_REFRESH_MS: z.coerce + .number() + .int() + .positive() + .default(5_000), // Hard timeout on the apiserver /metrics scrape. A hung request would otherwise // never settle and freeze the monitor's refresh loop (fail-open silently). TRIGGER_DEQUEUE_BACKPRESSURE_POD_COUNT_SCRAPE_TIMEOUT_MS: z.coerce @@ -350,7 +362,10 @@ export const Env = z path: ["TRIGGER_WORKLOAD_API_DOMAIN"], }); } - if (data.TRIGGER_DEQUEUE_BACKPRESSURE_ENABLED && !data.TRIGGER_DEQUEUE_BACKPRESSURE_REDIS_HOST) { + if ( + data.TRIGGER_DEQUEUE_BACKPRESSURE_ENABLED && + !data.TRIGGER_DEQUEUE_BACKPRESSURE_REDIS_HOST + ) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: diff --git a/apps/supervisor/src/index.ts b/apps/supervisor/src/index.ts index d2bc82c13b9..833448ad873 100644 --- a/apps/supervisor/src/index.ts +++ b/apps/supervisor/src/index.ts @@ -274,7 +274,10 @@ class ManagedSupervisor { rampMs: env.TRIGGER_DEQUEUE_BACKPRESSURE_RAMP_MS, dryRun: env.TRIGGER_DEQUEUE_BACKPRESSURE_POD_COUNT_DRY_RUN, logger: this.logger, - metrics: new BackpressureMetrics({ register, prefix: "supervisor_backpressure_pod_count" }), + metrics: new BackpressureMetrics({ + register, + prefix: "supervisor_backpressure_pod_count", + }), }) ); this.logger.log("πŸ›‘ Dequeue backpressure enabled (pod-count source)", { diff --git a/apps/supervisor/src/services/computeSnapshotService.test.ts b/apps/supervisor/src/services/computeSnapshotService.test.ts index b039b63bd4d..44a13a4bdd3 100644 --- a/apps/supervisor/src/services/computeSnapshotService.test.ts +++ b/apps/supervisor/src/services/computeSnapshotService.test.ts @@ -10,7 +10,9 @@ const DELAY_MS = 200; const SETTLE_MS = 600; function createService() { - const snapshot = vi.fn(async (_opts: { runnerId: string; metadata: Record }) => true); + const snapshot = vi.fn( + async (_opts: { runnerId: string; metadata: Record }) => true + ); const computeManager = { snapshotDelayMs: DELAY_MS, @@ -97,9 +99,7 @@ describe("ComputeSnapshotService", () => { expect(service.cancel("run_1", "runner-b")).toBe(false); await vi.waitFor(() => expect(snapshot).toHaveBeenCalledTimes(1), { timeout: 2_000 }); - expect(snapshot).toHaveBeenCalledWith( - expect.objectContaining({ runnerId: "runner-a" }) - ); + expect(snapshot).toHaveBeenCalledWith(expect.objectContaining({ runnerId: "runner-a" })); } finally { service.stop(); } diff --git a/apps/supervisor/src/services/otlpTraceService.test.ts b/apps/supervisor/src/services/otlpTraceService.test.ts index baf3bd90306..95053021ca1 100644 --- a/apps/supervisor/src/services/otlpTraceService.test.ts +++ b/apps/supervisor/src/services/otlpTraceService.test.ts @@ -31,9 +31,7 @@ describe("buildPayload", () => { expect(triggerAttr).toEqual({ key: "$trigger", value: { boolValue: true } }); // Resource attributes - const envAttr = resourceSpan.resource.attributes.find( - (a) => a.key === "ctx.environment.id" - ); + const envAttr = resourceSpan.resource.attributes.find((a) => a.key === "ctx.environment.id"); expect(envAttr).toEqual({ key: "ctx.environment.id", value: { stringValue: "env_123" }, diff --git a/apps/supervisor/src/services/warmStartVerificationService.test.ts b/apps/supervisor/src/services/warmStartVerificationService.test.ts index 994c678b4b8..4a5f58875c2 100644 --- a/apps/supervisor/src/services/warmStartVerificationService.test.ts +++ b/apps/supervisor/src/services/warmStartVerificationService.test.ts @@ -19,10 +19,7 @@ function makeMessage(runFriendlyId = "run_1"): DequeuedMessage { } as unknown as DequeuedMessage; } -function createService(opts: { - latestSnapshotId?: string; - probeError?: boolean; -}) { +function createService(opts: { latestSnapshotId?: string; probeError?: boolean }) { const getLatestSnapshot = vi.fn(async (_runId: string) => opts.probeError ? { success: false as const, error: "connection refused" } diff --git a/apps/supervisor/src/wideEvents/index.ts b/apps/supervisor/src/wideEvents/index.ts index 4eda429a50a..6e61d85896f 100644 --- a/apps/supervisor/src/wideEvents/index.ts +++ b/apps/supervisor/src/wideEvents/index.ts @@ -11,12 +11,7 @@ export { type Env, isValidRequestId, newState, type NewStateOptions } from "./ne export { emit, EmitMessage } from "./emit.js"; export { parseTraceId } from "./traceparent.js"; export { fromContext, wideEventStorage } from "./context.js"; -export { - type PhaseOpt, - recordPhase, - recordPhaseSince, - timePhase, -} from "./record.js"; +export { type PhaseOpt, recordPhase, recordPhaseSince, timePhase } from "./record.js"; export { emitOneShot, runWideEvent, diff --git a/apps/supervisor/src/wideEvents/middleware.test.ts b/apps/supervisor/src/wideEvents/middleware.test.ts index afb59f43d6e..a431df4ada5 100644 --- a/apps/supervisor/src/wideEvents/middleware.test.ts +++ b/apps/supervisor/src/wideEvents/middleware.test.ts @@ -124,7 +124,8 @@ describe("runWideEvent", () => { { service: "supervisor", env: {}, - enabled: true, op: "test", + enabled: true, + op: "test", setup: (state) => { state.meta.run_id = "run_abc"; state.extras.iteration = "dequeue"; @@ -158,16 +159,13 @@ describe("runWideEvent", () => { const lines = await captureStdout(async () => { await Promise.all( ["a", "b", "c"].map((tag) => - runWideEvent( - { service: "supervisor", env: {}, enabled: true, op: "test" }, - async () => { - const s = fromContext(); - if (!s) throw new Error("no state"); - s.meta.tag = tag; - await new Promise((r) => setTimeout(r, 5)); - expect(s.meta.tag).toBe(tag); - } - ) + runWideEvent({ service: "supervisor", env: {}, enabled: true, op: "test" }, async () => { + const s = fromContext(); + if (!s) throw new Error("no state"); + s.meta.tag = tag; + await new Promise((r) => setTimeout(r, 5)); + expect(s.meta.tag).toBe(tag); + }) ) ); }); @@ -182,7 +180,8 @@ describe("emitOneShot", () => { emitOneShot({ service: "supervisor", env: {}, - enabled: true, op: "test", + enabled: true, + op: "test", populate: (s) => { s.meta.run_id = "run_abc"; s.extras.event = "run:start"; diff --git a/apps/supervisor/src/workloadManager/compute.test.ts b/apps/supervisor/src/workloadManager/compute.test.ts index ea5ddabf285..95a9ca2f3b0 100644 --- a/apps/supervisor/src/workloadManager/compute.test.ts +++ b/apps/supervisor/src/workloadManager/compute.test.ts @@ -15,9 +15,7 @@ describe("runnerNameForAttempt", () => { describe("isRetryableCreateError", () => { it("retries statuses where the create definitely did not commit", () => { - expect(isRetryableCreateError(new ComputeClientError(500, "tap busy", "http://gw"))).toBe( - true - ); + expect(isRetryableCreateError(new ComputeClientError(500, "tap busy", "http://gw"))).toBe(true); expect(isRetryableCreateError(new ComputeClientError(503, "no placement", "http://gw"))).toBe( true ); diff --git a/apps/supervisor/src/workloadManager/compute.ts b/apps/supervisor/src/workloadManager/compute.ts index 7a4270513cf..857e129a83f 100644 --- a/apps/supervisor/src/workloadManager/compute.ts +++ b/apps/supervisor/src/workloadManager/compute.ts @@ -250,9 +250,7 @@ export class ComputeWorkloadManager implements WorkloadManager { // name registered, so subsequent attempts use a suffixed name. let suffixAttempts = false; for (; attempt <= this.createMaxAttempts; attempt++) { - const attemptRunnerId = suffixAttempts - ? runnerNameForAttempt(runnerId, attempt) - : runnerId; + const attemptRunnerId = suffixAttempts ? runnerNameForAttempt(runnerId, attempt) : runnerId; [error, data] = await tryCatch( this.compute.instances.create( attemptRunnerId === runnerId diff --git a/apps/supervisor/src/workloadManager/kubernetes.ts b/apps/supervisor/src/workloadManager/kubernetes.ts index 9e79fff76c2..762860104bd 100644 --- a/apps/supervisor/src/workloadManager/kubernetes.ts +++ b/apps/supervisor/src/workloadManager/kubernetes.ts @@ -431,7 +431,8 @@ export class KubernetesWorkloadManager implements WorkloadManager { // Only large machine affinity produces hard requirements (non-large runs must stay off the large pool). // Schedule affinity is soft both ways. const required = [ - ...(largeNodeAffinity?.requiredDuringSchedulingIgnoredDuringExecution?.nodeSelectorTerms ?? []), + ...(largeNodeAffinity?.requiredDuringSchedulingIgnoredDuringExecution?.nodeSelectorTerms ?? + []), ]; const hasNodeAffinity = preferred.length > 0 || required.length > 0; @@ -443,7 +444,9 @@ export class KubernetesWorkloadManager implements WorkloadManager { return { ...(hasNodeAffinity && { nodeAffinity: { - ...(preferred.length > 0 && { preferredDuringSchedulingIgnoredDuringExecution: preferred }), + ...(preferred.length > 0 && { + preferredDuringSchedulingIgnoredDuringExecution: preferred, + }), ...(required.length > 0 && { requiredDuringSchedulingIgnoredDuringExecution: { nodeSelectorTerms: required }, }), @@ -497,7 +500,10 @@ export class KubernetesWorkloadManager implements WorkloadManager { } #getScheduleNodeAffinityRules(isScheduledRun: boolean): k8s.V1NodeAffinity | undefined { - if (!env.KUBERNETES_SCHEDULED_RUN_AFFINITY_ENABLED || !env.KUBERNETES_SCHEDULED_RUN_AFFINITY_POOL_LABEL_VALUE) { + if ( + !env.KUBERNETES_SCHEDULED_RUN_AFFINITY_ENABLED || + !env.KUBERNETES_SCHEDULED_RUN_AFFINITY_POOL_LABEL_VALUE + ) { return undefined; } diff --git a/apps/supervisor/src/workloadManager/types.ts b/apps/supervisor/src/workloadManager/types.ts index 86199afe469..ee759cb8a79 100644 --- a/apps/supervisor/src/workloadManager/types.ts +++ b/apps/supervisor/src/workloadManager/types.ts @@ -1,4 +1,9 @@ -import type { EnvironmentType, MachinePreset, PlacementTag, RunAnnotations } from "@trigger.dev/core/v3"; +import type { + EnvironmentType, + MachinePreset, + PlacementTag, + RunAnnotations, +} from "@trigger.dev/core/v3"; export interface WorkloadManagerOptions { workloadApiProtocol: "http" | "https"; diff --git a/apps/supervisor/src/workloadServer/index.ts b/apps/supervisor/src/workloadServer/index.ts index 5dd1a79cc9a..7397313080a 100644 --- a/apps/supervisor/src/workloadServer/index.ts +++ b/apps/supervisor/src/workloadServer/index.ts @@ -317,9 +317,7 @@ export class WorkloadServer extends EventEmitter { return; } - reply.json( - completeResponse.data satisfies WorkloadRunAttemptCompleteResponseBody - ); + reply.json(completeResponse.data satisfies WorkloadRunAttemptCompleteResponseBody); return; } ), @@ -566,7 +564,9 @@ export class WorkloadServer extends EventEmitter { } reply.json( - dequeueResponse.data.map(legacifyCheckpointType) satisfies WorkloadDequeueFromVersionResponseBody + dequeueResponse.data.map( + legacifyCheckpointType + ) satisfies WorkloadDequeueFromVersionResponseBody ); } ), @@ -613,16 +613,22 @@ export class WorkloadServer extends EventEmitter { httpServer.route("/api/v1/compute/snapshot-complete", "POST", { bodySchema: SnapshotCallbackPayloadSchema, handler: async (ctx) => - this.wideRoute(ctx, "snapshot.callback", "/api/v1/compute/snapshot-complete", "POST", async () => { - const { reply, body } = ctx; - if (!this.snapshotService) { - reply.empty(404); - return; - } + this.wideRoute( + ctx, + "snapshot.callback", + "/api/v1/compute/snapshot-complete", + "POST", + async () => { + const { reply, body } = ctx; + if (!this.snapshotService) { + reply.empty(404); + return; + } - const result = await this.snapshotService.handleCallback(body); - reply.empty(result.status); - }), + const result = await this.snapshotService.handleCallback(body); + reply.empty(result.status); + } + ), }); return httpServer; diff --git a/apps/webapp/.eslintrc b/apps/webapp/.eslintrc deleted file mode 100644 index f292eef3cce..00000000000 --- a/apps/webapp/.eslintrc +++ /dev/null @@ -1,31 +0,0 @@ -{ - "plugins": ["react-hooks", "@typescript-eslint/eslint-plugin", "import"], - "parser": "@typescript-eslint/parser", - "overrides": [ - { - "files": ["*.ts", "*.tsx"], - "rules": { - // Autofixes imports from "@trigger.dev/core" to fine grained modules - // "@trigger.dev/no-trigger-core-import": "error", - // Normalize `import type {}` and `import { type }` - "@typescript-eslint/consistent-type-imports": [ - "warn", - { - // the "type" annotation can get tangled and cause syntax errors - // during some autofixes, so easier to just turn it off - "prefer": "type-imports", - "disallowTypeAnnotations": true, - "fixStyle": "inline-type-imports" - } - ], - // no-trigger-core-import splits imports into multiple lines - // this one merges them back into a single line - // if they still import from the same module - "import/no-duplicates": ["warn", { "prefer-inline": true }], - // lots of undeclared vars, enable this rule if you want to clean them up - "turbo/no-undeclared-env-vars": "off" - } - } - ], - "ignorePatterns": ["seed.js", "seedCloud.ts", "populate.js"] -} diff --git a/apps/webapp/.prettierignore b/apps/webapp/.prettierignore deleted file mode 100644 index 835d1a6cdd7..00000000000 --- a/apps/webapp/.prettierignore +++ /dev/null @@ -1,11 +0,0 @@ -node_modules - -/build -/public/build -.env - -/cypress/screenshots -/cypress/videos -/postgres-data - -/app/styles/tailwind.css \ No newline at end of file diff --git a/apps/webapp/app/assets/icons/AIPenIcon.tsx b/apps/webapp/app/assets/icons/AIPenIcon.tsx index 65f6fdb2f38..146edd0e6d2 100644 --- a/apps/webapp/app/assets/icons/AIPenIcon.tsx +++ b/apps/webapp/app/assets/icons/AIPenIcon.tsx @@ -19,13 +19,7 @@ export function AIPenIcon({ className }: { className?: string }) { stroke="currentColor" strokeWidth="2" /> - + ); } diff --git a/apps/webapp/app/assets/icons/AiProviderIcons.tsx b/apps/webapp/app/assets/icons/AiProviderIcons.tsx index 2be3fe38ed7..418cdeff569 100644 --- a/apps/webapp/app/assets/icons/AiProviderIcons.tsx +++ b/apps/webapp/app/assets/icons/AiProviderIcons.tsx @@ -147,7 +147,12 @@ export function CerebrasIcon({ className }: IconProps) { export function MistralIcon({ className }: IconProps) { return ( - + @@ -174,4 +179,3 @@ export function AzureIcon({ className }: IconProps) { ); } - diff --git a/apps/webapp/app/assets/icons/AttemptIcon.tsx b/apps/webapp/app/assets/icons/AttemptIcon.tsx index de2a886b9d5..4b6c7f03698 100644 --- a/apps/webapp/app/assets/icons/AttemptIcon.tsx +++ b/apps/webapp/app/assets/icons/AttemptIcon.tsx @@ -8,22 +8,8 @@ export function AttemptIcon({ className }: { className?: string }) { fill="none" xmlns="http://www.w3.org/2000/svg" > - - + + - + ); } diff --git a/apps/webapp/app/assets/icons/FunctionIcon.tsx b/apps/webapp/app/assets/icons/FunctionIcon.tsx index 2defe9c2f8d..11b630ac2fa 100644 --- a/apps/webapp/app/assets/icons/FunctionIcon.tsx +++ b/apps/webapp/app/assets/icons/FunctionIcon.tsx @@ -8,15 +8,7 @@ export function FunctionIcon({ className }: { className?: string }) { fill="none" xmlns="http://www.w3.org/2000/svg" > - + - + - + setHovered(true)} onMouseLeave={() => setHovered(false)} > - + - + + diff --git a/apps/webapp/app/assets/icons/StreamsIcon.tsx b/apps/webapp/app/assets/icons/StreamsIcon.tsx index 73cc480f4d4..8deb7a19c9e 100644 --- a/apps/webapp/app/assets/icons/StreamsIcon.tsx +++ b/apps/webapp/app/assets/icons/StreamsIcon.tsx @@ -1,10 +1,24 @@ export function StreamsIcon({ className }: { className?: string }) { return ( - - - - - + + + + + ); } - diff --git a/apps/webapp/app/assets/icons/TextSquareIcon.tsx b/apps/webapp/app/assets/icons/TextSquareIcon.tsx index d25faf6d0ba..cffb0aa9d2e 100644 --- a/apps/webapp/app/assets/icons/TextSquareIcon.tsx +++ b/apps/webapp/app/assets/icons/TextSquareIcon.tsx @@ -9,10 +9,42 @@ export function TextSquareIcon({ className }: { className?: string }) { xmlns="http://www.w3.org/2000/svg" > - - - - + + + + ); } diff --git a/apps/webapp/app/clientBeforeFirstRender.ts b/apps/webapp/app/clientBeforeFirstRender.ts index 3275c54423a..5b5b6404221 100644 --- a/apps/webapp/app/clientBeforeFirstRender.ts +++ b/apps/webapp/app/clientBeforeFirstRender.ts @@ -22,10 +22,7 @@ function cleanupLegacyResizablePanelStorage() { const toRemove: string[] = []; for (let i = 0; i < window.localStorage.length; i++) { const key = window.localStorage.key(i); - if ( - key && - (key.startsWith("panel-group-react-aria") || key === "panel-run-parent-v2") - ) { + if (key && (key.startsWith("panel-group-react-aria") || key === "panel-run-parent-v2")) { toRemove.push(key); } } diff --git a/apps/webapp/app/components/AskAI.tsx b/apps/webapp/app/components/AskAI.tsx index 814d4649c8f..bf76654cced 100644 --- a/apps/webapp/app/components/AskAI.tsx +++ b/apps/webapp/app/components/AskAI.tsx @@ -134,11 +134,7 @@ function AskAIProvider({ websiteId, isCollapsed = false }: AskAIProviderProps) { - + Ask AI diff --git a/apps/webapp/app/components/BlankStatePanels.tsx b/apps/webapp/app/components/BlankStatePanels.tsx index c52991d5435..04959680fdb 100644 --- a/apps/webapp/app/components/BlankStatePanels.tsx +++ b/apps/webapp/app/components/BlankStatePanels.tsx @@ -207,17 +207,17 @@ export function SessionsNone() { } > - A session is a stateful execution of an agent, with two-way streaming and durable - compute. A single session can have multiple runs associated with it, so one conversation - can span many task triggers. The input stream carries incoming user messages, and the - output stream carries everything the agent produces, including AI generation parts (text, - reasoning, tool calls, etc.) and any custom data parts your task emits. + A session is a stateful execution of an agent, with two-way streaming and durable compute. A + single session can have multiple runs associated with it, so one conversation can span many + task triggers. The input stream carries incoming user messages, and the output stream + carries everything the agent produces, including AI generation parts (text, reasoning, tool + calls, etc.) and any custom data parts your task emits. The easiest way to create one is to trigger a chat.agent task, which is built on sessions and handles the chat turn loop for you. You can also call{" "} - sessions.start() directly for non-chat patterns like agent - inboxes, approval flows, or server-to-server streaming. + sessions.start() directly for non-chat patterns like agent inboxes, + approval flows, or server-to-server streaming. ); diff --git a/apps/webapp/app/components/DevPresence.tsx b/apps/webapp/app/components/DevPresence.tsx index 7a99dab37a5..addb85e14f7 100644 --- a/apps/webapp/app/components/DevPresence.tsx +++ b/apps/webapp/app/components/DevPresence.tsx @@ -154,8 +154,8 @@ export function DevPresencePanel({ isConnected }: { isConnected: boolean | undef {isConnected === undefined ? "Checking connection..." : isConnected - ? "Your dev server is connected" - : "Your dev server is not connected"} + ? "Your dev server is connected" + : "Your dev server is not connected"}
@@ -169,8 +169,8 @@ export function DevPresencePanel({ isConnected }: { isConnected: boolean | undef {isConnected === undefined ? "Checking connection..." : isConnected - ? "Your local dev server is connected to Trigger.dev" - : "Your local dev server is not connected to Trigger.dev"} + ? "Your local dev server is connected to Trigger.dev" + : "Your local dev server is not connected to Trigger.dev"}
{isConnected ? null : ( diff --git a/apps/webapp/app/components/LoginPageLayout.tsx b/apps/webapp/app/components/LoginPageLayout.tsx index 3e42cd6894f..323faf4ea26 100644 --- a/apps/webapp/app/components/LoginPageLayout.tsx +++ b/apps/webapp/app/components/LoginPageLayout.tsx @@ -63,8 +63,8 @@ export function LoginPageLayout({ children }: { children: React.ReactNode }) {
{children}
- Having login issues? Email us{" "} - or ask us in Discord + Having login issues? Email us or{" "} + ask us in Discord diff --git a/apps/webapp/app/components/SetupCommands.tsx b/apps/webapp/app/components/SetupCommands.tsx index accb2f65a8f..d219b0be721 100644 --- a/apps/webapp/app/components/SetupCommands.tsx +++ b/apps/webapp/app/components/SetupCommands.tsx @@ -209,7 +209,10 @@ export function TriggerLoginStepV3({ title }: TabsProps) { ); } -export function TriggerDeployStep({ title, environment }: TabsProps & { environment: { type: string } }) { +export function TriggerDeployStep({ + title, + environment, +}: TabsProps & { environment: { type: string } }) { const triggerCliTag = useTriggerCliTag(); const { activePackageManager, setActivePackageManager } = usePackageManager(); diff --git a/apps/webapp/app/components/Shortcuts.tsx b/apps/webapp/app/components/Shortcuts.tsx index 4702f37239c..63b5fddf715 100644 --- a/apps/webapp/app/components/Shortcuts.tsx +++ b/apps/webapp/app/components/Shortcuts.tsx @@ -4,13 +4,7 @@ import { useShortcutKeys } from "~/hooks/useShortcutKeys"; import { Button } from "./primitives/Buttons"; import { Header3 } from "./primitives/Headers"; import { Paragraph } from "./primitives/Paragraph"; -import { - Sheet, - SheetContent, - SheetHeader, - SheetTitle, - SheetTrigger -} from "./primitives/SheetV3"; +import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from "./primitives/SheetV3"; import { ShortcutKey } from "./primitives/ShortcutKey"; export function Shortcuts() { @@ -83,7 +77,7 @@ function ShortcutContent() { - + diff --git a/apps/webapp/app/components/admin/backOffice/ApiRateLimitSection.server.ts b/apps/webapp/app/components/admin/backOffice/ApiRateLimitSection.server.ts index 4855c4c2465..7de475bda8e 100644 --- a/apps/webapp/app/components/admin/backOffice/ApiRateLimitSection.server.ts +++ b/apps/webapp/app/components/admin/backOffice/ApiRateLimitSection.server.ts @@ -40,9 +40,7 @@ export const apiRateLimitDomain: RateLimitDomain = { }, }; -export function resolveEffectiveApiRateLimit( - override: unknown -): EffectiveRateLimit { +export function resolveEffectiveApiRateLimit(override: unknown): EffectiveRateLimit { return resolveEffectiveRateLimit(override, apiRateLimitDomain); } diff --git a/apps/webapp/app/components/admin/backOffice/ApiRateLimitSection.tsx b/apps/webapp/app/components/admin/backOffice/ApiRateLimitSection.tsx index b27956f4360..6b13bac8b4f 100644 --- a/apps/webapp/app/components/admin/backOffice/ApiRateLimitSection.tsx +++ b/apps/webapp/app/components/admin/backOffice/ApiRateLimitSection.tsx @@ -1,17 +1,8 @@ -import { - RateLimitSection, - type RateLimitWrapperProps, -} from "./RateLimitSection"; +import { RateLimitSection, type RateLimitWrapperProps } from "./RateLimitSection"; export const API_RATE_LIMIT_INTENT = "set-rate-limit"; export const API_RATE_LIMIT_SAVED_VALUE = "rate-limit"; export function ApiRateLimitSection(props: RateLimitWrapperProps) { - return ( - - ); + return ; } diff --git a/apps/webapp/app/components/admin/backOffice/BatchRateLimitSection.server.ts b/apps/webapp/app/components/admin/backOffice/BatchRateLimitSection.server.ts index 83a368094a9..3891e4fc40c 100644 --- a/apps/webapp/app/components/admin/backOffice/BatchRateLimitSection.server.ts +++ b/apps/webapp/app/components/admin/backOffice/BatchRateLimitSection.server.ts @@ -40,9 +40,7 @@ export const batchRateLimitDomain: RateLimitDomain = { }, }; -export function resolveEffectiveBatchRateLimit( - override: unknown -): EffectiveRateLimit { +export function resolveEffectiveBatchRateLimit(override: unknown): EffectiveRateLimit { return resolveEffectiveRateLimit(override, batchRateLimitDomain); } diff --git a/apps/webapp/app/components/admin/backOffice/BatchRateLimitSection.tsx b/apps/webapp/app/components/admin/backOffice/BatchRateLimitSection.tsx index 0e52124d290..64ee6a05d41 100644 --- a/apps/webapp/app/components/admin/backOffice/BatchRateLimitSection.tsx +++ b/apps/webapp/app/components/admin/backOffice/BatchRateLimitSection.tsx @@ -1,17 +1,8 @@ -import { - RateLimitSection, - type RateLimitWrapperProps, -} from "./RateLimitSection"; +import { RateLimitSection, type RateLimitWrapperProps } from "./RateLimitSection"; export const BATCH_RATE_LIMIT_INTENT = "set-batch-rate-limit"; export const BATCH_RATE_LIMIT_SAVED_VALUE = "batch-rate-limit"; export function BatchRateLimitSection(props: RateLimitWrapperProps) { - return ( - - ); + return ; } diff --git a/apps/webapp/app/components/admin/backOffice/MaxProjectsSection.tsx b/apps/webapp/app/components/admin/backOffice/MaxProjectsSection.tsx index bf8ecf83161..8aa4b5c93c9 100644 --- a/apps/webapp/app/components/admin/backOffice/MaxProjectsSection.tsx +++ b/apps/webapp/app/components/admin/backOffice/MaxProjectsSection.tsx @@ -68,9 +68,7 @@ export function MaxProjectsSection({ Limit - - {maximumProjectCount.toLocaleString()} - + {maximumProjectCount.toLocaleString()} ) : ( @@ -89,11 +87,7 @@ export function MaxProjectsSection({ {fieldError("maximumProjectCount")}
-
{isLoading ? ( diff --git a/apps/webapp/app/components/code/QueryResultsChart.tsx b/apps/webapp/app/components/code/QueryResultsChart.tsx index 2c3f1c9f2bf..97013c192a4 100644 --- a/apps/webapp/app/components/code/QueryResultsChart.tsx +++ b/apps/webapp/app/components/code/QueryResultsChart.tsx @@ -872,10 +872,7 @@ export const QueryResultsChart = memo(function QueryResultsChart({ ); // Create value formatter for tooltips and legend based on column format - const tooltipValueFormatter = useMemo( - () => createValueFormatter(yAxisFormat), - [yAxisFormat] - ); + const tooltipValueFormatter = useMemo(() => createValueFormatter(yAxisFormat), [yAxisFormat]); // Check if the group-by column has a runStatus customRenderType const groupByIsRunStatus = useMemo(() => { @@ -1181,9 +1178,7 @@ function createYAxisFormatter( if (format === "bytes" || format === "decimalBytes") { const divisor = format === "bytes" ? 1024 : 1000; const units = - format === "bytes" - ? ["B", "KiB", "MiB", "GiB", "TiB"] - : ["B", "KB", "MB", "GB", "TB"]; + format === "bytes" ? ["B", "KiB", "MiB", "GiB", "TiB"] : ["B", "KB", "MB", "GB", "TB"]; return (value: number): string => { if (value === 0) return "0 B"; // Use consistent unit for all ticks based on max value @@ -1205,8 +1200,7 @@ function createYAxisFormatter( } if (format === "durationSeconds") { - return (value: number): string => - formatDurationMilliseconds(value * 1000, { style: "short" }); + return (value: number): string => formatDurationMilliseconds(value * 1000, { style: "short" }); } if (format === "durationNs") { diff --git a/apps/webapp/app/components/code/TSQLResultsTable.tsx b/apps/webapp/app/components/code/TSQLResultsTable.tsx index 73ca07180bf..8e10f8e6faa 100644 --- a/apps/webapp/app/components/code/TSQLResultsTable.tsx +++ b/apps/webapp/app/components/code/TSQLResultsTable.tsx @@ -186,10 +186,10 @@ const fuzzyFilter: FilterFn = (row, columnId, value, addMeta) => { cellValue === null ? "NULL" : cellValue === undefined - ? "" - : typeof cellValue === "object" - ? JSON.stringify(cellValue) - : String(cellValue); + ? "" + : typeof cellValue === "object" + ? JSON.stringify(cellValue) + : String(cellValue); // Build searchable strings - formatted value (if we have column metadata) const formattedValue = meta?.outputColumn @@ -569,12 +569,8 @@ function CellValue({ if (typeof value === "string") { const spanId = row?.["span_id"]; const runPath = v3RunPathFromFriendlyId(value); - const href = typeof spanId === "string" && spanId - ? `${runPath}?span=${spanId}` - : runPath; - const tooltip = typeof spanId === "string" && spanId - ? "Jump to span" - : "Jump to run"; + const href = typeof spanId === "string" && spanId ? `${runPath}?span=${spanId}` : runPath; + const tooltip = typeof spanId === "string" && spanId ? "Jump to span" : "Jump to run"; return ( hiddenColumns?.length ? columns.filter((col) => !hiddenColumns.includes(col.name)) : columns, + () => + hiddenColumns?.length ? columns.filter((col) => !hiddenColumns.includes(col.name)) : columns, [columns, hiddenColumns] ); const columnDefs = useMemo[]>( diff --git a/apps/webapp/app/components/code/codeMirrorTheme.ts b/apps/webapp/app/components/code/codeMirrorTheme.ts index 2faed6eaa54..c9dd2f12e86 100644 --- a/apps/webapp/app/components/code/codeMirrorTheme.ts +++ b/apps/webapp/app/components/code/codeMirrorTheme.ts @@ -67,10 +67,9 @@ export function darkTheme(): Extension { }, ".cm-cursor, .cm-dropCursor": { borderLeftColor: cursor }, - "&.cm-focused .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection": - { - backgroundColor: selection, - }, + "&.cm-focused .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection": { + backgroundColor: selection, + }, ".cm-panels": { backgroundColor: darkBackground, color: ivory }, ".cm-panels.cm-panels-top": { borderBottom: "2px solid black" }, @@ -167,20 +166,14 @@ export function darkTheme(): Extension { backgroundColor: scrollbarBg, }, }, - { dark: true }, + { dark: true } ); /// The highlighting style for code in the JSON Hero theme. const jsonHeroHighlightStyle = HighlightStyle.define([ { tag: tags.keyword, color: violet }, { - tag: [ - tags.name, - tags.deleted, - tags.character, - tags.propertyName, - tags.macroName, - ], + tag: [tags.name, tags.deleted, tags.character, tags.propertyName, tags.macroName], color: lilac, }, { tag: [tags.function(tags.variableName), tags.labelName], color: malibu }, diff --git a/apps/webapp/app/components/code/tsql/index.ts b/apps/webapp/app/components/code/tsql/index.ts index bda244e30fa..71c543161d8 100644 --- a/apps/webapp/app/components/code/tsql/index.ts +++ b/apps/webapp/app/components/code/tsql/index.ts @@ -2,5 +2,9 @@ // Provides syntax highlighting, autocompletion, and linting for TSQL queries export { createTSQLCompletion } from "./tsqlCompletion"; -export { createTSQLLinter, isValidTSQLQuery, getTSQLError, type TSQLLinterConfig } from "./tsqlLinter"; - +export { + createTSQLLinter, + isValidTSQLQuery, + getTSQLError, + type TSQLLinterConfig, +} from "./tsqlLinter"; diff --git a/apps/webapp/app/components/code/tsql/tsqlCompletion.ts b/apps/webapp/app/components/code/tsql/tsqlCompletion.ts index b53c551a4d3..03d75f179d3 100644 --- a/apps/webapp/app/components/code/tsql/tsqlCompletion.ts +++ b/apps/webapp/app/components/code/tsql/tsqlCompletion.ts @@ -92,8 +92,8 @@ function createFunctionCompletions(): Completion[] { meta.maxArgs === 0 ? "()" : meta.minArgs === meta.maxArgs - ? `(${meta.minArgs} args)` - : `(${meta.minArgs}${meta.maxArgs ? `-${meta.maxArgs}` : "+"} args)`; + ? `(${meta.minArgs} args)` + : `(${meta.minArgs}${meta.maxArgs ? `-${meta.maxArgs}` : "+"} args)`; functions.push({ label: name, @@ -111,8 +111,8 @@ function createFunctionCompletions(): Completion[] { meta.maxArgs === 0 ? "()" : meta.minArgs === meta.maxArgs - ? `(${meta.minArgs} args)` - : `(${meta.minArgs}${meta.maxArgs ? `-${meta.maxArgs}` : "+"} args)`; + ? `(${meta.minArgs} args)` + : `(${meta.minArgs}${meta.maxArgs ? `-${meta.maxArgs}` : "+"} args)`; functions.push({ label: name, diff --git a/apps/webapp/app/components/code/tsql/tsqlLinter.test.ts b/apps/webapp/app/components/code/tsql/tsqlLinter.test.ts index 8acc81d66f0..8ecc21a1658 100644 --- a/apps/webapp/app/components/code/tsql/tsqlLinter.test.ts +++ b/apps/webapp/app/components/code/tsql/tsqlLinter.test.ts @@ -28,9 +28,7 @@ describe("tsqlLinter", () => { true ); expect( - isValidTSQLQuery( - "SELECT * FROM users LEFT JOIN orders ON users.id = orders.user_id" - ) + isValidTSQLQuery("SELECT * FROM users LEFT JOIN orders ON users.id = orders.user_id") ).toBe(true); }); @@ -76,4 +74,3 @@ describe("tsqlLinter", () => { }); }); }); - diff --git a/apps/webapp/app/components/code/tsql/tsqlLinter.ts b/apps/webapp/app/components/code/tsql/tsqlLinter.ts index 72794e3e9ab..42f5288fb29 100644 --- a/apps/webapp/app/components/code/tsql/tsqlLinter.ts +++ b/apps/webapp/app/components/code/tsql/tsqlLinter.ts @@ -31,11 +31,7 @@ function parseErrorPosition(message: string): { line: number; column: number } | /** * Convert line/column to a document position */ -function positionToOffset( - doc: string, - line: number, - column: number -): number { +function positionToOffset(doc: string, line: number, column: number): number { const lines = doc.split("\n"); // line is 1-indexed @@ -210,4 +206,3 @@ export function getTSQLError(query: string): string | null { return "Unknown error"; } } - diff --git a/apps/webapp/app/components/dashboard-agent/RunDiagnosisCard.tsx b/apps/webapp/app/components/dashboard-agent/RunDiagnosisCard.tsx index a0a96ecb5e2..2fef1f3deb7 100644 --- a/apps/webapp/app/components/dashboard-agent/RunDiagnosisCard.tsx +++ b/apps/webapp/app/components/dashboard-agent/RunDiagnosisCard.tsx @@ -96,7 +96,14 @@ function DiagnosisActions({ actions }: { actions: NonNullable {actions.map((action, i) => { if (action.kind === "view_run" && /^run_[a-z0-9]+$/i.test(action.target)) { - return ; + return ( + + ); } if (action.kind === "docs") { const safeUrl = toSafeUrl(action.target); diff --git a/apps/webapp/app/components/errors/ConfigureErrorAlerts.tsx b/apps/webapp/app/components/errors/ConfigureErrorAlerts.tsx index 32ed778f877..8a823ee78c1 100644 --- a/apps/webapp/app/components/errors/ConfigureErrorAlerts.tsx +++ b/apps/webapp/app/components/errors/ConfigureErrorAlerts.tsx @@ -135,12 +135,7 @@ export function ConfigureErrorAlerts({ /> - +
diff --git a/apps/webapp/app/components/integrations/VercelBuildSettings.tsx b/apps/webapp/app/components/integrations/VercelBuildSettings.tsx index d8e9f3fe3f8..8449d342f53 100644 --- a/apps/webapp/app/components/integrations/VercelBuildSettings.tsx +++ b/apps/webapp/app/components/integrations/VercelBuildSettings.tsx @@ -113,9 +113,7 @@ export function BuildSettingsFields({
); if (disabled && disabledReason) { - return ( - - ); + return ; } return row; })} @@ -140,9 +138,7 @@ export function BuildSettingsFields({ disabled={!enabledSlugs.some((s) => pullEnvVarsBeforeBuild.includes(s))} onCheckedChange={(checked) => { onDiscoverEnvVarsChange( - checked - ? enabledSlugs.filter((s) => pullEnvVarsBeforeBuild.includes(s)) - : [] + checked ? enabledSlugs.filter((s) => pullEnvVarsBeforeBuild.includes(s)) : [] ); }} /> @@ -185,9 +181,7 @@ export function BuildSettingsFields({
); if (disabled && disabledReason) { - return ( - - ); + return ; } return row; })} @@ -208,10 +202,12 @@ export function BuildSettingsFields({ When enabled, production deployments wait for Vercel deployment to complete before - promoting the Trigger.dev deployment. This will disable the "Auto-assign Custom - Production Domains" option in your Vercel project settings to perform staged - deployments.{" "} - + promoting the Trigger.dev deployment. This will disable the "Auto-assign Custom Production + Domains" option in your Vercel project settings to perform staged deployments.{" "} + Learn more . @@ -225,9 +221,8 @@ export function BuildSettingsFields({ )} {!currentTriggerVersion && currentTriggerVersionFetchFailed && ( - Couldn't read{" "} - TRIGGER_VERSION from Vercel β€” - check the Vercel dashboard to confirm the production pin. + Couldn't read TRIGGER_VERSION from + Vercel β€” check the Vercel dashboard to confirm the production pin. )} @@ -244,9 +239,9 @@ export function BuildSettingsFields({ /> - When enabled, the integration automatically promotes the Vercel deployment after - the Trigger.dev build completes. Turn off to manually promote from your Vercel - dashboard β€” Trigger.dev will then promote automatically once you do. + When enabled, the integration automatically promotes the Vercel deployment after the + Trigger.dev build completes. Turn off to manually promote from your Vercel dashboard β€” + Trigger.dev will then promote automatically once you do. )} diff --git a/apps/webapp/app/components/integrations/VercelLogo.tsx b/apps/webapp/app/components/integrations/VercelLogo.tsx index 7ddf039abfd..856b74ebada 100644 --- a/apps/webapp/app/components/integrations/VercelLogo.tsx +++ b/apps/webapp/app/components/integrations/VercelLogo.tsx @@ -1,11 +1,6 @@ export function VercelLogo({ className }: { className?: string }) { return ( - + ); diff --git a/apps/webapp/app/components/integrations/VercelOnboardingModal.tsx b/apps/webapp/app/components/integrations/VercelOnboardingModal.tsx index 21734c5c038..7ae12b20d45 100644 --- a/apps/webapp/app/components/integrations/VercelOnboardingModal.tsx +++ b/apps/webapp/app/components/integrations/VercelOnboardingModal.tsx @@ -4,11 +4,7 @@ import { ChevronDownIcon, ChevronUpIcon, } from "@heroicons/react/20/solid"; -import { - useFetcher, - useNavigation, - useSearchParams, -} from "@remix-run/react"; +import { useFetcher, useNavigation, useSearchParams } from "@remix-run/react"; import { useTypedFetcher } from "remix-typedjson"; import { Dialog, DialogContent, DialogHeader } from "~/components/primitives/Dialog"; import { Button, LinkButton } from "~/components/primitives/Buttons"; @@ -31,9 +27,7 @@ import { import { VercelLogo } from "~/components/integrations/VercelLogo"; import { BuildSettingsFields } from "~/components/integrations/VercelBuildSettings"; import { OctoKitty } from "~/components/GitHubLoginButton"; -import { - ConnectGitHubRepoModal, -} from "~/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.github"; +import { ConnectGitHubRepoModal } from "~/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.github"; import { type SyncEnvVarsMapping, type EnvSlug, @@ -44,7 +38,12 @@ import { } from "~/v3/vercel/vercelProjectIntegrationSchema"; import { type VercelCustomEnvironment } from "~/models/vercelIntegration.server"; import { type VercelOnboardingData } from "~/presenters/v3/VercelSettingsPresenter.server"; -import { vercelAppInstallPath, v3ProjectSettingsIntegrationsPath, githubAppInstallPath, vercelResourcePath } from "~/utils/pathBuilder"; +import { + vercelAppInstallPath, + v3ProjectSettingsIntegrationsPath, + githubAppInstallPath, + vercelResourcePath, +} from "~/utils/pathBuilder"; import type { loader } from "~/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel"; import { useEffect, useState, useCallback, useRef } from "react"; import { usePostHogTracking } from "~/hooks/usePostHog"; @@ -73,9 +72,7 @@ function formatVercelTargets(targets: string[]): string { staging: "Staging", }; - return targets - .map((t) => targetLabels[t.toLowerCase()] || t) - .join(", "); + return targets.map((t) => targetLabels[t.toLowerCase()] || t).join(", "); } type OnboardingState = @@ -153,7 +150,8 @@ export function VercelOnboardingModal({ } // For marketplace origin, skip env-mapping step and go directly to env-var-sync if (!fromMarketplaceContext) { - const customEnvs = (onboardingData?.customEnvironments?.length ?? 0) > 0 && hasStagingEnvironment; + const customEnvs = + (onboardingData?.customEnvironments?.length ?? 0) > 0 && hasStagingEnvironment; if (customEnvs) { return "env-mapping"; } @@ -218,14 +216,18 @@ export function VercelOnboardingModal({ environmentId: string; displayName: string; } | null>(null); - const availableEnvSlugsForOnboarding = getAvailableEnvSlugs(hasStagingEnvironment, hasPreviewEnvironment); - const availableEnvSlugsForOnboardingBuildSettings = getAvailableEnvSlugsForBuildSettings(hasStagingEnvironment, hasPreviewEnvironment); + const availableEnvSlugsForOnboarding = getAvailableEnvSlugs( + hasStagingEnvironment, + hasPreviewEnvironment + ); + const availableEnvSlugsForOnboardingBuildSettings = getAvailableEnvSlugsForBuildSettings( + hasStagingEnvironment, + hasPreviewEnvironment + ); const [pullEnvVarsBeforeBuild, setPullEnvVarsBeforeBuild] = useState( () => availableEnvSlugsForOnboardingBuildSettings ); - const [atomicBuilds, setAtomicBuilds] = useState( - () => ["prod"] - ); + const [atomicBuilds, setAtomicBuilds] = useState(() => ["prod"]); const [discoverEnvVars, setDiscoverEnvVars] = useState( () => availableEnvSlugsForOnboardingBuildSettings ); @@ -309,7 +311,13 @@ export function VercelOnboardingModal({ }, 100); } } - }, [isOpen, fromMarketplaceContext, nextUrl, isOnboardingComplete, isGitHubConnectedForOnboarding]); + }, [ + isOpen, + fromMarketplaceContext, + nextUrl, + isOnboardingComplete, + isGitHubConnectedForOnboarding, + ]); useEffect(() => { if (!isOpen) { @@ -336,7 +344,6 @@ export function VercelOnboardingModal({ } switch (state) { - case "loading-projects": loadingStateRef.current = state; if (onDataReload) { @@ -371,19 +378,33 @@ export function VercelOnboardingModal({ }, [isOpen, state, onboardingData?.authInvalid, vercelStagingEnvironment, onDataReload, onClose]); useEffect(() => { - if (!onboardingData?.authInvalid && state === "loading-projects" && onboardingData?.availableProjects !== undefined) { + if ( + !onboardingData?.authInvalid && + state === "loading-projects" && + onboardingData?.availableProjects !== undefined + ) { setState("project-selection"); } }, [state, onboardingData?.availableProjects, onboardingData?.authInvalid]); useEffect(() => { - if (!onboardingData?.authInvalid && state === "loading-env-vars" && onboardingData?.environmentVariables) { + if ( + !onboardingData?.authInvalid && + state === "loading-env-vars" && + onboardingData?.environmentVariables + ) { setState("env-var-sync"); } }, [state, onboardingData?.environmentVariables, onboardingData?.authInvalid]); useEffect(() => { - if (state === "project-selection" && fetcher.data && "success" in fetcher.data && fetcher.data.success && fetcher.state === "idle") { + if ( + state === "project-selection" && + fetcher.data && + "success" in fetcher.data && + fetcher.data.success && + fetcher.state === "idle" + ) { trackOnboarding("vercel onboarding project selected", { vercel_project_name: selectedVercelProject?.name, }); @@ -394,12 +415,20 @@ export function VercelOnboardingModal({ } else if (fetcher.data && "error" in fetcher.data && typeof fetcher.data.error === "string") { setProjectSelectionError(fetcher.data.error); } - }, [state, fetcher.data, fetcher.state, onDataReload, trackOnboarding, selectedVercelProject?.name]); + }, [ + state, + fetcher.data, + fetcher.state, + onDataReload, + trackOnboarding, + selectedVercelProject?.name, + ]); // For marketplace origin, skip env-mapping step useEffect(() => { if (state === "loading-env-mapping" && onboardingData) { - const hasCustomEnvs = (onboardingData.customEnvironments?.length ?? 0) > 0 && hasStagingEnvironment; + const hasCustomEnvs = + (onboardingData.customEnvironments?.length ?? 0) > 0 && hasStagingEnvironment; if (hasCustomEnvs && !fromMarketplaceContext) { setState("env-mapping"); } else { @@ -410,14 +439,13 @@ export function VercelOnboardingModal({ const secretEnvVars = envVars.filter((v) => v.isSecret); const syncableEnvVars = envVars.filter((v) => !v.isSecret); - const enabledEnvVars = syncableEnvVars.filter( - (v) => shouldSyncEnvVarForAnyEnvironment(syncEnvVarsMapping, v.key) + const enabledEnvVars = syncableEnvVars.filter((v) => + shouldSyncEnvVarForAnyEnvironment(syncEnvVarsMapping, v.key) ); const overlappingEnvVarsCount = enabledEnvVars.filter((v) => existingVars[v.key]).length; - const isSubmitting = - navigation.state === "submitting" || navigation.state === "loading"; + const isSubmitting = navigation.state === "submitting" || navigation.state === "loading"; const actionUrl = vercelResourcePath(organizationSlug, projectSlug, environmentSlug); @@ -529,7 +557,6 @@ export function VercelOnboardingModal({ method: "post", action: actionUrl, }); - }, [vercelStagingEnvironment, envMappingFetcher, actionUrl, trackOnboarding]); const handleBuildSettingsNext = useCallback(() => { @@ -541,7 +568,10 @@ export function VercelOnboardingModal({ const formData = new FormData(); formData.append("action", "complete-onboarding"); - formData.append("vercelStagingEnvironment", vercelStagingEnvironment ? JSON.stringify(vercelStagingEnvironment) : ""); + formData.append( + "vercelStagingEnvironment", + vercelStagingEnvironment ? JSON.stringify(vercelStagingEnvironment) : "" + ); formData.append("pullEnvVarsBeforeBuild", JSON.stringify(pullEnvVarsBeforeBuild)); formData.append("atomicBuilds", JSON.stringify(atomicBuilds)); formData.append("discoverEnvVars", JSON.stringify(discoverEnvVars)); @@ -572,24 +602,52 @@ export function VercelOnboardingModal({ github_app_installed: gitHubAppInstallations.length > 0, }); } - }, [vercelStagingEnvironment, pullEnvVarsBeforeBuild, atomicBuilds, discoverEnvVars, syncEnvVarsMapping, nextUrl, fromMarketplaceContext, isGitHubConnectedForOnboarding, completeOnboardingFetcher, actionUrl, trackOnboarding, capture, organizationSlug, projectSlug, gitHubAppInstallations.length]); - - const handleFinishOnboarding = useCallback((e: React.FormEvent) => { - e.preventDefault(); - const form = e.currentTarget; - const formData = new FormData(form); - completeOnboardingFetcher.submit(formData, { - method: "post", - action: actionUrl, - }); - }, [completeOnboardingFetcher, actionUrl]); + }, [ + vercelStagingEnvironment, + pullEnvVarsBeforeBuild, + atomicBuilds, + discoverEnvVars, + syncEnvVarsMapping, + nextUrl, + fromMarketplaceContext, + isGitHubConnectedForOnboarding, + completeOnboardingFetcher, + actionUrl, + trackOnboarding, + capture, + organizationSlug, + projectSlug, + gitHubAppInstallations.length, + ]); + + const handleFinishOnboarding = useCallback( + (e: React.FormEvent) => { + e.preventDefault(); + const form = e.currentTarget; + const formData = new FormData(form); + completeOnboardingFetcher.submit(formData, { + method: "post", + action: actionUrl, + }); + }, + [completeOnboardingFetcher, actionUrl] + ); useEffect(() => { - if (completeOnboardingFetcher.data && typeof completeOnboardingFetcher.data === "object" && "success" in completeOnboardingFetcher.data && completeOnboardingFetcher.data.success && completeOnboardingFetcher.state === "idle") { + if ( + completeOnboardingFetcher.data && + typeof completeOnboardingFetcher.data === "object" && + "success" in completeOnboardingFetcher.data && + completeOnboardingFetcher.data.success && + completeOnboardingFetcher.state === "idle" + ) { if (state === "github-connection") { return; } - if ("redirectTo" in completeOnboardingFetcher.data && typeof completeOnboardingFetcher.data.redirectTo === "string") { + if ( + "redirectTo" in completeOnboardingFetcher.data && + typeof completeOnboardingFetcher.data.redirectTo === "string" + ) { const validRedirect = safeRedirectUrl(completeOnboardingFetcher.data.redirectTo); if (validRedirect) { window.location.href = validRedirect; @@ -632,7 +690,13 @@ export function VercelOnboardingModal({ }, [state, organizationSlug, projectSlug]); useEffect(() => { - if (envMappingFetcher.data && typeof envMappingFetcher.data === "object" && "success" in envMappingFetcher.data && envMappingFetcher.data.success && envMappingFetcher.state === "idle") { + if ( + envMappingFetcher.data && + typeof envMappingFetcher.data === "object" && + "success" in envMappingFetcher.data && + envMappingFetcher.data.success && + envMappingFetcher.state === "idle" + ) { setState("loading-env-vars"); } }, [envMappingFetcher.data, envMappingFetcher.state]); @@ -644,9 +708,7 @@ export function VercelOnboardingModal({ if (customEnvironments.length === 1) { selectedEnv = customEnvironments[0]; } else { - const stagingEnv = customEnvironments.find( - (env) => env.slug.toLowerCase() === "staging" - ); + const stagingEnv = customEnvironments.find((env) => env.slug.toLowerCase() === "staging"); selectedEnv = stagingEnv ?? customEnvironments[0]; } @@ -673,14 +735,17 @@ export function VercelOnboardingModal({ if (isLoadingState) { return ( - { - if (!open && !fromMarketplaceContext) { - if (state as string !== "completed") { - trackOnboarding("vercel onboarding abandoned"); + { + if (!open && !fromMarketplaceContext) { + if ((state as string) !== "completed") { + trackOnboarding("vercel onboarding abandoned"); + } + onClose(); } - onClose(); - } - }}> + }} + > e.preventDefault()}>
@@ -704,18 +769,23 @@ export function VercelOnboardingModal({ const disabledEnvSlugsForBuildSettings = hasStagingEnvironment && !vercelStagingEnvironment - ? ({ stg: "Map a custom Vercel environment to Staging to enable this" } as Partial>) + ? ({ stg: "Map a custom Vercel environment to Staging to enable this" } as Partial< + Record + >) : undefined; return ( - { - if (!open && !fromMarketplaceContext) { - if (state !== "completed") { - trackOnboarding("vercel onboarding abandoned"); + { + if (!open && !fromMarketplaceContext) { + if (state !== "completed") { + trackOnboarding("vercel onboarding abandoned"); + } + onClose(); } - onClose(); - } - }}> + }} + > e.preventDefault()}>
@@ -729,8 +799,8 @@ export function VercelOnboardingModal({
Select Vercel Project - Choose which Vercel project to connect with this Trigger.dev project. - Your API keys will be automatically synced to Vercel. + Choose which Vercel project to connect with this Trigger.dev project. Your API keys + will be automatically synced to Vercel. {availableProjects.length === 0 ? ( @@ -763,13 +833,14 @@ export function VercelOnboardingModal({ )} - {projectSelectionError && ( - {projectSelectionError} - )} + {projectSelectionError && {projectSelectionError}} - Once connected, your TRIGGER_SECRET_KEY will be - automatically synced to Vercel for each environment. + Once connected, your{" "} + + TRIGGER_SECRET_KEY + {" "} + will be automatically synced to Vercel for each environment. } cancelButton={ - } @@ -811,11 +879,13 @@ export function VercelOnboardingModal({ Map Vercel Environment to Staging Select which custom Vercel environment should map to Trigger.dev's Staging - environment. Production and Preview environments are mapped automatically. - If you skip this step, the{" "} - TRIGGER_SECRET_KEY{" "} - will not be installed for the staging environment in Vercel. You can configure this later in - project settings. + environment. Production and Preview environments are mapped automatically. If you + skip this step, the{" "} + + TRIGGER_SECRET_KEY + {" "} + will not be installed for the staging environment in Vercel. You can configure this + later in project settings. (() => ({ value }), [value]); @@ -193,11 +193,7 @@ const ClientTabsContent = React.forwardRef< )); diff --git a/apps/webapp/app/components/primitives/CopyTextLink.tsx b/apps/webapp/app/components/primitives/CopyTextLink.tsx index 33818fa6077..2c117af9b86 100644 --- a/apps/webapp/app/components/primitives/CopyTextLink.tsx +++ b/apps/webapp/app/components/primitives/CopyTextLink.tsx @@ -16,18 +16,12 @@ export function CopyTextLink({ value, className }: CopyTextLinkProps) { onClick={copy} className={cn( "inline-flex cursor-pointer items-center gap-1 text-xs transition-colors", - copied - ? "text-success" - : "text-text-dimmed hover:text-text-bright", + copied ? "text-success" : "text-text-dimmed hover:text-text-bright", className )} > {copied ? "Copied" : "Copy"} - {copied ? ( - - ) : ( - - )} + {copied ? : } ); } diff --git a/apps/webapp/app/components/primitives/DateTime.tsx b/apps/webapp/app/components/primitives/DateTime.tsx index 4dae92731af..41c51cdd74d 100644 --- a/apps/webapp/app/components/primitives/DateTime.tsx +++ b/apps/webapp/app/components/primitives/DateTime.tsx @@ -275,10 +275,10 @@ const DateTimeAccurateInner = ({ return hideDate ? formatTimeOnly(realDate, displayTimeZone, locales, hour12) : realPrevDate - ? isSameDay(realDate, realPrevDate) - ? formatTimeOnly(realDate, displayTimeZone, locales, hour12) - : formatDateTimeAccurate(realDate, displayTimeZone, locales, hour12) - : formatDateTimeAccurate(realDate, displayTimeZone, locales, hour12); + ? isSameDay(realDate, realPrevDate) + ? formatTimeOnly(realDate, displayTimeZone, locales, hour12) + : formatDateTimeAccurate(realDate, displayTimeZone, locales, hour12) + : formatDateTimeAccurate(realDate, displayTimeZone, locales, hour12); }, [realDate, displayTimeZone, locales, hour12, hideDate, previousDate]); if (!showTooltip) diff --git a/apps/webapp/app/components/primitives/Dialog.tsx b/apps/webapp/app/components/primitives/Dialog.tsx index 48fc59cfa78..bf485bbf1a2 100644 --- a/apps/webapp/app/components/primitives/Dialog.tsx +++ b/apps/webapp/app/components/primitives/Dialog.tsx @@ -122,5 +122,5 @@ export { DialogTitle, DialogDescription, DialogPortal, - DialogOverlay + DialogOverlay, }; diff --git a/apps/webapp/app/components/primitives/InputNumberStepper.tsx b/apps/webapp/app/components/primitives/InputNumberStepper.tsx index f4aafd5cae1..b434bb1d30a 100644 --- a/apps/webapp/app/components/primitives/InputNumberStepper.tsx +++ b/apps/webapp/app/components/primitives/InputNumberStepper.tsx @@ -67,7 +67,7 @@ export function InputNumberStepper({ const isMaxDisabled = max !== undefined && !Number.isNaN(numericValue) && numericValue >= max; function clamp(val: number): number { - if (Number.isNaN(val)) return typeof value === "number" ? value : min ?? 0; + if (Number.isNaN(val)) return typeof value === "number" ? value : (min ?? 0); let next = val; if (min !== undefined) next = Math.max(min, next); if (max !== undefined) next = Math.min(max, next); diff --git a/apps/webapp/app/components/primitives/MiddleTruncate.tsx b/apps/webapp/app/components/primitives/MiddleTruncate.tsx index c116205aed9..45915c2521d 100644 --- a/apps/webapp/app/components/primitives/MiddleTruncate.tsx +++ b/apps/webapp/app/components/primitives/MiddleTruncate.tsx @@ -139,16 +139,9 @@ export function MiddleTruncate({ text, className }: MiddleTruncateProps) { }, [calculateTruncation]); const content = ( - + {/* Hidden span for measuring text width */} -