import type { SpacePrimitives } from "./space_primitives.ts"; import { mime } from "../deps.ts"; import { FileMeta } from "$sb/types.ts"; import { DataStore } from "../../plugos/lib/datastore.ts"; export type FileContent = { name: string; meta: FileMeta; data: Uint8Array; }; const filesMetaPrefix = ["file", "meta"]; const filesContentPrefix = ["file", "content"]; /** * TODO: Replace this with ChunkedDatastoreSpacePrimitives */ export class DataStoreSpacePrimitives implements SpacePrimitives { constructor( private ds: DataStore, ) { } async fetchFileList(): Promise { return (await this.ds.query({ prefix: filesMetaPrefix })) .map((kv) => this.ensureFileMeta(kv.value)); } async readFile( name: string, ): Promise<{ data: Uint8Array; meta: FileMeta }> { const fileContent = await this.ds.get([ ...filesContentPrefix, name, ]); if (!fileContent) { throw new Error("Not found"); } return { data: fileContent.data, meta: this.ensureFileMeta(fileContent.meta), }; } async writeFile( name: string, data: Uint8Array, _selfUpdate?: boolean, suggestedMeta?: FileMeta, ): Promise { let meta: FileMeta | undefined; try { // Build off of the existing file meta, if file exists meta = await this.getFileMeta(name); } catch { // Not found, that's fine } if (!meta) { meta = { name, created: suggestedMeta?.lastModified || Date.now(), perm: suggestedMeta?.perm || "rw", contentType: mime.getType(name) || "application/octet-stream", // Overwritten in a sec lastModified: 0, size: 0, }; } meta.lastModified = suggestedMeta?.lastModified || Date.now(); meta.size = data.byteLength; await this.ds.batchSet([ { key: [...filesContentPrefix, name], value: { name, data, meta }, }, { key: [...filesMetaPrefix, name], value: meta, }, ]); return meta; } async deleteFile(name: string): Promise { const fileMeta = await this.ds.get([ ...filesMetaPrefix, name, ]); if (!fileMeta) { throw new Error("Not found"); } return this.ds.batchDelete([ [...filesMetaPrefix, name], [...filesContentPrefix, name], ]); } async getFileMeta(name: string): Promise { const fileMeta = await this.ds.get([...filesMetaPrefix, name]); if (!fileMeta) { throw new Error("Not found"); } return this.ensureFileMeta(fileMeta); } ensureFileMeta(fileMeta: FileMeta): FileMeta { if (!fileMeta.created) { fileMeta.created = fileMeta.lastModified; } return fileMeta; } }