@planet-matrix/mobius-model 0.6.0 → 0.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +50 -0
- package/oxlint.config.ts +1 -2
- package/package.json +29 -17
- package/scripts/build.ts +2 -52
- package/src/ai/README.md +1 -0
- package/src/ai/ai.ts +107 -0
- package/src/ai/chat-completion-ai/aihubmix-chat-completion.ts +78 -0
- package/src/ai/chat-completion-ai/chat-completion-ai.ts +270 -0
- package/src/ai/chat-completion-ai/chat-completion.ts +189 -0
- package/src/ai/chat-completion-ai/index.ts +7 -0
- package/src/ai/chat-completion-ai/lingyiwanwu-chat-completion.ts +78 -0
- package/src/ai/chat-completion-ai/ohmygpt-chat-completion.ts +78 -0
- package/src/ai/chat-completion-ai/openai-next-chat-completion.ts +78 -0
- package/src/ai/embedding-ai/embedding-ai.ts +63 -0
- package/src/ai/embedding-ai/embedding.ts +50 -0
- package/src/ai/embedding-ai/index.ts +4 -0
- package/src/ai/embedding-ai/openai-next-embedding.ts +23 -0
- package/src/ai/index.ts +4 -0
- package/src/aio/README.md +100 -0
- package/src/aio/content.ts +141 -0
- package/src/aio/index.ts +3 -0
- package/src/aio/json.ts +127 -0
- package/src/aio/prompt.ts +246 -0
- package/src/basic/README.md +20 -15
- package/src/basic/error.ts +19 -5
- package/src/basic/function.ts +2 -2
- package/src/basic/index.ts +1 -0
- package/src/basic/promise.ts +141 -71
- package/src/basic/schedule.ts +111 -0
- package/src/basic/stream.ts +135 -25
- package/src/credential/README.md +107 -0
- package/src/credential/api-key.ts +158 -0
- package/src/credential/bearer.ts +73 -0
- package/src/credential/index.ts +4 -0
- package/src/credential/json-web-token.ts +96 -0
- package/src/credential/password.ts +170 -0
- package/src/cron/README.md +86 -0
- package/src/cron/cron.ts +87 -0
- package/src/cron/index.ts +1 -0
- package/src/drizzle/README.md +1 -0
- package/src/drizzle/drizzle.ts +1 -0
- package/src/drizzle/helper.ts +47 -0
- package/src/drizzle/index.ts +5 -0
- package/src/drizzle/infer.ts +52 -0
- package/src/drizzle/kysely.ts +8 -0
- package/src/drizzle/pagination.ts +198 -0
- package/src/email/README.md +1 -0
- package/src/email/index.ts +1 -0
- package/src/email/resend.ts +25 -0
- package/src/event/class-event-proxy.ts +5 -6
- package/src/event/common.ts +13 -3
- package/src/event/event-manager.ts +3 -3
- package/src/event/instance-event-proxy.ts +5 -6
- package/src/event/internal.ts +4 -4
- package/src/exception/README.md +28 -19
- package/src/exception/error/error.ts +123 -0
- package/src/exception/error/index.ts +2 -0
- package/src/exception/error/match.ts +38 -0
- package/src/exception/error/must-fix.ts +17 -0
- package/src/exception/index.ts +2 -0
- package/src/file-system/find.ts +53 -0
- package/src/file-system/index.ts +2 -0
- package/src/file-system/path.ts +76 -0
- package/src/file-system/resolve.ts +22 -0
- package/src/form/README.md +25 -0
- package/src/form/index.ts +1 -0
- package/src/form/inputor-controller/base.ts +861 -0
- package/src/form/inputor-controller/boolean.ts +39 -0
- package/src/form/inputor-controller/file.ts +39 -0
- package/src/form/inputor-controller/form.ts +179 -0
- package/src/form/inputor-controller/helper.ts +117 -0
- package/src/form/inputor-controller/index.ts +17 -0
- package/src/form/inputor-controller/multi-select.ts +99 -0
- package/src/form/inputor-controller/number.ts +116 -0
- package/src/form/inputor-controller/select.ts +109 -0
- package/src/form/inputor-controller/text.ts +82 -0
- package/src/http/READMD.md +1 -0
- package/src/http/api/api-core.ts +84 -0
- package/src/http/api/api-handler.ts +79 -0
- package/src/http/api/api-host.ts +47 -0
- package/src/http/api/api-result.ts +56 -0
- package/src/http/api/api-schema.ts +154 -0
- package/src/http/api/api-server.ts +130 -0
- package/src/http/api/api-test.ts +142 -0
- package/src/http/api/api-type.ts +34 -0
- package/src/http/api/api.ts +81 -0
- package/src/http/api/index.ts +11 -0
- package/src/http/api-adapter/api-core-node-http.ts +260 -0
- package/src/http/api-adapter/api-host-node-http.ts +156 -0
- package/src/http/api-adapter/api-result-arktype.ts +294 -0
- package/src/http/api-adapter/api-result-zod.ts +286 -0
- package/src/http/api-adapter/index.ts +5 -0
- package/src/http/bin/gen-api-list/gen-api-list.ts +126 -0
- package/src/http/bin/gen-api-list/index.ts +1 -0
- package/src/http/bin/gen-api-test/gen-api-test.ts +136 -0
- package/src/http/bin/gen-api-test/index.ts +1 -0
- package/src/http/bin/gen-api-type/calc-code.ts +25 -0
- package/src/http/bin/gen-api-type/gen-api-type.ts +127 -0
- package/src/http/bin/gen-api-type/index.ts +2 -0
- package/src/http/bin/index.ts +2 -0
- package/src/http/index.ts +3 -0
- package/src/huawei/README.md +1 -0
- package/src/huawei/index.ts +2 -0
- package/src/huawei/moderation/index.ts +1 -0
- package/src/huawei/moderation/moderation.ts +355 -0
- package/src/huawei/obs/esdk-obs-nodejs.d.ts +87 -0
- package/src/huawei/obs/index.ts +1 -0
- package/src/huawei/obs/obs.ts +42 -0
- package/src/index.ts +21 -2
- package/src/json/README.md +92 -0
- package/src/json/index.ts +1 -0
- package/src/json/repair.ts +18 -0
- package/src/log/logger.ts +15 -4
- package/src/openai/README.md +1 -0
- package/src/openai/index.ts +1 -0
- package/src/openai/openai.ts +509 -0
- package/src/orchestration/README.md +9 -7
- package/src/orchestration/dispatching/dispatcher.ts +83 -0
- package/src/orchestration/dispatching/index.ts +2 -0
- package/src/orchestration/dispatching/selector/base-selector.ts +39 -0
- package/src/orchestration/dispatching/selector/down-count-selector.ts +119 -0
- package/src/orchestration/dispatching/selector/index.ts +2 -0
- package/src/orchestration/index.ts +2 -0
- package/src/orchestration/scheduling/index.ts +2 -0
- package/src/orchestration/scheduling/scheduler.ts +103 -0
- package/src/orchestration/scheduling/task.ts +32 -0
- package/src/random/README.md +8 -7
- package/src/random/base.ts +66 -0
- package/src/random/index.ts +5 -1
- package/src/random/random-boolean.ts +40 -0
- package/src/random/random-integer.ts +60 -0
- package/src/random/random-number.ts +72 -0
- package/src/random/random-string.ts +66 -0
- package/src/request/README.md +108 -0
- package/src/request/fetch/base.ts +108 -0
- package/src/request/fetch/browser.ts +280 -0
- package/src/request/fetch/general.ts +20 -0
- package/src/request/fetch/index.ts +4 -0
- package/src/request/fetch/nodejs.ts +280 -0
- package/src/request/index.ts +2 -0
- package/src/request/request/base.ts +246 -0
- package/src/request/request/general.ts +63 -0
- package/src/request/request/index.ts +3 -0
- package/src/request/request/resource.ts +68 -0
- package/src/result/README.md +4 -0
- package/src/result/controller.ts +58 -0
- package/src/result/either.ts +363 -0
- package/src/result/generator.ts +168 -0
- package/src/result/index.ts +3 -0
- package/src/route/README.md +105 -0
- package/src/route/adapter/browser.ts +122 -0
- package/src/route/adapter/driver.ts +56 -0
- package/src/route/adapter/index.ts +2 -0
- package/src/route/index.ts +3 -0
- package/src/route/router/index.ts +2 -0
- package/src/route/router/route.ts +630 -0
- package/src/route/router/router.ts +1641 -0
- package/src/route/uri/hash.ts +307 -0
- package/src/route/uri/index.ts +7 -0
- package/src/route/uri/pathname.ts +376 -0
- package/src/route/uri/search.ts +412 -0
- package/src/service/README.md +1 -0
- package/src/service/index.ts +1 -0
- package/src/service/service.ts +110 -0
- package/src/socket/README.md +105 -0
- package/src/socket/client/index.ts +2 -0
- package/src/socket/client/socket-unit.ts +658 -0
- package/src/socket/client/socket.ts +203 -0
- package/src/socket/common/index.ts +2 -0
- package/src/socket/common/socket-unit-common.ts +23 -0
- package/src/socket/common/socket-unit-heartbeat.ts +427 -0
- package/src/socket/index.ts +3 -0
- package/src/socket/server/index.ts +3 -0
- package/src/socket/server/server.ts +183 -0
- package/src/socket/server/socket-unit.ts +448 -0
- package/src/socket/server/socket.ts +264 -0
- package/src/storage/table.ts +3 -3
- package/src/timer/expiration/expiration-manager.ts +3 -3
- package/src/timer/expiration/remaining-manager.ts +3 -3
- package/src/tube/README.md +99 -0
- package/src/tube/helper.ts +137 -0
- package/src/tube/index.ts +2 -0
- package/src/tube/tube.ts +880 -0
- package/src/weixin/README.md +1 -0
- package/src/weixin/index.ts +2 -0
- package/src/weixin/official-account/authorization.ts +157 -0
- package/src/weixin/official-account/index.ts +2 -0
- package/src/weixin/official-account/js-api.ts +132 -0
- package/src/weixin/open/index.ts +1 -0
- package/src/weixin/open/oauth2.ts +131 -0
- package/tests/unit/ai/ai.spec.ts +85 -0
- package/tests/unit/aio/content.spec.ts +105 -0
- package/tests/unit/aio/json.spec.ts +146 -0
- package/tests/unit/aio/prompt.spec.ts +111 -0
- package/tests/unit/basic/error.spec.ts +16 -4
- package/tests/unit/basic/promise.spec.ts +158 -50
- package/tests/unit/basic/schedule.spec.ts +74 -0
- package/tests/unit/basic/stream.spec.ts +90 -37
- package/tests/unit/credential/api-key.spec.ts +36 -0
- package/tests/unit/credential/bearer.spec.ts +23 -0
- package/tests/unit/credential/json-web-token.spec.ts +23 -0
- package/tests/unit/credential/password.spec.ts +40 -0
- package/tests/unit/cron/cron.spec.ts +84 -0
- package/tests/unit/event/class-event-proxy.spec.ts +3 -3
- package/tests/unit/event/event-manager.spec.ts +3 -3
- package/tests/unit/event/instance-event-proxy.spec.ts +3 -3
- package/tests/unit/exception/error/error.spec.ts +83 -0
- package/tests/unit/exception/error/match.spec.ts +81 -0
- package/tests/unit/form/inputor-controller/base.spec.ts +458 -0
- package/tests/unit/form/inputor-controller/boolean.spec.ts +30 -0
- package/tests/unit/form/inputor-controller/file.spec.ts +27 -0
- package/tests/unit/form/inputor-controller/form.spec.ts +120 -0
- package/tests/unit/form/inputor-controller/helper.spec.ts +67 -0
- package/tests/unit/form/inputor-controller/multi-select.spec.ts +34 -0
- package/tests/unit/form/inputor-controller/number.spec.ts +36 -0
- package/tests/unit/form/inputor-controller/select.spec.ts +49 -0
- package/tests/unit/form/inputor-controller/text.spec.ts +34 -0
- package/tests/unit/http/api/api-core-host.spec.ts +207 -0
- package/tests/unit/http/api/api-schema.spec.ts +120 -0
- package/tests/unit/http/api/api-server.spec.ts +363 -0
- package/tests/unit/http/api/api-test.spec.ts +117 -0
- package/tests/unit/http/api/api.spec.ts +121 -0
- package/tests/unit/http/api-adapter/node-http.spec.ts +187 -0
- package/tests/unit/identifier/uuid.spec.ts +0 -1
- package/tests/unit/json/repair.spec.ts +11 -0
- package/tests/unit/log/logger.spec.ts +19 -4
- package/tests/unit/openai/openai.spec.ts +64 -0
- package/tests/unit/orchestration/dispatching/dispatcher.spec.ts +41 -0
- package/tests/unit/orchestration/dispatching/selector/down-count-selector.spec.ts +81 -0
- package/tests/unit/orchestration/scheduling/scheduler.spec.ts +103 -0
- package/tests/unit/random/base.spec.ts +58 -0
- package/tests/unit/random/random-boolean.spec.ts +25 -0
- package/tests/unit/random/random-integer.spec.ts +32 -0
- package/tests/unit/random/random-number.spec.ts +33 -0
- package/tests/unit/random/random-string.spec.ts +22 -0
- package/tests/unit/request/fetch/browser.spec.ts +222 -0
- package/tests/unit/request/fetch/general.spec.ts +43 -0
- package/tests/unit/request/fetch/nodejs.spec.ts +225 -0
- package/tests/unit/request/request/base.spec.ts +382 -0
- package/tests/unit/request/request/general.spec.ts +160 -0
- package/tests/unit/result/controller.spec.ts +82 -0
- package/tests/unit/result/either.spec.ts +377 -0
- package/tests/unit/result/generator.spec.ts +273 -0
- package/tests/unit/route/router/route.spec.ts +430 -0
- package/tests/unit/route/router/router.spec.ts +407 -0
- package/tests/unit/route/uri/hash.spec.ts +72 -0
- package/tests/unit/route/uri/pathname.spec.ts +146 -0
- package/tests/unit/route/uri/search.spec.ts +107 -0
- package/tests/unit/socket/client.spec.ts +208 -0
- package/tests/unit/socket/server.spec.ts +133 -0
- package/tests/unit/socket/socket-unit-heartbeat.spec.ts +214 -0
- package/tests/unit/tube/helper.spec.ts +139 -0
- package/tests/unit/tube/tube.spec.ts +501 -0
- package/vite.config.ts +2 -1
- package/dist/index.js +0 -50
- package/dist/index.js.map +0 -209
- package/src/random/string.ts +0 -35
- package/tests/unit/random/string.spec.ts +0 -11
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto"
|
|
2
|
+
import fs, { existsSync, mkdirSync } from "node:fs"
|
|
3
|
+
import path from "node:path"
|
|
4
|
+
import ts from "typescript"
|
|
5
|
+
|
|
6
|
+
import { getGlobalLogger } from "#Source/log/index.ts"
|
|
7
|
+
|
|
8
|
+
import { getCalcCode } from "./calc-code.ts"
|
|
9
|
+
|
|
10
|
+
export interface GenerateApiTypeOptions {
|
|
11
|
+
importFrom: string
|
|
12
|
+
/**
|
|
13
|
+
* Path to tsconfig.json file.
|
|
14
|
+
*/
|
|
15
|
+
tsconfigFilePath: string
|
|
16
|
+
/**
|
|
17
|
+
* Path to Api folder.
|
|
18
|
+
*/
|
|
19
|
+
apiFolderPath: string
|
|
20
|
+
/**
|
|
21
|
+
* Path to output file.
|
|
22
|
+
*/
|
|
23
|
+
outputFilePath: string
|
|
24
|
+
}
|
|
25
|
+
const toAbsolutePath = (options: GenerateApiTypeOptions): GenerateApiTypeOptions => {
|
|
26
|
+
const { tsconfigFilePath, apiFolderPath, outputFilePath } = options
|
|
27
|
+
const absoluteTsconfigFilePath = path.resolve(tsconfigFilePath)
|
|
28
|
+
const absoluteApiFolderPath = path.resolve(apiFolderPath)
|
|
29
|
+
const absoluteOutputFilePath = path.resolve(outputFilePath)
|
|
30
|
+
console.log("tsconfigFilePath:", absoluteTsconfigFilePath)
|
|
31
|
+
console.log("apiFolderPath:", absoluteApiFolderPath)
|
|
32
|
+
console.log("outputFilePath:", absoluteOutputFilePath)
|
|
33
|
+
return {
|
|
34
|
+
...options,
|
|
35
|
+
tsconfigFilePath: absoluteTsconfigFilePath,
|
|
36
|
+
apiFolderPath: absoluteApiFolderPath,
|
|
37
|
+
outputFilePath: absoluteOutputFilePath,
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export const generateApiType = (options: GenerateApiTypeOptions): void => {
|
|
41
|
+
const {
|
|
42
|
+
importFrom,
|
|
43
|
+
tsconfigFilePath,
|
|
44
|
+
apiFolderPath,
|
|
45
|
+
outputFilePath,
|
|
46
|
+
} = toAbsolutePath(options)
|
|
47
|
+
const logger = getGlobalLogger()
|
|
48
|
+
|
|
49
|
+
const tsconfigJson = ts.parseConfigFileTextToJson(tsconfigFilePath, fs.readFileSync(tsconfigFilePath, "utf8"))
|
|
50
|
+
if (tsconfigJson.error) {
|
|
51
|
+
throw new Error("无法解析 tsconfig.json")
|
|
52
|
+
}
|
|
53
|
+
const parsedTsconfig = ts.parseJsonConfigFileContent(tsconfigJson.config, ts.sys, path.resolve(tsconfigFilePath, ".."))
|
|
54
|
+
logger.debug("成功解析 tsconfig 文件...")
|
|
55
|
+
|
|
56
|
+
const projectHost = ts.createCompilerHost(parsedTsconfig.options)
|
|
57
|
+
const project = ts.createProgram(parsedTsconfig.fileNames, parsedTsconfig.options, projectHost)
|
|
58
|
+
logger.debug("成功读取项目...")
|
|
59
|
+
|
|
60
|
+
const allSourceFiles = project.getSourceFiles()
|
|
61
|
+
const apiSourceFiles = allSourceFiles.filter((sourceFile) => {
|
|
62
|
+
// 我们约定接口蓝图必须名为 type.ts
|
|
63
|
+
// oxlint-disable-next-line no-useless-escape unicorn/prefer-string-raw
|
|
64
|
+
return new RegExp(`${apiFolderPath.replaceAll("\\", "\\\\")}.*type\.ts`).test(path.resolve(sourceFile.fileName))
|
|
65
|
+
})
|
|
66
|
+
logger.debug(`找到 ${apiSourceFiles.length} 个接口...`)
|
|
67
|
+
|
|
68
|
+
// 为每一个接口文件生成一个虚拟计算文件
|
|
69
|
+
const apiTypeCalcFileNames = apiSourceFiles.map(sourceFile => sourceFile.fileName.replace("type.ts", `type-calculate-${randomUUID()}.ts`))
|
|
70
|
+
const calcCode = getCalcCode({ importFrom })
|
|
71
|
+
const apiTypeCalcFiles = apiTypeCalcFileNames.map((filename) => {
|
|
72
|
+
return {
|
|
73
|
+
name: filename,
|
|
74
|
+
sourceFile: ts.createSourceFile(filename, calcCode, ts.ScriptTarget.Latest),
|
|
75
|
+
}
|
|
76
|
+
})
|
|
77
|
+
logger.debug("成功生成虚拟计算文件...")
|
|
78
|
+
|
|
79
|
+
const newProject = ts.createProgram({
|
|
80
|
+
rootNames: [...parsedTsconfig.fileNames, ...apiTypeCalcFileNames],
|
|
81
|
+
options: parsedTsconfig.options,
|
|
82
|
+
host: {
|
|
83
|
+
...projectHost,
|
|
84
|
+
getSourceFile: (filename) => {
|
|
85
|
+
const apiTypeCalcSourceFile = apiTypeCalcFiles.find(apiTypeCalcSourceFile => apiTypeCalcSourceFile.name === filename)
|
|
86
|
+
if (apiTypeCalcSourceFile !== undefined) {
|
|
87
|
+
return apiTypeCalcSourceFile.sourceFile
|
|
88
|
+
}
|
|
89
|
+
return project.getSourceFile(filename)
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
oldProgram: project,
|
|
93
|
+
})
|
|
94
|
+
logger.debug("成功生成虚拟项目...")
|
|
95
|
+
|
|
96
|
+
const check = newProject.getTypeChecker()
|
|
97
|
+
logger.debug("成功生成虚拟项目类型检查器...")
|
|
98
|
+
|
|
99
|
+
const result: string[] = []
|
|
100
|
+
apiSourceFiles.forEach((apiSourceFile, index) => {
|
|
101
|
+
logger.info(`处理(${index + 1} / ${apiSourceFiles.length}):${apiSourceFile.fileName}`)
|
|
102
|
+
|
|
103
|
+
const apiCalcSourceFile = apiTypeCalcFiles[index]?.sourceFile
|
|
104
|
+
if (apiCalcSourceFile === undefined) {
|
|
105
|
+
throw new Error("非预期的数组越界")
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
for (const node of apiCalcSourceFile.statements) {
|
|
109
|
+
if (ts.isExportAssignment(node) && node.isExportEquals === undefined) {
|
|
110
|
+
const apiType = check.getTypeAtLocation(node.expression)
|
|
111
|
+
const typeString = check.typeToString(apiType, undefined, ts.TypeFormatFlags.NoTruncation | ts.TypeFormatFlags.AllowUniqueESSymbolType | ts.TypeFormatFlags.UseAliasDefinedOutsideCurrentScope)
|
|
112
|
+
result.push(typeString)
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
})
|
|
116
|
+
logger.debug("成功处理所有接口...")
|
|
117
|
+
|
|
118
|
+
const outputPathAbs = path.resolve(outputFilePath)
|
|
119
|
+
const outDir = path.dirname(outputPathAbs)
|
|
120
|
+
|
|
121
|
+
if (!existsSync(outDir)) {
|
|
122
|
+
mkdirSync(outDir, { recursive: true })
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
fs.writeFileSync(outputPathAbs, `export type ApiType = [${result.join(",")}]\n`)
|
|
126
|
+
logger.debug(`生成成功:${outputPathAbs}`)
|
|
127
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Huawei
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./moderation.ts"
|
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
import { fetch } from "undici"
|
|
2
|
+
import { type } from "arktype"
|
|
3
|
+
|
|
4
|
+
import type { LoggerFriendly, LoggerFriendlyOptions } from "#Source/log/index.ts"
|
|
5
|
+
import { Logger } from "#Source/log/index.ts"
|
|
6
|
+
|
|
7
|
+
type TextEventType =
|
|
8
|
+
| "nickname" // 昵称
|
|
9
|
+
| "title" // 标题
|
|
10
|
+
| "article" // 帖子
|
|
11
|
+
| "comment" // 评论
|
|
12
|
+
| "barrage" // 弹幕
|
|
13
|
+
| "search" // 搜索栏
|
|
14
|
+
| "profile" // 个人简介
|
|
15
|
+
type ImageEventType =
|
|
16
|
+
| "head_image" // 头像
|
|
17
|
+
| "album" // 相册
|
|
18
|
+
| "dynamic" // 动态
|
|
19
|
+
| "article" // 帖子
|
|
20
|
+
| "comment" // 评论
|
|
21
|
+
| "room_cover" // 房间封面
|
|
22
|
+
| "group_message" // 群聊图片
|
|
23
|
+
| "message" // 私聊图片
|
|
24
|
+
| "product" // 商品图片
|
|
25
|
+
type ImageCategories =
|
|
26
|
+
| "terrorism" // 暴恐元素的检测
|
|
27
|
+
| "porn" // 涉黄元素的检测
|
|
28
|
+
| "image_text" // 广告图文的检测
|
|
29
|
+
const SUGGESTION_TYPE = {
|
|
30
|
+
block: "block", // 包含敏感信息,不通过
|
|
31
|
+
pass: "pass", // 不包含敏感信息,通过
|
|
32
|
+
review: "review", // 需要人工复检
|
|
33
|
+
} as const
|
|
34
|
+
type SuggestionType = typeof SUGGESTION_TYPE[keyof typeof SUGGESTION_TYPE]
|
|
35
|
+
const suggestionTypeSchema = type.or("'block'", "'pass'", "'review'")
|
|
36
|
+
|
|
37
|
+
const textModerationResponseSchema = type({
|
|
38
|
+
result: type({
|
|
39
|
+
suggestion: suggestionTypeSchema,
|
|
40
|
+
}),
|
|
41
|
+
})
|
|
42
|
+
export interface ModerationTextOptions {
|
|
43
|
+
input: string
|
|
44
|
+
eventType: TextEventType
|
|
45
|
+
}
|
|
46
|
+
export interface ModerationTextResult {
|
|
47
|
+
suggestion: SuggestionType
|
|
48
|
+
}
|
|
49
|
+
const imageModerationResponseSchema = type({
|
|
50
|
+
result: type({
|
|
51
|
+
suggestion: suggestionTypeSchema,
|
|
52
|
+
}),
|
|
53
|
+
})
|
|
54
|
+
export interface ModerationImageOptions {
|
|
55
|
+
mode: "base64" | "url"
|
|
56
|
+
input: string
|
|
57
|
+
eventType: ImageEventType
|
|
58
|
+
categories: ImageCategories[]
|
|
59
|
+
}
|
|
60
|
+
export interface ModerationImageResult {
|
|
61
|
+
suggestion: SuggestionType
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface ModerationOptions extends LoggerFriendlyOptions {
|
|
65
|
+
userName: string
|
|
66
|
+
userPwd: string
|
|
67
|
+
domainName: string
|
|
68
|
+
projectName: string
|
|
69
|
+
projectId: string
|
|
70
|
+
endpoint: string
|
|
71
|
+
/**
|
|
72
|
+
* token 有效期,单位:毫秒,默认 11 小时
|
|
73
|
+
*/
|
|
74
|
+
tokenValidTime?: number | undefined
|
|
75
|
+
}
|
|
76
|
+
interface ResolvedModerationOptions {
|
|
77
|
+
userName: string
|
|
78
|
+
userPwd: string
|
|
79
|
+
domainName: string
|
|
80
|
+
projectName: string
|
|
81
|
+
projectId: string
|
|
82
|
+
endpoint: string
|
|
83
|
+
tokenValidTime: number
|
|
84
|
+
}
|
|
85
|
+
export class Moderation implements LoggerFriendly {
|
|
86
|
+
readonly options: ResolvedModerationOptions
|
|
87
|
+
|
|
88
|
+
readonly logger: Logger
|
|
89
|
+
|
|
90
|
+
protected tokenGetTime: number | null = null
|
|
91
|
+
protected token: string | null = null
|
|
92
|
+
|
|
93
|
+
constructor(options: ModerationOptions) {
|
|
94
|
+
this.options = {
|
|
95
|
+
...options,
|
|
96
|
+
// token 有效期 12 小时,保险起见,我们超过 11 小时就重写获取一次,以免调用到一半忽然失效
|
|
97
|
+
// 请求新 token 并不会造成旧 token 失效,所以想象中的问题不会发生
|
|
98
|
+
tokenValidTime: options.tokenValidTime ?? (11 * 60 * 60 * 1_000),
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
this.logger = Logger.fromOptions(options).setDefaultName("Moderation")
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* {@link https://support.huaweicloud.com/api-moderation/moderation_03_0003.html}
|
|
106
|
+
* {@link https://support.huaweicloud.com/api-iam/iam_30_0001.html}
|
|
107
|
+
*/
|
|
108
|
+
protected async getNewToken(): Promise<string> {
|
|
109
|
+
const {
|
|
110
|
+
userName,
|
|
111
|
+
userPwd,
|
|
112
|
+
domainName,
|
|
113
|
+
projectName,
|
|
114
|
+
} = this.options
|
|
115
|
+
const logger = this.logger
|
|
116
|
+
|
|
117
|
+
try {
|
|
118
|
+
logger.log("准备获取 token...")
|
|
119
|
+
|
|
120
|
+
const response = await fetch(
|
|
121
|
+
`https://iam.${projectName}.myhuaweicloud.com/v3/auth/tokens`,
|
|
122
|
+
{
|
|
123
|
+
method: "POST",
|
|
124
|
+
headers: { "Content-Type": "application/json" },
|
|
125
|
+
body: JSON.stringify({
|
|
126
|
+
auth: {
|
|
127
|
+
identity: {
|
|
128
|
+
methods: ["password"],
|
|
129
|
+
password: {
|
|
130
|
+
user: {
|
|
131
|
+
name: userName,
|
|
132
|
+
password: userPwd,
|
|
133
|
+
domain: {
|
|
134
|
+
name: domainName,
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
scope: {
|
|
140
|
+
project: {
|
|
141
|
+
name: projectName,
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
}),
|
|
146
|
+
}
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
const responseText = await response.text()
|
|
150
|
+
const responseBody = responseText === ""
|
|
151
|
+
? null
|
|
152
|
+
: (() => {
|
|
153
|
+
try {
|
|
154
|
+
return JSON.parse(responseText) as unknown
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
return responseText
|
|
158
|
+
}
|
|
159
|
+
})()
|
|
160
|
+
const errorMessage = typeof responseBody === "string"
|
|
161
|
+
? responseBody
|
|
162
|
+
: (() => {
|
|
163
|
+
try {
|
|
164
|
+
return JSON.stringify(responseBody)
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
return String(responseBody)
|
|
168
|
+
}
|
|
169
|
+
})()
|
|
170
|
+
|
|
171
|
+
if (response.ok === false) {
|
|
172
|
+
throw new Error(errorMessage)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const token = response.headers.get("x-subject-token")
|
|
176
|
+
if (token === null || token === undefined) {
|
|
177
|
+
throw new Error(errorMessage)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
logger.log("获取 token 成功")
|
|
181
|
+
this.token = token
|
|
182
|
+
this.tokenGetTime = Date.now()
|
|
183
|
+
return token
|
|
184
|
+
} catch (exception) {
|
|
185
|
+
logger.error(`获取 toekn 失败:${String(exception)}`)
|
|
186
|
+
throw exception
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
protected async getToken(): Promise<string> {
|
|
191
|
+
const token = this.token
|
|
192
|
+
const tokenGetTime = this.tokenGetTime
|
|
193
|
+
const { tokenValidTime } = this.options
|
|
194
|
+
|
|
195
|
+
if (
|
|
196
|
+
token === null
|
|
197
|
+
|| tokenGetTime === null
|
|
198
|
+
|| tokenGetTime + tokenValidTime < Date.now()
|
|
199
|
+
) {
|
|
200
|
+
return await this.getNewToken()
|
|
201
|
+
} else {
|
|
202
|
+
return await Promise.resolve(token)
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* @see {@link https://support.huaweicloud.com/api-moderation/moderation_03_0069.html}
|
|
208
|
+
*/
|
|
209
|
+
async text(
|
|
210
|
+
options: ModerationTextOptions
|
|
211
|
+
): Promise<ModerationTextResult> {
|
|
212
|
+
const { input, eventType } = options
|
|
213
|
+
const { endpoint, projectId } = this.options
|
|
214
|
+
const logger = this.logger
|
|
215
|
+
|
|
216
|
+
// empty string is always safe
|
|
217
|
+
if (input === "") {
|
|
218
|
+
return { suggestion: "pass" }
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
try {
|
|
222
|
+
logger.log(`开始调用 text 检查,输入:${input},类型:${eventType}`)
|
|
223
|
+
|
|
224
|
+
const token = await this.getToken()
|
|
225
|
+
const responseRaw = await fetch(
|
|
226
|
+
`https://${endpoint}/v3/${projectId}/moderation/text`,
|
|
227
|
+
{
|
|
228
|
+
method: "POST",
|
|
229
|
+
headers: {
|
|
230
|
+
"Content-Type": "application/json",
|
|
231
|
+
"X-Auth-Token": token,
|
|
232
|
+
},
|
|
233
|
+
body: JSON.stringify({
|
|
234
|
+
event_type: eventType,
|
|
235
|
+
data: {
|
|
236
|
+
language: "zh",
|
|
237
|
+
text: input,
|
|
238
|
+
},
|
|
239
|
+
glossary_names: [],
|
|
240
|
+
white_glossary_names: [],
|
|
241
|
+
categories: ["terrorism", "porn", "ban", "abuse"],
|
|
242
|
+
}),
|
|
243
|
+
}
|
|
244
|
+
)
|
|
245
|
+
const responseText = await responseRaw.text()
|
|
246
|
+
const response = responseText === ""
|
|
247
|
+
? null
|
|
248
|
+
: (() => {
|
|
249
|
+
try {
|
|
250
|
+
return JSON.parse(responseText) as unknown
|
|
251
|
+
}
|
|
252
|
+
catch {
|
|
253
|
+
return responseText
|
|
254
|
+
}
|
|
255
|
+
})()
|
|
256
|
+
|
|
257
|
+
if (responseRaw.ok === false) {
|
|
258
|
+
throw new Error(typeof response === "string" ? response : JSON.stringify(response))
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const validateResult = await textModerationResponseSchema["~standard"].validate(response)
|
|
262
|
+
if (validateResult.issues !== undefined) {
|
|
263
|
+
logger.error(`非预期的返回结果:${JSON.stringify(response)}`)
|
|
264
|
+
throw new Error("非预期的返回结果")
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
logger.log(`调用成功:${JSON.stringify(validateResult)}`)
|
|
268
|
+
return validateResult.value.result
|
|
269
|
+
}
|
|
270
|
+
catch (exception) {
|
|
271
|
+
logger.error(`调用失败:${String(exception)}`)
|
|
272
|
+
throw exception
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* @see {@link https://support.huaweicloud.com/api-moderation/moderation_03_0086.html}
|
|
278
|
+
*/
|
|
279
|
+
async image(
|
|
280
|
+
options: ModerationImageOptions
|
|
281
|
+
): Promise<ModerationImageResult> {
|
|
282
|
+
const { mode, input, eventType, categories } = options
|
|
283
|
+
const { endpoint, projectId } = this.options
|
|
284
|
+
const logger = this.logger
|
|
285
|
+
|
|
286
|
+
try {
|
|
287
|
+
logger.log(`开始调用 image 检查,输入:${input},类型:${eventType}`)
|
|
288
|
+
|
|
289
|
+
const token = await this.getToken()
|
|
290
|
+
|
|
291
|
+
interface argType {
|
|
292
|
+
event_type: ImageEventType
|
|
293
|
+
categories: ImageCategories[]
|
|
294
|
+
image?: string | undefined
|
|
295
|
+
url?: string | undefined
|
|
296
|
+
}
|
|
297
|
+
const arg: argType = {
|
|
298
|
+
event_type: eventType,
|
|
299
|
+
categories,
|
|
300
|
+
image: mode === "base64" ? input : undefined,
|
|
301
|
+
url: mode === "url" ? input : undefined,
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if (mode === "base64") {
|
|
305
|
+
arg.image = input
|
|
306
|
+
}
|
|
307
|
+
else if (mode === "url") {
|
|
308
|
+
arg.url = input
|
|
309
|
+
}
|
|
310
|
+
else {
|
|
311
|
+
throw new Error("意外的 mode")
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const responseRaw = await fetch(
|
|
315
|
+
`https://${endpoint}/v3/${projectId}/moderation/image`,
|
|
316
|
+
{
|
|
317
|
+
method: "POST",
|
|
318
|
+
headers: {
|
|
319
|
+
"Content-Type": "application/json",
|
|
320
|
+
"X-Auth-Token": token,
|
|
321
|
+
},
|
|
322
|
+
body: JSON.stringify(arg),
|
|
323
|
+
}
|
|
324
|
+
)
|
|
325
|
+
const responseText = await responseRaw.text()
|
|
326
|
+
const response = responseText === ""
|
|
327
|
+
? null
|
|
328
|
+
: (() => {
|
|
329
|
+
try {
|
|
330
|
+
return JSON.parse(responseText) as unknown
|
|
331
|
+
}
|
|
332
|
+
catch {
|
|
333
|
+
return responseText
|
|
334
|
+
}
|
|
335
|
+
})()
|
|
336
|
+
|
|
337
|
+
if (responseRaw.ok === false) {
|
|
338
|
+
throw new Error(typeof response === "string" ? response : JSON.stringify(response))
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
const validateResult = await imageModerationResponseSchema["~standard"].validate(response)
|
|
342
|
+
if (validateResult.issues !== undefined) {
|
|
343
|
+
logger.error(`非预期的返回结果:${JSON.stringify(response)}`)
|
|
344
|
+
throw new Error("非预期的返回结果")
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
logger.log(`调用成功:${JSON.stringify(validateResult.value)}`)
|
|
348
|
+
return validateResult.value.result
|
|
349
|
+
}
|
|
350
|
+
catch (exception) {
|
|
351
|
+
logger.error(`调用失败:${String(exception)}`)
|
|
352
|
+
throw exception
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
declare module "esdk-obs-nodejs" {
|
|
2
|
+
import type { Readable } from "node:stream"
|
|
3
|
+
|
|
4
|
+
interface CommonMsg {
|
|
5
|
+
Status: number
|
|
6
|
+
Code: string
|
|
7
|
+
Message: string
|
|
8
|
+
HostId: string
|
|
9
|
+
RequestId: string
|
|
10
|
+
Id2: string
|
|
11
|
+
Indicator: string
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
type ObsResult<InterfaceResult> = Promise<{
|
|
15
|
+
CommonMsg: CommonMsg
|
|
16
|
+
InterfaceResult: InterfaceResult
|
|
17
|
+
}>
|
|
18
|
+
|
|
19
|
+
export interface GetBucketMetadataOptions {
|
|
20
|
+
Bucket: string
|
|
21
|
+
}
|
|
22
|
+
export interface GetBucketMetadataInterfaceResult {
|
|
23
|
+
RequestId: string
|
|
24
|
+
StorageClass: string
|
|
25
|
+
Location: string
|
|
26
|
+
ObsVersion: string
|
|
27
|
+
AllowOrigin: string
|
|
28
|
+
AllowHeader: string
|
|
29
|
+
AllowMethod: string
|
|
30
|
+
ExposeHeader: string
|
|
31
|
+
MaxAgeSeconds: string
|
|
32
|
+
}
|
|
33
|
+
export type GetBucketMetadataResult = ObsResult<GetBucketMetadataInterfaceResult>
|
|
34
|
+
|
|
35
|
+
export interface PutObjectOptions {
|
|
36
|
+
Bucket: string
|
|
37
|
+
Key: string
|
|
38
|
+
Body: string | Readable
|
|
39
|
+
ContentType?: string | undefined
|
|
40
|
+
ProgressCallback?: ((count: number, objectTotalSize: number, time: number) => void) | undefined
|
|
41
|
+
}
|
|
42
|
+
export interface PutObjectInterfaceResult {
|
|
43
|
+
RequestId: string
|
|
44
|
+
ETag: string
|
|
45
|
+
VersionId: string
|
|
46
|
+
StorageClass: string
|
|
47
|
+
SseKms: string
|
|
48
|
+
SseKmsKey: string
|
|
49
|
+
SseC: string
|
|
50
|
+
SseCKeyMd5: string
|
|
51
|
+
}
|
|
52
|
+
export type PutObjectResult = ObsResult<PutObjectInterfaceResult>
|
|
53
|
+
|
|
54
|
+
export type DeleteObjectOptions = {
|
|
55
|
+
Bucket: string
|
|
56
|
+
Key: string
|
|
57
|
+
VersionId?: string | undefined
|
|
58
|
+
ProgressCallback?: ((count: number, objectTotalSize: number, time: number) => void) | undefined
|
|
59
|
+
}
|
|
60
|
+
export type DeleteObjectInterfaceResult = {
|
|
61
|
+
RequestId: string
|
|
62
|
+
DeleteMarker: string
|
|
63
|
+
VersionId: string
|
|
64
|
+
}
|
|
65
|
+
export type DeleteObjectResult = ObsResult<DeleteObjectInterfaceResult>
|
|
66
|
+
|
|
67
|
+
export interface ObsClientOptions {
|
|
68
|
+
access_key_id: string
|
|
69
|
+
secret_access_key: string
|
|
70
|
+
server: string
|
|
71
|
+
}
|
|
72
|
+
export class ObsClient {
|
|
73
|
+
constructor(options: ObsClientOptions)
|
|
74
|
+
|
|
75
|
+
GetBucketMetadata: (
|
|
76
|
+
options: GetBucketMetadataOptions
|
|
77
|
+
) => GetBucketMetadataResult
|
|
78
|
+
|
|
79
|
+
putObject: (
|
|
80
|
+
options: PutObjectOptions
|
|
81
|
+
) => PutObjectResult
|
|
82
|
+
|
|
83
|
+
deleteObject: (
|
|
84
|
+
options: DeleteObjectOptions
|
|
85
|
+
) => DeleteObjectResult
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./obs.ts"
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { DeleteObjectOptions, DeleteObjectResult, PutObjectOptions, PutObjectResult } from "esdk-obs-nodejs"
|
|
2
|
+
import { ObsClient } from "esdk-obs-nodejs"
|
|
3
|
+
|
|
4
|
+
export interface ObsOptions {
|
|
5
|
+
accessKeyId: string
|
|
6
|
+
secretAccessKey: string
|
|
7
|
+
server: string
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* @see {@link https://www.huaweicloud.com/product/obs.html | 华为云对象存储服务}
|
|
11
|
+
*/
|
|
12
|
+
export class Obs {
|
|
13
|
+
protected obsClient: ObsClient
|
|
14
|
+
|
|
15
|
+
constructor(options: ObsOptions) {
|
|
16
|
+
this.obsClient = new ObsClient({
|
|
17
|
+
access_key_id: options.accessKeyId,
|
|
18
|
+
secret_access_key: options.secretAccessKey,
|
|
19
|
+
server: options.server
|
|
20
|
+
})
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async putObject(
|
|
24
|
+
options: PutObjectOptions
|
|
25
|
+
): Promise<PutObjectResult> {
|
|
26
|
+
const result = await this.obsClient.putObject(options)
|
|
27
|
+
if (result.CommonMsg.Status >= 300) {
|
|
28
|
+
throw new Error(result.CommonMsg.Message)
|
|
29
|
+
}
|
|
30
|
+
return result
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async deleteObject(
|
|
34
|
+
options: DeleteObjectOptions
|
|
35
|
+
): Promise<DeleteObjectResult> {
|
|
36
|
+
const result = await this.obsClient.deleteObject(options)
|
|
37
|
+
if (result.CommonMsg.Status >= 300) {
|
|
38
|
+
throw new Error(result.CommonMsg.Message)
|
|
39
|
+
}
|
|
40
|
+
return result
|
|
41
|
+
}
|
|
42
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,20 +1,39 @@
|
|
|
1
1
|
export * as Basic from "./basic/index.ts"
|
|
2
2
|
export type * as Type from "./type/index.ts"
|
|
3
|
+
|
|
3
4
|
export * as Environment from "./environment/index.ts"
|
|
4
5
|
export * as Exception from "./exception/index.ts"
|
|
5
6
|
export * as Event from "./event/index.ts"
|
|
6
|
-
|
|
7
7
|
export * as Singleton from "./singleton/index.ts"
|
|
8
|
-
|
|
8
|
+
|
|
9
9
|
export * as Abort from "./abort/index.ts"
|
|
10
|
+
export * as Log from "./log/index.ts"
|
|
10
11
|
export * as Reactor from "./reactor/index.ts"
|
|
12
|
+
export * as Result from "./result/index.ts"
|
|
13
|
+
export * as Form from "./form/index.ts"
|
|
14
|
+
export * as Request from "./request/index.ts"
|
|
15
|
+
export * as Route from "./route/index.ts"
|
|
11
16
|
export * as Timer from "./timer/index.ts"
|
|
17
|
+
export * as Cron from "./cron/index.ts"
|
|
12
18
|
export * as Orchestration from "./orchestration/index.ts"
|
|
19
|
+
export * as Service from "./service/index.ts"
|
|
20
|
+
export * as Socket from "./socket/index.ts"
|
|
21
|
+
export * as Http from "./http/index.ts"
|
|
13
22
|
|
|
14
23
|
export * as Identifier from "./identifier/index.ts"
|
|
24
|
+
export * as Credential from "./credential/index.ts"
|
|
15
25
|
export * as Encoding from "./encoding/index.ts"
|
|
16
26
|
export * as Random from "./random/index.ts"
|
|
17
27
|
export * as Color from "./color/index.ts"
|
|
28
|
+
export * as Json from "./json/index.ts"
|
|
29
|
+
export * as Aio from "./aio/index.ts"
|
|
18
30
|
|
|
19
31
|
export * as Web from "./web/index.ts"
|
|
20
32
|
export * as CSS from "./css/index.ts"
|
|
33
|
+
export * as Drizzle from "./drizzle/index.ts"
|
|
34
|
+
export * as Email from "./email/index.ts"
|
|
35
|
+
export * as Huawei from "./huawei/index.ts"
|
|
36
|
+
export * as Openai from "./openai/index.ts"
|
|
37
|
+
export * as Weixin from "./weixin/index.ts"
|
|
38
|
+
export * as Ai from "./ai/index.ts"
|
|
39
|
+
export * as FileSystem from "./file-system/index.ts"
|