@koishi-ce/plugin-market 1.1.4 → 1.3.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.
@@ -25,7 +25,7 @@ import {
25
25
  } from "./helpers.ts";
26
26
 
27
27
  const { default: Installer } = await import(
28
- "../node/installer/index.ts"
28
+ "../installer/index.ts"
29
29
  );
30
30
 
31
31
  beforeAll(startApp);
@@ -202,16 +202,31 @@ describe("MarketProvider 市场数据服务", () => {
202
202
  );
203
203
  });
204
204
 
205
- it("依赖 / 注册表数据服务读取安装器缓存", async () => {
206
- const dependencies = await app
207
- .get("console.services.dependencies")
208
- ?.get();
205
+ it("依赖服务两拍推送:快照先行、元数据完成后填充", async () => {
206
+ const service = app.get(
207
+ "console.services.dependencies",
208
+ );
209
+ expect(service).toBeDefined();
210
+ const first = await service?.get();
211
+ // 第一拍:本地快照先行,元数据尚在拉取
209
212
  expect(
210
- dependencies?.["koishi-plugin-demo"]?.request,
213
+ first?.["koishi-plugin-demo"]?.request,
211
214
  ).toBeTruthy();
215
+ // 等元数据拉取完成(本地 registry 桩,秒级内):条目就地填充
216
+ await new Promise((resolve) =>
217
+ setTimeout(resolve, 800),
218
+ );
219
+ expect(first?.["koishi-plugin-demo"]?.latest).toBe(
220
+ "2.0.0",
221
+ );
212
222
  expect(
213
- dependencies?.["koishi-plugin-demo"]?.latest,
214
- ).toBe("2.0.0");
223
+ first?.["koishi-plugin-demo"]?.fetching,
224
+ ).toBeUndefined();
225
+ // 第二拍 get() 复用上一轮结果,不再拉取
226
+ const second = await service?.get();
227
+ expect(second?.["koishi-plugin-demo"]?.latest).toBe(
228
+ "2.0.0",
229
+ );
215
230
 
216
231
  const registry = await app
217
232
  .get("console.services.registry")
@@ -221,6 +236,55 @@ describe("MarketProvider 市场数据服务", () => {
221
236
  ).toContain("2.0.0");
222
237
  });
223
238
 
239
+ itQuiet(
240
+ "registry 无此包的条目两拍后标记 not-found 负缓存",
241
+ async () => {
242
+ // 向宿主清单添加一个 registry 桩里不存在的依赖
243
+ const manifestPath = join(tmp, "package.json");
244
+ const manifest = JSON.parse(
245
+ await Bun.file(manifestPath).text(),
246
+ ) as {
247
+ dependencies: Record<string, string>;
248
+ };
249
+ manifest.dependencies["koishi-plugin-ghost"] =
250
+ "^1.0.0";
251
+ await Bun.write(
252
+ manifestPath,
253
+ JSON.stringify(manifest, null, "\t"),
254
+ );
255
+ try {
256
+ const service = app.get(
257
+ "console.services.dependencies",
258
+ );
259
+ const snapshot = await service?.get();
260
+ expect(
261
+ snapshot?.["koishi-plugin-ghost"]?.request,
262
+ ).toBe("1.0.0");
263
+ await new Promise((resolve) =>
264
+ setTimeout(resolve, 800),
265
+ );
266
+ expect(
267
+ snapshot?.["koishi-plugin-ghost"]?.error,
268
+ ).toBe("not-found");
269
+ // 404 负缓存:重建快照仍标记 not-found 且不再拉取
270
+ const again = await service?.get();
271
+ expect(again?.["koishi-plugin-ghost"]?.error).toBe(
272
+ "not-found",
273
+ );
274
+ expect(
275
+ again?.["koishi-plugin-ghost"]?.fetching,
276
+ ).toBeUndefined();
277
+ } finally {
278
+ delete manifest.dependencies["koishi-plugin-ghost"];
279
+ await Bun.write(
280
+ manifestPath,
281
+ JSON.stringify(manifest, null, "\t"),
282
+ );
283
+ }
284
+ },
285
+ 15000,
286
+ );
287
+
224
288
  itQuiet(
225
289
  "搜索接口失败时 get 返回空数据与错误标记",
226
290
  async () => {
@@ -0,0 +1,116 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ // Copyright (c) 2019-present Shigma and Koishijs contributors.
3
+ // Copyright (c) 2026-present Koishi-CE contributors.
4
+
5
+ /**
6
+ * 依赖管理数据服务(`dependencies` 通道)。
7
+ *
8
+ * 单向数据流的 node 侧源头:get() 同步构建本地快照(纯磁盘读取)先行
9
+ * 推送(条目带 fetching 标记),随后在后台并发拉取 registry 元数据填充
10
+ * latest / error,完成后触发通道刷新二拍推送——前端首屏因此有明确的
11
+ * 「加载中」态,不会把未拉取完的条目误显示为错误。
12
+ *
13
+ * node 侧并发纪律(对标参照实现):
14
+ * - 单飞去重:元数据刷新任务同一时刻至多一个,并发调用复用同一任务;
15
+ * - 循环重扫:刷新循环每轮从快照里重新收集 fetching 条目,期间新的
16
+ * get()(安装完成、客户端接入)带来的新条目自动被后续轮次接管,
17
+ * 不会因单飞复用旧任务而漏拉;
18
+ * - 过期不覆盖:拉取完成后若快照代次已更新,跳过通道刷新(新循环
19
+ * 完成后自会推送)。
20
+ */
21
+ import { DataService } from "@koishi-ce/console";
22
+ import type { Context, Dict } from "@koishi-ce/koishi";
23
+ import { mapLimit } from "@koishi-ce/registry";
24
+ import { loadManifest } from "../installer/manifest.ts";
25
+ import {
26
+ collectDependencyRequests,
27
+ resolveLocalDependency,
28
+ } from "./snapshot.ts";
29
+ import type { Dependency } from "./types.ts";
30
+
31
+ /** registry 元数据并发拉取上限 */
32
+ const CONCURRENCY = 4;
33
+
34
+ export class DependencyService extends DataService<
35
+ Dict<Dependency>
36
+ > {
37
+ // 基类 DataService 声明了 console 注入,子类覆盖须一并保留
38
+ static override inject = ["console", "installer"];
39
+
40
+ /** 快照代次:每次 get() 自增,用于判定后台刷新结果是否已过期 */
41
+ private serial = 0;
42
+ /** 进行中的元数据刷新任务(单飞去重) */
43
+ declare private task: Promise<void> | undefined;
44
+ /** 最近一轮快照(新快照构建时复用 latest 与 404 负缓存) */
45
+ private snapshot: Dict<Dependency> = {};
46
+
47
+ constructor(ctx: Context) {
48
+ super(ctx, "dependencies", { authority: 4 });
49
+ }
50
+
51
+ override async get() {
52
+ this.serial++;
53
+ const manifest = loadManifest(this.ctx.baseDir);
54
+ const snapshot = collectDependencyRequests(
55
+ manifest.dependencies,
56
+ );
57
+ for (const [name, dep] of Object.entries(snapshot)) {
58
+ resolveLocalDependency(name, dep, {
59
+ previous: this.snapshot[name],
60
+ });
61
+ }
62
+ this.snapshot = snapshot;
63
+ this.ensureMetadata();
64
+ return snapshot;
65
+ }
66
+
67
+ private ensureMetadata() {
68
+ if (this.task) return;
69
+ this.task = this.refreshMetadata().finally(() => {
70
+ this.task = undefined;
71
+ });
72
+ }
73
+
74
+ /**
75
+ * 元数据刷新循环:拉完本轮全部 fetching 条目后,若快照代次未变则
76
+ * 触发通道刷新(二拍推送);代次已变(期间有新的 get())则重扫
77
+ * fetching 条目继续,直到无新增且代次一致。
78
+ */
79
+ private async refreshMetadata() {
80
+ for (;;) {
81
+ const serial = this.serial;
82
+ const targets = Object.entries(this.snapshot)
83
+ .filter(([, dep]) => dep.fetching)
84
+ .map(([name]) => name);
85
+ if (!targets.length) return;
86
+ await mapLimit(targets, CONCURRENCY, async (name) => {
87
+ await this.resolveMetadata(name);
88
+ });
89
+ if (serial !== this.serial) continue;
90
+ // 版本表经 registry 通道下发(版本下拉消费),一并刷新
91
+ this.ctx.get("console")?.refresh("dependencies");
92
+ this.ctx.get("console")?.refresh("registry");
93
+ return;
94
+ }
95
+ }
96
+
97
+ /** 单个条目的元数据解析:拉取版本表并填充 latest / error。 */
98
+ private async resolveMetadata(name: string) {
99
+ const dep = this.snapshot[name];
100
+ if (!dep?.fetching) return;
101
+ const installer = this.ctx.installer;
102
+ // getPackage 内部单飞去重并写 fullCache;失败也 resolve 空表
103
+ const versions = await installer.getPackage(name);
104
+ delete dep.fetching;
105
+ if (!Object.keys(versions).length) {
106
+ // 拉取失败:404 归类 not-found(负缓存),其余为网络错误
107
+ dep.error = installer.pkgErrors[name]?.notFound
108
+ ? "not-found"
109
+ : "network";
110
+ delete dep.latest;
111
+ return;
112
+ }
113
+ const [latest] = Object.keys(versions);
114
+ if (latest !== undefined) dep.latest = latest;
115
+ }
116
+ }
@@ -0,0 +1,196 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ // Copyright (c) 2019-present Shigma and Koishijs contributors.
3
+ // Copyright (c) 2026-present Koishi-CE contributors.
4
+
5
+ import { describe, expect, it } from "bun:test";
6
+ import {
7
+ mkdirSync,
8
+ mkdtempSync,
9
+ rmSync,
10
+ writeFileSync,
11
+ } from "node:fs";
12
+ import { tmpdir } from "node:os";
13
+ import { join } from "node:path";
14
+ import {
15
+ collectDependencyRequests,
16
+ resolveLocalDependency,
17
+ } from "./snapshot.ts";
18
+ import type { Dependency } from "./types.ts";
19
+
20
+ /** 造一个临时宿主目录,并在 node_modules 内放置指定包的清单。 */
21
+ function setupHost(installed: Record<string, string> = {}) {
22
+ const cwd = mkdtempSync(join(tmpdir(), "deps-snapshot-"));
23
+ for (const [name, version] of Object.entries(installed)) {
24
+ const dir = join(cwd, "node_modules", name);
25
+ mkdirSync(dir, { recursive: true });
26
+ writeFileSync(
27
+ join(dir, "package.json"),
28
+ JSON.stringify({ name, version }),
29
+ );
30
+ }
31
+ process.chdir(cwd);
32
+ return () => {
33
+ process.chdir(join(cwd, ".."));
34
+ rmSync(cwd, { recursive: true, force: true });
35
+ };
36
+ }
37
+
38
+ describe("collectDependencyRequests", () => {
39
+ it("以依赖声明为底建骨架并剥掉 ^/~ 前缀", () => {
40
+ const result = collectDependencyRequests({
41
+ "koishi-plugin-demo": "^1.2.3",
42
+ cosmokit: "~1.8.0",
43
+ pinned: "2.0.0",
44
+ });
45
+ expect(result["koishi-plugin-demo"]).toEqual({
46
+ request: "1.2.3",
47
+ fetching: true,
48
+ });
49
+ expect(result["cosmokit"]?.request).toBe("1.8.0");
50
+ expect(result["pinned"]?.request).toBe("2.0.0");
51
+ // 元数据全部置为待拉取
52
+ expect(result["pinned"]?.fetching).toBe(true);
53
+ });
54
+ });
55
+
56
+ describe("resolveLocalDependency", () => {
57
+ it("补已装版本并保持待拉取形态", () => {
58
+ const teardown = setupHost({ "pkg-a": "1.0.0" });
59
+ try {
60
+ const dep: Dependency = {
61
+ request: "1.0.0",
62
+ fetching: true,
63
+ };
64
+ resolveLocalDependency("pkg-a", dep, {
65
+ previous: undefined,
66
+ });
67
+ expect(dep.resolved).toBe("1.0.0");
68
+ expect(dep.workspace).toBe(false);
69
+ expect(dep.invalid).toBeUndefined();
70
+ // 已装且合法 semver:继续等待元数据刷新
71
+ expect(dep.fetching).toBe(true);
72
+ } finally {
73
+ teardown();
74
+ }
75
+ });
76
+
77
+ it("未安装的包 resolved 留空、不标 invalid", () => {
78
+ const teardown = setupHost();
79
+ try {
80
+ const dep: Dependency = {
81
+ request: "1.0.0",
82
+ fetching: true,
83
+ };
84
+ resolveLocalDependency("pkg-missing", dep, {
85
+ previous: undefined,
86
+ });
87
+ expect(dep.resolved).toBeUndefined();
88
+ expect(dep.invalid).toBeUndefined();
89
+ expect(dep.fetching).toBe(true);
90
+ } finally {
91
+ teardown();
92
+ }
93
+ });
94
+
95
+ it("npm: 钉名别名标 alias、不判 invalid 且不参与拉取", () => {
96
+ const teardown = setupHost({ koishi: "4.18.11" });
97
+ try {
98
+ const dep: Dependency = {
99
+ request: "npm:@koishi-ce/koishi-shim@^4.18.11",
100
+ fetching: true,
101
+ };
102
+ resolveLocalDependency("koishi", dep, {
103
+ previous: undefined,
104
+ });
105
+ expect(dep.alias).toBe(true);
106
+ expect(dep.resolved).toBe("4.18.11");
107
+ expect(dep.invalid).toBeUndefined();
108
+ // 钉名包无更新可言:不进入 registry 元数据拉取
109
+ expect(dep.fetching).toBeUndefined();
110
+ } finally {
111
+ teardown();
112
+ }
113
+ });
114
+
115
+ it("非精确 semver 的请求标记 invalid 且不参与拉取", () => {
116
+ const teardown = setupHost();
117
+ try {
118
+ const dep: Dependency = {
119
+ request: "file:./plugins/foo",
120
+ fetching: true,
121
+ };
122
+ resolveLocalDependency("local-thing", dep, {
123
+ previous: undefined,
124
+ });
125
+ expect(dep.invalid).toBe(true);
126
+ expect(dep.fetching).toBeUndefined();
127
+ } finally {
128
+ teardown();
129
+ }
130
+ });
131
+
132
+ it("上一轮 latest 与 404 负缓存被复用,网络错误不沿用", () => {
133
+ const teardown = setupHost({ "pkg-a": "1.0.0" });
134
+ try {
135
+ const base = { request: "1.0.0", resolved: "1.0.0" };
136
+ // latest 复用:fetching 随之消除
137
+ const withLatest: Dependency = {
138
+ ...base,
139
+ fetching: true,
140
+ latest: "2.0.0",
141
+ };
142
+ resolveLocalDependency("pkg-a", withLatest, {
143
+ previous: { ...base, latest: "2.0.0" },
144
+ });
145
+ expect(withLatest.latest).toBe("2.0.0");
146
+ expect(withLatest.fetching).toBeUndefined();
147
+
148
+ // not-found 复用(会话内不再重试)
149
+ const notFound: Dependency = {
150
+ ...base,
151
+ fetching: true,
152
+ };
153
+ resolveLocalDependency("pkg-a", notFound, {
154
+ previous: { ...base, error: "not-found" },
155
+ });
156
+ expect(notFound.error).toBe("not-found");
157
+ expect(notFound.fetching).toBeUndefined();
158
+
159
+ // 网络错误不沿用:下一轮重新拉取
160
+ const network: Dependency = {
161
+ ...base,
162
+ fetching: true,
163
+ };
164
+ resolveLocalDependency("pkg-a", network, {
165
+ previous: { ...base, error: "network" },
166
+ });
167
+ expect(network.error).toBeUndefined();
168
+ expect(network.fetching).toBe(true);
169
+ } finally {
170
+ teardown();
171
+ }
172
+ });
173
+
174
+ it("request 或 resolved 变化的条目不复用上一轮结果", () => {
175
+ const teardown = setupHost({ "pkg-a": "1.5.0" });
176
+ try {
177
+ const dep: Dependency = {
178
+ request: "1.5.0",
179
+ fetching: true,
180
+ };
181
+ resolveLocalDependency("pkg-a", dep, {
182
+ previous: {
183
+ request: "1.0.0",
184
+ resolved: "1.0.0",
185
+ latest: "2.0.0",
186
+ error: "not-found",
187
+ },
188
+ });
189
+ expect(dep.latest).toBeUndefined();
190
+ expect(dep.error).toBeUndefined();
191
+ expect(dep.fetching).toBe(true);
192
+ } finally {
193
+ teardown();
194
+ }
195
+ });
196
+ });
@@ -0,0 +1,97 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ // Copyright (c) 2019-present Shigma and Koishijs contributors.
3
+ // Copyright (c) 2026-present Koishi-CE contributors.
4
+
5
+ /**
6
+ * 宿主依赖本地快照的构建纯函数。
7
+ *
8
+ * 两步:collectDependencyRequests 以宿主 package.json 的依赖声明为底
9
+ * 构建骨架;resolveLocalDependency 逐包读 node_modules 补已装版本与
10
+ * workspace 标记。全程只碰本地磁盘、不发网络请求——registry 元数据由
11
+ * DependencyService 的刷新阶段另行填充。
12
+ *
13
+ * 上一轮条目的复用规则(防闪烁 + 负缓存语义):
14
+ * - request 与 resolved 未变时沿用 latest(省一次 registry 往返);
15
+ * - 仅 404(not-found)的 error 被沿用(registry 确认无此包,会话内
16
+ * 不再重试);网络错误的 error 不沿用,下一轮 get() 会重新拉取。
17
+ */
18
+ import type { Dict } from "@koishi-ce/koishi";
19
+ import { valid } from "semver";
20
+ import { loadManifest } from "../installer/manifest.ts";
21
+ import type { Dependency } from "./types.ts";
22
+
23
+ /** 以宿主 dependencies 声明为底构建快照骨架(元数据字段全部置为待拉取)。 */
24
+ export function collectDependencyRequests(
25
+ dependencies: Record<string, string>,
26
+ ): Dict<Dependency> {
27
+ const result: Dict<Dependency> = {};
28
+ for (const [name, request] of Object.entries(
29
+ dependencies,
30
+ )) {
31
+ // 剥掉 ^/~ 前缀:后续 valid() 判定与 latest 比较都按精确版本语义进行
32
+ result[name] = {
33
+ request: request.replace(/^[~^]/, ""),
34
+ fetching: true,
35
+ };
36
+ }
37
+ return result;
38
+ }
39
+
40
+ /** 逐包本地解析所需的上下文:上一轮同名条目(复用 latest 与 404 负缓存)。 */
41
+ export interface LocalResolveContext {
42
+ previous: Dependency | undefined;
43
+ }
44
+
45
+ /**
46
+ * 单个依赖条目的本地解析:已装版本 / workspace 标记 / invalid 标记,
47
+ * 就地修改传入的 dep。node_modules 内无此包时 resolved 留空,由后续
48
+ * 元数据刷新阶段补齐。
49
+ */
50
+ export function resolveLocalDependency(
51
+ name: string,
52
+ dep: Dependency,
53
+ ctx: LocalResolveContext,
54
+ ): void {
55
+ try {
56
+ // 已装版本读 node_modules 内的 package.json;未安装/读取失败留空
57
+ const meta = loadManifest(name);
58
+ dep.resolved = meta.version;
59
+ dep.workspace = meta.$workspace;
60
+ if (meta.$workspace) {
61
+ // workspace 包不参与 registry 拉取与更新判定
62
+ delete dep.fetching;
63
+ }
64
+ } catch {
65
+ // 尚未安装(或残留声明):保持待拉取形态
66
+ }
67
+
68
+ if (dep.request.startsWith("npm:")) {
69
+ // npm: 协议钉名别名(下游 shim 占名):声明键与真实包名不同是
70
+ // 设计内行为,语义上等同 workspace——固定钉死、无更新可言
71
+ dep.alias = true;
72
+ delete dep.fetching;
73
+ } else if (!dep.workspace && !valid(dep.request)) {
74
+ // 非精确 semver 的请求(file: 路径、git/url 串等):不参与
75
+ // latest 刷新,前端据此降级展示
76
+ dep.invalid = true;
77
+ delete dep.fetching;
78
+ }
79
+
80
+ const { previous } = ctx;
81
+ if (
82
+ previous &&
83
+ previous.request === dep.request &&
84
+ previous.resolved === dep.resolved
85
+ ) {
86
+ if (previous.latest) {
87
+ dep.latest = previous.latest;
88
+ delete dep.fetching;
89
+ }
90
+ // 404 负缓存:registry 已确认无此包,会话内不再重试;
91
+ // 网络错误不沿用,下一轮 get() 重新拉取
92
+ if (previous.error === "not-found") {
93
+ dep.error = "not-found";
94
+ delete dep.fetching;
95
+ }
96
+ }
97
+ }
@@ -0,0 +1,45 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ // Copyright (c) 2019-present Shigma and Koishijs contributors.
3
+ // Copyright (c) 2026-present Koishi-CE contributors.
4
+
5
+ /**
6
+ * 依赖条目类型定义(dependencies 数据通道的值类型)。
7
+ *
8
+ * 单个依赖条目的请求范围、已装版本与来源标记;latest / error / fetching
9
+ * 三个可选字段由元数据刷新阶段填充,前端依赖页的分类状态机据此判定
10
+ * 六态。类型集中在此,node 侧声明与 client 侧镜像(console-services.ts)
11
+ * 均从这里取形状。
12
+ */
13
+
14
+ /** registry 元数据拉取失败的归类:404(registry 无此包)与其余(网络等) */
15
+ export type DependencyError = "not-found" | "network";
16
+
17
+ /** 依赖条目:请求范围、已装版本、来源标记与最新版信息。 */
18
+ export interface Dependency {
19
+ /**
20
+ * requested semver range
21
+ * @example `^1.2.3` -> `1.2.3`
22
+ */
23
+ request: string;
24
+ /**
25
+ * installed package version
26
+ * @example `1.2.5`
27
+ */
28
+ resolved?: string | undefined;
29
+ /** whether it is a workspace package */
30
+ workspace?: boolean | undefined;
31
+ /**
32
+ * npm: 协议钉名别名(下游 shim 占名机制,如
33
+ * `npm:@koishi-ce/koishi-shim@^4.18.11`):设计内形态而非异常,
34
+ * 不判 invalid、不参与 registry 元数据拉取与更新判定
35
+ */
36
+ alias?: boolean | undefined;
37
+ /** valid (unsupported) syntax */
38
+ invalid?: boolean | undefined;
39
+ /** latest version */
40
+ latest?: string | undefined;
41
+ /** registry 元数据拉取失败的归类(缺失代表尚在拉取或已成功) */
42
+ error?: DependencyError | undefined;
43
+ /** 本轮元数据尚未拉取完成(前端据此显示加载中而非错误) */
44
+ fetching?: boolean | undefined;
45
+ }
package/src/node/deps.ts CHANGED
@@ -8,19 +8,6 @@ import type {
8
8
  DependencyMetaKey,
9
9
  RemotePackage,
10
10
  } from "@koishi-ce/registry";
11
- import type { Dependency } from "./installer/index.ts";
12
-
13
- class DependencyProvider extends DataService<
14
- Dict<Dependency>
15
- > {
16
- constructor(ctx: Context) {
17
- super(ctx, "dependencies", { authority: 4 });
18
- }
19
-
20
- override async get() {
21
- return this.ctx.installer.getDeps();
22
- }
23
- }
24
11
 
25
12
  class RegistryProvider extends DataService<
26
13
  Dict<Dict<Pick<RemotePackage, DependencyMetaKey>>>
@@ -34,4 +21,4 @@ class RegistryProvider extends DataService<
34
21
  }
35
22
  }
36
23
 
37
- export { DependencyProvider, RegistryProvider };
24
+ export { RegistryProvider };
package/src/node/index.ts CHANGED
@@ -16,16 +16,15 @@ import type {
16
16
  import { gt } from "semver";
17
17
  import messageZhCN from "../../locales/message.zh-CN.yml";
18
18
  import schemaZhCN from "../../locales/schema.zh-CN.yml";
19
- import {
20
- DependencyProvider,
21
- RegistryProvider,
22
- } from "./deps.ts";
19
+ import { DependencyService } from "./dependencies/service.ts";
20
+ import type { Dependency } from "./dependencies/types.ts";
21
+ import { RegistryProvider } from "./deps.ts";
23
22
  import Installer from "./installer/index.ts";
24
23
  import MarketProvider from "./market.ts";
25
24
 
26
25
  export * from "../shared/index.ts";
27
-
28
- export { DependencyProvider, Installer, RegistryProvider };
26
+ export type { Dependency };
27
+ export { DependencyService, Installer, RegistryProvider };
29
28
 
30
29
  declare module "@koishi-ce/koishi" {
31
30
  interface Context {
@@ -36,7 +35,7 @@ declare module "@koishi-ce/koishi" {
36
35
  declare module "@koishi-ce/console" {
37
36
  namespace Console {
38
37
  interface Services {
39
- dependencies: DependencyProvider;
38
+ dependencies: DependencyService;
40
39
  registry: RegistryProvider;
41
40
  }
42
41
  }
@@ -243,7 +242,7 @@ export function apply(ctx: Context, config: Config) {
243
242
  });
244
243
 
245
244
  ctx.inject(["console", "installer"], (ctx) => {
246
- ctx.plugin(DependencyProvider);
245
+ ctx.plugin(DependencyService);
247
246
  ctx.plugin(RegistryProvider);
248
247
  ctx.plugin(MarketProvider, config.search);
249
248