@pooder/core 2.0.0 → 2.1.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/src/index.ts CHANGED
@@ -1,145 +1,161 @@
1
- import { Service, ServiceRegistry } from "./service";
2
- import EventBus from "./event";
3
- import { ExtensionManager } from "./extension";
4
- import Disposable from "./disposable";
5
- import {
6
- Contribution,
7
- ContributionPoint,
8
- ContributionPointIds,
9
- ContributionRegistry,
10
- } from "./contribution";
11
- import { CommandService, ConfigurationService, WorkbenchService } from "./services";
12
- import { ExtensionContext } from "./context";
13
-
14
- export * from "./extension";
15
- export * from "./context";
16
- export * from "./contribution";
17
- export * from "./service";
18
- export * from "./services";
19
- export { default as EventBus } from "./event";
20
-
21
- export class Pooder {
22
- readonly eventBus: EventBus = new EventBus();
23
- private readonly services: ServiceRegistry = new ServiceRegistry();
24
- private readonly contributions: ContributionRegistry =
25
- new ContributionRegistry();
26
- readonly extensionManager: ExtensionManager;
27
-
28
- constructor() {
29
- // Initialize default contribution points
30
- this.initDefaultContributionPoints();
31
-
32
- const commandService = new CommandService();
33
- this.registerService(commandService, "CommandService");
34
-
35
- const configurationService = new ConfigurationService();
36
- this.registerService(configurationService, "ConfigurationService");
37
-
38
- const workbenchService = new WorkbenchService();
39
- workbenchService.setEventBus(this.eventBus);
40
- this.registerService(workbenchService, "WorkbenchService");
41
-
42
- // Create a restricted context for extensions
43
- const context: ExtensionContext = {
44
- eventBus: this.eventBus,
45
- services: {
46
- get: <T extends Service>(serviceName: string) =>
47
- this.services.get<T>(serviceName),
48
- },
49
- contributions: {
50
- get: <T>(pointId: string) => this.getContributions<T>(pointId),
51
- register: <T>(pointId: string, contribution: Contribution<T>) =>
52
- this.registerContribution(pointId, contribution),
53
- },
54
- };
55
-
56
- this.extensionManager = new ExtensionManager(context);
57
- }
58
-
59
- private initDefaultContributionPoints() {
60
- this.registerContributionPoint({
61
- id: ContributionPointIds.CONTRIBUTIONS,
62
- description: "Contribution point for contribution points",
63
- });
64
-
65
- this.registerContributionPoint({
66
- id: ContributionPointIds.COMMANDS,
67
- description: "Contribution point for commands",
68
- });
69
-
70
- this.registerContributionPoint({
71
- id: ContributionPointIds.TOOLS,
72
- description: "Contribution point for tools",
73
- });
74
-
75
- this.registerContributionPoint({
76
- id: ContributionPointIds.VIEWS,
77
- description: "Contribution point for UI views",
78
- });
79
-
80
- this.registerContributionPoint({
81
- id: ContributionPointIds.CONFIGURATIONS,
82
- description: "Contribution point for configurations",
83
- });
84
- }
85
-
86
- // --- Service Management ---
87
-
88
- registerService(service: Service, id?: string) {
89
- const serviceId = id || service.constructor.name;
90
-
91
- try {
92
- service?.init?.();
93
- } catch (e) {
94
- console.error(`Error initializing service ${serviceId}:`, e);
95
- return false;
96
- }
97
-
98
- this.services.register(serviceId, service);
99
- this.eventBus.emit("service:register", service);
100
- return true;
101
- }
102
-
103
- unregisterService(service: Service, id?: string) {
104
- const serviceId = id || service.constructor.name;
105
- if (!this.services.has(serviceId)) {
106
- console.warn(`Service ${serviceId} is not registered.`);
107
- return true;
108
- }
109
-
110
- try {
111
- service?.dispose?.();
112
- } catch (e) {
113
- console.error(`Error disposing service ${serviceId}:`, e);
114
- return false;
115
- }
116
-
117
- this.services.delete(serviceId);
118
- this.eventBus.emit("service:unregister", service);
119
- return true;
120
- }
121
-
122
- getService<T extends Service>(id: string): T | undefined {
123
- return this.services.get<T>(id);
124
- }
125
-
126
- // --- Contribution Management ---
127
-
128
- registerContributionPoint<T>(point: ContributionPoint<T>): void {
129
- this.contributions.registerPoint(point);
130
- this.eventBus.emit("contribution:point:register", point);
131
- }
132
-
133
- registerContribution<T>(
134
- pointId: string,
135
- contribution: Contribution<T>,
136
- ): Disposable {
137
- const disposable = this.contributions.register(pointId, contribution);
138
- this.eventBus.emit("contribution:register", { ...contribution, pointId });
139
- return disposable;
140
- }
141
-
142
- getContributions<T>(pointId: string): Contribution<T>[] {
143
- return this.contributions.get<T>(pointId);
144
- }
145
- }
1
+ import { Service, ServiceRegistry } from "./service";
2
+ import EventBus from "./event";
3
+ import { ExtensionManager } from "./extension";
4
+ import Disposable from "./disposable";
5
+ import {
6
+ Contribution,
7
+ ContributionPoint,
8
+ ContributionPointIds,
9
+ ContributionRegistry,
10
+ } from "./contribution";
11
+ import {
12
+ CommandService,
13
+ ConfigurationService,
14
+ ToolRegistryService,
15
+ ToolSessionService,
16
+ WorkbenchService,
17
+ } from "./services";
18
+ import { ExtensionContext } from "./context";
19
+
20
+ export * from "./extension";
21
+ export * from "./context";
22
+ export * from "./contribution";
23
+ export * from "./service";
24
+ export * from "./services";
25
+ export { default as EventBus } from "./event";
26
+
27
+ export class Pooder {
28
+ readonly eventBus: EventBus = new EventBus();
29
+ private readonly services: ServiceRegistry = new ServiceRegistry();
30
+ private readonly contributions: ContributionRegistry =
31
+ new ContributionRegistry();
32
+ readonly extensionManager: ExtensionManager;
33
+
34
+ constructor() {
35
+ // Initialize default contribution points
36
+ this.initDefaultContributionPoints();
37
+
38
+ const commandService = new CommandService();
39
+ this.registerService(commandService, "CommandService");
40
+
41
+ const configurationService = new ConfigurationService();
42
+ this.registerService(configurationService, "ConfigurationService");
43
+
44
+ const toolRegistryService = new ToolRegistryService();
45
+ this.registerService(toolRegistryService, "ToolRegistryService");
46
+
47
+ const toolSessionService = new ToolSessionService();
48
+ toolSessionService.setCommandService(commandService);
49
+ toolSessionService.setToolRegistry(toolRegistryService);
50
+ this.registerService(toolSessionService, "ToolSessionService");
51
+
52
+ const workbenchService = new WorkbenchService();
53
+ workbenchService.setEventBus(this.eventBus);
54
+ workbenchService.setToolRegistry(toolRegistryService);
55
+ workbenchService.setToolSessionService(toolSessionService);
56
+ this.registerService(workbenchService, "WorkbenchService");
57
+
58
+ // Create a restricted context for extensions
59
+ const context: ExtensionContext = {
60
+ eventBus: this.eventBus,
61
+ services: {
62
+ get: <T extends Service>(serviceName: string) =>
63
+ this.services.get<T>(serviceName),
64
+ },
65
+ contributions: {
66
+ get: <T>(pointId: string) => this.getContributions<T>(pointId),
67
+ register: <T>(pointId: string, contribution: Contribution<T>) =>
68
+ this.registerContribution(pointId, contribution),
69
+ },
70
+ };
71
+
72
+ this.extensionManager = new ExtensionManager(context);
73
+ }
74
+
75
+ private initDefaultContributionPoints() {
76
+ this.registerContributionPoint({
77
+ id: ContributionPointIds.CONTRIBUTIONS,
78
+ description: "Contribution point for contribution points",
79
+ });
80
+
81
+ this.registerContributionPoint({
82
+ id: ContributionPointIds.COMMANDS,
83
+ description: "Contribution point for commands",
84
+ });
85
+
86
+ this.registerContributionPoint({
87
+ id: ContributionPointIds.TOOLS,
88
+ description: "Contribution point for tools",
89
+ });
90
+
91
+ this.registerContributionPoint({
92
+ id: ContributionPointIds.VIEWS,
93
+ description: "Contribution point for UI views",
94
+ });
95
+
96
+ this.registerContributionPoint({
97
+ id: ContributionPointIds.CONFIGURATIONS,
98
+ description: "Contribution point for configurations",
99
+ });
100
+ }
101
+
102
+ // --- Service Management ---
103
+
104
+ registerService(service: Service, id?: string) {
105
+ const serviceId = id || service.constructor.name;
106
+
107
+ try {
108
+ service?.init?.();
109
+ } catch (e) {
110
+ console.error(`Error initializing service ${serviceId}:`, e);
111
+ return false;
112
+ }
113
+
114
+ this.services.register(serviceId, service);
115
+ this.eventBus.emit("service:register", service);
116
+ return true;
117
+ }
118
+
119
+ unregisterService(service: Service, id?: string) {
120
+ const serviceId = id || service.constructor.name;
121
+ if (!this.services.has(serviceId)) {
122
+ console.warn(`Service ${serviceId} is not registered.`);
123
+ return true;
124
+ }
125
+
126
+ try {
127
+ service?.dispose?.();
128
+ } catch (e) {
129
+ console.error(`Error disposing service ${serviceId}:`, e);
130
+ return false;
131
+ }
132
+
133
+ this.services.delete(serviceId);
134
+ this.eventBus.emit("service:unregister", service);
135
+ return true;
136
+ }
137
+
138
+ getService<T extends Service>(id: string): T | undefined {
139
+ return this.services.get<T>(id);
140
+ }
141
+
142
+ // --- Contribution Management ---
143
+
144
+ registerContributionPoint<T>(point: ContributionPoint<T>): void {
145
+ this.contributions.registerPoint(point);
146
+ this.eventBus.emit("contribution:point:register", point);
147
+ }
148
+
149
+ registerContribution<T>(
150
+ pointId: string,
151
+ contribution: Contribution<T>,
152
+ ): Disposable {
153
+ const disposable = this.contributions.register(pointId, contribution);
154
+ this.eventBus.emit("contribution:register", { ...contribution, pointId });
155
+ return disposable;
156
+ }
157
+
158
+ getContributions<T>(pointId: string): Contribution<T>[] {
159
+ return this.contributions.get<T>(pointId);
160
+ }
161
+ }
@@ -1,98 +1,98 @@
1
- import { Pooder } from "./index";
2
- import { fullExtension } from "./test-extension-full";
3
- import { ContributionPointIds } from "./contribution";
4
- import CommandService from "./services/CommandService";
5
-
6
- async function runTest() {
7
- console.log("Starting Test...");
8
- const app = new Pooder();
9
-
10
- // Register the full extension
11
- console.log("Registering extension...");
12
- app.extensionManager.register(fullExtension);
13
- const commandService = app.getService<CommandService>("CommandService")!;
14
-
15
- // 1. Verify Command Contributions
16
- console.log("\n--- Verifying Commands ---");
17
-
18
- // 1.1 Imperative Command
19
- try {
20
- const res = await commandService.executeCommand(
21
- "test.imperative.hello",
22
- "User",
23
- );
24
- console.log(
25
- `[Imperative] "test.imperative.hello" result: "${res}"` ===
26
- `[Imperative] "test.imperative.hello" result: "Hello Imperative User"`
27
- ? "✅ PASS"
28
- : "❌ FAIL",
29
- );
30
- } catch (e) {
31
- console.error("❌ FAIL: Imperative command failed", e);
32
- }
33
-
34
- // 1.2 Declarative Auto-Registered Command
35
- try {
36
- const res = await commandService.executeCommand("test.declarative.auto");
37
- console.log(
38
- `[Declarative] "test.declarative.auto" result: "${res}"` ===
39
- `[Declarative] "test.declarative.auto" result: "Auto Registered Result"`
40
- ? "✅ PASS"
41
- : "❌ FAIL",
42
- );
43
- } catch (e) {
44
- console.error("❌ FAIL: Declarative auto-registered command failed", e);
45
- }
46
-
47
- // 2. Verify Registry Entries
48
- console.log("\n--- Verifying Contribution Registry ---");
49
-
50
- const commands = app.getContributions(ContributionPointIds.COMMANDS);
51
- console.log(
52
- `Commands registered: ${commands.length}` === "Commands registered: 2"
53
- ? "✅ PASS (2 commands found)"
54
- : `❌ FAIL (${commands.length} commands found)`,
55
- );
56
-
57
- const tools = app.getContributions(ContributionPointIds.TOOLS);
58
- console.log(
59
- `Tools registered: ${tools.length}` === "Tools registered: 1"
60
- ? "✅ PASS (1 tool found)"
61
- : `❌ FAIL (${tools.length} tools found)`,
62
- );
63
-
64
- const views = app.getContributions(ContributionPointIds.VIEWS);
65
- console.log(
66
- `Views registered: ${views.length}` === "Views registered: 1"
67
- ? "✅ PASS (1 view found)"
68
- : `❌ FAIL (${views.length} views found)`,
69
- );
70
-
71
- // 3. Unregister and Verify Cleanup
72
- console.log("\n--- Verifying Unregistration/Cleanup ---");
73
- // The ID is now explicit: "full-feature-test-extension"
74
- const extensionId = "full-feature-test-extension";
75
- app.extensionManager.unregister(extensionId);
76
-
77
- const commandsAfter = app.getContributions(ContributionPointIds.COMMANDS);
78
- console.log(
79
- `Commands after unregister: ${commandsAfter.length}` ===
80
- "Commands after unregister: 0"
81
- ? "✅ PASS"
82
- : `❌ FAIL (${commandsAfter.length} left)`,
83
- );
84
-
85
- // Verify command service cleanup
86
- const cmdMap = commandService.getCommands();
87
- const hasCmd = cmdMap.has("test.declarative.auto");
88
- console.log(
89
- `Command service cleaned up: ${!hasCmd}` ===
90
- "Command service cleaned up: true"
91
- ? "✅ PASS"
92
- : "❌ FAIL (Command still exists)",
93
- );
94
-
95
- console.log("\nTest Completed.");
96
- }
97
-
98
- runTest().catch(console.error);
1
+ import { Pooder } from "./index";
2
+ import { fullExtension } from "./test-extension-full";
3
+ import { ContributionPointIds } from "./contribution";
4
+ import CommandService from "./services/CommandService";
5
+
6
+ async function runTest() {
7
+ console.log("Starting Test...");
8
+ const app = new Pooder();
9
+
10
+ // Register the full extension
11
+ console.log("Registering extension...");
12
+ app.extensionManager.register(fullExtension);
13
+ const commandService = app.getService<CommandService>("CommandService")!;
14
+
15
+ // 1. Verify Command Contributions
16
+ console.log("\n--- Verifying Commands ---");
17
+
18
+ // 1.1 Imperative Command
19
+ try {
20
+ const res = await commandService.executeCommand(
21
+ "test.imperative.hello",
22
+ "User",
23
+ );
24
+ console.log(
25
+ `[Imperative] "test.imperative.hello" result: "${res}"` ===
26
+ `[Imperative] "test.imperative.hello" result: "Hello Imperative User"`
27
+ ? "✅ PASS"
28
+ : "❌ FAIL",
29
+ );
30
+ } catch (e) {
31
+ console.error("❌ FAIL: Imperative command failed", e);
32
+ }
33
+
34
+ // 1.2 Declarative Auto-Registered Command
35
+ try {
36
+ const res = await commandService.executeCommand("test.declarative.auto");
37
+ console.log(
38
+ `[Declarative] "test.declarative.auto" result: "${res}"` ===
39
+ `[Declarative] "test.declarative.auto" result: "Auto Registered Result"`
40
+ ? "✅ PASS"
41
+ : "❌ FAIL",
42
+ );
43
+ } catch (e) {
44
+ console.error("❌ FAIL: Declarative auto-registered command failed", e);
45
+ }
46
+
47
+ // 2. Verify Registry Entries
48
+ console.log("\n--- Verifying Contribution Registry ---");
49
+
50
+ const commands = app.getContributions(ContributionPointIds.COMMANDS);
51
+ console.log(
52
+ `Commands registered: ${commands.length}` === "Commands registered: 2"
53
+ ? "✅ PASS (2 commands found)"
54
+ : `❌ FAIL (${commands.length} commands found)`,
55
+ );
56
+
57
+ const tools = app.getContributions(ContributionPointIds.TOOLS);
58
+ console.log(
59
+ `Tools registered: ${tools.length}` === "Tools registered: 1"
60
+ ? "✅ PASS (1 tool found)"
61
+ : `❌ FAIL (${tools.length} tools found)`,
62
+ );
63
+
64
+ const views = app.getContributions(ContributionPointIds.VIEWS);
65
+ console.log(
66
+ `Views registered: ${views.length}` === "Views registered: 1"
67
+ ? "✅ PASS (1 view found)"
68
+ : `❌ FAIL (${views.length} views found)`,
69
+ );
70
+
71
+ // 3. Unregister and Verify Cleanup
72
+ console.log("\n--- Verifying Unregistration/Cleanup ---");
73
+ // The ID is now explicit: "full-feature-test-extension"
74
+ const extensionId = "full-feature-test-extension";
75
+ app.extensionManager.unregister(extensionId);
76
+
77
+ const commandsAfter = app.getContributions(ContributionPointIds.COMMANDS);
78
+ console.log(
79
+ `Commands after unregister: ${commandsAfter.length}` ===
80
+ "Commands after unregister: 0"
81
+ ? "✅ PASS"
82
+ : `❌ FAIL (${commandsAfter.length} left)`,
83
+ );
84
+
85
+ // Verify command service cleanup
86
+ const cmdMap = commandService.getCommands();
87
+ const hasCmd = cmdMap.has("test.declarative.auto");
88
+ console.log(
89
+ `Command service cleaned up: ${!hasCmd}` ===
90
+ "Command service cleaned up: true"
91
+ ? "✅ PASS"
92
+ : "❌ FAIL (Command still exists)",
93
+ );
94
+
95
+ console.log("\nTest Completed.");
96
+ }
97
+
98
+ runTest().catch(console.error);
@@ -1,79 +1,79 @@
1
- import { Command } from "../command";
2
- import Disposable from "../disposable";
3
- import { Service } from "../service";
4
-
5
- export default class CommandService implements Service {
6
- private commands: Map<string, Command> = new Map();
7
-
8
- /**
9
- * Register a command
10
- * @param commandId Command Name (ID)
11
- * @param handler Command handler function
12
- * @param thisArg The `this` context for the handler
13
- * @returns Disposable to unregister the command
14
- */
15
- registerCommand(
16
- commandId: string,
17
- handler: (...args: any[]) => any,
18
- thisArg?: any,
19
- ): Disposable {
20
- if (this.commands.has(commandId)) {
21
- console.warn(
22
- `Command "${commandId}" is already registered. Overwriting.`,
23
- );
24
- }
25
-
26
- const command: Command = {
27
- id: commandId,
28
- handler: thisArg ? handler.bind(thisArg) : handler,
29
- };
30
-
31
- this.commands.set(commandId, command);
32
-
33
- return {
34
- dispose: () => {
35
- if (this.commands.get(commandId) === command) {
36
- this.commands.delete(commandId);
37
- }
38
- },
39
- };
40
- }
41
-
42
- /**
43
- * Execute a command
44
- * @param commandId Command Name (ID)
45
- * @param args Arguments to pass to the handler
46
- * @returns The result of the command handler
47
- */
48
- async executeCommand<T = any>(commandId: string, ...args: any[]): Promise<T> {
49
- const command = this.commands.get(commandId);
50
- if (!command) {
51
- throw new Error(`Command "${commandId}" not found.`);
52
- }
53
-
54
- try {
55
- return await command.handler(...args);
56
- } catch (error) {
57
- console.error(`Error executing command "${commandId}":`, error);
58
- throw error;
59
- }
60
- }
61
-
62
- /**
63
- * Get all registered commands
64
- */
65
- getCommands(): Map<string, Command> {
66
- return this.commands;
67
- }
68
-
69
- /**
70
- * Get a specific command
71
- */
72
- getCommand(commandId: string): Command | undefined {
73
- return this.commands.get(commandId);
74
- }
75
-
76
- dispose() {
77
- this.commands.clear();
78
- }
79
- }
1
+ import { Command } from "../command";
2
+ import Disposable from "../disposable";
3
+ import { Service } from "../service";
4
+
5
+ export default class CommandService implements Service {
6
+ private commands: Map<string, Command> = new Map();
7
+
8
+ /**
9
+ * Register a command
10
+ * @param commandId Command Name (ID)
11
+ * @param handler Command handler function
12
+ * @param thisArg The `this` context for the handler
13
+ * @returns Disposable to unregister the command
14
+ */
15
+ registerCommand(
16
+ commandId: string,
17
+ handler: (...args: any[]) => any,
18
+ thisArg?: any,
19
+ ): Disposable {
20
+ if (this.commands.has(commandId)) {
21
+ console.warn(
22
+ `Command "${commandId}" is already registered. Overwriting.`,
23
+ );
24
+ }
25
+
26
+ const command: Command = {
27
+ id: commandId,
28
+ handler: thisArg ? handler.bind(thisArg) : handler,
29
+ };
30
+
31
+ this.commands.set(commandId, command);
32
+
33
+ return {
34
+ dispose: () => {
35
+ if (this.commands.get(commandId) === command) {
36
+ this.commands.delete(commandId);
37
+ }
38
+ },
39
+ };
40
+ }
41
+
42
+ /**
43
+ * Execute a command
44
+ * @param commandId Command Name (ID)
45
+ * @param args Arguments to pass to the handler
46
+ * @returns The result of the command handler
47
+ */
48
+ async executeCommand<T = any>(commandId: string, ...args: any[]): Promise<T> {
49
+ const command = this.commands.get(commandId);
50
+ if (!command) {
51
+ throw new Error(`Command "${commandId}" not found.`);
52
+ }
53
+
54
+ try {
55
+ return await command.handler(...args);
56
+ } catch (error) {
57
+ console.error(`Error executing command "${commandId}":`, error);
58
+ throw error;
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Get all registered commands
64
+ */
65
+ getCommands(): Map<string, Command> {
66
+ return this.commands;
67
+ }
68
+
69
+ /**
70
+ * Get a specific command
71
+ */
72
+ getCommand(commandId: string): Command | undefined {
73
+ return this.commands.get(commandId);
74
+ }
75
+
76
+ dispose() {
77
+ this.commands.clear();
78
+ }
79
+ }