refactor registries to make registration more convenient

This commit is contained in:
dswbx
2024-12-10 08:47:23 +01:00
parent 847b08b505
commit 290498de6e
7 changed files with 134 additions and 91 deletions

View File

@@ -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<string, Test1>);
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<Test1>((a: ClassRef<What>) => {
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());
});
});

View File

@@ -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<typeof honoServe>[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";

View File

@@ -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<typeof honoServe>[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);
}
);
}

View File

@@ -1,25 +1,42 @@
export type Constructor<T> = new (...args: any[]) => T;
export class Registry<Item, Items extends Record<string, object> = Record<string, object>> {
export type RegisterFn<Item> = (unknown: any) => Item;
export class Registry<
Item,
Items extends Record<string, Item> = Record<string, Item>,
Fn extends RegisterFn<Item> = RegisterFn<Item>
> {
private is_set: boolean = false;
private items: Items = {} as Items;
set<Actual extends Record<string, object>>(items: Actual) {
constructor(private registerFn?: Fn) {}
set<Actual extends Record<string, Item>>(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<Item, Actual>;
return this as unknown as Registry<Item, Actual, Fn>;
}
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<Fn>[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 extends keyof Items>(name: Name): Items[Name] {
return this.items[name];
}

View File

@@ -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";

View File

@@ -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<T> = Constructor<T> & { prototype: T };
export const MediaAdapterRegistry = new Registry<{
cls: ClassThatImplements<StorageAdapter>;
schema: TObject;
}>().set({
s3: {
cls: StorageS3Adapter,
schema: StorageS3Adapter.prototype.getSchema()
},
cloudinary: {
cls: StorageCloudinaryAdapter,
schema: StorageCloudinaryAdapter.prototype.getSchema()
}
});
}>((cls: ClassThatImplements<StorageAdapter>) => ({
cls,
schema: cls.prototype.getSchema() as TObject
}))
.register("s3", StorageS3Adapter)
.register("cloudinary", StorageCloudinaryAdapter);
export const Adapters = {
s3: {

View File

@@ -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!,