Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

RFC Browser Support & Buffer Module #213

Open
viglucci opened this issue Jan 26, 2022 · 3 comments
Open

RFC Browser Support & Buffer Module #213

viglucci opened this issue Jan 26, 2022 · 3 comments
Labels
1.0 Pullrequests & issues related to the Typescript rewrite and 1.0 release enhancement Suggests, requests, or implements a feature or enhancement

Comments

@viglucci
Copy link
Member

viglucci commented Jan 26, 2022

Providing a pleasant experience when using RSocket-js in browser environments is a goal of the work we are doing in #158, and as we approach a preview release, I would like to take a closer look at what changes might be needed, and what options we have.

As demonstrated in #212, we know that the Buffer module, which is native to node, but unavailable in Browser environments, is something that will need to be supported in some way (as suspected) as it is required to produce the Binary data involved with sending and receiving payloads.

There are a few possible directions to go with this, in no particular order:

A) require consumers to polyfill the Buffer module with their build tool of choice


Most frontend build tools (Webpack, etc.) support a mechanism to polyfill Node APIs with alternatives that support browser environments. For example, the webpack resolve.fallback configuration can be used to provide an alternative version of Buffer that has browser compact.

Learn more here.

const webpackConfig = {
    ...
    resolve: {
        ...
        fallback: {
            buffer: require.resolve('buffer/'),
        },
    },
};

Pros:

  • no additional changes are required to rsocket-js packages to support browser environments
  • consumers may provide whichever polyfill they see fit

Cons:

  • rsocket-js packages are not browser compatible without special build tool configuration
  • an increased learning curve for consumers

B) update APIs to accept browser compatible Buffer modules as a dependency


A dependency injection type pattern could be adopted by the public APIs to accept a browser compatible Buffer implementation. This pattern would likely involve introducing a similar configuration as the wsCreator on the WebsocketClientTransport API. See below.

Pros:

  • APIs support browser environments without special build tool configuration
  • consumers may provide whichever polyfill they see fit

Cons:

  • potentially significant changes needed to expose necessary configuration and dependency injection throughout various layers of the current package implementations
  • some packages that currently expose primarily functional APIs could require significant alteration to support dependency injection/configuration. @rsocket/composite-metadata being one such package.

Example:

Changing the encodeRoutes API could require adding an additional argument that would provide a browser-compliant Buffer implementation.

export function encodeRoutes(...routes: string[], bufferImpl: Buffer): Buffer {
  if (routes.length < 1) {
    throw new Error("routes should be non empty array");
  }

  return bufferImpl.concat(routes.map((route) => encodeRoute(route, bufferImpl)));
}

Alternatively, @rsocket/composite-metadata could be altered to provide a more class-based approach which could provide benefits to dependency injection patterns, but at the potential cost of losing module tree-shaking at build time, due to the loss of individually exported functions. Loss of module tree-shaking may not be a concern though due to the relatively minimal size of the composite-metadata APIs.

C) produce browser compatible build artifacts with pre-provided Buffer polyfills


Currently, the project builds for commonjs environments, which supports Node as well as build tooling such as Webpack quite well. We could additionally introduce build targets/configurations that would provide a browser-compatible Buffer module within scope.

"module": "commonjs",

Pros:

  • APIs support browser environments without special build tool configuration

Cons:

  • consumers cannot control which Buffer polyfill is used
  • potentially difficult to control the size of the artifacts produced, which is a consideration when building for browsers
  • more complex build configuration and tooling required to maintain the project
  • consumers required to import specific artifacts for usage in browser environments (ex: require('@rsocket/core/browser'))
  • likely challenges with inter-package dependencies and build tooling

I would argue that this approach may be the least appealing approach from the package's maintenance perspective.

@viglucci viglucci changed the title RFC Browser Interop & Buffer Module (draft) RFC Browser Interop & Buffer Module Jan 26, 2022
@viglucci viglucci added the 1.0 Pullrequests & issues related to the Typescript rewrite and 1.0 release label Jan 26, 2022
@viglucci viglucci changed the title (draft) RFC Browser Interop & Buffer Module RFC Browser Support & Buffer Module Jan 26, 2022
@OlegDokuka
Copy link
Member

@viglucci as of me, the Node like Buffer polyfill is the best option

rsocket-js packages are not browser compatible without special build tool configuration

indeed, the configuration of a polyfill is something to be done if not automatically done by build-tool.

an increased learning curve for consumers

I guess this can be compensated with nice documentation. As for me, a standard Buffer API is the best option to decrease the learning curve since standard Node Buffer API already has very nice documentation so we don't have to do anything except document how to configure polyfills for different builds tools.

Based on the issue tracker, the other options would cause more challenges rather than benefits since

update APIs to accept browser compatible Buffer modules as a dependency

this requires extra configuration for every client/server setup while the polyfill options can be done once for the whole application and in a single place.

@benwiles1
Copy link

I am only loosely following this project, but I was wondering why you can't use Uint8Array as that is natively supported by node and browsers?

@viglucci
Copy link
Member Author

viglucci commented Jan 28, 2022

@benwiles1 I hadn't considered that and I'm not overly familiar with Uint8Array, but with some really brief searching, it seems like Buffer is a subclass of Uint8Array so it could be promising, and suggests that most of the necessary functionality would be supported.

The interesting thing I learned is that the feross/buffer package, which is the most popular Buffer polyfill that I know of actually uses Uint8Array under the hood (according to their docs), so I'm wondering if we should just adopt the feross/buffer package as the Buffer implementation for usage in Node and browsers.

The Buffer constructor returns instances of Uint8Array that have their prototype changed to Buffer.prototype. Furthermore, Buffer is a subclass of Uint8Array, so the returned instances will have all the node Buffer methods and the Uint8Array methods. Square bracket notation works as expected -- it returns a single octet.

The Uint8Array prototype remains unmodified.

Performance would of course be a consideration, and we would need to do some profiling to see if there is a noticeable overhead to doing so.

@viglucci viglucci added the enhancement Suggests, requests, or implements a feature or enhancement label Mar 6, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
1.0 Pullrequests & issues related to the Typescript rewrite and 1.0 release enhancement Suggests, requests, or implements a feature or enhancement
Projects
None yet
Development

No branches or pull requests

3 participants