@neocompose/cli 0.89.11 → 0.89.12

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,21 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.89.12
4
+
5
+ ### Patch Changes
6
+
7
+ - 6dc08d2: Preserve lexical value bindings and non-null narrowing when capturing variables in delegates, including nested closures and variables named after a class.
8
+
9
+ Validate simple member reads in declaration defaults, including missing members, nullable receivers, and inaccessible static members.
10
+
11
+ Rename interface members together with their implementations and overrides, preserving unrelated overloads.
12
+
13
+ Complete delegate parameters inside tuples and named constructor overloads, and restore member completion in switch guards.
14
+
15
+ - 444e57d: Keep generated child-override generic bindings in local exports when candidate preparation reuses a mutable source baseline.
16
+
17
+ Clone owned tuple elements with fresh identities so modifying a copied child does not change the original.
18
+
3
19
  ## 0.89.11
4
20
 
5
21
  ### Patch Changes
@@ -19377,7 +19377,7 @@ var init_registry = __esm({
19377
19377
  "../packages/manifest/schema-contract/registry.mjs"() {
19378
19378
  "use strict";
19379
19379
  PROJECT_SCHEMA_CONTRACT = Object.freeze({
19380
- cliVersion: "0.89.11",
19380
+ cliVersion: "0.89.12",
19381
19381
  projectFileUploadBatchSize: 32,
19382
19382
  documentRecords: {
19383
19383
  member: {
@@ -54884,11 +54884,13 @@ function cloneClassValueGraph(receiver, expected, ctx) {
54884
54884
  );
54885
54885
  clonedRecord[key] = clonedChild.id;
54886
54886
  }
54887
- } else if (Array.isArray(sourceRow.value) && (isMemberEntryCollectionBase(sourceMember) || sourceTypeInfo?.type === 6 /* List */ || sourceTypeInfo?.type === 28 /* Set */)) {
54887
+ } else if (Array.isArray(sourceRow.value) && (isMemberEntryCollectionBase(sourceMember) || isMemberTupleBase(sourceMember) || sourceTypeInfo?.type === 30 /* Tuple */ || sourceTypeInfo?.type === 6 /* List */ || sourceTypeInfo?.type === 28 /* Set */)) {
54888
54888
  const entryMember = isMemberEntryCollectionBase(sourceMember) ? evalMemberById(ctx.vm, sourceMember.entryMemberId) : null;
54889
54889
  const entryTypeInfo = sourceTypeInfo?.type === 6 /* List */ || sourceTypeInfo?.type === 28 /* Set */ ? sourceTypeInfo.entryTypeInfo : void 0;
54890
- if (entryMember !== null || entryTypeInfo !== void 0) {
54891
- clone.value = sourceRow.value.map((childId) => {
54890
+ const tuple = isMemberTupleBase(sourceMember) ? sourceMember : null;
54891
+ const tupleType = sourceTypeInfo?.type === 30 /* Tuple */ ? sourceTypeInfo : null;
54892
+ if (entryMember !== null || entryTypeInfo !== void 0 || tuple !== null || tupleType !== null) {
54893
+ clone.value = sourceRow.value.map((childId, index) => {
54892
54894
  if (typeof childId !== "string") return childId;
54893
54895
  const child2 = evalValueById(
54894
54896
  ctx,
@@ -54897,11 +54899,17 @@ function cloneClassValueGraph(receiver, expected, ctx) {
54897
54899
  ctx.__valueOverlay
54898
54900
  );
54899
54901
  if (child2 === null) return childId;
54902
+ const elementMember = tuple === null ? entryMember : evalMemberById(ctx.vm, tuple.elementMemberIds[index]);
54903
+ const elementType = tupleType === null ? entryTypeInfo : tupleType.elements[index]?.typeInfo;
54904
+ if (elementMember === null && elementType === void 0)
54905
+ throw new NSGetterRuntimeError(
54906
+ `Class.Clone tuple element ${index} has no type descriptor.`
54907
+ );
54900
54908
  return cloneRow2(
54901
54909
  child2,
54902
- entryMember,
54910
+ elementMember,
54903
54911
  child2.containerId === sourceRow.id ? id2 : void 0,
54904
- entryTypeInfo
54912
+ elementType
54905
54913
  ).id;
54906
54914
  });
54907
54915
  ctx.onUnattributableValueRead?.(containerEntriesRead(sourceRow.id));
@@ -75264,9 +75272,10 @@ var init_strict_resolver = __esm({
75264
75272
  if (outerEntry === null) return null;
75265
75273
  const existing = context.byOuterEntry.get(outerEntry);
75266
75274
  if (existing !== void 0) return existing;
75275
+ const capturedType = context.outerScope.lookupNarrowedType(name, outerEntry) ?? outerEntry.type;
75267
75276
  const parameter4 = variable(
75268
75277
  `__capture_${context.serial}_${context.captures.length}__`,
75269
- outerEntry.type,
75278
+ capturedType,
75270
75279
  void 0,
75271
75280
  this.project
75272
75281
  );
@@ -75274,7 +75283,7 @@ var init_strict_resolver = __esm({
75274
75283
  ...scopeVariable(
75275
75284
  name,
75276
75285
  parameter4,
75277
- outerEntry.type,
75286
+ capturedType,
75278
75287
  "local" /* Local */,
75279
75288
  "local" /* Local */,
75280
75289
  outerEntry.writeRoot,
@@ -83014,7 +83023,9 @@ var init_strict_resolver = __esm({
83014
83023
  return literal(symbol.type, null);
83015
83024
  }
83016
83025
  hasValueReceiver(name, scope) {
83017
- return scope.lookup(name) !== null || this.implicitMember(name) !== null;
83026
+ return scope.lookup(name) !== null || this.delegateCaptureContexts.some(
83027
+ (context) => context.outerScope.lookup(name) !== null
83028
+ ) || this.implicitMember(name) !== null;
83018
83029
  }
83019
83030
  implicitMember(name) {
83020
83031
  if (this.context.implicitMemberAccess !== true) return null;
@@ -99500,7 +99511,8 @@ function validateExpression(expression, expected, scope, environment, uri, range
99500
99511
  const sourceOnly = expected !== void 0 && (isNeoVariantTypeName(expected.name) || expected.name === "NeoAction" || expected.name === "ImageRef");
99501
99512
  let receiver = expression;
99502
99513
  while (receiver.kind === "member") receiver = receiver.receiver;
99503
- const computed = (environment.checkComputedInitializers || expected?.name === "NeoDelegate") && !sourceOnly && (expression.kind === "member" && (receiver.kind !== "ident" || receiver.typeQualifier !== void 0) || [
99514
+ const executableMember = expression.kind === "member" && (receiver.kind !== "ident" || receiver.typeQualifier !== void 0);
99515
+ const computed = (environment.checkComputedInitializers || expected?.name === "NeoDelegate") && !sourceOnly && (executableMember || expression.kind === "member" && expected?.name !== "NeoDelegate" || [
99504
99516
  "binary",
99505
99517
  "unary",
99506
99518
  "force",
@@ -99549,14 +99561,13 @@ function validateExpression(expression, expected, scope, environment, uri, range
99549
99561
  void 0,
99550
99562
  { allowPendingRegistryEntries: true }
99551
99563
  );
99552
- scopedResolver.validateInitializerExpression(
99553
- expression,
99554
- expected === void 0 ? void 0 : resolveNeoScriptType(
99555
- semanticAst(expected, expression.pos),
99556
- context,
99557
- environment.projectIndex
99558
- )
99564
+ const storedMember = storedValue && expression.kind === "member" && !executableMember;
99565
+ const expectedType = expected === void 0 || storedMember ? void 0 : resolveNeoScriptType(
99566
+ semanticAst(expected, expression.pos),
99567
+ context,
99568
+ environment.projectIndex
99559
99569
  );
99570
+ scopedResolver.validateInitializerExpression(expression, expectedType);
99560
99571
  } catch (error) {
99561
99572
  if (!(error instanceof CompileError)) throw error;
99562
99573
  pushDiagnostic(
@@ -104061,6 +104072,7 @@ function validateProjectSourceTypes(documents) {
104061
104072
  validateSetSpelling(documents, environment.typeKinds, diagnostics);
104062
104073
  return {
104063
104074
  diagnostics,
104075
+ memberRelations: environment.memberRelations,
104064
104076
  overloadImplementations: overloadImplementations(environment)
104065
104077
  };
104066
104078
  }
@@ -104137,6 +104149,7 @@ function createEnvironment2(documents) {
104137
104149
  }
104138
104150
  return {
104139
104151
  declarations,
104152
+ memberRelations: [],
104140
104153
  classes,
104141
104154
  interfaces,
104142
104155
  memberOwners,
@@ -105317,11 +105330,12 @@ function validateMembers(environment, diagnostics) {
105317
105330
  }
105318
105331
  function validateInterfaceMemberConflicts(ref, environment, diagnostics) {
105319
105332
  const inherited = /* @__PURE__ */ new Map();
105333
+ const substitution = identitySubstitution(ref);
105320
105334
  for (const base of ref.declaration.baseTypes) {
105321
105335
  const target = environment.interfaces.get(base.name);
105322
105336
  if (!target) continue;
105323
105337
  for (const candidate of interfaceMembers(
105324
- { owner: target, substitution: /* @__PURE__ */ new Map() },
105338
+ interfaceView(base, target, substitution),
105325
105339
  environment
105326
105340
  )) {
105327
105341
  const key = candidate.member.name.toLowerCase();
@@ -105338,6 +105352,10 @@ function validateInterfaceMemberConflicts(ref, environment, diagnostics) {
105338
105352
  "interface-inherited",
105339
105353
  environment
105340
105354
  ) === null) {
105355
+ environment.memberRelations.push([
105356
+ interfaceMemberLocation(prior),
105357
+ interfaceMemberLocation(candidate)
105358
+ ]);
105341
105359
  continue;
105342
105360
  }
105343
105361
  diagnose(
@@ -105359,7 +105377,6 @@ function validateInterfaceMemberConflicts(ref, environment, diagnostics) {
105359
105377
  );
105360
105378
  }
105361
105379
  }
105362
- const substitution = identitySubstitution(ref);
105363
105380
  for (const member of ref.declaration.members) {
105364
105381
  const prior = inherited.get(member.name.toLowerCase());
105365
105382
  if (!prior) continue;
@@ -105368,12 +105385,18 @@ function validateInterfaceMemberConflicts(ref, environment, diagnostics) {
105368
105385
  }
105369
105386
  const mismatch = memberCompatibility(
105370
105387
  member,
105371
- /* @__PURE__ */ new Map(),
105388
+ substitution,
105372
105389
  prior,
105373
105390
  "interface-inherited",
105374
105391
  environment
105375
105392
  );
105376
- if (!mismatch) continue;
105393
+ if (!mismatch) {
105394
+ environment.memberRelations.push([
105395
+ interfaceMemberLocation(prior),
105396
+ { uri: ref.uri, range: member.nameRange }
105397
+ ]);
105398
+ continue;
105399
+ }
105377
105400
  diagnose(
105378
105401
  diagnostics,
105379
105402
  ref.uri,
@@ -105405,7 +105428,13 @@ function validateAbstractInterfaceMembers(ref, environment, diagnostics) {
105405
105428
  "interface-contract",
105406
105429
  environment
105407
105430
  );
105408
- if (!mismatch) continue;
105431
+ if (!mismatch) {
105432
+ environment.memberRelations.push([
105433
+ interfaceMemberLocation(contract),
105434
+ memberLocation(implementation)
105435
+ ]);
105436
+ continue;
105437
+ }
105409
105438
  diagnose(
105410
105439
  diagnostics,
105411
105440
  ref.uri,
@@ -106447,6 +106476,10 @@ function validateConcreteClass(ref, environment, diagnostics) {
106447
106476
  environment
106448
106477
  ) : null;
106449
106478
  if (implementation && !implementation.member.modifiers.includes("abstract") && !implementation.member.modifiers.includes("static") && mismatch === null) {
106479
+ environment.memberRelations.push([
106480
+ interfaceMemberLocation(required2),
106481
+ memberLocation(implementation)
106482
+ ]);
106450
106483
  continue;
106451
106484
  }
106452
106485
  diagnose(
@@ -107561,6 +107594,7 @@ function analyzeNeoProjectSources(inputs, parsedDocuments = /* @__PURE__ */ new
107561
107594
  compiledBodies: bodyCompilation.bodies,
107562
107595
  bodyContexts: bodyCompilation.contexts,
107563
107596
  overloadImplementations: typeValidation.overloadImplementations,
107597
+ memberRelations: typeValidation.memberRelations,
107564
107598
  diagnostics
107565
107599
  };
107566
107600
  }
@@ -149329,9 +149363,8 @@ function materializeVariantChildOverrideBindingMembers(args) {
149329
149363
  classId
149330
149364
  );
149331
149365
  args.serverMintedBindingMemberIds?.add(member.id);
149332
- if (isFrozenJsonData(args.document.members))
149333
- args.document.members = [...args.document.members, member];
149334
- else args.document.members.push(member);
149366
+ if (minted.size === 0) args.document.members = [...args.document.members];
149367
+ args.document.members.push(member);
149335
149368
  args.prepared.push({
149336
149369
  recordKind: "member",
149337
149370
  recordId: member.id,
@@ -149354,6 +149387,7 @@ function materializeStaticSeedBindingMembers(args) {
149354
149387
  if (ids.size !== bindings.length) {
149355
149388
  throw new Error("Schema commit contains duplicate generic binding seeds.");
149356
149389
  }
149390
+ if (bindings.length > 0) args.document.members = [...args.document.members];
149357
149391
  for (const binding of bindings) {
149358
149392
  const expectedBindingId = worldAnimationChildOverrideBindingMemberId(
149359
149393
  binding.classId
@@ -149378,9 +149412,7 @@ function materializeStaticSeedBindingMembers(args) {
149378
149412
  binding.classId
149379
149413
  );
149380
149414
  args.serverMintedBindingMemberIds?.add(member.id);
149381
- if (isFrozenJsonData(args.document.members))
149382
- args.document.members = [...args.document.members, member];
149383
- else args.document.members.push(member);
149415
+ args.document.members.push(member);
149384
149416
  args.prepared.push({
149385
149417
  recordKind: "member",
149386
149418
  recordId: member.id,
@@ -151663,7 +151695,6 @@ var init_project_version_schema_commit = __esm({
151663
151695
  "../src/database/project-version-schema-commit.ts"() {
151664
151696
  "use strict";
151665
151697
  init_members();
151666
- init_immutable_json2();
151667
151698
  init_commit_preparation_pass_cache();
151668
151699
  init_compiled_construction_types();
151669
151700
  init_member_kinds();
@@ -189603,7 +189634,7 @@ function scriptFeatureIndex(snapshot) {
189603
189634
  typeAnnotations,
189604
189635
  tokens,
189605
189636
  tokenIndexes,
189606
- enclosingOpener: enclosingOpenerIndex(tokens),
189637
+ enclosingOpener: receiverEnclosingOpeners(tokens),
189607
189638
  objectInitializers
189608
189639
  };
189609
189640
  SCRIPT_FEATURE_INDEX.set(snapshot, index);
@@ -192574,7 +192605,7 @@ function flowProbe(snapshot) {
192574
192605
  const tokens = scriptFeatureIndex(snapshot).tokens;
192575
192606
  const overloaded = overloadedFunctionNames(snapshot.project);
192576
192607
  if (!tokens.some(
192577
- (token) => token.text === "is" || token.text === "switch" || overloaded.has(token.text)
192608
+ (token) => token.text === "is" || token.text === "=>" || token.text === "switch" || overloaded.has(token.text)
192578
192609
  ) && !hasGenericCall(snapshot))
192579
192610
  return result;
192580
192611
  const text = snapshot.source.text;
@@ -193921,6 +193952,8 @@ function expressionTokensBefore(tokens, endExclusive) {
193921
193952
  start = name;
193922
193953
  continue;
193923
193954
  }
193955
+ if (depth === 0 && token.text === "when" && start < endExclusive - 1 && ![".", "?.", "!", "[", "<"].includes(tokens[start + 1]?.text ?? "") && followsSwitchPattern(tokens, start))
193956
+ break;
193924
193957
  if (depth === 0 && (token.kind === "punctuation" && [";", "{", "}", ",", ":", "?"].includes(token.text) || // Only `?.` and `!` continue a receiver; every other operator
193925
193958
  // (`+=`, `==`, `<`, …) ends it.
193926
193959
  token.kind === "operator" && token.text !== "?." && token.text !== "!" || token.kind === "keyword" && ["return", "throw", "if", "else"].includes(token.text))) {
@@ -193929,6 +193962,24 @@ function expressionTokensBefore(tokens, endExclusive) {
193929
193962
  }
193930
193963
  return tokens.slice(start + 1, endExclusive);
193931
193964
  }
193965
+ function receiverEnclosingOpeners(tokens) {
193966
+ let index = RECEIVER_ENCLOSING_OPENERS.get(tokens);
193967
+ if (!index) {
193968
+ index = enclosingOpenerIndex(tokens);
193969
+ RECEIVER_ENCLOSING_OPENERS.set(tokens, index);
193970
+ }
193971
+ return index;
193972
+ }
193973
+ function followsSwitchPattern(tokens, index) {
193974
+ const opener = receiverEnclosingOpeners(tokens)[index] ?? -1;
193975
+ if (tokens[opener]?.text !== "{" || tokens[opener - 1]?.text !== "switch")
193976
+ return false;
193977
+ const previous = tokens[index - 1];
193978
+ if (!previous) return false;
193979
+ if (previous.text === "when")
193980
+ return ["{", ","].includes(tokens[index - 2]?.text ?? "");
193981
+ return previous.kind === "identifier" || previous.kind === "integer" || previous.kind === "float" || previous.kind === "type" || previous.kind === "string" || [")", "]", ">", "true", "false", "null"].includes(previous.text);
193982
+ }
193932
193983
  function genericCallNameIndex(tokens, close) {
193933
193984
  if (!["(", ".", "{"].includes(tokens[close + 1]?.text ?? "")) return -1;
193934
193985
  let depth = 0;
@@ -194082,7 +194133,7 @@ function isTypeAnnotationReference(snapshot, reference2) {
194082
194133
  function isConstructorTypeReference(snapshot, reference2) {
194083
194134
  return scriptFeatureIndex(snapshot).calls.get(reference2.start)?.kind === "constructor";
194084
194135
  }
194085
- var SCRIPT_FEATURE_INDEX, assumedLambdaInputs, LIST_OPENERS, LIST_CLOSERS, TUPLE_LABEL_DECLARATIONS, NAMED_ARGUMENT_LABELS, NO_EXPECTED_VALUE, CONSTRUCTIONS, ANNOTATION_TYPE_NAMES, FLOW_PROBES, PATTERN_DECLARATIONS, OVERLOADED_FUNCTION_NAMES, GENERIC_CALL_NAMES, LOCAL_SCOPE_INDEX, NAMED_ITEM_INDEX, COLLECTION_FUNCTION_DOCUMENTATION, LOCAL_SYMBOL_CACHE, CALL_COMMIT_CHARACTERS, GENERIC_CALL_COMMIT_CHARACTERS, SETTER_VALUE_ID_SUFFIX, LAMBDA_BODY_INDEX;
194136
+ var SCRIPT_FEATURE_INDEX, assumedLambdaInputs, LIST_OPENERS, LIST_CLOSERS, TUPLE_LABEL_DECLARATIONS, NAMED_ARGUMENT_LABELS, NO_EXPECTED_VALUE, CONSTRUCTIONS, ANNOTATION_TYPE_NAMES, FLOW_PROBES, PATTERN_DECLARATIONS, OVERLOADED_FUNCTION_NAMES, GENERIC_CALL_NAMES, LOCAL_SCOPE_INDEX, NAMED_ITEM_INDEX, COLLECTION_FUNCTION_DOCUMENTATION, LOCAL_SYMBOL_CACHE, CALL_COMMIT_CHARACTERS, GENERIC_CALL_COMMIT_CHARACTERS, SETTER_VALUE_ID_SUFFIX, RECEIVER_ENCLOSING_OPENERS, LAMBDA_BODY_INDEX;
194086
194137
  var init_analyzer = __esm({
194087
194138
  "../packages/neoscript-language/src/analyzer.ts"() {
194088
194139
  "use strict";
@@ -194162,6 +194213,7 @@ var init_analyzer = __esm({
194162
194213
  CALL_COMMIT_CHARACTERS = ["("];
194163
194214
  GENERIC_CALL_COMMIT_CHARACTERS = ["<", "("];
194164
194215
  SETTER_VALUE_ID_SUFFIX = ":setter-value";
194216
+ RECEIVER_ENCLOSING_OPENERS = /* @__PURE__ */ new WeakMap();
194165
194217
  LAMBDA_BODY_INDEX = /* @__PURE__ */ new WeakMap();
194166
194218
  }
194167
194219
  });
@@ -199770,19 +199822,22 @@ function projectReferenceFamily(analysis, target, index = buildProjectSymbolInde
199770
199822
  }
199771
199823
  if (target.kind !== "member" || !target.ownerName) return [target];
199772
199824
  const classes = /* @__PURE__ */ new Map();
199773
- for (const document of analysis.documents.values()) {
199825
+ const declarations = /* @__PURE__ */ new Map();
199826
+ for (const [uri, document] of analysis.documents) {
199774
199827
  for (const declaration of document.declarations) {
199775
- if (declaration.kind === "class")
199776
- classes.set(declaration.name, declaration);
199828
+ if (declaration.kind !== "class") continue;
199829
+ classes.set(declaration.name, declaration);
199830
+ for (const member of declaration.members)
199831
+ declarations.set(locationKey3({ uri, range: member.nameRange }), member);
199777
199832
  }
199778
199833
  }
199779
199834
  const joins = (member) => target.callable !== true || member?.kind === "function" && member.modifiers.some((modifier) => DISPATCH_MODIFIERS2.has(modifier));
199780
- const declared = (symbol) => classes.get(symbol.ownerName ?? "")?.members.find(
199781
- (member) => member.name === symbol.name && member.nameRange.start.line === symbol.location.range.start.line && member.nameRange.start.character === symbol.location.range.start.character
199835
+ const declared = (symbol) => declarations.get(locationKey3(symbol.location));
199836
+ const members2 = analysis.symbols.filter(
199837
+ (symbol) => symbol.kind === "member" && symbol.name.toLowerCase() === target.name.toLowerCase()
199782
199838
  );
199783
- if (!joins(declared(target))) return [target];
199784
199839
  const symbols = new Map(
199785
- named.filter((symbol) => symbol.kind === "member" && joins(declared(symbol))).map((symbol) => [symbol.ownerName, symbol])
199840
+ members2.filter((symbol) => symbol.kind === "member" && joins(declared(symbol))).map((symbol) => [symbol.ownerName, symbol])
199786
199841
  );
199787
199842
  const roots = /* @__PURE__ */ new Map();
199788
199843
  const root = (name) => {
@@ -199790,9 +199845,7 @@ function projectReferenceFamily(analysis, target, index = buildProjectSymbolInde
199790
199845
  roots.set(name, null);
199791
199846
  const declaration = classes.get(name);
199792
199847
  const own = symbols.get(name);
199793
- const member = declaration?.members.find(
199794
- (candidate) => candidate.name === target.name && joins(candidate)
199795
- );
199848
+ const member = own ? declared(own) : void 0;
199796
199849
  if (own && !member?.modifiers.includes("override")) {
199797
199850
  roots.set(name, own);
199798
199851
  return own;
@@ -199802,11 +199855,43 @@ function projectReferenceFamily(analysis, target, index = buildProjectSymbolInde
199802
199855
  roots.set(name, resolved);
199803
199856
  return resolved;
199804
199857
  };
199805
- const selected = root(target.ownerName);
199806
- if (!selected) return [target];
199807
- return [...symbols.values()].filter(
199808
- (symbol) => symbol.ownerName && root(symbol.ownerName) === selected
199858
+ const byLocation = new Map(
199859
+ members2.map((symbol) => [locationKey3(symbol.location), symbol])
199809
199860
  );
199861
+ const edges = /* @__PURE__ */ new Map();
199862
+ const connect = (a, b) => {
199863
+ const left = locationKey3(a);
199864
+ const right = locationKey3(b);
199865
+ if (!byLocation.has(left) || !byLocation.has(right)) return;
199866
+ const add = (from, to) => {
199867
+ let values = edges.get(from);
199868
+ if (!values) edges.set(from, values = /* @__PURE__ */ new Set());
199869
+ values.add(to);
199870
+ };
199871
+ add(left, right);
199872
+ add(right, left);
199873
+ };
199874
+ for (const symbol of symbols.values()) {
199875
+ const selected = symbol.ownerName ? root(symbol.ownerName) : null;
199876
+ if (selected) connect(symbol.location, selected.location);
199877
+ }
199878
+ for (const [contract, implementation] of analysis.memberRelations)
199879
+ connect(contract, implementation);
199880
+ const visited = /* @__PURE__ */ new Set();
199881
+ const pending = [locationKey3(target.location)];
199882
+ const family = [];
199883
+ while (pending.length > 0) {
199884
+ const key = pending.pop();
199885
+ if (visited.has(key)) continue;
199886
+ visited.add(key);
199887
+ const symbol = byLocation.get(key);
199888
+ if (symbol) family.push(symbol);
199889
+ for (const next of edges.get(key) ?? []) pending.push(next);
199890
+ }
199891
+ return family.length > 0 ? family : [target];
199892
+ }
199893
+ function locationKey3(location3) {
199894
+ return `${location3.uri}:${location3.range.start.line}:${location3.range.start.character}`;
199810
199895
  }
199811
199896
  var DISPATCH_MODIFIERS2;
199812
199897
  var init_project_source_reference_family = __esm({
@@ -200069,7 +200154,7 @@ function sameLocationInList(target, locations) {
200069
200154
  (location3) => location3.uri === target.uri && location3.range.start.line === target.range.start.line && location3.range.start.character === target.range.start.character
200070
200155
  );
200071
200156
  }
200072
- function locationKey3(location3) {
200157
+ function locationKey4(location3) {
200073
200158
  const { start, end } = location3.range;
200074
200159
  return JSON.stringify([
200075
200160
  location3.uri,
@@ -200636,8 +200721,9 @@ var init_service = __esm({
200636
200721
  if (!resolved) return [];
200637
200722
  const declaration = resolved.symbol;
200638
200723
  const family = projectReferenceFamily(analysis, declaration, index);
200724
+ const familyNames = new Set(family.map((member) => member.name));
200639
200725
  const familyLocations = new Set(
200640
- family.map((member) => locationKey3(member.location))
200726
+ family.map((member) => locationKey4(member.location))
200641
200727
  );
200642
200728
  const familyIds = new Set(
200643
200729
  family.flatMap((member) => member.id === null ? [] : [member.id])
@@ -200652,7 +200738,7 @@ var init_service = __esm({
200652
200738
  for (let tokenIndex = 0; tokenIndex < tokens.length; tokenIndex++) {
200653
200739
  const candidate = tokens[tokenIndex];
200654
200740
  if (!isProjectNameToken(candidate)) continue;
200655
- const matches = parameterName === null ? candidate.text === token.text : candidate.text === declaration.name || candidate.text.toLowerCase() === parameterName && isNamedArgumentLabel(tokens, tokenIndex);
200741
+ const matches = parameterName === null ? familyNames.has(candidate.text) : candidate.text === declaration.name || candidate.text.toLowerCase() === parameterName && isNamedArgumentLabel(tokens, tokenIndex);
200656
200742
  if (!matches) continue;
200657
200743
  const candidateSymbol = projectSymbolAt(
200658
200744
  analysis,
@@ -200660,11 +200746,11 @@ var init_service = __esm({
200660
200746
  candidate.range.start,
200661
200747
  index
200662
200748
  )?.symbol;
200663
- if (!candidateSymbol || !familyLocations.has(locationKey3(candidateSymbol.location))) {
200749
+ if (!candidateSymbol || !familyLocations.has(locationKey4(candidateSymbol.location))) {
200664
200750
  continue;
200665
200751
  }
200666
200752
  const location3 = { uri: state.document.uri, range: candidate.range };
200667
- if (!includeDeclaration && familyLocations.has(locationKey3(location3))) {
200753
+ if (!includeDeclaration && familyLocations.has(locationKey4(location3))) {
200668
200754
  continue;
200669
200755
  }
200670
200756
  locations.push(location3);
@@ -200675,11 +200761,11 @@ var init_service = __esm({
200675
200761
  if (isProjectDocument(state.document, state.context)) continue;
200676
200762
  const snapshot = this.scriptSnapshot(state);
200677
200763
  for (const candidate of snapshot.parsed.references) {
200678
- if (candidate.name !== declaration.name) continue;
200764
+ if (!familyNames.has(candidate.name)) continue;
200679
200765
  const symbolId = resolveReferenceSymbolId(snapshot, candidate);
200680
200766
  if (symbolId === null) continue;
200681
200767
  const matches = familyIds.has(symbolId) || definition(snapshot, candidate.range.start).some(
200682
- (location3) => familyLocations.has(locationKey3(location3))
200768
+ (location3) => familyLocations.has(locationKey4(location3))
200683
200769
  );
200684
200770
  if (!matches) continue;
200685
200771
  locations.push({ uri: state.document.uri, range: candidate.range });
@@ -200687,7 +200773,7 @@ var init_service = __esm({
200687
200773
  if (declaration.kind !== "parameter") continue;
200688
200774
  for (const label of namedArgumentLabels(snapshot)) {
200689
200775
  const parameter4 = label.parameter.selectionLocation;
200690
- if (parameter4 && familyLocations.has(locationKey3(parameter4)))
200776
+ if (parameter4 && familyLocations.has(locationKey4(parameter4)))
200691
200777
  locations.push({ uri: state.document.uri, range: label.range });
200692
200778
  }
200693
200779
  }
@@ -243072,7 +243158,7 @@ var releaseWorkspaceLock = () => void 0;
243072
243158
  async function main() {
243073
243159
  const args = parseArgs(process.argv.slice(2));
243074
243160
  if (args.command === "--version") {
243075
- console.log("0.89.11");
243161
+ console.log("0.89.12");
243076
243162
  return;
243077
243163
  }
243078
243164
  if (args.command === null || args.command === "help" || args.command === "--help" || args.command === "-h") {
@@ -41035,11 +41035,13 @@ function cloneClassValueGraph(receiver, expected, ctx) {
41035
41035
  );
41036
41036
  clonedRecord[key] = clonedChild.id;
41037
41037
  }
41038
- } else if (Array.isArray(sourceRow.value) && (isMemberEntryCollectionBase(sourceMember) || sourceTypeInfo?.type === 6 /* List */ || sourceTypeInfo?.type === 28 /* Set */)) {
41038
+ } else if (Array.isArray(sourceRow.value) && (isMemberEntryCollectionBase(sourceMember) || isMemberTupleBase(sourceMember) || sourceTypeInfo?.type === 30 /* Tuple */ || sourceTypeInfo?.type === 6 /* List */ || sourceTypeInfo?.type === 28 /* Set */)) {
41039
41039
  const entryMember = isMemberEntryCollectionBase(sourceMember) ? evalMemberById(ctx.vm, sourceMember.entryMemberId) : null;
41040
41040
  const entryTypeInfo = sourceTypeInfo?.type === 6 /* List */ || sourceTypeInfo?.type === 28 /* Set */ ? sourceTypeInfo.entryTypeInfo : void 0;
41041
- if (entryMember !== null || entryTypeInfo !== void 0) {
41042
- clone.value = sourceRow.value.map((childId) => {
41041
+ const tuple = isMemberTupleBase(sourceMember) ? sourceMember : null;
41042
+ const tupleType = sourceTypeInfo?.type === 30 /* Tuple */ ? sourceTypeInfo : null;
41043
+ if (entryMember !== null || entryTypeInfo !== void 0 || tuple !== null || tupleType !== null) {
41044
+ clone.value = sourceRow.value.map((childId, index) => {
41043
41045
  if (typeof childId !== "string") return childId;
41044
41046
  const child = evalValueById(
41045
41047
  ctx,
@@ -41048,11 +41050,17 @@ function cloneClassValueGraph(receiver, expected, ctx) {
41048
41050
  ctx.__valueOverlay
41049
41051
  );
41050
41052
  if (child === null) return childId;
41053
+ const elementMember = tuple === null ? entryMember : evalMemberById(ctx.vm, tuple.elementMemberIds[index]);
41054
+ const elementType = tupleType === null ? entryTypeInfo : tupleType.elements[index]?.typeInfo;
41055
+ if (elementMember === null && elementType === void 0)
41056
+ throw new NSGetterRuntimeError(
41057
+ `Class.Clone tuple element ${index} has no type descriptor.`
41058
+ );
41051
41059
  return cloneRow2(
41052
41060
  child,
41053
- entryMember,
41061
+ elementMember,
41054
41062
  child.containerId === sourceRow.id ? id : void 0,
41055
- entryTypeInfo
41063
+ elementType
41056
41064
  ).id;
41057
41065
  });
41058
41066
  ctx.onUnattributableValueRead?.(containerEntriesRead(sourceRow.id));
@@ -59025,9 +59033,10 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
59025
59033
  if (outerEntry === null) return null;
59026
59034
  const existing = context.byOuterEntry.get(outerEntry);
59027
59035
  if (existing !== void 0) return existing;
59036
+ const capturedType = context.outerScope.lookupNarrowedType(name, outerEntry) ?? outerEntry.type;
59028
59037
  const parameter3 = variable(
59029
59038
  `__capture_${context.serial}_${context.captures.length}__`,
59030
- outerEntry.type,
59039
+ capturedType,
59031
59040
  void 0,
59032
59041
  this.project
59033
59042
  );
@@ -59035,7 +59044,7 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
59035
59044
  ...scopeVariable(
59036
59045
  name,
59037
59046
  parameter3,
59038
- outerEntry.type,
59047
+ capturedType,
59039
59048
  "local" /* Local */,
59040
59049
  "local" /* Local */,
59041
59050
  outerEntry.writeRoot,
@@ -66775,7 +66784,9 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
66775
66784
  return literal(symbol.type, null);
66776
66785
  }
66777
66786
  hasValueReceiver(name, scope) {
66778
- return scope.lookup(name) !== null || this.implicitMember(name) !== null;
66787
+ return scope.lookup(name) !== null || this.delegateCaptureContexts.some(
66788
+ (context) => context.outerScope.lookup(name) !== null
66789
+ ) || this.implicitMember(name) !== null;
66779
66790
  }
66780
66791
  implicitMember(name) {
66781
66792
  if (this.context.implicitMemberAccess !== true) return null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neocompose/cli",
3
- "version": "0.89.11",
3
+ "version": "0.89.12",
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.89.11 -->
12
+ <!-- reviewed-through-cli: 0.89.12 -->
13
13
 
14
14
  # Neo Compose CLI
15
15
 
@@ -143,7 +143,7 @@ The marker near the top of `SKILL.md` must exactly match the package version,
143
143
  and this quoted copy is checked too:
144
144
 
145
145
  ```html
146
- <!-- reviewed-through-cli: 0.89.11 -->
146
+ <!-- reviewed-through-cli: 0.89.12 -->
147
147
  ```
148
148
 
149
149
  Keep this skill on current behavior. Upgrade steps, rollout prerequisites,
@@ -43,6 +43,9 @@ Generic bindings and their nested collection descriptors follow the owning
43
43
  declaration's identity. Deleting and recreating an idless declaration creates
44
44
  fresh bindings; retaining its pulled IDs preserves them.
45
45
 
46
+ Editor rename follows an interface member through its compatible implementations
47
+ and overrides. Unrelated members and overloads keep their names.
48
+
46
49
  ## Modifiers
47
50
 
48
51
  `public`, `protected`, `private`, `static`, `virtual`, `abstract`, `sealed`,
@@ -36,6 +36,8 @@ indentation, using the project's configured spaces or tabs.
36
36
  The editor checks computed defaults with the same expression resolver as push.
37
37
  Unknown names, missing members, invalid type arguments, and expression type
38
38
  errors appear at the offending initializer expression, including nested defaults.
39
+ This includes simple member reads such as `value.Name`: nullable receivers and
40
+ private or protected access receive the same checks as executable bodies.
39
41
 
40
42
  A default that isn't a closed literal stores as a computed initializer and
41
43
  replays for omitted members in push, web, and SDK. A stored override stays
@@ -149,3 +149,5 @@ computed properties and callable signatures for cursors; store `List<T>` data
149
149
  explicitly. Stored enumerable interface members accept authored class rows.
150
150
  Runtime sequences are shown as unenumerated and are not expanded for previews.
151
151
  `ToSet()` rejects enumerable/cursor interface elements.
152
+
153
+ `Clone()` also copies owned class values inside tuples. References inside the copied graph keep their targets.
@@ -11,7 +11,12 @@ List<Item> ready = this.Items.Where((item) => item.Count > 0);
11
11
  ```
12
12
 
13
13
  - Lambdas may be returned, stored, or assigned to typed locals. They capture
14
- surrounding locals and parameters by value when created.
14
+ surrounding locals and parameters by value when created. A non-null check
15
+ on a local remains valid inside its capture, even if the outer local is later
16
+ reassigned. This does not freeze mutable fields on captured objects.
17
+ A captured value can share a class name; member access uses the value.
18
+ - Editor completion uses the delegate parameter type inside tuple values and
19
+ named constructor arguments, including overloaded constructors.
15
20
  - `(x) => x * 2` equals `(x) => { return x * 2; }`. In a void delegate the
16
21
  expression must call or assign. Parenthesize a dictionary-literal body:
17
22
  `() => ({ "a": 1 })`.
@@ -64,6 +64,10 @@ frame. For editing an existing clip, see [frame overrides](animation.md).
64
64
  as instance overrides, and `ToVariant(Base)` removes the applied layer.
65
65
  - On an object nested inside another instance's construction, `ToVariant`
66
66
  applies in place and records nothing.
67
+ - Child overrides also answer omitted fields on children with their own
68
+ constructor recipes. The child keeps its identity and unanswered defaults.
69
+ Later instance writes remain pins until an answering variant transition clears
70
+ them. Structured partials change only the named components.
67
71
  - Selections compare equal when class, variant, and bound lookup row match.
68
72
  An unset optional selection differs from `Base`.
69
73