dreamboard 0.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/README.md +239 -0
- package/dist/chunk-2H7UOFLK.js +11 -0
- package/dist/chunk-2H7UOFLK.js.map +1 -0
- package/dist/chunk-FK6CWXQR.js +3479 -0
- package/dist/chunk-FK6CWXQR.js.map +1 -0
- package/dist/chunk-MP7IBNWW.js +13289 -0
- package/dist/chunk-MP7IBNWW.js.map +1 -0
- package/dist/dist-B3R64F6G.js +99 -0
- package/dist/dist-B3R64F6G.js.map +1 -0
- package/dist/embedded-harness-PF2LCIWC.js +345 -0
- package/dist/embedded-harness-PF2LCIWC.js.map +1 -0
- package/dist/index.js +25773 -0
- package/dist/index.js.map +1 -0
- package/dist/prompt-GMZABCJC.js +756 -0
- package/dist/prompt-GMZABCJC.js.map +1 -0
- package/package.json +40 -0
- package/skills/dreamboard/SKILL.md +119 -0
- package/skills/dreamboard/references/adversarial-testing.md +113 -0
- package/skills/dreamboard/references/all-players-tracking.md +75 -0
- package/skills/dreamboard/references/api-reference.md +193 -0
- package/skills/dreamboard/references/app-best-practices.md +86 -0
- package/skills/dreamboard/references/hands-vs-decks.md +86 -0
- package/skills/dreamboard/references/manifest-authoring.md +590 -0
- package/skills/dreamboard/references/phase-handlers.md +134 -0
- package/skills/dreamboard/references/rule-authoring.md +142 -0
- package/skills/dreamboard/references/scenario-format.md +99 -0
- package/skills/dreamboard/references/test-harness.md +225 -0
- package/skills/dreamboard/references/tts-migration-and-extractor.md +91 -0
- package/skills/dreamboard/references/ui-best-practices.md +158 -0
- package/skills/dreamboard/references/ui-genre-resource-management.md +187 -0
- package/skills/dreamboard/references/ui-genre-trick-taking.md +110 -0
- package/skills/dreamboard/references/ui-genre-worker-placement.md +143 -0
- package/skills/dreamboard/references/ui-style-guide.md +54 -0
- package/skills/dreamboard/scripts/events-extract.mjs +218 -0
- package/skills/dreamboard/scripts/extract_tts.py +1178 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
AuthAdminApi_default,
|
|
4
|
+
AuthApiError,
|
|
5
|
+
AuthClient_default,
|
|
6
|
+
AuthError,
|
|
7
|
+
AuthImplicitGrantRedirectError,
|
|
8
|
+
AuthInvalidCredentialsError,
|
|
9
|
+
AuthInvalidJwtError,
|
|
10
|
+
AuthInvalidTokenResponseError,
|
|
11
|
+
AuthPKCECodeVerifierMissingError,
|
|
12
|
+
AuthPKCEGrantCodeExchangeError,
|
|
13
|
+
AuthRetryableFetchError,
|
|
14
|
+
AuthSessionMissingError,
|
|
15
|
+
AuthUnknownError,
|
|
16
|
+
AuthWeakPasswordError,
|
|
17
|
+
CustomAuthError,
|
|
18
|
+
FunctionRegion,
|
|
19
|
+
FunctionsError,
|
|
20
|
+
FunctionsFetchError,
|
|
21
|
+
FunctionsHttpError,
|
|
22
|
+
FunctionsRelayError,
|
|
23
|
+
GoTrueAdminApi,
|
|
24
|
+
GoTrueClient_default,
|
|
25
|
+
NavigatorLockAcquireTimeoutError,
|
|
26
|
+
PostgrestError,
|
|
27
|
+
REALTIME_CHANNEL_STATES,
|
|
28
|
+
REALTIME_LISTEN_TYPES,
|
|
29
|
+
REALTIME_POSTGRES_CHANGES_LISTEN_EVENT,
|
|
30
|
+
REALTIME_PRESENCE_LISTEN_EVENTS,
|
|
31
|
+
REALTIME_SUBSCRIBE_STATES,
|
|
32
|
+
RealtimeChannel,
|
|
33
|
+
RealtimeClient,
|
|
34
|
+
RealtimePresence,
|
|
35
|
+
SIGN_OUT_SCOPES,
|
|
36
|
+
SupabaseClient,
|
|
37
|
+
createClient,
|
|
38
|
+
internals,
|
|
39
|
+
isAuthApiError,
|
|
40
|
+
isAuthError,
|
|
41
|
+
isAuthImplicitGrantRedirectError,
|
|
42
|
+
isAuthPKCECodeVerifierMissingError,
|
|
43
|
+
isAuthRetryableFetchError,
|
|
44
|
+
isAuthSessionMissingError,
|
|
45
|
+
isAuthWeakPasswordError,
|
|
46
|
+
navigatorLock,
|
|
47
|
+
processLock,
|
|
48
|
+
websocket_factory_default
|
|
49
|
+
} from "./chunk-MP7IBNWW.js";
|
|
50
|
+
import "./chunk-2H7UOFLK.js";
|
|
51
|
+
export {
|
|
52
|
+
AuthAdminApi_default as AuthAdminApi,
|
|
53
|
+
AuthApiError,
|
|
54
|
+
AuthClient_default as AuthClient,
|
|
55
|
+
AuthError,
|
|
56
|
+
AuthImplicitGrantRedirectError,
|
|
57
|
+
AuthInvalidCredentialsError,
|
|
58
|
+
AuthInvalidJwtError,
|
|
59
|
+
AuthInvalidTokenResponseError,
|
|
60
|
+
AuthPKCECodeVerifierMissingError,
|
|
61
|
+
AuthPKCEGrantCodeExchangeError,
|
|
62
|
+
AuthRetryableFetchError,
|
|
63
|
+
AuthSessionMissingError,
|
|
64
|
+
AuthUnknownError,
|
|
65
|
+
AuthWeakPasswordError,
|
|
66
|
+
CustomAuthError,
|
|
67
|
+
FunctionRegion,
|
|
68
|
+
FunctionsError,
|
|
69
|
+
FunctionsFetchError,
|
|
70
|
+
FunctionsHttpError,
|
|
71
|
+
FunctionsRelayError,
|
|
72
|
+
GoTrueAdminApi,
|
|
73
|
+
GoTrueClient_default as GoTrueClient,
|
|
74
|
+
NavigatorLockAcquireTimeoutError,
|
|
75
|
+
PostgrestError,
|
|
76
|
+
REALTIME_CHANNEL_STATES,
|
|
77
|
+
REALTIME_LISTEN_TYPES,
|
|
78
|
+
REALTIME_POSTGRES_CHANGES_LISTEN_EVENT,
|
|
79
|
+
REALTIME_PRESENCE_LISTEN_EVENTS,
|
|
80
|
+
REALTIME_SUBSCRIBE_STATES,
|
|
81
|
+
RealtimeChannel,
|
|
82
|
+
RealtimeClient,
|
|
83
|
+
RealtimePresence,
|
|
84
|
+
SIGN_OUT_SCOPES,
|
|
85
|
+
SupabaseClient,
|
|
86
|
+
websocket_factory_default as WebSocketFactory,
|
|
87
|
+
createClient,
|
|
88
|
+
isAuthApiError,
|
|
89
|
+
isAuthError,
|
|
90
|
+
isAuthImplicitGrantRedirectError,
|
|
91
|
+
isAuthPKCECodeVerifierMissingError,
|
|
92
|
+
isAuthRetryableFetchError,
|
|
93
|
+
isAuthSessionMissingError,
|
|
94
|
+
isAuthWeakPasswordError,
|
|
95
|
+
internals as lockInternals,
|
|
96
|
+
navigatorLock,
|
|
97
|
+
processLock
|
|
98
|
+
};
|
|
99
|
+
//# sourceMappingURL=dist-B3R64F6G.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
client,
|
|
4
|
+
consola,
|
|
5
|
+
exists,
|
|
6
|
+
hashContent,
|
|
7
|
+
loadManifest
|
|
8
|
+
} from "./chunk-FK6CWXQR.js";
|
|
9
|
+
import "./chunk-2H7UOFLK.js";
|
|
10
|
+
|
|
11
|
+
// src/services/testing/embedded-harness.ts
|
|
12
|
+
import { createHash } from "crypto";
|
|
13
|
+
import { spawn } from "child_process";
|
|
14
|
+
import { createWriteStream } from "fs";
|
|
15
|
+
import { mkdir, mkdtemp, readFile, rm } from "fs/promises";
|
|
16
|
+
import { tmpdir } from "os";
|
|
17
|
+
import path from "path";
|
|
18
|
+
import { createInterface } from "readline";
|
|
19
|
+
import { fileURLToPath } from "url";
|
|
20
|
+
var READY_PREFIX = "HARNESS_READY ";
|
|
21
|
+
var HARNESS_START_IDLE_TIMEOUT_MS = 18e4;
|
|
22
|
+
var HARNESS_START_MAX_TIMEOUT_MS = 6e5;
|
|
23
|
+
var HARNESS_STOP_TIMEOUT_MS = 1e4;
|
|
24
|
+
var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
25
|
+
async function startEmbeddedHarnessSession(options) {
|
|
26
|
+
const repoRoot = await resolveRepoRoot(options.projectRoot);
|
|
27
|
+
const gradlew = await resolveGradleLauncher(repoRoot);
|
|
28
|
+
const fixture = await prepareLocalHarnessFixture(options);
|
|
29
|
+
const logDir = path.join(options.projectRoot, "test", "generated");
|
|
30
|
+
const logFilePath = path.join(logDir, "embedded-harness.log");
|
|
31
|
+
await mkdir(logDir, { recursive: true });
|
|
32
|
+
const previousClientConfig = client.getConfig();
|
|
33
|
+
const child = spawn(
|
|
34
|
+
gradlew.command,
|
|
35
|
+
[
|
|
36
|
+
...gradlew.args,
|
|
37
|
+
"--console=plain",
|
|
38
|
+
":apps:backend:runEmbeddedHarness",
|
|
39
|
+
`-PharnessManifestPath=${path.join(options.projectRoot, "manifest.json")}`,
|
|
40
|
+
`-PharnessBundlePath=${fixture.bundleRoot}`,
|
|
41
|
+
`-PharnessGameId=${fixture.gameId}`,
|
|
42
|
+
`-PharnessManifestId=${fixture.manifestId}`,
|
|
43
|
+
`-PharnessCompiledResultId=${fixture.compiledResultId}`,
|
|
44
|
+
`-PharnessRuleId=${fixture.ruleId}`,
|
|
45
|
+
`-PharnessGameSlug=${fixture.gameSlug}`,
|
|
46
|
+
`-PharnessGameName=${fixture.gameName}`,
|
|
47
|
+
"-PharnessHostEmail=host@example.com",
|
|
48
|
+
"-PharnessGuestEmail=guest@example.com"
|
|
49
|
+
],
|
|
50
|
+
{
|
|
51
|
+
cwd: repoRoot,
|
|
52
|
+
env: process.env,
|
|
53
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
54
|
+
}
|
|
55
|
+
);
|
|
56
|
+
let stopped = false;
|
|
57
|
+
let restoreClientConfig;
|
|
58
|
+
let monitor;
|
|
59
|
+
try {
|
|
60
|
+
monitor = await waitForHarnessReady(child, logFilePath);
|
|
61
|
+
if (options.configureClient !== false) {
|
|
62
|
+
client.setConfig({
|
|
63
|
+
...previousClientConfig,
|
|
64
|
+
baseUrl: monitor.ready.baseUrl,
|
|
65
|
+
headers: {
|
|
66
|
+
...previousClientConfig.headers ?? {},
|
|
67
|
+
Authorization: `Bearer ${monitor.ready.hostToken}`
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
restoreClientConfig = () => client.setConfig(previousClientConfig);
|
|
71
|
+
}
|
|
72
|
+
} catch (error) {
|
|
73
|
+
await terminateChild(child);
|
|
74
|
+
await rm(fixture.bundleRoot, { recursive: true, force: true });
|
|
75
|
+
throw error;
|
|
76
|
+
}
|
|
77
|
+
consola.info(`Embedded harness ready at ${monitor.ready.baseUrl}`);
|
|
78
|
+
return {
|
|
79
|
+
gameId: fixture.gameId,
|
|
80
|
+
compiledResultId: fixture.compiledResultId,
|
|
81
|
+
manifestHash: fixture.manifestHash,
|
|
82
|
+
baseUrl: monitor.ready.baseUrl,
|
|
83
|
+
logFilePath,
|
|
84
|
+
stop: async () => {
|
|
85
|
+
if (stopped) return;
|
|
86
|
+
stopped = true;
|
|
87
|
+
restoreClientConfig?.();
|
|
88
|
+
await terminateChild(child);
|
|
89
|
+
monitor.dispose();
|
|
90
|
+
await rm(fixture.bundleRoot, { recursive: true, force: true });
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
async function prepareLocalHarnessFixture(options) {
|
|
95
|
+
const manifestPath = path.join(options.projectRoot, "manifest.json");
|
|
96
|
+
const manifestContent = await readFile(manifestPath, "utf8");
|
|
97
|
+
await loadManifest(options.projectRoot);
|
|
98
|
+
const appEntryPath = path.join(options.projectRoot, "app", "index.ts");
|
|
99
|
+
if (!await exists(appEntryPath)) {
|
|
100
|
+
throw new Error(
|
|
101
|
+
`Embedded harness requires ${path.relative(options.projectRoot, appEntryPath)}.`
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
const bundleRoot = await mkdtemp(path.join(tmpdir(), "dreamboard-harness-"));
|
|
105
|
+
const bundleEntryPath = path.join(bundleRoot, "src", "app", "index.js");
|
|
106
|
+
await mkdir(path.dirname(bundleEntryPath), { recursive: true });
|
|
107
|
+
const buildResult = await Bun.build({
|
|
108
|
+
entrypoints: [appEntryPath],
|
|
109
|
+
format: "esm",
|
|
110
|
+
target: "browser",
|
|
111
|
+
minify: false,
|
|
112
|
+
sourcemap: "none",
|
|
113
|
+
root: options.projectRoot
|
|
114
|
+
});
|
|
115
|
+
if (!buildResult.success) {
|
|
116
|
+
await rm(bundleRoot, { recursive: true, force: true });
|
|
117
|
+
const issues = buildResult.logs.map((log) => log.message).filter((message) => typeof message === "string" && message.length > 0).join("; ") || "unknown Bun build failure";
|
|
118
|
+
throw new Error(`Failed to bundle local game logic: ${issues}`);
|
|
119
|
+
}
|
|
120
|
+
for (const output of buildResult.outputs) {
|
|
121
|
+
const targetPath = path.join(bundleRoot, "src", output.path);
|
|
122
|
+
await mkdir(path.dirname(targetPath), { recursive: true });
|
|
123
|
+
await Bun.write(targetPath, output);
|
|
124
|
+
}
|
|
125
|
+
const bundleContent = await readFile(bundleEntryPath, "utf8");
|
|
126
|
+
const manifestHash = hashContent(manifestContent);
|
|
127
|
+
const bundleHash = hashContent(bundleContent);
|
|
128
|
+
const gameSlug = options.projectConfig.slug || path.basename(options.projectRoot);
|
|
129
|
+
return {
|
|
130
|
+
bundleRoot,
|
|
131
|
+
manifestHash,
|
|
132
|
+
gameId: normalizeUuid(
|
|
133
|
+
options.projectConfig.gameId,
|
|
134
|
+
`game:${gameSlug}:${options.projectRoot}`
|
|
135
|
+
),
|
|
136
|
+
manifestId: normalizeUuid(
|
|
137
|
+
options.projectConfig.manifestId,
|
|
138
|
+
`manifest:${manifestHash}`
|
|
139
|
+
),
|
|
140
|
+
compiledResultId: stableUuid(`compiled:${manifestHash}:${bundleHash}`),
|
|
141
|
+
ruleId: normalizeUuid(
|
|
142
|
+
options.projectConfig.ruleId,
|
|
143
|
+
`rule:${gameSlug}:${options.projectConfig.ruleId ?? "local"}`
|
|
144
|
+
),
|
|
145
|
+
gameSlug,
|
|
146
|
+
gameName: toTitleCase(gameSlug)
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
async function waitForHarnessReady(child, logFilePath) {
|
|
150
|
+
const stdout = child.stdout;
|
|
151
|
+
const stderr = child.stderr;
|
|
152
|
+
if (!stdout || !stderr) {
|
|
153
|
+
throw new Error("Embedded harness process did not expose stdout/stderr.");
|
|
154
|
+
}
|
|
155
|
+
const logStream = createWriteStream(logFilePath, { flags: "w" });
|
|
156
|
+
stdout.pipe(logStream, { end: false });
|
|
157
|
+
stderr.pipe(logStream, { end: false });
|
|
158
|
+
const stdoutReader = createInterface({ input: stdout });
|
|
159
|
+
const stderrReader = createInterface({ input: stderr });
|
|
160
|
+
const recentLines = [];
|
|
161
|
+
const rememberLine = (line) => {
|
|
162
|
+
if (!line.trim()) return;
|
|
163
|
+
recentLines.push(line);
|
|
164
|
+
while (recentLines.length > 30) {
|
|
165
|
+
recentLines.shift();
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
return await new Promise((resolve, reject) => {
|
|
169
|
+
let settled = false;
|
|
170
|
+
let idleTimeoutHandle;
|
|
171
|
+
let maxTimeoutHandle;
|
|
172
|
+
const dispose = () => {
|
|
173
|
+
stdout.off("data", refreshIdleTimeout);
|
|
174
|
+
stderr.off("data", refreshIdleTimeout);
|
|
175
|
+
stdoutReader.close();
|
|
176
|
+
stderrReader.close();
|
|
177
|
+
stdout.unpipe(logStream);
|
|
178
|
+
stderr.unpipe(logStream);
|
|
179
|
+
logStream.end();
|
|
180
|
+
};
|
|
181
|
+
const finish = (callback) => {
|
|
182
|
+
if (settled) return;
|
|
183
|
+
settled = true;
|
|
184
|
+
if (idleTimeoutHandle) {
|
|
185
|
+
clearTimeout(idleTimeoutHandle);
|
|
186
|
+
}
|
|
187
|
+
if (maxTimeoutHandle) {
|
|
188
|
+
clearTimeout(maxTimeoutHandle);
|
|
189
|
+
}
|
|
190
|
+
callback();
|
|
191
|
+
};
|
|
192
|
+
const refreshIdleTimeout = () => {
|
|
193
|
+
if (settled) {
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
if (idleTimeoutHandle) {
|
|
197
|
+
clearTimeout(idleTimeoutHandle);
|
|
198
|
+
}
|
|
199
|
+
idleTimeoutHandle = setTimeout(() => {
|
|
200
|
+
rejectWithContext(
|
|
201
|
+
`Timed out after ${HARNESS_START_IDLE_TIMEOUT_MS}ms without harness output while waiting for embedded harness readiness.`
|
|
202
|
+
);
|
|
203
|
+
}, HARNESS_START_IDLE_TIMEOUT_MS);
|
|
204
|
+
};
|
|
205
|
+
const rejectWithContext = (message) => {
|
|
206
|
+
finish(() => {
|
|
207
|
+
dispose();
|
|
208
|
+
const tail = recentLines.length > 0 ? `
|
|
209
|
+
Recent output:
|
|
210
|
+
${recentLines.join("\n")}` : "";
|
|
211
|
+
reject(new Error(`${message}
|
|
212
|
+
Harness log: ${logFilePath}${tail}`));
|
|
213
|
+
});
|
|
214
|
+
};
|
|
215
|
+
stdoutReader.on("line", (line) => {
|
|
216
|
+
rememberLine(line);
|
|
217
|
+
if (!line.startsWith(READY_PREFIX)) {
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
try {
|
|
221
|
+
const payload = JSON.parse(
|
|
222
|
+
line.slice(READY_PREFIX.length)
|
|
223
|
+
);
|
|
224
|
+
finish(
|
|
225
|
+
() => resolve({
|
|
226
|
+
ready: payload,
|
|
227
|
+
dispose
|
|
228
|
+
})
|
|
229
|
+
);
|
|
230
|
+
} catch (error) {
|
|
231
|
+
const message = error instanceof Error ? error.message : "invalid readiness payload";
|
|
232
|
+
rejectWithContext(
|
|
233
|
+
`Embedded harness emitted malformed readiness JSON: ${message}`
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
stderrReader.on("line", rememberLine);
|
|
238
|
+
stdout.on("data", refreshIdleTimeout);
|
|
239
|
+
stderr.on("data", refreshIdleTimeout);
|
|
240
|
+
child.once("error", (error) => {
|
|
241
|
+
rejectWithContext(`Failed to launch embedded harness: ${error.message}`);
|
|
242
|
+
});
|
|
243
|
+
child.once("exit", (code, signal) => {
|
|
244
|
+
if (settled) {
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
rejectWithContext(
|
|
248
|
+
`Embedded harness exited before becoming ready (code=${code ?? "null"}, signal=${signal ?? "null"}).`
|
|
249
|
+
);
|
|
250
|
+
});
|
|
251
|
+
refreshIdleTimeout();
|
|
252
|
+
maxTimeoutHandle = setTimeout(() => {
|
|
253
|
+
rejectWithContext(
|
|
254
|
+
`Timed out waiting ${HARNESS_START_MAX_TIMEOUT_MS}ms total for embedded harness readiness.`
|
|
255
|
+
);
|
|
256
|
+
}, HARNESS_START_MAX_TIMEOUT_MS);
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
async function terminateChild(child) {
|
|
260
|
+
if (child.exitCode !== null || child.signalCode !== null) {
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
child.kill("SIGTERM");
|
|
264
|
+
const exited = await waitForExit(child, HARNESS_STOP_TIMEOUT_MS);
|
|
265
|
+
if (exited) {
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
child.kill("SIGKILL");
|
|
269
|
+
await waitForExit(child, HARNESS_STOP_TIMEOUT_MS);
|
|
270
|
+
}
|
|
271
|
+
async function waitForExit(child, timeoutMs) {
|
|
272
|
+
if (child.exitCode !== null || child.signalCode !== null) {
|
|
273
|
+
return true;
|
|
274
|
+
}
|
|
275
|
+
return await new Promise((resolve) => {
|
|
276
|
+
const timeoutHandle = setTimeout(() => resolve(false), timeoutMs);
|
|
277
|
+
child.once("exit", () => {
|
|
278
|
+
clearTimeout(timeoutHandle);
|
|
279
|
+
resolve(true);
|
|
280
|
+
});
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
async function resolveGradleLauncher(repoRoot) {
|
|
284
|
+
if (process.platform === "win32") {
|
|
285
|
+
const gradleBat = path.join(repoRoot, "gradlew.bat");
|
|
286
|
+
if (!await exists(gradleBat)) {
|
|
287
|
+
throw new Error(`Missing Gradle launcher at ${gradleBat}`);
|
|
288
|
+
}
|
|
289
|
+
return { command: gradleBat, args: [] };
|
|
290
|
+
}
|
|
291
|
+
const gradleShell = path.join(repoRoot, "gradlew");
|
|
292
|
+
if (!await exists(gradleShell)) {
|
|
293
|
+
throw new Error(`Missing Gradle launcher at ${gradleShell}`);
|
|
294
|
+
}
|
|
295
|
+
return { command: gradleShell, args: [] };
|
|
296
|
+
}
|
|
297
|
+
async function resolveRepoRoot(projectRoot) {
|
|
298
|
+
const currentFile = fileURLToPath(import.meta.url);
|
|
299
|
+
const searchRoots = [path.dirname(currentFile), projectRoot, process.cwd()];
|
|
300
|
+
for (const root of searchRoots) {
|
|
301
|
+
const repoRoot = await findSourceCheckoutRepoRoot(root);
|
|
302
|
+
if (repoRoot) {
|
|
303
|
+
return repoRoot;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
throw new Error(
|
|
307
|
+
"Embedded harness is only available from a source checkout with local Dreamboard backend support. Use 'dreamboard test ... --env prod' from a published install."
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
async function findSourceCheckoutRepoRoot(startDir) {
|
|
311
|
+
let currentDir = path.resolve(startDir);
|
|
312
|
+
while (true) {
|
|
313
|
+
if (await isSourceCheckoutRepoRoot(currentDir)) {
|
|
314
|
+
return currentDir;
|
|
315
|
+
}
|
|
316
|
+
const parentDir = path.dirname(currentDir);
|
|
317
|
+
if (parentDir === currentDir) {
|
|
318
|
+
return null;
|
|
319
|
+
}
|
|
320
|
+
currentDir = parentDir;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
async function isSourceCheckoutRepoRoot(rootDir) {
|
|
324
|
+
return await exists(path.join(rootDir, "gradlew")) && await exists(path.join(rootDir, "apps", "backend", "build.gradle.kts"));
|
|
325
|
+
}
|
|
326
|
+
function normalizeUuid(candidate, fallbackSeed) {
|
|
327
|
+
if (candidate && UUID_PATTERN.test(candidate)) {
|
|
328
|
+
return candidate;
|
|
329
|
+
}
|
|
330
|
+
return stableUuid(fallbackSeed);
|
|
331
|
+
}
|
|
332
|
+
function stableUuid(seed) {
|
|
333
|
+
const hex = createHash("sha256").update(seed, "utf8").digest("hex").slice(0, 32);
|
|
334
|
+
const chars = hex.split("");
|
|
335
|
+
chars[12] = "5";
|
|
336
|
+
chars[16] = (parseInt(chars[16] ?? "0", 16) & 3 | 8).toString(16);
|
|
337
|
+
return `${chars.slice(0, 8).join("")}-${chars.slice(8, 12).join("")}-${chars.slice(12, 16).join("")}-${chars.slice(16, 20).join("")}-${chars.slice(20, 32).join("")}`;
|
|
338
|
+
}
|
|
339
|
+
function toTitleCase(slug) {
|
|
340
|
+
return slug.split(/[-_\s]+/).filter((part) => part.length > 0).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
341
|
+
}
|
|
342
|
+
export {
|
|
343
|
+
startEmbeddedHarnessSession
|
|
344
|
+
};
|
|
345
|
+
//# sourceMappingURL=embedded-harness-PF2LCIWC.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/services/testing/embedded-harness.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { spawn, type ChildProcess } from \"node:child_process\";\nimport { createWriteStream } from \"node:fs\";\nimport { mkdir, mkdtemp, readFile, rm } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport path from \"node:path\";\nimport { createInterface } from \"node:readline\";\nimport { fileURLToPath } from \"node:url\";\nimport { client } from \"@dreamboard/api-client/client.gen\";\nimport consola from \"consola\";\nimport type { ProjectConfig } from \"../../types.js\";\nimport { exists } from \"../../utils/fs.js\";\nimport { hashContent } from \"../../utils/crypto.js\";\nimport { loadManifest } from \"../project/local-files.js\";\n\nconst READY_PREFIX = \"HARNESS_READY \";\nconst HARNESS_START_IDLE_TIMEOUT_MS = 180_000;\nconst HARNESS_START_MAX_TIMEOUT_MS = 600_000;\nconst HARNESS_STOP_TIMEOUT_MS = 10_000;\nconst UUID_PATTERN =\n /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;\n\ntype EmbeddedHarnessReady = {\n status: \"ready\";\n baseUrl: string;\n port: number;\n gameId: string;\n compiledResultId: string;\n manifestId: string;\n ruleId: string;\n hostUserId: string;\n hostToken: string;\n guestUserId: string;\n guestToken: string;\n};\n\ntype LocalHarnessFixture = {\n bundleRoot: string;\n manifestHash: string;\n gameId: string;\n manifestId: string;\n compiledResultId: string;\n ruleId: string;\n gameSlug: string;\n gameName: string;\n};\n\ntype HarnessMonitor = {\n ready: EmbeddedHarnessReady;\n dispose: () => void;\n};\n\nexport type EmbeddedHarnessSession = {\n gameId: string;\n compiledResultId: string;\n manifestHash: string;\n baseUrl: string;\n logFilePath: string;\n stop: () => Promise<void>;\n};\n\nexport async function startEmbeddedHarnessSession(options: {\n projectRoot: string;\n projectConfig: ProjectConfig;\n configureClient?: boolean;\n}): Promise<EmbeddedHarnessSession> {\n const repoRoot = await resolveRepoRoot(options.projectRoot);\n const gradlew = await resolveGradleLauncher(repoRoot);\n const fixture = await prepareLocalHarnessFixture(options);\n const logDir = path.join(options.projectRoot, \"test\", \"generated\");\n const logFilePath = path.join(logDir, \"embedded-harness.log\");\n await mkdir(logDir, { recursive: true });\n\n const previousClientConfig = client.getConfig();\n const child = spawn(\n gradlew.command,\n [\n ...gradlew.args,\n \"--console=plain\",\n \":apps:backend:runEmbeddedHarness\",\n `-PharnessManifestPath=${path.join(options.projectRoot, \"manifest.json\")}`,\n `-PharnessBundlePath=${fixture.bundleRoot}`,\n `-PharnessGameId=${fixture.gameId}`,\n `-PharnessManifestId=${fixture.manifestId}`,\n `-PharnessCompiledResultId=${fixture.compiledResultId}`,\n `-PharnessRuleId=${fixture.ruleId}`,\n `-PharnessGameSlug=${fixture.gameSlug}`,\n `-PharnessGameName=${fixture.gameName}`,\n \"-PharnessHostEmail=host@example.com\",\n \"-PharnessGuestEmail=guest@example.com\",\n ],\n {\n cwd: repoRoot,\n env: process.env,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n },\n );\n\n let stopped = false;\n let restoreClientConfig: (() => void) | undefined;\n let monitor: HarnessMonitor;\n try {\n monitor = await waitForHarnessReady(child, logFilePath);\n if (options.configureClient !== false) {\n client.setConfig({\n ...previousClientConfig,\n baseUrl: monitor.ready.baseUrl,\n headers: {\n ...(previousClientConfig.headers ?? {}),\n Authorization: `Bearer ${monitor.ready.hostToken}`,\n },\n });\n restoreClientConfig = () => client.setConfig(previousClientConfig);\n }\n } catch (error) {\n await terminateChild(child);\n await rm(fixture.bundleRoot, { recursive: true, force: true });\n throw error;\n }\n\n consola.info(`Embedded harness ready at ${monitor.ready.baseUrl}`);\n\n return {\n gameId: fixture.gameId,\n compiledResultId: fixture.compiledResultId,\n manifestHash: fixture.manifestHash,\n baseUrl: monitor.ready.baseUrl,\n logFilePath,\n stop: async () => {\n if (stopped) return;\n stopped = true;\n restoreClientConfig?.();\n await terminateChild(child);\n monitor.dispose();\n await rm(fixture.bundleRoot, { recursive: true, force: true });\n },\n };\n}\n\nasync function prepareLocalHarnessFixture(options: {\n projectRoot: string;\n projectConfig: ProjectConfig;\n}): Promise<LocalHarnessFixture> {\n const manifestPath = path.join(options.projectRoot, \"manifest.json\");\n const manifestContent = await readFile(manifestPath, \"utf8\");\n await loadManifest(options.projectRoot);\n\n const appEntryPath = path.join(options.projectRoot, \"app\", \"index.ts\");\n if (!(await exists(appEntryPath))) {\n throw new Error(\n `Embedded harness requires ${path.relative(options.projectRoot, appEntryPath)}.`,\n );\n }\n\n const bundleRoot = await mkdtemp(path.join(tmpdir(), \"dreamboard-harness-\"));\n const bundleEntryPath = path.join(bundleRoot, \"src\", \"app\", \"index.js\");\n await mkdir(path.dirname(bundleEntryPath), { recursive: true });\n\n const buildResult = await Bun.build({\n entrypoints: [appEntryPath],\n format: \"esm\",\n target: \"browser\",\n minify: false,\n sourcemap: \"none\",\n root: options.projectRoot,\n });\n\n if (!buildResult.success) {\n await rm(bundleRoot, { recursive: true, force: true });\n const issues =\n buildResult.logs\n .map((log) => log.message)\n .filter((message) => typeof message === \"string\" && message.length > 0)\n .join(\"; \") || \"unknown Bun build failure\";\n throw new Error(`Failed to bundle local game logic: ${issues}`);\n }\n\n for (const output of buildResult.outputs) {\n const targetPath = path.join(bundleRoot, \"src\", output.path);\n await mkdir(path.dirname(targetPath), { recursive: true });\n await Bun.write(targetPath, output);\n }\n\n const bundleContent = await readFile(bundleEntryPath, \"utf8\");\n const manifestHash = hashContent(manifestContent);\n const bundleHash = hashContent(bundleContent);\n const gameSlug =\n options.projectConfig.slug || path.basename(options.projectRoot);\n\n return {\n bundleRoot,\n manifestHash,\n gameId: normalizeUuid(\n options.projectConfig.gameId,\n `game:${gameSlug}:${options.projectRoot}`,\n ),\n manifestId: normalizeUuid(\n options.projectConfig.manifestId,\n `manifest:${manifestHash}`,\n ),\n compiledResultId: stableUuid(`compiled:${manifestHash}:${bundleHash}`),\n ruleId: normalizeUuid(\n options.projectConfig.ruleId,\n `rule:${gameSlug}:${options.projectConfig.ruleId ?? \"local\"}`,\n ),\n gameSlug,\n gameName: toTitleCase(gameSlug),\n };\n}\n\nasync function waitForHarnessReady(\n child: ChildProcess,\n logFilePath: string,\n): Promise<HarnessMonitor> {\n const stdout = child.stdout;\n const stderr = child.stderr;\n if (!stdout || !stderr) {\n throw new Error(\"Embedded harness process did not expose stdout/stderr.\");\n }\n\n const logStream = createWriteStream(logFilePath, { flags: \"w\" });\n stdout.pipe(logStream, { end: false });\n stderr.pipe(logStream, { end: false });\n\n const stdoutReader = createInterface({ input: stdout });\n const stderrReader = createInterface({ input: stderr });\n const recentLines: string[] = [];\n\n const rememberLine = (line: string): void => {\n if (!line.trim()) return;\n recentLines.push(line);\n while (recentLines.length > 30) {\n recentLines.shift();\n }\n };\n\n return await new Promise<HarnessMonitor>((resolve, reject) => {\n let settled = false;\n let idleTimeoutHandle: ReturnType<typeof setTimeout> | undefined;\n let maxTimeoutHandle: ReturnType<typeof setTimeout> | undefined;\n\n const dispose = (): void => {\n stdout.off(\"data\", refreshIdleTimeout);\n stderr.off(\"data\", refreshIdleTimeout);\n stdoutReader.close();\n stderrReader.close();\n stdout.unpipe(logStream);\n stderr.unpipe(logStream);\n logStream.end();\n };\n\n const finish = (callback: () => void): void => {\n if (settled) return;\n settled = true;\n if (idleTimeoutHandle) {\n clearTimeout(idleTimeoutHandle);\n }\n if (maxTimeoutHandle) {\n clearTimeout(maxTimeoutHandle);\n }\n callback();\n };\n\n const refreshIdleTimeout = (): void => {\n if (settled) {\n return;\n }\n if (idleTimeoutHandle) {\n clearTimeout(idleTimeoutHandle);\n }\n idleTimeoutHandle = setTimeout(() => {\n rejectWithContext(\n `Timed out after ${HARNESS_START_IDLE_TIMEOUT_MS}ms without harness output while waiting for embedded harness readiness.`,\n );\n }, HARNESS_START_IDLE_TIMEOUT_MS);\n };\n const rejectWithContext = (message: string): void => {\n finish(() => {\n dispose();\n const tail =\n recentLines.length > 0\n ? `\\nRecent output:\\n${recentLines.join(\"\\n\")}`\n : \"\";\n reject(new Error(`${message}\\nHarness log: ${logFilePath}${tail}`));\n });\n };\n\n stdoutReader.on(\"line\", (line) => {\n rememberLine(line);\n if (!line.startsWith(READY_PREFIX)) {\n return;\n }\n\n try {\n const payload = JSON.parse(\n line.slice(READY_PREFIX.length),\n ) as EmbeddedHarnessReady;\n finish(() =>\n resolve({\n ready: payload,\n dispose,\n }),\n );\n } catch (error) {\n const message =\n error instanceof Error ? error.message : \"invalid readiness payload\";\n rejectWithContext(\n `Embedded harness emitted malformed readiness JSON: ${message}`,\n );\n }\n });\n\n stderrReader.on(\"line\", rememberLine);\n stdout.on(\"data\", refreshIdleTimeout);\n stderr.on(\"data\", refreshIdleTimeout);\n\n child.once(\"error\", (error) => {\n rejectWithContext(`Failed to launch embedded harness: ${error.message}`);\n });\n\n child.once(\"exit\", (code, signal) => {\n if (settled) {\n return;\n }\n rejectWithContext(\n `Embedded harness exited before becoming ready (code=${code ?? \"null\"}, signal=${signal ?? \"null\"}).`,\n );\n });\n\n refreshIdleTimeout();\n\n maxTimeoutHandle = setTimeout(() => {\n rejectWithContext(\n `Timed out waiting ${HARNESS_START_MAX_TIMEOUT_MS}ms total for embedded harness readiness.`,\n );\n }, HARNESS_START_MAX_TIMEOUT_MS);\n });\n}\n\nasync function terminateChild(child: ChildProcess): Promise<void> {\n if (child.exitCode !== null || child.signalCode !== null) {\n return;\n }\n\n child.kill(\"SIGTERM\");\n const exited = await waitForExit(child, HARNESS_STOP_TIMEOUT_MS);\n if (exited) {\n return;\n }\n\n child.kill(\"SIGKILL\");\n await waitForExit(child, HARNESS_STOP_TIMEOUT_MS);\n}\n\nasync function waitForExit(\n child: ChildProcess,\n timeoutMs: number,\n): Promise<boolean> {\n if (child.exitCode !== null || child.signalCode !== null) {\n return true;\n }\n\n return await new Promise<boolean>((resolve) => {\n const timeoutHandle = setTimeout(() => resolve(false), timeoutMs);\n child.once(\"exit\", () => {\n clearTimeout(timeoutHandle);\n resolve(true);\n });\n });\n}\n\nasync function resolveGradleLauncher(repoRoot: string): Promise<{\n command: string;\n args: string[];\n}> {\n if (process.platform === \"win32\") {\n const gradleBat = path.join(repoRoot, \"gradlew.bat\");\n if (!(await exists(gradleBat))) {\n throw new Error(`Missing Gradle launcher at ${gradleBat}`);\n }\n return { command: gradleBat, args: [] };\n }\n\n const gradleShell = path.join(repoRoot, \"gradlew\");\n if (!(await exists(gradleShell))) {\n throw new Error(`Missing Gradle launcher at ${gradleShell}`);\n }\n return { command: gradleShell, args: [] };\n}\n\nasync function resolveRepoRoot(projectRoot: string): Promise<string> {\n const currentFile = fileURLToPath(import.meta.url);\n const searchRoots = [path.dirname(currentFile), projectRoot, process.cwd()];\n\n for (const root of searchRoots) {\n const repoRoot = await findSourceCheckoutRepoRoot(root);\n if (repoRoot) {\n return repoRoot;\n }\n }\n\n throw new Error(\n \"Embedded harness is only available from a source checkout with local Dreamboard backend support. Use 'dreamboard test ... --env prod' from a published install.\",\n );\n}\n\nasync function findSourceCheckoutRepoRoot(\n startDir: string,\n): Promise<string | null> {\n let currentDir = path.resolve(startDir);\n\n while (true) {\n if (await isSourceCheckoutRepoRoot(currentDir)) {\n return currentDir;\n }\n\n const parentDir = path.dirname(currentDir);\n if (parentDir === currentDir) {\n return null;\n }\n currentDir = parentDir;\n }\n}\n\nasync function isSourceCheckoutRepoRoot(rootDir: string): Promise<boolean> {\n return (\n (await exists(path.join(rootDir, \"gradlew\"))) &&\n (await exists(path.join(rootDir, \"apps\", \"backend\", \"build.gradle.kts\")))\n );\n}\n\nfunction normalizeUuid(\n candidate: string | undefined,\n fallbackSeed: string,\n): string {\n if (candidate && UUID_PATTERN.test(candidate)) {\n return candidate;\n }\n return stableUuid(fallbackSeed);\n}\n\nfunction stableUuid(seed: string): string {\n const hex = createHash(\"sha256\")\n .update(seed, \"utf8\")\n .digest(\"hex\")\n .slice(0, 32);\n const chars = hex.split(\"\");\n chars[12] = \"5\";\n chars[16] = ((parseInt(chars[16] ?? \"0\", 16) & 0x3) | 0x8).toString(16);\n return `${chars.slice(0, 8).join(\"\")}-${chars.slice(8, 12).join(\"\")}-${chars.slice(12, 16).join(\"\")}-${chars.slice(16, 20).join(\"\")}-${chars.slice(20, 32).join(\"\")}`;\n}\n\nfunction toTitleCase(slug: string): string {\n return slug\n .split(/[-_\\s]+/)\n .filter((part) => part.length > 0)\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1))\n .join(\" \");\n}\n"],"mappings":"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","names":[]}
|