diff --git a/attest.config.js b/attest.config.js new file mode 100644 index 00000000..e69de29b diff --git a/package-lock.json b/package-lock.json index 00d4857f..65ebcda5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@supabase/node-fetch": "^2.6.14" }, "devDependencies": { + "@ark/attest": "^0.49.0", "@types/jest": "^27.5.1", "chokidar-cli": "^3.0.0", "cpy-cli": "^5.0.0", @@ -23,6 +24,7 @@ "semantic-release-plugin-update-version-in-files": "^1.1.0", "ts-jest": "^28.0.3", "tstyche": "^4.3.0", + "tsx": "^4.20.5", "type-fest": "^4.32.0", "typedoc": "^0.22.16", "typescript": "^4.5.5", @@ -43,6 +45,68 @@ "node": ">=6.0.0" } }, + "node_modules/@ark/attest": { + "version": "0.49.0", + "resolved": "https://registry.npmjs.org/@ark/attest/-/attest-0.49.0.tgz", + "integrity": "sha512-LYAJe4iwgA4GY+WLcSZ2ObTgr7F9lSwoQm4hR+B5ko0TfB3gqolXv04hA+7UtoP5HrGR1lVS+0DdwtWNnaSGnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ark/fs": "0.49.0", + "@ark/util": "0.49.0", + "@prettier/sync": "0.5.5", + "@typescript/analyze-trace": "0.10.1", + "@typescript/vfs": "1.6.1", + "arktype": "2.1.22", + "prettier": "3.5.3" + }, + "bin": { + "attest": "out/cli/cli.js" + }, + "peerDependencies": { + "typescript": "*" + } + }, + "node_modules/@ark/attest/node_modules/prettier": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", + "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/@ark/fs": { + "version": "0.49.0", + "resolved": "https://registry.npmjs.org/@ark/fs/-/fs-0.49.0.tgz", + "integrity": "sha512-AEjAQS/bu1CGIRiKK/XLaQ73cSJHixfexq28wNt+kBpQ0h1RwVIVzaGsn/+5IWw6DEbR7LB+3hil5gzrzEeyZQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@ark/schema": { + "version": "0.49.0", + "resolved": "https://registry.npmjs.org/@ark/schema/-/schema-0.49.0.tgz", + "integrity": "sha512-GphZBLpW72iS0v4YkeUtV3YIno35Gimd7+ezbPO9GwEi9kzdUrPVjvf6aXSBAfHikaFc/9pqZOpv3pOXnC71tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ark/util": "0.49.0" + } + }, + "node_modules/@ark/util": { + "version": "0.49.0", + "resolved": "https://registry.npmjs.org/@ark/util/-/util-0.49.0.tgz", + "integrity": "sha512-/BtnX7oCjNkxi2vi6y1399b+9xd1jnCrDYhZ61f0a+3X8x8DxlK52VgEEzyuC2UQMPACIfYrmHkhD3lGt2GaMA==", + "dev": true, + "license": "MIT" + }, "node_modules/@babel/code-frame": { "version": "7.22.13", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", @@ -666,6 +730,448 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz", + "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz", + "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz", + "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz", + "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz", + "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz", + "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz", + "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz", + "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz", + "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz", + "integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz", + "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz", + "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz", + "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz", + "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz", + "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz", + "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz", + "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz", + "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz", + "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz", + "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz", + "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz", + "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz", + "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz", + "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz", + "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz", + "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -1084,6 +1590,22 @@ "node": ">= 8" } }, + "node_modules/@prettier/sync": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@prettier/sync/-/sync-0.5.5.tgz", + "integrity": "sha512-6BMtNr7aQhyNcGzmumkL0tgr1YQGfm9d7ZdmRpWqWuqpc9vZBind4xMe5NMiRECOhjuSiWHfBWLBnXkpeE90bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "make-synchronized": "^0.4.2" + }, + "funding": { + "url": "https://github.com/prettier/prettier-synchronized?sponsor=1" + }, + "peerDependencies": { + "prettier": "*" + } + }, "node_modules/@sinclair/typebox": { "version": "0.23.5", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.23.5.tgz", @@ -1254,6 +1776,76 @@ "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", "dev": true }, + "node_modules/@typescript/analyze-trace": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@typescript/analyze-trace/-/analyze-trace-0.10.1.tgz", + "integrity": "sha512-RnlSOPh14QbopGCApgkSx5UBgGda5MX1cHqp2fsqfiDyCwGL/m1jaeB9fzu7didVS81LQqGZZuxFBcg8YU8EVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2", + "exit": "^0.1.2", + "jsonparse": "^1.3.1", + "jsonstream-next": "^3.0.0", + "p-limit": "^3.1.0", + "split2": "^3.2.2", + "treeify": "^1.1.0", + "yargs": "^16.2.0" + }, + "bin": { + "analyze-trace": "bin/analyze-trace", + "print-trace-types": "bin/print-trace-types", + "simplify-trace-types": "bin/simplify-trace-types" + } + }, + "node_modules/@typescript/analyze-trace/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@typescript/analyze-trace/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript/vfs": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@typescript/vfs/-/vfs-1.6.1.tgz", + "integrity": "sha512-JwoxboBh7Oz1v38tPbkrZ62ZXNHAk9bJ7c9x0eI5zBfBnBYGhURdbnh7Z4smN/MV48Y5OCcZb58n972UtbazsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1" + }, + "peerDependencies": { + "typescript": "*" + } + }, "node_modules/aggregate-error": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-4.0.1.tgz", @@ -1345,6 +1937,17 @@ "sprintf-js": "~1.0.2" } }, + "node_modules/arktype": { + "version": "2.1.22", + "resolved": "https://registry.npmjs.org/arktype/-/arktype-2.1.22.tgz", + "integrity": "sha512-xdzl6WcAhrdahvRRnXaNwsipCgHuNoLobRqhiP8RjnfL9Gp947abGlo68GAIyLtxbD+MLzNyH2YR4kEqioMmYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ark/schema": "0.49.0", + "@ark/util": "0.49.0" + } + }, "node_modules/arrify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", @@ -2327,6 +2930,48 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/esbuild": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz", + "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.9", + "@esbuild/android-arm": "0.25.9", + "@esbuild/android-arm64": "0.25.9", + "@esbuild/android-x64": "0.25.9", + "@esbuild/darwin-arm64": "0.25.9", + "@esbuild/darwin-x64": "0.25.9", + "@esbuild/freebsd-arm64": "0.25.9", + "@esbuild/freebsd-x64": "0.25.9", + "@esbuild/linux-arm": "0.25.9", + "@esbuild/linux-arm64": "0.25.9", + "@esbuild/linux-ia32": "0.25.9", + "@esbuild/linux-loong64": "0.25.9", + "@esbuild/linux-mips64el": "0.25.9", + "@esbuild/linux-ppc64": "0.25.9", + "@esbuild/linux-riscv64": "0.25.9", + "@esbuild/linux-s390x": "0.25.9", + "@esbuild/linux-x64": "0.25.9", + "@esbuild/netbsd-arm64": "0.25.9", + "@esbuild/netbsd-x64": "0.25.9", + "@esbuild/openbsd-arm64": "0.25.9", + "@esbuild/openbsd-x64": "0.25.9", + "@esbuild/openharmony-arm64": "0.25.9", + "@esbuild/sunos-x64": "0.25.9", + "@esbuild/win32-arm64": "0.25.9", + "@esbuild/win32-ia32": "0.25.9", + "@esbuild/win32-x64": "0.25.9" + } + }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -2609,11 +3254,12 @@ "dev": true }, "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, "hasInstallScript": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -2667,6 +3313,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/get-tsconfig": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz", + "integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, "node_modules/glob": { "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", @@ -4034,6 +4693,33 @@ "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", "dev": true }, + "node_modules/jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "dev": true, + "engines": [ + "node >= 0.2.0" + ], + "license": "MIT" + }, + "node_modules/jsonstream-next": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/jsonstream-next/-/jsonstream-next-3.0.0.tgz", + "integrity": "sha512-aAi6oPhdt7BKyQn1SrIIGZBt0ukKuOUE1qV6kJ3GgioSOYzsRc8z9Hfr1BVmacA/jLe9nARfmgMGgn68BqIAgg==", + "dev": true, + "license": "(MIT OR Apache-2.0)", + "dependencies": { + "jsonparse": "^1.2.0", + "through2": "^4.0.2" + }, + "bin": { + "jsonstream-next": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/junk": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/junk/-/junk-4.0.1.tgz", @@ -4205,6 +4891,16 @@ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true }, + "node_modules/make-synchronized": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/make-synchronized/-/make-synchronized-0.4.2.tgz", + "integrity": "sha512-EwEJSg8gSGLicKXp/VzNi1tvzhdmNBxOzslkkJSoNUCQFZKH/NIUIp7xlfN+noaHrz4BJDN73gne8IHnjl/F/A==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/fisker/make-synchronized?sponsor=1" + } + }, "node_modules/makeerror": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", @@ -5125,6 +5821,21 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -5208,6 +5919,16 @@ "node": ">=8" } }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, "node_modules/resolve.exports": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz", @@ -5410,6 +6131,16 @@ "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", "dev": true }, + "node_modules/split2": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", + "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", + "dev": true, + "license": "ISC", + "dependencies": { + "readable-stream": "^3.0.0" + } + }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -5437,6 +6168,37 @@ "node": ">=8" } }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -5636,6 +6398,16 @@ "integrity": "sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w==", "dev": true }, + "node_modules/through2": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "3" + } + }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -5668,6 +6440,16 @@ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, + "node_modules/treeify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/treeify/-/treeify-1.1.0.tgz", + "integrity": "sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, "node_modules/trim-newlines": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-4.0.2.tgz", @@ -5759,6 +6541,26 @@ } } }, + "node_modules/tsx": { + "version": "4.20.5", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.5.tgz", + "integrity": "sha512-+wKjMNU9w/EaQayHXb7WA7ZaHY6hN8WgfvHNQ3t1PnU91/7O8TcTnIhCDYTZwnt8JsO9IBqZ30Ln1r7pPF52Aw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.25.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, "node_modules/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -5857,6 +6659,13 @@ "node": ">=4.2.0" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, "node_modules/v8-to-istanbul": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.0.tgz", @@ -6087,6 +6896,50 @@ "@jridgewell/trace-mapping": "^0.3.9" } }, + "@ark/attest": { + "version": "0.49.0", + "resolved": "https://registry.npmjs.org/@ark/attest/-/attest-0.49.0.tgz", + "integrity": "sha512-LYAJe4iwgA4GY+WLcSZ2ObTgr7F9lSwoQm4hR+B5ko0TfB3gqolXv04hA+7UtoP5HrGR1lVS+0DdwtWNnaSGnQ==", + "dev": true, + "requires": { + "@ark/fs": "0.49.0", + "@ark/util": "0.49.0", + "@prettier/sync": "0.5.5", + "@typescript/analyze-trace": "0.10.1", + "@typescript/vfs": "1.6.1", + "arktype": "2.1.22", + "prettier": "3.5.3" + }, + "dependencies": { + "prettier": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", + "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", + "dev": true + } + } + }, + "@ark/fs": { + "version": "0.49.0", + "resolved": "https://registry.npmjs.org/@ark/fs/-/fs-0.49.0.tgz", + "integrity": "sha512-AEjAQS/bu1CGIRiKK/XLaQ73cSJHixfexq28wNt+kBpQ0h1RwVIVzaGsn/+5IWw6DEbR7LB+3hil5gzrzEeyZQ==", + "dev": true + }, + "@ark/schema": { + "version": "0.49.0", + "resolved": "https://registry.npmjs.org/@ark/schema/-/schema-0.49.0.tgz", + "integrity": "sha512-GphZBLpW72iS0v4YkeUtV3YIno35Gimd7+ezbPO9GwEi9kzdUrPVjvf6aXSBAfHikaFc/9pqZOpv3pOXnC71tw==", + "dev": true, + "requires": { + "@ark/util": "0.49.0" + } + }, + "@ark/util": { + "version": "0.49.0", + "resolved": "https://registry.npmjs.org/@ark/util/-/util-0.49.0.tgz", + "integrity": "sha512-/BtnX7oCjNkxi2vi6y1399b+9xd1jnCrDYhZ61f0a+3X8x8DxlK52VgEEzyuC2UQMPACIfYrmHkhD3lGt2GaMA==", + "dev": true + }, "@babel/code-frame": { "version": "7.22.13", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", @@ -6566,6 +7419,188 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "@esbuild/aix-ppc64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz", + "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==", + "dev": true, + "optional": true + }, + "@esbuild/android-arm": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz", + "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==", + "dev": true, + "optional": true + }, + "@esbuild/android-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz", + "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==", + "dev": true, + "optional": true + }, + "@esbuild/android-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz", + "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==", + "dev": true, + "optional": true + }, + "@esbuild/darwin-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz", + "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==", + "dev": true, + "optional": true + }, + "@esbuild/darwin-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz", + "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==", + "dev": true, + "optional": true + }, + "@esbuild/freebsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz", + "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==", + "dev": true, + "optional": true + }, + "@esbuild/freebsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz", + "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==", + "dev": true, + "optional": true + }, + "@esbuild/linux-arm": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz", + "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==", + "dev": true, + "optional": true + }, + "@esbuild/linux-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz", + "integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==", + "dev": true, + "optional": true + }, + "@esbuild/linux-ia32": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz", + "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==", + "dev": true, + "optional": true + }, + "@esbuild/linux-loong64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz", + "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-mips64el": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz", + "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==", + "dev": true, + "optional": true + }, + "@esbuild/linux-ppc64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz", + "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==", + "dev": true, + "optional": true + }, + "@esbuild/linux-riscv64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz", + "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==", + "dev": true, + "optional": true + }, + "@esbuild/linux-s390x": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz", + "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==", + "dev": true, + "optional": true + }, + "@esbuild/linux-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz", + "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==", + "dev": true, + "optional": true + }, + "@esbuild/netbsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz", + "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==", + "dev": true, + "optional": true + }, + "@esbuild/netbsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz", + "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==", + "dev": true, + "optional": true + }, + "@esbuild/openbsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz", + "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==", + "dev": true, + "optional": true + }, + "@esbuild/openbsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz", + "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==", + "dev": true, + "optional": true + }, + "@esbuild/openharmony-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz", + "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==", + "dev": true, + "optional": true + }, + "@esbuild/sunos-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz", + "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==", + "dev": true, + "optional": true + }, + "@esbuild/win32-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz", + "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==", + "dev": true, + "optional": true + }, + "@esbuild/win32-ia32": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz", + "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==", + "dev": true, + "optional": true + }, + "@esbuild/win32-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz", + "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==", + "dev": true, + "optional": true + }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -6895,6 +7930,15 @@ "fastq": "^1.6.0" } }, + "@prettier/sync": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@prettier/sync/-/sync-0.5.5.tgz", + "integrity": "sha512-6BMtNr7aQhyNcGzmumkL0tgr1YQGfm9d7ZdmRpWqWuqpc9vZBind4xMe5NMiRECOhjuSiWHfBWLBnXkpeE90bw==", + "dev": true, + "requires": { + "make-synchronized": "^0.4.2" + } + }, "@sinclair/typebox": { "version": "0.23.5", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.23.5.tgz", @@ -7062,6 +8106,57 @@ "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", "dev": true }, + "@typescript/analyze-trace": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@typescript/analyze-trace/-/analyze-trace-0.10.1.tgz", + "integrity": "sha512-RnlSOPh14QbopGCApgkSx5UBgGda5MX1cHqp2fsqfiDyCwGL/m1jaeB9fzu7didVS81LQqGZZuxFBcg8YU8EVw==", + "dev": true, + "requires": { + "chalk": "^4.1.2", + "exit": "^0.1.2", + "jsonparse": "^1.3.1", + "jsonstream-next": "^3.0.0", + "p-limit": "^3.1.0", + "split2": "^3.2.2", + "treeify": "^1.1.0", + "yargs": "^16.2.0" + }, + "dependencies": { + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + } + } + }, + "@typescript/vfs": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@typescript/vfs/-/vfs-1.6.1.tgz", + "integrity": "sha512-JwoxboBh7Oz1v38tPbkrZ62ZXNHAk9bJ7c9x0eI5zBfBnBYGhURdbnh7Z4smN/MV48Y5OCcZb58n972UtbazsA==", + "dev": true, + "requires": { + "debug": "^4.1.1" + } + }, "aggregate-error": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-4.0.1.tgz", @@ -7124,6 +8219,16 @@ "sprintf-js": "~1.0.2" } }, + "arktype": { + "version": "2.1.22", + "resolved": "https://registry.npmjs.org/arktype/-/arktype-2.1.22.tgz", + "integrity": "sha512-xdzl6WcAhrdahvRRnXaNwsipCgHuNoLobRqhiP8RjnfL9Gp947abGlo68GAIyLtxbD+MLzNyH2YR4kEqioMmYQ==", + "dev": true, + "requires": { + "@ark/schema": "0.49.0", + "@ark/util": "0.49.0" + } + }, "arrify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", @@ -7829,6 +8934,40 @@ "is-symbol": "^1.0.2" } }, + "esbuild": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz", + "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==", + "dev": true, + "requires": { + "@esbuild/aix-ppc64": "0.25.9", + "@esbuild/android-arm": "0.25.9", + "@esbuild/android-arm64": "0.25.9", + "@esbuild/android-x64": "0.25.9", + "@esbuild/darwin-arm64": "0.25.9", + "@esbuild/darwin-x64": "0.25.9", + "@esbuild/freebsd-arm64": "0.25.9", + "@esbuild/freebsd-x64": "0.25.9", + "@esbuild/linux-arm": "0.25.9", + "@esbuild/linux-arm64": "0.25.9", + "@esbuild/linux-ia32": "0.25.9", + "@esbuild/linux-loong64": "0.25.9", + "@esbuild/linux-mips64el": "0.25.9", + "@esbuild/linux-ppc64": "0.25.9", + "@esbuild/linux-riscv64": "0.25.9", + "@esbuild/linux-s390x": "0.25.9", + "@esbuild/linux-x64": "0.25.9", + "@esbuild/netbsd-arm64": "0.25.9", + "@esbuild/netbsd-x64": "0.25.9", + "@esbuild/openbsd-arm64": "0.25.9", + "@esbuild/openbsd-x64": "0.25.9", + "@esbuild/openharmony-arm64": "0.25.9", + "@esbuild/sunos-x64": "0.25.9", + "@esbuild/win32-arm64": "0.25.9", + "@esbuild/win32-ia32": "0.25.9", + "@esbuild/win32-x64": "0.25.9" + } + }, "escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -8045,9 +9184,9 @@ "dev": true }, "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, "optional": true }, @@ -8081,6 +9220,15 @@ "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "dev": true }, + "get-tsconfig": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz", + "integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==", + "dev": true, + "requires": { + "resolve-pkg-maps": "^1.0.0" + } + }, "glob": { "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", @@ -9105,6 +10253,22 @@ "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", "dev": true }, + "jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "dev": true + }, + "jsonstream-next": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/jsonstream-next/-/jsonstream-next-3.0.0.tgz", + "integrity": "sha512-aAi6oPhdt7BKyQn1SrIIGZBt0ukKuOUE1qV6kJ3GgioSOYzsRc8z9Hfr1BVmacA/jLe9nARfmgMGgn68BqIAgg==", + "dev": true, + "requires": { + "jsonparse": "^1.2.0", + "through2": "^4.0.2" + } + }, "junk": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/junk/-/junk-4.0.1.tgz", @@ -9236,6 +10400,12 @@ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true }, + "make-synchronized": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/make-synchronized/-/make-synchronized-0.4.2.tgz", + "integrity": "sha512-EwEJSg8gSGLicKXp/VzNi1tvzhdmNBxOzslkkJSoNUCQFZKH/NIUIp7xlfN+noaHrz4BJDN73gne8IHnjl/F/A==", + "dev": true + }, "makeerror": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", @@ -9884,6 +11054,17 @@ } } }, + "readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, "readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -9941,6 +11122,12 @@ "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true }, + "resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true + }, "resolve.exports": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz", @@ -10097,6 +11284,15 @@ "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", "dev": true }, + "split2": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", + "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", + "dev": true, + "requires": { + "readable-stream": "^3.0.0" + } + }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -10120,6 +11316,23 @@ } } }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "requires": { + "safe-buffer": "~5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } + } + }, "string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -10259,6 +11472,15 @@ "integrity": "sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w==", "dev": true }, + "through2": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", + "dev": true, + "requires": { + "readable-stream": "3" + } + }, "tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -10285,6 +11507,12 @@ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, + "treeify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/treeify/-/treeify-1.1.0.tgz", + "integrity": "sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A==", + "dev": true + }, "trim-newlines": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-4.0.2.tgz", @@ -10322,6 +11550,17 @@ "dev": true, "requires": {} }, + "tsx": { + "version": "4.20.5", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.5.tgz", + "integrity": "sha512-+wKjMNU9w/EaQayHXb7WA7ZaHY6hN8WgfvHNQ3t1PnU91/7O8TcTnIhCDYTZwnt8JsO9IBqZ30Ln1r7pPF52Aw==", + "dev": true, + "requires": { + "esbuild": "~0.25.0", + "fsevents": "~2.3.3", + "get-tsconfig": "^4.7.5" + } + }, "type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -10386,6 +11625,12 @@ "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", "dev": true }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, "v8-to-istanbul": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.0.tgz", diff --git a/package.json b/package.json index 6111da19..83f784dc 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,10 @@ "type-check:test": "tsc --noEmit --project tsconfig.test.json", "db:clean": "cd test/db && docker compose down --volumes", "db:run": "cd test/db && docker compose up --detach && wait-for-localhost 3000", - "db:generate-test-types": "cd test/db && docker compose up --detach && wait-for-localhost 8080 && curl --location 'http://0.0.0.0:8080/generators/typescript?included_schemas=public,personal&detect_one_to_one_relationships=true' > ../types.generated.ts && node ../scripts/update-json-type.js && prettier --write ../types.generated.ts" + "db:generate-test-types": "cd test/db && docker compose up --detach && wait-for-localhost 8080 && curl --location 'http://0.0.0.0:8080/generators/typescript?included_schemas=public,personal&detect_one_to_one_relationships=true' > ../types.generated.ts && node ../scripts/update-json-type.js && prettier --write ../types.generated.ts", + "benchmark:types": "tsx test/types-benchmark/benchmarks.ts", + "benchmark:update-baselines": "node test/types-benchmark/update-baselines.js --update", + "benchmark:show-baselines": "node test/types-benchmark/update-baselines.js --show" }, "dependencies": { "@supabase/node-fetch": "^2.6.14" @@ -66,6 +69,7 @@ "semantic-release-plugin-update-version-in-files": "^1.1.0", "ts-jest": "^28.0.3", "tstyche": "^4.3.0", + "tsx": "^4.20.5", "type-fest": "^4.32.0", "typedoc": "^0.22.16", "typescript": "^4.5.5", diff --git a/test/attest-test.ts b/test/attest-test.ts new file mode 100644 index 00000000..0519ecba --- /dev/null +++ b/test/attest-test.ts @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/types-benchmark/README.md b/test/types-benchmark/README.md new file mode 100644 index 00000000..9a82fbcd --- /dev/null +++ b/test/types-benchmark/README.md @@ -0,0 +1,47 @@ +# PostgREST-js Type Inference Benchmarks + +Type inference performance benchmarks using [@ark/attest](https://github.com/arktypeio/arktype/tree/main/ark/attest) based on the approach from [An approach to optimizing TypeScript type checking performance](https://www.geldata.com/blog/an-approach-to-optimizing-typescript-type-checking-performance). + +## Quick Start + +```bash +# Run benchmarks and see performance vs baseline +npm run benchmark:types + +# Update baselines (like vitest --update) +npm run benchmark:update-baselines + +# Show current baselines +npm run benchmark:show-baselines +``` + +## Files + +- **`benchmarks.ts`** - Core PostgREST operations (select, update, insert, RPC) +- **`baselines.json`** - Current baseline values for all benchmarks +- **`update-baselines.js`** - Baseline management utility +- **`tsconfig.json`** - TypeScript configuration for benchmarks + +## Understanding Results + +- **šŸ“Š Delta: 0.00%** - Performance is stable (no change) +- **šŸ“ˆ Exceeded baseline by X%** - Performance degraded (more instantiations) +- **šŸ“‰ Under baseline by X%** - Performance improved (fewer instantiations) + +## Updating Baselines + +When you make intentional changes that affect type inference performance: + +```bash +npm run benchmark:update-baselines +``` + +This captures current performance as the new baseline, similar to `vitest --update` for snapshots. + +## Current Baseline Values + +- **Simple Select: Basic**: 1,935 instantiations +- **Simple Select: Single Column**: 3,044 instantiations +- **Simple Update: Basic**: 108 instantiations +- **Simple Insert: Basic**: 108 instantiations +- **Simple RPC: Basic**: 53 instantiations diff --git a/test/types-benchmark/baselines.json b/test/types-benchmark/baselines.json new file mode 100644 index 00000000..5ff91584 --- /dev/null +++ b/test/types-benchmark/baselines.json @@ -0,0 +1,7 @@ +{ + "Simple Select: Basic": 1935, + "Simple Select: Single Column": 3044, + "Simple Update: Basic": 108, + "Simple Insert: Basic": 108, + "Simple RPC: Basic": 53 +} diff --git a/test/types-benchmark/benchmarks.ts b/test/types-benchmark/benchmarks.ts new file mode 100644 index 00000000..9d93a664 --- /dev/null +++ b/test/types-benchmark/benchmarks.ts @@ -0,0 +1,29 @@ +import { bench } from '@ark/attest' +import { PostgrestClient } from '../../src/index' +import { Database } from '../types.generated' + +// Create a mock client for type inference testing +const createMockClient = () => { + return new PostgrestClient('http://localhost:3000') +} + +// Simple benchmarks that should work +bench('Simple Select: Basic', () => { + return createMockClient().from('users').select() +}).types([1935, 'instantiations']) + +bench('Simple Select: Single Column', () => { + return createMockClient().from('users').select('username') +}).types([3044, 'instantiations']) + +bench('Simple Update: Basic', () => { + return createMockClient().from('users').update({ status: 'ONLINE' }) +}).types([108, 'instantiations']) + +bench('Simple Insert: Basic', () => { + return createMockClient().from('users').insert({ username: 'testuser' }) +}).types([108, 'instantiations']) + +bench('Simple RPC: Basic', () => { + return createMockClient().rpc('get_status', { name_param: 'test' }) +}).types([53, 'instantiations']) diff --git a/test/types-benchmark/run-type-benchmarks.ts b/test/types-benchmark/run-type-benchmarks.ts new file mode 100644 index 00000000..671927df --- /dev/null +++ b/test/types-benchmark/run-type-benchmarks.ts @@ -0,0 +1,318 @@ +#!/usr/bin/env tsx + +/** + * Type Inference Benchmark Runner for PostgREST-js + * + * This script runs comprehensive type inference benchmarks using @ark/attest + * and generates performance reports to help identify performance regressions + * and optimization opportunities. + * + * Based on the approach from: + * https://www.geldata.com/blog/an-approach-to-optimizing-typescript-type-checking-performance + */ + +import { execSync } from 'child_process' +import { writeFileSync, mkdirSync } from 'fs' +import { join } from 'path' + +interface BenchmarkResult { + name: string + instantiations: number + delta?: number + status: 'passed' | 'failed' | 'regression' +} + +interface BenchmarkSuite { + name: string + results: BenchmarkResult[] + totalInstantiations: number + averageInstantiations: number +} + +class TypeBenchmarkRunner { + private results: BenchmarkSuite[] = [] + private outputDir = join(__dirname, 'benchmark-results') + + constructor() { + this.setupOutputDirectory() + } + + private setupOutputDirectory() { + try { + mkdirSync(this.outputDir, { recursive: true }) + } catch (error) { + console.warn('Could not create output directory:', error) + } + } + + /** + * Run all benchmark suites + */ + async runAllBenchmarks() { + console.log('šŸš€ Starting PostgREST-js Type Inference Benchmarks') + console.log('='.repeat(60)) + + const suites = ['type-inference-benchmarks.ts', 'type-inference-benchmark-suite.ts'] + + for (const suite of suites) { + await this.runBenchmarkSuite(suite) + } + + this.generateReport() + } + + /** + * Run a specific benchmark suite using attest + */ + private async runBenchmarkSuite(suiteFile: string) { + console.log(`\nšŸ“Š Running benchmark suite: ${suiteFile}`) + + try { + // Use attest to run the benchmark suite + const command = `npx attest ${join(__dirname, suiteFile)}` + const output = execSync(command, { + cwd: process.cwd(), + encoding: 'utf-8', + stdio: 'pipe', + }) + + this.parseBenchmarkOutput(output, suiteFile) + } catch (error) { + console.error(`āŒ Failed to run benchmark suite ${suiteFile}:`, error) + } + } + + /** + * Parse attest benchmark output and extract results + */ + private parseBenchmarkOutput(output: string, suiteFile: string) { + try { + const lines = output.split('\n') + const suite: BenchmarkSuite = { + name: suiteFile.replace('.ts', ''), + results: [], + totalInstantiations: 0, + averageInstantiations: 0, + } + + let currentBenchmark: BenchmarkResult | null = null + + for (const line of lines) { + if (line.includes('šŸŒļø') && line.includes('instantiations')) { + // Extract benchmark name and instantiations + const match = line.match(/šŸŒļø\s+(.+?)\s+⛳\s+Result:\s+(\d+)\s+instantiations/) + if (match) { + if (currentBenchmark) { + suite.results.push(currentBenchmark) + } + currentBenchmark = { + name: match[1].trim(), + instantiations: parseInt(match[2]), + status: 'passed', + } + } + } else if (line.includes('šŸ“Š Delta:') && currentBenchmark) { + // Extract delta percentage + const deltaMatch = line.match(/šŸ“Š\s+Delta:\s+([+-]?\d+\.?\d*)%/) + if (deltaMatch) { + currentBenchmark.delta = parseFloat(deltaMatch[1]) + if (Math.abs(currentBenchmark.delta) > 20) { + currentBenchmark.status = currentBenchmark.delta > 0 ? 'regression' : 'passed' + } + } + } else if (line.includes('āŒ') && currentBenchmark) { + currentBenchmark.status = 'failed' + } + } + + if (currentBenchmark) { + suite.results.push(currentBenchmark) + } + + // Calculate totals + suite.totalInstantiations = suite.results.reduce((sum, r) => sum + r.instantiations, 0) + suite.averageInstantiations = suite.totalInstantiations / suite.results.length + + this.results.push(suite) + this.printSuiteResults(suite) + } catch (error) { + console.error('āŒ Failed to parse benchmark output:', error) + } + } + + /** + * Print results for a benchmark suite + */ + private printSuiteResults(suite: BenchmarkSuite) { + console.log(`\nšŸ“ˆ Results for ${suite.name}:`) + console.log(` Total Instantiations: ${suite.totalInstantiations}`) + console.log(` Average Instantiations: ${suite.averageInstantiations.toFixed(2)}`) + console.log(` Number of Tests: ${suite.results.length}`) + + const regressions = suite.results.filter((r) => r.status === 'regression') + const failures = suite.results.filter((r) => r.status === 'failed') + + if (regressions.length > 0) { + console.log(` āš ļø Performance Regressions: ${regressions.length}`) + regressions.forEach((r) => { + console.log(` - ${r.name}: +${r.delta}% (${r.instantiations} instantiations)`) + }) + } + + if (failures.length > 0) { + console.log(` āŒ Failures: ${failures.length}`) + failures.forEach((r) => { + console.log(` - ${r.name}`) + }) + } + + if (regressions.length === 0 && failures.length === 0) { + console.log(` āœ… All tests passed`) + } + } + + /** + * Generate comprehensive benchmark report + */ + private generateReport() { + console.log('\nšŸ“‹ Generating Benchmark Report') + console.log('='.repeat(60)) + + const report = this.generateMarkdownReport() + const jsonReport = this.generateJsonReport() + + const reportPath = join(this.outputDir, 'benchmark-report.md') + const jsonPath = join(this.outputDir, 'benchmark-results.json') + + writeFileSync(reportPath, report) + writeFileSync(jsonPath, JSON.stringify(jsonReport, null, 2)) + + console.log(`šŸ“„ Report generated: ${reportPath}`) + console.log(`šŸ“Š JSON data: ${jsonPath}`) + + this.printSummary() + } + + /** + * Generate markdown report + */ + private generateMarkdownReport(): string { + const timestamp = new Date().toISOString() + const totalSuites = this.results.length + const totalTests = this.results.reduce((sum, suite) => sum + suite.results.length, 0) + const totalRegressions = this.results.reduce( + (sum, suite) => sum + suite.results.filter((r) => r.status === 'regression').length, + 0 + ) + + let report = `# PostgREST-js Type Inference Benchmark Report\n\n` + report += `**Generated:** ${timestamp}\n` + report += `**Total Suites:** ${totalSuites}\n` + report += `**Total Tests:** ${totalTests}\n` + report += `**Performance Regressions:** ${totalRegressions}\n\n` + + report += `## Summary\n\n` + if (totalRegressions === 0) { + report += `āœ… **All benchmarks passed!** No performance regressions detected.\n\n` + } else { + report += `āš ļø **${totalRegressions} performance regressions detected.**\n\n` + } + + report += `## Detailed Results\n\n` + + this.results.forEach((suite) => { + report += `### ${suite.name}\n\n` + report += `- **Total Instantiations:** ${suite.totalInstantiations}\n` + report += `- **Average Instantiations:** ${suite.averageInstantiations.toFixed(2)}\n` + report += `- **Number of Tests:** ${suite.results.length}\n\n` + + const regressions = suite.results.filter((r) => r.status === 'regression') + if (regressions.length > 0) { + report += `#### Performance Regressions\n\n` + regressions.forEach((r) => { + report += `- **${r.name}:** +${r.delta}% (${r.instantiations} instantiations)\n` + }) + report += `\n` + } + + report += `#### All Results\n\n` + report += `| Test Name | Instantiations | Delta | Status |\n` + report += `|-----------|----------------|-------|--------|\n` + + suite.results.forEach((r) => { + const delta = r.delta ? `${r.delta > 0 ? '+' : ''}${r.delta}%` : '-' + const status = r.status === 'regression' ? 'āš ļø' : r.status === 'failed' ? 'āŒ' : 'āœ…' + report += `| ${r.name} | ${r.instantiations} | ${delta} | ${status} |\n` + }) + + report += `\n` + }) + + report += `## Recommendations\n\n` + if (totalRegressions > 0) { + report += `- Investigate the ${totalRegressions} performance regressions listed above\n` + report += `- Consider optimizing the most expensive type inference operations\n` + report += `- Review recent changes that might have impacted type performance\n` + } else { + report += `- Type inference performance is stable\n` + report += `- Continue monitoring for future regressions\n` + } + + return report + } + + /** + * Generate JSON report for programmatic access + */ + private generateJsonReport() { + return { + timestamp: new Date().toISOString(), + summary: { + totalSuites: this.results.length, + totalTests: this.results.reduce((sum, suite) => sum + suite.results.length, 0), + totalRegressions: this.results.reduce( + (sum, suite) => sum + suite.results.filter((r) => r.status === 'regression').length, + 0 + ), + totalInstantiations: this.results.reduce( + (sum, suite) => sum + suite.totalInstantiations, + 0 + ), + }, + suites: this.results, + } + } + + /** + * Print final summary + */ + private printSummary() { + const totalRegressions = this.results.reduce( + (sum, suite) => sum + suite.results.filter((r) => r.status === 'regression').length, + 0 + ) + + console.log('\nšŸŽÆ Benchmark Summary') + console.log('='.repeat(60)) + console.log(`Total Suites: ${this.results.length}`) + console.log( + `Total Tests: ${this.results.reduce((sum, suite) => sum + suite.results.length, 0)}` + ) + console.log(`Performance Regressions: ${totalRegressions}`) + + if (totalRegressions === 0) { + console.log('\nāœ… All benchmarks passed! No performance regressions detected.') + } else { + console.log(`\nāš ļø ${totalRegressions} performance regressions detected.`) + console.log('Check the detailed report for more information.') + } + } +} + +// Run benchmarks if this file is executed directly +if (require.main === module) { + const runner = new TypeBenchmarkRunner() + runner.runAllBenchmarks().catch(console.error) +} + +export { TypeBenchmarkRunner } diff --git a/test/types-benchmark/simple-benchmarks.ts b/test/types-benchmark/simple-benchmarks.ts new file mode 100644 index 00000000..92a7abf5 --- /dev/null +++ b/test/types-benchmark/simple-benchmarks.ts @@ -0,0 +1,29 @@ +import { bench } from '@ark/attest' +import { PostgrestClient } from '../../src/index' +import { Database } from '../types.generated' + +// Create a mock client for type inference testing +const createMockClient = () => { + return new PostgrestClient('http://localhost:3000') +} + +// Simple benchmarks that should work +bench('Simple Select: Basic', () => { + return createMockClient().from('users').select() +}).types([870, 'instantiations']) + +bench('Simple Select: Single Column', () => { + return createMockClient().from('users').select('username') +}).types([7, 'instantiations']) + +bench('Simple Update: Basic', () => { + return createMockClient().from('users').update({ status: 'ONLINE' }) +}).types([56, 'instantiations']) + +bench('Simple Insert: Basic', () => { + return createMockClient().from('users').insert({ username: 'testuser' }) +}).types([56, 'instantiations']) + +bench('Simple RPC: Basic', () => { + return createMockClient().rpc('get_status', { name_param: 'test' }) +}).types([53, 'instantiations']) diff --git a/test/types-benchmark/tsconfig.json b/test/types-benchmark/tsconfig.json new file mode 100644 index 00000000..761f3708 --- /dev/null +++ b/test/types-benchmark/tsconfig.json @@ -0,0 +1,14 @@ +{ + "include": ["**/*.ts", "../types.generated.ts", "../../src/**/*.ts"], + "compilerOptions": { + "target": "ES2015", + "downlevelIteration": true, + "noEmit": true, + "moduleResolution": "Node", + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "resolveJsonModule": true, + "strict": true, + "forceConsistentCasingInFileNames": true + } +} diff --git a/test/types-benchmark/type-inference-benchmark-suite.ts b/test/types-benchmark/type-inference-benchmark-suite.ts new file mode 100644 index 00000000..88e7a22e --- /dev/null +++ b/test/types-benchmark/type-inference-benchmark-suite.ts @@ -0,0 +1,322 @@ +import { bench } from '@ark/attest' +import { PostgrestClient } from '../src/index' +import { Database } from './types.generated' + +// Enhanced benchmarking suite for PostgREST-js type inference performance +// Based on the approach from: https://www.geldata.com/blog/an-approach-to-optimizing-typescript-type-checking-performance + +// Mock client factory for consistent testing +const createMockClient = () => { + return new PostgrestClient('http://localhost:3000') +} + +// Benchmark configuration following the article's approach +const config = { + iterations: 50, + warmup: 5, + threshold: 20, // 20% performance threshold +} + +// ============================================================================ +// BASELINE MEASUREMENT +// ============================================================================ + +bench.baseline(() => { + return createMockClient().from('users').select('username') +}) + +// ============================================================================ +// SCHEMA COMPLEXITY TESTS +// ============================================================================ + +// Small schema (1-2 tables) +bench('Schema Complexity: Small Schema', () => { + return createMockClient().from('users').select('username, status') +}).types([config.iterations, 'instantiations']) + +// Medium schema (3-5 tables) +bench('Schema Complexity: Medium Schema', () => { + return createMockClient() + .from('messages') + .select( + 'id, message, users!messages_username_fkey(username), channels!messages_channel_id_fkey(slug)' + ) +}).types([config.iterations, 'instantiations']) + +// Large schema (6+ tables with deep nesting) +bench('Schema Complexity: Large Schema', () => { + return createMockClient().from('messages').select(` + id, + message, + users!messages_username_fkey( + username, + status, + user_profiles(username), + best_friends!best_friends_first_user_fkey(second_user, users!best_friends_second_user_fkey(username)) + ), + channels!messages_channel_id_fkey( + slug, + channel_details(details) + ) + `) +}).types([config.iterations, 'instantiations']) + +// ============================================================================ +// OPERATION TYPE TESTS +// ============================================================================ + +// Select operations with varying complexity +bench('Operation Type: Select Basic', () => { + return createMockClient().from('users').select() +}).types([config.iterations, 'instantiations']) + +bench('Operation Type: Select With Relations', () => { + return createMockClient() + .from('best_friends') + .select('id, first_user, second_user, users!best_friends_first_user_fkey(username)') +}).types([config.iterations, 'instantiations']) + +bench('Operation Type: Select Deep Nesting', () => { + return createMockClient().from('messages').select(` + id, + message, + users!messages_username_fkey( + username, + status, + user_profiles(username), + best_friends!best_friends_first_user_fkey(second_user) + ), + channels!messages_channel_id_fkey( + slug, + channel_details(details) + ) + `) +}).types([config.iterations, 'instantiations']) + +// Update operations +bench('Operation Type: Update Simple', () => { + return createMockClient().from('users').update({ status: 'ONLINE' }) +}).types([config.iterations, 'instantiations']) + +bench('Operation Type: Update With Relations', () => { + return createMockClient().from('best_friends').update({ third_wheel: 'testuser' }) +}).types([config.iterations, 'instantiations']) + +// Insert operations +bench('Operation Type: Insert Simple', () => { + return createMockClient().from('users').insert({ username: 'testuser' }) +}).types([config.iterations, 'instantiations']) + +bench('Operation Type: Insert With Relations', () => { + return createMockClient() + .from('messages') + .insert({ message: 'test', username: 'testuser', channel_id: 1 }) +}).types([config.iterations, 'instantiations']) + +// RPC operations +bench('Operation Type: RPC Simple', () => { + return createMockClient().rpc('get_status', { name_param: 'test' }) +}).types([config.iterations, 'instantiations']) + +bench('Operation Type: RPC With Select', () => { + return createMockClient().rpc('get_status', { name_param: 'test' }).select('*') +}).types([config.iterations, 'instantiations']) + +// ============================================================================ +// RELATIONSHIP COMPLEXITY TESTS +// ============================================================================ + +// One-to-one relationships +bench('Relationship: One-to-One', () => { + return createMockClient() + .from('user_profiles') + .select('id, username, users!user_profiles_username_fkey(username, status)') +}).types([config.iterations, 'instantiations']) + +// One-to-many relationships +bench('Relationship: One-to-Many', () => { + return createMockClient() + .from('users') + .select('username, status, messages!messages_username_fkey(id, message)') +}).types([config.iterations, 'instantiations']) + +// Many-to-many relationships +bench('Relationship: Many-to-Many', () => { + return createMockClient().from('products').select(` + id, + name, + product_categories( + categories(name, description) + ) + `) +}).types([config.iterations, 'instantiations']) + +// Self-referencing relationships +bench('Relationship: Self-Referencing', () => { + return createMockClient() + .from('collections') + .select('id, description, parent_id, collections!collections_parent_id_fkey(id, description)') +}).types([config.iterations, 'instantiations']) + +// ============================================================================ +// AGGREGATE FUNCTION TESTS +// ============================================================================ + +bench('Aggregate: Single Count', () => { + return createMockClient().from('users').select('username.count()') +}).types([config.iterations, 'instantiations']) + +bench('Aggregate: Multiple Counts', () => { + return createMockClient().from('users').select('username.count(), status.count(), data.count()') +}).types([config.iterations, 'instantiations']) + +bench('Aggregate: With Relations', () => { + return createMockClient() + .from('messages') + .select('id.count(), users!messages_username_fkey(username.count())') +}).types([config.iterations, 'instantiations']) + +// ============================================================================ +// CASTING AND TYPE OPERATIONS +// ============================================================================ + +bench('Casting: Basic Casting', () => { + return createMockClient().from('users').select('username::text, status::text') +}).types([config.iterations, 'instantiations']) + +bench('Casting: Complex Casting', () => { + return createMockClient().from('users').select('username::text, status::text, data::jsonb') +}).types([config.iterations, 'instantiations']) + +bench('Casting: With Relations', () => { + return createMockClient() + .from('messages') + .select('id::text, message::text, users!messages_username_fkey(username::text)') +}).types([config.iterations, 'instantiations']) + +// ============================================================================ +// SPREAD OPERATIONS +// ============================================================================ + +bench('Spread: Basic Spread', () => { + return createMockClient().from('users').select('username, ...user_profiles(*)') +}).types([config.iterations, 'instantiations']) + +bench('Spread: Nested Spread', () => { + return createMockClient() + .from('users') + .select('username, ...user_profiles(username), ...best_friends(*)') +}).types([config.iterations, 'instantiations']) + +// ============================================================================ +// FILTER CHAIN TESTS +// ============================================================================ + +bench('Filter Chain: Simple Filters', () => { + return createMockClient().from('users').select('username, status').eq('status', 'ONLINE') +}).types([config.iterations, 'instantiations']) + +bench('Filter Chain: Complex Filters', () => { + return createMockClient() + .from('users') + .select('username, status') + .eq('status', 'ONLINE') + .gte('age_range', '[20,30)') +}).types([config.iterations, 'instantiations']) + +bench('Filter Chain: With Relations', () => { + return createMockClient() + .from('messages') + .select('id, message, users!messages_username_fkey(username)') + .eq('channel_id', 1) +}).types([config.iterations, 'instantiations']) + +// ============================================================================ +// STRESS TESTS +// ============================================================================ + +bench('Stress Test: Maximum Nesting', () => { + return createMockClient().from('messages').select(` + id, + message, + users!messages_username_fkey( + username, + status, + user_profiles(username), + best_friends!best_friends_first_user_fkey( + second_user, + users!best_friends_second_user_fkey( + username, + status, + user_profiles(username) + ) + ) + ), + channels!messages_channel_id_fkey( + slug, + channel_details(details) + ) + `) +}).types([config.iterations, 'instantiations']) + +bench('Stress Test: Maximum Aggregates', () => { + return createMockClient().from('users').select(` + username.count(), + status.count(), + data.count(), + messages!messages_username_fkey(id.count(), message.count()) + `) +}).types([config.iterations, 'instantiations']) + +bench('Stress Test: Maximum Casting', () => { + return createMockClient().from('users').select(` + username::text, + status::text, + data::jsonb, + messages!messages_username_fkey(id::text, message::text) + `) +}).types([config.iterations, 'instantiations']) + +// ============================================================================ +// PERFORMANCE REGRESSION TESTS +// ============================================================================ + +bench('Performance Regression: Deep Nesting', () => { + return createMockClient().from('messages').select(` + id, + message, + users!messages_username_fkey( + username, + status, + user_profiles(username), + best_friends!best_friends_first_user_fkey( + second_user, + users!best_friends_second_user_fkey(username, status) + ) + ), + channels!messages_channel_id_fkey( + slug, + channel_details(details) + ) + `) +}).types([config.iterations, 'instantiations']) + +bench('Performance Regression: Complex RPC with Select', () => { + return createMockClient() + .rpc('get_status', { name_param: 'test' }) + .select('username, status, data') +}).types([config.iterations, 'instantiations']) + +bench('Performance Regression: Many-to-Many with Aggregates', () => { + return createMockClient().from('products').select(` + id, + name, + product_categories.count(), + product_categories( + categories(name, description) + ) + `) +}).types([config.iterations, 'instantiations']) + +// Export configuration for use in other files +export { config } diff --git a/test/types-benchmark/type-inference-benchmarks.ts b/test/types-benchmark/type-inference-benchmarks.ts new file mode 100644 index 00000000..bd9c7f2b --- /dev/null +++ b/test/types-benchmark/type-inference-benchmarks.ts @@ -0,0 +1,215 @@ +import { bench } from '@ark/attest' +import { PostgrestClient } from '../src/index' +import { Database } from './types.generated' + +// Create a mock client for type inference testing +const createMockClient = () => { + return new PostgrestClient('http://localhost:3000') +} + +// ============================================================================ +// BASELINE MEASUREMENT +// ============================================================================ + +bench.baseline(() => { + return createMockClient().from('users').select('username') +}) + +// ============================================================================ +// SIMPLE OPERATIONS BENCHMARKS +// ============================================================================ + +bench('Simple Select: Basic', () => { + return createMockClient().from('users').select() +}).types([50, 'instantiations']) + +bench('Simple Select: Single Column', () => { + return createMockClient().from('users').select('username') +}).types([50, 'instantiations']) + +bench('Simple Select: Multiple Columns', () => { + return createMockClient().from('users').select('username, status, data') +}).types([50, 'instantiations']) + +bench('Simple Select: With Alias', () => { + return createMockClient().from('users').select('name:username, user_status:status') +}).types([50, 'instantiations']) + +bench('Simple Update: Basic', () => { + return createMockClient().from('users').update({ status: 'ONLINE' }) +}).types([50, 'instantiations']) + +bench('Simple Update: Multiple Fields', () => { + return createMockClient() + .from('users') + .update({ status: 'ONLINE', data: { test: true } }) +}).types([50, 'instantiations']) + +bench('Simple Insert: Basic', () => { + return createMockClient().from('users').insert({ username: 'testuser' }) +}).types([50, 'instantiations']) + +bench('Simple Insert: With Optional Fields', () => { + return createMockClient() + .from('users') + .insert({ username: 'testuser', status: 'ONLINE', data: { test: true } }) +}).types([50, 'instantiations']) + +bench('Simple RPC: Basic', () => { + return createMockClient().rpc('get_status', { name_param: 'test' }) +}).types([50, 'instantiations']) + +// ============================================================================ +// MEDIUM COMPLEXITY BENCHMARKS +// ============================================================================ + +bench('Medium Select: With Relations', () => { + return createMockClient() + .from('best_friends') + .select('id, first_user, second_user, users!best_friends_first_user_fkey(username, status)') +}).types([50, 'instantiations']) + +bench('Medium Select: Nested Relations', () => { + return createMockClient() + .from('messages') + .select( + 'id, message, users!messages_username_fkey(username), channels!messages_channel_id_fkey(slug)' + ) +}).types([50, 'instantiations']) + +bench('Medium Select: With Aggregates', () => { + return createMockClient().from('users').select('username.count(), status') +}).types([50, 'instantiations']) + +bench('Medium Select: Complex Aliases', () => { + return createMockClient() + .from('users') + .select('user_name:username, user_data:data, status_info:status') +}).types([50, 'instantiations']) + +bench('Medium Update: With Relations', () => { + return createMockClient().from('best_friends').update({ third_wheel: 'testuser' }) +}).types([50, 'instantiations']) + +bench('Medium Insert: With Relations', () => { + return createMockClient() + .from('messages') + .insert({ message: 'test', username: 'testuser', channel_id: 1 }) +}).types([50, 'instantiations']) + +// ============================================================================ +// HIGH COMPLEXITY BENCHMARKS +// ============================================================================ + +bench('Complex Select: Deep Nesting', () => { + return createMockClient().from('messages').select(` + id, + message, + users!messages_username_fkey( + username, + status, + user_profiles(username), + best_friends!best_friends_first_user_fkey(second_user, users!best_friends_second_user_fkey(username)) + ), + channels!messages_channel_id_fkey( + slug, + channel_details(details) + ) + `) +}).types([50, 'instantiations']) + +bench('Complex Select: Multiple Aggregates', () => { + return createMockClient().from('users').select('username.count(), status.count(), data.count()') +}).types([50, 'instantiations']) + +bench('Complex Select: Complex Casting', () => { + return createMockClient().from('users').select('username::text, status::text, data::jsonb') +}).types([50, 'instantiations']) + +bench('Complex Select: Spread Operations', () => { + return createMockClient().from('users').select('username, ...user_profiles(*)') +}).types([50, 'instantiations']) + +bench('Complex Update: Complex Relations', () => { + return createMockClient() + .from('best_friends') + .update({ third_wheel: 'testuser' }) + .eq('first_user', 'testuser') +}).types([50, 'instantiations']) + +// ============================================================================ +// EDGE CASES AND STRESS TESTS +// ============================================================================ + +bench('Edge Case Select: All Columns', () => { + return createMockClient().from('users').select('*') +}).types([50, 'instantiations']) + +bench('Edge Case Select: Empty Select', () => { + return createMockClient().from('users').select('') +}).types([50, 'instantiations']) + +bench('Edge Case Select: Self Referencing', () => { + return createMockClient() + .from('collections') + .select('id, description, parent_id, collections!collections_parent_id_fkey(id, description)') +}).types([50, 'instantiations']) + +bench('Edge Case Select: Many to Many', () => { + return createMockClient().from('products').select(` + id, + name, + product_categories( + categories(name, description) + ) + `) +}).types([50, 'instantiations']) + +bench('Edge Case RPC: With Select', () => { + return createMockClient().rpc('get_status', { name_param: 'test' }).select('*') +}).types([50, 'instantiations']) + +bench('Edge Case RPC: Complex RPC', () => { + return createMockClient().rpc('get_status', { name_param: 'test' }).select('username, status') +}).types([50, 'instantiations']) + +// ============================================================================ +// PERFORMANCE REGRESSION TESTS +// ============================================================================ + +bench('Performance Regression: Deep Nesting', () => { + return createMockClient().from('messages').select(` + id, + message, + users!messages_username_fkey( + username, + status, + user_profiles(username), + best_friends!best_friends_first_user_fkey( + second_user, + users!best_friends_second_user_fkey(username, status) + ) + ), + channels!messages_channel_id_fkey( + slug, + channel_details(details) + ) + `) +}).types([50, 'instantiations']) + +bench('Performance Regression: Complex RPC with Select', () => { + return createMockClient() + .rpc('get_status', { name_param: 'test' }) + .select('username, status, data') +}).types([50, 'instantiations']) + +bench('Performance Regression: Many-to-Many with Aggregates', () => { + return createMockClient().from('products').select(` + id, + name, + product_categories.count(), + product_categories( + categories(name, description) + ) + `) +}).types([50, 'instantiations']) diff --git a/test/types-benchmark/update-baselines.js b/test/types-benchmark/update-baselines.js new file mode 100755 index 00000000..e2417aad --- /dev/null +++ b/test/types-benchmark/update-baselines.js @@ -0,0 +1,98 @@ +#!/usr/bin/env node + +/** + * Simple baseline updater for PostgREST-js Type Inference Benchmarks + * Updates baselines.json with current benchmark results + */ + +const { execSync } = require('child_process') +const { writeFileSync, readFileSync } = require('fs') +const { join } = require('path') + +const baselinesPath = join(__dirname, 'baselines.json') + +function updateBaselines() { + console.log('šŸ”„ Updating PostgREST-js Type Inference Baselines') + console.log('='.repeat(60)) + + try { + // Run the benchmark and capture output + console.log('šŸ“Š Running benchmarks...') + const output = execSync('npx tsx test/types-benchmark/benchmarks.ts', { + cwd: process.cwd(), + encoding: 'utf-8', + stdio: 'pipe' + }) + + // Parse results + const baselines = {} + const lines = output.split('\n') + + for (let i = 0; i < lines.length; i++) { + const line = lines[i] + if (line.includes('šŸŒļø')) { + const nameMatch = line.match(/šŸŒļø\s+(.+?)$/) + const nextLine = lines[i + 1] + const instantiationsMatch = nextLine?.match(/⛳ Result:\s+(\d+)\s+instantiations/) + + if (nameMatch && instantiationsMatch) { + const name = nameMatch[1].trim() + const instantiations = parseInt(instantiationsMatch[1]) + baselines[name] = instantiations + } + } + } + + // Save baselines + writeFileSync(baselinesPath, JSON.stringify(baselines, null, 2)) + + console.log(`\nšŸ’¾ Updated ${Object.keys(baselines).length} baselines:`) + Object.entries(baselines).forEach(([name, value]) => { + console.log(` ${name}: ${value} instantiations`) + }) + + console.log('\nāœ… Baselines updated successfully!') + console.log('Run npm run benchmark:types to compare against new baselines') + + } catch (error) { + console.error('āŒ Failed to update baselines:', error.message) + process.exit(1) + } +} + +function showBaselines() { + try { + const baselines = JSON.parse(readFileSync(baselinesPath, 'utf-8')) + console.log('šŸ“Š Current Baselines') + console.log('='.repeat(60)) + + Object.entries(baselines).forEach(([name, value]) => { + console.log(`${name}: ${value} instantiations`) + }) + + console.log(`\nšŸ“ˆ Total: ${Object.keys(baselines).length} benchmarks`) + } catch (error) { + console.log('āŒ No baselines file found. Run with --update to create baselines.') + } +} + +// CLI interface +const args = process.argv.slice(2) + +if (args.includes('--update') || args.includes('-u')) { + updateBaselines() +} else if (args.includes('--show') || args.includes('-s')) { + showBaselines() +} else { + console.log(` +PostgREST-js Type Inference Baseline Manager + +Usage: + node update-baselines.js --update Update all baselines with current values + node update-baselines.js --show Show current baseline values + +Options: + -u, --update Update baselines (like vitest --update) + -s, --show Show current baselines +`) +} diff --git a/tsconfig.attest.json b/tsconfig.attest.json new file mode 100644 index 00000000..e69de29b