@neocompose/cli 0.21.6 → 0.22.0

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