@pooder/core 2.1.0 → 2.2.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/CHANGELOG.md +6 -0
- package/dist/index.d.mts +97 -18
- package/dist/index.d.ts +97 -18
- package/dist/index.js +473 -188
- package/dist/index.mjs +464 -187
- package/package.json +1 -1
- package/src/context.ts +7 -2
- package/src/extension.ts +10 -4
- package/src/index.ts +209 -32
- package/src/service.ts +191 -11
- package/src/services/ToolSessionService.ts +41 -4
- package/src/services/WorkbenchService.ts +65 -15
- package/src/services/index.ts +14 -0
- package/src/services/tokens.ts +27 -0
package/dist/index.mjs
CHANGED
|
@@ -1,20 +1,108 @@
|
|
|
1
1
|
// src/service.ts
|
|
2
|
+
function createServiceToken(name) {
|
|
3
|
+
if (!name) {
|
|
4
|
+
throw new Error("Service token name is required.");
|
|
5
|
+
}
|
|
6
|
+
return Object.freeze({
|
|
7
|
+
kind: "service-token",
|
|
8
|
+
key: Symbol(name),
|
|
9
|
+
name
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
function isServiceToken(identifier) {
|
|
13
|
+
return typeof identifier === "object" && identifier !== null && "kind" in identifier && identifier.kind === "service-token";
|
|
14
|
+
}
|
|
2
15
|
var ServiceRegistry = class {
|
|
3
16
|
constructor() {
|
|
4
|
-
this.
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
17
|
+
this.servicesByName = /* @__PURE__ */ new Map();
|
|
18
|
+
this.servicesByToken = /* @__PURE__ */ new Map();
|
|
19
|
+
this.registrationOrder = [];
|
|
20
|
+
}
|
|
21
|
+
register(identifier, service, options = {}) {
|
|
22
|
+
const normalized = this.normalizeIdentifier(identifier);
|
|
23
|
+
const existing = this.findEntry(normalized);
|
|
24
|
+
if (existing && !options.allowOverride) {
|
|
25
|
+
throw new Error(`Service "${normalized.name}" is already registered.`);
|
|
26
|
+
}
|
|
27
|
+
if (existing) {
|
|
28
|
+
this.removeEntry(existing);
|
|
29
|
+
}
|
|
30
|
+
const entry = {
|
|
31
|
+
token: normalized.token,
|
|
32
|
+
name: normalized.name,
|
|
33
|
+
service
|
|
34
|
+
};
|
|
35
|
+
this.servicesByName.set(entry.name, entry);
|
|
36
|
+
if (entry.token) {
|
|
37
|
+
this.servicesByToken.set(entry.token.key, entry);
|
|
38
|
+
}
|
|
39
|
+
this.registrationOrder.push(entry);
|
|
8
40
|
return service;
|
|
9
41
|
}
|
|
10
|
-
get(
|
|
11
|
-
|
|
42
|
+
get(identifier) {
|
|
43
|
+
const normalized = this.normalizeIdentifier(identifier);
|
|
44
|
+
const entry = this.findEntry(normalized);
|
|
45
|
+
return entry == null ? void 0 : entry.service;
|
|
12
46
|
}
|
|
13
|
-
|
|
14
|
-
|
|
47
|
+
getOrThrow(identifier, errorMessage) {
|
|
48
|
+
const service = this.get(identifier);
|
|
49
|
+
if (service) {
|
|
50
|
+
return service;
|
|
51
|
+
}
|
|
52
|
+
const normalized = this.normalizeIdentifier(identifier);
|
|
53
|
+
throw new Error(errorMessage != null ? errorMessage : `Service "${normalized.name}" not found.`);
|
|
54
|
+
}
|
|
55
|
+
has(identifier) {
|
|
56
|
+
const normalized = this.normalizeIdentifier(identifier);
|
|
57
|
+
return Boolean(this.findEntry(normalized));
|
|
58
|
+
}
|
|
59
|
+
delete(identifier) {
|
|
60
|
+
const normalized = this.normalizeIdentifier(identifier);
|
|
61
|
+
const entry = this.findEntry(normalized);
|
|
62
|
+
if (!entry) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
this.removeEntry(entry);
|
|
66
|
+
return true;
|
|
15
67
|
}
|
|
16
|
-
|
|
17
|
-
this.
|
|
68
|
+
list() {
|
|
69
|
+
return this.registrationOrder.map((entry) => ({
|
|
70
|
+
id: entry.name,
|
|
71
|
+
token: entry.token,
|
|
72
|
+
service: entry.service
|
|
73
|
+
}));
|
|
74
|
+
}
|
|
75
|
+
clear() {
|
|
76
|
+
this.servicesByName.clear();
|
|
77
|
+
this.servicesByToken.clear();
|
|
78
|
+
this.registrationOrder.length = 0;
|
|
79
|
+
}
|
|
80
|
+
findEntry(identifier) {
|
|
81
|
+
var _a;
|
|
82
|
+
if (identifier.token) {
|
|
83
|
+
return (_a = this.servicesByToken.get(identifier.token.key)) != null ? _a : this.servicesByName.get(identifier.name);
|
|
84
|
+
}
|
|
85
|
+
return this.servicesByName.get(identifier.name);
|
|
86
|
+
}
|
|
87
|
+
normalizeIdentifier(identifier) {
|
|
88
|
+
if (isServiceToken(identifier)) {
|
|
89
|
+
return { token: identifier, name: identifier.name };
|
|
90
|
+
}
|
|
91
|
+
const name = identifier.trim();
|
|
92
|
+
if (!name) {
|
|
93
|
+
throw new Error("Service identifier must be a non-empty string.");
|
|
94
|
+
}
|
|
95
|
+
return { name };
|
|
96
|
+
}
|
|
97
|
+
removeEntry(entry) {
|
|
98
|
+
this.servicesByName.delete(entry.name);
|
|
99
|
+
if (entry.token) {
|
|
100
|
+
this.servicesByToken.delete(entry.token.key);
|
|
101
|
+
}
|
|
102
|
+
const index = this.registrationOrder.lastIndexOf(entry);
|
|
103
|
+
if (index >= 0) {
|
|
104
|
+
this.registrationOrder.splice(index, 1);
|
|
105
|
+
}
|
|
18
106
|
}
|
|
19
107
|
};
|
|
20
108
|
|
|
@@ -172,127 +260,6 @@ var ContributionRegistry = class {
|
|
|
172
260
|
}
|
|
173
261
|
};
|
|
174
262
|
|
|
175
|
-
// src/extension.ts
|
|
176
|
-
var ExtensionRegistry = class extends Map {
|
|
177
|
-
};
|
|
178
|
-
var ExtensionManager = class {
|
|
179
|
-
constructor(context) {
|
|
180
|
-
this.extensionRegistry = new ExtensionRegistry();
|
|
181
|
-
this.extensionDisposables = /* @__PURE__ */ new Map();
|
|
182
|
-
this.context = context;
|
|
183
|
-
}
|
|
184
|
-
register(extension) {
|
|
185
|
-
if (this.extensionRegistry.has(extension.id)) {
|
|
186
|
-
console.warn(
|
|
187
|
-
`Plugin "${extension.id}" already registered. It will be overwritten.`
|
|
188
|
-
);
|
|
189
|
-
}
|
|
190
|
-
this.extensionDisposables.set(extension.id, []);
|
|
191
|
-
const disposables = this.extensionDisposables.get(extension.id);
|
|
192
|
-
if (extension.contribute) {
|
|
193
|
-
for (const [pointId, items] of Object.entries(extension.contribute())) {
|
|
194
|
-
if (Array.isArray(items)) {
|
|
195
|
-
items.forEach((item, index) => {
|
|
196
|
-
const contributionId = item.id || (item.command ? item.command : `${extension.id}.${pointId}.${index}`);
|
|
197
|
-
const contribution = {
|
|
198
|
-
id: contributionId,
|
|
199
|
-
metadata: {
|
|
200
|
-
extensionId: extension.id,
|
|
201
|
-
...item == null ? void 0 : item.metadata
|
|
202
|
-
},
|
|
203
|
-
data: item
|
|
204
|
-
};
|
|
205
|
-
const disposable = this.context.contributions.register(
|
|
206
|
-
pointId,
|
|
207
|
-
contribution
|
|
208
|
-
);
|
|
209
|
-
disposables.push(disposable);
|
|
210
|
-
const dispose = this.collectContribution(pointId, contribution);
|
|
211
|
-
if (dispose) {
|
|
212
|
-
disposables.push(dispose);
|
|
213
|
-
}
|
|
214
|
-
});
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
try {
|
|
219
|
-
this.extensionRegistry.set(extension.id, extension);
|
|
220
|
-
this.context.eventBus.emit("extension:register", extension);
|
|
221
|
-
} catch (error) {
|
|
222
|
-
console.error(
|
|
223
|
-
`Error in onCreate hook for plugin "${extension.id}":`,
|
|
224
|
-
error
|
|
225
|
-
);
|
|
226
|
-
}
|
|
227
|
-
try {
|
|
228
|
-
extension.activate(this.context);
|
|
229
|
-
} catch (error) {
|
|
230
|
-
console.error(
|
|
231
|
-
`Error in onActivate hook for plugin "${extension.id}":`,
|
|
232
|
-
error
|
|
233
|
-
);
|
|
234
|
-
}
|
|
235
|
-
console.log(`Plugin "${extension.id}" registered successfully`);
|
|
236
|
-
}
|
|
237
|
-
collectContribution(pointId, item) {
|
|
238
|
-
if (pointId === ContributionPointIds.CONFIGURATIONS) {
|
|
239
|
-
const configService = this.context.services.get(
|
|
240
|
-
"ConfigurationService"
|
|
241
|
-
);
|
|
242
|
-
configService == null ? void 0 : configService.initializeDefaults([item.data]);
|
|
243
|
-
}
|
|
244
|
-
if (pointId === ContributionPointIds.COMMANDS && item.data.handler) {
|
|
245
|
-
const commandService = this.context.services.get("CommandService");
|
|
246
|
-
return commandService.registerCommand(item.id, item.data.handler);
|
|
247
|
-
}
|
|
248
|
-
if (pointId === ContributionPointIds.TOOLS) {
|
|
249
|
-
const toolRegistry = this.context.services.get("ToolRegistryService");
|
|
250
|
-
if (!toolRegistry) return;
|
|
251
|
-
return toolRegistry.registerTool(item.data);
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
unregister(name) {
|
|
255
|
-
const extension = this.extensionRegistry.get(name);
|
|
256
|
-
if (!extension) {
|
|
257
|
-
console.warn(`Plugin "${name}" not found.`);
|
|
258
|
-
return;
|
|
259
|
-
}
|
|
260
|
-
try {
|
|
261
|
-
extension.deactivate(this.context);
|
|
262
|
-
} catch (error) {
|
|
263
|
-
console.error(`Error in deactivate for plugin "${name}":`, error);
|
|
264
|
-
}
|
|
265
|
-
const disposables = this.extensionDisposables.get(name);
|
|
266
|
-
if (disposables) {
|
|
267
|
-
disposables.forEach((d) => d.dispose());
|
|
268
|
-
this.extensionDisposables.delete(name);
|
|
269
|
-
}
|
|
270
|
-
this.extensionRegistry.delete(name);
|
|
271
|
-
console.log(`Plugin "${name}" unregistered`);
|
|
272
|
-
return true;
|
|
273
|
-
}
|
|
274
|
-
enable(name) {
|
|
275
|
-
const extension = this.extensionRegistry.get(name);
|
|
276
|
-
if (!extension) {
|
|
277
|
-
console.warn(`Plugin "${name}" not found.`);
|
|
278
|
-
return;
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
disable(name) {
|
|
282
|
-
const extension = this.extensionRegistry.get(name);
|
|
283
|
-
if (!extension) {
|
|
284
|
-
console.warn(`Plugin "${name}" not found.`);
|
|
285
|
-
return;
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
update() {
|
|
289
|
-
}
|
|
290
|
-
destroy() {
|
|
291
|
-
const extensionNames = Array.from(this.extensionRegistry.keys());
|
|
292
|
-
extensionNames.forEach((name) => this.unregister(name));
|
|
293
|
-
}
|
|
294
|
-
};
|
|
295
|
-
|
|
296
263
|
// src/services/CommandService.ts
|
|
297
264
|
var CommandService = class {
|
|
298
265
|
constructor() {
|
|
@@ -487,11 +454,42 @@ var ToolRegistryService = class {
|
|
|
487
454
|
}
|
|
488
455
|
};
|
|
489
456
|
|
|
457
|
+
// src/services/tokens.ts
|
|
458
|
+
var COMMAND_SERVICE = createServiceToken(
|
|
459
|
+
"CommandService"
|
|
460
|
+
);
|
|
461
|
+
var CONFIGURATION_SERVICE = createServiceToken(
|
|
462
|
+
"ConfigurationService"
|
|
463
|
+
);
|
|
464
|
+
var TOOL_REGISTRY_SERVICE = createServiceToken("ToolRegistryService");
|
|
465
|
+
var TOOL_SESSION_SERVICE = createServiceToken("ToolSessionService");
|
|
466
|
+
var WORKBENCH_SERVICE = createServiceToken("WorkbenchService");
|
|
467
|
+
var CORE_SERVICE_TOKENS = {
|
|
468
|
+
COMMAND: COMMAND_SERVICE,
|
|
469
|
+
CONFIGURATION: CONFIGURATION_SERVICE,
|
|
470
|
+
TOOL_REGISTRY: TOOL_REGISTRY_SERVICE,
|
|
471
|
+
TOOL_SESSION: TOOL_SESSION_SERVICE,
|
|
472
|
+
WORKBENCH: WORKBENCH_SERVICE
|
|
473
|
+
};
|
|
474
|
+
|
|
490
475
|
// src/services/ToolSessionService.ts
|
|
491
476
|
var ToolSessionService = class {
|
|
492
|
-
constructor() {
|
|
477
|
+
constructor(dependencies = {}) {
|
|
493
478
|
this.sessions = /* @__PURE__ */ new Map();
|
|
494
479
|
this.dirtyTrackers = /* @__PURE__ */ new Map();
|
|
480
|
+
this.commandService = dependencies.commandService;
|
|
481
|
+
this.toolRegistry = dependencies.toolRegistry;
|
|
482
|
+
}
|
|
483
|
+
init(context) {
|
|
484
|
+
var _a, _b;
|
|
485
|
+
(_a = this.commandService) != null ? _a : this.commandService = context.get(COMMAND_SERVICE);
|
|
486
|
+
(_b = this.toolRegistry) != null ? _b : this.toolRegistry = context.get(TOOL_REGISTRY_SERVICE);
|
|
487
|
+
if (!this.commandService) {
|
|
488
|
+
throw new Error("ToolSessionService requires CommandService.");
|
|
489
|
+
}
|
|
490
|
+
if (!this.toolRegistry) {
|
|
491
|
+
throw new Error("ToolSessionService requires ToolRegistryService.");
|
|
492
|
+
}
|
|
495
493
|
}
|
|
496
494
|
setCommandService(commandService) {
|
|
497
495
|
this.commandService = commandService;
|
|
@@ -541,12 +539,23 @@ var ToolSessionService = class {
|
|
|
541
539
|
session.lastUpdatedAt = Date.now();
|
|
542
540
|
}
|
|
543
541
|
resolveTool(toolId) {
|
|
544
|
-
|
|
545
|
-
return (_a = this.toolRegistry) == null ? void 0 : _a.getTool(toolId);
|
|
542
|
+
return this.getToolRegistry().getTool(toolId);
|
|
546
543
|
}
|
|
547
544
|
async runCommand(commandId, ...args) {
|
|
548
|
-
if (!commandId
|
|
549
|
-
return await this.
|
|
545
|
+
if (!commandId) return void 0;
|
|
546
|
+
return await this.getCommandService().executeCommand(commandId, ...args);
|
|
547
|
+
}
|
|
548
|
+
getCommandService() {
|
|
549
|
+
if (!this.commandService) {
|
|
550
|
+
throw new Error("ToolSessionService is not initialized.");
|
|
551
|
+
}
|
|
552
|
+
return this.commandService;
|
|
553
|
+
}
|
|
554
|
+
getToolRegistry() {
|
|
555
|
+
if (!this.toolRegistry) {
|
|
556
|
+
throw new Error("ToolSessionService is not initialized.");
|
|
557
|
+
}
|
|
558
|
+
return this.toolRegistry;
|
|
550
559
|
}
|
|
551
560
|
async begin(toolId) {
|
|
552
561
|
var _a;
|
|
@@ -626,11 +635,27 @@ var ToolSessionService = class {
|
|
|
626
635
|
|
|
627
636
|
// src/services/WorkbenchService.ts
|
|
628
637
|
var WorkbenchService = class {
|
|
629
|
-
constructor() {
|
|
638
|
+
constructor(dependencies = {}) {
|
|
630
639
|
this._activeToolId = null;
|
|
631
640
|
this.guards = [];
|
|
632
|
-
|
|
633
|
-
|
|
641
|
+
this.eventBus = dependencies.eventBus;
|
|
642
|
+
this.toolRegistry = dependencies.toolRegistry;
|
|
643
|
+
this.sessionService = dependencies.sessionService;
|
|
644
|
+
}
|
|
645
|
+
init(context) {
|
|
646
|
+
var _a, _b, _c;
|
|
647
|
+
(_a = this.eventBus) != null ? _a : this.eventBus = context.eventBus;
|
|
648
|
+
(_b = this.toolRegistry) != null ? _b : this.toolRegistry = context.get(TOOL_REGISTRY_SERVICE);
|
|
649
|
+
(_c = this.sessionService) != null ? _c : this.sessionService = context.get(TOOL_SESSION_SERVICE);
|
|
650
|
+
if (!this.eventBus) {
|
|
651
|
+
throw new Error("WorkbenchService requires EventBus.");
|
|
652
|
+
}
|
|
653
|
+
if (!this.toolRegistry) {
|
|
654
|
+
throw new Error("WorkbenchService requires ToolRegistryService.");
|
|
655
|
+
}
|
|
656
|
+
if (!this.sessionService) {
|
|
657
|
+
throw new Error("WorkbenchService requires ToolSessionService.");
|
|
658
|
+
}
|
|
634
659
|
}
|
|
635
660
|
dispose() {
|
|
636
661
|
this.guards = [];
|
|
@@ -666,11 +691,14 @@ var WorkbenchService = class {
|
|
|
666
691
|
return true;
|
|
667
692
|
}
|
|
668
693
|
async switchTool(id, options) {
|
|
669
|
-
var _a
|
|
694
|
+
var _a;
|
|
695
|
+
const eventBus = this.getEventBus();
|
|
696
|
+
const toolRegistry = this.getToolRegistry();
|
|
697
|
+
const sessionService = this.getSessionService();
|
|
670
698
|
if (this._activeToolId === id) {
|
|
671
699
|
return { ok: true, from: this._activeToolId, to: id };
|
|
672
700
|
}
|
|
673
|
-
if (id &&
|
|
701
|
+
if (id && !toolRegistry.hasTool(id)) {
|
|
674
702
|
return {
|
|
675
703
|
ok: false,
|
|
676
704
|
from: this._activeToolId,
|
|
@@ -685,7 +713,7 @@ var WorkbenchService = class {
|
|
|
685
713
|
};
|
|
686
714
|
const guardAllowed = await this.runGuards(context);
|
|
687
715
|
if (!guardAllowed) {
|
|
688
|
-
|
|
716
|
+
eventBus.emit("tool:switch:blocked", {
|
|
689
717
|
...context,
|
|
690
718
|
reason: "blocked-by-guard"
|
|
691
719
|
});
|
|
@@ -696,12 +724,10 @@ var WorkbenchService = class {
|
|
|
696
724
|
reason: "blocked-by-guard"
|
|
697
725
|
};
|
|
698
726
|
}
|
|
699
|
-
if (context.from
|
|
700
|
-
const leaveResult = await
|
|
701
|
-
context.from
|
|
702
|
-
);
|
|
727
|
+
if (context.from) {
|
|
728
|
+
const leaveResult = await sessionService.handleBeforeLeave(context.from);
|
|
703
729
|
if (leaveResult.decision === "blocked") {
|
|
704
|
-
|
|
730
|
+
eventBus.emit("tool:switch:blocked", {
|
|
705
731
|
...context,
|
|
706
732
|
reason: leaveResult.reason || "session-blocked"
|
|
707
733
|
});
|
|
@@ -712,19 +738,19 @@ var WorkbenchService = class {
|
|
|
712
738
|
reason: leaveResult.reason || "session-blocked"
|
|
713
739
|
};
|
|
714
740
|
}
|
|
715
|
-
|
|
741
|
+
sessionService.deactivateSession(context.from);
|
|
716
742
|
}
|
|
717
|
-
if (id
|
|
718
|
-
const tool =
|
|
719
|
-
if ((tool == null ? void 0 : tool.interaction) === "session" && ((
|
|
720
|
-
await
|
|
743
|
+
if (id) {
|
|
744
|
+
const tool = toolRegistry.getTool(id);
|
|
745
|
+
if ((tool == null ? void 0 : tool.interaction) === "session" && ((_a = tool.session) == null ? void 0 : _a.autoBegin) !== false) {
|
|
746
|
+
await sessionService.begin(id);
|
|
721
747
|
}
|
|
722
748
|
}
|
|
723
749
|
const previous = this._activeToolId;
|
|
724
750
|
this._activeToolId = id;
|
|
725
751
|
const reason = options == null ? void 0 : options.reason;
|
|
726
|
-
|
|
727
|
-
|
|
752
|
+
eventBus.emit("tool:activated", { id, previous, reason });
|
|
753
|
+
eventBus.emit("tool:switch", { from: previous, to: id, reason });
|
|
728
754
|
return { ok: true, from: previous, to: id };
|
|
729
755
|
}
|
|
730
756
|
async activate(id) {
|
|
@@ -733,6 +759,147 @@ var WorkbenchService = class {
|
|
|
733
759
|
async deactivate() {
|
|
734
760
|
return await this.switchTool(null, { reason: "deactivate" });
|
|
735
761
|
}
|
|
762
|
+
getEventBus() {
|
|
763
|
+
if (!this.eventBus) {
|
|
764
|
+
throw new Error("WorkbenchService is not initialized.");
|
|
765
|
+
}
|
|
766
|
+
return this.eventBus;
|
|
767
|
+
}
|
|
768
|
+
getToolRegistry() {
|
|
769
|
+
if (!this.toolRegistry) {
|
|
770
|
+
throw new Error("WorkbenchService is not initialized.");
|
|
771
|
+
}
|
|
772
|
+
return this.toolRegistry;
|
|
773
|
+
}
|
|
774
|
+
getSessionService() {
|
|
775
|
+
if (!this.sessionService) {
|
|
776
|
+
throw new Error("WorkbenchService is not initialized.");
|
|
777
|
+
}
|
|
778
|
+
return this.sessionService;
|
|
779
|
+
}
|
|
780
|
+
};
|
|
781
|
+
|
|
782
|
+
// src/extension.ts
|
|
783
|
+
var ExtensionRegistry = class extends Map {
|
|
784
|
+
};
|
|
785
|
+
var ExtensionManager = class {
|
|
786
|
+
constructor(context) {
|
|
787
|
+
this.extensionRegistry = new ExtensionRegistry();
|
|
788
|
+
this.extensionDisposables = /* @__PURE__ */ new Map();
|
|
789
|
+
this.context = context;
|
|
790
|
+
}
|
|
791
|
+
register(extension) {
|
|
792
|
+
if (this.extensionRegistry.has(extension.id)) {
|
|
793
|
+
console.warn(
|
|
794
|
+
`Plugin "${extension.id}" already registered. It will be overwritten.`
|
|
795
|
+
);
|
|
796
|
+
}
|
|
797
|
+
this.extensionDisposables.set(extension.id, []);
|
|
798
|
+
const disposables = this.extensionDisposables.get(extension.id);
|
|
799
|
+
if (extension.contribute) {
|
|
800
|
+
for (const [pointId, items] of Object.entries(extension.contribute())) {
|
|
801
|
+
if (Array.isArray(items)) {
|
|
802
|
+
items.forEach((item, index) => {
|
|
803
|
+
const contributionId = item.id || (item.command ? item.command : `${extension.id}.${pointId}.${index}`);
|
|
804
|
+
const contribution = {
|
|
805
|
+
id: contributionId,
|
|
806
|
+
metadata: {
|
|
807
|
+
extensionId: extension.id,
|
|
808
|
+
...item == null ? void 0 : item.metadata
|
|
809
|
+
},
|
|
810
|
+
data: item
|
|
811
|
+
};
|
|
812
|
+
const disposable = this.context.contributions.register(
|
|
813
|
+
pointId,
|
|
814
|
+
contribution
|
|
815
|
+
);
|
|
816
|
+
disposables.push(disposable);
|
|
817
|
+
const dispose = this.collectContribution(pointId, contribution);
|
|
818
|
+
if (dispose) {
|
|
819
|
+
disposables.push(dispose);
|
|
820
|
+
}
|
|
821
|
+
});
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
try {
|
|
826
|
+
this.extensionRegistry.set(extension.id, extension);
|
|
827
|
+
this.context.eventBus.emit("extension:register", extension);
|
|
828
|
+
} catch (error) {
|
|
829
|
+
console.error(
|
|
830
|
+
`Error in onCreate hook for plugin "${extension.id}":`,
|
|
831
|
+
error
|
|
832
|
+
);
|
|
833
|
+
}
|
|
834
|
+
try {
|
|
835
|
+
extension.activate(this.context);
|
|
836
|
+
} catch (error) {
|
|
837
|
+
console.error(
|
|
838
|
+
`Error in onActivate hook for plugin "${extension.id}":`,
|
|
839
|
+
error
|
|
840
|
+
);
|
|
841
|
+
}
|
|
842
|
+
console.log(`Plugin "${extension.id}" registered successfully`);
|
|
843
|
+
}
|
|
844
|
+
collectContribution(pointId, item) {
|
|
845
|
+
if (pointId === ContributionPointIds.CONFIGURATIONS) {
|
|
846
|
+
const configService = this.context.services.get(
|
|
847
|
+
CONFIGURATION_SERVICE
|
|
848
|
+
);
|
|
849
|
+
configService == null ? void 0 : configService.initializeDefaults([item.data]);
|
|
850
|
+
}
|
|
851
|
+
if (pointId === ContributionPointIds.COMMANDS && item.data.handler) {
|
|
852
|
+
const commandService = this.context.services.get(COMMAND_SERVICE);
|
|
853
|
+
return commandService.registerCommand(item.id, item.data.handler);
|
|
854
|
+
}
|
|
855
|
+
if (pointId === ContributionPointIds.TOOLS) {
|
|
856
|
+
const toolRegistry = this.context.services.get(
|
|
857
|
+
TOOL_REGISTRY_SERVICE
|
|
858
|
+
);
|
|
859
|
+
if (!toolRegistry) return;
|
|
860
|
+
return toolRegistry.registerTool(item.data);
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
unregister(name) {
|
|
864
|
+
const extension = this.extensionRegistry.get(name);
|
|
865
|
+
if (!extension) {
|
|
866
|
+
console.warn(`Plugin "${name}" not found.`);
|
|
867
|
+
return;
|
|
868
|
+
}
|
|
869
|
+
try {
|
|
870
|
+
extension.deactivate(this.context);
|
|
871
|
+
} catch (error) {
|
|
872
|
+
console.error(`Error in deactivate for plugin "${name}":`, error);
|
|
873
|
+
}
|
|
874
|
+
const disposables = this.extensionDisposables.get(name);
|
|
875
|
+
if (disposables) {
|
|
876
|
+
disposables.forEach((d) => d.dispose());
|
|
877
|
+
this.extensionDisposables.delete(name);
|
|
878
|
+
}
|
|
879
|
+
this.extensionRegistry.delete(name);
|
|
880
|
+
console.log(`Plugin "${name}" unregistered`);
|
|
881
|
+
return true;
|
|
882
|
+
}
|
|
883
|
+
enable(name) {
|
|
884
|
+
const extension = this.extensionRegistry.get(name);
|
|
885
|
+
if (!extension) {
|
|
886
|
+
console.warn(`Plugin "${name}" not found.`);
|
|
887
|
+
return;
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
disable(name) {
|
|
891
|
+
const extension = this.extensionRegistry.get(name);
|
|
892
|
+
if (!extension) {
|
|
893
|
+
console.warn(`Plugin "${name}" not found.`);
|
|
894
|
+
return;
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
update() {
|
|
898
|
+
}
|
|
899
|
+
destroy() {
|
|
900
|
+
const extensionNames = Array.from(this.extensionRegistry.keys());
|
|
901
|
+
extensionNames.forEach((name) => this.unregister(name));
|
|
902
|
+
}
|
|
736
903
|
};
|
|
737
904
|
|
|
738
905
|
// src/index.ts
|
|
@@ -740,27 +907,37 @@ var Pooder = class {
|
|
|
740
907
|
constructor() {
|
|
741
908
|
this.eventBus = new event_default();
|
|
742
909
|
this.services = new ServiceRegistry();
|
|
910
|
+
this.serviceContext = {
|
|
911
|
+
eventBus: this.eventBus,
|
|
912
|
+
get: (identifier) => this.services.get(identifier),
|
|
913
|
+
getOrThrow: (identifier, errorMessage) => this.services.getOrThrow(identifier, errorMessage),
|
|
914
|
+
has: (identifier) => this.services.has(identifier)
|
|
915
|
+
};
|
|
743
916
|
this.contributions = new ContributionRegistry();
|
|
744
917
|
this.initDefaultContributionPoints();
|
|
745
918
|
const commandService = new CommandService();
|
|
746
|
-
this.registerService(commandService,
|
|
919
|
+
this.registerService(commandService, CORE_SERVICE_TOKENS.COMMAND);
|
|
747
920
|
const configurationService = new ConfigurationService();
|
|
748
|
-
this.registerService(configurationService,
|
|
921
|
+
this.registerService(configurationService, CORE_SERVICE_TOKENS.CONFIGURATION);
|
|
749
922
|
const toolRegistryService = new ToolRegistryService();
|
|
750
|
-
this.registerService(toolRegistryService,
|
|
751
|
-
const toolSessionService = new ToolSessionService(
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
workbenchService
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
923
|
+
this.registerService(toolRegistryService, CORE_SERVICE_TOKENS.TOOL_REGISTRY);
|
|
924
|
+
const toolSessionService = new ToolSessionService({
|
|
925
|
+
commandService,
|
|
926
|
+
toolRegistry: toolRegistryService
|
|
927
|
+
});
|
|
928
|
+
this.registerService(toolSessionService, CORE_SERVICE_TOKENS.TOOL_SESSION);
|
|
929
|
+
const workbenchService = new WorkbenchService({
|
|
930
|
+
eventBus: this.eventBus,
|
|
931
|
+
toolRegistry: toolRegistryService,
|
|
932
|
+
sessionService: toolSessionService
|
|
933
|
+
});
|
|
934
|
+
this.registerService(workbenchService, CORE_SERVICE_TOKENS.WORKBENCH);
|
|
760
935
|
const context = {
|
|
761
936
|
eventBus: this.eventBus,
|
|
762
937
|
services: {
|
|
763
|
-
get: (
|
|
938
|
+
get: (identifier) => this.services.get(identifier),
|
|
939
|
+
getOrThrow: (identifier, errorMessage) => this.services.getOrThrow(identifier, errorMessage),
|
|
940
|
+
has: (identifier) => this.services.has(identifier)
|
|
764
941
|
},
|
|
765
942
|
contributions: {
|
|
766
943
|
get: (pointId) => this.getContributions(pointId),
|
|
@@ -792,38 +969,102 @@ var Pooder = class {
|
|
|
792
969
|
});
|
|
793
970
|
}
|
|
794
971
|
// --- Service Management ---
|
|
795
|
-
registerService(service,
|
|
796
|
-
|
|
797
|
-
const serviceId =
|
|
972
|
+
registerService(service, identifier, options = {}) {
|
|
973
|
+
const serviceIdentifier = this.resolveServiceIdentifier(service, identifier);
|
|
974
|
+
const serviceId = this.getServiceLabel(serviceIdentifier);
|
|
798
975
|
try {
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
976
|
+
const initResult = this.invokeServiceHook(service, "init");
|
|
977
|
+
if (this.isPromiseLike(initResult)) {
|
|
978
|
+
throw new Error(
|
|
979
|
+
`Service "${serviceId}" init() is async. Use registerServiceAsync() instead.`
|
|
980
|
+
);
|
|
981
|
+
}
|
|
982
|
+
this.services.register(serviceIdentifier, service, options);
|
|
983
|
+
this.eventBus.emit("service:register", service, { id: serviceId });
|
|
984
|
+
return true;
|
|
985
|
+
} catch (error) {
|
|
986
|
+
console.error(`Error initializing service ${serviceId}:`, error);
|
|
802
987
|
return false;
|
|
803
988
|
}
|
|
804
|
-
|
|
805
|
-
|
|
989
|
+
}
|
|
990
|
+
async registerServiceAsync(service, identifier, options = {}) {
|
|
991
|
+
const serviceIdentifier = this.resolveServiceIdentifier(service, identifier);
|
|
992
|
+
const serviceId = this.getServiceLabel(serviceIdentifier);
|
|
993
|
+
try {
|
|
994
|
+
await this.invokeServiceHookAsync(service, "init");
|
|
995
|
+
this.services.register(serviceIdentifier, service, options);
|
|
996
|
+
this.eventBus.emit("service:register", service, { id: serviceId });
|
|
997
|
+
return true;
|
|
998
|
+
} catch (error) {
|
|
999
|
+
console.error(`Error initializing service ${serviceId}:`, error);
|
|
1000
|
+
return false;
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
unregisterService(serviceOrIdentifier, id) {
|
|
1004
|
+
const resolvedIdentifier = this.resolveUnregisterIdentifier(
|
|
1005
|
+
serviceOrIdentifier,
|
|
1006
|
+
id
|
|
1007
|
+
);
|
|
1008
|
+
const serviceId = this.getServiceLabel(resolvedIdentifier);
|
|
1009
|
+
const registeredService = this.services.get(resolvedIdentifier);
|
|
1010
|
+
if (!registeredService) {
|
|
1011
|
+
console.warn(`Service ${serviceId} is not registered.`);
|
|
1012
|
+
return true;
|
|
1013
|
+
}
|
|
1014
|
+
try {
|
|
1015
|
+
const disposeResult = this.invokeServiceHook(registeredService, "dispose");
|
|
1016
|
+
if (this.isPromiseLike(disposeResult)) {
|
|
1017
|
+
throw new Error(
|
|
1018
|
+
`Service "${serviceId}" dispose() is async. Use unregisterServiceAsync() instead.`
|
|
1019
|
+
);
|
|
1020
|
+
}
|
|
1021
|
+
} catch (error) {
|
|
1022
|
+
console.error(`Error disposing service ${serviceId}:`, error);
|
|
1023
|
+
return false;
|
|
1024
|
+
}
|
|
1025
|
+
this.services.delete(resolvedIdentifier);
|
|
1026
|
+
this.eventBus.emit("service:unregister", registeredService, { id: serviceId });
|
|
806
1027
|
return true;
|
|
807
1028
|
}
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
1029
|
+
async unregisterServiceAsync(serviceOrIdentifier, id) {
|
|
1030
|
+
const resolvedIdentifier = this.resolveUnregisterIdentifier(
|
|
1031
|
+
serviceOrIdentifier,
|
|
1032
|
+
id
|
|
1033
|
+
);
|
|
1034
|
+
const serviceId = this.getServiceLabel(resolvedIdentifier);
|
|
1035
|
+
const registeredService = this.services.get(resolvedIdentifier);
|
|
1036
|
+
if (!registeredService) {
|
|
812
1037
|
console.warn(`Service ${serviceId} is not registered.`);
|
|
813
1038
|
return true;
|
|
814
1039
|
}
|
|
815
1040
|
try {
|
|
816
|
-
(
|
|
817
|
-
} catch (
|
|
818
|
-
console.error(`Error disposing service ${serviceId}:`,
|
|
1041
|
+
await this.invokeServiceHookAsync(registeredService, "dispose");
|
|
1042
|
+
} catch (error) {
|
|
1043
|
+
console.error(`Error disposing service ${serviceId}:`, error);
|
|
819
1044
|
return false;
|
|
820
1045
|
}
|
|
821
|
-
this.services.delete(
|
|
822
|
-
this.eventBus.emit("service:unregister",
|
|
1046
|
+
this.services.delete(resolvedIdentifier);
|
|
1047
|
+
this.eventBus.emit("service:unregister", registeredService, { id: serviceId });
|
|
823
1048
|
return true;
|
|
824
1049
|
}
|
|
825
|
-
getService(
|
|
826
|
-
return this.services.get(
|
|
1050
|
+
getService(identifier) {
|
|
1051
|
+
return this.services.get(identifier);
|
|
1052
|
+
}
|
|
1053
|
+
getServiceOrThrow(identifier, errorMessage) {
|
|
1054
|
+
return this.services.getOrThrow(identifier, errorMessage);
|
|
1055
|
+
}
|
|
1056
|
+
hasService(identifier) {
|
|
1057
|
+
return this.services.has(identifier);
|
|
1058
|
+
}
|
|
1059
|
+
async dispose() {
|
|
1060
|
+
var _a;
|
|
1061
|
+
this.extensionManager.destroy();
|
|
1062
|
+
const registrations = this.services.list().slice().reverse();
|
|
1063
|
+
for (const item of registrations) {
|
|
1064
|
+
const identifier = (_a = item.token) != null ? _a : item.id;
|
|
1065
|
+
await this.unregisterServiceAsync(identifier);
|
|
1066
|
+
}
|
|
1067
|
+
this.services.clear();
|
|
827
1068
|
}
|
|
828
1069
|
// --- Contribution Management ---
|
|
829
1070
|
registerContributionPoint(point) {
|
|
@@ -838,8 +1079,39 @@ var Pooder = class {
|
|
|
838
1079
|
getContributions(pointId) {
|
|
839
1080
|
return this.contributions.get(pointId);
|
|
840
1081
|
}
|
|
1082
|
+
resolveServiceIdentifier(service, identifier) {
|
|
1083
|
+
return identifier != null ? identifier : service.constructor.name;
|
|
1084
|
+
}
|
|
1085
|
+
resolveUnregisterIdentifier(serviceOrIdentifier, id) {
|
|
1086
|
+
if (typeof serviceOrIdentifier === "string" || isServiceToken(serviceOrIdentifier)) {
|
|
1087
|
+
return serviceOrIdentifier;
|
|
1088
|
+
}
|
|
1089
|
+
return id != null ? id : serviceOrIdentifier.constructor.name;
|
|
1090
|
+
}
|
|
1091
|
+
getServiceLabel(identifier) {
|
|
1092
|
+
if (typeof identifier === "string") {
|
|
1093
|
+
return identifier;
|
|
1094
|
+
}
|
|
1095
|
+
return identifier.name;
|
|
1096
|
+
}
|
|
1097
|
+
invokeServiceHook(service, hook) {
|
|
1098
|
+
const handler = service[hook];
|
|
1099
|
+
if (!handler) {
|
|
1100
|
+
return;
|
|
1101
|
+
}
|
|
1102
|
+
return handler.call(service, this.serviceContext);
|
|
1103
|
+
}
|
|
1104
|
+
async invokeServiceHookAsync(service, hook) {
|
|
1105
|
+
await this.invokeServiceHook(service, hook);
|
|
1106
|
+
}
|
|
1107
|
+
isPromiseLike(value) {
|
|
1108
|
+
return typeof value === "object" && value !== null && "then" in value && typeof value.then === "function";
|
|
1109
|
+
}
|
|
841
1110
|
};
|
|
842
1111
|
export {
|
|
1112
|
+
COMMAND_SERVICE,
|
|
1113
|
+
CONFIGURATION_SERVICE,
|
|
1114
|
+
CORE_SERVICE_TOKENS,
|
|
843
1115
|
CommandService,
|
|
844
1116
|
ConfigurationService,
|
|
845
1117
|
ContributionPointIds,
|
|
@@ -849,7 +1121,12 @@ export {
|
|
|
849
1121
|
ExtensionRegistry,
|
|
850
1122
|
Pooder,
|
|
851
1123
|
ServiceRegistry,
|
|
1124
|
+
TOOL_REGISTRY_SERVICE,
|
|
1125
|
+
TOOL_SESSION_SERVICE,
|
|
852
1126
|
ToolRegistryService,
|
|
853
1127
|
ToolSessionService,
|
|
854
|
-
|
|
1128
|
+
WORKBENCH_SERVICE,
|
|
1129
|
+
WorkbenchService,
|
|
1130
|
+
createServiceToken,
|
|
1131
|
+
isServiceToken
|
|
855
1132
|
};
|