/** * Text Encoding Factory * * Creates encoder/decoder instances with automatic polyfill selection * * @module text-encoding@1.0.0 */ import { TextDecoderPolyfill } from './TextDecoderPolyfill'; import { TextEncoderPolyfill } from './TextEncoderPolyfill'; import type { ITextDecoder, ITextEncoder, ITextEncodingFactory, TextDecoderOptions, } from './interfaces'; export class TextEncodingFactory implements ITextEncodingFactory { private static instance: TextEncodingFactory; /** * Get factory singleton instance */ static getInstance(): TextEncodingFactory { if (!TextEncodingFactory.instance) { TextEncodingFactory.instance = new TextEncodingFactory(); } return TextEncodingFactory.instance; } /** * Create a new TextEncoder instance * Uses native implementation if available, otherwise polyfill */ createEncoder(): ITextEncoder { // @ts-expect-error - Check for native TextEncoder if (typeof TextEncoder !== 'undefined') { try { // @ts-expect-error - Try to use native return new TextEncoder(); } catch (e) { // Fall back to polyfill if native fails console.warn('[TextEncodingFactory] Native TextEncoder failed, using polyfill:', e); } } return new TextEncoderPolyfill(); } /** * Create a new TextDecoder instance * Uses native implementation if available, otherwise polyfill */ createDecoder(label: string = 'utf-8', options?: TextDecoderOptions): ITextDecoder { // @ts-expect-error - Check for native TextDecoder if (typeof TextDecoder !== 'undefined') { try { // @ts-expect-error - Try to use native return new TextDecoder(label, options); } catch (e) { // Fall back to polyfill if native fails console.warn('[TextEncodingFactory] Native TextDecoder failed, using polyfill:', e); } } return new TextDecoderPolyfill(label, options); } /** * Check if native TextEncoder/TextDecoder are available */ isNativelySupported(): boolean { // @ts-expect-error - Check global scope if (typeof TextEncoder === 'undefined' || typeof TextDecoder === 'undefined') { return false; } // Try to instantiate to make sure they work try { // @ts-expect-error const encoder = new TextEncoder(); // @ts-expect-error const decoder = new TextDecoder(); // Basic functionality test const testString = 'test'; const encoded = encoder.encode(testString); const decoded = decoder.decode(encoded); return decoded === testString; } catch (_e) { return false; } } } /** * Convenience function to create an encoder */ export function createTextEncoder(): ITextEncoder { return TextEncodingFactory.getInstance().createEncoder(); } /** * Convenience function to create a decoder */ export function createTextDecoder(label?: string, options?: TextDecoderOptions): ITextDecoder { return TextEncodingFactory.getInstance().createDecoder(label, options); } /** * Install polyfills globally if not present * This makes TextEncoder/TextDecoder available everywhere */ export function installTextEncodingPolyfills(): void { // @ts-expect-error if (typeof global !== 'undefined') { // @ts-expect-error if (typeof global.TextEncoder === 'undefined') { // @ts-expect-error global.TextEncoder = TextEncoderPolyfill; console.info('[TextEncodingFactory] Installed TextEncoder polyfill globally'); } // @ts-expect-error if (typeof global.TextDecoder === 'undefined') { // @ts-expect-error global.TextDecoder = TextDecoderPolyfill; console.info('[TextEncodingFactory] Installed TextDecoder polyfill globally'); } } }