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.
@@ -734,12 +734,17 @@ func _reread_changed_uids(filesystem: EditorFileSystem) -> Dictionary:
734
734
  return {"reread": reread, "duplicated": duplicated}
735
735
 
736
736
 
737
- ## Every `.uid` under [param directory], skipping what the editor skips: hidden directories and any
738
- ## holding a `.gdignore`.
737
+ ## Every `.uid` under [param directory], skipping what the editor skips: hidden directories, any
738
+ ## holding a `.gdignore`, and any below the project's own that holds a `project.godot` of its own,
739
+ ## which the editor passes over as another project. A nested project's UIDs are never in the editor's
740
+ ## table, so without that last one its files would be read again on every rescan, and one holding a
741
+ ## copy of an outer file's `.uid` named as a duplicate on disk.
739
742
  func _uid_files(directory: String, found: Array[String]) -> void:
740
743
  var listing: DirAccess = DirAccess.open(directory)
741
744
  if listing == null or FileAccess.file_exists(directory.path_join(".gdignore")):
742
745
  return
746
+ if directory != "res://" and FileAccess.file_exists(directory.path_join("project.godot")):
747
+ return
743
748
  for child: String in listing.get_directories():
744
749
  if not child.begins_with("."):
745
750
  _uid_files(directory.path_join(child), found)
@@ -684,9 +684,11 @@ func set_property(params: Dictionary) -> Dictionary:
684
684
  ## A float arrives as 64 bits and most engine properties keep 32: Camera3D.h_offset given -1.1
685
685
  ## reads back as -1.10000002384186, so comparing the two exactly took a write of the value already
686
686
  ## held for one the engine refused (#780). Rounding both to 32 bits is exact for a 32-bit property.
687
- ## A 64-bit one can take a write this calls the same, and then reads back changed, which the caller
688
- ## checks before calling the write unchanged. Vectors and colours hold 32-bit parts in the value
689
- ## itself, so they already compare at the property's precision.
687
+ ## A 64-bit one can take a write this calls the same and read back changed, and the caller calls a
688
+ ## write unchanged only when it reads back as it was. What that cannot tell apart is a 64-bit
689
+ ## property whose setter ignored a value differing from the held one below 32 bits: it is answered
690
+ ## unchanged rather than refused. Vectors and colours hold 32-bit parts in the value itself, so they
691
+ ## already compare at the property's precision.
690
692
  static func _held_already(given: Variant, held: Variant) -> bool:
691
693
  if not Values.comparable(given, held):
692
694
  return false
@@ -145,7 +145,11 @@ func wait_until(params: Dictionary) -> Dictionary:
145
145
  # with null until the time ran out. #772 waited a minute for "null" that way and answered met
146
146
  # false with value null beside it.
147
147
  var declared: int = watched["declared"]
148
- if declared == TYPE_OBJECT and wanted is String and str(wanted).strip_edges() == "null":
148
+ # An untyped slot holding null or an object is read the same way, as set reads it: otherwise the
149
+ # two ops would disagree about one property, and the wait would run out on the text null.
150
+ var untyped_object: bool = declared == TYPE_NIL and (current == null or typeof(current) == TYPE_OBJECT)
151
+ var holds_objects: bool = declared == TYPE_OBJECT or untyped_object
152
+ if holds_objects and wanted is String and str(wanted).strip_edges() == "null":
149
153
  wanted = null
150
154
  var refused: String = _not_comparable(current, wanted, node_path, property)
151
155
  var never_held: bool = typeof(wanted) != TYPE_NIL and typeof(wanted) != TYPE_OBJECT
package/build/index.js CHANGED
@@ -26153,32 +26153,33 @@ function missingMemberIn(message) {
26153
26153
  if (called !== null) {
26154
26154
  return { kind: "static method", member: called[1] ?? "", type: called[2] ?? "" };
26155
26155
  }
26156
- const inBase = /Cannot find member "([A-Za-z_]\w*)" in base "([A-Za-z_]\w*)((?:\.[A-Za-z_]\w*)*)"/.exec(message);
26156
+ const inBase = /Cannot find member "([A-Za-z_]\w*)" in base "([A-Za-z_]\w*)(?:\.([A-Za-z_]\w*))?"/.exec(message);
26157
26157
  if (inBase === null) {
26158
26158
  return null;
26159
26159
  }
26160
26160
  const member = inBase[1] ?? "";
26161
26161
  const type = inBase[2] ?? "";
26162
- const within = (inBase[3] ?? "").split(".").filter((part) => part !== "");
26163
- const enumName = within.at(-1);
26162
+ const enumName = inBase[3];
26164
26163
  return enumName === undefined ? { kind: "member", member, type } : { kind: "enum member", member, type, enum: enumName };
26165
26164
  }
26166
26165
  function declaresMember(source, missing) {
26167
26166
  const name = missing.member.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
26167
+ const top = String.raw`^(?:@\w+(?:\([^)]*\))?\s+)*`;
26168
+ const declared = (kinds) => new RegExp(String.raw`${top}(?:${kinds})\s+${name}\b`, "m").test(source);
26168
26169
  if (missing.kind === "method") {
26169
- return new RegExp(String.raw`^\s*(?:static\s+)?func\s+${name}\s*\(`, "m").test(source);
26170
+ return new RegExp(String.raw`${top}(?:static\s+)?func\s+${name}\s*\(`, "m").test(source);
26170
26171
  }
26171
26172
  if (missing.kind === "static method") {
26172
- return new RegExp(String.raw`^\s*static\s+func\s+${name}\s*\(`, "m").test(source);
26173
+ return new RegExp(String.raw`${top}static\s+func\s+${name}\s*\(`, "m").test(source);
26173
26174
  }
26174
26175
  if (missing.kind === "enum member") {
26175
26176
  return enumValues(source, missing.enum ?? "").includes(missing.member);
26176
26177
  }
26177
- const kinds = missing.kind === "property" ? "var|const|enum|class|signal|func" : "var|const|enum|class|signal";
26178
- return new RegExp(String.raw`^\s*(?:static\s+)?(?:@\w+(?:\([^)]*\))?\s+)*(?:${kinds})\s+${name}\b`, "m").test(source) || enumValues(source, null).includes(missing.member);
26178
+ const kinds = missing.kind === "property" ? String.raw`(?:static\s+)?(?:var|func)|const|enum|class|signal` : String.raw`static\s+(?:var|func)|const|enum|class`;
26179
+ return declared(kinds) || enumValues(source, null).includes(missing.member);
26179
26180
  }
26180
26181
  function enumValues(source, named) {
26181
- const opening = named === null ? /^\s*enum\s*\{([^}]*)\}/gm : new RegExp(String.raw`^\s*enum\s+${named.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\s*\{([^}]*)\}`, "gm");
26182
+ const opening = named === null ? /^enum\s*\{([^}]*)\}/gm : new RegExp(String.raw`^enum\s+${named.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\s*\{([^}]*)\}`, "gm");
26182
26183
  const values = [];
26183
26184
  for (const [, body] of source.matchAll(opening)) {
26184
26185
  const uncommented = (body ?? "").split(`
@@ -26221,21 +26222,22 @@ function classesNamedIn(source, known) {
26221
26222
  return [...named].sort();
26222
26223
  }
26223
26224
  function contradictedDiagnostics(messages, classes, sourceOf) {
26225
+ const keyOf = (missing) => [missing.type, missing.enum ?? "", missing.member].join(".");
26226
+ const denials = messages.map((message) => missingMemberIn(message)).filter((missing) => missing !== null);
26227
+ const calledOnTheClass = new Set(denials.filter((missing) => missing.kind === "static method").map((missing) => keyOf(missing)));
26224
26228
  const read = new Map;
26225
26229
  const found = new Map;
26226
- for (const message of messages) {
26227
- const missing = missingMemberIn(message);
26228
- const declaredIn = missing === null ? undefined : classes.get(missing.type);
26229
- if (missing === null || declaredIn === undefined) {
26230
+ for (const missing of denials) {
26231
+ const declaredIn = classes.get(missing.type);
26232
+ const key = keyOf(missing);
26233
+ if (declaredIn === undefined || missing.kind === "method" && calledOnTheClass.has(key)) {
26230
26234
  continue;
26231
26235
  }
26232
26236
  if (!read.has(declaredIn)) {
26233
26237
  read.set(declaredIn, sourceOf(declaredIn));
26234
26238
  }
26235
26239
  const source = read.get(declaredIn) ?? null;
26236
- const key = [missing.type, missing.enum ?? "", missing.member].join(".");
26237
- const already = found.get(key);
26238
- if (source !== null && declaresMember(source, missing) && already?.kind !== "static method") {
26240
+ if (source !== null && declaresMember(source, missing)) {
26239
26241
  found.set(key, { ...missing, declaredIn });
26240
26242
  }
26241
26243
  }
@@ -26285,9 +26287,9 @@ function failedReloadNote(failed) {
26285
26287
  ];
26286
26288
  const unchanged = lost.length === 0 && missing(failed.heldAfter, failed.heldBefore).length === 0 && missing(failed.constantsAfter, failed.constantsBefore).length === 0;
26287
26289
  const answered = failed.codeName === "" ? `error ${failed.code}` : `error ${failed.code}, ${failed.codeName}`;
26288
- 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.`;
26290
+ 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.`;
26289
26291
  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.`;
26290
- return `${failed.script} did not compile, so nothing was reloaded: Godot answered ${answered}. ${copy} ` + `script_diagnostics on it gives the errors.${order}`;
26292
+ return `${failed.script} did not compile: Godot answered ${answered}. ${copy} ` + `script_diagnostics on it gives the errors.${order}`;
26291
26293
  }
26292
26294
  function uncachedClassNote(types) {
26293
26295
  if (types.length === 0) {
@@ -28147,14 +28149,34 @@ class GodotLocator {
28147
28149
 
28148
28150
  // src/headless.ts
28149
28151
  import { execFile as execFile3 } from "node:child_process";
28150
- import { writeFileSync as writeFileSync7 } from "node:fs";
28151
- import { join as join12 } from "node:path";
28152
+ import { writeFileSync as writeFileSync8 } from "node:fs";
28153
+ import { join as join13 } from "node:path";
28152
28154
  import { promisify as promisify3 } from "node:util";
28153
28155
 
28156
+ // src/reimport.ts
28157
+ import { createHash as createHash2 } from "node:crypto";
28158
+ import { existsSync as existsSync8, readFileSync as readFileSync11, rmSync as rmSync4, statSync as statSync5, utimesSync, writeFileSync as writeFileSync7 } from "node:fs";
28159
+ import { basename as basename2, join as join11 } from "node:path";
28160
+ function forceNextImport(projectPath, resourcePath) {
28161
+ const relative = resourcePath.replace(/^res:\/\//, "");
28162
+ const sidecar = join11(projectPath, `${relative}.import`);
28163
+ if (!existsSync8(sidecar)) {
28164
+ return;
28165
+ }
28166
+ const { atime, mtimeMs } = statSync5(sidecar);
28167
+ writeFileSync7(sidecar, readFileSync11(sidecar, "utf8").replace(/^valid=false\r?\n/m, ""));
28168
+ utimesSync(sidecar, atime, new Date(mtimeMs + 2000));
28169
+ rmSync4(importRecord(projectPath, resourcePath), { force: true });
28170
+ }
28171
+ function importRecord(projectPath, resourcePath) {
28172
+ const digest = createHash2("md5").update(resourcePath).digest("hex");
28173
+ return join11(projectPath, ".godot", "imported", `${basename2(resourcePath)}-${digest}.md5`);
28174
+ }
28175
+
28154
28176
  // src/scratch.ts
28155
- import { existsSync as existsSync8, mkdtempSync, readdirSync as readdirSync4, rmSync as rmSync4 } from "node:fs";
28177
+ import { existsSync as existsSync9, mkdtempSync, readdirSync as readdirSync4, rmSync as rmSync5 } from "node:fs";
28156
28178
  import { tmpdir as tmpdir3 } from "node:os";
28157
- import { join as join11 } from "node:path";
28179
+ import { join as join12 } from "node:path";
28158
28180
  import process7 from "node:process";
28159
28181
  var SWEPT = { recursive: true, force: true };
28160
28182
  var AGAIN_AFTER_MS = [250, 500, 1000, 2000, 4000, 8000, 16000];
@@ -28169,7 +28191,7 @@ var KINDS = [
28169
28191
  "tests"
28170
28192
  ];
28171
28193
  function scratchDirectory(kind) {
28172
- return mkdtempSync(join11(tmpdir3(), `gdharness-${kind}-${process7.pid}-`));
28194
+ return mkdtempSync(join12(tmpdir3(), `gdharness-${kind}-${process7.pid}-`));
28173
28195
  }
28174
28196
  function sweepAbandonedScratch(directory = tmpdir3()) {
28175
28197
  let entries;
@@ -28185,7 +28207,7 @@ function sweepAbandonedScratch(directory = tmpdir3()) {
28185
28207
  continue;
28186
28208
  }
28187
28209
  try {
28188
- remove(join11(directory, entry));
28210
+ remove(join12(directory, entry));
28189
28211
  } catch {}
28190
28212
  }
28191
28213
  }
@@ -28211,7 +28233,7 @@ function discard(...paths) {
28211
28233
  return left;
28212
28234
  }
28213
28235
  function remove(path) {
28214
- rmSync4(path, SWEPT);
28236
+ rmSync5(path, SWEPT);
28215
28237
  }
28216
28238
  function tryAgain(path, attempt) {
28217
28239
  if (attempt === 0) {
@@ -28223,7 +28245,7 @@ function tryAgain(path, attempt) {
28223
28245
  lastTryOnExit();
28224
28246
  }
28225
28247
  const after = AGAIN_AFTER_MS[attempt];
28226
- if (after === undefined || !existsSync8(path)) {
28248
+ if (after === undefined || !existsSync9(path)) {
28227
28249
  waiting.delete(path);
28228
28250
  if (after === undefined) {
28229
28251
  atExit.add(path);
@@ -28303,21 +28325,36 @@ function librariesNotCopied(messages) {
28303
28325
  }
28304
28326
  return [...found];
28305
28327
  }
28306
- function extensionRetryNote(libraries) {
28307
- 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.";
28328
+ function extensionRetryNote(libraries, stillNotLoaded = []) {
28329
+ 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.`;
28330
+ 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.";
28308
28331
  }
28309
- async function runImport(godotPath, projectPath, once = importOnce) {
28310
- const first = await once(godotPath, projectPath);
28332
+ async function runImport(godotPath, projectPath, options = {}) {
28333
+ const once = options.once ?? importOnce;
28334
+ const pass = () => {
28335
+ for (const target of options.forcing ?? []) {
28336
+ forceNextImport(projectPath, target);
28337
+ }
28338
+ return once(godotPath, projectPath);
28339
+ };
28340
+ const first = await pass();
28311
28341
  const collided = librariesNotCopied(first.messages);
28312
28342
  if (collided.length === 0) {
28313
28343
  return first;
28314
28344
  }
28315
- return { ...await once(godotPath, projectPath), librariesRetried: collided };
28345
+ const second = await pass();
28346
+ const still = librariesNotCopied(second.messages);
28347
+ return {
28348
+ ...second,
28349
+ librariesRetried: collided,
28350
+ ...still.length === 0 ? {} : { librariesNotLoaded: still },
28351
+ extensionNote: extensionRetryNote(collided, still)
28352
+ };
28316
28353
  }
28317
28354
  async function importOnce(godotPath, projectPath) {
28318
28355
  const logDir = scratchDirectory("import");
28319
28356
  try {
28320
- const { stderr } = await run3(godotPath, ["--headless", "--log-file", join12(logDir, "engine.log"), "--path", projectPath, "--import"], { signal: callSignal() });
28357
+ const { stderr } = await run3(godotPath, ["--headless", "--log-file", join13(logDir, "engine.log"), "--path", projectPath, "--import"], { signal: callSignal() });
28321
28358
  return { ok: true, messages: problems(stderr) };
28322
28359
  } catch (error) {
28323
28360
  if (error instanceof Error && "stdout" in error && "stderr" in error) {
@@ -28339,12 +28376,12 @@ async function importOnce(godotPath, projectPath) {
28339
28376
  }
28340
28377
  async function runOperation(engine, operation, params, projectPath) {
28341
28378
  const paramsDir = scratchDirectory("params");
28342
- const paramsFile = join12(paramsDir, `${operation}.json`);
28343
- writeFileSync7(paramsFile, JSON.stringify(snakeCased(params)), "utf8");
28379
+ const paramsFile = join13(paramsDir, `${operation}.json`);
28380
+ writeFileSync8(paramsFile, JSON.stringify(snakeCased(params)), "utf8");
28344
28381
  const args = [
28345
28382
  "--headless",
28346
28383
  "--log-file",
28347
- join12(paramsDir, "engine.log"),
28384
+ join13(paramsDir, "engine.log"),
28348
28385
  "--path",
28349
28386
  projectPath,
28350
28387
  "--script",
@@ -29400,14 +29437,14 @@ function secondsFromClock(printed) {
29400
29437
  }
29401
29438
 
29402
29439
  // src/project-scan.ts
29403
- import { existsSync as existsSync9, readdirSync as readdirSync5, readFileSync as readFileSync11 } from "node:fs";
29404
- import { join as join13 } from "node:path";
29440
+ import { existsSync as existsSync10, readdirSync as readdirSync5, readFileSync as readFileSync12 } from "node:fs";
29441
+ import { join as join14 } from "node:path";
29405
29442
  var SKIPPED = new Set(["node_modules"]);
29406
29443
  function skipped(name) {
29407
29444
  return name.startsWith(".") || SKIPPED.has(name);
29408
29445
  }
29409
29446
  function steppedOver(directory) {
29410
- return existsSync9(join13(directory, ".gdignore"));
29447
+ return existsSync10(join14(directory, ".gdignore"));
29411
29448
  }
29412
29449
  var ASSET_EXTENSIONS = new Set(["png", "jpg", "jpeg", "webp", "svg", "ttf", "otf", "wav", "mp3", "ogg"]);
29413
29450
  function projectStructure(projectPath) {
@@ -29421,7 +29458,7 @@ function projectStructure(projectPath) {
29421
29458
  continue;
29422
29459
  }
29423
29460
  if (entry.isDirectory()) {
29424
- visit(join13(directory, entry.name));
29461
+ visit(join14(directory, entry.name));
29425
29462
  } else if (entry.isFile()) {
29426
29463
  const extension = entry.name.split(".").pop()?.toLowerCase() ?? "";
29427
29464
  if (extension === "tscn") {
@@ -29452,14 +29489,14 @@ function scriptsWithoutUid(projectPath) {
29452
29489
  }
29453
29490
  const spelled = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
29454
29491
  if (entry.isDirectory()) {
29455
- visit(join13(directory, entry.name), spelled);
29492
+ visit(join14(directory, entry.name), spelled);
29456
29493
  continue;
29457
29494
  }
29458
29495
  if (!entry.isFile()) {
29459
29496
  continue;
29460
29497
  }
29461
29498
  const extension = entry.name.split(".").pop()?.toLowerCase() ?? "";
29462
- if (CARRIES_A_UID.has(extension) && !existsSync9(join13(directory, `${entry.name}.uid`))) {
29499
+ if (CARRIES_A_UID.has(extension) && !existsSync10(join14(directory, `${entry.name}.uid`))) {
29463
29500
  found.push(spelled);
29464
29501
  }
29465
29502
  }
@@ -29494,7 +29531,7 @@ function searchProject(projectPath, options) {
29494
29531
  if (skipped(entry.name)) {
29495
29532
  continue;
29496
29533
  }
29497
- const entryPath = join13(directory, entry.name);
29534
+ const entryPath = join14(directory, entry.name);
29498
29535
  if (entry.isDirectory()) {
29499
29536
  visit(entryPath);
29500
29537
  continue;
@@ -29505,7 +29542,7 @@ function searchProject(projectPath, options) {
29505
29542
  }
29506
29543
  result.summary.files_searched += 1;
29507
29544
  const matches = [];
29508
- for (const [index, line] of readFileSync11(entryPath, "utf8").split(`
29545
+ for (const [index, line] of readFileSync12(entryPath, "utf8").split(`
29509
29546
  `).entries()) {
29510
29547
  if (full()) {
29511
29548
  break;
@@ -29527,26 +29564,6 @@ function searchProject(projectPath, options) {
29527
29564
  return result;
29528
29565
  }
29529
29566
 
29530
- // src/reimport.ts
29531
- import { createHash as createHash2 } from "node:crypto";
29532
- import { existsSync as existsSync10, readFileSync as readFileSync12, rmSync as rmSync5, statSync as statSync5, utimesSync, writeFileSync as writeFileSync8 } from "node:fs";
29533
- import { basename as basename2, join as join14 } from "node:path";
29534
- function forceNextImport(projectPath, resourcePath) {
29535
- const relative = resourcePath.replace(/^res:\/\//, "");
29536
- const sidecar = join14(projectPath, `${relative}.import`);
29537
- if (!existsSync10(sidecar)) {
29538
- return;
29539
- }
29540
- const { atime, mtimeMs } = statSync5(sidecar);
29541
- writeFileSync8(sidecar, readFileSync12(sidecar, "utf8").replace(/^valid=false\r?\n/m, ""));
29542
- utimesSync(sidecar, atime, new Date(mtimeMs + 2000));
29543
- rmSync5(importRecord(projectPath, resourcePath), { force: true });
29544
- }
29545
- function importRecord(projectPath, resourcePath) {
29546
- const digest = createHash2("md5").update(resourcePath).digest("hex");
29547
- return join14(projectPath, ".godot", "imported", `${basename2(resourcePath)}-${digest}.md5`);
29548
- }
29549
-
29550
29567
  // src/resources.ts
29551
29568
  import { readFileSync as readFileSync13 } from "node:fs";
29552
29569
  import { extname, resolve as resolve4 } from "node:path";
@@ -31412,7 +31429,7 @@ var TOOL_SPECS = [
31412
31429
  },
31413
31430
  value: {
31414
31431
  ops: ["until"],
31415
- 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."
31432
+ 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."
31416
31433
  },
31417
31434
  says: {
31418
31435
  type: "string",
@@ -33111,7 +33128,7 @@ class GodotServer {
33111
33128
  uidsCreated: given,
33112
33129
  stillWithoutUid: after,
33113
33130
  note: uidsLeftNote(after.length),
33114
- ...imported.librariesRetried === undefined ? {} : { extensionNote: extensionRetryNote(imported.librariesRetried) }
33131
+ ...imported.extensionNote === undefined ? {} : { extensionNote: imported.extensionNote }
33115
33132
  }
33116
33133
  }, scanned));
33117
33134
  }
@@ -33256,19 +33273,16 @@ class GodotServer {
33256
33273
  if (!engine.ok) {
33257
33274
  return { ok: false, response: engine.response };
33258
33275
  }
33259
- for (const target of targets) {
33260
- forceNextImport(projectPath, target);
33261
- }
33262
33276
  const scanned = await this.untilTheEditorsScanIsWritten(projectPath);
33263
- const imported = await runImport(engine.value, projectPath);
33277
+ const imported = await runImport(engine.value, projectPath, { forcing: targets });
33264
33278
  if (!imported.ok) {
33265
33279
  return { ok: false, response: this.answer(withScanWait(imported, scanned)) };
33266
33280
  }
33267
33281
  if (imported.messages.length > 0) {
33268
33282
  extra["engine_messages"] = imported.messages;
33269
33283
  }
33270
- if (imported.librariesRetried !== undefined) {
33271
- extra["extensionNote"] = extensionRetryNote(imported.librariesRetried);
33284
+ if (imported.extensionNote !== undefined) {
33285
+ extra["extensionNote"] = imported.extensionNote;
33272
33286
  }
33273
33287
  }
33274
33288
  const after = await this.operation("get_import_status", resourcePath === undefined ? { includeUpToDate: true } : { resourcePath }, projectPath);
@@ -33759,7 +33773,7 @@ class GodotServer {
33759
33773
  const notes = [];
33760
33774
  if (contradicted.length > 0) {
33761
33775
  const dependsOn = new Set;
33762
- for (const one of contradicted) {
33776
+ for (const one of contradicted.filter((entry) => entry.kind === "method" || entry.kind === "property")) {
33763
33777
  const source = cached === null ? null : this.sourceOfClass(projectPath, one.declaredIn);
33764
33778
  if (source === null || cached === null) {
33765
33779
  continue;
@@ -33799,9 +33813,10 @@ class GodotServer {
33799
33813
  });
33800
33814
  }
33801
33815
  classesAScriptNames(projectPath, scriptPath) {
33802
- const source = this.sourceOfClass(projectPath, scriptPath);
33816
+ const resourcePath = scriptPath.startsWith("res://") ? scriptPath : `res://${scriptPath.replace(/^\/+/, "")}`;
33817
+ const source = this.sourceOfClass(projectPath, resourcePath);
33803
33818
  const known = cachedClasses(projectPath) ?? declaredClasses(projectPath);
33804
- return source === null ? [] : classesNamedIn(source, known).filter((name) => known.get(name) !== scriptPath);
33819
+ return source === null ? [] : classesNamedIn(source, known).filter((name) => known.get(name) !== resourcePath);
33805
33820
  }
33806
33821
  sourceOfClass(projectPath, resourcePath) {
33807
33822
  const contained = resolveWithinProject(projectPath, resourcePath);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gdharness",
3
- "version": "1.1.20",
3
+ "version": "1.1.21",
4
4
  "mcpName": "io.github.Aureliolo/gdharness",
5
5
  "description": "A harness for driving a Godot 4 project from an agent: editor addons, a runtime bridge into the running game, an MCP server in front of them, and a CLI that installs and diagnoses the Godot side.",
6
6
  "type": "module",