Merge pull request 'Release/0.21' (#6) from release/0.21 into main
Run Tests / test (push) Successful in 3m53s

Reviewed-on: shishantbiswas/bknd#6
This commit was merged in pull request #6.
This commit is contained in:
2026-09-26 13:17:00 +00:00
106 changed files with 4015 additions and 145 deletions
+6
View File
@@ -70,4 +70,10 @@ describe("Api", async () => {
expect(params.token_transport).toBe("header");
expect(params.host).toBe("http://another.com");
});
it("should extract tokens case insensitive", async () => {
const token = await sign({ sub: "test" }, "1234");
expect(new Api({ headers: new Headers({ Authorization: `Bearer ${token}` }) }).getAuthState().token).toBe(token);
expect(new Api({ headers: new Headers({ Authorization: `bearer ${token}` }) }).getAuthState().token).toBe(token);
})
});
+23
View File
@@ -38,4 +38,27 @@ describe("Authenticator", async () => {
expect(cookie).toStartWith("auth=");
expect(cookie).toEndWith("HttpOnly; Secure; SameSite=Strict");
});
test("bearer token is case insensitive", async () => {
const auth = new Authenticator({}, null as any, {
jwt: {
secret: "secret",
fields: ["sub"],
},
cookie: {
sameSite: "strict",
},
});
const token = await auth.jwt({ sub: "test" });
const res = await auth.resolveAuthFromRequest(new Headers({
Authorization: `Bearer ${token}`,
}));
expect((res as any).sub).toBe("test")
const res2 = await auth.resolveAuthFromRequest(new Headers({
Authorization: `bearer ${token}`,
}));
expect((res2 as any).sub).toBe("test")
})
});
+2 -2
View File
@@ -48,12 +48,12 @@ describe("postgres", () => {
if (!(await isPostgresRunning())) {
await $`docker run --rm --name bknd-test-postgres -d -e POSTGRES_PASSWORD=${credentials.password} -e POSTGRES_USER=${credentials.user} -e POSTGRES_DB=${credentials.database} -p ${credentials.port}:5432 postgres:17`;
await $waitUntil("Postgres is running", isPostgresRunning);
await $waitUntil("Postgres is running", isPostgresRunning, 500, 20);
await new Promise((resolve) => setTimeout(resolve, 500));
}
disableConsoleLog();
});
}, 30000);
afterAll(async () => {
if (await isPostgresRunning()) {
try {
+1 -1
View File
@@ -149,7 +149,7 @@ describe("AppAuth", () => {
});
await app.build();
app.registerAdminController();
app.registerAdminController({ forceDev: true });
const spy = spyOn(app.module.auth.authenticator, "requestCookieRefresh");
// register custom route
@@ -7,7 +7,9 @@ import v7 from "./samples/v7.json";
import v8 from "./samples/v8.json";
import v8_2 from "./samples/v8-2.json";
import v9 from "./samples/v9.json";
import v10 from "./samples/v10.json";
import { disableConsoleLog, enableConsoleLog } from "core/utils/test";
import { CURRENT_VERSION } from "modules/db/migrations";
beforeAll(() => disableConsoleLog());
afterAll(enableConsoleLog);
@@ -61,7 +63,7 @@ async function getRawConfig(
return await db
.selectFrom("__bknd")
.selectAll()
.$if(!!opts?.version, (qb) => qb.where("version", "=", opts?.version))
.where("version", "=", opts?.version ?? CURRENT_VERSION)
.$if((opts?.types?.length ?? 0) > 0, (qb) => qb.where("type", "in", opts?.types))
.execute();
}
@@ -115,7 +117,6 @@ describe("Migrations", () => {
"^^s3.secret_access_key^^",
);
const [config, secrets] = (await getRawConfig(app, {
version: 10,
types: ["config", "secrets"],
})) as any;
@@ -129,4 +130,15 @@ describe("Migrations", () => {
"^^s3.secret_access_key^^",
);
});
test("migration from 10 to 11", async () => {
expect(v10.version).toBe(10);
expect(v10.data.entities.test.fields.title.config.fillable).toEqual(["read", "update"]);
const app = await createVersionedApp(v10);
expect(app.version()).toBeGreaterThan(10);
const [config] = (await getRawConfig(app, { types: ["config"] })) as any;
expect(config.json.data.entities.test.fields.title.config.fillable).toEqual(true);
});
});
@@ -0,0 +1,270 @@
{
"version": 10,
"server": {
"cors": {
"origin": "*",
"allow_methods": ["GET", "POST", "PATCH", "PUT", "DELETE"],
"allow_headers": [
"Content-Type",
"Content-Length",
"Authorization",
"Accept"
],
"allow_credentials": true
},
"mcp": {
"enabled": true,
"path": "/api/system/mcp",
"logLevel": "warning"
}
},
"data": {
"basepath": "/api/data",
"default_primary_format": "integer",
"entities": {
"test": {
"type": "regular",
"fields": {
"id": {
"type": "primary",
"config": {
"format": "integer",
"fillable": false,
"required": false
}
},
"title": {
"type": "text",
"config": {
"required": false,
"fillable": ["read", "update"]
}
},
"status": {
"type": "enum",
"config": {
"default_value": "INACTIVE",
"options": {
"type": "strings",
"values": ["INACTIVE", "SUBSCRIBED", "UNSUBSCRIBED"]
},
"required": true,
"fillable": true
}
},
"created_at": {
"type": "date",
"config": {
"type": "datetime",
"required": true,
"fillable": true
}
},
"schema": {
"type": "jsonschema",
"config": {
"default_from_schema": true,
"schema": {
"type": "object",
"properties": {
"one": {
"type": "number",
"default": 1
}
}
},
"required": true,
"fillable": true
}
},
"text": {
"type": "text",
"config": {
"required": false,
"fillable": true
}
}
},
"config": {
"sort_field": "id",
"sort_dir": "asc"
}
},
"items": {
"type": "regular",
"fields": {
"id": {
"type": "primary",
"config": {
"format": "integer",
"fillable": false,
"required": false
}
},
"title": {
"type": "text",
"config": {
"required": false,
"fillable": true
}
}
},
"config": {
"sort_field": "id",
"sort_dir": "asc"
}
},
"media": {
"type": "system",
"fields": {
"id": {
"type": "primary",
"config": {
"format": "integer",
"fillable": false,
"required": false
}
},
"path": {
"type": "text",
"config": {
"required": true,
"fillable": true
}
},
"folder": {
"type": "boolean",
"config": {
"default_value": false,
"hidden": true,
"fillable": ["create"],
"required": false
}
},
"mime_type": {
"type": "text",
"config": {
"required": false,
"fillable": true
}
},
"size": {
"type": "number",
"config": {
"required": false,
"fillable": true
}
},
"scope": {
"type": "text",
"config": {
"hidden": true,
"fillable": ["create"],
"required": false
}
},
"etag": {
"type": "text",
"config": {
"required": false,
"fillable": true
}
},
"modified_at": {
"type": "date",
"config": {
"type": "datetime",
"required": false,
"fillable": true
}
},
"reference": {
"type": "text",
"config": {
"required": false,
"fillable": true
}
},
"entity_id": {
"type": "text",
"config": {
"required": false,
"fillable": true
}
},
"metadata": {
"type": "json",
"config": {
"required": false,
"fillable": true
}
}
},
"config": {
"sort_field": "id",
"sort_dir": "asc"
}
}
},
"relations": {},
"indices": {
"idx_unique_media_path": {
"entity": "media",
"fields": ["path"],
"unique": true
},
"idx_media_reference": {
"entity": "media",
"fields": ["reference"],
"unique": false
},
"idx_media_entity_id": {
"entity": "media",
"fields": ["entity_id"],
"unique": false
}
}
},
"auth": {
"enabled": false,
"basepath": "/api/auth",
"entity_name": "users",
"allow_register": true,
"jwt": {
"secret": "",
"alg": "HS256",
"fields": ["id", "email", "role"]
},
"cookie": {
"path": "/",
"sameSite": "lax",
"secure": true,
"httpOnly": true,
"expires": 604800,
"partitioned": false,
"renew": true,
"pathSuccess": "/",
"pathLoggedOut": "/"
},
"strategies": {
"password": {
"type": "password",
"enabled": true,
"config": {
"hashing": "sha256"
}
}
},
"guard": {
"enabled": false
},
"roles": {}
},
"media": {
"enabled": false
},
"flows": {
"basepath": "/api/flows",
"flows": {}
}
}
@@ -206,8 +206,7 @@ describe("AppReduced", () => {
describe("withBasePath - double slash fix (admin_basepath with trailing slash)", () => {
it("should not produce double slashes when admin_basepath has trailing slash", () => {
const options: BkndAdminProps["config"] = {
basepath: "/",
admin_basepath: "/admin/",
basepath: "/admin",
logo_return_path: "/",
};
@@ -220,8 +219,7 @@ describe("AppReduced", () => {
it("should work correctly when admin_basepath has no trailing slash", () => {
const options: BkndAdminProps["config"] = {
basepath: "/",
admin_basepath: "/admin",
basepath: "/admin",
logo_return_path: "/",
};
@@ -233,8 +231,7 @@ describe("AppReduced", () => {
it("should handle absolute paths with admin_basepath trailing slash", () => {
const options: BkndAdminProps["config"] = {
basepath: "/",
admin_basepath: "/admin/",
basepath: "/admin",
logo_return_path: "/",
};
@@ -247,8 +244,7 @@ describe("AppReduced", () => {
it("should handle settings path with admin_basepath trailing slash", () => {
const options: BkndAdminProps["config"] = {
basepath: "/",
admin_basepath: "/admin/",
basepath: "/admin",
logo_return_path: "/",
};