@neocompose/cli 0.21.6 → 0.22.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/neo.mjs CHANGED
@@ -256,6 +256,358 @@ var init_ui = __esm({
256
256
  }
257
257
  });
258
258
 
259
+ // src/commands/login.ts
260
+ function isObjectRecord(value) {
261
+ return typeof value === "object" && value !== null;
262
+ }
263
+ async function readStdin() {
264
+ const chunks = [];
265
+ for await (const chunk of process.stdin) {
266
+ chunks.push(Buffer.from(chunk));
267
+ }
268
+ return Buffer.concat(chunks).toString("utf8").trim();
269
+ }
270
+ async function runLogin(options) {
271
+ let profile = options.profile;
272
+ if (profile === null) {
273
+ profile = isInteractive() && !options.tokenStdin ? await promptSelect({
274
+ message: "Credential profile",
275
+ choices: [
276
+ {
277
+ name: "editor",
278
+ value: "editor",
279
+ description: "Day-to-day schema, content, and branch work (default)"
280
+ },
281
+ {
282
+ name: "release",
283
+ value: "release",
284
+ description: "Adds outward-facing ops: publish, archive, channels"
285
+ }
286
+ ],
287
+ nonInteractiveHint: "Pass --profile editor|release."
288
+ }) : "editor";
289
+ }
290
+ const scopes = [...profile === "release" ? RELEASE_SCOPES : EDITOR_SCOPES];
291
+ if (options.saveProjectId !== null) {
292
+ scopes.push(`project:${options.saveProjectId}:save:read`);
293
+ }
294
+ if (options.tokenStdin) {
295
+ const token = await readStdin();
296
+ if (token.length === 0) {
297
+ throw new Error("--token-stdin received an empty token.");
298
+ }
299
+ saveCredential({
300
+ token,
301
+ profile,
302
+ apiBaseUrl: options.apiBaseUrl,
303
+ scopes,
304
+ savedAt: Date.now()
305
+ });
306
+ console.log(`Stored token for ${options.apiBaseUrl} (${profile}).`);
307
+ return;
308
+ }
309
+ const clientId = CLIENT_ID_BY_PROFILE[profile];
310
+ const codeResponse = await fetch(
311
+ new URL("/api/auth/device/code", options.apiBaseUrl),
312
+ {
313
+ method: "POST",
314
+ headers: { "Content-Type": "application/json" },
315
+ body: JSON.stringify({ client_id: clientId, scope: scopes.join(" ") })
316
+ }
317
+ );
318
+ const codeBody = await codeResponse.json();
319
+ if (!codeResponse.ok) {
320
+ throw new Error(
321
+ `Device authorization request failed (${codeResponse.status}): ${JSON.stringify(codeBody)}`
322
+ );
323
+ }
324
+ if (!isObjectRecord(codeBody)) {
325
+ throw new Error("Device authorization response must be a JSON object.");
326
+ }
327
+ const deviceCode = codeBody.device_code;
328
+ const userCode = codeBody.user_code;
329
+ const verificationUriComplete = codeBody.verification_uri_complete;
330
+ const interval = typeof codeBody.interval === "number" ? codeBody.interval : 5;
331
+ if (typeof deviceCode !== "string") {
332
+ throw new Error('Device authorization response is missing "device_code".');
333
+ }
334
+ if (typeof userCode !== "string") {
335
+ throw new Error('Device authorization response is missing "user_code".');
336
+ }
337
+ console.log("");
338
+ console.log(` To authorize the Neo Compose CLI, open:`);
339
+ console.log(` ${color.cyan(String(verificationUriComplete))}`);
340
+ console.log("");
341
+ console.log(` and confirm this code: ${color.bold(userCode)}`);
342
+ console.log("");
343
+ if (isInteractive() && process.platform === "darwin") {
344
+ note(" (opening your browser\u2026)");
345
+ const { spawn } = await import("node:child_process");
346
+ spawn("open", [String(verificationUriComplete)], {
347
+ stdio: "ignore",
348
+ detached: true
349
+ }).unref();
350
+ }
351
+ const waiting = spinner("Waiting for approval in the browser\u2026");
352
+ const deadline = Date.now() + 15 * 60 * 1e3;
353
+ for (; ; ) {
354
+ if (Date.now() > deadline) {
355
+ waiting.fail("Device authorization timed out after 15 minutes.");
356
+ throw new Error("Device authorization timed out after 15 minutes.");
357
+ }
358
+ await sleep(interval * 1e3);
359
+ const tokenResponse = await fetch(
360
+ new URL("/api/auth/device/token", options.apiBaseUrl),
361
+ {
362
+ method: "POST",
363
+ headers: { "Content-Type": "application/json" },
364
+ body: JSON.stringify({
365
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code",
366
+ device_code: deviceCode,
367
+ client_id: clientId
368
+ })
369
+ }
370
+ );
371
+ const tokenBody = await tokenResponse.json();
372
+ if (!isObjectRecord(tokenBody)) {
373
+ throw new Error("Device token response must be a JSON object.");
374
+ }
375
+ if (!tokenResponse.ok) {
376
+ const errorCode = tokenBody.error;
377
+ if (errorCode === "authorization_pending" || errorCode === "slow_down") {
378
+ continue;
379
+ }
380
+ waiting.fail("Device authorization failed.");
381
+ throw new Error(
382
+ `Device token request failed (${tokenResponse.status}): ${JSON.stringify(tokenBody)}`
383
+ );
384
+ }
385
+ const accessToken = tokenBody.access_token;
386
+ if (typeof accessToken !== "string") {
387
+ throw new Error('Device token response is missing "access_token".');
388
+ }
389
+ saveCredential({
390
+ token: accessToken,
391
+ profile,
392
+ apiBaseUrl: options.apiBaseUrl,
393
+ scopes,
394
+ savedAt: Date.now()
395
+ });
396
+ waiting.succeed(`Logged in to ${options.apiBaseUrl} (${profile} profile).`);
397
+ return;
398
+ }
399
+ }
400
+ async function runWhoami(apiBaseUrl) {
401
+ const { loadToken: loadToken2 } = await Promise.resolve().then(() => (init_token_store(), token_store_exports));
402
+ const token = loadToken2(apiBaseUrl);
403
+ if (token === null) {
404
+ throw new Error(
405
+ `No credentials stored for "${apiBaseUrl}". Run "neo login".`
406
+ );
407
+ }
408
+ const response = await fetch(new URL("/api/auth/get-session", apiBaseUrl), {
409
+ headers: { Authorization: `Bearer ${token}` }
410
+ });
411
+ const body = await response.json();
412
+ if (!response.ok) {
413
+ throw new Error(
414
+ `get-session failed (${response.status}): ${JSON.stringify(body)}`
415
+ );
416
+ }
417
+ if (!isObjectRecord(body) || !isObjectRecord(body.user)) {
418
+ throw new Error("Not signed in (session lookup returned no user).");
419
+ }
420
+ const email = typeof body.user.email === "string" ? body.user.email : "(no email)";
421
+ const name = typeof body.user.name === "string" ? body.user.name : "(no name)";
422
+ console.log(`Signed in as ${name} <${email}> at ${apiBaseUrl}`);
423
+ }
424
+ var CLIENT_ID_BY_PROFILE, EDITOR_SCOPES, RELEASE_SCOPES, sleep;
425
+ var init_login = __esm({
426
+ "src/commands/login.ts"() {
427
+ "use strict";
428
+ init_token_store();
429
+ init_ui();
430
+ CLIENT_ID_BY_PROFILE = {
431
+ editor: "neo-cli-editor",
432
+ release: "neo-cli-release"
433
+ };
434
+ EDITOR_SCOPES = [
435
+ "openid",
436
+ "profile:read",
437
+ "project:list",
438
+ "project:read",
439
+ "project:details:read",
440
+ "project:version:read",
441
+ "project:version:create",
442
+ "project:version:status:read",
443
+ "project:version:changelog:read",
444
+ "project:record:schema:read",
445
+ "project:record:schema:write",
446
+ "project:record:values:read",
447
+ "project:record:values:write",
448
+ "project:record:world:read",
449
+ "project:record:world:write",
450
+ "project:dialogue:read",
451
+ "project:dialogue:write",
452
+ "project:dialogue:logic:read",
453
+ "project:dialogue:logic:compile",
454
+ "project:files:read",
455
+ "project:files:content:read",
456
+ "project:files:write",
457
+ "project:localization:config:read",
458
+ "project:localization:config:write",
459
+ "project:localization:status:read",
460
+ "project:localization:status:write",
461
+ "project:localization:main-values:read",
462
+ "project:localization:main-values:write",
463
+ "project:localization:values:read",
464
+ "project:localization:values:write",
465
+ "project:localization:export",
466
+ "project:localization:import",
467
+ "project:release-channel:read",
468
+ // `neo export unity` writes project.json + NeoGeneratedTypes.cs headlessly
469
+ // (the escape hatch when game code references not-yet-generated members and
470
+ // a broken compile blocks the in-editor sync).
471
+ "unity:export",
472
+ // Branch lifecycle (auto-archive after `neo merge`, branch archive/restore)
473
+ // is editor work; releases stay gated behind the release profile.
474
+ "project:version:archive",
475
+ "project:version:restore"
476
+ ];
477
+ RELEASE_SCOPES = [
478
+ ...EDITOR_SCOPES,
479
+ "project:version:status:write",
480
+ "project:release-channel:write",
481
+ "project:release-channel:publish"
482
+ ];
483
+ sleep = (ms) => new Promise((resolve4) => setTimeout(resolve4, ms));
484
+ }
485
+ });
486
+
487
+ // src/args.ts
488
+ function parseArgs(argv) {
489
+ const [command = null, ...rest] = argv;
490
+ const flags = /* @__PURE__ */ new Map();
491
+ const positional = [];
492
+ for (let index = 0; index < rest.length; index += 1) {
493
+ const arg = rest[index];
494
+ if (arg.startsWith("--")) {
495
+ const name = arg.slice(2);
496
+ const next = rest[index + 1];
497
+ if (!BOOLEAN_FLAGS.has(name) && next !== void 0 && !next.startsWith("--")) {
498
+ flags.set(name, next);
499
+ index += 1;
500
+ } else {
501
+ flags.set(name, true);
502
+ }
503
+ } else {
504
+ positional.push(arg);
505
+ }
506
+ }
507
+ return { command, flags, positional };
508
+ }
509
+ function stringFlag(args, name) {
510
+ const value = args.flags.get(name);
511
+ if (value === void 0) return null;
512
+ if (typeof value !== "string") {
513
+ throw new Error(`--${name} requires a value.`);
514
+ }
515
+ return value;
516
+ }
517
+ function boolFlag(args, name) {
518
+ return args.flags.get(name) === true;
519
+ }
520
+ function assertKnownFlags(args) {
521
+ for (const name of args.flags.keys()) {
522
+ if (KNOWN_FLAGS.has(name)) continue;
523
+ const hint = args.command === null ? "Run `neo help` for usage." : `Run \`neo ${args.command} --help\` for usage.`;
524
+ throw new Error(`Unknown flag "--${name}". ${hint}`);
525
+ }
526
+ }
527
+ var BOOLEAN_FLAGS, KNOWN_FLAGS;
528
+ var init_args = __esm({
529
+ "src/args.ts"() {
530
+ "use strict";
531
+ BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
532
+ "dry-run",
533
+ "json",
534
+ "all",
535
+ "force",
536
+ "force-recompile",
537
+ "token-stdin",
538
+ "push",
539
+ "mine",
540
+ "theirs",
541
+ "accept-bump",
542
+ "commit",
543
+ "skip-invalid",
544
+ "migrate",
545
+ "server",
546
+ "replace",
547
+ "reset",
548
+ "regenerate-source-names",
549
+ "generate-ids",
550
+ "help",
551
+ "abstract"
552
+ ]);
553
+ KNOWN_FLAGS = /* @__PURE__ */ new Set([
554
+ "accept-bump",
555
+ "abstract",
556
+ "all",
557
+ "api",
558
+ "args",
559
+ "member",
560
+ "bump",
561
+ "bind",
562
+ "commit",
563
+ "dir",
564
+ "dry-run",
565
+ "entries",
566
+ "file",
567
+ "force",
568
+ "force-recompile",
569
+ "function",
570
+ "from",
571
+ "generate-ids",
572
+ "group",
573
+ "help",
574
+ "into",
575
+ "json",
576
+ "key",
577
+ "kind",
578
+ "migrate",
579
+ "mine",
580
+ "mode",
581
+ "out",
582
+ "profile",
583
+ "primary",
584
+ "project",
585
+ "plan",
586
+ "push",
587
+ "replace",
588
+ "regenerate-source-names",
589
+ "reset",
590
+ "returns",
591
+ "run",
592
+ "save",
593
+ "save-project",
594
+ "server",
595
+ "skip-invalid",
596
+ "status",
597
+ "summary",
598
+ "confirm-scope",
599
+ "target",
600
+ "template",
601
+ "theirs",
602
+ "this",
603
+ "this-value",
604
+ "token-stdin",
605
+ "class",
606
+ "version"
607
+ ]);
608
+ }
609
+ });
610
+
259
611
  // ../packages/neoscript-language/src/language-spec.ts
260
612
  function neoProjectSourceKind(path) {
261
613
  if (path.endsWith(".neoflow")) return "flow";
@@ -11608,11 +11960,18 @@ ${document.text};` : functionUnit ? isolateUnitBody(
11608
11960
  ) : document.text;
11609
11961
  const resolverContext = expressionDocument ? { ...context, kind: "initializer" } : context;
11610
11962
  const ast = parseFunctionBody(compilableSource);
11611
- const ir = new StrictNeoScriptResolver(
11963
+ const projectIndex = options.projectIndex ?? createProjectIndex(resolverContext.project);
11964
+ const emitted = new StrictNeoScriptResolver(
11612
11965
  resolverContext,
11613
- options.projectIndex,
11966
+ projectIndex,
11614
11967
  compilableSource
11615
11968
  ).compile(ast);
11969
+ const ir = attachDependencyManifest(
11970
+ emitted,
11971
+ compilableSource,
11972
+ projectIndex,
11973
+ resolverContext
11974
+ );
11616
11975
  return {
11617
11976
  parsed: recovery.parsed,
11618
11977
  ast,
@@ -11688,14 +12047,61 @@ function compileUnit(source, unit, context, label, projectIndex) {
11688
12047
  const isolated = isolateUnitBody(source, unit.bodyStart, unit.bodyEnd);
11689
12048
  try {
11690
12049
  const ast = parseFunctionBody(isolated);
11691
- return new StrictNeoScriptResolver(context, projectIndex, isolated).compile(
11692
- ast
12050
+ const indexedProject = projectIndex ?? createProjectIndex(context.project);
12051
+ return attachDependencyManifest(
12052
+ new StrictNeoScriptResolver(context, indexedProject, isolated).compile(
12053
+ ast
12054
+ ),
12055
+ isolated,
12056
+ indexedProject,
12057
+ context
11693
12058
  );
11694
12059
  } catch (error) {
11695
12060
  if (error instanceof CompileError) throw error.withUnit(label);
11696
12061
  throw error;
11697
12062
  }
11698
12063
  }
12064
+ function attachDependencyManifest(compiled, source, project, context) {
12065
+ const recordIds = /* @__PURE__ */ new Set();
12066
+ collectDependencyIds(compiled, null, recordIds);
12067
+ const identifiers = new Set(
12068
+ lex2(source).filter((token) => token.kind === "ident").map((token) => token.text)
12069
+ );
12070
+ collectDependencyIds(context.thisClass, null, recordIds);
12071
+ collectDependencyIds(context.declaringType, null, recordIds);
12072
+ collectDependencyIds(context.returnType, null, recordIds);
12073
+ collectDependencyIds(context.parameters, null, recordIds);
12074
+ collectDependencyIds(context.dialogueContextType, null, recordIds);
12075
+ for (const alias of context.valueAliases ?? []) {
12076
+ if (!identifiers.has(alias.name)) continue;
12077
+ recordIds.add(alias.valueId);
12078
+ collectDependencyIds(alias.type, null, recordIds);
12079
+ }
12080
+ const typeNames = [...project.typeByName.keys()].filter(
12081
+ (name) => identifiers.has(name)
12082
+ );
12083
+ return {
12084
+ ...compiled,
12085
+ dependencies: {
12086
+ recordIds: [...recordIds].sort(),
12087
+ typeNames: typeNames.sort()
12088
+ }
12089
+ };
12090
+ }
12091
+ function collectDependencyIds(value, field, ids) {
12092
+ if (typeof value === "string") {
12093
+ if (field !== null && DEPENDENCY_ID_FIELDS.has(field)) ids.add(value);
12094
+ return;
12095
+ }
12096
+ if (Array.isArray(value)) {
12097
+ for (const entry of value) collectDependencyIds(entry, field, ids);
12098
+ return;
12099
+ }
12100
+ if (value === null || typeof value !== "object") return;
12101
+ for (const [childField, child] of Object.entries(value)) {
12102
+ collectDependencyIds(child, childField, ids);
12103
+ }
12104
+ }
11699
12105
  function isolateUnitBody(source, start, end) {
11700
12106
  let result = "";
11701
12107
  for (let index = 0; index < source.length; index++) {
@@ -11718,14 +12124,32 @@ function compileErrorDiagnostic(source, error) {
11718
12124
  message: error.message.replace(/^\d+:\d+:\s*/, "")
11719
12125
  };
11720
12126
  }
12127
+ var DEPENDENCY_ID_FIELDS;
11721
12128
  var init_compiler = __esm({
11722
12129
  "../packages/neoscript-language/src/compiler.ts"() {
11723
12130
  "use strict";
12131
+ init_project();
11724
12132
  init_source_text();
11725
12133
  init_syntax();
11726
12134
  init_strict_compile_error();
11727
12135
  init_strict_parser();
11728
12136
  init_strict_resolver();
12137
+ init_strict_lexer();
12138
+ DEPENDENCY_ID_FIELDS = /* @__PURE__ */ new Set([
12139
+ "classId",
12140
+ "collectionMemberId",
12141
+ "collectionValueId",
12142
+ "constructorId",
12143
+ "enumId",
12144
+ "fileId",
12145
+ "interfaceId",
12146
+ "listMemberId",
12147
+ "memberId",
12148
+ "ownerClassId",
12149
+ "primaryLinkedValueId",
12150
+ "typeId",
12151
+ "valueId"
12152
+ ]);
11729
12153
  }
11730
12154
  });
11731
12155
 
@@ -21911,7 +22335,7 @@ function isEnumOptionId(id2) {
21911
22335
  }
21912
22336
  return RFC_4122_UUID.test(id2);
21913
22337
  }
21914
- function analyzeNeoProjectSources(inputs) {
22338
+ function analyzeNeoProjectSources(inputs, parsedDocuments = /* @__PURE__ */ new Map()) {
21915
22339
  const documents = /* @__PURE__ */ new Map();
21916
22340
  const diagnostics = [];
21917
22341
  for (const input of inputs) {
@@ -21926,7 +22350,8 @@ function analyzeNeoProjectSources(inputs) {
21926
22350
  });
21927
22351
  continue;
21928
22352
  }
21929
- const document = parseNeoProjectSource(input.text, input.kind, input.uri);
22353
+ const cached = parsedDocuments.get(input.uri);
22354
+ const document = cached?.kind === input.kind && cached.sourceText === input.text ? cached : parseNeoProjectSource(input.text, input.kind, input.uri);
21930
22355
  documents.set(input.uri, document);
21931
22356
  diagnostics.push(
21932
22357
  ...document.diagnostics.map((diagnostic) => ({
@@ -24510,8 +24935,9 @@ var init_project_source_construction_quick_fixes = __esm({
24510
24935
  });
24511
24936
 
24512
24937
  // ../packages/neoscript-language/src/project-source-manifest.ts
24513
- function compileNeoProjectSources(inputs) {
24514
- const analysis = analyzeNeoProjectSources(inputs);
24938
+ function compileNeoProjectSources(inputs, options = {}) {
24939
+ const analysis = analyzeNeoProjectSources(inputs, options.parsedDocuments);
24940
+ options.onDocuments?.(analysis.documents);
24515
24941
  const schemaClasses = [];
24516
24942
  const schemaInterfaces = [];
24517
24943
  const schemaEnums = [];
@@ -47675,6 +48101,46 @@ function normalizeInitializerSource(source) {
47675
48101
  ...lines.slice(1).map((line) => line.length === 0 ? line : line.slice(common))
47676
48102
  ].join("\n");
47677
48103
  }
48104
+ function constructorExpressionSlice(source) {
48105
+ let quote6 = null;
48106
+ let escaped = false;
48107
+ let parentheses = 0;
48108
+ let brackets = 0;
48109
+ let braces = 0;
48110
+ let index = 0;
48111
+ while (index < source.length) {
48112
+ const character = source[index];
48113
+ if (quote6 !== null) {
48114
+ if (escaped) escaped = false;
48115
+ else if (character === "\\") escaped = true;
48116
+ else if (character === quote6) quote6 = null;
48117
+ index += 1;
48118
+ continue;
48119
+ }
48120
+ const afterComment = commentEndIndex(source, index);
48121
+ if (afterComment !== null) {
48122
+ index = afterComment;
48123
+ continue;
48124
+ }
48125
+ if (character === '"' || character === "'") {
48126
+ quote6 = character;
48127
+ index += 1;
48128
+ continue;
48129
+ }
48130
+ if (character === "(") parentheses += 1;
48131
+ else if (character === ")") parentheses -= 1;
48132
+ else if (character === "[") brackets += 1;
48133
+ else if (character === "]") brackets -= 1;
48134
+ else if (character === "{") {
48135
+ if (parentheses === 0 && brackets === 0 && braces === 0) {
48136
+ return normalizeInitializerSource(source.slice(0, index).trimEnd());
48137
+ }
48138
+ braces += 1;
48139
+ } else if (character === "}") braces -= 1;
48140
+ index += 1;
48141
+ }
48142
+ return normalizeInitializerSource(source);
48143
+ }
47678
48144
  function topLevelEntrySlices(initializer, open, close) {
47679
48145
  const start = topLevelIndexOf(initializer, open);
47680
48146
  if (start < 0) return [];
@@ -50307,8 +50773,8 @@ var init_analyzer_types = __esm({
50307
50773
  });
50308
50774
 
50309
50775
  // ../src/components/projects/member-code/neoscript-language-context-adapter.ts
50310
- function createNeoScriptDocumentContext(context) {
50311
- const project = createNeoScriptProject(context);
50776
+ function createNeoScriptDocumentContext(context, projectOverride) {
50777
+ const project = projectOverride ?? createNeoScriptProject(context);
50312
50778
  const documentKind = context.scriptKind ?? "getter";
50313
50779
  const thisClass = context.thisClass ? namedType(context.thisClass.id, true) : void 0;
50314
50780
  const returnType = context.functionReturnTypeInfo ? functionReturnType(context.functionReturnTypeInfo, context) : context.returnTypeInfo ? toLanguageType(context.returnTypeInfo, context) : void 0;
@@ -50931,19 +51397,14 @@ function createListIndexes(context) {
50931
51397
  continue;
50932
51398
  }
50933
51399
  if (!isMemberListBase(resolved)) continue;
50934
- let definitions;
50935
- try {
50936
- definitions = resolveListIndexDefinitions(
50937
- {
50938
- members: context.vm.members,
50939
- classes: context.vm.classes,
50940
- enums: context.vm.enums
50941
- },
50942
- record3
50943
- );
50944
- } catch {
50945
- continue;
50946
- }
51400
+ const definitions = resolveListIndexDefinitions(
51401
+ {
51402
+ members: context.vm.members,
51403
+ classes: context.vm.classes,
51404
+ enums: context.vm.enums
51405
+ },
51406
+ record3
51407
+ );
50947
51408
  const listType = memberRuntimeType(record3, context);
50948
51409
  if (listType.kind !== "list") continue;
50949
51410
  for (const definition2 of definitions) {
@@ -51378,6 +51839,7 @@ function projectLanguageVersion(context) {
51378
51839
  ...context.constructors ?? []
51379
51840
  ];
51380
51841
  return [
51842
+ `adapter:${NEOSCRIPT_COMPILER_ADAPTER_REVISION}`,
51381
51843
  context.vm.project.id,
51382
51844
  context.vm.project.updatedAt,
51383
51845
  context.vm.project.rootAssetsMemberId,
@@ -51525,7 +51987,7 @@ function virtualEnumOptionLine(optionName, optionId = optionName) {
51525
51987
  function virtualRootMemberLine(name, memberId) {
51526
51988
  return ` public object ${virtualCSharpIdentifier(name)} { get; } // Neo root member ${memberId}`;
51527
51989
  }
51528
- var UNKNOWN_TYPE2, storageResolverByContext;
51990
+ var NEOSCRIPT_COMPILER_ADAPTER_REVISION, UNKNOWN_TYPE2, storageResolverByContext;
51529
51991
  var init_neoscript_language_context_adapter = __esm({
51530
51992
  "../src/components/projects/member-code/neoscript-language-context-adapter.ts"() {
51531
51993
  "use strict";
@@ -51541,6 +52003,7 @@ var init_neoscript_language_context_adapter = __esm({
51541
52003
  init_project_root_members();
51542
52004
  init_project_file_registry();
51543
52005
  init_analyzer_types();
52006
+ NEOSCRIPT_COMPILER_ADAPTER_REVISION = 1;
51544
52007
  UNKNOWN_TYPE2 = {
51545
52008
  kind: "primitive",
51546
52009
  name: "unknown"
@@ -51550,6 +52013,28 @@ var init_neoscript_language_context_adapter = __esm({
51550
52013
  });
51551
52014
 
51552
52015
  // ../src/database/neoscript/compile.ts
52016
+ function clearNeoScriptBodyCompileCache() {
52017
+ neoScriptBodyCompileCache.clear();
52018
+ }
52019
+ function createNeoScriptCompilationProject(ctx) {
52020
+ const members = [...ctx.members];
52021
+ const membersById = new Map(members.map((member) => [member.id, member]));
52022
+ return createNeoScriptProject({
52023
+ vm: {
52024
+ project: ctx.project,
52025
+ projectFiles: [...ctx.projectFiles ?? []],
52026
+ members,
52027
+ classes: [...ctx.classes],
52028
+ enums: [...ctx.enums],
52029
+ interfaces: [...ctx.interfaces ?? []],
52030
+ databaseVM: {
52031
+ memberById: (id2) => membersById.get(id2) ?? null
52032
+ }
52033
+ },
52034
+ thisClass: null,
52035
+ ...ctx.constructors ? { constructors: ctx.constructors } : {}
52036
+ });
52037
+ }
51553
52038
  function compileNSGetter(code, ctx) {
51554
52039
  return compileStrict(
51555
52040
  code,
@@ -51646,6 +52131,28 @@ function withInitializerSourcePosition(error) {
51646
52131
  );
51647
52132
  }
51648
52133
  function compileStrict(code, context) {
52134
+ const cacheKey = JSON.stringify({
52135
+ code,
52136
+ projectIdentity: compilationProjectIdentity(context.project),
52137
+ kind: context.kind,
52138
+ thisClass: context.thisClass,
52139
+ returnType: context.returnType,
52140
+ parameters: context.parameters,
52141
+ dialogueContextType: context.dialogueContextType,
52142
+ deferred: context.deferred,
52143
+ functionName: context.functionName,
52144
+ migrationContext: context.migrationContext,
52145
+ valueAliases: context.valueAliases,
52146
+ implicitMemberAccess: context.implicitMemberAccess,
52147
+ declaringType: context.declaringType,
52148
+ staticMember: context.staticMember
52149
+ });
52150
+ const cached = neoScriptBodyCompileCache.get(cacheKey);
52151
+ if (cached !== void 0) {
52152
+ neoScriptBodyCompileCache.delete(cacheKey);
52153
+ neoScriptBodyCompileCache.set(cacheKey, cached);
52154
+ return structuredClone(cached);
52155
+ }
51649
52156
  const compiled = assertNeoScriptCompiles(
51650
52157
  {
51651
52158
  uri: `neo-compiler:///${context.kind}.neo`,
@@ -51660,10 +52167,24 @@ function compileStrict(code, context) {
51660
52167
  "A standalone NeoScript compiler entry point returned property units."
51661
52168
  );
51662
52169
  }
52170
+ neoScriptBodyCompileCache.set(cacheKey, structuredClone(compiled));
52171
+ while (neoScriptBodyCompileCache.size > NEOSCRIPT_BODY_COMPILE_CACHE_LIMIT) {
52172
+ const oldest = neoScriptBodyCompileCache.keys().next().value;
52173
+ if (typeof oldest !== "string") break;
52174
+ neoScriptBodyCompileCache.delete(oldest);
52175
+ }
51663
52176
  return compiled;
51664
52177
  }
52178
+ function compilationProjectIdentity(project) {
52179
+ const cached = neoScriptProjectIdentityCache.get(project);
52180
+ if (cached !== void 0) return cached;
52181
+ const identity2 = nextNeoScriptProjectIdentity++;
52182
+ neoScriptProjectIdentityCache.set(project, identity2);
52183
+ return identity2;
52184
+ }
51665
52185
  function createContext(ctx, options) {
51666
52186
  const members = [...ctx.members];
52187
+ const membersById = new Map(members.map((member) => [member.id, member]));
51667
52188
  const analyzer = {
51668
52189
  vm: {
51669
52190
  project: ctx.project,
@@ -51673,7 +52194,7 @@ function createContext(ctx, options) {
51673
52194
  enums: [...ctx.enums],
51674
52195
  interfaces: [...ctx.interfaces ?? []],
51675
52196
  databaseVM: {
51676
- memberById: (id2) => members.find((member) => member.id === id2) ?? null
52197
+ memberById: (id2) => membersById.get(id2) ?? null
51677
52198
  }
51678
52199
  },
51679
52200
  thisClass: ctx.thisClass,
@@ -51681,7 +52202,10 @@ function createContext(ctx, options) {
51681
52202
  ...ctx.constructors ? { constructors: ctx.constructors } : {},
51682
52203
  ...options
51683
52204
  };
51684
- const adapted = createNeoScriptDocumentContext(analyzer);
52205
+ const adapted = createNeoScriptDocumentContext(
52206
+ analyzer,
52207
+ ctx.compilationProject
52208
+ );
51685
52209
  if (ctx.implicitMemberAccess !== true) return adapted;
51686
52210
  return {
51687
52211
  ...adapted,
@@ -51691,11 +52215,16 @@ function createContext(ctx, options) {
51691
52215
  staticMember: ctx.staticMember === true
51692
52216
  };
51693
52217
  }
52218
+ var NEOSCRIPT_BODY_COMPILE_CACHE_LIMIT, neoScriptBodyCompileCache, neoScriptProjectIdentityCache, nextNeoScriptProjectIdentity;
51694
52219
  var init_compile = __esm({
51695
52220
  "../src/database/neoscript/compile.ts"() {
51696
52221
  "use strict";
51697
52222
  init_src();
51698
52223
  init_neoscript_language_context_adapter();
52224
+ NEOSCRIPT_BODY_COMPILE_CACHE_LIMIT = 1024;
52225
+ neoScriptBodyCompileCache = /* @__PURE__ */ new Map();
52226
+ neoScriptProjectIdentityCache = /* @__PURE__ */ new WeakMap();
52227
+ nextNeoScriptProjectIdentity = 1;
51699
52228
  }
51700
52229
  });
51701
52230
 
@@ -51711,18 +52240,22 @@ var init_compile_error = __esm({
51711
52240
  var compiler_adapter_exports = {};
51712
52241
  __export(compiler_adapter_exports, {
51713
52242
  CompileError: () => CompileError,
52243
+ NEOSCRIPT_COMPILER_ADAPTER_REVISION: () => NEOSCRIPT_COMPILER_ADAPTER_REVISION,
52244
+ clearNeoScriptBodyCompileCache: () => clearNeoScriptBodyCompileCache,
51714
52245
  compileNSAction: () => compileNSAction,
51715
52246
  compileNSConstructor: () => compileNSConstructor,
51716
52247
  compileNSFunction: () => compileNSFunction,
51717
52248
  compileNSGetter: () => compileNSGetter,
51718
52249
  compileNSInitializer: () => compileNSInitializer,
51719
- compileNSSetter: () => compileNSSetter
52250
+ compileNSSetter: () => compileNSSetter,
52251
+ createNeoScriptCompilationProject: () => createNeoScriptCompilationProject
51720
52252
  });
51721
52253
  var init_compiler_adapter = __esm({
51722
52254
  "../src/database/neoscript/compiler-adapter.ts"() {
51723
52255
  "use strict";
51724
52256
  init_compile();
51725
52257
  init_compile_error();
52258
+ init_neoscript_language_context_adapter();
51726
52259
  }
51727
52260
  });
51728
52261
 
@@ -51753,6 +52286,7 @@ function compileNSPropertyBodies(args) {
51753
52286
  enums: [...args.enums],
51754
52287
  interfaces: [...args.interfaces ?? []],
51755
52288
  constructors: args.constructors ?? [],
52289
+ ...args.compilationProject === void 0 ? {} : { compilationProject: args.compilationProject },
51756
52290
  thisClass: args.thisClass,
51757
52291
  returnTypeInfo,
51758
52292
  implicitMemberAccess: true,
@@ -51789,6 +52323,7 @@ function compileNSPropertyBodies(args) {
51789
52323
  enums: [...args.enums],
51790
52324
  interfaces: [...args.interfaces ?? []],
51791
52325
  constructors: args.constructors ?? [],
52326
+ ...args.compilationProject === void 0 ? {} : { compilationProject: args.compilationProject },
51792
52327
  thisClass: args.thisClass,
51793
52328
  valueTypeInfo: returnTypeInfo,
51794
52329
  implicitMemberAccess: true,
@@ -51837,6 +52372,7 @@ function compileNSFunctionBody(args) {
51837
52372
  enums: [...args.enums],
51838
52373
  interfaces: [...args.interfaces ?? []],
51839
52374
  constructors: args.constructors ?? [],
52375
+ ...args.compilationProject === void 0 ? {} : { compilationProject: args.compilationProject },
51840
52376
  thisClass: args.thisClass,
51841
52377
  functionName: args.member.name,
51842
52378
  implicitMemberAccess: true,
@@ -51877,6 +52413,7 @@ function compileNSFunctionBody(args) {
51877
52413
  enums: [...args.enums],
51878
52414
  interfaces: [...args.interfaces ?? []],
51879
52415
  constructors: args.constructors ?? [],
52416
+ ...args.compilationProject === void 0 ? {} : { compilationProject: args.compilationProject },
51880
52417
  thisClass: args.thisClass,
51881
52418
  functionName: args.member.name,
51882
52419
  implicitMemberAccess: true,
@@ -51913,6 +52450,7 @@ function compileMemberInitializerBody(args) {
51913
52450
  enums: [...args.enums],
51914
52451
  interfaces: [...args.interfaces ?? []],
51915
52452
  constructors: args.constructors ?? [],
52453
+ ...args.compilationProject === void 0 ? {} : { compilationProject: args.compilationProject },
51916
52454
  returnTypeInfo,
51917
52455
  initializerName: args.member.name,
51918
52456
  argumentTypes: initializerArgumentTypes(
@@ -51965,7 +52503,8 @@ function compileValueRowInitializerBody(args) {
51965
52503
  argumentTypes: initializerArgumentTypes(
51966
52504
  args.initializerOwnerClass,
51967
52505
  args.constructors ?? []
51968
- )
52506
+ ),
52507
+ ...args.compilationProject === void 0 ? {} : { compilationProject: args.compilationProject }
51969
52508
  })
51970
52509
  );
51971
52510
  } catch (error) {
@@ -52076,6 +52615,7 @@ function compileConstructorRecord(args) {
52076
52615
  enums: [...args.enums],
52077
52616
  interfaces: [...args.interfaces ?? []],
52078
52617
  constructors: args.constructors,
52618
+ ...args.compilationProject === void 0 ? {} : { compilationProject: args.compilationProject },
52079
52619
  thisClass: owner,
52080
52620
  argumentTypes,
52081
52621
  constructorName: owner.name
@@ -52114,6 +52654,7 @@ function compileConstructorBaseArguments(args, owner) {
52114
52654
  enums: [...args.enums],
52115
52655
  interfaces: [...args.interfaces ?? []],
52116
52656
  constructors: args.constructors,
52657
+ ...args.compilationProject === void 0 ? {} : { compilationProject: args.compilationProject },
52117
52658
  returnTypeInfo: parameter3,
52118
52659
  argumentTypes,
52119
52660
  initializerName: `${owner.name} base argument ${baseArgument.name}`
@@ -52173,6 +52714,7 @@ function compileConstructorBaseInitializerFields(args, owner) {
52173
52714
  enums: [...args.enums],
52174
52715
  interfaces: [...args.interfaces ?? []],
52175
52716
  constructors: args.constructors,
52717
+ ...args.compilationProject === void 0 ? {} : { compilationProject: args.compilationProject },
52176
52718
  returnTypeInfo,
52177
52719
  argumentTypes: args.constructor.argumentTypes,
52178
52720
  initializerName: `${owner.name} base initializer ${field.name}`
@@ -66064,7 +66606,7 @@ var init_dialogue_lower = __esm({
66064
66606
  });
66065
66607
 
66066
66608
  // src/project-source/root-source.ts
66067
- function emitProjectRootSourceV4(records2, manifest) {
66609
+ function emitProjectRootSourceV4(records2, manifest, materializedConstructors) {
66068
66610
  const project = [...records2.values()].find(
66069
66611
  (record3) => !record3.deleted && record3.recordKind === "project"
66070
66612
  );
@@ -66079,6 +66621,11 @@ function emitProjectRootSourceV4(records2, manifest) {
66079
66621
  manifest,
66080
66622
  targetTypedRoot: true
66081
66623
  });
66624
+ if (materializedConstructors !== void 0) {
66625
+ for (const [valueId, expression] of values.materializedConstructors) {
66626
+ materializedConstructors.set(valueId, expression);
66627
+ }
66628
+ }
66082
66629
  const declarations = PROJECT_ROOT_SOURCE_SLOTS.map((slot, index) => {
66083
66630
  const memberId = memberIds[index];
66084
66631
  const member = members.get(memberId);
@@ -67609,20 +68156,985 @@ var init_animation_clips = __esm({
67609
68156
  }
67610
68157
  });
67611
68158
 
68159
+ // src/project-source/materialized-construction-cache.ts
68160
+ import { mkdirSync as mkdirSync3, readFileSync as readFileSync3, renameSync, writeFileSync as writeFileSync3 } from "node:fs";
68161
+ import { createHash as createHash2 } from "node:crypto";
68162
+ import { dirname as dirname2, join as join3 } from "node:path";
68163
+ function readMaterializedConstructionBuildCacheV1(root, state) {
68164
+ try {
68165
+ const parsed = JSON.parse(
68166
+ readFileSync3(
68167
+ join3(root, MATERIALIZED_CONSTRUCTION_BUILD_CACHE_PATH),
68168
+ "utf8"
68169
+ )
68170
+ );
68171
+ if (parsed === null || typeof parsed !== "object") return null;
68172
+ const cache = parsed;
68173
+ if (cache.revision !== MATERIALIZED_CONSTRUCTION_BUILD_CACHE_REVISION || cache.stateFingerprint !== materializedConstructionStateFingerprint(state) || cache.expressions === null || typeof cache.expressions !== "object" || Array.isArray(cache.expressions)) {
68174
+ return null;
68175
+ }
68176
+ const expressions = /* @__PURE__ */ new Map();
68177
+ for (const [valueId, expression] of Object.entries(cache.expressions)) {
68178
+ if (typeof expression !== "string") return null;
68179
+ expressions.set(valueId, expression);
68180
+ }
68181
+ return expressions;
68182
+ } catch {
68183
+ return null;
68184
+ }
68185
+ }
68186
+ function writeMaterializedConstructionBuildCacheV1(root, state, expressions) {
68187
+ const file = join3(root, MATERIALIZED_CONSTRUCTION_BUILD_CACHE_PATH);
68188
+ mkdirSync3(dirname2(file), { recursive: true });
68189
+ const temporary = `${file}.${process.pid}.tmp`;
68190
+ writeFileSync3(
68191
+ temporary,
68192
+ `${JSON.stringify({
68193
+ revision: MATERIALIZED_CONSTRUCTION_BUILD_CACHE_REVISION,
68194
+ stateFingerprint: materializedConstructionStateFingerprint(state),
68195
+ expressions: Object.fromEntries(
68196
+ [...expressions].sort(([left], [right]) => left.localeCompare(right))
68197
+ )
68198
+ })}
68199
+ `,
68200
+ "utf8"
68201
+ );
68202
+ renameSync(temporary, file);
68203
+ }
68204
+ function materializedConstructionStateFingerprint(state) {
68205
+ const hash = createHash2("sha256");
68206
+ hash.update(String(MATERIALIZED_CONSTRUCTION_BUILD_CACHE_REVISION));
68207
+ for (const [key, record3] of Object.entries(state.records).sort(
68208
+ ([left], [right]) => left.localeCompare(right)
68209
+ )) {
68210
+ hash.update("\0");
68211
+ hash.update(key);
68212
+ hash.update("\0");
68213
+ hash.update(record3.contentHash);
68214
+ }
68215
+ return hash.digest("hex");
68216
+ }
68217
+ var MATERIALIZED_CONSTRUCTION_BUILD_CACHE_PATH, MATERIALIZED_CONSTRUCTION_BUILD_CACHE_REVISION;
68218
+ var init_materialized_construction_cache = __esm({
68219
+ "src/project-source/materialized-construction-cache.ts"() {
68220
+ "use strict";
68221
+ MATERIALIZED_CONSTRUCTION_BUILD_CACHE_PATH = ".neo/build/materialized-constructions-v1.json";
68222
+ MATERIALIZED_CONSTRUCTION_BUILD_CACHE_REVISION = 1;
68223
+ }
68224
+ });
68225
+
68226
+ // ../src/database/canonical-json.ts
68227
+ function canonicalJsonStringify(value) {
68228
+ return JSON.stringify(toCanonicalJsonValue(value));
68229
+ }
68230
+ function toCanonicalJsonValue(value) {
68231
+ if (value === null) return null;
68232
+ if (value === void 0) return void 0;
68233
+ if (value instanceof Date) return value.toISOString();
68234
+ if (typeof value === "string") return value;
68235
+ if (typeof value === "boolean") return value;
68236
+ if (typeof value === "number") {
68237
+ if (!Number.isFinite(value)) {
68238
+ throw new Error("Cannot canonicalize a non-finite number.");
68239
+ }
68240
+ return value;
68241
+ }
68242
+ if (typeof value === "bigint") {
68243
+ throw new Error("Cannot canonicalize bigint values.");
68244
+ }
68245
+ if (typeof value === "symbol") {
68246
+ throw new Error("Cannot canonicalize symbol values.");
68247
+ }
68248
+ if (typeof value === "function") {
68249
+ throw new Error("Cannot canonicalize function values.");
68250
+ }
68251
+ if (Array.isArray(value)) {
68252
+ return value.map((item) => {
68253
+ const canonicalItem = toCanonicalJsonValue(item);
68254
+ if (canonicalItem === void 0) return null;
68255
+ return canonicalItem;
68256
+ });
68257
+ }
68258
+ const entries = Object.entries(value).sort(([a], [b]) => a.localeCompare(b));
68259
+ const canonicalObject = {};
68260
+ for (const [key, entryValue] of entries) {
68261
+ const canonicalValue = toCanonicalJsonValue(entryValue);
68262
+ if (canonicalValue === void 0) continue;
68263
+ canonicalObject[key] = canonicalValue;
68264
+ }
68265
+ return canonicalObject;
68266
+ }
68267
+ var init_canonical_json = __esm({
68268
+ "../src/database/canonical-json.ts"() {
68269
+ "use strict";
68270
+ }
68271
+ });
68272
+
68273
+ // ../src/database/neo-script-recompile-scope.ts
68274
+ function memberNeoScriptContractChanged(current, next) {
68275
+ return canonicalJsonStringify(neoScriptMemberContractProjection(current)) !== canonicalJsonStringify(neoScriptMemberContractProjection(next));
68276
+ }
68277
+ function neoScriptMemberContractProjection(value) {
68278
+ const ignored = new Set(NON_CONTRACT_MEMBER_FIELDS);
68279
+ if (value.kind === 10 /* NSProperty */) {
68280
+ for (const field of NS_PROPERTY_BODY_FIELDS) ignored.add(field);
68281
+ }
68282
+ if (value.kind === 23 /* NSFunction */) {
68283
+ for (const field of NS_FUNCTION_BODY_FIELDS) ignored.add(field);
68284
+ }
68285
+ return {
68286
+ ...withoutFields(value, ignored),
68287
+ defaultPresent: value.defaultValue != null
68288
+ };
68289
+ }
68290
+ function collectNeoScriptRecompileTargets(args) {
68291
+ if (args.forceRecompile === true) return completeTargets(args.postDocument);
68292
+ const explicit = explicitTargetIds(args.changes);
68293
+ const { impactedIds, impactedTypeNames, constructedClassIds } = collectChangedContractIds(args);
68294
+ includeDerivedConstructionContracts(
68295
+ constructedClassIds,
68296
+ args.postDocument.classes
68297
+ );
68298
+ const schemaRecords = [
68299
+ ...collectSchemaDependencyRecords(args.postDocument),
68300
+ ...collectStructuralDependencyRecords(args.postDocument)
68301
+ ];
68302
+ const dependentsById = /* @__PURE__ */ new Map();
68303
+ for (const record3 of schemaRecords) {
68304
+ for (const dependencyId of collectCompilerReferenceIds(record3.value)) {
68305
+ const dependents = dependentsById.get(dependencyId) ?? [];
68306
+ dependents.push(record3);
68307
+ dependentsById.set(dependencyId, dependents);
68308
+ }
68309
+ }
68310
+ const queue = [...impactedIds];
68311
+ for (let index = 0; index < queue.length; index += 1) {
68312
+ const dependencyId = queue[index];
68313
+ if (dependencyId === void 0) continue;
68314
+ for (const dependent of dependentsById.get(dependencyId) ?? []) {
68315
+ addImpactedId(impactedIds, queue, dependent.id);
68316
+ }
68317
+ }
68318
+ const constructorOwnerById = constructorOwners(args.postDocument);
68319
+ const memberIds = new Set(explicit.memberIds);
68320
+ for (const member of args.postDocument.members) {
68321
+ if (recordDependsOnChangedContract(
68322
+ member,
68323
+ impactedIds,
68324
+ impactedTypeNames,
68325
+ constructedClassIds
68326
+ )) {
68327
+ memberIds.add(member.id);
68328
+ }
68329
+ }
68330
+ const postValues = args.postDocument.values ?? [];
68331
+ const valueIds = selectedRecordIds(
68332
+ postValues,
68333
+ explicit.valueIds,
68334
+ impactedIds,
68335
+ impactedTypeNames,
68336
+ constructedClassIds
68337
+ );
68338
+ if (impactedIds.size > 0 && postValues.length > 0) {
68339
+ const ownerByValueId = resolveOwnerMembersForValues(
68340
+ args.postDocument,
68341
+ new Set(postValues.map((record3) => record3.id))
68342
+ );
68343
+ for (const [valueId, owner] of ownerByValueId) {
68344
+ const ownerId = Reflect.get(owner, "id");
68345
+ if (typeof ownerId === "string" && impactedIds.has(ownerId)) {
68346
+ valueIds.add(valueId);
68347
+ }
68348
+ }
68349
+ }
68350
+ return {
68351
+ complete: false,
68352
+ memberIds,
68353
+ constructorIds: selectedRecordIds(
68354
+ args.postDocument.constructors ?? [],
68355
+ explicit.constructorIds,
68356
+ impactedIds,
68357
+ impactedTypeNames,
68358
+ constructedClassIds,
68359
+ (record3) => constructorOwnerById.get(record3.id) ?? null
68360
+ ),
68361
+ migrationIds: selectedRecordIds(
68362
+ args.postDocument.migrations ?? [],
68363
+ explicit.migrationIds,
68364
+ impactedIds,
68365
+ impactedTypeNames,
68366
+ constructedClassIds
68367
+ ),
68368
+ dialogueNodeIds: selectedRecordIds(
68369
+ args.postDocument.dialogueNodes ?? [],
68370
+ explicit.dialogueNodeIds,
68371
+ impactedIds,
68372
+ impactedTypeNames,
68373
+ constructedClassIds
68374
+ ),
68375
+ dialogueGroupIds: selectedRecordIds(
68376
+ args.postDocument.dialogueGroups ?? [],
68377
+ explicit.dialogueGroupIds,
68378
+ impactedIds,
68379
+ impactedTypeNames,
68380
+ constructedClassIds
68381
+ ),
68382
+ valueIds
68383
+ };
68384
+ }
68385
+ function collectCompilerReferenceIds(value) {
68386
+ const ids = /* @__PURE__ */ new Set();
68387
+ collectCompilerReferenceIdsInto(value, null, ids);
68388
+ return ids;
68389
+ }
68390
+ function collectCompilerReferenceNames(value) {
68391
+ const names = /* @__PURE__ */ new Set();
68392
+ collectCompilerReferenceNamesInto(value, null, names);
68393
+ return names;
68394
+ }
68395
+ function collectConstructedClassIds(value) {
68396
+ const ids = /* @__PURE__ */ new Set();
68397
+ collectConstructedClassIdsInto(value, ids);
68398
+ return ids;
68399
+ }
68400
+ function collectConstructedClassIdsInto(value, ids) {
68401
+ if (Array.isArray(value)) {
68402
+ for (const entry of value) collectConstructedClassIdsInto(entry, ids);
68403
+ return;
68404
+ }
68405
+ if (value === null || typeof value !== "object") return;
68406
+ const record3 = value;
68407
+ if ((record3.type === "classConstructor" || record3.type === "declaredConstructor") && record3.info !== null && typeof record3.info === "object") {
68408
+ const schemaClassInfo = Reflect.get(record3.info, "schemaClassInfo");
68409
+ if (schemaClassInfo !== null && typeof schemaClassInfo === "object") {
68410
+ const classId = Reflect.get(schemaClassInfo, "classId");
68411
+ if (typeof classId === "string") ids.add(classId);
68412
+ }
68413
+ }
68414
+ for (const child of Object.values(record3)) {
68415
+ collectConstructedClassIdsInto(child, ids);
68416
+ }
68417
+ }
68418
+ function collectCompilerReferenceIdsInto(value, field, ids) {
68419
+ if (typeof value === "string") {
68420
+ if (field !== null && isCompilerReferenceField(field)) ids.add(value);
68421
+ return;
68422
+ }
68423
+ if (Array.isArray(value)) {
68424
+ for (const entry of value)
68425
+ collectCompilerReferenceIdsInto(entry, field, ids);
68426
+ return;
68427
+ }
68428
+ if (value === null || typeof value !== "object") return;
68429
+ for (const [childField, child] of Object.entries(value)) {
68430
+ collectCompilerReferenceIdsInto(child, childField, ids);
68431
+ }
68432
+ }
68433
+ function collectCompilerReferenceNamesInto(value, field, names) {
68434
+ if (typeof value === "string") {
68435
+ if (field === "declaredTypeName" || field === "typeName" || field === "typeNames") {
68436
+ names.add(value);
68437
+ }
68438
+ return;
68439
+ }
68440
+ if (Array.isArray(value)) {
68441
+ for (const entry of value) {
68442
+ collectCompilerReferenceNamesInto(entry, field, names);
68443
+ }
68444
+ return;
68445
+ }
68446
+ if (value === null || typeof value !== "object") return;
68447
+ for (const [childField, child] of Object.entries(value)) {
68448
+ collectCompilerReferenceNamesInto(child, childField, names);
68449
+ }
68450
+ }
68451
+ function isCompilerReferenceField(field) {
68452
+ return /(?:member|class|enum|interface|constructor)(?:Type)?Ids?$/i.test(field) || field === "baseTypeIds" || field === "collectionValueId" || field === "fileId" || field === "primaryLinkedValueId" || field === "recordIds" || field === "valueId";
68453
+ }
68454
+ function collectChangedContractIds(args) {
68455
+ const impactedIds = /* @__PURE__ */ new Set();
68456
+ const impactedTypeNames = /* @__PURE__ */ new Set();
68457
+ const constructedClassIds = /* @__PURE__ */ new Set();
68458
+ const currentMembers = new Map(
68459
+ args.currentDocument.members.map((member) => [member.id, member])
68460
+ );
68461
+ const postMembers = new Map(
68462
+ args.postDocument.members.map((member) => [member.id, member])
68463
+ );
68464
+ const currentConstructors = new Map(
68465
+ (args.currentDocument.constructors ?? []).map((record3) => [
68466
+ record3.id,
68467
+ record3
68468
+ ])
68469
+ );
68470
+ const postConstructors = new Map(
68471
+ (args.postDocument.constructors ?? []).map((record3) => [record3.id, record3])
68472
+ );
68473
+ const currentValues = new Map(
68474
+ (args.currentDocument.values ?? []).map((record3) => [record3.id, record3])
68475
+ );
68476
+ const postValues = new Map(
68477
+ (args.postDocument.values ?? []).map((record3) => [record3.id, record3])
68478
+ );
68479
+ const currentSchemaRecords = schemaRecordsByKind(args.currentDocument);
68480
+ const postSchemaRecords = schemaRecordsByKind(args.postDocument);
68481
+ for (const change of args.changes) {
68482
+ if (change.recordKind === "class" || change.recordKind === "enum" || change.recordKind === "interface") {
68483
+ const kind = change.recordKind;
68484
+ const current2 = currentSchemaRecords[kind].get(change.recordId);
68485
+ const next2 = postSchemaRecords[kind].get(change.recordId);
68486
+ if (current2 !== void 0 && next2 !== void 0 && !schemaNeoScriptContractChanged(kind, current2, next2)) {
68487
+ continue;
68488
+ }
68489
+ impactedIds.add(change.recordId);
68490
+ const currentName = current2?.name;
68491
+ const nextName = next2?.name;
68492
+ if (typeof currentName === "string" && currentName !== nextName) {
68493
+ impactedTypeNames.add(currentName);
68494
+ }
68495
+ if (typeof nextName === "string" && nextName !== currentName) {
68496
+ impactedTypeNames.add(nextName);
68497
+ }
68498
+ continue;
68499
+ }
68500
+ if (change.recordKind === "constructor") {
68501
+ const current2 = currentConstructors.get(change.recordId);
68502
+ const next2 = postConstructors.get(change.recordId);
68503
+ if (current2 !== void 0 && next2 !== void 0 && !constructorNeoScriptContractChanged(
68504
+ current2,
68505
+ next2
68506
+ )) {
68507
+ continue;
68508
+ }
68509
+ impactedIds.add(change.recordId);
68510
+ continue;
68511
+ }
68512
+ if (change.recordKind === "value") {
68513
+ const current2 = currentValues.get(change.recordId);
68514
+ const next2 = postValues.get(change.recordId);
68515
+ if (current2 === void 0 || next2 === void 0 || Reflect.get(current2, "classId") !== Reflect.get(next2, "classId")) {
68516
+ impactedIds.add(change.recordId);
68517
+ }
68518
+ continue;
68519
+ }
68520
+ if (change.recordKind !== "member") continue;
68521
+ const current = currentMembers.get(change.recordId);
68522
+ const next = postMembers.get(change.recordId);
68523
+ if (current !== void 0 && next !== void 0 && !memberNeoScriptContractChanged(
68524
+ current,
68525
+ next
68526
+ )) {
68527
+ continue;
68528
+ }
68529
+ impactedIds.add(change.recordId);
68530
+ const currentRecord = current;
68531
+ const nextRecord = next;
68532
+ const canChangeUnqualifiedResolution = currentRecord === void 0 || nextRecord === void 0 || currentRecord.name !== nextRecord.name || currentRecord.kind !== nextRecord.kind || currentRecord.isStatic !== nextRecord.isStatic;
68533
+ if (canChangeUnqualifiedResolution) {
68534
+ for (const ownerId of classPlacementsForMember(
68535
+ change.recordId,
68536
+ args.currentDocument
68537
+ )) {
68538
+ impactedIds.add(ownerId);
68539
+ }
68540
+ for (const ownerId of classPlacementsForMember(
68541
+ change.recordId,
68542
+ args.postDocument
68543
+ )) {
68544
+ impactedIds.add(ownerId);
68545
+ }
68546
+ }
68547
+ if (generatedConstructorMemberContractChanged(currentRecord, nextRecord)) {
68548
+ for (const ownerId of classPlacementsForMember(
68549
+ change.recordId,
68550
+ args.currentDocument
68551
+ )) {
68552
+ constructedClassIds.add(ownerId);
68553
+ }
68554
+ for (const ownerId of classPlacementsForMember(
68555
+ change.recordId,
68556
+ args.postDocument
68557
+ )) {
68558
+ constructedClassIds.add(ownerId);
68559
+ }
68560
+ }
68561
+ }
68562
+ return { impactedIds, impactedTypeNames, constructedClassIds };
68563
+ }
68564
+ function generatedConstructorMemberContractChanged(current, next) {
68565
+ if (current === void 0 || next === void 0) return true;
68566
+ if (current.isStatic === true && next.isStatic === true) return false;
68567
+ const ignored = /* @__PURE__ */ new Set([
68568
+ ...NON_CONTRACT_MEMBER_FIELDS,
68569
+ ...NS_PROPERTY_BODY_FIELDS,
68570
+ ...NS_FUNCTION_BODY_FIELDS,
68571
+ "accessModifierKind",
68572
+ "indexes",
68573
+ "locked",
68574
+ "setter",
68575
+ "setterCode",
68576
+ "storage"
68577
+ ]);
68578
+ const project = (value) => ({
68579
+ ...withoutFields(value, ignored),
68580
+ defaultPresent: value.defaultValue != null
68581
+ });
68582
+ return canonicalJsonStringify(project(current)) !== canonicalJsonStringify(project(next));
68583
+ }
68584
+ function schemaNeoScriptContractChanged(kind, current, next) {
68585
+ return canonicalJsonStringify(neoScriptSchemaContractProjection(kind, current)) !== canonicalJsonStringify(neoScriptSchemaContractProjection(kind, next));
68586
+ }
68587
+ function neoScriptSchemaContractProjection(kind, value) {
68588
+ const ignored = /* @__PURE__ */ new Set([
68589
+ "createdAt",
68590
+ "updatedAt",
68591
+ "projectId",
68592
+ "docsText",
68593
+ "system"
68594
+ ]);
68595
+ if (kind === "class") ignored.add("hiddenInMemberSelector");
68596
+ if (kind === "enum") ignored.add("optionKeyOrder");
68597
+ if (kind === "interface") ignored.add("memberKeyOrder");
68598
+ const projected = withoutFields(value, ignored);
68599
+ if (kind === "enum" && isObjectRecord3(projected.options)) {
68600
+ projected.options = Object.fromEntries(
68601
+ Object.entries(projected.options).map(([id2, option]) => [
68602
+ id2,
68603
+ isObjectRecord3(option) ? { id: option.id, name: option.name } : option
68604
+ ])
68605
+ );
68606
+ }
68607
+ if (kind === "interface" && isObjectRecord3(projected.members)) {
68608
+ projected.members = Object.fromEntries(
68609
+ Object.entries(projected.members).map(([key, member]) => [
68610
+ key,
68611
+ isObjectRecord3(member) ? withoutFields(member, /* @__PURE__ */ new Set(["docsText"])) : member
68612
+ ])
68613
+ );
68614
+ }
68615
+ return projected;
68616
+ }
68617
+ function schemaRecordsByKind(document) {
68618
+ const map = (records2) => new Map(
68619
+ records2.map((record3) => [
68620
+ record3.id,
68621
+ record3
68622
+ ])
68623
+ );
68624
+ return {
68625
+ class: map(document.classes),
68626
+ member: map(document.members),
68627
+ constructor: map(document.constructors ?? []),
68628
+ enum: map(document.enums),
68629
+ interface: map(document.interfaces ?? [])
68630
+ };
68631
+ }
68632
+ function constructorNeoScriptContractChanged(current, next) {
68633
+ return canonicalJsonStringify(neoScriptConstructorContractProjection(current)) !== canonicalJsonStringify(neoScriptConstructorContractProjection(next));
68634
+ }
68635
+ function neoScriptConstructorContractProjection(value) {
68636
+ const ignored = /* @__PURE__ */ new Set([
68637
+ "code",
68638
+ "action",
68639
+ "compiledBaseArguments",
68640
+ "compiledBaseInitializerFields",
68641
+ "createdAt",
68642
+ "updatedAt",
68643
+ "projectId",
68644
+ "docsText"
68645
+ ]);
68646
+ return withoutFields(value, ignored);
68647
+ }
68648
+ function collectSchemaDependencyRecords(document) {
68649
+ return [
68650
+ ...document.classes.map((value) => ({
68651
+ id: value.id,
68652
+ value: neoScriptSchemaContractProjection(
68653
+ "class",
68654
+ value
68655
+ )
68656
+ })),
68657
+ ...document.members.map((value) => ({
68658
+ id: value.id,
68659
+ value: neoScriptMemberContractProjection(
68660
+ value
68661
+ )
68662
+ })),
68663
+ ...(document.constructors ?? []).map((value) => ({
68664
+ id: value.id,
68665
+ value: neoScriptConstructorContractProjection(
68666
+ value
68667
+ )
68668
+ })),
68669
+ ...document.enums.map((value) => ({
68670
+ id: value.id,
68671
+ value: neoScriptSchemaContractProjection(
68672
+ "enum",
68673
+ value
68674
+ )
68675
+ })),
68676
+ ...(document.interfaces ?? []).map((value) => ({
68677
+ id: value.id,
68678
+ value: neoScriptSchemaContractProjection(
68679
+ "interface",
68680
+ value
68681
+ )
68682
+ }))
68683
+ ];
68684
+ }
68685
+ function collectStructuralDependencyRecords(document) {
68686
+ const records2 = [];
68687
+ const resolver = new EffectiveStorageResolver({
68688
+ members: document.members,
68689
+ classes: document.classes,
68690
+ rootStorage: buildRootStorageMap(document.project)
68691
+ });
68692
+ for (const member of document.members) {
68693
+ for (const parentId of resolver.parentMemberIds(member.id)) {
68694
+ records2.push({ id: member.id, value: { memberId: parentId } });
68695
+ }
68696
+ }
68697
+ for (const schemaClass2 of document.classes) {
68698
+ for (const entry of mergeSchemas(
68699
+ resolveInheritanceChain(schemaClass2.id, document.classes)
68700
+ )) {
68701
+ records2.push({
68702
+ id: entry.memberId,
68703
+ value: { classId: schemaClass2.id }
68704
+ });
68705
+ }
68706
+ }
68707
+ return records2;
68708
+ }
68709
+ function classPlacementsForMember(memberId, document) {
68710
+ const placements = /* @__PURE__ */ new Set();
68711
+ for (const schemaClass2 of document.classes) {
68712
+ const surface = mergeSchemas(
68713
+ resolveInheritanceChain(schemaClass2.id, document.classes)
68714
+ );
68715
+ if (surface.some((entry) => entry.memberId === memberId)) {
68716
+ placements.add(schemaClass2.id);
68717
+ }
68718
+ }
68719
+ return placements;
68720
+ }
68721
+ function explicitTargetIds(changes) {
68722
+ const result = {
68723
+ memberIds: /* @__PURE__ */ new Set(),
68724
+ constructorIds: /* @__PURE__ */ new Set(),
68725
+ migrationIds: /* @__PURE__ */ new Set(),
68726
+ dialogueNodeIds: /* @__PURE__ */ new Set(),
68727
+ dialogueGroupIds: /* @__PURE__ */ new Set(),
68728
+ valueIds: /* @__PURE__ */ new Set()
68729
+ };
68730
+ for (const change of changes) {
68731
+ if (change.operation === "delete") continue;
68732
+ if (change.recordKind === "member") result.memberIds.add(change.recordId);
68733
+ if (change.recordKind === "constructor") {
68734
+ result.constructorIds.add(change.recordId);
68735
+ }
68736
+ if (change.recordKind === "migration") {
68737
+ result.migrationIds.add(change.recordId);
68738
+ }
68739
+ if (change.recordKind === "dialogue-node") {
68740
+ result.dialogueNodeIds.add(change.recordId);
68741
+ }
68742
+ if (change.recordKind === "dialogue-group") {
68743
+ result.dialogueGroupIds.add(change.recordId);
68744
+ }
68745
+ if (change.recordKind === "value") result.valueIds.add(change.recordId);
68746
+ }
68747
+ return result;
68748
+ }
68749
+ function selectedRecordIds(records2, explicitIds, impactedIds, impactedTypeNames, constructedClassIds, ownerId = () => null) {
68750
+ const selected2 = new Set(explicitIds);
68751
+ for (const record3 of records2) {
68752
+ const owner = ownerId(record3);
68753
+ if (owner !== null && impactedIds.has(owner) || recordDependsOnChangedContract(
68754
+ record3,
68755
+ impactedIds,
68756
+ impactedTypeNames,
68757
+ constructedClassIds
68758
+ )) {
68759
+ selected2.add(record3.id);
68760
+ }
68761
+ }
68762
+ return selected2;
68763
+ }
68764
+ function recordDependsOnChangedContract(record3, impactedIds, impactedTypeNames, constructedClassIds) {
68765
+ if (intersects(collectCompilerReferenceIds(record3), impactedIds)) return true;
68766
+ if (intersects(collectCompilerReferenceNames(record3), impactedTypeNames)) {
68767
+ return true;
68768
+ }
68769
+ if (intersects(collectConstructedClassIds(record3), constructedClassIds)) {
68770
+ return true;
68771
+ }
68772
+ return (impactedIds.size > 0 || impactedTypeNames.size > 0) && containsAuthoredNeoScriptSource(record3) && !containsCompiledNeoScriptBody(record3);
68773
+ }
68774
+ function containsAuthoredNeoScriptSource(value) {
68775
+ if (Array.isArray(value)) {
68776
+ return value.some(containsAuthoredNeoScriptSource);
68777
+ }
68778
+ if (!isObjectRecord3(value)) return false;
68779
+ for (const [field, child] of Object.entries(value)) {
68780
+ if ((field === "code" || field === "setterCode") && typeof child === "string" && child.trim().length > 0) {
68781
+ return true;
68782
+ }
68783
+ if (containsAuthoredNeoScriptSource(child)) return true;
68784
+ }
68785
+ return false;
68786
+ }
68787
+ function containsCompiledNeoScriptBody(value) {
68788
+ if (Array.isArray(value)) return value.some(containsCompiledNeoScriptBody);
68789
+ if (!isObjectRecord3(value)) return false;
68790
+ if (Array.isArray(value.parameters) && Array.isArray(value.instructions) && isObjectRecord3(value.typeInfo)) {
68791
+ return true;
68792
+ }
68793
+ return Object.values(value).some(containsCompiledNeoScriptBody);
68794
+ }
68795
+ function isObjectRecord3(value) {
68796
+ return value !== null && typeof value === "object" && !Array.isArray(value);
68797
+ }
68798
+ function constructorOwners(document) {
68799
+ const owners = /* @__PURE__ */ new Map();
68800
+ const constructorIds = new Set(
68801
+ (document.constructors ?? []).map((record3) => record3.id)
68802
+ );
68803
+ for (const schemaClass2 of document.classes) {
68804
+ for (const referenceId3 of collectCompilerReferenceIds(schemaClass2)) {
68805
+ if (constructorIds.has(referenceId3)) {
68806
+ owners.set(referenceId3, schemaClass2.id);
68807
+ }
68808
+ }
68809
+ }
68810
+ return owners;
68811
+ }
68812
+ function includeDerivedConstructionContracts(classIds, classes) {
68813
+ const derivedByBaseId = /* @__PURE__ */ new Map();
68814
+ for (const schemaClass2 of classes) {
68815
+ if (schemaClass2.extendsClassId === void 0) continue;
68816
+ const derived = derivedByBaseId.get(schemaClass2.extendsClassId) ?? [];
68817
+ derived.push(schemaClass2.id);
68818
+ derivedByBaseId.set(schemaClass2.extendsClassId, derived);
68819
+ }
68820
+ const queue = [...classIds];
68821
+ for (let index = 0; index < queue.length; index += 1) {
68822
+ const baseClassId = queue[index];
68823
+ if (baseClassId === void 0) continue;
68824
+ for (const derivedClassId of derivedByBaseId.get(baseClassId) ?? []) {
68825
+ if (classIds.has(derivedClassId)) continue;
68826
+ classIds.add(derivedClassId);
68827
+ queue.push(derivedClassId);
68828
+ }
68829
+ }
68830
+ }
68831
+ function completeTargets(document) {
68832
+ return {
68833
+ complete: true,
68834
+ memberIds: new Set(document.members.map((record3) => record3.id)),
68835
+ constructorIds: new Set(
68836
+ (document.constructors ?? []).map((record3) => record3.id)
68837
+ ),
68838
+ migrationIds: new Set(
68839
+ (document.migrations ?? []).map((record3) => record3.id)
68840
+ ),
68841
+ dialogueNodeIds: new Set(
68842
+ (document.dialogueNodes ?? []).map((record3) => record3.id)
68843
+ ),
68844
+ dialogueGroupIds: new Set(
68845
+ (document.dialogueGroups ?? []).map((record3) => record3.id)
68846
+ ),
68847
+ valueIds: new Set((document.values ?? []).map((record3) => record3.id))
68848
+ };
68849
+ }
68850
+ function addImpactedId(impactedIds, queue, id2) {
68851
+ if (impactedIds.has(id2)) return;
68852
+ impactedIds.add(id2);
68853
+ queue.push(id2);
68854
+ }
68855
+ function intersects(left, right) {
68856
+ for (const value of left) {
68857
+ if (right.has(value)) return true;
68858
+ }
68859
+ return false;
68860
+ }
68861
+ function withoutFields(value, ignored) {
68862
+ return Object.fromEntries(
68863
+ Object.entries(value).filter(([field]) => !ignored.has(field))
68864
+ );
68865
+ }
68866
+ var NON_CONTRACT_MEMBER_FIELDS, NS_PROPERTY_BODY_FIELDS, NS_FUNCTION_BODY_FIELDS;
68867
+ var init_neo_script_recompile_scope = __esm({
68868
+ "../src/database/neo-script-recompile-scope.ts"() {
68869
+ "use strict";
68870
+ init_canonical_json();
68871
+ init_member_kind_enum();
68872
+ init_inheritance();
68873
+ init_effective_storage();
68874
+ init_project_root_members();
68875
+ init_value_row_owner_members();
68876
+ NON_CONTRACT_MEMBER_FIELDS = /* @__PURE__ */ new Set([
68877
+ "createdAt",
68878
+ "updatedAt",
68879
+ "projectId",
68880
+ "docsText",
68881
+ "valueId",
68882
+ "defaultValue",
68883
+ "system",
68884
+ "storageKey",
68885
+ "searchKey",
68886
+ "minValue",
68887
+ "maxValue",
68888
+ "decimalPoints",
68889
+ "columnSettings",
68890
+ "schemaKeyOrder",
68891
+ "templateId"
68892
+ ]);
68893
+ NS_PROPERTY_BODY_FIELDS = /* @__PURE__ */ new Set([
68894
+ "code",
68895
+ "getter"
68896
+ ]);
68897
+ NS_FUNCTION_BODY_FIELDS = /* @__PURE__ */ new Set([
68898
+ "code",
68899
+ "action",
68900
+ "bodyMode",
68901
+ "uiAction"
68902
+ ]);
68903
+ }
68904
+ });
68905
+
68906
+ // ../src/database/project-content-hash.ts
68907
+ import { createHash as createHash3 } from "node:crypto";
68908
+ function hashCanonicalJson(value) {
68909
+ const canonicalJson = canonicalJsonStringify(value);
68910
+ return createHash3("sha256").update(canonicalJson).digest("hex");
68911
+ }
68912
+ var init_project_content_hash = __esm({
68913
+ "../src/database/project-content-hash.ts"() {
68914
+ "use strict";
68915
+ init_canonical_json();
68916
+ init_canonical_json();
68917
+ }
68918
+ });
68919
+
68920
+ // ../src/database/neoscript/project-fingerprint.ts
68921
+ function neoScriptCompilationProjectContract(document) {
68922
+ return {
68923
+ compilerRevision: NEOSCRIPT_COMPILER_REVISION,
68924
+ adapterRevision: NEOSCRIPT_COMPILER_ADAPTER_REVISION,
68925
+ project: {
68926
+ id: document.project.id,
68927
+ rootAssetsMemberId: document.project.rootAssetsMemberId,
68928
+ rootSaveFileMemberId: document.project.rootSaveFileMemberId,
68929
+ rootSessionMemberId: document.project.rootSessionMemberId
68930
+ },
68931
+ projectFiles: (document.projectFiles ?? []).map((file) => ({
68932
+ id: file.id,
68933
+ name: file.name,
68934
+ fileType: file.fileType
68935
+ })),
68936
+ members: document.members.map(
68937
+ (member) => neoScriptMemberContractProjection(
68938
+ member
68939
+ )
68940
+ ),
68941
+ classes: document.classes.map(
68942
+ (schemaClass2) => neoScriptSchemaContractProjection(
68943
+ "class",
68944
+ schemaClass2
68945
+ )
68946
+ ),
68947
+ enums: document.enums.map(
68948
+ (enumDefinition) => neoScriptSchemaContractProjection(
68949
+ "enum",
68950
+ enumDefinition
68951
+ )
68952
+ ),
68953
+ interfaces: (document.interfaces ?? []).map(
68954
+ (neoInterface) => neoScriptSchemaContractProjection(
68955
+ "interface",
68956
+ neoInterface
68957
+ )
68958
+ ),
68959
+ constructors: (document.constructors ?? []).map(
68960
+ (constructorRecord) => neoScriptConstructorContractProjection(
68961
+ constructorRecord
68962
+ )
68963
+ )
68964
+ };
68965
+ }
68966
+ var init_project_fingerprint = __esm({
68967
+ "../src/database/neoscript/project-fingerprint.ts"() {
68968
+ "use strict";
68969
+ init_src();
68970
+ init_neo_script_recompile_scope();
68971
+ init_compiler_adapter();
68972
+ }
68973
+ });
68974
+
68975
+ // src/project-source/neoscript-build-cache.ts
68976
+ import { mkdirSync as mkdirSync4, readFileSync as readFileSync4, renameSync as renameSync2, writeFileSync as writeFileSync4 } from "node:fs";
68977
+ import { createHash as createHash4 } from "node:crypto";
68978
+ import { dirname as dirname3, join as join4 } from "node:path";
68979
+ function loadOrBuildNeoScriptProjectV1(root, document) {
68980
+ const fingerprint = neoScriptProjectFingerprint(document);
68981
+ if (root.length > 0) {
68982
+ const cached = readCache(join4(root, NEOSCRIPT_BUILD_CACHE_PATH));
68983
+ if (cached?.fingerprint === fingerprint) return cached.project;
68984
+ }
68985
+ const project = createNeoScriptCompilationProject({
68986
+ project: document.project,
68987
+ projectFiles: document.projectFiles,
68988
+ members: document.members,
68989
+ classes: document.classes,
68990
+ enums: document.enums,
68991
+ interfaces: document.interfaces,
68992
+ constructors: document.constructors ?? []
68993
+ });
68994
+ if (root.length > 0) {
68995
+ writeCache(join4(root, NEOSCRIPT_BUILD_CACHE_PATH), {
68996
+ formatVersion: 1,
68997
+ fingerprint,
68998
+ project
68999
+ });
69000
+ }
69001
+ return project;
69002
+ }
69003
+ function neoScriptProjectFingerprint(document) {
69004
+ return createHash4("sha256").update(
69005
+ canonicalJsonStringify(neoScriptCompilationProjectContract(document))
69006
+ ).digest("hex");
69007
+ }
69008
+ function readCache(path) {
69009
+ try {
69010
+ const parsed = JSON.parse(readFileSync4(path, "utf8"));
69011
+ if (parsed === null || typeof parsed !== "object") return null;
69012
+ const cache = parsed;
69013
+ if (cache.formatVersion !== 1 || typeof cache.fingerprint !== "string" || !isNeoScriptProject(cache.project)) {
69014
+ return null;
69015
+ }
69016
+ return cache;
69017
+ } catch {
69018
+ return null;
69019
+ }
69020
+ }
69021
+ function writeCache(path, cache) {
69022
+ mkdirSync4(dirname3(path), { recursive: true });
69023
+ const temporary = `${path}.${process.pid}.tmp`;
69024
+ writeFileSync4(temporary, `${JSON.stringify(cache)}
69025
+ `, "utf8");
69026
+ renameSync2(temporary, path);
69027
+ }
69028
+ function isNeoScriptProject(value) {
69029
+ if (value === null || typeof value !== "object") return false;
69030
+ const record3 = value;
69031
+ return (typeof record3.version === "string" || typeof record3.version === "number") && Array.isArray(record3.types) && Array.isArray(record3.globals) && Array.isArray(record3.roots);
69032
+ }
69033
+ var NEOSCRIPT_BUILD_CACHE_PATH;
69034
+ var init_neoscript_build_cache = __esm({
69035
+ "src/project-source/neoscript-build-cache.ts"() {
69036
+ "use strict";
69037
+ init_project_content_hash();
69038
+ init_compiler_adapter();
69039
+ init_project_fingerprint();
69040
+ NEOSCRIPT_BUILD_CACHE_PATH = ".neo/build/neoscript-project-v1.json";
69041
+ }
69042
+ });
69043
+
67612
69044
  // src/project-source/local-initializer-materialization.ts
67613
69045
  function materializedInitializerReconciliationFailuresV4(args) {
67614
69046
  if (args.reconciliations.size === 0) return [];
67615
69047
  const records2 = reconciliationRecords(args.workspace, args.changes);
67616
69048
  const document = readPulledProjectDocumentV4(records2);
69049
+ const currentDocument = readPulledProjectDocumentV4(
69050
+ reconciliationRecords(args.workspace, [])
69051
+ );
69052
+ const recompileTargets = collectNeoScriptRecompileTargets({
69053
+ currentDocument,
69054
+ postDocument: document,
69055
+ forceRecompile: args.forceRecompile,
69056
+ changes: args.changes.map((change) => ({
69057
+ recordKind: change.recordKind,
69058
+ recordId: change.recordId,
69059
+ operation: change.kind,
69060
+ ...change.kind === "delete" ? { deleted: true } : {},
69061
+ ...change.nextData === void 0 ? {} : { nextData: change.nextData }
69062
+ }))
69063
+ });
67617
69064
  const currentRows = valueRowsFromWorkspace(args.workspace);
67618
69065
  const currentGraph = new MaterializedValueGraphContext(document, currentRows);
69066
+ const reconciliationByValueId = new Map(
69067
+ [...args.reconciliations.values()].map((entry) => [entry.valueId, entry])
69068
+ );
69069
+ const candidateValueIds = new Set(
69070
+ [...reconciliationByValueId.values()].filter((reconciliation) => reconciliation.storedConstructorArgs !== null).map((reconciliation) => reconciliation.valueId)
69071
+ );
69072
+ const candidateOwners = resolveOwnerMembersForValues(
69073
+ document,
69074
+ candidateValueIds
69075
+ );
69076
+ const currentRecords = reconciliationRecords(args.workspace, []);
69077
+ const cachedExpressions = readMaterializedConstructionBuildCacheV1(
69078
+ args.workspace.root,
69079
+ args.workspace.state
69080
+ );
69081
+ const canonicalExpressions = new Map(cachedExpressions ?? []);
69082
+ if (args.manifest !== void 0) {
69083
+ const missingRoots = [...candidateValueIds].flatMap((valueId) => {
69084
+ if (canonicalExpressions.has(valueId)) return [];
69085
+ const owner = candidateOwners.get(valueId);
69086
+ return owner === void 0 ? [] : [
69087
+ {
69088
+ valueId,
69089
+ member: owner
69090
+ }
69091
+ ];
69092
+ });
69093
+ if (missingRoots.length > 0) {
69094
+ for (const [valueId, expression] of emitStoredConstructorExpressionsV4(
69095
+ currentRecords,
69096
+ args.manifest,
69097
+ missingRoots
69098
+ )) {
69099
+ canonicalExpressions.set(valueId, expression);
69100
+ }
69101
+ writeMaterializedConstructionBuildCacheV1(
69102
+ args.workspace.root,
69103
+ args.workspace.state,
69104
+ canonicalExpressions
69105
+ );
69106
+ }
69107
+ }
69108
+ const replayableValueIds = /* @__PURE__ */ new Set();
69109
+ for (const valueId of candidateValueIds) {
69110
+ const reconciliation = reconciliationByValueId.get(valueId);
69111
+ const owner = candidateOwners.get(valueId);
69112
+ if (reconciliation === void 0 || owner === void 0) continue;
69113
+ const authoredConstructionChanged = args.manifest !== void 0 && canonicalExpressions.get(valueId) !== constructorExpressionSlice(reconciliation.code);
69114
+ if (authoredConstructionChanged || recompileTargets.valueIds.has(valueId)) {
69115
+ replayableValueIds.add(valueId);
69116
+ }
69117
+ }
69118
+ const owners = resolveOwnerMembersForValues(document, replayableValueIds);
69119
+ const compilationProject = replayableValueIds.size === 0 ? void 0 : loadOrBuildNeoScriptProjectV1(args.workspace.root, document);
67619
69120
  const failures = [];
67620
69121
  for (const reconciliation of args.reconciliations.values()) {
69122
+ if (reconciliation.storedConstructorArgs === null) continue;
69123
+ if (!replayableValueIds.has(reconciliation.valueId)) continue;
69124
+ const owner = owners.get(reconciliation.valueId);
69125
+ if (owner === void 0) {
69126
+ throw new Error(
69127
+ `Cannot reconcile stored construction for value "${reconciliation.valueId}": its owning member is unresolved.`
69128
+ );
69129
+ }
67621
69130
  const replayRows = new Map(currentRows);
67622
69131
  for (const [id2, row] of replayStoredConstructionV4({
67623
69132
  records: records2,
69133
+ document,
67624
69134
  valueId: reconciliation.valueId,
67625
- code: reconciliation.code
69135
+ code: reconciliation.code,
69136
+ member: owner,
69137
+ ...compilationProject === void 0 ? {} : { compilationProject }
67626
69138
  })) {
67627
69139
  if (!isMemberValue(row)) {
67628
69140
  throw new Error(
@@ -67634,7 +69146,7 @@ function materializedInitializerReconciliationFailuresV4(args) {
67634
69146
  const replay = replayRows.get(reconciliation.valueId);
67635
69147
  const replayArgs = replay !== void 0 && isLiteralValueContent(replay) ? replay.constructorArgs : void 0;
67636
69148
  const currentRoot = currentRows.get(reconciliation.valueId);
67637
- const argumentsMatch = reconciliation.storedConstructorArgs === null ? true : isObjectRecord2(replayArgs) && currentRoot !== void 0 && replay !== void 0 && canonicallyEqual(
69149
+ const argumentsMatch = isObjectRecord2(replayArgs) && currentRoot !== void 0 && replay !== void 0 && canonicallyEqual(
67638
69150
  currentGraph.normalizeConstructorArguments(
67639
69151
  reconciliation.storedConstructorArgs,
67640
69152
  currentRoot
@@ -67928,10 +69440,14 @@ var init_local_initializer_materialization = __esm({
67928
69440
  init_source_diagnostics();
67929
69441
  init_projection();
67930
69442
  init_value_sources();
69443
+ init_init_source();
69444
+ init_materialized_construction_cache();
67931
69445
  init_initializer_replay();
67932
69446
  init_members();
67933
69447
  init_constructor_argument_ownership();
67934
69448
  init_value_row_owner_members();
69449
+ init_neo_script_recompile_scope();
69450
+ init_neoscript_build_cache();
67935
69451
  }
67936
69452
  });
67937
69453
 
@@ -68081,6 +69597,12 @@ function compareWorkspacePaths(left, right) {
68081
69597
  return 0;
68082
69598
  }
68083
69599
  function computeWorkspaceStatus(workspace, options) {
69600
+ let phaseStarted = performance.now();
69601
+ const reportPhase = (phase) => {
69602
+ const finished = performance.now();
69603
+ options.reportPhase?.(phase, finished - phaseStarted);
69604
+ phaseStarted = finished;
69605
+ };
68084
69606
  const conflictedFiles = [];
68085
69607
  const parseErrors = [];
68086
69608
  const parseWarnings = [];
@@ -68138,13 +69660,12 @@ function computeWorkspaceStatus(workspace, options) {
68138
69660
  "Format-4 status requires a pulled schema base; run `neo pull --reset`."
68139
69661
  );
68140
69662
  }
68141
- const analysis = compileNeoProjectSources(
68142
- projectSources.map((source) => ({
68143
- uri: source.uri,
68144
- kind: source.kind,
68145
- text: source.source
68146
- }))
68147
- );
69663
+ const compilationSources = projectSources.map((source) => ({
69664
+ uri: source.uri,
69665
+ kind: source.kind,
69666
+ text: source.source
69667
+ }));
69668
+ const analysis = options.readProjectAnalysisCache?.(workspace.root, compilationSources) ?? options.compileProjectAnalysis?.(workspace.root, compilationSources) ?? compileNeoProjectSources(compilationSources);
68148
69669
  projectAnalysisV4 = analysis;
68149
69670
  for (const diagnostic of analysis.diagnostics) {
68150
69671
  const positioned = new SchemaSourceError(
@@ -68220,7 +69741,12 @@ function computeWorkspaceStatus(workspace, options) {
68220
69741
  })
68221
69742
  };
68222
69743
  }
68223
- options.writeProjectAnalysisCache?.(workspace.root, analysis);
69744
+ options.writeProjectAnalysisCache?.(
69745
+ workspace.root,
69746
+ analysis,
69747
+ compilationSources
69748
+ );
69749
+ reportPhase("analysis-schema-defaults");
68224
69750
  } catch (error) {
68225
69751
  parseErrors.push(
68226
69752
  error instanceof SchemaSourceError ? error : new SchemaSourceError(
@@ -68295,6 +69821,7 @@ function computeWorkspaceStatus(workspace, options) {
68295
69821
  manifest,
68296
69822
  { registry: valueLowerRegistry }
68297
69823
  );
69824
+ reportPhase("documents-static-root");
68298
69825
  authoredValueSeeds = new Map([...authoredValueSeeds, ...rootValues.seeds]);
68299
69826
  const rootPathResolutionState = overlayProspectiveSourceRecords(
68300
69827
  workspace.state.records,
@@ -68407,6 +69934,7 @@ function computeWorkspaceStatus(workspace, options) {
68407
69934
  prospectiveState,
68408
69935
  projectAnalysisV4
68409
69936
  );
69937
+ reportPhase("supplemental-dialogue");
68410
69938
  records2.push(
68411
69939
  ...staticValues.records,
68412
69940
  ...memberDefaults.records,
@@ -68523,6 +70051,7 @@ function computeWorkspaceStatus(workspace, options) {
68523
70051
  )
68524
70052
  );
68525
70053
  }
70054
+ reportPhase("record-diff-references");
68526
70055
  if (referenceFailures.length > 0) {
68527
70056
  return {
68528
70057
  changes: [],
@@ -68555,6 +70084,8 @@ function computeWorkspaceStatus(workspace, options) {
68555
70084
  changes,
68556
70085
  authoredValueSeeds,
68557
70086
  reconciliations: valueLowerRegistry.initializerReconciliations,
70087
+ manifest,
70088
+ forceRecompile: options.forceRecompile,
68558
70089
  sourceTextByUri: new Map(
68559
70090
  sourceEntries.map((entry) => [entry.relPath, entry.source])
68560
70091
  )
@@ -68570,6 +70101,7 @@ function computeWorkspaceStatus(workspace, options) {
68570
70101
  )
68571
70102
  );
68572
70103
  }
70104
+ reportPhase("initializer-reconciliation");
68573
70105
  for (const fileId of options.trustedPendingProjectFiles?.keys() ?? []) {
68574
70106
  const key = `project-file:${fileId}`;
68575
70107
  const base = workspace.state.records[key];
@@ -68607,6 +70139,7 @@ function computeWorkspaceStatus(workspace, options) {
68607
70139
  binaryChanges = binaryFiles.filter(
68608
70140
  (binary) => binary.action !== "unchanged" && binary.action !== "converged"
68609
70141
  );
70142
+ reportPhase("binary-inspection");
68610
70143
  const invalidatedFileIds = /* @__PURE__ */ new Set([
68611
70144
  ...binaryFiles.filter(
68612
70145
  (binary) => binary.action === "create" || binary.action === "upload"
@@ -70134,62 +71667,6 @@ var init_member_access_modifier_validation = __esm({
70134
71667
  }
70135
71668
  });
70136
71669
 
70137
- // ../src/database/canonical-json.ts
70138
- function canonicalJsonStringify(value) {
70139
- return JSON.stringify(toCanonicalJsonValue(value));
70140
- }
70141
- function toCanonicalJsonValue(value) {
70142
- if (value === null) return null;
70143
- if (value === void 0) return void 0;
70144
- if (value instanceof Date) return value.toISOString();
70145
- if (typeof value === "string") return value;
70146
- if (typeof value === "boolean") return value;
70147
- if (typeof value === "number") {
70148
- if (!Number.isFinite(value)) {
70149
- throw new Error("Cannot canonicalize a non-finite number.");
70150
- }
70151
- return value;
70152
- }
70153
- if (typeof value === "bigint") {
70154
- throw new Error("Cannot canonicalize bigint values.");
70155
- }
70156
- if (typeof value === "symbol") {
70157
- throw new Error("Cannot canonicalize symbol values.");
70158
- }
70159
- if (typeof value === "function") {
70160
- throw new Error("Cannot canonicalize function values.");
70161
- }
70162
- if (Array.isArray(value)) {
70163
- return value.map((item) => {
70164
- const canonicalItem = toCanonicalJsonValue(item);
70165
- if (canonicalItem === void 0) return null;
70166
- return canonicalItem;
70167
- });
70168
- }
70169
- const entries = Object.entries(value).sort(([a], [b]) => a.localeCompare(b));
70170
- const canonicalObject = {};
70171
- for (const [key, entryValue] of entries) {
70172
- const canonicalValue = toCanonicalJsonValue(entryValue);
70173
- if (canonicalValue === void 0) continue;
70174
- canonicalObject[key] = canonicalValue;
70175
- }
70176
- return canonicalObject;
70177
- }
70178
- var init_canonical_json = __esm({
70179
- "../src/database/canonical-json.ts"() {
70180
- "use strict";
70181
- }
70182
- });
70183
-
70184
- // ../src/database/project-content-hash.ts
70185
- var init_project_content_hash = __esm({
70186
- "../src/database/project-content-hash.ts"() {
70187
- "use strict";
70188
- init_canonical_json();
70189
- init_canonical_json();
70190
- }
70191
- });
70192
-
70193
71670
  // ../src/database/project-migration-created-values.ts
70194
71671
  function validateCreatedMigrationGraph(args) {
70195
71672
  if (args.createdSessionValues.length === 0) return [];
@@ -72459,60 +73936,9 @@ var init_localizable_member_value_writes = __esm({
72459
73936
  }
72460
73937
  });
72461
73938
 
72462
- // ../src/database/neo-script-recompile-scope.ts
72463
- function memberNeoScriptContractChanged(current, next) {
72464
- if (current.kind !== next.kind) return true;
72465
- const ignored = new Set(NON_CONTRACT_MEMBER_FIELDS);
72466
- if (current.kind === 10 /* NSProperty */) {
72467
- for (const field of NS_PROPERTY_BODY_FIELDS) ignored.add(field);
72468
- }
72469
- if (current.kind === 23 /* NSFunction */) {
72470
- for (const field of NS_FUNCTION_BODY_FIELDS) ignored.add(field);
72471
- }
72472
- return canonicalJsonStringify(withoutFields(current, ignored)) !== canonicalJsonStringify(withoutFields(next, ignored));
72473
- }
72474
- function withoutFields(value, ignored) {
72475
- return Object.fromEntries(
72476
- Object.entries(value).filter(([field]) => !ignored.has(field))
72477
- );
72478
- }
72479
- var NON_CONTRACT_MEMBER_FIELDS, NS_PROPERTY_BODY_FIELDS, NS_FUNCTION_BODY_FIELDS;
72480
- var init_neo_script_recompile_scope = __esm({
72481
- "../src/database/neo-script-recompile-scope.ts"() {
72482
- "use strict";
72483
- init_project_content_hash();
72484
- init_member_kind_enum();
72485
- NON_CONTRACT_MEMBER_FIELDS = /* @__PURE__ */ new Set([
72486
- "createdAt",
72487
- "updatedAt",
72488
- "projectId",
72489
- "docsText",
72490
- "valueId",
72491
- "system",
72492
- "storageKey",
72493
- "searchKey",
72494
- "minValue",
72495
- "maxValue",
72496
- "decimalPoints",
72497
- "columnSettings",
72498
- "schemaKeyOrder",
72499
- "templateId"
72500
- ]);
72501
- NS_PROPERTY_BODY_FIELDS = /* @__PURE__ */ new Set([
72502
- "code",
72503
- "getter"
72504
- ]);
72505
- NS_FUNCTION_BODY_FIELDS = /* @__PURE__ */ new Set([
72506
- "code",
72507
- "action",
72508
- "bodyMode",
72509
- "uiAction"
72510
- ]);
72511
- }
72512
- });
72513
-
72514
73939
  // ../src/database/project-version-schema-commit.ts
72515
73940
  function prepareServerOwnedSchemaCommit(args) {
73941
+ if (args.forceRecompile === true) clearNeoScriptBodyCompileCache();
72516
73942
  assertUniqueChanges(args.changes);
72517
73943
  let authoredChanges = completeLocalizedTextCreateEnvelopes({
72518
73944
  document: args.document,
@@ -72547,19 +73973,21 @@ function prepareServerOwnedSchemaCommit(args) {
72547
73973
  if (!hasSchemaChange && !hasMigrationChange && !hasDialogueSourceChange && !hasValueChange) {
72548
73974
  return authoredChanges;
72549
73975
  }
72550
- const requiresCompleteSweep = schemaChangesRequireCompleteBodySweep(
73976
+ const authoredPostDocument = applyProjectVersionWriteChanges(
72551
73977
  args.document,
72552
73978
  authoredChanges
72553
73979
  );
73980
+ const recompileTargets = collectNeoScriptRecompileTargets({
73981
+ currentDocument: args.document,
73982
+ postDocument: authoredPostDocument,
73983
+ changes: authoredChanges,
73984
+ forceRecompile: args.forceRecompile
73985
+ });
72554
73986
  const explicitlyChangedMemberIds = new Set(
72555
73987
  authoredChanges.filter(
72556
73988
  (change) => change.recordKind === "member" && change.operation !== "delete"
72557
73989
  ).map((change) => change.recordId)
72558
73990
  );
72559
- const authoredPostDocument = applyProjectVersionWriteChanges(
72560
- args.document,
72561
- authoredChanges
72562
- );
72563
73991
  const currentById = new Map(
72564
73992
  args.document.members.map((member) => [member.id, member])
72565
73993
  );
@@ -72569,24 +73997,33 @@ function prepareServerOwnedSchemaCommit(args) {
72569
73997
  const currentSourceMembers = args.document.members.map(
72570
73998
  (member) => stripDerivedBodies(member)
72571
73999
  );
74000
+ const postCompilationProject = cachedServerCompilationProject({
74001
+ ...authoredPostDocument,
74002
+ members: sourceMembers
74003
+ });
74004
+ const currentCompilationProject = cachedServerCompilationProject({
74005
+ ...args.document,
74006
+ members: currentSourceMembers
74007
+ });
72572
74008
  const toleratedPreexistingFailures = /* @__PURE__ */ new Set();
72573
74009
  for (const member of sourceMembers) {
72574
74010
  if (!hasAuthoredNeoScriptBody(member)) continue;
72575
- if (!requiresCompleteSweep && !explicitlyChangedMemberIds.has(member.id)) {
74011
+ if (!recompileTargets.memberIds.has(member.id)) {
72576
74012
  continue;
72577
74013
  }
72578
74014
  try {
72579
74015
  compileOneAuthoredMember({
72580
74016
  document: authoredPostDocument,
72581
74017
  members: sourceMembers,
72582
- member
74018
+ member,
74019
+ compilationProject: postCompilationProject
72583
74020
  });
72584
74021
  } catch (postWriteError) {
72585
74022
  if (explicitlyChangedMemberIds.has(member.id)) {
72586
74023
  throw postWriteError;
72587
74024
  }
72588
74025
  const current = currentById.get(member.id);
72589
- if (!requiresCompleteSweep || current === void 0 || !hasAuthoredNeoScriptBody(current)) {
74026
+ if (current === void 0 || !hasAuthoredNeoScriptBody(current)) {
72590
74027
  throw postWriteError;
72591
74028
  }
72592
74029
  const currentClone = stripDerivedBodies(current);
@@ -72594,7 +74031,8 @@ function prepareServerOwnedSchemaCommit(args) {
72594
74031
  compileOneAuthoredMember({
72595
74032
  document: args.document,
72596
74033
  members: currentSourceMembers,
72597
- member: currentClone
74034
+ member: currentClone,
74035
+ compilationProject: currentCompilationProject
72598
74036
  });
72599
74037
  } catch (preWriteError) {
72600
74038
  if (sameCompileFailure(preWriteError, postWriteError)) {
@@ -72627,7 +74065,7 @@ function prepareServerOwnedSchemaCommit(args) {
72627
74065
  args.contentHashHeads.filter((head) => !head.deleted).map((head) => [`${head.recordKind}:${head.recordId}`, head.contentHash])
72628
74066
  );
72629
74067
  for (const compiled of sourceMembers) {
72630
- if (!requiresCompleteSweep) break;
74068
+ if (!recompileTargets.memberIds.has(compiled.id)) continue;
72631
74069
  if (explicitMemberIds.has(compiled.id)) continue;
72632
74070
  if (toleratedPreexistingFailures.has(compiled.id)) continue;
72633
74071
  if (!hasAuthoredNeoScriptBody(compiled)) continue;
@@ -72654,7 +74092,7 @@ function prepareServerOwnedSchemaCommit(args) {
72654
74092
  document: args.document,
72655
74093
  postDocument: authoredPostDocument,
72656
74094
  prepared,
72657
- requiresCompleteSweep,
74095
+ targetIds: recompileTargets.constructorIds,
72658
74096
  contentHashHeads: args.contentHashHeads
72659
74097
  });
72660
74098
  const compiledPostDocument = applyProjectVersionWriteChanges(
@@ -72730,14 +74168,14 @@ function prepareServerOwnedSchemaCommit(args) {
72730
74168
  document: args.document,
72731
74169
  postDocument,
72732
74170
  prepared,
72733
- requiresCompleteSweep,
74171
+ targetIds: recompileTargets.migrationIds,
72734
74172
  contentHashHeads: args.contentHashHeads
72735
74173
  });
72736
74174
  prepareServerOwnedDialogueBodies({
72737
74175
  currentDocument: args.document,
72738
74176
  postDocument,
72739
74177
  prepared,
72740
- requiresCompleteSweep,
74178
+ recompileTargets,
72741
74179
  contentHashHeads: args.contentHashHeads
72742
74180
  });
72743
74181
  materializeAuthoredValueSeeds({
@@ -72750,7 +74188,7 @@ function prepareServerOwnedSchemaCommit(args) {
72750
74188
  document: args.document,
72751
74189
  postDocument,
72752
74190
  prepared,
72753
- requiresCompleteSweep,
74191
+ targetIds: recompileTargets.valueIds,
72754
74192
  contentHashHeads: args.contentHashHeads
72755
74193
  });
72756
74194
  materializePreparedInstanceInitializers({
@@ -72769,7 +74207,7 @@ function prepareServerOwnedSchemaCommit(args) {
72769
74207
  document: args.document,
72770
74208
  postDocument,
72771
74209
  prepared,
72772
- requiresCompleteSweep,
74210
+ targetIds: recompileTargets.valueIds,
72773
74211
  contentHashHeads: args.contentHashHeads
72774
74212
  });
72775
74213
  const committedDocument = applyProjectVersionWriteChanges(
@@ -73709,7 +75147,6 @@ function prepareServerOwnedDialogueBodies(args) {
73709
75147
  explicitGroupIds.add(change.recordId);
73710
75148
  }
73711
75149
  }
73712
- const compileEveryNode = args.requiresCompleteSweep;
73713
75150
  const compiledNodes = /* @__PURE__ */ new Map();
73714
75151
  for (const node of postNodes) {
73715
75152
  const nodeId = requiredRecordString(node, "id", "dialogue node");
@@ -73718,7 +75155,9 @@ function prepareServerOwnedDialogueBodies(args) {
73718
75155
  "dialogueId",
73719
75156
  `dialogue node ${nodeId}`
73720
75157
  );
73721
- if (!compileEveryNode && !changedDialogueIds.has(dialogueId)) continue;
75158
+ if (!changedDialogueIds.has(dialogueId) && !args.recompileTargets.dialogueNodeIds.has(nodeId)) {
75159
+ continue;
75160
+ }
73722
75161
  const dialogue = postDialogues.get(dialogueId);
73723
75162
  if (dialogue === void 0) {
73724
75163
  throw new Error(
@@ -73739,7 +75178,9 @@ function prepareServerOwnedDialogueBodies(args) {
73739
75178
  for (const groupValue of args.postDocument.dialogueGroups ?? []) {
73740
75179
  const group = groupValue;
73741
75180
  const groupId = requiredRecordString(group, "id", "dialogue group");
73742
- if (!args.requiresCompleteSweep && !explicitGroupIds.has(groupId)) continue;
75181
+ if (!explicitGroupIds.has(groupId) && !args.recompileTargets.dialogueGroupIds.has(groupId)) {
75182
+ continue;
75183
+ }
73743
75184
  compiledGroups.set(
73744
75185
  groupId,
73745
75186
  compileDialogueGroupSourceBodies({ group, document: args.postDocument })
@@ -74989,11 +76430,11 @@ function prepareServerOwnedValueInitializerBodies(args) {
74989
76430
  if (row === null) continue;
74990
76431
  explicitRows.set(index, row);
74991
76432
  }
74992
- const sweepRows = args.requiresCompleteSweep ? args.postDocument.values.filter(
74993
- (value) => isInitValueContent(value) && !args.prepared.some(
76433
+ const sweepRows = args.postDocument.values.filter(
76434
+ (value) => args.targetIds.has(value.id) && isInitValueContent(value) && !args.prepared.some(
74994
76435
  (change) => change.recordKind === "value" && change.recordId === value.id
74995
76436
  )
74996
- ) : [];
76437
+ );
74997
76438
  if (explicitRows.size === 0 && sweepRows.length === 0) return;
74998
76439
  const committedDocument = applyProjectVersionWriteChanges(
74999
76440
  args.postDocument,
@@ -75101,11 +76542,11 @@ function prepareServerOwnedDelegateValueBodies(args) {
75101
76542
  const row = delegateValueRow(change.nextData);
75102
76543
  if (row !== null) explicitRows.set(index, row);
75103
76544
  }
75104
- const sweepRows = args.requiresCompleteSweep ? args.postDocument.values.filter(
75105
- (value) => delegateValueRow(value) !== null && !args.prepared.some(
76545
+ const sweepRows = args.postDocument.values.filter(
76546
+ (value) => args.targetIds.has(value.id) && delegateValueRow(value) !== null && !args.prepared.some(
75106
76547
  (change) => change.recordKind === "value" && change.recordId === value.id
75107
76548
  )
75108
- ) : [];
76549
+ );
75109
76550
  if (explicitRows.size === 0 && sweepRows.length === 0) return;
75110
76551
  const committedDocument = applyProjectVersionWriteChanges(
75111
76552
  args.postDocument,
@@ -75204,12 +76645,19 @@ function prepareServerOwnedConstructorBodies(args) {
75204
76645
  const sourceConstructors = (args.postDocument.constructors ?? []).map(
75205
76646
  (record3) => stripConstructorDerivedBodies(record3)
75206
76647
  );
76648
+ const postCompilationProject = cachedServerCompilationProject({
76649
+ ...args.postDocument,
76650
+ constructors: sourceConstructors
76651
+ });
76652
+ const currentCompilationProject = cachedServerCompilationProject(
76653
+ args.document
76654
+ );
75207
76655
  const explicitIds = new Set(
75208
76656
  args.prepared.filter(
75209
76657
  (change) => change.recordKind === "constructor" && change.operation !== "delete"
75210
76658
  ).map((change) => change.recordId)
75211
76659
  );
75212
- const compileOne = (document, constructors, constructorRecord) => {
76660
+ const compileOne = (document, constructors, constructorRecord, compilationProject) => {
75213
76661
  compileConstructorRecord({
75214
76662
  project: document.project,
75215
76663
  projectFiles: document.projectFiles,
@@ -75218,24 +76666,33 @@ function prepareServerOwnedConstructorBodies(args) {
75218
76666
  enums: document.enums,
75219
76667
  interfaces: document.interfaces,
75220
76668
  constructors,
76669
+ compilationProject,
75221
76670
  constructor: constructorRecord
75222
76671
  });
75223
76672
  };
75224
76673
  const toleratedFailures = /* @__PURE__ */ new Set();
75225
76674
  for (const constructorRecord of sourceConstructors) {
75226
- if (!args.requiresCompleteSweep && !explicitIds.has(constructorRecord.id)) {
76675
+ if (!args.targetIds.has(constructorRecord.id)) {
75227
76676
  continue;
75228
76677
  }
75229
76678
  try {
75230
- compileOne(args.postDocument, sourceConstructors, constructorRecord);
76679
+ compileOne(
76680
+ args.postDocument,
76681
+ sourceConstructors,
76682
+ constructorRecord,
76683
+ postCompilationProject
76684
+ );
75231
76685
  } catch (postWriteError) {
75232
76686
  if (explicitIds.has(constructorRecord.id)) throw postWriteError;
75233
76687
  const current = currentById.get(constructorRecord.id);
75234
76688
  if (current === void 0) throw postWriteError;
75235
76689
  try {
75236
- compileOne(args.document, args.document.constructors ?? [], {
75237
- ...current
75238
- });
76690
+ compileOne(
76691
+ args.document,
76692
+ args.document.constructors ?? [],
76693
+ { ...current },
76694
+ currentCompilationProject
76695
+ );
75239
76696
  } catch (preWriteError) {
75240
76697
  if (sameCompileFailure(preWriteError, postWriteError)) {
75241
76698
  toleratedFailures.add(constructorRecord.id);
@@ -75260,7 +76717,6 @@ function prepareServerOwnedConstructorBodies(args) {
75260
76717
  }
75261
76718
  args.prepared[index] = { ...change, nextData: compiled };
75262
76719
  }
75263
- if (!args.requiresCompleteSweep) return;
75264
76720
  const explicitAllIds = new Set(
75265
76721
  args.prepared.filter((change) => change.recordKind === "constructor").map((change) => change.recordId)
75266
76722
  );
@@ -75268,6 +76724,7 @@ function prepareServerOwnedConstructorBodies(args) {
75268
76724
  args.contentHashHeads.filter((head) => head.recordKind === "constructor" && !head.deleted).map((head) => [head.recordId, head.contentHash])
75269
76725
  );
75270
76726
  for (const compiled of sourceConstructors) {
76727
+ if (!args.targetIds.has(compiled.id)) continue;
75271
76728
  if (explicitAllIds.has(compiled.id)) continue;
75272
76729
  if (toleratedFailures.has(compiled.id)) continue;
75273
76730
  const current = currentById.get(compiled.id);
@@ -75307,7 +76764,7 @@ function prepareServerOwnedMigrationBodies(args) {
75307
76764
  );
75308
76765
  const toleratedFailures = /* @__PURE__ */ new Set();
75309
76766
  for (const migration of sourceMigrations) {
75310
- if (!args.requiresCompleteSweep && !explicitIds.has(migration.id)) continue;
76767
+ if (!args.targetIds.has(migration.id)) continue;
75311
76768
  try {
75312
76769
  Object.assign(migration, {
75313
76770
  action: compileProjectMigrationAction(args.postDocument, migration)
@@ -75343,7 +76800,6 @@ function prepareServerOwnedMigrationBodies(args) {
75343
76800
  }
75344
76801
  args.prepared[index] = { ...change, nextData: compiled };
75345
76802
  }
75346
- if (!args.requiresCompleteSweep) return;
75347
76803
  const explicitAllIds = new Set(
75348
76804
  args.prepared.filter((change) => change.recordKind === "migration").map((change) => change.recordId)
75349
76805
  );
@@ -75351,6 +76807,7 @@ function prepareServerOwnedMigrationBodies(args) {
75351
76807
  args.contentHashHeads.filter((head) => head.recordKind === "migration" && !head.deleted).map((head) => [head.recordId, head.contentHash])
75352
76808
  );
75353
76809
  for (const compiled of sourceMigrations) {
76810
+ if (!args.targetIds.has(compiled.id)) continue;
75354
76811
  if (explicitAllIds.has(compiled.id) || toleratedFailures.has(compiled.id)) {
75355
76812
  continue;
75356
76813
  }
@@ -75384,6 +76841,7 @@ function compileOneAuthoredMember(args) {
75384
76841
  enums: args.document.enums,
75385
76842
  interfaces: args.document.interfaces,
75386
76843
  constructors: args.document.constructors,
76844
+ compilationProject: args.compilationProject,
75387
76845
  member: args.member,
75388
76846
  thisClass: placement?.ownerClass ?? null
75389
76847
  });
@@ -75392,22 +76850,24 @@ function sameCompileFailure(left, right) {
75392
76850
  if (!(left instanceof Error) || !(right instanceof Error)) return false;
75393
76851
  return left.name === right.name && left.message === right.message;
75394
76852
  }
75395
- function schemaChangesRequireCompleteBodySweep(document, changes) {
75396
- const currentMembers = new Map(
75397
- document.members.map((member) => [member.id, member])
76853
+ function cachedServerCompilationProject(document) {
76854
+ const fingerprint = hashCanonicalJson(
76855
+ neoScriptCompilationProjectContract(document)
75398
76856
  );
75399
- for (const change of changes) {
75400
- if (change.recordKind === "class" || change.recordKind === "constructor" || change.recordKind === "enum" || change.recordKind === "interface") {
75401
- return true;
75402
- }
75403
- if (change.recordKind !== "member") continue;
75404
- if (change.operation !== "update") return true;
75405
- const current = currentMembers.get(change.recordId);
75406
- const next = asRecord(change.nextData);
75407
- if (current === void 0 || next === null) return true;
75408
- if (memberNeoScriptContractChanged({ ...current }, next)) return true;
76857
+ const cached = serverCompilationProjectCache.get(fingerprint);
76858
+ if (cached !== void 0) {
76859
+ serverCompilationProjectCache.delete(fingerprint);
76860
+ serverCompilationProjectCache.set(fingerprint, cached);
76861
+ return cached;
75409
76862
  }
75410
- return false;
76863
+ const compiled = createNeoScriptCompilationProject(document);
76864
+ serverCompilationProjectCache.set(fingerprint, compiled);
76865
+ while (serverCompilationProjectCache.size > SERVER_COMPILATION_PROJECT_CACHE_LIMIT) {
76866
+ const oldest = serverCompilationProjectCache.keys().next().value;
76867
+ if (typeof oldest !== "string") break;
76868
+ serverCompilationProjectCache.delete(oldest);
76869
+ }
76870
+ return compiled;
75411
76871
  }
75412
76872
  function assertUniqueChanges(changes) {
75413
76873
  const keys = /* @__PURE__ */ new Set();
@@ -75673,7 +77133,7 @@ function requiredRecordString(value, field, label) {
75673
77133
  function canonicallyEqual2(left, right) {
75674
77134
  return canonicalJsonStringify(left) === canonicalJsonStringify(right);
75675
77135
  }
75676
- var READ_ONLY_SOURCE_CONVERSION_INTENT_SOURCE, SCHEMA_RECORD_KINDS, DIALOGUE_SOURCE_RECORD_KINDS;
77136
+ var SERVER_COMPILATION_PROJECT_CACHE_LIMIT, serverCompilationProjectCache, READ_ONLY_SOURCE_CONVERSION_INTENT_SOURCE, SCHEMA_RECORD_KINDS, DIALOGUE_SOURCE_RECORD_KINDS;
75677
77137
  var init_project_version_schema_commit = __esm({
75678
77138
  "../src/database/project-version-schema-commit.ts"() {
75679
77139
  "use strict";
@@ -75688,6 +77148,8 @@ var init_project_version_schema_commit = __esm({
75688
77148
  init_internal_record_relations();
75689
77149
  init_project_content_hash();
75690
77150
  init_compile_ns_property();
77151
+ init_compiler_adapter();
77152
+ init_project_fingerprint();
75691
77153
  init_project_migration_runner();
75692
77154
  init_neoscript_evaluator();
75693
77155
  init_project_version_intents();
@@ -75704,6 +77166,8 @@ var init_project_version_schema_commit = __esm({
75704
77166
  init_member_value_id();
75705
77167
  init_world_system_classes();
75706
77168
  init_neo_script_recompile_scope();
77169
+ SERVER_COMPILATION_PROJECT_CACHE_LIMIT = 4;
77170
+ serverCompilationProjectCache = /* @__PURE__ */ new Map();
75707
77171
  READ_ONLY_SOURCE_CONVERSION_INTENT_SOURCE = "server-readonly-source-conversion";
75708
77172
  SCHEMA_RECORD_KINDS = /* @__PURE__ */ new Set([
75709
77173
  "member",
@@ -78917,7 +80381,8 @@ function prepareServerPreparationChanges(args) {
78917
80381
  initializerMaterialization: args.initializerMaterialization,
78918
80382
  // A CLI push is always a trusted-source commit, so the server always
78919
80383
  // expands read-only source conversions for it.
78920
- expandReadOnlySourceConversions: true
80384
+ expandReadOnlySourceConversions: true,
80385
+ forceRecompile: args.forceRecompile
78921
80386
  });
78922
80387
  assertProjectVersionWholeGraphWritesValid({ document, changes: prepared });
78923
80388
  return prepared;
@@ -79072,7 +80537,8 @@ function replayStoredConstructionV4(args) {
79072
80537
  valueId: args.valueId,
79073
80538
  // Instance calls are self-contained. Declaration calls inherit the class
79074
80539
  // header parameters that are in lexical scope at their source site.
79075
- initializerOwnerClass: args.initializerOwnerClass ?? null
80540
+ initializerOwnerClass: args.initializerOwnerClass ?? null,
80541
+ ...args.compilationProject === void 0 ? {} : { compilationProject: args.compilationProject }
79076
80542
  });
79077
80543
  if (!isMemberValue(candidate) || !isInitValueContent(candidate)) {
79078
80544
  throw new Error(
@@ -79265,7 +80731,8 @@ function buildValueEmitContext(records2, manifest) {
79265
80731
  ),
79266
80732
  fileSymbols: projectFileSymbols(records2),
79267
80733
  localizedTextIds: /* @__PURE__ */ new Set(),
79268
- constructionReplays: /* @__PURE__ */ new Map()
80734
+ constructionReplays: /* @__PURE__ */ new Map(),
80735
+ materializedConstructors: /* @__PURE__ */ new Map()
79269
80736
  };
79270
80737
  }
79271
80738
  function emitStoredValueBindingSourcesV4(records2, memberIds, options) {
@@ -79302,7 +80769,100 @@ function emitStoredValueBindingSourcesV4(records2, memberIds, options) {
79302
80769
  );
79303
80770
  context.localizedTextIds.clear();
79304
80771
  }
79305
- return { initializers, recordKeysByMember };
80772
+ return {
80773
+ initializers,
80774
+ recordKeysByMember,
80775
+ materializedConstructors: context.materializedConstructors
80776
+ };
80777
+ }
80778
+ function emitStoredConstructorExpressionsV4(records2, manifest, roots) {
80779
+ const context = buildValueEmitContext(records2, manifest);
80780
+ return new Map(
80781
+ roots.map(({ member, valueId }) => [
80782
+ valueId,
80783
+ emitStoredConstructorExpression(context, member, valueId)
80784
+ ])
80785
+ );
80786
+ }
80787
+ function emitStoredConstructorExpression(context, member, valueId) {
80788
+ const value = context.values.get(valueId);
80789
+ if (value === void 0) {
80790
+ throw new Error(`Stored member value ${valueId} was not pulled.`);
80791
+ }
80792
+ const resolvedMember = resolveGenericValueMember(
80793
+ context,
80794
+ member,
80795
+ declaringClassGenericEnvironment(context, member)
80796
+ );
80797
+ if (numberField(resolvedMember, "kind") !== 7 /* Class */) {
80798
+ throw new Error(
80799
+ `Stored construction ${valueId} belongs to a non-Class member.`
80800
+ );
80801
+ }
80802
+ const classId = stringOrNull(value.classId) ?? stringField3(resolvedMember, "classId");
80803
+ const schemaClass2 = context.classes.get(classId);
80804
+ if (schemaClass2 === void 0) {
80805
+ throw new Error(`Unknown value class ${classId}.`);
80806
+ }
80807
+ const storedEnvironment = valueGenericEnvironment(
80808
+ value,
80809
+ instanceGenericEnvironment(context, classId, resolvedMember, void 0)
80810
+ );
80811
+ const targetValueIds = animationConstructorProjectionTargetValueIds(
80812
+ context,
80813
+ classId,
80814
+ value
80815
+ );
80816
+ const environment = inferAnimationChildOverrideEmitEnvironment(
80817
+ context,
80818
+ classId,
80819
+ targetValueIds,
80820
+ storedEnvironment
80821
+ );
80822
+ const className = classValueTypeName(
80823
+ context,
80824
+ classId,
80825
+ resolvedMember,
80826
+ environment
80827
+ );
80828
+ const constructor2 = storedConstructorCallSource(
80829
+ context,
80830
+ schemaClass2,
80831
+ value,
80832
+ className,
80833
+ environment,
80834
+ /* @__PURE__ */ new Set([valueId]),
80835
+ false
80836
+ );
80837
+ if (constructor2 === null) {
80838
+ throw new Error(
80839
+ `Stored materialized value ${valueId} has no constructor arguments.`
80840
+ );
80841
+ }
80842
+ return constructor2;
80843
+ }
80844
+ function animationConstructorProjectionTargetValueIds(context, classId, value) {
80845
+ if (context.manifestClasses.get(classId)?.system?.worldKind !== "animationChildOverride" || !isObjectRecord2(value.value)) {
80846
+ return [];
80847
+ }
80848
+ const targetIds = [];
80849
+ for (const projection of inheritedConstructorProjections2(
80850
+ context.manifestClasses,
80851
+ classId
80852
+ )) {
80853
+ const schemaKey = inheritedProjectionSchemaKey(
80854
+ context.manifestClasses,
80855
+ classId,
80856
+ projection.memberId
80857
+ );
80858
+ const childValueId = schemaKey === null ? void 0 : value.value[schemaKey];
80859
+ const childValue = typeof childValueId === "string" ? context.values.get(childValueId) : void 0;
80860
+ const projectedMember = context.members.get(projection.memberId);
80861
+ if (childValue !== void 0 && projectedMember !== void 0 && numberField(projectedMember, "kind") === 9 /* Lookup */ && Array.isArray(childValue.value) && typeof childValue.value[0] === "string") {
80862
+ targetIds.push(childValue.value[0]);
80863
+ }
80864
+ }
80865
+ return targetIds;
79306
80866
  }
79307
80867
  function rowBackedDefaultBody(member, isPulledValueRow) {
79308
80868
  if (member.isStatic === true) return null;
@@ -79393,7 +80953,11 @@ function emitMemberDefaultSourcesV4(records2, manifest) {
79393
80953
  );
79394
80954
  context.localizedTextIds.clear();
79395
80955
  }
79396
- return { initializers, recordKeysByMember };
80956
+ return {
80957
+ initializers,
80958
+ recordKeysByMember,
80959
+ materializedConstructors: context.materializedConstructors
80960
+ };
79397
80961
  }
79398
80962
  function qualifiedProjectFileSymbolsV4(records2) {
79399
80963
  const symbols = projectFileSymbols(records2);
@@ -83053,6 +84617,9 @@ function classValue(context, member, value, visited, targetTyped, outerEnvironme
83053
84617
  visited,
83054
84618
  targetTyped
83055
84619
  );
84620
+ if (storedConstruction !== null && typeof value.id === "string") {
84621
+ context.materializedConstructors.set(value.id, storedConstruction);
84622
+ }
83056
84623
  const replayConstruction = storedConstruction === null ? null : storedConstructorCallSource(
83057
84624
  context,
83058
84625
  schemaClass2,
@@ -84340,7 +85907,15 @@ function emitProjectDocumentFilesV4(records2) {
84340
85907
  };
84341
85908
  });
84342
85909
  const supplementalFiles = emitSupplementalProjectSourcesV4(records2);
84343
- const rootFile = emitProjectRootSourceV4(records2, manifest);
85910
+ const materializedConstructors = new Map([
85911
+ ...staticValues.materializedConstructors,
85912
+ ...memberDefaults.materializedConstructors
85913
+ ]);
85914
+ const rootFile = emitProjectRootSourceV4(
85915
+ records2,
85916
+ manifest,
85917
+ materializedConstructors
85918
+ );
84344
85919
  const dialogueFiles = emitDialogueProjectSourcesV4(records2);
84345
85920
  const files = [
84346
85921
  ...source.files,
@@ -84379,7 +85954,7 @@ ${errors.map(
84379
85954
  ).join("\n")}`
84380
85955
  );
84381
85956
  }
84382
- return { files, recordFiles, analysis };
85957
+ return { files, recordFiles, analysis, materializedConstructors };
84383
85958
  }
84384
85959
  function relationEndpointExpressions(records2) {
84385
85960
  const ownerByMemberId = /* @__PURE__ */ new Map();
@@ -84447,33 +86022,178 @@ var init_project_documents = __esm({
84447
86022
  });
84448
86023
 
84449
86024
  // src/project-source/project-document-cache.ts
84450
- import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "node:fs";
84451
- import { dirname as dirname2, join as join3 } from "node:path";
84452
- function writeProjectSourceAnalysisCacheV4(root, analysis) {
84453
- const file = join3(root, PROJECT_SOURCE_ANALYSIS_V4_CACHE_PATH);
84454
- mkdirSync3(dirname2(file), { recursive: true });
84455
- writeFileSync3(file, `${JSON.stringify(analysis, null, 2)}
86025
+ import { mkdirSync as mkdirSync5, readFileSync as readFileSync5, renameSync as renameSync3, writeFileSync as writeFileSync5 } from "node:fs";
86026
+ import { createHash as createHash5 } from "node:crypto";
86027
+ import { dirname as dirname4, join as join5 } from "node:path";
86028
+ function readProjectSourceAnalysisBuildCacheV4(root, sources) {
86029
+ try {
86030
+ const parsed = JSON.parse(
86031
+ readFileSync5(join5(root, PROJECT_SOURCE_BUILD_CACHE_PATH), "utf8")
86032
+ );
86033
+ if (parsed === null || typeof parsed !== "object") return null;
86034
+ const cache = parsed;
86035
+ if (cache.revision !== PROJECT_SOURCE_BUILD_CACHE_REVISION || cache.fingerprint !== projectSourceFingerprint(sources)) {
86036
+ return null;
86037
+ }
86038
+ assertProjectSourceAnalysisV4(cache.analysis);
86039
+ return cache.analysis;
86040
+ } catch {
86041
+ return null;
86042
+ }
86043
+ }
86044
+ function writeProjectSourceAnalysisCacheV4(root, analysis, sources) {
86045
+ const file = join5(root, PROJECT_SOURCE_ANALYSIS_V4_CACHE_PATH);
86046
+ mkdirSync5(dirname4(file), { recursive: true });
86047
+ writeFileSync5(file, `${JSON.stringify(analysis, null, 2)}
84456
86048
  `, "utf8");
86049
+ if (sources === void 0) return;
86050
+ const buildFile = join5(root, PROJECT_SOURCE_BUILD_CACHE_PATH);
86051
+ mkdirSync5(dirname4(buildFile), { recursive: true });
86052
+ const temporary = `${buildFile}.${process.pid}.tmp`;
86053
+ writeFileSync5(
86054
+ temporary,
86055
+ `${JSON.stringify({
86056
+ revision: PROJECT_SOURCE_BUILD_CACHE_REVISION,
86057
+ fingerprint: projectSourceFingerprint(sources),
86058
+ analysis
86059
+ })}
86060
+ `,
86061
+ "utf8"
86062
+ );
86063
+ renameSync3(temporary, buildFile);
86064
+ }
86065
+ function compileProjectSourceAnalysisWithBuildCacheV4(root, sources) {
86066
+ const parsedDocuments = readProjectSourceDocumentBuildCacheV1(root, sources);
86067
+ let documents;
86068
+ try {
86069
+ const analysis = compileNeoProjectSources(sources, {
86070
+ parsedDocuments,
86071
+ onDocuments: (result) => {
86072
+ documents = result;
86073
+ }
86074
+ });
86075
+ if (documents !== void 0) {
86076
+ writeProjectSourceDocumentBuildCacheV1(root, sources, documents);
86077
+ }
86078
+ return analysis;
86079
+ } catch (error) {
86080
+ if (parsedDocuments.size === 0) throw error;
86081
+ documents = void 0;
86082
+ const analysis = compileNeoProjectSources(sources, {
86083
+ onDocuments: (result) => {
86084
+ documents = result;
86085
+ }
86086
+ });
86087
+ if (documents !== void 0) {
86088
+ writeProjectSourceDocumentBuildCacheV1(root, sources, documents);
86089
+ }
86090
+ return analysis;
86091
+ }
86092
+ }
86093
+ function readProjectSourceDocumentBuildCacheV1(root, sources) {
86094
+ try {
86095
+ const parsed = JSON.parse(
86096
+ readFileSync5(
86097
+ join5(root, PROJECT_SOURCE_DOCUMENT_BUILD_CACHE_PATH),
86098
+ "utf8"
86099
+ )
86100
+ );
86101
+ if (parsed === null || typeof parsed !== "object") return /* @__PURE__ */ new Map();
86102
+ const cache = parsed;
86103
+ if (cache.revision !== PROJECT_SOURCE_DOCUMENT_BUILD_CACHE_REVISION || cache.compilerRevision !== NEOSCRIPT_COMPILER_REVISION || cache.files === null || typeof cache.files !== "object" || Array.isArray(cache.files)) {
86104
+ return /* @__PURE__ */ new Map();
86105
+ }
86106
+ const files = cache.files;
86107
+ const documents = /* @__PURE__ */ new Map();
86108
+ for (const source of sources) {
86109
+ const cached = files[source.uri];
86110
+ if (cached?.fingerprint !== projectSourceFileFingerprint(source) || !isCachedProjectSourceDocument(cached.document, source)) {
86111
+ continue;
86112
+ }
86113
+ documents.set(source.uri, cached.document);
86114
+ }
86115
+ return documents;
86116
+ } catch {
86117
+ return /* @__PURE__ */ new Map();
86118
+ }
86119
+ }
86120
+ function writeProjectSourceDocumentBuildCacheV1(root, sources, documents) {
86121
+ const file = join5(root, PROJECT_SOURCE_DOCUMENT_BUILD_CACHE_PATH);
86122
+ mkdirSync5(dirname4(file), { recursive: true });
86123
+ const temporary = `${file}.${process.pid}.tmp`;
86124
+ writeFileSync5(
86125
+ temporary,
86126
+ `${JSON.stringify({
86127
+ revision: PROJECT_SOURCE_DOCUMENT_BUILD_CACHE_REVISION,
86128
+ compilerRevision: NEOSCRIPT_COMPILER_REVISION,
86129
+ files: Object.fromEntries(
86130
+ sources.flatMap((source) => {
86131
+ const document = documents.get(source.uri);
86132
+ return document === void 0 ? [] : [
86133
+ [
86134
+ source.uri,
86135
+ {
86136
+ fingerprint: projectSourceFileFingerprint(source),
86137
+ document
86138
+ }
86139
+ ]
86140
+ ];
86141
+ })
86142
+ )
86143
+ })}
86144
+ `,
86145
+ "utf8"
86146
+ );
86147
+ renameSync3(temporary, file);
86148
+ }
86149
+ function projectSourceFileFingerprint(source) {
86150
+ return createHash5("sha256").update(String(PROJECT_SOURCE_DOCUMENT_BUILD_CACHE_REVISION)).update("\0").update(String(NEOSCRIPT_COMPILER_REVISION)).update("\0").update(source.kind).update("\0").update(source.text).digest("hex");
86151
+ }
86152
+ function isCachedProjectSourceDocument(value, source) {
86153
+ if (value === null || typeof value !== "object") return false;
86154
+ const document = value;
86155
+ return document.kind === source.kind && document.sourceText === source.text && Array.isArray(document.declarations) && Array.isArray(document.diagnostics);
86156
+ }
86157
+ function projectSourceFingerprint(sources) {
86158
+ const hash = createHash5("sha256");
86159
+ hash.update(String(PROJECT_SOURCE_BUILD_CACHE_REVISION));
86160
+ hash.update("\0");
86161
+ hash.update(String(NEOSCRIPT_COMPILER_REVISION));
86162
+ for (const source of sources) {
86163
+ hash.update("\0");
86164
+ hash.update(source.kind);
86165
+ hash.update("\0");
86166
+ hash.update(source.uri);
86167
+ hash.update("\0");
86168
+ hash.update(source.text);
86169
+ }
86170
+ return hash.digest("hex");
84457
86171
  }
86172
+ var PROJECT_SOURCE_BUILD_CACHE_PATH, PROJECT_SOURCE_BUILD_CACHE_REVISION, PROJECT_SOURCE_DOCUMENT_BUILD_CACHE_PATH, PROJECT_SOURCE_DOCUMENT_BUILD_CACHE_REVISION;
84458
86173
  var init_project_document_cache = __esm({
84459
86174
  "src/project-source/project-document-cache.ts"() {
84460
86175
  "use strict";
86176
+ init_src();
84461
86177
  init_project_documents();
86178
+ PROJECT_SOURCE_BUILD_CACHE_PATH = ".neo/build/project-source-analysis-v4.json";
86179
+ PROJECT_SOURCE_BUILD_CACHE_REVISION = 1;
86180
+ PROJECT_SOURCE_DOCUMENT_BUILD_CACHE_PATH = ".neo/build/project-source-documents-v1.json";
86181
+ PROJECT_SOURCE_DOCUMENT_BUILD_CACHE_REVISION = 1;
84462
86182
  }
84463
86183
  });
84464
86184
 
84465
86185
  // src/project-source/project-files.ts
84466
- import { createHash as createHash2 } from "node:crypto";
86186
+ import { createHash as createHash6 } from "node:crypto";
84467
86187
  import {
84468
86188
  existsSync as existsSync3,
84469
- mkdirSync as mkdirSync4,
84470
- readFileSync as readFileSync3,
86189
+ mkdirSync as mkdirSync6,
86190
+ readFileSync as readFileSync6,
84471
86191
  readdirSync,
84472
- renameSync,
86192
+ renameSync as renameSync4,
84473
86193
  rmSync,
84474
- writeFileSync as writeFileSync4
86194
+ writeFileSync as writeFileSync6
84475
86195
  } from "node:fs";
84476
- import { basename, dirname as dirname3, extname, join as join4, relative, sep } from "node:path";
86196
+ import { basename, dirname as dirname5, extname, join as join6, relative, sep } from "node:path";
84477
86197
  function lowerProjectFileRegistrySourcesV4(root, state, analysis, options = {}) {
84478
86198
  const templateIds = fileTemplateIdsByName2(analysis);
84479
86199
  const declarations = analysis.files.registries.flatMap(
@@ -84504,7 +86224,7 @@ function lowerProjectFileRegistrySourcesV4(root, state, analysis, options = {})
84504
86224
  seenSymbols.set(symbolKey, declaration);
84505
86225
  const baseState = state[`project-file:${declaration.recordId}`];
84506
86226
  const base = isObjectRecord2(baseState?.data) ? baseState.data : null;
84507
- const absolute = join4(root, normalizedPath);
86227
+ const absolute = join6(root, normalizedPath);
84508
86228
  const trustedPending = options.trustedPendingFiles?.get(
84509
86229
  declaration.recordId
84510
86230
  );
@@ -84621,7 +86341,7 @@ function inspectProjectBinaryStatusV4(root, state, analysis) {
84621
86341
  const result = explicit.map((declaration) => {
84622
86342
  const baseState = state[`project-file:${declaration.recordId}`];
84623
86343
  const data = isObjectRecord2(baseState?.data) ? baseState.data : {};
84624
- const absolute = join4(root, declaration.path);
86344
+ const absolute = join6(root, declaration.path);
84625
86345
  const local = existsSync3(absolute) ? inspectBinaryFile(absolute, declaration.path) : null;
84626
86346
  const baseSha256 = baseState?.projectBinary?.sha256 ?? normalizeSha256V4(
84627
86347
  data.contentSha256,
@@ -84693,7 +86413,7 @@ function discoverProjectBinariesV4(root, explicit, ignoredPaths = []) {
84693
86413
  );
84694
86414
  const candidates = [];
84695
86415
  for (const directory of ["Files/Images", "Files/AudioClips"]) {
84696
- const absoluteDirectory = join4(root, directory);
86416
+ const absoluteDirectory = join6(root, directory);
84697
86417
  if (!existsSync3(absoluteDirectory)) continue;
84698
86418
  visitBinaryFiles(absoluteDirectory, (absolute) => {
84699
86419
  const path = normalizeSlash2(relative(root, absolute));
@@ -84710,10 +86430,10 @@ function discoverProjectBinariesV4(root, explicit, ignoredPaths = []) {
84710
86430
  }).sort((left, right) => compareCodePoints(left.path, right.path));
84711
86431
  }
84712
86432
  function sha256Bytes(bytes) {
84713
- return createHash2("sha256").update(bytes).digest("hex");
86433
+ return createHash6("sha256").update(bytes).digest("hex");
84714
86434
  }
84715
86435
  function sha256File(path) {
84716
- return sha256Bytes(readFileSync3(path));
86436
+ return sha256Bytes(readFileSync6(path));
84717
86437
  }
84718
86438
  function planBinaryMergeV4(input) {
84719
86439
  const { baseDigest, localDigest, remoteDigest, declarationPresent } = input;
@@ -84744,17 +86464,17 @@ function writeVerifiedBinaryDownloadV4(destination, bytes, expectedSha256) {
84744
86464
  `Downloaded project file checksum ${actual} did not match expected SHA-256 ${expectedSha256}.`
84745
86465
  );
84746
86466
  }
84747
- mkdirSync4(dirname3(destination), { recursive: true });
86467
+ mkdirSync6(dirname5(destination), { recursive: true });
84748
86468
  const temporary = `${destination}.neo-download-${process.pid}`;
84749
86469
  try {
84750
- writeFileSync4(temporary, bytes);
84751
- renameSync(temporary, destination);
86470
+ writeFileSync6(temporary, bytes);
86471
+ renameSync4(temporary, destination);
84752
86472
  } finally {
84753
86473
  rmSync(temporary, { force: true });
84754
86474
  }
84755
86475
  }
84756
86476
  function writeBinaryConflictArtifactV4(root, fileId, fileName2, bytes, expectedSha256) {
84757
- const destination = join4(
86477
+ const destination = join6(
84758
86478
  root,
84759
86479
  ".neo",
84760
86480
  "conflicts",
@@ -84906,7 +86626,7 @@ function inspectBinaryFile(absolute, path) {
84906
86626
  throw new Error(
84907
86627
  `Unsupported project binary extension ${JSON.stringify(extname(path))} at ${path}.`
84908
86628
  );
84909
- const bytes = readFileSync3(absolute);
86629
+ const bytes = readFileSync6(absolute);
84910
86630
  return {
84911
86631
  path: normalizeSlash2(path),
84912
86632
  kind: type.kind,
@@ -84920,7 +86640,7 @@ function visitBinaryFiles(directory, visit) {
84920
86640
  (a, b) => compareCodePoints(a.name, b.name)
84921
86641
  )) {
84922
86642
  if (entry.isSymbolicLink()) continue;
84923
- const path = join4(directory, entry.name);
86643
+ const path = join6(directory, entry.name);
84924
86644
  if (entry.isDirectory()) visitBinaryFiles(path, visit);
84925
86645
  else if (entry.isFile() && SUPPORTED_BINARY_TYPES.has(extname(entry.name).toLowerCase()))
84926
86646
  visit(path);
@@ -84994,15 +86714,15 @@ var init_supplemental_records_file_system = __esm({
84994
86714
  });
84995
86715
 
84996
86716
  // src/project-source/workspace-status.ts
84997
- import { existsSync as existsSync4, readFileSync as readFileSync4, readdirSync as readdirSync2 } from "node:fs";
84998
- import { join as join5, relative as relative2, sep as sep2 } from "node:path";
86717
+ import { existsSync as existsSync4, readFileSync as readFileSync7, readdirSync as readdirSync2 } from "node:fs";
86718
+ import { join as join7, relative as relative2, sep as sep2 } from "node:path";
84999
86719
  function listProjectSourceFilesV4(root) {
85000
86720
  const files = [];
85001
86721
  const visit = (directory) => {
85002
86722
  if (!existsSync4(directory)) return;
85003
86723
  for (const entry of readdirSync2(directory, { withFileTypes: true })) {
85004
86724
  if (entry.isSymbolicLink()) continue;
85005
- const path = join5(directory, entry.name);
86725
+ const path = join7(directory, entry.name);
85006
86726
  if (entry.isDirectory()) {
85007
86727
  if (IGNORED_SCHEMA_DIRECTORIES2.has(entry.name)) continue;
85008
86728
  visit(path);
@@ -85017,12 +86737,14 @@ function listProjectSourceFilesV4(root) {
85017
86737
  function computeWorkspaceStatus2(workspace, options = {}) {
85018
86738
  const virtualSourceFiles = options.virtualSourceFiles ?? listProjectSourceFilesV4(workspace.root).map((path) => ({
85019
86739
  path: relative2(workspace.root, path).split(sep2).join("/"),
85020
- content: readFileSync4(path, "utf8")
86740
+ content: readFileSync7(path, "utf8")
85021
86741
  }));
85022
86742
  return computeWorkspaceStatus(workspace, {
85023
86743
  ...options,
85024
86744
  virtualSourceFiles,
85025
86745
  writeProjectAnalysisCache: options.writeProjectAnalysisCache ?? writeProjectSourceAnalysisCacheV4,
86746
+ readProjectAnalysisCache: options.readProjectAnalysisCache ?? readProjectSourceAnalysisBuildCacheV4,
86747
+ compileProjectAnalysis: options.compileProjectAnalysis ?? compileProjectSourceAnalysisWithBuildCacheV4,
85026
86748
  inspectProjectBinaries: inspectProjectBinaryStatusV4,
85027
86749
  lowerSupplementalRecords: (state, analysis, trustedPendingFiles) => lowerSupplementalProjectSourcesV4(workspace.root, state, analysis, {
85028
86750
  ...trustedPendingFiles === void 0 ? {} : { trustedPendingFiles }
@@ -85048,6 +86770,111 @@ var init_workspace_status = __esm({
85048
86770
  }
85049
86771
  });
85050
86772
 
86773
+ // src/project-source/status-output.ts
86774
+ function groupProjectStatusChangesV4(status) {
86775
+ const groups = /* @__PURE__ */ new Map();
86776
+ for (const change of status.changes) {
86777
+ const reconstructed3 = status.reconstructed.get(
86778
+ recordStateKey(change.recordKind, change.recordId)
86779
+ );
86780
+ const source = reconstructed3?.sourceSpan?.path ?? change.file ?? "<unplaced>";
86781
+ const entries = groups.get(source) ?? [];
86782
+ entries.push(change);
86783
+ groups.set(source, entries);
86784
+ }
86785
+ return [...groups].sort(([left], [right]) => compareCodePoints(left, right)).map(([source, changes]) => ({ source, changes }));
86786
+ }
86787
+ function projectStatusJsonV4(status, options) {
86788
+ return {
86789
+ conflictedFiles: status.conflictedFiles,
86790
+ // P49 §5. Warnings ride the same channel with `blocking: false`, so an
86791
+ // agent reading this envelope sees a rule that has not been promoted yet
86792
+ // without having to know which codes those are.
86793
+ diagnostics: [...status.parseErrors, ...status.parseWarnings].map(
86794
+ (error) => ({
86795
+ path: error.file,
86796
+ line: error.line,
86797
+ column: error.column,
86798
+ code: error.code ?? null,
86799
+ severity: error.severity,
86800
+ blocking: isBlockingSchemaSourceError(error),
86801
+ message: error.message
86802
+ })
86803
+ ),
86804
+ records: status.changes.map(
86805
+ (change) => recordChangeJsonV4(change, status, options)
86806
+ ),
86807
+ files: (status.binaryChanges ?? []).map((binary) => ({
86808
+ fileId: binary.fileId,
86809
+ symbol: binary.symbol,
86810
+ path: binary.path,
86811
+ kind: binary.kind,
86812
+ action: binary.action,
86813
+ digests: {
86814
+ baseSha256: binary.baseSha256,
86815
+ localSha256: binary.localSha256,
86816
+ remoteSha256: binary.remoteSha256
86817
+ },
86818
+ byteLength: binary.byteLength,
86819
+ mimeType: binary.mimeType,
86820
+ uploadIntent: binary.action === "create" || binary.action === "upload" ? {
86821
+ operation: binary.action === "create" ? "create" : "replace",
86822
+ contentSha256: binary.localSha256,
86823
+ byteLength: binary.byteLength,
86824
+ mimeType: binary.mimeType
86825
+ } : null,
86826
+ conflictArtifactPath: binary.conflictArtifactPath ?? null
86827
+ }))
86828
+ };
86829
+ }
86830
+ function recordChangeJsonV4(change, status, options) {
86831
+ const reconstructed3 = status.reconstructed.get(
86832
+ recordStateKey(change.recordKind, change.recordId)
86833
+ );
86834
+ const sourceSpan = reconstructed3?.sourceSpan ?? (reconstructed3 === void 0 ? change.file === null ? null : pointSpan(change.file, 1) : pointSpan(reconstructed3.file, reconstructed3.line));
86835
+ const semanticData = change.nextData ?? change.baseData;
86836
+ const result = {
86837
+ operation: change.kind,
86838
+ recordKind: change.recordKind,
86839
+ recordId: change.recordId,
86840
+ baseContentHash: change.baseContentHash ?? null,
86841
+ expectedBaseContentHash: change.casBaseHash ?? null,
86842
+ sourceSpan,
86843
+ placement: placementJsonV4(semanticData)
86844
+ };
86845
+ if (options.includeRecordData) {
86846
+ result.baseData = change.baseData ?? null;
86847
+ result.nextData = change.nextData ?? null;
86848
+ }
86849
+ return result;
86850
+ }
86851
+ function placementJsonV4(value) {
86852
+ if (!isObjectRecord2(value)) return null;
86853
+ const placement = {};
86854
+ for (const field of [
86855
+ "classId",
86856
+ "containerId",
86857
+ "mapKey",
86858
+ "genericBindings"
86859
+ ]) {
86860
+ if (value[field] !== void 0) placement[field] = value[field];
86861
+ }
86862
+ return Object.keys(placement).length === 0 ? null : placement;
86863
+ }
86864
+ function pointSpan(path, oneBasedLine) {
86865
+ const point = { line: Math.max(0, oneBasedLine - 1), character: 0 };
86866
+ return { path, start: point, end: point };
86867
+ }
86868
+ var init_status_output = __esm({
86869
+ "src/project-source/status-output.ts"() {
86870
+ "use strict";
86871
+ init_workspace();
86872
+ init_projection();
86873
+ init_source_diagnostics();
86874
+ init_source_format();
86875
+ }
86876
+ });
86877
+
85051
86878
  // ../convex/_generated/api.js
85052
86879
  var api_exports = {};
85053
86880
  __export(api_exports, {
@@ -85562,36 +87389,36 @@ var init_conflict_markers = __esm({
85562
87389
  // src/project-source/reset.ts
85563
87390
  import {
85564
87391
  existsSync as existsSync5,
85565
- mkdirSync as mkdirSync5,
85566
- readFileSync as readFileSync5,
87392
+ mkdirSync as mkdirSync7,
87393
+ readFileSync as readFileSync8,
85567
87394
  readdirSync as readdirSync3,
85568
87395
  rmSync as rmSync2,
85569
- writeFileSync as writeFileSync5,
87396
+ writeFileSync as writeFileSync7,
85570
87397
  statSync
85571
87398
  } from "node:fs";
85572
- import { dirname as dirname4, join as join6 } from "node:path";
87399
+ import { dirname as dirname6, join as join8 } from "node:path";
85573
87400
  function resetWorkspaceToProjectSourcesV4(workspace, document, options = {}) {
85574
87401
  const emissionRecords = options.regenerateSourceNames ? regenerateDialogueSourceNamesV4(document.records) : document.records;
85575
87402
  const emitted = emitProjectDocumentFilesV4(emissionRecords);
85576
87403
  assertUniqueEmittedPaths2(emitted.files);
85577
87404
  const previous = managedFilesBeforeReset(workspace.root);
85578
87405
  for (const directory of FORMAT_4_MANAGED_DIRECTORIES) {
85579
- rmSync2(join6(workspace.root, directory), { recursive: true, force: true });
87406
+ rmSync2(join8(workspace.root, directory), { recursive: true, force: true });
85580
87407
  }
85581
- rmSync2(join6(workspace.root, "Scripts"), { recursive: true, force: true });
87408
+ rmSync2(join8(workspace.root, "Scripts"), { recursive: true, force: true });
85582
87409
  for (const file of LEGACY_ROOT_FILES) {
85583
- rmSync2(join6(workspace.root, file), { force: true });
87410
+ rmSync2(join8(workspace.root, file), { force: true });
85584
87411
  }
85585
87412
  for (const privatePath of LEGACY_PRIVATE_PATHS) {
85586
- rmSync2(join6(workspace.root, privatePath), { recursive: true, force: true });
87413
+ rmSync2(join8(workspace.root, privatePath), { recursive: true, force: true });
85587
87414
  }
85588
87415
  for (const file of emitted.files) {
85589
- const absolute = join6(workspace.root, file.path);
85590
- mkdirSync5(dirname4(absolute), { recursive: true });
85591
- if (existsSync5(absolute) && readFileSync5(absolute, "utf8") === file.content) {
87416
+ const absolute = join8(workspace.root, file.path);
87417
+ mkdirSync7(dirname6(absolute), { recursive: true });
87418
+ if (existsSync5(absolute) && readFileSync8(absolute, "utf8") === file.content) {
85592
87419
  continue;
85593
87420
  }
85594
- writeFileSync5(absolute, file.content, "utf8");
87421
+ writeFileSync7(absolute, file.content, "utf8");
85595
87422
  }
85596
87423
  writeProjectSourceAnalysisCacheV4(workspace.root, emitted.analysis);
85597
87424
  const records2 = {};
@@ -85607,11 +87434,16 @@ function resetWorkspaceToProjectSourcesV4(workspace, document, options = {}) {
85607
87434
  };
85608
87435
  }
85609
87436
  workspace.state.records = records2;
87437
+ writeMaterializedConstructionBuildCacheV1(
87438
+ workspace.root,
87439
+ workspace.state,
87440
+ emitted.materializedConstructors
87441
+ );
85610
87442
  writeWorkspaceState(workspace.root, workspace.state);
85611
87443
  workspace.config = { ...workspace.config, formatVersion: 4 };
85612
87444
  writeWorkspaceConfig(workspace.root, workspace.config);
85613
87445
  const removed = [...previous].filter(
85614
- (file) => !existsSync5(join6(workspace.root, file))
87446
+ (file) => !existsSync5(join8(workspace.root, file))
85615
87447
  ).length;
85616
87448
  return {
85617
87449
  written: emitted.files.length,
@@ -85638,7 +87470,7 @@ function managedFilesBeforeReset(root) {
85638
87470
  collectFiles(root, directory, files);
85639
87471
  }
85640
87472
  for (const file of LEGACY_ROOT_FILES) {
85641
- if (existsSync5(join6(root, file))) files.add(file);
87473
+ if (existsSync5(join8(root, file))) files.add(file);
85642
87474
  }
85643
87475
  for (const privatePath of LEGACY_PRIVATE_PATHS) {
85644
87476
  collectFiles(root, privatePath, files);
@@ -85646,7 +87478,7 @@ function managedFilesBeforeReset(root) {
85646
87478
  return files;
85647
87479
  }
85648
87480
  function collectFiles(root, path, files) {
85649
- const absolute = join6(root, path);
87481
+ const absolute = join8(root, path);
85650
87482
  if (!existsSync5(absolute)) return;
85651
87483
  if (statSync(absolute).isFile()) {
85652
87484
  files.add(path);
@@ -85669,6 +87501,7 @@ var init_reset = __esm({
85669
87501
  init_workspace();
85670
87502
  init_project_documents();
85671
87503
  init_project_document_cache();
87504
+ init_materialized_construction_cache();
85672
87505
  init_dialogue_sources();
85673
87506
  FORMAT_4_MANAGED_DIRECTORIES = [
85674
87507
  "Classes",
@@ -85810,7 +87643,7 @@ var init_http = __esm({
85810
87643
 
85811
87644
  // src/project-source/project-file-pull.ts
85812
87645
  import { existsSync as existsSync6, rmSync as rmSync3 } from "node:fs";
85813
- import { join as join7 } from "node:path";
87646
+ import { join as join9 } from "node:path";
85814
87647
  async function pullProjectBinariesV4(args) {
85815
87648
  let client = args.client ?? null;
85816
87649
  const localById = new Map(
@@ -85832,7 +87665,7 @@ async function pullProjectBinariesV4(args) {
85832
87665
  const localStatus = localById.get(record3.recordId);
85833
87666
  const declarationPresent = args.destructive === true || args.localBinaries === void 0 || previous === void 0 || localStatus !== void 0;
85834
87667
  const path = localStatus?.path ?? previous?.projectBinary?.path ?? canonicalProjectBinaryPathV42(record3.data);
85835
- const absolute = join7(args.workspace.root, path);
87668
+ const absolute = join9(args.workspace.root, path);
85836
87669
  const localDigest = existsSync6(absolute) ? sha256File(absolute) : null;
85837
87670
  const baseDigest = previous?.projectBinary?.sha256 ?? readSha256(previous?.data) ?? null;
85838
87671
  const remoteDigest = requiredSha256(
@@ -85915,7 +87748,7 @@ async function pullProjectBinariesV4(args) {
85915
87748
  if (args.document.records.has(key)) continue;
85916
87749
  const localStatus = localById.get(previous.recordId);
85917
87750
  const path = localStatus?.path ?? previous.projectBinary?.path ?? canonicalProjectBinaryPathV42(previous.data);
85918
- const absolute = join7(args.workspace.root, path);
87751
+ const absolute = join9(args.workspace.root, path);
85919
87752
  const localDigest = existsSync6(absolute) ? sha256File(absolute) : null;
85920
87753
  const baseDigest = previous.projectBinary?.sha256 ?? readSha256(previous.data) ?? null;
85921
87754
  const action = planBinaryMergeV4({
@@ -86008,7 +87841,7 @@ function fileName(data) {
86008
87841
  }
86009
87842
  function removePreviousConflict(root, state) {
86010
87843
  if (state?.conflict?.artifactPath === void 0) return;
86011
- rmSync3(join7(root, state.conflict.artifactPath), { force: true });
87844
+ rmSync3(join9(root, state.conflict.artifactPath), { force: true });
86012
87845
  }
86013
87846
  var init_project_file_pull = __esm({
86014
87847
  "src/project-source/project-file-pull.ts"() {
@@ -86070,13 +87903,13 @@ __export(pull_exports, {
86070
87903
  runPull: () => runPull
86071
87904
  });
86072
87905
  import {
86073
- mkdirSync as mkdirSync6,
86074
- writeFileSync as writeFileSync6,
87906
+ mkdirSync as mkdirSync8,
87907
+ writeFileSync as writeFileSync8,
86075
87908
  rmSync as rmSync4,
86076
87909
  existsSync as existsSync7,
86077
- readFileSync as readFileSync6
87910
+ readFileSync as readFileSync9
86078
87911
  } from "node:fs";
86079
- import { dirname as dirname5, join as join8 } from "node:path";
87912
+ import { dirname as dirname7, join as join10 } from "node:path";
86080
87913
  async function runPull(workspace, options) {
86081
87914
  if (options.reset) {
86082
87915
  await runResetPull(workspace);
@@ -86367,12 +88200,12 @@ async function finishFormat4Pull(args) {
86367
88200
  versionId: workspace.config.versionId
86368
88201
  }) : local;
86369
88202
  if (content === void 0) continue;
86370
- const absolute = join8(workspace.root, path);
86371
- mkdirSync6(dirname5(absolute), { recursive: true });
86372
- const existing = existsSync7(absolute) ? readFileSync6(absolute, "utf8") : null;
88203
+ const absolute = join10(workspace.root, path);
88204
+ mkdirSync8(dirname7(absolute), { recursive: true });
88205
+ const existing = existsSync7(absolute) ? readFileSync9(absolute, "utf8") : null;
86373
88206
  if (existing !== null && !rewritePaths.has(path)) continue;
86374
88207
  if (existing !== content) {
86375
- writeFileSync6(absolute, content, "utf8");
88208
+ writeFileSync8(absolute, content, "utf8");
86376
88209
  written += 1;
86377
88210
  }
86378
88211
  }
@@ -86380,7 +88213,7 @@ async function finishFormat4Pull(args) {
86380
88213
  for (const recordState of Object.values(workspace.state.records)) {
86381
88214
  const previousPath = recordState.file;
86382
88215
  if (previousPath === void 0 || emittedPaths.has(previousPath)) continue;
86383
- const absolute = join8(workspace.root, previousPath);
88216
+ const absolute = join10(workspace.root, previousPath);
86384
88217
  if (existsSync7(absolute)) {
86385
88218
  rmSync4(absolute);
86386
88219
  removed += 1;
@@ -86443,6 +88276,14 @@ async function finishFormat4Pull(args) {
86443
88276
  writeWorkspaceState(workspace.root, workspace.state);
86444
88277
  if (conflictCount === 0) {
86445
88278
  writeProjectSourceAnalysisCacheV4(workspace.root, localResult.analysis);
88279
+ writeMaterializedConstructionBuildCacheV1(
88280
+ workspace.root,
88281
+ workspace.state,
88282
+ // The cache is a baseline for workspace.state, not the merged authored
88283
+ // tree. Retaining a local constructor edit here would bless it as the
88284
+ // pulled construction and bypass the P61 recreate-conflict check.
88285
+ serverResult.materializedConstructors
88286
+ );
86446
88287
  }
86447
88288
  const summary = [
86448
88289
  `Pulled ${document.records.size} records`,
@@ -86583,6 +88424,7 @@ var init_pull = __esm({
86583
88424
  init_reset();
86584
88425
  init_project_documents();
86585
88426
  init_project_document_cache();
88427
+ init_materialized_construction_cache();
86586
88428
  init_project_manifest();
86587
88429
  init_project_documents();
86588
88430
  init_project_file_pull();
@@ -86596,8 +88438,8 @@ var init_exports = {};
86596
88438
  __export(init_exports, {
86597
88439
  runInit: () => runInit
86598
88440
  });
86599
- import { existsSync as existsSync8, mkdirSync as mkdirSync7, readFileSync as readFileSync7, writeFileSync as writeFileSync7 } from "node:fs";
86600
- import { join as join9, resolve as resolve2 } from "node:path";
88441
+ import { existsSync as existsSync8, mkdirSync as mkdirSync9, readFileSync as readFileSync10, writeFileSync as writeFileSync9 } from "node:fs";
88442
+ import { join as join11, resolve as resolve2 } from "node:path";
86601
88443
  async function runInit(options) {
86602
88444
  let projectId = options.projectId;
86603
88445
  let projects = [];
@@ -86694,10 +88536,10 @@ async function runInit(options) {
86694
88536
  }) : "neo";
86695
88537
  }
86696
88538
  const root = resolve2(directory);
86697
- if (existsSync8(join9(root, NEO_CONFIG_FILE))) {
86698
- throw new Error(`"${join9(root, NEO_CONFIG_FILE)}" already exists.`);
88539
+ if (existsSync8(join11(root, NEO_CONFIG_FILE))) {
88540
+ throw new Error(`"${join11(root, NEO_CONFIG_FILE)}" already exists.`);
86699
88541
  }
86700
- mkdirSync7(root, { recursive: true });
88542
+ mkdirSync9(root, { recursive: true });
86701
88543
  ensurePrivateStateIgnored(root);
86702
88544
  const config = {
86703
88545
  formatVersion: CURRENT_FORMAT_VERSION,
@@ -86720,14 +88562,14 @@ async function runInit(options) {
86720
88562
  });
86721
88563
  }
86722
88564
  function ensurePrivateStateIgnored(root) {
86723
- const path = join9(root, ".gitignore");
86724
- const existing = existsSync8(path) ? readFileSync7(path, "utf8") : "";
88565
+ const path = join11(root, ".gitignore");
88566
+ const existing = existsSync8(path) ? readFileSync10(path, "utf8") : "";
86725
88567
  if (existing.split(/\r?\n/u).some((line) => line.trim() === ".neo/" || line.trim() === ".neo")) {
86726
88568
  return;
86727
88569
  }
86728
88570
  const prefix = existing.length === 0 || existing.endsWith("\n") ? existing : `${existing}
86729
88571
  `;
86730
- writeFileSync7(path, `${prefix}.neo/
88572
+ writeFileSync9(path, `${prefix}.neo/
86731
88573
  `, "utf8");
86732
88574
  }
86733
88575
  var init_init = __esm({
@@ -87311,14 +89153,14 @@ __export(scaffold_exports, {
87311
89153
  import { randomUUID as randomUUID2 } from "node:crypto";
87312
89154
  import {
87313
89155
  existsSync as existsSync9,
87314
- mkdirSync as mkdirSync8,
87315
- readFileSync as readFileSync8,
89156
+ mkdirSync as mkdirSync10,
89157
+ readFileSync as readFileSync11,
87316
89158
  readdirSync as readdirSync4,
87317
- renameSync as renameSync2,
89159
+ renameSync as renameSync5,
87318
89160
  rmSync as rmSync5,
87319
- writeFileSync as writeFileSync8
89161
+ writeFileSync as writeFileSync10
87320
89162
  } from "node:fs";
87321
- import { dirname as dirname6, isAbsolute, join as join10, relative as relative3, resolve as resolve3, sep as sep3 } from "node:path";
89163
+ import { dirname as dirname8, isAbsolute, join as join12, relative as relative3, resolve as resolve3, sep as sep3 } from "node:path";
87322
89164
  function runClassNew(workspace, name, isAbstract) {
87323
89165
  assertFormat4(workspace);
87324
89166
  assertIdentifier4(name, "Class name");
@@ -87542,7 +89384,7 @@ function loadValidSources(workspace) {
87542
89384
  return {
87543
89385
  uri,
87544
89386
  kind: requiredSourceKind(uri),
87545
- text: readFileSync8(absolute, "utf8")
89387
+ text: readFileSync11(absolute, "utf8")
87546
89388
  };
87547
89389
  });
87548
89390
  const analysis = analyzeNeoProjectSources(inputs);
@@ -87993,18 +89835,18 @@ function commitExistingSourceEdits(root, originals, planned) {
87993
89835
  const staged = [];
87994
89836
  try {
87995
89837
  for (const [uri, content] of planned) {
87996
- const target = join10(root, uri);
89838
+ const target = join12(root, uri);
87997
89839
  const original = originals.get(uri);
87998
89840
  if (original === void 0)
87999
89841
  throw new Error(`Source ${uri} was not loaded.`);
88000
- if (readFileSync8(target, "utf8") !== original) {
89842
+ if (readFileSync11(target, "utf8") !== original) {
88001
89843
  throw new Error(
88002
89844
  `${uri} changed while the scaffold was being prepared; retry the command.`
88003
89845
  );
88004
89846
  }
88005
89847
  const temporary = `${target}.neo-scaffold-${nonce}.tmp`;
88006
89848
  const backup = `${target}.neo-scaffold-${nonce}.bak`;
88007
- writeFileSync8(temporary, content, { encoding: "utf8", flag: "wx" });
89849
+ writeFileSync10(temporary, content, { encoding: "utf8", flag: "wx" });
88008
89850
  staged.push({
88009
89851
  target,
88010
89852
  temporary,
@@ -88014,16 +89856,16 @@ function commitExistingSourceEdits(root, originals, planned) {
88014
89856
  });
88015
89857
  }
88016
89858
  for (const file of staged) {
88017
- renameSync2(file.target, file.backup);
89859
+ renameSync5(file.target, file.backup);
88018
89860
  file.movedOriginal = true;
88019
- renameSync2(file.temporary, file.target);
89861
+ renameSync5(file.temporary, file.target);
88020
89862
  file.installed = true;
88021
89863
  }
88022
89864
  } catch (error) {
88023
89865
  for (const file of [...staged].reverse()) {
88024
89866
  if (file.installed) rmSync5(file.target, { force: true });
88025
89867
  if (file.movedOriginal && existsSync9(file.backup)) {
88026
- renameSync2(file.backup, file.target);
89868
+ renameSync5(file.backup, file.target);
88027
89869
  }
88028
89870
  rmSync5(file.temporary, { force: true });
88029
89871
  rmSync5(file.backup, { force: true });
@@ -88033,15 +89875,15 @@ function commitExistingSourceEdits(root, originals, planned) {
88033
89875
  for (const file of staged) rmSync5(file.backup, { force: true });
88034
89876
  }
88035
89877
  function writeNewSource(root, uri, content) {
88036
- const absolute = join10(root, uri);
88037
- mkdirSync8(dirname6(absolute), { recursive: true });
88038
- writeFileSync8(absolute, content, { encoding: "utf8", flag: "wx" });
89878
+ const absolute = join12(root, uri);
89879
+ mkdirSync10(dirname8(absolute), { recursive: true });
89880
+ writeFileSync10(absolute, content, { encoding: "utf8", flag: "wx" });
88039
89881
  }
88040
89882
  function assertNewPathAvailable(root, uri) {
88041
- const absolute = join10(root, uri);
89883
+ const absolute = join12(root, uri);
88042
89884
  if (existsSync9(absolute))
88043
89885
  throw new Error(`${uri} already exists; no file was changed.`);
88044
- const directory = dirname6(absolute);
89886
+ const directory = dirname8(absolute);
88045
89887
  if (!existsSync9(directory)) return;
88046
89888
  const basename3 = absolute.slice(directory.length + 1);
88047
89889
  const collision = readdirSync4(directory).find(
@@ -88049,7 +89891,7 @@ function assertNewPathAvailable(root, uri) {
88049
89891
  );
88050
89892
  if (collision) {
88051
89893
  throw new Error(
88052
- `${uri} collides case-insensitively with ${join10(relative3(root, directory), collision)}.`
89894
+ `${uri} collides case-insensitively with ${join12(relative3(root, directory), collision)}.`
88053
89895
  );
88054
89896
  }
88055
89897
  }
@@ -88105,9 +89947,9 @@ import {
88105
89947
  constants as fsConstants,
88106
89948
  accessSync,
88107
89949
  existsSync as existsSync10,
88108
- readFileSync as readFileSync9
89950
+ readFileSync as readFileSync12
88109
89951
  } from "node:fs";
88110
- import { extname as extname2, isAbsolute as isAbsolute2, join as join11, relative as relative4, sep as sep4 } from "node:path";
89952
+ import { extname as extname2, isAbsolute as isAbsolute2, join as join13, relative as relative4, sep as sep4 } from "node:path";
88111
89953
  function inspectNeoDoctor(workspace) {
88112
89954
  const formatCompatible = workspace.config.formatVersion === CURRENT_FORMAT_VERSION;
88113
89955
  const compiler = inspectCompilerContract();
@@ -88174,7 +90016,7 @@ function inspectSourceContract(workspace) {
88174
90016
  const inputs = files.map((file, index) => ({
88175
90017
  uri: relativeFiles[index],
88176
90018
  kind: requiredSourceKind2(relativeFiles[index]),
88177
- text: readFileSync9(file, "utf8")
90019
+ text: readFileSync12(file, "utf8")
88178
90020
  }));
88179
90021
  const analysis = compileNeoProjectSources(inputs);
88180
90022
  assertProjectSourceAnalysisV4(analysis);
@@ -88205,7 +90047,7 @@ function inspectSourceContract(workspace) {
88205
90047
  }
88206
90048
  }
88207
90049
  function inspectExtensionContract(root) {
88208
- const cachePath = join11(root, PROJECT_SOURCE_ANALYSIS_V4_CACHE_PATH);
90050
+ const cachePath = join13(root, PROJECT_SOURCE_ANALYSIS_V4_CACHE_PATH);
88209
90051
  if (!existsSync10(cachePath)) {
88210
90052
  return {
88211
90053
  id: NEO_VSCODE_EXTENSION_ID,
@@ -88217,7 +90059,7 @@ function inspectExtensionContract(root) {
88217
90059
  };
88218
90060
  }
88219
90061
  try {
88220
- const value = JSON.parse(readFileSync9(cachePath, "utf8"));
90062
+ const value = JSON.parse(readFileSync12(cachePath, "utf8"));
88221
90063
  assertProjectSourceAnalysisV4(value);
88222
90064
  return {
88223
90065
  id: NEO_VSCODE_EXTENSION_ID,
@@ -88289,7 +90131,7 @@ function inspectTrackedBinary(root, record3, errors) {
88289
90131
  `Project file ${record3.recordId} has an invalid SHA-256 base digest.`
88290
90132
  );
88291
90133
  }
88292
- const absolute = join11(root, path);
90134
+ const absolute = join13(root, path);
88293
90135
  if (existsSync10(absolute) && !canAccess(absolute, fsConstants.R_OK)) {
88294
90136
  errors.push(`Tracked project file ${path} is not readable.`);
88295
90137
  }
@@ -88384,8 +90226,8 @@ var init_doctor = __esm({
88384
90226
  });
88385
90227
 
88386
90228
  // src/commands/push-body-diagnostics.ts
88387
- import { existsSync as existsSync11, readFileSync as readFileSync10 } from "node:fs";
88388
- import { join as join12 } from "node:path";
90229
+ import { existsSync as existsSync11, readFileSync as readFileSync13 } from "node:fs";
90230
+ import { join as join14 } from "node:path";
88389
90231
  function createNeoScriptBodySourceLocator(workspace, status) {
88390
90232
  const textByFile = /* @__PURE__ */ new Map();
88391
90233
  return {
@@ -88407,8 +90249,8 @@ function createNeoScriptBodySourceLocator(workspace, status) {
88407
90249
  textOf(file) {
88408
90250
  const cached = textByFile.get(file);
88409
90251
  if (cached !== void 0) return cached;
88410
- const path = join12(workspace.root, file);
88411
- const text = existsSync11(path) ? readFileSync10(path, "utf8") : null;
90252
+ const path = join14(workspace.root, file);
90253
+ const text = existsSync11(path) ? readFileSync13(path, "utf8") : null;
88412
90254
  textByFile.set(file, text);
88413
90255
  return text;
88414
90256
  }
@@ -89030,7 +90872,7 @@ __export(script_exports, {
89030
90872
  readDocumentArrays: () => readDocumentArrays,
89031
90873
  runScript: () => runScript
89032
90874
  });
89033
- import { readFileSync as readFileSync11 } from "node:fs";
90875
+ import { readFileSync as readFileSync14 } from "node:fs";
89034
90876
  function readDocumentArrays(raw) {
89035
90877
  const arrayOf2 = (field) => {
89036
90878
  const value = raw[field];
@@ -89542,9 +91384,9 @@ function buildRootValue(document) {
89542
91384
  }
89543
91385
  function readSource(options, fallback) {
89544
91386
  if (options.source !== null) return options.source;
89545
- if (options.file !== null) return readFileSync11(options.file, "utf8");
91387
+ if (options.file !== null) return readFileSync14(options.file, "utf8");
89546
91388
  if (fallback !== void 0) return fallback;
89547
- const stdin = readFileSync11(0, "utf8");
91389
+ const stdin = readFileSync14(0, "utf8");
89548
91390
  if (stdin.trim().length === 0) {
89549
91391
  throw new Error(
89550
91392
  "Provide NeoScript source as an argument, --file, or stdin."
@@ -90651,8 +92493,8 @@ __export(migrate_exports, {
90651
92493
  runMigrate: () => runMigrate
90652
92494
  });
90653
92495
  import { randomUUID as randomUUID3 } from "node:crypto";
90654
- import { existsSync as existsSync12, mkdirSync as mkdirSync9, readdirSync as readdirSync5, writeFileSync as writeFileSync9 } from "node:fs";
90655
- import { join as join13 } from "node:path";
92496
+ import { existsSync as existsSync12, mkdirSync as mkdirSync11, readdirSync as readdirSync5, writeFileSync as writeFileSync11 } from "node:fs";
92497
+ import { join as join15 } from "node:path";
90656
92498
  async function runMigrate(workspace, subcommand, positional, targetRef, json, dependencies = {}) {
90657
92499
  if (subcommand === "new") {
90658
92500
  const name = positional[0];
@@ -90661,8 +92503,8 @@ async function runMigrate(workspace, subcommand, positional, targetRef, json, de
90661
92503
  "Usage: neo migrate new <name> [--target <ClassName|project>]"
90662
92504
  );
90663
92505
  }
90664
- const migrationsDir = join13(workspace.root, "Migrations");
90665
- mkdirSync9(migrationsDir, { recursive: true });
92506
+ const migrationsDir = join15(workspace.root, "Migrations");
92507
+ mkdirSync11(migrationsDir, { recursive: true });
90666
92508
  let nextOrder = 1;
90667
92509
  if (existsSync12(migrationsDir)) {
90668
92510
  for (const entry of readdirSync5(migrationsDir)) {
@@ -90673,7 +92515,7 @@ async function runMigrate(workspace, subcommand, positional, targetRef, json, de
90673
92515
  }
90674
92516
  }
90675
92517
  const relPath = migrationFileName(nextOrder, name);
90676
- const absolute = join13(workspace.root, relPath);
92518
+ const absolute = join15(workspace.root, relPath);
90677
92519
  if (existsSync12(absolute)) {
90678
92520
  throw new Error(`"${relPath}" already exists.`);
90679
92521
  }
@@ -90685,7 +92527,7 @@ async function runMigrate(workspace, subcommand, positional, targetRef, json, de
90685
92527
  target === "project" ? "// Runs once with root context. Write the NeoScript action below." : `// Runs once per ${target} value; \`this\` is the instance.`,
90686
92528
  ""
90687
92529
  ].join("\n");
90688
- writeFileSync9(absolute, template, "utf8");
92530
+ writeFileSync11(absolute, template, "utf8");
90689
92531
  console.log(`Created ${relPath} \u2014 edit the action body, then "neo push".`);
90690
92532
  console.log("(The id is assigned at push, same as schema creates.)");
90691
92533
  return;
@@ -93114,7 +94956,7 @@ __export(content_exports, {
93114
94956
  runRecords: () => runRecords,
93115
94957
  runValues: () => runValues
93116
94958
  });
93117
- import { readFileSync as readFileSync12 } from "node:fs";
94959
+ import { readFileSync as readFileSync15 } from "node:fs";
93118
94960
  import { randomUUID as randomUUID4 } from "node:crypto";
93119
94961
  function versionPath(workspace, suffix) {
93120
94962
  return `/api/projects/${workspace.config.projectId}/versions/${workspace.config.versionId}/${suffix}`;
@@ -93122,9 +94964,9 @@ function versionPath(workspace, suffix) {
93122
94964
  function readBatch(file) {
93123
94965
  let raw = null;
93124
94966
  if (file !== null) {
93125
- raw = readFileSync12(file, "utf8");
94967
+ raw = readFileSync15(file, "utf8");
93126
94968
  } else if (!process.stdin.isTTY) {
93127
- raw = readFileSync12(0, "utf8");
94969
+ raw = readFileSync15(0, "utf8");
93128
94970
  if (raw.trim().length === 0) raw = null;
93129
94971
  }
93130
94972
  if (raw === null) return null;
@@ -94146,8 +95988,8 @@ async function uploadProjectFile(context, filePath, preferredTemplateId) {
94146
95988
  const { readFileSync: readFile } = await import("node:fs");
94147
95989
  const { basename: basename3, extname: extname3 } = await import("node:path");
94148
95990
  const bytes = readFile(filePath);
94149
- const { createHash: createHash4 } = await import("node:crypto");
94150
- const contentSha256 = createHash4("sha256").update(bytes).digest("hex");
95991
+ const { createHash: createHash8 } = await import("node:crypto");
95992
+ const contentSha256 = createHash8("sha256").update(bytes).digest("hex");
94151
95993
  const name = basename3(filePath);
94152
95994
  const extension = extname3(filePath).toLowerCase();
94153
95995
  const mimeByExtension = {
@@ -94281,7 +96123,7 @@ async function runFiles(context, subcommand, positional) {
94281
96123
  "Usage: neo files texture-settings <fileId> --file <settings.json>"
94282
96124
  );
94283
96125
  }
94284
- const payload = JSON.parse(readFileSync12(payloadPath, "utf8"));
96126
+ const payload = JSON.parse(readFileSync15(payloadPath, "utf8"));
94285
96127
  const result = await context.client.post(
94286
96128
  versionPath(context.workspace, `files/${fileId}/unity-texture-settings`),
94287
96129
  payload
@@ -94309,8 +96151,8 @@ var export_exports = {};
94309
96151
  __export(export_exports, {
94310
96152
  runExportUnity: () => runExportUnity
94311
96153
  });
94312
- import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync10 } from "node:fs";
94313
- import { join as join14 } from "node:path";
96154
+ import { mkdirSync as mkdirSync12, writeFileSync as writeFileSync12 } from "node:fs";
96155
+ import { join as join16 } from "node:path";
94314
96156
  async function runExportUnity(workspace, outDir) {
94315
96157
  if (outDir === null) {
94316
96158
  throw new Error(
@@ -94322,23 +96164,23 @@ async function runExportUnity(workspace, outDir) {
94322
96164
  `/api/projects/${workspace.config.projectId}/export`,
94323
96165
  { versionId: workspace.config.versionId }
94324
96166
  );
94325
- const resourcesDir = join14(outDir, "Resources", "Neo");
94326
- const localizationDir = join14(resourcesDir, "Localization");
94327
- const scriptsDir = join14(outDir, "Scripts", "Neo");
94328
- mkdirSync10(localizationDir, { recursive: true });
94329
- mkdirSync10(scriptsDir, { recursive: true });
94330
- writeFileSync10(join14(resourcesDir, "project.json"), response.projectJson);
94331
- writeFileSync10(
94332
- join14(scriptsDir, "NeoGeneratedTypes.cs"),
96167
+ const resourcesDir = join16(outDir, "Resources", "Neo");
96168
+ const localizationDir = join16(resourcesDir, "Localization");
96169
+ const scriptsDir = join16(outDir, "Scripts", "Neo");
96170
+ mkdirSync12(localizationDir, { recursive: true });
96171
+ mkdirSync12(scriptsDir, { recursive: true });
96172
+ writeFileSync12(join16(resourcesDir, "project.json"), response.projectJson);
96173
+ writeFileSync12(
96174
+ join16(scriptsDir, "NeoGeneratedTypes.cs"),
94333
96175
  response.generatedTypes
94334
96176
  );
94335
96177
  for (const file of response.localizationFiles ?? []) {
94336
- writeFileSync10(join14(localizationDir, file.fileName), file.content);
96178
+ writeFileSync12(join16(localizationDir, file.fileName), file.content);
94337
96179
  }
94338
- console.log(`wrote ${join14(resourcesDir, "project.json")}`);
94339
- console.log(`wrote ${join14(scriptsDir, "NeoGeneratedTypes.cs")}`);
96180
+ console.log(`wrote ${join16(resourcesDir, "project.json")}`);
96181
+ console.log(`wrote ${join16(scriptsDir, "NeoGeneratedTypes.cs")}`);
94340
96182
  for (const file of response.localizationFiles ?? []) {
94341
- console.log(`wrote ${join14(localizationDir, file.fileName)}`);
96183
+ console.log(`wrote ${join16(localizationDir, file.fileName)}`);
94342
96184
  }
94343
96185
  const diagnostics = response.diagnostics ?? [];
94344
96186
  for (const diagnostic of diagnostics) {
@@ -94356,7 +96198,7 @@ var init_export = __esm({
94356
96198
  });
94357
96199
 
94358
96200
  // ../src/database/project-source-identity.ts
94359
- import { createHash as createHash3 } from "node:crypto";
96201
+ import { createHash as createHash7 } from "node:crypto";
94360
96202
  function hashProjectSourceFiles(inputFiles) {
94361
96203
  const files = normalizeSourceFiles(inputFiles);
94362
96204
  const bytes = Buffer.from(JSON.stringify({ version: 1, files }), "utf8");
@@ -94365,7 +96207,7 @@ function hashProjectSourceFiles(inputFiles) {
94365
96207
  `Project source identity is ${bytes.byteLength} bytes; the limit is ${MAX_SOURCE_BYTES} bytes.`
94366
96208
  );
94367
96209
  }
94368
- return createHash3("sha256").update(bytes).digest("hex");
96210
+ return createHash7("sha256").update(bytes).digest("hex");
94369
96211
  }
94370
96212
  function normalizeSourceFiles(inputFiles) {
94371
96213
  if (inputFiles.length > MAX_SOURCE_FILES) {
@@ -94456,8 +96298,8 @@ var init_project_source_identity = __esm({
94456
96298
  });
94457
96299
 
94458
96300
  // src/project-source/project-file-push.ts
94459
- import { basename as basename2, join as join15 } from "node:path";
94460
- import { readFileSync as readFileSync13 } from "node:fs";
96301
+ import { basename as basename2, join as join17 } from "node:path";
96302
+ import { readFileSync as readFileSync16 } from "node:fs";
94461
96303
  function ensureProjectFileBinaryChangesV4(args) {
94462
96304
  for (const binary of args.binaryChanges) {
94463
96305
  if (binary.action !== "upload") continue;
@@ -94502,8 +96344,8 @@ function prepareProjectFilePushesV4(args) {
94502
96344
  `Project file ${recordId} has upload bytes but its source change has no record data.`
94503
96345
  );
94504
96346
  }
94505
- const absolute = join15(args.workspace.root, binary.path);
94506
- const bytes = new Uint8Array(readFileSync13(absolute));
96347
+ const absolute = join17(args.workspace.root, binary.path);
96348
+ const bytes = new Uint8Array(readFileSync16(absolute));
94507
96349
  const digest = sha256Bytes(bytes);
94508
96350
  if (binary.localSha256 !== null && digest !== binary.localSha256) {
94509
96351
  throw new Error(
@@ -94846,11 +96688,11 @@ function assertProjectFileAuthoringRecordIdentifiers(recordKind, data) {
94846
96688
  if (recordKind !== "unity-texture-template" && recordKind !== "unity-audio-clip-template") {
94847
96689
  return;
94848
96690
  }
94849
- if (!isObjectRecord3(data) || typeof data.name !== "string") return;
96691
+ if (!isObjectRecord4(data) || typeof data.name !== "string") return;
94850
96692
  const label = recordKind === "unity-texture-template" ? "Unity texture template name" : "Unity audio clip template name";
94851
96693
  assertProjectFileAuthoringIdentifier(data.name, label);
94852
96694
  }
94853
- function isObjectRecord3(value) {
96695
+ function isObjectRecord4(value) {
94854
96696
  return typeof value === "object" && value !== null && !Array.isArray(value);
94855
96697
  }
94856
96698
  var init_project_file_authoring_identifiers = __esm({
@@ -94965,18 +96807,17 @@ __export(push_exports, {
94965
96807
  prepareNSPropertySetterChanges: () => prepareNSPropertySetterChanges,
94966
96808
  rewriteFilesFromState: () => rewriteFilesFromState,
94967
96809
  runPush: () => runPush,
94968
- stripServerDerivedNeoScript: () => stripServerDerivedNeoScript,
94969
- workspaceChangesRequireCompleteBodySweep: () => workspaceChangesRequireCompleteBodySweep
96810
+ stripServerDerivedNeoScript: () => stripServerDerivedNeoScript
94970
96811
  });
94971
96812
  import { randomUUID as randomUUID5 } from "node:crypto";
94972
96813
  import {
94973
- mkdirSync as mkdirSync11,
94974
- writeFileSync as writeFileSync11,
96814
+ mkdirSync as mkdirSync13,
96815
+ writeFileSync as writeFileSync13,
94975
96816
  rmSync as rmSync6,
94976
96817
  existsSync as existsSync13,
94977
- readFileSync as readFileSync14
96818
+ readFileSync as readFileSync17
94978
96819
  } from "node:fs";
94979
- import { dirname as dirname7, join as join16, relative as relative5, sep as sep5 } from "node:path";
96820
+ import { dirname as dirname9, join as join18, relative as relative5, sep as sep5 } from "node:path";
94980
96821
  function assignPendingIds(changes, authoredValueSeeds, reconstructed3, localInitializerMaterialization) {
94981
96822
  const assigned = /* @__PURE__ */ new Map();
94982
96823
  const assign = (pendingId2) => {
@@ -95549,7 +97390,9 @@ async function downloadProjectVersionTransactionResult(args) {
95549
97390
  }
95550
97391
  async function preparePushStatus(workspace, options, onPhase = () => void 0) {
95551
97392
  onPhase("Analyzing working copy\u2026");
95552
- const status = computeWorkspaceStatus2(workspace);
97393
+ const status = computeWorkspaceStatus2(workspace, {
97394
+ forceRecompile: options.forceRecompile
97395
+ });
95553
97396
  if (status.conflictedFiles.length > 0) {
95554
97397
  throw new Error(
95555
97398
  `Resolve conflict markers before pushing: ${status.conflictedFiles.join(", ")}`
@@ -95595,18 +97438,15 @@ ${blockingErrors.map((error) => ` ${error.message}`).join("\n")}`
95595
97438
  (change) => change.recordKind === "member" || change.recordKind === "class" || change.recordKind === "enum" || change.recordKind === "interface" || change.nextData !== void 0 && change.recordKind === "migration" && typeof change.nextData.code === "string" && change.nextData.code.trim().length > 0
95596
97439
  );
95597
97440
  const compileSchema = needsNeoScriptCompilation ? await (async () => {
95598
- const completeSweep = workspaceChangesRequireCompleteBodySweep(
95599
- status.changes
95600
- );
95601
97441
  onPhase(
95602
- completeSweep ? "Compiling affected NeoScript across the changed schema\u2026" : `Compiling NeoScript for ${status.changes.length.toLocaleString("en-US")} changed record(s)\u2026`
97442
+ options.forceRecompile === true ? "Compiling every stored NeoScript body\u2026" : `Compiling NeoScript for ${status.changes.length.toLocaleString("en-US")} changed record(s)\u2026`
95603
97443
  );
95604
97444
  return await buildPostPushCompileSchema(workspace, status);
95605
97445
  })() : null;
95606
97446
  const bodySourceLocator = createNeoScriptBodySourceLocator(workspace, status);
95607
97447
  if (compileSchema !== null) {
95608
97448
  prepareCompleteNeoScriptBodyChanges(workspace, status, compileSchema, {
95609
- completeSweep: workspaceChangesRequireCompleteBodySweep(status.changes),
97449
+ completeSweep: options.forceRecompile === true,
95610
97450
  bodySourceLocator
95611
97451
  });
95612
97452
  }
@@ -95670,7 +97510,10 @@ async function runPush(workspace, options, preparationOverride) {
95670
97510
  workspace,
95671
97511
  options.dryRun ? cloneStatusForDryRun(status) : status,
95672
97512
  (label) => preparation?.update(label),
95673
- { fullValidation: options.dryRun }
97513
+ {
97514
+ fullValidation: options.dryRun,
97515
+ forceRecompile: options.forceRecompile === true
97516
+ }
95674
97517
  );
95675
97518
  } catch (error) {
95676
97519
  if (options.dryRun) {
@@ -95829,7 +97672,8 @@ async function runPush(workspace, options, preparationOverride) {
95829
97672
  summary: options.summary ?? "neo push",
95830
97673
  // P43 §4. The local evaluator computes against the pulled snapshot, so
95831
97674
  // the server rejects a push whose base is not the project head.
95832
- headTransactionHash: workspace.state.headTransactionHash ?? null
97675
+ headTransactionHash: workspace.state.headTransactionHash ?? null,
97676
+ forceRecompile: options.forceRecompile === true
95833
97677
  },
95834
97678
  force ? { "x-neo-force-project-version-write": "true" } : void 0
95835
97679
  );
@@ -95842,6 +97686,7 @@ async function runPush(workspace, options, preparationOverride) {
95842
97686
  throw error;
95843
97687
  }
95844
97688
  if (accepted === null) {
97689
+ const committedChangeCount = immediateCommittedChangeCount(response);
95845
97690
  try {
95846
97691
  if (!applyCommittedHeadTransactionHash(workspace, response)) {
95847
97692
  await recoverHeadTransactionHashFromSchemaSignal(workspace);
@@ -95855,14 +97700,19 @@ async function runPush(workspace, options, preparationOverride) {
95855
97700
  type: "project-transaction-progress",
95856
97701
  transactionId: immediateTransactionId(response),
95857
97702
  phase: "committed",
95858
- totalChangeCount: status.changes.length,
95859
- appliedChangeCount: status.changes.length,
97703
+ totalChangeCount: committedChangeCount,
97704
+ appliedChangeCount: committedChangeCount,
95860
97705
  totalChunkCount: null,
95861
97706
  appliedChunkCount: 0,
95862
97707
  errorCode: null,
95863
97708
  errorMessage: null
95864
97709
  });
95865
97710
  reporter.stop();
97711
+ reportAdditionalServerChanges(
97712
+ committedChangeCount,
97713
+ status.changes.length,
97714
+ options.json === true
97715
+ );
95866
97716
  if (options.json !== true) success("Push complete.");
95867
97717
  return true;
95868
97718
  }
@@ -95944,6 +97794,11 @@ async function runPush(workspace, options, preparationOverride) {
95944
97794
  errorMessage: null
95945
97795
  });
95946
97796
  reporter.stop();
97797
+ reportAdditionalServerChanges(
97798
+ completed.totalChangeCount,
97799
+ status.changes.length,
97800
+ options.json === true
97801
+ );
95947
97802
  if (options.json !== true) success("Push complete.");
95948
97803
  return true;
95949
97804
  };
@@ -96034,7 +97889,10 @@ async function runPush(workspace, options, preparationOverride) {
96034
97889
  }
96035
97890
  await finishCommitResponse(result, progress);
96036
97891
  }
96037
- async function prepareLocalPushArtifactsV4(workspace, status, onPhase = () => void 0, options = { fullValidation: true }) {
97892
+ async function prepareLocalPushArtifactsV4(workspace, status, onPhase = () => void 0, options = {
97893
+ fullValidation: true,
97894
+ forceRecompile: false
97895
+ }) {
96038
97896
  onPhase("Hashing canonical project source\u2026");
96039
97897
  const source = createPendingProjectSourceIdentityV4(
96040
97898
  workspace,
@@ -96094,7 +97952,8 @@ async function prepareLocalPushArtifactsV4(workspace, status, onPhase = () => vo
96094
97952
  changes: transportChanges,
96095
97953
  authoredValueSeeds: transportSeeds,
96096
97954
  sourceByRecord: status.reconstructed,
96097
- localInitializerMaterialization: pendingAssignment.localInitializerMaterialization
97955
+ localInitializerMaterialization: pendingAssignment.localInitializerMaterialization,
97956
+ forceRecompile: options.forceRecompile
96098
97957
  });
96099
97958
  }
96100
97959
  return {
@@ -96407,6 +98266,20 @@ function immediateTransactionId(result) {
96407
98266
  if (!isObjectRecord2(result.transaction)) return null;
96408
98267
  return typeof result.transaction.id === "string" ? result.transaction.id : null;
96409
98268
  }
98269
+ function immediateCommittedChangeCount(result) {
98270
+ if (!isObjectRecord2(result) || !Array.isArray(result.changedRecords)) {
98271
+ throw new Error("Transaction response is missing changedRecords.");
98272
+ }
98273
+ return result.changedRecords.length;
98274
+ }
98275
+ function reportAdditionalServerChanges(totalChangeCount, authoredChangeCount, json) {
98276
+ if (json || totalChangeCount <= authoredChangeCount) return;
98277
+ console.log(
98278
+ color.dim(
98279
+ `Server prepared ${totalChangeCount - authoredChangeCount} additional derived record change(s) (${totalChangeCount} total).`
98280
+ )
98281
+ );
98282
+ }
96410
98283
  function applyCommittedProjectTransactionResult(args) {
96411
98284
  const { workspace, result, submittedChanges, candidateAssignments } = args;
96412
98285
  const localStatus = computeWorkspaceStatus2(workspace);
@@ -96735,15 +98608,15 @@ ${finalErrors.map(
96735
98608
  for (const recordState of Object.values(workspace.state.records)) {
96736
98609
  const previousPath = recordState.file;
96737
98610
  if (previousPath === void 0 || emittedPaths.has(previousPath)) continue;
96738
- const absolute = join16(workspace.root, previousPath);
98611
+ const absolute = join18(workspace.root, previousPath);
96739
98612
  if (existsSync13(absolute)) rmSync6(absolute);
96740
98613
  }
96741
98614
  for (const file of files) {
96742
- const absolute = join16(workspace.root, file.path);
96743
- mkdirSync11(dirname7(absolute), { recursive: true });
96744
- const existing = existsSync13(absolute) ? readFileSync14(absolute, "utf8") : null;
98615
+ const absolute = join18(workspace.root, file.path);
98616
+ mkdirSync13(dirname9(absolute), { recursive: true });
98617
+ const existing = existsSync13(absolute) ? readFileSync17(absolute, "utf8") : null;
96745
98618
  if (existing !== file.content)
96746
- writeFileSync11(absolute, file.content, "utf8");
98619
+ writeFileSync13(absolute, file.content, "utf8");
96747
98620
  }
96748
98621
  for (const [key, recordState] of Object.entries(workspace.state.records)) {
96749
98622
  const file = result.recordFiles.get(key);
@@ -96778,7 +98651,7 @@ function materializeAssignedSchemaIdsInAuthoredSource(workspace, replacements, p
96778
98651
  return {
96779
98652
  uri,
96780
98653
  kind,
96781
- text: readFileSync14(absolutePath, "utf8")
98654
+ text: readFileSync17(absolutePath, "utf8")
96782
98655
  };
96783
98656
  }
96784
98657
  );
@@ -97113,7 +98986,7 @@ function compileNSFunctionChange(schema, memberData, locator) {
97113
98986
  return next;
97114
98987
  }
97115
98988
  function prepareCompleteNeoScriptBodyChanges(workspace, status, schema, options = {}) {
97116
- const completeSweep = options.completeSweep ?? true;
98989
+ const completeSweep = options.completeSweep ?? false;
97117
98990
  const locator = options.bodySourceLocator ?? createNeoScriptBodySourceLocator(workspace, status);
97118
98991
  const changesById = new Map(
97119
98992
  status.changes.filter((change) => change.recordKind === "member").map((change) => [change.recordId, change])
@@ -97123,17 +98996,19 @@ function prepareCompleteNeoScriptBodyChanges(workspace, status, schema, options
97123
98996
  if (!completeSweep && !explicitlyChangedMemberIds.has(String(member.id))) {
97124
98997
  return member;
97125
98998
  }
97126
- if (member.kind === 10) {
98999
+ if (member.kind === 10 /* NSProperty */) {
97127
99000
  return compileNSPropertyChange(schema, member, locator);
97128
99001
  }
97129
- if (member.kind === 23) {
99002
+ if (member.kind === 23 /* NSFunction */) {
97130
99003
  return compileNSFunctionChange(schema, member, locator);
97131
99004
  }
97132
99005
  return member;
97133
99006
  });
97134
99007
  schema.members = compiledMembers;
97135
99008
  for (const compiled of compiledMembers) {
97136
- if (compiled.kind !== 10 && compiled.kind !== 23) continue;
99009
+ if (compiled.kind !== 10 /* NSProperty */ && compiled.kind !== 23 /* NSFunction */) {
99010
+ continue;
99011
+ }
97137
99012
  if (typeof compiled.id !== "string") continue;
97138
99013
  const explicit = changesById.get(compiled.id);
97139
99014
  if (explicit !== void 0) {
@@ -97191,22 +99066,6 @@ function mergeCompiledNeoScriptBody(persisted, compiled) {
97191
99066
  }
97192
99067
  return next;
97193
99068
  }
97194
- function workspaceChangesRequireCompleteBodySweep(changes) {
97195
- for (const change of changes) {
97196
- if (change.recordKind === "class" || change.recordKind === "enum" || change.recordKind === "interface") {
97197
- return true;
97198
- }
97199
- if (change.recordKind !== "member") continue;
97200
- if (change.kind !== "update") return true;
97201
- const current = isObjectRecord2(change.baseData) ? change.baseData : null;
97202
- const next = change.nextData;
97203
- if (current === null || next === void 0 || current.kind !== next.kind) {
97204
- return true;
97205
- }
97206
- if (memberNeoScriptContractChanged(current, next)) return true;
97207
- }
97208
- return false;
97209
- }
97210
99069
  function prepareNSPropertySetterChanges(workspace, status) {
97211
99070
  const members = /* @__PURE__ */ new Map();
97212
99071
  for (const record3 of status.reconstructed.values()) {
@@ -97495,11 +99354,11 @@ var init_push = __esm({
97495
99354
  init_project2();
97496
99355
  init_localization2();
97497
99356
  init_world_system_classes();
99357
+ init_members();
97498
99358
  init_project_manifest();
97499
99359
  init_merge();
97500
99360
  init_push_change_intent();
97501
99361
  init_push_body_diagnostics();
97502
- init_neo_script_recompile_scope();
97503
99362
  ({ compileNSAction: compileNSAction2, compileNSFunction: compileNSFunction2, compileNSGetter: compileNSGetter2, compileNSSetter: compileNSSetter2 } = compiler_adapter_exports);
97504
99363
  ProjectTransactionInterruptedError = class extends Error {
97505
99364
  constructor(transactionId) {
@@ -97541,7 +99400,7 @@ __export(dev_exports, {
97541
99400
  runDev: () => runDev
97542
99401
  });
97543
99402
  import { watch } from "node:fs";
97544
- import { join as join17 } from "node:path";
99403
+ import { join as join19 } from "node:path";
97545
99404
  import { emitKeypressEvents } from "node:readline";
97546
99405
  import { ConvexClient } from "convex/browser";
97547
99406
  function isSchemaSignal(value) {
@@ -97651,7 +99510,7 @@ async function runDev(workspace, options) {
97651
99510
  };
97652
99511
  for (const dir of ["Classes", "Enums"]) {
97653
99512
  try {
97654
- watch(join17(workspace.root, dir), { persistent: true }, onFileChange);
99513
+ watch(join19(workspace.root, dir), { persistent: true }, onFileChange);
97655
99514
  } catch {
97656
99515
  }
97657
99516
  }
@@ -97706,15 +99565,15 @@ __export(resolve_exports, {
97706
99565
  runResolve: () => runResolve,
97707
99566
  workspaceFilePath: () => workspaceFilePath
97708
99567
  });
97709
- import { readFileSync as readFileSync15, rmSync as rmSync7, writeFileSync as writeFileSync12 } from "node:fs";
97710
- import { join as join18 } from "node:path";
99568
+ import { readFileSync as readFileSync18, rmSync as rmSync7, writeFileSync as writeFileSync14 } from "node:fs";
99569
+ import { join as join20 } from "node:path";
97711
99570
  function runResolve(workspace, side) {
97712
99571
  let resolvedFiles = 0;
97713
99572
  for (const filePath of listProjectSourceFilesV4(workspace.root)) {
97714
- const source = readFileSync15(filePath, "utf8");
99573
+ const source = readFileSync18(filePath, "utf8");
97715
99574
  if (detectConflictMarkers(source) === null) continue;
97716
99575
  const resolved = resolveMarkers(source, side);
97717
- writeFileSync12(filePath, resolved, "utf8");
99576
+ writeFileSync14(filePath, resolved, "utf8");
97718
99577
  resolvedFiles += 1;
97719
99578
  }
97720
99579
  let resolvedBinaries = 0;
@@ -97722,12 +99581,12 @@ function runResolve(workspace, side) {
97722
99581
  const binary = state.projectBinary;
97723
99582
  const conflict2 = binary?.conflict;
97724
99583
  if (binary === void 0 || conflict2 === void 0) continue;
97725
- const destination = join18(workspace.root, binary.path);
99584
+ const destination = join20(workspace.root, binary.path);
97726
99585
  if (side === "theirs") {
97727
99586
  if (conflict2.remoteSha256 !== null && conflict2.artifactPath !== void 0) {
97728
99587
  writeVerifiedBinaryDownloadV4(
97729
99588
  destination,
97730
- readFileSync15(join18(workspace.root, conflict2.artifactPath)),
99589
+ readFileSync18(join20(workspace.root, conflict2.artifactPath)),
97731
99590
  conflict2.remoteSha256
97732
99591
  );
97733
99592
  binary.sha256 = conflict2.remoteSha256;
@@ -97737,7 +99596,7 @@ function runResolve(workspace, side) {
97737
99596
  }
97738
99597
  }
97739
99598
  if (conflict2.artifactPath !== void 0) {
97740
- rmSync7(join18(workspace.root, conflict2.artifactPath), { force: true });
99599
+ rmSync7(join20(workspace.root, conflict2.artifactPath), { force: true });
97741
99600
  }
97742
99601
  delete binary.conflict;
97743
99602
  resolvedBinaries += 1;
@@ -97790,7 +99649,7 @@ function resolveMarkers(source, side) {
97790
99649
  return output.join("\n");
97791
99650
  }
97792
99651
  function workspaceFilePath(workspace, file) {
97793
- return join18(workspace.root, file);
99652
+ return join20(workspace.root, file);
97794
99653
  }
97795
99654
  var init_resolve = __esm({
97796
99655
  "src/commands/resolve.ts"() {
@@ -97802,451 +99661,8 @@ var init_resolve = __esm({
97802
99661
  }
97803
99662
  });
97804
99663
 
97805
- // src/commands/login.ts
97806
- init_token_store();
97807
- init_ui();
97808
- var CLIENT_ID_BY_PROFILE = {
97809
- editor: "neo-cli-editor",
97810
- release: "neo-cli-release"
97811
- };
97812
- var EDITOR_SCOPES = [
97813
- "openid",
97814
- "profile:read",
97815
- "project:list",
97816
- "project:read",
97817
- "project:details:read",
97818
- "project:version:read",
97819
- "project:version:create",
97820
- "project:version:status:read",
97821
- "project:version:changelog:read",
97822
- "project:record:schema:read",
97823
- "project:record:schema:write",
97824
- "project:record:values:read",
97825
- "project:record:values:write",
97826
- "project:record:world:read",
97827
- "project:record:world:write",
97828
- "project:dialogue:read",
97829
- "project:dialogue:write",
97830
- "project:dialogue:logic:read",
97831
- "project:dialogue:logic:compile",
97832
- "project:files:read",
97833
- "project:files:content:read",
97834
- "project:files:write",
97835
- "project:localization:config:read",
97836
- "project:localization:config:write",
97837
- "project:localization:status:read",
97838
- "project:localization:status:write",
97839
- "project:localization:main-values:read",
97840
- "project:localization:main-values:write",
97841
- "project:localization:values:read",
97842
- "project:localization:values:write",
97843
- "project:localization:export",
97844
- "project:localization:import",
97845
- "project:release-channel:read",
97846
- // `neo export unity` writes project.json + NeoGeneratedTypes.cs headlessly
97847
- // (the escape hatch when game code references not-yet-generated members and
97848
- // a broken compile blocks the in-editor sync).
97849
- "unity:export",
97850
- // Branch lifecycle (auto-archive after `neo merge`, branch archive/restore)
97851
- // is editor work; releases stay gated behind the release profile.
97852
- "project:version:archive",
97853
- "project:version:restore"
97854
- ];
97855
- var RELEASE_SCOPES = [
97856
- ...EDITOR_SCOPES,
97857
- "project:version:status:write",
97858
- "project:release-channel:write",
97859
- "project:release-channel:publish"
97860
- ];
97861
- function isObjectRecord(value) {
97862
- return typeof value === "object" && value !== null;
97863
- }
97864
- async function readStdin() {
97865
- const chunks = [];
97866
- for await (const chunk of process.stdin) {
97867
- chunks.push(Buffer.from(chunk));
97868
- }
97869
- return Buffer.concat(chunks).toString("utf8").trim();
97870
- }
97871
- var sleep = (ms) => new Promise((resolve4) => setTimeout(resolve4, ms));
97872
- async function runLogin(options) {
97873
- let profile = options.profile;
97874
- if (profile === null) {
97875
- profile = isInteractive() && !options.tokenStdin ? await promptSelect({
97876
- message: "Credential profile",
97877
- choices: [
97878
- {
97879
- name: "editor",
97880
- value: "editor",
97881
- description: "Day-to-day schema, content, and branch work (default)"
97882
- },
97883
- {
97884
- name: "release",
97885
- value: "release",
97886
- description: "Adds outward-facing ops: publish, archive, channels"
97887
- }
97888
- ],
97889
- nonInteractiveHint: "Pass --profile editor|release."
97890
- }) : "editor";
97891
- }
97892
- const scopes = [...profile === "release" ? RELEASE_SCOPES : EDITOR_SCOPES];
97893
- if (options.saveProjectId !== null) {
97894
- scopes.push(`project:${options.saveProjectId}:save:read`);
97895
- }
97896
- if (options.tokenStdin) {
97897
- const token = await readStdin();
97898
- if (token.length === 0) {
97899
- throw new Error("--token-stdin received an empty token.");
97900
- }
97901
- saveCredential({
97902
- token,
97903
- profile,
97904
- apiBaseUrl: options.apiBaseUrl,
97905
- scopes,
97906
- savedAt: Date.now()
97907
- });
97908
- console.log(`Stored token for ${options.apiBaseUrl} (${profile}).`);
97909
- return;
97910
- }
97911
- const clientId = CLIENT_ID_BY_PROFILE[profile];
97912
- const codeResponse = await fetch(
97913
- new URL("/api/auth/device/code", options.apiBaseUrl),
97914
- {
97915
- method: "POST",
97916
- headers: { "Content-Type": "application/json" },
97917
- body: JSON.stringify({ client_id: clientId, scope: scopes.join(" ") })
97918
- }
97919
- );
97920
- const codeBody = await codeResponse.json();
97921
- if (!codeResponse.ok) {
97922
- throw new Error(
97923
- `Device authorization request failed (${codeResponse.status}): ${JSON.stringify(codeBody)}`
97924
- );
97925
- }
97926
- if (!isObjectRecord(codeBody)) {
97927
- throw new Error("Device authorization response must be a JSON object.");
97928
- }
97929
- const deviceCode = codeBody.device_code;
97930
- const userCode = codeBody.user_code;
97931
- const verificationUriComplete = codeBody.verification_uri_complete;
97932
- const interval = typeof codeBody.interval === "number" ? codeBody.interval : 5;
97933
- if (typeof deviceCode !== "string") {
97934
- throw new Error('Device authorization response is missing "device_code".');
97935
- }
97936
- if (typeof userCode !== "string") {
97937
- throw new Error('Device authorization response is missing "user_code".');
97938
- }
97939
- console.log("");
97940
- console.log(` To authorize the Neo Compose CLI, open:`);
97941
- console.log(` ${color.cyan(String(verificationUriComplete))}`);
97942
- console.log("");
97943
- console.log(` and confirm this code: ${color.bold(userCode)}`);
97944
- console.log("");
97945
- if (isInteractive() && process.platform === "darwin") {
97946
- note(" (opening your browser\u2026)");
97947
- const { spawn } = await import("node:child_process");
97948
- spawn("open", [String(verificationUriComplete)], {
97949
- stdio: "ignore",
97950
- detached: true
97951
- }).unref();
97952
- }
97953
- const waiting = spinner("Waiting for approval in the browser\u2026");
97954
- const deadline = Date.now() + 15 * 60 * 1e3;
97955
- for (; ; ) {
97956
- if (Date.now() > deadline) {
97957
- waiting.fail("Device authorization timed out after 15 minutes.");
97958
- throw new Error("Device authorization timed out after 15 minutes.");
97959
- }
97960
- await sleep(interval * 1e3);
97961
- const tokenResponse = await fetch(
97962
- new URL("/api/auth/device/token", options.apiBaseUrl),
97963
- {
97964
- method: "POST",
97965
- headers: { "Content-Type": "application/json" },
97966
- body: JSON.stringify({
97967
- grant_type: "urn:ietf:params:oauth:grant-type:device_code",
97968
- device_code: deviceCode,
97969
- client_id: clientId
97970
- })
97971
- }
97972
- );
97973
- const tokenBody = await tokenResponse.json();
97974
- if (!isObjectRecord(tokenBody)) {
97975
- throw new Error("Device token response must be a JSON object.");
97976
- }
97977
- if (!tokenResponse.ok) {
97978
- const errorCode = tokenBody.error;
97979
- if (errorCode === "authorization_pending" || errorCode === "slow_down") {
97980
- continue;
97981
- }
97982
- waiting.fail("Device authorization failed.");
97983
- throw new Error(
97984
- `Device token request failed (${tokenResponse.status}): ${JSON.stringify(tokenBody)}`
97985
- );
97986
- }
97987
- const accessToken = tokenBody.access_token;
97988
- if (typeof accessToken !== "string") {
97989
- throw new Error('Device token response is missing "access_token".');
97990
- }
97991
- saveCredential({
97992
- token: accessToken,
97993
- profile,
97994
- apiBaseUrl: options.apiBaseUrl,
97995
- scopes,
97996
- savedAt: Date.now()
97997
- });
97998
- waiting.succeed(`Logged in to ${options.apiBaseUrl} (${profile} profile).`);
97999
- return;
98000
- }
98001
- }
98002
- async function runWhoami(apiBaseUrl) {
98003
- const { loadToken: loadToken2 } = await Promise.resolve().then(() => (init_token_store(), token_store_exports));
98004
- const token = loadToken2(apiBaseUrl);
98005
- if (token === null) {
98006
- throw new Error(
98007
- `No credentials stored for "${apiBaseUrl}". Run "neo login".`
98008
- );
98009
- }
98010
- const response = await fetch(new URL("/api/auth/get-session", apiBaseUrl), {
98011
- headers: { Authorization: `Bearer ${token}` }
98012
- });
98013
- const body = await response.json();
98014
- if (!response.ok) {
98015
- throw new Error(
98016
- `get-session failed (${response.status}): ${JSON.stringify(body)}`
98017
- );
98018
- }
98019
- if (!isObjectRecord(body) || !isObjectRecord(body.user)) {
98020
- throw new Error("Not signed in (session lookup returned no user).");
98021
- }
98022
- const email = typeof body.user.email === "string" ? body.user.email : "(no email)";
98023
- const name = typeof body.user.name === "string" ? body.user.name : "(no name)";
98024
- console.log(`Signed in as ${name} <${email}> at ${apiBaseUrl}`);
98025
- }
98026
-
98027
- // src/args.ts
98028
- var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
98029
- "dry-run",
98030
- "json",
98031
- "all",
98032
- "force",
98033
- "token-stdin",
98034
- "push",
98035
- "mine",
98036
- "theirs",
98037
- "accept-bump",
98038
- "commit",
98039
- "skip-invalid",
98040
- "migrate",
98041
- "server",
98042
- "replace",
98043
- "reset",
98044
- "regenerate-source-names",
98045
- "generate-ids",
98046
- "help",
98047
- "abstract"
98048
- ]);
98049
- var KNOWN_FLAGS = /* @__PURE__ */ new Set([
98050
- "accept-bump",
98051
- "abstract",
98052
- "all",
98053
- "api",
98054
- "args",
98055
- "member",
98056
- "bump",
98057
- "bind",
98058
- "commit",
98059
- "dir",
98060
- "dry-run",
98061
- "entries",
98062
- "file",
98063
- "force",
98064
- "function",
98065
- "from",
98066
- "generate-ids",
98067
- "group",
98068
- "help",
98069
- "into",
98070
- "json",
98071
- "key",
98072
- "kind",
98073
- "migrate",
98074
- "mine",
98075
- "mode",
98076
- "out",
98077
- "profile",
98078
- "primary",
98079
- "project",
98080
- "plan",
98081
- "push",
98082
- "replace",
98083
- "regenerate-source-names",
98084
- "reset",
98085
- "returns",
98086
- "run",
98087
- "save",
98088
- "save-project",
98089
- "server",
98090
- "skip-invalid",
98091
- "status",
98092
- "summary",
98093
- "confirm-scope",
98094
- "target",
98095
- "template",
98096
- "theirs",
98097
- "this",
98098
- "this-value",
98099
- "token-stdin",
98100
- "class",
98101
- "version"
98102
- ]);
98103
- function parseArgs(argv) {
98104
- const [command = null, ...rest] = argv;
98105
- const flags = /* @__PURE__ */ new Map();
98106
- const positional = [];
98107
- for (let index = 0; index < rest.length; index += 1) {
98108
- const arg = rest[index];
98109
- if (arg.startsWith("--")) {
98110
- const name = arg.slice(2);
98111
- const next = rest[index + 1];
98112
- if (!BOOLEAN_FLAGS.has(name) && next !== void 0 && !next.startsWith("--")) {
98113
- flags.set(name, next);
98114
- index += 1;
98115
- } else {
98116
- flags.set(name, true);
98117
- }
98118
- } else {
98119
- positional.push(arg);
98120
- }
98121
- }
98122
- return { command, flags, positional };
98123
- }
98124
- function stringFlag(args, name) {
98125
- const value = args.flags.get(name);
98126
- if (value === void 0) return null;
98127
- if (typeof value !== "string") {
98128
- throw new Error(`--${name} requires a value.`);
98129
- }
98130
- return value;
98131
- }
98132
- function boolFlag(args, name) {
98133
- return args.flags.get(name) === true;
98134
- }
98135
- function assertKnownFlags(args) {
98136
- for (const name of args.flags.keys()) {
98137
- if (KNOWN_FLAGS.has(name)) continue;
98138
- const hint = args.command === null ? "Run `neo help` for usage." : `Run \`neo ${args.command} --help\` for usage.`;
98139
- throw new Error(`Unknown flag "--${name}". ${hint}`);
98140
- }
98141
- }
98142
-
98143
99664
  // src/main.ts
98144
- init_workspace_status();
98145
- init_workspace();
98146
-
98147
- // src/project-source/status-output.ts
98148
- init_workspace();
98149
- init_projection();
98150
- init_source_diagnostics();
98151
- init_source_format();
98152
- function groupProjectStatusChangesV4(status) {
98153
- const groups = /* @__PURE__ */ new Map();
98154
- for (const change of status.changes) {
98155
- const reconstructed3 = status.reconstructed.get(
98156
- recordStateKey(change.recordKind, change.recordId)
98157
- );
98158
- const source = reconstructed3?.sourceSpan?.path ?? change.file ?? "<unplaced>";
98159
- const entries = groups.get(source) ?? [];
98160
- entries.push(change);
98161
- groups.set(source, entries);
98162
- }
98163
- return [...groups].sort(([left], [right]) => compareCodePoints(left, right)).map(([source, changes]) => ({ source, changes }));
98164
- }
98165
- function projectStatusJsonV4(status, options) {
98166
- return {
98167
- conflictedFiles: status.conflictedFiles,
98168
- // P49 §5. Warnings ride the same channel with `blocking: false`, so an
98169
- // agent reading this envelope sees a rule that has not been promoted yet
98170
- // without having to know which codes those are.
98171
- diagnostics: [...status.parseErrors, ...status.parseWarnings].map(
98172
- (error) => ({
98173
- path: error.file,
98174
- line: error.line,
98175
- column: error.column,
98176
- code: error.code ?? null,
98177
- severity: error.severity,
98178
- blocking: isBlockingSchemaSourceError(error),
98179
- message: error.message
98180
- })
98181
- ),
98182
- records: status.changes.map(
98183
- (change) => recordChangeJsonV4(change, status, options)
98184
- ),
98185
- files: (status.binaryChanges ?? []).map((binary) => ({
98186
- fileId: binary.fileId,
98187
- symbol: binary.symbol,
98188
- path: binary.path,
98189
- kind: binary.kind,
98190
- action: binary.action,
98191
- digests: {
98192
- baseSha256: binary.baseSha256,
98193
- localSha256: binary.localSha256,
98194
- remoteSha256: binary.remoteSha256
98195
- },
98196
- byteLength: binary.byteLength,
98197
- mimeType: binary.mimeType,
98198
- uploadIntent: binary.action === "create" || binary.action === "upload" ? {
98199
- operation: binary.action === "create" ? "create" : "replace",
98200
- contentSha256: binary.localSha256,
98201
- byteLength: binary.byteLength,
98202
- mimeType: binary.mimeType
98203
- } : null,
98204
- conflictArtifactPath: binary.conflictArtifactPath ?? null
98205
- }))
98206
- };
98207
- }
98208
- function recordChangeJsonV4(change, status, options) {
98209
- const reconstructed3 = status.reconstructed.get(
98210
- recordStateKey(change.recordKind, change.recordId)
98211
- );
98212
- const sourceSpan = reconstructed3?.sourceSpan ?? (reconstructed3 === void 0 ? change.file === null ? null : pointSpan(change.file, 1) : pointSpan(reconstructed3.file, reconstructed3.line));
98213
- const semanticData = change.nextData ?? change.baseData;
98214
- const result = {
98215
- operation: change.kind,
98216
- recordKind: change.recordKind,
98217
- recordId: change.recordId,
98218
- baseContentHash: change.baseContentHash ?? null,
98219
- expectedBaseContentHash: change.casBaseHash ?? null,
98220
- sourceSpan,
98221
- placement: placementJsonV4(semanticData)
98222
- };
98223
- if (options.includeRecordData) {
98224
- result.baseData = change.baseData ?? null;
98225
- result.nextData = change.nextData ?? null;
98226
- }
98227
- return result;
98228
- }
98229
- function placementJsonV4(value) {
98230
- if (!isObjectRecord2(value)) return null;
98231
- const placement = {};
98232
- for (const field of [
98233
- "classId",
98234
- "containerId",
98235
- "mapKey",
98236
- "genericBindings"
98237
- ]) {
98238
- if (value[field] !== void 0) placement[field] = value[field];
98239
- }
98240
- return Object.keys(placement).length === 0 ? null : placement;
98241
- }
98242
- function pointSpan(path, oneBasedLine) {
98243
- const point = { line: Math.max(0, oneBasedLine - 1), character: 0 };
98244
- return { path, start: point, end: point };
98245
- }
98246
-
98247
- // src/main.ts
98248
- init_ui();
98249
- var DEFAULT_API_BASE_URL = "https://app.neocompose.com";
99665
+ var main_exports = {};
98250
99666
  function profileFlag(args) {
98251
99667
  const value = stringFlag(args, "profile");
98252
99668
  if (value === null) return null;
@@ -98280,7 +99696,7 @@ ${h("Start")}
98280
99696
  whoami ${d("[--api <url>]")}
98281
99697
 
98282
99698
  ${h("Working copy")}
98283
- pull ${d("[--force|--reset] [--regenerate-source-names]")} push ${d("[--dry-run] [--summary <text>] [--accept-bump] [--json]")}
99699
+ pull ${d("[--force|--reset] [--regenerate-source-names]")} push ${d("[--dry-run] [--force-recompile] [--summary <text>] [--accept-bump] [--json]")}
98284
99700
  status ${d("[--json]")} diff ${d("[--json]")}
98285
99701
  dev ${d("[--push]")} resolve ${d("[--mine|--theirs]")}
98286
99702
  doctor ${d("[--json]")} ${d("validate format/compiler/editor/source/file contracts")}
@@ -98330,10 +99746,11 @@ and tracked project binary from the authoritative server records.
98330
99746
  return `${h("neo push")} \u2014 commit the working copy to the server
98331
99747
 
98332
99748
  ${h("Usage")}
98333
- neo push ${d("[--dry-run] [--summary <text>] [--accept-bump] [--json]")}
99749
+ neo push ${d("[--dry-run] [--force-recompile] [--summary <text>] [--accept-bump] [--json]")}
98334
99750
 
98335
99751
  ${h("Flags")}
98336
99752
  --dry-run ${d("Preview the change set; commit nothing.")}
99753
+ --force-recompile ${d("Exhaustively recompile every stored NeoScript body through the durable server path (parity/recovery; slower, and may surface unrelated invalid bodies).")}
98337
99754
  --summary <text> ${d("Attach a summary message to the transaction.")}
98338
99755
  --accept-bump ${d("Accept a server-required version bump instead of aborting.")}
98339
99756
  --json ${d("Emit machine-readable plan and transaction progress events.")}
@@ -98841,6 +100258,7 @@ async function main() {
98841
100258
  dryRun: boolFlag(args, "dry-run"),
98842
100259
  summary: stringFlag(args, "summary"),
98843
100260
  acceptBump: boolFlag(args, "accept-bump"),
100261
+ forceRecompile: boolFlag(args, "force-recompile"),
98844
100262
  json
98845
100263
  },
98846
100264
  preparation
@@ -98937,11 +100355,29 @@ async function main() {
98937
100355
  process.exitCode = 1;
98938
100356
  }
98939
100357
  }
98940
- main().catch((error) => {
98941
- if (isPromptExit(error)) {
98942
- process.exitCode = 130;
98943
- return;
100358
+ var DEFAULT_API_BASE_URL;
100359
+ var init_main = __esm({
100360
+ "src/main.ts"() {
100361
+ "use strict";
100362
+ init_login();
100363
+ init_args();
100364
+ init_workspace_status();
100365
+ init_workspace();
100366
+ init_status_output();
100367
+ init_ui();
100368
+ DEFAULT_API_BASE_URL = "https://app.neocompose.com";
100369
+ main().catch((error) => {
100370
+ if (isPromptExit(error)) {
100371
+ process.exitCode = 130;
100372
+ return;
100373
+ }
100374
+ console.error(error instanceof Error ? error.message : String(error));
100375
+ process.exitCode = 1;
100376
+ });
98944
100377
  }
98945
- console.error(error instanceof Error ? error.message : String(error));
98946
- process.exitCode = 1;
98947
100378
  });
100379
+
100380
+ // src/entry.ts
100381
+ var invocationCwd = process.env.NEO_CLI_INVOCATION_CWD;
100382
+ if (invocationCwd !== void 0) process.chdir(invocationCwd);
100383
+ await Promise.resolve().then(() => (init_main(), main_exports));