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

Introduce level to start migratin from sqlite #68

Merged
merged 2 commits into from
Jul 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified dev-server-api/bun.lockb
Binary file not shown.
1 change: 1 addition & 0 deletions dev-server-api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"better-sqlite3": "^11.0.0",
"kysely": "^0.27.3",
"kysely-bun-sqlite": "^0.3.2",
"level": "^8.0.1",
"redaxios": "^0.5.1",
"winterspec": "0.0.81",
"zod": "^3.22.4"
Expand Down
98 changes: 98 additions & 0 deletions dev-server-api/src/db/level-db.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { Level } from "level"
import { z } from "zod"
import { DBSchema, type DBSchemaType } from "./schema"

// Create a wrapper class for Level with Zod validation
export class ZodLevelDatabase {
private db: Level<string, any>

constructor(location: string) {
this.db = new Level(location)
}

async get<K extends keyof DBSchemaType>(
collection: K,
id: string
): Promise<DBSchemaType[K]> {
const key = `${collection}:${id}`
const data = await this.db.get(key)
return DBSchema.shape[collection].parse(JSON.parse(data)) as any
}

async put<K extends keyof DBSchemaType>(
collection: K,
value: DBSchemaType[K]
): Promise<void> {
const idkey = `${collection}_id`
const valueLoose: any = value
if (!valueLoose[idkey]) {
// generate an id using the "count" key
let count = await this.db.get(`${collection}:count`)
if (!count) count = 1
;(value as any)[idkey] = count
await this.db.put(`${collection}:count`, count + 1)
}
const key = `${collection}:${valueLoose[idkey]}`
const validatedData = DBSchema.shape[collection].parse(value)
await this.db.put(key, JSON.stringify(validatedData))
}

async del<K extends keyof DBSchemaType>(
collection: K,
id: string
): Promise<void> {
const key = `${collection}:${id}`
await this.db.del(key)
}

async find<K extends keyof DBSchemaType>(
collection: K,
partialObject: Partial<DBSchemaType[K]>
): Promise<DBSchemaType[K] | null> {
const results: DBSchemaType[K][] = []
const schema = DBSchema.shape[collection]

for await (const [key, value] of this.db.iterator({
gte: `${collection}:`,
lte: `${collection}:\uffff`,
})) {
try {
const parsedValue = schema.parse(JSON.parse(value))
if (this.matchesPartialObject(parsedValue, partialObject)) {
return parsedValue as any
}
} catch (error) {
console.error(`Error parsing value for key ${key}:`, error)
}
}

return null
}

async findOrThrow<K extends keyof DBSchemaType>(
collection: K,
partialObject: Partial<DBSchemaType[K]>
): Promise<DBSchemaType[K]> {
const result = await this.find(collection, partialObject)
if (!result) {
throw new Error(
`No record in "${collection}" matches query ${JSON.stringify(
partialObject
)}`
)
}
return result
}

private matchesPartialObject<T>(
fullObject: T,
partialObject: Partial<T>
): boolean {
for (const [key, value] of Object.entries(partialObject)) {
if (fullObject[key as keyof T] !== value) {
return false
}
}
return true
}
}
64 changes: 64 additions & 0 deletions dev-server-api/src/db/schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { z } from "zod"

// Helper function for nullable fields
const nullableText = () => z.string().nullable()

// PackageInfo schema
export const PackageInfoSchema = z.object({
package_info_id: z.number().int(),
name: z.string(),
})

// DevPackageExample schema
export const DevPackageExampleSchema = z.object({
dev_package_example_id: z.number().int(),
file_path: z.string(),
export_name: nullableText(),
tscircuit_soup: z.any().nullable(), // Using any for JSON type
completed_edit_events: z.any().nullable(), // Using any for JSON type
error: nullableText(),
is_loading: z.boolean(),
soup_last_updated_at: nullableText(),
edit_events_last_updated_at: nullableText(),
edit_events_last_applied_at: nullableText(),
last_updated_at: nullableText(),
})

// ExportRequest schema
export const ExportRequestSchema = z.object({
export_request_id: z.number().int(),
example_file_path: nullableText(),
export_parameters: z.any().nullable(), // Using any for JSON type
export_name: nullableText(),
is_complete: z.boolean(),
has_error: z.boolean(),
error: nullableText(),
created_at: nullableText(),
})

// ExportFile schema
export const ExportFileSchema = z.object({
export_file_id: z.number().int(),
file_name: nullableText(),
file_content: z.instanceof(Buffer).nullable(), // For BLOB type
is_complete: z.boolean(),
export_request_id: z.number().int().nullable(),
created_at: nullableText(),
})

// Combined DBSchema
export const DBSchema = z.object({
package_info: PackageInfoSchema,
dev_package_example: DevPackageExampleSchema,
export_request: ExportRequestSchema,
export_file: ExportFileSchema,
})

// TypeScript type inference
export type DBSchemaType = z.infer<typeof DBSchema>

// You can also export individual types if needed
export type PackageInfo = z.infer<typeof PackageInfoSchema>
export type DevPackageExample = z.infer<typeof DevPackageExampleSchema>
export type ExportRequest = z.infer<typeof ExportRequestSchema>
export type ExportFile = z.infer<typeof ExportFileSchema>
Loading