diff --git a/app/__test__/core/Registry.spec.ts b/app/__test__/core/Registry.spec.ts index 1be9310..557b39a 100644 --- a/app/__test__/core/Registry.spec.ts +++ b/app/__test__/core/Registry.spec.ts @@ -1,4 +1,4 @@ -import { describe, test } from "bun:test"; +import { describe, expect, test } from "bun:test"; import type { TObject, TString } from "@sinclair/typebox"; import { Registry } from "../../src/core/registry/Registry"; import { type TSchema, Type } from "../../src/core/utils"; @@ -11,6 +11,9 @@ class What { method() { return null; } + getType() { + return Type.Object({ type: Type.String() }); + } } class What2 extends What {} class NotAllowed {} @@ -32,25 +35,53 @@ describe("Registry", () => { } satisfies Record); const item = registry.get("first"); + expect(item).toBeDefined(); + expect(item?.cls).toBe(What); + const second = Type.Object({ type: Type.String(), what: Type.String() }); registry.add("second", { cls: What2, - schema: Type.Object({ type: Type.String(), what: Type.String() }), + schema: second, enabled: true }); + // @ts-ignore + expect(registry.get("second").schema).toEqual(second); + + const third = Type.Object({ type: Type.String({ default: "1" }), what22: Type.String() }); registry.add("third", { // @ts-expect-error cls: NotAllowed, - schema: Type.Object({ type: Type.String({ default: "1" }), what22: Type.String() }), + schema: third, enabled: true }); + // @ts-ignore + expect(registry.get("third").schema).toEqual(third); + + const fourth = Type.Object({ type: Type.Number(), what22: Type.String() }); registry.add("fourth", { cls: What, // @ts-expect-error - schema: Type.Object({ type: Type.Number(), what22: Type.String() }), + schema: fourth, enabled: true }); + // @ts-ignore + expect(registry.get("fourth").schema).toEqual(fourth); - console.log("list", registry.all()); + expect(Object.keys(registry.all()).length).toBe(4); + }); + + test("uses registration fn", async () => { + const registry = new Registry((a: ClassRef) => { + return { + cls: a, + schema: a.prototype.getType(), + enabled: true + }; + }); + + registry.register("what2", What2); + expect(registry.get("what2")).toBeDefined(); + expect(registry.get("what2").cls).toBe(What2); + expect(registry.get("what2").schema).toEqual(What2.prototype.getType()); }); }); diff --git a/app/src/adapter/node/index.ts b/app/src/adapter/node/index.ts index 47d4c97..be24456 100644 --- a/app/src/adapter/node/index.ts +++ b/app/src/adapter/node/index.ts @@ -1,59 +1,5 @@ -import path from "node:path"; -import { serve as honoServe } from "@hono/node-server"; -import { serveStatic } from "@hono/node-server/serve-static"; -import { App, type CreateAppConfig } from "bknd"; - -export type NodeAdapterOptions = CreateAppConfig & { - relativeDistPath?: string; - port?: number; - hostname?: string; - listener?: Parameters[1]; -}; - -export function serve({ - relativeDistPath, - port = 1337, - hostname, - listener, - ...config -}: NodeAdapterOptions = {}) { - const root = path.relative( - process.cwd(), - path.resolve(relativeDistPath ?? "./node_modules/bknd/dist", "static") - ); - let app: App; - - honoServe( - { - port, - hostname, - fetch: async (req: Request) => { - if (!app) { - app = App.create(config); - - app.emgr.on( - "app-built", - async () => { - app.modules.server.get( - "/*", - serveStatic({ - root - }) - ); - app.registerAdminController(); - }, - "sync" - ); - - await app.build(); - } - - return app.fetch(req); - } - }, - (connInfo) => { - console.log(`Server is running on http://localhost:${connInfo.port}`); - listener?.(connInfo); - } - ); -} +export * from "./node.adapter"; +export { + StorageLocalAdapter, + type LocalAdapterConfig +} from "../../media/storage/adapters/StorageLocalAdapter"; diff --git a/app/src/adapter/node/node.adapter.ts b/app/src/adapter/node/node.adapter.ts new file mode 100644 index 0000000..47d4c97 --- /dev/null +++ b/app/src/adapter/node/node.adapter.ts @@ -0,0 +1,59 @@ +import path from "node:path"; +import { serve as honoServe } from "@hono/node-server"; +import { serveStatic } from "@hono/node-server/serve-static"; +import { App, type CreateAppConfig } from "bknd"; + +export type NodeAdapterOptions = CreateAppConfig & { + relativeDistPath?: string; + port?: number; + hostname?: string; + listener?: Parameters[1]; +}; + +export function serve({ + relativeDistPath, + port = 1337, + hostname, + listener, + ...config +}: NodeAdapterOptions = {}) { + const root = path.relative( + process.cwd(), + path.resolve(relativeDistPath ?? "./node_modules/bknd/dist", "static") + ); + let app: App; + + honoServe( + { + port, + hostname, + fetch: async (req: Request) => { + if (!app) { + app = App.create(config); + + app.emgr.on( + "app-built", + async () => { + app.modules.server.get( + "/*", + serveStatic({ + root + }) + ); + app.registerAdminController(); + }, + "sync" + ); + + await app.build(); + } + + return app.fetch(req); + } + }, + (connInfo) => { + console.log(`Server is running on http://localhost:${connInfo.port}`); + listener?.(connInfo); + } + ); +} diff --git a/app/src/core/registry/Registry.ts b/app/src/core/registry/Registry.ts index 12209ca..1fc78bb 100644 --- a/app/src/core/registry/Registry.ts +++ b/app/src/core/registry/Registry.ts @@ -1,25 +1,42 @@ export type Constructor = new (...args: any[]) => T; -export class Registry = Record> { + +export type RegisterFn = (unknown: any) => Item; + +export class Registry< + Item, + Items extends Record = Record, + Fn extends RegisterFn = RegisterFn +> { private is_set: boolean = false; private items: Items = {} as Items; - set>(items: Actual) { + constructor(private registerFn?: Fn) {} + + set>(items: Actual) { if (this.is_set) { throw new Error("Registry is already set"); } - // @ts-ignore - this.items = items; + this.items = items as unknown as Items; this.is_set = true; - return this as unknown as Registry; + return this as unknown as Registry; } add(name: string, item: Item) { - // @ts-ignore - this.items[name] = item; + this.items[name as keyof Items] = item as Items[keyof Items]; return this; } + register(name: string, specific: Parameters[0]) { + if (this.registerFn) { + const item = this.registerFn(specific); + this.items[name as keyof Items] = item as Items[keyof Items]; + return this; + } + + return this.add(name, specific); + } + get(name: Name): Items[Name] { return this.items[name]; } diff --git a/app/src/index.ts b/app/src/index.ts index 578ab5d..78e9f70 100644 --- a/app/src/index.ts +++ b/app/src/index.ts @@ -7,5 +7,7 @@ export { type ModuleSchemas } from "modules/ModuleManager"; +export { registries } from "modules/registries"; + export type * from "./adapter"; export { Api, type ApiOptions } from "./Api"; diff --git a/app/src/media/index.ts b/app/src/media/index.ts index bdc94c1..7a1cdc7 100644 --- a/app/src/media/index.ts +++ b/app/src/media/index.ts @@ -17,10 +17,6 @@ import { import { type S3AdapterConfig, StorageS3Adapter } from "./storage/adapters/StorageS3Adapter"; export { StorageS3Adapter, type S3AdapterConfig, StorageCloudinaryAdapter, type CloudinaryConfig }; -/*export { - StorageLocalAdapter, - type LocalAdapterConfig -} from "./storage/adapters/StorageLocalAdapter";*/ export * as StorageEvents from "./storage/events"; export { type FileUploadedEventData } from "./storage/events"; @@ -31,16 +27,12 @@ type ClassThatImplements = Constructor & { prototype: T }; export const MediaAdapterRegistry = new Registry<{ cls: ClassThatImplements; schema: TObject; -}>().set({ - s3: { - cls: StorageS3Adapter, - schema: StorageS3Adapter.prototype.getSchema() - }, - cloudinary: { - cls: StorageCloudinaryAdapter, - schema: StorageCloudinaryAdapter.prototype.getSchema() - } -}); +}>((cls: ClassThatImplements) => ({ + cls, + schema: cls.prototype.getSchema() as TObject +})) + .register("s3", StorageS3Adapter) + .register("cloudinary", StorageCloudinaryAdapter); export const Adapters = { s3: { diff --git a/app/vite.dev.ts b/app/vite.dev.ts index 608e24e..bed5beb 100644 --- a/app/vite.dev.ts +++ b/app/vite.dev.ts @@ -1,14 +1,10 @@ import { serveStatic } from "@hono/node-server/serve-static"; import { createClient } from "@libsql/client/node"; -import { App } from "./src"; +import { App, registries } from "./src"; import { LibsqlConnection } from "./src/data"; import { StorageLocalAdapter } from "./src/media/storage/adapters/StorageLocalAdapter"; -import { registries } from "./src/modules/registries"; -registries.media.add("local", { - cls: StorageLocalAdapter, - schema: StorageLocalAdapter.prototype.getSchema() -}); +registries.media.register("local", StorageLocalAdapter); const credentials = { url: import.meta.env.VITE_DB_URL!,