Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgrade to Remix Vite #1496

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
Open

Upgrade to Remix Vite #1496

wants to merge 12 commits into from

Conversation

jacobparis
Copy link
Collaborator

@jacobparis jacobparis commented Nov 24, 2024

Hey, revisiting this now that the ecosystem has improved a bunch

  • the bug in Remix that was breaking navigation as server deps were discovered, which required rewriting trigger core to not have barrel files has been fixed Optimize dependencies found through route module graphs remix-run/remix#9921
  • the bug where optimize deps issues wouldn't occur until navigation has been fixed with the unstable_optimizeDeps future flag
  • the Vite plugin vite-plugin-cjs-interop solves a bunch of the changes to package imports like switching to default exports and vice versa that were required before. I couldn't get this working last time, but was possibly just blocked by unrelated bugs that caused me to write this solution off

What we're left with is a much nicer and much more promising PR

  • updates to latest Remix 2.15.0 in one swoop
  • the server.ts has been split into two files: one server.js file that does NOT import anything from the app (and does not get bundled), and a server/app.ts file that return express middleware and can import anything from the app. This latter file will be processed by Vite

Outstanding concerns

  • I don't think you want to keep my dotenv solution here, but I couldn't get infisical working so you can feel free to remove it if it works in your environment
  • unclear what Node version this app is meant to run on. Package.json#engines says 16 and up but the node typings were set to 18 and I've bumped them to 20 because I was getting some mismatches where some things used node 20 types and some used 18
  • I think I've hit parity with the feature flag guards in the server.ts but since it's sliced a bit differently I'm not 100% sure. If you run different environments using these you'll want to double check that
  • could consider merging vite and vitest config

✅ Checklist

  • I have followed every step in the contributing guide
  • The PR title follows the convention.
  • I ran and tested the code works

Testing

[Describe the steps you took to test this change]


Changelog

[Short description of what has changed]


Screenshots

[Screenshots]

💯

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced a new cookie for managing redirection after GitHub authentication.
    • Added a function for parsing environment variable strings into an object format.
  • Improvements

    • Updated the import method for Tailwind CSS to include a URL reference.
    • Enhanced error handling during authentication by preserving redirect information in cookies.
    • Improved event handling and tracking within the event repository.
    • Established a new Express.js server setup for handling HTTP requests and WebSocket connections.
    • Updated various dependencies to their latest versions, including significant upgrades to the Remix framework and Vite.
  • Bug Fixes

    • Resolved a ReferenceError by adjusting the order of variable declarations in the WebSocket handling.
  • Configuration Updates

    • Transitioned to using ECMAScript modules and removed obsolete configuration files that impacted type definitions and server settings.

Copy link

changeset-bot bot commented Nov 24, 2024

⚠️ No Changeset found

Latest commit: 3cfc949

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Copy link
Contributor

coderabbitai bot commented Nov 24, 2024

Walkthrough

The pull request introduces several modifications across multiple files in the web application. Key changes include updates to import statements for various modules, the introduction of a new function for parsing environment variables, and adjustments to the handling of cookies for authentication. The loader function in root.tsx has been enhanced to return additional user-related data. Additionally, the project configuration files have been updated to support ECMAScript modules and the latest versions of dependencies, including the addition of a new Vite configuration file.

Changes

File Change Summary
apps/webapp/app/entry.server.tsx Import statement updated for PassThrough from stream to node:stream.
apps/webapp/app/root.tsx Import statement for Tailwind CSS updated to include ?url. The loader function now returns additional properties including user data and session messages.
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.v3.$projectParam.environment-variables.new/route.tsx New function parseEnv(src: string) added for parsing environment variables, replacing dotenv.parse in handlePaste.
apps/webapp/app/routes/auth.github.callback.tsx Import path for redirectCookie updated from ./auth.github to ./auth.github.server.
apps/webapp/app/routes/auth.github.server.ts New cookie redirectCookie created with a max age of one hour, enhancing redirection logic after authentication.
apps/webapp/app/routes/auth.github.ts Removed createCookie and updated error handling in the action function to use redirectCookie for storing redirect information on errors.
apps/webapp/app/v3/eventRepository.server.ts Import statement for EventEmitter changed from node:stream to node:events. Refined methods for managing events with enhanced error handling and logging.
apps/webapp/app/v3/handleWebsockets.server.ts Moved declaration of authenticatedConnections before export of wss to avoid ReferenceError.
apps/webapp/package.json Added "type": "module" and updated several dependencies to version 2.15.0, including Remix-related packages. New dependencies dotenv-cli and dotenv-expand added.
apps/webapp/postcss.config.js Export syntax changed from CommonJS (module.exports) to ES Module (export default).
apps/webapp/remix.config.js Deleted file containing Remix application configuration settings.
apps/webapp/remix.env.d.ts Deleted TypeScript reference directives for Remix types.
apps/webapp/server.js New file introducing an Express.js server setup with middleware configurations and routing logic, including health checks and error handling.
apps/webapp/server.ts Deleted file containing the previous implementation of the Express.js server.
apps/webapp/server/app.ts New file that sets up an Express server with HTTP request and WebSocket handling, including middleware for request IDs and rate limiting.
apps/webapp/tsconfig.json Updated compilerOptions to include new types and removed remix.env.d.ts from include.
apps/webapp/vite.config.ts New Vite configuration file to set up the build environment, integrating various plugins and features for development and SSR.
package.json Updated vite dependency version from ^4.1.1 to ^5.4.11.

Possibly related PRs

🐰 In the meadow, changes bloom,
With imports fresh, we make some room.
Cookies now hold our redirect dreams,
A server's heart beats in new streams.
From Vite to Tailwind, all is bright,
Our code hops forward, a joyful sight! 🌼

Warning

There were issues while running some tools. Please review the errors and either fix the tool’s configuration or disable the tool if it’s a critical failure.

🔧 eslint (1.23.1)

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

apps/webapp/server.js

(node:17322) ESLintIgnoreWarning: The ".eslintignore" file is no longer supported. Switch to using the "ignores" property in "eslint.config.js": https://eslint.org/docs/latest/use/configure/migration-guide#ignoring-files
(Use node --trace-warnings ... to show where the warning was created)

Oops! Something went wrong! :(

ESLint: 9.15.0

ESLint couldn't find an eslint.config.(js|mjs|cjs) file.

From ESLint v9.0.0, the default configuration file is now eslint.config.js.
If you are using a .eslintrc.* file, please follow the migration guide
to update your configuration file to the new format:

https://eslint.org/docs/latest/use/configure/migration-guide

If you still have problems after following the migration guide, please stop by
https://eslint.org/chat/help to chat with the team.


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 12

🧹 Outside diff range and nitpick comments (23)
apps/webapp/vite.config.ts (3)

9-9: Clarify the usage of Infisical for environment variables

The TODO comment suggests replacing dotenv and dotenv-expand with Infisical. If Infisical is the preferred solution for managing environment variables, consider integrating it to enhance security and streamline configuration. This will help in centralizing secret management and could improve development workflows.

If you decide to proceed, you might replace lines 10-14 with Infisical's initialization code. Ensure that all environment variables are correctly loaded and that this change aligns with your team's practices.


18-78: Consider merging Vite and Vitest configurations

As mentioned in the PR objectives, merging Vite and Vitest configurations can simplify your setup and reduce duplication. Since both tools can share similar configurations, especially regarding plugins and path resolutions, consolidating them could improve maintainability.

You might refactor your configuration files to export a base configuration that both Vite and Vitest can extend. This ensures consistency across your development and testing environments.


31-45: Simplify module resolution for Node.js built-in modules

The aliases for Node.js built-in modules might not be necessary if the dependencies are correctly configured. Instead of manually aliasing each module, consider enabling the resolve option preferBuiltins or using the nodePolyfills plugin to handle this automatically.

Option 1: Use the preferBuiltins option:

 resolve: {
-  alias: [
-    { find: "https", replacement: "node:https" },
-    { find: "path", replacement: "node:path" },
-    { find: "os", replacement: "node:os" },
-    // ... other aliases
-  ],
+  preferBuiltins: true,
 },

Option 2: Use the @vitejs/plugin-node-polyfills plugin:

npm install --save-dev @vitejs/plugin-node-polyfills

Then update your plugins:

+import NodeGlobalsPolyfillPlugin from '@vitejs/plugin-node-polyfills';

 plugins: [
+  NodeGlobalsPolyfillPlugin(),
   // ... other plugins
 ],

Ensure that this change doesn't introduce unintended side effects.

apps/webapp/server/app.ts (1)

82-90: Enhance error message for invalid WebSocket paths

The error message could be more informative to aid in debugging connection issues.

Consider including the attempted path in the error message:

- new Error(
-   "Cannot connect because of invalid path: Please include `/ws` in the path of your upgrade request."
- )
+ new Error(
+   `Cannot connect because of invalid path '${url.pathname}': Please include '/ws' in the path of your upgrade request.`
+ )
apps/webapp/server.js (1)

76-86: Simplify the Morgan logging skip conditions

The skip function in the Morgan logger excludes certain requests from logging. Consider combining the conditions using a regular expression for improved readability.

Apply this diff to streamline the conditions:

 skip: (req) => {
-  if (req.url.startsWith("/@")) return true;
-  if (req.url.match(/\.[jt]sx?$/)) return true;
-  return false;
+  return /^\/@|\.(j|t)sx?$/.test(req.url);
 },
apps/webapp/app/v3/eventRepository.server.ts (3)

Line range hint 680-706: Consider reusing Redis client instances to optimize connections

In the subscribeToTrace method, a new Redis client is instantiated for each subscription, which could lead to a large number of open connections if there are many subscribers. Consider refactoring to reuse a single Redis client instance or implement a connection pool to manage Redis connections more efficiently.


Line range hint 604-615: Use a stronger hash function for span ID generation

The #generateDeterministicSpanId method uses SHA-1, which is considered deprecated for cryptographic purposes. While this may not be critical for generating IDs, it's recommended to use a stronger hash function like SHA-256 for better security and future-proofing.

You can update the code as follows:

- const hash = createHash("sha1");
+ const hash = createHash("sha256");

Line range hint 972-974: Improve time precision in 'getNowInNanoseconds' function

The getNowInNanoseconds function uses Date.getTime(), which provides millisecond precision. For nanosecond precision, consider using process.hrtime.bigint() to get a high-resolution timestamp.

Update the function as follows:

-function getNowInNanoseconds(): bigint {
-  return BigInt(new Date().getTime() * 1_000_000);
+function getNowInNanoseconds(): bigint {
+  return process.hrtime.bigint();
}
apps/webapp/app/routes/auth.github.ts (2)

Line range hint 9-13: Add URL validation for redirectTo parameter

The redirectTo URL is used without validation, which could lead to open redirect vulnerabilities. Consider validating the URL to ensure it points to your application's domain.

Here's a suggested implementation:

 const url = new URL(request.url);
 const redirectTo = url.searchParams.get("redirectTo");
+const isValidRedirect = redirectTo 
+  ? new URL(redirectTo, request.url).origin === new URL(request.url).origin
+  : true;
+
+if (!isValidRedirect) {
+  throw new Error("Invalid redirect URL");
+}

Line range hint 24-27: Improve error handling for cookie serialization

The redirectCookie.serialize() call could throw an error, but it's not handled. Consider wrapping it in a try-catch block.

 if (error instanceof Response) {
-  error.headers.append("Set-Cookie", await redirectCookie.serialize(redirectTo));
+  try {
+    const cookieHeader = await redirectCookie.serialize(redirectTo);
+    error.headers.append("Set-Cookie", cookieHeader);
+  } catch (cookieError) {
+    console.error("Failed to serialize redirect cookie:", cookieError);
+    // Proceed with the redirect without the cookie
+  }
 }
apps/webapp/tsconfig.json (1)

5-5: LGTM! Consider optimizing type order.

The addition of @remix-run/node and vite/client types is correct for the Remix Vite migration. Consider moving vitest/globals first in the array as test types should generally take precedence to avoid any potential conflicts with runtime types.

-    "types": ["@remix-run/node", "vite/client", "vitest/globals"],
+    "types": ["vitest/globals", "@remix-run/node", "vite/client"],
apps/webapp/app/v3/handleWebsockets.server.ts (2)

10-13: Consider improving the state management pattern

While moving the declaration fixes the ReferenceError, the current implementation could be enhanced:

  1. Use const instead of let since the map reference itself doesn't need to be reassigned
  2. Consider encapsulating the connections map within the singleton pattern

Here's a suggested refactor:

-// Moved in front of export const wss because
-// ReferenceError: Cannot access 'authenticatedConnections' before initialization
-let authenticatedConnections: Map<string, AuthenticatedSocketConnection>;
-
-export const wss = singleton("wss", initalizeWebSocketServer);
+const authenticatedConnections = new Map<string, AuthenticatedSocketConnection>();
+
+export const wss = singleton("wss", () => initalizeWebSocketServer(authenticatedConnections));

Then update the initialization function to accept the map:

function initalizeWebSocketServer(
  connections: Map<string, AuthenticatedSocketConnection>
) {
  const server = new WebSocketServer({ noServer: true });
  server.on("connection", (ws, req) => 
    handleWebSocketConnection(ws, req, connections)
  );
  // ... rest of the function
}

Line range hint 1-9: Consider separating metrics setup from WebSocket handling

The file currently handles both WebSocket server initialization and metrics setup. Consider extracting the metrics setup into a separate module for better separation of concerns.

You could create a new file websocketMetrics.server.ts to handle all WebSocket-related metrics, making the code more modular and easier to maintain.

Example structure:

// websocketMetrics.server.ts
export function initializeWebSocketMetrics(connections: Map<string, AuthenticatedSocketConnection>) {
  return new Gauge({
    name: "dev_authenticated_connections",
    help: "Number of authenticated dev connections",
    collect() {
      this.set(connections.size);
    },
    registers: [metricsRegister],
  });
}
package.json (2)

Line range hint 39-39: Consider using stable TypeScript version

TypeScript ^5.5.4 is currently in beta/RC. Consider using the latest stable version (5.3.x) to avoid potential issues.

-    "typescript": "^5.5.4",
+    "typescript": "^5.3.3",

Line range hint 38-38: Standardize Node.js version

The @types/node version (20.14.14) suggests Node.js 20 LTS usage. As noted in the PR objectives, there's uncertainty about the intended Node version. Consider:

  1. Adding "engines" field to explicitly specify Node.js version requirements
  2. Adding .nvmrc file for version management
{
+  "engines": {
+    "node": ">=20.0.0 <21.0.0"
+  },
   "devDependencies": {
apps/webapp/app/root.tsx (2)

Line range hint 31-34: Consider removing fixed viewport width.

The viewport meta tag has a fixed width of 1024px which might affect responsiveness on smaller devices:

{
  name: "viewport",
  content: "width=1024, initial-scale=1",
}

Consider using a responsive viewport setting instead:

-  content: "width=1024, initial-scale=1",
+  content: "width=device-width, initial-scale=1",

Line range hint 1-57: Successfully migrated to Remix Vite.

The implementation correctly follows Remix Vite patterns:

  • Proper asset imports with ?url suffix
  • No barrel imports (addressing the mentioned navigation issues)
  • Clean separation of concerns in the root component

For future maintenance, consider documenting these Vite-specific patterns in your contribution guidelines to maintain consistency across the codebase.

apps/webapp/package.json (1)

231-232: Review dotenv implementation and CJS interop strategy

New dependencies added:

  1. dotenv-cli and dotenv-expand: The PR mentions concerns about the dotenv solution. Consider documenting the rationale for using these packages over alternatives.
  2. vite-plugin-cjs-interop: This is added to resolve import-related issues, but ensure it's only needed for specific CommonJS modules that can't be converted to ESM.

Consider:

  1. Document the dotenv strategy in the README
  2. Create a migration plan to gradually convert CommonJS modules to ESM to reduce reliance on CJS interop

Also applies to: 254-254

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.v3.$projectParam.environment-variables.new/route.tsx (5)

50-52: Add license attribution for dotenv code.

Since this code is copied from the dotenv library, please add proper license attribution to comply with open-source licensing requirements.


53-91: Improve type safety and maintainability of the parseEnv function.

The function has several areas that could be improved:

  1. The @ts-ignore comment is masking potential type safety issues
  2. The complex regex pattern could be extracted for better maintainability

Consider this refactored version:

+ const ENV_LINE_REGEX = /(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|\s*`(?:\\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/gm;
+
+ interface EnvVars {
+   [key: string]: string;
+ }
+
- function parseEnv(src: string) {
+ function parseEnv(src: string): EnvVars {
-  const LINE =
-    /(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|\s*`(?:\\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/gm;
-  const obj = {};
+  const obj: EnvVars = {};

   // Convert buffer to string
   let lines = src.toString();

   // Convert line breaks to same format
   lines = lines.replace(/\r\n?/gm, "\n");

   let match;
-  while ((match = LINE.exec(lines)) != null) {
+  while ((match = ENV_LINE_REGEX.exec(lines)) !== null) {
     const key = match[1];

     // Default undefined or null to empty string
     let value = match[2] || "";

     // Remove whitespace
     value = value.trim();

     // Check if double quoted
     const maybeQuote = value[0];

     // Remove surrounding quotes
     value = value.replace(/^(['"`])([\s\S]*)\1$/gm, "$2");

     // Expand newlines if double quoted
     if (maybeQuote === '"') {
       value = value.replace(/\\n/g, "\n");
       value = value.replace(/\\r/g, "\r");
     }

-    // @ts-ignore
     obj[key] = value;
   }

   return obj;
 }
🧰 Tools
🪛 Biome (1.9.4)

[error] 65-65: The assignment should not be in an expression.

The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.

(lint/suspicious/noAssignInExpressions)


Line range hint 409-424: Improve error handling and clarity in handlePaste function.

The paste handling could be more robust and clearer:

  1. Missing error handling for malformed input
  2. Unclear comment about default paste behavior

Consider this improved version:

   const variables = parseEnv(text);
   const keyValuePairs = Object.entries(variables).map(([key, value]) => ({ key, value }));

-  //do the default paste
+  // If no valid environment variables were parsed, let the browser handle the paste
   if (keyValuePairs.length === 0) return;

+  try {
     //prevent default pasting
     e.preventDefault();

     const [firstPair, ...rest] = keyValuePairs;
     update(index, firstPair);

     for (const pair of rest) {
       requestIntent(formRef.current ?? undefined, list.append(variablesFields.name));
     }
     insertAfter(index, rest);
+  } catch (error) {
+    console.error('Failed to process pasted environment variables:', error);
+    // Let the browser handle the paste as fallback
+    return;
+  }

Line range hint 466-468: Add security warning for clipboard operations.

Consider adding a warning about pasting sensitive data, as clipboard content could be intercepted by other applications.

   <div className={cn("flex items-center justify-between gap-4", items.length > 1 && "pr-10")}>
     <Paragraph variant="extra-small">
-      Tip: Paste your .env into this form to populate it.
+      Tip: Paste your .env into this form to populate it. Be cautious when copying sensitive data to clipboard.
     </Paragraph>
🧰 Tools
🪛 Biome (1.9.4)

[error] 65-65: The assignment should not be in an expression.

The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.

(lint/suspicious/noAssignInExpressions)


Based on the code analysis performed, I can now generate the final response:

Add a maximum limit validation for environment variables bulk paste operation

The current implementation allows unlimited environment variables to be pasted, which could lead to performance issues or potential abuse. While there's validation for minimum array size in other parts of the codebase (e.g., schedules), there's no maximum limit validation for environment variables.

  • Add a reasonable maximum limit (e.g., 100) to the variables schema in the form validation
  • Implement a check in the paste handler to prevent exceeding this limit
  • Consider showing a user-friendly error message when the limit is exceeded
🔗 Analysis chain

Line range hint 409-424: Add validation for maximum number of environment variables.

Consider adding a limit to the number of environment variables that can be pasted to prevent potential abuse or performance issues.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if there are any existing limits on environment variables in the codebase
rg -A 5 "MAX_ENV_VARS|maxEnvVars|MAX_ENVIRONMENT_VARIABLES" 

# Check for any validation schemas that might already handle this
ast-grep --pattern 'z.array($_, { max: $_ })'

Length of output: 109


Script:

#!/bin/bash
# Let's check for any validation related to environment variables using broader patterns
rg -i "environment.*variable.*validation|env.*var.*validation" -A 5

# Check for any zod schema definitions related to environment variables
rg -i "z\..*environment" -A 5

# Look for any array validation in the specific file
cat "apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.v3.$projectParam.environment-variables.new/route.tsx"

# Check for any existing validation in the form handling
ast-grep --pattern 'const $_ = z.array($_)'

Length of output: 11393


Script:

#!/bin/bash
# Let's check for any existing array size limits in the codebase
rg -i "z\.array.*max|z\.array.*min" -A 2

# Check for any validation in the specific route file
rg "variables.*validation|variables.*limit" "apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.v3.$projectParam.environment-variables.new/route.tsx"

# Look for any API-level validation for environment variables
rg "createEnvironmentVariables|addEnvironmentVariables" -A 5

Length of output: 566

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 3792394 and 2e05691.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (18)
  • apps/webapp/app/entry.server.tsx (1 hunks)
  • apps/webapp/app/root.tsx (1 hunks)
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.v3.$projectParam.environment-variables.new/route.tsx (2 hunks)
  • apps/webapp/app/routes/auth.github.callback.tsx (1 hunks)
  • apps/webapp/app/routes/auth.github.server.ts (1 hunks)
  • apps/webapp/app/routes/auth.github.ts (1 hunks)
  • apps/webapp/app/v3/eventRepository.server.ts (1 hunks)
  • apps/webapp/app/v3/handleWebsockets.server.ts (1 hunks)
  • apps/webapp/package.json (6 hunks)
  • apps/webapp/postcss.config.js (1 hunks)
  • apps/webapp/remix.config.js (0 hunks)
  • apps/webapp/remix.env.d.ts (0 hunks)
  • apps/webapp/server.js (1 hunks)
  • apps/webapp/server.ts (0 hunks)
  • apps/webapp/server/app.ts (1 hunks)
  • apps/webapp/tsconfig.json (1 hunks)
  • apps/webapp/vite.config.ts (1 hunks)
  • package.json (1 hunks)
💤 Files with no reviewable changes (3)
  • apps/webapp/remix.config.js
  • apps/webapp/remix.env.d.ts
  • apps/webapp/server.ts
✅ Files skipped from review due to trivial changes (2)
  • apps/webapp/app/entry.server.tsx
  • apps/webapp/postcss.config.js
🧰 Additional context used
🪛 Biome (1.9.4)
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.v3.$projectParam.environment-variables.new/route.tsx

[error] 65-65: The assignment should not be in an expression.

The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.

(lint/suspicious/noAssignInExpressions)

🔇 Additional comments (19)
apps/webapp/vite.config.ts (4)

16-16: Evaluate the necessity of installGlobals({ nativeFetch: true })

The installGlobals function from @remix-run/node is typically used to polyfill globals like fetch in a Node.js environment. Since Node.js versions 18 and above have native support for fetch, verify if this polyfill is required. If your deployment environment uses Node.js 18 or newer, you might be able to remove this.

Consider removing or commenting out this line if not needed:

-installGlobals({ nativeFetch: true });

Ensure that removing this does not break any server-side code relying on fetch.


23-29: Review the use of isSsrBuild for conditional rollupOptions

The isSsrBuild flag is intended for differentiating between SSR and client builds. However, in Vite configuration, you might want to use the ssr build option instead. Ensure that isSsrBuild accurately reflects the build context and that the conditional inclusion of rollupOptions behaves as expected.

Verify that isSsrBuild is correctly determined and that the rollupOptions are applied only during SSR builds.

If needed, adjust the configuration:

-export default defineConfig(({ isSsrBuild }) => ({
+export default defineConfig(({ mode, ssrBuild }) => ({
   // ...
-  rollupOptions: isSsrBuild
+  rollupOptions: ssrBuild

Refer to the Vite SSR Build Options for more details.


76-76: Specify correct path for tsconfigPaths plugin

The tsconfigPaths plugin is configured with projects: ["./tsconfig.json"]. Confirm that the tsconfig.json file is located at this path relative to the vite.config.ts file. If the tsconfig.json is in a different location, update the path accordingly to ensure that TypeScript path aliases are resolved properly.

If the tsconfig.json is in the root directory of apps/webapp, then the path is correct. Otherwise, adjust the path like so:

 tsconfigPaths({ projects: [
-  "./tsconfig.json"
+  "../../tsconfig.json"
 ] }),

This ensures that the TypeScript paths are correctly interpreted during the build.


69-75: ⚠️ Potential issue

Ensure correct conditional inclusion of the Remix plugin

The condition !process.env.VITEST is used to exclude the Remix plugin during testing. Verify that this environment variable is set appropriately when running Vitest to prevent unintended behavior.

Make sure that when running tests with Vitest, the VITEST environment variable is defined. If not, the Remix plugin might interfere with testing.

Consider modifying the condition to:

-!process.env.VITEST &&
+process.env.NODE_ENV !== "test" &&

This approach checks the NODE_ENV variable, which might be more reliable.

Alternatively, ensure that your test scripts set VITEST environment variable correctly.

apps/webapp/server/app.ts (2)

66-68: Confirm comprehensive error handling for sockets

While you're handling socket errors with socket.on("error", ...), ensure that all potential error events are properly managed to prevent unhandled exceptions.

Review the error handling to confirm that no errors can cause the process to crash.


9-13: Verify support for top-level await in your deployment environment

The use of top-level await in imports requires that your Node.js runtime and TypeScript compiler support ES modules and top-level await. Ensure that your environment is properly configured to handle this feature.

Run the following script to check your TypeScript configuration and Node.js version:

✅ Verification successful

Let me analyze the configuration results more deeply to verify the TypeScript settings specifically for the webapp directory.


Top-level await is properly supported in your environment

The configuration is correctly set up for top-level await:

  • Node.js version (22.9.0) well exceeds the minimum requirement
  • The webapp's tsconfig.json has "module": "esnext" and uses module bundler resolution
  • Package.json is configured with "type": "module"
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that top-level await is supported in your environment.

# Test: Check TypeScript configuration.
# Expected: "module" should be "esnext" and "target" should be "es2022" or higher.
if fd tsconfig.json | xargs -I {} jq '."compilerOptions".module, ."compilerOptions".target' {} ; then
  echo "Checked tsconfig.json for correct module and target settings."
else
  echo "Ensure that 'module' is set to 'esnext' and 'target' is set to 'es2022' or higher in tsconfig.json."
fi

# Test: Check Node.js version.
# Expected: Node.js version should be 14.8.0 or higher to support top-level await.
node_version=$(node -v | sed 's/v//')
required_version="14.8.0"
if [ "$(printf '%s\n' "$required_version" "$node_version" | sort -V | head -n1)" = "$required_version" ]; then
  echo "Node.js version is sufficient for top-level await."
else
  echo "Node.js version is too low. Please upgrade to Node.js 14.8.0 or higher."
fi

Length of output: 1135


Script:

#!/bin/bash
# Check TypeScript configuration specifically for the webapp directory
fd tsconfig.json apps/webapp | xargs -I {} sh -c 'echo "=== {} ==="; cat {}; echo'

# Also check package.json for type field
fd package.json apps/webapp | xargs -I {} sh -c 'echo "=== {} ==="; jq ".type" {}; echo'

Length of output: 1983

apps/webapp/server.js (1)

71-73: Review the condition for the health check endpoint

The health check endpoint is registered only if process.env.DASHBOARD_AND_API_DISABLED === "true". This logic might be unintuitive since disabling the dashboard and API also disables the health check. Verify if this is the intended behavior.

Ensure the health check endpoint is available as expected in different configurations.

apps/webapp/app/v3/eventRepository.server.ts (1)

25-25: Verify compatibility of 'EventEmitter' import change

The import of EventEmitter has been changed from "node:stream" to "node:events". While this aligns with the standard Node.js module, ensure that all usages of EventEmitter in your codebase are compatible with this change, especially if the project supports older Node.js versions where EventEmitter may have been exported differently.

You can run the following script to check for other imports of EventEmitter from "node:stream":

✅ Verification successful

Based on the search results, I can see that all imports of EventEmitter in the codebase are consistently using "node:events" and there are no imports from "node:stream". The change aligns with the standard Node.js module location for EventEmitter. Additionally, looking at the package.json files, the project has a minimum Node.js requirement of >=18.20.0 where this module structure is fully supported.

Import change from 'node:stream' to 'node:events' is correct and safe

The change to import EventEmitter from "node:events" is the correct approach as:

  • All other EventEmitter imports in the codebase use "node:events"
  • This is the standard location for EventEmitter in modern Node.js
  • The project's Node.js version requirements (>=18.20.0) fully support this module structure
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Find import statements of EventEmitter from 'node:stream'

rg 'import\s+{\s*EventEmitter\s*}\s+from\s+"node:stream"' -t js

Length of output: 210


Script:

#!/bin/bash
# Search for EventEmitter imports from both node:stream and node:events
rg "import.*EventEmitter.*from ['\"]node:(stream|events)['\"]"

# Also check for any usage of EventEmitter to understand the context
rg "EventEmitter"

# Check the Node.js version requirements in package.json
fd "package.json" --exec cat {}

Length of output: 64400

apps/webapp/app/routes/auth.github.server.ts (1)

1-6: Verify cookie import updates across the codebase

The cookie has been moved from auth.github.ts to this new server file. Let's verify all imports have been updated correctly.

✅ Verification successful

All cookie imports have been correctly updated

The verification shows that:

  • No old imports from auth.github remain
  • Two files correctly import from the new location auth.github.server:
    • apps/webapp/app/routes/auth.github.ts
    • apps/webapp/app/routes/auth.github.callback.tsx
  • The cookie name "redirect-to" is only referenced in the cookie creation, with no stray usages
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for any remaining imports from the old location and verify new imports

echo "Checking for old imports that need updating..."
rg "import.*redirectCookie.*from.*auth\.github[\"']" --type ts

echo "Verifying new import usage..."
rg "import.*redirectCookie.*from.*auth\.github\.server[\"']" --type ts

echo "Checking for any direct cookie name references that might need updating..."
rg "redirect-to" --type ts

Length of output: 800

apps/webapp/app/routes/auth.github.callback.tsx (2)

3-3: Verify cookie functionality remains intact.

The code uses redirectCookie for parsing and handling redirect URLs during authentication. Since this is a critical part of the authentication flow, we should verify the cookie functionality.

Let's check the cookie implementation:

Also applies to: 7-9

✅ Verification successful

Cookie functionality is properly implemented and maintained

The cookie implementation is consistent and working as expected across the authentication flow:

  • In auth.github.server.ts: Cookie is properly configured with security settings (httpOnly) and reasonable expiration (1 hour)
  • In auth.github.ts: Cookie is correctly set during redirect handling
  • In auth.github.callback.tsx: Cookie is properly parsed and used for redirection after authentication

The changes maintain the expected cookie functionality for handling authentication redirects.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the cookie implementation in the server file

# Look for cookie creation/configuration
ast-grep --pattern 'createCookie($$$)' 

# Check for any other routes using this cookie to ensure consistent usage
rg -l "redirectCookie" apps/webapp/app/routes/

Length of output: 517


Script:

#!/bin/bash
# Let's examine how the cookie is used across these files
rg "redirectCookie" apps/webapp/app/routes/auth.github.ts -A 5 -B 5
rg "redirectCookie" apps/webapp/app/routes/auth.github.callback.tsx -A 5 -B 5

Length of output: 1363


3-3: LGTM! Verify server file exists.

The import path change to include .server suffix aligns with Remix's convention for server-only code, which is appropriate for cookie handling.

Let's verify the server file exists and exports the required cookie:

✅ Verification successful

Import path change verified successfully

The verification confirms that:

  • The file apps/webapp/app/routes/auth.github.server.ts exists
  • It properly declares and exports redirectCookie using createCookie with appropriate security settings (httpOnly, maxAge)
  • The import path change in the callback route is correct
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the auth.github.server.ts file exists and exports redirectCookie

# Check if the file exists in the same directory
fd -e ts "auth.github.server.ts" apps/webapp/app/routes

# Verify the redirectCookie export
ast-grep --pattern 'export { redirectCookie }'

Length of output: 149


Script:

#!/bin/bash
# Let's verify the redirectCookie declaration in the server file
ast-grep --pattern 'const redirectCookie = $_'

# Also check if it's imported from somewhere else
rg "redirectCookie.*from" apps/webapp/app/routes/auth.github.server.ts

Length of output: 4118

apps/webapp/app/routes/auth.github.ts (1)

4-4: Verify cookie configuration consistency

The redirectCookie has been moved from local definition to a separate server file. Let's ensure the configuration remains consistent.

✅ Verification successful

Cookie configuration is properly maintained after relocation

The redirectCookie has been correctly moved to the server file with the same configuration:

  • Name: "redirect-to"
  • maxAge: 1 hour (60 * 60 seconds)
  • httpOnly: true

The cookie is properly imported and used in the authentication flow for storing the redirect URL during the GitHub OAuth process.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check cookie configuration in the server file
# Look for createCookie configuration to verify maxAge and httpOnly settings

rg -A 5 "createCookie.*redirectCookie" "./auth.github.server"

Length of output: 122


Script:

#!/bin/bash
# First, let's find the correct path to the auth.github.server file
fd "auth.github.server"

Length of output: 68


Script:

#!/bin/bash
# Now let's check the cookie configuration in the correct file
rg -A 5 "createCookie.*redirectCookie" "apps/webapp/app/routes/auth.github.server.ts"

# Also let's check the git history to see the previous configuration
git show HEAD -- apps/webapp/app/routes/auth.github.ts | rg -A 5 "createCookie.*redirectCookie"

Length of output: 183


Script:

#!/bin/bash
# Let's try a broader search for the cookie definition
rg -A 5 "redirectCookie" "apps/webapp/app/routes/auth.github.server.ts"

# And let's see the entire content of both files to understand the context better
echo "=== auth.github.server.ts ==="
cat "apps/webapp/app/routes/auth.github.server.ts"
echo -e "\n=== auth.github.ts ==="
cat "apps/webapp/app/routes/auth.github.ts"

Length of output: 1668

apps/webapp/tsconfig.json (1)

3-3: Verify type definitions after removing remix.env.d.ts

The removal of remix.env.d.ts from includes aligns with the Remix Vite migration, but we should verify that all Remix-specific types are still properly handled.

✅ Verification successful

Let me check the content of vite.config.ts to see if it includes any type definitions that might replace remix.env.d.ts.


Let me check one final thing to verify how the Remix types are being handled in the new setup.


Removal of remix.env.d.ts is safe as types are properly handled through @remix-run packages

The removal of remix.env.d.ts from includes is correct and aligns with the Remix Vite migration. The types are now being handled through:

  • Direct imports from @remix-run/node (as seen in vite.config.ts)
  • Vite's built-in type definitions through vite/client
  • Global type declarations are properly managed through global.d.ts which is included in tsconfig.json
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for any remaining references to remix.env.d.ts and verify new type definition setup

# Check for any remaining references to remix.env.d.ts
rg -l "remix\.env\.d\.ts"

# Verify if there's a new type definition file for Remix
fd -e d.ts -e ts -p "remix|vite"

Length of output: 315


Script:

#!/bin/bash
# Check vite.config.ts content and search for type-related configurations
cat apps/webapp/vite.config.ts

# Also check if there are any type definitions in node_modules that might be automatically included
rg -l "declare global" --type ts

Length of output: 2804


Script:

#!/bin/bash
# Check for Remix-specific type imports and declarations
rg -l "@remix-run.*types?" --type ts

# Also check the content of the global declarations we found
cat apps/webapp/app/services/worker.server.ts

Length of output: 26734

apps/webapp/app/v3/handleWebsockets.server.ts (1)

14-14: Verify singleton behavior with Vite HMR

Since this is part of the Remix Vite upgrade, ensure the WebSocket server singleton properly handles Hot Module Replacement and server reloads.

Let's check for proper cleanup handling in the server code:

✅ Verification successful

Let me gather more information about the singleton implementation and WebSocket server initialization.


WebSocket singleton implementation is properly handled

The WebSocket server implementation already includes proper cleanup handling:

  • The server is initialized in noServer mode, which is compatible with HMR
  • Connections are tracked in a Map and properly cleaned up when closed
  • Each connection has an onClose handler that removes it from the tracking Map
  • The singleton pattern ensures only one WebSocket server instance exists
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if there's proper cleanup handling for the WebSocket server
# Look for dispose/cleanup patterns in server files

# Search for cleanup patterns in server files
echo "Searching for cleanup handlers..."
rg -l "beforeClose|onDispose|cleanup|teardown" apps/webapp/server

# Check the new server structure for WebSocket initialization
echo "Checking WebSocket initialization in new server structure..."
rg -A 5 "handleWebsockets" apps/webapp/server

Length of output: 825


Script:

#!/bin/bash
# Check the singleton implementation and WebSocket server initialization
echo "Checking singleton implementation..."
rg -A 10 "singleton.*wss" apps/webapp

echo "\nChecking WebSocket server initialization..."
rg -A 10 "initalizeWebSocketServer" apps/webapp

echo "\nChecking for any existing cleanup or dispose patterns..."
rg -A 5 "close|dispose|cleanup|destroy" apps/webapp/app/v3/handleWebsockets.server.ts

Length of output: 2932

package.json (1)

61-61: Verify Vite 5.x upgrade compatibility

The upgrade from Vite 4.x to 5.x is a major version bump that may include breaking changes. Please ensure all Vite plugins and configurations are compatible with Vite 5.

✅ Verification successful

Let me check the Vite configuration files to understand how the plugins are being used and verify compatibility.


Based on the analysis of the Vite configuration files and dependencies, I can now provide a final response:

Vite 5.x upgrade is properly configured and compatible

The upgrade from Vite 4.x to 5.x has been implemented correctly with all necessary supporting changes:

  • The Vite configuration in apps/webapp/vite.config.ts uses compatible plugins:
    • vite-tsconfig-paths v4.3.2
    • vite-plugin-cjs-interop v2.1.5
    • @remix-run/dev v2.15.0 with Vite plugin support
  • The configuration includes proper CommonJS interop settings for dependencies
  • Node.js polyfills are correctly configured in the resolve.alias section
  • The Vite server integration in server.js follows the recommended setup for Remix
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for Vite plugins that might need updates
rg '"vite-plugin-|@vitejs/' -A 1 $(fd 'package.json' apps packages integrations)

# Look for Vite config files that might need updates
fd 'vite.config' apps packages integrations

Length of output: 1222


Script:

#!/bin/bash
# Check Vite config contents
cat apps/webapp/vite.config.ts packages/cli-v3/vite.config.ts

# Check if there are any Vite-specific issues or errors in recent commits
rg -g '!node_modules' -g '!dist' 'vite' -A 2 -B 2

Length of output: 101502

apps/webapp/app/root.tsx (2)

8-8: LGTM! Correct Vite asset import pattern.

The ?url suffix is the correct way to handle CSS imports in Vite, ensuring proper asset handling during build.

Let's verify this pattern is consistently applied across other stylesheets:

✅ Verification successful

Inconsistent CSS import patterns detected - update required

The codebase shows inconsistent CSS import patterns:

  • root.tsx correctly uses ?url suffix for Vite
  • renderMarkdown.server.ts uses direct CSS import without the suffix

Since this is a Vite migration, all client-side CSS imports should use the ?url suffix. However, the import in renderMarkdown.server.ts might be server-side only. Let's verify this:


Tailwind CSS import pattern is correctly implemented

The ?url suffix in root.tsx is correctly applied for client-side CSS. The other CSS import in renderMarkdown.server.ts is part of a server-side module (indicated by .server.ts extension) used for syntax highlighting with Prism.js, which doesn't require the Vite-specific ?url suffix as it's not served directly to the client.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other CSS imports to ensure consistent usage of ?url suffix
rg -g '*.tsx' -g '*.ts' '\.css(\?url)?' apps/webapp/app

Length of output: 250


Script:

#!/bin/bash
# Check the context of the CSS import in renderMarkdown.server.ts
rg -B 5 -A 5 "prismjs/plugins/line-numbers/prism-line-numbers.css" apps/webapp/app/services/renderMarkdown.server.ts

Length of output: 549


Line range hint 42-57: Review environment variable exposure to the client.

While the loader implementation is solid, please verify that it's safe to expose these environment variables to the client:

  • posthogProjectKey
  • highlightProjectId
  • appEnv
  • appOrigin

Let's check how these environment variables are defined and used:

✅ Verification successful

Environment variables are safely exposed to the client

The exposed environment variables are safe to share with the client-side:

  • POSTHOG_PROJECT_KEY is a public PostHog key (starts with phc_) used for analytics initialization
  • HIGHLIGHT_PROJECT_ID is an optional public key for the Highlight monitoring service
  • APP_ENV and APP_ORIGIN are non-sensitive configuration values needed for client operation

The implementation follows best practices:

  • Analytics keys are properly used for their intended client-side initialization
  • Sensitive keys (like AUTH_GITHUB_CLIENT_SECRET, RESEND_API_KEY) are not exposed
  • Environment variables are validated through a zod schema
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check environment variable definitions and usage
echo "Checking env.server.ts file..."
rg -A 5 "POSTHOG_PROJECT_KEY|HIGHLIGHT_PROJECT_ID|APP_ENV|APP_ORIGIN" apps/webapp/app/env.server.ts

echo "Checking for sensitive usage patterns..."
ast-grep --pattern 'process.env.$_' apps/webapp/app/env.server.ts

Length of output: 1307


Script:

#!/bin/bash
# Let's check how these variables are used in the frontend
echo "Checking PostHog usage..."
rg -A 5 "posthogProjectKey|POSTHOG_PROJECT_KEY" apps/webapp/app --type ts --type tsx

echo "Checking Highlight usage..."
rg -A 5 "highlightProjectId|HIGHLIGHT_PROJECT_ID" apps/webapp/app --type ts --type tsx

echo "Checking if these are public keys by looking at documentation or comments..."
rg -i "posthog|highlight" apps/webapp/app --type md --type txt

Length of output: 585


Script:

#!/bin/bash
# Let's try with correct file extensions
echo "Checking PostHog usage..."
rg -A 5 "posthogProjectKey|POSTHOG_PROJECT_KEY" apps/webapp/app

echo "Checking Highlight usage..."
rg -A 5 "highlightProjectId|HIGHLIGHT_PROJECT_ID" apps/webapp/app

echo "Checking entry points for analytics initialization..."
rg -A 10 "posthog|highlight" apps/webapp/app/entry.client.tsx || true
rg -A 10 "posthog|highlight" apps/webapp/app/root.tsx || true

Length of output: 5463

apps/webapp/package.json (2)

6-6: Verify CommonJS compatibility with "type": "module"

The switch to ES modules might affect existing CommonJS dependencies. Ensure all dependencies support ESM, especially internal workspace packages.

✅ Verification successful

The search results show that most require() and module.exports usage is in configuration files (tailwind, prettier, jest, eslint) which commonly use CommonJS format and are not affected by the "type": "module" in package.json. There's only one instance in production code (redis-worker) and one comment about CommonJS module in vite.config.ts.

Let's verify if these files are properly handled by checking their package.json configurations:


ES Modules configuration is properly set up and compatible

The module system configuration is correctly handled across the codebase:

  • Configuration files (tailwind, prettier, eslint) use the .js or .cjs extension, which allows them to remain as CommonJS regardless of package.json settings
  • The redis-worker package already has "type": "module" set, ensuring compatibility
  • The only production require() usage in redis-worker can be safely converted to an import statement
  • No other CommonJS dependencies issues were found that would affect the webapp's switch to ES modules
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for potential CommonJS compatibility issues
# Look for require() calls that might need to be converted to import
rg "require\(" --type js --type ts

# Look for module.exports that might need to be converted to export
rg "module\.exports" --type js --type ts

Length of output: 1229


Script:

#!/bin/bash
# Check package.json files for these directories
echo "=== redis-worker package.json ==="
cat internal-packages/redis-worker/package.json 2>/dev/null || echo "No package.json found"

# Check if there are any .cjs files which are explicitly marked as CommonJS
echo -e "\n=== Looking for .cjs files ==="
fd -e cjs

Length of output: 887


9-11: Review memory allocation and Node.js configuration

A few concerns about the script configurations:

  1. The --max-old-space-size=8192 (8GB) setting might be excessive for some environments
  2. The NODE_PATH configuration in dev:worker seems specific to pnpm - verify if this is still needed with Vite

Consider making the memory limit configurable through an environment variable.

Also applies to: 14-15

✅ Verification successful

Let me analyze the actual usage context of these configurations to provide a more accurate assessment.


Memory and NODE_PATH configurations are appropriately set

The configurations are justified based on the codebase analysis:

  1. The 8GB memory allocation (--max-old-space-size=8192) is consistently used across production environments (docker, start scripts) and appears intentional for handling the Vite SSR bundling and server operations.

  2. The NODE_PATH configuration in dev:worker is necessary as it's part of a consistent pattern:

    • Used in Docker environment for production
    • Used in e2e testing utilities
    • Specifically required for pnpm module resolution in monorepo setup

No changes are needed as these configurations are deliberate architectural decisions rather than oversight.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for memory-related configurations in other files
rg -l "max-old-space-size" 

# Check for NODE_PATH usage
rg -l "NODE_PATH"

Length of output: 181


Script:

#!/bin/bash
# Check the content of files with memory configurations
rg "max-old-space-size" -A 2 -B 2

# Check NODE_PATH usage context
rg "NODE_PATH" -A 2 -B 2

# Check server.js content for node path usage
cat apps/webapp/server.js

Length of output: 5891

apps/webapp/vite.config.ts Outdated Show resolved Hide resolved
Comment on lines +10 to +14
expand(
config({
path: "../../.env",
})
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Avoid loading environment variables in the Vite config file

Loading environment variables using dotenv and dotenv-expand directly in the Vite configuration can lead to inconsistencies and potential security risks. Vite automatically loads environment variables from .env files into import.meta.env. It's recommended to leverage Vite's built-in mechanism for handling environment variables.

Apply this diff to remove the manual loading and rely on Vite's functionality:

-expand(
-  config({
-    path: "../../.env",
-  })
-);

Place your .env file in the root of your project (or as per Vite's conventions) so that Vite can automatically pick it up.

For more information, refer to the Vite Env Variables and Modes documentation.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
expand(
config({
path: "../../.env",
})
);

Comment on lines +48 to +68
cjsInterop({
dependencies: [
// The requested module 'cron-parser' is a CommonJS module, which may not support all module.exports as named exports.
// CommonJS modules can always be imported via the default export, for example using:
// import pkg from 'cron-parser';
// const {parseExpression} = pkg;
"@prisma/instrumentation",
"cron-parser",
"react-use",
"regression",
// import * as TooltipPrimitive from "@radix-ui/react-tooltip"
// Tooltip.displayName = …
// TypeError: Cannot read properties of undefined (reading 'displayName')
"@radix-ui/react-tooltip",
"@radix-ui/react-popover",
"@radix-ui/react-dialog",
"@radix-ui/react-label",
"@radix-ui/react-select",
"@radix-ui/react-tabs",
],
}),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

Some dependencies can be removed from cjsInterop configuration

Based on the npm registry data, several dependencies can be removed from the cjsInterop plugin configuration as they already support ES Modules:

  • All @radix-ui/* packages (they expose ESM via ./dist/index.mjs)
  • react-use (exposes ESM via esm/index.js)

The following should remain in the configuration as they don't show ESM support:

  • @prisma/instrumentation
  • cron-parser
  • regression
🔗 Analysis chain

Review the dependencies in cjsInterop plugin configuration

The cjsInterop plugin is configured with a list of dependencies for CommonJS interop. Verify if all the listed packages still require this interop. Some packages might have updated to provide ESM exports, making the plugin unnecessary for them.

Run the following script to check if the packages have ESM support:

Based on the results, you might remove packages that no longer need the interop, simplifying your configuration.

Additionally, consider whether you can use named imports or default imports directly without the plugin for some packages.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if dependencies support ES Modules

for pkg in "@prisma/instrumentation" "cron-parser" "react-use" "regression" "@radix-ui/react-tooltip" "@radix-ui/react-popover" "@radix-ui/react-dialog" "@radix-ui/react-label" "@radix-ui/react-select" "@radix-ui/react-tabs"; do
  echo "Checking $pkg..."
  npm view "$pkg" module || echo "$pkg does not have 'module' field indicating ESM support."
done

Length of output: 3476

const server = createServer(app);

// WebSocket setup
socketIo?.io.attach(server);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Ensure socketIo is properly initialized before attaching

The optional chaining in socketIo?.io.attach(server); means that if socketIo is undefined, the Socket.IO server will not attach, potentially leading to missing WebSocket functionalities.

Ensure that socketIo is correctly imported and not undefined:

- socketIo?.io.attach(server);
+ if (socketIo) {
+   socketIo.io.attach(server);
+ } else {
+   console.error("socketIo is undefined. Socket.IO server not attached.");
+ }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
socketIo?.io.attach(server);
if (socketIo) {
socketIo.io.attach(server);
} else {
console.error("socketIo is undefined. Socket.IO server not attached.");
}


// WebSocket setup
socketIo?.io.attach(server);
server.removeAllListeners("upgrade"); // prevent duplicate upgrades from listeners created by io.attach()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Be cautious when removing all 'upgrade' event listeners

Using server.removeAllListeners("upgrade"); may unintentionally remove other 'upgrade' event listeners that are essential for your application or middleware.

Consider removing only the specific listener added by io.attach() to prevent duplicate upgrades without affecting other listeners:

- server.removeAllListeners("upgrade"); // prevent duplicate upgrades from listeners created by io.attach()
+ // Remove the specific 'upgrade' listener added by socket.io to prevent duplicates
+ const upgradeListeners = server.listeners("upgrade");
+ const socketIoListener = upgradeListeners.find(listener => listener.name === 'bound dispatch');
+ if (socketIoListener) {
+   server.removeListener("upgrade", socketIoListener);
+ }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
server.removeAllListeners("upgrade"); // prevent duplicate upgrades from listeners created by io.attach()
// Remove the specific 'upgrade' listener added by socket.io to prevent duplicates
const upgradeListeners = server.listeners("upgrade");
const socketIoListener = upgradeListeners.find(listener => listener.name === 'bound dispatch');
if (socketIoListener) {
server.removeListener("upgrade", socketIoListener);
}

apps/webapp/server.js Outdated Show resolved Hide resolved

// Security and URL cleanup middleware
app.use((req, res, next) => {
res.set("Strict-Transport-Security", `max-age=${60 * 60 * 24 * 365 * 100}`);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Set a realistic max-age for the HSTS header

The Strict-Transport-Security header sets max-age to 100 years (60 * 60 * 24 * 365 * 100). This duration is excessively long and may not be practical. Consider setting it to 1 year (31536000 seconds) and include includeSubDomains and preload directives for better security.

Apply this diff to adjust the header:

-  res.set("Strict-Transport-Security", `max-age=${60 * 60 * 24 * 365 * 100}`);
+  res.set("Strict-Transport-Security", "max-age=31536000; includeSubDomains; preload");
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
res.set("Strict-Transport-Security", `max-age=${60 * 60 * 24 * 365 * 100}`);
res.set("Strict-Transport-Security", "max-age=31536000; includeSubDomains; preload");

Comment on lines +88 to +98
const server = app.listen(port, async () => {
try {
// Ping the server and send an initial request
// That will trigger the Remix handler and should guarantee that Remix will break immediately if there's an issue
await performHealthcheck(port);
console.log(`✅ server ready: http://localhost:${port} [NODE_ENV: ${MODE}]`);
} catch (error) {
console.error("Server started but healthcheck failed:", error);
process.exit(1);
}
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Ensure server starts after middleware is set up

The server starts listening before asynchronous middleware (like Vite server) is fully initialized. This might cause incoming requests to hit uninitialized middleware. Consider restructuring the code to start the server after all middleware is ready.

Refactor the server initialization:

-const server = app.listen(port, async () => {
-  try {
-    await performHealthcheck(port);
-    console.log(`✅ server ready: http://localhost:${port} [NODE_ENV: ${MODE}]`);
-  } catch (error) {
-    console.error("Server started but healthcheck failed:", error);
-    process.exit(1);
-  }
-});
+async function startServer() {
+  try {
+    await performHealthcheck(port);
+    const server = app.listen(port, () => {
+      console.log(`✅ server ready: http://localhost:${port} [NODE_ENV: ${MODE}]`);
+    });
+
+    // Server cleanup
+    server.keepAliveTimeout = 65 * 1000;
+
+    process.on("SIGTERM", () => {
+      server.close((err) => {
+        if (err) {
+          console.error("Error closing express server:", err);
+        } else {
+          console.log("Express server closed gracefully.");
+        }
+      });
+    });
+  } catch (error) {
+    console.error("Server setup failed:", error);
+    process.exit(1);
+  }
+}
+
+await startServer();
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const server = app.listen(port, async () => {
try {
// Ping the server and send an initial request
// That will trigger the Remix handler and should guarantee that Remix will break immediately if there's an issue
await performHealthcheck(port);
console.log(`✅ server ready: http://localhost:${port} [NODE_ENV: ${MODE}]`);
} catch (error) {
console.error("Server started but healthcheck failed:", error);
process.exit(1);
}
});
async function startServer() {
try {
await performHealthcheck(port);
const server = app.listen(port, () => {
console.log(`✅ server ready: http://localhost:${port} [NODE_ENV: ${MODE}]`);
});
// Server cleanup
server.keepAliveTimeout = 65 * 1000;
process.on("SIGTERM", () => {
server.close((err) => {
if (err) {
console.error("Error closing express server:", err);
} else {
console.log("Express server closed gracefully.");
}
});
});
} catch (error) {
console.error("Server setup failed:", error);
process.exit(1);
}
}
await startServer();

Comment on lines +3 to +6
export const redirectCookie = createCookie("redirect-to", {
maxAge: 60 * 60, // 1 hour
httpOnly: true,
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider enhancing cookie security configuration

The basic implementation looks good, but consider adding these security-related cookie attributes:

  • secure: true to ensure HTTPS-only
  • sameSite: 'lax' to prevent CSRF attacks
  • path: '/auth' to restrict cookie scope
 export const redirectCookie = createCookie("redirect-to", {
   maxAge: 60 * 60, // 1 hour
   httpOnly: true,
+  secure: true,
+  sameSite: 'lax',
+  path: '/auth',
 });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const redirectCookie = createCookie("redirect-to", {
maxAge: 60 * 60, // 1 hour
httpOnly: true,
});
export const redirectCookie = createCookie("redirect-to", {
maxAge: 60 * 60, // 1 hour
httpOnly: true,
secure: true,
sameSite: 'lax',
path: '/auth',
});

Comment on lines +82 to +87
"@remix-run/express": "2.15.0",
"@remix-run/node": "2.15.0",
"@remix-run/react": "2.15.0",
"@remix-run/router": "^1.21.0",
"@remix-run/serve": "2.15.0",
"@remix-run/server-runtime": "2.15.0",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Verify Node.js version compatibility

The upgrade includes significant version changes:

  1. Remix packages upgraded to 2.15.0
  2. Node.js types upgraded to ^20.14.14
  3. Added Vite v5

However, the engines field still specifies "node": ">=16.0.0". This should be updated to match the Node.js version required by the new dependencies.

   "engines": {
-    "node": ">=16.0.0"
+    "node": ">=20.0.0"
   }

Also applies to: 191-193, 209-209, 254-255

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Outside diff range and nitpick comments (3)
apps/webapp/vite.config.ts (3)

9-9: Consider implementing Infisical for secure secrets management

The TODO comment suggests replacing dotenv with Infisical. This would be a good security improvement as Infisical provides better secrets management with encryption and access control.

Would you like assistance in implementing the Infisical integration?


71-73: Document the usage of unstable_optimizeDeps flag

While this flag addresses the navigation bug mentioned in the PR objectives, its unstable status warrants documentation and monitoring.

Consider adding a comment explaining why this flag is necessary:

 future: {
+  // Enable unstable_optimizeDeps to fix navigation issues with server dependencies
+  // Track status at: https://github.com/remix-run/remix/issues/[ISSUE_NUMBER]
   unstable_optimizeDeps: true,
 },

31-45: Consider adding more commonly used Node.js module aliases

The current list covers many core modules, but consider adding other commonly used ones to prevent potential resolution issues.

Consider adding these additional aliases:

 resolve: {
   alias: [
     // existing aliases...
+    { find: "util", replacement: "node:util" },
+    { find: "events", replacement: "node:events" },
+    { find: "fs", replacement: "node:fs" },
+    { find: "url", replacement: "node:url" },
   ],
 },
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 2e05691 and 5eb52c4.

📒 Files selected for processing (1)
  • apps/webapp/vite.config.ts (1 hunks)
🔇 Additional comments (1)
apps/webapp/vite.config.ts (1)

23-28: Verify the server file structure implementation

The conditional inclusion of server/app.ts aligns with the PR's server file splitting strategy, but we should verify that all necessary server integrations are properly moved to this file.

✅ Verification successful

Based on the code inspection, I can now provide a final response regarding the server file structure implementation.

Server file structure is properly implemented and follows best practices

The server implementation in apps/webapp/vite.config.ts correctly includes ./server/app.ts for the following reasons:

  • The server/app.ts file exists and contains a well-structured Express application setup
  • The app exports a single Express instance that handles:
    • Registry proxy routes (with feature flag)
    • Request context middleware
    • Dashboard and API routes (with feature flag)
    • WebSocket/Socket.io connections
  • The server code is properly modularized and imported by the main server.js file through Vite's SSR module loading
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify server file structure and imports

# Check if server/app.ts exists and contains Express middleware
echo "Checking server file structure..."
fd -e ts -e js "server.(ts|js)" -x cat {}
fd "app.ts" -p "server/" -x cat {}

# Look for any remaining server integrations that might need to be moved
rg -l "express\.(Router|Application)" --type ts --type js

Length of output: 119769


Script:

#!/bin/bash
# Let's try a different approach to find server-related files and Express usage

# Look for server/app.ts in the repository
fd -e ts "app.ts" -x cat {}

# Look for Express middleware and router usage
rg -A 5 "express\.(Router|Application)" --type ts --type js

# Look for any server-related files
fd -e ts -e js "server" -x cat {}

Length of output: 103722


Script:

#!/bin/bash
# Let's look for server-related files in the apps directory
fd -e ts -e js -p "apps/" "server" -x cat {}

# Look for Express middleware and router usage in apps directory
rg -A 5 "express\.(Router|Application)" --type ts --type js -g "apps/**"

# Look for any app.ts files in apps directory
fd -e ts "app.ts" -p "apps/" -x cat {}

Length of output: 3484

apps/webapp/vite.config.ts Outdated Show resolved Hide resolved
jacobparis and others added 2 commits November 25, 2024 00:12
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Outside diff range and nitpick comments (3)
apps/webapp/server.js (3)

20-25: Consider enhancing the URL cleanup middleware.

The URL cleanup middleware correctly handles trailing slashes, but it could be improved by:

  1. Using req.originalUrl instead of manually concatenating path and query
  2. Adding input validation for the path parameter
-    const query = req.url.slice(req.path.length);
-    const safepath = req.path.slice(0, -1).replace(/\/+/g, "/");
-    res.redirect(301, safepath + query);
+    const safepath = req.path.slice(0, -1).replace(/\/+/g, "/");
+    res.redirect(301, safepath + req.originalUrl.slice(req.path.length));

77-87: Improve logging configuration precision.

The current skip logic for Vite requests could be enhanced:

  1. Use more precise patterns for file extensions
  2. Consider adding request timing for performance monitoring
 app.use(
-  morgan("tiny", {
+  morgan(':method :url :status :response-time ms', {
     skip: (req) => {
-      // Vite dev server logs /@fs/ requests and hot reloads for specific files
-      // Unsure if this is the best way to suppress them from logs
-      if (req.url.startsWith("/@")) return true;
-      if (req.url.match(/\.[jt]sx?$/)) return true;
+      // Skip Vite development server requests
+      if (req.url.match(/^\/@(fs|vite|react-refresh)/)) return true;
+      // Skip source map requests
+      if (req.url.match(/\.(map|[jt]sx?)$/)) return true;
       return false;
     },
   })
 );

101-112: Enhance server cleanup handling.

Consider improving the server cleanup logic:

  1. Add handlers for SIGINT and SIGQUIT signals
  2. Implement a timeout for graceful shutdown
  3. Verify the keepAliveTimeout value aligns with upstream proxies
 server.keepAliveTimeout = 65 * 1000;
+server.headersTimeout = server.keepAliveTimeout + 1000;
+
+const signals = ['SIGTERM', 'SIGINT', 'SIGQUIT'];
+const shutdown = async (signal) => {
+  console.log(`Received ${signal}, starting graceful shutdown...`);
+  const shutdownTimeout = setTimeout(() => {
+    console.error('Shutdown timed out, forcing exit');
+    process.exit(1);
+  }, 10000);
 
-process.on("SIGTERM", () => {
   server.close((err) => {
+    clearTimeout(shutdownTimeout);
     if (err) {
       console.error("Error closing express server:", err);
+      process.exit(1);
     } else {
       console.log("Express server closed gracefully.");
+      process.exit(0);
     }
   });
-});
+};
+
+signals.forEach(signal => {
+  process.on(signal, () => shutdown(signal));
+});
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 5eb52c4 and 3cfc949.

📒 Files selected for processing (2)
  • apps/webapp/server.js (1 hunks)
  • apps/webapp/vite.config.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/webapp/vite.config.ts

Comment on lines +65 to +66
app.use("/assets", express.static("build/client/assets", { immutable: true, maxAge: "1y" }));
app.use(express.static("build/client", { maxAge: "1h" }));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add security headers for static file serving.

Consider adding security headers for static file serving to prevent potential security issues.

-    app.use("/assets", express.static("build/client/assets", { immutable: true, maxAge: "1y" }));
-    app.use(express.static("build/client", { maxAge: "1h" }));
+    app.use("/assets", express.static("build/client/assets", {
+      immutable: true,
+      maxAge: "1y",
+      setHeaders: (res) => {
+        res.set('X-Content-Type-Options', 'nosniff');
+        res.set('Cache-Control', 'no-transform');
+      }
+    }));
+    app.use(express.static("build/client", {
+      maxAge: "1h",
+      setHeaders: (res) => {
+        res.set('X-Content-Type-Options', 'nosniff');
+      }
+    }));
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
app.use("/assets", express.static("build/client/assets", { immutable: true, maxAge: "1y" }));
app.use(express.static("build/client", { maxAge: "1h" }));
app.use("/assets", express.static("build/client/assets", {
immutable: true,
maxAge: "1y",
setHeaders: (res) => {
res.set('X-Content-Type-Options', 'nosniff');
res.set('Cache-Control', 'no-transform');
}
}));
app.use(express.static("build/client", {
maxAge: "1h",
setHeaders: (res) => {
res.set('X-Content-Type-Options', 'nosniff');
}
}));

Comment on lines +44 to +56
app.use(async (req, res, next) => {
try {
/** @type {import("./server/app.ts")} */
const source = await viteServer.ssrLoadModule("./server/app.ts");

return source.app(req, res, next);
} catch (error) {
if (error instanceof Error) {
viteServer.ssrFixStacktrace(error);
}
next(error);
}
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enhance error handling in Vite middleware.

The current error handling could be improved by:

  1. Adding specific error types and handling
  2. Logging the error details before passing to next
 app.use(async (req, res, next) => {
   try {
     /** @type {import("./server/app.ts")} */
     const source = await viteServer.ssrLoadModule("./server/app.ts");
     return source.app(req, res, next);
   } catch (error) {
     if (error instanceof Error) {
       viteServer.ssrFixStacktrace(error);
+      console.error('Vite SSR error:', error);
+      if (error.code === 'ENOENT') {
+        return res.status(404).send('Not found');
+      }
     }
     next(error);
   }
 });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
app.use(async (req, res, next) => {
try {
/** @type {import("./server/app.ts")} */
const source = await viteServer.ssrLoadModule("./server/app.ts");
return source.app(req, res, next);
} catch (error) {
if (error instanceof Error) {
viteServer.ssrFixStacktrace(error);
}
next(error);
}
});
app.use(async (req, res, next) => {
try {
/** @type {import("./server/app.ts")} */
const source = await viteServer.ssrLoadModule("./server/app.ts");
return source.app(req, res, next);
} catch (error) {
if (error instanceof Error) {
viteServer.ssrFixStacktrace(error);
console.error('Vite SSR error:', error);
if (error.code === 'ENOENT') {
return res.status(404).send('Not found');
}
}
next(error);
}
});

Comment on lines +114 to +129
async function performHealthcheck(port, retries = 5, delay = 1000) {
for (let i = 0; i < retries; i++) {
try {
const res = await fetch(`http://localhost:${port}/`);

if (!res.ok) {
throw new Error(`Healthcheck failed with status ${res.status}`);
}

return res.text();
} catch (error) {
if (i === retries - 1) throw error;
await new Promise((resolve) => setTimeout(resolve, delay));
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add timeout and improve error handling in health check.

The health check implementation could be enhanced with:

  1. Request timeout to prevent hanging
  2. More specific error handling
  3. Exponential backoff for retries
-async function performHealthcheck(port, retries = 5, delay = 1000) {
+async function performHealthcheck(port, retries = 5, initialDelay = 1000) {
   for (let i = 0; i < retries; i++) {
     try {
-      const res = await fetch(`http://localhost:${port}/`);
+      const controller = new AbortController();
+      const timeout = setTimeout(() => controller.abort(), 5000);
+      
+      const res = await fetch(`http://localhost:${port}/`, {
+        signal: controller.signal
+      });
+      clearTimeout(timeout);
 
       if (!res.ok) {
         throw new Error(`Healthcheck failed with status ${res.status}`);
       }
 
       return res.text();
     } catch (error) {
+      if (error.name === 'AbortError') {
+        console.warn(`Healthcheck attempt ${i + 1} timed out`);
+      } else {
+        console.warn(`Healthcheck attempt ${i + 1} failed:`, error.message);
+      }
+
       if (i === retries - 1) throw error;
-      await new Promise((resolve) => setTimeout(resolve, delay));
+      
+      // Exponential backoff with jitter
+      const backoff = initialDelay * Math.pow(2, i) * (0.9 + Math.random() * 0.2);
+      await new Promise((resolve) => setTimeout(resolve, backoff));
     }
   }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async function performHealthcheck(port, retries = 5, delay = 1000) {
for (let i = 0; i < retries; i++) {
try {
const res = await fetch(`http://localhost:${port}/`);
if (!res.ok) {
throw new Error(`Healthcheck failed with status ${res.status}`);
}
return res.text();
} catch (error) {
if (i === retries - 1) throw error;
await new Promise((resolve) => setTimeout(resolve, delay));
}
}
}
async function performHealthcheck(port, retries = 5, initialDelay = 1000) {
for (let i = 0; i < retries; i++) {
try {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 5000);
const res = await fetch(`http://localhost:${port}/`, {
signal: controller.signal
});
clearTimeout(timeout);
if (!res.ok) {
throw new Error(`Healthcheck failed with status ${res.status}`);
}
return res.text();
} catch (error) {
if (error.name === 'AbortError') {
console.warn(`Healthcheck attempt ${i + 1} timed out`);
} else {
console.warn(`Healthcheck attempt ${i + 1} failed:`, error.message);
}
if (i === retries - 1) throw error;
// Exponential backoff with jitter
const backoff = initialDelay * Math.pow(2, i) * (0.9 + Math.random() * 0.2);
await new Promise((resolve) => setTimeout(resolve, backoff));
}
}
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant