RohanVashisht commited on
Commit
33ffc9c
·
1 Parent(s): cecb963

Refactor index.ts to streamline data sorting and filtering for packages and programs; implement indexDetails endpoints

Browse files
Files changed (1) hide show
  1. src/index.ts +110 -179
src/index.ts CHANGED
@@ -1,216 +1,147 @@
1
  import { serve } from "bun";
2
- import packages from "../database/database/packages.json";
3
- import programs from "../database/database/programs.json";
4
-
5
- // Pre-sort data for indexDetails endpoints
6
- const packagesLatestRepos = [...packages].sort(
7
- (a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime()
8
- );
9
- const packagesMostUsed = [...packages].sort(
10
- (a, b) => (b.usage_count || 0) - (a.usage_count || 0)
11
- );
12
- const programsLatestRepos = [...programs].sort(
13
- (a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime()
14
- );
15
- const programsMostUsed = [...programs].sort(
16
- (a, b) => (b.usage_count || 0) - (a.usage_count || 0)
17
- );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
 
19
  serve({
20
  port: 7860,
21
  fetch(req) {
22
  const url = new URL(req.url);
23
- const pathname = url.pathname;
24
- const q = url.searchParams.get("q")?.trim().toLowerCase();
25
- const filter = url.searchParams.get("filter")?.trim().toLowerCase();
26
 
27
- const corsHeaders = {
28
- "Content-Type": "application/json",
29
- "Access-Control-Allow-Origin": "*",
30
- };
31
-
32
- // CORS preflight handling
33
- if (req.method === "OPTIONS") {
34
  return new Response(null, {
35
  status: 204,
36
  headers: {
37
- "Access-Control-Allow-Origin": "*",
38
  "Access-Control-Allow-Methods": "GET, POST, OPTIONS",
39
  "Access-Control-Allow-Headers": "Content-Type",
40
  },
41
  });
42
- }
43
 
44
- if (pathname === "/api/searchPackages") {
45
- const results = packages.filter(
46
- ({ name, full_name, description, topics }) => {
47
- if (
48
- filter &&
49
- !topics?.some((t: string) => t.toLowerCase() === filter)
50
- )
51
- return false;
52
- if (!q) return true;
53
- return [name, full_name, description, ...(topics || [])].some(
54
- (field: string) => field?.toLowerCase().includes(q),
55
- );
56
- },
57
- );
58
 
59
- return new Response(JSON.stringify(results.slice(0, 25)), {
60
- status: 200,
61
- headers: corsHeaders,
62
- });
63
- }
64
-
65
- if (pathname === "/api/searchPrograms") {
66
- const results = programs.filter(({ full_name, description, topics }) => {
67
- if (filter && !topics?.some((t: string) => t.toLowerCase() === filter))
68
- return false;
69
- if (!q) return true;
70
- return [full_name, description, ...(topics || [])].some(
71
- (field: string) => field?.toLowerCase().includes(q),
72
- );
73
- });
74
-
75
- return new Response(JSON.stringify(results.slice(0, 25)), {
76
- status: 200,
77
- headers: corsHeaders,
78
- });
79
- }
80
 
81
  if (pathname === "/api/infiniteScrollPackages") {
82
- const pageNumberParam = url.searchParams.get("pageNumber");
83
- const pageNumber = parseInt(pageNumberParam || "0", 10);
84
-
85
- if (isNaN(pageNumber) || pageNumber < 0) {
86
- return new Response(JSON.stringify({ error: "Invalid page number" }), {
87
- status: 400,
88
- headers: corsHeaders,
89
- });
90
- }
91
-
92
- const lowerLimit = pageNumber * 10;
93
- const scrollResults = packages.slice(lowerLimit, lowerLimit + 10);
94
-
95
- return new Response(JSON.stringify(scrollResults), {
96
- status: 200,
97
- headers: corsHeaders,
98
- });
99
  }
100
 
101
  if (pathname === "/api/infiniteScrollPrograms") {
102
- const pageNumberParam = url.searchParams.get("pageNumber");
103
- const pageNumber = parseInt(pageNumberParam || "0", 10);
104
-
105
- if (isNaN(pageNumber) || pageNumber < 0) {
106
- return new Response(JSON.stringify({ error: "Invalid page number" }), {
107
- status: 400,
108
- headers: corsHeaders,
109
- });
110
- }
111
-
112
- const lowerLimit = pageNumber * 10;
113
- const scrollResults = programs.slice(lowerLimit, lowerLimit + 10);
114
-
115
- return new Response(JSON.stringify(scrollResults), {
116
- status: 200,
117
- headers: corsHeaders,
118
- });
119
  }
120
 
121
- // New: Get program by owner and repo name
122
- const programMatch = pathname.match(/^\/api\/programs\/([^\/]+)\/([^\/]+)$/);
123
  if (programMatch) {
124
- const [, owner, repo] = programMatch;
125
- const program = programs.find(
126
- (p) =>
127
- p.owner?.toLowerCase() === owner.toLowerCase() &&
128
- p.name?.toLowerCase() === repo.toLowerCase()
129
- );
130
- if (program) {
131
- return new Response(JSON.stringify(program), {
132
- status: 200,
133
- headers: corsHeaders,
134
- });
135
- }
136
- return new Response(JSON.stringify({ error: "Program not found" }), {
137
- status: 404,
138
  headers: corsHeaders,
139
  });
140
  }
141
 
142
- // New: Get package by owner and repo name
143
- const packageMatch = pathname.match(/^\/api\/packages\/([^\/]+)\/([^\/]+)$/);
144
  if (packageMatch) {
145
- const [, owner, repo] = packageMatch;
146
- const pkg = packages.find(
147
- (p) =>
148
- p.owner?.toLowerCase() === owner.toLowerCase() &&
149
- p.name?.toLowerCase() === repo.toLowerCase()
150
- );
151
- if (pkg) {
152
- return new Response(JSON.stringify(pkg), {
153
- status: 200,
154
- headers: corsHeaders,
155
- });
156
- }
157
- return new Response(JSON.stringify({ error: "Package not found" }), {
158
- status: 404,
159
  headers: corsHeaders,
160
  });
161
  }
162
 
163
- // Helper to parse range string like "0..100"
164
- function parseRange(rangeStr: string | null, max: number) {
165
- if (!rangeStr) return [0, Math.min(10, max)];
166
- const match = rangeStr.match(/^(\d+)\.\.(\d+)$/);
167
- if (!match) return [0, Math.min(10, max)];
168
- const start = Math.max(0, parseInt(match[1], 10));
169
- const end = Math.min(max, parseInt(match[2], 10));
170
- return [start, end];
 
171
  }
172
 
173
- // /api/indexDetailsPackages
174
- if (pathname === "/api/indexDetailsPackages") {
175
- const section = url.searchParams.get("section");
176
- const rangeStr = url.searchParams.get("range");
177
- let data = packages;
178
-
179
- if (section === "latestRepos") {
180
- data = packagesLatestRepos;
181
- } else if (section === "mostUsed") {
182
- data = packagesMostUsed;
183
- }
184
-
185
- const [start, end] = parseRange(rangeStr, data.length);
186
- return new Response(JSON.stringify(data.slice(start, end)), {
187
- status: 200,
188
- headers: corsHeaders,
189
- });
190
- }
191
-
192
- // /api/indexDetailsPrograms
193
- if (pathname === "/api/indexDetailsPrograms") {
194
- const section = url.searchParams.get("section");
195
- const rangeStr = url.searchParams.get("range");
196
- let data = programs;
197
-
198
- if (section === "latestRepos") {
199
- data = programsLatestRepos;
200
- } else if (section === "mostUsed") {
201
- data = programsMostUsed;
202
- }
203
-
204
- const [start, end] = parseRange(rangeStr, data.length);
205
- return new Response(JSON.stringify(data.slice(start, end)), {
206
- status: 200,
207
- headers: corsHeaders,
208
- });
209
- }
210
-
211
- return new Response("Not Found", {
212
- status: 404,
213
- headers: { "Access-Control-Allow-Origin": "*" },
214
- });
215
  },
216
  });
 
 
 
1
  import { serve } from "bun";
2
+ import packages from "../database/database/packages.json" assert { type: "json" };
3
+ import programs from "../database/database/programs.json" assert { type: "json" };
4
+
5
+ const typedPackages: Item[] = packages as Item[];
6
+ const typedPrograms: Item[] = programs as Item[];
7
+
8
+ type Item = {
9
+ name?: string;
10
+ full_name?: string;
11
+ owner?: string;
12
+ description?: string | null;
13
+ topics?: string[];
14
+ created_at?: string;
15
+ usage_count?: number;
16
+ };
17
+
18
+ const corsHeaders = {
19
+ "Content-Type": "application/json",
20
+ "Access-Control-Allow-Origin": "*",
21
+ };
22
+
23
+ const sortByDate = (a: Item, b: Item) =>
24
+ new Date(b.created_at ?? "").getTime() - new Date(a.created_at ?? "").getTime();
25
+
26
+ const sortByUsage = (a: Item, b: Item) =>
27
+ (b.usage_count ?? 0) - (a.usage_count ?? 0);
28
+
29
+ const sorted = {
30
+ packages: {
31
+ latest: [...typedPackages].sort(sortByDate),
32
+ mostUsed: [...typedPackages].sort(sortByUsage),
33
+ },
34
+ programs: {
35
+ latest: [...typedPrograms].sort(sortByDate),
36
+ mostUsed: [...typedPrograms].sort(sortByUsage),
37
+ },
38
+ };
39
+ mostUsed: [...programs].sort(sortByUsage),
40
+ },
41
+ };
42
+
43
+ function filterItems(items: Item[], q: string | null, filter: string | null) {
44
+ return items.filter(({ name, full_name, description, topics }) => {
45
+ if (filter && !topics?.some(t => t.toLowerCase() === filter)) return false;
46
+ if (!q) return true;
47
+ const qlc = q.toLowerCase();
48
+ return [name, full_name, description, ...(topics ?? [])].some(
49
+ field => field?.toLowerCase().includes(qlc)
50
+ );
51
+ });
52
+ }
53
+
54
+ function getPaginated(items: Item[], page = 0, size = 10) {
55
+ return items.slice(page * size, page * size + size);
56
+ }
57
+
58
+ function findItem(items: Item[], owner: string, repo: string) {
59
+ owner = owner.toLowerCase();
60
+ repo = repo.toLowerCase();
61
+ return items.find(
62
+ ({ owner: o, name, full_name }) =>
63
+ (o?.toLowerCase() === owner && name?.toLowerCase() === repo) ||
64
+ full_name?.toLowerCase() === `${owner}/${repo}`
65
+ );
66
+ }
67
+
68
+ function parseRange(str: string | null, max: number): [number, number] {
69
+ const m = str?.match(/^(\d+)\.\.(\d+)$/);
70
+ const [start, end] = m ? [parseInt(m[1]), parseInt(m[2])] : [0, 10];
71
+ return [Math.max(0, start), Math.min(max, end)];
72
+ }
73
 
74
  serve({
75
  port: 7860,
76
  fetch(req) {
77
  const url = new URL(req.url);
78
+ const { pathname, searchParams } = url;
79
+ const q = searchParams.get("q")?.trim().toLowerCase() ?? null;
80
+ const filter = searchParams.get("filter")?.trim().toLowerCase() ?? null;
81
 
82
+ if (req.method === "OPTIONS")
 
 
 
 
 
 
83
  return new Response(null, {
84
  status: 204,
85
  headers: {
86
+ ...corsHeaders,
87
  "Access-Control-Allow-Methods": "GET, POST, OPTIONS",
88
  "Access-Control-Allow-Headers": "Content-Type",
89
  },
90
  });
 
91
 
92
+ if (pathname === "/api/searchPackages")
93
+ return Response.json(filterItems(packages, q, filter).slice(0, 25), { headers: corsHeaders });
 
 
 
 
 
 
 
 
 
 
 
 
94
 
95
+ if (pathname === "/api/searchPrograms")
96
+ return Response.json(filterItems(programs, q, filter).slice(0, 25), { headers: corsHeaders });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
 
98
  if (pathname === "/api/infiniteScrollPackages") {
99
+ const page = parseInt(searchParams.get("pageNumber") || "0", 10);
100
+ if (isNaN(page) || page < 0)
101
+ return Response.json({ error: "Invalid page number" }, { status: 400, headers: corsHeaders });
102
+ return Response.json(getPaginated(packages, page), { headers: corsHeaders });
 
 
 
 
 
 
 
 
 
 
 
 
 
103
  }
104
 
105
  if (pathname === "/api/infiniteScrollPrograms") {
106
+ const page = parseInt(searchParams.get("pageNumber") || "0", 10);
107
+ if (isNaN(page) || page < 0)
108
+ return Response.json({ error: "Invalid page number" }, { status: 400, headers: corsHeaders });
109
+ return Response.json(getPaginated(programs, page), { headers: corsHeaders });
 
 
 
 
 
 
 
 
 
 
 
 
 
110
  }
111
 
112
+ const programMatch = pathname.match(/^\/api\/programs\/([^/]+)\/([^/]+)$/);
 
113
  if (programMatch) {
114
+ const [, owner, repo] = programMatch.map(s => s.toLowerCase());
115
+ const found = findItem(programs, owner, repo);
116
+ return Response.json(found || { error: "Program not found" }, {
117
+ status: found ? 200 : 404,
 
 
 
 
 
 
 
 
 
 
118
  headers: corsHeaders,
119
  });
120
  }
121
 
122
+ const packageMatch = pathname.match(/^\/api\/packages\/([^/]+)\/([^/]+)$/);
 
123
  if (packageMatch) {
124
+ const [, owner, repo] = packageMatch.map(s => s.toLowerCase());
125
+ const found = findItem(packages, owner, repo);
126
+ return Response.json(found || { error: "Package not found" }, {
127
+ status: found ? 200 : 404,
 
 
 
 
 
 
 
 
 
 
128
  headers: corsHeaders,
129
  });
130
  }
131
 
132
+ if (pathname === "/api/indexDetailsPackages" || pathname === "/api/indexDetailsPrograms") {
133
+ const section = searchParams.get("section");
134
+ if (section !== "latestRepos" && section !== "mostUsed")
135
+ return Response.json({ error: "Invalid section" }, { status: 400, headers: corsHeaders });
136
+ const key = section === "latestRepos" ? "latest" : "mostUsed";
137
+ const which = pathname.includes("Packages") ? "packages" : "programs";
138
+ const data = sorted[which as "packages" | "programs"][key as "latest" | "mostUsed"] ?? (which === "packages" ? packages : programs);
139
+ const [start, end] = parseRange(searchParams.get("range"), data.length);
140
+ return Response.json(data.slice(start, end), { headers: corsHeaders });
141
  }
142
 
143
+ return new Response("Not Found", { status: 404, headers: corsHeaders });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
144
  },
145
  });
146
+
147
+ console.log("Server running on http://localhost:7860");