gdharness 1.1.20 → 1.1.21

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/build/cli.js CHANGED
@@ -7850,6 +7850,27 @@ var init_game_log = __esm(() => {
7850
7850
  };
7851
7851
  });
7852
7852
 
7853
+ // src/reimport.ts
7854
+ import { createHash } from "node:crypto";
7855
+ import { existsSync as existsSync5, readFileSync as readFileSync3, rmSync as rmSync3, statSync as statSync2, utimesSync, writeFileSync as writeFileSync3 } from "node:fs";
7856
+ import { basename as basename2, join as join5 } from "node:path";
7857
+ function forceNextImport(projectPath, resourcePath) {
7858
+ const relative = resourcePath.replace(/^res:\/\//, "");
7859
+ const sidecar = join5(projectPath, `${relative}.import`);
7860
+ if (!existsSync5(sidecar)) {
7861
+ return;
7862
+ }
7863
+ const { atime, mtimeMs } = statSync2(sidecar);
7864
+ writeFileSync3(sidecar, readFileSync3(sidecar, "utf8").replace(/^valid=false\r?\n/m, ""));
7865
+ utimesSync(sidecar, atime, new Date(mtimeMs + 2000));
7866
+ rmSync3(importRecord(projectPath, resourcePath), { force: true });
7867
+ }
7868
+ function importRecord(projectPath, resourcePath) {
7869
+ const digest = createHash("md5").update(resourcePath).digest("hex");
7870
+ return join5(projectPath, ".godot", "imported", `${basename2(resourcePath)}-${digest}.md5`);
7871
+ }
7872
+ var init_reimport = () => {};
7873
+
7853
7874
  // src/alive.ts
7854
7875
  import process5 from "node:process";
7855
7876
  function alive(pid) {
@@ -7866,12 +7887,12 @@ function alive(pid) {
7866
7887
  var init_alive = () => {};
7867
7888
 
7868
7889
  // src/scratch.ts
7869
- import { existsSync as existsSync5, mkdtempSync, readdirSync as readdirSync2, rmSync as rmSync3 } from "node:fs";
7890
+ import { existsSync as existsSync6, mkdtempSync, readdirSync as readdirSync2, rmSync as rmSync4 } from "node:fs";
7870
7891
  import { tmpdir } from "node:os";
7871
- import { join as join5 } from "node:path";
7892
+ import { join as join6 } from "node:path";
7872
7893
  import process6 from "node:process";
7873
7894
  function scratchDirectory(kind) {
7874
- return mkdtempSync(join5(tmpdir(), `gdharness-${kind}-${process6.pid}-`));
7895
+ return mkdtempSync(join6(tmpdir(), `gdharness-${kind}-${process6.pid}-`));
7875
7896
  }
7876
7897
  function sweepAbandonedScratch(directory = tmpdir()) {
7877
7898
  let entries;
@@ -7887,7 +7908,7 @@ function sweepAbandonedScratch(directory = tmpdir()) {
7887
7908
  continue;
7888
7909
  }
7889
7910
  try {
7890
- remove(join5(directory, entry));
7911
+ remove(join6(directory, entry));
7891
7912
  } catch {}
7892
7913
  }
7893
7914
  }
@@ -7913,7 +7934,7 @@ function discard(...paths) {
7913
7934
  return left;
7914
7935
  }
7915
7936
  function remove(path) {
7916
- rmSync3(path, SWEPT);
7937
+ rmSync4(path, SWEPT);
7917
7938
  }
7918
7939
  function tryAgain(path, attempt) {
7919
7940
  if (attempt === 0) {
@@ -7925,7 +7946,7 @@ function tryAgain(path, attempt) {
7925
7946
  lastTryOnExit();
7926
7947
  }
7927
7948
  const after = AGAIN_AFTER_MS[attempt];
7928
- if (after === undefined || !existsSync5(path)) {
7949
+ if (after === undefined || !existsSync6(path)) {
7929
7950
  waiting.delete(path);
7930
7951
  if (after === undefined) {
7931
7952
  atExit.add(path);
@@ -7973,8 +7994,8 @@ var init_scratch = __esm(() => {
7973
7994
 
7974
7995
  // src/headless.ts
7975
7996
  import { execFile as execFile3 } from "node:child_process";
7976
- import { writeFileSync as writeFileSync3 } from "node:fs";
7977
- import { join as join6 } from "node:path";
7997
+ import { writeFileSync as writeFileSync4 } from "node:fs";
7998
+ import { join as join7 } from "node:path";
7978
7999
  import { promisify as promisify3 } from "node:util";
7979
8000
  function snakeCased(params) {
7980
8001
  const result = emptyRecord();
@@ -8023,21 +8044,36 @@ function librariesNotCopied(messages) {
8023
8044
  }
8024
8045
  return [...found];
8025
8046
  }
8026
- function extensionRetryNote(libraries) {
8027
- return `The first import could not load ${libraries.join(" and ")}: on Windows an engine running as the editor ` + "loads a GDExtension from a ~ copy beside the library, and the editor open on this project holds its own " + "copy there, so the first engine started beside it cannot make one. That failed attempt moves the " + "editor's copy aside, so the import was run again and loaded the extension, and the messages are from " + "that second run. The editor's copy is left beside the library as a ~RF….TMP file, which can be deleted " + "once the editor has exited.";
8047
+ function extensionRetryNote(libraries, stillNotLoaded = []) {
8048
+ const second = stillNotLoaded.length === 0 ? "so the import was run again and loaded the extension, and the messages are from that second run." : `so the import was run again, and it still could not load ${stillNotLoaded.join(" and ")}: this answer is from an import without ${stillNotLoaded.length === 1 ? "it" : "them"}, and its messages say what the engine hit.`;
8049
+ return `The first import could not load ${libraries.join(" and ")}: on Windows an engine running as the editor ` + "loads a GDExtension from a ~ copy beside the library, and the editor open on this project holds its own " + "copy there, so the first engine started beside it cannot make one. That failed attempt moves the " + `editor's copy aside, ${second} The editor's copy is left beside the library as a ~RF….TMP file, which ` + "can be deleted once the editor has exited.";
8028
8050
  }
8029
- async function runImport(godotPath, projectPath, once = importOnce) {
8030
- const first = await once(godotPath, projectPath);
8051
+ async function runImport(godotPath, projectPath, options = {}) {
8052
+ const once = options.once ?? importOnce;
8053
+ const pass = () => {
8054
+ for (const target of options.forcing ?? []) {
8055
+ forceNextImport(projectPath, target);
8056
+ }
8057
+ return once(godotPath, projectPath);
8058
+ };
8059
+ const first = await pass();
8031
8060
  const collided = librariesNotCopied(first.messages);
8032
8061
  if (collided.length === 0) {
8033
8062
  return first;
8034
8063
  }
8035
- return { ...await once(godotPath, projectPath), librariesRetried: collided };
8064
+ const second = await pass();
8065
+ const still = librariesNotCopied(second.messages);
8066
+ return {
8067
+ ...second,
8068
+ librariesRetried: collided,
8069
+ ...still.length === 0 ? {} : { librariesNotLoaded: still },
8070
+ extensionNote: extensionRetryNote(collided, still)
8071
+ };
8036
8072
  }
8037
8073
  async function importOnce(godotPath, projectPath) {
8038
8074
  const logDir = scratchDirectory("import");
8039
8075
  try {
8040
- const { stderr } = await run3(godotPath, ["--headless", "--log-file", join6(logDir, "engine.log"), "--path", projectPath, "--import"], { signal: callSignal() });
8076
+ const { stderr } = await run3(godotPath, ["--headless", "--log-file", join7(logDir, "engine.log"), "--path", projectPath, "--import"], { signal: callSignal() });
8041
8077
  return { ok: true, messages: problems(stderr) };
8042
8078
  } catch (error) {
8043
8079
  if (error instanceof Error && "stdout" in error && "stderr" in error) {
@@ -8059,12 +8095,12 @@ async function importOnce(godotPath, projectPath) {
8059
8095
  }
8060
8096
  async function runOperation(engine, operation, params, projectPath) {
8061
8097
  const paramsDir = scratchDirectory("params");
8062
- const paramsFile = join6(paramsDir, `${operation}.json`);
8063
- writeFileSync3(paramsFile, JSON.stringify(snakeCased(params)), "utf8");
8098
+ const paramsFile = join7(paramsDir, `${operation}.json`);
8099
+ writeFileSync4(paramsFile, JSON.stringify(snakeCased(params)), "utf8");
8064
8100
  const args = [
8065
8101
  "--headless",
8066
8102
  "--log-file",
8067
- join6(paramsDir, "engine.log"),
8103
+ join7(paramsDir, "engine.log"),
8068
8104
  "--path",
8069
8105
  projectPath,
8070
8106
  "--script",
@@ -8108,14 +8144,15 @@ var run3;
8108
8144
  var init_headless = __esm(() => {
8109
8145
  init_call_signal();
8110
8146
  init_game_log();
8147
+ init_reimport();
8111
8148
  init_scratch();
8112
8149
  run3 = promisify3(execFile3);
8113
8150
  });
8114
8151
 
8115
8152
  // src/update-check.ts
8116
- import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync4 } from "node:fs";
8153
+ import { existsSync as existsSync7, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync5 } from "node:fs";
8117
8154
  import { homedir as homedir3, tmpdir as tmpdir2 } from "node:os";
8118
- import { join as join7 } from "node:path";
8155
+ import { join as join8 } from "node:path";
8119
8156
  function packageUrl(root) {
8120
8157
  return `${root.replace(/\/+$/, "")}/gdharness/latest`;
8121
8158
  }
@@ -8137,28 +8174,28 @@ function cacheDirectory(environment) {
8137
8174
  };
8138
8175
  const home = set("HOME") ?? homedir3();
8139
8176
  if (process.platform === "win32") {
8140
- return join7(set("LOCALAPPDATA") ?? home, "gdharness");
8177
+ return join8(set("LOCALAPPDATA") ?? home, "gdharness");
8141
8178
  }
8142
8179
  if (process.platform === "darwin") {
8143
- return join7(home, "Library", "Caches", "gdharness");
8180
+ return join8(home, "Library", "Caches", "gdharness");
8144
8181
  }
8145
- return join7(set("XDG_CACHE_HOME") ?? join7(home, ".cache"), "gdharness");
8182
+ return join8(set("XDG_CACHE_HOME") ?? join8(home, ".cache"), "gdharness");
8146
8183
  }
8147
8184
  function cacheFile(environment = process.env) {
8148
8185
  try {
8149
8186
  const directory = cacheDirectory(environment);
8150
8187
  mkdirSync3(directory, { recursive: true, mode: 448 });
8151
- return join7(directory, "update-check.json");
8188
+ return join8(directory, "update-check.json");
8152
8189
  } catch {
8153
- return join7(tmpdir2(), "gdharness-update-check.json");
8190
+ return join8(tmpdir2(), "gdharness-update-check.json");
8154
8191
  }
8155
8192
  }
8156
8193
  function readCache(path) {
8157
8194
  try {
8158
- if (!existsSync6(path)) {
8195
+ if (!existsSync7(path)) {
8159
8196
  return null;
8160
8197
  }
8161
- const parsed = JSON.parse(readFileSync3(path, "utf8"));
8198
+ const parsed = JSON.parse(readFileSync4(path, "utf8"));
8162
8199
  if (typeof parsed !== "object" || parsed === null) {
8163
8200
  return null;
8164
8201
  }
@@ -8175,7 +8212,7 @@ function readCache(path) {
8175
8212
  }
8176
8213
  function writeCache(path, entry) {
8177
8214
  try {
8178
- writeFileSync4(path, JSON.stringify(entry), { encoding: "utf8", mode: 384 });
8215
+ writeFileSync5(path, JSON.stringify(entry), { encoding: "utf8", mode: 384 });
8179
8216
  } catch {}
8180
8217
  }
8181
8218
  function parts(version) {
@@ -8306,7 +8343,7 @@ var init_update_check = __esm(() => {
8306
8343
  });
8307
8344
 
8308
8345
  // src/server-version.ts
8309
- import { readFileSync as readFileSync4 } from "node:fs";
8346
+ import { readFileSync as readFileSync5 } from "node:fs";
8310
8347
  function editorIsStale(addonVersion, serverVersion, addonDigest, shippedDigest) {
8311
8348
  if (addonDigest !== undefined && addonDigest !== "" && shippedDigest !== undefined) {
8312
8349
  return addonDigest !== shippedDigest;
@@ -8348,7 +8385,7 @@ var init_server_version = __esm(() => {
8348
8385
  GODOT_DEBUG_MODE_DEFAULT = process.env["GODOT_DEBUG"] === "true" || DEBUG_MODE;
8349
8386
  SERVER_VERSION = (() => {
8350
8387
  try {
8351
- const pkg = JSON.parse(readFileSync4(new URL("../package.json", import.meta.url), "utf8"));
8388
+ const pkg = JSON.parse(readFileSync5(new URL("../package.json", import.meta.url), "utf8"));
8352
8389
  return typeof pkg.version === "string" ? pkg.version : "0.0.0";
8353
8390
  } catch {
8354
8391
  return "0.0.0";
@@ -8357,7 +8394,7 @@ var init_server_version = __esm(() => {
8357
8394
  });
8358
8395
 
8359
8396
  // src/issues.ts
8360
- import { createHash } from "node:crypto";
8397
+ import { createHash as createHash2 } from "node:crypto";
8361
8398
  import process7 from "node:process";
8362
8399
  function feedbackNotice(every) {
8363
8400
  return JSON.stringify({
@@ -8376,7 +8413,7 @@ function runtime() {
8376
8413
  function defectSignature(where, message) {
8377
8414
  const normalised = `${where}
8378
8415
  ${message}`.toLowerCase().replaceAll("\\", "/").replaceAll(/(?:[a-z]:)?(?:\/+[\w.-]+){2,}/gu, "<path>").replaceAll(/\d+/gu, "0");
8379
- return createHash("sha256").update(normalised).digest("hex").slice(0, 8);
8416
+ return createHash2("sha256").update(normalised).digest("hex").slice(0, 8);
8380
8417
  }
8381
8418
  function facts(where, message, godotVersion) {
8382
8419
  return [
@@ -8482,8 +8519,8 @@ var init_issues = __esm(() => {
8482
8519
  });
8483
8520
 
8484
8521
  // src/class-cache.ts
8485
- import { existsSync as existsSync7, readdirSync as readdirSync3, readFileSync as readFileSync5, statSync as statSync2 } from "node:fs";
8486
- import { join as join8 } from "node:path";
8522
+ import { existsSync as existsSync8, readdirSync as readdirSync3, readFileSync as readFileSync6, statSync as statSync3 } from "node:fs";
8523
+ import { join as join9 } from "node:path";
8487
8524
  function declaredClasses(projectPath) {
8488
8525
  const declared = new Map;
8489
8526
  eachScript(projectPath, (script, source) => {
@@ -8496,18 +8533,18 @@ function declaredClasses(projectPath) {
8496
8533
  }
8497
8534
  function eachScript(projectPath, each) {
8498
8535
  const visit = (directory, prefix) => {
8499
- if (existsSync7(join8(directory, ".gdignore"))) {
8536
+ if (existsSync8(join9(directory, ".gdignore"))) {
8500
8537
  return;
8501
8538
  }
8502
8539
  for (const entry of readdirSync3(directory, { withFileTypes: true })) {
8503
8540
  if (entry.name.startsWith(".")) {
8504
8541
  continue;
8505
8542
  }
8506
- const path = join8(directory, entry.name);
8543
+ const path = join9(directory, entry.name);
8507
8544
  if (entry.isDirectory()) {
8508
8545
  visit(path, `${prefix}${entry.name}/`);
8509
8546
  } else if (entry.isFile() && entry.name.endsWith(".gd")) {
8510
- each(`res://${prefix}${entry.name}`, readFileSync5(path, "utf8"));
8547
+ each(`res://${prefix}${entry.name}`, readFileSync6(path, "utf8"));
8511
8548
  }
8512
8549
  }
8513
8550
  };
@@ -8556,12 +8593,12 @@ function codeOf(source) {
8556
8593
  return code;
8557
8594
  }
8558
8595
  function cachedClasses(projectPath) {
8559
- const cache = join8(projectPath, ".godot", "global_script_class_cache.cfg");
8560
- if (!existsSync7(cache)) {
8596
+ const cache = join9(projectPath, ".godot", "global_script_class_cache.cfg");
8597
+ if (!existsSync8(cache)) {
8561
8598
  return null;
8562
8599
  }
8563
8600
  const listed = new Map;
8564
- const text = readFileSync5(cache, "utf8");
8601
+ const text = readFileSync6(cache, "utf8");
8565
8602
  for (const entry of text.matchAll(/"class":\s*&"([^"]+)"[\s\S]*?"path":\s*"([^"]+)"/g)) {
8566
8603
  listed.set(entry[1] ?? "", entry[2] ?? "");
8567
8604
  }
@@ -8604,32 +8641,33 @@ function missingMemberIn(message) {
8604
8641
  if (called !== null) {
8605
8642
  return { kind: "static method", member: called[1] ?? "", type: called[2] ?? "" };
8606
8643
  }
8607
- const inBase = /Cannot find member "([A-Za-z_]\w*)" in base "([A-Za-z_]\w*)((?:\.[A-Za-z_]\w*)*)"/.exec(message);
8644
+ const inBase = /Cannot find member "([A-Za-z_]\w*)" in base "([A-Za-z_]\w*)(?:\.([A-Za-z_]\w*))?"/.exec(message);
8608
8645
  if (inBase === null) {
8609
8646
  return null;
8610
8647
  }
8611
8648
  const member = inBase[1] ?? "";
8612
8649
  const type = inBase[2] ?? "";
8613
- const within = (inBase[3] ?? "").split(".").filter((part) => part !== "");
8614
- const enumName = within.at(-1);
8650
+ const enumName = inBase[3];
8615
8651
  return enumName === undefined ? { kind: "member", member, type } : { kind: "enum member", member, type, enum: enumName };
8616
8652
  }
8617
8653
  function declaresMember(source, missing) {
8618
8654
  const name = missing.member.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
8655
+ const top = String.raw`^(?:@\w+(?:\([^)]*\))?\s+)*`;
8656
+ const declared = (kinds) => new RegExp(String.raw`${top}(?:${kinds})\s+${name}\b`, "m").test(source);
8619
8657
  if (missing.kind === "method") {
8620
- return new RegExp(String.raw`^\s*(?:static\s+)?func\s+${name}\s*\(`, "m").test(source);
8658
+ return new RegExp(String.raw`${top}(?:static\s+)?func\s+${name}\s*\(`, "m").test(source);
8621
8659
  }
8622
8660
  if (missing.kind === "static method") {
8623
- return new RegExp(String.raw`^\s*static\s+func\s+${name}\s*\(`, "m").test(source);
8661
+ return new RegExp(String.raw`${top}static\s+func\s+${name}\s*\(`, "m").test(source);
8624
8662
  }
8625
8663
  if (missing.kind === "enum member") {
8626
8664
  return enumValues(source, missing.enum ?? "").includes(missing.member);
8627
8665
  }
8628
- const kinds = missing.kind === "property" ? "var|const|enum|class|signal|func" : "var|const|enum|class|signal";
8629
- return new RegExp(String.raw`^\s*(?:static\s+)?(?:@\w+(?:\([^)]*\))?\s+)*(?:${kinds})\s+${name}\b`, "m").test(source) || enumValues(source, null).includes(missing.member);
8666
+ const kinds = missing.kind === "property" ? String.raw`(?:static\s+)?(?:var|func)|const|enum|class|signal` : String.raw`static\s+(?:var|func)|const|enum|class`;
8667
+ return declared(kinds) || enumValues(source, null).includes(missing.member);
8630
8668
  }
8631
8669
  function enumValues(source, named) {
8632
- const opening = named === null ? /^\s*enum\s*\{([^}]*)\}/gm : new RegExp(String.raw`^\s*enum\s+${named.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\s*\{([^}]*)\}`, "gm");
8670
+ const opening = named === null ? /^enum\s*\{([^}]*)\}/gm : new RegExp(String.raw`^enum\s+${named.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\s*\{([^}]*)\}`, "gm");
8633
8671
  const values = [];
8634
8672
  for (const [, body] of source.matchAll(opening)) {
8635
8673
  const uncommented = (body ?? "").split(`
@@ -8672,21 +8710,22 @@ function classesNamedIn(source, known) {
8672
8710
  return [...named].sort();
8673
8711
  }
8674
8712
  function contradictedDiagnostics(messages, classes, sourceOf) {
8713
+ const keyOf = (missing) => [missing.type, missing.enum ?? "", missing.member].join(".");
8714
+ const denials = messages.map((message) => missingMemberIn(message)).filter((missing) => missing !== null);
8715
+ const calledOnTheClass = new Set(denials.filter((missing) => missing.kind === "static method").map((missing) => keyOf(missing)));
8675
8716
  const read = new Map;
8676
8717
  const found = new Map;
8677
- for (const message of messages) {
8678
- const missing = missingMemberIn(message);
8679
- const declaredIn = missing === null ? undefined : classes.get(missing.type);
8680
- if (missing === null || declaredIn === undefined) {
8718
+ for (const missing of denials) {
8719
+ const declaredIn = classes.get(missing.type);
8720
+ const key = keyOf(missing);
8721
+ if (declaredIn === undefined || missing.kind === "method" && calledOnTheClass.has(key)) {
8681
8722
  continue;
8682
8723
  }
8683
8724
  if (!read.has(declaredIn)) {
8684
8725
  read.set(declaredIn, sourceOf(declaredIn));
8685
8726
  }
8686
8727
  const source = read.get(declaredIn) ?? null;
8687
- const key = [missing.type, missing.enum ?? "", missing.member].join(".");
8688
- const already = found.get(key);
8689
- if (source !== null && declaresMember(source, missing) && already?.kind !== "static method") {
8728
+ if (source !== null && declaresMember(source, missing)) {
8690
8729
  found.set(key, { ...missing, declaredIn });
8691
8730
  }
8692
8731
  }
@@ -8736,9 +8775,9 @@ function failedReloadNote(failed) {
8736
8775
  ];
8737
8776
  const unchanged = lost.length === 0 && missing(failed.heldAfter, failed.heldBefore).length === 0 && missing(failed.constantsAfter, failed.constantsBefore).length === 0;
8738
8777
  const answered = failed.codeName === "" ? `error ${failed.code}` : `error ${failed.code}, ${failed.codeName}`;
8739
- const copy = unchanged ? "The copy the editor holds is as it was, with the members under heldBeforeReload and heldConstantsBeforeReload." : `The copy the editor holds changed even so${lost.length === 0 ? "" : `, and has lost ${lost.join(", ")}`}: anything diagnosed against it now is answered from what is left, and reloading it once it compiles rebuilds it.`;
8778
+ const copy = unchanged ? "Nothing was reloaded: the copy the editor holds is as it was, with the members under heldBeforeReload and heldConstantsBeforeReload." : `The copy the editor holds changed even so${lost.length === 0 ? "" : `, and has lost ${lost.join(", ")}`}: anything diagnosed against it now is answered from what is left, and reloading it once it compiles rebuilds it.`;
8740
8779
  const order = failed.names.length === 0 ? "" : ` If they deny a member of ${failed.names.join(" or ")} that its file declares, the editor's copy of that class is behind: reload that class first with reloadScript and then this script, the order that cleared it in the one project that has met this.`;
8741
- return `${failed.script} did not compile, so nothing was reloaded: Godot answered ${answered}. ${copy} ` + `script_diagnostics on it gives the errors.${order}`;
8780
+ return `${failed.script} did not compile: Godot answered ${answered}. ${copy} ` + `script_diagnostics on it gives the errors.${order}`;
8742
8781
  }
8743
8782
  function uncachedClassNote(types) {
8744
8783
  if (types.length === 0) {
@@ -8748,9 +8787,9 @@ function uncachedClassNote(types) {
8748
8787
  return `${types.join(", ")} ${one ? "is declared" : "are declared"} in this project and missing from ` + `.godot/global_script_class_cache.cfg, so a game launched now would not resolve ${one ? "it" : "them"} ` + "either. Try editor_rescan first: it asks the editor already running and starts nothing, and it " + "cleared this in 314ms in a second project with a 31-worker fan-out still importing. It is not " + "always enough, because a file another engine has already imported reads as settled and the " + "walk does not look inside it. project_import refresh_classes rewrites the cache from the " + "declarations on disk and does reach that case, at the cost of a short headless engine, which " + "is worth knowing when something else is already running against this project.";
8749
8788
  }
8750
8789
  function cacheWrittenAt(projectPath) {
8751
- const cache = join8(projectPath, ".godot", "global_script_class_cache.cfg");
8790
+ const cache = join9(projectPath, ".godot", "global_script_class_cache.cfg");
8752
8791
  try {
8753
- return statSync2(cache).mtimeMs;
8792
+ return statSync3(cache).mtimeMs;
8754
8793
  } catch {
8755
8794
  return null;
8756
8795
  }
@@ -8762,14 +8801,14 @@ function declaredSince(projectPath, paths, names, since) {
8762
8801
  return false;
8763
8802
  }
8764
8803
  try {
8765
- return statSync2(join8(projectPath, path.slice("res://".length))).mtimeMs > since;
8804
+ return statSync3(join9(projectPath, path.slice("res://".length))).mtimeMs > since;
8766
8805
  } catch {
8767
8806
  return false;
8768
8807
  }
8769
8808
  });
8770
8809
  }
8771
8810
  function cachedAtMissingPaths(cached, projectPath) {
8772
- return [...cached].filter(([, path]) => path.startsWith("res://") && !existsSync7(join8(projectPath, path.slice("res://".length)))).map(([name]) => name).sort();
8811
+ return [...cached].filter(([, path]) => path.startsWith("res://") && !existsSync8(join9(projectPath, path.slice("res://".length)))).map(([name]) => name).sort();
8773
8812
  }
8774
8813
  function staleAgainst(cached, projectPath) {
8775
8814
  return [...declaredClasses(projectPath)].filter(([name, path]) => cached.get(name) !== path).map(([name]) => name);
@@ -15956,7 +15995,7 @@ var init_paths = __esm(() => {
15956
15995
  });
15957
15996
 
15958
15997
  // src/resources.ts
15959
- import { readFileSync as readFileSync6 } from "node:fs";
15998
+ import { readFileSync as readFileSync7 } from "node:fs";
15960
15999
  import { extname, resolve as resolve2 } from "node:path";
15961
16000
  function ensureProjectPath(getProjectPath) {
15962
16001
  const projectPath = getProjectPath();
@@ -16122,7 +16161,7 @@ function readResourceText(uri, getProjectPath) {
16122
16161
  const parsedUri = parseGodotUri(uri);
16123
16162
  if (parsedUri.kind === "project-info") {
16124
16163
  const projectFilePath = resolveProjectFile(projectPath, "project.godot");
16125
- const rawProject = readFileSync6(projectFilePath, "utf-8");
16164
+ const rawProject = readFileSync7(projectFilePath, "utf-8");
16126
16165
  const parsedProject = parseProjectGodot(rawProject);
16127
16166
  return {
16128
16167
  mimeType: "application/json",
@@ -16131,7 +16170,7 @@ function readResourceText(uri, getProjectPath) {
16131
16170
  }
16132
16171
  const filePath = resolveProjectFile(projectPath, parsedUri.resourcePath);
16133
16172
  ensureAllowedExtension(parsedUri.kind, filePath);
16134
- const text = readFileSync6(filePath, "utf-8");
16173
+ const text = readFileSync7(filePath, "utf-8");
16135
16174
  return {
16136
16175
  mimeType: parsedUri.kind === "script" ? "text/x-gdscript" : "text/plain",
16137
16176
  text
@@ -16260,42 +16299,42 @@ function readParams(params, key) {
16260
16299
 
16261
16300
  // src/setup.ts
16262
16301
  import { spawnSync } from "node:child_process";
16263
- import { createHash as createHash2 } from "node:crypto";
16302
+ import { createHash as createHash3 } from "node:crypto";
16264
16303
  import {
16265
16304
  cpSync,
16266
- existsSync as existsSync8,
16305
+ existsSync as existsSync9,
16267
16306
  mkdirSync as mkdirSync4,
16268
16307
  readdirSync as readdirSync4,
16269
- readFileSync as readFileSync7,
16308
+ readFileSync as readFileSync8,
16270
16309
  rmdirSync,
16271
- rmSync as rmSync4,
16272
- statSync as statSync3,
16273
- writeFileSync as writeFileSync5
16310
+ rmSync as rmSync5,
16311
+ statSync as statSync4,
16312
+ writeFileSync as writeFileSync6
16274
16313
  } from "node:fs";
16275
- import { dirname as dirname4, join as join9 } from "node:path";
16314
+ import { dirname as dirname4, join as join10 } from "node:path";
16276
16315
  import { fileURLToPath } from "node:url";
16277
16316
  function editorAddonDigest(from = shippedAddonsDirectory()) {
16278
- const hash = createHash2("sha256");
16317
+ const hash = createHash3("sha256");
16279
16318
  const walk = (directory, relative) => {
16280
16319
  for (const name of readdirSync4(directory).sort()) {
16281
16320
  if (name === VERSION_MARKER || name === DIGEST_MARKER) {
16282
16321
  continue;
16283
16322
  }
16284
- const path = join9(directory, name);
16323
+ const path = join10(directory, name);
16285
16324
  const inside = `${relative}/${name}`;
16286
- if (statSync3(path).isDirectory()) {
16325
+ if (statSync4(path).isDirectory()) {
16287
16326
  walk(path, inside);
16288
16327
  } else {
16289
16328
  hash.update(`${inside}
16290
16329
  `);
16291
- hash.update(readFileSync7(path));
16330
+ hash.update(readFileSync8(path));
16292
16331
  hash.update(`
16293
16332
  `);
16294
16333
  }
16295
16334
  }
16296
16335
  };
16297
16336
  for (const name of EDITOR_PLUGINS) {
16298
- walk(join9(from, name), name);
16337
+ walk(join10(from, name), name);
16299
16338
  }
16300
16339
  return hash.digest("hex");
16301
16340
  }
@@ -16310,58 +16349,58 @@ function shippedEditorDigest() {
16310
16349
  return shippedDigest ?? undefined;
16311
16350
  }
16312
16351
  function installedEditorDigest(projectPath) {
16313
- const marker = join9(projectPath, "addons", ADDONS[0], DIGEST_MARKER);
16314
- if (!existsSync8(marker)) {
16352
+ const marker = join10(projectPath, "addons", ADDONS[0], DIGEST_MARKER);
16353
+ if (!existsSync9(marker)) {
16315
16354
  return null;
16316
16355
  }
16317
- const said = readFileSync7(marker, "utf8").trim();
16356
+ const said = readFileSync8(marker, "utf8").trim();
16318
16357
  return said === "" ? null : said;
16319
16358
  }
16320
16359
  function shippedAddonsDirectory() {
16321
- return join9(dirname4(fileURLToPath(import.meta.url)), "godot", "addons");
16360
+ return join10(dirname4(fileURLToPath(import.meta.url)), "godot", "addons");
16322
16361
  }
16323
16362
  function shippedOperationsScript() {
16324
- return join9(dirname4(fileURLToPath(import.meta.url)), "godot", "operations", "godot_operations.gd");
16363
+ return join10(dirname4(fileURLToPath(import.meta.url)), "godot", "operations", "godot_operations.gd");
16325
16364
  }
16326
16365
  function installAddons(projectPath, from = shippedAddonsDirectory()) {
16327
16366
  const installed = [];
16328
16367
  for (const name of ADDONS) {
16329
- const source = join9(from, name);
16330
- if (!existsSync8(join9(source, name === "gdharness_runtime" ? "runtime_autoload.gd" : "plugin.cfg"))) {
16368
+ const source = join10(from, name);
16369
+ if (!existsSync9(join10(source, name === "gdharness_runtime" ? "runtime_autoload.gd" : "plugin.cfg"))) {
16331
16370
  throw new Error(`The package holds no ${name} addon at ${source}.`);
16332
16371
  }
16333
- const target = join9(projectPath, "addons", name);
16334
- const replaced = existsSync8(target);
16335
- rmSync4(target, { recursive: true, force: true });
16372
+ const target = join10(projectPath, "addons", name);
16373
+ const replaced = existsSync9(target);
16374
+ rmSync5(target, { recursive: true, force: true });
16336
16375
  mkdirSync4(dirname4(target), { recursive: true });
16337
16376
  cpSync(source, target, { recursive: true });
16338
- writeFileSync5(join9(target, VERSION_MARKER), `${SERVER_VERSION}
16377
+ writeFileSync6(join10(target, VERSION_MARKER), `${SERVER_VERSION}
16339
16378
  `);
16340
16379
  installed.push({ name, path: target, replaced });
16341
16380
  }
16342
- writeFileSync5(join9(projectPath, "addons", ADDONS[0], DIGEST_MARKER), `${editorAddonDigest(from)}
16381
+ writeFileSync6(join10(projectPath, "addons", ADDONS[0], DIGEST_MARKER), `${editorAddonDigest(from)}
16343
16382
  `);
16344
16383
  return installed;
16345
16384
  }
16346
16385
  function installedAddonVersion(projectPath) {
16347
- const marker = join9(projectPath, "addons", ADDONS[0], VERSION_MARKER);
16348
- if (!existsSync8(marker)) {
16386
+ const marker = join10(projectPath, "addons", ADDONS[0], VERSION_MARKER);
16387
+ if (!existsSync9(marker)) {
16349
16388
  return null;
16350
16389
  }
16351
- const said = readFileSync7(marker, "utf8").trim();
16390
+ const said = readFileSync8(marker, "utf8").trim();
16352
16391
  return said === "" ? null : said;
16353
16392
  }
16354
16393
  function removeAddons(projectPath) {
16355
16394
  const removed = [];
16356
16395
  for (const name of ADDONS) {
16357
- const target = join9(projectPath, "addons", name);
16358
- if (existsSync8(target)) {
16359
- rmSync4(target, { recursive: true, force: true });
16396
+ const target = join10(projectPath, "addons", name);
16397
+ if (existsSync9(target)) {
16398
+ rmSync5(target, { recursive: true, force: true });
16360
16399
  removed.push(target);
16361
16400
  }
16362
16401
  }
16363
- const addons = join9(projectPath, "addons");
16364
- if (removed.length > 0 && existsSync8(addons) && readdirSync4(addons).length === 0) {
16402
+ const addons = join10(projectPath, "addons");
16403
+ if (removed.length > 0 && existsSync9(addons) && readdirSync4(addons).length === 0) {
16365
16404
  rmdirSync(addons);
16366
16405
  removed.push(addons);
16367
16406
  }
@@ -16395,7 +16434,7 @@ function loaderAmong(projectPath, named) {
16395
16434
  }
16396
16435
  let source = null;
16397
16436
  try {
16398
- source = readFileSync7(join9(projectPath, path), "utf8");
16437
+ source = readFileSync8(join10(projectPath, path), "utf8");
16399
16438
  } catch {}
16400
16439
  if (source?.includes(RUNTIME_AUTOLOAD.path)) {
16401
16440
  return { name: entry, path, exists: true };
@@ -16437,10 +16476,10 @@ function enabledPlugins(settings) {
16437
16476
  function inspectProject(projectPath) {
16438
16477
  const problems = [];
16439
16478
  const addons = ADDONS.map((name) => {
16440
- const directory = join9(projectPath, "addons", name);
16441
- const marker = join9(directory, VERSION_MARKER);
16442
- const installed = existsSync8(directory);
16443
- const version = existsSync8(marker) ? readFileSync7(marker, "utf8").trim() : null;
16479
+ const directory = join10(projectPath, "addons", name);
16480
+ const marker = join10(directory, VERSION_MARKER);
16481
+ const installed = existsSync9(directory);
16482
+ const version = existsSync9(marker) ? readFileSync8(marker, "utf8").trim() : null;
16444
16483
  const current = version === SERVER_VERSION;
16445
16484
  if (!installed) {
16446
16485
  problems.push(`addons/${name} is not installed; run gdharness setup`);
@@ -16449,7 +16488,7 @@ function inspectProject(projectPath) {
16449
16488
  }
16450
16489
  return { name, installed, version, current };
16451
16490
  });
16452
- const settings = parseProjectGodot(readFileSync7(join9(projectPath, "project.godot"), "utf8"));
16491
+ const settings = parseProjectGodot(readFileSync8(join10(projectPath, "project.godot"), "utf8"));
16453
16492
  const pluginsEnabled = enabledPlugins(settings);
16454
16493
  for (const name of EDITOR_PLUGINS) {
16455
16494
  if (!pluginsEnabled.includes(name)) {
@@ -16465,7 +16504,7 @@ function inspectProject(projectPath) {
16465
16504
  const tracked = trackedByGit(projectPath, [...named.values()]);
16466
16505
  const addonMissing = (path) => addons.some((addon) => !addon.installed && path.startsWith(`addons/${addon.name}/`));
16467
16506
  for (const [name, path] of named) {
16468
- if (!existsSync8(join9(projectPath, path))) {
16507
+ if (!existsSync9(join10(projectPath, path))) {
16469
16508
  if (!addonMissing(path)) {
16470
16509
  problems.push(`the ${name} autoload names res://${path}, which is not in this project, so it boots with a missing script; restore that file, or point the autoload at one that is here`);
16471
16510
  }
@@ -17755,7 +17794,7 @@ var init_tool_definitions = __esm(() => {
17755
17794
  },
17756
17795
  value: {
17757
17796
  ops: ["until"],
17758
- description: "until: the value to wait for, fitted to the property's type. On a property declared to hold an object, null, which the text null also means; any other value is refused, since the property can never hold it."
17797
+ description: "until: the value to wait for, fitted to the property's type. On a property holding an object, whether declared to or untyped and holding one or null, the text null means null. One declared to hold an object refuses any value other than null, since it can never hold it."
17759
17798
  },
17760
17799
  says: {
17761
17800
  type: "string",
@@ -32587,10 +32626,10 @@ var init_stdio2 = __esm(() => {
32587
32626
  });
32588
32627
 
32589
32628
  // src/runtime-client.ts
32590
- import { existsSync as existsSync10, readdirSync as readdirSync6, readFileSync as readFileSync9, statSync as statSync4, unlinkSync } from "node:fs";
32629
+ import { existsSync as existsSync11, readdirSync as readdirSync6, readFileSync as readFileSync10, statSync as statSync5, unlinkSync } from "node:fs";
32591
32630
  import { createConnection } from "node:net";
32592
32631
  import { tmpdir as tmpdir3 } from "node:os";
32593
- import { dirname as dirname7, join as join12, resolve as resolve3 } from "node:path";
32632
+ import { dirname as dirname7, join as join13, resolve as resolve3 } from "node:path";
32594
32633
  import { setTimeout as delay } from "node:timers/promises";
32595
32634
  function runtimeDirectory(variables = process.env) {
32596
32635
  const explicit = envValue("GDHARNESS_RUNTIME_DIR", variables);
@@ -32598,20 +32637,20 @@ function runtimeDirectory(variables = process.env) {
32598
32637
  return explicit;
32599
32638
  }
32600
32639
  const perUser = envValue("XDG_RUNTIME_DIR", variables);
32601
- return join12(perUser ?? tmpdir3(), "gdharness");
32640
+ return join13(perUser ?? tmpdir3(), "gdharness");
32602
32641
  }
32603
32642
  function runtimeDirectories(variables = process.env) {
32604
32643
  const candidates = [runtimeDirectory(variables)];
32605
32644
  const fallbacks = process.platform === "win32" ? [envValue("SystemRoot", variables) ?? envValue("windir", variables) ?? "C:\\Windows"] : ["/tmp", "/var/tmp"];
32606
32645
  for (const base of fallbacks) {
32607
- candidates.push(join12(base, process.platform === "win32" ? "Temp" : "", "gdharness"));
32646
+ candidates.push(join13(base, process.platform === "win32" ? "Temp" : "", "gdharness"));
32608
32647
  }
32609
32648
  return [...new Set(candidates.map((path) => resolve3(path)))];
32610
32649
  }
32611
32650
  function errorReportOf(pid, directories = runtimeDirectories()) {
32612
32651
  for (const directory of directories) {
32613
- const path = join12(directory, `runtime-${pid}.log`);
32614
- if (existsSync10(path)) {
32652
+ const path = join13(directory, `runtime-${pid}.log`);
32653
+ if (existsSync11(path)) {
32615
32654
  return path;
32616
32655
  }
32617
32656
  }
@@ -32642,7 +32681,7 @@ function reportAlreadyInTranscript(transcript, report) {
32642
32681
  function parseAnnouncement(file, pid) {
32643
32682
  let fields;
32644
32683
  try {
32645
- fields = asParams(JSON.parse(readFileSync9(file, "utf8")));
32684
+ fields = asParams(JSON.parse(readFileSync10(file, "utf8")));
32646
32685
  } catch {
32647
32686
  return { kind: "rubbish" };
32648
32687
  }
@@ -32659,7 +32698,7 @@ function parseAnnouncement(file, pid) {
32659
32698
  };
32660
32699
  let announcedAt;
32661
32700
  try {
32662
- announcedAt = statSync4(file).mtimeMs;
32701
+ announcedAt = statSync5(file).mtimeMs;
32663
32702
  } catch {}
32664
32703
  const when = announcedAt === undefined ? {} : { announcedAt };
32665
32704
  if (protocol !== RUNTIME_PROTOCOL) {
@@ -32700,7 +32739,7 @@ function discoverRuntimes(directories = runtimeDirectories()) {
32700
32739
  function wentAway(file, pid) {
32701
32740
  let project = "";
32702
32741
  try {
32703
- const fields = asParams(JSON.parse(readFileSync9(file, "utf8")));
32742
+ const fields = asParams(JSON.parse(readFileSync10(file, "utf8")));
32704
32743
  project = readString(readParams(fields, "project") ?? {}, "path") ?? "";
32705
32744
  } catch {
32706
32745
  return;
@@ -32743,7 +32782,7 @@ function strangersAmong(candidates, now, startTimes = startTimesOf) {
32743
32782
  }).map((one) => one.file));
32744
32783
  }
32745
32784
  function announcedIn(directory) {
32746
- if (!existsSync10(directory)) {
32785
+ if (!existsSync11(directory)) {
32747
32786
  return [];
32748
32787
  }
32749
32788
  const found = [];
@@ -32752,20 +32791,20 @@ function announcedIn(directory) {
32752
32791
  for (const entry of readdirSync6(directory)) {
32753
32792
  const report = ERROR_REPORT_PATTERN.exec(entry);
32754
32793
  if (report) {
32755
- sweepErrorReport(join12(directory, entry), Number.parseInt(report[1] ?? "", 10));
32794
+ sweepErrorReport(join13(directory, entry), Number.parseInt(report[1] ?? "", 10));
32756
32795
  continue;
32757
32796
  }
32758
32797
  const match = ANNOUNCEMENT_PATTERN.exec(entry);
32759
32798
  if (!match) {
32760
32799
  continue;
32761
32800
  }
32762
- const file = join12(directory, entry);
32801
+ const file = join13(directory, entry);
32763
32802
  const pid = Number.parseInt(match[1] ?? "", 10);
32764
32803
  const running = alive(pid);
32765
32804
  entries.push({ file, pid, running });
32766
32805
  if (running) {
32767
32806
  try {
32768
- candidates.push({ file, pid, writtenAt: statSync4(file).mtimeMs });
32807
+ candidates.push({ file, pid, writtenAt: statSync5(file).mtimeMs });
32769
32808
  } catch {}
32770
32809
  }
32771
32810
  }
@@ -32787,7 +32826,7 @@ function announcedIn(directory) {
32787
32826
  found.push(announced);
32788
32827
  }
32789
32828
  const held = new Set(entries.map((one) => one.file));
32790
- const here = join12(directory, ".");
32829
+ const here = join13(directory, ".");
32791
32830
  for (const file of verdicts.keys()) {
32792
32831
  if (dirname7(file) === here && !held.has(file)) {
32793
32832
  verdicts.delete(file);
@@ -32796,7 +32835,7 @@ function announcedIn(directory) {
32796
32835
  return found;
32797
32836
  }
32798
32837
  async function announcementEnded(pid, directories = runtimeDirectories()) {
32799
- const files = directories.map((directory) => join12(directory, `runtime-${pid}.json`)).filter((file) => existsSync10(file));
32838
+ const files = directories.map((directory) => join13(directory, `runtime-${pid}.json`)).filter((file) => existsSync11(file));
32800
32839
  const first = files[0];
32801
32840
  if (first === undefined) {
32802
32841
  return;
@@ -32822,7 +32861,7 @@ function sweepErrorReport(path, pid, now = Date.now()) {
32822
32861
  return;
32823
32862
  }
32824
32863
  try {
32825
- if (now - statSync4(path).mtimeMs >= ERROR_REPORT_KEEP_MS) {
32864
+ if (now - statSync5(path).mtimeMs >= ERROR_REPORT_KEEP_MS) {
32826
32865
  unlinkSync(path);
32827
32866
  }
32828
32867
  } catch {}
@@ -33096,14 +33135,14 @@ var init_runtime_client = __esm(() => {
33096
33135
  });
33097
33136
 
33098
33137
  // src/boot-note.ts
33099
- import { mkdirSync as mkdirSync6, readFileSync as readFileSync10, writeFileSync as writeFileSync7 } from "node:fs";
33100
- import { dirname as dirname8, join as join13 } from "node:path";
33138
+ import { mkdirSync as mkdirSync6, readFileSync as readFileSync11, writeFileSync as writeFileSync8 } from "node:fs";
33139
+ import { dirname as dirname8, join as join14 } from "node:path";
33101
33140
  function bootNotePath(projectPath) {
33102
- return join13(projectPath, ".godot", "gdharness-boot.json");
33141
+ return join14(projectPath, ".godot", "gdharness-boot.json");
33103
33142
  }
33104
33143
  function readBootNote(projectPath) {
33105
33144
  try {
33106
- const noted = readNumber(asParams(JSON.parse(readFileSync10(bootNotePath(projectPath), "utf8"))), "announcedAfterMs");
33145
+ const noted = readNumber(asParams(JSON.parse(readFileSync11(bootNotePath(projectPath), "utf8"))), "announcedAfterMs");
33107
33146
  return noted !== undefined && Number.isFinite(noted) && noted > 0 ? noted : null;
33108
33147
  } catch {
33109
33148
  return null;
@@ -33113,7 +33152,7 @@ function writeBootNote(projectPath, announcedAfterMs) {
33113
33152
  try {
33114
33153
  const path = bootNotePath(projectPath);
33115
33154
  mkdirSync6(dirname8(path), { recursive: true });
33116
- writeFileSync7(path, JSON.stringify({ announcedAfterMs: Math.round(announcedAfterMs), at: new Date().toISOString() }, null, 2), "utf8");
33155
+ writeFileSync8(path, JSON.stringify({ announcedAfterMs: Math.round(announcedAfterMs), at: new Date().toISOString() }, null, 2), "utf8");
33117
33156
  } catch {}
33118
33157
  }
33119
33158
  function halfAsLongAgain(lastBootMs) {
@@ -33131,10 +33170,10 @@ var init_boot_note = __esm(() => {
33131
33170
  });
33132
33171
 
33133
33172
  // src/breakpoint-note.ts
33134
- import { mkdirSync as mkdirSync7, readFileSync as readFileSync11, rmSync as rmSync6, writeFileSync as writeFileSync8 } from "node:fs";
33135
- import { dirname as dirname9, isAbsolute as isAbsolute2, join as join14, relative as relative2 } from "node:path";
33173
+ import { mkdirSync as mkdirSync7, readFileSync as readFileSync12, rmSync as rmSync7, writeFileSync as writeFileSync9 } from "node:fs";
33174
+ import { dirname as dirname9, isAbsolute as isAbsolute2, join as join15, relative as relative2 } from "node:path";
33136
33175
  function breakpointNotePath(projectPath) {
33137
- return join14(projectPath, ".godot", "gdharness-breakpoints.json");
33176
+ return join15(projectPath, ".godot", "gdharness-breakpoints.json");
33138
33177
  }
33139
33178
  function writeBreakpointNote(projectPath, held) {
33140
33179
  const path = breakpointNotePath(projectPath);
@@ -33142,7 +33181,7 @@ function writeBreakpointNote(projectPath, held) {
33142
33181
  const inside = held.map((one) => ({ scriptPath: realPathOr(one.scriptPath), lines: one.lines })).filter((one) => one.lines.length > 0 && withinProject(root, one.scriptPath));
33143
33182
  try {
33144
33183
  if (inside.length === 0) {
33145
- rmSync6(path, { force: true });
33184
+ rmSync7(path, { force: true });
33146
33185
  return;
33147
33186
  }
33148
33187
  const note = {
@@ -33152,7 +33191,7 @@ function writeBreakpointNote(projectPath, held) {
33152
33191
  }))
33153
33192
  };
33154
33193
  mkdirSync7(dirname9(path), { recursive: true });
33155
- writeFileSync8(path, `${JSON.stringify(note, null, 2)}
33194
+ writeFileSync9(path, `${JSON.stringify(note, null, 2)}
33156
33195
  `, "utf8");
33157
33196
  } catch (error) {
33158
33197
  console.error(`[SERVER] Could not record the breakpoints at ${path}: ${errorMessage(error)}`);
@@ -33160,7 +33199,7 @@ function writeBreakpointNote(projectPath, held) {
33160
33199
  }
33161
33200
  function readBreakpointNote(projectPath) {
33162
33201
  try {
33163
- const parsed = JSON.parse(readFileSync11(breakpointNotePath(projectPath), "utf8"));
33202
+ const parsed = JSON.parse(readFileSync12(breakpointNotePath(projectPath), "utf8"));
33164
33203
  if (typeof parsed !== "object" || parsed === null || !Array.isArray(parsed.breakpoints)) {
33165
33204
  return [];
33166
33205
  }
@@ -33175,7 +33214,7 @@ function readBreakpointNote(projectPath) {
33175
33214
  }
33176
33215
  const lines = Array.isArray(listed) ? listed.filter((line) => Number.isInteger(line) && line > 0) : [];
33177
33216
  if (lines.length > 0) {
33178
- held.push({ scriptPath: realPathOr(join14(projectPath, script)), lines });
33217
+ held.push({ scriptPath: realPathOr(join15(projectPath, script)), lines });
33179
33218
  }
33180
33219
  }
33181
33220
  return held;
@@ -33193,14 +33232,14 @@ var init_breakpoint_note = __esm(() => {
33193
33232
  });
33194
33233
 
33195
33234
  // src/class-note.ts
33196
- import { mkdirSync as mkdirSync8, readFileSync as readFileSync12, writeFileSync as writeFileSync9 } from "node:fs";
33197
- import { dirname as dirname10, join as join15 } from "node:path";
33235
+ import { mkdirSync as mkdirSync8, readFileSync as readFileSync13, writeFileSync as writeFileSync10 } from "node:fs";
33236
+ import { dirname as dirname10, join as join16 } from "node:path";
33198
33237
  function classNotePath(projectPath) {
33199
- return join15(projectPath, ".godot", "gdharness-classes.json");
33238
+ return join16(projectPath, ".godot", "gdharness-classes.json");
33200
33239
  }
33201
33240
  function readClassNote(projectPath) {
33202
33241
  try {
33203
- const fields = asParams(JSON.parse(readFileSync12(classNotePath(projectPath), "utf8")));
33242
+ const fields = asParams(JSON.parse(readFileSync13(classNotePath(projectPath), "utf8")));
33204
33243
  const rebuiltWith = readArray(fields, "rebuiltWith");
33205
33244
  if (rebuiltWith === undefined || !rebuiltWith.every((name) => typeof name === "string")) {
33206
33245
  return null;
@@ -33218,23 +33257,23 @@ function writeClassNote(projectPath, note) {
33218
33257
  try {
33219
33258
  const path = classNotePath(projectPath);
33220
33259
  mkdirSync8(dirname10(path), { recursive: true });
33221
- writeFileSync9(path, `${JSON.stringify(note, null, 2)}
33260
+ writeFileSync10(path, `${JSON.stringify(note, null, 2)}
33222
33261
  `, "utf8");
33223
33262
  } catch {}
33224
33263
  }
33225
33264
  var init_class_note = () => {};
33226
33265
 
33227
33266
  // src/config-pin.ts
33228
- import { existsSync as existsSync11, readFileSync as readFileSync13 } from "node:fs";
33229
- import { join as join16 } from "node:path";
33267
+ import { existsSync as existsSync12, readFileSync as readFileSync14 } from "node:fs";
33268
+ import { join as join17 } from "node:path";
33230
33269
  function versionsNamedInConfig(projectPath) {
33231
- const config = join16(projectPath, ".mcp.json");
33232
- if (!existsSync11(config)) {
33270
+ const config = join17(projectPath, ".mcp.json");
33271
+ if (!existsSync12(config)) {
33233
33272
  return [];
33234
33273
  }
33235
33274
  let text;
33236
33275
  try {
33237
- text = readFileSync13(config, "utf8");
33276
+ text = readFileSync14(config, "utf8");
33238
33277
  JSON.parse(text);
33239
33278
  } catch {
33240
33279
  return [];
@@ -33994,25 +34033,25 @@ var init_desktop = () => {};
33994
34033
  // src/editor-log.ts
33995
34034
  import {
33996
34035
  closeSync,
33997
- existsSync as existsSync12,
34036
+ existsSync as existsSync13,
33998
34037
  mkdirSync as mkdirSync9,
33999
34038
  openSync,
34000
- readFileSync as readFileSync14,
34039
+ readFileSync as readFileSync15,
34001
34040
  readSync,
34002
- rmSync as rmSync7,
34003
- statSync as statSync5,
34004
- writeFileSync as writeFileSync10
34041
+ rmSync as rmSync8,
34042
+ statSync as statSync6,
34043
+ writeFileSync as writeFileSync11
34005
34044
  } from "node:fs";
34006
- import { dirname as dirname11, join as join17 } from "node:path";
34045
+ import { dirname as dirname11, join as join18 } from "node:path";
34007
34046
  function editorLogPath(projectPath) {
34008
- return join17(projectPath, ".godot", "gdharness-editor.log");
34047
+ return join18(projectPath, ".godot", "gdharness-editor.log");
34009
34048
  }
34010
34049
  function editorLogNotePath(projectPath) {
34011
- return join17(projectPath, ".godot", "gdharness-editor.json");
34050
+ return join18(projectPath, ".godot", "gdharness-editor.json");
34012
34051
  }
34013
34052
  function readEditorLogNote(projectPath) {
34014
34053
  try {
34015
- const fields = asParams(JSON.parse(readFileSync14(editorLogNotePath(projectPath), "utf8")));
34054
+ const fields = asParams(JSON.parse(readFileSync15(editorLogNotePath(projectPath), "utf8")));
34016
34055
  const pid = readNumber(fields, "pid");
34017
34056
  const startedAt = readString(fields, "startedAt");
34018
34057
  return pid !== undefined && Number.isInteger(pid) && pid > 0 && startedAt !== undefined ? { pid, startedAt } : null;
@@ -34024,15 +34063,15 @@ function clearEditorLog(projectPath) {
34024
34063
  try {
34025
34064
  const path = editorLogPath(projectPath);
34026
34065
  mkdirSync9(dirname11(path), { recursive: true });
34027
- rmSync7(path, { force: true });
34028
- rmSync7(editorLogNotePath(projectPath), { force: true });
34066
+ rmSync8(path, { force: true });
34067
+ rmSync8(editorLogNotePath(projectPath), { force: true });
34029
34068
  } catch {}
34030
34069
  }
34031
34070
  function writeEditorLogNote(projectPath, pid) {
34032
34071
  try {
34033
34072
  const path = editorLogNotePath(projectPath);
34034
34073
  mkdirSync9(dirname11(path), { recursive: true });
34035
- writeFileSync10(path, JSON.stringify({ pid, startedAt: new Date().toISOString() }, null, 2), "utf8");
34074
+ writeFileSync11(path, JSON.stringify({ pid, startedAt: new Date().toISOString() }, null, 2), "utf8");
34036
34075
  } catch {}
34037
34076
  }
34038
34077
  function theConsoleWasNotCaptured(why, projectPath) {
@@ -34053,7 +34092,7 @@ function editorConsole(projectPath, editorPid, openedByAServer) {
34053
34092
  return { kind: "older server", notedPid: noted?.pid ?? null };
34054
34093
  }
34055
34094
  const path = editorLogPath(projectPath);
34056
- if (!existsSync12(path)) {
34095
+ if (!existsSync13(path)) {
34057
34096
  return { kind: "not written" };
34058
34097
  }
34059
34098
  const log = new GameLog;
@@ -34063,9 +34102,9 @@ function editorConsole(projectPath, editorPid, openedByAServer) {
34063
34102
  return { log, path, ...of === undefined ? {} : { readBytes: text.length, of } };
34064
34103
  }
34065
34104
  function theFirstOf(path) {
34066
- const whole = statSync5(path).size;
34105
+ const whole = statSync6(path).size;
34067
34106
  if (whole <= MOST_CONSOLE_BYTES) {
34068
- return { text: readFileSync14(path, "utf8") };
34107
+ return { text: readFileSync15(path, "utf8") };
34069
34108
  }
34070
34109
  const handle = openSync(path, "r");
34071
34110
  try {
@@ -38590,13 +38629,13 @@ var init_process_time = __esm(() => {
38590
38629
  });
38591
38630
 
38592
38631
  // src/project-scan.ts
38593
- import { existsSync as existsSync13, readdirSync as readdirSync7, readFileSync as readFileSync15 } from "node:fs";
38594
- import { join as join18 } from "node:path";
38632
+ import { existsSync as existsSync14, readdirSync as readdirSync7, readFileSync as readFileSync16 } from "node:fs";
38633
+ import { join as join19 } from "node:path";
38595
38634
  function skipped(name) {
38596
38635
  return name.startsWith(".") || SKIPPED.has(name);
38597
38636
  }
38598
38637
  function steppedOver(directory) {
38599
- return existsSync13(join18(directory, ".gdignore"));
38638
+ return existsSync14(join19(directory, ".gdignore"));
38600
38639
  }
38601
38640
  function projectStructure(projectPath) {
38602
38641
  const structure = { scenes: 0, scripts: 0, assets: 0, other: 0 };
@@ -38609,7 +38648,7 @@ function projectStructure(projectPath) {
38609
38648
  continue;
38610
38649
  }
38611
38650
  if (entry.isDirectory()) {
38612
- visit(join18(directory, entry.name));
38651
+ visit(join19(directory, entry.name));
38613
38652
  } else if (entry.isFile()) {
38614
38653
  const extension = entry.name.split(".").pop()?.toLowerCase() ?? "";
38615
38654
  if (extension === "tscn") {
@@ -38639,14 +38678,14 @@ function scriptsWithoutUid(projectPath) {
38639
38678
  }
38640
38679
  const spelled = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
38641
38680
  if (entry.isDirectory()) {
38642
- visit(join18(directory, entry.name), spelled);
38681
+ visit(join19(directory, entry.name), spelled);
38643
38682
  continue;
38644
38683
  }
38645
38684
  if (!entry.isFile()) {
38646
38685
  continue;
38647
38686
  }
38648
38687
  const extension = entry.name.split(".").pop()?.toLowerCase() ?? "";
38649
- if (CARRIES_A_UID.has(extension) && !existsSync13(join18(directory, `${entry.name}.uid`))) {
38688
+ if (CARRIES_A_UID.has(extension) && !existsSync14(join19(directory, `${entry.name}.uid`))) {
38650
38689
  found.push(spelled);
38651
38690
  }
38652
38691
  }
@@ -38681,7 +38720,7 @@ function searchProject(projectPath, options) {
38681
38720
  if (skipped(entry.name)) {
38682
38721
  continue;
38683
38722
  }
38684
- const entryPath = join18(directory, entry.name);
38723
+ const entryPath = join19(directory, entry.name);
38685
38724
  if (entry.isDirectory()) {
38686
38725
  visit(entryPath);
38687
38726
  continue;
@@ -38692,7 +38731,7 @@ function searchProject(projectPath, options) {
38692
38731
  }
38693
38732
  result.summary.files_searched += 1;
38694
38733
  const matches = [];
38695
- for (const [index, line] of readFileSync15(entryPath, "utf8").split(`
38734
+ for (const [index, line] of readFileSync16(entryPath, "utf8").split(`
38696
38735
  `).entries()) {
38697
38736
  if (full()) {
38698
38737
  break;
@@ -38720,27 +38759,6 @@ var init_project_scan = __esm(() => {
38720
38759
  CARRIES_A_UID = new Set(["gd", "shader", "gdshader"]);
38721
38760
  });
38722
38761
 
38723
- // src/reimport.ts
38724
- import { createHash as createHash3 } from "node:crypto";
38725
- import { existsSync as existsSync14, readFileSync as readFileSync16, rmSync as rmSync8, statSync as statSync6, utimesSync, writeFileSync as writeFileSync11 } from "node:fs";
38726
- import { basename as basename3, join as join19 } from "node:path";
38727
- function forceNextImport(projectPath, resourcePath) {
38728
- const relative = resourcePath.replace(/^res:\/\//, "");
38729
- const sidecar = join19(projectPath, `${relative}.import`);
38730
- if (!existsSync14(sidecar)) {
38731
- return;
38732
- }
38733
- const { atime, mtimeMs } = statSync6(sidecar);
38734
- writeFileSync11(sidecar, readFileSync16(sidecar, "utf8").replace(/^valid=false\r?\n/m, ""));
38735
- utimesSync(sidecar, atime, new Date(mtimeMs + 2000));
38736
- rmSync8(importRecord(projectPath, resourcePath), { force: true });
38737
- }
38738
- function importRecord(projectPath, resourcePath) {
38739
- const digest = createHash3("md5").update(resourcePath).digest("hex");
38740
- return join19(projectPath, ".godot", "imported", `${basename3(resourcePath)}-${digest}.md5`);
38741
- }
38742
- var init_reimport = () => {};
38743
-
38744
38762
  // src/restart-note.ts
38745
38763
  import { mkdirSync as mkdirSync10, readFileSync as readFileSync17, rmSync as rmSync9, writeFileSync as writeFileSync12 } from "node:fs";
38746
38764
  import { dirname as dirname13, join as join20 } from "node:path";
@@ -40651,7 +40669,7 @@ class GodotServer {
40651
40669
  uidsCreated: given,
40652
40670
  stillWithoutUid: after,
40653
40671
  note: uidsLeftNote(after.length),
40654
- ...imported.librariesRetried === undefined ? {} : { extensionNote: extensionRetryNote(imported.librariesRetried) }
40672
+ ...imported.extensionNote === undefined ? {} : { extensionNote: imported.extensionNote }
40655
40673
  }
40656
40674
  }, scanned));
40657
40675
  }
@@ -40796,19 +40814,16 @@ class GodotServer {
40796
40814
  if (!engine.ok) {
40797
40815
  return { ok: false, response: engine.response };
40798
40816
  }
40799
- for (const target of targets) {
40800
- forceNextImport(projectPath, target);
40801
- }
40802
40817
  const scanned = await this.untilTheEditorsScanIsWritten(projectPath);
40803
- const imported = await runImport(engine.value, projectPath);
40818
+ const imported = await runImport(engine.value, projectPath, { forcing: targets });
40804
40819
  if (!imported.ok) {
40805
40820
  return { ok: false, response: this.answer(withScanWait(imported, scanned)) };
40806
40821
  }
40807
40822
  if (imported.messages.length > 0) {
40808
40823
  extra["engine_messages"] = imported.messages;
40809
40824
  }
40810
- if (imported.librariesRetried !== undefined) {
40811
- extra["extensionNote"] = extensionRetryNote(imported.librariesRetried);
40825
+ if (imported.extensionNote !== undefined) {
40826
+ extra["extensionNote"] = imported.extensionNote;
40812
40827
  }
40813
40828
  }
40814
40829
  const after = await this.operation("get_import_status", resourcePath === undefined ? { includeUpToDate: true } : { resourcePath }, projectPath);
@@ -41299,7 +41314,7 @@ class GodotServer {
41299
41314
  const notes = [];
41300
41315
  if (contradicted.length > 0) {
41301
41316
  const dependsOn = new Set;
41302
- for (const one of contradicted) {
41317
+ for (const one of contradicted.filter((entry) => entry.kind === "method" || entry.kind === "property")) {
41303
41318
  const source = cached === null ? null : this.sourceOfClass(projectPath, one.declaredIn);
41304
41319
  if (source === null || cached === null) {
41305
41320
  continue;
@@ -41339,9 +41354,10 @@ class GodotServer {
41339
41354
  });
41340
41355
  }
41341
41356
  classesAScriptNames(projectPath, scriptPath) {
41342
- const source = this.sourceOfClass(projectPath, scriptPath);
41357
+ const resourcePath = scriptPath.startsWith("res://") ? scriptPath : `res://${scriptPath.replace(/^\/+/, "")}`;
41358
+ const source = this.sourceOfClass(projectPath, resourcePath);
41343
41359
  const known = cachedClasses(projectPath) ?? declaredClasses(projectPath);
41344
- return source === null ? [] : classesNamedIn(source, known).filter((name) => known.get(name) !== scriptPath);
41360
+ return source === null ? [] : classesNamedIn(source, known).filter((name) => known.get(name) !== resourcePath);
41345
41361
  }
41346
41362
  sourceOfClass(projectPath, resourcePath) {
41347
41363
  const contained = resolveWithinProject(projectPath, resourcePath);
@@ -43627,7 +43643,6 @@ var init_server3 = __esm(() => {
43627
43643
  init_process_children();
43628
43644
  init_process_time();
43629
43645
  init_project_scan();
43630
- init_reimport();
43631
43646
  init_resources();
43632
43647
  init_restart_note();
43633
43648
  init_run_record();
@@ -44587,8 +44602,8 @@ init_setup();
44587
44602
  // src/skill.ts
44588
44603
  init_setup();
44589
44604
  init_tool_definitions();
44590
- import { existsSync as existsSync9, mkdirSync as mkdirSync5, readdirSync as readdirSync5, rmSync as rmSync5, writeFileSync as writeFileSync6 } from "node:fs";
44591
- import { basename as basename2, dirname as dirname5, join as join10 } from "node:path";
44605
+ import { existsSync as existsSync10, mkdirSync as mkdirSync5, readdirSync as readdirSync5, rmSync as rmSync6, writeFileSync as writeFileSync7 } from "node:fs";
44606
+ import { basename as basename3, dirname as dirname5, join as join11 } from "node:path";
44592
44607
 
44593
44608
  // src/tool-reference.ts
44594
44609
  init_headless_operations();
@@ -44684,7 +44699,7 @@ function renderToolsMarkdown() {
44684
44699
 
44685
44700
  // src/skill.ts
44686
44701
  var SKILL_NAME = "gdharness";
44687
- var SHARED_SKILLS = join10(".agents", "skills");
44702
+ var SHARED_SKILLS = join11(".agents", "skills");
44688
44703
  function skillMarkdown(version) {
44689
44704
  return `---
44690
44705
  name: ${SKILL_NAME}
@@ -44875,27 +44890,27 @@ whatever the engine said on stderr comes back under \`engine_messages\`.
44875
44890
  function skillFiles(version) {
44876
44891
  return new Map([
44877
44892
  ["SKILL.md", skillMarkdown(version)],
44878
- [join10("references", "tools.md"), renderToolsMarkdown()]
44893
+ [join11("references", "tools.md"), renderToolsMarkdown()]
44879
44894
  ]);
44880
44895
  }
44881
44896
  function skillDirectories(harnesses, projectPath, exists) {
44882
- const directories = [join10(projectPath, SHARED_SKILLS, SKILL_NAME)];
44897
+ const directories = [join11(projectPath, SHARED_SKILLS, SKILL_NAME)];
44883
44898
  for (const harness of harnesses) {
44884
44899
  if (harness.skills === undefined) {
44885
44900
  continue;
44886
44901
  }
44887
- const own = join10(projectPath, harness.skills.dir);
44888
- if ((!harness.skills.shared || exists(own)) && !directories.includes(join10(own, SKILL_NAME))) {
44889
- directories.push(join10(own, SKILL_NAME));
44902
+ const own = join11(projectPath, harness.skills.dir);
44903
+ if ((!harness.skills.shared || exists(own)) && !directories.includes(join11(own, SKILL_NAME))) {
44904
+ directories.push(join11(own, SKILL_NAME));
44890
44905
  }
44891
44906
  }
44892
44907
  return directories;
44893
44908
  }
44894
44909
  function everySkillDirectory(projectPath, harnesses) {
44895
- const directories = new Set([join10(projectPath, SHARED_SKILLS, SKILL_NAME)]);
44910
+ const directories = new Set([join11(projectPath, SHARED_SKILLS, SKILL_NAME)]);
44896
44911
  for (const harness of harnesses) {
44897
44912
  if (harness.skills !== undefined) {
44898
- directories.add(join10(projectPath, harness.skills.dir, SKILL_NAME));
44913
+ directories.add(join11(projectPath, harness.skills.dir, SKILL_NAME));
44899
44914
  }
44900
44915
  }
44901
44916
  return [...directories];
@@ -44903,8 +44918,8 @@ function everySkillDirectory(projectPath, harnesses) {
44903
44918
  function removeSkill(directories, projectPath) {
44904
44919
  const removed = [];
44905
44920
  for (const directory of directories) {
44906
- if (existsSync9(directory)) {
44907
- rmSync5(directory, { recursive: true, force: true });
44921
+ if (existsSync10(directory)) {
44922
+ rmSync6(directory, { recursive: true, force: true });
44908
44923
  pruneEmpty(dirname5(directory), projectPath);
44909
44924
  removed.push(directory);
44910
44925
  }
@@ -44912,43 +44927,43 @@ function removeSkill(directories, projectPath) {
44912
44927
  return removed;
44913
44928
  }
44914
44929
  function pruneEmpty(directory, projectPath) {
44915
- const shared = join10(projectPath, ".agents");
44930
+ const shared = join11(projectPath, ".agents");
44916
44931
  let path = directory;
44917
- while (path !== projectPath && (basename2(path) === "skills" || path === shared)) {
44932
+ while (path !== projectPath && (basename3(path) === "skills" || path === shared)) {
44918
44933
  if (readdirSync5(path).length > 0) {
44919
44934
  return;
44920
44935
  }
44921
- rmSync5(path, { recursive: true, force: true });
44936
+ rmSync6(path, { recursive: true, force: true });
44922
44937
  path = dirname5(path);
44923
44938
  }
44924
44939
  }
44925
44940
  function writeSkill(directories, version) {
44926
44941
  const files = skillFiles(version);
44927
44942
  return directories.map((directory) => {
44928
- const replaced = existsSync9(directory);
44929
- rmSync5(directory, { recursive: true, force: true });
44943
+ const replaced = existsSync10(directory);
44944
+ rmSync6(directory, { recursive: true, force: true });
44930
44945
  for (const [name, contents] of files) {
44931
- const path = join10(directory, name);
44946
+ const path = join11(directory, name);
44932
44947
  mkdirSync5(dirname5(path), { recursive: true });
44933
- writeFileSync6(path, contents, "utf8");
44948
+ writeFileSync7(path, contents, "utf8");
44934
44949
  }
44935
44950
  return { path: directory, replaced };
44936
44951
  });
44937
44952
  }
44938
44953
 
44939
44954
  // src/version.ts
44940
- import { readFileSync as readFileSync8 } from "node:fs";
44941
- import { dirname as dirname6, join as join11 } from "node:path";
44955
+ import { readFileSync as readFileSync9 } from "node:fs";
44956
+ import { dirname as dirname6, join as join12 } from "node:path";
44942
44957
  import { fileURLToPath as fileURLToPath2 } from "node:url";
44943
44958
  function getLocalVersion() {
44944
44959
  const currentDirectory = dirname6(fileURLToPath2(import.meta.url));
44945
44960
  const packageCandidates = [
44946
- join11(currentDirectory, "..", "package.json"),
44947
- join11(currentDirectory, "..", "..", "package.json")
44961
+ join12(currentDirectory, "..", "package.json"),
44962
+ join12(currentDirectory, "..", "..", "package.json")
44948
44963
  ];
44949
44964
  for (const packagePath of packageCandidates) {
44950
44965
  try {
44951
- const value = JSON.parse(readFileSync8(packagePath, "utf-8"));
44966
+ const value = JSON.parse(readFileSync9(packagePath, "utf-8"));
44952
44967
  if (typeof value === "object" && value !== null && "name" in value && value.name === "gdharness" && "version" in value && typeof value.version === "string") {
44953
44968
  return value.version;
44954
44969
  }