@neocompose/cli 0.72.2 → 0.72.4

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.
@@ -33780,14 +33780,17 @@ var Scope = class {
33780
33780
  return this.invalidatedRoots;
33781
33781
  }
33782
33782
  effectiveNarrowedPaths() {
33783
- const effective = this.parent ? new Map(this.parent.effectiveNarrowedPaths()) : /* @__PURE__ */ new Map();
33784
- for (const invalidated of this.invalidatedRoots) {
33785
- for (const [path, narrowed] of effective) {
33786
- if (narrowed.rootEntry === invalidated) effective.delete(path);
33783
+ const effective = new Map(this.narrowedPaths);
33784
+ const invalidated = new Set(this.invalidatedRoots);
33785
+ for (const root of this.hiddenNarrowingRoots) invalidated.add(root);
33786
+ for (let current = this.parent; current; current = current.parent) {
33787
+ for (const [path, narrowed] of current.narrowedPaths) {
33788
+ if (!effective.has(path) && !invalidated.has(narrowed.rootEntry)) {
33789
+ effective.set(path, narrowed);
33790
+ }
33787
33791
  }
33788
- }
33789
- for (const [path, narrowed] of this.narrowedPaths) {
33790
- effective.set(path, narrowed);
33792
+ for (const root of current.invalidatedRoots) invalidated.add(root);
33793
+ for (const root of current.hiddenNarrowingRoots) invalidated.add(root);
33791
33794
  }
33792
33795
  return effective;
33793
33796
  }
@@ -33813,6 +33816,9 @@ var Scope = class {
33813
33816
  hideInheritedNarrowing(rootEntry) {
33814
33817
  this.hiddenNarrowingRoots.add(rootEntry);
33815
33818
  this.updatePatternType(rootEntry, rootEntry.type);
33819
+ for (const [path, narrowed] of this.narrowedPaths) {
33820
+ if (narrowed.rootEntry === rootEntry) this.narrowedPaths.delete(path);
33821
+ }
33816
33822
  }
33817
33823
  invalidate(rootEntry) {
33818
33824
  this.invalidatedRoots.add(rootEntry);
@@ -34370,24 +34376,26 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
34370
34376
  const branchExitNarrowings = [];
34371
34377
  const branchExitOwnershipScopes = [];
34372
34378
  const branches = statement.branches.map((branch) => {
34379
+ const conditionScope = new Scope(scope);
34380
+ this.applyFactsToScope(conditionScope, cumulativeFalseFacts);
34373
34381
  const condition = this.toBool(
34374
- this.resolveExpression(branch.cond, scope),
34382
+ this.resolveExpression(branch.cond, conditionScope),
34375
34383
  branch.cond.pos
34376
34384
  );
34377
34385
  const child = new Scope(scope);
34378
34386
  const branchFacts = [
34379
34387
  ...cumulativeFalseFacts,
34380
- ...this.conditionFacts(branch.cond, scope, true)
34388
+ ...this.conditionFacts(branch.cond, conditionScope, true)
34381
34389
  ];
34382
34390
  this.applyFactsToScope(child, branchFacts);
34383
34391
  const instructions = this.resolveStatements(branch.body, child);
34384
34392
  if (!instructionsTerminate(instructions)) {
34385
34393
  branchExitFacts.push(branchFacts);
34386
- branchExitNarrowings.push(new Map(child.narrowedPaths));
34394
+ branchExitNarrowings.push(child.effectiveNarrowedPaths());
34387
34395
  branchExitOwnershipScopes.push(child);
34388
34396
  }
34389
34397
  cumulativeFalseFacts.push(
34390
- ...this.conditionFacts(branch.cond, scope, false)
34398
+ ...this.conditionFacts(branch.cond, conditionScope, false)
34391
34399
  );
34392
34400
  return {
34393
34401
  expression: pointerToBooleanExpression(condition.pointer),
@@ -34401,14 +34409,14 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
34401
34409
  otherwise = this.resolveStatements(statement.elseBody, elseScope);
34402
34410
  if (!instructionsTerminate(otherwise)) {
34403
34411
  branchExitFacts.push([...cumulativeFalseFacts]);
34404
- branchExitNarrowings.push(new Map(elseScope.narrowedPaths));
34412
+ branchExitNarrowings.push(elseScope.effectiveNarrowedPaths());
34405
34413
  branchExitOwnershipScopes.push(elseScope);
34406
34414
  }
34407
34415
  } else {
34408
34416
  const noBranchScope = new Scope(scope);
34409
34417
  this.applyFactsToScope(noBranchScope, cumulativeFalseFacts);
34410
34418
  branchExitFacts.push([...cumulativeFalseFacts]);
34411
- branchExitNarrowings.push(new Map(noBranchScope.narrowedPaths));
34419
+ branchExitNarrowings.push(noBranchScope.effectiveNarrowedPaths());
34412
34420
  branchExitOwnershipScopes.push(noBranchScope);
34413
34421
  }
34414
34422
  mergeBranchInvalidations(scope, branchExitOwnershipScopes);
@@ -34642,12 +34650,15 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
34642
34650
  statement.body,
34643
34651
  conditionLocals
34644
34652
  );
34645
- this.invalidateLoopEntryNarrowingsAssignedBy(
34646
- loopEntryScope,
34647
- loopScope,
34648
- [statement.iterator],
34649
- conditionLocals
34650
- );
34653
+ const bodyFlow = statementFlow(statement.body, /* @__PURE__ */ new WeakMap());
34654
+ if (bodyFlow.has("fallthrough") || bodyFlow.has("continue")) {
34655
+ this.invalidateLoopEntryNarrowingsAssignedBy(
34656
+ loopEntryScope,
34657
+ loopScope,
34658
+ [statement.iterator],
34659
+ conditionLocals
34660
+ );
34661
+ }
34651
34662
  const condition = this.toBool(
34652
34663
  this.resolveExpression(statement.condition, loopEntryScope),
34653
34664
  statement.condition.pos
@@ -35514,13 +35525,13 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
35514
35525
  if (assignedPath) {
35515
35526
  const rootEntry = scope.lookup(pathRoot(assignedPath));
35516
35527
  if (rootEntry) {
35517
- const receiverFacts = receiverNarrowings(
35528
+ const retainedFacts = unaffectedNarrowings(
35518
35529
  assignedPath,
35519
35530
  rootEntry,
35520
35531
  scope
35521
35532
  );
35522
35533
  scope.invalidate(rootEntry);
35523
- for (const [path, type] of receiverFacts) {
35534
+ for (const [path, type] of retainedFacts) {
35524
35535
  scope.narrow(path, rootEntry, type);
35525
35536
  }
35526
35537
  if (statement.op === "=" && !isNullable(value.type)) {
@@ -35737,13 +35748,13 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
35737
35748
  if (assignedPath) {
35738
35749
  const rootEntry = scope.lookup(pathRoot(assignedPath));
35739
35750
  if (rootEntry) {
35740
- const receiverFacts = receiverNarrowings(
35751
+ const retainedFacts = unaffectedNarrowings(
35741
35752
  assignedPath,
35742
35753
  rootEntry,
35743
35754
  scope
35744
35755
  );
35745
35756
  scope.invalidate(rootEntry);
35746
- for (const [path, type] of receiverFacts) {
35757
+ for (const [path, type] of retainedFacts) {
35747
35758
  scope.narrow(path, rootEntry, type);
35748
35759
  }
35749
35760
  }
@@ -36358,18 +36369,19 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
36358
36369
  case "coalesce": {
36359
36370
  const left = this.resolveExpression(expression.left, scope);
36360
36371
  const leftOptional = { ...left.type, nullable: true };
36372
+ const nullLeft = isNull(left.type);
36361
36373
  const right = this.resolveExpression(
36362
36374
  expression.right,
36363
36375
  scope,
36364
- leftOptional
36376
+ nullLeft ? expected : leftOptional
36365
36377
  );
36366
- if (!isNeoScriptTypeAssignable(right.type, leftOptional, this.project)) {
36378
+ if (!nullLeft && !isNeoScriptTypeAssignable(right.type, leftOptional, this.project)) {
36367
36379
  throw new CompileError(
36368
36380
  `\`??\` right operand ${this.describe(right.type)} is not assignable to ${this.describe(leftOptional)}`,
36369
36381
  expression.pos
36370
36382
  );
36371
36383
  }
36372
- const resultType = {
36384
+ const resultType = nullLeft ? right.type : {
36373
36385
  ...left.type,
36374
36386
  nullable: isNullable(left.type) && isNullable(right.type)
36375
36387
  };
@@ -37070,20 +37082,25 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
37070
37082
  return whenTrue ? factsWhenTrue(expression, pathOf) : factsWhenFalse(expression, pathOf);
37071
37083
  }
37072
37084
  /**
37073
- * A loop body is resolved once but can execute repeatedly. Any narrowing rooted
37074
- * in an outer binding that the body or iterator may write is therefore stale
37075
- * at a later iteration entry. Invalidate those roots before resolving the
37076
- * shared iteration so only the loop's own condition can narrow them again.
37085
+ * A loop body is resolved once but can execute repeatedly. Hide facts about
37086
+ * paths it may replace before resolving the shared iteration; unrelated facts
37087
+ * still hold, and the loop condition can narrow the written paths again.
37077
37088
  */
37078
37089
  invalidateLoopEntryNarrowingsAssignedBy(loopEntryScope, outerScope, statements, locals = /* @__PURE__ */ new Set()) {
37079
- for (const rootName of collectAssignedPathRoots(
37090
+ for (const assignedPath of collectAssignedPaths(
37080
37091
  statements,
37081
37092
  /* @__PURE__ */ new Set(),
37082
37093
  locals
37083
37094
  )) {
37084
- const path = this.narrowingPath(rootName, outerScope);
37095
+ const path = this.narrowingPath(assignedPath, outerScope);
37085
37096
  const outerEntry = path === null ? null : outerScope.lookup(pathRoot(path));
37086
- if (outerEntry) loopEntryScope.hideInheritedNarrowing(outerEntry);
37097
+ if (outerEntry && path !== null) {
37098
+ const retained = unaffectedNarrowings(path, outerEntry, loopEntryScope);
37099
+ loopEntryScope.hideInheritedNarrowing(outerEntry);
37100
+ for (const [retainedPath, type] of retained) {
37101
+ loopEntryScope.narrow(retainedPath, outerEntry, type);
37102
+ }
37103
+ }
37087
37104
  }
37088
37105
  }
37089
37106
  applyFactsToScope(targetScope, facts, mode = "all") {
@@ -41503,12 +41520,18 @@ function identPathSegments(expression) {
41503
41520
  if (receiver === null) return null;
41504
41521
  return [...receiver, expression.name];
41505
41522
  }
41506
- function receiverNarrowings(path, rootEntry, scope) {
41523
+ function unaffectedNarrowings(path, rootEntry, scope) {
41507
41524
  const facts = [];
41508
- for (let dot = path.indexOf("."); dot !== -1; dot = path.indexOf(".", dot + 1)) {
41509
- const prefix = path.slice(0, dot);
41510
- const type = scope.lookupNarrowedType(prefix, rootEntry);
41511
- if (type !== null) facts.push([prefix, type]);
41525
+ const prefix = `${path}.`;
41526
+ const dot = path.lastIndexOf(".");
41527
+ if (dot === -1) return facts;
41528
+ const member = path.slice(dot);
41529
+ for (const [candidate, narrowed] of scope.effectiveNarrowedPaths()) {
41530
+ if (narrowed.rootEntry !== rootEntry) continue;
41531
+ if (candidate === path || candidate.startsWith(prefix)) continue;
41532
+ if (candidate.endsWith(member) || candidate.includes(`${member}.`))
41533
+ continue;
41534
+ facts.push([candidate, narrowed.type]);
41512
41535
  }
41513
41536
  return facts;
41514
41537
  }
@@ -41832,22 +41855,22 @@ function localsWithBindings(locals, expression, whenTrue) {
41832
41855
  ...facts.flatMap((fact) => fact.bindingName ? [fact.bindingName] : [])
41833
41856
  ]);
41834
41857
  }
41835
- function collectAssignedPathRoots(statements, roots = /* @__PURE__ */ new Set(), inheritedLocals = /* @__PURE__ */ new Set(), flowCache = /* @__PURE__ */ new WeakMap()) {
41858
+ function collectAssignedPaths(statements, roots = /* @__PURE__ */ new Set(), inheritedLocals = /* @__PURE__ */ new Set(), flowCache = /* @__PURE__ */ new WeakMap()) {
41836
41859
  const locals = new Set(inheritedLocals);
41837
41860
  for (const statement of statements) {
41838
41861
  if (statement.kind === "assign") {
41839
41862
  const assignedPath = canonicalPath(statement.target);
41840
41863
  if (assignedPath && !locals.has(pathRoot(assignedPath))) {
41841
- roots.add(pathRoot(assignedPath));
41864
+ roots.add(assignedPath);
41842
41865
  }
41843
- collectAssignedPathRootsInExpression(
41866
+ collectAssignedPathsInExpression(
41844
41867
  statement.target,
41845
41868
  roots,
41846
41869
  locals,
41847
41870
  flowCache
41848
41871
  );
41849
41872
  if (statement.value)
41850
- collectAssignedPathRootsInExpression(
41873
+ collectAssignedPathsInExpression(
41851
41874
  statement.value,
41852
41875
  roots,
41853
41876
  locals,
@@ -41856,7 +41879,7 @@ function collectAssignedPathRoots(statements, roots = /* @__PURE__ */ new Set(),
41856
41879
  continue;
41857
41880
  }
41858
41881
  if (statement.kind === "varDecl") {
41859
- collectAssignedPathRootsInExpression(
41882
+ collectAssignedPathsInExpression(
41860
41883
  statement.init,
41861
41884
  roots,
41862
41885
  locals,
@@ -41870,7 +41893,7 @@ function collectAssignedPathRoots(statements, roots = /* @__PURE__ */ new Set(),
41870
41893
  const falseFacts = [];
41871
41894
  const fallthroughFacts = [];
41872
41895
  for (const branch of statement.branches) {
41873
- collectAssignedPathRootsInExpression(
41896
+ collectAssignedPathsInExpression(
41874
41897
  branch.cond,
41875
41898
  roots,
41876
41899
  falseLocals,
@@ -41880,7 +41903,7 @@ function collectAssignedPathRoots(statements, roots = /* @__PURE__ */ new Set(),
41880
41903
  ...falseFacts,
41881
41904
  ...factsWhenTrue(branch.cond, canonicalPath)
41882
41905
  ];
41883
- collectAssignedPathRoots(
41906
+ collectAssignedPaths(
41884
41907
  branch.body,
41885
41908
  roots,
41886
41909
  localsWithBindings(falseLocals, branch.cond, true),
@@ -41892,12 +41915,7 @@ function collectAssignedPathRoots(statements, roots = /* @__PURE__ */ new Set(),
41892
41915
  falseLocals = localsWithBindings(falseLocals, branch.cond, false);
41893
41916
  }
41894
41917
  if (statement.elseBody) {
41895
- collectAssignedPathRoots(
41896
- statement.elseBody,
41897
- roots,
41898
- falseLocals,
41899
- flowCache
41900
- );
41918
+ collectAssignedPaths(statement.elseBody, roots, falseLocals, flowCache);
41901
41919
  if (statementFlow(statement.elseBody, flowCache).has("fallthrough"))
41902
41920
  fallthroughFacts.push(falseFacts);
41903
41921
  } else {
@@ -41909,18 +41927,18 @@ function collectAssignedPathRoots(statements, roots = /* @__PURE__ */ new Set(),
41909
41927
  continue;
41910
41928
  }
41911
41929
  if (statement.kind === "try") {
41912
- collectAssignedPathRoots(statement.body, roots, locals, flowCache);
41930
+ collectAssignedPaths(statement.body, roots, locals, flowCache);
41913
41931
  for (const clause of statement.catches) {
41914
41932
  const catchLocals = /* @__PURE__ */ new Set([...locals, clause.name]);
41915
41933
  if (clause.filter) {
41916
- collectAssignedPathRootsInExpression(
41934
+ collectAssignedPathsInExpression(
41917
41935
  clause.filter,
41918
41936
  roots,
41919
41937
  catchLocals,
41920
41938
  flowCache
41921
41939
  );
41922
41940
  }
41923
- collectAssignedPathRoots(
41941
+ collectAssignedPaths(
41924
41942
  clause.body,
41925
41943
  roots,
41926
41944
  clause.filter ? localsWithBindings(catchLocals, clause.filter, true) : catchLocals,
@@ -41930,13 +41948,13 @@ function collectAssignedPathRoots(statements, roots = /* @__PURE__ */ new Set(),
41930
41948
  continue;
41931
41949
  }
41932
41950
  if (statement.kind === "while" || statement.kind === "doWhile") {
41933
- collectAssignedPathRootsInExpression(
41951
+ collectAssignedPathsInExpression(
41934
41952
  statement.condition,
41935
41953
  roots,
41936
41954
  locals,
41937
41955
  flowCache
41938
41956
  );
41939
- collectAssignedPathRoots(
41957
+ collectAssignedPaths(
41940
41958
  statement.body,
41941
41959
  roots,
41942
41960
  statement.kind === "while" ? localsWithBindings(locals, statement.condition, true) : locals,
@@ -41945,14 +41963,14 @@ function collectAssignedPathRoots(statements, roots = /* @__PURE__ */ new Set(),
41945
41963
  continue;
41946
41964
  }
41947
41965
  if (statement.kind === "for") {
41948
- collectAssignedPathRootsInExpression(
41966
+ collectAssignedPathsInExpression(
41949
41967
  statement.initializer.init,
41950
41968
  roots,
41951
41969
  locals,
41952
41970
  flowCache
41953
41971
  );
41954
41972
  const loopLocals = /* @__PURE__ */ new Set([...locals, statement.initializer.name]);
41955
- collectAssignedPathRootsInExpression(
41973
+ collectAssignedPathsInExpression(
41956
41974
  statement.condition,
41957
41975
  roots,
41958
41976
  loopLocals,
@@ -41963,22 +41981,17 @@ function collectAssignedPathRoots(statements, roots = /* @__PURE__ */ new Set(),
41963
41981
  statement.condition,
41964
41982
  true
41965
41983
  );
41966
- collectAssignedPathRoots(
41984
+ collectAssignedPaths(
41967
41985
  [statement.iterator],
41968
41986
  roots,
41969
41987
  iterationLocals,
41970
41988
  flowCache
41971
41989
  );
41972
- collectAssignedPathRoots(
41973
- statement.body,
41974
- roots,
41975
- iterationLocals,
41976
- flowCache
41977
- );
41990
+ collectAssignedPaths(statement.body, roots, iterationLocals, flowCache);
41978
41991
  continue;
41979
41992
  }
41980
41993
  if (statement.kind === "forEach") {
41981
- collectAssignedPathRootsInExpression(
41994
+ collectAssignedPathsInExpression(
41982
41995
  statement.collection,
41983
41996
  roots,
41984
41997
  locals,
@@ -41986,11 +41999,11 @@ function collectAssignedPathRoots(statements, roots = /* @__PURE__ */ new Set(),
41986
41999
  );
41987
42000
  const loopLocals = /* @__PURE__ */ new Set([...locals, statement.name]);
41988
42001
  if (statement.key) loopLocals.add(statement.key.name);
41989
- collectAssignedPathRoots(statement.body, roots, loopLocals, flowCache);
42002
+ collectAssignedPaths(statement.body, roots, loopLocals, flowCache);
41990
42003
  continue;
41991
42004
  }
41992
42005
  if (statement.kind === "switch") {
41993
- collectAssignedPathRootsInExpression(
42006
+ collectAssignedPathsInExpression(
41994
42007
  statement.selector,
41995
42008
  roots,
41996
42009
  locals,
@@ -41999,7 +42012,7 @@ function collectAssignedPathRoots(statements, roots = /* @__PURE__ */ new Set(),
41999
42012
  for (const section of statement.sections) {
42000
42013
  for (const label of section.labels) {
42001
42014
  if (label.kind === "case") {
42002
- collectAssignedPathRootsInExpression(
42015
+ collectAssignedPathsInExpression(
42003
42016
  label.expression,
42004
42017
  roots,
42005
42018
  locals,
@@ -42007,13 +42020,13 @@ function collectAssignedPathRoots(statements, roots = /* @__PURE__ */ new Set(),
42007
42020
  );
42008
42021
  }
42009
42022
  }
42010
- collectAssignedPathRoots(section.body, roots, locals, flowCache);
42023
+ collectAssignedPaths(section.body, roots, locals, flowCache);
42011
42024
  }
42012
42025
  continue;
42013
42026
  }
42014
42027
  if (statement.kind === "return" || statement.kind === "throw") {
42015
42028
  if (statement.expr)
42016
- collectAssignedPathRootsInExpression(
42029
+ collectAssignedPathsInExpression(
42017
42030
  statement.expr,
42018
42031
  roots,
42019
42032
  locals,
@@ -42022,7 +42035,7 @@ function collectAssignedPathRoots(statements, roots = /* @__PURE__ */ new Set(),
42022
42035
  continue;
42023
42036
  }
42024
42037
  if (statement.kind === "exprStmt") {
42025
- collectAssignedPathRootsInExpression(
42038
+ collectAssignedPathsInExpression(
42026
42039
  statement.expr,
42027
42040
  roots,
42028
42041
  locals,
@@ -42032,43 +42045,28 @@ function collectAssignedPathRoots(statements, roots = /* @__PURE__ */ new Set(),
42032
42045
  }
42033
42046
  return roots;
42034
42047
  }
42035
- function collectAssignedPathRootsInExpression(expression, roots, locals, flowCache) {
42048
+ function collectAssignedPathsInExpression(expression, roots, locals, flowCache) {
42036
42049
  switch (expression.kind) {
42037
42050
  case "litInterp":
42038
42051
  for (const part of expression.parts) {
42039
42052
  if (part.kind === "expr") {
42040
- collectAssignedPathRootsInExpression(
42041
- part.expr,
42042
- roots,
42043
- locals,
42044
- flowCache
42045
- );
42053
+ collectAssignedPathsInExpression(part.expr, roots, locals, flowCache);
42046
42054
  }
42047
42055
  }
42048
42056
  return;
42049
42057
  case "litList":
42050
42058
  for (const element of expression.elements) {
42051
- collectAssignedPathRootsInExpression(element, roots, locals, flowCache);
42059
+ collectAssignedPathsInExpression(element, roots, locals, flowCache);
42052
42060
  }
42053
42061
  return;
42054
42062
  case "litDict":
42055
42063
  for (const entry of expression.entries) {
42056
- collectAssignedPathRootsInExpression(
42057
- entry.key,
42058
- roots,
42059
- locals,
42060
- flowCache
42061
- );
42062
- collectAssignedPathRootsInExpression(
42063
- entry.value,
42064
- roots,
42065
- locals,
42066
- flowCache
42067
- );
42064
+ collectAssignedPathsInExpression(entry.key, roots, locals, flowCache);
42065
+ collectAssignedPathsInExpression(entry.value, roots, locals, flowCache);
42068
42066
  }
42069
42067
  return;
42070
42068
  case "member":
42071
- collectAssignedPathRootsInExpression(
42069
+ collectAssignedPathsInExpression(
42072
42070
  expression.receiver,
42073
42071
  roots,
42074
42072
  locals,
@@ -42076,13 +42074,13 @@ function collectAssignedPathRootsInExpression(expression, roots, locals, flowCac
42076
42074
  );
42077
42075
  return;
42078
42076
  case "index":
42079
- collectAssignedPathRootsInExpression(
42077
+ collectAssignedPathsInExpression(
42080
42078
  expression.receiver,
42081
42079
  roots,
42082
42080
  locals,
42083
42081
  flowCache
42084
42082
  );
42085
- collectAssignedPathRootsInExpression(
42083
+ collectAssignedPathsInExpression(
42086
42084
  expression.index,
42087
42085
  roots,
42088
42086
  locals,
@@ -42090,32 +42088,22 @@ function collectAssignedPathRootsInExpression(expression, roots, locals, flowCac
42090
42088
  );
42091
42089
  return;
42092
42090
  case "call":
42093
- collectAssignedPathRootsInExpression(
42091
+ collectAssignedPathsInExpression(
42094
42092
  expression.callee,
42095
42093
  roots,
42096
42094
  locals,
42097
42095
  flowCache
42098
42096
  );
42099
42097
  for (const argument2 of expression.args) {
42100
- collectAssignedPathRootsInExpression(
42101
- argument2,
42102
- roots,
42103
- locals,
42104
- flowCache
42105
- );
42098
+ collectAssignedPathsInExpression(argument2, roots, locals, flowCache);
42106
42099
  }
42107
42100
  return;
42108
42101
  case "new":
42109
42102
  for (const argument2 of expression.args) {
42110
- collectAssignedPathRootsInExpression(
42111
- argument2,
42112
- roots,
42113
- locals,
42114
- flowCache
42115
- );
42103
+ collectAssignedPathsInExpression(argument2, roots, locals, flowCache);
42116
42104
  }
42117
42105
  for (const initializer of expression.initializer ?? []) {
42118
- collectAssignedPathRootsInExpression(
42106
+ collectAssignedPathsInExpression(
42119
42107
  initializer.value,
42120
42108
  roots,
42121
42109
  locals,
@@ -42126,15 +42114,10 @@ function collectAssignedPathRootsInExpression(expression, roots, locals, flowCac
42126
42114
  case "annotated":
42127
42115
  for (const annotation2 of expression.annotations) {
42128
42116
  for (const argument2 of annotation2.args) {
42129
- collectAssignedPathRootsInExpression(
42130
- argument2,
42131
- roots,
42132
- locals,
42133
- flowCache
42134
- );
42117
+ collectAssignedPathsInExpression(argument2, roots, locals, flowCache);
42135
42118
  }
42136
42119
  }
42137
- collectAssignedPathRootsInExpression(
42120
+ collectAssignedPathsInExpression(
42138
42121
  expression.expression,
42139
42122
  roots,
42140
42123
  locals,
@@ -42143,13 +42126,13 @@ function collectAssignedPathRootsInExpression(expression, roots, locals, flowCac
42143
42126
  return;
42144
42127
  case "binary":
42145
42128
  case "coalesce":
42146
- collectAssignedPathRootsInExpression(
42129
+ collectAssignedPathsInExpression(
42147
42130
  expression.left,
42148
42131
  roots,
42149
42132
  locals,
42150
42133
  flowCache
42151
42134
  );
42152
- collectAssignedPathRootsInExpression(
42135
+ collectAssignedPathsInExpression(
42153
42136
  expression.right,
42154
42137
  roots,
42155
42138
  locals,
@@ -42157,19 +42140,19 @@ function collectAssignedPathRootsInExpression(expression, roots, locals, flowCac
42157
42140
  );
42158
42141
  return;
42159
42142
  case "conditional":
42160
- collectAssignedPathRootsInExpression(
42143
+ collectAssignedPathsInExpression(
42161
42144
  expression.condition,
42162
42145
  roots,
42163
42146
  locals,
42164
42147
  flowCache
42165
42148
  );
42166
- collectAssignedPathRootsInExpression(
42149
+ collectAssignedPathsInExpression(
42167
42150
  expression.whenTrue,
42168
42151
  roots,
42169
42152
  locals,
42170
42153
  flowCache
42171
42154
  );
42172
- collectAssignedPathRootsInExpression(
42155
+ collectAssignedPathsInExpression(
42173
42156
  expression.whenFalse,
42174
42157
  roots,
42175
42158
  locals,
@@ -42178,7 +42161,7 @@ function collectAssignedPathRootsInExpression(expression, roots, locals, flowCac
42178
42161
  return;
42179
42162
  case "unary":
42180
42163
  case "force":
42181
- collectAssignedPathRootsInExpression(
42164
+ collectAssignedPathsInExpression(
42182
42165
  expression.operand,
42183
42166
  roots,
42184
42167
  locals,
@@ -42186,7 +42169,7 @@ function collectAssignedPathRootsInExpression(expression, roots, locals, flowCac
42186
42169
  );
42187
42170
  return;
42188
42171
  case "is":
42189
- collectAssignedPathRootsInExpression(
42172
+ collectAssignedPathsInExpression(
42190
42173
  expression.operand,
42191
42174
  roots,
42192
42175
  locals,
@@ -42194,7 +42177,7 @@ function collectAssignedPathRootsInExpression(expression, roots, locals, flowCac
42194
42177
  );
42195
42178
  return;
42196
42179
  case "lambda":
42197
- collectAssignedPathRoots(
42180
+ collectAssignedPaths(
42198
42181
  expression.body,
42199
42182
  roots,
42200
42183
  /* @__PURE__ */ new Set([
@@ -54658,7 +54641,7 @@ var SPECIALIZED_RELATION_KINDS = /* @__PURE__ */ new Set([
54658
54641
 
54659
54642
  // ../packages/manifest/schema-contract/registry.mjs
54660
54643
  var PROJECT_SCHEMA_CONTRACT = Object.freeze({
54661
- cliVersion: "0.72.2",
54644
+ cliVersion: "0.72.4",
54662
54645
  projectFileUploadBatchSize: 32,
54663
54646
  documentRecords: {
54664
54647
  member: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neocompose/cli",
3
- "version": "0.72.2",
3
+ "version": "0.72.4",
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.72.2 -->
12
+ <!-- reviewed-through-cli: 0.72.4 -->
13
13
 
14
14
  # Neo Compose CLI
15
15
 
@@ -118,7 +118,7 @@ wrappers.
118
118
  The marker near the top of `SKILL.md` must exactly match the package version:
119
119
 
120
120
  ```html
121
- <!-- reviewed-through-cli: 0.72.2 -->
121
+ <!-- reviewed-through-cli: 0.72.4 -->
122
122
  ```
123
123
 
124
124
  The quoted version above is checked too, so this instruction cannot go stale
@@ -178,7 +178,13 @@ or narrowing guard before the next access.
178
178
  Inside an instance body, `Member` and `this.Member` share null and type-check
179
179
  narrowing. A local or parameter named `Member` shadows the implicit member;
180
180
  `this.Member` still names the instance field. Reassigning a narrowed path to a
181
- nullable value clears its non-null fact.
181
+ nullable value clears its non-null fact and facts about its descendants. Writes to
182
+ unrelated members preserve existing guards, including inside loops. Earlier
183
+ conditions being false also narrow receivers in later `else if` conditions.
184
+
185
+ A nullable value coalesced with a non-null fallback produces a non-null result,
186
+ including in constructor arguments: `new Sprite(item.Sprite ?? SpriteInfo.Empty)`.
187
+ A nullable fallback keeps the result nullable when the left operand can be null.
182
188
 
183
189
  Treat lookup-return values with the same nullable narrowing rules.
184
190
 
@@ -57,7 +57,10 @@ keys as quoted IDs when their option has been deleted.
57
57
 
58
58
  ## Identity rules
59
59
 
60
- Preserve every existing ID through rename, reorder, and file moves. A copy
60
+ Preserve every existing ID through rename, reorder, and file moves. Renaming a
61
+ schema member while keeping its ID preserves its owned value graph. Update
62
+ initializer keys to the new name and retain existing child IDs, including when
63
+ editing those children in the same change. A copy
61
64
  must get a new ID. A stored nested class value may keep its UUID-v4 when moved
62
65
  to an empty, compatible class field on another existing parent. Remove its
63
66
  old owning declaration in the same push. Both fields must declare the same