Skip to content

Conversation

@Skyzreal
Copy link

@Skyzreal Skyzreal commented Nov 14, 2025

  • Changed @devolutions/multi-video-player and @devolutions/shadow-player from
    private to public
  • Added peer dependencies: @devolutions/shadow-player, video.js
  • Gateway API client for fetching recording data
  • build.ps1 for both packages
  • shadow-player: Added disconnect for proper cleanup

@Skyzreal Skyzreal requested review from a team as code owners November 14, 2025 14:04
@github-actions
Copy link

Let maintainers know that an action is required on their side

  • Add the label release-required Please cut a new release (Devolutions Gateway, Devolutions Agent, Jetsocat, PowerShell module) when you request a maintainer to cut a new release (Devolutions Gateway, Devolutions Agent, Jetsocat, PowerShell module)

  • Add the label release-blocker Follow-up is required before cutting a new release if a follow-up is required before cutting a new release

  • Add the label publish-required Please publish libraries (`Devolutions.Gateway.Utils`, OpenAPI clients, etc) when you request a maintainer to publish libraries (Devolutions.Gateway.Utils, OpenAPI clients, etc.)

  • Add the label publish-blocker Follow-up is required before publishing libraries if a follow-up is required before publishing libraries

@Skyzreal Skyzreal marked this pull request as draft November 14, 2025 14:43
@Skyzreal Skyzreal added the publish-required Please publish libraries (`Devolutions.Gateway.Utils`, OpenAPI clients, etc) label Nov 14, 2025
@github-actions
Copy link

Maintainer action requested

Author requested libraries to be published.

cc @CBenoit

@Skyzreal Skyzreal force-pushed the ARC-412-Extract-and-publish-NPM-package branch 2 times, most recently from 2307372 to d400247 Compare November 14, 2025 16:30
refactor(recording): publishing npm package


fix(recording-player): use published shadow-player exports instead of internal paths
@Skyzreal Skyzreal force-pushed the ARC-412-Extract-and-publish-NPM-package branch from d400247 to 14a9bf4 Compare November 14, 2025 16:37
@CBenoit
Copy link
Member

CBenoit commented Nov 17, 2025

@Skyzreal Are you looking for initial feedback on your draft PR?

@Skyzreal
Copy link
Author

Skyzreal commented Nov 17, 2025

@CBenoit Yes, exactly, I'm taking some time this morning to review the changes and would really appreciate any initial feedback, especially since it’s my first time commiting in the devolutions-gateway repo

@Skyzreal Skyzreal marked this pull request as ready for review November 17, 2025 16:09
@CBenoit CBenoit requested a review from Copilot November 19, 2025 13:20
Copilot finished reviewing on behalf of CBenoit November 19, 2025 13:23
"private": true,
"version": "0.0.0",
"private": false,
"version": "1.0.0",
Copy link
Member

Choose a reason for hiding this comment

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

suggestion: Let’s start with 0.1.0 (pre v1)

Suggested change
"version": "1.0.0",
"version": "0.1.0",

Comment on lines -9 to +10
"main": "./dist/shadow-player.js",
"module": "./dist/shadow-player.js",
"main": "./dist/webm-stream-player.js",
"module": "./dist/webm-stream-player.js",
Copy link
Member

@CBenoit CBenoit Nov 19, 2025

Choose a reason for hiding this comment

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

question: Is it intended? I see this is the package.json from shadow-player 🤔

suggestion: Use shadow-player; or better: many packages are using a generic index name for the entry points.

Copy link
Author

Choose a reason for hiding this comment

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

Thanks for noticing! I cleaned it up so the package now uses the standard index

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR prepares two packages (@devolutions/shadow-player and @devolutions/multi-video-player) for publication to npm by changing them from private to public packages. The changes include adding proper peer dependencies, implementing a Gateway API client for fetching recording data, and adding cleanup functionality to the shadow player.

Key changes:

  • Published shadow-player (v1.0.0) and multi-video-player (v1.0.0) as public npm packages
  • Implemented GatewayRecordingApi client for Gateway recording operations
  • Added disconnect() method to shadow-player for proper resource cleanup

Reviewed Changes

Copilot reviewed 11 out of 12 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
webapp/pnpm-lock.yaml Added shadow-player as a workspace dependency for multi-video-player
webapp/packages/shadow-player/src/streamer.ts Added disconnect method, session-not-found error type, and improved WebSocket lifecycle management
webapp/packages/shadow-player/package.json Changed to public package v1.0.0 with updated entry points
webapp/packages/shadow-player/build.ps1 Added PowerShell build script for package distribution
webapp/packages/multi-video-player/vite.config.ts Externalized video.js and shadow-player dependencies
webapp/packages/multi-video-player/src/live-streamer.ts New class for managing live recording streams with shadow-player
webapp/packages/multi-video-player/src/index.ts Exported gateway-api and live-streamer modules
webapp/packages/multi-video-player/src/gateway-api.ts New Gateway API client for recording operations
webapp/packages/multi-video-player/package.json Changed to public package v1.0.0 with peer dependencies
webapp/packages/multi-video-player/build.ps1 Added PowerShell build script for package distribution
webapp/apps/recording-player/src/streamers/webm.ts Updated imports to use published package paths
.github/workflows/publish-libraries.yml Added multi-video-player and shadow-player to npm publishing workflow
Files not reviewed (1)
  • webapp/pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

}

public disconnect(): void {
this.isDisconnecting = true;
Copy link

Copilot AI Nov 19, 2025

Choose a reason for hiding this comment

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

The isDisconnecting flag is set to true at the start of disconnect() but is never reset to false. If the shadow player component is reused after calling disconnect(), subsequent operations might behave incorrectly. Consider resetting this flag after cleanup is complete, or document that the component should not be reused after disconnect.

Copilot uses AI. Check for mistakes.
Comment on lines 11 to +15
"types": "./dist/shadow-player.d.ts",
"exports": {
".": {
"import": "./dist/webm-stream-player.js",
"types": "./dist/shadow-player.d.ts"
Copy link

Copilot AI Nov 19, 2025

Choose a reason for hiding this comment

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

The main and module fields point to webm-stream-player.js but the exports field also points to webm-stream-player.js, while the types field points to shadow-player.d.ts. This inconsistency in naming could cause issues. The main bundle filename appears to be webm-stream-player.js but the type definitions use a different basename (shadow-player). Consider ensuring these filenames are consistent or verify that the build process generates both correctly.

Suggested change
"types": "./dist/shadow-player.d.ts",
"exports": {
".": {
"import": "./dist/webm-stream-player.js",
"types": "./dist/shadow-player.d.ts"
"types": "./dist/webm-stream-player.d.ts",
"exports": {
".": {
"import": "./dist/webm-stream-player.js",
"types": "./dist/webm-stream-player.d.ts"

Copilot uses AI. Check for mistakes.
"vite-plugin-bundle-css": "^0.1.1"
},
"peerDependencies": {
"@devolutions/shadow-player": "workspace:*",
Copy link

Copilot AI Nov 19, 2025

Choose a reason for hiding this comment

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

The peer dependency for @devolutions/shadow-player is set to workspace:*, but this is not a valid version specifier for published packages. When this package is published to npm, workspace:* won't resolve correctly for consumers outside the workspace. This should be changed to a semantic version range like ^1.0.0 to match the published version of shadow-player.

Suggested change
"@devolutions/shadow-player": "workspace:*",
"@devolutions/shadow-player": "^1.0.0",

Copilot uses AI. Check for mistakes.
Copy link
Member

@CBenoit CBenoit Nov 19, 2025

Choose a reason for hiding this comment

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

Probably needs to be addressed, but I’m not sure how we are supposed to deal with that with npm.

Copy link
Member

Choose a reason for hiding this comment

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

suggestion: Refer to my suggestion about the "two package.json files" approach.

if (reactiveSourceBuffer && mediaSource.readyState === 'open') {
try {
mediaSource.endOfStream();
} catch (error) {
Copy link

Copilot AI Nov 19, 2025

Choose a reason for hiding this comment

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

The error in the catch block is being silently swallowed without any logging or handling. Consider at least logging the error for debugging purposes, or add a comment explaining why it's safe to ignore this error.

Suggested change
} catch (error) {
} catch (error) {
console.error('endOfStream error:', error);

Copilot uses AI. Check for mistakes.
Comment on lines +263 to +264
} catch (error) {
}
Copy link

Copilot AI Nov 19, 2025

Choose a reason for hiding this comment

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

The error in the catch block is being silently swallowed without any logging or handling. Consider at least logging the error for debugging purposes, or add a comment explaining why it's safe to ignore this error.

Copilot uses AI. Check for mistakes.
}

const sessions = await response.json();
const sessionExists = sessions.some((session: any) => session.id === this.sessionId);
Copy link

Copilot AI Nov 19, 2025

Choose a reason for hiding this comment

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

Using any type defeats the purpose of TypeScript's type safety. Consider defining a proper interface for the session object, e.g., interface Session { id: string; ... } and typing the sessions array appropriately.

Copilot uses AI. Check for mistakes.
@@ -0,0 +1,19 @@
#!/bin/env pwsh
Copy link

Copilot AI Nov 19, 2025

Choose a reason for hiding this comment

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

The shebang line is incorrect. It should be #!/usr/bin/env pwsh instead of #!/bin/env pwsh to properly locate the pwsh interpreter using the env command.

Suggested change
#!/bin/env pwsh
#!/usr/bin/env pwsh

Copilot uses AI. Check for mistakes.
@@ -0,0 +1,19 @@
#!/bin/env pwsh
Copy link

Copilot AI Nov 19, 2025

Choose a reason for hiding this comment

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

The shebang line is incorrect. It should be #!/usr/bin/env pwsh instead of #!/bin/env pwsh to properly locate the pwsh interpreter using the env command.

Suggested change
#!/bin/env pwsh
#!/usr/bin/env pwsh

Copilot uses AI. Check for mistakes.
Comment on lines +273 to +274
} catch (error) {
}
Copy link

Copilot AI Nov 19, 2025

Choose a reason for hiding this comment

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

The error in the catch block is being silently swallowed without any logging or handling. Consider at least logging the error for debugging purposes, or add a comment explaining why it's safe to ignore this error.

Copilot uses AI. Check for mistakes.

return sessionExists;
} catch (error) {

Copy link

Copilot AI Nov 19, 2025

Choose a reason for hiding this comment

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

The error in the catch block is being silently swallowed without any logging or handling. Consider at least logging the error for debugging purposes so issues can be diagnosed if the session check fails unexpectedly.

Suggested change
console.error('Error checking session activity:', error);

Copilot uses AI. Check for mistakes.
@CBenoit
Copy link
Member

CBenoit commented Nov 19, 2025

Please follow this Git workflow: https://github.com/Devolutions/devolutions-gateway/blob/4304148f0979809d3941543d2083101abc293840/AGENTS.md#git-workflow
There is no library scope yet for these, but you can use npm for now.

Comment on lines +32 to 35
"peerDependencies": {
"@devolutions/shadow-player": "workspace:*",
"video.js": "^8.0.0"
},
Copy link
Member

Choose a reason for hiding this comment

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

question: Why are they "peer dependencies"? I’m not super familiar with that.

"private": true,
"version": "0.0.0",
"private": false,
"version": "1.0.0",
Copy link
Member

Choose a reason for hiding this comment

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

suggestion: Let’s start with 0.1.0

Copy link
Member

@CBenoit CBenoit left a comment

Choose a reason for hiding this comment

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

I’m not an expert on npm packages, but I’ve done some digging, and I have some questions. Let’s check that together.

I’m not able to build the npm packages locally by running the scripts, did you check that?

Image Image

For ts-angular-client, the dist folder has a package.json:

Image

The package.json in dist is the one containing the "exports" key and other publishing related keys, not the top-level one.

I think the "two package.json approach" is the preferred pattern for TypeScript packages. It ensures we only publish a clean JS/d.ts only package using the resulting artifacts found in the dist/ folder. Also avoid import {…} from 'package/dist' in favor of import {…} from 'package', avoid unnecessary dev metadata, etc.

suggestion: Keep the root package.json files private ("private": true). Keep the types, module, main, exports and other runtime dependencies in the generated dist/package.json file.

You can either use a build script, or the vite-plugin-static-copy vite plugin. At least for IronRDP and IronVNC we use the vite-plugin-static-copy approach, you can refer to it: https://github.com/Devolutions/IronRDP/tree/master/web-client/iron-remote-desktop

note: Make sure to keep the publish-related metadata in the dist/package.json file only. In my comments I mention the description key. This must be in dist/package.json.

Comment on lines 36 to 39
"dependencies": {
"vite-plugin-dts": "^4.5.3",
"vite-plugin-static-copy": "^2.3.0"
},
Copy link
Member

Choose a reason for hiding this comment

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

issue?: Probably needs to be in devDependencies? Double check if these are development tools.

Comment on lines 28 to 29
"ts-node": "^10.9.2",
"vite-plugin-dts": "^4.3.0"
Copy link
Member

Choose a reason for hiding this comment

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

issue: I’m pretty sure those are development tools.

Copy link
Member

Choose a reason for hiding this comment

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

issue: Missing description field.

"vite-plugin-bundle-css": "^0.1.1"
},
"peerDependencies": {
"@devolutions/shadow-player": "workspace:*",
Copy link
Member

Choose a reason for hiding this comment

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

suggestion: Refer to my suggestion about the "two package.json files" approach.

@CBenoit
Copy link
Member

CBenoit commented Nov 19, 2025

  • Changed @devolutions/multi-video-player and @devolutions/shadow-player from
    private to public

    • Added peer dependencies: @devolutions/shadow-player, video.js

Mentioned that in one of my comments, but I would like some explanation on the rationale (mostly because I’m not familiar with that).

* Gateway API client for fetching recording data

[…]

* shadow-player: Added disconnect for proper cleanup

Note that it’s preferable to open multiple pull requests: one per change.

For instance:

  • One PR adding the Gateway API client code
  • One PR adding the proper disconnect logic
  • One PR for preparing the packages for publishing

It’s much easier to review, and easier to track the changes properly. Overall it’s easier to quickly merge multiple smaller PRs.

@Skyzreal
Copy link
Author

Skyzreal commented Nov 21, 2025

Thanks for the detailed feedback, Benoit! You're absolutely right, this should have been 3 separate PRs. I’ll make sure to keep future PRs smaller and more focused.

I should've written better description in my PR description, but basically the gateway-api is used to fetch data. It used to be scattered, but my idea is to have a reusable API (I have not touched the previous code so other projects can still use what is there).

For the shadow-player disconnect method, that one is just for proper cleanup. When testing with DVLS, I noticed WebSocket connections and video buffers stayed alive even after closing the dialog, so this makes sure everything is freed correctly.

Regarding dependencies: I initially used peerDependencies because, from what I could see, they can help avoid duplicates and let users manage their versions (especially for something big like video.js). I wasn't sure how RDM handled packages, so I wanted to avoid duplications (having 2 video.js), but after looking into it more, modern package managers (npm 7+) dedupe automatically, and regular dependencies give a simpler install experience, so I made the change to switch to regular dependencies

Right now I’m updating the packages to follow the two-package.json approach, working on that fix now

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

publish-required Please publish libraries (`Devolutions.Gateway.Utils`, OpenAPI clients, etc)

Development

Successfully merging this pull request may close these issues.

3 participants