@neocompose/cli 0.50.0 → 0.50.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.50.2] - 2026-09-12
4
+
5
+ - Export Unity C# as separate files named after their generated types. Keep stable identities in the manifest to preserve Unity GUIDs through renames. Preserve unchanged files and their Unity metadata, remove obsolete owned outputs, and share the integrity manifest with editor synchronization.
6
+
7
+ ## [0.50.1] - 2026-09-12
8
+
9
+ - Reuse schema and symbol indexes while compiling a push, including migration bodies, and compile each changed member body once. Bulk scripted changes no longer rebuild the complete project for every body.
10
+
3
11
  ## [0.50.0] - 2026-09-11
4
12
 
5
13
  - Expose tile placement assets with `@tile(VoidTile)` or `@tile(asset: VoidTile)`. Pull emits the tile class beside each placement; authored bindings resolve to the existing asset metadata.
package/dist/neo.mjs CHANGED
@@ -14506,8 +14506,12 @@ function compileNeoScriptStrict(document, context, options = {}) {
14506
14506
  };
14507
14507
  }
14508
14508
  }
14509
- function assertNeoScriptCompiles(document, context) {
14510
- const result = compileNeoScriptStrict(document, context);
14509
+ function assertNeoScriptCompiles(document, context, projectIndex) {
14510
+ const result = compileNeoScriptStrict(
14511
+ document,
14512
+ context,
14513
+ projectIndex === void 0 ? {} : { projectIndex }
14514
+ );
14511
14515
  const diagnostic = result.diagnostics[0];
14512
14516
  if (diagnostic) {
14513
14517
  throw new CompileError(diagnostic.message, {
@@ -14586,8 +14590,8 @@ function attachDependencyManifest(compiled, syntax, project, context) {
14586
14590
  recordIds.add(alias.valueId);
14587
14591
  collectDependencyIds(alias.type, null, recordIds);
14588
14592
  }
14589
- const typeNames = [...project.typeByName.keys()].filter(
14590
- (name) => identifiers.has(name)
14593
+ const typeNames = [...identifiers].filter(
14594
+ (name) => project.typeByName.has(name)
14591
14595
  );
14592
14596
  return {
14593
14597
  ...compiled,
@@ -33504,6 +33508,7 @@ var init_service = __esm({
33504
33508
  init_test_prelude();
33505
33509
  LanguageService = class {
33506
33510
  documents = /* @__PURE__ */ new Map();
33511
+ specSnapshots = /* @__PURE__ */ new Set();
33507
33512
  projectGeneration = 0;
33508
33513
  cachedProjectAnalysis;
33509
33514
  openDocument(document, context) {
@@ -33513,6 +33518,7 @@ var init_service = __esm({
33513
33518
  );
33514
33519
  }
33515
33520
  const previous = this.documents.get(document.uri);
33521
+ if (previous) this.specSnapshots.delete(previous);
33516
33522
  this.documents.set(document.uri, {
33517
33523
  document,
33518
33524
  context,
@@ -33538,6 +33544,7 @@ var init_service = __esm({
33538
33544
  state.document = document;
33539
33545
  const previousProjectIndex = state.snapshot?.project;
33540
33546
  state.snapshot = void 0;
33547
+ this.specSnapshots.delete(state);
33541
33548
  if (!isProjectDocument(document, state.context) && state.context.projectSourceKind !== "spec") {
33542
33549
  state.snapshot = createSnapshot(
33543
33550
  document,
@@ -33569,6 +33576,7 @@ var init_service = __esm({
33569
33576
  }
33570
33577
  state.context = context;
33571
33578
  state.contextKey = contextKey;
33579
+ this.specSnapshots.delete(state);
33572
33580
  state.snapshot = isProjectDocument(state.document, context) || context.projectSourceKind === "spec" ? void 0 : createSnapshot(state.document, context);
33573
33581
  state.validationGeneration++;
33574
33582
  if (isProjectDocument(state.document, context))
@@ -33577,9 +33585,11 @@ var init_service = __esm({
33577
33585
  closeDocument(uri) {
33578
33586
  const state = this.documents.get(uri);
33579
33587
  this.documents.delete(uri);
33588
+ if (state) this.specSnapshots.delete(state);
33580
33589
  if (state && isProjectDocument(state.document, state.context)) {
33581
33590
  this.invalidateProjectAnalysis();
33582
33591
  }
33592
+ if (this.documents.size === 0) this.cachedProjectAnalysis = void 0;
33583
33593
  }
33584
33594
  hasDocument(uri) {
33585
33595
  return this.documents.has(uri);
@@ -33817,7 +33827,8 @@ var init_service = __esm({
33817
33827
  kind: document.languageId === "neoflow" ? "flow" : context.kind === "migration" ? "migration" : "definition",
33818
33828
  text: document.text
33819
33829
  } : null
33820
- ).filter((input) => input !== null)
33830
+ ).filter((input) => input !== null),
33831
+ this.cachedProjectAnalysis?.analysis.documents
33821
33832
  );
33822
33833
  this.cachedProjectAnalysis = {
33823
33834
  generation: this.projectGeneration,
@@ -33827,11 +33838,8 @@ var init_service = __esm({
33827
33838
  }
33828
33839
  invalidateProjectAnalysis() {
33829
33840
  this.projectGeneration++;
33830
- this.cachedProjectAnalysis = void 0;
33831
- for (const state of this.documents.values()) {
33832
- if (state.context.projectSourceKind === "spec")
33833
- state.snapshot = void 0;
33834
- }
33841
+ for (const state of this.specSnapshots) state.snapshot = void 0;
33842
+ this.specSnapshots.clear();
33835
33843
  }
33836
33844
  projectReferences(document, position, includeDeclaration) {
33837
33845
  const token = projectTokenAt(document, position);
@@ -33883,6 +33891,8 @@ var init_service = __esm({
33883
33891
  project: this.projectAnalysis().project
33884
33892
  }) : state.context
33885
33893
  );
33894
+ if (state.context.projectSourceKind === "spec")
33895
+ this.specSnapshots.add(state);
33886
33896
  return state.snapshot;
33887
33897
  }
33888
33898
  requireState(uri) {
@@ -35355,7 +35365,7 @@ var init_login = __esm({
35355
35365
  "project:localization:export",
35356
35366
  "project:localization:import",
35357
35367
  "project:release-channel:read",
35358
- // `neo export unity` writes project.json + NeoGeneratedTypes.cs headlessly
35368
+ // `neo export unity` writes project.json + generated C# files headlessly
35359
35369
  // (the escape hatch when game code references not-yet-generated members and
35360
35370
  // a broken compile blocks the in-editor sync).
35361
35371
  "unity:export",
@@ -44326,36 +44336,36 @@ var MemberKind;
44326
44336
  var init_member_kind_enum = __esm({
44327
44337
  "../src/models/members/member-kind-enum.ts"() {
44328
44338
  "use strict";
44329
- MemberKind = /* @__PURE__ */ ((MemberKind15) => {
44330
- MemberKind15[MemberKind15["Null"] = 0] = "Null";
44331
- MemberKind15[MemberKind15["Bool"] = 1] = "Bool";
44332
- MemberKind15[MemberKind15["Int"] = 2] = "Int";
44333
- MemberKind15[MemberKind15["String"] = 3] = "String";
44334
- MemberKind15[MemberKind15["Float"] = 4] = "Float";
44335
- MemberKind15[MemberKind15["Dictionary"] = 5] = "Dictionary";
44336
- MemberKind15[MemberKind15["List"] = 6] = "List";
44337
- MemberKind15[MemberKind15["Class"] = 7] = "Class";
44338
- MemberKind15[MemberKind15["Enum"] = 8] = "Enum";
44339
- MemberKind15[MemberKind15["Lookup"] = 9] = "Lookup";
44340
- MemberKind15[MemberKind15["NSProperty"] = 10] = "NSProperty";
44341
- MemberKind15[MemberKind15["Sprite"] = 11] = "Sprite";
44342
- MemberKind15[MemberKind15["Audio"] = 12] = "Audio";
44343
- MemberKind15[MemberKind15["Function"] = 13] = "Function";
44344
- MemberKind15[MemberKind15["Vector2"] = 14] = "Vector2";
44345
- MemberKind15[MemberKind15["Vector2Int"] = 15] = "Vector2Int";
44346
- MemberKind15[MemberKind15["Vector3"] = 16] = "Vector3";
44347
- MemberKind15[MemberKind15["Vector3Int"] = 17] = "Vector3Int";
44348
- MemberKind15[MemberKind15["DialogueLookup"] = 18] = "DialogueLookup";
44349
- MemberKind15[MemberKind15["Color"] = 19] = "Color";
44350
- MemberKind15[MemberKind15["Decimal"] = 20] = "Decimal";
44351
- MemberKind15[MemberKind15["Generic"] = 21] = "Generic";
44352
- MemberKind15[MemberKind15["Interface"] = 22] = "Interface";
44353
- MemberKind15[MemberKind15["NSFunction"] = 23] = "NSFunction";
44354
- MemberKind15[MemberKind15["FunctionRef"] = 24] = "FunctionRef";
44355
- MemberKind15[MemberKind15["NSDelegate"] = 25] = "NSDelegate";
44356
- MemberKind15[MemberKind15["NSAction"] = 26] = "NSAction";
44357
- MemberKind15[MemberKind15["Variant"] = 27] = "Variant";
44358
- return MemberKind15;
44339
+ MemberKind = /* @__PURE__ */ ((MemberKind16) => {
44340
+ MemberKind16[MemberKind16["Null"] = 0] = "Null";
44341
+ MemberKind16[MemberKind16["Bool"] = 1] = "Bool";
44342
+ MemberKind16[MemberKind16["Int"] = 2] = "Int";
44343
+ MemberKind16[MemberKind16["String"] = 3] = "String";
44344
+ MemberKind16[MemberKind16["Float"] = 4] = "Float";
44345
+ MemberKind16[MemberKind16["Dictionary"] = 5] = "Dictionary";
44346
+ MemberKind16[MemberKind16["List"] = 6] = "List";
44347
+ MemberKind16[MemberKind16["Class"] = 7] = "Class";
44348
+ MemberKind16[MemberKind16["Enum"] = 8] = "Enum";
44349
+ MemberKind16[MemberKind16["Lookup"] = 9] = "Lookup";
44350
+ MemberKind16[MemberKind16["NSProperty"] = 10] = "NSProperty";
44351
+ MemberKind16[MemberKind16["Sprite"] = 11] = "Sprite";
44352
+ MemberKind16[MemberKind16["Audio"] = 12] = "Audio";
44353
+ MemberKind16[MemberKind16["Function"] = 13] = "Function";
44354
+ MemberKind16[MemberKind16["Vector2"] = 14] = "Vector2";
44355
+ MemberKind16[MemberKind16["Vector2Int"] = 15] = "Vector2Int";
44356
+ MemberKind16[MemberKind16["Vector3"] = 16] = "Vector3";
44357
+ MemberKind16[MemberKind16["Vector3Int"] = 17] = "Vector3Int";
44358
+ MemberKind16[MemberKind16["DialogueLookup"] = 18] = "DialogueLookup";
44359
+ MemberKind16[MemberKind16["Color"] = 19] = "Color";
44360
+ MemberKind16[MemberKind16["Decimal"] = 20] = "Decimal";
44361
+ MemberKind16[MemberKind16["Generic"] = 21] = "Generic";
44362
+ MemberKind16[MemberKind16["Interface"] = 22] = "Interface";
44363
+ MemberKind16[MemberKind16["NSFunction"] = 23] = "NSFunction";
44364
+ MemberKind16[MemberKind16["FunctionRef"] = 24] = "FunctionRef";
44365
+ MemberKind16[MemberKind16["NSDelegate"] = 25] = "NSDelegate";
44366
+ MemberKind16[MemberKind16["NSAction"] = 26] = "NSAction";
44367
+ MemberKind16[MemberKind16["Variant"] = 27] = "Variant";
44368
+ return MemberKind16;
44359
44369
  })(MemberKind || {});
44360
44370
  }
44361
44371
  });
@@ -53516,18 +53526,27 @@ function storedLeafExpectation(kind) {
53516
53526
  return "canonical decimal string";
53517
53527
  }
53518
53528
  function* storedValueChildReferences(args) {
53529
+ for (const child of storedValueChildEntries(args)) {
53530
+ if (typeof child.value !== "string") continue;
53531
+ yield {
53532
+ memberId: child.memberId,
53533
+ valueId: child.value,
53534
+ pathSuffix: child.pathSuffix
53535
+ };
53536
+ }
53537
+ }
53538
+ function* storedValueChildEntries(args) {
53519
53539
  const entryMemberId = args.member.entryMemberId;
53520
53540
  if (args.member.kind === 6 /* List */ && typeof entryMemberId === "string") {
53521
53541
  const ids = args.member.listKind === 1 /* Unordered */ && args.unorderedValueIds !== void 0 ? args.unorderedValueIds : Array.isArray(args.body) ? args.body : [];
53522
53542
  let pathIndex = 0;
53523
53543
  for (const valueId of ids) {
53524
- if (typeof valueId !== "string") continue;
53525
53544
  yield {
53526
53545
  memberId: entryMemberId,
53527
- valueId,
53546
+ value: valueId,
53528
53547
  pathSuffix: `[${pathIndex}]`
53529
53548
  };
53530
- pathIndex++;
53549
+ if (typeof valueId === "string") pathIndex++;
53531
53550
  }
53532
53551
  return;
53533
53552
  }
@@ -53536,16 +53555,14 @@ function* storedValueChildReferences(args) {
53536
53555
  for (const key in body) {
53537
53556
  if (!Object.hasOwn(body, key)) continue;
53538
53557
  const valueId = body[key];
53539
- if (typeof valueId !== "string") continue;
53540
- yield { memberId: entryMemberId, valueId, pathSuffix: `.${key}` };
53558
+ yield { memberId: entryMemberId, value: valueId, pathSuffix: `.${key}` };
53541
53559
  }
53542
53560
  return;
53543
53561
  }
53544
53562
  if (args.member.kind === 7 /* Class */ && body !== null) {
53545
53563
  for (const { schemaKey, memberId } of args.classFields ?? []) {
53546
53564
  const valueId = body[schemaKey];
53547
- if (typeof valueId !== "string") continue;
53548
- yield { memberId, valueId, pathSuffix: `.${schemaKey}` };
53565
+ yield { memberId, value: valueId, pathSuffix: `.${schemaKey}` };
53549
53566
  }
53550
53567
  }
53551
53568
  }
@@ -68159,25 +68176,31 @@ function clearNeoScriptBodyCompileCache() {
68159
68176
  neoScriptBodyCompileCache.clear();
68160
68177
  }
68161
68178
  function createNeoScriptCompilationProject(ctx) {
68162
- const members = [...ctx.members];
68163
- const membersById = new Map(members.map((member) => [member.id, member]));
68164
- return createNeoScriptProject({
68165
- vm: {
68166
- project: ctx.project,
68167
- projectFiles: [...ctx.projectFiles ?? []],
68168
- members,
68169
- classes: [...ctx.classes],
68170
- enums: [...ctx.enums],
68171
- interfaces: [...ctx.interfaces ?? []],
68172
- databaseVM: {
68173
- memberById: (id2) => membersById.get(id2) ?? null
68174
- }
68175
- },
68179
+ const vm = createAnalyzerProject(ctx);
68180
+ const project = createNeoScriptProject({
68181
+ vm,
68176
68182
  thisClass: null,
68177
68183
  ...ctx.constructors ? { constructors: ctx.constructors } : {},
68178
68184
  ...ctx.variants ? { variants: ctx.variants } : {},
68179
68185
  ...ctx.variantFolders ? { variantFolders: ctx.variantFolders } : {}
68180
68186
  });
68187
+ analyzerProjects.set(project, vm);
68188
+ return project;
68189
+ }
68190
+ function createAnalyzerProject(ctx) {
68191
+ const members = [...ctx.members];
68192
+ const membersById = new Map(members.map((member) => [member.id, member]));
68193
+ return {
68194
+ project: ctx.project,
68195
+ projectFiles: [...ctx.projectFiles ?? []],
68196
+ members,
68197
+ classes: [...ctx.classes],
68198
+ enums: [...ctx.enums],
68199
+ interfaces: [...ctx.interfaces ?? []],
68200
+ databaseVM: {
68201
+ memberById: (id2) => membersById.get(id2) ?? null
68202
+ }
68203
+ };
68181
68204
  }
68182
68205
  function compileNSGetter(code, ctx) {
68183
68206
  return compileStrict(
@@ -68311,7 +68334,8 @@ function compileStrict(code, context) {
68311
68334
  version: 1,
68312
68335
  text: code
68313
68336
  },
68314
- context
68337
+ context,
68338
+ compilationProjectIndex(context.project)
68315
68339
  );
68316
68340
  if ("getter" in compiled) {
68317
68341
  throw new Error(
@@ -68333,21 +68357,17 @@ function compilationProjectIdentity(project) {
68333
68357
  neoScriptProjectIdentityCache.set(project, identity2);
68334
68358
  return identity2;
68335
68359
  }
68360
+ function compilationProjectIndex(project) {
68361
+ const cached = neoScriptProjectIndexes.get(project);
68362
+ if (cached !== void 0) return cached;
68363
+ const index = createProjectIndex(project);
68364
+ neoScriptProjectIndexes.set(project, index);
68365
+ return index;
68366
+ }
68336
68367
  function createContext(ctx, options) {
68337
- const members = [...ctx.members];
68338
- const membersById = new Map(members.map((member) => [member.id, member]));
68368
+ const prepared = ctx.compilationProject === void 0 ? void 0 : analyzerProjects.get(ctx.compilationProject);
68339
68369
  const analyzer = {
68340
- vm: {
68341
- project: ctx.project,
68342
- projectFiles: [...ctx.projectFiles ?? []],
68343
- members,
68344
- classes: [...ctx.classes],
68345
- enums: [...ctx.enums],
68346
- interfaces: [...ctx.interfaces ?? []],
68347
- databaseVM: {
68348
- memberById: (id2) => membersById.get(id2) ?? null
68349
- }
68350
- },
68370
+ vm: prepared ?? createAnalyzerProject(ctx),
68351
68371
  thisClass: ctx.thisClass,
68352
68372
  ...ctx.valueAliases ? { valueAliases: ctx.valueAliases } : {},
68353
68373
  ...ctx.constructors ? { constructors: ctx.constructors } : {},
@@ -68374,7 +68394,7 @@ function createContext(ctx, options) {
68374
68394
  staticMember: ctx.staticMember === true
68375
68395
  };
68376
68396
  }
68377
- var NEOSCRIPT_BODY_COMPILE_CACHE_LIMIT, neoScriptBodyCompileCache, neoScriptProjectIdentityCache, nextNeoScriptProjectIdentity;
68397
+ var NEOSCRIPT_BODY_COMPILE_CACHE_LIMIT, neoScriptBodyCompileCache, neoScriptProjectIdentityCache, neoScriptProjectIndexes, analyzerProjects, nextNeoScriptProjectIdentity;
68378
68398
  var init_compile = __esm({
68379
68399
  "../src/database/neoscript/compile.ts"() {
68380
68400
  "use strict";
@@ -68383,6 +68403,8 @@ var init_compile = __esm({
68383
68403
  NEOSCRIPT_BODY_COMPILE_CACHE_LIMIT = 1024;
68384
68404
  neoScriptBodyCompileCache = /* @__PURE__ */ new Map();
68385
68405
  neoScriptProjectIdentityCache = /* @__PURE__ */ new WeakMap();
68406
+ neoScriptProjectIndexes = /* @__PURE__ */ new WeakMap();
68407
+ analyzerProjects = /* @__PURE__ */ new WeakMap();
68386
68408
  nextNeoScriptProjectIdentity = 1;
68387
68409
  }
68388
68410
  });
@@ -84253,6 +84275,108 @@ var init_project_record_migrations = __esm({
84253
84275
  }
84254
84276
  });
84255
84277
 
84278
+ // ../src/database/member-default-reference-ids.ts
84279
+ async function loadMemberDefaultValueClosure(args) {
84280
+ const classes = args.classes.filter(isNeoSchemaClass);
84281
+ const members = args.members.filter(isAnyMember);
84282
+ const index = projectSchemaIndexFor({ classes, members });
84283
+ const classSchemas = /* @__PURE__ */ new Map();
84284
+ const defaultEnvironments = /* @__PURE__ */ new Map();
84285
+ const defaultEnvironment = (classId) => {
84286
+ let env = defaultEnvironments.get(classId);
84287
+ if (env === void 0) {
84288
+ env = resolveInstanceEnv(classId, void 0, classes);
84289
+ defaultEnvironments.set(classId, env);
84290
+ }
84291
+ return env;
84292
+ };
84293
+ const pending = [];
84294
+ const visited = /* @__PURE__ */ new Set();
84295
+ const closure = /* @__PURE__ */ new Map();
84296
+ const visit = (rawMember, source, ambientEnv) => {
84297
+ if (!isLiteralValueContent(source)) return;
84298
+ if (typeof source.value !== "object" || source.value === null || Object.keys(source.value).length === 0)
84299
+ return;
84300
+ const stamp = "genericBindings" in source ? source.genericBindings : void 0;
84301
+ const withStamp = (base) => {
84302
+ if (typeof stamp !== "object" || stamp === null) return base;
84303
+ const stamped = new Map(base);
84304
+ for (const [parameterId, memberId] of Object.entries(stamp)) {
84305
+ if (typeof memberId === "string")
84306
+ stamped.set(parameterId, { kind: "member", memberId });
84307
+ }
84308
+ return stamped;
84309
+ };
84310
+ let env = withStamp(ambientEnv);
84311
+ const member = substituteMember(rawMember, env, members);
84312
+ let classFields;
84313
+ if (isMemberClassBase(member)) {
84314
+ const classId = source.classId ?? member.classId;
84315
+ classFields = classSchemas.get(classId);
84316
+ if (classFields === void 0) {
84317
+ classFields = mergeStoredInstanceSchema(classId, classes, members);
84318
+ classSchemas.set(classId, classFields);
84319
+ }
84320
+ env = withStamp(
84321
+ member.classArguments == null ? defaultEnvironment(classId) : resolveInstanceEnv(classId, member.classArguments, classes)
84322
+ );
84323
+ }
84324
+ for (const child of storedValueChildEntries({
84325
+ member,
84326
+ body: source.value,
84327
+ classFields
84328
+ })) {
84329
+ const childMember = index.member(child.memberId);
84330
+ if (childMember === void 0) continue;
84331
+ if (typeof child.value === "string")
84332
+ pending.push({ id: child.value, member: childMember, env });
84333
+ else if (isPackedValueEnvelope(child.value))
84334
+ visit(childMember, child.value["~packed"], env);
84335
+ }
84336
+ };
84337
+ for (const member of members) {
84338
+ if (member.defaultValue === void 0) continue;
84339
+ const placement = index.firstSchemaPlacement(member.id);
84340
+ const env = placement === null ? /* @__PURE__ */ new Map() : defaultEnvironment(placement.ownerClass.id);
84341
+ visit(member, member.defaultValue, env);
84342
+ }
84343
+ while (pending.length > 0) {
84344
+ const missing = [];
84345
+ while (pending.length > 0 && missing.length < 128) {
84346
+ const item = pending.pop();
84347
+ if (visited.has(item.id)) continue;
84348
+ visited.add(item.id);
84349
+ const row = args.valuesById.get(item.id);
84350
+ if (row === void 0) missing.push(item);
84351
+ else {
84352
+ closure.set(item.id, row);
84353
+ visit(item.member, args.data(row), item.env);
84354
+ }
84355
+ }
84356
+ if (missing.length === 0) continue;
84357
+ const loaded = await args.loadValues(missing.map((item) => item.id));
84358
+ for (const item of missing) {
84359
+ const row = loaded.get(item.id);
84360
+ if (row === void 0) continue;
84361
+ closure.set(item.id, row);
84362
+ visit(item.member, args.data(row), item.env);
84363
+ }
84364
+ }
84365
+ return closure;
84366
+ }
84367
+ var init_member_default_reference_ids = __esm({
84368
+ "../src/database/member-default-reference-ids.ts"() {
84369
+ "use strict";
84370
+ init_classes();
84371
+ init_inheritance();
84372
+ init_generics();
84373
+ init_members();
84374
+ init_packed_value_encoding();
84375
+ init_stored_value_shape();
84376
+ init_project_schema_index();
84377
+ }
84378
+ });
84379
+
84256
84380
  // ../src/database/project-document-read.ts
84257
84381
  var project_document_read_exports = {};
84258
84382
  __export(project_document_read_exports, {
@@ -84262,6 +84386,7 @@ __export(project_document_read_exports, {
84262
84386
  fetchProjectDocumentRawChunked: () => fetchProjectDocumentRawChunked,
84263
84387
  fetchProjectDocumentRawChunkedOnce: () => fetchProjectDocumentRawChunkedOnce,
84264
84388
  fetchProjectDocumentSnapshots: () => fetchProjectDocumentSnapshots,
84389
+ fetchProjectDocumentValueRecords: () => fetchProjectDocumentValueRecords,
84265
84390
  readArrayField: () => readArrayField,
84266
84391
  readProjectDocument: () => readProjectDocument,
84267
84392
  readProjectDocumentContentHashHeads: () => readProjectDocumentContentHashHeads,
@@ -84339,7 +84464,8 @@ async function fetchProjectDocumentRawChunkedOnce(runner, options = {}) {
84339
84464
  const pageResult = await runner.fetchManifestPage(
84340
84465
  { numItems: DOCUMENT_MANIFEST_PAGE_SIZE, cursor: null },
84341
84466
  mapKey,
84342
- readBase
84467
+ readBase,
84468
+ "omit"
84343
84469
  );
84344
84470
  if (pageResult === null) {
84345
84471
  throw new Error(
@@ -84351,8 +84477,34 @@ async function fetchProjectDocumentRawChunkedOnce(runner, options = {}) {
84351
84477
  await rememberRecords(page.records);
84352
84478
  await fetchRemainingPages(mapKey, page.continueCursor);
84353
84479
  }
84354
- const records2 = [...recordsByKey.values()];
84355
84480
  const snapshotsById = await snapshots.finish();
84481
+ if (options.mapKeys !== void 0 && !options.mapKeys.includes("all")) {
84482
+ const members = [];
84483
+ const classes = [];
84484
+ const valuesById = /* @__PURE__ */ new Map();
84485
+ for (const snapshot of snapshotsById.values()) {
84486
+ if (snapshot.recordKind === "member") members.push(snapshot.data);
84487
+ if (snapshot.recordKind === "class") classes.push(snapshot.data);
84488
+ if (snapshot.recordKind === "value")
84489
+ valuesById.set(snapshot.recordId, snapshot);
84490
+ }
84491
+ const defaults = await loadMemberDefaultValueClosure({
84492
+ classes,
84493
+ members,
84494
+ valuesById,
84495
+ data: (snapshot) => snapshot.data,
84496
+ loadValues: (ids) => fetchProjectDocumentValueRecords(runner, ids, readBase)
84497
+ });
84498
+ for (const snapshot of defaults.values()) {
84499
+ snapshotsById.set(snapshot.id, snapshot);
84500
+ recordsByKey.set(`value ${snapshot.recordId}`, {
84501
+ recordKind: "value",
84502
+ recordId: snapshot.recordId,
84503
+ snapshotId: snapshot.id
84504
+ });
84505
+ }
84506
+ }
84507
+ const records2 = [...recordsByKey.values()];
84356
84508
  await runner.validateReadBase(readBase);
84357
84509
  return assembleProjectDocumentRaw({
84358
84510
  version: metadata.version,
@@ -84379,47 +84531,95 @@ async function fetchProjectDocumentPartitionRecords(runner, mapKey, readBase) {
84379
84531
  );
84380
84532
  }
84381
84533
  const recordsByKey = /* @__PURE__ */ new Map();
84534
+ const snapshots = createProjectDocumentSnapshotLoader(
84535
+ (ids) => fetchScopedSnapshotBatch(runner, ids, readBase)
84536
+ );
84537
+ try {
84538
+ let cursor = null;
84539
+ do {
84540
+ const pageResult = await runner.fetchManifestPage(
84541
+ { numItems: DOCUMENT_MANIFEST_PAGE_SIZE, cursor },
84542
+ mapKey,
84543
+ readBase,
84544
+ "omit"
84545
+ );
84546
+ if (pageResult === null) {
84547
+ throw new Error(
84548
+ `Convex project document manifest returned null for partition "${mapKey}".`
84549
+ );
84550
+ }
84551
+ const page = readProjectDocumentManifestPage(pageResult);
84552
+ assertProjectReadBase(readBase, page.readBase);
84553
+ for (const record4 of page.records) {
84554
+ recordsByKey.set(`${record4.recordKind} ${record4.recordId}`, record4);
84555
+ }
84556
+ await snapshots.enqueue(page.records.map((record4) => record4.snapshotId));
84557
+ cursor = page.continueCursor;
84558
+ } while (cursor !== null);
84559
+ const snapshotsById = await snapshots.finish();
84560
+ await runner.validateReadBase(readBase);
84561
+ return [...recordsByKey.values()].map((record4) => {
84562
+ const snapshot = snapshotsById.get(record4.snapshotId);
84563
+ if (snapshot === void 0) {
84564
+ throw new Error(
84565
+ `Partition "${mapKey}" snapshot "${record4.snapshotId}" referenced by the manifest was not returned by any snapshot batch.`
84566
+ );
84567
+ }
84568
+ return {
84569
+ recordKind: record4.recordKind,
84570
+ recordId: record4.recordId,
84571
+ deleted: false,
84572
+ data: snapshot.data,
84573
+ contentHash: snapshot.contentHash
84574
+ };
84575
+ });
84576
+ } finally {
84577
+ await snapshots.stop();
84578
+ }
84579
+ }
84580
+ async function fetchProjectDocumentValueRecords(runner, valueIds, readBase) {
84581
+ if (runner.fetchValueHeadsPage === void 0) {
84582
+ throw new Error(
84583
+ "Partition-scoped document reads require a value-head reader for member defaults."
84584
+ );
84585
+ }
84586
+ const values = /* @__PURE__ */ new Map();
84382
84587
  let cursor = null;
84383
84588
  do {
84384
- const pageResult = await runner.fetchManifestPage(
84385
- { numItems: DOCUMENT_MANIFEST_PAGE_SIZE, cursor },
84386
- mapKey,
84387
- readBase
84388
- );
84389
- if (pageResult === null) {
84589
+ const page = await runner.fetchValueHeadsPage(valueIds, cursor, readBase);
84590
+ if (!isObject4(page)) throw new Error("Value-head page must be an object.");
84591
+ const revision = readProjectDocumentRevisionMarker({
84592
+ documentRevision: page.documentRevision
84593
+ });
84594
+ if (revision === null)
84595
+ throw new Error("Value-head page has no document revision.");
84596
+ assertProjectReadBase(readBase, revision.readBase);
84597
+ if (!Array.isArray(page.snapshotIds) || !page.snapshotIds.every((id2) => typeof id2 === "string"))
84390
84598
  throw new Error(
84391
- `Convex project document manifest returned null for partition "${mapKey}".`
84599
+ "Value-head page snapshotIds must be an array of strings."
84392
84600
  );
84393
- }
84394
- const page = readProjectDocumentManifestPage(pageResult);
84395
- assertProjectReadBase(readBase, page.readBase);
84396
- for (const record4 of page.records) {
84397
- recordsByKey.set(`${record4.recordKind} ${record4.recordId}`, record4);
84398
- }
84399
- cursor = page.continueCursor;
84400
- } while (cursor !== null);
84401
- const records2 = [...recordsByKey.values()];
84402
- const snapshotIds = [...new Set(records2.map((record4) => record4.snapshotId))];
84403
- const snapshotsById = await fetchProjectDocumentSnapshots(
84404
- (ids) => fetchScopedSnapshotBatch(runner, ids, readBase),
84405
- snapshotIds
84406
- );
84407
- await runner.validateReadBase(readBase);
84408
- return records2.map((record4) => {
84409
- const snapshot = snapshotsById.get(record4.snapshotId);
84410
- if (snapshot === void 0) {
84601
+ if (page.continueCursor !== null && typeof page.continueCursor !== "string")
84411
84602
  throw new Error(
84412
- `Partition "${mapKey}" snapshot "${record4.snapshotId}" referenced by the manifest was not returned by any snapshot batch.`
84603
+ "Value-head page continueCursor must be a string or null."
84413
84604
  );
84605
+ const loaded = await fetchProjectDocumentSnapshots(
84606
+ (ids) => fetchScopedSnapshotBatch(runner, ids, readBase),
84607
+ page.snapshotIds
84608
+ );
84609
+ for (const snapshot of loaded.values()) {
84610
+ if (snapshot.recordKind !== "value")
84611
+ throw new Error("Value-head page returned a non-value snapshot.");
84612
+ if (snapshot.deleted)
84613
+ throw new Error("Value-head page returned a deleted snapshot.");
84614
+ if (!valueIds.includes(snapshot.recordId))
84615
+ throw new Error(
84616
+ `Value-head page returned unrequested value "${snapshot.recordId}".`
84617
+ );
84618
+ values.set(snapshot.recordId, snapshot);
84414
84619
  }
84415
- return {
84416
- recordKind: record4.recordKind,
84417
- recordId: record4.recordId,
84418
- deleted: false,
84419
- data: snapshot.data,
84420
- contentHash: snapshot.contentHash
84421
- };
84422
- });
84620
+ cursor = page.continueCursor;
84621
+ } while (cursor !== null);
84622
+ return values;
84423
84623
  }
84424
84624
  function assertProjectReadBase(expected, actual) {
84425
84625
  if (!projectReadBasesMatch(expected, actual))
@@ -84989,8 +85189,9 @@ var init_project_document_read = __esm({
84989
85189
  init_projectDocumentDialogueMaterialization();
84990
85190
  init_projectRecordBuckets();
84991
85191
  init_project_record_migrations();
85192
+ init_member_default_reference_ids();
84992
85193
  DOCUMENT_MANIFEST_PAGE_SIZE = 2048;
84993
- DOCUMENT_SNAPSHOT_FETCH_CHUNK_SIZE = 512;
85194
+ DOCUMENT_SNAPSHOT_FETCH_CHUNK_SIZE = 128;
84994
85195
  DOCUMENT_SNAPSHOT_FETCH_CONCURRENCY = 4;
84995
85196
  }
84996
85197
  });
@@ -129738,12 +129939,10 @@ ${blockingErrors.map((error) => ` ${error.message}`).join("\n")}`
129738
129939
  return await buildPostPushCompileSchema(workspace, status);
129739
129940
  })() : null;
129740
129941
  const bodySourceLocator = createNeoScriptBodySourceLocator(workspace, status);
129741
- if (compileSchema !== null) {
129742
- prepareCompleteNeoScriptBodyChanges(workspace, status, compileSchema, {
129743
- completeSweep: options.forceRecompile === true,
129744
- bodySourceLocator
129745
- });
129746
- }
129942
+ const compileContext = compileSchema === null ? null : prepareCompleteNeoScriptBodyChanges(workspace, status, compileSchema, {
129943
+ completeSweep: options.forceRecompile === true,
129944
+ bodySourceLocator
129945
+ });
129747
129946
  const now = Date.now();
129748
129947
  for (const change of status.changes) {
129749
129948
  if (change.nextData === void 0) continue;
@@ -129751,26 +129950,12 @@ ${blockingErrors.map((error) => ` ${error.message}`).join("\n")}`
129751
129950
  change.nextData = {
129752
129951
  ...change.nextData,
129753
129952
  action: compileMigrationAction(
129754
- compileSchema,
129953
+ compileContext,
129755
129954
  change.nextData,
129756
129955
  bodySourceLocator
129757
129956
  )
129758
129957
  };
129759
129958
  }
129760
- if (change.recordKind === "member" && change.nextData.kind === 10) {
129761
- change.nextData = compileNSPropertyChange(
129762
- compileSchema,
129763
- change.nextData,
129764
- bodySourceLocator
129765
- );
129766
- }
129767
- if (change.recordKind === "member" && change.nextData.kind === 23) {
129768
- change.nextData = compileNSFunctionChange(
129769
- compileSchema,
129770
- change.nextData,
129771
- bodySourceLocator
129772
- );
129773
- }
129774
129959
  if (change.kind === "create") {
129775
129960
  change.nextData = {
129776
129961
  ...change.nextData,
@@ -131609,8 +131794,8 @@ async function buildPostPushCompileSchema(workspace, status) {
131609
131794
  variantFolders: [...bucket("variant-folder").values()]
131610
131795
  };
131611
131796
  }
131612
- function postPushCompilerProject(schema) {
131613
- return {
131797
+ function createPostPushCompileContext(schema) {
131798
+ const project = {
131614
131799
  project: schema.project,
131615
131800
  projectFiles: schema.projectFiles,
131616
131801
  members: schema.members,
@@ -131621,24 +131806,50 @@ function postPushCompilerProject(schema) {
131621
131806
  variants: schema.variants,
131622
131807
  variantFolders: schema.variantFolders
131623
131808
  };
131809
+ const membersById = /* @__PURE__ */ new Map();
131810
+ const classesById = /* @__PURE__ */ new Map();
131811
+ const ownersByMemberId = /* @__PURE__ */ new Map();
131812
+ for (const member of schema.members) {
131813
+ if (typeof member.id !== "string") continue;
131814
+ if (!membersById.has(member.id)) membersById.set(member.id, member);
131815
+ }
131816
+ for (const schemaClass2 of schema.classes) {
131817
+ if (typeof schemaClass2.id === "string" && !classesById.has(schemaClass2.id)) {
131818
+ classesById.set(schemaClass2.id, schemaClass2);
131819
+ }
131820
+ if (!isObjectRecord2(schemaClass2.schema)) continue;
131821
+ for (const memberId of Object.values(schemaClass2.schema)) {
131822
+ if (typeof memberId !== "string") continue;
131823
+ if (!ownersByMemberId.has(memberId))
131824
+ ownersByMemberId.set(memberId, schemaClass2);
131825
+ }
131826
+ }
131827
+ let compilationProject;
131828
+ return {
131829
+ membersById,
131830
+ classesById,
131831
+ ownersByMemberId,
131832
+ project() {
131833
+ compilationProject ??= createNeoScriptCompilationProject2(project);
131834
+ return { ...project, compilationProject };
131835
+ }
131836
+ };
131624
131837
  }
131625
- function containingClass(schema, memberId) {
131626
- return schema.classes.find((schemaClass2) => {
131627
- const classSchema = schemaClass2.schema;
131628
- return isObjectRecord2(classSchema) && Object.values(classSchema).includes(memberId);
131629
- }) ?? null;
131838
+ function containingClass(context, memberId) {
131839
+ if (typeof memberId !== "string") return null;
131840
+ return context.ownersByMemberId.get(memberId) ?? null;
131630
131841
  }
131631
131842
  function bodyOwnerName(thisClass) {
131632
131843
  if (thisClass === null) return null;
131633
131844
  if (typeof thisClass.name !== "string") return null;
131634
131845
  return thisClass.name;
131635
131846
  }
131636
- function compileNSPropertyChange(schema, memberData, locator) {
131847
+ function compileNSPropertyChange(context, memberData, locator) {
131637
131848
  const memberId = memberData.id;
131638
- const thisClass = containingClass(schema, memberId);
131849
+ const thisClass = containingClass(context, memberId);
131639
131850
  const returnTypeInfo = resolveNSPropertyReturnTypeInfo2(
131640
131851
  memberData,
131641
- schema.members
131852
+ context.membersById
131642
131853
  );
131643
131854
  const bodyIdentity = {
131644
131855
  recordKind: "member",
@@ -131651,7 +131862,7 @@ function compileNSPropertyChange(schema, memberData, locator) {
131651
131862
  const code = memberData.code;
131652
131863
  next.getter = compileNeoScriptBodyOrThrow(
131653
131864
  () => compileNSGetter2(code, {
131654
- ...postPushCompilerProject(schema),
131865
+ ...context.project(),
131655
131866
  thisClass,
131656
131867
  returnTypeInfo,
131657
131868
  dialogueContext: null,
@@ -131668,7 +131879,7 @@ function compileNSPropertyChange(schema, memberData, locator) {
131668
131879
  const setterCode = memberData.setterCode;
131669
131880
  next.setter = compileNeoScriptBodyOrThrow(
131670
131881
  () => compileNSSetter2(setterCode, {
131671
- ...postPushCompilerProject(schema),
131882
+ ...context.project(),
131672
131883
  thisClass,
131673
131884
  valueTypeInfo: returnTypeInfo,
131674
131885
  implicitMemberAccess: true,
@@ -131682,18 +131893,18 @@ function compileNSPropertyChange(schema, memberData, locator) {
131682
131893
  }
131683
131894
  return next;
131684
131895
  }
131685
- function compileNSFunctionChange(schema, memberData, locator) {
131896
+ function compileNSFunctionChange(context, memberData, locator) {
131686
131897
  const next = { ...memberData };
131687
131898
  if (typeof memberData.code !== "string") {
131688
131899
  delete next.action;
131689
131900
  return next;
131690
131901
  }
131691
131902
  const code = memberData.code;
131692
- const contract = resolveNSFunctionContract(memberData, schema.members);
131693
- const thisClass = containingClass(schema, memberData.id);
131903
+ const contract = resolveNSFunctionContract(memberData, context.membersById);
131904
+ const thisClass = containingClass(context, memberData.id);
131694
131905
  next.action = compileNeoScriptBodyOrThrow(
131695
131906
  () => compileNSFunction2(code, {
131696
- ...postPushCompilerProject(schema),
131907
+ ...context.project(),
131697
131908
  thisClass,
131698
131909
  returnTypeInfo: contract.returnTypeInfo,
131699
131910
  argumentTypes: contract.argumentTypes,
@@ -131716,6 +131927,7 @@ function compileNSFunctionChange(schema, memberData, locator) {
131716
131927
  }
131717
131928
  function prepareCompleteNeoScriptBodyChanges(workspace, status, schema, options = {}) {
131718
131929
  const completeSweep = options.completeSweep ?? false;
131930
+ const context = createPostPushCompileContext(schema);
131719
131931
  const locator = options.bodySourceLocator ?? createNeoScriptBodySourceLocator(workspace, status);
131720
131932
  const changesById = new Map(
131721
131933
  status.changes.filter((change) => change.recordKind === "member").map((change) => [change.recordId, change])
@@ -131726,10 +131938,10 @@ function prepareCompleteNeoScriptBodyChanges(workspace, status, schema, options
131726
131938
  return member;
131727
131939
  }
131728
131940
  if (member.kind === 10 /* NSProperty */) {
131729
- return compileNSPropertyChange(schema, member, locator);
131941
+ return compileNSPropertyChange(context, member, locator);
131730
131942
  }
131731
131943
  if (member.kind === 23 /* NSFunction */) {
131732
- return compileNSFunctionChange(schema, member, locator);
131944
+ return compileNSFunctionChange(context, member, locator);
131733
131945
  }
131734
131946
  return member;
131735
131947
  });
@@ -131779,6 +131991,7 @@ function prepareCompleteNeoScriptBodyChanges(workspace, status, schema, options
131779
131991
  status.changes.push(cascadeChange);
131780
131992
  changesById.set(compiled.id, cascadeChange);
131781
131993
  }
131994
+ return context;
131782
131995
  }
131783
131996
  function mergeCompiledNeoScriptBody(persisted, compiled) {
131784
131997
  const next = { ...persisted };
@@ -131975,7 +132188,7 @@ function resolveNSPropertyReturnTypeInfo2(member, members) {
131975
132188
  );
131976
132189
  }
131977
132190
  visited.add(parentId);
131978
- current = members.find((candidate) => candidate.id === parentId);
132191
+ current = members.get(parentId);
131979
132192
  }
131980
132193
  throw new Error(
131981
132194
  `Cannot compile NeoScript property "${String(member.id)}": its returnTypeInfo could not be resolved from the base chain.`
@@ -132002,7 +132215,7 @@ function resolveNSFunctionContract(member, members) {
132002
132215
  );
132003
132216
  }
132004
132217
  visited.add(parentId);
132005
- current = members.find((candidate) => candidate.id === parentId);
132218
+ current = members.get(parentId);
132006
132219
  if (current === void 0) {
132007
132220
  throw new Error(
132008
132221
  `Cannot compile NeoScript function "${String(member.id)}": missing parent member "${parentId}".`
@@ -132020,15 +132233,13 @@ function resolveNSFunctionContract(member, members) {
132020
132233
  deferred: dispatch === 1
132021
132234
  };
132022
132235
  }
132023
- function compileMigrationAction(schema, migrationData, locator) {
132236
+ function compileMigrationAction(context, migrationData, locator) {
132024
132237
  const targetClassId = migrationData.targetClassId;
132025
- const thisClass = typeof targetClassId === "string" ? schema.classes.find(
132026
- (schemaClass2) => schemaClass2.id === targetClassId
132027
- ) ?? null : null;
132238
+ const thisClass = typeof targetClassId === "string" ? context.classesById.get(targetClassId) ?? null : null;
132028
132239
  const code = String(migrationData.code);
132029
132240
  return compileNeoScriptBodyOrThrow(
132030
132241
  () => compileNSVoidBody2(code, {
132031
- ...postPushCompilerProject(schema),
132242
+ ...context.project(),
132032
132243
  thisClass,
132033
132244
  dialogueContext: null,
132034
132245
  migrationContext: true
@@ -132044,7 +132255,7 @@ function compileMigrationAction(schema, migrationData, locator) {
132044
132255
  locator
132045
132256
  );
132046
132257
  }
132047
- var compileNSVoidBody2, compileNSFunction2, compileNSGetter2, compileNSSetter2, ProjectTransactionInterruptedError, ProjectTransactionFailedError, PROJECT_TRANSACTION_POLL_DELAYS_MS, LocalCandidateArtifactError, SERVER_MINTED_PUSH_RECORD_KINDS;
132258
+ var compileNSVoidBody2, compileNSFunction2, compileNSGetter2, compileNSSetter2, createNeoScriptCompilationProject2, ProjectTransactionInterruptedError, ProjectTransactionFailedError, PROJECT_TRANSACTION_POLL_DELAYS_MS, LocalCandidateArtifactError, SERVER_MINTED_PUSH_RECORD_KINDS;
132048
132259
  var init_push = __esm({
132049
132260
  "src/commands/push.ts"() {
132050
132261
  "use strict";
@@ -132090,7 +132301,8 @@ var init_push = __esm({
132090
132301
  compileNSVoidBody: compileNSVoidBody2,
132091
132302
  compileNSFunction: compileNSFunction2,
132092
132303
  compileNSGetter: compileNSGetter2,
132093
- compileNSSetter: compileNSSetter2
132304
+ compileNSSetter: compileNSSetter2,
132305
+ createNeoScriptCompilationProject: createNeoScriptCompilationProject2
132094
132306
  } = compiler_adapter_exports);
132095
132307
  ProjectTransactionInterruptedError = class extends Error {
132096
132308
  constructor(transactionId) {
@@ -132140,7 +132352,7 @@ var init_registry2 = __esm({
132140
132352
  "schema-contract/registry.mjs"() {
132141
132353
  "use strict";
132142
132354
  PROJECT_SCHEMA_CONTRACT = Object.freeze({
132143
- cliVersion: "0.50.0",
132355
+ cliVersion: "0.50.2",
132144
132356
  projectFileUploadBatchSize: 32,
132145
132357
  documentRecords: {
132146
132358
  member: {
@@ -138616,13 +138828,177 @@ var init_dialogue_dryrun = __esm({
138616
138828
  }
138617
138829
  });
138618
138830
 
138831
+ // src/unity-generated-files.ts
138832
+ import { createHash as createHash12 } from "node:crypto";
138833
+ import {
138834
+ existsSync as existsSync16,
138835
+ mkdirSync as mkdirSync13,
138836
+ readFileSync as readFileSync21,
138837
+ unlinkSync,
138838
+ writeFileSync as writeFileSync13
138839
+ } from "node:fs";
138840
+ import { dirname as dirname11, join as join18 } from "node:path";
138841
+ function validatePath(path) {
138842
+ if (!/^Generated\/[a-zA-Z0-9_%./-]+\.g\.cs$/.test(path))
138843
+ throw new Error(`Invalid generated file path: ${path}`);
138844
+ if (path.split("/").some((part) => part === "." || part === ".." || part === ""))
138845
+ throw new Error(`Invalid generated file path segment: ${path}`);
138846
+ }
138847
+ function readManifest(path) {
138848
+ if (!existsSync16(path)) return null;
138849
+ const data = JSON.parse(readFileSync21(path, "utf8"));
138850
+ if (data === null || typeof data !== "object" || !("schemaVersion" in data) || data.schemaVersion !== 1)
138851
+ throw new Error(`Invalid generated-file manifest version: ${path}`);
138852
+ if (!("files" in data) || !Array.isArray(data.files) || data.files.length === 0)
138853
+ throw new Error(`Invalid generated-file manifest entries: ${path}`);
138854
+ if (!("projectId" in data) || typeof data.projectId !== "string")
138855
+ throw new Error(`Invalid generated-file manifest project: ${path}`);
138856
+ const ids = /* @__PURE__ */ new Set();
138857
+ const paths = /* @__PURE__ */ new Set();
138858
+ const files = data.files.map((entry) => {
138859
+ if (entry === null || typeof entry !== "object" || !("path" in entry) || typeof entry.path !== "string")
138860
+ throw new Error(`Invalid generated-file manifest entry: ${path}`);
138861
+ if (!("id" in entry) || typeof entry.id !== "string" || entry.id.length === 0)
138862
+ throw new Error(`Invalid generated-file manifest identity: ${path}`);
138863
+ if (ids.has(entry.id))
138864
+ throw new Error(
138865
+ `Duplicate generated-file manifest identity: ${entry.id}`
138866
+ );
138867
+ ids.add(entry.id);
138868
+ validatePath(entry.path);
138869
+ if (paths.has(entry.path.toLowerCase()))
138870
+ throw new Error(`Duplicate generated-file manifest path: ${entry.path}`);
138871
+ paths.add(entry.path.toLowerCase());
138872
+ return { id: entry.id, path: entry.path };
138873
+ });
138874
+ return { projectId: data.projectId, files };
138875
+ }
138876
+ function writeUnityGeneratedFiles(directory, projectId, files) {
138877
+ if (!Array.isArray(files) || files.length === 0)
138878
+ throw new Error(
138879
+ "The export returned no generated files. Update the server before exporting Unity code."
138880
+ );
138881
+ const expected = /* @__PURE__ */ new Set();
138882
+ const ids = /* @__PURE__ */ new Set();
138883
+ for (const file of files) {
138884
+ validatePath(file.path);
138885
+ if (typeof file.id !== "string" || file.id.length === 0)
138886
+ throw new Error(`Missing generated file identity: ${file.path}`);
138887
+ if (ids.has(file.id))
138888
+ throw new Error(`Duplicate generated file identity: ${file.id}`);
138889
+ ids.add(file.id);
138890
+ const key = file.path.toLowerCase();
138891
+ if (expected.has(key))
138892
+ throw new Error(`Duplicate generated file path: ${file.path}`);
138893
+ expected.add(key);
138894
+ if (typeof file.content !== "string" || file.content.trim().length === 0)
138895
+ throw new Error(`Empty generated C# file: ${file.path}`);
138896
+ }
138897
+ const manifestPath = join18(directory, "NeoGeneratedFiles.json");
138898
+ const manifest = readManifest(manifestPath);
138899
+ const previousFiles = manifest?.files ?? [];
138900
+ const previousById = new Map(previousFiles.map((file) => [file.id, file]));
138901
+ const previousPaths = new Set(
138902
+ previousFiles.map((file) => file.path.toLowerCase())
138903
+ );
138904
+ const incomingById = new Map(files.map((file) => [file.id, file]));
138905
+ const operations = [];
138906
+ for (const file of previousFiles) {
138907
+ if (incomingById.get(file.id)?.path === file.path && manifest?.projectId === projectId)
138908
+ continue;
138909
+ operations.push(
138910
+ [join18(directory, file.path), null],
138911
+ [join18(directory, file.path + ".meta"), null]
138912
+ );
138913
+ }
138914
+ operations.push(
138915
+ [join18(directory, "NeoGeneratedTypes.cs"), null],
138916
+ [join18(directory, "NeoGeneratedTypes.cs.meta"), null]
138917
+ );
138918
+ for (const file of files) {
138919
+ const old = manifest?.projectId === projectId ? previousById.get(file.id) : void 0;
138920
+ const path = join18(directory, file.path);
138921
+ if (old !== void 0 && old.path !== file.path) {
138922
+ if (!previousPaths.has(file.path.toLowerCase()) && (existsSync16(path) || existsSync16(path + ".meta")))
138923
+ throw new Error(
138924
+ `Generated rename destination is not owned by the manifest: ${file.path}`
138925
+ );
138926
+ const meta = join18(directory, old.path + ".meta");
138927
+ operations.push([
138928
+ path + ".meta",
138929
+ existsSync16(meta) ? readFileSync21(meta, "utf8") : null
138930
+ ]);
138931
+ }
138932
+ operations.push([path, file.content]);
138933
+ }
138934
+ operations.push([
138935
+ manifestPath,
138936
+ JSON.stringify(
138937
+ {
138938
+ schemaVersion: 1,
138939
+ projectId,
138940
+ files: [...files].sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0).map((file) => ({
138941
+ id: file.id,
138942
+ path: file.path,
138943
+ hash: createHash12("sha256").update(file.content).digest("hex")
138944
+ }))
138945
+ },
138946
+ null,
138947
+ 2
138948
+ ) + "\n"
138949
+ ]);
138950
+ const previous = /* @__PURE__ */ new Map();
138951
+ for (const [path] of operations) {
138952
+ const key = path.toLowerCase();
138953
+ if (!previous.has(key))
138954
+ previous.set(key, {
138955
+ path,
138956
+ content: existsSync16(path) ? readFileSync21(path, "utf8") : null
138957
+ });
138958
+ }
138959
+ const write = (path, content) => {
138960
+ if (content === null) {
138961
+ if (existsSync16(path)) unlinkSync(path);
138962
+ } else {
138963
+ mkdirSync13(dirname11(path), { recursive: true });
138964
+ writeFileSync13(path, content);
138965
+ }
138966
+ };
138967
+ const changed = [];
138968
+ try {
138969
+ for (const [path, content] of operations) {
138970
+ const current = existsSync16(path) ? readFileSync21(path, "utf8") : null;
138971
+ if (current === content) continue;
138972
+ changed.push(path);
138973
+ write(path, content);
138974
+ }
138975
+ } catch (error) {
138976
+ const restored = /* @__PURE__ */ new Set();
138977
+ for (const path of changed.reverse()) {
138978
+ const key = path.toLowerCase();
138979
+ if (restored.has(key)) continue;
138980
+ restored.add(key);
138981
+ const original = previous.get(key);
138982
+ if (original.path !== path && existsSync16(path)) unlinkSync(path);
138983
+ const current = existsSync16(original.path) ? readFileSync21(original.path, "utf8") : null;
138984
+ if (current !== original.content) write(original.path, original.content);
138985
+ }
138986
+ throw error;
138987
+ }
138988
+ }
138989
+ var init_unity_generated_files = __esm({
138990
+ "src/unity-generated-files.ts"() {
138991
+ "use strict";
138992
+ }
138993
+ });
138994
+
138619
138995
  // src/commands/export.ts
138620
138996
  var export_exports = {};
138621
138997
  __export(export_exports, {
138622
138998
  runExportUnity: () => runExportUnity
138623
138999
  });
138624
- import { mkdirSync as mkdirSync13, writeFileSync as writeFileSync13 } from "node:fs";
138625
- import { join as join18 } from "node:path";
139000
+ import { mkdirSync as mkdirSync14, writeFileSync as writeFileSync14 } from "node:fs";
139001
+ import { join as join19 } from "node:path";
138626
139002
  async function runExportUnity(workspace, outDir) {
138627
139003
  if (outDir === null) {
138628
139004
  throw new Error(
@@ -138634,23 +139010,24 @@ async function runExportUnity(workspace, outDir) {
138634
139010
  `/api/projects/${workspace.config.projectId}/export`,
138635
139011
  { versionId: workspace.config.versionId }
138636
139012
  );
138637
- const resourcesDir = join18(outDir, "Resources", "Neo");
138638
- const localizationDir = join18(resourcesDir, "Localization");
138639
- const scriptsDir = join18(outDir, "Scripts", "Neo");
138640
- mkdirSync13(localizationDir, { recursive: true });
138641
- mkdirSync13(scriptsDir, { recursive: true });
138642
- writeFileSync13(join18(resourcesDir, "project.json"), response.projectJson);
138643
- writeFileSync13(
138644
- join18(scriptsDir, "NeoGeneratedTypes.cs"),
138645
- response.generatedTypes
138646
- );
139013
+ const resourcesDir = join19(outDir, "Resources", "Neo");
139014
+ const localizationDir = join19(resourcesDir, "Localization");
139015
+ const scriptsDir = join19(outDir, "Scripts", "Neo");
139016
+ mkdirSync14(localizationDir, { recursive: true });
139017
+ mkdirSync14(scriptsDir, { recursive: true });
139018
+ writeUnityGeneratedFiles(
139019
+ scriptsDir,
139020
+ response.projectId,
139021
+ response.generatedFiles
139022
+ );
139023
+ writeFileSync14(join19(resourcesDir, "project.json"), response.projectJson);
138647
139024
  for (const file of response.localizationFiles ?? []) {
138648
- writeFileSync13(join18(localizationDir, file.fileName), file.content);
139025
+ writeFileSync14(join19(localizationDir, file.fileName), file.content);
138649
139026
  }
138650
- console.log(`wrote ${join18(resourcesDir, "project.json")}`);
138651
- console.log(`wrote ${join18(scriptsDir, "NeoGeneratedTypes.cs")}`);
139027
+ console.log(`wrote ${join19(resourcesDir, "project.json")}`);
139028
+ console.log(`synchronized generated files in ${scriptsDir}`);
138652
139029
  for (const file of response.localizationFiles ?? []) {
138653
- console.log(`wrote ${join18(localizationDir, file.fileName)}`);
139030
+ console.log(`wrote ${join19(localizationDir, file.fileName)}`);
138654
139031
  }
138655
139032
  const diagnostics = response.diagnostics ?? [];
138656
139033
  for (const diagnostic of diagnostics) {
@@ -138663,6 +139040,7 @@ async function runExportUnity(workspace, outDir) {
138663
139040
  var init_export = __esm({
138664
139041
  "src/commands/export.ts"() {
138665
139042
  "use strict";
139043
+ init_unity_generated_files();
138666
139044
  init_http();
138667
139045
  }
138668
139046
  });
@@ -138673,7 +139051,7 @@ __export(dev_exports, {
138673
139051
  runDev: () => runDev
138674
139052
  });
138675
139053
  import { watch } from "node:fs";
138676
- import { join as join19 } from "node:path";
139054
+ import { join as join20 } from "node:path";
138677
139055
  import { emitKeypressEvents } from "node:readline";
138678
139056
  import { ConvexClient } from "convex/browser";
138679
139057
  function isSchemaSignal(value) {
@@ -138783,7 +139161,7 @@ async function runDev(workspace, options) {
138783
139161
  };
138784
139162
  for (const dir of ["Classes", "Enums"]) {
138785
139163
  try {
138786
- watch(join19(workspace.root, dir), { persistent: true }, onFileChange);
139164
+ watch(join20(workspace.root, dir), { persistent: true }, onFileChange);
138787
139165
  } catch {
138788
139166
  }
138789
139167
  }
@@ -138837,8 +139215,8 @@ __export(resolve_exports, {
138837
139215
  resolveMarkers: () => resolveMarkers,
138838
139216
  runResolve: () => runResolve
138839
139217
  });
138840
- import { readFileSync as readFileSync21, rmSync as rmSync8, writeFileSync as writeFileSync14 } from "node:fs";
138841
- import { join as join20, relative as relative11 } from "node:path";
139218
+ import { readFileSync as readFileSync22, rmSync as rmSync8, writeFileSync as writeFileSync15 } from "node:fs";
139219
+ import { join as join21, relative as relative11 } from "node:path";
138842
139220
  function runResolve(workspace, side) {
138843
139221
  const marked = readMarkedProjectSourcesV4(workspace.root);
138844
139222
  assertRecordConflictsResolveTogetherV4(
@@ -138848,7 +139226,7 @@ function runResolve(workspace, side) {
138848
139226
  const resolvedRecords = adoptServerConflictBases(workspace);
138849
139227
  let resolvedFiles = 0;
138850
139228
  for (const file of marked) {
138851
- writeFileSync14(file.absolutePath, resolveMarkers(file.source, side), "utf8");
139229
+ writeFileSync15(file.absolutePath, resolveMarkers(file.source, side), "utf8");
138852
139230
  resolvedFiles += 1;
138853
139231
  }
138854
139232
  let resolvedBinaries = 0;
@@ -138856,12 +139234,12 @@ function runResolve(workspace, side) {
138856
139234
  const binary = state.projectBinary;
138857
139235
  const conflict2 = binary?.conflict;
138858
139236
  if (binary === void 0 || conflict2 === void 0) continue;
138859
- const destination = join20(workspace.root, binary.path);
139237
+ const destination = join21(workspace.root, binary.path);
138860
139238
  if (side === "theirs") {
138861
139239
  if (conflict2.remoteSha256 !== null && conflict2.artifactPath !== void 0) {
138862
139240
  writeVerifiedBinaryDownloadV4(
138863
139241
  destination,
138864
- readFileSync21(join20(workspace.root, conflict2.artifactPath)),
139242
+ readFileSync22(join21(workspace.root, conflict2.artifactPath)),
138865
139243
  conflict2.remoteSha256
138866
139244
  );
138867
139245
  binary.sha256 = conflict2.remoteSha256;
@@ -138871,7 +139249,7 @@ function runResolve(workspace, side) {
138871
139249
  }
138872
139250
  }
138873
139251
  if (conflict2.artifactPath !== void 0) {
138874
- rmSync8(join20(workspace.root, conflict2.artifactPath), { force: true });
139252
+ rmSync8(join21(workspace.root, conflict2.artifactPath), { force: true });
138875
139253
  }
138876
139254
  delete binary.conflict;
138877
139255
  resolvedBinaries += 1;
@@ -138890,7 +139268,7 @@ function runResolve(workspace, side) {
138890
139268
  function readMarkedProjectSourcesV4(root) {
138891
139269
  const files = [];
138892
139270
  for (const absolutePath of listProjectSourceFilesV4(root)) {
138893
- const source = readFileSync21(absolutePath, "utf8");
139271
+ const source = readFileSync22(absolutePath, "utf8");
138894
139272
  if (detectConflictMarkers(source) === null) continue;
138895
139273
  files.push({
138896
139274
  path: normalizeWorkspaceSourcePath(relative11(root, absolutePath)),
@@ -139190,7 +139568,7 @@ There is no --keep-current: preserving current semantic state defines flatten.
139190
139568
  async function main() {
139191
139569
  const args = parseArgs(process.argv.slice(2));
139192
139570
  if (args.command === "--version") {
139193
- console.log("0.50.0");
139571
+ console.log("0.50.2");
139194
139572
  return;
139195
139573
  }
139196
139574
  if (args.command === null || args.command === "help" || args.command === "--help" || args.command === "-h") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neocompose/cli",
3
- "version": "0.50.0",
3
+ "version": "0.50.2",
4
4
  "description": "Neo Compose native project-source CLI with bidirectional sync.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -9,7 +9,7 @@ description: >-
9
9
  `@neocompose/cli` or `node cli/bin/neo.mjs` in the neo-compose repository.
10
10
  ---
11
11
 
12
- <!-- reviewed-through-cli: 0.50.0 -->
12
+ <!-- reviewed-through-cli: 0.50.2 -->
13
13
 
14
14
  # Neo Compose CLI
15
15
 
@@ -88,7 +88,7 @@ wrappers.
88
88
  The marker near the top of `SKILL.md` must exactly match the package version:
89
89
 
90
90
  ```html
91
- <!-- reviewed-through-cli: 0.50.0 -->
91
+ <!-- reviewed-through-cli: 0.50.2 -->
92
92
  ```
93
93
 
94
94
  The quoted version above is checked too, so this instruction cannot go stale
@@ -61,6 +61,10 @@ bodies affected by changed declarations. Use `--force-recompile` with either a
61
61
  dry run or a real push only when diagnosing stale derived IR: it retains the
62
62
  exhaustive project-wide body sweep and is intentionally slower.
63
63
 
64
+ Changed member bodies and migration bodies share one schema projection and its
65
+ indexes throughout push preparation. Each changed member body is compiled once,
66
+ so a batch of scripted changes does not rebuild the whole schema for every body.
67
+
64
68
  Interactive status, diff, pull, and test commands report workspace loading and
65
69
  preparation phases. Spinners continue animating during synchronous compilation;
66
70
  `NO_COLOR` removes color without stopping the animation. JSON reporters and
@@ -242,3 +246,11 @@ honor both variables.
242
246
 
243
247
  Pass explicit project/version IDs and flags in automation. Prefer `--json` for
244
248
  machine-readable output and do not depend on interactive pickers or confirms.
249
+
250
+ ## Unity code export
251
+
252
+ `neo export unity --out <UnityAssetsDir>` writes separate C# files under
253
+ `Scripts/Neo/Generated/`, named after the generated C# types, plus project and localization JSON. It compares
254
+ contents before writing C#, preserves unchanged `.meta` files, removes obsolete
255
+ outputs listed in `NeoGeneratedFiles.json`, and removes the former
256
+ `NeoGeneratedTypes.cs`. Stable identities in the manifest preserve `.meta` GUIDs when types are renamed. Keep the manifest with the generated files.