@uzuhq/code-cli 0.6.0 → 0.6.2

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/cli.js CHANGED
@@ -3,8 +3,8 @@
3
3
  // src/cli.ts
4
4
  import { Command, Option } from "commander";
5
5
  import { execSync } from "child_process";
6
- import { readFileSync as readFileSync7, existsSync as existsSync4, unlinkSync, createWriteStream } from "fs";
7
- import { dirname as dirname5, resolve as resolve5 } from "path";
6
+ import { readFileSync as readFileSync6, existsSync as existsSync4, unlinkSync, createWriteStream } from "fs";
7
+ import { dirname as dirname5, resolve as resolve6 } from "path";
8
8
  import { ZipArchive } from "archiver";
9
9
  import { fileURLToPath as fileURLToPath4, pathToFileURL as pathToFileURL2 } from "url";
10
10
 
@@ -418,8 +418,30 @@ var registerRevision = async (params) => {
418
418
 
419
419
  // src/build-server-logic.ts
420
420
  import { build } from "esbuild";
421
- import { readFileSync as readFileSync2 } from "fs";
422
- var SDK_PACKAGES = ["@uzuhq/code-sdk", "@uzupj/uzu-sdk"];
421
+
422
+ // src/sdk-server-stub.ts
423
+ import { existsSync } from "fs";
424
+ import { dirname, resolve } from "path";
425
+ import { fileURLToPath } from "url";
426
+ var SDK_SPECIFIER = /^(@uzuhq\/code-sdk|@uzupj\/uzu-sdk)$/;
427
+ function resolveSdkShimPath() {
428
+ const here = fileURLToPath(import.meta.url);
429
+ const ext = here.endsWith(".ts") ? "ts" : "js";
430
+ const shim = resolve(dirname(here), "dev-server", `sdk-server-shim.${ext}`);
431
+ if (!existsSync(shim)) {
432
+ throw new Error(`sdk-server-shim \u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093: ${shim}`);
433
+ }
434
+ return shim;
435
+ }
436
+ var uzuSdkServerStub = {
437
+ name: "uzu-sdk-server-stub",
438
+ setup(build3) {
439
+ const shimPath = resolveSdkShimPath();
440
+ build3.onResolve({ filter: SDK_SPECIFIER }, () => ({ path: shimPath }));
441
+ }
442
+ };
443
+
444
+ // src/build-server-logic.ts
423
445
  var buildServerLogic = async (logicPath, outPath) => {
424
446
  await build({
425
447
  entryPoints: [logicPath],
@@ -428,29 +450,23 @@ var buildServerLogic = async (logicPath, outPath) => {
428
450
  format: "esm",
429
451
  target: "es2022",
430
452
  platform: "neutral",
431
- // SDK type-only import tsc で消えるが安全のため external 指定。
432
- // @uzupj/uzu-sdk は旧パッケージ名 (未移行 scenario 向けの両対応)
433
- external: SDK_PACKAGES
453
+ // SDK engine-core への素通し shim へ差し替えて bundle する。`uzu dev` と
454
+ // 同じ plugin を通すので、harness で動いたものが publish でも同じ形になる。
455
+ //
456
+ // external は 1 つも置かない。Worker Loader が facet へ渡すのは index.js /
457
+ // game-facet.js / logic.js の 3 つだけで npm の解決先が無いため、成果物に bare
458
+ // import が残ると部屋の起動時に落ちる。external を置かなければ、解決できない
459
+ // import は esbuild がここでエラーにするので、壊れた成果物は publish されない。
460
+ plugins: [uzuSdkServerStub]
434
461
  });
435
- assertNoSdkRuntimeImport(outPath);
436
462
  console.log(`Built server logic: ${logicPath} \u2192 ${outPath}`);
437
463
  };
438
- var assertNoSdkRuntimeImport = (outPath) => {
439
- const built = readFileSync2(outPath, "utf-8");
440
- const found = SDK_PACKAGES.find((pkg) => built.includes(pkg));
441
- if (!found) return;
442
- throw new Error(
443
- `logic \u304C ${found} \u304B\u3089\u5024\u3092 import \u3057\u3066\u3044\u307E\u3059\u3002
444
- logic \u304B\u3089 SDK \u3078\u5411\u3051\u3066\u3088\u3044\u306E\u306F\u578B\u3060\u3051\u3067\u3059 (import type)\u3002
445
- \u5B9F\u884C\u6642\u306B\u5FC5\u8981\u306A\u3082\u306E (serverOnly / SERVER_TIME \u7B49) \u306F @uzuhq/code-engine-core \u304B\u3089 import \u3057\u3066\u304F\u3060\u3055\u3044\u3002`
446
- );
447
- };
448
464
 
449
465
  // src/create-2d-game.ts
450
- import { mkdirSync, readFileSync as readFileSync3, writeFileSync, readdirSync, statSync } from "fs";
451
- import { resolve, dirname, join as join3 } from "path";
452
- import { fileURLToPath } from "url";
453
- var __dirname = dirname(fileURLToPath(import.meta.url));
466
+ import { mkdirSync, readFileSync as readFileSync2, writeFileSync, readdirSync, statSync } from "fs";
467
+ import { resolve as resolve2, dirname as dirname2, join as join3 } from "path";
468
+ import { fileURLToPath as fileURLToPath2 } from "url";
469
+ var __dirname = dirname2(fileURLToPath2(import.meta.url));
454
470
  var copyDir = (src, dest, replacements) => {
455
471
  mkdirSync(dest, { recursive: true });
456
472
  for (const entry of readdirSync(src)) {
@@ -461,13 +477,13 @@ var copyDir = (src, dest, replacements) => {
461
477
  continue;
462
478
  }
463
479
  if (entry.endsWith(".tpl")) {
464
- let content = readFileSync3(srcPath, "utf-8");
480
+ let content = readFileSync2(srcPath, "utf-8");
465
481
  for (const [key, value] of Object.entries(replacements)) {
466
482
  content = content.replaceAll(`{{${key}}}`, value);
467
483
  }
468
484
  writeFileSync(join3(dest, entry.replace(/\.tpl$/, "")), content);
469
485
  } else {
470
- const content = readFileSync3(srcPath, "utf-8");
486
+ const content = readFileSync2(srcPath, "utf-8");
471
487
  let output = content;
472
488
  for (const [key, value] of Object.entries(replacements)) {
473
489
  output = output.replaceAll(`{{${key}}}`, value);
@@ -478,8 +494,8 @@ var copyDir = (src, dest, replacements) => {
478
494
  };
479
495
  var toTitle = (name) => name.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
480
496
  var create2dGame = (name) => {
481
- const dest = resolve(process.cwd(), name);
482
- const templateDir = resolve(__dirname, "..", "game-2d-template");
497
+ const dest = resolve2(process.cwd(), name);
498
+ const templateDir = resolve2(__dirname, "..", "game-2d-template");
483
499
  const title = toTitle(name);
484
500
  const replacements = { name, title };
485
501
  console.log(`Creating 2D game project: ${name}`);
@@ -494,7 +510,7 @@ Next steps:`);
494
510
  };
495
511
 
496
512
  // src/cf-images-upload.ts
497
- import { readFileSync as readFileSync4 } from "fs";
513
+ import { readFileSync as readFileSync3 } from "fs";
498
514
  import { basename as basename2 } from "path";
499
515
  var parseUploadedImageId = (v) => {
500
516
  if (typeof v === "object" && v !== null && "success" in v && v.success === true && "result" in v && typeof v.result === "object" && v.result !== null && "id" in v.result && typeof v.result.id === "string" && v.result.id !== "") {
@@ -513,7 +529,7 @@ var uploadIconsToCfImages = async (env, token, filePaths) => {
513
529
  filePaths.map(async (filePath, i) => {
514
530
  const image = session.images[i];
515
531
  const formData = new FormData();
516
- formData.append("file", new Blob([readFileSync4(filePath)]), basename2(filePath));
532
+ formData.append("file", new Blob([readFileSync3(filePath)]), basename2(filePath));
517
533
  const res = await fetch(image.uploadURL, { method: "POST", body: formData });
518
534
  if (!res.ok) {
519
535
  const detail = (await res.text()).trim();
@@ -530,8 +546,8 @@ var uploadIconsToCfImages = async (env, token, filePaths) => {
530
546
 
531
547
  // src/dev.ts
532
548
  import { spawn } from "child_process";
533
- import { readFileSync as readFileSync5, existsSync as existsSync2 } from "fs";
534
- import { dirname as dirname3, resolve as resolve3 } from "path";
549
+ import { readFileSync as readFileSync4, existsSync as existsSync2 } from "fs";
550
+ import { dirname as dirname3, resolve as resolve4 } from "path";
535
551
  import { fileURLToPath as fileURLToPath3 } from "url";
536
552
  import { createInterface } from "readline";
537
553
 
@@ -549,8 +565,10 @@ import { WebSocketServer } from "ws";
549
565
  import { randomUUID } from "crypto";
550
566
 
551
567
  // ../engine-core/src/game-time.ts
552
- var plus = (t, d) => t + d;
568
+ var toMs = (t) => t;
569
+ var plus = (t, d) => toMs(t) + d;
553
570
  var asGameTime = (ms) => ms;
571
+ var GAME_START = asGameTime(0);
554
572
 
555
573
  // ../engine-core/src/game-clock.ts
556
574
  var NO_PLAYERS_GRACE_MS = 5 * 6e4;
@@ -584,7 +602,7 @@ var GameClock = class {
584
602
  }
585
603
  /** ゲーム内時刻を、alarm / setTimeout が使う実時刻へ直す。 */
586
604
  toWall(at) {
587
- return at + this.startedAtWall + this.pausedTotalMs;
605
+ return toMs(at) + this.startedAtWall + this.pausedTotalMs;
588
606
  }
589
607
  /** 実際に理由を足したら true。呼び出し側が永続化や tick 停止の要否に使う。 */
590
608
  freeze(reason) {
@@ -628,9 +646,10 @@ var GameClock = class {
628
646
  function withGameClock(time, fn) {
629
647
  const realNow = Date.now;
630
648
  const RealDate = Date;
631
- Date.now = () => time;
649
+ const ms = toMs(time);
650
+ Date.now = () => ms;
632
651
  globalThis.Date = new Proxy(RealDate, {
633
- construct: (target, args) => Reflect.construct(target, args.length === 0 ? [time] : args)
652
+ construct: (target, args) => Reflect.construct(target, args.length === 0 ? [ms] : args)
634
653
  });
635
654
  try {
636
655
  return fn();
@@ -1860,7 +1879,7 @@ function startHarnessServer(opts) {
1860
1879
  });
1861
1880
  httpServer.listen(opts.port, opts.host ?? "127.0.0.1");
1862
1881
  return {
1863
- stop: () => new Promise((resolve6) => {
1882
+ stop: () => new Promise((resolve7) => {
1864
1883
  for (const ws of wss.clients) {
1865
1884
  try {
1866
1885
  ws.close();
@@ -1868,7 +1887,7 @@ function startHarnessServer(opts) {
1868
1887
  }
1869
1888
  }
1870
1889
  wss.close(() => {
1871
- httpServer.close(() => resolve6());
1890
+ httpServer.close(() => resolve7());
1872
1891
  });
1873
1892
  })
1874
1893
  };
@@ -2084,29 +2103,12 @@ function proxyUpgradeToScenario(req, socket, head, scenarioUrl) {
2084
2103
 
2085
2104
  // src/dev-server/load-logic.ts
2086
2105
  import { build as build2 } from "esbuild";
2087
- import { existsSync, mkdtempSync, rmSync } from "fs";
2106
+ import { mkdtempSync, rmSync } from "fs";
2088
2107
  import { tmpdir } from "os";
2089
- import { dirname as dirname2, join as join4, resolve as resolve2 } from "path";
2090
- import { fileURLToPath as fileURLToPath2, pathToFileURL } from "url";
2091
- function resolveSdkShimPath() {
2092
- const here = fileURLToPath2(import.meta.url);
2093
- const shim = here.endsWith(".ts") ? resolve2(dirname2(here), "sdk-server-shim.ts") : resolve2(dirname2(here), "dev-server", "sdk-server-shim.js");
2094
- if (!existsSync(shim)) {
2095
- throw new Error(`sdk-server-shim \u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093: ${shim}`);
2096
- }
2097
- return shim;
2098
- }
2099
- var uzuSdkServerStub = {
2100
- name: "uzu-sdk-server-stub",
2101
- setup(build3) {
2102
- const shimPath = resolveSdkShimPath();
2103
- build3.onResolve({ filter: /^(@uzuhq\/code-sdk|@uzupj\/uzu-sdk)$/ }, () => ({
2104
- path: shimPath
2105
- }));
2106
- }
2107
- };
2108
+ import { join as join4, resolve as resolve3 } from "path";
2109
+ import { pathToFileURL } from "url";
2108
2110
  async function loadLogicFromPath(logicPath) {
2109
- const absPath = resolve2(logicPath);
2111
+ const absPath = resolve3(logicPath);
2110
2112
  const tempDir = mkdtempSync(join4(tmpdir(), "uzu-dev-logic-"));
2111
2113
  const outPath = join4(tempDir, "logic.mjs");
2112
2114
  await build2({
@@ -2183,12 +2185,12 @@ var DEFAULT_READY_PATTERN = "Local:\\s+(https?://[^\\s]+)";
2183
2185
  var DEFAULT_MIN_IFRAME_SHORT_EDGE = 360;
2184
2186
  async function runDevCommand() {
2185
2187
  const cwd = process.cwd();
2186
- const manifestPath = resolve3(cwd, "manifest.json");
2188
+ const manifestPath = resolve4(cwd, "manifest.json");
2187
2189
  if (!existsSync2(manifestPath)) {
2188
2190
  console.error("manifest.json \u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002 scenario \u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u3067\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002");
2189
2191
  process.exit(1);
2190
2192
  }
2191
- const manifest = JSON.parse(readFileSync5(manifestPath, "utf-8"));
2193
+ const manifest = JSON.parse(readFileSync4(manifestPath, "utf-8"));
2192
2194
  const playerCount = manifest.characters?.length ?? manifest.playerCount ?? 2;
2193
2195
  const orientation = manifest.orientation ?? "portrait";
2194
2196
  const devCommand = manifest.dev?.command ?? DEFAULT_DEV_COMMAND;
@@ -2196,13 +2198,13 @@ async function runDevCommand() {
2196
2198
  let loaded = null;
2197
2199
  if (manifest.serverActionLogicPath) {
2198
2200
  console.log(`[uzu dev] Building server logic from ${manifest.serverActionLogicPath}...`);
2199
- const logicPath = resolve3(cwd, manifest.serverActionLogicPath);
2201
+ const logicPath = resolve4(cwd, manifest.serverActionLogicPath);
2200
2202
  loaded = await loadLogicFromPath(logicPath);
2201
2203
  console.log("[uzu dev] Server logic ready");
2202
2204
  }
2203
2205
  const thisDir = dirname3(fileURLToPath3(import.meta.url));
2204
- const clientEntryJs = resolve3(thisDir, "harness", "client-entry.js");
2205
- const clientEntryTs = resolve3(thisDir, "harness", "client-entry.ts");
2206
+ const clientEntryJs = resolve4(thisDir, "harness", "client-entry.js");
2207
+ const clientEntryTs = resolve4(thisDir, "harness", "client-entry.ts");
2206
2208
  const clientEntry = existsSync2(clientEntryJs) ? clientEntryJs : existsSync2(clientEntryTs) ? clientEntryTs : null;
2207
2209
  if (!clientEntry) {
2208
2210
  console.error(`harness client entry \u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093: ${clientEntryJs}`);
@@ -2299,7 +2301,7 @@ function stripAnsi(s) {
2299
2301
  return s.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, "");
2300
2302
  }
2301
2303
  function watchForReady(child, pattern) {
2302
- return new Promise((resolve6, reject) => {
2304
+ return new Promise((resolve7, reject) => {
2303
2305
  const timeout = setTimeout(() => {
2304
2306
  reject(new Error("Timed out waiting for scenario dev server to become ready (30s)"));
2305
2307
  }, 3e4);
@@ -2315,7 +2317,7 @@ function watchForReady(child, pattern) {
2315
2317
  const url = /^https?:\/\//i.test(captured) ? captured : `http://localhost:${captured}`;
2316
2318
  resolved = true;
2317
2319
  clearTimeout(timeout);
2318
- resolve6(url.replace(/\/$/, ""));
2320
+ resolve7(url.replace(/\/$/, ""));
2319
2321
  };
2320
2322
  if (child.stdout) {
2321
2323
  const rl = createInterface({ input: child.stdout });
@@ -2342,14 +2344,14 @@ var MAX_HARNESS_PORT_ATTEMPTS = 100;
2342
2344
  var HARNESS_HOST = "0.0.0.0";
2343
2345
  async function findFreePort() {
2344
2346
  const net = await import("net");
2345
- const tryPort = (port) => new Promise((resolve6) => {
2347
+ const tryPort = (port) => new Promise((resolve7) => {
2346
2348
  const server = net.createServer();
2347
2349
  server.unref();
2348
- server.once("error", () => resolve6(null));
2350
+ server.once("error", () => resolve7(null));
2349
2351
  server.listen(port, HARNESS_HOST, () => {
2350
2352
  const address = server.address();
2351
2353
  const assigned = typeof address === "object" && address !== null ? address.port : port;
2352
- server.close(() => resolve6(assigned));
2354
+ server.close(() => resolve7(assigned));
2353
2355
  });
2354
2356
  });
2355
2357
  for (let i = 0; i < MAX_HARNESS_PORT_ATTEMPTS; i++) {
@@ -2363,8 +2365,8 @@ async function findFreePort() {
2363
2365
  }
2364
2366
 
2365
2367
  // src/sdk-version.ts
2366
- import { existsSync as existsSync3, readFileSync as readFileSync6 } from "fs";
2367
- import { dirname as dirname4, resolve as resolve4 } from "path";
2368
+ import { existsSync as existsSync3, readFileSync as readFileSync5 } from "fs";
2369
+ import { dirname as dirname4, resolve as resolve5 } from "path";
2368
2370
  var SDK_PACKAGE_PATHS = [
2369
2371
  ["@uzuhq", "code-sdk"],
2370
2372
  ["@uzupj", "uzu-sdk"]
@@ -2376,17 +2378,17 @@ var readInstalledSdkVersion = (cwd) => {
2376
2378
  "@uzuhq/code-sdk \u304C node_modules \u306B\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002\u4F9D\u5B58\u3092 install \u3057\u3066\u304B\u3089 publish \u3057\u3066\u304F\u3060\u3055\u3044\u3002"
2377
2379
  );
2378
2380
  }
2379
- const version = JSON.parse(readFileSync6(pkgPath, "utf-8")).version;
2381
+ const version = JSON.parse(readFileSync5(pkgPath, "utf-8")).version;
2380
2382
  if (!version) {
2381
2383
  throw new Error(`${pkgPath} \u306B version \u304C\u3042\u308A\u307E\u305B\u3093\u3002`);
2382
2384
  }
2383
2385
  return version;
2384
2386
  };
2385
2387
  var findSdkPackageJson = (cwd) => {
2386
- let dir = resolve4(cwd);
2388
+ let dir = resolve5(cwd);
2387
2389
  for (; ; ) {
2388
2390
  const found = SDK_PACKAGE_PATHS.map(
2389
- (segments) => resolve4(dir, "node_modules", ...segments, "package.json")
2391
+ (segments) => resolve5(dir, "node_modules", ...segments, "package.json")
2390
2392
  ).find(existsSync3);
2391
2393
  if (found) {
2392
2394
  return found;
@@ -2448,7 +2450,7 @@ var failureHtml = (msg) => {
2448
2450
  </body>`;
2449
2451
  };
2450
2452
  var startLoopbackServer = async () => {
2451
- let resolve6 = null;
2453
+ let resolve7 = null;
2452
2454
  let reject = null;
2453
2455
  let delivered = false;
2454
2456
  let pending = null;
@@ -2485,7 +2487,7 @@ var startLoopbackServer = async () => {
2485
2487
  }
2486
2488
  res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
2487
2489
  res.end(SUCCESS_HTML);
2488
- if (resolve6) resolve6(code);
2490
+ if (resolve7) resolve7(code);
2489
2491
  else pending = { code };
2490
2492
  });
2491
2493
  await new Promise((res, rej) => {
@@ -2507,7 +2509,7 @@ var startLoopbackServer = async () => {
2507
2509
  () => rej(new Error(`login timed out after ${timeoutMs}ms`)),
2508
2510
  timeoutMs
2509
2511
  );
2510
- resolve6 = (code) => {
2512
+ resolve7 = (code) => {
2511
2513
  clearTimeout(t);
2512
2514
  res(code);
2513
2515
  };
@@ -2555,8 +2557,8 @@ var runLoginFlow = async (env, onURL) => {
2555
2557
  // src/cli.ts
2556
2558
  var envOption = () => new Option("--env <env>", "\u63A5\u7D9A\u5148\u74B0\u5883 (dev | stg | prd)").default(DEFAULT_ENV).hideHelp();
2557
2559
  var readOwnCliVersion = () => {
2558
- const pkgPath = resolve5(dirname5(fileURLToPath4(import.meta.url)), "..", "package.json");
2559
- const version = JSON.parse(readFileSync7(pkgPath, "utf-8")).version;
2560
+ const pkgPath = resolve6(dirname5(fileURLToPath4(import.meta.url)), "..", "package.json");
2561
+ const version = JSON.parse(readFileSync6(pkgPath, "utf-8")).version;
2560
2562
  if (!version) {
2561
2563
  throw new Error("uzu-cli \u306E package.json \u306B version \u304C\u3042\u308A\u307E\u305B\u3093\u3002");
2562
2564
  }
@@ -2581,12 +2583,12 @@ program.command("dev").description(
2581
2583
  program.command("publish").description("\u30B2\u30FC\u30E0\u3092\u30D3\u30EB\u30C9\u3057\u3066 R2 \u306B\u30A2\u30C3\u30D7\u30ED\u30FC\u30C9 \u2192 \u767B\u9332").option("--change-notes <msg>", "\u30EA\u30D3\u30B8\u30E7\u30F3\u306E\u5909\u66F4\u30E1\u30E2", "").addOption(envOption()).action(async (options) => {
2582
2584
  const env = resolveEnv(options.env);
2583
2585
  const cwd = process.cwd();
2584
- const manifestPath = resolve5(cwd, "manifest.json");
2586
+ const manifestPath = resolve6(cwd, "manifest.json");
2585
2587
  if (!existsSync4(manifestPath)) {
2586
2588
  console.error("manifest.json \u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002\u30B2\u30FC\u30E0\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u3067\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002");
2587
2589
  process.exit(1);
2588
2590
  }
2589
- const manifest = JSON.parse(readFileSync7(manifestPath, "utf-8"));
2591
+ const manifest = JSON.parse(readFileSync6(manifestPath, "utf-8"));
2590
2592
  if (typeof manifest !== "object" || manifest === null) {
2591
2593
  console.error("manifest.json \u304C\u4E0D\u6B63\u306A\u5F62\u5F0F\u3067\u3059\u3002");
2592
2594
  process.exit(1);
@@ -2640,8 +2642,8 @@ program.command("publish").description("\u30B2\u30FC\u30E0\u3092\u30D3\u30EB\u30
2640
2642
  execSync(buildCommand, { stdio: "inherit", cwd });
2641
2643
  }
2642
2644
  console.log("Creating ZIP...");
2643
- const absoluteOutputDir = resolve5(cwd, outputDir);
2644
- const zipPath = resolve5(cwd, "__zip__.zip");
2645
+ const absoluteOutputDir = resolve6(cwd, outputDir);
2646
+ const zipPath = resolve6(cwd, "__zip__.zip");
2645
2647
  await new Promise((res, reject) => {
2646
2648
  const output = createWriteStream(zipPath);
2647
2649
  const archive = new ZipArchive({ zlib: { level: 9 } });
@@ -2656,8 +2658,8 @@ program.command("publish").description("\u30B2\u30FC\u30E0\u3092\u30D3\u30EB\u30
2656
2658
  const { resourceId, revisionId, token } = await uploadGameToR2(env, zipPath, absoluteOutputDir);
2657
2659
  console.log(`Uploaded to R2. revisionId: ${revisionId}, resourceId: ${resourceId}`);
2658
2660
  if (manifest.serverActionLogicPath) {
2659
- const logicEntryPoint = resolve5(cwd, manifest.serverActionLogicPath);
2660
- const logicOutPath = resolve5(cwd, "__logic__.js");
2661
+ const logicEntryPoint = resolve6(cwd, manifest.serverActionLogicPath);
2662
+ const logicOutPath = resolve6(cwd, "__logic__.js");
2661
2663
  try {
2662
2664
  console.log("Building server logic...");
2663
2665
  await buildServerLogic(logicEntryPoint, logicOutPath);
@@ -2672,7 +2674,7 @@ program.command("publish").description("\u30B2\u30FC\u30E0\u3092\u30D3\u30EB\u30
2672
2674
  process.exit(1);
2673
2675
  }
2674
2676
  console.log("Uploading logic.js to R2...");
2675
- const logicJsContent = readFileSync7(logicOutPath, "utf-8");
2677
+ const logicJsContent = readFileSync6(logicOutPath, "utf-8");
2676
2678
  await uploadLogicToR2(env, token, logicJsContent, { wireVersion: WIRE_VERSION });
2677
2679
  } finally {
2678
2680
  if (existsSync4(logicOutPath)) unlinkSync(logicOutPath);
@@ -2683,7 +2685,7 @@ program.command("publish").description("\u30B2\u30FC\u30E0\u3092\u30D3\u30EB\u30
2683
2685
  console.log("Resolving character icons...");
2684
2686
  const isLocalIcon = (char) => !!char.icon && !char.icon.startsWith("http://") && !char.icon.startsWith("https://");
2685
2687
  const localIconPaths = manifestCharacters.filter(isLocalIcon).map((char) => {
2686
- const iconAbsPath = resolve5(cwd, char.icon);
2688
+ const iconAbsPath = resolve6(cwd, char.icon);
2687
2689
  if (!existsSync4(iconAbsPath)) {
2688
2690
  console.error(`\u30A2\u30A4\u30B3\u30F3\u30D5\u30A1\u30A4\u30EB\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093: ${char.icon}`);
2689
2691
  process.exit(1);
@@ -2693,7 +2695,7 @@ program.command("publish").description("\u30B2\u30FC\u30E0\u3092\u30D3\u30EB\u30
2693
2695
  const iconURLs = await uploadIconsToCfImages(env, token, localIconPaths);
2694
2696
  resolvedCharacters = manifestCharacters.map((char) => {
2695
2697
  if (!isLocalIcon(char)) return char;
2696
- const cfImagesUrl = iconURLs.get(resolve5(cwd, char.icon));
2698
+ const cfImagesUrl = iconURLs.get(resolve6(cwd, char.icon));
2697
2699
  if (!cfImagesUrl) {
2698
2700
  console.error(`\u30A2\u30A4\u30B3\u30F3\u306E\u30A2\u30C3\u30D7\u30ED\u30FC\u30C9\u7D50\u679C\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093: ${char.icon}`);
2699
2701
  process.exit(1);
@@ -6,8 +6,214 @@ var DEFAULT_ICON_URLS = [
6
6
  "https://imagedelivery.net/htp-D7B2hJT5XtdWYN9e7Q/43f45d11-da38-4d6e-637d-3df78e583500/original"
7
7
  ];
8
8
 
9
+ // ../engine-core/src/game-time.ts
10
+ var toMs = (t) => t;
11
+ var plus = (t, d) => toMs(t) + d;
12
+ var sub = (t, d) => toMs(t) - d;
13
+ var minus = (a, b) => toMs(a) - toMs(b);
14
+ var asGameTime = (ms) => ms;
15
+ var GAME_START = asGameTime(0);
16
+
9
17
  // ../engine-core/src/game-clock.ts
10
18
  var NO_PLAYERS_GRACE_MS = 5 * 6e4;
19
+ var GameClock = class {
20
+ startedAtWall = 0;
21
+ pausedTotalMs = 0;
22
+ pausedAtWall = null;
23
+ frozenBy = /* @__PURE__ */ new Set();
24
+ /** 時計を 0 から始め直す。`setup()` を呼ぶ直前に通す。 */
25
+ restart() {
26
+ this.startedAtWall = Date.now();
27
+ this.pausedTotalMs = 0;
28
+ this.pausedAtWall = null;
29
+ this.frozenBy.clear();
30
+ }
31
+ get frozen() {
32
+ return this.frozenBy.size > 0;
33
+ }
34
+ isFrozenBy(reason) {
35
+ return this.frozenBy.has(reason);
36
+ }
37
+ /**
38
+ * 現在のゲーム内時刻。停止中は `pausedAtWall` で凍るので同じ値を返し続ける。
39
+ *
40
+ * IMPORTANT: [withGameClock] の中で呼んではいけない。実装が `Date.now()` を読むので、
41
+ * 差し替え済みの時刻を実時刻として引き算し、大きく負の値になる。
42
+ * ハンドラへ渡す時刻は必ず外で 1 回取ってから渡すこと。
43
+ */
44
+ now() {
45
+ return asGameTime((this.pausedAtWall ?? Date.now()) - this.startedAtWall - this.pausedTotalMs);
46
+ }
47
+ /** ゲーム内時刻を、alarm / setTimeout が使う実時刻へ直す。 */
48
+ toWall(at) {
49
+ return toMs(at) + this.startedAtWall + this.pausedTotalMs;
50
+ }
51
+ /** 実際に理由を足したら true。呼び出し側が永続化や tick 停止の要否に使う。 */
52
+ freeze(reason) {
53
+ if (this.frozenBy.has(reason)) return false;
54
+ const wasFrozen = this.frozen;
55
+ this.frozenBy.add(reason);
56
+ if (!wasFrozen) this.pausedAtWall = Date.now();
57
+ return true;
58
+ }
59
+ /** 実際に理由を外したら true。 */
60
+ unfreeze(reason) {
61
+ if (!this.frozenBy.delete(reason)) return false;
62
+ if (!this.frozen && this.pausedAtWall !== null) {
63
+ this.pausedTotalMs += Date.now() - this.pausedAtWall;
64
+ this.pausedAtWall = null;
65
+ }
66
+ return true;
67
+ }
68
+ snapshot() {
69
+ return {
70
+ startedAtWall: this.startedAtWall,
71
+ pausedTotalMs: this.pausedTotalMs,
72
+ pausedAtWall: this.pausedAtWall,
73
+ frozenBy: [...this.frozenBy]
74
+ };
75
+ }
76
+ /**
77
+ * 永続化した内容から復元する。欠損は「未停止」に倒す。
78
+ *
79
+ * 読めなかったせいで世界が止まったままになる方が、動き出すより悪い。
80
+ */
81
+ restore(saved) {
82
+ this.startedAtWall = saved.startedAtWall ?? 0;
83
+ this.pausedTotalMs = saved.pausedTotalMs ?? 0;
84
+ this.pausedAtWall = saved.pausedAtWall ?? null;
85
+ this.frozenBy.clear();
86
+ for (const reason of saved.frozenBy ?? []) this.frozenBy.add(reason);
87
+ if (this.frozen && this.pausedAtWall === null) this.frozenBy.clear();
88
+ }
89
+ };
90
+ function withGameClock(time, fn) {
91
+ const realNow = Date.now;
92
+ const RealDate = Date;
93
+ const ms = toMs(time);
94
+ Date.now = () => ms;
95
+ globalThis.Date = new Proxy(RealDate, {
96
+ construct: (target, args) => Reflect.construct(target, args.length === 0 ? [ms] : args)
97
+ });
98
+ try {
99
+ return fn();
100
+ } finally {
101
+ globalThis.Date = RealDate;
102
+ Date.now = realNow;
103
+ }
104
+ }
105
+ function resolveDeadline(at, key, warned, report) {
106
+ if (at === null || at === void 0) return null;
107
+ if (typeof at === "number" && Number.isFinite(at)) return asGameTime(at);
108
+ if (!warned.has(key)) {
109
+ warned.add(key);
110
+ report(
111
+ `deadline "${key}" \u306E at() \u304C\u7DE0\u5207\u306B\u4F7F\u3048\u306A\u3044\u5024\u3092\u8FD4\u3057\u305F: ${String(at)}\u3002\u3053\u306E\u7DE0\u5207\u306F\u4E8C\u5EA6\u3068\u767A\u706B\u3057\u306A\u3044\u3002\u5EC3\u6B62\u3055\u308C\u305F ctx.now \u3092\u8AAD\u3093\u3067\u3044\u306A\u3044\u304B\u78BA\u8A8D\u3059\u308B\u3053\u3068`
112
+ );
113
+ }
114
+ return null;
115
+ }
116
+
117
+ // ../engine-core/src/json-patch.ts
118
+ function escapePointer(key) {
119
+ return key.replace(/~/g, "~0").replace(/\//g, "~1");
120
+ }
121
+ function unescapePointer(token) {
122
+ return token.replace(/~1/g, "/").replace(/~0/g, "~");
123
+ }
124
+ function compare(oldObj, newObj, basePath = "") {
125
+ if (oldObj === newObj) return [];
126
+ if (oldObj === null || newObj === null || typeof oldObj !== "object" || typeof newObj !== "object") {
127
+ return [{ op: "replace", path: basePath || "/", value: newObj }];
128
+ }
129
+ if (Array.isArray(oldObj) || Array.isArray(newObj)) {
130
+ if (JSON.stringify(oldObj) === JSON.stringify(newObj)) return [];
131
+ return [{ op: "replace", path: basePath || "/", value: newObj }];
132
+ }
133
+ const ops = [];
134
+ const oldKeys = Object.keys(oldObj);
135
+ const newKeys = Object.keys(newObj);
136
+ for (const key of oldKeys) {
137
+ if (!(key in newObj)) {
138
+ ops.push({ op: "remove", path: `${basePath}/${escapePointer(key)}` });
139
+ }
140
+ }
141
+ for (const key of newKeys) {
142
+ const childPath = `${basePath}/${escapePointer(key)}`;
143
+ if (!(key in oldObj)) {
144
+ ops.push({ op: "add", path: childPath, value: newObj[key] });
145
+ } else {
146
+ const childOps = compare(oldObj[key], newObj[key], childPath);
147
+ ops.push(...childOps);
148
+ }
149
+ }
150
+ return ops;
151
+ }
152
+ function applyPatch(doc, ops) {
153
+ for (const op of ops) {
154
+ const tokens = op.path.split("/").slice(1).map(unescapePointer);
155
+ if (tokens.length === 0) return false;
156
+ if (op.op === "replace" || op.op === "add") {
157
+ let target = doc;
158
+ for (let i = 0; i < tokens.length - 1; i++) {
159
+ target = target?.[tokens[i]];
160
+ if (target === void 0 || target === null) return false;
161
+ }
162
+ const lastKey = tokens[tokens.length - 1];
163
+ target[lastKey] = op.value;
164
+ } else if (op.op === "remove") {
165
+ let target = doc;
166
+ for (let i = 0; i < tokens.length - 1; i++) {
167
+ target = target?.[tokens[i]];
168
+ if (target === void 0 || target === null) return false;
169
+ }
170
+ const lastKey = tokens[tokens.length - 1];
171
+ if (Array.isArray(target)) {
172
+ target.splice(Number(lastKey), 1);
173
+ } else {
174
+ delete target[lastKey];
175
+ }
176
+ }
177
+ }
178
+ return true;
179
+ }
180
+
181
+ // ../engine-core/src/random.ts
182
+ var SeededRandomImpl = class _SeededRandomImpl {
183
+ _state;
184
+ constructor(seed) {
185
+ this._state = seed | 0;
186
+ }
187
+ get state() {
188
+ return this._state;
189
+ }
190
+ static fromState(state) {
191
+ const r = new _SeededRandomImpl(0);
192
+ r._state = state;
193
+ return r;
194
+ }
195
+ float() {
196
+ this._state |= 0;
197
+ this._state = this._state + 1831565813 | 0;
198
+ let t = Math.imul(this._state ^ this._state >>> 15, 1 | this._state);
199
+ t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
200
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
201
+ }
202
+ int(max) {
203
+ return Math.floor(this.float() * max);
204
+ }
205
+ pick(array) {
206
+ return array[this.int(array.length)];
207
+ }
208
+ shuffle(array) {
209
+ const a = [...array];
210
+ for (let i = a.length - 1; i > 0; i--) {
211
+ const j = this.int(i + 1);
212
+ [a[i], a[j]] = [a[j], a[i]];
213
+ }
214
+ return a;
215
+ }
216
+ };
11
217
 
12
218
  // ../engine-core/src/server-only.ts
13
219
  function serverOnly(handler) {
@@ -16,9 +222,46 @@ function serverOnly(handler) {
16
222
  function isServerOnlyAction(handler) {
17
223
  return typeof handler === "function" && "__serverOnly" in handler && handler.__serverOnly === true;
18
224
  }
225
+
226
+ // ../engine-core/src/roster.ts
227
+ function parseRoster(rosterParam) {
228
+ if (!rosterParam) return null;
229
+ let raw;
230
+ try {
231
+ raw = JSON.parse(rosterParam);
232
+ } catch {
233
+ return null;
234
+ }
235
+ if (!Array.isArray(raw)) return null;
236
+ return raw.filter((p) => (p.kind ?? "player") === "player").map((p) => ({
237
+ id: p.id,
238
+ nickname: p.name ?? "Guest",
239
+ iconUrl: p.iconUrl ?? "",
240
+ characterId: p.characterId
241
+ }));
242
+ }
243
+
244
+ // ../engine-core/src/versions.ts
245
+ var BRIDGE_VERSION = 2;
246
+ var WIRE_VERSION = 2;
19
247
  export {
248
+ BRIDGE_VERSION,
20
249
  DEFAULT_ICON_URLS,
250
+ GAME_START,
251
+ GameClock,
252
+ NO_PLAYERS_GRACE_MS,
21
253
  SERVER_TIME,
254
+ SeededRandomImpl,
255
+ WIRE_VERSION,
256
+ applyPatch,
257
+ asGameTime,
258
+ compare,
22
259
  isServerOnlyAction,
23
- serverOnly
260
+ minus,
261
+ parseRoster,
262
+ plus,
263
+ resolveDeadline,
264
+ serverOnly,
265
+ sub,
266
+ withGameClock
24
267
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uzuhq/code-cli",
3
- "version": "0.6.0",
3
+ "version": "0.6.2",
4
4
  "description": "UZU ゲーム開発 CLI - ビルド・パブリッシュ・プロジェクト作成ツール",
5
5
  "type": "module",
6
6
  "bin": {