@couch-kit/cli 0.2.5 → 0.3.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/dist/index.js +221 -2
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1887,6 +1887,9 @@ var init_esm = __esm(() => {
|
|
|
1887
1887
|
} = import__.default);
|
|
1888
1888
|
});
|
|
1889
1889
|
|
|
1890
|
+
// ../core/src/middleware.ts
|
|
1891
|
+
var init_middleware = () => {};
|
|
1892
|
+
|
|
1890
1893
|
// ../core/src/types.ts
|
|
1891
1894
|
var init_types = () => {};
|
|
1892
1895
|
|
|
@@ -1931,11 +1934,35 @@ var init_constants = __esm(() => {
|
|
|
1931
1934
|
DEFAULT_DISCONNECT_TIMEOUT = 5 * 60 * 1000;
|
|
1932
1935
|
});
|
|
1933
1936
|
|
|
1937
|
+
// ../core/src/replay.ts
|
|
1938
|
+
function replayActions(recording, reducer) {
|
|
1939
|
+
const snapshots = [];
|
|
1940
|
+
let currentState = recording.initialState;
|
|
1941
|
+
for (let i = 0;i < recording.actions.length; i++) {
|
|
1942
|
+
const { action, timestamp } = recording.actions[i];
|
|
1943
|
+
currentState = reducer(currentState, action);
|
|
1944
|
+
snapshots.push({
|
|
1945
|
+
state: currentState,
|
|
1946
|
+
action,
|
|
1947
|
+
timestamp,
|
|
1948
|
+
index: i
|
|
1949
|
+
});
|
|
1950
|
+
}
|
|
1951
|
+
const endTimestamp = recording.endTimestamp ?? (recording.actions.length > 0 ? recording.actions[recording.actions.length - 1].timestamp : recording.startTimestamp);
|
|
1952
|
+
return {
|
|
1953
|
+
finalState: currentState,
|
|
1954
|
+
snapshots,
|
|
1955
|
+
duration: endTimestamp - recording.startTimestamp,
|
|
1956
|
+
actionCount: recording.actions.length
|
|
1957
|
+
};
|
|
1958
|
+
}
|
|
1959
|
+
|
|
1934
1960
|
// ../core/src/index.ts
|
|
1935
1961
|
var init_src = __esm(() => {
|
|
1936
1962
|
init_types();
|
|
1937
1963
|
init_protocol();
|
|
1938
1964
|
init_constants();
|
|
1965
|
+
init_middleware();
|
|
1939
1966
|
});
|
|
1940
1967
|
|
|
1941
1968
|
// src/commands/bundle.ts
|
|
@@ -2094,6 +2121,18 @@ __export(exports_init, {
|
|
|
2094
2121
|
});
|
|
2095
2122
|
import fs2 from "node:fs";
|
|
2096
2123
|
import path2 from "node:path";
|
|
2124
|
+
function detectPackageManager2() {
|
|
2125
|
+
if (typeof Bun !== "undefined")
|
|
2126
|
+
return "bun";
|
|
2127
|
+
const userAgent = process.env.npm_config_user_agent ?? "";
|
|
2128
|
+
if (userAgent.includes("yarn"))
|
|
2129
|
+
return "yarn";
|
|
2130
|
+
if (userAgent.includes("pnpm"))
|
|
2131
|
+
return "pnpm";
|
|
2132
|
+
if (userAgent.includes("bun"))
|
|
2133
|
+
return "bun";
|
|
2134
|
+
return "npm";
|
|
2135
|
+
}
|
|
2097
2136
|
var initCommand;
|
|
2098
2137
|
var init_init = __esm(() => {
|
|
2099
2138
|
init_esm();
|
|
@@ -2145,6 +2184,8 @@ var init_init = __esm(() => {
|
|
|
2145
2184
|
noEmit: true,
|
|
2146
2185
|
jsx: "react-jsx",
|
|
2147
2186
|
strict: true,
|
|
2187
|
+
noUncheckedIndexedAccess: true,
|
|
2188
|
+
exactOptionalPropertyTypes: true,
|
|
2148
2189
|
noUnusedLocals: true,
|
|
2149
2190
|
noUnusedParameters: true,
|
|
2150
2191
|
noFallthroughCasesInSwitch: true
|
|
@@ -2228,12 +2269,53 @@ export default App
|
|
|
2228
2269
|
fs2.writeFileSync(path2.join(targetDir, "src/index.css"), `
|
|
2229
2270
|
body { margin: 0; background: #111; color: #fff; }
|
|
2230
2271
|
`);
|
|
2272
|
+
const reducerContent = `import type { IAction, IGameState, GameReducer } from "@couch-kit/core";
|
|
2273
|
+
|
|
2274
|
+
export interface GameState extends IGameState {
|
|
2275
|
+
score: number;
|
|
2276
|
+
round: number;
|
|
2277
|
+
}
|
|
2278
|
+
|
|
2279
|
+
export interface GameAction extends IAction {
|
|
2280
|
+
type: "SCORE" | "NEXT_ROUND" | "RESET";
|
|
2281
|
+
payload?: { points?: number };
|
|
2282
|
+
}
|
|
2283
|
+
|
|
2284
|
+
export const gameReducer: GameReducer<GameState, GameAction> = (state, action) => {
|
|
2285
|
+
switch (action.type) {
|
|
2286
|
+
case "SCORE":
|
|
2287
|
+
return { ...state, score: state.score + (action.payload?.points ?? 1) };
|
|
2288
|
+
case "NEXT_ROUND":
|
|
2289
|
+
return { ...state, round: state.round + 1 };
|
|
2290
|
+
case "RESET":
|
|
2291
|
+
return { ...state, score: 0, round: 1 };
|
|
2292
|
+
default:
|
|
2293
|
+
return state;
|
|
2294
|
+
}
|
|
2295
|
+
};
|
|
2296
|
+
|
|
2297
|
+
export const initialState: GameState = {
|
|
2298
|
+
status: "lobby",
|
|
2299
|
+
players: {},
|
|
2300
|
+
score: 0,
|
|
2301
|
+
round: 1,
|
|
2302
|
+
};
|
|
2303
|
+
`;
|
|
2304
|
+
fs2.writeFileSync(path2.join(targetDir, "src/reducer.ts"), reducerContent);
|
|
2305
|
+
const gitignoreContent = `node_modules/
|
|
2306
|
+
dist/
|
|
2307
|
+
lib/
|
|
2308
|
+
.DS_Store
|
|
2309
|
+
*.local
|
|
2310
|
+
`;
|
|
2311
|
+
fs2.writeFileSync(path2.join(targetDir, ".gitignore"), gitignoreContent);
|
|
2312
|
+
const pm = detectPackageManager2();
|
|
2231
2313
|
console.log(`Done! Created new project in ${name}`);
|
|
2232
2314
|
console.log(`
|
|
2233
2315
|
Next steps:
|
|
2234
2316
|
cd ${name}
|
|
2235
|
-
|
|
2236
|
-
|
|
2317
|
+
${pm} install
|
|
2318
|
+
${pm} run dev
|
|
2237
2319
|
`);
|
|
2238
2320
|
} catch (e) {
|
|
2239
2321
|
console.error(`Init failed: ${toErrorMessage(e)}`);
|
|
@@ -2242,6 +2324,133 @@ Next steps:
|
|
|
2242
2324
|
});
|
|
2243
2325
|
});
|
|
2244
2326
|
|
|
2327
|
+
// src/commands/replay.ts
|
|
2328
|
+
var exports_replay = {};
|
|
2329
|
+
__export(exports_replay, {
|
|
2330
|
+
replay: () => replay2
|
|
2331
|
+
});
|
|
2332
|
+
import { resolve } from "node:path";
|
|
2333
|
+
var replay2;
|
|
2334
|
+
var init_replay = __esm(() => {
|
|
2335
|
+
init_esm();
|
|
2336
|
+
init_src();
|
|
2337
|
+
replay2 = new Command("replay").description("Replay a recorded game session against a reducer").argument("<recording>", "Path to recording JSON file").argument("<reducer>", "Path to reducer module (must export default or named 'reducer')").option("--snapshots", "Output intermediate state snapshots", false).option("--json", "Output as formatted JSON", false).action(async (recordingPath, reducerPath, options) => {
|
|
2338
|
+
try {
|
|
2339
|
+
const resolvedRecordingPath = resolve(recordingPath);
|
|
2340
|
+
const recordingFile = Bun.file(resolvedRecordingPath);
|
|
2341
|
+
const exists = await recordingFile.exists();
|
|
2342
|
+
if (!exists) {
|
|
2343
|
+
console.error(`Error: Recording file not found: ${recordingPath}`);
|
|
2344
|
+
process.exit(1);
|
|
2345
|
+
}
|
|
2346
|
+
const recording = await recordingFile.json();
|
|
2347
|
+
if (!recording.initialState || !Array.isArray(recording.actions)) {
|
|
2348
|
+
console.error("Error: Invalid recording format. Expected { initialState, actions[] }");
|
|
2349
|
+
process.exit(1);
|
|
2350
|
+
}
|
|
2351
|
+
const resolvedReducerPath = resolve(reducerPath);
|
|
2352
|
+
const reducerModule = await import(resolvedReducerPath);
|
|
2353
|
+
const reducer = reducerModule.default ?? reducerModule.reducer;
|
|
2354
|
+
if (typeof reducer !== "function") {
|
|
2355
|
+
console.error("Error: Reducer module must export a default function or named 'reducer' export");
|
|
2356
|
+
process.exit(1);
|
|
2357
|
+
}
|
|
2358
|
+
const result = replayActions(recording, reducer);
|
|
2359
|
+
if (options.json) {
|
|
2360
|
+
const output = options.snapshots ? result : {
|
|
2361
|
+
finalState: result.finalState,
|
|
2362
|
+
duration: result.duration,
|
|
2363
|
+
actionCount: result.actionCount
|
|
2364
|
+
};
|
|
2365
|
+
console.log(JSON.stringify(output, null, 2));
|
|
2366
|
+
} else {
|
|
2367
|
+
console.log(`Replayed ${result.actionCount} actions in ${result.duration}ms`);
|
|
2368
|
+
console.log(`
|
|
2369
|
+
Final state:`);
|
|
2370
|
+
console.log(JSON.stringify(result.finalState, null, 2));
|
|
2371
|
+
if (options.snapshots) {
|
|
2372
|
+
console.log(`
|
|
2373
|
+
Snapshots (${result.snapshots.length}):`);
|
|
2374
|
+
for (const snapshot of result.snapshots) {
|
|
2375
|
+
console.log(`
|
|
2376
|
+
[${snapshot.index}] ${JSON.stringify(snapshot.action)} @ ${snapshot.timestamp}`);
|
|
2377
|
+
console.log(` State: ${JSON.stringify(snapshot.state)}`);
|
|
2378
|
+
}
|
|
2379
|
+
}
|
|
2380
|
+
}
|
|
2381
|
+
} catch (error) {
|
|
2382
|
+
console.error(`Error: ${toErrorMessage(error)}`);
|
|
2383
|
+
process.exit(1);
|
|
2384
|
+
}
|
|
2385
|
+
});
|
|
2386
|
+
});
|
|
2387
|
+
|
|
2388
|
+
// src/commands/dev.ts
|
|
2389
|
+
var exports_dev = {};
|
|
2390
|
+
__export(exports_dev, {
|
|
2391
|
+
dev: () => dev
|
|
2392
|
+
});
|
|
2393
|
+
import { resolve as resolve2 } from "node:path";
|
|
2394
|
+
import { networkInterfaces } from "node:os";
|
|
2395
|
+
function getLanIp() {
|
|
2396
|
+
const interfaces = networkInterfaces();
|
|
2397
|
+
for (const name of Object.keys(interfaces)) {
|
|
2398
|
+
const nets = interfaces[name];
|
|
2399
|
+
if (!nets)
|
|
2400
|
+
continue;
|
|
2401
|
+
for (const net of nets) {
|
|
2402
|
+
if (net.family === "IPv4" && !net.internal) {
|
|
2403
|
+
return net.address;
|
|
2404
|
+
}
|
|
2405
|
+
}
|
|
2406
|
+
}
|
|
2407
|
+
return null;
|
|
2408
|
+
}
|
|
2409
|
+
var dev;
|
|
2410
|
+
var init_dev = __esm(() => {
|
|
2411
|
+
init_esm();
|
|
2412
|
+
init_src();
|
|
2413
|
+
dev = new Command("dev").description("Start development server with LAN access for mobile testing").option("-p, --port <port>", "Port number", "5173").option("--host", "Expose to LAN", true).option("--open", "Open browser automatically", false).action(async (options) => {
|
|
2414
|
+
try {
|
|
2415
|
+
const port = parseInt(options.port, 10);
|
|
2416
|
+
const cwd = process.cwd();
|
|
2417
|
+
let vite;
|
|
2418
|
+
try {
|
|
2419
|
+
const vitePkg = "vite";
|
|
2420
|
+
vite = await import(vitePkg);
|
|
2421
|
+
} catch {
|
|
2422
|
+
console.error("Error: Vite is not installed. Run: bun add -d vite");
|
|
2423
|
+
process.exit(1);
|
|
2424
|
+
}
|
|
2425
|
+
const lanIp = getLanIp();
|
|
2426
|
+
console.log(`Starting Couch Kit dev server...
|
|
2427
|
+
`);
|
|
2428
|
+
const server = await vite.createServer({
|
|
2429
|
+
root: cwd,
|
|
2430
|
+
server: {
|
|
2431
|
+
port,
|
|
2432
|
+
host: options.host ? "0.0.0.0" : "localhost",
|
|
2433
|
+
open: options.open
|
|
2434
|
+
},
|
|
2435
|
+
configFile: resolve2(cwd, "vite.config.ts")
|
|
2436
|
+
});
|
|
2437
|
+
await server.listen();
|
|
2438
|
+
console.log(` Local: http://localhost:${port}/`);
|
|
2439
|
+
if (lanIp) {
|
|
2440
|
+
console.log(` LAN: http://${lanIp}:${port}/`);
|
|
2441
|
+
console.log(`
|
|
2442
|
+
Scan QR or open LAN URL on your mobile device`);
|
|
2443
|
+
}
|
|
2444
|
+
console.log(`
|
|
2445
|
+
Press Ctrl+C to stop
|
|
2446
|
+
`);
|
|
2447
|
+
} catch (error) {
|
|
2448
|
+
console.error(`Error: ${toErrorMessage(error)}`);
|
|
2449
|
+
process.exit(1);
|
|
2450
|
+
}
|
|
2451
|
+
});
|
|
2452
|
+
});
|
|
2453
|
+
|
|
2245
2454
|
// src/index.ts
|
|
2246
2455
|
init_esm();
|
|
2247
2456
|
import { createRequire as createRequire2 } from "node:module";
|
|
@@ -2263,6 +2472,16 @@ program2.command("init").description("Scaffolds a new web controller project").a
|
|
|
2263
2472
|
const { initCommand: initCommand2 } = await Promise.resolve().then(() => (init_init(), exports_init));
|
|
2264
2473
|
await initCommand2.parseAsync(["init", name], { from: "user" });
|
|
2265
2474
|
});
|
|
2475
|
+
program2.command("replay").description("Replay a recorded game session against a reducer").argument("<recording>", "Path to recording JSON file").argument("<reducer>", "Path to reducer module").option("--snapshots", "Output intermediate state snapshots").option("--json", "Output as formatted JSON").action(async (recording, reducer, options) => {
|
|
2476
|
+
const { replay: replay3 } = await Promise.resolve().then(() => (init_replay(), exports_replay));
|
|
2477
|
+
await replay3.parseAsync(["replay", recording, reducer, ...reconstructArgs(options)], { from: "user" });
|
|
2478
|
+
});
|
|
2479
|
+
program2.command("dev").description("Start development server with LAN access").option("-p, --port <port>", "Port number", "5173").option("--host", "Expose to LAN").option("--open", "Open browser automatically").action(async (options) => {
|
|
2480
|
+
const { dev: dev2 } = await Promise.resolve().then(() => (init_dev(), exports_dev));
|
|
2481
|
+
await dev2.parseAsync(["dev", ...reconstructArgs(options)], {
|
|
2482
|
+
from: "user"
|
|
2483
|
+
});
|
|
2484
|
+
});
|
|
2266
2485
|
function reconstructArgs(options) {
|
|
2267
2486
|
const args = [];
|
|
2268
2487
|
for (const [key, value] of Object.entries(options)) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@couch-kit/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public",
|
|
6
6
|
"provenance": true
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"clean": "rm -rf dist"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@couch-kit/core": "0.
|
|
39
|
+
"@couch-kit/core": "0.8.0",
|
|
40
40
|
"commander": "^12.0.0"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|