@neocompose/cli 0.50.5 → 0.50.7

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.7] - 2026-09-14
4
+
5
+ - Preserve virtual default fields when cloning classes or importing constructor arguments. Fix `neo pull` failing with `Missing key 'Body' on object` for CharacterBody instances that clone a sparse cosmetic config. Index expanded graphs once to keep descendant reads linear.
6
+
7
+ ## [0.50.6] - 2026-09-14
8
+
9
+ - Match server preparation when adding world placements by loading their layer links and binding values without expanding unchanged placement siblings.
10
+
3
11
  ## [0.50.5] - 2026-09-14
4
12
 
5
13
  - Resolve push references, rewrite assigned identities, and cascade scripted member edits without rescanning the project for each changed record. Reuse animation replay indexes and constructor validation across roots. Preserve list membership indexes when an initializer only replaces entry content.
package/dist/neo.mjs CHANGED
@@ -76470,7 +76470,13 @@ function virtualExpandedRecordForReceiver(receiver, ctx) {
76470
76470
  if (rowId === null) return null;
76471
76471
  const rows = ctx.vm.databaseVM?.virtualInstanceRowsForValue(rowId);
76472
76472
  if (rows === void 0 || rows.length === 0) return null;
76473
- const expanded = rows.find((row) => row.id === rowId);
76473
+ ctx.__virtualRowsById ??= /* @__PURE__ */ new WeakMap();
76474
+ let rowsById = ctx.__virtualRowsById.get(rows);
76475
+ if (rowsById === void 0) {
76476
+ rowsById = new Map(rows.map((row) => [row.id, row]));
76477
+ ctx.__virtualRowsById.set(rows, rowsById);
76478
+ }
76479
+ const expanded = rowsById.get(rowId);
76474
76480
  if (expanded === void 0) return null;
76475
76481
  const record4 = expanded.value;
76476
76482
  if (typeof record4 !== "object" || record4 === null || Array.isArray(record4)) {
@@ -79022,7 +79028,7 @@ function evaluateInitializerInContext(init, member, ctx, createdValues, argument
79022
79028
  if (isMemberLookupBase(member)) {
79023
79029
  return encodeLookupInitializerResult(member, result.value, created, ctx);
79024
79030
  }
79025
- const rootRow = initializerRootRow(result.value, created);
79031
+ let rootRow = initializerRootRow(result.value, created);
79026
79032
  for (const row of created) {
79027
79033
  if (row === rootRow) continue;
79028
79034
  createdValues.push(row);
@@ -79038,11 +79044,36 @@ function evaluateInitializerInContext(init, member, ctx, createdValues, argument
79038
79044
  existingValueRow
79039
79045
  };
79040
79046
  }
79041
- return { value: result.value, classId: existingRuntimeClassId };
79047
+ if (existingValueRow !== null && existingRuntimeClassId !== null && isMemberClassBase(member)) {
79048
+ const storage = constructorArgumentStorage(existingValueRow.id, ctx);
79049
+ if (storage !== 3 /* Session */) {
79050
+ const checkpoint = sessionCreationCheckpoint(ctx);
79051
+ const value = cloneClassValueGraph(
79052
+ result.value,
79053
+ existingRuntimeClassId,
79054
+ ctx
79055
+ );
79056
+ const importedRows = createdSessionValuesSince(ctx, checkpoint);
79057
+ const imported = initializerRootRow(value, importedRows);
79058
+ if (imported === null) {
79059
+ throw new NSGetterRuntimeError(
79060
+ `Initializer '${member.name}' imported a Class without a backing row.`
79061
+ );
79062
+ }
79063
+ rootRow = imported;
79064
+ result = { ...result, value };
79065
+ for (const row of importedRows) {
79066
+ if (row !== rootRow) createdValues.push(row);
79067
+ }
79068
+ }
79069
+ }
79070
+ if (rootRow === null) {
79071
+ return { value: result.value, classId: existingRuntimeClassId };
79072
+ }
79042
79073
  }
79043
79074
  deleteRuntimeSessionValue(ctx, rootRow.id);
79044
79075
  ctx.__executionState?.constructorGroups.delete(rootRow.id);
79045
- const constructorArgs = typeof result.value === "object" && result.value !== null ? ctx.__constructedArgumentsByValue.get(result.value) : void 0;
79076
+ const constructorArgs = typeof result.value === "object" && result.value !== null ? ctx.__constructedArgumentsByValue.get(result.value) ?? rootRow.constructorArgs ?? void 0 : void 0;
79046
79077
  const provenance = pickInstanceProvenance({
79047
79078
  constructorArgs,
79048
79079
  instanceConstructorId: rootRow.instanceConstructorId,
@@ -80420,7 +80451,11 @@ function cloneConstructorArgumentGraph(args) {
80420
80451
  );
80421
80452
  const record4 = {};
80422
80453
  clone.value = record4;
80423
- for (const [schemaKey, childId] of Object.entries(source.value)) {
80454
+ const sourceRecord = {
80455
+ ...virtualExpandedRecordForReceiver(source.value, args.ctx),
80456
+ ...source.value
80457
+ };
80458
+ for (const [schemaKey, childId] of Object.entries(sourceRecord)) {
80424
80459
  if (typeof childId !== "string") {
80425
80460
  throw new NSGetterRuntimeError(
80426
80461
  `Constructor import found a non-id Class field '${schemaKey}' on value '${source.id}'.`
@@ -80979,10 +81014,12 @@ function cloneClassValueGraph(receiver, expectedClassId, ctx) {
80979
81014
  registerRuntimeSessionValue(ctx, clone);
80980
81015
  clonedBySourceId.set(sourceRow.id, clone);
80981
81016
  if (typeof sourceRow.value === "object" && sourceRow.value !== null && !Array.isArray(sourceRow.value)) {
80982
- const sourceRecord = sourceRow.value;
80983
- const clonedRecord = { ...sourceRecord };
81017
+ const clonedRecord = {
81018
+ ...virtualExpandedRecordForReceiver(sourceRow.value, ctx),
81019
+ ...sourceRow.value
81020
+ };
80984
81021
  clone.value = clonedRecord;
80985
- for (const [key, childId] of Object.entries(sourceRecord)) {
81022
+ for (const [key, childId] of Object.entries(clonedRecord)) {
80986
81023
  if (typeof childId !== "string") continue;
80987
81024
  const childMember = sourceTypeInfo?.type === 5 /* Dictionary */ ? null : ownedObjectChildMember(sourceRow, sourceMember, key, ctx);
80988
81025
  const childTypeInfo = sourceTypeInfo?.type === 5 /* Dictionary */ ? sourceTypeInfo.entryTypeInfo : void 0;
@@ -95528,6 +95565,16 @@ var init_project_version_relation_sweep_trigger = __esm({
95528
95565
  });
95529
95566
 
95530
95567
  // ../src/database/project-world-placement-bindings.ts
95568
+ function createdWorldPlacementIds(changes) {
95569
+ if (changes.length === 0) return null;
95570
+ const ids = /* @__PURE__ */ new Set();
95571
+ for (const change of changes) {
95572
+ if (change.recordKind !== "value" || change.operation !== "create" || change.deleted === true || !hasWorldPlacementSidecar(change.nextData))
95573
+ return null;
95574
+ ids.add(change.recordId);
95575
+ }
95576
+ return ids;
95577
+ }
95531
95578
  function hasWorldPlacementSidecar(value) {
95532
95579
  if (!isPlainRecord3(value) || !isPlainRecord3(value.value)) return false;
95533
95580
  const body = value.value;
@@ -96050,6 +96097,8 @@ function validateWorldContentSidecars(args) {
96050
96097
  valuesById
96051
96098
  });
96052
96099
  for (const placementId of placementIds) {
96100
+ if (args.placementIds !== void 0 && !args.placementIds.has(placementId))
96101
+ continue;
96053
96102
  const placement = valuesById.get(placementId);
96054
96103
  if (placement === void 0) {
96055
96104
  throw new Error(
@@ -96112,6 +96161,8 @@ function validateWorldContentSidecars(args) {
96112
96161
  placementList,
96113
96162
  containedIds
96114
96163
  )) {
96164
+ if (args.placementIds !== void 0 && !args.placementIds.has(placementId))
96165
+ continue;
96115
96166
  const placement = valuesById.get(placementId);
96116
96167
  if (placement === void 0) {
96117
96168
  throw new Error(
@@ -96133,6 +96184,8 @@ function validateWorldContentSidecars(args) {
96133
96184
  }
96134
96185
  }
96135
96186
  for (const value of args.values) {
96187
+ if (args.placementIds !== void 0 && !args.placementIds.has(value.id))
96188
+ continue;
96136
96189
  const record4 = valueObjectRecord(value);
96137
96190
  if (record4 === null) continue;
96138
96191
  if (optionalNonEmptyString(record4.assetClassId) !== null && !validatedPlacementIds.has(value.id) && !reachablePlacementIds.has(value.id)) {
@@ -103977,7 +104030,7 @@ function* prepareServerOwnedSchemaCommitPasses(args) {
103977
104030
  (change) => change.recordKind === "value"
103978
104031
  );
103979
104032
  const hasAdditionalContractImpact = (args.additionalImpactedIds?.size ?? 0) > 0 || (args.additionalChangedValueIds?.size ?? 0) > 0;
103980
- if (!hasSchemaChange && !hasMigrationChange && !hasDialogueSourceChange && !hasValueChange && !hasAdditionalContractImpact) {
104033
+ if (!hasSchemaChange && !hasMigrationChange && !hasDialogueSourceChange && !hasValueChange && !hasAdditionalContractImpact && (args.authoredValueSeeds?.length ?? 0) === 0) {
103981
104034
  return authoredChanges;
103982
104035
  }
103983
104036
  args.passLifecycle?.("start", "neo-script-recompile-targets");
@@ -108765,6 +108818,7 @@ function assertStagedWorldContentLayerBindingsValid(current, projected, changes)
108765
108818
  classes,
108766
108819
  relations,
108767
108820
  values: projected.values,
108821
+ placementIds: createdWorldPlacementIds(changes) ?? void 0,
108768
108822
  variants: worldPlacementVariantBindings({
108769
108823
  variants: projected.variants ?? [],
108770
108824
  variantFolders: projected.variantFolders ?? [],
@@ -112461,6 +112515,8 @@ function worldKindByClassId(document) {
112461
112515
  return result;
112462
112516
  }
112463
112517
  function commitPreparationWorldScopeKinds(args) {
112518
+ if (createdWorldPlacementIds(args.changes) !== null)
112519
+ return /* @__PURE__ */ new Set(["tileGrid", "tileLayerLink", "objectLayerLink"]);
112464
112520
  const allKinds = new Set(
112465
112521
  [...args.currentDocument.classes, ...args.postDocument.classes].flatMap(
112466
112522
  (schemaClass2) => typeof schemaClass2.system?.worldKind === "string" ? [schemaClass2.system.worldKind] : []
@@ -112612,7 +112668,10 @@ function planCommitPreparationInitialScope(args) {
112612
112668
  }
112613
112669
  if (change.recordKind !== "localized-text") {
112614
112670
  for (const valueId of collectStoredRecordIds(change.nextData))
112615
- rememberExact("value", valueId, true);
112671
+ rememberExact("value", valueId, change.recordKind !== "value");
112672
+ if (change.recordKind === "value")
112673
+ for (const valueId of collectStoredDescendantRecordIds(change.nextData))
112674
+ rememberExact("value", valueId, true);
112616
112675
  }
112617
112676
  }
112618
112677
  const currentTextsById = new Map(
@@ -113105,10 +113164,27 @@ function planCommitPreparationLoadedClosureScope(args) {
113105
113164
  });
113106
113165
  }
113107
113166
  }
113167
+ if (args.placementCreationScope && record4.recordKind === "value" && record4.data !== null && typeof record4.data === "object") {
113168
+ const body = Reflect.get(record4.data, "value");
113169
+ if (body !== null && typeof body === "object") {
113170
+ for (const key of [
113171
+ "Children",
113172
+ "Tiles",
113173
+ "Objects",
113174
+ "layerOverrideValueId"
113175
+ ])
113176
+ if (typeof Reflect.get(body, key) === "string")
113177
+ rememberExact("value", Reflect.get(body, key));
113178
+ }
113179
+ }
113108
113180
  if (record4.includeDescendants !== false) {
113109
113181
  for (const valueId of collectStoredRecordIds(record4.data)) {
113110
- rememberDescendant("value", valueId);
113182
+ if (record4.recordKind === "value") rememberExact("value", valueId);
113183
+ else rememberDescendant("value", valueId);
113111
113184
  }
113185
+ if (record4.recordKind === "value")
113186
+ for (const valueId of collectStoredDescendantRecordIds(record4.data))
113187
+ rememberDescendant("value", valueId);
113112
113188
  }
113113
113189
  }
113114
113190
  return {
@@ -113551,6 +113627,14 @@ function projectDocumentRecordsByKey(document) {
113551
113627
  add("value", document.values ?? []);
113552
113628
  return result;
113553
113629
  }
113630
+ function collectStoredDescendantRecordIds(value) {
113631
+ if (value === null || typeof value !== "object" || Array.isArray(value))
113632
+ return collectStoredRecordIds(value);
113633
+ const descendants = [];
113634
+ for (const [key, entry] of Object.entries(value))
113635
+ if (key !== "containerId") descendants.push(entry);
113636
+ return collectStoredRecordIds(descendants);
113637
+ }
113554
113638
  function collectStoredRecordIds(value) {
113555
113639
  const result = /* @__PURE__ */ new Set();
113556
113640
  const visit = (candidate) => {
@@ -114003,7 +114087,8 @@ function closeInMemoryLoadedScope(args) {
114003
114087
  records: closureRecords,
114004
114088
  rangeLimit: COMMIT_PREPARATION_SCOPE_RANGE_ROW_LIMIT,
114005
114089
  postingLimit: COMMIT_PREPARATION_SCOPE_POSTING_ROW_LIMIT,
114006
- animationSchema
114090
+ animationSchema,
114091
+ placementCreationScope: createdWorldPlacementIds(args.prepared) !== null
114007
114092
  });
114008
114093
  const answer = readCommitPreparationScopeInMemory({
114009
114094
  request,
@@ -114165,6 +114250,7 @@ var init_server_preparation_preflight = __esm({
114165
114250
  init_trusted_commit_verification();
114166
114251
  init_project_version_schema_commit();
114167
114252
  init_project_version_whole_graph_validation();
114253
+ init_project_world_placement_bindings();
114168
114254
  init_projectRecordBuckets();
114169
114255
  init_compile_ns_property();
114170
114256
  init_valueHeadPlacementGraph();
@@ -133193,7 +133279,7 @@ var init_registry2 = __esm({
133193
133279
  "schema-contract/registry.mjs"() {
133194
133280
  "use strict";
133195
133281
  PROJECT_SCHEMA_CONTRACT = Object.freeze({
133196
- cliVersion: "0.50.5",
133282
+ cliVersion: "0.50.7",
133197
133283
  projectFileUploadBatchSize: 32,
133198
133284
  documentRecords: {
133199
133285
  member: {
@@ -140409,7 +140495,7 @@ There is no --keep-current: preserving current semantic state defines flatten.
140409
140495
  async function main() {
140410
140496
  const args = parseArgs(process.argv.slice(2));
140411
140497
  if (args.command === "--version") {
140412
- console.log("0.50.5");
140498
+ console.log("0.50.7");
140413
140499
  return;
140414
140500
  }
140415
140501
  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.5",
3
+ "version": "0.50.7",
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.5 -->
12
+ <!-- reviewed-through-cli: 0.50.7 -->
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.5 -->
91
+ <!-- reviewed-through-cli: 0.50.7 -->
92
92
  ```
93
93
 
94
94
  The quoted version above is checked too, so this instruction cannot go stale