diff --git a/metrics/arktype.heap.after.json b/metrics/arktype.heap.after.json new file mode 100644 index 0000000..5668a44 --- /dev/null +++ b/metrics/arktype.heap.after.json @@ -0,0 +1,114 @@ +{ + "objectTypeCounts": { + "string": 116051, + "Function": 99724, + "Array": 74625, + "Structure": 70226, + "Object": 61007, + "FunctionExecutable": 38609, + "UnlinkedFunctionExecutable": 38323, + "JSLexicalEnvironment": 36478, + "FunctionRareData": 17279, + "FunctionCodeBlock": 16211, + "UnlinkedFunctionCodeBlock": 15837, + "StructureRareData": 870, + "SymbolTable": 541, + "NativeExecutable": 446, + "InternalPromise": 355, + "Immutable Butterfly": 211, + "PropertyTable": 199, + "RegExp": 133, + "Map": 121, + "JSModuleEnvironment": 117, + "JSSourceCode": 117, + "ModuleRecord": 117, + "ModuleProgramExecutable": 116, + "ModuleProgramCodeBlock": 115, + "UnlinkedModuleProgramCodeBlock": 110, + "GetterSetter": 109, + "CustomGetterSetter": 96, + "JSPropertyNameEnumerator": 75, + "DOMAttributeGetterSetter": 41, + "StructureChain": 27, + "symbol": 20, + "Callee": 3, + "ArrayBuffer": 2, + "AsyncFunction": 2, + "AsyncGeneratorFunction": 2, + "BigInt": 2, + "FormData": 2, + "GeneratorFunction": 2, + "Generator": 2, + "Headers": 2, + "Performance": 2, + "SparseArrayValueMap": 2, + "URL": 2, + "Array Iterator": 1, + "AsyncGenerator": 1, + "AsyncIterator": 1, + "BigInt64ArrayPrototype": 1, + "BigUint64ArrayPrototype": 1, + "Blob": 1, + "Boolean": 1, + "Bun": 1, + "EventEmitter": 1, + "EventTarget": 1, + "File": 1, + "FinalizationRegistry": 1, + "Float16ArrayPrototype": 1, + "Float32ArrayPrototype": 1, + "Float64ArrayPrototype": 1, + "GlobalObject": 1, + "Int16ArrayPrototype": 1, + "Int32ArrayPrototype": 1, + "Int8ArrayPrototype": 1, + "InternalFieldTuple": 1, + "InternalModuleRegistry": 1, + "InternalPromisePrototype": 1, + "Intl": 1, + "Iterator Helper": 1, + "Iterator": 1, + "JSGlobalLexicalEnvironment": 1, + "JSGlobalProxy": 1, + "JSON": 1, + "Map Iterator": 1, + "Math": 1, + "ModuleLoader": 1, + "ModuleNamespaceObject": 1, + "Number": 1, + "ProcessBindingConstants": 1, + "Process": 1, + "Promise": 1, + "Prototype": 1, + "Request": 1, + "Response": 1, + "Set Iterator": 1, + "Set": 1, + "ShadowRealm": 1, + "String Iterator": 1, + "String": 1, + "Symbol": 1, + "TextEncoderStreamEncoder": 1, + "Uint16ArrayPrototype": 1, + "Uint32ArrayPrototype": 1, + "Uint8ArrayPrototype": 1, + "Uint8ClampedArrayPrototype": 1, + "WeakMap": 1, + "WeakRef": 1, + "WeakSet": 1, + "WebAssembly": 1, + "console": 1 + }, + "protectedObjectTypeCounts": { + "UnlinkedFunctionExecutable": 1903, + "UnlinkedModuleProgramCodeBlock": 97, + "GlobalObject": 1 + }, + "heapSize": 77008258, + "heapCapacity": 104685746, + "extraMemorySize": 26599506, + "objectCount": 698315, + "protectedObjectCount": 2001, + "globalObjectCount": 1, + "protectedGlobalObjectCount": 1 +} \ No newline at end of file diff --git a/metrics/arktype.heap.before.json b/metrics/arktype.heap.before.json new file mode 100644 index 0000000..fcfb615 --- /dev/null +++ b/metrics/arktype.heap.before.json @@ -0,0 +1,115 @@ +{ + "objectTypeCounts": { + "string": 214279, + "Array": 51489, + "Function": 39309, + "Object": 35013, + "JSLexicalEnvironment": 33608, + "ArrayIterator": 25330, + "Immutable Butterfly": 10365, + "Structure": 10176, + "FunctionExecutable": 5716, + "UnlinkedFunctionExecutable": 5290, + "FunctionRareData": 4505, + "PropertyTable": 2188, + "SymbolTable": 1839, + "InternalPromise": 1139, + "FunctionCodeBlock": 1034, + "StructureRareData": 892, + "UnlinkedFunctionCodeBlock": 746, + "NativeExecutable": 405, + "Generator": 337, + "AsyncFunction": 335, + "RegExp": 229, + "Map": 121, + "Callee": 117, + "JSModuleEnvironment": 117, + "JSSourceCode": 117, + "ModuleRecord": 117, + "ModuleProgramExecutable": 116, + "ModuleProgramCodeBlock": 115, + "UnlinkedModuleProgramCodeBlock": 110, + "GetterSetter": 109, + "CustomGetterSetter": 96, + "JSPropertyNameEnumerator": 68, + "DOMAttributeGetterSetter": 41, + "StructureChain": 26, + "symbol": 20, + "Set": 3, + "ArrayBuffer": 2, + "AsyncGeneratorFunction": 2, + "BigInt": 2, + "FormData": 2, + "GeneratorFunction": 2, + "Headers": 2, + "Performance": 2, + "URL": 2, + "Array Iterator": 1, + "AsyncGenerator": 1, + "AsyncIterator": 1, + "BigInt64ArrayPrototype": 1, + "BigUint64ArrayPrototype": 1, + "Blob": 1, + "Boolean": 1, + "Bun": 1, + "Error": 1, + "EventEmitter": 1, + "EventTarget": 1, + "File": 1, + "FinalizationRegistry": 1, + "Float16ArrayPrototype": 1, + "Float32ArrayPrototype": 1, + "Float64ArrayPrototype": 1, + "GlobalObject": 1, + "Int16ArrayPrototype": 1, + "Int32ArrayPrototype": 1, + "Int8ArrayPrototype": 1, + "InternalFieldTuple": 1, + "InternalModuleRegistry": 1, + "InternalPromisePrototype": 1, + "Intl": 1, + "Iterator Helper": 1, + "Iterator": 1, + "JSGlobalLexicalEnvironment": 1, + "JSGlobalProxy": 1, + "JSON": 1, + "Map Iterator": 1, + "ModuleLoader": 1, + "ModuleNamespaceObject": 1, + "Number": 1, + "ProcessBindingConstants": 1, + "Process": 1, + "Promise": 1, + "Prototype": 1, + "Request": 1, + "Response": 1, + "Set Iterator": 1, + "ShadowRealm": 1, + "SparseArrayValueMap": 1, + "String Iterator": 1, + "String": 1, + "Symbol": 1, + "TextEncoderStreamEncoder": 1, + "Uint16ArrayPrototype": 1, + "Uint32ArrayPrototype": 1, + "Uint8ArrayPrototype": 1, + "Uint8ClampedArrayPrototype": 1, + "WeakMap": 1, + "WeakRef": 1, + "WeakSet": 1, + "WebAssembly": 1, + "console": 1 + }, + "protectedObjectTypeCounts": { + "UnlinkedModuleProgramCodeBlock": 110, + "UnlinkedFunctionExecutable": 96, + "GlobalObject": 1 + }, + "heapSize": 238877, + "heapCapacity": 23103309, + "extraMemorySize": 40589, + "objectCount": 2941, + "protectedObjectCount": 207, + "globalObjectCount": 1, + "protectedGlobalObjectCount": 1 +} \ No newline at end of file diff --git a/metrics/suretype.heap.after.json b/metrics/suretype.heap.after.json new file mode 100644 index 0000000..082f3cf --- /dev/null +++ b/metrics/suretype.heap.after.json @@ -0,0 +1,137 @@ +{ + "objectTypeCounts": { + "string": 5198, + "Function": 3184, + "Structure": 3145, + "FunctionExecutable": 2388, + "UnlinkedFunctionExecutable": 2088, + "NativeExecutable": 788, + "FunctionCodeBlock": 733, + "Object": 568, + "SymbolTable": 554, + "UnlinkedFunctionCodeBlock": 511, + "JSLexicalEnvironment": 344, + "StructureRareData": 331, + "InternalPromise": 305, + "RegExp": 301, + "GetterSetter": 275, + "Immutable Butterfly": 224, + "Array": 222, + "FunctionRareData": 221, + "PropertyTable": 190, + "CustomGetterSetter": 138, + "Map": 110, + "JSModuleEnvironment": 102, + "JSSourceCode": 102, + "ModuleRecord": 102, + "symbol": 90, + "ModuleProgramExecutable": 86, + "ModuleProgramCodeBlock": 85, + "UnlinkedModuleProgramCodeBlock": 85, + "StructureChain": 78, + "AsyncFunction": 69, + "Module": 57, + "UnlinkedProgramCodeBlock": 53, + "ProgramCodeBlock": 51, + "ProgramExecutable": 51, + "JSPropertyNameEnumerator": 37, + "SparseArrayValueMap": 37, + "DOMAttributeGetterSetter": 34, + "Set": 9, + "Promise": 7, + "ModuleNamespaceObject": 5, + "FileSink": 4, + "GeneratorFunction": 4, + "Arguments": 3, + "Callee": 3, + "Generator": 3, + "JSAsyncGeneratorFunction": 3, + "ReadableStream": 3, + "ArrayBuffer": 2, + "AsyncGeneratorFunction": 2, + "BigInt": 2, + "Blob": 2, + "FileInternalReadableStreamSource": 2, + "ImportMeta": 2, + "NodeJSFS": 2, + "Performance": 2, + "StringDecoder": 2, + "TextEncoder": 2, + "URLSearchParams": 2, + "URL": 2, + "WeakMap": 2, + "WeakSet": 2, + "Array Iterator": 1, + "AsyncGenerator": 1, + "AsyncIterator": 1, + "BigInt64ArrayPrototype": 1, + "BigUint64ArrayPrototype": 1, + "Boolean": 1, + "Buffer": 1, + "Bun": 1, + "Dirent": 1, + "EventEmitter": 1, + "EventTarget": 1, + "File": 1, + "FinalizationRegistry": 1, + "Float16ArrayPrototype": 1, + "Float32ArrayPrototype": 1, + "Float64ArrayPrototype": 1, + "GlobalObject": 1, + "Int16ArrayPrototype": 1, + "Int32ArrayPrototype": 1, + "Int8ArrayPrototype": 1, + "InternalFieldTuple": 1, + "InternalModuleRegistry": 1, + "InternalPromisePrototype": 1, + "Intl": 1, + "Iterator Helper": 1, + "Iterator": 1, + "JSGlobalLexicalEnvironment": 1, + "JSGlobalProxy": 1, + "JSON": 1, + "Map Iterator": 1, + "Math": 1, + "ModuleLoader": 1, + "ModulePrototype": 1, + "NextTickQueue": 1, + "Number": 1, + "ProcessBindingConstants": 1, + "Process": 1, + "Prototype": 1, + "ProxyObject": 1, + "Proxy": 1, + "Reflect": 1, + "Set Iterator": 1, + "ShadowRealm": 1, + "Stats": 1, + "String Iterator": 1, + "String": 1, + "Symbol": 1, + "TextDecoder": 1, + "TextEncoderStreamEncoder": 1, + "Uint16ArrayPrototype": 1, + "Uint32ArrayPrototype": 1, + "Uint8ArrayPrototype": 1, + "Uint8Array": 1, + "Uint8ClampedArrayPrototype": 1, + "WeakRef": 1, + "WebAssembly": 1, + "console": 1, + "require": 1, + "resolve": 1 + }, + "protectedObjectTypeCounts": { + "UnlinkedModuleProgramCodeBlock": 85, + "UnlinkedProgramCodeBlock": 53, + "UnlinkedFunctionExecutable": 5, + "GlobalObject": 1 + }, + "heapSize": 4831195, + "heapCapacity": 7239291, + "extraMemorySize": 2868747, + "objectCount": 24936, + "protectedObjectCount": 144, + "globalObjectCount": 1, + "protectedGlobalObjectCount": 1 +} \ No newline at end of file diff --git a/metrics/suretype.heap.before.json b/metrics/suretype.heap.before.json new file mode 100644 index 0000000..df24660 --- /dev/null +++ b/metrics/suretype.heap.before.json @@ -0,0 +1,137 @@ +{ + "objectTypeCounts": { + "string": 5437, + "Function": 5200, + "Structure": 3336, + "FunctionExecutable": 2344, + "Object": 2094, + "JSLexicalEnvironment": 2090, + "UnlinkedFunctionExecutable": 2033, + "SymbolTable": 1142, + "Array": 1136, + "InternalPromise": 885, + "NativeExecutable": 777, + "PropertyTable": 654, + "RegExp": 438, + "Arguments": 353, + "FunctionCodeBlock": 336, + "UnlinkedFunctionCodeBlock": 310, + "AsyncFunction": 308, + "GetterSetter": 283, + "Generator": 243, + "Immutable Butterfly": 216, + "FunctionRareData": 213, + "CustomGetterSetter": 142, + "StructureRareData": 122, + "Map": 110, + "ArrayIterator": 106, + "JSSourceCode": 105, + "JSModuleEnvironment": 102, + "ModuleRecord": 102, + "symbol": 95, + "Callee": 87, + "ModuleProgramExecutable": 86, + "ModuleProgramCodeBlock": 85, + "UnlinkedModuleProgramCodeBlock": 85, + "Module": 58, + "ProgramCodeBlock": 53, + "ProgramExecutable": 53, + "UnlinkedProgramCodeBlock": 53, + "SparseArrayValueMap": 37, + "StructureChain": 36, + "DOMAttributeGetterSetter": 34, + "Set": 11, + "Promise": 7, + "Map Iterator": 6, + "Set Iterator": 6, + "ModuleNamespaceObject": 5, + "Blob": 4, + "FileSink": 4, + "GeneratorFunction": 4, + "JSAsyncGeneratorFunction": 4, + "JSPropertyNameEnumerator": 3, + "ReadableStream": 3, + "ArrayBuffer": 2, + "AsyncGeneratorFunction": 2, + "BigInt": 2, + "FileInternalReadableStreamSource": 2, + "ImportMeta": 2, + "NodeJSFS": 2, + "Performance": 2, + "String Iterator": 2, + "StringDecoder": 2, + "TextEncoder": 2, + "URLSearchParams": 2, + "URL": 2, + "WeakMap": 2, + "WeakSet": 2, + "Array Iterator": 1, + "AsyncGenerator": 1, + "AsyncIterator": 1, + "BigInt64ArrayPrototype": 1, + "BigUint64ArrayPrototype": 1, + "Boolean": 1, + "Buffer": 1, + "Bun": 1, + "Dirent": 1, + "EventEmitter": 1, + "EventTarget": 1, + "File": 1, + "FinalizationRegistry": 1, + "Float16ArrayPrototype": 1, + "Float32ArrayPrototype": 1, + "Float64ArrayPrototype": 1, + "GlobalObject": 1, + "Int16ArrayPrototype": 1, + "Int32ArrayPrototype": 1, + "Int8ArrayPrototype": 1, + "InternalFieldTuple": 1, + "InternalModuleRegistry": 1, + "InternalPromisePrototype": 1, + "Intl": 1, + "Iterator Helper": 1, + "Iterator": 1, + "JSGlobalLexicalEnvironment": 1, + "JSGlobalProxy": 1, + "JSON": 1, + "Math": 1, + "ModuleLoader": 1, + "ModulePrototype": 1, + "NextTickQueue": 1, + "Number": 1, + "ProcessBindingConstants": 1, + "Process": 1, + "Prototype": 1, + "ProxyObject": 1, + "Proxy": 1, + "Reflect": 1, + "ShadowRealm": 1, + "Stats": 1, + "String": 1, + "Symbol": 1, + "TextDecoder": 1, + "TextEncoderStreamEncoder": 1, + "Uint16ArrayPrototype": 1, + "Uint32ArrayPrototype": 1, + "Uint8ArrayPrototype": 1, + "Uint8Array": 1, + "Uint8ClampedArrayPrototype": 1, + "WeakRef": 1, + "WebAssembly": 1, + "console": 1, + "require": 1, + "resolve": 1 + }, + "protectedObjectTypeCounts": { + "UnlinkedModuleProgramCodeBlock": 85, + "UnlinkedProgramCodeBlock": 53, + "GlobalObject": 1 + }, + "heapSize": 238301, + "heapCapacity": 3505965, + "extraMemorySize": 40589, + "objectCount": 2934, + "protectedObjectCount": 139, + "globalObjectCount": 1, + "protectedGlobalObjectCount": 1 +} \ No newline at end of file diff --git a/metrics/valibot.heap.after.json b/metrics/valibot.heap.after.json new file mode 100644 index 0000000..1aeddf2 --- /dev/null +++ b/metrics/valibot.heap.after.json @@ -0,0 +1,90 @@ +{ + "objectTypeCounts": { + "FunctionExecutable": 1223, + "Function": 1149, + "string": 849, + "UnlinkedFunctionExecutable": 815, + "Structure": 449, + "NativeExecutable": 333, + "FunctionCodeBlock": 125, + "RegExp": 121, + "UnlinkedFunctionCodeBlock": 89, + "PropertyTable": 77, + "SymbolTable": 59, + "StructureRareData": 39, + "GetterSetter": 35, + "Object": 28, + "CustomGetterSetter": 27, + "InternalPromise": 21, + "symbol": 20, + "Array": 17, + "Map": 9, + "JSLexicalEnvironment": 7, + "Immutable Butterfly": 6, + "JSModuleEnvironment": 6, + "JSSourceCode": 6, + "ModuleRecord": 6, + "ModuleProgramExecutable": 5, + "UnlinkedModuleProgramCodeBlock": 5, + "ModuleProgramCodeBlock": 4, + "Callee": 3, + "ArrayBuffer": 2, + "AsyncFunction": 2, + "AsyncGeneratorFunction": 2, + "BigInt": 2, + "DOMAttributeGetterSetter": 2, + "GeneratorFunction": 2, + "Generator": 2, + "JSPropertyNameEnumerator": 2, + "Performance": 2, + "StructureChain": 2, + "Array Iterator": 1, + "AsyncGenerator": 1, + "AsyncIterator": 1, + "Boolean": 1, + "Bun": 1, + "EventTarget": 1, + "FinalizationRegistry": 1, + "Float16ArrayPrototype": 1, + "GlobalObject": 1, + "InternalFieldTuple": 1, + "InternalModuleRegistry": 1, + "InternalPromisePrototype": 1, + "Intl": 1, + "Iterator Helper": 1, + "Iterator": 1, + "JSGlobalLexicalEnvironment": 1, + "JSGlobalProxy": 1, + "JSON": 1, + "Map Iterator": 1, + "Math": 1, + "ModuleLoader": 1, + "ModuleNamespaceObject": 1, + "Number": 1, + "ProcessBindingConstants": 1, + "Promise": 1, + "Prototype": 1, + "Set Iterator": 1, + "Set": 1, + "ShadowRealm": 1, + "SparseArrayValueMap": 1, + "String Iterator": 1, + "String": 1, + "Symbol": 1, + "TextEncoderStreamEncoder": 1, + "WeakRef": 1, + "WebAssembly": 1, + "console": 1 + }, + "protectedObjectTypeCounts": { + "UnlinkedModuleProgramCodeBlock": 5, + "GlobalObject": 1 + }, + "heapSize": 795561, + "heapCapacity": 1465289, + "extraMemorySize": 318409, + "objectCount": 6036, + "protectedObjectCount": 6, + "globalObjectCount": 1, + "protectedGlobalObjectCount": 1 +} \ No newline at end of file diff --git a/metrics/valibot.heap.before.json b/metrics/valibot.heap.before.json new file mode 100644 index 0000000..f0545f5 --- /dev/null +++ b/metrics/valibot.heap.before.json @@ -0,0 +1,88 @@ +{ + "objectTypeCounts": { + "FunctionExecutable": 1207, + "Function": 1199, + "UnlinkedFunctionExecutable": 796, + "string": 771, + "Structure": 362, + "NativeExecutable": 290, + "RegExp": 121, + "PropertyTable": 98, + "SymbolTable": 94, + "JSLexicalEnvironment": 88, + "Object": 73, + "InternalPromise": 51, + "FunctionCodeBlock": 48, + "UnlinkedFunctionCodeBlock": 48, + "Array": 40, + "GetterSetter": 35, + "CustomGetterSetter": 27, + "symbol": 20, + "StructureRareData": 18, + "Generator": 15, + "AsyncFunction": 13, + "Immutable Butterfly": 9, + "Map": 9, + "Callee": 6, + "JSModuleEnvironment": 6, + "JSSourceCode": 6, + "ModuleRecord": 6, + "ModuleProgramExecutable": 5, + "UnlinkedModuleProgramCodeBlock": 5, + "ModuleProgramCodeBlock": 4, + "Set": 3, + "ArrayBuffer": 2, + "AsyncGeneratorFunction": 2, + "BigInt": 2, + "DOMAttributeGetterSetter": 2, + "GeneratorFunction": 2, + "Performance": 2, + "Array Iterator": 1, + "AsyncGenerator": 1, + "AsyncIterator": 1, + "Boolean": 1, + "Bun": 1, + "EventTarget": 1, + "FinalizationRegistry": 1, + "Float16ArrayPrototype": 1, + "GlobalObject": 1, + "InternalFieldTuple": 1, + "InternalModuleRegistry": 1, + "InternalPromisePrototype": 1, + "Intl": 1, + "Iterator Helper": 1, + "Iterator": 1, + "JSGlobalLexicalEnvironment": 1, + "JSGlobalProxy": 1, + "JSON": 1, + "JSPropertyNameEnumerator": 1, + "Map Iterator": 1, + "ModuleLoader": 1, + "ModuleNamespaceObject": 1, + "Number": 1, + "ProcessBindingConstants": 1, + "Promise": 1, + "Prototype": 1, + "Set Iterator": 1, + "ShadowRealm": 1, + "SparseArrayValueMap": 1, + "String Iterator": 1, + "String": 1, + "Symbol": 1, + "TextEncoderStreamEncoder": 1, + "WeakRef": 1, + "WebAssembly": 1, + "console": 1 + }, + "protectedObjectTypeCounts": { + "UnlinkedModuleProgramCodeBlock": 5, + "GlobalObject": 1 + }, + "heapSize": 237661, + "heapCapacity": 1121293, + "extraMemorySize": 39949, + "objectCount": 2934, + "protectedObjectCount": 6, + "globalObjectCount": 1, + "protectedGlobalObjectCount": 1 +} \ No newline at end of file diff --git a/metrics/zod-mini.heap.after.json b/metrics/zod-mini.heap.after.json new file mode 100644 index 0000000..2ff0e1c --- /dev/null +++ b/metrics/zod-mini.heap.after.json @@ -0,0 +1,93 @@ +{ + "objectTypeCounts": { + "UnlinkedFunctionExecutable": 2102, + "Function": 1882, + "FunctionExecutable": 1564, + "string": 1534, + "Structure": 549, + "NativeExecutable": 341, + "JSLexicalEnvironment": 321, + "FunctionRareData": 316, + "Object": 248, + "FunctionCodeBlock": 244, + "SymbolTable": 177, + "UnlinkedFunctionCodeBlock": 153, + "RegExp": 115, + "InternalPromise": 97, + "StructureRareData": 95, + "PropertyTable": 80, + "Array": 48, + "GetterSetter": 36, + "Immutable Butterfly": 35, + "Map": 35, + "JSModuleEnvironment": 31, + "JSSourceCode": 31, + "ModuleRecord": 31, + "ModuleProgramExecutable": 30, + "UnlinkedModuleProgramCodeBlock": 30, + "ModuleProgramCodeBlock": 29, + "CustomGetterSetter": 27, + "symbol": 24, + "ModuleNamespaceObject": 11, + "SparseArrayValueMap": 11, + "BigInt": 6, + "StructureChain": 6, + "Callee": 3, + "Set": 3, + "ArrayBuffer": 2, + "AsyncFunction": 2, + "AsyncGeneratorFunction": 2, + "DOMAttributeGetterSetter": 2, + "GeneratorFunction": 2, + "Generator": 2, + "Performance": 2, + "WeakMap": 2, + "Array Iterator": 1, + "AsyncGenerator": 1, + "AsyncIterator": 1, + "Boolean": 1, + "Bun": 1, + "EventTarget": 1, + "FinalizationRegistry": 1, + "Float16ArrayPrototype": 1, + "GlobalObject": 1, + "InternalFieldTuple": 1, + "InternalModuleRegistry": 1, + "InternalPromisePrototype": 1, + "Intl": 1, + "Iterator Helper": 1, + "Iterator": 1, + "JSGlobalLexicalEnvironment": 1, + "JSGlobalProxy": 1, + "JSON": 1, + "JSPropertyNameEnumerator": 1, + "Map Iterator": 1, + "Math": 1, + "ModuleLoader": 1, + "Number": 1, + "ProcessBindingConstants": 1, + "Promise": 1, + "Prototype": 1, + "Set Iterator": 1, + "ShadowRealm": 1, + "String Iterator": 1, + "String": 1, + "Symbol": 1, + "TextEncoderStreamEncoder": 1, + "WeakRef": 1, + "WebAssembly": 1, + "console": 1 + }, + "protectedObjectTypeCounts": { + "UnlinkedFunctionExecutable": 1001, + "UnlinkedModuleProgramCodeBlock": 30, + "GlobalObject": 1 + }, + "heapSize": 1668146, + "heapCapacity": 3284098, + "extraMemorySize": 810114, + "objectCount": 11140, + "protectedObjectCount": 1032, + "globalObjectCount": 1, + "protectedGlobalObjectCount": 1 +} \ No newline at end of file diff --git a/metrics/zod-mini.heap.before.json b/metrics/zod-mini.heap.before.json new file mode 100644 index 0000000..511c016 --- /dev/null +++ b/metrics/zod-mini.heap.before.json @@ -0,0 +1,91 @@ +{ + "objectTypeCounts": { + "string": 5481, + "Function": 2415, + "FunctionExecutable": 1511, + "UnlinkedFunctionExecutable": 1045, + "JSLexicalEnvironment": 858, + "Object": 696, + "Structure": 437, + "FunctionRareData": 314, + "NativeExecutable": 296, + "InternalPromise": 273, + "SymbolTable": 223, + "Array": 173, + "PropertyTable": 153, + "RegExp": 115, + "Generator": 76, + "AsyncFunction": 74, + "FunctionCodeBlock": 66, + "UnlinkedFunctionCodeBlock": 58, + "Immutable Butterfly": 42, + "GetterSetter": 37, + "Map": 35, + "Callee": 31, + "JSModuleEnvironment": 31, + "JSSourceCode": 31, + "ModuleRecord": 31, + "ModuleProgramExecutable": 30, + "StructureRareData": 30, + "UnlinkedModuleProgramCodeBlock": 30, + "ModuleProgramCodeBlock": 29, + "CustomGetterSetter": 27, + "symbol": 24, + "ModuleNamespaceObject": 11, + "SparseArrayValueMap": 11, + "BigInt": 7, + "Set": 5, + "ArrayBuffer": 2, + "AsyncGeneratorFunction": 2, + "DOMAttributeGetterSetter": 2, + "GeneratorFunction": 2, + "Performance": 2, + "WeakMap": 2, + "Array Iterator": 1, + "AsyncGenerator": 1, + "AsyncIterator": 1, + "Boolean": 1, + "Bun": 1, + "EventTarget": 1, + "FinalizationRegistry": 1, + "Float16ArrayPrototype": 1, + "GlobalObject": 1, + "InternalFieldTuple": 1, + "InternalModuleRegistry": 1, + "InternalPromisePrototype": 1, + "Intl": 1, + "Iterator Helper": 1, + "Iterator": 1, + "JSGlobalLexicalEnvironment": 1, + "JSGlobalProxy": 1, + "JSON": 1, + "JSPropertyNameEnumerator": 1, + "Map Iterator": 1, + "ModuleLoader": 1, + "Number": 1, + "ProcessBindingConstants": 1, + "Promise": 1, + "Prototype": 1, + "Set Iterator": 1, + "ShadowRealm": 1, + "String Iterator": 1, + "String": 1, + "StructureChain": 1, + "Symbol": 1, + "TextEncoderStreamEncoder": 1, + "WeakRef": 1, + "WebAssembly": 1, + "console": 1 + }, + "protectedObjectTypeCounts": { + "UnlinkedModuleProgramCodeBlock": 30, + "GlobalObject": 1 + }, + "heapSize": 237661, + "heapCapacity": 1825805, + "extraMemorySize": 39949, + "objectCount": 2934, + "protectedObjectCount": 31, + "globalObjectCount": 1, + "protectedGlobalObjectCount": 1 +} \ No newline at end of file diff --git a/metrics/zod3.heap.after.json b/metrics/zod3.heap.after.json new file mode 100644 index 0000000..f8b2fd2 --- /dev/null +++ b/metrics/zod3.heap.after.json @@ -0,0 +1,94 @@ +{ + "objectTypeCounts": { + "Function": 1281, + "string": 1214, + "FunctionExecutable": 1125, + "Structure": 1080, + "UnlinkedFunctionExecutable": 940, + "NativeExecutable": 348, + "StructureRareData": 270, + "FunctionCodeBlock": 164, + "UnlinkedFunctionCodeBlock": 113, + "SymbolTable": 111, + "PropertyTable": 94, + "GetterSetter": 89, + "Object": 78, + "RegExp": 75, + "CustomGetterSetter": 28, + "JSLexicalEnvironment": 24, + "InternalPromise": 21, + "symbol": 21, + "Array": 17, + "StructureChain": 16, + "DOMAttributeGetterSetter": 14, + "FunctionRareData": 11, + "Immutable Butterfly": 9, + "Map": 9, + "JSModuleEnvironment": 6, + "JSSourceCode": 6, + "ModuleRecord": 6, + "AsyncFunction": 5, + "ModuleProgramExecutable": 5, + "UnlinkedModuleProgramCodeBlock": 5, + "ModuleProgramCodeBlock": 4, + "Callee": 3, + "SparseArrayValueMap": 3, + "WeakMap": 3, + "ArrayBuffer": 2, + "AsyncGeneratorFunction": 2, + "BigInt": 2, + "GeneratorFunction": 2, + "Generator": 2, + "Performance": 2, + "URL": 2, + "Arguments": 1, + "Array Iterator": 1, + "AsyncGenerator": 1, + "AsyncIterator": 1, + "Boolean": 1, + "Bun": 1, + "EventTarget": 1, + "FinalizationRegistry": 1, + "Float16ArrayPrototype": 1, + "GlobalObject": 1, + "InternalFieldTuple": 1, + "InternalModuleRegistry": 1, + "InternalPromisePrototype": 1, + "Intl": 1, + "Iterator Helper": 1, + "Iterator": 1, + "JSGlobalLexicalEnvironment": 1, + "JSGlobalProxy": 1, + "JSON": 1, + "JSPropertyNameEnumerator": 1, + "Map Iterator": 1, + "Math": 1, + "ModuleLoader": 1, + "ModuleNamespaceObject": 1, + "Number": 1, + "ProcessBindingConstants": 1, + "Promise": 1, + "Prototype": 1, + "Set Iterator": 1, + "Set": 1, + "ShadowRealm": 1, + "String Iterator": 1, + "String": 1, + "Symbol": 1, + "TextEncoderStreamEncoder": 1, + "WeakRef": 1, + "WebAssembly": 1, + "console": 1 + }, + "protectedObjectTypeCounts": { + "UnlinkedModuleProgramCodeBlock": 5, + "GlobalObject": 1 + }, + "heapSize": 1401479, + "heapCapacity": 2217511, + "extraMemorySize": 792103, + "objectCount": 7799, + "protectedObjectCount": 6, + "globalObjectCount": 1, + "protectedGlobalObjectCount": 1 +} \ No newline at end of file diff --git a/metrics/zod3.heap.before.json b/metrics/zod3.heap.before.json new file mode 100644 index 0000000..f2f67f9 --- /dev/null +++ b/metrics/zod3.heap.before.json @@ -0,0 +1,92 @@ +{ + "objectTypeCounts": { + "Function": 1326, + "FunctionExecutable": 1107, + "string": 949, + "UnlinkedFunctionExecutable": 919, + "Structure": 783, + "NativeExecutable": 297, + "SymbolTable": 173, + "PropertyTable": 157, + "Object": 122, + "JSLexicalEnvironment": 105, + "GetterSetter": 89, + "RegExp": 75, + "FunctionCodeBlock": 53, + "UnlinkedFunctionCodeBlock": 53, + "InternalPromise": 51, + "Array": 42, + "CustomGetterSetter": 27, + "symbol": 21, + "StructureRareData": 18, + "AsyncFunction": 16, + "Generator": 15, + "Immutable Butterfly": 11, + "Map": 9, + "StructureChain": 7, + "Callee": 6, + "JSModuleEnvironment": 6, + "JSSourceCode": 6, + "ModuleRecord": 6, + "ModuleProgramExecutable": 5, + "UnlinkedModuleProgramCodeBlock": 5, + "ModuleProgramCodeBlock": 4, + "Set": 3, + "SparseArrayValueMap": 3, + "WeakMap": 3, + "ArrayBuffer": 2, + "ArrayIterator": 2, + "AsyncGeneratorFunction": 2, + "BigInt": 2, + "DOMAttributeGetterSetter": 2, + "GeneratorFunction": 2, + "Performance": 2, + "Arguments": 1, + "Array Iterator": 1, + "AsyncGenerator": 1, + "AsyncIterator": 1, + "Boolean": 1, + "Bun": 1, + "EventTarget": 1, + "FinalizationRegistry": 1, + "Float16ArrayPrototype": 1, + "GlobalObject": 1, + "InternalFieldTuple": 1, + "InternalModuleRegistry": 1, + "InternalPromisePrototype": 1, + "Intl": 1, + "Iterator Helper": 1, + "Iterator": 1, + "JSGlobalLexicalEnvironment": 1, + "JSGlobalProxy": 1, + "JSON": 1, + "JSPropertyNameEnumerator": 1, + "Map Iterator": 1, + "ModuleLoader": 1, + "ModuleNamespaceObject": 1, + "Number": 1, + "ProcessBindingConstants": 1, + "Promise": 1, + "Prototype": 1, + "Set Iterator": 1, + "ShadowRealm": 1, + "String Iterator": 1, + "String": 1, + "Symbol": 1, + "TextEncoderStreamEncoder": 1, + "WeakRef": 1, + "WebAssembly": 1, + "console": 1 + }, + "protectedObjectTypeCounts": { + "UnlinkedModuleProgramCodeBlock": 5, + "GlobalObject": 1 + }, + "heapSize": 237661, + "heapCapacity": 1252365, + "extraMemorySize": 39949, + "objectCount": 2934, + "protectedObjectCount": 6, + "globalObjectCount": 1, + "protectedGlobalObjectCount": 1 +} \ No newline at end of file diff --git a/metrics/zod4.heap.after.json b/metrics/zod4.heap.after.json new file mode 100644 index 0000000..a156a3b --- /dev/null +++ b/metrics/zod4.heap.after.json @@ -0,0 +1,93 @@ +{ + "objectTypeCounts": { + "UnlinkedFunctionExecutable": 2203, + "Function": 1941, + "FunctionExecutable": 1668, + "string": 1615, + "Structure": 670, + "NativeExecutable": 344, + "JSLexicalEnvironment": 331, + "FunctionRareData": 316, + "FunctionCodeBlock": 264, + "Object": 261, + "SymbolTable": 185, + "UnlinkedFunctionCodeBlock": 161, + "RegExp": 115, + "InternalPromise": 104, + "StructureRareData": 104, + "PropertyTable": 83, + "Array": 58, + "Immutable Butterfly": 41, + "GetterSetter": 38, + "Map": 37, + "JSModuleEnvironment": 33, + "JSSourceCode": 33, + "ModuleRecord": 33, + "ModuleProgramExecutable": 32, + "UnlinkedModuleProgramCodeBlock": 32, + "ModuleProgramCodeBlock": 31, + "CustomGetterSetter": 27, + "symbol": 24, + "SparseArrayValueMap": 14, + "ModuleNamespaceObject": 13, + "BigInt": 6, + "StructureChain": 6, + "AsyncFunction": 4, + "Set": 4, + "Callee": 3, + "ArrayBuffer": 2, + "AsyncGeneratorFunction": 2, + "DOMAttributeGetterSetter": 2, + "GeneratorFunction": 2, + "Generator": 2, + "Performance": 2, + "WeakMap": 2, + "Array Iterator": 1, + "AsyncGenerator": 1, + "AsyncIterator": 1, + "Boolean": 1, + "Bun": 1, + "EventTarget": 1, + "FinalizationRegistry": 1, + "Float16ArrayPrototype": 1, + "GlobalObject": 1, + "InternalFieldTuple": 1, + "InternalModuleRegistry": 1, + "InternalPromisePrototype": 1, + "Intl": 1, + "Iterator Helper": 1, + "Iterator": 1, + "JSGlobalLexicalEnvironment": 1, + "JSGlobalProxy": 1, + "JSON": 1, + "JSPropertyNameEnumerator": 1, + "Map Iterator": 1, + "Math": 1, + "ModuleLoader": 1, + "Number": 1, + "ProcessBindingConstants": 1, + "Promise": 1, + "Prototype": 1, + "Set Iterator": 1, + "ShadowRealm": 1, + "String Iterator": 1, + "String": 1, + "Symbol": 1, + "TextEncoderStreamEncoder": 1, + "WeakRef": 1, + "WebAssembly": 1, + "console": 1 + }, + "protectedObjectTypeCounts": { + "UnlinkedFunctionExecutable": 1001, + "UnlinkedModuleProgramCodeBlock": 32, + "GlobalObject": 1 + }, + "heapSize": 1868321, + "heapCapacity": 3870289, + "extraMemorySize": 953937, + "objectCount": 11748, + "protectedObjectCount": 1034, + "globalObjectCount": 1, + "protectedGlobalObjectCount": 1 +} \ No newline at end of file diff --git a/metrics/zod4.heap.before.json b/metrics/zod4.heap.before.json new file mode 100644 index 0000000..b3881ee --- /dev/null +++ b/metrics/zod4.heap.before.json @@ -0,0 +1,91 @@ +{ + "objectTypeCounts": { + "string": 5902, + "Function": 2453, + "FunctionExecutable": 1523, + "UnlinkedFunctionExecutable": 1054, + "JSLexicalEnvironment": 888, + "Object": 714, + "Structure": 467, + "FunctionRareData": 314, + "NativeExecutable": 298, + "InternalPromise": 285, + "SymbolTable": 234, + "Array": 183, + "PropertyTable": 161, + "RegExp": 115, + "Generator": 78, + "AsyncFunction": 76, + "FunctionCodeBlock": 71, + "UnlinkedFunctionCodeBlock": 60, + "Immutable Butterfly": 46, + "GetterSetter": 38, + "Map": 37, + "Callee": 33, + "JSModuleEnvironment": 33, + "JSSourceCode": 33, + "ModuleRecord": 33, + "ModuleProgramExecutable": 32, + "UnlinkedModuleProgramCodeBlock": 32, + "ModuleProgramCodeBlock": 31, + "StructureRareData": 30, + "CustomGetterSetter": 27, + "symbol": 24, + "SparseArrayValueMap": 14, + "ModuleNamespaceObject": 13, + "BigInt": 7, + "Set": 5, + "ArrayBuffer": 2, + "AsyncGeneratorFunction": 2, + "DOMAttributeGetterSetter": 2, + "GeneratorFunction": 2, + "Performance": 2, + "WeakMap": 2, + "Array Iterator": 1, + "AsyncGenerator": 1, + "AsyncIterator": 1, + "Boolean": 1, + "Bun": 1, + "EventTarget": 1, + "FinalizationRegistry": 1, + "Float16ArrayPrototype": 1, + "GlobalObject": 1, + "InternalFieldTuple": 1, + "InternalModuleRegistry": 1, + "InternalPromisePrototype": 1, + "Intl": 1, + "Iterator Helper": 1, + "Iterator": 1, + "JSGlobalLexicalEnvironment": 1, + "JSGlobalProxy": 1, + "JSON": 1, + "JSPropertyNameEnumerator": 1, + "Map Iterator": 1, + "ModuleLoader": 1, + "Number": 1, + "ProcessBindingConstants": 1, + "Promise": 1, + "Prototype": 1, + "Set Iterator": 1, + "ShadowRealm": 1, + "String Iterator": 1, + "String": 1, + "StructureChain": 1, + "Symbol": 1, + "TextEncoderStreamEncoder": 1, + "WeakRef": 1, + "WebAssembly": 1, + "console": 1 + }, + "protectedObjectTypeCounts": { + "UnlinkedModuleProgramCodeBlock": 32, + "GlobalObject": 1 + }, + "heapSize": 237661, + "heapCapacity": 1842189, + "extraMemorySize": 39949, + "objectCount": 2934, + "protectedObjectCount": 33, + "globalObjectCount": 1, + "protectedGlobalObjectCount": 1 +} \ No newline at end of file diff --git a/package.json b/package.json index 634e5de..07052e2 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "scripts": { "bench": "bun bench:bun --warmup 5 --runs 10", "bench:bun": "hyperfine 'bun src/arktype.ts' 'bun src/suretype.ts' 'bun src/valibot.ts' 'bun src/zod-mini.ts' 'bun src/zod3.ts' 'bun src/zod4.ts'", + "bench:types": "hyperfine --warmup 5 --runs 10 'npx tsc -p tsconfig.arktype.json' 'npx tsc -p tsconfig.valibot.json' 'npx tsc -p tsconfig.zod3.json' 'npx tsc -p tsconfig.zod4.json'", "bench:node": "hyperfine --runs 10 'node dist/arktype.js' 'node dist/valibot.js' 'node dist/zod-mini.js' 'node dist/zod3.js' 'node dist/zod4.js'", "bench:node:strip": "hyperfine --runs 10 'node --experimental-strip-types src/arktype.ts' 'node --experimental-strip-types src/valibot.ts' 'node --experimental-strip-types src/zod-mini.ts' 'node --experimental-strip-types src/zod3.ts' 'node --experimental-strip-types src/zod4.ts'", "build": "bun scripts/build.ts && echo build success", @@ -56,8 +57,8 @@ "check:biome": "biome check --error-on-warnings --write --unsafe src && echo check:biome success", "check:once": "bun src/arktype.ts && bun src/suretype.ts && bun src/valibot.ts && bun src/zod-mini.ts && bun src/zod3.ts && bun src/zod4.ts && echo check:once success", "check:repo": "repo-check && echo check:repo success", - "check:tsc": "tsc --noEmit && echo check:tsc success" + "check:tsc": "tsc --noEmit -p tsconfig.app.json && echo check:tsc success" }, "type": "module", "version": "1.0.0" -} +} \ No newline at end of file diff --git a/src/arktype.ts b/src/arktype.ts index 7ac9fe9..59f4a06 100644 --- a/src/arktype.ts +++ b/src/arktype.ts @@ -1,6 +1,9 @@ +import { heapStats } from 'bun:jsc' import { type } from 'arktype' import { checkUserA, checkUserB, checkUserC, logExecTime, nbIterations } from './utils.ts' +Bun.write('./metrics/arktype.heap.before.json', JSON.stringify(heapStats())) + const startTime = performance.now() for (let i = 0; i < nbIterations; i++) { @@ -36,3 +39,7 @@ for (let i = 0; i < nbIterations; i++) { } logExecTime('ArkType', startTime) + +Bun.gc(true) + +Bun.write('./metrics/arktype.heap.after.json', JSON.stringify(heapStats())) diff --git a/src/schemas/arktype.ts b/src/schemas/arktype.ts new file mode 100644 index 0000000..f1df28d --- /dev/null +++ b/src/schemas/arktype.ts @@ -0,0 +1,132 @@ +import { type } from "arktype"; + +export const userSchema = type({ + name: type.string, + age: type.number.default(42), + phone: type.string + .or(type.number) + .pipe((phone: string | number) => + typeof phone === "number" ? phone.toString() : phone + ) + .default("123-456-7890"), +}); + +export type User = typeof userSchema.inferOut; + +export type UserInput = typeof userSchema.inferIn; + +// 1. Product Schema +export const productSchema = type({ + id: type.string, + name: type.string, + price: type.number.moreThan(0), + inStock: type.boolean, + tags: type.string.array(), +}); +export type Product = typeof productSchema.infer; + +// 2. Order Schema +export const orderSchema = type({ + orderId: "string.uuid.v4", + userId: type.string, + items: productSchema.array(), + totalAmount: type.number, + orderDate: type.Date, + status: type("'pending'|'shipped'|'delivered'|'cancelled'"), +}); +export type Order = typeof orderSchema.infer; + +// 3. Address Schema +export const addressSchema = type({ + street: type.string, + city: type.string, + state: type.string, + zipCode: /^[0-9]{5}(?:-[0-9]{4})?$/, + country: type.string.default("USA"), +}); +export type Address = typeof addressSchema.infer; + +// 4. Customer Schema +export const customerSchema = type({ + customerId: type.string, + basicInfo: userSchema, + shippingAddress: addressSchema, + billingAddress: addressSchema.or(type.null), + purchaseHistory: orderSchema.array().optional(), +}); +export type Customer = typeof customerSchema.inferOut; +export type CustomerInput = typeof customerSchema.inferIn; + +// 5. BlogPost Schema +export const blogPostSchema = type({ + postId: type.number.moreThan(0), + title: type.string, + content: type.string, + authorId: type.string, + publishDate: type.Date.or(type.undefined), + metadata: { + views: type.number.atLeast(0), + likes: type.number.atLeast(0), + category: type.string, + }, +}); +export type BlogPost = typeof blogPostSchema.infer; + +// 6. Configuration Schema +export const configSchema = type({ + apiKey: type.string, + timeoutMs: type.number.moreThan(0).default(5000), + retries: type.number.atLeast(0).atMost(5), + featureFlags: { + "betaFeature?": type.boolean, + newUI: type.boolean.default(false), + }, + "logLevel?": type("'debug'|'info'|'warn'|'error'"), +}); +export type Config = typeof configSchema.inferOut; +export type ConfigInput = typeof configSchema.inferIn; + +// 7. Event Schema +export const eventSchema = type({ + eventId: type.string, + eventName: type.string, + timestamp: type.number, + payload: type.unknown, + source: type("'web'|'mobile'|'server'"), +}); +export type Event = typeof eventSchema.infer; + +// 8. Image Metadata Schema +export const imageMetadataSchema = type({ + url: "string.url", + width: type.number.moreThan(0), + height: type.number.moreThan(0), + format: type("'jpeg'|'png'|'gif'|'webp'"), + "caption?": type.string.atMostLength(255), +}); +export type ImageMetadata = typeof imageMetadataSchema.infer; + +// 9. Employee Schema +export const employeeSchema = type({ + employeeId: type.string, + firstName: type.string, + lastName: type.string, + email: "string.email", + department: type.string, + startDate: type.Date, + isActive: type.boolean, + "managerId?": type.string.or(type.null), +}); +export type Employee = typeof employeeSchema.infer; + +// 10. Task Schema +export const taskSchema = type({ + taskId: type.string, + description: type.string, + dueDate: type.Date.optional(), + priority: type("'low'|'medium'|'high'"), + completed: type.boolean.default(false), + assignee: employeeSchema.or(type.null), +}); +export type Task = typeof taskSchema.inferOut; +export type TaskInput = typeof taskSchema.inferIn; diff --git a/src/schemas/valibot.ts b/src/schemas/valibot.ts new file mode 100644 index 0000000..c458c19 --- /dev/null +++ b/src/schemas/valibot.ts @@ -0,0 +1,132 @@ +import * as v from "valibot"; + +// User Schema +export const userSchema = v.object({ + name: v.string(), + age: v.optional(v.number(), 42), + phone: v.pipe( + v.optional(v.union([v.string(), v.number()]), "123-456-7890"), + v.transform((phone: string | number) => + typeof phone === "number" ? phone.toString() : phone + ) + ), +}); + +export type User = v.InferOutput; +export type UserInput = v.InferInput; + +// 1. Product Schema +export const productSchema = v.object({ + id: v.string(), + name: v.string(), + price: v.pipe(v.number(), v.minValue(0.01)), + inStock: v.boolean(), + tags: v.array(v.string()), +}); +export type Product = v.InferOutput; + +// 2. Order Schema +export const orderSchema = v.object({ + orderId: v.pipe(v.string(), v.uuid()), + userId: v.string(), + items: v.array(productSchema), + totalAmount: v.number(), + orderDate: v.date(), + status: v.picklist(["pending", "shipped", "delivered", "cancelled"]), +}); +export type Order = v.InferOutput; + +// 3. Address Schema +export const addressSchema = v.object({ + street: v.string(), + city: v.string(), + state: v.string(), + zipCode: v.pipe(v.string(), v.regex(/^[0-9]{5}(?:-[0-9]{4})?$/)), + country: v.optional(v.string(), "USA"), +}); +export type Address = v.InferOutput; + +// 4. Customer Schema +export const customerSchema = v.object({ + customerId: v.string(), + basicInfo: userSchema, + shippingAddress: addressSchema, + billingAddress: v.nullable(addressSchema), + purchaseHistory: v.optional(v.array(orderSchema)), +}); +export type Customer = v.InferOutput; +export type CustomerInput = v.InferInput; + +// 5. BlogPost Schema +export const blogPostSchema = v.object({ + postId: v.pipe(v.number(), v.minValue(1)), + title: v.string(), + content: v.string(), + authorId: v.string(), + publishDate: v.optional(v.date()), + metadata: v.object({ + views: v.pipe(v.number(), v.integer(), v.minValue(0)), + likes: v.pipe(v.number(), v.integer(), v.minValue(0)), + category: v.string(), + }), +}); +export type BlogPost = v.InferOutput; + +// 6. Configuration Schema +export const configSchema = v.object({ + apiKey: v.string(), + timeoutMs: v.optional(v.pipe(v.number(), v.integer(), v.minValue(1)), 5000), + retries: v.pipe(v.number(), v.integer(), v.minValue(0), v.maxValue(5)), + featureFlags: v.object({ + betaFeature: v.optional(v.boolean()), + newUI: v.optional(v.boolean(), false), + }), + logLevel: v.optional(v.picklist(["debug", "info", "warn", "error"])), +}); +export type Config = v.InferOutput; +export type ConfigInput = v.InferInput; + +// 7. Event Schema +export const eventSchema = v.object({ + eventId: v.string(), + eventName: v.string(), + timestamp: v.number(), + payload: v.unknown(), + source: v.picklist(["web", "mobile", "server"]), +}); +export type Event = v.InferOutput; + +// 8. Image Metadata Schema +export const imageMetadataSchema = v.object({ + url: v.pipe(v.string(), v.url()), + width: v.pipe(v.number(), v.integer(), v.minValue(1)), + height: v.pipe(v.number(), v.integer(), v.minValue(1)), + format: v.picklist(["jpeg", "png", "gif", "webp"]), + caption: v.optional(v.pipe(v.string(), v.maxLength(255))), +}); +export type ImageMetadata = v.InferOutput; + +// 9. Employee Schema +export const employeeSchema = v.object({ + employeeId: v.string(), + firstName: v.string(), + lastName: v.string(), + email: v.pipe(v.string(), v.email()), + department: v.string(), + startDate: v.date(), + isActive: v.boolean(), + managerId: v.optional(v.nullable(v.string())), +}); +export type Employee = v.InferOutput; + +// 10. Task Schema +export const taskSchema = v.object({ + taskId: v.string(), + description: v.string(), + dueDate: v.optional(v.date()), + priority: v.picklist(["low", "medium", "high"]), + completed: v.optional(v.boolean(), false), + assignee: v.nullable(employeeSchema), +}); +export type Task = v.InferOutput; +export type TaskInput = v.InferInput; diff --git a/src/schemas/zod3.ts b/src/schemas/zod3.ts new file mode 100644 index 0000000..6786128 --- /dev/null +++ b/src/schemas/zod3.ts @@ -0,0 +1,137 @@ +import { z } from "zod3"; + +// User Schema +export const userSchema = z.object({ + name: z.string(), + age: z.number().default(42), + phone: z + .union([z.string(), z.number()]) + .transform((phone) => + typeof phone === "number" ? phone.toString() : phone + ) + .default("123-456-7890"), +}); + +export type User = z.output; +export type UserInput = z.input; + +// 1. Product Schema +export const productSchema = z.object({ + id: z.string(), + name: z.string(), + price: z.number().positive(), + inStock: z.boolean(), + tags: z.array(z.string()), +}); +export type Product = z.infer; + +// 2. Order Schema +export const orderSchema = z.object({ + orderId: z.string().uuid(), + userId: z.string(), + // Need to define productSchema before referencing it here, which we did. + items: z.array(productSchema), + totalAmount: z.number(), + orderDate: z.date(), + status: z.enum(["pending", "shipped", "delivered", "cancelled"]), +}); +export type Order = z.infer; + +// 3. Address Schema +export const addressSchema = z.object({ + street: z.string(), + city: z.string(), + state: z.string(), + zipCode: z.string().regex(/^[0-9]{5}(?:-[0-9]{4})?$/), // US Zip code regex + country: z.string().default("USA"), +}); +export type Address = z.infer; + +// 4. Customer Schema +// Need to define userSchema and addressSchema first. +// Lazy evaluation needed if schemas referenced each other circularly, +// but here direct reference is fine. +export const customerSchema = z.object({ + customerId: z.string(), + basicInfo: userSchema, + shippingAddress: addressSchema, + billingAddress: addressSchema.nullable(), // Optional billing address + purchaseHistory: z.array(orderSchema).optional(), // Optional array of orders +}); +export type Customer = z.output; +export type CustomerInput = z.input; + +// 5. BlogPost Schema +export const blogPostSchema = z.object({ + postId: z.number().positive(), + title: z.string(), + content: z.string(), + authorId: z.string(), + publishDate: z.date().optional(), + metadata: z.object({ + views: z.number().int().nonnegative(), + likes: z.number().int().nonnegative(), + category: z.string(), + }), +}); +export type BlogPost = z.infer; + +// 6. Configuration Schema +export const configSchema = z.object({ + apiKey: z.string(), + timeoutMs: z.number().int().positive().default(5000), + retries: z.number().int().min(0).max(5), + featureFlags: z.object({ + betaFeature: z.boolean().optional(), // Optional boolean + newUI: z.boolean().default(false), + }), + logLevel: z.enum(["debug", "info", "warn", "error"]).optional(), // Optional literal +}); +export type Config = z.output; +export type ConfigInput = z.input; + +// 7. Event Schema +export const eventSchema = z.object({ + eventId: z.string(), + eventName: z.string(), + timestamp: z.number(), // Unix timestamp + payload: z.unknown(), // Can be anything + source: z.enum(["web", "mobile", "server"]), +}); +export type Event = z.infer; + +// 8. Image Metadata Schema +export const imageMetadataSchema = z.object({ + url: z.string().url(), + width: z.number().int().positive(), + height: z.number().int().positive(), + format: z.enum(["jpeg", "png", "gif", "webp"]), + caption: z.string().max(255).optional(), +}); +export type ImageMetadata = z.infer; + +// 9. Employee Schema +export const employeeSchema = z.object({ + employeeId: z.string(), + firstName: z.string(), + lastName: z.string(), + email: z.string().email(), + department: z.string(), + startDate: z.date(), + isActive: z.boolean(), + managerId: z.string().nullable().optional(), +}); +export type Employee = z.infer; + +// 10. Task Schema +// Need employeeSchema defined first. +export const taskSchema = z.object({ + taskId: z.string(), + description: z.string(), + dueDate: z.date().optional(), + priority: z.enum(["low", "medium", "high"]), + completed: z.boolean().default(false), + assignee: employeeSchema.nullable(), // Reference another schema +}); +export type Task = z.output; +export type TaskInput = z.input; diff --git a/src/schemas/zod4.ts b/src/schemas/zod4.ts new file mode 100644 index 0000000..55dc548 --- /dev/null +++ b/src/schemas/zod4.ts @@ -0,0 +1,137 @@ +import { z } from "zod4"; + +// User Schema +export const userSchema = z.object({ + name: z.string(), + age: z.number().default(42), + phone: z + .union([z.string(), z.number()]) + .transform((phone) => + typeof phone === "number" ? phone.toString() : phone + ) + .default("123-456-7890"), +}); + +export type User = z.output; +export type UserInput = z.input; + +// 1. Product Schema +export const productSchema = z.object({ + id: z.string(), + name: z.string(), + price: z.number().positive(), + inStock: z.boolean(), + tags: z.array(z.string()), +}); +export type Product = z.infer; + +// 2. Order Schema +export const orderSchema = z.object({ + orderId: z.string().uuid(), + userId: z.string(), + // Need to define productSchema before referencing it here, which we did. + items: z.array(productSchema), + totalAmount: z.number(), + orderDate: z.date(), + status: z.enum(["pending", "shipped", "delivered", "cancelled"]), +}); +export type Order = z.infer; + +// 3. Address Schema +export const addressSchema = z.object({ + street: z.string(), + city: z.string(), + state: z.string(), + zipCode: z.string().regex(/^[0-9]{5}(?:-[0-9]{4})?$/), // US Zip code regex + country: z.string().default("USA"), +}); +export type Address = z.infer; + +// 4. Customer Schema +// Need to define userSchema and addressSchema first. +// Lazy evaluation needed if schemas referenced each other circularly, +// but here direct reference is fine. +export const customerSchema = z.object({ + customerId: z.string(), + basicInfo: userSchema, + shippingAddress: addressSchema, + billingAddress: addressSchema.nullable(), // Optional billing address + purchaseHistory: z.array(orderSchema).optional(), // Optional array of orders +}); +export type Customer = z.output; +export type CustomerInput = z.input; + +// 5. BlogPost Schema +export const blogPostSchema = z.object({ + postId: z.number().positive(), + title: z.string(), + content: z.string(), + authorId: z.string(), + publishDate: z.date().optional(), + metadata: z.object({ + views: z.number().int().nonnegative(), + likes: z.number().int().nonnegative(), + category: z.string(), + }), +}); +export type BlogPost = z.infer; + +// 6. Configuration Schema +export const configSchema = z.object({ + apiKey: z.string(), + timeoutMs: z.number().int().positive().default(5000), + retries: z.number().int().min(0).max(5), + featureFlags: z.object({ + betaFeature: z.boolean().optional(), // Optional boolean + newUI: z.boolean().default(false), + }), + logLevel: z.enum(["debug", "info", "warn", "error"]).optional(), // Optional literal +}); +export type Config = z.output; +export type ConfigInput = z.input; + +// 7. Event Schema +export const eventSchema = z.object({ + eventId: z.string(), + eventName: z.string(), + timestamp: z.number(), // Unix timestamp + payload: z.unknown(), // Can be anything + source: z.enum(["web", "mobile", "server"]), +}); +export type Event = z.infer; + +// 8. Image Metadata Schema +export const imageMetadataSchema = z.object({ + url: z.string().url(), + width: z.number().int().positive(), + height: z.number().int().positive(), + format: z.enum(["jpeg", "png", "gif", "webp"]), + caption: z.string().max(255).optional(), +}); +export type ImageMetadata = z.infer; + +// 9. Employee Schema +export const employeeSchema = z.object({ + employeeId: z.string(), + firstName: z.string(), + lastName: z.string(), + email: z.string().email(), + department: z.string(), + startDate: z.date(), + isActive: z.boolean(), + managerId: z.string().nullable().optional(), +}); +export type Employee = z.infer; + +// 10. Task Schema +// Need employeeSchema defined first. +export const taskSchema = z.object({ + taskId: z.string(), + description: z.string(), + dueDate: z.date().optional(), + priority: z.enum(["low", "medium", "high"]), + completed: z.boolean().default(false), + assignee: employeeSchema.nullable(), // Reference another schema +}); +export type Task = z.output; +export type TaskInput = z.input; diff --git a/src/suretype.ts b/src/suretype.ts index 48e0c84..c1d6c8b 100644 --- a/src/suretype.ts +++ b/src/suretype.ts @@ -1,7 +1,16 @@ -import { type TypeOf, compile, v } from 'suretype' -import { checkUserA, checkUserB, checkUserC, logExecTime, nbIterations } from './utils' +import { heapStats } from "bun:jsc"; +import { type TypeOf, compile, v } from "suretype"; +import { + checkUserA, + checkUserB, + checkUserC, + logExecTime, + nbIterations, +} from "./utils"; -const startTime = performance.now() +Bun.write("./metrics/suretype.heap.before.json", JSON.stringify(heapStats())); + +const startTime = performance.now(); for (let i = 0; i < nbIterations; i++) { const userSchema = v.object({ @@ -9,45 +18,52 @@ for (let i = 0; i < nbIterations; i++) { age: v.number().default(42), // below work to have `phone?: string | number;` but then the default is not applied :/ // phone: v.anyOf([v.string().default("123-456-7890"), v.number().default(1234567890)]) - phone: v.string().default('123-456-7890'), - }) + phone: v.string().default("123-456-7890"), + }); // @ts-expect-error not used, it's ok :p // eslint-disable-next-line no-unused-vars type User = { - name: string - age: number - phone: string - } + name: string; + age: number; + phone: string; + }; - type UserInput = TypeOf + type UserInput = TypeOf; - const validator = compile(userSchema) - const ensureUser = compile(userSchema, { ensure: true, ajvOptions: { useDefaults: true } }) + const validator = compile(userSchema); + const ensureUser = compile(userSchema, { + ensure: true, + ajvOptions: { useDefaults: true }, + }); - const userA = ensureUser({ name: 'Jordan' }) + const userA = ensureUser({ name: "Jordan" }); // @ts-expect-error type mismatch - checkUserA(userA) + checkUserA(userA); function createUser(input: UserInput) { - if (typeof input.phone === 'number') { + if (typeof input.phone === "number") { // @ts-expect-error type mismatch because UserInput is not correct - input = { ...input, phone: input.phone.toString() } + input = { ...input, phone: input.phone.toString() }; } - const result = validator(input) - if (!result.ok) return userA - return ensureUser(input) + const result = validator(input); + if (!result.ok) return userA; + return ensureUser(input); } // @ts-expect-error type mismatch because UserInput is not correct - const userB = createUser({ name: 'Romain', age: 35, phone: 1234567890 }) + const userB = createUser({ name: "Romain", age: 35, phone: 1234567890 }); // @ts-expect-error type mismatch - checkUserB(userB) + checkUserB(userB); // @ts-expect-error age should be a number - const userC = createUser({ name: 'Romain', age: '35' }) + const userC = createUser({ name: "Romain", age: "35" }); // @ts-expect-error type mismatch - checkUserC(userC) + checkUserC(userC); } -logExecTime('Suretype', startTime) +logExecTime("Suretype", startTime); + +Bun.gc(true); + +Bun.write("./metrics/suretype.heap.after.json", JSON.stringify(heapStats())); diff --git a/src/valibot.ts b/src/valibot.ts index 98488bf..d829ea9 100644 --- a/src/valibot.ts +++ b/src/valibot.ts @@ -1,38 +1,65 @@ -import { type InferInput, type InferOutput, number, object, optional, parse, pipe, safeParse, string, transform, union } from 'valibot' -import { checkUserA, checkUserB, checkUserC, logExecTime, nbIterations } from './utils.ts' +import { heapStats } from "bun:jsc"; +import { + type InferInput, + type InferOutput, + number, + object, + optional, + parse, + pipe, + safeParse, + string, + transform, + union, +} from "valibot"; +import { + checkUserA, + checkUserB, + checkUserC, + logExecTime, + nbIterations, +} from "./utils.ts"; -const startTime = performance.now() +Bun.write("./metrics/valibot.heap.before.json", JSON.stringify(heapStats())); + +const startTime = performance.now(); for (let i = 0; i < nbIterations; i++) { const userSchema = object({ name: string(), age: optional(number(), 42), phone: pipe( - optional(union([string(), number()]), '123-456-7890'), - transform(phone => (typeof phone === 'number' ? phone.toString() : phone)), + optional(union([string(), number()]), "123-456-7890"), + transform((phone) => + typeof phone === "number" ? phone.toString() : phone + ) ), - }) + }); // @ts-expect-error not exported, it's ok :p - type User = InferOutput + type User = InferOutput; - type UserInput = InferInput + type UserInput = InferInput; - const userA = parse(userSchema, { name: 'Jordan' }) - checkUserA(userA) + const userA = parse(userSchema, { name: "Jordan" }); + checkUserA(userA); function createUser(input: UserInput) { - const result = safeParse(userSchema, input) - if (!result.success) return userA - return result.output + const result = safeParse(userSchema, input); + if (!result.success) return userA; + return result.output; } - const userB = createUser({ name: 'Romain', age: 35, phone: 1234567890 }) - checkUserB(userB) + const userB = createUser({ name: "Romain", age: 35, phone: 1234567890 }); + checkUserB(userB); // @ts-expect-error age should be a number - const userC = createUser({ name: 'Romain', age: '35' }) - checkUserC(userC) + const userC = createUser({ name: "Romain", age: "35" }); + checkUserC(userC); } -logExecTime('Valibot', startTime) +logExecTime("Valibot", startTime); + +Bun.gc(true); + +Bun.write("./metrics/valibot.heap.after.json", JSON.stringify(heapStats())); diff --git a/src/zod-mini.ts b/src/zod-mini.ts index 38daf7c..2a771f4 100644 --- a/src/zod-mini.ts +++ b/src/zod-mini.ts @@ -1,36 +1,65 @@ -import { type input as InferInput, type infer as InferOutput, _default, number, object, optional, refine, string } from '@zod/mini' -import { checkUserA, checkUserB, checkUserC, logExecTime, nbIterations } from './utils.ts' +import { heapStats } from "bun:jsc"; +import { + type input as InferInput, + type infer as InferOutput, + _default, + number, + object, + optional, + refine, + string, +} from "@zod/mini"; +import { + checkUserA, + checkUserB, + checkUserC, + logExecTime, + nbIterations, +} from "./utils.ts"; -const startTime = performance.now() +Bun.write("./metrics/zod-mini.heap.before.json", JSON.stringify(heapStats())); + +const startTime = performance.now(); for (let i = 0; i < nbIterations; i++) { const userSchema = object({ name: string(), age: _default(optional(number()), 42), - phone: _default(optional(string().check(refine(phone => (typeof phone === 'number' ? String(phone) : phone)))), '123-456-7890'), - }) + phone: _default( + optional( + string().check( + refine((phone) => (typeof phone === "number" ? String(phone) : phone)) + ) + ), + "123-456-7890" + ), + }); // @ts-expect-error not exported, it's ok :p - type User = InferOutput + type User = InferOutput; - type UserInput = InferInput + type UserInput = InferInput; - const userA = userSchema.parse({ name: 'Jordan' }) - checkUserA(userA) + const userA = userSchema.parse({ name: "Jordan" }); + checkUserA(userA); function createUser(input: UserInput) { - const result = userSchema.safeParse(input) - if (!result.success) return userA - return result.data + const result = userSchema.safeParse(input); + if (!result.success) return userA; + return result.data; } // (╯°□°)╯︵ ┻━┻ // Zod mini doesn't support "or" so a string is given for phone instead of a number like in other scripts - const userB = createUser({ name: 'Romain', age: 35, phone: '1234567890' }) - checkUserB(userB) + const userB = createUser({ name: "Romain", age: 35, phone: "1234567890" }); + checkUserB(userB); // @ts-expect-error age should be a number - const userC = createUser({ name: 'Romain', age: '35' }) - checkUserC(userC) + const userC = createUser({ name: "Romain", age: "35" }); + checkUserC(userC); } -logExecTime('Zod mini', startTime) +logExecTime("Zod mini", startTime); + +Bun.gc(true); + +Bun.write("./metrics/zod-mini.heap.after.json", JSON.stringify(heapStats())); diff --git a/src/zod3.ts b/src/zod3.ts index 18077fd..b1564e2 100644 --- a/src/zod3.ts +++ b/src/zod3.ts @@ -1,6 +1,9 @@ +import { heapStats } from 'bun:jsc' import { type input as InferInput, type infer as InferOutput, number, object, string } from 'zod3' import { checkUserA, checkUserB, checkUserC, logExecTime, nbIterations } from './utils.ts' +Bun.write('./metrics/zod3.heap.before.json', JSON.stringify(heapStats())) + const startTime = performance.now() for (let i = 0; i < nbIterations; i++) { @@ -36,3 +39,7 @@ for (let i = 0; i < nbIterations; i++) { } logExecTime('Zod v3', startTime) + +Bun.gc(true) + +Bun.write('./metrics/zod3.heap.after.json', JSON.stringify(heapStats())) diff --git a/src/zod4.ts b/src/zod4.ts index 3c29289..3941e08 100644 --- a/src/zod4.ts +++ b/src/zod4.ts @@ -1,6 +1,9 @@ +import { heapStats } from 'bun:jsc' import { type input as InferInput, type infer as InferOutput, number, object, string } from 'zod4' import { checkUserA, checkUserB, checkUserC, logExecTime, nbIterations } from './utils.ts' +Bun.write('./metrics/zod4.heap.before.json', JSON.stringify(heapStats())) + const startTime = performance.now() for (let i = 0; i < nbIterations; i++) { @@ -36,3 +39,7 @@ for (let i = 0; i < nbIterations; i++) { } logExecTime('Zod v4', startTime) + +Bun.gc(true) + +Bun.write('./metrics/zod4.heap.after.json', JSON.stringify(heapStats())) diff --git a/tsconfig.app.json b/tsconfig.app.json new file mode 100644 index 0000000..ca8b2c8 --- /dev/null +++ b/tsconfig.app.json @@ -0,0 +1,31 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "compilerOptions": { + "allowImportingTsExtensions": true, + "allowJs": true, + "erasableSyntaxOnly": true, + "forceConsistentCasingInFileNames": true, + "jsx": "react-jsx", + "lib": [ + "ESNext", + "DOM" + ], + "module": "ESNext", + "moduleDetection": "force", + "moduleResolution": "bundler", + "noEmit": true, + "noImplicitAny": true, + "noImplicitReturns": true, + "noPropertyAccessFromIndexSignature": false, + "outDir": "./dist", + "skipLibCheck": true, + "strict": true, + "target": "ESNext", + "verbatimModuleSyntax": true + }, + "extends": "@tsconfig/strictest/tsconfig.json", + "include": [ + "scripts", + "src" + ] +} \ No newline at end of file diff --git a/tsconfig.arktype.json b/tsconfig.arktype.json new file mode 100644 index 0000000..6a248c0 --- /dev/null +++ b/tsconfig.arktype.json @@ -0,0 +1,13 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "compilerOptions": { + "composite": true, + "emitDeclarationOnly": true, + "declaration": true, + "declarationDir": "./dist/types" + }, + "extends": "./tsconfig.json", + "include": [ + "src/schemas/arktype.ts" + ] +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index ca8b2c8..181001a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,9 +1,7 @@ { "$schema": "https://json.schemastore.org/tsconfig", "compilerOptions": { - "allowImportingTsExtensions": true, "allowJs": true, - "erasableSyntaxOnly": true, "forceConsistentCasingInFileNames": true, "jsx": "react-jsx", "lib": [ @@ -13,7 +11,6 @@ "module": "ESNext", "moduleDetection": "force", "moduleResolution": "bundler", - "noEmit": true, "noImplicitAny": true, "noImplicitReturns": true, "noPropertyAccessFromIndexSignature": false, @@ -24,8 +21,18 @@ "verbatimModuleSyntax": true }, "extends": "@tsconfig/strictest/tsconfig.json", - "include": [ - "scripts", - "src" + "references": [ + { + "path": "./tsconfig.arktype.json" + }, + { + "path": "./tsconfig.valibot.json" + }, + { + "path": "./tsconfig.zod3.json" + }, + { + "path": "./tsconfig.zod4.json" + } ] } \ No newline at end of file diff --git a/tsconfig.valibot.json b/tsconfig.valibot.json new file mode 100644 index 0000000..ab74b15 --- /dev/null +++ b/tsconfig.valibot.json @@ -0,0 +1,13 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "compilerOptions": { + "composite": true, + "emitDeclarationOnly": true, + "declaration": true, + "declarationDir": "./dist/types" + }, + "extends": "./tsconfig.json", + "include": [ + "src/schemas/valibot.ts" + ] +} \ No newline at end of file diff --git a/tsconfig.zod3.json b/tsconfig.zod3.json new file mode 100644 index 0000000..74eb394 --- /dev/null +++ b/tsconfig.zod3.json @@ -0,0 +1,13 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "compilerOptions": { + "composite": true, + "emitDeclarationOnly": true, + "declaration": true, + "declarationDir": "./dist/types" + }, + "extends": "./tsconfig.json", + "include": [ + "src/schemas/zod3.ts" + ] +} \ No newline at end of file diff --git a/tsconfig.zod4.json b/tsconfig.zod4.json new file mode 100644 index 0000000..638ab29 --- /dev/null +++ b/tsconfig.zod4.json @@ -0,0 +1,13 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "compilerOptions": { + "composite": true, + "emitDeclarationOnly": true, + "declaration": true, + "declarationDir": "./dist/types" + }, + "extends": "./tsconfig.json", + "include": [ + "src/schemas/zod4.ts" + ] +} \ No newline at end of file