@tscircuit/fake-snippets 0.0.105 → 0.0.107

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.
Files changed (33) hide show
  1. package/CLAUDE.md +92 -0
  2. package/api/generated-index.js +3 -4
  3. package/bun-tests/fake-snippets-api/routes/package_builds/get.test.ts +294 -0
  4. package/bun-tests/fake-snippets-api/routes/package_builds/list.test.ts +304 -0
  5. package/dist/bundle.js +698 -335
  6. package/dist/index.d.ts +147 -4
  7. package/dist/index.js +195 -7
  8. package/dist/schema.d.ts +206 -1
  9. package/dist/schema.js +31 -2
  10. package/fake-snippets-api/lib/db/db-client.ts +60 -3
  11. package/fake-snippets-api/lib/db/schema.ts +31 -0
  12. package/fake-snippets-api/lib/db/seed.ts +139 -2
  13. package/fake-snippets-api/lib/public-mapping/public-map-package-build.ts +41 -0
  14. package/fake-snippets-api/routes/api/package_builds/get.ts +70 -0
  15. package/fake-snippets-api/routes/api/package_builds/list.ts +97 -0
  16. package/package.json +3 -2
  17. package/src/App.tsx +21 -5
  18. package/src/components/PackageBreadcrumb.tsx +111 -0
  19. package/src/components/ViewPackagePage/components/sidebar-releases-section.tsx +22 -11
  20. package/src/components/preview/BuildsList.tsx +196 -211
  21. package/src/components/preview/ConnectedPackagesList.tsx +54 -25
  22. package/src/components/preview/ConnectedRepoOverview.tsx +63 -35
  23. package/src/components/preview/{ConnectedRepoDashboard.tsx → PackageReleasesDashboard.tsx} +33 -71
  24. package/src/components/preview/index.tsx +20 -77
  25. package/src/hooks/use-package-builds.ts +87 -0
  26. package/src/hooks/use-package-release-by-id-or-version.ts +36 -0
  27. package/src/hooks/use-package-release.ts +32 -0
  28. package/src/lib/utils/isUuid.ts +5 -0
  29. package/src/pages/preview-build.tsx +3 -3
  30. package/src/pages/release-builds.tsx +107 -0
  31. package/src/pages/release-detail.tsx +118 -0
  32. package/src/pages/releases.tsx +51 -0
  33. package/src/pages/view-connected-repo.tsx +0 -18
@@ -11,22 +11,30 @@ import {
11
11
  ChevronRight,
12
12
  User,
13
13
  Hash,
14
+ GitCommit,
14
15
  } from "lucide-react"
15
16
  import {
16
17
  Collapsible,
17
18
  CollapsibleContent,
18
19
  CollapsibleTrigger,
19
20
  } from "@/components/ui/collapsible"
20
- import { getBuildStatus, PackageBuild, StatusIcon } from "."
21
+ import { getBuildStatus, StatusIcon } from "."
21
22
  import { formatTimeAgo } from "@/lib/utils/formatTimeAgo"
22
-
23
- interface ConnectedRepoOverviewProps {
24
- build: PackageBuild
25
- }
23
+ import {
24
+ Package,
25
+ PackageBuild,
26
+ PackageRelease,
27
+ } from "fake-snippets-api/lib/db/schema"
26
28
 
27
29
  export const ConnectedRepoOverview = ({
28
30
  build,
29
- }: ConnectedRepoOverviewProps) => {
31
+ pkg,
32
+ packageRelease,
33
+ }: {
34
+ build: PackageBuild
35
+ pkg: Package
36
+ packageRelease: PackageRelease
37
+ }) => {
30
38
  const { status, label } = getBuildStatus(build)
31
39
  const [openSections, setOpenSections] = useState({
32
40
  transpilation: false,
@@ -52,9 +60,9 @@ export const ConnectedRepoOverview = ({
52
60
  }
53
61
 
54
62
  const getStepStatus = (
55
- error: string | null,
56
- completed: string | null,
57
- inProgress: boolean,
63
+ error?: string | null,
64
+ completed?: string | null,
65
+ inProgress?: boolean,
58
66
  ) => {
59
67
  if (error) return "error"
60
68
  if (completed) return "success"
@@ -63,8 +71,8 @@ export const ConnectedRepoOverview = ({
63
71
  }
64
72
 
65
73
  const getStepDuration = (
66
- started: string | null,
67
- completed: string | null,
74
+ started?: string | null,
75
+ completed?: string | null,
68
76
  ) => {
69
77
  if (started && completed) {
70
78
  const duration = Math.floor(
@@ -85,11 +93,13 @@ export const ConnectedRepoOverview = ({
85
93
  <div className="flex-1">
86
94
  <div className="flex flex-col sm:flex-row sm:items-center sm:gap-3">
87
95
  <h1 className="text-xl sm:text-2xl font-semibold text-gray-900">
88
- Deployment {label}
96
+ Build {label}
89
97
  </h1>
90
98
  </div>
91
99
  <p className="text-sm text-gray-600 mt-1">
92
- Created {formatTimeAgo(build.created_at)}
100
+ <time dateTime={build.created_at}>
101
+ Built {formatTimeAgo(build.created_at)}
102
+ </time>
93
103
  </p>
94
104
  </div>
95
105
  </div>
@@ -101,7 +111,7 @@ export const ConnectedRepoOverview = ({
101
111
  onClick={() => window.open(build.preview_url!, "_blank")}
102
112
  >
103
113
  <ExternalLink className="w-3 h-3" />
104
- Preview Deployment
114
+ Preview
105
115
  </Button>
106
116
  )}
107
117
  </div>
@@ -119,7 +129,7 @@ export const ConnectedRepoOverview = ({
119
129
  <div className="flex items-center gap-2">
120
130
  <button
121
131
  onClick={() => copyToClipboard(build.package_build_id)}
122
- className="group-hover:text-blue-500 rounded transition-colors"
132
+ className="group-hover:text-blue-500 rounded text-left transition-colors"
123
133
  >
124
134
  {build.package_build_id}
125
135
  </button>
@@ -128,13 +138,21 @@ export const ConnectedRepoOverview = ({
128
138
  </div>
129
139
 
130
140
  <div className="flex items-center gap-3 group">
131
- <GitBranch className="w-4 h-4 text-gray-500 group-hover:text-blue-500 transition-colors" />
141
+ {packageRelease?.is_pr_preview ? (
142
+ <GitBranch className="w-4 h-4 text-gray-500 group-hover:text-blue-500 transition-colors" />
143
+ ) : (
144
+ <GitCommit className="w-4 h-4 text-gray-500 group-hover:text-blue-500 transition-colors" />
145
+ )}
132
146
  <div>
133
147
  <p className="text-xs text-gray-500 uppercase tracking-wide">
134
- Branch
148
+ {packageRelease?.is_pr_preview ? "PR" : "Branch"}
135
149
  </p>
136
150
  <a
137
- href={`https://github.com/tscircuit/tscircuit/tree/${build.branch_name || "main"}`}
151
+ href={
152
+ packageRelease?.is_pr_preview
153
+ ? `https://github.com/${pkg.github_repo_full_name}/pull/${packageRelease.github_pr_number}`
154
+ : `https://github.com/${pkg.github_repo_full_name}/tree/${build.branch_name || "main"}`
155
+ }
138
156
  target="_blank"
139
157
  rel="noopener noreferrer"
140
158
  className="inline-block"
@@ -143,7 +161,9 @@ export const ConnectedRepoOverview = ({
143
161
  variant="outline"
144
162
  className="text-xs mt-1 hover:bg-gray-100 cursor-pointer transition-colors"
145
163
  >
146
- {build.branch_name || "main"}
164
+ {packageRelease?.is_pr_preview
165
+ ? `#${packageRelease.github_pr_number}`
166
+ : build?.branch_name || "main"}
147
167
  </Badge>
148
168
  </a>
149
169
  </div>
@@ -166,22 +186,20 @@ export const ConnectedRepoOverview = ({
166
186
  </div>
167
187
  </div>
168
188
 
169
- {buildDuration && (
170
- <div className="flex items-center gap-3 group">
171
- <Clock className="w-4 h-4 text-gray-500 group-hover:text-blue-500 transition-colors" />
172
- <div>
173
- <p className="text-xs text-gray-500 uppercase tracking-wide">
174
- Duration
175
- </p>
176
- <p
177
- className="text-sm font-medium hover:text-blue-500 transition-colors cursor-help"
178
- title={`Build started at ${build.build_started_at}`}
179
- >
180
- {buildDuration}s
181
- </p>
182
- </div>
189
+ <div className="flex items-center gap-3 group">
190
+ <Clock className="w-4 h-4 text-gray-500 group-hover:text-blue-500 transition-colors" />
191
+ <div>
192
+ <p className="text-xs text-gray-500 uppercase tracking-wide">
193
+ Duration
194
+ </p>
195
+ <p
196
+ className="text-sm font-medium hover:text-blue-500 transition-colors cursor-help"
197
+ title={`Build started at ${build.build_started_at}`}
198
+ >
199
+ {buildDuration}s
200
+ </p>
183
201
  </div>
184
- )}
202
+ </div>
185
203
  </div>
186
204
 
187
205
  {build.commit_message && (
@@ -198,7 +216,17 @@ export const ConnectedRepoOverview = ({
198
216
  </div>
199
217
 
200
218
  <div className="space-y-3">
201
- <h2 className="text-lg font-semibold text-gray-900">Logs</h2>
219
+ <div className="flex items-center justify-between">
220
+ <h2 className="text-lg font-semibold text-gray-900">
221
+ Latest Build Logs
222
+ </h2>
223
+ <a
224
+ href={`/${pkg.name.split("/")[0]}/${pkg.name.split("/")[1]}/release/${packageRelease.package_release_id}/builds`}
225
+ className="text-sm text-blue-600 hover:text-blue-800 transition-colors"
226
+ >
227
+ (previous builds)
228
+ </a>
229
+ </div>
202
230
 
203
231
  <Collapsible
204
232
  open={openSections.transpilation}
@@ -1,57 +1,44 @@
1
- import { useState } from "react"
2
1
  import { Button } from "@/components/ui/button"
3
2
  import { Badge } from "@/components/ui/badge"
4
- import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
5
3
  import {
6
4
  DropdownMenu,
7
5
  DropdownMenuContent,
8
6
  DropdownMenuItem,
9
7
  DropdownMenuTrigger,
10
8
  } from "@/components/ui/dropdown-menu"
11
- import {
12
- Activity,
13
- Settings,
14
- List,
15
- Github,
16
- MoreHorizontal,
17
- Zap,
18
- Clock,
19
- GitBranch,
20
- Hash,
21
- User,
22
- Eye,
23
- } from "lucide-react"
9
+ import { MoreHorizontal, Clock, GitBranch, Eye } from "lucide-react"
10
+ import { GitHubLogoIcon } from "@radix-ui/react-icons"
24
11
  import { useLocation } from "wouter"
25
- import { ConnectedRepoOverview } from "./ConnectedRepoOverview"
26
12
  import { BuildsList } from "./BuildsList"
27
13
  import Header from "../Header"
28
14
  import { formatTimeAgo } from "@/lib/utils/formatTimeAgo"
29
- import {
30
- getBuildStatus,
31
- PackageBuild,
32
- MOCK_DEPLOYMENTS,
33
- getPackageFromBuild,
34
- } from "."
15
+ import { getBuildStatus } from "."
35
16
  import { PrefetchPageLink } from "../PrefetchPageLink"
17
+ import { PackageBreadcrumb } from "../PackageBreadcrumb"
18
+ import { Package, PackageBuild } from "fake-snippets-api/lib/db/schema"
36
19
 
37
- export const ConnectedRepoDashboard = ({
20
+ export const PackageReleasesDashboard = ({
38
21
  latestBuild,
22
+ pkg,
39
23
  }: {
40
- latestBuild: PackageBuild
24
+ latestBuild: PackageBuild | null
25
+ pkg: Package
41
26
  }) => {
42
- const [activeTab, setActiveTab] = useState("overview")
43
27
  const [, setLocation] = useLocation()
44
- const pkg = getPackageFromBuild(latestBuild)
45
- const handleSelectBuild = (build: PackageBuild) => {
46
- setLocation(`/build/${build.package_build_id}`)
47
- setActiveTab("overview")
48
- }
49
28
  const { status, label } = getBuildStatus(latestBuild)
50
29
 
51
30
  return (
52
31
  <>
53
32
  <Header />
54
33
  <div className="min-h-screen bg-white">
34
+ <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 pt-4">
35
+ <PackageBreadcrumb
36
+ author={pkg.name.split("/")[0]}
37
+ packageName={pkg.name}
38
+ unscopedName={pkg.unscoped_name}
39
+ currentPage="releases"
40
+ />
41
+ </div>
55
42
  {/* Project Header */}
56
43
  <div className="bg-gray-50 border-b md:py-10">
57
44
  <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
@@ -70,7 +57,7 @@ export const ConnectedRepoDashboard = ({
70
57
  href={"/" + pkg.name}
71
58
  className="text-2xl font-bold text-gray-900 truncate"
72
59
  >
73
- {pkg.unscoped_name}
60
+ {pkg.name}
74
61
  </PrefetchPageLink>
75
62
  <Badge
76
63
  variant={
@@ -101,22 +88,26 @@ export const ConnectedRepoDashboard = ({
101
88
  className="flex cursor-pointer items-center gap-1"
102
89
  onClick={() =>
103
90
  window?.open(
104
- `https://github.com/${pkg.github_repo_full_name}/tree/${latestBuild.branch_name || "main"}`,
91
+ `https://github.com/${pkg.github_repo_full_name}/tree/${latestBuild?.branch_name || "main"}`,
105
92
  "_blank",
106
93
  )
107
94
  }
108
95
  >
109
96
  <GitBranch className="w-4 h-4 flex-shrink-0" />
110
97
  <span className="truncate">
111
- {latestBuild.branch_name || "main"}
98
+ {latestBuild?.branch_name || "main"}
112
99
  </span>
113
100
  </div>
114
101
  <div className="flex items-center gap-1">
115
102
  <Clock className="w-4 h-4 flex-shrink-0" />
116
103
  <span>
117
- <time dateTime={latestBuild.created_at}>
118
- Last deployed {formatTimeAgo(latestBuild.created_at)}
119
- </time>
104
+ {latestBuild?.created_at ? (
105
+ <time dateTime={latestBuild.created_at}>
106
+ Last built {formatTimeAgo(latestBuild.created_at)}
107
+ </time>
108
+ ) : (
109
+ "No builds yet"
110
+ )}
120
111
  </span>
121
112
  </div>
122
113
  </div>
@@ -135,18 +126,18 @@ export const ConnectedRepoDashboard = ({
135
126
  )
136
127
  }
137
128
  >
138
- <Github className="w-4 h-4" />
129
+ <GitHubLogoIcon className="w-4 h-4" />
139
130
  <span className="hidden sm:inline">Repository</span>
140
131
  <span className="sm:hidden">Repository</span>
141
132
  </Button>
142
- {latestBuild.preview_url && (
133
+ {latestBuild?.preview_url && (
143
134
  <Button
144
135
  variant="outline"
145
136
  size="sm"
146
137
  className="flex items-center gap-2 justify-center min-w-[120px] h-9"
147
138
  onClick={() =>
148
139
  window.open(
149
- `/build/${latestBuild.package_build_id}/preview`,
140
+ `/build/${latestBuild?.package_build_id}/preview`,
150
141
  "_blank",
151
142
  )
152
143
  }
@@ -156,14 +147,6 @@ export const ConnectedRepoDashboard = ({
156
147
  <span className="sm:hidden">Preview</span>
157
148
  </Button>
158
149
  )}
159
- <Button
160
- size="sm"
161
- className="flex items-center gap-2 justify-center min-w-[120px] h-9 bg-black text-white"
162
- >
163
- <Zap className="w-4 h-4" />
164
- <span className="hidden sm:inline">Redeploy</span>
165
- <span className="sm:hidden">Redeploy</span>
166
- </Button>
167
150
  <DropdownMenu>
168
151
  <DropdownMenuTrigger asChild>
169
152
  <Button
@@ -212,30 +195,9 @@ export const ConnectedRepoDashboard = ({
212
195
 
213
196
  {/* Main Content */}
214
197
  <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
215
- <Tabs
216
- value={activeTab}
217
- onValueChange={setActiveTab}
218
- className="space-y-6"
219
- >
220
- <TabsList className="grid w-full grid-cols-2 lg:w-auto lg:grid-cols-2">
221
- <TabsTrigger value="overview" className="flex items-center gap-2">
222
- <Activity className="w-4 h-4" />
223
- Overview
224
- </TabsTrigger>
225
- <TabsTrigger value="builds" className="flex items-center gap-2">
226
- <List className="w-4 h-4" />
227
- Builds
228
- </TabsTrigger>
229
- </TabsList>
230
-
231
- <TabsContent value="overview" className="space-y-6">
232
- {latestBuild && <ConnectedRepoOverview build={latestBuild} />}
233
- </TabsContent>
234
-
235
- <TabsContent value="builds" className="space-y-6">
236
- <BuildsList pkg={pkg} onSelectBuild={handleSelectBuild} />
237
- </TabsContent>
238
- </Tabs>
198
+ <div className="space-y-6">
199
+ <BuildsList pkg={pkg} />
200
+ </div>
239
201
  </div>
240
202
  </div>
241
203
  </>
@@ -1,55 +1,37 @@
1
1
  export { ConnectedRepoOverview } from "./ConnectedRepoOverview"
2
2
  export { BuildsList } from "./BuildsList"
3
- export { ConnectedRepoDashboard } from "./ConnectedRepoDashboard"
4
-
5
- export const getBuildStatus = (build: PackageBuild) => {
3
+ export { PackageReleasesDashboard } from "./PackageReleasesDashboard"
4
+ import {
5
+ Package,
6
+ PackageBuild,
7
+ PackageRelease,
8
+ } from "fake-snippets-api/lib/db/schema"
9
+ import { Clock, CheckCircle, AlertCircle, Loader2 } from "lucide-react"
10
+ export const getBuildStatus = (build: PackageBuild | null) => {
11
+ if (!build) {
12
+ return { status: "pending", label: "No builds" }
13
+ }
6
14
  if (
7
- build.build_error ||
8
- build.transpilation_error ||
9
- build.circuit_json_build_error
15
+ build?.build_error ||
16
+ build?.transpilation_error ||
17
+ build?.circuit_json_build_error
10
18
  ) {
11
19
  return { status: "error", label: "Failed" }
12
20
  }
13
21
  if (
14
- build.build_in_progress ||
15
- build.transpilation_in_progress ||
16
- build.circuit_json_build_in_progress
22
+ build?.build_in_progress ||
23
+ build?.transpilation_in_progress ||
24
+ build?.circuit_json_build_in_progress
17
25
  ) {
18
26
  return { status: "building", label: "Building" }
19
27
  }
20
- if (build.build_completed_at && build.transpilation_completed_at) {
28
+ if (build?.build_completed_at && build?.transpilation_completed_at) {
21
29
  return { status: "success", label: "Ready" }
22
30
  }
23
31
  return { status: "queued", label: "Queued" }
24
32
  }
25
33
 
26
- export interface PackageBuild {
27
- package_build_id: string
28
- package_release_id: string | null
29
- created_at: string
30
- transpilation_in_progress: boolean
31
- transpilation_started_at: string | null
32
- transpilation_completed_at: string | null
33
- transpilation_logs: any[]
34
- transpilation_error: string | null
35
- circuit_json_build_in_progress: boolean
36
- circuit_json_build_started_at: string | null
37
- circuit_json_build_completed_at: string | null
38
- circuit_json_build_logs: any[]
39
- circuit_json_build_error: string | null
40
- build_in_progress: boolean
41
- build_started_at: string | null
42
- build_completed_at: string | null
43
- build_error: string | null
44
- build_error_last_updated_at: string
45
- preview_url: string | null
46
- build_logs: string | null
47
- branch_name: string | null
48
- commit_message: string | null
49
- commit_author: string | null
50
- }
51
-
52
- export const MOCK_DEPLOYMENTS: PackageBuild[] = [
34
+ export const MOCK_PACKAGE_BUILDS: PackageBuild[] = [
53
35
  {
54
36
  package_build_id: "pb_1a2b3c4d",
55
37
  package_release_id: "pr_5e6f7g8h",
@@ -191,8 +173,6 @@ export const MOCK_DEPLOYMENTS: PackageBuild[] = [
191
173
  },
192
174
  ]
193
175
 
194
- import { Package, PackageRelease } from "fake-snippets-api/lib/db/schema"
195
- import { Clock, CheckCircle, AlertCircle, Loader2 } from "lucide-react"
196
176
  export const StatusIcon = ({ status }: { status: string }) => {
197
177
  switch (status) {
198
178
  case "success":
@@ -207,42 +187,5 @@ export const StatusIcon = ({ status }: { status: string }) => {
207
187
  }
208
188
 
209
189
  export const getLatestBuildForPackage = (pkg: Package): PackageBuild => {
210
- return MOCK_DEPLOYMENTS[0]
211
- }
212
- export const getLatestBuildFromPackageRelease = (
213
- pkg: PackageRelease,
214
- ): PackageBuild => {
215
- return MOCK_DEPLOYMENTS[0]
216
- }
217
- export const getPackageFromBuild = (build: PackageBuild): Package => {
218
- return {
219
- ai_description: "placeholder ai description",
220
- ai_usage_instructions: "placeholder ai usage instructions",
221
- created_at: "2025-08-06T14:37:05.802Z",
222
- creator_account_id: "account-1234",
223
- default_view: "files",
224
- description: "placeholder ai description",
225
- github_repo_full_name: "testuser/pcb-designs",
226
- is_board: false,
227
- is_footprint: false,
228
- is_model: false,
229
- is_package: false,
230
- is_private: false,
231
- is_public: true,
232
- is_snippet: false,
233
- is_source_from_github: false,
234
- is_unlisted: false,
235
- latest_package_release_fs_sha: "md5-425b47ef26be8f0863eca9e63e516dfa",
236
- latest_package_release_id: "package_release_1754491026466",
237
- latest_version: "0.0.1",
238
- license: null,
239
- name: "testuser/untitled-package-0",
240
- owner_github_username: "testuser",
241
- owner_org_id: "org-1234",
242
- package_id: "package_1754491025802",
243
- star_count: 0,
244
- unscoped_name: "untitled-package-0",
245
- updated_at: "2025-08-06T14:37:25.083Z",
246
- website: "",
247
- }
190
+ return MOCK_PACKAGE_BUILDS[0]
248
191
  }
@@ -0,0 +1,87 @@
1
+ import { useQuery } from "react-query"
2
+ import { useAxios } from "./use-axios"
3
+ import { PackageBuild } from "fake-snippets-api/lib/db/schema"
4
+
5
+ interface UsePackageBuildsParams {
6
+ package_id?: string
7
+ package_release_id?: string
8
+ }
9
+
10
+ export const usePackageBuilds = (params: UsePackageBuildsParams | null) => {
11
+ const axios = useAxios()
12
+
13
+ return useQuery<PackageBuild[], Error & { status: number }>(
14
+ ["packageBuilds", params],
15
+ async () => {
16
+ if (!params || (!params.package_id && !params.package_release_id)) {
17
+ throw new Error(
18
+ "Either package_id or package_release_id must be provided",
19
+ )
20
+ }
21
+
22
+ const { data } = await axios.get<{ package_builds: PackageBuild[] }>(
23
+ "/package_builds/list",
24
+ {
25
+ params: {
26
+ ...(params.package_id ? { package_id: params.package_id } : {}),
27
+ ...(params.package_release_id
28
+ ? { package_release_id: params.package_release_id }
29
+ : {}),
30
+ },
31
+ },
32
+ )
33
+
34
+ if (!data.package_builds) {
35
+ return []
36
+ }
37
+
38
+ return data.package_builds
39
+ },
40
+ {
41
+ enabled: Boolean(
42
+ params && (params.package_id || params.package_release_id),
43
+ ),
44
+ retry: false,
45
+ refetchOnWindowFocus: false,
46
+ staleTime: 0,
47
+ },
48
+ )
49
+ }
50
+
51
+ export const usePackageBuildsByPackageId = (packageId: string | null) => {
52
+ return usePackageBuilds(packageId ? { package_id: packageId } : null)
53
+ }
54
+
55
+ export const usePackageBuildsByReleaseId = (releaseId?: string | null) => {
56
+ return usePackageBuilds(releaseId ? { package_release_id: releaseId } : null)
57
+ }
58
+
59
+ export const usePackageBuild = (packageBuildId: string | null) => {
60
+ const axios = useAxios()
61
+
62
+ return useQuery<PackageBuild, Error & { status: number }>(
63
+ ["packageBuild", packageBuildId],
64
+ async () => {
65
+ if (!packageBuildId) {
66
+ throw new Error("package_build_id is required")
67
+ }
68
+
69
+ const { data } = await axios.get("/package_builds/get", {
70
+ params: {
71
+ package_build_id: packageBuildId,
72
+ },
73
+ })
74
+
75
+ if (!data.package_build) {
76
+ throw new Error("Package build not found")
77
+ }
78
+
79
+ return data.package_build
80
+ },
81
+ {
82
+ enabled: Boolean(packageBuildId),
83
+ retry: false,
84
+ refetchOnWindowFocus: false,
85
+ },
86
+ )
87
+ }
@@ -0,0 +1,36 @@
1
+ import {
2
+ usePackageReleaseById,
3
+ usePackageReleaseByNameAndVersion,
4
+ } from "./use-package-release"
5
+ import { isUuid } from "@/lib/utils/isUuid"
6
+
7
+ export const usePackageReleaseByIdOrVersion = (
8
+ releaseIdOrVersion: string | null,
9
+ packageName?: string | null,
10
+ ) => {
11
+ const isReleaseIdUuid = releaseIdOrVersion
12
+ ? isUuid(releaseIdOrVersion)
13
+ : false
14
+
15
+ // If it's a UUID, use the ID-based hook
16
+ const releaseByIdQuery = usePackageReleaseById(
17
+ isReleaseIdUuid ? releaseIdOrVersion : null,
18
+ )
19
+
20
+ // If it's not a UUID and we have a package name, construct the version-based query
21
+ const packageNameWithVersion =
22
+ !isReleaseIdUuid && packageName && releaseIdOrVersion
23
+ ? `${packageName}@${releaseIdOrVersion}`
24
+ : null
25
+
26
+ const releaseByVersionQuery = usePackageReleaseByNameAndVersion(
27
+ packageNameWithVersion,
28
+ )
29
+
30
+ // Return the appropriate query result
31
+ if (isReleaseIdUuid) {
32
+ return releaseByIdQuery
33
+ } else {
34
+ return releaseByVersionQuery
35
+ }
36
+ }
@@ -90,3 +90,35 @@ export const useLatestPackageRelease = (
90
90
 
91
91
  return usePackageRelease(query)
92
92
  }
93
+
94
+ export const usePackageReleasesByPackageId = (packageId: string | null) => {
95
+ const axios = useAxios()
96
+
97
+ return useQuery<PackageRelease[], Error & { status: number }>(
98
+ ["packageReleases", packageId],
99
+ async () => {
100
+ if (!packageId) {
101
+ throw new Error("package_id is required")
102
+ }
103
+
104
+ const { data } = await axios.post<{ package_releases: PackageRelease[] }>(
105
+ "/package_releases/list",
106
+ {
107
+ package_id: packageId,
108
+ },
109
+ )
110
+
111
+ if (!data.package_releases) {
112
+ return []
113
+ }
114
+
115
+ return data.package_releases
116
+ },
117
+ {
118
+ enabled: Boolean(packageId),
119
+ retry: false,
120
+ refetchOnWindowFocus: false,
121
+ staleTime: 0,
122
+ },
123
+ )
124
+ }
@@ -0,0 +1,5 @@
1
+ export function isUuid(value: string): boolean {
2
+ const uuidRegex =
3
+ /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i
4
+ return uuidRegex.test(value) || value.startsWith("package_release_")
5
+ }