mockpit 0.0.0-stage → 1.0.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/LICENSE +22 -0
- package/README.md +232 -2
- package/bin/demoData.js +132 -0
- package/bin/initDesign.js +846 -0
- package/bin/mockpit.js +1830 -0
- package/bin/serveUrl.d.ts +1 -0
- package/bin/serveUrl.js +7 -0
- package/dist/mcp/server.d.ts +2 -0
- package/dist/mcp/server.js +230 -0
- package/dist/server/apiViews.d.ts +329 -0
- package/dist/server/apiViews.js +135 -0
- package/dist/server/app.d.ts +53 -0
- package/dist/server/app.js +3112 -0
- package/dist/server/base64.d.ts +1 -0
- package/dist/server/base64.js +16 -0
- package/dist/server/cdn.d.ts +6 -0
- package/dist/server/cdn.js +33 -0
- package/dist/server/codeRunner.d.ts +5 -0
- package/dist/server/codeRunner.js +89 -0
- package/dist/server/codeWorker.d.ts +1 -0
- package/dist/server/codeWorker.js +196 -0
- package/dist/server/designGuide.d.ts +16 -0
- package/dist/server/designGuide.js +311 -0
- package/dist/server/events.d.ts +53 -0
- package/dist/server/events.js +14 -0
- package/dist/server/feedbackBatch.d.ts +43 -0
- package/dist/server/feedbackBatch.js +98 -0
- package/dist/server/icons.d.ts +50 -0
- package/dist/server/icons.js +225 -0
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.js +64 -0
- package/dist/server/kits.d.ts +36 -0
- package/dist/server/kits.js +279 -0
- package/dist/server/knobs.d.ts +15 -0
- package/dist/server/knobs.js +333 -0
- package/dist/server/mcpHttp.d.ts +53 -0
- package/dist/server/mcpHttp.js +143 -0
- package/dist/server/mcpSpec.d.ts +191 -0
- package/dist/server/mcpSpec.js +402 -0
- package/dist/server/parts.d.ts +31 -0
- package/dist/server/parts.js +338 -0
- package/dist/server/postMarkdown.d.ts +10 -0
- package/dist/server/postMarkdown.js +271 -0
- package/dist/server/postSurfaces.d.ts +40 -0
- package/dist/server/postSurfaces.js +407 -0
- package/dist/server/public.d.ts +5 -0
- package/dist/server/public.js +5 -0
- package/dist/server/push.d.ts +45 -0
- package/dist/server/push.js +234 -0
- package/dist/server/richRender.d.ts +15 -0
- package/dist/server/richRender.js +392 -0
- package/dist/server/run.d.ts +82 -0
- package/dist/server/run.js +290 -0
- package/dist/server/runApi.d.ts +6 -0
- package/dist/server/runApi.js +82 -0
- package/dist/server/slots.d.ts +14 -0
- package/dist/server/slots.js +71 -0
- package/dist/server/sqlStore.d.ts +119 -0
- package/dist/server/sqlStore.js +954 -0
- package/dist/server/sqliteStorage.d.ts +2 -0
- package/dist/server/sqliteStorage.js +90 -0
- package/dist/server/surfacePage.d.ts +48 -0
- package/dist/server/surfacePage.js +1099 -0
- package/dist/server/themes.d.ts +48 -0
- package/dist/server/themes.js +305 -0
- package/dist/server/types.d.ts +599 -0
- package/dist/server/types.js +299 -0
- package/dist/workers/public.d.ts +1 -0
- package/dist/workers/public.js +3 -0
- package/extensions/mockpit.js +703 -0
- package/guide/AGENT_SETUP.md +23 -0
- package/guide/topics/asks.md +87 -0
- package/guide/topics/feedback.md +127 -0
- package/guide/topics/html.md +175 -0
- package/guide/topics/http.md +76 -0
- package/guide/topics/knobs.md +72 -0
- package/guide/topics/scripts.md +129 -0
- package/guide/topics/surfaces.md +100 -0
- package/package.json +165 -4
- package/skills/launch-video/SKILL.md +159 -0
- package/skills/mockpit/SKILL.md +40 -0
- package/viewer/dist/index.html +1922 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function decodeBase64(b64: string): Uint8Array;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// base64 -> bytes, runtime-agnostic (atob is a global in Node and Workers).
|
|
2
|
+
// atob throws on malformed input; rethrow as a clean error so callers turn it
|
|
3
|
+
// into a 400 instead of letting a raw DOMException surface as a 500.
|
|
4
|
+
export function decodeBase64(b64) {
|
|
5
|
+
let bin;
|
|
6
|
+
try {
|
|
7
|
+
bin = atob(b64);
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
throw new Error("invalid base64 in `data`");
|
|
11
|
+
}
|
|
12
|
+
const bytes = new Uint8Array(bin.length);
|
|
13
|
+
for (let i = 0; i < bin.length; i++)
|
|
14
|
+
bytes[i] = bin.charCodeAt(i);
|
|
15
|
+
return bytes;
|
|
16
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// Origins html surfaces may load external resources from. Mirrors the allowlist
|
|
2
|
+
// agents already know from Claude's inline widget surface. One list, so the CSP
|
|
3
|
+
// and the kit URL check can never disagree about what a sandbox may fetch.
|
|
4
|
+
//
|
|
5
|
+
// Runtime-agnostic (no node imports).
|
|
6
|
+
export const CDN_ALLOWLIST = [
|
|
7
|
+
"https://cdnjs.cloudflare.com",
|
|
8
|
+
"https://esm.sh",
|
|
9
|
+
"https://cdn.jsdelivr.net",
|
|
10
|
+
"https://unpkg.com",
|
|
11
|
+
"https://fonts.googleapis.com",
|
|
12
|
+
"https://fonts.gstatic.com",
|
|
13
|
+
];
|
|
14
|
+
// A kit URL becomes a <link>/<script> in the sandboxed document, so it must be
|
|
15
|
+
// something the CSP would load anyway; anything else would silently fail there.
|
|
16
|
+
// Returns the normalized URL, or an error naming the allowed origins.
|
|
17
|
+
export function checkCdnUrl(raw, field) {
|
|
18
|
+
const allowed = `${field} must be an https URL on ${CDN_ALLOWLIST.map((o) => o.slice(8)).join(", ")}`;
|
|
19
|
+
if (typeof raw !== "string" || raw.length > 2048)
|
|
20
|
+
return { error: allowed };
|
|
21
|
+
let url;
|
|
22
|
+
try {
|
|
23
|
+
url = new URL(raw);
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return { error: allowed };
|
|
27
|
+
}
|
|
28
|
+
if (url.protocol !== "https:" || url.username || url.password)
|
|
29
|
+
return { error: allowed };
|
|
30
|
+
if (!CDN_ALLOWLIST.includes(url.origin))
|
|
31
|
+
return { error: allowed };
|
|
32
|
+
return { url: url.href };
|
|
33
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// The Node executor for `run`: one worker_thread per run, QuickJS in wasm
|
|
2
|
+
// inside it, host calls relayed to the main thread where the flows live.
|
|
3
|
+
// Node-only (worker_threads, fs); server/index.ts wires it in.
|
|
4
|
+
import { readFile } from "node:fs/promises";
|
|
5
|
+
import { dirname, join } from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { Worker } from "node:worker_threads";
|
|
8
|
+
// The source tree runs codeWorker.ts directly; the published package runs the
|
|
9
|
+
// compiled dist/server/codeWorker.js next to this file.
|
|
10
|
+
const HERE = fileURLToPath(import.meta.url);
|
|
11
|
+
const WORKER = join(dirname(HERE), HERE.endsWith(".ts") ? "codeWorker.ts" : "codeWorker.js");
|
|
12
|
+
// @types/node declares the WebAssembly namespace without its values.
|
|
13
|
+
const wasm = globalThis.WebAssembly;
|
|
14
|
+
const COVERAGE = process.env.NODE_V8_COVERAGE;
|
|
15
|
+
export function createNodeExecutor({ graceMs = 500 } = {}) {
|
|
16
|
+
// Compiled once per process and posted to each worker.
|
|
17
|
+
let module;
|
|
18
|
+
const load = () => (module ??= readFile(fileURLToPath(import.meta.resolve("quickjs-wasi/quickjs.wasm"))).then((bytes) => wasm.compile(bytes)));
|
|
19
|
+
return {
|
|
20
|
+
async run(req) {
|
|
21
|
+
const { limits } = req;
|
|
22
|
+
if (req.signal.aborted)
|
|
23
|
+
return stopped("aborted", "the caller went away");
|
|
24
|
+
const clock = new Float64Array(new SharedArrayBuffer(16));
|
|
25
|
+
const worker = new Worker(WORKER, {
|
|
26
|
+
workerData: {
|
|
27
|
+
module: await load(),
|
|
28
|
+
code: req.code,
|
|
29
|
+
cpuMs: limits.cpuMs,
|
|
30
|
+
memoryBytes: limits.memoryBytes,
|
|
31
|
+
stackBytes: limits.stackBytes,
|
|
32
|
+
clock: clock.buffer,
|
|
33
|
+
},
|
|
34
|
+
// The guest never sees the environment, but the thread needn't hold
|
|
35
|
+
// secrets either. Coverage is the one variable it needs to report.
|
|
36
|
+
env: COVERAGE ? { NODE_V8_COVERAGE: COVERAGE } : {},
|
|
37
|
+
stdout: true,
|
|
38
|
+
stderr: true,
|
|
39
|
+
// A backstop only: the guest heap is wasm linear memory, not V8 old-gen.
|
|
40
|
+
resourceLimits: { maxOldGenerationSizeMb: 64 },
|
|
41
|
+
});
|
|
42
|
+
return new Promise((resolve) => {
|
|
43
|
+
let done = false;
|
|
44
|
+
const finish = (result, kill) => {
|
|
45
|
+
if (done)
|
|
46
|
+
return;
|
|
47
|
+
done = true;
|
|
48
|
+
clearTimeout(deadline);
|
|
49
|
+
clearInterval(watchdog);
|
|
50
|
+
req.signal.removeEventListener("abort", onAbort);
|
|
51
|
+
if (kill)
|
|
52
|
+
void worker.terminate();
|
|
53
|
+
resolve(result);
|
|
54
|
+
};
|
|
55
|
+
const onAbort = () => finish(stopped("aborted", "the caller went away"), true);
|
|
56
|
+
req.signal.addEventListener("abort", onAbort, { once: true });
|
|
57
|
+
const deadline = setTimeout(() => finish(stopped("timeout", `wall deadline of ${limits.deadlineMs / 1000} s reached`), true), limits.deadlineMs);
|
|
58
|
+
// A builtin (sort, repeat, a regex) can run long without reaching the
|
|
59
|
+
// interrupt handler; only terminate() stops it.
|
|
60
|
+
const watchdog = setInterval(() => {
|
|
61
|
+
const start = clock[0];
|
|
62
|
+
if (start && clock[1] + (Date.now() - start) > limits.cpuMs + graceMs) {
|
|
63
|
+
finish(stopped("limit", `CPU budget of ${limits.cpuMs} ms exhausted`), true);
|
|
64
|
+
}
|
|
65
|
+
}, Math.max(20, Math.min(250, graceMs / 2)));
|
|
66
|
+
worker.on("message", (m) => {
|
|
67
|
+
if (done)
|
|
68
|
+
return;
|
|
69
|
+
if (m.t === "print")
|
|
70
|
+
return req.print(String(m.line));
|
|
71
|
+
if (m.t === "done")
|
|
72
|
+
return finish(m.outcome, false);
|
|
73
|
+
req.call(m.name, m.args).then((json) => done || worker.postMessage({ id: m.id, ok: true, json }), (err) => done ||
|
|
74
|
+
worker.postMessage({
|
|
75
|
+
id: m.id,
|
|
76
|
+
ok: false,
|
|
77
|
+
message: err instanceof Error ? err.message : String(err),
|
|
78
|
+
}));
|
|
79
|
+
});
|
|
80
|
+
worker.on("error", (err) => finish(stopped("limit", `sandbox stopped: ${err.message}`), true));
|
|
81
|
+
worker.on("exit", () => finish(stopped("limit", "sandbox exited"), false));
|
|
82
|
+
});
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
const stopped = (kind, message) => ({
|
|
87
|
+
ok: false,
|
|
88
|
+
error: { kind, message },
|
|
89
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
// worker_thread entry for one run (Node only). QuickJS runs inside wasm here;
|
|
2
|
+
// the guest reaches the host only through two functions the prelude captures,
|
|
3
|
+
// and everything crossing them is a string, so no guest getter or proxy ever
|
|
4
|
+
// runs host-side. The parent owns the wall deadline and can terminate() this
|
|
5
|
+
// thread; the CPU and memory checks below are the soft stops in front of that.
|
|
6
|
+
import { parentPort, workerData } from "node:worker_threads";
|
|
7
|
+
import { JSException, QuickJS } from "quickjs-wasi";
|
|
8
|
+
const input = workerData;
|
|
9
|
+
const port = parentPort;
|
|
10
|
+
const clock = new Float64Array(input.clock);
|
|
11
|
+
// The call site is captured synchronously: after an await QuickJS keeps no
|
|
12
|
+
// caller frames, so a host rejection would otherwise carry no line.
|
|
13
|
+
const PRELUDE = `(function (call, out) {
|
|
14
|
+
const fmt = (v) => typeof v === "string" ? v : (() => { try { return JSON.stringify(v); } catch { return String(v); } })();
|
|
15
|
+
const print = (...a) => out(a.map(fmt).join(" "));
|
|
16
|
+
const fn = (name) => (...args) => {
|
|
17
|
+
const site = new Error();
|
|
18
|
+
let json;
|
|
19
|
+
try { json = JSON.stringify(args); } catch (e) { return Promise.reject(e); }
|
|
20
|
+
return call(name, json === undefined ? "[]" : json).then(JSON.parse, (e) => {
|
|
21
|
+
const err = new Error(e.message);
|
|
22
|
+
err.stack = site.stack.split("\\n").slice(1).join("\\n");
|
|
23
|
+
throw err;
|
|
24
|
+
});
|
|
25
|
+
};
|
|
26
|
+
globalThis.mockpit = Object.freeze({
|
|
27
|
+
guide: fn("guide"), read: fn("read"), publish: fn("publish"), ask: fn("ask"),
|
|
28
|
+
feedback: fn("feedback"), say: fn("say"), export: fn("export"), upload: fn("upload"),
|
|
29
|
+
});
|
|
30
|
+
globalThis.print = print;
|
|
31
|
+
globalThis.console = Object.freeze({ log: print, info: print, warn: print, error: print });
|
|
32
|
+
const fail = (e) => ({ ok: false, name: String(e && e.name), message: String(e && e.message !== undefined ? e.message : e), stack: String(e && e.stack || "") });
|
|
33
|
+
// A value JSON can't encode (a BigInt, a throwing toJSON) fails the script.
|
|
34
|
+
return (f) => f().then((v) => {
|
|
35
|
+
try { return { ok: true, json: v === undefined ? undefined : JSON.stringify(v) }; } catch (e) { return fail(e); }
|
|
36
|
+
}, fail);
|
|
37
|
+
})`;
|
|
38
|
+
const TS_HINT = /^\s*(interface|type\s+\w+\s*=|enum\s)|\b(const|let|var)\s+\w+\s*:\s*\w|\)\s*:\s*(string|number|boolean|void|Promise)\b|\bas\s+(const|string|number|any|unknown)\b/m;
|
|
39
|
+
// The wrapper opens on line 1 so stack positions match the script as written;
|
|
40
|
+
// only line 1's columns shift by the wrapper's length.
|
|
41
|
+
const WRAP = "(async () => {";
|
|
42
|
+
function position(stack, message) {
|
|
43
|
+
const m = /run\.js:(\d+)(?::(\d+))?/.exec(`${stack}\n${message}`);
|
|
44
|
+
if (!m)
|
|
45
|
+
return {};
|
|
46
|
+
const line = Number(m[1]);
|
|
47
|
+
if (!m[2])
|
|
48
|
+
return { line };
|
|
49
|
+
const column = Number(m[2]);
|
|
50
|
+
return { line, column: line === 1 ? Math.max(1, column - WRAP.length) : column };
|
|
51
|
+
}
|
|
52
|
+
function scriptError(name, message, stack, code) {
|
|
53
|
+
if (/out of memory/i.test(message)) {
|
|
54
|
+
return { ok: false, error: { kind: "limit", message: "memory limit reached" } };
|
|
55
|
+
}
|
|
56
|
+
let text = name && name !== "Error" && name !== "undefined" ? `${name}: ${message}` : message;
|
|
57
|
+
if (name === "SyntaxError" && TS_HINT.test(code)) {
|
|
58
|
+
text += " (this looks like TypeScript; write plain JavaScript)";
|
|
59
|
+
}
|
|
60
|
+
return { ok: false, error: { kind: "script", message: text, ...position(stack, message) } };
|
|
61
|
+
}
|
|
62
|
+
// Discard QuickJS's own stdout/stderr: the default shim would write to the
|
|
63
|
+
// host's process.stdout.
|
|
64
|
+
const discardStdio = (memory) => ({
|
|
65
|
+
fd_write(_fd, iovs, len, nwritten) {
|
|
66
|
+
const view = new DataView(memory.buffer);
|
|
67
|
+
let n = 0;
|
|
68
|
+
for (let i = 0; i < len; i++)
|
|
69
|
+
n += view.getUint32(iovs + i * 8 + 4, true);
|
|
70
|
+
view.setUint32(nwritten, n, true);
|
|
71
|
+
return 0;
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
const pending = new Map();
|
|
75
|
+
let nextId = 1;
|
|
76
|
+
let wake = () => { };
|
|
77
|
+
port.on("message", (m) => {
|
|
78
|
+
const p = pending.get(m.id);
|
|
79
|
+
if (!p)
|
|
80
|
+
return;
|
|
81
|
+
pending.delete(m.id);
|
|
82
|
+
if (m.ok)
|
|
83
|
+
p.resolve(m.json ?? "null");
|
|
84
|
+
else
|
|
85
|
+
p.reject(new Error(m.message));
|
|
86
|
+
wake();
|
|
87
|
+
});
|
|
88
|
+
async function main() {
|
|
89
|
+
let stop = null;
|
|
90
|
+
let memory;
|
|
91
|
+
const burst = (fn) => {
|
|
92
|
+
clock[0] = Date.now();
|
|
93
|
+
try {
|
|
94
|
+
return fn();
|
|
95
|
+
}
|
|
96
|
+
finally {
|
|
97
|
+
clock[1] += Date.now() - clock[0];
|
|
98
|
+
clock[0] = 0;
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
const vm = await QuickJS.create({
|
|
102
|
+
wasm: input.module,
|
|
103
|
+
memoryLimit: input.memoryBytes,
|
|
104
|
+
maxStackSize: input.stackBytes,
|
|
105
|
+
wasi: (m) => {
|
|
106
|
+
memory = m;
|
|
107
|
+
return discardStdio(m);
|
|
108
|
+
},
|
|
109
|
+
// Called about every 10k bytecode ticks, never inside a long builtin: the
|
|
110
|
+
// parent's watchdog covers that case with terminate().
|
|
111
|
+
interruptHandler: () => {
|
|
112
|
+
if (stop)
|
|
113
|
+
return true;
|
|
114
|
+
if (clock[0] && clock[1] + (Date.now() - clock[0]) > input.cpuMs) {
|
|
115
|
+
stop = { kind: "limit", message: `CPU budget of ${input.cpuMs} ms exhausted` };
|
|
116
|
+
}
|
|
117
|
+
else if (memory && memory.buffer.byteLength >= input.memoryBytes) {
|
|
118
|
+
// Linear memory never shrinks, so reaching the cap means the guest hit
|
|
119
|
+
// the limit; stop rather than let a catch-and-retry loop spin in GC.
|
|
120
|
+
stop = { kind: "limit", message: "memory limit reached" };
|
|
121
|
+
}
|
|
122
|
+
return stop !== null;
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
try {
|
|
126
|
+
const call = vm.newFunction("__call", (nameH, argsH) => {
|
|
127
|
+
const name = nameH.toString();
|
|
128
|
+
const args = argsH.toString();
|
|
129
|
+
const deferred = vm.newPromise();
|
|
130
|
+
const id = nextId++;
|
|
131
|
+
pending.set(id, {
|
|
132
|
+
resolve: (json) => deferred.resolve(vm.newString(json)),
|
|
133
|
+
reject: (err) => deferred.reject(vm.newError(err.message)),
|
|
134
|
+
});
|
|
135
|
+
port.postMessage({ t: "call", id, name, args });
|
|
136
|
+
return deferred.handle;
|
|
137
|
+
});
|
|
138
|
+
const out = vm.newFunction("__out", (s) => {
|
|
139
|
+
port.postMessage({ t: "print", line: s.toString() });
|
|
140
|
+
return vm.undefined;
|
|
141
|
+
});
|
|
142
|
+
const runner = vm.callFunction(vm.evalCode(PRELUDE, "prelude.js"), vm.undefined, call, out);
|
|
143
|
+
let fnH;
|
|
144
|
+
try {
|
|
145
|
+
fnH = burst(() => vm.evalCode(`${WRAP}${input.code}\n})`, "run.js"));
|
|
146
|
+
}
|
|
147
|
+
catch (e) {
|
|
148
|
+
if (e instanceof JSException)
|
|
149
|
+
return scriptError(e.name, e.message, e.stack ?? "", input.code);
|
|
150
|
+
throw e;
|
|
151
|
+
}
|
|
152
|
+
const settled = burst(() => vm.callFunction(runner, vm.undefined, fnH));
|
|
153
|
+
for (;;) {
|
|
154
|
+
burst(() => vm.executePendingJobs());
|
|
155
|
+
if (stop)
|
|
156
|
+
return { ok: false, error: stop };
|
|
157
|
+
if (settled.promiseState !== 0)
|
|
158
|
+
break;
|
|
159
|
+
if (pending.size === 0) {
|
|
160
|
+
return {
|
|
161
|
+
ok: false,
|
|
162
|
+
error: { kind: "script", message: "the script awaits something that can never settle" },
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
await new Promise((resolve) => (wake = resolve));
|
|
166
|
+
}
|
|
167
|
+
// The runner settles every outcome as a value; it never rejects.
|
|
168
|
+
const r = await vm.resolvePromise(settled);
|
|
169
|
+
const result = vm.dump("value" in r ? r.value : r.error);
|
|
170
|
+
if (result.ok)
|
|
171
|
+
return { ok: true, json: result.json };
|
|
172
|
+
return scriptError(result.name ?? "", result.message ?? "", result.stack ?? "", input.code);
|
|
173
|
+
}
|
|
174
|
+
catch (e) {
|
|
175
|
+
if (stop)
|
|
176
|
+
return { ok: false, error: stop };
|
|
177
|
+
if (e instanceof JSException)
|
|
178
|
+
return scriptError(e.name, e.message, e.stack ?? "", input.code);
|
|
179
|
+
// A trap (stack overflow on the host side, an import that threw) leaves
|
|
180
|
+
// the VM unusable; it is discarded with this thread.
|
|
181
|
+
return {
|
|
182
|
+
ok: false,
|
|
183
|
+
error: { kind: "limit", message: `sandbox stopped: ${e instanceof Error ? e.message : e}` },
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
finally {
|
|
187
|
+
try {
|
|
188
|
+
vm.dispose();
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
// a trapped VM may refuse to dispose; the thread exits anyway
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
port.postMessage({ t: "done", outcome: await main() });
|
|
196
|
+
port.close();
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { DesignSettings } from "./types.ts";
|
|
2
|
+
export declare const GUIDE_TOPICS: readonly ["knobs", "asks", "surfaces", "html", "feedback", "http", "scripts"];
|
|
3
|
+
export type GuideTopic = (typeof GUIDE_TOPICS)[number];
|
|
4
|
+
export declare const isGuideTopic: (id: unknown) => id is GuideTopic;
|
|
5
|
+
export declare const unknownTopicMessage: (topic: string) => string;
|
|
6
|
+
export declare function iconsLine(design: DesignSettings | null): string;
|
|
7
|
+
/**
|
|
8
|
+
* What the repo's DESIGN.md, DTCG tokens and shadcn components.json say, as
|
|
9
|
+
* init stored them. Empty when the repo has none.
|
|
10
|
+
*/
|
|
11
|
+
export declare function designFilesSection(design: DesignSettings | null): string;
|
|
12
|
+
/**
|
|
13
|
+
* The project-aware brief. `design` is the stored `design:<project>` settings,
|
|
14
|
+
* or null for a project that never ran `mockpit init`.
|
|
15
|
+
*/
|
|
16
|
+
export declare function renderBriefGuide(design: DesignSettings | null): string;
|