@neocompose/cli 0.24.5 → 0.24.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,27 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.24.7] - 2026-08-08
4
+
5
+ ### Fixed
6
+
7
+ - Lower named delegate method groups such as `this.SelectTorso` to the P60
8
+ member-target wire form, then bind their receiver to each constructed
9
+ instance. Constructor-pane previews now retain that binding when replacing
10
+ the evaluator's provisional root, so animation tracks resolve against the
11
+ ephemeral preview graph instead of disappearing.
12
+ - Reconstruct stored inline-closure `this` from the nearest compatible lexical
13
+ owner in nested declaration graphs, keeping closures distinct from named
14
+ method groups as required by P60.
15
+
16
+ ## [0.24.6] - 2026-08-08
17
+
18
+ ### Fixed
19
+
20
+ - Preserve named delegate method groups such as `this.SelectTorso` as lexical
21
+ delegate source during CLI lowering. This lets construction bind the selector
22
+ to each object instance instead of persisting an unbound member target that
23
+ makes animation tracks disappear in preview.
24
+
3
25
  ## [0.24.5] - 2026-08-07
4
26
 
5
27
  ### Fixed
package/dist/neo.mjs CHANGED
@@ -10058,7 +10058,7 @@ var init_strict_resolver = __esm({
10058
10058
  }
10059
10059
  return void 0;
10060
10060
  }
10061
- resolveDelegateMethodGroup(expression, expected, scope) {
10061
+ resolveDelegateMethodGroup(expression, expected, scope, options = {}) {
10062
10062
  let symbol;
10063
10063
  let receiver = null;
10064
10064
  let owner;
@@ -10133,29 +10133,17 @@ var init_strict_resolver = __esm({
10133
10133
  expression.pos
10134
10134
  );
10135
10135
  }
10136
- const parameters = expected.parameterTypes.map(
10137
- (type, index) => variable(`__arg_${index}__`, type, void 0, this.project)
10136
+ if (receiver?.kind === "instance" && options.allowUnboundInstanceTarget !== true && (receiver.expression.pointer.type !== "variable" /* Variable */ || receiver.expression.pointer.variableId !== this.thisVariable.id)) {
10137
+ throw new CompileError(
10138
+ "NeoDelegate method groups currently require the current 'this' receiver.",
10139
+ expression.pos
10140
+ );
10141
+ }
10142
+ return literal(
10143
+ expected,
10144
+ { memberId: symbol.id, valueId: null },
10145
+ this.project
10138
10146
  );
10139
- const call = {
10140
- type: "callFunction" /* CallFunction */,
10141
- memberId: symbol.id,
10142
- receiver: receiver?.kind === "static" ? { kind: "static", memberId: receiver.memberId } : {
10143
- kind: "instance",
10144
- pointer: receiver?.expression.pointer ?? this.thisVariable.pointer
10145
- },
10146
- args: parameters.map((parameter4) => ({
10147
- type: "variable" /* Variable */,
10148
- variableId: parameter4.id
10149
- })),
10150
- callSiteId: this.nextCallSite(expression.pos)
10151
- };
10152
- const action = {
10153
- compilerRevision: NEOSCRIPT_COMPILER_REVISION,
10154
- parameters: [this.thisVariable, this.rootVariable, ...parameters],
10155
- instructions: expectedVoid ? [{ type: "functionCall" /* FunctionCall */, call }] : [{ type: "return" /* Return */, pointer: call }],
10156
- typeInfo: expectedVoid ? toWireType({ kind: "primitive", name: "null" }, this.project) : toWireType(expected.returnType, this.project)
10157
- };
10158
- return literal(expected, { action }, this.project);
10159
10147
  }
10160
10148
  /** Derives the exact expected delegate signature for a test mock target. */
10161
10149
  inferDelegateMethodGroupType(expression, scope) {
@@ -11830,7 +11818,10 @@ var init_strict_resolver = __esm({
11830
11818
  const target = this.resolveDelegateMethodGroup(
11831
11819
  targetAst,
11832
11820
  delegateType,
11833
- scope
11821
+ scope,
11822
+ // Mock targets are member-identity metadata. They are never invoked as
11823
+ // delegates, and mockHandle.on(receiver) owns receiver scoping.
11824
+ { allowUnboundInstanceTarget: true }
11834
11825
  );
11835
11826
  if (target === null) {
11836
11827
  throw new CompileError(
@@ -42238,6 +42229,7 @@ function evaluateLiteralContainer(args) {
42238
42229
  }
42239
42230
  return {
42240
42231
  literal: literal2,
42232
+ ...evaluated.provisionalRootId === void 0 ? {} : { provisionalRootId: evaluated.provisionalRootId },
42241
42233
  ...evaluated.existingValueRow === void 0 ? {} : { existingValueRow: evaluated.existingValueRow }
42242
42234
  };
42243
42235
  }
@@ -42259,6 +42251,13 @@ function buildDefaultMemberValue(args) {
42259
42251
  return evaluated.existingValueRow;
42260
42252
  }
42261
42253
  const value2 = buildNewValue(args.projectId, evaluated.literal);
42254
+ if (evaluated.provisionalRootId !== void 0) {
42255
+ retargetDelegateReceiverValueIds(
42256
+ [value2, ...args.createdValues],
42257
+ evaluated.provisionalRootId,
42258
+ value2.id
42259
+ );
42260
+ }
42262
42261
  args.createdValues.push(value2);
42263
42262
  recordStorageKeyDeclaration(args.storageKeyDeclarations, value2.id, member);
42264
42263
  return value2;
@@ -42465,6 +42464,28 @@ function buildNewValue(projectId, body) {
42465
42464
  };
42466
42465
  return props;
42467
42466
  }
42467
+ function retargetDelegateReceiverValueIds(rows, fromValueId, toValueId) {
42468
+ const visited = /* @__PURE__ */ new WeakSet();
42469
+ const visit = (value) => {
42470
+ if (typeof value !== "object" || value === null) return;
42471
+ if (visited.has(value)) return;
42472
+ visited.add(value);
42473
+ if (isMemberDelegateTarget(value)) {
42474
+ if (value.valueId === fromValueId) value.valueId = toValueId;
42475
+ return;
42476
+ }
42477
+ if (Array.isArray(value)) {
42478
+ for (const entry of value) visit(entry);
42479
+ return;
42480
+ }
42481
+ for (const entry of Object.values(value)) visit(entry);
42482
+ };
42483
+ for (const row of rows) {
42484
+ if (!isLiteralValueContent(row)) continue;
42485
+ visit(row.value);
42486
+ visit(row.constructorArgs);
42487
+ }
42488
+ }
42468
42489
  function cloneDefaultClassRecord(args) {
42469
42490
  const sourceValue = args.sourceBody?.value;
42470
42491
  if (!isStringRecord(sourceValue)) return {};
@@ -42601,6 +42622,13 @@ function cloneDefaultValueForMember(args) {
42601
42622
  return evaluated.existingValueRow;
42602
42623
  }
42603
42624
  const evaluatedRow = buildNewValue(args.projectId, evaluated.literal);
42625
+ if (evaluated.provisionalRootId !== void 0) {
42626
+ retargetDelegateReceiverValueIds(
42627
+ [evaluatedRow, ...args.createdValues],
42628
+ evaluated.provisionalRootId,
42629
+ evaluatedRow.id
42630
+ );
42631
+ }
42604
42632
  evaluatedRow.sourceValueId = sourceValue.sourceValueId ?? args.sourceValueId;
42605
42633
  args.createdValues.push(evaluatedRow);
42606
42634
  recordStorageKeyDeclaration(
@@ -58794,10 +58822,16 @@ function evalPointer(pointer, scope, ctx) {
58794
58822
  if (isNSDelegateClosureValue(value)) {
58795
58823
  return {
58796
58824
  ...value,
58797
- [DELEGATE_LEXICAL_THIS]: ctx.thisValue,
58825
+ ...ctx.thisValue === null || ctx.thisValue === void 0 ? {} : { [DELEGATE_LEXICAL_THIS]: ctx.thisValue },
58798
58826
  [DELEGATE_LEXICAL_ROOT]: ctx.rootValue
58799
58827
  };
58800
58828
  }
58829
+ if (isMemberDelegateTarget(value) && value.valueId === null) {
58830
+ const member = evalMemberById(ctx.vm, value.memberId);
58831
+ if (member === null || member.isStatic === true) return value;
58832
+ const receiver = trackedRowForValueReference(ctx.thisValue, ctx);
58833
+ return receiver === null ? value : { ...value, valueId: receiver.id };
58834
+ }
58801
58835
  return value;
58802
58836
  }
58803
58837
  case "variable" /* variable */: {
@@ -59173,50 +59207,49 @@ function delegateClosureLexicalThis(closure, ctx) {
59173
59207
  if (Object.prototype.hasOwnProperty.call(closure, DELEGATE_LEXICAL_THIS)) {
59174
59208
  return closure[DELEGATE_LEXICAL_THIS];
59175
59209
  }
59210
+ const lexicalThisType = closure.action.parameters[0]?.typeInfo;
59211
+ if (lexicalThisType === void 0 || lexicalThisType.type === NS_TYPE_UNKNOWN || lexicalThisType.type === 0 /* Null */) {
59212
+ return null;
59213
+ }
59176
59214
  const closureRow = trackedRowForValueReference(closure, ctx);
59177
59215
  if (closureRow === null) {
59178
59216
  throw new NSGetterRuntimeError(
59179
59217
  "Stored NeoDelegate closure has no resolvable value row; lexical this cannot be reconstructed from ownership."
59180
59218
  );
59181
59219
  }
59182
- const owners = /* @__PURE__ */ new Map();
59183
- for (const link of evaluatorParentLinks(
59184
- evaluatorIndexes(ctx),
59185
- closureRow.id
59220
+ const owners = [];
59221
+ const indexes = evaluatorIndexes(ctx);
59222
+ for (const [ancestorId, distance] of evaluatorOwnershipDistances(
59223
+ closureRow.id,
59224
+ indexes
59186
59225
  )) {
59187
- const parent = evalValueById(
59226
+ if (ancestorId === closureRow.id) continue;
59227
+ const ancestor = evalValueById(
59188
59228
  ctx.vm,
59189
- link.parentId,
59229
+ ancestorId,
59190
59230
  ctx.__runtimeSessionValues,
59191
59231
  ctx.__valueOverlay
59192
59232
  );
59193
- if (parent === null) continue;
59194
- if (typeof parent.value !== "object") continue;
59195
- if (parent.value === null) continue;
59196
- if (Array.isArray(parent.value)) continue;
59197
- const classId = classIdForValueRow(parent, ctx);
59198
- if (classId === void 0) continue;
59199
- const owningMember = memberForCustomSchemaValue(classId, link.key, ctx);
59200
- if (owningMember?.kind !== 25 /* NSDelegate */) continue;
59201
- owners.set(parent.id, parent.value);
59233
+ if (ancestor === null) continue;
59234
+ if (typeof ancestor.value !== "object") continue;
59235
+ if (ancestor.value === null || Array.isArray(ancestor.value)) continue;
59236
+ if (!runtimeValueMatchesType(ancestor.value, lexicalThisType, ctx))
59237
+ continue;
59238
+ owners.push({ id: ancestor.id, value: ancestor.value, distance });
59202
59239
  }
59203
- if (owners.size === 0) {
59240
+ if (owners.length === 0) {
59204
59241
  throw new NSGetterRuntimeError(
59205
59242
  `Stored NeoDelegate closure value '${closureRow.id}' has no owning class instance; lexical this cannot be reconstructed from ownership.`
59206
59243
  );
59207
59244
  }
59208
- if (owners.size > 1) {
59245
+ const nearestDistance = Math.min(...owners.map((owner) => owner.distance));
59246
+ const nearest = owners.filter((owner) => owner.distance === nearestDistance);
59247
+ if (nearest.length > 1) {
59209
59248
  throw new NSGetterRuntimeError(
59210
- `Stored NeoDelegate closure value '${closureRow.id}' has multiple owning class instances (${[...owners.keys()].join(", ")}); lexical this is ambiguous.`
59249
+ `Stored NeoDelegate closure value '${closureRow.id}' has multiple owning class instances (${nearest.map((owner) => owner.id).join(", ")}); lexical this is ambiguous.`
59211
59250
  );
59212
59251
  }
59213
- const owner = owners.values().next();
59214
- if (owner.done) {
59215
- throw new NSGetterRuntimeError(
59216
- `Stored NeoDelegate closure value '${closureRow.id}' lost its owning class instance during lexical-this resolution.`
59217
- );
59218
- }
59219
- return owner.value;
59252
+ return nearest[0].value;
59220
59253
  }
59221
59254
  function resolveCallableSignature(memberId, memberKind, ctx) {
59222
59255
  const cacheKey = `${memberKind}:${memberId}`;
@@ -61097,6 +61130,74 @@ function stageConstructedRows(createdValues, ctx) {
61097
61130
  if (ctx.__indexes !== void 0) indexEvaluatorRow(ctx.__indexes, row);
61098
61131
  }
61099
61132
  }
61133
+ function bindConstructedDelegateTargets(rootId, ctx) {
61134
+ const indexes = evaluatorIndexes(ctx);
61135
+ const pending = [rootId];
61136
+ const visited = /* @__PURE__ */ new Set();
61137
+ while (pending.length > 0) {
61138
+ const rowId = pending.pop();
61139
+ if (rowId === void 0 || visited.has(rowId)) continue;
61140
+ visited.add(rowId);
61141
+ const row = evalValueById(
61142
+ ctx.vm,
61143
+ rowId,
61144
+ ctx.__runtimeSessionValues,
61145
+ ctx.__valueOverlay
61146
+ );
61147
+ if (row === null) continue;
61148
+ if (Array.isArray(row.value)) {
61149
+ for (const childId of row.value) {
61150
+ if (typeof childId === "string") pending.push(childId);
61151
+ }
61152
+ } else if (typeof row.value === "object" && row.value !== null) {
61153
+ for (const childId of Object.values(row.value)) {
61154
+ if (typeof childId === "string") pending.push(childId);
61155
+ }
61156
+ }
61157
+ if (!isMemberDelegateTarget(row.value) || row.value.valueId !== null) {
61158
+ continue;
61159
+ }
61160
+ const target = evalMemberById(ctx.vm, row.value.memberId);
61161
+ if (target === null || target.isStatic === true) continue;
61162
+ const placement = findSchemaPlacement(target.id, ctx.vm.classes);
61163
+ if (placement === null) {
61164
+ throw new NSGetterRuntimeError(
61165
+ `NeoDelegate method target '${target.name}' has no declaring Class placement.`
61166
+ );
61167
+ }
61168
+ const matches = [];
61169
+ for (const [ancestorId, distance] of evaluatorOwnershipDistances(
61170
+ row.id,
61171
+ indexes
61172
+ )) {
61173
+ const ancestor = evalValueById(
61174
+ ctx.vm,
61175
+ ancestorId,
61176
+ ctx.__runtimeSessionValues,
61177
+ ctx.__valueOverlay
61178
+ );
61179
+ if (ancestor === null) continue;
61180
+ const ancestorClassId = classIdForValueRow(ancestor, ctx);
61181
+ if (ancestorClassId === void 0) continue;
61182
+ const ownsTarget = resolveInheritanceChain(
61183
+ ancestorClassId,
61184
+ ctx.vm.classes
61185
+ ).some((schemaClass2) => schemaClass2.id === placement.ownerClass.id);
61186
+ if (ownsTarget) matches.push({ id: ancestor.id, distance });
61187
+ }
61188
+ if (matches.length === 0) continue;
61189
+ const nearestDistance = Math.min(...matches.map((match) => match.distance));
61190
+ const nearest = matches.filter(
61191
+ (match) => match.distance === nearestDistance
61192
+ );
61193
+ if (nearest.length !== 1) {
61194
+ throw new NSGetterRuntimeError(
61195
+ `NeoDelegate method target '${target.name}' has multiple equally near receiver instances (${nearest.map((match) => match.id).join(", ")}).`
61196
+ );
61197
+ }
61198
+ row.value = { ...row.value, valueId: nearest[0].id };
61199
+ }
61200
+ }
61100
61201
  function publishConstructedRows(args) {
61101
61202
  const { root, createdValues, ctx } = args;
61102
61203
  assertConstructorRowsHaveSingleStructuralOwner(createdValues, ctx);
@@ -61158,6 +61259,7 @@ function publishConstructedRows(args) {
61158
61259
  destination.set(row.id, row);
61159
61260
  if (ctx.__indexes !== void 0) indexEvaluatorRow(ctx.__indexes, row);
61160
61261
  }
61262
+ bindConstructedDelegateTargets(root.id, ctx);
61161
61263
  state.constructorGroups.set(root.id, retained);
61162
61264
  state.ownedValueAttachments.clear();
61163
61265
  }
@@ -61855,6 +61957,7 @@ function evaluateInitializerInContext(init, member, ctx, createdValues, argument
61855
61957
  return {
61856
61958
  value: result.value,
61857
61959
  classId: rootRow.classId ?? null,
61960
+ provisionalRootId: rootRow.id,
61858
61961
  ...constructorArgs === void 0 ? {} : { constructorArgs: structuredClone(constructorArgs) }
61859
61962
  };
61860
61963
  }
@@ -62146,6 +62249,7 @@ function constructDeclaredClassValueWithinFrame(descriptor, record3, info, scope
62146
62249
  root.constructorArgs = structuredClone(encoded);
62147
62250
  }
62148
62251
  stageConstructedRows(createdValues, ctx);
62252
+ bindConstructedDelegateTargets(root.id, ctx);
62149
62253
  if (record3 !== null) {
62150
62254
  runDeclaredConstructorChain({
62151
62255
  record: record3,
@@ -63430,6 +63534,7 @@ function evaluateMemberInitializer(args) {
63430
63534
  return {
63431
63535
  value: result.value,
63432
63536
  classId: rootRow.classId ?? null,
63537
+ provisionalRootId: rootRow.id,
63433
63538
  ...constructorArgs === void 0 ? {} : { constructorArgs: structuredClone(constructorArgs) }
63434
63539
  };
63435
63540
  }
@@ -63490,6 +63595,13 @@ function materializeInitializerValue(args) {
63490
63595
  ...evaluated.classId === null ? {} : { classId: evaluated.classId },
63491
63596
  ...evaluated.constructorArgs === void 0 ? {} : { constructorArgs: structuredClone(evaluated.constructorArgs) }
63492
63597
  };
63598
+ if (evaluated.provisionalRootId !== void 0) {
63599
+ retargetDelegateReceiverValueIds(
63600
+ [root, ...createdValues],
63601
+ evaluated.provisionalRootId,
63602
+ root.id
63603
+ );
63604
+ }
63493
63605
  const allCreated = [root, ...createdValues];
63494
63606
  for (const created of createdValues) {
63495
63607
  if (created.classId === void 0) delete created.classId;
@@ -101548,7 +101660,7 @@ var init_registry2 = __esm({
101548
101660
  PROJECT_SCHEMA_CONTRACT = Object.freeze({
101549
101661
  formatVersion: 3,
101550
101662
  contractVersion: "3.9",
101551
- cliVersion: "0.24.5",
101663
+ cliVersion: "0.24.7",
101552
101664
  projectFileUploadBatchSize: 32,
101553
101665
  documentRecords: {
101554
101666
  member: {
@@ -102953,21 +103065,7 @@ function errorMessage2(error) {
102953
103065
  return error instanceof Error ? error.message : String(error);
102954
103066
  }
102955
103067
  function findDelegateTargetMemberId(value) {
102956
- if (!isRecord10(value) || !isRecord10(value.action)) return null;
102957
- const pending = [value.action.instructions];
102958
- while (pending.length > 0) {
102959
- const current = pending.shift();
102960
- if (Array.isArray(current)) {
102961
- pending.push(...current);
102962
- continue;
102963
- }
102964
- if (!isRecord10(current)) continue;
102965
- if (current.type === "callFunction" && typeof current.memberId === "string") {
102966
- return current.memberId;
102967
- }
102968
- pending.push(...Object.values(current));
102969
- }
102970
- return null;
103068
+ return isRecord10(value) && typeof value.memberId === "string" ? value.memberId : null;
102971
103069
  }
102972
103070
  function makeEvaluatorContext(rawDocument, interceptor, documentAlreadyIsolated = false) {
102973
103071
  const isolatedDocument = documentAlreadyIsolated ? rawDocument : structuredClone(rawDocument);
@@ -104083,7 +104181,7 @@ async function runTest(workspace, options, dependencies = {}) {
104083
104181
  cliVersion: PROJECT_SCHEMA_CONTRACT.cliVersion,
104084
104182
  runnerRevision: 3,
104085
104183
  compilerRevision: NEOSCRIPT_COMPILER_REVISION,
104086
- evaluatorRevision: 3,
104184
+ evaluatorRevision: 4,
104087
104185
  projectFingerprint,
104088
104186
  configurationSha256: createHash10("sha256").update(JSON.stringify(workspace.config.test ?? {})).digest("hex"),
104089
104187
  dependencyGraph: Object.fromEntries(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neocompose/cli",
3
- "version": "0.24.5",
3
+ "version": "0.24.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.24.5 -->
12
+ <!-- reviewed-through-cli: 0.24.7 -->
13
13
 
14
14
  # Neo Compose CLI
15
15
 
@@ -181,7 +181,11 @@ pulled rows.
181
181
  A selector argument such as `this.SelectPants` is evaluated in the lexical
182
182
  scope of the class that declares the clip. Here `this` is the `LegPart`, not
183
183
  the nested track row or an outer object instance that later receives a copy of
184
- the declaration default.
184
+ the declaration default. A named method group lowers to the P60 member-target
185
+ form. Its declaration default carries `valueId: null` because no instance row
186
+ exists yet; materializing the declaration binds it to the concrete `LegPart`
187
+ row. An inline closure remains compiled closure source and reconstructs its
188
+ lexical owner from the materialized graph. Do not interchange these two forms.
185
189
 
186
190
  Each track has `StartFrame`, `Direction` (`.Forward` or `.Reverse`), and a
187
191
  crop window `OffsetStartIndex`/`OffsetEndIndex`. Crop before reversing, then
@@ -82,7 +82,7 @@ wrappers.
82
82
  The marker near the top of `SKILL.md` must exactly match the package version:
83
83
 
84
84
  ```html
85
- <!-- reviewed-through-cli: 0.24.5 -->
85
+ <!-- reviewed-through-cli: 0.24.7 -->
86
86
  ```
87
87
 
88
88
  The quoted version above is checked too, so this instruction cannot go stale