@seayoo-web/captcha 1.0.0 → 2.0.0

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/README.md CHANGED
@@ -1 +1,52 @@
1
- # Captcha
1
+ # @seayoo-web/captcha
2
+
3
+ 腾讯天御验证码(Captcha)前端封装,对接 [腾讯云验证码](https://cloud.tencent.com/document/product/1110/36841)。
4
+
5
+ 负责动态加载腾讯 `TJCaptcha.js`、弹出验证码、并把结果归一化返回。**不包含**「向自家服务端拉取验证码开关 / 配置」这类业务编排,`captchaAppId` 等由调用方(通常来自服务端下发)传入。
6
+
7
+ ## 用法
8
+
9
+ ```ts
10
+ import { TencentCaptchaAgent } from "@seayoo-web/captcha";
11
+
12
+ // 可选:首屏渲染完成后提前预热 SDK,减少首次弹窗延迟
13
+ TencentCaptchaAgent.preloadSDK();
14
+
15
+ const agent = new TencentCaptchaAgent({
16
+ captchaAppId, // 验证码应用 id,通常由服务端下发
17
+ // aidEncrypted、userLanguage、needFeedBack、enableDarkMode 可选
18
+ });
19
+
20
+ const result = await agent.verify();
21
+ if (result.code === 0) {
22
+ // 验证通过,用 result.ticket / result.randstr 交服务端二次校验
23
+ } else if (result.code === 2) {
24
+ // 用户主动关闭
25
+ } else {
26
+ // 验证失败或容灾(code === 1001,ticket 为 trerror_ 前缀容灾票据)
27
+ }
28
+ ```
29
+
30
+ ## API
31
+
32
+ ### `new TencentCaptchaAgent(option)`
33
+
34
+ - `option.captchaAppId` — 必填,验证码应用 id。
35
+ - 其余字段(`aidEncrypted` / `userLanguage` / `needFeedBack` / `enableDarkMode`)透传给腾讯 SDK。
36
+
37
+ ### `agent.verify(): Promise<CaptchaResult>`
38
+
39
+ 触发一次人机验证。**不会抛错**:SDK 加载失败、用户关闭、超时(3 分钟)都会以 `CaptchaResult` 返回,其中加载 / 运行异常返回 `trerror_` 前缀的容灾票据(`code === 1001`),交由服务端识别降级。
40
+
41
+ `CaptchaResult`:
42
+
43
+ | 字段 | 说明 |
44
+ | --------- | ---------------------------------------------------------- |
45
+ | `code` | `0` 通过 / `2` 用户关闭 / `1001` 容灾 / 其它 失败 |
46
+ | `ticket` | 验证票据(`code === 0` 时有效) |
47
+ | `randstr` | 本次验证随机串 |
48
+ | `message` | 错误信息(正常时为空串) |
49
+
50
+ ### `TencentCaptchaAgent.preloadSDK()`
51
+
52
+ 提前加载腾讯 js sdk。建议在首屏渲染完成后调用——过早加载会在 Safari 上因样式未就绪而闪现一个很大的图标。
package/dist/index.js CHANGED
@@ -1,59 +1,59 @@
1
- var d = Object.defineProperty;
2
- var h = (a, t, e) => t in a ? d(a, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : a[t] = e;
3
- var n = (a, t, e) => h(a, typeof t != "symbol" ? t + "" : t, e);
4
- import { useConsole as p, EventBus as w, usePromise as l, loadJS as u } from "@seayoo-web/utils";
5
- const y = p("CaptchaSDK");
6
- let s;
7
- async function i() {
8
- return "TencentCaptcha" in window && window.TencentCaptcha ? window.TencentCaptcha : s ? await s : (s = u("https://turing.captcha.qcloud.com/TJCaptcha.js").then(() => window.TencentCaptcha ?? null), await s);
1
+ import { loadJS as e, useConsole as t } from "@seayoo-web/utils";
2
+ //#region src/tencent.ts
3
+ var n = t("CaptchaSDK"), r = "https://turing.captcha.qcloud.com/TJCaptcha.js", i = 6e4 * 3, a = null;
4
+ function o() {
5
+ return typeof document > "u" || typeof window > "u" ? Promise.resolve(null) : window.TencentCaptcha ? Promise.resolve(window.TencentCaptcha) : (a ||= e(r).then((e) => {
6
+ let t = e ? window.TencentCaptcha ?? null : null;
7
+ return t || (a = null), t;
8
+ }), a);
9
9
  }
10
- class m {
11
- /**
12
- * 创建验证码实例,创建后立即开始加载腾讯天御 SDK
13
- */
14
- constructor(t) {
15
- n(this, "sdk", null);
16
- n(this, "isReady", !1);
17
- n(this, "event", new w());
18
- i().then((e) => {
19
- if (!e)
20
- y.error("Load TencentCaptcha Error!");
21
- else {
22
- const { captchaAppId: r, ...c } = t;
23
- this.sdk = new e(
24
- r,
25
- (o) => {
26
- this.event.emit("done", o);
27
- },
28
- {
29
- ...c,
30
- ready: (o) => {
31
- this.isReady = !0, this.event.emit("ready", o);
32
- }
33
- }
34
- );
35
- }
36
- });
37
- }
38
- /** 提前加载腾讯 js sdk */
39
- static preloadSDK() {
40
- i();
41
- }
42
- async ready() {
43
- if (this.isReady)
44
- return;
45
- const { promise: t, resolve: e } = l();
46
- this.event.once("ready", e), await t;
47
- }
48
- async show() {
49
- var t;
50
- await this.ready(), (t = this.sdk) == null || t.show();
51
- }
52
- async destroy() {
53
- var t;
54
- await this.ready(), (t = this.sdk) == null || t.destroy();
55
- }
10
+ function s(e, t) {
11
+ return {
12
+ code: 1001,
13
+ ticket: `trerror_1001_${e}_${Math.floor(Date.now() / 1e3)}`,
14
+ randstr: `@${Math.random().toString(36).slice(2)}`,
15
+ message: t
16
+ };
56
17
  }
57
- export {
58
- m as TencentCaptchaAgent
18
+ var c = class {
19
+ static preloadSDK() {
20
+ o();
21
+ }
22
+ constructor(e) {
23
+ this.option = e;
24
+ }
25
+ async verify() {
26
+ let { captchaAppId: e, ...t } = this.option, r = await o();
27
+ if (!r) return n.error("Load TencentCaptcha Error!"), s(e, "Captcha init Error");
28
+ try {
29
+ return await new Promise((n, a) => {
30
+ let o = new r(e, (e) => {
31
+ s(), n({
32
+ code: e.errorCode || e.ret,
33
+ ticket: e.ticket,
34
+ randstr: e.randstr,
35
+ message: e.errorMessage ?? ""
36
+ });
37
+ }, t);
38
+ function s() {
39
+ window.removeEventListener("resize", c), clearTimeout(l);
40
+ }
41
+ function c() {
42
+ let e = document.getElementById("tcaptcha_transform_dy");
43
+ if (!e) return;
44
+ let t = e.style.opacity;
45
+ t === "1" ? o.show() : t === "0" && (o.destroy(), s(), a(/* @__PURE__ */ Error("Captcha is hidden")));
46
+ }
47
+ window.addEventListener("resize", c);
48
+ let l = setTimeout(() => {
49
+ o.destroy(), s(), a(/* @__PURE__ */ Error("Captcha Timeout"));
50
+ }, i);
51
+ o.show();
52
+ });
53
+ } catch (t) {
54
+ return s(e, t instanceof Error ? t.message : `Captcha Error: ${String(t)}`);
55
+ }
56
+ }
59
57
  };
58
+ //#endregion
59
+ export { c as TencentCaptchaAgent };
package/package.json CHANGED
@@ -1,41 +1,47 @@
1
1
  {
2
2
  "name": "@seayoo-web/captcha",
3
+ "version": "2.0.0",
3
4
  "description": "captcha agent",
4
- "version": "1.0.0",
5
- "type": "module",
6
- "main": "./dist/index.js",
7
- "module": "./dist/index.js",
5
+ "keywords": [
6
+ "captcha"
7
+ ],
8
+ "license": "MIT",
9
+ "author": "web@seayoo.com",
8
10
  "source": "./index.ts",
9
- "types": "./types/index.d.ts",
10
11
  "files": [
11
12
  "dist",
12
13
  "types",
13
14
  "README.md"
14
15
  ],
16
+ "type": "module",
17
+ "sideEffects": false,
18
+ "main": "./dist/index.js",
19
+ "module": "./dist/index.js",
20
+ "types": "./types/index.d.ts",
15
21
  "publishConfig": {
16
22
  "access": "public"
17
23
  },
18
- "engines": {
19
- "node": ">=18"
20
- },
21
- "keywords": [
22
- "captcha"
23
- ],
24
- "author": "web@seayoo.com",
25
- "license": "MIT",
26
24
  "devDependencies": {
27
- "@seayoo-web/scripts": "^1.0.6",
28
- "@seayoo-web/tsconfig": "^1.0.2",
29
- "@seayoo-web/utils": "^2.2.0"
25
+ "@vitest/coverage-istanbul": "^4.1.4",
26
+ "happy-dom": "^13.10.1",
27
+ "vitest": "^4.1.4",
28
+ "@seayoo-web/scripts": "^4.3.10",
29
+ "@seayoo-web/utils": "^4.5.4",
30
+ "@seayoo-web/tsconfig": "^1.0.6"
30
31
  },
31
32
  "peerDependencies": {
32
- "@seayoo-web/utils": "^2.2.0"
33
+ "@seayoo-web/utils": "^4.5.4"
34
+ },
35
+ "engines": {
36
+ "node": ">=22"
33
37
  },
34
38
  "scripts": {
35
- "build": "vite build && tsc --emitDeclarationOnly",
39
+ "prebuild": "pnpm -F utils build",
40
+ "build": "vite build && tsc --build --emitDeclarationOnly",
36
41
  "type-check": "tsc --noEmit",
37
- "lint": "eslint ./src/**/*.ts",
38
- "lint:fix": "eslint ./src/**/*.{ts,js} --fix",
39
- "prepublish": "pnpm lint:fix && pnpm build"
42
+ "test": "vitest --dom",
43
+ "coverage": "vitest run --coverage",
44
+ "lint": "oxlint -c ../../oxlint.config.mjs",
45
+ "lint:fix": "oxlint -c ../../oxlint.config.mjs --fix"
40
46
  }
41
47
  }
@@ -1,18 +1,18 @@
1
- /** 对腾讯的sdk做简单声明 */
1
+ /** spell-checker:ignore trerror_, randstr, tcaptcha */
2
+ /** 对腾讯的 sdk 做简单声明 */
2
3
  declare global {
3
4
  interface Window {
4
5
  TencentCaptcha?: TencentCaptchaSDK;
5
6
  }
6
7
  }
7
- interface VerifyResult {
8
+ /** 腾讯 sdk 回调返回的原始结构 */
9
+ interface TencentVerifyResponse {
8
10
  /**
9
- * 验证结果。等于 2 时表示用户关闭,不等于 0 表示失败
11
+ * 验证结果。0 = 成功;2 = 用户主动关闭;其它 = 失败
10
12
  */
11
13
  ret: number;
12
14
  /**
13
- * 验证票据,验证成功的票据,当且仅当 ret = 0 ticket 有值
14
- *
15
- * 请求验证码发生错误,验证码自动返回 trerror_ 前缀的容灾票据
15
+ * 验证票据,ret = 0 时有值;客户端发生异常时返回 trerror_ 前缀的容灾票据
16
16
  */
17
17
  ticket: string;
18
18
  /**
@@ -20,108 +20,99 @@ interface VerifyResult {
20
20
  */
21
21
  randstr: string;
22
22
  /**
23
- * 自定义透传参数
24
- */
25
- bizState?: unknown;
26
- /**
27
- * 验证码校验接口耗时(ms)
23
+ * 客户端异常错误码,出现时仍可能返回可用票据
28
24
  */
29
- verifyDuration: number;
25
+ errorCode?: number;
30
26
  /**
31
- * 操作校验成功耗时(用户动作+校验完成)(ms)
27
+ * 客户端异常错误信息
32
28
  */
33
- actionDuration: number;
34
- /**
35
- * 链路 sid
36
- */
37
- sid: string;
38
- }
39
- interface TencentCaptchaSDK {
40
- new (captchaAppId: string, callback: (result: VerifyResult) => void, option?: Omit<TencentCaptchaOption, "captchaAppId"> & {
41
- showFn?: () => void;
42
- }): TencentCaptchaSDKIns;
43
- }
44
- interface TencentCaptchaSDKIns {
45
- /** 显示验证码,可以反复调用 */
46
- show(): void;
47
- /** 隐藏验证码,可以反复调用 */
48
- destroy(): void;
49
- /** 获取验证成功后的 ticket */
50
- getTicket(): {
51
- CaptchaAppId: string;
52
- ticket: string;
53
- };
29
+ errorMessage?: string;
54
30
  }
31
+ /** 透传给腾讯 sdk 的配置项 */
55
32
  interface TencentCaptchaOption {
56
- captchaAppId: string;
57
- /**
58
- * 验证码相关资源加载完成的回调,回调参数为验证码实际的宽高(单位:px)
59
- *
60
- * 该参数仅为查看验证码宽高使用
61
- */
62
- ready?: (info: {
63
- sdkView: {
64
- width: number;
65
- height: number;
66
- };
67
- }) => void;
68
33
  /**
69
- * 开启自适应深夜模式或强制深夜模式
70
- *
71
- * - true 开启自适应深夜模式
72
- * - "force" 强制深夜模式
34
+ * 指定验证码提示文案的语言,优先级高于控制台配置。大小写不敏感。
73
35
  */
74
- enableDarkMode?: boolean | "force";
36
+ userLanguage?: string;
75
37
  /**
76
- * 仅支持移动端原生 webview 调用时传入,用来设置验证码 loading 加载弹窗的大小
77
- *
78
- * 💡注意,此参数调整的并非验证码弹窗大小
38
+ * CaptchaAppId 加密校验串,详见 [CaptchaAppid 加密校验能力接入指引](https://cloud.tencent.com/document/product/1110/36841#c6a3e517-4962-4932-9bf8-9fb10fd03166)
79
39
  */
80
- sdkOpts?: {
81
- width: number;
82
- height: number;
83
- };
40
+ aidEncrypted?: string;
84
41
  /**
85
42
  * 隐藏帮助按钮或自定义帮助按钮链接
86
43
  */
87
44
  needFeedBack?: boolean | `https://${string}`;
88
45
  /**
89
- * 是否在验证码加载过程中显示 loading 框。默认 true
46
+ * 开启自适应深夜模式或强制深夜模式
90
47
  *
91
- * 💡 如果隐藏 loading,则对应的 loading 半透蒙层也会一并隐藏
92
- */
93
- loading?: boolean;
94
- /**
95
- * 指定验证码提示文案的语言,优先级高于控制台配置。大小写不敏感。
48
+ * - true 开启自适应深夜模式
49
+ * - "force" 强制深夜模式
96
50
  */
97
- userLanguage?: string;
51
+ enableDarkMode?: boolean | "force";
52
+ }
53
+ interface TencentCaptchaSDK {
54
+ new (captchaAppId: string, callback: (res: TencentVerifyResponse) => void, option?: TencentCaptchaOption): TencentCaptchaSDKIns;
55
+ }
56
+ interface TencentCaptchaSDKIns {
57
+ /** 显示验证码,可反复调用 */
58
+ show(): void;
59
+ /** 重新加载验证码 */
60
+ reload(): void;
61
+ /** 销毁验证码,可反复调用 */
62
+ destroy(): void;
63
+ }
64
+ /** 归一化后的验证结果 */
65
+ export interface CaptchaResult {
98
66
  /**
99
- * 验证码展示方式,默认 "popup"
67
+ * 结果码
100
68
  *
101
- * - popup 弹出式,以浮层形式居中弹出展示验证码
102
- * - embed 嵌入式,以嵌入指定容器元素中的方式展示验证码
103
- */
104
- type?: "popup" | "embed";
105
- /**
106
- * CaptchaAppId 加密校验串,详见 [CaptchaAppid 加密校验能力接入指引](https://cloud.tencent.com/document/product/1110/36841#c6a3e517-4962-4932-9bf8-9fb10fd03166)
69
+ * - 0 验证通过,ticket 有效
70
+ * - 2 用户主动关闭验证码
71
+ * - 1001 容灾(加载失败 / 超时 / 异常关闭),ticket 为 trerror_ 前缀票据
72
+ * - 其它 验证失败
107
73
  */
108
- aidEncrypted?: string;
74
+ code: number;
75
+ ticket: string;
76
+ randstr: string;
77
+ message: string;
78
+ }
79
+ /** {@link TencentCaptchaAgent} 构造参数 */
80
+ export interface TencentCaptchaAgentOption extends TencentCaptchaOption {
81
+ /** 验证码应用 id,通常由服务端下发 */
82
+ captchaAppId: string;
109
83
  }
110
84
  /**
111
85
  * 腾讯天御验证码
86
+ *
87
+ * https://cloud.tencent.com/document/product/1110/36841
88
+ *
89
+ * @example
90
+ * ```ts
91
+ * // 建议在首屏渲染后提前预热,减少首次弹窗延迟
92
+ * TencentCaptchaAgent.preloadSDK();
93
+ *
94
+ * const agent = new TencentCaptchaAgent({ captchaAppId });
95
+ * const result = await agent.verify();
96
+ * if (result.code === 0) {
97
+ * // 用 result.ticket / result.randstr 去服务端校验
98
+ * }
99
+ * ```
112
100
  */
113
101
  export declare class TencentCaptchaAgent {
114
- private sdk;
115
- private isReady;
116
- private event;
117
- /** 提前加载腾讯 js sdk */
102
+ private readonly option;
103
+ /**
104
+ * 提前加载腾讯 js sdk。
105
+ *
106
+ * 建议在首屏渲染完成后调用:过早加载会在 Safari 上因样式未就绪而闪现一个很大的图标。
107
+ */
118
108
  static preloadSDK(): void;
109
+ constructor(option: TencentCaptchaAgentOption);
119
110
  /**
120
- * 创建验证码实例,创建后立即开始加载腾讯天御 SDK
111
+ * 触发一次人机验证,返回归一化结果。
112
+ *
113
+ * 该方法**不会抛错**:SDK 加载失败、用户关闭、超时等都会以 {@link CaptchaResult} 返回,
114
+ * 其中加载/运行异常返回 `trerror_` 前缀的容灾票据(`code === 1001`),交由服务端降级。
121
115
  */
122
- constructor(option: Omit<TencentCaptchaOption, "ready">);
123
- ready(): Promise<void>;
124
- show(): Promise<void>;
125
- destroy(): Promise<void>;
116
+ verify(): Promise<CaptchaResult>;
126
117
  }
127
118
  export {};