@gtkx/cli 0.20.0 → 1.0.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +173 -0
- package/bin/gtkx-dev-runner.js +9 -0
- package/dist/builder.d.ts +2 -50
- package/dist/builder.d.ts.map +1 -1
- package/dist/builder.js +22 -48
- package/dist/builder.js.map +1 -1
- package/dist/cli.d.ts +1 -2
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +11 -124
- package/dist/cli.js.map +1 -1
- package/dist/codegen/freshness.d.ts +11 -0
- package/dist/codegen/freshness.d.ts.map +1 -0
- package/dist/codegen/freshness.js +42 -0
- package/dist/codegen/freshness.js.map +1 -0
- package/dist/codegen/report.d.ts +3 -0
- package/dist/codegen/report.d.ts.map +1 -0
- package/dist/codegen/report.js +16 -0
- package/dist/codegen/report.js.map +1 -0
- package/dist/codegen/run-codegen.d.ts +35 -0
- package/dist/codegen/run-codegen.d.ts.map +1 -0
- package/dist/codegen/run-codegen.js +133 -0
- package/dist/codegen/run-codegen.js.map +1 -0
- package/dist/codegen/store-resolver.d.ts +21 -0
- package/dist/codegen/store-resolver.d.ts.map +1 -0
- package/dist/codegen/store-resolver.js +52 -0
- package/dist/codegen/store-resolver.js.map +1 -0
- package/dist/commands/build.d.ts +9 -0
- package/dist/commands/build.d.ts.map +1 -0
- package/dist/commands/build.js +33 -0
- package/dist/commands/build.js.map +1 -0
- package/dist/commands/codegen.d.ts +9 -0
- package/dist/commands/codegen.d.ts.map +1 -0
- package/dist/commands/codegen.js +40 -0
- package/dist/commands/codegen.js.map +1 -0
- package/dist/commands/dev.d.ts +5 -0
- package/dist/commands/dev.d.ts.map +1 -0
- package/dist/commands/dev.js +21 -0
- package/dist/commands/dev.js.map +1 -0
- package/dist/commands/docs.d.ts +19 -0
- package/dist/commands/docs.d.ts.map +1 -0
- package/dist/commands/docs.js +59 -0
- package/dist/commands/docs.js.map +1 -0
- package/dist/dev/icon-dir.d.ts +2 -0
- package/dist/dev/icon-dir.d.ts.map +1 -0
- package/dist/dev/icon-dir.js +13 -0
- package/dist/dev/icon-dir.js.map +1 -0
- package/dist/dev/refresh-tracker.d.ts +6 -0
- package/dist/dev/refresh-tracker.d.ts.map +1 -0
- package/dist/dev/refresh-tracker.js +18 -0
- package/dist/dev/refresh-tracker.js.map +1 -0
- package/dist/dev/runner-deps.d.ts +3 -0
- package/dist/dev/runner-deps.d.ts.map +1 -0
- package/dist/dev/runner-deps.js +39 -0
- package/dist/dev/runner-deps.js.map +1 -0
- package/dist/dev/runner-main.d.ts +2 -0
- package/dist/dev/runner-main.d.ts.map +1 -0
- package/dist/dev/runner-main.js +28 -0
- package/dist/dev/runner-main.js.map +1 -0
- package/dist/dev/runner.d.ts +23 -0
- package/dist/dev/runner.d.ts.map +1 -0
- package/dist/dev/runner.js +88 -0
- package/dist/dev/runner.js.map +1 -0
- package/dist/dev/schema-dir.d.ts +2 -0
- package/dist/dev/schema-dir.d.ts.map +1 -0
- package/dist/dev/schema-dir.js +10 -0
- package/dist/dev/schema-dir.js.map +1 -0
- package/dist/dev/supervisor.d.ts +17 -0
- package/dist/dev/supervisor.d.ts.map +1 -0
- package/dist/dev/supervisor.js +142 -0
- package/dist/dev/supervisor.js.map +1 -0
- package/dist/dev/vite-dev-server.d.ts +21 -0
- package/dist/dev/vite-dev-server.d.ts.map +1 -0
- package/dist/dev/vite-dev-server.js +12 -0
- package/dist/dev/vite-dev-server.js.map +1 -0
- package/dist/internal/banner.d.ts +3 -0
- package/dist/internal/banner.d.ts.map +1 -0
- package/dist/internal/banner.js +8 -0
- package/dist/internal/banner.js.map +1 -0
- package/dist/internal/data-dir.d.ts +3 -0
- package/dist/internal/data-dir.d.ts.map +1 -0
- package/dist/internal/data-dir.js +37 -0
- package/dist/internal/data-dir.js.map +1 -0
- package/dist/internal/entry-arg.d.ts +19 -0
- package/dist/internal/entry-arg.d.ts.map +1 -0
- package/dist/internal/entry-arg.js +33 -0
- package/dist/internal/entry-arg.js.map +1 -0
- package/dist/internal/errors.d.ts +4 -0
- package/dist/internal/errors.d.ts.map +1 -0
- package/dist/internal/errors.js +27 -0
- package/dist/internal/errors.js.map +1 -0
- package/dist/internal/list-files.d.ts +6 -0
- package/dist/internal/list-files.d.ts.map +1 -0
- package/dist/internal/list-files.js +13 -0
- package/dist/internal/list-files.js.map +1 -0
- package/dist/internal/resolve-cli-tool.d.ts +2 -0
- package/dist/internal/resolve-cli-tool.d.ts.map +1 -0
- package/dist/internal/resolve-cli-tool.js +20 -0
- package/dist/internal/resolve-cli-tool.js.map +1 -0
- package/dist/internal/staging-dir.d.ts +3 -0
- package/dist/internal/staging-dir.d.ts.map +1 -0
- package/dist/internal/staging-dir.js +16 -0
- package/dist/internal/staging-dir.js.map +1 -0
- package/dist/internal/xdg-data-dirs.d.ts +2 -0
- package/dist/internal/xdg-data-dirs.d.ts.map +1 -0
- package/dist/internal/xdg-data-dirs.js +8 -0
- package/dist/internal/xdg-data-dirs.js.map +1 -0
- package/dist/mcp/client.d.ts +23 -0
- package/dist/mcp/client.d.ts.map +1 -0
- package/dist/mcp/client.js +157 -0
- package/dist/mcp/client.js.map +1 -0
- package/dist/mcp/handlers.d.ts +8 -0
- package/dist/mcp/handlers.d.ts.map +1 -0
- package/dist/mcp/handlers.js +119 -0
- package/dist/mcp/handlers.js.map +1 -0
- package/dist/mcp/index.d.ts +4 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +17 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/serialize-widget.d.ts +10 -0
- package/dist/mcp/serialize-widget.d.ts.map +1 -0
- package/dist/mcp/serialize-widget.js +20 -0
- package/dist/mcp/serialize-widget.js.map +1 -0
- package/dist/mcp/testing-loader.d.ts +6 -0
- package/dist/mcp/testing-loader.d.ts.map +1 -0
- package/dist/mcp/testing-loader.js +25 -0
- package/dist/mcp/testing-loader.js.map +1 -0
- package/dist/mcp/widget-registry.d.ts +13 -0
- package/dist/mcp/widget-registry.d.ts.map +1 -0
- package/dist/mcp/widget-registry.js +38 -0
- package/dist/mcp/widget-registry.js.map +1 -0
- package/dist/refresh-runtime.d.ts +10 -20
- package/dist/refresh-runtime.d.ts.map +1 -1
- package/dist/refresh-runtime.js +12 -36
- package/dist/refresh-runtime.js.map +1 -1
- package/dist/settings/compile.d.ts +2 -0
- package/dist/settings/compile.d.ts.map +1 -0
- package/dist/settings/compile.js +19 -0
- package/dist/settings/compile.js.map +1 -0
- package/dist/settings/parser.d.ts +23 -0
- package/dist/settings/parser.d.ts.map +1 -0
- package/dist/settings/parser.js +117 -0
- package/dist/settings/parser.js.map +1 -0
- package/dist/settings/render.d.ts +4 -0
- package/dist/settings/render.d.ts.map +1 -0
- package/dist/settings/render.js +148 -0
- package/dist/settings/render.js.map +1 -0
- package/dist/settings/schema.d.ts +13 -0
- package/dist/settings/schema.d.ts.map +1 -0
- package/dist/settings/schema.js +103 -0
- package/dist/settings/schema.js.map +1 -0
- package/dist/vite-plugins/asset-extensions.d.ts +4 -0
- package/dist/vite-plugins/asset-extensions.d.ts.map +1 -0
- package/dist/vite-plugins/asset-extensions.js +27 -0
- package/dist/vite-plugins/asset-extensions.js.map +1 -0
- package/dist/vite-plugins/built-url.d.ts +3 -0
- package/dist/vite-plugins/built-url.d.ts.map +1 -0
- package/dist/{vite-plugin-gtkx-built-url.js → vite-plugins/built-url.js} +4 -17
- package/dist/vite-plugins/built-url.js.map +1 -0
- package/dist/vite-plugins/css.d.ts +3 -0
- package/dist/vite-plugins/css.d.ts.map +1 -0
- package/dist/vite-plugins/css.js +30 -0
- package/dist/vite-plugins/css.js.map +1 -0
- package/dist/vite-plugins/fast-refresh/refresh-filter.d.ts +7 -0
- package/dist/vite-plugins/fast-refresh/refresh-filter.d.ts.map +1 -0
- package/dist/vite-plugins/fast-refresh/refresh-filter.js +15 -0
- package/dist/vite-plugins/fast-refresh/refresh-filter.js.map +1 -0
- package/dist/vite-plugins/fast-refresh/swc-refresh.d.ts +5 -0
- package/dist/vite-plugins/fast-refresh/swc-refresh.d.ts.map +1 -0
- package/dist/vite-plugins/fast-refresh/swc-refresh.js +66 -0
- package/dist/vite-plugins/fast-refresh/swc-refresh.js.map +1 -0
- package/dist/vite-plugins/icons.d.ts +3 -0
- package/dist/vite-plugins/icons.d.ts.map +1 -0
- package/dist/vite-plugins/icons.js +47 -0
- package/dist/vite-plugins/icons.js.map +1 -0
- package/dist/vite-plugins/index.d.ts +3 -0
- package/dist/vite-plugins/index.d.ts.map +1 -0
- package/dist/vite-plugins/index.js +21 -0
- package/dist/vite-plugins/index.js.map +1 -0
- package/dist/vite-plugins/native.d.ts +3 -0
- package/dist/vite-plugins/native.d.ts.map +1 -0
- package/dist/vite-plugins/native.js +60 -0
- package/dist/vite-plugins/native.js.map +1 -0
- package/dist/vite-plugins/react-compiler.d.ts +4 -0
- package/dist/vite-plugins/react-compiler.d.ts.map +1 -0
- package/dist/vite-plugins/react-compiler.js +56 -0
- package/dist/vite-plugins/react-compiler.js.map +1 -0
- package/dist/vite-plugins/react-dom-prebundle.d.ts +3 -0
- package/dist/vite-plugins/react-dom-prebundle.d.ts.map +1 -0
- package/dist/vite-plugins/react-dom-prebundle.js +11 -0
- package/dist/vite-plugins/react-dom-prebundle.js.map +1 -0
- package/dist/vite-plugins/resource-init-module.d.ts +5 -0
- package/dist/vite-plugins/resource-init-module.d.ts.map +1 -0
- package/dist/vite-plugins/resource-init-module.js +50 -0
- package/dist/vite-plugins/resource-init-module.js.map +1 -0
- package/dist/vite-plugins/resource-shared.d.ts +6 -0
- package/dist/vite-plugins/resource-shared.d.ts.map +1 -0
- package/dist/vite-plugins/resource-shared.js +21 -0
- package/dist/vite-plugins/resource-shared.js.map +1 -0
- package/dist/vite-plugins/resources.d.ts +4 -0
- package/dist/vite-plugins/resources.d.ts.map +1 -0
- package/dist/vite-plugins/resources.js +220 -0
- package/dist/vite-plugins/resources.js.map +1 -0
- package/dist/vite-plugins/settings-worker-env.d.ts +3 -0
- package/dist/vite-plugins/settings-worker-env.d.ts.map +1 -0
- package/dist/vite-plugins/settings-worker-env.js +21 -0
- package/dist/vite-plugins/settings-worker-env.js.map +1 -0
- package/dist/vite-plugins/settings.d.ts +3 -0
- package/dist/vite-plugins/settings.d.ts.map +1 -0
- package/dist/vite-plugins/settings.js +190 -0
- package/dist/vite-plugins/settings.js.map +1 -0
- package/dist/vite-plugins/undeclared-library.d.ts +3 -0
- package/dist/vite-plugins/undeclared-library.d.ts.map +1 -0
- package/dist/vite-plugins/undeclared-library.js +47 -0
- package/dist/vite-plugins/undeclared-library.js.map +1 -0
- package/dist/vite-plugins/virtual-module.d.ts +22 -0
- package/dist/vite-plugins/virtual-module.d.ts.map +1 -0
- package/dist/vite-plugins/virtual-module.js +18 -0
- package/dist/vite-plugins/virtual-module.js.map +1 -0
- package/dist/vitest-plugin.d.ts +10 -0
- package/dist/vitest-plugin.d.ts.map +1 -0
- package/dist/vitest-plugin.js +12 -0
- package/dist/vitest-plugin.js.map +1 -0
- package/env.d.ts +131 -3
- package/package.json +32 -38
- package/src/babel-modules.d.ts +13 -0
- package/src/builder.ts +28 -76
- package/src/cli.ts +22 -0
- package/src/codegen/freshness.ts +52 -0
- package/src/codegen/report.ts +20 -0
- package/src/codegen/run-codegen.ts +185 -0
- package/src/codegen/store-resolver.ts +75 -0
- package/src/commands/build.ts +37 -0
- package/src/commands/codegen.ts +44 -0
- package/src/commands/dev.ts +25 -0
- package/src/commands/docs.ts +66 -0
- package/src/dev/icon-dir.ts +11 -0
- package/src/dev/refresh-tracker.ts +21 -0
- package/src/dev/runner-deps.ts +42 -0
- package/src/dev/runner-main.ts +33 -0
- package/src/dev/runner.ts +126 -0
- package/src/dev/schema-dir.ts +11 -0
- package/src/dev/supervisor.ts +178 -0
- package/src/dev/vite-dev-server.ts +34 -0
- package/src/internal/banner.ts +9 -0
- package/src/internal/data-dir.ts +39 -0
- package/src/internal/entry-arg.ts +38 -0
- package/src/internal/errors.ts +28 -0
- package/src/internal/list-files.ts +17 -0
- package/src/internal/resolve-cli-tool.ts +21 -0
- package/src/internal/staging-dir.ts +16 -0
- package/src/internal/xdg-data-dirs.ts +7 -0
- package/src/mcp/client.ts +177 -0
- package/src/mcp/handlers.ts +156 -0
- package/src/mcp/index.ts +22 -0
- package/src/mcp/serialize-widget.ts +34 -0
- package/src/mcp/testing-loader.ts +32 -0
- package/src/mcp/widget-registry.ts +43 -0
- package/src/refresh-runtime.ts +12 -41
- package/src/settings/compile.ts +19 -0
- package/src/settings/parser.ts +154 -0
- package/src/settings/render.ts +167 -0
- package/src/settings/schema.ts +115 -0
- package/src/vite-plugins/asset-extensions.ts +28 -0
- package/src/{vite-plugin-gtkx-built-url.ts → vite-plugins/built-url.ts} +3 -16
- package/src/vite-plugins/css.ts +38 -0
- package/src/vite-plugins/fast-refresh/refresh-filter.ts +18 -0
- package/src/vite-plugins/fast-refresh/swc-refresh.ts +79 -0
- package/src/vite-plugins/icons.ts +58 -0
- package/src/vite-plugins/index.ts +22 -0
- package/src/vite-plugins/native.ts +74 -0
- package/src/vite-plugins/react-compiler.ts +70 -0
- package/src/vite-plugins/react-dom-prebundle.ts +14 -0
- package/src/vite-plugins/resource-init-module.ts +54 -0
- package/src/vite-plugins/resource-shared.ts +27 -0
- package/src/vite-plugins/resources.ts +272 -0
- package/src/vite-plugins/settings-worker-env.ts +25 -0
- package/src/vite-plugins/settings.ts +221 -0
- package/src/vite-plugins/undeclared-library.ts +69 -0
- package/src/vite-plugins/virtual-module.ts +37 -0
- package/src/vitest-plugin.ts +14 -0
- package/dist/create.d.ts +0 -54
- package/dist/create.d.ts.map +0 -1
- package/dist/create.js +0 -221
- package/dist/create.js.map +0 -1
- package/dist/dev-server.d.ts +0 -38
- package/dist/dev-server.d.ts.map +0 -1
- package/dist/dev-server.js +0 -128
- package/dist/dev-server.js.map +0 -1
- package/dist/index.d.ts +0 -4
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -4
- package/dist/index.js.map +0 -1
- package/dist/mcp-client.d.ts +0 -31
- package/dist/mcp-client.d.ts.map +0 -1
- package/dist/mcp-client.js +0 -430
- package/dist/mcp-client.js.map +0 -1
- package/dist/templates.d.ts +0 -9
- package/dist/templates.d.ts.map +0 -1
- package/dist/templates.js +0 -16
- package/dist/templates.js.map +0 -1
- package/dist/vite-plugin-gtkx-assets.d.ts +0 -20
- package/dist/vite-plugin-gtkx-assets.d.ts.map +0 -1
- package/dist/vite-plugin-gtkx-assets.js +0 -57
- package/dist/vite-plugin-gtkx-assets.js.map +0 -1
- package/dist/vite-plugin-gtkx-built-url.d.ts +0 -18
- package/dist/vite-plugin-gtkx-built-url.d.ts.map +0 -1
- package/dist/vite-plugin-gtkx-built-url.js.map +0 -1
- package/dist/vite-plugin-gtkx-native.d.ts +0 -14
- package/dist/vite-plugin-gtkx-native.d.ts.map +0 -1
- package/dist/vite-plugin-gtkx-native.js +0 -53
- package/dist/vite-plugin-gtkx-native.js.map +0 -1
- package/dist/vite-plugin-gtkx-refresh.d.ts +0 -8
- package/dist/vite-plugin-gtkx-refresh.d.ts.map +0 -1
- package/dist/vite-plugin-gtkx-refresh.js +0 -37
- package/dist/vite-plugin-gtkx-refresh.js.map +0 -1
- package/dist/vite-plugin-swc-ssr-refresh.d.ts +0 -8
- package/dist/vite-plugin-swc-ssr-refresh.d.ts.map +0 -1
- package/dist/vite-plugin-swc-ssr-refresh.js +0 -46
- package/dist/vite-plugin-swc-ssr-refresh.js.map +0 -1
- package/src/cli.tsx +0 -154
- package/src/create.ts +0 -310
- package/src/dev-server.tsx +0 -162
- package/src/index.ts +0 -3
- package/src/mcp-client.ts +0 -518
- package/src/templates.ts +0 -26
- package/src/vite-plugin-gtkx-assets.ts +0 -69
- package/src/vite-plugin-gtkx-native.ts +0 -64
- package/src/vite-plugin-gtkx-refresh.ts +0 -54
- package/src/vite-plugin-swc-ssr-refresh.ts +0 -61
- package/templates/claude/EXAMPLES.md.ejs +0 -744
- package/templates/claude/SKILL.md.ejs +0 -93
- package/templates/claude/WIDGETS.md.ejs +0 -820
- package/templates/config/vitest.config.ts.ejs +0 -11
- package/templates/gitignore.ejs +0 -4
- package/templates/package.json.ejs +0 -16
- package/templates/src/app.tsx.ejs +0 -32
- package/templates/src/dev.tsx.ejs +0 -5
- package/templates/src/gtkx-env.d.ts.ejs +0 -1
- package/templates/src/index.tsx.ejs +0 -5
- package/templates/tests/app.test.tsx.ejs +0 -16
- package/templates/tsconfig.json.ejs +0 -14
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import * as net from "node:net";
|
|
2
|
+
import * as Gio from "@gtkx/gi/gio";
|
|
3
|
+
import * as Gtk from "@gtkx/gi/gtk";
|
|
4
|
+
import { DEFAULT_SOCKET_PATH, ErrorCode, ProtocolConnection, ProtocolError, type Request } from "@gtkx/mcp/internal";
|
|
5
|
+
import { error, errorMessage, info, normalizeError, warn } from "@gtkx/utils";
|
|
6
|
+
import { dispatch } from "./handlers.js";
|
|
7
|
+
import { WidgetRegistry } from "./widget-registry.js";
|
|
8
|
+
|
|
9
|
+
export type McpClientOptions = {
|
|
10
|
+
socketPath?: string;
|
|
11
|
+
applicationId: string;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const RECONNECT_DELAY_MS = 2000;
|
|
15
|
+
const REGISTER_TIMEOUT_MS = 30000;
|
|
16
|
+
|
|
17
|
+
export class McpClient {
|
|
18
|
+
private socket: net.Socket | null = null;
|
|
19
|
+
private connection: ProtocolConnection | null = null;
|
|
20
|
+
private socketPath: string;
|
|
21
|
+
private applicationId: string;
|
|
22
|
+
private reconnectTimer: NodeJS.Timeout | null = null;
|
|
23
|
+
private hasConnected = false;
|
|
24
|
+
private isStopping = false;
|
|
25
|
+
private pendingConnectReject: ((error: Error) => void) | null = null;
|
|
26
|
+
private registry = new WidgetRegistry();
|
|
27
|
+
|
|
28
|
+
constructor(options: McpClientOptions) {
|
|
29
|
+
this.socketPath = options.socketPath ?? DEFAULT_SOCKET_PATH;
|
|
30
|
+
this.applicationId = options.applicationId;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async connect(): Promise<void> {
|
|
34
|
+
return new Promise<void>((resolve, reject) => {
|
|
35
|
+
this.pendingConnectReject = reject;
|
|
36
|
+
this.attemptConnect(
|
|
37
|
+
() => {
|
|
38
|
+
this.pendingConnectReject = null;
|
|
39
|
+
resolve();
|
|
40
|
+
},
|
|
41
|
+
(error) => {
|
|
42
|
+
this.pendingConnectReject = null;
|
|
43
|
+
reject(error);
|
|
44
|
+
},
|
|
45
|
+
);
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
disconnect(): void {
|
|
50
|
+
this.isStopping = true;
|
|
51
|
+
if (this.pendingConnectReject) {
|
|
52
|
+
this.pendingConnectReject(new Error("Client disconnected before connection registered"));
|
|
53
|
+
this.pendingConnectReject = null;
|
|
54
|
+
}
|
|
55
|
+
if (this.reconnectTimer) {
|
|
56
|
+
clearTimeout(this.reconnectTimer);
|
|
57
|
+
this.reconnectTimer = null;
|
|
58
|
+
}
|
|
59
|
+
this.connection?.rejectPending(new Error("Client disconnected"));
|
|
60
|
+
if (this.socket) {
|
|
61
|
+
this.connection?.write({ id: crypto.randomUUID(), method: "app.unregister" });
|
|
62
|
+
this.socket.destroy();
|
|
63
|
+
this.socket = null;
|
|
64
|
+
}
|
|
65
|
+
this.connection = null;
|
|
66
|
+
this.hasConnected = false;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
private attemptConnect(onSuccess?: () => void, onError?: (error: Error) => void): void {
|
|
70
|
+
let settled = false;
|
|
71
|
+
|
|
72
|
+
const settle = <T extends unknown[]>(callback: ((...args: T) => void) | undefined, ...args: T) => {
|
|
73
|
+
if (settled) return;
|
|
74
|
+
settled = true;
|
|
75
|
+
callback?.(...args);
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const socket = net.createConnection(this.socketPath, () => {
|
|
79
|
+
info(`Connected to MCP server at ${this.socketPath}`);
|
|
80
|
+
this.hasConnected = true;
|
|
81
|
+
this.register()
|
|
82
|
+
.then(() => {
|
|
83
|
+
info("Registered with MCP server");
|
|
84
|
+
settle(onSuccess);
|
|
85
|
+
})
|
|
86
|
+
.catch((cause) => {
|
|
87
|
+
error("Failed to register with MCP server:", cause.message);
|
|
88
|
+
settle(onError, normalizeError(cause));
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
const connection = ProtocolConnection.fromSocket(socket, {
|
|
93
|
+
onClose: () => {
|
|
94
|
+
if (this.hasConnected) {
|
|
95
|
+
info("Disconnected from MCP server");
|
|
96
|
+
this.hasConnected = false;
|
|
97
|
+
}
|
|
98
|
+
this.socket = null;
|
|
99
|
+
this.connection = null;
|
|
100
|
+
this.scheduleReconnect();
|
|
101
|
+
},
|
|
102
|
+
onError: (socketError) => {
|
|
103
|
+
const code = (socketError as NodeJS.ErrnoException).code;
|
|
104
|
+
const isDisconnectError =
|
|
105
|
+
code === "ENOENT" || code === "ECONNREFUSED" || code === "EPIPE" || code === "ECONNRESET";
|
|
106
|
+
if (isDisconnectError) {
|
|
107
|
+
this.scheduleReconnect();
|
|
108
|
+
} else {
|
|
109
|
+
error("Socket error:", socketError.message);
|
|
110
|
+
}
|
|
111
|
+
settle(onError, socketError);
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
connection.on("request", (request) => {
|
|
115
|
+
this.handleRequest(request).catch((cause) => {
|
|
116
|
+
error("Error handling request:", cause);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
connection.on("invalid", ({ error: parseError }) => {
|
|
120
|
+
warn(`Received invalid JSON from MCP server: ${parseError.message}`);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
this.socket = socket;
|
|
124
|
+
this.connection = connection;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
private scheduleReconnect(): void {
|
|
128
|
+
if (this.reconnectTimer || this.isStopping) return;
|
|
129
|
+
this.reconnectTimer = setTimeout(() => {
|
|
130
|
+
this.reconnectTimer = null;
|
|
131
|
+
this.attemptConnect();
|
|
132
|
+
}, RECONNECT_DELAY_MS);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
private register(): Promise<unknown> {
|
|
136
|
+
if (!this.connection) {
|
|
137
|
+
return Promise.reject(new Error("Connection not initialized"));
|
|
138
|
+
}
|
|
139
|
+
return this.connection.send(
|
|
140
|
+
"app.register",
|
|
141
|
+
{
|
|
142
|
+
applicationId: this.applicationId,
|
|
143
|
+
pid: process.pid,
|
|
144
|
+
projectRoot: process.cwd(),
|
|
145
|
+
},
|
|
146
|
+
REGISTER_TIMEOUT_MS,
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
private async handleRequest(request: Request): Promise<void> {
|
|
151
|
+
const { id, method, params } = request;
|
|
152
|
+
const connection = this.connection;
|
|
153
|
+
if (!connection) return;
|
|
154
|
+
|
|
155
|
+
try {
|
|
156
|
+
const defaultApp = Gio.Application.getDefault();
|
|
157
|
+
if (!(defaultApp instanceof Gtk.Application)) {
|
|
158
|
+
throw new TypeError("Application not initialized");
|
|
159
|
+
}
|
|
160
|
+
this.registry.refresh();
|
|
161
|
+
const result = await dispatch(method, params, { app: defaultApp, registry: this.registry });
|
|
162
|
+
connection.write({ id, result });
|
|
163
|
+
} catch (error) {
|
|
164
|
+
if (error instanceof ProtocolError) {
|
|
165
|
+
connection.write({ id, error: error.toErrorObject() });
|
|
166
|
+
} else {
|
|
167
|
+
connection.write({
|
|
168
|
+
id,
|
|
169
|
+
error: {
|
|
170
|
+
code: ErrorCode.INTERNAL_ERROR,
|
|
171
|
+
message: errorMessage(error),
|
|
172
|
+
},
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname } from "node:path";
|
|
3
|
+
import * as Gtk from "@gtkx/gi/gtk";
|
|
4
|
+
import {
|
|
5
|
+
invalidRequestError,
|
|
6
|
+
methodNotFoundError,
|
|
7
|
+
type ParamsSchema,
|
|
8
|
+
type ServerInitiatedMethod,
|
|
9
|
+
type ServerRequestParams,
|
|
10
|
+
ServerRequestParamsSchemas,
|
|
11
|
+
widgetNotFoundError,
|
|
12
|
+
} from "@gtkx/mcp/internal";
|
|
13
|
+
import { serializeWidget } from "./serialize-widget.js";
|
|
14
|
+
import { loadTestingModule } from "./testing-loader.js";
|
|
15
|
+
import type { WidgetRegistry } from "./widget-registry.js";
|
|
16
|
+
|
|
17
|
+
export type HandlerContext = {
|
|
18
|
+
app: Gtk.Application;
|
|
19
|
+
registry: WidgetRegistry;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
type ValidatedHandler = (ctx: HandlerContext, params: unknown) => Promise<unknown>;
|
|
23
|
+
|
|
24
|
+
const validated = <Params>(
|
|
25
|
+
schema: ParamsSchema<Params>,
|
|
26
|
+
handler: (ctx: HandlerContext, params: Params) => Promise<unknown>,
|
|
27
|
+
): ValidatedHandler => {
|
|
28
|
+
return (ctx, params) => {
|
|
29
|
+
const parsed = schema.safeParse(params ?? {});
|
|
30
|
+
if (!parsed.success) {
|
|
31
|
+
throw invalidRequestError(parsed.error.message);
|
|
32
|
+
}
|
|
33
|
+
return handler(ctx, parsed.data);
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const requireWidget = (registry: WidgetRegistry, widgetId: string | undefined): Gtk.Widget => {
|
|
38
|
+
if (widgetId === undefined) {
|
|
39
|
+
throw widgetNotFoundError("undefined");
|
|
40
|
+
}
|
|
41
|
+
const widget = registry.get(widgetId);
|
|
42
|
+
if (!widget) {
|
|
43
|
+
throw widgetNotFoundError(widgetId);
|
|
44
|
+
}
|
|
45
|
+
return widget;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const extractSignalArg = (arg: unknown): unknown => {
|
|
49
|
+
const isTypedArg = typeof arg === "object" && arg !== null && "type" in arg && "value" in arg;
|
|
50
|
+
return isTypedArg ? (arg as { value: unknown }).value : arg;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const handleQuery = async (
|
|
54
|
+
{ app, registry }: HandlerContext,
|
|
55
|
+
params: ServerRequestParams<"widget.query">,
|
|
56
|
+
): Promise<unknown> => {
|
|
57
|
+
const testing = await loadTestingModule();
|
|
58
|
+
let widgets: Gtk.Widget[] = [];
|
|
59
|
+
|
|
60
|
+
switch (params.by) {
|
|
61
|
+
case "role": {
|
|
62
|
+
const roleValue =
|
|
63
|
+
typeof params.value === "string"
|
|
64
|
+
? Gtk.AccessibleRole[params.value as keyof typeof Gtk.AccessibleRole]
|
|
65
|
+
: params.value;
|
|
66
|
+
widgets = await testing.findAllByRole(app, roleValue as Gtk.AccessibleRole, params.options);
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
case "text":
|
|
70
|
+
widgets = await testing.findAllByText(app, String(params.value), params.options);
|
|
71
|
+
break;
|
|
72
|
+
case "name":
|
|
73
|
+
widgets = await testing.findAllByName(app, String(params.value), params.options);
|
|
74
|
+
break;
|
|
75
|
+
case "labelText":
|
|
76
|
+
widgets = await testing.findAllByLabelText(app, String(params.value), params.options);
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return { widgets: widgets.map((w) => serializeWidget(w, (widget) => registry.idFor(widget), testing)) };
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const defaultScreenshotTarget = (registry: WidgetRegistry): Gtk.Widget => {
|
|
84
|
+
const [toplevel] = registry.toplevels();
|
|
85
|
+
if (!toplevel) {
|
|
86
|
+
throw new Error("No windows available for screenshot");
|
|
87
|
+
}
|
|
88
|
+
return toplevel;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const handleScreenshot = async (
|
|
92
|
+
{ registry }: HandlerContext,
|
|
93
|
+
params: ServerRequestParams<"widget.screenshot">,
|
|
94
|
+
): Promise<unknown> => {
|
|
95
|
+
const testing = await loadTestingModule();
|
|
96
|
+
const target = params.windowId ? requireWidget(registry, params.windowId) : defaultScreenshotTarget(registry);
|
|
97
|
+
const result = await testing.screenshot(target);
|
|
98
|
+
if (params.path) {
|
|
99
|
+
mkdirSync(dirname(params.path), { recursive: true });
|
|
100
|
+
writeFileSync(params.path, Buffer.from(result.data, "base64"));
|
|
101
|
+
return { data: result.data, mimeType: result.mimeType, savedPath: params.path };
|
|
102
|
+
}
|
|
103
|
+
return { data: result.data, mimeType: result.mimeType };
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const HANDLERS: Record<ServerInitiatedMethod, ValidatedHandler> = {
|
|
107
|
+
"app.getWindows": validated(ServerRequestParamsSchemas["app.getWindows"], async ({ registry }) => ({
|
|
108
|
+
windows: registry.toplevels().map((window) => ({
|
|
109
|
+
id: registry.idFor(window),
|
|
110
|
+
title: window.getTitle(),
|
|
111
|
+
})),
|
|
112
|
+
})),
|
|
113
|
+
"widget.getTree": validated(ServerRequestParamsSchemas["widget.getTree"], async ({ app, registry }) => {
|
|
114
|
+
const testing = await loadTestingModule();
|
|
115
|
+
return { tree: testing.prettyWidget(app, { getId: (w) => registry.idFor(w), highlight: false }) };
|
|
116
|
+
}),
|
|
117
|
+
"widget.query": validated(ServerRequestParamsSchemas["widget.query"], handleQuery),
|
|
118
|
+
"widget.getProps": validated(ServerRequestParamsSchemas["widget.getProps"], async ({ registry }, params) => {
|
|
119
|
+
const testing = await loadTestingModule();
|
|
120
|
+
const widget = requireWidget(registry, params.widgetId);
|
|
121
|
+
return serializeWidget(widget, (target) => registry.idFor(target), testing);
|
|
122
|
+
}),
|
|
123
|
+
"widget.click": validated(ServerRequestParamsSchemas["widget.click"], async ({ registry }, params) => {
|
|
124
|
+
const testing = await loadTestingModule();
|
|
125
|
+
const widget = requireWidget(registry, params.widgetId);
|
|
126
|
+
await testing.userEvent.click(widget);
|
|
127
|
+
return { success: true };
|
|
128
|
+
}),
|
|
129
|
+
"widget.type": validated(ServerRequestParamsSchemas["widget.type"], async ({ registry }, params) => {
|
|
130
|
+
const testing = await loadTestingModule();
|
|
131
|
+
const widget = requireWidget(registry, params.widgetId);
|
|
132
|
+
if (params.clear) {
|
|
133
|
+
await testing.userEvent.clear(widget);
|
|
134
|
+
}
|
|
135
|
+
await testing.userEvent.type(widget, params.text);
|
|
136
|
+
return { success: true };
|
|
137
|
+
}),
|
|
138
|
+
"widget.fireEvent": validated(ServerRequestParamsSchemas["widget.fireEvent"], async ({ registry }, params) => {
|
|
139
|
+
const testing = await loadTestingModule();
|
|
140
|
+
const widget = requireWidget(registry, params.widgetId);
|
|
141
|
+
const signalArgs = (params.args ?? []).map(extractSignalArg);
|
|
142
|
+
await testing.fireEvent(widget, params.signal, ...signalArgs);
|
|
143
|
+
return { success: true };
|
|
144
|
+
}),
|
|
145
|
+
"widget.screenshot": validated(ServerRequestParamsSchemas["widget.screenshot"], handleScreenshot),
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
const isServerInitiatedMethod = (method: string): method is ServerInitiatedMethod =>
|
|
149
|
+
Object.hasOwn(ServerRequestParamsSchemas, method);
|
|
150
|
+
|
|
151
|
+
export const dispatch = async (method: string, params: unknown, ctx: HandlerContext): Promise<unknown> => {
|
|
152
|
+
if (!isServerInitiatedMethod(method)) {
|
|
153
|
+
throw methodNotFoundError(method);
|
|
154
|
+
}
|
|
155
|
+
return HANDLERS[method](ctx, params);
|
|
156
|
+
};
|
package/src/mcp/index.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { McpClient } from "./client.js";
|
|
2
|
+
|
|
3
|
+
let globalClient: McpClient | null = null;
|
|
4
|
+
|
|
5
|
+
export const startMcpClient = async (applicationId: string): Promise<McpClient> => {
|
|
6
|
+
if (globalClient) {
|
|
7
|
+
return globalClient;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
globalClient = new McpClient({ applicationId });
|
|
11
|
+
|
|
12
|
+
await globalClient.connect().catch(() => {});
|
|
13
|
+
|
|
14
|
+
return globalClient;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const stopMcpClient = (): void => {
|
|
18
|
+
if (globalClient) {
|
|
19
|
+
globalClient.disconnect();
|
|
20
|
+
globalClient = null;
|
|
21
|
+
}
|
|
22
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type * as Gtk from "@gtkx/gi/gtk";
|
|
2
|
+
import type { SerializedWidget } from "@gtkx/mcp/internal";
|
|
3
|
+
|
|
4
|
+
type WidgetIdResolver = (widget: Gtk.Widget) => string;
|
|
5
|
+
|
|
6
|
+
export type WidgetFormatting = {
|
|
7
|
+
formatRole(role: Gtk.AccessibleRole): string;
|
|
8
|
+
getWidgetNodeText(widget: Gtk.Widget): string | null;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const serializeWidget = (
|
|
12
|
+
widget: Gtk.Widget,
|
|
13
|
+
idFor: WidgetIdResolver,
|
|
14
|
+
testing: WidgetFormatting,
|
|
15
|
+
): SerializedWidget => {
|
|
16
|
+
const children: SerializedWidget[] = [];
|
|
17
|
+
let child = widget.getFirstChild();
|
|
18
|
+
while (child) {
|
|
19
|
+
children.push(serializeWidget(child, idFor, testing));
|
|
20
|
+
child = child.getNextSibling();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
id: idFor(widget),
|
|
25
|
+
type: widget.constructor.name,
|
|
26
|
+
role: testing.formatRole(widget.getAccessibleRole()),
|
|
27
|
+
name: widget.getName() || null,
|
|
28
|
+
text: testing.getWidgetNodeText(widget),
|
|
29
|
+
sensitive: widget.getSensitive(),
|
|
30
|
+
visible: widget.getVisible(),
|
|
31
|
+
cssClasses: widget.getCssClasses(),
|
|
32
|
+
children,
|
|
33
|
+
};
|
|
34
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
type TestingModule = typeof import("@gtkx/testing");
|
|
2
|
+
|
|
3
|
+
export type TestingModuleLoader = () => Promise<TestingModule>;
|
|
4
|
+
|
|
5
|
+
const defaultLoader: TestingModuleLoader = () => import("@gtkx/testing");
|
|
6
|
+
|
|
7
|
+
let loader: TestingModuleLoader = defaultLoader;
|
|
8
|
+
let testingModule: TestingModule | null = null;
|
|
9
|
+
let testingLoadError: Error | null = null;
|
|
10
|
+
|
|
11
|
+
export const setTestingModuleLoader = (next: TestingModuleLoader | null): void => {
|
|
12
|
+
loader = next ?? defaultLoader;
|
|
13
|
+
testingModule = null;
|
|
14
|
+
testingLoadError = null;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const loadTestingModule = async (): Promise<TestingModule> => {
|
|
18
|
+
if (testingModule) return testingModule;
|
|
19
|
+
if (testingLoadError) throw testingLoadError;
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
testingModule = await loader();
|
|
23
|
+
return testingModule;
|
|
24
|
+
} catch (cause) {
|
|
25
|
+
testingLoadError = new Error(
|
|
26
|
+
"@gtkx/testing is not installed, install it to enable MCP widget interactions: " +
|
|
27
|
+
`pnpm add -D @gtkx/testing (import failed: ${String(cause)})`,
|
|
28
|
+
{ cause },
|
|
29
|
+
);
|
|
30
|
+
throw testingLoadError;
|
|
31
|
+
}
|
|
32
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import * as Gtk from "@gtkx/gi/gtk";
|
|
2
|
+
|
|
3
|
+
export class WidgetRegistry {
|
|
4
|
+
private idByWidget = new WeakMap<Gtk.Widget, string>();
|
|
5
|
+
private nextId = 0;
|
|
6
|
+
private widgetById = new Map<string, Gtk.Widget>();
|
|
7
|
+
private toplevelWindows: Gtk.Window[] = [];
|
|
8
|
+
|
|
9
|
+
refresh(): void {
|
|
10
|
+
this.widgetById.clear();
|
|
11
|
+
this.toplevelWindows = Gtk.Window.listToplevels() as Gtk.Window[];
|
|
12
|
+
for (const window of this.toplevelWindows) {
|
|
13
|
+
this.register(window);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
toplevels(): Gtk.Window[] {
|
|
18
|
+
return this.toplevelWindows;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
register(widget: Gtk.Widget): void {
|
|
22
|
+
const id = this.idFor(widget);
|
|
23
|
+
this.widgetById.set(id, widget);
|
|
24
|
+
let child = widget.getFirstChild();
|
|
25
|
+
while (child) {
|
|
26
|
+
this.register(child);
|
|
27
|
+
child = child.getNextSibling();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
idFor(widget: Gtk.Widget): string {
|
|
32
|
+
let id = this.idByWidget.get(widget);
|
|
33
|
+
if (!id) {
|
|
34
|
+
id = String(this.nextId++);
|
|
35
|
+
this.idByWidget.set(widget, id);
|
|
36
|
+
}
|
|
37
|
+
return id;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
get(id: string): Gtk.Widget | undefined {
|
|
41
|
+
return this.widgetById.get(id);
|
|
42
|
+
}
|
|
43
|
+
}
|
package/src/refresh-runtime.ts
CHANGED
|
@@ -7,14 +7,11 @@ globalThis.$RefreshReg$ = () => {};
|
|
|
7
7
|
globalThis.$RefreshSig$ = () => (type: unknown) => type;
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
10
|
+
* Builds the per-module `$RefreshReg$` and `$RefreshSig$` hooks that React Refresh
|
|
11
|
+
* transformed code expects, scoping every registered component to the given module.
|
|
11
12
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* @param moduleId - Unique identifier for the module
|
|
16
|
-
* @returns Registration functions for the module
|
|
17
|
-
* @internal
|
|
13
|
+
* @param moduleId Identifier used to namespace registered component types so they are unique across modules.
|
|
14
|
+
* @returns An object with `$RefreshReg$` and `$RefreshSig$` to install as module-local globals.
|
|
18
15
|
*/
|
|
19
16
|
export function createModuleRegistration(moduleId: string): {
|
|
20
17
|
$RefreshReg$: (type: ComponentType, id: string) => void;
|
|
@@ -28,36 +25,14 @@ export function createModuleRegistration(moduleId: string): {
|
|
|
28
25
|
};
|
|
29
26
|
}
|
|
30
27
|
|
|
31
|
-
function isLikelyComponentType(value: unknown): boolean {
|
|
32
|
-
if (typeof value !== "function") {
|
|
33
|
-
return false;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const func = value as { $$typeof?: symbol };
|
|
37
|
-
|
|
38
|
-
if (func.$$typeof === Symbol.for("react.memo") || func.$$typeof === Symbol.for("react.forward_ref")) {
|
|
39
|
-
return true;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const name = (value as { name?: string }).name;
|
|
43
|
-
if (typeof name === "string" && /^[A-Z]/.test(name)) {
|
|
44
|
-
return true;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return false;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
28
|
/**
|
|
51
|
-
*
|
|
29
|
+
* Determines whether a module can act as a React Refresh boundary, meaning every one of
|
|
30
|
+
* its exports (ignoring `__esModule`) looks like a React component type.
|
|
52
31
|
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
* @param moduleExports - The module's exports object
|
|
57
|
-
* @returns `true` if the module can be fast-refreshed
|
|
58
|
-
* @internal
|
|
32
|
+
* @param moduleExports The module's export object to inspect.
|
|
33
|
+
* @returns `true` when the module qualifies as a refresh boundary, `false` otherwise.
|
|
59
34
|
*/
|
|
60
|
-
export function
|
|
35
|
+
export function isRefreshBoundary(moduleExports: Record<string, unknown>): boolean {
|
|
61
36
|
if (RefreshRuntime.isLikelyComponentType(moduleExports)) {
|
|
62
37
|
return true;
|
|
63
38
|
}
|
|
@@ -69,20 +44,16 @@ export function isReactRefreshBoundary(moduleExports: Record<string, unknown>):
|
|
|
69
44
|
|
|
70
45
|
const value = moduleExports[key];
|
|
71
46
|
|
|
72
|
-
if (!isLikelyComponentType(value)) {
|
|
47
|
+
if (!RefreshRuntime.isLikelyComponentType(value)) {
|
|
73
48
|
return false;
|
|
74
49
|
}
|
|
75
50
|
}
|
|
76
51
|
|
|
77
|
-
return Object.keys(moduleExports).
|
|
52
|
+
return Object.keys(moduleExports).some((k) => k !== "__esModule");
|
|
78
53
|
}
|
|
79
54
|
|
|
80
55
|
/**
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
* Called after module updates when all exports are React components.
|
|
84
|
-
*
|
|
85
|
-
* @internal
|
|
56
|
+
* Applies all pending React Refresh updates, re-rendering components whose modules have changed.
|
|
86
57
|
*/
|
|
87
58
|
export function performRefresh(): void {
|
|
88
59
|
RefreshRuntime.performReactRefresh();
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import { formatChildProcessError } from "@gtkx/utils";
|
|
3
|
+
import { resolveCliTool } from "../internal/resolve-cli-tool.js";
|
|
4
|
+
|
|
5
|
+
const SCHEMA_COMPILER = "glib-compile-schemas";
|
|
6
|
+
const SCHEMA_COMPILE_TIMEOUT_MS = 30_000;
|
|
7
|
+
|
|
8
|
+
export const compileSchemas = (dir: string): void => {
|
|
9
|
+
try {
|
|
10
|
+
execFileSync(resolveCliTool(SCHEMA_COMPILER), ["--strict", dir], {
|
|
11
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
12
|
+
timeout: SCHEMA_COMPILE_TIMEOUT_MS,
|
|
13
|
+
encoding: "utf-8",
|
|
14
|
+
});
|
|
15
|
+
} catch (error) {
|
|
16
|
+
const details = formatChildProcessError(error);
|
|
17
|
+
throw new Error(`glib-compile-schemas failed for ${dir}${details ? `:\n${details}` : ""}`, { cause: error });
|
|
18
|
+
}
|
|
19
|
+
};
|