@neocompose/cli 0.76.3 → 0.77.0

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
@@ -4183,7 +4183,10 @@ function isMemberEnum(value) {
4183
4183
  if (!isWithId(value)) return false;
4184
4184
  return isWithId(value);
4185
4185
  }
4186
- function isMemberLookupBase(value) {
4186
+ function isLookupSelection(selection2) {
4187
+ return selection2 === void 0 || selection2 === 0 /* Single */ || selection2 === 1 /* Multi */;
4188
+ }
4189
+ function isMemberCollectionLookupBase(value) {
4187
4190
  const v = asMemberBaseForKind(value, 9 /* Lookup */);
4188
4191
  if (v === null) return false;
4189
4192
  if (typeof v.collectionMemberId !== "string") return false;
@@ -4194,7 +4197,39 @@ function isMemberLookupBase(value) {
4194
4197
  if (v.declaredTypeInfo !== void 0 && v.declaredTypeInfo !== null && !isNSTypeInfo(v.declaredTypeInfo)) {
4195
4198
  return false;
4196
4199
  }
4197
- return v.selection === void 0 || v.selection === 0 /* Single */ || v.selection === 1 /* Multi */;
4200
+ return isLookupSelection(v.selection);
4201
+ }
4202
+ function isMemberTypeLookupBase(value) {
4203
+ const v = asMemberBaseForKind(value, 9 /* Lookup */);
4204
+ if (v === null) return false;
4205
+ if (v.collectionMemberId !== void 0) return false;
4206
+ if (v.collectionValueId !== void 0) return false;
4207
+ if (!isLookupTargetTypeInfo(v.declaredTypeInfo)) return false;
4208
+ return isLookupSelection(v.selection);
4209
+ }
4210
+ function isLookupTargetTypeInfo(value) {
4211
+ if (!isNSTypeInfo(value)) return false;
4212
+ return value.type === 7 /* Class */ || value.type === 21 /* Generic */;
4213
+ }
4214
+ function lookupShapeViolation(value) {
4215
+ const v = asMemberBaseForKind(value, 9 /* Lookup */);
4216
+ if (v === null) return null;
4217
+ if (v.extendsMemberId !== void 0) return null;
4218
+ if (v.collectionMemberId !== void 0) return null;
4219
+ const id2 = String(v.id);
4220
+ if (v.declaredTypeInfo === void 0 || v.declaredTypeInfo === null) {
4221
+ return `Lookup member \`${id2}\` has neither a collection nor a declared type.`;
4222
+ }
4223
+ if (v.collectionValueId !== void 0) {
4224
+ return `Lookup member \`${id2}\` names a collection value without a collection.`;
4225
+ }
4226
+ if (!isLookupTargetTypeInfo(v.declaredTypeInfo)) {
4227
+ return `Lookup member \`${id2}\` has no collection, so its declared type must be a class or generic parameter.`;
4228
+ }
4229
+ return null;
4230
+ }
4231
+ function isMemberLookupBase(value) {
4232
+ return isMemberCollectionLookupBase(value) || isMemberTypeLookupBase(value);
4198
4233
  }
4199
4234
  function isMemberLookup(value) {
4200
4235
  if (!isMemberLookupBase(value)) return false;
@@ -9053,6 +9088,13 @@ function resolveGenericScope(classId, classes, draft) {
9053
9088
  }
9054
9089
  return scope;
9055
9090
  }
9091
+ function genericParamClassConstraintId(typeInfo, classById) {
9092
+ const constraint = classById.get(typeInfo.ownerClassId)?.genericParams?.find(
9093
+ (param) => param.id === typeInfo.genericParamId
9094
+ )?.constraint;
9095
+ if (!constraint || !isClassGenericConstraint(constraint)) return null;
9096
+ return constraint.classId;
9097
+ }
9056
9098
  function findGenericParamDeclaration(genericParamId, classes) {
9057
9099
  for (const schemaClass2 of classes) {
9058
9100
  for (const param of schemaClass2.genericParams ?? []) {
@@ -17581,6 +17623,15 @@ function neoSourceTypeText(type) {
17581
17623
  const args = type.typeArguments.length > 0 ? `<${type.typeArguments.map(neoSourceTypeText).join(", ")}>` : "";
17582
17624
  return `${type.name}${args}${type.nullable ? "?" : ""}`;
17583
17625
  }
17626
+ function referenceReadType(type) {
17627
+ const target = type.name === "Reference" ? type.typeArguments[0] : void 0;
17628
+ if (target !== void 0) return { ...target, nullable: type.nullable };
17629
+ const element = type.name === "Set" ? type.typeArguments[0] : void 0;
17630
+ if (element?.name !== "Reference" || element.typeArguments.length === 0) {
17631
+ return type;
17632
+ }
17633
+ return { ...type, typeArguments: [referenceReadType(element)] };
17634
+ }
17584
17635
  function isNeoSourceAccessModifier(value) {
17585
17636
  return value === "public" || value === "protected" || value === "private";
17586
17637
  }
@@ -25852,6 +25903,13 @@ function ownershipForMemberStorage(ctx, memberId) {
25852
25903
  }
25853
25904
  function sourceForMemberRecord(ctx, memberId, member, inheritedOwnership) {
25854
25905
  if (isMemberLookupBase(member)) {
25906
+ if (member.collectionMemberId === void 0) {
25907
+ return {
25908
+ memberId,
25909
+ typeInfo: memberTypeInfoForCodegen(ctx, member),
25910
+ ownership: null
25911
+ };
25912
+ }
25855
25913
  const entryMember = resolveLookupEntryMember(ctx, member);
25856
25914
  const ownership = ownershipForMemberStorage(ctx, member.collectionMemberId) ?? inheritedOwnership;
25857
25915
  return {
@@ -25913,6 +25971,10 @@ function memberTypeInfoForCodegen(ctx, member) {
25913
25971
  return { type: 7 /* Class */, required: required2, classId: member.classId };
25914
25972
  }
25915
25973
  if (isMemberLookupBase(member)) {
25974
+ if (member.collectionMemberId === void 0) {
25975
+ const entryTypeInfo2 = { ...member.declaredTypeInfo, required: true };
25976
+ return isMultiSelectionMember(member) ? { type: 9 /* Lookup */, required: required2, entryTypeInfo: entryTypeInfo2 } : withRequiredTypeInfo(entryTypeInfo2, false);
25977
+ }
25916
25978
  const entryMember = resolveLookupEntryMember(ctx, member);
25917
25979
  if (entryMember === null) return { type: 0 /* Null */, required: false };
25918
25980
  const entryTypeInfo = withRequiredTypeInfo(
@@ -26058,6 +26120,15 @@ function lookupTargetClassProjection(ctx, member, env) {
26058
26120
  constructedSuffix: constructedTypeArgumentsSuffix(ctx, bindingMember, true, env) ?? ""
26059
26121
  };
26060
26122
  }
26123
+ function lookupTargetsClass(ctx, member) {
26124
+ if (!isMemberLookupBase(member)) return false;
26125
+ if (member.collectionMemberId === void 0) return true;
26126
+ const entryMember = resolveLookupEntryMember(ctx, member);
26127
+ return entryMember !== null && isMemberClassBase(entryMember);
26128
+ }
26129
+ function lookupHasRuntimeOwnership(member) {
26130
+ return isMemberLookupBase(member) && member.collectionMemberId === void 0;
26131
+ }
26061
26132
  function lookupNarrowsThroughGenericParam(member) {
26062
26133
  return lookupDeclaredTypeInfo(member)?.type === 21 /* Generic */;
26063
26134
  }
@@ -26094,6 +26165,7 @@ function lookupTargetValueType(ctx, member, saved, env) {
26094
26165
  }
26095
26166
  function lookupTargetSaved(ctx, member) {
26096
26167
  if (!isMemberLookupBase(member)) return null;
26168
+ if (member.collectionMemberId === void 0) return false;
26097
26169
  const storages = ctx.storageResolver.effectiveStorages(
26098
26170
  member.collectionMemberId
26099
26171
  );
@@ -26104,6 +26176,7 @@ function lookupTargetSaved(ctx, member) {
26104
26176
  }
26105
26177
  function resolveLookupEntryMember(ctx, member) {
26106
26178
  if (!isMemberLookupBase(member)) return null;
26179
+ if (member.collectionMemberId === void 0) return null;
26107
26180
  const collectionMember = resolveMemberById(
26108
26181
  ctx,
26109
26182
  member.collectionMemberId,
@@ -27433,7 +27506,7 @@ function validateDocumentAccessModifiers(ctx) {
27433
27506
  });
27434
27507
  }
27435
27508
  }
27436
- if (isMemberLookupBase(member)) {
27509
+ if (isMemberCollectionLookupBase(member)) {
27437
27510
  validateLookupTargetAccess({
27438
27511
  lookup: member,
27439
27512
  membersById,
@@ -30400,6 +30473,12 @@ function bindConstructorArgumentNames(className, declared, argumentNames, pos) {
30400
30473
  return parameter4.name;
30401
30474
  });
30402
30475
  }
30476
+ function localOwnership(initial) {
30477
+ if (initial.entryWritability !== void 0 && (initial.type.kind === "named" || initial.type.kind === "typeParameter")) {
30478
+ return initial.entryWritability;
30479
+ }
30480
+ return initial.writability ?? "local" /* Local */;
30481
+ }
30403
30482
  function scopeVariable(name, value, sourceType, writability, ownership, writeRoot, readonlyBinding) {
30404
30483
  return {
30405
30484
  name,
@@ -32428,7 +32507,7 @@ var init_strict_resolver = __esm({
32428
32507
  local,
32429
32508
  localType,
32430
32509
  "local" /* Local */,
32431
- initial.writability ?? "local" /* Local */,
32510
+ localOwnership(initial),
32432
32511
  this.localWriteTargetRoot(initial, scope)
32433
32512
  )
32434
32513
  );
@@ -35681,6 +35760,7 @@ var init_strict_resolver = __esm({
35681
35760
  } : {}
35682
35761
  };
35683
35762
  }
35763
+ const writability = member.writability ? toWritability(member.writability, this.writesUserData) : void 0;
35684
35764
  return {
35685
35765
  pointer: {
35686
35766
  type: "staticMember" /* StaticMember */,
@@ -35688,8 +35768,10 @@ var init_strict_resolver = __esm({
35688
35768
  },
35689
35769
  type: member.type,
35690
35770
  symbol: member,
35691
- ...member.writability ? {
35692
- writability: toWritability(member.writability, this.writesUserData)
35771
+ ...writability ? { writability } : {},
35772
+ ...member.lookup ? {
35773
+ entryWritability: this.lookupEntryWritability(member, writability),
35774
+ entryUserData: this.lookupEntryUserData(member)
35693
35775
  } : {}
35694
35776
  };
35695
35777
  }
@@ -35989,7 +36071,7 @@ var init_strict_resolver = __esm({
35989
36071
  const readonlySlot = member.readonlyDeclaration === true;
35990
36072
  const storageWritability = this.memberWritability(member, receiver);
35991
36073
  const writability = readonlySlot ? "readOnly" /* ReadOnly */ : storageWritability;
35992
- const entryWritability = member.lookup ? this.lookupEntryWritability(member, receiver) : readonlySlot ? storageWritability : void 0;
36074
+ const entryWritability = member.lookup ? this.lookupEntryWritability(member, receiver.writability) : readonlySlot ? storageWritability : void 0;
35993
36075
  const userData = this.memberUserData(member, receiver);
35994
36076
  const entryUserData = member.lookup ? this.lookupEntryUserData(member) : readonlySlot && userData;
35995
36077
  const lookupWire = member.lookup?.multiselect === true && memberType2.kind === "set" ? {
@@ -35999,7 +36081,7 @@ var init_strict_resolver = __esm({
35999
36081
  { ...memberType2.elementType, nullable: false },
36000
36082
  this.project
36001
36083
  ),
36002
- collectionMemberId: member.lookup.collectionMemberId,
36084
+ ...member.lookup.collectionMemberId !== void 0 ? { collectionMemberId: member.lookup.collectionMemberId } : {},
36003
36085
  ...member.lookup.collectionValueId !== void 0 ? { collectionValueId: member.lookup.collectionValueId } : {}
36004
36086
  } : void 0;
36005
36087
  return {
@@ -36123,16 +36205,18 @@ var init_strict_resolver = __esm({
36123
36205
  }
36124
36206
  lookupEntryUserData(member) {
36125
36207
  if (!member.lookup) return false;
36126
- const collectionWritability = member.lookup.collectionWritability ?? this.project.symbolById.get(member.lookup.collectionMemberId)?.writability;
36208
+ const collectionMemberId = member.lookup.collectionMemberId;
36209
+ if (collectionMemberId === void 0) return false;
36210
+ const collectionWritability = member.lookup.collectionWritability ?? this.project.symbolById.get(collectionMemberId)?.writability;
36127
36211
  return collectionWritability === "user";
36128
36212
  }
36129
- lookupEntryWritability(member, receiver) {
36213
+ lookupEntryWritability(member, receiverWritability) {
36130
36214
  if (!member.lookup) return "readOnly" /* ReadOnly */;
36131
- const target = this.project.symbolById.get(
36132
- member.lookup.collectionMemberId
36133
- );
36215
+ const collectionMemberId = member.lookup.collectionMemberId;
36216
+ if (collectionMemberId === void 0) return "runtime" /* Runtime */;
36217
+ const target = this.project.symbolById.get(collectionMemberId);
36134
36218
  const targetWritability = member.lookup.collectionWritability ? toWritability(member.lookup.collectionWritability, this.writesUserData) : target?.writability ? toWritability(target.writability, this.writesUserData) : target?.writable ? "runtime" /* Runtime */ : "readOnly" /* ReadOnly */;
36135
- if (receiver.writability === "immutable" /* Immutable */) {
36219
+ if (receiverWritability === "immutable" /* Immutable */) {
36136
36220
  if (targetWritability === "save" /* Save */) {
36137
36221
  return "immutableToSaveLookup" /* ImmutableToSaveLookup */;
36138
36222
  }
@@ -46865,6 +46949,8 @@ function matchingDeclaredConstructor(declaration, argumentNames) {
46865
46949
  return neoScriptNamedCallSignature(declaration.constructors, argumentNames) ?? null;
46866
46950
  }
46867
46951
  function semanticType(type) {
46952
+ const read = referenceReadType(type);
46953
+ if (read !== type) return semanticType(read);
46868
46954
  return {
46869
46955
  name: type.name,
46870
46956
  nullable: type.nullable,
@@ -49002,6 +49088,92 @@ var init_project_source_script_compiler = __esm({
49002
49088
  }
49003
49089
  });
49004
49090
 
49091
+ // ../packages/neoscript-language/src/project-source-reference-spelling.ts
49092
+ function oldReferenceSpelling(member) {
49093
+ if (member.kind !== "field") return null;
49094
+ const settings = member.annotations.find(
49095
+ (entry) => entry.name === "settings"
49096
+ );
49097
+ if (!settings?.arguments.some((entry) => entry.name === "collection")) {
49098
+ return null;
49099
+ }
49100
+ if (referenceReadType(member.type) !== member.type) return null;
49101
+ const spelled = neoSourceTypeText({ ...member.type, nullable: false });
49102
+ const element = member.type.name === "Set" || member.type.name === "List" ? member.type.typeArguments[0] : void 0;
49103
+ const fixed = element === void 0 ? `Reference<${spelled}>` : `Set<Reference<${neoSourceTypeText(element)}>>`;
49104
+ return { settings, spelled, fixed };
49105
+ }
49106
+ function validateReferenceSpelling(documents, diagnostics) {
49107
+ for (const [uri, document] of documents) {
49108
+ for (const declaration of document.declarations) {
49109
+ if (declaration.kind !== "class") continue;
49110
+ for (const member of declaration.members) {
49111
+ const old = oldReferenceSpelling(member);
49112
+ if (old === null) continue;
49113
+ diagnostics.push({
49114
+ uri,
49115
+ range: member.type.range,
49116
+ severity: "error",
49117
+ source: "neo-project",
49118
+ code: LOOKUP_REFERENCE_SPELLING_CODE,
49119
+ message: `Lookup member ${declaration.name}.${member.name} declares collection: on ${old.spelled}. Spell the type ${old.fixed}.`
49120
+ });
49121
+ }
49122
+ }
49123
+ }
49124
+ }
49125
+ function projectReferenceSpellingQuickFixes(analysis, document, diagnostic) {
49126
+ if (diagnostic.code !== LOOKUP_REFERENCE_SPELLING_CODE) return [];
49127
+ const source = analysis.documents.get(document.uri);
49128
+ for (const declaration of source?.declarations ?? []) {
49129
+ if (declaration.kind !== "class") continue;
49130
+ for (const member of declaration.members) {
49131
+ if (!sameRange(member.type.range, diagnostic.range)) continue;
49132
+ const old = oldReferenceSpelling(member);
49133
+ if (old === null) return [];
49134
+ const fixed = `${old.fixed}${member.type.nullable ? "?" : ""}`;
49135
+ const edits = [{ range: member.type.range, newText: fixed }];
49136
+ const selection2 = selectionArgumentRange(old.settings);
49137
+ if (selection2 !== null) edits.push({ range: selection2, newText: "" });
49138
+ return [
49139
+ {
49140
+ title: `Change to ${fixed}`,
49141
+ kind: "quickfix",
49142
+ diagnostics: [diagnostic],
49143
+ edit: { changes: { [document.uri]: edits } },
49144
+ preferred: true
49145
+ }
49146
+ ];
49147
+ }
49148
+ }
49149
+ return [];
49150
+ }
49151
+ function selectionArgumentRange(settings) {
49152
+ const args = settings.arguments;
49153
+ const index = args.findIndex((entry) => entry.name === "selection");
49154
+ if (index < 0) return null;
49155
+ const previous = args[index - 1];
49156
+ if (previous !== void 0) {
49157
+ return { start: previous.range.end, end: args[index].range.end };
49158
+ }
49159
+ const next = args[index + 1];
49160
+ return {
49161
+ start: args[index].range.start,
49162
+ end: next?.range.start ?? args[index].range.end
49163
+ };
49164
+ }
49165
+ function sameRange(left, right) {
49166
+ return left.start.line === right.start.line && left.start.character === right.start.character && left.end.line === right.end.line && left.end.character === right.end.character;
49167
+ }
49168
+ var LOOKUP_REFERENCE_SPELLING_CODE;
49169
+ var init_project_source_reference_spelling = __esm({
49170
+ "../packages/neoscript-language/src/project-source-reference-spelling.ts"() {
49171
+ "use strict";
49172
+ init_project_source_ast();
49173
+ LOOKUP_REFERENCE_SPELLING_CODE = "lookup-reference-spelling";
49174
+ }
49175
+ });
49176
+
49005
49177
  // ../packages/neoscript-language/src/project-source-type-checker.ts
49006
49178
  function validateProjectSourceTypes(documents) {
49007
49179
  const diagnostics = [];
@@ -49012,6 +49184,7 @@ function validateProjectSourceTypes(documents) {
49012
49184
  validateAllTypeApplications(documents, environment, diagnostics);
49013
49185
  validateMembers(environment, diagnostics);
49014
49186
  validateLifecycleHooks(environment, diagnostics);
49187
+ validateReferenceSpelling(documents, diagnostics);
49015
49188
  return diagnostics;
49016
49189
  }
49017
49190
  function createEnvironment2(documents) {
@@ -50703,6 +50876,8 @@ function composeSubstitution(inner, outer) {
50703
50876
  );
50704
50877
  }
50705
50878
  function typeShape2(type, substitution = /* @__PURE__ */ new Map()) {
50879
+ const read = referenceReadType(type);
50880
+ if (read !== type) return typeShape2(read, substitution);
50706
50881
  const replacement = type.typeArguments.length === 0 ? substitution.get(type.name) : void 0;
50707
50882
  if (replacement) {
50708
50883
  return {
@@ -50768,6 +50943,7 @@ var init_project_source_type_checker = __esm({
50768
50943
  "use strict";
50769
50944
  init_language_spec();
50770
50945
  init_project_source_ast();
50946
+ init_project_source_reference_spelling();
50771
50947
  init_lexer();
50772
50948
  init_executable_parser();
50773
50949
  init_strict_compile_error();
@@ -51202,6 +51378,7 @@ function collectDeclarationSymbols(uri, document, declaration, symbols, diagnost
51202
51378
  }
51203
51379
  }
51204
51380
  for (const member of declaration.members) {
51381
+ const readType = referenceReadType(member.type);
51205
51382
  collectSymbol(
51206
51383
  uri,
51207
51384
  "member",
@@ -51212,10 +51389,14 @@ function collectDeclarationSymbols(uri, document, declaration, symbols, diagnost
51212
51389
  symbols,
51213
51390
  diagnostics,
51214
51391
  void 0,
51215
- member.type.name,
51392
+ readType.name,
51216
51393
  member.kind === "function",
51217
51394
  member.docsText,
51218
- member.modifiers.includes("static")
51395
+ member.modifiers.includes("static"),
51396
+ readType === member.type ? void 0 : {
51397
+ storage: neoSourceTypeText(member.type),
51398
+ readsAs: neoSourceTypeText(readType)
51399
+ }
51219
51400
  );
51220
51401
  if (member.kind === "function") {
51221
51402
  for (const parameter4 of member.parameters) {
@@ -51313,7 +51494,7 @@ function collectFlowSymbols(uri, content, ownerName, scopeRange, symbols, diagno
51313
51494
  }
51314
51495
  }
51315
51496
  }
51316
- function collectSymbol(uri, kind, name, range2, annotations, ownerName, symbols, diagnostics, scopeRange, detail, callable2, documentation, staticMember) {
51497
+ function collectSymbol(uri, kind, name, range2, annotations, ownerName, symbols, diagnostics, scopeRange, detail, callable2, documentation, staticMember, reference2) {
51317
51498
  const memberLabel3 = kind === "member" || kind === "enumOption" || kind === "graphChild";
51318
51499
  const reserved = memberLabel3 ? isNeoProjectMemberReservedName(name) : isNeoProjectReservedName(name);
51319
51500
  if (reserved) {
@@ -51346,6 +51527,7 @@ function collectSymbol(uri, kind, name, range2, annotations, ownerName, symbols,
51346
51527
  ...detail ? { detail } : {},
51347
51528
  ...callable2 ? { callable: callable2 } : {},
51348
51529
  ...staticMember ? { static: true } : {},
51530
+ ...reference2 ? { reference: reference2 } : {},
51349
51531
  ...documentation ? { documentation } : {},
51350
51532
  location: { uri, range: range2 }
51351
51533
  });
@@ -51709,6 +51891,7 @@ var init_project_source_analysis = __esm({
51709
51891
  init_project_local_values();
51710
51892
  init_language_spec();
51711
51893
  init_pending_id();
51894
+ init_project_source_ast();
51712
51895
  init_project_source_identifiers();
51713
51896
  init_project_source_parser();
51714
51897
  init_project_source_registry();
@@ -52940,7 +53123,7 @@ var init_registry = __esm({
52940
53123
  "../packages/manifest/schema-contract/registry.mjs"() {
52941
53124
  "use strict";
52942
53125
  PROJECT_SCHEMA_CONTRACT = Object.freeze({
52943
- cliVersion: "0.76.3",
53126
+ cliVersion: "0.77.0",
52944
53127
  projectFileUploadBatchSize: 32,
52945
53128
  documentRecords: {
52946
53129
  member: {
@@ -54800,6 +54983,39 @@ var init_dialogue_source_names = __esm({
54800
54983
  });
54801
54984
 
54802
54985
  // src/project-source/key-reference-spelling.ts
54986
+ function parseIndexPathSpellingV4(expression) {
54987
+ const indexed = expression.kind === "force" ? expression.operand : expression;
54988
+ if (indexed.kind !== "index" || indexed.optional === true) return null;
54989
+ if (indexed.index.kind !== "litString") return null;
54990
+ const indexName = indexed.receiver;
54991
+ if (indexName.kind !== "member" || indexName.optional === true) return null;
54992
+ const marker = indexName.receiver;
54993
+ if (marker.kind !== "member" || marker.optional === true) return null;
54994
+ if (marker.name !== "Index") return null;
54995
+ const listPath = rootMemberPath(marker.receiver);
54996
+ if (listPath === null) return null;
54997
+ return {
54998
+ spelling: "indexPath",
54999
+ listPath,
55000
+ indexSchemaKey: indexName.name,
55001
+ key: indexed.index.value
55002
+ };
55003
+ }
55004
+ function rootMemberPath(expression) {
55005
+ const segments = [];
55006
+ let current = expression;
55007
+ while (current.kind === "member") {
55008
+ if (current.optional === true) return null;
55009
+ segments.unshift(current.name);
55010
+ current = current.receiver;
55011
+ }
55012
+ if (current.kind !== "ident" || current.name !== "root") return null;
55013
+ if (segments.length === 0) return null;
55014
+ return `root.${segments.join(".")}`;
55015
+ }
55016
+ function indexPathSourceV4(spelling) {
55017
+ return `${spelling.listPath}.Index.${spelling.indexSchemaKey}[${JSON.stringify(spelling.key)}]`;
55018
+ }
54803
55019
  function referenceCallNamesKeyV4(expression) {
54804
55020
  if (expression.kind !== "call") return false;
54805
55021
  return expression.argumentNames?.some((name) => name === "key") ?? false;
@@ -54840,11 +55056,7 @@ function astTypeNameV4(type) {
54840
55056
  return "Dictionary";
54841
55057
  }
54842
55058
  function encodeMemberDefaultKeyReferenceV4(spelling) {
54843
- return `${MEMBER_DEFAULT_KEY_PENDING_PREFIX}${JSON.stringify({
54844
- key: spelling.key,
54845
- indexSchemaKey: spelling.indexSchemaKey,
54846
- typeName: spelling.typeName
54847
- })}`;
55059
+ return `${MEMBER_DEFAULT_KEY_PENDING_PREFIX}${JSON.stringify(spelling)}`;
54848
55060
  }
54849
55061
  function isMemberDefaultKeyPlaceholderV4(value) {
54850
55062
  return typeof value === "string" && value.startsWith(MEMBER_DEFAULT_KEY_PENDING_PREFIX);
@@ -54856,6 +55068,16 @@ function decodeMemberDefaultKeyReferenceV4(encoded) {
54856
55068
  );
54857
55069
  if (!isObjectRecord2(payload)) return null;
54858
55070
  if (typeof payload.key !== "string") return null;
55071
+ if (payload.spelling === "indexPath") {
55072
+ if (typeof payload.listPath !== "string") return null;
55073
+ if (typeof payload.indexSchemaKey !== "string") return null;
55074
+ return {
55075
+ spelling: "indexPath",
55076
+ listPath: payload.listPath,
55077
+ indexSchemaKey: payload.indexSchemaKey,
55078
+ key: payload.key
55079
+ };
55080
+ }
54859
55081
  if (typeof payload.typeName !== "string") return null;
54860
55082
  if (payload.indexSchemaKey !== null && typeof payload.indexSchemaKey !== "string") {
54861
55083
  return null;
@@ -54867,19 +55089,16 @@ function decodeMemberDefaultKeyReferenceV4(encoded) {
54867
55089
  typeName: payload.typeName
54868
55090
  };
54869
55091
  }
54870
- function memberDefaultKeyPlaceholderInBodyV4(body) {
54871
- if (isMemberDefaultKeyPlaceholderV4(body)) return body;
54872
- if (!Array.isArray(body)) return null;
54873
- for (const entry of body) {
54874
- if (isMemberDefaultKeyPlaceholderV4(entry)) return entry;
54875
- }
54876
- return null;
55092
+ function memberDefaultKeyPlaceholdersInBodyV4(body) {
55093
+ if (isMemberDefaultKeyPlaceholderV4(body)) return [body];
55094
+ if (!Array.isArray(body)) return [];
55095
+ return body.filter(isMemberDefaultKeyPlaceholderV4);
54877
55096
  }
54878
- function substituteMemberDefaultKeyReferenceV4(body, targetId) {
54879
- if (isMemberDefaultKeyPlaceholderV4(body)) return targetId;
55097
+ function substituteMemberDefaultKeyReferenceV4(body, resolved) {
55098
+ if (isMemberDefaultKeyPlaceholderV4(body)) return resolved.get(body) ?? body;
54880
55099
  if (!Array.isArray(body)) return body;
54881
55100
  return body.map(
54882
- (entry) => isMemberDefaultKeyPlaceholderV4(entry) ? targetId : entry
55101
+ (entry) => isMemberDefaultKeyPlaceholderV4(entry) ? resolved.get(entry) ?? entry : entry
54883
55102
  );
54884
55103
  }
54885
55104
  var MEMBER_DEFAULT_KEY_PENDING_PREFIX;
@@ -57051,12 +57270,23 @@ var init_schema_member = __esm({
57051
57270
  enumId: nonEmptyString,
57052
57271
  multiselect: boolean()
57053
57272
  }),
57054
- memberSchema("lookup", {
57055
- collectionMemberId: nonEmptyString,
57056
- collectionValueId: nullableId,
57057
- multiselect: boolean(),
57058
- declaredType: nullable(schemaTypeSchema)
57059
- }),
57273
+ pipe(
57274
+ memberSchema("lookup", {
57275
+ /** `null` on an unbound `Reference<T>` (P107), typed by `declaredType`. */
57276
+ collectionMemberId: nullable(nonEmptyString),
57277
+ collectionValueId: nullableId,
57278
+ multiselect: boolean(),
57279
+ declaredType: nullable(schemaTypeSchema)
57280
+ }),
57281
+ check(
57282
+ (member) => member.collectionMemberId !== null || member.collectionValueId === null,
57283
+ "A lookup collection value requires a collection member."
57284
+ ),
57285
+ check(
57286
+ (member) => member.collectionMemberId !== null || member.declaredType?.kind === "class" || member.declaredType?.kind === "generic",
57287
+ "A lookup without a collection needs a class or generic declared type."
57288
+ )
57289
+ ),
57060
57290
  memberSchema("dialogueLookup", {
57061
57291
  dialogueGroupId: nullableId,
57062
57292
  multiselect: boolean()
@@ -57492,10 +57722,8 @@ function memberFromDocument(record7, members, owners, classNames, context) {
57492
57722
  return {
57493
57723
  ...common,
57494
57724
  kind: "lookup",
57495
- collectionMemberId: requiredStringValue(
57496
- field("collectionMemberId", void 0),
57497
- record7,
57498
- "collectionMemberId"
57725
+ collectionMemberId: optionalStringOrNull(
57726
+ field("collectionMemberId", null)
57499
57727
  ),
57500
57728
  collectionValueId: optionalStringOrNull(field("collectionValueId", null)),
57501
57729
  multiselect: ordinalValue(
@@ -57793,7 +58021,9 @@ function memberToDocumentFields(member, baseData3) {
57793
58021
  }
57794
58022
  }
57795
58023
  if (member.kind === "lookup") {
57796
- fields.collectionMemberId = member.collectionMemberId;
58024
+ if (member.collectionMemberId !== null) {
58025
+ fields.collectionMemberId = member.collectionMemberId;
58026
+ }
57797
58027
  if (member.collectionValueId !== null) {
57798
58028
  fields.collectionValueId = member.collectionValueId;
57799
58029
  }
@@ -66035,17 +66265,21 @@ var init_neoscript_source_catalog = __esm({
66035
66265
 
66036
66266
  // ../packages/models/src/members/lookup-declared-type.ts
66037
66267
  function lookupDeclaredTypeInfo2(member, collectionTypeInfo) {
66038
- if (collectionTypeInfo === null) return null;
66039
- if (collectionTypeInfo.type !== 6 /* List */ && collectionTypeInfo.type !== 5 /* Dictionary */) {
66040
- return null;
66041
- }
66042
- const entryTypeInfo = member.declaredTypeInfo ?? collectionTypeInfo.entryTypeInfo;
66268
+ const entryTypeInfo = lookupEntryTypeInfo(member, collectionTypeInfo);
66269
+ if (entryTypeInfo === null) return null;
66043
66270
  if (isMultiSelectionMember(member) !== true) {
66044
66271
  return {
66045
66272
  ...entryTypeInfo,
66046
66273
  required: isRequiredMember(member)
66047
66274
  };
66048
66275
  }
66276
+ if (member.collectionMemberId === void 0) {
66277
+ return {
66278
+ type: 9 /* Lookup */,
66279
+ required: isRequiredMember(member),
66280
+ entryTypeInfo
66281
+ };
66282
+ }
66049
66283
  return {
66050
66284
  type: 9 /* Lookup */,
66051
66285
  required: isRequiredMember(member),
@@ -66054,6 +66288,15 @@ function lookupDeclaredTypeInfo2(member, collectionTypeInfo) {
66054
66288
  collectionValueId: member.collectionValueId ?? null
66055
66289
  };
66056
66290
  }
66291
+ function lookupEntryTypeInfo(member, collectionTypeInfo) {
66292
+ if (member.collectionMemberId === void 0) return member.declaredTypeInfo;
66293
+ const collection = collectionTypeInfo(member.collectionMemberId);
66294
+ if (collection === null) return null;
66295
+ if (collection.type !== 6 /* List */ && collection.type !== 5 /* Dictionary */) {
66296
+ return null;
66297
+ }
66298
+ return member.declaredTypeInfo ?? collection.entryTypeInfo;
66299
+ }
66057
66300
  var init_lookup_declared_type = __esm({
66058
66301
  "../packages/models/src/members/lookup-declared-type.ts"() {
66059
66302
  "use strict";
@@ -66167,19 +66410,18 @@ function resolveMemberDeclaredTypeInfoInternal(member, members, seen, ownerClass
66167
66410
  };
66168
66411
  }
66169
66412
  if (isMemberLookupBase(resolved)) {
66170
- const collection = members.find(
66171
- (candidate) => candidate.id === resolved.collectionMemberId
66172
- );
66173
- if (collection === void 0) return null;
66174
- return lookupDeclaredTypeInfo2(
66175
- resolved,
66176
- resolveMemberDeclaredTypeInfoInternal(
66413
+ return lookupDeclaredTypeInfo2(resolved, (collectionMemberId) => {
66414
+ const collection = members.find(
66415
+ (candidate) => candidate.id === collectionMemberId
66416
+ );
66417
+ if (collection === void 0) return null;
66418
+ return resolveMemberDeclaredTypeInfoInternal(
66177
66419
  collection,
66178
66420
  members,
66179
66421
  nextSeen,
66180
66422
  ownerClass
66181
- )
66182
- );
66423
+ );
66424
+ });
66183
66425
  }
66184
66426
  if (resolved.kind === 9 /* Lookup */) return null;
66185
66427
  if (!memberKindSupportsStorage(resolved.kind)) return null;
@@ -66748,12 +66990,10 @@ function memberSymbol(member, schemaKey, environment, field) {
66748
66990
  environment
66749
66991
  );
66750
66992
  }
66751
- const lookupCollectionMemberId = kind === "lookup" ? string3(member.collectionMemberId, `${field}.collectionMemberId`) : null;
66752
- const lookupCollectionWritability = lookupCollectionMemberId === null ? void 0 : environment.effectiveWritabilityByMemberId.get(
66753
- lookupCollectionMemberId
66754
- );
66993
+ const lookupCollection = kind === "lookup" ? lookupCollectionMemberId(member, field) : null;
66994
+ const lookupCollectionWritability = lookupCollection === null ? void 0 : environment.effectiveWritabilityByMemberId.get(lookupCollection);
66755
66995
  const lookup = kind === "lookup" ? {
66756
- collectionMemberId: lookupCollectionMemberId,
66996
+ ...lookupCollection === null ? {} : { collectionMemberId: lookupCollection },
66757
66997
  ...member.collectionValueId === null || typeof member.collectionValueId === "string" ? { collectionValueId: member.collectionValueId } : {},
66758
66998
  multiselect: boolean2(member.multiselect, `${field}.multiselect`),
66759
66999
  ...lookupCollectionWritability === void 0 ? {} : { collectionWritability: lookupCollectionWritability }
@@ -66767,6 +67007,10 @@ function memberSymbol(member, schemaKey, environment, field) {
66767
67007
  ...lookup ? { lookup } : {}
66768
67008
  };
66769
67009
  }
67010
+ function lookupCollectionMemberId(member, field) {
67011
+ if (member.collectionMemberId === null) return null;
67012
+ return string3(member.collectionMemberId, `${field}.collectionMemberId`);
67013
+ }
66770
67014
  function interfaceType(value, environment, field) {
66771
67015
  const id2 = string3(value.id, `${field}.id`);
66772
67016
  return {
@@ -67235,9 +67479,8 @@ function memberType(member, environment, visiting, field) {
67235
67479
  };
67236
67480
  }
67237
67481
  if (kind === "lookup") {
67238
- const collection = environment.members.get(
67239
- string3(member.collectionMemberId, `${field}.collectionMemberId`)
67240
- );
67482
+ const collectionMemberId = lookupCollectionMemberId(member, field);
67483
+ const collection = collectionMemberId === null ? void 0 : environment.members.get(collectionMemberId);
67241
67484
  const collectionType = collection ? memberType(collection, environment, next, `${field}.collection`) : unknownType();
67242
67485
  const declared = optionalRecord(member.declaredType);
67243
67486
  const declaredType = declared ? manifestType2(declared, environment, `${field}.declaredType`) : null;
@@ -68640,6 +68883,13 @@ function memberToSymbol(record7, schemaKey, containingClass2, ownerClassId, inde
68640
68883
  };
68641
68884
  }
68642
68885
  if (isMemberLookupBase(resolved)) {
68886
+ if (resolved.collectionMemberId === void 0) {
68887
+ return {
68888
+ ...common,
68889
+ kind: "property",
68890
+ lookup: { multiselect: isMultiSelectionMember(resolved) }
68891
+ };
68892
+ }
68643
68893
  const collectionWritability = uniqueEffectiveWritability(
68644
68894
  resolved.collectionMemberId,
68645
68895
  context
@@ -68995,12 +69245,12 @@ function memberRuntimeType(record7, context, seen = /* @__PURE__ */ new Set(), g
68995
69245
  if (!isMemberLookupBase(member)) return UNKNOWN_TYPE2;
68996
69246
  const declared = member.declaredTypeInfo ? toLanguageType(member.declaredTypeInfo, context, genericEnvironment) : null;
68997
69247
  const narrowed = declared && !isUnknownType3(declared) ? declared : null;
68998
- const entry = narrowed ?? lookupCollectionEntryType(
69248
+ const entry = narrowed ?? (member.collectionMemberId === void 0 ? null : lookupCollectionEntryType(
68999
69249
  member.collectionMemberId,
69000
69250
  context,
69001
69251
  nextSeen,
69002
69252
  genericEnvironment
69003
- );
69253
+ ));
69004
69254
  if (!entry) return UNKNOWN_TYPE2;
69005
69255
  return isMultiSelectionMember(member) ? {
69006
69256
  kind: "set",
@@ -70840,6 +71090,7 @@ function inferChildMemberFromParentValue(parent, parentMember, data) {
70840
71090
  return findMemberById(parentMember.entryMemberId, data.members);
70841
71091
  }
70842
71092
  if (isMemberLookupBase(parentMember)) {
71093
+ if (parentMember.collectionMemberId === void 0) return null;
70843
71094
  const collectionMember = findMemberById(
70844
71095
  parentMember.collectionMemberId,
70845
71096
  data.members
@@ -75845,7 +76096,11 @@ function evalInstructions(instructions, scope, ctx, options) {
75845
76096
  }
75846
76097
  let targetTypeInfo = ins.target.typeInfo;
75847
76098
  if (lookupAssignment && targetTypeInfo.type !== 9 /* Lookup */ && targetTypeInfo.type !== 18 /* DialogueLookup */) {
75848
- targetTypeInfo = {
76099
+ targetTypeInfo = member.collectionMemberId === void 0 ? {
76100
+ type: 9 /* Lookup */,
76101
+ required: targetTypeInfo.required,
76102
+ entryTypeInfo: targetTypeInfo
76103
+ } : {
75849
76104
  type: 9 /* Lookup */,
75850
76105
  required: targetTypeInfo.required,
75851
76106
  entryTypeInfo: targetTypeInfo,
@@ -78539,6 +78794,13 @@ function runtimeTypeInfoForMember(rawMember, env, ctx, seen) {
78539
78794
  };
78540
78795
  }
78541
78796
  if (isMemberLookup(member)) {
78797
+ if (member.collectionMemberId === void 0) {
78798
+ const entryTypeInfo2 = {
78799
+ ...substituteRuntimeTypeInfo(member.declaredTypeInfo, env, ctx),
78800
+ required: isMultiSelectionMember(member) ? true : false
78801
+ };
78802
+ return isMultiSelectionMember(member) ? { type: 9 /* Lookup */, required: required2, entryTypeInfo: entryTypeInfo2 } : entryTypeInfo2;
78803
+ }
78542
78804
  const collection = evalMemberById(ctx.vm, member.collectionMemberId);
78543
78805
  if (collection === null) {
78544
78806
  throw new NSGetterRuntimeError(
@@ -94755,7 +95017,16 @@ function lowerFieldMember(context, ownerClass, declaration, id2, commonInput, ba
94755
95017
  const storedStatic = declaration.modifiers.includes("static");
94756
95018
  const settings = annotation(declaration.annotations, "settings");
94757
95019
  const inherited = inheritedMember(context, commonInput.overrideOf);
94758
- const storesSelectionArray = context.enumIdsByName.has(fieldType.name) || fieldType.name === "NeoDialogueReference" || Boolean(settings && argument(settings, "collection"));
95020
+ const reference2 = referenceMemberShape(fieldType);
95021
+ const collectionArgument = argument(settings, "collection");
95022
+ if (reference2 === null && collectionArgument !== null) {
95023
+ const spelled = formatManifestType({ ...fieldType, nullable: false });
95024
+ const fixed = (fieldType.name === "Set" || fieldType.name === "List") && fieldType.arguments[0] !== void 0 ? `Set<Reference<${formatManifestType(fieldType.arguments[0])}>>` : `Reference<${spelled}>`;
95025
+ throw new Error(
95026
+ `Lookup member ${ownerClass.name}.${declaration.name} declares collection: on ${spelled}. Spell the type ${fixed}.`
95027
+ );
95028
+ }
95029
+ const storesSelectionArray = context.enumIdsByName.has(fieldType.name) || fieldType.name === "NeoDialogueReference" || reference2 !== null;
94759
95030
  const common = {
94760
95031
  ...commonInput,
94761
95032
  // A stored static initializer authors the live value binding, never an
@@ -94764,13 +95035,14 @@ function lowerFieldMember(context, ownerClass, declaration, id2, commonInput, ba
94764
95035
  defaultValue: storedStatic ? base?.defaultValue ?? null : lowerDefault(
94765
95036
  context,
94766
95037
  declaration.initializer,
94767
- declaration.type,
95038
+ reference2?.readType ?? declaration.type,
94768
95039
  ownerClass,
94769
95040
  base,
94770
95041
  id2,
94771
95042
  storesSelectionArray,
94772
95043
  `${ownerClass.name}.${declaration.name}`,
94773
- !declaration.modifiers.includes("static") && contextualEnumArgument(settings, "kind") === "Unordered"
95044
+ !declaration.modifiers.includes("static") && contextualEnumArgument(settings, "kind") === "Unordered",
95045
+ reference2 !== null
94774
95046
  )
94775
95047
  };
94776
95048
  const name = fieldType.name;
@@ -94899,39 +95171,56 @@ function lowerFieldMember(context, ownerClass, declaration, id2, commonInput, ba
94899
95171
  };
94900
95172
  }
94901
95173
  if (primitive3) return { ...common, kind: primitive3 };
94902
- if (settings && argument(settings, "collection")) {
94903
- const collectionMemberId = resolveQualifiedMemberId(context, argument(settings, "collection")) ?? (base?.kind === "lookup" ? base.collectionMemberId : null);
94904
- if (!collectionMemberId) {
95174
+ if (reference2 !== null) {
95175
+ if (argument(settings, "selection") !== null) {
95176
+ throw new Error(
95177
+ `Lookup member ${ownerClass.name}.${declaration.name} declares selection:. Reference<T> selects one row and Set<Reference<T>> selects many, so remove selection:.`
95178
+ );
95179
+ }
95180
+ assertNoElementSettings(fieldType, ownerClass, declaration, "lookup");
95181
+ const inheritedCollectionMemberId = inherited?.kind === "lookup" ? inherited.collectionMemberId : null;
95182
+ if (collectionArgument === null && inheritedCollectionMemberId !== null) {
95183
+ throw new Error(
95184
+ `Lookup member ${ownerClass.name}.${declaration.name} overrides a lookup bound to ${storageChainStep(context, inheritedCollectionMemberId)?.label ?? inheritedCollectionMemberId}. An override cannot drop its collection; add @settings(collection: ...).`
95185
+ );
95186
+ }
95187
+ const collectionMemberId = collectionArgument === null ? null : resolveQualifiedMemberId(context, collectionArgument) ?? (base?.kind === "lookup" ? base.collectionMemberId : null);
95188
+ if (collectionArgument === null && argument(settings, "collectionValue") !== null) {
95189
+ throw new Error(
95190
+ `Lookup member ${ownerClass.name}.${declaration.name} declares collectionValue: without collection:. Add the collection it searches, or remove collectionValue:.`
95191
+ );
95192
+ }
95193
+ if (collectionArgument !== null && collectionMemberId === null) {
94905
95194
  throw new Error(
94906
95195
  `Lookup member ${ownerClass.name}.${declaration.name} has an unresolved collection.`
94907
95196
  );
94908
95197
  }
94909
- const selection2 = contextualEnumArgument(settings, "selection");
94910
- if (fieldType.name === "Set" && selection2 === "Single") {
95198
+ const declaredType = lookupDeclaredType(
95199
+ context,
95200
+ ownerClass,
95201
+ reference2.target
95202
+ );
95203
+ if (collectionMemberId === null && declaredType?.kind === "interface") {
94911
95204
  throw new Error(
94912
- `Lookup member ${ownerClass.name}.${declaration.name} declares Set<T> but explicitly selects .Single. Use the element type for a single selection or remove selection: .Single.`
95205
+ `Lookup member ${ownerClass.name}.${declaration.name} targets interface ${reference2.target.name}. Reference<T> needs a class or generic parameter.`
95206
+ );
95207
+ }
95208
+ if (collectionMemberId === null && declaredType === null) {
95209
+ throw new Error(
95210
+ `Lookup member ${ownerClass.name}.${declaration.name} targets ${formatManifestType(reference2.target)}. Reference<T> needs a class or generic parameter.`
94913
95211
  );
94914
95212
  }
94915
- const multiselect = fieldType.name === "Set" || selection2 === "Multi";
94916
- assertNoElementSettings(fieldType, ownerClass, declaration, "lookup");
94917
95213
  return {
94918
95214
  ...common,
94919
95215
  kind: "lookup",
94920
95216
  collectionMemberId,
94921
95217
  collectionValueId: referenceIdArgument(settings, "collectionValue"),
94922
- multiselect,
94923
- // The spelled type, kept only when it narrows the collection entry —
94924
- // `normalizeLookupDeclaredTypes` clears the redundant ones once every
94925
- // member is lowered and the entry can be resolved in either direction.
94926
- declaredType: lookupDeclaredType(
94927
- context,
94928
- ownerClass,
94929
- // A multiselect lookup is spelled `Set<T>` and stores the element
94930
- // type, which is what the emitter re-wraps. Keyed off the spelling
94931
- // rather than off `multiselect` so a mismatched pair reads the type it
94932
- // actually has instead of throwing for a missing type argument.
94933
- fieldType.name === "List" || fieldType.name === "Set" ? requiredTypeArgument(fieldType, 0) : fieldType
94934
- )
95218
+ multiselect: reference2.multiselect,
95219
+ // The spelled type. A bound lookup keeps it only when it narrows the
95220
+ // collection entry -- `normalizeLookupDeclaredTypes` clears the redundant
95221
+ // ones once every member is lowered. An unbound lookup always keeps it:
95222
+ // it is the only target type the member has.
95223
+ declaredType
94935
95224
  };
94936
95225
  }
94937
95226
  if (name === "List" || name === "Set") {
@@ -95183,6 +95472,10 @@ function overrideParentMemberId(context, memberId) {
95183
95472
  return overrides ? inheritedMemberId(context, ownerClass, member.name) : null;
95184
95473
  }
95185
95474
  function lowerCollectionEntry(context, ownerClass, entryType, entryId, parentMemberId, declaringMemberId, entrySettings) {
95475
+ const reference2 = referenceMemberShape(entryType);
95476
+ if (reference2 !== null) {
95477
+ throw referenceStorageTypeError(reference2.target);
95478
+ }
95186
95479
  const declared = declaredCollectionEntryType(context, declaringMemberId);
95187
95480
  const expected = declared === null ? null : resolveManifestTypeInDescendant(
95188
95481
  context,
@@ -95486,6 +95779,30 @@ function lowerEnum(context, declaration) {
95486
95779
  system: systemMetadata(declaration.annotations) ?? base?.system ?? null
95487
95780
  };
95488
95781
  }
95782
+ function referenceStorageTypeError(referenced) {
95783
+ const target = formatManifestType(referenced);
95784
+ return new Error(
95785
+ `Reference<${target}> is a member storage type. Write ${target} here.`
95786
+ );
95787
+ }
95788
+ function referenceMemberShape(type) {
95789
+ if (type.name === "Reference") {
95790
+ const target2 = requiredTypeArgument(type, 0);
95791
+ return {
95792
+ target: target2,
95793
+ multiselect: false,
95794
+ readType: { ...target2, nullable: type.nullable }
95795
+ };
95796
+ }
95797
+ const element = type.name === "Set" ? type.arguments[0] : void 0;
95798
+ if (element?.name !== "Reference") return null;
95799
+ const target = requiredTypeArgument(element, 0);
95800
+ return {
95801
+ target,
95802
+ multiselect: true,
95803
+ readType: { ...type, arguments: [target] }
95804
+ };
95805
+ }
95489
95806
  function lookupDeclaredType(context, ownerClass, type) {
95490
95807
  if (type.name === "object") return null;
95491
95808
  const lowered = lowerType(context, type, ownerClass);
@@ -95498,6 +95815,7 @@ function normalizeLookupDeclaredTypes(context) {
95498
95815
  for (const [memberId, member] of context.loweredMembers) {
95499
95816
  if (member.kind !== "lookup") continue;
95500
95817
  if (member.declaredType === null) continue;
95818
+ if (member.collectionMemberId === null) continue;
95501
95819
  if (!lookupDeclaredTypeNarrows(context, member)) {
95502
95820
  context.loweredMembers.set(memberId, { ...member, declaredType: null });
95503
95821
  }
@@ -95506,6 +95824,7 @@ function normalizeLookupDeclaredTypes(context) {
95506
95824
  function lookupDeclaredTypeNarrows(context, member) {
95507
95825
  const declared = member.declaredType;
95508
95826
  if (declared === null) return false;
95827
+ if (member.collectionMemberId === null) return true;
95509
95828
  const entry = lookupEntryMember(context, member.collectionMemberId);
95510
95829
  if (entry === void 0) return false;
95511
95830
  if (entry.kind === "class") {
@@ -95531,6 +95850,9 @@ function lowerType(context, type, ownerClass) {
95531
95850
  if (type.name === "void") {
95532
95851
  throw new Error("void is valid only as a function return type.");
95533
95852
  }
95853
+ if (type.name === "Reference") {
95854
+ throw referenceStorageTypeError(requiredTypeArgument(type, 0));
95855
+ }
95534
95856
  if (type.name === "Set") {
95535
95857
  return {
95536
95858
  kind: "lookup",
@@ -95628,7 +95950,18 @@ function lowerType(context, type, ownerClass) {
95628
95950
  if (type.name === "object") return { kind: "unknown", nullable: nullable2 };
95629
95951
  throw new Error(`Unknown Neo type ${JSON.stringify(type.name)}.`);
95630
95952
  }
95631
- function lowerDefault(context, initializer, type, ownerClass, base, memberId, storesSelectionArray, path, unorderedList = false) {
95953
+ function indexPathDefault(expression) {
95954
+ const elements = expression.kind === "litList" ? expression.elements : [expression];
95955
+ if (elements.length === 0) return null;
95956
+ const placeholders = [];
95957
+ for (const element of elements) {
95958
+ const spelling = parseIndexPathSpellingV4(element);
95959
+ if (spelling === null) return null;
95960
+ placeholders.push(encodeMemberDefaultKeyReferenceV4(spelling));
95961
+ }
95962
+ return placeholders;
95963
+ }
95964
+ function lowerDefault(context, initializer, type, ownerClass, base, memberId, storesSelectionArray, path, unorderedList = false, reference2 = false) {
95632
95965
  if (initializer === null) {
95633
95966
  return base?.defaultValue && "serverValueId" in base.defaultValue ? base.defaultValue : null;
95634
95967
  }
@@ -95650,6 +95983,8 @@ function lowerDefault(context, initializer, type, ownerClass, base, memberId, st
95650
95983
  }
95651
95984
  if (base?.defaultValue != null && matchesStoredInitializer(base.defaultValue, initializer))
95652
95985
  return base.defaultValue;
95986
+ const indexPaths = reference2 ? indexPathDefault(expression) : null;
95987
+ if (indexPaths !== null) return { value: indexPaths, classId: null };
95653
95988
  if (unorderedList && expression.kind === "litList" && expression.elements.length > 0 || positionRequiresEvaluation(context, expression, type, ownerClass)) {
95654
95989
  return {
95655
95990
  init: {
@@ -96014,7 +96349,8 @@ function lowerExpressionValue(context, expression, declaredExpected, ownerClass,
96014
96349
  );
96015
96350
  const member = memberId !== null ? context.loweredMembers.get(memberId) ?? context.baseMembers.get(memberId) : void 0;
96016
96351
  const sourceMember = memberId !== null ? context.sourceMembersById.get(memberId)?.member : void 0;
96017
- const declaredType = sourceMember?.type ?? (member ? manifestTypeForMember(context, member) : { name: "object", nullable: true, arguments: [] });
96352
+ const sourceType = sourceMember === void 0 ? void 0 : referenceMemberShape(sourceMember.type)?.readType ?? sourceMember.type;
96353
+ const declaredType = sourceType ?? (member ? manifestTypeForMember(context, member) : { name: "object", nullable: true, arguments: [] });
96018
96354
  result[assignment.name] = lowerExpressionValue(
96019
96355
  context,
96020
96356
  assignment.value,
@@ -97398,6 +97734,7 @@ function emitProjectSourcesV4(manifest, options = {}) {
97398
97734
  collectionValuePaths: options.collectionValuePaths ?? /* @__PURE__ */ new Map(),
97399
97735
  variantPathsById: buildVariantPathsByIdV4(manifest),
97400
97736
  formAlternates: [],
97737
+ ...options.referenceTargetSource === void 0 ? {} : { referenceTargetSource: options.referenceTargetSource },
97401
97738
  templateNames: new Map(
97402
97739
  [...manifest.textureTemplates, ...manifest.audioTemplates].map(
97403
97740
  (template) => [template.id, template.name]
@@ -98034,7 +98371,7 @@ function memberSettings(context, member) {
98034
98371
  if (member.listKind === "unordered") settings.push("kind: .Unordered");
98035
98372
  } else if (member.kind === "enum" && member.multiselect) {
98036
98373
  settings.push("selection: .Multi");
98037
- } else if (member.kind === "lookup") {
98374
+ } else if (member.kind === "lookup" && member.collectionMemberId !== null) {
98038
98375
  settings.push(
98039
98376
  `collection: ${qualifiedMember(context, member.collectionMemberId)}`
98040
98377
  );
@@ -98046,7 +98383,6 @@ function memberSettings(context, member) {
98046
98383
  rootPath === void 0 ? `collectionValue: Reference(id: ${quote(member.collectionValueId)})` : `collectionValue: Reference(${rootPath})`
98047
98384
  );
98048
98385
  }
98049
- if (member.multiselect) settings.push("selection: .Multi");
98050
98386
  } else if (member.kind === "dialogueLookup") {
98051
98387
  if (member.dialogueGroupId) {
98052
98388
  settings.push(
@@ -98196,7 +98532,7 @@ function renderMemberType(context, member, enclosingClassId, elementSettings = f
98196
98532
  break;
98197
98533
  }
98198
98534
  case "lookup": {
98199
- const target = member.declaredType === null ? renderLookupEntryType(context, member.collectionMemberId) : renderType(context, member.declaredType).replace(/\?$/u, "");
98535
+ const target = `Reference<${renderLookupTargetType(context, member)}>`;
98200
98536
  value = member.multiselect ? `Set<${target}>` : target;
98201
98537
  break;
98202
98538
  }
@@ -98248,6 +98584,17 @@ function renderActionType(argumentTypes) {
98248
98584
  if (argumentTypes.length === 0) return "NeoAction";
98249
98585
  return `NeoAction<${argumentTypes.join(", ")}>`;
98250
98586
  }
98587
+ function renderLookupTargetType(context, member) {
98588
+ if (member.declaredType !== null) {
98589
+ return renderType(context, member.declaredType).replace(/\?$/u, "");
98590
+ }
98591
+ if (member.collectionMemberId === null) {
98592
+ throw new Error(
98593
+ `Lookup member ${member.name} has neither a collection nor a declared type.`
98594
+ );
98595
+ }
98596
+ return renderLookupEntryType(context, member.collectionMemberId);
98597
+ }
98251
98598
  function renderLookupEntryType(context, collectionMemberId) {
98252
98599
  const collection = required(
98253
98600
  context.members,
@@ -98565,9 +98912,10 @@ function renderDefaultValue(context, member, wrapper, enclosingClassId) {
98565
98912
  ).name;
98566
98913
  }
98567
98914
  if ((member.kind === "lookup" || member.kind === "dialogueLookup") && Array.isArray(value)) {
98568
- const renderedType = referenceTargetType(context, member, enclosingClassId);
98569
- const targetType = member.multiselect ? /^Set<(.+)>$/u.exec(renderedType)?.[1] ?? renderedType : renderedType;
98570
- const refs = value.filter((entry) => typeof entry === "string").map((entry) => `Reference<${targetType}>(id: ${quote(entry)})`);
98915
+ const targetType = referenceTargetType(context, member, enclosingClassId);
98916
+ const refs = value.filter((entry) => typeof entry === "string").map(
98917
+ (entry) => unboundReferenceSource(context, member, entry) ?? `Reference<${targetType}>(id: ${quote(entry)})`
98918
+ );
98571
98919
  if (refs.length === 0 && value.length === 0) return "[]";
98572
98920
  return member.multiselect ? `[${refs.join(", ")}]` : refs[0] ?? "null";
98573
98921
  }
@@ -98620,8 +98968,15 @@ function renderDefaultValue(context, member, wrapper, enclosingClassId) {
98620
98968
  function spriteFileSource(context, fileId) {
98621
98969
  return context.fileSymbols.get(fileId) ?? `Reference<NeoImage>(id: ${quote(fileId)})`;
98622
98970
  }
98971
+ function unboundReferenceSource(context, member, targetId) {
98972
+ if (member.kind !== "lookup" || member.collectionMemberId !== null) {
98973
+ return null;
98974
+ }
98975
+ return context.referenceTargetSource?.(member, targetId) ?? null;
98976
+ }
98623
98977
  function referenceTargetType(context, member, enclosingClassId) {
98624
98978
  if (member.kind === "dialogueLookup") return "NeoDialogue";
98979
+ if (member.kind === "lookup") return renderLookupTargetType(context, member);
98625
98980
  return renderMemberType(context, member, enclosingClassId).replace(
98626
98981
  /\?$/u,
98627
98982
  ""
@@ -98639,7 +98994,7 @@ function renderNestedValue(context, member, value, enclosingClassId) {
98639
98994
  );
98640
98995
  if (option !== void 0) return `.${option.name}`;
98641
98996
  }
98642
- return `Reference<${referenceTargetType(context, member, enclosingClassId)}>(id: ${quote(value)})`;
98997
+ return unboundReferenceSource(context, member, value) ?? `Reference<${referenceTargetType(context, member, enclosingClassId)}>(id: ${quote(value)})`;
98643
98998
  }
98644
98999
  return renderDefaultValue(
98645
99000
  context,
@@ -105146,6 +105501,8 @@ function validateProjectRecordByKind(recordKind, data, options = {}) {
105146
105501
  if (recordKind === ProjectRecordKind.Member) {
105147
105502
  const memberGuard = options.members === "authored-or-compiled" ? isAnyMemberAuthoredOrCompiled : isAnyMember;
105148
105503
  if (memberGuard(data)) return;
105504
+ const lookupViolation = lookupShapeViolation(data);
105505
+ if (lookupViolation !== null) throw new Error(lookupViolation);
105149
105506
  const unrounded = unroundedFloatPath(data);
105150
105507
  if (unrounded !== null) {
105151
105508
  const id2 = typeof data === "object" && data !== null && "id" in data ? String(data.id) : "";
@@ -105236,6 +105593,7 @@ var init_project_record_validation = __esm({
105236
105593
  init_project();
105237
105594
  init_variants();
105238
105595
  init_members();
105596
+ init_member_kinds();
105239
105597
  init_classes();
105240
105598
  init_constructors2();
105241
105599
  init_packed_value_record_validation();
@@ -109802,22 +110160,31 @@ function lookupTargetClassContractViolation(args) {
109802
110160
  if (actualClassId === null) return null;
109803
110161
  const classById = args.schemaIndex.classesById;
109804
110162
  if (!classById.has(actualClassId)) return null;
110163
+ const collectionMemberId = args.member.collectionMemberId;
110164
+ if (collectionMemberId === void 0) {
110165
+ const target = args.member.declaredTypeInfo;
110166
+ const targetClassId = target.type === 7 /* Class */ ? target.classId : genericParamClassConstraintId(target, classById);
110167
+ if (targetClassId === null) return null;
110168
+ if (args.schemaIndex.classIsOrExtends(actualClassId, targetClassId))
110169
+ return null;
110170
+ return `lookup member ${args.member.name} target ${args.targetValueId} is ${classDisplayName(actualClassId, classById)}, but the member declares ${classDisplayName(targetClassId, classById)}. Point it at a ${classDisplayName(targetClassId, classById)} row.`;
110171
+ }
110172
+ const declaredClassId = lookupDeclaredClassId(args.member, classById);
109805
110173
  const entryClassId = lookupEntryClassId(
109806
- args.member,
110174
+ collectionMemberId,
109807
110175
  args.schemaIndex.membersById
109808
110176
  );
109809
110177
  if (entryClassId !== null && classById.has(entryClassId) && !args.schemaIndex.classIsOrExtends(actualClassId, entryClassId)) {
109810
- return `lookup member ${args.member.name} target ${args.targetValueId} is ${classDisplayName(actualClassId, classById)}, but collection ${collectionMemberName(args.member, args.schemaIndex)} holds ${classDisplayName(entryClassId, classById)}.`;
110178
+ return `lookup member ${args.member.name} target ${args.targetValueId} is ${classDisplayName(actualClassId, classById)}, but collection ${collectionMemberName(collectionMemberId, args.schemaIndex)} holds ${classDisplayName(entryClassId, classById)}.`;
109811
110179
  }
109812
- const declaredClassId = lookupDeclaredClassId(args.member, classById);
109813
110180
  if (declaredClassId !== null && !args.schemaIndex.classIsOrExtends(actualClassId, declaredClassId)) {
109814
- const narrowedFrom = entryClassId === null || !classById.has(entryClassId) ? `the collection ${collectionMemberName(args.member, args.schemaIndex)}` : `the collection ${collectionMemberName(args.member, args.schemaIndex)} holds ${classDisplayName(entryClassId, classById)}`;
110181
+ const narrowedFrom = entryClassId === null || !classById.has(entryClassId) ? `the collection ${collectionMemberName(collectionMemberId, args.schemaIndex)}` : `the collection ${collectionMemberName(collectionMemberId, args.schemaIndex)} holds ${classDisplayName(entryClassId, classById)}`;
109815
110182
  return `lookup member ${args.member.name} target ${args.targetValueId} is ${classDisplayName(actualClassId, classById)}, but the member declares ${classDisplayName(declaredClassId, classById)} \u2014 ${narrowedFrom} and this member narrows it. Point it at a ${classDisplayName(declaredClassId, classById)} row.`;
109816
110183
  }
109817
110184
  return null;
109818
110185
  }
109819
- function lookupEntryClassId(member, memberById2) {
109820
- const collection = memberById2.get(member.collectionMemberId);
110186
+ function lookupEntryClassId(collectionMemberId, memberById2) {
110187
+ const collection = memberById2.get(collectionMemberId);
109821
110188
  if (collection === void 0) return null;
109822
110189
  if (!isMemberListBase(collection) && !isMemberDictionaryBase(collection)) {
109823
110190
  return null;
@@ -109836,24 +110203,45 @@ function lookupDeclaredClassId(member, classById) {
109836
110203
  function classDisplayName(classId, classById) {
109837
110204
  return classById.get(classId)?.name ?? classId;
109838
110205
  }
109839
- function collectionMemberName(member, schemaIndex) {
109840
- const collection = schemaIndex.membersById.get(member.collectionMemberId);
109841
- if (collection === void 0) return member.collectionMemberId;
110206
+ function collectionMemberName(collectionMemberId, schemaIndex) {
110207
+ const collection = schemaIndex.membersById.get(collectionMemberId);
110208
+ if (collection === void 0) return collectionMemberId;
109842
110209
  const owner = schemaIndex.schemaPlacementsByMemberId.get(collection.id)?.[0]?.ownerClass;
109843
110210
  return owner ? `${owner.name}.${collection.name}` : collection.name;
109844
110211
  }
109845
110212
  function lookupDeclaredClassContractViolation(member, schemaIndex) {
110213
+ if (member.collectionMemberId === void 0) {
110214
+ return unboundLookupTargetViolation(member, schemaIndex);
110215
+ }
109846
110216
  const declared = lookupDeclaredClassId(member, schemaIndex.classesById);
109847
- const entry = lookupEntryClassId(member, schemaIndex.membersById);
110217
+ const entry = lookupEntryClassId(
110218
+ member.collectionMemberId,
110219
+ schemaIndex.membersById
110220
+ );
109848
110221
  if (declared === null || entry === null || !schemaIndex.classesById.has(entry))
109849
110222
  return null;
109850
110223
  if (schemaIndex.classIsOrExtends(declared, entry)) return null;
109851
- return `Lookup member ${member.name} declares ${classDisplayName(declared, schemaIndex.classesById)}, but collection ${collectionMemberName(member, schemaIndex)} holds ${classDisplayName(entry, schemaIndex.classesById)}.`;
110224
+ return `Lookup member ${member.name} declares ${classDisplayName(declared, schemaIndex.classesById)}, but collection ${collectionMemberName(member.collectionMemberId, schemaIndex)} holds ${classDisplayName(entry, schemaIndex.classesById)}.`;
110225
+ }
110226
+ function unboundLookupTargetViolation(member, schemaIndex) {
110227
+ const target = member.declaredTypeInfo;
110228
+ if (target.type === 7 /* Class */) {
110229
+ if (schemaIndex.classesById.has(target.classId)) return null;
110230
+ return `Lookup member ${member.name} references class ${target.classId}, which does not exist.`;
110231
+ }
110232
+ const scoped = schemaIndex.schemaPlacements(member.id).some(
110233
+ (placement) => resolveGenericScope(placement.ownerClass.id, schemaIndex.classes).some(
110234
+ (entry) => entry.param.id === target.genericParamId
110235
+ )
110236
+ );
110237
+ if (scoped) return null;
110238
+ return `Lookup member ${member.name} references generic parameter ${target.genericParamId}, which is not in scope on its declaring class.`;
109852
110239
  }
109853
110240
  var init_lookup_target_class_contract = __esm({
109854
110241
  "../src/database/lookup-target-class-contract.ts"() {
109855
110242
  "use strict";
109856
110243
  init_member_kinds();
110244
+ init_generics();
109857
110245
  }
109858
110246
  });
109859
110247
 
@@ -120959,6 +121347,14 @@ function assertProjectInterfaceDocumentValid(view, options = {}) {
120959
121347
  ...resolved.kind === 5 && resolved.keyEnumId ? { keyEnumId: resolved.keyEnumId } : {}
120960
121348
  };
120961
121349
  }
121350
+ if (resolved.kind === 9 && resolved.collectionMemberId === void 0) {
121351
+ const entryTypeInfo = substituteTypeInfoForClass(
121352
+ resolved.declaredTypeInfo,
121353
+ schemaClass2
121354
+ );
121355
+ if (!isRecord8(entryTypeInfo)) return null;
121356
+ return resolved.selection === 1 /* Multi */ ? { type: 9, required: required2, entryTypeInfo } : { ...entryTypeInfo, required: required2 };
121357
+ }
120962
121358
  if (resolved.kind === 9) {
120963
121359
  if (typeof resolved.collectionMemberId !== "string") return null;
120964
121360
  const collection = membersById.get(resolved.collectionMemberId);
@@ -126694,6 +127090,12 @@ function buildValueEmitIndexesV4(records3, manifest, localizedTextDependencies,
126694
127090
  }
126695
127091
  return listenerBindingOwners = owners;
126696
127092
  };
127093
+ const storedPlacements = storedIndexes?.placements ?? buildStoredValuePlacementIndexV4(records3.values(), values);
127094
+ const rootPathsByValueId = rootValuePathsByValueId(
127095
+ records3.values(),
127096
+ storedPlacements
127097
+ );
127098
+ let rootIndexPaths;
126697
127099
  return {
126698
127100
  records: records3,
126699
127101
  manifest,
@@ -126737,9 +127139,12 @@ function buildValueEmitIndexesV4(records3, manifest, localizedTextDependencies,
126737
127139
  localizedTexts,
126738
127140
  mainLocale: projectMainLocale(records3),
126739
127141
  symbolsByValueId,
126740
- rootPathsByValueId: rootValuePathsByValueId(
126741
- records3.values(),
126742
- storedIndexes?.placements ?? buildStoredValuePlacementIndexV4(records3.values(), values)
127142
+ rootPathsByValueId,
127143
+ readRootIndexPaths: () => rootIndexPaths ??= indexRootIndexPaths(
127144
+ values,
127145
+ manifestMembers,
127146
+ storedPlacements,
127147
+ rootPathsByValueId
126743
127148
  ),
126744
127149
  collectionBindingValueIds: /* @__PURE__ */ new Set([
126745
127150
  ...manifest.members.flatMap(
@@ -127643,7 +128048,7 @@ function buildValueLowerContext(state, manifest, options = {}) {
127643
128048
  ),
127644
128049
  collectionBindingMemberIds: /* @__PURE__ */ new Set([
127645
128050
  ...manifest.members.flatMap(
127646
- (member) => member.kind === "lookup" ? [member.collectionMemberId] : []
128051
+ (member) => member.kind === "lookup" && member.collectionMemberId !== null ? [member.collectionMemberId] : []
127647
128052
  ),
127648
128053
  ...(manifest.variantFolders ?? []).flatMap(
127649
128054
  (folder) => folder.binding === null ? [] : [folder.binding.collectionMemberId]
@@ -128412,7 +128817,8 @@ function drainValueReferenceObligationsV4(state, manifest, options) {
128412
128817
  const failure = settleReferenceObligation(
128413
128818
  context,
128414
128819
  obligation,
128415
- deletedValueIds
128820
+ deletedValueIds,
128821
+ options.resolveRootPath
128416
128822
  );
128417
128823
  if (failure === null) continue;
128418
128824
  failures.push({
@@ -128432,6 +128838,7 @@ function collectionValueObligations(manifest) {
128432
128838
  const obligations = [];
128433
128839
  for (const member of manifest.members) {
128434
128840
  if (member.kind !== "lookup") continue;
128841
+ if (member.collectionMemberId === null) continue;
128435
128842
  if (typeof member.collectionValueId !== "string") continue;
128436
128843
  if (member.collectionValueId.startsWith("__pending__:root-path:")) continue;
128437
128844
  obligations.push({
@@ -128449,10 +128856,15 @@ function collectionValueObligations(manifest) {
128449
128856
  }
128450
128857
  return obligations;
128451
128858
  }
128452
- function settleReferenceObligation(context, obligation, deletedValueIds) {
128859
+ function settleReferenceObligation(context, obligation, deletedValueIds, resolveRootPath) {
128453
128860
  try {
128454
128861
  if (obligation.kind === "reference") {
128455
- settleReferenceMember(context, obligation, deletedValueIds);
128862
+ settleReferenceMember(
128863
+ context,
128864
+ obligation,
128865
+ deletedValueIds,
128866
+ resolveRootPath
128867
+ );
128456
128868
  return null;
128457
128869
  }
128458
128870
  if (obligation.kind === "constructorProjection") {
@@ -128465,7 +128877,7 @@ function settleReferenceObligation(context, obligation, deletedValueIds) {
128465
128877
  return error instanceof Error ? error.message : String(error);
128466
128878
  }
128467
128879
  }
128468
- function settleReferenceMember(context, obligation, deletedValueIds) {
128880
+ function settleReferenceMember(context, obligation, deletedValueIds, resolveRootPath) {
128469
128881
  const member = context.members.get(obligation.memberId);
128470
128882
  if (member?.kind !== "lookup" && member?.kind !== "dialogueLookup") {
128471
128883
  throw new Error(
@@ -128476,6 +128888,18 @@ function settleReferenceMember(context, obligation, deletedValueIds) {
128476
128888
  settleKeyReference(context, member, obligation.target, obligation);
128477
128889
  return;
128478
128890
  }
128891
+ if (obligation.target.spelling === "indexPath") {
128892
+ const targetId = resolveValidIndexPathTargetId(
128893
+ context,
128894
+ member,
128895
+ obligation.target,
128896
+ obligation.ownerValueId,
128897
+ resolveRootPath
128898
+ );
128899
+ if (targetId === null) return;
128900
+ patchReferenceSlot(member, obligation.target.patch, targetId);
128901
+ return;
128902
+ }
128479
128903
  const target = resolveObligationTarget(
128480
128904
  context,
128481
128905
  obligation.target,
@@ -128491,14 +128915,96 @@ function settleKeyReference(context, member, spelling, obligation) {
128491
128915
  spelling,
128492
128916
  obligation.ownerValueId
128493
128917
  );
128494
- spelling.patch.ids[spelling.patch.index] = targetId;
128495
- const patched = spelling.patch.ids.filter((id2) => !isPendingId(id2));
128918
+ patchReferenceSlot(member, spelling.patch, targetId);
128919
+ }
128920
+ function patchReferenceSlot(member, patch, targetId) {
128921
+ patch.ids[patch.index] = targetId;
128922
+ const patched = patch.ids.filter((id2) => !isPendingId(id2));
128496
128923
  if (new Set(patched).size !== patched.length) {
128497
128924
  throw new Error(
128498
128925
  `Reference member ${member.name} cannot contain duplicate targets.`
128499
128926
  );
128500
128927
  }
128501
128928
  }
128929
+ function resolveValidIndexPathTargetId(context, member, spelling, ownerValueId, resolveRootPath) {
128930
+ const source = indexPathSourceV4(spelling);
128931
+ if (member.kind !== "lookup") {
128932
+ throw new Error(
128933
+ `Reference member ${member.name} is a dialogue lookup, and ${source} names a value row. Name the dialogue directly instead.`
128934
+ );
128935
+ }
128936
+ const resolution = resolveRootPath(spelling.listPath);
128937
+ if (resolution.kind === "unavailable") return null;
128938
+ if (resolution.kind === "failed") throw new Error(resolution.message);
128939
+ const listLabel = spelling.listPath.slice("root.".length);
128940
+ const list = indexPathList(context, resolution.valueId, resolution.memberId);
128941
+ if (list === null) {
128942
+ throw new Error(
128943
+ `${spelling.listPath} is not a List, so ${source} has no index to read.`
128944
+ );
128945
+ }
128946
+ const index = (list.indexes ?? []).find(
128947
+ (candidate) => candidate.schemaKey === spelling.indexSchemaKey
128948
+ );
128949
+ if (index === void 0) {
128950
+ throw new Error(
128951
+ `List ${listLabel} declares no index ${spelling.indexSchemaKey}.`
128952
+ );
128953
+ }
128954
+ if (!index.unique) {
128955
+ throw new Error(
128956
+ `Index ${listLabel}.Index.${spelling.indexSchemaKey} is not unique, so it can't name one row.`
128957
+ );
128958
+ }
128959
+ const matches = entriesWithIndexedKey(
128960
+ context,
128961
+ list,
128962
+ [resolution.valueId],
128963
+ index.schemaKey,
128964
+ spelling.key
128965
+ );
128966
+ if (matches.length > 1) {
128967
+ throw new Error(
128968
+ `List ${listLabel} holds ${matches.length} rows whose ${index.schemaKey} is ${JSON.stringify(spelling.key)}, but that index is unique. Fix the duplicate rows; ${source} needs exactly one.`
128969
+ );
128970
+ }
128971
+ const targetId = matches[0];
128972
+ if (targetId === void 0) throw new Error(`${source} names no row.`);
128973
+ const target = referenceTargetById(context, targetId, null);
128974
+ if (target === null) {
128975
+ throw new Error(
128976
+ `${source} resolved to ${targetId}, which names no value row in the project after this push.`
128977
+ );
128978
+ }
128979
+ validateReferenceContract(context, member, target, ownerValueId);
128980
+ return targetId;
128981
+ }
128982
+ function indexPathList(context, valueId, memberId) {
128983
+ const owners = memberId === null ? valueOwningMemberIds(context, valueId) : [memberId];
128984
+ for (const owner of owners) {
128985
+ const member = context.members.get(owner);
128986
+ if (member?.kind === "list") return member;
128987
+ }
128988
+ return null;
128989
+ }
128990
+ function entriesWithIndexedKey(context, collection, collectionValueIds, schemaKey, key) {
128991
+ const matches = [];
128992
+ for (const collectionValueId of collectionValueIds) {
128993
+ for (const entryId of collectionEntryIds(
128994
+ context,
128995
+ collection,
128996
+ collectionValueId
128997
+ )) {
128998
+ const entry = valueData(context, entryId);
128999
+ if (!entry || !isObjectRecord2(entry.value)) continue;
129000
+ const fieldRowId = entry.value[schemaKey];
129001
+ if (typeof fieldRowId !== "string") continue;
129002
+ if (valueData(context, fieldRowId)?.value !== key) continue;
129003
+ matches.push(entryId);
129004
+ }
129005
+ }
129006
+ return matches;
129007
+ }
128502
129008
  function resolveValidKeyReferenceTargetId(context, member, spelling, ownerValueId) {
128503
129009
  const targetId = resolveKeyReferenceTargetId(
128504
129010
  context,
@@ -128521,6 +129027,9 @@ function resolveKeyReferenceTargetId(context, member, spelling, ownerValueId) {
128521
129027
  `Reference member ${member.name} is a dialogue lookup, and the key form resolves through a value collection's unique index. Name the dialogue directly instead.`
128522
129028
  );
128523
129029
  }
129030
+ if (member.collectionMemberId === null) {
129031
+ throw unboundKeyReferenceError(context, spelling.typeName, member);
129032
+ }
128524
129033
  const collection = context.members.get(member.collectionMemberId);
128525
129034
  if (collection?.kind !== "list") {
128526
129035
  throw new Error(
@@ -128539,21 +129048,13 @@ function resolveKeyReferenceTargetId(context, member, spelling, ownerValueId) {
128539
129048
  `Reference member ${member.name} key ${JSON.stringify(spelling.key)} cannot be resolved: collection ${collectionMemberName2(context, member)} has no value row in the project after this push.`
128540
129049
  );
128541
129050
  }
128542
- const matches = [];
128543
- for (const collectionValueId of resolution.valueIds) {
128544
- for (const entryId of collectionEntryIds(
128545
- context,
128546
- collection,
128547
- collectionValueId
128548
- )) {
128549
- const entry = valueData(context, entryId);
128550
- if (!entry || !isObjectRecord2(entry.value)) continue;
128551
- const fieldRowId = entry.value[schemaKey];
128552
- if (typeof fieldRowId !== "string") continue;
128553
- if (valueData(context, fieldRowId)?.value !== spelling.key) continue;
128554
- matches.push(entryId);
128555
- }
128556
- }
129051
+ const matches = entriesWithIndexedKey(
129052
+ context,
129053
+ collection,
129054
+ resolution.valueIds,
129055
+ schemaKey,
129056
+ spelling.key
129057
+ );
128557
129058
  if (matches.length === 0) {
128558
129059
  throw new Error(
128559
129060
  `Reference member ${member.name} key ${JSON.stringify(spelling.key)} matches no row of collection ${collectionMemberName2(context, member)} under index ${schemaKey}.`
@@ -128708,9 +129209,9 @@ function resolveObligationTarget(context, spelling, deletedValueIds, subject) {
128708
129209
  spelling.patch.ids[spelling.patch.index] = id2;
128709
129210
  return target2;
128710
129211
  }
128711
- if (spelling.spelling === "key") {
129212
+ if (spelling.spelling === "key" || spelling.spelling === "indexPath") {
128712
129213
  throw new Error(
128713
- `${subject} carries a key spelling with no collection to resolve it against.`
129214
+ `${subject} carries a ${spelling.spelling} spelling that settleReferenceMember did not resolve.`
128714
129215
  );
128715
129216
  }
128716
129217
  const target = referenceTargetById(context, spelling.id, spelling.typeName);
@@ -128892,6 +129393,9 @@ function defaultRequiresOwnedRows(context, member, sourceExpression, source) {
128892
129393
  }
128893
129394
  function memberInitializerRequiresEvaluation(context, member, expression, source) {
128894
129395
  if (member.kind === "class" && member.partial === true) return false;
129396
+ if (member.kind === "lookup" && isIndexPathReferenceValue(expression)) {
129397
+ return false;
129398
+ }
128895
129399
  const unorderedDefault = source.purpose === "memberDefault" && member.id === source.memberId && member.kind === "list" && member.listKind === "unordered" && expression.kind === "litList" && expression.elements.length > 0;
128896
129400
  const requiresEvaluation = unorderedDefault || context.localValues.readsInstance(expression) || isSchemaMemberValueRead(context, expression, source) || isUnresolvedLiteralName(context, member, expression) || initializerRequiresEvaluation(
128897
129401
  context.declaredConstructors,
@@ -128901,6 +129405,21 @@ function memberInitializerRequiresEvaluation(context, member, expression, source
128901
129405
  );
128902
129406
  return requiresEvaluation;
128903
129407
  }
129408
+ function isIndexPathReferenceValue(sourceExpression) {
129409
+ const expression = annotatedValue(sourceExpression).expression;
129410
+ const elements = expression.kind === "litList" ? expression.elements : [expression];
129411
+ let indexPaths = 0;
129412
+ for (const element of elements) {
129413
+ if (parseIndexPathSpellingV4(element) !== null) {
129414
+ indexPaths += 1;
129415
+ continue;
129416
+ }
129417
+ if (element.kind !== "call" || element.callee.kind !== "ident" || element.callee.name !== "Reference") {
129418
+ return false;
129419
+ }
129420
+ }
129421
+ return indexPaths > 0;
129422
+ }
128904
129423
  function isSchemaMemberValueRead(context, expression, source) {
128905
129424
  const path = memberPath2(annotatedValue(expression).expression);
128906
129425
  if (path === null) return false;
@@ -132066,6 +132585,18 @@ function lowerReferences(context, member, expression, ownerValueId, source) {
132066
132585
  }
132067
132586
  const ids = [];
132068
132587
  for (const [index, entry] of elements.entries()) {
132588
+ const indexPath = member.kind === "lookup" ? parseIndexPathSpellingV4(entry) : null;
132589
+ if (indexPath !== null) {
132590
+ ids.push(`${PENDING_ID_PREFIX}reference-index:${source.label}:${index}`);
132591
+ context.referenceObligations.push({
132592
+ kind: "reference",
132593
+ site: referenceSite(source, entry),
132594
+ memberId: member.id,
132595
+ ownerValueId,
132596
+ target: { ...indexPath, patch: { ids, index } }
132597
+ });
132598
+ continue;
132599
+ }
132069
132600
  const target = referenceTargetSpelling(context, entry);
132070
132601
  if (target === null) {
132071
132602
  throw new Error(`Reference member ${member.name} has an invalid target.`);
@@ -132238,12 +132769,10 @@ function sourceValueSymbol(context, expression) {
132238
132769
  function resolveKeyReferenceMemberDefaultsV4(args) {
132239
132770
  const pending = /* @__PURE__ */ new Map();
132240
132771
  for (const member of args.manifest.members) {
132241
- const encoded = memberDefaultKeyPlaceholderInBodyV4(
132772
+ const placeholders = memberDefaultKeyPlaceholdersInBodyV4(
132242
132773
  memberDefaultBody(member)
132243
132774
  );
132244
- if (encoded === null) continue;
132245
- const spelling = decodeMemberDefaultKeyReferenceV4(encoded);
132246
- if (spelling !== null) pending.set(member.id, spelling);
132775
+ if (placeholders.length > 0) pending.set(member.id, placeholders);
132247
132776
  }
132248
132777
  if (pending.size === 0) return /* @__PURE__ */ new Map();
132249
132778
  const deletedValueIds = args.deletedValueIds ?? /* @__PURE__ */ new Set();
@@ -132257,21 +132786,13 @@ function resolveKeyReferenceMemberDefaultsV4(args) {
132257
132786
  })
132258
132787
  });
132259
132788
  const resolved = /* @__PURE__ */ new Map();
132260
- for (const [memberId, spelling] of pending) {
132789
+ for (const [memberId, placeholders] of pending) {
132261
132790
  const member = context.members.get(memberId);
132262
132791
  if (member?.kind !== "lookup" && member?.kind !== "dialogueLookup") {
132263
132792
  continue;
132264
132793
  }
132265
132794
  const site = args.siteForMember(memberId);
132266
- try {
132267
- const targetId = resolveValidKeyReferenceTargetId(
132268
- context,
132269
- member,
132270
- spelling,
132271
- null
132272
- );
132273
- resolved.set(memberId, targetId);
132274
- } catch (error) {
132795
+ const fail2 = (error) => {
132275
132796
  args.registry.loweringFailures.push({
132276
132797
  message: error instanceof Error ? error.message : String(error),
132277
132798
  site: {
@@ -132281,7 +132802,38 @@ function resolveKeyReferenceMemberDefaultsV4(args) {
132281
132802
  label: member.name
132282
132803
  }
132283
132804
  });
132805
+ };
132806
+ const targets = /* @__PURE__ */ new Map();
132807
+ for (const placeholder of placeholders) {
132808
+ const spelling = decodeMemberDefaultKeyReferenceV4(placeholder);
132809
+ if (spelling === null) continue;
132810
+ try {
132811
+ const targetId = spelling.spelling === "key" ? resolveValidKeyReferenceTargetId(context, member, spelling, null) : resolveValidIndexPathTargetId(
132812
+ context,
132813
+ member,
132814
+ spelling,
132815
+ null,
132816
+ args.resolveRootPath
132817
+ );
132818
+ if (targetId !== null) targets.set(placeholder, targetId);
132819
+ } catch (error) {
132820
+ fail2(error);
132821
+ }
132822
+ }
132823
+ const body = substituteMemberDefaultKeyReferenceV4(
132824
+ memberDefaultBody(member),
132825
+ targets
132826
+ );
132827
+ const ids = Array.isArray(body) ? body : [];
132828
+ if (new Set(ids).size !== ids.length) {
132829
+ fail2(
132830
+ new Error(
132831
+ `Reference member ${member.name} cannot contain duplicate targets.`
132832
+ )
132833
+ );
132834
+ continue;
132284
132835
  }
132836
+ if (targets.size > 0) resolved.set(memberId, targets);
132285
132837
  }
132286
132838
  return resolved;
132287
132839
  }
@@ -132399,6 +132951,9 @@ function initializerKeyLookup(context, target, site, initializer) {
132399
132951
  const storedBody = site.enclosing === null ? valueData(context, initializer.valueId)?.value : void 0;
132400
132952
  const storedFieldRow = isObjectRecord2(storedBody) ? storedBody[site.field] : void 0;
132401
132953
  const ownerValueId = typeof storedFieldRow === "string" ? storedFieldRow : null;
132954
+ if (member.collectionMemberId === null) {
132955
+ throw unboundKeyReferenceError(context, target.typeName, member);
132956
+ }
132402
132957
  const collection = context.members.get(member.collectionMemberId);
132403
132958
  if (ownerValueId === null && collection !== void 0 && classMemberIdByName(context, ownerClassId, collection.name) !== null) {
132404
132959
  throw new Error(
@@ -132477,6 +133032,10 @@ function validateReferenceContract(context, member, target, ownerValueId) {
132477
133032
  if (target.kind !== "value") {
132478
133033
  throw new Error(`Lookup member ${member.name} requires a value target.`);
132479
133034
  }
133035
+ if (member.collectionMemberId === null) {
133036
+ validateUnboundReferenceContract(context, member, target);
133037
+ return;
133038
+ }
132480
133039
  const expectedClassId = lookupEntryClassId2(context, member);
132481
133040
  const expectedTypeName = lookupEntryTypeName(context, member);
132482
133041
  const targetData = valueData(context, target.id);
@@ -132523,6 +133082,55 @@ function validateReferenceContract(context, member, target, ownerValueId) {
132523
133082
  }
132524
133083
  validateDialogueEligibility(context, member, target.id);
132525
133084
  }
133085
+ function validateUnboundReferenceContract(context, member, target) {
133086
+ const declaredClassId = unboundReferenceClassId(context, member);
133087
+ const rowClass = unboundTargetRowClass(context, target.id);
133088
+ if (rowClass.kind === "scalar") {
133089
+ throw new Error(
133090
+ `Lookup member ${member.name} target ${target.id} is not a class row. A reference selects a stored class value.`
133091
+ );
133092
+ }
133093
+ const actualClassId = rowClass.kind === "class" ? rowClass.classId : null;
133094
+ if (declaredClassId && actualClassId && !classAssignableToClass(context, actualClassId, declaredClassId)) {
133095
+ throw new Error(
133096
+ `Lookup member ${member.name} target ${target.id} is ${classDisplayName2(context, actualClassId)}, but the member declares ${classDisplayName2(context, declaredClassId)}. Point it at a ${classDisplayName2(context, declaredClassId)} row.`
133097
+ );
133098
+ }
133099
+ if (target.genericTypeName)
133100
+ context.referenceTypes().assertAssignable(
133101
+ target.id,
133102
+ target.genericTypeName,
133103
+ `Reference<${target.genericTypeName}>`
133104
+ );
133105
+ }
133106
+ function unboundTargetRowClass(context, valueId) {
133107
+ const pending = context.pendingValues.get(valueId);
133108
+ if (pending?.nodeType === "literal") {
133109
+ return pending.classId === null ? { kind: "scalar" } : { kind: "class", classId: pending.classId };
133110
+ }
133111
+ const data = valueData(context, valueId);
133112
+ if (typeof data?.classId === "string")
133113
+ return { kind: "class", classId: data.classId };
133114
+ const placing = pending === void 0 ? storedRowPlacementMember(context, valueId) : memberById(context, pending.memberId);
133115
+ if (placing === null) return { kind: "unknown" };
133116
+ if (placing.kind === "class")
133117
+ return { kind: "class", classId: placing.classId };
133118
+ return placing.kind === "generic" || placing.kind === "variant" ? { kind: "unknown" } : { kind: "scalar" };
133119
+ }
133120
+ function unboundReferenceClassId(context, member) {
133121
+ const declared = member.declaredType;
133122
+ if (declared?.kind !== "generic")
133123
+ return lookupDeclaredClassId2(context, member);
133124
+ const constraint = context.classes.get(declared.ownerClassId)?.genericParameters.find(
133125
+ (parameter4) => parameter4.id === declared.genericParamId
133126
+ )?.constraint;
133127
+ return constraint?.kind === "class" ? constraint.classId : null;
133128
+ }
133129
+ function unboundKeyReferenceError(context, typeName, member) {
133130
+ return new Error(
133131
+ `Reference<${typeName}>(key:) resolves through the member's collection, and ${qualifiedMemberName(context, member)} has no collection. Use an index path, id:, or bind a collection.`
133132
+ );
133133
+ }
132526
133134
  function lookupDeclaredClassId2(context, member) {
132527
133135
  const declared = member.declaredType;
132528
133136
  if (declared === null) return null;
@@ -132533,6 +133141,7 @@ function classDisplayName2(context, classId) {
132533
133141
  return context.classes.get(classId)?.name ?? classId;
132534
133142
  }
132535
133143
  function lookupEntryTypeName(context, member) {
133144
+ if (member.collectionMemberId === null) return null;
132536
133145
  const collection = context.members.get(member.collectionMemberId);
132537
133146
  const entry = collection?.kind === "list" || collection?.kind === "dictionary" ? context.members.get(collection.entryMemberId) : null;
132538
133147
  if (!entry) return null;
@@ -132558,6 +133167,7 @@ function referenceTypeNameAssignable(context, actualName, expectedName) {
132558
133167
  ) : false;
132559
133168
  }
132560
133169
  function validateLookupMembership(context, member, targetId, ownerValueId) {
133170
+ if (member.collectionMemberId === null) return;
132561
133171
  const collection = context.members.get(member.collectionMemberId);
132562
133172
  if (!collection || collection.kind !== "list" && collection.kind !== "dictionary") {
132563
133173
  throw new Error(
@@ -132592,18 +133202,28 @@ function validateLookupMembership(context, member, targetId, ownerValueId) {
132592
133202
  );
132593
133203
  }
132594
133204
  function collectionMemberName2(context, member) {
133205
+ if (member.collectionMemberId === null) {
133206
+ throw new Error(`Lookup member ${member.name} has no collection.`);
133207
+ }
132595
133208
  const collection = context.members.get(member.collectionMemberId);
132596
133209
  if (collection === void 0) return member.collectionMemberId;
132597
- const owner = collection.owner;
132598
- if (owner.kind !== "classMember") return collection.name;
133210
+ return qualifiedMemberName(context, collection);
133211
+ }
133212
+ function qualifiedMemberName(context, member) {
133213
+ const owner = member.owner;
133214
+ if (owner.kind !== "classMember") return member.name;
132599
133215
  const ownerClass = context.classes.get(owner.classId);
132600
- if (ownerClass === void 0) return collection.name;
132601
- return `${ownerClass.name}.${collection.name}`;
133216
+ if (ownerClass === void 0) return member.name;
133217
+ return `${ownerClass.name}.${member.name}`;
132602
133218
  }
132603
133219
  function resolveLookupCollectionValueIds(context, member, collection, ownerValueId) {
132604
133220
  if (typeof member.collectionValueId === "string") {
132605
133221
  return { valueIds: [member.collectionValueId], origin: "declared" };
132606
133222
  }
133223
+ const collectionMemberId = member.collectionMemberId;
133224
+ if (collectionMemberId === null) {
133225
+ throw new Error(`Lookup member ${member.name} has no collection.`);
133226
+ }
132607
133227
  if (ownerValueId !== null) {
132608
133228
  const container = context.valuePlacements.containerBodyByPlacement.get(
132609
133229
  `${member.name}:${ownerValueId}`
@@ -132615,11 +133235,11 @@ function resolveLookupCollectionValueIds(context, member, collection, ownerValue
132615
133235
  }
132616
133236
  const declaredRows = collectionRowsOfDeclaredMember(
132617
133237
  context,
132618
- member.collectionMemberId,
133238
+ collectionMemberId,
132619
133239
  collection.name
132620
133240
  );
132621
133241
  const candidates = /* @__PURE__ */ new Set();
132622
- const collectionState = context.state[`member:${member.collectionMemberId}`];
133242
+ const collectionState = context.state[`member:${collectionMemberId}`];
132623
133243
  const collectionMemberData = isObjectRecord2(collectionState?.data) ? collectionState.data : {};
132624
133244
  if (typeof collectionMemberData.valueId === "string") {
132625
133245
  candidates.add(collectionMemberData.valueId);
@@ -132827,6 +133447,7 @@ function validateDialogueEligibility(context, member, dialogueId) {
132827
133447
  }
132828
133448
  }
132829
133449
  function lookupEntryClassId2(context, member) {
133450
+ if (member.collectionMemberId === null) return null;
132830
133451
  const collection = context.members.get(member.collectionMemberId);
132831
133452
  const entry = collection?.kind === "list" || collection?.kind === "dictionary" ? context.members.get(collection.entryMemberId) : null;
132832
133453
  return entry?.kind === "class" ? entry.classId : null;
@@ -134574,9 +135195,17 @@ function schemaTypeForBindingMember(context, memberId, environment, visited = /*
134574
135195
  readOnly: false
134575
135196
  };
134576
135197
  case "lookup": {
135198
+ if (member.collectionMemberId === null && member.declaredType !== null) {
135199
+ return member.multiselect ? { kind: "lookup", nullable: nullable2, entryType: member.declaredType } : withSchemaTypeNullability(member.declaredType, nullable2);
135200
+ }
134577
135201
  if (member.declaredType !== null) {
134578
135202
  return withSchemaTypeNullability(member.declaredType, nullable2);
134579
135203
  }
135204
+ if (member.collectionMemberId === null) {
135205
+ throw new Error(
135206
+ `Lookup member ${member.name} has neither a collection nor a declared type.`
135207
+ );
135208
+ }
134580
135209
  const collection = context.manifestMembers.get(member.collectionMemberId);
134581
135210
  if (collection?.kind !== "list" && collection?.kind !== "dictionary") {
134582
135211
  throw new Error(
@@ -135326,21 +135955,31 @@ function referenceValue(context, member, body, dialogue) {
135326
135955
  }
135327
135956
  const symbol = context.localValues?.byValueId.get(id2)?.name ?? context.symbolsByValueId.get(id2);
135328
135957
  if (symbol !== void 0) return `Reference(${symbol})`;
135329
- const type = dialogue ? "NeoDialogue" : memberReferenceType(context, member);
135958
+ const type = dialogue ? "NeoDialogue" : unboundReferenceTypeName(context, member, id2) ?? memberReferenceType(context, member);
135330
135959
  if (!dialogue) {
135331
- const key = uniqueIndexKeyForTarget(context, member, id2);
135332
- if (key !== null) return `Reference<${type}>(key: ${quote3(key)})`;
135960
+ const manifestMember = context.manifestMembers.get(
135961
+ stringField2(member, "id")
135962
+ );
135963
+ if (manifestMember?.kind === "lookup") {
135964
+ if (manifestMember.collectionMemberId === null) {
135965
+ const path = indexPathForTarget(context, manifestMember, id2);
135966
+ if (path !== null) return path;
135967
+ } else {
135968
+ const key = uniqueIndexKeyForTarget(
135969
+ context,
135970
+ manifestMember.collectionMemberId,
135971
+ id2
135972
+ );
135973
+ if (key !== null) return `Reference<${type}>(key: ${quote3(key)})`;
135974
+ }
135975
+ }
135333
135976
  }
135334
135977
  return `Reference<${type}>(id: ${quote3(id2)})`;
135335
135978
  });
135336
135979
  return isMultiSelectionMember(member) ? `[${values.join(", ")}]` : values[0] ?? "null";
135337
135980
  }
135338
- function uniqueIndexKeyForTarget(context, member, targetId) {
135339
- const manifestMember = context.manifestMembers.get(stringField2(member, "id"));
135340
- if (manifestMember?.kind !== "lookup") return null;
135341
- const collection = context.manifestMembers.get(
135342
- manifestMember.collectionMemberId
135343
- );
135981
+ function uniqueIndexKeyForTarget(context, collectionMemberId, targetId) {
135982
+ const collection = context.manifestMembers.get(collectionMemberId);
135344
135983
  if (collection?.kind !== "list") return null;
135345
135984
  const unique = (collection.indexes ?? []).filter((index) => index.unique);
135346
135985
  const only = unique.length === 1 ? unique[0] : void 0;
@@ -135356,6 +135995,51 @@ function uniqueIndexKeyForTarget(context, member, targetId) {
135356
135995
  }
135357
135996
  return holders === 1 ? key : null;
135358
135997
  }
135998
+ function indexPathForTarget(context, member, targetId) {
135999
+ const path = context.readRootIndexPaths().get(targetId);
136000
+ if (path === void 0) return null;
136001
+ const unwrap = member.multiselect || member.required ? "!" : "";
136002
+ return `${path}${unwrap}`;
136003
+ }
136004
+ function indexRootIndexPaths(values, manifestMembers, placements, rootPaths) {
136005
+ const lists = /* @__PURE__ */ new Map();
136006
+ for (const [valueId, path] of rootPaths) {
136007
+ const memberId = placements.placementByChildValueId.get(valueId)?.memberId;
136008
+ const list = memberId === null || memberId === void 0 ? void 0 : manifestMembers.get(memberId);
136009
+ if (list?.kind !== "list") continue;
136010
+ const index = (list.indexes ?? []).find((candidate) => candidate.unique);
136011
+ if (index === void 0) continue;
136012
+ lists.set(valueId, { path, list, schemaKey: index.schemaKey });
136013
+ }
136014
+ const paths = /* @__PURE__ */ new Map();
136015
+ if (lists.size === 0) return paths;
136016
+ const unorderedEntries = /* @__PURE__ */ new Map();
136017
+ for (const [id2, row] of values) {
136018
+ const containerId = row.containerId;
136019
+ if (typeof containerId !== "string") continue;
136020
+ if (lists.get(containerId)?.list.listKind !== "unordered") continue;
136021
+ const contained = unorderedEntries.get(containerId) ?? [];
136022
+ contained.push(id2);
136023
+ unorderedEntries.set(containerId, contained);
136024
+ }
136025
+ const context = { values };
136026
+ for (const [listValueId, { path, list, schemaKey }] of lists) {
136027
+ const body = values.get(listValueId)?.value;
136028
+ const entryIds = list.listKind === "unordered" ? unorderedEntries.get(listValueId) ?? [] : Array.isArray(body) ? body.filter((entry) => typeof entry === "string") : [];
136029
+ const entryByKey = /* @__PURE__ */ new Map();
136030
+ for (const id2 of entryIds) {
136031
+ const key = indexedFieldValue(context, id2, schemaKey);
136032
+ if (key === null || key.length === 0) continue;
136033
+ entryByKey.set(key, entryByKey.has(key) ? null : id2);
136034
+ }
136035
+ for (const [key, id2] of entryByKey) {
136036
+ if (id2 !== null) {
136037
+ paths.set(id2, `${path}.Index.${schemaKey}[${quote3(key)}]`);
136038
+ }
136039
+ }
136040
+ }
136041
+ return paths;
136042
+ }
135359
136043
  function indexedFieldValue(context, rowId, schemaKey) {
135360
136044
  const row = context.values.get(rowId);
135361
136045
  const body = row?.value;
@@ -135577,6 +136261,15 @@ function projectMainLocale(records3) {
135577
136261
  );
135578
136262
  return isObjectRecord2(config?.data) && typeof config.data.mainLocale === "string" ? config.data.mainLocale : "en-US";
135579
136263
  }
136264
+ function unboundReferenceTypeName(context, member, targetId) {
136265
+ const manifestMember = context.manifestMembers.get(stringField2(member, "id"));
136266
+ if (manifestMember?.kind !== "lookup") return null;
136267
+ if (manifestMember.collectionMemberId !== null) return null;
136268
+ const declared = manifestMember.declaredType;
136269
+ const classId = declared?.kind === "class" ? declared.classId : context.values.get(targetId)?.classId;
136270
+ if (typeof classId !== "string") return null;
136271
+ return context.manifestClasses.get(classId)?.name ?? null;
136272
+ }
135580
136273
  function memberReferenceType(context, member) {
135581
136274
  if (typeof member.classId === "string") {
135582
136275
  const target = context.classes.get(member.classId);
@@ -140338,6 +141031,39 @@ function resolveRootPathVariantFolderCollectionValuesV4(args) {
140338
141031
  }
140339
141032
  return resolved;
140340
141033
  }
141034
+ function createRootValuePathResolverV4(args) {
141035
+ let placements;
141036
+ let rootValues;
141037
+ return (path) => {
141038
+ placements ??= prospectiveStoredValuePlacements(
141039
+ args.state,
141040
+ args.registry,
141041
+ args.manifest
141042
+ );
141043
+ if (rootValues === void 0) rootValues = rootSlotValueIds(args.state);
141044
+ const failures = [];
141045
+ const valueId = walkRootValuePath(
141046
+ path,
141047
+ args.state,
141048
+ args.registry,
141049
+ placements,
141050
+ rootValues,
141051
+ (failure) => failures.push(failure),
141052
+ args.resolveVirtualPath,
141053
+ args.unavailableSlots,
141054
+ "Index path"
141055
+ );
141056
+ if (valueId !== null) {
141057
+ return {
141058
+ kind: "resolved",
141059
+ valueId,
141060
+ memberId: placements.placementByChildValueId.get(valueId)?.memberId ?? null
141061
+ };
141062
+ }
141063
+ const message = failures[0];
141064
+ return message === void 0 ? { kind: "unavailable" } : { kind: "failed", message };
141065
+ };
141066
+ }
140341
141067
  function resolveRootPathDialogueGroupCollectionValuesV4(args) {
140342
141068
  let placements;
140343
141069
  let rootValues;
@@ -140389,12 +141115,12 @@ function rootSlotValueIds(state) {
140389
141115
  }
140390
141116
  return values;
140391
141117
  }
140392
- function walkRootValuePath(path, state, registry, placements, rootValues, fail2, resolveVirtualPath, unavailableSlots) {
141118
+ function walkRootValuePath(path, state, registry, placements, rootValues, fail2, resolveVirtualPath, unavailableSlots, subject = "collectionValue path") {
140393
141119
  const segments = path.split(".");
140394
141120
  const slotName = segments[1];
140395
141121
  if (segments[0] !== "root" || slotName === void 0) {
140396
141122
  fail2(
140397
- `collectionValue path ${path} must start at a root slot (root.Assets, root.Save, root.Session, or root.User).`
141123
+ `${subject} ${path} must start at a root slot (root.Assets, root.Save, root.Session, or root.User).`
140398
141124
  );
140399
141125
  return null;
140400
141126
  }
@@ -140404,14 +141130,14 @@ function walkRootValuePath(path, state, registry, placements, rootValues, fail2,
140404
141130
  );
140405
141131
  if (slotIndex < 0 || rootValues === null) {
140406
141132
  fail2(
140407
- `collectionValue path ${path} names root slot ${slotName ?? "<none>"}, which is not Assets, Save, Session, or User.`
141133
+ `${subject} ${path} names root slot ${slotName ?? "<none>"}, which is not Assets, Save, Session, or User.`
140408
141134
  );
140409
141135
  return null;
140410
141136
  }
140411
141137
  const slotValueId = rootValues.get(slotName);
140412
141138
  if (typeof slotValueId !== "string") {
140413
141139
  fail2(
140414
- `collectionValue path ${path} cannot start: root slot ${slotName} has no stable value row.`
141140
+ `${subject} ${path} cannot start: root slot ${slotName} has no stable value row.`
140415
141141
  );
140416
141142
  return null;
140417
141143
  }
@@ -140421,13 +141147,13 @@ function walkRootValuePath(path, state, registry, placements, rootValues, fail2,
140421
141147
  const body = (pending?.nodeType === "literal" ? pending.value : void 0) ?? (isObjectRecord2(state[`value:${cursor}`]?.data) ? (state[`value:${cursor}`]?.data).value : void 0);
140422
141148
  if (Array.isArray(body)) {
140423
141149
  fail2(
140424
- `collectionValue path ${path} travels through a list at segment ${segment}; a path cannot choose a list entry. Name the row with Reference(id: ...) instead.`
141150
+ `${subject} ${path} travels through a list at segment ${segment}; a path cannot choose a list entry. Name the row with Reference(id: ...) instead.`
140425
141151
  );
140426
141152
  return null;
140427
141153
  }
140428
141154
  if (!isObjectRecord2(body)) {
140429
141155
  fail2(
140430
- `collectionValue path ${path} stops before segment ${segment}: the row it reaches stores no single-valued members.`
141156
+ `${subject} ${path} stops before segment ${segment}: the row it reaches stores no single-valued members.`
140431
141157
  );
140432
141158
  return null;
140433
141159
  }
@@ -140438,12 +141164,12 @@ function walkRootValuePath(path, state, registry, placements, rootValues, fail2,
140438
141164
  if (resolved != null) return resolved;
140439
141165
  } catch (error) {
140440
141166
  fail2(
140441
- `collectionValue path ${path} could not resolve its construction: ${error instanceof Error ? error.message : String(error)}`
141167
+ `${subject} ${path} could not resolve its construction: ${error instanceof Error ? error.message : String(error)}`
140442
141168
  );
140443
141169
  return null;
140444
141170
  }
140445
141171
  fail2(
140446
- `collectionValue path ${path} has no value at segment ${segment} after this push.`
141172
+ `${subject} ${path} has no value at segment ${segment} after this push.`
140447
141173
  );
140448
141174
  return null;
140449
141175
  }
@@ -142319,6 +143045,7 @@ function emitProjectDocumentSourcesV4(records3, options = {}) {
142319
143045
  fileSymbols: qualifiedProjectFileSymbolsV4(indexes3),
142320
143046
  relationEndpointExpressions: relationEndpointExpressions(records3),
142321
143047
  collectionValuePaths: indexes3.rootPathsByValueId,
143048
+ referenceTargetSource: (member, targetId) => indexPathForTarget(indexes3, member, targetId),
142322
143049
  variantInitializers: variantValues.initializers,
142323
143050
  listenerStatementsByMember: listenerStatements
142324
143051
  });
@@ -143450,10 +144177,18 @@ function computeStatus(workspace, options, conflictedFiles, parseErrors, parseWa
143450
144177
  }
143451
144178
  }
143452
144179
  }
144180
+ const resolveRootPath = createRootValuePathResolverV4({
144181
+ manifest,
144182
+ state: rootPathResolutionState,
144183
+ resolveVirtualPath,
144184
+ unavailableSlots: rootValues.unavailableSlots,
144185
+ registry: valueLowerRegistry
144186
+ });
143453
144187
  const resolvedDefaultKeys = resolveKeyReferenceMemberDefaultsV4({
143454
144188
  manifest,
143455
144189
  state: rootPathResolutionState,
143456
144190
  registry: valueLowerRegistry,
144191
+ resolveRootPath,
143457
144192
  ...projectAnalysisV4 === null ? {} : { analysis: projectAnalysisV4 },
143458
144193
  siteForMember: (memberId) => {
143459
144194
  const span2 = sourceByKey.get(recordStateKey("member", memberId))?.span;
@@ -143464,8 +144199,8 @@ function computeStatus(workspace, options, conflictedFiles, parseErrors, parseWa
143464
144199
  manifest = {
143465
144200
  ...manifest,
143466
144201
  members: manifest.members.map((member) => {
143467
- const resolvedId = resolvedDefaultKeys.get(member.id);
143468
- if (resolvedId === void 0) return member;
144202
+ const resolvedTargets = resolvedDefaultKeys.get(member.id);
144203
+ if (resolvedTargets === void 0) return member;
143469
144204
  const defaultValue = member.defaultValue;
143470
144205
  if (defaultValue === null || defaultValue === void 0) return member;
143471
144206
  if (!("value" in defaultValue)) return member;
@@ -143475,20 +144210,20 @@ function computeStatus(workspace, options, conflictedFiles, parseErrors, parseWa
143475
144210
  ...defaultValue,
143476
144211
  value: substituteMemberDefaultKeyReferenceV4(
143477
144212
  defaultValue.value,
143478
- resolvedId
144213
+ resolvedTargets
143479
144214
  )
143480
144215
  }
143481
144216
  };
143482
144217
  })
143483
144218
  };
143484
- for (const [memberId, resolvedId] of resolvedDefaultKeys) {
144219
+ for (const [memberId, resolvedTargets] of resolvedDefaultKeys) {
143485
144220
  const document = documentsByKey.get(recordStateKey("member", memberId));
143486
144221
  if (document !== void 0 && isObjectRecord2(document.data)) {
143487
144222
  const stored = document.data.defaultValue;
143488
144223
  if (isObjectRecord2(stored) && "value" in stored) {
143489
144224
  stored.value = substituteMemberDefaultKeyReferenceV4(
143490
144225
  stored.value,
143491
- resolvedId
144226
+ resolvedTargets
143492
144227
  );
143493
144228
  }
143494
144229
  }
@@ -143497,7 +144232,7 @@ function computeStatus(workspace, options, conflictedFiles, parseErrors, parseWa
143497
144232
  if (isObjectRecord2(authored) && "value" in authored) {
143498
144233
  authored.value = substituteMemberDefaultKeyReferenceV4(
143499
144234
  authored.value,
143500
- resolvedId
144235
+ resolvedTargets
143501
144236
  );
143502
144237
  }
143503
144238
  }
@@ -143739,7 +144474,16 @@ function computeStatus(workspace, options, conflictedFiles, parseErrors, parseWa
143739
144474
  deletedValueIds,
143740
144475
  unavailableReferenceMemberIds,
143741
144476
  unavailableValueIds: rootValues.unavailableValueIds,
143742
- sourceTextByUri
144477
+ sourceTextByUri,
144478
+ // Built again here: later passes add rows the first resolver's graph
144479
+ // snapshot does not hold.
144480
+ resolveRootPath: createRootValuePathResolverV4({
144481
+ manifest,
144482
+ state: rootPathResolutionState,
144483
+ resolveVirtualPath,
144484
+ unavailableSlots: rootValues.unavailableSlots,
144485
+ registry: valueLowerRegistry
144486
+ })
143743
144487
  }
143744
144488
  );
143745
144489
  for (const failure of referenceFailures) {
@@ -149556,7 +150300,8 @@ function expectedTypeAt(snapshot, offset, body = lambdaBodyAt(snapshot, offset),
149556
150300
  const token = tokens[index];
149557
150301
  if (token.kind !== "operator" || token.text !== "=") continue;
149558
150302
  const local = features.locals.get(tokens[index - 1]?.start ?? -1);
149559
- const type = local && !local.inferred && !local.collectionIteration ? resolveDeclaredType(local, snapshot) : resolveChain(snapshot, expressionTokensBefore(tokens, index), offset)?.type;
150303
+ if (local?.inferred) break;
150304
+ const type = local && !local.collectionIteration ? resolveDeclaredType(local, snapshot) : resolveChain(snapshot, expressionTokensBefore(tokens, index), offset)?.type;
149560
150305
  if (type) {
149561
150306
  return expectedCollectionElementType(type, tokens, token.end, offset);
149562
150307
  }
@@ -150382,7 +151127,19 @@ function resolveChain(snapshot, tokens, offset, expectedType) {
150382
151127
  const staticRoot = tokens[0]?.kind === "type" ? snapshot.project.typeByName.get(tokens[0].text) : void 0;
150383
151128
  let current;
150384
151129
  let cursor;
150385
- if (tokens[0]?.text === "[" || tokens[0]?.text === "{") {
151130
+ if (tokens[0]?.text === "(") {
151131
+ const close = matchingDelimiterIndex(tokens, 0);
151132
+ if (close < 0) return null;
151133
+ current = {
151134
+ type: inferExpressionType2(
151135
+ tokens.slice(1, close),
151136
+ snapshot,
151137
+ offset,
151138
+ expectedType
151139
+ )
151140
+ };
151141
+ cursor = close + 1;
151142
+ } else if (tokens[0]?.text === "[" || tokens[0]?.text === "{") {
150386
151143
  const close = matchingDelimiterIndex(tokens, 0);
150387
151144
  if (close < 0) return null;
150388
151145
  current = {
@@ -150395,6 +151152,21 @@ function resolveChain(snapshot, tokens, offset, expectedType) {
150395
151152
  staticType: staticRoot
150396
151153
  };
150397
151154
  cursor = 1;
151155
+ if (staticRoot.id === NEOSCRIPT_LIST_TYPE_ID && tokens[1]?.text === "." && tokens[2]?.text === NEOSCRIPT_LIST_REPEAT_NAME) {
151156
+ const open = callOpenParenIndex(tokens, 2);
151157
+ const close = open < 0 ? -1 : findClosingToken(tokens, open, "(", ")");
151158
+ if (close >= 0) {
151159
+ current = {
151160
+ type: inferListRepeatType(
151161
+ snapshot,
151162
+ tokens.slice(open + 1, close),
151163
+ open > 3 ? callTypeArgumentTokens(tokens, 2, open) : void 0,
151164
+ close === tokens.length - 1 ? expectedType : void 0
151165
+ )
151166
+ };
151167
+ cursor = close + 1;
151168
+ }
151169
+ }
150398
151170
  } else if (contextualConstructor || constructorType?.constructorSignature) {
150399
151171
  current = {
150400
151172
  type: contextualConstructor ? expectedType : constructedTypeRef(snapshot, constructorType, tokens, 1)
@@ -150463,10 +151235,7 @@ function resolveChain(snapshot, tokens, offset, expectedType) {
150463
151235
  fallbackType,
150464
151236
  current.symbol?.name === "SelectMany"
150465
151237
  ) : fallbackType;
150466
- current = {
150467
- type: callType,
150468
- ...current.symbol ? { symbol: current.symbol } : {}
150469
- };
151238
+ current = { type: callType };
150470
151239
  cursor = close < 0 ? tokens.length : close + 1;
150471
151240
  continue;
150472
151241
  }
@@ -150485,6 +151254,32 @@ function resolveChain(snapshot, tokens, offset, expectedType) {
150485
151254
  }
150486
151255
  return current;
150487
151256
  }
151257
+ function inferListRepeatType(snapshot, argumentTokens, typeArgumentTokens, expectedType) {
151258
+ const comma = topLevelTokenIndex(
151259
+ argumentTokens,
151260
+ (token) => token.text === ","
151261
+ );
151262
+ const valueTokens = comma < 0 ? argumentTokens : argumentTokens.slice(0, comma);
151263
+ if (!valueTokens[0]) return UNKNOWN_TYPE;
151264
+ let expectedEntry = expectedType?.kind === "list" ? expectedType.elementType : void 0;
151265
+ if (typeArgumentTokens) {
151266
+ const parser = new Parser(typeArgumentTokens);
151267
+ if (parser.typeEnd() !== typeArgumentTokens.length) return UNKNOWN_TYPE;
151268
+ expectedEntry = resolveAuthoredType(parser, snapshot) ?? UNKNOWN_TYPE;
151269
+ }
151270
+ const value = inferExpressionType2(
151271
+ valueTokens,
151272
+ snapshot,
151273
+ valueTokens[0].start,
151274
+ expectedEntry
151275
+ );
151276
+ const elementType = commonNeoScriptAssignableType(
151277
+ expectedEntry ?? value,
151278
+ value,
151279
+ snapshot.project
151280
+ );
151281
+ return elementType ? { kind: "list", elementType } : UNKNOWN_TYPE;
151282
+ }
150488
151283
  function inferSelectResultType(argumentTokens, snapshot, fallbackType, flatten = false) {
150489
151284
  const arrow = topLevelTokenIndex(
150490
151285
  argumentTokens,
@@ -151002,7 +151797,16 @@ function inferExpressionType2(rawTokens, snapshot, offset, expectedType) {
151002
151797
  if (first.kind === "operator" && (first.text === "!" || first.text === "-")) {
151003
151798
  return first.text === "!" ? { kind: "primitive", name: "bool" } : inferExpressionType2(tokens.slice(1), snapshot, offset);
151004
151799
  }
151005
- return resolveChain(snapshot, tokens, offset, expectedType)?.type ?? UNKNOWN_TYPE;
151800
+ const resolved = resolveChain(snapshot, tokens, offset, expectedType);
151801
+ const symbol = resolved?.symbol;
151802
+ if (symbol?.kind === "method" || symbol?.kind === "function") {
151803
+ return {
151804
+ kind: "delegate",
151805
+ parameterTypes: symbol.parameters?.map((parameter4) => parameter4.type) ?? [],
151806
+ returnType: symbol.returnType ?? symbol.type
151807
+ };
151808
+ }
151809
+ return resolved?.type ?? UNKNOWN_TYPE;
151006
151810
  }
151007
151811
  function unwrapExpressionTokens(tokens) {
151008
151812
  let result = tokens;
@@ -153742,11 +154546,14 @@ ${resolved.symbol.documentation}` : "";
153742
154546
 
153743
154547
  ID: \`${resolved.symbol.id}\`` : "\n\nPending ID";
153744
154548
  const detail = resolved.symbol.detail ? `: \`${resolved.symbol.detail}\`` : "";
154549
+ const reference2 = resolved.symbol.reference ? `
154550
+
154551
+ \`${resolved.symbol.reference.storage}\` reads as \`${resolved.symbol.reference.readsAs}\`.` : "";
153745
154552
  const contract = resolved.symbol.kind === "class" ? projectConstructionContractMarkdown(analysis, resolved.symbol.name) : "";
153746
154553
  const lifecycle = resolved.symbol.kind === "member" && resolved.symbol.callable === true && resolved.symbol.static !== true && analysis.documents.get(resolved.symbol.location.uri)?.kind !== "flow" ? lifecycleHoverMarkdown(analysis, resolved.symbol.name) : "";
153747
154554
  return {
153748
154555
  range: resolved.token.range,
153749
- markdown: `**${resolved.symbol.kind}** \`${resolved.symbol.name}\`${detail}${owner}${documentation}${lifecycle}${contract}${id2}`
154556
+ markdown: `**${resolved.symbol.kind}** \`${resolved.symbol.name}\`${detail}${owner}${reference2}${documentation}${lifecycle}${contract}${id2}`
153750
154557
  };
153751
154558
  }
153752
154559
  function projectBuiltinSymbolAt(document, position) {
@@ -156712,6 +157519,7 @@ var init_service = __esm({
156712
157519
  init_project_source_identifiers();
156713
157520
  init_project_source_analysis();
156714
157521
  init_project_source_construction_quick_fixes();
157522
+ init_project_source_reference_spelling();
156715
157523
  init_project_source_lifecycle_editor();
156716
157524
  init_project_source_reference_family();
156717
157525
  init_project_source_language_features();
@@ -157109,6 +157917,13 @@ var init_service = __esm({
157109
157917
  actions.push(
157110
157918
  ...projectConstructionQuickFixes(project, state.document, diagnostic)
157111
157919
  );
157920
+ actions.push(
157921
+ ...projectReferenceSpellingQuickFixes(
157922
+ project,
157923
+ state.document,
157924
+ diagnostic
157925
+ )
157926
+ );
157112
157927
  actions.push(
157113
157928
  ...projectLifecycleQuickFixes(
157114
157929
  project,
@@ -164216,13 +165031,15 @@ function storedMemberKindInfo(member, members, genericEnv) {
164216
165031
  };
164217
165032
  }
164218
165033
  if (isMemberLookupBase(resolved)) {
164219
- const collection = members.find(
164220
- (candidate) => candidate.id === resolved.collectionMemberId
164221
- );
164222
- if (!collection) return null;
164223
165034
  const declaredInfo = lookupDeclaredTypeInfo2(
164224
165035
  resolved,
164225
- storedMemberKindInfo(collection, members, genericEnv) ?? null
165036
+ (collectionMemberId) => {
165037
+ const collection = members.find(
165038
+ (candidate) => candidate.id === collectionMemberId
165039
+ );
165040
+ if (!collection) return null;
165041
+ return storedMemberKindInfo(collection, members, genericEnv) ?? null;
165042
+ }
164226
165043
  );
164227
165044
  if (declaredInfo === null || genericEnv === void 0) return declaredInfo;
164228
165045
  return substituteTypeInfoForConformance(
@@ -169970,8 +170787,7 @@ function staticStoredSetter(ctx, member, memberId, storage) {
169970
170787
  return [...lines, "binding.BindValue(value);"];
169971
170788
  }
169972
170789
  if (isMemberLookupBase(member)) {
169973
- const entryMember = resolveLookupEntryMember(ctx, member);
169974
- const classTarget = entryMember !== null && isMemberClassBase(entryMember);
170790
+ const classTarget = lookupTargetsClass(ctx, member);
169975
170791
  if (isMultiSelectionMember(member)) {
169976
170792
  return [
169977
170793
  ...lines,
@@ -170015,7 +170831,7 @@ function staticLookupGetter(ctx, member, memberId, storage, saved, valueType, ge
170015
170831
  if (targetClass !== null) {
170016
170832
  const typeName = ctx.typeNames.get(targetClass.classId) ?? "NeoNode";
170017
170833
  const typeRef = `${qualifyGeneratedType(ctx, savedTypeName(typeName))}${targetClass.constructedSuffix}`;
170018
- factory = targetSaved ? `${typeRef}.CreateWritable(client, (NeoMemberClassWritable)__selected__)` : `${typeRef}.Create(client, (NeoMemberClass)__selected__)`;
170834
+ factory = lookupHasRuntimeOwnership(member) ? `__selected__ is NeoMemberClassWritable writableSelected ? ${typeRef}.CreateWritable(client, writableSelected) : ${typeRef}.Create(client, (NeoMemberClass)__selected__)` : targetSaved ? `${typeRef}.CreateWritable(client, (NeoMemberClassWritable)__selected__)` : `${typeRef}.Create(client, (NeoMemberClass)__selected__)`;
170019
170835
  } else {
170020
170836
  factory = 'new NeoLookupSelection(__selected__.overrideValueId ?? throw new InvalidOperationException("Lookup selected a value without an id."))';
170021
170837
  }
@@ -171916,8 +172732,7 @@ function writeExpression(ctx, member, schemaKey, ownerClassName, ownerClassId) {
171916
172732
  ];
171917
172733
  }
171918
172734
  if (isMemberLookupBase(member)) {
171919
- const entryMember = resolveLookupEntryMember(ctx, member);
171920
- const classTarget = entryMember !== null && isMemberClassBase(entryMember);
172735
+ const classTarget = lookupTargetsClass(ctx, member);
171921
172736
  return isMultiSelectionMember(member) ? [
171922
172737
  "var ids = new List<string>();",
171923
172738
  "if (value is not null)",
@@ -172099,6 +172914,8 @@ function lookupSelectionFactory(ctx, member, selection2, saved, writeGuardMember
172099
172914
  const typeName = ctx.typeNames.get(targetClass.classId) ?? "NeoNode";
172100
172915
  const savedTypeRef = qualifyGeneratedType(ctx, savedTypeName(typeName));
172101
172916
  const writable = `writable${selection2[0].toUpperCase()}${selection2.slice(1)}`;
172917
+ if (lookupHasRuntimeOwnership(member))
172918
+ return `${selection2} is NeoMemberClassWritable ${writable} ? ${savedTypeRef}.CreateWritable(client, ${writable}) : ${savedTypeRef}.Create(client, (NeoMemberClass)${selection2})`;
172102
172919
  return targetSaved && writeGuardMemberName != null ? `${selection2} is NeoMemberClassWritable ${writable} && !IsReadOnly ? ${savedTypeRef}.CreateWritable(client, ${writable}) : ${savedTypeRef}.Create(client, (NeoMemberClass)${selection2})` : targetSaved ? `${savedTypeRef}.CreateWritable(client, (NeoMemberClassWritable)${selection2})` : `${savedTypeRef}.Create(client, (NeoMemberClass)${selection2})`;
172103
172920
  }
172104
172921
  function lookupReadExpression(ctx, member, getNode, schemaKey, saved, writeGuardMemberName, genericEnv) {
@@ -174789,9 +175606,14 @@ function memberTypeInfo(member, context, seen = /* @__PURE__ */ new Set()) {
174789
175606
  };
174790
175607
  }
174791
175608
  if (isMemberLookupBase(resolved)) {
174792
- const collection = context.membersById?.get(resolved.collectionMemberId);
174793
- const collectionTypeInfo = memberTypeInfo(collection, context, seen);
174794
- return lookupDeclaredTypeInfo2(resolved, collectionTypeInfo ?? null);
175609
+ return lookupDeclaredTypeInfo2(
175610
+ resolved,
175611
+ (collectionMemberId) => memberTypeInfo(
175612
+ context.membersById?.get(collectionMemberId),
175613
+ context,
175614
+ seen
175615
+ ) ?? null
175616
+ );
174795
175617
  }
174796
175618
  const candidate = {
174797
175619
  type: resolved.kind,
@@ -195473,7 +196295,7 @@ There is no --keep-current: preserving current semantic state defines flatten.
195473
196295
  async function main() {
195474
196296
  const args = parseArgs(process.argv.slice(2));
195475
196297
  if (args.command === "--version") {
195476
- console.log("0.76.3");
196298
+ console.log("0.77.0");
195477
196299
  return;
195478
196300
  }
195479
196301
  if (args.command === null || args.command === "help" || args.command === "--help" || args.command === "-h") {