diff --git a/dprint.json b/dprint.json
deleted file mode 100644
index 3911cdd..0000000
--- a/dprint.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
- "incremental": true,
- "typescript": {
- "indentWidth": 2
- },
- "json": {
- },
- "markdown": {
- },
- "includes": ["**/*.{ts,tsx,js,jsx,cjs,mjs,json,md}"],
- "excludes": [
- "**/node_modules",
- "**/*-lock.json",
- "**/dist",
- "build/",
- "app/"
- ],
- "plugins": [
- "https://plugins.dprint.dev/typescript-0.84.4.wasm",
- "https://plugins.dprint.dev/json-0.17.2.wasm",
- "https://plugins.dprint.dev/markdown-0.15.2.wasm"
- ]
-}
diff --git a/packages/dbtype/types.ts b/packages/dbtype/types.ts
index 7a7beaf..3ea5ac5 100644
--- a/packages/dbtype/types.ts
+++ b/packages/dbtype/types.ts
@@ -34,7 +34,7 @@ export interface SchemaMigration {
export interface Tags {
description: string | null;
- name: string | null;
+ name: string;
}
export interface Users {
diff --git a/packages/server/app.ts b/packages/server/app.ts
index 687a4c1..baf6dea 100644
--- a/packages/server/app.ts
+++ b/packages/server/app.ts
@@ -1,145 +1,7 @@
-import { app, BrowserWindow, dialog, session } from "electron";
-import { ipcMain } from "electron";
-import { join } from "path";
-import { accessTokenName, getAdminAccessTokenValue, getAdminRefreshTokenValue, refreshTokenName } from "./src/login";
-import { UserAccessor } from "./src/model/mod";
import { create_server } from "./src/server";
-import { get_setting } from "./src/SettingConfig";
-function registerChannel(cntr: UserAccessor) {
- ipcMain.handle("reset_password", async (event, username: string, password: string) => {
- const user = await cntr.findUser(username);
- if (user === undefined) {
- return false;
- }
- user.reset_password(password);
- return true;
- });
-}
-const setting = get_setting();
-if (!setting.cli) {
- let wnd: BrowserWindow | null = null;
-
- const createWindow = async () => {
- wnd = new BrowserWindow({
- width: 800,
- height: 600,
- center: true,
- useContentSize: true,
- webPreferences: {
- preload: join(__dirname, "preload.js"),
- contextIsolation: true,
- },
- });
- await wnd.loadURL(`data:text/html;base64,` + Buffer.from(loading_html).toString("base64"));
- // await wnd.loadURL('../loading.html');
- // set admin cookies.
- await session.defaultSession.cookies.set({
- url: `http://localhost:${setting.port}`,
- name: accessTokenName,
- value: getAdminAccessTokenValue(),
- httpOnly: true,
- secure: false,
- sameSite: "strict",
- });
- await session.defaultSession.cookies.set({
- url: `http://localhost:${setting.port}`,
- name: refreshTokenName,
- value: getAdminRefreshTokenValue(),
- httpOnly: true,
- secure: false,
- sameSite: "strict",
- });
- try {
- const server = await create_server();
- const app = server.start_server();
- registerChannel(server.userController);
- await wnd.loadURL(`http://localhost:${setting.port}`);
- } catch (e) {
- if (e instanceof Error) {
- await dialog.showMessageBox({
- type: "error",
- title: "error!",
- message: e.message,
- });
- } else {
- await dialog.showMessageBox({
- type: "error",
- title: "error!",
- message: String(e),
- });
- }
- }
- wnd.on("closed", () => {
- wnd = null;
- });
- };
-
- const isPrimary = app.requestSingleInstanceLock();
- if (!isPrimary) {
- app.quit(); // exit window
- app.exit();
- }
- app.on("second-instance", () => {
- if (wnd != null) {
- if (wnd.isMinimized()) {
- wnd.restore();
- }
- wnd.focus();
- }
- });
- app.on("ready", (event, info) => {
- createWindow();
- });
-
- app.on("window-all-closed", () => {
- // quit when all windows are closed
- if (process.platform != "darwin") app.quit(); // (except leave MacOS app active until Cmd+Q)
- });
-
- app.on("activate", () => {
- // re-recreate window when dock icon is clicked and no other windows open
- if (wnd == null) createWindow();
- });
-} else {
- (async () => {
- try {
- const server = await create_server();
- server.start_server();
- } catch (error) {
- console.log(error);
- }
- })();
-}
-const loading_html = `
-
-
-loading
-
-
-
-
-
- Loading...
-
-
-`;
+create_server().then((server) => {
+ server.start_server();
+}).catch((err) => {
+ console.error(err);
+});
\ No newline at end of file
diff --git a/packages/server/comic_config.json b/packages/server/comic_config.json
new file mode 100644
index 0000000..e9692d3
--- /dev/null
+++ b/packages/server/comic_config.json
@@ -0,0 +1,3 @@
+{
+ "watch": []
+}
\ No newline at end of file
diff --git a/packages/server/knexfile.js b/packages/server/knexfile.js
deleted file mode 100644
index 76f47c9..0000000
--- a/packages/server/knexfile.js
+++ /dev/null
@@ -1,5 +0,0 @@
-require("ts-node").register();
-const { Knex } = require("./src/config");
-// Update with your config settings.
-
-module.exports = Knex.config;
diff --git a/packages/server/package.json b/packages/server/package.json
index 0215d59..d24bf18 100644
--- a/packages/server/package.json
+++ b/packages/server/package.json
@@ -4,10 +4,8 @@
"description": "",
"main": "build/app.js",
"scripts": {
- "compile": "tsc",
- "compile:watch": "tsc -w",
- "build": "cd src/client && pnpm run build:prod",
- "build:watch": "cd src/client && pnpm run build:watch",
+ "compile": "swc src --out-dir dist",
+ "dev": "nodemon -r @swc-node/register --exec node app.ts",
"start": "node build/app.js"
},
"author": "",
@@ -16,6 +14,7 @@
"@zip.js/zip.js": "^2.7.40",
"better-sqlite3": "^9.4.3",
"chokidar": "^3.6.0",
+ "dotenv": "^16.4.5",
"jsonwebtoken": "^8.5.1",
"koa": "^2.15.2",
"koa-bodyparser": "^4.4.1",
@@ -26,13 +25,18 @@
"tiny-async-pool": "^1.3.0"
},
"devDependencies": {
- "dbtype": "*",
+ "@swc-node/register": "^1.9.0",
+ "@swc/cli": "^0.3.10",
+ "@swc/core": "^1.4.11",
+ "@types/better-sqlite3": "^7.6.9",
"@types/jsonwebtoken": "^8.5.9",
"@types/koa": "^2.15.0",
"@types/koa-bodyparser": "^4.3.12",
"@types/koa-compose": "^3.2.8",
"@types/koa-router": "^7.4.8",
"@types/node": "^14.18.63",
- "@types/tiny-async-pool": "^1.0.5"
+ "@types/tiny-async-pool": "^1.0.5",
+ "dbtype": "workspace:^",
+ "nodemon": "^3.1.0"
}
}
diff --git a/packages/server/src/SettingConfig.ts b/packages/server/src/SettingConfig.ts
index f012079..a59944e 100644
--- a/packages/server/src/SettingConfig.ts
+++ b/packages/server/src/SettingConfig.ts
@@ -1,6 +1,6 @@
-import { randomBytes } from "crypto";
-import { existsSync, readFileSync, writeFileSync } from "fs";
-import { Permission } from "./permission/permission";
+import { randomBytes } from "node:crypto";
+import { existsSync, readFileSync, writeFileSync } from "node:fs";
+import type { Permission } from "./permission/permission";
export interface SettingConfig {
/**
@@ -46,6 +46,7 @@ const default_setting: SettingConfig = {
};
let setting: null | SettingConfig = null;
+// biome-ignore lint/suspicious/noExplicitAny:
const setEmptyToDefault = (target: any, default_table: SettingConfig) => {
let diff_occur = false;
for (const key in default_table) {
@@ -59,7 +60,7 @@ const setEmptyToDefault = (target: any, default_table: SettingConfig) => {
};
export const read_setting_from_file = () => {
- let ret = existsSync("settings.json") ? JSON.parse(readFileSync("settings.json", { encoding: "utf8" })) : {};
+ const ret = existsSync("settings.json") ? JSON.parse(readFileSync("settings.json", { encoding: "utf8" })) : {};
const partial_occur = setEmptyToDefault(ret, default_setting);
if (partial_occur) {
writeFileSync("settings.json", JSON.stringify(ret));
@@ -70,7 +71,7 @@ export function get_setting(): SettingConfig {
if (setting === null) {
setting = read_setting_from_file();
const env = process.env.NODE_ENV;
- if (env !== undefined && env != "production" && env != "development") {
+ if (env !== undefined && env !== "production" && env !== "development") {
throw new Error('process unknown value in NODE_ENV: must be either "development" or "production"');
}
setting.mode = env ?? setting.mode;
diff --git a/packages/server/src/config.ts b/packages/server/src/config.ts
index dcf3463..6807e05 100644
--- a/packages/server/src/config.ts
+++ b/packages/server/src/config.ts
@@ -1,4 +1,4 @@
-import { Knex as k } from "knex";
+import type { Knex as k } from "knex";
export namespace Knex {
export const config: {
diff --git a/packages/server/src/content/comic.ts b/packages/server/src/content/comic.ts
index 1b241c7..e623c36 100644
--- a/packages/server/src/content/comic.ts
+++ b/packages/server/src/content/comic.ts
@@ -1,7 +1,7 @@
-import { extname } from "path";
-import { DocumentBody } from "../model/doc";
+import { extname } from "node:path";
+import type { DocumentBody } from "../model/doc";
import { readAllFromZip, readZip } from "../util/zipwrap";
-import { ContentConstructOption, ContentFile, createDefaultClass, registerContentReferrer } from "./file";
+import { type ContentConstructOption, createDefaultClass, registerContentReferrer } from "./file";
type ComicType = "doujinshi" | "artist cg" | "manga" | "western";
interface ComicDesc {
@@ -51,7 +51,7 @@ export class ComicReferrer extends createDefaultClass("comic") {
tags = tags.concat(this.desc.character?.map((x) => `character:${x}`) ?? []);
tags = tags.concat(this.desc.group?.map((x) => `group:${x}`) ?? []);
tags = tags.concat(this.desc.series?.map((x) => `series:${x}`) ?? []);
- const type = this.desc.type instanceof Array ? this.desc.type[0] : this.desc.type;
+ const type = Array.isArray(this.desc.type) ? this.desc.type[0] : this.desc.type;
tags.push(`type:${type}`);
return {
...basebody,
diff --git a/packages/server/src/content/file.ts b/packages/server/src/content/file.ts
index 350248e..681a348 100644
--- a/packages/server/src/content/file.ts
+++ b/packages/server/src/content/file.ts
@@ -1,10 +1,9 @@
-import { createHash } from "crypto";
-import { promises, Stats } from "fs";
+import { createHash } from "node:crypto";
+import { promises, type Stats } from "node:fs";
import { Context, DefaultContext, DefaultState, Middleware, Next } from "koa";
import Router from "koa-router";
-import { extname } from "path";
-import path from "path";
-import { DocumentBody } from "../model/mod";
+import path, { extname } from "node:path";
+import type { DocumentBody } from "../model/mod";
/**
* content file or directory referrer
*/
@@ -24,13 +23,17 @@ type ContentFileConstructor = (new (
content_type: string;
};
export const createDefaultClass = (type: string): ContentFileConstructor => {
- let cons = class implements ContentFile {
+ const cons = class implements ContentFile {
readonly path: string;
// type = type;
static content_type = type;
protected hash: string | undefined;
protected stat: Stats | undefined;
+ protected getStat(){
+ return this.stat;
+ }
+
constructor(path: string, option?: ContentConstructOption) {
this.path = path;
this.hash = option?.hash;
@@ -67,14 +70,17 @@ export const createDefaultClass = (type: string): ContentFileConstructor => {
return this.hash;
}
async getMtime(): Promise {
- if (this.stat !== undefined) return this.stat.mtimeMs;
+ const oldStat = this.getStat();
+ if (oldStat !== undefined) return oldStat.mtimeMs;
await this.getHash();
- return this.stat!.mtimeMs;
+ const newStat = this.getStat();
+ if (newStat === undefined) throw new Error("stat is undefined");
+ return newStat.mtimeMs;
}
};
return cons;
};
-let ContstructorTable: { [k: string]: ContentFileConstructor } = {};
+const ContstructorTable: { [k: string]: ContentFileConstructor } = {};
export function registerContentReferrer(s: ContentFileConstructor) {
console.log(`registered content type: ${s.content_type}`);
ContstructorTable[s.content_type] = s;
diff --git a/packages/server/src/content/video.ts b/packages/server/src/content/video.ts
index 46c7548..c26cfd5 100644
--- a/packages/server/src/content/video.ts
+++ b/packages/server/src/content/video.ts
@@ -1,9 +1,6 @@
-import { ContentConstructOption, ContentFile, registerContentReferrer } from "./file";
+import { registerContentReferrer } from "./file";
import { createDefaultClass } from "./file";
export class VideoReferrer extends createDefaultClass("video") {
- constructor(path: string, desc?: ContentConstructOption) {
- super(path, desc);
- }
}
registerContentReferrer(VideoReferrer);
diff --git a/packages/server/src/database.ts b/packages/server/src/database.ts
index fc681d9..997fe9f 100644
--- a/packages/server/src/database.ts
+++ b/packages/server/src/database.ts
@@ -1,47 +1,26 @@
-import { existsSync } from "fs";
-import Knex from "knex";
-import { Knex as KnexConfig } from "./config";
+import { existsSync } from "node:fs";
import { get_setting } from "./SettingConfig";
+import { getKysely } from "./db/kysely";
export async function connectDB() {
- const env = get_setting().mode;
- const config = KnexConfig.config[env];
- if (!config.connection) {
- throw new Error("connection options required.");
- }
- const connection = config.connection;
- if (typeof connection === "string") {
- throw new Error("unknown connection options");
- }
- if (typeof connection === "function") {
- throw new Error("connection provider not supported...");
- }
- if (!("filename" in connection)) {
- throw new Error("sqlite3 config need");
- }
- const init_need = !existsSync(connection.filename);
- const knex = Knex(config);
+ const kysely = getKysely();
+
let tries = 0;
for (;;) {
try {
console.log("try to connect db");
- await knex.raw("select 1 + 1;");
+ await kysely.selectNoFrom(eb=> eb.val(1).as("dummy")).execute();
console.log("connect success");
} catch (err) {
if (tries < 3) {
tries++;
console.error(`connection fail ${err} retry...`);
+ await new Promise((resolve) => setTimeout(resolve, 1000));
continue;
- } else {
- throw err;
}
+ throw err;
}
break;
}
- if (init_need) {
- console.log("first execute: initialize database...");
- const migrate = await import("../migrations/initial");
- await migrate.up(knex);
- }
- return knex;
+ return kysely;
}
diff --git a/packages/server/src/db/doc.ts b/packages/server/src/db/doc.ts
index 544fc0c..b89a7d3 100644
--- a/packages/server/src/db/doc.ts
+++ b/packages/server/src/db/doc.ts
@@ -1,235 +1,222 @@
-import { Knex } from "knex";
-import { Document, DocumentAccessor, DocumentBody, QueryListOption } from "../model/doc";
-import { TagAccessor } from "../model/tag";
-import { createKnexTagController } from "./tag";
+import { getKysely } from "./kysely";
+import { jsonArrayFrom } from "kysely/helpers/sqlite";
+import type { Document, DocumentAccessor, DocumentBody, QueryListOption } from "../model/doc";
+import { ParseJSONResultsPlugin, type NotNull } from "kysely";
+import { MyParseJSONResultsPlugin } from "./plugin";
export type DBTagContentRelation = {
doc_id: number;
tag_name: string;
};
-class KnexDocumentAccessor implements DocumentAccessor {
- knex: Knex;
- tagController: TagAccessor;
- constructor(knex: Knex) {
- this.knex = knex;
- this.tagController = createKnexTagController(knex);
+class SqliteDocumentAccessor implements DocumentAccessor {
+ constructor(private kysely = getKysely()) {
}
async search(search_word: string): Promise {
throw new Error("Method not implemented.");
- const sw = `%${search_word}%`;
- const docs = await this.knex.select("*").from("document").where("title", "like", sw);
- return docs;
}
async addList(content_list: DocumentBody[]): Promise {
- return await this.knex.transaction(async (trx) => {
+ return await this.kysely.transaction().execute(async (trx) => {
// add tags
const tagCollected = new Set();
- content_list
- .map((x) => x.tags)
- .forEach((x) => {
- x.forEach((x) => {
- tagCollected.add(x);
- });
- });
- const tagCollectPromiseList = [];
- const tagController = createKnexTagController(trx);
- for (const it of tagCollected) {
- const p = tagController.addTag({ name: it });
- tagCollectPromiseList.push(p);
- }
- await Promise.all(tagCollectPromiseList);
- // add for each contents
- const ret = [];
for (const content of content_list) {
- const { tags, additional, ...rest } = content;
- const id_lst = await trx
- .insert({
+ for (const tag of content.tags) {
+ tagCollected.add(tag);
+ }
+ }
+ await trx.insertInto("tags")
+ .values(Array.from(tagCollected).map((x) => ({ name: x })))
+ .onConflict((oc) => oc.doNothing())
+ .execute();
+
+ const ids = await trx.insertInto("document")
+ .values(content_list.map((content) => {
+ const { tags, additional, ...rest } = content;
+ return {
additional: JSON.stringify(additional),
created_at: Date.now(),
...rest,
- })
- .into("document");
- const id = id_lst[0];
- if (tags.length > 0) {
- await trx
- .insert(
- tags.map((y) => ({
- doc_id: id,
- tag_name: y,
- })),
- )
- .into("doc_tag_relation");
- }
- ret.push(id);
- }
- return ret;
+ };
+ }))
+ .returning("id")
+ .execute();
+ const id_lst = ids.map((x) => x.id);
+
+ const doc_tags = content_list.flatMap((content, index) => {
+ const { tags, ...rest } = content;
+ return tags.map((tag) => ({ doc_id: id_lst[index], tag_name: tag }));
+ });
+ await trx.insertInto("doc_tag_relation")
+ .values(doc_tags)
+ .execute();
+ return id_lst;
});
}
async add(c: DocumentBody) {
- const { tags, additional, ...rest } = c;
- const id_lst = await this.knex
- .insert({
- additional: JSON.stringify(additional),
- created_at: Date.now(),
- ...rest,
- })
- .into("document");
- const id = id_lst[0];
- for (const it of tags) {
- this.tagController.addTag({ name: it });
- }
- if (tags.length > 0) {
- await this.knex
- .insert(tags.map((x) => ({ doc_id: id, tag_name: x })))
- .into("doc_tag_relation");
- }
- return id;
+ return await this.kysely.transaction().execute(async (trx) => {
+ const { tags, additional, ...rest } = c;
+ const id_lst = await trx.insertInto("document").values({
+ additional: JSON.stringify(additional),
+ created_at: Date.now(),
+ ...rest,
+ })
+ .returning("id")
+ .executeTakeFirst() as { id: number };
+ const id = id_lst.id;
+
+ // add tags
+ await trx.insertInto("tags")
+ .values(tags.map((x) => ({ name: x })))
+ // on conflict is supported in sqlite and postgresql.
+ .onConflict((oc) => oc.doNothing());
+
+ if (tags.length > 0) {
+ await trx.insertInto("doc_tag_relation")
+ .values(tags.map((x) => ({ doc_id: id, tag_name: x })));
+ }
+ return id;
+ });
}
async del(id: number) {
- if ((await this.findById(id)) !== undefined) {
- await this.knex.delete().from("doc_tag_relation").where({ doc_id: id });
- await this.knex.delete().from("document").where({ id: id });
- return true;
- }
- return false;
+ // delete tags
+ await this.kysely
+ .deleteFrom("doc_tag_relation")
+ .where("doc_id", "=", id)
+ .execute();
+ // delete document
+ const result = await this.kysely
+ .deleteFrom("document")
+ .where("id", "=", id)
+ .executeTakeFirst();
+ return result.numDeletedRows > 0;
}
async findById(id: number, tagload?: boolean): Promise {
- const s = await this.knex.select("*").from("document").where({ id: id });
- if (s.length === 0) return undefined;
- const first = s[0];
- let ret_tags: string[] = [];
- if (tagload === true) {
- const tags: DBTagContentRelation[] = await this.knex
- .select("*")
- .from("doc_tag_relation")
- .where({ doc_id: first.id });
- ret_tags = tags.map((x) => x.tag_name);
- }
+ const doc = await this.kysely.selectFrom("document")
+ .selectAll()
+ .where("id", "=", id)
+ .$if(tagload ?? false, (qb) =>
+ qb.select(eb => jsonArrayFrom(
+ eb.selectFrom("doc_tag_relation")
+ .select(["doc_tag_relation.tag_name"])
+ .whereRef("document.id", "=", "doc_tag_relation.doc_id")
+ .select("tag_name")
+ ).as("tags")).withPlugin(new MyParseJSONResultsPlugin("tags"))
+ )
+ .executeTakeFirst();
+ if (!doc) return undefined;
+
return {
- ...first,
- tags: ret_tags,
- additional: first.additional !== null ? JSON.parse(first.additional) : {},
+ ...doc,
+ content_hash: doc.content_hash ?? "",
+ additional: doc.additional !== null ? JSON.parse(doc.additional) : {},
+ tags: doc.tags?.map((x: { tag_name: string }) => x.tag_name) ?? [],
};
}
async findDeleted(content_type: string) {
- const s = await this.knex
- .select("*")
- .where({ content_type: content_type })
- .whereNotNull("update_at")
- .from("document");
- return s.map((x) => ({
+ const docs = await this.kysely
+ .selectFrom("document")
+ .selectAll()
+ .where("content_type", "=", content_type)
+ .where("deleted_at", "is not", null)
+ .$narrowType<{ deleted_at: NotNull }>()
+ .execute();
+ return docs.map((x) => ({
...x,
tags: [],
+ content_hash: x.content_hash ?? "",
additional: {},
}));
}
async findList(option?: QueryListOption) {
- option = option ?? {};
- const allow_tag = option.allow_tag ?? [];
- const eager_loading = option.eager_loading ?? true;
- const limit = option.limit ?? 20;
- const use_offset = option.use_offset ?? false;
- const offset = option.offset ?? 0;
- const word = option.word;
- const content_type = option.content_type;
- const cursor = option.cursor;
+ const {
+ allow_tag = [],
+ eager_loading = true,
+ limit = 20,
+ use_offset = false,
+ offset = 0,
+ word,
+ content_type,
+ cursor,
+ } = option ?? {};
- const buildquery = () => {
- let query = this.knex.select("document.*");
- if (allow_tag.length > 0) {
- query = query.from("doc_tag_relation as tags_0");
- query = query.where("tags_0.tag_name", "=", allow_tag[0]);
- for (let index = 1; index < allow_tag.length; index++) {
- const element = allow_tag[index];
- query = query.innerJoin(`doc_tag_relation as tags_${index}`, `tags_${index}.doc_id`, "tags_0.doc_id");
- query = query.where(`tags_${index}.tag_name`, "=", element);
- }
- query = query.innerJoin("document", "tags_0.doc_id", "document.id");
- } else {
- query = query.from("document");
- }
- if (word !== undefined) {
- // don't worry about sql injection.
- query = query.where("title", "like", `%${word}%`);
- }
- if (content_type !== undefined) {
- query = query.where("content_type", "=", content_type);
- }
- if (use_offset) {
- query = query.offset(offset);
- } else {
- if (cursor !== undefined) {
- query = query.where("id", "<", cursor);
- }
- }
- query = query.limit(limit);
- query = query.orderBy("id", "desc");
- return query;
- };
- let query = buildquery();
- // console.log(query.toSQL());
- let result: Document[] = await query;
- for (let i of result) {
- i.additional = JSON.parse(i.additional as unknown as string);
- }
- if (eager_loading) {
- let idmap: { [index: number]: Document } = {};
- for (const r of result) {
- idmap[r.id] = r;
- r.tags = [];
- }
- let subquery = buildquery();
- let tagquery = this.knex
- .select("id", "doc_tag_relation.tag_name")
- .from(subquery)
- .innerJoin("doc_tag_relation", "doc_tag_relation.doc_id", "id");
- // console.log(tagquery.toSQL());
- let tagresult: { id: number; tag_name: string }[] = await tagquery;
- for (const { id, tag_name } of tagresult) {
- idmap[id].tags.push(tag_name);
- }
- } else {
- result.forEach((v) => {
- v.tags = [];
- });
- }
- return result;
+ const result = await this.kysely
+ .selectFrom("document")
+ .selectAll()
+ .$if(allow_tag.length > 0, (qb) => {
+ return allow_tag.reduce((prevQb ,tag, index) => {
+ return prevQb.innerJoin(`doc_tag_relation as tags_${index}`, `tags_${index}.tag_name`, "document.id")
+ .where(`tags_${index}.tag_name`, "=", tag);
+ }, qb) as unknown as typeof qb;
+ })
+ .$if(word !== undefined, (qb) => qb.where("title", "like", `%${word}%`))
+ .$if(content_type !== undefined, (qb) => qb.where("content_type", "=", content_type as string))
+ .$if(use_offset, (qb) => qb.offset(offset))
+ .$if(!use_offset && cursor !== undefined, (qb) => qb.where("id", "<", cursor as number))
+ .limit(limit)
+ .$if(eager_loading, (qb) => {
+ return qb.select(eb => jsonArrayFrom(
+ eb.selectFrom("doc_tag_relation")
+ .select(["doc_tag_relation.tag_name"])
+ .whereRef("document.id", "=", "doc_tag_relation.doc_id")
+ ).as("tags")).withPlugin(new MyParseJSONResultsPlugin("tags"))
+ })
+ .orderBy("id", "desc")
+ .execute();
+ return result.map((x) => ({
+ ...x,
+ content_hash: x.content_hash ?? "",
+ additional: x.additional !== null ? (JSON.parse(x.additional)) : {},
+ tags: x.tags?.map((x: { tag_name: string }) => x.tag_name) ?? [],
+ }));
}
async findByPath(path: string, filename?: string): Promise {
- const e = filename == undefined ? {} : { filename: filename };
- const results = await this.knex
- .select("*")
- .from("document")
- .where({ basepath: path, ...e });
+ const results = await this.kysely
+ .selectFrom("document")
+ .selectAll()
+ .where("basepath", "=", path)
+ .$if(filename !== undefined, (qb) => qb.where("filename", "=", filename as string))
+ .execute();
return results.map((x) => ({
...x,
+ content_hash: x.content_hash ?? "",
tags: [],
additional: {},
}));
}
async update(c: Partial & { id: number }) {
- const { id, tags, ...rest } = c;
- if ((await this.findById(id)) !== undefined) {
- await this.knex.update(rest).where({ id: id }).from("document");
- return true;
- }
- return false;
+ const { id, tags, additional, ...rest } = c;
+ const r = await this.kysely.updateTable("document")
+ .set({
+ ...rest,
+ modified_at: Date.now(),
+ additional: additional !== undefined ? JSON.stringify(additional) : undefined,
+ })
+ .where("id", "=", id)
+ .executeTakeFirst();
+ return r.numUpdatedRows > 0;
}
async addTag(c: Document, tag_name: string) {
if (c.tags.includes(tag_name)) return false;
- this.tagController.addTag({ name: tag_name });
- await this.knex.insert({ tag_name: tag_name, doc_id: c.id }).into("doc_tag_relation");
+ await this.kysely.insertInto("tags")
+ .values({ name: tag_name })
+ .onConflict((oc) => oc.doNothing())
+ .execute();
+ await this.kysely.insertInto("doc_tag_relation")
+ .values({ tag_name: tag_name, doc_id: c.id })
+ .execute();
c.tags.push(tag_name);
return true;
}
async delTag(c: Document, tag_name: string) {
if (c.tags.includes(tag_name)) return false;
- await this.knex.delete().where({ tag_name: tag_name, doc_id: c.id }).from("doc_tag_relation");
- c.tags.push(tag_name);
+ await this.kysely.deleteFrom("doc_tag_relation")
+ .where("tag_name", "=", tag_name)
+ .where("doc_id", "=", c.id)
+ .execute();
+ c.tags.splice(c.tags.indexOf(tag_name), 1);
return true;
}
}
-export const createKnexDocumentAccessor = (knex: Knex): DocumentAccessor => {
- return new KnexDocumentAccessor(knex);
+export const createSqliteDocumentAccessor = (kysely = getKysely()): DocumentAccessor => {
+ return new SqliteDocumentAccessor(kysely);
};
diff --git a/packages/server/src/db/kysely.ts b/packages/server/src/db/kysely.ts
new file mode 100644
index 0000000..31e1ba2
--- /dev/null
+++ b/packages/server/src/db/kysely.ts
@@ -0,0 +1,26 @@
+import { Kysely, ParseJSONResultsPlugin, SqliteDialect } from "kysely";
+import SqliteDatabase from "better-sqlite3";
+import type { DB } from "dbtype/types";
+
+export function createSqliteDialect() {
+ const url = process.env.DATABASE_URL;
+ if (!url) {
+ throw new Error("DATABASE_URL is not set");
+ }
+ const db = new SqliteDatabase(url);
+ return new SqliteDialect({
+ database: db,
+ });
+}
+
+// Create a new Kysely instance with a new SqliteDatabase instance
+let kysely: Kysely | null = null;
+export function getKysely() {
+ if (!kysely) {
+ kysely = new Kysely({
+ dialect: createSqliteDialect(),
+ // plugins: [new ParseJSONResultsPlugin()],
+ });
+ }
+ return kysely;
+}
\ No newline at end of file
diff --git a/packages/server/src/db/plugin.ts b/packages/server/src/db/plugin.ts
new file mode 100644
index 0000000..5574973
--- /dev/null
+++ b/packages/server/src/db/plugin.ts
@@ -0,0 +1,24 @@
+import type { KyselyPlugin, PluginTransformQueryArgs, PluginTransformResultArgs, QueryResult, RootOperationNode, UnknownRow } from "kysely";
+
+export class MyParseJSONResultsPlugin implements KyselyPlugin {
+
+ constructor(private readonly itemPath: string) { }
+
+ transformQuery(args: PluginTransformQueryArgs): RootOperationNode {
+ // do nothing
+ return args.node;
+ }
+ async transformResult(args: PluginTransformResultArgs): Promise> {
+ return {
+ ...args.result,
+ rows: args.result.rows.map((row) => {
+ const newRow = { ...row };
+ const item = newRow[this.itemPath];
+ if (typeof item === "string") {
+ newRow[this.itemPath] = JSON.parse(item);
+ }
+ return newRow;
+ })
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/server/src/db/tag.ts b/packages/server/src/db/tag.ts
index 6e1badc..35ff2cf 100644
--- a/packages/server/src/db/tag.ts
+++ b/packages/server/src/db/tag.ts
@@ -1,61 +1,65 @@
-import { Knex } from "knex";
-import { Tag, TagAccessor, TagCount } from "../model/tag";
-import { DBTagContentRelation } from "./doc";
+import { getKysely } from "./kysely";
+import { jsonArrayFrom } from "kysely/helpers/sqlite";
+import type { Tag, TagAccessor, TagCount } from "../model/tag";
+import type { DBTagContentRelation } from "./doc";
-type DBTags = {
- name: string;
- description?: string;
-};
+class SqliteTagAccessor implements TagAccessor {
-class KnexTagAccessor implements TagAccessor {
- knex: Knex;
- constructor(knex: Knex) {
- this.knex = knex;
+ constructor(private kysely = getKysely()) {
}
async getAllTagCount(): Promise {
- const result = await this.knex("doc_tag_relation")
+ const result = await this.kysely
+ .selectFrom("doc_tag_relation")
.select("tag_name")
- .count("*", { as: "occurs" })
- .groupBy("tag_name");
+ .select(qb => qb.fn.count("doc_id").as("occurs"))
+ .groupBy("tag_name")
+ .execute();
return result;
}
- async getAllTagList(onlyname?: boolean) {
- onlyname = onlyname ?? false;
- const t: DBTags[] = await this.knex.select(onlyname ? "*" : "name").from("tags");
- return t;
+ async getAllTagList(): Promise {
+ return (await this.kysely.selectFrom("tags")
+ .selectAll()
+ .execute()
+ ).map((x) => ({
+ name: x.name,
+ description: x.description ?? undefined,
+ }));
}
async getTagByName(name: string) {
- const t: DBTags[] = await this.knex.select("*").from("tags").where({ name: name });
- if (t.length === 0) return undefined;
- return t[0];
+ const result = await this.kysely
+ .selectFrom("tags")
+ .selectAll()
+ .where("name", "=", name)
+ .executeTakeFirst();
+ if (result === undefined) {
+ return undefined;
+ }
+ return {
+ name: result.name,
+ description: result.description ?? undefined,
+ };
}
async addTag(tag: Tag) {
- if ((await this.getTagByName(tag.name)) === undefined) {
- await this.knex
- .insert({
- name: tag.name,
- description: tag.description === undefined ? "" : tag.description,
- })
- .into("tags");
- return true;
- }
- return false;
+ const result = await this.kysely.insertInto("tags")
+ .values([tag])
+ .onConflict((oc) => oc.doNothing())
+ .executeTakeFirst();
+ return (result.numInsertedOrUpdatedRows ?? 0n) > 0;
}
async delTag(name: string) {
- if ((await this.getTagByName(name)) !== undefined) {
- await this.knex.delete().where({ name: name }).from("tags");
- return true;
- }
- return false;
+ const result = await this.kysely.deleteFrom("tags")
+ .where("name", "=", name)
+ .executeTakeFirst();
+ return (result.numDeletedRows ?? 0n) > 0;
}
async updateTag(name: string, desc: string) {
- if ((await this.getTagByName(name)) !== undefined) {
- await this.knex.update({ description: desc }).where({ name: name }).from("tags");
- return true;
- }
- return false;
+ const result = await this.kysely.updateTable("tags")
+ .set({ description: desc })
+ .where("name", "=", name)
+ .executeTakeFirst();
+ return (result.numUpdatedRows ?? 0n) > 0;
}
}
-export const createKnexTagController = (knex: Knex): TagAccessor => {
- return new KnexTagAccessor(knex);
+export const createSqliteTagController = (kysely = getKysely()): TagAccessor => {
+ return new SqliteTagAccessor(kysely);
};
diff --git a/packages/server/src/db/user.ts b/packages/server/src/db/user.ts
index adfd255..ba2c899 100644
--- a/packages/server/src/db/user.ts
+++ b/packages/server/src/db/user.ts
@@ -1,88 +1,87 @@
-import { Knex } from "knex";
-import { IUser, Password, UserAccessor, UserCreateInput } from "../model/user";
+import { getKysely } from "./kysely";
+import { type IUser, Password, type UserAccessor, type UserCreateInput } from "../model/user";
-type PermissionTable = {
- username: string;
- name: string;
-};
-type DBUser = {
- username: string;
- password_hash: string;
- password_salt: string;
-};
-class KnexUser implements IUser {
- private knex: Knex;
+class SqliteUser implements IUser {
readonly username: string;
readonly password: Password;
- constructor(username: string, pw: Password, knex: Knex) {
+ constructor(username: string, pw: Password, private kysely = getKysely()) {
this.username = username;
this.password = pw;
- this.knex = knex;
}
async reset_password(password: string) {
this.password.set_password(password);
- await this.knex
- .from("users")
- .where({ username: this.username })
- .update({ password_hash: this.password.hash, password_salt: this.password.salt });
+ await this.kysely
+ .updateTable("users")
+ .where("username", "=", this.username)
+ .set({ password_hash: this.password.hash, password_salt: this.password.salt })
+ .execute();
}
async get_permissions() {
- let b = (await this.knex.select("*").from("permissions").where({ username: this.username })) as PermissionTable[];
- return b.map((x) => x.name);
+ const permissions = await this.kysely
+ .selectFrom("permissions")
+ .selectAll()
+ .where("username", "=", this.username)
+ .execute();
+ return permissions.map((x) => x.name);
}
async add(name: string) {
- if (!(await this.get_permissions()).includes(name)) {
- const r = await this.knex
- .insert({
- username: this.username,
- name: name,
- })
- .into("permissions");
- return true;
- }
- return false;
+ const result = await this.kysely
+ .insertInto("permissions")
+ .values({ username: this.username, name })
+ .onConflict((oc) => oc.doNothing())
+ .executeTakeFirst();
+ return (result.numInsertedOrUpdatedRows ?? 0n) > 0;
}
async remove(name: string) {
- const r = await this.knex
- .from("permissions")
- .where({
- username: this.username,
- name: name,
- })
- .delete();
- return r !== 0;
+ const result = await this.kysely
+ .deleteFrom("permissions")
+ .where("username", "=", this.username)
+ .where("name", "=", name)
+ .executeTakeFirst();
+ return (result.numDeletedRows ?? 0n) > 0;
}
}
-export const createKnexUserController = (knex: Knex): UserAccessor => {
- const createUserKnex = async (input: UserCreateInput) => {
- if (undefined !== (await findUserKenx(input.username))) {
+export const createSqliteUserController = (kysely = getKysely()): UserAccessor => {
+ const createUser = async (input: UserCreateInput) => {
+ if (undefined !== (await findUser(input.username))) {
return undefined;
}
- const user = new KnexUser(input.username, new Password(input.password), knex);
- await knex
- .insert({
- username: user.username,
- password_hash: user.password.hash,
- password_salt: user.password.salt,
- })
- .into("users");
+ const user = new SqliteUser(input.username, new Password(input.password), kysely);
+ await kysely
+ .insertInto("users")
+ .values({ username: user.username, password_hash: user.password.hash, password_salt: user.password.salt })
+ .execute();
return user;
};
- const findUserKenx = async (id: string) => {
- let user: DBUser[] = await knex.select("*").from("users").where({ username: id });
- if (user.length == 0) return undefined;
- const first = user[0];
- return new KnexUser(first.username, new Password({ hash: first.password_hash, salt: first.password_salt }), knex);
+ const findUser = async (id: string) => {
+ const user = await kysely
+ .selectFrom("users")
+ .selectAll()
+ .where("username", "=", id)
+ .executeTakeFirst();
+ if (!user) return undefined;
+ if (!user.password_hash || !user.password_salt) {
+ throw new Error("password hash or salt is missing");
+ }
+ if (user.username === null) {
+ throw new Error("username is null");
+ }
+ return new SqliteUser(user.username, new Password({
+ hash: user.password_hash,
+ salt: user.password_salt
+ }), kysely);
};
- const delUserKnex = async (id: string) => {
- let r = await knex.delete().from("users").where({ username: id });
- return r === 0;
+ const delUser = async (id: string) => {
+ const result = await kysely.deleteFrom("users")
+ .where("username", "=", id)
+ .executeTakeFirst();
+ return (result.numDeletedRows ?? 0n) > 0;
};
return {
- createUser: createUserKnex,
- findUser: findUserKenx,
- delUser: delUserKnex,
+ createUser: createUser,
+ findUser: findUser,
+ delUser: delUser,
};
};
diff --git a/packages/server/src/diff/content_handler.ts b/packages/server/src/diff/content_handler.ts
index d70f90c..d0e0cc6 100644
--- a/packages/server/src/diff/content_handler.ts
+++ b/packages/server/src/diff/content_handler.ts
@@ -1,8 +1,8 @@
-import { basename, dirname, join as pathjoin } from "path";
+import { basename, dirname, join as pathjoin } from "node:path";
import { ContentFile, createContentFile } from "../content/mod";
-import { Document, DocumentAccessor } from "../model/mod";
+import type { Document, DocumentAccessor } from "../model/mod";
import { ContentList } from "./content_list";
-import { IDiffWatcher } from "./watcher";
+import type { IDiffWatcher } from "./watcher";
// refactoring needed.
export class ContentDiffHandler {
diff --git a/packages/server/src/diff/content_list.ts b/packages/server/src/diff/content_list.ts
index 51035fb..9e3f7f8 100644
--- a/packages/server/src/diff/content_list.ts
+++ b/packages/server/src/diff/content_list.ts
@@ -1,4 +1,4 @@
-import { ContentFile } from "../content/mod";
+import type { ContentFile } from "../content/mod";
export class ContentList {
/** path map */
diff --git a/packages/server/src/diff/diff.ts b/packages/server/src/diff/diff.ts
index df75b22..5fd7c5a 100644
--- a/packages/server/src/diff/diff.ts
+++ b/packages/server/src/diff/diff.ts
@@ -1,7 +1,7 @@
import asyncPool from "tiny-async-pool";
-import { DocumentAccessor } from "../model/doc";
+import type { DocumentAccessor } from "../model/doc";
import { ContentDiffHandler } from "./content_handler";
-import { IDiffWatcher } from "./watcher";
+import type { IDiffWatcher } from "./watcher";
export class DiffManager {
watching: { [content_type: string]: ContentDiffHandler };
diff --git a/packages/server/src/diff/router.ts b/packages/server/src/diff/router.ts
index 21b7358..725bb6e 100644
--- a/packages/server/src/diff/router.ts
+++ b/packages/server/src/diff/router.ts
@@ -1,9 +1,9 @@
-import Koa from "koa";
+import type Koa from "koa";
import Router from "koa-router";
-import { ContentFile } from "../content/mod";
+import type { ContentFile } from "../content/mod";
import { AdminOnlyMiddleware } from "../permission/permission";
import { sendError } from "../route/error_handler";
-import { DiffManager } from "./diff";
+import type { DiffManager } from "./diff";
function content_file_to_return(x: ContentFile) {
return { path: x.path, type: x.type };
@@ -24,7 +24,7 @@ type PostAddedBody = {
}[];
function checkPostAddedBody(body: any): body is PostAddedBody {
- if (body instanceof Array) {
+ if (Array.isArray(body)) {
return body.map((x) => "type" in x && "path" in x).every((x) => x);
}
return false;
@@ -54,7 +54,7 @@ export const postAddedAll = (diffmgr: DiffManager) => async (ctx: Router.IRouter
sendError(400, 'format exception: there is no "type"');
return;
}
- const t = reqbody["type"];
+ const t = reqbody.type;
if (typeof t !== "string") {
sendError(400, 'format exception: invalid type of "type"');
return;
diff --git a/packages/server/src/diff/watcher.ts b/packages/server/src/diff/watcher.ts
index 0513374..70b01c8 100644
--- a/packages/server/src/diff/watcher.ts
+++ b/packages/server/src/diff/watcher.ts
@@ -1,8 +1,8 @@
-import event from "events";
-import { FSWatcher, watch } from "fs";
-import { promises } from "fs";
-import { join } from "path";
-import { DocumentAccessor } from "../model/doc";
+import type event from "node:events";
+import { FSWatcher, watch } from "node:fs";
+import { promises } from "node:fs";
+import { join } from "node:path";
+import type { DocumentAccessor } from "../model/doc";
const readdir = promises.readdir;
diff --git a/packages/server/src/diff/watcher/comic_watcher.ts b/packages/server/src/diff/watcher/comic_watcher.ts
index 5fd4942..96f5273 100644
--- a/packages/server/src/diff/watcher/comic_watcher.ts
+++ b/packages/server/src/diff/watcher/comic_watcher.ts
@@ -1,6 +1,3 @@
-import { EventEmitter } from "events";
-import { DocumentAccessor } from "../../model/doc";
-import { DiffWatcherEvent, IDiffWatcher } from "../watcher";
import { ComicConfig } from "./ComicConfig";
import { WatcherCompositer } from "./compositer";
import { RecursiveWatcher } from "./recursive_watcher";
diff --git a/packages/server/src/diff/watcher/common_watcher.ts b/packages/server/src/diff/watcher/common_watcher.ts
index 5d9444d..2782660 100644
--- a/packages/server/src/diff/watcher/common_watcher.ts
+++ b/packages/server/src/diff/watcher/common_watcher.ts
@@ -1,8 +1,8 @@
-import event from "events";
-import { FSWatcher, promises, watch } from "fs";
-import { join } from "path";
-import { DocumentAccessor } from "../../model/doc";
-import { DiffWatcherEvent, IDiffWatcher } from "../watcher";
+import event from "node:events";
+import { type FSWatcher, promises, watch } from "node:fs";
+import { join } from "node:path";
+import type { DocumentAccessor } from "../../model/doc";
+import type { DiffWatcherEvent, IDiffWatcher } from "../watcher";
import { setupHelp } from "./util";
const { readdir } = promises;
diff --git a/packages/server/src/diff/watcher/compositer.ts b/packages/server/src/diff/watcher/compositer.ts
index f993f61..b356f58 100644
--- a/packages/server/src/diff/watcher/compositer.ts
+++ b/packages/server/src/diff/watcher/compositer.ts
@@ -1,6 +1,6 @@
-import { EventEmitter } from "events";
-import { DocumentAccessor } from "../../model/doc";
-import { DiffWatcherEvent, IDiffWatcher, linkWatcher } from "../watcher";
+import { EventEmitter } from "node:events";
+import type { DocumentAccessor } from "../../model/doc";
+import { type DiffWatcherEvent, type IDiffWatcher, linkWatcher } from "../watcher";
export class WatcherCompositer extends EventEmitter implements IDiffWatcher {
refWatchers: IDiffWatcher[];
diff --git a/packages/server/src/diff/watcher/recursive_watcher.ts b/packages/server/src/diff/watcher/recursive_watcher.ts
index d14a31f..14fe70d 100644
--- a/packages/server/src/diff/watcher/recursive_watcher.ts
+++ b/packages/server/src/diff/watcher/recursive_watcher.ts
@@ -1,9 +1,8 @@
-import { FSWatcher, watch } from "chokidar";
-import { EventEmitter } from "events";
-import { join } from "path";
-import { DocumentAccessor } from "../../model/doc";
-import { DiffWatcherEvent, IDiffWatcher } from "../watcher";
-import { setupHelp, setupRecursive } from "./util";
+import { type FSWatcher, watch } from "chokidar";
+import { EventEmitter } from "node:events";
+import type { DocumentAccessor } from "../../model/doc";
+import type { DiffWatcherEvent, IDiffWatcher } from "../watcher";
+import { setupRecursive } from "./util";
type RecursiveWatcherOption = {
/** @default true */
diff --git a/packages/server/src/diff/watcher/util.ts b/packages/server/src/diff/watcher/util.ts
index 1823deb..7e136f0 100644
--- a/packages/server/src/diff/watcher/util.ts
+++ b/packages/server/src/diff/watcher/util.ts
@@ -1,14 +1,13 @@
-import { EventEmitter } from "events";
-import { promises } from "fs";
-import { join } from "path";
+import { promises } from "node:fs";
+import { join } from "node:path";
const { readdir } = promises;
-import { DocumentAccessor } from "../../model/doc";
-import { IDiffWatcher } from "../watcher";
+import type { DocumentAccessor } from "../../model/doc";
+import type { IDiffWatcher } from "../watcher";
function setupCommon(watcher: IDiffWatcher, basepath: string, initial_filenames: string[], cur: string[]) {
// Todo : reduce O(nm) to O(n+m) using hash map.
- let added = cur.filter((x) => !initial_filenames.includes(x));
- let deleted = initial_filenames.filter((x) => !cur.includes(x));
+ const added = cur.filter((x) => !initial_filenames.includes(x));
+ const deleted = initial_filenames.filter((x) => !cur.includes(x));
for (const it of added) {
const cpath = join(basepath, it);
watcher.emit("create", cpath);
diff --git a/packages/server/src/diff/watcher/watcher_filter.ts b/packages/server/src/diff/watcher/watcher_filter.ts
index a8a41f9..67d0fca 100644
--- a/packages/server/src/diff/watcher/watcher_filter.ts
+++ b/packages/server/src/diff/watcher/watcher_filter.ts
@@ -1,6 +1,6 @@
-import { EventEmitter } from "events";
-import { DocumentAccessor } from "../../model/doc";
-import { DiffWatcherEvent, IDiffWatcher, linkWatcher } from "../watcher";
+import { EventEmitter } from "node:events";
+import type { DocumentAccessor } from "../../model/doc";
+import { type DiffWatcherEvent, type IDiffWatcher, linkWatcher } from "../watcher";
export class WatcherFilter extends EventEmitter implements IDiffWatcher {
refWatcher: IDiffWatcher;
@@ -21,18 +21,16 @@ export class WatcherFilter extends EventEmitter implements IDiffWatcher {
if (this.filter(prev)) {
if (this.filter(cur)) {
return super.emit("change", prev, cur);
- } else {
- return super.emit("delete", cur);
}
- } else {
+ return super.emit("delete", cur);
+ }
if (this.filter(cur)) {
return super.emit("create", cur);
}
- }
return false;
- } else if (!this.filter(arg[0])) {
+ }if (!this.filter(arg[0])) {
return false;
- } else return super.emit(event, ...arg);
+ }return super.emit(event, ...arg);
}
constructor(refWatcher: IDiffWatcher, filter: (filename: string) => boolean) {
super();
diff --git a/packages/server/src/login.ts b/packages/server/src/login.ts
index 4d69feb..e10917a 100644
--- a/packages/server/src/login.ts
+++ b/packages/server/src/login.ts
@@ -1,10 +1,10 @@
-import { request } from "http";
+import { request } from "node:http";
import { decode, sign, TokenExpiredError, verify } from "jsonwebtoken";
import Knex from "knex";
-import Koa from "koa";
+import type Koa from "koa";
import Router from "koa-router";
-import { createKnexUserController } from "./db/mod";
-import { IUser, UserAccessor } from "./model/mod";
+import { createSqliteUserController } from "./db/mod";
+import type { IUser, UserAccessor } from "./model/mod";
import { sendError } from "./route/error_handler";
import { get_setting } from "./SettingConfig";
@@ -19,7 +19,7 @@ export type UserState = {
const isUserState = (obj: object | string): obj is PayloadInfo => {
if (typeof obj === "string") return false;
- return "username" in obj && "permission" in obj && (obj as { permission: unknown }).permission instanceof Array;
+ return "username" in obj && "permission" in obj && Array.isArray((obj as { permission: unknown }).permission);
};
type RefreshPayloadInfo = { username: string };
const isRefreshToken = (obj: object | string): obj is RefreshPayloadInfo => {
@@ -57,7 +57,7 @@ const publishRefreshToken = (secretKey: string, username: string, expiredtime: n
};
function setToken(ctx: Koa.Context, token_name: string, token_payload: string | null, expiredtime: number) {
const setting = get_setting();
- if (token_payload === null && !!!ctx.cookies.get(token_name)) {
+ if (token_payload === null && !ctx.cookies.get(token_name)) {
return;
}
ctx.cookies.set(token_name, token_payload, {
@@ -72,11 +72,11 @@ export const createLoginMiddleware = (userController: UserAccessor) => async (ct
const secretKey = setting.jwt_secretkey;
const body = ctx.request.body;
// check format
- if (typeof body == "string" || !("username" in body) || !("password" in body)) {
+ if (typeof body === "string" || !("username" in body) || !("password" in body)) {
return sendError(400, "invalid form : username or password is not found in query.");
}
- const username = body["username"];
- const password = body["password"];
+ const username = body.username;
+ const password = body.password;
// check type
if (typeof username !== "string" || typeof password !== "string") {
return sendError(400, "invalid form : username or password is not string");
@@ -125,7 +125,7 @@ export const createUserMiddleWare =
const setGuest = async () => {
setToken(ctx, accessTokenName, null, 0);
setToken(ctx, refreshTokenName, null, 0);
- ctx.state["user"] = { username: "", permission: setting.guest };
+ ctx.state.user = { username: "", permission: setting.guest };
return await next();
};
return await refreshToken(ctx, setGuest, next);
@@ -134,7 +134,7 @@ const refreshTokenHandler = (cntr: UserAccessor) => async (ctx: Koa.Context, fai
const accessPayload = ctx.cookies.get(accessTokenName);
const setting = get_setting();
const secretKey = setting.jwt_secretkey;
- if (accessPayload == undefined) {
+ if (accessPayload === undefined) {
return await checkRefreshAndUpdate();
}
try {
@@ -142,20 +142,19 @@ const refreshTokenHandler = (cntr: UserAccessor) => async (ctx: Koa.Context, fai
if (isUserState(o)) {
ctx.state.user = o;
return await next();
- } else {
+ }
console.error("invalid token detected");
throw new Error("token form invalid");
- }
} catch (e) {
if (e instanceof TokenExpiredError) {
return await checkRefreshAndUpdate();
- } else throw e;
+ }throw e;
}
async function checkRefreshAndUpdate() {
const refreshPayload = ctx.cookies.get(refreshTokenName);
if (refreshPayload === undefined) {
return await fail(); // refresh token doesn't exist
- } else {
+ }
try {
const o = verify(refreshPayload, secretKey);
if (isRefreshToken(o)) {
@@ -173,9 +172,8 @@ const refreshTokenHandler = (cntr: UserAccessor) => async (ctx: Koa.Context, fai
if (e instanceof TokenExpiredError) {
// refresh token is expired.
return await fail();
- } else throw e;
+ }throw e;
}
- }
return await next();
}
};
@@ -205,9 +203,9 @@ export const resetPasswordMiddleware = (cntr: UserAccessor) => async (ctx: Koa.C
if (typeof body !== "object" || !("username" in body) || !("oldpassword" in body) || !("newpassword" in body)) {
return sendError(400, "request body is invalid format");
}
- const username = body["username"];
- const oldpw = body["oldpassword"];
- const newpw = body["newpassword"];
+ const username = body.username;
+ const oldpw = body.oldpassword;
+ const newpw = body.newpassword;
if (typeof username !== "string" || typeof oldpw !== "string" || typeof newpw !== "string") {
return sendError(400, "request body is invalid format");
}
diff --git a/packages/server/src/model/doc.ts b/packages/server/src/model/doc.ts
index 3e01e75..9b2dca2 100644
--- a/packages/server/src/model/doc.ts
+++ b/packages/server/src/model/doc.ts
@@ -1,4 +1,4 @@
-import { JSONMap } from "../types/json";
+import type { JSONMap } from "../types/json";
import { check_type } from "../util/type_check";
import { TagAccessor } from "./tag";
@@ -8,7 +8,7 @@ export interface DocumentBody {
basepath: string;
filename: string;
modified_at: number;
- content_hash: string;
+ content_hash: string | null;
additional: JSONMap;
tags: string[]; // eager loading
}
@@ -23,7 +23,7 @@ export const MetaContentBody = {
tags: "string[]",
};
-export const isDocBody = (c: any): c is DocumentBody => {
+export const isDocBody = (c: unknown): c is DocumentBody => {
return check_type(c, MetaContentBody);
};
@@ -33,8 +33,9 @@ export interface Document extends DocumentBody {
readonly deleted_at: number | null;
}
-export const isDoc = (c: any): c is Document => {
- if ("id" in c && typeof c["id"] === "number") {
+export const isDoc = (c: unknown): c is Document => {
+ if (typeof c !== "object" || c === null) return false;
+ if ("id" in c && typeof c.id === "number") {
const { id, ...rest } = c;
return isDocBody(rest);
}
diff --git a/packages/server/src/model/tag.ts b/packages/server/src/model/tag.ts
index f2592fc..ab3f985 100644
--- a/packages/server/src/model/tag.ts
+++ b/packages/server/src/model/tag.ts
@@ -9,7 +9,7 @@ export interface TagCount {
}
export interface TagAccessor {
- getAllTagList: (onlyname?: boolean) => Promise;
+ getAllTagList: () => Promise;
getAllTagCount(): Promise;
getTagByName: (name: string) => Promise;
addTag: (tag: Tag) => Promise;
diff --git a/packages/server/src/model/user.ts b/packages/server/src/model/user.ts
index 700793d..f4b8005 100644
--- a/packages/server/src/model/user.ts
+++ b/packages/server/src/model/user.ts
@@ -1,4 +1,4 @@
-import { createHmac, randomBytes } from "crypto";
+import { createHmac, randomBytes } from "node:crypto";
function hashForPassword(salt: string, password: string) {
return createHmac("sha256", salt).update(password).digest("hex");
diff --git a/packages/server/src/permission/permission.ts b/packages/server/src/permission/permission.ts
index e6212e1..8229316 100644
--- a/packages/server/src/permission/permission.ts
+++ b/packages/server/src/permission/permission.ts
@@ -1,5 +1,5 @@
-import Koa from "koa";
-import { UserState } from "../login";
+import type Koa from "koa";
+import type { UserState } from "../login";
import { sendError } from "../route/error_handler";
export enum Permission {
@@ -37,7 +37,7 @@ export enum Permission {
export const createPermissionCheckMiddleware =
(...permissions: string[]) =>
async (ctx: Koa.ParameterizedContext, next: Koa.Next) => {
- const user = ctx.state["user"];
+ const user = ctx.state.user;
if (user.username === "admin") {
return await next();
}
@@ -46,12 +46,12 @@ export const createPermissionCheckMiddleware =
if (!permissions.map((p) => user_permission.includes(p)).every((x) => x)) {
if (user.username === "") {
return sendError(401, "you are guest. login needed.");
- } else return sendError(403, "do not have permission");
+ }return sendError(403, "do not have permission");
}
await next();
};
export const AdminOnlyMiddleware = async (ctx: Koa.ParameterizedContext, next: Koa.Next) => {
- const user = ctx.state["user"];
+ const user = ctx.state.user;
if (user.username !== "admin") {
return sendError(403, "admin only");
}
diff --git a/packages/server/src/route/all.ts b/packages/server/src/route/all.ts
index e99ba16..49724ef 100644
--- a/packages/server/src/route/all.ts
+++ b/packages/server/src/route/all.ts
@@ -1,8 +1,8 @@
-import { DefaultContext, Middleware, Next, ParameterizedContext } from "koa";
+import type { DefaultContext, Middleware, Next, ParameterizedContext } from "koa";
import compose from "koa-compose";
-import Router, { IParamMiddleware } from "koa-router";
+import Router from "koa-router";
import ComicRouter from "./comic";
-import { ContentContext } from "./context";
+import type { ContentContext } from "./context";
import VideoRouter from "./video";
const table: { [s: string]: Router | undefined } = {
@@ -12,25 +12,26 @@ const table: { [s: string]: Router | undefined } = {
const all_middleware =
(cont: string | undefined, restarg: string | undefined) =>
async (ctx: ParameterizedContext, next: Next) => {
- if (cont == undefined) {
+ if (cont === undefined) {
ctx.status = 404;
return;
}
- if (ctx.state.location.type != cont) {
+ if (ctx.state.location.type !== cont) {
console.error("not matched");
ctx.status = 404;
return;
}
const router = table[cont];
- if (router == undefined) {
+ if (router === undefined) {
ctx.status = 404;
return;
}
- const rest = "/" + (restarg ?? "");
+ const rest = `/${restarg ?? ""}`;
const result = router.match(rest, "GET");
if (!result.route) {
return await next();
}
+ // biome-ignore lint/suspicious/noExplicitAny:
const chain = result.pathAndMethod.reduce((combination: Middleware[], cur) => {
combination.push(async (ctx, next) => {
const captures = cur.captures(rest);
@@ -47,11 +48,11 @@ export class AllContentRouter extends Router {
constructor() {
super();
this.get("/:content_type", async (ctx, next) => {
- return await all_middleware(ctx.params["content_type"], undefined)(ctx, next);
+ return await all_middleware(ctx.params.content_type, undefined)(ctx, next);
});
this.get("/:content_type/:rest(.*)", async (ctx, next) => {
- const cont = ctx.params["content_type"] as string;
- return await all_middleware(cont, ctx.params["rest"])(ctx, next);
+ const cont = ctx.params.content_type as string;
+ return await all_middleware(cont, ctx.params.rest)(ctx, next);
});
}
}
diff --git a/packages/server/src/route/comic.ts b/packages/server/src/route/comic.ts
index 652f9bc..8606268 100644
--- a/packages/server/src/route/comic.ts
+++ b/packages/server/src/route/comic.ts
@@ -1,14 +1,14 @@
-import { Context, DefaultContext, DefaultState, Next } from "koa";
+import { type Context, DefaultContext, DefaultState, Next } from "koa";
import Router from "koa-router";
-import { createReadableStreamFromZip, entriesByNaturalOrder, readZip, ZipAsync } from "../util/zipwrap";
-import { ContentContext } from "./context";
+import { createReadableStreamFromZip, entriesByNaturalOrder, readZip, type ZipAsync } from "../util/zipwrap";
+import type { ContentContext } from "./context";
import { since_last_modified } from "./util";
/**
* zip stream cache.
*/
-let ZipStreamCache: { [path: string]: [ZipAsync, number] } = {};
+const ZipStreamCache: { [path: string]: [ZipAsync, number] } = {};
async function acquireZip(path: string) {
if (!(path in ZipStreamCache)) {
@@ -16,12 +16,11 @@ async function acquireZip(path: string) {
ZipStreamCache[path] = [ret, 1];
// console.log(`acquire ${path} 1`);
return ret;
- } else {
+ }
const [ret, refCount] = ZipStreamCache[path];
ZipStreamCache[path] = [ret, refCount + 1];
// console.log(`acquire ${path} ${refCount + 1}`);
return ret;
- }
}
function releaseZip(path: string) {
@@ -40,7 +39,7 @@ function releaseZip(path: string) {
async function renderZipImage(ctx: Context, path: string, page: number) {
const image_ext = ["gif", "png", "jpeg", "bmp", "webp", "jpg"];
// console.log(`opened ${page}`);
- let zip = await acquireZip(path);
+ const zip = await acquireZip(path);
const entries = (await entriesByNaturalOrder(zip)).filter((x) => {
const ext = x.name.split(".").pop();
return ext !== undefined && image_ext.includes(ext);
@@ -84,7 +83,7 @@ export class ComicRouter extends Router {
await renderZipImage(ctx, ctx.state.location.path, 0);
});
this.get("/:page(\\d+)", async (ctx, next) => {
- const page = Number.parseInt(ctx.params["page"]);
+ const page = Number.parseInt(ctx.params.page);
await renderZipImage(ctx, ctx.state.location.path, page);
});
this.get("/thumbnail", async (ctx, next) => {
diff --git a/packages/server/src/route/contents.ts b/packages/server/src/route/contents.ts
index dbb0cf6..af995d0 100644
--- a/packages/server/src/route/contents.ts
+++ b/packages/server/src/route/contents.ts
@@ -1,22 +1,22 @@
-import { Context, Next } from "koa";
+import type { Context, Next } from "koa";
import Router from "koa-router";
-import { join } from "path";
-import { Document, DocumentAccessor, isDocBody } from "../model/doc";
-import { QueryListOption } from "../model/doc";
+import { join } from "node:path";
+import { type Document, type DocumentAccessor, isDocBody } from "../model/doc";
+import type { QueryListOption } from "../model/doc";
import {
AdminOnlyMiddleware as AdminOnly,
createPermissionCheckMiddleware as PerCheck,
Permission as Per,
} from "../permission/permission";
import { AllContentRouter } from "./all";
-import { ContentLocation } from "./context";
+import type { ContentLocation } from "./context";
import { sendError } from "./error_handler";
import { ParseQueryArgString, ParseQueryArray, ParseQueryBoolean, ParseQueryNumber } from "./util";
const ContentIDHandler = (controller: DocumentAccessor) => async (ctx: Context, next: Next) => {
- const num = Number.parseInt(ctx.params["num"]);
- let document = await controller.findById(num, true);
- if (document == undefined) {
+ const num = Number.parseInt(ctx.params.num);
+ const document = await controller.findById(num, true);
+ if (document === undefined) {
return sendError(404, "document does not exist.");
}
ctx.body = document;
@@ -24,28 +24,22 @@ const ContentIDHandler = (controller: DocumentAccessor) => async (ctx: Context,
console.log(document.additional);
};
const ContentTagIDHandler = (controller: DocumentAccessor) => async (ctx: Context, next: Next) => {
- const num = Number.parseInt(ctx.params["num"]);
- let document = await controller.findById(num, true);
- if (document == undefined) {
+ const num = Number.parseInt(ctx.params.num);
+ const document = await controller.findById(num, true);
+ if (document === undefined) {
return sendError(404, "document does not exist.");
}
ctx.body = document.tags;
ctx.type = "json";
};
const ContentQueryHandler = (controller: DocumentAccessor) => async (ctx: Context, next: Next) => {
- let query_limit = ctx.query["limit"];
- let query_cursor = ctx.query["cursor"];
- let query_word = ctx.query["word"];
- let query_content_type = ctx.query["content_type"];
- let query_offset = ctx.query["offset"];
- let query_use_offset = ctx.query["use_offset"];
- if (
- query_limit instanceof Array ||
- query_cursor instanceof Array ||
- query_word instanceof Array ||
- query_content_type instanceof Array ||
- query_offset instanceof Array ||
- query_use_offset instanceof Array
+ const query_limit = ctx.query.limit;
+ const query_cursor = ctx.query.cursor;
+ const query_word = ctx.query.word;
+ const query_content_type = ctx.query.content_type;
+ const query_offset = ctx.query.offset;
+ const query_use_offset = ctx.query.use_offset;
+ if (Array.isArray(query_limit) ||Array.isArray(query_cursor) ||Array.isArray(query_word) ||Array.isArray(query_content_type) ||Array.isArray(query_offset) ||Array.isArray(query_use_offset)
) {
return sendError(400, "paramter can not be array");
}
@@ -54,10 +48,10 @@ const ContentQueryHandler = (controller: DocumentAccessor) => async (ctx: Contex
const word = ParseQueryArgString(query_word);
const content_type = ParseQueryArgString(query_content_type);
const offset = ParseQueryNumber(query_offset);
- if (limit === NaN || cursor === NaN || offset === NaN) {
+ if (Number.isNaN(limit) || Number.isNaN(cursor) || Number.isNaN(offset)) {
return sendError(400, "parameter limit, cursor or offset is not a number");
}
- const allow_tag = ParseQueryArray(ctx.query["allow_tag"]);
+ const allow_tag = ParseQueryArray(ctx.query.allow_tag);
const [ok, use_offset] = ParseQueryBoolean(query_use_offset);
if (!ok) {
return sendError(400, "use_offset must be true or false.");
@@ -72,12 +66,12 @@ const ContentQueryHandler = (controller: DocumentAccessor) => async (ctx: Contex
use_offset: use_offset,
content_type: content_type,
};
- let document = await controller.findList(option);
+ const document = await controller.findList(option);
ctx.body = document;
ctx.type = "json";
};
const UpdateContentHandler = (controller: DocumentAccessor) => async (ctx: Context, next: Next) => {
- const num = Number.parseInt(ctx.params["num"]);
+ const num = Number.parseInt(ctx.params.num);
if (ctx.request.type !== "json") {
return sendError(400, "update fail. invalid document type: it is not json.");
@@ -95,9 +89,9 @@ const UpdateContentHandler = (controller: DocumentAccessor) => async (ctx: Conte
};
const AddTagHandler = (controller: DocumentAccessor) => async (ctx: Context, next: Next) => {
- let tag_name = ctx.params["tag"];
- const num = Number.parseInt(ctx.params["num"]);
- if (typeof tag_name === undefined) {
+ let tag_name = ctx.params.tag;
+ const num = Number.parseInt(ctx.params.num);
+ if (typeof tag_name === "undefined") {
return sendError(400, "??? Unreachable");
}
tag_name = String(tag_name);
@@ -110,9 +104,9 @@ const AddTagHandler = (controller: DocumentAccessor) => async (ctx: Context, nex
ctx.type = "json";
};
const DelTagHandler = (controller: DocumentAccessor) => async (ctx: Context, next: Next) => {
- let tag_name = ctx.params["tag"];
- const num = Number.parseInt(ctx.params["num"]);
- if (typeof tag_name === undefined) {
+ let tag_name = ctx.params.tag;
+ const num = Number.parseInt(ctx.params.num);
+ if (typeof tag_name === "undefined") {
return sendError(400, "?? Unreachable");
}
tag_name = String(tag_name);
@@ -125,22 +119,22 @@ const DelTagHandler = (controller: DocumentAccessor) => async (ctx: Context, nex
ctx.type = "json";
};
const DeleteContentHandler = (controller: DocumentAccessor) => async (ctx: Context, next: Next) => {
- const num = Number.parseInt(ctx.params["num"]);
+ const num = Number.parseInt(ctx.params.num);
const r = await controller.del(num);
ctx.body = JSON.stringify(r);
ctx.type = "json";
};
const ContentHandler = (controller: DocumentAccessor) => async (ctx: Context, next: Next) => {
- const num = Number.parseInt(ctx.params["num"]);
- let document = await controller.findById(num, true);
- if (document == undefined) {
+ const num = Number.parseInt(ctx.params.num);
+ const document = await controller.findById(num, true);
+ if (document === undefined) {
return sendError(404, "document does not exist.");
}
if (document.deleted_at !== null) {
return sendError(404, "document has been removed.");
}
const path = join(document.basepath, document.filename);
- ctx.state["location"] = {
+ ctx.state.location = {
path: path,
type: document.content_type,
additional: document.additional,
diff --git a/packages/server/src/route/error_handler.ts b/packages/server/src/route/error_handler.ts
index deb1da9..a329645 100644
--- a/packages/server/src/route/error_handler.ts
+++ b/packages/server/src/route/error_handler.ts
@@ -1,4 +1,4 @@
-import { Context, Next } from "koa";
+import type { Context, Next } from "koa";
export interface ErrorFormat {
code: number;
diff --git a/packages/server/src/route/tags.ts b/packages/server/src/route/tags.ts
index edbbb75..da0f535 100644
--- a/packages/server/src/route/tags.ts
+++ b/packages/server/src/route/tags.ts
@@ -1,13 +1,13 @@
-import { Context, Next } from "koa";
-import Router, { RouterContext } from "koa-router";
-import { TagAccessor } from "../model/tag";
+import { type Context, Next } from "koa";
+import Router, { type RouterContext } from "koa-router";
+import type { TagAccessor } from "../model/tag";
import { createPermissionCheckMiddleware as PerCheck, Permission } from "../permission/permission";
import { sendError } from "./error_handler";
export function getTagRounter(tagController: TagAccessor) {
- let router = new Router();
+ const router = new Router();
router.get("/", PerCheck(Permission.QueryContent), async (ctx: Context) => {
- if (ctx.query["withCount"]) {
+ if (ctx.query.withCount) {
const c = await tagController.getAllTagCount();
ctx.body = c;
} else {
@@ -17,7 +17,7 @@ export function getTagRounter(tagController: TagAccessor) {
ctx.type = "json";
});
router.get("/:tag_name", PerCheck(Permission.QueryContent), async (ctx: RouterContext) => {
- const tag_name = ctx.params["tag_name"];
+ const tag_name = ctx.params.tag_name;
const c = await tagController.getTagByName(tag_name);
if (!c) {
sendError(404, "tags not found");
diff --git a/packages/server/src/route/util.ts b/packages/server/src/route/util.ts
index c07f9c4..bdc39f0 100644
--- a/packages/server/src/route/util.ts
+++ b/packages/server/src/route/util.ts
@@ -1,13 +1,13 @@
-import { Context } from "koa";
+import type { Context } from "koa";
export function ParseQueryNumber(s: string[] | string | undefined): number | undefined {
if (s === undefined) return undefined;
- else if (typeof s === "object") return undefined;
- else return Number.parseInt(s);
+ if (typeof s === "object") return undefined;
+ return Number.parseInt(s);
}
export function ParseQueryArray(s: string[] | string | undefined) {
- s = s ?? [];
- const r = s instanceof Array ? s : [s];
+ const input = s ?? [];
+ const r = Array.isArray(input) ? input : [input];
return r.map((x) => decodeURIComponent(x));
}
export function ParseQueryArgString(s: string[] | string | undefined) {
diff --git a/packages/server/src/route/video.ts b/packages/server/src/route/video.ts
index cc96367..198adc2 100644
--- a/packages/server/src/route/video.ts
+++ b/packages/server/src/route/video.ts
@@ -1,7 +1,7 @@
-import { createReadStream, promises } from "fs";
-import { Context } from "koa";
+import { createReadStream, promises } from "node:fs";
+import type { Context } from "koa";
import Router from "koa-router";
-import { ContentContext } from "./context";
+import type { ContentContext } from "./context";
export async function renderVideo(ctx: Context, path: string) {
const ext = path.trim().split(".").pop();
@@ -27,7 +27,7 @@ export async function renderVideo(ctx: Context, path: string) {
}
ctx.status = 200;
ctx.length = stat.size;
- let stream = createReadStream(path);
+ const stream = createReadStream(path);
ctx.body = stream;
} else {
const m = range_text.match(/^bytes=(\d+)-(\d*)/);
@@ -35,8 +35,8 @@ export async function renderVideo(ctx: Context, path: string) {
ctx.status = 416;
return;
}
- start = parseInt(m[1]);
- end = m[2].length > 0 ? parseInt(m[2]) : start + 1024 * 1024;
+ start = Number.parseInt(m[1]);
+ end = m[2].length > 0 ? Number.parseInt(m[2]) : start + 1024 * 1024;
end = Math.min(end, stat.size - 1);
if (start > end) {
ctx.status = 416;
diff --git a/packages/server/src/server.ts b/packages/server/src/server.ts
index a9cdc81..2b11d9e 100644
--- a/packages/server/src/server.ts
+++ b/packages/server/src/server.ts
@@ -5,18 +5,21 @@ import { connectDB } from "./database";
import { createDiffRouter, DiffManager } from "./diff/mod";
import { get_setting, SettingConfig } from "./SettingConfig";
-import { createReadStream, readFileSync } from "fs";
+import { createReadStream, readFileSync } from "node:fs";
import bodyparser from "koa-bodyparser";
-import { createKnexDocumentAccessor, createKnexTagController, createKnexUserController } from "./db/mod";
+import { createSqliteDocumentAccessor, createSqliteTagController, createSqliteUserController } from "./db/mod";
import { createLoginRouter, createUserMiddleWare, getAdmin, isAdminFirst } from "./login";
import getContentRouter from "./route/contents";
import { error_handler } from "./route/error_handler";
-import { createInterface as createReadlineInterface } from "readline";
+import { createInterface as createReadlineInterface } from "node:readline";
import { createComicWatcher } from "./diff/watcher/comic_watcher";
-import { DocumentAccessor, TagAccessor, UserAccessor } from "./model/mod";
+import type { DocumentAccessor, TagAccessor, UserAccessor } from "./model/mod";
import { getTagRounter } from "./route/tags";
+import { config } from "dotenv";
+config();
+
class ServerApplication {
readonly userController: UserAccessor;
readonly documentController: DocumentAccessor;
@@ -61,12 +64,12 @@ class ServerApplication {
app.use(error_handler);
app.use(createUserMiddleWare(this.userController));
- let diff_router = createDiffRouter(this.diffManger);
+ const diff_router = createDiffRouter(this.diffManger);
this.diffManger.register("comic", createComicWatcher());
console.log("setup router");
- let router = new Router();
+ const router = new Router();
router.use("/api/(.*)", async (ctx, next) => {
// For CORS
ctx.res.setHeader("access-control-allow-origin", "*");
@@ -92,12 +95,12 @@ class ServerApplication {
router.use("/user", login_router.routes());
router.use("/user", login_router.allowedMethods());
- if (setting.mode == "development") {
+ if (setting.mode === "development") {
let mm_count = 0;
app.use(async (ctx, next) => {
console.log(`==========================${mm_count++}`);
const ip = ctx.get("X-Real-IP") ?? ctx.ip;
- const fromClient = ctx.state["user"].username === "" ? ip : ctx.state["user"].username;
+ const fromClient = ctx.state.user.username === "" ? ip : ctx.state.user.username;
console.log(`${fromClient} : ${ctx.method} ${ctx.url}`);
await next();
// console.log(`404`);
@@ -132,8 +135,9 @@ class ServerApplication {
}
private serve_with_meta_index(router: Router) {
const DocMiddleware = async (ctx: Koa.ParameterizedContext) => {
- const docId = Number.parseInt(ctx.params["id"]);
+ const docId = Number.parseInt(ctx.params.id);
const doc = await this.documentController.findById(docId, true);
+ // biome-ignore lint/suspicious/noImplicitAnyLet:
let meta;
if (doc === undefined) {
ctx.status = 404;
@@ -190,7 +194,7 @@ class ServerApplication {
}
private serve_static_file(router: Router) {
const static_file_server = (path: string, type: string) => {
- router.get("/" + path, async (ctx, next) => {
+ router.get(`/${path}`, async (ctx, next) => {
const setting = get_setting();
ctx.type = type;
ctx.body = createReadStream(path);
@@ -211,19 +215,19 @@ class ServerApplication {
}
}
start_server() {
- let setting = get_setting();
+ const setting = get_setting();
// todo : support https
console.log(`listen on http://${setting.localmode ? "localhost" : "0.0.0.0"}:${setting.port}`);
return this.app.listen(setting.port, setting.localmode ? "127.0.0.1" : "0.0.0.0");
}
static async createServer() {
const setting = get_setting();
- let db = await connectDB();
+ const db = await connectDB();
const app = new ServerApplication({
- userController: createKnexUserController(db),
- documentController: createKnexDocumentAccessor(db),
- tagController: createKnexTagController(db),
+ userController: createSqliteUserController(db),
+ documentController: createSqliteDocumentAccessor(db),
+ tagController: createSqliteTagController(db),
});
await app.setup();
return app;
diff --git a/packages/server/src/types/db.d.ts b/packages/server/src/types/db.d.ts
deleted file mode 100644
index 7e1a63e..0000000
--- a/packages/server/src/types/db.d.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-import { Knex } from "knex";
-
-declare module "knex" {
- interface Tables {
- tags: {
- name: string;
- description?: string;
- };
- users: {
- username: string;
- password_hash: string;
- password_salt: string;
- };
- document: {
- id: number;
- title: string;
- content_type: string;
- basepath: string;
- filename: string;
- created_at: number;
- deleted_at: number | null;
- content_hash: string;
- additional: string | null;
- };
- doc_tag_relation: {
- doc_id: number;
- tag_name: string;
- };
- permissions: {
- username: string;
- name: string;
- };
- }
-}
diff --git a/packages/server/src/util/configRW.ts b/packages/server/src/util/configRW.ts
index c769c9c..e39d1b4 100644
--- a/packages/server/src/util/configRW.ts
+++ b/packages/server/src/util/configRW.ts
@@ -1,7 +1,7 @@
-import { existsSync, promises as fs, readFileSync, writeFileSync } from "fs";
+import { existsSync, promises as fs, readFileSync, writeFileSync } from "node:fs";
import { validate } from "jsonschema";
-export class ConfigManager {
+export class ConfigManager {
path: string;
default_config: T;
config: T | null;
diff --git a/packages/server/src/util/type_check.ts b/packages/server/src/util/type_check.ts
index 160eab9..6ee6d50 100644
--- a/packages/server/src/util/type_check.ts
+++ b/packages/server/src/util/type_check.ts
@@ -1,13 +1,15 @@
-export function check_type(obj: any, check_proto: Record): obj is T {
+export function check_type(obj: unknown, check_proto: Record): obj is T {
+ if (typeof obj !== "object" || obj === null) return false;
for (const it in check_proto) {
let defined = check_proto[it];
if (defined === undefined) return false;
defined = defined.trim();
if (defined.endsWith("[]")) {
- if (!(obj[it] instanceof Array)) {
+ if (!Array.isArray((obj as Record)[it])) {
return false;
}
- } else if (defined !== typeof obj[it]) {
+ // biome-ignore lint/suspicious/useValidTypeof:
+ } else if (defined !== typeof (obj as Record)[it]) {
return false;
}
}
diff --git a/packages/server/src/util/zipwrap.ts b/packages/server/src/util/zipwrap.ts
index 46270e5..d1f6164 100644
--- a/packages/server/src/util/zipwrap.ts
+++ b/packages/server/src/util/zipwrap.ts
@@ -1,6 +1,6 @@
-import { ZipEntry } from "node-stream-zip";
+import type { ZipEntry } from "node-stream-zip";
-import { ReadStream } from "fs";
+import { ReadStream } from "node:fs";
import { orderBy } from "natural-orderby";
import StreamZip from "node-stream-zip";
diff --git a/packages/server/tsconfig.json b/packages/server/tsconfig.json
index 4b13120..e824e11 100644
--- a/packages/server/tsconfig.json
+++ b/packages/server/tsconfig.json
@@ -6,7 +6,7 @@
// "incremental": true, /* Enable incremental compilation */
"target": "esnext", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
- "lib": ["DOM", "ES6"], /* Specify library files to be included in the compilation. */
+ "lib": ["DOM", "ESNext"],
// "allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */
"jsx": "react", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index e92ae14..4db78ad 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -78,6 +78,9 @@ importers:
chokidar:
specifier: ^3.6.0
version: 3.6.0
+ dotenv:
+ specifier: ^16.4.5
+ version: 16.4.5
jsonwebtoken:
specifier: ^8.5.1
version: 8.5.1
@@ -103,6 +106,18 @@ importers:
specifier: ^1.3.0
version: 1.3.0
devDependencies:
+ '@swc-node/register':
+ specifier: ^1.9.0
+ version: 1.9.0(@swc/core@1.4.11)(@swc/types@0.1.6)(typescript@5.4.3)
+ '@swc/cli':
+ specifier: ^0.3.10
+ version: 0.3.10(@swc/core@1.4.11)(chokidar@3.6.0)
+ '@swc/core':
+ specifier: ^1.4.11
+ version: 1.4.11
+ '@types/better-sqlite3':
+ specifier: ^7.6.9
+ version: 7.6.9
'@types/jsonwebtoken':
specifier: ^8.5.9
version: 8.5.9
@@ -124,6 +139,12 @@ importers:
'@types/tiny-async-pool':
specifier: ^1.0.5
version: 1.0.5
+ dbtype:
+ specifier: workspace:^
+ version: link:../dbtype
+ nodemon:
+ specifier: ^3.1.0
+ version: 3.1.0
packages:
@@ -447,7 +468,7 @@ packages:
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dependencies:
ajv: 6.12.6
- debug: 4.3.4
+ debug: 4.3.4(supports-color@5.5.0)
espree: 9.6.1
globals: 13.24.0
ignore: 5.3.1
@@ -469,7 +490,7 @@ packages:
engines: {node: '>=10.10.0'}
dependencies:
'@humanwhocodes/object-schema': 2.0.2
- debug: 4.3.4
+ debug: 4.3.4(supports-color@5.5.0)
minimatch: 3.1.2
transitivePeerDependencies:
- supports-color
@@ -484,6 +505,20 @@ packages:
resolution: {integrity: sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==}
dev: true
+ /@mole-inc/bin-wrapper@8.0.1:
+ resolution: {integrity: sha512-sTGoeZnjI8N4KS+sW2AN95gDBErhAguvkw/tWdCjeM8bvxpz5lqrnd0vOJABA1A+Ic3zED7PYoLP/RANLgVotA==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+ dependencies:
+ bin-check: 4.1.0
+ bin-version-check: 5.1.0
+ content-disposition: 0.5.4
+ ext-name: 5.0.0
+ file-type: 17.1.6
+ filenamify: 5.1.1
+ got: 11.8.6
+ os-filter-obj: 2.0.0
+ dev: true
+
/@nodelib/fs.scandir@2.1.5:
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
engines: {node: '>= 8'}
@@ -609,6 +644,72 @@ packages:
dev: true
optional: true
+ /@sindresorhus/is@4.6.0:
+ resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==}
+ engines: {node: '>=10'}
+ dev: true
+
+ /@swc-node/core@1.13.0(@swc/core@1.4.11)(@swc/types@0.1.6):
+ resolution: {integrity: sha512-lFPD4nmy4ifAOVMChFjwlpXN5KQXvegqeyuzz1KQz42q1lf+cL3Qux1/GteGuZjh8HC+Rj1RdNrHpE/MCfJSTw==}
+ engines: {node: '>= 10'}
+ peerDependencies:
+ '@swc/core': '>= 1.3'
+ '@swc/types': '>= 0.1'
+ dependencies:
+ '@swc/core': 1.4.11
+ '@swc/types': 0.1.6
+ dev: true
+
+ /@swc-node/register@1.9.0(@swc/core@1.4.11)(@swc/types@0.1.6)(typescript@5.4.3):
+ resolution: {integrity: sha512-i0iYInD4q5v3xQC6bKvs0QtfUxu197CU5qKALmpxEqTYs7sIhQ7KFLe3kP+eAR4gRkJTvAgjQgrokXLN2jZrOw==}
+ peerDependencies:
+ '@swc/core': '>= 1.3'
+ typescript: '>= 4.3'
+ dependencies:
+ '@swc-node/core': 1.13.0(@swc/core@1.4.11)(@swc/types@0.1.6)
+ '@swc-node/sourcemap-support': 0.5.0
+ '@swc/core': 1.4.11
+ colorette: 2.0.20
+ debug: 4.3.4(supports-color@5.5.0)
+ pirates: 4.0.6
+ tslib: 2.6.2
+ typescript: 5.4.3
+ transitivePeerDependencies:
+ - '@swc/types'
+ - supports-color
+ dev: true
+
+ /@swc-node/sourcemap-support@0.5.0:
+ resolution: {integrity: sha512-fbhjL5G0YvFoWwNhWleuBUfotiX+USiA9oJqu9STFw+Hb0Cgnddn+HVS/K5fI45mn92e8V+cHD2jgFjk4w2T9Q==}
+ dependencies:
+ source-map-support: 0.5.21
+ tslib: 2.6.2
+ dev: true
+
+ /@swc/cli@0.3.10(@swc/core@1.4.11)(chokidar@3.6.0):
+ resolution: {integrity: sha512-YWfYo9kXdbmIuGwIPth9geKgb0KssCMTdZa44zAN5KoqcuCP2rTW9s60heQDSRNpbtCmUr7BKF1VivsoHXrvrQ==}
+ engines: {node: '>= 16.14.0'}
+ hasBin: true
+ peerDependencies:
+ '@swc/core': ^1.2.66
+ chokidar: ^3.5.1
+ peerDependenciesMeta:
+ chokidar:
+ optional: true
+ dependencies:
+ '@mole-inc/bin-wrapper': 8.0.1
+ '@swc/core': 1.4.11
+ '@swc/counter': 0.1.3
+ chokidar: 3.6.0
+ commander: 8.3.0
+ fast-glob: 3.3.2
+ minimatch: 9.0.3
+ piscina: 4.4.0
+ semver: 7.6.0
+ slash: 3.0.0
+ source-map: 0.7.4
+ dev: true
+
/@swc/core-darwin-arm64@1.4.11:
resolution: {integrity: sha512-C1j1Qp/IHSelVWdEnT7f0iONWxQz6FAqzjCF2iaL+0vFg4V5f2nlgrueY8vj5pNNzSGhrAlxsMxEIp4dj1MXkg==}
engines: {node: '>=10'}
@@ -734,6 +835,17 @@ packages:
'@swc/counter': 0.1.3
dev: true
+ /@szmarczak/http-timer@4.0.6:
+ resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==}
+ engines: {node: '>=10'}
+ dependencies:
+ defer-to-connect: 2.0.1
+ dev: true
+
+ /@tokenizer/token@0.3.0:
+ resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==}
+ dev: true
+
/@types/accepts@1.3.7:
resolution: {integrity: sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==}
dependencies:
@@ -753,6 +865,15 @@ packages:
'@types/node': 14.18.63
dev: true
+ /@types/cacheable-request@6.0.3:
+ resolution: {integrity: sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==}
+ dependencies:
+ '@types/http-cache-semantics': 4.0.4
+ '@types/keyv': 3.1.4
+ '@types/node': 14.18.63
+ '@types/responselike': 1.0.3
+ dev: true
+
/@types/connect@3.4.38:
resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==}
dependencies:
@@ -798,6 +919,10 @@ packages:
resolution: {integrity: sha512-4+tE/lwdAahgZT1g30Jkdm9PzFRde0xwxBNUyRsCitRvCQB90iuA2uJYdUnhnANRcqGXaWOGY4FEoxeElNAK2g==}
dev: true
+ /@types/http-cache-semantics@4.0.4:
+ resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==}
+ dev: true
+
/@types/http-errors@2.0.4:
resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==}
dev: true
@@ -816,6 +941,12 @@ packages:
resolution: {integrity: sha512-lZuNAY9xeJt7Bx4t4dx0rYCDqGPW8RXhQZK1td7d4H6E9zYbLoOtjBvfwdTKpsyxQI/2jv+armjX/RW+ZNpXOQ==}
dev: true
+ /@types/keyv@3.1.4:
+ resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==}
+ dependencies:
+ '@types/node': 14.18.63
+ dev: true
+
/@types/koa-bodyparser@4.3.12:
resolution: {integrity: sha512-hKMmRMVP889gPIdLZmmtou/BijaU1tHPyMNmcK7FAHAdATnRcGQQy78EqTTxLH1D4FTsrxIzklAQCso9oGoebQ==}
dependencies:
@@ -885,6 +1016,12 @@ packages:
csstype: 3.1.3
dev: true
+ /@types/responselike@1.0.3:
+ resolution: {integrity: sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==}
+ dependencies:
+ '@types/node': 14.18.63
+ dev: true
+
/@types/scheduler@0.23.0:
resolution: {integrity: sha512-YIoDCTH3Af6XM5VuwGG/QL/CJqga1Zm3NkU3HZ4ZHK2fRMPYP1VczsTUqtsf43PH/iJNVlPHAo2oWX7BSdB2Hw==}
dev: true
@@ -929,7 +1066,7 @@ packages:
'@typescript-eslint/type-utils': 7.4.0(eslint@8.57.0)(typescript@5.4.3)
'@typescript-eslint/utils': 7.4.0(eslint@8.57.0)(typescript@5.4.3)
'@typescript-eslint/visitor-keys': 7.4.0
- debug: 4.3.4
+ debug: 4.3.4(supports-color@5.5.0)
eslint: 8.57.0
graphemer: 1.4.0
ignore: 5.3.1
@@ -955,7 +1092,7 @@ packages:
'@typescript-eslint/types': 7.4.0
'@typescript-eslint/typescript-estree': 7.4.0(typescript@5.4.3)
'@typescript-eslint/visitor-keys': 7.4.0
- debug: 4.3.4
+ debug: 4.3.4(supports-color@5.5.0)
eslint: 8.57.0
typescript: 5.4.3
transitivePeerDependencies:
@@ -982,7 +1119,7 @@ packages:
dependencies:
'@typescript-eslint/typescript-estree': 7.4.0(typescript@5.4.3)
'@typescript-eslint/utils': 7.4.0(eslint@8.57.0)(typescript@5.4.3)
- debug: 4.3.4
+ debug: 4.3.4(supports-color@5.5.0)
eslint: 8.57.0
ts-api-utils: 1.3.0(typescript@5.4.3)
typescript: 5.4.3
@@ -1006,7 +1143,7 @@ packages:
dependencies:
'@typescript-eslint/types': 7.4.0
'@typescript-eslint/visitor-keys': 7.4.0
- debug: 4.3.4
+ debug: 4.3.4(supports-color@5.5.0)
globby: 11.1.0
is-glob: 4.0.3
minimatch: 9.0.3
@@ -1064,6 +1201,10 @@ packages:
engines: {bun: '>=0.7.0', deno: '>=1.0.0', node: '>=16.5.0'}
dev: false
+ /abbrev@1.1.1:
+ resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==}
+ dev: true
+
/accepts@1.3.8:
resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==}
engines: {node: '>= 0.6'}
@@ -1120,7 +1261,10 @@ packages:
dependencies:
normalize-path: 3.0.0
picomatch: 2.3.1
- dev: false
+
+ /arch@2.2.0:
+ resolution: {integrity: sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==}
+ dev: true
/argparse@2.0.1:
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
@@ -1145,10 +1289,34 @@ packages:
bindings: 1.5.0
prebuild-install: 7.1.2
+ /bin-check@4.1.0:
+ resolution: {integrity: sha512-b6weQyEUKsDGFlACWSIOfveEnImkJyK/FGW6FAG42loyoquvjdtOIqO6yBFzHyqyVVhNgNkQxxx09SFLK28YnA==}
+ engines: {node: '>=4'}
+ dependencies:
+ execa: 0.7.0
+ executable: 4.1.1
+ dev: true
+
+ /bin-version-check@5.1.0:
+ resolution: {integrity: sha512-bYsvMqJ8yNGILLz1KP9zKLzQ6YpljV3ln1gqhuLkUtyfGi3qXKGuK2p+U4NAvjVFzDFiBBtOpCOSFNuYYEGZ5g==}
+ engines: {node: '>=12'}
+ dependencies:
+ bin-version: 6.0.0
+ semver: 7.6.0
+ semver-truncate: 3.0.0
+ dev: true
+
+ /bin-version@6.0.0:
+ resolution: {integrity: sha512-nk5wEsP4RiKjG+vF+uG8lFsEn4d7Y6FVDamzzftSunXOoOcOOkzcWdKVlGgFFwlUQCj63SgnUkLLGF8v7lufhw==}
+ engines: {node: '>=12'}
+ dependencies:
+ execa: 5.1.1
+ find-versions: 5.1.0
+ dev: true
+
/binary-extensions@2.3.0:
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
engines: {node: '>=8'}
- dev: false
/bindings@1.5.0:
resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==}
@@ -1185,6 +1353,10 @@ packages:
resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==}
dev: false
+ /buffer-from@1.1.2:
+ resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
+ dev: true
+
/buffer@5.7.1:
resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
dependencies:
@@ -1204,6 +1376,24 @@ packages:
ylru: 1.3.2
dev: false
+ /cacheable-lookup@5.0.4:
+ resolution: {integrity: sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==}
+ engines: {node: '>=10.6.0'}
+ dev: true
+
+ /cacheable-request@7.0.4:
+ resolution: {integrity: sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==}
+ engines: {node: '>=8'}
+ dependencies:
+ clone-response: 1.0.3
+ get-stream: 5.2.0
+ http-cache-semantics: 4.1.1
+ keyv: 4.5.4
+ lowercase-keys: 2.0.0
+ normalize-url: 6.1.0
+ responselike: 2.0.1
+ dev: true
+
/call-bind@1.0.7:
resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==}
engines: {node: '>= 0.4'}
@@ -1250,11 +1440,16 @@ packages:
readdirp: 3.6.0
optionalDependencies:
fsevents: 2.3.3
- dev: false
/chownr@1.1.4:
resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==}
+ /clone-response@1.0.3:
+ resolution: {integrity: sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==}
+ dependencies:
+ mimic-response: 1.0.1
+ dev: true
+
/co-body@6.1.0:
resolution: {integrity: sha512-m7pOT6CdLN7FuXUcpuz/8lfQ/L77x8SchHCF4G0RBTJO20Wzmhn5Sp4/5WsKy8OSpifBSUrmg83qEqaDHdyFuQ==}
dependencies:
@@ -1290,6 +1485,15 @@ packages:
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
dev: true
+ /colorette@2.0.20:
+ resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==}
+ dev: true
+
+ /commander@8.3.0:
+ resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==}
+ engines: {node: '>= 12'}
+ dev: true
+
/concat-map@0.0.1:
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
dev: true
@@ -1299,7 +1503,6 @@ packages:
engines: {node: '>= 0.6'}
dependencies:
safe-buffer: 5.2.1
- dev: false
/content-type@1.0.5:
resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==}
@@ -1318,6 +1521,14 @@ packages:
resolution: {integrity: sha512-3DdaFaU/Zf1AnpLiFDeNCD4TOWe3Zl2RZaTzUvWiIk5ERzcCodOE20Vqq4fzCbNoHURFHT4/us/Lfq+S2zyY4w==}
dev: false
+ /cross-spawn@5.1.0:
+ resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==}
+ dependencies:
+ lru-cache: 4.1.5
+ shebang-command: 1.2.0
+ which: 1.3.1
+ dev: true
+
/cross-spawn@7.0.3:
resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
engines: {node: '>= 8'}
@@ -1331,7 +1542,7 @@ packages:
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
dev: true
- /debug@4.3.4:
+ /debug@4.3.4(supports-color@5.5.0):
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
engines: {node: '>=6.0'}
peerDependencies:
@@ -1341,6 +1552,7 @@ packages:
optional: true
dependencies:
ms: 2.1.2
+ supports-color: 5.5.0
/decompress-response@6.0.0:
resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==}
@@ -1360,6 +1572,11 @@ packages:
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
dev: true
+ /defer-to-connect@2.0.1:
+ resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==}
+ engines: {node: '>=10'}
+ dev: true
+
/define-data-property@1.1.4:
resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==}
engines: {node: '>= 0.4'}
@@ -1421,7 +1638,6 @@ packages:
/dotenv@16.4.5:
resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==}
engines: {node: '>=12'}
- dev: true
/ecdsa-sig-formatter@1.0.11:
resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==}
@@ -1500,6 +1716,11 @@ packages:
engines: {node: '>=10'}
dev: true
+ /escape-string-regexp@5.0.0:
+ resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==}
+ engines: {node: '>=12'}
+ dev: true
+
/eslint-plugin-react-hooks@4.6.0(eslint@8.57.0):
resolution: {integrity: sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==}
engines: {node: '>=10'}
@@ -1546,7 +1767,7 @@ packages:
ajv: 6.12.6
chalk: 4.1.2
cross-spawn: 7.0.3
- debug: 4.3.4
+ debug: 4.3.4(supports-color@5.5.0)
doctrine: 3.0.0
escape-string-regexp: 4.0.0
eslint-scope: 7.2.2
@@ -1610,10 +1831,60 @@ packages:
engines: {node: '>=0.10.0'}
dev: true
+ /execa@0.7.0:
+ resolution: {integrity: sha512-RztN09XglpYI7aBBrJCPW95jEH7YF1UEPOoX9yDhUTPdp7mK+CQvnLTuD10BNXZ3byLTu2uehZ8EcKT/4CGiFw==}
+ engines: {node: '>=4'}
+ dependencies:
+ cross-spawn: 5.1.0
+ get-stream: 3.0.0
+ is-stream: 1.1.0
+ npm-run-path: 2.0.2
+ p-finally: 1.0.0
+ signal-exit: 3.0.7
+ strip-eof: 1.0.0
+ dev: true
+
+ /execa@5.1.1:
+ resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==}
+ engines: {node: '>=10'}
+ dependencies:
+ cross-spawn: 7.0.3
+ get-stream: 6.0.1
+ human-signals: 2.1.0
+ is-stream: 2.0.1
+ merge-stream: 2.0.0
+ npm-run-path: 4.0.1
+ onetime: 5.1.2
+ signal-exit: 3.0.7
+ strip-final-newline: 2.0.0
+ dev: true
+
+ /executable@4.1.1:
+ resolution: {integrity: sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==}
+ engines: {node: '>=4'}
+ dependencies:
+ pify: 2.3.0
+ dev: true
+
/expand-template@2.0.3:
resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==}
engines: {node: '>=6'}
+ /ext-list@2.2.2:
+ resolution: {integrity: sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA==}
+ engines: {node: '>=0.10.0'}
+ dependencies:
+ mime-db: 1.52.0
+ dev: true
+
+ /ext-name@5.0.0:
+ resolution: {integrity: sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ==}
+ engines: {node: '>=4'}
+ dependencies:
+ ext-list: 2.2.2
+ sort-keys-length: 1.0.1
+ dev: true
+
/fast-deep-equal@3.1.3:
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
dev: true
@@ -1650,9 +1921,32 @@ packages:
flat-cache: 3.2.0
dev: true
+ /file-type@17.1.6:
+ resolution: {integrity: sha512-hlDw5Ev+9e883s0pwUsuuYNu4tD7GgpUnOvykjv1Gya0ZIjuKumthDRua90VUn6/nlRKAjcxLUnHNTIUWwWIiw==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+ dependencies:
+ readable-web-to-node-stream: 3.0.2
+ strtok3: 7.0.0
+ token-types: 5.0.1
+ dev: true
+
/file-uri-to-path@1.0.0:
resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==}
+ /filename-reserved-regex@3.0.0:
+ resolution: {integrity: sha512-hn4cQfU6GOT/7cFHXBqeBg2TbrMBgdD0kcjLhvSQYYwm3s4B6cjvBfb7nBALJLAXqmU5xajSa7X2NnUud/VCdw==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+ dev: true
+
+ /filenamify@5.1.1:
+ resolution: {integrity: sha512-M45CbrJLGACfrPOkrTp3j2EcO9OBkKUYME0eiqOCa7i2poaklU0jhlIaMlr8ijLorT0uLAzrn3qXOp5684CkfA==}
+ engines: {node: '>=12.20'}
+ dependencies:
+ filename-reserved-regex: 3.0.0
+ strip-outer: 2.0.0
+ trim-repeated: 2.0.0
+ dev: true
+
/fill-range@7.0.1:
resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
engines: {node: '>=8'}
@@ -1667,6 +1961,13 @@ packages:
path-exists: 4.0.0
dev: true
+ /find-versions@5.1.0:
+ resolution: {integrity: sha512-+iwzCJ7C5v5KgcBuueqVoNiHVoQpwiUK5XFLjf0affFTep+Wcw93tPvmb8tqujDNmzhBDPddnWV/qgWSXgq+Hg==}
+ engines: {node: '>=12'}
+ dependencies:
+ semver-regex: 4.0.5
+ dev: true
+
/flat-cache@3.2.0:
resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==}
engines: {node: ^10.12.0 || >=12.0.0}
@@ -1713,6 +2014,23 @@ packages:
hasown: 2.0.2
dev: false
+ /get-stream@3.0.0:
+ resolution: {integrity: sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==}
+ engines: {node: '>=4'}
+ dev: true
+
+ /get-stream@5.2.0:
+ resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==}
+ engines: {node: '>=8'}
+ dependencies:
+ pump: 3.0.0
+ dev: true
+
+ /get-stream@6.0.1:
+ resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==}
+ engines: {node: '>=10'}
+ dev: true
+
/git-diff@2.0.6:
resolution: {integrity: sha512-/Iu4prUrydE3Pb3lCBMbcSNIf81tgGt0W1ZwknnyF62t3tHmtiJTRj0f+1ZIhp3+Rh0ktz1pJVoa7ZXUCskivA==}
engines: {node: '>= 4.8.0'}
@@ -1776,6 +2094,23 @@ packages:
get-intrinsic: 1.2.4
dev: false
+ /got@11.8.6:
+ resolution: {integrity: sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==}
+ engines: {node: '>=10.19.0'}
+ dependencies:
+ '@sindresorhus/is': 4.6.0
+ '@szmarczak/http-timer': 4.0.6
+ '@types/cacheable-request': 6.0.3
+ '@types/responselike': 1.0.3
+ cacheable-lookup: 5.0.4
+ cacheable-request: 7.0.4
+ decompress-response: 6.0.0
+ http2-wrapper: 1.0.3
+ lowercase-keys: 2.0.0
+ p-cancelable: 2.1.1
+ responselike: 2.0.1
+ dev: true
+
/graphemer@1.4.0:
resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
dev: true
@@ -1783,7 +2118,6 @@ packages:
/has-flag@3.0.0:
resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
engines: {node: '>=4'}
- dev: true
/has-flag@4.0.0:
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
@@ -1827,6 +2161,10 @@ packages:
http-errors: 1.8.1
dev: false
+ /http-cache-semantics@4.1.1:
+ resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==}
+ dev: true
+
/http-errors@1.8.1:
resolution: {integrity: sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==}
engines: {node: '>= 0.6'}
@@ -1849,6 +2187,19 @@ packages:
toidentifier: 1.0.1
dev: false
+ /http2-wrapper@1.0.3:
+ resolution: {integrity: sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==}
+ engines: {node: '>=10.19.0'}
+ dependencies:
+ quick-lru: 5.1.1
+ resolve-alpn: 1.2.1
+ dev: true
+
+ /human-signals@2.1.0:
+ resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==}
+ engines: {node: '>=10.17.0'}
+ dev: true
+
/iconv-lite@0.4.24:
resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
engines: {node: '>=0.10.0'}
@@ -1859,6 +2210,10 @@ packages:
/ieee754@1.2.1:
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
+ /ignore-by-default@1.0.1:
+ resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==}
+ dev: true
+
/ignore@5.3.1:
resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==}
engines: {node: '>= 4'}
@@ -1905,7 +2260,6 @@ packages:
engines: {node: '>=8'}
dependencies:
binary-extensions: 2.3.0
- dev: false
/is-core-module@2.13.1:
resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==}
@@ -1939,6 +2293,21 @@ packages:
engines: {node: '>=8'}
dev: true
+ /is-plain-obj@1.1.0:
+ resolution: {integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
+ /is-stream@1.1.0:
+ resolution: {integrity: sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
+ /is-stream@2.0.1:
+ resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
+ engines: {node: '>=8'}
+ dev: true
+
/isexe@2.0.0:
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
dev: true
@@ -2035,7 +2404,7 @@ packages:
resolution: {integrity: sha512-gaDdj3GtzoLoeosacd50kBBTnnh3B9AYxDThQUo4sfUyXdOhY6ku1qyZKW88tQCRgc3Sw6ChXYXWZwwgjOxE0w==}
engines: {node: '>= 12'}
dependencies:
- debug: 4.3.4
+ debug: 4.3.4(supports-color@5.5.0)
http-errors: 2.0.0
koa-compose: 4.1.0
methods: 1.1.2
@@ -2053,7 +2422,7 @@ packages:
content-disposition: 0.5.4
content-type: 1.0.5
cookies: 0.9.1
- debug: 4.3.4
+ debug: 4.3.4(supports-color@5.5.0)
delegates: 1.0.0
depd: 2.0.0
destroy: 1.2.0
@@ -2179,6 +2548,18 @@ packages:
js-tokens: 4.0.0
dev: false
+ /lowercase-keys@2.0.0:
+ resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==}
+ engines: {node: '>=8'}
+ dev: true
+
+ /lru-cache@4.1.5:
+ resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==}
+ dependencies:
+ pseudomap: 1.0.2
+ yallist: 2.1.2
+ dev: true
+
/lru-cache@6.0.0:
resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
engines: {node: '>=10'}
@@ -2190,6 +2571,10 @@ packages:
engines: {node: '>= 0.6'}
dev: false
+ /merge-stream@2.0.0:
+ resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
+ dev: true
+
/merge2@1.4.1:
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
engines: {node: '>= 8'}
@@ -2211,7 +2596,6 @@ packages:
/mime-db@1.52.0:
resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
engines: {node: '>= 0.6'}
- dev: false
/mime-types@2.1.35:
resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
@@ -2220,6 +2604,16 @@ packages:
mime-db: 1.52.0
dev: false
+ /mimic-fn@2.1.0:
+ resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
+ engines: {node: '>=6'}
+ dev: true
+
+ /mimic-response@1.0.1:
+ resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==}
+ engines: {node: '>=4'}
+ dev: true
+
/mimic-response@3.1.0:
resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==}
engines: {node: '>=10'}
@@ -2272,16 +2666,81 @@ packages:
engines: {node: '>= 0.6'}
dev: false
+ /nice-napi@1.0.2:
+ resolution: {integrity: sha512-px/KnJAJZf5RuBGcfD+Sp2pAKq0ytz8j+1NehvgIGFkvtvFrDM3T8E4x/JJODXK9WZow8RRGrbA9QQ3hs+pDhA==}
+ os: ['!win32']
+ requiresBuild: true
+ dependencies:
+ node-addon-api: 3.2.1
+ node-gyp-build: 4.8.0
+ dev: true
+ optional: true
+
/node-abi@3.56.0:
resolution: {integrity: sha512-fZjdhDOeRcaS+rcpve7XuwHBmktS1nS1gzgghwKUQQ8nTy2FdSDr6ZT8k6YhvlJeHmmQMYiT/IH9hfco5zeW2Q==}
engines: {node: '>=10'}
dependencies:
semver: 7.6.0
+ /node-addon-api@3.2.1:
+ resolution: {integrity: sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==}
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /node-gyp-build@4.8.0:
+ resolution: {integrity: sha512-u6fs2AEUljNho3EYTJNBfImO5QTo/J/1Etd+NVdCj7qWKUSN/bSLkZwhDv7I+w/MSC6qJ4cknepkAYykDdK8og==}
+ hasBin: true
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /nodemon@3.1.0:
+ resolution: {integrity: sha512-xqlktYlDMCepBJd43ZQhjWwMw2obW/JRvkrLxq5RCNcuDDX1DbcPT+qT1IlIIdf+DhnWs90JpTMe+Y5KxOchvA==}
+ engines: {node: '>=10'}
+ hasBin: true
+ dependencies:
+ chokidar: 3.6.0
+ debug: 4.3.4(supports-color@5.5.0)
+ ignore-by-default: 1.0.1
+ minimatch: 3.1.2
+ pstree.remy: 1.1.8
+ semver: 7.6.0
+ simple-update-notifier: 2.0.0
+ supports-color: 5.5.0
+ touch: 3.1.0
+ undefsafe: 2.0.5
+ dev: true
+
+ /nopt@1.0.10:
+ resolution: {integrity: sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==}
+ hasBin: true
+ dependencies:
+ abbrev: 1.1.1
+ dev: true
+
/normalize-path@3.0.0:
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
engines: {node: '>=0.10.0'}
- dev: false
+
+ /normalize-url@6.1.0:
+ resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==}
+ engines: {node: '>=10'}
+ dev: true
+
+ /npm-run-path@2.0.2:
+ resolution: {integrity: sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==}
+ engines: {node: '>=4'}
+ dependencies:
+ path-key: 2.0.1
+ dev: true
+
+ /npm-run-path@4.0.1:
+ resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==}
+ engines: {node: '>=8'}
+ dependencies:
+ path-key: 3.1.1
+ dev: true
/object-inspect@1.13.1:
resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==}
@@ -2299,6 +2758,13 @@ packages:
dependencies:
wrappy: 1.0.2
+ /onetime@5.1.2:
+ resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
+ engines: {node: '>=6'}
+ dependencies:
+ mimic-fn: 2.1.0
+ dev: true
+
/only@0.0.2:
resolution: {integrity: sha512-Fvw+Jemq5fjjyWz6CpKx6w9s7xxqo3+JCyM0WXWeCSOboZ8ABkyvP8ID4CZuChA/wxSx+XSJmdOm8rGVyJ1hdQ==}
dev: false
@@ -2315,6 +2781,23 @@ packages:
type-check: 0.4.0
dev: true
+ /os-filter-obj@2.0.0:
+ resolution: {integrity: sha512-uksVLsqG3pVdzzPvmAHpBK0wKxYItuzZr7SziusRPoz67tGV8rL1szZ6IdeUrbqLjGDwApBtN29eEE3IqGHOjg==}
+ engines: {node: '>=4'}
+ dependencies:
+ arch: 2.2.0
+ dev: true
+
+ /p-cancelable@2.1.1:
+ resolution: {integrity: sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==}
+ engines: {node: '>=8'}
+ dev: true
+
+ /p-finally@1.0.0:
+ resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==}
+ engines: {node: '>=4'}
+ dev: true
+
/p-limit@3.1.0:
resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
engines: {node: '>=10'}
@@ -2351,6 +2834,11 @@ packages:
engines: {node: '>=0.10.0'}
dev: true
+ /path-key@2.0.1:
+ resolution: {integrity: sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==}
+ engines: {node: '>=4'}
+ dev: true
+
/path-key@3.1.1:
resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
engines: {node: '>=8'}
@@ -2369,6 +2857,11 @@ packages:
engines: {node: '>=8'}
dev: true
+ /peek-readable@5.0.0:
+ resolution: {integrity: sha512-YtCKvLUOvwtMGmrniQPdO7MwPjgkFBtFIrmfSbYmYuq3tKDV/mcfAhBth1+C3ru7uXIZasc/pHnb+YDYNkkj4A==}
+ engines: {node: '>=14.16'}
+ dev: true
+
/picocolors@1.0.0:
resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
dev: true
@@ -2377,6 +2870,22 @@ packages:
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
engines: {node: '>=8.6'}
+ /pify@2.3.0:
+ resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
+ /pirates@4.0.6:
+ resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==}
+ engines: {node: '>= 6'}
+ dev: true
+
+ /piscina@4.4.0:
+ resolution: {integrity: sha512-+AQduEJefrOApE4bV7KRmp3N2JnnyErlVqq4P/jmko4FPz9Z877BCccl/iB3FdrWSUkvbGV9Kan/KllJgat3Vg==}
+ optionalDependencies:
+ nice-napi: 1.0.2
+ dev: true
+
/postcss@8.4.38:
resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==}
engines: {node: ^10 || ^12 || >=14}
@@ -2409,6 +2918,14 @@ packages:
engines: {node: '>= 0.8.0'}
dev: true
+ /pseudomap@1.0.2:
+ resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==}
+ dev: true
+
+ /pstree.remy@1.1.8:
+ resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==}
+ dev: true
+
/pump@3.0.0:
resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==}
dependencies:
@@ -2431,6 +2948,11 @@ packages:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
dev: true
+ /quick-lru@5.1.1:
+ resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==}
+ engines: {node: '>=10'}
+ dev: true
+
/raw-body@2.5.2:
resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==}
engines: {node: '>= 0.8'}
@@ -2475,12 +2997,18 @@ packages:
string_decoder: 1.3.0
util-deprecate: 1.0.2
+ /readable-web-to-node-stream@3.0.2:
+ resolution: {integrity: sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==}
+ engines: {node: '>=8'}
+ dependencies:
+ readable-stream: 3.6.2
+ dev: true
+
/readdirp@3.6.0:
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
engines: {node: '>=8.10.0'}
dependencies:
picomatch: 2.3.1
- dev: false
/rechoir@0.6.2:
resolution: {integrity: sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==}
@@ -2489,6 +3017,10 @@ packages:
resolve: 1.22.8
dev: true
+ /resolve-alpn@1.2.1:
+ resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==}
+ dev: true
+
/resolve-from@4.0.0:
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
engines: {node: '>=4'}
@@ -2503,6 +3035,12 @@ packages:
supports-preserve-symlinks-flag: 1.0.0
dev: true
+ /responselike@2.0.1:
+ resolution: {integrity: sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==}
+ dependencies:
+ lowercase-keys: 2.0.0
+ dev: true
+
/reusify@1.0.4:
resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
@@ -2557,6 +3095,18 @@ packages:
loose-envify: 1.4.0
dev: false
+ /semver-regex@4.0.5:
+ resolution: {integrity: sha512-hunMQrEy1T6Jr2uEVjrAIqjwWcQTgOAcIM52C8MY1EZSD3DDNft04XzvYKPqjED65bNVVko0YI38nYeEHCX3yw==}
+ engines: {node: '>=12'}
+ dev: true
+
+ /semver-truncate@3.0.0:
+ resolution: {integrity: sha512-LJWA9kSvMolR51oDE6PN3kALBNaUdkxzAGcexw8gjMA8xr5zUqK0JiR3CgARSqanYF3Z1YHvsErb1KDgh+v7Rg==}
+ engines: {node: '>=12'}
+ dependencies:
+ semver: 7.6.0
+ dev: true
+
/semver@5.7.2:
resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==}
hasBin: true
@@ -2585,6 +3135,13 @@ packages:
resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
dev: false
+ /shebang-command@1.2.0:
+ resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==}
+ engines: {node: '>=0.10.0'}
+ dependencies:
+ shebang-regex: 1.0.0
+ dev: true
+
/shebang-command@2.0.0:
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
engines: {node: '>=8'}
@@ -2592,6 +3149,11 @@ packages:
shebang-regex: 3.0.0
dev: true
+ /shebang-regex@1.0.0:
+ resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
/shebang-regex@3.0.0:
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
engines: {node: '>=8'}
@@ -2622,6 +3184,10 @@ packages:
object-inspect: 1.13.1
dev: false
+ /signal-exit@3.0.7:
+ resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
+ dev: true
+
/simple-concat@1.0.1:
resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==}
@@ -2632,16 +3198,54 @@ packages:
once: 1.4.0
simple-concat: 1.0.1
+ /simple-update-notifier@2.0.0:
+ resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==}
+ engines: {node: '>=10'}
+ dependencies:
+ semver: 7.6.0
+ dev: true
+
/slash@3.0.0:
resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
engines: {node: '>=8'}
dev: true
+ /sort-keys-length@1.0.1:
+ resolution: {integrity: sha512-GRbEOUqCxemTAk/b32F2xa8wDTs+Z1QHOkbhJDQTvv/6G3ZkbJ+frYWsTcc7cBB3Fu4wy4XlLCuNtJuMn7Gsvw==}
+ engines: {node: '>=0.10.0'}
+ dependencies:
+ sort-keys: 1.1.2
+ dev: true
+
+ /sort-keys@1.1.2:
+ resolution: {integrity: sha512-vzn8aSqKgytVik0iwdBEi+zevbTYZogewTUM6dtpmGwEcdzbub/TX4bCzRhebDCRC3QzXgJsLRKB2V/Oof7HXg==}
+ engines: {node: '>=0.10.0'}
+ dependencies:
+ is-plain-obj: 1.1.0
+ dev: true
+
/source-map-js@1.2.0:
resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==}
engines: {node: '>=0.10.0'}
dev: true
+ /source-map-support@0.5.21:
+ resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
+ dependencies:
+ buffer-from: 1.1.2
+ source-map: 0.6.1
+ dev: true
+
+ /source-map@0.6.1:
+ resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
+ /source-map@0.7.4:
+ resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==}
+ engines: {node: '>= 8'}
+ dev: true
+
/statuses@1.5.0:
resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==}
engines: {node: '>= 0.6'}
@@ -2664,6 +3268,16 @@ packages:
ansi-regex: 5.0.1
dev: true
+ /strip-eof@1.0.0:
+ resolution: {integrity: sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
+ /strip-final-newline@2.0.0:
+ resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==}
+ engines: {node: '>=6'}
+ dev: true
+
/strip-json-comments@2.0.1:
resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==}
engines: {node: '>=0.10.0'}
@@ -2673,12 +3287,24 @@ packages:
engines: {node: '>=8'}
dev: true
+ /strip-outer@2.0.0:
+ resolution: {integrity: sha512-A21Xsm1XzUkK0qK1ZrytDUvqsQWict2Cykhvi0fBQntGG5JSprESasEyV1EZ/4CiR5WB5KjzLTrP/bO37B0wPg==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+ dev: true
+
+ /strtok3@7.0.0:
+ resolution: {integrity: sha512-pQ+V+nYQdC5H3Q7qBZAz/MO6lwGhoC2gOAjuouGf/VO0m7vQRh8QNMl2Uf6SwAtzZ9bOw3UIeBukEGNJl5dtXQ==}
+ engines: {node: '>=14.16'}
+ dependencies:
+ '@tokenizer/token': 0.3.0
+ peek-readable: 5.0.0
+ dev: true
+
/supports-color@5.5.0:
resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
engines: {node: '>=4'}
dependencies:
has-flag: 3.0.0
- dev: true
/supports-color@7.2.0:
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
@@ -2731,6 +3357,28 @@ packages:
engines: {node: '>=0.6'}
dev: false
+ /token-types@5.0.1:
+ resolution: {integrity: sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg==}
+ engines: {node: '>=14.16'}
+ dependencies:
+ '@tokenizer/token': 0.3.0
+ ieee754: 1.2.1
+ dev: true
+
+ /touch@3.1.0:
+ resolution: {integrity: sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==}
+ hasBin: true
+ dependencies:
+ nopt: 1.0.10
+ dev: true
+
+ /trim-repeated@2.0.0:
+ resolution: {integrity: sha512-QUHBFTJGdOwmp0tbOG505xAgOp/YliZP/6UgafFXYZ26WT1bvQmSMJUvkeVSASuJJHbqsFbynTvkd5W8RBTipg==}
+ engines: {node: '>=12'}
+ dependencies:
+ escape-string-regexp: 5.0.0
+ dev: true
+
/ts-api-utils@1.3.0(typescript@5.4.3):
resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==}
engines: {node: '>=16'}
@@ -2740,6 +3388,10 @@ packages:
typescript: 5.4.3
dev: true
+ /tslib@2.6.2:
+ resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==}
+ dev: true
+
/tsscmp@1.0.6:
resolution: {integrity: sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==}
engines: {node: '>=0.6.x'}
@@ -2776,6 +3428,10 @@ packages:
hasBin: true
dev: true
+ /undefsafe@2.0.5:
+ resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==}
+ dev: true
+
/unpipe@1.0.0:
resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==}
engines: {node: '>= 0.8'}
@@ -2830,6 +3486,13 @@ packages:
fsevents: 2.3.3
dev: true
+ /which@1.3.1:
+ resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==}
+ hasBin: true
+ dependencies:
+ isexe: 2.0.0
+ dev: true
+
/which@2.0.2:
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
engines: {node: '>= 8'}
@@ -2841,6 +3504,10 @@ packages:
/wrappy@1.0.2:
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
+ /yallist@2.1.2:
+ resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==}
+ dev: true
+
/yallist@4.0.0:
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}