@neocompose/cli 0.31.10 → 0.31.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,24 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.31.12] - 2026-08-17
4
+
5
+ ### Fixed
6
+
7
+ - Re-emit stored variant member selections through canonical
8
+ `<Class>.Variants...` paths, including variants nested in class and list
9
+ rows, instead of failing with an unsupported numeric value kind.
10
+
11
+ ## [0.31.11] - 2026-08-17
12
+
13
+ ### Fixed
14
+
15
+ - Resolve lookup variants declared in root-path folders from member defaults
16
+ without adding an unaddressable empty path segment.
17
+ - Allow stored static class rows to assign variant members with canonical
18
+ `<Class>.Variants...` selections.
19
+ - Recognize nested value returns in variant `initialize` delegates and retain
20
+ declared `is` bindings after terminating guards.
21
+
3
22
  ## [0.31.10] - 2026-08-17
4
23
 
5
24
  ### 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 {
@@ -49611,6 +49634,93 @@ var init_structured_leaf_source = __esm({
49611
49634
  }
49612
49635
  });
49613
49636
 
49637
+ // src/project-source/variant-selection-source.ts
49638
+ function isObject2(value) {
49639
+ return typeof value === "object" && value !== null && !Array.isArray(value);
49640
+ }
49641
+ function buildVariantPathsByIdV4(manifest) {
49642
+ const variants = manifest.variants ?? [];
49643
+ if (variants.length === 0) return /* @__PURE__ */ new Map();
49644
+ const classNames = new Map(
49645
+ manifest.classes.map((entry) => [entry.id, entry.name])
49646
+ );
49647
+ const folderPaths = new Map(
49648
+ (manifest.variantFolders ?? []).map(
49649
+ (entry) => [entry.id, entry.path]
49650
+ )
49651
+ );
49652
+ const paths = /* @__PURE__ */ new Map();
49653
+ for (const variant of variants) {
49654
+ const className = classNames.get(variant.classId);
49655
+ if (className === void 0) continue;
49656
+ paths.set(
49657
+ variant.id,
49658
+ neoVariantDottedPath({
49659
+ className,
49660
+ folderPath: variant.folderId === null ? "" : folderPaths.get(variant.folderId) ?? "",
49661
+ variantName: variant.name
49662
+ })
49663
+ );
49664
+ }
49665
+ return paths;
49666
+ }
49667
+ function renderVariantSelectionV4(context, memberName, value) {
49668
+ if (value === null) return "null";
49669
+ if (!isObject2(value)) {
49670
+ throw new Error(
49671
+ `Variant member ${memberName} stores ${JSON.stringify(value)}, which is not a { classId, variantId } selection.`
49672
+ );
49673
+ }
49674
+ const variantId = value.variantId;
49675
+ if (variantId === null) {
49676
+ const classId = value.classId;
49677
+ if (typeof classId !== "string") {
49678
+ throw new Error(
49679
+ `Variant member ${memberName} selects the base entry without naming a class.`
49680
+ );
49681
+ }
49682
+ const className = context.classes.get(classId)?.name;
49683
+ if (className === void 0) {
49684
+ throw new Error(
49685
+ `Variant member ${memberName} selects class ${JSON.stringify(classId)}, which this project does not declare.`
49686
+ );
49687
+ }
49688
+ return neoVariantDottedPath({
49689
+ className,
49690
+ folderPath: "",
49691
+ variantName: null
49692
+ });
49693
+ }
49694
+ if (typeof variantId !== "string") {
49695
+ throw new Error(
49696
+ `Variant member ${memberName} stores a variant id that is neither a string nor null.`
49697
+ );
49698
+ }
49699
+ const path = context.variantPathsById.get(variantId);
49700
+ if (path === void 0) {
49701
+ throw new Error(
49702
+ `Variant member ${memberName} selects variant ${JSON.stringify(variantId)}, which this project does not declare.`
49703
+ );
49704
+ }
49705
+ const rowValueId = value.rowValueId;
49706
+ if (rowValueId === null || rowValueId === void 0) return path;
49707
+ if (typeof rowValueId !== "string") {
49708
+ throw new Error(
49709
+ `Variant member ${memberName} stores a malformed rowValueId.`
49710
+ );
49711
+ }
49712
+ const rootPath = context.collectionValuePaths.get(rowValueId);
49713
+ const reference2 = rootPath === void 0 ? `Reference(id: ${quoteNeoString(rowValueId)})` : `Reference(${rootPath})`;
49714
+ return `${path}.Bind(${reference2})`;
49715
+ }
49716
+ var init_variant_selection_source = __esm({
49717
+ "src/project-source/variant-selection-source.ts"() {
49718
+ "use strict";
49719
+ init_src();
49720
+ init_source_format();
49721
+ }
49722
+ });
49723
+
49614
49724
  // src/project-source/emitter.ts
49615
49725
  function emitProjectSourcesV4(manifest, options = {}) {
49616
49726
  const context = {
@@ -49626,7 +49736,7 @@ function emitProjectSourcesV4(manifest, options = {}) {
49626
49736
  defaultInitializers: options.defaultInitializers ?? /* @__PURE__ */ new Map(),
49627
49737
  fileSymbols: options.fileSymbols ?? /* @__PURE__ */ new Map(),
49628
49738
  collectionValuePaths: options.collectionValuePaths ?? /* @__PURE__ */ new Map(),
49629
- variantPathsById: buildVariantPathsById(manifest),
49739
+ variantPathsById: buildVariantPathsByIdV4(manifest),
49630
49740
  templateNames: new Map(
49631
49741
  [...manifest.textureTemplates, ...manifest.audioTemplates].map(
49632
49742
  (template) => [template.id, template.name]
@@ -50502,7 +50612,7 @@ function renderDefault(context, member) {
50502
50612
  return renderDefaultValue(context, member, value);
50503
50613
  }
50504
50614
  function renderActionListeners(context, member, value) {
50505
- if (!isObject2(value)) {
50615
+ if (!isObject3(value)) {
50506
50616
  throw new Error(
50507
50617
  `NeoAction member ${member.name} stores a default that is not a listener set.`
50508
50618
  );
@@ -50514,7 +50624,7 @@ function renderActionListeners(context, member, value) {
50514
50624
  );
50515
50625
  }
50516
50626
  const names = listeners.map((listener, index) => {
50517
- if (!isObject2(listener)) {
50627
+ if (!isObject3(listener)) {
50518
50628
  throw new Error(
50519
50629
  `NeoAction member ${member.name} listener ${index} is not a member target.`
50520
50630
  );
@@ -50575,7 +50685,7 @@ function renderDefaultValue(context, member, wrapper) {
50575
50685
  const entry = required(context.members, member.entryMemberId, "list entry");
50576
50686
  return `[${value.map((item) => renderNestedValue(context, entry, item)).join(", ")}]`;
50577
50687
  }
50578
- if (member.kind === "dictionary" && isObject2(value)) {
50688
+ if (member.kind === "dictionary" && isObject3(value)) {
50579
50689
  const entry = required(
50580
50690
  context.members,
50581
50691
  member.entryMemberId,
@@ -50585,7 +50695,7 @@ function renderDefaultValue(context, member, wrapper) {
50585
50695
  ([key, item]) => `${quote(key)}: ${renderNestedValue(context, entry, item)}`
50586
50696
  ).join(", ")} }`;
50587
50697
  }
50588
- if (member.kind === "class" && isObject2(value)) {
50698
+ if (member.kind === "class" && isObject3(value)) {
50589
50699
  const target = required(context.classes, member.classId, "class");
50590
50700
  const assignments = Object.entries(value).map(([key, item]) => {
50591
50701
  const childId = target.schema[key];
@@ -50602,9 +50712,9 @@ function renderDefaultValue(context, member, wrapper) {
50602
50712
  return renderActionListeners(context, member, value);
50603
50713
  }
50604
50714
  if (member.kind === "variant") {
50605
- return renderVariantSelection(context, member, value);
50715
+ return renderVariantSelectionV4(context, member.name, value);
50606
50716
  }
50607
- if (member.kind === "functionRef" && isObject2(value) && typeof value.functionMemberId === "string") {
50717
+ if (member.kind === "functionRef" && isObject3(value) && typeof value.functionMemberId === "string") {
50608
50718
  return required(
50609
50719
  context.members,
50610
50720
  value.functionMemberId,
@@ -50640,7 +50750,7 @@ function renderDefaultValue(context, member, wrapper) {
50640
50750
  return `new { ${assignments.join(", ")} }`;
50641
50751
  }
50642
50752
  if (member.kind === "sprite" || member.kind === "audio") {
50643
- if (!isObject2(value)) return jsonValue2(value);
50753
+ if (!isObject3(value)) return jsonValue2(value);
50644
50754
  if (typeof value.fileId !== "string") {
50645
50755
  throw new Error(
50646
50756
  `${member.kind === "sprite" ? "Sprite" : "Audio"} member ${JSON.stringify(member.name)} default stores a file value with no fileId. Emitting it as null would delete the default on the next push.`
@@ -50659,7 +50769,7 @@ function renderDefaultValue(context, member, wrapper) {
50659
50769
  const target = symbol ?? (member.kind === "sprite" ? `Reference<NeoImage>(id: ${quote(value.fileId)})` : `Reference<NeoAudioClip>(id: ${quote(value.fileId)})`);
50660
50770
  return member.kind === "sprite" ? `${target}.Slice(${typeof value.sliceIndex === "number" ? value.sliceIndex : 0})` : target;
50661
50771
  }
50662
- if (leafKind !== null && isObject2(value)) {
50772
+ if (leafKind !== null && isObject3(value)) {
50663
50773
  return `new(${structuredLeafFieldKeys(leafKind).map((key) => jsonValue2(value[key])).join(", ")})`;
50664
50774
  }
50665
50775
  return jsonValue2(value);
@@ -50954,7 +51064,7 @@ function jsonValue2(value) {
50954
51064
  if (typeof value === "boolean") return String(value);
50955
51065
  if (value === null || value === void 0) return "null";
50956
51066
  if (Array.isArray(value)) return `[${value.map(jsonValue2).join(", ")}]`;
50957
- if (isObject2(value))
51067
+ if (isObject3(value))
50958
51068
  return `{ ${Object.entries(value).map(([key, entry]) => `${quote(key)}: ${jsonValue2(entry)}`).join(", ")} }`;
50959
51069
  throw new Error(`Cannot emit Neo value ${String(value)}.`);
50960
51070
  }
@@ -51159,81 +51269,9 @@ function required(map, key, kind) {
51159
51269
  if (!value) throw new Error(`Unknown ${kind} ${quote(key)}.`);
51160
51270
  return value;
51161
51271
  }
51162
- function isObject2(value) {
51272
+ function isObject3(value) {
51163
51273
  return typeof value === "object" && value !== null && !Array.isArray(value);
51164
51274
  }
51165
- function buildVariantPathsById(manifest) {
51166
- if (manifest.variants.length === 0) return /* @__PURE__ */ new Map();
51167
- const classNames = new Map(
51168
- manifest.classes.map((entry) => [entry.id, entry.name])
51169
- );
51170
- const folderPaths = new Map(
51171
- manifest.variantFolders.map((entry) => [entry.id, entry.path])
51172
- );
51173
- const paths = /* @__PURE__ */ new Map();
51174
- for (const variant of manifest.variants) {
51175
- const className = classNames.get(variant.classId);
51176
- if (className === void 0) continue;
51177
- paths.set(
51178
- variant.id,
51179
- neoVariantDottedPath({
51180
- className,
51181
- folderPath: variant.folderId === null ? "" : folderPaths.get(variant.folderId) ?? "",
51182
- variantName: variant.name
51183
- })
51184
- );
51185
- }
51186
- return paths;
51187
- }
51188
- function renderVariantSelection(context, member, value) {
51189
- if (value === null) return "null";
51190
- if (!isObject2(value)) {
51191
- throw new Error(
51192
- `Variant member ${member.name} stores ${JSON.stringify(value)}, which is not a { classId, variantId } selection.`
51193
- );
51194
- }
51195
- const variantId = value.variantId;
51196
- if (variantId === null) {
51197
- const classId = value.classId;
51198
- if (typeof classId !== "string") {
51199
- throw new Error(
51200
- `Variant member ${member.name} selects the base entry without naming a class.`
51201
- );
51202
- }
51203
- const className = context.classes.get(classId)?.name;
51204
- if (className === void 0) {
51205
- throw new Error(
51206
- `Variant member ${member.name} selects class ${JSON.stringify(classId)}, which this project does not declare.`
51207
- );
51208
- }
51209
- return neoVariantDottedPath({
51210
- className,
51211
- folderPath: "",
51212
- variantName: null
51213
- });
51214
- }
51215
- if (typeof variantId !== "string") {
51216
- throw new Error(
51217
- `Variant member ${member.name} stores a variant id that is neither a string nor null.`
51218
- );
51219
- }
51220
- const path = context.variantPathsById.get(variantId);
51221
- if (path === void 0) {
51222
- throw new Error(
51223
- `Variant member ${member.name} selects variant ${JSON.stringify(variantId)}, which this project does not declare.`
51224
- );
51225
- }
51226
- const rowValueId = value.rowValueId;
51227
- if (rowValueId === null || rowValueId === void 0) return path;
51228
- if (typeof rowValueId !== "string") {
51229
- throw new Error(
51230
- `Variant member ${member.name} stores a malformed rowValueId.`
51231
- );
51232
- }
51233
- const rootPath = context.collectionValuePaths.get(rowValueId);
51234
- const reference2 = rootPath === void 0 ? `Reference(id: ${quote(rowValueId)})` : `Reference(${rootPath})`;
51235
- return `${path}.Bind(${reference2})`;
51236
- }
51237
51275
  var SPECIALIZED_RELATION_PROPERTIES;
51238
51276
  var init_emitter = __esm({
51239
51277
  "src/project-source/emitter.ts"() {
@@ -51244,6 +51282,7 @@ var init_emitter = __esm({
51244
51282
  init_class_generics();
51245
51283
  init_members();
51246
51284
  init_structured_leaf_source();
51285
+ init_variant_selection_source();
51247
51286
  SPECIALIZED_RELATION_PROPERTIES = /* @__PURE__ */ new Map([
51248
51287
  ["world.grid.tile-import", { property: "tileImports", collection: true }],
51249
51288
  ["world.grid.object-import", { property: "objectImports", collection: true }],
@@ -58785,6 +58824,285 @@ var init_lower_members = __esm({
58785
58824
  }
58786
58825
  });
58787
58826
 
58827
+ // src/project-source/lower-variants.ts
58828
+ function lowerVariants(context, analysis) {
58829
+ const folders = lowerVariantFolders(context, analysis);
58830
+ const foldersBySymbol = new Map(
58831
+ analysis.variants.globals.filter((global) => isNeoVariantFolderTypeName(global.type.name)).map(
58832
+ (global) => [
58833
+ folderSymbolKey(owningClassName(global), global.name),
58834
+ variantRecordId(global, global.type.name)
58835
+ ]
58836
+ )
58837
+ );
58838
+ const variants = variantGlobals(analysis).map((global) => {
58839
+ const className = owningClassName(global);
58840
+ const classId = requiredName2(context.classIdsByName, className, "class");
58841
+ const variantId = variantRecordId(global, global.type.name);
58842
+ return {
58843
+ id: variantId,
58844
+ source: sourceIdentity2(global, "variant", global.name),
58845
+ classId,
58846
+ name: global.name,
58847
+ folderId: variantFolderId2(global, className, foldersBySymbol),
58848
+ valueId: variantRootValueId(context, variantId, global, className)
58849
+ };
58850
+ });
58851
+ return { variants, variantFolders: folders };
58852
+ }
58853
+ function variantValueBindingsV4(manifest, analysis) {
58854
+ if (manifest.variants.length === 0) return [];
58855
+ const variantsById = new Map(
58856
+ manifest.variants.map((variant) => [variant.id, variant])
58857
+ );
58858
+ const classNames = new Map(
58859
+ manifest.classes.map((entry) => [entry.id, entry.name])
58860
+ );
58861
+ return variantGlobals(analysis).flatMap((global) => {
58862
+ const variant = variantsById.get(
58863
+ variantRecordId(global, NEO_VARIANT_TYPE_NAME)
58864
+ );
58865
+ if (variant === void 0) return [];
58866
+ return [
58867
+ {
58868
+ variantId: variant.id,
58869
+ targetClassId: variant.classId,
58870
+ targetClassName: requiredClassName2(classNames, variant.classId),
58871
+ variantTypeName: global.type.name,
58872
+ valueClassId: global.type.name === NEO_LOOKUP_VARIANT_TYPE_NAME ? requiredName2(
58873
+ new Map(
58874
+ manifest.classes.map((entry) => [entry.name, entry.id])
58875
+ ),
58876
+ valueClassName(global),
58877
+ "class"
58878
+ ) : null,
58879
+ valueId: variant.valueId,
58880
+ initializer: global.initializer,
58881
+ source: global.source,
58882
+ label: `${requiredClassName2(classNames, variant.classId)}.${NEO_VARIANT_SCOPE_NAME}.${variant.name}`
58883
+ }
58884
+ ];
58885
+ });
58886
+ }
58887
+ function requiredClassName2(classNames, classId) {
58888
+ const name = classNames.get(classId);
58889
+ if (name === void 0) {
58890
+ throw new Error(
58891
+ `Variant targets class ${JSON.stringify(classId)}, which this push does not declare.`
58892
+ );
58893
+ }
58894
+ return name;
58895
+ }
58896
+ function variantGlobals(analysis) {
58897
+ return analysis.variants.globals.filter(
58898
+ (global) => isNeoVariantTypeName(global.type.name)
58899
+ );
58900
+ }
58901
+ function lowerVariantFolders(context, analysis) {
58902
+ return analysis.variants.globals.filter((global) => isNeoVariantFolderTypeName(global.type.name)).map((global) => {
58903
+ const className = owningClassName(global);
58904
+ return {
58905
+ id: variantRecordId(global, NEO_VARIANT_FOLDER_TYPE_NAME),
58906
+ source: sourceIdentity2(global, "variantFolder", global.name),
58907
+ classId: requiredName2(context.classIdsByName, className, "class"),
58908
+ path: folderPath2(global),
58909
+ binding: folderBinding(context, global)
58910
+ };
58911
+ });
58912
+ }
58913
+ function owningClassName(global) {
58914
+ const argument2 = global.type.arguments[0]?.name;
58915
+ if (argument2 === void 0) {
58916
+ throw new Error(
58917
+ `${global.type.name} ${global.name} must name the class it belongs to as its single type argument.`
58918
+ );
58919
+ }
58920
+ return argument2;
58921
+ }
58922
+ function variantRecordId(global, typeName) {
58923
+ const kind = isNeoVariantTypeName(typeName) ? "variant" : "variant-folder";
58924
+ return materializedIdentityId(global, kind, global.name);
58925
+ }
58926
+ function variantRootValueId(context, variantId, global, className) {
58927
+ const stored = storedVariantValueId(context, variantId);
58928
+ if (stored !== null) return stored;
58929
+ 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}`)}`;
58930
+ }
58931
+ function storedVariantValueId(context, variantId) {
58932
+ const stored = context.base.variants.find(
58933
+ (variant) => variant.id === variantId
58934
+ );
58935
+ return stored?.valueId ?? null;
58936
+ }
58937
+ function variantFolderId2(global, className, foldersBySymbol) {
58938
+ const symbol = neoVariantFolderAnnotationSymbol(
58939
+ annotationsForFolderLookup(global.annotations)
58940
+ );
58941
+ if (symbol === null) return null;
58942
+ const folderId = foldersBySymbol.get(folderSymbolKey(className, symbol));
58943
+ if (folderId === void 0) {
58944
+ throw new Error(
58945
+ `Variant ${className}.${NEO_VARIANT_SCOPE_NAME}.${global.name} names folder ${JSON.stringify(symbol)}, which no NeoVariantFolder<${className}> declares.`
58946
+ );
58947
+ }
58948
+ return folderId;
58949
+ }
58950
+ function annotationsForFolderLookup(annotations) {
58951
+ return annotations.map((annotation2) => ({
58952
+ name: annotation2.name,
58953
+ arguments: annotation2.arguments.map((argument2) => ({
58954
+ ...argument2.name === void 0 ? {} : { name: argument2.name },
58955
+ text: argument2.expression
58956
+ }))
58957
+ }));
58958
+ }
58959
+ function folderSymbolKey(className, symbol) {
58960
+ return `${className} ${symbol}`;
58961
+ }
58962
+ function folderPath2(global) {
58963
+ const expression = unwrapAnnotated2(parseExpression(global.initializer));
58964
+ if (expression.kind !== "new") {
58965
+ throw new Error(
58966
+ `Variant folder ${global.name} must be declared as new("Path").`
58967
+ );
58968
+ }
58969
+ const pathIndex = expression.argumentNames?.findIndex(
58970
+ (name) => name === "path"
58971
+ );
58972
+ const argument2 = expression.args[pathIndex !== void 0 && pathIndex >= 0 ? pathIndex : 0];
58973
+ if (argument2 === void 0 || argument2.kind !== "litString") {
58974
+ throw new Error(
58975
+ `Variant folder ${global.name} requires one string-literal path argument.`
58976
+ );
58977
+ }
58978
+ if (argument2.value.length === 0 && global.type.name !== NEO_LOOKUP_VARIANT_FOLDER_TYPE_NAME) {
58979
+ throw new Error(
58980
+ `Only a bound lookup variant folder may have an empty path.`
58981
+ );
58982
+ }
58983
+ return argument2.value;
58984
+ }
58985
+ function unwrapAnnotated2(expression) {
58986
+ let current = expression;
58987
+ while (current.kind === "annotated") current = current.expression;
58988
+ return current;
58989
+ }
58990
+ function buildVariantPathIndexV4(classIdsByName, analysis) {
58991
+ const index = /* @__PURE__ */ new Map();
58992
+ const foldersBySymbol = new Map(
58993
+ analysis.variants.globals.filter((global) => isNeoVariantFolderTypeName(global.type.name)).map(
58994
+ (global) => [
58995
+ folderSymbolKey(owningClassName(global), global.name),
58996
+ folderPath2(global)
58997
+ ]
58998
+ )
58999
+ );
59000
+ for (const [className, classId] of classIdsByName) {
59001
+ index.set(
59002
+ `${className}.${NEO_VARIANT_SCOPE_NAME}.${NEO_VARIANT_RESERVED_NAME}`,
59003
+ {
59004
+ classId,
59005
+ variantId: null,
59006
+ valueClassId: null
59007
+ }
59008
+ );
59009
+ }
59010
+ for (const global of variantGlobals(analysis)) {
59011
+ const className = owningClassName(global);
59012
+ const classId = classIdsByName.get(className);
59013
+ if (classId === void 0) continue;
59014
+ const symbol = neoVariantFolderAnnotationSymbol(
59015
+ annotationsForFolderLookup(global.annotations)
59016
+ );
59017
+ const path = symbol === null ? null : foldersBySymbol.get(folderSymbolKey(className, symbol)) ?? null;
59018
+ const scope = path === null || path === "" ? `${className}.${NEO_VARIANT_SCOPE_NAME}` : `${className}.${NEO_VARIANT_SCOPE_NAME}.${path.split(NEO_VARIANT_FOLDER_PATH_SEPARATOR).join(".")}`;
59019
+ index.set(`${scope}.${global.name}`, {
59020
+ classId,
59021
+ variantId: variantRecordId(global, global.type.name),
59022
+ valueClassId: global.type.name === NEO_LOOKUP_VARIANT_TYPE_NAME ? classIdsByName.get(valueClassName(global)) ?? null : null
59023
+ });
59024
+ }
59025
+ return index;
59026
+ }
59027
+ function valueClassName(global) {
59028
+ const argument2 = global.type.arguments[1]?.name;
59029
+ if (argument2 === void 0) {
59030
+ throw new Error(
59031
+ `${global.type.name} ${global.name} requires a TValue type argument.`
59032
+ );
59033
+ }
59034
+ return argument2;
59035
+ }
59036
+ function folderBinding(context, global) {
59037
+ if (global.type.name !== NEO_LOOKUP_VARIANT_FOLDER_TYPE_NAME) return null;
59038
+ const expression = unwrapAnnotated2(parseExpression(global.initializer));
59039
+ if (expression.kind !== "new") {
59040
+ throw new Error(
59041
+ `Lookup variant folder ${global.name} must be declared with new(...).`
59042
+ );
59043
+ }
59044
+ const named = /* @__PURE__ */ new Map();
59045
+ expression.args.forEach((argument2, index) => {
59046
+ const name = expression.argumentNames?.[index];
59047
+ if (name !== void 0 && name !== null) named.set(name, argument2);
59048
+ });
59049
+ const collection = named.get("collection");
59050
+ const collectionValue = named.get("collectionValue");
59051
+ const collectionPath = collection === void 0 ? null : dottedPath(collection);
59052
+ if (collectionPath === null) {
59053
+ throw new Error(
59054
+ `Lookup variant folder ${global.name} requires collection: Class.Member.`
59055
+ );
59056
+ }
59057
+ const collectionMemberId = resolveQualifiedMemberId(context, collectionPath);
59058
+ if (collectionMemberId === null) {
59059
+ throw new Error(
59060
+ `Lookup variant folder ${global.name} names unknown collection ${collectionPath}.`
59061
+ );
59062
+ }
59063
+ if (collectionValue === void 0) {
59064
+ throw new Error(
59065
+ `Lookup variant folder ${global.name} requires collectionValue: Reference(...).`
59066
+ );
59067
+ }
59068
+ const collectionValueId = lowerReferenceValue(collectionValue);
59069
+ if (collectionValueId === null) {
59070
+ throw new Error(
59071
+ `Lookup variant folder ${global.name} has an invalid collectionValue reference.`
59072
+ );
59073
+ }
59074
+ return { collectionMemberId, collectionValueId };
59075
+ }
59076
+ function lowerReferenceValue(expression) {
59077
+ if (expression.kind !== "call" || expression.callee.kind !== "ident" || expression.callee.name !== "Reference")
59078
+ return null;
59079
+ const idIndex = expression.argumentNames?.findIndex((name) => name === "id") ?? -1;
59080
+ const target = expression.args[idIndex >= 0 ? idIndex : 0];
59081
+ if (target?.kind === "litString") return target.value;
59082
+ const path = target === void 0 ? null : dottedPath(target);
59083
+ if (path === null || !path.startsWith("root.")) return null;
59084
+ return referenceId(`Reference(${path})`);
59085
+ }
59086
+ function dottedPath(expression) {
59087
+ const parts = [];
59088
+ let cursor = expression;
59089
+ while (cursor.kind === "member") {
59090
+ parts.unshift(cursor.name);
59091
+ cursor = cursor.receiver;
59092
+ }
59093
+ if (cursor.kind !== "ident") return null;
59094
+ parts.unshift(cursor.name);
59095
+ return parts.join(".");
59096
+ }
59097
+ var init_lower_variants = __esm({
59098
+ "src/project-source/lower-variants.ts"() {
59099
+ "use strict";
59100
+ init_src();
59101
+ init_lower_support();
59102
+ init_lower_members();
59103
+ }
59104
+ });
59105
+
58788
59106
  // src/project-source/root-value-paths.ts
58789
59107
  function rootValuePathsByValueId(records2) {
58790
59108
  const indexed = indexRecords(records2);
@@ -72933,7 +73251,7 @@ async function fetchProjectDocumentPartitionRecords(runner, mapKey) {
72933
73251
  });
72934
73252
  }
72935
73253
  function readProjectDocumentManifestPage(value) {
72936
- if (!isObject3(value)) {
73254
+ if (!isObject4(value)) {
72937
73255
  throw new Error("Convex project document manifest page must be an object.");
72938
73256
  }
72939
73257
  const continueCursor = value.continueCursor;
@@ -72943,7 +73261,7 @@ function readProjectDocumentManifestPage(value) {
72943
73261
  );
72944
73262
  }
72945
73263
  const metadata = value.metadata ?? null;
72946
- if (metadata !== null && !isObject3(metadata)) {
73264
+ if (metadata !== null && !isObject4(metadata)) {
72947
73265
  throw new Error(
72948
73266
  "Convex project document manifest page metadata must be an object or null."
72949
73267
  );
@@ -72968,7 +73286,7 @@ function readProjectDocumentManifestRecords(value) {
72968
73286
  );
72969
73287
  }
72970
73288
  return value.map((record3, index) => {
72971
- if (!isObject3(record3)) {
73289
+ if (!isObject4(record3)) {
72972
73290
  throw new Error(
72973
73291
  `Convex project document manifest record at index ${index} was not an object.`
72974
73292
  );
@@ -73002,7 +73320,7 @@ function readProjectDocumentManifestRecords(value) {
73002
73320
  });
73003
73321
  }
73004
73322
  function readProjectDocumentManifestPageRecords(value) {
73005
- if (!isObject3(value)) {
73323
+ if (!isObject4(value)) {
73006
73324
  throw new Error("Convex project document manifest page must be an object.");
73007
73325
  }
73008
73326
  return readProjectDocumentManifestRecords(value.records);
@@ -73040,7 +73358,7 @@ async function fetchProjectDocumentSnapshots(fetchBatch, snapshotIds) {
73040
73358
  return snapshotsById;
73041
73359
  }
73042
73360
  function readProjectDocumentSnapshotBatch(value, requestedIds) {
73043
- if (!isObject3(value)) {
73361
+ if (!isObject4(value)) {
73044
73362
  throw new Error("Convex project record snapshot batch must be an object.");
73045
73363
  }
73046
73364
  const snapshots = value.snapshots;
@@ -73060,7 +73378,7 @@ function readProjectDocumentSnapshotBatch(value, requestedIds) {
73060
73378
  );
73061
73379
  }
73062
73380
  return snapshots.map((snapshot, index) => {
73063
- if (!isObject3(snapshot)) {
73381
+ if (!isObject4(snapshot)) {
73064
73382
  throw new Error(
73065
73383
  `Convex project record snapshot at index ${index} was not an object.`
73066
73384
  );
@@ -73242,7 +73560,7 @@ function appendBucketRecord(buckets, recordKind, data) {
73242
73560
  buckets[bucket].push(data);
73243
73561
  }
73244
73562
  function readProjectDocument(value, options = {}) {
73245
- if (!isObject3(value)) {
73563
+ if (!isObject4(value)) {
73246
73564
  throw new Error("Convex project document query returned a non-object.");
73247
73565
  }
73248
73566
  const localizationConfig = options.localizationConfig === "nullable" ? readOptionalNullableField(
@@ -73332,7 +73650,7 @@ function readProjectDocument(value, options = {}) {
73332
73650
  };
73333
73651
  }
73334
73652
  function readProjectDocumentRevisionMarker(value) {
73335
- if (!isObject3(value)) return null;
73653
+ if (!isObject4(value)) return null;
73336
73654
  if (!("documentRevision" in value)) return null;
73337
73655
  const marker = value.documentRevision;
73338
73656
  if (isProjectDocumentRevisionMarker(marker)) return marker;
@@ -73341,7 +73659,7 @@ function readProjectDocumentRevisionMarker(value) {
73341
73659
  );
73342
73660
  }
73343
73661
  function readProjectDocumentContentHashHeads(value) {
73344
- if (!isObject3(value)) {
73662
+ if (!isObject4(value)) {
73345
73663
  throw new Error(
73346
73664
  "Convex project document content hash source must be an object."
73347
73665
  );
@@ -73349,7 +73667,7 @@ function readProjectDocumentContentHashHeads(value) {
73349
73667
  return readArrayField(value, "projectDocumentContentHashHeads", isHashHead);
73350
73668
  }
73351
73669
  function isHashHead(value) {
73352
- if (!isObject3(value)) return false;
73670
+ if (!isObject4(value)) return false;
73353
73671
  if (!isProjectRecordKind(value.recordKind)) return false;
73354
73672
  if (typeof value.recordId !== "string") return false;
73355
73673
  if (typeof value.deleted !== "boolean") return false;
@@ -73385,12 +73703,12 @@ function readOptionalArrayField(source, key, isValid) {
73385
73703
  if (!Object.prototype.hasOwnProperty.call(source, key)) return [];
73386
73704
  return readArrayField(source, key, isValid);
73387
73705
  }
73388
- function isObject3(value) {
73706
+ function isObject4(value) {
73389
73707
  if (typeof value !== "object") return false;
73390
73708
  return value !== null;
73391
73709
  }
73392
73710
  function describeInvalidReadItem(value) {
73393
- if (!isObject3(value)) {
73711
+ if (!isObject4(value)) {
73394
73712
  return `received ${typeof value}`;
73395
73713
  }
73396
73714
  const parts = [];
@@ -75068,285 +75386,6 @@ var init_lower_relations = __esm({
75068
75386
  }
75069
75387
  });
75070
75388
 
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
75389
  // src/project-source/lower-schema.ts
75351
75390
  function lowerProjectSchemaV4(base, analysis, options = {}) {
75352
75391
  const classIdsByName = identityMap(analysis.schema.classes, "class");
@@ -95184,6 +95223,7 @@ function buildValueEmitContext(records2, manifest) {
95184
95223
  mainLocale: projectMainLocale(records2),
95185
95224
  symbolsByValueId,
95186
95225
  rootPathsByValueId: rootValuePathsByValueId(records2.values()),
95226
+ variantPathsById: buildVariantPathsByIdV4(manifest),
95187
95227
  symbolsByDialogueId: new Map(
95188
95228
  [...dialogues].flatMap(
95189
95229
  ([id2, dialogue]) => typeof dialogue.sourceName === "string" ? [[id2, dialogue.sourceName]] : []
@@ -95635,6 +95675,12 @@ function buildValueLowerContext(state, manifest, options = {}) {
95635
95675
  const classesByName = new Map(
95636
95676
  manifest.classes.map((entry) => [entry.name, entry])
95637
95677
  );
95678
+ const variantPathTargets = options.analysis === void 0 ? /* @__PURE__ */ new Map() : buildVariantPathIndexV4(
95679
+ new Map(
95680
+ manifest.classes.map((entry) => [entry.name, entry.id])
95681
+ ),
95682
+ options.analysis
95683
+ );
95638
95684
  const sourceConstructorIndex = options.analysis === void 0 ? EMPTY_DECLARED_CONSTRUCTOR_INDEX : buildDeclaredConstructorIndex(options.analysis);
95639
95685
  const declaredConstructorIndex = withPersistedConstructorSignatures(
95640
95686
  sourceConstructorIndex,
@@ -95661,6 +95707,7 @@ function buildValueLowerContext(state, manifest, options = {}) {
95661
95707
  ),
95662
95708
  staticValueIdsBySymbol: staticTargets,
95663
95709
  rootValueTargetsByPath: rootValueTargetsByPath(Object.values(state)),
95710
+ variantPathTargets,
95664
95711
  dialogueTargetsBySymbol: dialogueTargetsBySymbol(options.analysis),
95665
95712
  projectFileIdsBySymbol: projectFileIdsBySymbol(state, options.analysis),
95666
95713
  mainLocale: projectMainLocaleFromState(state),
@@ -97962,6 +98009,20 @@ function lowerValueBody(context, member, expression, base, source, environment,
97962
98009
  ...next,
97963
98010
  value: lowerActionValue(context, expression, source.ownerClassId)
97964
98011
  };
98012
+ case "variant":
98013
+ return {
98014
+ ...next,
98015
+ value: lowerVariantDefault(
98016
+ context,
98017
+ expression,
98018
+ {
98019
+ name: member.valueType === null ? NEO_VARIANT_TYPE_NAME : NEO_LOOKUP_VARIANT_TYPE_NAME,
98020
+ nullable: !member.required,
98021
+ arguments: []
98022
+ },
98023
+ `${source.label}.${member.name}`
98024
+ )
98025
+ };
97965
98026
  case "class":
97966
98027
  return lowerClassValue(
97967
98028
  context,
@@ -100054,6 +100115,17 @@ function emitValueBody(context, member, value, visited, targetTyped, environment
100054
100115
  if (kind === 24) return functionReferenceValue(context, body);
100055
100116
  if (kind === 25) return delegateValue(context, body);
100056
100117
  if (kind === 26) return actionValue(context, body);
100118
+ if (kind === 27 /* Variant */) {
100119
+ return renderVariantSelectionV4(
100120
+ {
100121
+ classes: context.manifestClasses,
100122
+ variantPathsById: context.variantPathsById,
100123
+ collectionValuePaths: context.rootPathsByValueId
100124
+ },
100125
+ String(member.name ?? member.id),
100126
+ body
100127
+ );
100128
+ }
100057
100129
  if (kind === 12) {
100058
100130
  return fileValue(
100059
100131
  context,
@@ -100076,8 +100148,9 @@ function emitValueBody(context, member, value, visited, targetTyped, environment
100076
100148
  targetTyped,
100077
100149
  environment
100078
100150
  );
100151
+ const kindName = MemberKind[kind];
100079
100152
  throw new Error(
100080
- `Stored member ${String(member.name ?? member.id)} has unsupported value kind ${kind}.`
100153
+ typeof kindName === "string" ? `Stored member ${String(member.name ?? member.id)} uses unsupported ${kindName} value kind (${kind}).` : `Stored member ${String(member.name ?? member.id)} has unknown value kind ${kind}.`
100081
100154
  );
100082
100155
  }
100083
100156
  function classValue(context, member, value, visited, targetTyped, outerEnvironment) {
@@ -101629,6 +101702,7 @@ var init_value_sources = __esm({
101629
101702
  init_projection();
101630
101703
  init_project_file_source();
101631
101704
  init_lower_members();
101705
+ init_lower_variants();
101632
101706
  init_key_reference_spelling();
101633
101707
  init_member_records();
101634
101708
  init_class_generics();
@@ -101645,6 +101719,7 @@ var init_value_sources = __esm({
101645
101719
  init_structured_leaf_source();
101646
101720
  init_member_kind_type_names();
101647
101721
  init_initializer_replay();
101722
+ init_variant_selection_source();
101648
101723
  INFERRED_GENERIC_CLASS_PREFIX = "__inferred_class__:";
101649
101724
  MEMBER_KIND_DICTIONARY2 = 5;
101650
101725
  MEMBER_KIND_LIST2 = 6;
@@ -111474,7 +111549,7 @@ var init_registry2 = __esm({
111474
111549
  PROJECT_SCHEMA_CONTRACT = Object.freeze({
111475
111550
  formatVersion: 3,
111476
111551
  contractVersion: "3.13",
111477
- cliVersion: "0.31.10",
111552
+ cliVersion: "0.31.12",
111478
111553
  projectFileUploadBatchSize: 32,
111479
111554
  documentRecords: {
111480
111555
  member: {
@@ -118069,7 +118144,7 @@ There is no --keep-current: preserving current semantic state defines flatten.
118069
118144
  async function main() {
118070
118145
  const args = parseArgs(process.argv.slice(2));
118071
118146
  if (args.command === "--version") {
118072
- console.log("0.31.10");
118147
+ console.log("0.31.12");
118073
118148
  return;
118074
118149
  }
118075
118150
  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.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.31.10 -->
12
+ <!-- reviewed-through-cli: 0.31.12 -->
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.12 -->
87
87
  ```
88
88
 
89
89
  The quoted version above is checked too, so this instruction cannot go stale
@@ -56,6 +56,17 @@ or narrowing guard before the next access.
56
56
 
57
57
  Treat lookup-return values with the same nullable narrowing rules.
58
58
 
59
+ A declaration pattern introduced by `is` stays available after a guard when
60
+ every path that falls through matched the pattern, including when the guard's
61
+ other paths return from a nested block:
62
+
63
+ ```neo
64
+ if (!(item is BedItem bedItem)) {
65
+ throw "Expected a bed item";
66
+ }
67
+ return bedItem.Name;
68
+ ```
69
+
59
70
  Use `SpriteInfo.Empty` when a required sprite should intentionally render no
60
71
  image. It is the canonical non-null empty sprite: it has no backing file and
61
72
  uses slice index zero.