From b1d95721d8188e1cdd51511e813a88069a6d20a6 Mon Sep 17 00:00:00 2001 From: Dominic Ferrando Date: Tue, 4 Nov 2025 21:41:50 -0500 Subject: [PATCH] Format --- .prettierrc | 3 + .zed/settings.json | 0 bun.lock | 88 +- package.json | 56 +- src/app.d.ts | 14 +- src/app.html | 16 +- src/lib/api.ts | 7 +- src/lib/data/avg-weights.json | 208 +- src/lib/data/standards.json | 10614 +++++++++++----------- src/lib/server/app.ts | 318 +- src/lib/server/types.ts | 2 +- src/lib/services/calculator/main.ts | 477 +- src/lib/services/calculator/util.ts | 25 +- src/lib/services/commerce/printful.ts | 142 +- src/lib/services/commerce/sync.ts | 348 +- src/lib/services/commerce/util/misc.ts | 291 +- src/lib/services/commerce/util/types.ts | 454 +- src/lib/services/commerce/webflow.ts | 236 +- src/lib/test/scratch.ts | 23 +- src/lib/test/validate-products.ts | 44 +- src/routes/+page.server.ts | 8 +- src/routes/[...slugs]/+server.ts | 6 +- src/routes/calculator/+page.svelte | 552 +- src/routes/commerce/+page.server.ts | 20 +- svelte.config.js | 20 +- tsconfig.json | 37 +- vite.config.ts | 6 +- 27 files changed, 7204 insertions(+), 6811 deletions(-) create mode 100644 .prettierrc create mode 100644 .zed/settings.json diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..0a02bce --- /dev/null +++ b/.prettierrc @@ -0,0 +1,3 @@ +{ + "tabWidth": 4 +} diff --git a/.zed/settings.json b/.zed/settings.json new file mode 100644 index 0000000..e69de29 diff --git a/bun.lock b/bun.lock index 65002b4..518a511 100644 --- a/bun.lock +++ b/bun.lock @@ -6,16 +6,16 @@ "@elysiajs/cors": "^1.4.0", "@elysiajs/eden": "^1.4.4", "@tailwindcss/vite": "^4.1.14", - "daisyui": "^5.3.2", + "daisyui": "^5.3.7", "elysia": "^1.4.12", "memoirist": "^0.4.0", "tailwindcss": "^4.1.14", }, "devDependencies": { - "@sveltejs/kit": "^2.46.5", + "@sveltejs/kit": "^2.47.1", "@sveltejs/vite-plugin-svelte": "^6.2.1", "@types/bun": "^1.3.0", - "svelte": "^5.40.0", + "svelte": "^5.40.2", "svelte-adapter-bun": "^0.5.2", "svelte-check": "^4.3.3", "typescript": "^5.9.3", @@ -82,8 +82,6 @@ "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.11", "", { "os": "win32", "cpu": "x64" }, "sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA=="], - "@isaacs/fs-minipass": ["@isaacs/fs-minipass@4.0.1", "", { "dependencies": { "minipass": "^7.0.4" } }, "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w=="], - "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="], @@ -146,47 +144,47 @@ "@sveltejs/acorn-typescript": ["@sveltejs/acorn-typescript@1.0.6", "", { "peerDependencies": { "acorn": "^8.9.0" } }, "sha512-4awhxtMh4cx9blePWl10HRHj8Iivtqj+2QdDCSMDzxG+XKa9+VCNupQuCuvzEhYPzZSrX+0gC+0lHA/0fFKKQQ=="], - "@sveltejs/kit": ["@sveltejs/kit@2.47.1", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/cookie": "^0.6.0", "acorn": "^8.14.1", "cookie": "^0.6.0", "devalue": "^5.3.2", "esm-env": "^1.2.2", "kleur": "^4.1.5", "magic-string": "^0.30.5", "mrmime": "^2.0.0", "sade": "^1.8.1", "set-cookie-parser": "^2.6.0", "sirv": "^3.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0", "svelte": "^4.0.0 || ^5.0.0-next.0", "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["@opentelemetry/api"], "bin": { "svelte-kit": "svelte-kit.js" } }, "sha512-1v+MbMHxTi6ctQyxmz3owLKqZGaBHyx4EQqTdq/PvDswPFzw3WlqhrOKOh2ZzH23+XpQGEF9G+KDIgYJE+byvg=="], + "@sveltejs/kit": ["@sveltejs/kit@2.48.4", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/cookie": "^0.6.0", "acorn": "^8.14.1", "cookie": "^0.6.0", "devalue": "^5.3.2", "esm-env": "^1.2.2", "kleur": "^4.1.5", "magic-string": "^0.30.5", "mrmime": "^2.0.0", "sade": "^1.8.1", "set-cookie-parser": "^2.6.0", "sirv": "^3.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0", "svelte": "^4.0.0 || ^5.0.0-next.0", "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["@opentelemetry/api"], "bin": { "svelte-kit": "svelte-kit.js" } }, "sha512-TGFX1pZUt9qqY20Cv5NyYvy0iLWHf2jXi8s+eCGsig7jQMdwZWKUFMR6TbvFNhfDSUpc1sH/Y5EHv20g3HHA3g=="], "@sveltejs/vite-plugin-svelte": ["@sveltejs/vite-plugin-svelte@6.2.1", "", { "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^5.0.0", "debug": "^4.4.1", "deepmerge": "^4.3.1", "magic-string": "^0.30.17", "vitefu": "^1.1.1" }, "peerDependencies": { "svelte": "^5.0.0", "vite": "^6.3.0 || ^7.0.0" } }, "sha512-YZs/OSKOQAQCnJvM/P+F1URotNnYNeU3P2s4oIpzm1uFaqUEqRxUB0g5ejMjEb5Gjb9/PiBI5Ktrq4rUUF8UVQ=="], "@sveltejs/vite-plugin-svelte-inspector": ["@sveltejs/vite-plugin-svelte-inspector@5.0.1", "", { "dependencies": { "debug": "^4.4.1" }, "peerDependencies": { "@sveltejs/vite-plugin-svelte": "^6.0.0-next.0", "svelte": "^5.0.0", "vite": "^6.3.0 || ^7.0.0" } }, "sha512-ubWshlMk4bc8mkwWbg6vNvCeT7lGQojE3ijDh3QTR6Zr/R+GXxsGbyH4PExEPpiFmqPhYiVSVmHBjUcVc1JIrA=="], - "@tailwindcss/node": ["@tailwindcss/node@4.1.14", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "enhanced-resolve": "^5.18.3", "jiti": "^2.6.0", "lightningcss": "1.30.1", "magic-string": "^0.30.19", "source-map-js": "^1.2.1", "tailwindcss": "4.1.14" } }, "sha512-hpz+8vFk3Ic2xssIA3e01R6jkmsAhvkQdXlEbRTk6S10xDAtiQiM3FyvZVGsucefq764euO/b8WUW9ysLdThHw=="], + "@tailwindcss/node": ["@tailwindcss/node@4.1.16", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "enhanced-resolve": "^5.18.3", "jiti": "^2.6.1", "lightningcss": "1.30.2", "magic-string": "^0.30.19", "source-map-js": "^1.2.1", "tailwindcss": "4.1.16" } }, "sha512-BX5iaSsloNuvKNHRN3k2RcCuTEgASTo77mofW0vmeHkfrDWaoFAFvNHpEgtu0eqyypcyiBkDWzSMxJhp3AUVcw=="], - "@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.14", "", { "dependencies": { "detect-libc": "^2.0.4", "tar": "^7.5.1" }, "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.14", "@tailwindcss/oxide-darwin-arm64": "4.1.14", "@tailwindcss/oxide-darwin-x64": "4.1.14", "@tailwindcss/oxide-freebsd-x64": "4.1.14", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.14", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.14", "@tailwindcss/oxide-linux-arm64-musl": "4.1.14", "@tailwindcss/oxide-linux-x64-gnu": "4.1.14", "@tailwindcss/oxide-linux-x64-musl": "4.1.14", "@tailwindcss/oxide-wasm32-wasi": "4.1.14", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.14", "@tailwindcss/oxide-win32-x64-msvc": "4.1.14" } }, "sha512-23yx+VUbBwCg2x5XWdB8+1lkPajzLmALEfMb51zZUBYaYVPDQvBSD/WYDqiVyBIo2BZFa3yw1Rpy3G2Jp+K0dw=="], + "@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.16", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.16", "@tailwindcss/oxide-darwin-arm64": "4.1.16", "@tailwindcss/oxide-darwin-x64": "4.1.16", "@tailwindcss/oxide-freebsd-x64": "4.1.16", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.16", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.16", "@tailwindcss/oxide-linux-arm64-musl": "4.1.16", "@tailwindcss/oxide-linux-x64-gnu": "4.1.16", "@tailwindcss/oxide-linux-x64-musl": "4.1.16", "@tailwindcss/oxide-wasm32-wasi": "4.1.16", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.16", "@tailwindcss/oxide-win32-x64-msvc": "4.1.16" } }, "sha512-2OSv52FRuhdlgyOQqgtQHuCgXnS8nFSYRp2tJ+4WZXKgTxqPy7SMSls8c3mPT5pkZ17SBToGM5LHEJBO7miEdg=="], - "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.14", "", { "os": "android", "cpu": "arm64" }, "sha512-a94ifZrGwMvbdeAxWoSuGcIl6/DOP5cdxagid7xJv6bwFp3oebp7y2ImYsnZBMTwjn5Ev5xESvS3FFYUGgPODQ=="], + "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.16", "", { "os": "android", "cpu": "arm64" }, "sha512-8+ctzkjHgwDJ5caq9IqRSgsP70xhdhJvm+oueS/yhD5ixLhqTw9fSL1OurzMUhBwE5zK26FXLCz2f/RtkISqHA=="], - "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.14", "", { "os": "darwin", "cpu": "arm64" }, "sha512-HkFP/CqfSh09xCnrPJA7jud7hij5ahKyWomrC3oiO2U9i0UjP17o9pJbxUN0IJ471GTQQmzwhp0DEcpbp4MZTA=="], + "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.16", "", { "os": "darwin", "cpu": "arm64" }, "sha512-C3oZy5042v2FOALBZtY0JTDnGNdS6w7DxL/odvSny17ORUnaRKhyTse8xYi3yKGyfnTUOdavRCdmc8QqJYwFKA=="], - "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.14", "", { "os": "darwin", "cpu": "x64" }, "sha512-eVNaWmCgdLf5iv6Qd3s7JI5SEFBFRtfm6W0mphJYXgvnDEAZ5sZzqmI06bK6xo0IErDHdTA5/t7d4eTfWbWOFw=="], + "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.16", "", { "os": "darwin", "cpu": "x64" }, "sha512-vjrl/1Ub9+JwU6BP0emgipGjowzYZMjbWCDqwA2Z4vCa+HBSpP4v6U2ddejcHsolsYxwL5r4bPNoamlV0xDdLg=="], - "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.14", "", { "os": "freebsd", "cpu": "x64" }, "sha512-QWLoRXNikEuqtNb0dhQN6wsSVVjX6dmUFzuuiL09ZeXju25dsei2uIPl71y2Ic6QbNBsB4scwBoFnlBfabHkEw=="], + "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.16", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TSMpPYpQLm+aR1wW5rKuUuEruc/oOX3C7H0BTnPDn7W/eMw8W+MRMpiypKMkXZfwH8wqPIRKppuZoedTtNj2tg=="], - "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.14", "", { "os": "linux", "cpu": "arm" }, "sha512-VB4gjQni9+F0VCASU+L8zSIyjrLLsy03sjcR3bM0V2g4SNamo0FakZFKyUQ96ZVwGK4CaJsc9zd/obQy74o0Fw=="], + "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.16", "", { "os": "linux", "cpu": "arm" }, "sha512-p0GGfRg/w0sdsFKBjMYvvKIiKy/LNWLWgV/plR4lUgrsxFAoQBFrXkZ4C0w8IOXfslB9vHK/JGASWD2IefIpvw=="], - "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.14", "", { "os": "linux", "cpu": "arm64" }, "sha512-qaEy0dIZ6d9vyLnmeg24yzA8XuEAD9WjpM5nIM1sUgQ/Zv7cVkharPDQcmm/t/TvXoKo/0knI3me3AGfdx6w1w=="], + "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.16", "", { "os": "linux", "cpu": "arm64" }, "sha512-DoixyMmTNO19rwRPdqviTrG1rYzpxgyYJl8RgQvdAQUzxC1ToLRqtNJpU/ATURSKgIg6uerPw2feW0aS8SNr/w=="], - "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.14", "", { "os": "linux", "cpu": "arm64" }, "sha512-ISZjT44s59O8xKsPEIesiIydMG/sCXoMBCqsphDm/WcbnuWLxxb+GcvSIIA5NjUw6F8Tex7s5/LM2yDy8RqYBQ=="], + "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.16", "", { "os": "linux", "cpu": "arm64" }, "sha512-H81UXMa9hJhWhaAUca6bU2wm5RRFpuHImrwXBUvPbYb+3jo32I9VIwpOX6hms0fPmA6f2pGVlybO6qU8pF4fzQ=="], - "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.14", "", { "os": "linux", "cpu": "x64" }, "sha512-02c6JhLPJj10L2caH4U0zF8Hji4dOeahmuMl23stk0MU1wfd1OraE7rOloidSF8W5JTHkFdVo/O7uRUJJnUAJg=="], + "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.16", "", { "os": "linux", "cpu": "x64" }, "sha512-ZGHQxDtFC2/ruo7t99Qo2TTIvOERULPl5l0K1g0oK6b5PGqjYMga+FcY1wIUnrUxY56h28FxybtDEla+ICOyew=="], - "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.14", "", { "os": "linux", "cpu": "x64" }, "sha512-TNGeLiN1XS66kQhxHG/7wMeQDOoL0S33x9BgmydbrWAb9Qw0KYdd8o1ifx4HOGDWhVmJ+Ul+JQ7lyknQFilO3Q=="], + "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.16", "", { "os": "linux", "cpu": "x64" }, "sha512-Oi1tAaa0rcKf1Og9MzKeINZzMLPbhxvm7rno5/zuP1WYmpiG0bEHq4AcRUiG2165/WUzvxkW4XDYCscZWbTLZw=="], - "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.14", "", { "dependencies": { "@emnapi/core": "^1.5.0", "@emnapi/runtime": "^1.5.0", "@emnapi/wasi-threads": "^1.1.0", "@napi-rs/wasm-runtime": "^1.0.5", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.4.0" }, "cpu": "none" }, "sha512-uZYAsaW/jS/IYkd6EWPJKW/NlPNSkWkBlaeVBi/WsFQNP05/bzkebUL8FH1pdsqx4f2fH/bWFcUABOM9nfiJkQ=="], + "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.16", "", { "dependencies": { "@emnapi/core": "^1.5.0", "@emnapi/runtime": "^1.5.0", "@emnapi/wasi-threads": "^1.1.0", "@napi-rs/wasm-runtime": "^1.0.7", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.4.0" }, "cpu": "none" }, "sha512-B01u/b8LteGRwucIBmCQ07FVXLzImWESAIMcUU6nvFt/tYsQ6IHz8DmZ5KtvmwxD+iTYBtM1xwoGXswnlu9v0Q=="], - "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.14", "", { "os": "win32", "cpu": "arm64" }, "sha512-Az0RnnkcvRqsuoLH2Z4n3JfAef0wElgzHD5Aky/e+0tBUxUhIeIqFBTMNQvmMRSP15fWwmvjBxZ3Q8RhsDnxAA=="], + "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.16", "", { "os": "win32", "cpu": "arm64" }, "sha512-zX+Q8sSkGj6HKRTMJXuPvOcP8XfYON24zJBRPlszcH1Np7xuHXhWn8qfFjIujVzvH3BHU+16jBXwgpl20i+v9A=="], - "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.14", "", { "os": "win32", "cpu": "x64" }, "sha512-ttblVGHgf68kEE4om1n/n44I0yGPkCPbLsqzjvybhpwa6mKKtgFfAzy6btc3HRmuW7nHe0OOrSeNP9sQmmH9XA=="], + "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.16", "", { "os": "win32", "cpu": "x64" }, "sha512-m5dDFJUEejbFqP+UXVstd4W/wnxA4F61q8SoL+mqTypId2T2ZpuxosNSgowiCnLp2+Z+rivdU0AqpfgiD7yCBg=="], - "@tailwindcss/vite": ["@tailwindcss/vite@4.1.14", "", { "dependencies": { "@tailwindcss/node": "4.1.14", "@tailwindcss/oxide": "4.1.14", "tailwindcss": "4.1.14" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7" } }, "sha512-BoFUoU0XqgCUS1UXWhmDJroKKhNXeDzD7/XwabjkDIAbMnc4ULn5e2FuEuBbhZ6ENZoSYzKlzvZ44Yr6EUDUSA=="], + "@tailwindcss/vite": ["@tailwindcss/vite@4.1.16", "", { "dependencies": { "@tailwindcss/node": "4.1.16", "@tailwindcss/oxide": "4.1.16", "tailwindcss": "4.1.16" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7" } }, "sha512-bbguNBcDxsRmi9nnlWJxhfDWamY3lmcyACHcdO1crxfzuLpOhHLLtEIN/nCbbAtj5rchUgQD17QVAKi1f7IsKg=="], "@tokenizer/inflate": ["@tokenizer/inflate@0.2.7", "", { "dependencies": { "debug": "^4.4.0", "fflate": "^0.8.2", "token-types": "^6.0.0" } }, "sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg=="], "@tokenizer/token": ["@tokenizer/token@0.3.0", "", {}, "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="], - "@types/bun": ["@types/bun@1.3.0", "", { "dependencies": { "bun-types": "1.3.0" } }, "sha512-+lAGCYjXjip2qY375xX/scJeVRmZ5cY0wyHYyCYxNcdEXrQ4AOe3gACgd4iQ8ksOslJtW4VNxBJ8llUwc3a6AA=="], + "@types/bun": ["@types/bun@1.3.1", "", { "dependencies": { "bun-types": "1.3.1" } }, "sha512-4jNMk2/K9YJtfqwoAa28c8wK+T7nvJFOjxI4h/7sORWcypRNxBpr+TPNaCfVWq70tLCJsqoFwcf0oI0JU/fvMQ=="], "@types/cookie": ["@types/cookie@0.6.0", "", {}, "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="], @@ -202,19 +200,17 @@ "axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="], - "bun-types": ["bun-types@1.3.0", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-u8X0thhx+yJ0KmkxuEo9HAtdfgCBaM/aI9K90VQcQioAmkVp3SG3FkwWGibUFz3WdXAdcsqOcbU40lK7tbHdkQ=="], + "bun-types": ["bun-types@1.3.1", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-NMrcy7smratanWJ2mMXdpatalovtxVggkj11bScuWuiOoXTiKIu2eVS1/7qbyI/4yHedtsn175n4Sm4JcdHLXw=="], "chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], - "chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="], - "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], "cookie": ["cookie@0.6.0", "", {}, "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw=="], "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], - "daisyui": ["daisyui@5.3.7", "", {}, "sha512-0+8PaSGift0HlIQABCeZzWOBV5Nx/vsI2TihB9hbaEyZENPlZZz+se2JnAH5rz9gBYTyDLB7NJup8hkREr6WBw=="], + "daisyui": ["daisyui@5.4.3", "", {}, "sha512-dfDCJnN4utErGoWfElgdEE252FtfHV9Mxj5Dq1+JzUq3nVkluWdF3JYykP0Xy/y/yArnPXYztO1tLNCow3kjmg=="], "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], @@ -224,7 +220,7 @@ "devalue": ["devalue@5.3.2", "", {}, "sha512-UDsjUbpQn9kvm68slnrs+mfxwFkIflOhkanmyabZ8zOYk8SMEIbJ3TK+88g70hSIeytu4y18f0z/hYHMTrXIWw=="], - "elysia": ["elysia@1.4.12", "", { "dependencies": { "cookie": "^1.0.2", "exact-mirror": "0.2.2", "fast-decode-uri-component": "^1.0.1" }, "peerDependencies": { "@sinclair/typebox": ">= 0.34.0 < 1", "file-type": ">= 20.0.0", "openapi-types": ">= 12.0.0", "typescript": ">= 5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-wbd0BkrobsjWSloIfYeF3f7G7rR0UWMa6tuLUhf6ZvwjiCEX3FVfhDsM+KaqqRRxkZpPDw42q4yIZlBLyE32ww=="], + "elysia": ["elysia@1.4.15", "", { "dependencies": { "cookie": "^1.0.2", "exact-mirror": "0.2.2", "fast-decode-uri-component": "^1.0.1", "memoirist": "^0.4.0" }, "peerDependencies": { "@sinclair/typebox": ">= 0.34.0 < 1", "@types/bun": ">= 1.2.0", "file-type": ">= 20.0.0", "openapi-types": ">= 12.0.0", "typescript": ">= 5.0.0" }, "optionalPeers": ["@types/bun", "typescript"] }, "sha512-RaDqqZdLuC4UJetfVRQ4Z5aVpGgEtQ+pZnsbI4ZzEaf3l/MzuHcqSVoL/Fue3d6qE4RV9HMB2rAZaHyPIxkyzg=="], "enhanced-resolve": ["enhanced-resolve@5.18.3", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww=="], @@ -260,27 +256,29 @@ "kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="], - "lightningcss": ["lightningcss@1.30.1", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-darwin-arm64": "1.30.1", "lightningcss-darwin-x64": "1.30.1", "lightningcss-freebsd-x64": "1.30.1", "lightningcss-linux-arm-gnueabihf": "1.30.1", "lightningcss-linux-arm64-gnu": "1.30.1", "lightningcss-linux-arm64-musl": "1.30.1", "lightningcss-linux-x64-gnu": "1.30.1", "lightningcss-linux-x64-musl": "1.30.1", "lightningcss-win32-arm64-msvc": "1.30.1", "lightningcss-win32-x64-msvc": "1.30.1" } }, "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg=="], + "lightningcss": ["lightningcss@1.30.2", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.30.2", "lightningcss-darwin-arm64": "1.30.2", "lightningcss-darwin-x64": "1.30.2", "lightningcss-freebsd-x64": "1.30.2", "lightningcss-linux-arm-gnueabihf": "1.30.2", "lightningcss-linux-arm64-gnu": "1.30.2", "lightningcss-linux-arm64-musl": "1.30.2", "lightningcss-linux-x64-gnu": "1.30.2", "lightningcss-linux-x64-musl": "1.30.2", "lightningcss-win32-arm64-msvc": "1.30.2", "lightningcss-win32-x64-msvc": "1.30.2" } }, "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ=="], - "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.30.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ=="], + "lightningcss-android-arm64": ["lightningcss-android-arm64@1.30.2", "", { "os": "android", "cpu": "arm64" }, "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A=="], - "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.30.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA=="], + "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.30.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA=="], - "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.30.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig=="], + "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.30.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ=="], - "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.30.1", "", { "os": "linux", "cpu": "arm" }, "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q=="], + "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.30.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA=="], - "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.30.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw=="], + "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.30.2", "", { "os": "linux", "cpu": "arm" }, "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA=="], - "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.30.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ=="], + "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.30.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A=="], - "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.30.1", "", { "os": "linux", "cpu": "x64" }, "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw=="], + "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.30.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA=="], - "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.30.1", "", { "os": "linux", "cpu": "x64" }, "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ=="], + "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.30.2", "", { "os": "linux", "cpu": "x64" }, "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w=="], - "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.30.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA=="], + "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.30.2", "", { "os": "linux", "cpu": "x64" }, "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA=="], - "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.30.1", "", { "os": "win32", "cpu": "x64" }, "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg=="], + "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.30.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ=="], + + "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.30.2", "", { "os": "win32", "cpu": "x64" }, "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw=="], "locate-character": ["locate-character@3.0.0", "", {}, "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="], @@ -288,10 +286,6 @@ "memoirist": ["memoirist@0.4.0", "", {}, "sha512-zxTgA0mSYELa66DimuNQDvyLq36AwDlTuVRbnQtB+VuTcKWm5Qc4z3WkSpgsFWHNhexqkIooqpv4hdcqrX5Nmg=="], - "minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], - - "minizlib": ["minizlib@3.1.0", "", { "dependencies": { "minipass": "^7.1.2" } }, "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw=="], - "mri": ["mri@1.2.0", "", {}, "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA=="], "mrmime": ["mrmime@2.0.1", "", {}, "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ=="], @@ -322,18 +316,16 @@ "strtok3": ["strtok3@10.3.4", "", { "dependencies": { "@tokenizer/token": "^0.3.0" } }, "sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg=="], - "svelte": ["svelte@5.40.2", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/estree": "^1.0.5", "acorn": "^8.12.1", "aria-query": "^5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", "esm-env": "^1.2.1", "esrap": "^2.1.0", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" } }, "sha512-wr/SwBVCVfeHU8FZr48vRrzSpWdBBzGo5mlErjGzeW4reJhK/CWutLZbk/eHwhKqO17ccjeTcvsqjrT4aK3wZA=="], + "svelte": ["svelte@5.43.3", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/estree": "^1.0.5", "acorn": "^8.12.1", "aria-query": "^5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", "esm-env": "^1.2.1", "esrap": "^2.1.0", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" } }, "sha512-kjkAjCk41mJfvJZG56XcJNOdJSke94JxtcX8zFzzz2vrt47E0LnoBzU6azIZ1aBxJgUep8qegAkguSf1GjxLXQ=="], "svelte-adapter-bun": ["svelte-adapter-bun@0.5.2", "", { "dependencies": { "tiny-glob": "^0.2.9" } }, "sha512-xEtFgaal6UgrCwwkSIcapO9kopoFNUYCYqyKCikdqxX9bz2TDYnrWQZ7qBnkunMxi1HOIERUCvTcebYGiarZLA=="], "svelte-check": ["svelte-check@4.3.3", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "chokidar": "^4.0.1", "fdir": "^6.2.0", "picocolors": "^1.0.0", "sade": "^1.7.4" }, "peerDependencies": { "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": ">=5.0.0" }, "bin": { "svelte-check": "bin/svelte-check" } }, "sha512-RYP0bEwenDXzfv0P1sKAwjZSlaRyqBn0Fz1TVni58lqyEiqgwztTpmodJrGzP6ZT2aHl4MbTvWP6gbmQ3FOnBg=="], - "tailwindcss": ["tailwindcss@4.1.14", "", {}, "sha512-b7pCxjGO98LnxVkKjaZSDeNuljC4ueKUddjENJOADtubtdo8llTaJy7HwBMeLNSSo2N5QIAgklslK1+Ir8r6CA=="], + "tailwindcss": ["tailwindcss@4.1.16", "", {}, "sha512-pONL5awpaQX4LN5eiv7moSiSPd/DLDzKVRJz8Q9PgzmAdd1R4307GQS2ZpfiN7ZmekdQrfhZZiSE5jkLR4WNaA=="], "tapable": ["tapable@2.3.0", "", {}, "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg=="], - "tar": ["tar@7.5.1", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.1.0", "yallist": "^5.0.0" } }, "sha512-nlGpxf+hv0v7GkWBK2V9spgactGOp0qvfWRxUMjqHyzrt3SgwE48DIv/FhqPHJYLHpgW1opq3nERbz5Anq7n1g=="], - "tiny-glob": ["tiny-glob@0.2.9", "", { "dependencies": { "globalyzer": "0.1.0", "globrex": "^0.1.2" } }, "sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg=="], "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], @@ -348,12 +340,10 @@ "undici-types": ["undici-types@7.14.0", "", {}, "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA=="], - "vite": ["vite@7.1.10", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-CmuvUBzVJ/e3HGxhg6cYk88NGgTnBoOo7ogtfJJ0fefUWAxN/WDSUa50o+oVBxuIhO8FoEZW0j2eW7sfjs5EtA=="], + "vite": ["vite@7.1.12", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug=="], "vitefu": ["vitefu@1.1.1", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["vite"] }, "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ=="], - "yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="], - "zimmerframe": ["zimmerframe@1.1.4", "", {}, "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ=="], "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.5.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg=="], diff --git a/package.json b/package.json index 4413fa1..60e504c 100644 --- a/package.json +++ b/package.json @@ -1,30 +1,30 @@ { - "type": "module", - "devDependencies": { - "@sveltejs/kit": "^2.47.1", - "@sveltejs/vite-plugin-svelte": "^6.2.1", - "@types/bun": "^1.3.0", - "svelte": "^5.40.2", - "svelte-adapter-bun": "^0.5.2", - "svelte-check": "^4.3.3", - "typescript": "^5.9.3", - "vite": "^7.1.10" - }, - "scripts": { - "dev": "vite dev", - "build": "vite build", - "preview": "vite preview", - "prepare": "svelte-kit sync || echo ''", - "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", - "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch" - }, - "dependencies": { - "@elysiajs/cors": "^1.4.0", - "@elysiajs/eden": "^1.4.4", - "@tailwindcss/vite": "^4.1.14", - "daisyui": "^5.3.7", - "elysia": "^1.4.12", - "memoirist": "^0.4.0", - "tailwindcss": "^4.1.14" - } + "type": "module", + "devDependencies": { + "@sveltejs/kit": "^2.48.4", + "@sveltejs/vite-plugin-svelte": "^6.2.1", + "@types/bun": "^1.3.1", + "svelte": "^5.43.3", + "svelte-adapter-bun": "^0.5.2", + "svelte-check": "^4.3.3", + "typescript": "^5.9.3", + "vite": "^7.1.12" + }, + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite preview", + "prepare": "svelte-kit sync || echo ''", + "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", + "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch" + }, + "dependencies": { + "@elysiajs/cors": "^1.4.0", + "@elysiajs/eden": "^1.4.4", + "@tailwindcss/vite": "^4.1.16", + "daisyui": "^5.4.3", + "elysia": "^1.4.15", + "memoirist": "^0.4.0", + "tailwindcss": "^4.1.16" + } } diff --git a/src/app.d.ts b/src/app.d.ts index da08e6d..d76242a 100644 --- a/src/app.d.ts +++ b/src/app.d.ts @@ -1,13 +1,13 @@ // See https://svelte.dev/docs/kit/types#app.d.ts // for information about these interfaces declare global { - namespace App { - // interface Error {} - // interface Locals {} - // interface PageData {} - // interface PageState {} - // interface Platform {} - } + namespace App { + // interface Error {} + // interface Locals {} + // interface PageData {} + // interface PageState {} + // interface Platform {} + } } export {}; diff --git a/src/app.html b/src/app.html index f273cc5..1966776 100644 --- a/src/app.html +++ b/src/app.html @@ -1,11 +1,11 @@ - - - - %sveltekit.head% - - -
%sveltekit.body%
- + + + + %sveltekit.head% + + +
%sveltekit.body%
+ diff --git a/src/lib/api.ts b/src/lib/api.ts index 446f7fc..407a28e 100644 --- a/src/lib/api.ts +++ b/src/lib/api.ts @@ -1,9 +1,8 @@ import { treaty } from "@elysiajs/eden"; import type { App } from "./server/types"; -const baseURL = - import.meta.env.PROD - ? "https://blade-and-brawn.fly.dev" - : "http://localhost:5173"; +const baseURL = import.meta.env.PROD + ? "https://blade-and-brawn.fly.dev" + : "http://localhost:5173"; export const api = treaty(baseURL).api; diff --git a/src/lib/data/avg-weights.json b/src/lib/data/avg-weights.json index 4187394..c26cdd8 100644 --- a/src/lib/data/avg-weights.json +++ b/src/lib/data/avg-weights.json @@ -1,107 +1,107 @@ { - "metadata": { - "unit": "kg" - }, - "weights": [ - { - "gender": "Male", - "age": 5, - "weight": 21.1 + "metadata": { + "unit": "kg" }, - { - "gender": "Male", - "age": 10, - "weight": 38.7 - }, - { - "gender": "Male", - "age": 15, - "weight": 66.1 - }, - { - "gender": "Male", - "age": 20, - "weight": 81.3 - }, - { - "gender": "Male", - "age": 30, - "weight": 89.7 - }, - { - "gender": "Male", - "age": 40, - "weight": 90.5 - }, - { - "gender": "Male", - "age": 50, - "weight": 89.6 - }, - { - "gender": "Male", - "age": 60, - "weight": 89.5 - }, - { - "gender": "Male", - "age": 70, - "weight": 89.3 - }, - { - "gender": "Male", - "age": 80, - "weight": 79.6 - }, - { - "gender": "Female", - "age": 5, - "weight": 21.0 - }, - { - "gender": "Female", - "age": 10, - "weight": 39.5 - }, - { - "gender": "Female", - "age": 15, - "weight": 58.1 - }, - { - "gender": "Female", - "age": 20, - "weight": 69.5 - }, - { - "gender": "Female", - "age": 30, - "weight": 73.3 - }, - { - "gender": "Female", - "age": 40, - "weight": 75.2 - }, - { - "gender": "Female", - "age": 50, - "weight": 74.6 - }, - { - "gender": "Female", - "age": 60, - "weight": 75.1 - }, - { - "gender": "Female", - "age": 70, - "weight": 73.2 - }, - { - "gender": "Female", - "age": 80, - "weight": 66.3 - } - ] + "weights": [ + { + "gender": "Male", + "age": 5, + "weight": 21.1 + }, + { + "gender": "Male", + "age": 10, + "weight": 38.7 + }, + { + "gender": "Male", + "age": 15, + "weight": 66.1 + }, + { + "gender": "Male", + "age": 20, + "weight": 81.3 + }, + { + "gender": "Male", + "age": 30, + "weight": 89.7 + }, + { + "gender": "Male", + "age": 40, + "weight": 90.5 + }, + { + "gender": "Male", + "age": 50, + "weight": 89.6 + }, + { + "gender": "Male", + "age": 60, + "weight": 89.5 + }, + { + "gender": "Male", + "age": 70, + "weight": 89.3 + }, + { + "gender": "Male", + "age": 80, + "weight": 79.6 + }, + { + "gender": "Female", + "age": 5, + "weight": 21.0 + }, + { + "gender": "Female", + "age": 10, + "weight": 39.5 + }, + { + "gender": "Female", + "age": 15, + "weight": 58.1 + }, + { + "gender": "Female", + "age": 20, + "weight": 69.5 + }, + { + "gender": "Female", + "age": 30, + "weight": 73.3 + }, + { + "gender": "Female", + "age": 40, + "weight": 75.2 + }, + { + "gender": "Female", + "age": 50, + "weight": 74.6 + }, + { + "gender": "Female", + "age": 60, + "weight": 75.1 + }, + { + "gender": "Female", + "age": 70, + "weight": 73.2 + }, + { + "gender": "Female", + "age": 80, + "weight": 66.3 + } + ] } diff --git a/src/lib/data/standards.json b/src/lib/data/standards.json index 3c02ee7..4383735 100644 --- a/src/lib/data/standards.json +++ b/src/lib/data/standards.json @@ -1,5316 +1,5316 @@ { - "BackSquat": { - "metadata": { - "attribute": "Strength", - "generators": [], - "unit": "kg", - "name": "Squat" + "BackSquat": { + "metadata": { + "attribute": "Strength", + "generators": [], + "unit": "kg", + "name": "Squat" + }, + "standards": [ + { + "metrics": { + "age": 17, + "weight": 57, + "gender": "Male" + }, + "levels": { + "1": 34, + "2": 59, + "3": 78, + "4": 107, + "5": 136 + } + }, + { + "metrics": { + "age": 17, + "weight": 68, + "gender": "Male" + }, + "levels": { + "1": 38, + "2": 65, + "3": 87, + "4": 120, + "5": 158 + } + }, + { + "metrics": { + "age": 17, + "weight": 79, + "gender": "Male" + }, + "levels": { + "1": 46, + "2": 80, + "3": 106, + "4": 146, + "5": 192 + } + }, + { + "metrics": { + "age": 17, + "weight": 91, + "gender": "Male" + }, + "levels": { + "1": 49, + "2": 85, + "3": 113, + "4": 155, + "5": 205 + } + }, + { + "metrics": { + "age": 17, + "weight": 102, + "gender": "Male" + }, + "levels": { + "1": 52, + "2": 89, + "3": 118, + "4": 162, + "5": 209 + } + }, + { + "metrics": { + "age": 17, + "weight": 113, + "gender": "Male" + }, + "levels": { + "1": 53, + "2": 90, + "3": 120, + "4": 165, + "5": 213 + } + }, + { + "metrics": { + "age": 17, + "weight": 136, + "gender": "Male" + }, + "levels": { + "1": 56, + "2": 97, + "3": 129, + "4": 177, + "5": 219 + } + }, + { + "metrics": { + "age": 25, + "weight": 57, + "gender": "Male" + }, + "levels": { + "1": 38, + "2": 65, + "3": 87, + "4": 120, + "5": 153 + } + }, + { + "metrics": { + "age": 25, + "weight": 68, + "gender": "Male" + }, + "levels": { + "1": 46, + "2": 78, + "3": 104, + "4": 143, + "5": 189 + } + }, + { + "metrics": { + "age": 25, + "weight": 79, + "gender": "Male" + }, + "levels": { + "1": 51, + "2": 87, + "3": 116, + "4": 160, + "5": 210 + } + }, + { + "metrics": { + "age": 25, + "weight": 91, + "gender": "Male" + }, + "levels": { + "1": 58, + "2": 98, + "3": 130, + "4": 179, + "5": 236 + } + }, + { + "metrics": { + "age": 25, + "weight": 102, + "gender": "Male" + }, + "levels": { + "1": 57, + "2": 98, + "3": 131, + "4": 180, + "5": 232 + } + }, + { + "metrics": { + "age": 25, + "weight": 113, + "gender": "Male" + }, + "levels": { + "1": 70, + "2": 120, + "3": 160, + "4": 220, + "5": 285 + } + }, + { + "metrics": { + "age": 25, + "weight": 136, + "gender": "Male" + }, + "levels": { + "1": 68, + "2": 116, + "3": 164, + "4": 226, + "5": 282 + } + }, + { + "metrics": { + "age": 35, + "weight": 57, + "gender": "Male" + }, + "levels": { + "1": 40, + "2": 68, + "3": 91, + "4": 125, + "5": 159 + } + }, + { + "metrics": { + "age": 35, + "weight": 68, + "gender": "Male" + }, + "levels": { + "1": 48, + "2": 82, + "3": 110, + "4": 151, + "5": 199 + } + }, + { + "metrics": { + "age": 35, + "weight": 79, + "gender": "Male" + }, + "levels": { + "1": 53, + "2": 91, + "3": 122, + "4": 167, + "5": 220 + } + }, + { + "metrics": { + "age": 35, + "weight": 91, + "gender": "Male" + }, + "levels": { + "1": 58, + "2": 99, + "3": 132, + "4": 181, + "5": 238 + } + }, + { + "metrics": { + "age": 35, + "weight": 102, + "gender": "Male" + }, + "levels": { + "1": 62, + "2": 106, + "3": 142, + "4": 195, + "5": 252 + } + }, + { + "metrics": { + "age": 35, + "weight": 113, + "gender": "Male" + }, + "levels": { + "1": 66, + "2": 113, + "3": 150, + "4": 206, + "5": 267 + } + }, + { + "metrics": { + "age": 35, + "weight": 136, + "gender": "Male" + }, + "levels": { + "1": 74, + "2": 127, + "3": 169, + "4": 232, + "5": 291 + } + }, + { + "metrics": { + "age": 45, + "weight": 57, + "gender": "Male" + }, + "levels": { + "1": 38, + "2": 65, + "3": 86, + "4": 118, + "5": 142 + } + }, + { + "metrics": { + "age": 45, + "weight": 68, + "gender": "Male" + }, + "levels": { + "1": 43, + "2": 73, + "3": 98, + "4": 134, + "5": 161 + } + }, + { + "metrics": { + "age": 45, + "weight": 79, + "gender": "Male" + }, + "levels": { + "1": 46, + "2": 79, + "3": 105, + "4": 144, + "5": 173 + } + }, + { + "metrics": { + "age": 45, + "weight": 91, + "gender": "Male" + }, + "levels": { + "1": 54, + "2": 92, + "3": 123, + "4": 169, + "5": 203 + } + }, + { + "metrics": { + "age": 45, + "weight": 102, + "gender": "Male" + }, + "levels": { + "1": 56, + "2": 97, + "3": 129, + "4": 177, + "5": 213 + } + }, + { + "metrics": { + "age": 45, + "weight": 113, + "gender": "Male" + }, + "levels": { + "1": 61, + "2": 104, + "3": 139, + "4": 191, + "5": 229 + } + }, + { + "metrics": { + "age": 45, + "weight": 136, + "gender": "Male" + }, + "levels": { + "1": 66, + "2": 113, + "3": 150, + "4": 206, + "5": 248 + } + }, + { + "metrics": { + "age": 55, + "weight": 57, + "gender": "Male" + }, + "levels": { + "1": 32, + "2": 55, + "3": 73, + "4": 100, + "5": 132 + } + }, + { + "metrics": { + "age": 55, + "weight": 68, + "gender": "Male" + }, + "levels": { + "1": 37, + "2": 64, + "3": 85, + "4": 117, + "5": 154 + } + }, + { + "metrics": { + "age": 55, + "weight": 79, + "gender": "Male" + }, + "levels": { + "1": 42, + "2": 71, + "3": 95, + "4": 131, + "5": 172 + } + }, + { + "metrics": { + "age": 55, + "weight": 91, + "gender": "Male" + }, + "levels": { + "1": 46, + "2": 80, + "3": 106, + "4": 146, + "5": 192 + } + }, + { + "metrics": { + "age": 55, + "weight": 102, + "gender": "Male" + }, + "levels": { + "1": 50, + "2": 86, + "3": 114, + "4": 157, + "5": 207 + } + }, + { + "metrics": { + "age": 55, + "weight": 113, + "gender": "Male" + }, + "levels": { + "1": 53, + "2": 90, + "3": 120, + "4": 165, + "5": 218 + } + }, + { + "metrics": { + "age": 55, + "weight": 136, + "gender": "Male" + }, + "levels": { + "1": 54, + "2": 93, + "3": 124, + "4": 171, + "5": 225 + } + }, + { + "metrics": { + "age": 65, + "weight": 57, + "gender": "Male" + }, + "levels": { + "1": 23, + "2": 40, + "3": 53, + "4": 73, + "5": 96 + } + }, + { + "metrics": { + "age": 65, + "weight": 68, + "gender": "Male" + }, + "levels": { + "1": 33, + "2": 57, + "3": 76, + "4": 105, + "5": 138 + } + }, + { + "metrics": { + "age": 65, + "weight": 79, + "gender": "Male" + }, + "levels": { + "1": 40, + "2": 68, + "3": 91, + "4": 125, + "5": 165 + } + }, + { + "metrics": { + "age": 65, + "weight": 91, + "gender": "Male" + }, + "levels": { + "1": 43, + "2": 74, + "3": 98, + "4": 135, + "5": 178 + } + }, + { + "metrics": { + "age": 65, + "weight": 102, + "gender": "Male" + }, + "levels": { + "1": 47, + "2": 80, + "3": 107, + "4": 147, + "5": 194 + } + }, + { + "metrics": { + "age": 65, + "weight": 113, + "gender": "Male" + }, + "levels": { + "1": 48, + "2": 83, + "3": 110, + "4": 151, + "5": 199 + } + }, + { + "metrics": { + "age": 65, + "weight": 136, + "gender": "Male" + }, + "levels": { + "1": 50, + "2": 86, + "3": 115, + "4": 158, + "5": 208 + } + }, + { + "metrics": { + "age": 75, + "weight": 57, + "gender": "Male" + }, + "levels": { + "1": 22, + "2": 38, + "3": 51, + "4": 70, + "5": 92 + } + }, + { + "metrics": { + "age": 75, + "weight": 68, + "gender": "Male" + }, + "levels": { + "1": 30, + "2": 51, + "3": 68, + "4": 94, + "5": 123 + } + }, + { + "metrics": { + "age": 75, + "weight": 79, + "gender": "Male" + }, + "levels": { + "1": 33, + "2": 56, + "3": 75, + "4": 103, + "5": 136 + } + }, + { + "metrics": { + "age": 75, + "weight": 91, + "gender": "Male" + }, + "levels": { + "1": 34, + "2": 59, + "3": 78, + "4": 107, + "5": 141 + } + }, + { + "metrics": { + "age": 75, + "weight": 102, + "gender": "Male" + }, + "levels": { + "1": 35, + "2": 59, + "3": 79, + "4": 109, + "5": 143 + } + }, + { + "metrics": { + "age": 75, + "weight": 113, + "gender": "Male" + }, + "levels": { + "1": 35, + "2": 60, + "3": 80, + "4": 110, + "5": 145 + } + }, + { + "metrics": { + "age": 75, + "weight": 136, + "gender": "Male" + }, + "levels": { + "1": 36, + "2": 62, + "3": 82, + "4": 113, + "5": 149 + } + }, + { + "metrics": { + "age": 85, + "weight": 57, + "gender": "Male" + }, + "levels": { + "1": 18, + "2": 30, + "3": 40, + "4": 55, + "5": 73 + } + }, + { + "metrics": { + "age": 85, + "weight": 68, + "gender": "Male" + }, + "levels": { + "1": 24, + "2": 40, + "3": 54, + "4": 74, + "5": 97 + } + }, + { + "metrics": { + "age": 85, + "weight": 79, + "gender": "Male" + }, + "levels": { + "1": 26, + "2": 44, + "3": 59, + "4": 81, + "5": 107 + } + }, + { + "metrics": { + "age": 85, + "weight": 91, + "gender": "Male" + }, + "levels": { + "1": 27, + "2": 46, + "3": 62, + "4": 85, + "5": 112 + } + }, + { + "metrics": { + "age": 85, + "weight": 102, + "gender": "Male" + }, + "levels": { + "1": 28, + "2": 46, + "3": 62, + "4": 86, + "5": 113 + } + }, + { + "metrics": { + "age": 85, + "weight": 113, + "gender": "Male" + }, + "levels": { + "1": 28, + "2": 47, + "3": 63, + "4": 87, + "5": 115 + } + }, + { + "metrics": { + "age": 85, + "weight": 136, + "gender": "Male" + }, + "levels": { + "1": 28, + "2": 49, + "3": 65, + "4": 89, + "5": 117 + } + }, + { + "metrics": { + "age": 17, + "weight": 57, + "gender": "Female" + }, + "levels": { + "1": 23, + "2": 32, + "3": 44, + "4": 61, + "5": 84 + } + }, + { + "metrics": { + "age": 17, + "weight": 68, + "gender": "Female" + }, + "levels": { + "1": 29, + "2": 41, + "3": 56, + "4": 77, + "5": 106 + } + }, + { + "metrics": { + "age": 17, + "weight": 79, + "gender": "Female" + }, + "levels": { + "1": 32, + "2": 44, + "3": 61, + "4": 84, + "5": 116 + } + }, + { + "metrics": { + "age": 17, + "weight": 91, + "gender": "Female" + }, + "levels": { + "1": 35, + "2": 48, + "3": 66, + "4": 91, + "5": 126 + } + }, + { + "metrics": { + "age": 17, + "weight": 102, + "gender": "Female" + }, + "levels": { + "1": 35, + "2": 48, + "3": 66, + "4": 91, + "5": 125 + } + }, + { + "metrics": { + "age": 17, + "weight": 113, + "gender": "Female" + }, + "levels": { + "1": 36, + "2": 50, + "3": 68, + "4": 94, + "5": 130 + } + }, + { + "metrics": { + "age": 17, + "weight": 136, + "gender": "Female" + }, + "levels": { + "1": 53, + "2": 73, + "3": 100, + "4": 138, + "5": 190 + } + }, + { + "metrics": { + "age": 25, + "weight": 57, + "gender": "Female" + }, + "levels": { + "1": 27, + "2": 37, + "3": 51, + "4": 70, + "5": 97 + } + }, + { + "metrics": { + "age": 25, + "weight": 68, + "gender": "Female" + }, + "levels": { + "1": 32, + "2": 45, + "3": 61, + "4": 85, + "5": 118 + } + }, + { + "metrics": { + "age": 25, + "weight": 79, + "gender": "Female" + }, + "levels": { + "1": 35, + "2": 49, + "3": 67, + "4": 93, + "5": 128 + } + }, + { + "metrics": { + "age": 25, + "weight": 91, + "gender": "Female" + }, + "levels": { + "1": 36, + "2": 50, + "3": 69, + "4": 95, + "5": 132 + } + }, + { + "metrics": { + "age": 25, + "weight": 102, + "gender": "Female" + }, + "levels": { + "1": 40, + "2": 55, + "3": 76, + "4": 105, + "5": 145 + } + }, + { + "metrics": { + "age": 25, + "weight": 113, + "gender": "Female" + }, + "levels": { + "1": 46, + "2": 63, + "3": 87, + "4": 120, + "5": 165 + } + }, + { + "metrics": { + "age": 25, + "weight": 136, + "gender": "Female" + }, + "levels": { + "1": 57, + "2": 75, + "3": 105, + "4": 140, + "5": 195 + } + }, + { + "metrics": { + "age": 35, + "weight": 57, + "gender": "Female" + }, + "levels": { + "1": 28, + "2": 43, + "3": 65, + "4": 89, + "5": 112 + } + }, + { + "metrics": { + "age": 35, + "weight": 68, + "gender": "Female" + }, + "levels": { + "1": 34, + "2": 48, + "3": 71, + "4": 97, + "5": 128 + } + }, + { + "metrics": { + "age": 35, + "weight": 79, + "gender": "Female" + }, + "levels": { + "1": 36, + "2": 55, + "3": 80, + "4": 110, + "5": 144 + } + }, + { + "metrics": { + "age": 35, + "weight": 91, + "gender": "Female" + }, + "levels": { + "1": 38, + "2": 59, + "3": 85, + "4": 117, + "5": 154 + } + }, + { + "metrics": { + "age": 35, + "weight": 102, + "gender": "Female" + }, + "levels": { + "1": 44, + "2": 62, + "3": 89, + "4": 123, + "5": 157 + } + }, + { + "metrics": { + "age": 35, + "weight": 113, + "gender": "Female" + }, + "levels": { + "1": 51, + "2": 68, + "3": 97, + "4": 134, + "5": 171 + } + }, + { + "metrics": { + "age": 35, + "weight": 136, + "gender": "Female" + }, + "levels": { + "1": 60, + "2": 81, + "3": 114, + "4": 157, + "5": 192 + } + }, + { + "metrics": { + "age": 45, + "weight": 57, + "gender": "Female" + }, + "levels": { + "1": 23, + "2": 38, + "3": 41, + "4": 69, + "5": 78 + } + }, + { + "metrics": { + "age": 45, + "weight": 68, + "gender": "Female" + }, + "levels": { + "1": 30, + "2": 49, + "3": 55, + "4": 90, + "5": 100 + } + }, + { + "metrics": { + "age": 45, + "weight": 79, + "gender": "Female" + }, + "levels": { + "1": 33, + "2": 53, + "3": 59, + "4": 96, + "5": 108 + } + }, + { + "metrics": { + "age": 45, + "weight": 91, + "gender": "Female" + }, + "levels": { + "1": 34, + "2": 55, + "3": 62, + "4": 101, + "5": 114 + } + }, + { + "metrics": { + "age": 45, + "weight": 102, + "gender": "Female" + }, + "levels": { + "1": 36, + "2": 58, + "3": 65, + "4": 105, + "5": 119 + } + }, + { + "metrics": { + "age": 45, + "weight": 113, + "gender": "Female" + }, + "levels": { + "1": 38, + "2": 61, + "3": 69, + "4": 111, + "5": 126 + } + }, + { + "metrics": { + "age": 45, + "weight": 136, + "gender": "Female" + }, + "levels": { + "1": 40, + "2": 64, + "3": 73, + "4": 117, + "5": 133 + } + }, + { + "metrics": { + "age": 55, + "weight": 57, + "gender": "Female" + }, + "levels": { + "1": 19, + "2": 33, + "3": 40, + "4": 60, + "5": 76 + } + }, + { + "metrics": { + "age": 55, + "weight": 68, + "gender": "Female" + }, + "levels": { + "1": 24, + "2": 41, + "3": 54, + "4": 74, + "5": 97 + } + }, + { + "metrics": { + "age": 55, + "weight": 79, + "gender": "Female" + }, + "levels": { + "1": 25, + "2": 42, + "3": 56, + "4": 76, + "5": 102 + } + }, + { + "metrics": { + "age": 55, + "weight": 91, + "gender": "Female" + }, + "levels": { + "1": 26, + "2": 44, + "3": 59, + "4": 81, + "5": 107 + } + }, + { + "metrics": { + "age": 55, + "weight": 102, + "gender": "Female" + }, + "levels": { + "1": 29, + "2": 49, + "3": 64, + "4": 90, + "5": 118 + } + }, + { + "metrics": { + "age": 55, + "weight": 113, + "gender": "Female" + }, + "levels": { + "1": 29, + "2": 50, + "3": 67, + "4": 92, + "5": 121 + } + }, + { + "metrics": { + "age": 55, + "weight": 136, + "gender": "Female" + }, + "levels": { + "1": 35, + "2": 59, + "3": 70, + "4": 111, + "5": 130 + } + }, + { + "metrics": { + "age": 65, + "weight": 57, + "gender": "Female" + }, + "levels": { + "1": 15, + "2": 26, + "3": 35, + "4": 48, + "5": 63 + } + }, + { + "metrics": { + "age": 65, + "weight": 68, + "gender": "Female" + }, + "levels": { + "1": 18, + "2": 33, + "3": 44, + "4": 62, + "5": 81 + } + }, + { + "metrics": { + "age": 65, + "weight": 79, + "gender": "Female" + }, + "levels": { + "1": 20, + "2": 35, + "3": 47, + "4": 65, + "5": 83 + } + }, + { + "metrics": { + "age": 65, + "weight": 91, + "gender": "Female" + }, + "levels": { + "1": 22, + "2": 37, + "3": 48, + "4": 66, + "5": 85 + } + }, + { + "metrics": { + "age": 65, + "weight": 102, + "gender": "Female" + }, + "levels": { + "1": 24, + "2": 40, + "3": 54, + "4": 74, + "5": 93 + } + }, + { + "metrics": { + "age": 65, + "weight": 113, + "gender": "Female" + }, + "levels": { + "1": 25, + "2": 42, + "3": 56, + "4": 78, + "5": 99 + } + }, + { + "metrics": { + "age": 65, + "weight": 136, + "gender": "Female" + }, + "levels": { + "1": 26, + "2": 45, + "3": 58, + "4": 79, + "5": 102 + } + }, + { + "metrics": { + "age": 75, + "weight": 57, + "gender": "Female" + }, + "levels": { + "1": 14, + "2": 24, + "3": 32, + "4": 44, + "5": 58 + } + }, + { + "metrics": { + "age": 75, + "weight": 68, + "gender": "Female" + }, + "levels": { + "1": 15, + "2": 25, + "3": 33, + "4": 46, + "5": 60 + } + }, + { + "metrics": { + "age": 75, + "weight": 79, + "gender": "Female" + }, + "levels": { + "1": 16, + "2": 27, + "3": 36, + "4": 50, + "5": 65 + } + }, + { + "metrics": { + "age": 75, + "weight": 91, + "gender": "Female" + }, + "levels": { + "1": 17, + "2": 28, + "3": 37, + "4": 52, + "5": 69 + } + }, + { + "metrics": { + "age": 75, + "weight": 102, + "gender": "Female" + }, + "levels": { + "1": 17, + "2": 29, + "3": 39, + "4": 54, + "5": 71 + } + }, + { + "metrics": { + "age": 75, + "weight": 113, + "gender": "Female" + }, + "levels": { + "1": 18, + "2": 30, + "3": 40, + "4": 55, + "5": 73 + } + }, + { + "metrics": { + "age": 75, + "weight": 136, + "gender": "Female" + }, + "levels": { + "1": 18, + "2": 30, + "3": 40, + "4": 55, + "5": 73 + } + }, + { + "metrics": { + "age": 85, + "weight": 57, + "gender": "Female" + }, + "levels": { + "1": 11, + "2": 19, + "3": 25, + "4": 35, + "5": 46 + } + }, + { + "metrics": { + "age": 85, + "weight": 68, + "gender": "Female" + }, + "levels": { + "1": 11, + "2": 20, + "3": 26, + "4": 36, + "5": 48 + } + }, + { + "metrics": { + "age": 85, + "weight": 79, + "gender": "Female" + }, + "levels": { + "1": 12, + "2": 21, + "3": 28, + "4": 39, + "5": 52 + } + }, + { + "metrics": { + "age": 85, + "weight": 91, + "gender": "Female" + }, + "levels": { + "1": 13, + "2": 23, + "3": 30, + "4": 41, + "5": 54 + } + }, + { + "metrics": { + "age": 85, + "weight": 102, + "gender": "Female" + }, + "levels": { + "1": 13, + "2": 23, + "3": 31, + "4": 42, + "5": 56 + } + }, + { + "metrics": { + "age": 85, + "weight": 113, + "gender": "Female" + }, + "levels": { + "1": 14, + "2": 24, + "3": 32, + "4": 43, + "5": 57 + } + }, + { + "metrics": { + "age": 85, + "weight": 136, + "gender": "Female" + }, + "levels": { + "1": 14, + "2": 24, + "3": 32, + "4": 43, + "5": 57 + } + } + ] }, - "standards": [ - { - "metrics": { - "age": 17, - "weight": 57, - "gender": "Male" - }, - "levels": { - "1": 34, - "2": 59, - "3": 78, - "4": 107, - "5": 136 - } - }, - { - "metrics": { - "age": 17, - "weight": 68, - "gender": "Male" - }, - "levels": { - "1": 38, - "2": 65, - "3": 87, - "4": 120, - "5": 158 - } - }, - { - "metrics": { - "age": 17, - "weight": 79, - "gender": "Male" - }, - "levels": { - "1": 46, - "2": 80, - "3": 106, - "4": 146, - "5": 192 - } - }, - { - "metrics": { - "age": 17, - "weight": 91, - "gender": "Male" - }, - "levels": { - "1": 49, - "2": 85, - "3": 113, - "4": 155, - "5": 205 - } - }, - { - "metrics": { - "age": 17, - "weight": 102, - "gender": "Male" - }, - "levels": { - "1": 52, - "2": 89, - "3": 118, - "4": 162, - "5": 209 - } - }, - { - "metrics": { - "age": 17, - "weight": 113, - "gender": "Male" - }, - "levels": { - "1": 53, - "2": 90, - "3": 120, - "4": 165, - "5": 213 - } - }, - { - "metrics": { - "age": 17, - "weight": 136, - "gender": "Male" - }, - "levels": { - "1": 56, - "2": 97, - "3": 129, - "4": 177, - "5": 219 - } - }, - { - "metrics": { - "age": 25, - "weight": 57, - "gender": "Male" - }, - "levels": { - "1": 38, - "2": 65, - "3": 87, - "4": 120, - "5": 153 - } - }, - { - "metrics": { - "age": 25, - "weight": 68, - "gender": "Male" - }, - "levels": { - "1": 46, - "2": 78, - "3": 104, - "4": 143, - "5": 189 - } - }, - { - "metrics": { - "age": 25, - "weight": 79, - "gender": "Male" - }, - "levels": { - "1": 51, - "2": 87, - "3": 116, - "4": 160, - "5": 210 - } - }, - { - "metrics": { - "age": 25, - "weight": 91, - "gender": "Male" - }, - "levels": { - "1": 58, - "2": 98, - "3": 130, - "4": 179, - "5": 236 - } - }, - { - "metrics": { - "age": 25, - "weight": 102, - "gender": "Male" - }, - "levels": { - "1": 57, - "2": 98, - "3": 131, - "4": 180, - "5": 232 - } - }, - { - "metrics": { - "age": 25, - "weight": 113, - "gender": "Male" - }, - "levels": { - "1": 70, - "2": 120, - "3": 160, - "4": 220, - "5": 285 - } - }, - { - "metrics": { - "age": 25, - "weight": 136, - "gender": "Male" - }, - "levels": { - "1": 68, - "2": 116, - "3": 164, - "4": 226, - "5": 282 - } - }, - { - "metrics": { - "age": 35, - "weight": 57, - "gender": "Male" - }, - "levels": { - "1": 40, - "2": 68, - "3": 91, - "4": 125, - "5": 159 - } - }, - { - "metrics": { - "age": 35, - "weight": 68, - "gender": "Male" - }, - "levels": { - "1": 48, - "2": 82, - "3": 110, - "4": 151, - "5": 199 - } - }, - { - "metrics": { - "age": 35, - "weight": 79, - "gender": "Male" - }, - "levels": { - "1": 53, - "2": 91, - "3": 122, - "4": 167, - "5": 220 - } - }, - { - "metrics": { - "age": 35, - "weight": 91, - "gender": "Male" - }, - "levels": { - "1": 58, - "2": 99, - "3": 132, - "4": 181, - "5": 238 - } - }, - { - "metrics": { - "age": 35, - "weight": 102, - "gender": "Male" - }, - "levels": { - "1": 62, - "2": 106, - "3": 142, - "4": 195, - "5": 252 - } - }, - { - "metrics": { - "age": 35, - "weight": 113, - "gender": "Male" - }, - "levels": { - "1": 66, - "2": 113, - "3": 150, - "4": 206, - "5": 267 - } - }, - { - "metrics": { - "age": 35, - "weight": 136, - "gender": "Male" - }, - "levels": { - "1": 74, - "2": 127, - "3": 169, - "4": 232, - "5": 291 - } - }, - { - "metrics": { - "age": 45, - "weight": 57, - "gender": "Male" - }, - "levels": { - "1": 38, - "2": 65, - "3": 86, - "4": 118, - "5": 142 - } - }, - { - "metrics": { - "age": 45, - "weight": 68, - "gender": "Male" - }, - "levels": { - "1": 43, - "2": 73, - "3": 98, - "4": 134, - "5": 161 - } - }, - { - "metrics": { - "age": 45, - "weight": 79, - "gender": "Male" - }, - "levels": { - "1": 46, - "2": 79, - "3": 105, - "4": 144, - "5": 173 - } - }, - { - "metrics": { - "age": 45, - "weight": 91, - "gender": "Male" - }, - "levels": { - "1": 54, - "2": 92, - "3": 123, - "4": 169, - "5": 203 - } - }, - { - "metrics": { - "age": 45, - "weight": 102, - "gender": "Male" - }, - "levels": { - "1": 56, - "2": 97, - "3": 129, - "4": 177, - "5": 213 - } - }, - { - "metrics": { - "age": 45, - "weight": 113, - "gender": "Male" - }, - "levels": { - "1": 61, - "2": 104, - "3": 139, - "4": 191, - "5": 229 - } - }, - { - "metrics": { - "age": 45, - "weight": 136, - "gender": "Male" - }, - "levels": { - "1": 66, - "2": 113, - "3": 150, - "4": 206, - "5": 248 - } - }, - { - "metrics": { - "age": 55, - "weight": 57, - "gender": "Male" - }, - "levels": { - "1": 32, - "2": 55, - "3": 73, - "4": 100, - "5": 132 - } - }, - { - "metrics": { - "age": 55, - "weight": 68, - "gender": "Male" - }, - "levels": { - "1": 37, - "2": 64, - "3": 85, - "4": 117, - "5": 154 - } - }, - { - "metrics": { - "age": 55, - "weight": 79, - "gender": "Male" - }, - "levels": { - "1": 42, - "2": 71, - "3": 95, - "4": 131, - "5": 172 - } - }, - { - "metrics": { - "age": 55, - "weight": 91, - "gender": "Male" - }, - "levels": { - "1": 46, - "2": 80, - "3": 106, - "4": 146, - "5": 192 - } - }, - { - "metrics": { - "age": 55, - "weight": 102, - "gender": "Male" - }, - "levels": { - "1": 50, - "2": 86, - "3": 114, - "4": 157, - "5": 207 - } - }, - { - "metrics": { - "age": 55, - "weight": 113, - "gender": "Male" - }, - "levels": { - "1": 53, - "2": 90, - "3": 120, - "4": 165, - "5": 218 - } - }, - { - "metrics": { - "age": 55, - "weight": 136, - "gender": "Male" - }, - "levels": { - "1": 54, - "2": 93, - "3": 124, - "4": 171, - "5": 225 - } - }, - { - "metrics": { - "age": 65, - "weight": 57, - "gender": "Male" - }, - "levels": { - "1": 23, - "2": 40, - "3": 53, - "4": 73, - "5": 96 - } - }, - { - "metrics": { - "age": 65, - "weight": 68, - "gender": "Male" - }, - "levels": { - "1": 33, - "2": 57, - "3": 76, - "4": 105, - "5": 138 - } - }, - { - "metrics": { - "age": 65, - "weight": 79, - "gender": "Male" - }, - "levels": { - "1": 40, - "2": 68, - "3": 91, - "4": 125, - "5": 165 - } - }, - { - "metrics": { - "age": 65, - "weight": 91, - "gender": "Male" - }, - "levels": { - "1": 43, - "2": 74, - "3": 98, - "4": 135, - "5": 178 - } - }, - { - "metrics": { - "age": 65, - "weight": 102, - "gender": "Male" - }, - "levels": { - "1": 47, - "2": 80, - "3": 107, - "4": 147, - "5": 194 - } - }, - { - "metrics": { - "age": 65, - "weight": 113, - "gender": "Male" - }, - "levels": { - "1": 48, - "2": 83, - "3": 110, - "4": 151, - "5": 199 - } - }, - { - "metrics": { - "age": 65, - "weight": 136, - "gender": "Male" - }, - "levels": { - "1": 50, - "2": 86, - "3": 115, - "4": 158, - "5": 208 - } - }, - { - "metrics": { - "age": 75, - "weight": 57, - "gender": "Male" - }, - "levels": { - "1": 22, - "2": 38, - "3": 51, - "4": 70, - "5": 92 - } - }, - { - "metrics": { - "age": 75, - "weight": 68, - "gender": "Male" - }, - "levels": { - "1": 30, - "2": 51, - "3": 68, - "4": 94, - "5": 123 - } - }, - { - "metrics": { - "age": 75, - "weight": 79, - "gender": "Male" - }, - "levels": { - "1": 33, - "2": 56, - "3": 75, - "4": 103, - "5": 136 - } - }, - { - "metrics": { - "age": 75, - "weight": 91, - "gender": "Male" - }, - "levels": { - "1": 34, - "2": 59, - "3": 78, - "4": 107, - "5": 141 - } - }, - { - "metrics": { - "age": 75, - "weight": 102, - "gender": "Male" - }, - "levels": { - "1": 35, - "2": 59, - "3": 79, - "4": 109, - "5": 143 - } - }, - { - "metrics": { - "age": 75, - "weight": 113, - "gender": "Male" - }, - "levels": { - "1": 35, - "2": 60, - "3": 80, - "4": 110, - "5": 145 - } - }, - { - "metrics": { - "age": 75, - "weight": 136, - "gender": "Male" - }, - "levels": { - "1": 36, - "2": 62, - "3": 82, - "4": 113, - "5": 149 - } - }, - { - "metrics": { - "age": 85, - "weight": 57, - "gender": "Male" - }, - "levels": { - "1": 18, - "2": 30, - "3": 40, - "4": 55, - "5": 73 - } - }, - { - "metrics": { - "age": 85, - "weight": 68, - "gender": "Male" - }, - "levels": { - "1": 24, - "2": 40, - "3": 54, - "4": 74, - "5": 97 - } - }, - { - "metrics": { - "age": 85, - "weight": 79, - "gender": "Male" - }, - "levels": { - "1": 26, - "2": 44, - "3": 59, - "4": 81, - "5": 107 - } - }, - { - "metrics": { - "age": 85, - "weight": 91, - "gender": "Male" - }, - "levels": { - "1": 27, - "2": 46, - "3": 62, - "4": 85, - "5": 112 - } - }, - { - "metrics": { - "age": 85, - "weight": 102, - "gender": "Male" - }, - "levels": { - "1": 28, - "2": 46, - "3": 62, - "4": 86, - "5": 113 - } - }, - { - "metrics": { - "age": 85, - "weight": 113, - "gender": "Male" - }, - "levels": { - "1": 28, - "2": 47, - "3": 63, - "4": 87, - "5": 115 - } - }, - { - "metrics": { - "age": 85, - "weight": 136, - "gender": "Male" - }, - "levels": { - "1": 28, - "2": 49, - "3": 65, - "4": 89, - "5": 117 - } - }, - { - "metrics": { - "age": 17, - "weight": 57, - "gender": "Female" - }, - "levels": { - "1": 23, - "2": 32, - "3": 44, - "4": 61, - "5": 84 - } - }, - { - "metrics": { - "age": 17, - "weight": 68, - "gender": "Female" - }, - "levels": { - "1": 29, - "2": 41, - "3": 56, - "4": 77, - "5": 106 - } - }, - { - "metrics": { - "age": 17, - "weight": 79, - "gender": "Female" - }, - "levels": { - "1": 32, - "2": 44, - "3": 61, - "4": 84, - "5": 116 - } - }, - { - "metrics": { - "age": 17, - "weight": 91, - "gender": "Female" - }, - "levels": { - "1": 35, - "2": 48, - "3": 66, - "4": 91, - "5": 126 - } - }, - { - "metrics": { - "age": 17, - "weight": 102, - "gender": "Female" - }, - "levels": { - "1": 35, - "2": 48, - "3": 66, - "4": 91, - "5": 125 - } - }, - { - "metrics": { - "age": 17, - "weight": 113, - "gender": "Female" - }, - "levels": { - "1": 36, - "2": 50, - "3": 68, - "4": 94, - "5": 130 - } - }, - { - "metrics": { - "age": 17, - "weight": 136, - "gender": "Female" - }, - "levels": { - "1": 53, - "2": 73, - "3": 100, - "4": 138, - "5": 190 - } - }, - { - "metrics": { - "age": 25, - "weight": 57, - "gender": "Female" - }, - "levels": { - "1": 27, - "2": 37, - "3": 51, - "4": 70, - "5": 97 - } - }, - { - "metrics": { - "age": 25, - "weight": 68, - "gender": "Female" - }, - "levels": { - "1": 32, - "2": 45, - "3": 61, - "4": 85, - "5": 118 - } - }, - { - "metrics": { - "age": 25, - "weight": 79, - "gender": "Female" - }, - "levels": { - "1": 35, - "2": 49, - "3": 67, - "4": 93, - "5": 128 - } - }, - { - "metrics": { - "age": 25, - "weight": 91, - "gender": "Female" - }, - "levels": { - "1": 36, - "2": 50, - "3": 69, - "4": 95, - "5": 132 - } - }, - { - "metrics": { - "age": 25, - "weight": 102, - "gender": "Female" - }, - "levels": { - "1": 40, - "2": 55, - "3": 76, - "4": 105, - "5": 145 - } - }, - { - "metrics": { - "age": 25, - "weight": 113, - "gender": "Female" - }, - "levels": { - "1": 46, - "2": 63, - "3": 87, - "4": 120, - "5": 165 - } - }, - { - "metrics": { - "age": 25, - "weight": 136, - "gender": "Female" - }, - "levels": { - "1": 57, - "2": 75, - "3": 105, - "4": 140, - "5": 195 - } - }, - { - "metrics": { - "age": 35, - "weight": 57, - "gender": "Female" - }, - "levels": { - "1": 28, - "2": 43, - "3": 65, - "4": 89, - "5": 112 - } - }, - { - "metrics": { - "age": 35, - "weight": 68, - "gender": "Female" - }, - "levels": { - "1": 34, - "2": 48, - "3": 71, - "4": 97, - "5": 128 - } - }, - { - "metrics": { - "age": 35, - "weight": 79, - "gender": "Female" - }, - "levels": { - "1": 36, - "2": 55, - "3": 80, - "4": 110, - "5": 144 - } - }, - { - "metrics": { - "age": 35, - "weight": 91, - "gender": "Female" - }, - "levels": { - "1": 38, - "2": 59, - "3": 85, - "4": 117, - "5": 154 - } - }, - { - "metrics": { - "age": 35, - "weight": 102, - "gender": "Female" - }, - "levels": { - "1": 44, - "2": 62, - "3": 89, - "4": 123, - "5": 157 - } - }, - { - "metrics": { - "age": 35, - "weight": 113, - "gender": "Female" - }, - "levels": { - "1": 51, - "2": 68, - "3": 97, - "4": 134, - "5": 171 - } - }, - { - "metrics": { - "age": 35, - "weight": 136, - "gender": "Female" - }, - "levels": { - "1": 60, - "2": 81, - "3": 114, - "4": 157, - "5": 192 - } - }, - { - "metrics": { - "age": 45, - "weight": 57, - "gender": "Female" - }, - "levels": { - "1": 23, - "2": 38, - "3": 41, - "4": 69, - "5": 78 - } - }, - { - "metrics": { - "age": 45, - "weight": 68, - "gender": "Female" - }, - "levels": { - "1": 30, - "2": 49, - "3": 55, - "4": 90, - "5": 100 - } - }, - { - "metrics": { - "age": 45, - "weight": 79, - "gender": "Female" - }, - "levels": { - "1": 33, - "2": 53, - "3": 59, - "4": 96, - "5": 108 - } - }, - { - "metrics": { - "age": 45, - "weight": 91, - "gender": "Female" - }, - "levels": { - "1": 34, - "2": 55, - "3": 62, - "4": 101, - "5": 114 - } - }, - { - "metrics": { - "age": 45, - "weight": 102, - "gender": "Female" - }, - "levels": { - "1": 36, - "2": 58, - "3": 65, - "4": 105, - "5": 119 - } - }, - { - "metrics": { - "age": 45, - "weight": 113, - "gender": "Female" - }, - "levels": { - "1": 38, - "2": 61, - "3": 69, - "4": 111, - "5": 126 - } - }, - { - "metrics": { - "age": 45, - "weight": 136, - "gender": "Female" - }, - "levels": { - "1": 40, - "2": 64, - "3": 73, - "4": 117, - "5": 133 - } - }, - { - "metrics": { - "age": 55, - "weight": 57, - "gender": "Female" - }, - "levels": { - "1": 19, - "2": 33, - "3": 40, - "4": 60, - "5": 76 - } - }, - { - "metrics": { - "age": 55, - "weight": 68, - "gender": "Female" - }, - "levels": { - "1": 24, - "2": 41, - "3": 54, - "4": 74, - "5": 97 - } - }, - { - "metrics": { - "age": 55, - "weight": 79, - "gender": "Female" - }, - "levels": { - "1": 25, - "2": 42, - "3": 56, - "4": 76, - "5": 102 - } - }, - { - "metrics": { - "age": 55, - "weight": 91, - "gender": "Female" - }, - "levels": { - "1": 26, - "2": 44, - "3": 59, - "4": 81, - "5": 107 - } - }, - { - "metrics": { - "age": 55, - "weight": 102, - "gender": "Female" - }, - "levels": { - "1": 29, - "2": 49, - "3": 64, - "4": 90, - "5": 118 - } - }, - { - "metrics": { - "age": 55, - "weight": 113, - "gender": "Female" - }, - "levels": { - "1": 29, - "2": 50, - "3": 67, - "4": 92, - "5": 121 - } - }, - { - "metrics": { - "age": 55, - "weight": 136, - "gender": "Female" - }, - "levels": { - "1": 35, - "2": 59, - "3": 70, - "4": 111, - "5": 130 - } - }, - { - "metrics": { - "age": 65, - "weight": 57, - "gender": "Female" - }, - "levels": { - "1": 15, - "2": 26, - "3": 35, - "4": 48, - "5": 63 - } - }, - { - "metrics": { - "age": 65, - "weight": 68, - "gender": "Female" - }, - "levels": { - "1": 18, - "2": 33, - "3": 44, - "4": 62, - "5": 81 - } - }, - { - "metrics": { - "age": 65, - "weight": 79, - "gender": "Female" - }, - "levels": { - "1": 20, - "2": 35, - "3": 47, - "4": 65, - "5": 83 - } - }, - { - "metrics": { - "age": 65, - "weight": 91, - "gender": "Female" - }, - "levels": { - "1": 22, - "2": 37, - "3": 48, - "4": 66, - "5": 85 - } - }, - { - "metrics": { - "age": 65, - "weight": 102, - "gender": "Female" - }, - "levels": { - "1": 24, - "2": 40, - "3": 54, - "4": 74, - "5": 93 - } - }, - { - "metrics": { - "age": 65, - "weight": 113, - "gender": "Female" - }, - "levels": { - "1": 25, - "2": 42, - "3": 56, - "4": 78, - "5": 99 - } - }, - { - "metrics": { - "age": 65, - "weight": 136, - "gender": "Female" - }, - "levels": { - "1": 26, - "2": 45, - "3": 58, - "4": 79, - "5": 102 - } - }, - { - "metrics": { - "age": 75, - "weight": 57, - "gender": "Female" - }, - "levels": { - "1": 14, - "2": 24, - "3": 32, - "4": 44, - "5": 58 - } - }, - { - "metrics": { - "age": 75, - "weight": 68, - "gender": "Female" - }, - "levels": { - "1": 15, - "2": 25, - "3": 33, - "4": 46, - "5": 60 - } - }, - { - "metrics": { - "age": 75, - "weight": 79, - "gender": "Female" - }, - "levels": { - "1": 16, - "2": 27, - "3": 36, - "4": 50, - "5": 65 - } - }, - { - "metrics": { - "age": 75, - "weight": 91, - "gender": "Female" - }, - "levels": { - "1": 17, - "2": 28, - "3": 37, - "4": 52, - "5": 69 - } - }, - { - "metrics": { - "age": 75, - "weight": 102, - "gender": "Female" - }, - "levels": { - "1": 17, - "2": 29, - "3": 39, - "4": 54, - "5": 71 - } - }, - { - "metrics": { - "age": 75, - "weight": 113, - "gender": "Female" - }, - "levels": { - "1": 18, - "2": 30, - "3": 40, - "4": 55, - "5": 73 - } - }, - { - "metrics": { - "age": 75, - "weight": 136, - "gender": "Female" - }, - "levels": { - "1": 18, - "2": 30, - "3": 40, - "4": 55, - "5": 73 - } - }, - { - "metrics": { - "age": 85, - "weight": 57, - "gender": "Female" - }, - "levels": { - "1": 11, - "2": 19, - "3": 25, - "4": 35, - "5": 46 - } - }, - { - "metrics": { - "age": 85, - "weight": 68, - "gender": "Female" - }, - "levels": { - "1": 11, - "2": 20, - "3": 26, - "4": 36, - "5": 48 - } - }, - { - "metrics": { - "age": 85, - "weight": 79, - "gender": "Female" - }, - "levels": { - "1": 12, - "2": 21, - "3": 28, - "4": 39, - "5": 52 - } - }, - { - "metrics": { - "age": 85, - "weight": 91, - "gender": "Female" - }, - "levels": { - "1": 13, - "2": 23, - "3": 30, - "4": 41, - "5": 54 - } - }, - { - "metrics": { - "age": 85, - "weight": 102, - "gender": "Female" - }, - "levels": { - "1": 13, - "2": 23, - "3": 31, - "4": 42, - "5": 56 - } - }, - { - "metrics": { - "age": 85, - "weight": 113, - "gender": "Female" - }, - "levels": { - "1": 14, - "2": 24, - "3": 32, - "4": 43, - "5": 57 - } - }, - { - "metrics": { - "age": 85, - "weight": 136, - "gender": "Female" - }, - "levels": { - "1": 14, - "2": 24, - "3": 32, - "4": 43, - "5": 57 - } - } - ] - }, - "Deadlift": { - "metadata": { - "attribute": "Strength", - "generators": [], - "unit": "kg", - "name": "Deadlift" + "Deadlift": { + "metadata": { + "attribute": "Strength", + "generators": [], + "unit": "kg", + "name": "Deadlift" + }, + "standards": [ + { + "metrics": { + "age": 17, + "weight": 57, + "gender": "Male" + }, + "levels": { + "1": 45, + "2": 74, + "3": 93, + "4": 103, + "5": 115 + } + }, + { + "metrics": { + "age": 17, + "weight": 68, + "gender": "Male" + }, + "levels": { + "1": 58, + "2": 97, + "3": 121, + "4": 134, + "5": 149 + } + }, + { + "metrics": { + "age": 17, + "weight": 79, + "gender": "Male" + }, + "levels": { + "1": 73, + "2": 122, + "3": 152, + "4": 169, + "5": 188 + } + }, + { + "metrics": { + "age": 17, + "weight": 91, + "gender": "Male" + }, + "levels": { + "1": 77, + "2": 129, + "3": 161, + "4": 179, + "5": 199 + } + }, + { + "metrics": { + "age": 17, + "weight": 102, + "gender": "Male" + }, + "levels": { + "1": 81, + "2": 135, + "3": 169, + "4": 188, + "5": 209 + } + }, + { + "metrics": { + "age": 17, + "weight": 113, + "gender": "Male" + }, + "levels": { + "1": 82, + "2": 137, + "3": 172, + "4": 191, + "5": 212 + } + }, + { + "metrics": { + "age": 17, + "weight": 136, + "gender": "Male" + }, + "levels": { + "1": 84, + "2": 139, + "3": 174, + "4": 194, + "5": 215 + } + }, + { + "metrics": { + "age": 25, + "weight": 57, + "gender": "Male" + }, + "levels": { + "1": 47, + "2": 81, + "3": 101, + "4": 127, + "5": 141 + } + }, + { + "metrics": { + "age": 25, + "weight": 68, + "gender": "Male" + }, + "levels": { + "1": 64, + "2": 112, + "3": 139, + "4": 186, + "5": 207 + } + }, + { + "metrics": { + "age": 25, + "weight": 79, + "gender": "Male" + }, + "levels": { + "1": 76, + "2": 131, + "3": 164, + "4": 219, + "5": 243 + } + }, + { + "metrics": { + "age": 25, + "weight": 91, + "gender": "Male" + }, + "levels": { + "1": 83, + "2": 144, + "3": 180, + "4": 240, + "5": 266 + } + }, + { + "metrics": { + "age": 25, + "weight": 102, + "gender": "Male" + }, + "levels": { + "1": 84, + "2": 146, + "3": 182, + "4": 243, + "5": 270 + } + }, + { + "metrics": { + "age": 25, + "weight": 113, + "gender": "Male" + }, + "levels": { + "1": 85, + "2": 147, + "3": 184, + "4": 245, + "5": 273 + } + }, + { + "metrics": { + "age": 25, + "weight": 136, + "gender": "Male" + }, + "levels": { + "1": 86, + "2": 149, + "3": 187, + "4": 249, + "5": 276 + } + }, + { + "metrics": { + "age": 35, + "weight": 57, + "gender": "Male" + }, + "levels": { + "1": 51, + "2": 89, + "3": 112, + "4": 149, + "5": 175 + } + }, + { + "metrics": { + "age": 35, + "weight": 68, + "gender": "Male" + }, + "levels": { + "1": 64, + "2": 112, + "3": 140, + "4": 186, + "5": 219 + } + }, + { + "metrics": { + "age": 35, + "weight": 79, + "gender": "Male" + }, + "levels": { + "1": 76, + "2": 132, + "3": 165, + "4": 220, + "5": 259 + } + }, + { + "metrics": { + "age": 35, + "weight": 91, + "gender": "Male" + }, + "levels": { + "1": 81, + "2": 142, + "3": 177, + "4": 236, + "5": 278 + } + }, + { + "metrics": { + "age": 35, + "weight": 102, + "gender": "Male" + }, + "levels": { + "1": 83, + "2": 145, + "3": 181, + "4": 242, + "5": 284 + } + }, + { + "metrics": { + "age": 35, + "weight": 113, + "gender": "Male" + }, + "levels": { + "1": 84, + "2": 146, + "3": 183, + "4": 244, + "5": 287 + } + }, + { + "metrics": { + "age": 35, + "weight": 136, + "gender": "Male" + }, + "levels": { + "1": 89, + "2": 154, + "3": 192, + "4": 257, + "5": 302 + } + }, + { + "metrics": { + "age": 45, + "weight": 57, + "gender": "Male" + }, + "levels": { + "1": 46, + "2": 80, + "3": 100, + "4": 118, + "5": 131 + } + }, + { + "metrics": { + "age": 45, + "weight": 68, + "gender": "Male" + }, + "levels": { + "1": 53, + "2": 92, + "3": 115, + "4": 154, + "5": 181 + } + }, + { + "metrics": { + "age": 45, + "weight": 79, + "gender": "Male" + }, + "levels": { + "1": 63, + "2": 109, + "3": 137, + "4": 182, + "5": 214 + } + }, + { + "metrics": { + "age": 45, + "weight": 91, + "gender": "Male" + }, + "levels": { + "1": 65, + "2": 113, + "3": 141, + "4": 188, + "5": 222 + } + }, + { + "metrics": { + "age": 45, + "weight": 102, + "gender": "Male" + }, + "levels": { + "1": 71, + "2": 124, + "3": 154, + "4": 206, + "5": 242 + } + }, + { + "metrics": { + "age": 45, + "weight": 113, + "gender": "Male" + }, + "levels": { + "1": 75, + "2": 130, + "3": 162, + "4": 217, + "5": 255 + } + }, + { + "metrics": { + "age": 45, + "weight": 136, + "gender": "Male" + }, + "levels": { + "1": 76, + "2": 133, + "3": 166, + "4": 221, + "5": 260 + } + }, + { + "metrics": { + "age": 55, + "weight": 57, + "gender": "Male" + }, + "levels": { + "1": 40, + "2": 69, + "3": 86, + "4": 102, + "5": 113 + } + }, + { + "metrics": { + "age": 55, + "weight": 68, + "gender": "Male" + }, + "levels": { + "1": 48, + "2": 84, + "3": 105, + "4": 140, + "5": 164 + } + }, + { + "metrics": { + "age": 55, + "weight": 79, + "gender": "Male" + }, + "levels": { + "1": 56, + "2": 97, + "3": 121, + "4": 161, + "5": 190 + } + }, + { + "metrics": { + "age": 55, + "weight": 91, + "gender": "Male" + }, + "levels": { + "1": 60, + "2": 104, + "3": 130, + "4": 173, + "5": 204 + } + }, + { + "metrics": { + "age": 55, + "weight": 102, + "gender": "Male" + }, + "levels": { + "1": 63, + "2": 110, + "3": 138, + "4": 183, + "5": 216 + } + }, + { + "metrics": { + "age": 55, + "weight": 113, + "gender": "Male" + }, + "levels": { + "1": 64, + "2": 111, + "3": 139, + "4": 185, + "5": 218 + } + }, + { + "metrics": { + "age": 55, + "weight": 136, + "gender": "Male" + }, + "levels": { + "1": 65, + "2": 114, + "3": 142, + "4": 190, + "5": 223 + } + }, + { + "metrics": { + "age": 65, + "weight": 57, + "gender": "Male" + }, + "levels": { + "1": 35, + "2": 61, + "3": 79, + "4": 95, + "5": 109 + } + }, + { + "metrics": { + "age": 65, + "weight": 68, + "gender": "Male" + }, + "levels": { + "1": 46, + "2": 79, + "3": 103, + "4": 124, + "5": 142 + } + }, + { + "metrics": { + "age": 65, + "weight": 79, + "gender": "Male" + }, + "levels": { + "1": 52, + "2": 90, + "3": 117, + "4": 141, + "5": 162 + } + }, + { + "metrics": { + "age": 65, + "weight": 91, + "gender": "Male" + }, + "levels": { + "1": 57, + "2": 98, + "3": 128, + "4": 155, + "5": 177 + } + }, + { + "metrics": { + "age": 65, + "weight": 102, + "gender": "Male" + }, + "levels": { + "1": 61, + "2": 107, + "3": 138, + "4": 167, + "5": 191 + } + }, + { + "metrics": { + "age": 65, + "weight": 113, + "gender": "Male" + }, + "levels": { + "1": 62, + "2": 109, + "3": 140, + "4": 170, + "5": 194 + } + }, + { + "metrics": { + "age": 65, + "weight": 136, + "gender": "Male" + }, + "levels": { + "1": 64, + "2": 112, + "3": 144, + "4": 175, + "5": 200 + } + }, + { + "metrics": { + "age": 75, + "weight": 57, + "gender": "Male" + }, + "levels": { + "1": 29, + "2": 51, + "3": 68, + "4": 85, + "5": 98 + } + }, + { + "metrics": { + "age": 75, + "weight": 68, + "gender": "Male" + }, + "levels": { + "1": 36, + "2": 62, + "3": 82, + "4": 103, + "5": 121 + } + }, + { + "metrics": { + "age": 75, + "weight": 79, + "gender": "Male" + }, + "levels": { + "1": 42, + "2": 74, + "3": 98, + "4": 123, + "5": 144 + } + }, + { + "metrics": { + "age": 75, + "weight": 91, + "gender": "Male" + }, + "levels": { + "1": 45, + "2": 79, + "3": 105, + "4": 131, + "5": 155 + } + }, + { + "metrics": { + "age": 75, + "weight": 102, + "gender": "Male" + }, + "levels": { + "1": 49, + "2": 86, + "3": 114, + "4": 143, + "5": 168 + } + }, + { + "metrics": { + "age": 75, + "weight": 113, + "gender": "Male" + }, + "levels": { + "1": 50, + "2": 87, + "3": 116, + "4": 145, + "5": 170 + } + }, + { + "metrics": { + "age": 75, + "weight": 136, + "gender": "Male" + }, + "levels": { + "1": 52, + "2": 90, + "3": 120, + "4": 150, + "5": 176 + } + }, + { + "metrics": { + "age": 85, + "weight": 57, + "gender": "Male" + }, + "levels": { + "1": 21, + "2": 36, + "3": 48, + "4": 60, + "5": 68 + } + }, + { + "metrics": { + "age": 85, + "weight": 68, + "gender": "Male" + }, + "levels": { + "1": 25, + "2": 43, + "3": 58, + "4": 72, + "5": 85 + } + }, + { + "metrics": { + "age": 85, + "weight": 79, + "gender": "Male" + }, + "levels": { + "1": 30, + "2": 51, + "3": 69, + "4": 86, + "5": 101 + } + }, + { + "metrics": { + "age": 85, + "weight": 91, + "gender": "Male" + }, + "levels": { + "1": 32, + "2": 55, + "3": 74, + "4": 92, + "5": 108 + } + }, + { + "metrics": { + "age": 85, + "weight": 102, + "gender": "Male" + }, + "levels": { + "1": 35, + "2": 60, + "3": 80, + "4": 100, + "5": 118 + } + }, + { + "metrics": { + "age": 85, + "weight": 113, + "gender": "Male" + }, + "levels": { + "1": 35, + "2": 61, + "3": 81, + "4": 101, + "5": 119 + } + }, + { + "metrics": { + "age": 85, + "weight": 136, + "gender": "Male" + }, + "levels": { + "1": 36, + "2": 63, + "3": 84, + "4": 105, + "5": 123 + } + }, + { + "metrics": { + "age": 17, + "weight": 57, + "gender": "Female" + }, + "levels": { + "1": 17, + "2": 39, + "3": 51, + "4": 62, + "5": 82 + } + }, + { + "metrics": { + "age": 17, + "weight": 68, + "gender": "Female" + }, + "levels": { + "1": 22, + "2": 51, + "3": 65, + "4": 80, + "5": 102 + } + }, + { + "metrics": { + "age": 17, + "weight": 79, + "gender": "Female" + }, + "levels": { + "1": 24, + "2": 56, + "3": 72, + "4": 88, + "5": 108 + } + }, + { + "metrics": { + "age": 17, + "weight": 91, + "gender": "Female" + }, + "levels": { + "1": 25, + "2": 59, + "3": 75, + "4": 92, + "5": 113 + } + }, + { + "metrics": { + "age": 17, + "weight": 102, + "gender": "Female" + }, + "levels": { + "1": 26, + "2": 61, + "3": 79, + "4": 99, + "5": 118 + } + }, + { + "metrics": { + "age": 17, + "weight": 113, + "gender": "Female" + }, + "levels": { + "1": 27, + "2": 62, + "3": 80, + "4": 103, + "5": 123 + } + }, + { + "metrics": { + "age": 17, + "weight": 136, + "gender": "Female" + }, + "levels": { + "1": 29, + "2": 69, + "3": 89, + "4": 109, + "5": 129 + } + }, + { + "metrics": { + "age": 25, + "weight": 57, + "gender": "Female" + }, + "levels": { + "1": 25, + "2": 58, + "3": 74, + "4": 91, + "5": 120 + } + }, + { + "metrics": { + "age": 25, + "weight": 68, + "gender": "Female" + }, + "levels": { + "1": 30, + "2": 70, + "3": 90, + "4": 110, + "5": 140 + } + }, + { + "metrics": { + "age": 25, + "weight": 79, + "gender": "Female" + }, + "levels": { + "1": 33, + "2": 77, + "3": 99, + "4": 121, + "5": 150 + } + }, + { + "metrics": { + "age": 25, + "weight": 91, + "gender": "Female" + }, + "levels": { + "1": 34, + "2": 79, + "3": 101, + "4": 124, + "5": 154 + } + }, + { + "metrics": { + "age": 25, + "weight": 102, + "gender": "Female" + }, + "levels": { + "1": 35, + "2": 82, + "3": 107, + "4": 133, + "5": 162 + } + }, + { + "metrics": { + "age": 25, + "weight": 113, + "gender": "Female" + }, + "levels": { + "1": 35, + "2": 82, + "3": 106, + "4": 134, + "5": 164 + } + }, + { + "metrics": { + "age": 25, + "weight": 136, + "gender": "Female" + }, + "levels": { + "1": 36, + "2": 84, + "3": 108, + "4": 132, + "5": 159 + } + }, + { + "metrics": { + "age": 35, + "weight": 57, + "gender": "Female" + }, + "levels": { + "1": 30, + "2": 70, + "3": 90, + "4": 110, + "5": 145 + } + }, + { + "metrics": { + "age": 35, + "weight": 68, + "gender": "Female" + }, + "levels": { + "1": 32, + "2": 74, + "3": 96, + "4": 117, + "5": 149 + } + }, + { + "metrics": { + "age": 35, + "weight": 79, + "gender": "Female" + }, + "levels": { + "1": 35, + "2": 81, + "3": 104, + "4": 127, + "5": 157 + } + }, + { + "metrics": { + "age": 35, + "weight": 91, + "gender": "Female" + }, + "levels": { + "1": 36, + "2": 83, + "3": 107, + "4": 131, + "5": 162 + } + }, + { + "metrics": { + "age": 35, + "weight": 102, + "gender": "Female" + }, + "levels": { + "1": 39, + "2": 91, + "3": 118, + "4": 147, + "5": 179 + } + }, + { + "metrics": { + "age": 35, + "weight": 113, + "gender": "Female" + }, + "levels": { + "1": 40, + "2": 93, + "3": 119, + "4": 151, + "5": 185 + } + }, + { + "metrics": { + "age": 35, + "weight": 136, + "gender": "Female" + }, + "levels": { + "1": 40, + "2": 94, + "3": 120, + "4": 147, + "5": 178 + } + }, + { + "metrics": { + "age": 45, + "weight": 57, + "gender": "Female" + }, + "levels": { + "1": 23, + "2": 54, + "3": 70, + "4": 85, + "5": 112 + } + }, + { + "metrics": { + "age": 45, + "weight": 68, + "gender": "Female" + }, + "levels": { + "1": 29, + "2": 67, + "3": 86, + "4": 105, + "5": 133 + } + }, + { + "metrics": { + "age": 45, + "weight": 79, + "gender": "Female" + }, + "levels": { + "1": 30, + "2": 69, + "3": 89, + "4": 109, + "5": 134 + } + }, + { + "metrics": { + "age": 45, + "weight": 91, + "gender": "Female" + }, + "levels": { + "1": 32, + "2": 74, + "3": 95, + "4": 116, + "5": 143 + } + }, + { + "metrics": { + "age": 45, + "weight": 102, + "gender": "Female" + }, + "levels": { + "1": 33, + "2": 78, + "3": 101, + "4": 126, + "5": 153 + } + }, + { + "metrics": { + "age": 45, + "weight": 113, + "gender": "Female" + }, + "levels": { + "1": 35, + "2": 81, + "3": 104, + "4": 132, + "5": 160 + } + }, + { + "metrics": { + "age": 45, + "weight": 136, + "gender": "Female" + }, + "levels": { + "1": 35, + "2": 82, + "3": 106, + "4": 129, + "5": 156 + } + }, + { + "metrics": { + "age": 55, + "weight": 57, + "gender": "Female" + }, + "levels": { + "1": 22, + "2": 51, + "3": 65, + "4": 80, + "5": 105 + } + }, + { + "metrics": { + "age": 55, + "weight": 68, + "gender": "Female" + }, + "levels": { + "1": 24, + "2": 57, + "3": 73, + "4": 89, + "5": 114 + } + }, + { + "metrics": { + "age": 55, + "weight": 79, + "gender": "Female" + }, + "levels": { + "1": 26, + "2": 60, + "3": 78, + "4": 95, + "5": 117 + } + }, + { + "metrics": { + "age": 55, + "weight": 91, + "gender": "Female" + }, + "levels": { + "1": 28, + "2": 65, + "3": 83, + "4": 102, + "5": 126 + } + }, + { + "metrics": { + "age": 55, + "weight": 102, + "gender": "Female" + }, + "levels": { + "1": 29, + "2": 69, + "3": 90, + "4": 112, + "5": 136 + } + }, + { + "metrics": { + "age": 55, + "weight": 113, + "gender": "Female" + }, + "levels": { + "1": 30, + "2": 69, + "3": 89, + "4": 114, + "5": 137 + } + }, + { + "metrics": { + "age": 55, + "weight": 136, + "gender": "Female" + }, + "levels": { + "1": 31, + "2": 72, + "3": 92, + "4": 113, + "5": 135 + } + }, + { + "metrics": { + "age": 65, + "weight": 57, + "gender": "Female" + }, + "levels": { + "1": 18, + "2": 43, + "3": 55, + "4": 67, + "5": 89 + } + }, + { + "metrics": { + "age": 65, + "weight": 68, + "gender": "Female" + }, + "levels": { + "1": 23, + "2": 53, + "3": 69, + "4": 84, + "5": 107 + } + }, + { + "metrics": { + "age": 65, + "weight": 79, + "gender": "Female" + }, + "levels": { + "1": 24, + "2": 56, + "3": 72, + "4": 89, + "5": 109 + } + }, + { + "metrics": { + "age": 65, + "weight": 91, + "gender": "Female" + }, + "levels": { + "1": 25, + "2": 58, + "3": 74, + "4": 91, + "5": 112 + } + }, + { + "metrics": { + "age": 65, + "weight": 102, + "gender": "Female" + }, + "levels": { + "1": 25, + "2": 60, + "3": 77, + "4": 97, + "5": 116 + } + }, + { + "metrics": { + "age": 65, + "weight": 113, + "gender": "Female" + }, + "levels": { + "1": 26, + "2": 60, + "3": 77, + "4": 99, + "5": 118 + } + }, + { + "metrics": { + "age": 65, + "weight": 136, + "gender": "Female" + }, + "levels": { + "1": 26, + "2": 60, + "3": 78, + "4": 95, + "5": 112 + } + }, + { + "metrics": { + "age": 75, + "weight": 57, + "gender": "Female" + }, + "levels": { + "1": 18, + "2": 42, + "3": 53, + "4": 66, + "5": 84 + } + }, + { + "metrics": { + "age": 75, + "weight": 68, + "gender": "Female" + }, + "levels": { + "1": 18, + "2": 43, + "3": 55, + "4": 67, + "5": 86 + } + }, + { + "metrics": { + "age": 75, + "weight": 79, + "gender": "Female" + }, + "levels": { + "1": 20, + "2": 46, + "3": 59, + "4": 72, + "5": 92 + } + }, + { + "metrics": { + "age": 75, + "weight": 91, + "gender": "Female" + }, + "levels": { + "1": 20, + "2": 47, + "3": 60, + "4": 73, + "5": 93 + } + }, + { + "metrics": { + "age": 75, + "weight": 102, + "gender": "Female" + }, + "levels": { + "1": 20, + "2": 47, + "3": 61, + "4": 79, + "5": 94 + } + }, + { + "metrics": { + "age": 75, + "weight": 113, + "gender": "Female" + }, + "levels": { + "1": 20, + "2": 47, + "3": 61, + "4": 79, + "5": 95 + } + }, + { + "metrics": { + "age": 75, + "weight": 136, + "gender": "Female" + }, + "levels": { + "1": 20, + "2": 48, + "3": 61, + "4": 79, + "5": 95 + } + }, + { + "metrics": { + "age": 85, + "weight": 57, + "gender": "Female" + }, + "levels": { + "1": 15, + "2": 34, + "3": 44, + "4": 53, + "5": 68 + } + }, + { + "metrics": { + "age": 85, + "weight": 68, + "gender": "Female" + }, + "levels": { + "1": 15, + "2": 35, + "3": 45, + "4": 55, + "5": 69 + } + }, + { + "metrics": { + "age": 85, + "weight": 79, + "gender": "Female" + }, + "levels": { + "1": 16, + "2": 37, + "3": 48, + "4": 58, + "5": 74 + } + }, + { + "metrics": { + "age": 85, + "weight": 91, + "gender": "Female" + }, + "levels": { + "1": 16, + "2": 38, + "3": 48, + "4": 59, + "5": 75 + } + }, + { + "metrics": { + "age": 85, + "weight": 102, + "gender": "Female" + }, + "levels": { + "1": 16, + "2": 38, + "3": 50, + "4": 65, + "5": 76 + } + }, + { + "metrics": { + "age": 85, + "weight": 113, + "gender": "Female" + }, + "levels": { + "1": 16, + "2": 38, + "3": 49, + "4": 65, + "5": 77 + } + }, + { + "metrics": { + "age": 85, + "weight": 136, + "gender": "Female" + }, + "levels": { + "1": 17, + "2": 39, + "3": 50, + "4": 65, + "5": 77 + } + } + ] }, - "standards": [ - { - "metrics": { - "age": 17, - "weight": 57, - "gender": "Male" - }, - "levels": { - "1": 45, - "2": 74, - "3": 93, - "4": 103, - "5": 115 - } - }, - { - "metrics": { - "age": 17, - "weight": 68, - "gender": "Male" - }, - "levels": { - "1": 58, - "2": 97, - "3": 121, - "4": 134, - "5": 149 - } - }, - { - "metrics": { - "age": 17, - "weight": 79, - "gender": "Male" - }, - "levels": { - "1": 73, - "2": 122, - "3": 152, - "4": 169, - "5": 188 - } - }, - { - "metrics": { - "age": 17, - "weight": 91, - "gender": "Male" - }, - "levels": { - "1": 77, - "2": 129, - "3": 161, - "4": 179, - "5": 199 - } - }, - { - "metrics": { - "age": 17, - "weight": 102, - "gender": "Male" - }, - "levels": { - "1": 81, - "2": 135, - "3": 169, - "4": 188, - "5": 209 - } - }, - { - "metrics": { - "age": 17, - "weight": 113, - "gender": "Male" - }, - "levels": { - "1": 82, - "2": 137, - "3": 172, - "4": 191, - "5": 212 - } - }, - { - "metrics": { - "age": 17, - "weight": 136, - "gender": "Male" - }, - "levels": { - "1": 84, - "2": 139, - "3": 174, - "4": 194, - "5": 215 - } - }, - { - "metrics": { - "age": 25, - "weight": 57, - "gender": "Male" - }, - "levels": { - "1": 47, - "2": 81, - "3": 101, - "4": 127, - "5": 141 - } - }, - { - "metrics": { - "age": 25, - "weight": 68, - "gender": "Male" - }, - "levels": { - "1": 64, - "2": 112, - "3": 139, - "4": 186, - "5": 207 - } - }, - { - "metrics": { - "age": 25, - "weight": 79, - "gender": "Male" - }, - "levels": { - "1": 76, - "2": 131, - "3": 164, - "4": 219, - "5": 243 - } - }, - { - "metrics": { - "age": 25, - "weight": 91, - "gender": "Male" - }, - "levels": { - "1": 83, - "2": 144, - "3": 180, - "4": 240, - "5": 266 - } - }, - { - "metrics": { - "age": 25, - "weight": 102, - "gender": "Male" - }, - "levels": { - "1": 84, - "2": 146, - "3": 182, - "4": 243, - "5": 270 - } - }, - { - "metrics": { - "age": 25, - "weight": 113, - "gender": "Male" - }, - "levels": { - "1": 85, - "2": 147, - "3": 184, - "4": 245, - "5": 273 - } - }, - { - "metrics": { - "age": 25, - "weight": 136, - "gender": "Male" - }, - "levels": { - "1": 86, - "2": 149, - "3": 187, - "4": 249, - "5": 276 - } - }, - { - "metrics": { - "age": 35, - "weight": 57, - "gender": "Male" - }, - "levels": { - "1": 51, - "2": 89, - "3": 112, - "4": 149, - "5": 175 - } - }, - { - "metrics": { - "age": 35, - "weight": 68, - "gender": "Male" - }, - "levels": { - "1": 64, - "2": 112, - "3": 140, - "4": 186, - "5": 219 - } - }, - { - "metrics": { - "age": 35, - "weight": 79, - "gender": "Male" - }, - "levels": { - "1": 76, - "2": 132, - "3": 165, - "4": 220, - "5": 259 - } - }, - { - "metrics": { - "age": 35, - "weight": 91, - "gender": "Male" - }, - "levels": { - "1": 81, - "2": 142, - "3": 177, - "4": 236, - "5": 278 - } - }, - { - "metrics": { - "age": 35, - "weight": 102, - "gender": "Male" - }, - "levels": { - "1": 83, - "2": 145, - "3": 181, - "4": 242, - "5": 284 - } - }, - { - "metrics": { - "age": 35, - "weight": 113, - "gender": "Male" - }, - "levels": { - "1": 84, - "2": 146, - "3": 183, - "4": 244, - "5": 287 - } - }, - { - "metrics": { - "age": 35, - "weight": 136, - "gender": "Male" - }, - "levels": { - "1": 89, - "2": 154, - "3": 192, - "4": 257, - "5": 302 - } - }, - { - "metrics": { - "age": 45, - "weight": 57, - "gender": "Male" - }, - "levels": { - "1": 46, - "2": 80, - "3": 100, - "4": 118, - "5": 131 - } - }, - { - "metrics": { - "age": 45, - "weight": 68, - "gender": "Male" - }, - "levels": { - "1": 53, - "2": 92, - "3": 115, - "4": 154, - "5": 181 - } - }, - { - "metrics": { - "age": 45, - "weight": 79, - "gender": "Male" - }, - "levels": { - "1": 63, - "2": 109, - "3": 137, - "4": 182, - "5": 214 - } - }, - { - "metrics": { - "age": 45, - "weight": 91, - "gender": "Male" - }, - "levels": { - "1": 65, - "2": 113, - "3": 141, - "4": 188, - "5": 222 - } - }, - { - "metrics": { - "age": 45, - "weight": 102, - "gender": "Male" - }, - "levels": { - "1": 71, - "2": 124, - "3": 154, - "4": 206, - "5": 242 - } - }, - { - "metrics": { - "age": 45, - "weight": 113, - "gender": "Male" - }, - "levels": { - "1": 75, - "2": 130, - "3": 162, - "4": 217, - "5": 255 - } - }, - { - "metrics": { - "age": 45, - "weight": 136, - "gender": "Male" - }, - "levels": { - "1": 76, - "2": 133, - "3": 166, - "4": 221, - "5": 260 - } - }, - { - "metrics": { - "age": 55, - "weight": 57, - "gender": "Male" - }, - "levels": { - "1": 40, - "2": 69, - "3": 86, - "4": 102, - "5": 113 - } - }, - { - "metrics": { - "age": 55, - "weight": 68, - "gender": "Male" - }, - "levels": { - "1": 48, - "2": 84, - "3": 105, - "4": 140, - "5": 164 - } - }, - { - "metrics": { - "age": 55, - "weight": 79, - "gender": "Male" - }, - "levels": { - "1": 56, - "2": 97, - "3": 121, - "4": 161, - "5": 190 - } - }, - { - "metrics": { - "age": 55, - "weight": 91, - "gender": "Male" - }, - "levels": { - "1": 60, - "2": 104, - "3": 130, - "4": 173, - "5": 204 - } - }, - { - "metrics": { - "age": 55, - "weight": 102, - "gender": "Male" - }, - "levels": { - "1": 63, - "2": 110, - "3": 138, - "4": 183, - "5": 216 - } - }, - { - "metrics": { - "age": 55, - "weight": 113, - "gender": "Male" - }, - "levels": { - "1": 64, - "2": 111, - "3": 139, - "4": 185, - "5": 218 - } - }, - { - "metrics": { - "age": 55, - "weight": 136, - "gender": "Male" - }, - "levels": { - "1": 65, - "2": 114, - "3": 142, - "4": 190, - "5": 223 - } - }, - { - "metrics": { - "age": 65, - "weight": 57, - "gender": "Male" - }, - "levels": { - "1": 35, - "2": 61, - "3": 79, - "4": 95, - "5": 109 - } - }, - { - "metrics": { - "age": 65, - "weight": 68, - "gender": "Male" - }, - "levels": { - "1": 46, - "2": 79, - "3": 103, - "4": 124, - "5": 142 - } - }, - { - "metrics": { - "age": 65, - "weight": 79, - "gender": "Male" - }, - "levels": { - "1": 52, - "2": 90, - "3": 117, - "4": 141, - "5": 162 - } - }, - { - "metrics": { - "age": 65, - "weight": 91, - "gender": "Male" - }, - "levels": { - "1": 57, - "2": 98, - "3": 128, - "4": 155, - "5": 177 - } - }, - { - "metrics": { - "age": 65, - "weight": 102, - "gender": "Male" - }, - "levels": { - "1": 61, - "2": 107, - "3": 138, - "4": 167, - "5": 191 - } - }, - { - "metrics": { - "age": 65, - "weight": 113, - "gender": "Male" - }, - "levels": { - "1": 62, - "2": 109, - "3": 140, - "4": 170, - "5": 194 - } - }, - { - "metrics": { - "age": 65, - "weight": 136, - "gender": "Male" - }, - "levels": { - "1": 64, - "2": 112, - "3": 144, - "4": 175, - "5": 200 - } - }, - { - "metrics": { - "age": 75, - "weight": 57, - "gender": "Male" - }, - "levels": { - "1": 29, - "2": 51, - "3": 68, - "4": 85, - "5": 98 - } - }, - { - "metrics": { - "age": 75, - "weight": 68, - "gender": "Male" - }, - "levels": { - "1": 36, - "2": 62, - "3": 82, - "4": 103, - "5": 121 - } - }, - { - "metrics": { - "age": 75, - "weight": 79, - "gender": "Male" - }, - "levels": { - "1": 42, - "2": 74, - "3": 98, - "4": 123, - "5": 144 - } - }, - { - "metrics": { - "age": 75, - "weight": 91, - "gender": "Male" - }, - "levels": { - "1": 45, - "2": 79, - "3": 105, - "4": 131, - "5": 155 - } - }, - { - "metrics": { - "age": 75, - "weight": 102, - "gender": "Male" - }, - "levels": { - "1": 49, - "2": 86, - "3": 114, - "4": 143, - "5": 168 - } - }, - { - "metrics": { - "age": 75, - "weight": 113, - "gender": "Male" - }, - "levels": { - "1": 50, - "2": 87, - "3": 116, - "4": 145, - "5": 170 - } - }, - { - "metrics": { - "age": 75, - "weight": 136, - "gender": "Male" - }, - "levels": { - "1": 52, - "2": 90, - "3": 120, - "4": 150, - "5": 176 - } - }, - { - "metrics": { - "age": 85, - "weight": 57, - "gender": "Male" - }, - "levels": { - "1": 21, - "2": 36, - "3": 48, - "4": 60, - "5": 68 - } - }, - { - "metrics": { - "age": 85, - "weight": 68, - "gender": "Male" - }, - "levels": { - "1": 25, - "2": 43, - "3": 58, - "4": 72, - "5": 85 - } - }, - { - "metrics": { - "age": 85, - "weight": 79, - "gender": "Male" - }, - "levels": { - "1": 30, - "2": 51, - "3": 69, - "4": 86, - "5": 101 - } - }, - { - "metrics": { - "age": 85, - "weight": 91, - "gender": "Male" - }, - "levels": { - "1": 32, - "2": 55, - "3": 74, - "4": 92, - "5": 108 - } - }, - { - "metrics": { - "age": 85, - "weight": 102, - "gender": "Male" - }, - "levels": { - "1": 35, - "2": 60, - "3": 80, - "4": 100, - "5": 118 - } - }, - { - "metrics": { - "age": 85, - "weight": 113, - "gender": "Male" - }, - "levels": { - "1": 35, - "2": 61, - "3": 81, - "4": 101, - "5": 119 - } - }, - { - "metrics": { - "age": 85, - "weight": 136, - "gender": "Male" - }, - "levels": { - "1": 36, - "2": 63, - "3": 84, - "4": 105, - "5": 123 - } - }, - { - "metrics": { - "age": 17, - "weight": 57, - "gender": "Female" - }, - "levels": { - "1": 17, - "2": 39, - "3": 51, - "4": 62, - "5": 82 - } - }, - { - "metrics": { - "age": 17, - "weight": 68, - "gender": "Female" - }, - "levels": { - "1": 22, - "2": 51, - "3": 65, - "4": 80, - "5": 102 - } - }, - { - "metrics": { - "age": 17, - "weight": 79, - "gender": "Female" - }, - "levels": { - "1": 24, - "2": 56, - "3": 72, - "4": 88, - "5": 108 - } - }, - { - "metrics": { - "age": 17, - "weight": 91, - "gender": "Female" - }, - "levels": { - "1": 25, - "2": 59, - "3": 75, - "4": 92, - "5": 113 - } - }, - { - "metrics": { - "age": 17, - "weight": 102, - "gender": "Female" - }, - "levels": { - "1": 26, - "2": 61, - "3": 79, - "4": 99, - "5": 118 - } - }, - { - "metrics": { - "age": 17, - "weight": 113, - "gender": "Female" - }, - "levels": { - "1": 27, - "2": 62, - "3": 80, - "4": 103, - "5": 123 - } - }, - { - "metrics": { - "age": 17, - "weight": 136, - "gender": "Female" - }, - "levels": { - "1": 29, - "2": 69, - "3": 89, - "4": 109, - "5": 129 - } - }, - { - "metrics": { - "age": 25, - "weight": 57, - "gender": "Female" - }, - "levels": { - "1": 25, - "2": 58, - "3": 74, - "4": 91, - "5": 120 - } - }, - { - "metrics": { - "age": 25, - "weight": 68, - "gender": "Female" - }, - "levels": { - "1": 30, - "2": 70, - "3": 90, - "4": 110, - "5": 140 - } - }, - { - "metrics": { - "age": 25, - "weight": 79, - "gender": "Female" - }, - "levels": { - "1": 33, - "2": 77, - "3": 99, - "4": 121, - "5": 150 - } - }, - { - "metrics": { - "age": 25, - "weight": 91, - "gender": "Female" - }, - "levels": { - "1": 34, - "2": 79, - "3": 101, - "4": 124, - "5": 154 - } - }, - { - "metrics": { - "age": 25, - "weight": 102, - "gender": "Female" - }, - "levels": { - "1": 35, - "2": 82, - "3": 107, - "4": 133, - "5": 162 - } - }, - { - "metrics": { - "age": 25, - "weight": 113, - "gender": "Female" - }, - "levels": { - "1": 35, - "2": 82, - "3": 106, - "4": 134, - "5": 164 - } - }, - { - "metrics": { - "age": 25, - "weight": 136, - "gender": "Female" - }, - "levels": { - "1": 36, - "2": 84, - "3": 108, - "4": 132, - "5": 159 - } - }, - { - "metrics": { - "age": 35, - "weight": 57, - "gender": "Female" - }, - "levels": { - "1": 30, - "2": 70, - "3": 90, - "4": 110, - "5": 145 - } - }, - { - "metrics": { - "age": 35, - "weight": 68, - "gender": "Female" - }, - "levels": { - "1": 32, - "2": 74, - "3": 96, - "4": 117, - "5": 149 - } - }, - { - "metrics": { - "age": 35, - "weight": 79, - "gender": "Female" - }, - "levels": { - "1": 35, - "2": 81, - "3": 104, - "4": 127, - "5": 157 - } - }, - { - "metrics": { - "age": 35, - "weight": 91, - "gender": "Female" - }, - "levels": { - "1": 36, - "2": 83, - "3": 107, - "4": 131, - "5": 162 - } - }, - { - "metrics": { - "age": 35, - "weight": 102, - "gender": "Female" - }, - "levels": { - "1": 39, - "2": 91, - "3": 118, - "4": 147, - "5": 179 - } - }, - { - "metrics": { - "age": 35, - "weight": 113, - "gender": "Female" - }, - "levels": { - "1": 40, - "2": 93, - "3": 119, - "4": 151, - "5": 185 - } - }, - { - "metrics": { - "age": 35, - "weight": 136, - "gender": "Female" - }, - "levels": { - "1": 40, - "2": 94, - "3": 120, - "4": 147, - "5": 178 - } - }, - { - "metrics": { - "age": 45, - "weight": 57, - "gender": "Female" - }, - "levels": { - "1": 23, - "2": 54, - "3": 70, - "4": 85, - "5": 112 - } - }, - { - "metrics": { - "age": 45, - "weight": 68, - "gender": "Female" - }, - "levels": { - "1": 29, - "2": 67, - "3": 86, - "4": 105, - "5": 133 - } - }, - { - "metrics": { - "age": 45, - "weight": 79, - "gender": "Female" - }, - "levels": { - "1": 30, - "2": 69, - "3": 89, - "4": 109, - "5": 134 - } - }, - { - "metrics": { - "age": 45, - "weight": 91, - "gender": "Female" - }, - "levels": { - "1": 32, - "2": 74, - "3": 95, - "4": 116, - "5": 143 - } - }, - { - "metrics": { - "age": 45, - "weight": 102, - "gender": "Female" - }, - "levels": { - "1": 33, - "2": 78, - "3": 101, - "4": 126, - "5": 153 - } - }, - { - "metrics": { - "age": 45, - "weight": 113, - "gender": "Female" - }, - "levels": { - "1": 35, - "2": 81, - "3": 104, - "4": 132, - "5": 160 - } - }, - { - "metrics": { - "age": 45, - "weight": 136, - "gender": "Female" - }, - "levels": { - "1": 35, - "2": 82, - "3": 106, - "4": 129, - "5": 156 - } - }, - { - "metrics": { - "age": 55, - "weight": 57, - "gender": "Female" - }, - "levels": { - "1": 22, - "2": 51, - "3": 65, - "4": 80, - "5": 105 - } - }, - { - "metrics": { - "age": 55, - "weight": 68, - "gender": "Female" - }, - "levels": { - "1": 24, - "2": 57, - "3": 73, - "4": 89, - "5": 114 - } - }, - { - "metrics": { - "age": 55, - "weight": 79, - "gender": "Female" - }, - "levels": { - "1": 26, - "2": 60, - "3": 78, - "4": 95, - "5": 117 - } - }, - { - "metrics": { - "age": 55, - "weight": 91, - "gender": "Female" - }, - "levels": { - "1": 28, - "2": 65, - "3": 83, - "4": 102, - "5": 126 - } - }, - { - "metrics": { - "age": 55, - "weight": 102, - "gender": "Female" - }, - "levels": { - "1": 29, - "2": 69, - "3": 90, - "4": 112, - "5": 136 - } - }, - { - "metrics": { - "age": 55, - "weight": 113, - "gender": "Female" - }, - "levels": { - "1": 30, - "2": 69, - "3": 89, - "4": 114, - "5": 137 - } - }, - { - "metrics": { - "age": 55, - "weight": 136, - "gender": "Female" - }, - "levels": { - "1": 31, - "2": 72, - "3": 92, - "4": 113, - "5": 135 - } - }, - { - "metrics": { - "age": 65, - "weight": 57, - "gender": "Female" - }, - "levels": { - "1": 18, - "2": 43, - "3": 55, - "4": 67, - "5": 89 - } - }, - { - "metrics": { - "age": 65, - "weight": 68, - "gender": "Female" - }, - "levels": { - "1": 23, - "2": 53, - "3": 69, - "4": 84, - "5": 107 - } - }, - { - "metrics": { - "age": 65, - "weight": 79, - "gender": "Female" - }, - "levels": { - "1": 24, - "2": 56, - "3": 72, - "4": 89, - "5": 109 - } - }, - { - "metrics": { - "age": 65, - "weight": 91, - "gender": "Female" - }, - "levels": { - "1": 25, - "2": 58, - "3": 74, - "4": 91, - "5": 112 - } - }, - { - "metrics": { - "age": 65, - "weight": 102, - "gender": "Female" - }, - "levels": { - "1": 25, - "2": 60, - "3": 77, - "4": 97, - "5": 116 - } - }, - { - "metrics": { - "age": 65, - "weight": 113, - "gender": "Female" - }, - "levels": { - "1": 26, - "2": 60, - "3": 77, - "4": 99, - "5": 118 - } - }, - { - "metrics": { - "age": 65, - "weight": 136, - "gender": "Female" - }, - "levels": { - "1": 26, - "2": 60, - "3": 78, - "4": 95, - "5": 112 - } - }, - { - "metrics": { - "age": 75, - "weight": 57, - "gender": "Female" - }, - "levels": { - "1": 18, - "2": 42, - "3": 53, - "4": 66, - "5": 84 - } - }, - { - "metrics": { - "age": 75, - "weight": 68, - "gender": "Female" - }, - "levels": { - "1": 18, - "2": 43, - "3": 55, - "4": 67, - "5": 86 - } - }, - { - "metrics": { - "age": 75, - "weight": 79, - "gender": "Female" - }, - "levels": { - "1": 20, - "2": 46, - "3": 59, - "4": 72, - "5": 92 - } - }, - { - "metrics": { - "age": 75, - "weight": 91, - "gender": "Female" - }, - "levels": { - "1": 20, - "2": 47, - "3": 60, - "4": 73, - "5": 93 - } - }, - { - "metrics": { - "age": 75, - "weight": 102, - "gender": "Female" - }, - "levels": { - "1": 20, - "2": 47, - "3": 61, - "4": 79, - "5": 94 - } - }, - { - "metrics": { - "age": 75, - "weight": 113, - "gender": "Female" - }, - "levels": { - "1": 20, - "2": 47, - "3": 61, - "4": 79, - "5": 95 - } - }, - { - "metrics": { - "age": 75, - "weight": 136, - "gender": "Female" - }, - "levels": { - "1": 20, - "2": 48, - "3": 61, - "4": 79, - "5": 95 - } - }, - { - "metrics": { - "age": 85, - "weight": 57, - "gender": "Female" - }, - "levels": { - "1": 15, - "2": 34, - "3": 44, - "4": 53, - "5": 68 - } - }, - { - "metrics": { - "age": 85, - "weight": 68, - "gender": "Female" - }, - "levels": { - "1": 15, - "2": 35, - "3": 45, - "4": 55, - "5": 69 - } - }, - { - "metrics": { - "age": 85, - "weight": 79, - "gender": "Female" - }, - "levels": { - "1": 16, - "2": 37, - "3": 48, - "4": 58, - "5": 74 - } - }, - { - "metrics": { - "age": 85, - "weight": 91, - "gender": "Female" - }, - "levels": { - "1": 16, - "2": 38, - "3": 48, - "4": 59, - "5": 75 - } - }, - { - "metrics": { - "age": 85, - "weight": 102, - "gender": "Female" - }, - "levels": { - "1": 16, - "2": 38, - "3": 50, - "4": 65, - "5": 76 - } - }, - { - "metrics": { - "age": 85, - "weight": 113, - "gender": "Female" - }, - "levels": { - "1": 16, - "2": 38, - "3": 49, - "4": 65, - "5": 77 - } - }, - { - "metrics": { - "age": 85, - "weight": 136, - "gender": "Female" - }, - "levels": { - "1": 17, - "2": 39, - "3": 50, - "4": 65, - "5": 77 - } - } - ] - }, - "BenchPress": { - "metadata": { - "attribute": "Strength", - "generators": [], - "unit": "kg", - "name": "Bench Press" + "BenchPress": { + "metadata": { + "attribute": "Strength", + "generators": [], + "unit": "kg", + "name": "Bench Press" + }, + "standards": [ + { + "metrics": { + "age": 17, + "weight": 57, + "gender": "Male" + }, + "levels": { + "1": 34, + "2": 47, + "3": 61, + "4": 74, + "5": 85 + } + }, + { + "metrics": { + "age": 17, + "weight": 68, + "gender": "Male" + }, + "levels": { + "1": 42, + "2": 59, + "3": 76, + "4": 92, + "5": 107 + } + }, + { + "metrics": { + "age": 17, + "weight": 79, + "gender": "Male" + }, + "levels": { + "1": 43, + "2": 60, + "3": 77, + "4": 94, + "5": 110 + } + }, + { + "metrics": { + "age": 17, + "weight": 91, + "gender": "Male" + }, + "levels": { + "1": 51, + "2": 71, + "3": 91, + "4": 111, + "5": 132 + } + }, + { + "metrics": { + "age": 17, + "weight": 102, + "gender": "Male" + }, + "levels": { + "1": 53, + "2": 74, + "3": 95, + "4": 116, + "5": 134 + } + }, + { + "metrics": { + "age": 17, + "weight": 113, + "gender": "Male" + }, + "levels": { + "1": 53, + "2": 74, + "3": 96, + "4": 117, + "5": 137 + } + }, + { + "metrics": { + "age": 17, + "weight": 136, + "gender": "Male" + }, + "levels": { + "1": 53, + "2": 74, + "3": 95, + "4": 116, + "5": 138 + } + }, + { + "metrics": { + "age": 25, + "weight": 57, + "gender": "Male" + }, + "levels": { + "1": 36, + "2": 51, + "3": 65, + "4": 80, + "5": 92 + } + }, + { + "metrics": { + "age": 25, + "weight": 68, + "gender": "Male" + }, + "levels": { + "1": 50, + "2": 70, + "3": 90, + "4": 110, + "5": 130 + } + }, + { + "metrics": { + "age": 25, + "weight": 79, + "gender": "Male" + }, + "levels": { + "1": 50, + "2": 70, + "3": 90, + "4": 110, + "5": 150 + } + }, + { + "metrics": { + "age": 25, + "weight": 91, + "gender": "Male" + }, + "levels": { + "1": 52, + "2": 73, + "3": 93, + "4": 114, + "5": 161 + } + }, + { + "metrics": { + "age": 25, + "weight": 102, + "gender": "Male" + }, + "levels": { + "1": 55, + "2": 77, + "3": 99, + "4": 121, + "5": 170 + } + }, + { + "metrics": { + "age": 25, + "weight": 113, + "gender": "Male" + }, + "levels": { + "1": 59, + "2": 82, + "3": 106, + "4": 129, + "5": 181 + } + }, + { + "metrics": { + "age": 25, + "weight": 136, + "gender": "Male" + }, + "levels": { + "1": 61, + "2": 85, + "3": 109, + "4": 133, + "5": 182 + } + }, + { + "metrics": { + "age": 35, + "weight": 57, + "gender": "Male" + }, + "levels": { + "1": 39, + "2": 55, + "3": 71, + "4": 87, + "5": 114 + } + }, + { + "metrics": { + "age": 35, + "weight": 68, + "gender": "Male" + }, + "levels": { + "1": 53, + "2": 75, + "3": 96, + "4": 117, + "5": 155 + } + }, + { + "metrics": { + "age": 35, + "weight": 79, + "gender": "Male" + }, + "levels": { + "1": 54, + "2": 75, + "3": 97, + "4": 119, + "5": 156 + } + }, + { + "metrics": { + "age": 35, + "weight": 91, + "gender": "Male" + }, + "levels": { + "1": 59, + "2": 83, + "3": 107, + "4": 131, + "5": 173 + } + }, + { + "metrics": { + "age": 35, + "weight": 102, + "gender": "Male" + }, + "levels": { + "1": 61, + "2": 84, + "3": 108, + "4": 135, + "5": 176 + } + }, + { + "metrics": { + "age": 35, + "weight": 113, + "gender": "Male" + }, + "levels": { + "1": 62, + "2": 85, + "3": 110, + "4": 139, + "5": 177 + } + }, + { + "metrics": { + "age": 35, + "weight": 136, + "gender": "Male" + }, + "levels": { + "1": 67, + "2": 94, + "3": 121, + "4": 148, + "5": 195 + } + }, + { + "metrics": { + "age": 45, + "weight": 57, + "gender": "Male" + }, + "levels": { + "1": 37, + "2": 52, + "3": 66, + "4": 81, + "5": 105 + } + }, + { + "metrics": { + "age": 45, + "weight": 68, + "gender": "Male" + }, + "levels": { + "1": 41, + "2": 57, + "3": 73, + "4": 89, + "5": 116 + } + }, + { + "metrics": { + "age": 45, + "weight": 79, + "gender": "Male" + }, + "levels": { + "1": 48, + "2": 67, + "3": 86, + "4": 105, + "5": 135 + } + }, + { + "metrics": { + "age": 45, + "weight": 91, + "gender": "Male" + }, + "levels": { + "1": 54, + "2": 75, + "3": 97, + "4": 118, + "5": 143 + } + }, + { + "metrics": { + "age": 45, + "weight": 102, + "gender": "Male" + }, + "levels": { + "1": 56, + "2": 78, + "3": 100, + "4": 122, + "5": 154 + } + }, + { + "metrics": { + "age": 45, + "weight": 113, + "gender": "Male" + }, + "levels": { + "1": 58, + "2": 81, + "3": 104, + "4": 127, + "5": 164 + } + }, + { + "metrics": { + "age": 45, + "weight": 136, + "gender": "Male" + }, + "levels": { + "1": 62, + "2": 87, + "3": 111, + "4": 136, + "5": 166 + } + }, + { + "metrics": { + "age": 55, + "weight": 57, + "gender": "Male" + }, + "levels": { + "1": 29, + "2": 41, + "3": 53, + "4": 65, + "5": 74 + } + }, + { + "metrics": { + "age": 55, + "weight": 68, + "gender": "Male" + }, + "levels": { + "1": 40, + "2": 56, + "3": 72, + "4": 88, + "5": 104 + } + }, + { + "metrics": { + "age": 55, + "weight": 79, + "gender": "Male" + }, + "levels": { + "1": 43, + "2": 60, + "3": 77, + "4": 94, + "5": 111 + } + }, + { + "metrics": { + "age": 55, + "weight": 91, + "gender": "Male" + }, + "levels": { + "1": 49, + "2": 68, + "3": 88, + "4": 107, + "5": 129 + } + }, + { + "metrics": { + "age": 55, + "weight": 102, + "gender": "Male" + }, + "levels": { + "1": 53, + "2": 74, + "3": 95, + "4": 116, + "5": 140 + } + }, + { + "metrics": { + "age": 55, + "weight": 113, + "gender": "Male" + }, + "levels": { + "1": 54, + "2": 75, + "3": 97, + "4": 118, + "5": 143 + } + }, + { + "metrics": { + "age": 55, + "weight": 136, + "gender": "Male" + }, + "levels": { + "1": 54, + "2": 76, + "3": 98, + "4": 120, + "5": 145 + } + }, + { + "metrics": { + "age": 65, + "weight": 57, + "gender": "Male" + }, + "levels": { + "1": 25, + "2": 35, + "3": 45, + "4": 55, + "5": 68 + } + }, + { + "metrics": { + "age": 65, + "weight": 68, + "gender": "Male" + }, + "levels": { + "1": 32, + "2": 45, + "3": 57, + "4": 70, + "5": 88 + } + }, + { + "metrics": { + "age": 65, + "weight": 79, + "gender": "Male" + }, + "levels": { + "1": 36, + "2": 50, + "3": 64, + "4": 78, + "5": 99 + } + }, + { + "metrics": { + "age": 65, + "weight": 91, + "gender": "Male" + }, + "levels": { + "1": 47, + "2": 66, + "3": 84, + "4": 103, + "5": 131 + } + }, + { + "metrics": { + "age": 65, + "weight": 102, + "gender": "Male" + }, + "levels": { + "1": 48, + "2": 67, + "3": 87, + "4": 106, + "5": 134 + } + }, + { + "metrics": { + "age": 65, + "weight": 113, + "gender": "Male" + }, + "levels": { + "1": 49, + "2": 68, + "3": 88, + "4": 107, + "5": 136 + } + }, + { + "metrics": { + "age": 65, + "weight": 136, + "gender": "Male" + }, + "levels": { + "1": 50, + "2": 69, + "3": 89, + "4": 109, + "5": 138 + } + }, + { + "metrics": { + "age": 75, + "weight": 57, + "gender": "Male" + }, + "levels": { + "1": 23, + "2": 32, + "3": 41, + "4": 50, + "5": 60 + } + }, + { + "metrics": { + "age": 75, + "weight": 68, + "gender": "Male" + }, + "levels": { + "1": 29, + "2": 40, + "3": 52, + "4": 63, + "5": 78 + } + }, + { + "metrics": { + "age": 75, + "weight": 79, + "gender": "Male" + }, + "levels": { + "1": 33, + "2": 46, + "3": 59, + "4": 72, + "5": 89 + } + }, + { + "metrics": { + "age": 75, + "weight": 91, + "gender": "Male" + }, + "levels": { + "1": 34, + "2": 48, + "3": 62, + "4": 76, + "5": 94 + } + }, + { + "metrics": { + "age": 75, + "weight": 102, + "gender": "Male" + }, + "levels": { + "1": 36, + "2": 51, + "3": 65, + "4": 80, + "5": 99 + } + }, + { + "metrics": { + "age": 75, + "weight": 113, + "gender": "Male" + }, + "levels": { + "1": 44, + "2": 61, + "3": 79, + "4": 96, + "5": 121 + } + }, + { + "metrics": { + "age": 75, + "weight": 136, + "gender": "Male" + }, + "levels": { + "1": 48, + "2": 67, + "3": 86, + "4": 105, + "5": 131 + } + }, + { + "metrics": { + "age": 85, + "weight": 57, + "gender": "Male" + }, + "levels": { + "1": 16, + "2": 22, + "3": 28, + "4": 34, + "5": 41 + } + }, + { + "metrics": { + "age": 85, + "weight": 68, + "gender": "Male" + }, + "levels": { + "1": 20, + "2": 28, + "3": 36, + "4": 44, + "5": 54 + } + }, + { + "metrics": { + "age": 85, + "weight": 79, + "gender": "Male" + }, + "levels": { + "1": 22, + "2": 31, + "3": 40, + "4": 49, + "5": 61 + } + }, + { + "metrics": { + "age": 85, + "weight": 91, + "gender": "Male" + }, + "levels": { + "1": 24, + "2": 33, + "3": 43, + "4": 52, + "5": 65 + } + }, + { + "metrics": { + "age": 85, + "weight": 102, + "gender": "Male" + }, + "levels": { + "1": 25, + "2": 35, + "3": 45, + "4": 55, + "5": 69 + } + }, + { + "metrics": { + "age": 85, + "weight": 113, + "gender": "Male" + }, + "levels": { + "1": 30, + "2": 42, + "3": 54, + "4": 66, + "5": 83 + } + }, + { + "metrics": { + "age": 85, + "weight": 136, + "gender": "Male" + }, + "levels": { + "1": 33, + "2": 46, + "3": 59, + "4": 72, + "5": 91 + } + }, + { + "metrics": { + "age": 17, + "weight": 57, + "gender": "Female" + }, + "levels": { + "1": 14, + "2": 19, + "3": 27, + "4": 37, + "5": 51 + } + }, + { + "metrics": { + "age": 17, + "weight": 68, + "gender": "Female" + }, + "levels": { + "1": 17, + "2": 23, + "3": 32, + "4": 44, + "5": 61 + } + }, + { + "metrics": { + "age": 17, + "weight": 79, + "gender": "Female" + }, + "levels": { + "1": 19, + "2": 27, + "3": 37, + "4": 51, + "5": 70 + } + }, + { + "metrics": { + "age": 17, + "weight": 91, + "gender": "Female" + }, + "levels": { + "1": 21, + "2": 28, + "3": 39, + "4": 54, + "5": 75 + } + }, + { + "metrics": { + "age": 17, + "weight": 102, + "gender": "Female" + }, + "levels": { + "1": 22, + "2": 30, + "3": 41, + "4": 57, + "5": 78 + } + }, + { + "metrics": { + "age": 17, + "weight": 113, + "gender": "Female" + }, + "levels": { + "1": 22, + "2": 31, + "3": 42, + "4": 58, + "5": 81 + } + }, + { + "metrics": { + "age": 17, + "weight": 136, + "gender": "Female" + }, + "levels": { + "1": 23, + "2": 32, + "3": 44, + "4": 61, + "5": 84 + } + }, + { + "metrics": { + "age": 25, + "weight": 57, + "gender": "Female" + }, + "levels": { + "1": 14, + "2": 19, + "3": 26, + "4": 36, + "5": 49 + } + }, + { + "metrics": { + "age": 25, + "weight": 68, + "gender": "Female" + }, + "levels": { + "1": 19, + "2": 27, + "3": 37, + "4": 51, + "5": 70 + } + }, + { + "metrics": { + "age": 25, + "weight": 79, + "gender": "Female" + }, + "levels": { + "1": 21, + "2": 29, + "3": 40, + "4": 56, + "5": 77 + } + }, + { + "metrics": { + "age": 25, + "weight": 91, + "gender": "Female" + }, + "levels": { + "1": 22, + "2": 30, + "3": 42, + "4": 57, + "5": 79 + } + }, + { + "metrics": { + "age": 25, + "weight": 102, + "gender": "Female" + }, + "levels": { + "1": 25, + "2": 34, + "3": 47, + "4": 64, + "5": 89 + } + }, + { + "metrics": { + "age": 25, + "weight": 113, + "gender": "Female" + }, + "levels": { + "1": 25, + "2": 35, + "3": 48, + "4": 66, + "5": 91 + } + }, + { + "metrics": { + "age": 25, + "weight": 136, + "gender": "Female" + }, + "levels": { + "1": 28, + "2": 39, + "3": 54, + "4": 75, + "5": 103 + } + }, + { + "metrics": { + "age": 35, + "weight": 57, + "gender": "Female" + }, + "levels": { + "1": 12, + "2": 17, + "3": 22, + "4": 27, + "5": 57 + } + }, + { + "metrics": { + "age": 35, + "weight": 68, + "gender": "Female" + }, + "levels": { + "1": 30, + "2": 41, + "3": 53, + "4": 65, + "5": 83 + } + }, + { + "metrics": { + "age": 35, + "weight": 79, + "gender": "Female" + }, + "levels": { + "1": 33, + "2": 45, + "3": 60, + "4": 73, + "5": 89 + } + }, + { + "metrics": { + "age": 35, + "weight": 91, + "gender": "Female" + }, + "levels": { + "1": 35, + "2": 51, + "3": 66, + "4": 80, + "5": 98 + } + }, + { + "metrics": { + "age": 35, + "weight": 102, + "gender": "Female" + }, + "levels": { + "1": 38, + "2": 51, + "3": 67, + "4": 84, + "5": 100 + } + }, + { + "metrics": { + "age": 35, + "weight": 113, + "gender": "Female" + }, + "levels": { + "1": 39, + "2": 52, + "3": 66, + "4": 86, + "5": 102 + } + }, + { + "metrics": { + "age": 35, + "weight": 136, + "gender": "Female" + }, + "levels": { + "1": 41, + "2": 58, + "3": 74, + "4": 90, + "5": 106 + } + }, + { + "metrics": { + "age": 45, + "weight": 57, + "gender": "Female" + }, + "levels": { + "1": 14, + "2": 14, + "3": 25, + "4": 25, + "5": 46 + } + }, + { + "metrics": { + "age": 45, + "weight": 68, + "gender": "Female" + }, + "levels": { + "1": 20, + "2": 31, + "3": 36, + "4": 56, + "5": 66 + } + }, + { + "metrics": { + "age": 45, + "weight": 79, + "gender": "Female" + }, + "levels": { + "1": 23, + "2": 37, + "3": 41, + "4": 67, + "5": 75 + } + }, + { + "metrics": { + "age": 45, + "weight": 91, + "gender": "Female" + }, + "levels": { + "1": 24, + "2": 44, + "3": 44, + "4": 80, + "5": 81 + } + }, + { + "metrics": { + "age": 45, + "weight": 102, + "gender": "Female" + }, + "levels": { + "1": 26, + "2": 32, + "3": 44, + "4": 59, + "5": 85 + } + }, + { + "metrics": { + "age": 45, + "weight": 113, + "gender": "Female" + }, + "levels": { + "1": 26, + "2": 41, + "3": 48, + "4": 75, + "5": 87 + } + }, + { + "metrics": { + "age": 45, + "weight": 136, + "gender": "Female" + }, + "levels": { + "1": 28, + "2": 44, + "3": 51, + "4": 79, + "5": 93 + } + }, + { + "metrics": { + "age": 55, + "weight": 57, + "gender": "Female" + }, + "levels": { + "1": 7, + "2": 13, + "3": 17, + "4": 23, + "5": 43 + } + }, + { + "metrics": { + "age": 55, + "weight": 68, + "gender": "Female" + }, + "levels": { + "1": 16, + "2": 22, + "3": 32, + "4": 37, + "5": 61 + } + }, + { + "metrics": { + "age": 55, + "weight": 79, + "gender": "Female" + }, + "levels": { + "1": 18, + "2": 30, + "3": 40, + "4": 55, + "5": 68 + } + }, + { + "metrics": { + "age": 55, + "weight": 91, + "gender": "Female" + }, + "levels": { + "1": 18, + "2": 31, + "3": 41, + "4": 56, + "5": 69 + } + }, + { + "metrics": { + "age": 55, + "weight": 102, + "gender": "Female" + }, + "levels": { + "1": 15, + "2": 27, + "3": 35, + "4": 49, + "5": 72 + } + }, + { + "metrics": { + "age": 55, + "weight": 113, + "gender": "Female" + }, + "levels": { + "1": 18, + "2": 32, + "3": 42, + "4": 58, + "5": 75 + } + }, + { + "metrics": { + "age": 55, + "weight": 136, + "gender": "Female" + }, + "levels": { + "1": 21, + "2": 36, + "3": 48, + "4": 66, + "5": 82 + } + }, + { + "metrics": { + "age": 65, + "weight": 57, + "gender": "Female" + }, + "levels": { + "1": 9, + "2": 13, + "3": 17, + "4": 38, + "5": 38 + } + }, + { + "metrics": { + "age": 65, + "weight": 68, + "gender": "Female" + }, + "levels": { + "1": 18, + "2": 33, + "3": 44, + "4": 40, + "5": 53 + } + }, + { + "metrics": { + "age": 65, + "weight": 79, + "gender": "Female" + }, + "levels": { + "1": 20, + "2": 25, + "3": 34, + "4": 46, + "5": 61 + } + }, + { + "metrics": { + "age": 65, + "weight": 91, + "gender": "Female" + }, + "levels": { + "1": 22, + "2": 33, + "3": 48, + "4": 66, + "5": 62 + } + }, + { + "metrics": { + "age": 65, + "weight": 102, + "gender": "Female" + }, + "levels": { + "1": 12, + "2": 20, + "3": 27, + "4": 37, + "5": 65 + } + }, + { + "metrics": { + "age": 65, + "weight": 113, + "gender": "Female" + }, + "levels": { + "1": 18, + "2": 31, + "3": 41, + "4": 56, + "5": 69 + } + }, + { + "metrics": { + "age": 65, + "weight": 136, + "gender": "Female" + }, + "levels": { + "1": 20, + "2": 45, + "3": 58, + "4": 65, + "5": 70 + } + }, + { + "metrics": { + "age": 75, + "weight": 57, + "gender": "Female" + }, + "levels": { + "1": 9, + "2": 12, + "3": 16, + "4": 21, + "5": 28 + } + }, + { + "metrics": { + "age": 75, + "weight": 68, + "gender": "Female" + }, + "levels": { + "1": 14, + "2": 18, + "3": 24, + "4": 32, + "5": 43 + } + }, + { + "metrics": { + "age": 75, + "weight": 79, + "gender": "Female" + }, + "levels": { + "1": 16, + "2": 21, + "3": 28, + "4": 37, + "5": 49 + } + }, + { + "metrics": { + "age": 75, + "weight": 91, + "gender": "Female" + }, + "levels": { + "1": 17, + "2": 23, + "3": 30, + "4": 40, + "5": 54 + } + }, + { + "metrics": { + "age": 75, + "weight": 102, + "gender": "Female" + }, + "levels": { + "1": 17, + "2": 23, + "3": 31, + "4": 41, + "5": 55 + } + }, + { + "metrics": { + "age": 75, + "weight": 113, + "gender": "Female" + }, + "levels": { + "1": 18, + "2": 24, + "3": 32, + "4": 42, + "5": 56 + } + }, + { + "metrics": { + "age": 75, + "weight": 136, + "gender": "Female" + }, + "levels": { + "1": 19, + "2": 25, + "3": 33, + "4": 44, + "5": 59 + } + }, + { + "metrics": { + "age": 85, + "weight": 57, + "gender": "Female" + }, + "levels": { + "1": 8, + "2": 10, + "3": 14, + "4": 18, + "5": 24 + } + }, + { + "metrics": { + "age": 85, + "weight": 68, + "gender": "Female" + }, + "levels": { + "1": 9, + "2": 14, + "3": 19, + "4": 25, + "5": 34 + } + }, + { + "metrics": { + "age": 85, + "weight": 79, + "gender": "Female" + }, + "levels": { + "1": 12, + "2": 16, + "3": 22, + "4": 29, + "5": 39 + } + }, + { + "metrics": { + "age": 85, + "weight": 91, + "gender": "Female" + }, + "levels": { + "1": 13, + "2": 18, + "3": 24, + "4": 32, + "5": 43 + } + }, + { + "metrics": { + "age": 85, + "weight": 102, + "gender": "Female" + }, + "levels": { + "1": 14, + "2": 19, + "3": 25, + "4": 33, + "5": 44 + } + }, + { + "metrics": { + "age": 85, + "weight": 113, + "gender": "Female" + }, + "levels": { + "1": 15, + "2": 19, + "3": 26, + "4": 34, + "5": 46 + } + }, + { + "metrics": { + "age": 85, + "weight": 136, + "gender": "Female" + }, + "levels": { + "1": 15, + "2": 20, + "3": 27, + "4": 36, + "5": 48 + } + } + ] }, - "standards": [ - { - "metrics": { - "age": 17, - "weight": 57, - "gender": "Male" - }, - "levels": { - "1": 34, - "2": 47, - "3": 61, - "4": 74, - "5": 85 - } - }, - { - "metrics": { - "age": 17, - "weight": 68, - "gender": "Male" - }, - "levels": { - "1": 42, - "2": 59, - "3": 76, - "4": 92, - "5": 107 - } - }, - { - "metrics": { - "age": 17, - "weight": 79, - "gender": "Male" - }, - "levels": { - "1": 43, - "2": 60, - "3": 77, - "4": 94, - "5": 110 - } - }, - { - "metrics": { - "age": 17, - "weight": 91, - "gender": "Male" - }, - "levels": { - "1": 51, - "2": 71, - "3": 91, - "4": 111, - "5": 132 - } - }, - { - "metrics": { - "age": 17, - "weight": 102, - "gender": "Male" - }, - "levels": { - "1": 53, - "2": 74, - "3": 95, - "4": 116, - "5": 134 - } - }, - { - "metrics": { - "age": 17, - "weight": 113, - "gender": "Male" - }, - "levels": { - "1": 53, - "2": 74, - "3": 96, - "4": 117, - "5": 137 - } - }, - { - "metrics": { - "age": 17, - "weight": 136, - "gender": "Male" - }, - "levels": { - "1": 53, - "2": 74, - "3": 95, - "4": 116, - "5": 138 - } - }, - { - "metrics": { - "age": 25, - "weight": 57, - "gender": "Male" - }, - "levels": { - "1": 36, - "2": 51, - "3": 65, - "4": 80, - "5": 92 - } - }, - { - "metrics": { - "age": 25, - "weight": 68, - "gender": "Male" - }, - "levels": { - "1": 50, - "2": 70, - "3": 90, - "4": 110, - "5": 130 - } - }, - { - "metrics": { - "age": 25, - "weight": 79, - "gender": "Male" - }, - "levels": { - "1": 50, - "2": 70, - "3": 90, - "4": 110, - "5": 150 - } - }, - { - "metrics": { - "age": 25, - "weight": 91, - "gender": "Male" - }, - "levels": { - "1": 52, - "2": 73, - "3": 93, - "4": 114, - "5": 161 - } - }, - { - "metrics": { - "age": 25, - "weight": 102, - "gender": "Male" - }, - "levels": { - "1": 55, - "2": 77, - "3": 99, - "4": 121, - "5": 170 - } - }, - { - "metrics": { - "age": 25, - "weight": 113, - "gender": "Male" - }, - "levels": { - "1": 59, - "2": 82, - "3": 106, - "4": 129, - "5": 181 - } - }, - { - "metrics": { - "age": 25, - "weight": 136, - "gender": "Male" - }, - "levels": { - "1": 61, - "2": 85, - "3": 109, - "4": 133, - "5": 182 - } - }, - { - "metrics": { - "age": 35, - "weight": 57, - "gender": "Male" - }, - "levels": { - "1": 39, - "2": 55, - "3": 71, - "4": 87, - "5": 114 - } - }, - { - "metrics": { - "age": 35, - "weight": 68, - "gender": "Male" - }, - "levels": { - "1": 53, - "2": 75, - "3": 96, - "4": 117, - "5": 155 - } - }, - { - "metrics": { - "age": 35, - "weight": 79, - "gender": "Male" - }, - "levels": { - "1": 54, - "2": 75, - "3": 97, - "4": 119, - "5": 156 - } - }, - { - "metrics": { - "age": 35, - "weight": 91, - "gender": "Male" - }, - "levels": { - "1": 59, - "2": 83, - "3": 107, - "4": 131, - "5": 173 - } - }, - { - "metrics": { - "age": 35, - "weight": 102, - "gender": "Male" - }, - "levels": { - "1": 61, - "2": 84, - "3": 108, - "4": 135, - "5": 176 - } - }, - { - "metrics": { - "age": 35, - "weight": 113, - "gender": "Male" - }, - "levels": { - "1": 62, - "2": 85, - "3": 110, - "4": 139, - "5": 177 - } - }, - { - "metrics": { - "age": 35, - "weight": 136, - "gender": "Male" - }, - "levels": { - "1": 67, - "2": 94, - "3": 121, - "4": 148, - "5": 195 - } - }, - { - "metrics": { - "age": 45, - "weight": 57, - "gender": "Male" - }, - "levels": { - "1": 37, - "2": 52, - "3": 66, - "4": 81, - "5": 105 - } - }, - { - "metrics": { - "age": 45, - "weight": 68, - "gender": "Male" - }, - "levels": { - "1": 41, - "2": 57, - "3": 73, - "4": 89, - "5": 116 - } - }, - { - "metrics": { - "age": 45, - "weight": 79, - "gender": "Male" - }, - "levels": { - "1": 48, - "2": 67, - "3": 86, - "4": 105, - "5": 135 - } - }, - { - "metrics": { - "age": 45, - "weight": 91, - "gender": "Male" - }, - "levels": { - "1": 54, - "2": 75, - "3": 97, - "4": 118, - "5": 143 - } - }, - { - "metrics": { - "age": 45, - "weight": 102, - "gender": "Male" - }, - "levels": { - "1": 56, - "2": 78, - "3": 100, - "4": 122, - "5": 154 - } - }, - { - "metrics": { - "age": 45, - "weight": 113, - "gender": "Male" - }, - "levels": { - "1": 58, - "2": 81, - "3": 104, - "4": 127, - "5": 164 - } - }, - { - "metrics": { - "age": 45, - "weight": 136, - "gender": "Male" - }, - "levels": { - "1": 62, - "2": 87, - "3": 111, - "4": 136, - "5": 166 - } - }, - { - "metrics": { - "age": 55, - "weight": 57, - "gender": "Male" - }, - "levels": { - "1": 29, - "2": 41, - "3": 53, - "4": 65, - "5": 74 - } - }, - { - "metrics": { - "age": 55, - "weight": 68, - "gender": "Male" - }, - "levels": { - "1": 40, - "2": 56, - "3": 72, - "4": 88, - "5": 104 - } - }, - { - "metrics": { - "age": 55, - "weight": 79, - "gender": "Male" - }, - "levels": { - "1": 43, - "2": 60, - "3": 77, - "4": 94, - "5": 111 - } - }, - { - "metrics": { - "age": 55, - "weight": 91, - "gender": "Male" - }, - "levels": { - "1": 49, - "2": 68, - "3": 88, - "4": 107, - "5": 129 - } - }, - { - "metrics": { - "age": 55, - "weight": 102, - "gender": "Male" - }, - "levels": { - "1": 53, - "2": 74, - "3": 95, - "4": 116, - "5": 140 - } - }, - { - "metrics": { - "age": 55, - "weight": 113, - "gender": "Male" - }, - "levels": { - "1": 54, - "2": 75, - "3": 97, - "4": 118, - "5": 143 - } - }, - { - "metrics": { - "age": 55, - "weight": 136, - "gender": "Male" - }, - "levels": { - "1": 54, - "2": 76, - "3": 98, - "4": 120, - "5": 145 - } - }, - { - "metrics": { - "age": 65, - "weight": 57, - "gender": "Male" - }, - "levels": { - "1": 25, - "2": 35, - "3": 45, - "4": 55, - "5": 68 - } - }, - { - "metrics": { - "age": 65, - "weight": 68, - "gender": "Male" - }, - "levels": { - "1": 32, - "2": 45, - "3": 57, - "4": 70, - "5": 88 - } - }, - { - "metrics": { - "age": 65, - "weight": 79, - "gender": "Male" - }, - "levels": { - "1": 36, - "2": 50, - "3": 64, - "4": 78, - "5": 99 - } - }, - { - "metrics": { - "age": 65, - "weight": 91, - "gender": "Male" - }, - "levels": { - "1": 47, - "2": 66, - "3": 84, - "4": 103, - "5": 131 - } - }, - { - "metrics": { - "age": 65, - "weight": 102, - "gender": "Male" - }, - "levels": { - "1": 48, - "2": 67, - "3": 87, - "4": 106, - "5": 134 - } - }, - { - "metrics": { - "age": 65, - "weight": 113, - "gender": "Male" - }, - "levels": { - "1": 49, - "2": 68, - "3": 88, - "4": 107, - "5": 136 - } - }, - { - "metrics": { - "age": 65, - "weight": 136, - "gender": "Male" - }, - "levels": { - "1": 50, - "2": 69, - "3": 89, - "4": 109, - "5": 138 - } - }, - { - "metrics": { - "age": 75, - "weight": 57, - "gender": "Male" - }, - "levels": { - "1": 23, - "2": 32, - "3": 41, - "4": 50, - "5": 60 - } - }, - { - "metrics": { - "age": 75, - "weight": 68, - "gender": "Male" - }, - "levels": { - "1": 29, - "2": 40, - "3": 52, - "4": 63, - "5": 78 - } - }, - { - "metrics": { - "age": 75, - "weight": 79, - "gender": "Male" - }, - "levels": { - "1": 33, - "2": 46, - "3": 59, - "4": 72, - "5": 89 - } - }, - { - "metrics": { - "age": 75, - "weight": 91, - "gender": "Male" - }, - "levels": { - "1": 34, - "2": 48, - "3": 62, - "4": 76, - "5": 94 - } - }, - { - "metrics": { - "age": 75, - "weight": 102, - "gender": "Male" - }, - "levels": { - "1": 36, - "2": 51, - "3": 65, - "4": 80, - "5": 99 - } - }, - { - "metrics": { - "age": 75, - "weight": 113, - "gender": "Male" - }, - "levels": { - "1": 44, - "2": 61, - "3": 79, - "4": 96, - "5": 121 - } - }, - { - "metrics": { - "age": 75, - "weight": 136, - "gender": "Male" - }, - "levels": { - "1": 48, - "2": 67, - "3": 86, - "4": 105, - "5": 131 - } - }, - { - "metrics": { - "age": 85, - "weight": 57, - "gender": "Male" - }, - "levels": { - "1": 16, - "2": 22, - "3": 28, - "4": 34, - "5": 41 - } - }, - { - "metrics": { - "age": 85, - "weight": 68, - "gender": "Male" - }, - "levels": { - "1": 20, - "2": 28, - "3": 36, - "4": 44, - "5": 54 - } - }, - { - "metrics": { - "age": 85, - "weight": 79, - "gender": "Male" - }, - "levels": { - "1": 22, - "2": 31, - "3": 40, - "4": 49, - "5": 61 - } - }, - { - "metrics": { - "age": 85, - "weight": 91, - "gender": "Male" - }, - "levels": { - "1": 24, - "2": 33, - "3": 43, - "4": 52, - "5": 65 - } - }, - { - "metrics": { - "age": 85, - "weight": 102, - "gender": "Male" - }, - "levels": { - "1": 25, - "2": 35, - "3": 45, - "4": 55, - "5": 69 - } - }, - { - "metrics": { - "age": 85, - "weight": 113, - "gender": "Male" - }, - "levels": { - "1": 30, - "2": 42, - "3": 54, - "4": 66, - "5": 83 - } - }, - { - "metrics": { - "age": 85, - "weight": 136, - "gender": "Male" - }, - "levels": { - "1": 33, - "2": 46, - "3": 59, - "4": 72, - "5": 91 - } - }, - { - "metrics": { - "age": 17, - "weight": 57, - "gender": "Female" - }, - "levels": { - "1": 14, - "2": 19, - "3": 27, - "4": 37, - "5": 51 - } - }, - { - "metrics": { - "age": 17, - "weight": 68, - "gender": "Female" - }, - "levels": { - "1": 17, - "2": 23, - "3": 32, - "4": 44, - "5": 61 - } - }, - { - "metrics": { - "age": 17, - "weight": 79, - "gender": "Female" - }, - "levels": { - "1": 19, - "2": 27, - "3": 37, - "4": 51, - "5": 70 - } - }, - { - "metrics": { - "age": 17, - "weight": 91, - "gender": "Female" - }, - "levels": { - "1": 21, - "2": 28, - "3": 39, - "4": 54, - "5": 75 - } - }, - { - "metrics": { - "age": 17, - "weight": 102, - "gender": "Female" - }, - "levels": { - "1": 22, - "2": 30, - "3": 41, - "4": 57, - "5": 78 - } - }, - { - "metrics": { - "age": 17, - "weight": 113, - "gender": "Female" - }, - "levels": { - "1": 22, - "2": 31, - "3": 42, - "4": 58, - "5": 81 - } - }, - { - "metrics": { - "age": 17, - "weight": 136, - "gender": "Female" - }, - "levels": { - "1": 23, - "2": 32, - "3": 44, - "4": 61, - "5": 84 - } - }, - { - "metrics": { - "age": 25, - "weight": 57, - "gender": "Female" - }, - "levels": { - "1": 14, - "2": 19, - "3": 26, - "4": 36, - "5": 49 - } - }, - { - "metrics": { - "age": 25, - "weight": 68, - "gender": "Female" - }, - "levels": { - "1": 19, - "2": 27, - "3": 37, - "4": 51, - "5": 70 - } - }, - { - "metrics": { - "age": 25, - "weight": 79, - "gender": "Female" - }, - "levels": { - "1": 21, - "2": 29, - "3": 40, - "4": 56, - "5": 77 - } - }, - { - "metrics": { - "age": 25, - "weight": 91, - "gender": "Female" - }, - "levels": { - "1": 22, - "2": 30, - "3": 42, - "4": 57, - "5": 79 - } - }, - { - "metrics": { - "age": 25, - "weight": 102, - "gender": "Female" - }, - "levels": { - "1": 25, - "2": 34, - "3": 47, - "4": 64, - "5": 89 - } - }, - { - "metrics": { - "age": 25, - "weight": 113, - "gender": "Female" - }, - "levels": { - "1": 25, - "2": 35, - "3": 48, - "4": 66, - "5": 91 - } - }, - { - "metrics": { - "age": 25, - "weight": 136, - "gender": "Female" - }, - "levels": { - "1": 28, - "2": 39, - "3": 54, - "4": 75, - "5": 103 - } - }, - { - "metrics": { - "age": 35, - "weight": 57, - "gender": "Female" - }, - "levels": { - "1": 12, - "2": 17, - "3": 22, - "4": 27, - "5": 57 - } - }, - { - "metrics": { - "age": 35, - "weight": 68, - "gender": "Female" - }, - "levels": { - "1": 30, - "2": 41, - "3": 53, - "4": 65, - "5": 83 - } - }, - { - "metrics": { - "age": 35, - "weight": 79, - "gender": "Female" - }, - "levels": { - "1": 33, - "2": 45, - "3": 60, - "4": 73, - "5": 89 - } - }, - { - "metrics": { - "age": 35, - "weight": 91, - "gender": "Female" - }, - "levels": { - "1": 35, - "2": 51, - "3": 66, - "4": 80, - "5": 98 - } - }, - { - "metrics": { - "age": 35, - "weight": 102, - "gender": "Female" - }, - "levels": { - "1": 38, - "2": 51, - "3": 67, - "4": 84, - "5": 100 - } - }, - { - "metrics": { - "age": 35, - "weight": 113, - "gender": "Female" - }, - "levels": { - "1": 39, - "2": 52, - "3": 66, - "4": 86, - "5": 102 - } - }, - { - "metrics": { - "age": 35, - "weight": 136, - "gender": "Female" - }, - "levels": { - "1": 41, - "2": 58, - "3": 74, - "4": 90, - "5": 106 - } - }, - { - "metrics": { - "age": 45, - "weight": 57, - "gender": "Female" - }, - "levels": { - "1": 14, - "2": 14, - "3": 25, - "4": 25, - "5": 46 - } - }, - { - "metrics": { - "age": 45, - "weight": 68, - "gender": "Female" - }, - "levels": { - "1": 20, - "2": 31, - "3": 36, - "4": 56, - "5": 66 - } - }, - { - "metrics": { - "age": 45, - "weight": 79, - "gender": "Female" - }, - "levels": { - "1": 23, - "2": 37, - "3": 41, - "4": 67, - "5": 75 - } - }, - { - "metrics": { - "age": 45, - "weight": 91, - "gender": "Female" - }, - "levels": { - "1": 24, - "2": 44, - "3": 44, - "4": 80, - "5": 81 - } - }, - { - "metrics": { - "age": 45, - "weight": 102, - "gender": "Female" - }, - "levels": { - "1": 26, - "2": 32, - "3": 44, - "4": 59, - "5": 85 - } - }, - { - "metrics": { - "age": 45, - "weight": 113, - "gender": "Female" - }, - "levels": { - "1": 26, - "2": 41, - "3": 48, - "4": 75, - "5": 87 - } - }, - { - "metrics": { - "age": 45, - "weight": 136, - "gender": "Female" - }, - "levels": { - "1": 28, - "2": 44, - "3": 51, - "4": 79, - "5": 93 - } - }, - { - "metrics": { - "age": 55, - "weight": 57, - "gender": "Female" - }, - "levels": { - "1": 7, - "2": 13, - "3": 17, - "4": 23, - "5": 43 - } - }, - { - "metrics": { - "age": 55, - "weight": 68, - "gender": "Female" - }, - "levels": { - "1": 16, - "2": 22, - "3": 32, - "4": 37, - "5": 61 - } - }, - { - "metrics": { - "age": 55, - "weight": 79, - "gender": "Female" - }, - "levels": { - "1": 18, - "2": 30, - "3": 40, - "4": 55, - "5": 68 - } - }, - { - "metrics": { - "age": 55, - "weight": 91, - "gender": "Female" - }, - "levels": { - "1": 18, - "2": 31, - "3": 41, - "4": 56, - "5": 69 - } - }, - { - "metrics": { - "age": 55, - "weight": 102, - "gender": "Female" - }, - "levels": { - "1": 15, - "2": 27, - "3": 35, - "4": 49, - "5": 72 - } - }, - { - "metrics": { - "age": 55, - "weight": 113, - "gender": "Female" - }, - "levels": { - "1": 18, - "2": 32, - "3": 42, - "4": 58, - "5": 75 - } - }, - { - "metrics": { - "age": 55, - "weight": 136, - "gender": "Female" - }, - "levels": { - "1": 21, - "2": 36, - "3": 48, - "4": 66, - "5": 82 - } - }, - { - "metrics": { - "age": 65, - "weight": 57, - "gender": "Female" - }, - "levels": { - "1": 9, - "2": 13, - "3": 17, - "4": 38, - "5": 38 - } - }, - { - "metrics": { - "age": 65, - "weight": 68, - "gender": "Female" - }, - "levels": { - "1": 18, - "2": 33, - "3": 44, - "4": 40, - "5": 53 - } - }, - { - "metrics": { - "age": 65, - "weight": 79, - "gender": "Female" - }, - "levels": { - "1": 20, - "2": 25, - "3": 34, - "4": 46, - "5": 61 - } - }, - { - "metrics": { - "age": 65, - "weight": 91, - "gender": "Female" - }, - "levels": { - "1": 22, - "2": 33, - "3": 48, - "4": 66, - "5": 62 - } - }, - { - "metrics": { - "age": 65, - "weight": 102, - "gender": "Female" - }, - "levels": { - "1": 12, - "2": 20, - "3": 27, - "4": 37, - "5": 65 - } - }, - { - "metrics": { - "age": 65, - "weight": 113, - "gender": "Female" - }, - "levels": { - "1": 18, - "2": 31, - "3": 41, - "4": 56, - "5": 69 - } - }, - { - "metrics": { - "age": 65, - "weight": 136, - "gender": "Female" - }, - "levels": { - "1": 20, - "2": 45, - "3": 58, - "4": 65, - "5": 70 - } - }, - { - "metrics": { - "age": 75, - "weight": 57, - "gender": "Female" - }, - "levels": { - "1": 9, - "2": 12, - "3": 16, - "4": 21, - "5": 28 - } - }, - { - "metrics": { - "age": 75, - "weight": 68, - "gender": "Female" - }, - "levels": { - "1": 14, - "2": 18, - "3": 24, - "4": 32, - "5": 43 - } - }, - { - "metrics": { - "age": 75, - "weight": 79, - "gender": "Female" - }, - "levels": { - "1": 16, - "2": 21, - "3": 28, - "4": 37, - "5": 49 - } - }, - { - "metrics": { - "age": 75, - "weight": 91, - "gender": "Female" - }, - "levels": { - "1": 17, - "2": 23, - "3": 30, - "4": 40, - "5": 54 - } - }, - { - "metrics": { - "age": 75, - "weight": 102, - "gender": "Female" - }, - "levels": { - "1": 17, - "2": 23, - "3": 31, - "4": 41, - "5": 55 - } - }, - { - "metrics": { - "age": 75, - "weight": 113, - "gender": "Female" - }, - "levels": { - "1": 18, - "2": 24, - "3": 32, - "4": 42, - "5": 56 - } - }, - { - "metrics": { - "age": 75, - "weight": 136, - "gender": "Female" - }, - "levels": { - "1": 19, - "2": 25, - "3": 33, - "4": 44, - "5": 59 - } - }, - { - "metrics": { - "age": 85, - "weight": 57, - "gender": "Female" - }, - "levels": { - "1": 8, - "2": 10, - "3": 14, - "4": 18, - "5": 24 - } - }, - { - "metrics": { - "age": 85, - "weight": 68, - "gender": "Female" - }, - "levels": { - "1": 9, - "2": 14, - "3": 19, - "4": 25, - "5": 34 - } - }, - { - "metrics": { - "age": 85, - "weight": 79, - "gender": "Female" - }, - "levels": { - "1": 12, - "2": 16, - "3": 22, - "4": 29, - "5": 39 - } - }, - { - "metrics": { - "age": 85, - "weight": 91, - "gender": "Female" - }, - "levels": { - "1": 13, - "2": 18, - "3": 24, - "4": 32, - "5": 43 - } - }, - { - "metrics": { - "age": 85, - "weight": 102, - "gender": "Female" - }, - "levels": { - "1": 14, - "2": 19, - "3": 25, - "4": 33, - "5": 44 - } - }, - { - "metrics": { - "age": 85, - "weight": 113, - "gender": "Female" - }, - "levels": { - "1": 15, - "2": 19, - "3": 26, - "4": 34, - "5": 46 - } - }, - { - "metrics": { - "age": 85, - "weight": 136, - "gender": "Female" - }, - "levels": { - "1": 15, - "2": 20, - "3": 27, - "4": 36, - "5": 48 - } - } - ] - }, - "Run": { - "metadata": { - "attribute": "Endurance", - "generators": [ - { - "metric": "weight" - } - ], - "unit": "ms", - "name": "Mile Run" + "Run": { + "metadata": { + "attribute": "Endurance", + "generators": [ + { + "metric": "weight" + } + ], + "unit": "ms", + "name": "Mile Run" + }, + "standards": [ + { + "metrics": { + "age": 10, + "weight": 0, + "gender": "Male" + }, + "levels": { + "1": 676000, + "2": 560000, + "3": 475000, + "4": 414000, + "5": 369000 + } + }, + { + "metrics": { + "age": 15, + "weight": 0, + "gender": "Male" + }, + "levels": { + "1": 585000, + "2": 485000, + "3": 411000, + "4": 358000, + "5": 319000 + } + }, + { + "metrics": { + "age": 20, + "weight": 0, + "gender": "Male" + }, + "levels": { + "1": 565000, + "2": 468000, + "3": 398000, + "4": 346000, + "5": 308000 + } + }, + { + "metrics": { + "age": 25, + "weight": 0, + "gender": "Male" + }, + "levels": { + "1": 565000, + "2": 468000, + "3": 398000, + "4": 346000, + "5": 308000 + } + }, + { + "metrics": { + "age": 30, + "weight": 0, + "gender": "Male" + }, + "levels": { + "1": 566000, + "2": 469000, + "3": 398000, + "4": 346000, + "5": 309000 + } + }, + { + "metrics": { + "age": 35, + "weight": 0, + "gender": "Male" + }, + "levels": { + "1": 575000, + "2": 476000, + "3": 404000, + "4": 352000, + "5": 314000 + } + }, + { + "metrics": { + "age": 40, + "weight": 0, + "gender": "Male" + }, + "levels": { + "1": 595000, + "2": 493000, + "3": 418000, + "4": 364000, + "5": 325000 + } + }, + { + "metrics": { + "age": 45, + "weight": 0, + "gender": "Male" + }, + "levels": { + "1": 617000, + "2": 511000, + "3": 434000, + "4": 378000, + "5": 337000 + } + }, + { + "metrics": { + "age": 50, + "weight": 0, + "gender": "Male" + }, + "levels": { + "1": 642000, + "2": 531000, + "3": 451000, + "4": 393000, + "5": 350000 + } + }, + { + "metrics": { + "age": 55, + "weight": 0, + "gender": "Male" + }, + "levels": { + "1": 668000, + "2": 553000, + "3": 469000, + "4": 409000, + "5": 364000 + } + }, + { + "metrics": { + "age": 60, + "weight": 0, + "gender": "Male" + }, + "levels": { + "1": 696000, + "2": 577000, + "3": 489000, + "4": 426000, + "5": 380000 + } + }, + { + "metrics": { + "age": 65, + "weight": 0, + "gender": "Male" + }, + "levels": { + "1": 727000, + "2": 602000, + "3": 511000, + "4": 445000, + "5": 397000 + } + }, + { + "metrics": { + "age": 70, + "weight": 0, + "gender": "Male" + }, + "levels": { + "1": 763000, + "2": 632000, + "3": 537000, + "4": 467000, + "5": 417000 + } + }, + { + "metrics": { + "age": 75, + "weight": 0, + "gender": "Male" + }, + "levels": { + "1": 820000, + "2": 680000, + "3": 577000, + "4": 502000, + "5": 448000 + } + }, + { + "metrics": { + "age": 80, + "weight": 0, + "gender": "Male" + }, + "levels": { + "1": 908000, + "2": 752000, + "3": 638000, + "4": 556000, + "5": 495000 + } + }, + { + "metrics": { + "age": 85, + "weight": 0, + "gender": "Male" + }, + "levels": { + "1": 1045000, + "2": 866000, + "3": 735000, + "4": 640000, + "5": 570000 + } + }, + { + "metrics": { + "age": 90, + "weight": 0, + "gender": "Male" + }, + "levels": { + "1": 1273000, + "2": 1055000, + "3": 895000, + "4": 779000, + "5": 695000 + } + }, + { + "metrics": { + "age": 10, + "weight": 0, + "gender": "Female" + }, + "levels": { + "1": 749000, + "2": 632000, + "3": 543000, + "4": 477000, + "5": 428000 + } + }, + { + "metrics": { + "age": 15, + "weight": 0, + "gender": "Female" + }, + "levels": { + "1": 672000, + "2": 567000, + "3": 488000, + "4": 428000, + "5": 384000 + } + }, + { + "metrics": { + "age": 20, + "weight": 0, + "gender": "Female" + }, + "levels": { + "1": 640000, + "2": 540000, + "3": 464000, + "4": 408000, + "5": 366000 + } + }, + { + "metrics": { + "age": 25, + "weight": 0, + "gender": "Female" + }, + "levels": { + "1": 640000, + "2": 540000, + "3": 464000, + "4": 408000, + "5": 366000 + } + }, + { + "metrics": { + "age": 30, + "weight": 0, + "gender": "Female" + }, + "levels": { + "1": 640000, + "2": 540000, + "3": 464000, + "4": 408000, + "5": 366000 + } + }, + { + "metrics": { + "age": 35, + "weight": 0, + "gender": "Female" + }, + "levels": { + "1": 644000, + "2": 544000, + "3": 467000, + "4": 410000, + "5": 368000 + } + }, + { + "metrics": { + "age": 40, + "weight": 0, + "gender": "Female" + }, + "levels": { + "1": 658000, + "2": 555000, + "3": 477000, + "4": 419000, + "5": 376000 + } + }, + { + "metrics": { + "age": 45, + "weight": 0, + "gender": "Female" + }, + "levels": { + "1": 681000, + "2": 575000, + "3": 494000, + "4": 434000, + "5": 389000 + } + }, + { + "metrics": { + "age": 50, + "weight": 0, + "gender": "Female" + }, + "levels": { + "1": 716000, + "2": 604000, + "3": 520000, + "4": 456000, + "5": 409000 + } + }, + { + "metrics": { + "age": 55, + "weight": 0, + "gender": "Female" + }, + "levels": { + "1": 757000, + "2": 639000, + "3": 550000, + "4": 482000, + "5": 433000 + } + }, + { + "metrics": { + "age": 60, + "weight": 0, + "gender": "Female" + }, + "levels": { + "1": 804000, + "2": 678000, + "3": 583000, + "4": 512000, + "5": 459000 + } + }, + { + "metrics": { + "age": 65, + "weight": 0, + "gender": "Female" + }, + "levels": { + "1": 856000, + "2": 722000, + "3": 621000, + "4": 545000, + "5": 489000 + } + }, + { + "metrics": { + "age": 70, + "weight": 0, + "gender": "Female" + }, + "levels": { + "1": 915000, + "2": 772000, + "3": 664000, + "4": 583000, + "5": 523000 + } + }, + { + "metrics": { + "age": 75, + "weight": 0, + "gender": "Female" + }, + "levels": { + "1": 983000, + "2": 830000, + "3": 713000, + "4": 626000, + "5": 562000 + } + }, + { + "metrics": { + "age": 80, + "weight": 0, + "gender": "Female" + }, + "levels": { + "1": 1065000, + "2": 898000, + "3": 772000, + "4": 678000, + "5": 608000 + } + }, + { + "metrics": { + "age": 85, + "weight": 0, + "gender": "Female" + }, + "levels": { + "1": 1199000, + "2": 1012000, + "3": 870000, + "4": 763000, + "5": 685000 + } + }, + { + "metrics": { + "age": 90, + "weight": 0, + "gender": "Female" + }, + "levels": { + "1": 1445000, + "2": 1219000, + "3": 1048000, + "4": 920000, + "5": 825000 + } + } + ] }, - "standards": [ - { - "metrics": { - "age": 10, - "weight": 0, - "gender": "Male" + "BroadJump": { + "metadata": { + "attribute": "Power", + "generators": [ + { + "metric": "age" + }, + { + "metric": "weight" + } + ], + "unit": "cm", + "name": "Broad Jump" }, - "levels": { - "1": 676000, - "2": 560000, - "3": 475000, - "4": 414000, - "5": 369000 - } - }, - { - "metrics": { - "age": 15, - "weight": 0, - "gender": "Male" - }, - "levels": { - "1": 585000, - "2": 485000, - "3": 411000, - "4": 358000, - "5": 319000 - } - }, - { - "metrics": { - "age": 20, - "weight": 0, - "gender": "Male" - }, - "levels": { - "1": 565000, - "2": 468000, - "3": 398000, - "4": 346000, - "5": 308000 - } - }, - { - "metrics": { - "age": 25, - "weight": 0, - "gender": "Male" - }, - "levels": { - "1": 565000, - "2": 468000, - "3": 398000, - "4": 346000, - "5": 308000 - } - }, - { - "metrics": { - "age": 30, - "weight": 0, - "gender": "Male" - }, - "levels": { - "1": 566000, - "2": 469000, - "3": 398000, - "4": 346000, - "5": 309000 - } - }, - { - "metrics": { - "age": 35, - "weight": 0, - "gender": "Male" - }, - "levels": { - "1": 575000, - "2": 476000, - "3": 404000, - "4": 352000, - "5": 314000 - } - }, - { - "metrics": { - "age": 40, - "weight": 0, - "gender": "Male" - }, - "levels": { - "1": 595000, - "2": 493000, - "3": 418000, - "4": 364000, - "5": 325000 - } - }, - { - "metrics": { - "age": 45, - "weight": 0, - "gender": "Male" - }, - "levels": { - "1": 617000, - "2": 511000, - "3": 434000, - "4": 378000, - "5": 337000 - } - }, - { - "metrics": { - "age": 50, - "weight": 0, - "gender": "Male" - }, - "levels": { - "1": 642000, - "2": 531000, - "3": 451000, - "4": 393000, - "5": 350000 - } - }, - { - "metrics": { - "age": 55, - "weight": 0, - "gender": "Male" - }, - "levels": { - "1": 668000, - "2": 553000, - "3": 469000, - "4": 409000, - "5": 364000 - } - }, - { - "metrics": { - "age": 60, - "weight": 0, - "gender": "Male" - }, - "levels": { - "1": 696000, - "2": 577000, - "3": 489000, - "4": 426000, - "5": 380000 - } - }, - { - "metrics": { - "age": 65, - "weight": 0, - "gender": "Male" - }, - "levels": { - "1": 727000, - "2": 602000, - "3": 511000, - "4": 445000, - "5": 397000 - } - }, - { - "metrics": { - "age": 70, - "weight": 0, - "gender": "Male" - }, - "levels": { - "1": 763000, - "2": 632000, - "3": 537000, - "4": 467000, - "5": 417000 - } - }, - { - "metrics": { - "age": 75, - "weight": 0, - "gender": "Male" - }, - "levels": { - "1": 820000, - "2": 680000, - "3": 577000, - "4": 502000, - "5": 448000 - } - }, - { - "metrics": { - "age": 80, - "weight": 0, - "gender": "Male" - }, - "levels": { - "1": 908000, - "2": 752000, - "3": 638000, - "4": 556000, - "5": 495000 - } - }, - { - "metrics": { - "age": 85, - "weight": 0, - "gender": "Male" - }, - "levels": { - "1": 1045000, - "2": 866000, - "3": 735000, - "4": 640000, - "5": 570000 - } - }, - { - "metrics": { - "age": 90, - "weight": 0, - "gender": "Male" - }, - "levels": { - "1": 1273000, - "2": 1055000, - "3": 895000, - "4": 779000, - "5": 695000 - } - }, - { - "metrics": { - "age": 10, - "weight": 0, - "gender": "Female" - }, - "levels": { - "1": 749000, - "2": 632000, - "3": 543000, - "4": 477000, - "5": 428000 - } - }, - { - "metrics": { - "age": 15, - "weight": 0, - "gender": "Female" - }, - "levels": { - "1": 672000, - "2": 567000, - "3": 488000, - "4": 428000, - "5": 384000 - } - }, - { - "metrics": { - "age": 20, - "weight": 0, - "gender": "Female" - }, - "levels": { - "1": 640000, - "2": 540000, - "3": 464000, - "4": 408000, - "5": 366000 - } - }, - { - "metrics": { - "age": 25, - "weight": 0, - "gender": "Female" - }, - "levels": { - "1": 640000, - "2": 540000, - "3": 464000, - "4": 408000, - "5": 366000 - } - }, - { - "metrics": { - "age": 30, - "weight": 0, - "gender": "Female" - }, - "levels": { - "1": 640000, - "2": 540000, - "3": 464000, - "4": 408000, - "5": 366000 - } - }, - { - "metrics": { - "age": 35, - "weight": 0, - "gender": "Female" - }, - "levels": { - "1": 644000, - "2": 544000, - "3": 467000, - "4": 410000, - "5": 368000 - } - }, - { - "metrics": { - "age": 40, - "weight": 0, - "gender": "Female" - }, - "levels": { - "1": 658000, - "2": 555000, - "3": 477000, - "4": 419000, - "5": 376000 - } - }, - { - "metrics": { - "age": 45, - "weight": 0, - "gender": "Female" - }, - "levels": { - "1": 681000, - "2": 575000, - "3": 494000, - "4": 434000, - "5": 389000 - } - }, - { - "metrics": { - "age": 50, - "weight": 0, - "gender": "Female" - }, - "levels": { - "1": 716000, - "2": 604000, - "3": 520000, - "4": 456000, - "5": 409000 - } - }, - { - "metrics": { - "age": 55, - "weight": 0, - "gender": "Female" - }, - "levels": { - "1": 757000, - "2": 639000, - "3": 550000, - "4": 482000, - "5": 433000 - } - }, - { - "metrics": { - "age": 60, - "weight": 0, - "gender": "Female" - }, - "levels": { - "1": 804000, - "2": 678000, - "3": 583000, - "4": 512000, - "5": 459000 - } - }, - { - "metrics": { - "age": 65, - "weight": 0, - "gender": "Female" - }, - "levels": { - "1": 856000, - "2": 722000, - "3": 621000, - "4": 545000, - "5": 489000 - } - }, - { - "metrics": { - "age": 70, - "weight": 0, - "gender": "Female" - }, - "levels": { - "1": 915000, - "2": 772000, - "3": 664000, - "4": 583000, - "5": 523000 - } - }, - { - "metrics": { - "age": 75, - "weight": 0, - "gender": "Female" - }, - "levels": { - "1": 983000, - "2": 830000, - "3": 713000, - "4": 626000, - "5": 562000 - } - }, - { - "metrics": { - "age": 80, - "weight": 0, - "gender": "Female" - }, - "levels": { - "1": 1065000, - "2": 898000, - "3": 772000, - "4": 678000, - "5": 608000 - } - }, - { - "metrics": { - "age": 85, - "weight": 0, - "gender": "Female" - }, - "levels": { - "1": 1199000, - "2": 1012000, - "3": 870000, - "4": 763000, - "5": 685000 - } - }, - { - "metrics": { - "age": 90, - "weight": 0, - "gender": "Female" - }, - "levels": { - "1": 1445000, - "2": 1219000, - "3": 1048000, - "4": 920000, - "5": 825000 - } - } - ] - }, - "BroadJump": { - "metadata": { - "attribute": "Power", - "generators": [ - { - "metric": "age" - }, - { - "metric": "weight" - } - ], - "unit": "cm", - "name": "Broad Jump" + "standards": [ + { + "metrics": { + "age": 27, + "weight": 0, + "gender": "Male" + }, + "levels": { + "1": 200, + "2": 230, + "3": 250, + "4": 270, + "5": 300 + } + }, + { + "metrics": { + "age": 27, + "weight": 0, + "gender": "Female" + }, + "levels": { + "1": 170, + "2": 190, + "3": 220, + "4": 250, + "5": 280 + } + } + ] }, - "standards": [ - { - "metrics": { - "age": 27, - "weight": 0, - "gender": "Male" + "ConeDrill": { + "metadata": { + "attribute": "Agility", + "generators": [ + { + "metric": "weight" + }, + { + "metric": "age" + } + ], + "unit": "ms", + "name": "3 Cone Drill" }, - "levels": { - "1": 200, - "2": 230, - "3": 250, - "4": 270, - "5": 300 - } - }, - { - "metrics": { - "age": 27, - "weight": 0, - "gender": "Female" - }, - "levels": { - "1": 170, - "2": 190, - "3": 220, - "4": 250, - "5": 280 - } - } - ] - }, - "ConeDrill": { - "metadata": { - "attribute": "Agility", - "generators": [ - { - "metric": "weight" - }, - { - "metric": "age" - } - ], - "unit": "ms", - "name": "3 Cone Drill" - }, - "standards": [ - { - "metrics": { - "age": 27, - "weight": 0, - "gender": "Male" - }, - "levels": { - "1": 9500, - "2": 8700, - "3": 7800, - "4": 7000, - "5": 6000 - } - }, - { - "metrics": { - "age": 27, - "weight": 0, - "gender": "Female" - }, - "levels": { - "1": 10500, - "2": 9600, - "3": 8700, - "4": 7800, - "5": 6700 - } - } - ] - } + "standards": [ + { + "metrics": { + "age": 27, + "weight": 0, + "gender": "Male" + }, + "levels": { + "1": 9500, + "2": 8700, + "3": 7800, + "4": 7000, + "5": 6000 + } + }, + { + "metrics": { + "age": 27, + "weight": 0, + "gender": "Female" + }, + "levels": { + "1": 10500, + "2": 9600, + "3": 8700, + "4": 7800, + "5": 6700 + } + } + ] + } } diff --git a/src/lib/server/app.ts b/src/lib/server/app.ts index bcc6364..231a00f 100644 --- a/src/lib/server/app.ts +++ b/src/lib/server/app.ts @@ -1,176 +1,200 @@ -import { Elysia, NotFoundError } from "elysia"; +import rawStandards from "$lib/data/standards.json" assert { type: "json" }; +import { + LevelCalculator, + Standards, + type ActivityStandards, +} from "$lib/services/calculator/main"; +import type { + ActivityPerformance, + Player, +} from "$lib/services/calculator/util"; import { cors } from "@elysiajs/cors"; -import { LevelCalculator, Standards, type ActivityStandards, type LevelCalculatorOutput } from "$lib/services/calculator/main"; -import type { ActivityPerformance, Player } from "$lib/services/calculator/util"; -import rawStandards from "$lib/data/standards.json" assert { type: "json" } +import { Elysia, NotFoundError } from "elysia"; +import PrintfulService from "../services/commerce/printful"; +import SyncService from "../services/commerce/sync"; +import { FetchError, getStateFromZip } from "../services/commerce/util/misc"; import { Printful, Webflow } from "../services/commerce/util/types"; import WebflowService from "../services/commerce/webflow"; -import SyncService from "../services/commerce/sync"; -import PrintfulService from "../services/commerce/printful"; -import { FetchError, getStateFromZip } from "../services/commerce/util/misc"; const MAIN_STANDARDS = new Standards(rawStandards as ActivityStandards); const levelCalculator = new LevelCalculator(MAIN_STANDARDS); export const app = new Elysia({ prefix: "/api" }) - .use( - cors({ - origin: [ - // bladeandbrawn.com (apex + any subdomain) - /^https?:\/\/([a-z0-9-]+\.)?bladeandbrawn\.com$/i, - // Webflow preview domains - /^https?:\/\/([a-z0-9-]+\.)?bladeandbrawn\.webflow\.io$/i, - // Fly app host (this one already works) - /^https?:\/\/blade-and-brawn\.fly\.dev$/i, - // Local dev - "http://localhost:5173", - ] - }) - ) + .use( + cors({ + origin: [ + // bladeandbrawn.com (apex + any subdomain) + /^https?:\/\/([a-z0-9-]+\.)?bladeandbrawn\.com$/i, + // Webflow preview domains + /^https?:\/\/([a-z0-9-]+\.)?bladeandbrawn\.webflow\.io$/i, + // Fly app host (this one already works) + /^https?:\/\/blade-and-brawn\.fly\.dev$/i, + // Local dev + "http://localhost:5173", + ], + }), + ) - .error({ - FetchError - }) + .error({ + FetchError, + }) - .onError(async ({ code, error }) => { - switch (code) { - case "FetchError": - console.error(error.message, await error.parse()); - return error; - } - }) + .onError(async ({ code, error }) => { + switch (code) { + case "FetchError": + console.error(error.message, await error.parse()); + return error; + } + }) - .onAfterHandle(({ request, set }) => { - console.log(JSON.stringify({ - lvl: "info", - msg: "req", - method: request.method, - path: new URL(request.url).pathname, - status: set.status ?? 200 - })); - }) + .onAfterHandle(({ request, set }) => { + console.log( + JSON.stringify({ + lvl: "info", + msg: "req", + method: request.method, + path: new URL(request.url).pathname, + status: set.status ?? 200, + }), + ); + }) - .get("/", () => "Hello world") + .get("/", () => "Hello world") - // CALCULATOR + // CALCULATOR - .post("/calculate", async ({ body, query }) => { - interface CalcRequest { - player: Player; - activityPerformances: ActivityPerformance[]; - } - const { player, activityPerformances } = body as CalcRequest; - const output = { levels: levelCalculator.calculate(player, activityPerformances) }; + .post("/calculate", async ({ body, query }) => { + interface CalcRequest { + player: Player; + activityPerformances: ActivityPerformance[]; + } + const { player, activityPerformances } = body as CalcRequest; + const output = { + levels: levelCalculator.calculate(player, activityPerformances), + }; - console.log({ log: query?.log }); - if (query?.log === "true") { - void fetch("https://kv-logger.xominus.workers.dev", { - method: "POST", - headers: { "content-type": "application/json" }, - body: JSON.stringify({ output, player, activityPerformances }) - }).catch(e => { }); - } + console.log({ log: query?.log }); + if (query?.log === "true") { + void fetch("https://kv-logger.xominus.workers.dev", { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ output, player, activityPerformances }), + }).catch(() => {}); + } - return output; - }) + return output; + }) - // COMMERCE - // TODO: protect with api key/token - .group("/products", app => app - .group("/sync", app => app - .get("/", async ({ }) => SyncService.state) - .post("/:printfulProductId?", async ({ params, set }) => { - if (SyncService.state.isSyncing) { - set.status = 409; - return { error: "Sync already in progress" }; - } - const printfulProductId = params.printfulProductId ? - +params.printfulProductId : - undefined; - await SyncService.sync(printfulProductId); - return { ok: true }; - }) - ) - .get("/:printfulProductId", async ({ params }) => { - const printfulProductId = +params.printfulProductId; - const printfulProduct = await PrintfulService.Products.get(printfulProductId); - if (!printfulProduct) { - return new NotFoundError(); - } + // COMMERCE + // TODO: protect with api key/token + .group("/products", (app) => + app + .group("/sync", (app) => + app + .get("/", async ({}) => SyncService.state) + .post("/:printfulProductId?", async ({ params, set }) => { + if (SyncService.state.isSyncing) { + set.status = 409; + return { error: "Sync already in progress" }; + } + const printfulProductId = params.printfulProductId + ? +params.printfulProductId + : undefined; + await SyncService.sync(printfulProductId); + return { ok: true }; + }), + ) + .get("/:printfulProductId", async ({ params }) => { + const printfulProductId = +params.printfulProductId; + const printfulProduct = + await PrintfulService.Products.get(printfulProductId); + if (!printfulProduct) { + return new NotFoundError(); + } - const webflowProductId = printfulProduct.sync_product.external_id.split("-")[0]; - const webflowProduct = await WebflowService.Products.get(webflowProductId); + const webflowProductId = + printfulProduct.sync_product.external_id.split("-")[0]; + const webflowProduct = + await WebflowService.Products.get(webflowProductId); - return { printfulProduct, webflowProduct }; - }) - ) + return { printfulProduct, webflowProduct }; + }), + ) - // WEBHOOKS - .post("/webhook/printful", async ({ body }) => { - const payload = body as Printful.Webhook.EventPayload; + // WEBHOOKS + .post("/webhook/printful", async ({ body }) => { + const payload = body as Printful.Webhook.EventPayload; - switch (payload.type) { - case Printful.Webhook.Event.ProductUpdated: { - const printfulProduct = payload.data.sync_product; - await SyncService.sync(printfulProduct.id); - break; - } - case Printful.Webhook.Event.ProductDeleted: { - await WebflowService.Products.remove(payload.data.sync_product.external_id); - break; - } - case Printful.Webhook.Event.PackageShipped: { - const webflowOrderId = payload.data.order.external_id; - const shipInfo = payload.data.shipment; + switch (payload.type) { + case Printful.Webhook.Event.ProductUpdated: { + const printfulProduct = payload.data.sync_product; + await SyncService.sync(printfulProduct.id); + break; + } + case Printful.Webhook.Event.ProductDeleted: { + await WebflowService.Products.remove( + payload.data.sync_product.external_id, + ); + break; + } + case Printful.Webhook.Event.PackageShipped: { + const webflowOrderId = payload.data.order.external_id; + const shipInfo = payload.data.shipment; - await WebflowService.Orders.update(webflowOrderId, { - shippingTrackingURL: shipInfo.tracking_url, - shippingTracking: shipInfo.tracking_number, - shippingProvider: shipInfo.carrier - }); - await WebflowService.Orders.fulfill(webflowOrderId, { sendOrderFulfilledEmail: true }); - break; - } - } + await WebflowService.Orders.update(webflowOrderId, { + shippingTrackingURL: shipInfo.tracking_url, + shippingTracking: shipInfo.tracking_number, + shippingProvider: shipInfo.carrier, + }); + await WebflowService.Orders.fulfill(webflowOrderId, { + sendOrderFulfilledEmail: true, + }); + break; + } + } - return { ok: true }; - }) - .post("/webhook/webflow", async ({ request, body, set }) => { - if (!WebflowService.Util.verifyWebflowSignature(request, body)) { - set.status = 400; - return "Invalid signature"; - } + return { ok: true }; + }) + .post("/webhook/webflow", async ({ request, body, set }) => { + if (!WebflowService.Util.verifyWebflowSignature(request, body)) { + set.status = 400; + return "Invalid signature"; + } - const payload = body as Webflow.Webhook.EventPayload; + const payload = body as Webflow.Webhook.EventPayload; - switch (payload.triggerType) { - case Webflow.Webhook.Event.OrderCreated: { - const webflowOrder = payload.payload; + switch (payload.triggerType) { + case Webflow.Webhook.Event.OrderCreated: { + const webflowOrder = payload.payload; - await PrintfulService.Orders.create({ - external_id: webflowOrder.orderId, - // TODO: derive from webflow - shipping: "STANDARD", - recipient: { - name: webflowOrder.shippingAddress.addressee, - address1: webflowOrder.shippingAddress.line1, - address2: webflowOrder.shippingAddress.line2, - city: webflowOrder.shippingAddress.city, - state_code: getStateFromZip(webflowOrder.shippingAddress.postalCode), - country_code: webflowOrder.shippingAddress.country, - zip: webflowOrder.shippingAddress.postalCode - }, - items: webflowOrder.purchasedItems.map(webflowOrderSku => ({ - external_variant_id: webflowOrderSku.variantId, - quantity: webflowOrderSku.count - })) - }); + await PrintfulService.Orders.create({ + external_id: webflowOrder.orderId, + // TODO: derive from webflow + shipping: "STANDARD", + recipient: { + name: webflowOrder.shippingAddress.addressee, + address1: webflowOrder.shippingAddress.line1, + address2: webflowOrder.shippingAddress.line2, + city: webflowOrder.shippingAddress.city, + state_code: getStateFromZip( + webflowOrder.shippingAddress.postalCode, + ), + country_code: webflowOrder.shippingAddress.country, + zip: webflowOrder.shippingAddress.postalCode, + }, + items: webflowOrder.purchasedItems.map( + (webflowOrderSku) => ({ + external_variant_id: webflowOrderSku.variantId, + quantity: webflowOrderSku.count, + }), + ), + }); - console.log("Complete"); + console.log("Complete"); - break; - } - } + break; + } + } - return { ok: true }; - }); + return { ok: true }; + }); diff --git a/src/lib/server/types.ts b/src/lib/server/types.ts index 3134c02..b02f418 100644 --- a/src/lib/server/types.ts +++ b/src/lib/server/types.ts @@ -1,2 +1,2 @@ -import type { app } from './app'; +import type { app } from "./app"; export type App = typeof app; diff --git a/src/lib/services/calculator/main.ts b/src/lib/services/calculator/main.ts index 374d783..dbb6673 100644 --- a/src/lib/services/calculator/main.ts +++ b/src/lib/services/calculator/main.ts @@ -1,7 +1,17 @@ -import { Activity, Attribute, Gender, getAvgWeight, kgToLb, lbToKg, type ActivityPerformance, type Player, type StandardUnit } from "./util"; +import { + Activity, + Attribute, + Gender, + getAvgWeight, + kgToLb, + lbToKg, + type ActivityPerformance, + type Player, + type StandardUnit, +} from "./util"; // SOURCES -// Squat, Bench, Dead Lift: +// Squat, Bench, Dead Lift: // http://lonkilgore.com/resources/Lon_Kilgore_Strength_Standard_Tables-Copyright-2023.pdf // 1 mile run: // https://runninglevel.com/running-times/1-mile-times @@ -11,17 +21,17 @@ import { Activity, Attribute, Gender, getAvgWeight, kgToLb, lbToKg, type Activit // https://nflsavant.com/combine.php?utm_source=chatgpt.com export type LevelCalculatorOutput = { - player: number, - attributes: Record -} + player: number; + attributes: Record; +}; -export type Levels = Record +export type Levels = Record; export interface Metrics { age: number; weight: number; gender: Gender; -}; +} type NumberMetric = Extract; @@ -31,18 +41,21 @@ export interface Standard { } type Generator = { - metric: NumberMetric -} + metric: NumberMetric; +}; -export type ActivityStandards = Record; +export type ActivityStandards = Record< + Activity, + { + metadata: { + attribute: Attribute; + generators: Generator[]; + unit: StandardUnit; + name: string; + }; + standards: Standard[]; + } +>; export class LevelCalculator { standards: Standards; @@ -55,7 +68,10 @@ export class LevelCalculator { // Level calculations // ------------------------------------------------------------------------------------------------- - public calculate(player: Player, activityPerformances: ActivityPerformance[]): LevelCalculatorOutput { + public calculate( + player: Player, + activityPerformances: ActivityPerformance[], + ): LevelCalculatorOutput { const levels: LevelCalculatorOutput = { player: 0, attributes: { @@ -63,22 +79,28 @@ export class LevelCalculator { [Attribute.Power]: 0, [Attribute.Endurance]: 0, [Attribute.Agility]: 0, - } as const + } as const, }; for (const value of Object.values(player)) { - if (!value) - return levels; + if (!value) return levels; } - levels.attributes = this.calculateAllAttributeLevels(player, activityPerformances); + levels.attributes = this.calculateAllAttributeLevels( + player, + activityPerformances, + ); for (const level of Object.values(levels.attributes)) { - if (!level) - return levels; + if (!level) return levels; } - const attrLevelsSum = Object.values(levels.attributes).reduce((sum, lvl) => sum + lvl, 0); - levels.player = Math.round(attrLevelsSum / Object.keys(Attribute).length); + const attrLevelsSum = Object.values(levels.attributes).reduce( + (sum, lvl) => sum + lvl, + 0, + ); + levels.player = Math.round( + attrLevelsSum / Object.keys(Attribute).length, + ); return levels; } @@ -95,42 +117,54 @@ export class LevelCalculator { } as const; for (const value of Object.values(player)) { - if (!value) - return attrLevels; + if (!value) return attrLevels; } (Object.values(Attribute) as Attribute[]).forEach((attribute) => { const attrActivityPerformances = activityPerformances.filter( - (p) => this.standards.byActivity(p.activity).getMetadata().attribute === attribute, + (p) => + this.standards.byActivity(p.activity).getMetadata() + .attribute === attribute, + ); + attrLevels[attribute] = this.calculateAttributeLevel( + attribute, + player, + attrActivityPerformances, ); - attrLevels[attribute] = this.calculateAttributeLevel(attribute, player, attrActivityPerformances); }); return attrLevels; } - private calculateAttributeLevel( attribute: Attribute, player: Player, activityPerformances: ActivityPerformance[], ): number { // must be activities of the given attribute - if (activityPerformances.some((p) => this.standards.byActivity(p.activity).getMetadata().attribute !== attribute)) { + if ( + activityPerformances.some( + (p) => + this.standards.byActivity(p.activity).getMetadata() + .attribute !== attribute, + ) + ) { throw new Error("Wrong activity performance attribute"); } // must perform all of an attributes activities - for (const activity of this.standards.getAttributeActivities(attribute)) { - const filteredActivites = activityPerformances.map((p) => p.activity); - if (!filteredActivites.includes(activity)) - return 0; + for (const activity of this.standards.getAttributeActivities( + attribute, + )) { + const filteredActivites = activityPerformances.map( + (p) => p.activity, + ); + if (!filteredActivites.includes(activity)) return 0; } - // must have all activties using a valid performance value (> 0) + // must have all activties using a valid performance value (> 0) for (const activityPerformance of activityPerformances) { - if (activityPerformance.performance <= 0) - return 0 + if (activityPerformance.performance <= 0) return 0; } const activityLevels = activityPerformances.map((p) => { @@ -142,7 +176,8 @@ export class LevelCalculator { }); const activityLevelsAvg = Math.round( - activityLevels.reduce((sum, curr) => sum + curr, 0) / activityLevels.length, + activityLevels.reduce((sum, curr) => sum + curr, 0) / + activityLevels.length, ); return activityLevelsAvg; @@ -171,85 +206,109 @@ export class LevelCalculator { export type StandardsConfig = { global: { - maxLevel: number, - }, - activity: Record -} + maxLevel: number; + }; + activity: Record< + Activity, + { + weightModifier: number; + weightSkew: number; + ageModifier: number; + enableGeneration: boolean; + difficultyModifier: number; + peakAge: number; + } + >; +}; export class Standards { readonly cfg: StandardsConfig; private activityStandards: ActivityStandards; - constructor(activityStandards: ActivityStandards, cfg?: Partial) { + constructor( + activityStandards: ActivityStandards, + cfg?: Partial, + ) { // TODO: set these somewhere const defaultActivitiesConfig = Object.fromEntries( - Object.values(Activity).map(activity => [ + Object.values(Activity).map((activity) => [ activity, { enableGeneration: true, - weightModifier: activity === Activity.BroadJump ? - -0.1 : - 0.1, + weightModifier: + activity === Activity.BroadJump ? -0.1 : 0.1, weightSkew: 0, ageModifier: 0.5, - difficultyModifier: activity === Activity.BroadJump ? - 0.05 : - 0 + difficultyModifier: + activity === Activity.BroadJump ? 0.05 : 0, + peakAge: 27, }, - ]) + ]), ) as Record; this.cfg = { global: { maxLevel: cfg?.global?.maxLevel ?? 100, }, - activity: Object.assign(defaultActivitiesConfig, cfg?.activity) + activity: Object.assign(defaultActivitiesConfig, cfg?.activity), }; // prepare data this.activityStandards = structuredClone(activityStandards); - for (const activity of Object.keys(this.activityStandards) as Activity[]) { + for (const activity of Object.keys( + this.activityStandards, + ) as Activity[]) { for (const standard of this.activityStandards[activity].standards) { // expand let i = 1; - while (Object.keys(standard.levels).length < this.cfg.global.maxLevel) { + while ( + Object.keys(standard.levels).length < + this.cfg.global.maxLevel + ) { standard.levels = this.expandLevels(standard.levels, i++); } // compress - if (Object.keys(standard.levels).length > this.cfg.global.maxLevel) { - standard.levels = this.compressLevels(standard.levels, this.cfg.global.maxLevel); + if ( + Object.keys(standard.levels).length > + this.cfg.global.maxLevel + ) { + standard.levels = this.compressLevels( + standard.levels, + this.cfg.global.maxLevel, + ); } // apply skew config for (const level in standard.levels) { - standard.levels[level] = standard.levels[level] * (1 + this.cfg.activity[activity].difficultyModifier) + standard.levels[level] = + standard.levels[level] * + (1 + this.cfg.activity[activity].difficultyModifier); } } // generate data - const allGenerators = this.cfg.activity[activity].enableGeneration ? - this.activityStandards[activity].metadata.generators : - []; + const allGenerators = this.cfg.activity[activity].enableGeneration + ? this.activityStandards[activity].metadata.generators + : []; - const ageGenerators = allGenerators.filter(g => g.metric === "age"); + const ageGenerators = allGenerators.filter( + (g) => g.metric === "age", + ); for (const ageGenerator of ageGenerators) { for (const gender of Object.values(Gender)) { - const peakAge = 27; + const peakAge = this.cfg.activity[activity].peakAge; const ageStep = 10; const minAge = 0; const maxAge = 100; - const referenceStandard = this - .byActivity(activity) - .byMetrics({ weight: getAvgWeight(gender, peakAge), gender, age: peakAge }) + const referenceStandard = this.byActivity(activity) + .byMetrics({ + weight: getAvgWeight(gender, peakAge), + gender, + age: peakAge, + }) .getOneInterpolated(); let currAge = minAge; @@ -258,9 +317,9 @@ export class Standards { metrics: { weight: getAvgWeight(gender, currAge), age: currAge, - gender + gender, }, - levels: {} + levels: {}, }; for (const lvl in referenceStandard.levels) { @@ -275,12 +334,17 @@ export class Standards { const youngFloor = 1 - (1 - youngFloor0) * s; const oldFloor = 1 - (1 - oldFloor0) * s; - const cy = s * (1 - youngFloor0) / Math.pow(peakAge - minAge, 2); - const co = s * (1 - oldFloor0) / Math.pow(maxAge - peakAge, 2); + const cy = + (s * (1 - youngFloor0)) / + Math.pow(peakAge - minAge, 2); + const co = + (s * (1 - oldFloor0)) / + Math.pow(maxAge - peakAge, 2); - let f = currAge <= peakAge - ? 1 - cy * Math.pow(peakAge - currAge, 2) - : 1 - co * Math.pow(currAge - peakAge, 2); + let f = + currAge <= peakAge + ? 1 - cy * Math.pow(peakAge - currAge, 2) + : 1 - co * Math.pow(currAge - peakAge, 2); const floor = Math.min(youngFloor, oldFloor); const fCurr = Math.max(floor, Math.min(1, f)); @@ -289,34 +353,41 @@ export class Standards { } // prefer real data over generated data - const overlappingStandard = this - .byActivity(activity) + const overlappingStandard = this.byActivity(activity) .byMetrics(newStandard.metrics) .getOne(); if (!overlappingStandard) - this.activityStandards[activity].standards.push(newStandard); + this.activityStandards[activity].standards.push( + newStandard, + ); currAge += ageStep; } } // now that we generated metric data, remove the old data - this.activityStandards[activity].standards = this.activityStandards[activity].standards - .filter(s => s.metrics.weight) + this.activityStandards[activity].standards = + this.activityStandards[activity].standards.filter( + (s) => s.metrics.weight, + ); } - const weightGenerators = allGenerators.filter(g => g.metric === "weight"); + const weightGenerators = allGenerators.filter( + (g) => g.metric === "weight", + ); for (const weightGenerator of weightGenerators) { for (const gender of Object.values(Gender)) { for (const age of this.agesFor(activity, gender)) { const weightStep = 12; const referenceWeight = getAvgWeight(gender, age); - const minWeight = Math.max(referenceWeight - (3 * weightStep), 1); - const maxWeight = referenceWeight + (3 * weightStep); + const minWeight = Math.max( + referenceWeight - 3 * weightStep, + 1, + ); + const maxWeight = referenceWeight + 3 * weightStep; - const referenceStandard = this - .byActivity(activity) + const referenceStandard = this.byActivity(activity) .byMetrics({ weight: referenceWeight, gender, age }) .getOneInterpolated(); @@ -326,28 +397,35 @@ export class Standards { metrics: { weight: currWeight, age, - gender + gender, }, - levels: {} + levels: {}, }; // apply allometric scaling to generate levels for (const lvl in referenceStandard.levels) { if (!referenceStandard.levels[lvl]) continue; - const scalingExponent = this.cfg.activity[activity].weightModifier - const coefficient = currWeight / referenceWeight; + const scalingExponent = + this.cfg.activity[activity].weightModifier; + const coefficient = + currWeight / referenceWeight; - newStandard.levels[lvl] = referenceStandard.levels[lvl] * (coefficient ** scalingExponent); + newStandard.levels[lvl] = + referenceStandard.levels[lvl] * + coefficient ** scalingExponent; } // prefer real data over generated data - const overlappingStandard = this - .byActivity(activity) + const overlappingStandard = this.byActivity( + activity, + ) .byMetrics(newStandard.metrics) .getOne(); if (!overlappingStandard) - this.activityStandards[activity].standards.push(newStandard); + this.activityStandards[activity].standards.push( + newStandard, + ); currWeight += weightStep; } @@ -355,20 +433,26 @@ export class Standards { } // now that we generated metric data, remove the old data - this.activityStandards[activity].standards = this.activityStandards[activity].standards - .filter(s => s.metrics.weight); + this.activityStandards[activity].standards = + this.activityStandards[activity].standards.filter( + (s) => s.metrics.weight, + ); } - this.activityStandards[activity].standards = this.activityStandards[activity].standards - .sort((a, b) => { - const genderOrder: Record = { Male: 0, Female: 1 }; - const gA = genderOrder[b.metrics.gender] ?? 99; - const gB = genderOrder[b.metrics.gender] ?? 99; - if (gA !== gB) return gA - gB; - if (a.metrics.age !== b.metrics.age) - return a.metrics.age - b.metrics.age; - return a.metrics.weight - b.metrics.weight; - }); + this.activityStandards[activity].standards = this.activityStandards[ + activity + ].standards.sort((a, b) => { + const genderOrder: Record = { + Male: 0, + Female: 1, + }; + const gA = genderOrder[b.metrics.gender] ?? 99; + const gB = genderOrder[b.metrics.gender] ?? 99; + if (gA !== gB) return gA - gB; + if (a.metrics.age !== b.metrics.age) + return a.metrics.age - b.metrics.age; + return a.metrics.weight - b.metrics.weight; + }); } } @@ -387,32 +471,57 @@ export class Standards { const out: Standard[] = []; for (let i = 0; i < src.length; i++) { const s = src[i]; - if (state.gender != null && s.metrics.gender !== state.gender) continue; - if (state.age != null && s.metrics.age !== state.age) continue; - if (state.weight != null && s.metrics.weight !== state.weight) continue; + if ( + state.gender != null && + s.metrics.gender !== state.gender + ) + continue; + if (state.age != null && s.metrics.age !== state.age) + continue; + if ( + state.weight != null && + s.metrics.weight !== state.weight + ) + continue; out.push(s); } return out; }, - getOne: function(): Standard | undefined { + getOne: function (): Standard | undefined { return execMethods.exact.getAll()[0]; }, - getNearest(metric: NumberMetric, target: number): { lower: Standard; upper: Standard } { + getNearest( + metric: NumberMetric, + target: number, + ): { lower: Standard; upper: Standard } { const standards = execMethods.exact.getAll(); - if (standards.length === 0) throw new Error('no standards'); + if (standards.length === 0) throw new Error("no standards"); - let lower = standards[0]; // best <= target - let upper = standards[0]; // best >= target - let hasLower = false, hasUpper = false; + let lower = standards[0]; // best <= target + let upper = standards[0]; // best >= target + let hasLower = false, + hasUpper = false; for (const standard of standards) { const standardMetricValue = standard.metrics[metric]; if (standardMetricValue <= target) { - if (!hasLower || standardMetricValue > lower.metrics[metric]) { lower = standard; hasLower = true; } + if ( + !hasLower || + standardMetricValue > lower.metrics[metric] + ) { + lower = standard; + hasLower = true; + } } if (standardMetricValue >= target) { - if (!hasUpper || standardMetricValue < upper.metrics[metric]) { upper = standard; hasUpper = true; } + if ( + !hasUpper || + standardMetricValue < upper.metrics[metric] + ) { + upper = standard; + hasUpper = true; + } } } @@ -420,14 +529,16 @@ export class Standards { if (!hasLower) lower = standards[0]; if (!hasUpper) upper = standards[standards.length - 1]; return { lower, upper }; - } - } + }, + }, }; const interpolateByAgeAndWeight = (metrics: Metrics): Standard => { const interpolateByWeight = (targetAge: number): Levels => { - const { lower: lowerAgeWeightStandard, upper: upperAgeWeightStandard } = this - .byActivity(activity) + const { + lower: lowerAgeWeightStandard, + upper: upperAgeWeightStandard, + } = this.byActivity(activity) .byGender(metrics.gender) .byAge(targetAge) .getNearest("weight", metrics.weight); @@ -435,49 +546,67 @@ export class Standards { const weightLower = lowerAgeWeightStandard.metrics.weight; const weightUpper = upperAgeWeightStandard.metrics.weight; - let weightRatio = weightUpper === weightLower ? 1 : (metrics.weight - weightLower) / (weightUpper - weightLower); + let weightRatio = + weightUpper === weightLower + ? 1 + : (metrics.weight - weightLower) / + (weightUpper - weightLower); weightRatio = Math.max(0, Math.min(1, weightRatio)); // metrics.weight = lowerAgeWeightStandard.metrics.weight + (upperAgeWeightStandard.metrics.weight - lowerAgeWeightStandard.metrics.weight) * weightRatio; - return this.interpolateLevels(lowerAgeWeightStandard.levels, upperAgeWeightStandard.levels, weightRatio); - } + return this.interpolateLevels( + lowerAgeWeightStandard.levels, + upperAgeWeightStandard.levels, + weightRatio, + ); + }; // Find nearest age standards - const { lower: lowerAgeStandard, upper: upperAgeStandard } = this - .byActivity(activity) - .byGender(metrics.gender) - .getNearest("age", metrics.age); + const { lower: lowerAgeStandard, upper: upperAgeStandard } = + this.byActivity(activity) + .byGender(metrics.gender) + .getNearest("age", metrics.age); const ageLower = lowerAgeStandard.metrics.age; const ageUpper = upperAgeStandard.metrics.age; // Interpolate by age and weight - let ageRatio = ageUpper === ageLower ? 1 : (metrics.age - ageLower) / (ageUpper - ageLower); + let ageRatio = + ageUpper === ageLower + ? 1 + : (metrics.age - ageLower) / (ageUpper - ageLower); ageRatio = Math.max(0, Math.min(1, ageRatio)); const interpolatedLevels = this.interpolateLevels( interpolateByWeight(ageLower), interpolateByWeight(ageUpper), - ageRatio + ageRatio, ); return { metrics: metrics, - levels: interpolatedLevels - } + levels: interpolatedLevels, + }; }; - const getAllInterpolated = (opt?: { normalizeForLb?: boolean }): Standard[] => { + const getAllInterpolated = (opt?: { + normalizeForLb?: boolean; + }): Standard[] => { const metrics: Metrics = state as Metrics; - let weights = this.weightsFor(activity, metrics.gender, metrics.age); + let weights = this.weightsFor( + activity, + metrics.gender, + metrics.age, + ); if (opt?.normalizeForLb) { - weights = weights.map(w => lbToKg(Math.round(kgToLb(w)))); + weights = weights.map((w) => lbToKg(Math.round(kgToLb(w)))); } - const interpolatedStandards: Standard[] = weights - .map(weight => interpolateByAgeAndWeight({ ...metrics, weight })); + const interpolatedStandards: Standard[] = weights.map((weight) => + interpolateByAgeAndWeight({ ...metrics, weight }), + ); return interpolatedStandards; }; @@ -494,31 +623,40 @@ export class Standards { const byGender = (gender: Gender) => { state.gender = gender; return { byAge, ...execMethods.exact }; - } + }; const byAge = (age: number) => { state.age = age; return { byWeight, ...execMethods.exact, getAllInterpolated }; - } + }; const byWeight = (weight: number) => { state.weight = weight; return { ...execMethods.exact, getOneInterpolated }; - } + }; const byMetrics = (metrics: Metrics) => { Object.assign(state, metrics); return { ...execMethods.exact, getOneInterpolated }; - } + }; - const initialMethods = { byGender, byMetrics, getMetadata, ...execMethods.exact }; + const initialMethods = { + byGender, + byMetrics, + getMetadata, + ...execMethods.exact, + }; return initialMethods; } public getAttributeActivities(attribute: Attribute): Activity[] { const activities: Activity[] = []; - for (const activity of Object.keys(this.activityStandards) as Activity[]) { - const activityStandard = this.activityStandards[activity] as ActivityStandards[Activity]; + for (const activity of Object.keys( + this.activityStandards, + ) as Activity[]) { + const activityStandard = this.activityStandards[ + activity + ] as ActivityStandards[Activity]; if (activityStandard.metadata.attribute === attribute) { activities.push(activity as Activity); } @@ -528,36 +666,33 @@ export class Standards { // TODO: turn these into queries? public weightsFor(activity: Activity, gender: Gender, age: number) { - const { lower, upper } = this - .byActivity(activity) + const { lower, upper } = this.byActivity(activity) .byGender(gender) .getNearest("age", age); const lowerDiff = Math.abs(lower.metrics.age - age); const upperDiff = Math.abs(upper.metrics.age - age); - const closestAge = lowerDiff < upperDiff ? - lower.metrics.age : - upper.metrics.age; + const closestAge = + lowerDiff < upperDiff ? lower.metrics.age : upper.metrics.age; - const weights = this - .byActivity(activity) + const weights = this.byActivity(activity) .byGender(gender) .byAge(closestAge) - .getAll().map((s) => s.metrics.weight); + .getAll() + .map((s) => s.metrics.weight); return weights; } public agesFor(activity: Activity, gender: Gender) { - const standardsByGender = this.byActivity(activity).byGender(gender).getAll(); - const ages = [...new Set(standardsByGender.map(s => s.metrics.age))]; + const standardsByGender = this.byActivity(activity) + .byGender(gender) + .getAll(); + const ages = [...new Set(standardsByGender.map((s) => s.metrics.age))]; return ages; } - private expandLevels( - levels: Levels, - i: number - ): Levels { + private expandLevels(levels: Levels, i: number): Levels { if (i == 0) { return levels; } @@ -578,10 +713,7 @@ export class Standards { return this.expandLevels(newLevels, i - 1); } - private compressLevels( - levels: Levels, - targetLevel: number, - ): Levels { + private compressLevels(levels: Levels, targetLevel: number): Levels { const levelsAmount = Object.keys(levels).length; if (levelsAmount < targetLevel) { throw new Error( @@ -601,7 +733,8 @@ export class Standards { const upperValue = levels[upperIndex + 1]; const weight = ratioIndex - lowerIndex; - compressedLevels[i + 1] = lowerValue + (upperValue - lowerValue) * weight; + compressedLevels[i + 1] = + lowerValue + (upperValue - lowerValue) * weight; } return compressedLevels; @@ -613,9 +746,13 @@ export class Standards { ratio: number, ): Levels { if (Object.keys(lower).length !== Object.keys(upper).length) - throw new Error("Cannot interpolate between varying number of levels"); + throw new Error( + "Cannot interpolate between varying number of levels", + ); - const lerp = (lvl: string) => (lower[lvl] as number) + ((upper[lvl] as number) - (lower[lvl] as number)) * ratio; + const lerp = (lvl: string) => + (lower[lvl] as number) + + ((upper[lvl] as number) - (lower[lvl] as number)) * ratio; const interpolatedLevels: Levels = {}; for (const lvl in lower) { diff --git a/src/lib/services/calculator/util.ts b/src/lib/services/calculator/util.ts index 3f92049..72f00f8 100644 --- a/src/lib/services/calculator/util.ts +++ b/src/lib/services/calculator/util.ts @@ -1,14 +1,14 @@ import { type Metrics } from "$lib/services/calculator/main"; -import avgWeightDataRaw from "$lib/data/avg-weights.json" +import avgWeightDataRaw from "$lib/data/avg-weights.json"; // ------------------------------------------------------------------------------------------------- -// DATA MODELS +// DATA MODELS // ------------------------------------------------------------------------------------------------- export type Player = { name?: string; metrics: Metrics; -} +}; export enum Attribute { Strength = "Strength", @@ -33,7 +33,7 @@ export interface ActivityPerformance { export enum Gender { Male = "Male", - Female = "Female" + Female = "Female", } // ------------------------------------------------------------------------------------------------- @@ -65,23 +65,24 @@ export const msToTime = (ms: number, includeMs = false): string => { return formattedTime; }; -export const range = (length: number) => Array.from({ length: length }, (_, i) => i + 1); +export const range = (length: number) => + Array.from({ length: length }, (_, i) => i + 1); export const getAvgWeight = (gender: Gender, age: number) => { type AvgWeightData = { metadata: { - unit: StandardUnit - }, - weights: Metrics[] - } + unit: StandardUnit; + }; + weights: Metrics[]; + }; let avgWeightData = avgWeightDataRaw as AvgWeightData; - const avgWeights = avgWeightData.weights.filter(a => a.gender === gender); + const avgWeights = avgWeightData.weights.filter((a) => a.gender === gender); if (!avgWeights.length) throw new Error("No average weights"); const closest = { diff: Math.abs(avgWeights[0].age - age), - avg: avgWeights[0] + avg: avgWeights[0], }; for (const avg of avgWeights) { const diff = Math.abs(avg.age - age); @@ -92,4 +93,4 @@ export const getAvgWeight = (gender: Gender, age: number) => { } return closest.avg.weight; -} +}; diff --git a/src/lib/services/commerce/printful.ts b/src/lib/services/commerce/printful.ts index 5392a37..db804c5 100644 --- a/src/lib/services/commerce/printful.ts +++ b/src/lib/services/commerce/printful.ts @@ -4,11 +4,16 @@ import { FetchError, type DeepPartial } from "./util/misc"; export default class PrintfulService { static Products = class { static Variants = class { - static async get(variantId: number | string): Promise { - const res = await fetch(`${env().API_URL}/store/variants/${variantId}`, { - method: "GET", - headers: { ...env().AUTH_HEADERS } - }); + static async get( + variantId: number | string, + ): Promise { + const res = await fetch( + `${env().API_URL}/store/variants/${variantId}`, + { + method: "GET", + headers: { ...env().AUTH_HEADERS }, + }, + ); if (res.status === 404 || res.status === 400) { return; @@ -18,32 +23,41 @@ export default class PrintfulService { throw new FetchError("Failed to get Printful variant", res); } - const payload: Printful.Products.MetaDataSingle = await res.json(); + const payload: Printful.Products.MetaDataSingle = + await res.json(); return payload.result; } - } + }; - static async getAll(offset: number = 0): Promise { + static async getAll( + offset: number = 0, + ): Promise { const allSyncProducts: Printful.Products.SyncProduct[] = []; while (true) { try { - const res = await fetch(`${env().API_URL}/store/products?offset=${offset}`, { - method: "GET", - headers: { ...env().AUTH_HEADERS } - }) + const res = await fetch( + `${env().API_URL}/store/products?offset=${offset}`, + { + method: "GET", + headers: { ...env().AUTH_HEADERS }, + }, + ); if (!res.ok) { - throw new FetchError("Failed to get all Printful products", res); + throw new FetchError( + "Failed to get all Printful products", + res, + ); } - const payload: Printful.Products.MetaDataMulti = await res.json(); + const payload: Printful.Products.MetaDataMulti = + await res.json(); allSyncProducts.push(...payload.result); offset = allSyncProducts.length; - if (allSyncProducts.length >= payload.paging.total) - break; + if (allSyncProducts.length >= payload.paging.total) break; } catch (error) { throw error; } @@ -52,11 +66,16 @@ export default class PrintfulService { return allSyncProducts; } - static async get(productId: number | string): Promise { - const res = await fetch(`${env().API_URL}/store/products/${productId}`, { - method: "GET", - headers: { ...env().AUTH_HEADERS } - }); + static async get( + productId: number | string, + ): Promise { + const res = await fetch( + `${env().API_URL}/store/products/${productId}`, + { + method: "GET", + headers: { ...env().AUTH_HEADERS }, + }, + ); if (res.status === 404 || res.status === 400) { return; @@ -66,25 +85,32 @@ export default class PrintfulService { throw new FetchError("Failed to get Printful product", res); } - const payload: Printful.Products.MetaDataSingle = await res.json(); + const payload: Printful.Products.MetaDataSingle = + await res.json(); return payload.result; } - static async update(printfulProductId: number, printfulProduct: DeepPartial) { - const res = await fetch(`${env().API_URL}/store/products/${printfulProductId}`, { - method: "PUT", - headers: { - "Content-Type": "application/json", - ...env().AUTH_HEADERS + static async update( + printfulProductId: number, + printfulProduct: DeepPartial, + ) { + const res = await fetch( + `${env().API_URL}/store/products/${printfulProductId}`, + { + method: "PUT", + headers: { + "Content-Type": "application/json", + ...env().AUTH_HEADERS, + }, + body: JSON.stringify(printfulProduct), }, - body: JSON.stringify(printfulProduct) - }); + ); if (!res.ok) { throw new FetchError("Failed to update Printful product", res); } } - } + }; static Orders = class { static async create(printfulOrder: Printful.Orders.Order) { @@ -92,9 +118,9 @@ export default class PrintfulService { method: "POST", headers: { "Content-Type": "application/json", - ...env().AUTH_HEADERS + ...env().AUTH_HEADERS, }, - body: JSON.stringify(printfulOrder) + body: JSON.stringify(printfulOrder), }); if (!res.ok) { @@ -102,27 +128,35 @@ export default class PrintfulService { } } - static async getAll(offset: number = 0): Promise { + static async getAll( + offset: number = 0, + ): Promise { const allPrintfulOrders: Printful.Orders.Order[] = []; while (true) { try { - const res = await fetch(`${env().API_URL}/orders?offset=${offset}`, { - method: "GET", - headers: { ...env().AUTH_HEADERS } - }) + const res = await fetch( + `${env().API_URL}/orders?offset=${offset}`, + { + method: "GET", + headers: { ...env().AUTH_HEADERS }, + }, + ); if (!res.ok) { - throw new FetchError("Failed to get all Printful orders", res); + throw new FetchError( + "Failed to get all Printful orders", + res, + ); } - const payload: Printful.Products.MetaDataMulti = await res.json(); + const payload: Printful.Products.MetaDataMulti = + await res.json(); allPrintfulOrders.push(...payload.result); offset = allPrintfulOrders.length; - if (allPrintfulOrders.length >= payload.paging.total) - break; + if (allPrintfulOrders.length >= payload.paging.total) break; } catch (error) { throw error; } @@ -130,32 +164,38 @@ export default class PrintfulService { return allPrintfulOrders; } - } + }; static Util = class { - static getVariantMainImage(syncVariant: Printful.Products.SyncVariant): string { - const previewFile = syncVariant.files.find(f => f.type === "preview"); + static getVariantMainImage( + syncVariant: Printful.Products.SyncVariant, + ): string { + const previewFile = syncVariant.files.find( + (f) => f.type === "preview", + ); return previewFile?.preview_url ?? syncVariant.product.image; } - } + }; } export const env = () => { if (typeof Bun === "undefined") { - throw new Error("Must be in a server context. Make sure to run using --bun."); + throw new Error( + "Must be in a server context. Make sure to run using --bun.", + ); } const vars = { API_URL: "https://api.printful.com", AUTH_TOKEN: Bun.env.PRINTFUL_AUTH, - STORE_ID: Bun.env.PRINTFUL_STORE_ID + STORE_ID: Bun.env.PRINTFUL_STORE_ID, }; return { ...vars, AUTH_HEADERS: { - "Authorization": `Bearer ${vars.AUTH_TOKEN}`, - "X-PF-Store-Id": `${vars.STORE_ID}` - } + Authorization: `Bearer ${vars.AUTH_TOKEN}`, + "X-PF-Store-Id": `${vars.STORE_ID}`, + }, }; }; diff --git a/src/lib/services/commerce/sync.ts b/src/lib/services/commerce/sync.ts index e5116a1..c4a8fe4 100644 --- a/src/lib/services/commerce/sync.ts +++ b/src/lib/services/commerce/sync.ts @@ -3,26 +3,26 @@ import { FetchError, formatSlug, type DeepPartial } from "./util/misc"; import WebflowService from "./webflow"; import PrintfulService from "./printful"; -const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); +const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); export const R2_WORKER_URL = "https://r2-worker.xominus.workers.dev"; export const WEBSITE_MEDIA_URL = "https://website-media.bladeandbrawn.com"; type MainProduct = { - name: string, - externalId: string, - colors: Set, + name: string; + externalId: string; + colors: Set; variants: { - color: string, - product: Printful.Products.Product - }[] -} + color: string; + product: Printful.Products.Product; + }[]; +}; export default class SyncService { static state = { isSyncing: false, - syncingIds: [] as number[] - } + syncingIds: [] as number[], + }; static async sync(printfulProductId: number | undefined) { this.state.isSyncing = true; @@ -32,43 +32,54 @@ export default class SyncService { try { let printfulProducts = await PrintfulService.Products.getAll(); if (printfulProductId) { - const printfulProduct = printfulProducts.find(p => p.id === printfulProductId); + const printfulProduct = printfulProducts.find( + (p) => p.id === printfulProductId, + ); if (printfulProduct) { - const mainProductName = this.getMainProductName(printfulProduct.name); - printfulProducts = printfulProducts.filter(p => p.name.includes(mainProductName)); + const mainProductName = this.getMainProductName( + printfulProduct.name, + ); + printfulProducts = printfulProducts.filter((p) => + p.name.includes(mainProductName), + ); } } - webflowProducts.push(...await WebflowService.Products.getAll()); + webflowProducts.push(...(await WebflowService.Products.getAll())); // Generate main printful products console.log("Generating main products..."); for (const printfulProduct of printfulProducts) { - const mainProductName = this.getMainProductName(printfulProduct.name); + const mainProductName = this.getMainProductName( + printfulProduct.name, + ); - let mainPrintfulProduct = mainPrintfulProducts - .find(mp => mp.name.includes(mainProductName)); + let mainPrintfulProduct = mainPrintfulProducts.find((mp) => + mp.name.includes(mainProductName), + ); if (!mainPrintfulProduct) { mainPrintfulProduct = { name: mainProductName, variants: [], externalId: printfulProduct.external_id, - colors: new Set() - } + colors: new Set(), + }; mainPrintfulProducts.push(mainPrintfulProduct); } - const productColor = this.findColorInProductName(printfulProduct.name); + const productColor = this.findColorInProductName( + printfulProduct.name, + ); mainPrintfulProduct.variants.push({ color: productColor, - product: (await PrintfulService.Products.get(printfulProduct.id))! + product: (await PrintfulService.Products.get( + printfulProduct.id, + ))!, }); mainPrintfulProduct.colors.add(productColor); } - } - catch (error) { + } catch (error) { throw error; - } - finally { + } finally { this.state.isSyncing = false; this.state.syncingIds = []; } @@ -80,19 +91,26 @@ export default class SyncService { for (const mainPrintfulProduct of mainPrintfulProducts) { try { this.state.isSyncing = true; - this.state.syncingIds = mainPrintfulProduct.variants.map(v => v.product.sync_product.id); + this.state.syncingIds = mainPrintfulProduct.variants.map( + (v) => v.product.sync_product.id, + ); const foundColors = mainPrintfulProduct.colors; const foundSizes: Set = new Set(); - const webflowSkus: DeepPartial[] = []; + const webflowSkus: DeepPartial[] = + []; for (const specialVariant of mainPrintfulProduct.variants) { - for (const printfulVariant of specialVariant.product.sync_variants) { + for (const printfulVariant of specialVariant.product + .sync_variants) { // check if this variant size is in all other special variants let sizeIsInAllVariants = true; for (const specialVariant of mainPrintfulProduct.variants) { - const sizes = specialVariant.product.sync_variants.map(v => v.size); + const sizes = + specialVariant.product.sync_variants.map( + (v) => v.size, + ); if (!sizes.includes(printfulVariant.size)) sizeIsInAllVariants = false; } @@ -101,29 +119,37 @@ export default class SyncService { foundSizes.add(printfulVariant.size); webflowSkus.push({ - "id": printfulVariant.external_id, - "fieldData": { - "name": printfulVariant.name, - "slug": formatSlug(printfulVariant.name), + id: printfulVariant.external_id, + fieldData: { + name: printfulVariant.name, + slug: formatSlug(printfulVariant.name), "sku-values": { - "color": specialVariant.color, - "size": printfulVariant.size, + color: specialVariant.color, + size: printfulVariant.size, }, - "price": { - "value": Math.floor(+printfulVariant.retail_price * 100), - "unit": printfulVariant.currency, - "currency": printfulVariant.currency + price: { + value: Math.floor( + +printfulVariant.retail_price * 100, + ), + unit: printfulVariant.currency, + currency: printfulVariant.currency, }, - "main-image": PrintfulService.Util.getVariantMainImage(printfulVariant) - } - }) + "main-image": + PrintfulService.Util.getVariantMainImage( + printfulVariant, + ), + }, + }); } } } // SYNC PRODUCT DATA - let existingWebflowProduct = webflowProducts - .find(webflowProduct => webflowProduct.product.id === mainPrintfulProduct.externalId.split("-")[0]); + let existingWebflowProduct = webflowProducts.find( + (webflowProduct) => + webflowProduct.product.id === + mainPrintfulProduct.externalId.split("-")[0], + ); if (existingWebflowProduct) { // SYNC PRODUCT UPDATE @@ -132,131 +158,165 @@ export default class SyncService { delete webflowSku.fieldData?.["main-image"]; } - const webflowProductId = mainPrintfulProduct.externalId.split("-")[0]; + const webflowProductId = + mainPrintfulProduct.externalId.split("-")[0]; WebflowService.Products.update(webflowProductId, { - "product": { - "fieldData": { - "name": mainPrintfulProduct.name, - "slug": formatSlug(mainPrintfulProduct.name), - "shippable": true, + product: { + fieldData: { + name: mainPrintfulProduct.name, + slug: formatSlug(mainPrintfulProduct.name), + shippable: true, "tax-category": "standard-taxable", "sku-properties": [ { - "id": "color", - "name": "Color", - "enum": Array.from(foundColors).map(color => ({ - "id": color, - "slug": formatSlug(color), - "name": color - })) + id: "color", + name: "Color", + enum: Array.from(foundColors).map( + (color) => ({ + id: color, + slug: formatSlug(color), + name: color, + }), + ), }, { - "id": "size", - "name": "Size", - "enum": Array.from(foundSizes).map(size => ({ - "id": size, - "slug": formatSlug(size), - "name": size - })) + id: "size", + name: "Size", + enum: Array.from(foundSizes).map( + (size) => ({ + id: size, + slug: formatSlug(size), + name: size, + }), + ), }, - ] - } + ], + }, }, - "sku": webflowSkus[0] + sku: webflowSkus[0], }); for (const webflowSku of webflowSkus.slice(1)) { - const existingWebflowSku = existingWebflowProduct.skus.find(sku => sku.id === webflowSku.id); + const existingWebflowSku = + existingWebflowProduct.skus.find( + (sku) => sku.id === webflowSku.id, + ); if (webflowSku.id && existingWebflowSku) { - await WebflowService.Products.Skus.update(webflowProductId, webflowSku.id, webflowSku); - } - else { - await WebflowService.Products.Skus.create(webflowProductId, [webflowSku]); + await WebflowService.Products.Skus.update( + webflowProductId, + webflowSku.id, + webflowSku, + ); + } else { + await WebflowService.Products.Skus.create( + webflowProductId, + [webflowSku], + ); } } - } - else { - // SYNC PRODUCT CREATE - const webflowProductId = await WebflowService.Products.create({ - "product": { - "fieldData": { - "name": mainPrintfulProduct.name, - "slug": formatSlug(mainPrintfulProduct.name), - "shippable": true, - "tax-category": "standard-taxable", - "sku-properties": [ - { - "id": "color", - "name": "Color", - "enum": Array.from(foundColors).map(color => ({ - "id": color, - "slug": formatSlug(color), - "name": color - })) - }, - { - "id": "size", - "name": "Size", - "enum": Array.from(foundSizes).map(size => ({ - "id": size, - "slug": formatSlug(size), - "name": size - })) - }, - ] - } - }, - "sku": webflowSkus[0], - }); + } else { + // SYNC PRODUCT CREATE + const webflowProductId = + await WebflowService.Products.create({ + product: { + fieldData: { + name: mainPrintfulProduct.name, + slug: formatSlug(mainPrintfulProduct.name), + shippable: true, + "tax-category": "standard-taxable", + "sku-properties": [ + { + id: "color", + name: "Color", + enum: Array.from(foundColors).map( + (color) => ({ + id: color, + slug: formatSlug(color), + name: color, + }), + ), + }, + { + id: "size", + name: "Size", + enum: Array.from(foundSizes).map( + (size) => ({ + id: size, + slug: formatSlug(size), + name: size, + }), + ), + }, + ], + }, + }, + sku: webflowSkus[0], + }); - // create webflow product SKUs - await WebflowService.Products.Skus.create(webflowProductId, webflowSkus.slice(1)); + // create webflow product SKUs + await WebflowService.Products.Skus.create( + webflowProductId, + webflowSkus.slice(1), + ); - existingWebflowProduct = await WebflowService.Products.get(webflowProductId); + existingWebflowProduct = + await WebflowService.Products.get(webflowProductId); if (!existingWebflowProduct) { console.error("Missing webflow product"); throw new Error("Failed to create Webflow product"); } for (const specialVariant of mainPrintfulProduct.variants) { - const newPrintfulVariants: DeepPartial[] = []; - for (const printfulVariant of specialVariant.product.sync_variants) { - const associatedWebflowSku = existingWebflowProduct.skus - .find(sku => sku.fieldData["sku-values"]?.["color"] === printfulVariant.color && - sku.fieldData["sku-values"]?.["size"] === printfulVariant.size); + const newPrintfulVariants: DeepPartial[] = + []; + for (const printfulVariant of specialVariant.product + .sync_variants) { + const associatedWebflowSku = + existingWebflowProduct.skus.find( + (sku) => + sku.fieldData["sku-values"]?.[ + "color" + ] === printfulVariant.color && + sku.fieldData["sku-values"]?.[ + "size" + ] === printfulVariant.size, + ); if (associatedWebflowSku) { newPrintfulVariants.push({ - "id": printfulVariant.id, // printful variant id - "external_id": String(associatedWebflowSku.id), // webflow variant id + id: printfulVariant.id, // printful variant id + external_id: String( + associatedWebflowSku.id, + ), // webflow variant id }); } } await sleep(10000); - await PrintfulService.Products.update(specialVariant.product.sync_product.id, { - "sync_product": { - "id": specialVariant.product.sync_product.id, - "external_id": `${webflowProductId}-${specialVariant.color}` + await PrintfulService.Products.update( + specialVariant.product.sync_product.id, + { + sync_product: { + id: specialVariant.product.sync_product.id, + external_id: `${webflowProductId}-${specialVariant.color}`, + }, + sync_variants: newPrintfulVariants, }, - "sync_variants": newPrintfulVariants - }); + ); } } - } - - // SYNC IMAGES - // for (const sku of existingWebflowProduct.skus) { - // const skuColor = sku.fieldData["sku-values"]?.["color"]?.toLowerCase() ?? "None"; - // const skuSlug = `${existingWebflowProduct.product.fieldData.slug}-${skuColor}`; - // const skuImageUrls = await this.getProductImageUrls(skuSlug); - // - // sku.fieldData["main-image"] = skuImageUrls[0] ?? sku.fieldData["main-image"]; - // sku.fieldData["more-images"] = skuImageUrls.slice(1).map(url => ({ url })); - // - // await WebflowService.Products.Skus.update(existingWebflowProduct.product.id, sku.id, sku); - // } - // - catch (err) { + } catch (err) { + // SYNC IMAGES + // for (const sku of existingWebflowProduct.skus) { + // const skuColor = sku.fieldData["sku-values"]?.["color"]?.toLowerCase() ?? "None"; + // const skuSlug = `${existingWebflowProduct.product.fieldData.slug}-${skuColor}`; + // const skuImageUrls = await this.getProductImageUrls(skuSlug); + // + // sku.fieldData["main-image"] = skuImageUrls[0] ?? sku.fieldData["main-image"]; + // sku.fieldData["more-images"] = skuImageUrls.slice(1).map(url => ({ url })); + // + // await WebflowService.Products.Skus.update(existingWebflowProduct.product.id, sku.id, sku); + // } + // if (err instanceof FetchError) { console.error(err.message, err.payload); } @@ -268,14 +328,17 @@ export default class SyncService { } private static getProductImageUrls = async (slug: string) => { - const res = await (fetch(`${R2_WORKER_URL}/product-images/${slug}`)); + const res = await fetch(`${R2_WORKER_URL}/product-images/${slug}`); if (!res.ok) { - console.error("Failed to get product image keys:", await res.json()); + console.error( + "Failed to get product image keys:", + await res.json(), + ); throw new Error("Failed to get product image keys"); } const productImageKeys: string[] = await res.json(); - return productImageKeys.map(key => `${WEBSITE_MEDIA_URL}/${key}`); - } + return productImageKeys.map((key) => `${WEBSITE_MEDIA_URL}/${key}`); + }; public static findColorInProductName = (productName: string): string => { const m = productName.match(/\[([^\]]+)\]/); @@ -286,8 +349,7 @@ export default class SyncService { const colorInName = this.findColorInProductName(productName); if (colorInName) { return productName.replace(`[${colorInName}]`, "").trimEnd(); - } - else { + } else { return productName; } }; diff --git a/src/lib/services/commerce/util/misc.ts b/src/lib/services/commerce/util/misc.ts index 05c165e..9db0d3a 100644 --- a/src/lib/services/commerce/util/misc.ts +++ b/src/lib/services/commerce/util/misc.ts @@ -1,21 +1,26 @@ -export type DeepPartial = T extends object ? { - [P in keyof T]?: DeepPartial; -} : T; +export type DeepPartial = T extends object + ? { + [P in keyof T]?: DeepPartial; + } + : T; export const formatSlug = (name: string): string => { return name .toLowerCase() - .replace(/[^a-z0-9-_]/g, '-') // replace illegal chars with dashes - .replace(/^-+/, '') // remove leading dashes - .replace(/-+$/, '') // remove trailing dashes - .replace(/--+/g, '-') // collapse multiple dashes - .replace(/^([^a-z0-9_])/, '_$1'); // if it starts with an invalid char, prefix underscore + .replace(/[^a-z0-9-_]/g, "-") // replace illegal chars with dashes + .replace(/^-+/, "") // remove leading dashes + .replace(/-+$/, "") // remove trailing dashes + .replace(/--+/g, "-") // collapse multiple dashes + .replace(/^([^a-z0-9_])/, "_$1"); // if it starts with an invalid char, prefix underscore }; export class FetchError extends Error { public payload: Object | undefined; - constructor(message: string, public response: Response) { + constructor( + message: string, + public response: Response, + ) { super(message); this.name = "FetchError"; } @@ -25,24 +30,22 @@ export class FetchError extends Error { this.payload = await this.response.clone().json(); } catch { this.payload = await this.response.text().catch(() => undefined); - } - finally { + } finally { return this.payload; } } } export function getStateFromZip(zipString: string): string { - /* Ensure param is a string to prevent unpredictable parsing results */ - if (typeof zipString !== 'string') { - console.error('Must pass the zipcode as a string.'); + if (typeof zipString !== "string") { + console.error("Must pass the zipcode as a string."); return "none"; } /* Ensure we have exactly 5 characters to parse */ if (zipString.length !== 5) { - console.error('Must pass a 5-digit zipcode.'); + console.error("Must pass a 5-digit zipcode."); return "none"; } @@ -54,164 +57,194 @@ export function getStateFromZip(zipString: string): string { /* Code cases alphabetized by state */ if (zipcode >= 35000 && zipcode <= 36999) { - st = 'AL'; - state = 'Alabama'; + st = "AL"; + state = "Alabama"; } else if (zipcode >= 99500 && zipcode <= 99999) { - st = 'AK'; - state = 'Alaska'; + st = "AK"; + state = "Alaska"; } else if (zipcode >= 85000 && zipcode <= 86999) { - st = 'AZ'; - state = 'Arizona'; + st = "AZ"; + state = "Arizona"; } else if (zipcode >= 71600 && zipcode <= 72999) { - st = 'AR'; - state = 'Arkansas'; + st = "AR"; + state = "Arkansas"; } else if (zipcode >= 90000 && zipcode <= 96699) { - st = 'CA'; - state = 'California'; + st = "CA"; + state = "California"; } else if (zipcode >= 80000 && zipcode <= 81999) { - st = 'CO'; - state = 'Colorado'; - } else if ((zipcode >= 6000 && zipcode <= 6389) || (zipcode >= 6391 && zipcode <= 6999)) { - st = 'CT'; - state = 'Connecticut'; + st = "CO"; + state = "Colorado"; + } else if ( + (zipcode >= 6000 && zipcode <= 6389) || + (zipcode >= 6391 && zipcode <= 6999) + ) { + st = "CT"; + state = "Connecticut"; } else if (zipcode >= 19700 && zipcode <= 19999) { - st = 'DE'; - state = 'Delaware'; + st = "DE"; + state = "Delaware"; } else if (zipcode >= 32000 && zipcode <= 34999) { - st = 'FL'; - state = 'Florida'; - } else if ((zipcode >= 30000 && zipcode <= 31999) || (zipcode >= 39800 && zipcode <= 39999)) { - st = 'GA'; - state = 'Georgia'; + st = "FL"; + state = "Florida"; + } else if ( + (zipcode >= 30000 && zipcode <= 31999) || + (zipcode >= 39800 && zipcode <= 39999) + ) { + st = "GA"; + state = "Georgia"; } else if (zipcode >= 96700 && zipcode <= 96999) { - st = 'HI'; - state = 'Hawaii'; + st = "HI"; + state = "Hawaii"; } else if (zipcode >= 83200 && zipcode <= 83999 && zipcode != 83414) { - st = 'ID'; - state = 'Idaho'; + st = "ID"; + state = "Idaho"; } else if (zipcode >= 60000 && zipcode <= 62999) { - st = 'IL'; - state = 'Illinois'; + st = "IL"; + state = "Illinois"; } else if (zipcode >= 46000 && zipcode <= 47999) { - st = 'IN'; - state = 'Indiana'; + st = "IN"; + state = "Indiana"; } else if (zipcode >= 50000 && zipcode <= 52999) { - st = 'IA'; - state = 'Iowa'; + st = "IA"; + state = "Iowa"; } else if (zipcode >= 66000 && zipcode <= 67999) { - st = 'KS'; - state = 'Kansas'; + st = "KS"; + state = "Kansas"; } else if (zipcode >= 40000 && zipcode <= 42999) { - st = 'KY'; - state = 'Kentucky'; + st = "KY"; + state = "Kentucky"; } else if (zipcode >= 70000 && zipcode <= 71599) { - st = 'LA'; - state = 'Louisiana'; + st = "LA"; + state = "Louisiana"; } else if (zipcode >= 3900 && zipcode <= 4999) { - st = 'ME'; - state = 'Maine'; + st = "ME"; + state = "Maine"; } else if (zipcode >= 20600 && zipcode <= 21999) { - st = 'MD'; - state = 'Maryland'; - } else if ((zipcode >= 1000 && zipcode <= 2799) || (zipcode == 5501) || (zipcode == 5544)) { - st = 'MA'; - state = 'Massachusetts'; + st = "MD"; + state = "Maryland"; + } else if ( + (zipcode >= 1000 && zipcode <= 2799) || + zipcode == 5501 || + zipcode == 5544 + ) { + st = "MA"; + state = "Massachusetts"; } else if (zipcode >= 48000 && zipcode <= 49999) { - st = 'MI'; - state = 'Michigan'; + st = "MI"; + state = "Michigan"; } else if (zipcode >= 55000 && zipcode <= 56899) { - st = 'MN'; - state = 'Minnesota'; + st = "MN"; + state = "Minnesota"; } else if (zipcode >= 38600 && zipcode <= 39999) { - st = 'MS'; - state = 'Mississippi'; + st = "MS"; + state = "Mississippi"; } else if (zipcode >= 63000 && zipcode <= 65999) { - st = 'MO'; - state = 'Missouri'; + st = "MO"; + state = "Missouri"; } else if (zipcode >= 59000 && zipcode <= 59999) { - st = 'MT'; - state = 'Montana'; + st = "MT"; + state = "Montana"; } else if (zipcode >= 27000 && zipcode <= 28999) { - st = 'NC'; - state = 'North Carolina'; + st = "NC"; + state = "North Carolina"; } else if (zipcode >= 58000 && zipcode <= 58999) { - st = 'ND'; - state = 'North Dakota'; + st = "ND"; + state = "North Dakota"; } else if (zipcode >= 68000 && zipcode <= 69999) { - st = 'NE'; - state = 'Nebraska'; + st = "NE"; + state = "Nebraska"; } else if (zipcode >= 88900 && zipcode <= 89999) { - st = 'NV'; - state = 'Nevada'; + st = "NV"; + state = "Nevada"; } else if (zipcode >= 3000 && zipcode <= 3899) { - st = 'NH'; - state = 'New Hampshire'; + st = "NH"; + state = "New Hampshire"; } else if (zipcode >= 7000 && zipcode <= 8999) { - st = 'NJ'; - state = 'New Jersey'; + st = "NJ"; + state = "New Jersey"; } else if (zipcode >= 87000 && zipcode <= 88499) { - st = 'NM'; - state = 'New Mexico'; - } else if ((zipcode >= 10000 && zipcode <= 14999) || (zipcode == 6390) || (zipcode == 501) || (zipcode == 544)) { - st = 'NY'; - state = 'New York'; + st = "NM"; + state = "New Mexico"; + } else if ( + (zipcode >= 10000 && zipcode <= 14999) || + zipcode == 6390 || + zipcode == 501 || + zipcode == 544 + ) { + st = "NY"; + state = "New York"; } else if (zipcode >= 43000 && zipcode <= 45999) { - st = 'OH'; - state = 'Ohio'; - } else if ((zipcode >= 73000 && zipcode <= 73199) || (zipcode >= 73400 && zipcode <= 74999)) { - st = 'OK'; - state = 'Oklahoma'; + st = "OH"; + state = "Ohio"; + } else if ( + (zipcode >= 73000 && zipcode <= 73199) || + (zipcode >= 73400 && zipcode <= 74999) + ) { + st = "OK"; + state = "Oklahoma"; } else if (zipcode >= 97000 && zipcode <= 97999) { - st = 'OR'; - state = 'Oregon'; + st = "OR"; + state = "Oregon"; } else if (zipcode >= 15000 && zipcode <= 19699) { - st = 'PA'; - state = 'Pennsylvania'; + st = "PA"; + state = "Pennsylvania"; } else if (zipcode >= 300 && zipcode <= 999) { - st = 'PR'; - state = 'Puerto Rico'; + st = "PR"; + state = "Puerto Rico"; } else if (zipcode >= 2800 && zipcode <= 2999) { - st = 'RI'; - state = 'Rhode Island'; + st = "RI"; + state = "Rhode Island"; } else if (zipcode >= 29000 && zipcode <= 29999) { - st = 'SC'; - state = 'South Carolina'; + st = "SC"; + state = "South Carolina"; } else if (zipcode >= 57000 && zipcode <= 57999) { - st = 'SD'; - state = 'South Dakota'; + st = "SD"; + state = "South Dakota"; } else if (zipcode >= 37000 && zipcode <= 38599) { - st = 'TN'; - state = 'Tennessee'; - } else if ((zipcode >= 75000 && zipcode <= 79999) || (zipcode >= 73301 && zipcode <= 73399) || (zipcode >= 88500 && zipcode <= 88599)) { - st = 'TX'; - state = 'Texas'; + st = "TN"; + state = "Tennessee"; + } else if ( + (zipcode >= 75000 && zipcode <= 79999) || + (zipcode >= 73301 && zipcode <= 73399) || + (zipcode >= 88500 && zipcode <= 88599) + ) { + st = "TX"; + state = "Texas"; } else if (zipcode >= 84000 && zipcode <= 84999) { - st = 'UT'; - state = 'Utah'; + st = "UT"; + state = "Utah"; } else if (zipcode >= 5000 && zipcode <= 5999) { - st = 'VT'; - state = 'Vermont'; - } else if ((zipcode >= 20100 && zipcode <= 20199) || (zipcode >= 22000 && zipcode <= 24699) || (zipcode == 20598)) { - st = 'VA'; - state = 'Virginia'; - } else if ((zipcode >= 20000 && zipcode <= 20099) || (zipcode >= 20200 && zipcode <= 20599) || (zipcode >= 56900 && zipcode <= 56999)) { - st = 'DC'; - state = 'Washington DC'; + st = "VT"; + state = "Vermont"; + } else if ( + (zipcode >= 20100 && zipcode <= 20199) || + (zipcode >= 22000 && zipcode <= 24699) || + zipcode == 20598 + ) { + st = "VA"; + state = "Virginia"; + } else if ( + (zipcode >= 20000 && zipcode <= 20099) || + (zipcode >= 20200 && zipcode <= 20599) || + (zipcode >= 56900 && zipcode <= 56999) + ) { + st = "DC"; + state = "Washington DC"; } else if (zipcode >= 98000 && zipcode <= 99499) { - st = 'WA'; - state = 'Washington'; + st = "WA"; + state = "Washington"; } else if (zipcode >= 24700 && zipcode <= 26999) { - st = 'WV'; - state = 'West Virginia'; + st = "WV"; + state = "West Virginia"; } else if (zipcode >= 53000 && zipcode <= 54999) { - st = 'WI'; - state = 'Wisconsin'; + st = "WI"; + state = "Wisconsin"; } else if ((zipcode >= 82000 && zipcode <= 83199) || zipcode == 83414) { - st = 'WY'; - state = 'Wyoming'; + st = "WY"; + state = "Wyoming"; } else { - st = 'none'; - state = 'none'; + st = "none"; + state = "none"; } /* Return `state` for full name or `st` for postal abbreviation */ diff --git a/src/lib/services/commerce/util/types.ts b/src/lib/services/commerce/util/types.ts index 3d94fa6..5b46327 100644 --- a/src/lib/services/commerce/util/types.ts +++ b/src/lib/services/commerce/util/types.ts @@ -11,16 +11,16 @@ export namespace Printful { code: number; result: T[]; paging: { - total: number, - offset: number, - limit: number - } + total: number; + offset: number; + limit: number; + }; } type Option = { id: string; value: string; - } + }; type File = { type: string; @@ -41,12 +41,12 @@ export namespace Printful { visible: boolean; is_temporary: boolean; stitch_count_tier: string; - } + }; export type Product = { - sync_product: SyncProduct, - sync_variants: SyncVariant[] - } + sync_product: SyncProduct; + sync_variants: SyncVariant[]; + }; export type SyncProduct = { id: number; @@ -56,7 +56,7 @@ export namespace Printful { synced: number; thumbnail_url: string; is_ignored: boolean; - } + }; export type SyncVariant = { id: number; @@ -83,30 +83,30 @@ export namespace Printful { size: string; color: string; availability_status: string; - } + }; } export namespace Orders { export type Order = { - external_id: string, - shipping: string + external_id: string; + shipping: string; recipient: { - name: string - company?: string - address1: string - address2: string - city: string - state_code: string - state_name?: string - country_code: string - country_name?: string - zip: string - phone?: string - email?: string - tax_number?: string - } - items: Array<{ external_variant_id: string, quantity: number }> - } + name: string; + company?: string; + address1: string; + address2: string; + city: string; + state_code: string; + state_name?: string; + country_code: string; + country_name?: string; + zip: string; + phone?: string; + email?: string; + tax_number?: string; + }; + items: Array<{ external_variant_id: string; quantity: number }>; + }; } export namespace Webhook { @@ -116,55 +116,70 @@ export namespace Printful { created: number; retries: number; store: number; - data: D + data: D; } export enum Event { ProductUpdated = "product_updated", ProductDeleted = "product_deleted", - PackageShipped = "package_shipped" + PackageShipped = "package_shipped", } - export interface ProductUpdated extends MetaData { } + export interface ProductUpdated + extends MetaData< + Event.ProductUpdated, + { + sync_product: { + id: number; + external_id: string; + name: string; + variants: number; + synced: number; + thumbnail_url: string; + is_ignored: boolean; + }; + } + > {} - export interface ProductDeleted extends MetaData { } + export interface ProductDeleted + extends MetaData< + Event.ProductDeleted, + { + sync_product: { + id: number; + external_id: string; + name: string; + }; + } + > {} - export interface PackageShipped extends MetaData { } + export interface PackageShipped + extends MetaData< + Event.PackageShipped, + { + shipment: { + id: number; + carrier: string; + service: string; + tracking_number: string; + tracking_url: string; + created: number; + ship_date: string; + shipped_at: number; + reshipment: boolean; + }; + order: { + id: number; + external_id: string; + status: string; + }; + } + > {} - export type EventPayload = ProductUpdated | ProductDeleted | PackageShipped + export type EventPayload = + | ProductUpdated + | ProductDeleted + | PackageShipped; } } @@ -172,29 +187,28 @@ export namespace Webflow { export namespace Products { export namespace Skus { export type Sku = { - id: string, + id: string; fieldData: { name: string; slug: string; - "sku-values"?: Record, + "sku-values"?: Record; price: { value: number; unit: string; currency: string; }; "main-image"?: string; - "more-images"?: - { - fileId?: string, - url: string, - alt?: string, - }[] - } - } + "more-images"?: { + fileId?: string; + url: string; + alt?: string; + }[]; + }; + }; } export type Product = { - id: string, + id: string; fieldData: { name: string; slug: string; @@ -210,12 +224,12 @@ export namespace Webflow { slug: string; }[]; }[]; - } - } + }; + }; export interface ProductAndSkus { - product: Product - skus: Skus.Sku[] + product: Product; + skus: Skus.Sku[]; } export interface ProductAndSku { @@ -227,85 +241,91 @@ export namespace Webflow { export namespace Orders { export type Order = { - orderId: string - status: "pending" | "unfulfilled" | "fulfilled" | "disputed" | "dispute-lost" | "refunded" - comment: string - orderComment: string - acceptedOn: string - fulfilledOn: string | null - refundedOn: string | null - disputedOn: string | null - disputeUpdatedOn: string | null - disputeLastStatus: string | null + orderId: string; + status: + | "pending" + | "unfulfilled" + | "fulfilled" + | "disputed" + | "dispute-lost" + | "refunded"; + comment: string; + orderComment: string; + acceptedOn: string; + fulfilledOn: string | null; + refundedOn: string | null; + disputedOn: string | null; + disputeUpdatedOn: string | null; + disputeLastStatus: string | null; customerPaid: { - unit: string - value: string - string: string - } + unit: string; + value: string; + string: string; + }; netAmount: { - unit: string - value: string - string: string - } + unit: string; + value: string; + string: string; + }; applicationFee: { - unit: string - value: string - string: string - } + unit: string; + value: string; + string: string; + }; allAddresses: Array<{ - type: string - addressee: string - line1: string - line2: string - city: string - state: string - country: string - postalCode: string - }> + type: string; + addressee: string; + line1: string; + line2: string; + city: string; + state: string; + country: string; + postalCode: string; + }>; shippingAddress: { - type: string - japanType: string - addressee: string - line1: string - line2: string - city: string - state: string - country: string - postalCode: string - } + type: string; + japanType: string; + addressee: string; + line1: string; + line2: string; + city: string; + state: string; + country: string; + postalCode: string; + }; billingAddress: { - type: string - addressee: string - line1: string - line2: string - city: string - state: string - country: string - postalCode: string - } - shippingProvider: string - shippingTracking: string - shippingTrackingURL: string + type: string; + addressee: string; + line1: string; + line2: string; + city: string; + state: string; + country: string; + postalCode: string; + }; + shippingProvider: string; + shippingTracking: string; + shippingTrackingURL: string; customerInfo: { - fullName: string - email: string - } + fullName: string; + email: string; + }; purchasedItems: Array<{ - count: number + count: number; rowTotal: { - unit: string - value: string - string: string - } - productId: string - productName: string - productSlug: string - variantId: string - variantName: string - variantSlug: string - variantSKU: string + unit: string; + value: string; + string: string; + }; + productId: string; + productName: string; + productSlug: string; + variantId: string; + variantName: string; + variantSlug: string; + variantSKU: string; variantImage: { - url: string + url: string; file: { size: number; originalFileName: string; @@ -315,95 +335,97 @@ export namespace Webflow { height: number; variants: { size: number; - url: string, + url: string; originalFileName: string; width: number; height: number; - }[] - } - } + }[]; + }; + }; variantPrice: { - unit: string - value: string - string: string - } - weight: number - width: number - height: number - length: number - }> - purchasedItemsCount: number + unit: string; + value: string; + string: string; + }; + weight: number; + width: number; + height: number; + length: number; + }>; + purchasedItemsCount: number; stripeDetails: { - subscriptionId: string | null - paymentMethod: string - paymentIntentId: string - customerId: string - chargeId: string - disputeId: string | null - refundId: string - refundReason: string - } + subscriptionId: string | null; + paymentMethod: string; + paymentIntentId: string; + customerId: string; + chargeId: string; + disputeId: string | null; + refundId: string; + refundReason: string; + }; stripeCard: { - last4: string - brand: string - ownerName: string + last4: string; + brand: string; + ownerName: string; expires: { - year: number - month: number - } - } - paypalDetails: Object - customData: Array + year: number; + month: number; + }; + }; + paypalDetails: Object; + customData: Array; metadata: { - isBuyNow: boolean - hasDownloads: boolean - paymentProcessor: string - } - isCustomerDeleted: boolean - isShippingRequired: boolean + isBuyNow: boolean; + hasDownloads: boolean; + paymentProcessor: string; + }; + isCustomerDeleted: boolean; + isShippingRequired: boolean; totals: { subtotal: { - unit: string - value: string - string: string - } + unit: string; + value: string; + string: string; + }; extras: Array<{ - type: string - name: string - description: string + type: string; + name: string; + description: string; price: { - unit: string - value: string - string: string - } - }> + unit: string; + value: string; + string: string; + }; + }>; total: { - unit: string - value: string - string: string - } - } + unit: string; + value: string; + string: string; + }; + }; downloadFiles: Array<{ - id: string - name: string - url: string - }> - } + id: string; + name: string; + url: string; + }>; + }; } export namespace Webhook { interface MetaData { triggerType: E; - payload: D + payload: D; } export enum Event { OrderCreated = "ecomm_new_order", - OrderUpdated = "ecomm_order_changed" + OrderUpdated = "ecomm_order_changed", } - export interface OrderCreated extends MetaData { } - export interface OrderUpdated extends MetaData { } + export interface OrderCreated + extends MetaData {} + export interface OrderUpdated + extends MetaData {} export type EventPayload = OrderCreated | OrderUpdated; } diff --git a/src/lib/services/commerce/webflow.ts b/src/lib/services/commerce/webflow.ts index 49f3a84..ef29e80 100644 --- a/src/lib/services/commerce/webflow.ts +++ b/src/lib/services/commerce/webflow.ts @@ -5,48 +5,69 @@ import crypto from "node:crypto"; export default class WebflowService { static Products = class { static Skus = class { - static async create(webflowProductId: string, skus: DeepPartial[]) { - const res = await fetch(`${env().API_SITES_URL}/products/${webflowProductId}/skus`, { - method: "POST", - headers: { - "Content-Type": "application/json", - ...env().AUTH_HEADER + static async create( + webflowProductId: string, + skus: DeepPartial[], + ) { + const res = await fetch( + `${env().API_SITES_URL}/products/${webflowProductId}/skus`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + ...env().AUTH_HEADER, + }, + body: JSON.stringify({ + skus: skus, + }), }, - body: JSON.stringify({ - "skus": skus - }) - }); + ); if (!res.ok) { - throw new FetchError("Failed to create Webflow product SKU", res); + throw new FetchError( + "Failed to create Webflow product SKU", + res, + ); } } - static async update(webflowProductId: string, webflowSkuId: string, webflowSku: DeepPartial) { - const res = await fetch(`${env().API_SITES_URL}/products/${webflowProductId}/skus/${webflowSkuId}`, { - method: "PATCH", - headers: { - "Content-Type": "application/json", - ...env().AUTH_HEADER + static async update( + webflowProductId: string, + webflowSkuId: string, + webflowSku: DeepPartial, + ) { + const res = await fetch( + `${env().API_SITES_URL}/products/${webflowProductId}/skus/${webflowSkuId}`, + { + method: "PATCH", + headers: { + "Content-Type": "application/json", + ...env().AUTH_HEADER, + }, + body: JSON.stringify({ + sku: webflowSku, + }), }, - body: JSON.stringify({ - "sku": webflowSku - }) - }); + ); if (!res.ok) { - throw new FetchError("Failed to update Webflow product SKU", res); + throw new FetchError( + "Failed to update Webflow product SKU", + res, + ); } } - } + }; - static async create(webflowProductAndSku: DeepPartial) { + static async create( + webflowProductAndSku: DeepPartial, + ) { const res = await fetch(`${env().API_SITES_URL}/products`, { method: "POST", headers: { "Content-Type": "application/json", - ...env().AUTH_HEADER + ...env().AUTH_HEADER, }, - body: JSON.stringify(webflowProductAndSku) + body: JSON.stringify(webflowProductAndSku), }); if (!res.ok) { @@ -54,7 +75,7 @@ export default class WebflowService { } const createdWebflowProduct = await res.json(); - return createdWebflowProduct.product.id + return createdWebflowProduct.product.id; } static async getAll(): Promise { @@ -62,7 +83,7 @@ export default class WebflowService { method: "GET", headers: { ...env().AUTH_HEADER, - } + }, }); if (!res.ok) { @@ -73,13 +94,18 @@ export default class WebflowService { return payload.items as Webflow.Products.ProductAndSkus[]; } - static async get(webflowProductId: string): Promise { - const res = await fetch(`${env().API_SITES_URL}/products/${webflowProductId}`, { - method: "GET", - headers: { - ...env().AUTH_HEADER, - } - }); + static async get( + webflowProductId: string, + ): Promise { + const res = await fetch( + `${env().API_SITES_URL}/products/${webflowProductId}`, + { + method: "GET", + headers: { + ...env().AUTH_HEADER, + }, + }, + ); if (res.status === 404 || res.status === 400) { return; @@ -93,15 +119,21 @@ export default class WebflowService { return payload as Webflow.Products.ProductAndSkus; } - static async update(webflowProductId: string, webflowProduct: DeepPartial) { - const res = await fetch(`${env().API_SITES_URL}/products/${webflowProductId}`, { - method: "PATCH", - headers: { - "Content-Type": "application/json", - ...env().AUTH_HEADER + static async update( + webflowProductId: string, + webflowProduct: DeepPartial, + ) { + const res = await fetch( + `${env().API_SITES_URL}/products/${webflowProductId}`, + { + method: "PATCH", + headers: { + "Content-Type": "application/json", + ...env().AUTH_HEADER, + }, + body: JSON.stringify(webflowProduct), }, - body: JSON.stringify(webflowProduct) - }); + ); if (!res.ok) { throw new FetchError("Failed to update Webflow product", res); @@ -109,33 +141,41 @@ export default class WebflowService { } static async remove(webflowProductId: string) { - const res = await fetch(`${env().API_COLLECTIONS_URL}/items/${webflowProductId}`, { - method: "DELETE", - headers: { - "Content-Type": "application/json", - ...env().AUTH_HEADER + const res = await fetch( + `${env().API_COLLECTIONS_URL}/items/${webflowProductId}`, + { + method: "DELETE", + headers: { + "Content-Type": "application/json", + ...env().AUTH_HEADER, + }, + body: JSON.stringify({}), }, - body: JSON.stringify({}) - }); + ); if (!res.ok) { throw new FetchError("Failed to remove Webflow product", res); } } - } + }; static Orders = class { - static async getAll(opt: { status?: Webflow.Orders.Order["status"] } = {}): Promise { + static async getAll( + opt: { status?: Webflow.Orders.Order["status"] } = {}, + ): Promise { // TODO: pagination const params = new URLSearchParams(); if (opt.status !== undefined) { params.set("status", opt.status); } - const res = await fetch(`${env().API_SITES_URL}/orders?${params.toString()}`, { - method: "GET", - headers: { ...env().AUTH_HEADER }, - }); + const res = await fetch( + `${env().API_SITES_URL}/orders?${params.toString()}`, + { + method: "GET", + headers: { ...env().AUTH_HEADER }, + }, + ); if (!res.ok) { throw new FetchError("Failed to get all Webflow orders", res); @@ -145,36 +185,53 @@ export default class WebflowService { return payload.orders as Webflow.Orders.Order[]; } - static async update(webflowOrderId: string, webflowOrderUpdate: { comment?: string, shippingProvider?: string, shippingTracking?: string, shippingTrackingURL: string }) { - const res = await fetch(`${env().API_SITES_URL}/orders/${webflowOrderId}`, { - method: "PATCH", - headers: { - "Content-Type": "application/json", - ...env().AUTH_HEADER + static async update( + webflowOrderId: string, + webflowOrderUpdate: { + comment?: string; + shippingProvider?: string; + shippingTracking?: string; + shippingTrackingURL: string; + }, + ) { + const res = await fetch( + `${env().API_SITES_URL}/orders/${webflowOrderId}`, + { + method: "PATCH", + headers: { + "Content-Type": "application/json", + ...env().AUTH_HEADER, + }, + body: JSON.stringify(webflowOrderUpdate), }, - body: JSON.stringify(webflowOrderUpdate) - }); + ); if (!res.ok) { throw new FetchError("Failed to update Webflow order", res); } } - static async fulfill(webflowOrderId: string, opt: { sendOrderFulfilledEmail?: boolean } = {}) { - const res = await fetch(`${env().API_SITES_URL}/orders/${webflowOrderId}/fulfill`, { - method: "POST", - headers: { - "Content-Type": "application/json", - ...env().AUTH_HEADER + static async fulfill( + webflowOrderId: string, + opt: { sendOrderFulfilledEmail?: boolean } = {}, + ) { + const res = await fetch( + `${env().API_SITES_URL}/orders/${webflowOrderId}/fulfill`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + ...env().AUTH_HEADER, + }, + body: JSON.stringify(opt), }, - body: JSON.stringify(opt) - }); + ); if (!res.ok) { throw new FetchError("Failed to fulfill Webflow order", res); } } - } + }; static Util = class { static verifyWebflowSignature(request: Request, body: unknown) { @@ -183,7 +240,9 @@ export default class WebflowService { if (!timestamp) { throw new Error("No timestamp provided"); } - const providedSignature = request.headers.get("x-webflow-signature"); + const providedSignature = request.headers.get( + "x-webflow-signature", + ); if (!providedSignature) { throw new Error("No signature provided"); } @@ -199,47 +258,52 @@ export default class WebflowService { const requestTimestamp = parseInt(timestamp, 10); const data = `${requestTimestamp}:${JSON.stringify(body)}`; - const hash = crypto.createHmac('sha256', secret) + const hash = crypto + .createHmac("sha256", secret) .update(data) - .digest('hex'); + .digest("hex"); - if (!crypto.timingSafeEqual(Buffer.from(hash, 'hex'), Buffer.from(providedSignature, 'hex'))) { - throw new Error('Invalid signature'); + if ( + !crypto.timingSafeEqual( + Buffer.from(hash, "hex"), + Buffer.from(providedSignature, "hex"), + ) + ) { + throw new Error("Invalid signature"); } const currentTime = Date.now(); if (currentTime - requestTimestamp > 300000) { - throw new Error('Request is older than 5 minutes'); + throw new Error("Request is older than 5 minutes"); } return true; - } catch (err) { console.error(`Error verifying signature: ${err}`); return false; } - } - } + }; } const env = () => { if (typeof Bun === "undefined") { - throw new Error("Must be in a server context. Make sure to run using --bun."); + throw new Error( + "Must be in a server context. Make sure to run using --bun.", + ); } const vars = { SITE_ID: Bun.env.WEBFLOW_SITE_ID, COLLECTIONS_ID: Bun.env.WEBFLOW_COLLECTION_ID, AUTH_TOKEN: Bun.env.WEBFLOW_AUTH, - WEBHOOK_SECRET: Bun.env.WEBFLOW_WEBHOOK_SECRET + WEBHOOK_SECRET: Bun.env.WEBFLOW_WEBHOOK_SECRET, }; return { ...vars, API_SITES_URL: `https://api.webflow.com/v2/sites/${vars.SITE_ID}`, API_COLLECTIONS_URL: `https://api.webflow.com/v2/collections/${vars.COLLECTIONS_ID}`, - AUTH_HEADER: { "Authorization": `bearer ${vars.AUTH_TOKEN}` } + AUTH_HEADER: { Authorization: `bearer ${vars.AUTH_TOKEN}` }, }; }; - diff --git a/src/lib/test/scratch.ts b/src/lib/test/scratch.ts index ae7e348..e072977 100644 --- a/src/lib/test/scratch.ts +++ b/src/lib/test/scratch.ts @@ -1,25 +1,37 @@ -import { Activity, ftToCm, Gender, getAvgWeight, inToCm, kgToLb, lbToKg, minToMs, secToMs, type ActivityPerformance, type Player } from "$lib/services/calculator/util"; +import { + Activity, + ftToCm, + Gender, + getAvgWeight, + inToCm, + kgToLb, + lbToKg, + minToMs, + secToMs, + type ActivityPerformance, + type Player, +} from "$lib/services/calculator/util"; import PrintfulService from "$lib/services/commerce/printful"; import SyncService from "$lib/services/commerce/sync"; import { FetchError, type DeepPartial } from "$lib/services/commerce/util/misc"; import type { Printful } from "$lib/services/commerce/util/types"; import WebflowService from "$lib/services/commerce/webflow"; -const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); +const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); const player: Player = { metrics: { age: 25, weight: lbToKg(190), - gender: Gender.Male - } + gender: Gender.Male, + }, }; const computedPerformances: ActivityPerformance[] = [ // STRENGTH { activity: Activity.BenchPress, - performance: lbToKg(225) + performance: lbToKg(225), }, { activity: Activity.Deadlift, @@ -115,7 +127,6 @@ console.log(pOrders); // } // console.log("DONE"); - // await WebflowService.Products.update(wProduct?.product.id!, wProduct!); // // const sku = { diff --git a/src/lib/test/validate-products.ts b/src/lib/test/validate-products.ts index 8de7f5e..bff3ca9 100644 --- a/src/lib/test/validate-products.ts +++ b/src/lib/test/validate-products.ts @@ -2,12 +2,12 @@ import PrintfulService from "$lib/services/commerce/printful"; import SyncService from "$lib/services/commerce/sync"; import WebflowService from "$lib/services/commerce/webflow"; -const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); +const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); type Validation = { - isValid: boolean, - message: string -} + isValid: boolean; + message: string; +}; const validateEquality = (wValue: any, pValue: any): Validation => { const validation = { @@ -17,12 +17,11 @@ const validateEquality = (wValue: any, pValue: any): Validation => { if (wValue !== pValue) { validation.isValid = false; - validation.message = `${wValue} !== ${pValue}` - }; + validation.message = `${wValue} !== ${pValue}`; + } return validation; -} - +}; const wProducts = await WebflowService.Products.getAll(); @@ -31,14 +30,25 @@ for (const wProduct of wProducts) { for (const sku of wProduct.skus ?? []) { console.log("Webflow: " + sku.id, sku.fieldData.name); - const pVariant = await PrintfulService.Products.Variants.get(`@${sku.id}`); + const pVariant = await PrintfulService.Products.Variants.get( + `@${sku.id}`, + ); if (pVariant) { console.log("Printful: " + pVariant.id, pVariant.name); const validations = [ // validateEquality(sku.fieldData.name, pVariant.name), - validateEquality(sku.fieldData["sku-values"]?.["color"], SyncService.findColorInProductName(pVariant.name)), - validateEquality(sku.fieldData["sku-values"]?.["size"], pVariant.size), - validateEquality(sku.fieldData.price.value, Math.floor(+pVariant.retail_price * 100)), + validateEquality( + sku.fieldData["sku-values"]?.["color"], + SyncService.findColorInProductName(pVariant.name), + ), + validateEquality( + sku.fieldData["sku-values"]?.["size"], + pVariant.size, + ), + validateEquality( + sku.fieldData.price.value, + Math.floor(+pVariant.retail_price * 100), + ), ]; // if (!validateEquality(sku.fieldData.name, pVariant.name).isValid) { @@ -46,17 +56,15 @@ for (const wProduct of wProducts) { // await WebflowService.Products.Skus.update(wProduct.product.id, sku.id, sku); // } - const errors = validations.filter(v => !v.isValid); + const errors = validations.filter((v) => !v.isValid); if (errors.length) { ++invalidCount; console.log("INVALID: "); - console.log(errors.map(e => e.message).join(", ")); - } - else { + console.log(errors.map((e) => e.message).join(", ")); + } else { console.log("VALID"); } - } - else { + } else { ++invalidCount; console.log("INVALID"); console.log("Not found"); diff --git a/src/routes/+page.server.ts b/src/routes/+page.server.ts index c271734..563052d 100644 --- a/src/routes/+page.server.ts +++ b/src/routes/+page.server.ts @@ -1,6 +1,6 @@ -import { redirect } from '@sveltejs/kit'; -import type { PageServerLoad } from './$types'; +import { redirect } from "@sveltejs/kit"; +import type { PageServerLoad } from "./$types"; -export const load: PageServerLoad = ({ }) => { - redirect(308, '/calculator'); +export const load: PageServerLoad = ({}) => { + redirect(308, "/calculator"); }; diff --git a/src/routes/[...slugs]/+server.ts b/src/routes/[...slugs]/+server.ts index 8ae8391..08bc4e4 100644 --- a/src/routes/[...slugs]/+server.ts +++ b/src/routes/[...slugs]/+server.ts @@ -1,4 +1,4 @@ -import { app } from "$lib/server/app" +import { app } from "$lib/server/app"; -type RequestHandler = (v: { request: Request }) => Response | Promise -export const fallback: RequestHandler = ({ request }) => app.handle(request) +type RequestHandler = (v: { request: Request }) => Response | Promise; +export const fallback: RequestHandler = ({ request }) => app.handle(request); diff --git a/src/routes/calculator/+page.svelte b/src/routes/calculator/+page.svelte index c97e171..f49c1b2 100644 --- a/src/routes/calculator/+page.svelte +++ b/src/routes/calculator/+page.svelte @@ -1,309 +1,309 @@
-
- Data +
+ Data -
-
- - General - +
+
+ + General + -
- -
-
+
+ +
+
-
- - Data Generation - +
+ + Data Generation + -
- - - - - - - - - - - - - - - - - - - - -
-
-
-
+
+ + + + + + + + + + + + + + + + + + + + +
+
+ + - {#if activeTab === "standards"} -
- - Metrics - -
- + {#if activeTab === "standards"} +
+ + Metrics + +
+ - + - + - -
-
- {/if} + +
+
+ {/if}
- + - +
{#if activeTab === "standards"} - + {/if} {#if activeTab === "players"} - + {/if} diff --git a/src/routes/commerce/+page.server.ts b/src/routes/commerce/+page.server.ts index 5398b75..b1b4975 100644 --- a/src/routes/commerce/+page.server.ts +++ b/src/routes/commerce/+page.server.ts @@ -1,12 +1,12 @@ -import PrintfulService from '$lib/services/commerce/printful'; -import WebflowService from '$lib/services/commerce/webflow'; -import type { PageServerLoad } from './$types'; +import PrintfulService from "$lib/services/commerce/printful"; +import WebflowService from "$lib/services/commerce/webflow"; +import type { PageServerLoad } from "./$types"; -export const load = async ({ }: Parameters[0]) => { - return { - products: { - printful: PrintfulService.Products.getAll(), - webflow: WebflowService.Products.getAll(), - } - }; +export const load = async ({}: Parameters[0]) => { + return { + products: { + printful: PrintfulService.Products.getAll(), + webflow: WebflowService.Products.getAll(), + }, + }; }; diff --git a/svelte.config.js b/svelte.config.js index 10419b5..d9277d4 100644 --- a/svelte.config.js +++ b/svelte.config.js @@ -1,18 +1,18 @@ import adapter from "svelte-adapter-bun"; -import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; +import { vitePreprocess } from "@sveltejs/vite-plugin-svelte"; /** @type {import('@sveltejs/kit').Config} */ const config = { - // Consult https://svelte.dev/docs/kit/integrations - // for more information about preprocessors - preprocess: vitePreprocess(), + // Consult https://svelte.dev/docs/kit/integrations + // for more information about preprocessors + preprocess: vitePreprocess(), - kit: { - // adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list. - // If your environment is not supported, or you settled on a specific environment, switch out the adapter. - // See https://svelte.dev/docs/kit/adapters for more information about adapters. - adapter: adapter(), - } + kit: { + // adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list. + // If your environment is not supported, or you settled on a specific environment, switch out the adapter. + // See https://svelte.dev/docs/kit/adapters for more information about adapters. + adapter: adapter(), + }, }; export default config; diff --git a/tsconfig.json b/tsconfig.json index 39a4099..ecd756a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,21 +1,20 @@ { - "extends": "./.svelte-kit/tsconfig.json", - "compilerOptions": { - "allowJs": true, - "checkJs": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "resolveJsonModule": true, - "verbatimModuleSyntax": true, - "skipLibCheck": true, - "sourceMap": true, - "strict": true, - "moduleResolution": "bundler", - "importsNotUsedAsValue": true - } - // Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias - // except $lib which is handled by https://svelte.dev/docs/kit/configuration#files - // - // To make changes to top-level options such as include and exclude, we recommend extending - // the generated config; see https://svelte.dev/docs/kit/configuration#typescript + "extends": "./.svelte-kit/tsconfig.json", + "compilerOptions": { + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "verbatimModuleSyntax": true, + "skipLibCheck": true, + "sourceMap": true, + "strict": true, + "moduleResolution": "bundler" + } + // Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias + // except $lib which is handled by https://svelte.dev/docs/kit/configuration#files + // + // To make changes to top-level options such as include and exclude, we recommend extending + // the generated config; see https://svelte.dev/docs/kit/configuration#typescript } diff --git a/vite.config.ts b/vite.config.ts index 3ca802f..deb4172 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,7 +1,7 @@ import tailwindcss from "@tailwindcss/vite"; -import { sveltekit } from '@sveltejs/kit/vite'; -import { defineConfig } from 'vite'; +import { sveltekit } from "@sveltejs/kit/vite"; +import { defineConfig } from "vite"; export default defineConfig({ - plugins: [tailwindcss(), sveltekit()] + plugins: [tailwindcss(), sveltekit()], });