@neocompose/cli 0.31.10 → 0.31.11

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,16 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.31.11] - 2026-08-17
4
+
5
+ ### Fixed
6
+
7
+ - Resolve lookup variants declared in root-path folders from member defaults
8
+ without adding an unaddressable empty path segment.
9
+ - Allow stored static class rows to assign variant members with canonical
10
+ `<Class>.Variants...` selections.
11
+ - Recognize nested value returns in variant `initialize` delegates and retain
12
+ declared `is` bindings after terminating guards.
13
+
3
14
  ## [0.31.10] - 2026-08-17
4
15
 
5
16
  ### Fixed
package/dist/neo.mjs CHANGED
@@ -7482,6 +7482,42 @@ var init_strict_parser = __esm({
7482
7482
  }
7483
7483
  });
7484
7484
 
7485
+ // ../packages/neoscript-language/src/strict-ast.ts
7486
+ function collectReturnStatements(statements) {
7487
+ const result = [];
7488
+ for (const statement of statements) {
7489
+ if (statement.kind === "return") result.push(statement);
7490
+ if (statement.kind === "if") {
7491
+ for (const branch of statement.branches) {
7492
+ result.push(...collectReturnStatements(branch.body));
7493
+ }
7494
+ if (statement.elseBody) {
7495
+ result.push(...collectReturnStatements(statement.elseBody));
7496
+ }
7497
+ }
7498
+ if (statement.kind === "for" || statement.kind === "forEach") {
7499
+ result.push(...collectReturnStatements(statement.body));
7500
+ }
7501
+ if (statement.kind === "switch") {
7502
+ for (const section of statement.sections) {
7503
+ result.push(...collectReturnStatements(section.body));
7504
+ }
7505
+ }
7506
+ if (statement.kind === "try") {
7507
+ result.push(...collectReturnStatements(statement.body));
7508
+ for (const clause of statement.catches) {
7509
+ result.push(...collectReturnStatements(clause.body));
7510
+ }
7511
+ }
7512
+ }
7513
+ return result;
7514
+ }
7515
+ var init_strict_ast = __esm({
7516
+ "../packages/neoscript-language/src/strict-ast.ts"() {
7517
+ "use strict";
7518
+ }
7519
+ });
7520
+
7485
7521
  // ../packages/neoscript-language/src/declared-constructors.ts
7486
7522
  function neoScriptArgumentNameSetKey(names) {
7487
7523
  return [...names].map((name) => name.toLowerCase()).sort().join(",");
@@ -8152,7 +8188,7 @@ function mergeFactSets(sets) {
8152
8188
  return first.filter(
8153
8189
  (fact) => sets.slice(1).every(
8154
8190
  (set) => set.some(
8155
- (candidate) => candidate.path === fact.path && candidate.nullValue === fact.nullValue && typeAnnotationsEqual(candidate.targetType, fact.targetType)
8191
+ (candidate) => candidate.path === fact.path && candidate.nullValue === fact.nullValue && candidate.bindingName === fact.bindingName && candidate.loose === fact.loose && typeAnnotationsEqual(candidate.targetType, fact.targetType)
8156
8192
  )
8157
8193
  )
8158
8194
  );
@@ -9106,31 +9142,6 @@ function instructionsPropagatePastSwitch(instructions) {
9106
9142
  }
9107
9143
  return false;
9108
9144
  }
9109
- function collectReturns(statements) {
9110
- const result = [];
9111
- for (const statement of statements) {
9112
- if (statement.kind === "return") result.push(statement);
9113
- if (statement.kind === "if") {
9114
- for (const branch of statement.branches)
9115
- result.push(...collectReturns(branch.body));
9116
- if (statement.elseBody)
9117
- result.push(...collectReturns(statement.elseBody));
9118
- }
9119
- if (statement.kind === "for" || statement.kind === "forEach") {
9120
- result.push(...collectReturns(statement.body));
9121
- }
9122
- if (statement.kind === "switch") {
9123
- for (const section of statement.sections)
9124
- result.push(...collectReturns(section.body));
9125
- }
9126
- if (statement.kind === "try") {
9127
- result.push(...collectReturns(statement.body));
9128
- for (const clause of statement.catches)
9129
- result.push(...collectReturns(clause.body));
9130
- }
9131
- }
9132
- return result;
9133
- }
9134
9145
  function functionArityMessage(name, minimum, maximum, got) {
9135
9146
  if (minimum === maximum) {
9136
9147
  return `Function '${name}' expects ${maximum} argument${maximum === 1 ? "" : "s"}, got ${got}.`;
@@ -9192,6 +9203,7 @@ var init_strict_resolver = __esm({
9192
9203
  "use strict";
9193
9204
  init_project();
9194
9205
  init_language_spec();
9206
+ init_strict_ast();
9195
9207
  init_strict_lexer();
9196
9208
  init_declared_constructors();
9197
9209
  init_strict_compile_error();
@@ -9595,6 +9607,7 @@ var init_strict_resolver = __esm({
9595
9607
  }
9596
9608
  case "if": {
9597
9609
  const cumulativeFalseFacts = [];
9610
+ const branchExitFacts = [];
9598
9611
  const branchExitNarrowings = [];
9599
9612
  const branchExitOwnershipScopes = [];
9600
9613
  const branches = statement.branches.map((branch) => {
@@ -9603,12 +9616,14 @@ var init_strict_resolver = __esm({
9603
9616
  branch.cond.pos
9604
9617
  );
9605
9618
  const child = new Scope(scope);
9606
- this.applyFactsToScope(scope, child, [
9619
+ const branchFacts = [
9607
9620
  ...cumulativeFalseFacts,
9608
9621
  ...factsWhenTrue(branch.cond)
9609
- ]);
9622
+ ];
9623
+ this.applyFactsToScope(scope, child, branchFacts);
9610
9624
  const instructions = this.resolveStatements(branch.body, child);
9611
9625
  if (!instructionsTerminate(instructions)) {
9626
+ branchExitFacts.push(branchFacts);
9612
9627
  branchExitNarrowings.push(new Map(child.narrowedPaths));
9613
9628
  branchExitOwnershipScopes.push(child);
9614
9629
  }
@@ -9624,12 +9639,14 @@ var init_strict_resolver = __esm({
9624
9639
  this.applyFactsToScope(scope, elseScope, cumulativeFalseFacts);
9625
9640
  otherwise = this.resolveStatements(statement.elseBody, elseScope);
9626
9641
  if (!instructionsTerminate(otherwise)) {
9642
+ branchExitFacts.push([...cumulativeFalseFacts]);
9627
9643
  branchExitNarrowings.push(new Map(elseScope.narrowedPaths));
9628
9644
  branchExitOwnershipScopes.push(elseScope);
9629
9645
  }
9630
9646
  } else {
9631
9647
  const noBranchScope = new Scope(scope);
9632
9648
  this.applyFactsToScope(scope, noBranchScope, cumulativeFalseFacts);
9649
+ branchExitFacts.push([...cumulativeFalseFacts]);
9633
9650
  branchExitNarrowings.push(new Map(noBranchScope.narrowedPaths));
9634
9651
  branchExitOwnershipScopes.push(noBranchScope);
9635
9652
  }
@@ -9645,6 +9662,12 @@ var init_strict_resolver = __esm({
9645
9662
  }
9646
9663
  }
9647
9664
  mergeBranchOwnership(scope, branchExitOwnershipScopes);
9665
+ this.applyFactsToScope(
9666
+ scope,
9667
+ scope,
9668
+ mergeFactSets(branchExitFacts),
9669
+ "bindings-only"
9670
+ );
9648
9671
  return {
9649
9672
  type: "if" /* If */,
9650
9673
  branches,
@@ -11468,8 +11491,9 @@ var init_strict_resolver = __esm({
11468
11491
  pos
11469
11492
  );
11470
11493
  }
11471
- applyFactsToScope(outerScope, targetScope, facts) {
11494
+ applyFactsToScope(outerScope, targetScope, facts, mode = "all") {
11472
11495
  for (const fact of facts) {
11496
+ if (mode === "bindings-only" && !fact.bindingName) continue;
11473
11497
  const rootEntry = outerScope.lookup(pathRoot(fact.path));
11474
11498
  if (!rootEntry) continue;
11475
11499
  const natural = this.resolveExpression(fact.expression, outerScope);
@@ -11490,8 +11514,13 @@ var init_strict_resolver = __esm({
11490
11514
  const naturalDefinition = natural.type.kind === "named" ? this.project.typeById.get(natural.type.typeId) : void 0;
11491
11515
  const retainConcreteClass = !fact.bindingName && naturalDefinition?.kind === "class" && targetDefinition?.kind === "interface";
11492
11516
  const narrowed = retainConcreteClass ? { ...natural.type, nullable: false } : { ...target, nullable: false };
11493
- targetScope.narrow(fact.path, rootEntry, narrowed);
11517
+ if (mode === "all") {
11518
+ targetScope.narrow(fact.path, rootEntry, narrowed);
11519
+ }
11494
11520
  if (fact.bindingName) {
11521
+ if (mode === "bindings-only" && targetScope.lookup(fact.bindingName) !== null) {
11522
+ continue;
11523
+ }
11495
11524
  targetScope.define({
11496
11525
  name: fact.bindingName,
11497
11526
  type: narrowed,
@@ -13763,7 +13792,7 @@ var init_strict_resolver = __esm({
13763
13792
  }
13764
13793
  }
13765
13794
  inferLambdaReturn(lambda, collection, source, outerScope) {
13766
- const returns = collectReturns(lambda.body);
13795
+ const returns = collectReturnStatements(lambda.body);
13767
13796
  if (returns.length === 0) {
13768
13797
  throw new CompileError("Select lambda must return a value.", lambda.pos);
13769
13798
  }
@@ -20552,7 +20581,7 @@ function validateInitialize(variant, argument2, diagnostics) {
20552
20581
  )
20553
20582
  );
20554
20583
  }
20555
- const returns = lambda.body.some(
20584
+ const returns = collectReturnStatements(lambda.body).some(
20556
20585
  (statement) => statement.kind === "return" && statement.expr !== null
20557
20586
  );
20558
20587
  if (returns) return;
@@ -20579,7 +20608,7 @@ function validateApply(variant, argument2, diagnostics) {
20579
20608
  )
20580
20609
  );
20581
20610
  }
20582
- const returnsValue = lambda.body.some(
20611
+ const returnsValue = collectReturnStatements(lambda.body).some(
20583
20612
  (statement) => statement.kind === "return" && statement.expr !== null
20584
20613
  );
20585
20614
  if (!returnsValue) return;
@@ -20827,6 +20856,7 @@ var init_project_source_variants = __esm({
20827
20856
  init_generated_csharp_identifiers();
20828
20857
  init_language_spec();
20829
20858
  init_strict_parser();
20859
+ init_strict_ast();
20830
20860
  NEO_VARIANT_CONSTRUCTOR_ARGUMENTS = [
20831
20861
  "initialize",
20832
20862
  "apply",
@@ -33083,13 +33113,6 @@ var init_service = __esm({
33083
33113
  }
33084
33114
  });
33085
33115
 
33086
- // ../packages/neoscript-language/src/strict-ast.ts
33087
- var init_strict_ast = __esm({
33088
- "../packages/neoscript-language/src/strict-ast.ts"() {
33089
- "use strict";
33090
- }
33091
- });
33092
-
33093
33116
  // ../packages/neoscript-language/src/strict-parity-fixture.ts
33094
33117
  function location2(uri, line, startCharacter, endCharacter) {
33095
33118
  return {
@@ -58785,6 +58808,285 @@ var init_lower_members = __esm({
58785
58808
  }
58786
58809
  });
58787
58810
 
58811
+ // src/project-source/lower-variants.ts
58812
+ function lowerVariants(context, analysis) {
58813
+ const folders = lowerVariantFolders(context, analysis);
58814
+ const foldersBySymbol = new Map(
58815
+ analysis.variants.globals.filter((global) => isNeoVariantFolderTypeName(global.type.name)).map(
58816
+ (global) => [
58817
+ folderSymbolKey(owningClassName(global), global.name),
58818
+ variantRecordId(global, global.type.name)
58819
+ ]
58820
+ )
58821
+ );
58822
+ const variants = variantGlobals(analysis).map((global) => {
58823
+ const className = owningClassName(global);
58824
+ const classId = requiredName2(context.classIdsByName, className, "class");
58825
+ const variantId = variantRecordId(global, global.type.name);
58826
+ return {
58827
+ id: variantId,
58828
+ source: sourceIdentity2(global, "variant", global.name),
58829
+ classId,
58830
+ name: global.name,
58831
+ folderId: variantFolderId2(global, className, foldersBySymbol),
58832
+ valueId: variantRootValueId(context, variantId, global, className)
58833
+ };
58834
+ });
58835
+ return { variants, variantFolders: folders };
58836
+ }
58837
+ function variantValueBindingsV4(manifest, analysis) {
58838
+ if (manifest.variants.length === 0) return [];
58839
+ const variantsById = new Map(
58840
+ manifest.variants.map((variant) => [variant.id, variant])
58841
+ );
58842
+ const classNames = new Map(
58843
+ manifest.classes.map((entry) => [entry.id, entry.name])
58844
+ );
58845
+ return variantGlobals(analysis).flatMap((global) => {
58846
+ const variant = variantsById.get(
58847
+ variantRecordId(global, NEO_VARIANT_TYPE_NAME)
58848
+ );
58849
+ if (variant === void 0) return [];
58850
+ return [
58851
+ {
58852
+ variantId: variant.id,
58853
+ targetClassId: variant.classId,
58854
+ targetClassName: requiredClassName2(classNames, variant.classId),
58855
+ variantTypeName: global.type.name,
58856
+ valueClassId: global.type.name === NEO_LOOKUP_VARIANT_TYPE_NAME ? requiredName2(
58857
+ new Map(
58858
+ manifest.classes.map((entry) => [entry.name, entry.id])
58859
+ ),
58860
+ valueClassName(global),
58861
+ "class"
58862
+ ) : null,
58863
+ valueId: variant.valueId,
58864
+ initializer: global.initializer,
58865
+ source: global.source,
58866
+ label: `${requiredClassName2(classNames, variant.classId)}.${NEO_VARIANT_SCOPE_NAME}.${variant.name}`
58867
+ }
58868
+ ];
58869
+ });
58870
+ }
58871
+ function requiredClassName2(classNames, classId) {
58872
+ const name = classNames.get(classId);
58873
+ if (name === void 0) {
58874
+ throw new Error(
58875
+ `Variant targets class ${JSON.stringify(classId)}, which this push does not declare.`
58876
+ );
58877
+ }
58878
+ return name;
58879
+ }
58880
+ function variantGlobals(analysis) {
58881
+ return analysis.variants.globals.filter(
58882
+ (global) => isNeoVariantTypeName(global.type.name)
58883
+ );
58884
+ }
58885
+ function lowerVariantFolders(context, analysis) {
58886
+ return analysis.variants.globals.filter((global) => isNeoVariantFolderTypeName(global.type.name)).map((global) => {
58887
+ const className = owningClassName(global);
58888
+ return {
58889
+ id: variantRecordId(global, NEO_VARIANT_FOLDER_TYPE_NAME),
58890
+ source: sourceIdentity2(global, "variantFolder", global.name),
58891
+ classId: requiredName2(context.classIdsByName, className, "class"),
58892
+ path: folderPath2(global),
58893
+ binding: folderBinding(context, global)
58894
+ };
58895
+ });
58896
+ }
58897
+ function owningClassName(global) {
58898
+ const argument2 = global.type.arguments[0]?.name;
58899
+ if (argument2 === void 0) {
58900
+ throw new Error(
58901
+ `${global.type.name} ${global.name} must name the class it belongs to as its single type argument.`
58902
+ );
58903
+ }
58904
+ return argument2;
58905
+ }
58906
+ function variantRecordId(global, typeName) {
58907
+ const kind = isNeoVariantTypeName(typeName) ? "variant" : "variant-folder";
58908
+ return materializedIdentityId(global, kind, global.name);
58909
+ }
58910
+ function variantRootValueId(context, variantId, global, className) {
58911
+ const stored = storedVariantValueId(context, variantId);
58912
+ if (stored !== null) return stored;
58913
+ return `__pending__:value:${encodeURIComponent(global.source.uri)}:${global.source.range.start.line}:${global.source.range.start.character}:${encodeURIComponent(`${className}.${NEO_VARIANT_SCOPE_NAME}.${global.name}`)}`;
58914
+ }
58915
+ function storedVariantValueId(context, variantId) {
58916
+ const stored = context.base.variants.find(
58917
+ (variant) => variant.id === variantId
58918
+ );
58919
+ return stored?.valueId ?? null;
58920
+ }
58921
+ function variantFolderId2(global, className, foldersBySymbol) {
58922
+ const symbol = neoVariantFolderAnnotationSymbol(
58923
+ annotationsForFolderLookup(global.annotations)
58924
+ );
58925
+ if (symbol === null) return null;
58926
+ const folderId = foldersBySymbol.get(folderSymbolKey(className, symbol));
58927
+ if (folderId === void 0) {
58928
+ throw new Error(
58929
+ `Variant ${className}.${NEO_VARIANT_SCOPE_NAME}.${global.name} names folder ${JSON.stringify(symbol)}, which no NeoVariantFolder<${className}> declares.`
58930
+ );
58931
+ }
58932
+ return folderId;
58933
+ }
58934
+ function annotationsForFolderLookup(annotations) {
58935
+ return annotations.map((annotation2) => ({
58936
+ name: annotation2.name,
58937
+ arguments: annotation2.arguments.map((argument2) => ({
58938
+ ...argument2.name === void 0 ? {} : { name: argument2.name },
58939
+ text: argument2.expression
58940
+ }))
58941
+ }));
58942
+ }
58943
+ function folderSymbolKey(className, symbol) {
58944
+ return `${className} ${symbol}`;
58945
+ }
58946
+ function folderPath2(global) {
58947
+ const expression = unwrapAnnotated2(parseExpression(global.initializer));
58948
+ if (expression.kind !== "new") {
58949
+ throw new Error(
58950
+ `Variant folder ${global.name} must be declared as new("Path").`
58951
+ );
58952
+ }
58953
+ const pathIndex = expression.argumentNames?.findIndex(
58954
+ (name) => name === "path"
58955
+ );
58956
+ const argument2 = expression.args[pathIndex !== void 0 && pathIndex >= 0 ? pathIndex : 0];
58957
+ if (argument2 === void 0 || argument2.kind !== "litString") {
58958
+ throw new Error(
58959
+ `Variant folder ${global.name} requires one string-literal path argument.`
58960
+ );
58961
+ }
58962
+ if (argument2.value.length === 0 && global.type.name !== NEO_LOOKUP_VARIANT_FOLDER_TYPE_NAME) {
58963
+ throw new Error(
58964
+ `Only a bound lookup variant folder may have an empty path.`
58965
+ );
58966
+ }
58967
+ return argument2.value;
58968
+ }
58969
+ function unwrapAnnotated2(expression) {
58970
+ let current = expression;
58971
+ while (current.kind === "annotated") current = current.expression;
58972
+ return current;
58973
+ }
58974
+ function buildVariantPathIndexV4(classIdsByName, analysis) {
58975
+ const index = /* @__PURE__ */ new Map();
58976
+ const foldersBySymbol = new Map(
58977
+ analysis.variants.globals.filter((global) => isNeoVariantFolderTypeName(global.type.name)).map(
58978
+ (global) => [
58979
+ folderSymbolKey(owningClassName(global), global.name),
58980
+ folderPath2(global)
58981
+ ]
58982
+ )
58983
+ );
58984
+ for (const [className, classId] of classIdsByName) {
58985
+ index.set(
58986
+ `${className}.${NEO_VARIANT_SCOPE_NAME}.${NEO_VARIANT_RESERVED_NAME}`,
58987
+ {
58988
+ classId,
58989
+ variantId: null,
58990
+ valueClassId: null
58991
+ }
58992
+ );
58993
+ }
58994
+ for (const global of variantGlobals(analysis)) {
58995
+ const className = owningClassName(global);
58996
+ const classId = classIdsByName.get(className);
58997
+ if (classId === void 0) continue;
58998
+ const symbol = neoVariantFolderAnnotationSymbol(
58999
+ annotationsForFolderLookup(global.annotations)
59000
+ );
59001
+ const path = symbol === null ? null : foldersBySymbol.get(folderSymbolKey(className, symbol)) ?? null;
59002
+ const scope = path === null || path === "" ? `${className}.${NEO_VARIANT_SCOPE_NAME}` : `${className}.${NEO_VARIANT_SCOPE_NAME}.${path.split(NEO_VARIANT_FOLDER_PATH_SEPARATOR).join(".")}`;
59003
+ index.set(`${scope}.${global.name}`, {
59004
+ classId,
59005
+ variantId: variantRecordId(global, global.type.name),
59006
+ valueClassId: global.type.name === NEO_LOOKUP_VARIANT_TYPE_NAME ? classIdsByName.get(valueClassName(global)) ?? null : null
59007
+ });
59008
+ }
59009
+ return index;
59010
+ }
59011
+ function valueClassName(global) {
59012
+ const argument2 = global.type.arguments[1]?.name;
59013
+ if (argument2 === void 0) {
59014
+ throw new Error(
59015
+ `${global.type.name} ${global.name} requires a TValue type argument.`
59016
+ );
59017
+ }
59018
+ return argument2;
59019
+ }
59020
+ function folderBinding(context, global) {
59021
+ if (global.type.name !== NEO_LOOKUP_VARIANT_FOLDER_TYPE_NAME) return null;
59022
+ const expression = unwrapAnnotated2(parseExpression(global.initializer));
59023
+ if (expression.kind !== "new") {
59024
+ throw new Error(
59025
+ `Lookup variant folder ${global.name} must be declared with new(...).`
59026
+ );
59027
+ }
59028
+ const named = /* @__PURE__ */ new Map();
59029
+ expression.args.forEach((argument2, index) => {
59030
+ const name = expression.argumentNames?.[index];
59031
+ if (name !== void 0 && name !== null) named.set(name, argument2);
59032
+ });
59033
+ const collection = named.get("collection");
59034
+ const collectionValue = named.get("collectionValue");
59035
+ const collectionPath = collection === void 0 ? null : dottedPath(collection);
59036
+ if (collectionPath === null) {
59037
+ throw new Error(
59038
+ `Lookup variant folder ${global.name} requires collection: Class.Member.`
59039
+ );
59040
+ }
59041
+ const collectionMemberId = resolveQualifiedMemberId(context, collectionPath);
59042
+ if (collectionMemberId === null) {
59043
+ throw new Error(
59044
+ `Lookup variant folder ${global.name} names unknown collection ${collectionPath}.`
59045
+ );
59046
+ }
59047
+ if (collectionValue === void 0) {
59048
+ throw new Error(
59049
+ `Lookup variant folder ${global.name} requires collectionValue: Reference(...).`
59050
+ );
59051
+ }
59052
+ const collectionValueId = lowerReferenceValue(collectionValue);
59053
+ if (collectionValueId === null) {
59054
+ throw new Error(
59055
+ `Lookup variant folder ${global.name} has an invalid collectionValue reference.`
59056
+ );
59057
+ }
59058
+ return { collectionMemberId, collectionValueId };
59059
+ }
59060
+ function lowerReferenceValue(expression) {
59061
+ if (expression.kind !== "call" || expression.callee.kind !== "ident" || expression.callee.name !== "Reference")
59062
+ return null;
59063
+ const idIndex = expression.argumentNames?.findIndex((name) => name === "id") ?? -1;
59064
+ const target = expression.args[idIndex >= 0 ? idIndex : 0];
59065
+ if (target?.kind === "litString") return target.value;
59066
+ const path = target === void 0 ? null : dottedPath(target);
59067
+ if (path === null || !path.startsWith("root.")) return null;
59068
+ return referenceId(`Reference(${path})`);
59069
+ }
59070
+ function dottedPath(expression) {
59071
+ const parts = [];
59072
+ let cursor = expression;
59073
+ while (cursor.kind === "member") {
59074
+ parts.unshift(cursor.name);
59075
+ cursor = cursor.receiver;
59076
+ }
59077
+ if (cursor.kind !== "ident") return null;
59078
+ parts.unshift(cursor.name);
59079
+ return parts.join(".");
59080
+ }
59081
+ var init_lower_variants = __esm({
59082
+ "src/project-source/lower-variants.ts"() {
59083
+ "use strict";
59084
+ init_src();
59085
+ init_lower_support();
59086
+ init_lower_members();
59087
+ }
59088
+ });
59089
+
58788
59090
  // src/project-source/root-value-paths.ts
58789
59091
  function rootValuePathsByValueId(records2) {
58790
59092
  const indexed = indexRecords(records2);
@@ -75068,285 +75370,6 @@ var init_lower_relations = __esm({
75068
75370
  }
75069
75371
  });
75070
75372
 
75071
- // src/project-source/lower-variants.ts
75072
- function lowerVariants(context, analysis) {
75073
- const folders = lowerVariantFolders(context, analysis);
75074
- const foldersBySymbol = new Map(
75075
- analysis.variants.globals.filter((global) => isNeoVariantFolderTypeName(global.type.name)).map(
75076
- (global) => [
75077
- folderSymbolKey(owningClassName(global), global.name),
75078
- variantRecordId(global, global.type.name)
75079
- ]
75080
- )
75081
- );
75082
- const variants = variantGlobals(analysis).map((global) => {
75083
- const className = owningClassName(global);
75084
- const classId = requiredName2(context.classIdsByName, className, "class");
75085
- const variantId = variantRecordId(global, global.type.name);
75086
- return {
75087
- id: variantId,
75088
- source: sourceIdentity2(global, "variant", global.name),
75089
- classId,
75090
- name: global.name,
75091
- folderId: variantFolderId2(global, className, foldersBySymbol),
75092
- valueId: variantRootValueId(context, variantId, global, className)
75093
- };
75094
- });
75095
- return { variants, variantFolders: folders };
75096
- }
75097
- function variantValueBindingsV4(manifest, analysis) {
75098
- if (manifest.variants.length === 0) return [];
75099
- const variantsById = new Map(
75100
- manifest.variants.map((variant) => [variant.id, variant])
75101
- );
75102
- const classNames = new Map(
75103
- manifest.classes.map((entry) => [entry.id, entry.name])
75104
- );
75105
- return variantGlobals(analysis).flatMap((global) => {
75106
- const variant = variantsById.get(
75107
- variantRecordId(global, NEO_VARIANT_TYPE_NAME)
75108
- );
75109
- if (variant === void 0) return [];
75110
- return [
75111
- {
75112
- variantId: variant.id,
75113
- targetClassId: variant.classId,
75114
- targetClassName: requiredClassName2(classNames, variant.classId),
75115
- variantTypeName: global.type.name,
75116
- valueClassId: global.type.name === NEO_LOOKUP_VARIANT_TYPE_NAME ? requiredName2(
75117
- new Map(
75118
- manifest.classes.map((entry) => [entry.name, entry.id])
75119
- ),
75120
- valueClassName(global),
75121
- "class"
75122
- ) : null,
75123
- valueId: variant.valueId,
75124
- initializer: global.initializer,
75125
- source: global.source,
75126
- label: `${requiredClassName2(classNames, variant.classId)}.${NEO_VARIANT_SCOPE_NAME}.${variant.name}`
75127
- }
75128
- ];
75129
- });
75130
- }
75131
- function requiredClassName2(classNames, classId) {
75132
- const name = classNames.get(classId);
75133
- if (name === void 0) {
75134
- throw new Error(
75135
- `Variant targets class ${JSON.stringify(classId)}, which this push does not declare.`
75136
- );
75137
- }
75138
- return name;
75139
- }
75140
- function variantGlobals(analysis) {
75141
- return analysis.variants.globals.filter(
75142
- (global) => isNeoVariantTypeName(global.type.name)
75143
- );
75144
- }
75145
- function lowerVariantFolders(context, analysis) {
75146
- return analysis.variants.globals.filter((global) => isNeoVariantFolderTypeName(global.type.name)).map((global) => {
75147
- const className = owningClassName(global);
75148
- return {
75149
- id: variantRecordId(global, NEO_VARIANT_FOLDER_TYPE_NAME),
75150
- source: sourceIdentity2(global, "variantFolder", global.name),
75151
- classId: requiredName2(context.classIdsByName, className, "class"),
75152
- path: folderPath2(global),
75153
- binding: folderBinding(context, global)
75154
- };
75155
- });
75156
- }
75157
- function owningClassName(global) {
75158
- const argument2 = global.type.arguments[0]?.name;
75159
- if (argument2 === void 0) {
75160
- throw new Error(
75161
- `${global.type.name} ${global.name} must name the class it belongs to as its single type argument.`
75162
- );
75163
- }
75164
- return argument2;
75165
- }
75166
- function variantRecordId(global, typeName) {
75167
- const kind = isNeoVariantTypeName(typeName) ? "variant" : "variant-folder";
75168
- return materializedIdentityId(global, kind, global.name);
75169
- }
75170
- function variantRootValueId(context, variantId, global, className) {
75171
- const stored = storedVariantValueId(context, variantId);
75172
- if (stored !== null) return stored;
75173
- return `__pending__:value:${encodeURIComponent(global.source.uri)}:${global.source.range.start.line}:${global.source.range.start.character}:${encodeURIComponent(`${className}.${NEO_VARIANT_SCOPE_NAME}.${global.name}`)}`;
75174
- }
75175
- function storedVariantValueId(context, variantId) {
75176
- const stored = context.base.variants.find(
75177
- (variant) => variant.id === variantId
75178
- );
75179
- return stored?.valueId ?? null;
75180
- }
75181
- function variantFolderId2(global, className, foldersBySymbol) {
75182
- const symbol = neoVariantFolderAnnotationSymbol(
75183
- annotationsForFolderLookup(global.annotations)
75184
- );
75185
- if (symbol === null) return null;
75186
- const folderId = foldersBySymbol.get(folderSymbolKey(className, symbol));
75187
- if (folderId === void 0) {
75188
- throw new Error(
75189
- `Variant ${className}.${NEO_VARIANT_SCOPE_NAME}.${global.name} names folder ${JSON.stringify(symbol)}, which no NeoVariantFolder<${className}> declares.`
75190
- );
75191
- }
75192
- return folderId;
75193
- }
75194
- function annotationsForFolderLookup(annotations) {
75195
- return annotations.map((annotation2) => ({
75196
- name: annotation2.name,
75197
- arguments: annotation2.arguments.map((argument2) => ({
75198
- ...argument2.name === void 0 ? {} : { name: argument2.name },
75199
- text: argument2.expression
75200
- }))
75201
- }));
75202
- }
75203
- function folderSymbolKey(className, symbol) {
75204
- return `${className} ${symbol}`;
75205
- }
75206
- function folderPath2(global) {
75207
- const expression = unwrapAnnotated2(parseExpression(global.initializer));
75208
- if (expression.kind !== "new") {
75209
- throw new Error(
75210
- `Variant folder ${global.name} must be declared as new("Path").`
75211
- );
75212
- }
75213
- const pathIndex = expression.argumentNames?.findIndex(
75214
- (name) => name === "path"
75215
- );
75216
- const argument2 = expression.args[pathIndex !== void 0 && pathIndex >= 0 ? pathIndex : 0];
75217
- if (argument2 === void 0 || argument2.kind !== "litString") {
75218
- throw new Error(
75219
- `Variant folder ${global.name} requires one string-literal path argument.`
75220
- );
75221
- }
75222
- if (argument2.value.length === 0 && global.type.name !== NEO_LOOKUP_VARIANT_FOLDER_TYPE_NAME) {
75223
- throw new Error(
75224
- `Only a bound lookup variant folder may have an empty path.`
75225
- );
75226
- }
75227
- return argument2.value;
75228
- }
75229
- function unwrapAnnotated2(expression) {
75230
- let current = expression;
75231
- while (current.kind === "annotated") current = current.expression;
75232
- return current;
75233
- }
75234
- function buildVariantPathIndexV4(classIdsByName, analysis) {
75235
- const index = /* @__PURE__ */ new Map();
75236
- const foldersBySymbol = new Map(
75237
- analysis.variants.globals.filter((global) => isNeoVariantFolderTypeName(global.type.name)).map(
75238
- (global) => [
75239
- folderSymbolKey(owningClassName(global), global.name),
75240
- folderPath2(global)
75241
- ]
75242
- )
75243
- );
75244
- for (const [className, classId] of classIdsByName) {
75245
- index.set(
75246
- `${className}.${NEO_VARIANT_SCOPE_NAME}.${NEO_VARIANT_RESERVED_NAME}`,
75247
- {
75248
- classId,
75249
- variantId: null,
75250
- valueClassId: null
75251
- }
75252
- );
75253
- }
75254
- for (const global of variantGlobals(analysis)) {
75255
- const className = owningClassName(global);
75256
- const classId = classIdsByName.get(className);
75257
- if (classId === void 0) continue;
75258
- const symbol = neoVariantFolderAnnotationSymbol(
75259
- annotationsForFolderLookup(global.annotations)
75260
- );
75261
- const path = symbol === null ? null : foldersBySymbol.get(folderSymbolKey(className, symbol)) ?? null;
75262
- const scope = path === null ? `${className}.${NEO_VARIANT_SCOPE_NAME}` : `${className}.${NEO_VARIANT_SCOPE_NAME}.${path.split(NEO_VARIANT_FOLDER_PATH_SEPARATOR).join(".")}`;
75263
- index.set(`${scope}.${global.name}`, {
75264
- classId,
75265
- variantId: variantRecordId(global, global.type.name),
75266
- valueClassId: global.type.name === NEO_LOOKUP_VARIANT_TYPE_NAME ? classIdsByName.get(valueClassName(global)) ?? null : null
75267
- });
75268
- }
75269
- return index;
75270
- }
75271
- function valueClassName(global) {
75272
- const argument2 = global.type.arguments[1]?.name;
75273
- if (argument2 === void 0) {
75274
- throw new Error(
75275
- `${global.type.name} ${global.name} requires a TValue type argument.`
75276
- );
75277
- }
75278
- return argument2;
75279
- }
75280
- function folderBinding(context, global) {
75281
- if (global.type.name !== NEO_LOOKUP_VARIANT_FOLDER_TYPE_NAME) return null;
75282
- const expression = unwrapAnnotated2(parseExpression(global.initializer));
75283
- if (expression.kind !== "new") {
75284
- throw new Error(
75285
- `Lookup variant folder ${global.name} must be declared with new(...).`
75286
- );
75287
- }
75288
- const named = /* @__PURE__ */ new Map();
75289
- expression.args.forEach((argument2, index) => {
75290
- const name = expression.argumentNames?.[index];
75291
- if (name !== void 0 && name !== null) named.set(name, argument2);
75292
- });
75293
- const collection = named.get("collection");
75294
- const collectionValue = named.get("collectionValue");
75295
- const collectionPath = collection === void 0 ? null : dottedPath(collection);
75296
- if (collectionPath === null) {
75297
- throw new Error(
75298
- `Lookup variant folder ${global.name} requires collection: Class.Member.`
75299
- );
75300
- }
75301
- const collectionMemberId = resolveQualifiedMemberId(context, collectionPath);
75302
- if (collectionMemberId === null) {
75303
- throw new Error(
75304
- `Lookup variant folder ${global.name} names unknown collection ${collectionPath}.`
75305
- );
75306
- }
75307
- if (collectionValue === void 0) {
75308
- throw new Error(
75309
- `Lookup variant folder ${global.name} requires collectionValue: Reference(...).`
75310
- );
75311
- }
75312
- const collectionValueId = lowerReferenceValue(collectionValue);
75313
- if (collectionValueId === null) {
75314
- throw new Error(
75315
- `Lookup variant folder ${global.name} has an invalid collectionValue reference.`
75316
- );
75317
- }
75318
- return { collectionMemberId, collectionValueId };
75319
- }
75320
- function lowerReferenceValue(expression) {
75321
- if (expression.kind !== "call" || expression.callee.kind !== "ident" || expression.callee.name !== "Reference")
75322
- return null;
75323
- const idIndex = expression.argumentNames?.findIndex((name) => name === "id") ?? -1;
75324
- const target = expression.args[idIndex >= 0 ? idIndex : 0];
75325
- if (target?.kind === "litString") return target.value;
75326
- const path = target === void 0 ? null : dottedPath(target);
75327
- if (path === null || !path.startsWith("root.")) return null;
75328
- return referenceId(`Reference(${path})`);
75329
- }
75330
- function dottedPath(expression) {
75331
- const parts = [];
75332
- let cursor = expression;
75333
- while (cursor.kind === "member") {
75334
- parts.unshift(cursor.name);
75335
- cursor = cursor.receiver;
75336
- }
75337
- if (cursor.kind !== "ident") return null;
75338
- parts.unshift(cursor.name);
75339
- return parts.join(".");
75340
- }
75341
- var init_lower_variants = __esm({
75342
- "src/project-source/lower-variants.ts"() {
75343
- "use strict";
75344
- init_src();
75345
- init_lower_support();
75346
- init_lower_members();
75347
- }
75348
- });
75349
-
75350
75373
  // src/project-source/lower-schema.ts
75351
75374
  function lowerProjectSchemaV4(base, analysis, options = {}) {
75352
75375
  const classIdsByName = identityMap(analysis.schema.classes, "class");
@@ -95635,6 +95658,12 @@ function buildValueLowerContext(state, manifest, options = {}) {
95635
95658
  const classesByName = new Map(
95636
95659
  manifest.classes.map((entry) => [entry.name, entry])
95637
95660
  );
95661
+ const variantPathTargets = options.analysis === void 0 ? /* @__PURE__ */ new Map() : buildVariantPathIndexV4(
95662
+ new Map(
95663
+ manifest.classes.map((entry) => [entry.name, entry.id])
95664
+ ),
95665
+ options.analysis
95666
+ );
95638
95667
  const sourceConstructorIndex = options.analysis === void 0 ? EMPTY_DECLARED_CONSTRUCTOR_INDEX : buildDeclaredConstructorIndex(options.analysis);
95639
95668
  const declaredConstructorIndex = withPersistedConstructorSignatures(
95640
95669
  sourceConstructorIndex,
@@ -95661,6 +95690,7 @@ function buildValueLowerContext(state, manifest, options = {}) {
95661
95690
  ),
95662
95691
  staticValueIdsBySymbol: staticTargets,
95663
95692
  rootValueTargetsByPath: rootValueTargetsByPath(Object.values(state)),
95693
+ variantPathTargets,
95664
95694
  dialogueTargetsBySymbol: dialogueTargetsBySymbol(options.analysis),
95665
95695
  projectFileIdsBySymbol: projectFileIdsBySymbol(state, options.analysis),
95666
95696
  mainLocale: projectMainLocaleFromState(state),
@@ -97962,6 +97992,20 @@ function lowerValueBody(context, member, expression, base, source, environment,
97962
97992
  ...next,
97963
97993
  value: lowerActionValue(context, expression, source.ownerClassId)
97964
97994
  };
97995
+ case "variant":
97996
+ return {
97997
+ ...next,
97998
+ value: lowerVariantDefault(
97999
+ context,
98000
+ expression,
98001
+ {
98002
+ name: member.valueType === null ? NEO_VARIANT_TYPE_NAME : NEO_LOOKUP_VARIANT_TYPE_NAME,
98003
+ nullable: !member.required,
98004
+ arguments: []
98005
+ },
98006
+ `${source.label}.${member.name}`
98007
+ )
98008
+ };
97965
98009
  case "class":
97966
98010
  return lowerClassValue(
97967
98011
  context,
@@ -101629,6 +101673,7 @@ var init_value_sources = __esm({
101629
101673
  init_projection();
101630
101674
  init_project_file_source();
101631
101675
  init_lower_members();
101676
+ init_lower_variants();
101632
101677
  init_key_reference_spelling();
101633
101678
  init_member_records();
101634
101679
  init_class_generics();
@@ -111474,7 +111519,7 @@ var init_registry2 = __esm({
111474
111519
  PROJECT_SCHEMA_CONTRACT = Object.freeze({
111475
111520
  formatVersion: 3,
111476
111521
  contractVersion: "3.13",
111477
- cliVersion: "0.31.10",
111522
+ cliVersion: "0.31.11",
111478
111523
  projectFileUploadBatchSize: 32,
111479
111524
  documentRecords: {
111480
111525
  member: {
@@ -118069,7 +118114,7 @@ There is no --keep-current: preserving current semantic state defines flatten.
118069
118114
  async function main() {
118070
118115
  const args = parseArgs(process.argv.slice(2));
118071
118116
  if (args.command === "--version") {
118072
- console.log("0.31.10");
118117
+ console.log("0.31.11");
118073
118118
  return;
118074
118119
  }
118075
118120
  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.31.10",
3
+ "version": "0.31.11",
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.31.10 -->
12
+ <!-- reviewed-through-cli: 0.31.11 -->
13
13
 
14
14
  # Neo Compose CLI
15
15
 
@@ -83,7 +83,7 @@ wrappers.
83
83
  The marker near the top of `SKILL.md` must exactly match the package version:
84
84
 
85
85
  ```html
86
- <!-- reviewed-through-cli: 0.31.10 -->
86
+ <!-- reviewed-through-cli: 0.31.11 -->
87
87
  ```
88
88
 
89
89
  The quoted version above is checked too, so this instruction cannot go stale