Files
feuerwerk/tools/lib/projects.mjs
T
rootandClaude Opus 5 4f78c7518c Give the graph a loop, and build Tetris with it
A Tetris example project needs to draw 200 board cells and scan rows
for a full line. Neither is expressible without iteration, so the node
system gets a Repeat node plus the missing text side: Substring, Find
text and To number. The interpreter drops its data cache once per
round -- otherwise every round would recompute with the first round's
values.

Object by name takes its name as an input now, not just as a setting;
addressing "c" + index is impossible otherwise. An unconnected input
reads the stored setting, so saved projects are untouched.

The board itself is a 200-character text, "." for empty. That makes
both operations of Tetris a single expression instead of a loop, and
each frame attempts exactly one move -- rotate beats sideways beats
fall -- so the fit check exists once in the whole graph.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-09 19:10:41 +02:00

64 lines
2.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Which project each tool writes to, and the guard that keeps every tool away from the
* playground.
*
* playground the user's own project. No tool writes it – it is maintained in the editor.
* fixture rewritten by every e2e run (tools/seed-e2e.mjs + tools/e2e.mjs).
* jumpRun the example game (tools/seed-jumprun.mjs, played by tools/smoke-jumprun.mjs).
* tetris the second example game (tools/seed-tetris.mjs, played by tools/smoke-tetris.mjs).
*
* All ids exist on the server (created via POST ?r=projects); PUT does not create projects.
*/
export const PROJECTS = {
playground: '649188c15414',
fixture: '3caa9b6a0c92',
jumpRun: '3d271f3bb58b',
tetris: '2072b56583af',
};
export const DEFAULT_BASE_URL = 'https://nockal.com/game-maker/';
/**
* Reads `[projectId] [baseUrl]` from argv, falling back to `defaultId`. Targeting the playground
* aborts: no tool may overwrite or mutate it.
*/
export function cliArgs(script, defaultId) {
const projectId = process.argv[2] ?? defaultId;
const baseUrl = process.argv[3] ?? DEFAULT_BASE_URL;
if (projectId === PROJECTS.playground) {
console.error(`refusing to run ${script} against the playground project (${PROJECTS.playground}).`);
process.exit(2);
}
return { projectId, baseUrl };
}
/** Overwrites a project, keeping the name it already has on the server. */
export async function writeProject(projectId, baseUrl, project) {
const url = `${baseUrl}api/index.php?r=projects/${projectId}`;
// A request without a timeout keeps its socket open, so a stalled server hangs the seed
// instead of failing it – and the run that follows never starts.
const timeout = () => AbortSignal.timeout(20_000);
const current = await fetch(url, { signal: timeout() });
if (!current.ok) {
console.error(`GET failed: ${current.status} – create the project first (POST ?r=projects)`);
process.exit(1);
}
const doc = await current.json();
const body = {
revision: doc.revision,
project: { ...project, id: projectId, name: doc.name ?? project.name },
};
const res = await fetch(url, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
signal: timeout(),
});
const result = await res.json();
if (!res.ok) {
console.error('PUT failed', res.status, result);
process.exit(1);
}
console.log(`seeded ${projectId} (${body.project.name}) → revision ${result.revision}`);
}