mirror of
https://github.com/bknd-io/bknd/
synced 2026-08-02 16:16:02 +00:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7e25d54847 | |||
| 9d5bc61f33 | |||
| ea46dc8df0 | |||
| 88ec3432a0 | |||
| 43c8f14470 |
@@ -6,7 +6,7 @@ import { dataConfigSchema } from "../../src/data/data-schema";
|
|||||||
import * as proto from "../../src/data/prototype";
|
import * as proto from "../../src/data/prototype";
|
||||||
import { schemaToEm } from "../helper";
|
import { schemaToEm } from "../helper";
|
||||||
import { disableConsoleLog, enableConsoleLog } from "core/utils/test";
|
import { disableConsoleLog, enableConsoleLog } from "core/utils/test";
|
||||||
import { parse } from "core/object/schema";
|
import { parse } from "bknd/core";
|
||||||
|
|
||||||
beforeAll(disableConsoleLog);
|
beforeAll(disableConsoleLog);
|
||||||
afterAll(enableConsoleLog);
|
afterAll(enableConsoleLog);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { describe, expect, test } from "bun:test";
|
import { describe, expect, test } from "bun:test";
|
||||||
|
import { registries } from "../../src";
|
||||||
import { createApp } from "core/test/utils";
|
import { createApp } from "core/test/utils";
|
||||||
import * as proto from "../../src/data/prototype";
|
import * as proto from "../../src/data/prototype";
|
||||||
import { StorageLocalAdapter } from "adapter/node/storage/StorageLocalAdapter";
|
import { StorageLocalAdapter } from "adapter/node/storage/StorageLocalAdapter";
|
||||||
@@ -13,8 +14,8 @@ describe("repros", async () => {
|
|||||||
* There was an issue that AppData had old configs because of system entity "media"
|
* There was an issue that AppData had old configs because of system entity "media"
|
||||||
*/
|
*/
|
||||||
test("registers media entity correctly to relate to it", async () => {
|
test("registers media entity correctly to relate to it", async () => {
|
||||||
|
registries.media.register("local", StorageLocalAdapter);
|
||||||
const app = createApp();
|
const app = createApp();
|
||||||
app.module.media.adapters.set("local", StorageLocalAdapter);
|
|
||||||
await app.build();
|
await app.build();
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { describe, expect, test } from "bun:test";
|
|||||||
import { Authenticator, type User, type UserPool } from "../../src/auth";
|
import { Authenticator, type User, type UserPool } from "../../src/auth";
|
||||||
import { cookieConfig } from "../../src/auth/authenticate/Authenticator";
|
import { cookieConfig } from "../../src/auth/authenticate/Authenticator";
|
||||||
import { PasswordStrategy } from "../../src/auth/authenticate/strategies/PasswordStrategy";
|
import { PasswordStrategy } from "../../src/auth/authenticate/strategies/PasswordStrategy";
|
||||||
import { parse } from "core/object/schema";
|
import { parse } from "bknd/core";
|
||||||
|
|
||||||
/*class MemoryUserPool implements UserPool {
|
/*class MemoryUserPool implements UserPool {
|
||||||
constructor(private users: User[] = []) {}
|
constructor(private users: User[] = []) {}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { describe, expect, test } from "bun:test";
|
import { describe, expect, test } from "bun:test";
|
||||||
import { Registry } from "core";
|
import { Registry } from "core";
|
||||||
import { s } from "core/object/schema";
|
import { s } from "bknd/core";
|
||||||
|
|
||||||
type Constructor<T> = new (...args: any[]) => T;
|
type Constructor<T> = new (...args: any[]) => T;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { describe, expect, test } from "bun:test";
|
import { describe, expect, test } from "bun:test";
|
||||||
import { SchemaObject } from "../../../src/core";
|
import { SchemaObject } from "../../../src/core";
|
||||||
import { s } from "core/object/schema";
|
import { s } from "bknd/core";
|
||||||
|
|
||||||
describe("SchemaObject", async () => {
|
describe("SchemaObject", async () => {
|
||||||
test("basic", async () => {
|
test("basic", async () => {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
||||||
|
|
||||||
import { Guard } from "../../src/auth";
|
import { Guard } from "../../src/auth";
|
||||||
import { parse } from "core/object/schema";
|
import { parse } from "bknd/core";
|
||||||
import {
|
import {
|
||||||
Entity,
|
Entity,
|
||||||
type EntityData,
|
type EntityData,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { afterAll, expect as bunExpect, describe, test } from "bun:test";
|
import { afterAll, expect as bunExpect, describe, test } from "bun:test";
|
||||||
import { stripMark } from "core/object/schema";
|
import { stripMark } from "bknd/core";
|
||||||
import { Entity, EntityManager, PolymorphicRelation, TextField } from "../../src/data";
|
import { Entity, EntityManager, PolymorphicRelation, TextField } from "../../src/data";
|
||||||
import { getDummyConnection } from "./helper";
|
import { getDummyConnection } from "./helper";
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { describe, expect, test } from "bun:test";
|
|||||||
import { baseFieldConfigSchema, Field } from "../../../../src/data/fields/Field";
|
import { baseFieldConfigSchema, Field } from "../../../../src/data/fields/Field";
|
||||||
import { fieldTestSuite } from "data/fields/field-test-suite";
|
import { fieldTestSuite } from "data/fields/field-test-suite";
|
||||||
import { bunTestRunner } from "adapter/bun/test";
|
import { bunTestRunner } from "adapter/bun/test";
|
||||||
import { stripMark } from "core/object/schema";
|
import { stripMark } from "bknd/core";
|
||||||
|
|
||||||
describe("[data] Field", async () => {
|
describe("[data] Field", async () => {
|
||||||
class FieldSpec extends Field {
|
class FieldSpec extends Field {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { describe, expect, test } from "bun:test";
|
import { describe, expect, test } from "bun:test";
|
||||||
import { Entity, EntityIndex, Field } from "../../../../src/data";
|
import { Entity, EntityIndex, Field } from "../../../../src/data";
|
||||||
import { s } from "core/object/schema";
|
import { s } from "bknd/core";
|
||||||
|
|
||||||
class TestField extends Field {
|
class TestField extends Field {
|
||||||
protected getSchema(): any {
|
protected getSchema(): any {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { describe, expect, test } from "bun:test";
|
import { describe, expect, test } from "bun:test";
|
||||||
import { Flow, LogTask, SubFlowTask, RenderTask, Task } from "../../src/flows";
|
import { Flow, LogTask, SubFlowTask, RenderTask, Task } from "../../src/flows";
|
||||||
import { s } from "core/object/schema";
|
import { s } from "bknd/core";
|
||||||
|
|
||||||
export class StringifyTask<Output extends string> extends Task<
|
export class StringifyTask<Output extends string> extends Task<
|
||||||
typeof StringifyTask.schema,
|
typeof StringifyTask.schema,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { describe, expect, test } from "bun:test";
|
import { describe, expect, test } from "bun:test";
|
||||||
import { Task } from "../../src/flows";
|
import { Task } from "../../src/flows";
|
||||||
import { dynamic } from "../../src/flows/tasks/Task";
|
import { dynamic } from "../../src/flows/tasks/Task";
|
||||||
import { s } from "core/object/schema";
|
import { s } from "bknd/core";
|
||||||
|
|
||||||
describe.skip("Task", async () => {
|
describe.skip("Task", async () => {
|
||||||
test("resolveParams: template with parse", async () => {
|
test("resolveParams: template with parse", async () => {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { describe, expect, test } from "bun:test";
|
import { describe, expect, test } from "bun:test";
|
||||||
import { Hono } from "hono";
|
import { Hono } from "hono";
|
||||||
import { Event, EventManager } from "../../src/core/events";
|
import { Event, EventManager } from "../../src/core/events";
|
||||||
import { s, parse } from "core/object/schema";
|
import { s, parse } from "bknd/core";
|
||||||
import { EventTrigger, Flow, HttpTrigger, type InputsMap, Task } from "../../src/flows";
|
import { EventTrigger, Flow, HttpTrigger, type InputsMap, Task } from "../../src/flows";
|
||||||
import { dynamic } from "../../src/flows/tasks/Task";
|
import { dynamic } from "../../src/flows/tasks/Task";
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import { describe, expect, test } from "bun:test";
|
import { describe, expect, test } from "bun:test";
|
||||||
import { isEqual } from "lodash-es";
|
import { isEqual } from "lodash-es";
|
||||||
import { _jsonp, withDisabledConsole } from "../../src/core/utils";
|
import { _jsonp, withDisabledConsole } from "../../src/core/utils";
|
||||||
import { s } from "core/object/schema";
|
import { s } from "bknd/core";
|
||||||
import { Condition, ExecutionEvent, FetchTask, Flow, LogTask, Task } from "../../src/flows";
|
import { Condition, ExecutionEvent, FetchTask, Flow, LogTask, Task } from "../../src/flows";
|
||||||
|
|
||||||
/*beforeAll(disableConsoleLog);
|
/*beforeAll(disableConsoleLog);
|
||||||
|
|||||||
@@ -1,12 +1,17 @@
|
|||||||
/// <reference types="@types/bun" />
|
/// <reference types="@types/bun" />
|
||||||
|
|
||||||
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
||||||
|
import { registries } from "../../src";
|
||||||
import { createApp } from "core/test/utils";
|
import { createApp } from "core/test/utils";
|
||||||
import { mergeObject, randomString } from "../../src/core/utils";
|
import { mergeObject, randomString } from "../../src/core/utils";
|
||||||
import type { TAppMediaConfig } from "../../src/media/media-schema";
|
import type { TAppMediaConfig } from "../../src/media/media-schema";
|
||||||
import { StorageLocalAdapter } from "adapter/node/storage/StorageLocalAdapter";
|
import { StorageLocalAdapter } from "adapter/node/storage/StorageLocalAdapter";
|
||||||
import { assetsPath, assetsTmpPath, disableConsoleLog, enableConsoleLog } from "../helper";
|
import { assetsPath, assetsTmpPath, disableConsoleLog, enableConsoleLog } from "../helper";
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
registries.media.register("local", StorageLocalAdapter);
|
||||||
|
});
|
||||||
|
|
||||||
const path = `${assetsPath}/image.png`;
|
const path = `${assetsPath}/image.png`;
|
||||||
|
|
||||||
async function makeApp(mediaOverride: Partial<TAppMediaConfig> = {}) {
|
async function makeApp(mediaOverride: Partial<TAppMediaConfig> = {}) {
|
||||||
@@ -27,8 +32,6 @@ async function makeApp(mediaOverride: Partial<TAppMediaConfig> = {}) {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
app.module.media.adapters.set("local", StorageLocalAdapter);
|
|
||||||
|
|
||||||
await app.build();
|
await app.build();
|
||||||
return app;
|
return app;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { beforeEach, describe, expect, test } from "bun:test";
|
import { beforeEach, describe, expect, test } from "bun:test";
|
||||||
import { parse } from "core/object/schema";
|
import { parse } from "bknd/core";
|
||||||
import { fieldsSchema } from "../../src/data/data-schema";
|
import { fieldsSchema } from "../../src/data/data-schema";
|
||||||
import { AppData, type ModuleBuildContext } from "../../src/modules";
|
import { AppData, type ModuleBuildContext } from "../../src/modules";
|
||||||
import { makeCtx, moduleTestSuite } from "./module-test-suite";
|
import { makeCtx, moduleTestSuite } from "./module-test-suite";
|
||||||
|
|||||||
@@ -1,15 +1,22 @@
|
|||||||
import { describe, expect, test } from "bun:test";
|
import { describe, expect, test } from "bun:test";
|
||||||
|
import { registries } from "../../src";
|
||||||
import { createApp } from "core/test/utils";
|
import { createApp } from "core/test/utils";
|
||||||
import { em, entity, text } from "../../src/data";
|
import { em, entity, text } from "../../src/data";
|
||||||
import { StorageLocalAdapter } from "adapter/node/storage/StorageLocalAdapter";
|
import { StorageLocalAdapter } from "adapter/node/storage/StorageLocalAdapter";
|
||||||
import { AppMedia } from "../../src/media/AppMedia";
|
import { AppMedia } from "../../src/media/AppMedia";
|
||||||
|
import { mediaConfigSchema } from "../../src/media/media-schema";
|
||||||
import { moduleTestSuite } from "./module-test-suite";
|
import { moduleTestSuite } from "./module-test-suite";
|
||||||
|
|
||||||
describe("AppMedia", () => {
|
describe("AppMedia", () => {
|
||||||
|
test.only("...", () => {
|
||||||
|
const media = new AppMedia();
|
||||||
|
console.log(media.toJSON());
|
||||||
|
});
|
||||||
|
|
||||||
moduleTestSuite(AppMedia);
|
moduleTestSuite(AppMedia);
|
||||||
|
|
||||||
test("should allow additional fields", async () => {
|
test("should allow additional fields", async () => {
|
||||||
//registries.media.register("local", StorageLocalAdapter);
|
registries.media.register("local", StorageLocalAdapter);
|
||||||
|
|
||||||
const app = createApp({
|
const app = createApp({
|
||||||
initialConfig: {
|
initialConfig: {
|
||||||
@@ -30,7 +37,6 @@ describe("AppMedia", () => {
|
|||||||
}).toJSON(),
|
}).toJSON(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
app.module.media.adapters.set("local", StorageLocalAdapter);
|
|
||||||
|
|
||||||
await app.build();
|
await app.build();
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { describe, expect, test } from "bun:test";
|
import { describe, expect, test } from "bun:test";
|
||||||
import { s, stripMark } from "core/object/schema";
|
import { s, stripMark } from "bknd/core";
|
||||||
import { EntityManager, em, entity, index, text } from "../../src/data";
|
import { EntityManager, em, entity, index, text } from "../../src/data";
|
||||||
import { DummyConnection } from "../../src/data/connection/DummyConnection";
|
import { DummyConnection } from "../../src/data/connection/DummyConnection";
|
||||||
import { Module } from "../../src/modules/Module";
|
import { Module } from "../../src/modules/Module";
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { Module } from "modules/Module";
|
|||||||
import { type ConfigTable, getDefaultConfig, ModuleManager } from "modules/ModuleManager";
|
import { type ConfigTable, getDefaultConfig, ModuleManager } from "modules/ModuleManager";
|
||||||
import { CURRENT_VERSION, TABLE_NAME } from "modules/migrations";
|
import { CURRENT_VERSION, TABLE_NAME } from "modules/migrations";
|
||||||
import { getDummyConnection } from "../helper";
|
import { getDummyConnection } from "../helper";
|
||||||
import { s, stripMark } from "core/object/schema";
|
import { s, stripMark } from "bknd/core";
|
||||||
|
|
||||||
describe("ModuleManager", async () => {
|
describe("ModuleManager", async () => {
|
||||||
test("s1: no config, no build", async () => {
|
test("s1: no config, no build", async () => {
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ export type AppPluginConfig = {
|
|||||||
onServerInit?: (server: Hono<ServerEnv>) => MaybePromise<void>;
|
onServerInit?: (server: Hono<ServerEnv>) => MaybePromise<void>;
|
||||||
onFirstBoot?: () => MaybePromise<void>;
|
onFirstBoot?: () => MaybePromise<void>;
|
||||||
onBoot?: () => MaybePromise<void>;
|
onBoot?: () => MaybePromise<void>;
|
||||||
onModulesCreated?: (modules: Modules) => void;
|
|
||||||
};
|
};
|
||||||
export type AppPlugin = (app: App) => AppPluginConfig;
|
export type AppPlugin = (app: App) => AppPluginConfig;
|
||||||
|
|
||||||
@@ -114,7 +113,6 @@ export class App<C extends Connection = Connection, Options extends AppOptions =
|
|||||||
onFirstBoot: this.onFirstBoot.bind(this),
|
onFirstBoot: this.onFirstBoot.bind(this),
|
||||||
onServerInit: this.onServerInit.bind(this),
|
onServerInit: this.onServerInit.bind(this),
|
||||||
onModulesBuilt: this.onModulesBuilt.bind(this),
|
onModulesBuilt: this.onModulesBuilt.bind(this),
|
||||||
onModulesCreated: this.onModulesCreated.bind(this),
|
|
||||||
});
|
});
|
||||||
this.modules.ctx().emgr.registerEvents(AppEvents);
|
this.modules.ctx().emgr.registerEvents(AppEvents);
|
||||||
}
|
}
|
||||||
@@ -327,11 +325,6 @@ export class App<C extends Connection = Connection, Options extends AppOptions =
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async onModulesCreated(modules: Modules, ctx: ModuleBuildContext) {
|
|
||||||
await this.runPlugins("onModulesCreated", modules, ctx);
|
|
||||||
this.options?.manager?.onModulesCreated?.(modules, ctx);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createApp(config: CreateAppConfig = {}) {
|
export function createApp(config: CreateAppConfig = {}) {
|
||||||
|
|||||||
@@ -17,8 +17,9 @@ export async function createApp<Env = BunEnv>(
|
|||||||
opts?: RuntimeOptions,
|
opts?: RuntimeOptions,
|
||||||
) {
|
) {
|
||||||
const root = path.resolve(distPath ?? "./node_modules/bknd/dist", "static");
|
const root = path.resolve(distPath ?? "./node_modules/bknd/dist", "static");
|
||||||
|
registerLocalMediaAdapter();
|
||||||
|
|
||||||
const app = await createRuntimeApp(
|
return await createRuntimeApp(
|
||||||
{
|
{
|
||||||
...config,
|
...config,
|
||||||
serveStatic: serveStatic({ root }),
|
serveStatic: serveStatic({ root }),
|
||||||
@@ -26,8 +27,6 @@ export async function createApp<Env = BunEnv>(
|
|||||||
args ?? (process.env as Env),
|
args ?? (process.env as Env),
|
||||||
opts,
|
opts,
|
||||||
);
|
);
|
||||||
registerLocalMediaAdapter(app);
|
|
||||||
return app;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createHandler<Env = BunEnv>(
|
export function createHandler<Env = BunEnv>(
|
||||||
|
|||||||
@@ -12,7 +12,10 @@ export function getBindings<T extends GetBindingType>(env: any, type: T): Bindin
|
|||||||
const bindings: BindingMap<T>[] = [];
|
const bindings: BindingMap<T>[] = [];
|
||||||
for (const key in env) {
|
for (const key in env) {
|
||||||
try {
|
try {
|
||||||
if (env[key] && (env[key] as any).constructor.name === type) {
|
if (
|
||||||
|
env[key] &&
|
||||||
|
((env[key] as any).constructor.name === type || String(env[key]) === `[object ${type}]`)
|
||||||
|
) {
|
||||||
bindings.push({
|
bindings.push({
|
||||||
key,
|
key,
|
||||||
value: env[key] as BindingTypeMap[T],
|
value: env[key] as BindingTypeMap[T],
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import { getCached } from "./modes/cached";
|
|||||||
import { getDurable } from "./modes/durable";
|
import { getDurable } from "./modes/durable";
|
||||||
import type { App } from "bknd";
|
import type { App } from "bknd";
|
||||||
import { $console } from "core/utils";
|
import { $console } from "core/utils";
|
||||||
import { registerMedia } from "./storage/StorageR2Adapter";
|
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
namespace Cloudflare {
|
namespace Cloudflare {
|
||||||
@@ -34,7 +33,7 @@ export type CloudflareBkndConfig<Env = CloudflareEnv> = RuntimeBkndConfig<Env> &
|
|||||||
keepAliveSeconds?: number;
|
keepAliveSeconds?: number;
|
||||||
forceHttps?: boolean;
|
forceHttps?: boolean;
|
||||||
manifest?: string;
|
manifest?: string;
|
||||||
registerMedia?: boolean;
|
registerMedia?: boolean | ((env: Env) => void);
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Context<Env = CloudflareEnv> = {
|
export type Context<Env = CloudflareEnv> = {
|
||||||
@@ -100,16 +99,7 @@ export function serve<Env extends CloudflareEnv = CloudflareEnv>(
|
|||||||
throw new Error(`Unknown mode ${mode}`);
|
throw new Error(`Unknown mode ${mode}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
registerMediaInternal(app, config, context);
|
|
||||||
return app.fetch(request, env, ctx);
|
return app.fetch(request, env, ctx);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let media_registered: boolean = false;
|
|
||||||
function registerMediaInternal(app: App, config: CloudflareBkndConfig<any>, ctx?: Context) {
|
|
||||||
if (!media_registered && config.registerMedia !== false) {
|
|
||||||
registerMedia(app, ctx?.env as any);
|
|
||||||
media_registered = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/// <reference types="@cloudflare/workers-types" />
|
/// <reference types="@cloudflare/workers-types" />
|
||||||
|
|
||||||
|
import { registerMedia } from "./storage/StorageR2Adapter";
|
||||||
import { getBinding } from "./bindings";
|
import { getBinding } from "./bindings";
|
||||||
import { d1Sqlite } from "./connection/D1Connection";
|
import { d1Sqlite } from "./connection/D1Connection";
|
||||||
import { Connection } from "bknd/data";
|
import { Connection } from "bknd/data";
|
||||||
@@ -87,10 +88,20 @@ export function d1SessionHelper(config: CloudflareBkndConfig<any>) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let media_registered: boolean = false;
|
||||||
export function makeConfig<Env extends CloudflareEnv = CloudflareEnv>(
|
export function makeConfig<Env extends CloudflareEnv = CloudflareEnv>(
|
||||||
config: CloudflareBkndConfig<Env>,
|
config: CloudflareBkndConfig<Env>,
|
||||||
args?: CfMakeConfigArgs<Env>,
|
args?: CfMakeConfigArgs<Env>,
|
||||||
) {
|
) {
|
||||||
|
if (!media_registered && config.registerMedia !== false) {
|
||||||
|
if (typeof config.registerMedia === "function") {
|
||||||
|
config.registerMedia(args?.env as any);
|
||||||
|
} else {
|
||||||
|
registerMedia(args?.env as any);
|
||||||
|
}
|
||||||
|
media_registered = true;
|
||||||
|
}
|
||||||
|
|
||||||
const appConfig = makeAdapterConfig(config, args?.env);
|
const appConfig = makeAdapterConfig(config, args?.env);
|
||||||
|
|
||||||
// if connection instance is given, don't do anything
|
// if connection instance is given, don't do anything
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ export {
|
|||||||
type BindingMap,
|
type BindingMap,
|
||||||
} from "./bindings";
|
} from "./bindings";
|
||||||
export { constants } from "./config";
|
export { constants } from "./config";
|
||||||
export { StorageR2Adapter } from "./storage/StorageR2Adapter";
|
export { StorageR2Adapter, registerMedia } from "./storage/StorageR2Adapter";
|
||||||
|
export { registries } from "bknd";
|
||||||
|
|
||||||
// for compatibility with old code
|
// for compatibility with old code
|
||||||
export function d1<DB extends D1Database | D1DatabaseSession = D1Database>(
|
export function d1<DB extends D1Database | D1DatabaseSession = D1Database>(
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import type { App } from "bknd";
|
import { registries } from "bknd";
|
||||||
import { isDebug } from "bknd/core";
|
import { isDebug } from "bknd/core";
|
||||||
import { guessMimeType as guess, StorageAdapter, type FileBody } from "bknd/media";
|
import { guessMimeType as guess, StorageAdapter, type FileBody } from "bknd/media";
|
||||||
import { getBindings } from "../bindings";
|
import { getBindings } from "../bindings";
|
||||||
import { s } from "core/object/schema";
|
import { s } from "bknd/core";
|
||||||
|
|
||||||
export function makeSchema(bindings: string[] = []) {
|
export function makeSchema(bindings: string[] = []) {
|
||||||
return s.object(
|
return s.object(
|
||||||
@@ -13,10 +13,10 @@ export function makeSchema(bindings: string[] = []) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function registerMedia(app: App, env: Record<string, any>) {
|
export function registerMedia(env: Record<string, any>) {
|
||||||
const r2_bindings = getBindings(env, "R2Bucket");
|
const r2_bindings = getBindings(env, "R2Bucket");
|
||||||
|
|
||||||
app.module.media.adapters.set(
|
registries.media.register(
|
||||||
"r2",
|
"r2",
|
||||||
class extends StorageR2Adapter {
|
class extends StorageR2Adapter {
|
||||||
constructor(private config: any) {
|
constructor(private config: any) {
|
||||||
|
|||||||
@@ -4,14 +4,13 @@ import { $console } from "bknd/utils";
|
|||||||
import type { MiddlewareHandler } from "hono";
|
import type { MiddlewareHandler } from "hono";
|
||||||
import type { AdminControllerOptions } from "modules/server/AdminController";
|
import type { AdminControllerOptions } from "modules/server/AdminController";
|
||||||
import { Connection } from "bknd/data";
|
import { Connection } from "bknd/data";
|
||||||
import type { MaybePromise } from "core/types";
|
|
||||||
|
|
||||||
export { Connection } from "bknd/data";
|
export { Connection } from "bknd/data";
|
||||||
|
|
||||||
export type BkndConfig<Args = any> = CreateAppConfig & {
|
export type BkndConfig<Args = any> = CreateAppConfig & {
|
||||||
app?: CreateAppConfig | ((args: Args) => CreateAppConfig);
|
app?: CreateAppConfig | ((args: Args) => CreateAppConfig);
|
||||||
onBuilt?: (app: App) => MaybePromise<void>;
|
onBuilt?: (app: App) => Promise<void>;
|
||||||
beforeBuild?: (app: App) => MaybePromise<void>;
|
beforeBuild?: (app: App) => Promise<void>;
|
||||||
buildConfig?: Parameters<App["build"]>[0];
|
buildConfig?: Parameters<App["build"]>[0];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,8 @@ export async function createApp<Env = NodeEnv>(
|
|||||||
console.warn("relativeDistPath is deprecated, please use distPath instead");
|
console.warn("relativeDistPath is deprecated, please use distPath instead");
|
||||||
}
|
}
|
||||||
|
|
||||||
const app = await createRuntimeApp(
|
registerLocalMediaAdapter();
|
||||||
|
return await createRuntimeApp(
|
||||||
{
|
{
|
||||||
...config,
|
...config,
|
||||||
serveStatic: serveStatic({ root }),
|
serveStatic: serveStatic({ root }),
|
||||||
@@ -38,8 +39,6 @@ export async function createApp<Env = NodeEnv>(
|
|||||||
args ?? { env: process.env },
|
args ?? { env: process.env },
|
||||||
opts,
|
opts,
|
||||||
);
|
);
|
||||||
registerLocalMediaAdapter(app);
|
|
||||||
return app;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createHandler<Env = NodeEnv>(
|
export function createHandler<Env = NodeEnv>(
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { readFile, readdir, stat, unlink, writeFile } from "node:fs/promises";
|
|||||||
import { isFile } from "bknd/utils";
|
import { isFile } from "bknd/utils";
|
||||||
import type { FileBody, FileListObject, FileMeta, FileUploadPayload } from "bknd/media";
|
import type { FileBody, FileListObject, FileMeta, FileUploadPayload } from "bknd/media";
|
||||||
import { StorageAdapter, guessMimeType as guess } from "bknd/media";
|
import { StorageAdapter, guessMimeType as guess } from "bknd/media";
|
||||||
import { parse, s } from "core/object/schema";
|
import { parse, s } from "bknd/core";
|
||||||
|
|
||||||
export const localAdapterConfig = s.object(
|
export const localAdapterConfig = s.object(
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
import type { App } from "bknd";
|
import { registries } from "bknd";
|
||||||
import { type LocalAdapterConfig, StorageLocalAdapter } from "./StorageLocalAdapter";
|
import { type LocalAdapterConfig, StorageLocalAdapter } from "./StorageLocalAdapter";
|
||||||
|
|
||||||
export * from "./StorageLocalAdapter";
|
export * from "./StorageLocalAdapter";
|
||||||
|
|
||||||
export function registerLocalMediaAdapter(app: App) {
|
let registered = false;
|
||||||
app.module.media.adapters.set("local", StorageLocalAdapter);
|
export function registerLocalMediaAdapter() {
|
||||||
|
if (!registered) {
|
||||||
|
registries.media.register("local", StorageLocalAdapter);
|
||||||
|
registered = true;
|
||||||
|
}
|
||||||
|
|
||||||
return (config: Partial<LocalAdapterConfig> = {}) => {
|
return (config: Partial<LocalAdapterConfig> = {}) => {
|
||||||
const adapter = new StorageLocalAdapter(config);
|
const adapter = new StorageLocalAdapter(config);
|
||||||
|
|||||||
@@ -32,7 +32,8 @@ async function createApp<ViteEnv>(
|
|||||||
env: ViteEnv = {} as ViteEnv,
|
env: ViteEnv = {} as ViteEnv,
|
||||||
opts: FrameworkOptions = {},
|
opts: FrameworkOptions = {},
|
||||||
): Promise<App> {
|
): Promise<App> {
|
||||||
const app = await createRuntimeApp(
|
registerLocalMediaAdapter();
|
||||||
|
return await createRuntimeApp(
|
||||||
{
|
{
|
||||||
...config,
|
...config,
|
||||||
adminOptions: config.adminOptions ?? {
|
adminOptions: config.adminOptions ?? {
|
||||||
@@ -48,8 +49,6 @@ async function createApp<ViteEnv>(
|
|||||||
env,
|
env,
|
||||||
opts,
|
opts,
|
||||||
);
|
);
|
||||||
registerLocalMediaAdapter(app);
|
|
||||||
return app;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function serve<ViteEnv>(
|
export function serve<ViteEnv>(
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { transformObject } from "core/utils";
|
|||||||
import { DataPermissions } from "data";
|
import { DataPermissions } from "data";
|
||||||
import type { Hono } from "hono";
|
import type { Hono } from "hono";
|
||||||
import { Controller, type ServerEnv } from "modules/Controller";
|
import { Controller, type ServerEnv } from "modules/Controller";
|
||||||
import { describeRoute, jsc, s, parse, InvalidSchemaError } from "core/object/schema";
|
import { describeRoute, jsc, s, parse, InvalidSchemaError } from "bknd/core";
|
||||||
|
|
||||||
export type AuthActionResponse = {
|
export type AuthActionResponse = {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { cookieConfig, jwtConfig } from "auth/authenticate/Authenticator";
|
import { cookieConfig, jwtConfig } from "auth/authenticate/Authenticator";
|
||||||
import { CustomOAuthStrategy, OAuthStrategy, PasswordStrategy } from "auth/authenticate/strategies";
|
import { CustomOAuthStrategy, OAuthStrategy, PasswordStrategy } from "auth/authenticate/strategies";
|
||||||
import { objectTransform } from "core/utils";
|
import { objectTransform } from "core/utils";
|
||||||
import { s } from "core/object/schema";
|
import { s } from "bknd/core";
|
||||||
|
|
||||||
export const Strategies = {
|
export const Strategies = {
|
||||||
password: {
|
password: {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import type { CookieOptions } from "hono/utils/cookie";
|
|||||||
import type { ServerEnv } from "modules/Controller";
|
import type { ServerEnv } from "modules/Controller";
|
||||||
import { pick } from "lodash-es";
|
import { pick } from "lodash-es";
|
||||||
import { InvalidConditionsException } from "auth/errors";
|
import { InvalidConditionsException } from "auth/errors";
|
||||||
import { s, parse, secret } from "core/object/schema";
|
import { s, parse, secret } from "bknd/core";
|
||||||
|
|
||||||
type Input = any; // workaround
|
type Input = any; // workaround
|
||||||
export type JWTPayload = Parameters<typeof sign>[0];
|
export type JWTPayload = Parameters<typeof sign>[0];
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { hash, $console } from "core/utils";
|
|||||||
import { Hono } from "hono";
|
import { Hono } from "hono";
|
||||||
import { compare as bcryptCompare, genSalt as bcryptGenSalt, hash as bcryptHash } from "bcryptjs";
|
import { compare as bcryptCompare, genSalt as bcryptGenSalt, hash as bcryptHash } from "bcryptjs";
|
||||||
import { Strategy } from "./Strategy";
|
import { Strategy } from "./Strategy";
|
||||||
import { s, parse, jsc } from "core/object/schema";
|
import { s, parse, jsc } from "bknd/core";
|
||||||
|
|
||||||
const schema = s
|
const schema = s
|
||||||
.object({
|
.object({
|
||||||
@@ -33,7 +33,7 @@ export class PasswordStrategy extends Strategy<typeof schema> {
|
|||||||
private getPayloadSchema() {
|
private getPayloadSchema() {
|
||||||
return s.object({
|
return s.object({
|
||||||
email: s.string({
|
email: s.string({
|
||||||
pattern: /^[\w-\.\+_]+@([\w-]+\.)+[\w-]{2,4}$/,
|
format: "email",
|
||||||
}),
|
}),
|
||||||
password: s.string({
|
password: s.string({
|
||||||
minLength: 8, // @todo: this should be configurable
|
minLength: 8, // @todo: this should be configurable
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import type {
|
|||||||
StrategyActions,
|
StrategyActions,
|
||||||
} from "../Authenticator";
|
} from "../Authenticator";
|
||||||
import type { Hono } from "hono";
|
import type { Hono } from "hono";
|
||||||
import { type s, parse } from "core/object/schema";
|
import { type s, parse } from "bknd/core";
|
||||||
|
|
||||||
export type StrategyMode = "form" | "external";
|
export type StrategyMode = "form" | "external";
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type * as oauth from "oauth4webapi";
|
import type * as oauth from "oauth4webapi";
|
||||||
import { OAuthStrategy } from "./OAuthStrategy";
|
import { OAuthStrategy } from "./OAuthStrategy";
|
||||||
import { s } from "core/object/schema";
|
import { s } from "bknd/core";
|
||||||
|
|
||||||
type SupportedTypes = "oauth2" | "oidc";
|
type SupportedTypes = "oauth2" | "oidc";
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { getSignedCookie, setSignedCookie } from "hono/cookie";
|
|||||||
import * as oauth from "oauth4webapi";
|
import * as oauth from "oauth4webapi";
|
||||||
import * as issuers from "./issuers";
|
import * as issuers from "./issuers";
|
||||||
import { Strategy } from "auth/authenticate/strategies/Strategy";
|
import { Strategy } from "auth/authenticate/strategies/Strategy";
|
||||||
import { s } from "core/object/schema";
|
import { s } from "bknd/core";
|
||||||
|
|
||||||
type ConfiguredIssuers = keyof typeof issuers;
|
type ConfiguredIssuers = keyof typeof issuers;
|
||||||
type SupportedTypes = "oauth2" | "oidc";
|
type SupportedTypes = "oauth2" | "oidc";
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import type { Config } from "@libsql/client/node";
|
import type { Config } from "@libsql/client/node";
|
||||||
import type { App, CreateAppConfig } from "App";
|
import type { App, CreateAppConfig } from "App";
|
||||||
import { registerLocalMediaAdapter } from "adapter/node/storage";
|
import { StorageLocalAdapter } from "adapter/node/storage";
|
||||||
import type { CliBkndConfig, CliCommand } from "cli/types";
|
import type { CliBkndConfig, CliCommand } from "cli/types";
|
||||||
import { Option } from "commander";
|
import { Option } from "commander";
|
||||||
import { config } from "core";
|
import { config } from "core";
|
||||||
import dotenv from "dotenv";
|
import dotenv from "dotenv";
|
||||||
|
import { registries } from "modules/registries";
|
||||||
import c from "picocolors";
|
import c from "picocolors";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import {
|
import {
|
||||||
@@ -56,6 +57,12 @@ export const run: CliCommand = (program) => {
|
|||||||
.action(action);
|
.action(action);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// automatically register local adapter
|
||||||
|
const local = StorageLocalAdapter.prototype.getName();
|
||||||
|
if (!registries.media.has(local)) {
|
||||||
|
registries.media.register(local, StorageLocalAdapter);
|
||||||
|
}
|
||||||
|
|
||||||
type MakeAppConfig = {
|
type MakeAppConfig = {
|
||||||
connection?: CreateAppConfig["connection"];
|
connection?: CreateAppConfig["connection"];
|
||||||
server?: { platform?: Platform };
|
server?: { platform?: Platform };
|
||||||
@@ -64,12 +71,10 @@ type MakeAppConfig = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
async function makeApp(config: MakeAppConfig) {
|
async function makeApp(config: MakeAppConfig) {
|
||||||
const app = await createRuntimeApp({
|
return await createRuntimeApp({
|
||||||
serveStatic: await serveStatic(config.server?.platform ?? "node"),
|
serveStatic: await serveStatic(config.server?.platform ?? "node"),
|
||||||
...config,
|
...config,
|
||||||
});
|
});
|
||||||
registerLocalMediaAdapter(app);
|
|
||||||
return app;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function makeConfigApp(_config: CliBkndConfig, platform?: Platform) {
|
export async function makeConfigApp(_config: CliBkndConfig, platform?: Platform) {
|
||||||
|
|||||||
@@ -25,8 +25,12 @@ export {
|
|||||||
} from "./object/query/query";
|
} from "./object/query/query";
|
||||||
export { Registry, type Constructor } from "./registry/Registry";
|
export { Registry, type Constructor } from "./registry/Registry";
|
||||||
export { getFlashMessage } from "./server/flash";
|
export { getFlashMessage } from "./server/flash";
|
||||||
/* export {
|
export {
|
||||||
s,
|
s,
|
||||||
|
stripMark,
|
||||||
|
mark,
|
||||||
|
stringIdentifier,
|
||||||
|
secret,
|
||||||
parse,
|
parse,
|
||||||
jsc,
|
jsc,
|
||||||
describeRoute,
|
describeRoute,
|
||||||
@@ -34,7 +38,8 @@ export { getFlashMessage } from "./server/flash";
|
|||||||
openAPISpecs,
|
openAPISpecs,
|
||||||
type ParseOptions,
|
type ParseOptions,
|
||||||
InvalidSchemaError,
|
InvalidSchemaError,
|
||||||
} from "./object/schema"; */
|
} from "./object/schema";
|
||||||
|
export * as $diff from "./object/diff";
|
||||||
|
|
||||||
export * from "./drivers";
|
export * from "./drivers";
|
||||||
export * from "./events";
|
export * from "./events";
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { get, has, omit, set } from "lodash-es";
|
import { get, has, omit, set } from "lodash-es";
|
||||||
import { getFullPathKeys, mergeObjectWith } from "../utils";
|
import { getFullPathKeys, mergeObjectWith } from "../utils";
|
||||||
import { type s, parse, stripMark } from "core/object/schema";
|
import { type s, parse, stripMark } from "bknd/core";
|
||||||
|
|
||||||
export type SchemaObjectOptions<Schema extends s.Schema> = {
|
export type SchemaObjectOptions<Schema extends s.Schema> = {
|
||||||
onUpdate?: (config: s.Static<Schema>) => void | Promise<void>;
|
onUpdate?: (config: s.Static<Schema>) => void | Promise<void>;
|
||||||
@@ -29,7 +29,7 @@ export class SchemaObject<Schema extends TSchema = TSchema> {
|
|||||||
this._default = _schema.template({}, { withOptional: true }) as any;
|
this._default = _schema.template({}, { withOptional: true }) as any;
|
||||||
this._value = parse(_schema, structuredClone(initial ?? {}), {
|
this._value = parse(_schema, structuredClone(initial ?? {}), {
|
||||||
withDefaults: true,
|
withDefaults: true,
|
||||||
//withExtendedDefaults: true,
|
withExtendedDefaults: true,
|
||||||
forceParse: this.isForceParse(),
|
forceParse: this.isForceParse(),
|
||||||
skipMark: this.isForceParse(),
|
skipMark: this.isForceParse(),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import {
|
|||||||
import type { Handler } from "hono/types";
|
import type { Handler } from "hono/types";
|
||||||
import type { ModuleBuildContext } from "modules";
|
import type { ModuleBuildContext } from "modules";
|
||||||
import { Controller } from "modules/Controller";
|
import { Controller } from "modules/Controller";
|
||||||
import { jsc, s, describeRoute, schemaToSpec } from "core/object/schema";
|
import { jsc, s, describeRoute, schemaToSpec } from "bknd/core";
|
||||||
import * as SystemPermissions from "modules/permissions";
|
import * as SystemPermissions from "modules/permissions";
|
||||||
import type { AppDataConfig } from "../data-schema";
|
import type { AppDataConfig } from "../data-schema";
|
||||||
import { omitKeys } from "core/utils";
|
import { omitKeys } from "core/utils";
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { FieldClassMap } from "data/fields";
|
|||||||
import { RelationClassMap, RelationFieldClassMap } from "data/relations";
|
import { RelationClassMap, RelationFieldClassMap } from "data/relations";
|
||||||
import { entityConfigSchema, entityTypes } from "data/entities";
|
import { entityConfigSchema, entityTypes } from "data/entities";
|
||||||
import { primaryFieldTypes } from "./fields";
|
import { primaryFieldTypes } from "./fields";
|
||||||
import { s } from "core/object/schema";
|
import { s } from "bknd/core";
|
||||||
|
|
||||||
export const FIELDS = {
|
export const FIELDS = {
|
||||||
...FieldClassMap,
|
...FieldClassMap,
|
||||||
@@ -18,6 +18,7 @@ export const RELATIONS = RelationClassMap;
|
|||||||
export const fieldsSchemaObject = objectTransform(FIELDS, (field, name) => {
|
export const fieldsSchemaObject = objectTransform(FIELDS, (field, name) => {
|
||||||
return s.strictObject(
|
return s.strictObject(
|
||||||
{
|
{
|
||||||
|
name: s.string().optional(), // @todo: verify, old schema wasn't strict (req in UI)
|
||||||
type: s.literal(name),
|
type: s.literal(name),
|
||||||
config: field.schema.optional(),
|
config: field.schema.optional(),
|
||||||
},
|
},
|
||||||
@@ -32,9 +33,10 @@ export type TAppDataField = s.Static<typeof fieldsSchema>;
|
|||||||
export type TAppDataEntityFields = s.Static<typeof entityFields>;
|
export type TAppDataEntityFields = s.Static<typeof entityFields>;
|
||||||
|
|
||||||
export const entitiesSchema = s.strictObject({
|
export const entitiesSchema = s.strictObject({
|
||||||
type: s.string({ enum: entityTypes, default: "regular", readOnly: true }),
|
name: s.string().optional(), // @todo: verify, old schema wasn't strict (req in UI)
|
||||||
config: entityConfigSchema.optional(),
|
type: s.string({ enum: entityTypes, default: "regular" }),
|
||||||
fields: entityFields.optional(),
|
config: entityConfigSchema,
|
||||||
|
fields: entityFields,
|
||||||
});
|
});
|
||||||
export type TAppDataEntity = s.Static<typeof entitiesSchema>;
|
export type TAppDataEntity = s.Static<typeof entitiesSchema>;
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
type TActionContext,
|
type TActionContext,
|
||||||
type TRenderContext,
|
type TRenderContext,
|
||||||
} from "../fields";
|
} from "../fields";
|
||||||
import { s, parse } from "core/object/schema";
|
import { s, parse } from "bknd/core";
|
||||||
|
|
||||||
// @todo: entity must be migrated to typebox
|
// @todo: entity must be migrated to typebox
|
||||||
export const entityConfigSchema = s
|
export const entityConfigSchema = s
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Exception } from "core";
|
import { Exception } from "core";
|
||||||
import { HttpStatus } from "core/utils";
|
import { HttpStatus } from "core/utils";
|
||||||
import type { InvalidSchemaError } from "core/object/schema";
|
import type { InvalidSchemaError } from "bknd/core";
|
||||||
import type { Entity } from "./entities";
|
import type { Entity } from "./entities";
|
||||||
import type { Field } from "./fields";
|
import type { Field } from "./fields";
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { omitKeys } from "core/utils";
|
|||||||
import type { EntityManager } from "data";
|
import type { EntityManager } from "data";
|
||||||
import { TransformPersistFailedException } from "../errors";
|
import { TransformPersistFailedException } from "../errors";
|
||||||
import { Field, type TActionContext, type TRenderContext, baseFieldConfigSchema } from "./Field";
|
import { Field, type TActionContext, type TRenderContext, baseFieldConfigSchema } from "./Field";
|
||||||
import { s } from "core/object/schema";
|
import { s } from "bknd/core";
|
||||||
|
|
||||||
export const booleanFieldConfigSchema = s
|
export const booleanFieldConfigSchema = s
|
||||||
.strictObject({
|
.strictObject({
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import type { EntityManager } from "../entities";
|
|||||||
import { Field, type TActionContext, type TRenderContext, baseFieldConfigSchema } from "./Field";
|
import { Field, type TActionContext, type TRenderContext, baseFieldConfigSchema } from "./Field";
|
||||||
import { $console } from "core/utils";
|
import { $console } from "core/utils";
|
||||||
import type { TFieldTSType } from "data/entities/EntityTypescript";
|
import type { TFieldTSType } from "data/entities/EntityTypescript";
|
||||||
import { s } from "core/object/schema";
|
import { s } from "bknd/core";
|
||||||
|
|
||||||
export const dateFieldConfigSchema = s
|
export const dateFieldConfigSchema = s
|
||||||
.strictObject({
|
.strictObject({
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import type { EntityManager } from "data";
|
|||||||
import { TransformPersistFailedException } from "../errors";
|
import { TransformPersistFailedException } from "../errors";
|
||||||
import { baseFieldConfigSchema, Field, type TActionContext, type TRenderContext } from "./Field";
|
import { baseFieldConfigSchema, Field, type TActionContext, type TRenderContext } from "./Field";
|
||||||
import type { TFieldTSType } from "data/entities/EntityTypescript";
|
import type { TFieldTSType } from "data/entities/EntityTypescript";
|
||||||
import { s } from "core/object/schema";
|
import { s } from "bknd/core";
|
||||||
|
|
||||||
export const enumFieldConfigSchema = s
|
export const enumFieldConfigSchema = s
|
||||||
.strictObject({
|
.strictObject({
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import type { EntityManager } from "../entities";
|
|||||||
import { InvalidFieldConfigException, TransformPersistFailedException } from "../errors";
|
import { InvalidFieldConfigException, TransformPersistFailedException } from "../errors";
|
||||||
import type { FieldSpec } from "data/connection/Connection";
|
import type { FieldSpec } from "data/connection/Connection";
|
||||||
import type { TFieldTSType } from "data/entities/EntityTypescript";
|
import type { TFieldTSType } from "data/entities/EntityTypescript";
|
||||||
import { s, parse, InvalidSchemaError } from "core/object/schema";
|
import { s, parse, InvalidSchemaError } from "bknd/core";
|
||||||
|
|
||||||
// @todo: contexts need to be reworked
|
// @todo: contexts need to be reworked
|
||||||
// e.g. "table" is irrelevant, because if read is not given, it fails
|
// e.g. "table" is irrelevant, because if read is not given, it fails
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import type { EntityManager } from "data";
|
|||||||
import { TransformPersistFailedException } from "../errors";
|
import { TransformPersistFailedException } from "../errors";
|
||||||
import { Field, type TActionContext, type TRenderContext, baseFieldConfigSchema } from "./Field";
|
import { Field, type TActionContext, type TRenderContext, baseFieldConfigSchema } from "./Field";
|
||||||
import type { TFieldTSType } from "data/entities/EntityTypescript";
|
import type { TFieldTSType } from "data/entities/EntityTypescript";
|
||||||
import { s } from "core/object/schema";
|
import { s } from "bknd/core";
|
||||||
|
|
||||||
export const jsonFieldConfigSchema = s
|
export const jsonFieldConfigSchema = s
|
||||||
.strictObject({
|
.strictObject({
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import type { EntityManager } from "data";
|
|||||||
import { TransformPersistFailedException } from "../errors";
|
import { TransformPersistFailedException } from "../errors";
|
||||||
import { Field, type TActionContext, type TRenderContext, baseFieldConfigSchema } from "./Field";
|
import { Field, type TActionContext, type TRenderContext, baseFieldConfigSchema } from "./Field";
|
||||||
import type { TFieldTSType } from "data/entities/EntityTypescript";
|
import type { TFieldTSType } from "data/entities/EntityTypescript";
|
||||||
import { s } from "core/object/schema";
|
import { s } from "bknd/core";
|
||||||
|
|
||||||
export const jsonSchemaFieldConfigSchema = s
|
export const jsonSchemaFieldConfigSchema = s
|
||||||
.strictObject({
|
.strictObject({
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import type { EntityManager } from "data";
|
|||||||
import { TransformPersistFailedException } from "../errors";
|
import { TransformPersistFailedException } from "../errors";
|
||||||
import { Field, type TActionContext, type TRenderContext, baseFieldConfigSchema } from "./Field";
|
import { Field, type TActionContext, type TRenderContext, baseFieldConfigSchema } from "./Field";
|
||||||
import type { TFieldTSType } from "data/entities/EntityTypescript";
|
import type { TFieldTSType } from "data/entities/EntityTypescript";
|
||||||
import { s } from "core/object/schema";
|
import { s } from "bknd/core";
|
||||||
import { omitKeys } from "core/utils";
|
import { omitKeys } from "core/utils";
|
||||||
|
|
||||||
export const numberFieldConfigSchema = s
|
export const numberFieldConfigSchema = s
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { config } from "core";
|
|||||||
import { omitKeys, uuidv7 } from "core/utils";
|
import { omitKeys, uuidv7 } from "core/utils";
|
||||||
import { Field, baseFieldConfigSchema } from "./Field";
|
import { Field, baseFieldConfigSchema } from "./Field";
|
||||||
import type { TFieldTSType } from "data/entities/EntityTypescript";
|
import type { TFieldTSType } from "data/entities/EntityTypescript";
|
||||||
import { s } from "core/object/schema";
|
import { s } from "bknd/core";
|
||||||
|
|
||||||
export const primaryFieldTypes = ["integer", "uuid"] as const;
|
export const primaryFieldTypes = ["integer", "uuid"] as const;
|
||||||
export type TPrimaryFieldFormat = (typeof primaryFieldTypes)[number];
|
export type TPrimaryFieldFormat = (typeof primaryFieldTypes)[number];
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import type { EntityManager } from "data";
|
|||||||
import { omitKeys } from "core/utils";
|
import { omitKeys } from "core/utils";
|
||||||
import { TransformPersistFailedException } from "../errors";
|
import { TransformPersistFailedException } from "../errors";
|
||||||
import { Field, type TActionContext, baseFieldConfigSchema } from "./Field";
|
import { Field, type TActionContext, baseFieldConfigSchema } from "./Field";
|
||||||
import { s } from "core/object/schema";
|
import { s } from "bknd/core";
|
||||||
|
|
||||||
export const textFieldConfigSchema = s
|
export const textFieldConfigSchema = s
|
||||||
.strictObject({
|
.strictObject({
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Field, baseFieldConfigSchema } from "./Field";
|
import { Field, baseFieldConfigSchema } from "./Field";
|
||||||
import { s } from "core/object/schema";
|
import { s } from "bknd/core";
|
||||||
|
|
||||||
export const virtualFieldConfigSchema = s
|
export const virtualFieldConfigSchema = s
|
||||||
.strictObject({
|
.strictObject({
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { parse } from "core/object/schema";
|
import { parse } from "bknd/core";
|
||||||
import type { ExpressionBuilder, SelectQueryBuilder } from "kysely";
|
import type { ExpressionBuilder, SelectQueryBuilder } from "kysely";
|
||||||
import type { Entity, EntityData, EntityManager } from "../entities";
|
import type { Entity, EntityData, EntityManager } from "../entities";
|
||||||
import {
|
import {
|
||||||
@@ -9,7 +9,7 @@ import {
|
|||||||
import type { RepoQuery } from "../server/query";
|
import type { RepoQuery } from "../server/query";
|
||||||
import type { RelationType } from "./relation-types";
|
import type { RelationType } from "./relation-types";
|
||||||
import type { PrimaryFieldType } from "core";
|
import type { PrimaryFieldType } from "core";
|
||||||
import { s } from "core/object/schema";
|
import { s } from "bknd/core";
|
||||||
|
|
||||||
const directions = ["source", "target"] as const;
|
const directions = ["source", "target"] as const;
|
||||||
export type TDirection = (typeof directions)[number];
|
export type TDirection = (typeof directions)[number];
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { EntityRelation, type KyselyQueryBuilder } from "./EntityRelation";
|
|||||||
import { EntityRelationAnchor } from "./EntityRelationAnchor";
|
import { EntityRelationAnchor } from "./EntityRelationAnchor";
|
||||||
import { RelationField } from "./RelationField";
|
import { RelationField } from "./RelationField";
|
||||||
import { type RelationType, RelationTypes } from "./relation-types";
|
import { type RelationType, RelationTypes } from "./relation-types";
|
||||||
import { s } from "core/object/schema";
|
import { s } from "bknd/core";
|
||||||
|
|
||||||
export type ManyToManyRelationConfig = s.Static<typeof ManyToManyRelation.schema>;
|
export type ManyToManyRelationConfig = s.Static<typeof ManyToManyRelation.schema>;
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { EntityRelationAnchor } from "./EntityRelationAnchor";
|
|||||||
import { RelationField, type RelationFieldBaseConfig } from "./RelationField";
|
import { RelationField, type RelationFieldBaseConfig } from "./RelationField";
|
||||||
import type { MutationInstructionResponse } from "./RelationMutator";
|
import type { MutationInstructionResponse } from "./RelationMutator";
|
||||||
import { type RelationType, RelationTypes } from "./relation-types";
|
import { type RelationType, RelationTypes } from "./relation-types";
|
||||||
import { s } from "core/object/schema";
|
import { s } from "bknd/core";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Source entity receives the mapping field
|
* Source entity receives the mapping field
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import type { RepoQuery } from "../server/query";
|
|||||||
import { EntityRelation, type KyselyJsonFrom, type KyselyQueryBuilder } from "./EntityRelation";
|
import { EntityRelation, type KyselyJsonFrom, type KyselyQueryBuilder } from "./EntityRelation";
|
||||||
import { EntityRelationAnchor } from "./EntityRelationAnchor";
|
import { EntityRelationAnchor } from "./EntityRelationAnchor";
|
||||||
import { type RelationType, RelationTypes } from "./relation-types";
|
import { type RelationType, RelationTypes } from "./relation-types";
|
||||||
import { s } from "core/object/schema";
|
import { s } from "bknd/core";
|
||||||
|
|
||||||
export type PolymorphicRelationConfig = s.Static<typeof PolymorphicRelation.schema>;
|
export type PolymorphicRelationConfig = s.Static<typeof PolymorphicRelation.schema>;
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { Field, baseFieldConfigSchema } from "../fields";
|
|||||||
import type { EntityRelation } from "./EntityRelation";
|
import type { EntityRelation } from "./EntityRelation";
|
||||||
import type { EntityRelationAnchor } from "./EntityRelationAnchor";
|
import type { EntityRelationAnchor } from "./EntityRelationAnchor";
|
||||||
import type { TFieldTSType } from "data/entities/EntityTypescript";
|
import type { TFieldTSType } from "data/entities/EntityTypescript";
|
||||||
import { s } from "core/object/schema";
|
import { s } from "bknd/core";
|
||||||
|
|
||||||
const CASCADES = ["cascade", "set null", "set default", "restrict", "no action"] as const;
|
const CASCADES = ["cascade", "set null", "set default", "restrict", "no action"] as const;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { test, describe, expect } from "bun:test";
|
import { test, describe, expect } from "bun:test";
|
||||||
import * as q from "./query";
|
import * as q from "./query";
|
||||||
import { s as schema, parse as $parse, type ParseOptions } from "core/object/schema";
|
import { s as schema, parse as $parse, type ParseOptions } from "bknd/core";
|
||||||
|
|
||||||
const parse = (v: unknown, o: ParseOptions = {}) =>
|
const parse = (v: unknown, o: ParseOptions = {}) =>
|
||||||
$parse(q.repoQuery, v, {
|
$parse(q.repoQuery, v, {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { s } from "core/object/schema";
|
import { s } from "bknd/core";
|
||||||
import { WhereBuilder, type WhereQuery } from "data/entities/query/WhereBuilder";
|
import { WhereBuilder, type WhereQuery } from "data/entities/query/WhereBuilder";
|
||||||
import { isObject, $console } from "core/utils";
|
import { isObject, $console } from "core/utils";
|
||||||
import type { anyOf, CoercionOptions, Schema } from "jsonv-ts";
|
import type { anyOf, CoercionOptions, Schema } from "jsonv-ts";
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { Flow, HttpTrigger } from "flows";
|
|||||||
import { Hono } from "hono";
|
import { Hono } from "hono";
|
||||||
import { Module } from "modules/Module";
|
import { Module } from "modules/Module";
|
||||||
import { TASKS, flowsConfigSchema } from "./flows-schema";
|
import { TASKS, flowsConfigSchema } from "./flows-schema";
|
||||||
import type { s } from "core/object/schema";
|
import type { s } from "bknd/core";
|
||||||
|
|
||||||
export type AppFlowsSchema = s.Static<typeof flowsConfigSchema>;
|
export type AppFlowsSchema = s.Static<typeof flowsConfigSchema>;
|
||||||
export type TAppFlowSchema = AppFlowsSchema["flows"][number];
|
export type TAppFlowSchema = AppFlowsSchema["flows"][number];
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { transformObject } from "core/utils";
|
import { transformObject } from "core/utils";
|
||||||
import { TaskMap, TriggerMap } from "flows";
|
import { TaskMap, TriggerMap } from "flows";
|
||||||
import { s } from "core/object/schema";
|
import { s } from "bknd/core";
|
||||||
|
|
||||||
export const TASKS = {
|
export const TASKS = {
|
||||||
...TaskMap,
|
...TaskMap,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import type { EventManager } from "core/events";
|
|||||||
import type { Flow } from "../Flow";
|
import type { Flow } from "../Flow";
|
||||||
import { Trigger } from "./Trigger";
|
import { Trigger } from "./Trigger";
|
||||||
import { $console } from "core/utils";
|
import { $console } from "core/utils";
|
||||||
import { s } from "core/object/schema";
|
import { s } from "bknd/core";
|
||||||
|
|
||||||
export class EventTrigger extends Trigger<typeof EventTrigger.schema> {
|
export class EventTrigger extends Trigger<typeof EventTrigger.schema> {
|
||||||
override type = "event";
|
override type = "event";
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { Context, Hono } from "hono";
|
import type { Context, Hono } from "hono";
|
||||||
import type { Flow } from "../Flow";
|
import type { Flow } from "../Flow";
|
||||||
import { Trigger } from "./Trigger";
|
import { Trigger } from "./Trigger";
|
||||||
import { s } from "core/object/schema";
|
import { s } from "bknd/core";
|
||||||
|
|
||||||
const httpMethods = ["GET", "POST", "PUT", "PATCH", "DELETE"] as const;
|
const httpMethods = ["GET", "POST", "PUT", "PATCH", "DELETE"] as const;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { Execution } from "../Execution";
|
import type { Execution } from "../Execution";
|
||||||
import type { Flow } from "../Flow";
|
import type { Flow } from "../Flow";
|
||||||
import { s, parse } from "core/object/schema";
|
import { s, parse } from "bknd/core";
|
||||||
|
|
||||||
export class Trigger<Schema extends typeof Trigger.schema = typeof Trigger.schema> {
|
export class Trigger<Schema extends typeof Trigger.schema = typeof Trigger.schema> {
|
||||||
// @todo: remove this
|
// @todo: remove this
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
//import { BkndError, SimpleRenderer } from "core";
|
//import { BkndError, SimpleRenderer } from "core";
|
||||||
import { BkndError } from "core/errors";
|
import { BkndError } from "core/errors";
|
||||||
|
|
||||||
import { s, parse } from "core/object/schema";
|
import { s, parse } from "bknd/core";
|
||||||
import type { InputsMap } from "../flows/Execution";
|
import type { InputsMap } from "../flows/Execution";
|
||||||
import { SimpleRenderer } from "core/template/SimpleRenderer";
|
import { SimpleRenderer } from "core/template/SimpleRenderer";
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Task, dynamic } from "../Task";
|
import { Task, dynamic } from "../Task";
|
||||||
import { s } from "core/object/schema";
|
import { s } from "bknd/core";
|
||||||
|
|
||||||
const FetchMethods = ["GET", "POST", "PUT", "PATCH", "DELETE"];
|
const FetchMethods = ["GET", "POST", "PUT", "PATCH", "DELETE"];
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Task } from "../Task";
|
import { Task } from "../Task";
|
||||||
import { $console } from "core/utils";
|
import { $console } from "core/utils";
|
||||||
import { s } from "core/object/schema";
|
import { s } from "bknd/core";
|
||||||
|
|
||||||
export class LogTask extends Task<typeof LogTask.schema> {
|
export class LogTask extends Task<typeof LogTask.schema> {
|
||||||
type = "log";
|
type = "log";
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Task } from "../Task";
|
import { Task } from "../Task";
|
||||||
import { s } from "core/object/schema";
|
import { s } from "bknd/core";
|
||||||
|
|
||||||
export class RenderTask<Output extends Record<string, any>> extends Task<
|
export class RenderTask<Output extends Record<string, any>> extends Task<
|
||||||
typeof RenderTask.schema,
|
typeof RenderTask.schema,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Flow } from "../../flows/Flow";
|
import { Flow } from "../../flows/Flow";
|
||||||
import { Task, dynamic } from "../Task";
|
import { Task, dynamic } from "../Task";
|
||||||
import { s } from "core/object/schema";
|
import { s } from "bknd/core";
|
||||||
|
|
||||||
export class SubFlowTask<Output extends Record<string, any>> extends Task<
|
export class SubFlowTask<Output extends Record<string, any>> extends Task<
|
||||||
typeof SubFlowTask.schema,
|
typeof SubFlowTask.schema,
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ export type { ServerEnv } from "modules/Controller";
|
|||||||
export type { BkndConfig } from "bknd/adapter";
|
export type { BkndConfig } from "bknd/adapter";
|
||||||
|
|
||||||
export * as middlewares from "modules/middlewares";
|
export * as middlewares from "modules/middlewares";
|
||||||
|
export { registries } from "modules/registries";
|
||||||
|
|
||||||
export type { MediaFieldSchema } from "media/AppMedia";
|
export type { MediaFieldSchema } from "media/AppMedia";
|
||||||
export type { UserFieldSchema } from "auth/AppAuth";
|
export type { UserFieldSchema } from "auth/AppAuth";
|
||||||
|
|||||||
@@ -1,15 +1,12 @@
|
|||||||
import type { AppEntity, Constructor } from "core";
|
import type { AppEntity } from "core";
|
||||||
import { $console, objectTransform } from "core/utils";
|
import { $console } from "core/utils";
|
||||||
import type { Entity, EntityManager } from "data";
|
import type { Entity, EntityManager } from "data";
|
||||||
import { type FileUploadedEventData, Storage, type StorageAdapter, MediaPermissions } from "media";
|
import { type FileUploadedEventData, Storage, type StorageAdapter, MediaPermissions } from "media";
|
||||||
import { Module, type ModuleBuildContext } from "modules/Module";
|
import { Module } from "modules/Module";
|
||||||
import { type FieldSchema, em, entity } from "../data/prototype";
|
import { type FieldSchema, em, entity } from "../data/prototype";
|
||||||
import { MediaController } from "./api/MediaController";
|
import { MediaController } from "./api/MediaController";
|
||||||
import { mediaConfigSchema, type TAppMediaConfig } from "./media-schema";
|
import { buildMediaSchema, type mediaConfigSchema, registry, type TAppMediaConfig } from "./media-schema";
|
||||||
import { mediaFields } from "./media-entities";
|
import { mediaFields } from "./media-entities";
|
||||||
import { StorageS3Adapter } from "media/storage/adapters/s3/StorageS3Adapter";
|
|
||||||
import { StorageCloudinaryAdapter } from "media/storage/adapters/cloudinary/StorageCloudinaryAdapter";
|
|
||||||
import { s } from "core/object/schema";
|
|
||||||
|
|
||||||
export type MediaFieldSchema = FieldSchema<typeof AppMedia.mediaFields>;
|
export type MediaFieldSchema = FieldSchema<typeof AppMedia.mediaFields>;
|
||||||
declare module "core" {
|
declare module "core" {
|
||||||
@@ -18,18 +15,10 @@ declare module "core" {
|
|||||||
media: Media;
|
media: Media;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
type ClassThatImplements<T> = Constructor<T> & { prototype: T };
|
|
||||||
|
|
||||||
// @todo: current workaround to make it all required
|
// @todo: current workaround to make it all required
|
||||||
export class AppMedia extends Module<Required<TAppMediaConfig>> {
|
export class AppMedia extends Module<Required<TAppMediaConfig>> {
|
||||||
private _storage?: Storage;
|
private _storage?: Storage;
|
||||||
adapters: Map<string, ClassThatImplements<StorageAdapter>> = new Map();
|
|
||||||
|
|
||||||
constructor(initial?: Partial<TAppMediaConfig>, _ctx?: ModuleBuildContext) {
|
|
||||||
super(initial, _ctx);
|
|
||||||
this.adapters.set("s3", StorageS3Adapter);
|
|
||||||
this.adapters.set("cloudinary", StorageCloudinaryAdapter);
|
|
||||||
}
|
|
||||||
|
|
||||||
override async build() {
|
override async build() {
|
||||||
if (!this.config.enabled) {
|
if (!this.config.enabled) {
|
||||||
@@ -46,11 +35,7 @@ export class AppMedia extends Module<Required<TAppMediaConfig>> {
|
|||||||
let adapter: StorageAdapter;
|
let adapter: StorageAdapter;
|
||||||
try {
|
try {
|
||||||
const { type, config } = this.config.adapter;
|
const { type, config } = this.config.adapter;
|
||||||
const cls = this.adapters.get(type as any);
|
const cls = registry.get(type as any).cls;
|
||||||
if (!cls) {
|
|
||||||
throw new Error(`Adapter ${type} not found`);
|
|
||||||
}
|
|
||||||
|
|
||||||
adapter = new cls(config as any);
|
adapter = new cls(config as any);
|
||||||
|
|
||||||
this._storage = new Storage(adapter, this.config.storage, this.ctx.emgr);
|
this._storage = new Storage(adapter, this.config.storage, this.ctx.emgr);
|
||||||
@@ -74,34 +59,7 @@ export class AppMedia extends Module<Required<TAppMediaConfig>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getSchema() {
|
getSchema() {
|
||||||
if (!this.adapters) {
|
return buildMediaSchema();
|
||||||
return mediaConfigSchema;
|
|
||||||
}
|
|
||||||
const adapterSchemaObject = objectTransform(
|
|
||||||
Object.fromEntries(this.adapters.entries()),
|
|
||||||
(adapter, name) => {
|
|
||||||
const schema = adapter.prototype.getSchema();
|
|
||||||
if (!schema) {
|
|
||||||
throw new Error(`Adapter ${name} has no schema`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return s.strictObject(
|
|
||||||
{
|
|
||||||
type: s.literal(name),
|
|
||||||
config: schema,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: String(schema.title ?? name),
|
|
||||||
description: schema.description,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
return s.strictObject({
|
|
||||||
...mediaConfigSchema.properties,
|
|
||||||
adapter: s.anyOf(Object.values(adapterSchemaObject)).optional(),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get basepath() {
|
get basepath() {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Field, baseFieldConfigSchema } from "data/fields";
|
import { Field, baseFieldConfigSchema } from "data/fields";
|
||||||
import { s } from "core/object/schema";
|
import { s } from "bknd/core";
|
||||||
|
|
||||||
export const mediaFieldConfigSchema = s
|
export const mediaFieldConfigSchema = s
|
||||||
.strictObject({
|
.strictObject({
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { DataPermissions } from "data";
|
|||||||
import { Controller } from "modules/Controller";
|
import { Controller } from "modules/Controller";
|
||||||
import type { AppMedia } from "../AppMedia";
|
import type { AppMedia } from "../AppMedia";
|
||||||
import { MediaField } from "../MediaField";
|
import { MediaField } from "../MediaField";
|
||||||
import { jsc, s, describeRoute } from "core/object/schema";
|
import { jsc, s, describeRoute } from "bknd/core";
|
||||||
|
|
||||||
export class MediaController extends Controller {
|
export class MediaController extends Controller {
|
||||||
constructor(private readonly media: AppMedia) {
|
constructor(private readonly media: AppMedia) {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { type Constructor, Registry } from "core";
|
||||||
|
|
||||||
export { guess as guessMimeType } from "./storage/mime-types-tiny";
|
export { guess as guessMimeType } from "./storage/mime-types-tiny";
|
||||||
export {
|
export {
|
||||||
Storage,
|
Storage,
|
||||||
@@ -13,6 +15,7 @@ import {
|
|||||||
StorageCloudinaryAdapter,
|
StorageCloudinaryAdapter,
|
||||||
} from "./storage/adapters/cloudinary/StorageCloudinaryAdapter";
|
} from "./storage/adapters/cloudinary/StorageCloudinaryAdapter";
|
||||||
import { type S3AdapterConfig, StorageS3Adapter } from "./storage/adapters/s3/StorageS3Adapter";
|
import { type S3AdapterConfig, StorageS3Adapter } from "./storage/adapters/s3/StorageS3Adapter";
|
||||||
|
import type { s } from "bknd/core";
|
||||||
|
|
||||||
export { StorageAdapter };
|
export { StorageAdapter };
|
||||||
export { StorageS3Adapter, type S3AdapterConfig, StorageCloudinaryAdapter, type CloudinaryConfig };
|
export { StorageS3Adapter, type S3AdapterConfig, StorageCloudinaryAdapter, type CloudinaryConfig };
|
||||||
@@ -22,4 +25,27 @@ export * as MediaPermissions from "./media-permissions";
|
|||||||
export type { FileUploadedEventData } from "./storage/events";
|
export type { FileUploadedEventData } from "./storage/events";
|
||||||
export * from "./utils";
|
export * from "./utils";
|
||||||
|
|
||||||
|
type ClassThatImplements<T> = Constructor<T> & { prototype: T };
|
||||||
|
|
||||||
|
export const MediaAdapterRegistry = new Registry<{
|
||||||
|
cls: ClassThatImplements<StorageAdapter>;
|
||||||
|
schema: s.Schema;
|
||||||
|
}>((cls: ClassThatImplements<StorageAdapter>) => ({
|
||||||
|
cls,
|
||||||
|
schema: cls.prototype.getSchema() as s.Schema,
|
||||||
|
}))
|
||||||
|
.register("s3", StorageS3Adapter)
|
||||||
|
.register("cloudinary", StorageCloudinaryAdapter);
|
||||||
|
|
||||||
|
export const Adapters = {
|
||||||
|
s3: {
|
||||||
|
cls: StorageS3Adapter,
|
||||||
|
schema: StorageS3Adapter.prototype.getSchema(),
|
||||||
|
},
|
||||||
|
cloudinary: {
|
||||||
|
cls: StorageCloudinaryAdapter,
|
||||||
|
schema: StorageCloudinaryAdapter.prototype.getSchema(),
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
export { adapterTestSuite } from "./storage/adapters/adapter-test-suite";
|
export { adapterTestSuite } from "./storage/adapters/adapter-test-suite";
|
||||||
|
|||||||
@@ -1,29 +1,50 @@
|
|||||||
import { s } from "core/object/schema";
|
import { objectTransform } from "core/utils";
|
||||||
|
import { Adapters } from "media";
|
||||||
|
import { registries } from "modules/registries";
|
||||||
|
import { s } from "bknd/core";
|
||||||
|
|
||||||
export const mediaConfigSchema = s.strictObject(
|
export const ADAPTERS = {
|
||||||
{
|
...Adapters,
|
||||||
enabled: s.boolean({ default: false }),
|
} as const;
|
||||||
basepath: s.string({ default: "/api/media" }),
|
|
||||||
entity_name: s.string({ default: "media" }),
|
export const registry = registries.media;
|
||||||
storage: s.strictObject(
|
|
||||||
|
export function buildMediaSchema() {
|
||||||
|
const adapterSchemaObject = objectTransform(registry.all(), (adapter, name) => {
|
||||||
|
return s.strictObject(
|
||||||
{
|
{
|
||||||
body_max_size: s
|
type: s.literal(name),
|
||||||
.number({
|
config: adapter.schema,
|
||||||
description: "Max size of the body in bytes. Leave blank for unlimited.",
|
|
||||||
})
|
|
||||||
.optional(),
|
|
||||||
},
|
},
|
||||||
{ default: {} },
|
{
|
||||||
),
|
title: adapter.schema?.title ?? name,
|
||||||
adapter: s
|
description: adapter.schema?.description,
|
||||||
.strictObject({
|
},
|
||||||
type: s.string(),
|
);
|
||||||
config: s.any(),
|
});
|
||||||
})
|
|
||||||
.optional(),
|
return s.strictObject(
|
||||||
},
|
{
|
||||||
{
|
enabled: s.boolean({ default: false }),
|
||||||
default: {},
|
basepath: s.string({ default: "/api/media" }),
|
||||||
},
|
entity_name: s.string({ default: "media" }),
|
||||||
);
|
storage: s.strictObject(
|
||||||
|
{
|
||||||
|
body_max_size: s
|
||||||
|
.number({
|
||||||
|
description: "Max size of the body in bytes. Leave blank for unlimited.",
|
||||||
|
})
|
||||||
|
.optional(),
|
||||||
|
},
|
||||||
|
{ default: {} },
|
||||||
|
),
|
||||||
|
adapter: s.anyOf(Object.values(adapterSchemaObject)).optional(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
default: {},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const mediaConfigSchema = buildMediaSchema();
|
||||||
export type TAppMediaConfig = s.Static<typeof mediaConfigSchema>;
|
export type TAppMediaConfig = s.Static<typeof mediaConfigSchema>;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { FileListObject, FileMeta } from "media";
|
import type { FileListObject, FileMeta } from "media";
|
||||||
import type { FileBody, FileUploadPayload } from "media/storage/Storage";
|
import type { FileBody, FileUploadPayload } from "media/storage/Storage";
|
||||||
import type { s } from "core/object/schema";
|
import type { s } from "bknd/core";
|
||||||
|
|
||||||
const SYMBOL = Symbol.for("bknd:storage");
|
const SYMBOL = Symbol.for("bknd:storage");
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { hash, pickHeaders } from "core/utils";
|
import { hash, pickHeaders } from "core/utils";
|
||||||
import type { FileBody, FileListObject, FileMeta } from "../../Storage";
|
import type { FileBody, FileListObject, FileMeta } from "../../Storage";
|
||||||
import { StorageAdapter } from "../../StorageAdapter";
|
import { StorageAdapter } from "../../StorageAdapter";
|
||||||
import { s, parse } from "core/object/schema";
|
import { s, parse } from "bknd/core";
|
||||||
|
|
||||||
export const cloudinaryAdapterConfig = s.object(
|
export const cloudinaryAdapterConfig = s.object(
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { isFile, pickHeaders2 } from "core/utils";
|
|||||||
import { transform } from "lodash-es";
|
import { transform } from "lodash-es";
|
||||||
import type { FileBody, FileListObject } from "../../Storage";
|
import type { FileBody, FileListObject } from "../../Storage";
|
||||||
import { StorageAdapter } from "../../StorageAdapter";
|
import { StorageAdapter } from "../../StorageAdapter";
|
||||||
import { parse, s } from "core/object/schema";
|
import { parse, s } from "bknd/core";
|
||||||
|
|
||||||
export const s3AdapterConfig = s.object(
|
export const s3AdapterConfig = s.object(
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { type Context, type Env, Hono } from "hono";
|
|||||||
import * as middlewares from "modules/middlewares";
|
import * as middlewares from "modules/middlewares";
|
||||||
import type { SafeUser } from "auth";
|
import type { SafeUser } from "auth";
|
||||||
import type { EntityManager } from "data";
|
import type { EntityManager } from "data";
|
||||||
import { s } from "core/object/schema";
|
import { s } from "bknd/core";
|
||||||
|
|
||||||
export interface ServerEnv extends Env {
|
export interface ServerEnv extends Env {
|
||||||
Variables: {
|
Variables: {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import type { Connection, EntityManager } from "data";
|
|||||||
import type { Hono } from "hono";
|
import type { Hono } from "hono";
|
||||||
import type { ServerEnv } from "modules/Controller";
|
import type { ServerEnv } from "modules/Controller";
|
||||||
import type { ModuleHelper } from "./ModuleHelper";
|
import type { ModuleHelper } from "./ModuleHelper";
|
||||||
import type { s } from "core/object/schema";
|
import type { s } from "bknd/core";
|
||||||
|
|
||||||
type PartialRec<T> = { [P in keyof T]?: PartialRec<T[P]> };
|
type PartialRec<T> = { [P in keyof T]?: PartialRec<T[P]> };
|
||||||
|
|
||||||
@@ -38,11 +38,8 @@ export abstract class Module<Schema extends object = object> {
|
|||||||
overwritePaths: this.getOverwritePaths(),
|
overwritePaths: this.getOverwritePaths(),
|
||||||
onBeforeUpdate: this.onBeforeUpdate.bind(this),
|
onBeforeUpdate: this.onBeforeUpdate.bind(this),
|
||||||
});
|
});
|
||||||
this.init();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
init() {}
|
|
||||||
|
|
||||||
static ctx_flags = {
|
static ctx_flags = {
|
||||||
sync_required: false,
|
sync_required: false,
|
||||||
ctx_reload_required: false,
|
ctx_reload_required: false,
|
||||||
|
|||||||
@@ -20,7 +20,8 @@ import { AppMedia } from "../media/AppMedia";
|
|||||||
import type { ServerEnv } from "./Controller";
|
import type { ServerEnv } from "./Controller";
|
||||||
import { Module, type ModuleBuildContext } from "./Module";
|
import { Module, type ModuleBuildContext } from "./Module";
|
||||||
import { ModuleHelper } from "./ModuleHelper";
|
import { ModuleHelper } from "./ModuleHelper";
|
||||||
import { s } from "core/object/schema";
|
import { s } from "bknd/core";
|
||||||
|
import { mark, stripMark } from "bknd/core";
|
||||||
|
|
||||||
export type { ModuleBuildContext };
|
export type { ModuleBuildContext };
|
||||||
|
|
||||||
@@ -80,8 +81,6 @@ export type ModuleManagerOptions = {
|
|||||||
seed?: (ctx: ModuleBuildContext) => Promise<void>;
|
seed?: (ctx: ModuleBuildContext) => Promise<void>;
|
||||||
// called right after modules are built, before finish
|
// called right after modules are built, before finish
|
||||||
onModulesBuilt?: (ctx: ModuleBuildContext) => Promise<void>;
|
onModulesBuilt?: (ctx: ModuleBuildContext) => Promise<void>;
|
||||||
// called right after modules are created, before build
|
|
||||||
onModulesCreated?: (modules: Modules, ctx: ModuleBuildContext) => void;
|
|
||||||
/** @deprecated */
|
/** @deprecated */
|
||||||
verbosity?: Verbosity;
|
verbosity?: Verbosity;
|
||||||
};
|
};
|
||||||
@@ -129,16 +128,16 @@ export class ModuleManagerConfigUpdateEvent<
|
|||||||
}> {
|
}> {
|
||||||
static override slug = "mm-config-update";
|
static override slug = "mm-config-update";
|
||||||
}
|
}
|
||||||
export class ModuleManagerModulesCreatedEvent extends ModuleManagerEvent<{
|
|
||||||
modules: Modules;
|
export class ModuleManagerConfigDiffEvent extends ModuleManagerEvent<{
|
||||||
ctx: ModuleBuildContext;
|
diffs: $diff.DiffEntry[];
|
||||||
}> {
|
}> {
|
||||||
static override slug = "mm-modules-created";
|
static override slug = "mm-config-diff";
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ModuleManagerEvents = {
|
export const ModuleManagerEvents = {
|
||||||
ModuleManagerConfigUpdateEvent,
|
ModuleManagerConfigUpdateEvent,
|
||||||
ModuleManagerModulesCreatedEvent,
|
ModuleManagerConfigDiffEvent,
|
||||||
};
|
};
|
||||||
|
|
||||||
// @todo: cleanup old diffs on upgrade
|
// @todo: cleanup old diffs on upgrade
|
||||||
@@ -176,8 +175,7 @@ export class ModuleManager {
|
|||||||
if ("version" in options.initial) {
|
if ("version" in options.initial) {
|
||||||
const { version, ...initialConfig } = options.initial;
|
const { version, ...initialConfig } = options.initial;
|
||||||
this._version = version;
|
this._version = version;
|
||||||
//initial = stripMark(initialConfig);
|
initial = stripMark(initialConfig);
|
||||||
initial = initialConfig;
|
|
||||||
|
|
||||||
this._booted_with = "provided";
|
this._booted_with = "provided";
|
||||||
} else {
|
} else {
|
||||||
@@ -205,11 +203,6 @@ export class ModuleManager {
|
|||||||
|
|
||||||
this.modules[key] = module;
|
this.modules[key] = module;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.options?.onModulesCreated) {
|
|
||||||
this.options.onModulesCreated(this.modules, context);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.logger.log("modules created");
|
this.logger.log("modules created");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.logger.log("failed to create modules", e);
|
this.logger.log("failed to create modules", e);
|
||||||
@@ -364,6 +357,13 @@ export class ModuleManager {
|
|||||||
// validate diffs, it'll throw on invalid
|
// validate diffs, it'll throw on invalid
|
||||||
this.validateDiffs(diffs);
|
this.validateDiffs(diffs);
|
||||||
|
|
||||||
|
await this.emgr.emit(
|
||||||
|
new ModuleManagerConfigDiffEvent({
|
||||||
|
ctx: this.ctx(),
|
||||||
|
diffs: $diff.clone(diffs),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
const date = new Date();
|
const date = new Date();
|
||||||
// store diff
|
// store diff
|
||||||
await this.mutator().insertOne({
|
await this.mutator().insertOne({
|
||||||
@@ -503,11 +503,10 @@ export class ModuleManager {
|
|||||||
// set version and config from fetched
|
// set version and config from fetched
|
||||||
this._version = result.version;
|
this._version = result.version;
|
||||||
|
|
||||||
// @todo: add back?
|
if (this.options?.trustFetched === true) {
|
||||||
/* if (this.options?.trustFetched === true) {
|
|
||||||
this.logger.log("trusting fetched config (mark)");
|
this.logger.log("trusting fetched config (mark)");
|
||||||
mark(result.json);
|
mark(result.json);
|
||||||
} */
|
}
|
||||||
|
|
||||||
// if version doesn't match, migrate before building
|
// if version doesn't match, migrate before building
|
||||||
if (this.version() !== CURRENT_VERSION) {
|
if (this.version() !== CURRENT_VERSION) {
|
||||||
@@ -749,6 +748,7 @@ export function getDefaultSchema() {
|
|||||||
export function getDefaultConfig(): ModuleConfigs {
|
export function getDefaultConfig(): ModuleConfigs {
|
||||||
const config = transformObject(MODULES, (module) => {
|
const config = transformObject(MODULES, (module) => {
|
||||||
return module.prototype.getSchema().template();
|
return module.prototype.getSchema().template();
|
||||||
|
//return Default(module.prototype.getSchema(), {});
|
||||||
});
|
});
|
||||||
|
|
||||||
return config as any;
|
return config as any;
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { MediaAdapterRegistry } from "media";
|
||||||
|
|
||||||
|
const registries = {
|
||||||
|
media: MediaAdapterRegistry,
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export { registries };
|
||||||
@@ -3,7 +3,7 @@ import { $console } from "core/utils";
|
|||||||
import { cors } from "hono/cors";
|
import { cors } from "hono/cors";
|
||||||
import { Module } from "modules/Module";
|
import { Module } from "modules/Module";
|
||||||
import { AuthException } from "auth/errors";
|
import { AuthException } from "auth/errors";
|
||||||
import { s } from "core/object/schema";
|
import { s } from "bknd/core";
|
||||||
|
|
||||||
const serverMethods = ["GET", "POST", "PATCH", "PUT", "DELETE"] as const;
|
const serverMethods = ["GET", "POST", "PATCH", "PUT", "DELETE"] as const;
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import {
|
|||||||
type ModuleKey,
|
type ModuleKey,
|
||||||
} from "modules/ModuleManager";
|
} from "modules/ModuleManager";
|
||||||
import * as SystemPermissions from "modules/permissions";
|
import * as SystemPermissions from "modules/permissions";
|
||||||
import { jsc, s, describeRoute, InvalidSchemaError } from "core/object/schema";
|
import { jsc, s, describeRoute, InvalidSchemaError } from "bknd/core";
|
||||||
import { getVersion } from "core/env";
|
import { getVersion } from "core/env";
|
||||||
import { SecretSchema } from "core/object/schema/secret";
|
import { SecretSchema } from "core/object/schema/secret";
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import { type App, ModuleManagerEvents, type AppPlugin } from "bknd";
|
||||||
|
import type { $diff } from "bknd/core";
|
||||||
|
|
||||||
|
export type SyncDiffsOptions = {
|
||||||
|
enabled?: boolean;
|
||||||
|
write: (name: string, diffs: $diff.DiffEntry[]) => Promise<void>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function syncDiffs({ enabled = true, write }: SyncDiffsOptions): AppPlugin {
|
||||||
|
return (app: App) => ({
|
||||||
|
name: "bknd-sync-diffs",
|
||||||
|
onBuilt: async () => {
|
||||||
|
if (!enabled) return;
|
||||||
|
app.emgr.onEvent(
|
||||||
|
ModuleManagerEvents.ModuleManagerConfigDiffEvent,
|
||||||
|
async (event) => {
|
||||||
|
const name = `${new Date().getTime()}.diff.json`;
|
||||||
|
await write?.(name, event.params.diffs);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "sync-config",
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -5,3 +5,4 @@ export {
|
|||||||
export { showRoutes, type ShowRoutesOptions } from "./dev/show-routes.plugin";
|
export { showRoutes, type ShowRoutesOptions } from "./dev/show-routes.plugin";
|
||||||
export { syncConfig, type SyncConfigOptions } from "./dev/sync-config.plugin";
|
export { syncConfig, type SyncConfigOptions } from "./dev/sync-config.plugin";
|
||||||
export { syncTypes, type SyncTypesOptions } from "./dev/sync-types.plugin";
|
export { syncTypes, type SyncTypesOptions } from "./dev/sync-types.plugin";
|
||||||
|
export { syncDiffs, type SyncDiffsOptions } from "./dev/sync-diffs.plugin";
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import {
|
|||||||
import { useBknd } from "ui/client/bknd";
|
import { useBknd } from "ui/client/bknd";
|
||||||
import type { TSchemaActions } from "ui/client/schema/actions";
|
import type { TSchemaActions } from "ui/client/schema/actions";
|
||||||
import { bkndModals } from "ui/modals";
|
import { bkndModals } from "ui/modals";
|
||||||
import { s, parse, InvalidSchemaError } from "core/object/schema";
|
import { s, parse, InvalidSchemaError } from "bknd/core";
|
||||||
|
|
||||||
export function useBkndData() {
|
export function useBkndData() {
|
||||||
const { config, app, schema, actions: bkndActions } = useBknd();
|
const { config, app, schema, actions: bkndActions } = useBknd();
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { parse } from "core/object/schema";
|
import { parse } from "bknd/core";
|
||||||
import { type TAppFlowSchema, flowSchema } from "flows/flows-schema";
|
import { type TAppFlowSchema, flowSchema } from "flows/flows-schema";
|
||||||
import { useBknd } from "../../BkndProvider";
|
import { useBknd } from "../../BkndProvider";
|
||||||
|
|
||||||
|
|||||||
@@ -64,13 +64,12 @@ const FieldImpl = ({
|
|||||||
const id = `${name}-${useId()}`;
|
const id = `${name}-${useId()}`;
|
||||||
const required = typeof _required === "boolean" ? _required : ctx.required;
|
const required = typeof _required === "boolean" ? _required : ctx.required;
|
||||||
|
|
||||||
if (!schema) {
|
if (!schema)
|
||||||
return (
|
return (
|
||||||
<Pre>
|
<Pre>
|
||||||
[Field] {path} has no schema ({JSON.stringify(schema)})
|
[Field] {path} has no schema ({JSON.stringify(schema)})
|
||||||
</Pre>
|
</Pre>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
if (isType(schema.type, "object")) {
|
if (isType(schema.type, "object")) {
|
||||||
return <ObjectField path={name} />;
|
return <ObjectField path={name} />;
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export class JsonvTsValidator<T = any, S extends StrictRJSFSchema = RJSFSchema>
|
|||||||
return { errors: [], validationError: null as any };
|
return { errors: [], validationError: null as any };
|
||||||
}
|
}
|
||||||
|
|
||||||
const jsSchema = s.fromSchema(schema as any);
|
const jsSchema = s.fromSchema(JSON.parse(JSON.stringify(schema)) as any);
|
||||||
const result = jsSchema.validate(formData);
|
const result = jsSchema.validate(formData);
|
||||||
|
|
||||||
if (result.valid) {
|
if (result.valid) {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { Button } from "ui/components/buttons/Button";
|
|||||||
import { Group, Input, Password, Label } from "ui/components/form/Formy/components";
|
import { Group, Input, Password, Label } from "ui/components/form/Formy/components";
|
||||||
import { SocialLink } from "./SocialLink";
|
import { SocialLink } from "./SocialLink";
|
||||||
import type { Validator } from "json-schema-form-react";
|
import type { Validator } from "json-schema-form-react";
|
||||||
import { s } from "core/object/schema";
|
import { s } from "bknd/core";
|
||||||
import type { ErrorDetail } from "jsonv-ts";
|
import type { ErrorDetail } from "jsonv-ts";
|
||||||
|
|
||||||
class JsonvTsValidator implements Validator<ErrorDetail> {
|
class JsonvTsValidator implements Validator<ErrorDetail> {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { decodeSearch, encodeSearch, mergeObject } from "core/utils";
|
import { decodeSearch, encodeSearch, mergeObject } from "core/utils";
|
||||||
import { isEqual, transform } from "lodash-es";
|
import { isEqual, transform } from "lodash-es";
|
||||||
import { useLocation, useSearch as useWouterSearch } from "wouter";
|
import { useLocation, useSearch as useWouterSearch } from "wouter";
|
||||||
import { type s, parse } from "core/object/schema";
|
import { type s, parse } from "bknd/core";
|
||||||
import { useEffect, useMemo, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
|
|
||||||
export type UseSearchOptions<Schema extends s.Schema = s.Schema> = {
|
export type UseSearchOptions<Schema extends s.Schema = s.Schema> = {
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ export function SchemaFormModal({
|
|||||||
<JsonSchemaForm
|
<JsonSchemaForm
|
||||||
tagName="form"
|
tagName="form"
|
||||||
ref={formRef}
|
ref={formRef}
|
||||||
schema={schema}
|
schema={JSON.parse(JSON.stringify(schema))}
|
||||||
uiSchema={uiSchema}
|
uiSchema={uiSchema}
|
||||||
className="legacy hide-required-mark fieldset-alternative mute-root"
|
className="legacy hide-required-mark fieldset-alternative mute-root"
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import type { ModalProps } from "@mantine/core";
|
import type { ModalProps } from "@mantine/core";
|
||||||
import type { ContextModalProps } from "@mantine/modals";
|
import type { ContextModalProps } from "@mantine/modals";
|
||||||
import { entitiesSchema, fieldsSchema, relationsSchema } from "data/data-schema";
|
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { type Modal2Ref, ModalBody, ModalFooter, ModalTitle } from "ui/components/modal/Modal2";
|
import { type Modal2Ref, ModalBody, ModalFooter, ModalTitle } from "ui/components/modal/Modal2";
|
||||||
import { Step, Steps, useStepContext } from "ui/components/steps/Steps";
|
import { Step, Steps, useStepContext } from "ui/components/steps/Steps";
|
||||||
@@ -10,52 +9,10 @@ import { StepEntityFields } from "./step.entity.fields";
|
|||||||
import { StepRelation } from "./step.relation";
|
import { StepRelation } from "./step.relation";
|
||||||
import { StepSelect } from "./step.select";
|
import { StepSelect } from "./step.select";
|
||||||
import Templates from "./templates/register";
|
import Templates from "./templates/register";
|
||||||
import { s } from "core/object/schema";
|
import type { TCreateModalSchema } from "./schema";
|
||||||
|
|
||||||
export type CreateModalRef = Modal2Ref;
|
export type CreateModalRef = Modal2Ref;
|
||||||
|
|
||||||
export const ModalActions = ["entity", "relation", "media"] as const;
|
|
||||||
|
|
||||||
export const entitySchema = s.object({
|
|
||||||
name: s.string(),
|
|
||||||
...entitiesSchema.properties,
|
|
||||||
});
|
|
||||||
|
|
||||||
// @todo: this union is not fully working, just "string"
|
|
||||||
const schemaAction = s.anyOf([
|
|
||||||
s.string({ enum: ["entity", "relation", "media"] }),
|
|
||||||
s.string({ pattern: "^template-" }),
|
|
||||||
]);
|
|
||||||
export type TSchemaAction = s.Static<typeof schemaAction>;
|
|
||||||
|
|
||||||
const createFieldSchema = s.object({
|
|
||||||
entity: s.string(),
|
|
||||||
name: s.string(),
|
|
||||||
field: s.array(fieldsSchema),
|
|
||||||
});
|
|
||||||
export type TFieldCreate = s.Static<typeof createFieldSchema>;
|
|
||||||
|
|
||||||
const createModalSchema = s.strictObject({
|
|
||||||
action: schemaAction,
|
|
||||||
initial: s.any().optional(),
|
|
||||||
entities: s
|
|
||||||
.object({
|
|
||||||
create: s.array(entitySchema).optional(),
|
|
||||||
})
|
|
||||||
.optional(),
|
|
||||||
relations: s
|
|
||||||
.object({
|
|
||||||
create: s.array(s.anyOf(relationsSchema)).optional(),
|
|
||||||
})
|
|
||||||
.optional(),
|
|
||||||
fields: s
|
|
||||||
.object({
|
|
||||||
create: s.array(createFieldSchema).optional(),
|
|
||||||
})
|
|
||||||
.optional(),
|
|
||||||
});
|
|
||||||
export type TCreateModalSchema = s.Static<typeof createModalSchema>;
|
|
||||||
|
|
||||||
export function CreateModal({
|
export function CreateModal({
|
||||||
context,
|
context,
|
||||||
id,
|
id,
|
||||||
@@ -107,4 +64,4 @@ CreateModal.modalProps = {
|
|||||||
padding: 0,
|
padding: 0,
|
||||||
} satisfies Partial<ModalProps>;
|
} satisfies Partial<ModalProps>;
|
||||||
|
|
||||||
export { ModalBody, ModalFooter, ModalTitle, useStepContext, relationsSchema };
|
export { ModalBody, ModalFooter, ModalTitle, useStepContext };
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import { s } from "bknd/core";
|
||||||
|
import { entitiesSchema, fieldsSchema, relationsSchema } from "data/data-schema";
|
||||||
|
|
||||||
|
export const ModalActions = ["entity", "relation", "media"] as const;
|
||||||
|
|
||||||
|
export const entitySchema = s.object({
|
||||||
|
...entitiesSchema.properties,
|
||||||
|
name: s.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
// @todo: this union is not fully working, just "string"
|
||||||
|
const schemaAction = s.anyOf([
|
||||||
|
s.string({ enum: ["entity", "relation", "media"] }),
|
||||||
|
s.string({ pattern: "^template-" }),
|
||||||
|
]);
|
||||||
|
export type TSchemaAction = s.Static<typeof schemaAction>;
|
||||||
|
|
||||||
|
const createFieldSchema = s.object({
|
||||||
|
entity: s.string(),
|
||||||
|
name: s.string(),
|
||||||
|
field: s.array(fieldsSchema),
|
||||||
|
});
|
||||||
|
export type TFieldCreate = s.Static<typeof createFieldSchema>;
|
||||||
|
|
||||||
|
const createModalSchema = s.strictObject({
|
||||||
|
action: schemaAction,
|
||||||
|
initial: s.any().optional(),
|
||||||
|
entities: s
|
||||||
|
.object({
|
||||||
|
create: s.array(entitySchema).optional(),
|
||||||
|
})
|
||||||
|
.optional(),
|
||||||
|
relations: s
|
||||||
|
.object({
|
||||||
|
create: s.array(s.anyOf(relationsSchema)).optional(),
|
||||||
|
})
|
||||||
|
.optional(),
|
||||||
|
fields: s
|
||||||
|
.object({
|
||||||
|
create: s.array(createFieldSchema).optional(),
|
||||||
|
})
|
||||||
|
.optional(),
|
||||||
|
});
|
||||||
|
export type TCreateModalSchema = s.Static<typeof createModalSchema>;
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
import { useDisclosure } from "@mantine/hooks";
|
import { useDisclosure } from "@mantine/hooks";
|
||||||
import {
|
import {
|
||||||
IconAlignJustified,
|
IconAlignJustified,
|
||||||
IconAugmentedReality,
|
|
||||||
IconBox,
|
IconBox,
|
||||||
IconCirclesRelation,
|
IconCirclesRelation,
|
||||||
IconInfoCircle,
|
IconInfoCircle,
|
||||||
@@ -15,7 +14,7 @@ import { IconButton, type IconType } from "ui/components/buttons/IconButton";
|
|||||||
import { JsonViewer } from "ui/components/code/JsonViewer";
|
import { JsonViewer } from "ui/components/code/JsonViewer";
|
||||||
import { ModalBody, ModalFooter } from "ui/components/modal/Modal2";
|
import { ModalBody, ModalFooter } from "ui/components/modal/Modal2";
|
||||||
import { useStepContext } from "ui/components/steps/Steps";
|
import { useStepContext } from "ui/components/steps/Steps";
|
||||||
import type { TCreateModalSchema } from "ui/modules/data/components/schema/create-modal/CreateModal";
|
import type { TCreateModalSchema } from "ui/modules/data/components/schema/create-modal/schema";
|
||||||
|
|
||||||
type ActionItem = SummaryItemProps & {
|
type ActionItem = SummaryItemProps & {
|
||||||
run: () => Promise<boolean>;
|
run: () => Promise<boolean>;
|
||||||
@@ -35,9 +34,9 @@ export function StepCreate() {
|
|||||||
action: "add",
|
action: "add",
|
||||||
Icon: IconBox,
|
Icon: IconBox,
|
||||||
type: "Entity",
|
type: "Entity",
|
||||||
name: entity.name,
|
name: entity.name!,
|
||||||
json: entity,
|
json: entity,
|
||||||
run: async () => await $data.actions.entity.add(entity.name, entity),
|
run: async () => await $data.actions.entity.add(entity.name!, entity),
|
||||||
})),
|
})),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
//import { typeboxResolver } from "@hookform/resolvers/typebox";
|
|
||||||
import { objectCleanEmpty } from "core/utils";
|
import { objectCleanEmpty } from "core/utils";
|
||||||
import { type TAppDataEntityFields, entitiesSchema } from "data/data-schema";
|
import { type TAppDataEntityFields, entitiesSchema } from "data/data-schema";
|
||||||
import { mergeWith } from "lodash-es";
|
import { mergeWith } from "lodash-es";
|
||||||
@@ -10,12 +9,13 @@ import {
|
|||||||
EntityFieldsForm,
|
EntityFieldsForm,
|
||||||
type EntityFieldsFormRef,
|
type EntityFieldsFormRef,
|
||||||
} from "ui/routes/data/forms/entity.fields.form";
|
} from "ui/routes/data/forms/entity.fields.form";
|
||||||
import { ModalBody, ModalFooter, type TCreateModalSchema, useStepContext } from "./CreateModal";
|
import { ModalBody, ModalFooter, useStepContext } from "./CreateModal";
|
||||||
import { useBkndData } from "ui/client/schema/data/use-bknd-data";
|
import { useBkndData } from "ui/client/schema/data/use-bknd-data";
|
||||||
import type { s } from "core/object/schema";
|
import type { s } from "bknd/core";
|
||||||
import { standardSchemaResolver } from "@hookform/resolvers/standard-schema";
|
import { standardSchemaResolver } from "@hookform/resolvers/standard-schema";
|
||||||
|
import { entitySchema, type TCreateModalSchema } from "./schema";
|
||||||
|
|
||||||
const schema = entitiesSchema;
|
const schema = entitySchema;
|
||||||
type Schema = s.Static<typeof schema>;
|
type Schema = s.Static<typeof schema>;
|
||||||
|
|
||||||
export function StepEntityFields() {
|
export function StepEntityFields() {
|
||||||
|
|||||||
@@ -2,30 +2,29 @@ import { standardSchemaResolver } from "@hookform/resolvers/standard-schema";
|
|||||||
import { TextInput, Textarea } from "@mantine/core";
|
import { TextInput, Textarea } from "@mantine/core";
|
||||||
import { useFocusTrap } from "@mantine/hooks";
|
import { useFocusTrap } from "@mantine/hooks";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import {
|
import { ModalBody, ModalFooter, useStepContext } from "./CreateModal";
|
||||||
ModalBody,
|
import { entitySchema, type TCreateModalSchema } from "./schema";
|
||||||
ModalFooter,
|
import { s } from "bknd/core";
|
||||||
type TCreateModalSchema,
|
import { cloneSchema } from "core/object/schema";
|
||||||
entitySchema,
|
|
||||||
useStepContext,
|
const schema = s.object({
|
||||||
} from "./CreateModal";
|
name: entitySchema.properties.name,
|
||||||
|
config: entitySchema.properties.config.partial().optional(),
|
||||||
|
});
|
||||||
|
type Schema = s.Static<typeof schema>;
|
||||||
|
|
||||||
export function StepEntity() {
|
export function StepEntity() {
|
||||||
const focusTrapRef = useFocusTrap();
|
const focusTrapRef = useFocusTrap();
|
||||||
|
|
||||||
const { nextStep, stepBack, state, setState } = useStepContext<TCreateModalSchema>();
|
const { nextStep, stepBack, state, setState } = useStepContext<TCreateModalSchema>();
|
||||||
const { register, handleSubmit, formState, watch, control } = useForm({
|
const { register, handleSubmit, formState, watch, control } = useForm({
|
||||||
mode: "onTouched",
|
mode: "onChange",
|
||||||
resolver: standardSchemaResolver(entitySchema),
|
resolver: standardSchemaResolver(cloneSchema(schema)),
|
||||||
defaultValues: state.entities?.create?.[0] ?? {},
|
defaultValues: (state.entities?.create?.[0] ?? {}) as Schema,
|
||||||
});
|
});
|
||||||
/*const data = watch();
|
|
||||||
console.log("state", { isValid });
|
|
||||||
console.log("schema", JSON.stringify(entitySchema));
|
|
||||||
console.log("data", JSON.stringify(data));*/
|
|
||||||
|
|
||||||
function onSubmit(data: any) {
|
function onSubmit(data: any) {
|
||||||
console.log(data);
|
console.log("onSubmit", data);
|
||||||
setState((prev) => {
|
setState((prev) => {
|
||||||
const prevEntity = prev.entities?.create?.[0];
|
const prevEntity = prev.entities?.create?.[0];
|
||||||
if (prevEntity && prevEntity.name !== data.name) {
|
if (prevEntity && prevEntity.name !== data.name) {
|
||||||
@@ -45,6 +44,7 @@ export function StepEntity() {
|
|||||||
<>
|
<>
|
||||||
<form onSubmit={handleSubmit(onSubmit)} ref={focusTrapRef}>
|
<form onSubmit={handleSubmit(onSubmit)} ref={focusTrapRef}>
|
||||||
<ModalBody>
|
<ModalBody>
|
||||||
|
<input type="hidden" {...register("type")} defaultValue="regular" />
|
||||||
<TextInput
|
<TextInput
|
||||||
data-autofocus
|
data-autofocus
|
||||||
required
|
required
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user