Format
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"tabWidth": 4
|
||||
}
|
||||
@@ -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=="],
|
||||
|
||||
+28
-28
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+7
-7
@@ -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 {};
|
||||
|
||||
+8
-8
@@ -1,11 +1,11 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
%sveltekit.head%
|
||||
</head>
|
||||
<body data-sveltekit-preload-data="hover">
|
||||
<div style="display: contents">%sveltekit.body%</div>
|
||||
</body>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
%sveltekit.head%
|
||||
</head>
|
||||
<body data-sveltekit-preload-data="hover">
|
||||
<div style="display: contents">%sveltekit.body%</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+3
-4
@@ -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<App>(baseURL).api;
|
||||
|
||||
+104
-104
@@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
+5307
-5307
File diff suppressed because it is too large
Load Diff
+171
-147
@@ -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 };
|
||||
});
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
import type { app } from './app';
|
||||
import type { app } from "./app";
|
||||
export type App = typeof app;
|
||||
|
||||
+307
-170
@@ -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<Attribute, number>
|
||||
}
|
||||
player: number;
|
||||
attributes: Record<Attribute, number>;
|
||||
};
|
||||
|
||||
export type Levels = Record<string, number>
|
||||
export type Levels = Record<string, number>;
|
||||
|
||||
export interface Metrics {
|
||||
age: number;
|
||||
weight: number;
|
||||
gender: Gender;
|
||||
};
|
||||
}
|
||||
|
||||
type NumberMetric = Extract<keyof Metrics, "age" | "weight">;
|
||||
|
||||
@@ -31,18 +41,21 @@ export interface Standard {
|
||||
}
|
||||
|
||||
type Generator = {
|
||||
metric: NumberMetric
|
||||
}
|
||||
metric: NumberMetric;
|
||||
};
|
||||
|
||||
export type ActivityStandards = Record<Activity, {
|
||||
metadata: {
|
||||
attribute: Attribute,
|
||||
generators: Generator[],
|
||||
unit: StandardUnit,
|
||||
name: string
|
||||
},
|
||||
standards: Standard[]
|
||||
}>;
|
||||
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<Activity, {
|
||||
weightModifier: number,
|
||||
weightSkew: number,
|
||||
ageModifier: number,
|
||||
enableGeneration: boolean,
|
||||
difficultyModifier: number
|
||||
}>
|
||||
}
|
||||
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<StandardsConfig>) {
|
||||
constructor(
|
||||
activityStandards: ActivityStandards,
|
||||
cfg?: Partial<StandardsConfig>,
|
||||
) {
|
||||
// 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<Activity, StandardsConfig["activity"][Activity]>;
|
||||
|
||||
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<Gender, number> = { 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<Gender, number> = {
|
||||
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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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<Printful.Products.SyncVariant | undefined> {
|
||||
const res = await fetch(`${env().API_URL}/store/variants/${variantId}`, {
|
||||
method: "GET",
|
||||
headers: { ...env().AUTH_HEADERS }
|
||||
});
|
||||
static async get(
|
||||
variantId: number | string,
|
||||
): Promise<Printful.Products.SyncVariant | undefined> {
|
||||
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<Printful.Products.SyncVariant> = await res.json();
|
||||
const payload: Printful.Products.MetaDataSingle<Printful.Products.SyncVariant> =
|
||||
await res.json();
|
||||
return payload.result;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static async getAll(offset: number = 0): Promise<Printful.Products.SyncProduct[]> {
|
||||
static async getAll(
|
||||
offset: number = 0,
|
||||
): Promise<Printful.Products.SyncProduct[]> {
|
||||
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<Printful.Products.SyncProduct> = await res.json();
|
||||
const payload: Printful.Products.MetaDataMulti<Printful.Products.SyncProduct> =
|
||||
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<Printful.Products.Product | undefined> {
|
||||
const res = await fetch(`${env().API_URL}/store/products/${productId}`, {
|
||||
method: "GET",
|
||||
headers: { ...env().AUTH_HEADERS }
|
||||
});
|
||||
static async get(
|
||||
productId: number | string,
|
||||
): Promise<Printful.Products.Product | undefined> {
|
||||
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<Printful.Products.Product> = await res.json();
|
||||
const payload: Printful.Products.MetaDataSingle<Printful.Products.Product> =
|
||||
await res.json();
|
||||
return payload.result;
|
||||
}
|
||||
|
||||
static async update(printfulProductId: number, printfulProduct: DeepPartial<Printful.Products.Product>) {
|
||||
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<Printful.Products.Product>,
|
||||
) {
|
||||
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<Printful.Orders.Order[]> {
|
||||
static async getAll(
|
||||
offset: number = 0,
|
||||
): Promise<Printful.Orders.Order[]> {
|
||||
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<Printful.Orders.Order> = await res.json();
|
||||
const payload: Printful.Products.MetaDataMulti<Printful.Orders.Order> =
|
||||
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}`,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
+205
-143
@@ -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<string>,
|
||||
name: string;
|
||||
externalId: string;
|
||||
colors: Set<string>;
|
||||
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<string> = new Set();
|
||||
|
||||
const webflowSkus: DeepPartial<Webflow.Products.Skus.Sku>[] = [];
|
||||
const webflowSkus: DeepPartial<Webflow.Products.Skus.Sku>[] =
|
||||
[];
|
||||
|
||||
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<Printful.Products.SyncVariant>[] = [];
|
||||
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<Printful.Products.SyncVariant>[] =
|
||||
[];
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,21 +1,26 @@
|
||||
export type DeepPartial<T> = T extends object ? {
|
||||
[P in keyof T]?: DeepPartial<T[P]>;
|
||||
} : T;
|
||||
export type DeepPartial<T> = T extends object
|
||||
? {
|
||||
[P in keyof T]?: DeepPartial<T[P]>;
|
||||
}
|
||||
: 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 */
|
||||
|
||||
@@ -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<Event.ProductUpdated, {
|
||||
sync_product: {
|
||||
id: number;
|
||||
external_id: string;
|
||||
name: string;
|
||||
variants: number;
|
||||
synced: number;
|
||||
thumbnail_url: string;
|
||||
is_ignored: boolean;
|
||||
};
|
||||
}> { }
|
||||
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<Event.ProductDeleted, {
|
||||
sync_product: {
|
||||
id: number;
|
||||
external_id: string;
|
||||
name: string;
|
||||
};
|
||||
}> { }
|
||||
export interface ProductDeleted
|
||||
extends MetaData<
|
||||
Event.ProductDeleted,
|
||||
{
|
||||
sync_product: {
|
||||
id: number;
|
||||
external_id: string;
|
||||
name: string;
|
||||
};
|
||||
}
|
||||
> {}
|
||||
|
||||
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 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<string, string>,
|
||||
"sku-values"?: Record<string, string>;
|
||||
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<Object>
|
||||
year: number;
|
||||
month: number;
|
||||
};
|
||||
};
|
||||
paypalDetails: Object;
|
||||
customData: Array<Object>;
|
||||
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<E extends Event, D> {
|
||||
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<Event.OrderCreated, Orders.Order> { }
|
||||
export interface OrderUpdated extends MetaData<Event.OrderUpdated, Orders.Order> { }
|
||||
export interface OrderCreated
|
||||
extends MetaData<Event.OrderCreated, Orders.Order> {}
|
||||
export interface OrderUpdated
|
||||
extends MetaData<Event.OrderUpdated, Orders.Order> {}
|
||||
|
||||
export type EventPayload = OrderCreated | OrderUpdated;
|
||||
}
|
||||
|
||||
@@ -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<Webflow.Products.Skus.Sku>[]) {
|
||||
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<Webflow.Products.Skus.Sku>[],
|
||||
) {
|
||||
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<Webflow.Products.Skus.Sku>) {
|
||||
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<Webflow.Products.Skus.Sku>,
|
||||
) {
|
||||
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<Webflow.Products.ProductAndSku>) {
|
||||
static async create(
|
||||
webflowProductAndSku: DeepPartial<Webflow.Products.ProductAndSku>,
|
||||
) {
|
||||
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<Webflow.Products.ProductAndSkus[]> {
|
||||
@@ -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<Webflow.Products.ProductAndSkus | undefined> {
|
||||
const res = await fetch(`${env().API_SITES_URL}/products/${webflowProductId}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
...env().AUTH_HEADER,
|
||||
}
|
||||
});
|
||||
static async get(
|
||||
webflowProductId: string,
|
||||
): Promise<Webflow.Products.ProductAndSkus | undefined> {
|
||||
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<Webflow.Products.ProductAndSku>) {
|
||||
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<Webflow.Products.ProductAndSku>,
|
||||
) {
|
||||
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<Webflow.Orders.Order[]> {
|
||||
static async getAll(
|
||||
opt: { status?: Webflow.Orders.Order["status"] } = {},
|
||||
): Promise<Webflow.Orders.Order[]> {
|
||||
// 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}` },
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
+17
-6
@@ -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 = {
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { app } from "$lib/server/app"
|
||||
import { app } from "$lib/server/app";
|
||||
|
||||
type RequestHandler = (v: { request: Request }) => Response | Promise<Response>
|
||||
export const fallback: RequestHandler = ({ request }) => app.handle(request)
|
||||
type RequestHandler = (v: { request: Request }) => Response | Promise<Response>;
|
||||
export const fallback: RequestHandler = ({ request }) => app.handle(request);
|
||||
|
||||
+276
-276
@@ -1,309 +1,309 @@
|
||||
<script lang="ts">
|
||||
import {
|
||||
Standards,
|
||||
type ActivityStandards,
|
||||
type Metrics,
|
||||
type StandardsConfig,
|
||||
} from "$lib/services/calculator/main";
|
||||
import allStandardsRaw from "$lib/data/standards.json" assert { type: "json" };
|
||||
import { Activity, Gender, lbToKg } from "$lib/services/calculator/util";
|
||||
import StandardsTable from "$lib/components/StandardsTable.svelte";
|
||||
import PlayersTable from "$lib/components/PlayersTable.svelte";
|
||||
import {
|
||||
Standards,
|
||||
type ActivityStandards,
|
||||
type Metrics,
|
||||
type StandardsConfig,
|
||||
} from "$lib/services/calculator/main";
|
||||
import allStandardsRaw from "$lib/data/standards.json" assert { type: "json" };
|
||||
import { Activity, Gender, lbToKg } from "$lib/services/calculator/util";
|
||||
import StandardsTable from "$lib/components/StandardsTable.svelte";
|
||||
import PlayersTable from "$lib/components/PlayersTable.svelte";
|
||||
|
||||
let activeTab = $state("standards");
|
||||
let activeTab = $state("standards");
|
||||
|
||||
const defaultStandards = new Standards(
|
||||
allStandardsRaw as ActivityStandards,
|
||||
);
|
||||
const defaultStandards = new Standards(
|
||||
allStandardsRaw as ActivityStandards,
|
||||
);
|
||||
|
||||
let input = $state({
|
||||
activity: Activity.BenchPress,
|
||||
metrics: {
|
||||
gender: Gender.Male,
|
||||
age: "",
|
||||
weight: "",
|
||||
},
|
||||
cfg: {
|
||||
global: {
|
||||
maxLevel: String(defaultStandards.cfg.global.maxLevel),
|
||||
},
|
||||
activity: Object.fromEntries(
|
||||
Object.values(Activity).map((activity) => {
|
||||
return [
|
||||
activity,
|
||||
{
|
||||
enableGeneration:
|
||||
defaultStandards.cfg.activity[activity]
|
||||
.enableGeneration,
|
||||
weightAdvantage: String(
|
||||
defaultStandards.cfg.activity[activity]
|
||||
.weightModifier,
|
||||
),
|
||||
ageModifier: String(
|
||||
defaultStandards.cfg.activity[activity]
|
||||
.ageModifier,
|
||||
),
|
||||
difficultyModifier: String(
|
||||
defaultStandards.cfg.activity[activity]
|
||||
.difficultyModifier,
|
||||
),
|
||||
},
|
||||
];
|
||||
}),
|
||||
),
|
||||
},
|
||||
});
|
||||
let input = $state({
|
||||
activity: Activity.BenchPress,
|
||||
metrics: {
|
||||
gender: Gender.Male,
|
||||
age: "",
|
||||
weight: "",
|
||||
},
|
||||
cfg: {
|
||||
global: {
|
||||
maxLevel: String(defaultStandards.cfg.global.maxLevel),
|
||||
},
|
||||
activity: Object.fromEntries(
|
||||
Object.values(Activity).map((activity) => {
|
||||
return [
|
||||
activity,
|
||||
{
|
||||
enableGeneration:
|
||||
defaultStandards.cfg.activity[activity]
|
||||
.enableGeneration,
|
||||
weightAdvantage: String(
|
||||
defaultStandards.cfg.activity[activity]
|
||||
.weightModifier,
|
||||
),
|
||||
ageModifier: String(
|
||||
defaultStandards.cfg.activity[activity]
|
||||
.ageModifier,
|
||||
),
|
||||
difficultyModifier: String(
|
||||
defaultStandards.cfg.activity[activity]
|
||||
.difficultyModifier,
|
||||
),
|
||||
},
|
||||
];
|
||||
}),
|
||||
),
|
||||
},
|
||||
});
|
||||
|
||||
const selected = $derived({
|
||||
activity: input.activity,
|
||||
metrics: {
|
||||
gender: input.metrics.gender,
|
||||
age: +input.metrics.age,
|
||||
weight: lbToKg(+input.metrics.weight),
|
||||
} as Metrics,
|
||||
cfg: {
|
||||
global: {
|
||||
maxLevel: +input.cfg.global.maxLevel,
|
||||
},
|
||||
activity: Object.fromEntries(
|
||||
Object.values(Activity).map((activity) => [
|
||||
activity,
|
||||
{
|
||||
weightModifier:
|
||||
+input.cfg.activity[activity].weightAdvantage,
|
||||
ageModifier: +input.cfg.activity[activity].ageModifier,
|
||||
enableGeneration:
|
||||
input.cfg.activity[activity].enableGeneration,
|
||||
difficultyModifier:
|
||||
+input.cfg.activity[activity].difficultyModifier,
|
||||
},
|
||||
]),
|
||||
),
|
||||
} as StandardsConfig,
|
||||
});
|
||||
const selected = $derived({
|
||||
activity: input.activity,
|
||||
metrics: {
|
||||
gender: input.metrics.gender,
|
||||
age: +input.metrics.age,
|
||||
weight: lbToKg(+input.metrics.weight),
|
||||
} as Metrics,
|
||||
cfg: {
|
||||
global: {
|
||||
maxLevel: +input.cfg.global.maxLevel,
|
||||
},
|
||||
activity: Object.fromEntries(
|
||||
Object.values(Activity).map((activity) => [
|
||||
activity,
|
||||
{
|
||||
weightModifier:
|
||||
+input.cfg.activity[activity].weightAdvantage,
|
||||
ageModifier: +input.cfg.activity[activity].ageModifier,
|
||||
enableGeneration:
|
||||
input.cfg.activity[activity].enableGeneration,
|
||||
difficultyModifier:
|
||||
+input.cfg.activity[activity].difficultyModifier,
|
||||
},
|
||||
]),
|
||||
),
|
||||
} as StandardsConfig,
|
||||
});
|
||||
|
||||
const allStandards = $derived(
|
||||
new Standards(allStandardsRaw as ActivityStandards, selected.cfg),
|
||||
);
|
||||
const allStandards = $derived(
|
||||
new Standards(allStandardsRaw as ActivityStandards, selected.cfg),
|
||||
);
|
||||
|
||||
$effect(() => {
|
||||
console.log(selected);
|
||||
if (!input.metrics.age) input.metrics.weight = "";
|
||||
});
|
||||
$effect(() => {
|
||||
console.log(selected);
|
||||
if (!input.metrics.age) input.metrics.weight = "";
|
||||
});
|
||||
|
||||
$effect(() => {
|
||||
const savedParameters = localStorage.getItem("parameters");
|
||||
if (savedParameters) input = JSON.parse(savedParameters);
|
||||
});
|
||||
$effect(() => {
|
||||
const savedParameters = localStorage.getItem("parameters");
|
||||
if (savedParameters) input = JSON.parse(savedParameters);
|
||||
});
|
||||
|
||||
$effect(() => {
|
||||
localStorage.setItem("parameters", JSON.stringify(input));
|
||||
});
|
||||
$effect(() => {
|
||||
localStorage.setItem("parameters", JSON.stringify(input));
|
||||
});
|
||||
</script>
|
||||
|
||||
<section class="w-full flex justify-between gap-2 pt-10 items-center">
|
||||
<fieldset class="fieldset bg-base-200 p-6 max-w-lg">
|
||||
<legend class="fieldset-legend text-lg font-semibold"> Data </legend>
|
||||
<fieldset class="fieldset bg-base-200 p-6 max-w-lg">
|
||||
<legend class="fieldset-legend text-lg font-semibold"> Data </legend>
|
||||
|
||||
<div class="flex gap-4">
|
||||
<fieldset class="fieldset bg-base-300 p-3 max-w-xl">
|
||||
<legend class="fieldset-legend text-sm font-semibold">
|
||||
General
|
||||
</legend>
|
||||
<div class="flex gap-4">
|
||||
<fieldset class="fieldset bg-base-300 p-3 max-w-xl">
|
||||
<legend class="fieldset-legend text-sm font-semibold">
|
||||
General
|
||||
</legend>
|
||||
|
||||
<div class="grid grid-cols-1 gap-4">
|
||||
<label>
|
||||
<span class="label mb-1">Max level</span>
|
||||
<input
|
||||
class="input w-full"
|
||||
type="number"
|
||||
min="1"
|
||||
max="100"
|
||||
placeholder="5"
|
||||
bind:value={input.cfg.global.maxLevel}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</fieldset>
|
||||
<div class="grid grid-cols-1 gap-4">
|
||||
<label>
|
||||
<span class="label mb-1">Max level</span>
|
||||
<input
|
||||
class="input w-full"
|
||||
type="number"
|
||||
min="1"
|
||||
max="100"
|
||||
placeholder="5"
|
||||
bind:value={input.cfg.global.maxLevel}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="fieldset bg-base-300 p-3 max-w-3xs">
|
||||
<legend class="fieldset-legend text-sm font-semibold">
|
||||
Data Generation
|
||||
</legend>
|
||||
<fieldset class="fieldset bg-base-300 p-3 max-w-3xs">
|
||||
<legend class="fieldset-legend text-sm font-semibold">
|
||||
Data Generation
|
||||
</legend>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<label class="label">
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={
|
||||
input.cfg.activity[selected.activity]
|
||||
.enableGeneration
|
||||
}
|
||||
class="toggle toggle-lg m-auto"
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
<span class="label mb-1">Weight significance</span>
|
||||
<input
|
||||
class="input w-full"
|
||||
type="number"
|
||||
min="-1"
|
||||
max="1"
|
||||
step=".05"
|
||||
placeholder=".1"
|
||||
bind:value={
|
||||
input.cfg.activity[selected.activity]
|
||||
.weightAdvantage
|
||||
}
|
||||
disabled={!selected.cfg.activity[selected.activity]
|
||||
.enableGeneration}
|
||||
/>
|
||||
</label>
|
||||
<!-- <label> -->
|
||||
<!-- <span class="label mb-1">Weight skew</span> -->
|
||||
<!-- <input -->
|
||||
<!-- class="input w-full" -->
|
||||
<!-- type="number" -->
|
||||
<!-- min="-1" -->
|
||||
<!-- max="1" -->
|
||||
<!-- step=".05" -->
|
||||
<!-- bind:value={ -->
|
||||
<!-- inputSelected.cfg.activity[selected.activity] -->
|
||||
<!-- .weightSkew -->
|
||||
<!-- } -->
|
||||
<!-- disabled={!selected.cfg.activity[selected.activity] -->
|
||||
<!-- .enableGeneration} -->
|
||||
<!-- /> -->
|
||||
<!-- </label> -->
|
||||
<label>
|
||||
<span class="label mb-1">Difficulty modifier</span>
|
||||
<input
|
||||
class="input w-full"
|
||||
type="number"
|
||||
min="-1"
|
||||
max="1"
|
||||
step=".05"
|
||||
bind:value={
|
||||
input.cfg.activity[selected.activity]
|
||||
.difficultyModifier
|
||||
}
|
||||
disabled={!selected.cfg.activity[selected.activity]
|
||||
.enableGeneration}
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
<span class="label mb-1">Age significance</span>
|
||||
<input
|
||||
class="input w-full"
|
||||
type="number"
|
||||
min="-1"
|
||||
max="1"
|
||||
step=".05"
|
||||
bind:value={
|
||||
input.cfg.activity[selected.activity]
|
||||
.ageModifier
|
||||
}
|
||||
disabled={!selected.cfg.activity[selected.activity]
|
||||
.enableGeneration}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
</fieldset>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<label class="label">
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={
|
||||
input.cfg.activity[selected.activity]
|
||||
.enableGeneration
|
||||
}
|
||||
class="toggle toggle-lg m-auto"
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
<span class="label mb-1">Weight significance</span>
|
||||
<input
|
||||
class="input w-full"
|
||||
type="number"
|
||||
min="-1"
|
||||
max="1"
|
||||
step=".05"
|
||||
placeholder=".1"
|
||||
bind:value={
|
||||
input.cfg.activity[selected.activity]
|
||||
.weightAdvantage
|
||||
}
|
||||
disabled={!selected.cfg.activity[selected.activity]
|
||||
.enableGeneration}
|
||||
/>
|
||||
</label>
|
||||
<!-- <label> -->
|
||||
<!-- <span class="label mb-1">Weight skew</span> -->
|
||||
<!-- <input -->
|
||||
<!-- class="input w-full" -->
|
||||
<!-- type="number" -->
|
||||
<!-- min="-1" -->
|
||||
<!-- max="1" -->
|
||||
<!-- step=".05" -->
|
||||
<!-- bind:value={ -->
|
||||
<!-- inputSelected.cfg.activity[selected.activity] -->
|
||||
<!-- .weightSkew -->
|
||||
<!-- } -->
|
||||
<!-- disabled={!selected.cfg.activity[selected.activity] -->
|
||||
<!-- .enableGeneration} -->
|
||||
<!-- /> -->
|
||||
<!-- </label> -->
|
||||
<label>
|
||||
<span class="label mb-1">Difficulty modifier</span>
|
||||
<input
|
||||
class="input w-full"
|
||||
type="number"
|
||||
min="-1"
|
||||
max="1"
|
||||
step=".05"
|
||||
bind:value={
|
||||
input.cfg.activity[selected.activity]
|
||||
.difficultyModifier
|
||||
}
|
||||
disabled={!selected.cfg.activity[selected.activity]
|
||||
.enableGeneration}
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
<span class="label mb-1">Age significance</span>
|
||||
<input
|
||||
class="input w-full"
|
||||
type="number"
|
||||
min="-1"
|
||||
max="1"
|
||||
step=".05"
|
||||
bind:value={
|
||||
input.cfg.activity[selected.activity]
|
||||
.ageModifier
|
||||
}
|
||||
disabled={!selected.cfg.activity[selected.activity]
|
||||
.enableGeneration}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
{#if activeTab === "standards"}
|
||||
<fieldset class="fieldset bg-base-200 p-6 max-w-lg">
|
||||
<legend class="fieldset-legend text-lg font-semibold">
|
||||
Metrics
|
||||
</legend>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<label>
|
||||
<span class="label mb-1">Activity</span>
|
||||
<select
|
||||
class="select w-full"
|
||||
name="activities"
|
||||
bind:value={input.activity}
|
||||
>
|
||||
{#each Object.values(Activity) as activity}
|
||||
<option value={activity}>
|
||||
{allStandards.byActivity(activity).getMetadata()
|
||||
.name}
|
||||
</option>
|
||||
{/each}
|
||||
</select>
|
||||
</label>
|
||||
{#if activeTab === "standards"}
|
||||
<fieldset class="fieldset bg-base-200 p-6 max-w-lg">
|
||||
<legend class="fieldset-legend text-lg font-semibold">
|
||||
Metrics
|
||||
</legend>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<label>
|
||||
<span class="label mb-1">Activity</span>
|
||||
<select
|
||||
class="select w-full"
|
||||
name="activities"
|
||||
bind:value={input.activity}
|
||||
>
|
||||
{#each Object.values(Activity) as activity}
|
||||
<option value={activity}>
|
||||
{allStandards.byActivity(activity).getMetadata()
|
||||
.name}
|
||||
</option>
|
||||
{/each}
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<span class="label mb-1">Gender</span>
|
||||
<select
|
||||
class="select w-full"
|
||||
name="genders"
|
||||
bind:value={input.metrics.gender}
|
||||
>
|
||||
{#each Object.values(Gender) as gender}
|
||||
<option value={gender}>{gender}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
<span class="label mb-1">Gender</span>
|
||||
<select
|
||||
class="select w-full"
|
||||
name="genders"
|
||||
bind:value={input.metrics.gender}
|
||||
>
|
||||
{#each Object.values(Gender) as gender}
|
||||
<option value={gender}>{gender}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<span class="label mb-1">Age</span>
|
||||
<input
|
||||
class="input w-full"
|
||||
type="number"
|
||||
min="0"
|
||||
max="100"
|
||||
placeholder="18"
|
||||
bind:value={input.metrics.age}
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
<span class="label mb-1">Age</span>
|
||||
<input
|
||||
class="input w-full"
|
||||
type="number"
|
||||
min="0"
|
||||
max="100"
|
||||
placeholder="18"
|
||||
bind:value={input.metrics.age}
|
||||
/>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<span class="label mb-1">Weight (lb)</span>
|
||||
<input
|
||||
class="input w-full"
|
||||
type="number"
|
||||
min="0"
|
||||
max="600"
|
||||
placeholder={selected.metrics.age
|
||||
? "170"
|
||||
: "Requires age"}
|
||||
bind:value={input.metrics.weight}
|
||||
disabled={!selected.metrics.age}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</fieldset>
|
||||
{/if}
|
||||
<label>
|
||||
<span class="label mb-1">Weight (lb)</span>
|
||||
<input
|
||||
class="input w-full"
|
||||
type="number"
|
||||
min="0"
|
||||
max="600"
|
||||
placeholder={selected.metrics.age
|
||||
? "170"
|
||||
: "Requires age"}
|
||||
bind:value={input.metrics.weight}
|
||||
disabled={!selected.metrics.age}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</fieldset>
|
||||
{/if}
|
||||
</section>
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
<div role="tablist" class="tabs tabs-border">
|
||||
<button
|
||||
role="tab"
|
||||
class="tab"
|
||||
class:tab-active={activeTab === "standards"}
|
||||
onclick={() => (activeTab = "standards")}
|
||||
>
|
||||
Standards
|
||||
</button>
|
||||
<button
|
||||
role="tab"
|
||||
class="tab"
|
||||
class:tab-active={activeTab === "standards"}
|
||||
onclick={() => (activeTab = "standards")}
|
||||
>
|
||||
Standards
|
||||
</button>
|
||||
|
||||
<button
|
||||
role="tab"
|
||||
class="tab"
|
||||
class:tab-active={activeTab === "players"}
|
||||
onclick={() => (activeTab = "players")}
|
||||
>
|
||||
Players
|
||||
</button>
|
||||
<button
|
||||
role="tab"
|
||||
class="tab"
|
||||
class:tab-active={activeTab === "players"}
|
||||
onclick={() => (activeTab = "players")}
|
||||
>
|
||||
Players
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{#if activeTab === "standards"}
|
||||
<StandardsTable {selected} {allStandards} />
|
||||
<StandardsTable {selected} {allStandards} />
|
||||
{/if}
|
||||
|
||||
{#if activeTab === "players"}
|
||||
<PlayersTable {allStandards} />
|
||||
<PlayersTable {allStandards} />
|
||||
{/if}
|
||||
|
||||
@@ -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<PageServerLoad>[0]) => {
|
||||
return {
|
||||
products: {
|
||||
printful: PrintfulService.Products.getAll(),
|
||||
webflow: WebflowService.Products.getAll(),
|
||||
}
|
||||
};
|
||||
export const load = async ({}: Parameters<PageServerLoad>[0]) => {
|
||||
return {
|
||||
products: {
|
||||
printful: PrintfulService.Products.getAll(),
|
||||
webflow: WebflowService.Products.getAll(),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
+10
-10
@@ -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;
|
||||
|
||||
+18
-19
@@ -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
|
||||
}
|
||||
|
||||
+3
-3
@@ -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()],
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user