Compare commits

...

40 Commits

Author SHA1 Message Date
fda55061a0 feat: 更新版本至 0.0.52,升级依赖 @kevisual/query 至 0.0.47 和 @kevisual/router 至 0.0.75,移除不必要的 setBaseResponse 调用 2026-02-18 08:41:56 +08:00
90a73bc1f6 temp 2026-02-17 23:13:28 +08:00
c0bd0b7964 temp 2026-02-16 19:25:56 +08:00
b8db664c62 feat: 更新版本至 0.0.50,升级 @kevisual/context 依赖至 0.0.6 2026-02-15 20:26:54 +08:00
9fed28819e feat: 添加 createViewData 函数以构建 RouterViewData 结构 2026-02-15 20:17:49 +08:00
9400ccf229 temp 2026-02-15 20:08:22 +08:00
b972ed3a36 temp 2026-02-15 19:55:15 +08:00
05c8496469 feat: 更新版本至 0.0.49,新增 storeList 支持,优化登录缓存,添加 store-auth 和 store-mark 功能 2026-02-14 03:06:16 +08:00
d2ffc8d8ec 更新版本至 0.0.48,升级依赖项,优化哈希函数实现,新增 QueryMark 类及相关功能 2026-02-14 01:11:03 +08:00
e4990d5e3d 更新版本至 0.0.47,升级 @kevisual/remote-app 依赖至 0.0.4,并在 QueryApp 类中扩展 getApp 方法的参数以支持创建新应用 2026-02-05 18:34:54 +08:00
4e36d01fb1 更新版本至 0.0.45,升级依赖项并新增发布应用功能 2026-02-05 14:08:56 +08:00
2d7375e32d 更新版本至 0.0.44,新增从 JSON Schema 转换的功能,并优化路由初始化逻辑 2026-02-03 02:54:52 +08:00
2fec4dd04e feat: refactor query proxy and router API, add random utilities, and create index files for queries
- Updated imports in proxy.ts to use browser-specific router.
- Refactored router-api-proxy.ts to streamline imports.
- Enhanced random.ts with additional utility functions for generating random IDs and letters.
- Created bun.config.ts for building query modules dynamically.
- Added index files for query modules (query-ai, query-app, query-config, query-login, query-shop, query-upload) to facilitate exports.
- Implemented get-query-list.ts to automate the generation of package entries for queries.
2026-02-03 01:30:06 +08:00
171847a46c 更新版本至 0.0.42,修改依赖项版本并在 QueryResources 类中新增 onProcess 回调以支持上传进度通知 2026-02-02 16:57:30 +08:00
916bb083af 0.0.41 2026-02-01 18:09:37 +08:00
5e5df744ce init update query 2026-02-01 18:09:35 +08:00
9fb118bd71 test 2026-02-01 17:47:11 +08:00
17d00ca08f 更新版本至 0.0.40,新增 getState 方法以获取文件状态,并扩展 Stat 类型以包含更多元数据 2026-02-01 17:43:00 +08:00
78f781ef39 更新版本至 0.0.39,优化 uploadFile 和 uploadChunkedFile 方法,支持自定义 chunkSize 和 maxSize 参数 2026-02-01 16:22:59 +08:00
f4afea4560 更新版本至 0.0.38,修复文件上传时的 chunk 参数设置,优化文件切片处理 2026-02-01 15:45:49 +08:00
d1fa2dc6b5 更新版本至 0.0.37,优化文件上传功能,支持大文件分块上传,改用 SparkMD5 计算 Blob 哈希 2026-02-01 15:09:00 +08:00
ddfa18480f update 2026-01-30 17:26:29 +08:00
6ee39d2028 更新版本至 0.0.35,优化 fetchFile 方法以支持额外选项 2026-01-30 17:14:31 +08:00
e2b7d62693 更新 @kevisual/api 版本至 0.0.34,增强文件上传功能,支持 Blob 类型,新增创建文件夹和重命名功能 2026-01-30 15:48:25 +08:00
97e7a53a2b add delete 2026-01-30 14:27:42 +08:00
ef5d3c3805 更新 @kevisual/api 版本至 0.0.29,添加路径处理和哈希功能,新增随机 ID 生成器 2026-01-30 14:23:51 +08:00
0086aeced2 更新 @kevisual/api 版本至 0.0.28,添加 query-secret 模块并更新导出路径 2026-01-27 19:46:05 +08:00
969fb31351 更新 @kevisual/api 版本至 0.0.27,升级依赖 @kevisual/router 至 0.0.62,@kevisual/types 至 0.0.12,@types/node 至 25.0.10,并在 QueryProxy 中添加 Fuse.js 以增强路由查询功能 2026-01-26 01:59:16 +08:00
ee261f5c27 udpate 2026-01-23 02:12:21 +08:00
a1460a97b4 更新 @kevisual/api 版本至 0.0.25,优化路由初始化失败时的错误处理逻辑 2026-01-21 23:09:31 +08:00
aa2bf17791 更新 @kevisual/api 版本至 0.0.24,升级 @kevisual/query 依赖至 0.0.38 2026-01-21 23:07:55 +08:00
7de76f29e9 更新 @kevisual/api 版本至 0.0.23,添加 metadata.url 属性以增强路由注册信息 2026-01-21 20:40:58 +08:00
ba44339b48 更新 @kevisual/api 版本至 0.0.22,修正文件引用路径并优化路由注册逻辑 2026-01-21 20:00:59 +08:00
e295a6ddbe 更新 @kevisual/api 版本至 0.0.21,添加 router-api-proxy.ts 和 proxy.ts 文件,重构 QueryProxy 类以优化 API 查询初始化 2026-01-21 19:40:02 +08:00
711e1d4ca5 更新 @kevisual/api 版本至 0.0.20,升级 @kevisual/query 依赖至 0.0.37 2026-01-21 18:20:29 +08:00
b89e0d02a8 更新 @kevisual/api 版本至 0.0.19,升级 @kevisual/query 依赖至 0.0.36 2026-01-21 18:17:00 +08:00
b61e32d883 Refactor code structure for improved readability and maintainability 2026-01-21 17:35:32 +08:00
f420125c78 update 2026-01-03 18:22:59 +08:00
80bd6112ad 更新 @kevisual/api 版本至 0.0.17,升级 @kevisual/cache 和 @kevisual/query 依赖至最新版本,重构 QueryProxy 类以优化 API 查询初始化 2026-01-03 18:20:02 +08:00
cb8723c172 更新 @kevisual/api 版本至 0.0.16,增强 RouterViewItem 类型,添加 question、response 和 action 属性,重构 initRouterViewQuery 和 callWorker 方法以优化路由视图初始化和工作线程调用 2026-01-03 01:54:55 +08:00
27 changed files with 1642 additions and 843 deletions

33
bun.config.ts Normal file
View File

@@ -0,0 +1,33 @@
import { buildWithBun } from '@kevisual/code-builder'
import { queryList, storeList } from './src/scripts/get-query-list.ts'
// await buildWithBun({ naming: "app", entry: "query/index.ts", meta: import.meta, dts: true })
for (const query of queryList) {
await buildWithBun({
naming: query.name,
entry: `query/${query.name}/index.ts`,
meta: import.meta,
target: 'browser',
dts: true,
})
}
for (const store of storeList) {
await buildWithBun({
naming: store.name,
entry: `store/${store.name}/index.ts`,
meta: import.meta,
external: ['sonner', 'zustand', '@kevisual/context'],
target: 'browser',
dts: true,
})
}
await buildWithBun({
naming: 'utils-node',
entry: 'query/utils/index.ts',
meta: import.meta,
target: 'node',
dts: true,
})

View File

@@ -1,11 +1,10 @@
{ {
"name": "@kevisual/api", "name": "@kevisual/api",
"version": "0.0.15", "version": "0.0.52",
"description": "", "description": "",
"main": "mod.ts", "main": "mod.ts",
"scripts": { "scripts": {
"build": "bun run bun.config.ts", "build": "bun run bun.config.ts"
"postbuild": "bun bun.copy.config.mjs"
}, },
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"
@@ -13,30 +12,42 @@
"files": [ "files": [
"dist", "dist",
"query", "query",
"store",
"mod.ts" "mod.ts"
], ],
"keywords": [], "keywords": [],
"author": "abearxiong <xiongxiao@xiongxiao.me> (https://www.xiongxiao.me)", "author": "abearxiong <xiongxiao@xiongxiao.me> (https://www.xiongxiao.me)",
"license": "MIT", "license": "MIT",
"packageManager": "pnpm@10.27.0", "packageManager": "pnpm@10.30.0",
"type": "module", "type": "module",
"devDependencies": { "devDependencies": {
"@kevisual/cache": "^0.0.4", "@kevisual/cache": "^0.0.5",
"@kevisual/query": "^0.0.33", "@kevisual/code-builder": "^0.0.6",
"@kevisual/router": "^0.0.52", "@kevisual/query": "^0.0.47",
"@kevisual/types": "^0.0.10", "@kevisual/remote-app": "^0.0.4",
"@kevisual/use-config": "^1.0.21", "@kevisual/router": "^0.0.75",
"@types/bun": "^1.3.5", "@kevisual/types": "^0.0.12",
"@types/node": "^25.0.3", "@kevisual/use-config": "^1.0.30",
"dotenv": "^17.2.3", "@types/bun": "^1.3.9",
"fast-glob": "^3.3.3" "@types/node": "^25.2.3",
"@types/spark-md5": "^3.0.5",
"dotenv": "^17.3.1",
"fast-glob": "^3.3.3",
"ws": "npm:@kevisual/ws"
}, },
"dependencies": { "dependencies": {
"@kevisual/js-filter": "^0.0.3", "@kevisual/context": "^0.0.6",
"@kevisual/js-filter": "^0.0.5",
"@kevisual/load": "^0.0.6", "@kevisual/load": "^0.0.6",
"es-toolkit": "^1.43.0", "@paralleldrive/cuid2": "^3.3.0",
"eventemitter3": "^5.0.1", "es-toolkit": "^1.44.0",
"nanoid": "^5.1.6" "eventemitter3": "^5.0.4",
"fuse.js": "^7.1.0",
"nanoid": "^5.1.6",
"path-browserify-esm": "^1.0.6",
"sonner": "^2.0.7",
"spark-md5": "^3.0.2",
"zustand": "^5.0.11"
}, },
"exports": { "exports": {
".": "./mod.ts", ".": "./mod.ts",
@@ -44,6 +55,24 @@
"./login-node": "./query/query-login/query-login-node.ts", "./login-node": "./query/query-login/query-login-node.ts",
"./config": "./query/query-config/query-config.ts", "./config": "./query/query-config/query-config.ts",
"./proxy": "./query/query-proxy/index.ts", "./proxy": "./query/query-proxy/index.ts",
"./query/**/*.ts": "./query/**/*.ts" "./secret": "./query/query-secret/index.ts",
"./resources": "./query/query-resources/index.ts",
"./utils-node": "./dist/utils-node.js",
"./utils": "./dist/utils.js",
"./query-secret": "./dist/query-secret.js",
"./query-mark": "./dist/query-mark.js",
"./query-upload": "./dist/query-upload.js",
"./query-app": "./dist/query-app.js",
"./query-proxy": "./dist/query-proxy.js",
"./query-shop": "./dist/query-shop.js",
"./query-config": "./dist/query-config.js",
"./query-login": "./dist/query-login.js",
"./query-ai": "./dist/query-ai.js",
"./query-resources": "./dist/query-resources.js",
"./store-mark": "./dist/store-mark.js",
"./store-auth": "./dist/store-auth.js",
"./src/*": "./src/*",
"./store/*": "./src/*",
"./query/*": "./query/*"
} }
} }

590
pnpm-lock.yaml generated
View File

@@ -8,49 +8,82 @@ importers:
.: .:
dependencies: dependencies:
'@kevisual/context':
specifier: ^0.0.6
version: 0.0.6
'@kevisual/js-filter': '@kevisual/js-filter':
specifier: ^0.0.3 specifier: ^0.0.5
version: 0.0.3 version: 0.0.5
'@kevisual/load': '@kevisual/load':
specifier: ^0.0.6 specifier: ^0.0.6
version: 0.0.6 version: 0.0.6
'@paralleldrive/cuid2':
specifier: ^3.3.0
version: 3.3.0
es-toolkit: es-toolkit:
specifier: ^1.43.0 specifier: ^1.44.0
version: 1.43.0 version: 1.44.0
eventemitter3: eventemitter3:
specifier: ^5.0.1 specifier: ^5.0.4
version: 5.0.1 version: 5.0.4
fuse.js:
specifier: ^7.1.0
version: 7.1.0
nanoid: nanoid:
specifier: ^5.1.6 specifier: ^5.1.6
version: 5.1.6 version: 5.1.6
path-browserify-esm:
specifier: ^1.0.6
version: 1.0.6
sonner:
specifier: ^2.0.7
version: 2.0.7(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
spark-md5:
specifier: ^3.0.2
version: 3.0.2
zustand:
specifier: ^5.0.11
version: 5.0.11(react@19.2.4)
devDependencies: devDependencies:
'@kevisual/cache': '@kevisual/cache':
specifier: ^0.0.5
version: 0.0.5
'@kevisual/code-builder':
specifier: ^0.0.6
version: 0.0.6
'@kevisual/query':
specifier: ^0.0.47
version: 0.0.47
'@kevisual/remote-app':
specifier: ^0.0.4 specifier: ^0.0.4
version: 0.0.4 version: 0.0.4
'@kevisual/query':
specifier: ^0.0.33
version: 0.0.33
'@kevisual/router': '@kevisual/router':
specifier: ^0.0.52 specifier: ^0.0.75
version: 0.0.52 version: 0.0.75
'@kevisual/types': '@kevisual/types':
specifier: ^0.0.10 specifier: ^0.0.12
version: 0.0.10 version: 0.0.12
'@kevisual/use-config': '@kevisual/use-config':
specifier: ^1.0.21 specifier: ^1.0.30
version: 1.0.21(dotenv@17.2.3) version: 1.0.30(dotenv@17.3.1)
'@types/bun': '@types/bun':
specifier: ^1.3.5 specifier: ^1.3.9
version: 1.3.5 version: 1.3.9
'@types/node': '@types/node':
specifier: ^25.0.3 specifier: ^25.2.3
version: 25.0.3 version: 25.2.3
'@types/spark-md5':
specifier: ^3.0.5
version: 3.0.5
dotenv: dotenv:
specifier: ^17.2.3 specifier: ^17.3.1
version: 17.2.3 version: 17.3.1
fast-glob: fast-glob:
specifier: ^3.3.3 specifier: ^3.3.3
version: 3.3.3 version: 3.3.3
ws:
specifier: npm:@kevisual/ws
version: '@kevisual/ws@8.19.0'
packages/api: packages/api:
dependencies: dependencies:
@@ -74,11 +107,18 @@ importers:
packages: packages:
'@kevisual/cache@0.0.4': '@kevisual/cache@0.0.5':
resolution: {integrity: sha512-NlyriJ9fC27TgQhWYbEH9hG84R2k0lIofOxo/+nVHN6a6LJSLnVbpDIysRcnH8MI52n/XHfWwLSjeDDL3D1/cQ==} resolution: {integrity: sha512-fgtUYGUUq/DY0KFV4CkWszNqvQUaA8XvMTUjoR9ZXRpau5IIDolD/Wen2TFsZ7G3Rfy+lef5dnaiZVDkZwdVKg==}
'@kevisual/js-filter@0.0.3': '@kevisual/code-builder@0.0.6':
resolution: {integrity: sha512-vgUB2fUAWS75GUFr/a/tGSSDrPUUmVDktO38k3hIKwU3ZE4tpuhcVxrpUbkXlFS5i0rbL2mAQeID1C6kIlMGRg==} resolution: {integrity: sha512-0aqATB31/yw4k4s5/xKnfr4DKbUnx8e3Z3BmKbiXTrc+CqWiWTdlGe9bKI9dZ2Df+xNp6g11W4xM2NICNyyCCw==}
hasBin: true
'@kevisual/context@0.0.6':
resolution: {integrity: sha512-w7HBOuO3JH37n6xT6W3FD7ykqHTwtyxOQzTzfEcKDCbsvGB1wVreSxFm2bvoFnnFLuxT/5QMpKlnPrwvmcTGnw==}
'@kevisual/js-filter@0.0.5':
resolution: {integrity: sha512-+S+Sf3K/aP6XtZI2s7TgKOr35UuvUvtpJ9YDW30a+mY0/N8gRuzyKhieBzQN7Ykayzz70uoMavBXut2rUlLgzw==}
'@kevisual/load@0.0.6': '@kevisual/load@0.0.6':
resolution: {integrity: sha512-+3YTFehRcZ1haGel5DKYMUwmi5i6f2psyaPZlfkKU/cOXgkpwoG9/BEqPCnPjicKqqnksEpixVRkyHJ+5bjLVA==} resolution: {integrity: sha512-+3YTFehRcZ1haGel5DKYMUwmi5i6f2psyaPZlfkKU/cOXgkpwoG9/BEqPCnPjicKqqnksEpixVRkyHJ+5bjLVA==}
@@ -86,26 +126,36 @@ packages:
'@kevisual/query@0.0.18': '@kevisual/query@0.0.18':
resolution: {integrity: sha512-I2vHTu0I6AyD9PJyr+vxyp9jIJ6rd2EZqLVHTv/+zrVKVc2SS76Tg7aGNkmAFqqLSCB8kLLsmMGtSJU1Qb8VVg==} resolution: {integrity: sha512-I2vHTu0I6AyD9PJyr+vxyp9jIJ6rd2EZqLVHTv/+zrVKVc2SS76Tg7aGNkmAFqqLSCB8kLLsmMGtSJU1Qb8VVg==}
'@kevisual/query@0.0.33': '@kevisual/query@0.0.47':
resolution: {integrity: sha512-3w74bcLpwV3z483eg8n0DgkftfjWC6iLONXBvfyjW6IZf6jMOuouFaM4Rk+uEsTgElU6XGMKseNTp6dlQdWYkg==} resolution: {integrity: sha512-ZR7WXeDDGUSzBtcGVU3J173sA0hCqrGTw5ybGbdNGlM0VyJV/XQIovCcSoZh1YpnciLRRqJvzXUgTnCkam+M3g==}
'@kevisual/remote-app@0.0.4':
resolution: {integrity: sha512-2yIlWY98pLCcxG+DJsqXXkd5YYEgymuOsyElH+31AoEPb7mlNREnYS81zN0KM9nvdSmU2G51vV4UVirJlYBZCQ==}
'@kevisual/router@0.0.20': '@kevisual/router@0.0.20':
resolution: {integrity: sha512-uSwDYWh+kvAu6i0m0SJVgcLR/CYz7WvIWGz0nSF8Vg6smJuAgI+laHR4ESO8Fbz+Xn8bPHuSwmM//HHLMLx2FA==} resolution: {integrity: sha512-uSwDYWh+kvAu6i0m0SJVgcLR/CYz7WvIWGz0nSF8Vg6smJuAgI+laHR4ESO8Fbz+Xn8bPHuSwmM//HHLMLx2FA==}
'@kevisual/router@0.0.52': '@kevisual/router@0.0.75':
resolution: {integrity: sha512-Qiv3P1XjzD813Tm79S+atrDb2eickGCI9tuy/aCu512LcoYYJqZhwwkeT4ES0DinnA13Ckqd43QWBR6UmuYkHQ==} resolution: {integrity: sha512-WBDRKMjNYTP7ymkUUtiQwWYIcqnc+TGo3rFuRze8ovYV2UN5cQxIkIfsDbgWOdV1/v9b57gtiJvJRqWjCBWKRg==}
'@kevisual/types@0.0.10': '@kevisual/types@0.0.10':
resolution: {integrity: sha512-Q73uzzjk9UidumnmCvOpgzqDDvQxsblz22bIFuoiioUFJWwaparx8bpd8ArRyFojicYL1YJoFDzDZ9j9NN8grA==} resolution: {integrity: sha512-Q73uzzjk9UidumnmCvOpgzqDDvQxsblz22bIFuoiioUFJWwaparx8bpd8ArRyFojicYL1YJoFDzDZ9j9NN8grA==}
'@kevisual/use-config@1.0.21': '@kevisual/types@0.0.12':
resolution: {integrity: sha512-czgy4+tBDBJI6QTnKh2PCwswET6ZpZ4ZqBE/SPkkOivEtlrcPzLs5elwMLZ3goD1XMD4VB3yjumb5WuW/8H8MA==} resolution: {integrity: sha512-zJXH2dosir3jVrQ6QG4i0+iLQeT9gJ3H+cKXs8ReWboxBSYzUZO78XssVeVrFPsJ33iaAqo4q3DWbSS1dWGn7Q==}
'@kevisual/use-config@1.0.30':
resolution: {integrity: sha512-kPdna0FW/X7D600aMdiZ5UTjbCo6d8d4jjauSc8RMmBwUU6WliFDSPUNKVpzm2BsDX5Nth1IXFPYMqH+wxqAmw==}
peerDependencies: peerDependencies:
dotenv: ^17 dotenv: ^17
'@noble/hashes@1.4.0': '@kevisual/ws@8.19.0':
resolution: {integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==} resolution: {integrity: sha512-jLsL80wBBKkrJZrfk3SQpJ9JA/zREdlUROj7eCkmzqduAWKSI0wVcXuCKf+mLFCHB0Q0Tkh2rgzjSlurt3JQgw==}
engines: {node: '>= 16'} engines: {node: '>=10.0.0'}
'@noble/hashes@2.0.1':
resolution: {integrity: sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw==}
engines: {node: '>= 20.19.0'}
'@nodelib/fs.scandir@2.1.5': '@nodelib/fs.scandir@2.1.5':
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
@@ -119,42 +169,12 @@ packages:
resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
engines: {node: '>= 8'} engines: {node: '>= 8'}
'@peculiar/asn1-cms@2.6.0': '@paralleldrive/cuid2@3.3.0':
resolution: {integrity: sha512-2uZqP+ggSncESeUF/9Su8rWqGclEfEiz1SyU02WX5fUONFfkjzS2Z/F1Li0ofSmf4JqYXIOdCAZqIXAIBAT1OA==} resolution: {integrity: sha512-OqiFvSOF0dBSesELYY2CAMa4YINvlLpvKOz/rv6NeZEqiyttlHgv98Juwv4Ch+GrEV7IZ8jfI2VcEoYUjXXCjw==}
hasBin: true
'@peculiar/asn1-csr@2.6.0': '@types/bun@1.3.9':
resolution: {integrity: sha512-BeWIu5VpTIhfRysfEp73SGbwjjoLL/JWXhJ/9mo4vXnz3tRGm+NGm3KNcRzQ9VMVqwYS2RHlolz21svzRXIHPQ==} resolution: {integrity: sha512-KQ571yULOdWJiMH+RIWIOZ7B2RXQGpL1YQrBtLIV3FqDcCu6FsbFUBwhdKUlCKUpS3PJDsHlJ1QKlpxoVR+xtw==}
'@peculiar/asn1-ecc@2.6.0':
resolution: {integrity: sha512-FF3LMGq6SfAOwUG2sKpPXblibn6XnEIKa+SryvUl5Pik+WR9rmRA3OCiwz8R3lVXnYnyRkSZsSLdml8H3UiOcw==}
'@peculiar/asn1-pfx@2.6.0':
resolution: {integrity: sha512-rtUvtf+tyKGgokHHmZzeUojRZJYPxoD/jaN1+VAB4kKR7tXrnDCA/RAWXAIhMJJC+7W27IIRGe9djvxKgsldCQ==}
'@peculiar/asn1-pkcs8@2.6.0':
resolution: {integrity: sha512-KyQ4D8G/NrS7Fw3XCJrngxmjwO/3htnA0lL9gDICvEQ+GJ+EPFqldcJQTwPIdvx98Tua+WjkdKHSC0/Km7T+lA==}
'@peculiar/asn1-pkcs9@2.6.0':
resolution: {integrity: sha512-b78OQ6OciW0aqZxdzliXGYHASeCvvw5caqidbpQRYW2mBtXIX2WhofNXTEe7NyxTb0P6J62kAAWLwn0HuMF1Fw==}
'@peculiar/asn1-rsa@2.6.0':
resolution: {integrity: sha512-Nu4C19tsrTsCp9fDrH+sdcOKoVfdfoQQ7S3VqjJU6vedR7tY3RLkQ5oguOIB3zFW33USDUuYZnPEQYySlgha4w==}
'@peculiar/asn1-schema@2.6.0':
resolution: {integrity: sha512-xNLYLBFTBKkCzEZIw842BxytQQATQv+lDTCEMZ8C196iJcJJMBUZxrhSTxLaohMyKK8QlzRNTRkUmanucnDSqg==}
'@peculiar/asn1-x509-attr@2.6.0':
resolution: {integrity: sha512-MuIAXFX3/dc8gmoZBkwJWxUWOSvG4MMDntXhrOZpJVMkYX+MYc/rUAU2uJOved9iJEoiUx7//3D8oG83a78UJA==}
'@peculiar/asn1-x509@2.6.0':
resolution: {integrity: sha512-uzYbPEpoQiBoTq0/+jZtpM6Gq6zADBx+JNFP3yqRgziWBxQ/Dt/HcuvRfm9zJTPdRcBqPNdaRHTVwpyiq6iNMA==}
'@peculiar/x509@1.14.2':
resolution: {integrity: sha512-r2w1Hg6pODDs0zfAKHkSS5HLkOLSeburtcgwvlLLWWCixw+MmW3U6kD5ddyvc2Y2YdbGuVwCF2S2ASoU1cFAag==}
engines: {node: '>=22.0.0'}
'@types/bun@1.3.5':
resolution: {integrity: sha512-RnygCqNrd3srIPEWBd5LFeUYG7plCoH2Yw9WaZGyNmdTEei+gWaHqydbaIRkIkcbXwhBT94q78QljxN0Sk838w==}
'@types/node-fetch@2.6.12': '@types/node-fetch@2.6.12':
resolution: {integrity: sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==} resolution: {integrity: sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==}
@@ -168,8 +188,11 @@ packages:
'@types/node@22.15.27': '@types/node@22.15.27':
resolution: {integrity: sha512-5fF+eu5mwihV2BeVtX5vijhdaZOfkQTATrePEaXTcKqI16LhJ7gi2/Vhd9OZM0UojcdmiOCVg5rrax+i1MdoQQ==} resolution: {integrity: sha512-5fF+eu5mwihV2BeVtX5vijhdaZOfkQTATrePEaXTcKqI16LhJ7gi2/Vhd9OZM0UojcdmiOCVg5rrax+i1MdoQQ==}
'@types/node@25.0.3': '@types/node@25.2.3':
resolution: {integrity: sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA==} resolution: {integrity: sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ==}
'@types/spark-md5@3.0.5':
resolution: {integrity: sha512-lWf05dnD42DLVKQJZrDHtWFidcLrHuip01CtnC2/S6AMhX4t9ZlEUj4iuRlAnts0PQk7KESOqKxeGE/b6sIPGg==}
abort-controller@3.0.0: abort-controller@3.0.0:
resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==}
@@ -179,23 +202,18 @@ packages:
resolution: {integrity: sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==} resolution: {integrity: sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==}
engines: {node: '>= 8.0.0'} engines: {node: '>= 8.0.0'}
asn1js@3.0.6:
resolution: {integrity: sha512-UOCGPYbl0tv8+006qks/dTgV9ajs97X2p0FAbyS2iyCRrmLSRolDaHdp+v/CLgnzHc3fVB+CwYiUmei7ndFcgA==}
engines: {node: '>=12.0.0'}
asynckit@0.4.0: asynckit@0.4.0:
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
bignumber.js@9.3.1:
resolution: {integrity: sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==}
braces@3.0.3: braces@3.0.3:
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
engines: {node: '>=8'} engines: {node: '>=8'}
bun-types@1.3.5: bun-types@1.3.9:
resolution: {integrity: sha512-inmAYe2PFLs0SUbFOWSVD24sg1jFlMPxOjOSSCYqUgn4Hsc3rDc7dFvfVYjFPNHtov6kgUeulV4SxbuIV/stPw==} resolution: {integrity: sha512-+UBWWOakIP4Tswh0Bt0QD0alpTY8cb5hvgiYeWCMet9YukHbzuruIEeXC2D7nMJPB12kbh8C7XJykSexEqGKJg==}
bytestreamjs@2.0.1:
resolution: {integrity: sha512-U1Z/ob71V/bXfVABvNr/Kumf5VyeQRBEm6Txb0PQ6S7V5GpBM3w4Cbqz/xPDicR5tN0uvDifng8C+5qECeGwyQ==}
engines: {node: '>=6.0.0'}
call-bind-apply-helpers@1.0.2: call-bind-apply-helpers@1.0.2:
resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
@@ -205,37 +223,20 @@ packages:
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
engines: {node: '>= 0.8'} engines: {node: '>= 0.8'}
debug@4.4.3:
resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
engines: {node: '>=6.0'}
peerDependencies:
supports-color: '*'
peerDependenciesMeta:
supports-color:
optional: true
delayed-stream@1.0.0: delayed-stream@1.0.0:
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
engines: {node: '>=0.4.0'} engines: {node: '>=0.4.0'}
depd@2.0.0: dotenv@17.3.1:
resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} resolution: {integrity: sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==}
engines: {node: '>= 0.8'}
dotenv@17.2.3:
resolution: {integrity: sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==}
engines: {node: '>=12'} engines: {node: '>=12'}
dunder-proto@1.0.1: dunder-proto@1.0.1:
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
ee-first@1.1.1: error-causes@3.0.2:
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} resolution: {integrity: sha512-i0B8zq1dHL6mM85FGoxaJnVtx6LD5nL2v0hlpGdntg5FOSyzQ46c9lmz5qx0xRS2+PWHGOHcYxGIBC5Le2dRMw==}
encodeurl@2.0.0:
resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==}
engines: {node: '>= 0.8'}
es-define-property@1.0.1: es-define-property@1.0.1:
resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==}
@@ -253,22 +254,15 @@ packages:
resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
es-toolkit@1.43.0: es-toolkit@1.44.0:
resolution: {integrity: sha512-SKCT8AsWvYzBBuUqMk4NPwFlSdqLpJwmy6AP322ERn8W2YLIB6JBXnwMI2Qsh2gfphT3q7EKAxKb23cvFHFwKA==} resolution: {integrity: sha512-6penXeZalaV88MM3cGkFZZfOoLGWshWWfdy0tWw/RlVVyhvMaWSBTOvXNeiW3e5FwdS5ePW0LGEu17zT139ktg==}
escape-html@1.0.3:
resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==}
etag@1.8.1:
resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==}
engines: {node: '>= 0.6'}
event-target-shim@5.0.1: event-target-shim@5.0.1:
resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==}
engines: {node: '>=6'} engines: {node: '>=6'}
eventemitter3@5.0.1: eventemitter3@5.0.4:
resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==}
fast-glob@3.3.3: fast-glob@3.3.3:
resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==}
@@ -292,13 +286,13 @@ packages:
resolution: {integrity: sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==} resolution: {integrity: sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==}
engines: {node: '>= 12.20'} engines: {node: '>= 12.20'}
fresh@2.0.0:
resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==}
engines: {node: '>= 0.8'}
function-bind@1.1.2: function-bind@1.1.2:
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
fuse.js@7.1.0:
resolution: {integrity: sha512-trLf4SzuuUxfusZADLINj+dE8clK1frKdmqiJNb1Es75fmI5oY6X2mxLVUciLLjxqw/xr72Dhy+lER6dGd02FQ==}
engines: {node: '>=10'}
get-intrinsic@1.3.0: get-intrinsic@1.3.0:
resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@@ -327,19 +321,12 @@ packages:
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
http-errors@2.0.1:
resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==}
engines: {node: '>= 0.8'}
humanize-ms@1.2.1: humanize-ms@1.2.1:
resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==}
idb-keyval@6.2.2: idb-keyval@6.2.2:
resolution: {integrity: sha512-yjD9nARJ/jb1g+CvD0tlhUHOrJ9Sy0P8T9MF3YaLlHnSRpwPfpTX0XIvpmw3gAJUmEu3FiICLBDPXVwyEvrleg==} resolution: {integrity: sha512-yjD9nARJ/jb1g+CvD0tlhUHOrJ9Sy0P8T9MF3YaLlHnSRpwPfpTX0XIvpmw3gAJUmEu3FiICLBDPXVwyEvrleg==}
inherits@2.0.4:
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
is-extglob@2.1.1: is-extglob@2.1.1:
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
@@ -372,18 +359,10 @@ packages:
resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
engines: {node: '>= 0.6'} engines: {node: '>= 0.6'}
mime-db@1.54.0:
resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==}
engines: {node: '>= 0.6'}
mime-types@2.1.35: mime-types@2.1.35:
resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
engines: {node: '>= 0.6'} engines: {node: '>= 0.6'}
mime-types@3.0.2:
resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==}
engines: {node: '>=18'}
ms@2.1.3: ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
@@ -410,10 +389,6 @@ packages:
resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==} resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==}
engines: {node: '>= 6.13.0'} engines: {node: '>= 6.13.0'}
on-finished@2.4.1:
resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==}
engines: {node: '>= 0.8'}
openai@4.98.0: openai@4.98.0:
resolution: {integrity: sha512-TmDKur1WjxxMPQAtLG5sgBSCJmX7ynTsGmewKzoDwl1fRxtbLOsiR0FA/AOAAtYUmP6azal+MYQuOENfdU+7yg==} resolution: {integrity: sha512-TmDKur1WjxxMPQAtLG5sgBSCJmX7ynTsGmewKzoDwl1fRxtbLOsiR0FA/AOAAtYUmP6azal+MYQuOENfdU+7yg==}
hasBin: true hasBin: true
@@ -426,37 +401,28 @@ packages:
zod: zod:
optional: true optional: true
path-browserify-esm@1.0.6:
resolution: {integrity: sha512-9nUwYvvu/yq1PYrUyYCihNWmpzacaRYF6gGbjLWErrZ4MRDWyfPN7RpE8E7tsw8eqBU/rr7mcoTXbS+Vih8uUA==}
path-to-regexp@8.2.0: path-to-regexp@8.2.0:
resolution: {integrity: sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==} resolution: {integrity: sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==}
engines: {node: '>=16'} engines: {node: '>=16'}
path-to-regexp@8.3.0:
resolution: {integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==}
picomatch@2.3.1: picomatch@2.3.1:
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
engines: {node: '>=8.6'} engines: {node: '>=8.6'}
pkijs@3.3.3:
resolution: {integrity: sha512-+KD8hJtqQMYoTuL1bbGOqxb4z+nZkTAwVdNtWwe8Tc2xNbEmdJYIYoc6Qt0uF55e6YW6KuTHw1DjQ18gMhzepw==}
engines: {node: '>=16.0.0'}
pvtsutils@1.3.6:
resolution: {integrity: sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==}
pvutils@1.1.5:
resolution: {integrity: sha512-KTqnxsgGiQ6ZAzZCVlJH5eOjSnvlyEgx1m8bkRJfOhmGRqfo5KLvmAlACQkrjEtOQ4B7wF9TdSLIs9O90MX9xA==}
engines: {node: '>=16.0.0'}
queue-microtask@1.2.3: queue-microtask@1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
range-parser@1.2.1: react-dom@19.2.4:
resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} resolution: {integrity: sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==}
engines: {node: '>= 0.6'} peerDependencies:
react: ^19.2.4
reflect-metadata@0.2.2: react@19.2.4:
resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==} resolution: {integrity: sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==}
engines: {node: '>=0.10.0'}
reusify@1.1.0: reusify@1.1.0:
resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
@@ -465,46 +431,29 @@ packages:
run-parallel@1.2.0: run-parallel@1.2.0:
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
scheduler@0.27.0:
resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==}
selfsigned@2.4.1: selfsigned@2.4.1:
resolution: {integrity: sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==} resolution: {integrity: sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==}
engines: {node: '>=10'} engines: {node: '>=10'}
selfsigned@5.4.0: sonner@2.0.7:
resolution: {integrity: sha512-Yn8qZOOJv+NhcGY19iC+ngW6hlUCNpvWEkrKllXNhmkLgR9fcErm8EqZ/wev7/tiwjKC9qj17Fa/PtBNzb6q8g==} resolution: {integrity: sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w==}
engines: {node: '>=15.6.0'} peerDependencies:
react: ^18.0.0 || ^19.0.0 || ^19.0.0-rc
react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-rc
send@1.2.1: spark-md5@3.0.2:
resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} resolution: {integrity: sha512-wcFzz9cDfbuqe0FZzfi2or1sgyIrsDwmPwfZC4hiNidPdPINjeUwNfv5kldczoEAcjl9Y1L3SM7Uz2PUEQzxQw==}
engines: {node: '>= 18'}
setprototypeof@1.2.0:
resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
statuses@2.0.2:
resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==}
engines: {node: '>= 0.8'}
to-regex-range@5.0.1: to-regex-range@5.0.1:
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
engines: {node: '>=8.0'} engines: {node: '>=8.0'}
toidentifier@1.0.1:
resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
engines: {node: '>=0.6'}
tr46@0.0.3: tr46@0.0.3:
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
tslib@1.14.1:
resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==}
tslib@2.8.1:
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
tsyringe@4.10.0:
resolution: {integrity: sha512-axr3IdNuVIxnaK5XGEUFTu3YmAQ6lllgrvqfEoR16g/HGnYY/6We4oWENtAnzK6/LpJ2ur9PAb80RBt7/U4ugw==}
engines: {node: '>= 6.0.0'}
undici-types@5.26.5: undici-types@5.26.5:
resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}
@@ -524,19 +473,41 @@ packages:
whatwg-url@5.0.0: whatwg-url@5.0.0:
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
zustand@5.0.11:
resolution: {integrity: sha512-fdZY+dk7zn/vbWNCYmzZULHRrss0jx5pPFiOuMZ/5HJN6Yv3u+1Wswy/4MpZEkEGhtNH+pwxZB8OKgUBPzYAGg==}
engines: {node: '>=12.20.0'}
peerDependencies:
'@types/react': '>=18.0.0'
immer: '>=9.0.6'
react: '>=18.0.0'
use-sync-external-store: '>=1.2.0'
peerDependenciesMeta:
'@types/react':
optional: true
immer:
optional: true
react:
optional: true
use-sync-external-store:
optional: true
snapshots: snapshots:
'@kevisual/cache@0.0.4': '@kevisual/cache@0.0.5':
dependencies: dependencies:
idb-keyval: 6.2.2 idb-keyval: 6.2.2
lru-cache: 11.2.4 lru-cache: 11.2.4
nanoid: 5.1.6 nanoid: 5.1.6
'@kevisual/js-filter@0.0.3': {} '@kevisual/code-builder@0.0.6': {}
'@kevisual/context@0.0.6': {}
'@kevisual/js-filter@0.0.5': {}
'@kevisual/load@0.0.6': '@kevisual/load@0.0.6':
dependencies: dependencies:
eventemitter3: 5.0.1 eventemitter3: 5.0.4
'@kevisual/query@0.0.18': '@kevisual/query@0.0.18':
dependencies: dependencies:
@@ -546,30 +517,31 @@ snapshots:
- ws - ws
- zod - zod
'@kevisual/query@0.0.33': {} '@kevisual/query@0.0.47': {}
'@kevisual/remote-app@0.0.4': {}
'@kevisual/router@0.0.20': '@kevisual/router@0.0.20':
dependencies: dependencies:
path-to-regexp: 8.2.0 path-to-regexp: 8.2.0
selfsigned: 2.4.1 selfsigned: 2.4.1
'@kevisual/router@0.0.52': '@kevisual/router@0.0.75':
dependencies: dependencies:
eventemitter3: 5.0.1 es-toolkit: 1.44.0
path-to-regexp: 8.3.0
selfsigned: 5.4.0
send: 1.2.1
transitivePeerDependencies:
- supports-color
'@kevisual/types@0.0.10': {} '@kevisual/types@0.0.10': {}
'@kevisual/use-config@1.0.21(dotenv@17.2.3)': '@kevisual/types@0.0.12': {}
'@kevisual/use-config@1.0.30(dotenv@17.3.1)':
dependencies: dependencies:
'@kevisual/load': 0.0.6 '@kevisual/load': 0.0.6
dotenv: 17.2.3 dotenv: 17.3.1
'@noble/hashes@1.4.0': {} '@kevisual/ws@8.19.0': {}
'@noble/hashes@2.0.1': {}
'@nodelib/fs.scandir@2.1.5': '@nodelib/fs.scandir@2.1.5':
dependencies: dependencies:
@@ -583,99 +555,15 @@ snapshots:
'@nodelib/fs.scandir': 2.1.5 '@nodelib/fs.scandir': 2.1.5
fastq: 1.19.1 fastq: 1.19.1
'@peculiar/asn1-cms@2.6.0': '@paralleldrive/cuid2@3.3.0':
dependencies: dependencies:
'@peculiar/asn1-schema': 2.6.0 '@noble/hashes': 2.0.1
'@peculiar/asn1-x509': 2.6.0 bignumber.js: 9.3.1
'@peculiar/asn1-x509-attr': 2.6.0 error-causes: 3.0.2
asn1js: 3.0.6
tslib: 2.8.1
'@peculiar/asn1-csr@2.6.0': '@types/bun@1.3.9':
dependencies: dependencies:
'@peculiar/asn1-schema': 2.6.0 bun-types: 1.3.9
'@peculiar/asn1-x509': 2.6.0
asn1js: 3.0.6
tslib: 2.8.1
'@peculiar/asn1-ecc@2.6.0':
dependencies:
'@peculiar/asn1-schema': 2.6.0
'@peculiar/asn1-x509': 2.6.0
asn1js: 3.0.6
tslib: 2.8.1
'@peculiar/asn1-pfx@2.6.0':
dependencies:
'@peculiar/asn1-cms': 2.6.0
'@peculiar/asn1-pkcs8': 2.6.0
'@peculiar/asn1-rsa': 2.6.0
'@peculiar/asn1-schema': 2.6.0
asn1js: 3.0.6
tslib: 2.8.1
'@peculiar/asn1-pkcs8@2.6.0':
dependencies:
'@peculiar/asn1-schema': 2.6.0
'@peculiar/asn1-x509': 2.6.0
asn1js: 3.0.6
tslib: 2.8.1
'@peculiar/asn1-pkcs9@2.6.0':
dependencies:
'@peculiar/asn1-cms': 2.6.0
'@peculiar/asn1-pfx': 2.6.0
'@peculiar/asn1-pkcs8': 2.6.0
'@peculiar/asn1-schema': 2.6.0
'@peculiar/asn1-x509': 2.6.0
'@peculiar/asn1-x509-attr': 2.6.0
asn1js: 3.0.6
tslib: 2.8.1
'@peculiar/asn1-rsa@2.6.0':
dependencies:
'@peculiar/asn1-schema': 2.6.0
'@peculiar/asn1-x509': 2.6.0
asn1js: 3.0.6
tslib: 2.8.1
'@peculiar/asn1-schema@2.6.0':
dependencies:
asn1js: 3.0.6
pvtsutils: 1.3.6
tslib: 2.8.1
'@peculiar/asn1-x509-attr@2.6.0':
dependencies:
'@peculiar/asn1-schema': 2.6.0
'@peculiar/asn1-x509': 2.6.0
asn1js: 3.0.6
tslib: 2.8.1
'@peculiar/asn1-x509@2.6.0':
dependencies:
'@peculiar/asn1-schema': 2.6.0
asn1js: 3.0.6
pvtsutils: 1.3.6
tslib: 2.8.1
'@peculiar/x509@1.14.2':
dependencies:
'@peculiar/asn1-cms': 2.6.0
'@peculiar/asn1-csr': 2.6.0
'@peculiar/asn1-ecc': 2.6.0
'@peculiar/asn1-pkcs9': 2.6.0
'@peculiar/asn1-rsa': 2.6.0
'@peculiar/asn1-schema': 2.6.0
'@peculiar/asn1-x509': 2.6.0
pvtsutils: 1.3.6
reflect-metadata: 0.2.2
tslib: 2.8.1
tsyringe: 4.10.0
'@types/bun@1.3.5':
dependencies:
bun-types: 1.3.5
'@types/node-fetch@2.6.12': '@types/node-fetch@2.6.12':
dependencies: dependencies:
@@ -694,10 +582,12 @@ snapshots:
dependencies: dependencies:
undici-types: 6.21.0 undici-types: 6.21.0
'@types/node@25.0.3': '@types/node@25.2.3':
dependencies: dependencies:
undici-types: 7.16.0 undici-types: 7.16.0
'@types/spark-md5@3.0.5': {}
abort-controller@3.0.0: abort-controller@3.0.0:
dependencies: dependencies:
event-target-shim: 5.0.1 event-target-shim: 5.0.1
@@ -706,23 +596,17 @@ snapshots:
dependencies: dependencies:
humanize-ms: 1.2.1 humanize-ms: 1.2.1
asn1js@3.0.6:
dependencies:
pvtsutils: 1.3.6
pvutils: 1.1.5
tslib: 2.8.1
asynckit@0.4.0: {} asynckit@0.4.0: {}
bignumber.js@9.3.1: {}
braces@3.0.3: braces@3.0.3:
dependencies: dependencies:
fill-range: 7.1.1 fill-range: 7.1.1
bun-types@1.3.5: bun-types@1.3.9:
dependencies: dependencies:
'@types/node': 25.0.3 '@types/node': 25.2.3
bytestreamjs@2.0.1: {}
call-bind-apply-helpers@1.0.2: call-bind-apply-helpers@1.0.2:
dependencies: dependencies:
@@ -733,15 +617,9 @@ snapshots:
dependencies: dependencies:
delayed-stream: 1.0.0 delayed-stream: 1.0.0
debug@4.4.3:
dependencies:
ms: 2.1.3
delayed-stream@1.0.0: {} delayed-stream@1.0.0: {}
depd@2.0.0: {} dotenv@17.3.1: {}
dotenv@17.2.3: {}
dunder-proto@1.0.1: dunder-proto@1.0.1:
dependencies: dependencies:
@@ -749,9 +627,7 @@ snapshots:
es-errors: 1.3.0 es-errors: 1.3.0
gopd: 1.2.0 gopd: 1.2.0
ee-first@1.1.1: {} error-causes@3.0.2: {}
encodeurl@2.0.0: {}
es-define-property@1.0.1: {} es-define-property@1.0.1: {}
@@ -768,15 +644,11 @@ snapshots:
has-tostringtag: 1.0.2 has-tostringtag: 1.0.2
hasown: 2.0.2 hasown: 2.0.2
es-toolkit@1.43.0: {} es-toolkit@1.44.0: {}
escape-html@1.0.3: {}
etag@1.8.1: {}
event-target-shim@5.0.1: {} event-target-shim@5.0.1: {}
eventemitter3@5.0.1: {} eventemitter3@5.0.4: {}
fast-glob@3.3.3: fast-glob@3.3.3:
dependencies: dependencies:
@@ -808,10 +680,10 @@ snapshots:
node-domexception: 1.0.0 node-domexception: 1.0.0
web-streams-polyfill: 4.0.0-beta.3 web-streams-polyfill: 4.0.0-beta.3
fresh@2.0.0: {}
function-bind@1.1.2: {} function-bind@1.1.2: {}
fuse.js@7.1.0: {}
get-intrinsic@1.3.0: get-intrinsic@1.3.0:
dependencies: dependencies:
call-bind-apply-helpers: 1.0.2 call-bind-apply-helpers: 1.0.2
@@ -846,22 +718,12 @@ snapshots:
dependencies: dependencies:
function-bind: 1.1.2 function-bind: 1.1.2
http-errors@2.0.1:
dependencies:
depd: 2.0.0
inherits: 2.0.4
setprototypeof: 1.2.0
statuses: 2.0.2
toidentifier: 1.0.1
humanize-ms@1.2.1: humanize-ms@1.2.1:
dependencies: dependencies:
ms: 2.1.3 ms: 2.1.3
idb-keyval@6.2.2: {} idb-keyval@6.2.2: {}
inherits@2.0.4: {}
is-extglob@2.1.1: {} is-extglob@2.1.1: {}
is-glob@4.0.3: is-glob@4.0.3:
@@ -883,16 +745,10 @@ snapshots:
mime-db@1.52.0: {} mime-db@1.52.0: {}
mime-db@1.54.0: {}
mime-types@2.1.35: mime-types@2.1.35:
dependencies: dependencies:
mime-db: 1.52.0 mime-db: 1.52.0
mime-types@3.0.2:
dependencies:
mime-db: 1.54.0
ms@2.1.3: {} ms@2.1.3: {}
nanoid@5.1.6: {} nanoid@5.1.6: {}
@@ -905,10 +761,6 @@ snapshots:
node-forge@1.3.1: {} node-forge@1.3.1: {}
on-finished@2.4.1:
dependencies:
ee-first: 1.1.1
openai@4.98.0: openai@4.98.0:
dependencies: dependencies:
'@types/node': 18.19.100 '@types/node': 18.19.100
@@ -921,32 +773,20 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- encoding - encoding
path-to-regexp@8.2.0: {} path-browserify-esm@1.0.6: {}
path-to-regexp@8.3.0: {} path-to-regexp@8.2.0: {}
picomatch@2.3.1: {} picomatch@2.3.1: {}
pkijs@3.3.3:
dependencies:
'@noble/hashes': 1.4.0
asn1js: 3.0.6
bytestreamjs: 2.0.1
pvtsutils: 1.3.6
pvutils: 1.1.5
tslib: 2.8.1
pvtsutils@1.3.6:
dependencies:
tslib: 2.8.1
pvutils@1.1.5: {}
queue-microtask@1.2.3: {} queue-microtask@1.2.3: {}
range-parser@1.2.1: {} react-dom@19.2.4(react@19.2.4):
dependencies:
react: 19.2.4
scheduler: 0.27.0
reflect-metadata@0.2.2: {} react@19.2.4: {}
reusify@1.1.0: {} reusify@1.1.0: {}
@@ -954,52 +794,26 @@ snapshots:
dependencies: dependencies:
queue-microtask: 1.2.3 queue-microtask: 1.2.3
scheduler@0.27.0: {}
selfsigned@2.4.1: selfsigned@2.4.1:
dependencies: dependencies:
'@types/node-forge': 1.3.11 '@types/node-forge': 1.3.11
node-forge: 1.3.1 node-forge: 1.3.1
selfsigned@5.4.0: sonner@2.0.7(react-dom@19.2.4(react@19.2.4))(react@19.2.4):
dependencies: dependencies:
'@peculiar/x509': 1.14.2 react: 19.2.4
pkijs: 3.3.3 react-dom: 19.2.4(react@19.2.4)
send@1.2.1: spark-md5@3.0.2: {}
dependencies:
debug: 4.4.3
encodeurl: 2.0.0
escape-html: 1.0.3
etag: 1.8.1
fresh: 2.0.0
http-errors: 2.0.1
mime-types: 3.0.2
ms: 2.1.3
on-finished: 2.4.1
range-parser: 1.2.1
statuses: 2.0.2
transitivePeerDependencies:
- supports-color
setprototypeof@1.2.0: {}
statuses@2.0.2: {}
to-regex-range@5.0.1: to-regex-range@5.0.1:
dependencies: dependencies:
is-number: 7.0.0 is-number: 7.0.0
toidentifier@1.0.1: {}
tr46@0.0.3: {} tr46@0.0.3: {}
tslib@1.14.1: {}
tslib@2.8.1: {}
tsyringe@4.10.0:
dependencies:
tslib: 1.14.1
undici-types@5.26.5: {} undici-types@5.26.5: {}
undici-types@6.21.0: {} undici-types@6.21.0: {}
@@ -1014,3 +828,7 @@ snapshots:
dependencies: dependencies:
tr46: 0.0.3 tr46: 0.0.3
webidl-conversions: 3.0.1 webidl-conversions: 3.0.1
zustand@5.0.11(react@19.2.4):
optionalDependencies:
react: 19.2.4

1
query/query-ai/index.ts Normal file
View File

@@ -0,0 +1 @@
export * from './query-ai.ts'

1
query/query-app/index.ts Normal file
View File

@@ -0,0 +1 @@
export * from './query-app.ts'

View File

@@ -22,11 +22,38 @@ export class QueryApp extends BaseQuery {
data: data, data: data,
}, opts); }, opts);
} }
getApp(data: any, opts?: DataOpts) { getApp(data: {
key?: string;
version?: string;
/**
* 当app不存在会创建一个新的
* 之后上传文件就好了,会执行检测任务
*/
create?: boolean;
}, opts?: DataOpts) {
return this.query.post({ return this.query.post({
path: 'app', path: 'app',
key: 'get', key: 'get',
data: data, data: data,
}, opts); }, opts);
} }
/**
* 发布应用
* @param data
* @param opts
* @returns
*/
publichApp(data: {
id?: string;
username?: string,
detect?: boolean,
appKey?: string;
version?: string;
}, opts?: DataOpts) {
return this.query.post({
path: 'app',
key: 'publish',
data: { detect: true, ...data },
}, opts);
}
} }

View File

@@ -0,0 +1 @@
export * from './query-config.ts';

View File

@@ -0,0 +1 @@
export * from './query-login-browser.ts';

View File

@@ -31,6 +31,8 @@ export type CacheLoginUser = {
id?: string; id?: string;
accessToken?: string; accessToken?: string;
refreshToken?: string; refreshToken?: string;
accessTokenExpiresIn?: number;
createdAt?: number;
}; };
type CacheLogin = { type CacheLogin = {
loginUsers: CacheLoginUser[]; loginUsers: CacheLoginUser[];
@@ -162,6 +164,8 @@ export class LoginCacheStore implements CacheStore<any> {
this.cacheData.id = user.id; this.cacheData.id = user.id;
this.cacheData.accessToken = user.accessToken; this.cacheData.accessToken = user.accessToken;
this.cacheData.refreshToken = user.refreshToken; this.cacheData.refreshToken = user.refreshToken;
this.cacheData.accessTokenExpiresIn = user.accessTokenExpiresIn;
this.cacheData.createdAt = user.createdAt;
await this.setValue(this.cacheData); await this.setValue(this.cacheData);
} }

View File

@@ -6,7 +6,7 @@ export class QueryLoginBrowser extends QueryLogin {
constructor(opts: QueryLoginNodeOptsWithoutCache) { constructor(opts: QueryLoginNodeOptsWithoutCache) {
super({ super({
...opts, ...opts,
cache: new MyCache('login'), cache: new MyCache({ key: 'login' }),
}); });
} }
} }

View File

@@ -1,6 +1,5 @@
import { Query, BaseQuery } from '@kevisual/query'; import { Query, BaseQuery } from '@kevisual/query';
import type { Result, DataOpts } from '@kevisual/query/query'; import type { Result, DataOpts } from '@kevisual/query/query';
import { setBaseResponse } from '@kevisual/query/query';
import { LoginCacheStore, CacheStore } from './login-cache.ts'; import { LoginCacheStore, CacheStore } from './login-cache.ts';
import { Cache } from './login-cache.ts'; import { Cache } from './login-cache.ts';
import { BaseLoad } from '@kevisual/load'; import { BaseLoad } from '@kevisual/load';
@@ -39,7 +38,7 @@ export class QueryLogin extends BaseQuery {
this.isBrowser = opts?.isBrowser ?? true; this.isBrowser = opts?.isBrowser ?? true;
this.init(); this.init();
this.onLoad = opts?.onLoad; this.onLoad = opts?.onLoad;
this.storage = opts?.storage || localStorage; this.storage = opts?.storage || globalThis?.localStorage;
} }
setQuery(query: Query) { setQuery(query: Query) {
this.query = query; this.query = query;
@@ -67,9 +66,9 @@ export class QueryLogin extends BaseQuery {
async login(data: QueryLoginData) { async login(data: QueryLoginData) {
const res = await this.post<QueryLoginResult>({ key: 'login', ...data }); const res = await this.post<QueryLoginResult>({ key: 'login', ...data });
if (res.code === 200) { if (res.code === 200) {
const { accessToken, refreshToken } = res?.data || {}; const { accessToken, refreshToken, accessTokenExpiresIn } = res?.data || {};
this.storage.setItem('token', accessToken || ''); this.storage.setItem('token', accessToken || '');
await this.beforeSetLoginUser({ accessToken, refreshToken }); await this.beforeSetLoginUser({ accessToken, refreshToken, accessTokenExpiresIn });
} }
return res; return res;
} }
@@ -81,9 +80,9 @@ export class QueryLogin extends BaseQuery {
async loginByCode(data: { phone: string; code: string }) { async loginByCode(data: { phone: string; code: string }) {
const res = await this.post<QueryLoginResult>({ path: 'sms', key: 'login', data }); const res = await this.post<QueryLoginResult>({ path: 'sms', key: 'login', data });
if (res.code === 200) { if (res.code === 200) {
const { accessToken, refreshToken } = res?.data || {}; const { accessToken, refreshToken, accessTokenExpiresIn } = res?.data || {};
this.storage.setItem('token', accessToken || ''); this.storage.setItem('token', accessToken || '');
await this.beforeSetLoginUser({ accessToken, refreshToken }); await this.beforeSetLoginUser({ accessToken, refreshToken, accessTokenExpiresIn });
} }
return res; return res;
} }
@@ -91,17 +90,17 @@ export class QueryLogin extends BaseQuery {
* 设置token * 设置token
* @param token * @param token
*/ */
async setLoginToken(token: { accessToken: string; refreshToken: string }) { async setLoginToken(token: { accessToken: string; refreshToken: string; accessTokenExpiresIn?: number }) {
const { accessToken, refreshToken } = token; const { accessToken, refreshToken, accessTokenExpiresIn } = token;
this.storage.setItem('token', accessToken || ''); this.storage.setItem('token', accessToken || '');
await this.beforeSetLoginUser({ accessToken, refreshToken }); await this.beforeSetLoginUser({ accessToken, refreshToken, accessTokenExpiresIn });
} }
async loginByWechat(data: { code: string }) { async loginByWechat(data: { code: string }) {
const res = await this.post<QueryLoginResult>({ path: 'wx', key: 'open-login', code: data.code }); const res = await this.post<QueryLoginResult>({ path: 'wx', key: 'open-login', code: data.code });
if (res.code === 200) { if (res.code === 200) {
const { accessToken, refreshToken } = res?.data || {}; const { accessToken, refreshToken, accessTokenExpiresIn } = res?.data || {};
this.storage.setItem('token', accessToken || ''); this.storage.setItem('token', accessToken || '');
await this.beforeSetLoginUser({ accessToken, refreshToken }); await this.beforeSetLoginUser({ accessToken, refreshToken, accessTokenExpiresIn });
} }
return res; return res;
} }
@@ -126,7 +125,7 @@ export class QueryLogin extends BaseQuery {
* 登陆成功,需要获取用户信息进行缓存 * 登陆成功,需要获取用户信息进行缓存
* @param param0 * @param param0
*/ */
async beforeSetLoginUser({ accessToken, refreshToken, check401 }: { accessToken?: string; refreshToken?: string; check401?: boolean }) { async beforeSetLoginUser({ accessToken, refreshToken, check401, accessTokenExpiresIn }: { accessTokenExpiresIn?: number, accessToken?: string; refreshToken?: string; check401?: boolean }) {
if (accessToken && refreshToken) { if (accessToken && refreshToken) {
const resUser = await this.getMe(accessToken, check401); const resUser = await this.getMe(accessToken, check401);
if (resUser.code === 200) { if (resUser.code === 200) {
@@ -137,6 +136,8 @@ export class QueryLogin extends BaseQuery {
id: user.id, id: user.id,
accessToken, accessToken,
refreshToken, refreshToken,
accessTokenExpiresIn,
createdAt: Date.now(),
}); });
} else { } else {
console.error('登录失败'); console.error('登录失败');
@@ -164,7 +165,6 @@ export class QueryLogin extends BaseQuery {
{ key: 'refreshToken', data }, { key: 'refreshToken', data },
{ {
afterResponse: async (response, ctx) => { afterResponse: async (response, ctx) => {
setBaseResponse(response);
return response as any; return response as any;
}, },
}, },
@@ -185,9 +185,9 @@ export class QueryLogin extends BaseQuery {
if (hasRefreshToken) { if (hasRefreshToken) {
const res = await that.queryRefreshToken(hasRefreshToken); const res = await that.queryRefreshToken(hasRefreshToken);
if (res.code === 200) { if (res.code === 200) {
const { accessToken, refreshToken } = res?.data || {}; const { accessToken, refreshToken, accessTokenExpiresIn } = res?.data || {};
that.storage.setItem('token', accessToken || ''); that.storage.setItem('token', accessToken || '');
await that.beforeSetLoginUser({ accessToken, refreshToken, check401: false }); await that.beforeSetLoginUser({ accessToken, refreshToken, accessTokenExpiresIn, check401: false });
if (refetch && ctx && ctx.req && ctx.req.url && ctx.fetch) { if (refetch && ctx && ctx.req && ctx.req.url && ctx.fetch) {
await new Promise((resolve) => setTimeout(resolve, 1500)); await new Promise((resolve) => setTimeout(resolve, 1500));
const url = ctx.req?.url; const url = ctx.req?.url;
@@ -198,7 +198,6 @@ export class QueryLogin extends BaseQuery {
body: body, body: body,
headers: { ...headers, Authorization: `Bearer ${accessToken}` }, headers: { ...headers, Authorization: `Bearer ${accessToken}` },
}); });
setBaseResponse(res);
return res; return res;
} }
} else { } else {
@@ -336,12 +335,17 @@ export class QueryLogin extends BaseQuery {
const user = localUserList.find((userItem) => userItem.user!.username === username); const user = localUserList.find((userItem) => userItem.user!.username === username);
if (user) { if (user) {
this.storage.setItem('token', user.accessToken || ''); this.storage.setItem('token', user.accessToken || '');
await this.beforeSetLoginUser({ accessToken: user.accessToken, refreshToken: user.refreshToken }); await this.beforeSetLoginUser({
accessToken: user.accessToken,
refreshToken: user.refreshToken,
accessTokenExpiresIn: user.accessTokenExpiresIn
});
return { return {
code: 200, code: 200,
data: { data: {
accessToken: user.accessToken, accessToken: user.accessToken,
refreshToken: user.refreshToken, refreshToken: user.refreshToken,
accessTokenExpiresIn: user.accessTokenExpiresIn,
}, },
success: true, success: true,
message: '切换用户成功', message: '切换用户成功',
@@ -350,9 +354,9 @@ export class QueryLogin extends BaseQuery {
const res = await this.postSwitchUser(username); const res = await this.postSwitchUser(username);
if (res.code === 200) { if (res.code === 200) {
const { accessToken, refreshToken } = res?.data || {}; const { accessToken, refreshToken, accessTokenExpiresIn } = res?.data || {};
this.storage.setItem('token', accessToken || ''); this.storage.setItem('token', accessToken || '');
await this.beforeSetLoginUser({ accessToken, refreshToken }); await this.beforeSetLoginUser({ accessToken, refreshToken, accessTokenExpiresIn });
} }
return res; return res;
} }
@@ -409,9 +413,9 @@ export class QueryLogin extends BaseQuery {
loginToken: token, loginToken: token,
}); });
if (res.code === 200) { if (res.code === 200) {
const accessToken = res.data?.accessToken; const { accessTokenExpiresIn, accessToken, refreshToken } = res.data;
this.storage.setItem('token', accessToken || ''); this.storage.setItem('token', accessToken || '');
await this.beforeSetLoginUser({ accessToken, refreshToken: res.data?.refreshToken }); await this.beforeSetLoginUser({ accessToken, refreshToken, accessTokenExpiresIn });
return res; return res;
} }
return false; return false;
@@ -421,7 +425,7 @@ export class QueryLogin extends BaseQuery {
* *
* *
import MD5 from 'crypto-js/md5.js'; // import MD5 from 'crypto-js/md5.js';
import jsonwebtoken from 'jsonwebtoken'; import jsonwebtoken from 'jsonwebtoken';
*/ */

154
query/query-mark/index.ts Normal file
View File

@@ -0,0 +1,154 @@
import { Query } from '@kevisual/query';
import type { Result, DataOpts } from '@kevisual/query/query';
export type SimpleObject = Record<string, any>;
export const markType = ['simple', 'md', 'mdx', 'wallnote', 'excalidraw', 'chat'] as const;
export type MarkType = (typeof markType)[number];
export type MarkData = {
nodes?: any[];
edges?: any[];
elements?: any[];
permission?: any;
[key: string]: any;
};
export type Mark = {
id: string;
title: string;
description: string;
markType: MarkType;
link: string;
data?: MarkData;
uid: string;
puid: string;
summary: string;
thumbnail?: string;
tags: string[];
createdAt: string;
updatedAt: string;
version: number;
};
export type ShowMarkPick = Pick<Mark, 'id' | 'title' | 'description' | 'summary' | 'link' | 'tags' | 'thumbnail' | 'updatedAt'>;
export type SearchOpts = {
page?: number;
pageSize?: number;
search?: string;
sort?: string; // DESC, ASC
markType?: MarkType; // 类型
[key: string]: any;
};
export type QueryMarkOpts<T extends SimpleObject = SimpleObject> = {
query?: Query;
isBrowser?: boolean;
onLoad?: () => void;
} & T;
export type ResultMarkList = {
list: Mark[];
pagination: {
pageSize: number;
current: number;
total: number;
};
};
export type QueryMarkData = {
id?: string;
title?: string;
description?: string;
[key: string]: any;
};
export type QueryMarkResult = {
accessToken: string;
refreshToken: string;
};
export class QueryMarkBase<T extends SimpleObject = SimpleObject> {
query: Query;
isBrowser: boolean;
load?: boolean;
storage?: Storage;
onLoad?: () => void;
constructor(opts?: QueryMarkOpts<T>) {
this.query = opts?.query || new Query();
this.isBrowser = opts?.isBrowser ?? true;
this.init();
this.onLoad = opts?.onLoad;
}
setQuery(query: Query) {
this.query = query;
}
private async init() {
this.load = true;
this.onLoad?.();
}
async post<T = Result<any>>(data: any, opts?: DataOpts): Promise<T> {
try {
return this.query.post({ path: 'mark', ...data }, opts) as Promise<T>;
} catch (error) {
console.log('error', error);
return {
code: 400,
} as any;
}
}
async getMarkList(search: SearchOpts, opts?: DataOpts) {
return this.post<Result<ResultMarkList>>({ key: 'list', ...search }, opts);
}
async getMark(id: string, opts?: DataOpts) {
return this.post<Result<Mark>>({ key: 'get', id }, opts);
}
async getVersion(id: string, opts?: DataOpts) {
return this.post<Result<{ version: number; id: string }>>({ key: 'getVersion', id }, opts);
}
/**
* 检查版本
* 当需要更新时返回true
* @param id
* @param version
* @param opts
* @returns
*/
async checkVersion(id: string, version?: number, opts?: DataOpts) {
if (!version) {
return true;
}
const res = await this.getVersion(id, opts);
if (res.code === 200) {
if (res.data!.version > version) {
return true;
}
return false;
}
return true;
}
async updateMark(data: any, opts?: DataOpts) {
return this.post<Result<Mark>>({ key: 'update', data }, opts);
}
async deleteMark(id: string, opts?: DataOpts) {
return this.post<Result<Mark>>({ key: 'delete', id }, opts);
}
}
export class QueryMark extends QueryMarkBase<SimpleObject> {
markType: string;
constructor(opts?: QueryMarkOpts & { markType?: MarkType }) {
super(opts);
this.markType = opts?.markType || 'simple';
}
async getMarkList(search?: SearchOpts, opts?: DataOpts) {
return this.post<Result<ResultMarkList>>({ key: 'list', ...search, markType: this.markType }, opts);
}
async updateMark(data: any, opts?: DataOpts) {
if (!data.id) {
data.markType = this.markType || 'simple';
}
return super.updateMark(data, opts);
}
}

View File

@@ -1,405 +1,3 @@
import { Query, Result } from '@kevisual/query/query'; export * from './proxy.ts';
import { QueryRouterServer, Route } from '@kevisual/router/src/route.ts';
import { filter } from '@kevisual/js-filter'
import { EventEmitter } from 'eventemitter3';
export const RouteTypeList = ['api', 'context', 'worker', 'page'] as const; export { initApi } from './router-api-proxy.ts';
export type RouterViewItem = RouterViewApi | RouterViewContext | RouterViewWorker | RouteViewPage;
type RouteViewBase = {
id: string;
title: string;
description: string;
enabled?: boolean;
}
export type RouterViewApi = {
type: 'api',
api: {
url: string,
// 已初始化的query实例不需要编辑配置
query?: Query
}
} & RouteViewBase;
export type RouterViewContext = {
type: 'context',
context: {
key: string,
// 从context中获取router不需要编辑配置
router?: QueryRouterServer
}
} & RouteViewBase;
export type RouterViewWorker = {
type: 'worker',
worker: {
type: 'Worker' | 'SharedWorker' | 'serviceWorker',
url: string,
// 已初始化的worker实例不需要编辑配置
worker?: Worker | SharedWorker | ServiceWorker,
/**
* worker选项
* default: { type: 'module' }
*/
workerOptions?: {
type: 'module' | 'classic'
}
}
} & RouteViewBase;
/**
* 注入 js 的url地址使用importScripts加载
*/
export type RouteViewPage = {
type: 'page',
page: {
url: string,
}
} & RouteViewBase;
export type RouterViewQuery = {
id: string,
query: string,
title: string
}
export type RouterViewData = {
data: { items: RouterViewItem[]; }
views: RouterViewQuery[];
viewId?: string;
[key: string]: any;
}
export class QueryProxy {
router: QueryRouterServer;
token?: string;
routerViewItems: RouterViewItem[];
views: RouterViewQuery[];
emitter: EventEmitter;
constructor(opts?: { router?: QueryRouterServer, token?: string, routerViewData?: RouterViewData }) {
this.router = opts?.router || new QueryRouterServer();
this.token = opts?.token || this.getDefulatToken();
this.routerViewItems = opts?.routerViewData?.data?.items || [];
this.views = opts?.routerViewData?.views || [];
this.initRouterViewQuery();
this.emitter = new EventEmitter();
}
getDefulatToken() {
try {
if (typeof window !== 'undefined' && typeof window.localStorage !== 'undefined') {
return localStorage.getItem('token') || undefined;
}
} catch (e) {
return undefined;
}
}
async initRouterViewQuery() {
this.routerViewItems = this.routerViewItems.map(item => {
if (item.type === 'api' && item.api?.url) {
const url = item.api.url;
if (item?.api?.query) return item;
item['api'] = { url: url, query: new Query({ url: url }) };
}
if (item.type === 'worker' && item.worker?.url) {
let viewItem = item as RouterViewWorker;
if (!item.worker?.workerOptions?.type) {
item.worker.workerOptions = { ...item.worker.workerOptions, type: 'module' };
}
if (item.worker.worker) {
return item;
}
let worker: Worker | SharedWorker | ServiceWorker | undefined = undefined;
if (item.worker.type === 'SharedWorker') {
worker = new SharedWorker(item.worker.url, item.worker.workerOptions);
worker.port.start();
} else if (viewItem.worker.type === 'serviceWorker') {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register(viewItem.worker.url, item.worker.workerOptions).then(function (registration) {
console.debug('注册serviceWorker成功 ', registration.scope);
}, function (err) {
console.debug('注册 serviceWorker 失败: ', err);
});
} else {
console.warn('当前浏览器不支持serviceWorker');
}
} else {
worker = new Worker(viewItem.worker.url, item.worker.workerOptions);
}
viewItem['worker']['worker'] = worker;
}
if (item.type === 'context' && item.context?.key) {
if (item.context?.router) {
return item;
}
// @ts-ignore
const context = globalThis['context'] || {}
const router = context[item.context.key] as QueryRouterServer;
if (router) {
item['context']['router'] = router;
}
}
return item;
}).filter(item => {
const enabled = item.enabled ?? true;
return enabled;
});
}
/**
* 初始化路由
* @returns
*/
async init() {
const routerViewItems = this.routerViewItems || [];
if (routerViewItems.length === 0) {
// 默认初始化api类型路由
await this.initApi();
return;
}
for (const item of routerViewItems) {
switch (item.type) {
case 'api':
await this.initApi(item);
break;
case 'context':
await this.initContext(item);
break;
case 'worker':
await this.initWorker(item);
break;
case 'page':
await this.initPage(item);
break;
}
}
this.emitter.emit('initComplete');
}
/**
* 监听初始化完成
* @returns
*/
async listenInitComplete(): Promise<boolean> {
return new Promise((resolve) => {
const timer = setTimeout(() => {
this.emitter.removeAllListeners('initComplete');
resolve(false);
}, 3 * 60000); // 3分钟超时
const func = () => {
clearTimeout(timer);
resolve(true);
}
this.emitter.once('initComplete', func);
});
}
async initApi(item?: RouterViewApi) {
const that = this;
const query = item?.api?.query || new Query({ url: item?.api?.url || '/api/router' })
const res = await query.post<{ list: RouterItem[] }>({ path: "router", key: 'list', token: this.token });
if (res.code !== 200) {
console.error('Failed to init query proxy router:', res.message);
return
}
const _list = res.data?.list || []
for (const r of _list) {
if (r.path || r.id) {
console.debug(`注册路由: [${r.path}] ${r?.key}`, 'API');
let metadata = r.metadata || {};
metadata.viewItem = item;
this.router.route({
path: r.path,
key: r.key || '',
id: r.id,
description: r.description,
metadata: metadata,
}).define(async (ctx) => {
const msg = { ...ctx.query };
if (msg.token === undefined && that.token !== undefined) {
msg.token = that.token;
}
const res = await query.post<any>({ path: r.path, key: r.key, ...msg });
ctx.forward(res)
}).addTo(that.router);
}
}
}
async initContext(item?: RouterViewContext) {
// @ts-ignore
const context = globalThis['context'] || {}
const router = item?.context?.router || context[item?.context?.key] as QueryRouterServer;
if (!router) {
console.warn(`未发现Context router ${item?.context?.key}`);
return
}
const routes = router.getList();
for (const r of routes) {
console.debug(`注册路由: [${r.path}] ${r?.key}`, 'Context');
let metadata = r.metadata || {};
metadata.viewItem = item;
this.router.route({
path: r.path,
key: r.key || '',
id: r.id,
description: r.description,
metadata: metadata,
}).define(async (ctx) => {
const res = await router.run({ path: r.path, key: r.key, ...ctx.query });
ctx.forward(res)
}).addTo(this.router);
}
}
generateId() {
return 'route_' + Math.random().toString(36).substring(2, 9);
}
async initWorker(item?: RouterViewWorker) {
const that = this;
if (!item?.worker?.url) {
console.warn('Worker URL not provided');
return;
}
const viewItem = item.worker;
const worker = viewItem?.worker;
if (!worker) {
console.warn('Worker not initialized');
return;
}
const callResponse = (e: MessageEvent) => {
const msg = e.data;
if (msg.requestId) {
const requestId = msg.requestId;
that.emitter.emit(requestId, msg);
} else {
that.router.run(msg);
}
}
if (item.worker.type === 'SharedWorker') {
const port = (worker as SharedWorker).port;
port.onmessage = callResponse;
port.start();
} else if (item.worker.type === 'serviceWorker') {
navigator.serviceWorker.addEventListener('message', callResponse);
} else {
(worker as Worker).onmessage = callResponse;
}
const callWorker = async (msg: any, viewItem: RouterViewWorker['worker']): Promise<Result> => {
const requestId = this.generateId();
const worker = viewItem?.worker;
if (!worker) {
return { code: 500, message: 'Worker未初始化' };
}
let port: MessagePort | Worker | ServiceWorker;
if (viewItem.type === 'SharedWorker') {
port = (worker as SharedWorker).port;
} else {
port = worker as Worker | ServiceWorker;
}
port.postMessage({
...msg,
requestId: requestId,
})
return new Promise((resolve) => {
const timer = setTimeout(() => {
that.emitter.removeAllListeners(requestId);
resolve({ code: 500, message: '请求超时' });
}, 3 * 60 * 1000); // 3分钟超时
that.emitter.once(requestId, (res: any) => {
clearTimeout(timer);
resolve(res);
});
});
}
const res = await callWorker({
path: "router",
key: 'list',
token: this.token,
}, viewItem);
if (res.code !== 200) {
console.error('Failed to init query proxy router:', res.message);
return;
}
const _list = res.data?.list || []
for (const r of _list) {
if (r.path || r.id) {
console.debug(`注册路由: [${r.path}] ${r?.key}`, 'API');
let metadata = r.metadata || {};
metadata.viewItem = item;
this.router.route({
path: r.path,
key: r.key || '',
id: r.id,
description: r.description,
metadata: metadata,
}).define(async (ctx) => {
const msg = { ...ctx.query };
if (msg.token === undefined && that.token !== undefined) {
msg.token = that.token;
}
const res = await callWorker({ path: r.path, key: r.key, ...msg }, viewItem);
ctx.forward(res)
}).addTo(that.router);
}
}
}
async initPage(item?: RouteViewPage) {
if (!item?.page?.url) {
console.warn('Page地址未提供');
return;
}
const url = item.page.url;
try {
if (typeof window !== 'undefined') {
await import(url).then((module) => { }).catch((err) => {
console.error('引入Page脚本失败:', url, err);
});
}
} catch (e) {
console.warn('引入Page脚本失败:', url, e);
return;
}
}
/**
* 列出路由
* @param filter
* @param query WHERE metadata.tags CONTAINS 'premium'
* @returns
*/
async listRoutes(filterFn?: (item: Route) => boolean, opts?: { viewId?: string, query?: string }) {
let query = opts?.query;
if (opts?.viewId) {
const view = this.views.find(v => v.id === opts.viewId);
if (view) {
query = view.query;
}
} if (opts?.query) {
query = opts.query;
}
const routes = this.router.routes.filter(filterFn || (() => true));
if (query) {
return filter(routes, query);
}
return routes;
}
async getViewQuery(viewId: string) {
const view = this.views.find(v => v.id === viewId);
if (view) {
return view.query;
}
return undefined;
}
/**
* 运行路由
* @param msg
* @returns
*/
async run(msg: { id?: string, path?: string, key?: string }) {
return await this.router.run(msg);
}
}
type RouterItem = {
id?: string;
path?: string;
key?: string;
description?: string;
middleware?: string[];
metadata?: Record<string, any>;
}

496
query/query-proxy/proxy.ts Normal file
View File

@@ -0,0 +1,496 @@
import { QueryClient as Query, Result } from '@kevisual/query';
import { QueryRouterServer, type App, type Route, fromJSONSchema, toJSONSchema } from '@kevisual/router/browser';
import { filter } from '@kevisual/js-filter'
import { EventEmitter } from 'eventemitter3';
import { initApi } from './router-api-proxy.ts';
import Fuse from 'fuse.js';
export const RouteTypeList = ['api', 'context', 'worker', 'page'] as const;
export type RouterViewItemInfo = RouterViewApi | RouterViewContext | RouterViewWorker | RouteViewPage;
export type RouterViewItem<T = {}> = RouterViewItemInfo & T;
type RouteViewBase = {
/**
* _id 用于纯本地存储标识
*/
_id?: string;
id?: string;
title?: string;
description?: string;
enabled?: boolean;
/**
* 响应数据
*/
response?: any;
/**
* 默认动作配置
*/
action?: { path?: string; key?: string; id?: string; payload?: any;[key: string]: any };
}
export type RouterViewApi = {
type: 'api',
api: {
url: string,
// 已初始化的query实例不需要编辑配置
query?: Query
}
} & RouteViewBase;
export type RouterViewContext = {
type: 'context',
context: {
key: string,
// 从context中获取router不需要编辑配置
router?: QueryRouterServer
}
} & RouteViewBase;
export type RouterViewWorker = {
type: 'worker',
worker: {
type: 'Worker' | 'SharedWorker' | 'serviceWorker',
url: string,
// 已初始化的worker实例不需要编辑配置
worker?: Worker | SharedWorker | ServiceWorker,
/**
* worker选项
* default: { type: 'module' }
*/
workerOptions?: {
type: 'module' | 'classic'
}
}
} & RouteViewBase;
/**
* 去掉不需要保存的服务器的数据
* @param item
* @returns
*/
export const pickRouterViewData = (item: RouterViewItem) => {
const { action, response, _id, ...rest } = item;
if (rest.type === 'api') {
if (rest.api) {
delete rest.api.query;
}
}
if (rest.type === 'worker') {
if (rest.worker) {
delete rest.worker.worker;
}
}
if (rest.type === 'context') {
if (rest.context) {
delete rest.context.router;
}
}
return rest
}
/**
* 注入 js 的url地址使用 importScripts 加载
*/
export type RouteViewPage = {
type: 'page',
page: {
url: string,
}
} & RouteViewBase;
export type RouterViewQuery = {
id: string,
query: string,
title: string
}
/**
* 后端存储结构
*/
export type RouterViewData = {
data: { items: RouterViewItem[]; }
views: RouterViewQuery[];
viewId?: string;
[key: string]: any;
}
export class QueryProxy {
router: QueryRouterServer;
token?: string;
routerViewItems: RouterViewItem[];
views: RouterViewQuery[];
emitter: EventEmitter;
constructor(opts?: { router?: QueryRouterServer, token?: string, routerViewData?: RouterViewData }) {
this.router = opts?.router || new QueryRouterServer();
this.token = opts?.token || this.getDefulatToken();
this.routerViewItems = opts?.routerViewData?.data?.items || [];
this.views = opts?.routerViewData?.views || [];
this.initRouterViewQuery();
this.emitter = new EventEmitter();
}
private getDefulatToken() {
try {
if (typeof window !== 'undefined' && typeof window.localStorage !== 'undefined') {
return localStorage.getItem('token') || undefined;
}
} catch (e) {
return undefined;
}
}
initRouterViewQuery() {
this.routerViewItems = this.routerViewItems.map(item => {
return this.initRouterView(item);
}).filter(item => {
const enabled = item.enabled ?? true;
return enabled;
});
}
private initRouterView(item: RouterViewItem) {
if (item.type === 'api' && item.api?.url) {
const url = item.api.url;
if (item?.api?.query) return item;
const query = new Query({ url: url });
item['api'] = { url: url, query: query };
}
if (item.type === 'worker' && item.worker?.url) {
let viewItem = item as RouterViewWorker;
if (!item.worker?.workerOptions?.type) {
item.worker.workerOptions = { ...item.worker.workerOptions, type: 'module' };
}
if (item.worker.worker) {
return item;
}
let worker: Worker | SharedWorker | ServiceWorker | undefined = undefined;
if (item.worker.type === 'SharedWorker') {
worker = new SharedWorker(item.worker.url, item.worker.workerOptions);
worker.port.start();
} else if (viewItem.worker.type === 'serviceWorker') {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register(viewItem.worker.url, item.worker.workerOptions).then(function (registration) {
console.debug('注册serviceWorker成功 ', registration.scope);
}, function (err) {
console.debug('注册 serviceWorker 失败: ', err);
});
} else {
console.warn('当前浏览器不支持serviceWorker');
}
} else {
worker = new Worker(viewItem.worker.url, item.worker.workerOptions);
}
viewItem['worker']['worker'] = worker;
}
if (item.type === 'context' && item.context?.key) {
if (item.context?.router) {
return item;
}
// @ts-ignore
const context = globalThis['context'] || {}
const router = context[item.context.key] as QueryRouterServer;
if (router) {
item['context']['router'] = router;
}
}
return item;
}
/**
* 初始化路由
* main
* @returns
*/
async init() {
const routerViewItems = this.routerViewItems || [];
if (routerViewItems.length === 0) {
// 默认初始化api类型路由
await this.initApi();
return;
}
for (const item of routerViewItems) {
switch (item.type) {
case 'api':
await this.initApi(item);
break;
case 'context':
await this.initContext(item);
break;
case 'worker':
await this.initWorker(item);
break;
case 'page':
await this.initPage(item);
break;
}
}
this.emitter.emit('initComplete');
}
/**
* 监听初始化完成
* @returns
*/
async listenInitComplete(): Promise<boolean> {
return new Promise((resolve) => {
const timer = setTimeout(() => {
this.emitter.removeAllListeners('initComplete');
resolve(false);
}, 3 * 60000); // 3分钟超时
const func = () => {
clearTimeout(timer);
resolve(true);
}
this.emitter.once('initComplete', func);
});
}
async initApi(item?: RouterViewApi) {
initApi({ item: item, router: this.router, token: this.token });
}
async initContext(item?: RouterViewContext) {
// @ts-ignore
const context = globalThis['context'] || {}
const router = item?.context?.router || context[item?.context?.key] as QueryRouterServer;
if (!router) {
console.warn(`未发现Context router ${item?.context?.key}`);
return
}
const routes = router.getList();
// TODO: args
// const args = fromJSONSchema(r);
for (const r of routes) {
console.debug(`注册路由: [${r.path}] ${r?.key}`, 'Context');
let metadata = r.metadata || {};
metadata.viewItem = item;
metadata.source = 'query-proxy-context';
this.router.route({
path: r.path,
key: r.key || '',
id: r.id,
description: r.description,
metadata: metadata,
}).define(async (ctx) => {
const res = await router.run({ path: r.path, key: r.key, ...ctx.query });
ctx.forward(res)
}).addTo(this.router);
}
}
generateId() {
return 'route_' + Math.random().toString(36).substring(2, 9);
}
private async callWorker(msg: any, viewItem: RouterViewWorker['worker']): Promise<Result> {
const that = this;
const requestId = this.generateId();
const worker = viewItem?.worker;
if (!worker) {
return { code: 500, message: 'Worker未初始化' };
}
let port: MessagePort | Worker | ServiceWorker;
if (viewItem.type === 'SharedWorker') {
port = (worker as SharedWorker).port;
} else {
port = worker as Worker | ServiceWorker;
}
port.postMessage({
...msg,
requestId: requestId,
})
return new Promise((resolve) => {
const timer = setTimeout(() => {
that.emitter.removeAllListeners(requestId);
resolve({ code: 500, message: '请求超时' });
}, 3 * 60 * 1000); // 3分钟超时
that.emitter.once(requestId, (res: any) => {
clearTimeout(timer);
resolve(res);
});
});
}
private async initWorker(item?: RouterViewWorker, initRoutes: boolean = true) {
const that = this;
if (!item?.worker?.url) {
console.warn('Worker URL not provided');
return;
}
const viewItem = item.worker;
const worker = viewItem?.worker;
if (!worker) {
console.warn('Worker not initialized');
return;
}
const callResponse = (e: MessageEvent) => {
const msg = e.data;
if (msg.requestId) {
const requestId = msg.requestId;
that.emitter.emit(requestId, msg);
} else {
that.router.run(msg);
}
}
if (item.worker.type === 'SharedWorker') {
const port = (worker as SharedWorker).port;
port.onmessage = callResponse;
port.start();
} else if (item.worker.type === 'serviceWorker') {
navigator.serviceWorker.addEventListener('message', callResponse);
} else {
(worker as Worker).onmessage = callResponse;
}
if (!initRoutes) {
return;
}
const callWorker = this.callWorker.bind(this);
const res = await callWorker({
path: "router",
key: 'list',
token: this.token,
}, viewItem);
if (res.code !== 200) {
console.error('Failed to init query proxy router:', res.message);
return;
}
const _list = res.data?.list || []
for (const r of _list) {
if (r.path || r.id) {
console.debug(`注册路由: [${r.path}] ${r?.key}`, 'API');
let metadata = r.metadata || {};
metadata.viewItem = item;
metadata.source = 'query-proxy-worker';
this.router.route({
path: r.path,
key: r.key || '',
id: r.id,
description: r.description,
metadata: metadata,
}).define(async (ctx) => {
const msg = { ...ctx.query };
if (msg.token === undefined && that.token !== undefined) {
msg.token = that.token;
}
const res = await callWorker({ path: r.path, key: r.key, ...msg }, viewItem);
ctx.forward(res)
}).addTo(that.router);
}
}
}
private async initPage(item?: RouteViewPage) {
if (!item?.page?.url) {
console.warn('Page地址未提供');
return;
}
const url = item.page.url;
try {
if (typeof window !== 'undefined') {
await import(url).then((module) => { }).catch((err) => {
console.error('引入Page脚本失败:', url, err);
});
}
} catch (e) {
console.warn('引入Page脚本失败:', url, e);
return;
}
}
private getQueryByViewId(viewId: string): string | undefined {
const view = this.views.find(v => v.id === viewId);
if (view) {
return view.query;
}
return undefined;
}
/**
* 列出路由
* @param filter
* @param query WHERE metadata.tags CONTAINS 'premium'
* @returns
*/
async listRoutes(filterFn?: (item: Route) => boolean, opts?: { viewId?: string, query?: string }) {
let query = opts?.query;
if (opts?.viewId && !query) {
query = this.getQueryByViewId(opts.viewId);
}
const routes = this.router.routes.filter(filterFn || (() => true));
if (query) {
if (query.toLocaleUpperCase().startsWith('WHERE')) {
return filter(routes, query);
} else {
const fuse = new Fuse(routes, {
keys: ['path', 'key', 'description'],
threshold: 0.4,
});
let findsRoutes = fuse.search(query);
const resultRoutes = findsRoutes.map(r => r.item);
return resultRoutes;
}
}
return routes;
}
async getViewQuery(viewId: string) {
const view = this.views.find(v => v.id === viewId);
if (view) {
return view.query;
}
return undefined;
}
/**
* 运行路由
* @param msg
* @returns
*/
async run(msg: { id?: string, path?: string, key?: string }) {
return await this.router.run(msg);
}
async runByRouteView(routeView: RouterViewItem) {
if (routeView.response) {
return routeView;
}
const item = this.initRouterView(routeView);
if (item.type === 'api' && item.api?.url) {
const query = item.api.query!;
const res = await query.post<any>(item.action || {});
item.response = res;
return item;
} else if (item.type === 'api') {
item.response = { code: 500, message: 'API URL未配置' };
return item;
}
if (item.type === 'context' && item.context?.router) {
const router = item.context.router;
const res = await router.run(item.action || {});
item.response = res;
return item;
}
if (item.type === 'page') {
await this.initPage(item);
const res = await this.router.run(item.action || {});
item.response = res;
return item;
}
if (item.type === 'worker' && item.worker?.worker) {
await this.initWorker(item, false);
const callWorker = this.callWorker.bind(this);
const res = await callWorker(item.action || {}, item.worker);
item.response = res;
return item;
}
item.response = { code: 500, message: '无法处理的路由类型', data: item };
return item;
}
}
export type RouterItem = {
id?: string;
path?: string;
key?: string;
description?: string;
middleware?: string[];
metadata?: Record<string, any>;
}
export const createViewData = (routerViewData: RouterViewItem | RouterViewItem[], data?: RouterViewData): RouterViewData => {
return {
views: [],
viewId: undefined,
data: {
items: Array.isArray(routerViewData) ? routerViewData : [routerViewData],
},
...data
}
}

View File

@@ -0,0 +1,62 @@
import { Query } from "@kevisual/query";
import { RouterViewApi, RouterItem } from "./proxy.ts";
import { type App, type QueryRouterServer } from "@kevisual/router";
import { filter } from "@kevisual/js-filter";
import { isBrowser } from "es-toolkit";
export const initApi = async (opts: {
item?: RouterViewApi,
router: QueryRouterServer | App,
token?: string,
/**
* WHERE path = 'auth' OR path = 'router' OR path = 'call'
*/
exclude?: string;
}) => {
const router = opts?.router! as QueryRouterServer;
const item = opts?.item;
const token = opts?.token;
const query = item?.api?.query || new Query({ url: item?.api?.url || '/api/router' })
const res = await query.post<{ list: RouterItem[] }>({ path: "router", key: 'list', token: token });
if (res.code !== 200) {
return {
code: res.code,
message: `初始化路由失败: ${res.message}, url: ${query.url}`
}
}
let _list = res.data?.list || []
if (opts?.exclude) {
if (opts?.exclude) {
let excludeList = filter(_list, opts.exclude);
const excludeIds = excludeList.map(i => i.id);
_list = _list.filter(i => !excludeIds.includes(i.id!));
}
}
const _isBrowser = isBrowser();
for (const r of _list) {
if (r.path || r.id) {
_isBrowser && console.debug(`注册路由: [${r.path}] ${r?.key}`, 'API');
let metadata = r.metadata || {};
metadata.viewItem = item;
metadata.url = query.url;
metadata.source = 'query-proxy-api';
router.route({
path: r.path,
key: r.key || '',
id: r.id,
description: r.description,
metadata: metadata,
}).define(async (ctx) => {
const msg = { ...ctx.query };
if (msg.token === undefined && token !== undefined && !_isBrowser) {
msg.token = token;
}
const res = await query.post<any>({ path: r.path, key: r.key, ...msg });
ctx.forward(res)
}).addTo(router);
}
}
return {
code: 200,
message: '初始化路由成功'
}
}

View File

@@ -0,0 +1,5 @@
import { RemoteApp } from '@kevisual/remote-app'
export {
RemoteApp
}

View File

@@ -1,14 +1,19 @@
import { adapter, DataOpts, Result } from '@kevisual/query'; import { adapter, DataOpts, Result } from '@kevisual/query';
import path from 'path-browserify-esm';
import { hashContent } from './utils';
type Process = {}
type QueryResourcesOptions = { type QueryResourcesOptions = {
prefix?: string; prefix?: string;
storage?: Storage; storage?: Storage;
username?: string; username?: string;
onProcess?: (opts?: Process) => void;
[key: string]: any; [key: string]: any;
}; };
export class QueryResources { export class QueryResources {
prefix: string; // root/resources prefix: string; // root/resources
storage: Storage; storage: Storage;
onProcess?: (opts?: Process) => void;
constructor(opts: QueryResourcesOptions) { constructor(opts: QueryResourcesOptions) {
if (opts.username) { if (opts.username) {
this.prefix = `/${opts.username}/resources/`; this.prefix = `/${opts.username}/resources/`;
@@ -16,10 +21,14 @@ export class QueryResources {
this.prefix = opts.prefix || ''; this.prefix = opts.prefix || '';
} }
this.storage = opts.storage || localStorage; this.storage = opts.storage || localStorage;
this.onProcess = opts.onProcess || (() => { });
} }
setUsername(username: string) { setUsername(username: string) {
this.prefix = `/${username}/resources/`; this.prefix = `/${username}/resources/`;
} }
setPrefix(prefix: string) {
this.prefix = prefix;
}
header(headers?: Record<string, string>, json = true): Record<string, string> { header(headers?: Record<string, string>, json = true): Record<string, string> {
const token = this.storage.getItem('token'); const token = this.storage.getItem('token');
const _headers: Record<string, string> = { const _headers: Record<string, string> = {
@@ -54,18 +63,237 @@ export class QueryResources {
}); });
} }
async fetchFile(filepath: string, opts?: DataOpts): Promise<Result<any>> { async fetchFile(filepath: string, opts?: DataOpts): Promise<Result<any>> {
return fetch(`${this.prefix}${filepath}`, { const url = `${this.prefix}${filepath}`;
method: 'GET', return this.get({}, { url, method: 'GET', ...opts, headers: this.header(opts?.headers, false), isText: true });
headers: this.header(opts?.headers, false),
}).then(async (res) => {
if (!res.ok) {
return {
code: 500,
success: false,
message: `Failed to fetch file: ${res.status} ${res.statusText}`,
} as Result<any>;
} }
return { code: 200, data: await res.text(), success: true } as Result<any>; async uploadFile(filepath: string, content: string | Blob, opts?: DataOpts & { chunkSize?: number, maxSize?: number }): Promise<Result<any>> {
const pathname = `${this.prefix}${filepath}`;
const filename = path.basename(pathname);
const type = getContentType(filename);
const url = new URL(pathname, window.location.origin);
const hashResult = hashContent(content);
// Blob 类型时 hashContent 返回 Promise
const hash = hashResult instanceof Promise ? await hashResult : hashResult;
url.searchParams.set('hash', hash);
const { chunkSize, maxSize, ...restOpts } = opts || {};
// 判断是否需要分块上传文件大于20MB
const isBlob = content instanceof Blob;
const fileSize = isBlob ? content.size : new Blob([content]).size;
const CHUNK_THRESHOLD = maxSize ?? 20 * 1024 * 1024; // 20MB
if (fileSize > CHUNK_THRESHOLD && isBlob) {
// 使用分块上传
return this.uploadChunkedFile(filepath, content, hash, { chunkSize, ...restOpts });
}
this.onProcess?.({ type: 'uploadBegin', filename, size: fileSize, process: 0 });
const formData = new FormData();
if (isBlob) {
formData.append('file', content);
} else {
formData.append('file', new Blob([content], { type }));
}
const res = await adapter({
url: url.toString(),
isPostFile: true,
method: 'POST',
body: formData,
timeout: 5 * 60 * 1000, // 5分钟超时
...restOpts,
headers: { ...restOpts?.headers, ...this.header(restOpts?.headers, false) },
params: {
hash: hash,
...restOpts?.params,
},
});
this.onProcess?.({ type: 'uploadFinish', filename, size: fileSize, process: 100 });
return res;
}
async uploadChunkedFile(filepath: string, file: Blob, hash: string, opts?: DataOpts & { chunkSize?: number }): Promise<Result<any>> {
const pathname = `${this.prefix}${filepath}`;
const filename = path.basename(pathname);
const url = new URL(pathname, window.location.origin);
url.searchParams.set('hash', hash);
url.searchParams.set('chunk', '1');
console.log(`url,`, url, hash);
// 预留 eventSource 支持(暂不处理)
// const createEventSource = opts?.createEventSource;
const { chunkSize: _chunkSize, ...restOpts } = opts || {};
const chunkSize = _chunkSize ?? 5 * 1024 * 1024; // 5MB
const totalChunks = Math.ceil(file.size / chunkSize);
this.onProcess?.({ type: 'uploadBegin', filename, size: file.size, process: 0 });
for (let currentChunk = 0; currentChunk < totalChunks; currentChunk++) {
this.onProcess?.({ type: 'uploadChunkedFile', filename, size: file.size, process: 0, totalChunks, currentChunk: currentChunk + 1 });
const start = currentChunk * chunkSize;
const end = Math.min(start + chunkSize, file.size);
const chunkBlob = file.slice(start, end);
// 转换为 File 类型
const chunkFile = new File([chunkBlob], filename, { type: file.type || 'application/octet-stream' });
const formData = new FormData();
formData.append('file', chunkFile, filename);
formData.append('chunkIndex', currentChunk.toString());
formData.append('totalChunks', totalChunks.toString());
console.log(`Uploading chunk ${currentChunk + 1}/${totalChunks}`, url.toString());
try {
const res = await adapter({
url: url.toString(),
isPostFile: true,
method: 'POST',
body: formData,
timeout: 5 * 60 * 1000, // 5分钟超时
...restOpts,
headers: { ...restOpts?.headers, ...this.header(restOpts?.headers, false) },
params: {
hash: hash,
chunk: '1',
chunkIndex: currentChunk,
totalChunks,
...restOpts?.params,
},
});
if (res.code !== 200) {
throw new Error(`Chunk 上传失败 code ${res!.code}, 错误信息是: ${res!.message}`);
}
console.log(`Chunk ${currentChunk + 1}/${totalChunks} uploaded`, res);
this.onProcess?.({ type: 'uploadChunkedFile', filename, size: file.size, process: Math.round(((currentChunk + 1) / totalChunks) * 100), totalChunks, currentChunk: currentChunk + 1 });
} catch (error) {
console.error(`Error uploading chunk ${currentChunk + 1}/${totalChunks}`, error);
return { code: 500, message: `分块上传失败: ${(error as Error).message}` };
}
}
this.onProcess?.({ type: 'uploadFinish', filename, size: file.size, process: 100 });
return { code: 200, message: '上传成功' };
}
async getStat(filepath: string, opts?: DataOpts): Promise<Result<Stat>> {
const url = `${this.prefix}${filepath}`;
return adapter({
url,
params: {
stat: '1',
},
method: 'GET' as any,
headers: this.header(opts?.headers),
});
}
/**
* @deprecated use getStat instead
* @param filepath
* @param opts
* @returns
*/
async getState(filepath: string, opts?: DataOpts): Promise<Result<Stat>> {
return this.getStat(filepath, opts);
}
async createFolder(folderpath: string, opts?: DataOpts): Promise<Result<any>> {
const filepath = folderpath.endsWith('/') ? `${folderpath}keep.txt` : `${folderpath}/keep.txt`;
return this.uploadFile(filepath, '文件夹占位,其他文件不存在,文件夹不存在,如果有其他文件夹,删除当前文件夹占位文件即可', opts);
}
async rename(oldpath: string, newpath: string, opts?: DataOpts): Promise<Result<any>> {
const pathname = `${this.prefix}${oldpath}`;
const newName = `${this.prefix}${newpath}`;
const params = {
newName: newName,
};
const url = pathname
return adapter({
url,
method: 'PUT' as any,
headers: this.header(opts?.headers),
params,
});
}
async deleteFile(filepath: string, opts?: DataOpts): Promise<Result<any>> {
const url = `${this.prefix}${filepath}`;
return adapter({
url,
method: 'DELETE' as any,
headers: this.header(opts?.headers),
}); });
} }
} }
export const getContentType = (filename: string): string => {
const ext = path.extname(filename);
let type = 'text/plain';
switch (ext) {
case '':
type = 'application/octet-stream';
break;
case '.json':
type = 'application/json';
break;
case '.txt':
type = 'text/plain';
break;
case '.csv':
type = 'text/csv';
break;
case '.md':
type = 'text/markdown';
break;
case '.html':
case '.htm':
type = 'text/html';
break;
case '.xml':
type = 'application/xml';
break;
case '.js':
type = 'application/javascript';
break;
case '.css':
type = 'text/css';
break;
case '.ts':
type = 'application/typescript';
break;
case '.pdf':
type = 'application/pdf';
break;
case '.zip':
type = 'application/zip';
break;
case '.docx':
type = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
break;
case '.xlsx':
type = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
break;
case '.mp3':
type = 'audio/mpeg';
break;
case '.mp4':
type = 'video/mp4';
break;
case '.png':
case '.jpg':
case '.jpeg':
case '.gif':
case '.webp':
type = `image/${ext.slice(1)}`;
break;
case '.svg':
type = 'image/svg+xml';
break;
}
return type;
};
type Stat = {
"standardHeaders": any,
"size": string,
"etag": string,
"lastModified": string,
"metaData": {
"app-source": string,
"cache-control": string,
"content-type": string,
"share"?: string
},
"versionId": null
}

View File

@@ -0,0 +1,31 @@
import SparkMD5 from 'spark-md5';
export const hashContent = (str: string | Blob | Buffer): Promise<string> | string => {
if (typeof str === 'string') {
return SparkMD5.hash(str);
} else if (str instanceof Blob) {
return hashBlob(str);
} else if (Buffer.isBuffer(str)) {
return SparkMD5.hash(str.toString());
}
console.error('hashContent error: input must be a string, Blob, or Buffer');
return '';
};
// 直接计算整个 Blob 的 MD5
export const hashBlob = (blob: Blob): Promise<string> => {
return new Promise(async (resolve, reject) => {
try {
const spark = new SparkMD5.ArrayBuffer();
spark.append(await blob.arrayBuffer());
resolve(spark.end());
} catch (error) {
console.error('hashBlob error', error);
reject(error);
}
});
};
export const hashFile = (file: File): Promise<string> => {
return hashBlob(file);
};

View File

@@ -0,0 +1 @@
export * from './query-secret.ts'

View File

@@ -0,0 +1 @@
export * from './query-shop.ts'

View File

@@ -0,0 +1 @@
export * from './query-upload-browser.ts';

1
query/utils/index.ts Normal file
View File

@@ -0,0 +1 @@
export * from './random.ts';

44
query/utils/random.ts Normal file
View File

@@ -0,0 +1,44 @@
import { customAlphabet } from 'nanoid';
import { createId } from '@paralleldrive/cuid2';
export const letter = 'abcdefghijklmnopqrstuvwxyz';
export const number = '0123456789';
const alphanumeric = `${letter}${number}`;
export const alphanumericWithDash = `${alphanumeric}-`;
export const cuid2 = createId;
/**
* 创建一个随机的字母字符串
*/
export const uuid = customAlphabet(letter);
/**
* 创建一个随机的 id包含字母和数字
*/
export const nanoid = customAlphabet(alphanumeric, 16);
/**
* 创建一个随机的 id包含字母、数字和短横线
*/
export const nanoidWithDash = customAlphabet(alphanumericWithDash, 16);
/**
* 创建一个随机的 id以字母开头的字符串
* @param number
* @returns
*/
export const randomId = (number: number) => {
const _letter = uuid(1);
return `${_letter}${nanoid(number)}`;
};
/**
* 创建一个随机的字母字符串
* @param number
* @param opts
* @returns
*/
export const randomLetter = (number: number = 8, opts?: { before?: string; after?: string }) => {
const { before = '', after = '' } = opts || {};
return `${before}${uuid(number)}${after}`;
};

View File

@@ -0,0 +1,37 @@
import glob from 'fast-glob';
const lists = glob.sync('query/*/index.ts', {
absolute: true,
});
export const queryList = lists.map((filePath) => {
const segments = filePath.split('/');
const queryName = segments[segments.length - 2];
return {
name: queryName,
pkgs: `"./${queryName}": "./dist/${queryName}.js",`
};
});
// console.log('Query List:', queryList);
const pkgs = queryList.map(q => q.pkgs).join('\n');
console.log('Package Entries:\n\r', pkgs);
const storeLists = glob.sync('store/*/index.ts', {
absolute: true,
});
export const storeList = storeLists.map((filePath) => {
const segments = filePath.split('/');
const storeName = segments[segments.length - 2];
return {
name: storeName,
pkgs: `"./${storeName}": "./dist/${storeName}.js",`
};
});
// console.log('Store List:', storeList);
const storePkgs = storeList.map(s => s.pkgs).join('\n');
console.log('Store Entries:\n\r', storePkgs);

66
store/store-auth/index.ts Normal file
View File

@@ -0,0 +1,66 @@
'use strict';
import { create } from 'zustand';
import { toast } from 'sonner';
import { useContextKey } from '@kevisual/context';
import { type QueryLoginBrowser } from '@/query/query-login/index.ts'
const queryLogin = useContextKey<QueryLoginBrowser>('queryLogin');
type Me = {
id?: string;
username?: string;
nickname?: string | null;
needChangePassword?: boolean;
role?: string;
description?: string | null;
type?: 'user' | 'org';
orgs?: string[];
avatar?: string;
};
export type LayoutStore = {
open: boolean;
setOpen: (open: boolean) => void;
me: Me;
setMe: (me: Me) => void;
getMe: () => Promise<void>;
openUser: boolean;
setOpenUser: (openUser: boolean) => void;
switchOrg: (username?: string, type?: 'user' | 'org') => Promise<void>;
isAdmin: boolean;
setIsAdmin: (isAdmin: boolean) => void;
checkHasOrg: () => boolean;
};
export const useLayoutStore = create<LayoutStore>((set, get) => ({
open: false,
setOpen: (open) => set({ open }),
me: {},
setMe: (me) => set({ me }),
getMe: async () => {
const res = await queryLogin.getMe();
if (res.code === 200) {
set({ me: res.data });
set({ isAdmin: res.data.orgs?.includes('admin') });
}
},
openUser: false,
setOpenUser: (openUser) => set({ openUser }),
switchOrg: async (username?: string, type?: string) => {
const res = await queryLogin.switchUser(username || '');
if (res.code === 200) {
toast.success('Switch success');
setTimeout(() => {
window.location.reload();
}, 1000);
} else {
toast.error(res.message || 'Request failed');
}
},
isAdmin: false,
setIsAdmin: (isAdmin) => set({ isAdmin }),
checkHasOrg: () => {
const user = get().me || {};
if (!user.orgs) {
return false;
}
return user?.orgs?.length > 0;
},
}));

132
store/store-mark/index.ts Normal file
View File

@@ -0,0 +1,132 @@
import { create } from 'zustand';
import { type Result } from '@kevisual/query/query';
import { MarkType, Mark, QueryMark } from '@kevisual/api/query-mark';
import { uniqBy } from 'es-toolkit';
import { useContextKey } from '@kevisual/context';
import { type QueryLoginBrowser } from '@/query/query-login/index.ts'
const queryClient = useContextKey<QueryLoginBrowser>('queryLogin');
type ManagerStore = {
/** 当前选中的Mark */
currentMarkId: string;
setCurrentMarkId: (markId: string) => void;
markData?: Mark | undefined;
setMarkData: (mark?: Partial<Mark>) => void;
/** 获取Mark列表 */
getList: () => Promise<any>;
getMarkFromList: (markId: string) => Mark | undefined;
updateMark: (mark: Mark) => Promise<any>;
getMark: (markId: string) => Promise<Result<Mark>>;
deleteMark: (markId: string) => Promise<any>;
/** Mark列表 */
list: Mark[];
setList: (list: Mark[]) => void;
pagination: {
current: number;
pageSize: number;
total: number;
};
setPagination: (pagination: { current: number; pageSize: number; total: number }) => void;
/** 搜索 */
search: string;
setSearch: (search: string) => void;
/** 初始化 */
init: (markType: MarkType) => Promise<void>;
queryMark?: QueryMark;
markType?: MarkType;
open: boolean;
setOpen: (open: boolean) => void;
};
export const useMarkStore = create<ManagerStore>((set, get) => {
return {
currentMarkId: '',
setCurrentMarkId: (markId: string) => set(() => ({ currentMarkId: markId })),
open: false,
setOpen: (open: boolean) => set(() => ({ open })),
getList: async () => {
const queryMark = get().queryMark!;
const { search, pagination } = get();
const res = await queryMark.getMarkList({ page: pagination.current, pageSize: pagination.pageSize, search });
const oldList = get().list;
if (res.code === 200) {
const { pagination, list } = res.data || {};
const newList = [...oldList, ...list!];
const uniqueList = uniqBy(newList, (item) => item.id);
set(() => ({ list: uniqueList }));
set(() => ({ pagination: { current: pagination!.current, pageSize: pagination!.pageSize, total: pagination!.total } }));
}
},
getMarkFromList: (markId: string) => {
return get().list.find((item) => item.id === markId);
},
updateMark: async (mark: Mark) => {
const queryMark = get().queryMark!;
const res = await queryMark.updateMark(mark);
if (res.code === 200) {
set((state) => {
const oldList = state.list;
const resMark = res.data!;
const newList = oldList.map((item) => (item.id === mark.id ? mark : item));
if (!mark.id) {
newList.unshift(resMark);
}
return {
list: newList,
};
});
}
return res;
},
getMark: async (markId: string) => {
const queryMark = get().queryMark!;
const res = await queryMark.getMark(markId);
return res;
},
list: [],
setList: (list: any[]) => set(() => ({ list })),
init: async (markType: MarkType = 'wallnote') => {
// await get().getList();
console.log('init', set, get);
const queryMark = new QueryMark({
query: queryClient as any,
markType,
});
const url = new URL(window.location.href);
const pageSize = url.searchParams.get('pageSize') || '10';
set({ queryMark, markType, list: [], pagination: { current: 1, pageSize: parseInt(pageSize), total: 0 }, currentMarkId: '', markData: undefined });
setTimeout(async () => {
console.log('get', get);
get().getList();
}, 1000);
},
deleteMark: async (markId: string) => {
const queryMark = get().queryMark!;
const res = await queryMark.deleteMark(markId);
const currentMarkId = get().currentMarkId;
if (res.code === 200) {
// get().getList();
set((state) => ({
list: state.list.filter((item) => item.id !== markId),
}));
if (currentMarkId === markId) {
set(() => ({ currentMarkId: '', markData: undefined }));
}
}
return res;
},
queryMark: undefined,
markType: 'simple',
markData: undefined,
setMarkData: (mark?: Partial<Mark>) => set(() => ({ markData: mark as Mark })),
pagination: {
current: 1,
pageSize: 10,
total: 0,
},
setPagination: (pagination: { current: number; pageSize: number; total: number }) => set(() => ({ pagination })),
/** 搜索 */
search: '',
setSearch: (search: string) => set(() => ({ search, list: [], pagination: { current: 1, pageSize: 10, total: 0 } })),
};
});

23
tsconfig.json Normal file
View File

@@ -0,0 +1,23 @@
{
"extends": "@kevisual/types/json/frontend.json",
"compilerOptions": {
"baseUrl": "./",
"jsx": "react-jsx",
"paths": {
"@/query*": [
"query/*"
],
"@/store*": [
"store/*"
],
"@/*": [
"src/*"
]
},
},
"include": [
"src",
"query",
"store",
]
}