@aprovan/patchwork-mcp 0.1.0-dev.030eb3d
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/LICENSE +373 -0
- package/README.md +15 -0
- package/dist/index.d.ts +128 -0
- package/dist/index.js +989 -0
- package/dist/index.js.map +1 -0
- package/dist/runtime/assets/__vite-browser-external-BIHI7g3E.js +1 -0
- package/dist/runtime/assets/index-CSnUKUMa.js +1058 -0
- package/dist/runtime/index.html +38 -0
- package/dist/server.d.ts +6 -0
- package/dist/server.js +1400 -0
- package/dist/server.js.map +1 -0
- package/dist/shell/shell.js +67 -0
- package/docs/widget-preview.png +0 -0
- package/e2e/__snapshots__/.gitkeep +0 -0
- package/e2e/global-setup.ts +114 -0
- package/e2e/global-teardown.ts +15 -0
- package/e2e/visual-regression.test.ts +86 -0
- package/e2e/widget-smoke.test.ts +109 -0
- package/index.html +32 -0
- package/package.json +55 -0
- package/playwright.config.ts +43 -0
- package/src/__tests__/live-update.test.ts +158 -0
- package/src/__tests__/local-backend.test.ts +100 -0
- package/src/__tests__/memory-backend.ts +144 -0
- package/src/__tests__/services.test.ts +153 -0
- package/src/__tests__/shim.test.ts +60 -0
- package/src/__tests__/widget-store.test.ts +188 -0
- package/src/e2e-visual.ts +148 -0
- package/src/index.ts +608 -0
- package/src/live-update.ts +150 -0
- package/src/logger.ts +44 -0
- package/src/reference-widgets/live-dashboard.ts +162 -0
- package/src/registry-backend.ts +47 -0
- package/src/runtime/index.html +38 -0
- package/src/runtime/main.ts +164 -0
- package/src/scripts/export-artifacts.ts +51 -0
- package/src/server.ts +399 -0
- package/src/services.ts +307 -0
- package/src/shell/main.ts +172 -0
- package/src/shim.ts +106 -0
- package/src/tunnel.ts +123 -0
- package/src/widget-store/index.ts +10 -0
- package/src/widget-store/local-backend.ts +77 -0
- package/src/widget-store/store.ts +242 -0
- package/src/widget-store/types.ts +53 -0
- package/tsconfig.json +10 -0
- package/tsup.config.ts +14 -0
- package/vite.runtime.config.ts +28 -0
- package/vite.shell.config.ts +30 -0
- package/vitest.config.ts +7 -0
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import cors from "cors";
|
|
6
|
+
import express from "express";
|
|
7
|
+
import { log, error } from "./logger.js";
|
|
8
|
+
import {
|
|
9
|
+
REFERENCE_WIDGET_FILES,
|
|
10
|
+
REFERENCE_WIDGET_MANIFEST,
|
|
11
|
+
} from "./reference-widgets/live-dashboard.js";
|
|
12
|
+
import { startTunnel, stopTunnel } from "./tunnel.js";
|
|
13
|
+
import { getWidgetStore, resetWidgetStore } from "./widget-store/index.js";
|
|
14
|
+
|
|
15
|
+
const WIDGET_PORT = Number(process.env["WIDGET_PORT"] ?? 3002);
|
|
16
|
+
const ARTIFACTS_DIR = resolve(process.cwd(), ".artifacts");
|
|
17
|
+
const RUNTIME_DIR = fileURLToPath(new URL("../dist/runtime", import.meta.url));
|
|
18
|
+
|
|
19
|
+
interface WidgetPreviewEntry {
|
|
20
|
+
name: string;
|
|
21
|
+
hash: string;
|
|
22
|
+
url: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function parseArgs(): { tunnel: boolean } {
|
|
26
|
+
return {
|
|
27
|
+
tunnel: process.argv.includes("--tunnel"),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async function saveReferenceWidgets(): Promise<Array<{ name: string; hash: string }>> {
|
|
32
|
+
resetWidgetStore();
|
|
33
|
+
const store = getWidgetStore();
|
|
34
|
+
|
|
35
|
+
const hash = "reference";
|
|
36
|
+
await store.saveWidget(hash, REFERENCE_WIDGET_FILES, REFERENCE_WIDGET_MANIFEST, "main.tsx");
|
|
37
|
+
|
|
38
|
+
return [{ name: REFERENCE_WIDGET_MANIFEST.name, hash }];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function startWidgetServer(): Promise<void> {
|
|
42
|
+
if (!existsSync(RUNTIME_DIR)) {
|
|
43
|
+
throw new Error(`Runtime bundle missing at ${RUNTIME_DIR}. Run \`pnpm build:runtime\` first.`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const app = express();
|
|
47
|
+
app.use(cors());
|
|
48
|
+
app.use("/runtime", express.static(RUNTIME_DIR));
|
|
49
|
+
|
|
50
|
+
const store = getWidgetStore();
|
|
51
|
+
|
|
52
|
+
app.get("/widget/:name/:hash/files", async (req, res) => {
|
|
53
|
+
const { name, hash } = req.params;
|
|
54
|
+
try {
|
|
55
|
+
const widget = await store.getWidget(name, hash);
|
|
56
|
+
if (!widget) {
|
|
57
|
+
res.status(404).json({ error: "Widget not found" });
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
res.json({ files: widget.files, entry: widget.entry, manifest: widget.manifest });
|
|
61
|
+
} catch (err) {
|
|
62
|
+
error("e2e-visual", "Error serving widget files:", err);
|
|
63
|
+
res.status(500).json({ error: "Internal server error" });
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
app.get("/health", (_req, res) => {
|
|
68
|
+
res.json({ status: "ok", service: "patchwork-e2e-visual" });
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
await new Promise<void>((resolve) => {
|
|
72
|
+
app.listen(WIDGET_PORT, "0.0.0.0", () => {
|
|
73
|
+
log("e2e-visual", `Widget server listening on http://0.0.0.0:${WIDGET_PORT}`);
|
|
74
|
+
resolve();
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function printSummaryTable(entries: WidgetPreviewEntry[]): void {
|
|
80
|
+
console.log("\n┌─────────────────────┬─────────────────────────────────────────────────┐");
|
|
81
|
+
console.log("│ Widget │ Preview URL │");
|
|
82
|
+
console.log("├─────────────────────┼─────────────────────────────────────────────────┤");
|
|
83
|
+
for (const entry of entries) {
|
|
84
|
+
const namePad = entry.name.padEnd(19);
|
|
85
|
+
const urlPad = entry.url.length > 47 ? entry.url : entry.url.padEnd(47);
|
|
86
|
+
console.log(`│ ${namePad} │ ${urlPad} │`);
|
|
87
|
+
}
|
|
88
|
+
console.log("└─────────────────────┴─────────────────────────────────────────────────┘\n");
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async function savePreviewManifest(entries: WidgetPreviewEntry[]): Promise<void> {
|
|
92
|
+
await mkdir(ARTIFACTS_DIR, { recursive: true });
|
|
93
|
+
const manifestPath = resolve(ARTIFACTS_DIR, "preview-urls.json");
|
|
94
|
+
await writeFile(manifestPath, JSON.stringify({ generatedAt: new Date().toISOString(), widgets: entries }, null, 2));
|
|
95
|
+
log("e2e-visual", `Preview manifest saved to ${manifestPath}`);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async function main(): Promise<void> {
|
|
99
|
+
const { tunnel } = parseArgs();
|
|
100
|
+
|
|
101
|
+
log("e2e-visual", "Saving reference widgets...");
|
|
102
|
+
const widgets = await saveReferenceWidgets();
|
|
103
|
+
log("e2e-visual", `Saved ${widgets.length} widget(s)`);
|
|
104
|
+
|
|
105
|
+
log("e2e-visual", "Starting widget server...");
|
|
106
|
+
await startWidgetServer();
|
|
107
|
+
|
|
108
|
+
let baseUrl = `http://localhost:${WIDGET_PORT}`;
|
|
109
|
+
|
|
110
|
+
if (tunnel) {
|
|
111
|
+
log("e2e-visual", "Starting Cloudflare tunnel...");
|
|
112
|
+
try {
|
|
113
|
+
baseUrl = await startTunnel(WIDGET_PORT);
|
|
114
|
+
log("e2e-visual", `Tunnel established: ${baseUrl}`);
|
|
115
|
+
} catch (err) {
|
|
116
|
+
error("e2e-visual", "Failed to start tunnel, using localhost:", err);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const entries: WidgetPreviewEntry[] = widgets.map((w) => ({
|
|
121
|
+
name: w.name,
|
|
122
|
+
hash: w.hash,
|
|
123
|
+
url: `${baseUrl}/runtime/?widget=${w.name}/${w.hash}`,
|
|
124
|
+
}));
|
|
125
|
+
|
|
126
|
+
printSummaryTable(entries);
|
|
127
|
+
await savePreviewManifest(entries);
|
|
128
|
+
|
|
129
|
+
if (tunnel) {
|
|
130
|
+
log("e2e-visual", "Tunnel mode active. Press Ctrl+C to shut down cleanly.");
|
|
131
|
+
const shutdown = () => {
|
|
132
|
+
log("e2e-visual", "Shutting down...");
|
|
133
|
+
stopTunnel();
|
|
134
|
+
process.exit(0);
|
|
135
|
+
};
|
|
136
|
+
process.on("SIGINT", shutdown);
|
|
137
|
+
process.on("SIGTERM", shutdown);
|
|
138
|
+
} else {
|
|
139
|
+
log("e2e-visual", "Done (no tunnel). Use --tunnel for live preview URLs.");
|
|
140
|
+
process.exit(0);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
main().catch((err: unknown) => {
|
|
145
|
+
error("e2e-visual", "Fatal error:", err);
|
|
146
|
+
stopTunnel();
|
|
147
|
+
process.exit(1);
|
|
148
|
+
});
|