mirror of
https://github.com/shishantbiswas/bknd.git
synced 2026-03-16 04:27:21 +00:00
@@ -1,5 +1,4 @@
|
|||||||
[](https://npmjs.org/package/bknd)
|
[](https://npmjs.org/package/bknd)
|
||||||
[](https://www.npmjs.com/package/bknd)
|
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
@@ -18,14 +17,14 @@ bknd simplifies app development by providing a fully functional backend for data
|
|||||||
> and therefore full backward compatibility is not guaranteed before reaching v1.0.0.
|
> and therefore full backward compatibility is not guaranteed before reaching v1.0.0.
|
||||||
|
|
||||||
## Size
|
## Size
|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|

|
||||||
|
|
||||||
The size on npm is misleading, as the `bknd` package includes the backend, the ui components as well as the whole backend bundled into the cli including static assets.
|
The size on npm is misleading, as the `bknd` package includes the backend, the ui components as well as the whole backend bundled into the cli including static assets.
|
||||||
|
|
||||||
Depending on what you use, the size can be higher as additional dependencies are getting pulled in. The minimal size of a full `bknd` app as an API is around 212 kB gzipped (e.g. deployed as Cloudflare Worker).
|
Depending on what you use, the size can be higher as additional dependencies are getting pulled in. The minimal size of a full `bknd` app as an API is around 300 kB gzipped (e.g. deployed as Cloudflare Worker).
|
||||||
|
|
||||||
## Motivation
|
## Motivation
|
||||||
Creating digital products always requires developing both the backend (the logic) and the frontend (the appearance). Building a backend from scratch demands deep knowledge in areas such as authentication and database management. Using a backend framework can speed up initial development, but it still requires ongoing effort to work within its constraints (e.g., *"how to do X with Y?"*), which can quickly slow you down. Choosing a backend system is a tough decision, as you might not be aware of its limitations until you encounter them.
|
Creating digital products always requires developing both the backend (the logic) and the frontend (the appearance). Building a backend from scratch demands deep knowledge in areas such as authentication and database management. Using a backend framework can speed up initial development, but it still requires ongoing effort to work within its constraints (e.g., *"how to do X with Y?"*), which can quickly slow you down. Choosing a backend system is a tough decision, as you might not be aware of its limitations until you encounter them.
|
||||||
|
|||||||
@@ -110,4 +110,18 @@ describe("some tests", async () => {
|
|||||||
new EntityManager([entity, entity2], connection);
|
new EntityManager([entity, entity2], connection);
|
||||||
}).toThrow();
|
}).toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("primary uuid", async () => {
|
||||||
|
const entity = new Entity("users", [
|
||||||
|
new PrimaryField("id", { format: "uuid" }),
|
||||||
|
new TextField("username"),
|
||||||
|
]);
|
||||||
|
const em = new EntityManager([entity], getDummyConnection().dummyConnection);
|
||||||
|
await em.schema().sync({ force: true });
|
||||||
|
|
||||||
|
const mutator = em.mutator(entity);
|
||||||
|
const data = await mutator.insertOne({ username: "test" });
|
||||||
|
expect(data.data.id).toBeDefined();
|
||||||
|
expect(data.data.id).toBeString();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -39,4 +39,28 @@ describe("[data] PrimaryField", async () => {
|
|||||||
expect(field.transformPersist(1)).rejects.toThrow();
|
expect(field.transformPersist(1)).rejects.toThrow();
|
||||||
expect(field.transformRetrieve(1)).toBe(1);
|
expect(field.transformRetrieve(1)).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("format", () => {
|
||||||
|
const uuid = new PrimaryField("uuid", { format: "uuid" });
|
||||||
|
expect(uuid.format).toBe("uuid");
|
||||||
|
expect(uuid.fieldType).toBe("text");
|
||||||
|
expect(uuid.getNewValue()).toBeString();
|
||||||
|
expect(uuid.toType()).toEqual({
|
||||||
|
required: true,
|
||||||
|
comment: undefined,
|
||||||
|
type: "Generated<string>",
|
||||||
|
import: [{ package: "kysely", name: "Generated" }],
|
||||||
|
});
|
||||||
|
|
||||||
|
const integer = new PrimaryField("integer", { format: "integer" });
|
||||||
|
expect(integer.format).toBe("integer");
|
||||||
|
expect(integer.fieldType).toBe("integer");
|
||||||
|
expect(integer.getNewValue()).toBeUndefined();
|
||||||
|
expect(integer.toType()).toEqual({
|
||||||
|
required: true,
|
||||||
|
comment: undefined,
|
||||||
|
type: "Generated<number>",
|
||||||
|
import: [{ package: "kysely", name: "Generated" }],
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -153,6 +153,7 @@ describe("AppAuth", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await app.build();
|
await app.build();
|
||||||
|
app.registerAdminController();
|
||||||
const spy = spyOn(app.module.auth.authenticator, "requestCookieRefresh");
|
const spy = spyOn(app.module.auth.authenticator, "requestCookieRefresh");
|
||||||
|
|
||||||
// register custom route
|
// register custom route
|
||||||
@@ -162,6 +163,10 @@ describe("AppAuth", () => {
|
|||||||
await app.server.request("/api/system/ping");
|
await app.server.request("/api/system/ping");
|
||||||
await app.server.request("/test");
|
await app.server.request("/test");
|
||||||
|
|
||||||
|
expect(spy.mock.calls.length).toBe(0);
|
||||||
|
|
||||||
|
// admin route
|
||||||
|
await app.server.request("/");
|
||||||
expect(spy.mock.calls.length).toBe(1);
|
expect(spy.mock.calls.length).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
"bin": "./dist/cli/index.js",
|
"bin": "./dist/cli/index.js",
|
||||||
"version": "0.13.0",
|
"version": "0.14.0-rc.2",
|
||||||
"description": "Lightweight Firebase/Supabase alternative built to run anywhere — incl. Next.js, React Router, Astro, Cloudflare, Bun, Node, AWS Lambda & more.",
|
"description": "Lightweight Firebase/Supabase alternative built to run anywhere — incl. Next.js, React Router, Astro, Cloudflare, Bun, Node, AWS Lambda & more.",
|
||||||
"homepage": "https://bknd.io",
|
"homepage": "https://bknd.io",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -61,23 +61,24 @@
|
|||||||
"bcryptjs": "^3.0.2",
|
"bcryptjs": "^3.0.2",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
"fast-xml-parser": "^5.0.8",
|
"fast-xml-parser": "^5.0.8",
|
||||||
"hono": "^4.7.4",
|
|
||||||
"json-schema-form-react": "^0.0.2",
|
"json-schema-form-react": "^0.0.2",
|
||||||
"json-schema-library": "10.0.0-rc7",
|
"json-schema-library": "10.0.0-rc7",
|
||||||
"json-schema-to-ts": "^3.1.1",
|
"json-schema-to-ts": "^3.1.1",
|
||||||
"kysely": "^0.27.6",
|
"kysely": "^0.27.6",
|
||||||
|
"hono": "^4.7.11",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"oauth4webapi": "^2.11.1",
|
"oauth4webapi": "^2.11.1",
|
||||||
"object-path-immutable": "^4.1.2",
|
"object-path-immutable": "^4.1.2",
|
||||||
"radix-ui": "^1.1.3",
|
"radix-ui": "^1.1.3",
|
||||||
"swr": "^2.3.3"
|
"swr": "^2.3.3",
|
||||||
|
"uuid": "^11.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@aws-sdk/client-s3": "^3.758.0",
|
"@aws-sdk/client-s3": "^3.758.0",
|
||||||
"@bluwy/giget-core": "^0.1.2",
|
"@bluwy/giget-core": "^0.1.2",
|
||||||
"@dagrejs/dagre": "^1.1.4",
|
"@dagrejs/dagre": "^1.1.4",
|
||||||
"@hono/typebox-validator": "^0.3.2",
|
"@hono/typebox-validator": "^0.3.3",
|
||||||
"@hono/vite-dev-server": "^0.19.0",
|
"@hono/vite-dev-server": "^0.19.1",
|
||||||
"@hookform/resolvers": "^4.1.3",
|
"@hookform/resolvers": "^4.1.3",
|
||||||
"@libsql/kysely-libsql": "^0.4.1",
|
"@libsql/kysely-libsql": "^0.4.1",
|
||||||
"@mantine/modals": "^7.17.1",
|
"@mantine/modals": "^7.17.1",
|
||||||
@@ -99,7 +100,7 @@
|
|||||||
"dotenv": "^16.4.7",
|
"dotenv": "^16.4.7",
|
||||||
"jotai": "^2.12.2",
|
"jotai": "^2.12.2",
|
||||||
"jsdom": "^26.0.0",
|
"jsdom": "^26.0.0",
|
||||||
"jsonv-ts": "^0.0.14-alpha.6",
|
"jsonv-ts": "^0.1.0",
|
||||||
"kysely-d1": "^0.3.0",
|
"kysely-d1": "^0.3.0",
|
||||||
"open": "^10.1.0",
|
"open": "^10.1.0",
|
||||||
"openapi-types": "^12.1.3",
|
"openapi-types": "^12.1.3",
|
||||||
@@ -120,13 +121,14 @@
|
|||||||
"tsc-alias": "^1.8.11",
|
"tsc-alias": "^1.8.11",
|
||||||
"tsup": "^8.4.0",
|
"tsup": "^8.4.0",
|
||||||
"tsx": "^4.19.3",
|
"tsx": "^4.19.3",
|
||||||
"vite": "^6.2.1",
|
"vite": "^6.3.5",
|
||||||
"vite-tsconfig-paths": "^5.1.4",
|
"vite-tsconfig-paths": "^5.1.4",
|
||||||
"vitest": "^3.0.9",
|
"vitest": "^3.0.9",
|
||||||
"wouter": "^3.6.0"
|
"wouter": "^3.6.0",
|
||||||
|
"@cloudflare/workers-types": "^4.20250606.0"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@hono/node-server": "^1.13.8"
|
"@hono/node-server": "^1.14.3"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": ">=19",
|
"react": ">=19",
|
||||||
|
|||||||
@@ -253,6 +253,11 @@ export class App {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// call server init if set
|
||||||
|
if (this.options?.manager?.onServerInit) {
|
||||||
|
this.options.manager.onServerInit(server);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { afterAll, beforeAll, describe, expect, it } from "bun:test";
|
import { afterAll, beforeAll, describe, expect, it } from "bun:test";
|
||||||
import { makeApp } from "./modes/fresh";
|
import { makeApp } from "./modes/fresh";
|
||||||
import { makeConfig } from "./config";
|
import { makeConfig, type CfMakeConfigArgs } from "./config";
|
||||||
import { disableConsoleLog, enableConsoleLog } from "core/utils";
|
import { disableConsoleLog, enableConsoleLog } from "core/utils";
|
||||||
import { adapterTestSuite } from "adapter/adapter-test-suite";
|
import { adapterTestSuite } from "adapter/adapter-test-suite";
|
||||||
import { bunTestRunner } from "adapter/bun/test";
|
import { bunTestRunner } from "adapter/bun/test";
|
||||||
@@ -23,7 +23,7 @@ describe("cf adapter", () => {
|
|||||||
{
|
{
|
||||||
connection: { url: DB_URL },
|
connection: { url: DB_URL },
|
||||||
},
|
},
|
||||||
{},
|
$ctx({ DB_URL }),
|
||||||
),
|
),
|
||||||
).toEqual({ connection: { url: DB_URL } });
|
).toEqual({ connection: { url: DB_URL } });
|
||||||
|
|
||||||
@@ -34,15 +34,15 @@ describe("cf adapter", () => {
|
|||||||
connection: { url: env.DB_URL },
|
connection: { url: env.DB_URL },
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
{
|
$ctx({ DB_URL }),
|
||||||
DB_URL,
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
).toEqual({ connection: { url: DB_URL } });
|
).toEqual({ connection: { url: DB_URL } });
|
||||||
});
|
});
|
||||||
|
|
||||||
adapterTestSuite<CloudflareBkndConfig, object>(bunTestRunner, {
|
adapterTestSuite<CloudflareBkndConfig, CfMakeConfigArgs<any>>(bunTestRunner, {
|
||||||
makeApp,
|
makeApp: async (c, a, o) => {
|
||||||
|
return await makeApp(c, { env: a } as any, o);
|
||||||
|
},
|
||||||
makeHandler: (c, a, o) => {
|
makeHandler: (c, a, o) => {
|
||||||
return async (request: any) => {
|
return async (request: any) => {
|
||||||
const app = await makeApp(
|
const app = await makeApp(
|
||||||
@@ -50,7 +50,7 @@ describe("cf adapter", () => {
|
|||||||
c ?? {
|
c ?? {
|
||||||
connection: { url: DB_URL },
|
connection: { url: DB_URL },
|
||||||
},
|
},
|
||||||
a,
|
a!,
|
||||||
o,
|
o,
|
||||||
);
|
);
|
||||||
return app.fetch(request);
|
return app.fetch(request);
|
||||||
|
|||||||
@@ -9,7 +9,13 @@ import { getDurable } from "./modes/durable";
|
|||||||
import type { App } from "bknd";
|
import type { App } from "bknd";
|
||||||
import { $console } from "core";
|
import { $console } from "core";
|
||||||
|
|
||||||
export type CloudflareEnv = object;
|
declare global {
|
||||||
|
namespace Cloudflare {
|
||||||
|
interface Env {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CloudflareEnv = Cloudflare.Env;
|
||||||
export type CloudflareBkndConfig<Env = CloudflareEnv> = RuntimeBkndConfig<Env> & {
|
export type CloudflareBkndConfig<Env = CloudflareEnv> = RuntimeBkndConfig<Env> & {
|
||||||
mode?: "warm" | "fresh" | "cache" | "durable";
|
mode?: "warm" | "fresh" | "cache" | "durable";
|
||||||
bindings?: (args: Env) => {
|
bindings?: (args: Env) => {
|
||||||
@@ -17,6 +23,11 @@ export type CloudflareBkndConfig<Env = CloudflareEnv> = RuntimeBkndConfig<Env> &
|
|||||||
dobj?: DurableObjectNamespace;
|
dobj?: DurableObjectNamespace;
|
||||||
db?: D1Database;
|
db?: D1Database;
|
||||||
};
|
};
|
||||||
|
d1?: {
|
||||||
|
session?: boolean;
|
||||||
|
transport?: "header" | "cookie";
|
||||||
|
first?: D1SessionConstraint;
|
||||||
|
};
|
||||||
static?: "kv" | "assets";
|
static?: "kv" | "assets";
|
||||||
key?: string;
|
key?: string;
|
||||||
keepAliveSeconds?: number;
|
keepAliveSeconds?: number;
|
||||||
|
|||||||
@@ -1,37 +1,117 @@
|
|||||||
|
/// <reference types="@cloudflare/workers-types" />
|
||||||
|
|
||||||
import { registerMedia } from "./storage/StorageR2Adapter";
|
import { registerMedia } from "./storage/StorageR2Adapter";
|
||||||
import { getBinding } from "./bindings";
|
import { getBinding } from "./bindings";
|
||||||
import { D1Connection } from "./D1Connection";
|
import { D1Connection } from "./connection/D1Connection";
|
||||||
import type { CloudflareBkndConfig, CloudflareEnv } from ".";
|
import type { CloudflareBkndConfig, CloudflareEnv } from ".";
|
||||||
import { App } from "bknd";
|
import { App } from "bknd";
|
||||||
import { makeConfig as makeAdapterConfig } from "bknd/adapter";
|
import { makeConfig as makeAdapterConfig } from "bknd/adapter";
|
||||||
import type { ExecutionContext } from "hono";
|
import type { Context, ExecutionContext } from "hono";
|
||||||
import { $console } from "core";
|
import { $console } from "core";
|
||||||
|
import { setCookie } from "hono/cookie";
|
||||||
|
|
||||||
export const constants = {
|
export const constants = {
|
||||||
exec_async_event_id: "cf_register_waituntil",
|
exec_async_event_id: "cf_register_waituntil",
|
||||||
cache_endpoint: "/__bknd/cache",
|
cache_endpoint: "/__bknd/cache",
|
||||||
do_endpoint: "/__bknd/do",
|
do_endpoint: "/__bknd/do",
|
||||||
|
d1_session: {
|
||||||
|
cookie: "cf_d1_session",
|
||||||
|
header: "x-cf-d1-session",
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type CfMakeConfigArgs<Env extends CloudflareEnv = CloudflareEnv> = {
|
||||||
|
env: Env;
|
||||||
|
ctx?: ExecutionContext;
|
||||||
|
request?: Request;
|
||||||
|
};
|
||||||
|
|
||||||
|
function getCookieValue(cookies: string | null, name: string) {
|
||||||
|
if (!cookies) return null;
|
||||||
|
|
||||||
|
for (const cookie of cookies.split("; ")) {
|
||||||
|
const [key, value] = cookie.split("=");
|
||||||
|
if (key === name && value) {
|
||||||
|
return decodeURIComponent(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function d1SessionHelper(config: CloudflareBkndConfig<any>) {
|
||||||
|
const headerKey = constants.d1_session.header;
|
||||||
|
const cookieKey = constants.d1_session.cookie;
|
||||||
|
const transport = config.d1?.transport;
|
||||||
|
|
||||||
|
return {
|
||||||
|
get: (request?: Request): D1SessionBookmark | undefined => {
|
||||||
|
if (!request || !config.d1?.session) return undefined;
|
||||||
|
|
||||||
|
if (!transport || transport === "cookie") {
|
||||||
|
const cookies = request.headers.get("Cookie");
|
||||||
|
if (cookies) {
|
||||||
|
const cookie = getCookieValue(cookies, cookieKey);
|
||||||
|
if (cookie) {
|
||||||
|
return cookie;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!transport || transport === "header") {
|
||||||
|
if (request.headers.has(headerKey)) {
|
||||||
|
return request.headers.get(headerKey) as any;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
},
|
||||||
|
set: (c: Context, d1?: D1DatabaseSession) => {
|
||||||
|
if (!d1 || !config.d1?.session) return;
|
||||||
|
|
||||||
|
const session = d1.getBookmark();
|
||||||
|
if (session) {
|
||||||
|
if (!transport || transport === "header") {
|
||||||
|
c.header(headerKey, session);
|
||||||
|
}
|
||||||
|
if (!transport || transport === "cookie") {
|
||||||
|
setCookie(c, cookieKey, session, {
|
||||||
|
httpOnly: true,
|
||||||
|
secure: true,
|
||||||
|
sameSite: "Lax",
|
||||||
|
maxAge: 60 * 5, // 5 minutes
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
let media_registered: boolean = false;
|
let media_registered: boolean = false;
|
||||||
export function makeConfig<Env extends CloudflareEnv = CloudflareEnv>(
|
export function makeConfig<Env extends CloudflareEnv = CloudflareEnv>(
|
||||||
config: CloudflareBkndConfig<Env>,
|
config: CloudflareBkndConfig<Env>,
|
||||||
args: Env = {} as Env,
|
args?: CfMakeConfigArgs<Env>,
|
||||||
) {
|
) {
|
||||||
if (!media_registered) {
|
if (!media_registered) {
|
||||||
registerMedia(args as any);
|
registerMedia(args as any);
|
||||||
media_registered = true;
|
media_registered = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const appConfig = makeAdapterConfig(config, args);
|
const appConfig = makeAdapterConfig(config, args?.env);
|
||||||
const bindings = config.bindings?.(args);
|
|
||||||
|
if (args?.env) {
|
||||||
|
const bindings = config.bindings?.(args?.env);
|
||||||
|
|
||||||
|
const sessionHelper = d1SessionHelper(config);
|
||||||
|
const sessionId = sessionHelper.get(args.request);
|
||||||
|
let session: D1DatabaseSession | undefined;
|
||||||
|
|
||||||
if (!appConfig.connection) {
|
if (!appConfig.connection) {
|
||||||
let db: D1Database | undefined;
|
let db: D1Database | undefined;
|
||||||
if (bindings?.db) {
|
if (bindings?.db) {
|
||||||
$console.log("Using database from bindings");
|
$console.log("Using database from bindings");
|
||||||
db = bindings.db;
|
db = bindings.db;
|
||||||
} else if (Object.keys(args).length > 0) {
|
} else if (Object.keys(args).length > 0) {
|
||||||
const binding = getBinding(args, "D1Database");
|
const binding = getBinding(args.env, "D1Database");
|
||||||
if (binding) {
|
if (binding) {
|
||||||
$console.log(`Using database from env "${binding.key}"`);
|
$console.log(`Using database from env "${binding.key}"`);
|
||||||
db = binding.value;
|
db = binding.value;
|
||||||
@@ -39,12 +119,33 @@ export function makeConfig<Env extends CloudflareEnv = CloudflareEnv>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (db) {
|
if (db) {
|
||||||
|
if (config.d1?.session) {
|
||||||
|
session = db.withSession(sessionId ?? config.d1?.first);
|
||||||
|
appConfig.connection = new D1Connection({ binding: session });
|
||||||
|
} else {
|
||||||
appConfig.connection = new D1Connection({ binding: db });
|
appConfig.connection = new D1Connection({ binding: db });
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new Error("No database connection given");
|
throw new Error("No database connection given");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config.d1?.session) {
|
||||||
|
appConfig.options = {
|
||||||
|
...appConfig.options,
|
||||||
|
manager: {
|
||||||
|
...appConfig.options?.manager,
|
||||||
|
onServerInit: (server) => {
|
||||||
|
server.use(async (c, next) => {
|
||||||
|
sessionHelper.set(c, session);
|
||||||
|
await next();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return appConfig;
|
return appConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import type { QB } from "data/connection/Connection";
|
|||||||
import { type DatabaseIntrospector, Kysely, ParseJSONResultsPlugin } from "kysely";
|
import { type DatabaseIntrospector, Kysely, ParseJSONResultsPlugin } from "kysely";
|
||||||
import { D1Dialect } from "kysely-d1";
|
import { D1Dialect } from "kysely-d1";
|
||||||
|
|
||||||
export type D1ConnectionConfig = {
|
export type D1ConnectionConfig<DB extends D1Database | D1DatabaseSession = D1Database> = {
|
||||||
binding: D1Database;
|
binding: DB;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CustomD1Dialect extends D1Dialect {
|
class CustomD1Dialect extends D1Dialect {
|
||||||
@@ -17,22 +17,24 @@ class CustomD1Dialect extends D1Dialect {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class D1Connection extends SqliteConnection {
|
export class D1Connection<
|
||||||
|
DB extends D1Database | D1DatabaseSession = D1Database,
|
||||||
|
> extends SqliteConnection {
|
||||||
protected override readonly supported = {
|
protected override readonly supported = {
|
||||||
batching: true,
|
batching: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(private config: D1ConnectionConfig) {
|
constructor(private config: D1ConnectionConfig<DB>) {
|
||||||
const plugins = [new ParseJSONResultsPlugin()];
|
const plugins = [new ParseJSONResultsPlugin()];
|
||||||
|
|
||||||
const kysely = new Kysely({
|
const kysely = new Kysely({
|
||||||
dialect: new CustomD1Dialect({ database: config.binding }),
|
dialect: new CustomD1Dialect({ database: config.binding as D1Database }),
|
||||||
plugins,
|
plugins,
|
||||||
});
|
});
|
||||||
super(kysely, {}, plugins);
|
super(kysely, {}, plugins);
|
||||||
}
|
}
|
||||||
|
|
||||||
get client(): D1Database {
|
get client(): DB {
|
||||||
return this.config.binding;
|
return this.config.binding;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { D1Connection, type D1ConnectionConfig } from "./D1Connection";
|
import { D1Connection, type D1ConnectionConfig } from "./connection/D1Connection";
|
||||||
|
|
||||||
export * from "./cloudflare-workers.adapter";
|
export * from "./cloudflare-workers.adapter";
|
||||||
export { makeApp, getFresh } from "./modes/fresh";
|
export { makeApp, getFresh } from "./modes/fresh";
|
||||||
@@ -12,6 +12,7 @@ export {
|
|||||||
type GetBindingType,
|
type GetBindingType,
|
||||||
type BindingMap,
|
type BindingMap,
|
||||||
} from "./bindings";
|
} from "./bindings";
|
||||||
|
export { constants } from "./config";
|
||||||
|
|
||||||
export function d1(config: D1ConnectionConfig) {
|
export function d1(config: D1ConnectionConfig) {
|
||||||
return new D1Connection(config);
|
return new D1Connection(config);
|
||||||
|
|||||||
@@ -5,8 +5,9 @@ import { makeConfig, registerAsyncsExecutionContext, constants } from "../config
|
|||||||
|
|
||||||
export async function getCached<Env extends CloudflareEnv = CloudflareEnv>(
|
export async function getCached<Env extends CloudflareEnv = CloudflareEnv>(
|
||||||
config: CloudflareBkndConfig<Env>,
|
config: CloudflareBkndConfig<Env>,
|
||||||
{ env, ctx, ...args }: Context<Env>,
|
args: Context<Env>,
|
||||||
) {
|
) {
|
||||||
|
const { env, ctx } = args;
|
||||||
const { kv } = config.bindings?.(env)!;
|
const { kv } = config.bindings?.(env)!;
|
||||||
if (!kv) throw new Error("kv namespace is not defined in cloudflare.bindings");
|
if (!kv) throw new Error("kv namespace is not defined in cloudflare.bindings");
|
||||||
const key = config.key ?? "app";
|
const key = config.key ?? "app";
|
||||||
@@ -20,7 +21,7 @@ export async function getCached<Env extends CloudflareEnv = CloudflareEnv>(
|
|||||||
|
|
||||||
const app = await createRuntimeApp(
|
const app = await createRuntimeApp(
|
||||||
{
|
{
|
||||||
...makeConfig(config, env),
|
...makeConfig(config, args),
|
||||||
initialConfig,
|
initialConfig,
|
||||||
onBuilt: async (app) => {
|
onBuilt: async (app) => {
|
||||||
registerAsyncsExecutionContext(app, ctx);
|
registerAsyncsExecutionContext(app, ctx);
|
||||||
@@ -41,7 +42,7 @@ export async function getCached<Env extends CloudflareEnv = CloudflareEnv>(
|
|||||||
await config.beforeBuild?.(app);
|
await config.beforeBuild?.(app);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{ env, ctx, ...args },
|
args,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!cachedConfig) {
|
if (!cachedConfig) {
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { createRuntimeApp, type RuntimeOptions } from "bknd/adapter";
|
import { createRuntimeApp, type RuntimeOptions } from "bknd/adapter";
|
||||||
import type { CloudflareBkndConfig, Context, CloudflareEnv } from "../index";
|
import type { CloudflareBkndConfig, Context, CloudflareEnv } from "../index";
|
||||||
import { makeConfig, registerAsyncsExecutionContext } from "../config";
|
import { makeConfig, registerAsyncsExecutionContext, type CfMakeConfigArgs } from "../config";
|
||||||
|
|
||||||
export async function makeApp<Env extends CloudflareEnv = CloudflareEnv>(
|
export async function makeApp<Env extends CloudflareEnv = CloudflareEnv>(
|
||||||
config: CloudflareBkndConfig<Env>,
|
config: CloudflareBkndConfig<Env>,
|
||||||
args: Env = {} as Env,
|
args?: CfMakeConfigArgs<Env>,
|
||||||
opts?: RuntimeOptions,
|
opts?: RuntimeOptions,
|
||||||
) {
|
) {
|
||||||
return await createRuntimeApp<Env>(makeConfig(config, args), args, opts);
|
return await createRuntimeApp<Env>(makeConfig(config, args), args?.env, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getFresh<Env extends CloudflareEnv = CloudflareEnv>(
|
export async function getFresh<Env extends CloudflareEnv = CloudflareEnv>(
|
||||||
@@ -23,7 +23,7 @@ export async function getFresh<Env extends CloudflareEnv = CloudflareEnv>(
|
|||||||
await config.onBuilt?.(app);
|
await config.onBuilt?.(app);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ctx.env,
|
ctx,
|
||||||
opts,
|
opts,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,17 @@
|
|||||||
import { serveStatic } from "@hono/node-server/serve-static";
|
import { serveStatic } from "@hono/node-server/serve-static";
|
||||||
import {
|
import { type DevServerOptions, default as honoViteDevServer } from "@hono/vite-dev-server";
|
||||||
type DevServerOptions,
|
|
||||||
default as honoViteDevServer,
|
|
||||||
} from "@hono/vite-dev-server";
|
|
||||||
import type { App } from "bknd";
|
import type { App } from "bknd";
|
||||||
import {
|
import { type RuntimeBkndConfig, createRuntimeApp, type FrameworkOptions } from "bknd/adapter";
|
||||||
type RuntimeBkndConfig,
|
|
||||||
createRuntimeApp,
|
|
||||||
type FrameworkOptions,
|
|
||||||
} from "bknd/adapter";
|
|
||||||
import { registerLocalMediaAdapter } from "bknd/adapter/node";
|
import { registerLocalMediaAdapter } from "bknd/adapter/node";
|
||||||
import { devServerConfig } from "./dev-server-config";
|
import { devServerConfig } from "./dev-server-config";
|
||||||
|
import type { MiddlewareHandler } from "hono";
|
||||||
|
|
||||||
export type ViteEnv = NodeJS.ProcessEnv;
|
export type ViteEnv = NodeJS.ProcessEnv;
|
||||||
export type ViteBkndConfig<Env = ViteEnv> = RuntimeBkndConfig<Env> & {};
|
export type ViteBkndConfig<Env = ViteEnv> = RuntimeBkndConfig<Env> & {
|
||||||
|
serveStatic?: false | MiddlewareHandler;
|
||||||
|
};
|
||||||
|
|
||||||
export function addViteScript(
|
export function addViteScript(html: string, addBkndContext: boolean = true) {
|
||||||
html: string,
|
|
||||||
addBkndContext: boolean = true,
|
|
||||||
) {
|
|
||||||
return html.replace(
|
return html.replace(
|
||||||
"</head>",
|
"</head>",
|
||||||
`<script type="module">
|
`<script type="module">
|
||||||
@@ -48,7 +41,10 @@ async function createApp<ViteEnv>(
|
|||||||
mainPath: "/src/main.tsx",
|
mainPath: "/src/main.tsx",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
serveStatic: ["/assets/*", serveStatic({ root: config.distPath ?? "./" })],
|
serveStatic: config.serveStatic || [
|
||||||
|
"/assets/*",
|
||||||
|
serveStatic({ root: config.distPath ?? "./" }),
|
||||||
|
],
|
||||||
},
|
},
|
||||||
env,
|
env,
|
||||||
opts,
|
opts,
|
||||||
|
|||||||
@@ -121,6 +121,7 @@ export class AuthController extends Controller {
|
|||||||
const claims = c.get("auth")?.user;
|
const claims = c.get("auth")?.user;
|
||||||
if (claims) {
|
if (claims) {
|
||||||
const { data: user } = await this.userRepo.findId(claims.id);
|
const { data: user } = await this.userRepo.findId(claims.id);
|
||||||
|
await this.auth.authenticator?.requestCookieRefresh(c);
|
||||||
return c.json({ user });
|
return c.json({ user });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -347,6 +347,7 @@ export class Authenticator<Strategies extends Record<string, Strategy> = Record<
|
|||||||
}
|
}
|
||||||
|
|
||||||
async logout(c: Context<ServerEnv>) {
|
async logout(c: Context<ServerEnv>) {
|
||||||
|
$console.info("Logging out");
|
||||||
c.set("auth", undefined);
|
c.set("auth", undefined);
|
||||||
|
|
||||||
const cookie = await this.getAuthCookie(c);
|
const cookie = await this.getAuthCookie(c);
|
||||||
|
|||||||
@@ -60,11 +60,7 @@ export const auth = (options?: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await next();
|
await next();
|
||||||
|
// @todo: potentially add cookie refresh if content-type html and about to expire
|
||||||
if (!skipped) {
|
|
||||||
// renew cookie if applicable
|
|
||||||
authenticator?.requestCookieRefresh(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
// release
|
// release
|
||||||
authCtx.skip = false;
|
authCtx.skip = false;
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
*/
|
*/
|
||||||
import type { Generated } from "kysely";
|
import type { Generated } from "kysely";
|
||||||
|
|
||||||
export type PrimaryFieldType<IdType extends number = number> = IdType | Generated<IdType>;
|
export type PrimaryFieldType<IdType = number | string> = IdType | Generated<IdType>;
|
||||||
|
|
||||||
export interface AppEntity<IdType extends number = number> {
|
export interface AppEntity<IdType = number | string> {
|
||||||
id: PrimaryFieldType<IdType>;
|
id: PrimaryFieldType<IdType>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,12 @@ function hasColors() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const __consoles = {
|
const __consoles = {
|
||||||
|
critical: {
|
||||||
|
prefix: "CRT",
|
||||||
|
color: colors.red,
|
||||||
|
args_color: colors.red,
|
||||||
|
original: console.error,
|
||||||
|
},
|
||||||
error: {
|
error: {
|
||||||
prefix: "ERR",
|
prefix: "ERR",
|
||||||
color: colors.red,
|
color: colors.red,
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export type ParseOptions = {
|
|||||||
clone?: boolean;
|
clone?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const cloneSchema = <S extends s.TSchema>(schema: S): S => {
|
export const cloneSchema = <S extends s.TSchema>(schema: S): S => {
|
||||||
const json = schema.toJSON();
|
const json = schema.toJSON();
|
||||||
return s.fromSchema(json) as S;
|
return s.fromSchema(json) as S;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -117,7 +117,9 @@ async function detectMimeType(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getFileFromContext(c: Context<any>): Promise<File> {
|
type HonoAnyContext = Context<any, any, any>;
|
||||||
|
|
||||||
|
export async function getFileFromContext(c: HonoAnyContext): Promise<File> {
|
||||||
const contentType = c.req.header("Content-Type") ?? "application/octet-stream";
|
const contentType = c.req.header("Content-Type") ?? "application/octet-stream";
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@@ -149,7 +151,7 @@ export async function getFileFromContext(c: Context<any>): Promise<File> {
|
|||||||
throw new Error("No file found in request");
|
throw new Error("No file found in request");
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getBodyFromContext(c: Context<any>): Promise<ReadableStream | File> {
|
export async function getBodyFromContext(c: HonoAnyContext): Promise<ReadableStream | File> {
|
||||||
const contentType = c.req.header("Content-Type") ?? "application/octet-stream";
|
const contentType = c.req.header("Content-Type") ?? "application/octet-stream";
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export function disableConsoleLog(severities: ConsoleSeverity[] = ["log", "warn"
|
|||||||
severities.forEach((severity) => {
|
severities.forEach((severity) => {
|
||||||
console[severity] = () => null;
|
console[severity] = () => null;
|
||||||
});
|
});
|
||||||
$console.setLevel("error");
|
$console.setLevel("critical");
|
||||||
}
|
}
|
||||||
|
|
||||||
export function enableConsoleLog() {
|
export function enableConsoleLog() {
|
||||||
|
|||||||
@@ -1,4 +1,10 @@
|
|||||||
|
import { v4, v7 } from "uuid";
|
||||||
|
|
||||||
// generates v4
|
// generates v4
|
||||||
export function uuid(): string {
|
export function uuid(): string {
|
||||||
return crypto.randomUUID();
|
return v4();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function uuidv7(): string {
|
||||||
|
return v7();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -233,6 +233,8 @@ export class DataController extends Controller {
|
|||||||
const hono = this.create();
|
const hono = this.create();
|
||||||
|
|
||||||
const entitiesEnum = this.getEntitiesEnum(this.em);
|
const entitiesEnum = this.getEntitiesEnum(this.em);
|
||||||
|
// @todo: make dynamic based on entity
|
||||||
|
const idType = s.anyOf([s.number(), s.string()], { coerce: (v) => v as any });
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function endpoints
|
* Function endpoints
|
||||||
@@ -333,7 +335,7 @@ export class DataController extends Controller {
|
|||||||
"param",
|
"param",
|
||||||
s.object({
|
s.object({
|
||||||
entity: entitiesEnum,
|
entity: entitiesEnum,
|
||||||
id: s.string(),
|
id: idType,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
jsc("query", repoQuery, { skipOpenAPI: true }),
|
jsc("query", repoQuery, { skipOpenAPI: true }),
|
||||||
@@ -343,7 +345,7 @@ export class DataController extends Controller {
|
|||||||
return this.notFound(c);
|
return this.notFound(c);
|
||||||
}
|
}
|
||||||
const options = c.req.valid("query") as RepoQuery;
|
const options = c.req.valid("query") as RepoQuery;
|
||||||
const result = await this.em.repository(entity).findId(Number(id), options);
|
const result = await this.em.repository(entity).findId(id, options);
|
||||||
|
|
||||||
return c.json(this.repoResult(result), { status: result.data ? 200 : 404 });
|
return c.json(this.repoResult(result), { status: result.data ? 200 : 404 });
|
||||||
},
|
},
|
||||||
@@ -362,7 +364,7 @@ export class DataController extends Controller {
|
|||||||
"param",
|
"param",
|
||||||
s.object({
|
s.object({
|
||||||
entity: entitiesEnum,
|
entity: entitiesEnum,
|
||||||
id: s.string(),
|
id: idType,
|
||||||
reference: s.string(),
|
reference: s.string(),
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
@@ -376,7 +378,7 @@ export class DataController extends Controller {
|
|||||||
const options = c.req.valid("query") as RepoQuery;
|
const options = c.req.valid("query") as RepoQuery;
|
||||||
const result = await this.em
|
const result = await this.em
|
||||||
.repository(entity)
|
.repository(entity)
|
||||||
.findManyByReference(Number(id), reference, options);
|
.findManyByReference(id, reference, options);
|
||||||
|
|
||||||
return c.json(this.repoResult(result), { status: result.data ? 200 : 404 });
|
return c.json(this.repoResult(result), { status: result.data ? 200 : 404 });
|
||||||
},
|
},
|
||||||
@@ -485,7 +487,7 @@ export class DataController extends Controller {
|
|||||||
tags: ["data"],
|
tags: ["data"],
|
||||||
}),
|
}),
|
||||||
permission(DataPermissions.entityUpdate),
|
permission(DataPermissions.entityUpdate),
|
||||||
jsc("param", s.object({ entity: entitiesEnum, id: s.number() })),
|
jsc("param", s.object({ entity: entitiesEnum, id: idType })),
|
||||||
jsc("json", s.object({})),
|
jsc("json", s.object({})),
|
||||||
async (c) => {
|
async (c) => {
|
||||||
const { entity, id } = c.req.valid("param");
|
const { entity, id } = c.req.valid("param");
|
||||||
@@ -493,7 +495,7 @@ export class DataController extends Controller {
|
|||||||
return this.notFound(c);
|
return this.notFound(c);
|
||||||
}
|
}
|
||||||
const body = (await c.req.json()) as EntityData;
|
const body = (await c.req.json()) as EntityData;
|
||||||
const result = await this.em.mutator(entity).updateOne(Number(id), body);
|
const result = await this.em.mutator(entity).updateOne(id, body);
|
||||||
|
|
||||||
return c.json(this.mutatorResult(result));
|
return c.json(this.mutatorResult(result));
|
||||||
},
|
},
|
||||||
@@ -507,13 +509,13 @@ export class DataController extends Controller {
|
|||||||
tags: ["data"],
|
tags: ["data"],
|
||||||
}),
|
}),
|
||||||
permission(DataPermissions.entityDelete),
|
permission(DataPermissions.entityDelete),
|
||||||
jsc("param", s.object({ entity: entitiesEnum, id: s.number() })),
|
jsc("param", s.object({ entity: entitiesEnum, id: idType })),
|
||||||
async (c) => {
|
async (c) => {
|
||||||
const { entity, id } = c.req.valid("param");
|
const { entity, id } = c.req.valid("param");
|
||||||
if (!this.entityExists(entity)) {
|
if (!this.entityExists(entity)) {
|
||||||
return this.notFound(c);
|
return this.notFound(c);
|
||||||
}
|
}
|
||||||
const result = await this.em.mutator(entity).deleteOne(Number(id));
|
const result = await this.em.mutator(entity).deleteOne(id);
|
||||||
|
|
||||||
return c.json(this.mutatorResult(result));
|
return c.json(this.mutatorResult(result));
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import {
|
|||||||
type AliasableExpression,
|
type AliasableExpression,
|
||||||
type ColumnBuilderCallback,
|
type ColumnBuilderCallback,
|
||||||
type ColumnDataType,
|
type ColumnDataType,
|
||||||
|
type DatabaseIntrospector,
|
||||||
|
type Dialect,
|
||||||
type Expression,
|
type Expression,
|
||||||
type Kysely,
|
type Kysely,
|
||||||
type KyselyPlugin,
|
type KyselyPlugin,
|
||||||
@@ -12,7 +14,8 @@ import {
|
|||||||
type Simplify,
|
type Simplify,
|
||||||
sql,
|
sql,
|
||||||
} from "kysely";
|
} from "kysely";
|
||||||
import type { BaseIntrospector } from "./BaseIntrospector";
|
import type { BaseIntrospector, BaseIntrospectorConfig } from "./BaseIntrospector";
|
||||||
|
import type { Constructor } from "core";
|
||||||
|
|
||||||
export type QB = SelectQueryBuilder<any, any, any>;
|
export type QB = SelectQueryBuilder<any, any, any>;
|
||||||
|
|
||||||
@@ -159,3 +162,19 @@ export abstract class Connection<DB = any> {
|
|||||||
// no-op by default
|
// no-op by default
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function customIntrospector<T extends Constructor<Dialect>>(
|
||||||
|
dialect: T,
|
||||||
|
introspector: Constructor<DatabaseIntrospector>,
|
||||||
|
options: BaseIntrospectorConfig = {},
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
create(...args: ConstructorParameters<T>) {
|
||||||
|
return new (class extends dialect {
|
||||||
|
override createIntrospector(db: Kysely<any>): DatabaseIntrospector {
|
||||||
|
return new introspector(db, options);
|
||||||
|
}
|
||||||
|
})(...args);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ export {
|
|||||||
type IndexSpec,
|
type IndexSpec,
|
||||||
type DbFunctions,
|
type DbFunctions,
|
||||||
type SchemaResponse,
|
type SchemaResponse,
|
||||||
|
customIntrospector,
|
||||||
} from "./Connection";
|
} from "./Connection";
|
||||||
|
|
||||||
// sqlite
|
// sqlite
|
||||||
|
|||||||
@@ -31,8 +31,12 @@ export class SqliteConnection extends Connection {
|
|||||||
type,
|
type,
|
||||||
(col: ColumnDefinitionBuilder) => {
|
(col: ColumnDefinitionBuilder) => {
|
||||||
if (spec.primary) {
|
if (spec.primary) {
|
||||||
|
if (spec.type === "integer") {
|
||||||
return col.primaryKey().notNull().autoIncrement();
|
return col.primaryKey().notNull().autoIncrement();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return col.primaryKey().notNull();
|
||||||
|
}
|
||||||
if (spec.references) {
|
if (spec.references) {
|
||||||
let relCol = col.references(spec.references);
|
let relCol = col.references(spec.references);
|
||||||
if (spec.onDelete) relCol = relCol.onDelete(spec.onDelete);
|
if (spec.onDelete) relCol = relCol.onDelete(spec.onDelete);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { type Static, StringRecord, objectTransform } from "core/utils";
|
import { type Static, StringEnum, StringRecord, objectTransform } from "core/utils";
|
||||||
import * as tb from "@sinclair/typebox";
|
import * as tb from "@sinclair/typebox";
|
||||||
import {
|
import {
|
||||||
FieldClassMap,
|
FieldClassMap,
|
||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
entityTypes,
|
entityTypes,
|
||||||
} from "data";
|
} from "data";
|
||||||
import { MediaField, mediaFieldConfigSchema } from "../media/MediaField";
|
import { MediaField, mediaFieldConfigSchema } from "../media/MediaField";
|
||||||
|
import { primaryFieldTypes } from "./fields";
|
||||||
|
|
||||||
export const FIELDS = {
|
export const FIELDS = {
|
||||||
...FieldClassMap,
|
...FieldClassMap,
|
||||||
@@ -72,6 +73,9 @@ export const indicesSchema = tb.Type.Object(
|
|||||||
export const dataConfigSchema = tb.Type.Object(
|
export const dataConfigSchema = tb.Type.Object(
|
||||||
{
|
{
|
||||||
basepath: tb.Type.Optional(tb.Type.String({ default: "/api/data" })),
|
basepath: tb.Type.Optional(tb.Type.String({ default: "/api/data" })),
|
||||||
|
default_primary_format: tb.Type.Optional(
|
||||||
|
StringEnum(primaryFieldTypes, { default: "integer" }),
|
||||||
|
),
|
||||||
entities: tb.Type.Optional(StringRecord(entitiesSchema, { default: {} })),
|
entities: tb.Type.Optional(StringRecord(entitiesSchema, { default: {} })),
|
||||||
relations: tb.Type.Optional(StringRecord(tb.Type.Union(relationsSchema), { default: {} })),
|
relations: tb.Type.Optional(StringRecord(tb.Type.Union(relationsSchema), { default: {} })),
|
||||||
indices: tb.Type.Optional(StringRecord(indicesSchema, { default: {} })),
|
indices: tb.Type.Optional(StringRecord(indicesSchema, { default: {} })),
|
||||||
|
|||||||
@@ -6,7 +6,13 @@ import {
|
|||||||
snakeToPascalWithSpaces,
|
snakeToPascalWithSpaces,
|
||||||
transformObject,
|
transformObject,
|
||||||
} from "core/utils";
|
} from "core/utils";
|
||||||
import { type Field, PrimaryField, type TActionContext, type TRenderContext } from "../fields";
|
import {
|
||||||
|
type Field,
|
||||||
|
PrimaryField,
|
||||||
|
primaryFieldTypes,
|
||||||
|
type TActionContext,
|
||||||
|
type TRenderContext,
|
||||||
|
} from "../fields";
|
||||||
import * as tbbox from "@sinclair/typebox";
|
import * as tbbox from "@sinclair/typebox";
|
||||||
const { Type } = tbbox;
|
const { Type } = tbbox;
|
||||||
|
|
||||||
@@ -18,6 +24,7 @@ export const entityConfigSchema = Type.Object(
|
|||||||
description: Type.Optional(Type.String()),
|
description: Type.Optional(Type.String()),
|
||||||
sort_field: Type.Optional(Type.String({ default: config.data.default_primary_field })),
|
sort_field: Type.Optional(Type.String({ default: config.data.default_primary_field })),
|
||||||
sort_dir: Type.Optional(StringEnum(["asc", "desc"], { default: "asc" })),
|
sort_dir: Type.Optional(StringEnum(["asc", "desc"], { default: "asc" })),
|
||||||
|
primary_format: Type.Optional(StringEnum(primaryFieldTypes)),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
additionalProperties: false,
|
additionalProperties: false,
|
||||||
@@ -68,7 +75,14 @@ export class Entity<
|
|||||||
if (primary_count > 1) {
|
if (primary_count > 1) {
|
||||||
throw new Error(`Entity "${name}" has more than one primary field`);
|
throw new Error(`Entity "${name}" has more than one primary field`);
|
||||||
}
|
}
|
||||||
this.fields = primary_count === 1 ? [] : [new PrimaryField()];
|
this.fields =
|
||||||
|
primary_count === 1
|
||||||
|
? []
|
||||||
|
: [
|
||||||
|
new PrimaryField(undefined, {
|
||||||
|
format: this.config.primary_format,
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
if (fields) {
|
if (fields) {
|
||||||
fields.forEach((field) => this.addField(field));
|
fields.forEach((field) => this.addField(field));
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ export class Mutator<
|
|||||||
|
|
||||||
// if listener returned, take what's returned
|
// if listener returned, take what's returned
|
||||||
const _data = result.returned ? result.params.data : data;
|
const _data = result.returned ? result.params.data : data;
|
||||||
const validatedData = {
|
let validatedData = {
|
||||||
...entity.getDefaultObject(),
|
...entity.getDefaultObject(),
|
||||||
...(await this.getValidatedData(_data, "create")),
|
...(await this.getValidatedData(_data, "create")),
|
||||||
};
|
};
|
||||||
@@ -159,6 +159,16 @@ export class Mutator<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// primary
|
||||||
|
const primary = entity.getPrimaryField();
|
||||||
|
const primary_value = primary.getNewValue();
|
||||||
|
if (primary_value) {
|
||||||
|
validatedData = {
|
||||||
|
[primary.name]: primary_value,
|
||||||
|
...validatedData,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const query = this.conn
|
const query = this.conn
|
||||||
.insertInto(entity.name)
|
.insertInto(entity.name)
|
||||||
.values(validatedData)
|
.values(validatedData)
|
||||||
@@ -175,7 +185,7 @@ export class Mutator<
|
|||||||
|
|
||||||
async updateOne(id: PrimaryFieldType, data: Partial<Input>): Promise<MutatorResponse<Output>> {
|
async updateOne(id: PrimaryFieldType, data: Partial<Input>): Promise<MutatorResponse<Output>> {
|
||||||
const entity = this.entity;
|
const entity = this.entity;
|
||||||
if (!Number.isInteger(id)) {
|
if (!id) {
|
||||||
throw new Error("ID must be provided for update");
|
throw new Error("ID must be provided for update");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,7 +222,7 @@ export class Mutator<
|
|||||||
|
|
||||||
async deleteOne(id: PrimaryFieldType): Promise<MutatorResponse<Output>> {
|
async deleteOne(id: PrimaryFieldType): Promise<MutatorResponse<Output>> {
|
||||||
const entity = this.entity;
|
const entity = this.entity;
|
||||||
if (!Number.isInteger(id)) {
|
if (!id) {
|
||||||
throw new Error("ID must be provided for deletion");
|
throw new Error("ID must be provided for deletion");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,17 @@
|
|||||||
import { config } from "core";
|
import { config } from "core";
|
||||||
import type { Static } from "core/utils";
|
import { StringEnum, uuidv7, type Static } from "core/utils";
|
||||||
import { Field, baseFieldConfigSchema } from "./Field";
|
import { Field, baseFieldConfigSchema } from "./Field";
|
||||||
import * as tbbox from "@sinclair/typebox";
|
import * as tbbox from "@sinclair/typebox";
|
||||||
import type { TFieldTSType } from "data/entities/EntityTypescript";
|
import type { TFieldTSType } from "data/entities/EntityTypescript";
|
||||||
const { Type } = tbbox;
|
const { Type } = tbbox;
|
||||||
|
|
||||||
|
export const primaryFieldTypes = ["integer", "uuid"] as const;
|
||||||
|
export type TPrimaryFieldFormat = (typeof primaryFieldTypes)[number];
|
||||||
|
|
||||||
export const primaryFieldConfigSchema = Type.Composite([
|
export const primaryFieldConfigSchema = Type.Composite([
|
||||||
Type.Omit(baseFieldConfigSchema, ["required"]),
|
Type.Omit(baseFieldConfigSchema, ["required"]),
|
||||||
Type.Object({
|
Type.Object({
|
||||||
|
format: Type.Optional(StringEnum(primaryFieldTypes, { default: "integer" })),
|
||||||
required: Type.Optional(Type.Literal(false)),
|
required: Type.Optional(Type.Literal(false)),
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
@@ -21,8 +25,8 @@ export class PrimaryField<Required extends true | false = false> extends Field<
|
|||||||
> {
|
> {
|
||||||
override readonly type = "primary";
|
override readonly type = "primary";
|
||||||
|
|
||||||
constructor(name: string = config.data.default_primary_field) {
|
constructor(name: string = config.data.default_primary_field, cfg?: PrimaryFieldConfig) {
|
||||||
super(name, { fillable: false, required: false });
|
super(name, { fillable: false, required: false, ...cfg });
|
||||||
}
|
}
|
||||||
|
|
||||||
override isRequired(): boolean {
|
override isRequired(): boolean {
|
||||||
@@ -30,32 +34,53 @@ export class PrimaryField<Required extends true | false = false> extends Field<
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected getSchema() {
|
protected getSchema() {
|
||||||
return baseFieldConfigSchema;
|
return primaryFieldConfigSchema;
|
||||||
|
}
|
||||||
|
|
||||||
|
get format() {
|
||||||
|
return this.config.format ?? "integer";
|
||||||
|
}
|
||||||
|
|
||||||
|
get fieldType() {
|
||||||
|
return this.format === "integer" ? "integer" : "text";
|
||||||
}
|
}
|
||||||
|
|
||||||
override schema() {
|
override schema() {
|
||||||
return Object.freeze({
|
return Object.freeze({
|
||||||
type: "integer",
|
type: this.fieldType,
|
||||||
name: this.name,
|
name: this.name,
|
||||||
primary: true,
|
primary: true,
|
||||||
nullable: false,
|
nullable: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getNewValue(): any {
|
||||||
|
if (this.format === "uuid") {
|
||||||
|
return uuidv7();
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
override async transformPersist(value: any): Promise<number> {
|
override async transformPersist(value: any): Promise<number> {
|
||||||
throw new Error("PrimaryField: This function should not be called");
|
throw new Error("PrimaryField: This function should not be called");
|
||||||
}
|
}
|
||||||
|
|
||||||
override toJsonSchema() {
|
override toJsonSchema() {
|
||||||
|
if (this.format === "uuid") {
|
||||||
|
return this.toSchemaWrapIfRequired(Type.String({ writeOnly: undefined }));
|
||||||
|
}
|
||||||
|
|
||||||
return this.toSchemaWrapIfRequired(Type.Number({ writeOnly: undefined }));
|
return this.toSchemaWrapIfRequired(Type.Number({ writeOnly: undefined }));
|
||||||
}
|
}
|
||||||
|
|
||||||
override toType(): TFieldTSType {
|
override toType(): TFieldTSType {
|
||||||
|
const type = this.format === "integer" ? "number" : "string";
|
||||||
return {
|
return {
|
||||||
...super.toType(),
|
...super.toType(),
|
||||||
required: true,
|
required: true,
|
||||||
import: [{ package: "kysely", name: "Generated" }],
|
import: [{ package: "kysely", name: "Generated" }],
|
||||||
type: "Generated<number>",
|
type: `Generated<${type}>`,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
import type { RepoQuery } from "../server/query";
|
import type { RepoQuery } from "../server/query";
|
||||||
import type { RelationType } from "./relation-types";
|
import type { RelationType } from "./relation-types";
|
||||||
import * as tbbox from "@sinclair/typebox";
|
import * as tbbox from "@sinclair/typebox";
|
||||||
|
import type { PrimaryFieldType } from "core";
|
||||||
const { Type } = tbbox;
|
const { Type } = tbbox;
|
||||||
|
|
||||||
const directions = ["source", "target"] as const;
|
const directions = ["source", "target"] as const;
|
||||||
@@ -72,7 +73,7 @@ export abstract class EntityRelation<
|
|||||||
reference: string,
|
reference: string,
|
||||||
): KyselyQueryBuilder;
|
): KyselyQueryBuilder;
|
||||||
|
|
||||||
getReferenceQuery(entity: Entity, id: number, reference: string): Partial<RepoQuery> {
|
getReferenceQuery(entity: Entity, id: PrimaryFieldType, reference: string): Partial<RepoQuery> {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { type Static, StringEnum } from "core/utils";
|
import { type Static, StringEnum } from "core/utils";
|
||||||
import type { EntityManager } from "../entities";
|
import type { EntityManager } from "../entities";
|
||||||
import { Field, baseFieldConfigSchema } from "../fields";
|
import { Field, baseFieldConfigSchema, primaryFieldTypes } from "../fields";
|
||||||
import type { EntityRelation } from "./EntityRelation";
|
import type { EntityRelation } from "./EntityRelation";
|
||||||
import type { EntityRelationAnchor } from "./EntityRelationAnchor";
|
import type { EntityRelationAnchor } from "./EntityRelationAnchor";
|
||||||
import * as tbbox from "@sinclair/typebox";
|
import * as tbbox from "@sinclair/typebox";
|
||||||
@@ -15,6 +15,7 @@ export const relationFieldConfigSchema = Type.Composite([
|
|||||||
reference: Type.String(),
|
reference: Type.String(),
|
||||||
target: Type.String(), // @todo: potentially has to be an instance!
|
target: Type.String(), // @todo: potentially has to be an instance!
|
||||||
target_field: Type.Optional(Type.String({ default: "id" })),
|
target_field: Type.Optional(Type.String({ default: "id" })),
|
||||||
|
target_field_type: Type.Optional(StringEnum(["integer", "text"], { default: "integer" })),
|
||||||
on_delete: Type.Optional(StringEnum(CASCADES, { default: "set null" })),
|
on_delete: Type.Optional(StringEnum(CASCADES, { default: "set null" })),
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
@@ -45,6 +46,7 @@ export class RelationField extends Field<RelationFieldConfig> {
|
|||||||
reference: target.reference,
|
reference: target.reference,
|
||||||
target: target.entity.name,
|
target: target.entity.name,
|
||||||
target_field: target.entity.getPrimaryField().name,
|
target_field: target.entity.getPrimaryField().name,
|
||||||
|
target_field_type: target.entity.getPrimaryField().fieldType,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,7 +65,7 @@ export class RelationField extends Field<RelationFieldConfig> {
|
|||||||
override schema() {
|
override schema() {
|
||||||
return Object.freeze({
|
return Object.freeze({
|
||||||
...super.schema()!,
|
...super.schema()!,
|
||||||
type: "integer",
|
type: this.config.target_field_type ?? "integer",
|
||||||
references: `${this.config.target}.${this.config.target_field}`,
|
references: `${this.config.target}.${this.config.target_field}`,
|
||||||
onDelete: this.config.on_delete ?? "set null",
|
onDelete: this.config.on_delete ?? "set null",
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import type { CompiledQuery, TableMetadata } from "kysely";
|
|||||||
import type { IndexMetadata, SchemaResponse } from "../connection/Connection";
|
import type { IndexMetadata, SchemaResponse } from "../connection/Connection";
|
||||||
import type { Entity, EntityManager } from "../entities";
|
import type { Entity, EntityManager } from "../entities";
|
||||||
import { PrimaryField } from "../fields";
|
import { PrimaryField } from "../fields";
|
||||||
|
import { $console } from "core";
|
||||||
|
|
||||||
type IntrospectedTable = TableMetadata & {
|
type IntrospectedTable = TableMetadata & {
|
||||||
indices: IndexMetadata[];
|
indices: IndexMetadata[];
|
||||||
@@ -332,6 +333,7 @@ export class SchemaManager {
|
|||||||
|
|
||||||
if (config.force) {
|
if (config.force) {
|
||||||
try {
|
try {
|
||||||
|
$console.debug("[SchemaManager]", sql);
|
||||||
await qb.execute();
|
await qb.execute();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new Error(`Failed to execute query: ${sql}: ${(e as any).message}`);
|
throw new Error(`Failed to execute query: ${sql}: ${(e as any).message}`);
|
||||||
|
|||||||
@@ -183,7 +183,7 @@ export class ModuleManager {
|
|||||||
const context = this.ctx(true);
|
const context = this.ctx(true);
|
||||||
|
|
||||||
for (const key in MODULES) {
|
for (const key in MODULES) {
|
||||||
const moduleConfig = key in initial ? initial[key] : {};
|
const moduleConfig = initial && key in initial ? initial[key] : {};
|
||||||
const module = new MODULES[key](moduleConfig, context) as Module;
|
const module = new MODULES[key](moduleConfig, context) as Module;
|
||||||
module.setListener(async (c) => {
|
module.setListener(async (c) => {
|
||||||
await this.onModuleConfigUpdated(key, c);
|
await this.onModuleConfigUpdated(key, c);
|
||||||
|
|||||||
@@ -50,11 +50,11 @@ export class AdminController extends Controller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get basepath() {
|
get basepath() {
|
||||||
return this.options.basepath ?? "/";
|
return this.withAdminBasePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
private withBasePath(route: string = "") {
|
private withBasePath(route: string = "") {
|
||||||
return (this.basepath + route).replace(/(?<!:)\/+/g, "/");
|
return (this.options.basepath + route).replace(/(?<!:)\/+/g, "/");
|
||||||
}
|
}
|
||||||
|
|
||||||
private withAdminBasePath(route: string = "") {
|
private withAdminBasePath(route: string = "") {
|
||||||
@@ -80,10 +80,34 @@ export class AdminController extends Controller {
|
|||||||
loggedOut: configs.auth.cookie.pathLoggedOut ?? this.withAdminBasePath("/"),
|
loggedOut: configs.auth.cookie.pathLoggedOut ?? this.withAdminBasePath("/"),
|
||||||
login: this.withAdminBasePath("/auth/login"),
|
login: this.withAdminBasePath("/auth/login"),
|
||||||
register: this.withAdminBasePath("/auth/register"),
|
register: this.withAdminBasePath("/auth/register"),
|
||||||
logout: this.withAdminBasePath("/auth/logout"),
|
logout: "/api/auth/logout",
|
||||||
};
|
};
|
||||||
|
|
||||||
hono.use("*", async (c, next) => {
|
const paths = ["/", "/data/*", "/auth/*", "/media/*", "/flows/*", "/settings/*"];
|
||||||
|
if (isDebug()) {
|
||||||
|
paths.push("/test/*");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const path of paths) {
|
||||||
|
hono.get(
|
||||||
|
path,
|
||||||
|
permission(SystemPermissions.accessAdmin, {
|
||||||
|
onDenied: async (c) => {
|
||||||
|
if (!path.startsWith("/auth")) {
|
||||||
|
addFlashMessage(c, "You are not authorized to access the Admin UI", "error");
|
||||||
|
|
||||||
|
$console.log("redirecting", authRoutes.login);
|
||||||
|
return c.redirect(authRoutes.login);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
permission(SystemPermissions.schemaRead, {
|
||||||
|
onDenied: async (c) => {
|
||||||
|
addFlashMessage(c, "You not allowed to read the schema", "warning");
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
async (c) => {
|
||||||
const obj = {
|
const obj = {
|
||||||
user: c.get("auth")?.user,
|
user: c.get("auth")?.user,
|
||||||
logout_route: authRoutes.logout,
|
logout_route: authRoutes.logout,
|
||||||
@@ -95,10 +119,12 @@ export class AdminController extends Controller {
|
|||||||
// re-casting to void as a return is not required
|
// re-casting to void as a return is not required
|
||||||
return c.notFound() as unknown as void;
|
return c.notFound() as unknown as void;
|
||||||
}
|
}
|
||||||
c.set("html", html);
|
|
||||||
|
|
||||||
await next();
|
await auth.authenticator?.requestCookieRefresh(c);
|
||||||
});
|
return c.html(html);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (auth_enabled) {
|
if (auth_enabled) {
|
||||||
const redirectRouteParams = [
|
const redirectRouteParams = [
|
||||||
@@ -126,27 +152,6 @@ export class AdminController extends Controller {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// @todo: only load known paths
|
|
||||||
hono.get(
|
|
||||||
"/*",
|
|
||||||
permission(SystemPermissions.accessAdmin, {
|
|
||||||
onDenied: async (c) => {
|
|
||||||
addFlashMessage(c, "You are not authorized to access the Admin UI", "error");
|
|
||||||
|
|
||||||
$console.log("redirecting");
|
|
||||||
return c.redirect(authRoutes.login);
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
permission(SystemPermissions.schemaRead, {
|
|
||||||
onDenied: async (c) => {
|
|
||||||
addFlashMessage(c, "You not allowed to read the schema", "warning");
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
async (c) => {
|
|
||||||
return c.html(c.get("html")!);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
return hono;
|
return hono;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,9 +199,13 @@ export class AdminController extends Controller {
|
|||||||
}).then((res) => res.default);
|
}).then((res) => res.default);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
// @todo: load all marked as entry (incl. css)
|
// @todo: load all marked as entry (incl. css)
|
||||||
assets.js = manifest["src/ui/main.tsx"].file;
|
assets.js = manifest["src/ui/main.tsx"].file;
|
||||||
assets.css = manifest["src/ui/main.tsx"].css[0] as any;
|
assets.css = manifest["src/ui/main.tsx"].css[0] as any;
|
||||||
|
} catch (e) {
|
||||||
|
$console.warn("Couldn't find assets in manifest", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const favicon = isProd ? this.options.assetsPath + "favicon.ico" : "/favicon.ico";
|
const favicon = isProd ? this.options.assetsPath + "favicon.ico" : "/favicon.ico";
|
||||||
|
|||||||
@@ -331,6 +331,6 @@ export class SystemController extends Controller {
|
|||||||
);
|
);
|
||||||
hono.get("/swagger", swaggerUI({ url: "/api/system/openapi.json" }));
|
hono.get("/swagger", swaggerUI({ url: "/api/system/openapi.json" }));
|
||||||
|
|
||||||
return hono.all("*", (c) => c.notFound());
|
return hono;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,15 +104,17 @@ export function DataTable<Data extends Record<string, any> = Record<string, any>
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className={twMerge(
|
className={twMerge(
|
||||||
"link hover:bg-primary/5 py-1.5 rounded-md inline-flex flex-row justify-start items-center gap-1",
|
"py-1.5 rounded-md inline-flex flex-row justify-start items-center gap-1",
|
||||||
onClickSort ? "pl-2.5 pr-1" : "px-2.5",
|
onClickSort
|
||||||
|
? "link hover:bg-primary/5 pl-2.5 pr-1"
|
||||||
|
: "px-2.5",
|
||||||
)}
|
)}
|
||||||
onClick={() => onClickSort?.(property)}
|
onClick={() => onClickSort?.(property)}
|
||||||
>
|
>
|
||||||
<span className="text-left text-nowrap whitespace-nowrap">
|
<span className="text-left text-nowrap whitespace-nowrap">
|
||||||
{label}
|
{label}
|
||||||
</span>
|
</span>
|
||||||
{onClickSort && (
|
{(onClickSort || (sort && sort.by === property)) && (
|
||||||
<SortIndicator sort={sort} field={property} />
|
<SortIndicator sort={sort} field={property} />
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import type { Api } from "bknd/client";
|
import type { Api } from "bknd/client";
|
||||||
|
import type { PrimaryFieldType } from "core";
|
||||||
import type { RepoQueryIn } from "data";
|
import type { RepoQueryIn } from "data";
|
||||||
import type { MediaFieldSchema } from "media/AppMedia";
|
import type { MediaFieldSchema } from "media/AppMedia";
|
||||||
import type { TAppMediaConfig } from "media/media-schema";
|
import type { TAppMediaConfig } from "media/media-schema";
|
||||||
import { useId, useEffect, useRef, useState } from "react";
|
import { useId, useEffect, useRef, useState } from "react";
|
||||||
import { useApi, useApiInfiniteQuery, useApiQuery, useInvalidate } from "ui/client";
|
import { useApi, useApiInfiniteQuery, useApiQuery, useInvalidate } from "bknd/client";
|
||||||
import { useEvent } from "ui/hooks/use-event";
|
import { useEvent } from "ui/hooks/use-event";
|
||||||
import { Dropzone, type DropzoneProps } from "./Dropzone";
|
import { Dropzone, type DropzoneProps } from "./Dropzone";
|
||||||
import { mediaItemsToFileStates } from "./helper";
|
import { mediaItemsToFileStates } from "./helper";
|
||||||
@@ -14,7 +15,7 @@ export type DropzoneContainerProps = {
|
|||||||
infinite?: boolean;
|
infinite?: boolean;
|
||||||
entity?: {
|
entity?: {
|
||||||
name: string;
|
name: string;
|
||||||
id: number;
|
id: PrimaryFieldType;
|
||||||
field: string;
|
field: string;
|
||||||
};
|
};
|
||||||
media?: Pick<TAppMediaConfig, "entity_name" | "storage">;
|
media?: Pick<TAppMediaConfig, "entity_name" | "storage">;
|
||||||
|
|||||||
@@ -1,37 +1,43 @@
|
|||||||
import { decodeSearch, encodeSearch, parseDecode } from "core/utils";
|
import { decodeSearch, encodeSearch, mergeObject } from "core/utils";
|
||||||
import { isEqual, transform } from "lodash-es";
|
import { isEqual, transform } from "lodash-es";
|
||||||
import { useLocation, useSearch as useWouterSearch } from "wouter";
|
import { useLocation, useSearch as useWouterSearch } from "wouter";
|
||||||
import { type s, parse } from "core/object/schema";
|
import { type s, parse } from "core/object/schema";
|
||||||
|
import { useEffect, useMemo, useState } from "react";
|
||||||
|
|
||||||
|
export type UseSearchOptions<Schema extends s.TAnySchema = s.TAnySchema> = {
|
||||||
|
defaultValue?: Partial<s.StaticCoerced<Schema>>;
|
||||||
|
beforeEncode?: (search: Partial<s.StaticCoerced<Schema>>) => object;
|
||||||
|
};
|
||||||
|
|
||||||
// @todo: migrate to Typebox
|
|
||||||
export function useSearch<Schema extends s.TAnySchema = s.TAnySchema>(
|
export function useSearch<Schema extends s.TAnySchema = s.TAnySchema>(
|
||||||
schema: Schema,
|
schema: Schema,
|
||||||
defaultValue?: Partial<s.StaticCoerced<Schema>>,
|
options?: UseSearchOptions<Schema>,
|
||||||
) {
|
) {
|
||||||
const searchString = useWouterSearch();
|
const searchString = useWouterSearch();
|
||||||
const [location, navigate] = useLocation();
|
const [location, navigate] = useLocation();
|
||||||
const initial = searchString.length > 0 ? decodeSearch(searchString) : (defaultValue ?? {});
|
const [value, setValue] = useState<s.StaticCoerced<Schema>>(
|
||||||
const value = parse(schema, initial, {
|
options?.defaultValue ?? ({} as any),
|
||||||
withDefaults: true,
|
|
||||||
clone: true,
|
|
||||||
}) as s.StaticCoerced<Schema>;
|
|
||||||
|
|
||||||
// @todo: add option to set multiple keys at once
|
|
||||||
function set<Key extends keyof s.StaticCoerced<Schema>>(
|
|
||||||
key: Key,
|
|
||||||
value: s.StaticCoerced<Schema>[Key],
|
|
||||||
): void {
|
|
||||||
//console.log("set", key, value);
|
|
||||||
const update = parse(schema, { ...decodeSearch(searchString), [key]: value });
|
|
||||||
const search = transform(
|
|
||||||
update as any,
|
|
||||||
(result, value, key) => {
|
|
||||||
if (defaultValue && isEqual(value, defaultValue[key])) return;
|
|
||||||
result[key] = value;
|
|
||||||
},
|
|
||||||
{} as s.StaticCoerced<Schema>,
|
|
||||||
);
|
);
|
||||||
const encoded = encodeSearch(search, { encode: false });
|
|
||||||
|
const defaults = useMemo(() => {
|
||||||
|
return mergeObject(
|
||||||
|
// @ts-ignore
|
||||||
|
schema.template({ withOptional: true }),
|
||||||
|
options?.defaultValue ?? {},
|
||||||
|
);
|
||||||
|
}, [JSON.stringify({ schema, dflt: options?.defaultValue })]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const initial =
|
||||||
|
searchString.length > 0 ? decodeSearch(searchString) : (options?.defaultValue ?? {});
|
||||||
|
const v = parse(schema, Object.assign({}, defaults, initial)) as any;
|
||||||
|
setValue(v);
|
||||||
|
}, [searchString, JSON.stringify(options?.defaultValue), location]);
|
||||||
|
|
||||||
|
function set<Update extends Partial<s.StaticCoerced<Schema>>>(update: Update): void {
|
||||||
|
const search = getWithoutDefaults(Object.assign({}, value, update), defaults);
|
||||||
|
const prepared = options?.beforeEncode?.(search) ?? search;
|
||||||
|
const encoded = encodeSearch(prepared, { encode: false });
|
||||||
navigate(location + (encoded.length > 0 ? "?" + encoded : ""));
|
navigate(location + (encoded.length > 0 ? "?" + encoded : ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,3 +46,14 @@ export function useSearch<Schema extends s.TAnySchema = s.TAnySchema>(
|
|||||||
set,
|
set,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getWithoutDefaults(value: object, defaultValue: object) {
|
||||||
|
return transform(
|
||||||
|
value as any,
|
||||||
|
(result, value, key) => {
|
||||||
|
if (defaultValue && isEqual(value, defaultValue[key])) return;
|
||||||
|
result[key] = value;
|
||||||
|
},
|
||||||
|
{} as object,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { SegmentedControl, Tooltip } from "@mantine/core";
|
import { SegmentedControl, Tooltip } from "@mantine/core";
|
||||||
import { IconApi, IconKeyOff, IconSettings, IconUser } from "@tabler/icons-react";
|
import { IconApi, IconBook, IconKeyOff, IconSettings, IconUser } from "@tabler/icons-react";
|
||||||
import {
|
import {
|
||||||
TbDatabase,
|
TbDatabase,
|
||||||
TbFingerprint,
|
TbFingerprint,
|
||||||
@@ -24,6 +24,7 @@ import { useLocation } from "wouter";
|
|||||||
import { NavLink } from "./AppShell";
|
import { NavLink } from "./AppShell";
|
||||||
import { autoFormatString } from "core/utils";
|
import { autoFormatString } from "core/utils";
|
||||||
import { appShellStore } from "ui/store";
|
import { appShellStore } from "ui/store";
|
||||||
|
import { getVersion } from "core/env";
|
||||||
|
|
||||||
export function HeaderNavigation() {
|
export function HeaderNavigation() {
|
||||||
const [location, navigate] = useLocation();
|
const [location, navigate] = useLocation();
|
||||||
@@ -164,6 +165,11 @@ function UserMenu() {
|
|||||||
onClick: () => window.open("/api/system/swagger", "_blank"),
|
onClick: () => window.open("/api/system/swagger", "_blank"),
|
||||||
icon: IconApi,
|
icon: IconApi,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: "Docs",
|
||||||
|
onClick: () => window.open("https://docs.bknd.io", "_blank"),
|
||||||
|
icon: IconBook,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
if (config.auth.enabled) {
|
if (config.auth.enabled) {
|
||||||
@@ -182,6 +188,11 @@ function UserMenu() {
|
|||||||
if (!options.theme) {
|
if (!options.theme) {
|
||||||
items.push(() => <UserMenuThemeToggler />);
|
items.push(() => <UserMenuThemeToggler />);
|
||||||
}
|
}
|
||||||
|
items.push(() => (
|
||||||
|
<div className="font-mono leading-none text-xs text-primary/50 text-center pb-1 pt-2 mt-1 border-t border-primary/5">
|
||||||
|
{getVersion()}
|
||||||
|
</div>
|
||||||
|
));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import { EntityRelationalFormField } from "./fields/EntityRelationalFormField";
|
|||||||
import ErrorBoundary from "ui/components/display/ErrorBoundary";
|
import ErrorBoundary from "ui/components/display/ErrorBoundary";
|
||||||
import { Alert } from "ui/components/display/Alert";
|
import { Alert } from "ui/components/display/Alert";
|
||||||
import { bkndModals } from "ui/modals";
|
import { bkndModals } from "ui/modals";
|
||||||
|
import type { PrimaryFieldType } from "core";
|
||||||
|
|
||||||
// simplify react form types 🤦
|
// simplify react form types 🤦
|
||||||
export type FormApi = ReactFormExtendedApi<any, any, any, any, any, any, any, any, any, any>;
|
export type FormApi = ReactFormExtendedApi<any, any, any, any, any, any, any, any, any, any>;
|
||||||
@@ -30,7 +31,7 @@ export type TFieldApi = FieldApi<any, any, any, any, any, any, any, any, any, an
|
|||||||
|
|
||||||
type EntityFormProps = {
|
type EntityFormProps = {
|
||||||
entity: Entity;
|
entity: Entity;
|
||||||
entityId?: number;
|
entityId?: PrimaryFieldType;
|
||||||
data?: EntityData;
|
data?: EntityData;
|
||||||
handleSubmit: (e: React.FormEvent<HTMLFormElement>) => void;
|
handleSubmit: (e: React.FormEvent<HTMLFormElement>) => void;
|
||||||
fieldsDisabled: boolean;
|
fieldsDisabled: boolean;
|
||||||
@@ -225,7 +226,7 @@ function EntityMediaFormField({
|
|||||||
formApi: FormApi;
|
formApi: FormApi;
|
||||||
field: MediaField;
|
field: MediaField;
|
||||||
entity: Entity;
|
entity: Entity;
|
||||||
entityId?: number;
|
entityId?: PrimaryFieldType;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
}) {
|
}) {
|
||||||
if (!entityId) return;
|
if (!entityId) return;
|
||||||
|
|||||||
@@ -11,12 +11,14 @@ import {
|
|||||||
type EntityFieldsFormRef,
|
type EntityFieldsFormRef,
|
||||||
} from "ui/routes/data/forms/entity.fields.form";
|
} from "ui/routes/data/forms/entity.fields.form";
|
||||||
import { ModalBody, ModalFooter, type TCreateModalSchema, useStepContext } from "./CreateModal";
|
import { ModalBody, ModalFooter, type TCreateModalSchema, useStepContext } from "./CreateModal";
|
||||||
|
import { useBkndData } from "ui/client/schema/data/use-bknd-data";
|
||||||
|
|
||||||
const schema = entitiesSchema;
|
const schema = entitiesSchema;
|
||||||
type Schema = Static<typeof schema>;
|
type Schema = Static<typeof schema>;
|
||||||
|
|
||||||
export function StepEntityFields() {
|
export function StepEntityFields() {
|
||||||
const { nextStep, stepBack, state, setState } = useStepContext<TCreateModalSchema>();
|
const { nextStep, stepBack, state, setState } = useStepContext<TCreateModalSchema>();
|
||||||
|
const { config } = useBkndData();
|
||||||
const entity = state.entities?.create?.[0]!;
|
const entity = state.entities?.create?.[0]!;
|
||||||
const defaultFields = { id: { type: "primary", name: "id" } } as const;
|
const defaultFields = { id: { type: "primary", name: "id" } } as const;
|
||||||
const ref = useRef<EntityFieldsFormRef>(null);
|
const ref = useRef<EntityFieldsFormRef>(null);
|
||||||
@@ -82,6 +84,8 @@ export function StepEntityFields() {
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
fields={initial.fields as any}
|
fields={initial.fields as any}
|
||||||
onChange={updateListener}
|
onChange={updateListener}
|
||||||
|
defaultPrimaryFormat={config?.default_primary_format}
|
||||||
|
isNew={true}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -10,12 +10,13 @@ import {
|
|||||||
entitySchema,
|
entitySchema,
|
||||||
useStepContext,
|
useStepContext,
|
||||||
} from "./CreateModal";
|
} from "./CreateModal";
|
||||||
|
import { MantineSelect } from "ui/components/form/hook-form-mantine/MantineSelect";
|
||||||
|
|
||||||
export function StepEntity() {
|
export function StepEntity() {
|
||||||
const focusTrapRef = useFocusTrap();
|
const focusTrapRef = useFocusTrap();
|
||||||
|
|
||||||
const { nextStep, stepBack, state, setState } = useStepContext<TCreateModalSchema>();
|
const { nextStep, stepBack, state, setState } = useStepContext<TCreateModalSchema>();
|
||||||
const { register, handleSubmit, formState, watch } = useForm({
|
const { register, handleSubmit, formState, watch, control } = useForm({
|
||||||
mode: "onTouched",
|
mode: "onTouched",
|
||||||
resolver: typeboxResolver(entitySchema),
|
resolver: typeboxResolver(entitySchema),
|
||||||
defaultValues: state.entities?.create?.[0] ?? {},
|
defaultValues: state.entities?.create?.[0] ?? {},
|
||||||
@@ -56,7 +57,6 @@ export function StepEntity() {
|
|||||||
label="What's the name of the entity?"
|
label="What's the name of the entity?"
|
||||||
description="Use plural form, and all lowercase. It will be used as the database table."
|
description="Use plural form, and all lowercase. It will be used as the database table."
|
||||||
/>
|
/>
|
||||||
{/*<input type="submit" value="submit" />*/}
|
|
||||||
<TextInput
|
<TextInput
|
||||||
{...register("config.name")}
|
{...register("config.name")}
|
||||||
error={formState.errors.config?.name?.message}
|
error={formState.errors.config?.name?.message}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import type { PrimaryFieldType } from "core";
|
||||||
import { ucFirst } from "core/utils";
|
import { ucFirst } from "core/utils";
|
||||||
import type { Entity, EntityData, EntityRelation } from "data";
|
import type { Entity, EntityData, EntityRelation } from "data";
|
||||||
import { Fragment, useState } from "react";
|
import { Fragment, useState } from "react";
|
||||||
@@ -24,7 +25,7 @@ export function DataEntityUpdate({ params }) {
|
|||||||
return <Message.NotFound description={`Entity "${params.entity}" doesn't exist.`} />;
|
return <Message.NotFound description={`Entity "${params.entity}" doesn't exist.`} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const entityId = Number.parseInt(params.id as string);
|
const entityId = params.id as PrimaryFieldType;
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [navigate] = useNavigate();
|
const [navigate] = useNavigate();
|
||||||
useBrowserTitle(["Data", entity.label, `#${entityId}`]);
|
useBrowserTitle(["Data", entity.label, `#${entityId}`]);
|
||||||
@@ -202,7 +203,7 @@ function EntityDetailRelations({
|
|||||||
entity,
|
entity,
|
||||||
relations,
|
relations,
|
||||||
}: {
|
}: {
|
||||||
id: number;
|
id: PrimaryFieldType;
|
||||||
entity: Entity;
|
entity: Entity;
|
||||||
relations: EntityRelation[];
|
relations: EntityRelation[];
|
||||||
}) {
|
}) {
|
||||||
@@ -250,21 +251,26 @@ function EntityDetailInner({
|
|||||||
entity,
|
entity,
|
||||||
relation,
|
relation,
|
||||||
}: {
|
}: {
|
||||||
id: number;
|
id: PrimaryFieldType;
|
||||||
entity: Entity;
|
entity: Entity;
|
||||||
relation: EntityRelation;
|
relation: EntityRelation;
|
||||||
}) {
|
}) {
|
||||||
const other = relation.other(entity);
|
const other = relation.other(entity);
|
||||||
const [navigate] = useNavigate();
|
const [navigate] = useNavigate();
|
||||||
|
const [search, setSearch] = useState({
|
||||||
const search = {
|
|
||||||
select: other.entity.getSelect(undefined, "table"),
|
select: other.entity.getSelect(undefined, "table"),
|
||||||
|
sort: other.entity.getDefaultSort(),
|
||||||
limit: 10,
|
limit: 10,
|
||||||
offset: 0,
|
offset: 0,
|
||||||
};
|
});
|
||||||
|
|
||||||
// @todo: add custom key for invalidation
|
// @todo: add custom key for invalidation
|
||||||
const $q = useApiQuery((api) =>
|
const $q = useApiQuery(
|
||||||
api.data.readManyByReference(entity.name, id, other.reference, search),
|
(api) => api.data.readManyByReference(entity.name, id, other.reference, search),
|
||||||
|
{
|
||||||
|
keepPreviousData: true,
|
||||||
|
revalidateOnFocus: true,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
function handleClickRow(row: Record<string, any>) {
|
function handleClickRow(row: Record<string, any>) {
|
||||||
@@ -299,11 +305,17 @@ function EntityDetailInner({
|
|||||||
select={search.select}
|
select={search.select}
|
||||||
data={$q.data ?? null}
|
data={$q.data ?? null}
|
||||||
entity={other.entity}
|
entity={other.entity}
|
||||||
|
sort={search.sort}
|
||||||
onClickRow={handleClickRow}
|
onClickRow={handleClickRow}
|
||||||
onClickNew={handleClickNew}
|
onClickNew={handleClickNew}
|
||||||
page={1}
|
page={Math.floor(search.offset / search.limit) + 1}
|
||||||
total={$q.data?.body?.meta?.count ?? 1}
|
total={$q.data?.body?.meta?.count ?? 1}
|
||||||
/*onClickPage={handleClickPage}*/
|
onClickPage={(page) => {
|
||||||
|
setSearch((s) => ({
|
||||||
|
...s,
|
||||||
|
offset: (page - 1) * s.limit,
|
||||||
|
}));
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ const searchSchema = s.partialObject({
|
|||||||
perPage: s.number({ default: 10 }).optional(),
|
perPage: s.number({ default: 10 }).optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const PER_PAGE_OPTIONS = [5, 10, 25];
|
const PER_PAGE_OPTIONS = [5, 10, 25, 50, 100];
|
||||||
|
|
||||||
export function DataEntityList({ params }) {
|
export function DataEntityList({ params }) {
|
||||||
const { $data } = useBkndData();
|
const { $data } = useBkndData();
|
||||||
@@ -35,8 +35,19 @@ export function DataEntityList({ params }) {
|
|||||||
useBrowserTitle(["Data", entity?.label ?? params.entity]);
|
useBrowserTitle(["Data", entity?.label ?? params.entity]);
|
||||||
const [navigate] = useNavigate();
|
const [navigate] = useNavigate();
|
||||||
const search = useSearch(searchSchema, {
|
const search = useSearch(searchSchema, {
|
||||||
|
defaultValue: {
|
||||||
select: entity.getSelect(undefined, "table"),
|
select: entity.getSelect(undefined, "table"),
|
||||||
sort: entity.getDefaultSort(),
|
sort: entity.getDefaultSort(),
|
||||||
|
},
|
||||||
|
beforeEncode: (v) => {
|
||||||
|
if ("sort" in v && v.sort) {
|
||||||
|
return {
|
||||||
|
...v,
|
||||||
|
sort: `${v.sort.dir === "asc" ? "" : "-"}${v.sort.by}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const $q = useApiQuery(
|
const $q = useApiQuery(
|
||||||
@@ -61,19 +72,18 @@ export function DataEntityList({ params }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleClickPage(page: number) {
|
function handleClickPage(page: number) {
|
||||||
search.set("page", page);
|
search.set({ page });
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSortClick(name: string) {
|
function handleSortClick(name: string) {
|
||||||
const sort = search.value.sort!;
|
const sort = search.value.sort!;
|
||||||
const newSort = { by: name, dir: sort.by === name && sort.dir === "asc" ? "desc" : "asc" };
|
const newSort = { by: name, dir: sort.by === name && sort.dir === "asc" ? "desc" : "asc" };
|
||||||
|
|
||||||
search.set("sort", newSort as any);
|
search.set({ sort: newSort as any });
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleClickPerPage(perPage: number) {
|
function handleClickPerPage(perPage: number) {
|
||||||
// @todo: also reset page to 1
|
search.set({ perPage, page: 1 });
|
||||||
search.set("perPage", perPage);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const isUpdating = $q.isLoading || $q.isValidating;
|
const isUpdating = $q.isLoading || $q.isValidating;
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ export function DataSchemaEntity({ params }) {
|
|||||||
const Fields = ({ entity }: { entity: Entity }) => {
|
const Fields = ({ entity }: { entity: Entity }) => {
|
||||||
const [submitting, setSubmitting] = useState(false);
|
const [submitting, setSubmitting] = useState(false);
|
||||||
const [updates, setUpdates] = useState(0);
|
const [updates, setUpdates] = useState(0);
|
||||||
const { actions, $data } = useBkndData();
|
const { actions, $data, config } = useBkndData();
|
||||||
const [res, setRes] = useState<any>();
|
const [res, setRes] = useState<any>();
|
||||||
const ref = useRef<EntityFieldsFormRef>(null);
|
const ref = useRef<EntityFieldsFormRef>(null);
|
||||||
async function handleUpdate() {
|
async function handleUpdate() {
|
||||||
@@ -201,6 +201,8 @@ const Fields = ({ entity }: { entity: Entity }) => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
}))}
|
}))}
|
||||||
|
defaultPrimaryFormat={config?.default_primary_format}
|
||||||
|
isNew={false}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{isDebug() && (
|
{isDebug() && (
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ import { type TFieldSpec, fieldSpecs } from "ui/modules/data/components/fields-s
|
|||||||
import { dataFieldsUiSchema } from "../../settings/routes/data.settings";
|
import { dataFieldsUiSchema } from "../../settings/routes/data.settings";
|
||||||
import * as tbbox from "@sinclair/typebox";
|
import * as tbbox from "@sinclair/typebox";
|
||||||
import { useRoutePathState } from "ui/hooks/use-route-path-state";
|
import { useRoutePathState } from "ui/hooks/use-route-path-state";
|
||||||
|
import { MantineSelect } from "ui/components/form/hook-form-mantine/MantineSelect";
|
||||||
|
import type { TPrimaryFieldFormat } from "data/fields/PrimaryField";
|
||||||
const { Type } = tbbox;
|
const { Type } = tbbox;
|
||||||
|
|
||||||
const fieldsSchemaObject = originalFieldsSchemaObject;
|
const fieldsSchemaObject = originalFieldsSchemaObject;
|
||||||
@@ -65,6 +67,8 @@ export type EntityFieldsFormProps = {
|
|||||||
sortable?: boolean;
|
sortable?: boolean;
|
||||||
additionalFieldTypes?: (TFieldSpec & { onClick: () => void })[];
|
additionalFieldTypes?: (TFieldSpec & { onClick: () => void })[];
|
||||||
routePattern?: string;
|
routePattern?: string;
|
||||||
|
defaultPrimaryFormat?: TPrimaryFieldFormat;
|
||||||
|
isNew?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type EntityFieldsFormRef = {
|
export type EntityFieldsFormRef = {
|
||||||
@@ -77,7 +81,7 @@ export type EntityFieldsFormRef = {
|
|||||||
|
|
||||||
export const EntityFieldsForm = forwardRef<EntityFieldsFormRef, EntityFieldsFormProps>(
|
export const EntityFieldsForm = forwardRef<EntityFieldsFormRef, EntityFieldsFormProps>(
|
||||||
function EntityFieldsForm(
|
function EntityFieldsForm(
|
||||||
{ fields: _fields, sortable, additionalFieldTypes, routePattern, ...props },
|
{ fields: _fields, sortable, additionalFieldTypes, routePattern, isNew, ...props },
|
||||||
ref,
|
ref,
|
||||||
) {
|
) {
|
||||||
const entityFields = Object.entries(_fields).map(([name, field]) => ({
|
const entityFields = Object.entries(_fields).map(([name, field]) => ({
|
||||||
@@ -172,6 +176,10 @@ export const EntityFieldsForm = forwardRef<EntityFieldsFormRef, EntityFieldsForm
|
|||||||
remove={remove}
|
remove={remove}
|
||||||
dnd={dnd}
|
dnd={dnd}
|
||||||
routePattern={routePattern}
|
routePattern={routePattern}
|
||||||
|
primary={{
|
||||||
|
defaultFormat: props.defaultPrimaryFormat,
|
||||||
|
editable: isNew,
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
@@ -186,6 +194,10 @@ export const EntityFieldsForm = forwardRef<EntityFieldsFormRef, EntityFieldsForm
|
|||||||
errors={errors}
|
errors={errors}
|
||||||
remove={remove}
|
remove={remove}
|
||||||
routePattern={routePattern}
|
routePattern={routePattern}
|
||||||
|
primary={{
|
||||||
|
defaultFormat: props.defaultPrimaryFormat,
|
||||||
|
editable: isNew,
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -281,6 +293,7 @@ function EntityField({
|
|||||||
errors,
|
errors,
|
||||||
dnd,
|
dnd,
|
||||||
routePattern,
|
routePattern,
|
||||||
|
primary,
|
||||||
}: {
|
}: {
|
||||||
field: FieldArrayWithId<TFieldsFormSchema, "fields", "id">;
|
field: FieldArrayWithId<TFieldsFormSchema, "fields", "id">;
|
||||||
index: number;
|
index: number;
|
||||||
@@ -292,6 +305,10 @@ function EntityField({
|
|||||||
errors: any;
|
errors: any;
|
||||||
dnd?: SortableItemProps;
|
dnd?: SortableItemProps;
|
||||||
routePattern?: string;
|
routePattern?: string;
|
||||||
|
primary?: {
|
||||||
|
defaultFormat?: TPrimaryFieldFormat;
|
||||||
|
editable?: boolean;
|
||||||
|
};
|
||||||
}) {
|
}) {
|
||||||
const prefix = `fields.${index}.field` as const;
|
const prefix = `fields.${index}.field` as const;
|
||||||
const type = field.field.type;
|
const type = field.field.type;
|
||||||
@@ -363,15 +380,29 @@ function EntityField({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="flex-col gap-1 hidden md:flex">
|
<div className="flex-col gap-1 hidden md:flex">
|
||||||
<span className="text-xs text-primary/50 leading-none">Required</span>
|
|
||||||
{is_primary ? (
|
{is_primary ? (
|
||||||
<Switch size="sm" defaultChecked disabled />
|
<>
|
||||||
|
<MantineSelect
|
||||||
|
data={["integer", "uuid"]}
|
||||||
|
defaultValue={primary?.defaultFormat}
|
||||||
|
disabled={!primary?.editable}
|
||||||
|
placeholder="Select format"
|
||||||
|
name={`${prefix}.config.format`}
|
||||||
|
allowDeselect={false}
|
||||||
|
control={control}
|
||||||
|
size="xs"
|
||||||
|
className="w-22"
|
||||||
|
/>
|
||||||
|
</>
|
||||||
) : (
|
) : (
|
||||||
|
<>
|
||||||
|
<span className="text-xs text-primary/50 leading-none">Required</span>
|
||||||
<MantineSwitch
|
<MantineSwitch
|
||||||
size="sm"
|
size="sm"
|
||||||
name={`${prefix}.config.required`}
|
name={`${prefix}.config.required`}
|
||||||
control={control}
|
control={control}
|
||||||
/>
|
/>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"types": ["bun-types", "@cloudflare/workers-types"],
|
"types": ["bun-types"],
|
||||||
"composite": false,
|
"composite": false,
|
||||||
"incremental": true,
|
"incremental": true,
|
||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
@@ -30,7 +30,14 @@
|
|||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"outDir": "./dist/types",
|
"outDir": "./dist/types",
|
||||||
"paths": {
|
"paths": {
|
||||||
"*": ["./src/*"]
|
"*": ["./src/*"],
|
||||||
|
"bknd": ["./src/index.ts"],
|
||||||
|
"bknd/core": ["./src/core/index.ts"],
|
||||||
|
"bknd/adapter": ["./src/adapter/index.ts"],
|
||||||
|
"bknd/client": ["./src/ui/client/index.ts"],
|
||||||
|
"bknd/data": ["./src/data/index.ts"],
|
||||||
|
"bknd/media": ["./src/media/index.ts"],
|
||||||
|
"bknd/auth": ["./src/auth/index.ts"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
|
|||||||
319
bun.lock
319
bun.lock
@@ -6,7 +6,6 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "1.9.4",
|
"@biomejs/biome": "1.9.4",
|
||||||
"@clack/prompts": "^0.10.0",
|
"@clack/prompts": "^0.10.0",
|
||||||
"@cloudflare/workers-types": "^4.20240620.0",
|
|
||||||
"@tsconfig/strictest": "^2.0.5",
|
"@tsconfig/strictest": "^2.0.5",
|
||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"bun-types": "^1.1.18",
|
"bun-types": "^1.1.18",
|
||||||
@@ -27,7 +26,7 @@
|
|||||||
},
|
},
|
||||||
"app": {
|
"app": {
|
||||||
"name": "bknd",
|
"name": "bknd",
|
||||||
"version": "0.12.0",
|
"version": "0.14.0-rc.0",
|
||||||
"bin": "./dist/cli/index.js",
|
"bin": "./dist/cli/index.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@cfworker/json-schema": "^4.1.1",
|
"@cfworker/json-schema": "^4.1.1",
|
||||||
@@ -46,7 +45,7 @@
|
|||||||
"bcryptjs": "^3.0.2",
|
"bcryptjs": "^3.0.2",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
"fast-xml-parser": "^5.0.8",
|
"fast-xml-parser": "^5.0.8",
|
||||||
"hono": "^4.7.4",
|
"hono": "^4.7.11",
|
||||||
"json-schema-form-react": "^0.0.2",
|
"json-schema-form-react": "^0.0.2",
|
||||||
"json-schema-library": "10.0.0-rc7",
|
"json-schema-library": "10.0.0-rc7",
|
||||||
"json-schema-to-ts": "^3.1.1",
|
"json-schema-to-ts": "^3.1.1",
|
||||||
@@ -56,13 +55,15 @@
|
|||||||
"object-path-immutable": "^4.1.2",
|
"object-path-immutable": "^4.1.2",
|
||||||
"radix-ui": "^1.1.3",
|
"radix-ui": "^1.1.3",
|
||||||
"swr": "^2.3.3",
|
"swr": "^2.3.3",
|
||||||
|
"uuid": "^11.1.0",
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@aws-sdk/client-s3": "^3.758.0",
|
"@aws-sdk/client-s3": "^3.758.0",
|
||||||
"@bluwy/giget-core": "^0.1.2",
|
"@bluwy/giget-core": "^0.1.2",
|
||||||
|
"@cloudflare/workers-types": "^4.20250606.0",
|
||||||
"@dagrejs/dagre": "^1.1.4",
|
"@dagrejs/dagre": "^1.1.4",
|
||||||
"@hono/typebox-validator": "^0.3.2",
|
"@hono/typebox-validator": "^0.3.3",
|
||||||
"@hono/vite-dev-server": "^0.19.0",
|
"@hono/vite-dev-server": "^0.19.1",
|
||||||
"@hookform/resolvers": "^4.1.3",
|
"@hookform/resolvers": "^4.1.3",
|
||||||
"@libsql/kysely-libsql": "^0.4.1",
|
"@libsql/kysely-libsql": "^0.4.1",
|
||||||
"@mantine/modals": "^7.17.1",
|
"@mantine/modals": "^7.17.1",
|
||||||
@@ -84,7 +85,7 @@
|
|||||||
"dotenv": "^16.4.7",
|
"dotenv": "^16.4.7",
|
||||||
"jotai": "^2.12.2",
|
"jotai": "^2.12.2",
|
||||||
"jsdom": "^26.0.0",
|
"jsdom": "^26.0.0",
|
||||||
"jsonv-ts": "^0.0.14-alpha.6",
|
"jsonv-ts": "^0.1.0",
|
||||||
"kysely-d1": "^0.3.0",
|
"kysely-d1": "^0.3.0",
|
||||||
"open": "^10.1.0",
|
"open": "^10.1.0",
|
||||||
"openapi-types": "^12.1.3",
|
"openapi-types": "^12.1.3",
|
||||||
@@ -105,13 +106,13 @@
|
|||||||
"tsc-alias": "^1.8.11",
|
"tsc-alias": "^1.8.11",
|
||||||
"tsup": "^8.4.0",
|
"tsup": "^8.4.0",
|
||||||
"tsx": "^4.19.3",
|
"tsx": "^4.19.3",
|
||||||
"vite": "^6.2.1",
|
"vite": "^6.3.5",
|
||||||
"vite-tsconfig-paths": "^5.1.4",
|
"vite-tsconfig-paths": "^5.1.4",
|
||||||
"vitest": "^3.0.9",
|
"vitest": "^3.0.9",
|
||||||
"wouter": "^3.6.0",
|
"wouter": "^3.6.0",
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@hono/node-server": "^1.13.8",
|
"@hono/node-server": "^1.14.3",
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": ">=19",
|
"react": ">=19",
|
||||||
@@ -132,7 +133,6 @@
|
|||||||
"version": "0.5.1",
|
"version": "0.5.1",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/bun": "latest",
|
"@types/bun": "latest",
|
||||||
"bknd": "workspace:*",
|
|
||||||
"tsdx": "^0.14.1",
|
"tsdx": "^0.14.1",
|
||||||
"typescript": "^5.0.0",
|
"typescript": "^5.0.0",
|
||||||
},
|
},
|
||||||
@@ -149,19 +149,24 @@
|
|||||||
},
|
},
|
||||||
"packages/postgres": {
|
"packages/postgres": {
|
||||||
"name": "@bknd/postgres",
|
"name": "@bknd/postgres",
|
||||||
"version": "0.0.1",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
|
||||||
"kysely": "^0.27.6",
|
|
||||||
"pg": "^8.14.0",
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/bun": "^1.2.5",
|
"@types/bun": "^1.2.5",
|
||||||
"@types/node": "^22.13.10",
|
"@types/node": "^22.13.10",
|
||||||
"@types/pg": "^8.11.11",
|
"@types/pg": "^8.11.11",
|
||||||
|
"@xata.io/client": "^0.0.0-next.v93343b9646f57a1e5c51c35eccf0767c2bb80baa",
|
||||||
|
"@xata.io/kysely": "^0.2.1",
|
||||||
"bknd": "workspace:*",
|
"bknd": "workspace:*",
|
||||||
|
"kysely-neon": "^1.3.0",
|
||||||
"tsup": "^8.4.0",
|
"tsup": "^8.4.0",
|
||||||
"typescript": "^5.8.2",
|
"typescript": "^5.8.2",
|
||||||
},
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"kysely": "^0.27.6",
|
||||||
|
"kysely-postgres-js": "^2.0.0",
|
||||||
|
"pg": "^8.14.0",
|
||||||
|
"postgres": "^3.4.7",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
"packages/sqlocal": {
|
"packages/sqlocal": {
|
||||||
"name": "@bknd/sqlocal",
|
"name": "@bknd/sqlocal",
|
||||||
@@ -523,7 +528,7 @@
|
|||||||
|
|
||||||
"@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20250224.0", "", { "os": "win32", "cpu": "x64" }, "sha512-x2iF1CsmYmmPEorWb1GRpAAouX5rRjmhuHMC259ojIlozR4G0LarlB9XfmeLEvtw537Ea0kJ6SOhjvUcWzxSvA=="],
|
"@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20250224.0", "", { "os": "win32", "cpu": "x64" }, "sha512-x2iF1CsmYmmPEorWb1GRpAAouX5rRjmhuHMC259ojIlozR4G0LarlB9XfmeLEvtw537Ea0kJ6SOhjvUcWzxSvA=="],
|
||||||
|
|
||||||
"@cloudflare/workers-types": ["@cloudflare/workers-types@4.20250310.0", "", {}, "sha512-SNE2ohlL9/VxFbcHQc28n3Nj70FiS1Ea0wrUhCXUIbR2lsr4ceRVndNxhuzhcF9EZd2UXm2wwow34RIS1mm+Mg=="],
|
"@cloudflare/workers-types": ["@cloudflare/workers-types@4.20250606.0", "", {}, "sha512-9T/Y/Mxe57UVzqgfjJKheiMplnStj/3CmCHlgoZNLU8JW2waRbXvpY3EEeliiYAJfeHZTjeAaKO2pCabxAoyCw=="],
|
||||||
|
|
||||||
"@cnakazawa/watch": ["@cnakazawa/watch@1.0.4", "", { "dependencies": { "exec-sh": "^0.3.2", "minimist": "^1.2.0" }, "bin": { "watch": "cli.js" } }, "sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ=="],
|
"@cnakazawa/watch": ["@cnakazawa/watch@1.0.4", "", { "dependencies": { "exec-sh": "^0.3.2", "minimist": "^1.2.0" }, "bin": { "watch": "cli.js" } }, "sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ=="],
|
||||||
|
|
||||||
@@ -639,13 +644,13 @@
|
|||||||
|
|
||||||
"@hello-pangea/dnd": ["@hello-pangea/dnd@18.0.1", "", { "dependencies": { "@babel/runtime": "^7.26.7", "css-box-model": "^1.2.1", "raf-schd": "^4.0.3", "react-redux": "^9.2.0", "redux": "^5.0.1" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-xojVWG8s/TGrKT1fC8K2tIWeejJYTAeJuj36zM//yEm/ZrnZUSFGS15BpO+jGZT1ybWvyXmeDJwPYb4dhWlbZQ=="],
|
"@hello-pangea/dnd": ["@hello-pangea/dnd@18.0.1", "", { "dependencies": { "@babel/runtime": "^7.26.7", "css-box-model": "^1.2.1", "raf-schd": "^4.0.3", "react-redux": "^9.2.0", "redux": "^5.0.1" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-xojVWG8s/TGrKT1fC8K2tIWeejJYTAeJuj36zM//yEm/ZrnZUSFGS15BpO+jGZT1ybWvyXmeDJwPYb4dhWlbZQ=="],
|
||||||
|
|
||||||
"@hono/node-server": ["@hono/node-server@1.13.8", "", { "peerDependencies": { "hono": "^4" } }, "sha512-fsn8ucecsAXUoVxrUil0m13kOEq4mkX4/4QozCqmY+HpGfKl74OYSn8JcMA8GnG0ClfdRI4/ZSeG7zhFaVg+wg=="],
|
"@hono/node-server": ["@hono/node-server@1.14.3", "", { "peerDependencies": { "hono": "^4" } }, "sha512-KuDMwwghtFYSmIpr4WrKs1VpelTrptvJ+6x6mbUcZnFcc213cumTF5BdqfHyW93B19TNI4Vaev14vOI2a0Ie3w=="],
|
||||||
|
|
||||||
"@hono/swagger-ui": ["@hono/swagger-ui@0.5.1", "", { "peerDependencies": { "hono": "*" } }, "sha512-XpUCfszLJ9b1rtFdzqOSHfdg9pfBiC2J5piEjuSanYpDDTIwpMz0ciiv5N3WWUaQpz9fEgH8lttQqL41vIFuDA=="],
|
"@hono/swagger-ui": ["@hono/swagger-ui@0.5.1", "", { "peerDependencies": { "hono": "*" } }, "sha512-XpUCfszLJ9b1rtFdzqOSHfdg9pfBiC2J5piEjuSanYpDDTIwpMz0ciiv5N3WWUaQpz9fEgH8lttQqL41vIFuDA=="],
|
||||||
|
|
||||||
"@hono/typebox-validator": ["@hono/typebox-validator@0.3.2", "", { "peerDependencies": { "@sinclair/typebox": ">=0.31.15 <1", "hono": ">=3.9.0" } }, "sha512-MIxYk80vtuFnkvbNreMubZ/vLoNCCQivLH8n3vNDY5dFNsZ12BFaZV3FmsLJHGibNMMpmkO6y4w5gNWY4KzSdg=="],
|
"@hono/typebox-validator": ["@hono/typebox-validator@0.3.3", "", { "peerDependencies": { "@sinclair/typebox": ">=0.31.15 <1", "hono": ">=3.9.0" } }, "sha512-BH6TOkVKlLIYYX4qfadpkNZDP/knxtCXp4210T9apKioA7q8mq1m3ELEvMMLhrtZBhzPOAlTr83A6RLZ3awDtg=="],
|
||||||
|
|
||||||
"@hono/vite-dev-server": ["@hono/vite-dev-server@0.19.0", "", { "dependencies": { "@hono/node-server": "^1.12.0", "minimatch": "^9.0.3" }, "peerDependencies": { "hono": "*", "miniflare": "*", "wrangler": "*" }, "optionalPeers": ["miniflare", "wrangler"] }, "sha512-myMc4Nm0nFQSPaeE6I/a1ODyDR5KpQ4EHodX4Tu/7qlB31GfUemhUH/WsO91HJjDEpRRpsT4Zbg+PleMlpTljw=="],
|
"@hono/vite-dev-server": ["@hono/vite-dev-server@0.19.1", "", { "dependencies": { "@hono/node-server": "^1.14.2", "minimatch": "^9.0.3" }, "peerDependencies": { "hono": "*", "miniflare": "*", "wrangler": "*" }, "optionalPeers": ["miniflare", "wrangler"] }, "sha512-hh+0u3IxHErEyj4YwHk/U+2f+qAHEQZ9EIQtadG9jeHfxEXH6r/ZecjnpyEkQbDK7JtgEEoVAq/JGOkd3Dvqww=="],
|
||||||
|
|
||||||
"@hookform/resolvers": ["@hookform/resolvers@4.1.3", "", { "dependencies": { "@standard-schema/utils": "^0.3.0" }, "peerDependencies": { "react-hook-form": "^7.0.0" } }, "sha512-Jsv6UOWYTrEFJ/01ZrnwVXs7KDvP8XIo115i++5PWvNkNvkrsTfGiLS6w+eJ57CYtUtDQalUWovCZDHFJ8u1VQ=="],
|
"@hookform/resolvers": ["@hookform/resolvers@4.1.3", "", { "dependencies": { "@standard-schema/utils": "^0.3.0" }, "peerDependencies": { "react-hook-form": "^7.0.0" } }, "sha512-Jsv6UOWYTrEFJ/01ZrnwVXs7KDvP8XIo115i++5PWvNkNvkrsTfGiLS6w+eJ57CYtUtDQalUWovCZDHFJ8u1VQ=="],
|
||||||
|
|
||||||
@@ -791,6 +796,8 @@
|
|||||||
|
|
||||||
"@neon-rs/load": ["@neon-rs/load@0.0.4", "", {}, "sha512-kTPhdZyTQxB+2wpiRcFWrDcejc4JI6tkPuS7UZCG4l6Zvc5kU/gGQ/ozvHTh1XR5tS+UlfAfGuPajjzQjCiHCw=="],
|
"@neon-rs/load": ["@neon-rs/load@0.0.4", "", {}, "sha512-kTPhdZyTQxB+2wpiRcFWrDcejc4JI6tkPuS7UZCG4l6Zvc5kU/gGQ/ozvHTh1XR5tS+UlfAfGuPajjzQjCiHCw=="],
|
||||||
|
|
||||||
|
"@neondatabase/serverless": ["@neondatabase/serverless@0.4.26", "", { "dependencies": { "@types/pg": "8.6.6" } }, "sha512-6DYEKos2GYn8NTgcJf33BLAx//LcgqzHVavQWe6ZkaDqmEq0I0Xtub6pzwFdq9iayNdCj7e2b0QKr5a8QKB8kQ=="],
|
||||||
|
|
||||||
"@next/env": ["@next/env@15.2.1", "", {}, "sha512-JmY0qvnPuS2NCWOz2bbby3Pe0VzdAQ7XpEB6uLIHmtXNfAsAO0KLQLkuAoc42Bxbo3/jMC3dcn9cdf+piCcG2Q=="],
|
"@next/env": ["@next/env@15.2.1", "", {}, "sha512-JmY0qvnPuS2NCWOz2bbby3Pe0VzdAQ7XpEB6uLIHmtXNfAsAO0KLQLkuAoc42Bxbo3/jMC3dcn9cdf+piCcG2Q=="],
|
||||||
|
|
||||||
"@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@15.2.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-aWXT+5KEREoy3K5AKtiKwioeblmOvFFjd+F3dVleLvvLiQ/mD//jOOuUcx5hzcO9ISSw4lrqtUPntTpK32uXXQ=="],
|
"@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@15.2.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-aWXT+5KEREoy3K5AKtiKwioeblmOvFFjd+F3dVleLvvLiQ/mD//jOOuUcx5hzcO9ISSw4lrqtUPntTpK32uXXQ=="],
|
||||||
@@ -1227,7 +1234,7 @@
|
|||||||
|
|
||||||
"@types/babel__traverse": ["@types/babel__traverse@7.20.6", "", { "dependencies": { "@babel/types": "^7.20.7" } }, "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg=="],
|
"@types/babel__traverse": ["@types/babel__traverse@7.20.6", "", { "dependencies": { "@babel/types": "^7.20.7" } }, "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg=="],
|
||||||
|
|
||||||
"@types/bun": ["@types/bun@1.2.9", "", { "dependencies": { "bun-types": "1.2.9" } }, "sha512-epShhLGQYc4Bv/aceHbmBhOz1XgUnuTZgcxjxk+WXwNyDXavv5QHD1QEFV0FwbTSQtNq6g4ZcV6y0vZakTjswg=="],
|
"@types/bun": ["@types/bun@1.2.15", "", { "dependencies": { "bun-types": "1.2.15" } }, "sha512-U1ljPdBEphF0nw1MIk0hI7kPg7dFdPyM7EenHsp6W5loNHl7zqy6JQf/RKCgnUn2KDzUpkBwHPnEJEjII594bA=="],
|
||||||
|
|
||||||
"@types/cookie": ["@types/cookie@0.6.0", "", {}, "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="],
|
"@types/cookie": ["@types/cookie@0.6.0", "", {}, "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="],
|
||||||
|
|
||||||
@@ -1395,6 +1402,10 @@
|
|||||||
|
|
||||||
"@web3-storage/multipart-parser": ["@web3-storage/multipart-parser@1.0.0", "", {}, "sha512-BEO6al7BYqcnfX15W2cnGR+Q566ACXAT9UQykORCWW80lmkpWsnEob6zJS1ZVBKsSJC8+7vJkHwlp+lXG1UCdw=="],
|
"@web3-storage/multipart-parser": ["@web3-storage/multipart-parser@1.0.0", "", {}, "sha512-BEO6al7BYqcnfX15W2cnGR+Q566ACXAT9UQykORCWW80lmkpWsnEob6zJS1ZVBKsSJC8+7vJkHwlp+lXG1UCdw=="],
|
||||||
|
|
||||||
|
"@xata.io/client": ["@xata.io/client@0.0.0-next.v93343b9646f57a1e5c51c35eccf0767c2bb80baa", "", { "peerDependencies": { "typescript": ">=4.5" } }, "sha512-4Js4SAKwmmOPmZVIS1l2K8XVGGkUOi8L1jXuagDfeUX56n95wfA4xYMSmsVS0RLMmRWI4UM4bp5UcFJxwbFYGw=="],
|
||||||
|
|
||||||
|
"@xata.io/kysely": ["@xata.io/kysely@0.2.1", "", { "dependencies": { "@xata.io/client": "0.30.1" }, "peerDependencies": { "kysely": "*" } }, "sha512-0+WBcFkBSNEu11wVTyJyeNMOPUuolDKJMjXQr1nheHTNZLfsL0qKshTZOKIC/bGInjepGA7DQ/HFeKDHe5CDpA=="],
|
||||||
|
|
||||||
"@xyflow/react": ["@xyflow/react@12.4.4", "", { "dependencies": { "@xyflow/system": "0.0.52", "classcat": "^5.0.3", "zustand": "^4.4.0" }, "peerDependencies": { "react": ">=17", "react-dom": ">=17" } }, "sha512-9RZ9dgKZNJOlbrXXST5HPb5TcXPOIDGondjwcjDro44OQRPl1E0ZRPTeWPGaQtVjbg4WpR4BUYwOeshNI2TuVg=="],
|
"@xyflow/react": ["@xyflow/react@12.4.4", "", { "dependencies": { "@xyflow/system": "0.0.52", "classcat": "^5.0.3", "zustand": "^4.4.0" }, "peerDependencies": { "react": ">=17", "react-dom": ">=17" } }, "sha512-9RZ9dgKZNJOlbrXXST5HPb5TcXPOIDGondjwcjDro44OQRPl1E0ZRPTeWPGaQtVjbg4WpR4BUYwOeshNI2TuVg=="],
|
||||||
|
|
||||||
"@xyflow/system": ["@xyflow/system@0.0.52", "", { "dependencies": { "@types/d3-drag": "^3.0.7", "@types/d3-selection": "^3.0.10", "@types/d3-transition": "^3.0.8", "@types/d3-zoom": "^3.0.8", "d3-drag": "^3.0.0", "d3-selection": "^3.0.0", "d3-zoom": "^3.0.0" } }, "sha512-pJBMaoh/GEebIABWEIxAai0yf57dm+kH7J/Br+LnLFPuJL87Fhcmm4KFWd/bCUy/kCWUg+2/yFAGY0AUHRPOnQ=="],
|
"@xyflow/system": ["@xyflow/system@0.0.52", "", { "dependencies": { "@types/d3-drag": "^3.0.7", "@types/d3-selection": "^3.0.10", "@types/d3-transition": "^3.0.8", "@types/d3-zoom": "^3.0.8", "d3-drag": "^3.0.0", "d3-selection": "^3.0.0", "d3-zoom": "^3.0.0" } }, "sha512-pJBMaoh/GEebIABWEIxAai0yf57dm+kH7J/Br+LnLFPuJL87Fhcmm4KFWd/bCUy/kCWUg+2/yFAGY0AUHRPOnQ=="],
|
||||||
@@ -2075,7 +2086,7 @@
|
|||||||
|
|
||||||
"fd-slicer": ["fd-slicer@1.1.0", "", { "dependencies": { "pend": "~1.2.0" } }, "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g=="],
|
"fd-slicer": ["fd-slicer@1.1.0", "", { "dependencies": { "pend": "~1.2.0" } }, "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g=="],
|
||||||
|
|
||||||
"fdir": ["fdir@6.4.3", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw=="],
|
"fdir": ["fdir@6.4.5", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-4BG7puHpVsIYxZUbiUE3RqGloLaSSwzYie5jvasC4LWuBWzZawynvYouhjbQKw2JuIGYdm0DzIxl8iVidKlUEw=="],
|
||||||
|
|
||||||
"fetch-blob": ["fetch-blob@3.2.0", "", { "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" } }, "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ=="],
|
"fetch-blob": ["fetch-blob@3.2.0", "", { "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" } }, "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ=="],
|
||||||
|
|
||||||
@@ -2225,7 +2236,7 @@
|
|||||||
|
|
||||||
"headers-polyfill": ["headers-polyfill@4.0.3", "", {}, "sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ=="],
|
"headers-polyfill": ["headers-polyfill@4.0.3", "", {}, "sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ=="],
|
||||||
|
|
||||||
"hono": ["hono@4.7.4", "", {}, "sha512-Pst8FuGqz3L7tFF+u9Pu70eI0xa5S3LPUmrNd5Jm8nTHze9FxLTK9Kaj5g/k4UcwuJSXTP65SyHOPLrffpcAJg=="],
|
"hono": ["hono@4.7.11", "", {}, "sha512-rv0JMwC0KALbbmwJDEnxvQCeJh+xbS3KEWW5PC9cMJ08Ur9xgatI0HmtgYZfOdOSOeYsp5LO2cOhdI8cLEbDEQ=="],
|
||||||
|
|
||||||
"hosted-git-info": ["hosted-git-info@4.1.0", "", { "dependencies": { "lru-cache": "^6.0.0" } }, "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA=="],
|
"hosted-git-info": ["hosted-git-info@4.1.0", "", { "dependencies": { "lru-cache": "^6.0.0" } }, "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA=="],
|
||||||
|
|
||||||
@@ -2521,7 +2532,7 @@
|
|||||||
|
|
||||||
"jsonpointer": ["jsonpointer@5.0.1", "", {}, "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ=="],
|
"jsonpointer": ["jsonpointer@5.0.1", "", {}, "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ=="],
|
||||||
|
|
||||||
"jsonv-ts": ["jsonv-ts@0.0.14-alpha.6", "", { "peerDependencies": { "typescript": "^5.0.0" } }, "sha512-pwMpjEbNtyq8Xi6QBXuQ8dOZm7WQAEwvCPu3vVf9b3aU2KRHW+cfTPqO53U01YYdjWSSRkqaTKcLSiYdfwBYRA=="],
|
"jsonv-ts": ["jsonv-ts@0.1.0", "", { "peerDependencies": { "typescript": "^5.0.0" } }, "sha512-wJ+79o49MNie2Xk9w1hPN8ozjqemVWXOfWUTdioLui/SeGDC7C+QKXTDxsmUaIay86lorkjb3CCGo6JDKbyTZQ=="],
|
||||||
|
|
||||||
"jsonwebtoken": ["jsonwebtoken@9.0.2", "", { "dependencies": { "jws": "^3.2.2", "lodash.includes": "^4.3.0", "lodash.isboolean": "^3.0.3", "lodash.isinteger": "^4.0.4", "lodash.isnumber": "^3.0.3", "lodash.isplainobject": "^4.0.6", "lodash.isstring": "^4.0.1", "lodash.once": "^4.0.0", "ms": "^2.1.1", "semver": "^7.5.4" } }, "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ=="],
|
"jsonwebtoken": ["jsonwebtoken@9.0.2", "", { "dependencies": { "jws": "^3.2.2", "lodash.includes": "^4.3.0", "lodash.isboolean": "^3.0.3", "lodash.isinteger": "^4.0.4", "lodash.isnumber": "^3.0.3", "lodash.isplainobject": "^4.0.6", "lodash.isstring": "^4.0.1", "lodash.once": "^4.0.0", "ms": "^2.1.1", "semver": "^7.5.4" } }, "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ=="],
|
||||||
|
|
||||||
@@ -2543,6 +2554,10 @@
|
|||||||
|
|
||||||
"kysely-d1": ["kysely-d1@0.3.0", "", { "peerDependencies": { "kysely": "*" } }, "sha512-9wTbE6ooLiYtBa4wPg9e4fjfcmvRtgE/2j9pAjYrIq+iz+EsH/Hj9YbtxpEXA6JoRgfulVQ1EtGj6aycGGRpYw=="],
|
"kysely-d1": ["kysely-d1@0.3.0", "", { "peerDependencies": { "kysely": "*" } }, "sha512-9wTbE6ooLiYtBa4wPg9e4fjfcmvRtgE/2j9pAjYrIq+iz+EsH/Hj9YbtxpEXA6JoRgfulVQ1EtGj6aycGGRpYw=="],
|
||||||
|
|
||||||
|
"kysely-neon": ["kysely-neon@1.3.0", "", { "peerDependencies": { "@neondatabase/serverless": "^0.4.3", "kysely": "0.x.x", "ws": "^8.13.0" }, "optionalPeers": ["ws"] }, "sha512-CIIlbmqpIXVJDdBEYtEOwbmALag0jmqYrGfBeM4cHKb9AgBGs+X1SvXUZ8TqkDacQEqEZN2XtsDoUkcMIISjHw=="],
|
||||||
|
|
||||||
|
"kysely-postgres-js": ["kysely-postgres-js@2.0.0", "", { "peerDependencies": { "kysely": ">= 0.24.0 < 1", "postgres": ">= 3.4.0 < 4" } }, "sha512-R1tWx6/x3tSatWvsmbHJxpBZYhNNxcnMw52QzZaHKg7ZOWtHib4iZyEaw4gb2hNKVctWQ3jfMxZT/ZaEMK6kBQ=="],
|
||||||
|
|
||||||
"language-subtag-registry": ["language-subtag-registry@0.3.23", "", {}, "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ=="],
|
"language-subtag-registry": ["language-subtag-registry@0.3.23", "", {}, "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ=="],
|
||||||
|
|
||||||
"language-tags": ["language-tags@1.0.9", "", { "dependencies": { "language-subtag-registry": "^0.3.20" } }, "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA=="],
|
"language-tags": ["language-tags@1.0.9", "", { "dependencies": { "language-subtag-registry": "^0.3.20" } }, "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA=="],
|
||||||
@@ -2905,7 +2920,7 @@
|
|||||||
|
|
||||||
"pg-protocol": ["pg-protocol@1.8.0", "", {}, "sha512-jvuYlEkL03NRvOoyoRktBK7+qU5kOvlAwvmrH8sr3wbLrOdVWsRxQfz8mMy9sZFsqJ1hEWNfdWKI4SAmoL+j7g=="],
|
"pg-protocol": ["pg-protocol@1.8.0", "", {}, "sha512-jvuYlEkL03NRvOoyoRktBK7+qU5kOvlAwvmrH8sr3wbLrOdVWsRxQfz8mMy9sZFsqJ1hEWNfdWKI4SAmoL+j7g=="],
|
||||||
|
|
||||||
"pg-types": ["pg-types@4.0.2", "", { "dependencies": { "pg-int8": "1.0.1", "pg-numeric": "1.0.2", "postgres-array": "~3.0.1", "postgres-bytea": "~3.0.0", "postgres-date": "~2.1.0", "postgres-interval": "^3.0.0", "postgres-range": "^1.1.1" } }, "sha512-cRL3JpS3lKMGsKaWndugWQoLOCoP+Cic8oseVcbr0qhPzYD5DWXK+RZ9LY9wxRf7RQia4SCwQlXk0q6FCPrVng=="],
|
"pg-types": ["pg-types@2.2.0", "", { "dependencies": { "pg-int8": "1.0.1", "postgres-array": "~2.0.0", "postgres-bytea": "~1.0.0", "postgres-date": "~1.0.4", "postgres-interval": "^1.1.0" } }, "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA=="],
|
||||||
|
|
||||||
"pgpass": ["pgpass@1.0.5", "", { "dependencies": { "split2": "^4.1.0" } }, "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug=="],
|
"pgpass": ["pgpass@1.0.5", "", { "dependencies": { "split2": "^4.1.0" } }, "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug=="],
|
||||||
|
|
||||||
@@ -2961,13 +2976,15 @@
|
|||||||
|
|
||||||
"postcss-value-parser": ["postcss-value-parser@4.2.0", "", {}, "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="],
|
"postcss-value-parser": ["postcss-value-parser@4.2.0", "", {}, "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="],
|
||||||
|
|
||||||
"postgres-array": ["postgres-array@3.0.4", "", {}, "sha512-nAUSGfSDGOaOAEGwqsRY27GPOea7CNipJPOA7lPbdEpx5Kg3qzdP0AaWC5MlhTWV9s4hFX39nomVZ+C4tnGOJQ=="],
|
"postgres": ["postgres@3.4.7", "", {}, "sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw=="],
|
||||||
|
|
||||||
"postgres-bytea": ["postgres-bytea@3.0.0", "", { "dependencies": { "obuf": "~1.1.2" } }, "sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw=="],
|
"postgres-array": ["postgres-array@2.0.0", "", {}, "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA=="],
|
||||||
|
|
||||||
"postgres-date": ["postgres-date@2.1.0", "", {}, "sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA=="],
|
"postgres-bytea": ["postgres-bytea@1.0.0", "", {}, "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w=="],
|
||||||
|
|
||||||
"postgres-interval": ["postgres-interval@3.0.0", "", {}, "sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw=="],
|
"postgres-date": ["postgres-date@1.0.7", "", {}, "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q=="],
|
||||||
|
|
||||||
|
"postgres-interval": ["postgres-interval@1.2.0", "", { "dependencies": { "xtend": "^4.0.0" } }, "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ=="],
|
||||||
|
|
||||||
"postgres-range": ["postgres-range@1.1.4", "", {}, "sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w=="],
|
"postgres-range": ["postgres-range@1.1.4", "", {}, "sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w=="],
|
||||||
|
|
||||||
@@ -3603,7 +3620,7 @@
|
|||||||
|
|
||||||
"utils-merge": ["utils-merge@1.0.1", "", {}, "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA=="],
|
"utils-merge": ["utils-merge@1.0.1", "", {}, "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA=="],
|
||||||
|
|
||||||
"uuid": ["uuid@8.3.2", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="],
|
"uuid": ["uuid@11.1.0", "", { "bin": { "uuid": "dist/esm/bin/uuid" } }, "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A=="],
|
||||||
|
|
||||||
"v8-compile-cache": ["v8-compile-cache@2.4.0", "", {}, "sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw=="],
|
"v8-compile-cache": ["v8-compile-cache@2.4.0", "", {}, "sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw=="],
|
||||||
|
|
||||||
@@ -3635,7 +3652,7 @@
|
|||||||
|
|
||||||
"verror": ["verror@1.10.0", "", { "dependencies": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", "extsprintf": "^1.2.0" } }, "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw=="],
|
"verror": ["verror@1.10.0", "", { "dependencies": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", "extsprintf": "^1.2.0" } }, "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw=="],
|
||||||
|
|
||||||
"vite": ["vite@6.2.1", "", { "dependencies": { "esbuild": "^0.25.0", "postcss": "^8.5.3", "rollup": "^4.30.1" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-n2GnqDb6XPhlt9B8olZPrgMD/es/Nd1RdChF6CBD/fHW6pUyUTt2sQW2fPRX5GiD9XEa6+8A6A4f2vT6pSsE7Q=="],
|
"vite": ["vite@6.3.5", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ=="],
|
||||||
|
|
||||||
"vite-node": ["vite-node@3.0.8", "", { "dependencies": { "cac": "^6.7.14", "debug": "^4.4.0", "es-module-lexer": "^1.6.0", "pathe": "^2.0.3", "vite": "^5.0.0 || ^6.0.0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-6PhR4H9VGlcwXZ+KWCdMqbtG649xCPZqfI9j2PsK1FcXgEzro5bGHcVKFCTqPLaNKZES8Evqv4LwvZARsq5qlg=="],
|
"vite-node": ["vite-node@3.0.8", "", { "dependencies": { "cac": "^6.7.14", "debug": "^4.4.0", "es-module-lexer": "^1.6.0", "pathe": "^2.0.3", "vite": "^5.0.0 || ^6.0.0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-6PhR4H9VGlcwXZ+KWCdMqbtG649xCPZqfI9j2PsK1FcXgEzro5bGHcVKFCTqPLaNKZES8Evqv4LwvZARsq5qlg=="],
|
||||||
|
|
||||||
@@ -3847,12 +3864,12 @@
|
|||||||
|
|
||||||
"@babel/runtime/regenerator-runtime": ["regenerator-runtime@0.14.1", "", {}, "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="],
|
"@babel/runtime/regenerator-runtime": ["regenerator-runtime@0.14.1", "", {}, "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="],
|
||||||
|
|
||||||
"@bknd/postgres/@types/bun": ["@types/bun@1.2.5", "", { "dependencies": { "bun-types": "1.2.5" } }, "sha512-w2OZTzrZTVtbnJew1pdFmgV99H0/L+Pvw+z1P67HaR18MHOzYnTYOi6qzErhK8HyT+DB782ADVPPE92Xu2/Opg=="],
|
|
||||||
|
|
||||||
"@bundled-es-modules/cookie/cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="],
|
"@bundled-es-modules/cookie/cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="],
|
||||||
|
|
||||||
"@bundled-es-modules/tough-cookie/tough-cookie": ["tough-cookie@4.1.4", "", { "dependencies": { "psl": "^1.1.33", "punycode": "^2.1.1", "universalify": "^0.2.0", "url-parse": "^1.5.3" } }, "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag=="],
|
"@bundled-es-modules/tough-cookie/tough-cookie": ["tough-cookie@4.1.4", "", { "dependencies": { "psl": "^1.1.33", "punycode": "^2.1.1", "universalify": "^0.2.0", "url-parse": "^1.5.3" } }, "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag=="],
|
||||||
|
|
||||||
|
"@cypress/request/uuid": ["uuid@8.3.2", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="],
|
||||||
|
|
||||||
"@emnapi/runtime/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
"@emnapi/runtime/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||||
|
|
||||||
"@inquirer/core/cli-width": ["cli-width@4.1.0", "", {}, "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ=="],
|
"@inquirer/core/cli-width": ["cli-width@4.1.0", "", {}, "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ=="],
|
||||||
@@ -3909,6 +3926,8 @@
|
|||||||
|
|
||||||
"@libsql/kysely-libsql/@libsql/client": ["@libsql/client@0.8.1", "", { "dependencies": { "@libsql/core": "^0.8.1", "@libsql/hrana-client": "^0.6.2", "js-base64": "^3.7.5", "libsql": "^0.3.10", "promise-limit": "^2.7.0" } }, "sha512-xGg0F4iTDFpeBZ0r4pA6icGsYa5rG6RAG+i/iLDnpCAnSuTqEWMDdPlVseiq4Z/91lWI9jvvKKiKpovqJ1kZWA=="],
|
"@libsql/kysely-libsql/@libsql/client": ["@libsql/client@0.8.1", "", { "dependencies": { "@libsql/core": "^0.8.1", "@libsql/hrana-client": "^0.6.2", "js-base64": "^3.7.5", "libsql": "^0.3.10", "promise-limit": "^2.7.0" } }, "sha512-xGg0F4iTDFpeBZ0r4pA6icGsYa5rG6RAG+i/iLDnpCAnSuTqEWMDdPlVseiq4Z/91lWI9jvvKKiKpovqJ1kZWA=="],
|
||||||
|
|
||||||
|
"@neondatabase/serverless/@types/pg": ["@types/pg@8.6.6", "", { "dependencies": { "@types/node": "*", "pg-protocol": "*", "pg-types": "^2.2.0" } }, "sha512-O2xNmXebtwVekJDD+02udOncjVcMZQuTEQEMpKJ0ZRf5E7/9JJX3izhKUcUifBkyKpljyUM6BTgy2trmviKlpw=="],
|
||||||
|
|
||||||
"@plasmicapp/query/swr": ["swr@1.3.0", "", { "peerDependencies": { "react": "^16.11.0 || ^17.0.0 || ^18.0.0" } }, "sha512-dkghQrOl2ORX9HYrMDtPa7LTVHJjCTeZoB1dqTbnnEDlSvN8JEKpYIYurDfvbQFUUS8Cg8PceFVZNkW0KNNYPw=="],
|
"@plasmicapp/query/swr": ["swr@1.3.0", "", { "peerDependencies": { "react": "^16.11.0 || ^17.0.0 || ^18.0.0" } }, "sha512-dkghQrOl2ORX9HYrMDtPa7LTVHJjCTeZoB1dqTbnnEDlSvN8JEKpYIYurDfvbQFUUS8Cg8PceFVZNkW0KNNYPw=="],
|
||||||
|
|
||||||
"@remix-run/node/cookie-signature": ["cookie-signature@1.2.2", "", {}, "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="],
|
"@remix-run/node/cookie-signature": ["cookie-signature@1.2.2", "", {}, "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="],
|
||||||
@@ -4059,12 +4078,14 @@
|
|||||||
|
|
||||||
"@testing-library/jest-dom/chalk": ["chalk@3.0.0", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg=="],
|
"@testing-library/jest-dom/chalk": ["chalk@3.0.0", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg=="],
|
||||||
|
|
||||||
"@types/bun/bun-types": ["bun-types@1.2.9", "", { "dependencies": { "@types/node": "*", "@types/ws": "*" } }, "sha512-dk/kOEfQbajENN/D6FyiSgOKEuUi9PWfqKQJEgwKrCMWbjS/S6tEXp178mWvWAcUSYm9ArDlWHZKO3T/4cLXiw=="],
|
"@types/bun/bun-types": ["bun-types@1.2.15", "", { "dependencies": { "@types/node": "*" } }, "sha512-NarRIaS+iOaQU1JPfyKhZm4AsUOrwUOqRNHY0XxI8GI8jYxiLXLcdjYMG9UKS+fwWasc1uw1htV9AX24dD+p4w=="],
|
||||||
|
|
||||||
"@types/jest/jest-diff": ["jest-diff@25.5.0", "", { "dependencies": { "chalk": "^3.0.0", "diff-sequences": "^25.2.6", "jest-get-type": "^25.2.6", "pretty-format": "^25.5.0" } }, "sha512-z1kygetuPiREYdNIumRpAHY6RXiGmp70YHptjdaxTWGmA085W3iCnXNx0DhflK3vwrKmrRWyY1wUpkPMVxMK7A=="],
|
"@types/jest/jest-diff": ["jest-diff@25.5.0", "", { "dependencies": { "chalk": "^3.0.0", "diff-sequences": "^25.2.6", "jest-get-type": "^25.2.6", "pretty-format": "^25.5.0" } }, "sha512-z1kygetuPiREYdNIumRpAHY6RXiGmp70YHptjdaxTWGmA085W3iCnXNx0DhflK3vwrKmrRWyY1wUpkPMVxMK7A=="],
|
||||||
|
|
||||||
"@types/jest/pretty-format": ["pretty-format@25.5.0", "", { "dependencies": { "@jest/types": "^25.5.0", "ansi-regex": "^5.0.0", "ansi-styles": "^4.0.0", "react-is": "^16.12.0" } }, "sha512-kbo/kq2LQ/A/is0PQwsEHM7Ca6//bGPPvU6UnsdDRSKTWxT/ru/xb88v4BJf6a69H+uTytOEsTusT9ksd/1iWQ=="],
|
"@types/jest/pretty-format": ["pretty-format@25.5.0", "", { "dependencies": { "@jest/types": "^25.5.0", "ansi-regex": "^5.0.0", "ansi-styles": "^4.0.0", "react-is": "^16.12.0" } }, "sha512-kbo/kq2LQ/A/is0PQwsEHM7Ca6//bGPPvU6UnsdDRSKTWxT/ru/xb88v4BJf6a69H+uTytOEsTusT9ksd/1iWQ=="],
|
||||||
|
|
||||||
|
"@types/pg/pg-types": ["pg-types@4.0.2", "", { "dependencies": { "pg-int8": "1.0.1", "pg-numeric": "1.0.2", "postgres-array": "~3.0.1", "postgres-bytea": "~3.0.0", "postgres-date": "~2.1.0", "postgres-interval": "^3.0.0", "postgres-range": "^1.1.1" } }, "sha512-cRL3JpS3lKMGsKaWndugWQoLOCoP+Cic8oseVcbr0qhPzYD5DWXK+RZ9LY9wxRf7RQia4SCwQlXk0q6FCPrVng=="],
|
||||||
|
|
||||||
"@typescript-eslint/experimental-utils/eslint-utils": ["eslint-utils@2.1.0", "", { "dependencies": { "eslint-visitor-keys": "^1.1.0" } }, "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg=="],
|
"@typescript-eslint/experimental-utils/eslint-utils": ["eslint-utils@2.1.0", "", { "dependencies": { "eslint-visitor-keys": "^1.1.0" } }, "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg=="],
|
||||||
|
|
||||||
"@typescript-eslint/typescript-estree/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="],
|
"@typescript-eslint/typescript-estree/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="],
|
||||||
@@ -4125,6 +4146,8 @@
|
|||||||
|
|
||||||
"@wdio/utils/decamelize": ["decamelize@6.0.0", "", {}, "sha512-Fv96DCsdOgB6mdGl67MT5JaTNKRzrzill5OH5s8bjYJXVlcXyPYGyPsUkWyGV5p1TXI5esYIYMMeDJL0hEIwaA=="],
|
"@wdio/utils/decamelize": ["decamelize@6.0.0", "", {}, "sha512-Fv96DCsdOgB6mdGl67MT5JaTNKRzrzill5OH5s8bjYJXVlcXyPYGyPsUkWyGV5p1TXI5esYIYMMeDJL0hEIwaA=="],
|
||||||
|
|
||||||
|
"@xata.io/kysely/@xata.io/client": ["@xata.io/client@0.30.1", "", { "peerDependencies": { "typescript": ">=4.5" } }, "sha512-dAzDPHmIfenVIpF39m1elmW5ngjWu2mO8ZqJBN7dmYdXr98uhPANfLdVZnc3mUNG+NH37LqY1dSO862hIo2oRw=="],
|
||||||
|
|
||||||
"accepts/negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="],
|
"accepts/negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="],
|
||||||
|
|
||||||
"acorn-globals/acorn": ["acorn@6.4.2", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ=="],
|
"acorn-globals/acorn": ["acorn@6.4.2", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ=="],
|
||||||
@@ -4159,6 +4182,8 @@
|
|||||||
|
|
||||||
"bknd-cli/@libsql/client": ["@libsql/client@0.14.0", "", { "dependencies": { "@libsql/core": "^0.14.0", "@libsql/hrana-client": "^0.7.0", "js-base64": "^3.7.5", "libsql": "^0.4.4", "promise-limit": "^2.7.0" } }, "sha512-/9HEKfn6fwXB5aTEEoMeFh4CtG0ZzbncBb1e++OCdVpgKZ/xyMsIVYXm0w7Pv4RUel803vE6LwniB3PqD72R0Q=="],
|
"bknd-cli/@libsql/client": ["@libsql/client@0.14.0", "", { "dependencies": { "@libsql/core": "^0.14.0", "@libsql/hrana-client": "^0.7.0", "js-base64": "^3.7.5", "libsql": "^0.4.4", "promise-limit": "^2.7.0" } }, "sha512-/9HEKfn6fwXB5aTEEoMeFh4CtG0ZzbncBb1e++OCdVpgKZ/xyMsIVYXm0w7Pv4RUel803vE6LwniB3PqD72R0Q=="],
|
||||||
|
|
||||||
|
"bknd-cli/hono": ["hono@4.7.4", "", {}, "sha512-Pst8FuGqz3L7tFF+u9Pu70eI0xa5S3LPUmrNd5Jm8nTHze9FxLTK9Kaj5g/k4UcwuJSXTP65SyHOPLrffpcAJg=="],
|
||||||
|
|
||||||
"body-parser/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
|
"body-parser/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
|
||||||
|
|
||||||
"body-parser/iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="],
|
"body-parser/iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="],
|
||||||
@@ -4467,8 +4492,6 @@
|
|||||||
|
|
||||||
"peek-stream/duplexify": ["duplexify@3.7.1", "", { "dependencies": { "end-of-stream": "^1.0.0", "inherits": "^2.0.1", "readable-stream": "^2.0.0", "stream-shift": "^1.0.0" } }, "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g=="],
|
"peek-stream/duplexify": ["duplexify@3.7.1", "", { "dependencies": { "end-of-stream": "^1.0.0", "inherits": "^2.0.1", "readable-stream": "^2.0.0", "stream-shift": "^1.0.0" } }, "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g=="],
|
||||||
|
|
||||||
"pg/pg-types": ["pg-types@2.2.0", "", { "dependencies": { "pg-int8": "1.0.1", "postgres-array": "~2.0.0", "postgres-bytea": "~1.0.0", "postgres-date": "~1.0.4", "postgres-interval": "^1.1.0" } }, "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA=="],
|
|
||||||
|
|
||||||
"playwright/fsevents": ["fsevents@2.3.2", "", { "os": "darwin" }, "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA=="],
|
"playwright/fsevents": ["fsevents@2.3.2", "", { "os": "darwin" }, "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA=="],
|
||||||
|
|
||||||
"pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="],
|
"pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="],
|
||||||
@@ -4609,6 +4632,8 @@
|
|||||||
|
|
||||||
"through2/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="],
|
"through2/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="],
|
||||||
|
|
||||||
|
"tinyglobby/fdir": ["fdir@6.4.3", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw=="],
|
||||||
|
|
||||||
"to-object-path/kind-of": ["kind-of@3.2.2", "", { "dependencies": { "is-buffer": "^1.1.5" } }, "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ=="],
|
"to-object-path/kind-of": ["kind-of@3.2.2", "", { "dependencies": { "is-buffer": "^1.1.5" } }, "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ=="],
|
||||||
|
|
||||||
"ts-jest/mkdirp": ["mkdirp@0.5.6", "", { "dependencies": { "minimist": "^1.2.6" }, "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw=="],
|
"ts-jest/mkdirp": ["mkdirp@0.5.6", "", { "dependencies": { "minimist": "^1.2.6" }, "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw=="],
|
||||||
@@ -4657,6 +4682,12 @@
|
|||||||
|
|
||||||
"vite/esbuild": ["esbuild@0.25.1", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.1", "@esbuild/android-arm": "0.25.1", "@esbuild/android-arm64": "0.25.1", "@esbuild/android-x64": "0.25.1", "@esbuild/darwin-arm64": "0.25.1", "@esbuild/darwin-x64": "0.25.1", "@esbuild/freebsd-arm64": "0.25.1", "@esbuild/freebsd-x64": "0.25.1", "@esbuild/linux-arm": "0.25.1", "@esbuild/linux-arm64": "0.25.1", "@esbuild/linux-ia32": "0.25.1", "@esbuild/linux-loong64": "0.25.1", "@esbuild/linux-mips64el": "0.25.1", "@esbuild/linux-ppc64": "0.25.1", "@esbuild/linux-riscv64": "0.25.1", "@esbuild/linux-s390x": "0.25.1", "@esbuild/linux-x64": "0.25.1", "@esbuild/netbsd-arm64": "0.25.1", "@esbuild/netbsd-x64": "0.25.1", "@esbuild/openbsd-arm64": "0.25.1", "@esbuild/openbsd-x64": "0.25.1", "@esbuild/sunos-x64": "0.25.1", "@esbuild/win32-arm64": "0.25.1", "@esbuild/win32-ia32": "0.25.1", "@esbuild/win32-x64": "0.25.1" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-BGO5LtrGC7vxnqucAe/rmvKdJllfGaYWdyABvyMoXQlfYMb2bbRuReWR5tEGE//4LcNJj9XrkovTqNYRFZHAMQ=="],
|
"vite/esbuild": ["esbuild@0.25.1", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.1", "@esbuild/android-arm": "0.25.1", "@esbuild/android-arm64": "0.25.1", "@esbuild/android-x64": "0.25.1", "@esbuild/darwin-arm64": "0.25.1", "@esbuild/darwin-x64": "0.25.1", "@esbuild/freebsd-arm64": "0.25.1", "@esbuild/freebsd-x64": "0.25.1", "@esbuild/linux-arm": "0.25.1", "@esbuild/linux-arm64": "0.25.1", "@esbuild/linux-ia32": "0.25.1", "@esbuild/linux-loong64": "0.25.1", "@esbuild/linux-mips64el": "0.25.1", "@esbuild/linux-ppc64": "0.25.1", "@esbuild/linux-riscv64": "0.25.1", "@esbuild/linux-s390x": "0.25.1", "@esbuild/linux-x64": "0.25.1", "@esbuild/netbsd-arm64": "0.25.1", "@esbuild/netbsd-x64": "0.25.1", "@esbuild/openbsd-arm64": "0.25.1", "@esbuild/openbsd-x64": "0.25.1", "@esbuild/sunos-x64": "0.25.1", "@esbuild/win32-arm64": "0.25.1", "@esbuild/win32-ia32": "0.25.1", "@esbuild/win32-x64": "0.25.1" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-BGO5LtrGC7vxnqucAe/rmvKdJllfGaYWdyABvyMoXQlfYMb2bbRuReWR5tEGE//4LcNJj9XrkovTqNYRFZHAMQ=="],
|
||||||
|
|
||||||
|
"vite/tinyglobby": ["tinyglobby@0.2.14", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ=="],
|
||||||
|
|
||||||
|
"vite-node/vite": ["vite@6.2.1", "", { "dependencies": { "esbuild": "^0.25.0", "postcss": "^8.5.3", "rollup": "^4.30.1" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-n2GnqDb6XPhlt9B8olZPrgMD/es/Nd1RdChF6CBD/fHW6pUyUTt2sQW2fPRX5GiD9XEa6+8A6A4f2vT6pSsE7Q=="],
|
||||||
|
|
||||||
|
"vitest/vite": ["vite@6.2.1", "", { "dependencies": { "esbuild": "^0.25.0", "postcss": "^8.5.3", "rollup": "^4.30.1" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-n2GnqDb6XPhlt9B8olZPrgMD/es/Nd1RdChF6CBD/fHW6pUyUTt2sQW2fPRX5GiD9XEa6+8A6A4f2vT6pSsE7Q=="],
|
||||||
|
|
||||||
"webdriver/@types/node": ["@types/node@20.17.24", "", { "dependencies": { "undici-types": "~6.19.2" } }, "sha512-d7fGCyB96w9BnWQrOsJtpyiSaBcAYYr75bnK6ZRjDbql2cGLj/3GsL5OYmLPNq76l7Gf2q4Rv9J2o6h5CrD9sA=="],
|
"webdriver/@types/node": ["@types/node@20.17.24", "", { "dependencies": { "undici-types": "~6.19.2" } }, "sha512-d7fGCyB96w9BnWQrOsJtpyiSaBcAYYr75bnK6ZRjDbql2cGLj/3GsL5OYmLPNq76l7Gf2q4Rv9J2o6h5CrD9sA=="],
|
||||||
|
|
||||||
"webdriver/undici": ["undici@6.21.1", "", {}, "sha512-q/1rj5D0/zayJB2FraXdaWxbhWiNKDvu8naDT2dl1yTlvJp4BLtOcp2a5BvgGNQpYYJzau7tf1WgKv3b+7mqpQ=="],
|
"webdriver/undici": ["undici@6.21.1", "", {}, "sha512-q/1rj5D0/zayJB2FraXdaWxbhWiNKDvu8naDT2dl1yTlvJp4BLtOcp2a5BvgGNQpYYJzau7tf1WgKv3b+7mqpQ=="],
|
||||||
@@ -4741,6 +4772,14 @@
|
|||||||
|
|
||||||
"@types/jest/pretty-format/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
|
"@types/jest/pretty-format/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
|
||||||
|
|
||||||
|
"@types/pg/pg-types/postgres-array": ["postgres-array@3.0.4", "", {}, "sha512-nAUSGfSDGOaOAEGwqsRY27GPOea7CNipJPOA7lPbdEpx5Kg3qzdP0AaWC5MlhTWV9s4hFX39nomVZ+C4tnGOJQ=="],
|
||||||
|
|
||||||
|
"@types/pg/pg-types/postgres-bytea": ["postgres-bytea@3.0.0", "", { "dependencies": { "obuf": "~1.1.2" } }, "sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw=="],
|
||||||
|
|
||||||
|
"@types/pg/pg-types/postgres-date": ["postgres-date@2.1.0", "", {}, "sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA=="],
|
||||||
|
|
||||||
|
"@types/pg/pg-types/postgres-interval": ["postgres-interval@3.0.0", "", {}, "sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw=="],
|
||||||
|
|
||||||
"@typescript-eslint/typescript-estree/glob/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
|
"@typescript-eslint/typescript-estree/glob/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
|
||||||
|
|
||||||
"@verdaccio/local-storage-legacy/debug/ms": ["ms@2.1.2", "", {}, "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="],
|
"@verdaccio/local-storage-legacy/debug/ms": ["ms@2.1.2", "", {}, "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="],
|
||||||
@@ -4765,6 +4804,8 @@
|
|||||||
|
|
||||||
"@verdaccio/middleware/express/path-to-regexp": ["path-to-regexp@0.1.10", "", {}, "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w=="],
|
"@verdaccio/middleware/express/path-to-regexp": ["path-to-regexp@0.1.10", "", {}, "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w=="],
|
||||||
|
|
||||||
|
"@vitest/coverage-v8/vitest/vite": ["vite@6.2.1", "", { "dependencies": { "esbuild": "^0.25.0", "postcss": "^8.5.3", "rollup": "^4.30.1" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-n2GnqDb6XPhlt9B8olZPrgMD/es/Nd1RdChF6CBD/fHW6pUyUTt2sQW2fPRX5GiD9XEa6+8A6A4f2vT6pSsE7Q=="],
|
||||||
|
|
||||||
"@wdio/logger/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="],
|
"@wdio/logger/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="],
|
||||||
|
|
||||||
"@wdio/repl/@types/node/undici-types": ["undici-types@6.19.8", "", {}, "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw=="],
|
"@wdio/repl/@types/node/undici-types": ["undici-types@6.19.8", "", {}, "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw=="],
|
||||||
@@ -4777,6 +4818,8 @@
|
|||||||
|
|
||||||
"bknd-cli/@libsql/client/libsql": ["libsql@0.4.7", "", { "dependencies": { "@neon-rs/load": "^0.0.4", "detect-libc": "2.0.2" }, "optionalDependencies": { "@libsql/darwin-arm64": "0.4.7", "@libsql/darwin-x64": "0.4.7", "@libsql/linux-arm64-gnu": "0.4.7", "@libsql/linux-arm64-musl": "0.4.7", "@libsql/linux-x64-gnu": "0.4.7", "@libsql/linux-x64-musl": "0.4.7", "@libsql/win32-x64-msvc": "0.4.7" }, "os": [ "linux", "win32", "darwin", ], "cpu": [ "x64", "arm64", ] }, "sha512-T9eIRCs6b0J1SHKYIvD8+KCJMcWZ900iZyxdnSCdqxN12Z1ijzT+jY5nrk72Jw4B0HGzms2NgpryArlJqvc3Lw=="],
|
"bknd-cli/@libsql/client/libsql": ["libsql@0.4.7", "", { "dependencies": { "@neon-rs/load": "^0.0.4", "detect-libc": "2.0.2" }, "optionalDependencies": { "@libsql/darwin-arm64": "0.4.7", "@libsql/darwin-x64": "0.4.7", "@libsql/linux-arm64-gnu": "0.4.7", "@libsql/linux-arm64-musl": "0.4.7", "@libsql/linux-x64-gnu": "0.4.7", "@libsql/linux-x64-musl": "0.4.7", "@libsql/win32-x64-msvc": "0.4.7" }, "os": [ "linux", "win32", "darwin", ], "cpu": [ "x64", "arm64", ] }, "sha512-T9eIRCs6b0J1SHKYIvD8+KCJMcWZ900iZyxdnSCdqxN12Z1ijzT+jY5nrk72Jw4B0HGzms2NgpryArlJqvc3Lw=="],
|
||||||
|
|
||||||
|
"bknd/vitest/vite": ["vite@6.2.1", "", { "dependencies": { "esbuild": "^0.25.0", "postcss": "^8.5.3", "rollup": "^4.30.1" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-n2GnqDb6XPhlt9B8olZPrgMD/es/Nd1RdChF6CBD/fHW6pUyUTt2sQW2fPRX5GiD9XEa6+8A6A4f2vT6pSsE7Q=="],
|
||||||
|
|
||||||
"body-parser/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
"body-parser/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
||||||
|
|
||||||
"class-utils/define-property/is-descriptor": ["is-descriptor@0.1.7", "", { "dependencies": { "is-accessor-descriptor": "^1.0.1", "is-data-descriptor": "^1.0.1" } }, "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg=="],
|
"class-utils/define-property/is-descriptor": ["is-descriptor@0.1.7", "", { "dependencies": { "is-accessor-descriptor": "^1.0.1", "is-data-descriptor": "^1.0.1" } }, "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg=="],
|
||||||
@@ -4929,14 +4972,6 @@
|
|||||||
|
|
||||||
"peek-stream/duplexify/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="],
|
"peek-stream/duplexify/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="],
|
||||||
|
|
||||||
"pg/pg-types/postgres-array": ["postgres-array@2.0.0", "", {}, "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA=="],
|
|
||||||
|
|
||||||
"pg/pg-types/postgres-bytea": ["postgres-bytea@1.0.0", "", {}, "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w=="],
|
|
||||||
|
|
||||||
"pg/pg-types/postgres-date": ["postgres-date@1.0.7", "", {}, "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q=="],
|
|
||||||
|
|
||||||
"pg/pg-types/postgres-interval": ["postgres-interval@1.2.0", "", { "dependencies": { "xtend": "^4.0.0" } }, "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ=="],
|
|
||||||
|
|
||||||
"progress-estimator/chalk/ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "^1.9.0" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="],
|
"progress-estimator/chalk/ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "^1.9.0" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="],
|
||||||
|
|
||||||
"progress-estimator/chalk/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="],
|
"progress-estimator/chalk/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="],
|
||||||
@@ -5119,6 +5154,8 @@
|
|||||||
|
|
||||||
"verdaccio-audit/https-proxy-agent/agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="],
|
"verdaccio-audit/https-proxy-agent/agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="],
|
||||||
|
|
||||||
|
"vite-node/vite/esbuild": ["esbuild@0.25.1", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.1", "@esbuild/android-arm": "0.25.1", "@esbuild/android-arm64": "0.25.1", "@esbuild/android-x64": "0.25.1", "@esbuild/darwin-arm64": "0.25.1", "@esbuild/darwin-x64": "0.25.1", "@esbuild/freebsd-arm64": "0.25.1", "@esbuild/freebsd-x64": "0.25.1", "@esbuild/linux-arm": "0.25.1", "@esbuild/linux-arm64": "0.25.1", "@esbuild/linux-ia32": "0.25.1", "@esbuild/linux-loong64": "0.25.1", "@esbuild/linux-mips64el": "0.25.1", "@esbuild/linux-ppc64": "0.25.1", "@esbuild/linux-riscv64": "0.25.1", "@esbuild/linux-s390x": "0.25.1", "@esbuild/linux-x64": "0.25.1", "@esbuild/netbsd-arm64": "0.25.1", "@esbuild/netbsd-x64": "0.25.1", "@esbuild/openbsd-arm64": "0.25.1", "@esbuild/openbsd-x64": "0.25.1", "@esbuild/sunos-x64": "0.25.1", "@esbuild/win32-arm64": "0.25.1", "@esbuild/win32-ia32": "0.25.1", "@esbuild/win32-x64": "0.25.1" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-BGO5LtrGC7vxnqucAe/rmvKdJllfGaYWdyABvyMoXQlfYMb2bbRuReWR5tEGE//4LcNJj9XrkovTqNYRFZHAMQ=="],
|
||||||
|
|
||||||
"vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.1", "", { "os": "aix", "cpu": "ppc64" }, "sha512-kfYGy8IdzTGy+z0vFGvExZtxkFlA4zAxgKEahG9KE1ScBjpQnFsNOX8KTU5ojNru5ed5CVoJYXFtoxaq5nFbjQ=="],
|
"vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.1", "", { "os": "aix", "cpu": "ppc64" }, "sha512-kfYGy8IdzTGy+z0vFGvExZtxkFlA4zAxgKEahG9KE1ScBjpQnFsNOX8KTU5ojNru5ed5CVoJYXFtoxaq5nFbjQ=="],
|
||||||
|
|
||||||
"vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.25.1", "", { "os": "android", "cpu": "arm" }, "sha512-dp+MshLYux6j/JjdqVLnMglQlFu+MuVeNrmT5nk6q07wNhCdSnB7QZj+7G8VMUGh1q+vj2Bq8kRsuyA00I/k+Q=="],
|
"vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.25.1", "", { "os": "android", "cpu": "arm" }, "sha512-dp+MshLYux6j/JjdqVLnMglQlFu+MuVeNrmT5nk6q07wNhCdSnB7QZj+7G8VMUGh1q+vj2Bq8kRsuyA00I/k+Q=="],
|
||||||
@@ -5167,6 +5204,8 @@
|
|||||||
|
|
||||||
"vite/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.1", "", { "os": "win32", "cpu": "x64" }, "sha512-Y1EQdcfwMSeQN/ujR5VayLOJ1BHaK+ssyk0AEzPjC+t1lITgsnccPqFjb6V+LsTp/9Iov4ysfjxLaGJ9RPtkVg=="],
|
"vite/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.1", "", { "os": "win32", "cpu": "x64" }, "sha512-Y1EQdcfwMSeQN/ujR5VayLOJ1BHaK+ssyk0AEzPjC+t1lITgsnccPqFjb6V+LsTp/9Iov4ysfjxLaGJ9RPtkVg=="],
|
||||||
|
|
||||||
|
"vitest/vite/esbuild": ["esbuild@0.25.1", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.1", "@esbuild/android-arm": "0.25.1", "@esbuild/android-arm64": "0.25.1", "@esbuild/android-x64": "0.25.1", "@esbuild/darwin-arm64": "0.25.1", "@esbuild/darwin-x64": "0.25.1", "@esbuild/freebsd-arm64": "0.25.1", "@esbuild/freebsd-x64": "0.25.1", "@esbuild/linux-arm": "0.25.1", "@esbuild/linux-arm64": "0.25.1", "@esbuild/linux-ia32": "0.25.1", "@esbuild/linux-loong64": "0.25.1", "@esbuild/linux-mips64el": "0.25.1", "@esbuild/linux-ppc64": "0.25.1", "@esbuild/linux-riscv64": "0.25.1", "@esbuild/linux-s390x": "0.25.1", "@esbuild/linux-x64": "0.25.1", "@esbuild/netbsd-arm64": "0.25.1", "@esbuild/netbsd-x64": "0.25.1", "@esbuild/openbsd-arm64": "0.25.1", "@esbuild/openbsd-x64": "0.25.1", "@esbuild/sunos-x64": "0.25.1", "@esbuild/win32-arm64": "0.25.1", "@esbuild/win32-ia32": "0.25.1", "@esbuild/win32-x64": "0.25.1" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-BGO5LtrGC7vxnqucAe/rmvKdJllfGaYWdyABvyMoXQlfYMb2bbRuReWR5tEGE//4LcNJj9XrkovTqNYRFZHAMQ=="],
|
||||||
|
|
||||||
"webdriver/@types/node/undici-types": ["undici-types@6.19.8", "", {}, "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw=="],
|
"webdriver/@types/node/undici-types": ["undici-types@6.19.8", "", {}, "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw=="],
|
||||||
|
|
||||||
"webdriverio/@types/node/undici-types": ["undici-types@6.19.8", "", {}, "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw=="],
|
"webdriverio/@types/node/undici-types": ["undici-types@6.19.8", "", {}, "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw=="],
|
||||||
@@ -5265,6 +5304,8 @@
|
|||||||
|
|
||||||
"@verdaccio/middleware/express/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
"@verdaccio/middleware/express/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
||||||
|
|
||||||
|
"@vitest/coverage-v8/vitest/vite/esbuild": ["esbuild@0.25.1", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.1", "@esbuild/android-arm": "0.25.1", "@esbuild/android-arm64": "0.25.1", "@esbuild/android-x64": "0.25.1", "@esbuild/darwin-arm64": "0.25.1", "@esbuild/darwin-x64": "0.25.1", "@esbuild/freebsd-arm64": "0.25.1", "@esbuild/freebsd-x64": "0.25.1", "@esbuild/linux-arm": "0.25.1", "@esbuild/linux-arm64": "0.25.1", "@esbuild/linux-ia32": "0.25.1", "@esbuild/linux-loong64": "0.25.1", "@esbuild/linux-mips64el": "0.25.1", "@esbuild/linux-ppc64": "0.25.1", "@esbuild/linux-riscv64": "0.25.1", "@esbuild/linux-s390x": "0.25.1", "@esbuild/linux-x64": "0.25.1", "@esbuild/netbsd-arm64": "0.25.1", "@esbuild/netbsd-x64": "0.25.1", "@esbuild/openbsd-arm64": "0.25.1", "@esbuild/openbsd-x64": "0.25.1", "@esbuild/sunos-x64": "0.25.1", "@esbuild/win32-arm64": "0.25.1", "@esbuild/win32-ia32": "0.25.1", "@esbuild/win32-x64": "0.25.1" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-BGO5LtrGC7vxnqucAe/rmvKdJllfGaYWdyABvyMoXQlfYMb2bbRuReWR5tEGE//4LcNJj9XrkovTqNYRFZHAMQ=="],
|
||||||
|
|
||||||
"bknd-cli/@libsql/client/libsql/@libsql/darwin-arm64": ["@libsql/darwin-arm64@0.4.7", "", { "os": "darwin", "cpu": "arm64" }, "sha512-yOL742IfWUlUevnI5PdnIT4fryY3LYTdLm56bnY0wXBw7dhFcnjuA7jrH3oSVz2mjZTHujxoITgAE7V6Z+eAbg=="],
|
"bknd-cli/@libsql/client/libsql/@libsql/darwin-arm64": ["@libsql/darwin-arm64@0.4.7", "", { "os": "darwin", "cpu": "arm64" }, "sha512-yOL742IfWUlUevnI5PdnIT4fryY3LYTdLm56bnY0wXBw7dhFcnjuA7jrH3oSVz2mjZTHujxoITgAE7V6Z+eAbg=="],
|
||||||
|
|
||||||
"bknd-cli/@libsql/client/libsql/@libsql/darwin-x64": ["@libsql/darwin-x64@0.4.7", "", { "os": "darwin", "cpu": "x64" }, "sha512-ezc7V75+eoyyH07BO9tIyJdqXXcRfZMbKcLCeF8+qWK5nP8wWuMcfOVywecsXGRbT99zc5eNra4NEx6z5PkSsA=="],
|
"bknd-cli/@libsql/client/libsql/@libsql/darwin-x64": ["@libsql/darwin-x64@0.4.7", "", { "os": "darwin", "cpu": "x64" }, "sha512-ezc7V75+eoyyH07BO9tIyJdqXXcRfZMbKcLCeF8+qWK5nP8wWuMcfOVywecsXGRbT99zc5eNra4NEx6z5PkSsA=="],
|
||||||
@@ -5281,6 +5322,8 @@
|
|||||||
|
|
||||||
"bknd-cli/@libsql/client/libsql/detect-libc": ["detect-libc@2.0.2", "", {}, "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw=="],
|
"bknd-cli/@libsql/client/libsql/detect-libc": ["detect-libc@2.0.2", "", {}, "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw=="],
|
||||||
|
|
||||||
|
"bknd/vitest/vite/esbuild": ["esbuild@0.25.1", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.1", "@esbuild/android-arm": "0.25.1", "@esbuild/android-arm64": "0.25.1", "@esbuild/android-x64": "0.25.1", "@esbuild/darwin-arm64": "0.25.1", "@esbuild/darwin-x64": "0.25.1", "@esbuild/freebsd-arm64": "0.25.1", "@esbuild/freebsd-x64": "0.25.1", "@esbuild/linux-arm": "0.25.1", "@esbuild/linux-arm64": "0.25.1", "@esbuild/linux-ia32": "0.25.1", "@esbuild/linux-loong64": "0.25.1", "@esbuild/linux-mips64el": "0.25.1", "@esbuild/linux-ppc64": "0.25.1", "@esbuild/linux-riscv64": "0.25.1", "@esbuild/linux-s390x": "0.25.1", "@esbuild/linux-x64": "0.25.1", "@esbuild/netbsd-arm64": "0.25.1", "@esbuild/netbsd-x64": "0.25.1", "@esbuild/openbsd-arm64": "0.25.1", "@esbuild/openbsd-x64": "0.25.1", "@esbuild/sunos-x64": "0.25.1", "@esbuild/win32-arm64": "0.25.1", "@esbuild/win32-ia32": "0.25.1", "@esbuild/win32-x64": "0.25.1" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-BGO5LtrGC7vxnqucAe/rmvKdJllfGaYWdyABvyMoXQlfYMb2bbRuReWR5tEGE//4LcNJj9XrkovTqNYRFZHAMQ=="],
|
||||||
|
|
||||||
"eslint/chalk/ansi-styles/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="],
|
"eslint/chalk/ansi-styles/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="],
|
||||||
|
|
||||||
"eslint/chalk/supports-color/has-flag": ["has-flag@3.0.0", "", {}, "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="],
|
"eslint/chalk/supports-color/has-flag": ["has-flag@3.0.0", "", {}, "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="],
|
||||||
@@ -5353,8 +5396,200 @@
|
|||||||
|
|
||||||
"verdaccio-audit/express/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
"verdaccio-audit/express/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
||||||
|
|
||||||
|
"vite-node/vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.1", "", { "os": "aix", "cpu": "ppc64" }, "sha512-kfYGy8IdzTGy+z0vFGvExZtxkFlA4zAxgKEahG9KE1ScBjpQnFsNOX8KTU5ojNru5ed5CVoJYXFtoxaq5nFbjQ=="],
|
||||||
|
|
||||||
|
"vite-node/vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.25.1", "", { "os": "android", "cpu": "arm" }, "sha512-dp+MshLYux6j/JjdqVLnMglQlFu+MuVeNrmT5nk6q07wNhCdSnB7QZj+7G8VMUGh1q+vj2Bq8kRsuyA00I/k+Q=="],
|
||||||
|
|
||||||
|
"vite-node/vite/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.1", "", { "os": "android", "cpu": "arm64" }, "sha512-50tM0zCJW5kGqgG7fQ7IHvQOcAn9TKiVRuQ/lN0xR+T2lzEFvAi1ZcS8DiksFcEpf1t/GYOeOfCAgDHFpkiSmA=="],
|
||||||
|
|
||||||
|
"vite-node/vite/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.25.1", "", { "os": "android", "cpu": "x64" }, "sha512-GCj6WfUtNldqUzYkN/ITtlhwQqGWu9S45vUXs7EIYf+7rCiiqH9bCloatO9VhxsL0Pji+PF4Lz2XXCES+Q8hDw=="],
|
||||||
|
|
||||||
|
"vite-node/vite/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-5hEZKPf+nQjYoSr/elb62U19/l1mZDdqidGfmFutVUjjUZrOazAtwK+Kr+3y0C/oeJfLlxo9fXb1w7L+P7E4FQ=="],
|
||||||
|
|
||||||
|
"vite-node/vite/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-hxVnwL2Dqs3fM1IWq8Iezh0cX7ZGdVhbTfnOy5uURtao5OIVCEyj9xIzemDi7sRvKsuSdtCAhMKarxqtlyVyfA=="],
|
||||||
|
|
||||||
|
"vite-node/vite/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-1MrCZs0fZa2g8E+FUo2ipw6jw5qqQiH+tERoS5fAfKnRx6NXH31tXBKI3VpmLijLH6yriMZsxJtaXUyFt/8Y4A=="],
|
||||||
|
|
||||||
|
"vite-node/vite/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-0IZWLiTyz7nm0xuIs0q1Y3QWJC52R8aSXxe40VUxm6BB1RNmkODtW6LHvWRrGiICulcX7ZvyH6h5fqdLu4gkww=="],
|
||||||
|
|
||||||
|
"vite-node/vite/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.1", "", { "os": "linux", "cpu": "arm" }, "sha512-NdKOhS4u7JhDKw9G3cY6sWqFcnLITn6SqivVArbzIaf3cemShqfLGHYMx8Xlm/lBit3/5d7kXvriTUGa5YViuQ=="],
|
||||||
|
|
||||||
|
"vite-node/vite/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-jaN3dHi0/DDPelk0nLcXRm1q7DNJpjXy7yWaWvbfkPvI+7XNSc/lDOnCLN7gzsyzgu6qSAmgSvP9oXAhP973uQ=="],
|
||||||
|
|
||||||
|
"vite-node/vite/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.1", "", { "os": "linux", "cpu": "ia32" }, "sha512-OJykPaF4v8JidKNGz8c/q1lBO44sQNUQtq1KktJXdBLn1hPod5rE/Hko5ugKKZd+D2+o1a9MFGUEIUwO2YfgkQ=="],
|
||||||
|
|
||||||
|
"vite-node/vite/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.1", "", { "os": "linux", "cpu": "none" }, "sha512-nGfornQj4dzcq5Vp835oM/o21UMlXzn79KobKlcs3Wz9smwiifknLy4xDCLUU0BWp7b/houtdrgUz7nOGnfIYg=="],
|
||||||
|
|
||||||
|
"vite-node/vite/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.1", "", { "os": "linux", "cpu": "none" }, "sha512-1osBbPEFYwIE5IVB/0g2X6i1qInZa1aIoj1TdL4AaAb55xIIgbg8Doq6a5BzYWgr+tEcDzYH67XVnTmUzL+nXg=="],
|
||||||
|
|
||||||
|
"vite-node/vite/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-/6VBJOwUf3TdTvJZ82qF3tbLuWsscd7/1w+D9LH0W/SqUgM5/JJD0lrJ1fVIfZsqB6RFmLCe0Xz3fmZc3WtyVg=="],
|
||||||
|
|
||||||
|
"vite-node/vite/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.1", "", { "os": "linux", "cpu": "none" }, "sha512-nSut/Mx5gnilhcq2yIMLMe3Wl4FK5wx/o0QuuCLMtmJn+WeWYoEGDN1ipcN72g1WHsnIbxGXd4i/MF0gTcuAjQ=="],
|
||||||
|
|
||||||
|
"vite-node/vite/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-cEECeLlJNfT8kZHqLarDBQso9a27o2Zd2AQ8USAEoGtejOrCYHNtKP8XQhMDJMtthdF4GBmjR2au3x1udADQQQ=="],
|
||||||
|
|
||||||
|
"vite-node/vite/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.1", "", { "os": "linux", "cpu": "x64" }, "sha512-xbfUhu/gnvSEg+EGovRc+kjBAkrvtk38RlerAzQxvMzlB4fXpCFCeUAYzJvrnhFtdeyVCDANSjJvOvGYoeKzFA=="],
|
||||||
|
|
||||||
|
"vite-node/vite/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.1", "", { "os": "none", "cpu": "x64" }, "sha512-X53z6uXip6KFXBQ+Krbx25XHV/NCbzryM6ehOAeAil7X7oa4XIq+394PWGnwaSQ2WRA0KI6PUO6hTO5zeF5ijA=="],
|
||||||
|
|
||||||
|
"vite-node/vite/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.1", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-Na9T3szbXezdzM/Kfs3GcRQNjHzM6GzFBeU1/6IV/npKP5ORtp9zbQjvkDJ47s6BCgaAZnnnu/cY1x342+MvZg=="],
|
||||||
|
|
||||||
|
"vite-node/vite/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-T3H78X2h1tszfRSf+txbt5aOp/e7TAz3ptVKu9Oyir3IAOFPGV6O9c2naym5TOriy1l0nNf6a4X5UXRZSGX/dw=="],
|
||||||
|
|
||||||
|
"vite-node/vite/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.1", "", { "os": "sunos", "cpu": "x64" }, "sha512-2H3RUvcmULO7dIE5EWJH8eubZAI4xw54H1ilJnRNZdeo8dTADEZ21w6J22XBkXqGJbe0+wnNJtw3UXRoLJnFEg=="],
|
||||||
|
|
||||||
|
"vite-node/vite/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-GE7XvrdOzrb+yVKB9KsRMq+7a2U/K5Cf/8grVFRAGJmfADr/e/ODQ134RK2/eeHqYV5eQRFxb1hY7Nr15fv1NQ=="],
|
||||||
|
|
||||||
|
"vite-node/vite/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-uOxSJCIcavSiT6UnBhBzE8wy3n0hOkJsBOzy7HDAuTDE++1DJMRRVCPGisULScHL+a/ZwdXPpXD3IyFKjA7K8A=="],
|
||||||
|
|
||||||
|
"vite-node/vite/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.1", "", { "os": "win32", "cpu": "x64" }, "sha512-Y1EQdcfwMSeQN/ujR5VayLOJ1BHaK+ssyk0AEzPjC+t1lITgsnccPqFjb6V+LsTp/9Iov4ysfjxLaGJ9RPtkVg=="],
|
||||||
|
|
||||||
|
"vitest/vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.1", "", { "os": "aix", "cpu": "ppc64" }, "sha512-kfYGy8IdzTGy+z0vFGvExZtxkFlA4zAxgKEahG9KE1ScBjpQnFsNOX8KTU5ojNru5ed5CVoJYXFtoxaq5nFbjQ=="],
|
||||||
|
|
||||||
|
"vitest/vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.25.1", "", { "os": "android", "cpu": "arm" }, "sha512-dp+MshLYux6j/JjdqVLnMglQlFu+MuVeNrmT5nk6q07wNhCdSnB7QZj+7G8VMUGh1q+vj2Bq8kRsuyA00I/k+Q=="],
|
||||||
|
|
||||||
|
"vitest/vite/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.1", "", { "os": "android", "cpu": "arm64" }, "sha512-50tM0zCJW5kGqgG7fQ7IHvQOcAn9TKiVRuQ/lN0xR+T2lzEFvAi1ZcS8DiksFcEpf1t/GYOeOfCAgDHFpkiSmA=="],
|
||||||
|
|
||||||
|
"vitest/vite/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.25.1", "", { "os": "android", "cpu": "x64" }, "sha512-GCj6WfUtNldqUzYkN/ITtlhwQqGWu9S45vUXs7EIYf+7rCiiqH9bCloatO9VhxsL0Pji+PF4Lz2XXCES+Q8hDw=="],
|
||||||
|
|
||||||
|
"vitest/vite/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-5hEZKPf+nQjYoSr/elb62U19/l1mZDdqidGfmFutVUjjUZrOazAtwK+Kr+3y0C/oeJfLlxo9fXb1w7L+P7E4FQ=="],
|
||||||
|
|
||||||
|
"vitest/vite/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-hxVnwL2Dqs3fM1IWq8Iezh0cX7ZGdVhbTfnOy5uURtao5OIVCEyj9xIzemDi7sRvKsuSdtCAhMKarxqtlyVyfA=="],
|
||||||
|
|
||||||
|
"vitest/vite/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-1MrCZs0fZa2g8E+FUo2ipw6jw5qqQiH+tERoS5fAfKnRx6NXH31tXBKI3VpmLijLH6yriMZsxJtaXUyFt/8Y4A=="],
|
||||||
|
|
||||||
|
"vitest/vite/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-0IZWLiTyz7nm0xuIs0q1Y3QWJC52R8aSXxe40VUxm6BB1RNmkODtW6LHvWRrGiICulcX7ZvyH6h5fqdLu4gkww=="],
|
||||||
|
|
||||||
|
"vitest/vite/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.1", "", { "os": "linux", "cpu": "arm" }, "sha512-NdKOhS4u7JhDKw9G3cY6sWqFcnLITn6SqivVArbzIaf3cemShqfLGHYMx8Xlm/lBit3/5d7kXvriTUGa5YViuQ=="],
|
||||||
|
|
||||||
|
"vitest/vite/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-jaN3dHi0/DDPelk0nLcXRm1q7DNJpjXy7yWaWvbfkPvI+7XNSc/lDOnCLN7gzsyzgu6qSAmgSvP9oXAhP973uQ=="],
|
||||||
|
|
||||||
|
"vitest/vite/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.1", "", { "os": "linux", "cpu": "ia32" }, "sha512-OJykPaF4v8JidKNGz8c/q1lBO44sQNUQtq1KktJXdBLn1hPod5rE/Hko5ugKKZd+D2+o1a9MFGUEIUwO2YfgkQ=="],
|
||||||
|
|
||||||
|
"vitest/vite/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.1", "", { "os": "linux", "cpu": "none" }, "sha512-nGfornQj4dzcq5Vp835oM/o21UMlXzn79KobKlcs3Wz9smwiifknLy4xDCLUU0BWp7b/houtdrgUz7nOGnfIYg=="],
|
||||||
|
|
||||||
|
"vitest/vite/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.1", "", { "os": "linux", "cpu": "none" }, "sha512-1osBbPEFYwIE5IVB/0g2X6i1qInZa1aIoj1TdL4AaAb55xIIgbg8Doq6a5BzYWgr+tEcDzYH67XVnTmUzL+nXg=="],
|
||||||
|
|
||||||
|
"vitest/vite/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-/6VBJOwUf3TdTvJZ82qF3tbLuWsscd7/1w+D9LH0W/SqUgM5/JJD0lrJ1fVIfZsqB6RFmLCe0Xz3fmZc3WtyVg=="],
|
||||||
|
|
||||||
|
"vitest/vite/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.1", "", { "os": "linux", "cpu": "none" }, "sha512-nSut/Mx5gnilhcq2yIMLMe3Wl4FK5wx/o0QuuCLMtmJn+WeWYoEGDN1ipcN72g1WHsnIbxGXd4i/MF0gTcuAjQ=="],
|
||||||
|
|
||||||
|
"vitest/vite/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-cEECeLlJNfT8kZHqLarDBQso9a27o2Zd2AQ8USAEoGtejOrCYHNtKP8XQhMDJMtthdF4GBmjR2au3x1udADQQQ=="],
|
||||||
|
|
||||||
|
"vitest/vite/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.1", "", { "os": "linux", "cpu": "x64" }, "sha512-xbfUhu/gnvSEg+EGovRc+kjBAkrvtk38RlerAzQxvMzlB4fXpCFCeUAYzJvrnhFtdeyVCDANSjJvOvGYoeKzFA=="],
|
||||||
|
|
||||||
|
"vitest/vite/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.1", "", { "os": "none", "cpu": "x64" }, "sha512-X53z6uXip6KFXBQ+Krbx25XHV/NCbzryM6ehOAeAil7X7oa4XIq+394PWGnwaSQ2WRA0KI6PUO6hTO5zeF5ijA=="],
|
||||||
|
|
||||||
|
"vitest/vite/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.1", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-Na9T3szbXezdzM/Kfs3GcRQNjHzM6GzFBeU1/6IV/npKP5ORtp9zbQjvkDJ47s6BCgaAZnnnu/cY1x342+MvZg=="],
|
||||||
|
|
||||||
|
"vitest/vite/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-T3H78X2h1tszfRSf+txbt5aOp/e7TAz3ptVKu9Oyir3IAOFPGV6O9c2naym5TOriy1l0nNf6a4X5UXRZSGX/dw=="],
|
||||||
|
|
||||||
|
"vitest/vite/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.1", "", { "os": "sunos", "cpu": "x64" }, "sha512-2H3RUvcmULO7dIE5EWJH8eubZAI4xw54H1ilJnRNZdeo8dTADEZ21w6J22XBkXqGJbe0+wnNJtw3UXRoLJnFEg=="],
|
||||||
|
|
||||||
|
"vitest/vite/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-GE7XvrdOzrb+yVKB9KsRMq+7a2U/K5Cf/8grVFRAGJmfADr/e/ODQ134RK2/eeHqYV5eQRFxb1hY7Nr15fv1NQ=="],
|
||||||
|
|
||||||
|
"vitest/vite/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-uOxSJCIcavSiT6UnBhBzE8wy3n0hOkJsBOzy7HDAuTDE++1DJMRRVCPGisULScHL+a/ZwdXPpXD3IyFKjA7K8A=="],
|
||||||
|
|
||||||
|
"vitest/vite/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.1", "", { "os": "win32", "cpu": "x64" }, "sha512-Y1EQdcfwMSeQN/ujR5VayLOJ1BHaK+ssyk0AEzPjC+t1lITgsnccPqFjb6V+LsTp/9Iov4ysfjxLaGJ9RPtkVg=="],
|
||||||
|
|
||||||
"@jest/core/rimraf/glob/minimatch/brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="],
|
"@jest/core/rimraf/glob/minimatch/brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="],
|
||||||
|
|
||||||
|
"@vitest/coverage-v8/vitest/vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.1", "", { "os": "aix", "cpu": "ppc64" }, "sha512-kfYGy8IdzTGy+z0vFGvExZtxkFlA4zAxgKEahG9KE1ScBjpQnFsNOX8KTU5ojNru5ed5CVoJYXFtoxaq5nFbjQ=="],
|
||||||
|
|
||||||
|
"@vitest/coverage-v8/vitest/vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.25.1", "", { "os": "android", "cpu": "arm" }, "sha512-dp+MshLYux6j/JjdqVLnMglQlFu+MuVeNrmT5nk6q07wNhCdSnB7QZj+7G8VMUGh1q+vj2Bq8kRsuyA00I/k+Q=="],
|
||||||
|
|
||||||
|
"@vitest/coverage-v8/vitest/vite/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.1", "", { "os": "android", "cpu": "arm64" }, "sha512-50tM0zCJW5kGqgG7fQ7IHvQOcAn9TKiVRuQ/lN0xR+T2lzEFvAi1ZcS8DiksFcEpf1t/GYOeOfCAgDHFpkiSmA=="],
|
||||||
|
|
||||||
|
"@vitest/coverage-v8/vitest/vite/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.25.1", "", { "os": "android", "cpu": "x64" }, "sha512-GCj6WfUtNldqUzYkN/ITtlhwQqGWu9S45vUXs7EIYf+7rCiiqH9bCloatO9VhxsL0Pji+PF4Lz2XXCES+Q8hDw=="],
|
||||||
|
|
||||||
|
"@vitest/coverage-v8/vitest/vite/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-5hEZKPf+nQjYoSr/elb62U19/l1mZDdqidGfmFutVUjjUZrOazAtwK+Kr+3y0C/oeJfLlxo9fXb1w7L+P7E4FQ=="],
|
||||||
|
|
||||||
|
"@vitest/coverage-v8/vitest/vite/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-hxVnwL2Dqs3fM1IWq8Iezh0cX7ZGdVhbTfnOy5uURtao5OIVCEyj9xIzemDi7sRvKsuSdtCAhMKarxqtlyVyfA=="],
|
||||||
|
|
||||||
|
"@vitest/coverage-v8/vitest/vite/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-1MrCZs0fZa2g8E+FUo2ipw6jw5qqQiH+tERoS5fAfKnRx6NXH31tXBKI3VpmLijLH6yriMZsxJtaXUyFt/8Y4A=="],
|
||||||
|
|
||||||
|
"@vitest/coverage-v8/vitest/vite/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-0IZWLiTyz7nm0xuIs0q1Y3QWJC52R8aSXxe40VUxm6BB1RNmkODtW6LHvWRrGiICulcX7ZvyH6h5fqdLu4gkww=="],
|
||||||
|
|
||||||
|
"@vitest/coverage-v8/vitest/vite/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.1", "", { "os": "linux", "cpu": "arm" }, "sha512-NdKOhS4u7JhDKw9G3cY6sWqFcnLITn6SqivVArbzIaf3cemShqfLGHYMx8Xlm/lBit3/5d7kXvriTUGa5YViuQ=="],
|
||||||
|
|
||||||
|
"@vitest/coverage-v8/vitest/vite/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-jaN3dHi0/DDPelk0nLcXRm1q7DNJpjXy7yWaWvbfkPvI+7XNSc/lDOnCLN7gzsyzgu6qSAmgSvP9oXAhP973uQ=="],
|
||||||
|
|
||||||
|
"@vitest/coverage-v8/vitest/vite/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.1", "", { "os": "linux", "cpu": "ia32" }, "sha512-OJykPaF4v8JidKNGz8c/q1lBO44sQNUQtq1KktJXdBLn1hPod5rE/Hko5ugKKZd+D2+o1a9MFGUEIUwO2YfgkQ=="],
|
||||||
|
|
||||||
|
"@vitest/coverage-v8/vitest/vite/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.1", "", { "os": "linux", "cpu": "none" }, "sha512-nGfornQj4dzcq5Vp835oM/o21UMlXzn79KobKlcs3Wz9smwiifknLy4xDCLUU0BWp7b/houtdrgUz7nOGnfIYg=="],
|
||||||
|
|
||||||
|
"@vitest/coverage-v8/vitest/vite/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.1", "", { "os": "linux", "cpu": "none" }, "sha512-1osBbPEFYwIE5IVB/0g2X6i1qInZa1aIoj1TdL4AaAb55xIIgbg8Doq6a5BzYWgr+tEcDzYH67XVnTmUzL+nXg=="],
|
||||||
|
|
||||||
|
"@vitest/coverage-v8/vitest/vite/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-/6VBJOwUf3TdTvJZ82qF3tbLuWsscd7/1w+D9LH0W/SqUgM5/JJD0lrJ1fVIfZsqB6RFmLCe0Xz3fmZc3WtyVg=="],
|
||||||
|
|
||||||
|
"@vitest/coverage-v8/vitest/vite/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.1", "", { "os": "linux", "cpu": "none" }, "sha512-nSut/Mx5gnilhcq2yIMLMe3Wl4FK5wx/o0QuuCLMtmJn+WeWYoEGDN1ipcN72g1WHsnIbxGXd4i/MF0gTcuAjQ=="],
|
||||||
|
|
||||||
|
"@vitest/coverage-v8/vitest/vite/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-cEECeLlJNfT8kZHqLarDBQso9a27o2Zd2AQ8USAEoGtejOrCYHNtKP8XQhMDJMtthdF4GBmjR2au3x1udADQQQ=="],
|
||||||
|
|
||||||
|
"@vitest/coverage-v8/vitest/vite/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.1", "", { "os": "linux", "cpu": "x64" }, "sha512-xbfUhu/gnvSEg+EGovRc+kjBAkrvtk38RlerAzQxvMzlB4fXpCFCeUAYzJvrnhFtdeyVCDANSjJvOvGYoeKzFA=="],
|
||||||
|
|
||||||
|
"@vitest/coverage-v8/vitest/vite/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.1", "", { "os": "none", "cpu": "x64" }, "sha512-X53z6uXip6KFXBQ+Krbx25XHV/NCbzryM6ehOAeAil7X7oa4XIq+394PWGnwaSQ2WRA0KI6PUO6hTO5zeF5ijA=="],
|
||||||
|
|
||||||
|
"@vitest/coverage-v8/vitest/vite/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.1", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-Na9T3szbXezdzM/Kfs3GcRQNjHzM6GzFBeU1/6IV/npKP5ORtp9zbQjvkDJ47s6BCgaAZnnnu/cY1x342+MvZg=="],
|
||||||
|
|
||||||
|
"@vitest/coverage-v8/vitest/vite/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-T3H78X2h1tszfRSf+txbt5aOp/e7TAz3ptVKu9Oyir3IAOFPGV6O9c2naym5TOriy1l0nNf6a4X5UXRZSGX/dw=="],
|
||||||
|
|
||||||
|
"@vitest/coverage-v8/vitest/vite/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.1", "", { "os": "sunos", "cpu": "x64" }, "sha512-2H3RUvcmULO7dIE5EWJH8eubZAI4xw54H1ilJnRNZdeo8dTADEZ21w6J22XBkXqGJbe0+wnNJtw3UXRoLJnFEg=="],
|
||||||
|
|
||||||
|
"@vitest/coverage-v8/vitest/vite/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-GE7XvrdOzrb+yVKB9KsRMq+7a2U/K5Cf/8grVFRAGJmfADr/e/ODQ134RK2/eeHqYV5eQRFxb1hY7Nr15fv1NQ=="],
|
||||||
|
|
||||||
|
"@vitest/coverage-v8/vitest/vite/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-uOxSJCIcavSiT6UnBhBzE8wy3n0hOkJsBOzy7HDAuTDE++1DJMRRVCPGisULScHL+a/ZwdXPpXD3IyFKjA7K8A=="],
|
||||||
|
|
||||||
|
"@vitest/coverage-v8/vitest/vite/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.1", "", { "os": "win32", "cpu": "x64" }, "sha512-Y1EQdcfwMSeQN/ujR5VayLOJ1BHaK+ssyk0AEzPjC+t1lITgsnccPqFjb6V+LsTp/9Iov4ysfjxLaGJ9RPtkVg=="],
|
||||||
|
|
||||||
|
"bknd/vitest/vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.1", "", { "os": "aix", "cpu": "ppc64" }, "sha512-kfYGy8IdzTGy+z0vFGvExZtxkFlA4zAxgKEahG9KE1ScBjpQnFsNOX8KTU5ojNru5ed5CVoJYXFtoxaq5nFbjQ=="],
|
||||||
|
|
||||||
|
"bknd/vitest/vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.25.1", "", { "os": "android", "cpu": "arm" }, "sha512-dp+MshLYux6j/JjdqVLnMglQlFu+MuVeNrmT5nk6q07wNhCdSnB7QZj+7G8VMUGh1q+vj2Bq8kRsuyA00I/k+Q=="],
|
||||||
|
|
||||||
|
"bknd/vitest/vite/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.1", "", { "os": "android", "cpu": "arm64" }, "sha512-50tM0zCJW5kGqgG7fQ7IHvQOcAn9TKiVRuQ/lN0xR+T2lzEFvAi1ZcS8DiksFcEpf1t/GYOeOfCAgDHFpkiSmA=="],
|
||||||
|
|
||||||
|
"bknd/vitest/vite/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.25.1", "", { "os": "android", "cpu": "x64" }, "sha512-GCj6WfUtNldqUzYkN/ITtlhwQqGWu9S45vUXs7EIYf+7rCiiqH9bCloatO9VhxsL0Pji+PF4Lz2XXCES+Q8hDw=="],
|
||||||
|
|
||||||
|
"bknd/vitest/vite/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-5hEZKPf+nQjYoSr/elb62U19/l1mZDdqidGfmFutVUjjUZrOazAtwK+Kr+3y0C/oeJfLlxo9fXb1w7L+P7E4FQ=="],
|
||||||
|
|
||||||
|
"bknd/vitest/vite/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-hxVnwL2Dqs3fM1IWq8Iezh0cX7ZGdVhbTfnOy5uURtao5OIVCEyj9xIzemDi7sRvKsuSdtCAhMKarxqtlyVyfA=="],
|
||||||
|
|
||||||
|
"bknd/vitest/vite/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-1MrCZs0fZa2g8E+FUo2ipw6jw5qqQiH+tERoS5fAfKnRx6NXH31tXBKI3VpmLijLH6yriMZsxJtaXUyFt/8Y4A=="],
|
||||||
|
|
||||||
|
"bknd/vitest/vite/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-0IZWLiTyz7nm0xuIs0q1Y3QWJC52R8aSXxe40VUxm6BB1RNmkODtW6LHvWRrGiICulcX7ZvyH6h5fqdLu4gkww=="],
|
||||||
|
|
||||||
|
"bknd/vitest/vite/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.1", "", { "os": "linux", "cpu": "arm" }, "sha512-NdKOhS4u7JhDKw9G3cY6sWqFcnLITn6SqivVArbzIaf3cemShqfLGHYMx8Xlm/lBit3/5d7kXvriTUGa5YViuQ=="],
|
||||||
|
|
||||||
|
"bknd/vitest/vite/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-jaN3dHi0/DDPelk0nLcXRm1q7DNJpjXy7yWaWvbfkPvI+7XNSc/lDOnCLN7gzsyzgu6qSAmgSvP9oXAhP973uQ=="],
|
||||||
|
|
||||||
|
"bknd/vitest/vite/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.1", "", { "os": "linux", "cpu": "ia32" }, "sha512-OJykPaF4v8JidKNGz8c/q1lBO44sQNUQtq1KktJXdBLn1hPod5rE/Hko5ugKKZd+D2+o1a9MFGUEIUwO2YfgkQ=="],
|
||||||
|
|
||||||
|
"bknd/vitest/vite/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.1", "", { "os": "linux", "cpu": "none" }, "sha512-nGfornQj4dzcq5Vp835oM/o21UMlXzn79KobKlcs3Wz9smwiifknLy4xDCLUU0BWp7b/houtdrgUz7nOGnfIYg=="],
|
||||||
|
|
||||||
|
"bknd/vitest/vite/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.1", "", { "os": "linux", "cpu": "none" }, "sha512-1osBbPEFYwIE5IVB/0g2X6i1qInZa1aIoj1TdL4AaAb55xIIgbg8Doq6a5BzYWgr+tEcDzYH67XVnTmUzL+nXg=="],
|
||||||
|
|
||||||
|
"bknd/vitest/vite/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-/6VBJOwUf3TdTvJZ82qF3tbLuWsscd7/1w+D9LH0W/SqUgM5/JJD0lrJ1fVIfZsqB6RFmLCe0Xz3fmZc3WtyVg=="],
|
||||||
|
|
||||||
|
"bknd/vitest/vite/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.1", "", { "os": "linux", "cpu": "none" }, "sha512-nSut/Mx5gnilhcq2yIMLMe3Wl4FK5wx/o0QuuCLMtmJn+WeWYoEGDN1ipcN72g1WHsnIbxGXd4i/MF0gTcuAjQ=="],
|
||||||
|
|
||||||
|
"bknd/vitest/vite/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-cEECeLlJNfT8kZHqLarDBQso9a27o2Zd2AQ8USAEoGtejOrCYHNtKP8XQhMDJMtthdF4GBmjR2au3x1udADQQQ=="],
|
||||||
|
|
||||||
|
"bknd/vitest/vite/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.1", "", { "os": "linux", "cpu": "x64" }, "sha512-xbfUhu/gnvSEg+EGovRc+kjBAkrvtk38RlerAzQxvMzlB4fXpCFCeUAYzJvrnhFtdeyVCDANSjJvOvGYoeKzFA=="],
|
||||||
|
|
||||||
|
"bknd/vitest/vite/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.1", "", { "os": "none", "cpu": "x64" }, "sha512-X53z6uXip6KFXBQ+Krbx25XHV/NCbzryM6ehOAeAil7X7oa4XIq+394PWGnwaSQ2WRA0KI6PUO6hTO5zeF5ijA=="],
|
||||||
|
|
||||||
|
"bknd/vitest/vite/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.1", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-Na9T3szbXezdzM/Kfs3GcRQNjHzM6GzFBeU1/6IV/npKP5ORtp9zbQjvkDJ47s6BCgaAZnnnu/cY1x342+MvZg=="],
|
||||||
|
|
||||||
|
"bknd/vitest/vite/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-T3H78X2h1tszfRSf+txbt5aOp/e7TAz3ptVKu9Oyir3IAOFPGV6O9c2naym5TOriy1l0nNf6a4X5UXRZSGX/dw=="],
|
||||||
|
|
||||||
|
"bknd/vitest/vite/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.1", "", { "os": "sunos", "cpu": "x64" }, "sha512-2H3RUvcmULO7dIE5EWJH8eubZAI4xw54H1ilJnRNZdeo8dTADEZ21w6J22XBkXqGJbe0+wnNJtw3UXRoLJnFEg=="],
|
||||||
|
|
||||||
|
"bknd/vitest/vite/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-GE7XvrdOzrb+yVKB9KsRMq+7a2U/K5Cf/8grVFRAGJmfADr/e/ODQ134RK2/eeHqYV5eQRFxb1hY7Nr15fv1NQ=="],
|
||||||
|
|
||||||
|
"bknd/vitest/vite/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-uOxSJCIcavSiT6UnBhBzE8wy3n0hOkJsBOzy7HDAuTDE++1DJMRRVCPGisULScHL+a/ZwdXPpXD3IyFKjA7K8A=="],
|
||||||
|
|
||||||
|
"bknd/vitest/vite/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.1", "", { "os": "win32", "cpu": "x64" }, "sha512-Y1EQdcfwMSeQN/ujR5VayLOJ1BHaK+ssyk0AEzPjC+t1lITgsnccPqFjb6V+LsTp/9Iov4ysfjxLaGJ9RPtkVg=="],
|
||||||
|
|
||||||
"eslint/chalk/ansi-styles/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="],
|
"eslint/chalk/ansi-styles/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="],
|
||||||
|
|
||||||
"flat-cache/rimraf/glob/minimatch/brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="],
|
"flat-cache/rimraf/glob/minimatch/brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="],
|
||||||
|
|||||||
@@ -206,3 +206,33 @@ tag = "v2"
|
|||||||
renamed_classes = [{from = "DurableBkndApp", to = "CustomDurableBkndApp"}]
|
renamed_classes = [{from = "DurableBkndApp", to = "CustomDurableBkndApp"}]
|
||||||
deleted_classes = ["DurableBkndApp"]
|
deleted_classes = ["DurableBkndApp"]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## D1 Sessions (experimental)
|
||||||
|
D1 now supports to enable [global read replication](https://developers.cloudflare.com/d1/best-practices/read-replication/). This allows to reduce latency by reading from the closest region. In order for this to work, D1 has to be started from a bookmark. You can enable this behavior on bknd by setting the `d1.session` property:
|
||||||
|
|
||||||
|
```typescript src/index.ts
|
||||||
|
import { serve } from "bknd/adapter/cloudflare";
|
||||||
|
|
||||||
|
export default serve({
|
||||||
|
// currently recommended to use "fresh" mode
|
||||||
|
// otherwise consecutive requests will use the same bookmark
|
||||||
|
mode: "fresh",
|
||||||
|
// ...
|
||||||
|
d1: {
|
||||||
|
// enables D1 sessions
|
||||||
|
session: true,
|
||||||
|
// (optional) restrict the transport, options: "header" | "cookie"
|
||||||
|
// if not specified, it supports both
|
||||||
|
transport: "cookie",
|
||||||
|
// (optional) choose session constraint if not bookmark present
|
||||||
|
// options: "first-primary" | "first-unconstrained"
|
||||||
|
first: "first-primary"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
If bknd is used in a stateful user context (like in a browser), it'll automatically send the session cookie to the server to set the correct bookmark. If you need to manually set the bookmark, you can do so by setting the `x-cf-d1-session` header:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "x-cf-d1-session: <bookmark>" ...
|
||||||
|
```
|
||||||
@@ -3,13 +3,8 @@ title: 'Database'
|
|||||||
description: 'Choosing the right database configuration'
|
description: 'Choosing the right database configuration'
|
||||||
---
|
---
|
||||||
|
|
||||||
In order to use **bknd**, you need to prepare access information to your database and install
|
In order to use **bknd**, you need to prepare access information to your database and install the dependencies. Connections to the database are managed using Kysely. Therefore, all [its dialects](https://kysely.dev/docs/dialects) are theoretically supported.
|
||||||
the dependencies.
|
|
||||||
|
|
||||||
<Note>
|
|
||||||
Connections to the database are managed using Kysely. Therefore, all its dialects are
|
|
||||||
theoretically supported. However, only the `SQLite` dialect is implemented as of now.
|
|
||||||
</Note>
|
|
||||||
|
|
||||||
## Database
|
## Database
|
||||||
### SQLite in-memory
|
### SQLite in-memory
|
||||||
@@ -56,7 +51,9 @@ connection object to your new database:
|
|||||||
```
|
```
|
||||||
|
|
||||||
### Cloudflare D1
|
### Cloudflare D1
|
||||||
Using the [Cloudflare Adapter](/integration/cloudflare), you can choose to use a D1 database binding. To do so, you only need to add a D1 database to your `wrangler.toml` and it'll pick up automatically. To manually specify which D1 database to take, you can specify it manually:
|
Using the [Cloudflare Adapter](/integration/cloudflare), you can choose to use a D1 database binding. To do so, you only need to add a D1 database to your `wrangler.toml` and it'll pick up automatically.
|
||||||
|
|
||||||
|
To manually specify which D1 database to take, you can specify it explicitly:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { serve, d1 } from "bknd/adapter/cloudflare";
|
import { serve, d1 } from "bknd/adapter/cloudflare";
|
||||||
@@ -73,17 +70,19 @@ To use bknd with Postgres, you need to install the `@bknd/postgres` package. You
|
|||||||
npm install @bknd/postgres
|
npm install @bknd/postgres
|
||||||
```
|
```
|
||||||
|
|
||||||
This package uses `pg` under the hood. If you'd like to see `postgres` or any other flavor, please create an [issue on Github](https://github.com/bknd-io/bknd/issues/new).
|
You can connect to your Postgres database using `pg` or `postgres` dialects. Additionally, you may also define your custom connection.
|
||||||
|
|
||||||
To establish a connection to your database, you can use any connection options available on the [`pg`](https://node-postgres.com/apis/client) package. Here is a quick example using the [Node.js Adapter](http://localhost:3000/integration/node):
|
#### Using `pg`
|
||||||
|
|
||||||
|
To establish a connection to your database, you can use any connection options available on the [`pg`](https://node-postgres.com/apis/client) package.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
import { serve } from "bknd/adapter/node";
|
import { serve } from "bknd/adapter/node";
|
||||||
import { PostgresConnection } from "@bknd/postgres";
|
import { pg } from "@bknd/postgres";
|
||||||
|
|
||||||
/** @type {import("bknd/adapter/node").NodeBkndConfig} */
|
/** @type {import("bknd/adapter/node").NodeBkndConfig} */
|
||||||
const config = {
|
const config = {
|
||||||
connection: new PostgresConnection({
|
connection: pg({
|
||||||
connectionString:
|
connectionString:
|
||||||
"postgresql://user:password@localhost:5432/database",
|
"postgresql://user:password@localhost:5432/database",
|
||||||
}),
|
}),
|
||||||
@@ -92,6 +91,64 @@ const config = {
|
|||||||
serve(config);
|
serve(config);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Using `postgres`
|
||||||
|
|
||||||
|
To establish a connection to your database, you can use any connection options available on the [`postgres`](https://github.com/porsager/postgres) package.
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { serve } from "bknd/adapter/node";
|
||||||
|
import { postgresJs } from "@bknd/postgres";
|
||||||
|
|
||||||
|
serve({
|
||||||
|
connection: postgresJs("postgresql://user:password@localhost:5432/database"),
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Using custom connection
|
||||||
|
|
||||||
|
Several Postgres hosting providers offer their own clients to connect to their database, e.g. suitable for serverless environments.
|
||||||
|
|
||||||
|
Example using `@neondatabase/serverless`:
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { createCustomPostgresConnection } from "@bknd/postgres";
|
||||||
|
import { NeonDialect } from "kysely-neon";
|
||||||
|
|
||||||
|
const connection = createCustomPostgresConnection(NeonDialect)({
|
||||||
|
connectionString: process.env.NEON,
|
||||||
|
});
|
||||||
|
|
||||||
|
serve({
|
||||||
|
connection: connection,
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Example using `@xata.io/client`:
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { createCustomPostgresConnection } from "@bknd/postgres";
|
||||||
|
import { XataDialect } from "@xata.io/kysely";
|
||||||
|
import { buildClient } from "@xata.io/client";
|
||||||
|
|
||||||
|
const client = buildClient();
|
||||||
|
const xata = new client({
|
||||||
|
databaseURL: process.env.XATA_URL,
|
||||||
|
apiKey: process.env.XATA_API_KEY,
|
||||||
|
branch: process.env.XATA_BRANCH,
|
||||||
|
});
|
||||||
|
|
||||||
|
const connection = createCustomPostgresConnection(XataDialect, {
|
||||||
|
supports: {
|
||||||
|
batching: false,
|
||||||
|
},
|
||||||
|
})({ xata });
|
||||||
|
|
||||||
|
serve({
|
||||||
|
connection: connection,
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
### SQLocal
|
### SQLocal
|
||||||
To use bknd with `sqlocal` for a offline expierence, you need to install the `@bknd/sqlocal` package. You can do so by running the following command:
|
To use bknd with `sqlocal` for a offline expierence, you need to install the `@bknd/sqlocal` package. You can do so by running the following command:
|
||||||
|
|
||||||
@@ -138,7 +195,11 @@ const app = createApp({ connection })
|
|||||||
## Initial Structure
|
## Initial Structure
|
||||||
To provide an initial database structure, you can pass `initialConfig` to the creation of an app. This will only be used if there isn't an existing configuration found in the database given. Here is a quick example:
|
To provide an initial database structure, you can pass `initialConfig` to the creation of an app. This will only be used if there isn't an existing configuration found in the database given. Here is a quick example:
|
||||||
|
|
||||||
```ts
|
<Note>
|
||||||
|
The initial structure is only respected if the database is empty! If you made updates, ensure to delete the database first, or perform updates through the Admin UI.
|
||||||
|
</Note>
|
||||||
|
|
||||||
|
```typescript
|
||||||
import { createApp } from "bknd";
|
import { createApp } from "bknd";
|
||||||
import { em, entity, text, number } from "bknd/data";
|
import { em, entity, text, number } from "bknd/data";
|
||||||
|
|
||||||
@@ -193,9 +254,13 @@ Note that we didn't add relational fields directly to the entity, but instead de
|
|||||||
</Note>
|
</Note>
|
||||||
|
|
||||||
### Type completion
|
### Type completion
|
||||||
|
To get type completion, there are two options:
|
||||||
|
1. Use the CLI to [generate the types](/usage/cli#generating-types-types)
|
||||||
|
2. If you have an initial structure created with the prototype functions, you can extend the `DB` interface with your own schema.
|
||||||
|
|
||||||
All entity related functions use the types defined in `DB` from `bknd/core`. To get type completion, you can extend that interface with your own schema:
|
All entity related functions use the types defined in `DB` from `bknd/core`. To get type completion, you can extend that interface with your own schema:
|
||||||
|
|
||||||
```ts
|
```typescript
|
||||||
import { em } from "bknd/data";
|
import { em } from "bknd/data";
|
||||||
import { Api } from "bknd/client";
|
import { Api } from "bknd/client";
|
||||||
|
|
||||||
@@ -217,10 +282,12 @@ The type completion is available for the API as well as all provided [React hook
|
|||||||
To seed your database with initial data, you can pass a `seed` function to the configuration. It
|
To seed your database with initial data, you can pass a `seed` function to the configuration. It
|
||||||
provides the `ModuleBuildContext` as the first argument.
|
provides the `ModuleBuildContext` as the first argument.
|
||||||
|
|
||||||
|
<Note>
|
||||||
Note that the seed function will only be executed on app's first boot. If a configuration
|
Note that the seed function will only be executed on app's first boot. If a configuration
|
||||||
already exists in the database, it will not be executed.
|
already exists in the database, it will not be executed.
|
||||||
|
</Note>
|
||||||
|
|
||||||
```ts
|
```typescript
|
||||||
import { createApp, type ModuleBuildContext } from "bknd";
|
import { createApp, type ModuleBuildContext } from "bknd";
|
||||||
|
|
||||||
const app = createApp({
|
const app = createApp({
|
||||||
|
|||||||
@@ -5,6 +5,6 @@ import react from "@astrojs/react";
|
|||||||
|
|
||||||
// https://astro.build/config
|
// https://astro.build/config
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
output: "hybrid",
|
output: "server",
|
||||||
integrations: [react()]
|
integrations: [react()],
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -9,11 +9,10 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bknd": "file:../../app",
|
"bknd": "file:../../app",
|
||||||
"kysely-d1": "^0.3.0"
|
"kysely-d1": "^0.4.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@cloudflare/workers-types": "^4.20240620.0",
|
"typescript": "^5.8.3",
|
||||||
"typescript": "^5.5.3",
|
"wrangler": "^4.19.1"
|
||||||
"wrangler": "^4.4.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
/// <reference types="@cloudflare/workers-types" />
|
|
||||||
|
|
||||||
import { serve } from "bknd/adapter/cloudflare";
|
import { serve } from "bknd/adapter/cloudflare";
|
||||||
|
|
||||||
export default serve({
|
export default serve({
|
||||||
mode: "warm",
|
mode: "warm",
|
||||||
onBuilt: async (app) => {
|
d1: {
|
||||||
app.modules.server.get("/custom", (c) => c.json({ hello: "world" }));
|
session: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
"jsx": "react-jsx",
|
"jsx": "react-jsx",
|
||||||
"module": "es2022",
|
"module": "es2022",
|
||||||
"moduleResolution": "Bundler",
|
"moduleResolution": "Bundler",
|
||||||
"types": ["@cloudflare/workers-types/2023-07-01"],
|
"types": ["./worker-configuration.d.ts"],
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"checkJs": false,
|
"checkJs": false,
|
||||||
|
|||||||
@@ -1,12 +1,8 @@
|
|||||||
// Generated by Wrangler
|
// placeholder, run generation again
|
||||||
// After adding bindings to `wrangler.toml`, regenerate this interface via `npm run cf-typegen`
|
declare namespace Cloudflare {
|
||||||
|
|
||||||
interface Env {
|
interface Env {
|
||||||
DB_URL: string;
|
BUCKET: R2Bucket;
|
||||||
DB_TOKEN: string;
|
DB: D1Database;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module "__STATIC_CONTENT_MANIFEST" {
|
|
||||||
const value: string;
|
|
||||||
export default value;
|
|
||||||
}
|
}
|
||||||
|
interface Env extends Cloudflare.Env {}
|
||||||
|
|||||||
@@ -15,8 +15,8 @@
|
|||||||
"d1_databases": [
|
"d1_databases": [
|
||||||
{
|
{
|
||||||
"binding": "DB",
|
"binding": "DB",
|
||||||
"database_name": "bknd-cf-example",
|
"database_name": "bknd-dev-weur",
|
||||||
"database_id": "7ad67953-2bbf-47fc-8696-f4517dbfe674"
|
"database_id": "81d8dfcc-4eaf-4453-8f0f-8f6d463fb867"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"r2_buckets": [
|
"r2_buckets": [
|
||||||
|
|||||||
@@ -21,7 +21,6 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "1.9.4",
|
"@biomejs/biome": "1.9.4",
|
||||||
"@clack/prompts": "^0.10.0",
|
"@clack/prompts": "^0.10.0",
|
||||||
"@cloudflare/workers-types": "^4.20240620.0",
|
|
||||||
"@tsconfig/strictest": "^2.0.5",
|
"@tsconfig/strictest": "^2.0.5",
|
||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"bun-types": "^1.1.18",
|
"bun-types": "^1.1.18",
|
||||||
@@ -42,8 +41,5 @@
|
|||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=20.0.0"
|
"node": ">=20.0.0"
|
||||||
},
|
},
|
||||||
"workspaces": [
|
"workspaces": ["app", "packages/*"]
|
||||||
"app",
|
|
||||||
"packages/*"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
# Postgres adapter for `bknd` (experimental)
|
# Postgres adapter for `bknd` (experimental)
|
||||||
This packages adds an adapter to use a Postgres database with [`bknd`](https://github.com/bknd-io/bknd). It is based on [`pg`](https://github.com/brianc/node-postgres) and the driver included in [`kysely`](https://github.com/kysely-org/kysely).
|
This packages adds an adapter to use a Postgres database with [`bknd`](https://github.com/bknd-io/bknd). It works with both `pg` and `postgres` drivers, and supports custom postgres connections.
|
||||||
|
* works with any Postgres database (tested with Supabase, Neon, Xata, and RDS)
|
||||||
|
* choose between `pg` and `postgres` drivers
|
||||||
|
* create custom postgres connections with any kysely postgres dialect
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
Install the adapter with:
|
Install the adapter with:
|
||||||
@@ -7,44 +10,93 @@ Install the adapter with:
|
|||||||
npm install @bknd/postgres
|
npm install @bknd/postgres
|
||||||
```
|
```
|
||||||
|
|
||||||
## Usage
|
## Using `pg` driver
|
||||||
|
Install the [`pg`](https://github.com/brianc/node-postgres) driver with:
|
||||||
|
```bash
|
||||||
|
npm install pg
|
||||||
|
```
|
||||||
|
|
||||||
Create a connection:
|
Create a connection:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { PostgresConnection } from "@bknd/postgres";
|
import { pg } from "@bknd/postgres";
|
||||||
|
|
||||||
const connection = new PostgresConnection({
|
// accepts `pg` configuration
|
||||||
|
const connection = pg({
|
||||||
host: "localhost",
|
host: "localhost",
|
||||||
port: 5432,
|
port: 5432,
|
||||||
user: "postgres",
|
user: "postgres",
|
||||||
password: "postgres",
|
password: "postgres",
|
||||||
database: "bknd",
|
database: "postgres",
|
||||||
|
});
|
||||||
|
|
||||||
|
// or with a connection string
|
||||||
|
const connection = pg({
|
||||||
|
connectionString: "postgres://postgres:postgres@localhost:5432/postgres",
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
Use the connection depending on which framework or runtime you are using. E.g., when using `createApp`, you can use the connection as follows:
|
## Using `postgres` driver
|
||||||
|
|
||||||
```ts
|
Install the [`postgres`](https://github.com/porsager/postgres) driver with:
|
||||||
import { createApp } from "bknd";
|
```bash
|
||||||
import { PostgresConnection } from "@bknd/postgres";
|
npm install postgres
|
||||||
|
|
||||||
const connection = new PostgresConnection();
|
|
||||||
const app = createApp({ connection });
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Or if you're using it with a framework, say Next.js, you can add the connection object to where you're initializating the app:
|
Create a connection:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
// e.g. in src/app/api/[[...bknd]]/route.ts
|
import { postgresJs } from "@bknd/postgres";
|
||||||
import { serve } from "bknd/adapter/nextjs";
|
|
||||||
import { PostgresConnection } from "@bknd/postgres";
|
|
||||||
|
|
||||||
const connection = new PostgresConnection();
|
// accepts `postgres` configuration
|
||||||
const handler = serve({
|
const connection = postgresJs("postgres://postgres:postgres@localhost:5432/postgres");
|
||||||
connection
|
|
||||||
})
|
|
||||||
|
|
||||||
// ...
|
|
||||||
```
|
```
|
||||||
|
|
||||||
For more information about how to integrate Next.js in general, check out the [Next.js documentation](https://docs.bknd.io/integration/nextjs).
|
## Using custom postgres dialects
|
||||||
|
|
||||||
|
You can create a custom kysely postgres dialect by using the `createCustomPostgresConnection` function.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { createCustomPostgresConnection } from "@bknd/postgres";
|
||||||
|
|
||||||
|
const connection = createCustomPostgresConnection(MyDialect)({
|
||||||
|
// your custom dialect configuration
|
||||||
|
supports: {
|
||||||
|
batching: true
|
||||||
|
},
|
||||||
|
excludeTables: ["my_table"],
|
||||||
|
plugins: [new MyKyselyPlugin()],
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Custom `neon` connection
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { createCustomPostgresConnection } from "@bknd/postgres";
|
||||||
|
import { NeonDialect } from "kysely-neon";
|
||||||
|
|
||||||
|
const connection = createCustomPostgresConnection(NeonDialect)({
|
||||||
|
connectionString: process.env.NEON,
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Custom `xata` connection
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { createCustomPostgresConnection } from "@bknd/postgres";
|
||||||
|
import { XataDialect } from "@xata.io/kysely";
|
||||||
|
import { buildClient } from "@xata.io/client";
|
||||||
|
|
||||||
|
const client = buildClient();
|
||||||
|
const xata = new client({
|
||||||
|
databaseURL: process.env.XATA_URL,
|
||||||
|
apiKey: process.env.XATA_API_KEY,
|
||||||
|
branch: process.env.XATA_BRANCH,
|
||||||
|
});
|
||||||
|
|
||||||
|
const connection = createCustomPostgresConnection(XataDialect, {
|
||||||
|
supports: {
|
||||||
|
batching: false,
|
||||||
|
},
|
||||||
|
})({ xata });
|
||||||
|
```
|
||||||
14
packages/postgres/examples/neon.ts
Normal file
14
packages/postgres/examples/neon.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { serve } from "bknd/adapter/bun";
|
||||||
|
import { createCustomPostgresConnection } from "../src";
|
||||||
|
import { NeonDialect } from "kysely-neon";
|
||||||
|
|
||||||
|
const neon = createCustomPostgresConnection(NeonDialect);
|
||||||
|
|
||||||
|
export default serve({
|
||||||
|
connection: neon({
|
||||||
|
connectionString: process.env.NEON,
|
||||||
|
}),
|
||||||
|
// ignore this, it's only required within this repository
|
||||||
|
// because bknd is installed via "workspace:*"
|
||||||
|
distPath: "../../app/dist",
|
||||||
|
});
|
||||||
24
packages/postgres/examples/xata.ts
Normal file
24
packages/postgres/examples/xata.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { serve } from "bknd/adapter/bun";
|
||||||
|
import { createCustomPostgresConnection } from "../src";
|
||||||
|
import { XataDialect } from "@xata.io/kysely";
|
||||||
|
import { buildClient } from "@xata.io/client";
|
||||||
|
|
||||||
|
const client = buildClient();
|
||||||
|
const xata = new client({
|
||||||
|
databaseURL: process.env.XATA_URL,
|
||||||
|
apiKey: process.env.XATA_API_KEY,
|
||||||
|
branch: process.env.XATA_BRANCH,
|
||||||
|
});
|
||||||
|
|
||||||
|
const connection = createCustomPostgresConnection(XataDialect, {
|
||||||
|
supports: {
|
||||||
|
batching: false,
|
||||||
|
},
|
||||||
|
})({ xata });
|
||||||
|
|
||||||
|
export default serve({
|
||||||
|
connection,
|
||||||
|
// ignore this, it's only required within this repository
|
||||||
|
// because bknd is installed via "workspace:*"
|
||||||
|
distPath: "../../../app/dist",
|
||||||
|
});
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@bknd/postgres",
|
"name": "@bknd/postgres",
|
||||||
"version": "0.0.1",
|
"version": "0.1.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"module": "dist/index.js",
|
"module": "dist/index.js",
|
||||||
@@ -17,15 +17,20 @@
|
|||||||
"docker:start": "docker run --rm --name bknd-test-postgres -d -e POSTGRES_PASSWORD=postgres -e POSTGRES_USER=postgres -e POSTGRES_DB=bknd -p 5430:5432 postgres:17",
|
"docker:start": "docker run --rm --name bknd-test-postgres -d -e POSTGRES_PASSWORD=postgres -e POSTGRES_USER=postgres -e POSTGRES_DB=bknd -p 5430:5432 postgres:17",
|
||||||
"docker:stop": "docker stop bknd-test-postgres"
|
"docker:stop": "docker stop bknd-test-postgres"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"optionalDependencies": {
|
||||||
|
"kysely": "^0.27.6",
|
||||||
|
"kysely-postgres-js": "^2.0.0",
|
||||||
"pg": "^8.14.0",
|
"pg": "^8.14.0",
|
||||||
"kysely": "^0.27.6"
|
"postgres": "^3.4.7"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/bun": "^1.2.5",
|
"@types/bun": "^1.2.5",
|
||||||
"@types/node": "^22.13.10",
|
"@types/node": "^22.13.10",
|
||||||
"@types/pg": "^8.11.11",
|
"@types/pg": "^8.11.11",
|
||||||
|
"@xata.io/client": "^0.0.0-next.v93343b9646f57a1e5c51c35eccf0767c2bb80baa",
|
||||||
|
"@xata.io/kysely": "^0.2.1",
|
||||||
"bknd": "workspace:*",
|
"bknd": "workspace:*",
|
||||||
|
"kysely-neon": "^1.3.0",
|
||||||
"tsup": "^8.4.0",
|
"tsup": "^8.4.0",
|
||||||
"typescript": "^5.8.2"
|
"typescript": "^5.8.2"
|
||||||
},
|
},
|
||||||
@@ -33,10 +38,11 @@
|
|||||||
"entry": ["src/index.ts"],
|
"entry": ["src/index.ts"],
|
||||||
"format": ["esm"],
|
"format": ["esm"],
|
||||||
"target": "es2022",
|
"target": "es2022",
|
||||||
|
"metafile": true,
|
||||||
"clean": true,
|
"clean": true,
|
||||||
"minify": true,
|
"minify": true,
|
||||||
"dts": true,
|
"dts": true,
|
||||||
"external": ["bknd", "pg", "kysely"]
|
"external": ["bknd", "pg", "postgres", "kysely", "kysely-postgres-js"]
|
||||||
},
|
},
|
||||||
"files": ["dist", "README.md", "!*.map", "!metafile*.json"]
|
"files": ["dist", "README.md", "!*.map", "!metafile*.json"]
|
||||||
}
|
}
|
||||||
|
|||||||
32
packages/postgres/src/PgPostgresConnection.ts
Normal file
32
packages/postgres/src/PgPostgresConnection.ts
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import { Kysely, PostgresDialect } from "kysely";
|
||||||
|
import { PostgresIntrospector } from "./PostgresIntrospector";
|
||||||
|
import { PostgresConnection, plugins } from "./PostgresConnection";
|
||||||
|
import { customIntrospector } from "bknd/data";
|
||||||
|
import $pg from "pg";
|
||||||
|
|
||||||
|
export type PgPostgresConnectionConfig = $pg.PoolConfig;
|
||||||
|
|
||||||
|
export class PgPostgresConnection extends PostgresConnection {
|
||||||
|
private pool: $pg.Pool;
|
||||||
|
|
||||||
|
constructor(config: PgPostgresConnectionConfig) {
|
||||||
|
const pool = new $pg.Pool(config);
|
||||||
|
const kysely = new Kysely({
|
||||||
|
dialect: customIntrospector(PostgresDialect, PostgresIntrospector, {
|
||||||
|
excludeTables: [],
|
||||||
|
}).create({ pool }),
|
||||||
|
plugins,
|
||||||
|
});
|
||||||
|
|
||||||
|
super(kysely);
|
||||||
|
this.pool = pool;
|
||||||
|
}
|
||||||
|
|
||||||
|
override async close(): Promise<void> {
|
||||||
|
await this.pool.end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function pg(config: PgPostgresConnectionConfig): PgPostgresConnection {
|
||||||
|
return new PgPostgresConnection(config);
|
||||||
|
}
|
||||||
@@ -1,61 +1,44 @@
|
|||||||
import { Connection, type FieldSpec, type SchemaResponse } from "bknd/data";
|
import { Connection, type DbFunctions, type FieldSpec, type SchemaResponse } from "bknd/data";
|
||||||
import {
|
import {
|
||||||
|
ParseJSONResultsPlugin,
|
||||||
type ColumnDataType,
|
type ColumnDataType,
|
||||||
type ColumnDefinitionBuilder,
|
type ColumnDefinitionBuilder,
|
||||||
type DatabaseIntrospector,
|
type Kysely,
|
||||||
Kysely,
|
type KyselyPlugin,
|
||||||
ParseJSONResultsPlugin,
|
|
||||||
PostgresDialect,
|
|
||||||
type SelectQueryBuilder,
|
type SelectQueryBuilder,
|
||||||
} from "kysely";
|
} from "kysely";
|
||||||
import { jsonArrayFrom, jsonBuildObject, jsonObjectFrom } from "kysely/helpers/postgres";
|
import { jsonArrayFrom, jsonBuildObject, jsonObjectFrom } from "kysely/helpers/postgres";
|
||||||
import pg from "pg";
|
|
||||||
import { PostgresIntrospector } from "./PostgresIntrospector";
|
|
||||||
|
|
||||||
export type PostgresConnectionConfig = pg.PoolConfig;
|
|
||||||
export type QB = SelectQueryBuilder<any, any, any>;
|
export type QB = SelectQueryBuilder<any, any, any>;
|
||||||
|
|
||||||
const plugins = [new ParseJSONResultsPlugin()];
|
export const plugins = [new ParseJSONResultsPlugin()];
|
||||||
|
|
||||||
class CustomPostgresDialect extends PostgresDialect {
|
export abstract class PostgresConnection<DB = any> extends Connection<DB> {
|
||||||
override createIntrospector(db: Kysely<any>): DatabaseIntrospector {
|
|
||||||
return new PostgresIntrospector(db, {
|
|
||||||
excludeTables: [],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class PostgresConnection extends Connection {
|
|
||||||
protected override readonly supported = {
|
protected override readonly supported = {
|
||||||
batching: true,
|
batching: true,
|
||||||
};
|
};
|
||||||
private pool: pg.Pool;
|
|
||||||
|
|
||||||
constructor(config: PostgresConnectionConfig) {
|
|
||||||
const pool = new pg.Pool(config);
|
|
||||||
const kysely = new Kysely({
|
|
||||||
dialect: new CustomPostgresDialect({
|
|
||||||
pool,
|
|
||||||
}),
|
|
||||||
plugins,
|
|
||||||
//log: ["query", "error"],
|
|
||||||
});
|
|
||||||
|
|
||||||
|
constructor(kysely: Kysely<DB>, fn?: Partial<DbFunctions>, _plugins?: KyselyPlugin[]) {
|
||||||
super(
|
super(
|
||||||
kysely,
|
kysely,
|
||||||
{
|
fn ?? {
|
||||||
jsonArrayFrom,
|
jsonArrayFrom,
|
||||||
jsonBuildObject,
|
jsonBuildObject,
|
||||||
jsonObjectFrom,
|
jsonObjectFrom,
|
||||||
},
|
},
|
||||||
plugins,
|
_plugins ?? plugins,
|
||||||
);
|
);
|
||||||
this.pool = pool;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override getFieldSchema(spec: FieldSpec): SchemaResponse {
|
override getFieldSchema(spec: FieldSpec): SchemaResponse {
|
||||||
this.validateFieldSpecType(spec.type);
|
this.validateFieldSpecType(spec.type);
|
||||||
let type: ColumnDataType = spec.primary ? "serial" : spec.type;
|
let type: ColumnDataType = spec.type;
|
||||||
|
|
||||||
|
if (spec.primary) {
|
||||||
|
if (spec.type === "integer") {
|
||||||
|
type = "serial";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch (spec.type) {
|
switch (spec.type) {
|
||||||
case "blob":
|
case "blob":
|
||||||
@@ -90,10 +73,6 @@ export class PostgresConnection extends Connection {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
override async close(): Promise<void> {
|
|
||||||
await this.pool.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override async batch<Queries extends QB[]>(
|
protected override async batch<Queries extends QB[]>(
|
||||||
queries: [...Queries],
|
queries: [...Queries],
|
||||||
): Promise<{
|
): Promise<{
|
||||||
|
|||||||
41
packages/postgres/src/PostgresJsConnection.ts
Normal file
41
packages/postgres/src/PostgresJsConnection.ts
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import { Kysely } from "kysely";
|
||||||
|
import { PostgresIntrospector } from "./PostgresIntrospector";
|
||||||
|
import { PostgresConnection, plugins } from "./PostgresConnection";
|
||||||
|
import { customIntrospector } from "bknd/data";
|
||||||
|
import { PostgresJSDialect } from "kysely-postgres-js";
|
||||||
|
import $postgresJs, { type Sql, type Options, type PostgresType } from "postgres";
|
||||||
|
|
||||||
|
export type PostgresJsConfig = Options<Record<string, PostgresType>>;
|
||||||
|
|
||||||
|
export class PostgresJsConnection extends PostgresConnection {
|
||||||
|
private postgres: Sql;
|
||||||
|
|
||||||
|
constructor(opts: { postgres: Sql }) {
|
||||||
|
const kysely = new Kysely({
|
||||||
|
dialect: customIntrospector(PostgresJSDialect, PostgresIntrospector, {
|
||||||
|
excludeTables: [],
|
||||||
|
}).create({ postgres: opts.postgres }),
|
||||||
|
plugins,
|
||||||
|
});
|
||||||
|
|
||||||
|
super(kysely);
|
||||||
|
this.postgres = opts.postgres;
|
||||||
|
}
|
||||||
|
|
||||||
|
override async close(): Promise<void> {
|
||||||
|
await this.postgres.end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function postgresJs(
|
||||||
|
connectionString: string,
|
||||||
|
config?: PostgresJsConfig,
|
||||||
|
): PostgresJsConnection;
|
||||||
|
export function postgresJs(config: PostgresJsConfig): PostgresJsConnection;
|
||||||
|
export function postgresJs(
|
||||||
|
first: PostgresJsConfig | string,
|
||||||
|
second?: PostgresJsConfig,
|
||||||
|
): PostgresJsConnection {
|
||||||
|
const postgres = typeof first === "string" ? $postgresJs(first, second) : $postgresJs(first);
|
||||||
|
return new PostgresJsConnection({ postgres });
|
||||||
|
}
|
||||||
43
packages/postgres/src/custom.ts
Normal file
43
packages/postgres/src/custom.ts
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import type { Constructor } from "bknd/core";
|
||||||
|
import { customIntrospector, type DbFunctions } from "bknd/data";
|
||||||
|
import { Kysely, type Dialect, type KyselyPlugin } from "kysely";
|
||||||
|
import { plugins, PostgresConnection } from "./PostgresConnection";
|
||||||
|
import { PostgresIntrospector } from "./PostgresIntrospector";
|
||||||
|
|
||||||
|
export type CustomPostgresConnection = {
|
||||||
|
supports?: PostgresConnection["supported"];
|
||||||
|
fn?: Partial<DbFunctions>;
|
||||||
|
plugins?: KyselyPlugin[];
|
||||||
|
excludeTables?: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export function createCustomPostgresConnection<
|
||||||
|
T extends Constructor<Dialect>,
|
||||||
|
C extends ConstructorParameters<T>[0],
|
||||||
|
>(
|
||||||
|
dialect: Constructor<Dialect>,
|
||||||
|
options?: CustomPostgresConnection,
|
||||||
|
): (config: C) => PostgresConnection<any> {
|
||||||
|
const supported = {
|
||||||
|
batching: true,
|
||||||
|
...((options?.supports ?? {}) as any),
|
||||||
|
};
|
||||||
|
|
||||||
|
return (config: C) =>
|
||||||
|
new (class extends PostgresConnection<any> {
|
||||||
|
protected override readonly supported = supported;
|
||||||
|
|
||||||
|
constructor(config: C) {
|
||||||
|
super(
|
||||||
|
new Kysely({
|
||||||
|
dialect: customIntrospector(dialect, PostgresIntrospector, {
|
||||||
|
excludeTables: options?.excludeTables ?? [],
|
||||||
|
}).create(config),
|
||||||
|
plugins: options?.plugins ?? plugins,
|
||||||
|
}),
|
||||||
|
options?.fn,
|
||||||
|
options?.plugins,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})(config);
|
||||||
|
}
|
||||||
@@ -1,2 +1,5 @@
|
|||||||
export { PostgresConnection, type PostgresConnectionConfig } from "./PostgresConnection";
|
export { pg, PgPostgresConnection, type PgPostgresConnectionConfig } from "./PgPostgresConnection";
|
||||||
export { PostgresIntrospector } from "./PostgresIntrospector";
|
export { PostgresIntrospector } from "./PostgresIntrospector";
|
||||||
|
export { PostgresConnection, type QB, plugins } from "./PostgresConnection";
|
||||||
|
export { postgresJs, PostgresJsConnection, type PostgresJsConfig } from "./PostgresJsConnection";
|
||||||
|
export { createCustomPostgresConnection } from "./custom";
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
import { describe, it, expect } from "bun:test";
|
|
||||||
|
|
||||||
import { PostgresConnection } from "../src";
|
|
||||||
import { createConnection, cleanDatabase } from "./setup";
|
|
||||||
|
|
||||||
describe(PostgresConnection, () => {
|
|
||||||
it("should connect to the database", async () => {
|
|
||||||
const connection = createConnection();
|
|
||||||
expect(await connection.ping()).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should clean the database", async () => {
|
|
||||||
const connection = createConnection();
|
|
||||||
await cleanDatabase(connection);
|
|
||||||
|
|
||||||
const tables = await connection.getIntrospector().getTables();
|
|
||||||
expect(tables).toEqual([]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,113 +0,0 @@
|
|||||||
import { describe, it, expect, beforeAll, afterAll, afterEach } from "bun:test";
|
|
||||||
|
|
||||||
import { createApp } from "bknd";
|
|
||||||
import * as proto from "bknd/data";
|
|
||||||
|
|
||||||
import { createConnection, cleanDatabase } from "./setup";
|
|
||||||
import type { PostgresConnection } from "../src";
|
|
||||||
|
|
||||||
let connection: PostgresConnection;
|
|
||||||
beforeAll(async () => {
|
|
||||||
connection = createConnection();
|
|
||||||
await cleanDatabase(connection);
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(async () => {
|
|
||||||
await cleanDatabase(connection);
|
|
||||||
});
|
|
||||||
|
|
||||||
afterAll(async () => {
|
|
||||||
await connection.close();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("integration", () => {
|
|
||||||
it("should create app and ping", async () => {
|
|
||||||
const app = createApp({
|
|
||||||
connection,
|
|
||||||
});
|
|
||||||
await app.build();
|
|
||||||
|
|
||||||
expect(app.version()).toBeDefined();
|
|
||||||
expect(await app.em.ping()).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should create a basic schema", async () => {
|
|
||||||
const schema = proto.em(
|
|
||||||
{
|
|
||||||
posts: proto.entity("posts", {
|
|
||||||
title: proto.text().required(),
|
|
||||||
content: proto.text(),
|
|
||||||
}),
|
|
||||||
comments: proto.entity("comments", {
|
|
||||||
content: proto.text(),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
(fns, s) => {
|
|
||||||
fns.relation(s.comments).manyToOne(s.posts);
|
|
||||||
fns.index(s.posts).on(["title"], true);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
const app = createApp({
|
|
||||||
connection,
|
|
||||||
initialConfig: {
|
|
||||||
data: schema.toJSON(),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
await app.build();
|
|
||||||
|
|
||||||
expect(app.em.entities.length).toBe(2);
|
|
||||||
expect(app.em.entities.map((e) => e.name)).toEqual(["posts", "comments"]);
|
|
||||||
|
|
||||||
const api = app.getApi();
|
|
||||||
|
|
||||||
expect(
|
|
||||||
(
|
|
||||||
await api.data.createMany("posts", [
|
|
||||||
{
|
|
||||||
title: "Hello",
|
|
||||||
content: "World",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Hello 2",
|
|
||||||
content: "World 2",
|
|
||||||
},
|
|
||||||
])
|
|
||||||
).data,
|
|
||||||
).toEqual([
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
title: "Hello",
|
|
||||||
content: "World",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
title: "Hello 2",
|
|
||||||
content: "World 2",
|
|
||||||
},
|
|
||||||
] as any);
|
|
||||||
|
|
||||||
// try to create an existing
|
|
||||||
expect(
|
|
||||||
(
|
|
||||||
await api.data.createOne("posts", {
|
|
||||||
title: "Hello",
|
|
||||||
})
|
|
||||||
).ok,
|
|
||||||
).toBe(false);
|
|
||||||
|
|
||||||
// add a comment to a post
|
|
||||||
await api.data.createOne("comments", {
|
|
||||||
content: "Hello",
|
|
||||||
posts_id: 1,
|
|
||||||
});
|
|
||||||
|
|
||||||
// and then query using a `with` property
|
|
||||||
const result = await api.data.readMany("posts", { with: ["comments"] });
|
|
||||||
expect(result.length).toBe(2);
|
|
||||||
expect(result[0].comments.length).toBe(1);
|
|
||||||
expect(result[0].comments[0].content).toBe("Hello");
|
|
||||||
expect(result[1].comments.length).toBe(0);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
16
packages/postgres/test/pg.test.ts
Normal file
16
packages/postgres/test/pg.test.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { describe } from "bun:test";
|
||||||
|
import { pg } from "../src/PgPostgresConnection";
|
||||||
|
import { testSuite } from "./suite";
|
||||||
|
|
||||||
|
describe("pg", () => {
|
||||||
|
testSuite({
|
||||||
|
createConnection: () =>
|
||||||
|
pg({
|
||||||
|
host: "localhost",
|
||||||
|
port: 5430,
|
||||||
|
user: "postgres",
|
||||||
|
password: "postgres",
|
||||||
|
database: "bknd",
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
});
|
||||||
16
packages/postgres/test/postgresjs.test.ts
Normal file
16
packages/postgres/test/postgresjs.test.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { describe } from "bun:test";
|
||||||
|
import { postgresJs } from "../src/PostgresJsConnection";
|
||||||
|
import { testSuite } from "./suite";
|
||||||
|
|
||||||
|
describe("postgresjs", () => {
|
||||||
|
testSuite({
|
||||||
|
createConnection: () =>
|
||||||
|
postgresJs({
|
||||||
|
host: "localhost",
|
||||||
|
port: 5430,
|
||||||
|
user: "postgres",
|
||||||
|
password: "postgres",
|
||||||
|
database: "bknd",
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
import type { Kysely } from "kysely";
|
|
||||||
import { PostgresConnection, PostgresIntrospector, type PostgresConnectionConfig } from "../src";
|
|
||||||
|
|
||||||
export const info = {
|
|
||||||
host: "localhost",
|
|
||||||
port: 5430,
|
|
||||||
user: "postgres",
|
|
||||||
password: "postgres",
|
|
||||||
database: "bknd",
|
|
||||||
};
|
|
||||||
|
|
||||||
export function createConnection(config: PostgresConnectionConfig = {}) {
|
|
||||||
return new PostgresConnection({
|
|
||||||
...info,
|
|
||||||
...config,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function cleanDatabase(connection: PostgresConnection) {
|
|
||||||
const kysely = connection.kysely;
|
|
||||||
|
|
||||||
// drop all tables & create new schema
|
|
||||||
await kysely.schema.dropSchema("public").ifExists().cascade().execute();
|
|
||||||
await kysely.schema.createSchema("public").execute();
|
|
||||||
}
|
|
||||||
197
packages/postgres/test/suite.ts
Normal file
197
packages/postgres/test/suite.ts
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
import { describe, beforeAll, afterAll, expect, it, afterEach } from "bun:test";
|
||||||
|
import type { PostgresConnection } from "../src";
|
||||||
|
import { createApp } from "bknd";
|
||||||
|
import * as proto from "bknd/data";
|
||||||
|
import { disableConsoleLog, enableConsoleLog } from "bknd/utils";
|
||||||
|
|
||||||
|
export type TestSuiteConfig = {
|
||||||
|
createConnection: () => InstanceType<typeof PostgresConnection>;
|
||||||
|
cleanDatabase?: (connection: InstanceType<typeof PostgresConnection>) => Promise<void>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function defaultCleanDatabase(connection: InstanceType<typeof PostgresConnection>) {
|
||||||
|
const kysely = connection.kysely;
|
||||||
|
|
||||||
|
// drop all tables & create new schema
|
||||||
|
await kysely.schema.dropSchema("public").ifExists().cascade().execute();
|
||||||
|
await kysely.schema.createSchema("public").execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function cleanDatabase(
|
||||||
|
connection: InstanceType<typeof PostgresConnection>,
|
||||||
|
config: TestSuiteConfig,
|
||||||
|
) {
|
||||||
|
if (config.cleanDatabase) {
|
||||||
|
await config.cleanDatabase(connection);
|
||||||
|
} else {
|
||||||
|
await defaultCleanDatabase(connection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function testSuite(config: TestSuiteConfig) {
|
||||||
|
beforeAll(() => disableConsoleLog(["log", "warn", "error"]));
|
||||||
|
afterAll(() => enableConsoleLog());
|
||||||
|
|
||||||
|
describe("base", () => {
|
||||||
|
it("should connect to the database", async () => {
|
||||||
|
const connection = config.createConnection();
|
||||||
|
expect(await connection.ping()).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should clean the database", async () => {
|
||||||
|
const connection = config.createConnection();
|
||||||
|
await cleanDatabase(connection, config);
|
||||||
|
|
||||||
|
const tables = await connection.getIntrospector().getTables();
|
||||||
|
expect(tables).toEqual([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("integration", () => {
|
||||||
|
let connection: PostgresConnection;
|
||||||
|
beforeAll(async () => {
|
||||||
|
connection = config.createConnection();
|
||||||
|
await cleanDatabase(connection, config);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
await cleanDatabase(connection, config);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await connection.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should create app and ping", async () => {
|
||||||
|
const app = createApp({
|
||||||
|
connection,
|
||||||
|
});
|
||||||
|
await app.build();
|
||||||
|
|
||||||
|
expect(app.version()).toBeDefined();
|
||||||
|
expect(await app.em.ping()).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should create a basic schema", async () => {
|
||||||
|
const schema = proto.em(
|
||||||
|
{
|
||||||
|
posts: proto.entity("posts", {
|
||||||
|
title: proto.text().required(),
|
||||||
|
content: proto.text(),
|
||||||
|
}),
|
||||||
|
comments: proto.entity("comments", {
|
||||||
|
content: proto.text(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
(fns, s) => {
|
||||||
|
fns.relation(s.comments).manyToOne(s.posts);
|
||||||
|
fns.index(s.posts).on(["title"], true);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const app = createApp({
|
||||||
|
connection,
|
||||||
|
initialConfig: {
|
||||||
|
data: schema.toJSON(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await app.build();
|
||||||
|
|
||||||
|
expect(app.em.entities.length).toBe(2);
|
||||||
|
expect(app.em.entities.map((e) => e.name)).toEqual(["posts", "comments"]);
|
||||||
|
|
||||||
|
const api = app.getApi();
|
||||||
|
|
||||||
|
expect(
|
||||||
|
(
|
||||||
|
await api.data.createMany("posts", [
|
||||||
|
{
|
||||||
|
title: "Hello",
|
||||||
|
content: "World",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Hello 2",
|
||||||
|
content: "World 2",
|
||||||
|
},
|
||||||
|
])
|
||||||
|
).data,
|
||||||
|
).toEqual([
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
title: "Hello",
|
||||||
|
content: "World",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
title: "Hello 2",
|
||||||
|
content: "World 2",
|
||||||
|
},
|
||||||
|
] as any);
|
||||||
|
|
||||||
|
// try to create an existing
|
||||||
|
expect(
|
||||||
|
(
|
||||||
|
await api.data.createOne("posts", {
|
||||||
|
title: "Hello",
|
||||||
|
})
|
||||||
|
).ok,
|
||||||
|
).toBe(false);
|
||||||
|
|
||||||
|
// add a comment to a post
|
||||||
|
await api.data.createOne("comments", {
|
||||||
|
content: "Hello",
|
||||||
|
posts_id: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
// and then query using a `with` property
|
||||||
|
const result = await api.data.readMany("posts", { with: ["comments"] });
|
||||||
|
expect(result.length).toBe(2);
|
||||||
|
expect(result[0].comments.length).toBe(1);
|
||||||
|
expect(result[0].comments[0].content).toBe("Hello");
|
||||||
|
expect(result[1].comments.length).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should support uuid", async () => {
|
||||||
|
const schema = proto.em(
|
||||||
|
{
|
||||||
|
posts: proto.entity(
|
||||||
|
"posts",
|
||||||
|
{
|
||||||
|
title: proto.text().required(),
|
||||||
|
content: proto.text(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
primary_format: "uuid",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
comments: proto.entity("comments", {
|
||||||
|
content: proto.text(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
(fns, s) => {
|
||||||
|
fns.relation(s.comments).manyToOne(s.posts);
|
||||||
|
fns.index(s.posts).on(["title"], true);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const app = createApp({
|
||||||
|
connection,
|
||||||
|
initialConfig: {
|
||||||
|
data: schema.toJSON(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await app.build();
|
||||||
|
const config = app.toJSON();
|
||||||
|
// @ts-expect-error
|
||||||
|
expect(config.data.entities?.posts.fields?.id.config?.format).toBe("uuid");
|
||||||
|
|
||||||
|
const em = app.em;
|
||||||
|
const mutator = em.mutator(em.entity("posts"));
|
||||||
|
const data = await mutator.insertOne({ title: "Hello", content: "World" });
|
||||||
|
expect(data.data.id).toBeString();
|
||||||
|
expect(String(data.data.id).length).toBe(36);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user