'use strict'; require('../common'); const assert = require('assert'); const prefix = "Failed to execute 'structuredClone'"; const key = 'transfer'; const context = 'Options'; const memberConverterError = `${prefix}: ${key} in ${context} can not be converted to sequence.`; const dictionaryConverterError = `${prefix}: ${context} cannot be converted to a dictionary`; assert.throws(() => structuredClone(), { code: 'ERR_MISSING_ARGS' }); assert.throws(() => structuredClone(undefined, ''), { code: 'ERR_INVALID_ARG_TYPE', message: dictionaryConverterError }); assert.throws(() => structuredClone(undefined, 1), { code: 'ERR_INVALID_ARG_TYPE', message: dictionaryConverterError }); assert.throws(() => structuredClone(undefined, { transfer: 1 }), { code: 'ERR_INVALID_ARG_TYPE', message: memberConverterError }); assert.throws(() => structuredClone(undefined, { transfer: '' }), { code: 'ERR_INVALID_ARG_TYPE', message: memberConverterError }); assert.throws(() => structuredClone(undefined, { transfer: null }), { code: 'ERR_INVALID_ARG_TYPE', message: memberConverterError }); // Options can be null or undefined. assert.strictEqual(structuredClone(undefined), undefined); assert.strictEqual(structuredClone(undefined, null), undefined); // Transfer can be null or undefined. assert.strictEqual(structuredClone(undefined, { }), undefined); // Transferables or its subclasses should be received with its closest transferable superclass for (const StreamClass of [ReadableStream, WritableStream, TransformStream]) { const original = new StreamClass(); const transfer = structuredClone(original, { transfer: [original] }); assert.strictEqual(Object.getPrototypeOf(transfer), StreamClass.prototype); assert.ok(transfer instanceof StreamClass); const extended = class extends StreamClass {}; const extendedOriginal = new extended(); const extendedTransfer = structuredClone(extendedOriginal, { transfer: [extendedOriginal] }); assert.strictEqual(Object.getPrototypeOf(extendedTransfer), StreamClass.prototype); assert.ok(extendedTransfer instanceof StreamClass); } for (const Transferrable of [File, Blob]) { const a2 = Transferrable === File ? '' : {}; const original = new Transferrable([], a2); const transfer = structuredClone(original); assert.strictEqual(Object.getPrototypeOf(transfer), Transferrable.prototype); assert.ok(transfer instanceof Transferrable); const extendedOriginal = new (class extends Transferrable {})([], a2); const extendedTransfer = structuredClone(extendedOriginal); assert.strictEqual(Object.getPrototypeOf(extendedTransfer), Transferrable.prototype); assert.ok(extendedTransfer instanceof Transferrable); } // Transfer can be iterable { const value = { a: new ReadableStream(), b: new WritableStream(), }; const cloned = structuredClone(value, { transfer: { *[Symbol.iterator]() { for (const key in value) { yield value[key]; } } } }); for (const key in value) { assert.ok(value[key].locked); assert.ok(!cloned[key].locked); } } { // See: https://github.com/nodejs/node/issues/49940 const cloned = structuredClone({}, { transfer: { *[Symbol.iterator]() {} } }); assert.deepStrictEqual(cloned, {}); } const blob = new Blob(); assert.throws(() => structuredClone(blob, { transfer: [blob] }), { name: 'DataCloneError' });