@tscircuit/fake-snippets 0.0.3 → 0.0.5

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 (54) hide show
  1. package/.github/workflows/bundle-size-analysis.yml +2 -2
  2. package/bun-tests/fake-snippets-api/routes/package_releases/create.test.ts +131 -0
  3. package/bun-tests/fake-snippets-api/routes/package_releases/get.test.ts +89 -0
  4. package/bun-tests/fake-snippets-api/routes/package_releases/list.test.ts +157 -0
  5. package/bun-tests/fake-snippets-api/routes/package_releases/update.test.ts +189 -0
  6. package/bun-tests/fake-snippets-api/routes/packages/create.test.ts +26 -0
  7. package/bun-tests/fake-snippets-api/routes/packages/delete.test.ts +100 -0
  8. package/bun-tests/fake-snippets-api/routes/packages/get.test.ts +59 -0
  9. package/bun-tests/fake-snippets-api/routes/packages/list.test.ts +167 -0
  10. package/bun.lock +3514 -0
  11. package/dist/bundle.js +741 -108
  12. package/fake-snippets-api/lib/db/db-client.ts +79 -2
  13. package/fake-snippets-api/lib/db/schema.ts +55 -0
  14. package/fake-snippets-api/lib/public-mapping/public-map-package-release.ts +10 -0
  15. package/fake-snippets-api/lib/public-mapping/public-map-package.ts +31 -0
  16. package/fake-snippets-api/lib/with-winter-spec.ts +1 -0
  17. package/fake-snippets-api/routes/api/package_files/download.ts +170 -0
  18. package/fake-snippets-api/routes/api/package_files/get.ts +52 -0
  19. package/fake-snippets-api/routes/api/package_files/list.ts +60 -0
  20. package/fake-snippets-api/routes/api/package_releases/create.ts +83 -0
  21. package/fake-snippets-api/routes/api/package_releases/get.ts +34 -0
  22. package/fake-snippets-api/routes/api/package_releases/list.ts +77 -0
  23. package/fake-snippets-api/routes/api/package_releases/update.ts +96 -0
  24. package/fake-snippets-api/routes/api/packages/create.ts +58 -0
  25. package/fake-snippets-api/routes/api/packages/delete.ts +43 -0
  26. package/fake-snippets-api/routes/api/packages/get.ts +36 -0
  27. package/fake-snippets-api/routes/api/packages/list.ts +56 -0
  28. package/fake-snippets-api/routes/api/snippets/create.ts +5 -3
  29. package/package.json +12 -9
  30. package/playwright-tests/circuit-json-import.spec.ts +133 -0
  31. package/playwright-tests/cmd-click.spec.ts +1 -1
  32. package/playwright-tests/editor-page.spec.ts +1 -1
  33. package/playwright-tests/exampleCircuitJson.ts +498 -0
  34. package/playwright-tests/preview-page.spec.ts +2 -9
  35. package/playwright-tests/snapshots/cmd-click.spec.ts-underlined-imports.png +0 -0
  36. package/playwright-tests/snapshots/editor-page.spec.ts-editor-with-snippet.png +0 -0
  37. package/playwright-tests/snapshots/preview-page.spec.ts-preview-snippet-pcb.png +0 -0
  38. package/src/components/CircuitJsonImportDialog.tsx +186 -0
  39. package/src/components/CodeAndPreview.tsx +60 -2
  40. package/src/components/EditorNav.tsx +23 -2
  41. package/src/components/FootprintDialog.tsx +3 -6
  42. package/src/components/Header2.tsx +7 -0
  43. package/src/components/PrefetchPageLink.tsx +4 -1
  44. package/src/components/SuspenseRunFrame.tsx +16 -0
  45. package/src/components/dialogs/import-snippet-dialog.tsx +12 -8
  46. package/src/hooks/use-debounce.ts +17 -0
  47. package/src/hooks/use-global-store.ts +5 -0
  48. package/src/hooks/use-run-tsx/index.tsx +1 -0
  49. package/src/pages/landing.tsx +2 -2
  50. package/src/pages/preview.tsx +2 -28
  51. package/src/pages/quickstart.tsx +24 -0
  52. package/vite.config.ts +1 -1
  53. package/bun.lockb +0 -0
  54. package/playwright-tests/snapshots/preview-page.spec.ts-preview-snippet-schematic.png +0 -0
@@ -14,6 +14,10 @@ import {
14
14
  Order,
15
15
  OrderFile,
16
16
  AccountSnippet,
17
+ packageReleaseSchema,
18
+ packageSchema,
19
+ Package,
20
+ PackageRelease,
17
21
  } from "./schema.ts"
18
22
  import { combine } from "zustand/middleware"
19
23
  import { seed as seedFn } from "./seed"
@@ -85,17 +89,31 @@ const initializer = combine(databaseSchema.parse({}), (set, get) => ({
85
89
  return newAccount
86
90
  },
87
91
  addSnippet: (
88
- snippet: Omit<z.input<typeof snippetSchema>, "snippet_id">,
92
+ snippet: Omit<
93
+ z.input<typeof snippetSchema>,
94
+ "snippet_id" | "package_release_id"
95
+ >,
89
96
  ): Snippet => {
90
97
  const newSnippetId = `snippet_${get().idCounter + 1}`
98
+ const newPackageRelease = packageReleaseSchema.parse({
99
+ package_release_id: `package_release_${get().idCounter + 1}`,
100
+ package_id: newSnippetId,
101
+ version: "0.0.1",
102
+ is_locked: false,
103
+ is_latest: true,
104
+ created_at: new Date().toISOString(),
105
+ updated_at: new Date().toISOString(),
106
+ })
91
107
  const newSnippet = snippetSchema.parse({
92
108
  ...snippet,
93
109
  snippet_id: newSnippetId,
110
+ package_release_id: newPackageRelease.package_release_id,
94
111
  })
95
112
  set((state) => {
96
113
  return {
97
114
  snippets: [...state.snippets, newSnippet],
98
- idCounter: state.idCounter + 1,
115
+ packageReleases: [...state.packageReleases, newPackageRelease],
116
+ idCounter: state.idCounter + 2,
99
117
  }
100
118
  })
101
119
  return { ...newSnippet, snippet_id: newSnippetId }
@@ -340,4 +358,63 @@ const initializer = combine(databaseSchema.parse({}), (set, get) => ({
340
358
  as.has_starred,
341
359
  )
342
360
  },
361
+ addPackage: (
362
+ _package: Omit<z.input<typeof packageSchema>, "package_id">,
363
+ ): Package => {
364
+ const newPackage = {
365
+ package_id: `package_${Date.now()}`,
366
+ ..._package,
367
+ }
368
+ const packageRelease = packageReleaseSchema.parse({
369
+ package_release_id: `package_release_${Date.now()}`,
370
+ package_id: newPackage.package_id,
371
+ version: "0.0.1",
372
+ is_locked: false,
373
+ is_latest: true,
374
+ created_at: new Date().toISOString(),
375
+ updated_at: new Date().toISOString(),
376
+ })
377
+ newPackage.latest_package_release_id = packageRelease.package_release_id
378
+ set((state) => ({
379
+ packages: [...state.packages, newPackage],
380
+ }))
381
+ return newPackage
382
+ },
383
+ getPackageById: (package_id: string): Package | undefined => {
384
+ const state = get()
385
+ const pkg = state.packages.find((pkg) => pkg.package_id === package_id)
386
+ if (!pkg) return undefined
387
+ return {
388
+ ...pkg,
389
+ }
390
+ },
391
+ getPackageReleaseById: (
392
+ package_release_id: string,
393
+ ): PackageRelease | undefined => {
394
+ const state = get()
395
+ return state.packageReleases.find(
396
+ (pr) => pr.package_release_id === package_release_id,
397
+ )
398
+ },
399
+ addPackageRelease: (
400
+ packageRelease: Omit<PackageRelease, "package_release_id">,
401
+ ): PackageRelease => {
402
+ const newPackageRelease = {
403
+ package_release_id: `package_release_${Date.now()}`,
404
+ ...packageRelease,
405
+ }
406
+ set((state) => ({
407
+ packageReleases: [...state.packageReleases, newPackageRelease],
408
+ }))
409
+ return newPackageRelease
410
+ },
411
+ updatePackageRelease: (packageRelease: PackageRelease): void => {
412
+ set((state) => ({
413
+ packageReleases: state.packageReleases.map((pr) =>
414
+ pr.package_release_id === packageRelease.package_release_id
415
+ ? packageRelease
416
+ : pr,
417
+ ),
418
+ }))
419
+ },
343
420
  }))
@@ -2,6 +2,7 @@ import { z } from "zod"
2
2
 
3
3
  export const snippetSchema = z.object({
4
4
  snippet_id: z.string(),
5
+ package_release_id: z.string(),
5
6
  name: z.string(),
6
7
  unscoped_name: z.string(),
7
8
  owner_name: z.string(),
@@ -99,12 +100,66 @@ export const accountSnippetSchema = z.object({
99
100
  })
100
101
  export type AccountSnippet = z.infer<typeof accountSnippetSchema>
101
102
 
103
+ export const packageReleaseSchema = z.object({
104
+ package_release_id: z.string(),
105
+ package_id: z.string(),
106
+ version: z.string().nullable(),
107
+ is_locked: z.boolean(),
108
+ is_latest: z.boolean(),
109
+ created_at: z.string().datetime(),
110
+ commit_sha: z.string().nullable().optional(),
111
+ license: z.string().nullable().optional(),
112
+ })
113
+ export type PackageRelease = z.infer<typeof packageReleaseSchema>
114
+
115
+ export const packageFileSchema = z.object({
116
+ package_file_id: z.string(),
117
+ package_release_id: z.string(),
118
+ file_path: z.string(),
119
+ content_text: z.string().nullable().optional(),
120
+ created_at: z.string().datetime(),
121
+ })
122
+ export type PackageFile = z.infer<typeof packageFileSchema>
123
+
124
+ export const packageSchema = z.object({
125
+ package_id: z.string(),
126
+ creator_account_id: z.string(),
127
+ latest_package_release_id: z.string().nullable(),
128
+ latest_version: z.string().nullable(),
129
+ license: z.string().nullable(),
130
+ owner_org_id: z.string(),
131
+ owner_github_username: z.string().nullable(),
132
+ is_source_from_github: z.boolean(),
133
+ description: z.string().nullable(),
134
+ name: z.string(),
135
+ unscoped_name: z.string(),
136
+ star_count: z.number(),
137
+ created_at: z.string().datetime(),
138
+ updated_at: z.string().datetime(),
139
+ ai_description: z.string().nullable(),
140
+ })
141
+ export type Package = z.infer<typeof packageSchema>
142
+
143
+ export const errorSchema = z
144
+ .object({
145
+ error_code: z.string(),
146
+ message: z.string(),
147
+ })
148
+ .passthrough()
149
+
150
+ export const errorResponseSchema = z.object({
151
+ error: errorSchema,
152
+ })
153
+
102
154
  export const databaseSchema = z.object({
103
155
  idCounter: z.number().default(0),
104
156
  snippets: z.array(snippetSchema).default([]),
157
+ packageReleases: z.array(packageReleaseSchema).default([]),
158
+ packageFiles: z.array(packageFileSchema).default([]),
105
159
  sessions: z.array(sessionSchema).default([]),
106
160
  loginPages: z.array(loginPageSchema).default([]),
107
161
  accounts: z.array(accountSchema).default([]),
162
+ packages: z.array(packageSchema).default([]),
108
163
  orders: z.array(orderSchema).default([]),
109
164
  orderFiles: z.array(orderFileSchema).default([]),
110
165
  accountSnippets: z.array(accountSnippetSchema).default([]),
@@ -0,0 +1,10 @@
1
+ import * as ZT from "fake-snippets-api/lib/db/schema"
2
+
3
+ export const publicMapPackageRelease = (
4
+ internal_package_release: ZT.PackageRelease,
5
+ ): ZT.PackageRelease => {
6
+ return {
7
+ ...internal_package_release,
8
+ created_at: internal_package_release.created_at,
9
+ }
10
+ }
@@ -0,0 +1,31 @@
1
+ import { z } from "zod"
2
+ import * as ZT from "fake-snippets-api/lib/db/schema"
3
+
4
+ export const publicMapPackage = (internal_package: {
5
+ package_id: string
6
+ creator_account_id: string
7
+ owner_org_id: string
8
+ owner_github_username: string | null
9
+ is_source_from_github: boolean
10
+ description: string | null
11
+ latest_version?: string | null
12
+ latest_license?: string | null
13
+ latest_package_release_id?: string | null
14
+ star_count?: number | null
15
+ name: string
16
+ unscoped_name: string
17
+ updated_at: string
18
+ created_at: string
19
+ ai_description: string | null
20
+ }): ZT.Package => {
21
+ return {
22
+ ...internal_package,
23
+ latest_package_release_id:
24
+ internal_package.latest_package_release_id ?? null,
25
+ latest_version: internal_package.latest_version ?? null,
26
+ license: internal_package.latest_license ?? null,
27
+ star_count: internal_package.star_count ?? 0,
28
+ created_at: internal_package.created_at,
29
+ updated_at: internal_package.updated_at,
30
+ }
31
+ }
@@ -0,0 +1 @@
1
+ export * from "./middleware/with-winter-spec"
@@ -0,0 +1,170 @@
1
+ import { withRouteSpec } from "fake-snippets-api/lib/with-winter-spec"
2
+ import { z } from "zod"
3
+
4
+ export default withRouteSpec({
5
+ methods: ["GET"],
6
+ auth: "none",
7
+ queryParams: z.object({
8
+ jsdelivr_resolve: z
9
+ .enum(["true", "false"])
10
+ .optional()
11
+ .transform((v) => v === "true"),
12
+ jsdelivr_path: z.string(),
13
+ }),
14
+ jsonResponse: z.any(),
15
+ })(async (req, ctx) => {
16
+ const { jsdelivr_path, jsdelivr_resolve } = req.query
17
+
18
+ // Parse the file path
19
+ const [owner, packageWithVersion, ...rest] = jsdelivr_path.split("/")
20
+ if (!packageWithVersion) {
21
+ return ctx.error(400, {
22
+ error_code: "invalid_path",
23
+ message: "Invalid path",
24
+ })
25
+ }
26
+ const [packageName, version] = packageWithVersion.split("@")
27
+ const fileName = rest.join("/")
28
+
29
+ // Find the snippet
30
+ // const snippet =
31
+
32
+ // if (!snippet) {
33
+ // return ctx.error(404, {
34
+ // error_code: "snippet_not_found",
35
+ // message: "Snippet not found",
36
+ // })
37
+ // }
38
+
39
+ // if (!fileName && !jsdelivr_resolve) {
40
+ // return new Response(
41
+ // JSON.stringify({
42
+ // tags: {
43
+ // latest: "0.0.1",
44
+ // },
45
+ // versions: ["0.0.1"],
46
+ // }),
47
+ // {
48
+ // status: 200,
49
+ // headers: { "Content-Type": "application/json" },
50
+ // },
51
+ // )
52
+ // }
53
+
54
+ // if (!fileName && jsdelivr_resolve) {
55
+ // return new Response(
56
+ // JSON.stringify({
57
+ // version: "0.0.1",
58
+ // }),
59
+ // {
60
+ // status: 200,
61
+ // headers: { "Content-Type": "application/json" },
62
+ // },
63
+ // )
64
+ // }
65
+
66
+ // const latestRelease = await ctx.db
67
+ // .selectFrom("main.package_release")
68
+ // .where("package_id", "=", snippet.snippet_id)
69
+ // .where("is_latest", "=", true)
70
+ // .selectAll()
71
+ // .executeTakeFirst()
72
+
73
+ // const dtsFile = await ctx.db
74
+ // .selectFrom("main.package_file")
75
+ // .where("package_release_id", "=", latestRelease!.package_release_id)
76
+ // .where("file_path", "=", "/dist/index.d.ts")
77
+ // .select("content_text")
78
+ // .executeTakeFirst()
79
+
80
+ // // If no fileName is provided, return the directory listing
81
+ // if (!fileName || fileName === "flat") {
82
+ // const files = [
83
+ // {
84
+ // type: "file",
85
+ // name: "index.ts",
86
+ // hash: "placeholder_hash",
87
+ // time: snippet.updated_at,
88
+ // size: snippet.code?.length ?? 0,
89
+ // },
90
+ // {
91
+ // type: "file",
92
+ // name: "index.d.ts",
93
+ // hash: "placeholder_hash",
94
+ // time: snippet.updated_at,
95
+ // size: dtsFile?.content_text?.length ?? 0,
96
+ // },
97
+ // {
98
+ // type: "file",
99
+ // name: "package.json",
100
+ // hash: "placeholder_hash",
101
+ // time: snippet.updated_at,
102
+ // size: JSON.stringify({
103
+ // name: `@tsci/${owner}.${packageName}`,
104
+ // version: version || "0.0.1",
105
+ // main: "index.ts",
106
+ // types: "index.d.ts",
107
+ // }).length,
108
+ // },
109
+ // ]
110
+
111
+ // const response = {
112
+ // default: "/index.ts",
113
+ // files:
114
+ // fileName === "flat"
115
+ // ? files.map((f) => ({
116
+ // name: `/${f.name}`,
117
+ // hash: f.hash,
118
+ // time: f.time,
119
+ // size: f.size,
120
+ // }))
121
+ // : [
122
+ // {
123
+ // type: "directory",
124
+ // name: ".",
125
+ // files: files,
126
+ // },
127
+ // ],
128
+ // }
129
+
130
+ // return new Response(JSON.stringify(response, null, 2), {
131
+ // status: 200,
132
+ // headers: { "Content-Type": "application/json" },
133
+ // })
134
+ // }
135
+
136
+ // // Handle file downloads
137
+ // let content: string
138
+ // switch (fileName) {
139
+ // case "index.ts":
140
+ // content = snippet.code ?? ""
141
+ // break
142
+ // case "index.d.ts":
143
+ // content = dtsFile?.content_text ?? ""
144
+ // break
145
+ // case "package.json":
146
+ // content = JSON.stringify(
147
+ // {
148
+ // name: `@tsci/${owner}.${packageName}`,
149
+ // version: version || "0.0.1",
150
+ // main: "index.ts",
151
+ // types: "index.d.ts",
152
+ // },
153
+ // null,
154
+ // 2,
155
+ // )
156
+ // break
157
+ // default:
158
+ // return ctx.error(404, {
159
+ // error_code: "file_not_found",
160
+ // message: "Requested file not found",
161
+ // })
162
+ // }
163
+
164
+ const content = "TODO"
165
+
166
+ return new Response(content, {
167
+ status: 200,
168
+ headers: { "Content-Type": "text/plain" },
169
+ })
170
+ })
@@ -0,0 +1,52 @@
1
+ import { withRouteSpec } from "fake-snippets-api/lib/with-winter-spec"
2
+ import { z } from "zod"
3
+ import * as ZT from "fake-snippets-api/lib/db/schema"
4
+ import { NotFoundError } from "winterspec/middleware"
5
+
6
+ const routeSpec = {
7
+ methods: ["POST"],
8
+ auth: "none",
9
+ jsonBody: z
10
+ .object({
11
+ package_file_id: z.string().uuid(),
12
+ })
13
+ .or(
14
+ z.object({
15
+ package_release_id: z.string().uuid(),
16
+ file_path: z.string(),
17
+ }),
18
+ )
19
+ .or(
20
+ z.object({
21
+ package_id: z.string().uuid(),
22
+ version: z.string().optional(),
23
+ file_path: z.string(),
24
+ }),
25
+ )
26
+ .or(
27
+ z.object({
28
+ package_name: z.string(),
29
+ version: z.string().optional(),
30
+ file_path: z.string(),
31
+ }),
32
+ )
33
+ .or(
34
+ z.object({
35
+ package_name_with_version: z.string(),
36
+ file_path: z.string(),
37
+ }),
38
+ ),
39
+ jsonResponse: z
40
+ .object({
41
+ ok: z.boolean(),
42
+ package_file: ZT.packageFileSchema.optional(),
43
+ })
44
+ .or(ZT.errorResponseSchema),
45
+ } as const
46
+
47
+ export default withRouteSpec(routeSpec)(async (req, ctx) => {
48
+ return ctx.json({
49
+ ok: true,
50
+ package_file: undefined,
51
+ })
52
+ })
@@ -0,0 +1,60 @@
1
+ import { withRouteSpec } from "fake-snippets-api/lib/with-winter-spec"
2
+ import { z } from "zod"
3
+ import * as ZT from "fake-snippets-api/lib/db/schema"
4
+
5
+ const routeSpec = {
6
+ methods: ["POST"],
7
+ auth: "none",
8
+ jsonBody: z
9
+ .object({
10
+ package_release_id: z.string().uuid(),
11
+ })
12
+ .or(
13
+ z.object({
14
+ package_name: z.string(),
15
+ use_latest_version: z.literal(true),
16
+ }),
17
+ )
18
+ .or(
19
+ z.object({
20
+ package_name_with_version: z.string(),
21
+ }),
22
+ ),
23
+ jsonResponse: z.object({
24
+ ok: z.boolean(),
25
+ package_files: z.array(ZT.packageFileSchema),
26
+ }),
27
+ } as const
28
+
29
+ export default withRouteSpec(routeSpec)(async (req, ctx) => {
30
+ // const package_release_id = await findPackageReleaseId(req.jsonBody, ctx)
31
+ // if (!package_release_id) {
32
+ // return ctx.error(404, {
33
+ // error_code: "package_release_not_found",
34
+ // message: "Package release not found",
35
+ // })
36
+ // }
37
+ // const package_files = await ctx.db
38
+ // .selectFrom("main.package_file")
39
+ // .select([
40
+ // "package_file_id",
41
+ // "package_release_id",
42
+ // "content_mimetype",
43
+ // "file_path",
44
+ // "created_at",
45
+ // ])
46
+ // .where("package_release_id", "=", package_release_id)
47
+ // .where("file_path", "not like", ".tscircuit-internal/%")
48
+ // .execute()
49
+ // return ctx.json({
50
+ // ok: true,
51
+ // package_files: package_files.map((pf) => ({
52
+ // ...pf,
53
+ // created_at: pf.created_at.toISOString(),
54
+ // })),
55
+ // })
56
+ return ctx.json({
57
+ ok: true,
58
+ package_files: [],
59
+ })
60
+ })
@@ -0,0 +1,83 @@
1
+ import { packageReleaseSchema } from "fake-snippets-api/lib/db/schema"
2
+ import { withRouteSpec } from "fake-snippets-api/lib/middleware/with-winter-spec"
3
+ import { publicMapPackageRelease } from "fake-snippets-api/lib/public-mapping/public-map-package-release"
4
+ import { z } from "zod"
5
+
6
+ export default withRouteSpec({
7
+ methods: ["POST"],
8
+ auth: "none",
9
+ jsonBody: z.object({
10
+ package_id: z.string().optional(),
11
+ version: z.string().optional(),
12
+ is_latest: z.boolean().optional(),
13
+ commit_sha: z.string().optional(),
14
+ package_name_with_version: z.string().optional(),
15
+ }),
16
+ jsonResponse: z.object({
17
+ ok: z.boolean(),
18
+ package_release: packageReleaseSchema,
19
+ }),
20
+ })(async (req, ctx) => {
21
+ let {
22
+ package_id,
23
+ is_latest = true,
24
+ version,
25
+ commit_sha,
26
+ package_name_with_version,
27
+ } = req.jsonBody
28
+
29
+ if (package_name_with_version && !version && !package_id) {
30
+ const [packageName, parsedVersion] = package_name_with_version.split("@")
31
+ const pkg = ctx.db.packages.find((p) => p.name === packageName)
32
+
33
+ if (!pkg) {
34
+ return ctx.error(404, {
35
+ error_code: "package_not_found",
36
+ message: `Package not found: ${packageName}`,
37
+ })
38
+ }
39
+
40
+ package_id = pkg.package_id
41
+ version = parsedVersion
42
+ }
43
+
44
+ if (!package_id || !version) {
45
+ return ctx.error(400, {
46
+ error_code: "missing_options",
47
+ message: "package_id and version are required",
48
+ })
49
+ }
50
+
51
+ // Check if version already exists
52
+ const existingRelease = ctx.db.packageReleases.find(
53
+ (pr) => pr.package_id === package_id && pr.version === version,
54
+ )
55
+
56
+ if (existingRelease) {
57
+ return ctx.error(400, {
58
+ error_code: "version_already_exists",
59
+ message: `Version ${version} already exists for this package`,
60
+ })
61
+ }
62
+
63
+ // Update previous latest if needed
64
+ if (is_latest) {
65
+ ctx.db.packageReleases
66
+ .filter((pr) => pr.package_id === package_id && pr.is_latest)
67
+ .forEach((pr) => (pr.is_latest = false))
68
+ }
69
+
70
+ const newPackageRelease = ctx.db.addPackageRelease({
71
+ package_id,
72
+ is_latest,
73
+ version,
74
+ is_locked: false,
75
+ created_at: new Date().toISOString(),
76
+ commit_sha: commit_sha ?? null,
77
+ })
78
+
79
+ return ctx.json({
80
+ ok: true,
81
+ package_release: publicMapPackageRelease(newPackageRelease),
82
+ })
83
+ })
@@ -0,0 +1,34 @@
1
+ import * as zt from "fake-snippets-api/lib/db/schema"
2
+ import { publicMapPackageRelease } from "fake-snippets-api/lib/public-mapping/public-map-package-release"
3
+ import { withRouteSpec } from "fake-snippets-api/lib/with-winter-spec"
4
+ import { z } from "zod"
5
+
6
+ export default withRouteSpec({
7
+ methods: ["POST"],
8
+ auth: "none",
9
+ jsonBody: z.object({
10
+ package_release_id: z.string().optional(),
11
+ package_name_with_version: z.string().optional(),
12
+ }),
13
+ jsonResponse: z.object({
14
+ ok: z.boolean(),
15
+ package_release: zt.packageReleaseSchema,
16
+ }),
17
+ })(async (req, ctx) => {
18
+ const { package_release_id } = req.jsonBody
19
+
20
+ const foundRelease =
21
+ package_release_id && ctx.db.getPackageReleaseById(package_release_id)
22
+
23
+ if (!foundRelease) {
24
+ return ctx.error(404, {
25
+ error_code: "package_release_not_found",
26
+ message: "Package release not found",
27
+ })
28
+ }
29
+
30
+ return ctx.json({
31
+ ok: true,
32
+ package_release: publicMapPackageRelease(foundRelease),
33
+ })
34
+ })