@neocompose/cli 0.31.2 → 0.31.3

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/dist/neo.mjs CHANGED
@@ -1253,7 +1253,15 @@ function neoProjectPathHasIgnoredDirectory(path) {
1253
1253
  }
1254
1254
  function isNeoVariantFamilyTypeName(name) {
1255
1255
  if (name === NEO_VARIANT_TYPE_NAME) return true;
1256
- return name === NEO_VARIANT_FOLDER_TYPE_NAME;
1256
+ if (name === NEO_VARIANT_FOLDER_TYPE_NAME) return true;
1257
+ if (name === NEO_LOOKUP_VARIANT_TYPE_NAME) return true;
1258
+ return name === NEO_LOOKUP_VARIANT_FOLDER_TYPE_NAME;
1259
+ }
1260
+ function isNeoVariantTypeName(name) {
1261
+ return name === NEO_VARIANT_TYPE_NAME || name === NEO_LOOKUP_VARIANT_TYPE_NAME;
1262
+ }
1263
+ function isNeoVariantFolderTypeName(name) {
1264
+ return name === NEO_VARIANT_FOLDER_TYPE_NAME || name === NEO_LOOKUP_VARIANT_FOLDER_TYPE_NAME;
1257
1265
  }
1258
1266
  function isNeoScriptStructuredLeafTypeName(name) {
1259
1267
  return Object.prototype.hasOwnProperty.call(
@@ -1314,7 +1322,7 @@ function vectorTypes() {
1314
1322
  members: structuredLeafFieldProperties(name)
1315
1323
  }));
1316
1324
  }
1317
- var NEO_PROJECT_IGNORED_DIRECTORY_NAMES, NEOSCRIPT_KEYWORDS, NEOSCRIPT_INFERRED_LOCAL_KEYWORD, NEOSCRIPT_CONSTRUCTOR_KEYWORD, NEOSCRIPT_PRIMITIVE_TYPES, NEOSCRIPT_BUILTIN_TYPES, NEO_VARIANT_TYPE_NAME, NEO_VARIANT_FOLDER_TYPE_NAME, NEO_VARIANT_SCOPE_NAME, NEO_VARIANT_RESERVED_NAME, NEO_VARIANT_FOLDER_PATH_SEPARATOR, NEOSCRIPT_OPERATORS, NEOSCRIPT_STATEMENT_SNIPPETS, STRING_TYPE, INT_TYPE, FLOAT_TYPE, BOOL_TYPE, IMAGE_REF_TYPE, SPRITE_INFO_TYPE, FLOAT_OPTIONAL_TYPE, NEOSCRIPT_STRUCTURED_LEAF_FIELDS, NEOSCRIPT_IMAGE_REGISTRY_TYPE, NEOSCRIPT_AUDIO_CLIP_REGISTRY_TYPE, NEOSCRIPT_PENDING_SYMBOL_ID_PREFIX, REGISTRY_ENTRY_TYPES, NEOSCRIPT_GLOBALS, NEOSCRIPT_SPRITE_INFO_EMPTY_MEMBER_ID, NEOSCRIPT_SPRITE_INFO_EMPTY_VALUE, NEOSCRIPT_BUILTIN_TYPE_SYMBOLS;
1325
+ var NEO_PROJECT_IGNORED_DIRECTORY_NAMES, NEOSCRIPT_KEYWORDS, NEOSCRIPT_INFERRED_LOCAL_KEYWORD, NEOSCRIPT_CONSTRUCTOR_KEYWORD, NEOSCRIPT_PRIMITIVE_TYPES, NEOSCRIPT_BUILTIN_TYPES, NEO_VARIANT_TYPE_NAME, NEO_VARIANT_FOLDER_TYPE_NAME, NEO_LOOKUP_VARIANT_TYPE_NAME, NEO_LOOKUP_VARIANT_FOLDER_TYPE_NAME, NEO_VARIANT_SCOPE_NAME, NEO_VARIANT_RESERVED_NAME, NEO_VARIANT_FOLDER_PATH_SEPARATOR, NEOSCRIPT_OPERATORS, NEOSCRIPT_STATEMENT_SNIPPETS, STRING_TYPE, INT_TYPE, FLOAT_TYPE, BOOL_TYPE, IMAGE_REF_TYPE, SPRITE_INFO_TYPE, FLOAT_OPTIONAL_TYPE, NEOSCRIPT_STRUCTURED_LEAF_FIELDS, NEOSCRIPT_IMAGE_REGISTRY_TYPE, NEOSCRIPT_AUDIO_CLIP_REGISTRY_TYPE, NEOSCRIPT_PENDING_SYMBOL_ID_PREFIX, REGISTRY_ENTRY_TYPES, NEOSCRIPT_GLOBALS, NEOSCRIPT_SPRITE_INFO_EMPTY_MEMBER_ID, NEOSCRIPT_SPRITE_INFO_EMPTY_VALUE, NEOSCRIPT_BUILTIN_TYPE_SYMBOLS;
1318
1326
  var init_language_spec = __esm({
1319
1327
  "../packages/neoscript-language/src/language-spec.ts"() {
1320
1328
  "use strict";
@@ -1404,6 +1412,8 @@ var init_language_spec = __esm({
1404
1412
  ];
1405
1413
  NEO_VARIANT_TYPE_NAME = "NeoVariant";
1406
1414
  NEO_VARIANT_FOLDER_TYPE_NAME = "NeoVariantFolder";
1415
+ NEO_LOOKUP_VARIANT_TYPE_NAME = "NeoLookupVariant";
1416
+ NEO_LOOKUP_VARIANT_FOLDER_TYPE_NAME = "NeoLookupVariantFolder";
1407
1417
  NEO_VARIANT_SCOPE_NAME = "Variants";
1408
1418
  NEO_VARIANT_RESERVED_NAME = "Base";
1409
1419
  NEO_VARIANT_FOLDER_PATH_SEPARATOR = "/";
@@ -7928,7 +7938,7 @@ var NEOSCRIPT_COMPILER_REVISION;
7928
7938
  var init_strict_ir = __esm({
7929
7939
  "../packages/neoscript-language/src/strict-ir.ts"() {
7930
7940
  "use strict";
7931
- NEOSCRIPT_COMPILER_REVISION = 10;
7941
+ NEOSCRIPT_COMPILER_REVISION = 11;
7932
7942
  }
7933
7943
  });
7934
7944
 
@@ -11464,10 +11474,11 @@ var init_strict_resolver = __esm({
11464
11474
  * over {@link NeoScriptTypeRef} already handles a `named` type.
11465
11475
  */
11466
11476
  variantExpression(classType, variant, pos) {
11467
- const variantType = this.project.typeByName.get(NEO_VARIANT_TYPE_NAME);
11477
+ const typeName = variant?.valueTypeId == null ? NEO_VARIANT_TYPE_NAME : NEO_LOOKUP_VARIANT_TYPE_NAME;
11478
+ const variantType = this.project.typeByName.get(typeName);
11468
11479
  if (variantType === void 0) {
11469
11480
  throw new CompileError(
11470
- `The seeded '${NEO_VARIANT_TYPE_NAME}' class is not in this project's type table, so '${classType.name}.${NEO_VARIANT_SCOPE_NAME}' cannot be typed.`,
11481
+ `The seeded '${typeName}' class is not in this project's type table, so '${classType.name}.${NEO_VARIANT_SCOPE_NAME}' cannot be typed.`,
11471
11482
  pos
11472
11483
  );
11473
11484
  }
@@ -11475,24 +11486,31 @@ var init_strict_resolver = __esm({
11475
11486
  pointer: {
11476
11487
  type: "variant" /* Variant */,
11477
11488
  classId: classType.id,
11478
- variantId: variant === null ? null : variant.id
11489
+ variantId: variant === null ? null : variant.id,
11490
+ rowValueId: null
11479
11491
  },
11480
11492
  type: {
11481
11493
  kind: "named",
11482
11494
  typeId: variantType.id,
11483
- typeArguments: [{ kind: "named", typeId: classType.id }]
11495
+ typeArguments: [
11496
+ { kind: "named", typeId: classType.id },
11497
+ ...variant?.valueTypeId == null ? [] : [{ kind: "named", typeId: variant.valueTypeId }]
11498
+ ]
11484
11499
  }
11485
11500
  };
11486
11501
  }
11487
- /** The `TObject` of a `NeoVariant<TObject>` type, or null for anything else. */
11488
- variantTargetClassId(type) {
11502
+ variantTypeInfo(type) {
11489
11503
  if (type.kind !== "named") return null;
11490
11504
  const declared = this.project.typeById.get(type.typeId);
11491
11505
  if (declared === void 0) return null;
11492
- if (declared.name !== NEO_VARIANT_TYPE_NAME) return null;
11506
+ if (declared.name !== NEO_VARIANT_TYPE_NAME && declared.name !== NEO_LOOKUP_VARIANT_TYPE_NAME)
11507
+ return null;
11493
11508
  const argument2 = type.typeArguments?.[0];
11494
11509
  if (argument2 === void 0 || argument2.kind !== "named") return null;
11495
- return argument2.typeId;
11510
+ return {
11511
+ targetClassId: argument2.typeId,
11512
+ valueType: declared.name === NEO_LOOKUP_VARIANT_TYPE_NAME ? type.typeArguments?.[1] ?? null : null
11513
+ };
11496
11514
  }
11497
11515
  /**
11498
11516
  * P67 §4.1/§4.2 — `<variant>.Initialize()` and `<source>.ToVariant(<variant>)`.
@@ -11512,23 +11530,35 @@ var init_strict_resolver = __esm({
11512
11530
  }
11513
11531
  const receiver = this.resolveExpression(callee.receiver, scope);
11514
11532
  if (isInitialize) {
11515
- const targetClassId2 = this.variantTargetClassId(receiver.type);
11516
- if (targetClassId2 === null) return null;
11533
+ const variantInfo2 = this.variantTypeInfo(receiver.type);
11534
+ if (variantInfo2 === null) return null;
11517
11535
  if (callee.optional === true) {
11518
11536
  throw new CompileError(
11519
11537
  `\`?.\` is not valid on ${NEO_VARIANT_TYPE_NAME}.${VARIANT_INITIALIZE_NAME}; narrow the variant with a null check or force-unwrap it first.`,
11520
11538
  pos
11521
11539
  );
11522
11540
  }
11541
+ const expectedArgCount = variantInfo2.valueType === null ? 0 : 1;
11523
11542
  requireArgCount(
11524
- `${NEO_VARIANT_TYPE_NAME}.${VARIANT_INITIALIZE_NAME}`,
11543
+ `${variantInfo2.valueType === null ? NEO_VARIANT_TYPE_NAME : NEO_LOOKUP_VARIANT_TYPE_NAME}.${VARIANT_INITIALIZE_NAME}`,
11525
11544
  argumentsList2,
11526
- 0,
11545
+ expectedArgCount,
11527
11546
  pos
11528
11547
  );
11548
+ const row2 = variantInfo2.valueType === null ? null : this.resolveExpression(
11549
+ argumentsList2[0],
11550
+ scope,
11551
+ variantInfo2.valueType
11552
+ );
11553
+ if (row2 !== null && !isNeoScriptTypeAssignable(row2.type, variantInfo2.valueType, this.project)) {
11554
+ throw new CompileError(
11555
+ `Lookup variant row expected ${this.describe(variantInfo2.valueType)} but got ${this.describe(row2.type)}.`,
11556
+ pos
11557
+ );
11558
+ }
11529
11559
  const targetType2 = {
11530
11560
  kind: "named",
11531
- typeId: targetClassId2
11561
+ typeId: variantInfo2.targetClassId
11532
11562
  };
11533
11563
  return {
11534
11564
  pointer: {
@@ -11537,6 +11567,7 @@ var init_strict_resolver = __esm({
11537
11567
  type: "variantInitialize" /* VariantInitialize */,
11538
11568
  info: {
11539
11569
  variantPointer: receiver.pointer,
11570
+ ...row2 === null ? {} : { rowPointer: row2.pointer },
11540
11571
  schemaClassInfo: this.requireClassWireType(targetType2, pos)
11541
11572
  }
11542
11573
  }
@@ -11558,25 +11589,36 @@ var init_strict_resolver = __esm({
11558
11589
  pos
11559
11590
  );
11560
11591
  }
11592
+ const argumentAst = argumentsList2[0];
11593
+ if (argumentAst === void 0) return null;
11594
+ const argument2 = this.resolveExpression(argumentAst, scope);
11595
+ const variantInfo = this.variantTypeInfo(argument2.type);
11596
+ if (variantInfo === null) {
11597
+ throw new CompileError(
11598
+ `${receiverType.name}.${VARIANT_APPLY_NAME} takes a ${NEO_VARIANT_TYPE_NAME}<${receiverType.name}>, got ${this.describe(argument2.type)}.`,
11599
+ pos
11600
+ );
11601
+ }
11561
11602
  requireArgCount(
11562
11603
  `${receiverType.name}.${VARIANT_APPLY_NAME}`,
11563
11604
  argumentsList2,
11564
- 1,
11605
+ variantInfo.valueType === null ? 1 : 2,
11565
11606
  pos
11566
11607
  );
11567
- const argumentAst = argumentsList2[0];
11568
- if (argumentAst === void 0) return null;
11569
- const argument2 = this.resolveExpression(argumentAst, scope);
11570
- const targetClassId = this.variantTargetClassId(argument2.type);
11571
- if (targetClassId === null) {
11608
+ const row = variantInfo.valueType === null ? null : this.resolveExpression(
11609
+ argumentsList2[1],
11610
+ scope,
11611
+ variantInfo.valueType
11612
+ );
11613
+ if (row !== null && !isNeoScriptTypeAssignable(row.type, variantInfo.valueType, this.project)) {
11572
11614
  throw new CompileError(
11573
- `${receiverType.name}.${VARIANT_APPLY_NAME} takes a ${NEO_VARIANT_TYPE_NAME}<${receiverType.name}>, got ${this.describe(argument2.type)}.`,
11615
+ `Lookup variant row expected ${this.describe(variantInfo.valueType)} but got ${this.describe(row.type)}.`,
11574
11616
  pos
11575
11617
  );
11576
11618
  }
11577
11619
  const targetType = {
11578
11620
  kind: "named",
11579
- typeId: targetClassId
11621
+ typeId: variantInfo.targetClassId
11580
11622
  };
11581
11623
  if (!isNeoScriptTypeAssignable(receiver.type, targetType, this.project)) {
11582
11624
  throw new CompileError(
@@ -11592,6 +11634,7 @@ var init_strict_resolver = __esm({
11592
11634
  info: {
11593
11635
  receiverPointer: receiver.pointer,
11594
11636
  variantPointer: argument2.pointer,
11637
+ ...row === null ? {} : { rowPointer: row.pointer },
11595
11638
  schemaClassInfo: this.requireClassWireType(receiver.type, pos)
11596
11639
  }
11597
11640
  }
@@ -17705,6 +17748,7 @@ var init_project_schema_contract_generated = __esm({
17705
17748
  "collectionValueId",
17706
17749
  "declaredTypeInfo",
17707
17750
  "targetTypeInfo",
17751
+ "variantValueTypeInfo",
17708
17752
  "dialogueGroupId",
17709
17753
  "code",
17710
17754
  "setterCode",
@@ -17756,7 +17800,8 @@ var init_project_schema_contract_generated = __esm({
17756
17800
  "variant-folder": [
17757
17801
  "id",
17758
17802
  "classId",
17759
- "path"
17803
+ "path",
17804
+ "binding"
17760
17805
  ],
17761
17806
  "interface": [
17762
17807
  "id",
@@ -17967,7 +18012,9 @@ var init_project_schema_contract_generated = __esm({
17967
18012
  "AnimationSegmentTrack",
17968
18013
  "SpriteAnimationSegmentTrack",
17969
18014
  "Variant",
17970
- "VariantFolder"
18015
+ "VariantFolder",
18016
+ "LookupVariant",
18017
+ "LookupVariantFolder"
17971
18018
  ],
17972
18019
  "NeoTextureType": [
17973
18020
  "Default",
@@ -19832,7 +19879,15 @@ var init_project_source_construction_diagnostics = __esm({
19832
19879
 
19833
19880
  // ../packages/neoscript-language/src/project-source-variants.ts
19834
19881
  function neoVariantConstructionSiteCall(className, argumentNames, initializerNames) {
19835
- if (className !== NEO_VARIANT_TYPE_NAME) return null;
19882
+ if (className === NEO_LOOKUP_VARIANT_FOLDER_TYPE_NAME) {
19883
+ return {
19884
+ argumentNames: argumentNames.filter(
19885
+ (name) => name !== "collection" && name !== "collectionValue"
19886
+ ),
19887
+ initializerNames
19888
+ };
19889
+ }
19890
+ if (!isNeoVariantTypeName(className)) return null;
19836
19891
  const settled = [...initializerNames];
19837
19892
  const remaining = [];
19838
19893
  for (const name of argumentNames) {
@@ -19851,12 +19906,15 @@ function collectNeoVariantDeclarations(documents) {
19851
19906
  for (const declaration of document.declarations) {
19852
19907
  if (declaration.kind !== "global") continue;
19853
19908
  if (!isNeoVariantFamilyTypeName(declaration.type.name)) continue;
19854
- const family = declaration.type.name === NEO_VARIANT_TYPE_NAME ? "variant" : "folder";
19909
+ const family = isNeoVariantTypeName(declaration.type.name) ? "variant" : "folder";
19855
19910
  declarations.push({
19856
19911
  uri,
19857
19912
  declaration,
19858
19913
  family,
19914
+ lookup: declaration.type.name === NEO_LOOKUP_VARIANT_TYPE_NAME || declaration.type.name === NEO_LOOKUP_VARIANT_FOLDER_TYPE_NAME,
19859
19915
  owningClassName: declaration.type.typeArguments[0]?.name ?? null,
19916
+ valueClassName: declaration.type.typeArguments[1]?.name ?? null,
19917
+ collectionPath: family === "folder" && declaration.type.name === NEO_LOOKUP_VARIANT_FOLDER_TYPE_NAME ? folderCollectionPath(declaration) : null,
19860
19918
  folderSymbol: neoVariantFolderAnnotationSymbol(declaration.annotations),
19861
19919
  path: family === "folder" ? folderPath(declaration) : null
19862
19920
  });
@@ -19887,10 +19945,32 @@ function neoVariantFolderAnnotationSymbol(annotations) {
19887
19945
  function folderPath(declaration) {
19888
19946
  const expression = parseInitializer(declaration);
19889
19947
  if (expression === null || expression.kind !== "new") return null;
19890
- const argument2 = expression.args[0];
19948
+ const pathIndex = expression.argumentNames?.findIndex(
19949
+ (name) => name === "path"
19950
+ );
19951
+ const argument2 = expression.args[pathIndex !== void 0 && pathIndex >= 0 ? pathIndex : 0];
19891
19952
  if (argument2 === void 0 || argument2.kind !== "litString") return null;
19892
19953
  return argument2.value;
19893
19954
  }
19955
+ function folderCollectionPath(declaration) {
19956
+ const expression = parseInitializer(declaration);
19957
+ if (expression === null || expression.kind !== "new") return null;
19958
+ const index = expression.argumentNames?.findIndex(
19959
+ (name) => name === "collection"
19960
+ );
19961
+ if (index === void 0 || index < 0) return null;
19962
+ const argument2 = expression.args[index];
19963
+ if (argument2 === void 0) return null;
19964
+ const parts = [];
19965
+ let cursor = argument2;
19966
+ while (cursor.kind === "member") {
19967
+ parts.unshift(cursor.name);
19968
+ cursor = cursor.receiver;
19969
+ }
19970
+ if (cursor.kind !== "ident") return null;
19971
+ parts.unshift(cursor.name);
19972
+ return parts.join(".");
19973
+ }
19894
19974
  function parseInitializer(declaration) {
19895
19975
  try {
19896
19976
  return unwrapAnnotated(parseExpression(declaration.initializer.text));
@@ -19932,7 +20012,7 @@ function buildNeoVariantScopeIndex(documents) {
19932
20012
  const className = folder.owningClassName;
19933
20013
  if (folder.family !== "folder" || className === null) continue;
19934
20014
  if (folder.path === null) continue;
19935
- const segments = folder.path.split(NEO_VARIANT_FOLDER_PATH_SEPARATOR);
20015
+ const segments = folder.path.length === 0 ? [] : folder.path.split(NEO_VARIANT_FOLDER_PATH_SEPARATOR);
19936
20016
  segments.forEach((segment, index) => {
19937
20017
  const parent = dottedScope(
19938
20018
  className,
@@ -20077,8 +20157,29 @@ function validateVariantDeclarations(documents) {
20077
20157
  validateScopeName(entry, entry.declaration.name, diagnostics);
20078
20158
  }
20079
20159
  const folderPathsByClass = /* @__PURE__ */ new Map();
20160
+ const rootFolderByClass = /* @__PURE__ */ new Map();
20080
20161
  for (const folder of folders) {
20081
- validateFolderDeclaration(folder, folderPathsByClass, diagnostics);
20162
+ validateFolderDeclaration(
20163
+ folder,
20164
+ folderPathsByClass,
20165
+ classesByName,
20166
+ diagnostics
20167
+ );
20168
+ if (folder.path === "" && folder.owningClassName !== null) {
20169
+ const prior = rootFolderByClass.get(folder.owningClassName);
20170
+ if (prior !== void 0) {
20171
+ diagnostics.push(
20172
+ variantDiagnostic(
20173
+ folder,
20174
+ folder.declaration.initializer.range,
20175
+ "duplicate-root-lookup-variant-folder",
20176
+ `Class '${folder.owningClassName}' already has root lookup folder '${prior.declaration.name}'; at most one bound folder may enumerate the class root.`
20177
+ )
20178
+ );
20179
+ } else {
20180
+ rootFolderByClass.set(folder.owningClassName, folder);
20181
+ }
20182
+ }
20082
20183
  }
20083
20184
  const foldersBySymbol = /* @__PURE__ */ new Map();
20084
20185
  for (const folder of folders) {
@@ -20125,7 +20226,7 @@ function validateVariantDeclarations(documents) {
20125
20226
  for (const folder of folders) {
20126
20227
  const owningClassName2 = folder.owningClassName;
20127
20228
  if (owningClassName2 === null || folder.path === null) continue;
20128
- const segments = folder.path.split(NEO_VARIANT_FOLDER_PATH_SEPARATOR);
20229
+ const segments = folder.path.length === 0 ? [] : folder.path.split(NEO_VARIANT_FOLDER_PATH_SEPARATOR);
20129
20230
  segments.forEach((segment, index) => {
20130
20231
  claimFolderSegment(
20131
20232
  owningClassName2,
@@ -20146,12 +20247,49 @@ function validateVariantDeclarations(documents) {
20146
20247
  for (const variant of variants) {
20147
20248
  validateVariantInitializer(variant, diagnostics);
20148
20249
  if (variant.owningClassName === null) continue;
20250
+ if (variant.folderSymbol === null && rootFolderByClass.has(variant.owningClassName)) {
20251
+ diagnostics.push(
20252
+ variantDiagnostic(
20253
+ variant,
20254
+ variant.declaration.nameRange,
20255
+ "root-lookup-variant-folder-conflict",
20256
+ `Class '${variant.owningClassName}' has a root lookup folder, so variants at the class root must belong to that bound folder rather than coexist as plain root variants.`
20257
+ )
20258
+ );
20259
+ continue;
20260
+ }
20149
20261
  const scopePath = resolveVariantScopePath(
20150
20262
  variant,
20151
20263
  foldersBySymbol,
20152
20264
  diagnostics
20153
20265
  );
20154
20266
  if (scopePath === null) continue;
20267
+ const folder = variant.folderSymbol === null ? null : foldersBySymbol.get(
20268
+ `${variant.owningClassName} ${variant.folderSymbol}`
20269
+ ) ?? null;
20270
+ if ((folder?.lookup ?? false) !== variant.lookup) {
20271
+ const argument2 = variantFolderAnnotationArgument(variant);
20272
+ diagnostics.push(
20273
+ variantDiagnostic(
20274
+ variant,
20275
+ argument2?.range ?? variant.declaration.type.range,
20276
+ "variant-folder-family-mismatch",
20277
+ variant.lookup ? `NeoLookupVariant requires a NeoLookupVariantFolder with a collection binding.` : `NeoVariant cannot be placed in a NeoLookupVariantFolder; lookup variants receive the bound row parameter.`
20278
+ )
20279
+ );
20280
+ continue;
20281
+ }
20282
+ if (variant.lookup && folder !== null && variant.valueClassName !== folder.valueClassName) {
20283
+ diagnostics.push(
20284
+ variantDiagnostic(
20285
+ variant,
20286
+ variant.declaration.type.range,
20287
+ "lookup-variant-value-type-mismatch",
20288
+ `Lookup variant '${variant.declaration.name}' uses '${variant.valueClassName ?? "<missing>"}', but folder '${folder.declaration.name}' expects '${folder.valueClassName ?? "<missing>"}'.`
20289
+ )
20290
+ );
20291
+ continue;
20292
+ }
20155
20293
  claim(
20156
20294
  variant.owningClassName,
20157
20295
  scopePath,
@@ -20273,13 +20411,14 @@ function validateVariantInitializer(variant, diagnostics) {
20273
20411
  function validateInitialize(variant, argument2, diagnostics) {
20274
20412
  const lambda = unwrapAnnotated(argument2);
20275
20413
  if (lambda.kind !== "lambda") return;
20276
- if (lambda.params.length > 0) {
20414
+ const expected = variant.lookup ? 1 : 0;
20415
+ if (lambda.params.length !== expected) {
20277
20416
  diagnostics.push(
20278
20417
  variantDiagnostic(
20279
20418
  variant,
20280
20419
  variant.declaration.initializer.range,
20281
20420
  "variant-initialize-arity",
20282
- `A variant's initialize delegate takes no parameters and returns ${variant.owningClassName ?? "the target class"}; it was declared with ${lambda.params.length}.`
20421
+ variant.lookup ? `A lookup variant's initialize delegate takes exactly one ${variant.valueClassName ?? "row"} parameter and returns ${variant.owningClassName ?? "the target class"}; it was declared with ${lambda.params.length}.` : `A variant's initialize delegate takes no parameters and returns ${variant.owningClassName ?? "the target class"}; it was declared with ${lambda.params.length}.`
20283
20422
  )
20284
20423
  );
20285
20424
  }
@@ -20299,13 +20438,14 @@ function validateInitialize(variant, argument2, diagnostics) {
20299
20438
  function validateApply(variant, argument2, diagnostics) {
20300
20439
  const lambda = unwrapAnnotated(argument2);
20301
20440
  if (lambda.kind !== "lambda") return;
20302
- if (lambda.params.length !== 1) {
20441
+ const expected = variant.lookup ? 2 : 1;
20442
+ if (lambda.params.length !== expected) {
20303
20443
  diagnostics.push(
20304
20444
  variantDiagnostic(
20305
20445
  variant,
20306
20446
  variant.declaration.initializer.range,
20307
20447
  "variant-apply-arity",
20308
- `A variant's apply delegate takes exactly one ${variant.owningClassName ?? "target class"} parameter; it was declared with ${lambda.params.length}.`
20448
+ variant.lookup ? `A lookup variant's apply delegate takes exactly (${variant.owningClassName ?? "source"}, ${variant.valueClassName ?? "row"}); it was declared with ${lambda.params.length} parameters.` : `A variant's apply delegate takes exactly one ${variant.owningClassName ?? "target class"} parameter; it was declared with ${lambda.params.length}.`
20309
20449
  )
20310
20450
  );
20311
20451
  }
@@ -20322,7 +20462,7 @@ function validateApply(variant, argument2, diagnostics) {
20322
20462
  )
20323
20463
  );
20324
20464
  }
20325
- function validateFolderDeclaration(folder, folderPathsByClass, diagnostics) {
20465
+ function validateFolderDeclaration(folder, folderPathsByClass, classesByName, diagnostics) {
20326
20466
  if (folder.path === null) {
20327
20467
  diagnostics.push(
20328
20468
  variantDiagnostic(
@@ -20334,7 +20474,43 @@ function validateFolderDeclaration(folder, folderPathsByClass, diagnostics) {
20334
20474
  );
20335
20475
  return;
20336
20476
  }
20477
+ if (folder.path.length === 0 && !folder.lookup) {
20478
+ diagnostics.push(
20479
+ variantDiagnostic(
20480
+ folder,
20481
+ folder.declaration.initializer.range,
20482
+ "invalid-variant-folder-path",
20483
+ "Only a bound NeoLookupVariantFolder may use the class-root path ''."
20484
+ )
20485
+ );
20486
+ return;
20487
+ }
20488
+ if (folder.lookup) {
20489
+ const [className, memberName, ...rest] = folder.collectionPath?.split(".") ?? [];
20490
+ const collectionMember = className !== void 0 && memberName !== void 0 && rest.length === 0 ? classesByName.get(className)?.members.find((member) => member.name === memberName) : void 0;
20491
+ const entryType = collectionMember?.type.name === "List" ? collectionMember.type.typeArguments[0]?.name : void 0;
20492
+ if (entryType === void 0) {
20493
+ diagnostics.push(
20494
+ variantDiagnostic(
20495
+ folder,
20496
+ folder.declaration.initializer.range,
20497
+ "invalid-lookup-variant-folder-collection",
20498
+ `Lookup variant folder '${folder.declaration.name}' requires collection: Class.ListMember whose entries are Class values.`
20499
+ )
20500
+ );
20501
+ } else if (entryType !== folder.valueClassName) {
20502
+ diagnostics.push(
20503
+ variantDiagnostic(
20504
+ folder,
20505
+ folder.declaration.type.range,
20506
+ "lookup-folder-value-type-mismatch",
20507
+ `Lookup variant folder '${folder.declaration.name}' declares TValue '${folder.valueClassName ?? "<missing>"}', but collection '${folder.collectionPath}' contains '${entryType}'.`
20508
+ )
20509
+ );
20510
+ }
20511
+ }
20337
20512
  for (const segment of folder.path.split(NEO_VARIANT_FOLDER_PATH_SEPARATOR)) {
20513
+ if (segment.length === 0) continue;
20338
20514
  validateScopeName(folder, segment, diagnostics);
20339
20515
  }
20340
20516
  if (folder.owningClassName === null) return;
@@ -20386,7 +20562,7 @@ function validateOwningClass(entry, classesByName, diagnostics) {
20386
20562
  entry,
20387
20563
  entry.declaration.type.range,
20388
20564
  "variant-target-missing",
20389
- `${entry.declaration.type.name} names the class it belongs to as its single type argument.`
20565
+ `${entry.declaration.type.name} names the class it belongs to as its first type argument.`
20390
20566
  )
20391
20567
  );
20392
20568
  return;
@@ -20459,7 +20635,7 @@ function validateVariantTypePositions(documents, diagnostics) {
20459
20635
  }
20460
20636
  }
20461
20637
  function reportVariantTypePositions(uri, type, diagnostics, root = "nested") {
20462
- if (type.name === NEO_VARIANT_TYPE_NAME && root === "reference") {
20638
+ if (isNeoVariantTypeName(type.name) && root === "reference") {
20463
20639
  for (const argument2 of type.typeArguments) {
20464
20640
  reportVariantTypePositions(uri, argument2, diagnostics);
20465
20641
  }
@@ -20481,7 +20657,7 @@ function reportVariantTypePositions(uri, type, diagnostics, root = "nested") {
20481
20657
  }
20482
20658
  }
20483
20659
  function variantTypePositionMessage(typeName, root) {
20484
- if (typeName === NEO_VARIANT_FOLDER_TYPE_NAME) {
20660
+ if (isNeoVariantFolderTypeName(typeName)) {
20485
20661
  return `'${typeName}' is reference-only: it declares variant folders as top-level globals and is never a member, entry, parameter, or generic binding type.`;
20486
20662
  }
20487
20663
  return `'${typeName}' cannot be nested inside another type. A variant reference is a member, parameter, or return type in its own right (P67 \xA76); wrapping it in a collection, lookup, or 'Reference' asks for an instance of a class that has none.`;
@@ -21178,7 +21354,7 @@ function validateProjectSourceSemantics(documents) {
21178
21354
  }
21179
21355
  function annotationContextForDeclaration(declaration) {
21180
21356
  if (declaration.kind === "class") return { kind: "class", declaration };
21181
- if (declaration.kind === "global" && declaration.type.name === NEO_VARIANT_TYPE_NAME) {
21357
+ if (declaration.kind === "global" && isNeoVariantTypeName(declaration.type.name)) {
21182
21358
  return { kind: "variantGlobal", declaration };
21183
21359
  }
21184
21360
  return { kind: "schema" };
@@ -21843,10 +22019,11 @@ function fieldsForAnnotation(name, context, environment) {
21843
22019
  }
21844
22020
  function settingsFields(context) {
21845
22021
  if (context.kind === "variantGlobal") {
22022
+ const lookup = context.declaration.type.name !== NEO_VARIANT_TYPE_NAME;
21846
22023
  return [
21847
22024
  {
21848
22025
  name: NEO_VARIANT_FOLDER_SETTING,
21849
- type: `${NEO_VARIANT_FOLDER_TYPE_NAME}<${context.declaration.type.typeArguments[0]?.name ?? "TObject"}>`
22026
+ type: lookup ? `${NEO_LOOKUP_VARIANT_FOLDER_TYPE_NAME}<${context.declaration.type.typeArguments[0]?.name ?? "TObject"}, ${context.declaration.type.typeArguments[1]?.name ?? "TValue"}>` : `${NEO_VARIANT_FOLDER_TYPE_NAME}<${context.declaration.type.typeArguments[0]?.name ?? "TObject"}>`
21850
22027
  }
21851
22028
  ];
21852
22029
  }
@@ -22347,7 +22524,7 @@ function validateStatements(statements, returnType, scope, environment, uri, ran
22347
22524
  }
22348
22525
  }
22349
22526
  function validateExpression(expression, expected, scope, environment, uri, range2, diagnostics, anchor) {
22350
- if (expected?.name === NEO_VARIANT_TYPE_NAME) {
22527
+ if (expected !== void 0 && isNeoVariantTypeName(expected.name)) {
22351
22528
  validateVariantValuedExpression(
22352
22529
  expression,
22353
22530
  expected,
@@ -22565,6 +22742,9 @@ function validateExpression(expression, expected, scope, environment, uri, range
22565
22742
  const overload = declaration && declaration.constructors.length > 0 ? matchingDeclaredConstructor(declaration, expression.argumentNames) : null;
22566
22743
  expression.args.forEach((argument2, index) => {
22567
22744
  const named = expression.argumentNames?.[index];
22745
+ if (declaration.name === NEO_LOOKUP_VARIANT_FOLDER_TYPE_NAME && named === "collection") {
22746
+ return;
22747
+ }
22568
22748
  const parameter4 = named ? (declaration.headerParameters ?? []).find(
22569
22749
  (candidate) => candidate.name === named
22570
22750
  ) ?? overload?.parameters.find((candidate) => candidate.name === named) : void 0;
@@ -22573,9 +22753,10 @@ function validateExpression(expression, expected, scope, environment, uri, range
22573
22753
  (candidate) => candidate.name === (variantMember ?? named)
22574
22754
  ) : fields[index];
22575
22755
  const expectedType = parameter4?.type ?? field?.type;
22756
+ const expectedSemanticType = declaration.name === NEO_LOOKUP_VARIANT_FOLDER_TYPE_NAME && named === "collectionValue" ? { name: "string" } : expectedType ? semanticType(expectedType) : void 0;
22576
22757
  validateExpression(
22577
22758
  argument2,
22578
- expectedType ? semanticType(expectedType) : void 0,
22759
+ expectedSemanticType,
22579
22760
  scope,
22580
22761
  environment,
22581
22762
  uri,
@@ -27537,10 +27718,18 @@ function projectConstructorArgumentCompletions(analysis, document, position) {
27537
27718
  used.add(token.text);
27538
27719
  }
27539
27720
  }
27540
- const fields = graphConstructorNamedArguments(typeName) ?? declaredConstructorNamedArguments(analysis, typeName, used);
27721
+ const fields = lookupVariantFolderArgumentFields(typeName) ?? graphConstructorNamedArguments(typeName) ?? declaredConstructorNamedArguments(analysis, typeName, used);
27541
27722
  if (!fields) return null;
27542
27723
  return fields.filter((field) => !used.has(field.name));
27543
27724
  }
27725
+ function lookupVariantFolderArgumentFields(typeName) {
27726
+ if (typeName !== NEO_LOOKUP_VARIANT_FOLDER_TYPE_NAME) return null;
27727
+ return [
27728
+ { name: "path", type: "string" },
27729
+ { name: "collection", type: "List<TValue>" },
27730
+ { name: "collectionValue", type: "Reference<List<TValue>>" }
27731
+ ];
27732
+ }
27544
27733
  function declaredConstructorNamedArguments(analysis, typeName, used) {
27545
27734
  const contract = classConstructionContract(
27546
27735
  projectConstructionIndex(analysis),
@@ -27673,7 +27862,7 @@ function projectDottedPathBefore(document, position) {
27673
27862
  }
27674
27863
  function projectVariantFolderCompletions(analysis, source, position) {
27675
27864
  const declaration = source.declarations.find(
27676
- (candidate) => candidate.kind === "global" && candidate.type.name === NEO_VARIANT_TYPE_NAME && candidate.annotations.some(
27865
+ (candidate) => candidate.kind === "global" && isNeoVariantTypeName(candidate.type.name) && candidate.annotations.some(
27677
27866
  (annotation2) => rangeContains(annotation2.range, position)
27678
27867
  )
27679
27868
  );
@@ -28836,6 +29025,8 @@ function projectConstructorTypeName(analysis, document, callee, beforeCallee) {
28836
29025
  return contextualConstructorType(analysis, document, callee.range.start);
28837
29026
  }
28838
29027
  function projectConstructorParameters(analysis, typeName) {
29028
+ const lookup = lookupVariantFolderArgumentFields(typeName);
29029
+ if (lookup !== null) return lookup;
28839
29030
  const declaration = findTypeDeclaration(analysis, typeName);
28840
29031
  return declaration ? declarationConstructorParameters(declaration) : graphConstructorParameters(typeName);
28841
29032
  }
@@ -32970,6 +33161,7 @@ var init_document_contracts = __esm({
32970
33161
  "collectionValueId",
32971
33162
  "declaredTypeInfo",
32972
33163
  "targetTypeInfo",
33164
+ "variantValueTypeInfo",
32973
33165
  "dialogueGroupId",
32974
33166
  "code",
32975
33167
  "setterCode",
@@ -33085,10 +33277,12 @@ var init_document_contracts = __esm({
33085
33277
  }
33086
33278
  },
33087
33279
  "variant-folder": {
33088
- authored: ["id", "classId", "path"],
33280
+ authored: ["id", "classId", "path", "binding"],
33089
33281
  derived: [],
33090
33282
  volatile: ["projectId", "createdAt", "updatedAt"],
33091
- normalization: {}
33283
+ normalization: {
33284
+ binding: "missingIsNull"
33285
+ }
33092
33286
  },
33093
33287
  interface: {
33094
33288
  authored: [
@@ -34425,6 +34619,11 @@ function memberFromDocument(record3, members, owners, classNames, context) {
34425
34619
  field("targetTypeInfo", void 0),
34426
34620
  record3,
34427
34621
  "targetTypeInfo"
34622
+ ),
34623
+ valueType: field("variantValueTypeInfo", null) === null ? null : documentTypeToManifest(
34624
+ field("variantValueTypeInfo", void 0),
34625
+ record3,
34626
+ "variantValueTypeInfo"
34428
34627
  )
34429
34628
  };
34430
34629
  }
@@ -34572,6 +34771,7 @@ function memberToDocumentFields(member, baseData3) {
34572
34771
  }
34573
34772
  if (member.kind === "variant") {
34574
34773
  fields.targetTypeInfo = manifestTypeToDocument(member.targetType);
34774
+ fields.variantValueTypeInfo = member.valueType === null ? null : manifestTypeToDocument(member.valueType);
34575
34775
  }
34576
34776
  if (member.kind === "generic") {
34577
34777
  fields.genericParamId = member.genericParamId;
@@ -35109,7 +35309,9 @@ function isSchemaSystemMetadata(value) {
35109
35309
  "animationSegmentTrack",
35110
35310
  "spriteAnimationSegmentTrack",
35111
35311
  "variant",
35112
- "variantFolder"
35312
+ "variantFolder",
35313
+ "lookupVariant",
35314
+ "lookupVariantFolder"
35113
35315
  ]);
35114
35316
  return typeof value.kind === "string" && kinds.has(value.kind) && (value.worldKind === null || typeof value.worldKind === "string" && worlds.has(value.worldKind));
35115
35317
  }
@@ -36382,6 +36584,7 @@ function variantFromDocument(record3, context) {
36382
36584
  }
36383
36585
  function variantFolderFromDocument(record3, context) {
36384
36586
  const data = normalizeDocumentFields("variant-folder", record3.data);
36587
+ const binding = variantFolderBinding(data.binding, record3);
36385
36588
  return {
36386
36589
  id: record3.recordId,
36387
36590
  source: context.sourceForRecord(
@@ -36390,9 +36593,21 @@ function variantFolderFromDocument(record3, context) {
36390
36593
  "variantFolder"
36391
36594
  ),
36392
36595
  classId: requiredString(data, "classId", record3),
36393
- path: requiredString(data, "path", record3)
36596
+ path: variantFolderPath(data.path, binding, record3),
36597
+ binding
36394
36598
  };
36395
36599
  }
36600
+ function variantFolderPath(value, binding, record3) {
36601
+ if (typeof value !== "string") {
36602
+ throw new Error(`Variant folder ${record3.recordId} has a non-string path.`);
36603
+ }
36604
+ if (value.length === 0 && binding === null) {
36605
+ throw new Error(
36606
+ `Variant folder ${record3.recordId} may use the class-root path only with a collection binding.`
36607
+ );
36608
+ }
36609
+ return value;
36610
+ }
36396
36611
  function variantFolderId(value, record3) {
36397
36612
  if (value === null) return null;
36398
36613
  if (typeof value !== "string") {
@@ -36420,9 +36635,27 @@ function variantFolderToDocument(folder) {
36420
36635
  return omitUndefined({
36421
36636
  id: folder.id,
36422
36637
  classId: folder.classId,
36423
- path: folder.path
36638
+ path: folder.path,
36639
+ binding: folder.binding
36424
36640
  });
36425
36641
  }
36642
+ function variantFolderBinding(value, record3) {
36643
+ if (value === null || value === void 0) return null;
36644
+ if (typeof value !== "object" || Array.isArray(value)) {
36645
+ throw new Error(
36646
+ `Variant folder ${record3.recordId} has an invalid binding.`
36647
+ );
36648
+ }
36649
+ const binding = value;
36650
+ const collectionMemberId = binding.collectionMemberId;
36651
+ const collectionValueId = binding.collectionValueId;
36652
+ if (typeof collectionMemberId !== "string" || collectionMemberId.length === 0 || typeof collectionValueId !== "string" || collectionValueId.length === 0) {
36653
+ throw new Error(
36654
+ `Variant folder ${record3.recordId} binding requires collectionMemberId and collectionValueId.`
36655
+ );
36656
+ }
36657
+ return { collectionMemberId, collectionValueId };
36658
+ }
36426
36659
  var VARIANT_SCHEMA_RECORD_ADAPTER, VARIANT_FOLDER_SCHEMA_RECORD_ADAPTER;
36427
36660
  var init_variant = __esm({
36428
36661
  "src/project-manifest/record-adapters/variant.ts"() {
@@ -36761,10 +36994,33 @@ function assertVariant(value, path) {
36761
36994
  nonEmptyString(variant.folderId, `${path}.folderId`);
36762
36995
  }
36763
36996
  function assertVariantFolder(value, path) {
36764
- const folder = objectAt(value, path, ["id", "source", "classId", "path"]);
36997
+ const folder = objectAt(value, path, [
36998
+ "id",
36999
+ "source",
37000
+ "classId",
37001
+ "path",
37002
+ "binding"
37003
+ ]);
36765
37004
  assertIdentity2(folder, path, "variantFolder");
36766
37005
  nonEmptyString(folder.classId, `${path}.classId`);
36767
- nonEmptyString(folder.path, `${path}.path`);
37006
+ if (typeof folder.path !== "string")
37007
+ invalid(`${path}.path`, "must be a string.");
37008
+ if (folder.binding === null) {
37009
+ nonEmptyString(folder.path, `${path}.path`);
37010
+ return;
37011
+ }
37012
+ const binding = objectAt(folder.binding, `${path}.binding`, [
37013
+ "collectionMemberId",
37014
+ "collectionValueId"
37015
+ ]);
37016
+ nonEmptyString(
37017
+ binding.collectionMemberId,
37018
+ `${path}.binding.collectionMemberId`
37019
+ );
37020
+ nonEmptyString(
37021
+ binding.collectionValueId,
37022
+ `${path}.binding.collectionValueId`
37023
+ );
36768
37024
  }
36769
37025
  function assertConstructorBaseArgument(value, path) {
36770
37026
  const argument2 = objectAt(value, path, ["name", "code"]);
@@ -37702,6 +37958,19 @@ function assertMember2(value, path) {
37702
37958
  `a variant member targets a class or a generic parameter, not ${JSON.stringify(targetKind)}.`
37703
37959
  );
37704
37960
  }
37961
+ if (member.valueType !== null) {
37962
+ assertType2(member.valueType, `${path}.valueType`);
37963
+ const valueKind = requireRecord(
37964
+ member.valueType,
37965
+ `${path}.valueType`
37966
+ ).kind;
37967
+ if (valueKind !== "class") {
37968
+ invalid(
37969
+ `${path}.valueType.kind`,
37970
+ "a lookup variant member's TValue must be a collection entry class."
37971
+ );
37972
+ }
37973
+ }
37705
37974
  return;
37706
37975
  }
37707
37976
  if (kind === "generic") {
@@ -38903,7 +39172,9 @@ var init_validate = __esm({
38903
39172
  "animationSegmentTrack",
38904
39173
  "spriteAnimationSegmentTrack",
38905
39174
  "variant",
38906
- "variantFolder"
39175
+ "variantFolder",
39176
+ "lookupVariant",
39177
+ "lookupVariantFolder"
38907
39178
  ]);
38908
39179
  MEMBER_COMMON_KEYS = [
38909
39180
  "id",
@@ -38959,7 +39230,7 @@ var init_validate = __esm({
38959
39230
  action: ["arguments"],
38960
39231
  // P67 §6: a variant member carries only its declared target; the selection
38961
39232
  // itself is an ordinary `defaultValue`.
38962
- variant: ["targetType"]
39233
+ variant: ["targetType", "valueType"]
38963
39234
  };
38964
39235
  PRIMITIVE_TYPE_KINDS = /* @__PURE__ */ new Set([
38965
39236
  "null",
@@ -39705,7 +39976,9 @@ var init_world_system_kinds_generated = __esm({
39705
39976
  AnimationSegmentTrack: "animationSegmentTrack",
39706
39977
  SpriteAnimationSegmentTrack: "spriteAnimationSegmentTrack",
39707
39978
  Variant: "variant",
39708
- VariantFolder: "variantFolder"
39979
+ VariantFolder: "variantFolder",
39980
+ LookupVariant: "lookupVariant",
39981
+ LookupVariantFolder: "lookupVariantFolder"
39709
39982
  };
39710
39983
  NEO_WORLD_SYSTEM_CLASS_KIND_VALUES = new Set(
39711
39984
  Object.values(NeoWorldSystemClassKind)
@@ -40462,6 +40735,8 @@ function isNSFunctionVariantInitialize(value) {
40462
40735
  const info = v.info;
40463
40736
  if (typeof info !== "object" || info === null) return false;
40464
40737
  if (!isNSPointer(info.variantPointer)) return false;
40738
+ if (info.rowPointer !== void 0 && !isNSPointer(info.rowPointer))
40739
+ return false;
40465
40740
  return isNSTypeInfoClass(info.schemaClassInfo) && info.schemaClassInfo.required;
40466
40741
  }
40467
40742
  function isNSFunctionVariantApply(value) {
@@ -40471,6 +40746,8 @@ function isNSFunctionVariantApply(value) {
40471
40746
  if (typeof info !== "object" || info === null) return false;
40472
40747
  if (!isNSPointer(info.receiverPointer)) return false;
40473
40748
  if (!isNSPointer(info.variantPointer)) return false;
40749
+ if (info.rowPointer !== void 0 && !isNSPointer(info.rowPointer))
40750
+ return false;
40474
40751
  return isNSTypeInfoClass(info.schemaClassInfo) && info.schemaClassInfo.required;
40475
40752
  }
40476
40753
  function isNSFunctionClassConstructor(value) {
@@ -41619,6 +41896,9 @@ function isMemberListBase(value) {
41619
41896
  if (v.listKind !== void 0 && v.listKind !== null && !isListKind(v.listKind)) {
41620
41897
  return false;
41621
41898
  }
41899
+ if (v.valueTypeInfo !== void 0 && v.valueTypeInfo !== null && (!isNSTypeInfo(v.valueTypeInfo) || v.valueTypeInfo.type !== 7 /* Class */)) {
41900
+ return false;
41901
+ }
41622
41902
  if (v.indexes !== void 0 && v.indexes !== null) {
41623
41903
  if (!Array.isArray(v.indexes)) return false;
41624
41904
  const seenIndexKeys = /* @__PURE__ */ new Set();
@@ -42018,10 +42298,14 @@ function isMemberFunctionRefBase(value) {
42018
42298
  function isVariantRefValue(value) {
42019
42299
  if (!value || typeof value !== "object" || Array.isArray(value)) return false;
42020
42300
  const record3 = value;
42021
- if (!hasExactKeys(record3, ["classId", "variantId"])) return false;
42301
+ const keys = Object.hasOwn(record3, "rowValueId") ? ["classId", "variantId", "rowValueId"] : ["classId", "variantId"];
42302
+ if (!hasExactKeys(record3, keys)) return false;
42022
42303
  if (!isString(record3.classId)) return false;
42023
42304
  if (record3.classId.length === 0) return false;
42024
- if (record3.variantId === null) return true;
42305
+ if (record3.rowValueId !== void 0 && record3.rowValueId !== null && (!isString(record3.rowValueId) || record3.rowValueId.length === 0)) {
42306
+ return false;
42307
+ }
42308
+ if (record3.variantId === null) return record3.rowValueId == null;
42025
42309
  if (!isString(record3.variantId)) return false;
42026
42310
  return record3.variantId.length > 0;
42027
42311
  }
@@ -44031,6 +44315,222 @@ var init_world_system_classes_generated = __esm({
44031
44315
  }
44032
44316
  ],
44033
44317
  worldKind: "variantFolder"
44318
+ },
44319
+ {
44320
+ classId: "system_38ddb113-5df8-4385-9324-1cecf7f3bd99",
44321
+ name: "NeoLookupVariant",
44322
+ docsText: "A NeoObject configuration parameterized by a row from a bound collection.",
44323
+ isAbstract: false,
44324
+ isSealed: true,
44325
+ hiddenInMemberSelector: true,
44326
+ allowedStorage: "immutable" /* Immutable */,
44327
+ requiredConstructorId: "system_d29a7faa-7196-52ab-ac8f-0f4d6230cc0e",
44328
+ constructors: [
44329
+ {
44330
+ constructorId: "system_d29a7faa-7196-52ab-ac8f-0f4d6230cc0e",
44331
+ argumentTypes: [
44332
+ {
44333
+ name: "initialize",
44334
+ type: 25,
44335
+ required: true,
44336
+ returnTypeInfo: {
44337
+ type: 21,
44338
+ required: true,
44339
+ ownerClassId: "system_38ddb113-5df8-4385-9324-1cecf7f3bd99",
44340
+ genericParamId: "system_1d416b31-fa1a-4bbe-a716-249eb855712d"
44341
+ },
44342
+ argumentTypes: [
44343
+ {
44344
+ type: 21,
44345
+ required: true,
44346
+ ownerClassId: "system_38ddb113-5df8-4385-9324-1cecf7f3bd99",
44347
+ genericParamId: "system_4031d9da-38a7-4332-892e-b3fd8282378a"
44348
+ }
44349
+ ]
44350
+ },
44351
+ {
44352
+ name: "apply",
44353
+ type: 25,
44354
+ required: false,
44355
+ returnTypeInfo: { type: "Void", required: true },
44356
+ argumentTypes: [
44357
+ {
44358
+ type: 21,
44359
+ required: true,
44360
+ ownerClassId: "system_38ddb113-5df8-4385-9324-1cecf7f3bd99",
44361
+ genericParamId: "system_1d416b31-fa1a-4bbe-a716-249eb855712d"
44362
+ },
44363
+ {
44364
+ type: 21,
44365
+ required: true,
44366
+ ownerClassId: "system_38ddb113-5df8-4385-9324-1cecf7f3bd99",
44367
+ genericParamId: "system_4031d9da-38a7-4332-892e-b3fd8282378a"
44368
+ }
44369
+ ],
44370
+ defaultValue: { value: null }
44371
+ },
44372
+ {
44373
+ name: "overrides",
44374
+ type: 21,
44375
+ required: false,
44376
+ ownerClassId: "system_38ddb113-5df8-4385-9324-1cecf7f3bd99",
44377
+ genericParamId: "system_1d416b31-fa1a-4bbe-a716-249eb855712d",
44378
+ defaultValue: { value: null }
44379
+ },
44380
+ {
44381
+ name: "childOverrides",
44382
+ type: 6,
44383
+ required: false,
44384
+ entryTypeInfo: {
44385
+ type: 7,
44386
+ required: true,
44387
+ classId: "system_e2e88eba-5335-4a16-9dcf-2c0e1951e8bd",
44388
+ typeArguments: {
44389
+ "system_de23448a-45be-4e3f-8965-072a545e08f0": {
44390
+ type: 7,
44391
+ required: true,
44392
+ classId: "system_61b30a92-90dc-4bf8-8503-ee4f6414effc"
44393
+ }
44394
+ }
44395
+ },
44396
+ defaultValue: { value: null }
44397
+ }
44398
+ ],
44399
+ code: null
44400
+ }
44401
+ ],
44402
+ genericParams: [
44403
+ {
44404
+ id: "system_1d416b31-fa1a-4bbe-a716-249eb855712d",
44405
+ name: "TObject",
44406
+ constraintClassWorldKind: "object"
44407
+ },
44408
+ { id: "system_4031d9da-38a7-4332-892e-b3fd8282378a", name: "TValue" }
44409
+ ],
44410
+ schemaFields: [
44411
+ {
44412
+ memberId: "system_ddff93cb-2e4e-43ed-ae1a-b523eb53e0e9",
44413
+ memberKind: "delegate",
44414
+ defaultInitializerCode: "initialize",
44415
+ returnTypeInfo: {
44416
+ type: 21,
44417
+ required: true,
44418
+ ownerClassId: "system_38ddb113-5df8-4385-9324-1cecf7f3bd99",
44419
+ genericParamId: "system_1d416b31-fa1a-4bbe-a716-249eb855712d"
44420
+ },
44421
+ argumentTypes: [
44422
+ {
44423
+ type: 21,
44424
+ required: true,
44425
+ ownerClassId: "system_38ddb113-5df8-4385-9324-1cecf7f3bd99",
44426
+ genericParamId: "system_4031d9da-38a7-4332-892e-b3fd8282378a",
44427
+ name: "p1"
44428
+ }
44429
+ ],
44430
+ required: true,
44431
+ schemaKey: "Initialize"
44432
+ },
44433
+ {
44434
+ memberId: "system_42da97f6-c714-47b6-b750-232232ec4f83",
44435
+ memberKind: "delegate",
44436
+ defaultInitializerCode: "apply",
44437
+ returnTypeInfo: { type: "Void", required: true },
44438
+ argumentTypes: [
44439
+ {
44440
+ type: 21,
44441
+ required: true,
44442
+ ownerClassId: "system_38ddb113-5df8-4385-9324-1cecf7f3bd99",
44443
+ genericParamId: "system_1d416b31-fa1a-4bbe-a716-249eb855712d",
44444
+ name: "p1"
44445
+ },
44446
+ {
44447
+ type: 21,
44448
+ required: true,
44449
+ ownerClassId: "system_38ddb113-5df8-4385-9324-1cecf7f3bd99",
44450
+ genericParamId: "system_4031d9da-38a7-4332-892e-b3fd8282378a",
44451
+ name: "p2"
44452
+ }
44453
+ ],
44454
+ required: false,
44455
+ schemaKey: "Apply"
44456
+ },
44457
+ {
44458
+ memberId: "system_070ae22a-7603-48cf-94e2-528d88b72825",
44459
+ memberKind: "generic",
44460
+ defaultInitializerCode: "overrides",
44461
+ genericParamId: "system_1d416b31-fa1a-4bbe-a716-249eb855712d",
44462
+ partial: true,
44463
+ required: false,
44464
+ schemaKey: "Overrides"
44465
+ },
44466
+ {
44467
+ memberId: "system_ae23d675-2531-44a2-b792-8a121d99aec2",
44468
+ memberKind: "list",
44469
+ defaultInitializerCode: "childOverrides",
44470
+ entryMemberId: "system_d922e73b-d2d8-5b2e-b333-75b80ca9c3ab",
44471
+ required: false,
44472
+ schemaKey: "ChildOverrides"
44473
+ }
44474
+ ],
44475
+ supportingFields: [
44476
+ {
44477
+ memberId: "system_d922e73b-d2d8-5b2e-b333-75b80ca9c3ab",
44478
+ memberKind: "class",
44479
+ schemaClassWorldKind: "animationChildOverride",
44480
+ classArguments: {
44481
+ "system_de23448a-45be-4e3f-8965-072a545e08f0": {
44482
+ kind: "member",
44483
+ memberId: "system_788215c7-3dcf-5df0-8810-79f08da02c66"
44484
+ }
44485
+ },
44486
+ required: true,
44487
+ schemaKey: "__ChildOverrideEntry"
44488
+ },
44489
+ {
44490
+ memberId: "system_788215c7-3dcf-5df0-8810-79f08da02c66",
44491
+ memberKind: "class",
44492
+ schemaClassWorldKind: "objectBase",
44493
+ required: true,
44494
+ schemaKey: "__NeoObjectBaseTypeArgument"
44495
+ }
44496
+ ],
44497
+ worldKind: "lookupVariant"
44498
+ },
44499
+ {
44500
+ classId: "system_b3bd4d91-6e03-4034-b201-05afd78f7c72",
44501
+ name: "NeoLookupVariantFolder",
44502
+ docsText: "A variant folder whose variants receive a row from a bound collection.",
44503
+ isAbstract: false,
44504
+ isSealed: true,
44505
+ hiddenInMemberSelector: true,
44506
+ allowedStorage: "immutable" /* Immutable */,
44507
+ requiredConstructorId: "system_b0239c15-91f3-5ddb-bd4e-43fd86e42e50",
44508
+ constructors: [
44509
+ {
44510
+ constructorId: "system_b0239c15-91f3-5ddb-bd4e-43fd86e42e50",
44511
+ argumentTypes: [{ name: "path", type: 3, required: true }],
44512
+ code: null
44513
+ }
44514
+ ],
44515
+ genericParams: [
44516
+ {
44517
+ id: "system_a5488f2b-966e-4643-93df-71995c9c24ad",
44518
+ name: "TObject",
44519
+ constraintClassWorldKind: "object"
44520
+ },
44521
+ { id: "system_39544f5d-e527-44fb-96b7-f584b7cd1a25", name: "TValue" }
44522
+ ],
44523
+ schemaFields: [
44524
+ {
44525
+ memberId: "system_d36ebea5-151f-4d9d-95f2-28d8cfb05870",
44526
+ memberKind: "string",
44527
+ defaultInitializerCode: "path",
44528
+ localizable: false,
44529
+ required: true,
44530
+ schemaKey: "Path"
44531
+ }
44532
+ ],
44533
+ worldKind: "lookupVariantFolder"
44034
44534
  }
44035
44535
  ];
44036
44536
  }
@@ -45161,6 +45661,28 @@ var init_member_kinds_db_response = __esm({
45161
45661
  });
45162
45662
 
45163
45663
  // ../src/models/members/unordered-list-membership.ts
45664
+ function unorderedListEntryIds(values, listValue2, membershipIndex) {
45665
+ if (!Array.isArray(listValue2.value)) return [];
45666
+ if (membershipIndex !== void 0) {
45667
+ return [...membershipIndex.get(listValue2.id) ?? []];
45668
+ }
45669
+ const ids = [];
45670
+ for (const candidate of values) {
45671
+ if (candidate.containerId === listValue2.id) ids.push(candidate.id);
45672
+ }
45673
+ ids.sort();
45674
+ return ids;
45675
+ }
45676
+ function listEntryIdsForValue(member, listValue2, values) {
45677
+ if (listValue2.value === null) return [];
45678
+ if (listKindOf(member) === "unordered") {
45679
+ return unorderedListEntryIds(values, listValue2);
45680
+ }
45681
+ if (!Array.isArray(listValue2.value)) return [];
45682
+ return listValue2.value.filter(
45683
+ (entry) => typeof entry === "string"
45684
+ );
45685
+ }
45164
45686
  var init_unordered_list_membership = __esm({
45165
45687
  "../src/models/members/unordered-list-membership.ts"() {
45166
45688
  "use strict";
@@ -47267,6 +47789,11 @@ function primitiveFallbackValue(document, member) {
47267
47789
  return { value: { listeners: [] } };
47268
47790
  }
47269
47791
  if (isMemberVariantBase(member)) {
47792
+ if (member.valueTypeInfo != null) {
47793
+ throw new Error(
47794
+ `Cannot create default value: lookup variant member "${member.name}" requires an authored lookup-variant selection.`
47795
+ );
47796
+ }
47270
47797
  if (!member.required) return { value: null };
47271
47798
  if (member.targetTypeInfo.type !== 7 /* Class */) {
47272
47799
  throw new Error(
@@ -47274,7 +47801,11 @@ function primitiveFallbackValue(document, member) {
47274
47801
  );
47275
47802
  }
47276
47803
  return {
47277
- value: { classId: member.targetTypeInfo.classId, variantId: null }
47804
+ value: {
47805
+ classId: member.targetTypeInfo.classId,
47806
+ variantId: null,
47807
+ rowValueId: null
47808
+ }
47278
47809
  };
47279
47810
  }
47280
47811
  if (member.kind === 21 /* Generic */) {
@@ -49209,7 +49740,7 @@ function renderMemberType(context, member, enclosingClassId) {
49209
49740
  );
49210
49741
  break;
49211
49742
  case "variant":
49212
- value = `NeoVariant<${renderType(context, member.targetType)}>`;
49743
+ value = member.valueType === null ? `NeoVariant<${renderType(context, member.targetType)}>` : `${NEO_LOOKUP_VARIANT_TYPE_NAME}<${renderType(context, member.targetType)}, ${renderType(context, member.valueType)}>`;
49213
49744
  break;
49214
49745
  }
49215
49746
  if ((member.kind === "class" || member.kind === "generic") && member.partial === true) {
@@ -49842,7 +50373,7 @@ function emitVariantFiles(context, variantInitializers) {
49842
50373
  const foldersById = new Map(
49843
50374
  manifest.variantFolders.map((folder) => [folder.id, folder])
49844
50375
  );
49845
- const pathFor = (className, folderPath3) => folderPath3 === null ? `Variants/${className}.neo` : `Variants/${className}/${folderPath3}.neo`;
50376
+ const pathFor = (className, folderPath3) => folderPath3 === null || folderPath3.length === 0 ? `Variants/${className}.neo` : `Variants/${className}/${folderPath3}.neo`;
49846
50377
  const fileFor = (className, folder) => {
49847
50378
  const path = pathFor(className, folder?.path ?? null);
49848
50379
  const existing = files.get(path);
@@ -49873,7 +50404,7 @@ function emitVariantFiles(context, variantInitializers) {
49873
50404
  const declarations = [];
49874
50405
  const recordKeys = [];
49875
50406
  if (file.folder !== null) {
49876
- declarations.push(emitVariantFolder(className, file.folder));
50407
+ declarations.push(emitVariantFolder(context, className, file.folder));
49877
50408
  recordKeys.push(`variant-folder:${file.folder.id}`);
49878
50409
  }
49879
50410
  const ordered = [...file.variants].sort(
@@ -49895,11 +50426,27 @@ function emitVariantFiles(context, variantInitializers) {
49895
50426
  `, recordKeys };
49896
50427
  });
49897
50428
  }
49898
- function emitVariantFolder(className, folder) {
50429
+ function emitVariantFolder(context, className, folder) {
49899
50430
  for (const segment of folder.path.split("/")) {
50431
+ if (segment.length === 0) continue;
49900
50432
  assertIdentifier(segment, "variant folder path segment");
49901
50433
  }
49902
- return `${id(folder.id)}NeoVariantFolder<${className}> ${variantFolderSymbol(folder)} = new(${quote(folder.path)});`;
50434
+ if (folder.binding === null) {
50435
+ return `${id(folder.id)}NeoVariantFolder<${className}> ${variantFolderSymbol(folder)} = new(${quote(folder.path)});`;
50436
+ }
50437
+ const valueType = renderLookupEntryType(
50438
+ context,
50439
+ folder.binding.collectionMemberId
50440
+ );
50441
+ const rootPath = context.collectionValuePaths.get(
50442
+ folder.binding.collectionValueId
50443
+ );
50444
+ const collectionValue = rootPath === void 0 ? `Reference(id: ${quote(folder.binding.collectionValueId)})` : `Reference(${rootPath})`;
50445
+ return `${id(folder.id)}${NEO_LOOKUP_VARIANT_FOLDER_TYPE_NAME}<${className}, ${valueType}> ${variantFolderSymbol(folder)} = new(
50446
+ path: ${quote(folder.path)},
50447
+ collection: ${qualifiedMember(context, folder.binding.collectionMemberId)},
50448
+ collectionValue: ${collectionValue},
50449
+ );`;
49903
50450
  }
49904
50451
  function emitVariant(context, className, variant, folder, variantInitializers) {
49905
50452
  assertIdentifier(variant.name, "variant");
@@ -49913,11 +50460,13 @@ function emitVariant(context, className, variant, folder, variantInitializers) {
49913
50460
  id(variant.id).trimEnd(),
49914
50461
  ...folder === null ? [] : [`@settings(folder: ${variantFolderSymbol(folder)})`]
49915
50462
  ];
50463
+ const type = folder?.binding == null ? `NeoVariant<${className}>` : `${NEO_LOOKUP_VARIANT_TYPE_NAME}<${className}, ${renderLookupEntryType(context, folder.binding.collectionMemberId)}>`;
49916
50464
  return `${annotations.join("\n")}
49917
- NeoVariant<${className}> ${variant.name} =
50465
+ ${type} ${variant.name} =
49918
50466
  ${indentNeoSourceNonEmptyLines(initializer, 2)};`;
49919
50467
  }
49920
50468
  function variantFolderSymbol(folder) {
50469
+ if (folder.path.length === 0) return "Root";
49921
50470
  return neoVariantFolderSymbol(folder.path);
49922
50471
  }
49923
50472
  function assertUniqueVariantFolderSymbols(folders) {
@@ -50031,7 +50580,16 @@ function renderVariantSelection(context, member, value) {
50031
50580
  `Variant member ${member.name} selects variant ${JSON.stringify(variantId)}, which this project does not declare.`
50032
50581
  );
50033
50582
  }
50034
- return path;
50583
+ const rowValueId = value.rowValueId;
50584
+ if (rowValueId === null || rowValueId === void 0) return path;
50585
+ if (typeof rowValueId !== "string") {
50586
+ throw new Error(
50587
+ `Variant member ${member.name} stores a malformed rowValueId.`
50588
+ );
50589
+ }
50590
+ const rootPath = context.collectionValuePaths.get(rowValueId);
50591
+ const reference2 = rootPath === void 0 ? `Reference(id: ${quote(rowValueId)})` : `Reference(${rootPath})`;
50592
+ return `${path}.Bind(${reference2})`;
50035
50593
  }
50036
50594
  var SPECIALIZED_RELATION_PROPERTIES;
50037
50595
  var init_emitter = __esm({
@@ -55540,7 +56098,7 @@ function lowerFieldMember(context, ownerClass, declaration, id2, commonInput, ba
55540
56098
  }))
55541
56099
  };
55542
56100
  }
55543
- if (name === NEO_VARIANT_TYPE_NAME) {
56101
+ if (name === NEO_VARIANT_TYPE_NAME || name === NEO_LOOKUP_VARIANT_TYPE_NAME) {
55544
56102
  if (partial) {
55545
56103
  throw new Error("Partial<T> requires a Class or Class generic target.");
55546
56104
  }
@@ -55551,7 +56109,8 @@ function lowerFieldMember(context, ownerClass, declaration, id2, commonInput, ba
55551
56109
  context,
55552
56110
  requiredTypeArgument(fieldType, 0),
55553
56111
  ownerClass
55554
- )
56112
+ ),
56113
+ valueType: name === NEO_LOOKUP_VARIANT_TYPE_NAME ? lowerType(context, requiredTypeArgument(fieldType, 1), ownerClass) : null
55555
56114
  };
55556
56115
  }
55557
56116
  if (name === "NeoAction") {
@@ -56429,7 +56988,7 @@ function lowerExpressionValue(context, expression, declaredExpected, ownerClass,
56429
56988
  if (expected.name === "NeoAction") {
56430
56989
  return lowerActionDefault(context, expression, ownerClass, path);
56431
56990
  }
56432
- if (expected.name === NEO_VARIANT_TYPE_NAME) {
56991
+ if (expected.name === NEO_VARIANT_TYPE_NAME || expected.name === NEO_LOOKUP_VARIANT_TYPE_NAME) {
56433
56992
  return lowerVariantDefault(context, expression, expected, path);
56434
56993
  }
56435
56994
  switch (expression.kind) {
@@ -57420,7 +57979,22 @@ function lowerVariantDefault(context, expression, expected, path) {
57420
57979
  }
57421
57980
  return null;
57422
57981
  }
57423
- const segments = variantPathSegments2(unwrapped);
57982
+ let candidate = unwrapped;
57983
+ let rowValueId = null;
57984
+ if (candidate.kind === "call" && candidate.callee.kind === "member" && candidate.callee.name === "Bind") {
57985
+ if (expected.name !== NEO_VARIANT_TYPE_NAME) {
57986
+ throw new Error(
57987
+ `${path} cannot Bind a row to NeoLookupVariant; keep the lookup handle unbound.`
57988
+ );
57989
+ }
57990
+ const row = candidate.args[0];
57991
+ if (row === void 0) {
57992
+ throw new Error(`${path}.Bind requires one Reference row.`);
57993
+ }
57994
+ rowValueId = lowerVariantBindingReference(context, row, path);
57995
+ candidate = candidate.callee.receiver;
57996
+ }
57997
+ const segments = variantPathSegments2(candidate);
57424
57998
  if (segments === null) {
57425
57999
  throw new Error(
57426
58000
  `${path} must be a variant path such as Tree.${NEO_VARIANT_SCOPE_NAME}.ChoppedTree (or Tree.${NEO_VARIANT_SCOPE_NAME}.${NEO_VARIANT_RESERVED_NAME} for the class itself). A variant is a record, so Reference(...) cannot name one.`
@@ -57433,7 +58007,59 @@ function lowerVariantDefault(context, expression, expected, path) {
57433
58007
  `${path} names variant path ${JSON.stringify(spelled)}, which this project does not declare.`
57434
58008
  );
57435
58009
  }
57436
- return { classId: target.classId, variantId: target.variantId };
58010
+ if (target.valueClassId !== null && expected.name === NEO_VARIANT_TYPE_NAME) {
58011
+ if (rowValueId === null) {
58012
+ throw new Error(
58013
+ `${path} selects a lookup variant and must bind a collection row with .Bind(Reference(...)).`
58014
+ );
58015
+ }
58016
+ } else if (rowValueId !== null) {
58017
+ throw new Error(`${path} can bind a row only to a lookup variant.`);
58018
+ }
58019
+ if (expected.name === NEO_LOOKUP_VARIANT_TYPE_NAME && target.valueClassId === null) {
58020
+ throw new Error(
58021
+ `${path} expects NeoLookupVariant but selects a plain NeoVariant.`
58022
+ );
58023
+ }
58024
+ return {
58025
+ classId: target.classId,
58026
+ variantId: target.variantId,
58027
+ rowValueId
58028
+ };
58029
+ }
58030
+ function lowerVariantBindingReference(context, expression, path) {
58031
+ const id2 = referenceId(expressionSource(expression));
58032
+ if (id2 === null) {
58033
+ throw new Error(
58034
+ `${path}.Bind requires Reference(id: ...) or Reference(root....).`
58035
+ );
58036
+ }
58037
+ const prefix = `${PENDING_ID_PREFIX}root-path:`;
58038
+ if (!id2.startsWith(prefix)) return id2;
58039
+ const rootPath = id2.slice(prefix.length);
58040
+ const target = context.rootValueTargetsByPath.get(rootPath);
58041
+ if (target === void 0) {
58042
+ throw new Error(`${path}.Bind references unknown row path ${rootPath}.`);
58043
+ }
58044
+ return target.valueId;
58045
+ }
58046
+ function expressionSource(expression) {
58047
+ if (expression.kind === "annotated") {
58048
+ return expressionSource(expression.expression);
58049
+ }
58050
+ if (expression.kind === "litString") return JSON.stringify(expression.value);
58051
+ if (expression.kind === "ident") return expression.name;
58052
+ if (expression.kind === "member") {
58053
+ return `${expressionSource(expression.receiver)}.${expression.name}`;
58054
+ }
58055
+ if (expression.kind === "call") {
58056
+ const args = expression.args.map((argument2, index) => {
58057
+ const name = expression.argumentNames?.[index];
58058
+ return `${name == null ? "" : `${name}: `}${expressionSource(argument2)}`;
58059
+ });
58060
+ return `${expressionSource(expression.callee)}(${args.join(", ")})`;
58061
+ }
58062
+ throw new Error("Unsupported Reference expression in variant binding.");
57437
58063
  }
57438
58064
  function variantPathSegments2(expression) {
57439
58065
  const segments = [];
@@ -57617,7 +58243,12 @@ function isNeoClassVariantFolder(value) {
57617
58243
  const v = value;
57618
58244
  if (!isNonEmptyString2(v.id)) return false;
57619
58245
  if (!isNonEmptyString2(v.classId)) return false;
57620
- if (!isNonEmptyString2(v.path)) return false;
58246
+ if (typeof v.path !== "string") return false;
58247
+ if (v.binding === void 0 || v.binding === null) return v.path.length > 0;
58248
+ if (typeof v.binding !== "object" || Array.isArray(v.binding)) return false;
58249
+ const binding = v.binding;
58250
+ if (!isNonEmptyString2(binding.collectionMemberId)) return false;
58251
+ if (!isNonEmptyString2(binding.collectionValueId)) return false;
57621
58252
  return true;
57622
58253
  }
57623
58254
  var init_variants = __esm({
@@ -57627,14 +58258,19 @@ var init_variants = __esm({
57627
58258
  });
57628
58259
 
57629
58260
  // ../src/models/variants/variant-member.ts
57630
- function neoVariantSystemClass(classes) {
58261
+ function neoVariantSystemClass(classes, lookup = false) {
57631
58262
  const declared = classes.find(
57632
- (item) => item.system?.worldKind === NeoWorldSystemClassKind.Variant
58263
+ (item) => item.system?.worldKind === (lookup ? NeoWorldSystemClassKind.LookupVariant : NeoWorldSystemClassKind.Variant)
57633
58264
  );
57634
58265
  if (declared === void 0) return null;
57635
58266
  const targetParamId = declared.genericParams?.[0]?.id;
57636
58267
  if (targetParamId === void 0) return null;
57637
- return { classId: declared.id, targetParamId };
58268
+ const valueParamId = declared.genericParams?.[1]?.id;
58269
+ return {
58270
+ classId: declared.id,
58271
+ targetParamId,
58272
+ ...valueParamId === void 0 ? {} : { valueParamId }
58273
+ };
57638
58274
  }
57639
58275
  var init_variant_member = __esm({
57640
58276
  "../src/models/variants/variant-member.ts"() {
@@ -57876,11 +58512,25 @@ function variantProjection(context) {
57876
58512
  (folder) => [folder.id, folder.path]
57877
58513
  )
57878
58514
  );
58515
+ const foldersById = new Map(
58516
+ (context.variantFolders ?? []).map(
58517
+ (folder) => [folder.id, folder]
58518
+ )
58519
+ );
58520
+ const membersById = new Map(
58521
+ context.vm.members.map((member) => [member.id, member])
58522
+ );
57879
58523
  return variants.map((variant) => ({
57880
58524
  id: variant.id,
57881
58525
  classId: variant.classId,
57882
58526
  name: variant.name,
57883
- folder: variant.folderId === null ? null : pathByFolderId.get(variant.folderId) ?? null
58527
+ folder: variant.folderId === null ? null : pathByFolderId.get(variant.folderId) ?? null,
58528
+ ...(() => {
58529
+ const folder = variant.folderId === null ? void 0 : foldersById.get(variant.folderId);
58530
+ const collection = folder?.binding == null ? void 0 : membersById.get(folder.binding.collectionMemberId);
58531
+ const entry = isMemberListBase(collection) ? membersById.get(collection.entryMemberId) : void 0;
58532
+ return isMemberClassBase(entry) ? { valueTypeId: entry.classId } : {};
58533
+ })()
57884
58534
  }));
57885
58535
  }
57886
58536
  function virtualCSharpIdentifier(value) {
@@ -58658,7 +59308,10 @@ function memberRuntimeType(record3, context, seen = /* @__PURE__ */ new Set(), g
58658
59308
  }
58659
59309
  case 27 /* Variant */: {
58660
59310
  if (!isMemberVariantBase(member)) return UNKNOWN_TYPE2;
58661
- const systemClass = neoVariantSystemClass(context.vm.classes);
59311
+ const systemClass = neoVariantSystemClass(
59312
+ context.vm.classes,
59313
+ member.valueTypeInfo != null
59314
+ );
58662
59315
  if (systemClass === null) return UNKNOWN_TYPE2;
58663
59316
  const target = toLanguageType(
58664
59317
  member.targetTypeInfo,
@@ -58669,7 +59322,16 @@ function memberRuntimeType(record3, context, seen = /* @__PURE__ */ new Set(), g
58669
59322
  kind: "named",
58670
59323
  typeId: systemClass.classId,
58671
59324
  nullable: !required2,
58672
- typeArguments: [target]
59325
+ typeArguments: [
59326
+ target,
59327
+ ...member.valueTypeInfo == null ? [] : [
59328
+ toLanguageType(
59329
+ member.valueTypeInfo,
59330
+ context,
59331
+ genericEnvironment
59332
+ )
59333
+ ]
59334
+ ]
58673
59335
  };
58674
59336
  }
58675
59337
  case 22 /* Interface */:
@@ -64699,7 +65361,11 @@ function evalPointer(pointer, scope, ctx) {
64699
65361
  return row.value;
64700
65362
  }
64701
65363
  case "variant" /* variant */:
64702
- return { classId: pointer.classId, variantId: pointer.variantId };
65364
+ return {
65365
+ classId: pointer.classId,
65366
+ variantId: pointer.variantId,
65367
+ rowValueId: pointer.rowValueId ?? null
65368
+ };
64703
65369
  case "staticMember" /* staticMember */:
64704
65370
  return evalStaticMember(pointer.memberId, ctx);
64705
65371
  case "keyOf" /* keyOf */:
@@ -68508,13 +69174,22 @@ function readVariantSelection(value, label) {
68508
69174
  throw new NSGetterRuntimeError(`${label} carries no class id.`);
68509
69175
  }
68510
69176
  const variantId = record3.variantId;
69177
+ const rowValueId = record3.rowValueId;
69178
+ if (rowValueId !== void 0 && rowValueId !== null && (typeof rowValueId !== "string" || rowValueId.length === 0)) {
69179
+ throw new NSGetterRuntimeError(
69180
+ `${label} carries a malformed lookup row id.`
69181
+ );
69182
+ }
68511
69183
  if (variantId === null || variantId === void 0) {
68512
- return { classId, variantId: null };
69184
+ if (rowValueId != null) {
69185
+ throw new NSGetterRuntimeError(`${label} cannot bind a row to Base.`);
69186
+ }
69187
+ return { classId, variantId: null, rowValueId: null };
68513
69188
  }
68514
69189
  if (typeof variantId !== "string" || variantId.length === 0) {
68515
69190
  throw new NSGetterRuntimeError(`${label} carries a malformed variant id.`);
68516
69191
  }
68517
- return { classId, variantId };
69192
+ return { classId, variantId, rowValueId: rowValueId ?? null };
68518
69193
  }
68519
69194
  function resolveVariantGraph(selection2, ctx) {
68520
69195
  const variantId = selection2.variantId;
@@ -68557,9 +69232,67 @@ function resolveVariantGraph(selection2, ctx) {
68557
69232
  variantId,
68558
69233
  name: record3.name,
68559
69234
  targetClassId: record3.classId,
68560
- root
69235
+ root,
69236
+ binding: record3.folderId === null ? null : (ctx.vm.variantFolders ?? []).find(
69237
+ (folder) => folder.id === record3.folderId
69238
+ )?.binding ?? null
68561
69239
  };
68562
69240
  }
69241
+ function resolveVariantRowParameter(args) {
69242
+ const { graph, selection: selection2, explicitPointer, scope, ctx } = args;
69243
+ const binding = graph.binding;
69244
+ if (binding === null) {
69245
+ if (explicitPointer !== void 0 || selection2.rowValueId !== null) {
69246
+ throw new NSGetterRuntimeError(
69247
+ `Variant '${graph.name}' is not collection-bound and cannot receive a row.`
69248
+ );
69249
+ }
69250
+ return [];
69251
+ }
69252
+ let rowId = selection2.rowValueId;
69253
+ let rowValue;
69254
+ if (explicitPointer !== void 0) {
69255
+ rowValue = evalPointer(explicitPointer, scope, ctx);
69256
+ if (typeof rowValue === "object" && rowValue !== null) {
69257
+ rowId = trackedRowForValueReference(rowValue, ctx)?.id ?? null;
69258
+ }
69259
+ }
69260
+ if (rowId === null) {
69261
+ throw new NSGetterRuntimeError(
69262
+ `Lookup variant '${graph.name}' requires a row from its bound collection.`
69263
+ );
69264
+ }
69265
+ const row = evalValueById(
69266
+ ctx,
69267
+ rowId,
69268
+ ctx.__runtimeSessionValues,
69269
+ ctx.__valueOverlay
69270
+ );
69271
+ if (row === null) {
69272
+ throw new NSGetterRuntimeError(
69273
+ `Lookup variant '${graph.name}' references missing row '${rowId}'.`
69274
+ );
69275
+ }
69276
+ const collection = evalValueById(
69277
+ ctx,
69278
+ binding.collectionValueId,
69279
+ ctx.__runtimeSessionValues,
69280
+ ctx.__valueOverlay
69281
+ );
69282
+ const isOrderedEntry = Array.isArray(collection?.value) ? collection.value.includes(rowId) : false;
69283
+ const isUnorderedEntry = row.containerId === binding.collectionValueId;
69284
+ if (!isOrderedEntry && !isUnorderedEntry) {
69285
+ throw new NSGetterRuntimeError(
69286
+ `Lookup variant '${graph.name}' row '${rowId}' is not an entry of collection '${binding.collectionValueId}'.`
69287
+ );
69288
+ }
69289
+ if (explicitPointer !== void 0 && rowValue !== row.value) {
69290
+ throw new NSGetterRuntimeError(
69291
+ `Lookup variant '${graph.name}' received a value that does not resolve to bound row '${rowId}'.`
69292
+ );
69293
+ }
69294
+ return [row.value];
69295
+ }
68563
69296
  function invokeVariantClosure(args) {
68564
69297
  const { closureValueId, ctx, label, lexicalThis, parameters } = args;
68565
69298
  if (typeof closureValueId !== "string" || closureValueId.length === 0) {
@@ -68743,6 +69476,13 @@ function evalVariantInitialize(info, scope, ctx) {
68743
69476
  );
68744
69477
  }
68745
69478
  const graph = resolveVariantGraph(selection2, ctx);
69479
+ const parameters = resolveVariantRowParameter({
69480
+ graph,
69481
+ selection: selection2,
69482
+ explicitPointer: info.rowPointer,
69483
+ scope,
69484
+ ctx
69485
+ });
68746
69486
  const closeFrame = pushConstructionFrame(ctx, `Variants.${graph.name}`);
68747
69487
  try {
68748
69488
  const instance = invokeVariantClosure({
@@ -68750,7 +69490,7 @@ function evalVariantInitialize(info, scope, ctx) {
68750
69490
  ctx,
68751
69491
  label: `Variant '${graph.name}' Initialize`,
68752
69492
  lexicalThis: null,
68753
- parameters: []
69493
+ parameters
68754
69494
  });
68755
69495
  if (instance === null || instance === void 0) {
68756
69496
  throw new NSGetterRuntimeError(
@@ -68777,6 +69517,13 @@ function evalVariantApply(info, scope, ctx) {
68777
69517
  );
68778
69518
  if (selection2.variantId === null) return receiver;
68779
69519
  const graph = resolveVariantGraph(selection2, ctx);
69520
+ const rowParameters = resolveVariantRowParameter({
69521
+ graph,
69522
+ selection: selection2,
69523
+ explicitPointer: info.rowPointer,
69524
+ scope,
69525
+ ctx
69526
+ });
68780
69527
  const applyValueId = graph.root.Apply;
68781
69528
  if (typeof applyValueId === "string" && applyValueId.length > 0) {
68782
69529
  invokeVariantClosure({
@@ -68784,7 +69531,7 @@ function evalVariantApply(info, scope, ctx) {
68784
69531
  ctx,
68785
69532
  label: `Variant '${graph.name}' Apply`,
68786
69533
  lexicalThis: null,
68787
- parameters: [receiver]
69534
+ parameters: [receiver, ...rowParameters]
68788
69535
  });
68789
69536
  }
68790
69537
  applyVariantDeclarativeHalves(receiver, graph, ctx);
@@ -73573,17 +74320,17 @@ var init_lower_relations = __esm({
73573
74320
  function lowerVariants(context, analysis) {
73574
74321
  const folders = lowerVariantFolders(context, analysis);
73575
74322
  const foldersBySymbol = new Map(
73576
- analysis.variants.globals.filter((global) => global.type.name === NEO_VARIANT_FOLDER_TYPE_NAME).map(
74323
+ analysis.variants.globals.filter((global) => isNeoVariantFolderTypeName(global.type.name)).map(
73577
74324
  (global) => [
73578
74325
  folderSymbolKey(owningClassName(global), global.name),
73579
- variantRecordId(global, NEO_VARIANT_FOLDER_TYPE_NAME)
74326
+ variantRecordId(global, global.type.name)
73580
74327
  ]
73581
74328
  )
73582
74329
  );
73583
74330
  const variants = variantGlobals(analysis).map((global) => {
73584
74331
  const className = owningClassName(global);
73585
74332
  const classId = requiredName2(context.classIdsByName, className, "class");
73586
- const variantId = variantRecordId(global, NEO_VARIANT_TYPE_NAME);
74333
+ const variantId = variantRecordId(global, global.type.name);
73587
74334
  return {
73588
74335
  id: variantId,
73589
74336
  source: sourceIdentity2(global, "variant", global.name),
@@ -73613,6 +74360,14 @@ function variantValueBindingsV4(manifest, analysis) {
73613
74360
  variantId: variant.id,
73614
74361
  targetClassId: variant.classId,
73615
74362
  targetClassName: requiredClassName2(classNames, variant.classId),
74363
+ variantTypeName: global.type.name,
74364
+ valueClassId: global.type.name === NEO_LOOKUP_VARIANT_TYPE_NAME ? requiredName2(
74365
+ new Map(
74366
+ manifest.classes.map((entry) => [entry.name, entry.id])
74367
+ ),
74368
+ valueClassName(global),
74369
+ "class"
74370
+ ) : null,
73616
74371
  valueId: variant.valueId,
73617
74372
  initializer: global.initializer,
73618
74373
  source: global.source,
@@ -73632,17 +74387,18 @@ function requiredClassName2(classNames, classId) {
73632
74387
  }
73633
74388
  function variantGlobals(analysis) {
73634
74389
  return analysis.variants.globals.filter(
73635
- (global) => global.type.name === NEO_VARIANT_TYPE_NAME
74390
+ (global) => isNeoVariantTypeName(global.type.name)
73636
74391
  );
73637
74392
  }
73638
74393
  function lowerVariantFolders(context, analysis) {
73639
- return analysis.variants.globals.filter((global) => global.type.name === NEO_VARIANT_FOLDER_TYPE_NAME).map((global) => {
74394
+ return analysis.variants.globals.filter((global) => isNeoVariantFolderTypeName(global.type.name)).map((global) => {
73640
74395
  const className = owningClassName(global);
73641
74396
  return {
73642
74397
  id: variantRecordId(global, NEO_VARIANT_FOLDER_TYPE_NAME),
73643
74398
  source: sourceIdentity2(global, "variantFolder", global.name),
73644
74399
  classId: requiredName2(context.classIdsByName, className, "class"),
73645
- path: folderPath2(global)
74400
+ path: folderPath2(global),
74401
+ binding: folderBinding(context, global)
73646
74402
  };
73647
74403
  });
73648
74404
  }
@@ -73656,7 +74412,7 @@ function owningClassName(global) {
73656
74412
  return argument2;
73657
74413
  }
73658
74414
  function variantRecordId(global, typeName) {
73659
- const kind = typeName === NEO_VARIANT_TYPE_NAME ? "variant" : "variant-folder";
74415
+ const kind = isNeoVariantTypeName(typeName) ? "variant" : "variant-folder";
73660
74416
  return materializedIdentityId(global, kind, global.name);
73661
74417
  }
73662
74418
  function variantRootValueId(context, variantId, global, className) {
@@ -73702,14 +74458,19 @@ function folderPath2(global) {
73702
74458
  `Variant folder ${global.name} must be declared as new("Path").`
73703
74459
  );
73704
74460
  }
73705
- const argument2 = expression.args[0];
74461
+ const pathIndex = expression.argumentNames?.findIndex(
74462
+ (name) => name === "path"
74463
+ );
74464
+ const argument2 = expression.args[pathIndex !== void 0 && pathIndex >= 0 ? pathIndex : 0];
73706
74465
  if (argument2 === void 0 || argument2.kind !== "litString") {
73707
74466
  throw new Error(
73708
74467
  `Variant folder ${global.name} requires one string-literal path argument.`
73709
74468
  );
73710
74469
  }
73711
- if (argument2.value.length === 0) {
73712
- throw new Error(`Variant folder ${global.name} has an empty path.`);
74470
+ if (argument2.value.length === 0 && global.type.name !== NEO_LOOKUP_VARIANT_FOLDER_TYPE_NAME) {
74471
+ throw new Error(
74472
+ `Only a bound lookup variant folder may have an empty path.`
74473
+ );
73713
74474
  }
73714
74475
  return argument2.value;
73715
74476
  }
@@ -73721,7 +74482,7 @@ function unwrapAnnotated2(expression) {
73721
74482
  function buildVariantPathIndexV4(classIdsByName, analysis) {
73722
74483
  const index = /* @__PURE__ */ new Map();
73723
74484
  const foldersBySymbol = new Map(
73724
- analysis.variants.globals.filter((global) => global.type.name === NEO_VARIANT_FOLDER_TYPE_NAME).map(
74485
+ analysis.variants.globals.filter((global) => isNeoVariantFolderTypeName(global.type.name)).map(
73725
74486
  (global) => [
73726
74487
  folderSymbolKey(owningClassName(global), global.name),
73727
74488
  folderPath2(global)
@@ -73733,7 +74494,8 @@ function buildVariantPathIndexV4(classIdsByName, analysis) {
73733
74494
  `${className}.${NEO_VARIANT_SCOPE_NAME}.${NEO_VARIANT_RESERVED_NAME}`,
73734
74495
  {
73735
74496
  classId,
73736
- variantId: null
74497
+ variantId: null,
74498
+ valueClassId: null
73737
74499
  }
73738
74500
  );
73739
74501
  }
@@ -73748,16 +74510,88 @@ function buildVariantPathIndexV4(classIdsByName, analysis) {
73748
74510
  const scope = path === null ? `${className}.${NEO_VARIANT_SCOPE_NAME}` : `${className}.${NEO_VARIANT_SCOPE_NAME}.${path.split(NEO_VARIANT_FOLDER_PATH_SEPARATOR).join(".")}`;
73749
74511
  index.set(`${scope}.${global.name}`, {
73750
74512
  classId,
73751
- variantId: variantRecordId(global, NEO_VARIANT_TYPE_NAME)
74513
+ variantId: variantRecordId(global, global.type.name),
74514
+ valueClassId: global.type.name === NEO_LOOKUP_VARIANT_TYPE_NAME ? classIdsByName.get(valueClassName(global)) ?? null : null
73752
74515
  });
73753
74516
  }
73754
74517
  return index;
73755
74518
  }
74519
+ function valueClassName(global) {
74520
+ const argument2 = global.type.arguments[1]?.name;
74521
+ if (argument2 === void 0) {
74522
+ throw new Error(
74523
+ `${global.type.name} ${global.name} requires a TValue type argument.`
74524
+ );
74525
+ }
74526
+ return argument2;
74527
+ }
74528
+ function folderBinding(context, global) {
74529
+ if (global.type.name !== NEO_LOOKUP_VARIANT_FOLDER_TYPE_NAME) return null;
74530
+ const expression = unwrapAnnotated2(parseExpression(global.initializer));
74531
+ if (expression.kind !== "new") {
74532
+ throw new Error(
74533
+ `Lookup variant folder ${global.name} must be declared with new(...).`
74534
+ );
74535
+ }
74536
+ const named = /* @__PURE__ */ new Map();
74537
+ expression.args.forEach((argument2, index) => {
74538
+ const name = expression.argumentNames?.[index];
74539
+ if (name !== void 0 && name !== null) named.set(name, argument2);
74540
+ });
74541
+ const collection = named.get("collection");
74542
+ const collectionValue = named.get("collectionValue");
74543
+ const collectionPath = collection === void 0 ? null : dottedPath(collection);
74544
+ if (collectionPath === null) {
74545
+ throw new Error(
74546
+ `Lookup variant folder ${global.name} requires collection: Class.Member.`
74547
+ );
74548
+ }
74549
+ const collectionMemberId = resolveQualifiedMemberId(context, collectionPath);
74550
+ if (collectionMemberId === null) {
74551
+ throw new Error(
74552
+ `Lookup variant folder ${global.name} names unknown collection ${collectionPath}.`
74553
+ );
74554
+ }
74555
+ if (collectionValue === void 0) {
74556
+ throw new Error(
74557
+ `Lookup variant folder ${global.name} requires collectionValue: Reference(...).`
74558
+ );
74559
+ }
74560
+ const collectionValueId = lowerReferenceValue(collectionValue);
74561
+ if (collectionValueId === null) {
74562
+ throw new Error(
74563
+ `Lookup variant folder ${global.name} has an invalid collectionValue reference.`
74564
+ );
74565
+ }
74566
+ return { collectionMemberId, collectionValueId };
74567
+ }
74568
+ function lowerReferenceValue(expression) {
74569
+ if (expression.kind !== "call" || expression.callee.kind !== "ident" || expression.callee.name !== "Reference")
74570
+ return null;
74571
+ const idIndex = expression.argumentNames?.findIndex((name) => name === "id") ?? -1;
74572
+ const target = expression.args[idIndex >= 0 ? idIndex : 0];
74573
+ if (target?.kind === "litString") return target.value;
74574
+ const path = target === void 0 ? null : dottedPath(target);
74575
+ if (path === null || !path.startsWith("root.")) return null;
74576
+ return referenceId(`Reference(${path})`);
74577
+ }
74578
+ function dottedPath(expression) {
74579
+ const parts = [];
74580
+ let cursor = expression;
74581
+ while (cursor.kind === "member") {
74582
+ parts.unshift(cursor.name);
74583
+ cursor = cursor.receiver;
74584
+ }
74585
+ if (cursor.kind !== "ident") return null;
74586
+ parts.unshift(cursor.name);
74587
+ return parts.join(".");
74588
+ }
73756
74589
  var init_lower_variants = __esm({
73757
74590
  "src/project-source/lower-variants.ts"() {
73758
74591
  "use strict";
73759
74592
  init_src();
73760
74593
  init_lower_support();
74594
+ init_lower_members();
73761
74595
  }
73762
74596
  });
73763
74597
 
@@ -76406,6 +77240,30 @@ function resolveRootPathCollectionValuesV4(args) {
76406
77240
  }
76407
77241
  return resolved;
76408
77242
  }
77243
+ function resolveRootPathVariantFolderCollectionValuesV4(args) {
77244
+ const resolved = /* @__PURE__ */ new Map();
77245
+ for (const folder of args.manifest.variantFolders) {
77246
+ const spelled = folder.binding?.collectionValueId;
77247
+ if (typeof spelled !== "string") continue;
77248
+ if (!spelled.startsWith(ROOT_PATH_PENDING_PREFIX)) continue;
77249
+ const path = spelled.slice(ROOT_PATH_PENDING_PREFIX.length);
77250
+ const site = args.siteForFolder(folder.id);
77251
+ const fail = (message) => {
77252
+ args.registry.loweringFailures.push({
77253
+ message,
77254
+ site: {
77255
+ uri: site?.uri ?? "<schema>",
77256
+ declarationStart: site?.start ?? { line: 1, character: 0 },
77257
+ expression: null,
77258
+ label: folder.path
77259
+ }
77260
+ });
77261
+ };
77262
+ const target = walkRootValuePath(path, args.state, args.registry, fail);
77263
+ if (target !== null) resolved.set(folder.id, target);
77264
+ }
77265
+ return resolved;
77266
+ }
76409
77267
  function walkRootValuePath(path, state, registry, fail) {
76410
77268
  const segments = path.split(".");
76411
77269
  const slotName = segments[1];
@@ -77890,18 +78748,31 @@ function syntheticMember(fields) {
77890
78748
  function variantOwnershipRoots(args) {
77891
78749
  const empty = { roots: [], bindingMembers: [] };
77892
78750
  if (args.variants.length === 0) return empty;
77893
- const variantClass = args.classes.find(
78751
+ const plainVariantClass = args.classes.find(
77894
78752
  (schemaClass2) => schemaClass2.system?.worldKind === NeoWorldSystemClassKind.Variant
77895
78753
  );
77896
- if (variantClass === void 0) return empty;
77897
- const parameterId = variantClass.genericParams?.[0]?.id;
77898
- if (parameterId === void 0) return empty;
78754
+ if (plainVariantClass === void 0) return empty;
77899
78755
  const valueById = new Map(args.values.map((value) => [value.id, value]));
78756
+ const folderById = new Map(
78757
+ (args.variantFolders ?? []).map((folder) => [folder.id, folder])
78758
+ );
78759
+ const memberById = new Map(
78760
+ (args.members ?? []).map((member) => [member.id, member])
78761
+ );
77900
78762
  const roots = [];
77901
78763
  const bindingMembers = [];
77902
78764
  for (const variant of args.variants) {
77903
78765
  const root = valueById.get(variant.valueId);
77904
78766
  if (root === void 0) continue;
78767
+ const lookupFolder = variant.folderId == null ? void 0 : folderById.get(variant.folderId);
78768
+ const inferredVariantKind = lookupFolder?.binding == null ? NeoWorldSystemClassKind.Variant : NeoWorldSystemClassKind.LookupVariant;
78769
+ const variantClass = args.classes.find(
78770
+ (schemaClass2) => schemaClass2.id === root.classId && (schemaClass2.system?.worldKind === NeoWorldSystemClassKind.Variant || schemaClass2.system?.worldKind === NeoWorldSystemClassKind.LookupVariant)
78771
+ ) ?? args.classes.find(
78772
+ (schemaClass2) => schemaClass2.system?.worldKind === inferredVariantKind
78773
+ ) ?? plainVariantClass;
78774
+ const parameterId = variantClass.genericParams?.[0]?.id;
78775
+ if (parameterId === void 0) continue;
77905
78776
  const stampedTargetMemberId = readStampedTargetMemberId(root, parameterId);
77906
78777
  const targetClassId = variant.classId;
77907
78778
  let binding = null;
@@ -77919,13 +78790,31 @@ function variantOwnershipRoots(args) {
77919
78790
  );
77920
78791
  binding = { kind: "member", memberId: bindingMemberId };
77921
78792
  }
78793
+ const stampedArguments = Object.fromEntries(
78794
+ (variantClass.genericParams ?? []).flatMap((parameter4) => {
78795
+ const memberId = root.genericBindings?.[parameter4.id];
78796
+ return typeof memberId === "string" ? [[parameter4.id, { kind: "member", memberId }]] : [];
78797
+ })
78798
+ );
78799
+ const lookupParameterId = variantClass.genericParams?.[1]?.id;
78800
+ const lookupCollection = lookupFolder?.binding == null ? void 0 : memberById.get(lookupFolder.binding.collectionMemberId);
78801
+ const lookupBinding = lookupParameterId !== void 0 && isMemberListBase(lookupCollection) ? {
78802
+ [lookupParameterId]: {
78803
+ kind: "member",
78804
+ memberId: lookupCollection.entryMemberId
78805
+ }
78806
+ } : {};
77922
78807
  roots.push({
77923
78808
  valueId: variant.valueId,
77924
78809
  member: syntheticMember({
77925
78810
  id: variantRootMemberId(variant.id),
77926
78811
  name: "Variant",
77927
78812
  classId: variantClass.id,
77928
- classArguments: binding === null ? {} : { [parameterId]: binding }
78813
+ classArguments: {
78814
+ ...stampedArguments,
78815
+ ...binding === null ? {} : { [parameterId]: binding },
78816
+ ...lookupBinding
78817
+ }
77929
78818
  })
77930
78819
  });
77931
78820
  }
@@ -77941,8 +78830,10 @@ function readStampedTargetMemberId(root, parameterId) {
77941
78830
  function withVariantOwnershipRoots(document, variants) {
77942
78831
  const { roots, bindingMembers } = variantOwnershipRoots({
77943
78832
  variants,
78833
+ variantFolders: document.variantFolders,
77944
78834
  values: document.values,
77945
- classes: document.classes
78835
+ classes: document.classes,
78836
+ members: document.members
77946
78837
  });
77947
78838
  if (bindingMembers.length === 0) return { document, roots };
77948
78839
  return {
@@ -79716,6 +80607,28 @@ var init_world_layer_link_target = __esm({
79716
80607
  });
79717
80608
 
79718
80609
  // ../src/models/project/world-content-sidecar-validation.ts
80610
+ function worldPlacementVariantBindings(args) {
80611
+ const foldersById = new Map(
80612
+ args.variantFolders.map((folder) => [folder.id, folder])
80613
+ );
80614
+ const membersById = new Map(
80615
+ args.members.map((member) => [member.id, member])
80616
+ );
80617
+ const valuesById = new Map(args.values.map((value) => [value.id, value]));
80618
+ return args.variants.map((variant) => {
80619
+ const folder = variant.folderId === null ? null : foldersById.get(variant.folderId) ?? null;
80620
+ const binding = folder?.binding ?? null;
80621
+ if (binding === null) return { id: variant.id, classId: variant.classId };
80622
+ const collection = membersById.get(binding.collectionMemberId);
80623
+ const value = valuesById.get(binding.collectionValueId);
80624
+ const rows = isMemberListBase(collection) && value !== void 0 ? listEntryIdsForValue(collection, value, args.values) : [];
80625
+ return {
80626
+ id: variant.id,
80627
+ classId: variant.classId,
80628
+ lookupRowValueIds: rows
80629
+ };
80630
+ });
80631
+ }
79719
80632
  function validateWorldContentSidecars(args) {
79720
80633
  const variants = args.variants ?? [];
79721
80634
  const valuesById = new Map(args.values.map((value) => [value.id, value]));
@@ -79984,11 +80897,19 @@ function validateWorldPlacementSidecars(args) {
79984
80897
  assetClassId,
79985
80898
  placementId: args.placement.id,
79986
80899
  variantId: optionalNonEmptyString(record3.variantId),
80900
+ rowValueId: optionalNonEmptyString(record3.rowValueId),
79987
80901
  variants: args.variants
79988
80902
  });
79989
80903
  }
79990
80904
  function validatePlacementVariantBinding(args) {
79991
- if (args.variantId === null) return;
80905
+ if (args.variantId === null) {
80906
+ if (args.rowValueId !== null) {
80907
+ throw new Error(
80908
+ `Placement value "${args.placementId}" binds row "${args.rowValueId}" without a variant.`
80909
+ );
80910
+ }
80911
+ return;
80912
+ }
79992
80913
  const variant = args.variants.find(
79993
80914
  (candidate) => candidate.id === args.variantId
79994
80915
  );
@@ -80002,6 +80923,20 @@ function validatePlacementVariantBinding(args) {
80002
80923
  `Placement value "${args.placementId}" places class "${args.assetClassId}" but names variant "${args.variantId}" of class "${variant.classId}"; variant lookup is not virtual.`
80003
80924
  );
80004
80925
  }
80926
+ const lookupRows = variant.lookupRowValueIds;
80927
+ if (lookupRows === void 0) {
80928
+ if (args.rowValueId !== null) {
80929
+ throw new Error(
80930
+ `Placement value "${args.placementId}" binds a row to plain variant "${args.variantId}".`
80931
+ );
80932
+ }
80933
+ return;
80934
+ }
80935
+ if (args.rowValueId === null || !lookupRows.includes(args.rowValueId)) {
80936
+ throw new Error(
80937
+ `Placement value "${args.placementId}" must bind a live row from lookup variant "${args.variantId}".`
80938
+ );
80939
+ }
80005
80940
  }
80006
80941
  function validateOwnedSparseValue(args) {
80007
80942
  if (args.referenceId === null) return;
@@ -80136,6 +81071,7 @@ var init_world_content_sidecar_validation = __esm({
80136
81071
  init_classes();
80137
81072
  init_world_layer_link_target();
80138
81073
  init_core();
81074
+ init_members();
80139
81075
  init_internal_record_relations();
80140
81076
  WORLD_PLACEMENT_SIDECAR_KEYS = [
80141
81077
  "assetClassId",
@@ -80199,10 +81135,29 @@ function readVariantFolderRecord(value) {
80199
81135
  if (typeof value.classId !== "string" || value.classId.length === 0) {
80200
81136
  throw new Error(`Variant folder "${value.id}" classId is invalid.`);
80201
81137
  }
80202
- if (typeof value.path !== "string" || value.path.length === 0) {
81138
+ if (typeof value.path !== "string") {
80203
81139
  throw new Error(`Variant folder "${value.id}" path is invalid.`);
80204
81140
  }
80205
- return { id: value.id, classId: value.classId, path: value.path };
81141
+ let binding = null;
81142
+ if (value.binding !== void 0 && value.binding !== null) {
81143
+ if (!isRecordObject(value.binding)) {
81144
+ throw new Error(`Variant folder "${value.id}" binding is invalid.`);
81145
+ }
81146
+ const collectionMemberId = value.binding.collectionMemberId;
81147
+ const collectionValueId = value.binding.collectionValueId;
81148
+ if (typeof collectionMemberId !== "string" || collectionMemberId.length === 0 || typeof collectionValueId !== "string" || collectionValueId.length === 0) {
81149
+ throw new Error(
81150
+ `Variant folder "${value.id}" binding requires collectionMemberId and collectionValueId.`
81151
+ );
81152
+ }
81153
+ binding = { collectionMemberId, collectionValueId };
81154
+ }
81155
+ if (value.path.length === 0 && binding === null) {
81156
+ throw new Error(
81157
+ `Variant folder "${value.id}" may use the class-root path only with a collection binding.`
81158
+ );
81159
+ }
81160
+ return { id: value.id, classId: value.classId, path: value.path, binding };
80206
81161
  }
80207
81162
  function resolveVariantWorldKind(classId, classesById) {
80208
81163
  const visited = /* @__PURE__ */ new Set();
@@ -80220,7 +81175,9 @@ function resolveVariantWorldKind(classId, classesById) {
80220
81175
  function isVariantFamilyClassId(classId, classesById) {
80221
81176
  const worldKind = resolveVariantWorldKind(classId, classesById);
80222
81177
  if (worldKind === VARIANT_WORLD_KIND) return true;
80223
- return worldKind === VARIANT_FOLDER_WORLD_KIND;
81178
+ if (worldKind === VARIANT_FOLDER_WORLD_KIND) return true;
81179
+ if (worldKind === LOOKUP_VARIANT_WORLD_KIND) return true;
81180
+ return worldKind === LOOKUP_VARIANT_FOLDER_WORLD_KIND;
80224
81181
  }
80225
81182
  function variantFamilyLabel(classId, classesById) {
80226
81183
  const schemaClass2 = classesById.get(classId);
@@ -80325,7 +81282,7 @@ function assertNoVariantTypePositions(args) {
80325
81282
  function assertNoAuthoredVariantMembers(args) {
80326
81283
  for (const schemaClass2 of args.classesById.values()) {
80327
81284
  const worldKind = schemaClass2.system?.worldKind;
80328
- if (worldKind !== VARIANT_WORLD_KIND && worldKind !== VARIANT_FOLDER_WORLD_KIND) {
81285
+ if (worldKind !== VARIANT_WORLD_KIND && worldKind !== VARIANT_FOLDER_WORLD_KIND && worldKind !== LOOKUP_VARIANT_WORLD_KIND && worldKind !== LOOKUP_VARIANT_FOLDER_WORLD_KIND) {
80329
81286
  continue;
80330
81287
  }
80331
81288
  for (const [schemaKey, memberId] of Object.entries(schemaClass2.schema)) {
@@ -80354,6 +81311,12 @@ function assertOpaqueVariantsValid(args) {
80354
81311
  args.folders.map((folder) => [folder.id, folder])
80355
81312
  );
80356
81313
  const folderPathsByClass = /* @__PURE__ */ new Map();
81314
+ const rootFolderByClass = /* @__PURE__ */ new Map();
81315
+ const valuesById = new Map(
81316
+ (args.values ?? []).flatMap(
81317
+ (value) => isRecordObject(value) && typeof value.id === "string" ? [[value.id, value]] : []
81318
+ )
81319
+ );
80357
81320
  for (const folder of args.folders) {
80358
81321
  const owner = args.classesById.get(folder.classId);
80359
81322
  if (owner === void 0) {
@@ -80362,7 +81325,42 @@ function assertOpaqueVariantsValid(args) {
80362
81325
  );
80363
81326
  }
80364
81327
  assertVariantTargetClass(owner, args, `Variant folder "${folder.id}"`);
80365
- const segments = folder.path.split(VARIANT_FOLDER_PATH_SEPARATOR);
81328
+ const binding = folder.binding ?? null;
81329
+ if (binding !== null) {
81330
+ const collection = args.membersById?.get(binding.collectionMemberId);
81331
+ if (collection === void 0 || collection.kind !== MEMBER_KIND_LIST) {
81332
+ throw new Error(
81333
+ `Lookup variant folder "${folder.id}" binds missing or non-list collection member "${binding.collectionMemberId}".`
81334
+ );
81335
+ }
81336
+ const entry = typeof collection.entryMemberId === "string" ? args.membersById?.get(collection.entryMemberId) : void 0;
81337
+ if (entry === void 0 || entry.kind !== MEMBER_KIND_CLASS || typeof entry.classId !== "string") {
81338
+ throw new Error(
81339
+ `Lookup variant folder "${folder.id}" collection "${binding.collectionMemberId}" must contain Class entries.`
81340
+ );
81341
+ }
81342
+ const collectionValue = valuesById.get(binding.collectionValueId);
81343
+ if (collectionValue === void 0) {
81344
+ throw new Error(
81345
+ `Lookup variant folder "${folder.id}" binds missing collection value "${binding.collectionValueId}".`
81346
+ );
81347
+ }
81348
+ if (Object.hasOwn(collectionValue, "memberId") && collectionValue.memberId !== null && collectionValue.memberId !== void 0 && collectionValue.memberId !== binding.collectionMemberId) {
81349
+ throw new Error(
81350
+ `Lookup variant folder "${folder.id}" binds collection value "${binding.collectionValueId}" from member "${String(collectionValue.memberId)}" instead of collection "${binding.collectionMemberId}".`
81351
+ );
81352
+ }
81353
+ }
81354
+ if (folder.path.length === 0) {
81355
+ const prior = rootFolderByClass.get(folder.classId);
81356
+ if (prior !== void 0) {
81357
+ throw new Error(
81358
+ `Lookup variant folders "${prior}" and "${folder.id}" both enumerate the class root; at most one is allowed per class.`
81359
+ );
81360
+ }
81361
+ rootFolderByClass.set(folder.classId, folder.id);
81362
+ }
81363
+ const segments = folder.path.length === 0 ? [] : folder.path.split(VARIANT_FOLDER_PATH_SEPARATOR);
80366
81364
  for (const segment of segments) {
80367
81365
  assertVariantIdentifier(
80368
81366
  segment,
@@ -80402,7 +81400,7 @@ function assertOpaqueVariantsValid(args) {
80402
81400
  scopeMembers.set(key, members);
80403
81401
  };
80404
81402
  for (const folder of args.folders) {
80405
- const segments = folder.path.split(VARIANT_FOLDER_PATH_SEPARATOR);
81403
+ const segments = folder.path.length === 0 ? [] : folder.path.split(VARIANT_FOLDER_PATH_SEPARATOR);
80406
81404
  segments.forEach((segment, index) => {
80407
81405
  const parentPath = segments.slice(0, index).join(VARIANT_FOLDER_PATH_SEPARATOR);
80408
81406
  const ownPath = segments.slice(0, index + 1).join(VARIANT_FOLDER_PATH_SEPARATOR);
@@ -80449,6 +81447,19 @@ function assertOpaqueVariantsValid(args) {
80449
81447
  );
80450
81448
  }
80451
81449
  scopePath = folder.path;
81450
+ const root = valuesById.get(variant.valueId);
81451
+ const rootClassId = root?.classId;
81452
+ const rootWorldKind = typeof rootClassId === "string" ? resolveVariantWorldKind(rootClassId, args.classesById) : null;
81453
+ const expectedWorldKind = (folder.binding ?? null) === null ? VARIANT_WORLD_KIND : LOOKUP_VARIANT_WORLD_KIND;
81454
+ if (rootWorldKind !== null && rootWorldKind !== expectedWorldKind) {
81455
+ throw new Error(
81456
+ `Variant "${variant.id}" in folder "${folder.id}" roots world kind "${rootWorldKind}"; this folder requires "${expectedWorldKind}".`
81457
+ );
81458
+ }
81459
+ } else if (rootFolderByClass.has(variant.classId)) {
81460
+ throw new Error(
81461
+ `Variant "${variant.id}" is declared at the class root beside root lookup folder "${rootFolderByClass.get(variant.classId)}"; the root is either authored or collection-bound, never both.`
81462
+ );
80452
81463
  }
80453
81464
  claimScope(
80454
81465
  variant.classId,
@@ -80467,13 +81478,15 @@ function assertVariantTargetClass(owner, args, describe2) {
80467
81478
  );
80468
81479
  }
80469
81480
  }
80470
- var VARIANT_WORLD_KIND, VARIANT_FOLDER_WORLD_KIND, RESERVED_VARIANT_NAME, VARIANT_FOLDER_PATH_SEPARATOR, MEMBER_KIND_DICTIONARY, MEMBER_KIND_LIST, MEMBER_KIND_CLASS, MEMBER_KIND_LOOKUP, MEMBER_KIND_VARIANT, variantScopeKey, variantFolderScopeKey;
81481
+ var VARIANT_WORLD_KIND, VARIANT_FOLDER_WORLD_KIND, LOOKUP_VARIANT_WORLD_KIND, LOOKUP_VARIANT_FOLDER_WORLD_KIND, RESERVED_VARIANT_NAME, VARIANT_FOLDER_PATH_SEPARATOR, MEMBER_KIND_DICTIONARY, MEMBER_KIND_LIST, MEMBER_KIND_CLASS, MEMBER_KIND_LOOKUP, MEMBER_KIND_VARIANT, variantScopeKey, variantFolderScopeKey;
80471
81482
  var init_projectVariantValidation = __esm({
80472
81483
  "../convex/projectVariantValidation.ts"() {
80473
81484
  "use strict";
80474
81485
  init_src();
80475
81486
  VARIANT_WORLD_KIND = "variant";
80476
81487
  VARIANT_FOLDER_WORLD_KIND = "variantFolder";
81488
+ LOOKUP_VARIANT_WORLD_KIND = "lookupVariant";
81489
+ LOOKUP_VARIANT_FOLDER_WORLD_KIND = "lookupVariantFolder";
80477
81490
  RESERVED_VARIANT_NAME = "Base";
80478
81491
  VARIANT_FOLDER_PATH_SEPARATOR = "/";
80479
81492
  MEMBER_KIND_DICTIONARY = 5;
@@ -80873,7 +81886,9 @@ function assertProjectInterfaceDocumentValid(view, options = {}) {
80873
81886
  variants: (view.variants ?? []).map(readVariantRecord),
80874
81887
  folders: (view.variantFolders ?? []).map(readVariantFolderRecord),
80875
81888
  classesById,
80876
- objectWorldKind: NEO_OBJECT_WORLD_KIND
81889
+ objectWorldKind: NEO_OBJECT_WORLD_KIND,
81890
+ membersById,
81891
+ values: view.values
80877
81892
  });
80878
81893
  }
80879
81894
  for (const member of members) {
@@ -90083,7 +91098,13 @@ function assertStagedWorldContentLayerBindingsValid(current, projected, changes)
90083
91098
  validateWorldContentSidecars({
90084
91099
  classes,
90085
91100
  relations,
90086
- values: projected.values
91101
+ values: projected.values,
91102
+ variants: worldPlacementVariantBindings({
91103
+ variants: projected.variants ?? [],
91104
+ variantFolders: projected.variantFolders ?? [],
91105
+ members: projected.members,
91106
+ values: projected.values
91107
+ })
90087
91108
  });
90088
91109
  validateWorldLayerLinkClassTargets({
90089
91110
  classes,
@@ -90235,7 +91256,8 @@ function assertStagedProjectInterfaceValid(projected, changes) {
90235
91256
  constructors: projected.constructors ?? [],
90236
91257
  variants: projected.variants ?? [],
90237
91258
  variantFolders: projected.variantFolders ?? [],
90238
- interfaces: projected.interfaces
91259
+ interfaces: projected.interfaces,
91260
+ values: projected.values
90239
91261
  },
90240
91262
  {
90241
91263
  validateMemberSchemaKeys: relevant.some(
@@ -90555,6 +91577,15 @@ function assertStagedVariantMemberValuesValid(projected, changes) {
90555
91577
  const variantsById = new Map(
90556
91578
  (projected.variants ?? []).map((variant) => [variant.id, variant])
90557
91579
  );
91580
+ const foldersById = new Map(
91581
+ (projected.variantFolders ?? []).map((folder) => [folder.id, folder])
91582
+ );
91583
+ const membersById = new Map(
91584
+ projected.members.map((member) => [member.id, member])
91585
+ );
91586
+ const valuesById = new Map(
91587
+ projected.values.map((value) => [value.id, value])
91588
+ );
90558
91589
  const graded = [];
90559
91590
  for (const member of variantMembers) {
90560
91591
  const declaration = substituteMemberForListenerGrading(member, projected);
@@ -90594,7 +91625,11 @@ function assertStagedVariantMemberValuesValid(projected, changes) {
90594
91625
  declaration: entry.declaration,
90595
91626
  owner: entry.label,
90596
91627
  classesById,
90597
- variantsById
91628
+ variantsById,
91629
+ foldersById,
91630
+ membersById,
91631
+ valuesById,
91632
+ values: projected.values
90598
91633
  });
90599
91634
  }
90600
91635
  }
@@ -90625,7 +91660,16 @@ function assertVariantMemberTargetValid(args) {
90625
91660
  }
90626
91661
  }
90627
91662
  function assertVariantReferenceValid(args) {
90628
- const { value, declaration, owner, classesById, variantsById } = args;
91663
+ const {
91664
+ value,
91665
+ declaration,
91666
+ owner,
91667
+ classesById,
91668
+ variantsById,
91669
+ foldersById,
91670
+ membersById,
91671
+ valuesById
91672
+ } = args;
90629
91673
  const target = variantMemberTargetTypeInfo(declaration);
90630
91674
  if (target === null) return;
90631
91675
  if (value === null) {
@@ -90659,7 +91703,18 @@ function assertVariantReferenceValid(args) {
90659
91703
  );
90660
91704
  }
90661
91705
  }
90662
- if (value.variantId === null) return;
91706
+ const declaredValueType = variantMemberValueTypeInfo(declaration);
91707
+ if (value.variantId === null) {
91708
+ if (declaredValueType !== null) {
91709
+ throw new Error(
91710
+ `${owner} declares NeoLookupVariant and must select a lookup variant rather than the base class.`
91711
+ );
91712
+ }
91713
+ if (value.rowValueId !== null && value.rowValueId !== void 0) {
91714
+ throw new Error(`${owner} cannot bind a row to the base class.`);
91715
+ }
91716
+ return;
91717
+ }
90663
91718
  const variant = variantsById.get(value.variantId);
90664
91719
  if (variant === void 0) {
90665
91720
  throw new Error(
@@ -90672,6 +91727,58 @@ function assertVariantReferenceValid(args) {
90672
91727
  `${owner} selects class "${selectedClass.name}" but names variant "${variant.name}" of "${ownerClassName}". Variant lookup is not virtual, so a variant belongs to exactly one class (P67 \xA74.3).`
90673
91728
  );
90674
91729
  }
91730
+ const folder = variant.folderId === null ? null : foldersById.get(variant.folderId) ?? null;
91731
+ const binding = folder?.binding ?? null;
91732
+ if (declaredValueType !== null) {
91733
+ if (binding === null) {
91734
+ throw new Error(
91735
+ `${owner} declares NeoLookupVariant but selects plain variant "${variant.name}".`
91736
+ );
91737
+ }
91738
+ if (value.rowValueId !== null && value.rowValueId !== void 0) {
91739
+ throw new Error(
91740
+ `${owner} keeps its lookup row unbound, so rowValueId must be null.`
91741
+ );
91742
+ }
91743
+ if (declaredValueType.type === 7 /* Class */ && lookupBindingEntryClassId(binding, membersById) !== declaredValueType.classId) {
91744
+ throw new Error(
91745
+ `${owner} lookup value type does not match variant folder "${folder?.path ?? ""}".`
91746
+ );
91747
+ }
91748
+ return;
91749
+ }
91750
+ if (binding === null) {
91751
+ if (value.rowValueId !== null && value.rowValueId !== void 0) {
91752
+ throw new Error(
91753
+ `${owner} binds a row to plain variant "${variant.name}".`
91754
+ );
91755
+ }
91756
+ return;
91757
+ }
91758
+ if (value.rowValueId === null || value.rowValueId === void 0) {
91759
+ throw new Error(
91760
+ `${owner} selects lookup variant "${variant.name}" and must bind one collection row.`
91761
+ );
91762
+ }
91763
+ const collection = membersById.get(binding.collectionMemberId);
91764
+ const collectionValue = valuesById.get(binding.collectionValueId);
91765
+ if (!isMemberListBase(collection) || collectionValue === void 0 || !valuesById.has(value.rowValueId) || !listEntryIdsForValue(collection, collectionValue, args.values).includes(
91766
+ value.rowValueId
91767
+ )) {
91768
+ throw new Error(
91769
+ `${owner} binds row "${value.rowValueId}", which is not in lookup variant folder "${folder?.path ?? ""}".`
91770
+ );
91771
+ }
91772
+ }
91773
+ function variantMemberValueTypeInfo(declaration) {
91774
+ const valueType = Reflect.get(declaration, "valueTypeInfo");
91775
+ return isNSTypeInfo(valueType) ? valueType : null;
91776
+ }
91777
+ function lookupBindingEntryClassId(binding, membersById) {
91778
+ const collection = membersById.get(binding.collectionMemberId);
91779
+ if (!isMemberListBase(collection)) return null;
91780
+ const entry = membersById.get(collection.entryMemberId);
91781
+ return isMemberClassBase(entry) ? entry.classId : null;
90675
91782
  }
90676
91783
  function variantMemberTargetTypeInfo(declaration) {
90677
91784
  const target = Reflect.get(declaration, "targetTypeInfo");
@@ -91054,6 +92161,43 @@ function computeWorkspaceStatus(workspace, options) {
91054
92161
  }
91055
92162
  }
91056
92163
  }
92164
+ const resolvedVariantFolderPaths = resolveRootPathVariantFolderCollectionValuesV4({
92165
+ manifest,
92166
+ state: rootPathResolutionState,
92167
+ registry: valueLowerRegistry,
92168
+ siteForFolder: (folderId) => {
92169
+ const span2 = sourceByKey.get(
92170
+ recordStateKey("variant-folder", folderId)
92171
+ )?.span;
92172
+ return span2 === void 0 ? null : { uri: span2.path, start: span2.start };
92173
+ }
92174
+ });
92175
+ if (resolvedVariantFolderPaths.size > 0) {
92176
+ manifest = {
92177
+ ...manifest,
92178
+ variantFolders: manifest.variantFolders.map((folder) => {
92179
+ const resolvedId = resolvedVariantFolderPaths.get(folder.id);
92180
+ return resolvedId === void 0 || folder.binding === null ? folder : {
92181
+ ...folder,
92182
+ binding: { ...folder.binding, collectionValueId: resolvedId }
92183
+ };
92184
+ })
92185
+ };
92186
+ for (const [folderId, resolvedId] of resolvedVariantFolderPaths) {
92187
+ const document = documentsByKey.get(
92188
+ recordStateKey("variant-folder", folderId)
92189
+ );
92190
+ if (document !== void 0 && isObjectRecord2(document.data.binding)) {
92191
+ document.data.binding.collectionValueId = resolvedId;
92192
+ }
92193
+ const record3 = records2.find(
92194
+ (candidate) => candidate.recordKind === "variant-folder" && candidate.recordId === folderId
92195
+ );
92196
+ if (record3 !== void 0 && isObjectRecord2(record3.fileFields.binding)) {
92197
+ record3.fileFields.binding.collectionValueId = resolvedId;
92198
+ }
92199
+ }
92200
+ }
91057
92201
  const resolvedDefaultKeys = resolveKeyReferenceMemberDefaultsV4({
91058
92202
  manifest,
91059
92203
  state: rootPathResolutionState,
@@ -93967,8 +95111,14 @@ function lowerVariantRootRow(context, binding, rootMember, source, rows, localiz
93967
95111
  rows,
93968
95112
  localizedTexts,
93969
95113
  void 0,
93970
- /* @__PURE__ */ new Map([
93971
- [parameter4.id, inferredGenericClassBinding(binding.targetClassId)]
95114
+ new Map([
95115
+ [parameter4.id, inferredGenericClassBinding(binding.targetClassId)],
95116
+ ...binding.valueClassId === null || variantClass.genericParameters[1] === void 0 ? [] : [
95117
+ [
95118
+ variantClass.genericParameters[1].id,
95119
+ inferredGenericClassBinding(binding.valueClassId)
95120
+ ]
95121
+ ]
93972
95122
  ]),
93973
95123
  binding.initializer
93974
95124
  );
@@ -94081,10 +95231,10 @@ function variantValueRecord(binding, fileFields, source) {
94081
95231
  };
94082
95232
  }
94083
95233
  function variantRootMember(context, binding) {
94084
- const variantClass = context.classesByName.get(NEO_VARIANT_TYPE_NAME);
95234
+ const variantClass = context.classesByName.get(binding.variantTypeName);
94085
95235
  if (variantClass === void 0) {
94086
95236
  throw new Error(
94087
- `Variant ${binding.label} cannot be lowered: this project has no seeded ${NEO_VARIANT_TYPE_NAME} class. Run \`neo pull\` against a project on the current platform schema.`
95237
+ `Variant ${binding.label} cannot be lowered: this project has no seeded ${binding.variantTypeName} class. Run \`neo pull\` against a project on the current platform schema.`
94088
95238
  );
94089
95239
  }
94090
95240
  const parameter4 = variantClass.genericParameters[0];
@@ -94115,6 +95265,12 @@ function variantRootMember(context, binding) {
94115
95265
  [parameter4.id]: {
94116
95266
  kind: "member",
94117
95267
  memberId: inferredGenericClassBinding(binding.targetClassId)
95268
+ },
95269
+ ...binding.valueClassId === null || variantClass.genericParameters[1] === void 0 ? {} : {
95270
+ [variantClass.genericParameters[1].id]: {
95271
+ kind: "member",
95272
+ memberId: inferredGenericClassBinding(binding.valueClassId)
95273
+ }
94118
95274
  }
94119
95275
  },
94120
95276
  isStatic: false,
@@ -109477,7 +110633,7 @@ var init_registry2 = __esm({
109477
110633
  PROJECT_SCHEMA_CONTRACT = Object.freeze({
109478
110634
  formatVersion: 3,
109479
110635
  contractVersion: "3.12",
109480
- cliVersion: "0.31.2",
110636
+ cliVersion: "0.31.3",
109481
110637
  projectFileUploadBatchSize: 32,
109482
110638
  documentRecords: {
109483
110639
  member: {
@@ -109519,6 +110675,7 @@ var init_registry2 = __esm({
109519
110675
  "collectionValueId",
109520
110676
  "declaredTypeInfo",
109521
110677
  "targetTypeInfo",
110678
+ "variantValueTypeInfo",
109522
110679
  "dialogueGroupId",
109523
110680
  "code",
109524
110681
  "setterCode",
@@ -109650,10 +110807,10 @@ var init_registry2 = __esm({
109650
110807
  "variant-folder": {
109651
110808
  // P67 §3.3. A folder's whole content is its path, which is what lets a
109652
110809
  // rename be a one-record edit that orphans nothing.
109653
- authored: ["id", "classId", "path"],
110810
+ authored: ["id", "classId", "path", "binding"],
109654
110811
  derived: [],
109655
110812
  volatile: ["projectId", "createdAt", "updatedAt"],
109656
- normalization: {}
110813
+ normalization: { binding: "missingIsNull" }
109657
110814
  },
109658
110815
  interface: {
109659
110816
  authored: [
@@ -110169,7 +111326,9 @@ var init_registry2 = __esm({
110169
111326
  "AnimationSegmentTrack",
110170
111327
  "SpriteAnimationSegmentTrack",
110171
111328
  "Variant",
110172
- "VariantFolder"
111329
+ "VariantFolder",
111330
+ "LookupVariant",
111331
+ "LookupVariantFolder"
110173
111332
  ],
110174
111333
  NeoTextureType: [
110175
111334
  "Default",
@@ -116069,7 +117228,7 @@ There is no --keep-current: preserving current semantic state defines flatten.
116069
117228
  async function main() {
116070
117229
  const args = parseArgs(process.argv.slice(2));
116071
117230
  if (args.command === "--version") {
116072
- console.log("0.31.2");
117231
+ console.log("0.31.3");
116073
117232
  return;
116074
117233
  }
116075
117234
  if (args.command === null || args.command === "help" || args.command === "--help" || args.command === "-h") {