Skip to content
This repository was archived by the owner on Jan 2, 2025. It is now read-only.

Commit 1e32338

Browse files
authored
Introduce level to start migratin from sqlite (#68)
* introduce new level schema and handler to migrate off sqlite * add level as dependency
1 parent fd44483 commit 1e32338

File tree

4 files changed

+163
-0
lines changed

4 files changed

+163
-0
lines changed

dev-server-api/bun.lockb

5.1 KB
Binary file not shown.

dev-server-api/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"better-sqlite3": "^11.0.0",
2020
"kysely": "^0.27.3",
2121
"kysely-bun-sqlite": "^0.3.2",
22+
"level": "^8.0.1",
2223
"redaxios": "^0.5.1",
2324
"winterspec": "0.0.81",
2425
"zod": "^3.22.4"

dev-server-api/src/db/level-db.ts

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import { Level } from "level"
2+
import { z } from "zod"
3+
import { DBSchema, type DBSchemaType } 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 get<K extends keyof DBSchemaType>(
14+
collection: K,
15+
id: string
16+
): Promise<DBSchemaType[K]> {
17+
const key = `${collection}:${id}`
18+
const data = await this.db.get(key)
19+
return DBSchema.shape[collection].parse(JSON.parse(data)) as any
20+
}
21+
22+
async put<K extends keyof DBSchemaType>(
23+
collection: K,
24+
value: DBSchemaType[K]
25+
): Promise<void> {
26+
const idkey = `${collection}_id`
27+
const valueLoose: any = value
28+
if (!valueLoose[idkey]) {
29+
// generate an id using the "count" key
30+
let count = await this.db.get(`${collection}:count`)
31+
if (!count) count = 1
32+
;(value as any)[idkey] = count
33+
await this.db.put(`${collection}:count`, count + 1)
34+
}
35+
const key = `${collection}:${valueLoose[idkey]}`
36+
const validatedData = DBSchema.shape[collection].parse(value)
37+
await this.db.put(key, JSON.stringify(validatedData))
38+
}
39+
40+
async del<K extends keyof DBSchemaType>(
41+
collection: K,
42+
id: string
43+
): Promise<void> {
44+
const key = `${collection}:${id}`
45+
await this.db.del(key)
46+
}
47+
48+
async find<K extends keyof DBSchemaType>(
49+
collection: K,
50+
partialObject: Partial<DBSchemaType[K]>
51+
): Promise<DBSchemaType[K] | null> {
52+
const results: DBSchemaType[K][] = []
53+
const schema = DBSchema.shape[collection]
54+
55+
for await (const [key, value] of this.db.iterator({
56+
gte: `${collection}:`,
57+
lte: `${collection}:\uffff`,
58+
})) {
59+
try {
60+
const parsedValue = schema.parse(JSON.parse(value))
61+
if (this.matchesPartialObject(parsedValue, partialObject)) {
62+
return parsedValue as any
63+
}
64+
} catch (error) {
65+
console.error(`Error parsing value for key ${key}:`, error)
66+
}
67+
}
68+
69+
return null
70+
}
71+
72+
async findOrThrow<K extends keyof DBSchemaType>(
73+
collection: K,
74+
partialObject: Partial<DBSchemaType[K]>
75+
): Promise<DBSchemaType[K]> {
76+
const result = await this.find(collection, partialObject)
77+
if (!result) {
78+
throw new Error(
79+
`No record in "${collection}" matches query ${JSON.stringify(
80+
partialObject
81+
)}`
82+
)
83+
}
84+
return result
85+
}
86+
87+
private matchesPartialObject<T>(
88+
fullObject: T,
89+
partialObject: Partial<T>
90+
): boolean {
91+
for (const [key, value] of Object.entries(partialObject)) {
92+
if (fullObject[key as keyof T] !== value) {
93+
return false
94+
}
95+
}
96+
return true
97+
}
98+
}

dev-server-api/src/db/schema.ts

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { z } from "zod"
2+
3+
// Helper function for nullable fields
4+
const nullableText = () => z.string().nullable()
5+
6+
// PackageInfo schema
7+
export const PackageInfoSchema = z.object({
8+
package_info_id: z.number().int(),
9+
name: z.string(),
10+
})
11+
12+
// DevPackageExample schema
13+
export const DevPackageExampleSchema = z.object({
14+
dev_package_example_id: z.number().int(),
15+
file_path: z.string(),
16+
export_name: nullableText(),
17+
tscircuit_soup: z.any().nullable(), // Using any for JSON type
18+
completed_edit_events: z.any().nullable(), // Using any for JSON type
19+
error: nullableText(),
20+
is_loading: z.boolean(),
21+
soup_last_updated_at: nullableText(),
22+
edit_events_last_updated_at: nullableText(),
23+
edit_events_last_applied_at: nullableText(),
24+
last_updated_at: nullableText(),
25+
})
26+
27+
// ExportRequest schema
28+
export const ExportRequestSchema = z.object({
29+
export_request_id: z.number().int(),
30+
example_file_path: nullableText(),
31+
export_parameters: z.any().nullable(), // Using any for JSON type
32+
export_name: nullableText(),
33+
is_complete: z.boolean(),
34+
has_error: z.boolean(),
35+
error: nullableText(),
36+
created_at: nullableText(),
37+
})
38+
39+
// ExportFile schema
40+
export const ExportFileSchema = z.object({
41+
export_file_id: z.number().int(),
42+
file_name: nullableText(),
43+
file_content: z.instanceof(Buffer).nullable(), // For BLOB type
44+
is_complete: z.boolean(),
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+
60+
// You can also export individual types if needed
61+
export type PackageInfo = z.infer<typeof PackageInfoSchema>
62+
export type DevPackageExample = z.infer<typeof DevPackageExampleSchema>
63+
export type ExportRequest = z.infer<typeof ExportRequestSchema>
64+
export type ExportFile = z.infer<typeof ExportFileSchema>

0 commit comments

Comments
 (0)