@zhin.js/plugin-short-url 1.0.1 → 1.1.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/README.md ADDED
@@ -0,0 +1,28 @@
1
+ # @zhin.js/plugin-short-url
2
+
3
+ Zhin.js plugin for short-url functionality.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pnpm add @zhin.js/plugin-short-url
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ Add the plugin to your `zhin.config.yml`:
14
+
15
+ ```yaml
16
+ plugins:
17
+ - @zhin.js/plugin-short-url
18
+ ```
19
+
20
+ Or via CLI:
21
+
22
+ ```bash
23
+ zhin install @zhin.js/plugin-short-url
24
+ ```
25
+
26
+ ## Configuration
27
+
28
+ Configure in `zhin.config.yml` under the plugin's key. Refer to the plugin source for available options.
@@ -0,0 +1,19 @@
1
+ // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
+ import { defineCommand } from 'zhin.js/command';
3
+ import { expandUrl, isValidUrl } from "../../../lib/short-url-lib.js";
4
+ export default defineCommand({
5
+ description: '展开一个短链接,显示原始地址',
6
+ params: { url: { type: 'string' } },
7
+ async execute({ params }) {
8
+ const url = String(params.url ?? '');
9
+ if (!isValidUrl(url))
10
+ return '请提供有效的 HTTP/HTTPS 链接';
11
+ try {
12
+ const original = await expandUrl(url);
13
+ return `原始链接: ${original}`;
14
+ }
15
+ catch (e) {
16
+ return `展开失败: ${e instanceof Error ? e.message : String(e)}`;
17
+ }
18
+ },
19
+ });
@@ -0,0 +1,17 @@
1
+ import { defineCommand } from 'zhin.js/command';
2
+ import { expandUrl, isValidUrl } from '../../../src/short-url-lib.js';
3
+
4
+ export default defineCommand({
5
+ description: '展开一个短链接,显示原始地址',
6
+ params: { url: { type: 'string' } },
7
+ async execute({ params }) {
8
+ const url = String(params.url ?? '');
9
+ if (!isValidUrl(url)) return '请提供有效的 HTTP/HTTPS 链接';
10
+ try {
11
+ const original = await expandUrl(url);
12
+ return `原始链接: ${original}`;
13
+ } catch (e: unknown) {
14
+ return `展开失败: ${e instanceof Error ? e.message : String(e)}`;
15
+ }
16
+ },
17
+ });
@@ -0,0 +1,19 @@
1
+ // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
+ import { defineCommand } from 'zhin.js/command';
3
+ import { isValidUrl, shortenUrl } from "../../../lib/short-url-lib.js";
4
+ export default defineCommand({
5
+ description: '缩短一个 URL',
6
+ params: { url: { type: 'string' } },
7
+ async execute({ params }) {
8
+ const url = String(params.url ?? '');
9
+ if (!isValidUrl(url))
10
+ return '请提供有效的 HTTP/HTTPS 链接';
11
+ try {
12
+ const short = await shortenUrl(url);
13
+ return `短链接: ${short}`;
14
+ }
15
+ catch (e) {
16
+ return `缩短失败: ${e instanceof Error ? e.message : String(e)}`;
17
+ }
18
+ },
19
+ });
@@ -0,0 +1,17 @@
1
+ import { defineCommand } from 'zhin.js/command';
2
+ import { isValidUrl, shortenUrl } from '../../../src/short-url-lib.js';
3
+
4
+ export default defineCommand({
5
+ description: '缩短一个 URL',
6
+ params: { url: { type: 'string' } },
7
+ async execute({ params }) {
8
+ const url = String(params.url ?? '');
9
+ if (!isValidUrl(url)) return '请提供有效的 HTTP/HTTPS 链接';
10
+ try {
11
+ const short = await shortenUrl(url);
12
+ return `短链接: ${short}`;
13
+ } catch (e: unknown) {
14
+ return `缩短失败: ${e instanceof Error ? e.message : String(e)}`;
15
+ }
16
+ },
17
+ });
package/lib/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export {};
1
+ export { expandUrl, isValidUrl, shortenUrl } from './short-url-lib.js';
2
2
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC"}
package/lib/index.js CHANGED
@@ -1,80 +1,2 @@
1
- import { formatCompact, MessageCommand, usePlugin, ZhinTool } from 'zhin.js';
2
- const { addCommand, addTool, logger } = usePlugin();
3
- // ── helpers ──────────────────────────────────────────────
4
- function isValidUrl(input) {
5
- try {
6
- const u = new URL(input);
7
- return u.protocol === 'http:' || u.protocol === 'https:';
8
- }
9
- catch {
10
- return false;
11
- }
12
- }
13
- async function shortenUrl(url) {
14
- const api = `https://is.gd/create.php?format=json&url=${encodeURIComponent(url)}`;
15
- const res = await fetch(api);
16
- if (!res.ok)
17
- throw new Error(`is.gd 返回 ${res.status}`);
18
- const data = (await res.json());
19
- if (data.errorcode)
20
- throw new Error(data.errormessage ?? '缩短失败');
21
- return data.shorturl;
22
- }
23
- async function expandUrl(url) {
24
- const res = await fetch(url, { redirect: 'manual' });
25
- const location = res.headers.get('location');
26
- if (location)
27
- return location;
28
- if (res.ok)
29
- return url;
30
- throw new Error(`无法展开链接 (${res.status})`);
31
- }
32
- // ── commands ─────────────────────────────────────────────
33
- addCommand(new MessageCommand('短链 <url:text>')
34
- .desc('缩短一个 URL')
35
- .action(async (_message, result) => {
36
- const url = result.params.url;
37
- if (!isValidUrl(url))
38
- return '请提供有效的 HTTP/HTTPS 链接';
39
- try {
40
- const short = await shortenUrl(url);
41
- return `短链接: ${short}`;
42
- }
43
- catch (e) {
44
- logger.warn(formatCompact({ op: 'shorten', ok: false, error: e instanceof Error ? e.message : String(e) }));
45
- return `缩短失败: ${e instanceof Error ? e.message : String(e)}`;
46
- }
47
- }));
48
- addCommand(new MessageCommand('展开 <url:text>')
49
- .desc('展开一个短链接,显示原始地址')
50
- .action(async (_message, result) => {
51
- const url = result.params.url;
52
- if (!isValidUrl(url))
53
- return '请提供有效的 HTTP/HTTPS 链接';
54
- try {
55
- const original = await expandUrl(url);
56
- return `原始链接: ${original}`;
57
- }
58
- catch (e) {
59
- logger.warn(formatCompact({ op: 'expand', ok: false, error: e instanceof Error ? e.message : String(e) }));
60
- return `展开失败: ${e instanceof Error ? e.message : String(e)}`;
61
- }
62
- }));
63
- // ── AI tool ──────────────────────────────────────────────
64
- addTool(new ZhinTool('short_url')
65
- .desc('缩短一个 URL,返回短链接')
66
- .keyword('短链', '缩短', 'shorten', 'short url')
67
- .param('url', { type: 'string', description: '要缩短的完整 URL' }, true)
68
- .execute(async (args) => {
69
- const url = args.url;
70
- if (!isValidUrl(url))
71
- return '无效的 URL,需要 http:// 或 https:// 开头';
72
- try {
73
- return await shortenUrl(url);
74
- }
75
- catch (e) {
76
- return `缩短失败: ${e instanceof Error ? e.message : String(e)}`;
77
- }
78
- })
79
- .toTool());
1
+ export { expandUrl, isValidUrl, shortenUrl } from './short-url-lib.js';
80
2
  //# sourceMappingURL=index.js.map
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,4 @@
1
+ export declare function isValidUrl(input: string): boolean;
2
+ export declare function shortenUrl(url: string): Promise<string>;
3
+ export declare function expandUrl(url: string): Promise<string>;
4
+ //# sourceMappingURL=short-url-lib.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"short-url-lib.d.ts","sourceRoot":"","sources":["../src/short-url-lib.ts"],"names":[],"mappings":"AAAA,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAOjD;AAED,wBAAsB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAO7D;AAED,wBAAsB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAO5D"}
@@ -0,0 +1,30 @@
1
+ export function isValidUrl(input) {
2
+ try {
3
+ const u = new URL(input);
4
+ return u.protocol === 'http:' || u.protocol === 'https:';
5
+ }
6
+ catch {
7
+ return false;
8
+ }
9
+ }
10
+ export async function shortenUrl(url) {
11
+ const api = `https://is.gd/create.php?format=json&url=${encodeURIComponent(url)}`;
12
+ const res = await fetch(api);
13
+ if (!res.ok)
14
+ throw new Error(`is.gd 返回 ${res.status}`);
15
+ const data = (await res.json());
16
+ if (!data.shorturl)
17
+ throw new Error(data.errormessage ?? '缩短失败');
18
+ return data.shorturl;
19
+ }
20
+ export async function expandUrl(url) {
21
+ // 不能用 redirect:'manual':undici 下返回 opaqueredirect(status 0、headers 空),
22
+ // 拿不到 location。改为跟随重定向后比较最终 URL。
23
+ const res = await fetch(url, { redirect: 'follow' });
24
+ if (res.url && res.url !== url)
25
+ return res.url;
26
+ if (res.ok)
27
+ return url;
28
+ throw new Error(`无法展开链接 (${res.status})`);
29
+ }
30
+ //# sourceMappingURL=short-url-lib.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"short-url-lib.js","sourceRoot":"","sources":["../src/short-url-lib.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,UAAU,CAAC,KAAa;IACtC,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC;QACzB,OAAO,CAAC,CAAC,QAAQ,KAAK,OAAO,IAAI,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC;IAC3D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,GAAW;IAC1C,MAAM,GAAG,GAAG,4CAA4C,kBAAkB,CAAC,GAAG,CAAC,EAAE,CAAC;IAClF,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC;IAC7B,IAAI,CAAC,GAAG,CAAC,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,YAAY,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;IACvD,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAqE,CAAC;IACpG,IAAI,CAAC,IAAI,CAAC,QAAQ;QAAE,MAAM,IAAI,KAAK,CAAC,IAAI,CAAC,YAAY,IAAI,MAAM,CAAC,CAAC;IACjE,OAAO,IAAI,CAAC,QAAQ,CAAC;AACvB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,GAAW;IACzC,uEAAuE;IACvE,iCAAiC;IACjC,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC;IACrD,IAAI,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,KAAK,GAAG;QAAE,OAAO,GAAG,CAAC,GAAG,CAAC;IAC/C,IAAI,GAAG,CAAC,EAAE;QAAE,OAAO,GAAG,CAAC;IACvB,MAAM,IAAI,KAAK,CAAC,WAAW,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC;AAC5C,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@zhin.js/plugin-short-url",
3
- "version": "1.0.1",
4
- "description": "URL shortener plugin for Zhin.js — shorten & expand URLs",
3
+ "version": "1.1.1",
4
+ "description": "URL shortener plugin for Zhin.js — shorten & expand URLs (Plugin Runtime)",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
7
7
  "types": "./lib/index.d.ts",
@@ -14,8 +14,13 @@
14
14
  "./package.json": "./package.json"
15
15
  },
16
16
  "files": [
17
+ "plugin.js",
18
+ "schema.json",
19
+ "commands",
17
20
  "src",
18
21
  "lib",
22
+ "tools",
23
+ "skills",
19
24
  "README.md"
20
25
  ],
21
26
  "keywords": [
@@ -32,13 +37,24 @@
32
37
  "url": "https://github.com/lc-cn"
33
38
  },
34
39
  "license": "MIT",
35
- "dependencies": {},
36
40
  "devDependencies": {
37
41
  "typescript": "^6.0.3",
38
- "zhin.js": "2.0.1"
42
+ "vitest": "^4.1.10",
43
+ "zod": "^4.4.3",
44
+ "zhin.js": "1.1.2"
39
45
  },
40
46
  "peerDependencies": {
41
- "zhin.js": "2.0.1"
47
+ "zod": "^4.0.0",
48
+ "@zhin.js/command": "^1.1.1",
49
+ "zhin.js": "^1.1.2"
50
+ },
51
+ "peerDependenciesMeta": {
52
+ "@zhin.js/command": {
53
+ "optional": true
54
+ },
55
+ "zod": {
56
+ "optional": true
57
+ }
42
58
  },
43
59
  "repository": {
44
60
  "type": "git",
@@ -49,8 +65,38 @@
49
65
  "access": "public",
50
66
  "registry": "https://registry.npmjs.org"
51
67
  },
68
+ "engines": {
69
+ "node": "^20.19.0 || >=22.12.0"
70
+ },
71
+ "zhin": {
72
+ "protocol": 1,
73
+ "type": "plugin",
74
+ "entry": "./plugin.js",
75
+ "engine": "^1.0.0",
76
+ "runtime": "trusted",
77
+ "features": [
78
+ {
79
+ "package": "@zhin.js/command",
80
+ "api": "^1.0.0"
81
+ },
82
+ {
83
+ "package": "@zhin.js/tool",
84
+ "api": "^1.0.0"
85
+ },
86
+ {
87
+ "package": "@zhin.js/skill",
88
+ "api": "^1.0.0"
89
+ }
90
+ ],
91
+ "plugins": []
92
+ },
93
+ "dependencies": {
94
+ "@zhin.js/tool": "1.1.1",
95
+ "@zhin.js/skill": "1.1.1"
96
+ },
52
97
  "scripts": {
53
- "build": "tsc --build",
54
- "clean": "rimraf lib"
98
+ "build": "tsc",
99
+ "clean": "rimraf lib",
100
+ "test": "NODE_OPTIONS=--experimental-strip-types vitest run --root ../../.. plugins/utils/short-url/tests"
55
101
  }
56
102
  }
package/plugin.js ADDED
@@ -0,0 +1,8 @@
1
+ // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
+ import { definePlugin } from 'zhin.js';
3
+ export default definePlugin({
4
+ name: 'short-url',
5
+ metadata: {
6
+ displayName: 'Short URL',
7
+ },
8
+ });
package/schema.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "type": "object",
4
+ "additionalProperties": false,
5
+ "properties": {}
6
+ }
@@ -0,0 +1,40 @@
1
+ ---
2
+ name: short-url
3
+ description: >-
4
+ 短链接生成能力。当用户想缩短一个长 URL 或生成短链时使用。
5
+ 用户说「帮我缩短这个链接」「转成短链」或给了一个很长的 URL 时应触发。
6
+ keywords:
7
+ - short-url
8
+ - 短链
9
+ - shorten
10
+ - URL
11
+ - 链接
12
+ - 缩短
13
+ tags:
14
+ - short-url
15
+ - utility
16
+ tools:
17
+ - short_url
18
+ ---
19
+
20
+ # 短链接生成技能
21
+
22
+ ## 工具
23
+
24
+ | 工具 | 用途 | 参数 |
25
+ |------|------|------|
26
+ | `short_url` | 缩短 URL | `url`(原始链接,必须是 http/https) |
27
+
28
+ **Example:**
29
+ ```
30
+ 用户: 帮我缩短 https://very-long-domain.com/path/to/something?with=params&and=more
31
+ → short_url(url="https://very-long-domain.com/path/to/something?with=params&and=more")
32
+ → 返回: https://is.gd/xxxxx
33
+ ```
34
+
35
+ 使用 is.gd 服务生成短链接。
36
+
37
+ ## 注意
38
+
39
+ - 输入必须是合法的 http/https URL。
40
+ - 展开短链查看原始地址的功能通过聊天命令 `展开 <url>` 使用。
@@ -0,0 +1,19 @@
1
+ // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
+ import { defineAgentTool } from '@zhin.js/tool';
3
+ import { z } from 'zod';
4
+ import { isValidUrl, shortenUrl } from "../../../../lib/short-url-lib.js";
5
+ export default defineAgentTool({
6
+ description: '缩短一个 URL,返回短链接',
7
+ inputSchema: z.object({ url: z.string().min(1) }),
8
+ keywords: ['短链', '缩短', 'shorten', 'short url'],
9
+ async execute({ url }) {
10
+ if (!isValidUrl(url))
11
+ return '无效的 URL,需要 http:// 或 https:// 开头';
12
+ try {
13
+ return await shortenUrl(url);
14
+ }
15
+ catch (e) {
16
+ return `缩短失败: ${e instanceof Error ? e.message : String(e)}`;
17
+ }
18
+ },
19
+ });
@@ -0,0 +1,17 @@
1
+ import { defineAgentTool } from '@zhin.js/tool';
2
+ import { z } from 'zod';
3
+ import { isValidUrl, shortenUrl } from '../../../../src/short-url-lib.js';
4
+
5
+ export default defineAgentTool<{ url: string }>({
6
+ description: '缩短一个 URL,返回短链接',
7
+ inputSchema: z.object({ url: z.string().min(1) }),
8
+ keywords: ['短链', '缩短', 'shorten', 'short url'],
9
+ async execute({ url }) {
10
+ if (!isValidUrl(url)) return '无效的 URL,需要 http:// 或 https:// 开头';
11
+ try {
12
+ return await shortenUrl(url);
13
+ } catch (e: unknown) {
14
+ return `缩短失败: ${e instanceof Error ? e.message : String(e)}`;
15
+ }
16
+ },
17
+ });
package/src/index.ts CHANGED
@@ -1,84 +1 @@
1
- import { formatCompact, MessageCommand, usePlugin, ZhinTool } from 'zhin.js'
2
-
3
- const { addCommand, addTool, logger } = usePlugin()
4
-
5
- // ── helpers ──────────────────────────────────────────────
6
-
7
- function isValidUrl(input: string): boolean {
8
- try {
9
- const u = new URL(input)
10
- return u.protocol === 'http:' || u.protocol === 'https:'
11
- } catch {
12
- return false
13
- }
14
- }
15
-
16
- async function shortenUrl(url: string): Promise<string> {
17
- const api = `https://is.gd/create.php?format=json&url=${encodeURIComponent(url)}`
18
- const res = await fetch(api)
19
- if (!res.ok) throw new Error(`is.gd 返回 ${res.status}`)
20
- const data = (await res.json()) as { shorturl?: string; errorcode?: number; errormessage?: string }
21
- if (data.errorcode) throw new Error(data.errormessage ?? '缩短失败')
22
- return data.shorturl!
23
- }
24
-
25
- async function expandUrl(url: string): Promise<string> {
26
- const res = await fetch(url, { redirect: 'manual' })
27
- const location = res.headers.get('location')
28
- if (location) return location
29
- if (res.ok) return url
30
- throw new Error(`无法展开链接 (${res.status})`)
31
- }
32
-
33
- // ── commands ─────────────────────────────────────────────
34
-
35
- addCommand(
36
- new MessageCommand('短链 <url:text>')
37
- .desc('缩短一个 URL')
38
- .action(async (_message, result) => {
39
- const url = result.params.url as string
40
- if (!isValidUrl(url)) return '请提供有效的 HTTP/HTTPS 链接'
41
- try {
42
- const short = await shortenUrl(url)
43
- return `短链接: ${short}`
44
- } catch (e: unknown) {
45
- logger.warn(formatCompact( { op: 'shorten', ok: false, error: e instanceof Error ? e.message : String(e) }))
46
- return `缩短失败: ${e instanceof Error ? e.message : String(e)}`
47
- }
48
- }),
49
- )
50
-
51
- addCommand(
52
- new MessageCommand('展开 <url:text>')
53
- .desc('展开一个短链接,显示原始地址')
54
- .action(async (_message, result) => {
55
- const url = result.params.url as string
56
- if (!isValidUrl(url)) return '请提供有效的 HTTP/HTTPS 链接'
57
- try {
58
- const original = await expandUrl(url)
59
- return `原始链接: ${original}`
60
- } catch (e: unknown) {
61
- logger.warn(formatCompact( { op: 'expand', ok: false, error: e instanceof Error ? e.message : String(e) }))
62
- return `展开失败: ${e instanceof Error ? e.message : String(e)}`
63
- }
64
- }),
65
- )
66
-
67
- // ── AI tool ──────────────────────────────────────────────
68
-
69
- addTool(
70
- new ZhinTool('short_url')
71
- .desc('缩短一个 URL,返回短链接')
72
- .keyword('短链', '缩短', 'shorten', 'short url')
73
- .param('url', { type: 'string', description: '要缩短的完整 URL' }, true)
74
- .execute(async (args) => {
75
- const url = args.url as string
76
- if (!isValidUrl(url)) return '无效的 URL,需要 http:// 或 https:// 开头'
77
- try {
78
- return await shortenUrl(url)
79
- } catch (e: unknown) {
80
- return `缩短失败: ${e instanceof Error ? e.message : String(e)}`
81
- }
82
- })
83
- .toTool(),
84
- )
1
+ export { expandUrl, isValidUrl, shortenUrl } from './short-url-lib.js';
@@ -0,0 +1,26 @@
1
+ export function isValidUrl(input: string): boolean {
2
+ try {
3
+ const u = new URL(input);
4
+ return u.protocol === 'http:' || u.protocol === 'https:';
5
+ } catch {
6
+ return false;
7
+ }
8
+ }
9
+
10
+ export async function shortenUrl(url: string): Promise<string> {
11
+ const api = `https://is.gd/create.php?format=json&url=${encodeURIComponent(url)}`;
12
+ const res = await fetch(api);
13
+ if (!res.ok) throw new Error(`is.gd 返回 ${res.status}`);
14
+ const data = (await res.json()) as { shorturl?: string; errorcode?: number; errormessage?: string };
15
+ if (!data.shorturl) throw new Error(data.errormessage ?? '缩短失败');
16
+ return data.shorturl;
17
+ }
18
+
19
+ export async function expandUrl(url: string): Promise<string> {
20
+ // 不能用 redirect:'manual':undici 下返回 opaqueredirect(status 0、headers 空),
21
+ // 拿不到 location。改为跟随重定向后比较最终 URL。
22
+ const res = await fetch(url, { redirect: 'follow' });
23
+ if (res.url && res.url !== url) return res.url;
24
+ if (res.ok) return url;
25
+ throw new Error(`无法展开链接 (${res.status})`);
26
+ }