@vatvaghool/create-ipl-dashboard 0.1.23 → 0.1.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -61,6 +61,7 @@ npm run sync:ipl:watch # Start live sync
61
61
  | `npm run sync:ipl` | Scrape live leaderboard snapshot |
62
62
  | `npm run sync:ipl:watch` | Scrape leaderboard in watch mode |
63
63
  | `npm run sync:cloud` | Run all scrapers |
64
+ | `npm run sync:ipl:transfers-daily` | Scrape transfer/booster data |
64
65
  | `npm run seed:api` | POST /api/ops/seed — seed match data into MongoDB |
65
66
  | `npm run seed:league` | Seed league metadata into MongoDB |
66
67
  | `npm run test` | Run tests |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vatvaghool/create-ipl-dashboard",
3
- "version": "0.1.23",
3
+ "version": "0.1.25",
4
4
  "description": "Scaffold an IPL fantasy cricket dashboard project",
5
5
  "type": "module",
6
6
  "bin": {
@@ -21,6 +21,7 @@ const SKIP_PATTERNS = [
21
21
  "packages/ipl-dashboard-utils/dist",
22
22
  "next-env.d.ts",
23
23
  "tsconfig.tsbuildinfo",
24
+ "AGENTS.md",
24
25
  ];
25
26
 
26
27
  async function getAllFiles(dir) {
package/src/index.mjs CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  import { join } from "node:path";
4
4
  import { existsSync } from "node:fs";
5
- import { mkdir } from "node:fs/promises";
6
- import { getProjectName, getLeagueName, getTeams, close } from "./prompts.mjs";
5
+ import { mkdir, rm } from "node:fs/promises";
6
+ import { getProjectName, getLeagueName, getTeams } from "./prompts.mjs";
7
7
  import { scaffoldProject } from "./scaffold.mjs";
8
8
 
9
9
  function printHelp(exitCode) {
@@ -48,7 +48,6 @@ async function main() {
48
48
  const projectPath = join(process.cwd(), projectName);
49
49
 
50
50
  if (existsSync(projectPath)) {
51
- const { rm } = await import("node:fs/promises");
52
51
  console.log(` Directory "${projectName}" already exists. Removing...`);
53
52
  await rm(projectPath, { recursive: true, force: true });
54
53
  }
@@ -72,8 +71,6 @@ async function main() {
72
71
  ];
73
72
  }
74
73
 
75
- close();
76
-
77
74
  await scaffoldProject(projectPath, { leagueName, teams, skipInstall: flags.skipInstall });
78
75
 
79
76
  console.log("");
package/src/prompts.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { createInterface } from "node:readline";
2
- import { stdin as input, stdout as output, stdin } from "node:process";
2
+ import { stdin as input, stdin } from "node:process";
3
3
 
4
4
  const isTTY = stdin.isTTY;
5
5
 
@@ -65,4 +65,4 @@ export async function getTeams() {
65
65
  return parseTeams(answer);
66
66
  }
67
67
 
68
- export function close() {}
68
+
package/src/scaffold.mjs CHANGED
@@ -25,7 +25,6 @@ export async function scaffoldProject(
25
25
  for (const f of [
26
26
  "AGENTS.md",
27
27
  "tsconfig.tsbuildinfo",
28
- "tsconfig.buildinfo",
29
28
  "package-lock.json",
30
29
  ]) {
31
30
  try {
@@ -51,13 +50,6 @@ export async function scaffoldProject(
51
50
  if (!skipInstall) {
52
51
  console.log(" Running npm install...");
53
52
  execSync("npm install", { cwd: projectPath, stdio: "inherit" });
54
-
55
- console.log(" Installing Playwright browser...");
56
- try {
57
- execSync("npx playwright install chromium", { cwd: projectPath, stdio: "inherit" });
58
- } catch {
59
- console.log(" Playwright install skipped (run manually: npx playwright install chromium)");
60
- }
61
53
  } else {
62
54
  console.log(
63
55
  " Skipping npm install (run manually: cd <project> && npm install)",
@@ -24,6 +24,7 @@ Open http://localhost:3000
24
24
  | `npm run sync:ipl` | Scrape live leaderboard snapshot |
25
25
  | `npm run sync:ipl:watch` | Scrape leaderboard in watch mode |
26
26
  | `npm run sync:cloud` | Run all scrapers |
27
+ | `npm run sync:ipl:transfers-daily` | Scrape transfer/booster data |
27
28
  | `npm run seed:api` | Seed match data into MongoDB |
28
29
  | `npm run seed:league` | Seed league metadata |
29
30
  | `npm run test` | Run tests |
@@ -3,7 +3,7 @@ import path from "node:path";
3
3
  export const config = {
4
4
  mongodb: {
5
5
  databaseName: process.env.IPL_DB_NAME || "ipl",
6
- collectionName: process.env.IPL_COLLECTION_NAME || "ipl",
6
+ collectionName: process.env.COLLECTION_NAME || process.env.IPL_COLLECTION_NAME || "ipl",
7
7
  },
8
8
 
9
9
  league: {
@@ -1,14 +1,5 @@
1
1
  import { getChartColor } from "../../lib/utils/getChartColor";
2
-
3
- export function formatCompactNumber(value: number): string {
4
- if (value >= 1000000) {
5
- return `${(value / 1000000).toFixed(1)}M`;
6
- }
7
- if (value >= 1000) {
8
- return `${(value / 1000).toFixed(1)}K`;
9
- }
10
- return value.toString();
11
- }
2
+ export { formatCompactNumber } from "../../lib/utils";
12
3
 
13
4
  export function formatLedgerNumber(value?: number): string {
14
5
  if (typeof value !== "number" || !Number.isFinite(value)) {
@@ -9,6 +9,7 @@
9
9
  "capture:ipl-auth": "node scripts/capture-ipl-auth.mjs",
10
10
  "sync:ipl": "node scripts/sync-ipl.mjs",
11
11
  "sync:ipl:watch": "node scripts/sync-ipl.mjs --watch",
12
+ "sync:ipl:transfers-daily": "node scripts/sync-transfers-daily.mjs",
12
13
  "sync:cloud": "node scripts/sync-cloud.mjs",
13
14
  "build": "next build",
14
15
  "start": "next start",
@@ -13,17 +13,7 @@ import type {
13
13
  const DEFAULT_TOTAL_TRANSFERS = 160;
14
14
  const FLOAT_TOLERANCE = 0.01;
15
15
 
16
- const RAW_TEAM_ALIASES: Record<string, string> = {
17
- "Pankaj Konde": "PKs11",
18
- "Rishikesh Shinde": "Watapi",
19
- "Rushabh Shah": "RushS01",
20
- "Vijay Swami": "VATVAGHOOL XI",
21
- "Aditya Raut": "SquadSeven9",
22
- "Nilesh Birajdar": "Nilesh Birajdar",
23
- "Ravi Kiran Guthula": "RKs Stallions",
24
- "Rahul Sharma": "RSAwesome 11",
25
- "Raviteja Jakkani": "Bat Bowl XI",
26
- };
16
+ const RAW_TEAM_ALIASES: Record<string, string> = {};
27
17
 
28
18
  const resolveTeamName = (
29
19
  name: string,
@@ -35,6 +35,7 @@ const cmds = [
35
35
  { cmd: "sync:ipl", desc: "Scrape leaderboard snapshot" },
36
36
  { cmd: "sync:ipl:watch", desc: "Watch mode — rescrape every 60s" },
37
37
  { cmd: "sync:cloud", desc: "Run all scrapers" },
38
+ { cmd: "sync:ipl:transfers-daily", desc: "Scrape transfer/booster data" },
38
39
  { cmd: "seed:api", desc: "Seed match data into MongoDB" },
39
40
  { cmd: "seed:league", desc: "Seed league metadata" },
40
41
  { cmd: "test", desc: "Run tests" },
@@ -1,50 +0,0 @@
1
- #!/usr/bin/env bash
2
-
3
- set -euo pipefail
4
-
5
- APP_BASE_URL="${APP_BASE_URL:-}"
6
-
7
- if [ -z "${APP_BASE_URL}" ]; then
8
- echo "APP_BASE_URL is required. Example: APP_BASE_URL=https://your-app-domain" >&2
9
- exit 1
10
- fi
11
-
12
- URL="${APP_BASE_URL%/}/api/ops/status"
13
- TMP_BODY="$(mktemp)"
14
- TMP_HEADERS="$(mktemp)"
15
-
16
- cleanup() {
17
- rm -f "${TMP_BODY}" "${TMP_HEADERS}"
18
- }
19
-
20
- trap cleanup EXIT
21
-
22
- HTTP_CODE="$(
23
- curl -sS \
24
- -D "${TMP_HEADERS}" \
25
- -o "${TMP_BODY}" \
26
- -w "%{http_code}" \
27
- -H "Accept: application/json" \
28
- "${URL}"
29
- )"
30
-
31
- if [ "${HTTP_CODE}" -ge 500 ]; then
32
- echo "monitor failure: ${URL} returned HTTP ${HTTP_CODE}" >&2
33
- cat "${TMP_BODY}" >&2
34
- exit 1
35
- fi
36
-
37
- if grep -q '"status":"critical"' "${TMP_BODY}"; then
38
- echo "monitor failure: critical app status reported by ${URL}" >&2
39
- cat "${TMP_BODY}" >&2
40
- exit 1
41
- fi
42
-
43
- if grep -q '"status":"degraded"' "${TMP_BODY}"; then
44
- echo "monitor warning: degraded app status reported by ${URL}" >&2
45
- cat "${TMP_BODY}"
46
- exit 2
47
- fi
48
-
49
- echo "monitor ok: ${URL} returned HTTP ${HTTP_CODE}"
50
- cat "${TMP_BODY}"
@@ -1,115 +0,0 @@
1
- import { MongoClient } from "mongodb";
2
- import {
3
- buildManualDashboard,
4
- normalizeRawApiUsers,
5
- } from "../app/api/ipl/transform.ts";
6
- import { rawApiUsers } from "../app/api/ipl/data.ts";
7
-
8
- try {
9
- process.loadEnvFile(".env");
10
- } catch {
11
- // Environment variables may already be provided by the shell.
12
- }
13
-
14
- const DB_NAME = "ipl";
15
- const COLLECTION_NAME = "ipl";
16
- const RAW_USERS_DOCUMENT_TYPE = "raw-users";
17
-
18
- const args = new Set(process.argv.slice(2));
19
- const dryRun = args.has("--dry-run");
20
- const force = args.has("--force");
21
- const reset = args.has("--reset");
22
-
23
- const loadSeedUsers = () => {
24
- const users = normalizeRawApiUsers(rawApiUsers);
25
-
26
- if (!users) {
27
- throw new Error(
28
- "Failed to normalize raw seed users from app/api/ipl/data.ts",
29
- );
30
- }
31
-
32
- return users;
33
- };
34
-
35
- const createRawUsersDocument = (users: ReturnType<typeof loadSeedUsers>) => ({
36
- type: RAW_USERS_DOCUMENT_TYPE,
37
- updatedAt: new Date().toISOString(),
38
- users: users.map((user) => ({
39
- rno: user.rno,
40
- temname: user.temname,
41
- points: user.points,
42
- matches: [...user.matches].sort((a, b) => a.matchId - b.matchId),
43
- })),
44
- });
45
-
46
- const logPlan = () => {
47
- const users = loadSeedUsers();
48
- const dashboard = buildManualDashboard(users);
49
-
50
- console.log(`Seed users: ${users.length}`);
51
- console.log(
52
- `Latest match id: ${Math.max(
53
- ...users.flatMap((user) => user.matches.map((match) => match.matchId)),
54
- )}`,
55
- );
56
- console.log(`Reset requested: ${reset ? "yes" : "no"}`);
57
- console.log(`Dashboard rows: ${dashboard.daily.length}`);
58
- console.log(`Leaderboard rows: ${dashboard.overall.length}`);
59
- };
60
-
61
- const run = async () => {
62
- if (dryRun) {
63
- logPlan();
64
- console.log("Dry run only. No MongoDB writes performed.");
65
- return;
66
- }
67
-
68
- const uri = process.env.MONGODB_URI;
69
-
70
- if (!uri) {
71
- throw new Error("MONGODB_URI is required to seed MongoDB.");
72
- }
73
-
74
- const users = loadSeedUsers();
75
- const client = new MongoClient(uri);
76
-
77
- try {
78
- await client.connect();
79
-
80
- const collection = client.db(DB_NAME).collection(COLLECTION_NAME);
81
-
82
- if (reset) {
83
- const result = await collection.deleteMany({});
84
- console.log(`Deleted ${result.deletedCount} IPL MongoDB documents.`);
85
- }
86
-
87
- const existingRawUsers = await collection.findOne({
88
- type: RAW_USERS_DOCUMENT_TYPE,
89
- });
90
-
91
- if (!existingRawUsers || force || reset) {
92
- await collection.updateOne(
93
- { type: RAW_USERS_DOCUMENT_TYPE },
94
- { $set: createRawUsersDocument(users) },
95
- { upsert: true },
96
- );
97
- console.log(
98
- existingRawUsers
99
- ? "Updated raw-users document from local seed data."
100
- : "Inserted raw-users document from local seed data.",
101
- );
102
- } else {
103
- console.log(
104
- "Skipped raw-users document because it already exists. Use --force to overwrite.",
105
- );
106
- }
107
- } finally {
108
- await client.close();
109
- }
110
- };
111
-
112
- run().catch((error) => {
113
- console.error(error instanceof Error ? error.message : error);
114
- process.exitCode = 1;
115
- });
@@ -1,108 +0,0 @@
1
- const readEnv = (name, fallback = "") => process.env[name]?.trim() || fallback;
2
-
3
- const baseUrl = readEnv("APP_BASE_URL");
4
-
5
- if (!baseUrl) {
6
- throw new Error("APP_BASE_URL is required. Example: https://your-app-domain");
7
- }
8
-
9
- const normalizedBaseUrl = baseUrl.replace(/\/$/, "");
10
-
11
- const checks = [
12
- {
13
- name: "ops status",
14
- path: "/api/ops/status",
15
- validate: (payload, response) => {
16
- if (!payload || typeof payload !== "object") {
17
- throw new Error("Invalid JSON payload.");
18
- }
19
- if (!("mongoConfigured" in payload)) {
20
- throw new Error("Missing mongoConfigured field.");
21
- }
22
- if (!response.ok) {
23
- const statusLabel =
24
- payload && typeof payload === "object" && "status" in payload
25
- ? String(payload.status ?? "unknown")
26
- : "unknown";
27
- throw new Error(
28
- `Ops status reported HTTP ${response.status} with status ${statusLabel}.`,
29
- );
30
- }
31
- },
32
- },
33
- {
34
- name: "dashboard",
35
- path: "/api/ipl",
36
- validate: (payload) => {
37
- if (!payload || typeof payload !== "object") {
38
- throw new Error("Invalid JSON payload.");
39
- }
40
- if (!("overall" in payload) && !("error" in payload)) {
41
- throw new Error("Expected overall data or error payload.");
42
- }
43
- },
44
- },
45
- {
46
- name: "snapshot",
47
- path: "/api/ipl?format=snapshot",
48
- validate: (payload) => {
49
- if (!payload || typeof payload !== "object") {
50
- throw new Error("Invalid JSON payload.");
51
- }
52
- if (!("leaders" in payload) && !("error" in payload)) {
53
- throw new Error("Expected leaders data or error payload.");
54
- }
55
- },
56
- },
57
- {
58
- name: "transfers",
59
- path: "/api/ipl/transfers",
60
- validate: (payload) => {
61
- if (!payload || typeof payload !== "object") {
62
- throw new Error("Invalid JSON payload.");
63
- }
64
- if (!("teams" in payload) && !("error" in payload)) {
65
- throw new Error("Expected transfer snapshot or error payload.");
66
- }
67
- },
68
- },
69
- ];
70
-
71
- const run = async () => {
72
- for (const check of checks) {
73
- const url = `${normalizedBaseUrl}${check.path}`;
74
- const response = await fetch(url, {
75
- headers: {
76
- Accept: "application/json",
77
- },
78
- });
79
-
80
- const rawBody = await response.text();
81
- let payload;
82
- try {
83
- payload = JSON.parse(rawBody);
84
- } catch {
85
- throw new Error(`[verify-production] ${check.name} returned non-JSON: ${rawBody}`);
86
- }
87
-
88
- check.validate(payload, response);
89
-
90
- console.log(
91
- JSON.stringify(
92
- {
93
- check: check.name,
94
- url,
95
- status: response.status,
96
- ok: response.ok,
97
- },
98
- null,
99
- 2,
100
- ),
101
- );
102
- }
103
- };
104
-
105
- run().catch((error) => {
106
- console.error(error instanceof Error ? error.message : error);
107
- process.exitCode = 1;
108
- });