Skip to content

encodeToBase64 / decodeFromBase64 depend on Buffer, which forces a polyfill in browser bundles #276

Closed

Description

@sasjs-dev

What

encodeToBase64 and decodeFromBase64 use Buffer, which does not exist in browsers, so any bundler that includes utils/base64.ts injects a Buffer polyfill:

src/utils/base64.ts:15   Buffer.from(randomHexPrefix() + str).toString('base64')
src/utils/base64.ts:23   Buffer.from(str, 'base64').toString()

#274 removed the crypto import from this file, which was the part pulling the 555 KB crypto-browserify chain. Buffer is the remaining browser-hostile dependency in the encode path.

Why it matters

It keeps a polyfill in the bundle of every browser consumer that reaches this file, and @sasjs/utils is a browser-facing package. The function is otherwise pure - the prefix is obfuscation padding and the encoding is plain base64.

Proposed fix

Use the platform APIs, keeping the output byte-identical:

encode: btoa(String.fromCharCode(...new TextEncoder().encode(s)))
decode: new TextDecoder().decode(Uint8Array.from(atob(str), (c) => c.charCodeAt(0)))

TextEncoder/TextDecoder matter rather than a bare btoa: this function stores CLI credentials, and btoa is latin1, so a naive swap corrupts non-ASCII passwords. The spread also needs care for long strings - build the binary string in chunks rather than spreading a large array.

Keep the Node Buffer path if the extra branch is wanted, or drop it now that the floor is node >= 22, where btoa/atob/TextEncoder are globals.

Note on scope

This does not by itself remove the Buffer polyfill from sasjs/adapter - the adapter uses Buffer.from directly in getAccessTokenForViya.ts and refreshTokensForViya.ts. It is worth doing so this file is browser-clean on its own merits, and so the encode path carries no dependency a bundler has to shim.

Verification

  • round-trip a non-ASCII password (for example passwoerd-日本語) through encode then decode, and assert equality
  • assert the emitted value still matches ^{sasjs_encoded}, and that decode still strips a fixed 200-character offset, so stored credentials keep decoding
  • assert the module contains no Buffer reference

Activity

  1. added a commit that references this issue on Oct 15, 2026
    79ef9c8
  2. sasjs-gha commented on Oct 15, 2026

    @sasjs-gha

    🎉 This issue has been resolved in version 3.7.4 🎉

    The release is available on GitHub release

    Your semantic-release bot 📦🚀

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestjavascriptPull requests that update javascript codereleased

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions