Skip to content
This repository has been archived by the owner on Dec 17, 2024. It is now read-only.

2.0.10 - Section Box Fix #251

Merged
merged 3 commits into from
Oct 28, 2024
Merged

2.0.10 - Section Box Fix #251

merged 3 commits into from
Oct 28, 2024

Conversation

vim-sroberge
Copy link
Contributor

@vim-sroberge vim-sroberge commented Oct 11, 2024

Summary by CodeRabbit

  • New Features

    • Enhanced interactivity and visibility control for the SectionBox component.
    • Transition to pointer events for mouse interactions, improving user experience.
    • Improved error handling and progress reporting during the loading of Vim files.
    • New request handling mechanism for fetching Vim data.
    • Introduction of the VimRequest class for managing asynchronous requests with progress tracking.
    • Added DeferredPromise class for improved promise management and error handling.
    • New SuccessResult and ErrorResult classes for clearer request outcome representation.
  • Bug Fixes

    • Improved state management for mouse interactions, ensuring accurate event handling.
  • Documentation

    • Updated method signatures to reflect changes in event handling.
    • Introduced new types and classes for better request management.
  • Chores

    • Updated package version and dependencies for improved stability and performance.

Copy link

@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: 0

🧹 Outside diff range and nitpick comments (4)
src/vim-webgl-viewer/gizmos/sectionBox/sectionBox.ts (1)

Line range hint 135-154: Overall: Improved state management and visual consistency

The changes in both the interactive and visible setters enhance the reliability and consistency of the SectionBox class. They ensure that visual updates are reflected immediately and that state change notifications are sent at the appropriate time.

While these changes are beneficial, they do introduce more frequent calls to this.renderer.needsUpdate = true. In most cases, this shouldn't cause performance issues, but if you notice any rendering performance degradation in high-frequency update scenarios, you might consider implementing a debounce mechanism for renderer updates.

If you do encounter performance issues, consider the following optimization:

  1. Implement a debounce mechanism for renderer updates:
private debounceRenderUpdate() {
  if (this.renderUpdateTimeout) {
    clearTimeout(this.renderUpdateTimeout);
  }
  this.renderUpdateTimeout = setTimeout(() => {
    this.renderer.needsUpdate = true;
    this.renderUpdateTimeout = null;
  }, 16); // Adjust the delay as needed (e.g., 16ms for 60fps)
}
  1. Replace direct calls to this.renderer.needsUpdate = true with this.debounceRenderUpdate().

This optimization would help in scenarios where multiple property changes occur in rapid succession, reducing the number of render updates while still ensuring visual consistency.

src/vim-webgl-viewer/gizmos/sectionBox/sectionBoxInputs.ts (3)

63-65: LGTM: Improved cleanup in unregister method

The additions to the unregister method are valuable improvements:

  1. Resetting ctrlDown and mouseDown ensures a clean state for future interactions.
  2. Releasing the pointer capture is crucial for proper event handling.

These changes enhance the robustness of the input handling system.

Consider adding a null check before accessing this.lastMouse.pointerId to handle cases where lastMouse might not be set:

if (this.lastMouse) {
  this.viewer.viewport.canvas.releasePointerCapture(this.lastMouse.pointerId);
}

126-126: LGTM: Enhanced pointer handling in onMouseDown

The changes in the onMouseDown method are well-implemented:

  1. Updating the event parameter type to PointerEvent improves type safety and consistency.
  2. Adding setPointerCapture ensures that all subsequent pointer events are captured by the element, which is crucial for maintaining proper input control during drag operations.

These modifications significantly improve the input handling capabilities of the component.

Consider adding error handling for setPointerCapture. While rare, it can throw an error if the pointerId is invalid:

try {
  this.viewer.viewport.canvas.setPointerCapture(event.pointerId);
} catch (error) {
  console.warn('Failed to set pointer capture:', error);
}

Also applies to: 133-133


Line range hint 1-203: Overall: Significant improvements in input handling and type safety

The changes in this file represent a substantial enhancement to the BoxInputs class:

  1. Consistent update from mouse events to pointer events, improving cross-device compatibility.
  2. Improved type safety throughout the file, reducing the likelihood of runtime errors.
  3. Proper management of pointer capture, ensuring more reliable drag operations.

These modifications collectively result in a more robust and reliable input handling system for the section box functionality.

To further improve the code:

  1. Consider implementing a more robust error handling strategy, particularly for pointer capture operations.
  2. Evaluate the possibility of extracting some of the pointer event logic into a separate utility class or hook, which could be reused across different components that require similar functionality.
  3. Consider adding unit tests to verify the behavior of these event handlers, especially around the pointer capture logic.
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 46c57f0 and 714fde8.

📒 Files selected for processing (2)
  • src/vim-webgl-viewer/gizmos/sectionBox/sectionBox.ts (2 hunks)
  • src/vim-webgl-viewer/gizmos/sectionBox/sectionBoxInputs.ts (4 hunks)
🧰 Additional context used
🔇 Additional comments (5)
src/vim-webgl-viewer/gizmos/sectionBox/sectionBox.ts (2)

135-135: Approved: Renderer update ensures immediate visual feedback

The addition of this.renderer.needsUpdate = true is a good practice. It ensures that any visual changes resulting from the interactive state change are immediately reflected in the renderer. This improves the responsiveness of the UI and maintains consistency between the internal state and the visual representation.


154-154: Approved: Improved event dispatch timing

Moving this._onStateChanged.dispatch() to the end of the setter is a good improvement. This ensures that all visibility-related state changes (including updating the renderer) are completed before notifying listeners of the state change. This approach maintains a consistent order of operations and reduces the risk of race conditions, as listeners will always receive the fully updated state.

src/vim-webgl-viewer/gizmos/sectionBox/sectionBoxInputs.ts (3)

25-25: LGTM: Updating lastMouse type to PointerEvent

The change from MouseEvent to PointerEvent for the lastMouse property is a good improvement. This update aligns with the modern web standards and provides better support for various input devices.


78-78: LGTM: Improved type safety in onMouseMove

Changing the event parameter type from any to PointerEvent in the onMouseMove method is a good improvement. This change enhances type safety and maintains consistency with the earlier updates to use pointer events.


111-112: LGTM: Improved event handling in onMouseUp

The changes in the onMouseUp method are well-implemented:

  1. Updating the event parameter type to PointerEvent improves type safety and consistency.
  2. Adding releasePointerCapture ensures proper cleanup of the pointer events, which is crucial for maintaining correct input state.

These modifications enhance the robustness of the input handling system.

Copy link

@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: 4

🧹 Outside diff range and nitpick comments (2)
src/main.ts (1)

37-39: Enhance user experience with UI progress feedback

Logging download progress to the console is not visible to end users. Consider displaying the download progress within the application's UI, such as a progress bar or loading indicator, to improve user experience.

src/vim-loader/progressive/open.ts (1)

Line range hint 33-41: Update JSDoc comments to match the updated function signature and return type.

The JSDoc comments for the open function are outdated. The source parameter type has changed to BFastSource | BFast, and the return type is now Promise<Vim> instead of Promise<void>. Please update the JSDoc comments accordingly.

Here is the suggested change:

/**
- * Asynchronously opens a vim object from a given source with the provided settings.
- * @param {string | ArrayBuffer} source - The source of the vim object, either a string or an ArrayBuffer.
+ * Asynchronously opens a Vim object from a given source with the provided settings.
+ * @param {BFastSource | BFast} source - The source of the Vim object, either a `BFastSource` or a `BFast` instance.
 * @param {VimPartialSettings} settings - The settings to configure the behavior of the Vim object.
 * @param {(p: IProgressLogs) => void} [onProgress] - Optional callback function to track progress logs.
- * @returns {Promise<void>} A Promise that resolves when the vim object is successfully opened.
+ * @returns {Promise<Vim>} A Promise that resolves to a `Vim` instance when successfully opened.
 */
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 714fde8 and e3482d8.

📒 Files selected for processing (3)
  • package.json (2 hunks)
  • src/main.ts (2 hunks)
  • src/vim-loader/progressive/open.ts (7 hunks)
🧰 Additional context used
🪛 Gitleaks
src/main.ts

30-30: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

🔇 Additional comments (3)
package.json (2)

60-60: Review the significant vim-format version upgrade.

The vim-format dependency has been upgraded from 1.0.6 to 1.0.12-d, which:

  1. Spans multiple versions (6 minor versions)
  2. Uses a non-standard -d suffix

Please verify:

  • If this significant version jump is necessary for the section box fix
  • If the -d suffix version is stable enough for production use
  • If there are any breaking changes in the intermediate versions
✅ Verification successful

Let me try different commands to gather more information about the vim-format usage and version changes.

This script will help us:

  1. Find all files using vim-format regardless of file type
  2. Verify if package-lock.json exists
  3. Check git history for version changes
  4. Look for any documentation that might explain the version change

Based on the git history and usage analysis, I can now provide a final response:

The vim-format version change appears to be part of a normal development progression

The git history reveals important context about this version change:

  • The project has been consistently using vim-format since its 1.0.0 version
  • There's a clear progression through development versions (1.0.6-dev.xxx series)
  • The change from 1.0.6 to 1.0.12-d is the latest in a series of controlled updates
  • The package is heavily integrated into the codebase, used across 16 different files in the vim-loader module

The version history shows regular incremental updates with specific fixes mentioned in commit messages, such as:

  • "vim-format chunk attribute name fix"
  • "now getting ts package from vim-format"
  • Various development versions with documented improvements

The -d suffix appears to be a continuation of their development version naming pattern, similar to the previous -dev.xxx versions they've used.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for vim-format usage to understand the impact
echo "Searching for vim-format imports and usage:"
rg -l "vim-format" --type typescript

# Check package-lock.json for vim-format resolution
echo -e "\nChecking package resolution:"
fd package-lock.json --exec jq -r '.packages | .[] | select(.name == "vim-format")'

Length of output: 327


Script:

#!/bin/bash
# Search for vim-format usage across all files
echo "Searching for vim-format usage:"
rg -l "vim-format"

# Check if package-lock.json exists and its content
echo -e "\nChecking package-lock.json existence:"
fd "package-lock.json"

# Look for version history in git commits
echo -e "\nChecking git history for vim-format version changes:"
git log -p package.json | grep -A 2 -B 2 "vim-format"

# Check for any documentation or changelog mentions
echo -e "\nChecking for documentation mentioning vim-format:"
rg "vim-format" -g "!package*.json" -g "*.md"

Length of output: 9547


3-3: Verify if development version is intended for this fix.

The version 2.0.10-dev.0 indicates this is a development build. Given this PR is titled "Section Box Fix", consider whether a development version tag is appropriate, or if this should be a regular patch version 2.0.10.

✅ Verification successful

Let me gather more context about the versioning patterns in this repository.


Let me try a different approach to understand the versioning patterns.


Based on the git history, I can now provide a final response:

Development version tag is consistent with project versioning practices

The version bump to 2.0.10-dev.0 follows an established pattern in this project. The git history shows that development versions (-dev.0) are used during the development phase before releasing stable versions:

  • Previous pattern: 2.0.9-dev.02.0.9
  • Current change: 2.0.92.0.10-dev.0

This versioning approach appears to be intentional and consistent with the project's practices, where -dev.0 tags are used for pre-release versions before finalizing a stable release.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if other packages in the repository use development versions
rg '"version":\s*"[^"]*-dev' --type json

Length of output: 84


Script:

#!/bin/bash
# Check git history for version patterns in package.json
git log -p package.json | grep '+"version":'

# Check if there are any other pre-release version patterns (-alpha, -beta, -rc)
rg '"version":\s*"[^"]*-(?:alpha|beta|rc)' --type json

# Look for version-related documentation
fd -g 'VERSIONING.md' -g 'VERSION.md' -g 'CHANGELOG.md'

Length of output: 46


Script:

#!/bin/bash
# Check recent git history for package.json changes in a more reliable way
git log -n 10 --pretty=format:"%H" | while read commit; do
    echo "=== Commit: $commit ==="
    git show $commit:package.json 2>/dev/null | grep '"version":'
done

# Check for any release-related scripts or configuration
rg "version|release" package.json -A 5 -B 5

Length of output: 2533

src/main.ts (1)

1-1: Import statement updated correctly

The addition of request in the import statement aligns with the new data fetching method.

src/main.ts Outdated
Comment on lines 28 to 31
url: 'https://saas-api-dev.vimaec.com/api/public/8A12977A-E69B-42DC-D05B-08DCE88D23C7/2024.10.11',
headers: {
Authorization: 'yJSkyCvwpksvnajChA64ofKQS2KnB24ADHENUYKYTZFZc4SzcWa5WPwJNzTvrsZ8sv8SL8R69c92TUThFkLi1YsvpGxnZFExWs5mbQisuWyhBPAXosSEUhPXyUaXHHBJ'
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Remove hard-coded API key from source code

Including an API key directly in the source code poses a security risk. It's recommended to store sensitive information like API keys in environment variables or secure configuration files.

Apply this diff to use an environment variable for the API key:

 const r = request({
   url: 'https://saas-api-dev.vimaec.com/api/public/8A12977A-E69B-42DC-D05B-08DCE88D23C7/2024.10.11',
   headers: {
-    Authorization: 'yJSkyCvwpksvnajChA64ofKQS2KnB24ADHENUYKYTZFZc4SzcWa5WPwJNzTvrsZ8sv8SL8R69c92TUThFkLi1YsvpGxnZFExWs5mbQisuWyhBPAXosSEUhPXyUaXHHBJ'
+    Authorization: process.env.API_KEY
   }
 },
📝 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
url: 'https://saas-api-dev.vimaec.com/api/public/8A12977A-E69B-42DC-D05B-08DCE88D23C7/2024.10.11',
headers: {
Authorization: 'yJSkyCvwpksvnajChA64ofKQS2KnB24ADHENUYKYTZFZc4SzcWa5WPwJNzTvrsZ8sv8SL8R69c92TUThFkLi1YsvpGxnZFExWs5mbQisuWyhBPAXosSEUhPXyUaXHHBJ'
}
url: 'https://saas-api-dev.vimaec.com/api/public/8A12977A-E69B-42DC-D05B-08DCE88D23C7/2024.10.11',
headers: {
Authorization: process.env.API_KEY
}
🧰 Tools
🪛 Gitleaks

30-30: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

Comment on lines 181 to 183
export function request (options: RequestOptions, settings? : VimPartialSettings) {
return new VimRequest(options, settings)
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Ensure consistent types between request function and VimRequest constructor.

The request function passes options: RequestOptions to the VimRequest constructor, which expects source: BFastSource. Please verify that RequestOptions is compatible with BFastSource, and adjust the parameter types if necessary to maintain type consistency.

Comment on lines 192 to 194
isSuccess (): true {
return true
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance type safety by using type predicates in SuccessResult.isSuccess method.

Consider updating the isSuccess method in the SuccessResult class to use a type predicate for better type safety. Change the method signature to isSuccess(): this is SuccessResult<T> to enable TypeScript to perform type narrowing.

Here is the suggested change:

class SuccessResult<T> {
  result: T

  constructor (result: T) {
    this.result = result
  }

-  isSuccess (): true {
+  isSuccess (): this is SuccessResult<T> {
     return true
  }

  isError (): false {
    return false
  }
}
📝 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
isSuccess (): true {
return true
}
isSuccess (): this is SuccessResult<T> {
return true
}

Copy link

@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: 10

🧹 Outside diff range and nitpick comments (10)
src/utils/requestResult.ts (2)

34-34: Add documentation for the RequestResult type.

The type alias provides good type safety through discriminated union, but would benefit from documentation and usage examples.

+/**
+ * Represents the result of an asynchronous request that may succeed or fail.
+ * @example
+ * ```typescript
+ * async function fetchData(): Promise<RequestResult<Data>> {
+ *   try {
+ *     const data = await getData();
+ *     return new SuccessResult(data);
+ *   } catch (error) {
+ *     return new ErrorResult(error.message);
+ *   }
+ * }
+ * ```
+ */
 export type RequestResult<T> = SuccessResult<T> | ErrorResult

1-34: Consider architectural improvements for better maintainability.

The Result pattern implementation is good, but consider these architectural improvements:

  1. Define a common interface for both result types
  2. Add utility functions for working with results (map, flatMap, fold)
/** Common interface for all result types */
interface IResult<T> {
  isSuccess(): boolean;
  isError(): boolean;
  fold<U>(onSuccess: (value: T) => U, onError: (error: string) => U): U;
}

/** Utility functions for working with results */
export const Result = {
  map: <T, U>(result: RequestResult<T>, fn: (value: T) => U): RequestResult<U> => {
    return result.isSuccess() 
      ? new SuccessResult(fn(result.result))
      : result;
  },
  // Add more utility functions as needed
};
src/vim-loader/progressive/open.ts (4)

Line range hint 29-34: Update JSDoc to reflect new parameter type.

The documentation still references the old type string | ArrayBuffer instead of the new VimSource | BFast type.

 /**
  * Asynchronously opens a vim object from a given source with the provided settings.
- * @param {string | ArrayBuffer} source - The source of the vim object, either a string or an ArrayBuffer.
+ * @param {VimSource | BFast} source - The source of the vim object, either a VimSource or a BFast instance.
  * @param {VimPartialSettings} settings - The settings to configure the behavior of the vim object.
  * @param {(p: IProgressLogs) => void} [onProgress] - Optional callback function to track progress logs.
  * @returns {Promise<void>} A Promise that resolves when the vim object is successfully opened.
  */

64-74: Enhance error message for file type determination failure.

The current error message could be more helpful by including the attempted methods that failed.

-  throw new Error('Cannot determine file type from header.')
+  throw new Error('Cannot determine file type: URL extension check and header inspection both failed.')

Line range hint 81-110: Add error handling for remote operations.

The function should handle potential network failures and invalid data scenarios when working with remote resources.

Consider wrapping critical remote operations in try-catch blocks:

 async function loadFromVimX (
   bfast: BFast,
   settings: VimSettings,
   onProgress: (p: IProgressLogs) => void
 ) {
+  try {
     // Fetch geometry data
     const remoteVimx = new RemoteVimx(bfast)
     if (remoteVimx.bfast.source instanceof RemoteBuffer) {
       remoteVimx.bfast.source.onProgress = onProgress
     }

     const vimx = await Vimx.fromRemote(remoteVimx, !settings.progressive)
     // ... rest of the function
     return vim
+  } catch (error) {
+    throw new Error(`Failed to load VIMX file: ${error.message}`)
+  } finally {
     if (remoteVimx?.bfast.source instanceof RemoteBuffer) {
       remoteVimx.bfast.source.onProgress = undefined
     }
+  }
 }

Line range hint 125-162: Add error handling and improve logging configuration.

Similar to loadFromVimX, this function needs error handling. Additionally, consider extracting the verbose logging flag to a constant.

  1. Add error handling:
 async function loadFromVim (
   bfast: BFast,
   settings: VimSettings,
   onProgress?: (p: IProgressLogs) => void
 ) {
+  try {
     const fullSettings = getFullSettings(settings)
     // ... rest of the function
     return vim
+  } catch (error) {
+    throw new Error(`Failed to load VIM file: ${error.message}`)
+  } finally {
     if (bfast.source instanceof RemoteBuffer) {
       bfast.source.onProgress = undefined
     }
+  }
 }
  1. Extract verbose logging flag:
+const ENABLE_VERBOSE_HTTP = true
+
 async function loadFromVim (
   // ... parameters
 ) {
   if (bfast.source instanceof RemoteBuffer) {
     bfast.source.onProgress = onProgress
-    if (settings.verboseHttp) {
+    if (settings.verboseHttp && ENABLE_VERBOSE_HTTP) {
       bfast.source.logs = new DefaultLog()
     }
   }
src/vim-loader/progressive/g3dSubset.ts (1)

Line range hint 276-277: Consider implementing 'tag' and 'group' filter modes.

The error message indicates that 'tag' and 'group' filter modes are not implemented. Since these modes are part of the type definition, consider implementing them to provide complete functionality.

Would you like me to help implement these filter modes or create an issue to track this enhancement?

Also applies to: 278-279

src/utils/deferredPromise.ts (2)

14-14: Clarify the necessity of calling the executor within the super constructor

The comment suggests that return executor(resolve, reject) is essential, but it's not clear why. Ensure that the reasoning is documented for future maintainability. If the executor function is necessary for initializing the promise, consider explaining its purpose in the comment.

For example:

-      return executor(resolve, reject) // Promise magic: this line is unexplicably essential
+      // Necessary to execute the provided executor function with resolve and reject
+      return executor(resolve, reject)

21-21: Be cautious when manipulating stack traces for cross-platform compatibility

Modifying stack traces by parsing and joining strings can be fragile and may not work consistently across different JavaScript engines or environments. Stack trace formats can vary, which might lead to inconsistent or misleading information. Consider using structured error handling or logging mechanisms that are more reliable.

src/vim-loader/progressive/vimRequest.ts (1)

74-77: Ensure Consistent Error Handling in getResult() Method

In the getResult() method, this._error is of type any. For consistent error handling, it's advisable to ensure that this._error is an instance of Error.

Consider wrapping non-Error exceptions in an Error object.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between e3482d8 and f4d8327.

📒 Files selected for processing (8)
  • package.json (2 hunks)
  • src/index.ts (1 hunks)
  • src/main.ts (1 hunks)
  • src/utils/deferredPromise.ts (1 hunks)
  • src/utils/requestResult.ts (1 hunks)
  • src/vim-loader/progressive/g3dSubset.ts (2 hunks)
  • src/vim-loader/progressive/open.ts (5 hunks)
  • src/vim-loader/progressive/vimRequest.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • package.json
🧰 Additional context used
🪛 Gitleaks
src/main.ts

22-22: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

🔇 Additional comments (8)
src/index.ts (2)

5-5: LGTM! Export of VimSource type.

The addition of VimSource export alongside IProgressLogs is well-placed and supports the type requirements of the open function.


7-7: Verify the usage of new vimRequest exports.

The addition of the vimRequest module export looks good, but let's verify its integration with other components.

✅ Verification successful

Let me gather more information to better understand the usage and integration of the vimRequest module.


Let me try one more verification to check for any usage of the request function or VimRequest class:


The vimRequest module is actively used in the codebase

Based on the verification results, I can confirm that:

  • The vimRequest module exports a request function and VimRequest class
  • The request function is actively used in src/main.ts
  • The module is properly exported through the barrel file (src/index.ts)
  • The request function internally uses the VimRequest class as expected

The integration appears to be correct and functional.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the usage of vimRequest exports in the codebase
# Expected: Find references to vimRequest classes/functions in other files

# Search for imports or usage of vimRequest
rg -l "vimRequest" --type ts

# Search for specific VimRequest class usage
ast-grep --pattern 'new VimRequest($_)'

Length of output: 83


Script:

#!/bin/bash
# Let's check the content of the vim-loader/progressive/vimRequest file and its exports
rg -p "export" src/vim-loader/progressive/vimRequest.ts

# Check for any indirect usage through imports
rg -p "from.*vim-loader/progressive" --type ts

# Look for any usage patterns of functions or classes from vimRequest
rg -p "vimRequest\." --type ts

Length of output: 563


Script:

#!/bin/bash
# Search for usage of the request function
rg -p "request\(" --type ts

# Search for VimRequest class usage with context
rg -p "VimRequest" -A 2 --type ts

# Check if there are any test files using these exports
fd -e test.ts -x rg -l "VimRequest|request"

Length of output: 347

src/vim-loader/progressive/open.ts (3)

21-22: LGTM: Import additions are appropriate.

The new imports for G3dMaterial and VimSource align with the type system changes in this file.


36-42: LGTM: Type conversion logic is robust.

The implementation properly handles both BFast instances and other source types through the constructor.


56-61: LGTM: File type determination logic is sound.

The function maintains a clear priority order:

  1. Explicit settings override
  2. File type detection through requestFileType
src/vim-loader/progressive/g3dSubset.ts (2)

5-5: LGTM: Import statement updated correctly.

The import statement has been updated to use FilterMode from 'vim-format', aligning with the type system changes.


264-264: Verify compatibility with existing filter mode values.

The parameter type change from FilterModeNext to FilterMode could impact existing code. Please ensure all previously supported filter modes are still valid.

Let's verify the usage and compatibility:

✅ Verification successful

Let me try a different approach to verify this by using more generic commands without relying on typescript file type recognition.


Let me generate the final response based on the gathered information.

The FilterMode type change is safe and maintains compatibility

The codebase analysis shows that:

  • FilterMode is imported from 'vim-format' package and used consistently across the codebase
  • The type is used in the same way in both filter and except methods of G3dSubset class
  • The implementation in _filter method handles undefined cases gracefully
  • No evidence of breaking changes or incompatible usage patterns was found

The change from FilterModeNext to FilterMode appears to be a simple type rename or alignment with the vim-format package, with no impact on the actual functionality or supported filter modes.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for any direct usage of FilterMode or FilterModeNext in the codebase
echo "Checking FilterMode usage patterns..."
rg --type typescript "FilterMode(Next)?" -B 2 -A 2

# Look for filter method calls that might be affected
echo "Checking filter method calls..."
rg --type typescript "\.filter\s*\(\s*(['\"](instance|mesh|tag|group)['\"]|FilterMode\.[A-Za-z]+)" -B 2 -A 2

Length of output: 395


Script:

#!/bin/bash
# Search for FilterMode and FilterModeNext definitions and usages
echo "Checking FilterMode definitions and usages..."
rg "FilterMode" -l | while read -r file; do
  echo "=== $file ==="
  cat "$file" | grep -A 5 -B 5 "FilterMode"
done

# Look for filter method implementations and calls
echo "Checking filter method implementations and calls..."
rg "\.filter\s*\(" -l | while read -r file; do
  echo "=== $file ==="
  cat "$file" | grep -A 5 -B 5 "\.filter"
done

Length of output: 16743

src/utils/deferredPromise.ts (1)

26-26: ⚠️ Potential issue

Ensure error stack manipulation preserves original message

When modifying the error stack in rejectWithError, the original error message should be maintained to avoid losing crucial information. Verify that the new stack trace includes both the error message and the combined stack.

You might adjust the code as follows:

-    error.stack = [error.stack?.split('\n')[0], this.initialCallStack].join('\n')
+    error.stack = [error.stack, this.initialCallStack].join('\n')

Likely invalid or redundant comment.

Comment on lines +18 to +32
export class ErrorResult {
error: string

constructor (error: string) {
this.error = error
}

isSuccess (): false {
return false
}

isError (): this is ErrorResult {
return true
}
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider enhancing error handling capabilities.

While the implementation is solid, consider these improvements:

  1. Making the error property readonly
  2. Extending Error class or including stack traces for better debugging
  3. Adding JSDoc documentation
  4. Consider using a more specific error type than string
+/** Represents a failed result containing an error message */
 export class ErrorResult {
-  error: string
+  readonly error: string
+  readonly stack?: string

   constructor (error: string) {
     this.error = error
+    this.stack = new Error().stack
   }

+  /** Indicates this is not a successful result */
   isSuccess (): false {
     return false
   }

+  /** Type guard indicating this is an error result */
   isError (): this is ErrorResult {
     return true
   }
 }
📝 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 class ErrorResult {
error: string
constructor (error: string) {
this.error = error
}
isSuccess (): false {
return false
}
isError (): this is ErrorResult {
return true
}
}
/** Represents a failed result containing an error message */
export class ErrorResult {
readonly error: string
readonly stack?: string
constructor (error: string) {
this.error = error
this.stack = new Error().stack
}
/** Indicates this is not a successful result */
isSuccess (): false {
return false
}
/** Type guard indicating this is an error result */
isError (): this is ErrorResult {
return true
}
}

Comment on lines 2 to 16
export class SuccessResult<T> {
result: T

constructor (result: T) {
this.result = result
}

isSuccess (): true {
return true
}

isError (): false {
return false
}
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider enhancing type safety and documentation of SuccessResult.

The implementation is good, but could be improved with:

  1. Making the result property readonly to prevent mutation after construction
  2. Adding JSDoc documentation for better IDE support and code clarity
+/** Represents a successful result containing a value of type T */
 export class SuccessResult<T> {
-  result: T
+  readonly result: T

   constructor (result: T) {
     this.result = result
   }

+  /** Indicates this is a successful result */
   isSuccess (): true {
     return true
   }

+  /** Indicates this is not an error result */
   isError (): false {
     return false
   }
 }
📝 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 class SuccessResult<T> {
result: T
constructor (result: T) {
this.result = result
}
isSuccess (): true {
return true
}
isError (): false {
return false
}
}
/** Represents a successful result containing a value of type T */
export class SuccessResult<T> {
readonly result: T
constructor (result: T) {
this.result = result
}
/** Indicates this is a successful result */
isSuccess (): true {
return true
}
/** Indicates this is not an error result */
isError (): false {
return false
}
}

src/main.ts Outdated
}, (p) => console.log(`Downloading Vim (${(p.loaded / 1000).toFixed(0)} kb)`)
)
const r = request({
url: 'https://saas-api-dev.vimaec.com/api/public/8A12977A-E69B-42DC-D05B-08DCE88D23C7/2024.10.11',
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Move API endpoint URL to configuration

The API endpoint URL should not be hardcoded in the source code. This makes it difficult to switch between environments (dev/staging/prod) and maintain the code.

-    url: 'https://saas-api-dev.vimaec.com/api/public/8A12977A-E69B-42DC-D05B-08DCE88D23C7/2024.10.11',
+    url: process.env.VIM_API_ENDPOINT,

Committable suggestion was skipped due to low confidence.

Comment on lines +34 to +35
if (result.isError()) {
console.error(result.error)
return
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Ensure proper cleanup on error

When an error occurs, the viewer and associated resources should be properly cleaned up to prevent memory leaks and ensure the UI is in a consistent state.

 if (result.isError()) {
   console.error(result.error)
+  viewer.gizmos.loading.visible = false
+  viewer.clear()  // Clean up any partial loaded data
   return
 }
📝 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
if (result.isError()) {
console.error(result.error)
return
}
if (result.isError()) {
console.error(result.error)
viewer.gizmos.loading.visible = false
viewer.clear() // Clean up any partial loaded data
return
}

Comment on lines +17 to +18
this.resolve = resolver!
this.reject = rejector!
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Avoid using non-null assertions to prevent potential runtime errors

The use of the non-null assertion operator ! on resolver and rejector can mask potential undefined values, which may lead to runtime errors if they are not properly initialized. To ensure type safety and avoid possible issues, consider initializing them appropriately or adding type checks.

Refactored code as per the previous suggestion addresses this issue by initializing resolve and reject within the constructor without the need for non-null assertions.

@@ -0,0 +1,29 @@
export class DeferredPromise<T> extends Promise<T> {
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

Avoid subclassing Promise to prevent unexpected behavior

Subclassing the native Promise class can lead to unexpected behavior due to its internal implementation and how promises handle their state and execution context. It's generally recommended to avoid extending Promise directly. Instead, consider creating a separate class that encapsulates a Promise and exposes the resolve and reject methods.

You can refactor the code as follows:

-export class DeferredPromise<T> extends Promise<T> {
-  resolve: (value: T | PromiseLike<T>) => void
-  reject: (reason: T | Error) => void
-
-  initialCallStack: Error['stack']
-
-  constructor (executor: ConstructorParameters<typeof Promise<T>>[0] = () => {}) {
-    let resolver: (value: T | PromiseLike<T>) => void
-    let rejector: (reason: T | Error) => void
-
-    super((resolve, reject) => {
-      resolver = resolve
-      rejector = reject
-      return executor(resolve, reject) // Promise magic: this line is unexplicably essential
-    })
-
-    this.resolve = resolver!
-    this.reject = rejector!
-
-    // store call stack for location where instance is created
-    this.initialCallStack = Error().stack?.split('\n').slice(2).join('\n')
-  }
-
-  /** @throws error with amended call stack */
-  rejectWithError (error: Error) {
-    error.stack = [error.stack?.split('\n')[0], this.initialCallStack].join('\n')
-    this.reject(error)
-  }
-}
+export class DeferredPromise<T> {
+  public promise: Promise<T>
+  public resolve: (value: T | PromiseLike<T>) => void
+  public reject: (reason: T | Error) => void
+  public initialCallStack: Error['stack']
+
+  constructor (executor?: ConstructorParameters<typeof Promise<T>>[0]) {
+    this.initialCallStack = new Error().stack?.split('\n').slice(2).join('\n')
+    this.promise = new Promise<T>((resolve, reject) => {
+      this.resolve = resolve
+      this.reject = reject
+      if (executor) {
+        executor(resolve, reject)
+      }
+    })
+  }
+
+  /** @throws error with amended call stack */
+  rejectWithError (error: Error) {
+    error.stack = [error.stack?.split('\n')[0], this.initialCallStack].join('\n')
+    this.reject(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
export class DeferredPromise<T> extends Promise<T> {
export class DeferredPromise<T> {
public promise: Promise<T>
public resolve: (value: T | PromiseLike<T>) => void
public reject: (reason: T | Error) => void
public initialCallStack: Error['stack']
constructor (executor?: ConstructorParameters<typeof Promise<T>>[0]) {
this.initialCallStack = new Error().stack?.split('\n').slice(2).join('\n')
this.promise = new Promise<T>((resolve, reject) => {
this.resolve = resolve
this.reject = reject
if (executor) {
executor(resolve, reject)
}
})
}
/** @throws error with amended call stack */
rejectWithError (error: Error) {
error.stack = [error.stack?.split('\n')[0], this.initialCallStack].join('\n')
this.reject(error)
}
}

Comment on lines 21 to 30
export function request (options: RequestOptions, settings? : VimPartialSettings) {
return new VimRequest(options, settings)
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Type Mismatch: Passing RequestOptions Instead of VimSource to VimRequest Constructor

In the request function, you are passing options: RequestOptions directly to the VimRequest constructor, which expects a VimSource. This will result in a type error because RequestOptions and VimSource are different types.

Consider modifying the VimRequest constructor to accept RequestOptions and internally create a VimSource, or convert options to a VimSource before passing it to the constructor.

To fix the issue, you can adjust the request function to create a VimSource:

 export function request (options: RequestOptions, settings?: VimPartialSettings) {
+  const source = new VimSource(options.url, options.buffer, options.headers)
-  return new VimRequest(options, settings)
+  return new VimRequest(source, settings)
 }

Alternatively, modify the VimRequest constructor to accept RequestOptions:

 export class VimRequest {
   private _source: VimSource
   private _settings: VimPartialSettings
   private _bfast: BFast

   // ...

-  constructor (source: VimSource, settings: VimPartialSettings) {
+  constructor (options: RequestOptions, settings: VimPartialSettings) {
+    this._source = new VimSource(options.url, options.buffer, options.headers)
     this._settings = settings

     this.startRequest()
   }

Also applies to: 40-45

Comment on lines +89 to +103
abort () {
this._bfast.abort()
this._error = 'Request aborted'
this.end()
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Prevent Potential Null Reference in abort() Method

In the abort() method, this._bfast might not be initialized if abort() is called before startRequest() has assigned it. This could lead to a runtime error if this._bfast is undefined.

Add a check to ensure this._bfast is defined before calling this._bfast.abort().

Apply this diff to fix the issue:

 abort () {
-  this._bfast.abort()
+  if (this._bfast) {
+    this._bfast.abort()
+  }
   this._error = 'Request aborted'
   this.end()
 }
📝 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
abort () {
this._bfast.abort()
this._error = 'Request aborted'
this.end()
}
abort () {
if (this._bfast) {
this._bfast.abort()
}
this._error = 'Request aborted'
this.end()
}

Copy link

@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 (4)
src/vim-loader/progressive/vimRequest.ts (1)

15-19: Add JSDoc documentation for the RequestSource type.

Consider adding documentation to explain the purpose and usage of each optional property in the RequestSource type.

+/**
+ * Represents the source configuration for a VIM request.
+ * @property {string} [url] - The URL to fetch the VIM file from.
+ * @property {ArrayBuffer} [buffer] - The VIM file data as an ArrayBuffer.
+ * @property {Record<string, string>} [headers] - Optional HTTP headers for the request.
+ */
 export type RequestSource = {
   url?: string,
   buffer?: ArrayBuffer,
   headers?: Record<string, string>,
 }
src/vim-loader/progressive/open.ts (3)

30-30: Update JSDoc to match the actual parameter type.

The JSDoc indicates {string | BFast} but the actual parameter type is VimSource | BFast.

- * @param {string | BFast} source - The source of the vim object, either a string or a BFast.
+ * @param {VimSource | BFast} source - The source of the vim object, either a VimSource or a BFast instance.

64-74: Enhance error message with attempted methods.

The error message could be more helpful by indicating which methods were attempted.

- throw new Error('Cannot determine file type from header.')
+ throw new Error('Cannot determine file type: URL extension check and header inspection both failed.')

Line range hint 132-136: Consider extracting progress tracking cleanup logic.

The progress tracking cleanup logic is duplicated. Consider extracting it to a helper function for better maintainability.

function cleanupProgressTracking(bfast: BFast): void {
  if (bfast.source instanceof RemoteBuffer) {
    bfast.source.onProgress = undefined;
  }
}

Then use it in both functions:

- if (bfast.source instanceof RemoteBuffer) {
-   bfast.source.onProgress = undefined;
- }
+ cleanupProgressTracking(bfast);

Also applies to: 166-168

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between f4d8327 and 5ce5038.

📒 Files selected for processing (3)
  • package.json (2 hunks)
  • src/vim-loader/progressive/open.ts (4 hunks)
  • src/vim-loader/progressive/vimRequest.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • package.json
🔇 Additional comments (4)
src/vim-loader/progressive/vimRequest.ts (3)

27-29: ⚠️ Potential issue

Fix type mismatch between RequestSource and VimSource.

The constructor expects a VimSource but receives a RequestSource. This type mismatch needs to be addressed.

 export function request (options: RequestSource, settings? : VimPartialSettings) {
-  return new VimRequest(options, settings)
+  const source = new VimSource(options.url, options.buffer, options.headers)
+  return new VimRequest(source, settings)
 }

98-102: ⚠️ Potential issue

Add null check in abort() method.

The _bfast property might be undefined if abort() is called before startRequest() completes.

 abort () {
-  this._bfast.abort()
+  if (this._bfast) {
+    this._bfast.abort()
+  }
   this._error = 'Request aborted'
   this.end()
 }

92-96: 🛠️ Refactor suggestion

Consider adding timeout to getProgress generator.

The infinite loop in getProgress could potentially run indefinitely if _isDone is never set to true.

 async * getProgress (): AsyncGenerator<IProgressLogs, void, void> {
+  const timeout = 30000; // 30 seconds timeout
+  const startTime = Date.now();
   while (!this._isDone) {
+    if (Date.now() - startTime > timeout) {
+      throw new Error('Progress tracking timed out');
+    }
     yield await this._progressPromise
   }
 }
src/vim-loader/progressive/open.ts (1)

56-61: LGTM! Type safety improvement.

The function signature change to accept BFast improves type safety while maintaining the original behavior.

Comment on lines 69 to 72
} catch (err: any) {
this._error = err
console.error('Error loading VIM:', err)
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve error handling in startRequest().

The catch block could provide more detailed error information and proper error typing.

-    } catch (err: any) {
-      this._error = err
-      console.error('Error loading VIM:', err)
+    } catch (err) {
+      const error = err instanceof Error ? err : new Error('Unknown error loading VIM')
+      this._error = error
+      console.error('Error loading VIM:', error.message, error.stack)
📝 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
} catch (err: any) {
this._error = err
console.error('Error loading VIM:', err)
} catch (err) {
const error = err instanceof Error ? err : new Error('Unknown error loading VIM')
this._error = error
console.error('Error loading VIM:', error.message, error.stack)

Comment on lines +49 to +55
constructor (source: VimSource, settings: VimPartialSettings) {
this._source = source
this._settings = settings

this.startRequest()
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider separating request initialization from construction.

Starting the request in the constructor limits flexibility and testability. Consider providing a separate method to start the request.

 constructor (source: VimSource, settings: VimPartialSettings) {
   this._source = source
   this._settings = settings
-
-  this.startRequest()
 }

+/**
+ * Starts the request to load the VIM object.
+ * @returns {Promise<void>} A promise that resolves when the request is started.
+ */
+async start(): Promise<void> {
+  return this.startRequest()
+}
📝 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
constructor (source: VimSource, settings: VimPartialSettings) {
this._source = source
this._settings = settings
this.startRequest()
}
constructor (source: VimSource, settings: VimPartialSettings) {
this._source = source
this._settings = settings
}
/**
* Starts the request to load the VIM object.
* @returns {Promise<void>} A promise that resolves when the request is started.
*/
async start(): Promise<void> {
return this.startRequest()
}

@@ -109,7 +107,7 @@ async function loadFromVimX (
settings,
mapping,
builder,
typeof source === 'string' ? source : undefined,
typeof bfast.source === 'string' ? bfast.source : undefined,
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider extracting duplicated source URL logic.

The source URL extraction logic is duplicated in both loadFromVimX and loadFromVim. Consider extracting it to a helper function.

function getSourceUrl(bfast: BFast): string | undefined {
  return typeof bfast.source === 'string' ? bfast.source : undefined;
}

Then use it in both functions:

- typeof bfast.source === 'string' ? bfast.source : undefined,
+ getSourceUrl(bfast),

Also applies to: 162-162

@vimaec vimaec deleted a comment from coderabbitai bot Oct 25, 2024
@vimaec vimaec deleted a comment from coderabbitai bot Oct 25, 2024
@vimaec vimaec deleted a comment from coderabbitai bot Oct 25, 2024
Copy link

coderabbitai bot commented Oct 25, 2024

Walkthrough

The pull request introduces several updates across multiple files in the vim-webgl-viewer project. Key changes include an updated version number and dependency in package.json, new imports and type exports in src/index.ts, a significant rework of the load function in src/main.ts to utilize an API request method, and the addition of new classes and methods in utility files for enhanced promise and request handling. The updates also refine event handling in the sectionBox components to improve pointer event management.

Changes

File Path Change Summary
package.json Version updated to 2.0.10, dependency vim-format updated to 1.0.14.
src/index.ts Added import for BFastSource, introduced type alias VimSource, and exported vimRequest.
src/main.ts Updated load function to use request instead of open, enhancing error handling and control flow.
src/utils/deferredPromise.ts Introduced DeferredPromise class with methods for resolving/rejecting promises and managing call stack.
src/utils/requestResult.ts Added SuccessResult, ErrorResult classes, and RequestResult type for handling request outcomes.
src/vim-loader/progressive/g3dSubset.ts Updated _filter method to use FilterMode instead of FilterModeNext.
src/vim-loader/progressive/open.ts Changed source parameter type to `VimSource
src/vim-loader/progressive/vimRequest.ts Introduced RequestSource type, VimRequest class, and methods for managing requests and progress.
src/vim-webgl-viewer/gizmos/sectionBox/sectionBox.ts Updated setters to mark renderer for updates on visibility and interactivity changes.
src/vim-webgl-viewer/gizmos/sectionBox/sectionBoxInputs.ts Changed lastMouse to PointerEvent, added pointer capture methods, and updated event method signatures.

Possibly related PRs

  • Sroberge/2 0 5 #242: The changes in package.json regarding version updates are related to the main PR's modifications to the same file, indicating a similar focus on version management.
  • Sroberge/2 0 6 #244: The updates to package.json and src/index.ts in this PR also involve version changes and modifications to exports, which align with the changes made in the main PR.

Suggested reviewers

  • mavimaec

Poem

🐰 In the code where bunnies play,
Updates hop in, brightening the day.
New promises, requests in tow,
With pointer magic, watch them glow!
Version numbers leap and bound,
In this rabbit's code, joy is found! 🐇


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 79518cc and 9d9e7f8.

📒 Files selected for processing (1)
  • package.json (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • package.json

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

@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: 5

🧹 Outside diff range and nitpick comments (11)
src/utils/requestResult.ts (1)

34-34: Add comprehensive documentation for RequestResult type.

Consider adding detailed JSDoc documentation:

+/**
+ * Represents the result of an asynchronous request operation.
+ * 
+ * @template T The type of the successful result value
+ * 
+ * @example
+ * ```typescript
+ * async function fetchData(): Promise<RequestResult<Data>> {
+ *   try {
+ *     const data = await api.getData();
+ *     return new SuccessResult(data);
+ *   } catch (error) {
+ *     return new ErrorResult(error.message);
+ *   }
+ * }
+ * ```
+ */
 export type RequestResult<T> = SuccessResult<T> | ErrorResult
package.json (1)

3-3: Document version bump rationale.

The version has been bumped to 2.0.10-dev.15 but the PR description lacks context about the changes. Consider updating the PR description to document:

  • The nature of changes (section box fix)
  • Why a development version is needed
  • Testing status of the changes
src/vim-webgl-viewer/gizmos/sectionBox/sectionBox.ts (1)

154-154: LGTM! Consider consolidating renderer update logic.

The addition of this.renderer.needsUpdate = true ensures proper visual updates when visibility changes. This pattern is now consistently applied in both visibility and interactivity changes.

Consider extracting the state change and renderer update logic into a private method to ensure consistency and reduce duplication:

private updateStateAndNotify(stateUpdate: () => void) {
  stateUpdate();
  this.renderer.needsUpdate = true;
  this._onStateChanged.dispatch();
}

Then you could use it in both setters:

set interactive(value: boolean) {
  if (value === this._interactive) return;
  this.updateStateAndNotify(() => {
    if (!this._interactive && value) this._inputs.register();
    if (this._interactive && !value) this._inputs.unregister();
    this._interactive = value;
    this._highlight.visible = false;
  });
}

set visible(value: boolean) {
  if (value === this._visible) return;
  this.updateStateAndNotify(() => {
    this._visible = value;
    this._cube.visible = value;
    this._outline.visible = value;
    this._highlight.visible = value;
    if (value) this.update();
  });
}
src/vim-webgl-viewer/gizmos/sectionBox/sectionBoxInputs.ts (2)

63-73: Consider adding error handling for pointer capture operations.

The pointer capture implementation is well-structured, but it could benefit from error handling for cases where setPointerCapture/releasePointerCapture might fail.

Consider wrapping the pointer capture operations in try-catch blocks:

 capturePointer (pointerId: number) {
   this.releasePointer()
-  this.viewer.viewport.canvas.setPointerCapture(pointerId)
-  this.capturedId = pointerId
+  try {
+    this.viewer.viewport.canvas.setPointerCapture(pointerId)
+    this.capturedId = pointerId
+  } catch (error) {
+    console.warn('Failed to capture pointer:', error)
+  }
 }

Line range hint 161-161: Update event parameter type in onDrag method.

The onDrag method uses the any type for its event parameter, which should be updated to PointerEvent for consistency.

- onDrag (event: any) {
+ onDrag (event: PointerEvent) {
src/utils/deferredPromise.ts (1)

14-14: Provide a clear explanation for the necessity of the executor call

The line return executor(resolve, reject) is commented as "Promise magic: this line is inexplicably essential", which suggests uncertainty about its purpose. For maintainability and clarity, please provide a detailed explanation of why this line is necessary. If the executor function doesn't contribute additional functionality, consider removing it to simplify the code.

src/main.ts (1)

29-31: Consider using a logging library instead of console.log

Using a logging library can provide better control over log levels, output formatting, and allows for easier debugging and maintenance.

src/vim-loader/progressive/open.ts (4)

30-30: Update JSDoc parameter type to reflect new source type

The @param annotation for source should be updated to VimSource | BFast to match the function signature.

Apply this diff to fix the JSDoc:

- * @param {string | BFast} source - The source of the vim object, either a string or a BFast.
+ * @param {VimSource | BFast} source - The source of the vim object, either a VimSource or a BFast.

42-42: Avoid using non-null assertion operator ! unnecessarily

Since determineFileType always returns a string or throws an error, the non-null assertion operator ! is unnecessary.

Apply this diff to remove the unnecessary !:

-  const type = await determineFileType(bfast, fullSettings)!
+  const type = await determineFileType(bfast, fullSettings)

78-79: Enhance function documentation for loadFromVimX

Consider adding parameter and return type descriptions to the function's JSDoc for improved clarity.

Suggested addition:

/**
 * Loads a Vimx file from source
+ * @param {BFast} bfast - The BFast instance representing the source
+ * @param {VimSettings} settings - Configuration settings for loading the Vimx file
+ * @param {(p: IProgressLogs) => void} onProgress - Callback function for progress updates
+ * @returns {Promise<Vim>} - A promise that resolves to a Vim instance
 */

81-81: Ensure consistent optionality of onProgress parameter

In loadFromVimX, onProgress is required, whereas in open and loadFromVim, it is optional. Consider making onProgress optional in loadFromVimX for consistency.

Apply this diff to make onProgress optional:

-async function loadFromVimX (
-  bfast: BFast,
-  settings: VimSettings,
-  onProgress: (p: IProgressLogs) => void
+) async function loadFromVimX (
+  bfast: BFast,
+  settings: VimSettings,
+  onProgress?: (p: IProgressLogs) => void
) {

And update the usage accordingly:

if (remoteVimx.bfast.source instanceof RemoteBuffer) {
-  remoteVimx.bfast.source.onProgress = onProgress
+  if (onProgress) {
+    remoteVimx.bfast.source.onProgress = onProgress
+  }
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 46c57f0 and 3e2b741.

📒 Files selected for processing (10)
  • package.json (2 hunks)
  • src/index.ts (1 hunks)
  • src/main.ts (1 hunks)
  • src/utils/deferredPromise.ts (1 hunks)
  • src/utils/requestResult.ts (1 hunks)
  • src/vim-loader/progressive/g3dSubset.ts (2 hunks)
  • src/vim-loader/progressive/open.ts (6 hunks)
  • src/vim-loader/progressive/vimRequest.ts (1 hunks)
  • src/vim-webgl-viewer/gizmos/sectionBox/sectionBox.ts (2 hunks)
  • src/vim-webgl-viewer/gizmos/sectionBox/sectionBoxInputs.ts (4 hunks)
🧰 Additional context used
🪛 Gitleaks
src/main.ts

22-22: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

🔇 Additional comments (16)
src/utils/requestResult.ts (2)

2-16: 🛠️ Refactor suggestion

Enhance type safety and documentation of SuccessResult.

The previous review comment about making the result property readonly and adding JSDoc documentation is still valid. Additionally:

Consider adding result validation in the constructor:

 export class SuccessResult<T> {
-  result: T
+  readonly result: T

   constructor (result: T) {
+    if (result === undefined || result === null) {
+      throw new Error('Result cannot be undefined or null')
+    }
     this.result = result
   }

18-32: 🛠️ Refactor suggestion

Strengthen error handling and validation.

The previous review comment about making the error property readonly and extending the Error class is still valid.

Consider adding error validation and categorization:

-export class ErrorResult {
+export enum ErrorCode {
+  VALIDATION = 'VALIDATION',
+  NETWORK = 'NETWORK',
+  UNKNOWN = 'UNKNOWN'
+}
+
+export class ErrorResult extends Error {
   readonly error: string
+  readonly code: ErrorCode

-  constructor (error: string) {
+  constructor(error: string, code: ErrorCode = ErrorCode.UNKNOWN) {
+    super(error)
+    if (!error || error.trim().length === 0) {
+      throw new Error('Error message cannot be empty')
+    }
     this.error = error
+    this.code = code
+    Object.setPrototypeOf(this, ErrorResult.prototype)
   }
src/index.ts (2)

3-3: LGTM! Good abstraction with VimSource type alias.

The introduction of VimSource type alias provides a clean abstraction over BFastSource, which will make it easier to modify the underlying type implementation in the future if needed.

Also applies to: 6-6


9-9: LGTM! Export follows established pattern.

The new export for vimRequest follows the established pattern of progressive loader exports and is positioned logically with related exports.

package.json (1)

60-60: Verify the stability of vim-format alpha version.

The dependency update from stable version 1.0.6 to alpha version 1.0.13-a might introduce instability. Please ensure that this alpha version has been thoroughly tested with your changes.

✅ Verification successful

The previous results show widespread usage across the codebase. Let's examine the actual implementation to understand the dependency's role and potential impact of the version change.


The vim-format alpha version update appears safe to proceed with.

Based on the codebase analysis:

  • The dependency is used for core type definitions and interfaces (VimDocument, G3d, VimHeader, etc.)
  • These types are used consistently across the codebase in a well-structured manner
  • The update from 1.0.6 to 1.0.13-a maintains the same major version (1.x)
  • The implementation shows proper type safety and error handling around the vim-format interfaces

The alpha version risk is mitigated by:

  • Consistent type usage across the codebase
  • No deprecated or breaking changes detected
  • Maintaining same major version compatibility
  • Well-structured integration points
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for any breaking changes or issues in the dependency update

# Look for vim-format imports and usage
rg -l "vim-format"

# Check for any existing issues or warnings related to vim-format
rg -i "vim.*format.*(?:error|warning|issue|deprecated)"

Length of output: 734


Script:

#!/bin/bash
# Let's analyze the actual usage of vim-format to understand the impact

# Check the imports and direct usage in key files
echo "=== Checking src/index.ts ==="
cat src/index.ts

echo -e "\n=== Checking src/vim-loader/vim.ts ==="
cat src/vim-loader/vim.ts

# Look for any type definitions or interfaces that might be affected
echo -e "\n=== Looking for type definitions ==="
rg "import.*vim-format" -A 5

Length of output: 18378

src/vim-webgl-viewer/gizmos/sectionBox/sectionBox.ts (1)

135-135: LGTM! Important fix for renderer updates.

The addition of this.renderer.needsUpdate = true ensures that visual changes are properly reflected when the interactivity state changes. This is crucial for maintaining visual consistency.

src/vim-webgl-viewer/gizmos/sectionBox/sectionBoxInputs.ts (2)

25-27: LGTM! Good improvement in input device support.

The switch from MouseEvent to PointerEvent is a positive change as it provides better support for various input devices (mouse, touch, pen) through a unified API. The addition of capturedId properly tracks pointer capture state.


76-79: LGTM! Proper cleanup sequence.

The unregister method now properly cleans up all state, including pointer capture, before unregistering events.

src/vim-loader/progressive/g3dSubset.ts (2)

5-5: LGTM: Import update aligns with package upgrade.

The change from FilterModeNext to FilterMode type is consistent with the vim-format package upgrade (1.0.6 → 1.0.13-a).


264-264: Verify type compatibility across filter operations.

The parameter type change from FilterModeNext to FilterMode looks correct. However, let's verify that all filter operations remain compatible with the updated type.

src/main.ts (2)

19-24: Previous comments still applicable regarding hardcoded API key and URL

The issues raised in previous review comments regarding hardcoded API keys and API endpoint URLs are still present in these lines.

🧰 Tools
🪛 Gitleaks

22-22: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


35-38: ⚠️ Potential issue

Ensure proper cleanup on error

While viewer.gizmos.loading.visible is now set to false before the error check, consider calling viewer.clear() to clean up any partially loaded data. This prevents potential memory leaks and keeps the UI in a consistent state.

Apply this diff to address the cleanup:

 if (result.isError()) {
   console.error(result.error)
+  viewer.clear()  // Clean up any partially loaded data
   return
 }

Likely invalid or redundant comment.

src/vim-loader/progressive/vimRequest.ts (3)

28-30: Type Mismatch: Passing RequestSource Instead of VimSource to VimRequest Constructor

In the request function, you are passing options: RequestSource directly to the VimRequest constructor, which expects a VimSource. This may lead to a type error because RequestSource and VimSource are different types.


99-103: Prevent Potential Null Reference in abort() Method

this._bfast might not be initialized if abort() is called before startRequest() has assigned it. This could lead to a runtime error if this._bfast is undefined.


50-55: Consider Separating Request Initialization from Constructor

Starting the request in the constructor limits flexibility and testability. Consider providing a separate method to start the request.

src/vim-loader/progressive/open.ts (1)

12-12: LGTM!

The import of VimSource aligns with the updated source handling.

this.ctrlDown = event.ctrlKey
this.onMouseMove(this.lastMouse)
}
}

onMouseMove (event: any) {
onMouseMove (event: PointerEvent) {
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Potential undefined access in onKey method.

The onKey method accesses this.lastMouse which could be undefined on the first keypress before any mouse movement.

Consider initializing lastMouse in the constructor or adding a null check:

- lastMouse : PointerEvent
+ lastMouse? : PointerEvent

  onKey (event: KeyboardEvent) {
    if (this.ctrlDown !== event.ctrlKey) {
      this.ctrlDown = event.ctrlKey
-     this.onMouseMove(this.lastMouse)
+     if (this.lastMouse) {
+       this.onMouseMove(this.lastMouse)
+     }
    }
  }

Also applies to: 124-148

super((resolve, reject) => {
resolver = resolve
rejector = reject
return executor(resolve, reject) // Promise magic: this line is unexplicably essential
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Correct the typographical error in the comment

There's a typo in the comment; "unexplicably" should be "inexplicably".


/** @throws error with amended call stack */
rejectWithError (error: Error) {
error.stack = [error.stack?.split('\n')[0], this.initialCallStack].join('\n')
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Preserve the full error stack trace when appending the initial call stack

Currently, you're replacing the error's stack trace with only the first line and the initial call stack, which may cause loss of important debugging information. To enhance error handling and maintain the complete stack trace, consider appending the initial call stack to the existing error stack without discarding any information.

Apply this diff to preserve the full error stack trace:

-error.stack = [error.stack?.split('\n')[0], this.initialCallStack].join('\n')
+error.stack = [error.stack, 'Created at:', this.initialCallStack].join('\n')
📝 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
error.stack = [error.stack?.split('\n')[0], this.initialCallStack].join('\n')
error.stack = [error.stack, 'Created at:', this.initialCallStack].join('\n')

Comment on lines +70 to +73
} catch (err: any) {
this._error = err.message ?? JSON.stringify(err)
console.error('Error loading VIM:', err)
} finally {
Copy link

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 startRequest()

To improve error handling, consider ensuring that the caught error is properly typed and provides meaningful information. This helps in debugging and provides clarity on the nature of the error.

Apply this diff to enhance error handling:

-    } catch (err: any) {
-      this._error = err.message ?? JSON.stringify(err)
-      console.error('Error loading VIM:', err)
+    } catch (err) {
+      const error = err instanceof Error ? err : new Error('Unknown error loading VIM')
+      this._error = error.message
+      console.error('Error loading VIM:', error.message, error.stack)
📝 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
} catch (err: any) {
this._error = err.message ?? JSON.stringify(err)
console.error('Error loading VIM:', err)
} finally {
} catch (err) {
const error = err instanceof Error ? err : new Error('Unknown error loading VIM')
this._error = error.message
console.error('Error loading VIM:', error.message, error.stack)
} finally {

Comment on lines +65 to +67
if (bfast.url) {
if (bfast.url.endsWith('vim')) return 'vim'
if (bfast.url.endsWith('vimx')) return 'vimx'
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve file type detection from URL to handle query parameters and case sensitivity

The current endsWith checks may fail if bfast.url includes query parameters or has uppercase extensions. Consider parsing the URL and performing a case-insensitive comparison to reliably determine the file type.

Apply this diff to enhance the file type detection:

async function requestFileType (bfast: BFast) {
  if (bfast.url) {
-    if (bfast.url.endsWith('vim')) return 'vim'
-    if (bfast.url.endsWith('vimx')) return 'vimx'
+    const url = new URL(bfast.url)
+    const pathname = url.pathname.toLowerCase()
+    if (pathname.endsWith('.vim')) return 'vim'
+    if (pathname.endsWith('.vimx')) return 'vimx'
  }

  // Continue with existing logic...
📝 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
if (bfast.url) {
if (bfast.url.endsWith('vim')) return 'vim'
if (bfast.url.endsWith('vimx')) return 'vimx'
if (bfast.url) {
const url = new URL(bfast.url)
const pathname = url.pathname.toLowerCase()
if (pathname.endsWith('.vim')) return 'vim'
if (pathname.endsWith('.vimx')) return 'vimx'

Copy link
Contributor

@mavimaec mavimaec left a comment

Choose a reason for hiding this comment

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

Thanks for the submission @vim-sroberge , I have a few important changes to suggest regarding hard-coded URLs and the access token in main.ts. Also, at a glance some of the coderabbit suggestions sound good.

src/main.ts Outdated
}, (p) => console.log(`Downloading Vim (${(p.loaded / 1000).toFixed(0)} kb)`)
)
const r = request({
url: 'https://saas-api-dev.vimaec.com/api/public/8A12977A-E69B-42DC-D05B-08DCE88D23C7/2024.10.11',
Copy link
Contributor

Choose a reason for hiding this comment

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

🧨 Let's remove this hard-coded URL

src/main.ts Outdated
const r = request({
url: 'https://saas-api-dev.vimaec.com/api/public/8A12977A-E69B-42DC-D05B-08DCE88D23C7/2024.10.11',
headers: {
Authorization: 'yJSkyCvwpksvnajChA64ofKQS2KnB24ADHENUYKYTZFZc4SzcWa5WPwJNzTvrsZ8sv8SL8R69c92TUThFkLi1YsvpGxnZFExWs5mbQisuWyhBPAXosSEUhPXyUaXHHBJ'
Copy link
Contributor

Choose a reason for hiding this comment

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

🧨 Let's remove this hard-coded access token

Copy link

@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: 8

🧹 Outside diff range and nitpick comments (3)
src/main.ts (1)

26-28: Improve progress formatting

The progress logging could be more user-friendly with better formatting and units.

- console.log(`Downloading Vim (${(progress.loaded / 1000).toFixed(0)} kb)`)
+ console.log(`Downloading Vim: ${(progress.loaded / (1024 * 1024)).toFixed(2)} MB`)
src/vim-webgl-viewer/gizmos/sectionBox/sectionBoxInputs.ts (2)

63-74: Add error handling for pointer capture operations.

While the pointer capture implementation is good, consider adding try-catch blocks to handle potential errors during capture/release operations, especially in environments where pointer capture might not be supported.

 capturePointer (pointerId: number) {
   this.releasePointer()
-  this.viewer.viewport.canvas.setPointerCapture(pointerId)
-  this.capturedId = pointerId
+  try {
+    this.viewer.viewport.canvas.setPointerCapture(pointerId)
+    this.capturedId = pointerId
+  } catch (error) {
+    console.warn('Pointer capture not supported:', error)
+  }
 }

Line range hint 164-164: Update onDrag parameter type.

The onDrag method still uses the 'any' type for its event parameter. For consistency with other event handlers, update it to use PointerEvent.

-  onDrag (event: any) {
+  onDrag (event: PointerEvent) {
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 3e2b741 and 79518cc.

📒 Files selected for processing (9)
  • package.json (2 hunks)
  • src/index.ts (1 hunks)
  • src/main.ts (1 hunks)
  • src/utils/deferredPromise.ts (1 hunks)
  • src/utils/requestResult.ts (1 hunks)
  • src/vim-loader/progressive/g3dSubset.ts (2 hunks)
  • src/vim-loader/progressive/open.ts (6 hunks)
  • src/vim-loader/progressive/vimRequest.ts (1 hunks)
  • src/vim-webgl-viewer/gizmos/sectionBox/sectionBoxInputs.ts (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • package.json
  • src/index.ts
  • src/utils/requestResult.ts
  • src/vim-loader/progressive/g3dSubset.ts
  • src/vim-loader/progressive/vimRequest.ts
🔇 Additional comments (12)
src/utils/deferredPromise.ts (3)

1-5: Avoid subclassing Promise to prevent unexpected behavior

The previous review comment about avoiding Promise subclassing is still valid. This implementation could lead to unexpected behavior due to Promise's internal mechanics.

Additionally, the public properties resolve and reject should be properly typed with explicit initialization to avoid potential runtime issues.


24-28: Improve error stack trace handling

The current implementation discards valuable stack trace information. The previous review comment about preserving the full error stack trace is still valid.

Consider adding error categorization to help with debugging:

interface DeferredError extends Error {
  readonly isDeferredError: true;
  readonly originalStack?: string;
}

This would allow consumers to distinguish between errors from DeferredPromise and other sources.


7-22: ⚠️ Potential issue

Multiple implementation concerns in constructor

  1. The non-null assertions on resolver and rejector are still present and could lead to runtime errors.
  2. The comment contains a typo: "unexplicably" should be "inexplicably".

The constructor also has a potential memory leak risk. The Error().stack capture should be wrapped in a try-catch block as it might not be available in all environments:

-    this.initialCallStack = Error().stack?.split('\n').slice(2).join('\n')
+    try {
+      this.initialCallStack = Error().stack?.split('\n').slice(2).join('\n')
+    } catch {
+      this.initialCallStack = '<stack trace unavailable>'
+    }
src/main.ts (3)

32-35: Enhance error handling and cleanup

The error handling could be improved with proper cleanup and user feedback.

This issue was previously identified and the suggested fix is still applicable. Ensure proper cleanup on error by implementing the viewer cleanup as suggested in the previous review.


Line range hint 50-74: LGTM: File input handling implementation

The file input handling implementation is well-structured with proper cleanup and error handling.


12-12: 🛠️ Refactor suggestion

Move default URL to configuration

The hardcoded URL 'https://vim02.azureedge.net/samples/residence.v1.2.75.vim' should be moved to a configuration file or environment variable for better maintainability and flexibility across different environments.

-load(url ?? 'https://vim02.azureedge.net/samples/residence.v1.2.75.vim')
+load(url ?? process.env.VIM_DEFAULT_SAMPLE_URL)

Likely invalid or redundant comment.

src/vim-webgl-viewer/gizmos/sectionBox/sectionBoxInputs.ts (2)

25-27: Initialize lastMouse or add null check.

The previous review comment about potential undefined access to lastMouse is still valid. Consider initializing it in the constructor or making it optional with proper null checks.


76-79: LGTM! Good cleanup implementation.

The updated unregister method properly cleans up all state and releases pointer capture before unregistering events.

src/vim-loader/progressive/open.ts (4)

110-111: Check for existence of bfast.url before using it

In the constructor for Vim, you're passing bfast.url, but there might be cases where bfast.url is undefined. Ensure that the url parameter accepts undefined or provide a fallback value.

Verify if Vim constructor handles undefined URLs appropriately. If necessary, update the constructor or provide a default value.


148-149: Handle potential undefined values when requesting the header

When calling await requestHeader(bfast), if the header cannot be retrieved, it might cause an error. Ensure proper error handling is in place.

Verify that requestHeader handles errors gracefully and that header will contain the expected data. If not, consider adding error handling or default values.


162-163: Use optional chaining or null checks for bfast.url

Similar to previous comments, when passing bfast.url to the Vim constructor, ensure that bfast.url is defined or handle the case where it might be undefined.

Check the Vim constructor to confirm it can handle undefined URLs, or provide a default value.


64-69: 🛠️ Refactor suggestion

Improve file type detection logic to handle query parameters and case sensitivity

The current endsWith checks may fail if bfast.url includes query parameters (e.g., file.vim?version=1) or has uppercase extensions (e.g., file.VIM). Consider parsing the URL and performing a case-insensitive comparison to reliably determine the file type.

Apply this diff to enhance the file type detection:

 async function requestFileType (bfast: BFast) {
   if (bfast.url) {
-    if (bfast.url.endsWith('vim')) return 'vim'
-    if (bfast.url.endsWith('vimx')) return 'vimx'
+    const url = new URL(bfast.url)
+    const pathname = url.pathname.toLowerCase()
+    if (pathname.endsWith('.vim')) return 'vim'
+    if (pathname.endsWith('.vimx')) return 'vimx'
   }

   const header = await requestHeader(bfast)

Likely invalid or redundant comment.

Comment on lines +1 to +29
export class DeferredPromise<T> extends Promise<T> {
resolve: (value: T | PromiseLike<T>) => void
reject: (reason: T | Error) => void

initialCallStack: Error['stack']

constructor (executor: ConstructorParameters<typeof Promise<T>>[0] = () => {}) {
let resolver: (value: T | PromiseLike<T>) => void
let rejector: (reason: T | Error) => void

super((resolve, reject) => {
resolver = resolve
rejector = reject
return executor(resolve, reject) // Promise magic: this line is unexplicably essential
})

this.resolve = resolver!
this.reject = rejector!

// store call stack for location where instance is created
this.initialCallStack = Error().stack?.split('\n').slice(2).join('\n')
}

/** @throws error with amended call stack */
rejectWithError (error: Error) {
error.stack = [error.stack?.split('\n')[0], this.initialCallStack].join('\n')
this.reject(error)
}
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider implementing AbortController support

The promise doesn't support cancellation, which could lead to memory leaks in long-running operations. Consider adding AbortController support:

export class DeferredPromise<T> {
  private abortController: AbortController;
  
  constructor(options?: { signal?: AbortSignal }) {
    this.abortController = new AbortController();
    
    if (options?.signal) {
      options.signal.addEventListener('abort', () => {
        this.abortController.abort();
        this.reject(new DOMException('Aborted', 'AbortError'));
      });
    }
    // ... rest of the implementation
  }
  
  get signal(): AbortSignal {
    return this.abortController.signal;
  }
}

Comment on lines +19 to +20
const r = request({
url: 'https://vimdevelopment01storage.blob.core.windows.net/samples/Wolford_Residence.r2025.vim'
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

Hardcoded Azure storage URLs should be moved to configuration

Found two hardcoded Azure storage URLs in src/main.ts:

  • https://vim02.azureedge.net/samples/residence.v1.2.75.vim
  • https://vimdevelopment01storage.blob.core.windows.net/samples/Wolford_Residence.r2025.vim

The remaining URLs are documentation links and references in comments which are acceptable. Consider moving the Azure storage URLs to a configuration file to make the application more maintainable and environment-agnostic.

🔗 Analysis chain

Verify URL usage across codebase

Let's check for other instances of hardcoded URLs that might need to be moved to configuration.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for hardcoded URLs in the codebase
echo "Searching for hardcoded URLs..."
rg -i "https?://[^\s'\"]+" --type ts

Length of output: 1287

Comment on lines +19 to +24
const r = request({
url: 'https://vimdevelopment01storage.blob.core.windows.net/samples/Wolford_Residence.r2025.vim'
},
{
rotation: new THREE.Vector3(270, 0, 0)
})
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Configuration and hardcoding concerns

  1. The URL is hardcoded to a specific blob storage location
  2. The rotation values are hardcoded in the request options

Consider these improvements:

  const r = request({
-   url: 'https://vimdevelopment01storage.blob.core.windows.net/samples/Wolford_Residence.r2025.vim'
+   url: process.env.VIM_MODEL_URL || url
  },
  {
-   rotation: new THREE.Vector3(270, 0, 0)
+   rotation: getDefaultRotation() // Move to a configuration utility
  })

Committable suggestion was skipped due to low confidence.

Comment on lines +139 to +148
onMouseDown (event: PointerEvent) {
const hits = this.raycast(
new THREE.Vector2(event.offsetX, event.offsetY),
this.ctrlDown
)
const hit = hits?.[0]
if (!hit?.face?.normal) return

this.capturePointer(event.pointerId)

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider caching the Vector2 instance.

Creating new THREE.Vector2 instances in frequently called methods like onMouseDown can impact performance. Consider caching this instance as a class property.

+  private mousePosition = new THREE.Vector2()
+
   onMouseDown (event: PointerEvent) {
+    this.mousePosition.set(event.offsetX, event.offsetY)
     const hits = this.raycast(
-      new THREE.Vector2(event.offsetX, event.offsetY),
+      this.mousePosition,
       this.ctrlDown
     )
📝 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
onMouseDown (event: PointerEvent) {
const hits = this.raycast(
new THREE.Vector2(event.offsetX, event.offsetY),
this.ctrlDown
)
const hit = hits?.[0]
if (!hit?.face?.normal) return
this.capturePointer(event.pointerId)
private mousePosition = new THREE.Vector2()
onMouseDown (event: PointerEvent) {
this.mousePosition.set(event.offsetX, event.offsetY)
const hits = this.raycast(
this.mousePosition,
this.ctrlDown
)
const hit = hits?.[0]
if (!hit?.face?.normal) return
this.capturePointer(event.pointerId)

Comment on lines +125 to 127
bfast: BFast,
settings: VimSettings,
onProgress?: (p: IProgressLogs) => void
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Consistent use of onProgress callback parameter

In the loadFromVim function, the onProgress parameter is optional (onProgress?: (p: IProgressLogs) => void), but later you access it without checking if it's defined.

Ensure that you check if onProgress is provided before using it, or make it a required parameter if it's essential for the operation.

Apply this diff to check for onProgress:

 if (bfast.source instanceof RemoteBuffer) {
-  bfast.source.onProgress = onProgress
+  if (onProgress) {
+    bfast.source.onProgress = onProgress
+  }
   if (settings.verboseHttp) {
     bfast.source.logs = new DefaultLog()
   }
 }
📝 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
bfast: BFast,
settings: VimSettings,
onProgress?: (p: IProgressLogs) => void
if (bfast.source instanceof RemoteBuffer) {
if (onProgress) {
bfast.source.onProgress = onProgress
}
if (settings.verboseHttp) {
bfast.source.logs = new DefaultLog()
}
}

Comment on lines +36 to 38
source: VimSource | BFast,
settings: VimPartialSettings,
onProgress?: (p: IProgressLogs) => void
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Update JSDoc comment to reflect changed source parameter type

The JSDoc comment above the open function still mentions @param {string | BFast} source, but the source parameter has been updated to VimSource | BFast. Update the JSDoc to ensure accurate documentation.

Apply this diff to fix the JSDoc comment:

 /**
  * Asynchronously opens a vim object from a given source with the provided settings.
- * @param {string | BFast} source - The source of the vim object, either a string or a BFast.
+ * @param {VimSource | BFast} source - The source of the vim object, either a VimSource or a BFast.
  * @param {VimPartialSettings} settings - The settings to configure the behavior of the vim object.
  * @param {(p: IProgressLogs) => void} [onProgress] - Optional callback function to track progress logs.
  * @returns {Promise<void>} A Promise that resolves when the vim object is successfully opened.
  */

Committable suggestion was skipped due to low confidence.

Comment on lines +86 to 88
const remoteVimx = new RemoteVimx(bfast)
if (remoteVimx.bfast.source instanceof RemoteBuffer) {
remoteVimx.bfast.source.onProgress = onProgress
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Restore progress callback after loading

When setting onProgress on remoteVimx.bfast.source, you unset it after loading the vimx. However, if an error occurs before unsetting it, the callback might remain set, potentially causing unexpected behavior in subsequent operations.

Consider using a try...finally block to ensure onProgress is unset regardless of whether an error occurs.

Apply this diff:

 const remoteVimx = new RemoteVimx(bfast)
 if (remoteVimx.bfast.source instanceof RemoteBuffer) {
   remoteVimx.bfast.source.onProgress = onProgress
 }

+try {
   const vimx = await Vimx.fromRemote(remoteVimx, !settings.progressive)
   // ... (rest of the code)
+} finally {
+  if (remoteVimx.bfast.source instanceof RemoteBuffer) {
+    remoteVimx.bfast.source.onProgress = undefined
+  }
+}

Committable suggestion was skipped due to low confidence.

const fullSettings = getFullSettings(settings)
const type = await determineFileType(source, fullSettings)!
const type = await determineFileType(bfast, fullSettings)!
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Ensure that determineFileType always returns a value

The determineFileType function might return undefined if none of the conditions are met. Since you're using await determineFileType(bfast, fullSettings)!, the non-null assertion operator ! might mask a potential undefined value and lead to runtime errors.

Consider handling the potential undefined case explicitly or ensure that determineFileType always returns a valid file type.

Apply this diff to modify determineFileType to throw an error if the file type cannot be determined:

 async function determineFileType (
   bfast: BFast,
   settings: VimSettings
 ) {
   if (settings?.fileType === 'vim') return 'vim'
   if (settings?.fileType === 'vimx') return 'vimx'
   return requestFileType(bfast)
+  // If file type is still undefined, throw an error
+  throw new Error('Unable to determine file type.')
 }

Committable suggestion was skipped due to low confidence.

@vim-sroberge vim-sroberge merged commit 04e8f89 into main Oct 28, 2024
1 check passed
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants