Composite Codecs
Description
Section titled “Description”This example demonstrates how to use composite codec types for encoding/decoding arrays, maps, and records in wire format (JSON or MessagePack). Topics covered:
- ArrayCodec for encoding/decoding typed arrays
- Pre-built array codecs: numberArrayCodec, stringArrayCodec, bigIntArrayCodec, booleanArrayCodec, bytesArrayCodec, addressArrayCodec
- MapCodec for encoding/decoding Map<K, V> types
- RecordCodec for encoding/decoding Record<string, V> types
- Creating custom ArrayCodec with a specific element codec
- Encoding nested structures (array of arrays, map of arrays)
- Round-trip verification for each composite codec
Prerequisites
Section titled “Prerequisites”- No LocalNet required
Run This Example
Section titled “Run This Example”From the repository root:
cd examplesnpm run example common/10-composite-codecs.ts/** * Example: Composite Codecs * * This example demonstrates how to use composite codec types for encoding/decoding * arrays, maps, and records in wire format (JSON or MessagePack). * * Topics covered: * - ArrayCodec for encoding/decoding typed arrays * - Pre-built array codecs: numberArrayCodec, stringArrayCodec, bigIntArrayCodec, * booleanArrayCodec, bytesArrayCodec, addressArrayCodec * - MapCodec for encoding/decoding Map<K, V> types * - RecordCodec for encoding/decoding Record<string, V> types * - Creating custom ArrayCodec with a specific element codec * - Encoding nested structures (array of arrays, map of arrays) * - Round-trip verification for each composite codec * * Prerequisites: * - No LocalNet required */
import type { EncodingFormat } from '@algorandfoundation/algokit-utils/common';import { // Utilities Address, // Composite codecs ArrayCodec, MapCodec, RecordCodec, addressArrayCodec, addressCodec, arrayEqual, bigIntArrayCodec, bigIntCodec, booleanArrayCodec, bytesArrayCodec, bytesCodec, // Pre-built array codecs numberArrayCodec, // Primitive codecs (for creating custom composites) numberCodec, stringArrayCodec, stringCodec,} from '@algorandfoundation/algokit-utils/common';import { formatHex, printHeader, printInfo, printStep, printSuccess } from '../shared/utils.js';
// ============================================================================// Main Example// ============================================================================
printHeader('Composite Codecs Example');
// ============================================================================// Step 1: Introduction to Composite Codecs// ============================================================================printStep(1, 'Introduction to Composite Codecs');
printInfo('Composite codecs build on primitive codecs to handle complex data structures.');printInfo('');printInfo('Three composite codec types:');printInfo(' ArrayCodec<T> - Encodes/decodes arrays of type T[]');printInfo(' MapCodec<K, V> - Encodes/decodes Map<K, V>');printInfo(' RecordCodec<V> - Encodes/decodes Record<string, V>');printInfo('');printInfo('Each uses an underlying codec for element encoding/decoding.');printInfo('');printSuccess('Composite codecs extend primitive codecs for collections');
// ============================================================================// Step 2: ArrayCodec - Typed Array Encoding/Decoding// ============================================================================printStep(2, 'ArrayCodec - Typed Array Encoding/Decoding');
printInfo('ArrayCodec wraps an element codec to encode/decode arrays.');printInfo('Creating an ArrayCodec: new ArrayCodec(elementCodec)');printInfo('');
// Create a custom ArrayCodec with numberCodecconst customNumberArray = new ArrayCodec(numberCodec);const numbers = [1, 2, 3, 4, 5];
printInfo(`Original array: [${numbers.join(', ')}]`);
const encodedJson = customNumberArray.encode(numbers, 'json');const encodedMsgpack = customNumberArray.encode(numbers, 'msgpack');const decodedJson = customNumberArray.decode(encodedJson, 'json');const decodedMsgpack = customNumberArray.decode(encodedMsgpack, 'msgpack');
printInfo(`JSON: encoded=[${encodedJson.join(', ')}], decoded=[${decodedJson.join(', ')}]`);printInfo( `msgpack: encoded=[${encodedMsgpack.join(', ')}], decoded=[${decodedMsgpack.join(', ')}]`,);printInfo('');
// Default valueprintInfo( `ArrayCodec default value: [${customNumberArray.defaultValue().join(', ')}] (empty array)`,);printInfo('');printSuccess('ArrayCodec encodes each element using the underlying codec');
// ============================================================================// Step 3: Pre-built Array Codecs// ============================================================================printStep(3, 'Pre-built Array Codecs');
printInfo('algokit-utils provides pre-built array codecs for common types:');printInfo('');
// numberArrayCodecprintInfo('numberArrayCodec - for number arrays:');const numberArray = [10, 20, 30, 40];const numEncoded = numberArrayCodec.encode(numberArray, 'json');const numDecoded = numberArrayCodec.decode(numEncoded, 'json');printInfo(` Original: [${numberArray.join(', ')}]`);printInfo(` Decoded: [${numDecoded.join(', ')}]`);printInfo(` Match: ${numberArray.every((n, i) => n === numDecoded[i])}`);printInfo('');
// stringArrayCodecprintInfo('stringArrayCodec - for string arrays:');const stringArray = ['Alice', 'Bob', 'Charlie'];const strEncoded = stringArrayCodec.encode(stringArray, 'json');const strDecoded = stringArrayCodec.decode(strEncoded, 'json');printInfo(` Original: ["${stringArray.join('", "')}"]`);printInfo(` Decoded: ["${strDecoded.join('", "')}"]`);printInfo(` Match: ${stringArray.every((s, i) => s === strDecoded[i])}`);printInfo('');
// bigIntArrayCodecprintInfo('bigIntArrayCodec - for BigInt arrays:');const bigIntArray = [100n, 9007199254740993n, 18446744073709551615n];const bigEncoded = bigIntArrayCodec.encode(bigIntArray, 'msgpack');const bigDecoded = bigIntArrayCodec.decode(bigEncoded, 'msgpack');printInfo(` Original: [${bigIntArray.map(b => `${b}n`).join(', ')}]`);printInfo(` Decoded: [${bigDecoded.map((b: bigint) => `${b}n`).join(', ')}]`);printInfo(` Match: ${bigIntArray.every((b, i) => b === bigDecoded[i])}`);printInfo('');
// booleanArrayCodecprintInfo('booleanArrayCodec - for boolean arrays:');const boolArray = [true, false, true, false];const boolEncoded = booleanArrayCodec.encode(boolArray, 'json');const boolDecoded = booleanArrayCodec.decode(boolEncoded, 'json');printInfo(` Original: [${boolArray.join(', ')}]`);printInfo(` Decoded: [${boolDecoded.join(', ')}]`);printInfo(` Match: ${boolArray.every((b, i) => b === boolDecoded[i])}`);printInfo('');
// bytesArrayCodecprintInfo('bytesArrayCodec - for Uint8Array arrays:');const bytesArray = [ new Uint8Array([1, 2, 3]), new Uint8Array([4, 5, 6]), new Uint8Array([7, 8, 9]),];const bytesEncoded = bytesArrayCodec.encode(bytesArray, 'json');const bytesDecoded = bytesArrayCodec.decode(bytesEncoded, 'json');printInfo(` Original: [${bytesArray.map(formatHex).join(', ')}]`);printInfo(` Decoded: [${bytesDecoded.map(formatHex).join(', ')}]`);printInfo(` Match: ${bytesArray.every((b, i) => arrayEqual(b, bytesDecoded[i]))}`);printInfo('');
// addressArrayCodecprintInfo('addressArrayCodec - for Address arrays:');const addrBytes1 = new Uint8Array(32).fill(1);const addrBytes2 = new Uint8Array(32).fill(2);const addressArray = [new Address(addrBytes1), new Address(addrBytes2)];const addrEncoded = addressArrayCodec.encode(addressArray, 'json');const addrDecoded = addressArrayCodec.decode(addrEncoded, 'json');printInfo(` Original: [${addressArray.map(a => `${a.toString().slice(0, 12)}...`).join(', ')}]`);printInfo( ` Decoded: [${addrDecoded.map((a: Address) => `${a.toString().slice(0, 12)}...`).join(', ')}]`,);printInfo(` Match: ${addressArray.every((a, i) => a.equals(addrDecoded[i]))}`);printInfo('');
printSuccess('Pre-built array codecs available for all primitive types');
// ============================================================================// Step 4: MapCodec - Map<K, V> Encoding/Decoding// ============================================================================printStep(4, 'MapCodec - Map<K, V> Encoding/Decoding');
printInfo('MapCodec handles Maps with various key types.');printInfo('Creating a MapCodec: new MapCodec(keyCodec, valueCodec)');printInfo('');
// Map with string keys and number valuesprintInfo('Map<string, number> example:');const stringNumberMap = new MapCodec(stringCodec, numberCodec);const strNumMap = new Map<string, number>([ ['alice', 100], ['bob', 200], ['charlie', 300],]);
printInfo( ` Original: Map { ${Array.from(strNumMap.entries()) .map(([k, v]) => `"${k}" => ${v}`) .join(', ')} }`,);
const mapEncodedJson = stringNumberMap.encode(strNumMap, 'json');const mapDecodedJson = stringNumberMap.decode(mapEncodedJson, 'json');printInfo(` JSON encoded: ${JSON.stringify(mapEncodedJson)}`);const mapDecodedJsonEntries = [...mapDecodedJson.entries()];printInfo( ` JSON decoded: Map { ${mapDecodedJsonEntries.map(([k, v]) => `"${k}" => ${v}`).join(', ')} }`,);
const mapEncodedMsgpack = stringNumberMap.encode(strNumMap, 'msgpack');const mapDecodedMsgpack = stringNumberMap.decode(mapEncodedMsgpack, 'msgpack');printInfo(` msgpack encoded: Map (preserved as Map)`);const mapDecodedMsgpackEntries = [...mapDecodedMsgpack.entries()];printInfo( ` msgpack decoded: Map { ${mapDecodedMsgpackEntries.map(([k, v]) => `"${k}" => ${v}`).join(', ')} }`,);printInfo('');
// Map with bigint keysprintInfo('Map<bigint, string> example (bigint keys become strings in JSON):');const bigintStringMap = new MapCodec(bigIntCodec, stringCodec);const bigStrMap = new Map<bigint, string>([ [1n, 'one'], [2n, 'two'], [9007199254740993n, 'large'],]);
printInfo( ` Original: Map { ${Array.from(bigStrMap.entries()) .map(([k, v]) => `${k}n => "${v}"`) .join(', ')} }`,);
const bigMapEncodedJson = bigintStringMap.encode(bigStrMap, 'json');const bigMapDecodedJson = bigintStringMap.decode(bigMapEncodedJson, 'json');printInfo(` JSON encoded: ${JSON.stringify(bigMapEncodedJson)} (keys as strings)`);const bigMapDecodedEntries = [...bigMapDecodedJson.entries()];printInfo( ` JSON decoded: Map { ${bigMapDecodedEntries.map(([k, v]) => `${k}n => "${v}"`).join(', ')} }`,);printInfo('');
// Default valueprintInfo(`MapCodec default value: Map(${stringNumberMap.defaultValue().size}) (empty map)`);printInfo('');
printSuccess('MapCodec supports string and bigint keys in JSON, all types in msgpack');
// ============================================================================// Step 5: RecordCodec - Record<string, V> Encoding/Decoding// ============================================================================printStep(5, 'RecordCodec - Record<string, V> Encoding/Decoding');
printInfo('RecordCodec handles string-keyed objects with homogeneous values.');printInfo('Creating a RecordCodec: new RecordCodec(valueCodec)');printInfo('');
// Record with number valuesprintInfo('Record<string, number> example:');const numberRecord = new RecordCodec(numberCodec);const scores: Record<string, number> = { alice: 95, bob: 87, charlie: 92,};
printInfo( ` Original: { ${Object.entries(scores) .map(([k, v]) => `${k}: ${v}`) .join(', ')} }`,);
const recEncodedJson = numberRecord.encode(scores, 'json');const recDecodedJson = numberRecord.decode(recEncodedJson, 'json');printInfo(` JSON encoded: ${JSON.stringify(recEncodedJson)}`);printInfo( ` JSON decoded: { ${Object.entries(recDecodedJson) .map(([k, v]) => `${k}: ${v}`) .join(', ')} }`,);
const recEncodedMsgpack = numberRecord.encode(scores, 'msgpack');const recDecodedMsgpack = numberRecord.decode(recEncodedMsgpack, 'msgpack');printInfo( ` msgpack encoded/decoded: { ${Object.entries(recDecodedMsgpack) .map(([k, v]) => `${k}: ${v}`) .join(', ')} }`,);printInfo('');
// Record with string valuesprintInfo('Record<string, string> example:');const stringRecord = new RecordCodec(stringCodec);const metadata: Record<string, string> = { name: 'My App', version: '1.0.0', author: 'Algorand Developer',};
printInfo( ` Original: { ${Object.entries(metadata) .map(([k, v]) => `${k}: "${v}"`) .join(', ')} }`,);
const metaEncoded = stringRecord.encode(metadata, 'json');const metaDecoded = stringRecord.decode(metaEncoded, 'json');printInfo( ` Decoded: { ${Object.entries(metaDecoded) .map(([k, v]) => `${k}: "${v}"`) .join(', ')} }`,);printInfo('');
// Default valueprintInfo(`RecordCodec default value: {} (empty object)`);printInfo( ` Object.keys(defaultValue()).length = ${Object.keys(numberRecord.defaultValue()).length}`,);printInfo('');
printSuccess('RecordCodec simplifies string-keyed object encoding');
// ============================================================================// Step 6: Custom ArrayCodec with Specific Element Codec// ============================================================================printStep(6, 'Custom ArrayCodec with Specific Element Codec');
printInfo('You can create custom ArrayCodecs for any element type.');printInfo('');
// Custom codec for arrays of addresses (same as addressArrayCodec)printInfo('Creating custom ArrayCodec<Address>:');const customAddressArray = new ArrayCodec(addressCodec);
const testAddresses = [Address.zeroAddress(), new Address(new Uint8Array(32).fill(0xab))];const customAddrEncoded = customAddressArray.encode(testAddresses, 'json');const customAddrDecoded = customAddressArray.decode(customAddrEncoded, 'json');
printInfo(` Original: [${testAddresses.map(a => `${a.toString().slice(0, 12)}...`).join(', ')}]`);printInfo( ` Encoded (JSON): [${(customAddrEncoded as string[]).map(s => `${s.slice(0, 12)}...`).join(', ')}]`,);printInfo( ` Decoded: [${customAddrDecoded.map((a: Address) => `${a.toString().slice(0, 12)}...`).join(', ')}]`,);printInfo('');
// Custom codec for arrays of bytes (same as bytesArrayCodec)printInfo('Creating custom ArrayCodec<Uint8Array>:');const customBytesArray = new ArrayCodec(bytesCodec);
const testBytesArrays = [new Uint8Array([0xde, 0xad]), new Uint8Array([0xbe, 0xef])];const customBytesEncoded = customBytesArray.encode(testBytesArrays, 'json');const customBytesDecoded = customBytesArray.decode(customBytesEncoded, 'json');
printInfo(` Original: [${testBytesArrays.map(formatHex).join(', ')}]`);printInfo(` Encoded (JSON): ["${(customBytesEncoded as string[]).join('", "')}"] (base64)`);printInfo(` Decoded: [${customBytesDecoded.map(formatHex).join(', ')}]`);printInfo('');
printSuccess('Custom ArrayCodecs can wrap any primitive or composite codec');
// ============================================================================// Step 7: Nested Structures - Array of Arrays// ============================================================================printStep(7, 'Nested Structures - Array of Arrays');
printInfo('Composite codecs can be nested for complex structures.');printInfo('');
// Array of number arrays (2D array)printInfo('Array of number arrays (2D matrix):');const numberArrayCodecInstance = new ArrayCodec(numberCodec);const arrayOfNumberArrays = new ArrayCodec(numberArrayCodecInstance);
const matrix = [ [1, 2, 3], [4, 5, 6], [7, 8, 9],];
printInfo(` Original matrix:`);for (const row of matrix) { printInfo(` [${row.join(', ')}]`);}
const matrixEncoded = arrayOfNumberArrays.encode(matrix, 'json');const matrixDecoded = arrayOfNumberArrays.decode(matrixEncoded, 'json');
printInfo(` Decoded matrix:`);for (const row of matrixDecoded) { printInfo(` [${row.join(', ')}]`);}
// Verify matchconst matrixMatch = matrix.every((row, i) => row.every((val, j) => val === matrixDecoded[i][j]));printInfo(` Match: ${matrixMatch}`);printInfo('');
// Array of string arraysprintInfo('Array of string arrays (groups of names):');const stringArrayCodecInstance = new ArrayCodec(stringCodec);const arrayOfStringArrays = new ArrayCodec(stringArrayCodecInstance);
const groups = [ ['Alice', 'Bob'], ['Charlie', 'David', 'Eve'],];
printInfo(` Original: [${groups.map(g => `["${g.join('", "')}"]`).join(', ')}]`);
const groupsEncoded = arrayOfStringArrays.encode(groups, 'json');const groupsDecoded = arrayOfStringArrays.decode(groupsEncoded, 'json');
printInfo( ` Decoded: [${groupsDecoded.map((g: string[]) => `["${g.join('", "')}"]`).join(', ')}]`,);printInfo('');
printSuccess('Nested ArrayCodecs enable multi-dimensional arrays');
// ============================================================================// Step 8: Nested Structures - Map of Arrays// ============================================================================printStep(8, 'Nested Structures - Map of Arrays');
printInfo('MapCodec can use array codecs as value codecs.');printInfo('');
// Map<string, number[]> - users to scoresprintInfo('Map<string, number[]> example (user scores):');const mapOfNumberArrays = new MapCodec(stringCodec, numberArrayCodec);
const userScores = new Map<string, number[]>([ ['alice', [95, 88, 92]], ['bob', [78, 85, 90]],]);
printInfo(` Original:`);for (const [user, scoreList] of userScores) { printInfo(` "${user}" => [${scoreList.join(', ')}]`);}
const userScoresEncodedJson = mapOfNumberArrays.encode(userScores, 'json');const userScoresDecodedJson = mapOfNumberArrays.decode(userScoresEncodedJson, 'json');
printInfo(` JSON encoded: ${JSON.stringify(userScoresEncodedJson)}`);printInfo(` Decoded:`);for (const [user, scoreList] of userScoresDecodedJson) { printInfo(` "${user}" => [${scoreList.join(', ')}]`);}printInfo('');
// Map<string, string[]> - categories to itemsprintInfo('Map<string, string[]> example (category items):');const mapOfStringArrays = new MapCodec(stringCodec, stringArrayCodec);
const categories = new Map<string, string[]>([ ['fruits', ['apple', 'banana', 'cherry']], ['colors', ['red', 'green', 'blue']],]);
printInfo(` Original:`);for (const [cat, items] of categories) { printInfo(` "${cat}" => ["${items.join('", "')}"]`);}
const categoriesEncoded = mapOfStringArrays.encode(categories, 'msgpack');const categoriesDecoded = mapOfStringArrays.decode(categoriesEncoded, 'msgpack');
printInfo(` msgpack Decoded:`);for (const [cat, items] of categoriesDecoded) { printInfo(` "${cat}" => ["${items.join('", "')}"]`);}printInfo('');
printSuccess('Composite codecs can be combined for complex structures');
// ============================================================================// Step 9: Round-Trip Verification for All Composite Codecs// ============================================================================printStep(9, 'Round-Trip Verification for All Composite Codecs');
printInfo('Verifying decode(encode(value)) === value for all composite codecs:');printInfo('');
const roundTrips: Array<{ name: string; value: string; format: EncodingFormat; success: boolean }> = [];
// numberArrayCodecconst rtNumArray = [1, 2, 3, 4, 5];roundTrips.push({ name: 'numberArrayCodec', value: `[${rtNumArray.join(', ')}]`, format: 'json', success: rtNumArray.every( (n, i) => n === numberArrayCodec.decode(numberArrayCodec.encode(rtNumArray, 'json'), 'json')[i], ),});
// stringArrayCodecconst rtStrArray = ['a', 'b', 'c'];roundTrips.push({ name: 'stringArrayCodec', value: `["${rtStrArray.join('", "')}"]`, format: 'json', success: rtStrArray.every( (s, i) => s === stringArrayCodec.decode(stringArrayCodec.encode(rtStrArray, 'json'), 'json')[i], ),});
// bigIntArrayCodecconst rtBigArray = [100n, 200n, 300n];roundTrips.push({ name: 'bigIntArrayCodec', value: `[${rtBigArray.map(b => `${b}n`).join(', ')}]`, format: 'msgpack', success: rtBigArray.every( (b, i) => b === bigIntArrayCodec.decode(bigIntArrayCodec.encode(rtBigArray, 'msgpack'), 'msgpack')[i], ),});
// booleanArrayCodecconst rtBoolArray = [true, false, true];roundTrips.push({ name: 'booleanArrayCodec', value: `[${rtBoolArray.join(', ')}]`, format: 'json', success: rtBoolArray.every( (b, i) => b === booleanArrayCodec.decode(booleanArrayCodec.encode(rtBoolArray, 'json'), 'json')[i], ),});
// bytesArrayCodecconst rtBytesArray = [new Uint8Array([1, 2]), new Uint8Array([3, 4])];const rtBytesDecoded = bytesArrayCodec.decode(bytesArrayCodec.encode(rtBytesArray, 'json'), 'json');roundTrips.push({ name: 'bytesArrayCodec', value: `[${rtBytesArray.map(formatHex).join(', ')}]`, format: 'json', success: rtBytesArray.every((b, i) => arrayEqual(b, rtBytesDecoded[i])),});
// addressArrayCodecconst rtAddrArray = [Address.zeroAddress(), new Address(new Uint8Array(32).fill(0xff))];const rtAddrDecoded = addressArrayCodec.decode( addressArrayCodec.encode(rtAddrArray, 'json'), 'json',);roundTrips.push({ name: 'addressArrayCodec', value: `[${rtAddrArray.map(a => `${a.toString().slice(0, 8)}...`).join(', ')}]`, format: 'json', success: rtAddrArray.every((a, i) => a.equals(rtAddrDecoded[i])),});
// MapCodec<string, number>const rtStrNumMap = new Map<string, number>([ ['a', 1], ['b', 2],]);const strNumMapCodec = new MapCodec(stringCodec, numberCodec);const rtStrNumDecoded = strNumMapCodec.decode(strNumMapCodec.encode(rtStrNumMap, 'json'), 'json');roundTrips.push({ name: 'MapCodec<string, number>', value: 'Map { "a" => 1, "b" => 2 }', format: 'json', success: Array.from(rtStrNumMap.entries()).every(([k, v]) => rtStrNumDecoded.get(k) === v),});
// MapCodec<bigint, string>const rtBigStrMap = new Map<bigint, string>([ [1n, 'one'], [2n, 'two'],]);const bigStrMapCodec = new MapCodec(bigIntCodec, stringCodec);const rtBigStrDecoded = bigStrMapCodec.decode(bigStrMapCodec.encode(rtBigStrMap, 'json'), 'json');roundTrips.push({ name: 'MapCodec<bigint, string>', value: 'Map { 1n => "one", 2n => "two" }', format: 'json', success: Array.from(rtBigStrMap.entries()).every(([k, v]) => rtBigStrDecoded.get(k) === v),});
// RecordCodec<number>const rtNumRecord = { a: 1, b: 2, c: 3 };const numRecordCodec = new RecordCodec(numberCodec);const rtNumRecDecoded = numRecordCodec.decode(numRecordCodec.encode(rtNumRecord, 'json'), 'json');roundTrips.push({ name: 'RecordCodec<number>', value: '{ a: 1, b: 2, c: 3 }', format: 'json', success: Object.entries(rtNumRecord).every(([k, v]) => rtNumRecDecoded[k] === v),});
// RecordCodec<string>const rtStrRecord = { name: 'test', type: 'example' };const strRecordCodec = new RecordCodec(stringCodec);const rtStrRecDecoded = strRecordCodec.decode(strRecordCodec.encode(rtStrRecord, 'json'), 'json');roundTrips.push({ name: 'RecordCodec<string>', value: '{ name: "test", type: "example" }', format: 'json', success: Object.entries(rtStrRecord).every(([k, v]) => rtStrRecDecoded[k] === v),});
// Nested: Array of arraysconst rtNestedArray = [ [1, 2], [3, 4],];const nestedArrayCodec = new ArrayCodec(new ArrayCodec(numberCodec));const rtNestedDecoded = nestedArrayCodec.decode( nestedArrayCodec.encode(rtNestedArray, 'json'), 'json',);roundTrips.push({ name: 'ArrayCodec (nested)', value: '[[1, 2], [3, 4]]', format: 'json', success: rtNestedArray.every((row, i) => row.every((val, j) => val === rtNestedDecoded[i][j])),});
// Nested: Map of arraysconst rtMapOfArrays = new Map<string, number[]>([ ['x', [1, 2, 3]], ['y', [4, 5, 6]],]);const mapOfArraysCodec = new MapCodec(stringCodec, numberArrayCodec);const rtMapOfArraysDecoded = mapOfArraysCodec.decode( mapOfArraysCodec.encode(rtMapOfArrays, 'json'), 'json',);roundTrips.push({ name: 'MapCodec (with array values)', value: 'Map { "x" => [1,2,3], "y" => [4,5,6] }', format: 'json', success: Array.from(rtMapOfArrays.entries()).every(([k, v]) => { const decoded = rtMapOfArraysDecoded.get(k); return decoded && v.every((val, i) => val === decoded[i]); }),});
// Display resultsfor (const rt of roundTrips) { const status = rt.success ? 'PASS' : 'FAIL'; printInfo(` [${status}] ${rt.name} (${rt.format}): ${rt.value}`);}
const allPassed = roundTrips.every(rt => rt.success);if (allPassed) { printInfo(''); printSuccess('All round-trip verifications passed!');}
// ============================================================================// Step 10: Summary// ============================================================================printStep(10, 'Summary');
printInfo('Composite codecs for complex data structures:');printInfo('');printInfo(' ArrayCodec<T>:');printInfo(' - new ArrayCodec(elementCodec)');printInfo(' - Encodes/decodes T[] using the element codec');printInfo(' - Default value: [] (empty array)');printInfo('');printInfo(' Pre-built Array Codecs:');printInfo(' - numberArrayCodec - number[]');printInfo(' - stringArrayCodec - string[]');printInfo(' - bigIntArrayCodec - bigint[]');printInfo(' - booleanArrayCodec - boolean[]');printInfo(' - bytesArrayCodec - Uint8Array[]');printInfo(' - addressArrayCodec - Address[]');printInfo('');printInfo(' MapCodec<K, V>:');printInfo(' - new MapCodec(keyCodec, valueCodec)');printInfo(' - JSON: string/bigint keys, encoded as object');printInfo(' - msgpack: any key type, preserved as Map');printInfo(' - Default value: new Map() (empty map)');printInfo('');printInfo(' RecordCodec<V>:');printInfo(' - new RecordCodec(valueCodec)');printInfo(' - Encodes/decodes Record<string, V>');printInfo(' - Default value: {} (empty object)');printInfo('');printInfo(' Nesting:');printInfo(' - Composite codecs can be nested');printInfo(' - ArrayCodec(ArrayCodec(...)) for 2D arrays');printInfo(' - MapCodec(keyCodec, ArrayCodec(...)) for map of arrays');printInfo('');printSuccess('Composite Codecs Example completed!');