import { createHash } from "node:crypto"; import { promises, type Stats } from "node:fs"; import path, { extname } from "node:path"; import type { DocumentBody } from "dbtype/api"; /** * content file or directory referrer */ export interface ContentFile { getHash(): Promise; createDocumentBody(): Promise; readonly path: string; readonly type: string; } export type ContentConstructOption = { hash: string; }; type ContentFileConstructor = (new ( path: string, option?: ContentConstructOption, ) => ContentFile) & { content_type: string; }; export const createDefaultClass = (type: string): ContentFileConstructor => { const cons = class implements ContentFile { readonly path: string; // type = type; static content_type = type; protected hash: string | undefined; protected stat: Stats | undefined; protected getStat(){ return this.stat; } constructor(path: string, option?: ContentConstructOption) { this.path = path; this.hash = option?.hash; this.stat = undefined; } async createDocumentBody(): Promise { console.log(`createDocumentBody: ${this.path}`); const { base, dir, name } = path.parse(this.path); const ret = { title: name, basepath: dir, additional: {}, content_type: cons.content_type, filename: base, tags: [], content_hash: await this.getHash(), modified_at: await this.getMtime(), } as DocumentBody; return ret; } get type(): string { return cons.content_type; } async getHash(): Promise { if (this.hash !== undefined) return this.hash; this.stat = await promises.stat(this.path); const hash = createHash("sha512"); hash.update(extname(this.path)); hash.update(this.stat.mode.toString()); // if(this.desc !== undefined) // hash.update(JSON.stringify(this.desc)); hash.update(this.stat.size.toString()); this.hash = hash.digest("base64"); return this.hash; } async getMtime(): Promise { const oldStat = this.getStat(); if (oldStat !== undefined) return oldStat.mtimeMs; await this.getHash(); const newStat = this.getStat(); if (newStat === undefined) throw new Error("stat is undefined"); return newStat.mtimeMs; } }; return cons; }; const ContstructorTable: { [k: string]: ContentFileConstructor } = {}; export function registerContentReferrer(s: ContentFileConstructor) { console.log(`registered content type: ${s.content_type}`); ContstructorTable[s.content_type] = s; } export function createContentFile(type: string, path: string, option?: ContentConstructOption) { const constructorMethod = ContstructorTable[type]; if (constructorMethod === undefined) { console.log(`${type} are not in ${JSON.stringify(ContstructorTable)}`); throw new Error("construction method of the content type is undefined"); } return new constructorMethod(path, option); } export function getContentFileConstructor(type: string): ContentFileConstructor | undefined { const ret = ContstructorTable[type]; return ret; }