@tscircuit/cli 0.0.116 → 0.0.119

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 (58) hide show
  1. package/.github/workflows/release.yml +1 -1
  2. package/.github/workflows/server-tests.yml +31 -0
  3. package/.github/workflows/test.yml +4 -1
  4. package/README.md +1 -2
  5. package/bun.lockb +0 -0
  6. package/dev-server-api/bun.lockb +0 -0
  7. package/dev-server-api/package.json +2 -4
  8. package/dev-server-api/routes/api/db/download.ts +25 -0
  9. package/dev-server-api/routes/api/dev_package_examples/create.ts +15 -25
  10. package/dev-server-api/routes/api/dev_package_examples/get.ts +12 -21
  11. package/dev-server-api/routes/api/dev_package_examples/list.ts +17 -22
  12. package/dev-server-api/routes/api/dev_package_examples/update.ts +39 -37
  13. package/dev-server-api/routes/api/dev_server/reset.ts +6 -8
  14. package/dev-server-api/routes/api/export_files/create.ts +10 -13
  15. package/dev-server-api/routes/api/export_files/download.ts +10 -7
  16. package/dev-server-api/routes/api/export_requests/create.ts +11 -15
  17. package/dev-server-api/routes/api/export_requests/get.ts +24 -15
  18. package/dev-server-api/routes/api/export_requests/list.ts +9 -10
  19. package/dev-server-api/routes/api/export_requests/update.ts +18 -18
  20. package/dev-server-api/routes/api/package_info/create.ts +9 -11
  21. package/dev-server-api/routes/api/package_info/get.ts +5 -8
  22. package/dev-server-api/routes/index.ts +16 -0
  23. package/dev-server-api/server.ts +1 -1
  24. package/dev-server-api/src/db/get-db.ts +11 -102
  25. package/dev-server-api/src/db/schema.ts +65 -0
  26. package/dev-server-api/src/db/zod-level-db.ts +146 -0
  27. package/dev-server-api/src/middlewares/with-db.ts +7 -3
  28. package/dev-server-api/static-routes.ts +3 -1
  29. package/dev-server-api/tests/fixtures/get-test-server.ts +30 -0
  30. package/dev-server-api/tests/fixtures/start-server.ts +20 -0
  31. package/dev-server-api/tests/routes/dev_package_examples/create.test.ts +19 -0
  32. package/dev-server-api/tests/routes/dev_package_examples/get.test.ts +25 -0
  33. package/dev-server-api/tests/routes/dev_package_examples/list.test.ts +32 -0
  34. package/dev-server-api/tests/routes/dev_package_examples/update.test.ts +28 -0
  35. package/dev-server-api/tests/routes/export_files/create.test.ts +18 -0
  36. package/dev-server-api/tests/routes/export_files/download.test.ts +29 -0
  37. package/dev-server-api/tests/routes/export_requests/create.test.ts +24 -0
  38. package/dev-server-api/tests/routes/export_requests/get.test.ts +41 -0
  39. package/dev-server-api/tests/routes/export_requests/list.test.ts +35 -0
  40. package/dev-server-api/tests/routes/export_requests/update.test.ts +50 -0
  41. package/dev-server-api/tests/routes/health.test.ts +10 -0
  42. package/dist/cli.js +487 -368
  43. package/lib/cmd-fns/dev/index.ts +14 -11
  44. package/package.json +6 -8
  45. package/tests/open.test.ts +1 -2
  46. package/tests/soupify.test.ts +1 -1
  47. package/dev-server-api/src/lib/zod/export_file.ts +0 -8
  48. package/dev-server-api/src/lib/zod/export_package_info.ts +0 -5
  49. package/dev-server-api/src/lib/zod/export_request.ts +0 -21
  50. /package/{tests/assets/example-project → example-project}/README.md +0 -0
  51. /package/{tests/assets/example-project → example-project}/examples/basic-bug.tsx +0 -0
  52. /package/{tests/assets/example-project → example-project}/examples/basic-capacitor.tsx +0 -0
  53. /package/{tests/assets/example-project → example-project}/examples/basic-resistor.tsx +0 -0
  54. /package/{tests/assets/example-project → example-project}/index.ts +0 -0
  55. /package/{tests/assets/example-project → example-project}/package-lock.json +0 -0
  56. /package/{tests/assets/example-project → example-project}/package.json +0 -0
  57. /package/{tests/assets/example-project → example-project}/src/MyCircuit.tsx +0 -0
  58. /package/{tests/assets/example-project → example-project}/src/manual-edits.ts +0 -0
@@ -4,25 +4,23 @@ import { z } from "zod"
4
4
  export default withWinterSpec({
5
5
  methods: ["POST"],
6
6
  jsonBody: z.object({
7
- package_name: z.string()
7
+ package_name: z.string(),
8
8
  }),
9
9
  jsonResponse: z.object({
10
10
  package_info: z.object({
11
- name: z.string()
12
- })
11
+ name: z.string(),
12
+ }),
13
13
  }),
14
14
  auth: "none",
15
15
  })(async (req, ctx) => {
16
16
  const package_name = req.jsonBody.package_name
17
- const package_info = await ctx.db
18
- .insertInto("package_info")
19
- .values({
20
- name: package_name
21
- })
22
- .returningAll()
23
- .executeTakeFirstOrThrow()
17
+
18
+ const package_info = await ctx.db.put("package_info", {
19
+ package_info_id: 1,
20
+ name: package_name,
21
+ })
24
22
 
25
23
  return ctx.json({
26
- package_info
24
+ package_info,
27
25
  })
28
26
  })
@@ -1,18 +1,15 @@
1
- import { export_package_info } from "src/lib/zod/export_package_info"
1
+ import { PackageInfoSchema } from "src/db/schema"
2
2
  import { withWinterSpec } from "src/with-winter-spec"
3
3
  import { z } from "zod"
4
4
 
5
5
  export default withWinterSpec({
6
6
  methods: ["GET"],
7
7
  jsonResponse: z.object({
8
- package_info: export_package_info
8
+ package_info: PackageInfoSchema,
9
9
  }),
10
10
  auth: "none",
11
11
  })(async (req, ctx) => {
12
- const package_info = await ctx.db
13
- .selectFrom("package_info")
14
- .select("name")
15
- .executeTakeFirstOrThrow()
12
+ const package_info = await ctx.db.get("package_info", 1)
16
13
 
17
- return ctx.json({ package_info })
18
- })
14
+ return ctx.json({ package_info: package_info! })
15
+ })
@@ -0,0 +1,16 @@
1
+ import { withWinterSpec } from "../src/with-winter-spec"
2
+ import { z } from "zod"
3
+
4
+ export default withWinterSpec({
5
+ methods: ["GET"],
6
+ auth: "none",
7
+ })(async (req, ctx) => {
8
+ return new Response(
9
+ `<html><body>This is the dev server API <a href="/api/db/download">view database</a></body></html>`,
10
+ {
11
+ headers: {
12
+ "content-type": "text/html",
13
+ },
14
+ }
15
+ )
16
+ })
@@ -6,7 +6,7 @@ const serverFetch = await createFetchHandlerFromDir(
6
6
  join(import.meta.dir, "./routes")
7
7
  )
8
8
 
9
- console.log("starting dev-server-api on localhost:3021")
9
+ console.log("starting dev-server-api on http://localhost:3021")
10
10
  Bun.serve({
11
11
  fetch: (bunReq) => {
12
12
  const req = new EdgeRuntimeRequest(bunReq.url, {
@@ -1,117 +1,26 @@
1
1
  import { mkdirSync } from "fs"
2
- import { Kysely, SqliteDialect, sql, type Generated } from "kysely"
3
2
  import * as Path from "path"
4
- import { createSchema } from "./create-schema"
3
+ import { ZodLevelDatabase } from "./zod-level-db"
5
4
 
6
- export interface PackageInfo {
7
- name: string
8
- }
9
-
10
- export interface DevPackageExample {
11
- dev_package_example_id: Generated<number>
12
- tscircuit_soup: any
13
- completed_edit_events: any
14
- file_path: string
15
- export_name: string
16
- error: string | null
17
- is_loading: 1 | 0
18
- last_updated_at: string
19
- soup_last_updated_at: string
20
- edit_events_last_updated_at: string
21
- edit_events_last_applied_at: string
22
- }
23
-
24
- export interface ExportRequest {
25
- export_request_id: Generated<number>
26
- example_file_path: string
27
- export_parameters: string
28
- export_name: string
29
- is_complete: 1 | 0
30
- has_error: 1 | 0
31
- error?: string
32
- created_at: string
33
- }
34
-
35
- export interface ExportFile {
36
- export_file_id: Generated<number>
37
- file_name: string
38
- file_content: Buffer
39
- export_request_id: number
40
- created_at: string
41
- }
42
-
43
- interface KyselyDatabaseSchema {
44
- dev_package_example: DevPackageExample
45
- export_request: ExportRequest
46
- export_file: ExportFile
47
- package_info: PackageInfo
48
- }
49
-
50
- export type DbClient = Kysely<KyselyDatabaseSchema>
51
-
52
- // let globalDb: Database | undefined
53
-
54
- let globalDb: Kysely<KyselyDatabaseSchema> | undefined
5
+ let globalDb: ZodLevelDatabase | undefined
55
6
 
56
7
  export const getDbFilePath = () =>
57
- process.env.TSCI_DEV_SERVER_DB ?? "./.tscircuit/dev-server.sqlite"
8
+ process.env.TSCI_DEV_SERVER_DB ?? "./.tscircuit/devdb"
58
9
 
59
- export const getDb = async (): Promise<Kysely<KyselyDatabaseSchema>> => {
60
- if (globalDb) return globalDb
61
-
62
- const devServerDbPath = getDbFilePath()
63
-
64
- mkdirSync(Path.dirname(devServerDbPath), { recursive: true })
65
-
66
- // better-sqlite3 doesn't work in bun, so if we see we can use the bun
67
- // alternative, attempt to use that instead
68
- let dialect: any
69
-
70
- if (typeof Bun !== "undefined") {
71
- // console.log("Attempting to use bun-sqlite")
72
- try {
73
- const { BunSqliteDialect } = await import("kysely-bun-sqlite")
74
- const { Database } = await import("bun:sqlite")
75
- dialect = new BunSqliteDialect({
76
- database: new Database(devServerDbPath, {
77
- create: true,
78
- }),
79
- })
80
- } catch (e) { }
81
- }
82
-
83
- if (!dialect) {
84
- // console.log("Attempting to use better-sqlite3")
85
- try {
86
- const BetterSqlite3 = await import("better-sqlite3")
87
- dialect = new SqliteDialect({
88
- database: new BetterSqlite3.default(devServerDbPath),
89
- })
90
- } catch (e) { }
91
- }
92
-
93
- if (!dialect) {
94
- throw new Error("Was not able to load sqlite dialect")
10
+ export const getDb = async (): Promise<ZodLevelDatabase> => {
11
+ if (globalDb) {
12
+ return globalDb
95
13
  }
96
14
 
97
- const db = new Kysely<KyselyDatabaseSchema>({
98
- dialect,
99
- })
15
+ const devServerDbPath = getDbFilePath()
100
16
 
101
- await sql`pragma busy_timeout = 5000`.execute(db)
17
+ mkdirSync(devServerDbPath, { recursive: true })
102
18
 
103
- const schemaExistsResult = await sql`
104
- SELECT name
105
- FROM sqlite_master
106
- WHERE type='table' AND name IN ('dev_package_example', 'export_request', 'export_file', 'package_info')
107
- `.execute(db)
19
+ const db = new ZodLevelDatabase(devServerDbPath)
108
20
 
109
- // Check if the number of existing tables matches the number of required tables
110
- if (schemaExistsResult.rows.length < 4) {
111
- await createSchema(db)
112
- }
21
+ db.open()
113
22
 
114
23
  globalDb = db
115
24
 
116
25
  return db
117
- }
26
+ }
@@ -0,0 +1,65 @@
1
+ import { z } from "zod"
2
+
3
+ // Helper function for nullable fields
4
+ const nullableText = () => z.string().nullable().default(null)
5
+ const id = () => z.any().pipe(z.number().int())
6
+
7
+ // PackageInfo schema
8
+ export const PackageInfoSchema = z.object({
9
+ package_info_id: id(),
10
+ name: z.string(),
11
+ })
12
+
13
+ // DevPackageExample schema
14
+ export const DevPackageExampleSchema = z.object({
15
+ dev_package_example_id: id(),
16
+ file_path: z.string(),
17
+ export_name: nullableText(),
18
+ tscircuit_soup: z.any().nullable(), // Using any for JSON type
19
+ completed_edit_events: z.array(z.any()).default([]), // Using any for JSON type
20
+ error: nullableText(),
21
+ is_loading: z.boolean(),
22
+ soup_last_updated_at: nullableText(),
23
+ edit_events_last_updated_at: nullableText(),
24
+ edit_events_last_applied_at: nullableText(),
25
+ last_updated_at: nullableText(),
26
+ })
27
+
28
+ // ExportRequest schema
29
+ export const ExportRequestSchema = z.object({
30
+ export_request_id: id(),
31
+ example_file_path: nullableText(),
32
+ export_parameters: z.any().nullable(), // Using any for JSON type
33
+ export_name: nullableText(),
34
+ is_complete: z.boolean(),
35
+ has_error: z.boolean(),
36
+ error: nullableText(),
37
+ created_at: nullableText(),
38
+ })
39
+
40
+ // ExportFile schema
41
+ export const ExportFileSchema = z.object({
42
+ export_file_id: id(),
43
+ file_name: nullableText(),
44
+ file_content_base64: z.string().nullable(),
45
+ export_request_id: z.number().int().nullable(),
46
+ created_at: nullableText(),
47
+ })
48
+
49
+ // Combined DBSchema
50
+ export const DBSchema = z.object({
51
+ package_info: PackageInfoSchema,
52
+ dev_package_example: DevPackageExampleSchema,
53
+ export_request: ExportRequestSchema,
54
+ export_file: ExportFileSchema,
55
+ })
56
+
57
+ // TypeScript type inference
58
+ export type DBSchemaType = z.infer<typeof DBSchema>
59
+ export type DBInputSchemaType = z.input<typeof DBSchema>
60
+
61
+ // You can also export individual types if needed
62
+ export type PackageInfo = z.infer<typeof PackageInfoSchema>
63
+ export type DevPackageExample = z.infer<typeof DevPackageExampleSchema>
64
+ export type ExportRequest = z.infer<typeof ExportRequestSchema>
65
+ export type ExportFile = z.infer<typeof ExportFileSchema>
@@ -0,0 +1,146 @@
1
+ import { Level } from "level"
2
+ import { z } from "zod"
3
+ import { DBSchema, type DBSchemaType, type DBInputSchemaType } from "./schema"
4
+
5
+ // Create a wrapper class for Level with Zod validation
6
+ export class ZodLevelDatabase {
7
+ private db: Level<string, any>
8
+
9
+ constructor(location: string) {
10
+ this.db = new Level(location)
11
+ }
12
+
13
+ async open() {
14
+ return this.db.open()
15
+ }
16
+
17
+ async close() {
18
+ return this.db.close()
19
+ }
20
+
21
+ async get<K extends keyof DBSchemaType>(
22
+ collection: K,
23
+ id: string | number
24
+ ): Promise<DBSchemaType[K] | null> {
25
+ const key = `${collection}:${id}`
26
+ const data = await this.db.get(key)
27
+ return DBSchema.shape[collection].parse(JSON.parse(data)) as any
28
+ }
29
+
30
+ async put<K extends keyof DBSchemaType>(
31
+ collection: K,
32
+ value: DBInputSchemaType[K]
33
+ ): Promise<DBSchemaType[K]> {
34
+ const idkey = `${collection}_id`
35
+ const valueLoose: any = value
36
+ if (!valueLoose[idkey]) {
37
+ // generate an id using the "count" key
38
+ let count = await this.db
39
+ .get(`${collection}.count`, { valueEncoding: "json" })
40
+ .catch(() => 1)
41
+ ;(value as any)[idkey] = count
42
+ await this.db.put(`${collection}.count`, count + 1)
43
+ }
44
+ const key = `${collection}:${valueLoose[idkey]}`
45
+ const validatedData = DBSchema.shape[collection].parse(value)
46
+ await this.db.put(key, JSON.stringify(validatedData))
47
+ return validatedData as DBSchemaType[K]
48
+ }
49
+
50
+ async del<K extends keyof DBSchemaType>(
51
+ collection: K,
52
+ id: string
53
+ ): Promise<void> {
54
+ const key = `${collection}:${id}`
55
+ await this.db.del(key)
56
+ }
57
+
58
+ async find<K extends keyof DBSchemaType>(
59
+ collection: K,
60
+ partialObject: Partial<DBSchemaType[K]>
61
+ ): Promise<DBSchemaType[K] | null> {
62
+ const schema = DBSchema.shape[collection]
63
+
64
+ for await (const [key, value] of this.db.iterator({
65
+ gte: `${collection}:`,
66
+ lte: `${collection}:\uffff`,
67
+ })) {
68
+ try {
69
+ const parsedValue = schema.parse(JSON.parse(value))
70
+ if (this.matchesPartialObject(parsedValue, partialObject)) {
71
+ return parsedValue as any
72
+ }
73
+ } catch (error) {
74
+ console.error(`Error parsing value for key ${key}:`, error)
75
+ }
76
+ }
77
+
78
+ return null
79
+ }
80
+
81
+ async findOrThrow<K extends keyof DBSchemaType>(
82
+ collection: K,
83
+ partialObject: Partial<DBSchemaType[K]>
84
+ ): Promise<DBSchemaType[K]> {
85
+ const result = await this.find(collection, partialObject)
86
+ if (!result) {
87
+ throw new Error(
88
+ `No record in "${collection}" matches query ${JSON.stringify(
89
+ partialObject
90
+ )}`
91
+ )
92
+ }
93
+ return result
94
+ }
95
+
96
+ private matchesPartialObject<T>(
97
+ fullObject: T,
98
+ partialObject: Partial<T>
99
+ ): boolean {
100
+ for (const [key, value] of Object.entries(partialObject)) {
101
+ if (fullObject[key as keyof T] !== value) {
102
+ return false
103
+ }
104
+ }
105
+ return true
106
+ }
107
+
108
+ async dump(): Promise<DBSchemaType> {
109
+ // Serialize all data in the database
110
+ const dump: any = {}
111
+ for await (const [key, value] of this.db.iterator({})) {
112
+ const [collection, id] = key.split(":")
113
+ if (!dump[collection]) {
114
+ dump[collection] = {}
115
+ }
116
+ dump[collection][id] = JSON.parse(value)
117
+ }
118
+ return dump
119
+ }
120
+
121
+ async list<K extends keyof DBSchemaType>(
122
+ collection: K
123
+ ): Promise<DBSchemaType[K][]> {
124
+ const schema = DBSchema.shape[collection]
125
+ const results: DBSchemaType[K][] = []
126
+
127
+ for await (const [key, value] of this.db.iterator({
128
+ gte: `${collection}:`,
129
+ lte: `${collection}:\uffff`,
130
+ })) {
131
+ if (key.endsWith(".count")) continue
132
+ try {
133
+ const parsedValue = schema.parse(JSON.parse(value))
134
+ results.push(parsedValue as DBSchemaType[K])
135
+ } catch (error) {
136
+ console.error(`Error parsing value for key ${key}:`, error)
137
+ }
138
+ }
139
+
140
+ return results
141
+ }
142
+
143
+ async clear() {
144
+ return this.db.clear()
145
+ }
146
+ }
@@ -1,14 +1,18 @@
1
1
  import type { Middleware } from "winterspec"
2
- import { getDb, type DbClient } from "../db/get-db"
2
+ import { getDb } from "../db/get-db"
3
+ import type { ZodLevelDatabase } from "src/db/zod-level-db"
3
4
 
4
5
  export const withDb: Middleware<
5
6
  {},
6
7
  {
7
- db: DbClient
8
+ db: ZodLevelDatabase
8
9
  }
9
10
  > = async (req, ctx, next) => {
10
11
  if (!ctx.db) {
11
12
  ctx.db = await getDb()
12
13
  }
13
- return next(req, ctx)
14
+ // await ctx.db.open()
15
+ const res = await next(req, ctx)
16
+ // await ctx.db.close()
17
+ return res
14
18
  }
@@ -2,6 +2,7 @@
2
2
  // import { WinterSpecRouteMap } from "@winterspec/types"
3
3
 
4
4
  const routeMap = {
5
+ "/api/db/download": (await import('routes/api/db/download.ts')).default,
5
6
  "/api/dev_package_examples/create": (await import('routes/api/dev_package_examples/create.ts')).default,
6
7
  "/api/dev_package_examples/get": (await import('routes/api/dev_package_examples/get.ts')).default,
7
8
  "/api/dev_package_examples/list": (await import('routes/api/dev_package_examples/list.ts')).default,
@@ -16,7 +17,8 @@ const routeMap = {
16
17
  "/api/health": (await import('routes/api/health.ts')).default,
17
18
  "/api/package_info/create": (await import('routes/api/package_info/create.ts')).default,
18
19
  "/api/package_info/get": (await import('routes/api/package_info/get.ts')).default,
19
- "/health": (await import('routes/health.ts')).default
20
+ "/health": (await import('routes/health.ts')).default,
21
+ "/": (await import('routes/index.ts')).default
20
22
  }
21
23
 
22
24
  export default routeMap
@@ -0,0 +1,30 @@
1
+ import { afterEach } from "bun:test"
2
+ import defaultAxios from "redaxios"
3
+ import { startServer } from "./start-server"
4
+ import { tmpdir } from "node:os"
5
+
6
+ interface TestFixture {
7
+ url: string
8
+ server: any
9
+ axios: typeof defaultAxios
10
+ }
11
+
12
+ export const getTestFixture = async (): Promise<TestFixture> => {
13
+ process.env.TSCI_DEV_SERVER_DB = tmpdir() + `/${Math.random()}` + "/devdb"
14
+ const port = 3001 + Math.floor(Math.random() * 999)
15
+ const server = startServer({ port })
16
+ const url = `http://localhost:${port}`
17
+ const axios = defaultAxios.create({
18
+ baseURL: url,
19
+ })
20
+
21
+ afterEach(() => {
22
+ server.stop()
23
+ })
24
+
25
+ return {
26
+ url,
27
+ server,
28
+ axios,
29
+ }
30
+ }
@@ -0,0 +1,20 @@
1
+ import { createFetchHandlerFromDir } from "winterspec/adapters/node"
2
+ import { Request as EdgeRuntimeRequest } from "@edge-runtime/primitives"
3
+ import { join } from "node:path"
4
+
5
+ const serverFetch = await createFetchHandlerFromDir(
6
+ join(import.meta.dir, "../../routes")
7
+ )
8
+
9
+ export const startServer = ({ port }: { port: number }) =>
10
+ Bun.serve({
11
+ fetch: (bunReq) => {
12
+ const req = new EdgeRuntimeRequest(bunReq.url, {
13
+ headers: bunReq.headers,
14
+ method: bunReq.method,
15
+ body: bunReq.body,
16
+ })
17
+ return serverFetch(req as any)
18
+ },
19
+ port,
20
+ })
@@ -0,0 +1,19 @@
1
+ import { it, expect } from "bun:test"
2
+ import { getTestFixture } from "tests/fixtures/get-test-server"
3
+
4
+ it("POST /api/dev_package_examples/create", async () => {
5
+ const { axios } = await getTestFixture()
6
+
7
+ const res = await axios
8
+ .post("/api/dev_package_examples/create", {
9
+ file_path: "examples/basic-resistor.tsx",
10
+ export_name: "default",
11
+ tscircuit_soup: [],
12
+ is_loading: true,
13
+ })
14
+ .then((r) => r.data)
15
+
16
+ expect(res.dev_package_example.file_path).toEqual(
17
+ "examples/basic-resistor.tsx"
18
+ )
19
+ })
@@ -0,0 +1,25 @@
1
+ import { it, expect } from "bun:test"
2
+ import { getTestFixture } from "tests/fixtures/get-test-server"
3
+
4
+ it("POST /api/dev_package_examples/create", async () => {
5
+ const { axios } = await getTestFixture()
6
+
7
+ await axios
8
+ .post("/api/dev_package_examples/create", {
9
+ file_path: "examples/basic-resistor.tsx",
10
+ export_name: "default",
11
+ tscircuit_soup: [],
12
+ is_loading: true,
13
+ })
14
+ .then((r) => r.data)
15
+
16
+ const res = await axios
17
+ .post("/api/dev_package_examples/get", {
18
+ dev_package_example_id: 1,
19
+ })
20
+ .then((r) => r.data)
21
+
22
+ expect(res.dev_package_example.file_path).toEqual(
23
+ "examples/basic-resistor.tsx"
24
+ )
25
+ })
@@ -0,0 +1,32 @@
1
+ import { it, expect } from "bun:test"
2
+ import { getTestFixture } from "tests/fixtures/get-test-server"
3
+
4
+ it("GET /api/dev_package_examples/list", async () => {
5
+ const { axios } = await getTestFixture()
6
+
7
+ // First, create a dev package example
8
+ await axios.post("/api/dev_package_examples/create", {
9
+ file_path: "examples/test-example.tsx",
10
+ export_name: "default",
11
+ tscircuit_soup: [],
12
+ is_loading: false,
13
+ })
14
+
15
+ // Then, list all dev package examples
16
+ const res = await axios
17
+ .post("/api/dev_package_examples/list")
18
+ .then((r) => r.data)
19
+
20
+ expect(res.dev_package_examples).toBeDefined()
21
+ expect(Array.isArray(res.dev_package_examples)).toBe(true)
22
+ expect(res.dev_package_examples.length).toBeGreaterThan(0)
23
+
24
+ const example = res.dev_package_examples.find(
25
+ (e: any) => e.file_path === "examples/test-example.tsx"
26
+ )
27
+ expect(example).toBeDefined()
28
+ expect(example.export_name).toBe("default")
29
+ expect(example.is_loading).toBe(false)
30
+ expect(example.dev_package_example_id).toBeDefined()
31
+ expect(example.last_updated_at).toBeDefined()
32
+ })
@@ -0,0 +1,28 @@
1
+ import { it, expect } from "bun:test"
2
+ import { getTestFixture } from "tests/fixtures/get-test-server"
3
+
4
+ it("POST /api/dev_package_examples/update", async () => {
5
+ const { axios } = await getTestFixture()
6
+
7
+ await axios
8
+ .post("/api/dev_package_examples/create", {
9
+ file_path: "examples/basic-resistor.tsx",
10
+ export_name: "default",
11
+ tscircuit_soup: [],
12
+ is_loading: true,
13
+ })
14
+ .then((r) => r.data)
15
+
16
+ const res = await axios
17
+ .post("/api/dev_package_examples/update", {
18
+ dev_package_example_id: 1,
19
+ completed_edit_events: [],
20
+ edit_events_last_applied_at: "2023-01-01T00:00:00.000Z",
21
+ })
22
+ .then((r) => r.data)
23
+
24
+ expect(res.dev_package_example.completed_edit_events).toEqual([])
25
+ expect(res.dev_package_example.edit_events_last_applied_at).toEqual(
26
+ "2023-01-01T00:00:00.000Z"
27
+ )
28
+ })
@@ -0,0 +1,18 @@
1
+ import { it, expect } from "bun:test"
2
+ import { getTestFixture } from "tests/fixtures/get-test-server"
3
+
4
+ it("POST /api/export_files/create", async () => {
5
+ const { axios } = await getTestFixture()
6
+
7
+ const res = await axios
8
+ .post("/api/export_files/create", {
9
+ export_request_id: 1,
10
+ file_name: "test.png",
11
+ file_content_base64:
12
+ "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==",
13
+ })
14
+ .then((r) => r.data)
15
+
16
+ expect(res.export_file.export_request_id).toEqual(1)
17
+ expect(res.export_file.file_name).toEqual("test.png")
18
+ })
@@ -0,0 +1,29 @@
1
+ import { it, expect } from "bun:test"
2
+ import { getTestFixture } from "tests/fixtures/get-test-server"
3
+
4
+ it("GET /api/export_files/download", async () => {
5
+ const { axios } = await getTestFixture()
6
+
7
+ const exampleBase64 = Buffer.from("example").toString("base64")
8
+
9
+ const res = await axios
10
+ .post("/api/export_files/create", {
11
+ export_request_id: 1,
12
+ file_name: "test.png",
13
+ file_content_base64: exampleBase64,
14
+ })
15
+ .then((r) => r.data)
16
+
17
+ const downloadRes = await axios
18
+ .get(
19
+ `/api/export_files/download?export_file_id=${res.export_file.export_file_id}`
20
+ )
21
+ .then((r) => r.data)
22
+
23
+ // Convert downloadRes to base64 string
24
+ const downloadResBase64 = Buffer.from(downloadRes, "binary").toString(
25
+ "base64"
26
+ )
27
+
28
+ expect(downloadResBase64).toEqual(exampleBase64)
29
+ })