@ricsam/isolate-encoding 0.1.11 → 0.1.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -176,6 +176,227 @@ var encodingCode = `
176
176
 
177
177
  return result;
178
178
  };
179
+
180
+ // ============================================
181
+ // Buffer implementation
182
+ // ============================================
183
+
184
+ const BUFFER_SYMBOL = Symbol.for('__isBuffer');
185
+
186
+ class Buffer extends Uint8Array {
187
+ constructor(arg, encodingOrOffset, length) {
188
+ if (typeof arg === 'number') {
189
+ super(arg);
190
+ } else if (typeof arg === 'string') {
191
+ const bytes = stringToBytes(arg, encodingOrOffset || 'utf8');
192
+ super(bytes);
193
+ } else if (arg instanceof ArrayBuffer) {
194
+ if (typeof encodingOrOffset === 'number') {
195
+ super(arg, encodingOrOffset, length);
196
+ } else {
197
+ super(arg);
198
+ }
199
+ } else if (ArrayBuffer.isView(arg)) {
200
+ super(arg.buffer, arg.byteOffset, arg.byteLength);
201
+ } else if (Array.isArray(arg)) {
202
+ super(arg);
203
+ } else {
204
+ super(arg);
205
+ }
206
+ Object.defineProperty(this, BUFFER_SYMBOL, { value: true, writable: false });
207
+ }
208
+
209
+ toString(encoding = 'utf8') {
210
+ encoding = normalizeEncoding(encoding);
211
+ if (encoding === 'utf8') {
212
+ return new TextDecoder('utf-8').decode(this);
213
+ } else if (encoding === 'base64') {
214
+ return bytesToBase64(this);
215
+ } else if (encoding === 'hex') {
216
+ return bytesToHex(this);
217
+ }
218
+ return new TextDecoder('utf-8').decode(this);
219
+ }
220
+
221
+ slice(start, end) {
222
+ const sliced = super.slice(start, end);
223
+ return Buffer.from(sliced);
224
+ }
225
+
226
+ subarray(start, end) {
227
+ const sub = super.subarray(start, end);
228
+ const buf = new Buffer(sub.length);
229
+ buf.set(sub);
230
+ return buf;
231
+ }
232
+
233
+ static from(value, encodingOrOffset, length) {
234
+ if (typeof value === 'string') {
235
+ return new Buffer(value, encodingOrOffset);
236
+ }
237
+ if (value instanceof ArrayBuffer) {
238
+ if (typeof encodingOrOffset === 'number') {
239
+ return new Buffer(value, encodingOrOffset, length);
240
+ }
241
+ return new Buffer(value);
242
+ }
243
+ if (ArrayBuffer.isView(value)) {
244
+ return new Buffer(value);
245
+ }
246
+ if (Array.isArray(value)) {
247
+ return new Buffer(value);
248
+ }
249
+ if (value && typeof value[Symbol.iterator] === 'function') {
250
+ return new Buffer(Array.from(value));
251
+ }
252
+ throw new TypeError('First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object');
253
+ }
254
+
255
+ static alloc(size, fill, encoding) {
256
+ if (typeof size !== 'number' || size < 0) {
257
+ throw new RangeError('Invalid size');
258
+ }
259
+ const buf = new Buffer(size);
260
+ if (fill !== undefined) {
261
+ if (typeof fill === 'number') {
262
+ buf.fill(fill);
263
+ } else if (typeof fill === 'string') {
264
+ const fillBytes = stringToBytes(fill, encoding || 'utf8');
265
+ for (let i = 0; i < size; i++) {
266
+ buf[i] = fillBytes[i % fillBytes.length];
267
+ }
268
+ } else if (Buffer.isBuffer(fill) || fill instanceof Uint8Array) {
269
+ for (let i = 0; i < size; i++) {
270
+ buf[i] = fill[i % fill.length];
271
+ }
272
+ }
273
+ }
274
+ return buf;
275
+ }
276
+
277
+ static allocUnsafe(size) {
278
+ if (typeof size !== 'number' || size < 0) {
279
+ throw new RangeError('Invalid size');
280
+ }
281
+ return new Buffer(size);
282
+ }
283
+
284
+ static concat(list, totalLength) {
285
+ if (!Array.isArray(list)) {
286
+ throw new TypeError('list argument must be an array');
287
+ }
288
+ if (list.length === 0) {
289
+ return Buffer.alloc(0);
290
+ }
291
+ if (totalLength === undefined) {
292
+ totalLength = 0;
293
+ for (const buf of list) {
294
+ totalLength += buf.length;
295
+ }
296
+ }
297
+ const result = Buffer.alloc(totalLength);
298
+ let offset = 0;
299
+ for (const buf of list) {
300
+ if (offset + buf.length > totalLength) {
301
+ result.set(buf.subarray(0, totalLength - offset), offset);
302
+ break;
303
+ }
304
+ result.set(buf, offset);
305
+ offset += buf.length;
306
+ }
307
+ return result;
308
+ }
309
+
310
+ static isBuffer(obj) {
311
+ return obj != null && obj[BUFFER_SYMBOL] === true;
312
+ }
313
+
314
+ static byteLength(string, encoding = 'utf8') {
315
+ if (typeof string !== 'string') {
316
+ if (ArrayBuffer.isView(string) || string instanceof ArrayBuffer) {
317
+ return string.byteLength;
318
+ }
319
+ throw new TypeError('First argument must be a string, Buffer, or ArrayBuffer');
320
+ }
321
+ encoding = normalizeEncoding(encoding);
322
+ if (encoding === 'utf8') {
323
+ return new TextEncoder().encode(string).length;
324
+ } else if (encoding === 'base64') {
325
+ const padding = (string.match(/=+$/) || [''])[0].length;
326
+ return Math.floor((string.length * 3) / 4) - padding;
327
+ } else if (encoding === 'hex') {
328
+ return Math.floor(string.length / 2);
329
+ }
330
+ return new TextEncoder().encode(string).length;
331
+ }
332
+
333
+ static isEncoding(encoding) {
334
+ return ['utf8', 'utf-8', 'base64', 'hex'].includes(normalizeEncoding(encoding));
335
+ }
336
+ }
337
+
338
+ function normalizeEncoding(encoding) {
339
+ if (!encoding) return 'utf8';
340
+ const lower = String(encoding).toLowerCase().replace('-', '');
341
+ if (lower === 'utf8' || lower === 'utf-8') return 'utf8';
342
+ if (lower === 'base64') return 'base64';
343
+ if (lower === 'hex') return 'hex';
344
+ return lower;
345
+ }
346
+
347
+ function stringToBytes(str, encoding) {
348
+ encoding = normalizeEncoding(encoding);
349
+ if (encoding === 'utf8') {
350
+ return new TextEncoder().encode(str);
351
+ } else if (encoding === 'base64') {
352
+ return base64ToBytes(str);
353
+ } else if (encoding === 'hex') {
354
+ return hexToBytes(str);
355
+ }
356
+ return new TextEncoder().encode(str);
357
+ }
358
+
359
+ function base64ToBytes(str) {
360
+ const binary = atob(str);
361
+ const bytes = new Uint8Array(binary.length);
362
+ for (let i = 0; i < binary.length; i++) {
363
+ bytes[i] = binary.charCodeAt(i);
364
+ }
365
+ return bytes;
366
+ }
367
+
368
+ function bytesToBase64(bytes) {
369
+ let binary = '';
370
+ for (let i = 0; i < bytes.length; i++) {
371
+ binary += String.fromCharCode(bytes[i]);
372
+ }
373
+ return btoa(binary);
374
+ }
375
+
376
+ function hexToBytes(str) {
377
+ if (str.length % 2 !== 0) {
378
+ throw new TypeError('Invalid hex string');
379
+ }
380
+ const bytes = new Uint8Array(str.length / 2);
381
+ for (let i = 0; i < str.length; i += 2) {
382
+ const byte = parseInt(str.substr(i, 2), 16);
383
+ if (isNaN(byte)) {
384
+ throw new TypeError('Invalid hex string');
385
+ }
386
+ bytes[i / 2] = byte;
387
+ }
388
+ return bytes;
389
+ }
390
+
391
+ function bytesToHex(bytes) {
392
+ let hex = '';
393
+ for (let i = 0; i < bytes.length; i++) {
394
+ hex += bytes[i].toString(16).padStart(2, '0');
395
+ }
396
+ return hex;
397
+ }
398
+
399
+ globalThis.Buffer = Buffer;
179
400
  })();
180
401
  `;
181
402
  async function setupEncoding(context) {
@@ -185,4 +406,4 @@ async function setupEncoding(context) {
185
406
  };
186
407
  }
187
408
 
188
- //# debugId=799BCB9316B516F464756E2164756E21
409
+ //# debugId=49911E9EE082911E64756E2164756E21
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../../src/index.ts"],
4
4
  "sourcesContent": [
5
- "import type ivm from \"isolated-vm\";\n\nexport interface EncodingHandle {\n dispose(): void;\n}\n\nconst encodingCode = `\n(function() {\n // Define DOMException if not available\n if (typeof DOMException === 'undefined') {\n globalThis.DOMException = class DOMException extends Error {\n constructor(message, name) {\n super(message);\n this.name = name || 'DOMException';\n }\n };\n }\n\n const base64Chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';\n\n // Build reverse lookup table\n const base64Lookup = new Map();\n for (let i = 0; i < base64Chars.length; i++) {\n base64Lookup.set(base64Chars[i], i);\n }\n\n globalThis.btoa = function btoa(str) {\n if (str === undefined) {\n throw new TypeError(\"1 argument required, but only 0 present.\");\n }\n\n str = String(str);\n\n // Check for characters outside Latin-1 range\n for (let i = 0; i < str.length; i++) {\n if (str.charCodeAt(i) > 255) {\n throw new DOMException(\n \"The string to be encoded contains characters outside of the Latin1 range.\",\n \"InvalidCharacterError\"\n );\n }\n }\n\n if (str.length === 0) {\n return '';\n }\n\n let result = '';\n let i = 0;\n\n while (i < str.length) {\n const a = str.charCodeAt(i++);\n const bExists = i < str.length;\n const b = bExists ? str.charCodeAt(i++) : 0;\n const cExists = i < str.length;\n const c = cExists ? str.charCodeAt(i++) : 0;\n\n const triplet = (a << 16) | (b << 8) | c;\n\n result += base64Chars[(triplet >> 18) & 0x3F];\n result += base64Chars[(triplet >> 12) & 0x3F];\n result += bExists ? base64Chars[(triplet >> 6) & 0x3F] : '=';\n result += cExists ? base64Chars[triplet & 0x3F] : '=';\n }\n\n return result;\n };\n\n globalThis.atob = function atob(str) {\n if (str === undefined) {\n throw new TypeError(\"1 argument required, but only 0 present.\");\n }\n\n str = String(str);\n\n // Remove whitespace\n str = str.replace(/[\\\\t\\\\n\\\\f\\\\r ]/g, '');\n\n // Validate characters and length\n if (str.length === 0) {\n return '';\n }\n\n // Check for invalid characters (before padding normalization)\n for (let i = 0; i < str.length; i++) {\n const c = str[i];\n if (c !== '=' && !base64Lookup.has(c)) {\n throw new DOMException(\n \"The string to be decoded is not correctly encoded.\",\n \"InvalidCharacterError\"\n );\n }\n }\n\n // Validate padding position (must be at end)\n const paddingIndex = str.indexOf('=');\n if (paddingIndex !== -1) {\n for (let i = paddingIndex; i < str.length; i++) {\n if (str[i] !== '=') {\n throw new DOMException(\n \"The string to be decoded is not correctly encoded.\",\n \"InvalidCharacterError\"\n );\n }\n }\n const paddingLength = str.length - paddingIndex;\n if (paddingLength > 2) {\n throw new DOMException(\n \"The string to be decoded is not correctly encoded.\",\n \"InvalidCharacterError\"\n );\n }\n }\n\n // Length without padding must be valid (can't have remainder of 1)\n const strWithoutPadding = str.replace(/=/g, '');\n if (strWithoutPadding.length % 4 === 1) {\n throw new DOMException(\n \"The string to be decoded is not correctly encoded.\",\n \"InvalidCharacterError\"\n );\n }\n\n // Pad to multiple of 4 if needed (for inputs without explicit padding)\n while (str.length % 4 !== 0) {\n str += '=';\n }\n\n let result = '';\n let i = 0;\n\n while (i < str.length) {\n const a = base64Lookup.get(str[i++]) ?? 0;\n const b = base64Lookup.get(str[i++]) ?? 0;\n const c = base64Lookup.get(str[i++]) ?? 0;\n const d = base64Lookup.get(str[i++]) ?? 0;\n\n const triplet = (a << 18) | (b << 12) | (c << 6) | d;\n\n result += String.fromCharCode((triplet >> 16) & 0xFF);\n if (str[i - 2] !== '=') {\n result += String.fromCharCode((triplet >> 8) & 0xFF);\n }\n if (str[i - 1] !== '=') {\n result += String.fromCharCode(triplet & 0xFF);\n }\n }\n\n return result;\n };\n})();\n`;\n\n/**\n * Setup encoding APIs in an isolated-vm context\n *\n * Injects atob and btoa for Base64 encoding/decoding\n *\n * @example\n * const handle = await setupEncoding(context);\n * await context.eval(`\n * const encoded = btoa(\"hello\");\n * const decoded = atob(encoded);\n * `);\n */\nexport async function setupEncoding(\n context: ivm.Context\n): Promise<EncodingHandle> {\n context.evalSync(encodingCode);\n return {\n dispose() {\n // No resources to cleanup for pure JS injection\n },\n };\n}\n"
5
+ "import type ivm from \"isolated-vm\";\n\nexport interface EncodingHandle {\n dispose(): void;\n}\n\nconst encodingCode = `\n(function() {\n // Define DOMException if not available\n if (typeof DOMException === 'undefined') {\n globalThis.DOMException = class DOMException extends Error {\n constructor(message, name) {\n super(message);\n this.name = name || 'DOMException';\n }\n };\n }\n\n const base64Chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';\n\n // Build reverse lookup table\n const base64Lookup = new Map();\n for (let i = 0; i < base64Chars.length; i++) {\n base64Lookup.set(base64Chars[i], i);\n }\n\n globalThis.btoa = function btoa(str) {\n if (str === undefined) {\n throw new TypeError(\"1 argument required, but only 0 present.\");\n }\n\n str = String(str);\n\n // Check for characters outside Latin-1 range\n for (let i = 0; i < str.length; i++) {\n if (str.charCodeAt(i) > 255) {\n throw new DOMException(\n \"The string to be encoded contains characters outside of the Latin1 range.\",\n \"InvalidCharacterError\"\n );\n }\n }\n\n if (str.length === 0) {\n return '';\n }\n\n let result = '';\n let i = 0;\n\n while (i < str.length) {\n const a = str.charCodeAt(i++);\n const bExists = i < str.length;\n const b = bExists ? str.charCodeAt(i++) : 0;\n const cExists = i < str.length;\n const c = cExists ? str.charCodeAt(i++) : 0;\n\n const triplet = (a << 16) | (b << 8) | c;\n\n result += base64Chars[(triplet >> 18) & 0x3F];\n result += base64Chars[(triplet >> 12) & 0x3F];\n result += bExists ? base64Chars[(triplet >> 6) & 0x3F] : '=';\n result += cExists ? base64Chars[triplet & 0x3F] : '=';\n }\n\n return result;\n };\n\n globalThis.atob = function atob(str) {\n if (str === undefined) {\n throw new TypeError(\"1 argument required, but only 0 present.\");\n }\n\n str = String(str);\n\n // Remove whitespace\n str = str.replace(/[\\\\t\\\\n\\\\f\\\\r ]/g, '');\n\n // Validate characters and length\n if (str.length === 0) {\n return '';\n }\n\n // Check for invalid characters (before padding normalization)\n for (let i = 0; i < str.length; i++) {\n const c = str[i];\n if (c !== '=' && !base64Lookup.has(c)) {\n throw new DOMException(\n \"The string to be decoded is not correctly encoded.\",\n \"InvalidCharacterError\"\n );\n }\n }\n\n // Validate padding position (must be at end)\n const paddingIndex = str.indexOf('=');\n if (paddingIndex !== -1) {\n for (let i = paddingIndex; i < str.length; i++) {\n if (str[i] !== '=') {\n throw new DOMException(\n \"The string to be decoded is not correctly encoded.\",\n \"InvalidCharacterError\"\n );\n }\n }\n const paddingLength = str.length - paddingIndex;\n if (paddingLength > 2) {\n throw new DOMException(\n \"The string to be decoded is not correctly encoded.\",\n \"InvalidCharacterError\"\n );\n }\n }\n\n // Length without padding must be valid (can't have remainder of 1)\n const strWithoutPadding = str.replace(/=/g, '');\n if (strWithoutPadding.length % 4 === 1) {\n throw new DOMException(\n \"The string to be decoded is not correctly encoded.\",\n \"InvalidCharacterError\"\n );\n }\n\n // Pad to multiple of 4 if needed (for inputs without explicit padding)\n while (str.length % 4 !== 0) {\n str += '=';\n }\n\n let result = '';\n let i = 0;\n\n while (i < str.length) {\n const a = base64Lookup.get(str[i++]) ?? 0;\n const b = base64Lookup.get(str[i++]) ?? 0;\n const c = base64Lookup.get(str[i++]) ?? 0;\n const d = base64Lookup.get(str[i++]) ?? 0;\n\n const triplet = (a << 18) | (b << 12) | (c << 6) | d;\n\n result += String.fromCharCode((triplet >> 16) & 0xFF);\n if (str[i - 2] !== '=') {\n result += String.fromCharCode((triplet >> 8) & 0xFF);\n }\n if (str[i - 1] !== '=') {\n result += String.fromCharCode(triplet & 0xFF);\n }\n }\n\n return result;\n };\n\n // ============================================\n // Buffer implementation\n // ============================================\n\n const BUFFER_SYMBOL = Symbol.for('__isBuffer');\n\n class Buffer extends Uint8Array {\n constructor(arg, encodingOrOffset, length) {\n if (typeof arg === 'number') {\n super(arg);\n } else if (typeof arg === 'string') {\n const bytes = stringToBytes(arg, encodingOrOffset || 'utf8');\n super(bytes);\n } else if (arg instanceof ArrayBuffer) {\n if (typeof encodingOrOffset === 'number') {\n super(arg, encodingOrOffset, length);\n } else {\n super(arg);\n }\n } else if (ArrayBuffer.isView(arg)) {\n super(arg.buffer, arg.byteOffset, arg.byteLength);\n } else if (Array.isArray(arg)) {\n super(arg);\n } else {\n super(arg);\n }\n Object.defineProperty(this, BUFFER_SYMBOL, { value: true, writable: false });\n }\n\n toString(encoding = 'utf8') {\n encoding = normalizeEncoding(encoding);\n if (encoding === 'utf8') {\n return new TextDecoder('utf-8').decode(this);\n } else if (encoding === 'base64') {\n return bytesToBase64(this);\n } else if (encoding === 'hex') {\n return bytesToHex(this);\n }\n return new TextDecoder('utf-8').decode(this);\n }\n\n slice(start, end) {\n const sliced = super.slice(start, end);\n return Buffer.from(sliced);\n }\n\n subarray(start, end) {\n const sub = super.subarray(start, end);\n const buf = new Buffer(sub.length);\n buf.set(sub);\n return buf;\n }\n\n static from(value, encodingOrOffset, length) {\n if (typeof value === 'string') {\n return new Buffer(value, encodingOrOffset);\n }\n if (value instanceof ArrayBuffer) {\n if (typeof encodingOrOffset === 'number') {\n return new Buffer(value, encodingOrOffset, length);\n }\n return new Buffer(value);\n }\n if (ArrayBuffer.isView(value)) {\n return new Buffer(value);\n }\n if (Array.isArray(value)) {\n return new Buffer(value);\n }\n if (value && typeof value[Symbol.iterator] === 'function') {\n return new Buffer(Array.from(value));\n }\n throw new TypeError('First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object');\n }\n\n static alloc(size, fill, encoding) {\n if (typeof size !== 'number' || size < 0) {\n throw new RangeError('Invalid size');\n }\n const buf = new Buffer(size);\n if (fill !== undefined) {\n if (typeof fill === 'number') {\n buf.fill(fill);\n } else if (typeof fill === 'string') {\n const fillBytes = stringToBytes(fill, encoding || 'utf8');\n for (let i = 0; i < size; i++) {\n buf[i] = fillBytes[i % fillBytes.length];\n }\n } else if (Buffer.isBuffer(fill) || fill instanceof Uint8Array) {\n for (let i = 0; i < size; i++) {\n buf[i] = fill[i % fill.length];\n }\n }\n }\n return buf;\n }\n\n static allocUnsafe(size) {\n if (typeof size !== 'number' || size < 0) {\n throw new RangeError('Invalid size');\n }\n return new Buffer(size);\n }\n\n static concat(list, totalLength) {\n if (!Array.isArray(list)) {\n throw new TypeError('list argument must be an array');\n }\n if (list.length === 0) {\n return Buffer.alloc(0);\n }\n if (totalLength === undefined) {\n totalLength = 0;\n for (const buf of list) {\n totalLength += buf.length;\n }\n }\n const result = Buffer.alloc(totalLength);\n let offset = 0;\n for (const buf of list) {\n if (offset + buf.length > totalLength) {\n result.set(buf.subarray(0, totalLength - offset), offset);\n break;\n }\n result.set(buf, offset);\n offset += buf.length;\n }\n return result;\n }\n\n static isBuffer(obj) {\n return obj != null && obj[BUFFER_SYMBOL] === true;\n }\n\n static byteLength(string, encoding = 'utf8') {\n if (typeof string !== 'string') {\n if (ArrayBuffer.isView(string) || string instanceof ArrayBuffer) {\n return string.byteLength;\n }\n throw new TypeError('First argument must be a string, Buffer, or ArrayBuffer');\n }\n encoding = normalizeEncoding(encoding);\n if (encoding === 'utf8') {\n return new TextEncoder().encode(string).length;\n } else if (encoding === 'base64') {\n const padding = (string.match(/=+$/) || [''])[0].length;\n return Math.floor((string.length * 3) / 4) - padding;\n } else if (encoding === 'hex') {\n return Math.floor(string.length / 2);\n }\n return new TextEncoder().encode(string).length;\n }\n\n static isEncoding(encoding) {\n return ['utf8', 'utf-8', 'base64', 'hex'].includes(normalizeEncoding(encoding));\n }\n }\n\n function normalizeEncoding(encoding) {\n if (!encoding) return 'utf8';\n const lower = String(encoding).toLowerCase().replace('-', '');\n if (lower === 'utf8' || lower === 'utf-8') return 'utf8';\n if (lower === 'base64') return 'base64';\n if (lower === 'hex') return 'hex';\n return lower;\n }\n\n function stringToBytes(str, encoding) {\n encoding = normalizeEncoding(encoding);\n if (encoding === 'utf8') {\n return new TextEncoder().encode(str);\n } else if (encoding === 'base64') {\n return base64ToBytes(str);\n } else if (encoding === 'hex') {\n return hexToBytes(str);\n }\n return new TextEncoder().encode(str);\n }\n\n function base64ToBytes(str) {\n const binary = atob(str);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return bytes;\n }\n\n function bytesToBase64(bytes) {\n let binary = '';\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary);\n }\n\n function hexToBytes(str) {\n if (str.length % 2 !== 0) {\n throw new TypeError('Invalid hex string');\n }\n const bytes = new Uint8Array(str.length / 2);\n for (let i = 0; i < str.length; i += 2) {\n const byte = parseInt(str.substr(i, 2), 16);\n if (isNaN(byte)) {\n throw new TypeError('Invalid hex string');\n }\n bytes[i / 2] = byte;\n }\n return bytes;\n }\n\n function bytesToHex(bytes) {\n let hex = '';\n for (let i = 0; i < bytes.length; i++) {\n hex += bytes[i].toString(16).padStart(2, '0');\n }\n return hex;\n }\n\n globalThis.Buffer = Buffer;\n})();\n`;\n\n/**\n * Setup encoding APIs in an isolated-vm context\n *\n * Injects:\n * - atob/btoa for Base64 encoding/decoding\n * - Buffer class for binary data handling (utf8, base64, hex)\n *\n * @example\n * const handle = await setupEncoding(context);\n * await context.eval(`\n * const encoded = btoa(\"hello\");\n * const decoded = atob(encoded);\n * const buf = Buffer.from(\"hello\");\n * const hex = buf.toString(\"hex\");\n * `);\n */\nexport async function setupEncoding(\n context: ivm.Context\n): Promise<EncodingHandle> {\n context.evalSync(encodingCode);\n return {\n dispose() {\n // No resources to cleanup for pure JS injection\n },\n };\n}\n"
6
6
  ],
7
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAMA,IAAM,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA+JrB,eAAsB,aAAa,CACjC,SACyB;AAAA,EACzB,QAAQ,SAAS,YAAY;AAAA,EAC7B,OAAO;AAAA,IACL,OAAO,GAAG;AAAA,EAGZ;AAAA;",
8
- "debugId": "799BCB9316B516F464756E2164756E21",
7
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAMA,IAAM,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAgYrB,eAAsB,aAAa,CACjC,SACyB;AAAA,EACzB,QAAQ,SAAS,YAAY;AAAA,EAC7B,OAAO;AAAA,IACL,OAAO,GAAG;AAAA,EAGZ;AAAA;",
8
+ "debugId": "49911E9EE082911E64756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/isolate-encoding",
3
- "version": "0.1.11",
3
+ "version": "0.1.12",
4
4
  "type": "commonjs"
5
5
  }
@@ -143,6 +143,227 @@ var encodingCode = `
143
143
 
144
144
  return result;
145
145
  };
146
+
147
+ // ============================================
148
+ // Buffer implementation
149
+ // ============================================
150
+
151
+ const BUFFER_SYMBOL = Symbol.for('__isBuffer');
152
+
153
+ class Buffer extends Uint8Array {
154
+ constructor(arg, encodingOrOffset, length) {
155
+ if (typeof arg === 'number') {
156
+ super(arg);
157
+ } else if (typeof arg === 'string') {
158
+ const bytes = stringToBytes(arg, encodingOrOffset || 'utf8');
159
+ super(bytes);
160
+ } else if (arg instanceof ArrayBuffer) {
161
+ if (typeof encodingOrOffset === 'number') {
162
+ super(arg, encodingOrOffset, length);
163
+ } else {
164
+ super(arg);
165
+ }
166
+ } else if (ArrayBuffer.isView(arg)) {
167
+ super(arg.buffer, arg.byteOffset, arg.byteLength);
168
+ } else if (Array.isArray(arg)) {
169
+ super(arg);
170
+ } else {
171
+ super(arg);
172
+ }
173
+ Object.defineProperty(this, BUFFER_SYMBOL, { value: true, writable: false });
174
+ }
175
+
176
+ toString(encoding = 'utf8') {
177
+ encoding = normalizeEncoding(encoding);
178
+ if (encoding === 'utf8') {
179
+ return new TextDecoder('utf-8').decode(this);
180
+ } else if (encoding === 'base64') {
181
+ return bytesToBase64(this);
182
+ } else if (encoding === 'hex') {
183
+ return bytesToHex(this);
184
+ }
185
+ return new TextDecoder('utf-8').decode(this);
186
+ }
187
+
188
+ slice(start, end) {
189
+ const sliced = super.slice(start, end);
190
+ return Buffer.from(sliced);
191
+ }
192
+
193
+ subarray(start, end) {
194
+ const sub = super.subarray(start, end);
195
+ const buf = new Buffer(sub.length);
196
+ buf.set(sub);
197
+ return buf;
198
+ }
199
+
200
+ static from(value, encodingOrOffset, length) {
201
+ if (typeof value === 'string') {
202
+ return new Buffer(value, encodingOrOffset);
203
+ }
204
+ if (value instanceof ArrayBuffer) {
205
+ if (typeof encodingOrOffset === 'number') {
206
+ return new Buffer(value, encodingOrOffset, length);
207
+ }
208
+ return new Buffer(value);
209
+ }
210
+ if (ArrayBuffer.isView(value)) {
211
+ return new Buffer(value);
212
+ }
213
+ if (Array.isArray(value)) {
214
+ return new Buffer(value);
215
+ }
216
+ if (value && typeof value[Symbol.iterator] === 'function') {
217
+ return new Buffer(Array.from(value));
218
+ }
219
+ throw new TypeError('First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object');
220
+ }
221
+
222
+ static alloc(size, fill, encoding) {
223
+ if (typeof size !== 'number' || size < 0) {
224
+ throw new RangeError('Invalid size');
225
+ }
226
+ const buf = new Buffer(size);
227
+ if (fill !== undefined) {
228
+ if (typeof fill === 'number') {
229
+ buf.fill(fill);
230
+ } else if (typeof fill === 'string') {
231
+ const fillBytes = stringToBytes(fill, encoding || 'utf8');
232
+ for (let i = 0; i < size; i++) {
233
+ buf[i] = fillBytes[i % fillBytes.length];
234
+ }
235
+ } else if (Buffer.isBuffer(fill) || fill instanceof Uint8Array) {
236
+ for (let i = 0; i < size; i++) {
237
+ buf[i] = fill[i % fill.length];
238
+ }
239
+ }
240
+ }
241
+ return buf;
242
+ }
243
+
244
+ static allocUnsafe(size) {
245
+ if (typeof size !== 'number' || size < 0) {
246
+ throw new RangeError('Invalid size');
247
+ }
248
+ return new Buffer(size);
249
+ }
250
+
251
+ static concat(list, totalLength) {
252
+ if (!Array.isArray(list)) {
253
+ throw new TypeError('list argument must be an array');
254
+ }
255
+ if (list.length === 0) {
256
+ return Buffer.alloc(0);
257
+ }
258
+ if (totalLength === undefined) {
259
+ totalLength = 0;
260
+ for (const buf of list) {
261
+ totalLength += buf.length;
262
+ }
263
+ }
264
+ const result = Buffer.alloc(totalLength);
265
+ let offset = 0;
266
+ for (const buf of list) {
267
+ if (offset + buf.length > totalLength) {
268
+ result.set(buf.subarray(0, totalLength - offset), offset);
269
+ break;
270
+ }
271
+ result.set(buf, offset);
272
+ offset += buf.length;
273
+ }
274
+ return result;
275
+ }
276
+
277
+ static isBuffer(obj) {
278
+ return obj != null && obj[BUFFER_SYMBOL] === true;
279
+ }
280
+
281
+ static byteLength(string, encoding = 'utf8') {
282
+ if (typeof string !== 'string') {
283
+ if (ArrayBuffer.isView(string) || string instanceof ArrayBuffer) {
284
+ return string.byteLength;
285
+ }
286
+ throw new TypeError('First argument must be a string, Buffer, or ArrayBuffer');
287
+ }
288
+ encoding = normalizeEncoding(encoding);
289
+ if (encoding === 'utf8') {
290
+ return new TextEncoder().encode(string).length;
291
+ } else if (encoding === 'base64') {
292
+ const padding = (string.match(/=+$/) || [''])[0].length;
293
+ return Math.floor((string.length * 3) / 4) - padding;
294
+ } else if (encoding === 'hex') {
295
+ return Math.floor(string.length / 2);
296
+ }
297
+ return new TextEncoder().encode(string).length;
298
+ }
299
+
300
+ static isEncoding(encoding) {
301
+ return ['utf8', 'utf-8', 'base64', 'hex'].includes(normalizeEncoding(encoding));
302
+ }
303
+ }
304
+
305
+ function normalizeEncoding(encoding) {
306
+ if (!encoding) return 'utf8';
307
+ const lower = String(encoding).toLowerCase().replace('-', '');
308
+ if (lower === 'utf8' || lower === 'utf-8') return 'utf8';
309
+ if (lower === 'base64') return 'base64';
310
+ if (lower === 'hex') return 'hex';
311
+ return lower;
312
+ }
313
+
314
+ function stringToBytes(str, encoding) {
315
+ encoding = normalizeEncoding(encoding);
316
+ if (encoding === 'utf8') {
317
+ return new TextEncoder().encode(str);
318
+ } else if (encoding === 'base64') {
319
+ return base64ToBytes(str);
320
+ } else if (encoding === 'hex') {
321
+ return hexToBytes(str);
322
+ }
323
+ return new TextEncoder().encode(str);
324
+ }
325
+
326
+ function base64ToBytes(str) {
327
+ const binary = atob(str);
328
+ const bytes = new Uint8Array(binary.length);
329
+ for (let i = 0; i < binary.length; i++) {
330
+ bytes[i] = binary.charCodeAt(i);
331
+ }
332
+ return bytes;
333
+ }
334
+
335
+ function bytesToBase64(bytes) {
336
+ let binary = '';
337
+ for (let i = 0; i < bytes.length; i++) {
338
+ binary += String.fromCharCode(bytes[i]);
339
+ }
340
+ return btoa(binary);
341
+ }
342
+
343
+ function hexToBytes(str) {
344
+ if (str.length % 2 !== 0) {
345
+ throw new TypeError('Invalid hex string');
346
+ }
347
+ const bytes = new Uint8Array(str.length / 2);
348
+ for (let i = 0; i < str.length; i += 2) {
349
+ const byte = parseInt(str.substr(i, 2), 16);
350
+ if (isNaN(byte)) {
351
+ throw new TypeError('Invalid hex string');
352
+ }
353
+ bytes[i / 2] = byte;
354
+ }
355
+ return bytes;
356
+ }
357
+
358
+ function bytesToHex(bytes) {
359
+ let hex = '';
360
+ for (let i = 0; i < bytes.length; i++) {
361
+ hex += bytes[i].toString(16).padStart(2, '0');
362
+ }
363
+ return hex;
364
+ }
365
+
366
+ globalThis.Buffer = Buffer;
146
367
  })();
147
368
  `;
148
369
  async function setupEncoding(context) {
@@ -155,4 +376,4 @@ export {
155
376
  setupEncoding
156
377
  };
157
378
 
158
- //# debugId=2247BF7C4E326F2B64756E2164756E21
379
+ //# debugId=EF7519E527710EEC64756E2164756E21
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../../src/index.ts"],
4
4
  "sourcesContent": [
5
- "import type ivm from \"isolated-vm\";\n\nexport interface EncodingHandle {\n dispose(): void;\n}\n\nconst encodingCode = `\n(function() {\n // Define DOMException if not available\n if (typeof DOMException === 'undefined') {\n globalThis.DOMException = class DOMException extends Error {\n constructor(message, name) {\n super(message);\n this.name = name || 'DOMException';\n }\n };\n }\n\n const base64Chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';\n\n // Build reverse lookup table\n const base64Lookup = new Map();\n for (let i = 0; i < base64Chars.length; i++) {\n base64Lookup.set(base64Chars[i], i);\n }\n\n globalThis.btoa = function btoa(str) {\n if (str === undefined) {\n throw new TypeError(\"1 argument required, but only 0 present.\");\n }\n\n str = String(str);\n\n // Check for characters outside Latin-1 range\n for (let i = 0; i < str.length; i++) {\n if (str.charCodeAt(i) > 255) {\n throw new DOMException(\n \"The string to be encoded contains characters outside of the Latin1 range.\",\n \"InvalidCharacterError\"\n );\n }\n }\n\n if (str.length === 0) {\n return '';\n }\n\n let result = '';\n let i = 0;\n\n while (i < str.length) {\n const a = str.charCodeAt(i++);\n const bExists = i < str.length;\n const b = bExists ? str.charCodeAt(i++) : 0;\n const cExists = i < str.length;\n const c = cExists ? str.charCodeAt(i++) : 0;\n\n const triplet = (a << 16) | (b << 8) | c;\n\n result += base64Chars[(triplet >> 18) & 0x3F];\n result += base64Chars[(triplet >> 12) & 0x3F];\n result += bExists ? base64Chars[(triplet >> 6) & 0x3F] : '=';\n result += cExists ? base64Chars[triplet & 0x3F] : '=';\n }\n\n return result;\n };\n\n globalThis.atob = function atob(str) {\n if (str === undefined) {\n throw new TypeError(\"1 argument required, but only 0 present.\");\n }\n\n str = String(str);\n\n // Remove whitespace\n str = str.replace(/[\\\\t\\\\n\\\\f\\\\r ]/g, '');\n\n // Validate characters and length\n if (str.length === 0) {\n return '';\n }\n\n // Check for invalid characters (before padding normalization)\n for (let i = 0; i < str.length; i++) {\n const c = str[i];\n if (c !== '=' && !base64Lookup.has(c)) {\n throw new DOMException(\n \"The string to be decoded is not correctly encoded.\",\n \"InvalidCharacterError\"\n );\n }\n }\n\n // Validate padding position (must be at end)\n const paddingIndex = str.indexOf('=');\n if (paddingIndex !== -1) {\n for (let i = paddingIndex; i < str.length; i++) {\n if (str[i] !== '=') {\n throw new DOMException(\n \"The string to be decoded is not correctly encoded.\",\n \"InvalidCharacterError\"\n );\n }\n }\n const paddingLength = str.length - paddingIndex;\n if (paddingLength > 2) {\n throw new DOMException(\n \"The string to be decoded is not correctly encoded.\",\n \"InvalidCharacterError\"\n );\n }\n }\n\n // Length without padding must be valid (can't have remainder of 1)\n const strWithoutPadding = str.replace(/=/g, '');\n if (strWithoutPadding.length % 4 === 1) {\n throw new DOMException(\n \"The string to be decoded is not correctly encoded.\",\n \"InvalidCharacterError\"\n );\n }\n\n // Pad to multiple of 4 if needed (for inputs without explicit padding)\n while (str.length % 4 !== 0) {\n str += '=';\n }\n\n let result = '';\n let i = 0;\n\n while (i < str.length) {\n const a = base64Lookup.get(str[i++]) ?? 0;\n const b = base64Lookup.get(str[i++]) ?? 0;\n const c = base64Lookup.get(str[i++]) ?? 0;\n const d = base64Lookup.get(str[i++]) ?? 0;\n\n const triplet = (a << 18) | (b << 12) | (c << 6) | d;\n\n result += String.fromCharCode((triplet >> 16) & 0xFF);\n if (str[i - 2] !== '=') {\n result += String.fromCharCode((triplet >> 8) & 0xFF);\n }\n if (str[i - 1] !== '=') {\n result += String.fromCharCode(triplet & 0xFF);\n }\n }\n\n return result;\n };\n})();\n`;\n\n/**\n * Setup encoding APIs in an isolated-vm context\n *\n * Injects atob and btoa for Base64 encoding/decoding\n *\n * @example\n * const handle = await setupEncoding(context);\n * await context.eval(`\n * const encoded = btoa(\"hello\");\n * const decoded = atob(encoded);\n * `);\n */\nexport async function setupEncoding(\n context: ivm.Context\n): Promise<EncodingHandle> {\n context.evalSync(encodingCode);\n return {\n dispose() {\n // No resources to cleanup for pure JS injection\n },\n };\n}\n"
5
+ "import type ivm from \"isolated-vm\";\n\nexport interface EncodingHandle {\n dispose(): void;\n}\n\nconst encodingCode = `\n(function() {\n // Define DOMException if not available\n if (typeof DOMException === 'undefined') {\n globalThis.DOMException = class DOMException extends Error {\n constructor(message, name) {\n super(message);\n this.name = name || 'DOMException';\n }\n };\n }\n\n const base64Chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';\n\n // Build reverse lookup table\n const base64Lookup = new Map();\n for (let i = 0; i < base64Chars.length; i++) {\n base64Lookup.set(base64Chars[i], i);\n }\n\n globalThis.btoa = function btoa(str) {\n if (str === undefined) {\n throw new TypeError(\"1 argument required, but only 0 present.\");\n }\n\n str = String(str);\n\n // Check for characters outside Latin-1 range\n for (let i = 0; i < str.length; i++) {\n if (str.charCodeAt(i) > 255) {\n throw new DOMException(\n \"The string to be encoded contains characters outside of the Latin1 range.\",\n \"InvalidCharacterError\"\n );\n }\n }\n\n if (str.length === 0) {\n return '';\n }\n\n let result = '';\n let i = 0;\n\n while (i < str.length) {\n const a = str.charCodeAt(i++);\n const bExists = i < str.length;\n const b = bExists ? str.charCodeAt(i++) : 0;\n const cExists = i < str.length;\n const c = cExists ? str.charCodeAt(i++) : 0;\n\n const triplet = (a << 16) | (b << 8) | c;\n\n result += base64Chars[(triplet >> 18) & 0x3F];\n result += base64Chars[(triplet >> 12) & 0x3F];\n result += bExists ? base64Chars[(triplet >> 6) & 0x3F] : '=';\n result += cExists ? base64Chars[triplet & 0x3F] : '=';\n }\n\n return result;\n };\n\n globalThis.atob = function atob(str) {\n if (str === undefined) {\n throw new TypeError(\"1 argument required, but only 0 present.\");\n }\n\n str = String(str);\n\n // Remove whitespace\n str = str.replace(/[\\\\t\\\\n\\\\f\\\\r ]/g, '');\n\n // Validate characters and length\n if (str.length === 0) {\n return '';\n }\n\n // Check for invalid characters (before padding normalization)\n for (let i = 0; i < str.length; i++) {\n const c = str[i];\n if (c !== '=' && !base64Lookup.has(c)) {\n throw new DOMException(\n \"The string to be decoded is not correctly encoded.\",\n \"InvalidCharacterError\"\n );\n }\n }\n\n // Validate padding position (must be at end)\n const paddingIndex = str.indexOf('=');\n if (paddingIndex !== -1) {\n for (let i = paddingIndex; i < str.length; i++) {\n if (str[i] !== '=') {\n throw new DOMException(\n \"The string to be decoded is not correctly encoded.\",\n \"InvalidCharacterError\"\n );\n }\n }\n const paddingLength = str.length - paddingIndex;\n if (paddingLength > 2) {\n throw new DOMException(\n \"The string to be decoded is not correctly encoded.\",\n \"InvalidCharacterError\"\n );\n }\n }\n\n // Length without padding must be valid (can't have remainder of 1)\n const strWithoutPadding = str.replace(/=/g, '');\n if (strWithoutPadding.length % 4 === 1) {\n throw new DOMException(\n \"The string to be decoded is not correctly encoded.\",\n \"InvalidCharacterError\"\n );\n }\n\n // Pad to multiple of 4 if needed (for inputs without explicit padding)\n while (str.length % 4 !== 0) {\n str += '=';\n }\n\n let result = '';\n let i = 0;\n\n while (i < str.length) {\n const a = base64Lookup.get(str[i++]) ?? 0;\n const b = base64Lookup.get(str[i++]) ?? 0;\n const c = base64Lookup.get(str[i++]) ?? 0;\n const d = base64Lookup.get(str[i++]) ?? 0;\n\n const triplet = (a << 18) | (b << 12) | (c << 6) | d;\n\n result += String.fromCharCode((triplet >> 16) & 0xFF);\n if (str[i - 2] !== '=') {\n result += String.fromCharCode((triplet >> 8) & 0xFF);\n }\n if (str[i - 1] !== '=') {\n result += String.fromCharCode(triplet & 0xFF);\n }\n }\n\n return result;\n };\n\n // ============================================\n // Buffer implementation\n // ============================================\n\n const BUFFER_SYMBOL = Symbol.for('__isBuffer');\n\n class Buffer extends Uint8Array {\n constructor(arg, encodingOrOffset, length) {\n if (typeof arg === 'number') {\n super(arg);\n } else if (typeof arg === 'string') {\n const bytes = stringToBytes(arg, encodingOrOffset || 'utf8');\n super(bytes);\n } else if (arg instanceof ArrayBuffer) {\n if (typeof encodingOrOffset === 'number') {\n super(arg, encodingOrOffset, length);\n } else {\n super(arg);\n }\n } else if (ArrayBuffer.isView(arg)) {\n super(arg.buffer, arg.byteOffset, arg.byteLength);\n } else if (Array.isArray(arg)) {\n super(arg);\n } else {\n super(arg);\n }\n Object.defineProperty(this, BUFFER_SYMBOL, { value: true, writable: false });\n }\n\n toString(encoding = 'utf8') {\n encoding = normalizeEncoding(encoding);\n if (encoding === 'utf8') {\n return new TextDecoder('utf-8').decode(this);\n } else if (encoding === 'base64') {\n return bytesToBase64(this);\n } else if (encoding === 'hex') {\n return bytesToHex(this);\n }\n return new TextDecoder('utf-8').decode(this);\n }\n\n slice(start, end) {\n const sliced = super.slice(start, end);\n return Buffer.from(sliced);\n }\n\n subarray(start, end) {\n const sub = super.subarray(start, end);\n const buf = new Buffer(sub.length);\n buf.set(sub);\n return buf;\n }\n\n static from(value, encodingOrOffset, length) {\n if (typeof value === 'string') {\n return new Buffer(value, encodingOrOffset);\n }\n if (value instanceof ArrayBuffer) {\n if (typeof encodingOrOffset === 'number') {\n return new Buffer(value, encodingOrOffset, length);\n }\n return new Buffer(value);\n }\n if (ArrayBuffer.isView(value)) {\n return new Buffer(value);\n }\n if (Array.isArray(value)) {\n return new Buffer(value);\n }\n if (value && typeof value[Symbol.iterator] === 'function') {\n return new Buffer(Array.from(value));\n }\n throw new TypeError('First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object');\n }\n\n static alloc(size, fill, encoding) {\n if (typeof size !== 'number' || size < 0) {\n throw new RangeError('Invalid size');\n }\n const buf = new Buffer(size);\n if (fill !== undefined) {\n if (typeof fill === 'number') {\n buf.fill(fill);\n } else if (typeof fill === 'string') {\n const fillBytes = stringToBytes(fill, encoding || 'utf8');\n for (let i = 0; i < size; i++) {\n buf[i] = fillBytes[i % fillBytes.length];\n }\n } else if (Buffer.isBuffer(fill) || fill instanceof Uint8Array) {\n for (let i = 0; i < size; i++) {\n buf[i] = fill[i % fill.length];\n }\n }\n }\n return buf;\n }\n\n static allocUnsafe(size) {\n if (typeof size !== 'number' || size < 0) {\n throw new RangeError('Invalid size');\n }\n return new Buffer(size);\n }\n\n static concat(list, totalLength) {\n if (!Array.isArray(list)) {\n throw new TypeError('list argument must be an array');\n }\n if (list.length === 0) {\n return Buffer.alloc(0);\n }\n if (totalLength === undefined) {\n totalLength = 0;\n for (const buf of list) {\n totalLength += buf.length;\n }\n }\n const result = Buffer.alloc(totalLength);\n let offset = 0;\n for (const buf of list) {\n if (offset + buf.length > totalLength) {\n result.set(buf.subarray(0, totalLength - offset), offset);\n break;\n }\n result.set(buf, offset);\n offset += buf.length;\n }\n return result;\n }\n\n static isBuffer(obj) {\n return obj != null && obj[BUFFER_SYMBOL] === true;\n }\n\n static byteLength(string, encoding = 'utf8') {\n if (typeof string !== 'string') {\n if (ArrayBuffer.isView(string) || string instanceof ArrayBuffer) {\n return string.byteLength;\n }\n throw new TypeError('First argument must be a string, Buffer, or ArrayBuffer');\n }\n encoding = normalizeEncoding(encoding);\n if (encoding === 'utf8') {\n return new TextEncoder().encode(string).length;\n } else if (encoding === 'base64') {\n const padding = (string.match(/=+$/) || [''])[0].length;\n return Math.floor((string.length * 3) / 4) - padding;\n } else if (encoding === 'hex') {\n return Math.floor(string.length / 2);\n }\n return new TextEncoder().encode(string).length;\n }\n\n static isEncoding(encoding) {\n return ['utf8', 'utf-8', 'base64', 'hex'].includes(normalizeEncoding(encoding));\n }\n }\n\n function normalizeEncoding(encoding) {\n if (!encoding) return 'utf8';\n const lower = String(encoding).toLowerCase().replace('-', '');\n if (lower === 'utf8' || lower === 'utf-8') return 'utf8';\n if (lower === 'base64') return 'base64';\n if (lower === 'hex') return 'hex';\n return lower;\n }\n\n function stringToBytes(str, encoding) {\n encoding = normalizeEncoding(encoding);\n if (encoding === 'utf8') {\n return new TextEncoder().encode(str);\n } else if (encoding === 'base64') {\n return base64ToBytes(str);\n } else if (encoding === 'hex') {\n return hexToBytes(str);\n }\n return new TextEncoder().encode(str);\n }\n\n function base64ToBytes(str) {\n const binary = atob(str);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return bytes;\n }\n\n function bytesToBase64(bytes) {\n let binary = '';\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary);\n }\n\n function hexToBytes(str) {\n if (str.length % 2 !== 0) {\n throw new TypeError('Invalid hex string');\n }\n const bytes = new Uint8Array(str.length / 2);\n for (let i = 0; i < str.length; i += 2) {\n const byte = parseInt(str.substr(i, 2), 16);\n if (isNaN(byte)) {\n throw new TypeError('Invalid hex string');\n }\n bytes[i / 2] = byte;\n }\n return bytes;\n }\n\n function bytesToHex(bytes) {\n let hex = '';\n for (let i = 0; i < bytes.length; i++) {\n hex += bytes[i].toString(16).padStart(2, '0');\n }\n return hex;\n }\n\n globalThis.Buffer = Buffer;\n})();\n`;\n\n/**\n * Setup encoding APIs in an isolated-vm context\n *\n * Injects:\n * - atob/btoa for Base64 encoding/decoding\n * - Buffer class for binary data handling (utf8, base64, hex)\n *\n * @example\n * const handle = await setupEncoding(context);\n * await context.eval(`\n * const encoded = btoa(\"hello\");\n * const decoded = atob(encoded);\n * const buf = Buffer.from(\"hello\");\n * const hex = buf.toString(\"hex\");\n * `);\n */\nexport async function setupEncoding(\n context: ivm.Context\n): Promise<EncodingHandle> {\n context.evalSync(encodingCode);\n return {\n dispose() {\n // No resources to cleanup for pure JS injection\n },\n };\n}\n"
6
6
  ],
7
- "mappings": ";AAMA,IAAM,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA+JrB,eAAsB,aAAa,CACjC,SACyB;AAAA,EACzB,QAAQ,SAAS,YAAY;AAAA,EAC7B,OAAO;AAAA,IACL,OAAO,GAAG;AAAA,EAGZ;AAAA;",
8
- "debugId": "2247BF7C4E326F2B64756E2164756E21",
7
+ "mappings": ";AAMA,IAAM,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAgYrB,eAAsB,aAAa,CACjC,SACyB;AAAA,EACzB,QAAQ,SAAS,YAAY;AAAA,EAC7B,OAAO;AAAA,IACL,OAAO,GAAG;AAAA,EAGZ;AAAA;",
8
+ "debugId": "EF7519E527710EEC64756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/isolate-encoding",
3
- "version": "0.1.11",
3
+ "version": "0.1.12",
4
4
  "type": "module"
5
5
  }
@@ -5,13 +5,17 @@ export interface EncodingHandle {
5
5
  /**
6
6
  * Setup encoding APIs in an isolated-vm context
7
7
  *
8
- * Injects atob and btoa for Base64 encoding/decoding
8
+ * Injects:
9
+ * - atob/btoa for Base64 encoding/decoding
10
+ * - Buffer class for binary data handling (utf8, base64, hex)
9
11
  *
10
12
  * @example
11
13
  * const handle = await setupEncoding(context);
12
14
  * await context.eval(`
13
15
  * const encoded = btoa("hello");
14
16
  * const decoded = atob(encoded);
17
+ * const buf = Buffer.from("hello");
18
+ * const hex = buf.toString("hex");
15
19
  * `);
16
20
  */
17
21
  export declare function setupEncoding(context: ivm.Context): Promise<EncodingHandle>;
@@ -31,4 +31,162 @@ declare global {
31
31
  * btoa("Hello"); // "SGVsbG8="
32
32
  */
33
33
  function btoa(stringToEncode: string): string;
34
+
35
+ // ============================================
36
+ // Buffer
37
+ // ============================================
38
+
39
+ /**
40
+ * Buffer encoding types supported by the isolate Buffer implementation.
41
+ */
42
+ type BufferEncoding = "utf8" | "utf-8" | "base64" | "hex";
43
+
44
+ /**
45
+ * Buffer class for working with binary data.
46
+ * Extends Uint8Array for compatibility.
47
+ * @see https://nodejs.org/api/buffer.html
48
+ */
49
+ interface Buffer extends Uint8Array {
50
+ /**
51
+ * Convert the buffer to a string.
52
+ *
53
+ * @param encoding - The encoding to use (default: "utf8")
54
+ * @returns The string representation
55
+ *
56
+ * @example
57
+ * const buf = Buffer.from("hello");
58
+ * buf.toString(); // "hello"
59
+ * buf.toString("hex"); // "68656c6c6f"
60
+ * buf.toString("base64"); // "aGVsbG8="
61
+ */
62
+ toString(encoding?: BufferEncoding): string;
63
+
64
+ /**
65
+ * Returns a new Buffer that references the same memory as the original,
66
+ * but offset and cropped by the start and end indices.
67
+ *
68
+ * @param start - Start index (default: 0)
69
+ * @param end - End index (default: buffer.length)
70
+ * @returns A new Buffer instance
71
+ *
72
+ * @example
73
+ * const buf = Buffer.from("hello");
74
+ * buf.slice(1, 4).toString(); // "ell"
75
+ */
76
+ slice(start?: number, end?: number): Buffer;
77
+
78
+ /**
79
+ * Returns a new Buffer that references the same memory as the original,
80
+ * but offset and cropped by the start and end indices.
81
+ *
82
+ * @param start - Start index (default: 0)
83
+ * @param end - End index (default: buffer.length)
84
+ * @returns A new Buffer instance
85
+ */
86
+ subarray(start?: number, end?: number): Buffer;
87
+ }
88
+
89
+ /**
90
+ * Buffer constructor interface.
91
+ */
92
+ interface BufferConstructor {
93
+ /**
94
+ * Creates a new Buffer from a string, array, ArrayBuffer, or another Buffer.
95
+ *
96
+ * @param value - The value to create a buffer from
97
+ * @param encodingOrOffset - Encoding for strings, or byte offset for ArrayBuffer
98
+ * @param length - Length for ArrayBuffer (when offset is provided)
99
+ * @returns A new Buffer instance
100
+ *
101
+ * @example
102
+ * Buffer.from("hello"); // UTF-8 encoded
103
+ * Buffer.from("aGVsbG8=", "base64"); // base64 decoded
104
+ * Buffer.from("68656c6c6f", "hex"); // hex decoded
105
+ * Buffer.from([104, 101, 108, 108, 111]); // from array
106
+ */
107
+ from(value: string, encoding?: BufferEncoding): Buffer;
108
+ from(value: ArrayBuffer, byteOffset?: number, length?: number): Buffer;
109
+ from(value: Uint8Array | ReadonlyArray<number>): Buffer;
110
+ from(value: Iterable<number>): Buffer;
111
+
112
+ /**
113
+ * Allocates a new Buffer of the specified size, filled with zeros or the specified fill value.
114
+ *
115
+ * @param size - The size of the buffer in bytes
116
+ * @param fill - Value to fill the buffer with (default: 0)
117
+ * @param encoding - Encoding for string fill values
118
+ * @returns A new Buffer instance
119
+ *
120
+ * @example
121
+ * Buffer.alloc(5); // <Buffer 00 00 00 00 00>
122
+ * Buffer.alloc(5, 1); // <Buffer 01 01 01 01 01>
123
+ * Buffer.alloc(5, "ab"); // <Buffer 61 62 61 62 61>
124
+ */
125
+ alloc(size: number, fill?: number | string | Buffer, encoding?: BufferEncoding): Buffer;
126
+
127
+ /**
128
+ * Allocates a new Buffer of the specified size without initializing the memory.
129
+ * The contents are unknown and may contain sensitive data.
130
+ *
131
+ * @param size - The size of the buffer in bytes
132
+ * @returns A new Buffer instance
133
+ */
134
+ allocUnsafe(size: number): Buffer;
135
+
136
+ /**
137
+ * Concatenates a list of Buffers.
138
+ *
139
+ * @param list - Array of Buffer instances to concatenate
140
+ * @param totalLength - Total length of the buffers (optional)
141
+ * @returns A new Buffer instance
142
+ *
143
+ * @example
144
+ * const buf1 = Buffer.from("hel");
145
+ * const buf2 = Buffer.from("lo");
146
+ * Buffer.concat([buf1, buf2]).toString(); // "hello"
147
+ */
148
+ concat(list: ReadonlyArray<Uint8Array>, totalLength?: number): Buffer;
149
+
150
+ /**
151
+ * Returns true if the given object is a Buffer.
152
+ *
153
+ * @param obj - Object to test
154
+ * @returns true if obj is a Buffer
155
+ *
156
+ * @example
157
+ * Buffer.isBuffer(Buffer.from("test")); // true
158
+ * Buffer.isBuffer(new Uint8Array(5)); // false
159
+ */
160
+ isBuffer(obj: unknown): obj is Buffer;
161
+
162
+ /**
163
+ * Returns the byte length of a string when encoded.
164
+ *
165
+ * @param string - The string to measure
166
+ * @param encoding - The encoding (default: "utf8")
167
+ * @returns The byte length
168
+ *
169
+ * @example
170
+ * Buffer.byteLength("hello"); // 5
171
+ * Buffer.byteLength("aGVsbG8=", "base64"); // 5 (decoded length)
172
+ */
173
+ byteLength(string: string, encoding?: BufferEncoding): number;
174
+ byteLength(buffer: ArrayBufferView | ArrayBuffer): number;
175
+
176
+ /**
177
+ * Returns true if the encoding is a valid buffer encoding.
178
+ *
179
+ * @param encoding - The encoding to check
180
+ * @returns true if the encoding is supported
181
+ */
182
+ isEncoding(encoding: string): encoding is BufferEncoding;
183
+
184
+ readonly prototype: Buffer;
185
+ }
186
+
187
+ /**
188
+ * Buffer class for working with binary data.
189
+ * @see https://nodejs.org/api/buffer.html
190
+ */
191
+ const Buffer: BufferConstructor;
34
192
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ricsam/isolate-encoding",
3
- "version": "0.1.11",
3
+ "version": "0.1.12",
4
4
  "main": "./dist/cjs/index.cjs",
5
5
  "types": "./dist/types/index.d.ts",
6
6
  "exports": {