@pooder/core 2.0.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/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.services = /* @__PURE__ */ new Map();
17
+ this.servicesByName = /* @__PURE__ */ new Map();
18
+ this.servicesByToken = /* @__PURE__ */ new Map();
19
+ this.registrationOrder = [];
5
20
  }
6
- register(name, service) {
7
- this.services.set(name, service);
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(serviceName) {
11
- return this.services.get(serviceName);
42
+ get(identifier) {
43
+ const normalized = this.normalizeIdentifier(identifier);
44
+ const entry = this.findEntry(normalized);
45
+ return entry == null ? void 0 : entry.service;
46
+ }
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.`);
12
54
  }
13
- has(serviceName) {
14
- return this.services.has(serviceName);
55
+ has(identifier) {
56
+ const normalized = this.normalizeIdentifier(identifier);
57
+ return Boolean(this.findEntry(normalized));
15
58
  }
16
- delete(serviceName) {
17
- this.services.delete(serviceName);
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;
67
+ }
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,122 +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
- }
249
- unregister(name) {
250
- const extension = this.extensionRegistry.get(name);
251
- if (!extension) {
252
- console.warn(`Plugin "${name}" not found.`);
253
- return;
254
- }
255
- try {
256
- extension.deactivate(this.context);
257
- } catch (error) {
258
- console.error(`Error in deactivate for plugin "${name}":`, error);
259
- }
260
- const disposables = this.extensionDisposables.get(name);
261
- if (disposables) {
262
- disposables.forEach((d) => d.dispose());
263
- this.extensionDisposables.delete(name);
264
- }
265
- this.extensionRegistry.delete(name);
266
- console.log(`Plugin "${name}" unregistered`);
267
- return true;
268
- }
269
- enable(name) {
270
- const extension = this.extensionRegistry.get(name);
271
- if (!extension) {
272
- console.warn(`Plugin "${name}" not found.`);
273
- return;
274
- }
275
- }
276
- disable(name) {
277
- const extension = this.extensionRegistry.get(name);
278
- if (!extension) {
279
- console.warn(`Plugin "${name}" not found.`);
280
- return;
281
- }
282
- }
283
- update() {
284
- }
285
- destroy() {
286
- const extensionNames = Array.from(this.extensionRegistry.keys());
287
- extensionNames.forEach((name) => this.unregister(name));
288
- }
289
- };
290
-
291
263
  // src/services/CommandService.ts
292
264
  var CommandService = class {
293
265
  constructor() {
@@ -447,27 +419,486 @@ var ConfigurationService = class {
447
419
  }
448
420
  };
449
421
 
422
+ // src/services/ToolRegistryService.ts
423
+ var ToolRegistryService = class {
424
+ constructor() {
425
+ this.tools = /* @__PURE__ */ new Map();
426
+ }
427
+ registerTool(tool) {
428
+ if (!(tool == null ? void 0 : tool.id)) {
429
+ throw new Error("ToolContribution.id is required.");
430
+ }
431
+ this.tools.set(tool.id, tool);
432
+ return {
433
+ dispose: () => {
434
+ if (this.tools.get(tool.id) === tool) {
435
+ this.tools.delete(tool.id);
436
+ }
437
+ }
438
+ };
439
+ }
440
+ unregisterTool(toolId) {
441
+ this.tools.delete(toolId);
442
+ }
443
+ getTool(toolId) {
444
+ return this.tools.get(toolId);
445
+ }
446
+ listTools() {
447
+ return Array.from(this.tools.values());
448
+ }
449
+ hasTool(toolId) {
450
+ return this.tools.has(toolId);
451
+ }
452
+ dispose() {
453
+ this.tools.clear();
454
+ }
455
+ };
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
+
475
+ // src/services/ToolSessionService.ts
476
+ var ToolSessionService = class {
477
+ constructor(dependencies = {}) {
478
+ this.sessions = /* @__PURE__ */ new Map();
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
+ }
493
+ }
494
+ setCommandService(commandService) {
495
+ this.commandService = commandService;
496
+ }
497
+ setToolRegistry(toolRegistry) {
498
+ this.toolRegistry = toolRegistry;
499
+ }
500
+ registerDirtyTracker(toolId, callback) {
501
+ const wrapped = () => {
502
+ try {
503
+ return callback();
504
+ } catch (e) {
505
+ return false;
506
+ }
507
+ };
508
+ this.dirtyTrackers.set(toolId, wrapped);
509
+ return {
510
+ dispose: () => {
511
+ if (this.dirtyTrackers.get(toolId) === wrapped) {
512
+ this.dirtyTrackers.delete(toolId);
513
+ }
514
+ }
515
+ };
516
+ }
517
+ ensureSession(toolId) {
518
+ const existing = this.sessions.get(toolId);
519
+ if (existing) return existing;
520
+ const created = {
521
+ toolId,
522
+ status: "idle",
523
+ dirty: false
524
+ };
525
+ this.sessions.set(toolId, created);
526
+ return created;
527
+ }
528
+ getState(toolId) {
529
+ return { ...this.ensureSession(toolId) };
530
+ }
531
+ isDirty(toolId) {
532
+ const tracker = this.dirtyTrackers.get(toolId);
533
+ if (tracker) return tracker();
534
+ return this.ensureSession(toolId).dirty;
535
+ }
536
+ markDirty(toolId, dirty = true) {
537
+ const session = this.ensureSession(toolId);
538
+ session.dirty = dirty;
539
+ session.lastUpdatedAt = Date.now();
540
+ }
541
+ resolveTool(toolId) {
542
+ return this.getToolRegistry().getTool(toolId);
543
+ }
544
+ async runCommand(commandId, ...args) {
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;
559
+ }
560
+ async begin(toolId) {
561
+ var _a;
562
+ const tool = this.resolveTool(toolId);
563
+ const session = this.ensureSession(toolId);
564
+ if (session.status === "active") return;
565
+ await this.runCommand((_a = tool == null ? void 0 : tool.commands) == null ? void 0 : _a.begin);
566
+ session.status = "active";
567
+ session.startedAt = Date.now();
568
+ session.lastUpdatedAt = session.startedAt;
569
+ }
570
+ async validate(toolId) {
571
+ var _a;
572
+ const tool = this.resolveTool(toolId);
573
+ if (!((_a = tool == null ? void 0 : tool.commands) == null ? void 0 : _a.validate)) {
574
+ return { ok: true };
575
+ }
576
+ const result = await this.runCommand(tool.commands.validate);
577
+ if (result === false) return { ok: false, result };
578
+ if (result && typeof result === "object" && "ok" in result) {
579
+ return { ok: Boolean(result.ok), result };
580
+ }
581
+ return { ok: true, result };
582
+ }
583
+ async commit(toolId) {
584
+ var _a;
585
+ const tool = this.resolveTool(toolId);
586
+ const validateResult = await this.validate(toolId);
587
+ if (!validateResult.ok) return validateResult;
588
+ const result = await this.runCommand((_a = tool == null ? void 0 : tool.commands) == null ? void 0 : _a.commit);
589
+ const session = this.ensureSession(toolId);
590
+ session.dirty = false;
591
+ session.status = "idle";
592
+ session.lastUpdatedAt = Date.now();
593
+ return { ok: true, result };
594
+ }
595
+ async rollback(toolId) {
596
+ var _a, _b;
597
+ const tool = this.resolveTool(toolId);
598
+ await this.runCommand(((_a = tool == null ? void 0 : tool.commands) == null ? void 0 : _a.rollback) || ((_b = tool == null ? void 0 : tool.commands) == null ? void 0 : _b.reset));
599
+ const session = this.ensureSession(toolId);
600
+ session.dirty = false;
601
+ session.status = "idle";
602
+ session.lastUpdatedAt = Date.now();
603
+ }
604
+ deactivateSession(toolId) {
605
+ const session = this.ensureSession(toolId);
606
+ session.status = "idle";
607
+ session.lastUpdatedAt = Date.now();
608
+ }
609
+ async handleBeforeLeave(toolId) {
610
+ var _a, _b;
611
+ const tool = this.resolveTool(toolId);
612
+ if (!tool) return { decision: "allow" };
613
+ if (tool.interaction !== "session") return { decision: "allow" };
614
+ const dirty = this.isDirty(toolId);
615
+ if (!dirty) return { decision: "allow" };
616
+ const leavePolicy = (_b = (_a = tool.session) == null ? void 0 : _a.leavePolicy) != null ? _b : "block";
617
+ if (leavePolicy === "commit") {
618
+ const committed = await this.commit(toolId);
619
+ if (!committed.ok) {
620
+ return { decision: "blocked", reason: "session-validation-failed" };
621
+ }
622
+ return { decision: "allow" };
623
+ }
624
+ if (leavePolicy === "rollback") {
625
+ await this.rollback(toolId);
626
+ return { decision: "allow" };
627
+ }
628
+ return { decision: "blocked", reason: "session-dirty" };
629
+ }
630
+ dispose() {
631
+ this.sessions.clear();
632
+ this.dirtyTrackers.clear();
633
+ }
634
+ };
635
+
450
636
  // src/services/WorkbenchService.ts
451
637
  var WorkbenchService = class {
452
- constructor() {
638
+ constructor(dependencies = {}) {
453
639
  this._activeToolId = null;
640
+ this.guards = [];
641
+ this.eventBus = dependencies.eventBus;
642
+ this.toolRegistry = dependencies.toolRegistry;
643
+ this.sessionService = dependencies.sessionService;
454
644
  }
455
- init() {
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
+ }
456
659
  }
457
660
  dispose() {
661
+ this.guards = [];
458
662
  }
459
663
  setEventBus(bus) {
460
664
  this.eventBus = bus;
461
665
  }
666
+ setToolRegistry(toolRegistry) {
667
+ this.toolRegistry = toolRegistry;
668
+ }
669
+ setToolSessionService(sessionService) {
670
+ this.sessionService = sessionService;
671
+ }
462
672
  get activeToolId() {
463
673
  return this._activeToolId;
464
674
  }
465
- activate(id) {
675
+ registerSwitchGuard(guard, priority = 0) {
676
+ const item = { guard, priority };
677
+ this.guards.push(item);
678
+ this.guards.sort((a, b) => b.priority - a.priority);
679
+ return {
680
+ dispose: () => {
681
+ const index = this.guards.indexOf(item);
682
+ if (index >= 0) this.guards.splice(index, 1);
683
+ }
684
+ };
685
+ }
686
+ async runGuards(context) {
687
+ for (const { guard } of this.guards) {
688
+ const allowed = await Promise.resolve(guard(context));
689
+ if (!allowed) return false;
690
+ }
691
+ return true;
692
+ }
693
+ async switchTool(id, options) {
466
694
  var _a;
467
- if (this._activeToolId === id) return;
695
+ const eventBus = this.getEventBus();
696
+ const toolRegistry = this.getToolRegistry();
697
+ const sessionService = this.getSessionService();
698
+ if (this._activeToolId === id) {
699
+ return { ok: true, from: this._activeToolId, to: id };
700
+ }
701
+ if (id && !toolRegistry.hasTool(id)) {
702
+ return {
703
+ ok: false,
704
+ from: this._activeToolId,
705
+ to: id,
706
+ reason: `tool-not-registered:${id}`
707
+ };
708
+ }
709
+ const context = {
710
+ from: this._activeToolId,
711
+ to: id,
712
+ reason: options == null ? void 0 : options.reason
713
+ };
714
+ const guardAllowed = await this.runGuards(context);
715
+ if (!guardAllowed) {
716
+ eventBus.emit("tool:switch:blocked", {
717
+ ...context,
718
+ reason: "blocked-by-guard"
719
+ });
720
+ return {
721
+ ok: false,
722
+ from: this._activeToolId,
723
+ to: id,
724
+ reason: "blocked-by-guard"
725
+ };
726
+ }
727
+ if (context.from) {
728
+ const leaveResult = await sessionService.handleBeforeLeave(context.from);
729
+ if (leaveResult.decision === "blocked") {
730
+ eventBus.emit("tool:switch:blocked", {
731
+ ...context,
732
+ reason: leaveResult.reason || "session-blocked"
733
+ });
734
+ return {
735
+ ok: false,
736
+ from: this._activeToolId,
737
+ to: id,
738
+ reason: leaveResult.reason || "session-blocked"
739
+ };
740
+ }
741
+ sessionService.deactivateSession(context.from);
742
+ }
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);
747
+ }
748
+ }
468
749
  const previous = this._activeToolId;
469
750
  this._activeToolId = id;
470
- (_a = this.eventBus) == null ? void 0 : _a.emit("tool:activated", { id, previous });
751
+ const reason = options == null ? void 0 : options.reason;
752
+ eventBus.emit("tool:activated", { id, previous, reason });
753
+ eventBus.emit("tool:switch", { from: previous, to: id, reason });
754
+ return { ok: true, from: previous, to: id };
755
+ }
756
+ async activate(id) {
757
+ return await this.switchTool(id, { reason: "activate" });
758
+ }
759
+ async deactivate() {
760
+ return await this.switchTool(null, { reason: "deactivate" });
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));
471
902
  }
472
903
  };
473
904
 
@@ -476,19 +907,37 @@ var Pooder = class {
476
907
  constructor() {
477
908
  this.eventBus = new event_default();
478
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
+ };
479
916
  this.contributions = new ContributionRegistry();
480
917
  this.initDefaultContributionPoints();
481
918
  const commandService = new CommandService();
482
- this.registerService(commandService, "CommandService");
919
+ this.registerService(commandService, CORE_SERVICE_TOKENS.COMMAND);
483
920
  const configurationService = new ConfigurationService();
484
- this.registerService(configurationService, "ConfigurationService");
485
- const workbenchService = new WorkbenchService();
486
- workbenchService.setEventBus(this.eventBus);
487
- this.registerService(workbenchService, "WorkbenchService");
921
+ this.registerService(configurationService, CORE_SERVICE_TOKENS.CONFIGURATION);
922
+ const toolRegistryService = new ToolRegistryService();
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);
488
935
  const context = {
489
936
  eventBus: this.eventBus,
490
937
  services: {
491
- get: (serviceName) => this.services.get(serviceName)
938
+ get: (identifier) => this.services.get(identifier),
939
+ getOrThrow: (identifier, errorMessage) => this.services.getOrThrow(identifier, errorMessage),
940
+ has: (identifier) => this.services.has(identifier)
492
941
  },
493
942
  contributions: {
494
943
  get: (pointId) => this.getContributions(pointId),
@@ -520,38 +969,102 @@ var Pooder = class {
520
969
  });
521
970
  }
522
971
  // --- Service Management ---
523
- registerService(service, id) {
524
- var _a;
525
- const serviceId = id || service.constructor.name;
972
+ registerService(service, identifier, options = {}) {
973
+ const serviceIdentifier = this.resolveServiceIdentifier(service, identifier);
974
+ const serviceId = this.getServiceLabel(serviceIdentifier);
526
975
  try {
527
- (_a = service == null ? void 0 : service.init) == null ? void 0 : _a.call(service);
528
- } catch (e) {
529
- console.error(`Error initializing service ${serviceId}:`, e);
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);
987
+ return false;
988
+ }
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);
530
1000
  return false;
531
1001
  }
532
- this.services.register(serviceId, service);
533
- this.eventBus.emit("service:register", service);
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 });
534
1027
  return true;
535
1028
  }
536
- unregisterService(service, id) {
537
- var _a;
538
- const serviceId = id || service.constructor.name;
539
- if (!this.services.has(serviceId)) {
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) {
540
1037
  console.warn(`Service ${serviceId} is not registered.`);
541
1038
  return true;
542
1039
  }
543
1040
  try {
544
- (_a = service == null ? void 0 : service.dispose) == null ? void 0 : _a.call(service);
545
- } catch (e) {
546
- console.error(`Error disposing service ${serviceId}:`, e);
1041
+ await this.invokeServiceHookAsync(registeredService, "dispose");
1042
+ } catch (error) {
1043
+ console.error(`Error disposing service ${serviceId}:`, error);
547
1044
  return false;
548
1045
  }
549
- this.services.delete(serviceId);
550
- this.eventBus.emit("service:unregister", service);
1046
+ this.services.delete(resolvedIdentifier);
1047
+ this.eventBus.emit("service:unregister", registeredService, { id: serviceId });
551
1048
  return true;
552
1049
  }
553
- getService(id) {
554
- return this.services.get(id);
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();
555
1068
  }
556
1069
  // --- Contribution Management ---
557
1070
  registerContributionPoint(point) {
@@ -566,8 +1079,39 @@ var Pooder = class {
566
1079
  getContributions(pointId) {
567
1080
  return this.contributions.get(pointId);
568
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
+ }
569
1110
  };
570
1111
  export {
1112
+ COMMAND_SERVICE,
1113
+ CONFIGURATION_SERVICE,
1114
+ CORE_SERVICE_TOKENS,
571
1115
  CommandService,
572
1116
  ConfigurationService,
573
1117
  ContributionPointIds,
@@ -577,5 +1121,12 @@ export {
577
1121
  ExtensionRegistry,
578
1122
  Pooder,
579
1123
  ServiceRegistry,
580
- WorkbenchService
1124
+ TOOL_REGISTRY_SERVICE,
1125
+ TOOL_SESSION_SERVICE,
1126
+ ToolRegistryService,
1127
+ ToolSessionService,
1128
+ WORKBENCH_SERVICE,
1129
+ WorkbenchService,
1130
+ createServiceToken,
1131
+ isServiceToken
581
1132
  };