added format command and added trailing commas to reduce conflicts

This commit is contained in:
dswbx
2025-02-26 20:06:03 +01:00
parent 88b5359f1c
commit 7743f71a11
414 changed files with 3622 additions and 3610 deletions

View File

@@ -19,8 +19,8 @@ describe("Api", async () => {
const token = await sign({ foo: "bar" }, "1234"); const token = await sign({ foo: "bar" }, "1234");
const request = new Request("http://example.com/test", { const request = new Request("http://example.com/test", {
headers: { headers: {
Authorization: `Bearer ${token}` Authorization: `Bearer ${token}`,
} },
}); });
const api = new Api({ request }); const api = new Api({ request });
expect(api.isAuthVerified()).toBe(false); expect(api.isAuthVerified()).toBe(false);
@@ -35,8 +35,8 @@ describe("Api", async () => {
const token = await sign({ foo: "bar" }, "1234"); const token = await sign({ foo: "bar" }, "1234");
const request = new Request("http://example.com/test", { const request = new Request("http://example.com/test", {
headers: { headers: {
Cookie: `auth=${token}` Cookie: `auth=${token}`,
} },
}); });
const api = new Api({ request }); const api = new Api({ request });
expect(api.isAuthVerified()).toBe(false); expect(api.isAuthVerified()).toBe(false);

View File

@@ -26,7 +26,7 @@ describe("DataApi", () => {
it("returns result", async () => { it("returns result", async () => {
const schema = proto.em({ const schema = proto.em({
posts: proto.entity("posts", { title: proto.text() }) posts: proto.entity("posts", { title: proto.text() }),
}); });
const em = schemaToEm(schema); const em = schemaToEm(schema);
await em.schema().sync({ force: true }); await em.schema().sync({ force: true });
@@ -60,7 +60,7 @@ describe("DataApi", () => {
select: ["title"], select: ["title"],
limit: 100000, limit: 100000,
offset: 0, offset: 0,
sort: "id" sort: "id",
}); });
expect(req.request.method).toBe("POST"); expect(req.request.method).toBe("POST");
const res = await req; const res = await req;

View File

@@ -18,8 +18,8 @@ const mockedBackend = new Hono()
return new Response(file, { return new Response(file, {
headers: { headers: {
"Content-Type": file.type, "Content-Type": file.type,
"Content-Length": file.size.toString() "Content-Length": file.size.toString(),
} },
}); });
}); });
@@ -30,7 +30,7 @@ describe("MediaApi", () => {
// @ts-ignore tests // @ts-ignore tests
const api = new MediaApi({ const api = new MediaApi({
host, host,
basepath basepath,
}); });
expect(api.getFileUploadUrl({ path: "path" })).toBe(`${host}${basepath}/upload/path`); expect(api.getFileUploadUrl({ path: "path" })).toBe(`${host}${basepath}/upload/path`);
}); });
@@ -38,7 +38,7 @@ describe("MediaApi", () => {
it("should have correct upload headers", () => { it("should have correct upload headers", () => {
// @ts-ignore tests // @ts-ignore tests
const api = new MediaApi({ const api = new MediaApi({
token: "token" token: "token",
}); });
expect(api.getUploadHeaders().get("Authorization")).toBe("Bearer token"); expect(api.getUploadHeaders().get("Authorization")).toBe("Bearer token");
}); });
@@ -139,7 +139,7 @@ describe("MediaApi", () => {
const response = (await mockedBackend.request(url)) as Response; const response = (await mockedBackend.request(url)) as Response;
await matches( await matches(
await api.upload(response.body!, { filename: "readable.png" }), await api.upload(response.body!, { filename: "readable.png" }),
"readable.png" "readable.png",
); );
} }
}); });

View File

@@ -61,7 +61,7 @@ describe("ModuleApi", () => {
it("adds additional headers from options", () => { it("adds additional headers from options", () => {
const headers = new Headers({ const headers = new Headers({
"X-Test": "123" "X-Test": "123",
}); });
const api = new Api({ host, headers }); const api = new Api({ host, headers });
expect(api.get("/").request.headers.get("X-Test")).toEqual("123"); expect(api.get("/").request.headers.get("X-Test")).toEqual("123");
@@ -75,7 +75,7 @@ describe("ModuleApi", () => {
it("uses search params", () => { it("uses search params", () => {
const api = new Api({ host }); const api = new Api({ host });
const search = new URLSearchParams({ const search = new URLSearchParams({
foo: "bar" foo: "bar",
}); });
expect(api.get("/", search).request.url).toEqual("http://localhost/?" + search.toString()); expect(api.get("/", search).request.url).toEqual("http://localhost/?" + search.toString());
}); });

View File

@@ -24,9 +24,9 @@ describe("repros", async () => {
adapter: { adapter: {
type: "local", type: "local",
config: { config: {
path: "./" path: "./",
} },
} },
}); });
expect(config.enabled).toBe(true); expect(config.enabled).toBe(true);
@@ -38,9 +38,9 @@ describe("repros", async () => {
"entities.test", "entities.test",
proto proto
.entity("test", { .entity("test", {
content: proto.text() content: proto.text(),
}) })
.toJSON() .toJSON(),
); );
expect(app.em.entities.map((e) => e.name)).toContain("test"); expect(app.em.entities.map((e) => e.name)).toContain("test");
} }
@@ -54,8 +54,8 @@ describe("repros", async () => {
hidden: false, hidden: false,
mime_types: [], mime_types: [],
virtual: true, virtual: true,
entity: "test" entity: "test",
} },
}); });
expect( expect(
@@ -63,8 +63,8 @@ describe("repros", async () => {
type: "poly", type: "poly",
source: "test", source: "test",
target: "media", target: "media",
config: { mappedBy: "files" } config: { mappedBy: "files" },
}) }),
).resolves.toBeDefined(); ).resolves.toBeDefined();
} }
@@ -75,17 +75,17 @@ describe("repros", async () => {
const schema = proto.em( const schema = proto.em(
{ {
products: proto.entity("products", { products: proto.entity("products", {
title: proto.text() title: proto.text(),
}), }),
product_likes: proto.entity("product_likes", { product_likes: proto.entity("product_likes", {
created_at: proto.date() created_at: proto.date(),
}), }),
users: proto.entity("users", {}) users: proto.entity("users", {}),
}, },
(fns, schema) => { (fns, schema) => {
fns.relation(schema.product_likes).manyToOne(schema.products, { inversedBy: "likes" }); fns.relation(schema.product_likes).manyToOne(schema.products, { inversedBy: "likes" });
fns.relation(schema.product_likes).manyToOne(schema.users); fns.relation(schema.product_likes).manyToOne(schema.users);
} },
); );
const app = createApp({ initialConfig: { data: schema.toJSON() } }); const app = createApp({ initialConfig: { data: schema.toJSON() } });
await app.build(); await app.build();
@@ -96,8 +96,8 @@ describe("repros", async () => {
expect(info.relations.listable).toEqual([ expect(info.relations.listable).toEqual([
{ {
entity: "product_likes", entity: "product_likes",
ref: "likes" ref: "likes",
} },
]); ]);
}); });
}); });

View File

@@ -7,13 +7,13 @@ describe("authorize", () => {
["read", "write"], ["read", "write"],
{ {
admin: { admin: {
permissions: ["read", "write"] permissions: ["read", "write"],
}
}, },
{ enabled: true } },
{ enabled: true },
); );
const user = { const user = {
role: "admin" role: "admin",
}; };
expect(guard.granted("read", user)).toBe(true); expect(guard.granted("read", user)).toBe(true);
@@ -27,21 +27,21 @@ describe("authorize", () => {
["read", "write"], ["read", "write"],
{ {
admin: { admin: {
permissions: ["read", "write"] permissions: ["read", "write"],
}, },
guest: { guest: {
permissions: ["read"], permissions: ["read"],
is_default: true is_default: true,
}
}, },
{ enabled: true } },
{ enabled: true },
); );
expect(guard.granted("read")).toBe(true); expect(guard.granted("read")).toBe(true);
expect(guard.granted("write")).toBe(false); expect(guard.granted("write")).toBe(false);
const user = { const user = {
role: "admin" role: "admin",
}; };
expect(guard.granted("read", user)).toBe(true); expect(guard.granted("read", user)).toBe(true);
@@ -58,12 +58,12 @@ describe("authorize", () => {
test("role implicit allow", async () => { test("role implicit allow", async () => {
const guard = Guard.create(["read", "write"], { const guard = Guard.create(["read", "write"], {
admin: { admin: {
implicit_allow: true implicit_allow: true,
} },
}); });
const user = { const user = {
role: "admin" role: "admin",
}; };
expect(guard.granted("read", user)).toBe(true); expect(guard.granted("read", user)).toBe(true);
@@ -74,8 +74,8 @@ describe("authorize", () => {
const guard = Guard.create(["read", "write"], { const guard = Guard.create(["read", "write"], {
guest: { guest: {
implicit_allow: true, implicit_allow: true,
is_default: true is_default: true,
} },
}); });
expect(guard.getUserRole()?.name).toBe("guest"); expect(guard.getUserRole()?.name).toBe("guest");

View File

@@ -8,9 +8,9 @@ describe("OAuthStrategy", async () => {
type: "oidc", type: "oidc",
client: { client: {
client_id: process.env.OAUTH_CLIENT_ID, client_id: process.env.OAUTH_CLIENT_ID,
client_secret: process.env.OAUTH_CLIENT_SECRET client_secret: process.env.OAUTH_CLIENT_SECRET,
}, },
name: "google" name: "google",
}); });
const state = "---"; const state = "---";
const redirect_uri = "http://localhost:3000/auth/google/callback"; const redirect_uri = "http://localhost:3000/auth/google/callback";
@@ -21,7 +21,7 @@ describe("OAuthStrategy", async () => {
const request = await strategy.request({ const request = await strategy.request({
redirect_uri, redirect_uri,
state state,
}); });
const server = Bun.serve({ const server = Bun.serve({
@@ -31,13 +31,13 @@ describe("OAuthStrategy", async () => {
console.log("req", req); console.log("req", req);
const user = await strategy.callback(url, { const user = await strategy.callback(url, {
redirect_uri, redirect_uri,
state state,
}); });
console.log("---user", user); console.log("---user", user);
} }
return new Response("Bun!"); return new Response("Bun!");
} },
}); });
console.log("request", request); console.log("request", request);

View File

@@ -26,7 +26,7 @@ class ReturnEvent extends Event<{ foo: string }, string> {
} }
return this.clone({ return this.clone({
foo: [this.params.foo, value].join("-") foo: [this.params.foo, value].join("-"),
}); });
} }
} }
@@ -52,7 +52,7 @@ describe("EventManager", async () => {
await new Promise((resolve) => setTimeout(resolve, 50)); await new Promise((resolve) => setTimeout(resolve, 50));
delayed(); delayed();
}, },
"sync" "sync",
); );
// don't allow unknown // don't allow unknown
@@ -83,8 +83,8 @@ describe("EventManager", async () => {
const emgr = new EventManager( const emgr = new EventManager(
{ InformationalEvent }, { InformationalEvent },
{ {
asyncExecutor asyncExecutor,
} },
); );
emgr.onEvent(InformationalEvent, async () => {}); emgr.onEvent(InformationalEvent, async () => {});
@@ -98,8 +98,8 @@ describe("EventManager", async () => {
const emgr = new EventManager( const emgr = new EventManager(
{ ReturnEvent, InformationalEvent }, { ReturnEvent, InformationalEvent },
{ {
onInvalidReturn onInvalidReturn,
} },
); );
// @ts-expect-error InformationalEvent has no return value // @ts-expect-error InformationalEvent has no return value
@@ -140,7 +140,7 @@ describe("EventManager", async () => {
expect(slug).toBe("informational-event"); expect(slug).toBe("informational-event");
call(); call();
}, },
{ mode: "sync", once: true } { mode: "sync", once: true },
); );
expect(emgr.getListeners().length).toBe(1); expect(emgr.getListeners().length).toBe(1);

View File

@@ -30,8 +30,8 @@ describe("Registry", () => {
first: { first: {
cls: What, cls: What,
schema: Type.Object({ type: Type.String(), what: Type.String() }), schema: Type.Object({ type: Type.String(), what: Type.String() }),
enabled: true enabled: true,
} },
} satisfies Record<string, Test1>); } satisfies Record<string, Test1>);
const item = registry.get("first"); const item = registry.get("first");
@@ -42,7 +42,7 @@ describe("Registry", () => {
registry.add("second", { registry.add("second", {
cls: What2, cls: What2,
schema: second, schema: second,
enabled: true enabled: true,
}); });
// @ts-ignore // @ts-ignore
expect(registry.get("second").schema).toEqual(second); expect(registry.get("second").schema).toEqual(second);
@@ -52,7 +52,7 @@ describe("Registry", () => {
// @ts-expect-error // @ts-expect-error
cls: NotAllowed, cls: NotAllowed,
schema: third, schema: third,
enabled: true enabled: true,
}); });
// @ts-ignore // @ts-ignore
expect(registry.get("third").schema).toEqual(third); expect(registry.get("third").schema).toEqual(third);
@@ -62,7 +62,7 @@ describe("Registry", () => {
cls: What, cls: What,
// @ts-expect-error // @ts-expect-error
schema: fourth, schema: fourth,
enabled: true enabled: true,
}); });
// @ts-ignore // @ts-ignore
expect(registry.get("fourth").schema).toEqual(fourth); expect(registry.get("fourth").schema).toEqual(fourth);
@@ -75,7 +75,7 @@ describe("Registry", () => {
return { return {
cls: a, cls: a,
schema: a.prototype.getType(), schema: a.prototype.getType(),
enabled: true enabled: true,
}; };
}); });

View File

@@ -4,7 +4,7 @@ import { after, beforeEach, describe, test } from "node:test";
import { Miniflare } from "miniflare"; import { Miniflare } from "miniflare";
import { import {
CloudflareKVCacheItem, CloudflareKVCacheItem,
CloudflareKVCachePool CloudflareKVCachePool,
} from "../../../src/core/cache/adapters/CloudflareKvCache"; } from "../../../src/core/cache/adapters/CloudflareKvCache";
import { runTests } from "./cache-test-suite"; import { runTests } from "./cache-test-suite";
@@ -26,7 +26,7 @@ describe("CloudflareKv", async () => {
mf = new Miniflare({ mf = new Miniflare({
modules: true, modules: true,
script: "export default { async fetch() { return new Response(null); } }", script: "export default { async fetch() { return new Response(null); } }",
kvNamespaces: ["TEST"] kvNamespaces: ["TEST"],
}); });
const kv = await mf.getKVNamespace("TEST"); const kv = await mf.getKVNamespace("TEST");
return new CloudflareKVCachePool(kv as any); return new CloudflareKVCachePool(kv as any);
@@ -45,10 +45,10 @@ describe("CloudflareKv", async () => {
}, },
toBeUndefined() { toBeUndefined() {
assert.equal(actual, undefined); assert.equal(actual, undefined);
} },
}; };
} },
} },
}); });
after(async () => { after(async () => {

View File

@@ -9,7 +9,7 @@ describe("MemoryCache", () => {
tester: { tester: {
test, test,
beforeEach, beforeEach,
expect expect,
} },
}); });
}); });

View File

@@ -4,7 +4,7 @@ import { checksum, hash } from "../../src/core/utils";
describe("crypto", async () => { describe("crypto", async () => {
test("sha256", async () => { test("sha256", async () => {
expect(await hash.sha256("test")).toBe( expect(await hash.sha256("test")).toBe(
"9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08" "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
); );
}); });
test("sha1", async () => { test("sha1", async () => {

View File

@@ -8,8 +8,8 @@ describe("SchemaObject", async () => {
Type.Object({ a: Type.String({ default: "b" }) }), Type.Object({ a: Type.String({ default: "b" }) }),
{ a: "test" }, { a: "test" },
{ {
forceParse: true forceParse: true,
} },
); );
expect(m.get()).toEqual({ a: "test" }); expect(m.get()).toEqual({ a: "test" });
@@ -30,14 +30,14 @@ describe("SchemaObject", async () => {
b: Type.Object( b: Type.Object(
{ {
c: Type.String({ default: "d" }), c: Type.String({ default: "d" }),
e: Type.String({ default: "f" }) e: Type.String({ default: "f" }),
}, },
{ default: {} } { default: {} },
) ),
}, },
{ default: {}, additionalProperties: false } { default: {}, additionalProperties: false },
) ),
}) }),
); );
expect(m.get()).toEqual({ s: { a: "b", b: { c: "d", e: "f" } } }); expect(m.get()).toEqual({ s: { a: "b", b: { c: "d", e: "f" } } });
@@ -59,8 +59,8 @@ describe("SchemaObject", async () => {
test("patch array", async () => { test("patch array", async () => {
const m = new SchemaObject( const m = new SchemaObject(
Type.Object({ Type.Object({
methods: Type.Array(Type.String(), { default: ["GET", "PATCH"] }) methods: Type.Array(Type.String(), { default: ["GET", "PATCH"] }),
}) }),
); );
expect(m.get()).toEqual({ methods: ["GET", "PATCH"] }); expect(m.get()).toEqual({ methods: ["GET", "PATCH"] });
@@ -81,14 +81,14 @@ describe("SchemaObject", async () => {
a: Type.String({ default: "b" }), a: Type.String({ default: "b" }),
b: Type.Object( b: Type.Object(
{ {
c: Type.String({ default: "d" }) c: Type.String({ default: "d" }),
}, },
{ default: {} } { default: {} },
) ),
}, },
{ default: {} } { default: {} },
) ),
}) }),
); );
expect(m.get()).toEqual({ s: { a: "b", b: { c: "d" } } }); expect(m.get()).toEqual({ s: { a: "b", b: { c: "d" } } });
@@ -108,8 +108,8 @@ describe("SchemaObject", async () => {
test("set", async () => { test("set", async () => {
const m = new SchemaObject( const m = new SchemaObject(
Type.Object({ Type.Object({
methods: Type.Array(Type.String(), { default: ["GET", "PATCH"] }) methods: Type.Array(Type.String(), { default: ["GET", "PATCH"] }),
}) }),
); );
expect(m.get()).toEqual({ methods: ["GET", "PATCH"] }); expect(m.get()).toEqual({ methods: ["GET", "PATCH"] });
@@ -125,7 +125,7 @@ describe("SchemaObject", async () => {
let result: any; let result: any;
const m = new SchemaObject( const m = new SchemaObject(
Type.Object({ Type.Object({
methods: Type.Array(Type.String(), { default: ["GET", "PATCH"] }) methods: Type.Array(Type.String(), { default: ["GET", "PATCH"] }),
}), }),
undefined, undefined,
{ {
@@ -133,8 +133,8 @@ describe("SchemaObject", async () => {
await new Promise((r) => setTimeout(r, 10)); await new Promise((r) => setTimeout(r, 10));
called = true; called = true;
result = config; result = config;
} },
} },
); );
await m.set({ methods: ["GET", "POST"] }); await m.set({ methods: ["GET", "POST"] });
@@ -146,7 +146,7 @@ describe("SchemaObject", async () => {
let called = false; let called = false;
const m = new SchemaObject( const m = new SchemaObject(
Type.Object({ Type.Object({
methods: Type.Array(Type.String(), { default: ["GET", "PATCH"] }) methods: Type.Array(Type.String(), { default: ["GET", "PATCH"] }),
}), }),
undefined, undefined,
{ {
@@ -155,8 +155,8 @@ describe("SchemaObject", async () => {
called = true; called = true;
to.methods.push("OPTIONS"); to.methods.push("OPTIONS");
return to; return to;
} },
} },
); );
const result = await m.set({ methods: ["GET", "POST"] }); const result = await m.set({ methods: ["GET", "POST"] });
@@ -168,7 +168,7 @@ describe("SchemaObject", async () => {
test("throwIfRestricted", async () => { test("throwIfRestricted", async () => {
const m = new SchemaObject(Type.Object({}), undefined, { const m = new SchemaObject(Type.Object({}), undefined, {
restrictPaths: ["a.b"] restrictPaths: ["a.b"],
}); });
expect(() => m.throwIfRestricted("a.b")).toThrow(); expect(() => m.throwIfRestricted("a.b")).toThrow();
@@ -185,18 +185,18 @@ describe("SchemaObject", async () => {
a: Type.String({ default: "b" }), a: Type.String({ default: "b" }),
b: Type.Object( b: Type.Object(
{ {
c: Type.String({ default: "d" }) c: Type.String({ default: "d" }),
}, },
{ default: {} } { default: {} },
) ),
}, },
{ default: {} } { default: {} },
) ),
}), }),
undefined, undefined,
{ {
restrictPaths: ["s.b"] restrictPaths: ["s.b"],
} },
); );
expect(m.patch("s.b.c", "e")).rejects.toThrow(); expect(m.patch("s.b.c", "e")).rejects.toThrow();
@@ -217,33 +217,33 @@ describe("SchemaObject", async () => {
additionalProperties: Type.Object({ additionalProperties: Type.Object({
type: Type.String(), type: Type.String(),
config: Type.Optional( config: Type.Optional(
Type.Object({}, { additionalProperties: Type.String() }) Type.Object({}, { additionalProperties: Type.String() }),
) ),
}) }),
} },
),
config: Type.Optional(Type.Object({}, { additionalProperties: Type.String() })),
}),
},
), ),
config: Type.Optional(Type.Object({}, { additionalProperties: Type.String() }))
})
}
)
}, },
{ {
additionalProperties: false additionalProperties: false,
} },
); );
test("patch safe object, overwrite", async () => { test("patch safe object, overwrite", async () => {
const data = { const data = {
entities: { entities: {
some: { some: {
fields: { fields: {
a: { type: "string", config: { some: "thing" } } a: { type: "string", config: { some: "thing" } },
} },
} },
} },
}; };
const m = new SchemaObject(dataEntitiesSchema, data, { const m = new SchemaObject(dataEntitiesSchema, data, {
forceParse: true, forceParse: true,
overwritePaths: [/^entities\..*\.fields\..*\.config/] overwritePaths: [/^entities\..*\.fields\..*\.config/],
}); });
await m.patch("entities.some.fields.a", { type: "string", config: { another: "one" } }); await m.patch("entities.some.fields.a", { type: "string", config: { another: "one" } });
@@ -252,10 +252,10 @@ describe("SchemaObject", async () => {
entities: { entities: {
some: { some: {
fields: { fields: {
a: { type: "string", config: { another: "one" } } a: { type: "string", config: { another: "one" } },
} },
} },
} },
}); });
}); });
@@ -265,22 +265,22 @@ describe("SchemaObject", async () => {
users: { users: {
fields: { fields: {
email: { type: "string" }, email: { type: "string" },
password: { type: "string" } password: { type: "string" },
} },
} },
} },
}; };
const m = new SchemaObject(dataEntitiesSchema, data, { const m = new SchemaObject(dataEntitiesSchema, data, {
forceParse: true, forceParse: true,
overwritePaths: [/^entities\..*\.fields\..*\.config\.html_config$/] overwritePaths: [/^entities\..*\.fields\..*\.config\.html_config$/],
}); });
await m.patch("entities.test", { await m.patch("entities.test", {
fields: { fields: {
content: { content: {
type: "text" type: "text",
} },
} },
}); });
expect(m.get()).toEqual({ expect(m.get()).toEqual({
@@ -288,17 +288,17 @@ describe("SchemaObject", async () => {
users: { users: {
fields: { fields: {
email: { type: "string" }, email: { type: "string" },
password: { type: "string" } password: { type: "string" },
} },
}, },
test: { test: {
fields: { fields: {
content: { content: {
type: "text" type: "text",
} },
} },
} },
} },
}); });
}); });
@@ -308,14 +308,14 @@ describe("SchemaObject", async () => {
users: { users: {
fields: { fields: {
email: { type: "string" }, email: { type: "string" },
password: { type: "string" } password: { type: "string" },
} },
} },
} },
}; };
const m = new SchemaObject(dataEntitiesSchema, data, { const m = new SchemaObject(dataEntitiesSchema, data, {
forceParse: true, forceParse: true,
overwritePaths: [/^entities\..*\.fields\..*\.config\.html_config$/] overwritePaths: [/^entities\..*\.fields\..*\.config\.html_config$/],
}); });
expect(m.patch("desc", "entities.users.config.sort_dir")).rejects.toThrow(); expect(m.patch("desc", "entities.users.config.sort_dir")).rejects.toThrow();
@@ -323,13 +323,13 @@ describe("SchemaObject", async () => {
await m.patch("entities.test", { await m.patch("entities.test", {
fields: { fields: {
content: { content: {
type: "text" type: "text",
} },
} },
}); });
await m.patch("entities.users.config", { await m.patch("entities.users.config", {
sort_dir: "desc" sort_dir: "desc",
}); });
expect(m.get()).toEqual({ expect(m.get()).toEqual({
@@ -337,20 +337,20 @@ describe("SchemaObject", async () => {
users: { users: {
fields: { fields: {
email: { type: "string" }, email: { type: "string" },
password: { type: "string" } password: { type: "string" },
}, },
config: { config: {
sort_dir: "desc" sort_dir: "desc",
} },
}, },
test: { test: {
fields: { fields: {
content: { content: {
type: "text" type: "text",
} },
} },
} },
} },
}); });
}); });
}); });

View File

@@ -13,8 +13,8 @@ describe("diff", () => {
t: "a", t: "a",
p: ["b"], p: ["b"],
o: undefined, o: undefined,
n: 2 n: 2,
} },
]); ]);
const appliedObj = apply(oldObj, diffs); const appliedObj = apply(oldObj, diffs);
@@ -35,8 +35,8 @@ describe("diff", () => {
t: "r", t: "r",
p: ["b"], p: ["b"],
o: 2, o: 2,
n: undefined n: undefined,
} },
]); ]);
const appliedObj = apply(oldObj, diffs); const appliedObj = apply(oldObj, diffs);
@@ -57,8 +57,8 @@ describe("diff", () => {
t: "e", t: "e",
p: ["a"], p: ["a"],
o: 1, o: 1,
n: 2 n: 2,
} },
]); ]);
const appliedObj = apply(oldObj, diffs); const appliedObj = apply(oldObj, diffs);
@@ -79,8 +79,8 @@ describe("diff", () => {
t: "e", t: "e",
p: ["a", "b"], p: ["a", "b"],
o: 1, o: 1,
n: 2 n: 2,
} },
]); ]);
const appliedObj = apply(oldObj, diffs); const appliedObj = apply(oldObj, diffs);
@@ -101,14 +101,14 @@ describe("diff", () => {
t: "e", t: "e",
p: ["a", 1], p: ["a", 1],
o: 2, o: 2,
n: 4 n: 4,
}, },
{ {
t: "a", t: "a",
p: ["a", 3], p: ["a", 3],
o: undefined, o: undefined,
n: 5 n: 5,
} },
]); ]);
const appliedObj = apply(oldObj, diffs); const appliedObj = apply(oldObj, diffs);
@@ -129,20 +129,20 @@ describe("diff", () => {
t: "a", t: "a",
p: ["a", 0], p: ["a", 0],
o: undefined, o: undefined,
n: 1 n: 1,
}, },
{ {
t: "a", t: "a",
p: ["a", 1], p: ["a", 1],
o: undefined, o: undefined,
n: 2 n: 2,
}, },
{ {
t: "a", t: "a",
p: ["a", 2], p: ["a", 2],
o: undefined, o: undefined,
n: 3 n: 3,
} },
]); ]);
const appliedObj = apply(oldObj, diffs); const appliedObj = apply(oldObj, diffs);
@@ -163,14 +163,14 @@ describe("diff", () => {
t: "e", t: "e",
p: ["a", 1], p: ["a", 1],
o: 2, o: 2,
n: 3 n: 3,
}, },
{ {
t: "r", t: "r",
p: ["a", 2], p: ["a", 2],
o: 3, o: 3,
n: undefined n: undefined,
} },
]); ]);
const appliedObj = apply(oldObj, diffs); const appliedObj = apply(oldObj, diffs);
@@ -183,14 +183,14 @@ describe("diff", () => {
it("should handle complex nested changes", () => { it("should handle complex nested changes", () => {
const oldObj = { const oldObj = {
a: { a: {
b: [1, 2, { c: 3 }] b: [1, 2, { c: 3 }],
} },
}; };
const newObj = { const newObj = {
a: { a: {
b: [1, 2, { c: 4 }, 5] b: [1, 2, { c: 4 }, 5],
} },
}; };
const diffs = diff(oldObj, newObj); const diffs = diff(oldObj, newObj);
@@ -200,14 +200,14 @@ describe("diff", () => {
t: "e", t: "e",
p: ["a", "b", 2, "c"], p: ["a", "b", 2, "c"],
o: 3, o: 3,
n: 4 n: 4,
}, },
{ {
t: "a", t: "a",
p: ["a", "b", 3], p: ["a", "b", 3],
o: undefined, o: undefined,
n: 5 n: 5,
} },
]); ]);
const appliedObj = apply(oldObj, diffs); const appliedObj = apply(oldObj, diffs);
@@ -228,14 +228,14 @@ describe("diff", () => {
t: "e", t: "e",
p: ["a"], p: ["a"],
o: undefined, o: undefined,
n: null n: null,
}, },
{ {
t: "e", t: "e",
p: ["b"], p: ["b"],
o: null, o: null,
n: undefined n: undefined,
} },
]); ]);
const appliedObj = apply(oldObj, diffs); const appliedObj = apply(oldObj, diffs);
@@ -256,8 +256,8 @@ describe("diff", () => {
t: "e", t: "e",
p: ["a"], p: ["a"],
o: 1, o: 1,
n: "1" n: "1",
} },
]); ]);
const appliedObj = apply(oldObj, diffs); const appliedObj = apply(oldObj, diffs);
@@ -278,14 +278,14 @@ describe("diff", () => {
t: "r", t: "r",
p: ["b"], p: ["b"],
o: 2, o: 2,
n: undefined n: undefined,
}, },
{ {
t: "a", t: "a",
p: ["c"], p: ["c"],
o: undefined, o: undefined,
n: 3 n: 3,
} },
]); ]);
const appliedObj = apply(oldObj, diffs); const appliedObj = apply(oldObj, diffs);
@@ -306,8 +306,8 @@ describe("diff", () => {
t: "e", t: "e",
p: ["a"], p: ["a"],
o: [1, 2, 3], o: [1, 2, 3],
n: { b: 4 } n: { b: 4 },
} },
]); ]);
const appliedObj = apply(oldObj, diffs); const appliedObj = apply(oldObj, diffs);
@@ -328,8 +328,8 @@ describe("diff", () => {
t: "e", t: "e",
p: ["a"], p: ["a"],
o: { b: 1 }, o: { b: 1 },
n: 2 n: 2,
} },
]); ]);
const appliedObj = apply(oldObj, diffs); const appliedObj = apply(oldObj, diffs);
@@ -350,14 +350,14 @@ describe("diff", () => {
t: "r", t: "r",
p: ["a"], p: ["a"],
o: 1, o: 1,
n: undefined n: undefined,
}, },
{ {
t: "a", t: "a",
p: ["b"], p: ["b"],
o: undefined, o: undefined,
n: 2 n: 2,
} },
]); ]);
const appliedObj = apply(oldObj, diffs); const appliedObj = apply(oldObj, diffs);
@@ -408,8 +408,8 @@ describe("diff", () => {
t: "a", t: "a",
p: ["a"], p: ["a"],
o: undefined, o: undefined,
n: 1 n: 1,
} },
]); ]);
const appliedObj = apply(oldObj, diffs); const appliedObj = apply(oldObj, diffs);
@@ -430,8 +430,8 @@ describe("diff", () => {
t: "r", t: "r",
p: ["a"], p: ["a"],
o: 1, o: 1,
n: undefined n: undefined,
} },
]); ]);
const appliedObj = apply(oldObj, diffs); const appliedObj = apply(oldObj, diffs);

View File

@@ -9,7 +9,7 @@ describe("object-query", () => {
test("validates", async () => { test("validates", async () => {
const converted = convert({ const converted = convert({
name: { $eq: "ch" } name: { $eq: "ch" },
}); });
validate(converted, { name: "Michael" }); validate(converted, { name: "Michael" });
}); });
@@ -31,7 +31,7 @@ describe("object-query", () => {
[{ val: { $notnull: 1 } }, { val: null }, false], [{ val: { $notnull: 1 } }, { val: null }, false],
[{ val: { $regex: ".*" } }, { val: "test" }, true], [{ val: { $regex: ".*" } }, { val: "test" }, true],
[{ val: { $regex: /^t.*/ } }, { val: "test" }, true], [{ val: { $regex: /^t.*/ } }, { val: "test" }, true],
[{ val: { $regex: /^b.*/ } }, { val: "test" }, false] [{ val: { $regex: /^b.*/ } }, { val: "test" }, false],
]; ];
for (const [query, object, expected] of tests) { for (const [query, object, expected] of tests) {
@@ -55,10 +55,10 @@ describe("object-query", () => {
[ [
{ $or: { val1: { $eq: "foo" }, val2: { $eq: "bar" } } }, { $or: { val1: { $eq: "foo" }, val2: { $eq: "bar" } } },
{ val1: "foo", val2: "bar" }, { val1: "foo", val2: "bar" },
true true,
], ],
[{ val1: { $eq: 1 }, $or: { val1: { $eq: 2 } } }, { val1: 1 }, true], [{ val1: { $eq: 1 }, $or: { val1: { $eq: 2 } } }, { val1: 1 }, true],
[{ val1: { $eq: 1 }, $or: { val1: { $eq: 2 } } }, { val1: 3 }, false] [{ val1: { $eq: 1 }, $or: { val1: { $eq: 2 } } }, { val1: 3 }, false],
]; ];
for (const [query, object, expected] of tests) { for (const [query, object, expected] of tests) {

View File

@@ -16,7 +16,7 @@ describe("Core Utils", async () => {
expect(result).toEqual([ expect(result).toEqual([
{ key: "a", value: 1 }, { key: "a", value: 1 },
{ key: "b", value: 2 }, { key: "b", value: 2 },
{ key: "c", value: 3 } { key: "c", value: 3 },
]); ]);
}); });
@@ -51,7 +51,7 @@ describe("Core Utils", async () => {
const obj = utils.headersToObject(headers); const obj = utils.headersToObject(headers);
expect(obj).toEqual({ expect(obj).toEqual({
"content-type": "application/json", "content-type": "application/json",
authorization: "Bearer 123" authorization: "Bearer 123",
}); });
}); });
@@ -82,7 +82,7 @@ describe("Core Utils", async () => {
file: new File([""], "file.txt"), file: new File([""], "file.txt"),
stream: new ReadableStream(), stream: new ReadableStream(),
arrayBuffer: new ArrayBuffer(10), arrayBuffer: new ArrayBuffer(10),
arrayBufferView: new Uint8Array(new ArrayBuffer(10)) arrayBufferView: new Uint8Array(new ArrayBuffer(10)),
}; };
const fns = [ const fns = [
@@ -90,7 +90,7 @@ describe("Core Utils", async () => {
[utils.isBlob, "blob", ["stream", "arrayBuffer", "arrayBufferView"]], [utils.isBlob, "blob", ["stream", "arrayBuffer", "arrayBufferView"]],
[utils.isFile, "file", ["stream", "arrayBuffer", "arrayBufferView"]], [utils.isFile, "file", ["stream", "arrayBuffer", "arrayBufferView"]],
[utils.isArrayBuffer, "arrayBuffer"], [utils.isArrayBuffer, "arrayBuffer"],
[utils.isArrayBufferView, "arrayBufferView"] [utils.isArrayBufferView, "arrayBufferView"],
] as const; ] as const;
const additional = [0, 0.0, "", null, undefined, {}, []]; const additional = [0, 0.0, "", null, undefined, {}, []];
@@ -116,10 +116,10 @@ describe("Core Utils", async () => {
const name = "test.json"; const name = "test.json";
const text = "attachment; filename=" + name; const text = "attachment; filename=" + name;
const headers = new Headers({ const headers = new Headers({
"Content-Disposition": text "Content-Disposition": text,
}); });
const request = new Request("http://example.com", { const request = new Request("http://example.com", {
headers headers,
}); });
expect(utils.getContentName(text)).toBe(name); expect(utils.getContentName(text)).toBe(name);
@@ -166,7 +166,7 @@ describe("Core Utils", async () => {
[{ a: 1, b: 2, c: 3 }, ["b"], { a: 1, c: 3 }], [{ a: 1, b: 2, c: 3 }, ["b"], { a: 1, c: 3 }],
[{ a: 1, b: 2, c: 3 }, ["c"], { a: 1, b: 2 }], [{ a: 1, b: 2, c: 3 }, ["c"], { a: 1, b: 2 }],
[{ a: 1, b: 2, c: 3 }, ["a", "b"], { c: 3 }], [{ a: 1, b: 2, c: 3 }, ["a", "b"], { c: 3 }],
[{ a: 1, b: 2, c: 3 }, ["a", "b", "c"], {}] [{ a: 1, b: 2, c: 3 }, ["a", "b", "c"], {}],
] as [object, string[], object][]; ] as [object, string[], object][];
for (const [obj, keys, expected] of objects) { for (const [obj, keys, expected] of objects) {
@@ -197,9 +197,9 @@ describe("Core Utils", async () => {
new Map([["a", 1]]), new Map([["a", 1]]),
new Map([ new Map([
["a", 1], ["a", 1],
["b", 2] ["b", 2],
]), ]),
false false,
], ],
[{ a: 1 }, { a: 1 }, true], [{ a: 1 }, { a: 1 }, true],
[{ a: 1 }, { a: 2 }, false], [{ a: 1 }, { a: 2 }, false],
@@ -220,7 +220,7 @@ describe("Core Utils", async () => {
[[1, 2, 3], [1, 2, 3, 4], false], [[1, 2, 3], [1, 2, 3, 4], false],
[[{ a: 1 }], [{ a: 1 }], true], [[{ a: 1 }], [{ a: 1 }], true],
[[{ a: 1 }], [{ a: 2 }], false], [[{ a: 1 }], [{ a: 2 }], false],
[[{ a: 1 }], [{ b: 1 }], false] [[{ a: 1 }], [{ b: 1 }], false],
] as [any, any, boolean][]; ] as [any, any, boolean][];
for (const [a, b, expected] of objects) { for (const [a, b, expected] of objects) {
@@ -236,7 +236,7 @@ describe("Core Utils", async () => {
[{ a: { b: 1 } }, "a.b", 1], [{ a: { b: 1 } }, "a.b", 1],
[{ a: { b: 1 } }, "a.b.c", null, null], [{ a: { b: 1 } }, "a.b.c", null, null],
[{ a: { b: 1 } }, "a.b.c", 1, 1], [{ a: { b: 1 } }, "a.b.c", 1, 1],
[[[1]], "0.0", 1] [[[1]], "0.0", 1],
] as [object, string, any, any][]; ] as [object, string, any, any][];
for (const [obj, path, expected, defaultValue] of tests) { for (const [obj, path, expected, defaultValue] of tests) {

View File

@@ -9,7 +9,7 @@ import {
ManyToOneRelation, ManyToOneRelation,
type MutatorResponse, type MutatorResponse,
type RepositoryResponse, type RepositoryResponse,
TextField TextField,
} from "../../src/data"; } from "../../src/data";
import { DataController } from "../../src/data/api/DataController"; import { DataController } from "../../src/data/api/DataController";
import { dataConfigSchema } from "../../src/data/data-schema"; import { dataConfigSchema } from "../../src/data/data-schema";
@@ -35,17 +35,17 @@ describe("[data] DataController", async () => {
meta: { meta: {
total: 0, total: 0,
count: 0, count: 0,
items: 0 items: 0,
} },
}); });
expect(res).toEqual({ expect(res).toEqual({
meta: { meta: {
total: 0, total: 0,
count: 0, count: 0,
items: 0 items: 0,
}, },
data: [] data: [],
}); });
}); });
@@ -59,22 +59,22 @@ describe("[data] DataController", async () => {
data: [] as any, data: [] as any,
sql: "", sql: "",
parameters: [] as any, parameters: [] as any,
result: [] as any result: [] as any,
}); });
expect(res).toEqual({ expect(res).toEqual({
data: [] data: [],
}); });
}); });
describe("getController", async () => { describe("getController", async () => {
const users = new Entity("users", [ const users = new Entity("users", [
new TextField("name", { required: true }), new TextField("name", { required: true }),
new TextField("bio") new TextField("bio"),
]); ]);
const posts = new Entity("posts", [new TextField("content")]); const posts = new Entity("posts", [new TextField("content")]);
const em = new EntityManager([users, posts], dummyConnection, [ const em = new EntityManager([users, posts], dummyConnection, [
new ManyToOneRelation(posts, users) new ManyToOneRelation(posts, users),
]); ]);
await em.schema().sync({ force: true }); await em.schema().sync({ force: true });
@@ -83,12 +83,12 @@ describe("[data] DataController", async () => {
users: [ users: [
{ name: "foo", bio: "bar" }, { name: "foo", bio: "bar" },
{ name: "bar", bio: null }, { name: "bar", bio: null },
{ name: "baz", bio: "!!!" } { name: "baz", bio: "!!!" },
], ],
posts: [ posts: [
{ content: "post 1", users_id: 1 }, { content: "post 1", users_id: 1 },
{ content: "post 2", users_id: 2 } { content: "post 2", users_id: 2 },
] ],
}; };
const ctx: any = { em, guard: new Guard() }; const ctx: any = { em, guard: new Guard() };
@@ -118,7 +118,7 @@ describe("[data] DataController", async () => {
for await (const _user of fixtures.users) { for await (const _user of fixtures.users) {
const res = await app.request("/entity/users", { const res = await app.request("/entity/users", {
method: "POST", method: "POST",
body: JSON.stringify(_user) body: JSON.stringify(_user),
}); });
//console.log("res", { _user }, res); //console.log("res", { _user }, res);
const result = (await res.json()) as MutatorResponse; const result = (await res.json()) as MutatorResponse;
@@ -133,7 +133,7 @@ describe("[data] DataController", async () => {
for await (const _post of fixtures.posts) { for await (const _post of fixtures.posts) {
const res = await app.request("/entity/posts", { const res = await app.request("/entity/posts", {
method: "POST", method: "POST",
body: JSON.stringify(_post) body: JSON.stringify(_post),
}); });
const result = (await res.json()) as MutatorResponse; const result = (await res.json()) as MutatorResponse;
const { id, ...data } = result.data as any; const { id, ...data } = result.data as any;
@@ -159,11 +159,11 @@ describe("[data] DataController", async () => {
const res = await app.request("/entity/users/query", { const res = await app.request("/entity/users/query", {
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/json" "Content-Type": "application/json",
}, },
body: JSON.stringify({ body: JSON.stringify({
where: { bio: { $isnull: 1 } } where: { bio: { $isnull: 1 } },
}) }),
}); });
const data = (await res.json()) as RepositoryResponse; const data = (await res.json()) as RepositoryResponse;
@@ -199,7 +199,7 @@ describe("[data] DataController", async () => {
test("/:entity (update one)", async () => { test("/:entity (update one)", async () => {
const res = await app.request("/entity/users/3", { const res = await app.request("/entity/users/3", {
method: "PATCH", method: "PATCH",
body: JSON.stringify({ name: "new name" }) body: JSON.stringify({ name: "new name" }),
}); });
const { data } = (await res.json()) as MutatorResponse; const { data } = (await res.json()) as MutatorResponse;
@@ -221,7 +221,7 @@ describe("[data] DataController", async () => {
test("/:entity/:id (delete one)", async () => { test("/:entity/:id (delete one)", async () => {
const res = await app.request("/entity/posts/2", { const res = await app.request("/entity/posts/2", {
method: "DELETE" method: "DELETE",
}); });
const { data } = (await res.json()) as RepositoryResponse<EntityData>; const { data } = (await res.json()) as RepositoryResponse<EntityData>;
expect(data).toEqual({ id: 2, ...fixtures.posts[1] }); expect(data).toEqual({ id: 2, ...fixtures.posts[1] });

View File

@@ -30,7 +30,7 @@ describe("data-query-impl", () => {
[{ val: { $isnull: 0 } }, '"val" is not null', []], [{ val: { $isnull: 0 } }, '"val" is not null', []],
[{ val: { $isnull: false } }, '"val" is not null', []], [{ val: { $isnull: false } }, '"val" is not null', []],
[{ val: { $like: "what" } }, '"val" like ?', ["what"]], [{ val: { $like: "what" } }, '"val" like ?', ["what"]],
[{ val: { $like: "w*t" } }, '"val" like ?', ["w%t"]] [{ val: { $like: "w*t" } }, '"val" like ?', ["w%t"]],
]; ];
for (const [query, expectedSql, expectedParams] of tests) { for (const [query, expectedSql, expectedParams] of tests) {
@@ -51,22 +51,22 @@ describe("data-query-impl", () => {
[ [
{ val1: { $eq: "foo" }, val2: { $eq: "bar" } }, { val1: { $eq: "foo" }, val2: { $eq: "bar" } },
'("val1" = ? and "val2" = ?)', '("val1" = ? and "val2" = ?)',
["foo", "bar"] ["foo", "bar"],
], ],
[ [
{ val1: { $eq: "foo" }, val2: { $eq: "bar" } }, { val1: { $eq: "foo" }, val2: { $eq: "bar" } },
'("val1" = ? and "val2" = ?)', '("val1" = ? and "val2" = ?)',
["foo", "bar"] ["foo", "bar"],
], ],
// or constructs // or constructs
[ [
{ $or: { val1: { $eq: "foo" }, val2: { $eq: "bar" } } }, { $or: { val1: { $eq: "foo" }, val2: { $eq: "bar" } } },
'("val1" = ? or "val2" = ?)', '("val1" = ? or "val2" = ?)',
["foo", "bar"] ["foo", "bar"],
], ],
[{ val1: { $eq: 1 }, $or: { val1: { $eq: 2 } } }, '("val1" = ? or "val1" = ?)', [1, 2]], [{ val1: { $eq: 1 }, $or: { val1: { $eq: 2 } } }, '("val1" = ? or "val1" = ?)', [1, 2]],
[{ val1: { $eq: 1 }, $or: { val1: { $eq: 2 } } }, '("val1" = ? or "val1" = ?)', [1, 2]] [{ val1: { $eq: 1 }, $or: { val1: { $eq: 2 } } }, '("val1" = ? or "val1" = ?)', [1, 2]],
]; ];
for (const [query, expectedSql, expectedParams] of tests) { for (const [query, expectedSql, expectedParams] of tests) {
@@ -86,7 +86,7 @@ describe("data-query-impl", () => {
// or constructs // or constructs
[{ $or: { val1: { $eq: "foo" }, val2: { $eq: "bar" } } }, ["val1", "val2"]], [{ $or: { val1: { $eq: "foo" }, val2: { $eq: "bar" } } }, ["val1", "val2"]],
[{ val1: { $eq: 1 }, $or: { val1: { $eq: 2 } } }, ["val1"]] [{ val1: { $eq: 1 }, $or: { val1: { $eq: 2 } } }, ["val1"]],
]; ];
for (const [query, expectedKeys] of tests) { for (const [query, expectedKeys] of tests) {
@@ -105,23 +105,23 @@ describe("data-query-impl", () => {
posts: { posts: {
with: { with: {
images: { images: {
select: ["id"] select: ["id"],
} },
} },
} },
} },
}, },
{ {
with: { with: {
posts: { posts: {
with: { with: {
images: { images: {
select: ["id"] select: ["id"],
} },
} },
} },
} },
} },
); );
// over http // over http

View File

@@ -5,7 +5,7 @@ import {
NumberField, NumberField,
PrimaryField, PrimaryField,
Repository, Repository,
TextField TextField,
} from "../../src/data"; } from "../../src/data";
import { getDummyConnection } from "./helper"; import { getDummyConnection } from "./helper";
@@ -18,14 +18,14 @@ describe("some tests", async () => {
const users = new Entity("users", [ const users = new Entity("users", [
new TextField("username", { required: true, default_value: "nobody" }), new TextField("username", { required: true, default_value: "nobody" }),
new TextField("email", { maxLength: 3 }) new TextField("email", { maxLength: 3 }),
]); ]);
const posts = new Entity("posts", [ const posts = new Entity("posts", [
new TextField("title"), new TextField("title"),
new TextField("content"), new TextField("content"),
new TextField("created_at"), new TextField("created_at"),
new NumberField("likes", { default_value: 0 }) new NumberField("likes", { default_value: 0 }),
]); ]);
const em = new EntityManager([users, posts], connection); const em = new EntityManager([users, posts], connection);
@@ -43,7 +43,7 @@ describe("some tests", async () => {
});*/ });*/
expect(query.sql).toBe( expect(query.sql).toBe(
'select "users"."id" as "id", "users"."username" as "username", "users"."email" as "email" from "users" where "id" = ? limit ?' 'select "users"."id" as "id", "users"."username" as "username", "users"."email" as "email" from "users" where "id" = ? limit ?',
); );
expect(query.parameters).toEqual([1, 1]); expect(query.parameters).toEqual([1, 1]);
expect(query.result).toEqual([]); expect(query.result).toEqual([]);
@@ -53,7 +53,7 @@ describe("some tests", async () => {
const query = await em.repository(users).findMany(); const query = await em.repository(users).findMany();
expect(query.sql).toBe( expect(query.sql).toBe(
'select "users"."id" as "id", "users"."username" as "username", "users"."email" as "email" from "users" order by "users"."id" asc limit ? offset ?' 'select "users"."id" as "id", "users"."username" as "username", "users"."email" as "email" from "users" order by "users"."id" asc limit ? offset ?',
); );
expect(query.parameters).toEqual([10, 0]); expect(query.parameters).toEqual([10, 0]);
expect(query.result).toEqual([]); expect(query.result).toEqual([]);
@@ -63,7 +63,7 @@ describe("some tests", async () => {
const query = await em.repository(posts).findMany(); const query = await em.repository(posts).findMany();
expect(query.sql).toBe( expect(query.sql).toBe(
'select "posts"."id" as "id", "posts"."title" as "title", "posts"."content" as "content", "posts"."created_at" as "created_at", "posts"."likes" as "likes" from "posts" order by "posts"."id" asc limit ? offset ?' 'select "posts"."id" as "id", "posts"."title" as "title", "posts"."content" as "content", "posts"."created_at" as "created_at", "posts"."likes" as "likes" from "posts" order by "posts"."id" asc limit ? offset ?',
); );
expect(query.parameters).toEqual([10, 0]); expect(query.parameters).toEqual([10, 0]);
expect(query.result).toEqual([]); expect(query.result).toEqual([]);
@@ -74,7 +74,7 @@ describe("some tests", async () => {
new Entity("users", [ new Entity("users", [
new TextField("username"), new TextField("username"),
new TextField("email"), new TextField("email"),
new TextField("email") // not throwing, it's just being ignored new TextField("email"), // not throwing, it's just being ignored
]); ]);
}).toBeDefined(); }).toBeDefined();
@@ -83,7 +83,7 @@ describe("some tests", async () => {
new TextField("username"), new TextField("username"),
new TextField("email"), new TextField("email"),
// field config differs, will throw // field config differs, will throw
new TextField("email", { required: true }) new TextField("email", { required: true }),
]); ]);
}).toThrow(); }).toThrow();
@@ -91,7 +91,7 @@ describe("some tests", async () => {
new Entity("users", [ new Entity("users", [
new PrimaryField(), new PrimaryField(),
new TextField("username"), new TextField("username"),
new TextField("email") new TextField("email"),
]); ]);
}).toBeDefined(); }).toBeDefined();
}); });

View File

@@ -16,7 +16,7 @@ export function getDummyDatabase(memory: boolean = true): {
afterAllCleanup: async () => { afterAllCleanup: async () => {
if (!memory) await unlink(DB_NAME); if (!memory) await unlink(DB_NAME);
return true; return true;
} },
}; };
} }
@@ -26,7 +26,7 @@ export function getDummyConnection(memory: boolean = true) {
return { return {
dummyConnection, dummyConnection,
afterAllCleanup afterAllCleanup,
}; };
} }

View File

@@ -6,7 +6,7 @@ import {
ManyToOneRelation, ManyToOneRelation,
NumberField, NumberField,
SchemaManager, SchemaManager,
TextField TextField,
} from "../../src/data"; } from "../../src/data";
import { getDummyConnection } from "./helper"; import { getDummyConnection } from "./helper";
@@ -21,7 +21,7 @@ describe("Mutator relation", async () => {
const posts = new Entity("posts", [ const posts = new Entity("posts", [
new TextField("title"), new TextField("title"),
new TextField("content", { default_value: "..." }), new TextField("content", { default_value: "..." }),
new NumberField("count", { default_value: 0 }) new NumberField("count", { default_value: 0 }),
]); ]);
const users = new Entity("users", [new TextField("username")]); const users = new Entity("users", [new TextField("username")]);
@@ -44,7 +44,7 @@ describe("Mutator relation", async () => {
expect(em.mutator(posts).insertOne({ title: "post2", users_id: 10 })).rejects.toThrow(); expect(em.mutator(posts).insertOne({ title: "post2", users_id: 10 })).rejects.toThrow();
expect( expect(
em.mutator(posts).insertOne({ title: "post2", users_id: data.id }) em.mutator(posts).insertOne({ title: "post2", users_id: data.id }),
).resolves.toBeDefined(); ).resolves.toBeDefined();
}); });
}); });

View File

@@ -14,7 +14,7 @@ describe("Mutator simple", async () => {
const items = new Entity("items", [ const items = new Entity("items", [
new TextField("label", { required: true, minLength: 1 }), new TextField("label", { required: true, minLength: 1 }),
new NumberField("count", { default_value: 0 }) new NumberField("count", { default_value: 0 }),
]); ]);
const em = new EntityManager<any>([items], connection); const em = new EntityManager<any>([items], connection);
@@ -29,11 +29,11 @@ describe("Mutator simple", async () => {
test("insert single row", async () => { test("insert single row", async () => {
const mutation = await em.mutator(items).insertOne({ const mutation = await em.mutator(items).insertOne({
label: "test", label: "test",
count: 1 count: 1,
}); });
expect(mutation.sql).toBe( expect(mutation.sql).toBe(
'insert into "items" ("count", "label") values (?, ?) returning "id", "label", "count"' 'insert into "items" ("count", "label") values (?, ?) returning "id", "label", "count"',
); );
expect(mutation.data).toEqual({ id: 1, label: "test", count: 1 }); expect(mutation.data).toEqual({ id: 1, label: "test", count: 1 });
@@ -41,8 +41,8 @@ describe("Mutator simple", async () => {
limit: 1, limit: 1,
sort: { sort: {
by: "id", by: "id",
dir: "desc" dir: "desc",
} },
}); });
expect(query.result).toEqual([{ id: 1, label: "test", count: 1 }]); expect(query.result).toEqual([{ id: 1, label: "test", count: 1 }]);
@@ -53,18 +53,18 @@ describe("Mutator simple", async () => {
limit: 1, limit: 1,
sort: { sort: {
by: "id", by: "id",
dir: "desc" dir: "desc",
} },
}); });
const id = query.data![0].id as number; const id = query.data![0].id as number;
const mutation = await em.mutator(items).updateOne(id, { const mutation = await em.mutator(items).updateOne(id, {
label: "new label", label: "new label",
count: 100 count: 100,
}); });
expect(mutation.sql).toBe( expect(mutation.sql).toBe(
'update "items" set "label" = ?, "count" = ? where "id" = ? returning "id", "label", "count"' 'update "items" set "label" = ?, "count" = ? where "id" = ? returning "id", "label", "count"',
); );
expect(mutation.data).toEqual({ id, label: "new label", count: 100 }); expect(mutation.data).toEqual({ id, label: "new label", count: 100 });
}); });
@@ -74,15 +74,15 @@ describe("Mutator simple", async () => {
limit: 1, limit: 1,
sort: { sort: {
by: "id", by: "id",
dir: "desc" dir: "desc",
} },
}); });
const id = query.data![0].id as number; const id = query.data![0].id as number;
const mutation = await em.mutator(items).deleteOne(id); const mutation = await em.mutator(items).deleteOne(id);
expect(mutation.sql).toBe( expect(mutation.sql).toBe(
'delete from "items" where "id" = ? returning "id", "label", "count"' 'delete from "items" where "id" = ? returning "id", "label", "count"',
); );
expect(mutation.data).toEqual({ id, label: "new label", count: 100 }); expect(mutation.data).toEqual({ id, label: "new label", count: 100 });
@@ -94,7 +94,7 @@ describe("Mutator simple", async () => {
const incompleteCreate = async () => const incompleteCreate = async () =>
await em.mutator(items).insertOne({ await em.mutator(items).insertOne({
//label: "test", //label: "test",
count: 1 count: 1,
}); });
expect(incompleteCreate()).rejects.toThrow(); expect(incompleteCreate()).rejects.toThrow();
@@ -104,7 +104,7 @@ describe("Mutator simple", async () => {
const invalidCreate1 = async () => const invalidCreate1 = async () =>
await em.mutator(items).insertOne({ await em.mutator(items).insertOne({
label: 111, // this should work label: 111, // this should work
count: "1" // this should fail count: "1", // this should fail
}); });
expect(invalidCreate1()).rejects.toThrow(TransformPersistFailedException); expect(invalidCreate1()).rejects.toThrow(TransformPersistFailedException);
@@ -112,7 +112,7 @@ describe("Mutator simple", async () => {
const invalidCreate2 = async () => const invalidCreate2 = async () =>
await em.mutator(items).insertOne({ await em.mutator(items).insertOne({
label: "", // this should fail label: "", // this should fail
count: 1 count: 1,
}); });
expect(invalidCreate2()).rejects.toThrow(TransformPersistFailedException); expect(invalidCreate2()).rejects.toThrow(TransformPersistFailedException);
@@ -152,27 +152,27 @@ describe("Mutator simple", async () => {
await em.mutator(items).updateWhere( await em.mutator(items).updateWhere(
{ count: 2 }, { count: 2 },
{ {
count: 10 count: 10,
} },
); );
expect((await em.repository(items).findMany()).data).toEqual([ expect((await em.repository(items).findMany()).data).toEqual([
{ id: 6, label: "update", count: 1 }, { id: 6, label: "update", count: 1 },
{ id: 7, label: "update too", count: 1 }, { id: 7, label: "update too", count: 1 },
{ id: 8, label: "keep", count: 0 } { id: 8, label: "keep", count: 0 },
]); ]);
// expect 2 to be updated // expect 2 to be updated
await em.mutator(items).updateWhere( await em.mutator(items).updateWhere(
{ count: 2 }, { count: 2 },
{ {
count: 1 count: 1,
} },
); );
expect((await em.repository(items).findMany()).data).toEqual([ expect((await em.repository(items).findMany()).data).toEqual([
{ id: 6, label: "update", count: 2 }, { id: 6, label: "update", count: 2 },
{ id: 7, label: "update too", count: 2 }, { id: 7, label: "update too", count: 2 },
{ id: 8, label: "keep", count: 0 } { id: 8, label: "keep", count: 0 },
]); ]);
}); });

View File

@@ -23,23 +23,23 @@ describe("Polymorphic", async () => {
source: "categories", source: "categories",
target: "media", target: "media",
config: { config: {
mappedBy: "image" mappedBy: "image",
} },
}); });
// media should not see categories // media should not see categories
expect(em.relationsOf(media.name).map((r) => r.toJSON())).toEqual([]); expect(em.relationsOf(media.name).map((r) => r.toJSON())).toEqual([]);
// it's important that media cannot access categories // it's important that media cannot access categories
expect(em.relations.targetRelationsOf(categories).map((r) => r.source.entity.name)).toEqual( expect(em.relations.targetRelationsOf(categories).map((r) => r.source.entity.name)).toEqual(
[] [],
); );
expect(em.relations.targetRelationsOf(media).map((r) => r.source.entity.name)).toEqual([]); expect(em.relations.targetRelationsOf(media).map((r) => r.source.entity.name)).toEqual([]);
expect(em.relations.sourceRelationsOf(categories).map((r) => r.target.entity.name)).toEqual([ expect(em.relations.sourceRelationsOf(categories).map((r) => r.target.entity.name)).toEqual([
"media" "media",
]); ]);
expect(em.relations.sourceRelationsOf(categories).map((r) => r.target.reference)).toEqual([ expect(em.relations.sourceRelationsOf(categories).map((r) => r.target.reference)).toEqual([
"image" "image",
]); ]);
expect(em.relations.sourceRelationsOf(media).map((r) => r.target.entity.name)).toEqual([]); expect(em.relations.sourceRelationsOf(media).map((r) => r.target.entity.name)).toEqual([]);
@@ -48,7 +48,7 @@ describe("Polymorphic", async () => {
"id", "id",
"path", "path",
"reference", "reference",
"entity_id" "entity_id",
]); ]);
expect(media.getSelect()).toEqual(["id", "path"]); expect(media.getSelect()).toEqual(["id", "path"]);
}); });
@@ -60,7 +60,7 @@ describe("Polymorphic", async () => {
const entities = [media, categories]; const entities = [media, categories];
const single = new PolymorphicRelation(categories, media, { const single = new PolymorphicRelation(categories, media, {
mappedBy: "single", mappedBy: "single",
targetCardinality: 1 targetCardinality: 1,
}); });
const multiple = new PolymorphicRelation(categories, media, { mappedBy: "multiple" }); const multiple = new PolymorphicRelation(categories, media, { mappedBy: "multiple" });
@@ -71,17 +71,17 @@ describe("Polymorphic", async () => {
// it's important that media cannot access categories // it's important that media cannot access categories
expect(em.relations.targetRelationsOf(categories).map((r) => r.source.entity.name)).toEqual( expect(em.relations.targetRelationsOf(categories).map((r) => r.source.entity.name)).toEqual(
[] [],
); );
expect(em.relations.targetRelationsOf(media).map((r) => r.source.entity.name)).toEqual([]); expect(em.relations.targetRelationsOf(media).map((r) => r.source.entity.name)).toEqual([]);
expect(em.relations.sourceRelationsOf(categories).map((r) => r.target.entity.name)).toEqual([ expect(em.relations.sourceRelationsOf(categories).map((r) => r.target.entity.name)).toEqual([
"media", "media",
"media" "media",
]); ]);
expect(em.relations.sourceRelationsOf(categories).map((r) => r.target.reference)).toEqual([ expect(em.relations.sourceRelationsOf(categories).map((r) => r.target.reference)).toEqual([
"single", "single",
"multiple" "multiple",
]); ]);
expect(em.relations.sourceRelationsOf(media).map((r) => r.target.entity.name)).toEqual([]); expect(em.relations.sourceRelationsOf(media).map((r) => r.target.entity.name)).toEqual([]);
@@ -90,7 +90,7 @@ describe("Polymorphic", async () => {
"id", "id",
"path", "path",
"reference", "reference",
"entity_id" "entity_id",
]); ]);
}); });
}); });

View File

@@ -12,7 +12,7 @@ import {
NumberField, NumberField,
OneToOneRelation, OneToOneRelation,
PolymorphicRelation, PolymorphicRelation,
TextField TextField,
} from "../../src/data"; } from "../../src/data";
import { DummyConnection } from "../../src/data/connection/DummyConnection"; import { DummyConnection } from "../../src/data/connection/DummyConnection";
import { import {
@@ -31,7 +31,7 @@ import {
medium, medium,
number, number,
relation, relation,
text text,
} from "../../src/data/prototype"; } from "../../src/data/prototype";
import { MediaField } from "../../src/media/MediaField"; import { MediaField } from "../../src/media/MediaField";
@@ -54,7 +54,7 @@ describe("prototype", () => {
name: text(), name: text(),
bio: text(), bio: text(),
age: number(), age: number(),
some: number() some: number(),
}); });
type db = { type db = {
users: Schema<typeof users>; users: Schema<typeof users>;
@@ -70,7 +70,7 @@ describe("prototype", () => {
name: text({ default_value: "hello" }).required(), name: text({ default_value: "hello" }).required(),
bio: text(), bio: text(),
age: number(), age: number(),
some: number().required() some: number().required(),
}); });
const obj: InsertSchema<typeof user> = { name: "yo", some: 1 }; const obj: InsertSchema<typeof user> = { name: "yo", some: 1 };
@@ -83,12 +83,12 @@ describe("prototype", () => {
new TextField("title", { required: true }), new TextField("title", { required: true }),
new TextField("content"), new TextField("content"),
new DateField("created_at", { new DateField("created_at", {
type: "datetime" type: "datetime",
}), }),
// @ts-ignore // @ts-ignore
new MediaField("images", { entity: "posts" }), new MediaField("images", { entity: "posts" }),
// @ts-ignore // @ts-ignore
new MediaField("cover", { entity: "posts", max_items: 1 }) new MediaField("cover", { entity: "posts", max_items: 1 }),
]); ]);
const posts2 = entity("posts", { const posts2 = entity("posts", {
@@ -96,7 +96,7 @@ describe("prototype", () => {
content: text(), content: text(),
created_at: datetime(), created_at: datetime(),
images: media(), images: media(),
cover: medium() cover: medium(),
}); });
type Posts = Schema<typeof posts2>; type Posts = Schema<typeof posts2>;
@@ -117,11 +117,11 @@ describe("prototype", () => {
type: "objects", type: "objects",
values: [ values: [
{ value: "active", label: "Active" }, { value: "active", label: "Active" },
{ value: "inactive", label: "Not active" } { value: "inactive", label: "Not active" },
] ],
} },
}), }),
new JsonField("json") new JsonField("json"),
]); ]);
const test2 = entity("test", { const test2 = entity("test", {
@@ -134,10 +134,10 @@ describe("prototype", () => {
status: enumm<"active" | "inactive">({ status: enumm<"active" | "inactive">({
enum: [ enum: [
{ value: "active", label: "Active" }, { value: "active", label: "Active" },
{ value: "inactive", label: "Not active" } { value: "inactive", label: "Not active" },
] ],
}), }),
json: json<{ some: number }>() json: json<{ some: number }>(),
}); });
expect(test.toJSON()).toEqual(test2.toJSON()); expect(test.toJSON()).toEqual(test2.toJSON());
@@ -161,12 +161,12 @@ describe("prototype", () => {
// category has single image // category has single image
new PolymorphicRelation(categories, _media, { new PolymorphicRelation(categories, _media, {
mappedBy: "image", mappedBy: "image",
targetCardinality: 1 targetCardinality: 1,
}), }),
// post has multiple images // post has multiple images
new PolymorphicRelation(posts, _media, { mappedBy: "images" }), new PolymorphicRelation(posts, _media, { mappedBy: "images" }),
new PolymorphicRelation(posts, _media, { mappedBy: "cover", targetCardinality: 1 }) new PolymorphicRelation(posts, _media, { mappedBy: "cover", targetCardinality: 1 }),
]; ];
const relations2 = [ const relations2 = [
@@ -180,7 +180,7 @@ describe("prototype", () => {
relation(categories).polyToOne(_media, { mappedBy: "image" }), relation(categories).polyToOne(_media, { mappedBy: "image" }),
relation(posts).polyToMany(_media, { mappedBy: "images" }), relation(posts).polyToMany(_media, { mappedBy: "images" }),
relation(posts).polyToOne(_media, { mappedBy: "cover" }) relation(posts).polyToOne(_media, { mappedBy: "cover" }),
]; ];
expect(relations.map((r) => r.toJSON())).toEqual(relations2.map((r) => r.toJSON())); expect(relations.map((r) => r.toJSON())).toEqual(relations2.map((r) => r.toJSON()));
@@ -194,21 +194,21 @@ describe("prototype", () => {
posts, posts,
categories, categories,
{ {
connectionTableMappedName: "custom" connectionTableMappedName: "custom",
}, },
[new TextField("description")] [new TextField("description")],
); );
const fields = { const fields = {
description: text() description: text(),
}; };
let o: FieldSchema<typeof fields>; let o: FieldSchema<typeof fields>;
const rel2 = relation(posts).manyToMany( const rel2 = relation(posts).manyToMany(
categories, categories,
{ {
connectionTableMappedName: "custom" connectionTableMappedName: "custom",
}, },
fields fields,
); );
expect(rel.toJSON()).toEqual(rel2.toJSON()); expect(rel.toJSON()).toEqual(rel2.toJSON());
@@ -216,11 +216,11 @@ describe("prototype", () => {
test("devexample", async () => { test("devexample", async () => {
const users = entity("users", { const users = entity("users", {
username: text() username: text(),
}); });
const comments = entity("comments", { const comments = entity("comments", {
content: text() content: text(),
}); });
const posts = entity("posts", { const posts = entity("posts", {
@@ -228,17 +228,17 @@ describe("prototype", () => {
content: text(), content: text(),
created_at: datetime(), created_at: datetime(),
images: media(), images: media(),
cover: medium() cover: medium(),
}); });
const categories = entity("categories", { const categories = entity("categories", {
name: text(), name: text(),
description: text(), description: text(),
image: medium() image: medium(),
}); });
const settings = entity("settings", { const settings = entity("settings", {
theme: text() theme: text(),
}); });
const test = entity("test", { const test = entity("test", {
@@ -251,10 +251,10 @@ describe("prototype", () => {
status: enumm<"active" | "inactive">({ status: enumm<"active" | "inactive">({
enum: [ enum: [
{ value: "active", label: "Active" }, { value: "active", label: "Active" },
{ value: "inactive", label: "Not active" } { value: "inactive", label: "Not active" },
] ],
}), }),
json: json<{ some: number }>() json: json<{ some: number }>(),
}); });
const _media = entity("media", {}); const _media = entity("media", {});
@@ -270,7 +270,7 @@ describe("prototype", () => {
relation(users).oneToOne(settings), relation(users).oneToOne(settings),
relation(comments).manyToOne(users, { required: true }), relation(comments).manyToOne(users, { required: true }),
relation(comments).manyToOne(posts, { required: true }) relation(comments).manyToOne(posts, { required: true }),
]; ];
const obj: Schema<typeof test> = {} as any; const obj: Schema<typeof test> = {} as any;
@@ -281,12 +281,12 @@ describe("prototype", () => {
{ {
posts: entity("posts", { name: text(), slug: text().required() }), posts: entity("posts", { name: text(), slug: text().required() }),
comments: entity("comments", { some: text() }), comments: entity("comments", { some: text() }),
users: entity("users", { email: text() }) users: entity("users", { email: text() }),
}, },
({ relation, index }, { posts, comments, users }) => { ({ relation, index }, { posts, comments, users }) => {
relation(posts).manyToOne(comments).manyToOne(users); relation(posts).manyToOne(comments).manyToOne(users);
index(posts).on(["name"]).on(["slug"], true); index(posts).on(["name"]).on(["slug"], true);
} },
); );
type LocalDb = (typeof _em)["DB"]; type LocalDb = (typeof _em)["DB"];
@@ -294,7 +294,7 @@ describe("prototype", () => {
const es = [ const es = [
new Entity("posts", [new TextField("name"), new TextField("slug", { required: true })]), new Entity("posts", [new TextField("name"), new TextField("slug", { required: true })]),
new Entity("comments", [new TextField("some")]), new Entity("comments", [new TextField("some")]),
new Entity("users", [new TextField("email")]) new Entity("users", [new TextField("email")]),
]; ];
const _em2 = new EntityManager( const _em2 = new EntityManager(
es, es,
@@ -302,8 +302,8 @@ describe("prototype", () => {
[new ManyToOneRelation(es[0], es[1]), new ManyToOneRelation(es[0], es[2])], [new ManyToOneRelation(es[0], es[1]), new ManyToOneRelation(es[0], es[2])],
[ [
new EntityIndex(es[0], [es[0].field("name")!]), new EntityIndex(es[0], [es[0].field("name")!]),
new EntityIndex(es[0], [es[0].field("slug")!], true) new EntityIndex(es[0], [es[0].field("slug")!], true),
] ],
); );
// @ts-ignore // @ts-ignore

View File

@@ -6,7 +6,7 @@ import {
ManyToOneRelation, ManyToOneRelation,
OneToOneRelation, OneToOneRelation,
PolymorphicRelation, PolymorphicRelation,
RelationField RelationField,
} from "../../src/data/relations"; } from "../../src/data/relations";
import { getDummyConnection } from "./helper"; import { getDummyConnection } from "./helper";
@@ -22,7 +22,7 @@ describe("Relations", async () => {
const r1 = new RelationField("users_id", { const r1 = new RelationField("users_id", {
reference: "users", reference: "users",
target: "users", target: "users",
target_field: "id" target_field: "id",
}); });
const sql1 = schema const sql1 = schema
@@ -31,14 +31,14 @@ describe("Relations", async () => {
.compile().sql; .compile().sql;
expect(sql1).toBe( expect(sql1).toBe(
'create table "posts" ("users_id" integer references "users" ("id") on delete set null)' 'create table "posts" ("users_id" integer references "users" ("id") on delete set null)',
); );
//const r2 = new RelationField(new Entity("users"), "author"); //const r2 = new RelationField(new Entity("users"), "author");
const r2 = new RelationField("author_id", { const r2 = new RelationField("author_id", {
reference: "author", reference: "author",
target: "users", target: "users",
target_field: "id" target_field: "id",
}); });
const sql2 = schema const sql2 = schema
@@ -47,7 +47,7 @@ describe("Relations", async () => {
.compile().sql; .compile().sql;
expect(sql2).toBe( expect(sql2).toBe(
'create table "posts" ("author_id" integer references "users" ("id") on delete set null)' 'create table "posts" ("author_id" integer references "users" ("id") on delete set null)',
); );
}); });
@@ -57,7 +57,7 @@ describe("Relations", async () => {
reference: "users", reference: "users",
target: "users", target: "users",
target_field: "id", target_field: "id",
required: true required: true,
}); });
expect(r1.isRequired()).toBeTrue(); expect(r1.isRequired()).toBeTrue();
}); });
@@ -66,8 +66,8 @@ describe("Relations", async () => {
const users = new Entity("users", [new TextField("username")]); const users = new Entity("users", [new TextField("username")]);
const posts = new Entity("posts", [ const posts = new Entity("posts", [
new TextField("title", { new TextField("title", {
maxLength: 2 maxLength: 2,
}) }),
]); ]);
const entities = [users, posts]; const entities = [users, posts];
@@ -122,7 +122,7 @@ describe("Relations", async () => {
.selectFrom(users.name) .selectFrom(users.name)
.select((eb) => postAuthorRel.buildWith(users, "posts")(eb).as("posts")); .select((eb) => postAuthorRel.buildWith(users, "posts")(eb).as("posts"));
expect(selectPostsFromUsers.compile().sql).toBe( expect(selectPostsFromUsers.compile().sql).toBe(
'select (select from "posts" as "posts" where "posts"."author_id" = "users"."id") as "posts" from "users"' 'select (select from "posts" as "posts" where "posts"."author_id" = "users"."id") as "posts" from "users"',
); );
expect(postAuthorRel!.getField()).toBeInstanceOf(RelationField); expect(postAuthorRel!.getField()).toBeInstanceOf(RelationField);
const userObj = { id: 1, username: "test" }; const userObj = { id: 1, username: "test" };
@@ -142,7 +142,7 @@ describe("Relations", async () => {
.select((eb) => postAuthorRel.buildWith(posts, "author")(eb).as("author")); .select((eb) => postAuthorRel.buildWith(posts, "author")(eb).as("author"));
expect(selectUsersFromPosts.compile().sql).toBe( expect(selectUsersFromPosts.compile().sql).toBe(
'select (select from "users" as "author" where "author"."id" = "posts"."author_id" limit ?) as "author" from "posts"' 'select (select from "users" as "author" where "author"."id" = "posts"."author_id" limit ?) as "author" from "posts"',
); );
expect(postAuthorRel.getField()).toBeInstanceOf(RelationField); expect(postAuthorRel.getField()).toBeInstanceOf(RelationField);
const postObj = { id: 1, title: "test" }; const postObj = { id: 1, title: "test" };
@@ -158,7 +158,7 @@ describe("Relations", async () => {
$detach: false, $detach: false,
primary: undefined, primary: undefined,
cardinality: undefined, cardinality: undefined,
relation_type: "n:1" relation_type: "n:1",
}); });
expect(postAuthorRel!.helper(posts.name)!.getMutationInfo()).toEqual({ expect(postAuthorRel!.helper(posts.name)!.getMutationInfo()).toEqual({
@@ -170,7 +170,7 @@ describe("Relations", async () => {
$detach: false, $detach: false,
primary: "id", primary: "id",
cardinality: 1, cardinality: 1,
relation_type: "n:1" relation_type: "n:1",
}); });
/*console.log("ManyToOne (source=posts, target=users)"); /*console.log("ManyToOne (source=posts, target=users)");
@@ -225,7 +225,7 @@ describe("Relations", async () => {
$detach: false, $detach: false,
primary: "id", primary: "id",
cardinality: 1, cardinality: 1,
relation_type: "1:1" relation_type: "1:1",
}); });
expect(userSettingRel!.helper(settings.name)!.getMutationInfo()).toEqual({ expect(userSettingRel!.helper(settings.name)!.getMutationInfo()).toEqual({
reference: "users", reference: "users",
@@ -236,7 +236,7 @@ describe("Relations", async () => {
$detach: false, $detach: false,
primary: undefined, primary: undefined,
cardinality: 1, cardinality: 1,
relation_type: "1:1" relation_type: "1:1",
}); });
/*console.log(""); /*console.log("");
@@ -312,14 +312,14 @@ describe("Relations", async () => {
.selectFrom(posts.name) .selectFrom(posts.name)
.select((eb) => postCategoriesRel.buildWith(posts)(eb).select("id").as("categories")); .select((eb) => postCategoriesRel.buildWith(posts)(eb).select("id").as("categories"));
expect(selectCategoriesFromPosts.compile().sql).toBe( expect(selectCategoriesFromPosts.compile().sql).toBe(
'select (select "id" from "categories" inner join "posts_categories" on "categories"."id" = "posts_categories"."categories_id" where "posts"."id" = "posts_categories"."posts_id" limit ?) as "categories" from "posts"' 'select (select "id" from "categories" inner join "posts_categories" on "categories"."id" = "posts_categories"."categories_id" where "posts"."id" = "posts_categories"."posts_id" limit ?) as "categories" from "posts"',
); );
const selectPostsFromCategories = kysely const selectPostsFromCategories = kysely
.selectFrom(categories.name) .selectFrom(categories.name)
.select((eb) => postCategoriesRel.buildWith(categories)(eb).select("id").as("posts")); .select((eb) => postCategoriesRel.buildWith(categories)(eb).select("id").as("posts"));
expect(selectPostsFromCategories.compile().sql).toBe( expect(selectPostsFromCategories.compile().sql).toBe(
'select (select "id" from "posts" inner join "posts_categories" on "posts"."id" = "posts_categories"."posts_id" where "categories"."id" = "posts_categories"."categories_id" limit ?) as "posts" from "categories"' 'select (select "id" from "posts" inner join "posts_categories" on "posts"."id" = "posts_categories"."posts_id" where "categories"."id" = "posts_categories"."categories_id" limit ?) as "posts" from "categories"',
); );
// mutation info // mutation info
@@ -332,7 +332,7 @@ describe("Relations", async () => {
$detach: true, $detach: true,
primary: "id", primary: "id",
cardinality: undefined, cardinality: undefined,
relation_type: "m:n" relation_type: "m:n",
}); });
expect(relations[0].helper(categories.name)!.getMutationInfo()).toEqual({ expect(relations[0].helper(categories.name)!.getMutationInfo()).toEqual({
reference: "posts", reference: "posts",
@@ -343,7 +343,7 @@ describe("Relations", async () => {
$detach: false, $detach: false,
primary: undefined, primary: undefined,
cardinality: undefined, cardinality: undefined,
relation_type: "m:n" relation_type: "m:n",
}); });
/*console.log(""); /*console.log("");

View File

@@ -6,7 +6,7 @@ describe("[data] Entity", async () => {
new TextField("name", { required: true }), new TextField("name", { required: true }),
new TextField("description"), new TextField("description"),
new NumberField("age", { fillable: false, default_value: 18 }), new NumberField("age", { fillable: false, default_value: 18 }),
new TextField("hidden", { hidden: true, default_value: "secret" }) new TextField("hidden", { hidden: true, default_value: "secret" }),
]); ]);
test("getSelect", async () => { test("getSelect", async () => {
@@ -17,7 +17,7 @@ describe("[data] Entity", async () => {
expect(entity.getFillableFields().map((f) => f.name)).toEqual([ expect(entity.getFillableFields().map((f) => f.name)).toEqual([
"name", "name",
"description", "description",
"hidden" "hidden",
]); ]);
}); });
@@ -28,7 +28,7 @@ describe("[data] Entity", async () => {
test("getDefaultObject", async () => { test("getDefaultObject", async () => {
expect(entity.getDefaultObject()).toEqual({ expect(entity.getDefaultObject()).toEqual({
age: 18, age: 18,
hidden: "secret" hidden: "secret",
}); });
}); });

View File

@@ -4,7 +4,7 @@ import {
EntityManager, EntityManager,
ManyToManyRelation, ManyToManyRelation,
ManyToOneRelation, ManyToOneRelation,
SchemaManager SchemaManager,
} from "../../../src/data"; } from "../../../src/data";
import { UnableToConnectException } from "../../../src/data/errors"; import { UnableToConnectException } from "../../../src/data/errors";
import { getDummyConnection } from "../helper"; import { getDummyConnection } from "../helper";
@@ -25,7 +25,7 @@ describe("[data] EntityManager", async () => {
expect(await em.ping()).toBe(true); expect(await em.ping()).toBe(true);
expect(() => em.entity("...")).toThrow(); expect(() => em.entity("...")).toThrow();
expect(() => expect(() =>
em.addRelation(new ManyToOneRelation(new Entity("1"), new Entity("2"))) em.addRelation(new ManyToOneRelation(new Entity("1"), new Entity("2"))),
).toThrow(); ).toThrow();
expect(em.schema()).toBeInstanceOf(SchemaManager); expect(em.schema()).toBeInstanceOf(SchemaManager);
@@ -98,7 +98,7 @@ describe("[data] EntityManager", async () => {
expect(userTargetRel.map((r) => r.other(users).entity.name)).toEqual(["posts", "comments"]); expect(userTargetRel.map((r) => r.other(users).entity.name)).toEqual(["posts", "comments"]);
expect(postTargetRel.map((r) => r.other(posts).entity.name)).toEqual([ expect(postTargetRel.map((r) => r.other(posts).entity.name)).toEqual([
"comments", "comments",
"categories" "categories",
]); ]);
expect(commentTargetRel.map((r) => r.other(comments).entity.name)).toEqual([]); expect(commentTargetRel.map((r) => r.other(comments).entity.name)).toEqual([]);
expect(categoriesTargetRel.map((r) => r.other(categories).entity.name)).toEqual(["posts"]); expect(categoriesTargetRel.map((r) => r.other(categories).entity.name)).toEqual(["posts"]);

View File

@@ -12,7 +12,7 @@ describe("[data] JoinBuilder", async () => {
const em = new EntityManager([users], dummyConnection); const em = new EntityManager([users], dummyConnection);
expect(() => expect(() =>
JoinBuilder.addClause(em, em.connection.kysely.selectFrom("users"), users, ["posts"]) JoinBuilder.addClause(em, em.connection.kysely.selectFrom("users"), users, ["posts"]),
).toThrow('Relation "posts" not found'); ).toThrow('Relation "posts" not found');
}); });
@@ -23,7 +23,7 @@ describe("[data] JoinBuilder", async () => {
const em = new EntityManager([users, posts], dummyConnection, relations); const em = new EntityManager([users, posts], dummyConnection, relations);
const qb = JoinBuilder.addClause(em, em.connection.kysely.selectFrom("users"), users, [ const qb = JoinBuilder.addClause(em, em.connection.kysely.selectFrom("users"), users, [
"posts" "posts",
]); ]);
const res = qb.compile(); const res = qb.compile();
@@ -34,7 +34,7 @@ describe("[data] JoinBuilder", async () => {
);*/ );*/
const qb2 = JoinBuilder.addClause(em, em.connection.kysely.selectFrom("posts"), posts, [ const qb2 = JoinBuilder.addClause(em, em.connection.kysely.selectFrom("posts"), posts, [
"author" "author",
]); ]);
const res2 = qb2.compile(); const res2 = qb2.compile();

View File

@@ -9,7 +9,7 @@ import {
OneToOneRelation, OneToOneRelation,
type RelationField, type RelationField,
RelationMutator, RelationMutator,
TextField TextField,
} from "../../../src/data"; } from "../../../src/data";
import * as proto from "../../../src/data/prototype"; import * as proto from "../../../src/data/prototype";
import { getDummyConnection } from "../helper"; import { getDummyConnection } from "../helper";
@@ -22,7 +22,7 @@ describe("[data] Mutator (base)", async () => {
new TextField("label", { required: true }), new TextField("label", { required: true }),
new NumberField("count"), new NumberField("count"),
new TextField("hidden", { hidden: true }), new TextField("hidden", { hidden: true }),
new TextField("not_fillable", { fillable: false }) new TextField("not_fillable", { fillable: false }),
]); ]);
const em = new EntityManager<any>([entity], dummyConnection); const em = new EntityManager<any>([entity], dummyConnection);
await em.schema().sync({ force: true }); await em.schema().sync({ force: true });
@@ -44,7 +44,7 @@ describe("[data] Mutator (base)", async () => {
test("updateOne", async () => { test("updateOne", async () => {
const { data } = await em.mutator(entity).insertOne(payload); const { data } = await em.mutator(entity).insertOne(payload);
const updated = await em.mutator(entity).updateOne(data.id, { const updated = await em.mutator(entity).updateOne(data.id, {
count: 2 count: 2,
}); });
expect(updated.parameters).toEqual([2, data.id]); expect(updated.parameters).toEqual([2, data.id]);
@@ -77,7 +77,7 @@ describe("[data] Mutator (ManyToOne)", async () => {
// persisting relational field should just return key value to be added // persisting relational field should just return key value to be added
expect( expect(
postRelMutator.persistRelationField(postRelField, "users_id", userData.data.id) postRelMutator.persistRelationField(postRelField, "users_id", userData.data.id),
).resolves.toEqual(["users_id", userData.data.id]); ).resolves.toEqual(["users_id", userData.data.id]);
// persisting invalid value should throw // persisting invalid value should throw
@@ -86,8 +86,8 @@ describe("[data] Mutator (ManyToOne)", async () => {
// persisting reference should ... // persisting reference should ...
expect( expect(
postRelMutator.persistReference(relations[0]!, "users", { postRelMutator.persistReference(relations[0]!, "users", {
$set: { id: userData.data.id } $set: { id: userData.data.id },
}) }),
).resolves.toEqual(["users_id", userData.data.id]); ).resolves.toEqual(["users_id", userData.data.id]);
// @todo: add what methods are allowed to relation, like $create should not be allowed for post<>users // @todo: add what methods are allowed to relation, like $create should not be allowed for post<>users
@@ -99,8 +99,8 @@ describe("[data] Mutator (ManyToOne)", async () => {
expect( expect(
em.mutator(posts).insertOne({ em.mutator(posts).insertOne({
title: "post1", title: "post1",
users_id: 100 // user does not exist yet users_id: 100, // user does not exist yet
}) }),
).rejects.toThrow(); ).rejects.toThrow();
}); });
@@ -111,7 +111,7 @@ describe("[data] Mutator (ManyToOne)", async () => {
const em = new EntityManager([items, cats], dummyConnection, relations); const em = new EntityManager([items, cats], dummyConnection, relations);
expect(em.mutator(items).insertOne({ label: "test" })).rejects.toThrow( expect(em.mutator(items).insertOne({ label: "test" })).rejects.toThrow(
'Field "cats_id" is required' 'Field "cats_id" is required',
); );
}); });
@@ -119,14 +119,14 @@ describe("[data] Mutator (ManyToOne)", async () => {
const { data } = await em.mutator(users).insertOne({ username: "user1" }); const { data } = await em.mutator(users).insertOne({ username: "user1" });
const res = await em.mutator(posts).insertOne({ const res = await em.mutator(posts).insertOne({
title: "post1", title: "post1",
users_id: data.id users_id: data.id,
}); });
expect(res.data.users_id).toBe(data.id); expect(res.data.users_id).toBe(data.id);
// setting "null" should be allowed // setting "null" should be allowed
const res2 = await em.mutator(posts).insertOne({ const res2 = await em.mutator(posts).insertOne({
title: "post1", title: "post1",
users_id: null users_id: null,
}); });
expect(res2.data.users_id).toBe(null); expect(res2.data.users_id).toBe(null);
}); });
@@ -135,14 +135,14 @@ describe("[data] Mutator (ManyToOne)", async () => {
const { data } = await em.mutator(users).insertOne({ username: "user1" }); const { data } = await em.mutator(users).insertOne({ username: "user1" });
const res = await em.mutator(posts).insertOne({ const res = await em.mutator(posts).insertOne({
title: "post1", title: "post1",
users: { $set: { id: data.id } } users: { $set: { id: data.id } },
}); });
expect(res.data.users_id).toBe(data.id); expect(res.data.users_id).toBe(data.id);
// setting "null" should be allowed // setting "null" should be allowed
const res2 = await em.mutator(posts).insertOne({ const res2 = await em.mutator(posts).insertOne({
title: "post1", title: "post1",
users: { $set: { id: null } } users: { $set: { id: null } },
}); });
expect(res2.data.users_id).toBe(null); expect(res2.data.users_id).toBe(null);
}); });
@@ -151,8 +151,8 @@ describe("[data] Mutator (ManyToOne)", async () => {
expect( expect(
em.mutator(posts).insertOne({ em.mutator(posts).insertOne({
title: "test", title: "test",
users: { $create: { username: "test" } } users: { $create: { username: "test" } },
}) }),
).rejects.toThrow(); ).rejects.toThrow();
}); });
@@ -162,27 +162,27 @@ describe("[data] Mutator (ManyToOne)", async () => {
const res2 = await em.mutator(posts).insertOne({ title: "post1" }); const res2 = await em.mutator(posts).insertOne({ title: "post1" });
const up1 = await em.mutator(posts).updateOne(res2.data.id, { const up1 = await em.mutator(posts).updateOne(res2.data.id, {
users: { $set: { id: res1.data.id } } users: { $set: { id: res1.data.id } },
}); });
expect(up1.data.users_id).toBe(res1.data.id); expect(up1.data.users_id).toBe(res1.data.id);
const up2 = await em.mutator(posts).updateOne(res2.data.id, { const up2 = await em.mutator(posts).updateOne(res2.data.id, {
users: { $set: { id: res1_1.data.id } } users: { $set: { id: res1_1.data.id } },
}); });
expect(up2.data.users_id).toBe(res1_1.data.id); expect(up2.data.users_id).toBe(res1_1.data.id);
const up3_1 = await em.mutator(posts).updateOne(res2.data.id, { const up3_1 = await em.mutator(posts).updateOne(res2.data.id, {
users_id: res1.data.id users_id: res1.data.id,
}); });
expect(up3_1.data.users_id).toBe(res1.data.id); expect(up3_1.data.users_id).toBe(res1.data.id);
const up3_2 = await em.mutator(posts).updateOne(res2.data.id, { const up3_2 = await em.mutator(posts).updateOne(res2.data.id, {
users_id: res1_1.data.id users_id: res1_1.data.id,
}); });
expect(up3_2.data.users_id).toBe(res1_1.data.id); expect(up3_2.data.users_id).toBe(res1_1.data.id);
const up4 = await em.mutator(posts).updateOne(res2.data.id, { const up4 = await em.mutator(posts).updateOne(res2.data.id, {
users_id: null users_id: null,
}); });
expect(up4.data.users_id).toBe(null); expect(up4.data.users_id).toBe(null);
}); });
@@ -199,8 +199,8 @@ describe("[data] Mutator (OneToOne)", async () => {
expect( expect(
em.mutator(users).insertOne({ em.mutator(users).insertOne({
username: "test", username: "test",
settings_id: 1 // todo: throws because it doesn't exist, but it shouldn't be allowed settings_id: 1, // todo: throws because it doesn't exist, but it shouldn't be allowed
}) }),
).rejects.toThrow(); ).rejects.toThrow();
}); });
@@ -210,15 +210,15 @@ describe("[data] Mutator (OneToOne)", async () => {
expect( expect(
em.mutator(users).insertOne({ em.mutator(users).insertOne({
username: "test", username: "test",
settings: { $set: { id: data.id } } settings: { $set: { id: data.id } },
}) }),
).rejects.toThrow(); ).rejects.toThrow();
}); });
test("insertOne: using $create", async () => { test("insertOne: using $create", async () => {
const res = await em.mutator(users).insertOne({ const res = await em.mutator(users).insertOne({
username: "test", username: "test",
settings: { $create: { theme: "dark" } } settings: { $create: { theme: "dark" } },
}); });
expect(res.data.settings_id).toBeDefined(); expect(res.data.settings_id).toBeDefined();
}); });
@@ -303,7 +303,7 @@ describe("[data] Mutator (Events)", async () => {
test("insertOne event return is respected", async () => { test("insertOne event return is respected", async () => {
const posts = proto.entity("posts", { const posts = proto.entity("posts", {
title: proto.text(), title: proto.text(),
views: proto.number() views: proto.number(),
}); });
const conn = getDummyConnection(); const conn = getDummyConnection();
@@ -318,10 +318,10 @@ describe("[data] Mutator (Events)", async () => {
async (event) => { async (event) => {
return { return {
...event.params.data, ...event.params.data,
views: 2 views: 2,
}; };
}, },
"sync" "sync",
); );
const mutator = em.mutator("posts"); const mutator = em.mutator("posts");
@@ -329,14 +329,14 @@ describe("[data] Mutator (Events)", async () => {
expect(result.data).toEqual({ expect(result.data).toEqual({
id: 1, id: 1,
title: "test", title: "test",
views: 2 views: 2,
}); });
}); });
test("updateOne event return is respected", async () => { test("updateOne event return is respected", async () => {
const posts = proto.entity("posts", { const posts = proto.entity("posts", {
title: proto.text(), title: proto.text(),
views: proto.number() views: proto.number(),
}); });
const conn = getDummyConnection(); const conn = getDummyConnection();
@@ -351,10 +351,10 @@ describe("[data] Mutator (Events)", async () => {
async (event) => { async (event) => {
return { return {
...event.params.data, ...event.params.data,
views: event.params.data.views + 1 views: event.params.data.views + 1,
}; };
}, },
"sync" "sync",
); );
const mutator = em.mutator("posts"); const mutator = em.mutator("posts");
@@ -363,7 +363,7 @@ describe("[data] Mutator (Events)", async () => {
expect(result.data).toEqual({ expect(result.data).toEqual({
id: 1, id: 1,
title: "test", title: "test",
views: 3 views: 3,
}); });
}); });
}); });

View File

@@ -7,7 +7,7 @@ import {
LibsqlConnection, LibsqlConnection,
ManyToOneRelation, ManyToOneRelation,
RepositoryEvents, RepositoryEvents,
TextField TextField,
} from "../../../src/data"; } from "../../../src/data";
import { getDummyConnection } from "../helper"; import { getDummyConnection } from "../helper";
@@ -70,13 +70,13 @@ describe("[Repository]", async () => {
const q1 = selectQ(conn).compile(); const q1 = selectQ(conn).compile();
const res = await client.execute({ const res = await client.execute({
sql: q1.sql, sql: q1.sql,
args: q1.parameters as any args: q1.parameters as any,
}); });
const q2 = countQ(conn).compile(); const q2 = countQ(conn).compile();
const count = await client.execute({ const count = await client.execute({
sql: q2.sql, sql: q2.sql,
args: q2.parameters as any args: q2.parameters as any,
}); });
return [res, count]; return [res, count];
} }
@@ -93,7 +93,7 @@ describe("[Repository]", async () => {
const exec = async ( const exec = async (
name: string, name: string,
fn: (em: EntityManager<any>) => Promise<any>, fn: (em: EntityManager<any>) => Promise<any>,
em: EntityManager<any> em: EntityManager<any>,
) => { ) => {
const res = await Perf.execute(() => fn(em), times); const res = await Perf.execute(() => fn(em), times);
await sleep(1000); await sleep(1000);
@@ -102,7 +102,7 @@ describe("[Repository]", async () => {
total: res.total.toFixed(2), total: res.total.toFixed(2),
avg: (res.total / times).toFixed(2), avg: (res.total / times).toFixed(2),
first: res.marks[0].time.toFixed(2), first: res.marks[0].time.toFixed(2),
last: res.marks[res.marks.length - 1].time.toFixed(2) last: res.marks[res.marks.length - 1].time.toFixed(2),
}; };
console.log(info.name, info, res.marks); console.log(info.name, info, res.marks);
return info; return info;
@@ -183,7 +183,7 @@ describe("[data] Repository (Events)", async () => {
const items = new Entity("items", [new TextField("label")]); const items = new Entity("items", [new TextField("label")]);
const categories = new Entity("categories", [new TextField("label")]); const categories = new Entity("categories", [new TextField("label")]);
const em = new EntityManager([items, categories], dummyConnection, [ const em = new EntityManager([items, categories], dummyConnection, [
new ManyToOneRelation(categories, items) new ManyToOneRelation(categories, items),
]); ]);
await em.schema().sync({ force: true }); await em.schema().sync({ force: true });
const events = new Map<string, any>(); const events = new Map<string, any>();

View File

@@ -26,7 +26,7 @@ describe("SchemaManager tests", async () => {
isNullable: true, isNullable: true,
isAutoIncrementing: true, isAutoIncrementing: true,
hasDefaultValue: false, hasDefaultValue: false,
comment: undefined comment: undefined,
}, },
{ {
name: "username", name: "username",
@@ -34,7 +34,7 @@ describe("SchemaManager tests", async () => {
isNullable: true, isNullable: true,
isAutoIncrementing: false, isAutoIncrementing: false,
hasDefaultValue: false, hasDefaultValue: false,
comment: undefined comment: undefined,
}, },
{ {
name: "email", name: "email",
@@ -42,7 +42,7 @@ describe("SchemaManager tests", async () => {
isNullable: true, isNullable: true,
isAutoIncrementing: false, isAutoIncrementing: false,
hasDefaultValue: false, hasDefaultValue: false,
comment: undefined comment: undefined,
}, },
{ {
name: "bio", name: "bio",
@@ -50,8 +50,8 @@ describe("SchemaManager tests", async () => {
isNullable: true, isNullable: true,
isAutoIncrementing: false, isAutoIncrementing: false,
hasDefaultValue: false, hasDefaultValue: false,
comment: undefined comment: undefined,
} },
], ],
indices: [ indices: [
{ {
@@ -61,11 +61,11 @@ describe("SchemaManager tests", async () => {
columns: [ columns: [
{ {
name: "email", name: "email",
order: 0 order: 0,
} },
] ],
} },
] ],
}); });
}); });
@@ -77,10 +77,10 @@ describe("SchemaManager tests", async () => {
new Entity(table, [ new Entity(table, [
new TextField("username"), new TextField("username"),
new TextField("email"), new TextField("email"),
new TextField("bio") new TextField("bio"),
]) ]),
], ],
dummyConnection dummyConnection,
); );
const kysely = em.connection.kysely; const kysely = em.connection.kysely;
@@ -101,8 +101,8 @@ describe("SchemaManager tests", async () => {
name: table, name: table,
isNew: false, isNew: false,
columns: { add: ["bio"], drop: [], change: [] }, columns: { add: ["bio"], drop: [], change: [] },
indices: { add: [], drop: [index] } indices: { add: [], drop: [index] },
} },
]); ]);
// now sync // now sync
@@ -119,7 +119,7 @@ describe("SchemaManager tests", async () => {
const table = "drop_column"; const table = "drop_column";
const em = new EntityManager( const em = new EntityManager(
[new Entity(table, [new TextField("username")])], [new Entity(table, [new TextField("username")])],
dummyConnection dummyConnection,
); );
const kysely = em.connection.kysely; const kysely = em.connection.kysely;
@@ -141,10 +141,10 @@ describe("SchemaManager tests", async () => {
columns: { columns: {
add: [], add: [],
drop: ["email"], drop: ["email"],
change: [] change: [],
},
indices: { add: [], drop: [] },
}, },
indices: { add: [], drop: [] }
}
]); ]);
// now sync // now sync
@@ -165,15 +165,15 @@ describe("SchemaManager tests", async () => {
new Entity(usersTable, [ new Entity(usersTable, [
new TextField("username"), new TextField("username"),
new TextField("email"), new TextField("email"),
new TextField("bio") new TextField("bio"),
]), ]),
new Entity(postsTable, [ new Entity(postsTable, [
new TextField("title"), new TextField("title"),
new TextField("content"), new TextField("content"),
new TextField("created_at") new TextField("created_at"),
]) ]),
], ],
dummyConnection dummyConnection,
); );
const kysely = em.connection.kysely; const kysely = em.connection.kysely;
@@ -192,7 +192,7 @@ describe("SchemaManager tests", async () => {
name: usersTable, name: usersTable,
isNew: false, isNew: false,
columns: { add: ["bio"], drop: [], change: [] }, columns: { add: ["bio"], drop: [], change: [] },
indices: { add: [], drop: [] } indices: { add: [], drop: [] },
}, },
{ {
name: postsTable, name: postsTable,
@@ -200,10 +200,10 @@ describe("SchemaManager tests", async () => {
columns: { columns: {
add: ["id", "title", "content", "created_at"], add: ["id", "title", "content", "created_at"],
drop: [], drop: [],
change: [] change: [],
},
indices: { add: [], drop: [] },
}, },
indices: { add: [], drop: [] }
}
]); ]);
// now sync // now sync
@@ -228,8 +228,8 @@ describe("SchemaManager tests", async () => {
name: entity.name, name: entity.name,
isNew: true, isNew: true,
columns: { add: ["id", "email"], drop: [], change: [] }, columns: { add: ["id", "email"], drop: [], change: [] },
indices: { add: [index.name!], drop: [] } indices: { add: [index.name!], drop: [] },
} },
]); ]);
// sync and then check again // sync and then check again
@@ -256,8 +256,8 @@ describe("SchemaManager tests", async () => {
name: entity.name, name: entity.name,
isNew: false, isNew: false,
columns: { add: [], drop: [], change: [] }, columns: { add: [], drop: [], change: [] },
indices: { add: [index.name!], drop: [] } indices: { add: [index.name!], drop: [] },
} },
]); ]);
// sync and then check again // sync and then check again

View File

@@ -7,7 +7,7 @@ import {
ManyToOneRelation, ManyToOneRelation,
PolymorphicRelation, PolymorphicRelation,
TextField, TextField,
WithBuilder WithBuilder,
} from "../../../src/data"; } from "../../../src/data";
import * as proto from "../../../src/data/prototype"; import * as proto from "../../../src/data/prototype";
import { compileQb, prettyPrintQb, schemaToEm } from "../../helper"; import { compileQb, prettyPrintQb, schemaToEm } from "../../helper";
@@ -21,12 +21,12 @@ describe("[data] WithBuilder", async () => {
{ {
posts: proto.entity("posts", {}), posts: proto.entity("posts", {}),
users: proto.entity("users", {}), users: proto.entity("users", {}),
media: proto.entity("media", {}) media: proto.entity("media", {}),
}, },
({ relation }, { posts, users, media }) => { ({ relation }, { posts, users, media }) => {
relation(posts).manyToOne(users); relation(posts).manyToOne(users);
relation(users).polyToOne(media, { mappedBy: "avatar" }); relation(users).polyToOne(media, { mappedBy: "avatar" });
} },
); );
const em = schemaToEm(schema); const em = schemaToEm(schema);
@@ -36,17 +36,17 @@ describe("[data] WithBuilder", async () => {
expect( expect(
WithBuilder.validateWiths(em, "posts", { WithBuilder.validateWiths(em, "posts", {
users: { users: {
with: { avatar: {} } with: { avatar: {} },
} },
}) }),
).toBe(2); ).toBe(2);
expect(() => WithBuilder.validateWiths(em, "posts", { author: {} })).toThrow(); expect(() => WithBuilder.validateWiths(em, "posts", { author: {} })).toThrow();
expect(() => expect(() =>
WithBuilder.validateWiths(em, "posts", { WithBuilder.validateWiths(em, "posts", {
users: { users: {
with: { glibberish: {} } with: { glibberish: {} },
} },
}) }),
).toThrow(); ).toThrow();
}); });
@@ -56,8 +56,8 @@ describe("[data] WithBuilder", async () => {
expect(() => expect(() =>
WithBuilder.addClause(em, em.connection.kysely.selectFrom("users"), users, { WithBuilder.addClause(em, em.connection.kysely.selectFrom("users"), users, {
posts: {} posts: {},
}) }),
).toThrow('Relation "users<>posts" not found'); ).toThrow('Relation "users<>posts" not found');
}); });
@@ -68,13 +68,13 @@ describe("[data] WithBuilder", async () => {
const em = new EntityManager([users, posts], dummyConnection, relations); const em = new EntityManager([users, posts], dummyConnection, relations);
const qb = WithBuilder.addClause(em, em.connection.kysely.selectFrom("users"), users, { const qb = WithBuilder.addClause(em, em.connection.kysely.selectFrom("users"), users, {
posts: {} posts: {},
}); });
const res = qb.compile(); const res = qb.compile();
expect(res.sql).toBe( expect(res.sql).toBe(
'select (select coalesce(json_group_array(json_object(\'id\', "agg"."id", \'content\', "agg"."content", \'author_id\', "agg"."author_id")), \'[]\') from (select "posts"."id" as "id", "posts"."content" as "content", "posts"."author_id" as "author_id" from "posts" as "posts" where "posts"."author_id" = "users"."id" order by "posts"."id" asc limit ? offset ?) as agg) as "posts" from "users"' 'select (select coalesce(json_group_array(json_object(\'id\', "agg"."id", \'content\', "agg"."content", \'author_id\', "agg"."author_id")), \'[]\') from (select "posts"."id" as "id", "posts"."content" as "content", "posts"."author_id" as "author_id" from "posts" as "posts" where "posts"."author_id" = "users"."id" order by "posts"."id" asc limit ? offset ?) as agg) as "posts" from "users"',
); );
expect(res.parameters).toEqual([10, 0]); expect(res.parameters).toEqual([10, 0]);
@@ -83,14 +83,14 @@ describe("[data] WithBuilder", async () => {
em.connection.kysely.selectFrom("posts"), em.connection.kysely.selectFrom("posts"),
posts, // @todo: try with "users", it gives output! posts, // @todo: try with "users", it gives output!
{ {
author: {} author: {},
} },
); );
const res2 = qb2.compile(); const res2 = qb2.compile();
expect(res2.sql).toBe( expect(res2.sql).toBe(
'select (select json_object(\'id\', "obj"."id", \'username\', "obj"."username") from (select "users"."id" as "id", "users"."username" as "username" from "users" as "author" where "author"."id" = "posts"."author_id" order by "users"."id" asc limit ? offset ?) as obj) as "author" from "posts"' 'select (select json_object(\'id\', "obj"."id", \'username\', "obj"."username") from (select "users"."id" as "id", "users"."username" as "username" from "users" as "author" where "author"."id" = "posts"."author_id" order by "users"."id" asc limit ? offset ?) as obj) as "author" from "posts"',
); );
expect(res2.parameters).toEqual([1, 0]); expect(res2.parameters).toEqual([1, 0]);
}); });
@@ -124,7 +124,7 @@ describe("[data] WithBuilder", async () => {
.values([ .values([
{ posts_id: 1, categories_id: 1 }, { posts_id: 1, categories_id: 1 },
{ posts_id: 2, categories_id: 2 }, { posts_id: 2, categories_id: 2 },
{ posts_id: 1, categories_id: 2 } { posts_id: 1, categories_id: 2 },
]) ])
.execute(); .execute();
@@ -138,14 +138,14 @@ describe("[data] WithBuilder", async () => {
title: "fashion post", title: "fashion post",
categories: [ categories: [
{ id: 1, label: "fashion" }, { id: 1, label: "fashion" },
{ id: 2, label: "beauty" } { id: 2, label: "beauty" },
] ],
}, },
{ {
id: 2, id: 2,
title: "beauty post", title: "beauty post",
categories: [{ id: 2, label: "beauty" }] categories: [{ id: 2, label: "beauty" }],
} },
]); ]);
const res2 = await em.repository(categories).findMany({ with: { posts: {} } }); const res2 = await em.repository(categories).findMany({ with: { posts: {} } });
@@ -156,21 +156,21 @@ describe("[data] WithBuilder", async () => {
{ {
id: 1, id: 1,
label: "fashion", label: "fashion",
posts: [{ id: 1, title: "fashion post" }] posts: [{ id: 1, title: "fashion post" }],
}, },
{ {
id: 2, id: 2,
label: "beauty", label: "beauty",
posts: [ posts: [
{ id: 1, title: "fashion post" }, { id: 1, title: "fashion post" },
{ id: 2, title: "beauty post" } { id: 2, title: "beauty post" },
] ],
}, },
{ {
id: 3, id: 3,
label: "tech", label: "tech",
posts: [] posts: [],
} },
]); ]);
}); });
@@ -181,7 +181,7 @@ describe("[data] WithBuilder", async () => {
const entities = [media, categories]; const entities = [media, categories];
const single = new PolymorphicRelation(categories, media, { const single = new PolymorphicRelation(categories, media, {
mappedBy: "single", mappedBy: "single",
targetCardinality: 1 targetCardinality: 1,
}); });
const multiple = new PolymorphicRelation(categories, media, { mappedBy: "multiple" }); const multiple = new PolymorphicRelation(categories, media, { mappedBy: "multiple" });
@@ -191,11 +191,11 @@ describe("[data] WithBuilder", async () => {
em, em,
em.connection.kysely.selectFrom("categories"), em.connection.kysely.selectFrom("categories"),
categories, categories,
{ single: {} } { single: {} },
); );
const res = qb.compile(); const res = qb.compile();
expect(res.sql).toBe( expect(res.sql).toBe(
'select (select json_object(\'id\', "obj"."id", \'path\', "obj"."path") from (select "media"."id" as "id", "media"."path" as "path" from "media" where "media"."reference" = ? and "categories"."id" = "media"."entity_id" order by "media"."id" asc limit ? offset ?) as obj) as "single" from "categories"' 'select (select json_object(\'id\', "obj"."id", \'path\', "obj"."path") from (select "media"."id" as "id", "media"."path" as "path" from "media" where "media"."reference" = ? and "categories"."id" = "media"."entity_id" order by "media"."id" asc limit ? offset ?) as obj) as "single" from "categories"',
); );
expect(res.parameters).toEqual(["categories.single", 1, 0]); expect(res.parameters).toEqual(["categories.single", 1, 0]);
@@ -203,11 +203,11 @@ describe("[data] WithBuilder", async () => {
em, em,
em.connection.kysely.selectFrom("categories"), em.connection.kysely.selectFrom("categories"),
categories, categories,
{ multiple: {} } { multiple: {} },
); );
const res2 = qb2.compile(); const res2 = qb2.compile();
expect(res2.sql).toBe( expect(res2.sql).toBe(
'select (select coalesce(json_group_array(json_object(\'id\', "agg"."id", \'path\', "agg"."path")), \'[]\') from (select "media"."id" as "id", "media"."path" as "path" from "media" where "media"."reference" = ? and "categories"."id" = "media"."entity_id" order by "media"."id" asc limit ? offset ?) as agg) as "multiple" from "categories"' 'select (select coalesce(json_group_array(json_object(\'id\', "agg"."id", \'path\', "agg"."path")), \'[]\') from (select "media"."id" as "id", "media"."path" as "path" from "media" where "media"."reference" = ? and "categories"."id" = "media"."entity_id" order by "media"."id" asc limit ? offset ?) as agg) as "multiple" from "categories"',
); );
expect(res2.parameters).toEqual(["categories.multiple", 10, 0]); expect(res2.parameters).toEqual(["categories.multiple", 10, 0]);
}); });
@@ -240,16 +240,16 @@ describe("[data] WithBuilder", async () => {
{ {
posts: proto.entity("posts", {}), posts: proto.entity("posts", {}),
users: proto.entity("users", { users: proto.entity("users", {
username: proto.text() username: proto.text(),
}), }),
media: proto.entity("media", { media: proto.entity("media", {
path: proto.text() path: proto.text(),
}) }),
}, },
({ relation }, { posts, users, media }) => { ({ relation }, { posts, users, media }) => {
relation(posts).manyToOne(users); relation(posts).manyToOne(users);
relation(users).polyToOne(media, { mappedBy: "avatar" }); relation(users).polyToOne(media, { mappedBy: "avatar" });
} },
); );
const em = schemaToEm(schema); const em = schemaToEm(schema);
@@ -265,16 +265,16 @@ describe("[data] WithBuilder", async () => {
with: { with: {
avatar: { avatar: {
select: ["id", "path"], select: ["id", "path"],
limit: 2 // ignored limit: 2, // ignored
} },
} },
} },
} },
); );
//prettyPrintQb(qb); //prettyPrintQb(qb);
expect(qb.compile().sql).toBe( expect(qb.compile().sql).toBe(
'select (select json_object(\'id\', "obj"."id", \'username\', "obj"."username", \'avatar\', "obj"."avatar") from (select "users"."id" as "id", "users"."username" as "username", (select json_object(\'id\', "obj"."id", \'path\', "obj"."path") from (select "media"."id" as "id", "media"."path" as "path" from "media" where "media"."reference" = ? and "users"."id" = "media"."entity_id" order by "media"."id" asc limit ? offset ?) as obj) as "avatar" from "users" as "users" where "users"."id" = "posts"."users_id" order by "users"."username" asc limit ? offset ?) as obj) as "users" from "posts"' 'select (select json_object(\'id\', "obj"."id", \'username\', "obj"."username", \'avatar\', "obj"."avatar") from (select "users"."id" as "id", "users"."username" as "username", (select json_object(\'id\', "obj"."id", \'path\', "obj"."path") from (select "media"."id" as "id", "media"."path" as "path" from "media" where "media"."reference" = ? and "users"."id" = "media"."entity_id" order by "media"."id" asc limit ? offset ?) as obj) as "avatar" from "users" as "users" where "users"."id" = "posts"."users_id" order by "users"."username" asc limit ? offset ?) as obj) as "users" from "posts"',
); );
expect(qb.compile().parameters).toEqual(["users.avatar", 1, 0, 1, 0]); expect(qb.compile().parameters).toEqual(["users.avatar", 1, 0, 1, 0]);
}); });
@@ -285,17 +285,17 @@ describe("[data] WithBuilder", async () => {
posts: proto.entity("posts", {}), posts: proto.entity("posts", {}),
comments: proto.entity("comments", {}), comments: proto.entity("comments", {}),
users: proto.entity("users", { users: proto.entity("users", {
username: proto.text() username: proto.text(),
}), }),
media: proto.entity("media", { media: proto.entity("media", {
path: proto.text() path: proto.text(),
}) }),
}, },
({ relation }, { posts, comments, users, media }) => { ({ relation }, { posts, comments, users, media }) => {
relation(posts).manyToOne(users).polyToOne(media, { mappedBy: "images" }); relation(posts).manyToOne(users).polyToOne(media, { mappedBy: "images" });
relation(users).polyToOne(media, { mappedBy: "avatar" }); relation(users).polyToOne(media, { mappedBy: "avatar" });
relation(comments).manyToOne(posts).manyToOne(users); relation(comments).manyToOne(posts).manyToOne(users);
} },
); );
const em = schemaToEm(schema); const em = schemaToEm(schema);
@@ -308,15 +308,15 @@ describe("[data] WithBuilder", async () => {
limit: 12, limit: 12,
with: { with: {
users: { users: {
select: ["username"] select: ["username"],
} },
} },
} },
} },
); );
expect(qb.compile().sql).toBe( expect(qb.compile().sql).toBe(
'select (select coalesce(json_group_array(json_object(\'id\', "agg"."id", \'posts_id\', "agg"."posts_id", \'users_id\', "agg"."users_id", \'users\', "agg"."users")), \'[]\') from (select "comments"."id" as "id", "comments"."posts_id" as "posts_id", "comments"."users_id" as "users_id", (select json_object(\'username\', "obj"."username") from (select "users"."username" as "username" from "users" as "users" where "users"."id" = "comments"."users_id" order by "users"."id" asc limit ? offset ?) as obj) as "users" from "comments" as "comments" where "comments"."posts_id" = "posts"."id" order by "comments"."id" asc limit ? offset ?) as agg) as "comments" from "posts"' 'select (select coalesce(json_group_array(json_object(\'id\', "agg"."id", \'posts_id\', "agg"."posts_id", \'users_id\', "agg"."users_id", \'users\', "agg"."users")), \'[]\') from (select "comments"."id" as "id", "comments"."posts_id" as "posts_id", "comments"."users_id" as "users_id", (select json_object(\'username\', "obj"."username") from (select "users"."username" as "username" from "users" as "users" where "users"."id" = "comments"."users_id" order by "users"."id" asc limit ? offset ?) as obj) as "users" from "comments" as "comments" where "comments"."posts_id" = "posts"."id" order by "comments"."id" asc limit ? offset ?) as agg) as "comments" from "posts"',
); );
expect(qb.compile().parameters).toEqual([1, 0, 12, 0]); expect(qb.compile().parameters).toEqual([1, 0, 12, 0]);
}); });
@@ -325,23 +325,23 @@ describe("[data] WithBuilder", async () => {
const schema = proto.em( const schema = proto.em(
{ {
posts: proto.entity("posts", { posts: proto.entity("posts", {
title: proto.text() title: proto.text(),
}), }),
comments: proto.entity("comments", { comments: proto.entity("comments", {
content: proto.text() content: proto.text(),
}), }),
users: proto.entity("users", { users: proto.entity("users", {
username: proto.text() username: proto.text(),
}), }),
media: proto.entity("media", { media: proto.entity("media", {
path: proto.text() path: proto.text(),
}) }),
}, },
({ relation }, { posts, comments, users, media }) => { ({ relation }, { posts, comments, users, media }) => {
relation(posts).manyToOne(users).polyToOne(media, { mappedBy: "images" }); relation(posts).manyToOne(users).polyToOne(media, { mappedBy: "images" });
relation(users).polyToOne(media, { mappedBy: "avatar" }); relation(users).polyToOne(media, { mappedBy: "avatar" });
relation(comments).manyToOne(posts).manyToOne(users); relation(comments).manyToOne(posts).manyToOne(users);
} },
); );
const em = schemaToEm(schema); const em = schemaToEm(schema);
await em.schema().sync({ force: true }); await em.schema().sync({ force: true });
@@ -351,7 +351,7 @@ describe("[data] WithBuilder", async () => {
await em.mutator("posts").insertMany([ await em.mutator("posts").insertMany([
{ title: "post1", users_id: 1 }, { title: "post1", users_id: 1 },
{ title: "post2", users_id: 1 }, { title: "post2", users_id: 1 },
{ title: "post3", users_id: 2 } { title: "post3", users_id: 2 },
]); ]);
await em.mutator("comments").insertMany([ await em.mutator("comments").insertMany([
{ content: "comment1", posts_id: 1, users_id: 1 }, { content: "comment1", posts_id: 1, users_id: 1 },
@@ -360,7 +360,7 @@ describe("[data] WithBuilder", async () => {
{ content: "comment3", posts_id: 2, users_id: 1 }, { content: "comment3", posts_id: 2, users_id: 1 },
{ content: "comment4", posts_id: 2, users_id: 2 }, { content: "comment4", posts_id: 2, users_id: 2 },
{ content: "comment5", posts_id: 3, users_id: 1 }, { content: "comment5", posts_id: 3, users_id: 1 },
{ content: "comment6", posts_id: 3, users_id: 2 } { content: "comment6", posts_id: 3, users_id: 2 },
]); ]);
const result = await em.repo("posts").findMany({ const result = await em.repo("posts").findMany({
@@ -371,11 +371,11 @@ describe("[data] WithBuilder", async () => {
select: ["content"], select: ["content"],
with: { with: {
users: { users: {
select: ["username"] select: ["username"],
} },
} },
} },
} },
}); });
expect(result.data).toEqual([ expect(result.data).toEqual([
@@ -385,16 +385,16 @@ describe("[data] WithBuilder", async () => {
{ {
content: "comment1", content: "comment1",
users: { users: {
username: "user1" username: "user1",
} },
}, },
{ {
content: "comment1-1", content: "comment1-1",
users: { users: {
username: "user1" username: "user1",
} },
} },
] ],
}, },
{ {
title: "post2", title: "post2",
@@ -402,16 +402,16 @@ describe("[data] WithBuilder", async () => {
{ {
content: "comment3", content: "comment3",
users: { users: {
username: "user1" username: "user1",
} },
}, },
{ {
content: "comment4", content: "comment4",
users: { users: {
username: "user2" username: "user2",
} },
} },
] ],
}, },
{ {
title: "post3", title: "post3",
@@ -419,17 +419,17 @@ describe("[data] WithBuilder", async () => {
{ {
content: "comment5", content: "comment5",
users: { users: {
username: "user1" username: "user1",
} },
}, },
{ {
content: "comment6", content: "comment6",
users: { users: {
username: "user2" username: "user2",
} },
} },
] ],
} },
]); ]);
//console.log(_jsonp(result.data)); //console.log(_jsonp(result.data));
}); });

View File

@@ -22,10 +22,10 @@ describe("Connection", async () => {
columns: [ columns: [
{ {
name: "name", name: "name",
order: 0 order: 0,
} },
] ],
} },
]); ]);
}); });
@@ -54,14 +54,14 @@ describe("Connection", async () => {
columns: [ columns: [
{ {
name: "name", name: "name",
order: 0 order: 0,
}, },
{ {
name: "desc", name: "desc",
order: 1 order: 1,
} },
] ],
} },
]); ]);
}); });
@@ -83,10 +83,10 @@ describe("Connection", async () => {
columns: [ columns: [
{ {
name: "name", name: "name",
order: 0 order: 0,
} },
] ],
} },
]); ]);
}); });
}); });

View File

@@ -10,12 +10,12 @@ describe("[data] EnumField", async () => {
runBaseFieldTests( runBaseFieldTests(
EnumField, EnumField,
{ defaultValue: "a", schemaType: "text" }, { defaultValue: "a", schemaType: "text" },
{ options: options(["a", "b", "c"]) } { options: options(["a", "b", "c"]) },
); );
test("yields if default value is not a valid option", async () => { test("yields if default value is not a valid option", async () => {
expect( expect(
() => new EnumField("test", { options: options(["a", "b"]), default_value: "c" }) () => new EnumField("test", { options: options(["a", "b"]), default_value: "c" }),
).toThrow(); ).toThrow();
}); });
@@ -31,7 +31,7 @@ describe("[data] EnumField", async () => {
const field = new EnumField("test", { const field = new EnumField("test", {
options: options(["a", "b", "c"]), options: options(["a", "b", "c"]),
default_value: "a", default_value: "a",
required: true required: true,
}); });
expect(field.transformRetrieve(null)).toBe("a"); expect(field.transformRetrieve(null)).toBe("a");

View File

@@ -24,20 +24,20 @@ describe("[data] Field", async () => {
const required = new FieldSpec("test", { required: true }); const required = new FieldSpec("test", { required: true });
const requiredDefault = new FieldSpec("test", { const requiredDefault = new FieldSpec("test", {
required: true, required: true,
default_value: "test" default_value: "test",
}); });
expect(required.transformPersist(null, undefined as any, undefined as any)).rejects.toThrow(); expect(required.transformPersist(null, undefined as any, undefined as any)).rejects.toThrow();
expect( expect(
required.transformPersist(undefined, undefined as any, undefined as any) required.transformPersist(undefined, undefined as any, undefined as any),
).rejects.toThrow(); ).rejects.toThrow();
// works because it has a default value // works because it has a default value
expect( expect(
requiredDefault.transformPersist(null, undefined as any, undefined as any) requiredDefault.transformPersist(null, undefined as any, undefined as any),
).resolves.toBeDefined(); ).resolves.toBeDefined();
expect( expect(
requiredDefault.transformPersist(undefined, undefined as any, undefined as any) requiredDefault.transformPersist(undefined, undefined as any, undefined as any),
).resolves.toBeDefined(); ).resolves.toBeDefined();
}); });
}); });

View File

@@ -5,7 +5,7 @@ import {
EntityIndex, EntityIndex,
type EntityManager, type EntityManager,
Field, Field,
type SchemaResponse type SchemaResponse,
} from "../../../../src/data"; } from "../../../../src/data";
class TestField extends Field { class TestField extends Field {

View File

@@ -7,7 +7,7 @@ describe("[data] JsonField", async () => {
runBaseFieldTests(JsonField, { runBaseFieldTests(JsonField, {
defaultValue: { a: 1 }, defaultValue: { a: 1 },
sampleValues: ["string", { test: 1 }, 1], sampleValues: ["string", { test: 1 }, 1],
schemaType: "text" schemaType: "text",
}); });
test("transformPersist (no config)", async () => { test("transformPersist (no config)", async () => {

View File

@@ -18,7 +18,7 @@ export function transformPersist(field: Field, value: any, context?: TActionCont
export function runBaseFieldTests( export function runBaseFieldTests(
fieldClass: ConstructableField, fieldClass: ConstructableField,
config: FieldTestConfig, config: FieldTestConfig,
_requiredConfig: any = {} _requiredConfig: any = {},
) { ) {
const noConfigField = new fieldClass("no_config", _requiredConfig); const noConfigField = new fieldClass("no_config", _requiredConfig);
const fillable = new fieldClass("fillable", { ..._requiredConfig, fillable: true }); const fillable = new fieldClass("fillable", { ..._requiredConfig, fillable: true });
@@ -29,7 +29,7 @@ export function runBaseFieldTests(
..._requiredConfig, ..._requiredConfig,
fillable: true, fillable: true,
required: true, required: true,
default_value: config.defaultValue default_value: config.defaultValue,
}); });
test("schema", () => { test("schema", () => {
@@ -37,7 +37,7 @@ export function runBaseFieldTests(
expect(noConfigField.schema(null as any)).toEqual([ expect(noConfigField.schema(null as any)).toEqual([
"no_config", "no_config",
config.schemaType, config.schemaType,
expect.any(Function) expect.any(Function),
]); ]);
}); });
@@ -96,7 +96,7 @@ export function runBaseFieldTests(
//order: 1, //order: 1,
fillable: true, fillable: true,
required: false, required: false,
hidden: false hidden: false,
//virtual: false, //virtual: false,
//default_value: undefined //default_value: undefined
}; };
@@ -105,20 +105,20 @@ export function runBaseFieldTests(
const json = field.toJSON(); const json = field.toJSON();
return { return {
...json, ...json,
config: omit(json.config, ["html"]) config: omit(json.config, ["html"]),
}; };
} }
expect(fieldJson(noConfigField)).toEqual({ expect(fieldJson(noConfigField)).toEqual({
//name: "no_config", //name: "no_config",
type: noConfigField.type, type: noConfigField.type,
config: _config config: _config,
}); });
expect(fieldJson(fillable)).toEqual({ expect(fieldJson(fillable)).toEqual({
//name: "fillable", //name: "fillable",
type: noConfigField.type, type: noConfigField.type,
config: _config config: _config,
}); });
expect(fieldJson(required)).toEqual({ expect(fieldJson(required)).toEqual({
@@ -126,8 +126,8 @@ export function runBaseFieldTests(
type: required.type, type: required.type,
config: { config: {
..._config, ..._config,
required: true required: true,
} },
}); });
expect(fieldJson(hidden)).toEqual({ expect(fieldJson(hidden)).toEqual({
@@ -135,8 +135,8 @@ export function runBaseFieldTests(
type: required.type, type: required.type,
config: { config: {
..._config, ..._config,
hidden: true hidden: true,
} },
}); });
expect(fieldJson(dflt)).toEqual({ expect(fieldJson(dflt)).toEqual({
@@ -144,8 +144,8 @@ export function runBaseFieldTests(
type: dflt.type, type: dflt.type,
config: { config: {
..._config, ..._config,
default_value: config.defaultValue default_value: config.defaultValue,
} },
}); });
expect(fieldJson(requiredAndDefault)).toEqual({ expect(fieldJson(requiredAndDefault)).toEqual({
@@ -155,8 +155,8 @@ export function runBaseFieldTests(
..._config, ..._config,
fillable: true, fillable: true,
required: true, required: true,
default_value: config.defaultValue default_value: config.defaultValue,
} },
}); });
}); });
} }

View File

@@ -4,7 +4,7 @@ import {
type BaseRelationConfig, type BaseRelationConfig,
EntityRelation, EntityRelation,
EntityRelationAnchor, EntityRelationAnchor,
RelationTypes RelationTypes,
} from "../../../../src/data/relations"; } from "../../../../src/data/relations";
class TestEntityRelation extends EntityRelation { class TestEntityRelation extends EntityRelation {
@@ -12,7 +12,7 @@ class TestEntityRelation extends EntityRelation {
super( super(
new EntityRelationAnchor(new Entity("source"), "source"), new EntityRelationAnchor(new Entity("source"), "source"),
new EntityRelationAnchor(new Entity("target"), "target"), new EntityRelationAnchor(new Entity("target"), "target"),
config config,
); );
} }
initialize(em: EntityManager<any>) {} initialize(em: EntityManager<any>) {}

View File

@@ -30,14 +30,14 @@ beforeAll(() =>
method: init?.method ?? "GET", method: init?.method ?? "GET",
// @ts-ignore // @ts-ignore
headers: Object.fromEntries(init?.headers?.entries() ?? []), headers: Object.fromEntries(init?.headers?.entries() ?? []),
body: init?.body body: init?.body,
}; };
return new Response(JSON.stringify({ todos: [1, 2], request }), { return new Response(JSON.stringify({ todos: [1, 2], request }), {
status: 200, status: 200,
headers: { "Content-Type": "application/json" } headers: { "Content-Type": "application/json" },
}); });
}) }),
); );
afterAll(unmockFetch); afterAll(unmockFetch);
@@ -46,7 +46,7 @@ describe("FetchTask", async () => {
const task = new FetchTask("Fetch Something", { const task = new FetchTask("Fetch Something", {
url: "https://jsonplaceholder.typicode.com/todos/1", url: "https://jsonplaceholder.typicode.com/todos/1",
method: "GET", method: "GET",
headers: [{ key: "Content-Type", value: "application/json" }] headers: [{ key: "Content-Type", value: "application/json" }],
}); });
const result = await task.run(); const result = await task.run();
@@ -62,18 +62,18 @@ describe("FetchTask", async () => {
expect( expect(
// // @ts-expect-error // // @ts-expect-error
() => new FetchTask("", { url: "https://jsonplaceholder.typicode.com", method: 1 }) () => new FetchTask("", { url: "https://jsonplaceholder.typicode.com", method: 1 }),
).toThrow(); ).toThrow();
expect( expect(
new FetchTask("", { new FetchTask("", {
url: "https://jsonplaceholder.typicode.com", url: "https://jsonplaceholder.typicode.com",
method: "invalid" method: "invalid",
}).execute() }).execute(),
).rejects.toThrow(/^Invalid method/); ).rejects.toThrow(/^Invalid method/);
expect( expect(
() => new FetchTask("", { url: "https://jsonplaceholder.typicode.com", method: "GET" }) () => new FetchTask("", { url: "https://jsonplaceholder.typicode.com", method: "GET" }),
).toBeDefined(); ).toBeDefined();
expect(() => new FetchTask("", { url: "", method: "Invalid" })).toThrow(); expect(() => new FetchTask("", { url: "", method: "Invalid" })).toThrow();
@@ -85,17 +85,17 @@ describe("FetchTask", async () => {
method: "{{ flow.output.method }}", method: "{{ flow.output.method }}",
headers: [ headers: [
{ key: "Content-{{ flow.output.headerKey }}", value: "application/json" }, { key: "Content-{{ flow.output.headerKey }}", value: "application/json" },
{ key: "Authorization", value: "Bearer {{ flow.output.apiKey }}" } { key: "Authorization", value: "Bearer {{ flow.output.apiKey }}" },
], ],
body: JSON.stringify({ body: JSON.stringify({
email: "{{ flow.output.email }}" email: "{{ flow.output.email }}",
}) }),
}); });
const inputs = { const inputs = {
headerKey: "Type", headerKey: "Type",
apiKey: 123, apiKey: 123,
email: "what@else.com", email: "what@else.com",
method: "PATCH" method: "PATCH",
}; };
const flow = new Flow("", [task]); const flow = new Flow("", [task]);

View File

@@ -4,16 +4,16 @@ import { Flow, LogTask, RenderTask, SubFlowTask } from "../../src/flows";
describe("SubFlowTask", async () => { describe("SubFlowTask", async () => {
test("Simple Subflow", async () => { test("Simple Subflow", async () => {
const subTask = new RenderTask("render", { const subTask = new RenderTask("render", {
render: "subflow" render: "subflow",
}); });
const subflow = new Flow("subflow", [subTask]); const subflow = new Flow("subflow", [subTask]);
const task = new LogTask("log"); const task = new LogTask("log");
const task2 = new SubFlowTask("sub", { const task2 = new SubFlowTask("sub", {
flow: subflow flow: subflow,
}); });
const task3 = new RenderTask("render2", { const task3 = new RenderTask("render2", {
render: "Subflow output: {{ sub.output }}" render: "Subflow output: {{ sub.output }}",
}); });
const flow = new Flow("test", [task, task2, task3], []); const flow = new Flow("test", [task, task2, task3], []);
@@ -30,7 +30,7 @@ describe("SubFlowTask", async () => {
test("Simple loop", async () => { test("Simple loop", async () => {
const subTask = new RenderTask("render", { const subTask = new RenderTask("render", {
render: "run {{ flow.output }}" render: "run {{ flow.output }}",
}); });
const subflow = new Flow("subflow", [subTask]); const subflow = new Flow("subflow", [subTask]);
@@ -38,10 +38,10 @@ describe("SubFlowTask", async () => {
const task2 = new SubFlowTask("sub", { const task2 = new SubFlowTask("sub", {
flow: subflow, flow: subflow,
loop: true, loop: true,
input: [1, 2, 3] input: [1, 2, 3],
}); });
const task3 = new RenderTask("render2", { const task3 = new RenderTask("render2", {
render: `Subflow output: {{ sub.output | join: ", " }}` render: `Subflow output: {{ sub.output | join: ", " }}`,
}); });
const flow = new Flow("test", [task, task2, task3], []); const flow = new Flow("test", [task, task2, task3], []);
@@ -61,7 +61,7 @@ describe("SubFlowTask", async () => {
test("Simple loop from flow input", async () => { test("Simple loop from flow input", async () => {
const subTask = new RenderTask("render", { const subTask = new RenderTask("render", {
render: "run {{ flow.output }}" render: "run {{ flow.output }}",
}); });
const subflow = new Flow("subflow", [subTask]); const subflow = new Flow("subflow", [subTask]);
@@ -70,10 +70,10 @@ describe("SubFlowTask", async () => {
const task2 = new SubFlowTask("sub", { const task2 = new SubFlowTask("sub", {
flow: subflow, flow: subflow,
loop: true, loop: true,
input: "{{ flow.output | json }}" input: "{{ flow.output | json }}",
}); });
const task3 = new RenderTask("render2", { const task3 = new RenderTask("render2", {
render: `Subflow output: {{ sub.output | join: ", " }}` render: `Subflow output: {{ sub.output | join: ", " }}`,
}); });
const flow = new Flow("test", [task, task2, task3], []); const flow = new Flow("test", [task, task2, task3], []);

View File

@@ -8,13 +8,13 @@ describe("Task", async () => {
const result = await Task.resolveParams( const result = await Task.resolveParams(
Type.Object({ test: dynamic(Type.Number()) }), Type.Object({ test: dynamic(Type.Number()) }),
{ {
test: "{{ some.path }}" test: "{{ some.path }}",
}, },
{ {
some: { some: {
path: 1 path: 1,
} },
} },
); );
expect(result.test).toBe(1); expect(result.test).toBe(1);
@@ -24,13 +24,13 @@ describe("Task", async () => {
const result = await Task.resolveParams( const result = await Task.resolveParams(
Type.Object({ test: Type.String() }), Type.Object({ test: Type.String() }),
{ {
test: "{{ some.path }}" test: "{{ some.path }}",
}, },
{ {
some: { some: {
path: "1/1" path: "1/1",
} },
} },
); );
expect(result.test).toBe("1/1"); expect(result.test).toBe("1/1");
@@ -40,13 +40,13 @@ describe("Task", async () => {
const result = await Task.resolveParams( const result = await Task.resolveParams(
Type.Object({ test: dynamic(Type.Object({ key: Type.String(), value: Type.String() })) }), Type.Object({ test: dynamic(Type.Object({ key: Type.String(), value: Type.String() })) }),
{ {
test: { key: "path", value: "{{ some.path }}" } test: { key: "path", value: "{{ some.path }}" },
}, },
{ {
some: { some: {
path: "1/1" path: "1/1",
} },
} },
); );
expect(result.test).toEqual({ key: "path", value: "1/1" }); expect(result.test).toEqual({ key: "path", value: "1/1" });
@@ -55,17 +55,17 @@ describe("Task", async () => {
test("resolveParams: with json", async () => { test("resolveParams: with json", async () => {
const result = await Task.resolveParams( const result = await Task.resolveParams(
Type.Object({ Type.Object({
test: dynamic(Type.Object({ key: Type.String(), value: Type.String() })) test: dynamic(Type.Object({ key: Type.String(), value: Type.String() })),
}), }),
{ {
test: "{{ some | json }}" test: "{{ some | json }}",
}, },
{ {
some: { some: {
key: "path", key: "path",
value: "1/1" value: "1/1",
} },
} },
); );
expect(result.test).toEqual({ key: "path", value: "1/1" }); expect(result.test).toEqual({ key: "path", value: "1/1" });
@@ -74,11 +74,11 @@ describe("Task", async () => {
test("resolveParams: with array", async () => { test("resolveParams: with array", async () => {
const result = await Task.resolveParams( const result = await Task.resolveParams(
Type.Object({ Type.Object({
test: dynamic(Type.Array(Type.String())) test: dynamic(Type.Array(Type.String())),
}), }),
{ {
test: '{{ "1,2,3" | split: "," | json }}' test: '{{ "1,2,3" | split: "," | json }}',
} },
); );
expect(result.test).toEqual(["1", "2", "3"]); expect(result.test).toEqual(["1", "2", "3"]);
@@ -87,11 +87,11 @@ describe("Task", async () => {
test("resolveParams: boolean", async () => { test("resolveParams: boolean", async () => {
const result = await Task.resolveParams( const result = await Task.resolveParams(
Type.Object({ Type.Object({
test: dynamic(Type.Boolean()) test: dynamic(Type.Boolean()),
}), }),
{ {
test: "{{ true }}" test: "{{ true }}",
} },
); );
expect(result.test).toEqual(true); expect(result.test).toEqual(true);
@@ -100,11 +100,11 @@ describe("Task", async () => {
test("resolveParams: float", async () => { test("resolveParams: float", async () => {
const result = await Task.resolveParams( const result = await Task.resolveParams(
Type.Object({ Type.Object({
test: dynamic(Type.Number(), Number.parseFloat) test: dynamic(Type.Number(), Number.parseFloat),
}), }),
{ {
test: "{{ 3.14 }}" test: "{{ 3.14 }}",
} },
); );
expect(result.test).toEqual(3.14); expect(result.test).toEqual(3.14);

View File

@@ -7,11 +7,11 @@ const first = getNamedTask(
//throw new Error("Error"); //throw new Error("Error");
return { return {
inner: { inner: {
result: 2 result: 2,
} },
}; };
}, },
1000 1000,
); );
const second = getNamedTask("second (if match)"); const second = getNamedTask("second (if match)");
const third = getNamedTask("third (if error)"); const third = getNamedTask("third (if error)");

View File

@@ -11,7 +11,7 @@ class ExecTask extends Task {
constructor( constructor(
name: string, name: string,
params: any, params: any,
private fn: () => any private fn: () => any,
) { ) {
super(name, params); super(name, params);
} }
@@ -54,8 +54,8 @@ export function getNamedTask(name: string, _func?: () => Promise<any>, delay?: n
return new ExecTask( return new ExecTask(
name, name,
{ {
delay delay,
}, },
func func,
); );
} }

View File

@@ -4,7 +4,7 @@ const first = new LogTask("First", { delay: 1000 });
const second = new LogTask("Second", { delay: 1000 }); const second = new LogTask("Second", { delay: 1000 });
const third = new LogTask("Long Third", { delay: 2500 }); const third = new LogTask("Long Third", { delay: 2500 });
const fourth = new FetchTask("Fetch Something", { const fourth = new FetchTask("Fetch Something", {
url: "https://jsonplaceholder.typicode.com/todos/1" url: "https://jsonplaceholder.typicode.com/todos/1",
}); });
const fifth = new LogTask("Task 4", { delay: 500 }); // without connection const fifth = new LogTask("Task 4", { delay: 500 }); // without connection

View File

@@ -23,10 +23,10 @@ class OutputParamTask extends Task<typeof OutputParamTask.schema> {
static override schema = Type.Object({ static override schema = Type.Object({
number: dynamic( number: dynamic(
Type.Number({ Type.Number({
title: "Output number" title: "Output number",
}), }),
Number.parseInt Number.parseInt,
) ),
}); });
async execute(inputs: InputsMap) { async execute(inputs: InputsMap) {
@@ -75,7 +75,7 @@ describe("Flow task inputs", async () => {
test("output/input", async () => { test("output/input", async () => {
const task = new OutputParamTask("task1", { number: 111 }); const task = new OutputParamTask("task1", { number: 111 });
const task2 = new OutputParamTask("task2", { const task2 = new OutputParamTask("task2", {
number: "{{ task1.output }}" number: "{{ task1.output }}",
}); });
const flow = new Flow("test", [task, task2]); const flow = new Flow("test", [task, task2]);
@@ -94,10 +94,10 @@ describe("Flow task inputs", async () => {
test("input from flow", async () => { test("input from flow", async () => {
const task = new OutputParamTask("task1", { const task = new OutputParamTask("task1", {
number: "{{flow.output.someFancyParam}}" number: "{{flow.output.someFancyParam}}",
}); });
const task2 = new OutputParamTask("task2", { const task2 = new OutputParamTask("task2", {
number: "{{task1.output}}" number: "{{task1.output}}",
}); });
const flow = new Flow("test", [task, task2]); const flow = new Flow("test", [task, task2]);
@@ -126,7 +126,7 @@ describe("Flow task inputs", async () => {
const emgr = new EventManager({ EventTriggerClass }); const emgr = new EventManager({ EventTriggerClass });
const task = new OutputParamTask("event", { const task = new OutputParamTask("event", {
number: "{{flow.output.number}}" number: "{{flow.output.number}}",
}); });
const flow = new Flow( const flow = new Flow(
"test", "test",
@@ -134,8 +134,8 @@ describe("Flow task inputs", async () => {
[], [],
new EventTrigger({ new EventTrigger({
event: "test-event", event: "test-event",
mode: "sync" mode: "sync",
}) }),
); );
flow.setRespondingTask(task); flow.setRespondingTask(task);
flow.trigger.register(flow, emgr); flow.trigger.register(flow, emgr);
@@ -155,8 +155,8 @@ describe("Flow task inputs", async () => {
new HttpTrigger({ new HttpTrigger({
path: "/test", path: "/test",
method: "GET", method: "GET",
mode: "sync" mode: "sync",
}) }),
); );
flow.setRespondingTask(task); flow.setRespondingTask(task);

View File

@@ -10,7 +10,7 @@ const flows = {
back, back,
fanout, fanout,
parallel, parallel,
simpleFetch simpleFetch,
}; };
const arg = process.argv[2]; const arg = process.argv[2];
@@ -32,7 +32,7 @@ const colors = [
"#F78F1E", // Saffron "#F78F1E", // Saffron
"#BD10E0", // Vivid Purple "#BD10E0", // Vivid Purple
"#50E3C2", // Turquoise "#50E3C2", // Turquoise
"#9013FE" // Grape "#9013FE", // Grape
]; ];
const colorsCache: Record<string, string> = {}; const colorsCache: Record<string, string> = {};
@@ -82,7 +82,7 @@ function TerminalFlow({ flow }: { flow: Flow }) {
} }
return t; return t;
}) }),
); );
} }
}); });
@@ -92,7 +92,7 @@ function TerminalFlow({ flow }: { flow: Flow }) {
console.log("done", response ? response : "(no response)"); console.log("done", response ? response : "(no response)");
console.log( console.log(
"Executed tasks:", "Executed tasks:",
execution.logs.map((l) => l.task.name) execution.logs.map((l) => l.task.name),
); );
console.log("Executed count:", execution.logs.length); console.log("Executed count:", execution.logs.length);
}); });

View File

@@ -11,7 +11,7 @@ class ExecTask extends Task {
constructor( constructor(
name: string, name: string,
params: any, params: any,
private fn: () => any private fn: () => any,
) { ) {
super(name, params); super(name, params);
} }
@@ -60,7 +60,7 @@ describe("Flow trigger", async () => {
"test", "test",
[task], [task],
[], [],
new EventTrigger({ event: "test-event", mode: "sync" }) new EventTrigger({ event: "test-event", mode: "sync" }),
); );
flow.trigger.register(flow, emgr); flow.trigger.register(flow, emgr);
@@ -107,8 +107,8 @@ describe("Flow trigger", async () => {
new HttpTrigger({ new HttpTrigger({
path: "/test", path: "/test",
method: "GET", method: "GET",
mode: "sync" mode: "sync",
}) }),
); );
const hono = new Hono(); const hono = new Hono();
@@ -123,7 +123,7 @@ describe("Flow trigger", async () => {
test("http trigger with response", async () => { test("http trigger with response", async () => {
const task = ExecTask.create("http", () => ({ const task = ExecTask.create("http", () => ({
called: true called: true,
})); }));
const flow = new Flow( const flow = new Flow(
"test", "test",
@@ -132,8 +132,8 @@ describe("Flow trigger", async () => {
new HttpTrigger({ new HttpTrigger({
path: "/test", path: "/test",
method: "GET", method: "GET",
mode: "sync" mode: "sync",
}) }),
); );
flow.setRespondingTask(task); flow.setRespondingTask(task);

View File

@@ -11,13 +11,13 @@ class ExecTask extends Task<typeof ExecTask.schema> {
type = "exec"; type = "exec";
static override schema = Type.Object({ static override schema = Type.Object({
delay: Type.Number({ default: 10 }) delay: Type.Number({ default: 10 }),
}); });
constructor( constructor(
name: string, name: string,
params: Static<typeof ExecTask.schema>, params: Static<typeof ExecTask.schema>,
private func: () => Promise<any> private func: () => Promise<any>,
) { ) {
super(name, params); super(name, params);
} }
@@ -36,12 +36,12 @@ function getTask(num: number = 0, delay: number = 5) {
return new ExecTask( return new ExecTask(
`Task ${num}`, `Task ${num}`,
{ {
delay delay,
}, },
async () => { async () => {
//console.log(`[DONE] Task: ${num}`); //console.log(`[DONE] Task: ${num}`);
return true; return true;
} },
); );
//return new LogTask(`Log ${num}`, { delay }); //return new LogTask(`Log ${num}`, { delay });
} }
@@ -56,9 +56,9 @@ function getNamedTask(name: string, _func?: () => Promise<any>, delay?: number)
return new ExecTask( return new ExecTask(
name, name,
{ {
delay: delay ?? 0 delay: delay ?? 0,
}, },
func func,
); );
} }
@@ -228,7 +228,7 @@ describe("Flow tests", async () => {
back back
.task(third) .task(third)
.getOutTasks() .getOutTasks()
.map((t) => t.name) .map((t) => t.name),
).toEqual(["second", "fourth"]); ).toEqual(["second", "fourth"]);
const execution = back.createExecution(); const execution = back.createExecution();
@@ -263,7 +263,7 @@ describe("Flow tests", async () => {
back back
.task(third) .task(third)
.getOutTasks() .getOutTasks()
.map((t) => t.name) .map((t) => t.name),
).toEqual(["second", "fourth"]); ).toEqual(["second", "fourth"]);
const execution = back.createExecution(); const execution = back.createExecution();
@@ -324,8 +324,8 @@ describe("Flow tests", async () => {
const first = getNamedTask("first", async () => { const first = getNamedTask("first", async () => {
return { return {
inner: { inner: {
result: 2 result: 2,
} },
}; };
}); });
const second = getNamedTask("second"); const second = getNamedTask("second");
@@ -361,7 +361,7 @@ describe("Flow tests", async () => {
"[event]", "[event]",
event.isStart() ? "start" : "end", event.isStart() ? "start" : "end",
event.task().name, event.task().name,
event.isStart() ? undefined : event.succeeded() event.isStart() ? undefined : event.succeeded(),
); );
} }
}); });
@@ -389,7 +389,7 @@ describe("Flow tests", async () => {
const second = new LogTask("Task 1"); const second = new LogTask("Task 1");
const third = new LogTask("Task 2", { delay: 50 }); const third = new LogTask("Task 2", { delay: 50 });
const fourth = new FetchTask("Fetch Something", { const fourth = new FetchTask("Fetch Something", {
url: "https://jsonplaceholder.typicode.com/todos/1" url: "https://jsonplaceholder.typicode.com/todos/1",
}); });
const fifth = new LogTask("Task 4"); // without connection const fifth = new LogTask("Task 4"); // without connection
@@ -405,7 +405,7 @@ describe("Flow tests", async () => {
// @todo: fix // @todo: fix
const deserialized = Flow.fromObject("", original, { const deserialized = Flow.fromObject("", original, {
fetch: { cls: FetchTask }, fetch: { cls: FetchTask },
log: { cls: LogTask } log: { cls: LogTask },
} as any); } as any);
const diffdeep = getObjectDiff(original, deserialized.toJSON()); const diffdeep = getObjectDiff(original, deserialized.toJSON());
@@ -414,7 +414,7 @@ describe("Flow tests", async () => {
expect(flow.startTask.name).toEqual(deserialized.startTask.name); expect(flow.startTask.name).toEqual(deserialized.startTask.name);
expect(flow.respondingTask?.name).toEqual( expect(flow.respondingTask?.name).toEqual(
// @ts-ignore // @ts-ignore
deserialized.respondingTask?.name deserialized.respondingTask?.name,
); );
//console.log("--- creating original sequence"); //console.log("--- creating original sequence");

View File

@@ -17,7 +17,7 @@ export function getDummyDatabase(memory: boolean = true): {
afterAllCleanup: async () => { afterAllCleanup: async () => {
if (!memory) await unlink(DB_NAME); if (!memory) await unlink(DB_NAME);
return true; return true;
} },
}; };
} }
@@ -27,7 +27,7 @@ export function getDummyConnection(memory: boolean = true) {
return { return {
dummyConnection, dummyConnection,
afterAllCleanup afterAllCleanup,
}; };
} }
@@ -39,7 +39,7 @@ type ConsoleSeverity = "log" | "warn" | "error";
const _oldConsoles = { const _oldConsoles = {
log: console.log, log: console.log,
warn: console.warn, warn: console.warn,
error: console.error error: console.error,
}; };
export function disableConsoleLog(severities: ConsoleSeverity[] = ["log", "warn"]) { export function disableConsoleLog(severities: ConsoleSeverity[] = ["log", "warn"]) {

View File

@@ -16,25 +16,25 @@ const roles = {
"system.schema.read", "system.schema.read",
"system.access.api", "system.access.api",
"system.config.read", "system.config.read",
"data.entity.read" "data.entity.read",
], ],
is_default: true is_default: true,
}, },
admin: { admin: {
is_default: true, is_default: true,
implicit_allow: true implicit_allow: true,
} },
}, },
strict: { strict: {
guest: { guest: {
permissions: ["system.access.api", "system.config.read", "data.entity.read"], permissions: ["system.access.api", "system.config.read", "data.entity.read"],
is_default: true is_default: true,
}, },
admin: { admin: {
is_default: true, is_default: true,
implicit_allow: true implicit_allow: true,
} },
} },
}; };
const configs = { const configs = {
auth: { auth: {
@@ -42,31 +42,31 @@ const configs = {
entity_name: "users", entity_name: "users",
jwt: { jwt: {
secret: secureRandomString(20), secret: secureRandomString(20),
issuer: randomString(10) issuer: randomString(10),
}, },
roles: roles.strict, roles: roles.strict,
guard: { guard: {
enabled: true enabled: true,
} },
}, },
users: { users: {
normal: { normal: {
email: "normal@bknd.io", email: "normal@bknd.io",
password: "12345678" password: "12345678",
}, },
admin: { admin: {
email: "admin@bknd.io", email: "admin@bknd.io",
password: "12345678", password: "12345678",
role: "admin" role: "admin",
} },
} },
}; };
function createAuthApp() { function createAuthApp() {
const app = createApp({ const app = createApp({
initialConfig: { initialConfig: {
auth: configs.auth auth: configs.auth,
} },
}); });
app.emgr.onEvent( app.emgr.onEvent(
@@ -75,7 +75,7 @@ function createAuthApp() {
await app.createUser(configs.users.normal); await app.createUser(configs.users.normal);
await app.createUser(configs.users.admin); await app.createUser(configs.users.admin);
}, },
"sync" "sync",
); );
return app; return app;
@@ -94,14 +94,14 @@ const fns = <Mode extends "cookie" | "token" = "token">(app: App, mode?: Mode) =
if (mode === "cookie") { if (mode === "cookie") {
return { return {
cookie: `auth=${token};`, cookie: `auth=${token};`,
...additional ...additional,
}; };
} }
return { return {
Authorization: token ? `Bearer ${token}` : "", Authorization: token ? `Bearer ${token}` : "",
"Content-Type": "application/json", "Content-Type": "application/json",
...additional ...additional,
}; };
} }
function body(obj?: Record<string, any>) { function body(obj?: Record<string, any>) {
@@ -118,12 +118,12 @@ const fns = <Mode extends "cookie" | "token" = "token">(app: App, mode?: Mode) =
return { return {
login: async ( login: async (
user: any user: any,
): Promise<{ res: Response; data: Mode extends "token" ? AuthResponse : string }> => { ): Promise<{ res: Response; data: Mode extends "token" ? AuthResponse : string }> => {
const res = (await app.server.request("/api/auth/password/login", { const res = (await app.server.request("/api/auth/password/login", {
method: "POST", method: "POST",
headers: headers(), headers: headers(),
body: body(user) body: body(user),
})) as Response; })) as Response;
const data = mode === "cookie" ? getCookie(res, "auth") : await res.json(); const data = mode === "cookie" ? getCookie(res, "auth") : await res.json();
@@ -133,10 +133,10 @@ const fns = <Mode extends "cookie" | "token" = "token">(app: App, mode?: Mode) =
me: async (token?: string): Promise<Pick<AuthResponse, "user">> => { me: async (token?: string): Promise<Pick<AuthResponse, "user">> => {
const res = (await app.server.request("/api/auth/me", { const res = (await app.server.request("/api/auth/me", {
method: "GET", method: "GET",
headers: headers(token) headers: headers(token),
})) as Response; })) as Response;
return await res.json(); return await res.json();
} },
}; };
}; };
@@ -219,7 +219,7 @@ describe("integration auth", () => {
app.server.get("/get", auth(), async (c) => { app.server.get("/get", auth(), async (c) => {
return c.json({ return c.json({
user: c.get("auth").user ?? null user: c.get("auth").user ?? null,
}); });
}); });
app.server.get("/wait", auth(), async (c) => { app.server.get("/wait", auth(), async (c) => {
@@ -232,7 +232,7 @@ describe("integration auth", () => {
expect(me.user.email).toBe(configs.users.normal.email); expect(me.user.email).toBe(configs.users.normal.email);
app.server.request("/wait", { app.server.request("/wait", {
headers: { Authorization: `Bearer ${data.token}` } headers: { Authorization: `Bearer ${data.token}` },
}); });
{ {

View File

@@ -8,7 +8,7 @@ describe("integration config", () => {
await app.build(); await app.build();
const api = new Api({ const api = new Api({
host: "http://localhost", host: "http://localhost",
fetcher: app.server.request as typeof fetch fetcher: app.server.request as typeof fetch,
}); });
// create entity // create entity
@@ -16,7 +16,7 @@ describe("integration config", () => {
name: "posts", name: "posts",
config: { sort_field: "id", sort_dir: "asc" }, config: { sort_field: "id", sort_dir: "asc" },
fields: { id: { type: "primary", name: "id" }, asdf: { type: "text" } }, fields: { id: { type: "primary", name: "id" }, asdf: { type: "text" } },
type: "regular" type: "regular",
}); });
expect(app.em.entities.map((e) => e.name)).toContain("posts"); expect(app.em.entities.map((e) => e.name)).toContain("posts");

View File

@@ -22,13 +22,13 @@ async function makeApp(mediaOverride: Partial<TAppMediaConfig> = {}) {
adapter: { adapter: {
type: "local", type: "local",
config: { config: {
path: assetsTmpPath path: assetsTmpPath,
} },
} },
},
mediaOverride,
),
}, },
mediaOverride
)
}
}); });
await app.build(); await app.build();
@@ -50,7 +50,7 @@ describe("MediaController", () => {
const name = makeName("png"); const name = makeName("png");
const res = await app.server.request("/api/media/upload/" + name, { const res = await app.server.request("/api/media/upload/" + name, {
method: "POST", method: "POST",
body: file body: file,
}); });
const result = (await res.json()) as any; const result = (await res.json()) as any;
console.log(result); console.log(result);
@@ -71,7 +71,7 @@ describe("MediaController", () => {
const res = await app.server.request("/api/media/upload/" + name, { const res = await app.server.request("/api/media/upload/" + name, {
method: "POST", method: "POST",
body: form body: form,
}); });
const result = (await res.json()) as any; const result = (await res.json()) as any;
expect(result.name).toBe(name); expect(result.name).toBe(name);
@@ -88,7 +88,7 @@ describe("MediaController", () => {
const name = makeName("png"); const name = makeName("png");
const res = await app.server.request("/api/media/upload/" + name, { const res = await app.server.request("/api/media/upload/" + name, {
method: "POST", method: "POST",
body: file body: file,
}); });
expect(res.status).toBe(413); expect(res.status).toBe(413);

View File

@@ -60,7 +60,7 @@ describe("Storage", async () => {
test("uploads a file", async () => { test("uploads a file", async () => {
const { const {
meta: { type, size } meta: { type, size },
} = await storage.uploadFile("hello", "world.txt"); } = await storage.uploadFile("hello", "world.txt");
expect({ type, size }).toEqual({ type: "text/plain", size: 0 }); expect({ type, size }).toEqual({ type: "text/plain", size: 0 });
}); });

View File

@@ -14,7 +14,7 @@ test("what", async () => {
const mf = new Miniflare({ const mf = new Miniflare({
modules: true, modules: true,
script: "export default { async fetch() { return new Response(null); } }", script: "export default { async fetch() { return new Response(null); } }",
r2Buckets: ["BUCKET"] r2Buckets: ["BUCKET"],
}); });
const bucket = await mf.getR2Bucket("BUCKET"); const bucket = await mf.getR2Bucket("BUCKET");

View File

@@ -8,7 +8,7 @@ const {
CLOUDINARY_CLOUD_NAME, CLOUDINARY_CLOUD_NAME,
CLOUDINARY_API_KEY, CLOUDINARY_API_KEY,
CLOUDINARY_API_SECRET, CLOUDINARY_API_SECRET,
CLOUDINARY_UPLOAD_PRESET CLOUDINARY_UPLOAD_PRESET,
} = dotenvOutput.parsed!; } = dotenvOutput.parsed!;
const ALL_TESTS = !!process.env.ALL_TESTS; const ALL_TESTS = !!process.env.ALL_TESTS;
@@ -20,7 +20,7 @@ describe.skipIf(ALL_TESTS)("StorageCloudinaryAdapter", () => {
cloud_name: CLOUDINARY_CLOUD_NAME as string, cloud_name: CLOUDINARY_CLOUD_NAME as string,
api_key: CLOUDINARY_API_KEY as string, api_key: CLOUDINARY_API_KEY as string,
api_secret: CLOUDINARY_API_SECRET as string, api_secret: CLOUDINARY_API_SECRET as string,
upload_preset: CLOUDINARY_UPLOAD_PRESET as string upload_preset: CLOUDINARY_UPLOAD_PRESET as string,
}); });
const file = Bun.file(`${import.meta.dir}/icon.png`); const file = Bun.file(`${import.meta.dir}/icon.png`);

View File

@@ -5,7 +5,7 @@ import { assetsPath, assetsTmpPath } from "../../helper";
describe("StorageLocalAdapter", () => { describe("StorageLocalAdapter", () => {
const adapter = new StorageLocalAdapter({ const adapter = new StorageLocalAdapter({
path: assetsTmpPath path: assetsTmpPath,
}); });
const file = Bun.file(`${assetsPath}/image.png`); const file = Bun.file(`${assetsPath}/image.png`);
@@ -36,7 +36,7 @@ describe("StorageLocalAdapter", () => {
test("gets object meta", async () => { test("gets object meta", async () => {
expect(await adapter.getObjectMeta(filename)).toEqual({ expect(await adapter.getObjectMeta(filename)).toEqual({
type: file.type, // image/png type: file.type, // image/png
size: file.size size: file.size,
}); });
}); });

View File

@@ -20,17 +20,17 @@ describe.skipIf(true)("StorageS3Adapter", async () => {
new StorageS3Adapter({ new StorageS3Adapter({
access_key: R2_ACCESS_KEY as string, access_key: R2_ACCESS_KEY as string,
secret_access_key: R2_SECRET_ACCESS_KEY as string, secret_access_key: R2_SECRET_ACCESS_KEY as string,
url: R2_URL as string url: R2_URL as string,
}) }),
], ],
[ [
"s3", "s3",
new StorageS3Adapter({ new StorageS3Adapter({
access_key: AWS_ACCESS_KEY as string, access_key: AWS_ACCESS_KEY as string,
secret_access_key: AWS_SECRET_KEY as string, secret_access_key: AWS_SECRET_KEY as string,
url: AWS_S3_URL as string url: AWS_S3_URL as string,
}) }),
] ],
] as const; ] as const;
const _conf = { const _conf = {
@@ -41,8 +41,8 @@ describe.skipIf(true)("StorageS3Adapter", async () => {
"objectExists", "objectExists",
"getObject", "getObject",
"deleteObject", "deleteObject",
"getObjectMeta" "getObjectMeta",
] ],
}; };
const file = Bun.file(`${import.meta.dir}/icon.png`); const file = Bun.file(`${import.meta.dir}/icon.png`);
@@ -86,7 +86,7 @@ describe.skipIf(true)("StorageS3Adapter", async () => {
test.skipIf(disabled("getObjectMeta"))("gets object meta", async () => { test.skipIf(disabled("getObjectMeta"))("gets object meta", async () => {
expect(await adapter.getObjectMeta(filename)).toEqual({ expect(await adapter.getObjectMeta(filename)).toEqual({
type: file.type, // image/png type: file.type, // image/png
size: file.size size: file.size,
}); });
}); });

View File

@@ -28,7 +28,7 @@ describe("media/mime-types", () => {
exts, exts,
ext, ext,
expected: ex, expected: ex,
actual: large.guessMimeType("." + ext) actual: large.guessMimeType("." + ext),
}); });
throw new Error(`Failed for ${ext}`); throw new Error(`Failed for ${ext}`);
} }
@@ -49,13 +49,13 @@ describe("media/mime-types", () => {
["image/gif", true], ["image/gif", true],
["whatever", false], ["whatever", false],
["text/tab-separated-values", true], ["text/tab-separated-values", true],
["application/zip", true] ["application/zip", true],
]; ];
for (const [mime, expected, exclude] of tests) { for (const [mime, expected, exclude] of tests) {
expect( expect(
tiny.isMimeType(mime, exclude as any), tiny.isMimeType(mime, exclude as any),
`isMimeType(): ${mime} should be ${expected}` `isMimeType(): ${mime} should be ${expected}`,
).toBe(expected as any); ).toBe(expected as any);
} }
}); });
@@ -68,7 +68,7 @@ describe("media/mime-types", () => {
["image/jpeg", "jpeg"], ["image/jpeg", "jpeg"],
["application/zip", "zip"], ["application/zip", "zip"],
["text/tab-separated-values", "tsv"], ["text/tab-separated-values", "tsv"],
["application/zip", "zip"] ["application/zip", "zip"],
]; ];
for (const [mime, ext] of tests) { for (const [mime, ext] of tests) {
@@ -85,13 +85,13 @@ describe("media/mime-types", () => {
["image.heic", "heic"], ["image.heic", "heic"],
["image.jpeg", "jpeg"], ["image.jpeg", "jpeg"],
["-473Wx593H-466453554-black-MODEL.jpg", "jpg"], ["-473Wx593H-466453554-black-MODEL.jpg", "jpg"],
["-473Wx593H-466453554-black-MODEL.avif", "avif"] ["-473Wx593H-466453554-black-MODEL.avif", "avif"],
]; ];
for (const [filename, ext] of tests) { for (const [filename, ext] of tests) {
expect( expect(
getRandomizedFilename(filename).split(".").pop(), getRandomizedFilename(filename).split(".").pop(),
`getRandomizedFilename(): ${filename} should end with ${ext}` `getRandomizedFilename(): ${filename} should end with ${ext}`,
).toBe(ext); ).toBe(ext);
} }
}); });

View File

@@ -43,10 +43,10 @@ describe("AppAuth", () => {
{ {
enabled: true, enabled: true,
jwt: { jwt: {
secret: "123456" secret: "123456",
}
}, },
ctx },
ctx,
); );
await auth.build(); await auth.build();
@@ -63,12 +63,12 @@ describe("AppAuth", () => {
const res = await app.request("/password/register", { const res = await app.request("/password/register", {
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/json" "Content-Type": "application/json",
}, },
body: JSON.stringify({ body: JSON.stringify({
email: "some@body.com", email: "some@body.com",
password: "123456" password: "123456",
}) }),
}); });
enableConsoleLog(); enableConsoleLog();
expect(res.status).toBe(200); expect(res.status).toBe(200);
@@ -85,10 +85,10 @@ describe("AppAuth", () => {
auth: { auth: {
enabled: true, enabled: true,
jwt: { jwt: {
secret: "123456" secret: "123456",
} },
} },
} },
}); });
await app.build(); await app.build();
@@ -109,14 +109,14 @@ describe("AppAuth", () => {
initialConfig: { initialConfig: {
auth: { auth: {
entity_name: "users", entity_name: "users",
enabled: true enabled: true,
}, },
data: em({ data: em({
users: entity("users", { users: entity("users", {
additional: text() additional: text(),
}) }),
}).toJSON() }).toJSON(),
} },
}); });
await app.build(); await app.build();
@@ -132,21 +132,21 @@ describe("AppAuth", () => {
const app = createApp({ const app = createApp({
initialConfig: { initialConfig: {
auth: { auth: {
enabled: true enabled: true,
}, },
data: em({ data: em({
users: entity("users", { users: entity("users", {
strategy: text({ strategy: text({
fillable: true, fillable: true,
hidden: false hidden: false,
}), }),
strategy_value: text({ strategy_value: text({
fillable: true, fillable: true,
hidden: false hidden: false,
}) }),
}) }),
}).toJSON() }).toJSON(),
} },
}); });
await app.build(); await app.build();

View File

@@ -19,16 +19,16 @@ describe("AppMedia", () => {
adapter: { adapter: {
type: "local", type: "local",
config: { config: {
path: "./" path: "./",
} },
} },
}, },
data: em({ data: em({
media: entity("media", { media: entity("media", {
additional: text() additional: text(),
}) }),
}).toJSON() }).toJSON(),
} },
}); });
await app.build(); await app.build();
@@ -49,7 +49,7 @@ describe("AppMedia", () => {
"modified_at", "modified_at",
"reference", "reference",
"entity_id", "entity_id",
"metadata" "metadata",
]); ]);
}); });
}); });

View File

@@ -47,7 +47,7 @@ describe("Module", async () => {
prt = { prt = {
ensureEntity: this.ensureEntity.bind(this), ensureEntity: this.ensureEntity.bind(this),
ensureIndex: this.ensureIndex.bind(this), ensureIndex: this.ensureIndex.bind(this),
ensureSchema: this.ensureSchema.bind(this) ensureSchema: this.ensureSchema.bind(this),
}; };
get em() { get em() {
@@ -60,7 +60,7 @@ describe("Module", async () => {
Object.values(_em.entities), Object.values(_em.entities),
new DummyConnection(), new DummyConnection(),
_em.relations, _em.relations,
_em.indices _em.indices,
); );
return new M({} as any, { em, flags: Module.ctx_flags } as any); return new M({} as any, { em, flags: Module.ctx_flags } as any);
} }
@@ -69,14 +69,14 @@ describe("Module", async () => {
entities: _em.entities.map((e) => ({ entities: _em.entities.map((e) => ({
name: e.name, name: e.name,
fields: e.fields.map((f) => f.name), fields: e.fields.map((f) => f.name),
type: e.type type: e.type,
})), })),
indices: _em.indices.map((i) => ({ indices: _em.indices.map((i) => ({
name: i.name, name: i.name,
entity: i.entity.name, entity: i.entity.name,
fields: i.fields.map((f) => f.name), fields: i.fields.map((f) => f.name),
unique: i.unique unique: i.unique,
})) })),
}; };
} }
@@ -88,15 +88,15 @@ describe("Module", async () => {
expect(flat(make(initial).em)).toEqual({ expect(flat(make(initial).em)).toEqual({
entities: [], entities: [],
indices: [] indices: [],
}); });
}); });
test("init", () => { test("init", () => {
const initial = em({ const initial = em({
users: entity("u", { users: entity("u", {
name: text() name: text(),
}) }),
}); });
const m = make(initial); const m = make(initial);
@@ -107,18 +107,18 @@ describe("Module", async () => {
{ {
name: "u", name: "u",
fields: ["id", "name"], fields: ["id", "name"],
type: "regular" type: "regular",
} },
], ],
indices: [] indices: [],
}); });
}); });
test("ensure entity", () => { test("ensure entity", () => {
const initial = em({ const initial = em({
users: entity("u", { users: entity("u", {
name: text() name: text(),
}) }),
}); });
const m = make(initial); const m = make(initial);
@@ -127,17 +127,17 @@ describe("Module", async () => {
{ {
name: "u", name: "u",
fields: ["id", "name"], fields: ["id", "name"],
type: "regular" type: "regular",
} },
], ],
indices: [] indices: [],
}); });
// this should add a new entity // this should add a new entity
m.prt.ensureEntity( m.prt.ensureEntity(
entity("p", { entity("p", {
title: text() title: text(),
}) }),
); );
// this should only add the field "important" // this should only add the field "important"
@@ -145,11 +145,11 @@ describe("Module", async () => {
entity( entity(
"u", "u",
{ {
important: text() important: text(),
}, },
undefined, undefined,
"system" "system",
) ),
); );
expect(m.ctx.flags.sync_required).toBe(true); expect(m.ctx.flags.sync_required).toBe(true);
@@ -159,22 +159,22 @@ describe("Module", async () => {
name: "u", name: "u",
fields: ["id", "name", "important"], fields: ["id", "name", "important"],
// ensured type must be present // ensured type must be present
type: "system" type: "system",
}, },
{ {
name: "p", name: "p",
fields: ["id", "title"], fields: ["id", "title"],
type: "regular" type: "regular",
} },
], ],
indices: [] indices: [],
}); });
}); });
test("ensure index", () => { test("ensure index", () => {
const users = entity("u", { const users = entity("u", {
name: text(), name: text(),
title: text() title: text(),
}); });
const initial = em({ users }, ({ index }, { users }) => { const initial = em({ users }, ({ index }, { users }) => {
index(users).on(["title"]); index(users).on(["title"]);
@@ -189,23 +189,23 @@ describe("Module", async () => {
{ {
name: "u", name: "u",
fields: ["id", "name", "title"], fields: ["id", "name", "title"],
type: "regular" type: "regular",
} },
], ],
indices: [ indices: [
{ {
name: "idx_u_title", name: "idx_u_title",
entity: "u", entity: "u",
fields: ["title"], fields: ["title"],
unique: false unique: false,
}, },
{ {
name: "idx_u_name", name: "idx_u_name",
entity: "u", entity: "u",
fields: ["name"], fields: ["name"],
unique: false unique: false,
} },
] ],
}); });
}); });
}); });

View File

@@ -39,10 +39,10 @@ describe("ModuleManager", async () => {
basepath: "/api/data2", basepath: "/api/data2",
entities: { entities: {
test: entity("test", { test: entity("test", {
content: text() content: text(),
}).toJSON() }).toJSON(),
} },
} },
}); });
//const { version, ...json } = mm.toJSON() as any; //const { version, ...json } = mm.toJSON() as any;
@@ -69,10 +69,10 @@ describe("ModuleManager", async () => {
basepath: "/api/data2", basepath: "/api/data2",
entities: { entities: {
test: entity("test", { test: entity("test", {
content: text() content: text(),
}).toJSON() }).toJSON(),
} },
} },
}; };
//const { version, ...json } = mm.toJSON() as any; //const { version, ...json } = mm.toJSON() as any;
@@ -105,7 +105,7 @@ describe("ModuleManager", async () => {
const c2 = getDummyConnection(); const c2 = getDummyConnection();
const db = c2.dummyConnection.kysely; const db = c2.dummyConnection.kysely;
const mm2 = new ModuleManager(c2.dummyConnection, { const mm2 = new ModuleManager(c2.dummyConnection, {
initial: { version: version - 1, ...json } initial: { version: version - 1, ...json },
}); });
await mm2.syncConfigTable(); await mm2.syncConfigTable();
@@ -129,7 +129,7 @@ describe("ModuleManager", async () => {
const db = c2.dummyConnection.kysely; const db = c2.dummyConnection.kysely;
const mm2 = new ModuleManager(c2.dummyConnection, { const mm2 = new ModuleManager(c2.dummyConnection, {
initial: { version: version - 1, ...json } initial: { version: version - 1, ...json },
}); });
await mm2.syncConfigTable(); await mm2.syncConfigTable();
await db await db
@@ -157,8 +157,8 @@ describe("ModuleManager", async () => {
...json, ...json,
data: { data: {
...json.data, ...json.data,
basepath: "/api/data2" basepath: "/api/data2",
} },
}; };
await db await db
.insertInto(TABLE_NAME) .insertInto(TABLE_NAME)
@@ -190,9 +190,9 @@ describe("ModuleManager", async () => {
...configs.server, ...configs.server,
admin: { admin: {
...configs.server.admin, ...configs.server.admin,
color_scheme: "dark" color_scheme: "dark",
} },
} },
}); });
}); });
@@ -201,11 +201,11 @@ describe("ModuleManager", async () => {
const partial = { const partial = {
auth: { auth: {
enabled: true enabled: true,
} },
}; };
const mm = new ModuleManager(dummyConnection, { const mm = new ModuleManager(dummyConnection, {
initial: partial initial: partial,
}); });
await mm.build(); await mm.build();
@@ -227,9 +227,9 @@ describe("ModuleManager", async () => {
const mm2 = new ModuleManager(c2.dummyConnection, { const mm2 = new ModuleManager(c2.dummyConnection, {
initial: { initial: {
auth: { auth: {
basepath: "/shouldnt/take/this" basepath: "/shouldnt/take/this",
} },
} },
}); });
await mm2.syncConfigTable(); await mm2.syncConfigTable();
const payload = { const payload = {
@@ -237,15 +237,15 @@ describe("ModuleManager", async () => {
auth: { auth: {
...json.auth, ...json.auth,
enabled: true, enabled: true,
basepath: "/api/auth2" basepath: "/api/auth2",
} },
}; };
await db await db
.insertInto(TABLE_NAME) .insertInto(TABLE_NAME)
.values({ .values({
type: "config", type: "config",
json: JSON.stringify(payload), json: JSON.stringify(payload),
version: CURRENT_VERSION version: CURRENT_VERSION,
}) })
.execute(); .execute();
await mm2.build(); await mm2.build();
@@ -256,7 +256,7 @@ describe("ModuleManager", async () => {
describe("revert", async () => { describe("revert", async () => {
const failingModuleSchema = Type.Object({ const failingModuleSchema = Type.Object({
value: Type.Optional(Type.Number()) value: Type.Optional(Type.Number()),
}); });
class FailingModule extends Module<typeof failingModuleSchema> { class FailingModule extends Module<typeof failingModuleSchema> {
getSchema() { getSchema() {
@@ -301,8 +301,8 @@ describe("ModuleManager", async () => {
const mm = new TestModuleManager(dummyConnection, { const mm = new TestModuleManager(dummyConnection, {
initial: { initial: {
// @ts-ignore // @ts-ignore
failing: { value: 2 } failing: { value: 2 },
} },
}); });
await mm.build(); await mm.build();
expect(mm.configs()["failing"].value).toBe(2); expect(mm.configs()["failing"].value).toBe(2);
@@ -313,8 +313,8 @@ describe("ModuleManager", async () => {
const mm = new TestModuleManager(dummyConnection, { const mm = new TestModuleManager(dummyConnection, {
initial: { initial: {
// @ts-ignore // @ts-ignore
failing: { value: -1 } failing: { value: -1 },
} },
}); });
expect(mm.build()).rejects.toThrow(/value must be positive/); expect(mm.build()).rejects.toThrow(/value must be positive/);
expect(mm.configs()["failing"].value).toBe(-1); expect(mm.configs()["failing"].value).toBe(-1);
@@ -326,7 +326,7 @@ describe("ModuleManager", async () => {
const mm = new TestModuleManager(dummyConnection, { const mm = new TestModuleManager(dummyConnection, {
onUpdated: async () => { onUpdated: async () => {
mockOnUpdated(); mockOnUpdated();
} },
}); });
await mm.build(); await mm.build();
// @ts-ignore // @ts-ignore
@@ -342,11 +342,11 @@ describe("ModuleManager", async () => {
const mm = new TestModuleManager(dummyConnection, { const mm = new TestModuleManager(dummyConnection, {
initial: { initial: {
// @ts-ignore // @ts-ignore
failing: { value: 1 } failing: { value: 1 },
}, },
onUpdated: async () => { onUpdated: async () => {
mockOnUpdated(); mockOnUpdated();
} },
}); });
await mm.build(); await mm.build();
expect(mm.configs()["failing"].value).toBe(1); expect(mm.configs()["failing"].value).toBe(1);
@@ -354,7 +354,7 @@ describe("ModuleManager", async () => {
// now safe mutate // now safe mutate
// @ts-ignore // @ts-ignore
expect(mm.mutateConfigSafe("failing").set({ value: -2 })).rejects.toThrow( expect(mm.mutateConfigSafe("failing").set({ value: -2 })).rejects.toThrow(
/value must be positive/ /value must be positive/,
); );
expect(mm.configs()["failing"].value).toBe(1); expect(mm.configs()["failing"].value).toBe(1);
expect(mockOnUpdated).toHaveBeenCalled(); expect(mockOnUpdated).toHaveBeenCalled();

View File

@@ -19,7 +19,7 @@ export function makeCtx(overrides?: Partial<ModuleBuildContext>): ModuleBuildCon
guard: new Guard(), guard: new Guard(),
flags: Module.ctx_flags, flags: Module.ctx_flags,
logger: new DebugLogger(false), logger: new DebugLogger(false),
...overrides ...overrides,
}; };
} }

View File

@@ -16,7 +16,7 @@ describe("json form", () => {
["0", { type: "boolean" }, false], ["0", { type: "boolean" }, false],
["on", { type: "boolean" }, true], ["on", { type: "boolean" }, true],
["off", { type: "boolean" }, false], ["off", { type: "boolean" }, false],
["null", { type: "null" }, null] ["null", { type: "null" }, null],
] satisfies [string, Exclude<JSONSchema, boolean>, any][]; ] satisfies [string, Exclude<JSONSchema, boolean>, any][];
for (const [input, schema, output] of examples) { for (const [input, schema, output] of examples) {
@@ -35,7 +35,7 @@ describe("json form", () => {
["array", "array", true], ["array", "array", true],
["object", "array", false], ["object", "array", false],
[["string", "number"], "number", true], [["string", "number"], "number", true],
["number", ["string", "number"], true] ["number", ["string", "number"], true],
] satisfies [IsTypeType, IsTypeType, boolean][]; ] satisfies [IsTypeType, IsTypeType, boolean][];
for (const [type, schemaType, output] of examples) { for (const [type, schemaType, output] of examples) {
@@ -48,7 +48,7 @@ describe("json form", () => {
["#/nested/property/0/name", "#/nested/property/0"], ["#/nested/property/0/name", "#/nested/property/0"],
["#/nested/property/0", "#/nested/property"], ["#/nested/property/0", "#/nested/property"],
["#/nested/property", "#/nested"], ["#/nested/property", "#/nested"],
["#/nested", "#"] ["#/nested", "#"],
]; ];
for (const [input, output] of examples) { for (const [input, output] of examples) {
@@ -61,16 +61,16 @@ describe("json form", () => {
[ [
"#/description", "#/description",
{ type: "object", properties: { description: { type: "string" } } }, { type: "object", properties: { description: { type: "string" } } },
false false,
], ],
[ [
"#/description", "#/description",
{ {
type: "object", type: "object",
required: ["description"], required: ["description"],
properties: { description: { type: "string" } } properties: { description: { type: "string" } },
}, },
true true,
], ],
[ [
"#/nested/property", "#/nested/property",
@@ -79,11 +79,11 @@ describe("json form", () => {
properties: { properties: {
nested: { nested: {
type: "object", type: "object",
properties: { property: { type: "string" } } properties: { property: { type: "string" } },
}
}
}, },
false },
},
false,
], ],
[ [
"#/nested/property", "#/nested/property",
@@ -93,12 +93,12 @@ describe("json form", () => {
nested: { nested: {
type: "object", type: "object",
required: ["property"], required: ["property"],
properties: { property: { type: "string" } } properties: { property: { type: "string" } },
}
}
}, },
true },
] },
true,
],
] satisfies [string, Exclude<JSONSchema, boolean>, boolean][]; ] satisfies [string, Exclude<JSONSchema, boolean>, boolean][];
for (const [pointer, schema, output] of examples) { for (const [pointer, schema, output] of examples) {
@@ -113,7 +113,7 @@ describe("json form", () => {
["tags", "0", "0.tags"], ["tags", "0", "0.tags"],
["tags", 0, "0.tags"], ["tags", 0, "0.tags"],
["nested.property", "prefix", "prefix.nested.property"], ["nested.property", "prefix", "prefix.nested.property"],
["nested.property", "", "nested.property"] ["nested.property", "", "nested.property"],
] satisfies [string, any, string][]; ] satisfies [string, any, string][];
for (const [path, prefix, output] of examples) { for (const [path, prefix, output] of examples) {
@@ -128,7 +128,7 @@ describe("json form", () => {
["tags", "0", "tags.0"], ["tags", "0", "tags.0"],
["tags", 0, "tags.0"], ["tags", 0, "tags.0"],
["nested.property", "suffix", "nested.property.suffix"], ["nested.property", "suffix", "nested.property.suffix"],
["nested.property", "", "nested.property"] ["nested.property", "", "nested.property"],
] satisfies [string, any, string][]; ] satisfies [string, any, string][];
for (const [path, suffix, output] of examples) { for (const [path, suffix, output] of examples) {

View File

@@ -19,11 +19,11 @@ const baseOptions: Partial<Omit<esbuild.BuildOptions, "plugins">> & { plugins?:
format: "esm", format: "esm",
drop: ["console", "debugger"], drop: ["console", "debugger"],
loader: { loader: {
".svg": "dataurl" ".svg": "dataurl",
}, },
define: { define: {
__isDev: "0" __isDev: "0",
} },
}; };
// @ts-ignore // @ts-ignore
@@ -40,7 +40,7 @@ const builds: Record<string, BuildFn> = {
"src/core/index.ts", "src/core/index.ts",
"src/core/utils/index.ts", "src/core/utils/index.ts",
"src/ui/index.ts", "src/ui/index.ts",
"src/ui/main.css" "src/ui/main.css",
], ],
outdir: "dist", outdir: "dist",
outExtension: { ".js": format === "esm" ? ".js" : ".cjs" }, outExtension: { ".js": format === "esm" ? ".js" : ".cjs" },
@@ -49,7 +49,7 @@ const builds: Record<string, BuildFn> = {
bundle: true, bundle: true,
plugins: [postcss()], plugins: [postcss()],
//target: "es2022", //target: "es2022",
format format,
}), }),
/*components: (format = "esm") => ({ /*components: (format = "esm") => ({
...baseOptions, ...baseOptions,
@@ -84,11 +84,11 @@ const builds: Record<string, BuildFn> = {
format, format,
loader: { loader: {
".svg": "dataurl", ".svg": "dataurl",
".js": "jsx" ".js": "jsx",
}, },
define: { define: {
__isDev: "0", __isDev: "0",
"process.env.NODE_ENV": '"production"' "process.env.NODE_ENV": '"production"',
}, },
chunkNames: "chunks/[name]-[hash]", chunkNames: "chunks/[name]-[hash]",
plugins: [ plugins: [
@@ -100,7 +100,7 @@ const builds: Record<string, BuildFn> = {
return { return {
name, name,
path: output, path: output,
mime: guessMimeType(name) mime: guessMimeType(name),
}; };
}; };
for (const { output, meta } of info) { for (const { output, meta } of info) {
@@ -113,9 +113,9 @@ const builds: Record<string, BuildFn> = {
const manifest_file = "dist/static/manifest.json"; const manifest_file = "dist/static/manifest.json";
await Bun.write(manifest_file, JSON.stringify(manifest, null, 2)); await Bun.write(manifest_file, JSON.stringify(manifest, null, 2));
console.log(`Manifest written to ${manifest_file}`, manifest); console.log(`Manifest written to ${manifest_file}`, manifest);
}) }),
] ],
}) }),
}; };
function adapter(adapter: string, overrides: Partial<esbuild.BuildOptions> = {}): BuildOptions { function adapter(adapter: string, overrides: Partial<esbuild.BuildOptions> = {}): BuildOptions {
@@ -135,12 +135,12 @@ function adapter(adapter: string, overrides: Partial<esbuild.BuildOptions> = {})
"react*", "react*",
"next*", "next*",
"libsql", "libsql",
"@libsql*" "@libsql*",
], ],
splitting: false, splitting: false,
treeShaking: true, treeShaking: true,
bundle: true, bundle: true,
...overrides ...overrides,
}; };
} }
const adapters = [ const adapters = [
@@ -152,7 +152,7 @@ const adapters = [
adapter("remix", { format: "cjs" }), adapter("remix", { format: "cjs" }),
adapter("bun"), adapter("bun"),
adapter("node", { platform: "node", format: "esm" }), adapter("node", { platform: "node", format: "esm" }),
adapter("node", { platform: "node", format: "cjs" }) adapter("node", { platform: "node", format: "cjs" }),
]; ];
const collect = [ const collect = [
@@ -161,7 +161,7 @@ const collect = [
//builds.components(), //builds.components(),
builds.backend("cjs"), builds.backend("cjs"),
//builds.components("cjs"), //builds.components("cjs"),
...adapters ...adapters,
]; ];
if (watch) { if (watch) {
@@ -172,7 +172,7 @@ if (watch) {
} = { } = {
timeout: undefined, timeout: undefined,
cleanup: undefined, cleanup: undefined,
building: undefined building: undefined,
}; };
async function rebuildTypes() { async function rebuildTypes() {
@@ -190,9 +190,9 @@ if (watch) {
onExit: () => { onExit: () => {
_state.building = undefined; _state.building = undefined;
console.log("Types rebuilt"); console.log("Types rebuilt");
} },
}); });
} },
}); });
}, 1000); }, 1000);
} }
@@ -209,9 +209,9 @@ if (watch) {
console.log(`rebuilt ${name} with ${result.errors.length} errors`); console.log(`rebuilt ${name} with ${result.errors.length} errors`);
rebuildTypes(); rebuildTypes();
}); });
} },
} },
] ],
}); });
ctx.watch(); ctx.watch();
} }
@@ -235,9 +235,9 @@ if (watch) {
const from = String(i).padStart(String(count).length); const from = String(i).padStart(String(count).length);
console.log(`[${from}/${count}] built ${name} with ${errors} errors`); console.log(`[${from}/${count}] built ${name} with ${errors} errors`);
}); });
} },
} },
] ],
}); });
} }
@@ -249,7 +249,7 @@ if (watch) {
Bun.spawn(["bun", "build:types"], { Bun.spawn(["bun", "build:types"], {
onExit: () => { onExit: () => {
console.log("Types rebuilt"); console.log("Types rebuilt");
} },
}); });
} }

View File

@@ -27,9 +27,9 @@ function buildTypes() {
onExit: () => { onExit: () => {
console.log("Types aliased"); console.log("Types aliased");
types_running = false; types_running = false;
} },
}); });
} },
}); });
} }
@@ -63,11 +63,11 @@ async function buildApi() {
splitting: false, splitting: false,
treeshake: true, treeshake: true,
loader: { loader: {
".svg": "dataurl" ".svg": "dataurl",
}, },
onSuccess: async () => { onSuccess: async () => {
delayTypes(); delayTypes();
} },
}); });
} }
@@ -90,7 +90,7 @@ async function buildUi() {
"use-sync-external-store", "use-sync-external-store",
/codemirror/, /codemirror/,
"@xyflow/react", "@xyflow/react",
"@mantine/core" "@mantine/core",
], ],
metafile: true, metafile: true,
platform: "browser", platform: "browser",
@@ -99,14 +99,14 @@ async function buildUi() {
bundle: true, bundle: true,
treeshake: true, treeshake: true,
loader: { loader: {
".svg": "dataurl" ".svg": "dataurl",
}, },
esbuildOptions: (options) => { esbuildOptions: (options) => {
options.logLevel = "silent"; options.logLevel = "silent";
}, },
onSuccess: async () => { onSuccess: async () => {
delayTypes(); delayTypes();
} },
}); });
} }
@@ -128,7 +128,7 @@ async function buildUiElements() {
"react-dom", "react-dom",
"react/jsx-runtime", "react/jsx-runtime",
"react/jsx-dev-runtime", "react/jsx-dev-runtime",
"use-sync-external-store" "use-sync-external-store",
], ],
metafile: true, metafile: true,
platform: "browser", platform: "browser",
@@ -137,12 +137,12 @@ async function buildUiElements() {
bundle: true, bundle: true,
treeshake: true, treeshake: true,
loader: { loader: {
".svg": "dataurl" ".svg": "dataurl",
}, },
esbuildOptions: (options) => { esbuildOptions: (options) => {
options.alias = { options.alias = {
// not important for elements, mock to reduce bundle // not important for elements, mock to reduce bundle
"tailwind-merge": "./src/ui/elements/mocks/tailwind-merge.ts" "tailwind-merge": "./src/ui/elements/mocks/tailwind-merge.ts",
}; };
}, },
onSuccess: async () => { onSuccess: async () => {
@@ -152,7 +152,7 @@ async function buildUiElements() {
await Bun.write(path, bundle.replaceAll("ui/client", "bknd/client")); await Bun.write(path, bundle.replaceAll("ui/client", "bknd/client"));
delayTypes(); delayTypes();
} },
}); });
} }
@@ -176,15 +176,15 @@ function baseConfig(adapter: string, overrides: Partial<tsup.Options> = {}): tsu
...overrides, ...overrides,
define: { define: {
__isDev: "0", __isDev: "0",
...overrides.define ...overrides.define,
}, },
external: [ external: [
/^cloudflare*/, /^cloudflare*/,
/^@?(hono|libsql).*?/, /^@?(hono|libsql).*?/,
/^(bknd|react|next|node).*?/, /^(bknd|react|next|node).*?/,
/.*\.(html)$/, /.*\.(html)$/,
...(Array.isArray(overrides.external) ? overrides.external : []) ...(Array.isArray(overrides.external) ? overrides.external : []),
] ],
}; };
} }
@@ -193,7 +193,7 @@ async function buildAdapters() {
await tsup.build({ await tsup.build({
...baseConfig(""), ...baseConfig(""),
entry: ["src/adapter/index.ts"], entry: ["src/adapter/index.ts"],
outDir: "dist/adapter" outDir: "dist/adapter",
}); });
// specific adatpers // specific adatpers
@@ -202,23 +202,23 @@ async function buildAdapters() {
await tsup.build(baseConfig("astro")); await tsup.build(baseConfig("astro"));
await tsup.build( await tsup.build(
baseConfig("cloudflare", { baseConfig("cloudflare", {
external: [/^kysely/] external: [/^kysely/],
}) }),
); );
await tsup.build({ await tsup.build({
...baseConfig("vite"), ...baseConfig("vite"),
platform: "node" platform: "node",
}); });
await tsup.build({ await tsup.build({
...baseConfig("nextjs"), ...baseConfig("nextjs"),
platform: "node" platform: "node",
}); });
await tsup.build({ await tsup.build({
...baseConfig("node"), ...baseConfig("node"),
platform: "node" platform: "node",
}); });
} }

View File

@@ -5,8 +5,8 @@ export const entryOutputMeta = (
outputs: { outputs: {
output: string; output: string;
meta: Metafile["outputs"][string]; meta: Metafile["outputs"][string];
}[] }[],
) => void | Promise<void> ) => void | Promise<void>,
): Plugin => ({ ): Plugin => ({
name: "report-entry-output-plugin", name: "report-entry-output-plugin",
setup(build) { setup(build) {
@@ -29,5 +29,5 @@ export const entryOutputMeta = (
await onComplete?.(outputs); await onComplete?.(outputs);
} }
}); });
} },
}); });

View File

@@ -11,8 +11,8 @@ export default {
"mantine-breakpoint-sm": "48em", "mantine-breakpoint-sm": "48em",
"mantine-breakpoint-md": "62em", "mantine-breakpoint-md": "62em",
"mantine-breakpoint-lg": "75em", "mantine-breakpoint-lg": "75em",
"mantine-breakpoint-xl": "88em" "mantine-breakpoint-xl": "88em",
} },
} },
} },
}; };

View File

@@ -153,7 +153,7 @@ export class Api {
return { return {
token: this.token, token: this.token,
user: this.user, user: this.user,
verified: this.verified verified: this.verified,
}; };
} }
@@ -198,7 +198,7 @@ export class Api {
token: this.token, token: this.token,
headers: this.options.headers, headers: this.options.headers,
token_transport: this.token_transport, token_transport: this.token_transport,
verbose: this.options.verbose verbose: this.options.verbose,
}); });
} }
@@ -211,9 +211,9 @@ export class Api {
this.auth = new AuthApi( this.auth = new AuthApi(
{ {
...baseParams, ...baseParams,
onTokenUpdate: (token) => this.updateToken(token, true) onTokenUpdate: (token) => this.updateToken(token, true),
}, },
fetcher fetcher,
); );
this.media = new MediaApi(baseParams, fetcher); this.media = new MediaApi(baseParams, fetcher);
} }

View File

@@ -8,7 +8,7 @@ import {
type ModuleBuildContext, type ModuleBuildContext,
ModuleManager, ModuleManager,
type ModuleManagerOptions, type ModuleManagerOptions,
type Modules type Modules,
} from "modules/ModuleManager"; } from "modules/ModuleManager";
import * as SystemPermissions from "modules/permissions"; import * as SystemPermissions from "modules/permissions";
import { AdminController, type AdminControllerOptions } from "modules/server/AdminController"; import { AdminController, type AdminControllerOptions } from "modules/server/AdminController";
@@ -61,7 +61,7 @@ export class App {
constructor( constructor(
private connection: Connection, private connection: Connection,
_initialConfig?: InitialModuleConfigs, _initialConfig?: InitialModuleConfigs,
private options?: AppOptions private options?: AppOptions,
) { ) {
this.plugins = options?.plugins ?? []; this.plugins = options?.plugins ?? [];
this.modules = new ModuleManager(connection, { this.modules = new ModuleManager(connection, {
@@ -91,7 +91,7 @@ export class App {
c.set("app", this); c.set("app", this);
await next(); await next();
}); });
} },
}); });
this.modules.ctx().emgr.registerEvents(AppEvents); this.modules.ctx().emgr.registerEvents(AppEvents);
} }
@@ -148,8 +148,8 @@ export class App {
{ {
get: (_, module: keyof Modules) => { get: (_, module: keyof Modules) => {
return this.modules.get(module); return this.modules.get(module);
} },
} },
) as Modules; ) as Modules;
} }
@@ -203,7 +203,7 @@ export function createApp(config: CreateAppConfig = {}) {
} else if (typeof config.connection === "object") { } else if (typeof config.connection === "object") {
if ("type" in config.connection) { if ("type" in config.connection) {
$console.warn( $console.warn(
"Using deprecated connection type 'libsql', use the 'config' object directly." "Using deprecated connection type 'libsql', use the 'config' object directly.",
); );
connection = new LibsqlConnection(config.connection.config); connection = new LibsqlConnection(config.connection.config);
} else { } else {

View File

@@ -17,7 +17,7 @@ export type Options = {
export async function getApi(Astro: TAstro, options: Options = { mode: "static" }) { export async function getApi(Astro: TAstro, options: Options = { mode: "static" }) {
const api = new Api({ const api = new Api({
host: new URL(Astro.request.url).origin, host: new URL(Astro.request.url).origin,
headers: options.mode === "dynamic" ? Astro.request.headers : undefined headers: options.mode === "dynamic" ? Astro.request.headers : undefined,
}); });
await api.verifyAuth(); await api.verifyAuth();
return api; return api;

View File

@@ -19,7 +19,7 @@ export async function createApp({ distPath, ...config }: RuntimeBkndConfig = {})
registerLocalMediaAdapter(); registerLocalMediaAdapter();
app = await createRuntimeApp({ app = await createRuntimeApp({
...config, ...config,
serveStatic: serveStatic({ root }) serveStatic: serveStatic({ root }),
}); });
} }
@@ -46,10 +46,10 @@ export function serve({
options, options,
onBuilt, onBuilt,
buildConfig, buildConfig,
distPath distPath,
}); });
return app.fetch(request); return app.fetch(request);
} },
}); });
console.log(`Server is running on http://localhost:${port}`); console.log(`Server is running on http://localhost:${port}`);

View File

@@ -12,7 +12,7 @@ export type D1ConnectionConfig = {
class CustomD1Dialect extends D1Dialect { class CustomD1Dialect extends D1Dialect {
override createIntrospector(db: Kysely<any>): DatabaseIntrospector { override createIntrospector(db: Kysely<any>): DatabaseIntrospector {
return new SqliteIntrospector(db, { return new SqliteIntrospector(db, {
excludeTables: ["_cf_KV"] excludeTables: ["_cf_KV"],
}); });
} }
} }
@@ -23,7 +23,7 @@ export class D1Connection extends SqliteConnection {
const kysely = new Kysely({ const kysely = new Kysely({
dialect: new CustomD1Dialect({ database: config.binding }), dialect: new CustomD1Dialect({ database: config.binding }),
plugins plugins,
}); });
super(kysely, {}, plugins); super(kysely, {}, plugins);
} }
@@ -37,7 +37,7 @@ export class D1Connection extends SqliteConnection {
} }
protected override async batch<Queries extends QB[]>( protected override async batch<Queries extends QB[]>(
queries: [...Queries] queries: [...Queries],
): Promise<{ ): Promise<{
[K in keyof Queries]: Awaited<ReturnType<Queries[K]["execute"]>>; [K in keyof Queries]: Awaited<ReturnType<Queries[K]["execute"]>>;
}> { }> {
@@ -47,7 +47,7 @@ export class D1Connection extends SqliteConnection {
queries.map((q) => { queries.map((q) => {
const { sql, parameters } = q.compile(); const { sql, parameters } = q.compile();
return db.prepare(sql).bind(...parameters); return db.prepare(sql).bind(...parameters);
}) }),
); );
// let it run through plugins // let it run through plugins

View File

@@ -8,9 +8,9 @@ import { getBindings } from "./bindings";
export function makeSchema(bindings: string[] = []) { export function makeSchema(bindings: string[] = []) {
return Type.Object( return Type.Object(
{ {
binding: bindings.length > 0 ? StringEnum(bindings) : Type.Optional(Type.String()) binding: bindings.length > 0 ? StringEnum(bindings) : Type.Optional(Type.String()),
}, },
{ title: "R2", description: "Cloudflare R2 storage" } { title: "R2", description: "Cloudflare R2 storage" },
); );
} }
@@ -36,10 +36,10 @@ export function registerMedia(env: Record<string, any>) {
override toJSON() { override toJSON() {
return { return {
...super.toJSON(), ...super.toJSON(),
config: this.config config: this.config,
}; };
} }
} },
); );
} }
@@ -67,13 +67,13 @@ export class StorageR2Adapter implements StorageAdapter {
} }
} }
async listObjects( async listObjects(
prefix?: string prefix?: string,
): Promise<{ key: string; last_modified: Date; size: number }[]> { ): Promise<{ key: string; last_modified: Date; size: number }[]> {
const list = await this.bucket.list({ limit: 50 }); const list = await this.bucket.list({ limit: 50 });
return list.objects.map((item) => ({ return list.objects.map((item) => ({
key: item.key, key: item.key,
size: item.size, size: item.size,
last_modified: item.uploaded last_modified: item.uploaded,
})); }));
} }
@@ -89,7 +89,7 @@ export class StorageR2Adapter implements StorageAdapter {
let object: R2ObjectBody | null; let object: R2ObjectBody | null;
const responseHeaders = new Headers({ const responseHeaders = new Headers({
"Accept-Ranges": "bytes", "Accept-Ranges": "bytes",
"Content-Type": guess(key) "Content-Type": guess(key),
}); });
//console.log("getObject:headers", headersToObject(headers)); //console.log("getObject:headers", headersToObject(headers));
@@ -98,7 +98,7 @@ export class StorageR2Adapter implements StorageAdapter {
? {} // miniflare doesn't support range requests ? {} // miniflare doesn't support range requests
: { : {
range: headers, range: headers,
onlyIf: headers onlyIf: headers,
}; };
object = (await this.bucket.get(key, options)) as R2ObjectBody; object = (await this.bucket.get(key, options)) as R2ObjectBody;
@@ -130,7 +130,7 @@ export class StorageR2Adapter implements StorageAdapter {
return new Response(object.body, { return new Response(object.body, {
status: object.range ? 206 : 200, status: object.range ? 206 : 200,
headers: responseHeaders headers: responseHeaders,
}); });
} }
@@ -139,7 +139,7 @@ export class StorageR2Adapter implements StorageAdapter {
if (!metadata || Object.keys(metadata).length === 0) { if (!metadata || Object.keys(metadata).length === 0) {
// guessing is especially required for dev environment (miniflare) // guessing is especially required for dev environment (miniflare)
metadata = { metadata = {
contentType: guess(object.key) contentType: guess(object.key),
}; };
} }
@@ -157,7 +157,7 @@ export class StorageR2Adapter implements StorageAdapter {
return { return {
type: String(head.httpMetadata?.contentType ?? guess(key)), type: String(head.httpMetadata?.contentType ?? guess(key)),
size: head.size size: head.size,
}; };
} }
@@ -172,7 +172,7 @@ export class StorageR2Adapter implements StorageAdapter {
toJSON(secrets?: boolean) { toJSON(secrets?: boolean) {
return { return {
type: this.getName(), type: this.getName(),
config: {} config: {},
}; };
} }
} }

View File

@@ -15,7 +15,7 @@ export function getBindings<T extends GetBindingType>(env: any, type: T): Bindin
if (env[key] && (env[key] as any).constructor.name === type) { if (env[key] && (env[key] as any).constructor.name === type) {
bindings.push({ bindings.push({
key, key,
value: env[key] as BindingTypeMap[T] value: env[key] as BindingTypeMap[T],
}); });
} }
} catch (e) {} } catch (e) {}

View File

@@ -84,7 +84,7 @@ export function serve<Env = any>(config: CloudflareBkndConfig<Env> = {}) {
hono.all("*", async (c, next) => { hono.all("*", async (c, next) => {
const res = await serveStatic({ const res = await serveStatic({
path: `./${pathname}`, path: `./${pathname}`,
manifest: config.manifest! manifest: config.manifest!,
})(c as any, next); })(c as any, next);
if (res instanceof Response) { if (res instanceof Response) {
const ttl = 60 * 60 * 24 * 365; const ttl = 60 * 60 * 24 * 365;
@@ -114,6 +114,6 @@ export function serve<Env = any>(config: CloudflareBkndConfig<Env> = {}) {
default: default:
throw new Error(`Unknown mode ${mode}`); throw new Error(`Unknown mode ${mode}`);
} }
} },
}; };
} }

View File

@@ -10,7 +10,7 @@ export {
getBindings, getBindings,
type BindingTypeMap, type BindingTypeMap,
type GetBindingType, type GetBindingType,
type BindingMap type BindingMap,
} from "./bindings"; } from "./bindings";
export function d1(config: D1ConnectionConfig) { export function d1(config: D1ConnectionConfig) {

View File

@@ -31,13 +31,13 @@ export async function getCached(config: CloudflareBkndConfig, { env, ctx, ...arg
async ({ params: { app } }) => { async ({ params: { app } }) => {
saveConfig(app.toJSON(true)); saveConfig(app.toJSON(true));
}, },
"sync" "sync",
); );
await config.beforeBuild?.(app); await config.beforeBuild?.(app);
}, },
adminOptions: { html: config.html } adminOptions: { html: config.html },
}, },
{ env, ctx, ...args } { env, ctx, ...args },
); );
if (!cachedConfig) { if (!cachedConfig) {

View File

@@ -23,7 +23,7 @@ export async function getDurable(config: CloudflareBkndConfig, ctx: Context) {
config: create_config, config: create_config,
html: config.html, html: config.html,
keepAliveSeconds: config.keepAliveSeconds, keepAliveSeconds: config.keepAliveSeconds,
setAdminHtml: config.setAdminHtml setAdminHtml: config.setAdminHtml,
}); });
const headers = new Headers(res.headers); const headers = new Headers(res.headers);
@@ -32,7 +32,7 @@ export async function getDurable(config: CloudflareBkndConfig, ctx: Context) {
return new Response(res.body, { return new Response(res.body, {
status: res.status, status: res.status,
statusText: res.statusText, statusText: res.statusText,
headers headers,
}); });
} }
@@ -48,7 +48,7 @@ export class DurableBkndApp extends DurableObject {
html?: string; html?: string;
keepAliveSeconds?: number; keepAliveSeconds?: number;
setAdminHtml?: boolean; setAdminHtml?: boolean;
} },
) { ) {
let buildtime = 0; let buildtime = 0;
if (!this.app) { if (!this.app) {
@@ -73,7 +73,7 @@ export class DurableBkndApp extends DurableObject {
return c.json({ return c.json({
id: this.id, id: this.id,
keepAliveSeconds: options?.keepAliveSeconds ?? 0, keepAliveSeconds: options?.keepAliveSeconds ?? 0,
colo: context.colo colo: context.colo,
}); });
}); });
@@ -82,7 +82,7 @@ export class DurableBkndApp extends DurableObject {
adminOptions: { html: options.html }, adminOptions: { html: options.html },
beforeBuild: async (app) => { beforeBuild: async (app) => {
await this.beforeBuild(app); await this.beforeBuild(app);
} },
}); });
buildtime = performance.now() - start; buildtime = performance.now() - start;
@@ -101,7 +101,7 @@ export class DurableBkndApp extends DurableObject {
return new Response(res.body, { return new Response(res.body, {
status: res.status, status: res.status,
statusText: res.statusText, statusText: res.statusText,
headers headers,
}); });
} }

View File

@@ -6,9 +6,9 @@ export async function makeApp(config: CloudflareBkndConfig, ctx: Context) {
return await createRuntimeApp( return await createRuntimeApp(
{ {
...makeCfConfig(config, ctx), ...makeCfConfig(config, ctx),
adminOptions: config.html ? { html: config.html } : undefined adminOptions: config.html ? { html: config.html } : undefined,
}, },
ctx ctx,
); );
} }

View File

@@ -34,7 +34,7 @@ export function makeConfig<Args = any>(config: BkndConfig<Args>, args?: Args): C
export async function createFrameworkApp<Args = any>( export async function createFrameworkApp<Args = any>(
config: FrameworkBkndConfig, config: FrameworkBkndConfig,
args?: Args args?: Args,
): Promise<App> { ): Promise<App> {
const app = App.create(makeConfig(config, args)); const app = App.create(makeConfig(config, args));
@@ -44,7 +44,7 @@ export async function createFrameworkApp<Args = any>(
async () => { async () => {
await config.onBuilt?.(app); await config.onBuilt?.(app);
}, },
"sync" "sync",
); );
} }
@@ -63,7 +63,7 @@ export async function createRuntimeApp<Env = any>(
serveStatic?: MiddlewareHandler | [string, MiddlewareHandler]; serveStatic?: MiddlewareHandler | [string, MiddlewareHandler];
adminOptions?: AdminControllerOptions | false; adminOptions?: AdminControllerOptions | false;
}, },
env?: Env env?: Env,
): Promise<App> { ): Promise<App> {
const app = App.create(makeConfig(config, env)); const app = App.create(makeConfig(config, env));
@@ -82,7 +82,7 @@ export async function createRuntimeApp<Env = any>(
app.registerAdminController(adminOptions); app.registerAdminController(adminOptions);
} }
}, },
"sync" "sync",
); );
await config.beforeBuild?.(app); await config.beforeBuild?.(app);

View File

@@ -26,7 +26,7 @@ export function createApi({ req }: GetServerSidePropsContext) {
const request = nodeRequestToRequest(req); const request = nodeRequestToRequest(req);
return new Api({ return new Api({
host: new URL(request.url).origin, host: new URL(request.url).origin,
headers: request.headers headers: request.headers,
}); });
} }
@@ -40,7 +40,7 @@ export function withApi<T>(handler: (ctx: GetServerSidePropsContext & { api: Api
function getCleanRequest( function getCleanRequest(
req: Request, req: Request,
{ cleanSearch = ["route"] }: Pick<NextjsBkndConfig, "cleanSearch"> { cleanSearch = ["route"] }: Pick<NextjsBkndConfig, "cleanSearch">,
) { ) {
const url = new URL(req.url); const url = new URL(req.url);
cleanSearch?.forEach((k) => url.searchParams.delete(k)); cleanSearch?.forEach((k) => url.searchParams.delete(k));
@@ -48,7 +48,7 @@ function getCleanRequest(
return new Request(url.toString(), { return new Request(url.toString(), {
method: req.method, method: req.method,
headers: req.headers, headers: req.headers,
body: req.body body: req.body,
}); });
} }

View File

@@ -1,7 +1,7 @@
import { registries } from "bknd"; import { registries } from "bknd";
import { import {
type LocalAdapterConfig, type LocalAdapterConfig,
StorageLocalAdapter StorageLocalAdapter,
} from "../../media/storage/adapters/StorageLocalAdapter"; } from "../../media/storage/adapters/StorageLocalAdapter";
export * from "./node.adapter"; export * from "./node.adapter";

View File

@@ -24,7 +24,7 @@ export function serve({
}: NodeBkndConfig = {}) { }: NodeBkndConfig = {}) {
const root = path.relative( const root = path.relative(
process.cwd(), process.cwd(),
path.resolve(distPath ?? relativeDistPath ?? "./node_modules/bknd/dist", "static") path.resolve(distPath ?? relativeDistPath ?? "./node_modules/bknd/dist", "static"),
); );
if (relativeDistPath) { if (relativeDistPath) {
console.warn("relativeDistPath is deprecated, please use distPath instead"); console.warn("relativeDistPath is deprecated, please use distPath instead");
@@ -41,16 +41,16 @@ export function serve({
registerLocalMediaAdapter(); registerLocalMediaAdapter();
app = await createRuntimeApp({ app = await createRuntimeApp({
...config, ...config,
serveStatic: serveStatic({ root }) serveStatic: serveStatic({ root }),
}); });
} }
return app.fetch(req); return app.fetch(req);
} },
}, },
(connInfo) => { (connInfo) => {
console.log(`Server is running on http://localhost:${connInfo.port}`); console.log(`Server is running on http://localhost:${connInfo.port}`);
listener?.(connInfo); listener?.(connInfo);
} },
); );
} }

View File

@@ -28,7 +28,7 @@ export async function getApp(config: RemixBkndConfig, args?: RemixContext) {
} }
export function serve<Args extends RemixContext = RemixContext>( export function serve<Args extends RemixContext = RemixContext>(
config: RemixBkndConfig<Args> = {} config: RemixBkndConfig<Args> = {},
) { ) {
return async (args: Args) => { return async (args: Args) => {
app = await createFrameworkApp(config, args); app = await createFrameworkApp(config, args);

View File

@@ -20,6 +20,6 @@ export function nodeRequestToRequest(req: IncomingMessage): Request {
const method = req.method || "GET"; const method = req.method || "GET";
return new Request(url, { return new Request(url, {
method, method,
headers headers,
}); });
} }

View File

@@ -8,7 +8,7 @@ export const devServerConfig = {
/^\/@.+$/, /^\/@.+$/,
/\/components.*?\.json.*/, // @todo: improve /\/components.*?\.json.*/, // @todo: improve
/^\/(public|assets|static)\/.+/, /^\/(public|assets|static)\/.+/,
/^\/node_modules\/.*/ /^\/node_modules\/.*/,
] as any, ] as any,
injectClientScript: false injectClientScript: false,
} as const; } as const;

View File

@@ -24,7 +24,7 @@ window.__vite_plugin_react_preamble_installed__ = true
</script> </script>
<script type="module" src="/@vite/client"></script> <script type="module" src="/@vite/client"></script>
${addBkndContext ? "<!-- BKND_CONTEXT -->" : ""} ${addBkndContext ? "<!-- BKND_CONTEXT -->" : ""}
</head>` </head>`,
); );
} }
@@ -39,12 +39,12 @@ async function createApp(config: ViteBkndConfig = {}, env?: any) {
: { : {
html: config.html, html: config.html,
forceDev: config.forceDev ?? { forceDev: config.forceDev ?? {
mainPath: "/src/main.tsx" mainPath: "/src/main.tsx",
}
}, },
serveStatic: ["/assets/*", serveStatic({ root: config.distPath ?? "./" })]
}, },
env serveStatic: ["/assets/*", serveStatic({ root: config.distPath ?? "./" })],
},
env,
); );
} }
@@ -53,7 +53,7 @@ export function serveFresh(config: Omit<ViteBkndConfig, "mode"> = {}) {
async fetch(request: Request, env: any, ctx: ExecutionContext) { async fetch(request: Request, env: any, ctx: ExecutionContext) {
const app = await createApp(config, env); const app = await createApp(config, env);
return app.fetch(request, env, ctx); return app.fetch(request, env, ctx);
} },
}; };
} }
@@ -66,7 +66,7 @@ export function serveCached(config: Omit<ViteBkndConfig, "mode"> = {}) {
} }
return app.fetch(request, env, ctx); return app.fetch(request, env, ctx);
} },
}; };
} }
@@ -77,6 +77,6 @@ export function serve({ mode, ...config }: ViteBkndConfig = {}) {
export function devServer(options: DevServerOptions) { export function devServer(options: DevServerOptions) {
return honoViteDevServer({ return honoViteDevServer({
...devServerConfig, ...devServerConfig,
...options ...options,
}); });
} }

View File

@@ -4,7 +4,7 @@ import {
Authenticator, Authenticator,
type ProfileExchange, type ProfileExchange,
Role, Role,
type Strategy type Strategy,
} from "auth"; } from "auth";
import type { PasswordStrategy } from "auth/authenticate/strategies"; import type { PasswordStrategy } from "auth/authenticate/strategies";
import { type DB, Exception, type PrimaryFieldType } from "core"; import { type DB, Exception, type PrimaryFieldType } from "core";
@@ -68,15 +68,15 @@ export class AppAuth extends Module<typeof authConfigSchema> {
} catch (e) { } catch (e) {
throw new Error( throw new Error(
`Could not build strategy ${String( `Could not build strategy ${String(
name name,
)} with config ${JSON.stringify(strategy.config)}` )} with config ${JSON.stringify(strategy.config)}`,
); );
} }
}); });
this._authenticator = new Authenticator(strategies, this.resolveUser.bind(this), { this._authenticator = new Authenticator(strategies, this.resolveUser.bind(this), {
jwt: this.config.jwt, jwt: this.config.jwt,
cookie: this.config.cookie cookie: this.config.cookie,
}); });
this.registerEntities(); this.registerEntities();
@@ -117,7 +117,7 @@ export class AppAuth extends Module<typeof authConfigSchema> {
action: AuthAction, action: AuthAction,
strategy: Strategy, strategy: Strategy,
identifier: string, identifier: string,
profile: ProfileExchange profile: ProfileExchange,
): Promise<any> { ): Promise<any> {
if (!this.config.allow_register && action === "register") { if (!this.config.allow_register && action === "register") {
throw new Exception("Registration is not allowed", 403); throw new Exception("Registration is not allowed", 403);
@@ -127,7 +127,7 @@ export class AppAuth extends Module<typeof authConfigSchema> {
.getFillableFields("create") .getFillableFields("create")
.map((f) => f.name); .map((f) => f.name);
const filteredProfile = Object.fromEntries( const filteredProfile = Object.fromEntries(
Object.entries(profile).filter(([key]) => fields.includes(key)) Object.entries(profile).filter(([key]) => fields.includes(key)),
); );
switch (action) { switch (action) {
@@ -190,7 +190,7 @@ export class AppAuth extends Module<typeof authConfigSchema> {
const payload: any = { const payload: any = {
...profile, ...profile,
strategy: strategy.getName(), strategy: strategy.getName(),
strategy_value: identifier strategy_value: identifier,
}; };
const mutator = this.em.mutator(users); const mutator = this.em.mutator(users);
@@ -240,13 +240,13 @@ export class AppAuth extends Module<typeof authConfigSchema> {
email: text().required(), email: text().required(),
strategy: text({ strategy: text({
fillable: ["create"], fillable: ["create"],
hidden: ["update", "form"] hidden: ["update", "form"],
}).required(), }).required(),
strategy_value: text({ strategy_value: text({
fillable: ["create"], fillable: ["create"],
hidden: ["read", "table", "update", "form"] hidden: ["read", "table", "update", "form"],
}).required(), }).required(),
role: text() role: text(),
}; };
registerEntities() { registerEntities() {
@@ -254,12 +254,12 @@ export class AppAuth extends Module<typeof authConfigSchema> {
this.ensureSchema( this.ensureSchema(
em( em(
{ {
[users.name as "users"]: users [users.name as "users"]: users,
}, },
({ index }, { users }) => { ({ index }, { users }) => {
index(users).on(["email"], true).on(["strategy"]).on(["strategy_value"]); index(users).on(["email"], true).on(["strategy"]).on(["strategy_value"]);
} },
) ),
); );
try { try {
@@ -288,7 +288,7 @@ export class AppAuth extends Module<typeof authConfigSchema> {
...(additional as any), ...(additional as any),
email, email,
strategy, strategy,
strategy_value strategy_value,
}); });
mutator.__unstable_toggleSystemEntityCreation(true); mutator.__unstable_toggleSystemEntityCreation(true);
return created; return created;
@@ -307,8 +307,8 @@ export class AppAuth extends Module<typeof authConfigSchema> {
strategies: transformObject(strategies, (strategy) => ({ strategies: transformObject(strategies, (strategy) => ({
enabled: this.isStrategyEnabled(strategy), enabled: this.isStrategyEnabled(strategy),
type: strategy.getType(), type: strategy.getType(),
config: strategy.toJSON(secrets) config: strategy.toJSON(secrets),
})) })),
}; };
} }
} }

View File

@@ -10,13 +10,13 @@ export type AuthApiOptions = BaseModuleApiOptions & {
export class AuthApi extends ModuleApi<AuthApiOptions> { export class AuthApi extends ModuleApi<AuthApiOptions> {
protected override getDefaultOptions(): Partial<AuthApiOptions> { protected override getDefaultOptions(): Partial<AuthApiOptions> {
return { return {
basepath: "/api/auth" basepath: "/api/auth",
}; };
} }
async login(strategy: string, input: any) { async login(strategy: string, input: any) {
const res = await this.post<AuthResponse>([strategy, "login"], input, { const res = await this.post<AuthResponse>([strategy, "login"], input, {
credentials: "include" credentials: "include",
}); });
if (res.ok && res.body.token) { if (res.ok && res.body.token) {
@@ -27,7 +27,7 @@ export class AuthApi extends ModuleApi<AuthApiOptions> {
async register(strategy: string, input: any) { async register(strategy: string, input: any) {
const res = await this.post<AuthResponse>([strategy, "register"], input, { const res = await this.post<AuthResponse>([strategy, "register"], input, {
credentials: "include" credentials: "include",
}); });
if (res.ok && res.body.token) { if (res.ok && res.body.token) {

View File

@@ -58,7 +58,7 @@ export class AuthController extends Controller {
try { try {
const body = await this.auth.authenticator.getBody(c); const body = await this.auth.authenticator.getBody(c);
const valid = parse(create.schema, body, { const valid = parse(create.schema, body, {
skipMark: true skipMark: true,
}); });
const processed = (await create.preprocess?.(valid)) ?? valid; const processed = (await create.preprocess?.(valid)) ?? valid;
@@ -67,7 +67,7 @@ export class AuthController extends Controller {
mutator.__unstable_toggleSystemEntityCreation(false); mutator.__unstable_toggleSystemEntityCreation(false);
const { data: created } = await mutator.insertOne({ const { data: created } = await mutator.insertOne({
...processed, ...processed,
strategy: name strategy: name,
}); });
mutator.__unstable_toggleSystemEntityCreation(true); mutator.__unstable_toggleSystemEntityCreation(true);
@@ -75,21 +75,21 @@ export class AuthController extends Controller {
success: true, success: true,
action: "create", action: "create",
strategy: name, strategy: name,
data: created as unknown as SafeUser data: created as unknown as SafeUser,
} as AuthActionResponse); } as AuthActionResponse);
} catch (e) { } catch (e) {
if (e instanceof TypeInvalidError) { if (e instanceof TypeInvalidError) {
return c.json( return c.json(
{ {
success: false, success: false,
errors: e.errors errors: e.errors,
}, },
400 400,
); );
} }
throw e; throw e;
} }
} },
); );
hono.get("create/schema.json", async (c) => { hono.get("create/schema.json", async (c) => {
return c.json(create.schema); return c.json(create.schema);
@@ -147,12 +147,12 @@ export class AuthController extends Controller {
strategies: transformObject(strategies ?? {}, (strategy, name) => { strategies: transformObject(strategies ?? {}, (strategy, name) => {
return this.auth.isStrategyEnabled(name) ? strategy : undefined; return this.auth.isStrategyEnabled(name) ? strategy : undefined;
}), }),
basepath basepath,
}); });
} }
return c.json({ strategies, basepath }); return c.json({ strategies, basepath });
} },
); );
return hono.all("*", (c) => c.notFound()); return hono.all("*", (c) => c.notFound());

View File

@@ -5,16 +5,16 @@ import { type Static, StringRecord, Type, objectTransform } from "core/utils";
export const Strategies = { export const Strategies = {
password: { password: {
cls: PasswordStrategy, cls: PasswordStrategy,
schema: PasswordStrategy.prototype.getSchema() schema: PasswordStrategy.prototype.getSchema(),
}, },
oauth: { oauth: {
cls: OAuthStrategy, cls: OAuthStrategy,
schema: OAuthStrategy.prototype.getSchema() schema: OAuthStrategy.prototype.getSchema(),
}, },
custom_oauth: { custom_oauth: {
cls: CustomOAuthStrategy, cls: CustomOAuthStrategy,
schema: CustomOAuthStrategy.prototype.getSchema() schema: CustomOAuthStrategy.prototype.getSchema(),
} },
} as const; } as const;
export const STRATEGIES = Strategies; export const STRATEGIES = Strategies;
@@ -23,12 +23,12 @@ const strategiesSchemaObject = objectTransform(STRATEGIES, (strategy, name) => {
{ {
enabled: Type.Optional(Type.Boolean({ default: true })), enabled: Type.Optional(Type.Boolean({ default: true })),
type: Type.Const(name, { default: name, readOnly: true }), type: Type.Const(name, { default: name, readOnly: true }),
config: strategy.schema config: strategy.schema,
}, },
{ {
title: name, title: name,
additionalProperties: false additionalProperties: false,
} },
); );
}); });
const strategiesSchema = Type.Union(Object.values(strategiesSchemaObject)); const strategiesSchema = Type.Union(Object.values(strategiesSchemaObject));
@@ -37,15 +37,15 @@ export type AppAuthOAuthStrategy = Static<typeof STRATEGIES.oauth.schema>;
export type AppAuthCustomOAuthStrategy = Static<typeof STRATEGIES.custom_oauth.schema>; export type AppAuthCustomOAuthStrategy = Static<typeof STRATEGIES.custom_oauth.schema>;
const guardConfigSchema = Type.Object({ const guardConfigSchema = Type.Object({
enabled: Type.Optional(Type.Boolean({ default: false })) enabled: Type.Optional(Type.Boolean({ default: false })),
}); });
export const guardRoleSchema = Type.Object( export const guardRoleSchema = Type.Object(
{ {
permissions: Type.Optional(Type.Array(Type.String())), permissions: Type.Optional(Type.Array(Type.String())),
is_default: Type.Optional(Type.Boolean()), is_default: Type.Optional(Type.Boolean()),
implicit_allow: Type.Optional(Type.Boolean()) implicit_allow: Type.Optional(Type.Boolean()),
}, },
{ additionalProperties: false } { additionalProperties: false },
); );
export const authConfigSchema = Type.Object( export const authConfigSchema = Type.Object(
@@ -64,19 +64,19 @@ export const authConfigSchema = Type.Object(
type: "password", type: "password",
enabled: true, enabled: true,
config: { config: {
hashing: "sha256" hashing: "sha256",
} },
} },
} },
}) }),
), ),
guard: Type.Optional(guardConfigSchema), guard: Type.Optional(guardConfigSchema),
roles: Type.Optional(StringRecord(guardRoleSchema, { default: {} })) roles: Type.Optional(StringRecord(guardRoleSchema, { default: {} })),
}, },
{ {
title: "Authentication", title: "Authentication",
additionalProperties: false additionalProperties: false,
} },
); );
export type AppAuthSchema = Static<typeof authConfigSchema>; export type AppAuthSchema = Static<typeof authConfigSchema>;

View File

@@ -7,7 +7,7 @@ import {
Type, Type,
parse, parse,
runtimeSupports, runtimeSupports,
transformObject transformObject,
} from "core/utils"; } from "core/utils";
import type { Context, Hono } from "hono"; import type { Context, Hono } from "hono";
import { deleteCookie, getSignedCookie, setSignedCookie } from "hono/cookie"; import { deleteCookie, getSignedCookie, setSignedCookie } from "hono/cookie";
@@ -71,9 +71,9 @@ export const cookieConfig = Type.Partial(
expires: Type.Number({ default: defaultCookieExpires }), // seconds expires: Type.Number({ default: defaultCookieExpires }), // seconds
renew: Type.Boolean({ default: true }), renew: Type.Boolean({ default: true }),
pathSuccess: Type.String({ default: "/" }), pathSuccess: Type.String({ default: "/" }),
pathLoggedOut: Type.String({ default: "/" }) pathLoggedOut: Type.String({ default: "/" }),
}), }),
{ default: {}, additionalProperties: false } { default: {}, additionalProperties: false },
); );
// @todo: maybe add a config to not allow cookie/api tokens to be used interchangably? // @todo: maybe add a config to not allow cookie/api tokens to be used interchangably?
@@ -86,16 +86,16 @@ export const jwtConfig = Type.Object(
alg: Type.Optional(StringEnum(["HS256", "HS384", "HS512"], { default: "HS256" })), alg: Type.Optional(StringEnum(["HS256", "HS384", "HS512"], { default: "HS256" })),
expires: Type.Optional(Type.Number()), // seconds expires: Type.Optional(Type.Number()), // seconds
issuer: Type.Optional(Type.String()), issuer: Type.Optional(Type.String()),
fields: Type.Array(Type.String(), { default: ["id", "email", "role"] }) fields: Type.Array(Type.String(), { default: ["id", "email", "role"] }),
}, },
{ {
default: {}, default: {},
additionalProperties: false additionalProperties: false,
} },
); );
export const authenticatorConfig = Type.Object({ export const authenticatorConfig = Type.Object({
jwt: jwtConfig, jwt: jwtConfig,
cookie: cookieConfig cookie: cookieConfig,
}); });
type AuthConfig = Static<typeof authenticatorConfig>; type AuthConfig = Static<typeof authenticatorConfig>;
@@ -104,7 +104,7 @@ export type AuthUserResolver = (
action: AuthAction, action: AuthAction,
strategy: Strategy, strategy: Strategy,
identifier: string, identifier: string,
profile: ProfileExchange profile: ProfileExchange,
) => Promise<SafeUser | undefined>; ) => Promise<SafeUser | undefined>;
type AuthClaims = SafeUser & { type AuthClaims = SafeUser & {
iat: number; iat: number;
@@ -127,7 +127,7 @@ export class Authenticator<Strategies extends Record<string, Strategy> = Record<
action: AuthAction, action: AuthAction,
strategy: Strategy, strategy: Strategy,
identifier: string, identifier: string,
profile: ProfileExchange profile: ProfileExchange,
): Promise<AuthResponse> { ): Promise<AuthResponse> {
//console.log("resolve", { action, strategy: strategy.getName(), profile }); //console.log("resolve", { action, strategy: strategy.getName(), profile });
const user = await this.userResolver(action, strategy, identifier, profile); const user = await this.userResolver(action, strategy, identifier, profile);
@@ -135,7 +135,7 @@ export class Authenticator<Strategies extends Record<string, Strategy> = Record<
if (user) { if (user) {
return { return {
user, user,
token: await this.jwt(user) token: await this.jwt(user),
}; };
} }
@@ -148,7 +148,7 @@ export class Authenticator<Strategies extends Record<string, Strategy> = Record<
strategy< strategy<
StrategyName extends keyof Strategies, StrategyName extends keyof Strategies,
Strat extends Strategy = Strategies[StrategyName] Strat extends Strategy = Strategies[StrategyName],
>(strategy: StrategyName): Strat { >(strategy: StrategyName): Strat {
try { try {
return this.strategies[strategy] as unknown as Strat; return this.strategies[strategy] as unknown as Strat;
@@ -168,7 +168,7 @@ export class Authenticator<Strategies extends Record<string, Strategy> = Record<
const payload: JWTPayload = { const payload: JWTPayload = {
...user, ...user,
iat: Math.floor(Date.now() / 1000) iat: Math.floor(Date.now() / 1000),
}; };
// issuer // issuer
@@ -194,7 +194,7 @@ export class Authenticator<Strategies extends Record<string, Strategy> = Record<
const payload = await verify( const payload = await verify(
jwt, jwt,
this.config.jwt?.secret ?? "", this.config.jwt?.secret ?? "",
this.config.jwt?.alg ?? "HS256" this.config.jwt?.alg ?? "HS256",
); );
// manually verify issuer (hono doesn't support it) // manually verify issuer (hono doesn't support it)
@@ -215,7 +215,7 @@ export class Authenticator<Strategies extends Record<string, Strategy> = Record<
return { return {
...cookieConfig, ...cookieConfig,
expires: new Date(Date.now() + expires * 1000) expires: new Date(Date.now() + expires * 1000),
}; };
} }
@@ -342,17 +342,17 @@ export class Authenticator<Strategies extends Record<string, Strategy> = Record<
toJSON(secrets?: boolean) { toJSON(secrets?: boolean) {
return { return {
...this.config, ...this.config,
jwt: secrets ? this.config.jwt : undefined jwt: secrets ? this.config.jwt : undefined,
}; };
} }
} }
export function createStrategyAction<S extends TObject>( export function createStrategyAction<S extends TObject>(
schema: S, schema: S,
preprocess: (input: Static<S>) => Promise<Partial<DB["users"]>> preprocess: (input: Static<S>) => Promise<Partial<DB["users"]>>,
) { ) {
return { return {
schema, schema,
preprocess preprocess,
} as StrategyAction<S>; } as StrategyAction<S>;
} }

View File

@@ -9,7 +9,7 @@ type LoginSchema = { username: string; password: string } | { email: string; pas
type RegisterSchema = { email: string; password: string; [key: string]: any }; type RegisterSchema = { email: string; password: string; [key: string]: any };
const schema = Type.Object({ const schema = Type.Object({
hashing: StringEnum(["plain", "sha256" /*, "bcrypt"*/] as const, { default: "sha256" }) hashing: StringEnum(["plain", "sha256" /*, "bcrypt"*/] as const, { default: "sha256" }),
}); });
export type PasswordStrategyOptions = Static<typeof schema>; export type PasswordStrategyOptions = Static<typeof schema>;
@@ -49,7 +49,7 @@ export class PasswordStrategy implements Strategy {
return { return {
...input, ...input,
password: await this.hash(input.password) password: await this.hash(input.password),
}; };
} }
@@ -62,8 +62,8 @@ export class PasswordStrategy implements Strategy {
tb( tb(
"query", "query",
Type.Object({ Type.Object({
redirect: Type.Optional(Type.String()) redirect: Type.Optional(Type.String()),
}) }),
), ),
async (c) => { async (c) => {
const body = await authenticator.getBody(c); const body = await authenticator.getBody(c);
@@ -75,22 +75,22 @@ export class PasswordStrategy implements Strategy {
"login", "login",
this, this,
payload.password, payload.password,
payload payload,
); );
return await authenticator.respond(c, data, redirect); return await authenticator.respond(c, data, redirect);
} catch (e) { } catch (e) {
return await authenticator.respond(c, e); return await authenticator.respond(c, e);
} }
} },
) )
.post( .post(
"/register", "/register",
tb( tb(
"query", "query",
Type.Object({ Type.Object({
redirect: Type.Optional(Type.String()) redirect: Type.Optional(Type.String()),
}) }),
), ),
async (c) => { async (c) => {
const body = await authenticator.getBody(c); const body = await authenticator.getBody(c);
@@ -101,11 +101,11 @@ export class PasswordStrategy implements Strategy {
"register", "register",
this, this,
payload.password, payload.password,
payload payload,
); );
return await authenticator.respond(c, data, redirect); return await authenticator.respond(c, data, redirect);
} },
); );
} }
@@ -114,19 +114,19 @@ export class PasswordStrategy implements Strategy {
create: createStrategyAction( create: createStrategyAction(
Type.Object({ Type.Object({
email: Type.String({ email: Type.String({
pattern: "^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$" pattern: "^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$",
}), }),
password: Type.String({ password: Type.String({
minLength: 8 // @todo: this should be configurable minLength: 8, // @todo: this should be configurable
}) }),
}), }),
async ({ password, ...input }) => { async ({ password, ...input }) => {
return { return {
...input, ...input,
strategy_value: await this.hash(password) strategy_value: await this.hash(password),
}; };
} },
) ),
}; };
} }

View File

@@ -9,5 +9,5 @@ export {
type PasswordStrategyOptions, type PasswordStrategyOptions,
OAuthStrategy, OAuthStrategy,
OAuthCallbackException, OAuthCallbackException,
CustomOAuthStrategy CustomOAuthStrategy,
}; };

View File

@@ -15,11 +15,11 @@ const oauthSchemaCustom = Type.Object(
{ {
client_id: Type.String(), client_id: Type.String(),
client_secret: Type.String(), client_secret: Type.String(),
token_endpoint_auth_method: StringEnum(["client_secret_basic"]) token_endpoint_auth_method: StringEnum(["client_secret_basic"]),
}, },
{ {
additionalProperties: false additionalProperties: false,
} },
), ),
as: Type.Object( as: Type.Object(
{ {
@@ -29,15 +29,15 @@ const oauthSchemaCustom = Type.Object(
scope_separator: Type.Optional(Type.String({ default: " " })), scope_separator: Type.Optional(Type.String({ default: " " })),
authorization_endpoint: Type.Optional(UrlString), authorization_endpoint: Type.Optional(UrlString),
token_endpoint: Type.Optional(UrlString), token_endpoint: Type.Optional(UrlString),
userinfo_endpoint: Type.Optional(UrlString) userinfo_endpoint: Type.Optional(UrlString),
}, },
{ {
additionalProperties: false additionalProperties: false,
} },
) ),
// @todo: profile mapping // @todo: profile mapping
}, },
{ title: "Custom OAuth", additionalProperties: false } { title: "Custom OAuth", additionalProperties: false },
); );
type OAuthConfigCustom = Static<typeof oauthSchemaCustom>; type OAuthConfigCustom = Static<typeof oauthSchemaCustom>;
@@ -57,7 +57,7 @@ export type IssuerConfig<UserInfo = any> = {
profile: ( profile: (
info: UserInfo, info: UserInfo,
config: Omit<IssuerConfig, "profile">, config: Omit<IssuerConfig, "profile">,
tokenResponse: any tokenResponse: any,
) => Promise<UserProfile>; ) => Promise<UserProfile>;
}; };

View File

@@ -18,14 +18,14 @@ const schemaProvided = Type.Object(
client: Type.Object( client: Type.Object(
{ {
client_id: Type.String(), client_id: Type.String(),
client_secret: Type.String() client_secret: Type.String(),
}, },
{ {
additionalProperties: false additionalProperties: false,
}
)
}, },
{ title: "OAuth" } ),
},
{ title: "OAuth" },
); );
type ProvidedOAuthConfig = Static<typeof schemaProvided>; type ProvidedOAuthConfig = Static<typeof schemaProvided>;
@@ -56,7 +56,7 @@ export type IssuerConfig<UserInfo = any> = {
profile: ( profile: (
info: UserInfo, info: UserInfo,
config: Omit<IssuerConfig, "profile">, config: Omit<IssuerConfig, "profile">,
tokenResponse: any tokenResponse: any,
) => Promise<UserProfile>; ) => Promise<UserProfile>;
}; };
@@ -65,7 +65,7 @@ export class OAuthCallbackException extends Exception {
constructor( constructor(
public error: any, public error: any,
public step: string public step: string,
) { ) {
super("OAuthCallbackException on " + step); super("OAuthCallbackException on " + step);
} }
@@ -103,8 +103,8 @@ export class OAuthStrategy implements Strategy {
type: info.type, type: info.type,
client: { client: {
...info.client, ...info.client,
...this._config.client ...this._config.client,
} },
}; };
} }
@@ -129,7 +129,7 @@ export class OAuthStrategy implements Strategy {
const { challenge_supported, challenge, challenge_method } = await this.getCodeChallenge( const { challenge_supported, challenge, challenge_method } = await this.getCodeChallenge(
as, as,
options.state options.state,
); );
if (!as.authorization_endpoint) { if (!as.authorization_endpoint) {
@@ -150,7 +150,7 @@ export class OAuthStrategy implements Strategy {
client_id: client.client_id, client_id: client.client_id,
redirect_uri: options.redirect_uri, redirect_uri: options.redirect_uri,
response_type: "code", response_type: "code",
scope: scopes.join(as.scope_separator ?? " ") scope: scopes.join(as.scope_separator ?? " "),
}; };
if (challenge_supported) { if (challenge_supported) {
params.code_challenge = challenge; params.code_challenge = challenge;
@@ -162,13 +162,13 @@ export class OAuthStrategy implements Strategy {
return { return {
url: new URL(endpoint) + "?" + new URLSearchParams(params).toString(), url: new URL(endpoint) + "?" + new URLSearchParams(params).toString(),
endpoint, endpoint,
params params,
}; };
} }
private async oidc( private async oidc(
callbackParams: URL | URLSearchParams, callbackParams: URL | URLSearchParams,
options: { redirect_uri: string; state: string; scopes?: string[] } options: { redirect_uri: string; state: string; scopes?: string[] },
) { ) {
const config = await this.getConfig(); const config = await this.getConfig();
const { client, as, type } = config; const { client, as, type } = config;
@@ -178,7 +178,7 @@ export class OAuthStrategy implements Strategy {
as, as,
client, // no client_secret required client, // no client_secret required
callbackParams, callbackParams,
oauth.expectNoState oauth.expectNoState,
); );
if (oauth.isOAuth2Error(parameters)) { if (oauth.isOAuth2Error(parameters)) {
//console.log("callback.error", parameters); //console.log("callback.error", parameters);
@@ -193,7 +193,7 @@ export class OAuthStrategy implements Strategy {
client, client,
parameters, parameters,
options.redirect_uri, options.redirect_uri,
options.state options.state,
); );
//console.log("callback.response", response); //console.log("callback.response", response);
@@ -213,7 +213,7 @@ export class OAuthStrategy implements Strategy {
as, as,
client, client,
response, response,
expectedNonce expectedNonce,
); );
if (oauth.isOAuth2Error(result)) { if (oauth.isOAuth2Error(result)) {
console.log("callback.error", result); console.log("callback.error", result);
@@ -236,7 +236,7 @@ export class OAuthStrategy implements Strategy {
private async oauth2( private async oauth2(
callbackParams: URL | URLSearchParams, callbackParams: URL | URLSearchParams,
options: { redirect_uri: string; state: string; scopes?: string[] } options: { redirect_uri: string; state: string; scopes?: string[] },
) { ) {
const config = await this.getConfig(); const config = await this.getConfig();
const { client, type, as, profile } = config; const { client, type, as, profile } = config;
@@ -246,7 +246,7 @@ export class OAuthStrategy implements Strategy {
as, as,
client, // no client_secret required client, // no client_secret required
callbackParams, callbackParams,
oauth.expectNoState oauth.expectNoState,
); );
if (oauth.isOAuth2Error(parameters)) { if (oauth.isOAuth2Error(parameters)) {
console.log("callback.error", parameters); console.log("callback.error", parameters);
@@ -254,14 +254,14 @@ export class OAuthStrategy implements Strategy {
} }
console.log( console.log(
"callback.parameters", "callback.parameters",
JSON.stringify(Object.fromEntries(parameters.entries()), null, 2) JSON.stringify(Object.fromEntries(parameters.entries()), null, 2),
); );
const response = await oauth.authorizationCodeGrantRequest( const response = await oauth.authorizationCodeGrantRequest(
as, as,
client, client,
parameters, parameters,
options.redirect_uri, options.redirect_uri,
options.state options.state,
); );
const challenges = oauth.parseWwwAuthenticateChallenges(response); const challenges = oauth.parseWwwAuthenticateChallenges(response);
@@ -297,7 +297,7 @@ export class OAuthStrategy implements Strategy {
async callback( async callback(
callbackParams: URL | URLSearchParams, callbackParams: URL | URLSearchParams,
options: { redirect_uri: string; state: string; scopes?: string[] } options: { redirect_uri: string; state: string; scopes?: string[] },
): Promise<UserProfile> { ): Promise<UserProfile> {
const type = this.getIssuerConfig().type; const type = this.getIssuerConfig().type;
@@ -330,7 +330,7 @@ export class OAuthStrategy implements Strategy {
secure: true, secure: true,
httpOnly: true, httpOnly: true,
sameSite: "Lax", sameSite: "Lax",
maxAge: 60 * 5 // 5 minutes maxAge: 60 * 5, // 5 minutes
}); });
}; };
@@ -339,7 +339,7 @@ export class OAuthStrategy implements Strategy {
return { return {
state: c.req.header("X-State-Challenge"), state: c.req.header("X-State-Challenge"),
action: c.req.header("X-State-Action"), action: c.req.header("X-State-Action"),
mode: "token" mode: "token",
} as any; } as any;
} }
@@ -366,7 +366,7 @@ export class OAuthStrategy implements Strategy {
const profile = await this.callback(params, { const profile = await this.callback(params, {
redirect_uri, redirect_uri,
state: state.state state: state.state,
}); });
try { try {
@@ -392,7 +392,7 @@ export class OAuthStrategy implements Strategy {
const params = new URLSearchParams(url.search); const params = new URLSearchParams(url.search);
return c.json({ return c.json({
code: params.get("code") ?? null code: params.get("code") ?? null,
}); });
}); });
@@ -410,7 +410,7 @@ export class OAuthStrategy implements Strategy {
const state = oauth.generateRandomCodeVerifier(); const state = oauth.generateRandomCodeVerifier();
const response = await this.request({ const response = await this.request({
redirect_uri, redirect_uri,
state state,
}); });
//console.log("_state", state); //console.log("_state", state);
@@ -433,7 +433,7 @@ export class OAuthStrategy implements Strategy {
const state = oauth.generateRandomCodeVerifier(); const state = oauth.generateRandomCodeVerifier();
const response = await this.request({ const response = await this.request({
redirect_uri, redirect_uri,
state state,
}); });
if (isDebug()) { if (isDebug()) {
@@ -442,14 +442,14 @@ export class OAuthStrategy implements Strategy {
redirect_uri, redirect_uri,
challenge: state, challenge: state,
action, action,
params: response.params params: response.params,
}); });
} }
return c.json({ return c.json({
url: response.url, url: response.url,
challenge: state, challenge: state,
action action,
}); });
}); });
@@ -477,7 +477,7 @@ export class OAuthStrategy implements Strategy {
return { return {
type: this.getIssuerConfig().type, type: this.getIssuerConfig().type,
...config ...config,
}; };
} }
} }

View File

@@ -37,7 +37,7 @@ export class Guard {
implicit_allow?: boolean; implicit_allow?: boolean;
} }
>, >,
config?: GuardConfig config?: GuardConfig,
) { ) {
const _roles = roles const _roles = roles
? objectTransform(roles, ({ permissions = [], is_default, implicit_allow }, name) => { ? objectTransform(roles, ({ permissions = [], is_default, implicit_allow }, name) => {
@@ -103,7 +103,7 @@ export class Guard {
debug && debug &&
console.log("guard: role not found", { console.log("guard: role not found", {
user: user, user: user,
role: user?.role role: user?.role,
}); });
return this.getDefaultRole(); return this.getDefaultRole();
} }
@@ -141,14 +141,14 @@ export class Guard {
} }
const rolePermission = role.permissions.find( const rolePermission = role.permissions.find(
(rolePermission) => rolePermission.permission.name === name (rolePermission) => rolePermission.permission.name === name,
); );
debug && debug &&
console.log("guard: rolePermission, allowing?", { console.log("guard: rolePermission, allowing?", {
permission: name, permission: name,
role: role.name, role: role.name,
allowing: !!rolePermission allowing: !!rolePermission,
}); });
return !!rolePermission; return !!rolePermission;
} }
@@ -162,7 +162,7 @@ export class Guard {
if (!this.granted(permission, c)) { if (!this.granted(permission, c)) {
throw new Exception( throw new Exception(
`Permission "${typeof permission === "string" ? permission : permission.name}" not granted`, `Permission "${typeof permission === "string" ? permission : permission.name}" not granted`,
403 403,
); );
} }
} }

View File

@@ -3,7 +3,7 @@ import { Permission } from "core";
export class RolePermission { export class RolePermission {
constructor( constructor(
public permission: Permission, public permission: Permission,
public config?: any public config?: any,
) {} ) {}
} }
@@ -12,20 +12,20 @@ export class Role {
public name: string, public name: string,
public permissions: RolePermission[] = [], public permissions: RolePermission[] = [],
public is_default: boolean = false, public is_default: boolean = false,
public implicit_allow: boolean = false public implicit_allow: boolean = false,
) {} ) {}
static createWithPermissionNames( static createWithPermissionNames(
name: string, name: string,
permissionNames: string[], permissionNames: string[],
is_default: boolean = false, is_default: boolean = false,
implicit_allow: boolean = false implicit_allow: boolean = false,
) { ) {
return new Role( return new Role(
name, name,
permissionNames.map((name) => new RolePermission(new Permission(name))), permissionNames.map((name) => new RolePermission(new Permission(name))),
is_default, is_default,
implicit_allow implicit_allow,
); );
} }
@@ -39,7 +39,7 @@ export class Role {
config.name, config.name,
config.permissions?.map((name) => new RolePermission(new Permission(name))) ?? [], config.permissions?.map((name) => new RolePermission(new Permission(name))) ?? [],
config.is_default, config.is_default,
config.implicit_allow config.implicit_allow,
); );
} }
} }

Some files were not shown because too many files have changed in this diff Show More