mirror of
https://github.com/bknd-io/bknd/
synced 2026-08-02 16:16:02 +00:00
Compare commits
14 Commits
feat/init-e2e
...
v0.11.2
| Author | SHA1 | Date | |
|---|---|---|---|
| 07723ce6ae | |||
| 9010401af6 | |||
| 4c11789ea8 | |||
| 2988e4c3bd | |||
| ad7926db4c | |||
| 53a3dcdee7 | |||
| 53467d6750 | |||
| a80a731498 | |||
| 7e1757b7f4 | |||
| 2c29e06fb8 | |||
| ca86fa58ac | |||
| de984fa101 | |||
| a12d4e13d0 | |||
| fa6c7acaf5 |
+3
-2
@@ -1,3 +1,4 @@
|
||||
test-results
|
||||
playwright-report
|
||||
bknd.config.*
|
||||
test-results
|
||||
bknd.config.*
|
||||
__test__/helper.d.ts
|
||||
@@ -1,13 +1,51 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { beforeEach, describe, expect, test } from "bun:test";
|
||||
import { parse } from "../../src/core/utils";
|
||||
import { fieldsSchema } from "../../src/data/data-schema";
|
||||
import { AppData } from "../../src/modules";
|
||||
import { moduleTestSuite } from "./module-test-suite";
|
||||
import { AppData, type ModuleBuildContext } from "../../src/modules";
|
||||
import { makeCtx, moduleTestSuite } from "./module-test-suite";
|
||||
import * as proto from "data/prototype";
|
||||
|
||||
describe("AppData", () => {
|
||||
moduleTestSuite(AppData);
|
||||
|
||||
let ctx: ModuleBuildContext;
|
||||
beforeEach(() => {
|
||||
ctx = makeCtx();
|
||||
});
|
||||
|
||||
test("field config construction", () => {
|
||||
expect(parse(fieldsSchema, { type: "text" })).toBeDefined();
|
||||
});
|
||||
|
||||
test("should prevent multi-deletion of entities in single request", async () => {
|
||||
const schema = proto.em({
|
||||
one: proto.entity("one", {
|
||||
text: proto.text(),
|
||||
}),
|
||||
two: proto.entity("two", {
|
||||
text: proto.text(),
|
||||
}),
|
||||
three: proto.entity("three", {
|
||||
text: proto.text(),
|
||||
}),
|
||||
});
|
||||
const check = () => {
|
||||
const expected = ["one", "two", "three"];
|
||||
const fromConfig = Object.keys(data.config.entities ?? {});
|
||||
const fromEm = data.em.entities.map((e) => e.name);
|
||||
expect(fromConfig).toEqual(expected);
|
||||
expect(fromEm).toEqual(expected);
|
||||
};
|
||||
|
||||
// auth must be enabled, otherwise default config is returned
|
||||
const data = new AppData(schema.toJSON(), ctx);
|
||||
await data.build();
|
||||
check();
|
||||
|
||||
expect(data.schema().remove("entities")).rejects.toThrow(/more than one entity/);
|
||||
check();
|
||||
|
||||
await data.setContext(makeCtx()).build();
|
||||
check();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
|
||||
import { Type, disableConsoleLog, enableConsoleLog, stripMark } from "../../src/core/utils";
|
||||
import { entity, text } from "../../src/data";
|
||||
import { disableConsoleLog, enableConsoleLog, stripMark, Type } from "../../src/core/utils";
|
||||
import { Connection, entity, text } from "../../src/data";
|
||||
import { Module } from "../../src/modules/Module";
|
||||
import { ModuleManager, getDefaultConfig } from "../../src/modules/ModuleManager";
|
||||
import { type ConfigTable, getDefaultConfig, ModuleManager } from "../../src/modules/ModuleManager";
|
||||
import { CURRENT_VERSION, TABLE_NAME } from "../../src/modules/migrations";
|
||||
import { getDummyConnection } from "../helper";
|
||||
import { diff } from "core/object/diff";
|
||||
import type { Static } from "@sinclair/typebox";
|
||||
|
||||
describe("ModuleManager", async () => {
|
||||
test("s1: no config, no build", async () => {
|
||||
@@ -380,4 +382,128 @@ describe("ModuleManager", async () => {
|
||||
expect(() => f.default()).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
async function getRawConfig(c: Connection) {
|
||||
return (await c.kysely
|
||||
.selectFrom(TABLE_NAME)
|
||||
.selectAll()
|
||||
.where("type", "=", "config")
|
||||
.orderBy("version", "desc")
|
||||
.executeTakeFirstOrThrow()) as unknown as ConfigTable;
|
||||
}
|
||||
|
||||
async function getDiffs(c: Connection, opts?: { dir?: "asc" | "desc"; limit?: number }) {
|
||||
return await c.kysely
|
||||
.selectFrom(TABLE_NAME)
|
||||
.selectAll()
|
||||
.where("type", "=", "diff")
|
||||
.orderBy("version", opts?.dir ?? "desc")
|
||||
.$if(!!opts?.limit, (b) => b.limit(opts!.limit!))
|
||||
.execute();
|
||||
}
|
||||
|
||||
describe("diffs", () => {
|
||||
test("never empty", async () => {
|
||||
const { dummyConnection: c } = getDummyConnection();
|
||||
const mm = new ModuleManager(c);
|
||||
await mm.build();
|
||||
await mm.save();
|
||||
expect(await getDiffs(c)).toHaveLength(0);
|
||||
});
|
||||
|
||||
test("has timestamps", async () => {
|
||||
const { dummyConnection: c } = getDummyConnection();
|
||||
const mm = new ModuleManager(c);
|
||||
await mm.build();
|
||||
|
||||
await mm.get("data").schema().patch("basepath", "/api/data2");
|
||||
await mm.save();
|
||||
|
||||
const config = await getRawConfig(c);
|
||||
const diffs = await getDiffs(c);
|
||||
|
||||
expect(config.json.data.basepath).toBe("/api/data2");
|
||||
expect(diffs).toHaveLength(1);
|
||||
expect(diffs[0]!.created_at).toBeDefined();
|
||||
expect(diffs[0]!.updated_at).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("validate & revert", () => {
|
||||
const schema = Type.Object({
|
||||
value: Type.Array(Type.Number(), { default: [] }),
|
||||
});
|
||||
type SampleSchema = Static<typeof schema>;
|
||||
class Sample extends Module<typeof schema> {
|
||||
getSchema() {
|
||||
return schema;
|
||||
}
|
||||
override async build() {
|
||||
this.setBuilt();
|
||||
}
|
||||
override async onBeforeUpdate(from: SampleSchema, to: SampleSchema) {
|
||||
if (to.value.length > 3) {
|
||||
throw new Error("too many values");
|
||||
}
|
||||
if (to.value.includes(7)) {
|
||||
throw new Error("contains 7");
|
||||
}
|
||||
|
||||
return to;
|
||||
}
|
||||
}
|
||||
class TestModuleManager extends ModuleManager {
|
||||
constructor(...args: ConstructorParameters<typeof ModuleManager>) {
|
||||
super(...args);
|
||||
this.modules["module1"] = new Sample({}, this.ctx());
|
||||
}
|
||||
}
|
||||
test("respects module onBeforeUpdate", async () => {
|
||||
const { dummyConnection: c } = getDummyConnection();
|
||||
const mm = new TestModuleManager(c);
|
||||
await mm.build();
|
||||
|
||||
const m = mm.get("module1" as any) as Sample;
|
||||
|
||||
{
|
||||
expect(async () => {
|
||||
await m.schema().set({ value: [1, 2, 3, 4, 5] });
|
||||
return mm.save();
|
||||
}).toThrow(/too many values/);
|
||||
|
||||
expect(m.config.value).toHaveLength(0);
|
||||
expect((mm.configs() as any).module1.value).toHaveLength(0);
|
||||
}
|
||||
|
||||
{
|
||||
expect(async () => {
|
||||
await mm.mutateConfigSafe("module1" as any).set({ value: [1, 2, 3, 4, 5] });
|
||||
return mm.save();
|
||||
}).toThrow(/too many values/);
|
||||
|
||||
expect(m.config.value).toHaveLength(0);
|
||||
expect((mm.configs() as any).module1.value).toHaveLength(0);
|
||||
}
|
||||
|
||||
{
|
||||
expect(async () => {
|
||||
await m.schema().set({ value: [1, 7, 5] });
|
||||
return mm.save();
|
||||
}).toThrow(/contains 7/);
|
||||
|
||||
expect(m.config.value).toHaveLength(0);
|
||||
expect((mm.configs() as any).module1.value).toHaveLength(0);
|
||||
}
|
||||
|
||||
{
|
||||
expect(async () => {
|
||||
await mm.mutateConfigSafe("module1" as any).set({ value: [1, 7, 5] });
|
||||
return mm.save();
|
||||
}).toThrow(/contains 7/);
|
||||
|
||||
expect(m.config.value).toHaveLength(0);
|
||||
expect((mm.configs() as any).module1.value).toHaveLength(0);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
|
||||
describe("Example Test Suite", () => {
|
||||
it("should pass basic arithmetic", () => {
|
||||
expect(1 + 1).toBe(2);
|
||||
});
|
||||
|
||||
it("should handle async operations", async () => {
|
||||
const result = await Promise.resolve(42);
|
||||
expect(result).toBe(42);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,8 @@
|
||||
import "@testing-library/jest-dom";
|
||||
import { afterEach } from "vitest";
|
||||
import { cleanup } from "@testing-library/react";
|
||||
|
||||
// Automatically cleanup after each test
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
});
|
||||
@@ -0,0 +1,214 @@
|
||||
import { $ } from "bun";
|
||||
import path from "node:path";
|
||||
import c from "picocolors";
|
||||
|
||||
const basePath = new URL(import.meta.resolve("../../")).pathname.slice(0, -1);
|
||||
|
||||
async function run(
|
||||
cmd: string[] | string,
|
||||
opts: Bun.SpawnOptions.OptionsObject & {},
|
||||
onChunk: (chunk: string, resolve: (data: any) => void, reject: (err: Error) => void) => void,
|
||||
): Promise<{ proc: Bun.Subprocess; data: any }> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const proc = Bun.spawn(Array.isArray(cmd) ? cmd : cmd.split(" "), {
|
||||
...opts,
|
||||
stdout: "pipe",
|
||||
stderr: "pipe",
|
||||
});
|
||||
|
||||
// Read from stdout
|
||||
const reader = proc.stdout.getReader();
|
||||
const decoder = new TextDecoder();
|
||||
|
||||
// Function to read chunks
|
||||
let resolveCalled = false;
|
||||
(async () => {
|
||||
try {
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
|
||||
const text = decoder.decode(value);
|
||||
if (!resolveCalled) {
|
||||
console.log(c.dim(text.replace(/\n$/, "")));
|
||||
}
|
||||
onChunk(
|
||||
text,
|
||||
(data) => {
|
||||
resolve({ proc, data });
|
||||
resolveCalled = true;
|
||||
},
|
||||
reject,
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
})();
|
||||
|
||||
proc.exited.then((code) => {
|
||||
if (code !== 0 && code !== 130) {
|
||||
throw new Error(`Process exited with code ${code}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const adapters = {
|
||||
node: {
|
||||
dir: path.join(basePath, "examples/node"),
|
||||
clean: async function () {
|
||||
const cwd = path.relative(process.cwd(), this.dir);
|
||||
await $`cd ${cwd} && rm -rf uploads data.db && mkdir -p uploads`;
|
||||
},
|
||||
start: async function () {
|
||||
return await run(
|
||||
"npm run start",
|
||||
{
|
||||
cwd: this.dir,
|
||||
},
|
||||
(chunk, resolve, reject) => {
|
||||
const regex = /running on (http:\/\/.*)\n/;
|
||||
if (regex.test(chunk)) {
|
||||
resolve(chunk.match(regex)?.[1]);
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
},
|
||||
bun: {
|
||||
dir: path.join(basePath, "examples/bun"),
|
||||
clean: async function () {
|
||||
const cwd = path.relative(process.cwd(), this.dir);
|
||||
await $`cd ${cwd} && rm -rf uploads data.db && mkdir -p uploads`;
|
||||
},
|
||||
start: async function () {
|
||||
return await run(
|
||||
"npm run start",
|
||||
{
|
||||
cwd: this.dir,
|
||||
},
|
||||
(chunk, resolve, reject) => {
|
||||
const regex = /running on (http:\/\/.*)\n/;
|
||||
if (regex.test(chunk)) {
|
||||
resolve(chunk.match(regex)?.[1]);
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
},
|
||||
cloudflare: {
|
||||
dir: path.join(basePath, "examples/cloudflare-worker"),
|
||||
clean: async function () {
|
||||
const cwd = path.relative(process.cwd(), this.dir);
|
||||
await $`cd ${cwd} && rm -rf .wrangler node_modules/.cache node_modules/.mf`;
|
||||
},
|
||||
start: async function () {
|
||||
return await run(
|
||||
"npm run dev",
|
||||
{
|
||||
cwd: this.dir,
|
||||
},
|
||||
(chunk, resolve, reject) => {
|
||||
const regex = /Ready on (http:\/\/.*)/;
|
||||
if (regex.test(chunk)) {
|
||||
resolve(chunk.match(regex)?.[1]);
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
},
|
||||
"react-router": {
|
||||
dir: path.join(basePath, "examples/react-router"),
|
||||
clean: async function () {
|
||||
const cwd = path.relative(process.cwd(), this.dir);
|
||||
await $`cd ${cwd} && rm -rf .react-router data.db`;
|
||||
await $`cd ${cwd} && rm -rf public/uploads && mkdir -p public/uploads`;
|
||||
},
|
||||
start: async function () {
|
||||
return await run(
|
||||
"npm run dev",
|
||||
{
|
||||
cwd: this.dir,
|
||||
},
|
||||
(chunk, resolve, reject) => {
|
||||
const regex = /Local.*?(http:\/\/.*)\//;
|
||||
if (regex.test(chunk)) {
|
||||
resolve(chunk.match(regex)?.[1]);
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
},
|
||||
nextjs: {
|
||||
dir: path.join(basePath, "examples/nextjs"),
|
||||
clean: async function () {
|
||||
const cwd = path.relative(process.cwd(), this.dir);
|
||||
await $`cd ${cwd} && rm -rf .nextjs data.db`;
|
||||
await $`cd ${cwd} && rm -rf public/uploads && mkdir -p public/uploads`;
|
||||
},
|
||||
start: async function () {
|
||||
return await run(
|
||||
"npm run dev",
|
||||
{
|
||||
cwd: this.dir,
|
||||
},
|
||||
(chunk, resolve, reject) => {
|
||||
const regex = /Local.*?(http:\/\/.*)\n/;
|
||||
if (regex.test(chunk)) {
|
||||
resolve(chunk.match(regex)?.[1]);
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
},
|
||||
astro: {
|
||||
dir: path.join(basePath, "examples/astro"),
|
||||
clean: async function () {
|
||||
const cwd = path.relative(process.cwd(), this.dir);
|
||||
await $`cd ${cwd} && rm -rf .astro data.db`;
|
||||
await $`cd ${cwd} && rm -rf public/uploads && mkdir -p public/uploads`;
|
||||
},
|
||||
start: async function () {
|
||||
return await run(
|
||||
"npm run dev",
|
||||
{
|
||||
cwd: this.dir,
|
||||
},
|
||||
(chunk, resolve, reject) => {
|
||||
const regex = /Local.*?(http:\/\/.*)\//;
|
||||
if (regex.test(chunk)) {
|
||||
resolve(chunk.match(regex)?.[1]);
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
||||
async function testAdapter(name: keyof typeof adapters) {
|
||||
const config = adapters[name];
|
||||
console.log("adapter", c.cyan(name));
|
||||
await config.clean();
|
||||
|
||||
const { proc, data } = await config.start();
|
||||
console.log("proc:", proc.pid, "data:", c.cyan(data));
|
||||
//proc.kill();process.exit(0);
|
||||
|
||||
await $`TEST_URL=${data} TEST_ADAPTER=${name} bun run test:e2e`;
|
||||
console.log("DONE!");
|
||||
|
||||
while (!proc.killed) {
|
||||
proc.kill("SIGINT");
|
||||
await Bun.sleep(250);
|
||||
console.log("Waiting for process to exit...");
|
||||
}
|
||||
}
|
||||
|
||||
if (process.env.TEST_ADAPTER) {
|
||||
await testAdapter(process.env.TEST_ADAPTER as any);
|
||||
} else {
|
||||
for (const [name] of Object.entries(adapters)) {
|
||||
await testAdapter(name as any);
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 265 KiB |
@@ -0,0 +1,25 @@
|
||||
// @ts-check
|
||||
import { test, expect } from "@playwright/test";
|
||||
import { testIds } from "../src/ui/lib/config";
|
||||
|
||||
import { getAdapterConfig } from "./inc/adapters";
|
||||
const config = getAdapterConfig();
|
||||
|
||||
test("start page has expected title", async ({ page }) => {
|
||||
await page.goto(config.base_path);
|
||||
await expect(page).toHaveTitle(/BKND/);
|
||||
});
|
||||
|
||||
test("start page has expected heading", async ({ page }) => {
|
||||
await page.goto(config.base_path);
|
||||
|
||||
// Example of checking if a heading with "No entity selected" exists and is visible
|
||||
const heading = page.getByRole("heading", { name: /No entity selected/i });
|
||||
await expect(heading).toBeVisible();
|
||||
});
|
||||
|
||||
test("modal opens on button click", async ({ page }) => {
|
||||
await page.goto(config.base_path);
|
||||
await page.getByTestId(testIds.data.btnCreateEntity).click();
|
||||
await expect(page.getByRole("dialog")).toBeVisible();
|
||||
});
|
||||
@@ -0,0 +1,44 @@
|
||||
const adapter = process.env.TEST_ADAPTER;
|
||||
|
||||
const default_config = {
|
||||
media_adapter: "local",
|
||||
base_path: "",
|
||||
} as const;
|
||||
|
||||
const configs = {
|
||||
cloudflare: {
|
||||
media_adapter: "r2",
|
||||
},
|
||||
"react-router": {
|
||||
base_path: "/admin",
|
||||
},
|
||||
nextjs: {
|
||||
base_path: "/admin",
|
||||
},
|
||||
astro: {
|
||||
base_path: "/admin",
|
||||
},
|
||||
node: {
|
||||
base_path: "",
|
||||
},
|
||||
bun: {
|
||||
base_path: "",
|
||||
},
|
||||
};
|
||||
|
||||
export function getAdapterConfig(): typeof default_config {
|
||||
if (adapter) {
|
||||
if (!configs[adapter]) {
|
||||
console.warn(
|
||||
`Adapter "${adapter}" not found. Available adapters: ${Object.keys(configs).join(", ")}`,
|
||||
);
|
||||
} else {
|
||||
return {
|
||||
...default_config,
|
||||
...configs[adapter],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return default_config;
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
// @ts-check
|
||||
import { test, expect } from "@playwright/test";
|
||||
import { testIds } from "../src/ui/lib/config";
|
||||
import type { SchemaResponse } from "../src/modules/server/SystemController";
|
||||
import { getAdapterConfig } from "./inc/adapters";
|
||||
|
||||
// Annotate entire file as serial.
|
||||
test.describe.configure({ mode: "serial" });
|
||||
|
||||
const config = getAdapterConfig();
|
||||
|
||||
test("can enable media", async ({ page }) => {
|
||||
await page.goto(`${config.base_path}/media/settings`);
|
||||
|
||||
// enable
|
||||
const enableToggle = page.getByTestId(testIds.media.switchEnabled);
|
||||
if ((await enableToggle.getAttribute("aria-checked")) !== "true") {
|
||||
await expect(enableToggle).toBeVisible();
|
||||
await enableToggle.click();
|
||||
await expect(enableToggle).toHaveAttribute("aria-checked", "true");
|
||||
|
||||
// select local
|
||||
const adapterChoice = page.locator(`css=button#adapter-${config.media_adapter}`);
|
||||
await expect(adapterChoice).toBeVisible();
|
||||
await adapterChoice.click();
|
||||
|
||||
// save
|
||||
const saveBtn = page.getByRole("button", { name: /Update/i });
|
||||
await expect(saveBtn).toBeVisible();
|
||||
|
||||
// intercept network request, wait for it to finish and get the response
|
||||
const [request] = await Promise.all([
|
||||
page.waitForRequest((request) => request.url().includes("api/system/schema")),
|
||||
saveBtn.click(),
|
||||
]);
|
||||
const response = await request.response();
|
||||
expect(response?.status(), "fresh config 200").toBe(200);
|
||||
const body = (await response?.json()) as SchemaResponse;
|
||||
expect(body.config.media.enabled, "media is enabled").toBe(true);
|
||||
expect(body.config.media.adapter?.type, "correct adapter").toBe(config.media_adapter);
|
||||
}
|
||||
});
|
||||
|
||||
test("can upload a file", async ({ page }) => {
|
||||
await page.goto(`${config.base_path}/media`);
|
||||
// check any text to contain "Upload files"
|
||||
await expect(page.getByText(/Upload files/i)).toBeVisible();
|
||||
|
||||
// upload a file from disk
|
||||
// Start waiting for file chooser before clicking. Note no await.
|
||||
const fileChooserPromise = page.waitForEvent("filechooser");
|
||||
await page.getByText("Upload file").click();
|
||||
const fileChooser = await fileChooserPromise;
|
||||
await fileChooser.setFiles("./e2e/assets/image.jpg");
|
||||
});
|
||||
+24
-10
@@ -3,7 +3,7 @@
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
"bin": "./dist/cli/index.js",
|
||||
"version": "0.11.0-rc.2",
|
||||
"version": "0.11.2",
|
||||
"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",
|
||||
"repository": {
|
||||
@@ -15,12 +15,6 @@
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"test": "ALL_TESTS=1 bun test --bail",
|
||||
"test:all": "bun run test && bun run test:node",
|
||||
"test:bun": "ALL_TESTS=1 bun test --bail",
|
||||
"test:node": "tsx --test $(find . -type f -name '*.native-spec.ts')",
|
||||
"test:adapters": "bun test src/adapter/**/*.adapter.spec.ts --bail",
|
||||
"test:coverage": "ALL_TESTS=1 bun test --bail --coverage",
|
||||
"build": "NODE_ENV=production bun run build.ts --minify --types",
|
||||
"build:all": "rm -rf dist && bun run build:static && NODE_ENV=production bun run build.ts --minify --types --clean && bun run build:cli",
|
||||
"build:ci": "mkdir -p dist/static/.vite && echo '{}' > dist/static/.vite/manifest.json && NODE_ENV=production bun run build.ts",
|
||||
@@ -32,8 +26,22 @@
|
||||
"build:types": "tsc -p tsconfig.build.json --emitDeclarationOnly && tsc-alias",
|
||||
"updater": "bun x npm-check-updates -ui",
|
||||
"cli": "LOCAL=1 bun src/cli/index.ts",
|
||||
"prepublishOnly": "bun run types && bun run test && bun run test:node && bun run build:all && cp ../README.md ./",
|
||||
"postpublish": "rm -f README.md"
|
||||
"prepublishOnly": "bun run types && bun run test && bun run test:node && bun run test:e2e && bun run build:all && cp ../README.md ./",
|
||||
"postpublish": "rm -f README.md",
|
||||
"test": "ALL_TESTS=1 bun test --bail",
|
||||
"test:all": "bun run test && bun run test:node",
|
||||
"test:bun": "ALL_TESTS=1 bun test --bail",
|
||||
"test:node": "tsx --test $(find . -type f -name '*.native-spec.ts')",
|
||||
"test:adapters": "bun test src/adapter/**/*.adapter.spec.ts --bail",
|
||||
"test:coverage": "ALL_TESTS=1 bun test --bail --coverage",
|
||||
"test:vitest": "vitest run",
|
||||
"test:vitest:watch": "vitest",
|
||||
"test:vitest:coverage": "vitest run --coverage",
|
||||
"test:e2e": "playwright test",
|
||||
"test:e2e:adapters": "bun run e2e/adapters.ts",
|
||||
"test:e2e:ui": "playwright test --ui",
|
||||
"test:e2e:debug": "playwright test --debug",
|
||||
"test:e2e:report": "playwright show-report"
|
||||
},
|
||||
"license": "FSL-1.1-MIT",
|
||||
"dependencies": {
|
||||
@@ -54,7 +62,7 @@
|
||||
"fast-xml-parser": "^5.0.8",
|
||||
"hono": "^4.7.4",
|
||||
"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",
|
||||
"kysely": "^0.27.6",
|
||||
"liquidjs": "^10.21.0",
|
||||
@@ -76,18 +84,23 @@
|
||||
"@libsql/kysely-libsql": "^0.4.1",
|
||||
"@mantine/modals": "^7.17.1",
|
||||
"@mantine/notifications": "^7.17.1",
|
||||
"@playwright/test": "^1.51.1",
|
||||
"@rjsf/core": "5.22.2",
|
||||
"@tabler/icons-react": "3.18.0",
|
||||
"@tailwindcss/postcss": "^4.0.12",
|
||||
"@tailwindcss/vite": "^4.0.12",
|
||||
"@testing-library/jest-dom": "^6.6.3",
|
||||
"@testing-library/react": "^16.2.0",
|
||||
"@types/node": "^22.13.10",
|
||||
"@types/react": "^19.0.10",
|
||||
"@types/react-dom": "^19.0.4",
|
||||
"@vitejs/plugin-react": "^4.3.4",
|
||||
"@vitest/coverage-v8": "^3.0.9",
|
||||
"autoprefixer": "^10.4.21",
|
||||
"clsx": "^2.1.1",
|
||||
"dotenv": "^16.4.7",
|
||||
"jotai": "^2.12.2",
|
||||
"jsdom": "^26.0.0",
|
||||
"kysely-d1": "^0.3.0",
|
||||
"open": "^10.1.0",
|
||||
"openapi-types": "^12.1.3",
|
||||
@@ -109,6 +122,7 @@
|
||||
"tsx": "^4.19.3",
|
||||
"vite": "^6.2.1",
|
||||
"vite-tsconfig-paths": "^5.1.4",
|
||||
"vitest": "^3.0.9",
|
||||
"wouter": "^3.6.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
import { defineConfig, devices } from "@playwright/test";
|
||||
|
||||
const baseUrl = process.env.TEST_URL || "http://localhost:28623";
|
||||
const startCommand = process.env.TEST_START_COMMAND || "bun run dev";
|
||||
const autoStart = ["1", "true", undefined].includes(process.env.TEST_AUTO_START);
|
||||
|
||||
export default defineConfig({
|
||||
testMatch: "**/*.e2e-spec.ts",
|
||||
testDir: "./e2e",
|
||||
fullyParallel: true,
|
||||
forbidOnly: !!process.env.CI,
|
||||
retries: process.env.CI ? 2 : 0,
|
||||
workers: process.env.CI ? 1 : undefined,
|
||||
reporter: "html",
|
||||
timeout: 20000,
|
||||
use: {
|
||||
baseURL: baseUrl,
|
||||
trace: "on-first-retry",
|
||||
video: "on-first-retry",
|
||||
},
|
||||
projects: [
|
||||
{
|
||||
name: "chromium",
|
||||
use: { ...devices["Desktop Chrome"] },
|
||||
},
|
||||
/* {
|
||||
name: "firefox",
|
||||
use: { ...devices["Desktop Firefox"] },
|
||||
},
|
||||
{
|
||||
name: "webkit",
|
||||
use: { ...devices["Desktop Safari"] },
|
||||
}, */
|
||||
],
|
||||
webServer: autoStart
|
||||
? {
|
||||
command: startCommand,
|
||||
url: baseUrl,
|
||||
reuseExistingServer: !process.env.CI,
|
||||
}
|
||||
: undefined,
|
||||
});
|
||||
+4
-1
@@ -180,7 +180,10 @@ export class App {
|
||||
registerAdminController(config?: AdminControllerOptions) {
|
||||
// register admin
|
||||
this.adminController = new AdminController(this, config);
|
||||
this.modules.server.route(config?.basepath ?? "/", this.adminController.getController());
|
||||
this.modules.server.route(
|
||||
this.adminController.basepath,
|
||||
this.adminController.getController(),
|
||||
);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ export async function createApp<Env extends AwsLambdaEnv = AwsLambdaEnv>(
|
||||
case "url":
|
||||
additional.adminOptions = {
|
||||
...(typeof adminOptions === "object" ? adminOptions : {}),
|
||||
assets_path: assets.url,
|
||||
assetsPath: assets.url,
|
||||
};
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
/// <reference types="@cloudflare/workers-types" />
|
||||
|
||||
import type { FrameworkBkndConfig } from "bknd/adapter";
|
||||
import type { RuntimeBkndConfig } from "bknd/adapter";
|
||||
import { Hono } from "hono";
|
||||
import { serveStatic } from "hono/cloudflare-workers";
|
||||
import { getFresh } from "./modes/fresh";
|
||||
import { getCached } from "./modes/cached";
|
||||
import { getDurable } from "./modes/durable";
|
||||
import { getFresh, getWarm } from "./modes/fresh";
|
||||
import type { App } from "bknd";
|
||||
|
||||
export type CloudflareEnv = object;
|
||||
export type CloudflareBkndConfig<Env = CloudflareEnv> = FrameworkBkndConfig<Env> & {
|
||||
export type CloudflareBkndConfig<Env = CloudflareEnv> = RuntimeBkndConfig<Env> & {
|
||||
mode?: "warm" | "fresh" | "cache" | "durable";
|
||||
bindings?: (args: Env) => {
|
||||
kv?: KVNamespace;
|
||||
@@ -20,8 +21,6 @@ export type CloudflareBkndConfig<Env = CloudflareEnv> = FrameworkBkndConfig<Env>
|
||||
keepAliveSeconds?: number;
|
||||
forceHttps?: boolean;
|
||||
manifest?: string;
|
||||
setAdminHtml?: boolean;
|
||||
html?: string;
|
||||
};
|
||||
|
||||
export type Context<Env = CloudflareEnv> = {
|
||||
@@ -43,7 +42,7 @@ export function serve<Env extends CloudflareEnv = CloudflareEnv>(
|
||||
throw new Error("manifest is required with static 'kv'");
|
||||
}
|
||||
|
||||
if (config.manifest && config.static !== "assets") {
|
||||
if (config.manifest && config.static === "kv") {
|
||||
const pathname = url.pathname.slice(1);
|
||||
const assetManifest = JSON.parse(config.manifest);
|
||||
if (pathname && pathname in assetManifest) {
|
||||
@@ -70,18 +69,24 @@ export function serve<Env extends CloudflareEnv = CloudflareEnv>(
|
||||
const context = { request, env, ctx } as Context<Env>;
|
||||
const mode = config.mode ?? "warm";
|
||||
|
||||
let app: App;
|
||||
switch (mode) {
|
||||
case "fresh":
|
||||
return await getFresh(config, context);
|
||||
app = await getFresh(config, context, { force: true });
|
||||
break;
|
||||
case "warm":
|
||||
return await getWarm(config, context);
|
||||
app = await getFresh(config, context);
|
||||
break;
|
||||
case "cache":
|
||||
return await getCached(config, context);
|
||||
app = await getCached(config, context);
|
||||
break;
|
||||
case "durable":
|
||||
return await getDurable(config, context);
|
||||
default:
|
||||
throw new Error(`Unknown mode ${mode}`);
|
||||
}
|
||||
|
||||
return app.fetch(request, env, ctx);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { D1Connection, type D1ConnectionConfig } from "./D1Connection";
|
||||
|
||||
export * from "./cloudflare-workers.adapter";
|
||||
export { makeApp, getFresh, getWarm } from "./modes/fresh";
|
||||
export { makeApp, getFresh } from "./modes/fresh";
|
||||
export { getCached } from "./modes/cached";
|
||||
export { DurableBkndApp, getDurable } from "./modes/durable";
|
||||
export { D1Connection, type D1ConnectionConfig };
|
||||
|
||||
@@ -40,7 +40,6 @@ export async function getCached<Env extends CloudflareEnv = CloudflareEnv>(
|
||||
);
|
||||
await config.beforeBuild?.(app);
|
||||
},
|
||||
adminOptions: { html: config.html },
|
||||
},
|
||||
{ env, ctx, ...args },
|
||||
);
|
||||
|
||||
@@ -25,9 +25,7 @@ export async function getDurable<Env extends CloudflareEnv = CloudflareEnv>(
|
||||
|
||||
const res = await stub.fire(ctx.request, {
|
||||
config: create_config,
|
||||
html: config.html,
|
||||
keepAliveSeconds: config.keepAliveSeconds,
|
||||
setAdminHtml: config.setAdminHtml,
|
||||
});
|
||||
|
||||
const headers = new Headers(res.headers);
|
||||
@@ -110,6 +108,7 @@ export class DurableBkndApp extends DurableObject {
|
||||
}
|
||||
|
||||
async onBuilt(app: App) {}
|
||||
|
||||
async beforeBuild(app: App) {}
|
||||
|
||||
protected keepAlive(seconds: number) {
|
||||
|
||||
@@ -7,22 +7,15 @@ export async function makeApp<Env extends CloudflareEnv = CloudflareEnv>(
|
||||
args: Env = {} as Env,
|
||||
opts?: RuntimeOptions,
|
||||
) {
|
||||
return await createRuntimeApp<Env>(
|
||||
{
|
||||
...makeConfig(config, args),
|
||||
adminOptions: config.html ? { html: config.html } : undefined,
|
||||
},
|
||||
args,
|
||||
opts,
|
||||
);
|
||||
return await createRuntimeApp<Env>(makeConfig(config, args), args, opts);
|
||||
}
|
||||
|
||||
export async function getWarm<Env extends CloudflareEnv = CloudflareEnv>(
|
||||
export async function getFresh<Env extends CloudflareEnv = CloudflareEnv>(
|
||||
config: CloudflareBkndConfig<Env>,
|
||||
ctx: Context<Env>,
|
||||
opts: RuntimeOptions = {},
|
||||
) {
|
||||
const app = await makeApp(
|
||||
return await makeApp(
|
||||
{
|
||||
...config,
|
||||
onBuilt: async (app) => {
|
||||
@@ -33,16 +26,4 @@ export async function getWarm<Env extends CloudflareEnv = CloudflareEnv>(
|
||||
ctx.env,
|
||||
opts,
|
||||
);
|
||||
return app.fetch(ctx.request);
|
||||
}
|
||||
|
||||
export async function getFresh<Env extends CloudflareEnv = CloudflareEnv>(
|
||||
config: CloudflareBkndConfig<Env>,
|
||||
ctx: Context<Env>,
|
||||
opts: RuntimeOptions = {},
|
||||
) {
|
||||
return await getWarm(config, ctx, {
|
||||
...opts,
|
||||
force: true,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,18 +1,24 @@
|
||||
import { serveStatic } from "@hono/node-server/serve-static";
|
||||
import { type DevServerOptions, default as honoViteDevServer } from "@hono/vite-dev-server";
|
||||
import {
|
||||
type DevServerOptions,
|
||||
default as honoViteDevServer,
|
||||
} from "@hono/vite-dev-server";
|
||||
import type { App } from "bknd";
|
||||
import { type RuntimeBkndConfig, createRuntimeApp } from "bknd/adapter";
|
||||
import {
|
||||
type RuntimeBkndConfig,
|
||||
createRuntimeApp,
|
||||
type FrameworkOptions,
|
||||
} from "bknd/adapter";
|
||||
import { registerLocalMediaAdapter } from "bknd/adapter/node";
|
||||
import { devServerConfig } from "./dev-server-config";
|
||||
|
||||
export type ViteBkndConfig<Env = any> = RuntimeBkndConfig<Env> & {
|
||||
mode?: "cached" | "fresh";
|
||||
setAdminHtml?: boolean;
|
||||
forceDev?: boolean | { mainPath: string };
|
||||
html?: string;
|
||||
};
|
||||
export type ViteEnv = NodeJS.ProcessEnv;
|
||||
export type ViteBkndConfig<Env = ViteEnv> = RuntimeBkndConfig<Env> & {};
|
||||
|
||||
export function addViteScript(html: string, addBkndContext: boolean = true) {
|
||||
export function addViteScript(
|
||||
html: string,
|
||||
addBkndContext: boolean = true,
|
||||
) {
|
||||
return html.replace(
|
||||
"</head>",
|
||||
`<script type="module">
|
||||
@@ -28,52 +34,40 @@ ${addBkndContext ? "<!-- BKND_CONTEXT -->" : ""}
|
||||
);
|
||||
}
|
||||
|
||||
async function createApp(config: ViteBkndConfig = {}, env?: any) {
|
||||
async function createApp<ViteEnv>(
|
||||
config: ViteBkndConfig<ViteEnv> = {},
|
||||
env: ViteEnv = {} as ViteEnv,
|
||||
opts: FrameworkOptions = {},
|
||||
): Promise<App> {
|
||||
registerLocalMediaAdapter();
|
||||
return await createRuntimeApp(
|
||||
{
|
||||
...config,
|
||||
adminOptions:
|
||||
config.setAdminHtml === false
|
||||
? undefined
|
||||
: {
|
||||
html: config.html,
|
||||
forceDev: config.forceDev ?? {
|
||||
mainPath: "/src/main.tsx",
|
||||
},
|
||||
},
|
||||
adminOptions: config.adminOptions ?? {
|
||||
forceDev: {
|
||||
mainPath: "/src/main.tsx",
|
||||
},
|
||||
},
|
||||
serveStatic: ["/assets/*", serveStatic({ root: config.distPath ?? "./" })],
|
||||
},
|
||||
env,
|
||||
opts,
|
||||
);
|
||||
}
|
||||
|
||||
export function serveFresh(config: Omit<ViteBkndConfig, "mode"> = {}) {
|
||||
export function serve<ViteEnv>(
|
||||
config: ViteBkndConfig<ViteEnv> = {},
|
||||
args?: ViteEnv,
|
||||
opts?: FrameworkOptions,
|
||||
) {
|
||||
return {
|
||||
async fetch(request: Request, env: any, ctx: ExecutionContext) {
|
||||
const app = await createApp(config, env);
|
||||
const app = await createApp(config, env, opts);
|
||||
return app.fetch(request, env, ctx);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
let app: App;
|
||||
export function serveCached(config: Omit<ViteBkndConfig, "mode"> = {}) {
|
||||
return {
|
||||
async fetch(request: Request, env: any, ctx: ExecutionContext) {
|
||||
if (!app) {
|
||||
app = await createApp(config, env);
|
||||
}
|
||||
|
||||
return app.fetch(request, env, ctx);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function serve({ mode, ...config }: ViteBkndConfig = {}) {
|
||||
return mode === "fresh" ? serveFresh(config) : serveCached(config);
|
||||
}
|
||||
|
||||
export function devServer(options: DevServerOptions) {
|
||||
return honoViteDevServer({
|
||||
...devServerConfig,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { type DB, Exception } from "core";
|
||||
import { type DB, Exception, type PrimaryFieldType } from "core";
|
||||
import { addFlashMessage } from "core/server/flash";
|
||||
import {
|
||||
type Static,
|
||||
@@ -14,6 +14,7 @@ import { deleteCookie, getSignedCookie, setSignedCookie } from "hono/cookie";
|
||||
import { sign, verify } from "hono/jwt";
|
||||
import type { CookieOptions } from "hono/utils/cookie";
|
||||
import type { ServerEnv } from "modules/Controller";
|
||||
import { pick } from "lodash-es";
|
||||
|
||||
type Input = any; // workaround
|
||||
export type JWTPayload = Parameters<typeof sign>[0];
|
||||
@@ -37,11 +38,10 @@ export interface Strategy {
|
||||
}
|
||||
|
||||
export type User = {
|
||||
id: number;
|
||||
id: PrimaryFieldType;
|
||||
email: string;
|
||||
username: string;
|
||||
password: string;
|
||||
role: string;
|
||||
role?: string | null;
|
||||
};
|
||||
|
||||
export type ProfileExchange = {
|
||||
@@ -158,13 +158,8 @@ export class Authenticator<Strategies extends Record<string, Strategy> = Record<
|
||||
}
|
||||
|
||||
// @todo: add jwt tests
|
||||
async jwt(user: Omit<User, "password">): Promise<string> {
|
||||
const prohibited = ["password"];
|
||||
for (const prop of prohibited) {
|
||||
if (prop in user) {
|
||||
throw new Error(`Property "${prop}" is prohibited`);
|
||||
}
|
||||
}
|
||||
async jwt(_user: Omit<User, "password">): Promise<string> {
|
||||
const user = pick(_user, this.config.jwt.fields);
|
||||
|
||||
const payload: JWTPayload = {
|
||||
...user,
|
||||
|
||||
@@ -29,13 +29,15 @@ export const cloudflare = {
|
||||
{ dir: ctx.dir },
|
||||
);
|
||||
|
||||
const db = ctx.skip ? "d1" : await $p.select({
|
||||
message: "What database do you want to use?",
|
||||
options: [
|
||||
{ label: "Cloudflare D1", value: "d1" },
|
||||
{ label: "LibSQL", value: "libsql" },
|
||||
],
|
||||
});
|
||||
const db = ctx.skip
|
||||
? "d1"
|
||||
: await $p.select({
|
||||
message: "What database do you want to use?",
|
||||
options: [
|
||||
{ label: "Cloudflare D1", value: "d1" },
|
||||
{ label: "LibSQL", value: "libsql" },
|
||||
],
|
||||
});
|
||||
if ($p.isCancel(db)) {
|
||||
process.exit(1);
|
||||
}
|
||||
@@ -64,17 +66,19 @@ export const cloudflare = {
|
||||
|
||||
async function createD1(ctx: TemplateSetupCtx) {
|
||||
const default_db = "data";
|
||||
const name = ctx.skip ? default_db : await $p.text({
|
||||
message: "Enter database name",
|
||||
initialValue: default_db,
|
||||
placeholder: default_db,
|
||||
validate: (v) => {
|
||||
if (!v) {
|
||||
return "Invalid name";
|
||||
}
|
||||
return;
|
||||
},
|
||||
});
|
||||
const name = ctx.skip
|
||||
? default_db
|
||||
: await $p.text({
|
||||
message: "Enter database name",
|
||||
initialValue: default_db,
|
||||
placeholder: default_db,
|
||||
validate: (v) => {
|
||||
if (!v) {
|
||||
return "Invalid name";
|
||||
}
|
||||
return;
|
||||
},
|
||||
});
|
||||
if ($p.isCancel(name)) {
|
||||
process.exit(1);
|
||||
}
|
||||
@@ -153,13 +157,16 @@ async function createLibsql(ctx: TemplateSetupCtx) {
|
||||
}
|
||||
|
||||
async function createR2(ctx: TemplateSetupCtx) {
|
||||
const create = ctx.skip ?? await $p.confirm({
|
||||
message: "Do you want to use a R2 bucket?",
|
||||
initialValue: true,
|
||||
});
|
||||
const create = ctx.skip
|
||||
? false
|
||||
: await $p.confirm({
|
||||
message: "Do you want to use a R2 bucket?",
|
||||
initialValue: true,
|
||||
});
|
||||
if ($p.isCancel(create)) {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!create) {
|
||||
await overrideJson(
|
||||
WRANGLER_FILE,
|
||||
@@ -173,17 +180,19 @@ async function createR2(ctx: TemplateSetupCtx) {
|
||||
}
|
||||
|
||||
const default_bucket = "bucket";
|
||||
const name = ctx.skip ? default_bucket : await $p.text({
|
||||
message: "Enter bucket name",
|
||||
initialValue: default_bucket,
|
||||
placeholder: default_bucket,
|
||||
validate: (v) => {
|
||||
if (!v) {
|
||||
return "Invalid name";
|
||||
}
|
||||
return;
|
||||
},
|
||||
});
|
||||
const name = ctx.skip
|
||||
? default_bucket
|
||||
: await $p.text({
|
||||
message: "Enter bucket name",
|
||||
initialValue: default_bucket,
|
||||
placeholder: default_bucket,
|
||||
validate: (v) => {
|
||||
if (!v) {
|
||||
return "Invalid name";
|
||||
}
|
||||
return;
|
||||
},
|
||||
});
|
||||
if ($p.isCancel(name)) {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { config } from "core";
|
||||
import type { MiddlewareHandler } from "hono";
|
||||
import open from "open";
|
||||
import { fileExists, getRelativeDistPath } from "../../utils/sys";
|
||||
import type { App } from "App";
|
||||
|
||||
export const PLATFORMS = ["node", "bun"] as const;
|
||||
export type Platform = (typeof PLATFORMS)[number];
|
||||
@@ -32,7 +33,7 @@ export async function attachServeStatic(app: any, platform: Platform) {
|
||||
|
||||
export async function startServer(
|
||||
server: Platform,
|
||||
app: any,
|
||||
app: App,
|
||||
options: { port: number; open?: boolean },
|
||||
) {
|
||||
const port = options.port;
|
||||
|
||||
@@ -17,12 +17,13 @@ import {
|
||||
startServer,
|
||||
} from "./platform";
|
||||
import { makeConfig } from "adapter";
|
||||
import { isBun as $isBun } from "cli/utils/sys";
|
||||
|
||||
const env_files = [".env", ".dev.vars"];
|
||||
dotenv.config({
|
||||
path: env_files.map((file) => path.resolve(process.cwd(), file)),
|
||||
});
|
||||
const isBun = typeof Bun !== "undefined";
|
||||
const isBun = $isBun();
|
||||
|
||||
export const run: CliCommand = (program) => {
|
||||
program
|
||||
@@ -76,12 +77,12 @@ async function makeApp(config: MakeAppConfig) {
|
||||
app.emgr.onEvent(
|
||||
App.Events.AppBuiltEvent,
|
||||
async () => {
|
||||
await attachServeStatic(app, config.server?.platform ?? "node");
|
||||
app.registerAdminController();
|
||||
|
||||
if (config.onBuilt) {
|
||||
await config.onBuilt(app);
|
||||
}
|
||||
|
||||
await attachServeStatic(app, config.server?.platform ?? "node");
|
||||
app.registerAdminController();
|
||||
},
|
||||
"sync",
|
||||
);
|
||||
@@ -91,14 +92,14 @@ async function makeApp(config: MakeAppConfig) {
|
||||
}
|
||||
|
||||
export async function makeConfigApp(_config: CliBkndConfig, platform?: Platform) {
|
||||
const config = makeConfig(_config, { env: process.env });
|
||||
const config = makeConfig(_config, process.env);
|
||||
return makeApp({
|
||||
...config,
|
||||
server: { platform },
|
||||
});
|
||||
}
|
||||
|
||||
async function action(options: {
|
||||
type RunOptions = {
|
||||
port: number;
|
||||
memory?: boolean;
|
||||
config?: string;
|
||||
@@ -106,8 +107,9 @@ async function action(options: {
|
||||
dbToken?: string;
|
||||
server: Platform;
|
||||
open?: boolean;
|
||||
}) {
|
||||
colorizeConsole(console);
|
||||
};
|
||||
|
||||
export async function makeAppFromEnv(options: Partial<RunOptions> = {}) {
|
||||
const configFilePath = await getConfigPath(options.config);
|
||||
|
||||
let app: App | undefined = undefined;
|
||||
@@ -147,12 +149,19 @@ async function action(options: {
|
||||
// if nothing helps, create a file based app
|
||||
if (!app) {
|
||||
const connection = { url: "file:data.db" } as Config;
|
||||
console.info("Using connection", c.cyan(connection.url));
|
||||
console.info("Using fallback connection", c.cyan(connection.url));
|
||||
app = await makeApp({
|
||||
connection,
|
||||
server: { platform: options.server },
|
||||
});
|
||||
}
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
async function action(options: RunOptions) {
|
||||
colorizeConsole(console);
|
||||
|
||||
const app = await makeAppFromEnv(options);
|
||||
await startServer(options.server, app, { port: options.port, open: options.open });
|
||||
}
|
||||
|
||||
@@ -1,28 +1,32 @@
|
||||
import { password as $password, text as $text } from "@clack/prompts";
|
||||
import {
|
||||
isCancel as $isCancel,
|
||||
log as $log,
|
||||
password as $password,
|
||||
text as $text,
|
||||
} from "@clack/prompts";
|
||||
import type { App } from "App";
|
||||
import type { PasswordStrategy } from "auth/authenticate/strategies";
|
||||
import { makeConfigApp } from "cli/commands/run";
|
||||
import { getConfigPath } from "cli/commands/run/platform";
|
||||
import type { CliBkndConfig, CliCommand } from "cli/types";
|
||||
import { makeAppFromEnv } from "cli/commands/run";
|
||||
import type { CliCommand } from "cli/types";
|
||||
import { Argument } from "commander";
|
||||
import { $console } from "core";
|
||||
import c from "picocolors";
|
||||
import { isBun } from "cli/utils/sys";
|
||||
|
||||
export const user: CliCommand = (program) => {
|
||||
program
|
||||
.command("user")
|
||||
.description("create and update user (auth)")
|
||||
.addArgument(new Argument("<action>", "action to perform").choices(["create", "update"]))
|
||||
.description("create/update users, or generate a token (auth)")
|
||||
.addArgument(
|
||||
new Argument("<action>", "action to perform").choices(["create", "update", "token"]),
|
||||
)
|
||||
.action(action);
|
||||
};
|
||||
|
||||
async function action(action: "create" | "update", options: any) {
|
||||
const configFilePath = await getConfigPath();
|
||||
if (!configFilePath) {
|
||||
console.error("config file not found");
|
||||
return;
|
||||
}
|
||||
|
||||
const config = (await import(configFilePath).then((m) => m.default)) as CliBkndConfig;
|
||||
const app = await makeConfigApp(config, options.server);
|
||||
async function action(action: "create" | "update" | "token", options: any) {
|
||||
const app = await makeAppFromEnv({
|
||||
server: "node",
|
||||
});
|
||||
|
||||
switch (action) {
|
||||
case "create":
|
||||
@@ -31,6 +35,9 @@ async function action(action: "create" | "update", options: any) {
|
||||
case "update":
|
||||
await update(app, options);
|
||||
break;
|
||||
case "token":
|
||||
await token(app, options);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +45,8 @@ async function create(app: App, options: any) {
|
||||
const strategy = app.module.auth.authenticator.strategy("password") as PasswordStrategy;
|
||||
|
||||
if (!strategy) {
|
||||
throw new Error("Password strategy not configured");
|
||||
$log.error("Password strategy not configured");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const email = await $text({
|
||||
@@ -50,6 +58,7 @@ async function create(app: App, options: any) {
|
||||
return;
|
||||
},
|
||||
});
|
||||
if ($isCancel(email)) process.exit(1);
|
||||
|
||||
const password = await $password({
|
||||
message: "Enter password",
|
||||
@@ -60,20 +69,17 @@ async function create(app: App, options: any) {
|
||||
return;
|
||||
},
|
||||
});
|
||||
|
||||
if (typeof email !== "string" || typeof password !== "string") {
|
||||
console.log("Cancelled");
|
||||
process.exit(0);
|
||||
}
|
||||
if ($isCancel(password)) process.exit(1);
|
||||
|
||||
try {
|
||||
const created = await app.createUser({
|
||||
email,
|
||||
password: await strategy.hash(password as string),
|
||||
});
|
||||
console.log("Created:", created);
|
||||
$log.success(`Created user: ${c.cyan(created.email)}`);
|
||||
} catch (e) {
|
||||
console.error("Error", e);
|
||||
$log.error("Error creating user");
|
||||
$console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,17 +98,14 @@ async function update(app: App, options: any) {
|
||||
return;
|
||||
},
|
||||
})) as string;
|
||||
if (typeof email !== "string") {
|
||||
console.log("Cancelled");
|
||||
process.exit(0);
|
||||
}
|
||||
if ($isCancel(email)) process.exit(1);
|
||||
|
||||
const { data: user } = await em.repository(users_entity).findOne({ email });
|
||||
if (!user) {
|
||||
console.log("User not found");
|
||||
process.exit(0);
|
||||
$log.error("User not found");
|
||||
process.exit(1);
|
||||
}
|
||||
console.log("User found:", user);
|
||||
$log.info(`User found: ${c.cyan(user.email)}`);
|
||||
|
||||
const password = await $password({
|
||||
message: "New Password?",
|
||||
@@ -113,10 +116,7 @@ async function update(app: App, options: any) {
|
||||
return;
|
||||
},
|
||||
});
|
||||
if (typeof password !== "string") {
|
||||
console.log("Cancelled");
|
||||
process.exit(0);
|
||||
}
|
||||
if ($isCancel(password)) process.exit(1);
|
||||
|
||||
try {
|
||||
function togglePw(visible: boolean) {
|
||||
@@ -134,8 +134,42 @@ async function update(app: App, options: any) {
|
||||
});
|
||||
togglePw(false);
|
||||
|
||||
console.log("Updated:", user);
|
||||
$log.success(`Updated user: ${c.cyan(user.email)}`);
|
||||
} catch (e) {
|
||||
console.error("Error", e);
|
||||
$log.error("Error updating user");
|
||||
$console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
async function token(app: App, options: any) {
|
||||
if (isBun()) {
|
||||
$log.error("Please use node to generate tokens");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const config = app.module.auth.toJSON(true);
|
||||
const users_entity = config.entity_name as "users";
|
||||
const em = app.modules.ctx().em;
|
||||
|
||||
const email = (await $text({
|
||||
message: "Which user? Enter email",
|
||||
validate: (v) => {
|
||||
if (!v.includes("@")) {
|
||||
return "Invalid email";
|
||||
}
|
||||
return;
|
||||
},
|
||||
})) as string;
|
||||
if ($isCancel(email)) process.exit(1);
|
||||
|
||||
const { data: user } = await em.repository(users_entity).findOne({ email });
|
||||
if (!user) {
|
||||
$log.error("User not found");
|
||||
process.exit(1);
|
||||
}
|
||||
$log.info(`User found: ${c.cyan(user.email)}`);
|
||||
|
||||
console.log(
|
||||
`\n${c.dim("Token:")}\n${c.yellow(await app.module.auth.authenticator.jwt(user))}\n`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,14 @@ import { readFile } from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import url from "node:url";
|
||||
|
||||
export function isBun(): boolean {
|
||||
try {
|
||||
return typeof Bun !== "undefined";
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function getRootPath() {
|
||||
const _path = path.dirname(url.fileURLToPath(import.meta.url));
|
||||
// because of "src", local needs one more level up
|
||||
|
||||
+14
-13
@@ -1,4 +1,5 @@
|
||||
enum Change {
|
||||
// biome-ignore lint/suspicious/noConstEnum: <explanation>
|
||||
export const enum DiffChange {
|
||||
Add = "a",
|
||||
Remove = "r",
|
||||
Edit = "e",
|
||||
@@ -7,8 +8,8 @@ enum Change {
|
||||
type Object = object;
|
||||
type Primitive = string | number | boolean | null | object | any[] | undefined;
|
||||
|
||||
interface DiffEntry {
|
||||
t: Change | string;
|
||||
export interface DiffEntry {
|
||||
t: DiffChange | string;
|
||||
p: (string | number)[];
|
||||
o: Primitive;
|
||||
n: Primitive;
|
||||
@@ -47,7 +48,7 @@ function diff(oldObj: Object, newObj: Object): DiffEntry[] {
|
||||
|
||||
if (typeof oldValue !== typeof newValue) {
|
||||
diffs.push({
|
||||
t: Change.Edit,
|
||||
t: DiffChange.Edit,
|
||||
p: path,
|
||||
o: oldValue,
|
||||
n: newValue,
|
||||
@@ -57,14 +58,14 @@ function diff(oldObj: Object, newObj: Object): DiffEntry[] {
|
||||
for (let i = 0; i < maxLength; i++) {
|
||||
if (i >= oldValue.length) {
|
||||
diffs.push({
|
||||
t: Change.Add,
|
||||
t: DiffChange.Add,
|
||||
p: [...path, i],
|
||||
o: undefined,
|
||||
n: newValue[i],
|
||||
});
|
||||
} else if (i >= newValue.length) {
|
||||
diffs.push({
|
||||
t: Change.Remove,
|
||||
t: DiffChange.Remove,
|
||||
p: [...path, i],
|
||||
o: oldValue[i],
|
||||
n: undefined,
|
||||
@@ -80,14 +81,14 @@ function diff(oldObj: Object, newObj: Object): DiffEntry[] {
|
||||
for (const key of allKeys) {
|
||||
if (!(key in oldValue)) {
|
||||
diffs.push({
|
||||
t: Change.Add,
|
||||
t: DiffChange.Add,
|
||||
p: [...path, key],
|
||||
o: undefined,
|
||||
n: newValue[key],
|
||||
});
|
||||
} else if (!(key in newValue)) {
|
||||
diffs.push({
|
||||
t: Change.Remove,
|
||||
t: DiffChange.Remove,
|
||||
p: [...path, key],
|
||||
o: oldValue[key],
|
||||
n: undefined,
|
||||
@@ -98,7 +99,7 @@ function diff(oldObj: Object, newObj: Object): DiffEntry[] {
|
||||
}
|
||||
} else {
|
||||
diffs.push({
|
||||
t: Change.Edit,
|
||||
t: DiffChange.Edit,
|
||||
p: path,
|
||||
o: oldValue,
|
||||
n: newValue,
|
||||
@@ -136,9 +137,9 @@ function applyChange(obj: Object, diff: DiffEntry) {
|
||||
const parent = getParent(obj, path.slice(0, -1));
|
||||
const key = path[path.length - 1]!;
|
||||
|
||||
if (type === Change.Add || type === Change.Edit) {
|
||||
if (type === DiffChange.Add || type === DiffChange.Edit) {
|
||||
parent[key] = newValue;
|
||||
} else if (type === Change.Remove) {
|
||||
} else if (type === DiffChange.Remove) {
|
||||
if (Array.isArray(parent)) {
|
||||
parent.splice(key as number, 1);
|
||||
} else {
|
||||
@@ -152,13 +153,13 @@ function revertChange(obj: Object, diff: DiffEntry) {
|
||||
const parent = getParent(obj, path.slice(0, -1));
|
||||
const key = path[path.length - 1]!;
|
||||
|
||||
if (type === Change.Add) {
|
||||
if (type === DiffChange.Add) {
|
||||
if (Array.isArray(parent)) {
|
||||
parent.splice(key as number, 1);
|
||||
} else {
|
||||
delete parent[key];
|
||||
}
|
||||
} else if (type === Change.Remove || type === Change.Edit) {
|
||||
} else if (type === DiffChange.Remove || type === DiffChange.Edit) {
|
||||
parent[key] = oldValue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,6 +61,19 @@ export class AppData extends Module<typeof dataConfigSchema> {
|
||||
this.setBuilt();
|
||||
}
|
||||
|
||||
override async onBeforeUpdate(from: AppDataConfig, to: AppDataConfig): Promise<AppDataConfig> {
|
||||
// this is not 100% yet, since it could be legit
|
||||
const entities = {
|
||||
from: Object.keys(from.entities ?? {}),
|
||||
to: Object.keys(to.entities ?? {}),
|
||||
};
|
||||
if (entities.from.length - entities.to.length > 1) {
|
||||
throw new Error("Cannot remove more than one entity at a time");
|
||||
}
|
||||
|
||||
return to;
|
||||
}
|
||||
|
||||
getSchema() {
|
||||
return dataConfigSchema;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { App } from "App";
|
||||
import { type Context, Hono } from "hono";
|
||||
import * as middlewares from "modules/middlewares";
|
||||
import type { SafeUser } from "auth";
|
||||
|
||||
export type ServerEnv = {
|
||||
Variables: {
|
||||
@@ -10,7 +11,7 @@ export type ServerEnv = {
|
||||
resolved: boolean;
|
||||
registered: boolean;
|
||||
skip: boolean;
|
||||
user?: { id: any; role?: string; [key: string]: any };
|
||||
user?: SafeUser;
|
||||
};
|
||||
html?: string;
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Guard } from "auth";
|
||||
import { $console, BkndError, DebugLogger, env } from "core";
|
||||
import { EventManager } from "core/events";
|
||||
import { clone, diff } from "core/object/diff";
|
||||
import * as $diff from "core/object/diff";
|
||||
import {
|
||||
Default,
|
||||
type Static,
|
||||
@@ -95,7 +95,7 @@ export type ModuleManagerOptions = {
|
||||
verbosity?: Verbosity;
|
||||
};
|
||||
|
||||
type ConfigTable<Json = ModuleConfigs> = {
|
||||
export type ConfigTable<Json = ModuleConfigs> = {
|
||||
id?: number;
|
||||
version: number;
|
||||
type: "config" | "diff" | "backup";
|
||||
@@ -144,6 +144,7 @@ export class ModuleManager {
|
||||
private _version: number = 0;
|
||||
private _built = false;
|
||||
private readonly _booted_with?: "provided" | "partial";
|
||||
private _stable_configs: ModuleConfigs | undefined;
|
||||
|
||||
private logger: DebugLogger;
|
||||
|
||||
@@ -310,7 +311,7 @@ export class ModuleManager {
|
||||
|
||||
try {
|
||||
const state = await this.fetch();
|
||||
if (!state) throw new BkndError("save: no config found");
|
||||
if (!state) throw new BkndError("no config found");
|
||||
this.logger.log("fetched version", state.version);
|
||||
|
||||
if (state.version !== version) {
|
||||
@@ -330,15 +331,21 @@ export class ModuleManager {
|
||||
this.logger.log("version matches", state.version);
|
||||
|
||||
// clean configs because of Diff() function
|
||||
const diffs = diff(state.json, clone(configs));
|
||||
const diffs = $diff.diff(state.json, $diff.clone(configs));
|
||||
this.logger.log("checking diff", [diffs.length]);
|
||||
|
||||
if (diff.length > 0) {
|
||||
if (diffs.length > 0) {
|
||||
// validate diffs, it'll throw on invalid
|
||||
this.validateDiffs(diffs);
|
||||
|
||||
const date = new Date();
|
||||
// store diff
|
||||
await this.mutator().insertOne({
|
||||
version,
|
||||
type: "diff",
|
||||
json: clone(diffs),
|
||||
json: $diff.clone(diffs),
|
||||
created_at: date,
|
||||
updated_at: date,
|
||||
});
|
||||
|
||||
// store new version
|
||||
@@ -346,7 +353,7 @@ export class ModuleManager {
|
||||
{
|
||||
version,
|
||||
json: configs,
|
||||
updated_at: new Date(),
|
||||
updated_at: date,
|
||||
} as any,
|
||||
{
|
||||
type: "config",
|
||||
@@ -358,7 +365,7 @@ export class ModuleManager {
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
if (e instanceof BkndError) {
|
||||
if (e instanceof BkndError && e.message === "no config found") {
|
||||
this.logger.log("no config, just save fresh");
|
||||
// no config, just save
|
||||
await this.mutator().insertOne({
|
||||
@@ -369,10 +376,12 @@ export class ModuleManager {
|
||||
updated_at: new Date(),
|
||||
});
|
||||
} else if (e instanceof TransformPersistFailedException) {
|
||||
console.error("Cannot save invalid config");
|
||||
$console.error("ModuleManager: Cannot save invalid config");
|
||||
this.revertModules();
|
||||
throw e;
|
||||
} else {
|
||||
console.error("Aborting");
|
||||
$console.error("ModuleManager: Aborting");
|
||||
this.revertModules();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
@@ -386,6 +395,52 @@ export class ModuleManager {
|
||||
return this;
|
||||
}
|
||||
|
||||
private revertModules() {
|
||||
if (this._stable_configs) {
|
||||
$console.warn("ModuleManager: Reverting modules");
|
||||
this.setConfigs(this._stable_configs as any);
|
||||
} else {
|
||||
$console.error("ModuleManager: No stable configs to revert to");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates received diffs for an additional security control.
|
||||
* Checks:
|
||||
* - check if module is registered
|
||||
* - run modules onBeforeUpdate() for added protection
|
||||
*
|
||||
* **Important**: Throw `Error` so it won't get catched.
|
||||
*
|
||||
* @param diffs
|
||||
* @private
|
||||
*/
|
||||
private validateDiffs(diffs: $diff.DiffEntry[]): void {
|
||||
// check top level paths, and only allow a single module to be modified in a single transaction
|
||||
const modules = [...new Set(diffs.map((d) => d.p[0]))];
|
||||
if (modules.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const moduleName of modules) {
|
||||
const name = moduleName as ModuleKey;
|
||||
const module = this.get(name) as Module;
|
||||
if (!module) {
|
||||
const msg = "validateDiffs: module not registered";
|
||||
// biome-ignore format: ...
|
||||
$console.error(msg, JSON.stringify({ module: name, diffs }, null, 2));
|
||||
throw new Error(msg);
|
||||
}
|
||||
|
||||
// pass diffs to the module to allow it to throw
|
||||
if (this._stable_configs?.[name]) {
|
||||
const current = $diff.clone(this._stable_configs?.[name]);
|
||||
const modified = $diff.apply({ [name]: current }, diffs)[name];
|
||||
module.onBeforeUpdate(current, modified);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private setConfigs(configs: ModuleConfigs): void {
|
||||
this.logger.log("setting configs");
|
||||
objectEach(configs, (config, key) => {
|
||||
@@ -519,6 +574,9 @@ export class ModuleManager {
|
||||
this.logger.log("resetting flags");
|
||||
ctx.flags = Module.ctx_flags;
|
||||
|
||||
// storing last stable config version
|
||||
this._stable_configs = $diff.clone(this.configs());
|
||||
|
||||
this.logger.clear();
|
||||
return state;
|
||||
}
|
||||
@@ -551,7 +609,6 @@ export class ModuleManager {
|
||||
name: Module,
|
||||
): Pick<ReturnType<Modules[Module]["schema"]>, "set" | "patch" | "overwrite" | "remove"> {
|
||||
const module = this.modules[name];
|
||||
const copy = structuredClone(this.configs());
|
||||
|
||||
return new Proxy(module.schema(), {
|
||||
get: (target, prop: string) => {
|
||||
@@ -560,7 +617,7 @@ export class ModuleManager {
|
||||
}
|
||||
|
||||
return async (...args) => {
|
||||
console.log("[Safe Mutate]", name);
|
||||
$console.log("[Safe Mutate]", name);
|
||||
try {
|
||||
// overwrite listener to run build inside this try/catch
|
||||
module.setListener(async () => {
|
||||
@@ -582,12 +639,12 @@ export class ModuleManager {
|
||||
|
||||
return result;
|
||||
} catch (e) {
|
||||
console.error("[Safe Mutate] failed", e);
|
||||
$console.error(`[Safe Mutate] failed "${name}":`, String(e));
|
||||
|
||||
// revert to previous config & rebuild using original listener
|
||||
this.setConfigs(copy);
|
||||
this.revertModules();
|
||||
await this.onModuleConfigUpdated(name, module.config as any);
|
||||
console.log("[Safe Mutate] reverted");
|
||||
$console.log(`[Safe Mutate] reverted "${name}":`);
|
||||
|
||||
// make sure to throw the error
|
||||
throw e;
|
||||
|
||||
@@ -14,10 +14,11 @@ const htmlBkndContextReplace = "<!-- BKND_CONTEXT -->";
|
||||
// @todo: add migration to remove admin path from config
|
||||
export type AdminControllerOptions = {
|
||||
basepath?: string;
|
||||
assets_path?: string;
|
||||
adminBasepath?: string;
|
||||
assetsPath?: string;
|
||||
html?: string;
|
||||
forceDev?: boolean | { mainPath: string };
|
||||
debug_rerenders?: boolean;
|
||||
debugRerenders?: boolean;
|
||||
};
|
||||
|
||||
export class AdminController extends Controller {
|
||||
@@ -36,7 +37,8 @@ export class AdminController extends Controller {
|
||||
return {
|
||||
...this._options,
|
||||
basepath: this._options.basepath ?? "/",
|
||||
assets_path: this._options.assets_path ?? config.server.assets_path,
|
||||
adminBasepath: this._options.adminBasepath ?? "",
|
||||
assetsPath: this._options.assetsPath ?? config.server.assets_path,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -48,6 +50,10 @@ export class AdminController extends Controller {
|
||||
return (this.basepath + route).replace(/(?<!:)\/+/g, "/");
|
||||
}
|
||||
|
||||
private withAdminBasePath(route: string = "") {
|
||||
return this.withBasePath(this.options.adminBasepath + route);
|
||||
}
|
||||
|
||||
override getController() {
|
||||
const { auth: authMiddleware, permission } = this.middlewares;
|
||||
const hono = this.create().use(
|
||||
@@ -63,16 +69,17 @@ export class AdminController extends Controller {
|
||||
|
||||
const authRoutes = {
|
||||
root: "/",
|
||||
success: configs.auth.cookie.pathSuccess ?? "/",
|
||||
loggedOut: configs.auth.cookie.pathLoggedOut ?? "/",
|
||||
login: "/auth/login",
|
||||
logout: "/auth/logout",
|
||||
success: configs.auth.cookie.pathSuccess ?? this.withAdminBasePath("/"),
|
||||
loggedOut: configs.auth.cookie.pathLoggedOut ?? this.withAdminBasePath("/"),
|
||||
login: this.withAdminBasePath("/auth/login"),
|
||||
register: this.withAdminBasePath("/auth/register"),
|
||||
logout: this.withAdminBasePath("/auth/logout"),
|
||||
};
|
||||
|
||||
hono.use("*", async (c, next) => {
|
||||
const obj = {
|
||||
user: c.get("auth")?.user,
|
||||
logout_route: this.withBasePath(authRoutes.logout),
|
||||
logout_route: this.withAdminBasePath(authRoutes.logout),
|
||||
};
|
||||
const html = await this.getHtml(obj);
|
||||
if (!html) {
|
||||
@@ -86,8 +93,7 @@ export class AdminController extends Controller {
|
||||
});
|
||||
|
||||
if (auth_enabled) {
|
||||
hono.get(
|
||||
authRoutes.login,
|
||||
const redirectRouteParams = [
|
||||
permission([SystemPermissions.accessAdmin, SystemPermissions.schemaRead], {
|
||||
// @ts-ignore
|
||||
onGranted: async (c) => {
|
||||
@@ -101,7 +107,10 @@ export class AdminController extends Controller {
|
||||
async (c) => {
|
||||
return c.html(c.get("html")!);
|
||||
},
|
||||
);
|
||||
] as const;
|
||||
|
||||
hono.get(authRoutes.login, ...redirectRouteParams);
|
||||
hono.get(authRoutes.register, ...redirectRouteParams);
|
||||
|
||||
hono.get(authRoutes.logout, async (c) => {
|
||||
await auth.authenticator?.logout(c);
|
||||
@@ -164,8 +173,8 @@ export class AdminController extends Controller {
|
||||
|
||||
if (isProd) {
|
||||
let manifest: any;
|
||||
if (this.options.assets_path.startsWith("http")) {
|
||||
manifest = await fetch(this.options.assets_path + "manifest.json", {
|
||||
if (this.options.assetsPath.startsWith("http")) {
|
||||
manifest = await fetch(this.options.assetsPath + "manifest.json", {
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
},
|
||||
@@ -182,7 +191,7 @@ export class AdminController extends Controller {
|
||||
assets.css = manifest["src/ui/main.tsx"].css[0] as any;
|
||||
}
|
||||
|
||||
const favicon = isProd ? this.options.assets_path + "favicon.ico" : "/favicon.ico";
|
||||
const favicon = isProd ? this.options.assetsPath + "favicon.ico" : "/favicon.ico";
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
@@ -197,7 +206,7 @@ export class AdminController extends Controller {
|
||||
/>
|
||||
<link rel="icon" href={favicon} type="image/x-icon" />
|
||||
<title>BKND</title>
|
||||
{this.options.debug_rerenders && (
|
||||
{this.options.debugRerenders && (
|
||||
<script
|
||||
crossOrigin="anonymous"
|
||||
src="//unpkg.com/react-scan/dist/auto.global.js"
|
||||
@@ -205,8 +214,8 @@ export class AdminController extends Controller {
|
||||
)}
|
||||
{isProd ? (
|
||||
<Fragment>
|
||||
<script type="module" src={this.options.assets_path + assets?.js} />
|
||||
<link rel="stylesheet" href={this.options.assets_path + assets?.css} />
|
||||
<script type="module" src={this.options.assetsPath + assets?.js} />
|
||||
<link rel="stylesheet" href={this.options.assetsPath + assets?.css} />
|
||||
</Fragment>
|
||||
) : (
|
||||
<Fragment>
|
||||
|
||||
@@ -18,6 +18,7 @@ import { Controller } from "modules/Controller";
|
||||
import {
|
||||
MODULE_NAMES,
|
||||
type ModuleConfigs,
|
||||
type ModuleSchemas,
|
||||
type ModuleKey,
|
||||
getDefaultConfig,
|
||||
} from "modules/ModuleManager";
|
||||
@@ -36,6 +37,12 @@ export type ConfigUpdate<Key extends ModuleKey = ModuleKey> = {
|
||||
export type ConfigUpdateResponse<Key extends ModuleKey = ModuleKey> =
|
||||
| ConfigUpdate<Key>
|
||||
| { success: false; type: "type-invalid" | "error" | "unknown"; error?: any; errors?: any };
|
||||
export type SchemaResponse = {
|
||||
version: string;
|
||||
schema: ModuleSchemas;
|
||||
config: ModuleConfigs;
|
||||
permissions: string[];
|
||||
};
|
||||
|
||||
export class SystemController extends Controller {
|
||||
constructor(private readonly app: App) {
|
||||
@@ -153,7 +160,6 @@ export class SystemController extends Controller {
|
||||
if (this.app.modules.get(module).schema().has(path)) {
|
||||
return c.json({ success: false, path, error: "Path already exists" }, { status: 400 });
|
||||
}
|
||||
console.log("-- add", module, path, value);
|
||||
|
||||
return await handleConfigUpdateResponse(c, async () => {
|
||||
await this.app.mutateConfig(module).patch(path, value);
|
||||
|
||||
+22
-23
@@ -1,6 +1,6 @@
|
||||
import { MantineProvider } from "@mantine/core";
|
||||
import { Notifications } from "@mantine/notifications";
|
||||
import React from "react";
|
||||
import React, { type ReactNode } from "react";
|
||||
import { BkndProvider, type BkndAdminOptions } from "ui/client/bknd";
|
||||
import { useTheme } from "ui/client/use-theme";
|
||||
import { Logo } from "ui/components/display/Logo";
|
||||
@@ -21,33 +21,32 @@ export default function Admin({
|
||||
withProvider = false,
|
||||
config,
|
||||
}: BkndAdminProps) {
|
||||
const Component = (
|
||||
const { theme } = useTheme();
|
||||
const Provider = ({ children }: any) =>
|
||||
withProvider ? (
|
||||
<ClientProvider
|
||||
baseUrl={baseUrlOverride}
|
||||
{...(typeof withProvider === "object" ? withProvider : {})}
|
||||
>
|
||||
{children}
|
||||
</ClientProvider>
|
||||
) : (
|
||||
children
|
||||
);
|
||||
|
||||
const BkndWrapper = ({ children }: { children: ReactNode }) => (
|
||||
<BkndProvider options={config} fallback={<Skeleton theme={config?.theme} />}>
|
||||
<AdminInternal />
|
||||
{children}
|
||||
</BkndProvider>
|
||||
);
|
||||
return withProvider ? (
|
||||
<ClientProvider
|
||||
baseUrl={baseUrlOverride}
|
||||
{...(typeof withProvider === "object" ? withProvider : {})}
|
||||
>
|
||||
{Component}
|
||||
</ClientProvider>
|
||||
) : (
|
||||
Component
|
||||
);
|
||||
}
|
||||
|
||||
function AdminInternal() {
|
||||
const { theme } = useTheme();
|
||||
|
||||
return (
|
||||
<MantineProvider {...createMantineTheme(theme as any)}>
|
||||
<Notifications position="top-right" />
|
||||
<BkndModalsProvider>
|
||||
<Routes />
|
||||
</BkndModalsProvider>
|
||||
</MantineProvider>
|
||||
<Provider>
|
||||
<MantineProvider {...createMantineTheme(theme as any)}>
|
||||
<Notifications position="top-right" />
|
||||
<Routes BkndWrapper={BkndWrapper} basePath={config?.basepath} />
|
||||
</MantineProvider>
|
||||
</Provider>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@ import { useApi } from "ui/client";
|
||||
import { type TSchemaActions, getSchemaActions } from "./schema/actions";
|
||||
import { AppReduced } from "./utils/AppReduced";
|
||||
import type { AppTheme } from "ui/client/use-theme";
|
||||
import { Message } from "ui/components/display/Message";
|
||||
import { useNavigate } from "ui/lib/routes";
|
||||
|
||||
export type BkndAdminOptions = {
|
||||
logo_return_path?: string;
|
||||
@@ -102,13 +104,19 @@ export function BkndProvider({
|
||||
} as any);
|
||||
|
||||
startTransition(() => {
|
||||
document.startViewTransition(() => {
|
||||
const commit = () => {
|
||||
setSchema(newSchema);
|
||||
setWithSecrets(_includeSecrets);
|
||||
setFetched(true);
|
||||
set_local_version((v) => v + 1);
|
||||
fetching.current = Fetching.None;
|
||||
});
|
||||
};
|
||||
|
||||
if ("startViewTransition" in document) {
|
||||
document.startViewTransition(commit);
|
||||
} else {
|
||||
commit();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -132,22 +140,24 @@ export function BkndProvider({
|
||||
value={{ ...schema, actions, requireSecrets, app, options: app.options, hasSecrets }}
|
||||
key={local_version}
|
||||
>
|
||||
{/*{error && (
|
||||
<Alert.Exception className="gap-2">
|
||||
<IconAlertHexagon />
|
||||
You attempted to load system configuration with secrets without having proper
|
||||
permission.
|
||||
<a href={schema.config.server.admin.basepath || "/"}>
|
||||
<Button variant="red">Reload</Button>
|
||||
</a>
|
||||
</Alert.Exception>
|
||||
)}*/}
|
||||
|
||||
{children}
|
||||
{error ? <AccessDenied /> : children}
|
||||
</BkndContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
function AccessDenied() {
|
||||
const [navigate] = useNavigate();
|
||||
return (
|
||||
<Message.MissingPermission
|
||||
what="the Admin UI"
|
||||
primary={{
|
||||
children: "Login",
|
||||
onClick: () => navigate("/auth/login"),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function useBknd({ withSecrets }: { withSecrets?: boolean } = {}): BkndContext {
|
||||
const ctx = useContext(BkndContext);
|
||||
if (withSecrets) ctx.requireSecrets();
|
||||
|
||||
@@ -36,6 +36,7 @@ export type BaseProps = {
|
||||
size?: keyof typeof sizes;
|
||||
variant?: keyof typeof styles;
|
||||
labelClassName?: string;
|
||||
"data-testid"?: string;
|
||||
};
|
||||
|
||||
const Base = ({
|
||||
|
||||
@@ -16,9 +16,11 @@ const MissingPermission = ({
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
const NotEnabled = (props: Partial<EmptyProps>) => <Empty title="Not Enabled" {...props} />;
|
||||
|
||||
export const Message = {
|
||||
NotFound,
|
||||
NotAllowed,
|
||||
NotEnabled,
|
||||
MissingPermission,
|
||||
};
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
import type { JsonSchema } from "json-schema-library";
|
||||
import type { ChangeEvent, ComponentPropsWithoutRef, ReactNode } from "react";
|
||||
import {
|
||||
type ChangeEvent,
|
||||
type ComponentPropsWithoutRef,
|
||||
type ElementType,
|
||||
type ReactNode,
|
||||
useEffect,
|
||||
useId,
|
||||
} from "react";
|
||||
import ErrorBoundary from "ui/components/display/ErrorBoundary";
|
||||
import * as Formy from "ui/components/form/Formy";
|
||||
import { useEvent } from "ui/hooks/use-event";
|
||||
@@ -7,7 +14,7 @@ import { ArrayField } from "./ArrayField";
|
||||
import { FieldWrapper, type FieldwrapperProps } from "./FieldWrapper";
|
||||
import { useDerivedFieldContext, useFormValue } from "./Form";
|
||||
import { ObjectField } from "./ObjectField";
|
||||
import { coerce, isType, isTypeSchema } from "./utils";
|
||||
import { coerce, firstDefined, isType, isTypeSchema } from "./utils";
|
||||
|
||||
export type FieldProps = {
|
||||
onChange?: (e: ChangeEvent<any>) => void;
|
||||
@@ -24,6 +31,19 @@ export const Field = (props: FieldProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
export const HiddenField = ({
|
||||
as = "div",
|
||||
className,
|
||||
...props
|
||||
}: FieldProps & { as?: ElementType; className?: string }) => {
|
||||
const Component = as;
|
||||
return (
|
||||
<Component className={[className, "hidden"].filter(Boolean).join(" ")}>
|
||||
<Field {...props} />
|
||||
</Component>
|
||||
);
|
||||
};
|
||||
|
||||
const fieldErrorBoundary =
|
||||
({ name }: FieldProps) =>
|
||||
({ error }: { error: Error }) => (
|
||||
@@ -41,8 +61,9 @@ const FieldImpl = ({
|
||||
...props
|
||||
}: FieldProps) => {
|
||||
const { path, setValue, schema, ...ctx } = useDerivedFieldContext(name);
|
||||
const id = `${name}-${useId()}`;
|
||||
const required = typeof _required === "boolean" ? _required : ctx.required;
|
||||
//console.log("Field", { name, path, schema });
|
||||
|
||||
if (!isTypeSchema(schema))
|
||||
return (
|
||||
<Pre>
|
||||
@@ -58,7 +79,21 @@ const FieldImpl = ({
|
||||
return <ArrayField path={name} />;
|
||||
}
|
||||
|
||||
const disabled = props.disabled ?? schema.readOnly ?? "const" in schema ?? false;
|
||||
// account for `defaultValue`
|
||||
// like <Field name="name" inputProps={{ defaultValue: "oauth" }} />
|
||||
useEffect(() => {
|
||||
if (inputProps?.defaultValue) {
|
||||
setValue(path, inputProps.defaultValue);
|
||||
}
|
||||
}, [inputProps?.defaultValue]);
|
||||
|
||||
const disabled = firstDefined(
|
||||
inputProps?.disabled,
|
||||
props.disabled,
|
||||
schema.readOnly,
|
||||
"const" in schema,
|
||||
false,
|
||||
);
|
||||
|
||||
const handleChange = useEvent((e: ChangeEvent<HTMLInputElement>) => {
|
||||
const value = coerce(e.target.value, schema as any, { required });
|
||||
@@ -70,9 +105,10 @@ const FieldImpl = ({
|
||||
});
|
||||
|
||||
return (
|
||||
<FieldWrapper name={name} required={required} schema={schema} {...props}>
|
||||
<FieldWrapper name={name} required={required} schema={schema} fieldId={id} {...props}>
|
||||
<FieldComponent
|
||||
{...inputProps}
|
||||
id={id}
|
||||
schema={schema}
|
||||
name={name}
|
||||
required={required}
|
||||
@@ -93,6 +129,7 @@ export const Pre = ({ children }) => (
|
||||
export type FieldComponentProps = {
|
||||
schema: JsonSchema;
|
||||
render?: (props: Omit<FieldComponentProps, "render">) => ReactNode;
|
||||
"data-testId"?: string;
|
||||
} & ComponentPropsWithoutRef<"input">;
|
||||
|
||||
export const FieldComponent = ({ schema, render, ..._props }: FieldComponentProps) => {
|
||||
@@ -111,7 +148,7 @@ export const FieldComponent = ({ schema, render, ..._props }: FieldComponentProp
|
||||
if (render) return render({ schema, ...props });
|
||||
|
||||
if (schema.enum) {
|
||||
return <Formy.Select id={props.name} options={schema.enum} {...(props as any)} />;
|
||||
return <Formy.Select options={schema.enum} {...(props as any)} />;
|
||||
}
|
||||
|
||||
if (isType(schema.type, ["number", "integer"])) {
|
||||
@@ -121,26 +158,17 @@ export const FieldComponent = ({ schema, render, ..._props }: FieldComponentProp
|
||||
step: schema.multipleOf,
|
||||
};
|
||||
|
||||
return (
|
||||
<Formy.Input
|
||||
type="number"
|
||||
id={props.name}
|
||||
{...props}
|
||||
value={props.value ?? ""}
|
||||
{...additional}
|
||||
/>
|
||||
);
|
||||
return <Formy.Input type="number" {...props} value={props.value ?? ""} {...additional} />;
|
||||
}
|
||||
|
||||
if (isType(schema.type, "boolean")) {
|
||||
return <Formy.Switch id={props.name} {...(props as any)} checked={value === true} />;
|
||||
return <Formy.Switch {...(props as any)} checked={value === true} />;
|
||||
}
|
||||
|
||||
if (isType(schema.type, "string") && schema.format === "date-time") {
|
||||
const value = props.value ? new Date(props.value as string).toISOString().slice(0, 16) : "";
|
||||
return (
|
||||
<Formy.DateInput
|
||||
id={props.name}
|
||||
{...props}
|
||||
value={value}
|
||||
type="datetime-local"
|
||||
@@ -156,7 +184,7 @@ export const FieldComponent = ({ schema, render, ..._props }: FieldComponentProp
|
||||
}
|
||||
|
||||
if (isType(schema.type, "string") && schema.format === "date") {
|
||||
return <Formy.DateInput id={props.name} {...props} value={props.value ?? ""} />;
|
||||
return <Formy.DateInput {...props} value={props.value ?? ""} />;
|
||||
}
|
||||
|
||||
const additional = {
|
||||
@@ -171,7 +199,5 @@ export const FieldComponent = ({ schema, render, ..._props }: FieldComponentProp
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Formy.TypeAwareInput id={props.name} {...props} value={props.value ?? ""} {...additional} />
|
||||
);
|
||||
return <Formy.TypeAwareInput {...props} value={props.value ?? ""} {...additional} />;
|
||||
};
|
||||
|
||||
@@ -24,6 +24,7 @@ export type FieldwrapperProps = {
|
||||
errorPlacement?: "top" | "bottom";
|
||||
description?: string;
|
||||
descriptionPlacement?: "top" | "bottom";
|
||||
fieldId?: string;
|
||||
};
|
||||
|
||||
export function FieldWrapper({
|
||||
@@ -36,6 +37,7 @@ export function FieldWrapper({
|
||||
errorPlacement = "bottom",
|
||||
descriptionPlacement = "bottom",
|
||||
children,
|
||||
fieldId,
|
||||
...props
|
||||
}: FieldwrapperProps) {
|
||||
const errors = useFormError(name, { strict: true });
|
||||
@@ -66,7 +68,7 @@ export function FieldWrapper({
|
||||
{label && (
|
||||
<Formy.Label
|
||||
as={wrapper === "fieldset" ? "legend" : "label"}
|
||||
htmlFor={name}
|
||||
htmlFor={fieldId}
|
||||
className="self-start"
|
||||
>
|
||||
{label} {required && <span className="font-medium opacity-30">*</span>}
|
||||
|
||||
@@ -138,3 +138,10 @@ export function omitSchema<Given extends JSONSchema>(_schema: Given, keys: strin
|
||||
export function isTypeSchema(schema?: JsonSchema): schema is JsonSchema {
|
||||
return typeof schema === "object" && "type" in schema && !isType(schema.type, "error");
|
||||
}
|
||||
|
||||
export function firstDefined<T>(...args: T[]): T | undefined {
|
||||
for (const arg of args) {
|
||||
if (typeof arg !== "undefined") return arg;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import { SocialLink } from "./SocialLink";
|
||||
import type { ValueError } from "@sinclair/typebox/value";
|
||||
import { type TSchema, Value } from "core/utils";
|
||||
import type { Validator } from "json-schema-form-react";
|
||||
import { useTheme } from "ui/client/use-theme";
|
||||
|
||||
class TypeboxValidator implements Validator<ValueError> {
|
||||
async validate(schema: TSchema, data: any) {
|
||||
@@ -46,7 +45,6 @@ export function AuthForm({
|
||||
buttonLabel = action === "login" ? "Sign in" : "Sign up",
|
||||
...props
|
||||
}: LoginFormProps) {
|
||||
const { theme } = useTheme();
|
||||
const basepath = auth?.basepath ?? "/api/auth";
|
||||
const password = {
|
||||
action: `${basepath}/password/${action}`,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ReactNode } from "react";
|
||||
import { isValidElement, type ReactNode } from "react";
|
||||
import { useAuthStrategies } from "../hooks/use-auth";
|
||||
import { AuthForm } from "./AuthForm";
|
||||
|
||||
@@ -30,11 +30,13 @@ export function AuthScreen({
|
||||
{!loading && (
|
||||
<div className="flex flex-col gap-4 items-center w-96 px-6 py-7">
|
||||
{logo ? logo : null}
|
||||
{typeof intro !== "undefined" ? (
|
||||
{isValidElement(intro) ? (
|
||||
intro
|
||||
) : (
|
||||
<div className="flex flex-col items-center">
|
||||
<h1 className="text-xl font-bold">Sign in to your admin panel</h1>
|
||||
<h1 className="text-xl font-bold">
|
||||
Sign {action === "login" ? "in" : "up"} to your admin panel
|
||||
</h1>
|
||||
<p className="text-primary/50">Enter your credentials below to get access.</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import { useApi, useApiInfiniteQuery, useInvalidate } from "ui/client";
|
||||
import { useApi, useApiInfiniteQuery, useApiQuery, useInvalidate } from "ui/client";
|
||||
import { useEvent } from "ui/hooks/use-event";
|
||||
import { Dropzone, type DropzoneProps, type DropzoneRenderProps, type FileState } from "./Dropzone";
|
||||
import { mediaItemsToFileStates } from "./helper";
|
||||
@@ -20,6 +20,7 @@ import { useInViewport } from "@mantine/hooks";
|
||||
export type DropzoneContainerProps = {
|
||||
children?: ReactNode;
|
||||
initialItems?: MediaFieldSchema[] | false;
|
||||
infinite?: boolean;
|
||||
entity?: {
|
||||
name: string;
|
||||
id: number;
|
||||
@@ -39,6 +40,7 @@ export function DropzoneContainer({
|
||||
query,
|
||||
children,
|
||||
randomFilename,
|
||||
infinite = false,
|
||||
...props
|
||||
}: DropzoneContainerProps) {
|
||||
const id = useId();
|
||||
@@ -54,7 +56,7 @@ export function DropzoneContainer({
|
||||
const entity_name = (media?.entity_name ?? "media") as "media";
|
||||
//console.log("dropzone:baseUrl", baseUrl);
|
||||
|
||||
const selectApi = (api: Api, page: number) =>
|
||||
const selectApi = (api: Api, page: number = 0) =>
|
||||
entity
|
||||
? api.data.readManyByReference(entity.name, entity.id, entity.field, {
|
||||
...query,
|
||||
@@ -70,7 +72,11 @@ export function DropzoneContainer({
|
||||
...defaultQuery(page),
|
||||
});
|
||||
|
||||
const $q = useApiInfiniteQuery(selectApi, {});
|
||||
const $q = infinite
|
||||
? useApiInfiniteQuery(selectApi, {})
|
||||
: useApiQuery(selectApi, {
|
||||
enabled: initialItems !== false && !initialItems,
|
||||
});
|
||||
|
||||
const getUploadInfo = useEvent((file) => {
|
||||
const url = entity
|
||||
@@ -108,11 +114,17 @@ export function DropzoneContainer({
|
||||
autoUpload
|
||||
initialItems={_initialItems}
|
||||
footer={
|
||||
<Footer
|
||||
items={_initialItems.length}
|
||||
length={$q._data?.[0]?.body.meta.count ?? 0}
|
||||
onFirstVisible={() => $q.setSize($q.size + 1)}
|
||||
/>
|
||||
infinite &&
|
||||
"setSize" in $q && (
|
||||
<Footer
|
||||
items={_initialItems.length}
|
||||
length={Math.min(
|
||||
$q._data?.[0]?.body.meta.count ?? 0,
|
||||
_initialItems.length + pageSize,
|
||||
)}
|
||||
onFirstVisible={() => $q.setSize($q.size + 1)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
{...props}
|
||||
>
|
||||
@@ -142,15 +154,15 @@ const Footer = ({ items = 0, length = 0, onFirstVisible }) => {
|
||||
const _len = length - items;
|
||||
if (_len <= 0) return null;
|
||||
|
||||
return new Array(Math.max(length - items, 0)).fill(0).map((_, i) => (
|
||||
<div
|
||||
key={i}
|
||||
ref={i === 0 ? ref : undefined}
|
||||
className="w-[49%] md:w-60 bg-muted aspect-square"
|
||||
>
|
||||
{i === 0 ? (inViewport ? `load ${visible}` : "first") : "other"}
|
||||
</div>
|
||||
));
|
||||
return new Array(Math.max(length - items, 0))
|
||||
.fill(0)
|
||||
.map((_, i) => (
|
||||
<div
|
||||
key={i}
|
||||
ref={i === 0 ? ref : undefined}
|
||||
className="w-[49%] md:w-60 bg-muted aspect-square"
|
||||
/>
|
||||
));
|
||||
};
|
||||
|
||||
export function useDropzone() {
|
||||
|
||||
@@ -134,7 +134,7 @@ export function Header({ hasSidebar = true }) {
|
||||
<SidebarToggler />
|
||||
<UserMenu />
|
||||
</div>
|
||||
<div className="hidden lg:flex flex-row items-center px-4 gap-2">
|
||||
<div className="hidden md:flex flex-row items-center px-4 gap-2">
|
||||
<UserMenu />
|
||||
</div>
|
||||
</header>
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
export const config = {};
|
||||
|
||||
export const testIds = {
|
||||
data: {
|
||||
btnCreateEntity: "data-btns-create-entity",
|
||||
},
|
||||
media: {
|
||||
switchEnabled: "media-switch-enabled",
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
import { Logo } from "ui/components/display/Logo";
|
||||
import { Link } from "ui/components/wouter/Link";
|
||||
import { Auth } from "ui/elements";
|
||||
import { useBrowserTitle } from "ui/hooks/use-browser-title";
|
||||
|
||||
export function AuthRegister() {
|
||||
useBrowserTitle(["Register"]);
|
||||
return (
|
||||
<Auth.Screen
|
||||
action="register"
|
||||
logo={
|
||||
<Link href={"/"} className="link">
|
||||
<Logo scale={0.25} />
|
||||
</Link>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import {
|
||||
Form,
|
||||
FormContextOverride,
|
||||
FormDebug,
|
||||
HiddenField,
|
||||
ObjectField,
|
||||
Subscribe,
|
||||
useDerivedFieldContext,
|
||||
@@ -36,9 +37,16 @@ import * as AppShell from "../../layouts/AppShell/AppShell";
|
||||
export function AuthStrategiesList(props) {
|
||||
useBrowserTitle(["Auth", "Strategies"]);
|
||||
|
||||
const { hasSecrets } = useBknd({ withSecrets: true });
|
||||
const {
|
||||
hasSecrets,
|
||||
config: {
|
||||
auth: { enabled },
|
||||
},
|
||||
} = useBknd({ withSecrets: true });
|
||||
if (!hasSecrets) {
|
||||
return <Message.MissingPermission what="Auth Strategies" />;
|
||||
} else if (!enabled) {
|
||||
return <Message.NotEnabled description="Enable Auth first." />;
|
||||
}
|
||||
|
||||
return <AuthStrategiesListInternal {...props} />;
|
||||
@@ -62,7 +70,6 @@ function AuthStrategiesListInternal() {
|
||||
);
|
||||
|
||||
async function handleSubmit(data: any) {
|
||||
console.log("submit", { strategies: data });
|
||||
await $auth.actions.config.set({ strategies: data });
|
||||
}
|
||||
|
||||
@@ -152,7 +159,7 @@ const Strategy = ({ type, name, unavailable }: StrategyProps) => {
|
||||
<span className="leading-none">{autoFormatString(name)}</span>
|
||||
</div>
|
||||
<div className="flex flex-row gap-4 items-center">
|
||||
<StrategyToggle />
|
||||
<StrategyToggle type={type} />
|
||||
<IconButton
|
||||
Icon={TbSettings}
|
||||
size="lg"
|
||||
@@ -168,7 +175,7 @@ const Strategy = ({ type, name, unavailable }: StrategyProps) => {
|
||||
"flex flex-col border-t border-t-muted px-4 pt-3 pb-4 bg-lightest/50 gap-4",
|
||||
)}
|
||||
>
|
||||
<StrategyForm type={type} />
|
||||
<StrategyForm type={type} name={name} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -176,7 +183,7 @@ const Strategy = ({ type, name, unavailable }: StrategyProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
const StrategyToggle = () => {
|
||||
const StrategyToggle = ({ type }: { type: StrategyProps["type"] }) => {
|
||||
const ctx = useDerivedFieldContext("");
|
||||
const { value } = useFormValue("");
|
||||
|
||||
@@ -219,8 +226,10 @@ const OAUTH_BRANDS = {
|
||||
discord: TbBrandDiscordFilled,
|
||||
};
|
||||
|
||||
const StrategyForm = ({ type }: Pick<StrategyProps, "type">) => {
|
||||
let Component = () => <ObjectField path="" wrapperProps={{ wrapper: "group", label: false }} />;
|
||||
const StrategyForm = ({ type, name }: Pick<StrategyProps, "type" | "name">) => {
|
||||
let Component = (p: any) => (
|
||||
<ObjectField path="" wrapperProps={{ wrapper: "group", label: false }} />
|
||||
);
|
||||
switch (type) {
|
||||
case "password":
|
||||
Component = StrategyPasswordForm;
|
||||
@@ -230,16 +239,22 @@ const StrategyForm = ({ type }: Pick<StrategyProps, "type">) => {
|
||||
break;
|
||||
}
|
||||
|
||||
return <Component />;
|
||||
return (
|
||||
<>
|
||||
<HiddenField name="type" inputProps={{ disabled: true, defaultValue: type }} />
|
||||
<Component type={type} name={name} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const StrategyPasswordForm = () => {
|
||||
return <ObjectField path="config" wrapperProps={{ wrapper: "group", label: false }} />;
|
||||
};
|
||||
|
||||
const StrategyOAuthForm = () => {
|
||||
const StrategyOAuthForm = ({ type, name }: Pick<StrategyProps, "type" | "name">) => {
|
||||
return (
|
||||
<>
|
||||
<HiddenField name="config.name" inputProps={{ disabled: true, defaultValue: name }} />
|
||||
<Field name="config.client.client_id" required inputProps={{ type: "password" }} />
|
||||
<Field name="config.client.client_secret" required inputProps={{ type: "password" }} />
|
||||
</>
|
||||
|
||||
@@ -21,6 +21,7 @@ import { Link, isLinkActive } from "ui/components/wouter/Link";
|
||||
import { useBrowserTitle } from "ui/hooks/use-browser-title";
|
||||
import * as AppShell from "ui/layouts/AppShell/AppShell";
|
||||
import { routes, useNavigate, useRouteNavigate } from "ui/lib/routes";
|
||||
import { testIds } from "ui/lib/config";
|
||||
|
||||
export function DataRoot({ children }) {
|
||||
// @todo: settings routes should be centralized
|
||||
@@ -269,6 +270,7 @@ export function DataEmpty() {
|
||||
}}
|
||||
primary={{
|
||||
children: "Create entity",
|
||||
"data-testid": testIds.data.btnCreateEntity,
|
||||
onClick: $data.modals.createEntity,
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -164,26 +164,36 @@ export function DataEntityUpdate({ params }) {
|
||||
]}
|
||||
/>
|
||||
</AppShell.SectionHeader>
|
||||
<AppShell.Scrollable>
|
||||
{error && (
|
||||
<div className="flex flex-row dark:bg-red-950 bg-red-100 p-4">
|
||||
<b className="mr-2">Update failed: </b> {error}
|
||||
</div>
|
||||
)}
|
||||
<EntityForm
|
||||
entity={entity}
|
||||
entityId={entityId}
|
||||
handleSubmit={handleSubmit}
|
||||
fieldsDisabled={fieldsDisabled}
|
||||
data={data ?? undefined}
|
||||
Form={Form}
|
||||
action="update"
|
||||
className="flex flex-grow flex-col gap-3 p-3"
|
||||
/>
|
||||
{targetRelations.length > 0 ? (
|
||||
<EntityDetailRelations id={entityId} entity={entity} relations={targetRelations} />
|
||||
) : null}
|
||||
</AppShell.Scrollable>
|
||||
{$q.isLoading ? (
|
||||
<div className="w-full h-full flex justify-center items-center font-mono opacity-30">
|
||||
Loading...
|
||||
</div>
|
||||
) : (
|
||||
<AppShell.Scrollable>
|
||||
{error && (
|
||||
<div className="flex flex-row dark:bg-red-950 bg-red-100 p-4">
|
||||
<b className="mr-2">Update failed: </b> {error}
|
||||
</div>
|
||||
)}
|
||||
<EntityForm
|
||||
entity={entity}
|
||||
entityId={entityId}
|
||||
handleSubmit={handleSubmit}
|
||||
fieldsDisabled={fieldsDisabled}
|
||||
data={data ?? undefined}
|
||||
Form={Form}
|
||||
action="update"
|
||||
className="flex flex-grow flex-col gap-3 p-3"
|
||||
/>
|
||||
{targetRelations.length > 0 ? (
|
||||
<EntityDetailRelations
|
||||
id={entityId}
|
||||
entity={entity}
|
||||
relations={targetRelations}
|
||||
/>
|
||||
) : null}
|
||||
</AppShell.Scrollable>
|
||||
)}
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
+52
-43
@@ -1,5 +1,4 @@
|
||||
import React, { Suspense, lazy } from "react";
|
||||
import { useBknd } from "ui/client/bknd";
|
||||
import { Suspense, lazy, type ComponentType, type ReactNode } from "react";
|
||||
import { useTheme } from "ui/client/use-theme";
|
||||
import { Route, Router, Switch } from "wouter";
|
||||
import AuthRoutes from "./auth";
|
||||
@@ -10,60 +9,70 @@ import MediaRoutes from "./media";
|
||||
import { Root, RootEmpty } from "./root";
|
||||
import SettingsRoutes from "./settings";
|
||||
import { FlashMessage } from "ui/modules/server/FlashMessage";
|
||||
import { AuthRegister } from "ui/routes/auth/auth.register";
|
||||
import { BkndModalsProvider } from "ui/modals";
|
||||
|
||||
// @ts-ignore
|
||||
const TestRoutes = lazy(() => import("./test"));
|
||||
|
||||
export function Routes() {
|
||||
const { app } = useBknd();
|
||||
export function Routes({
|
||||
BkndWrapper,
|
||||
basePath = "",
|
||||
}: { BkndWrapper: ComponentType<{ children: ReactNode }>; basePath?: string }) {
|
||||
const { theme } = useTheme();
|
||||
|
||||
return (
|
||||
<div id="bknd-admin" className={theme + " antialiased"}>
|
||||
<FlashMessage />
|
||||
<Router base={app.options.basepath}>
|
||||
<Router base={basePath}>
|
||||
<Switch>
|
||||
<Route path="/auth/login" component={AuthLogin} />
|
||||
<Route path="/" nest>
|
||||
<Root>
|
||||
<Switch>
|
||||
<Route path="/test*" nest>
|
||||
<Suspense fallback={null}>
|
||||
<TestRoutes />
|
||||
</Suspense>
|
||||
</Route>
|
||||
<Route path="/auth/register" component={AuthRegister} />
|
||||
|
||||
<Route path="/" component={RootEmpty} />
|
||||
<Route path="/data" nest>
|
||||
<Suspense fallback={null}>
|
||||
<DataRoutes />
|
||||
</Suspense>
|
||||
</Route>
|
||||
<Route path="/flows" nest>
|
||||
<Suspense fallback={null}>
|
||||
<FlowRoutes />
|
||||
</Suspense>
|
||||
</Route>
|
||||
<Route path="/auth" nest>
|
||||
<Suspense fallback={null}>
|
||||
<AuthRoutes />
|
||||
</Suspense>
|
||||
</Route>
|
||||
<Route path="/media" nest>
|
||||
<Suspense fallback={null}>
|
||||
<MediaRoutes />
|
||||
</Suspense>
|
||||
</Route>
|
||||
<Route path="/settings" nest>
|
||||
<Suspense fallback={null}>
|
||||
<SettingsRoutes />
|
||||
</Suspense>
|
||||
</Route>
|
||||
<BkndWrapper>
|
||||
<BkndModalsProvider>
|
||||
<Route path="/" nest>
|
||||
<Root>
|
||||
<Switch>
|
||||
<Route path="/test*" nest>
|
||||
<Suspense fallback={null}>
|
||||
<TestRoutes />
|
||||
</Suspense>
|
||||
</Route>
|
||||
|
||||
<Route path="*" component={NotFound} />
|
||||
</Switch>
|
||||
</Root>
|
||||
</Route>
|
||||
<Route path="/" component={RootEmpty} />
|
||||
<Route path="/data" nest>
|
||||
<Suspense fallback={null}>
|
||||
<DataRoutes />
|
||||
</Suspense>
|
||||
</Route>
|
||||
<Route path="/flows" nest>
|
||||
<Suspense fallback={null}>
|
||||
<FlowRoutes />
|
||||
</Suspense>
|
||||
</Route>
|
||||
<Route path="/auth" nest>
|
||||
<Suspense fallback={null}>
|
||||
<AuthRoutes />
|
||||
</Suspense>
|
||||
</Route>
|
||||
<Route path="/media" nest>
|
||||
<Suspense fallback={null}>
|
||||
<MediaRoutes />
|
||||
</Suspense>
|
||||
</Route>
|
||||
<Route path="/settings" nest>
|
||||
<Suspense fallback={null}>
|
||||
<SettingsRoutes />
|
||||
</Suspense>
|
||||
</Route>
|
||||
|
||||
<Route path="*" component={NotFound} />
|
||||
</Switch>
|
||||
</Root>
|
||||
</Route>
|
||||
</BkndModalsProvider>
|
||||
</BkndWrapper>
|
||||
</Switch>
|
||||
</Router>
|
||||
</div>
|
||||
|
||||
@@ -35,7 +35,7 @@ export function MediaIndex() {
|
||||
return (
|
||||
<AppShell.Scrollable>
|
||||
<div className="flex flex-1 p-3">
|
||||
<Media.Dropzone onClick={onClick} />
|
||||
<Media.Dropzone onClick={onClick} infinite />
|
||||
</div>
|
||||
</AppShell.Scrollable>
|
||||
);
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
} from "ui/components/form/json-schema-form";
|
||||
import { useBrowserTitle } from "ui/hooks/use-browser-title";
|
||||
import * as AppShell from "ui/layouts/AppShell/AppShell";
|
||||
import { testIds } from "ui/lib/config";
|
||||
|
||||
export function MediaSettings(props) {
|
||||
useBrowserTitle(["Media", "Settings"]);
|
||||
@@ -79,7 +80,10 @@ function MediaSettingsInternal() {
|
||||
<AppShell.Scrollable>
|
||||
<RootFormError />
|
||||
<div className="flex flex-col gap-3 p-3">
|
||||
<Field name="enabled" />
|
||||
<Field
|
||||
name="enabled"
|
||||
inputProps={{ "data-testId": testIds.media.switchEnabled }}
|
||||
/>
|
||||
<div className="flex flex-col gap-3 relative">
|
||||
<Overlay />
|
||||
<Field name="storage.body_max_size" label="Storage Body Max Size" />
|
||||
@@ -139,6 +143,7 @@ function Adapters() {
|
||||
<Button
|
||||
key={i}
|
||||
onClick={() => ctx.select(i)}
|
||||
id={`adapter-${schema.properties.type.const}`}
|
||||
variant={ctx.selected === i ? "primary" : "outline"}
|
||||
className={twMerge(
|
||||
"flex flex-row items-center justify-center gap-3 border",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"include": ["./src/**/*.ts", "./src/**/*.tsx"],
|
||||
"exclude": ["./node_modules", "./__test__"]
|
||||
"exclude": ["./node_modules", "./__test__", "./e2e"]
|
||||
}
|
||||
|
||||
+8
-1
@@ -33,6 +33,13 @@
|
||||
"bknd": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["./src/**/*.ts", "./src/**/*.tsx", "vite.dev.ts", "build.ts", "__test__"],
|
||||
"include": [
|
||||
"./src/**/*.ts",
|
||||
"./src/**/*.tsx",
|
||||
"vite.dev.ts",
|
||||
"build.ts",
|
||||
"__test__",
|
||||
"e2e/**/*.ts"
|
||||
],
|
||||
"exclude": ["node_modules", "dist", "dist/types", "**/*.d.ts"]
|
||||
}
|
||||
|
||||
+4
-2
@@ -50,7 +50,7 @@ if (example) {
|
||||
}
|
||||
|
||||
let app: App;
|
||||
const recreate = import.meta.env.VITE_APP_DISABLE_FRESH !== "1";
|
||||
const recreate = import.meta.env.VITE_APP_FRESH === "1";
|
||||
let firstStart = true;
|
||||
export default {
|
||||
async fetch(request: Request) {
|
||||
@@ -66,7 +66,9 @@ export default {
|
||||
},
|
||||
"sync",
|
||||
);
|
||||
await app.build();
|
||||
await app.build({
|
||||
sync: !!(firstStart && example),
|
||||
});
|
||||
|
||||
// log routes
|
||||
if (firstStart) {
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import { defineConfig } from "vitest/config";
|
||||
import react from "@vitejs/plugin-react";
|
||||
import tsconfigPaths from "vite-tsconfig-paths";
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react(), tsconfigPaths()],
|
||||
test: {
|
||||
globals: true,
|
||||
environment: "jsdom",
|
||||
setupFiles: ["./__test__/vitest/setup.ts"],
|
||||
include: ["**/*.vi-test.ts"],
|
||||
coverage: {
|
||||
provider: "v8",
|
||||
reporter: ["text", "json", "html"],
|
||||
exclude: ["node_modules/", "**/*.d.ts", "**/*.test.ts", "**/*.config.ts"],
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -69,18 +69,23 @@
|
||||
"@libsql/kysely-libsql": "^0.4.1",
|
||||
"@mantine/modals": "^7.17.1",
|
||||
"@mantine/notifications": "^7.17.1",
|
||||
"@playwright/test": "^1.51.1",
|
||||
"@rjsf/core": "5.22.2",
|
||||
"@tabler/icons-react": "3.18.0",
|
||||
"@tailwindcss/postcss": "^4.0.12",
|
||||
"@tailwindcss/vite": "^4.0.12",
|
||||
"@testing-library/jest-dom": "^6.6.3",
|
||||
"@testing-library/react": "^16.2.0",
|
||||
"@types/node": "^22.13.10",
|
||||
"@types/react": "^19.0.10",
|
||||
"@types/react-dom": "^19.0.4",
|
||||
"@vitejs/plugin-react": "^4.3.4",
|
||||
"@vitest/coverage-v8": "^3.0.9",
|
||||
"autoprefixer": "^10.4.21",
|
||||
"clsx": "^2.1.1",
|
||||
"dotenv": "^16.4.7",
|
||||
"jotai": "^2.12.2",
|
||||
"jsdom": "^26.0.0",
|
||||
"kysely-d1": "^0.3.0",
|
||||
"open": "^10.1.0",
|
||||
"openapi-types": "^12.1.3",
|
||||
@@ -102,6 +107,7 @@
|
||||
"tsx": "^4.19.3",
|
||||
"vite": "^6.2.1",
|
||||
"vite-tsconfig-paths": "^5.1.4",
|
||||
"vitest": "^3.0.9",
|
||||
"wouter": "^3.6.0",
|
||||
},
|
||||
"optionalDependencies": {
|
||||
@@ -176,10 +182,14 @@
|
||||
},
|
||||
},
|
||||
"packages": {
|
||||
"@adobe/css-tools": ["@adobe/css-tools@4.4.2", "", {}, "sha512-baYZExFpsdkBNuvGKTKWCwKH57HRZLVtycZS05WTQNVOiXVSeAki3nU35zlRbToeMW8aHlJfyS+1C4BOv27q0A=="],
|
||||
|
||||
"@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="],
|
||||
|
||||
"@ampproject/remapping": ["@ampproject/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="],
|
||||
|
||||
"@asamuzakjp/css-color": ["@asamuzakjp/css-color@3.1.1", "", { "dependencies": { "@csstools/css-calc": "^2.1.2", "@csstools/css-color-parser": "^3.0.8", "@csstools/css-parser-algorithms": "^3.0.4", "@csstools/css-tokenizer": "^3.0.3", "lru-cache": "^10.4.3" } }, "sha512-hpRD68SV2OMcZCsrbdkccTw5FXjNDLo5OuqSHyHZfwweGsDWZwDJ2+gONyNAbazZclobMirACLw0lk8WVxIqxA=="],
|
||||
|
||||
"@aws-crypto/crc32": ["@aws-crypto/crc32@5.2.0", "", { "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "tslib": "^2.6.2" } }, "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg=="],
|
||||
|
||||
"@aws-crypto/crc32c": ["@aws-crypto/crc32c@5.2.0", "", { "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "tslib": "^2.6.2" } }, "sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag=="],
|
||||
@@ -460,7 +470,7 @@
|
||||
|
||||
"@babel/types": ["@babel/types@7.26.9", "", { "dependencies": { "@babel/helper-string-parser": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9" } }, "sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw=="],
|
||||
|
||||
"@bcoe/v8-coverage": ["@bcoe/v8-coverage@0.2.3", "", {}, "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw=="],
|
||||
"@bcoe/v8-coverage": ["@bcoe/v8-coverage@1.0.2", "", {}, "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA=="],
|
||||
|
||||
"@biomejs/biome": ["@biomejs/biome@1.9.4", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "1.9.4", "@biomejs/cli-darwin-x64": "1.9.4", "@biomejs/cli-linux-arm64": "1.9.4", "@biomejs/cli-linux-arm64-musl": "1.9.4", "@biomejs/cli-linux-x64": "1.9.4", "@biomejs/cli-linux-x64-musl": "1.9.4", "@biomejs/cli-win32-arm64": "1.9.4", "@biomejs/cli-win32-x64": "1.9.4" }, "bin": { "biome": "bin/biome" } }, "sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog=="],
|
||||
|
||||
@@ -546,6 +556,16 @@
|
||||
|
||||
"@cspotcode/source-map-support": ["@cspotcode/source-map-support@0.8.1", "", { "dependencies": { "@jridgewell/trace-mapping": "0.3.9" } }, "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw=="],
|
||||
|
||||
"@csstools/color-helpers": ["@csstools/color-helpers@5.0.2", "", {}, "sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA=="],
|
||||
|
||||
"@csstools/css-calc": ["@csstools/css-calc@2.1.2", "", { "peerDependencies": { "@csstools/css-parser-algorithms": "^3.0.4", "@csstools/css-tokenizer": "^3.0.3" } }, "sha512-TklMyb3uBB28b5uQdxjReG4L80NxAqgrECqLZFQbyLekwwlcDDS8r3f07DKqeo8C4926Br0gf/ZDe17Zv4wIuw=="],
|
||||
|
||||
"@csstools/css-color-parser": ["@csstools/css-color-parser@3.0.8", "", { "dependencies": { "@csstools/color-helpers": "^5.0.2", "@csstools/css-calc": "^2.1.2" }, "peerDependencies": { "@csstools/css-parser-algorithms": "^3.0.4", "@csstools/css-tokenizer": "^3.0.3" } }, "sha512-pdwotQjCCnRPuNi06jFuP68cykU1f3ZWExLe/8MQ1LOs8Xq+fTkYgd+2V8mWUWMrOn9iS2HftPVaMZDaXzGbhQ=="],
|
||||
|
||||
"@csstools/css-parser-algorithms": ["@csstools/css-parser-algorithms@3.0.4", "", { "peerDependencies": { "@csstools/css-tokenizer": "^3.0.3" } }, "sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A=="],
|
||||
|
||||
"@csstools/css-tokenizer": ["@csstools/css-tokenizer@3.0.3", "", {}, "sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw=="],
|
||||
|
||||
"@cypress/request": ["@cypress/request@3.0.6", "", { "dependencies": { "aws-sign2": "~0.7.0", "aws4": "^1.8.0", "caseless": "~0.12.0", "combined-stream": "~1.0.6", "extend": "~3.0.2", "forever-agent": "~0.6.1", "form-data": "~4.0.0", "http-signature": "~1.4.0", "is-typedarray": "~1.0.0", "isstream": "~0.1.2", "json-stringify-safe": "~5.0.1", "mime-types": "~2.1.19", "performance-now": "^2.1.0", "qs": "6.13.0", "safe-buffer": "^5.1.2", "tough-cookie": "^5.0.0", "tunnel-agent": "^0.6.0", "uuid": "^8.3.2" } }, "sha512-fi0eVdCOtKu5Ed6+E8mYxUF6ZTFJDZvHogCBelM0xVXmrDEkyM22gRArQzq1YcHPm1V47Vf/iAD+WgVdUlJCGg=="],
|
||||
|
||||
"@dagrejs/dagre": ["@dagrejs/dagre@1.1.4", "", { "dependencies": { "@dagrejs/graphlib": "2.2.4" } }, "sha512-QUTc54Cg/wvmlEUxB+uvoPVKFazM1H18kVHBQNmK2NbrDR5ihOCR6CXLnDSZzMcSQKJtabPUWridBOlJM3WkDg=="],
|
||||
@@ -832,6 +852,8 @@
|
||||
|
||||
"@plasmicapp/watcher": ["@plasmicapp/watcher@1.0.83", "", { "dependencies": { "socket.io-client": "^4.1.2" } }, "sha512-0XTCsTydTBfOE5al9QOyW89kigjzU9az2JL4n7e3+fVlStEqbhYSB9PIa+Bhiv1iVN91V7xjUSyVJXloWJgwPg=="],
|
||||
|
||||
"@playwright/test": ["@playwright/test@1.51.1", "", { "dependencies": { "playwright": "1.51.1" }, "bin": { "playwright": "cli.js" } }, "sha512-nM+kEaTSAoVlXmMPH10017vn3FSiFqr/bh4fKg9vmAdMfd9SDqRZNvPSiAHADc/itWak+qPvMPZQOPwCBW7k7Q=="],
|
||||
|
||||
"@polka/url": ["@polka/url@1.0.0-next.28", "", {}, "sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw=="],
|
||||
|
||||
"@promptbook/utils": ["@promptbook/utils@0.69.5", "", { "dependencies": { "spacetrim": "0.11.59" } }, "sha512-xm5Ti/Hp3o4xHrsK9Yy3MS6KbDxYbq485hDsFvxqaNA7equHLPdo8H8faTitTeb14QCDfLW4iwCxdVYu5sn6YQ=="],
|
||||
@@ -1184,6 +1206,10 @@
|
||||
|
||||
"@testing-library/dom": ["@testing-library/dom@10.4.0", "", { "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", "@types/aria-query": "^5.0.1", "aria-query": "5.3.0", "chalk": "^4.1.0", "dom-accessibility-api": "^0.5.9", "lz-string": "^1.5.0", "pretty-format": "^27.0.2" } }, "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ=="],
|
||||
|
||||
"@testing-library/jest-dom": ["@testing-library/jest-dom@6.6.3", "", { "dependencies": { "@adobe/css-tools": "^4.4.0", "aria-query": "^5.0.0", "chalk": "^3.0.0", "css.escape": "^1.5.1", "dom-accessibility-api": "^0.6.3", "lodash": "^4.17.21", "redent": "^3.0.0" } }, "sha512-IteBhl4XqYNkM54f4ejhLRJiZNqcSCoXUOG2CPK7qbD322KjQozM4kHQOfkG2oln9b9HTYqs+Sae8vBATubxxA=="],
|
||||
|
||||
"@testing-library/react": ["@testing-library/react@16.2.0", "", { "dependencies": { "@babel/runtime": "^7.12.5" }, "peerDependencies": { "@testing-library/dom": "^10.0.0", "@types/react": "^18.0.0 || ^19.0.0", "@types/react-dom": "^18.0.0 || ^19.0.0", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-2cSskAvA1QNtKc8Y9VJQRv0tm3hLVgxRGDB+KYhIaPQJ1I+RHbhIXcM+zClKXzMes/wshsMVzf4B9vS4IZpqDQ=="],
|
||||
|
||||
"@testing-library/user-event": ["@testing-library/user-event@14.6.1", "", { "peerDependencies": { "@testing-library/dom": ">=7.21.4" } }, "sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw=="],
|
||||
|
||||
"@tootallnate/quickjs-emscripten": ["@tootallnate/quickjs-emscripten@0.23.0", "", {}, "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA=="],
|
||||
@@ -1338,6 +1364,8 @@
|
||||
|
||||
"@vitest/browser": ["@vitest/browser@3.0.8", "", { "dependencies": { "@testing-library/user-event": "^14.6.1", "@vitest/mocker": "3.0.8", "@vitest/utils": "3.0.8", "magic-string": "^0.30.17", "msw": "^2.7.3", "sirv": "^3.0.1", "tinyrainbow": "^2.0.0", "ws": "^8.18.1" }, "peerDependencies": { "playwright": "*", "vitest": "3.0.8", "webdriverio": "^7.0.0 || ^8.0.0 || ^9.0.0" }, "optionalPeers": ["playwright", "webdriverio"] }, "sha512-ARAGav2gJE/t+qF44fOwJlK0dK8ZJEYjZ725ewHzN6liBAJSCt9elqv/74iwjl5RJzel00k/wufJB7EEu+MJEw=="],
|
||||
|
||||
"@vitest/coverage-v8": ["@vitest/coverage-v8@3.0.9", "", { "dependencies": { "@ampproject/remapping": "^2.3.0", "@bcoe/v8-coverage": "^1.0.2", "debug": "^4.4.0", "istanbul-lib-coverage": "^3.2.2", "istanbul-lib-report": "^3.0.1", "istanbul-lib-source-maps": "^5.0.6", "istanbul-reports": "^3.1.7", "magic-string": "^0.30.17", "magicast": "^0.3.5", "std-env": "^3.8.0", "test-exclude": "^7.0.1", "tinyrainbow": "^2.0.0" }, "peerDependencies": { "@vitest/browser": "3.0.9", "vitest": "3.0.9" }, "optionalPeers": ["@vitest/browser"] }, "sha512-15OACZcBtQ34keIEn19JYTVuMFTlFrClclwWjHo/IRPg/8ELpkgNTl0o7WLP9WO9XGH6+tip9CPYtEOrIDJvBA=="],
|
||||
|
||||
"@vitest/expect": ["@vitest/expect@3.0.8", "", { "dependencies": { "@vitest/spy": "3.0.8", "@vitest/utils": "3.0.8", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-Xu6TTIavTvSSS6LZaA3EebWFr6tsoXPetOWNMOlc7LO88QVVBwq2oQWBoDiLCN6YTvNYsGSjqOO8CAdjom5DCQ=="],
|
||||
|
||||
"@vitest/mocker": ["@vitest/mocker@3.0.8", "", { "dependencies": { "@vitest/spy": "3.0.8", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^5.0.0 || ^6.0.0" }, "optionalPeers": ["msw", "vite"] }, "sha512-n3LjS7fcW1BCoF+zWZxG7/5XvuYH+lsFg+BDwwAz0arIwHQJFUEsKBQ0BLU49fCxuM/2HSeBPHQD8WjgrxMfow=="],
|
||||
@@ -1392,7 +1420,7 @@
|
||||
|
||||
"acorn-walk": ["acorn-walk@8.3.2", "", {}, "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A=="],
|
||||
|
||||
"agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="],
|
||||
"agent-base": ["agent-base@7.1.3", "", {}, "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw=="],
|
||||
|
||||
"ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="],
|
||||
|
||||
@@ -1746,11 +1774,13 @@
|
||||
|
||||
"css-what": ["css-what@6.1.0", "", {}, "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw=="],
|
||||
|
||||
"css.escape": ["css.escape@1.5.1", "", {}, "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg=="],
|
||||
|
||||
"cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="],
|
||||
|
||||
"cssom": ["cssom@0.4.4", "", {}, "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw=="],
|
||||
|
||||
"cssstyle": ["cssstyle@2.3.0", "", { "dependencies": { "cssom": "~0.3.6" } }, "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A=="],
|
||||
"cssstyle": ["cssstyle@4.3.0", "", { "dependencies": { "@asamuzakjp/css-color": "^3.1.1", "rrweb-cssom": "^0.8.0" } }, "sha512-6r0NiY0xizYqfBvWp1G7WXJ06/bZyrk7Dc6PHql82C/pKGUTKu4yAX4Y8JPamb1ob9nBKuxWzCGTRuGwU3yxJQ=="],
|
||||
|
||||
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
|
||||
|
||||
@@ -1778,7 +1808,7 @@
|
||||
|
||||
"data-uri-to-buffer": ["data-uri-to-buffer@4.0.1", "", {}, "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A=="],
|
||||
|
||||
"data-urls": ["data-urls@1.1.0", "", { "dependencies": { "abab": "^2.0.0", "whatwg-mimetype": "^2.2.0", "whatwg-url": "^7.0.0" } }, "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ=="],
|
||||
"data-urls": ["data-urls@5.0.0", "", { "dependencies": { "whatwg-mimetype": "^4.0.0", "whatwg-url": "^14.0.0" } }, "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg=="],
|
||||
|
||||
"data-view-buffer": ["data-view-buffer@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ=="],
|
||||
|
||||
@@ -1794,6 +1824,8 @@
|
||||
|
||||
"decamelize-keys": ["decamelize-keys@1.1.1", "", { "dependencies": { "decamelize": "^1.1.0", "map-obj": "^1.0.0" } }, "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg=="],
|
||||
|
||||
"decimal.js": ["decimal.js@10.5.0", "", {}, "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw=="],
|
||||
|
||||
"decode-formdata": ["decode-formdata@0.8.0", "", {}, "sha512-iUzDgnWsw5ToSkFY7VPFA5Gfph6ROoOxOB7Ybna4miUSzLZ4KaSJk6IAB2AdW6+C9vCVWhjjNA4gjT6wF3eZHQ=="],
|
||||
|
||||
"decode-uri-component": ["decode-uri-component@0.2.2", "", {}, "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ=="],
|
||||
@@ -1846,7 +1878,7 @@
|
||||
|
||||
"doctrine": ["doctrine@3.0.0", "", { "dependencies": { "esutils": "^2.0.2" } }, "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w=="],
|
||||
|
||||
"dom-accessibility-api": ["dom-accessibility-api@0.5.16", "", {}, "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg=="],
|
||||
"dom-accessibility-api": ["dom-accessibility-api@0.6.3", "", {}, "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w=="],
|
||||
|
||||
"dom-helpers": ["dom-helpers@5.2.1", "", { "dependencies": { "@babel/runtime": "^7.8.7", "csstype": "^3.0.2" } }, "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA=="],
|
||||
|
||||
@@ -2200,7 +2232,7 @@
|
||||
|
||||
"hosted-git-info": ["hosted-git-info@4.1.0", "", { "dependencies": { "lru-cache": "^6.0.0" } }, "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA=="],
|
||||
|
||||
"html-encoding-sniffer": ["html-encoding-sniffer@1.0.2", "", { "dependencies": { "whatwg-encoding": "^1.0.1" } }, "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw=="],
|
||||
"html-encoding-sniffer": ["html-encoding-sniffer@4.0.0", "", { "dependencies": { "whatwg-encoding": "^3.1.1" } }, "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ=="],
|
||||
|
||||
"html-escaper": ["html-escaper@2.0.2", "", {}, "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg=="],
|
||||
|
||||
@@ -2216,13 +2248,13 @@
|
||||
|
||||
"http-status-codes": ["http-status-codes@2.3.0", "", {}, "sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA=="],
|
||||
|
||||
"https-proxy-agent": ["https-proxy-agent@5.0.1", "", { "dependencies": { "agent-base": "6", "debug": "4" } }, "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA=="],
|
||||
"https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="],
|
||||
|
||||
"human-signals": ["human-signals@1.1.1", "", {}, "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw=="],
|
||||
|
||||
"humanize-duration": ["humanize-duration@3.32.1", "", {}, "sha512-inh5wue5XdfObhu/IGEMiA1nUXigSGcaKNemcbLRKa7jXYGDZXr3LoT9pTIzq2hPEbld7w/qv9h+ikWGz8fL1g=="],
|
||||
|
||||
"iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="],
|
||||
"iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="],
|
||||
|
||||
"ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
|
||||
|
||||
@@ -2328,6 +2360,8 @@
|
||||
|
||||
"is-plain-object": ["is-plain-object@5.0.0", "", {}, "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q=="],
|
||||
|
||||
"is-potential-custom-element-name": ["is-potential-custom-element-name@1.0.1", "", {}, "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ=="],
|
||||
|
||||
"is-promise": ["is-promise@2.2.2", "", {}, "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ=="],
|
||||
|
||||
"is-reference": ["is-reference@1.2.1", "", { "dependencies": { "@types/estree": "*" } }, "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ=="],
|
||||
@@ -2374,7 +2408,7 @@
|
||||
|
||||
"istanbul-lib-report": ["istanbul-lib-report@3.0.1", "", { "dependencies": { "istanbul-lib-coverage": "^3.0.0", "make-dir": "^4.0.0", "supports-color": "^7.1.0" } }, "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw=="],
|
||||
|
||||
"istanbul-lib-source-maps": ["istanbul-lib-source-maps@4.0.1", "", { "dependencies": { "debug": "^4.1.1", "istanbul-lib-coverage": "^3.0.0", "source-map": "^0.6.1" } }, "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw=="],
|
||||
"istanbul-lib-source-maps": ["istanbul-lib-source-maps@5.0.6", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.23", "debug": "^4.1.1", "istanbul-lib-coverage": "^3.0.0" } }, "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A=="],
|
||||
|
||||
"istanbul-reports": ["istanbul-reports@3.1.7", "", { "dependencies": { "html-escaper": "^2.0.0", "istanbul-lib-report": "^3.0.0" } }, "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g=="],
|
||||
|
||||
@@ -2456,7 +2490,7 @@
|
||||
|
||||
"jsbn": ["jsbn@0.1.1", "", {}, "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg=="],
|
||||
|
||||
"jsdom": ["jsdom@15.2.1", "", { "dependencies": { "abab": "^2.0.0", "acorn": "^7.1.0", "acorn-globals": "^4.3.2", "array-equal": "^1.0.0", "cssom": "^0.4.1", "cssstyle": "^2.0.0", "data-urls": "^1.1.0", "domexception": "^1.0.1", "escodegen": "^1.11.1", "html-encoding-sniffer": "^1.0.2", "nwsapi": "^2.2.0", "parse5": "5.1.0", "pn": "^1.1.0", "request": "^2.88.0", "request-promise-native": "^1.0.7", "saxes": "^3.1.9", "symbol-tree": "^3.2.2", "tough-cookie": "^3.0.1", "w3c-hr-time": "^1.0.1", "w3c-xmlserializer": "^1.1.2", "webidl-conversions": "^4.0.2", "whatwg-encoding": "^1.0.5", "whatwg-mimetype": "^2.3.0", "whatwg-url": "^7.0.0", "ws": "^7.0.0", "xml-name-validator": "^3.0.0" }, "peerDependencies": { "canvas": "^2.5.0" }, "optionalPeers": ["canvas"] }, "sha512-fAl1W0/7T2G5vURSyxBzrJ1LSdQn6Tr5UX/xD4PXDx/PDgwygedfW6El/KIj3xJ7FU61TTYnc/l/B7P49Eqt6g=="],
|
||||
"jsdom": ["jsdom@26.0.0", "", { "dependencies": { "cssstyle": "^4.2.1", "data-urls": "^5.0.0", "decimal.js": "^10.4.3", "form-data": "^4.0.1", "html-encoding-sniffer": "^4.0.0", "http-proxy-agent": "^7.0.2", "https-proxy-agent": "^7.0.6", "is-potential-custom-element-name": "^1.0.1", "nwsapi": "^2.2.16", "parse5": "^7.2.1", "rrweb-cssom": "^0.8.0", "saxes": "^6.0.0", "symbol-tree": "^3.2.4", "tough-cookie": "^5.0.0", "w3c-xmlserializer": "^5.0.0", "webidl-conversions": "^7.0.0", "whatwg-encoding": "^3.1.1", "whatwg-mimetype": "^4.0.0", "whatwg-url": "^14.1.0", "ws": "^8.18.0", "xml-name-validator": "^5.0.0" }, "peerDependencies": { "canvas": "^3.0.0" }, "optionalPeers": ["canvas"] }, "sha512-BZYDGVAIriBWTpIxYzrXjv3E/4u8+/pSG5bQdIYCbNCGOvsPkDQfTVLAIXAf9ETdCpduCVTkDe2NNZ8NIwUVzw=="],
|
||||
|
||||
"jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="],
|
||||
|
||||
@@ -2614,7 +2648,9 @@
|
||||
|
||||
"magic-string": ["magic-string@0.30.17", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA=="],
|
||||
|
||||
"make-dir": ["make-dir@3.1.0", "", { "dependencies": { "semver": "^6.0.0" } }, "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw=="],
|
||||
"magicast": ["magicast@0.3.5", "", { "dependencies": { "@babel/parser": "^7.25.4", "@babel/types": "^7.25.4", "source-map-js": "^1.2.0" } }, "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ=="],
|
||||
|
||||
"make-dir": ["make-dir@4.0.0", "", { "dependencies": { "semver": "^7.5.3" } }, "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw=="],
|
||||
|
||||
"make-error": ["make-error@1.3.6", "", {}, "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw=="],
|
||||
|
||||
@@ -2822,7 +2858,7 @@
|
||||
|
||||
"parse-json": ["parse-json@5.2.0", "", { "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" } }, "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg=="],
|
||||
|
||||
"parse5": ["parse5@5.1.0", "", {}, "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ=="],
|
||||
"parse5": ["parse5@7.2.1", "", { "dependencies": { "entities": "^4.5.0" } }, "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ=="],
|
||||
|
||||
"parse5-htmlparser2-tree-adapter": ["parse5-htmlparser2-tree-adapter@7.1.0", "", { "dependencies": { "domhandler": "^5.0.3", "parse5": "^7.0.0" } }, "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g=="],
|
||||
|
||||
@@ -2896,6 +2932,10 @@
|
||||
|
||||
"pkginfo": ["pkginfo@0.4.1", "", {}, "sha512-8xCNE/aT/EXKenuMDZ+xTVwkT8gsoHN2z/Q29l80u0ppGEXVvsKRzNMbtKhg8LS8k1tJLAHHylf6p4VFmP6XUQ=="],
|
||||
|
||||
"playwright": ["playwright@1.51.1", "", { "dependencies": { "playwright-core": "1.51.1" }, "optionalDependencies": { "fsevents": "2.3.2" }, "bin": { "playwright": "cli.js" } }, "sha512-kkx+MB2KQRkyxjYPc3a0wLZZoDczmppyGJIvQ43l+aZihkaVvmu/21kiyaHeHjiFxjxNNFnUncKmcGIyOojsaw=="],
|
||||
|
||||
"playwright-core": ["playwright-core@1.51.1", "", { "bin": { "playwright-core": "cli.js" } }, "sha512-/crRMj8+j/Nq5s8QcvegseuyeZPxpQCZb6HNk3Sos3BlZyAknRjoyJPFWkpNn8v0+P3WiwqFF8P+zQo4eqiNuw=="],
|
||||
|
||||
"plimit-lit": ["plimit-lit@1.6.1", "", { "dependencies": { "queue-lit": "^1.5.1" } }, "sha512-B7+VDyb8Tl6oMJT9oSO2CW8XC/T4UcJGrwOVoNGwOQsQYhlpfajmrMj5xeejqaASq3V/EqThyOeATEOMuSEXiA=="],
|
||||
|
||||
"plur": ["plur@4.0.0", "", { "dependencies": { "irregular-plurals": "^3.2.0" } }, "sha512-4UGewrYgqDFw9vV6zNV+ADmPAUAfJPKtGvb/VdpQAx25X5f3xXdGdyOEVFwkl8Hl/tl7+xbeHqSEM+D5/TirUg=="],
|
||||
@@ -3134,6 +3174,8 @@
|
||||
|
||||
"rollup-pluginutils": ["rollup-pluginutils@2.8.2", "", { "dependencies": { "estree-walker": "^0.6.1" } }, "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ=="],
|
||||
|
||||
"rrweb-cssom": ["rrweb-cssom@0.8.0", "", {}, "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw=="],
|
||||
|
||||
"rsvp": ["rsvp@4.8.5", "", {}, "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA=="],
|
||||
|
||||
"run-applescript": ["run-applescript@7.0.0", "", {}, "sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A=="],
|
||||
@@ -3164,7 +3206,7 @@
|
||||
|
||||
"sane": ["sane@4.1.0", "", { "dependencies": { "@cnakazawa/watch": "^1.0.3", "anymatch": "^2.0.0", "capture-exit": "^2.0.0", "exec-sh": "^0.3.2", "execa": "^1.0.0", "fb-watchman": "^2.0.0", "micromatch": "^3.1.4", "minimist": "^1.1.1", "walker": "~1.0.5" }, "bin": { "sane": "./src/cli.js" } }, "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA=="],
|
||||
|
||||
"saxes": ["saxes@3.1.11", "", { "dependencies": { "xmlchars": "^2.1.1" } }, "sha512-Ydydq3zC+WYDJK1+gRxRapLIED9PWeSuuS41wqyoRmzvhhh9nc+QQrVMKJYzJFULazeGhzSV0QleN2wD3boh2g=="],
|
||||
"saxes": ["saxes@6.0.0", "", { "dependencies": { "xmlchars": "^2.2.0" } }, "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA=="],
|
||||
|
||||
"scheduler": ["scheduler@0.25.0", "", {}, "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA=="],
|
||||
|
||||
@@ -3388,7 +3430,7 @@
|
||||
|
||||
"terser": ["terser@4.8.1", "", { "dependencies": { "commander": "^2.20.0", "source-map": "~0.6.1", "source-map-support": "~0.5.12" }, "bin": { "terser": "bin/terser" } }, "sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw=="],
|
||||
|
||||
"test-exclude": ["test-exclude@6.0.0", "", { "dependencies": { "@istanbuljs/schema": "^0.1.2", "glob": "^7.1.4", "minimatch": "^3.0.4" } }, "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w=="],
|
||||
"test-exclude": ["test-exclude@7.0.1", "", { "dependencies": { "@istanbuljs/schema": "^0.1.2", "glob": "^10.4.1", "minimatch": "^9.0.4" } }, "sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg=="],
|
||||
|
||||
"text-decoder": ["text-decoder@1.2.3", "", { "dependencies": { "b4a": "^1.6.4" } }, "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA=="],
|
||||
|
||||
@@ -3442,7 +3484,7 @@
|
||||
|
||||
"tough-cookie": ["tough-cookie@5.1.2", "", { "dependencies": { "tldts": "^6.1.32" } }, "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A=="],
|
||||
|
||||
"tr46": ["tr46@1.0.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA=="],
|
||||
"tr46": ["tr46@5.1.0", "", { "dependencies": { "punycode": "^2.3.1" } }, "sha512-IUWnUK7ADYR5Sl1fZlO1INDUhVhatWl7BtJWsIhwJ0UAK7ilzzIa8uIqOO/aYVWHZPJkKbEL+362wrzoeRF7bw=="],
|
||||
|
||||
"tree-kill": ["tree-kill@1.2.2", "", { "bin": { "tree-kill": "cli.js" } }, "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A=="],
|
||||
|
||||
@@ -3608,7 +3650,7 @@
|
||||
|
||||
"w3c-keyname": ["w3c-keyname@2.2.8", "", {}, "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ=="],
|
||||
|
||||
"w3c-xmlserializer": ["w3c-xmlserializer@1.1.2", "", { "dependencies": { "domexception": "^1.0.1", "webidl-conversions": "^4.0.2", "xml-name-validator": "^3.0.0" } }, "sha512-p10l/ayESzrBMYWRID6xbuCKh2Fp77+sA0doRuGn4tTIMrrZVeqfpKjXHY+oDh3K4nLdPgNwMTVP6Vp4pvqbNg=="],
|
||||
"w3c-xmlserializer": ["w3c-xmlserializer@5.0.0", "", { "dependencies": { "xml-name-validator": "^5.0.0" } }, "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA=="],
|
||||
|
||||
"wait-port": ["wait-port@1.1.0", "", { "dependencies": { "chalk": "^4.1.2", "commander": "^9.3.0", "debug": "^4.3.4" }, "bin": { "wait-port": "bin/wait-port.js" } }, "sha512-3e04qkoN3LxTMLakdqeWth8nih8usyg+sf1Bgdf9wwUkp05iuK1eSY/QpLvscT/+F/gA89+LpUmmgBtesbqI2Q=="],
|
||||
|
||||
@@ -3624,13 +3666,13 @@
|
||||
|
||||
"webdriverio": ["webdriverio@9.12.0", "", { "dependencies": { "@types/node": "^20.11.30", "@types/sinonjs__fake-timers": "^8.1.5", "@wdio/config": "9.11.0", "@wdio/logger": "9.4.4", "@wdio/protocols": "9.7.0", "@wdio/repl": "9.4.4", "@wdio/types": "9.10.1", "@wdio/utils": "9.11.0", "archiver": "^7.0.1", "aria-query": "^5.3.0", "cheerio": "^1.0.0-rc.12", "css-shorthand-properties": "^1.1.1", "css-value": "^0.0.1", "grapheme-splitter": "^1.0.4", "htmlfy": "^0.6.0", "is-plain-obj": "^4.1.0", "jszip": "^3.10.1", "lodash.clonedeep": "^4.5.0", "lodash.zip": "^4.2.0", "query-selector-shadow-dom": "^1.0.1", "resq": "^1.11.0", "rgb2hex": "0.2.5", "serialize-error": "^11.0.3", "urlpattern-polyfill": "^10.0.0", "webdriver": "9.11.0" }, "peerDependencies": { "puppeteer-core": "^22.3.0" }, "optionalPeers": ["puppeteer-core"] }, "sha512-QQ+HFL1POqQwOeyb3cpu+PfJw73JXGzqKUH/tMTK9+pG3Dd3AfeXJIKJw0RJBPjEgv/D2Di8u/wk41Yf5Dce6g=="],
|
||||
|
||||
"webidl-conversions": ["webidl-conversions@4.0.2", "", {}, "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg=="],
|
||||
"webidl-conversions": ["webidl-conversions@7.0.0", "", {}, "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g=="],
|
||||
|
||||
"whatwg-encoding": ["whatwg-encoding@1.0.5", "", { "dependencies": { "iconv-lite": "0.4.24" } }, "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw=="],
|
||||
"whatwg-encoding": ["whatwg-encoding@3.1.1", "", { "dependencies": { "iconv-lite": "0.6.3" } }, "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ=="],
|
||||
|
||||
"whatwg-mimetype": ["whatwg-mimetype@2.3.0", "", {}, "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g=="],
|
||||
"whatwg-mimetype": ["whatwg-mimetype@4.0.0", "", {}, "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg=="],
|
||||
|
||||
"whatwg-url": ["whatwg-url@7.1.0", "", { "dependencies": { "lodash.sortby": "^4.7.0", "tr46": "^1.0.1", "webidl-conversions": "^4.0.2" } }, "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg=="],
|
||||
"whatwg-url": ["whatwg-url@14.2.0", "", { "dependencies": { "tr46": "^5.1.0", "webidl-conversions": "^7.0.0" } }, "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw=="],
|
||||
|
||||
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
|
||||
|
||||
@@ -3670,7 +3712,7 @@
|
||||
|
||||
"ws": ["ws@8.18.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw=="],
|
||||
|
||||
"xml-name-validator": ["xml-name-validator@3.0.0", "", {}, "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw=="],
|
||||
"xml-name-validator": ["xml-name-validator@5.0.0", "", {}, "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg=="],
|
||||
|
||||
"xmlchars": ["xmlchars@2.2.0", "", {}, "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="],
|
||||
|
||||
@@ -3702,6 +3744,8 @@
|
||||
|
||||
"@ampproject/remapping/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="],
|
||||
|
||||
"@asamuzakjp/css-color/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
|
||||
|
||||
"@aws-crypto/crc32/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
|
||||
"@aws-crypto/crc32c/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
@@ -3840,10 +3884,14 @@
|
||||
|
||||
"@jest/core/rimraf": ["rimraf@3.0.2", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "bin.js" } }, "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA=="],
|
||||
|
||||
"@jest/reporters/@bcoe/v8-coverage": ["@bcoe/v8-coverage@0.2.3", "", {}, "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw=="],
|
||||
|
||||
"@jest/reporters/chalk": ["chalk@3.0.0", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg=="],
|
||||
|
||||
"@jest/reporters/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=="],
|
||||
|
||||
"@jest/reporters/istanbul-lib-source-maps": ["istanbul-lib-source-maps@4.0.1", "", { "dependencies": { "debug": "^4.1.1", "istanbul-lib-coverage": "^3.0.0", "source-map": "^0.6.1" } }, "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw=="],
|
||||
|
||||
"@jest/reporters/jest-worker": ["jest-worker@25.5.0", "", { "dependencies": { "merge-stream": "^2.0.0", "supports-color": "^7.0.0" } }, "sha512-/dsSmUkIy5EBGfv/IjjqmFxrNAUpBERfGs1oHROyD7yxjG/w+t0GOJDX8O1k32ySmd7+a5IhnJU2qQFcJ4n1vw=="],
|
||||
|
||||
"@jest/reporters/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
|
||||
@@ -4010,8 +4058,12 @@
|
||||
|
||||
"@testing-library/dom/aria-query": ["aria-query@5.3.0", "", { "dependencies": { "dequal": "^2.0.3" } }, "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A=="],
|
||||
|
||||
"@testing-library/dom/dom-accessibility-api": ["dom-accessibility-api@0.5.16", "", {}, "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg=="],
|
||||
|
||||
"@testing-library/dom/pretty-format": ["pretty-format@27.5.1", "", { "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", "react-is": "^17.0.1" } }, "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ=="],
|
||||
|
||||
"@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.7", "", { "dependencies": { "@types/node": "*", "@types/ws": "*" } }, "sha512-P4hHhk7kjF99acXqKvltyuMQ2kf/rzIw3ylEDpCxDS9Xa0X0Yp/gJu/vDCucmWpiur5qJ0lwB2bWzOXa2GlHqA=="],
|
||||
|
||||
"@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=="],
|
||||
@@ -4064,6 +4116,8 @@
|
||||
|
||||
"@vitest/browser/ws": ["ws@8.18.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w=="],
|
||||
|
||||
"@vitest/coverage-v8/vitest": ["vitest@3.0.9", "", { "dependencies": { "@vitest/expect": "3.0.9", "@vitest/mocker": "3.0.9", "@vitest/pretty-format": "^3.0.9", "@vitest/runner": "3.0.9", "@vitest/snapshot": "3.0.9", "@vitest/spy": "3.0.9", "@vitest/utils": "3.0.9", "chai": "^5.2.0", "debug": "^4.4.0", "expect-type": "^1.1.0", "magic-string": "^0.30.17", "pathe": "^2.0.3", "std-env": "^3.8.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", "tinypool": "^1.0.2", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0", "vite-node": "3.0.9", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "@vitest/browser": "3.0.9", "@vitest/ui": "3.0.9", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-BbcFDqNyBlfSpATmTtXOAOj71RNKDDvjBM/uPfnxxVGrG+FSH2RQIwgeEngTaTkuU/h0ScFvf+tRcKfYXzBybQ=="],
|
||||
|
||||
"@vitest/mocker/estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="],
|
||||
|
||||
"@wdio/logger/chalk": ["chalk@5.4.1", "", {}, "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w=="],
|
||||
@@ -4094,6 +4148,8 @@
|
||||
|
||||
"babel-plugin-istanbul/istanbul-lib-instrument": ["istanbul-lib-instrument@5.2.1", "", { "dependencies": { "@babel/core": "^7.12.3", "@babel/parser": "^7.14.7", "@istanbuljs/schema": "^0.1.2", "istanbul-lib-coverage": "^3.2.0", "semver": "^6.3.0" } }, "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg=="],
|
||||
|
||||
"babel-plugin-istanbul/test-exclude": ["test-exclude@6.0.0", "", { "dependencies": { "@istanbuljs/schema": "^0.1.2", "glob": "^7.1.4", "minimatch": "^3.0.4" } }, "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w=="],
|
||||
|
||||
"babel-plugin-polyfill-corejs2/@babel/helper-define-polyfill-provider": ["@babel/helper-define-polyfill-provider@0.6.3", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.22.6", "@babel/helper-plugin-utils": "^7.22.5", "debug": "^4.1.1", "lodash.debounce": "^4.0.8", "resolve": "^1.14.2" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-HK7Bi+Hj6H+VTHA3ZvBis7V/6hu9QuTrnMXNybfUf2iiuU/N97I8VjB+KbhFF8Rld/Lx5MzoCwPCpPjfK+n8Cg=="],
|
||||
|
||||
"babel-plugin-polyfill-corejs2/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
||||
@@ -4104,24 +4160,24 @@
|
||||
|
||||
"base/pascalcase": ["pascalcase@0.1.1", "", {}, "sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw=="],
|
||||
|
||||
"bknd/vitest": ["vitest@3.0.9", "", { "dependencies": { "@vitest/expect": "3.0.9", "@vitest/mocker": "3.0.9", "@vitest/pretty-format": "^3.0.9", "@vitest/runner": "3.0.9", "@vitest/snapshot": "3.0.9", "@vitest/spy": "3.0.9", "@vitest/utils": "3.0.9", "chai": "^5.2.0", "debug": "^4.4.0", "expect-type": "^1.1.0", "magic-string": "^0.30.17", "pathe": "^2.0.3", "std-env": "^3.8.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", "tinypool": "^1.0.2", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0", "vite-node": "3.0.9", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "@vitest/browser": "3.0.9", "@vitest/ui": "3.0.9", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-BbcFDqNyBlfSpATmTtXOAOj71RNKDDvjBM/uPfnxxVGrG+FSH2RQIwgeEngTaTkuU/h0ScFvf+tRcKfYXzBybQ=="],
|
||||
|
||||
"bknd/wrangler": ["wrangler@4.4.1", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.4.0", "@cloudflare/unenv-preset": "2.3.0", "blake3-wasm": "2.1.5", "esbuild": "0.24.2", "miniflare": "4.20250321.0", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.15", "workerd": "1.20250321.0" }, "optionalDependencies": { "fsevents": "~2.3.2", "sharp": "^0.33.5" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20250321.0" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-EFwr7hiVeAmPOuOGQ7HFfeaLKLxEXQMJ86kyn6RFB8pGjMEUtvZMsVa9cPubKkKgNi3WcDEFeFLalclGyq+tGA=="],
|
||||
|
||||
"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=="],
|
||||
|
||||
"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=="],
|
||||
|
||||
"browser-resolve/resolve": ["resolve@1.1.7", "", {}, "sha512-9znBF0vBcaSN3W2j7wKvdERPwqTxSpCq+if5C0WoTCyV9n24rua28jeuQ2pL/HOf+yUe/Mef+H/5p60K0Id3bg=="],
|
||||
|
||||
"browserify-zlib/pako": ["pako@0.2.9", "", {}, "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA=="],
|
||||
|
||||
"camelcase-keys/camelcase": ["camelcase@5.3.1", "", {}, "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="],
|
||||
|
||||
"cheerio/parse5": ["parse5@7.2.1", "", { "dependencies": { "entities": "^4.5.0" } }, "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ=="],
|
||||
|
||||
"cheerio/undici": ["undici@6.21.1", "", {}, "sha512-q/1rj5D0/zayJB2FraXdaWxbhWiNKDvu8naDT2dl1yTlvJp4BLtOcp2a5BvgGNQpYYJzau7tf1WgKv3b+7mqpQ=="],
|
||||
|
||||
"cheerio/whatwg-mimetype": ["whatwg-mimetype@4.0.0", "", {}, "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg=="],
|
||||
|
||||
"class-utils/define-property": ["define-property@0.2.5", "", { "dependencies": { "is-descriptor": "^0.1.0" } }, "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA=="],
|
||||
|
||||
"cliui/wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
|
||||
@@ -4130,28 +4186,22 @@
|
||||
|
||||
"compression/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
|
||||
|
||||
"cssstyle/cssom": ["cssom@0.3.8", "", {}, "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg=="],
|
||||
|
||||
"decamelize-keys/map-obj": ["map-obj@1.0.1", "", {}, "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg=="],
|
||||
|
||||
"degenerator/escodegen": ["escodegen@2.1.0", "", { "dependencies": { "esprima": "^4.0.1", "estraverse": "^5.2.0", "esutils": "^2.0.2" }, "optionalDependencies": { "source-map": "~0.6.1" }, "bin": { "esgenerate": "bin/esgenerate.js", "escodegen": "bin/escodegen.js" } }, "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w=="],
|
||||
|
||||
"domexception/webidl-conversions": ["webidl-conversions@4.0.2", "", {}, "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg=="],
|
||||
|
||||
"duplexify/readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
|
||||
|
||||
"edgedriver/decamelize": ["decamelize@6.0.0", "", {}, "sha512-Fv96DCsdOgB6mdGl67MT5JaTNKRzrzill5OH5s8bjYJXVlcXyPYGyPsUkWyGV5p1TXI5esYIYMMeDJL0hEIwaA=="],
|
||||
|
||||
"edgedriver/fast-xml-parser": ["fast-xml-parser@4.5.3", "", { "dependencies": { "strnum": "^1.1.1" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig=="],
|
||||
|
||||
"edgedriver/https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="],
|
||||
|
||||
"edgedriver/node-fetch": ["node-fetch@3.3.2", "", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="],
|
||||
|
||||
"edgedriver/which": ["which@5.0.0", "", { "dependencies": { "isexe": "^3.1.1" }, "bin": { "node-which": "bin/which.js" } }, "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ=="],
|
||||
|
||||
"encoding-sniffer/iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="],
|
||||
|
||||
"encoding-sniffer/whatwg-encoding": ["whatwg-encoding@3.1.1", "", { "dependencies": { "iconv-lite": "0.6.3" } }, "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ=="],
|
||||
|
||||
"engine.io-client/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="],
|
||||
|
||||
"engine.io-client/ws": ["ws@8.17.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ=="],
|
||||
@@ -4224,6 +4274,8 @@
|
||||
|
||||
"express/path-to-regexp": ["path-to-regexp@0.1.10", "", {}, "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w=="],
|
||||
|
||||
"external-editor/iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="],
|
||||
|
||||
"extglob/define-property": ["define-property@1.0.0", "", { "dependencies": { "is-descriptor": "^1.0.0" } }, "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA=="],
|
||||
|
||||
"extglob/extend-shallow": ["extend-shallow@2.0.1", "", { "dependencies": { "is-extendable": "^0.1.0" } }, "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug=="],
|
||||
@@ -4232,6 +4284,8 @@
|
||||
|
||||
"finalhandler/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
|
||||
|
||||
"find-cache-dir/make-dir": ["make-dir@3.1.0", "", { "dependencies": { "semver": "^6.0.0" } }, "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw=="],
|
||||
|
||||
"flat-cache/flatted": ["flatted@2.0.2", "", {}, "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA=="],
|
||||
|
||||
"flat-cache/rimraf": ["rimraf@2.6.3", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "./bin.js" } }, "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA=="],
|
||||
@@ -4242,8 +4296,6 @@
|
||||
|
||||
"geckodriver/decamelize": ["decamelize@6.0.0", "", {}, "sha512-Fv96DCsdOgB6mdGl67MT5JaTNKRzrzill5OH5s8bjYJXVlcXyPYGyPsUkWyGV5p1TXI5esYIYMMeDJL0hEIwaA=="],
|
||||
|
||||
"geckodriver/https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="],
|
||||
|
||||
"geckodriver/node-fetch": ["node-fetch@3.3.2", "", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="],
|
||||
|
||||
"geckodriver/which": ["which@5.0.0", "", { "dependencies": { "isexe": "^3.1.1" }, "bin": { "node-which": "bin/which.js" } }, "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ=="],
|
||||
@@ -4266,8 +4318,6 @@
|
||||
|
||||
"hosted-git-info/lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="],
|
||||
|
||||
"http-proxy-agent/agent-base": ["agent-base@7.1.3", "", {}, "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw=="],
|
||||
|
||||
"import-fresh/resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
|
||||
|
||||
"ip-address/jsbn": ["jsbn@1.1.0", "", {}, "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A=="],
|
||||
@@ -4278,9 +4328,7 @@
|
||||
|
||||
"istanbul-lib-instrument/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
||||
|
||||
"istanbul-lib-report/make-dir": ["make-dir@4.0.0", "", { "dependencies": { "semver": "^7.5.3" } }, "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw=="],
|
||||
|
||||
"istanbul-lib-source-maps/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
|
||||
"istanbul-lib-source-maps/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="],
|
||||
|
||||
"jest-changed-files/execa": ["execa@3.4.0", "", { "dependencies": { "cross-spawn": "^7.0.0", "get-stream": "^5.0.0", "human-signals": "^1.1.1", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.0", "onetime": "^5.1.0", "p-finally": "^2.0.0", "signal-exit": "^3.0.2", "strip-final-newline": "^2.0.0" } }, "sha512-r9vdGQk4bmCuK1yKQu1KTwcT2zwfWdbdaXfCtAh+5nU/4fSX+JAb7vZGvI5naJrQlvONrEB20jeruESI69530g=="],
|
||||
|
||||
@@ -4302,6 +4350,8 @@
|
||||
|
||||
"jest-each/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=="],
|
||||
|
||||
"jest-environment-jsdom/jsdom": ["jsdom@15.2.1", "", { "dependencies": { "abab": "^2.0.0", "acorn": "^7.1.0", "acorn-globals": "^4.3.2", "array-equal": "^1.0.0", "cssom": "^0.4.1", "cssstyle": "^2.0.0", "data-urls": "^1.1.0", "domexception": "^1.0.1", "escodegen": "^1.11.1", "html-encoding-sniffer": "^1.0.2", "nwsapi": "^2.2.0", "parse5": "5.1.0", "pn": "^1.1.0", "request": "^2.88.0", "request-promise-native": "^1.0.7", "saxes": "^3.1.9", "symbol-tree": "^3.2.2", "tough-cookie": "^3.0.1", "w3c-hr-time": "^1.0.1", "w3c-xmlserializer": "^1.1.2", "webidl-conversions": "^4.0.2", "whatwg-encoding": "^1.0.5", "whatwg-mimetype": "^2.3.0", "whatwg-url": "^7.0.0", "ws": "^7.0.0", "xml-name-validator": "^3.0.0" }, "peerDependencies": { "canvas": "^2.5.0" }, "optionalPeers": ["canvas"] }, "sha512-fAl1W0/7T2G5vURSyxBzrJ1LSdQn6Tr5UX/xD4PXDx/PDgwygedfW6El/KIj3xJ7FU61TTYnc/l/B7P49Eqt6g=="],
|
||||
|
||||
"jest-environment-node/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
||||
|
||||
"jest-haste-map/jest-worker": ["jest-worker@25.5.0", "", { "dependencies": { "merge-stream": "^2.0.0", "supports-color": "^7.0.0" } }, "sha512-/dsSmUkIy5EBGfv/IjjqmFxrNAUpBERfGs1oHROyD7yxjG/w+t0GOJDX8O1k32ySmd7+a5IhnJU2qQFcJ4n1vw=="],
|
||||
@@ -4344,12 +4394,16 @@
|
||||
|
||||
"jest-snapshot/jest-get-type": ["jest-get-type@25.2.6", "", {}, "sha512-DxjtyzOHjObRM+sM1knti6or+eOgcGU4xVSb2HNP1TqO4ahsT+rqZg+nyqHWJSvWgKC5cG3QjGFBqxLghiF/Ig=="],
|
||||
|
||||
"jest-snapshot/make-dir": ["make-dir@3.1.0", "", { "dependencies": { "semver": "^6.0.0" } }, "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw=="],
|
||||
|
||||
"jest-snapshot/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=="],
|
||||
|
||||
"jest-snapshot/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
||||
|
||||
"jest-util/chalk": ["chalk@3.0.0", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg=="],
|
||||
|
||||
"jest-util/make-dir": ["make-dir@3.1.0", "", { "dependencies": { "semver": "^6.0.0" } }, "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw=="],
|
||||
|
||||
"jest-validate/camelcase": ["camelcase@5.3.1", "", {}, "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="],
|
||||
|
||||
"jest-validate/chalk": ["chalk@3.0.0", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg=="],
|
||||
@@ -4364,11 +4418,7 @@
|
||||
|
||||
"jest-worker/supports-color": ["supports-color@6.1.0", "", { "dependencies": { "has-flag": "^3.0.0" } }, "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ=="],
|
||||
|
||||
"jsdom/acorn": ["acorn@7.4.1", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A=="],
|
||||
|
||||
"jsdom/tough-cookie": ["tough-cookie@3.0.1", "", { "dependencies": { "ip-regex": "^2.1.0", "psl": "^1.1.28", "punycode": "^2.1.1" } }, "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg=="],
|
||||
|
||||
"jsdom/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="],
|
||||
"jsdom/ws": ["ws@8.18.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w=="],
|
||||
|
||||
"jszip/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=="],
|
||||
|
||||
@@ -4386,8 +4436,6 @@
|
||||
|
||||
"lower-case/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
|
||||
"make-dir/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
||||
|
||||
"meow/type-fest": ["type-fest@0.18.1", "", {}, "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw=="],
|
||||
|
||||
"micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||
@@ -4420,14 +4468,6 @@
|
||||
|
||||
"ora/log-symbols": ["log-symbols@3.0.0", "", { "dependencies": { "chalk": "^2.4.2" } }, "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ=="],
|
||||
|
||||
"pac-proxy-agent/agent-base": ["agent-base@7.1.3", "", {}, "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw=="],
|
||||
|
||||
"pac-proxy-agent/https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="],
|
||||
|
||||
"parse5-htmlparser2-tree-adapter/parse5": ["parse5@7.2.1", "", { "dependencies": { "entities": "^4.5.0" } }, "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ=="],
|
||||
|
||||
"parse5-parser-stream/parse5": ["parse5@7.2.1", "", { "dependencies": { "entities": "^4.5.0" } }, "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ=="],
|
||||
|
||||
"pascal-case/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
|
||||
"path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
|
||||
@@ -4438,6 +4478,8 @@
|
||||
|
||||
"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=="],
|
||||
|
||||
"pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="],
|
||||
|
||||
"progress-estimator/chalk": ["chalk@2.4.2", "", { "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" } }, "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="],
|
||||
@@ -4448,14 +4490,12 @@
|
||||
|
||||
"prop-types/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
|
||||
|
||||
"proxy-agent/agent-base": ["agent-base@7.1.3", "", {}, "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw=="],
|
||||
|
||||
"proxy-agent/https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="],
|
||||
|
||||
"pumpify/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=="],
|
||||
|
||||
"pumpify/pump": ["pump@2.0.1", "", { "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA=="],
|
||||
|
||||
"raw-body/iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="],
|
||||
|
||||
"react-remove-scroll/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
|
||||
"react-remove-scroll-bar/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
@@ -4552,7 +4592,7 @@
|
||||
|
||||
"socket.io-parser/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="],
|
||||
|
||||
"socks-proxy-agent/agent-base": ["agent-base@7.1.3", "", {}, "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw=="],
|
||||
"source-map/whatwg-url": ["whatwg-url@7.1.0", "", { "dependencies": { "lodash.sortby": "^4.7.0", "tr46": "^1.0.1", "webidl-conversions": "^4.0.2" } }, "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg=="],
|
||||
|
||||
"source-map-support/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
|
||||
|
||||
@@ -4576,10 +4616,6 @@
|
||||
|
||||
"terser/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
|
||||
|
||||
"test-exclude/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=="],
|
||||
|
||||
"test-exclude/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
|
||||
|
||||
"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=="],
|
||||
|
||||
"to-object-path/kind-of": ["kind-of@3.2.2", "", { "dependencies": { "is-buffer": "^1.1.5" } }, "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ=="],
|
||||
@@ -4620,6 +4656,8 @@
|
||||
|
||||
"verdaccio-audit/express": ["express@4.21.0", "", { "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", "cookie": "0.6.0", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "finalhandler": "1.3.1", "fresh": "0.5.2", "http-errors": "2.0.0", "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "0.1.10", "proxy-addr": "~2.0.7", "qs": "6.13.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "0.19.0", "serve-static": "1.16.2", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" } }, "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng=="],
|
||||
|
||||
"verdaccio-audit/https-proxy-agent": ["https-proxy-agent@5.0.1", "", { "dependencies": { "agent-base": "6", "debug": "4" } }, "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA=="],
|
||||
|
||||
"verdaccio-htpasswd/@verdaccio/file-locking": ["@verdaccio/file-locking@13.0.0-next-8.0", "", { "dependencies": { "lockfile": "1.0.4" } }, "sha512-28XRwpKiE3Z6KsnwE7o8dEM+zGWOT+Vef7RVJyUlG176JVDbGGip3HfCmFioE1a9BklLyGEFTu6D69BzfbRkzA=="],
|
||||
|
||||
"verdaccio-htpasswd/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="],
|
||||
@@ -4768,8 +4806,6 @@
|
||||
|
||||
"edgedriver/fast-xml-parser/strnum": ["strnum@1.1.2", "", {}, "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA=="],
|
||||
|
||||
"edgedriver/https-proxy-agent/agent-base": ["agent-base@7.1.3", "", {}, "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw=="],
|
||||
|
||||
"edgedriver/which/isexe": ["isexe@3.1.1", "", {}, "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ=="],
|
||||
|
||||
"eslint-plugin-import/minimatch/brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="],
|
||||
@@ -4814,9 +4850,9 @@
|
||||
|
||||
"finalhandler/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
||||
|
||||
"flat-cache/rimraf/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=="],
|
||||
"find-cache-dir/make-dir/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
||||
|
||||
"geckodriver/https-proxy-agent/agent-base": ["agent-base@7.1.3", "", {}, "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw=="],
|
||||
"flat-cache/rimraf/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=="],
|
||||
|
||||
"geckodriver/which/isexe": ["isexe@3.1.1", "", {}, "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ=="],
|
||||
|
||||
@@ -4836,6 +4872,34 @@
|
||||
|
||||
"jest-each/pretty-format/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
|
||||
|
||||
"jest-environment-jsdom/jsdom/acorn": ["acorn@7.4.1", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A=="],
|
||||
|
||||
"jest-environment-jsdom/jsdom/cssstyle": ["cssstyle@2.3.0", "", { "dependencies": { "cssom": "~0.3.6" } }, "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A=="],
|
||||
|
||||
"jest-environment-jsdom/jsdom/data-urls": ["data-urls@1.1.0", "", { "dependencies": { "abab": "^2.0.0", "whatwg-mimetype": "^2.2.0", "whatwg-url": "^7.0.0" } }, "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ=="],
|
||||
|
||||
"jest-environment-jsdom/jsdom/html-encoding-sniffer": ["html-encoding-sniffer@1.0.2", "", { "dependencies": { "whatwg-encoding": "^1.0.1" } }, "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw=="],
|
||||
|
||||
"jest-environment-jsdom/jsdom/parse5": ["parse5@5.1.0", "", {}, "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ=="],
|
||||
|
||||
"jest-environment-jsdom/jsdom/saxes": ["saxes@3.1.11", "", { "dependencies": { "xmlchars": "^2.1.1" } }, "sha512-Ydydq3zC+WYDJK1+gRxRapLIED9PWeSuuS41wqyoRmzvhhh9nc+QQrVMKJYzJFULazeGhzSV0QleN2wD3boh2g=="],
|
||||
|
||||
"jest-environment-jsdom/jsdom/tough-cookie": ["tough-cookie@3.0.1", "", { "dependencies": { "ip-regex": "^2.1.0", "psl": "^1.1.28", "punycode": "^2.1.1" } }, "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg=="],
|
||||
|
||||
"jest-environment-jsdom/jsdom/w3c-xmlserializer": ["w3c-xmlserializer@1.1.2", "", { "dependencies": { "domexception": "^1.0.1", "webidl-conversions": "^4.0.2", "xml-name-validator": "^3.0.0" } }, "sha512-p10l/ayESzrBMYWRID6xbuCKh2Fp77+sA0doRuGn4tTIMrrZVeqfpKjXHY+oDh3K4nLdPgNwMTVP6Vp4pvqbNg=="],
|
||||
|
||||
"jest-environment-jsdom/jsdom/webidl-conversions": ["webidl-conversions@4.0.2", "", {}, "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg=="],
|
||||
|
||||
"jest-environment-jsdom/jsdom/whatwg-encoding": ["whatwg-encoding@1.0.5", "", { "dependencies": { "iconv-lite": "0.4.24" } }, "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw=="],
|
||||
|
||||
"jest-environment-jsdom/jsdom/whatwg-mimetype": ["whatwg-mimetype@2.3.0", "", {}, "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g=="],
|
||||
|
||||
"jest-environment-jsdom/jsdom/whatwg-url": ["whatwg-url@7.1.0", "", { "dependencies": { "lodash.sortby": "^4.7.0", "tr46": "^1.0.1", "webidl-conversions": "^4.0.2" } }, "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg=="],
|
||||
|
||||
"jest-environment-jsdom/jsdom/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="],
|
||||
|
||||
"jest-environment-jsdom/jsdom/xml-name-validator": ["xml-name-validator@3.0.0", "", {}, "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw=="],
|
||||
|
||||
"jest-jasmine2/pretty-format/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
|
||||
|
||||
"jest-leak-detector/pretty-format/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
|
||||
@@ -4856,6 +4920,8 @@
|
||||
|
||||
"jest-snapshot/pretty-format/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
|
||||
|
||||
"jest-util/make-dir/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
||||
|
||||
"jest-validate/pretty-format/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
|
||||
|
||||
"jest-worker/supports-color/has-flag": ["has-flag@3.0.0", "", {}, "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="],
|
||||
@@ -4938,6 +5004,10 @@
|
||||
|
||||
"snapdragon/extend-shallow/is-extendable": ["is-extendable@0.1.1", "", {}, "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw=="],
|
||||
|
||||
"source-map/whatwg-url/tr46": ["tr46@1.0.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA=="],
|
||||
|
||||
"source-map/whatwg-url/webidl-conversions": ["webidl-conversions@4.0.2", "", {}, "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg=="],
|
||||
|
||||
"static-extend/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=="],
|
||||
|
||||
"string-length/strip-ansi/ansi-regex": ["ansi-regex@4.1.1", "", {}, "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g=="],
|
||||
@@ -4950,8 +5020,6 @@
|
||||
|
||||
"table/string-width/strip-ansi": ["strip-ansi@5.2.0", "", { "dependencies": { "ansi-regex": "^4.1.0" } }, "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA=="],
|
||||
|
||||
"test-exclude/minimatch/brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="],
|
||||
|
||||
"through2/readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
|
||||
|
||||
"through2/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="],
|
||||
@@ -5068,6 +5136,8 @@
|
||||
|
||||
"verdaccio-audit/express/path-to-regexp": ["path-to-regexp@0.1.10", "", {}, "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w=="],
|
||||
|
||||
"verdaccio-audit/https-proxy-agent/agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="],
|
||||
|
||||
"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=="],
|
||||
@@ -5306,6 +5376,12 @@
|
||||
|
||||
"jest-config/glob/minimatch/brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="],
|
||||
|
||||
"jest-environment-jsdom/jsdom/cssstyle/cssom": ["cssom@0.3.8", "", {}, "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg=="],
|
||||
|
||||
"jest-environment-jsdom/jsdom/whatwg-encoding/iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="],
|
||||
|
||||
"jest-environment-jsdom/jsdom/whatwg-url/tr46": ["tr46@1.0.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA=="],
|
||||
|
||||
"jest-runtime/glob/minimatch/brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="],
|
||||
|
||||
"jest-runtime/yargs/cliui/wrap-ansi": ["wrap-ansi@6.2.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="],
|
||||
|
||||
@@ -22,4 +22,7 @@ pnpm-debug.log*
|
||||
|
||||
# jetbrains setting folder
|
||||
.idea/
|
||||
*.db
|
||||
*.db
|
||||
|
||||
# Public uploads
|
||||
/public/uploads/*
|
||||
|
||||
@@ -42,7 +42,7 @@ export default {
|
||||
media: {
|
||||
enabled: true,
|
||||
adapter: local({
|
||||
path: "./public",
|
||||
path: "./public/uploads",
|
||||
}),
|
||||
},
|
||||
},
|
||||
|
||||
@@ -173,3 +173,6 @@ dist
|
||||
|
||||
# Finder (MacOS) folder config
|
||||
.DS_Store
|
||||
|
||||
*.db
|
||||
uploads/*
|
||||
@@ -8,6 +8,15 @@ const config: BunBkndConfig = {
|
||||
connection: {
|
||||
url: "file:data.db",
|
||||
},
|
||||
initialConfig: {
|
||||
media: {
|
||||
enabled: true,
|
||||
adapter: {
|
||||
type: "local",
|
||||
config: { path: "./uploads" },
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
serve(config);
|
||||
|
||||
@@ -39,3 +39,6 @@ yarn-error.log*
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
|
||||
# Public uploads
|
||||
/public/uploads/*
|
||||
|
||||
@@ -51,7 +51,7 @@ export default {
|
||||
media: {
|
||||
enabled: true,
|
||||
adapter: local({
|
||||
path: "./public",
|
||||
path: "./public/uploads",
|
||||
}),
|
||||
},
|
||||
},
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
"use client";
|
||||
|
||||
import { type BkndAdminProps, Admin } from "bknd/ui";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
export function AdminComponent(props: BkndAdminProps) {
|
||||
const [ready, setReady] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window !== "undefined") setReady(true);
|
||||
}, []);
|
||||
if (!ready) return null;
|
||||
|
||||
return <Admin {...props} />;
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
import { Admin } from "bknd/ui";
|
||||
import "bknd/dist/styles.css";
|
||||
import { AdminComponent } from "./Admin";
|
||||
import { getApi } from "@/bknd";
|
||||
import "bknd/dist/styles.css";
|
||||
|
||||
export default async function AdminPage() {
|
||||
const api = await getApi({ verify: true });
|
||||
|
||||
return (
|
||||
<Admin
|
||||
<AdminComponent
|
||||
withProvider={{ user: api.getUser() }}
|
||||
config={{
|
||||
basepath: "/admin",
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
*.db
|
||||
uploads/*
|
||||
+13
-2
@@ -7,8 +7,19 @@ import { serve } from "bknd/adapter/node";
|
||||
/** @type {import("bknd/adapter/node").NodeBkndConfig} */
|
||||
const config = {
|
||||
connection: {
|
||||
url: "file:data.db"
|
||||
}
|
||||
url: "file:data.db",
|
||||
},
|
||||
initialConfig: {
|
||||
media: {
|
||||
enabled: true,
|
||||
adapter: {
|
||||
type: "local",
|
||||
config: {
|
||||
path: "./uploads",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
serve(config);
|
||||
|
||||
@@ -7,3 +7,6 @@
|
||||
# React Router
|
||||
/.react-router/
|
||||
/build/
|
||||
|
||||
# Public uploads
|
||||
/public/uploads/*
|
||||
|
||||
@@ -41,7 +41,7 @@ export default {
|
||||
media: {
|
||||
enabled: true,
|
||||
adapter: local({
|
||||
path: "./public",
|
||||
path: "./public/uploads",
|
||||
}),
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user