diff --git a/apps/public-api/package.json b/apps/public-api/package.json index 0ca19ca..2ac6fd7 100644 --- a/apps/public-api/package.json +++ b/apps/public-api/package.json @@ -20,6 +20,7 @@ "zod": "catalog:" }, "devDependencies": { + "@sofa/tsconfig": "workspace:*", "@types/bun": "catalog:", "typescript": "catalog:" } diff --git a/apps/public-api/tsconfig.json b/apps/public-api/tsconfig.json index 92bbeaf..5e4fa20 100644 --- a/apps/public-api/tsconfig.json +++ b/apps/public-api/tsconfig.json @@ -1,15 +1,4 @@ { - "compilerOptions": { - "target": "ES2017", - "lib": ["esnext"], - "strict": true, - "noEmit": true, - "module": "esnext", - "moduleResolution": "bundler", - "isolatedModules": true, - "skipLibCheck": true, - "types": ["bun"] - }, - "include": ["**/*.ts"], - "exclude": ["node_modules", "dist"] + "extends": "@sofa/tsconfig/base.json", + "include": ["src"] } diff --git a/apps/server/package.json b/apps/server/package.json index 67fdb56..ed85f69 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -29,6 +29,7 @@ "zod": "catalog:" }, "devDependencies": { + "@sofa/tsconfig": "workspace:*", "@types/bun": "catalog:", "typescript": "catalog:" } diff --git a/apps/server/tsconfig.json b/apps/server/tsconfig.json index ff11b78..5e4fa20 100644 --- a/apps/server/tsconfig.json +++ b/apps/server/tsconfig.json @@ -1,15 +1,4 @@ { - "compilerOptions": { - "target": "ES2017", - "lib": ["esnext"], - "strict": true, - "noEmit": true, - "module": "esnext", - "moduleResolution": "bundler", - "isolatedModules": true, - "skipLibCheck": true, - "types": ["bun"], - "jsx": "react-jsx" - }, + "extends": "@sofa/tsconfig/base.json", "include": ["src"] } diff --git a/apps/web/package.json b/apps/web/package.json index 051612c..a57a236 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -49,6 +49,7 @@ "devDependencies": { "@lingui/babel-plugin-lingui-macro": "catalog:", "@lingui/vite-plugin": "catalog:", + "@sofa/tsconfig": "workspace:*", "@tailwindcss/vite": "4.2.2", "@tanstack/devtools-vite": "0.6.0", "@tanstack/react-devtools": "0.10.0", diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json index bcbbdcc..4d9a09b 100644 --- a/apps/web/tsconfig.json +++ b/apps/web/tsconfig.json @@ -1,18 +1,11 @@ { + "extends": "@sofa/tsconfig/base.json", "compilerOptions": { - "target": "ES2022", - "lib": ["dom", "dom.iterable", "esnext"], - "skipLibCheck": true, - "strict": true, - "noEmit": true, - "module": "esnext", - "moduleResolution": "bundler", + "lib": ["DOM", "DOM.Iterable", "ESNext"], "jsx": "react-jsx", - "isolatedModules": true, "paths": { "@/*": ["./src/*"] - }, - "types": ["bun"] + } }, "include": ["src", "vite.config.ts", "../../packages/i18n/src/po.d.ts"], "exclude": ["node_modules", "dist"] diff --git a/bun.lock b/bun.lock index f69cfa6..8e60cd4 100644 --- a/bun.lock +++ b/bun.lock @@ -122,6 +122,7 @@ "zod": "catalog:", }, "devDependencies": { + "@sofa/tsconfig": "workspace:*", "@types/bun": "catalog:", "typescript": "catalog:", }, @@ -147,6 +148,7 @@ "zod": "catalog:", }, "devDependencies": { + "@sofa/tsconfig": "workspace:*", "@types/bun": "catalog:", "typescript": "catalog:", }, @@ -190,6 +192,7 @@ "devDependencies": { "@lingui/babel-plugin-lingui-macro": "catalog:", "@lingui/vite-plugin": "catalog:", + "@sofa/tsconfig": "workspace:*", "@tailwindcss/vite": "4.2.2", "@tanstack/devtools-vite": "0.6.0", "@tanstack/react-devtools": "0.10.0", @@ -218,6 +221,7 @@ "zod": "catalog:", }, "devDependencies": { + "@sofa/tsconfig": "workspace:*", "typescript": "catalog:", }, }, @@ -233,6 +237,7 @@ "better-auth": "catalog:", }, "devDependencies": { + "@sofa/tsconfig": "workspace:*", "@types/bun": "catalog:", "typescript": "catalog:", }, @@ -241,6 +246,7 @@ "name": "@sofa/config", "version": "0.1.0", "devDependencies": { + "@sofa/tsconfig": "workspace:*", "@types/bun": "catalog:", "typescript": "catalog:", }, @@ -262,6 +268,7 @@ "zod": "catalog:", }, "devDependencies": { + "@sofa/tsconfig": "workspace:*", "@types/adm-zip": "0.5.8", "@types/bun": "catalog:", "typescript": "catalog:", @@ -277,6 +284,7 @@ "drizzle-orm": "catalog:", }, "devDependencies": { + "@sofa/tsconfig": "workspace:*", "@types/bun": "catalog:", "drizzle-kit": "catalog:", "typescript": "catalog:", @@ -293,9 +301,11 @@ "@lingui/cli": "catalog:", "@lingui/conf": "catalog:", "@lingui/format-po": "catalog:", + "@sofa/tsconfig": "workspace:*", "@types/bun": "catalog:", "react": "catalog:", "typescript": "catalog:", + "vitest": "catalog:", }, }, "packages/logger": { @@ -306,6 +316,7 @@ "pino-pretty": "13.1.3", }, "devDependencies": { + "@sofa/tsconfig": "workspace:*", "@types/bun": "catalog:", "typescript": "catalog:", }, @@ -319,6 +330,7 @@ "drizzle-orm": "catalog:", }, "devDependencies": { + "@sofa/tsconfig": "workspace:*", "@types/better-sqlite3": "7.6.13", "typescript": "catalog:", "vitest": "catalog:", @@ -332,10 +344,15 @@ "openapi-fetch": "0.17.0", }, "devDependencies": { + "@sofa/tsconfig": "workspace:*", "@types/bun": "catalog:", "typescript": "catalog:", }, }, + "packages/tsconfig": { + "name": "@sofa/tsconfig", + "version": "0.1.0", + }, }, "catalog": { "@better-auth/expo": "1.5.6", @@ -1272,6 +1289,8 @@ "@sofa/tmdb": ["@sofa/tmdb@workspace:packages/tmdb"], + "@sofa/tsconfig": ["@sofa/tsconfig@workspace:packages/tsconfig"], + "@sofa/web": ["@sofa/web@workspace:apps/web"], "@solid-primitives/event-listener": ["@solid-primitives/event-listener@2.4.5", "", { "dependencies": { "@solid-primitives/utils": "^6.4.0" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-nwRV558mIabl4yVAhZKY8cb6G+O1F0M6Z75ttTu5hk+SxdOnKSGj+eetDIu7Oax1P138ZdUU01qnBPR8rnxaEA=="], @@ -1600,7 +1619,7 @@ "babel-preset-jest": ["babel-preset-jest@29.6.3", "", { "dependencies": { "babel-plugin-jest-hoist": "^29.6.3", "babel-preset-current-node-syntax": "^1.0.0" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA=="], - "balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], + "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], @@ -1632,7 +1651,7 @@ "bplist-parser": ["bplist-parser@0.3.2", "", { "dependencies": { "big-integer": "1.6.x" } }, "sha512-apC2+fspHGI3mMKj+dGevkGo/tCqVB8jMb6i+OX+E29p0Iposz07fABkRIfVUPNd5A5VbuOz1bZbnmkKLYF+wQ=="], - "brace-expansion": ["brace-expansion@5.0.4", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg=="], + "brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], @@ -2096,7 +2115,7 @@ "glob": ["glob@11.1.0", "", { "dependencies": { "foreground-child": "^3.3.1", "jackspeak": "^4.1.1", "minimatch": "^10.1.1", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^2.0.0" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw=="], - "glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], "globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="], @@ -2426,7 +2445,7 @@ "mimic-response": ["mimic-response@3.1.0", "", {}, "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="], - "minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="], + "minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="], "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], @@ -3142,10 +3161,6 @@ "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], - "@eslint/config-array/minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="], - - "@eslint/eslintrc/minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="], - "@eslint/eslintrc/strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], "@expo/cli/arg": ["arg@5.0.2", "", {}, "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="], @@ -3166,6 +3181,8 @@ "@expo/fingerprint/glob": ["glob@13.0.6", "", { "dependencies": { "minimatch": "^10.2.2", "minipass": "^7.1.3", "path-scurry": "^2.0.2" } }, "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw=="], + "@expo/fingerprint/minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="], + "@expo/metro/metro-runtime": ["metro-runtime@0.83.3", "", { "dependencies": { "@babel/runtime": "^7.25.0", "flow-enums-runtime": "^0.0.6" } }, "sha512-JHCJb9ebr9rfJ+LcssFYA2x1qPYuSD/bbePupIGhpMrsla7RCwC/VL3yJ9cSU+nUhU4c9Ixxy8tBta+JbDeZWw=="], "@expo/metro/metro-source-map": ["metro-source-map@0.83.3", "", { "dependencies": { "@babel/traverse": "^7.25.3", "@babel/traverse--for-generate-function-map": "npm:@babel/traverse@^7.25.3", "@babel/types": "^7.25.2", "flow-enums-runtime": "^0.0.6", "invariant": "^2.2.4", "metro-symbolicate": "0.83.3", "nullthrows": "^1.1.1", "ob1": "0.83.3", "source-map": "^0.5.6", "vlq": "^1.0.0" } }, "sha512-xkC3qwUBh2psVZgVavo8+r2C9Igkk3DibiOXSAht1aYRRcztEZNFtAMtfSB7sdO2iFMx2Mlyu++cBxz/fhdzQg=="], @@ -3264,6 +3281,10 @@ "@tanstack/router-plugin/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], + "@ts-morph/common/minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="], + + "@typescript-eslint/typescript-estree/minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="], + "@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@5.0.1", "", {}, "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA=="], "ajv-formats/ajv": ["ajv@8.18.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A=="], @@ -3286,6 +3307,8 @@ "chokidar/fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + "chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + "chrome-launcher/is-wsl": ["is-wsl@2.2.0", "", { "dependencies": { "is-docker": "^2.0.0" } }, "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww=="], "chromium-edge-launcher/is-wsl": ["is-wsl@2.2.0", "", { "dependencies": { "is-docker": "^2.0.0" } }, "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww=="], @@ -3304,10 +3327,6 @@ "eciesjs/@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="], - "eslint/glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], - - "eslint/minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="], - "expo-modules-autolinking/commander": ["commander@7.2.0", "", {}, "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="], "expo-router/semver": ["semver@7.6.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A=="], @@ -3328,6 +3347,8 @@ "express/serve-static": ["serve-static@2.2.1", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw=="], + "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + "figures/is-unicode-supported": ["is-unicode-supported@2.1.0", "", {}, "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ=="], "finalhandler/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], @@ -3338,6 +3359,8 @@ "finalhandler/statuses": ["statuses@1.5.0", "", {}, "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA=="], + "glob/minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="], + "hoist-non-react-statics/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="], "hosted-git-info/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], @@ -3464,8 +3487,6 @@ "test-exclude/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], - "test-exclude/minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="], - "tsx/esbuild": ["esbuild@0.27.4", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.4", "@esbuild/android-arm": "0.27.4", "@esbuild/android-arm64": "0.27.4", "@esbuild/android-x64": "0.27.4", "@esbuild/darwin-arm64": "0.27.4", "@esbuild/darwin-x64": "0.27.4", "@esbuild/freebsd-arm64": "0.27.4", "@esbuild/freebsd-x64": "0.27.4", "@esbuild/linux-arm": "0.27.4", "@esbuild/linux-arm64": "0.27.4", "@esbuild/linux-ia32": "0.27.4", "@esbuild/linux-loong64": "0.27.4", "@esbuild/linux-mips64el": "0.27.4", "@esbuild/linux-ppc64": "0.27.4", "@esbuild/linux-riscv64": "0.27.4", "@esbuild/linux-s390x": "0.27.4", "@esbuild/linux-x64": "0.27.4", "@esbuild/netbsd-arm64": "0.27.4", "@esbuild/netbsd-x64": "0.27.4", "@esbuild/openbsd-arm64": "0.27.4", "@esbuild/openbsd-x64": "0.27.4", "@esbuild/openharmony-arm64": "0.27.4", "@esbuild/sunos-x64": "0.27.4", "@esbuild/win32-arm64": "0.27.4", "@esbuild/win32-ia32": "0.27.4", "@esbuild/win32-x64": "0.27.4" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ=="], "tsx/fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], @@ -3496,9 +3517,7 @@ "@dotenvx/dotenvx/execa/strip-final-newline": ["strip-final-newline@2.0.0", "", {}, "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="], - "@eslint/config-array/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], - - "@eslint/eslintrc/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], + "@expo/cli/glob/minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="], "@expo/cli/ora/chalk": ["chalk@2.4.2", "", { "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" } }, "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="], @@ -3508,6 +3527,14 @@ "@expo/cli/ora/strip-ansi": ["strip-ansi@5.2.0", "", { "dependencies": { "ansi-regex": "^4.1.0" } }, "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA=="], + "@expo/config-plugins/glob/minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="], + + "@expo/config/glob/minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="], + + "@expo/fingerprint/minimatch/brace-expansion": ["brace-expansion@5.0.4", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg=="], + + "@expo/metro-config/glob/minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="], + "@expo/metro-config/hermes-parser/hermes-estree": ["hermes-estree@0.32.1", "", {}, "sha512-ne5hkuDxheNBAikDjqvCZCwihnz0vVu9YsBzAEO1puiyFR4F1+PAz/SiPHSsNTuOveCYGRMX8Xbx4LOubeC0Qg=="], "@expo/metro-config/lightningcss/lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.32.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ=="], @@ -3568,8 +3595,6 @@ "@radix-ui/react-tabs/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], - "@react-native/codegen/glob/minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="], - "@react-native/codegen/hermes-parser/hermes-estree": ["hermes-estree@0.32.0", "", {}, "sha512-KWn3BqnlDOl97Xe1Yviur6NbgIZ+IP+UVSpshlZWkq+EtoHg6/cwiDj/osP9PCEgFE15KBm1O55JRwbMEm5ejQ=="], "@react-native/dev-middleware/open/is-docker": ["is-docker@2.2.1", "", { "bin": { "is-docker": "cli.js" } }, "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ=="], @@ -3626,8 +3651,14 @@ "@tanstack/router-plugin/chokidar/fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + "@tanstack/router-plugin/chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + "@tanstack/router-plugin/chokidar/readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="], + "@ts-morph/common/minimatch/brace-expansion": ["brace-expansion@5.0.4", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg=="], + + "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@5.0.4", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg=="], + "ajv-formats/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], "babel-plugin-syntax-hermes-parser/hermes-parser/hermes-estree": ["hermes-estree@0.32.0", "", {}, "sha512-KWn3BqnlDOl97Xe1Yviur6NbgIZ+IP+UVSpshlZWkq+EtoHg6/cwiDj/osP9PCEgFE15KBm1O55JRwbMEm5ejQ=="], @@ -3648,7 +3679,7 @@ "cross-spawn/which/isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], - "eslint/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], + "expo-updates/glob/minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="], "express/accepts/negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], @@ -3656,6 +3687,8 @@ "finalhandler/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], + "glob/minimatch/brace-expansion": ["brace-expansion@5.0.4", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg=="], + "lighthouse-logger/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], "metro-babel-transformer/hermes-parser/hermes-estree": ["hermes-estree@0.32.0", "", {}, "sha512-KWn3BqnlDOl97Xe1Yviur6NbgIZ+IP+UVSpshlZWkq+EtoHg6/cwiDj/osP9PCEgFE15KBm1O55JRwbMEm5ejQ=="], @@ -3672,12 +3705,8 @@ "node-vibrant/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], - "react-native/glob/minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="], - "readable-web-to-node-stream/readable-stream/buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="], - "rimraf/glob/minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="], - "send/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], "shadcn/ora/chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], @@ -3698,8 +3727,6 @@ "tedious/bl/readable-stream": ["readable-stream@4.7.0", "", { "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", "process": "^0.11.10", "string_decoder": "^1.3.0" } }, "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg=="], - "test-exclude/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], - "tsx/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.4", "", { "os": "aix", "cpu": "ppc64" }, "sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q=="], "tsx/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.27.4", "", { "os": "android", "cpu": "arm" }, "sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ=="], @@ -3774,9 +3801,7 @@ "vite/lightningcss/lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.32.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q=="], - "@eslint/config-array/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], - - "@eslint/eslintrc/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + "@expo/cli/glob/minimatch/brace-expansion": ["brace-expansion@5.0.4", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg=="], "@expo/cli/ora/chalk/ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "^1.9.0" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="], @@ -3788,6 +3813,14 @@ "@expo/cli/ora/strip-ansi/ansi-regex": ["ansi-regex@4.1.1", "", {}, "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g=="], + "@expo/config-plugins/glob/minimatch/brace-expansion": ["brace-expansion@5.0.4", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg=="], + + "@expo/config/glob/minimatch/brace-expansion": ["brace-expansion@5.0.4", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg=="], + + "@expo/fingerprint/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], + + "@expo/metro-config/glob/minimatch/brace-expansion": ["brace-expansion@5.0.4", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg=="], + "@expo/package-manager/ora/chalk/ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "^1.9.0" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="], "@expo/package-manager/ora/chalk/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="], @@ -3802,8 +3835,6 @@ "@istanbuljs/load-nyc-config/js-yaml/argparse/sprintf-js": ["sprintf-js@1.0.3", "", {}, "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="], - "@react-native/codegen/glob/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], - "@tailwindcss/vite/@tailwindcss/node/lightningcss/lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.32.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ=="], "@tailwindcss/vite/@tailwindcss/node/lightningcss/lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.32.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w=="], @@ -3838,11 +3869,13 @@ "@tanstack/router-plugin/chokidar/readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], - "eslint/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + "@ts-morph/common/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], - "react-native/glob/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], + "@typescript-eslint/typescript-estree/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], - "rimraf/glob/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], + "expo-updates/glob/minimatch/brace-expansion": ["brace-expansion@5.0.4", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg=="], + + "glob/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], "shadcn/ora/cli-cursor/restore-cursor": ["restore-cursor@5.1.0", "", { "dependencies": { "onetime": "^7.0.0", "signal-exit": "^4.1.0" } }, "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA=="], @@ -3852,7 +3885,7 @@ "shadcn/ora/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], - "test-exclude/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + "@expo/cli/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], "@expo/cli/ora/chalk/ansi-styles/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="], @@ -3862,6 +3895,12 @@ "@expo/cli/ora/cli-cursor/restore-cursor/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], + "@expo/config-plugins/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], + + "@expo/config/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], + + "@expo/metro-config/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], + "@expo/package-manager/ora/chalk/ansi-styles/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="], "@expo/package-manager/ora/chalk/supports-color/has-flag": ["has-flag@3.0.0", "", {}, "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="], @@ -3872,11 +3911,7 @@ "@istanbuljs/load-nyc-config/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="], - "@react-native/codegen/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], - - "react-native/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], - - "rimraf/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + "expo-updates/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], "shadcn/ora/cli-cursor/restore-cursor/onetime": ["onetime@7.0.0", "", { "dependencies": { "mimic-function": "^5.0.0" } }, "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ=="], diff --git a/packages/api/package.json b/packages/api/package.json index 7349072..5bc2ce7 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -7,8 +7,7 @@ "./contract": "./src/contract.ts", "./schemas": "./src/schemas.ts", "./errors": "./src/errors.ts", - "./utils": "./src/utils.ts", - "./display-status": "./src/display-status.ts" + "./utils": "./src/utils.ts" }, "scripts": { "lint": "oxlint", @@ -21,6 +20,7 @@ "zod": "catalog:" }, "devDependencies": { + "@sofa/tsconfig": "workspace:*", "typescript": "catalog:" } } diff --git a/packages/api/tsconfig.json b/packages/api/tsconfig.json index acd1898..5e4fa20 100644 --- a/packages/api/tsconfig.json +++ b/packages/api/tsconfig.json @@ -1,13 +1,4 @@ { - "compilerOptions": { - "target": "ES2017", - "lib": ["esnext"], - "strict": true, - "noEmit": true, - "module": "esnext", - "moduleResolution": "bundler", - "isolatedModules": true, - "skipLibCheck": true - }, + "extends": "@sofa/tsconfig/base.json", "include": ["src"] } diff --git a/packages/auth/package.json b/packages/auth/package.json index 7196ca4..d9e8918 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -22,6 +22,7 @@ "better-auth": "catalog:" }, "devDependencies": { + "@sofa/tsconfig": "workspace:*", "@types/bun": "catalog:", "typescript": "catalog:" } diff --git a/packages/auth/tsconfig.json b/packages/auth/tsconfig.json index 6438538..5e4fa20 100644 --- a/packages/auth/tsconfig.json +++ b/packages/auth/tsconfig.json @@ -1,14 +1,4 @@ { - "compilerOptions": { - "target": "ES2017", - "lib": ["esnext"], - "strict": true, - "noEmit": true, - "module": "esnext", - "moduleResolution": "bundler", - "isolatedModules": true, - "skipLibCheck": true, - "types": ["bun"] - }, + "extends": "@sofa/tsconfig/base.json", "include": ["src"] } diff --git a/packages/config/package.json b/packages/config/package.json index 14bad9a..0fe3498 100644 --- a/packages/config/package.json +++ b/packages/config/package.json @@ -13,6 +13,7 @@ "check-types": "tsc --noEmit" }, "devDependencies": { + "@sofa/tsconfig": "workspace:*", "@types/bun": "catalog:", "typescript": "catalog:" } diff --git a/packages/config/tsconfig.json b/packages/config/tsconfig.json index 6438538..5e4fa20 100644 --- a/packages/config/tsconfig.json +++ b/packages/config/tsconfig.json @@ -1,14 +1,4 @@ { - "compilerOptions": { - "target": "ES2017", - "lib": ["esnext"], - "strict": true, - "noEmit": true, - "module": "esnext", - "moduleResolution": "bundler", - "isolatedModules": true, - "skipLibCheck": true, - "types": ["bun"] - }, + "extends": "@sofa/tsconfig/base.json", "include": ["src"] } diff --git a/packages/core/package.json b/packages/core/package.json index 7f17dff..1e46d00 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -11,6 +11,7 @@ "./credits": "./src/credits.ts", "./cron": "./src/cron.ts", "./discovery": "./src/discovery.ts", + "./display-status": "./src/display-status.ts", "./export": "./src/export.ts", "./image-cache": "./src/image-cache.ts", "./imports": "./src/imports/index.ts", @@ -49,6 +50,7 @@ "zod": "catalog:" }, "devDependencies": { + "@sofa/tsconfig": "workspace:*", "@types/adm-zip": "0.5.8", "@types/bun": "catalog:", "typescript": "catalog:", diff --git a/packages/core/src/discovery.ts b/packages/core/src/discovery.ts index 6d39392..acfb795 100644 --- a/packages/core/src/discovery.ts +++ b/packages/core/src/discovery.ts @@ -1,4 +1,3 @@ -import type { DisplayStatus } from "@sofa/api/display-status"; import { getAllTrackedTitleIds, getAvailabilityByTitleIds, @@ -25,6 +24,7 @@ import { } from "@sofa/db/queries/discovery"; import { tmdbImageUrl } from "@sofa/tmdb/image"; +import type { DisplayStatus } from "./display-status"; import { getDisplayStatusesByTitleIds } from "./tracking"; function formatLocalDate(d: Date): string { diff --git a/packages/api/src/display-status.ts b/packages/core/src/display-status.ts similarity index 100% rename from packages/api/src/display-status.ts rename to packages/core/src/display-status.ts diff --git a/packages/core/src/tracking.ts b/packages/core/src/tracking.ts index bac8bf0..717ba0a 100644 --- a/packages/core/src/tracking.ts +++ b/packages/core/src/tracking.ts @@ -1,5 +1,3 @@ -import type { DisplayStatus } from "@sofa/api/display-status"; -import { getDisplayStatus } from "@sofa/api/display-status"; import { getTitlesByIds } from "@sofa/db/queries/discovery"; import { getTitleById } from "@sofa/db/queries/title"; import { @@ -25,6 +23,9 @@ import { upsertTitleStatus, } from "@sofa/db/queries/tracking"; +import type { DisplayStatus } from "./display-status"; +import { getDisplayStatus } from "./display-status"; + export function setTitleStatus( userId: string, titleId: string, diff --git a/packages/core/test/display-status.test.ts b/packages/core/test/display-status.test.ts new file mode 100644 index 0000000..fb36b57 --- /dev/null +++ b/packages/core/test/display-status.test.ts @@ -0,0 +1,105 @@ +import { describe, expect, test } from "vitest"; + +import { ONGOING_TMDB_STATUSES, getDisplayStatus } from "../src/display-status"; + +describe("ONGOING_TMDB_STATUSES", () => { + test("contains expected values", () => { + expect(ONGOING_TMDB_STATUSES).toContain("Returning Series"); + expect(ONGOING_TMDB_STATUSES).toContain("In Production"); + expect(ONGOING_TMDB_STATUSES).toHaveLength(2); + }); +}); + +describe("getDisplayStatus", () => { + // -- Movies -- + + test("movie with watchlist status returns in_watchlist", () => { + expect(getDisplayStatus("watchlist", "movie", null, null)).toBe("in_watchlist"); + }); + + test("movie with completed status returns completed", () => { + expect(getDisplayStatus("completed", "movie", null, null)).toBe("completed"); + }); + + test("movie with in_progress status returns in_watchlist", () => { + // Movies don't have an "in_progress" concept — falls through to in_watchlist + expect(getDisplayStatus("in_progress", "movie", null, null)).toBe("in_watchlist"); + }); + + // -- TV: watchlist -- + + test("TV with watchlist status returns in_watchlist", () => { + expect(getDisplayStatus("watchlist", "tv", "Returning Series", null)).toBe("in_watchlist"); + }); + + // -- TV: in_progress, no/partial progress -- + + test("TV in_progress with no progress data returns watching", () => { + expect(getDisplayStatus("in_progress", "tv", "Returning Series", null)).toBe("watching"); + }); + + test("TV in_progress with partial progress returns watching", () => { + expect( + getDisplayStatus("in_progress", "tv", "Returning Series", { watched: 3, total: 10 }), + ).toBe("watching"); + }); + + test("TV in_progress with zero total returns watching", () => { + expect(getDisplayStatus("in_progress", "tv", "Ended", { watched: 0, total: 0 })).toBe( + "watching", + ); + }); + + // -- TV: in_progress, all watched, ongoing -- + + test("TV in_progress, all watched, Returning Series returns caught_up", () => { + expect( + getDisplayStatus("in_progress", "tv", "Returning Series", { watched: 10, total: 10 }), + ).toBe("caught_up"); + }); + + test("TV in_progress, all watched, In Production returns caught_up", () => { + expect(getDisplayStatus("in_progress", "tv", "In Production", { watched: 10, total: 10 })).toBe( + "caught_up", + ); + }); + + // -- TV: in_progress, all watched, ended -- + + test("TV in_progress, all watched, Ended returns completed", () => { + expect(getDisplayStatus("in_progress", "tv", "Ended", { watched: 10, total: 10 })).toBe( + "completed", + ); + }); + + test("TV in_progress, all watched, Canceled returns completed", () => { + expect(getDisplayStatus("in_progress", "tv", "Canceled", { watched: 5, total: 5 })).toBe( + "completed", + ); + }); + + test("TV in_progress, all watched, null tmdbStatus returns completed", () => { + expect(getDisplayStatus("in_progress", "tv", null, { watched: 10, total: 10 })).toBe( + "completed", + ); + }); + + // -- Edge cases -- + + test("TV in_progress, watched exceeds total still returns caught_up/completed", () => { + // watched > total shouldn't happen, but if it does, treat as all-watched + expect( + getDisplayStatus("in_progress", "tv", "Returning Series", { watched: 15, total: 10 }), + ).toBe("caught_up"); + expect(getDisplayStatus("in_progress", "tv", "Ended", { watched: 15, total: 10 })).toBe( + "completed", + ); + }); + + test("TV completed status returns in_watchlist (no special handling)", () => { + // "completed" stored status on TV falls through the movie branch check + // titleType !== "movie" so it doesn't enter that branch, and storedStatus !== "watchlist" + // so it falls to the episode progress check → no progress → "watching" + expect(getDisplayStatus("completed", "tv", "Ended", null)).toBe("watching"); + }); +}); diff --git a/packages/core/tsconfig.json b/packages/core/tsconfig.json index a156cba..167cdea 100644 --- a/packages/core/tsconfig.json +++ b/packages/core/tsconfig.json @@ -1,14 +1,4 @@ { - "compilerOptions": { - "target": "ES2017", - "lib": ["esnext"], - "strict": true, - "noEmit": true, - "module": "esnext", - "moduleResolution": "bundler", - "isolatedModules": true, - "skipLibCheck": true, - "types": ["bun"] - }, + "extends": "@sofa/tsconfig/base.json", "include": ["src", "test"] } diff --git a/packages/db/package.json b/packages/db/package.json index 90e71f6..c5accbb 100644 --- a/packages/db/package.json +++ b/packages/db/package.json @@ -25,6 +25,7 @@ "drizzle-orm": "catalog:" }, "devDependencies": { + "@sofa/tsconfig": "workspace:*", "@types/bun": "catalog:", "drizzle-kit": "catalog:", "typescript": "catalog:" diff --git a/packages/db/tsconfig.json b/packages/db/tsconfig.json index 6438538..5e4fa20 100644 --- a/packages/db/tsconfig.json +++ b/packages/db/tsconfig.json @@ -1,14 +1,4 @@ { - "compilerOptions": { - "target": "ES2017", - "lib": ["esnext"], - "strict": true, - "noEmit": true, - "module": "esnext", - "moduleResolution": "bundler", - "isolatedModules": true, - "skipLibCheck": true, - "types": ["bun"] - }, + "extends": "@sofa/tsconfig/base.json", "include": ["src"] } diff --git a/packages/i18n/package.json b/packages/i18n/package.json index 1f4e921..7152e3e 100644 --- a/packages/i18n/package.json +++ b/packages/i18n/package.json @@ -14,7 +14,8 @@ "lint": "oxlint", "format": "oxfmt --config ../../.oxfmtrc.json", "format:check": "oxfmt --check --config ../../.oxfmtrc.json", - "check-types": "tsc --noEmit" + "check-types": "tsc --noEmit", + "test": "vitest run" }, "dependencies": { "@lingui/core": "catalog:", @@ -24,8 +25,10 @@ "@lingui/cli": "catalog:", "@lingui/conf": "catalog:", "@lingui/format-po": "catalog:", + "@sofa/tsconfig": "workspace:*", "@types/bun": "catalog:", "react": "catalog:", - "typescript": "catalog:" + "typescript": "catalog:", + "vitest": "catalog:" } } diff --git a/packages/i18n/test/date-buckets.test.ts b/packages/i18n/test/date-buckets.test.ts new file mode 100644 index 0000000..0107498 --- /dev/null +++ b/packages/i18n/test/date-buckets.test.ts @@ -0,0 +1,111 @@ +import { afterEach, beforeEach, describe, expect, test, vi } from "vitest"; + +vi.mock("../src/index", () => ({ + i18n: { + locale: "en", + _: (descriptor: { id?: string; message?: string } | string) => { + if (typeof descriptor === "string") return descriptor; + return descriptor.message ?? descriptor.id ?? ""; + }, + }, +})); + +vi.mock("@lingui/core/macro", () => ({ + msg: (strings: TemplateStringsArray, ...values: unknown[]) => ({ + id: String.raw(strings, ...values), + message: String.raw(strings, ...values), + }), +})); + +import { formatLocalDate, groupByDateBucket } from "../src/date-buckets"; + +describe("formatLocalDate", () => { + test("formats a date as YYYY-MM-DD", () => { + expect(formatLocalDate(new Date(2024, 0, 5))).toBe("2024-01-05"); + }); + + test("pads single-digit month and day", () => { + expect(formatLocalDate(new Date(2024, 2, 3))).toBe("2024-03-03"); + }); + + test("handles December correctly", () => { + expect(formatLocalDate(new Date(2024, 11, 31))).toBe("2024-12-31"); + }); +}); + +describe("groupByDateBucket", () => { + // Pin "today" to a Wednesday: 2024-06-12 + beforeEach(() => { + vi.useFakeTimers(); + vi.setSystemTime(new Date("2024-06-12T10:00:00")); + }); + + afterEach(() => { + vi.useRealTimers(); + }); + + test("empty array returns empty result", () => { + expect(groupByDateBucket([])).toEqual([]); + }); + + test("item on today goes to 'today' bucket", () => { + const result = groupByDateBucket([{ date: "2024-06-12" }]); + expect(result).toHaveLength(1); + expect(result[0].key).toBe("today"); + expect(result[0].items).toHaveLength(1); + }); + + test("item on tomorrow goes to 'tomorrow' bucket", () => { + const result = groupByDateBucket([{ date: "2024-06-13" }]); + expect(result).toHaveLength(1); + expect(result[0].key).toBe("tomorrow"); + }); + + test("item within 6 days goes to 'this_week' bucket", () => { + // Today is June 12 (Wed), end of week = June 18 (Tue) + const result = groupByDateBucket([{ date: "2024-06-17" }]); + expect(result).toHaveLength(1); + expect(result[0].key).toBe("this_week"); + }); + + test("item 7-13 days out goes to 'next_week' bucket", () => { + // End of week = June 18, next week ends June 25 + const result = groupByDateBucket([{ date: "2024-06-22" }]); + expect(result).toHaveLength(1); + expect(result[0].key).toBe("next_week"); + }); + + test("item 30+ days out goes to month bucket", () => { + const result = groupByDateBucket([{ date: "2024-08-15" }]); + expect(result).toHaveLength(1); + expect(result[0].key).toBe("month_2024-08"); + }); + + test("multiple items in same bucket are grouped together", () => { + const result = groupByDateBucket([ + { date: "2024-06-12", name: "a" }, + { date: "2024-06-12", name: "b" }, + ]); + expect(result).toHaveLength(1); + expect(result[0].key).toBe("today"); + expect(result[0].items).toHaveLength(2); + }); + + test("items across multiple buckets are ordered by first appearance", () => { + const result = groupByDateBucket([ + { date: "2024-06-13" }, // tomorrow + { date: "2024-06-12" }, // today + { date: "2024-08-01" }, // month + ]); + expect(result).toHaveLength(3); + expect(result[0].key).toBe("tomorrow"); + expect(result[1].key).toBe("today"); + expect(result[2].key).toBe("month_2024-08"); + }); + + test("bucket labels are populated", () => { + const result = groupByDateBucket([{ date: "2024-06-12" }, { date: "2024-06-13" }]); + expect(result[0].label).toBeTruthy(); + expect(result[1].label).toBeTruthy(); + }); +}); diff --git a/packages/i18n/test/format.test.ts b/packages/i18n/test/format.test.ts new file mode 100644 index 0000000..fbe92cc --- /dev/null +++ b/packages/i18n/test/format.test.ts @@ -0,0 +1,148 @@ +import { afterEach, beforeEach, describe, expect, test, vi } from "vitest"; + +vi.mock("../src/index", () => ({ + i18n: { locale: "en" }, +})); + +import { + formatBytes, + formatDate, + formatNumber, + formatRelativeTime, + formatShortDate, +} from "../src/format"; + +describe("formatDate", () => { + test("formats a Date object", () => { + const result = formatDate(new Date("2024-06-15T12:00:00Z")); + expect(result).toContain("2024"); + expect(result).toContain("15"); + }); + + test("formats date-only ISO string in UTC to avoid off-by-one", () => { + // "1990-05-15" is parsed as UTC midnight — formatting in UTC prevents + // users west of UTC from seeing May 14 instead of May 15 + const result = formatDate("1990-05-15"); + expect(result).toContain("15"); + expect(result).toContain("1990"); + }); + + test("respects custom options", () => { + const result = formatDate("2024-06-15", { month: "short" }); + expect(result).toContain("Jun"); + }); + + test("does not force UTC when a timeZone option is provided", () => { + const result = formatDate("2024-06-15", { timeZone: "America/New_York" }); + expect(result).toBeDefined(); + }); + + test("formats a full ISO datetime string (not date-only)", () => { + const result = formatDate("2024-06-15T18:30:00Z"); + expect(result).toContain("2024"); + }); +}); + +describe("formatShortDate", () => { + test("formats with short month", () => { + // Use a local Date to avoid UTC-to-local day shift + const result = formatShortDate(new Date(2024, 0, 3)); + expect(result).toContain("Jan"); + expect(result).toContain("3"); + expect(result).toContain("2024"); + }); + + test("accepts a Date object", () => { + const result = formatShortDate(new Date(2024, 11, 25)); + expect(result).toContain("Dec"); + expect(result).toContain("25"); + }); +}); + +describe("formatRelativeTime", () => { + beforeEach(() => { + vi.useFakeTimers(); + vi.setSystemTime(new Date("2024-06-15T12:00:00Z")); + }); + + afterEach(() => { + vi.useRealTimers(); + }); + + test("seconds ago", () => { + const result = formatRelativeTime("2024-06-15T11:59:30Z"); + expect(result).toContain("30 seconds ago"); + }); + + test("minutes ago", () => { + const result = formatRelativeTime("2024-06-15T11:55:00Z"); + expect(result).toContain("5 minutes ago"); + }); + + test("hours ago", () => { + const result = formatRelativeTime("2024-06-15T09:00:00Z"); + expect(result).toContain("3 hours ago"); + }); + + test("days ago", () => { + const result = formatRelativeTime("2024-06-10T12:00:00Z"); + expect(result).toContain("5 days ago"); + }); + + test("months ago", () => { + const result = formatRelativeTime("2024-03-15T12:00:00Z"); + expect(result).toContain("3 months ago"); + }); + + test("years ago", () => { + const result = formatRelativeTime("2022-06-15T12:00:00Z"); + expect(result).toContain("2 years ago"); + }); + + test("future seconds", () => { + const result = formatRelativeTime("2024-06-15T12:00:45Z"); + expect(result).toContain("in 45 seconds"); + }); + + test("future days", () => { + const result = formatRelativeTime("2024-06-20T12:00:00Z"); + expect(result).toContain("in 5 days"); + }); +}); + +describe("formatNumber", () => { + test("formats a number", () => { + expect(formatNumber(1234)).toBe("1,234"); + }); + + test("respects options", () => { + const result = formatNumber(0.5, { style: "percent" }); + expect(result).toBe("50%"); + }); +}); + +describe("formatBytes", () => { + test("zero bytes", () => { + expect(formatBytes(0)).toBe("0 B"); + }); + + test("bytes", () => { + expect(formatBytes(512)).toBe("512 B"); + }); + + test("kilobytes", () => { + expect(formatBytes(1024)).toBe("1 KB"); + }); + + test("megabytes", () => { + expect(formatBytes(1024 * 1024)).toBe("1 MB"); + }); + + test("gigabytes", () => { + expect(formatBytes(1024 * 1024 * 1024)).toBe("1 GB"); + }); + + test("fractional megabytes", () => { + expect(formatBytes(1536 * 1024)).toBe("1.5 MB"); + }); +}); diff --git a/packages/i18n/tsconfig.json b/packages/i18n/tsconfig.json index 57475a2..c6a3e30 100644 --- a/packages/i18n/tsconfig.json +++ b/packages/i18n/tsconfig.json @@ -1,15 +1,7 @@ { + "extends": "@sofa/tsconfig/base.json", "compilerOptions": { - "target": "ESNext", - "module": "ESNext", - "moduleResolution": "bundler", - "jsx": "react-jsx", - "strict": true, - "skipLibCheck": true, - "noEmit": true, - "isolatedModules": true, - "esModuleInterop": true, - "types": ["bun"] + "jsx": "react-jsx" }, - "include": ["src"] + "include": ["src", "test"] } diff --git a/packages/i18n/vitest.config.ts b/packages/i18n/vitest.config.ts new file mode 100644 index 0000000..469e5b7 --- /dev/null +++ b/packages/i18n/vitest.config.ts @@ -0,0 +1,7 @@ +import { defineProject } from "vitest/config"; + +export default defineProject({ + test: { + include: ["test/**/*.test.ts"], + }, +}); diff --git a/packages/logger/package.json b/packages/logger/package.json index 9ef974c..4854b18 100644 --- a/packages/logger/package.json +++ b/packages/logger/package.json @@ -17,6 +17,7 @@ "pino-pretty": "13.1.3" }, "devDependencies": { + "@sofa/tsconfig": "workspace:*", "@types/bun": "catalog:", "typescript": "catalog:" } diff --git a/packages/logger/tsconfig.json b/packages/logger/tsconfig.json index 6438538..5e4fa20 100644 --- a/packages/logger/tsconfig.json +++ b/packages/logger/tsconfig.json @@ -1,14 +1,4 @@ { - "compilerOptions": { - "target": "ES2017", - "lib": ["esnext"], - "strict": true, - "noEmit": true, - "module": "esnext", - "moduleResolution": "bundler", - "isolatedModules": true, - "skipLibCheck": true, - "types": ["bun"] - }, + "extends": "@sofa/tsconfig/base.json", "include": ["src"] } diff --git a/packages/test/package.json b/packages/test/package.json index 32b0a55..9db44e7 100644 --- a/packages/test/package.json +++ b/packages/test/package.json @@ -13,6 +13,7 @@ "drizzle-orm": "catalog:" }, "devDependencies": { + "@sofa/tsconfig": "workspace:*", "@types/better-sqlite3": "7.6.13", "typescript": "catalog:", "vitest": "catalog:" diff --git a/packages/test/tsconfig.json b/packages/test/tsconfig.json index 6438538..5e4fa20 100644 --- a/packages/test/tsconfig.json +++ b/packages/test/tsconfig.json @@ -1,14 +1,4 @@ { - "compilerOptions": { - "target": "ES2017", - "lib": ["esnext"], - "strict": true, - "noEmit": true, - "module": "esnext", - "moduleResolution": "bundler", - "isolatedModules": true, - "skipLibCheck": true, - "types": ["bun"] - }, + "extends": "@sofa/tsconfig/base.json", "include": ["src"] } diff --git a/packages/tmdb/package.json b/packages/tmdb/package.json index 539fdce..4fb184e 100644 --- a/packages/tmdb/package.json +++ b/packages/tmdb/package.json @@ -20,6 +20,7 @@ "openapi-fetch": "0.17.0" }, "devDependencies": { + "@sofa/tsconfig": "workspace:*", "@types/bun": "catalog:", "typescript": "catalog:" } diff --git a/packages/tmdb/tsconfig.json b/packages/tmdb/tsconfig.json index 6438538..5e4fa20 100644 --- a/packages/tmdb/tsconfig.json +++ b/packages/tmdb/tsconfig.json @@ -1,14 +1,4 @@ { - "compilerOptions": { - "target": "ES2017", - "lib": ["esnext"], - "strict": true, - "noEmit": true, - "module": "esnext", - "moduleResolution": "bundler", - "isolatedModules": true, - "skipLibCheck": true, - "types": ["bun"] - }, + "extends": "@sofa/tsconfig/base.json", "include": ["src"] } diff --git a/packages/tsconfig/base.json b/packages/tsconfig/base.json new file mode 100644 index 0000000..8b2fa20 --- /dev/null +++ b/packages/tsconfig/base.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://www.schemastore.org/tsconfig.json", + "compilerOptions": { + "target": "ESNext", + "lib": ["ESNext"], + "module": "ESNext", + "moduleResolution": "bundler", + "isolatedModules": true, + "strict": true, + "skipLibCheck": true, + "noEmit": true, + "types": ["bun"] + } +} diff --git a/packages/tsconfig/package.json b/packages/tsconfig/package.json new file mode 100644 index 0000000..ffc5f6a --- /dev/null +++ b/packages/tsconfig/package.json @@ -0,0 +1,9 @@ +{ + "name": "@sofa/tsconfig", + "version": "0.1.0", + "private": true, + "type": "module", + "exports": { + "./base.json": "./base.json" + } +}