@neocompose/cli 0.15.0 → 0.16.1

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.
Files changed (3) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/dist/neo.mjs +2661 -395
  3. package/package.json +1 -1
package/dist/neo.mjs CHANGED
@@ -672,6 +672,11 @@ function toCanonical(value) {
672
672
  function isPendingId(id2) {
673
673
  return id2.startsWith(PENDING_ID_PREFIX);
674
674
  }
675
+ function isUuidV4Id(id2) {
676
+ return /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/u.test(
677
+ id2
678
+ );
679
+ }
675
680
  var PENDING_ID_PREFIX;
676
681
  var init_projection = __esm({
677
682
  "src/project-sync/projection.ts"() {
@@ -856,6 +861,7 @@ var init_document_contracts = __esm({
856
861
  "multiselect",
857
862
  "collectionMemberId",
858
863
  "collectionValueId",
864
+ "declaredTypeInfo",
859
865
  "dialogueGroupId",
860
866
  "code",
861
867
  "setterCode",
@@ -892,6 +898,7 @@ var init_document_contracts = __esm({
892
898
  partial: "missingIsFalseOnFullMember",
893
899
  multiselect: "missingIsFalseOnFullMember",
894
900
  collectionValueId: "missingIsNullOnFullMember",
901
+ declaredTypeInfo: "nullIsAbsent",
895
902
  dialogueGroupId: "missingIsNullOnFullMember",
896
903
  templateId: "missingIsNullOnFullMember",
897
904
  setterCode: "nullIsAbsent",
@@ -916,11 +923,13 @@ var init_document_contracts = __esm({
916
923
  "genericParams",
917
924
  "extendsGenericBindings",
918
925
  "constructorProjections",
919
- "constructorIds"
926
+ "constructorIds",
927
+ "targetMemberId"
920
928
  ],
921
929
  derived: [],
922
930
  volatile: ["projectId", "createdAt", "updatedAt"],
923
931
  normalization: {
932
+ targetMemberId: "nullIsAbsent",
924
933
  schemaKeyOrder: "nullIsAbsent",
925
934
  extendsClassId: "nullIsAbsent",
926
935
  implementsInterfaceIds: "nullOrEmptyArrayIsAbsent",
@@ -12970,7 +12979,7 @@ var init_project_schema_contract_generated = __esm({
12970
12979
  "../packages/neoscript-language/src/project-schema-contract.generated.ts"() {
12971
12980
  "use strict";
12972
12981
  PROJECT_SCHEMA_MANIFEST_FORMAT_VERSION = 3;
12973
- PROJECT_SCHEMA_MANIFEST_CONTRACT_VERSION = "3.6";
12982
+ PROJECT_SCHEMA_MANIFEST_CONTRACT_VERSION = "3.8";
12974
12983
  PROJECT_FILE_UPLOAD_BATCH_SIZE = 32;
12975
12984
  NEO_PROJECT_SOURCE_RECORD_CONTRACT = {
12976
12985
  "recordFields": {
@@ -13010,6 +13019,7 @@ var init_project_schema_contract_generated = __esm({
13010
13019
  "multiselect",
13011
13020
  "collectionMemberId",
13012
13021
  "collectionValueId",
13022
+ "declaredTypeInfo",
13013
13023
  "dialogueGroupId",
13014
13024
  "code",
13015
13025
  "setterCode",
@@ -13037,7 +13047,8 @@ var init_project_schema_contract_generated = __esm({
13037
13047
  "genericParams",
13038
13048
  "extendsGenericBindings",
13039
13049
  "constructorProjections",
13040
- "constructorIds"
13050
+ "constructorIds",
13051
+ "targetMemberId"
13041
13052
  ],
13042
13053
  "constructor": [
13043
13054
  "id",
@@ -13247,7 +13258,14 @@ var init_project_schema_contract_generated = __esm({
13247
13258
  "AnimationClip",
13248
13259
  "AnimationFrame",
13249
13260
  "AnimationChildOverride",
13250
- "AnimationChildTrack"
13261
+ "AnimationFrameBase",
13262
+ "AnimationSegmentFrame",
13263
+ "AnimationSegment",
13264
+ "SpriteAnimationSegment",
13265
+ "AnimationTrack",
13266
+ "AnimationChildTrack",
13267
+ "AnimationSegmentTrack",
13268
+ "SpriteAnimationSegmentTrack"
13251
13269
  ],
13252
13270
  "NeoTextureType": [
13253
13271
  "Default",
@@ -14412,6 +14430,16 @@ var init_project_schema_contract_generated = __esm({
14412
14430
  }
14413
14431
  ]
14414
14432
  },
14433
+ "NeoClassSchemaSettings": {
14434
+ "constructors": [],
14435
+ "properties": [
14436
+ {
14437
+ "type": "string",
14438
+ "name": "TargetMember",
14439
+ "default": "string.Empty"
14440
+ }
14441
+ ]
14442
+ },
14415
14443
  "NeoDialogueSettings": {
14416
14444
  "constructors": [],
14417
14445
  "properties": [
@@ -15630,7 +15658,7 @@ function settingsFields(context) {
15630
15658
  { name: "saveOptionChoices", type: "bool" }
15631
15659
  ];
15632
15660
  }
15633
- return [];
15661
+ return classSchemaSettingsFields();
15634
15662
  }
15635
15663
  if (context.kind === "member" && context.memberKind === "function") {
15636
15664
  return settingsClassFields("NeoFunctionSettings");
@@ -15918,6 +15946,12 @@ function lookupSettingsFields() {
15918
15946
  name: field.name === "collectionMember" ? "collection" : field.name === "collectionValueMember" ? "collectionValue" : field.name
15919
15947
  }));
15920
15948
  }
15949
+ function classSchemaSettingsFields() {
15950
+ return settingsClassFields("NeoClassSchemaSettings").map((field) => ({
15951
+ ...field,
15952
+ name: field.name === "targetMember" ? "target" : field.name
15953
+ }));
15954
+ }
15921
15955
  function mergeFields(...groups) {
15922
15956
  const fields = /* @__PURE__ */ new Map();
15923
15957
  for (const group of groups) {
@@ -16263,7 +16297,10 @@ function validateExpression(expression, expected, scope, environment, uri, range
16263
16297
  );
16264
16298
  validateExpression(
16265
16299
  entry.value,
16266
- field ? semanticType(field.type) : void 0,
16300
+ field ? substituteSemanticType(
16301
+ semanticType(field.member.type),
16302
+ field.bindings
16303
+ ) : void 0,
16267
16304
  scope,
16268
16305
  environment,
16269
16306
  uri,
@@ -16364,16 +16401,44 @@ function validateExpression(expression, expected, scope, environment, uri, range
16364
16401
  function sourceClassFieldByName(declaration, name, environment) {
16365
16402
  const visited = /* @__PURE__ */ new Set();
16366
16403
  let current = declaration;
16404
+ let bindings = /* @__PURE__ */ new Map();
16367
16405
  while (current !== void 0 && !visited.has(current.name)) {
16368
16406
  visited.add(current.name);
16369
16407
  const member = current.members.find(
16370
16408
  (candidate) => candidate.kind === "field" && candidate.name === name
16371
16409
  );
16372
- if (member !== void 0) return member;
16373
- current = current.baseTypes.map((base) => environment.types.get(base.name)).find((candidate) => candidate !== void 0);
16410
+ if (member !== void 0) return { member, bindings };
16411
+ const base = current.baseTypes.find(
16412
+ (candidate) => environment.types.has(candidate.name)
16413
+ );
16414
+ const next = base === void 0 ? void 0 : environment.types.get(base.name);
16415
+ if (base !== void 0 && next !== void 0) {
16416
+ bindings = new Map(
16417
+ next.genericParameters.flatMap((parameter3, index) => {
16418
+ const argument2 = base.typeArguments[index];
16419
+ return argument2 === void 0 ? [] : [
16420
+ [
16421
+ parameter3.name,
16422
+ substituteSemanticType(semanticType(argument2), bindings)
16423
+ ]
16424
+ ];
16425
+ })
16426
+ );
16427
+ }
16428
+ current = next;
16374
16429
  }
16375
16430
  return void 0;
16376
16431
  }
16432
+ function substituteSemanticType(type, bindings) {
16433
+ const bound = bindings.get(type.name);
16434
+ if (bound !== void 0 && (type.arguments ?? []).length === 0) {
16435
+ return type.nullable === void 0 ? bound : { ...bound, nullable: type.nullable };
16436
+ }
16437
+ const argumentsList2 = (type.arguments ?? []).map(
16438
+ (argument2) => substituteSemanticType(argument2, bindings)
16439
+ );
16440
+ return { ...type, arguments: argumentsList2 };
16441
+ }
16377
16442
  function semanticTypesAssignable(actual, expected) {
16378
16443
  if (actual.name === expected.name) return true;
16379
16444
  if (actual.name === "int" && (expected.name === "float" || expected.name === "decimal")) {
@@ -16484,6 +16549,37 @@ function validateReferenceExpression(expression, expected, environment, uri, ran
16484
16549
  malformed("Reference accepts at most one generic target type.");
16485
16550
  return;
16486
16551
  }
16552
+ const keyIndex = names.findIndex((name) => name === "key");
16553
+ if (keyIndex >= 0) {
16554
+ const indexIndex = names.findIndex((name) => name === "index");
16555
+ if (keyIndex !== 0) {
16556
+ malformed("Reference key must be the first argument.");
16557
+ return;
16558
+ }
16559
+ if (names.some((name) => name !== "key" && name !== "index")) {
16560
+ malformed("Reference(key: ...) accepts only key: and index: arguments.");
16561
+ return;
16562
+ }
16563
+ if (expression.args.length !== (indexIndex >= 0 ? 2 : 1)) {
16564
+ malformed("Reference(key: ...) accepts only key: and index: arguments.");
16565
+ return;
16566
+ }
16567
+ const keyArgument = expression.args[keyIndex];
16568
+ if (keyArgument?.kind !== "litString" || keyArgument.value.length === 0) {
16569
+ malformed("Reference key must be one non-empty string literal.");
16570
+ return;
16571
+ }
16572
+ if (indexIndex >= 0 && expression.args[indexIndex]?.kind !== "ident") {
16573
+ malformed("Reference index must name the indexed member.");
16574
+ return;
16575
+ }
16576
+ if (!typeArguments[0]) {
16577
+ malformed(
16578
+ "Reference(key: ...) requires an explicit generic target type."
16579
+ );
16580
+ }
16581
+ return;
16582
+ }
16487
16583
  if (expression.args.length !== 1 || hasId && (idIndex !== 0 || names.some((name) => name !== "id")) || !hasId && names.some((name) => name !== null)) {
16488
16584
  malformed(
16489
16585
  "Reference requires exactly one symbol argument or one named id: string argument."
@@ -16512,6 +16608,7 @@ function validateReferenceExpression(expression, expected, environment, uri, ran
16512
16608
  return;
16513
16609
  }
16514
16610
  const path = referenceTargetPath(argument2);
16611
+ if (path !== null && path.startsWith("root.")) return;
16515
16612
  const target = path ? environment.referenceTargets.get(path) : void 0;
16516
16613
  if (!target) {
16517
16614
  malformed(
@@ -18290,7 +18387,7 @@ function validateInterfaceMemberConflicts(ref, environment, diagnostics) {
18290
18387
  candidate.member,
18291
18388
  candidate.substitution,
18292
18389
  prior,
18293
- false,
18390
+ "interface-inherited",
18294
18391
  environment
18295
18392
  ) === null) {
18296
18393
  continue;
@@ -18321,7 +18418,7 @@ function validateInterfaceMemberConflicts(ref, environment, diagnostics) {
18321
18418
  member,
18322
18419
  /* @__PURE__ */ new Map(),
18323
18420
  prior,
18324
- false,
18421
+ "interface-inherited",
18325
18422
  environment
18326
18423
  );
18327
18424
  if (!mismatch) continue;
@@ -18348,7 +18445,7 @@ function validateAbstractInterfaceMembers(ref, environment, diagnostics) {
18348
18445
  implementation.member,
18349
18446
  implementation.substitution,
18350
18447
  contract,
18351
- true,
18448
+ "interface-contract",
18352
18449
  environment
18353
18450
  );
18354
18451
  if (!mismatch) continue;
@@ -18570,9 +18667,8 @@ function validateClassOverrides(ref, environment, diagnostics) {
18570
18667
  member,
18571
18668
  identitySubstitution(ref),
18572
18669
  target,
18573
- false,
18574
- environment,
18575
- true
18670
+ "class-override",
18671
+ environment
18576
18672
  );
18577
18673
  const readOnlyContract = candidates.find(
18578
18674
  (candidate) => candidate.member.modifiers.includes("abstract") && candidate.member.modifiers.includes("readonly")
@@ -18620,10 +18716,15 @@ function validateClassOverrides(ref, environment, diagnostics) {
18620
18716
  }
18621
18717
  }
18622
18718
  }
18719
+ function isPlatformImplementedSystemMember(member) {
18720
+ if (!member.modifiers.includes("protected")) return false;
18721
+ return member.annotations.some((entry) => entry.name === "system");
18722
+ }
18623
18723
  function validateConcreteClass(ref, environment, diagnostics) {
18624
18724
  const effective = effectiveClassMembers(ref, environment);
18625
18725
  for (const member of effective.values()) {
18626
18726
  if (!member.member.modifiers.includes("abstract")) continue;
18727
+ if (isPlatformImplementedSystemMember(member.member)) continue;
18627
18728
  diagnose(
18628
18729
  diagnostics,
18629
18730
  ref.uri,
@@ -18640,7 +18741,7 @@ function validateConcreteClass(ref, environment, diagnostics) {
18640
18741
  implementation.member,
18641
18742
  implementation.substitution,
18642
18743
  required2,
18643
- true,
18744
+ "interface-contract",
18644
18745
  environment
18645
18746
  ) : null;
18646
18747
  if (implementation && !implementation.member.modifiers.includes("abstract") && !implementation.member.modifiers.includes("static") && mismatch === null) {
@@ -18770,13 +18871,35 @@ function interfaceMembers(view, environment) {
18770
18871
  visit(view);
18771
18872
  return members;
18772
18873
  }
18773
- function memberCompatibility(implementation, implementationSubstitution, target, allowStoredInterfaceImplementation, environment, allowStoredOverrideNullability = false) {
18874
+ function isAbstractPropertyContract(target, accessors) {
18875
+ if (target.kind !== "property") return false;
18876
+ if (!target.modifiers.includes("abstract")) return false;
18877
+ return accessors.get && !accessors.set;
18878
+ }
18879
+ function isClassTypedShape(type, environment, genericParameters, seen = /* @__PURE__ */ new Set()) {
18880
+ if (environment.classes.has(type.name)) return true;
18881
+ if (seen.has(type.name)) return false;
18882
+ seen.add(type.name);
18883
+ const constraint = genericParameters.get(type.name)?.constraint;
18884
+ if (constraint === void 0) return false;
18885
+ return isClassTypedShape(
18886
+ typeShape(constraint),
18887
+ environment,
18888
+ genericParameters,
18889
+ seen
18890
+ );
18891
+ }
18892
+ function memberCompatibility(implementation, implementationSubstitution, target, mode, environment) {
18893
+ const treatTargetAsContract = mode === "interface-contract";
18894
+ const isClassOverride = mode === "class-override";
18774
18895
  const implementationFamily = implementation.kind === "function" ? "function" : "value";
18775
18896
  const targetFamily = target.member.kind === "function" ? "function" : "value";
18776
18897
  if (implementationFamily !== targetFamily) {
18777
18898
  return `expected a ${targetFamily} member, got ${implementationFamily}`;
18778
18899
  }
18779
- if (implementationFamily === "value" && implementation.kind !== target.member.kind && !allowStoredInterfaceImplementation) {
18900
+ const targetAccessors = target.member.kind === "function" ? { get: false, set: false } : valueAccessors(target.member, environment);
18901
+ const targetIsContract = treatTargetAsContract || isClassOverride && isAbstractPropertyContract(target.member, targetAccessors);
18902
+ if (implementationFamily === "value" && implementation.kind !== target.member.kind && !targetIsContract) {
18780
18903
  return `expected a ${target.member.kind} member, got ${implementation.kind}`;
18781
18904
  }
18782
18905
  const implementationType = typeShape(
@@ -18784,13 +18907,23 @@ function memberCompatibility(implementation, implementationSubstitution, target,
18784
18907
  implementationSubstitution
18785
18908
  );
18786
18909
  const targetType = typeShape(target.member.type, target.substitution);
18787
- const targetAccessors = target.member.kind === "function" ? { get: false, set: false } : valueAccessors(target.member, environment);
18788
- const permitsCovariantGetter = allowStoredInterfaceImplementation && implementationFamily === "value" && targetAccessors.get && !targetAccessors.set;
18789
- const permitsStoredOverrideNullability = allowStoredOverrideNullability && implementation.kind === "field" && target.member.kind === "field" && typesEqual(
18790
- { ...implementationType, nullable: false },
18791
- { ...targetType, nullable: false }
18910
+ const implementationGenericParameters = genericParameterMap(
18911
+ environment.memberOwners.get(implementation)?.declaration.genericParameters ?? []
18792
18912
  );
18793
- if (!typesEqual(implementationType, targetType) && !(permitsCovariantGetter && isTypeAssignable(implementationType, targetType, environment, /* @__PURE__ */ new Map())) && !permitsStoredOverrideNullability) {
18913
+ const permitsCovariantGetter = targetIsContract && implementationFamily === "value" && targetAccessors.get && !targetAccessors.set;
18914
+ const permitsClassCovariance = isClassOverride && implementationFamily === "value" && isClassTypedShape(
18915
+ implementationType,
18916
+ environment,
18917
+ implementationGenericParameters
18918
+ ) && isClassTypedShape(targetType, environment, implementationGenericParameters);
18919
+ const permitsStoredOverrideNullability = isClassOverride && implementation.kind === "field" && (target.member.kind === "field" || isAbstractPropertyContract(target.member, targetAccessors));
18920
+ const comparableImplementationType = permitsStoredOverrideNullability ? { ...implementationType, nullable: targetType.nullable } : implementationType;
18921
+ if (!typesEqual(comparableImplementationType, targetType) && !((permitsCovariantGetter || permitsClassCovariance) && isTypeAssignable(
18922
+ comparableImplementationType,
18923
+ targetType,
18924
+ environment,
18925
+ implementationGenericParameters
18926
+ ))) {
18794
18927
  return `type '${formatType2(implementationType)}' does not match '${formatType2(targetType)}'`;
18795
18928
  }
18796
18929
  if (implementation.kind !== "function" || target.member.kind !== "function") {
@@ -24202,6 +24335,11 @@ function memberFromDocument(record3, members, owners, classNames, context) {
24202
24335
  field("multiselect", false),
24203
24336
  record3,
24204
24337
  "multiselect"
24338
+ ),
24339
+ declaredType: optionalDocumentType(
24340
+ field("declaredTypeInfo", null),
24341
+ record3,
24342
+ "declaredTypeInfo"
24205
24343
  )
24206
24344
  };
24207
24345
  }
@@ -24382,6 +24520,9 @@ function memberToDocumentFields(member, baseData3) {
24382
24520
  fields.collectionMemberId = member.collectionMemberId;
24383
24521
  fields.collectionValueId = member.collectionValueId;
24384
24522
  fields.multiselect = member.multiselect;
24523
+ if (member.declaredType !== null) {
24524
+ fields.declaredTypeInfo = manifestTypeToDocument(member.declaredType);
24525
+ }
24385
24526
  }
24386
24527
  if (member.kind === "dialogueLookup") {
24387
24528
  fields.dialogueGroupId = member.dialogueGroupId;
@@ -24507,6 +24648,10 @@ function documentArgumentsToManifest(value, record3, source) {
24507
24648
  };
24508
24649
  });
24509
24650
  }
24651
+ function optionalDocumentType(value, record3, path) {
24652
+ if (value === null || value === void 0) return null;
24653
+ return documentTypeToManifest(value, record3, path);
24654
+ }
24510
24655
  function documentReturnTypeToManifest(value, record3, path) {
24511
24656
  if (isRecord2(value) && value.type === "Void") {
24512
24657
  return { kind: "void", nullable: false };
@@ -24863,7 +25008,14 @@ function isSchemaSystemMetadata(value) {
24863
25008
  "animationClip",
24864
25009
  "animationFrame",
24865
25010
  "animationChildOverride",
24866
- "animationChildTrack"
25011
+ "animationFrameBase",
25012
+ "animationSegmentFrame",
25013
+ "animationSegment",
25014
+ "spriteAnimationSegment",
25015
+ "animationTrack",
25016
+ "animationChildTrack",
25017
+ "animationSegmentTrack",
25018
+ "spriteAnimationSegmentTrack"
24867
25019
  ]);
24868
25020
  return typeof value.kind === "string" && kinds.has(value.kind) && (value.worldKind === null || typeof value.worldKind === "string" && worlds.has(value.worldKind));
24869
25021
  }
@@ -25708,6 +25860,7 @@ function schemaClassFromDocument(record3, context) {
25708
25860
  record3,
25709
25861
  "extendsGenericBindings"
25710
25862
  ),
25863
+ targetMemberId: optionalStringOrNull(data.targetMemberId),
25711
25864
  ...constructorProjections.length === 0 ? {} : { constructorProjections },
25712
25865
  ...constructorIds.length === 0 ? {} : { constructorIds }
25713
25866
  };
@@ -25733,7 +25886,8 @@ function schemaClassToDocument(schemaClass2) {
25733
25886
  })),
25734
25887
  extendsGenericBindings: schemaClass2.extendsGenericBindings,
25735
25888
  constructorProjections: schemaClass2.constructorProjections,
25736
- constructorIds: schemaClass2.constructorIds
25889
+ constructorIds: schemaClass2.constructorIds,
25890
+ targetMemberId: schemaClass2.targetMemberId
25737
25891
  });
25738
25892
  }
25739
25893
  function optionalDocsTextProperty(value, record3) {
@@ -26237,6 +26391,24 @@ function assertProjectSchemaManifest(value) {
26237
26391
  assertInternalRecordRelationInvariants(manifest);
26238
26392
  assertStaticMemberInvariants(manifest);
26239
26393
  assertConstructorInvariants(manifest);
26394
+ assertClassTargetMemberInvariants(manifest);
26395
+ }
26396
+ function assertClassTargetMemberInvariants(manifest) {
26397
+ const memberIds = new Set(
26398
+ requireArray(manifest.members, "$.members").map(
26399
+ (value, index) => String(requireRecord(value, `$.members[${index}]`).id)
26400
+ )
26401
+ );
26402
+ requireArray(manifest.classes, "$.classes").forEach((value, index) => {
26403
+ const schemaClass2 = requireRecord(value, `$.classes[${index}]`);
26404
+ const targetMemberId = schemaClass2.targetMemberId;
26405
+ if (typeof targetMemberId !== "string") return;
26406
+ if (memberIds.has(targetMemberId)) return;
26407
+ invalid(
26408
+ `$.classes[${index}].targetMemberId`,
26409
+ `class ${JSON.stringify(String(schemaClass2.name))} targets member ${JSON.stringify(targetMemberId)}, which this manifest does not declare.`
26410
+ );
26411
+ });
26240
26412
  }
26241
26413
  function assertConstructorInvariants(manifest) {
26242
26414
  const constructors = requireArray(
@@ -26925,7 +27097,8 @@ function assertNeoSchemaClass(value, path) {
26925
27097
  "allowedStorage",
26926
27098
  "allowedStorageKeys",
26927
27099
  "genericParameters",
26928
- "extendsGenericBindings"
27100
+ "extendsGenericBindings",
27101
+ "targetMemberId"
26929
27102
  ],
26930
27103
  ["docsText", "constructorProjections", "constructorIds"]
26931
27104
  );
@@ -26954,6 +27127,7 @@ function assertNeoSchemaClass(value, path) {
26954
27127
  type.extendsGenericBindings,
26955
27128
  `${path}.extendsGenericBindings`
26956
27129
  );
27130
+ nullableNonEmptyString(type.targetMemberId, `${path}.targetMemberId`);
26957
27131
  if (type.constructorProjections !== void 0) {
26958
27132
  arrayOf(
26959
27133
  type.constructorProjections,
@@ -27169,6 +27343,9 @@ function assertMember2(value, path) {
27169
27343
  `${path}.collectionValueId`
27170
27344
  );
27171
27345
  booleanAt(member.multiselect, `${path}.multiselect`);
27346
+ if (member.declaredType !== null) {
27347
+ assertType2(member.declaredType, `${path}.declaredType`);
27348
+ }
27172
27349
  return;
27173
27350
  }
27174
27351
  if (kind === "dialogueLookup") {
@@ -28358,7 +28535,14 @@ var init_validate = __esm({
28358
28535
  "animationClip",
28359
28536
  "animationFrame",
28360
28537
  "animationChildOverride",
28361
- "animationChildTrack"
28538
+ "animationFrameBase",
28539
+ "animationSegmentFrame",
28540
+ "animationSegment",
28541
+ "spriteAnimationSegment",
28542
+ "animationTrack",
28543
+ "animationChildTrack",
28544
+ "animationSegmentTrack",
28545
+ "spriteAnimationSegmentTrack"
28362
28546
  ]);
28363
28547
  MEMBER_COMMON_KEYS = [
28364
28548
  "id",
@@ -28394,7 +28578,12 @@ var init_validate = __esm({
28394
28578
  list: ["entryMemberId", "listKind", "indexes", "columns"],
28395
28579
  class: ["classId", "schemaKeyOrder", "classArguments"],
28396
28580
  enum: ["enumId", "multiselect"],
28397
- lookup: ["collectionMemberId", "collectionValueId", "multiselect"],
28581
+ lookup: [
28582
+ "collectionMemberId",
28583
+ "collectionValueId",
28584
+ "multiselect",
28585
+ "declaredType"
28586
+ ],
28398
28587
  dialogueLookup: ["dialogueGroupId", "multiselect"],
28399
28588
  computed: ["returnType", "script"],
28400
28589
  sprite: ["templateId"],
@@ -30033,14 +30222,18 @@ function declaresParameterlessConstructor(index, className) {
30033
30222
  if (declared === void 0) return false;
30034
30223
  return declared.some((entry) => entry.parameters.length === 0);
30035
30224
  }
30036
- function initializerRequiresEvaluation(index, expression, targetClassName) {
30225
+ function initializerRequiresEvaluation(index, expression, targetClassName, runtimeIdentifiers = /* @__PURE__ */ new Set()) {
30037
30226
  if (expression.kind === "annotated") {
30038
30227
  return initializerRequiresEvaluation(
30039
30228
  index,
30040
30229
  expression.expression,
30041
- targetClassName
30230
+ targetClassName,
30231
+ runtimeIdentifiers
30042
30232
  );
30043
30233
  }
30234
+ if (expressionReadsRuntimeIdentifier(expression, runtimeIdentifiers)) {
30235
+ return true;
30236
+ }
30044
30237
  if (expression.kind === "call") return !isLiteralCall(expression);
30045
30238
  if (expression.kind !== "new") return false;
30046
30239
  const className = expression.className ?? targetClassName;
@@ -30050,6 +30243,73 @@ function initializerRequiresEvaluation(index, expression, targetClassName) {
30050
30243
  }
30051
30244
  return declaresParameterlessConstructor(index, className);
30052
30245
  }
30246
+ function declaredConstructorParameterNames(index, className) {
30247
+ if (className === null) return /* @__PURE__ */ new Set();
30248
+ return new Set(
30249
+ (index.byClassName.get(className) ?? []).flatMap(
30250
+ (constructor2) => constructor2.parameters.map((parameter3) => parameter3.name)
30251
+ )
30252
+ );
30253
+ }
30254
+ function expressionReadsRuntimeIdentifier(expression, runtimeIdentifiers) {
30255
+ if (runtimeIdentifiers.size === 0) return false;
30256
+ switch (expression.kind) {
30257
+ case "ident":
30258
+ return runtimeIdentifiers.has(expression.name);
30259
+ case "annotated":
30260
+ return expressionReadsRuntimeIdentifier(
30261
+ expression.expression,
30262
+ runtimeIdentifiers
30263
+ );
30264
+ case "new":
30265
+ return expression.args.some(
30266
+ (argument2) => expressionReadsRuntimeIdentifier(argument2, runtimeIdentifiers)
30267
+ );
30268
+ case "call":
30269
+ return expressionReadsRuntimeIdentifier(
30270
+ expression.callee,
30271
+ runtimeIdentifiers
30272
+ ) || expression.args.some(
30273
+ (argument2) => expressionReadsRuntimeIdentifier(argument2, runtimeIdentifiers)
30274
+ );
30275
+ case "member":
30276
+ return expressionReadsRuntimeIdentifier(
30277
+ expression.receiver,
30278
+ runtimeIdentifiers
30279
+ );
30280
+ case "index":
30281
+ return expressionReadsRuntimeIdentifier(
30282
+ expression.receiver,
30283
+ runtimeIdentifiers
30284
+ ) || expressionReadsRuntimeIdentifier(expression.index, runtimeIdentifiers);
30285
+ case "binary":
30286
+ case "coalesce":
30287
+ return expressionReadsRuntimeIdentifier(expression.left, runtimeIdentifiers) || expressionReadsRuntimeIdentifier(expression.right, runtimeIdentifiers);
30288
+ case "unary":
30289
+ case "force":
30290
+ case "is":
30291
+ return expressionReadsRuntimeIdentifier(
30292
+ expression.operand,
30293
+ runtimeIdentifiers
30294
+ );
30295
+ case "litInterp":
30296
+ return expression.parts.some(
30297
+ (part) => part.kind === "expr" && expressionReadsRuntimeIdentifier(part.expr, runtimeIdentifiers)
30298
+ );
30299
+ case "lambda":
30300
+ return false;
30301
+ case "litList":
30302
+ case "litDict":
30303
+ case "litNull":
30304
+ case "litBool":
30305
+ case "litInt":
30306
+ case "litFloat":
30307
+ case "litString":
30308
+ case "litTripleString":
30309
+ case "contextualEnum":
30310
+ return false;
30311
+ }
30312
+ }
30053
30313
  function isLiteralCall(expression) {
30054
30314
  if (expression.callee.kind === "ident" && expression.callee.name === "Reference") {
30055
30315
  return true;
@@ -30914,10 +31174,17 @@ var init_world_system_kinds_generated = __esm({
30914
31174
  SmartTile: "smartTile",
30915
31175
  SmartTileRule: "smartTileRule",
30916
31176
  SmartTileNeighbor: "smartTileNeighbor",
31177
+ AnimationFrameBase: "animationFrameBase",
30917
31178
  AnimationClip: "animationClip",
30918
31179
  AnimationFrame: "animationFrame",
31180
+ AnimationSegmentFrame: "animationSegmentFrame",
31181
+ AnimationSegment: "animationSegment",
31182
+ SpriteAnimationSegment: "spriteAnimationSegment",
30919
31183
  AnimationChildOverride: "animationChildOverride",
30920
- AnimationChildTrack: "animationChildTrack"
31184
+ AnimationTrack: "animationTrack",
31185
+ AnimationChildTrack: "animationChildTrack",
31186
+ AnimationSegmentTrack: "animationSegmentTrack",
31187
+ SpriteAnimationSegmentTrack: "spriteAnimationSegmentTrack"
30921
31188
  };
30922
31189
  NEO_WORLD_SYSTEM_CLASS_KIND_VALUES = new Set(
30923
31190
  Object.values(NeoWorldSystemClassKind)
@@ -31995,7 +32262,7 @@ function isNeoSchemaClassBase(value) {
31995
32262
  (interfaceId) => typeof interfaceId === "string" && interfaceId.length > 0
31996
32263
  )) && typeof v?.hiddenInMemberSelector === "boolean" && typeof v?.isAbstract === "boolean" && (v.allowedStorage === void 0 || v.allowedStorage === null || isEffectiveMemberStorage(v.allowedStorage)) && (v.allowedStorageKeys === void 0 || v.allowedStorageKeys === null || Array.isArray(v.allowedStorageKeys) && v.allowedStorageKeys.every((key) => typeof key === "string")) && (v.genericParams === void 0 || v.genericParams === null || Array.isArray(v.genericParams) && v.genericParams.every(isGenericParamDeclaration)) && (v.extendsGenericBindings === void 0 || v.extendsGenericBindings === null || isGenericBindingsRecord(v.extendsGenericBindings)) && (v.constructorProjections === void 0 || v.constructorProjections === null || Array.isArray(v.constructorProjections) && v.constructorProjections.every(isNeoClassConstructorProjection) && v.system?.kind === SystemProtectionKind.WorldAuthoring) && (v.constructorIds === void 0 || v.constructorIds === null || Array.isArray(v.constructorIds) && v.constructorIds.every(
31997
32264
  (constructorId) => typeof constructorId === "string" && constructorId.length > 0
31998
- ) && new Set(v.constructorIds).size === v.constructorIds.length) && (v.system === void 0 || v.system === null || isSystemMetadata(v.system));
32265
+ ) && new Set(v.constructorIds).size === v.constructorIds.length) && (v.targetMemberId === void 0 || v.targetMemberId === null || typeof v.targetMemberId === "string" && v.targetMemberId.length > 0) && (v.system === void 0 || v.system === null || isSystemMetadata(v.system));
31999
32266
  }
32000
32267
  function isNeoClassConstructorProjection(value) {
32001
32268
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
@@ -32470,6 +32737,9 @@ function isMemberLookupBase(value) {
32470
32737
  if (v.collectionValueId !== void 0 && v.collectionValueId !== null && typeof v.collectionValueId !== "string") {
32471
32738
  return false;
32472
32739
  }
32740
+ if (v.declaredTypeInfo !== void 0 && v.declaredTypeInfo !== null && !isNSTypeInfo(v.declaredTypeInfo)) {
32741
+ return false;
32742
+ }
32473
32743
  return typeof v.multiselect === "boolean";
32474
32744
  }
32475
32745
  function isMemberLookup(value) {
@@ -32498,6 +32768,14 @@ function isMemberNSPropertyBase(value) {
32498
32768
  if (v.setterCode !== void 0 && v.setterCode.length === 0) return false;
32499
32769
  return isNSTypeInfo(v.returnTypeInfo);
32500
32770
  }
32771
+ function isMemberNSPropertyContractBase(value) {
32772
+ const v = asMemberBaseForKind(value, 10 /* NSProperty */);
32773
+ if (v === null) return false;
32774
+ if (v.isAbstract !== true) return false;
32775
+ if (v.code !== void 0) return false;
32776
+ if (v.setterCode !== void 0) return false;
32777
+ return isNSTypeInfo(v.returnTypeInfo);
32778
+ }
32501
32779
  function isMemberNSProperty(value) {
32502
32780
  if (!isMemberNSPropertyBase(value)) return false;
32503
32781
  if (!isWithId(value)) return false;
@@ -32696,6 +32974,11 @@ function memberKindSupportsStorage(kind) {
32696
32974
  if (kind === 23 /* NSFunction */) return false;
32697
32975
  return kind !== 13 /* Function */;
32698
32976
  }
32977
+ function memberKindOwnsStoredValue(kind) {
32978
+ if (kind === 10 /* NSProperty */) return false;
32979
+ if (kind === 23 /* NSFunction */) return false;
32980
+ return kind !== 13 /* Function */;
32981
+ }
32699
32982
  function hasValidStorageField(v) {
32700
32983
  if (v.storage === void 0) return true;
32701
32984
  if (v.storage === null) return true;
@@ -32712,7 +32995,7 @@ function isMemberBase(value) {
32712
32995
  if (partial !== void 0 && v?.kind !== 7 /* Class */ && v?.kind !== 21 /* Generic */) {
32713
32996
  return false;
32714
32997
  }
32715
- const matchesConcreteType = isMemberNullBase(value) || isMemberBoolBase(value) || isMemberIntBase(value) || isMemberStringBase(value) || isMemberFloatBase(value) || isMemberDictionaryBase(value) || isMemberListBase(value) || isMemberClassBase(value) || isMemberEnumBase(value) || isMemberLookupBase(value) || isMemberDialogueLookupBase(value) || isMemberNSPropertyBase(value) || isMemberSpriteBase(value) || isMemberAudioBase(value) || isMemberFunctionBase(value) || isMemberNSFunctionBase(value) || isMemberNSFunction(value) || isMemberFunctionRefBase(value) || isMemberVector2Base(value) || isMemberVector2IntBase(value) || isMemberVector3Base(value) || isMemberVector3IntBase(value) || isMemberColorBase(value) || isMemberDecimalBase(value) || isMemberGenericBase(value);
32998
+ const matchesConcreteType = isMemberNullBase(value) || isMemberBoolBase(value) || isMemberIntBase(value) || isMemberStringBase(value) || isMemberFloatBase(value) || isMemberDictionaryBase(value) || isMemberListBase(value) || isMemberClassBase(value) || isMemberEnumBase(value) || isMemberLookupBase(value) || isMemberDialogueLookupBase(value) || isMemberNSPropertyBase(value) || isMemberNSPropertyContractBase(value) || isMemberSpriteBase(value) || isMemberAudioBase(value) || isMemberFunctionBase(value) || isMemberNSFunctionBase(value) || isMemberNSFunction(value) || isMemberFunctionRefBase(value) || isMemberVector2Base(value) || isMemberVector2IntBase(value) || isMemberVector3Base(value) || isMemberVector3IntBase(value) || isMemberColorBase(value) || isMemberDecimalBase(value) || isMemberGenericBase(value);
32716
32999
  if (!matchesConcreteType) return false;
32717
33000
  if (!isValidDocsText(v?.docsText)) return false;
32718
33001
  if (typeof v?.name !== "string") return false;
@@ -33167,11 +33450,10 @@ var init_inheritance = __esm({
33167
33450
  });
33168
33451
 
33169
33452
  // ../src/models/classes/world-system-classes.generated.ts
33170
- var WORLD_GRID_CHILDREN_MEMBER_ID, WORLD_GRID_CHILDREN_ENTRY_MEMBER_ID, WORLD_OBJECT_CHILDREN_MEMBER_ID, WORLD_OBJECT_CHILDREN_ENTRY_MEMBER_ID, WORLD_OBJECT_PLACEMENT_TILES_MEMBER_ID, WORLD_OBJECT_PLACEMENT_TILES_ENTRY_MEMBER_ID, WORLD_OBJECT_PLACEMENT_TILE_CELL_MEMBER_ID, WORLD_TILE_INSTANCE_CELL_MEMBER_ID, WORLD_TILE_LAYER_LINK_TILES_MEMBER_ID, WORLD_TILE_LAYER_LINK_TILE_ENTRY_MEMBER_ID, WORLD_OBJECT_LAYER_LINK_OBJECTS_MEMBER_ID, WORLD_OBJECT_LAYER_LINK_OBJECT_ENTRY_MEMBER_ID, WORLD_ANIMATION_CLIP_TARGET_PARAM_ID, WORLD_ANIMATION_CHILD_OVERRIDE_ENTRY_PARAM_ID, WORLD_ANIMATION_CLIP_FPS_MEMBER_ID, WORLD_ANIMATION_CLIP_DURATION_MEMBER_ID, WORLD_ANIMATION_CLIP_FRAMES_MEMBER_ID, WORLD_ANIMATION_CLIP_TRACKS_MEMBER_ID, WORLD_ANIMATION_FRAME_INDEX_MEMBER_ID, WORLD_ANIMATION_FRAME_OVERRIDES_MEMBER_ID, WORLD_ANIMATION_FRAME_CHILD_OVERRIDES_MEMBER_ID, WORLD_ANIMATION_FRAME_CHILD_OVERRIDES_ENTRY_MEMBER_ID, WORLD_ANIMATION_FRAME_ACTIONS_MEMBER_ID, WORLD_ANIMATION_CHILD_OVERRIDE_CHILD_MEMBER_ID, WORLD_ANIMATION_CHILD_OVERRIDE_OVERRIDES_MEMBER_ID, WORLD_ANIMATION_CHILD_TRACK_CHILD_MEMBER_ID, WORLD_ANIMATION_CHILD_TRACK_CLIP_KEY_MEMBER_ID, WORLD_ANIMATION_CHILD_TRACK_START_FRAME_MEMBER_ID, WORLD_SYSTEM_CLASS_DEFINITIONS;
33453
+ var WORLD_GRID_CHILDREN_MEMBER_ID, WORLD_GRID_CHILDREN_ENTRY_MEMBER_ID, WORLD_OBJECT_CHILDREN_MEMBER_ID, WORLD_OBJECT_CHILDREN_ENTRY_MEMBER_ID, WORLD_OBJECT_PLACEMENT_TILES_MEMBER_ID, WORLD_OBJECT_PLACEMENT_TILES_ENTRY_MEMBER_ID, WORLD_OBJECT_PLACEMENT_TILE_CELL_MEMBER_ID, WORLD_TILE_INSTANCE_CELL_MEMBER_ID, WORLD_TILE_LAYER_LINK_TILES_MEMBER_ID, WORLD_TILE_LAYER_LINK_TILE_ENTRY_MEMBER_ID, WORLD_OBJECT_LAYER_LINK_OBJECTS_MEMBER_ID, WORLD_OBJECT_LAYER_LINK_OBJECT_ENTRY_MEMBER_ID, WORLD_ANIMATION_CLIP_TARGET_PARAM_ID, WORLD_ANIMATION_CHILD_OVERRIDE_ENTRY_PARAM_ID, WORLD_ANIMATION_CLIP_FPS_MEMBER_ID, WORLD_ANIMATION_CLIP_DURATION_MEMBER_ID, WORLD_ANIMATION_CLIP_FRAMES_MEMBER_ID, WORLD_ANIMATION_CLIP_TRACKS_MEMBER_ID, WORLD_ANIMATION_FRAME_BASE_INDEX_MEMBER_ID, WORLD_ANIMATION_FRAME_OVERRIDES_MEMBER_ID, WORLD_ANIMATION_FRAME_CHILD_OVERRIDES_MEMBER_ID, WORLD_ANIMATION_FRAME_CHILD_OVERRIDES_ENTRY_MEMBER_ID, WORLD_ANIMATION_FRAME_ACTIONS_MEMBER_ID, WORLD_ANIMATION_CHILD_OVERRIDE_CHILD_MEMBER_ID, WORLD_ANIMATION_CHILD_OVERRIDE_OVERRIDES_MEMBER_ID, WORLD_ANIMATION_TRACK_CHILD_MEMBER_ID, WORLD_ANIMATION_TRACK_START_FRAME_MEMBER_ID, WORLD_ANIMATION_TRACK_DIRECTION_MEMBER_ID, WORLD_ANIMATION_TRACK_OFFSET_START_INDEX_MEMBER_ID, WORLD_ANIMATION_TRACK_OFFSET_END_INDEX_MEMBER_ID, WORLD_ANIMATION_CHILD_TRACK_CLIP_KEY_MEMBER_ID, WORLD_ANIMATION_SEGMENT_DURATION_MEMBER_ID, WORLD_ANIMATION_SEGMENT_FRAMES_MEMBER_ID, WORLD_ANIMATION_SEGMENT_TRACK_CHILD_PARAM_ID, WORLD_ANIMATION_SEGMENT_TRACK_VALUE_PARAM_ID, WORLD_PLAY_DIRECTION_FORWARD_OPTION_ID, WORLD_PLAY_DIRECTION_REVERSE_OPTION_ID, WORLD_SYSTEM_CLASS_DEFINITIONS;
33171
33454
  var init_world_system_classes_generated = __esm({
33172
33455
  "../src/models/classes/world-system-classes.generated.ts"() {
33173
33456
  "use strict";
33174
- init_member_storage();
33175
33457
  WORLD_GRID_CHILDREN_MEMBER_ID = "system_98578ba3-a70e-4397-9283-996a898d44c8";
33176
33458
  WORLD_GRID_CHILDREN_ENTRY_MEMBER_ID = "system_c86da069-7ef9-5693-a54b-5c331755fde9";
33177
33459
  WORLD_OBJECT_CHILDREN_MEMBER_ID = "system_bb5d2cf1-a0dd-4eba-a62e-0e1bf0177071";
@@ -33190,16 +33472,25 @@ var init_world_system_classes_generated = __esm({
33190
33472
  WORLD_ANIMATION_CLIP_DURATION_MEMBER_ID = "system_e1a1ba86-2599-43dc-a19a-46d08a2e4256";
33191
33473
  WORLD_ANIMATION_CLIP_FRAMES_MEMBER_ID = "system_485b0f05-ec7b-4e61-ba80-9a5d7d262b39";
33192
33474
  WORLD_ANIMATION_CLIP_TRACKS_MEMBER_ID = "system_eb602654-04a9-4766-860f-300b443be86e";
33193
- WORLD_ANIMATION_FRAME_INDEX_MEMBER_ID = "system_0dbdd0f9-9dd2-4b4b-9403-50180c1500dd";
33475
+ WORLD_ANIMATION_FRAME_BASE_INDEX_MEMBER_ID = "system_0dbdd0f9-9dd2-4b4b-9403-50180c1500dd";
33194
33476
  WORLD_ANIMATION_FRAME_OVERRIDES_MEMBER_ID = "system_8ab7bb00-a475-43f0-8579-1b08f133d658";
33195
33477
  WORLD_ANIMATION_FRAME_CHILD_OVERRIDES_MEMBER_ID = "system_a5905750-c472-46a8-89e9-f04f0bf66696";
33196
33478
  WORLD_ANIMATION_FRAME_CHILD_OVERRIDES_ENTRY_MEMBER_ID = "system_86688965-6b26-529e-95f3-a4070f022582";
33197
33479
  WORLD_ANIMATION_FRAME_ACTIONS_MEMBER_ID = "system_23e05410-6428-4bd1-b085-c0390ed7fcb7";
33198
33480
  WORLD_ANIMATION_CHILD_OVERRIDE_CHILD_MEMBER_ID = "system_6e15db41-a6f2-43fd-9793-c52fe4f3d0f8";
33199
33481
  WORLD_ANIMATION_CHILD_OVERRIDE_OVERRIDES_MEMBER_ID = "system_819b3743-0c3d-4896-b679-9aeeb73255f4";
33200
- WORLD_ANIMATION_CHILD_TRACK_CHILD_MEMBER_ID = "system_29abc85e-2d38-4cef-a200-88915d176d06";
33482
+ WORLD_ANIMATION_TRACK_CHILD_MEMBER_ID = "system_29abc85e-2d38-4cef-a200-88915d176d06";
33483
+ WORLD_ANIMATION_TRACK_START_FRAME_MEMBER_ID = "system_5af60692-74a0-45ea-a9ba-747854989b2a";
33484
+ WORLD_ANIMATION_TRACK_DIRECTION_MEMBER_ID = "system_75a314b5-c799-4af6-9e64-4cde6eae1b30";
33485
+ WORLD_ANIMATION_TRACK_OFFSET_START_INDEX_MEMBER_ID = "system_c71caaab-df72-48c6-a291-2772288351eb";
33486
+ WORLD_ANIMATION_TRACK_OFFSET_END_INDEX_MEMBER_ID = "system_6755f539-a459-46ec-b44a-cc74a18ad83e";
33201
33487
  WORLD_ANIMATION_CHILD_TRACK_CLIP_KEY_MEMBER_ID = "system_2c816624-0281-46d3-9ed5-d20bd43519f4";
33202
- WORLD_ANIMATION_CHILD_TRACK_START_FRAME_MEMBER_ID = "system_5af60692-74a0-45ea-a9ba-747854989b2a";
33488
+ WORLD_ANIMATION_SEGMENT_DURATION_MEMBER_ID = "system_8a159870-36a4-40e4-98bf-15919410650a";
33489
+ WORLD_ANIMATION_SEGMENT_FRAMES_MEMBER_ID = "system_40658ad2-b4a5-4404-bb6d-ed778088a772";
33490
+ WORLD_ANIMATION_SEGMENT_TRACK_CHILD_PARAM_ID = "system_d4fe9f71-d9d9-4baf-bc52-8de16933b655";
33491
+ WORLD_ANIMATION_SEGMENT_TRACK_VALUE_PARAM_ID = "system_03497842-3381-45a2-b2fa-b2dee36c2759";
33492
+ WORLD_PLAY_DIRECTION_FORWARD_OPTION_ID = "system_2e4ca40e-f305-49c6-a91b-b99d56239ba0";
33493
+ WORLD_PLAY_DIRECTION_REVERSE_OPTION_ID = "system_6478d195-3905-48db-befe-d276eb5478f0";
33203
33494
  WORLD_SYSTEM_CLASS_DEFINITIONS = [
33204
33495
  {
33205
33496
  classId: "system_b78f7f1d-f63c-4abb-ae65-852e69246534",
@@ -33784,11 +34075,27 @@ var init_world_system_classes_generated = __esm({
33784
34075
  ],
33785
34076
  worldKind: "smartTileNeighbor"
33786
34077
  },
34078
+ {
34079
+ classId: "system_6529416a-f68b-49a3-b3d2-2af8fda60cb4",
34080
+ name: "NeoAnimationFrameBase",
34081
+ docsText: "A row at a frame index. Rows are sparse: each one holds from its index\nuntil the next row you author, so you only author the frames where\nsomething changes.",
34082
+ isAbstract: true,
34083
+ schemaFields: [
34084
+ {
34085
+ memberId: "system_0dbdd0f9-9dd2-4b4b-9403-50180c1500dd",
34086
+ memberKind: "int",
34087
+ minValue: 0,
34088
+ required: true,
34089
+ schemaKey: "Index"
34090
+ }
34091
+ ],
34092
+ worldKind: "animationFrameBase"
34093
+ },
33787
34094
  {
33788
34095
  classId: "system_994b3886-7392-4bd2-8f9d-d5f7fd316a5a",
33789
34096
  name: "NeoAnimationClip",
34097
+ docsText: "Composition and timing for one object and the children under it: how long\nit runs, how fast, the sparse frames that override values, and the tracks\nthat schedule other content onto the same timeline.\n\nEditing a clip while it plays takes effect the next time playback starts,\nand in the editor at the next preview recompute. Segments are the exception\n\u2014 they are re-read every frame.",
33790
34098
  isAbstract: false,
33791
- allowedStorage: "immutable" /* Immutable */,
33792
34099
  genericParams: [
33793
34100
  {
33794
34101
  id: "system_268e9d23-347b-4eb6-8bec-cf0edf326aa2",
@@ -33848,7 +34155,7 @@ var init_world_system_classes_generated = __esm({
33848
34155
  memberId: "system_f89776c0-a7ac-5e80-9fcb-df9a5a1033fc",
33849
34156
  memberKind: "class",
33850
34157
  defaultValue: {},
33851
- schemaClassWorldKind: "animationChildTrack",
34158
+ schemaClassWorldKind: "animationTrack",
33852
34159
  required: true,
33853
34160
  schemaKey: "__TrackEntry"
33854
34161
  }
@@ -33858,8 +34165,9 @@ var init_world_system_classes_generated = __esm({
33858
34165
  {
33859
34166
  classId: "system_ba921c72-e4b2-47f9-861f-f30ac1156965",
33860
34167
  name: "NeoAnimationFrame",
34168
+ docsText: "One row of a clip's timeline: the overrides, child overrides, and actions\nthat apply from its index until the next row you author.",
34169
+ extendsWorldKind: "animationFrameBase",
33861
34170
  isAbstract: false,
33862
- allowedStorage: "immutable" /* Immutable */,
33863
34171
  genericParams: [
33864
34172
  {
33865
34173
  id: "system_d2194ea1-08ec-4a0c-b9a0-97877031d39b",
@@ -33868,13 +34176,6 @@ var init_world_system_classes_generated = __esm({
33868
34176
  }
33869
34177
  ],
33870
34178
  schemaFields: [
33871
- {
33872
- memberId: "system_0dbdd0f9-9dd2-4b4b-9403-50180c1500dd",
33873
- memberKind: "int",
33874
- minValue: 0,
33875
- required: true,
33876
- schemaKey: "Index"
33877
- },
33878
34179
  {
33879
34180
  memberId: "system_8ab7bb00-a475-43f0-8579-1b08f133d658",
33880
34181
  memberKind: "generic",
@@ -33930,11 +34231,96 @@ var init_world_system_classes_generated = __esm({
33930
34231
  ],
33931
34232
  worldKind: "animationFrame"
33932
34233
  },
34234
+ {
34235
+ classId: "system_9c4f3bfb-f0d8-4231-a7e7-9115bab8d5ab",
34236
+ name: "NeoAnimationSegmentFrame",
34237
+ docsText: "One value at a frame index inside a segment. It holds until the next row\nyou author, or until the segment's Duration runs out.",
34238
+ extendsWorldKind: "animationFrameBase",
34239
+ isAbstract: false,
34240
+ genericParams: [
34241
+ { id: "system_887b11d3-e56a-47ff-8d01-e2fcefb846a1", name: "T" }
34242
+ ],
34243
+ schemaFields: [
34244
+ {
34245
+ memberId: "system_b71d1f73-da72-49de-ab9a-911cc9bffa43",
34246
+ memberKind: "generic",
34247
+ genericParamId: "system_887b11d3-e56a-47ff-8d01-e2fcefb846a1",
34248
+ required: false,
34249
+ schemaKey: "Value"
34250
+ }
34251
+ ],
34252
+ worldKind: "animationSegmentFrame"
34253
+ },
34254
+ {
34255
+ classId: "system_2866011d-30fe-410a-8a04-d2ff291b77cd",
34256
+ name: "NeoAnimationSegment",
34257
+ docsText: "A sequence of one member's values over a frame index. Frames are sparse and\neach one holds until the next row or the end of Duration. A segment has no\nfps of its own \u2014 the clip that schedules it owns the clock.\n\nA segment can live anywhere: immutable catalog data, a save, or session\nstate a game writes at runtime. Its frames and its Duration are re-read on\nevery frame that plays, so a change made mid-playback shows on the next\nframe.",
34258
+ isAbstract: true,
34259
+ genericParams: [
34260
+ { id: "system_a35d028e-bd71-49aa-9f9d-944b6e813b80", name: "T" }
34261
+ ],
34262
+ schemaFields: [
34263
+ {
34264
+ memberId: "system_8a159870-36a4-40e4-98bf-15919410650a",
34265
+ memberKind: "int",
34266
+ defaultValue: 1,
34267
+ minValue: 1,
34268
+ required: true,
34269
+ schemaKey: "Duration"
34270
+ },
34271
+ {
34272
+ memberId: "system_40658ad2-b4a5-4404-bb6d-ed778088a772",
34273
+ memberKind: "list",
34274
+ defaultValue: [],
34275
+ entryMemberId: "system_da56f93b-d3a6-5feb-9f51-3a9fd029272a",
34276
+ required: true,
34277
+ schemaKey: "Frames"
34278
+ }
34279
+ ],
34280
+ supportingFields: [
34281
+ {
34282
+ memberId: "system_da56f93b-d3a6-5feb-9f51-3a9fd029272a",
34283
+ memberKind: "class",
34284
+ schemaClassWorldKind: "animationSegmentFrame",
34285
+ classArguments: {
34286
+ "system_887b11d3-e56a-47ff-8d01-e2fcefb846a1": {
34287
+ kind: "generic",
34288
+ genericParamId: "system_a35d028e-bd71-49aa-9f9d-944b6e813b80"
34289
+ }
34290
+ },
34291
+ required: true,
34292
+ schemaKey: "__FrameEntry"
34293
+ }
34294
+ ],
34295
+ worldKind: "animationSegment"
34296
+ },
34297
+ {
34298
+ classId: "system_ffc766b3-f3ac-4c20-91cf-38ff7e8e88f3",
34299
+ name: "NeoSpriteAnimationSegment",
34300
+ docsText: "A sprite flipbook: which sprite to show at each frame index. This is the\nsegment to reach for when animating a sprite. To animate any other kind of\nvalue \u2014 a Vector3 bounce, a Color flash \u2014 declare your own segment class\nderiving from NeoAnimationSegment.",
34301
+ extendsWorldKind: "animationSegment",
34302
+ isAbstract: false,
34303
+ extendsGenericBindings: {
34304
+ "system_a35d028e-bd71-49aa-9f9d-944b6e813b80": {
34305
+ kind: "member",
34306
+ memberId: "system_f10bc0fd-9b8e-5901-9f4b-38e7cdc7a241"
34307
+ }
34308
+ },
34309
+ supportingFields: [
34310
+ {
34311
+ memberId: "system_f10bc0fd-9b8e-5901-9f4b-38e7cdc7a241",
34312
+ memberKind: "sprite",
34313
+ required: true,
34314
+ schemaKey: "__SpriteInfoTypeArgument"
34315
+ }
34316
+ ],
34317
+ worldKind: "spriteAnimationSegment"
34318
+ },
33933
34319
  {
33934
34320
  classId: "system_e2e88eba-5335-4a16-9dcf-2c0e1951e8bd",
33935
34321
  name: "NeoAnimationChildOverride",
34322
+ docsText: "What one child looks like while a clip frame holds: which child, and the\nvalues to override on it.",
33936
34323
  isAbstract: false,
33937
- allowedStorage: "immutable" /* Immutable */,
33938
34324
  constructorProjections: [
33939
34325
  {
33940
34326
  memberId: "system_6e15db41-a6f2-43fd-9793-c52fe4f3d0f8",
@@ -33969,11 +34355,76 @@ var init_world_system_classes_generated = __esm({
33969
34355
  ],
33970
34356
  worldKind: "animationChildOverride"
33971
34357
  },
34358
+ {
34359
+ classId: "system_45755778-ecfe-4a6c-b65f-a08017a7fb56",
34360
+ name: "NeoAnimationTrackBase",
34361
+ docsText: "A lane on a clip's timeline: which child it plays against, when it starts,\nwhich way it runs, and which slice of the content to use. Both kinds of\ntrack derive from this, so playing a child clip reversed or cropped is\nsomething you author on the lane rather than pass in when you play it.",
34362
+ isAbstract: true,
34363
+ schemaFields: [
34364
+ {
34365
+ memberId: "system_29abc85e-2d38-4cef-a200-88915d176d06",
34366
+ memberKind: "lookup",
34367
+ collectionMemberId: "system_bb5d2cf1-a0dd-4eba-a62e-0e1bf0177071",
34368
+ collectionValueId: null,
34369
+ multiselect: false,
34370
+ required: true,
34371
+ schemaKey: "Child"
34372
+ },
34373
+ {
34374
+ memberId: "system_5af60692-74a0-45ea-a9ba-747854989b2a",
34375
+ memberKind: "int",
34376
+ defaultValue: 0,
34377
+ minValue: 0,
34378
+ required: true,
34379
+ schemaKey: "StartFrame"
34380
+ },
34381
+ {
34382
+ memberId: "system_75a314b5-c799-4af6-9e64-4cde6eae1b30",
34383
+ memberKind: "enum",
34384
+ defaultValue: ["system_2e4ca40e-f305-49c6-a91b-b99d56239ba0"],
34385
+ docsText: "Which way the scheduled content plays: forward, or last frame first. This is playback order, not a direction in the world.",
34386
+ enumId: "system_705ccc39-e46e-4c9f-af3e-3ec8fd818709",
34387
+ multiselect: false,
34388
+ required: true,
34389
+ schemaKey: "Direction"
34390
+ },
34391
+ {
34392
+ memberId: "system_c71caaab-df72-48c6-a291-2772288351eb",
34393
+ memberKind: "int",
34394
+ defaultValue: 0,
34395
+ docsText: "The first frame of the scheduled content to play, counted in that content's own frames. Dragging the left edge of a lane edits this.",
34396
+ minValue: 0,
34397
+ required: false,
34398
+ schemaKey: "OffsetStartIndex"
34399
+ },
34400
+ {
34401
+ memberId: "system_6755f539-a459-46ec-b44a-cc74a18ad83e",
34402
+ memberKind: "int",
34403
+ defaultValue: null,
34404
+ docsText: "Where to stop, counted in the scheduled content's own frames. The frame at this index is not played. Leave it empty to run to the end. Dragging the right edge of a lane edits this.",
34405
+ minValue: 1,
34406
+ required: false,
34407
+ schemaKey: "OffsetEndIndex"
34408
+ },
34409
+ {
34410
+ memberId: "system_626f7a7a-66b7-4e54-aaaa-7174a74c9467",
34411
+ memberKind: "computed",
34412
+ accessModifierKind: "protected",
34413
+ isAbstract: true,
34414
+ docsText: "How long the scheduled content runs, in its own frames \u2014 the referenced clip's Duration on a child track, the segment's Duration on a segment track. Neo answers this for you; it is not a value you author.",
34415
+ returnTypeInfo: { type: 2, required: true },
34416
+ required: true,
34417
+ schemaKey: "BaseDuration"
34418
+ }
34419
+ ],
34420
+ worldKind: "animationTrack"
34421
+ },
33972
34422
  {
33973
34423
  classId: "system_8dc78ecf-15b8-4b8a-86f3-7691a5b487d0",
33974
34424
  name: "NeoAnimationChildTrack",
34425
+ docsText: "Plays another object's clip on this clip's timeline, looked up by name. The\nlane is as long as the clip it names.",
34426
+ extendsWorldKind: "animationTrack",
33975
34427
  isAbstract: false,
33976
- allowedStorage: "immutable" /* Immutable */,
33977
34428
  constructorProjections: [
33978
34429
  {
33979
34430
  memberId: "system_29abc85e-2d38-4cef-a200-88915d176d06",
@@ -33982,31 +34433,110 @@ var init_world_system_classes_generated = __esm({
33982
34433
  ],
33983
34434
  schemaFields: [
33984
34435
  {
33985
- memberId: "system_29abc85e-2d38-4cef-a200-88915d176d06",
34436
+ memberId: "system_2c816624-0281-46d3-9ed5-d20bd43519f4",
34437
+ memberKind: "string",
34438
+ localizable: false,
34439
+ required: true,
34440
+ schemaKey: "ClipKey"
34441
+ }
34442
+ ],
34443
+ worldKind: "animationChildTrack"
34444
+ },
34445
+ {
34446
+ classId: "system_fd551dd8-6e6b-4044-9f32-7445ab344200",
34447
+ name: "NeoAnimationSegmentTrack",
34448
+ docsText: "Plays a sequence of values onto one member of one child, on the owning\nclip's clock. Where a child track hands off to another clip, this lane is a\nleaf: it writes one value per frame to the member its subclass targets.\n\nDerive from it with the child type you author against, so that a Segment\ngetter can reach that child's own members.",
34449
+ extendsWorldKind: "animationTrack",
34450
+ isAbstract: true,
34451
+ constructorProjections: [
34452
+ {
34453
+ memberId: "system_87900a74-0d7c-4dfc-a6f6-3bd204ebb8f6",
34454
+ parameterName: "id"
34455
+ }
34456
+ ],
34457
+ genericParams: [
34458
+ {
34459
+ id: "system_d4fe9f71-d9d9-4baf-bc52-8de16933b655",
34460
+ name: "TChild",
34461
+ constraintClassWorldKind: "objectBase"
34462
+ },
34463
+ { id: "system_03497842-3381-45a2-b2fa-b2dee36c2759", name: "TValue" }
34464
+ ],
34465
+ schemaFields: [
34466
+ {
34467
+ memberId: "system_87900a74-0d7c-4dfc-a6f6-3bd204ebb8f6",
33986
34468
  memberKind: "lookup",
34469
+ extendsMemberId: "system_29abc85e-2d38-4cef-a200-88915d176d06",
34470
+ docsText: "The child this lane plays against, typed as the child class you derived with, so a Segment getter can read that child's own members.",
33987
34471
  collectionMemberId: "system_bb5d2cf1-a0dd-4eba-a62e-0e1bf0177071",
33988
34472
  collectionValueId: null,
33989
34473
  multiselect: false,
34474
+ declaredTypeInfo: {
34475
+ type: 21,
34476
+ required: true,
34477
+ ownerClassId: "system_fd551dd8-6e6b-4044-9f32-7445ab344200",
34478
+ genericParamId: "system_d4fe9f71-d9d9-4baf-bc52-8de16933b655"
34479
+ },
33990
34480
  required: true,
33991
34481
  schemaKey: "Child"
33992
34482
  },
33993
34483
  {
33994
- memberId: "system_2c816624-0281-46d3-9ed5-d20bd43519f4",
33995
- memberKind: "string",
33996
- localizable: false,
34484
+ memberId: "system_d2d0bf9b-211f-4b2f-bff3-3d6da5766abd",
34485
+ memberKind: "computed",
34486
+ isAbstract: true,
34487
+ docsText: "The sequence of values this lane plays. Your subclass decides where it comes from \u2014 a value you store, a lookup, or a getter you write \u2014 and it is resolved again on every frame that plays.",
34488
+ returnTypeInfo: {
34489
+ type: 7,
34490
+ required: true,
34491
+ classId: "system_2866011d-30fe-410a-8a04-d2ff291b77cd",
34492
+ typeArguments: {
34493
+ "system_a35d028e-bd71-49aa-9f9d-944b6e813b80": {
34494
+ type: 21,
34495
+ required: true,
34496
+ ownerClassId: "system_fd551dd8-6e6b-4044-9f32-7445ab344200",
34497
+ genericParamId: "system_03497842-3381-45a2-b2fa-b2dee36c2759"
34498
+ }
34499
+ }
34500
+ },
33997
34501
  required: true,
33998
- schemaKey: "ClipKey"
34502
+ schemaKey: "Segment"
34503
+ }
34504
+ ],
34505
+ worldKind: "animationSegmentTrack"
34506
+ },
34507
+ {
34508
+ classId: "system_24b70585-0798-49ad-b650-4c118bac1eb1",
34509
+ name: "NeoSpriteAnimationSegmentTrack",
34510
+ docsText: "Plays a sprite segment onto a sprite child's Sprite member \u2014 the lane that\nanimates a sprite over time. Derive from it to say where the segment comes\nfrom; Segment is left for you to fill in, so every lane that plays is one of\nyour own classes.",
34511
+ extendsWorldKind: "animationSegmentTrack",
34512
+ isAbstract: true,
34513
+ extendsGenericBindings: {
34514
+ "system_d4fe9f71-d9d9-4baf-bc52-8de16933b655": {
34515
+ kind: "generic",
34516
+ genericParamId: "system_4de90637-6a3b-4bba-95ef-2ff9dfb05268"
33999
34517
  },
34518
+ "system_03497842-3381-45a2-b2fa-b2dee36c2759": {
34519
+ kind: "member",
34520
+ memberId: "system_65388373-2b28-53f4-b821-8c74dada7c8c"
34521
+ }
34522
+ },
34523
+ genericParams: [
34000
34524
  {
34001
- memberId: "system_5af60692-74a0-45ea-a9ba-747854989b2a",
34002
- memberKind: "int",
34003
- defaultValue: 0,
34004
- minValue: 0,
34525
+ id: "system_4de90637-6a3b-4bba-95ef-2ff9dfb05268",
34526
+ name: "TChild",
34527
+ constraintClassWorldKind: "spriteObject"
34528
+ }
34529
+ ],
34530
+ supportingFields: [
34531
+ {
34532
+ memberId: "system_65388373-2b28-53f4-b821-8c74dada7c8c",
34533
+ memberKind: "sprite",
34005
34534
  required: true,
34006
- schemaKey: "StartFrame"
34535
+ schemaKey: "__SpriteInfoTypeArgument"
34007
34536
  }
34008
34537
  ],
34009
- worldKind: "animationChildTrack"
34538
+ targetMemberId: "system_e9288ba9-f5a2-4485-8443-6afb155b31e0",
34539
+ worldKind: "spriteAnimationSegmentTrack"
34010
34540
  }
34011
34541
  ];
34012
34542
  }
@@ -34022,7 +34552,7 @@ function worldAnimationChildOverrideBindingMemberId(classId) {
34022
34552
  )
34023
34553
  );
34024
34554
  }
34025
- var WORLD_ANIMATION_CHILD_OVERRIDE_BINDING_MEMBER_NAME, WORLD_ANIMATION_CHILD_OVERRIDE_BINDING_MEMBER_NAMESPACE, WORLD_SYSTEM_ANIMATION_KINDS, WORLD_SYSTEM_ALL_KINDS, WORLD_SYSTEM_CLASS_DEFINITION_BY_KIND, WORLD_SYSTEM_SCHEMA_FIELD_SITES_BY_MEMBER_ID;
34555
+ var WORLD_ANIMATION_CHILD_OVERRIDE_BINDING_MEMBER_NAME, WORLD_ANIMATION_CHILD_OVERRIDE_BINDING_MEMBER_NAMESPACE, WORLD_SYSTEM_ANIMATION_KIND_MARKER, WORLD_SYSTEM_ANIMATION_KINDS, GENERIC_WORLD_SYSTEM_CLASS_KINDS, WORLD_SYSTEM_ALL_KINDS, WORLD_SYSTEM_CLASS_DEFINITION_BY_KIND, WORLD_SYSTEM_SCHEMA_FIELD_SITES_BY_MEMBER_ID;
34026
34556
  var init_world_system_classes = __esm({
34027
34557
  "../src/models/classes/world-system-classes.ts"() {
34028
34558
  "use strict";
@@ -34034,12 +34564,19 @@ var init_world_system_classes = __esm({
34034
34564
  init_world_system_classes_generated();
34035
34565
  WORLD_ANIMATION_CHILD_OVERRIDE_BINDING_MEMBER_NAME = "NeoAnimationChildBinding";
34036
34566
  WORLD_ANIMATION_CHILD_OVERRIDE_BINDING_MEMBER_NAMESPACE = "2048c3bb-8ae5-51cc-a1dc-15178cc41865";
34037
- WORLD_SYSTEM_ANIMATION_KINDS = /* @__PURE__ */ new Set([
34038
- NeoWorldSystemClassKind.AnimationClip,
34039
- NeoWorldSystemClassKind.AnimationFrame,
34040
- NeoWorldSystemClassKind.AnimationChildOverride,
34041
- NeoWorldSystemClassKind.AnimationChildTrack
34042
- ]);
34567
+ WORLD_SYSTEM_ANIMATION_KIND_MARKER = "animation";
34568
+ WORLD_SYSTEM_ANIMATION_KINDS = new Set(
34569
+ WORLD_SYSTEM_CLASS_DEFINITIONS.map(
34570
+ (definition2) => definition2.worldKind
34571
+ ).filter(
34572
+ (worldKind) => worldKind.toLowerCase().includes(WORLD_SYSTEM_ANIMATION_KIND_MARKER)
34573
+ )
34574
+ );
34575
+ GENERIC_WORLD_SYSTEM_CLASS_KINDS = new Set(
34576
+ WORLD_SYSTEM_CLASS_DEFINITIONS.filter(
34577
+ (definition2) => (definition2.genericParams?.length ?? 0) > 0
34578
+ ).map((definition2) => definition2.worldKind)
34579
+ );
34043
34580
  WORLD_SYSTEM_ALL_KINDS = new Set(
34044
34581
  WORLD_SYSTEM_CLASS_DEFINITIONS.map((definition2) => definition2.worldKind)
34045
34582
  );
@@ -35990,10 +36527,7 @@ function buildDefaultMemberValue(args) {
35990
36527
  for (const entry of member.partial === true ? [] : merged) {
35991
36528
  if (entry.memberId === null) continue;
35992
36529
  const childMember = findMemberInDocument(args.document, entry.memberId);
35993
- if (isMemberNSPropertyBase(childMember)) continue;
35994
- if (childMember.kind === 13 /* Function */ || childMember.kind === 23 /* NSFunction */) {
35995
- continue;
35996
- }
36530
+ if (!memberKindOwnsStoredValue(childMember.kind)) continue;
35997
36531
  const resolvedChildMember = substituteMember(
35998
36532
  childMember,
35999
36533
  childEnv,
@@ -37582,6 +38116,57 @@ var init_structured_leaf_source = __esm({
37582
38116
  }
37583
38117
  });
37584
38118
 
38119
+ // src/project-source/member-kind-type-names.ts
38120
+ function fixedSourceTypeNameForKind(kind) {
38121
+ return MEMBER_KIND_SOURCE_TYPE_NAMES[kind] ?? null;
38122
+ }
38123
+ function fixedSourceTypeNameForKindNumber(kind) {
38124
+ return SOURCE_TYPE_NAME_BY_MEMBER_KIND_NUMBER.get(kind) ?? null;
38125
+ }
38126
+ var MEMBER_KIND_SOURCE_TYPE_NAMES, MEMBER_KIND_NUMBERS, SOURCE_TYPE_NAME_BY_MEMBER_KIND_NUMBER;
38127
+ var init_member_kind_type_names = __esm({
38128
+ "src/project-source/member-kind-type-names.ts"() {
38129
+ "use strict";
38130
+ init_members();
38131
+ MEMBER_KIND_SOURCE_TYPE_NAMES = {
38132
+ null: "null",
38133
+ bool: "bool",
38134
+ int: "int",
38135
+ string: "string",
38136
+ float: "float",
38137
+ decimal: "decimal",
38138
+ sprite: "SpriteInfo",
38139
+ audio: "AudioClipInfo",
38140
+ vector2: "Vector2",
38141
+ vector2Int: "Vector2Int",
38142
+ vector3: "Vector3",
38143
+ vector3Int: "Vector3Int",
38144
+ color: "Color"
38145
+ };
38146
+ MEMBER_KIND_NUMBERS = {
38147
+ null: 0 /* Null */,
38148
+ bool: 1 /* Bool */,
38149
+ int: 2 /* Int */,
38150
+ string: 3 /* String */,
38151
+ float: 4 /* Float */,
38152
+ decimal: 20 /* Decimal */,
38153
+ sprite: 11 /* Sprite */,
38154
+ audio: 12 /* Audio */,
38155
+ vector2: 14 /* Vector2 */,
38156
+ vector2Int: 15 /* Vector2Int */,
38157
+ vector3: 16 /* Vector3 */,
38158
+ vector3Int: 17 /* Vector3Int */,
38159
+ color: 19 /* Color */
38160
+ };
38161
+ SOURCE_TYPE_NAME_BY_MEMBER_KIND_NUMBER = new Map(
38162
+ Object.entries(MEMBER_KIND_NUMBERS).map(([slug, kind]) => [
38163
+ kind,
38164
+ MEMBER_KIND_SOURCE_TYPE_NAMES[slug]
38165
+ ])
38166
+ );
38167
+ }
38168
+ });
38169
+
37585
38170
  // src/project-source/lower-members.ts
37586
38171
  function lowerClass(context, declaration) {
37587
38172
  const id2 = materializedId(declaration, "class", declaration.name);
@@ -37610,13 +38195,21 @@ function lowerClass(context, declaration) {
37610
38195
  const placements = context.placements.get(memberId) ?? [];
37611
38196
  placements.push(placement);
37612
38197
  context.placements.set(memberId, placements);
37613
- const lowered = lowerMember(
37614
- context,
37615
- declaration,
37616
- memberDeclaration,
37617
- memberId,
37618
- { kind: "classMember", ...placement }
37619
- );
38198
+ let lowered;
38199
+ try {
38200
+ lowered = lowerMember(context, declaration, memberDeclaration, memberId, {
38201
+ kind: "classMember",
38202
+ ...placement
38203
+ });
38204
+ } catch (error) {
38205
+ if (error instanceof SchemaSourceError) throw error;
38206
+ throw new SchemaSourceError(
38207
+ error instanceof Error ? error.message : String(error),
38208
+ memberDeclaration.source.uri,
38209
+ memberDeclaration.source.range.start.line + 1,
38210
+ memberDeclaration.source.range.start.character + 1
38211
+ );
38212
+ }
37620
38213
  const existing = context.loweredMembers.get(memberId);
37621
38214
  if (existing && !sameSharedMember(existing, lowered)) {
37622
38215
  throw new Error(
@@ -37626,6 +38219,7 @@ function lowerClass(context, declaration) {
37626
38219
  context.loweredMembers.set(memberId, existing ?? lowered);
37627
38220
  }
37628
38221
  const storage = annotation(declaration.annotations, "storage");
38222
+ const targetMemberId = lowerClassTargetMemberId(context, declaration, base);
37629
38223
  const genericParameters = declaration.genericParameters.map((parameter3) => {
37630
38224
  const parameterId = materializedIdentityId(
37631
38225
  parameter3.identity,
@@ -37657,6 +38251,7 @@ function lowerClass(context, declaration) {
37657
38251
  // Class storage keys never exist in source. Retain only the historical
37658
38252
  // null representation while the clean-break record migration completes.
37659
38253
  allowedStorageKeys: null,
38254
+ targetMemberId,
37660
38255
  genericParameters,
37661
38256
  extendsGenericBindings: extendsClassId && declaration.baseTypes[0]?.arguments.length ? lowerExtendsBindings(
37662
38257
  context,
@@ -37997,12 +38592,25 @@ function lowerFieldMember(context, ownerClass, declaration, id2, commonInput, ba
37997
38592
  `Lookup member ${ownerClass.name}.${declaration.name} has an unresolved collection.`
37998
38593
  );
37999
38594
  }
38595
+ const multiselect = booleanArgument(settings, "multiselect") ?? false;
38000
38596
  return {
38001
38597
  ...common,
38002
38598
  kind: "lookup",
38003
38599
  collectionMemberId,
38004
38600
  collectionValueId: referenceIdArgument(settings, "collectionValue"),
38005
- multiselect: booleanArgument(settings, "multiselect") ?? false
38601
+ multiselect,
38602
+ // The spelled type, kept only when it narrows the collection entry —
38603
+ // `normalizeLookupDeclaredTypes` clears the redundant ones once every
38604
+ // member is lowered and the entry can be resolved in either direction.
38605
+ declaredType: lookupDeclaredType(
38606
+ context,
38607
+ ownerClass,
38608
+ // A multiselect lookup is spelled `List<T>` and stores the element
38609
+ // type, which is what the emitter re-wraps. Keyed off the spelling
38610
+ // rather than off `multiselect` so a mismatched pair reads the type it
38611
+ // actually has instead of throwing for a missing type argument.
38612
+ fieldType.name === "List" ? requiredTypeArgument(fieldType, 0) : fieldType
38613
+ )
38006
38614
  };
38007
38615
  }
38008
38616
  if (name === "List") {
@@ -38022,10 +38630,20 @@ function lowerFieldMember(context, ownerClass, declaration, id2, commonInput, ba
38022
38630
  multiselect: true
38023
38631
  };
38024
38632
  }
38025
- const entryId = collectionEntryMemberId(
38026
- declaringCollectionMemberId(context, id2, common.overrideOf)
38633
+ const declaringMemberId = declaringCollectionMemberId(
38634
+ context,
38635
+ id2,
38636
+ common.overrideOf
38637
+ );
38638
+ const entryId = collectionEntryMemberId(declaringMemberId);
38639
+ lowerCollectionEntry(
38640
+ context,
38641
+ ownerClass,
38642
+ entryType,
38643
+ entryId,
38644
+ id2,
38645
+ declaringMemberId
38027
38646
  );
38028
- lowerCollectionEntry(context, ownerClass, entryType, entryId, id2);
38029
38647
  return {
38030
38648
  ...common,
38031
38649
  kind: "list",
@@ -38041,10 +38659,20 @@ function lowerFieldMember(context, ownerClass, declaration, id2, commonInput, ba
38041
38659
  }
38042
38660
  const keyType = requiredTypeArgument(fieldType, 0);
38043
38661
  const entryType = requiredTypeArgument(fieldType, 1);
38044
- const entryId = collectionEntryMemberId(
38045
- declaringCollectionMemberId(context, id2, common.overrideOf)
38662
+ const declaringMemberId = declaringCollectionMemberId(
38663
+ context,
38664
+ id2,
38665
+ common.overrideOf
38666
+ );
38667
+ const entryId = collectionEntryMemberId(declaringMemberId);
38668
+ lowerCollectionEntry(
38669
+ context,
38670
+ ownerClass,
38671
+ entryType,
38672
+ entryId,
38673
+ id2,
38674
+ declaringMemberId
38046
38675
  );
38047
- lowerCollectionEntry(context, ownerClass, entryType, entryId, id2);
38048
38676
  return {
38049
38677
  ...common,
38050
38678
  kind: "dictionary",
@@ -38194,12 +38822,24 @@ function overrideParentMemberId(context, memberId) {
38194
38822
  const overrides = member.modifiers.includes("override") || member.modifiers.includes("sealed");
38195
38823
  return overrides ? inheritedMemberId(context, ownerClass, member.name) : null;
38196
38824
  }
38197
- function lowerCollectionEntry(context, ownerClass, entryType, entryId, parentMemberId) {
38825
+ function lowerCollectionEntry(context, ownerClass, entryType, entryId, parentMemberId, declaringMemberId) {
38826
+ const declared = declaredCollectionEntryType(context, declaringMemberId);
38827
+ const expected = declared === null ? null : resolveManifestTypeInDescendant(
38828
+ context,
38829
+ declared.type,
38830
+ declared.ownerClass,
38831
+ ownerClass
38832
+ );
38833
+ if (expected !== null && !sameManifestType(expected, entryType)) {
38834
+ throw new Error(
38835
+ `Collection member ${ownerClass.name}.${parentMemberId} declares entry type ${formatManifestType(entryType)}, but the member it overrides declares ${formatManifestType(expected)}. An override cannot change a collection's element type.`
38836
+ );
38837
+ }
38198
38838
  const already = context.entryTypesByEntryId.get(entryId);
38199
38839
  if (already !== void 0) {
38200
- if (JSON.stringify(already) !== JSON.stringify(entryType)) {
38840
+ if (expected === null && !sameManifestType(already, entryType)) {
38201
38841
  throw new Error(
38202
- `Collection member ${ownerClass.name}.${parentMemberId} declares entry type ${entryType.name}, but the member it overrides declares ${already.name}. An override cannot change a collection's element type.`
38842
+ `Collection member ${ownerClass.name}.${parentMemberId} declares entry type ${formatManifestType(entryType)}, but the member it overrides declares ${formatManifestType(already)}. An override cannot change a collection's element type.`
38203
38843
  );
38204
38844
  }
38205
38845
  return;
@@ -38243,6 +38883,82 @@ function lowerCollectionEntry(context, ownerClass, entryType, entryId, parentMem
38243
38883
  lowerFieldMember(context, ownerClass, declaration, entryId, common, base)
38244
38884
  );
38245
38885
  }
38886
+ function declaredCollectionEntryType(context, declaringMemberId) {
38887
+ const placement = context.sourceMembersById.get(declaringMemberId);
38888
+ if (placement === void 0) return null;
38889
+ const collectionType = placement.member.type;
38890
+ const argumentIndex = collectionType.name === "Dictionary" ? 1 : 0;
38891
+ const type = collectionType.arguments[argumentIndex];
38892
+ return type === void 0 ? null : { type, ownerClass: placement.ownerClass };
38893
+ }
38894
+ function resolveManifestTypeInDescendant(context, type, declaringOwner, descendantOwner) {
38895
+ const declaringOwnerId = materializedId(
38896
+ declaringOwner,
38897
+ "class",
38898
+ declaringOwner.name
38899
+ );
38900
+ let current = descendantOwner;
38901
+ let environment = new Map(
38902
+ current.genericParameters.map((parameter3) => [
38903
+ parameter3.name,
38904
+ manifestNamedType(parameter3.name)
38905
+ ])
38906
+ );
38907
+ const visited = /* @__PURE__ */ new Set();
38908
+ while (true) {
38909
+ const currentId = materializedId(current, "class", current.name);
38910
+ if (currentId === declaringOwnerId) {
38911
+ return substituteManifestType2(type, environment);
38912
+ }
38913
+ if (visited.has(currentId)) return null;
38914
+ visited.add(currentId);
38915
+ const baseType = current.baseTypes[0];
38916
+ if (baseType === void 0) return null;
38917
+ const baseId = context.classIdsByName.get(baseType.name);
38918
+ if (baseId === void 0) return null;
38919
+ const baseClass = context.sourceClasses.get(baseId);
38920
+ if (baseClass === void 0) return null;
38921
+ if (baseType.arguments.length !== baseClass.genericParameters.length) {
38922
+ return null;
38923
+ }
38924
+ environment = new Map(
38925
+ baseClass.genericParameters.map((parameter3, index) => [
38926
+ parameter3.name,
38927
+ substituteManifestType2(baseType.arguments[index], environment)
38928
+ ])
38929
+ );
38930
+ current = baseClass;
38931
+ }
38932
+ }
38933
+ function substituteManifestType2(type, environment) {
38934
+ if (type.arguments.length === 0) {
38935
+ const binding = environment.get(type.name);
38936
+ if (binding !== void 0) {
38937
+ return {
38938
+ ...binding,
38939
+ nullable: type.nullable || binding.nullable
38940
+ };
38941
+ }
38942
+ }
38943
+ return {
38944
+ ...type,
38945
+ arguments: type.arguments.map(
38946
+ (argument2) => substituteManifestType2(argument2, environment)
38947
+ )
38948
+ };
38949
+ }
38950
+ function sameManifestType(left, right) {
38951
+ return left.name === right.name && left.nullable === right.nullable && left.arguments.length === right.arguments.length && left.arguments.every(
38952
+ (argument2, index) => sameManifestType(argument2, right.arguments[index])
38953
+ );
38954
+ }
38955
+ function manifestNamedType(name) {
38956
+ return { name, nullable: false, arguments: [] };
38957
+ }
38958
+ function formatManifestType(type) {
38959
+ const argumentsText = type.arguments.length === 0 ? "" : `<${type.arguments.map(formatManifestType).join(", ")}>`;
38960
+ return `${type.name}${argumentsText}${type.nullable ? "?" : ""}`;
38961
+ }
38246
38962
  function lowerInterface(context, declaration) {
38247
38963
  const id2 = materializedId(declaration, "interface", declaration.name);
38248
38964
  const base = context.baseInterfaces.get(id2);
@@ -38323,6 +39039,44 @@ function lowerEnum(context, declaration) {
38323
39039
  system: systemMetadata(declaration.annotations) ?? base?.system ?? null
38324
39040
  };
38325
39041
  }
39042
+ function lookupDeclaredType(context, ownerClass, type) {
39043
+ if (type.name === "object") return null;
39044
+ const lowered = lowerType(context, type, ownerClass);
39045
+ if (lowered.kind === "class" || lowered.kind === "generic" || lowered.kind === "interface") {
39046
+ return lowered;
39047
+ }
39048
+ return null;
39049
+ }
39050
+ function normalizeLookupDeclaredTypes(context) {
39051
+ for (const [memberId, member] of context.loweredMembers) {
39052
+ if (member.kind !== "lookup") continue;
39053
+ if (member.declaredType === null) continue;
39054
+ if (!lookupDeclaredTypeNarrows(context, member)) {
39055
+ context.loweredMembers.set(memberId, { ...member, declaredType: null });
39056
+ }
39057
+ }
39058
+ }
39059
+ function lookupDeclaredTypeNarrows(context, member) {
39060
+ const declared = member.declaredType;
39061
+ if (declared === null) return false;
39062
+ const entry = lookupEntryMember(context, member.collectionMemberId);
39063
+ if (entry === void 0) return false;
39064
+ if (entry.kind === "class") {
39065
+ return declared.kind !== "class" || declared.classId !== entry.classId;
39066
+ }
39067
+ if (entry.kind === "generic") {
39068
+ return declared.kind !== "generic" || declared.genericParamId !== entry.genericParamId;
39069
+ }
39070
+ return true;
39071
+ }
39072
+ function lookupEntryMember(context, collectionMemberId) {
39073
+ const collection = context.loweredMembers.get(collectionMemberId) ?? context.baseMembers.get(collectionMemberId);
39074
+ if (collection === void 0) return void 0;
39075
+ if (collection.kind !== "list" && collection.kind !== "dictionary") {
39076
+ return void 0;
39077
+ }
39078
+ return context.loweredMembers.get(collection.entryMemberId) ?? context.baseMembers.get(collection.entryMemberId);
39079
+ }
38326
39080
  function lowerType(context, type, ownerClass) {
38327
39081
  const nullable = type.nullable;
38328
39082
  const primitive3 = primitiveTypeKind(type.name);
@@ -38395,7 +39149,11 @@ function lowerDefault(context, initializer, type, ownerClass, base, memberId, st
38395
39149
  if (initializerRequiresEvaluation(
38396
39150
  context.declaredConstructors,
38397
39151
  expression,
38398
- type.name
39152
+ type.name,
39153
+ declaredConstructorParameterNames(
39154
+ context.declaredConstructors,
39155
+ ownerClass.name
39156
+ )
38399
39157
  )) {
38400
39158
  return { init: { code: normalizeInitializerSource(initializer) } };
38401
39159
  }
@@ -38412,14 +39170,67 @@ function lowerDefault(context, initializer, type, ownerClass, base, memberId, st
38412
39170
  classId: baseDefault && !("serverValueId" in baseDefault) && !("init" in baseDefault) ? baseDefault.classId : null
38413
39171
  };
38414
39172
  }
38415
- function lowerExpressionValue(context, expression, expected, ownerClass, path) {
39173
+ function substituteGenericTypeNames(type, environment) {
39174
+ if (environment.size === 0) return type;
39175
+ const bound = environment.get(type.name);
39176
+ if (bound !== void 0 && type.arguments.length === 0) {
39177
+ return type.nullable && !bound.nullable ? { ...bound, nullable: true } : bound;
39178
+ }
39179
+ if (type.arguments.length === 0) return type;
39180
+ return {
39181
+ ...type,
39182
+ arguments: type.arguments.map(
39183
+ (argument2) => substituteGenericTypeNames(argument2, environment)
39184
+ )
39185
+ };
39186
+ }
39187
+ function constructedGenericEnvironment(context, classId, expression, expectedType, outer) {
39188
+ const parameters = context.genericParametersByClass.get(classId);
39189
+ if (parameters === void 0 || parameters.size === 0) return outer;
39190
+ const authored = expression.className !== null && expression.className !== "Partial" ? expression.typeArguments : void 0;
39191
+ const environment = new Map(outer);
39192
+ [...parameters.keys()].forEach((parameterName, index) => {
39193
+ const authoredArgument = authored?.[index];
39194
+ const argument2 = authoredArgument !== void 0 ? manifestTypeFromAstType(authoredArgument) : expectedType.arguments[index];
39195
+ if (argument2 === void 0) return;
39196
+ environment.set(parameterName, substituteGenericTypeNames(argument2, outer));
39197
+ });
39198
+ return environment;
39199
+ }
39200
+ function manifestTypeFromAstType(type) {
39201
+ if (type.kind !== "named") {
39202
+ return { name: "object", nullable: false, arguments: [] };
39203
+ }
39204
+ return {
39205
+ name: type.name,
39206
+ nullable: false,
39207
+ arguments: (type.typeArguments ?? []).map(manifestTypeFromAstType)
39208
+ };
39209
+ }
39210
+ function lowerExpressionValue(context, expression, declaredExpected, ownerClass, path, genericEnvironment = EMPTY_GENERIC_TYPE_ENVIRONMENT) {
39211
+ const expected = substituteGenericTypeNames(
39212
+ declaredExpected,
39213
+ genericEnvironment
39214
+ );
39215
+ if (initializerRequiresEvaluation(
39216
+ context.declaredConstructors,
39217
+ expression,
39218
+ expected.name,
39219
+ declaredConstructorParameterNames(
39220
+ context.declaredConstructors,
39221
+ ownerClass.name
39222
+ )
39223
+ )) {
39224
+ return null;
39225
+ }
38416
39226
  if (expression.kind === "annotated") {
38417
39227
  return lowerExpressionValue(
38418
39228
  context,
38419
39229
  expression.expression,
38420
39230
  expected,
38421
39231
  ownerClass,
38422
- path
39232
+ path,
39233
+ genericEnvironment
38423
39234
  );
38424
39235
  }
38425
39236
  switch (expression.kind) {
@@ -38443,7 +39254,8 @@ function lowerExpressionValue(context, expression, expected, ownerClass, path) {
38443
39254
  expression.operand,
38444
39255
  expected,
38445
39256
  ownerClass,
38446
- path
39257
+ path,
39258
+ genericEnvironment
38447
39259
  );
38448
39260
  if (typeof operand === "number") return -operand;
38449
39261
  if (expected.name === "decimal" && typeof operand === "string") {
@@ -38472,7 +39284,8 @@ function lowerExpressionValue(context, expression, expected, ownerClass, path) {
38472
39284
  element,
38473
39285
  entry,
38474
39286
  ownerClass,
38475
- `${path}[${index}]`
39287
+ `${path}[${index}]`,
39288
+ genericEnvironment
38476
39289
  )
38477
39290
  );
38478
39291
  }
@@ -38485,7 +39298,8 @@ function lowerExpressionValue(context, expression, expected, ownerClass, path) {
38485
39298
  entry.key,
38486
39299
  { name: "string", nullable: false, arguments: [] },
38487
39300
  ownerClass,
38488
- path
39301
+ path,
39302
+ genericEnvironment
38489
39303
  );
38490
39304
  if (typeof key !== "string")
38491
39305
  throw new Error("Dictionary keys must be strings.");
@@ -38494,7 +39308,8 @@ function lowerExpressionValue(context, expression, expected, ownerClass, path) {
38494
39308
  entry.value,
38495
39309
  valueType,
38496
39310
  ownerClass,
38497
- `${path}[${JSON.stringify(key)}]`
39311
+ `${path}[${JSON.stringify(key)}]`,
39312
+ genericEnvironment
38498
39313
  );
38499
39314
  }
38500
39315
  return value;
@@ -38535,31 +39350,35 @@ function lowerExpressionValue(context, expression, expected, ownerClass, path) {
38535
39350
  }
38536
39351
  if (expression.className === null && expression.initializer !== void 0 && expression.hasArgumentList !== true && !partialExpected) {
38537
39352
  throw new Error(
38538
- "Inferred `new { ... }` construction is only valid in a Partial position."
39353
+ `Inferred new { ... } construction is only valid in a Partial position (${path}).`
38539
39354
  );
38540
39355
  }
38541
39356
  const className = explicitPartial ? expectedType.name : expression.className ?? expectedType.name;
38542
39357
  const classId = requiredName(context.classIdsByName, className, "class");
38543
- const target = context.baseClasses.get(classId);
38544
- const sourceTarget = context.sourceClasses.get(classId);
39358
+ const innerEnvironment = constructedGenericEnvironment(
39359
+ context,
39360
+ classId,
39361
+ expression,
39362
+ expectedType,
39363
+ genericEnvironment
39364
+ );
38545
39365
  const result = {};
38546
39366
  for (const assignment of expression.initializer ?? []) {
38547
- const sourceMember = sourceTarget?.members.find(
38548
- (candidate) => candidate.name === assignment.name
39367
+ const memberId = effectiveMemberIdByName(
39368
+ context,
39369
+ classId,
39370
+ assignment.name
38549
39371
  );
38550
- const memberId = target?.schema[assignment.name] ?? (sourceMember ? materializedId(
38551
- sourceMember,
38552
- "member",
38553
- `${sourceTarget?.name ?? className}.${sourceMember.name}`
38554
- ) : void 0);
38555
- const member = memberId ? context.loweredMembers.get(memberId) ?? context.baseMembers.get(memberId) : void 0;
39372
+ const member = memberId !== null ? context.loweredMembers.get(memberId) ?? context.baseMembers.get(memberId) : void 0;
39373
+ const sourceMember = memberId !== null ? context.sourceMembersById.get(memberId)?.member : void 0;
38556
39374
  const declaredType = sourceMember?.type ?? (member ? manifestTypeForMember(context, member) : { name: "object", nullable: true, arguments: [] });
38557
39375
  result[assignment.name] = lowerExpressionValue(
38558
39376
  context,
38559
39377
  assignment.value,
38560
39378
  structuredLeafAssignmentType(partialExpected, declaredType),
38561
39379
  ownerClass,
38562
- `${path}.${assignment.name}`
39380
+ `${path}.${assignment.name}`,
39381
+ innerEnvironment
38563
39382
  );
38564
39383
  }
38565
39384
  return result;
@@ -38877,7 +39696,11 @@ function inheritedMemberId(context, ownerClass, name) {
38877
39696
  const directBase = ownerClass.baseTypes.find(
38878
39697
  (entry) => context.classIdsByName.has(entry.name)
38879
39698
  );
38880
- let classId = directBase ? requiredName(context.classIdsByName, directBase.name, "class") : null;
39699
+ const classId = directBase ? requiredName(context.classIdsByName, directBase.name, "class") : null;
39700
+ return effectiveMemberIdByName(context, classId, name);
39701
+ }
39702
+ function effectiveMemberIdByName(context, startClassId, name) {
39703
+ let classId = startClassId;
38881
39704
  const visited = /* @__PURE__ */ new Set();
38882
39705
  while (classId !== null && !visited.has(classId)) {
38883
39706
  visited.add(classId);
@@ -38907,6 +39730,20 @@ function inheritedMemberId(context, ownerClass, name) {
38907
39730
  }
38908
39731
  return null;
38909
39732
  }
39733
+ function lowerClassTargetMemberId(context, declaration, base) {
39734
+ const target = argument(
39735
+ annotation(declaration.annotations, "settings"),
39736
+ "target"
39737
+ );
39738
+ if (target === null) return base?.targetMemberId ?? null;
39739
+ const memberId = resolveQualifiedMemberId(context, target);
39740
+ if (memberId === null) {
39741
+ throw new Error(
39742
+ `Class ${declaration.name} declares @settings(target: ${target.trim()}), which names no member of a known class.`
39743
+ );
39744
+ }
39745
+ return memberId;
39746
+ }
38910
39747
  function resolveQualifiedMemberId(context, expression) {
38911
39748
  const match = /^([A-Za-z_][A-Za-z0-9_]*)\.([A-Za-z_][A-Za-z0-9_]*)$/.exec(
38912
39749
  expression.trim()
@@ -38914,14 +39751,7 @@ function resolveQualifiedMemberId(context, expression) {
38914
39751
  if (!match) return referenceId(expression);
38915
39752
  const classId = context.classIdsByName.get(match[1]);
38916
39753
  if (!classId) return null;
38917
- const loweredClass = context.baseClasses.get(classId);
38918
- if (loweredClass !== void 0) return loweredClass.schema[match[2]] ?? null;
38919
- const sourceClass = context.sourceClasses.get(classId);
38920
- if (sourceClass === void 0) return null;
38921
- const member = sourceClass.members.find(
38922
- (candidate) => candidate.name === match[2]
38923
- );
38924
- return member === void 0 ? null : materializedId(member, "member", `${sourceClass.name}.${member.name}`);
39754
+ return effectiveMemberIdByName(context, classId, match[2]);
38925
39755
  }
38926
39756
  function hasAnnotation(annotations, name) {
38927
39757
  return annotation(annotations, name) !== void 0;
@@ -38978,11 +39808,25 @@ function referenceId(raw) {
38978
39808
  }
38979
39809
  const index = expression.argumentNames?.findIndex((entry) => entry === "id") ?? -1;
38980
39810
  const value = expression.args[index >= 0 ? index : 0];
38981
- return value?.kind === "litString" ? value.value : null;
39811
+ if (value?.kind === "litString") return value.value;
39812
+ const path = value === void 0 ? null : rootValuePath(value);
39813
+ return path === null ? null : `${PENDING_ID_PREFIX}root-path:${path}`;
38982
39814
  } catch {
38983
39815
  return null;
38984
39816
  }
38985
39817
  }
39818
+ function rootValuePath(expression) {
39819
+ const segments = [];
39820
+ let cursor = expression;
39821
+ while (cursor.kind === "member") {
39822
+ segments.unshift(cursor.name);
39823
+ cursor = cursor.receiver;
39824
+ }
39825
+ if (cursor.kind !== "ident") return null;
39826
+ if (cursor.name !== "root") return null;
39827
+ if (segments.length === 0) return null;
39828
+ return ["root", ...segments].join(".");
39829
+ }
38986
39830
  function isImpliedSchemaKeyOrder(order) {
38987
39831
  return order.every((key, index) => index === 0 || order[index - 1] <= key);
38988
39832
  }
@@ -39081,22 +39925,7 @@ function primitiveTypeKind(name) {
39081
39925
  return values[name] ?? null;
39082
39926
  }
39083
39927
  function memberKindTypeName(kind) {
39084
- const names = {
39085
- null: "null",
39086
- bool: "bool",
39087
- int: "int",
39088
- string: "string",
39089
- float: "float",
39090
- decimal: "decimal",
39091
- sprite: "SpriteInfo",
39092
- audio: "AudioClipInfo",
39093
- vector2: "Vector2",
39094
- vector2Int: "Vector2Int",
39095
- vector3: "Vector3",
39096
- vector3Int: "Vector3Int",
39097
- color: "Color"
39098
- };
39099
- return names[kind] ?? "object";
39928
+ return fixedSourceTypeNameForKind(kind) ?? "object";
39100
39929
  }
39101
39930
  function requiredTypeArgument(type, index) {
39102
39931
  const value = type.arguments[index];
@@ -39113,20 +39942,24 @@ function* zip(left, right) {
39113
39942
  yield [left[index], right[index]];
39114
39943
  }
39115
39944
  }
39116
- var UNSET_LIST_COLUMN_WIDTH, primitiveMemberKinds;
39945
+ var UNSET_LIST_COLUMN_WIDTH, EMPTY_GENERIC_TYPE_ENVIRONMENT, primitiveMemberKinds;
39117
39946
  var init_lower_members = __esm({
39118
39947
  "src/project-source/lower-members.ts"() {
39119
39948
  "use strict";
39120
39949
  init_src();
39121
39950
  init_lower_support();
39122
39951
  init_lower_constructors();
39952
+ init_projection();
39123
39953
  init_declared_constructors2();
39124
39954
  init_init_source();
39125
39955
  init_ui_action_source();
39126
39956
  init_entry_member_id();
39127
39957
  init_generic_argument_member_id();
39128
39958
  init_structured_leaf_source();
39959
+ init_member_kind_type_names();
39960
+ init_source_diagnostics();
39129
39961
  UNSET_LIST_COLUMN_WIDTH = -1;
39962
+ EMPTY_GENERIC_TYPE_ENVIRONMENT = /* @__PURE__ */ new Map();
39130
39963
  primitiveMemberKinds = /* @__PURE__ */ new Set([
39131
39964
  "null",
39132
39965
  "bool",
@@ -39562,6 +40395,7 @@ function lowerProjectSchemaV4(base, analysis, options = {}) {
39562
40395
  (type, ownerClass) => lowerType(context, type, ownerClass)
39563
40396
  )
39564
40397
  );
40398
+ normalizeLookupDeclaredTypes(context);
39565
40399
  for (const [memberId, placements] of context.placements) {
39566
40400
  const member = context.loweredMembers.get(memberId);
39567
40401
  if (!member) continue;
@@ -39851,6 +40685,7 @@ function emitProjectSourcesV4(manifest, options = {}) {
39851
40685
  staticInitializers: options.staticInitializers ?? /* @__PURE__ */ new Map(),
39852
40686
  defaultInitializers: options.defaultInitializers ?? /* @__PURE__ */ new Map(),
39853
40687
  fileSymbols: options.fileSymbols ?? /* @__PURE__ */ new Map(),
40688
+ collectionValuePaths: options.collectionValuePaths ?? /* @__PURE__ */ new Map(),
39854
40689
  templateNames: new Map(
39855
40690
  [...manifest.textureTemplates, ...manifest.audioTemplates].map(
39856
40691
  (template) => [template.id, template.name]
@@ -40033,6 +40868,9 @@ function emitClass(context, schemaClass2, memberIds) {
40033
40868
  id(schemaClass2.id).trimEnd(),
40034
40869
  ...schemaClass2.hiddenInMemberSelector ? ["@hidden"] : [],
40035
40870
  ...schemaClass2.allowedStorage ? [`@storage(allowed: .${enumCase(schemaClass2.allowedStorage)})`] : [],
40871
+ ...schemaClass2.targetMemberId ? [
40872
+ `@settings(target: ${qualifiedMember(context, schemaClass2.targetMemberId)})`
40873
+ ] : [],
40036
40874
  ...schemaClass2.system ? [systemAnnotation(schemaClass2.system)] : [],
40037
40875
  ...relationsAnnotations(context, schemaClass2)
40038
40876
  ];
@@ -40327,8 +41165,11 @@ function memberSettings(context, member) {
40327
41165
  `collection: ${qualifiedMember(context, member.collectionMemberId)}`
40328
41166
  );
40329
41167
  if (member.collectionValueId) {
41168
+ const rootPath = context.collectionValuePaths.get(
41169
+ member.collectionValueId
41170
+ );
40330
41171
  settings.push(
40331
- `collectionValue: Reference(id: ${quote(member.collectionValueId)})`
41172
+ rootPath === void 0 ? `collectionValue: Reference(id: ${quote(member.collectionValueId)})` : `collectionValue: Reference(${rootPath})`
40332
41173
  );
40333
41174
  }
40334
41175
  if (member.multiselect) settings.push("multiselect: true");
@@ -40462,13 +41303,7 @@ function renderMemberType(context, member) {
40462
41303
  break;
40463
41304
  }
40464
41305
  case "lookup": {
40465
- const collection = required(
40466
- context.members,
40467
- member.collectionMemberId,
40468
- "lookup collection"
40469
- );
40470
- const entry = collection.kind === "list" || collection.kind === "dictionary" ? required(context.members, collection.entryMemberId, "lookup entry") : void 0;
40471
- const target = entry ? renderMemberType(context, entry).replace(/\?$/, "") : "object";
41306
+ const target = member.declaredType === null ? renderLookupEntryType(context, member.collectionMemberId) : renderType(context, member.declaredType).replace(/\?$/u, "");
40472
41307
  value = member.multiselect ? `List<${target}>` : target;
40473
41308
  break;
40474
41309
  }
@@ -40495,6 +41330,22 @@ function renderMemberType(context, member) {
40495
41330
  if (member.kind === "generic") return value;
40496
41331
  return nullable && value !== "null" ? `${value}?` : value;
40497
41332
  }
41333
+ function renderLookupEntryType(context, collectionMemberId) {
41334
+ const collection = required(
41335
+ context.members,
41336
+ collectionMemberId,
41337
+ "lookup collection"
41338
+ );
41339
+ if (collection.kind !== "list" && collection.kind !== "dictionary") {
41340
+ return "object";
41341
+ }
41342
+ const entry = required(
41343
+ context.members,
41344
+ collection.entryMemberId,
41345
+ "lookup entry"
41346
+ );
41347
+ return renderMemberType(context, entry).replace(/\?$/u, "");
41348
+ }
40498
41349
  function renderType(context, type) {
40499
41350
  let value;
40500
41351
  switch (type.kind) {
@@ -45471,7 +46322,17 @@ function qualifiedProjectFileSymbolsV4(records2) {
45471
46322
  }
45472
46323
  return result;
45473
46324
  }
45474
- function lowerStaticValueSourcesV4(state, analysis, manifest) {
46325
+ function createValueLowerRegistryV4() {
46326
+ return {
46327
+ pendingValues: /* @__PURE__ */ new Map(),
46328
+ pendingLocalizedTexts: /* @__PURE__ */ new Map(),
46329
+ pendingBindingMembersByClassId: /* @__PURE__ */ new Map(),
46330
+ referenceObligations: [],
46331
+ initAuthoredRowIds: /* @__PURE__ */ new Map(),
46332
+ loweringFailures: []
46333
+ };
46334
+ }
46335
+ function lowerStaticValueSourcesV4(state, analysis, manifest, options = {}) {
45475
46336
  const bindings = [];
45476
46337
  for (const sourceClass of analysis.schema.classes) {
45477
46338
  for (const declaration of sourceClass.members) {
@@ -45493,9 +46354,13 @@ function lowerStaticValueSourcesV4(state, analysis, manifest) {
45493
46354
  });
45494
46355
  }
45495
46356
  }
45496
- return lowerStoredValueBindingsV4(state, manifest, bindings, { analysis });
46357
+ return lowerStoredValueBindingsV4(state, manifest, bindings, {
46358
+ analysis,
46359
+ registry: options.registry
46360
+ });
45497
46361
  }
45498
46362
  function buildValueLowerContext(state, manifest, options = {}) {
46363
+ const registry = options.registry ?? createValueLowerRegistryV4();
45499
46364
  const members = new Map(
45500
46365
  manifest.members.map((member) => [member.id, member])
45501
46366
  );
@@ -45516,6 +46381,7 @@ function buildValueLowerContext(state, manifest, options = {}) {
45516
46381
  return {
45517
46382
  state,
45518
46383
  members,
46384
+ memberOverrides: indexMemberOverrides(members),
45519
46385
  classes,
45520
46386
  classesByName,
45521
46387
  interfaceIdsByName: new Map(
@@ -45531,22 +46397,43 @@ function buildValueLowerContext(state, manifest, options = {}) {
45531
46397
  dialogueTargetsBySymbol: dialogueTargetsBySymbol(options.analysis),
45532
46398
  projectFileIdsBySymbol: projectFileIdsBySymbol(state, options.analysis),
45533
46399
  mainLocale: projectMainLocaleFromState(state),
45534
- valuePlacements: indexValuePlacements(state),
46400
+ valuePlacements: options.valuePlacements ?? indexValuePlacements(state),
45535
46401
  parsedInitializers,
45536
46402
  declaredInitializers: indexDeclaredInitializers(options.analysis),
45537
46403
  reconstructed: /* @__PURE__ */ new Map(),
45538
- pendingValues: /* @__PURE__ */ new Map(),
45539
- pendingLocalizedTexts: /* @__PURE__ */ new Map(),
46404
+ pendingValues: registry.pendingValues,
46405
+ pendingLocalizedTexts: registry.pendingLocalizedTexts,
45540
46406
  loweredMemberIdByValueId: /* @__PURE__ */ new Map(),
45541
- pendingBindingMembersByClassId: /* @__PURE__ */ new Map(),
46407
+ pendingBindingMembersByClassId: registry.pendingBindingMembersByClassId,
45542
46408
  declaredConstructors: declaredConstructorIndex,
45543
- initAuthoredRowIds: indexInitializerAuthoredRowIds(
45544
- options.analysis,
45545
- declaredConstructorIndex,
45546
- classesByName
45547
- )
46409
+ initAuthoredRowIds: seedInitAuthoredRowIds(
46410
+ registry.initAuthoredRowIds,
46411
+ indexInitializerAuthoredRowIds(
46412
+ options.analysis,
46413
+ declaredConstructorIndex,
46414
+ classesByName
46415
+ )
46416
+ ),
46417
+ referenceObligations: registry.referenceObligations,
46418
+ loweringFailures: registry.loweringFailures
45548
46419
  };
45549
46420
  }
46421
+ function indexMemberOverrides(members) {
46422
+ const result = /* @__PURE__ */ new Map();
46423
+ for (const member of members.values()) {
46424
+ if (member.overrideOf === null) continue;
46425
+ const overrides = result.get(member.overrideOf) ?? [];
46426
+ overrides.push(member.id);
46427
+ result.set(member.overrideOf, overrides);
46428
+ }
46429
+ return result;
46430
+ }
46431
+ function seedInitAuthoredRowIds(shared, discovered) {
46432
+ for (const [id2, owner] of discovered) {
46433
+ if (!shared.has(id2)) shared.set(id2, owner);
46434
+ }
46435
+ return shared;
46436
+ }
45550
46437
  function indexInitializerAuthoredRowIds(analysis, declaredConstructors2, classesByName) {
45551
46438
  const index = /* @__PURE__ */ new Map();
45552
46439
  if (analysis === void 0) return index;
@@ -45560,14 +46447,15 @@ function indexInitializerAuthoredRowIds(analysis, declaredConstructors2, classes
45560
46447
  declaration.initializer,
45561
46448
  declaration.type,
45562
46449
  declaredConstructors2,
45563
- classesByName
46450
+ classesByName,
46451
+ sourceClass.name
45564
46452
  );
45565
46453
  for (const source of sources) indexAuthoredRowIds(index, source, label);
45566
46454
  }
45567
46455
  }
45568
46456
  return index;
45569
46457
  }
45570
- function initializerSourcesRequiringEvaluation(initializer, declaredType, declaredConstructors2, classesByName) {
46458
+ function initializerSourcesRequiringEvaluation(initializer, declaredType, declaredConstructors2, classesByName, ownerClassName) {
45571
46459
  let expression;
45572
46460
  try {
45573
46461
  expression = parseExpression(initializer);
@@ -45578,7 +46466,8 @@ function initializerSourcesRequiringEvaluation(initializer, declaredType, declar
45578
46466
  if (initializerRequiresEvaluation(
45579
46467
  declaredConstructors2,
45580
46468
  expression,
45581
- targetClassName
46469
+ targetClassName,
46470
+ declaredConstructorParameterNames(declaredConstructors2, ownerClassName)
45582
46471
  )) {
45583
46472
  return [normalizeInitializerSource(initializer)];
45584
46473
  }
@@ -45586,7 +46475,12 @@ function initializerSourcesRequiringEvaluation(initializer, declaredType, declar
45586
46475
  const entryClassName = declaredEntryClassName(declaredType, classesByName);
45587
46476
  if (body.kind === "litList") {
45588
46477
  return topLevelEntrySlices(initializer, "[", "]").filter(
45589
- (entry) => entrySliceRequiresEvaluation(entry, declaredConstructors2, entryClassName)
46478
+ (entry) => entrySliceRequiresEvaluation(
46479
+ entry,
46480
+ declaredConstructors2,
46481
+ entryClassName,
46482
+ ownerClassName
46483
+ )
45590
46484
  );
45591
46485
  }
45592
46486
  if (body.kind === "litDict") {
@@ -45594,7 +46488,8 @@ function initializerSourcesRequiringEvaluation(initializer, declaredType, declar
45594
46488
  (entry) => entrySliceRequiresEvaluation(
45595
46489
  entry,
45596
46490
  declaredConstructors2,
45597
- entryClassName
46491
+ entryClassName,
46492
+ ownerClassName
45598
46493
  )
45599
46494
  );
45600
46495
  }
@@ -45608,12 +46503,13 @@ function declaredEntryClassName(declaredType, classesByName) {
45608
46503
  if (entryType === void 0) return null;
45609
46504
  return declaredClassName(entryType, classesByName);
45610
46505
  }
45611
- function entrySliceRequiresEvaluation(entry, declaredConstructors2, entryClassName) {
46506
+ function entrySliceRequiresEvaluation(entry, declaredConstructors2, entryClassName, ownerClassName) {
45612
46507
  try {
45613
46508
  return initializerRequiresEvaluation(
45614
46509
  declaredConstructors2,
45615
46510
  parseExpression(entry),
45616
- entryClassName
46511
+ entryClassName,
46512
+ declaredConstructorParameterNames(declaredConstructors2, ownerClassName)
45617
46513
  );
45618
46514
  } catch {
45619
46515
  return false;
@@ -45664,8 +46560,11 @@ function lowerStoredValueBindingsV4(state, manifest, bindings, options = {}) {
45664
46560
  seeds
45665
46561
  };
45666
46562
  }
45667
- function lowerMemberDefaultSourcesV4(state, analysis, manifest) {
45668
- const context = buildValueLowerContext(state, manifest, { analysis });
46563
+ function lowerMemberDefaultSourcesV4(state, analysis, manifest, options = {}) {
46564
+ const context = buildValueLowerContext(state, manifest, {
46565
+ analysis,
46566
+ registry: options.registry
46567
+ });
45669
46568
  const pulledValueIds = /* @__PURE__ */ new Set();
45670
46569
  for (const record3 of Object.values(state)) {
45671
46570
  if (record3.recordKind === "value") pulledValueIds.add(record3.recordId);
@@ -45697,7 +46596,8 @@ function lowerMemberDefaultSourcesV4(state, analysis, manifest) {
45697
46596
  const requiresEvaluation = initializerRequiresEvaluation(
45698
46597
  context.declaredConstructors,
45699
46598
  annotatedValue(expression).expression,
45700
- memberClassName(context, member)
46599
+ memberClassName(context, member),
46600
+ bindingRuntimeIdentifiers(context, binding)
45701
46601
  );
45702
46602
  const baseData3 = stateData(context, "member", memberId);
45703
46603
  const backed = baseData3 === null || requiresEvaluation ? null : rowBackedDefaultBody(baseData3, (id2) => pulledValueIds.has(id2));
@@ -45708,7 +46608,9 @@ function lowerMemberDefaultSourcesV4(state, analysis, manifest) {
45708
46608
  );
45709
46609
  continue;
45710
46610
  }
45711
- if (!defaultRequiresOwnedRows(context, member, expression)) continue;
46611
+ if (!defaultRequiresOwnedRows(context, member, expression, binding)) {
46612
+ continue;
46613
+ }
45712
46614
  const rootValueId = pendingValueId(binding, `${label}.default`);
45713
46615
  const seed = lowerStaticSeed(
45714
46616
  context,
@@ -45730,12 +46632,379 @@ function lowerMemberDefaultSourcesV4(state, analysis, manifest) {
45730
46632
  seeds
45731
46633
  };
45732
46634
  }
45733
- function defaultRequiresOwnedRows(context, member, sourceExpression) {
46635
+ function drainValueReferenceObligationsV4(state, manifest, options) {
46636
+ const deletedValueIds = options.deletedValueIds ?? /* @__PURE__ */ new Set();
46637
+ const survivingState = withoutDeletedValues(state, deletedValueIds);
46638
+ const context = buildValueLowerContext(survivingState, manifest, {
46639
+ analysis: options.analysis,
46640
+ registry: options.registry,
46641
+ valuePlacements: indexValuePlacements(survivingState, {
46642
+ pendingValues: options.registry.pendingValues,
46643
+ deletedValueIds
46644
+ })
46645
+ });
46646
+ const failures = [];
46647
+ for (const recorded of options.registry.loweringFailures) {
46648
+ failures.push({
46649
+ message: recorded.message,
46650
+ ...composeReferenceSitePosition(recorded.site, options.sourceTextByUri)
46651
+ });
46652
+ }
46653
+ for (const obligation of [
46654
+ ...collectionValueObligations(manifest),
46655
+ ...options.registry.referenceObligations
46656
+ ]) {
46657
+ const failure = settleReferenceObligation(
46658
+ context,
46659
+ obligation,
46660
+ deletedValueIds
46661
+ );
46662
+ if (failure === null) continue;
46663
+ failures.push({
46664
+ message: failure,
46665
+ ...composeReferenceSitePosition(obligation.site, options.sourceTextByUri)
46666
+ });
46667
+ }
46668
+ return failures;
46669
+ }
46670
+ function withoutDeletedValues(state, deletedValueIds) {
46671
+ if (deletedValueIds.size === 0) return state;
46672
+ const surviving = { ...state };
46673
+ for (const valueId of deletedValueIds) delete surviving[`value:${valueId}`];
46674
+ return surviving;
46675
+ }
46676
+ function collectionValueObligations(manifest) {
46677
+ const obligations = [];
46678
+ for (const member of manifest.members) {
46679
+ if (member.kind !== "lookup") continue;
46680
+ if (typeof member.collectionValueId !== "string") continue;
46681
+ obligations.push({
46682
+ kind: "collectionValue",
46683
+ site: {
46684
+ uri: member.source.span.path,
46685
+ declarationStart: member.source.span.start,
46686
+ expression: null,
46687
+ label: member.name
46688
+ },
46689
+ memberId: member.id,
46690
+ collectionMemberId: member.collectionMemberId,
46691
+ collectionValueId: member.collectionValueId
46692
+ });
46693
+ }
46694
+ return obligations;
46695
+ }
46696
+ function settleReferenceObligation(context, obligation, deletedValueIds) {
46697
+ try {
46698
+ if (obligation.kind === "reference") {
46699
+ settleReferenceMember(context, obligation, deletedValueIds);
46700
+ return null;
46701
+ }
46702
+ if (obligation.kind === "constructorProjection") {
46703
+ settleConstructorProjection(context, obligation, deletedValueIds);
46704
+ return null;
46705
+ }
46706
+ settleCollectionValue(context, obligation);
46707
+ return null;
46708
+ } catch (error) {
46709
+ return error instanceof Error ? error.message : String(error);
46710
+ }
46711
+ }
46712
+ function settleReferenceMember(context, obligation, deletedValueIds) {
46713
+ const member = context.members.get(obligation.memberId);
46714
+ if (member?.kind !== "lookup" && member?.kind !== "dialogueLookup") {
46715
+ throw new Error(
46716
+ `Reference member ${obligation.site.label} no longer declares a lookup after this push.`
46717
+ );
46718
+ }
46719
+ if (obligation.target.spelling === "key") {
46720
+ settleKeyReference(context, member, obligation.target, obligation);
46721
+ return;
46722
+ }
46723
+ const target = resolveObligationTarget(
46724
+ context,
46725
+ obligation.target,
46726
+ deletedValueIds,
46727
+ `Reference member ${member.name}`
46728
+ );
46729
+ validateReferenceContract(context, member, target, obligation.ownerValueId);
46730
+ }
46731
+ function settleKeyReference(context, member, spelling, obligation) {
46732
+ if (member.kind !== "lookup") {
46733
+ throw new Error(
46734
+ `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.`
46735
+ );
46736
+ }
46737
+ const collection = context.members.get(member.collectionMemberId);
46738
+ if (collection?.kind !== "list") {
46739
+ throw new Error(
46740
+ `Reference member ${member.name} uses the key form, but its declared collection ${collectionMemberName(context, member)} is not an indexed list.`
46741
+ );
46742
+ }
46743
+ const schemaKey = keyIndexSchemaKey(context, member, collection, spelling);
46744
+ const resolution = resolveLookupCollectionValueIds(
46745
+ context,
46746
+ member,
46747
+ collection,
46748
+ obligation.ownerValueId
46749
+ );
46750
+ if (resolution.valueIds.length === 0) {
46751
+ throw new Error(
46752
+ `Reference member ${member.name} key ${JSON.stringify(spelling.key)} cannot be resolved: collection ${collectionMemberName(context, member)} has no value row in the project after this push.`
46753
+ );
46754
+ }
46755
+ const matches = [];
46756
+ for (const collectionValueId of resolution.valueIds) {
46757
+ for (const entryId of collectionEntryIds(
46758
+ context,
46759
+ collection,
46760
+ collectionValueId
46761
+ )) {
46762
+ const entry = valueData(context, entryId);
46763
+ if (!entry || !isObjectRecord2(entry.value)) continue;
46764
+ const fieldRowId = entry.value[schemaKey];
46765
+ if (typeof fieldRowId !== "string") continue;
46766
+ if (valueData(context, fieldRowId)?.value !== spelling.key) continue;
46767
+ matches.push(entryId);
46768
+ }
46769
+ }
46770
+ if (matches.length === 0) {
46771
+ throw new Error(
46772
+ `Reference member ${member.name} key ${JSON.stringify(spelling.key)} matches no row of collection ${collectionMemberName(context, member)} under index ${schemaKey}.`
46773
+ );
46774
+ }
46775
+ if (matches.length > 1) {
46776
+ throw new Error(
46777
+ `Collection ${collectionMemberName(context, member)} holds ${matches.length} rows whose ${schemaKey} is ${JSON.stringify(spelling.key)}, but that index is unique. Fix the duplicate rows; the key form needs exactly one.`
46778
+ );
46779
+ }
46780
+ const targetId = matches[0];
46781
+ if (targetId === void 0) {
46782
+ throw new Error(
46783
+ `Reference member ${member.name} key ${JSON.stringify(spelling.key)} resolved to no target id.`
46784
+ );
46785
+ }
46786
+ const target = referenceTargetById(context, targetId, spelling.typeName);
46787
+ if (target === null) {
46788
+ throw new Error(
46789
+ `Reference member ${member.name} key ${JSON.stringify(spelling.key)} resolved to ${targetId}, which names no value row in the project after this push.`
46790
+ );
46791
+ }
46792
+ validateReferenceContract(context, member, target, obligation.ownerValueId);
46793
+ spelling.patch.ids[spelling.patch.index] = targetId;
46794
+ const patched = spelling.patch.ids.filter((id2) => !isPendingId(id2));
46795
+ if (new Set(patched).size !== patched.length) {
46796
+ throw new Error(
46797
+ `Reference member ${member.name} cannot contain duplicate targets.`
46798
+ );
46799
+ }
46800
+ }
46801
+ function keyIndexSchemaKey(context, member, collection, spelling) {
46802
+ const indexes = collection.indexes ?? [];
46803
+ if (spelling.indexSchemaKey !== null) {
46804
+ const named = indexes.find(
46805
+ (candidate) => candidate.schemaKey === spelling.indexSchemaKey
46806
+ );
46807
+ if (named === void 0) {
46808
+ throw new Error(
46809
+ `Reference member ${member.name} names index ${spelling.indexSchemaKey}, which collection ${collectionMemberName(context, member)} does not declare.`
46810
+ );
46811
+ }
46812
+ if (!named.unique) {
46813
+ throw new Error(
46814
+ `Reference member ${member.name} names index ${spelling.indexSchemaKey}, which is not unique. The key form resolves exactly one row, so it needs a unique index.`
46815
+ );
46816
+ }
46817
+ return named.schemaKey;
46818
+ }
46819
+ const unique = indexes.filter((candidate) => candidate.unique);
46820
+ if (unique.length === 0) {
46821
+ throw new Error(
46822
+ `Reference member ${member.name} uses the key form, but collection ${collectionMemberName(context, member)} declares no unique index.`
46823
+ );
46824
+ }
46825
+ if (unique.length > 1) {
46826
+ throw new Error(
46827
+ `Reference member ${member.name} uses the bare key form, but collection ${collectionMemberName(context, member)} declares ${unique.length} unique indexes (${unique.map((candidate) => candidate.schemaKey).join(", ")}). Write index: to name one.`
46828
+ );
46829
+ }
46830
+ const only = unique[0];
46831
+ if (only === void 0) {
46832
+ throw new Error(
46833
+ `Reference member ${member.name} resolved no unique index for collection ${collectionMemberName(context, member)}.`
46834
+ );
46835
+ }
46836
+ return only.schemaKey;
46837
+ }
46838
+ function collectionEntryIds(context, collection, collectionValueId) {
46839
+ if (collection.listKind === "unordered") {
46840
+ return [
46841
+ ...context.valuePlacements.valueIdsByContainerId.get(
46842
+ collectionValueId
46843
+ ) ?? []
46844
+ ];
46845
+ }
46846
+ const body = valueData(context, collectionValueId)?.value;
46847
+ return Array.isArray(body) ? body.filter((entry) => typeof entry === "string") : [];
46848
+ }
46849
+ function settleConstructorProjection(context, obligation, deletedValueIds) {
46850
+ const schemaClass2 = context.classes.get(obligation.schemaClassId);
46851
+ if (schemaClass2 === void 0) {
46852
+ throw new Error(
46853
+ `Constructor projection ${obligation.parameterName} names class ${obligation.schemaClassId}, which this push does not declare.`
46854
+ );
46855
+ }
46856
+ const projectedMember = context.members.get(obligation.projectedMemberId);
46857
+ if (projectedMember?.kind !== "lookup") {
46858
+ throw new Error(
46859
+ `Class ${schemaClass2.name} constructor projection ${obligation.parameterName} does not resolve to a Lookup field.`
46860
+ );
46861
+ }
46862
+ const target = referenceTargetById(context, obligation.targetId, "");
46863
+ if (target === null || target.kind !== "value") {
46864
+ const owner = context.initAuthoredRowIds.get(obligation.targetId);
46865
+ if (owner !== void 0) {
46866
+ throw new Error(
46867
+ `Class ${schemaClass2.name} constructor argument ${obligation.parameterName} targets value ${obligation.targetId}, which is authored inside the computed initializer of ${owner} and therefore stores no row. Point it at a literal default's row instead.`
46868
+ );
46869
+ }
46870
+ if (deletedValueIds.has(obligation.targetId)) {
46871
+ throw new Error(
46872
+ `Class ${schemaClass2.name} constructor argument ${obligation.parameterName} targets value ${obligation.targetId}, which this push deletes.`
46873
+ );
46874
+ }
46875
+ throw new Error(
46876
+ `Class ${schemaClass2.name} constructor argument ${obligation.parameterName} targets a missing value ${obligation.targetId}. No row in the project carries that id after this push.`
46877
+ );
46878
+ }
46879
+ validateReferenceContract(
46880
+ context,
46881
+ projectedMember,
46882
+ { ...target, genericTypeName: null },
46883
+ obligation.ownerValueId
46884
+ );
46885
+ validateAnimationProjectionBinding(
46886
+ context,
46887
+ schemaClass2,
46888
+ obligation.environment,
46889
+ target.id
46890
+ );
46891
+ }
46892
+ function settleCollectionValue(context, obligation) {
46893
+ const collection = context.members.get(obligation.collectionMemberId);
46894
+ if (collection?.kind !== "list" && collection?.kind !== "dictionary") {
46895
+ throw new Error(
46896
+ `Lookup member ${obligation.site.label} declares collectionValue ${obligation.collectionValueId} for a collection member that this push does not declare.`
46897
+ );
46898
+ }
46899
+ const row = valueData(context, obligation.collectionValueId);
46900
+ if (row === null) {
46901
+ throw new Error(
46902
+ `Lookup member ${obligation.site.label} declares collectionValue ${obligation.collectionValueId}, which names no value row in the project after this push. Searched the rows this push creates and the project's existing rows.`
46903
+ );
46904
+ }
46905
+ const declaredMemberIds = declaredCollectionMemberIds(
46906
+ context,
46907
+ obligation.collectionMemberId
46908
+ );
46909
+ const owners = valueOwningMemberIds(context, obligation.collectionValueId);
46910
+ if (owners.size === 0) return;
46911
+ for (const owner of owners) {
46912
+ if (declaredMemberIds.has(owner)) return;
46913
+ }
46914
+ throw new Error(
46915
+ `Lookup member ${obligation.site.label} declares collectionValue ${obligation.collectionValueId}, which is a value of ${[...owners].map((owner) => context.members.get(owner)?.name ?? owner).join(", ")} rather than of collection ${collection.name}.`
46916
+ );
46917
+ }
46918
+ function resolveObligationTarget(context, spelling, deletedValueIds, subject) {
46919
+ if (spelling.spelling === "symbol") return spelling.resolved;
46920
+ if (spelling.spelling === "key") {
46921
+ throw new Error(
46922
+ `${subject} carries a key spelling with no collection to resolve it against.`
46923
+ );
46924
+ }
46925
+ const target = referenceTargetById(context, spelling.id, spelling.typeName);
46926
+ if (target !== null) return target;
46927
+ if (deletedValueIds.has(spelling.id)) {
46928
+ throw new Error(
46929
+ `${subject} targets ${spelling.id}, which this push deletes. Keep the declaration that creates that row, or point the reference at a row the push leaves behind.`
46930
+ );
46931
+ }
46932
+ const owner = context.initAuthoredRowIds.get(spelling.id);
46933
+ if (owner !== void 0) {
46934
+ throw new Error(
46935
+ `${subject} targets ${spelling.id}, which is authored inside the computed initializer of ${owner} and therefore stores no row. Point it at a literal default's row instead.`
46936
+ );
46937
+ }
46938
+ throw new Error(
46939
+ `${subject} targets ${spelling.id}, which names no value or dialogue row in the project after this push. Searched the rows this push creates and the project's existing rows.`
46940
+ );
46941
+ }
46942
+ function composeReferenceSitePosition(site, sourceTextByUri) {
46943
+ const declaration = {
46944
+ file: site.uri,
46945
+ line: site.declarationStart.line + 1,
46946
+ column: site.declarationStart.character + 1
46947
+ };
46948
+ const text = sourceTextByUri?.get(site.uri);
46949
+ if (site.expression === null || text === void 0) return declaration;
46950
+ const declarationOffset = sourceOffsetAtPosition(text, site.declarationStart);
46951
+ if (declarationOffset === null) return declaration;
46952
+ const initializerOffset = text.indexOf(
46953
+ site.expression.initializer,
46954
+ declarationOffset
46955
+ );
46956
+ if (initializerOffset < 0) return declaration;
46957
+ const origin = sourcePositionAtOffset(text, initializerOffset);
46958
+ const { line, column } = site.expression.pos;
46959
+ if (line === 1) {
46960
+ return {
46961
+ file: site.uri,
46962
+ line: origin.line + 1,
46963
+ column: origin.character + column
46964
+ };
46965
+ }
46966
+ return { file: site.uri, line: origin.line + line, column };
46967
+ }
46968
+ function sourceOffsetAtPosition(text, position) {
46969
+ let offset = 0;
46970
+ for (let line = 0; line < position.line; line += 1) {
46971
+ const next = text.indexOf("\n", offset);
46972
+ if (next < 0) return null;
46973
+ offset = next + 1;
46974
+ }
46975
+ const candidate = offset + position.character;
46976
+ return candidate > text.length ? null : candidate;
46977
+ }
46978
+ function sourcePositionAtOffset(text, offset) {
46979
+ let line = 0;
46980
+ let lineStart = 0;
46981
+ for (let cursor = 0; cursor < offset; cursor += 1) {
46982
+ if (text[cursor] !== "\n") continue;
46983
+ line += 1;
46984
+ lineStart = cursor + 1;
46985
+ }
46986
+ return { line, character: offset - lineStart };
46987
+ }
46988
+ function declaredCollectionMemberIds(context, collectionMemberId) {
46989
+ const ids = /* @__PURE__ */ new Set([collectionMemberId]);
46990
+ const pending = [collectionMemberId];
46991
+ while (pending.length > 0) {
46992
+ const current = pending.pop();
46993
+ for (const override of context.memberOverrides.get(current) ?? []) {
46994
+ if (ids.has(override)) continue;
46995
+ ids.add(override);
46996
+ pending.push(override);
46997
+ }
46998
+ }
46999
+ return ids;
47000
+ }
47001
+ function defaultRequiresOwnedRows(context, member, sourceExpression, source) {
45734
47002
  const expression = annotatedValue(sourceExpression).expression;
45735
47003
  if (initializerRequiresEvaluation(
45736
47004
  context.declaredConstructors,
45737
47005
  expression,
45738
- memberClassName(context, member)
47006
+ memberClassName(context, member),
47007
+ bindingRuntimeIdentifiers(context, source)
45739
47008
  )) {
45740
47009
  return false;
45741
47010
  }
@@ -45794,12 +47063,33 @@ function memberClassName(context, member) {
45794
47063
  if (member.kind !== "class") return null;
45795
47064
  return context.classes.get(member.classId)?.name ?? null;
45796
47065
  }
47066
+ function bindingRuntimeIdentifiers(context, source) {
47067
+ const ownerClassName = source.ownerClassId === void 0 ? null : context.classes.get(source.ownerClassId)?.name ?? null;
47068
+ return declaredConstructorParameterNames(
47069
+ context.declaredConstructors,
47070
+ ownerClassName
47071
+ );
47072
+ }
47073
+ function bindingGenericEnvironment(context, source) {
47074
+ if (source.ownerClassId === void 0) return /* @__PURE__ */ new Map();
47075
+ return classGenericEnvironment(context, source.ownerClassId);
47076
+ }
47077
+ function classGenericEnvironment(context, classId) {
47078
+ const bindings = classGenericBindings(context.classes, classId);
47079
+ const environment = /* @__PURE__ */ new Map();
47080
+ for (const genericParamId of bindings.keys()) {
47081
+ const memberId = terminalGenericBindingMemberId(genericParamId, bindings);
47082
+ if (memberId !== void 0) environment.set(genericParamId, memberId);
47083
+ }
47084
+ return environment;
47085
+ }
45797
47086
  function lowerRowBackedDefault(context, member, backed, baseData3, binding) {
45798
47087
  const baseDefault = isObjectRecord2(baseData3.defaultValue) ? baseData3.defaultValue : {};
45799
47088
  const storedClassId = stringOrNull(baseDefault.classId);
45800
47089
  const expression = annotatedValue(
45801
47090
  parseCachedInitializer(context.parsedInitializers, binding.initializer)
45802
47091
  ).expression;
47092
+ const bindingEnvironment = bindingGenericEnvironment(context, binding);
45803
47093
  if (member.kind === "class" && backed.kind === "class") {
45804
47094
  if (expression.kind !== "new") {
45805
47095
  throw new Error(`Default value ${binding.label} requires new(...).`);
@@ -45817,7 +47107,7 @@ function lowerRowBackedDefault(context, member, backed, baseData3, binding) {
45817
47107
  context,
45818
47108
  classId,
45819
47109
  member,
45820
- void 0
47110
+ bindingEnvironment
45821
47111
  );
45822
47112
  const environment = inferAnimationChildOverrideLowerEnvironment(
45823
47113
  context,
@@ -45846,7 +47136,7 @@ function lowerRowBackedDefault(context, member, backed, baseData3, binding) {
45846
47136
  );
45847
47137
  for (const assignment of expression.initializer ?? []) {
45848
47138
  const childMember = classMemberByName(context, classId, assignment.name);
45849
- if (schemaClass2.constructorProjections?.some(
47139
+ if (inheritedConstructorProjections(context.classes, schemaClass2.id).some(
45850
47140
  (projection) => projection.memberId === childMember.id
45851
47141
  )) {
45852
47142
  throw new Error(
@@ -45903,7 +47193,7 @@ function lowerRowBackedDefault(context, member, backed, baseData3, binding) {
45903
47193
  element,
45904
47194
  itemId,
45905
47195
  binding,
45906
- void 0,
47196
+ bindingEnvironment,
45907
47197
  entrySlices[index]
45908
47198
  );
45909
47199
  });
@@ -45937,7 +47227,7 @@ function lowerRowBackedDefault(context, member, backed, baseData3, binding) {
45937
47227
  entry.value,
45938
47228
  entryId,
45939
47229
  binding,
45940
- void 0,
47230
+ bindingEnvironment,
45941
47231
  dictionaryValueSlice(entrySlices[entryIndex])
45942
47232
  );
45943
47233
  entryIndex += 1;
@@ -45980,7 +47270,15 @@ function lowerStoredBinding(context, binding, memberValueIds, seeds) {
45980
47270
  (pending) => pending.id
45981
47271
  )
45982
47272
  );
45983
- lowerValueRow(context, member, expression, valueId, binding);
47273
+ lowerValueRow(
47274
+ context,
47275
+ member,
47276
+ expression,
47277
+ valueId,
47278
+ binding,
47279
+ bindingGenericEnvironment(context, binding),
47280
+ binding.initializer
47281
+ );
45984
47282
  const pendingRows = [...context.pendingValues.values()].filter(
45985
47283
  (row) => !existingPendingValueIds.has(row.id)
45986
47284
  );
@@ -46080,7 +47378,7 @@ function lowerStaticSeed(context, member, sourceExpression, source, valueId) {
46080
47378
  rows,
46081
47379
  localizedTexts,
46082
47380
  void 0,
46083
- void 0,
47381
+ bindingGenericEnvironment(context, source),
46084
47382
  void 0,
46085
47383
  source.initializer
46086
47384
  );
@@ -46111,7 +47409,8 @@ function lowerSeedRow(context, member, sourceExpression, source, valueId, path,
46111
47409
  if (initializerRequiresEvaluation(
46112
47410
  context.declaredConstructors,
46113
47411
  expression,
46114
- memberClassName(context, resolvedMember)
47412
+ memberClassName(context, resolvedMember),
47413
+ bindingRuntimeIdentifiers(context, source)
46115
47414
  )) {
46116
47415
  const code = initializerExpressionSlice(authoredSlice);
46117
47416
  if (code === void 0) {
@@ -46128,11 +47427,7 @@ function lowerSeedRow(context, member, sourceExpression, source, valueId, path,
46128
47427
  ...containerId === void 0 ? {} : { containerId },
46129
47428
  ...initSourceValueId === null ? {} : { sourceValueId: initSourceValueId }
46130
47429
  };
46131
- if (rows.has(valueId)) {
46132
- throw new Error(`Value construction reuses identity ${valueId}.`);
46133
- }
46134
- rows.set(valueId, initRow);
46135
- context.pendingValues.set(valueId, initRow);
47430
+ registerSeedRow(context, rows, initRow, source, sourceExpression);
46136
47431
  return initRow;
46137
47432
  }
46138
47433
  let value;
@@ -46194,7 +47489,8 @@ function lowerSeedRow(context, member, sourceExpression, source, valueId, path,
46194
47489
  effectiveClass,
46195
47490
  expression,
46196
47491
  valueId,
46197
- environment
47492
+ environment,
47493
+ source
46198
47494
  )) {
46199
47495
  const childPath = `${path}.${schemaKey}`;
46200
47496
  const childValueId = pendingNestedValueId(source, childPath);
@@ -46220,9 +47516,10 @@ function lowerSeedRow(context, member, sourceExpression, source, valueId, path,
46220
47516
  effectiveClass.id,
46221
47517
  assignment.name
46222
47518
  );
46223
- if (effectiveClass.constructorProjections?.some(
46224
- (projection) => projection.memberId === childMember.id
46225
- )) {
47519
+ if (inheritedConstructorProjections(
47520
+ context.classes,
47521
+ effectiveClass.id
47522
+ ).some((projection) => projection.memberId === childMember.id)) {
46226
47523
  throw new Error(
46227
47524
  `Class value ${path}.${assignment.name} is constructor-projected and must be set through its named constructor argument.`
46228
47525
  );
@@ -46351,13 +47648,24 @@ function lowerSeedRow(context, member, sourceExpression, source, valueId, path,
46351
47648
  ...genericBindings === void 0 ? {} : { genericBindings },
46352
47649
  ...sourceValueId === null ? {} : { sourceValueId }
46353
47650
  };
46354
- if (rows.has(valueId)) {
46355
- throw new Error(`Value construction reuses identity ${valueId}.`);
46356
- }
46357
- rows.set(valueId, row);
46358
- context.pendingValues.set(valueId, row);
47651
+ registerSeedRow(context, rows, row, source, sourceExpression);
46359
47652
  return row;
46360
47653
  }
47654
+ function registerSeedRow(context, rows, row, source, expression) {
47655
+ if (rows.has(row.id)) {
47656
+ throw new Error(`Value construction reuses identity ${row.id}.`);
47657
+ }
47658
+ if (context.pendingValues.has(row.id)) {
47659
+ context.loweringFailures.push({
47660
+ message: `Value ${source.label} claims identity ${row.id}, which another value created by this push already owns. Two new rows cannot share one @id.`,
47661
+ site: referenceSite(source, expression)
47662
+ });
47663
+ rows.set(row.id, row);
47664
+ return;
47665
+ }
47666
+ rows.set(row.id, row);
47667
+ context.pendingValues.set(row.id, row);
47668
+ }
46361
47669
  function materializeRequiredDefaults(context, member, effectiveClass, body, source, path, rows, localizedTexts, environment) {
46362
47670
  if (member.partial === true) return;
46363
47671
  for (const [schemaKey, childMember] of storedSchemaEntries(
@@ -46373,7 +47681,13 @@ function materializeRequiredDefaults(context, member, effectiveClass, body, sour
46373
47681
  context,
46374
47682
  recursivePartialMember(member, childMember),
46375
47683
  parseCachedInitializer(context.parsedInitializers, initializer),
46376
- source,
47684
+ // P47 §1.6. This subtree's AST comes from the child member's own declared
47685
+ // initializer, not the binding's, and every position in it is measured
47686
+ // from there. Handing down a binding that names the text keeps "positions
47687
+ // are relative to `source.initializer`" true everywhere, which is what
47688
+ // `referenceSite` composes against. Identity derivation reads the uri and
47689
+ // range, so the rows this produces are unchanged.
47690
+ { ...source, initializer },
46377
47691
  `${path}.${schemaKey}`,
46378
47692
  rows,
46379
47693
  localizedTexts,
@@ -46475,7 +47789,8 @@ function lowerValueRow(context, member, sourceExpression, expectedValueId, sourc
46475
47789
  if (initializerRequiresEvaluation(
46476
47790
  context.declaredConstructors,
46477
47791
  expression,
46478
- memberClassName(context, resolvedMember)
47792
+ memberClassName(context, resolvedMember),
47793
+ bindingRuntimeIdentifiers(context, source)
46479
47794
  )) {
46480
47795
  const code = initializerExpressionSlice(authoredSlice);
46481
47796
  if (code === void 0) {
@@ -46502,12 +47817,13 @@ function lowerValueRow(context, member, sourceExpression, expectedValueId, sourc
46502
47817
  expression,
46503
47818
  base,
46504
47819
  source,
46505
- environment
47820
+ environment,
47821
+ authoredSlice
46506
47822
  );
46507
47823
  addReconstructed(context, "value", expectedValueId, value, source.source);
46508
47824
  return expectedValueId;
46509
47825
  }
46510
- function lowerValueBody(context, member, expression, base, source, environment) {
47826
+ function lowerValueBody(context, member, expression, base, source, environment, authoredSlice) {
46511
47827
  const next = valueFileFields(base);
46512
47828
  if (expression.kind === "litNull") return { ...next, value: null };
46513
47829
  switch (member.kind) {
@@ -46544,7 +47860,8 @@ function lowerValueBody(context, member, expression, base, source, environment)
46544
47860
  context,
46545
47861
  member,
46546
47862
  expression,
46547
- typeof base.id === "string" ? base.id : null
47863
+ typeof base.id === "string" ? base.id : null,
47864
+ source
46548
47865
  )
46549
47866
  };
46550
47867
  case "sprite":
@@ -46566,7 +47883,8 @@ function lowerValueBody(context, member, expression, base, source, environment)
46566
47883
  expression,
46567
47884
  base,
46568
47885
  source,
46569
- environment
47886
+ environment,
47887
+ authoredSlice
46570
47888
  );
46571
47889
  case "list":
46572
47890
  return lowerListValue(
@@ -46592,7 +47910,7 @@ function lowerValueBody(context, member, expression, base, source, environment)
46592
47910
  );
46593
47911
  }
46594
47912
  }
46595
- function lowerClassValue(context, member, expression, base, source, outerEnvironment) {
47913
+ function lowerClassValue(context, member, expression, base, source, outerEnvironment, authoredSlice) {
46596
47914
  if (expression.kind !== "new")
46597
47915
  throw new Error("Class values require new(...).");
46598
47916
  const currentClassId = stringOrNull(base.classId) ?? member.classId;
@@ -46630,6 +47948,7 @@ function lowerClassValue(context, member, expression, base, source, outerEnviron
46630
47948
  );
46631
47949
  const baseBody = isObjectRecord2(base.value) ? base.value : {};
46632
47950
  const body = { ...baseBody };
47951
+ const assignmentSlices = objectInitializerSlices(authoredSlice);
46633
47952
  lowerConstructorProjections(
46634
47953
  context,
46635
47954
  schemaClass2,
@@ -46642,7 +47961,7 @@ function lowerClassValue(context, member, expression, base, source, outerEnviron
46642
47961
  );
46643
47962
  for (const assignment of expression.initializer ?? []) {
46644
47963
  const childMember = classMemberByName(context, classId, assignment.name);
46645
- if (schemaClass2.constructorProjections?.some(
47964
+ if (inheritedConstructorProjections(context.classes, schemaClass2.id).some(
46646
47965
  (projection) => projection.memberId === childMember.id
46647
47966
  )) {
46648
47967
  throw new Error(
@@ -46651,9 +47970,26 @@ function lowerClassValue(context, member, expression, base, source, outerEnviron
46651
47970
  }
46652
47971
  const childId = baseBody[assignment.name];
46653
47972
  if (typeof childId !== "string") {
46654
- throw new Error(
46655
- `Class value ${String(base.id)}.${assignment.name} has no existing structural row. New structural rows are not implemented by this slice.`
47973
+ const symbolId2 = sourceValueSymbol(context, assignment.value);
47974
+ if (symbolId2 !== null) {
47975
+ throw new Error(
47976
+ `Class value ${String(base.id)}.${assignment.name} cannot place existing value ${symbolId2} into a new structural slot. Create the nested value inline so the atomic construction transaction can own it.`
47977
+ );
47978
+ }
47979
+ body[assignment.name] = lowerSeedChild(
47980
+ context,
47981
+ recursivePartialMember(member, childMember),
47982
+ assignment.value,
47983
+ source,
47984
+ `${String(base.id)}.${assignment.name}`,
47985
+ /* @__PURE__ */ new Map(),
47986
+ /* @__PURE__ */ new Map(),
47987
+ void 0,
47988
+ environment,
47989
+ void 0,
47990
+ assignmentSlices.get(assignment.name)
46656
47991
  );
47992
+ continue;
46657
47993
  }
46658
47994
  const symbolId = sourceValueSymbol(context, assignment.value);
46659
47995
  if (symbolId !== null) {
@@ -46671,7 +48007,8 @@ function lowerClassValue(context, member, expression, base, source, outerEnviron
46671
48007
  assignment.value,
46672
48008
  childId,
46673
48009
  source,
46674
- environment
48010
+ environment,
48011
+ assignmentSlices.get(assignment.name)
46675
48012
  );
46676
48013
  }
46677
48014
  return {
@@ -46686,7 +48023,8 @@ function lowerConstructorProjections(context, schemaClass2, expression, base, ba
46686
48023
  schemaClass2,
46687
48024
  expression,
46688
48025
  typeof base.id === "string" ? base.id : null,
46689
- environment
48026
+ environment,
48027
+ source
46690
48028
  );
46691
48029
  for (const { schemaKey, target } of resolved) {
46692
48030
  const childValueId = baseBody[schemaKey];
@@ -46706,8 +48044,44 @@ function lowerConstructorProjections(context, schemaClass2, expression, base, ba
46706
48044
  body[schemaKey] = childValueId;
46707
48045
  }
46708
48046
  }
46709
- function resolveConstructorProjectionArguments(context, schemaClass2, expression, ownerValueId, environment) {
46710
- const projections = schemaClass2.constructorProjections ?? [];
48047
+ function classInheritanceChain(classes, classId) {
48048
+ const chain = [];
48049
+ const visited = /* @__PURE__ */ new Set();
48050
+ let current = classId;
48051
+ while (current !== null && !visited.has(current)) {
48052
+ visited.add(current);
48053
+ const schemaClass2 = classes.get(current);
48054
+ if (schemaClass2 === void 0) break;
48055
+ chain.push(schemaClass2);
48056
+ current = schemaClass2.extendsClassId;
48057
+ }
48058
+ return chain;
48059
+ }
48060
+ function inheritedConstructorProjections(classes, classId) {
48061
+ const resolved = [];
48062
+ const claimed = /* @__PURE__ */ new Set();
48063
+ for (const schemaClass2 of classInheritanceChain(classes, classId)) {
48064
+ for (const projection of schemaClass2.constructorProjections ?? []) {
48065
+ if (claimed.has(projection.parameterName)) continue;
48066
+ claimed.add(projection.parameterName);
48067
+ resolved.push(projection);
48068
+ }
48069
+ }
48070
+ return resolved;
48071
+ }
48072
+ function inheritedProjectionSchemaKey(classes, classId, memberId) {
48073
+ for (const schemaClass2 of classInheritanceChain(classes, classId)) {
48074
+ for (const [schemaKey, declared] of Object.entries(schemaClass2.schema)) {
48075
+ if (declared === memberId) return schemaKey;
48076
+ }
48077
+ }
48078
+ return null;
48079
+ }
48080
+ function resolveConstructorProjectionArguments(context, schemaClass2, expression, ownerValueId, environment, source) {
48081
+ const projections = inheritedConstructorProjections(
48082
+ context.classes,
48083
+ schemaClass2.id
48084
+ );
46711
48085
  if (projections.length === 0) {
46712
48086
  if (expression.args.length > 0) {
46713
48087
  throw new Error(
@@ -46751,44 +48125,32 @@ function resolveConstructorProjectionArguments(context, schemaClass2, expression
46751
48125
  `Class ${schemaClass2.name} constructor argument ${parameterName} must be a value-row id string.`
46752
48126
  );
46753
48127
  }
46754
- const schemaKey = Object.entries(schemaClass2.schema).find(
46755
- ([, memberId]) => memberId === projection.memberId
46756
- )?.[0];
48128
+ const schemaKey = inheritedProjectionSchemaKey(
48129
+ context.classes,
48130
+ schemaClass2.id,
48131
+ projection.memberId
48132
+ );
46757
48133
  const projectedMember = context.members.get(projection.memberId);
46758
- if (schemaKey === void 0 || projectedMember?.kind !== "lookup") {
48134
+ if (schemaKey === null || projectedMember?.kind !== "lookup") {
46759
48135
  throw new Error(
46760
48136
  `Class ${schemaClass2.name} constructor projection ${parameterName} does not resolve to a Lookup field.`
46761
48137
  );
46762
48138
  }
46763
- const target = referenceTargetById(context, argument2.value, "");
46764
- if (target === null || target.kind !== "value") {
46765
- const owner = context.initAuthoredRowIds.get(argument2.value);
46766
- if (owner !== void 0) {
46767
- throw new Error(
46768
- `Class ${schemaClass2.name} constructor argument ${parameterName} targets value ${argument2.value}, which is authored inside the computed initializer of ${owner} and therefore stores no row. Point it at a literal default's row instead.`
46769
- );
46770
- }
46771
- throw new Error(
46772
- `Class ${schemaClass2.name} constructor argument ${parameterName} targets a missing value ${argument2.value}.`
46773
- );
46774
- }
46775
- validateReferenceContract(
46776
- context,
46777
- projectedMember,
46778
- { ...target, genericTypeName: null },
46779
- ownerValueId
46780
- );
46781
- validateAnimationProjectionBinding(
46782
- context,
46783
- schemaClass2,
46784
- environment,
46785
- target.id
46786
- );
48139
+ context.referenceObligations.push({
48140
+ kind: "constructorProjection",
48141
+ site: referenceSite(source, argument2),
48142
+ schemaClassId: schemaClass2.id,
48143
+ parameterName,
48144
+ projectedMemberId: projectedMember.id,
48145
+ targetId: argument2.value,
48146
+ ownerValueId,
48147
+ environment
48148
+ });
46787
48149
  resolved.push({
46788
48150
  parameterName,
46789
48151
  projectedMember,
46790
48152
  schemaKey,
46791
- target: { id: target.id, kind: "value" }
48153
+ target: { id: argument2.value, kind: "value" }
46792
48154
  });
46793
48155
  }
46794
48156
  return resolved;
@@ -46887,7 +48249,7 @@ function genericBindingClassId(context, bindingMemberId) {
46887
48249
  return rawBinding?.kind === 7 && typeof rawBinding.classId === "string" ? rawBinding.classId : null;
46888
48250
  }
46889
48251
  function validateConstructedGenericArguments(context, schemaClass2, expression, environment, path) {
46890
- const actual = expression.typeArguments ?? [];
48252
+ const actual = expression.className === "Partial" ? [] : expression.typeArguments ?? [];
46891
48253
  const expected = schemaClass2.genericParameters;
46892
48254
  if (expected.length === 0) {
46893
48255
  if (actual.length > 0) {
@@ -46928,14 +48290,14 @@ function lowerRawMemberTypeName(context, memberId) {
46928
48290
  if (member.kind === 7 && typeof member.classId === "string") {
46929
48291
  return context.classes.get(member.classId)?.name ?? null;
46930
48292
  }
46931
- return typeof member.kind === "number" ? MEMBER_KIND_SOURCE_NAMES.get(member.kind) ?? null : null;
48293
+ return typeof member.kind === "number" ? fixedSourceTypeNameForKindNumber(member.kind) : null;
46932
48294
  }
46933
48295
  function lowerSchemaMemberTypeName(context, member) {
46934
48296
  if (member.kind === "class") {
46935
48297
  return context.classes.get(member.classId)?.name ?? null;
46936
48298
  }
46937
48299
  if (member.kind === "generic") return null;
46938
- return member.kind;
48300
+ return fixedSourceTypeNameForKind(member.kind) ?? member.kind;
46939
48301
  }
46940
48302
  function constructedClass(context, member, expression, currentClassId, path) {
46941
48303
  if (member.partial === true) {
@@ -46963,13 +48325,22 @@ function constructedClass(context, member, expression, currentClassId, path) {
46963
48325
  );
46964
48326
  }
46965
48327
  if (expression.className !== null) {
46966
- return requiredClassByName(context, expression.className);
48328
+ const named = requiredClassByName(context, expression.className);
48329
+ assertConstructedClassAssignable(context, named, member, path);
48330
+ return named;
46967
48331
  }
46968
48332
  const target = context.classes.get(currentClassId);
46969
48333
  if (target === void 0)
46970
48334
  throw new Error(`Unknown value class ${currentClassId}.`);
46971
48335
  return target;
46972
48336
  }
48337
+ function assertConstructedClassAssignable(context, named, member, path) {
48338
+ if (classAssignableToClass(context, named.id, member.classId)) return;
48339
+ const declaredName = context.classes.get(member.classId)?.name ?? member.classId;
48340
+ throw new Error(
48341
+ `Class value ${path} constructs ${named.name}, which does not descend from the declared class ${declaredName}.`
48342
+ );
48343
+ }
46973
48344
  function recursivePartialMember(parent, child) {
46974
48345
  if (parent.partial !== true) return child;
46975
48346
  if (child.kind === "class" || child.kind === "generic") {
@@ -47018,7 +48389,13 @@ function lowerListValue(context, member, expression, base, source, inheritedEnvi
47018
48389
  continue;
47019
48390
  }
47020
48391
  const itemId = annotatedItemId;
47021
- if (isPendingId(itemId) && context.state[`value:${itemId}`] === void 0) {
48392
+ if (context.state[`value:${itemId}`] === void 0) {
48393
+ if (!isPendingId(itemId) && !isUuidV4Id(itemId)) {
48394
+ context.loweringFailures.push({
48395
+ message: `List ${member.name} entry @id("${itemId}") names no existing row, and a new row's durable identity must be a UUID v4. Fix the id if it meant an existing row, or drop the @id to mint a fresh identity.`,
48396
+ site: referenceSite(source, element)
48397
+ });
48398
+ }
47022
48399
  ids.push(
47023
48400
  lowerPendingListItem(
47024
48401
  context,
@@ -47173,19 +48550,37 @@ function lowerEnum2(context, member, expression) {
47173
48550
  return id2;
47174
48551
  });
47175
48552
  }
47176
- function lowerReferences(context, member, expression, ownerValueId) {
48553
+ function lowerReferences(context, member, expression, ownerValueId, source) {
47177
48554
  const elements = member.multiselect ? expression.kind === "litList" ? expression.elements : null : [expression];
47178
48555
  if (elements === null) {
47179
48556
  throw new Error(`Reference member ${member.name} requires [...].`);
47180
48557
  }
47181
- const ids = elements.map((entry) => {
47182
- const target = resolveReferenceTarget(context, entry);
48558
+ const ids = [];
48559
+ for (const [index, entry] of elements.entries()) {
48560
+ const target = referenceTargetSpelling(context, entry);
47183
48561
  if (target === null) {
47184
48562
  throw new Error(`Reference member ${member.name} has an invalid target.`);
47185
48563
  }
47186
- validateReferenceContract(context, member, target, ownerValueId);
47187
- return target.id;
47188
- });
48564
+ if (target.spelling === "key") {
48565
+ ids.push(`${PENDING_ID_PREFIX}reference-key:${source.label}:${index}`);
48566
+ context.referenceObligations.push({
48567
+ kind: "reference",
48568
+ site: referenceSite(source, entry),
48569
+ memberId: member.id,
48570
+ ownerValueId,
48571
+ target: { ...target, patch: { ids, index } }
48572
+ });
48573
+ continue;
48574
+ }
48575
+ context.referenceObligations.push({
48576
+ kind: "reference",
48577
+ site: referenceSite(source, entry),
48578
+ memberId: member.id,
48579
+ ownerValueId,
48580
+ target
48581
+ });
48582
+ ids.push(target.spelling === "id" ? target.id : target.resolved.id);
48583
+ }
47189
48584
  if (new Set(ids).size !== ids.length) {
47190
48585
  throw new Error(
47191
48586
  `Reference member ${member.name} cannot contain duplicate targets.`
@@ -47193,6 +48588,27 @@ function lowerReferences(context, member, expression, ownerValueId) {
47193
48588
  }
47194
48589
  return ids;
47195
48590
  }
48591
+ function referenceSite(source, expression) {
48592
+ return {
48593
+ uri: source.source.uri,
48594
+ declarationStart: source.source.range.start,
48595
+ expression: {
48596
+ initializer: source.initializer,
48597
+ pos: expressionAnchorPos(expression)
48598
+ },
48599
+ label: source.label
48600
+ };
48601
+ }
48602
+ function expressionAnchorPos(expression) {
48603
+ if (expression.kind === "annotated") {
48604
+ return expressionAnchorPos(expression.expression);
48605
+ }
48606
+ if (expression.kind === "call") return expressionAnchorPos(expression.callee);
48607
+ if (expression.kind === "member") {
48608
+ return expressionAnchorPos(expression.receiver);
48609
+ }
48610
+ return { line: expression.pos.line, column: expression.pos.column };
48611
+ }
47196
48612
  function lowerFileValue(context, member, expression) {
47197
48613
  if (member.kind === "sprite") {
47198
48614
  if (expression.kind !== "call" || expression.callee.kind !== "member" || expression.callee.name !== "Slice") {
@@ -47281,24 +48697,53 @@ function sourceValueSymbol(context, expression) {
47281
48697
  const path = memberPath(unwrapped);
47282
48698
  return path === null ? null : context.staticValueIdsBySymbol.get(path) ?? null;
47283
48699
  }
47284
- function resolveReferenceTarget(context, expression) {
48700
+ function referenceTargetSpelling(context, expression) {
47285
48701
  if (expression.kind !== "call" || expression.callee.kind !== "ident" || expression.callee.name !== "Reference") {
47286
48702
  return null;
47287
48703
  }
47288
48704
  if ((expression.typeArguments?.length ?? 0) > 1) return null;
47289
48705
  const genericTypeName = expression.typeArguments?.[0] ? astTypeName(expression.typeArguments[0]) : null;
48706
+ const keyNamed = expression.argumentNames?.findIndex((name) => name === "key") ?? -1;
48707
+ if (keyNamed >= 0) {
48708
+ const keyArgument = expression.args[keyNamed];
48709
+ if (keyArgument?.kind !== "litString" || !genericTypeName) return null;
48710
+ const indexNamed = expression.argumentNames?.findIndex((name) => name === "index") ?? -1;
48711
+ if (expression.args.length !== (indexNamed >= 0 ? 2 : 1)) return null;
48712
+ if (indexNamed < 0) {
48713
+ return {
48714
+ spelling: "key",
48715
+ key: keyArgument.value,
48716
+ indexSchemaKey: null,
48717
+ typeName: genericTypeName
48718
+ };
48719
+ }
48720
+ const indexArgument = expression.args[indexNamed];
48721
+ if (indexArgument?.kind !== "ident") return null;
48722
+ return {
48723
+ spelling: "key",
48724
+ key: keyArgument.value,
48725
+ indexSchemaKey: indexArgument.name,
48726
+ typeName: genericTypeName
48727
+ };
48728
+ }
47290
48729
  const named = expression.argumentNames?.findIndex((name) => name === "id") ?? -1;
47291
48730
  const argument2 = expression.args[named >= 0 ? named : 0];
47292
48731
  if (expression.args.length !== 1 || !argument2) return null;
47293
48732
  if (named >= 0) {
47294
48733
  if (argument2.kind !== "litString" || !genericTypeName) return null;
47295
- return referenceTargetById(context, argument2.value, genericTypeName);
48734
+ return { spelling: "id", id: argument2.value, typeName: genericTypeName };
47296
48735
  }
47297
- const path = argument2 ? memberPath(argument2) : null;
48736
+ const resolved = referenceTargetBySymbol(context, argument2, genericTypeName);
48737
+ const path = memberPath(argument2);
48738
+ if (resolved === null || path === null) return null;
48739
+ return { spelling: "symbol", path, resolved };
48740
+ }
48741
+ function referenceTargetBySymbol(context, argument2, genericTypeName) {
48742
+ const path = memberPath(argument2);
47298
48743
  if (path === null) return null;
47299
48744
  const valueId = context.staticValueIdsBySymbol.get(path);
47300
48745
  if (valueId) {
47301
- const value = stateData(context, "value", valueId);
48746
+ const value = valueData(context, valueId);
47302
48747
  return {
47303
48748
  id: valueId,
47304
48749
  kind: "value",
@@ -47365,7 +48810,7 @@ function validateReferenceContract(context, member, target, ownerValueId) {
47365
48810
  const actualClassId = targetData && typeof targetData.classId === "string" ? targetData.classId : null;
47366
48811
  if (expectedClassId && actualClassId && !classAssignableToClass(context, actualClassId, expectedClassId)) {
47367
48812
  throw new Error(
47368
- `Lookup member ${member.name} target ${target.id} has an incompatible value type.`
48813
+ `Lookup member ${member.name} target ${target.id} is ${context.classes.get(actualClassId)?.name ?? actualClassId}, but collection ${collectionMemberName(context, member)} holds ${context.classes.get(expectedClassId)?.name ?? expectedClassId}.`
47369
48814
  );
47370
48815
  }
47371
48816
  if (target.genericTypeName && expectedTypeName && !referenceTypeNameAssignable(
@@ -47424,32 +48869,45 @@ function validateLookupMembership(context, member, targetId, ownerValueId) {
47424
48869
  `Lookup member ${member.name} references a missing collection member.`
47425
48870
  );
47426
48871
  }
47427
- const collectionValueIds = resolveLookupCollectionValueIds(
48872
+ const resolution = resolveLookupCollectionValueIds(
47428
48873
  context,
47429
48874
  member,
47430
48875
  collection,
47431
48876
  ownerValueId
47432
48877
  );
47433
- if (collectionValueIds.length === 0) {
48878
+ if (resolution.valueIds.length === 0) {
47434
48879
  throw new Error(
47435
- `Lookup member ${member.name} has no resolvable collection value for membership validation.`
48880
+ `Lookup member ${member.name} target ${targetId} cannot be checked: collection ${collectionMemberName(context, member)} has no value row in the project after this push. Declare @settings(collectionValue: ...) to name the list this member selects from.`
47436
48881
  );
47437
48882
  }
47438
- const present = collectionValueIds.some((collectionValueId) => {
48883
+ const present = resolution.valueIds.some((collectionValueId) => {
47439
48884
  const collectionValue = valueData(context, collectionValueId);
47440
48885
  if (!collectionValue) return false;
47441
48886
  const body = collectionValue.value;
47442
48887
  return collection.kind === "list" && collection.listKind === "unordered" ? valueData(context, targetId)?.containerId === collectionValueId : Array.isArray(body) ? body.includes(targetId) : isObjectRecord2(body) ? Object.values(body).includes(targetId) : false;
47443
48888
  });
47444
- if (!present) {
48889
+ if (present) return;
48890
+ if (resolution.origin === "nameScan") {
47445
48891
  throw new Error(
47446
- `Lookup member ${member.name} target ${targetId} is not a member of collection ${collectionValueIds.join(", ")}.`
48892
+ `Lookup member ${member.name} target ${targetId} cannot be checked: collection ${collectionMemberName(context, member)} could not be resolved to a value row, and the rows found by searching for the name ${JSON.stringify(collection.name)} (${resolution.valueIds.join(", ")}) are not it. Declare @settings(collectionValue: ...) to name the list this member selects from.`
47447
48893
  );
47448
48894
  }
48895
+ throw new Error(
48896
+ `Lookup member ${member.name} target ${targetId} is not a member of collection ${collectionMemberName(context, member)} (${resolution.valueIds.join(", ")}).`
48897
+ );
48898
+ }
48899
+ function collectionMemberName(context, member) {
48900
+ const collection = context.members.get(member.collectionMemberId);
48901
+ if (collection === void 0) return member.collectionMemberId;
48902
+ const owner = collection.owner;
48903
+ if (owner.kind !== "classMember") return collection.name;
48904
+ const ownerClass = context.classes.get(owner.classId);
48905
+ if (ownerClass === void 0) return collection.name;
48906
+ return `${ownerClass.name}.${collection.name}`;
47449
48907
  }
47450
48908
  function resolveLookupCollectionValueIds(context, member, collection, ownerValueId) {
47451
48909
  if (typeof member.collectionValueId === "string") {
47452
- return [member.collectionValueId];
48910
+ return { valueIds: [member.collectionValueId], origin: "declared" };
47453
48911
  }
47454
48912
  if (ownerValueId !== null) {
47455
48913
  const container = context.valuePlacements.containerBodyByPlacement.get(
@@ -47457,9 +48915,17 @@ function resolveLookupCollectionValueIds(context, member, collection, ownerValue
47457
48915
  );
47458
48916
  const siblingValueId = container?.[collection.name];
47459
48917
  if (typeof siblingValueId === "string") {
47460
- return [siblingValueId];
48918
+ return { valueIds: [siblingValueId], origin: "sibling" };
47461
48919
  }
47462
48920
  }
48921
+ const declaredRows = collectionRowsOfDeclaredMember(
48922
+ context,
48923
+ member.collectionMemberId,
48924
+ collection.name
48925
+ );
48926
+ if (declaredRows.size > 0) {
48927
+ return { valueIds: [...declaredRows], origin: "collectionMember" };
48928
+ }
47463
48929
  const candidates = /* @__PURE__ */ new Set();
47464
48930
  const collectionState = context.state[`member:${member.collectionMemberId}`];
47465
48931
  const collectionMemberData = isObjectRecord2(collectionState?.data) ? collectionState.data : {};
@@ -47471,21 +48937,92 @@ function resolveLookupCollectionValueIds(context, member, collection, ownerValue
47471
48937
  ) ?? []) {
47472
48938
  candidates.add(placedValueId);
47473
48939
  }
47474
- return [...candidates];
48940
+ return { valueIds: [...candidates], origin: "nameScan" };
48941
+ }
48942
+ function collectionRowsOfDeclaredMember(context, collectionMemberId, schemaKey) {
48943
+ const declaredMemberIds = declaredCollectionMemberIds(
48944
+ context,
48945
+ collectionMemberId
48946
+ );
48947
+ const rows = /* @__PURE__ */ new Set();
48948
+ for (const memberId of declaredMemberIds) {
48949
+ for (const valueId of context.valuePlacements.valueIdsByMemberId.get(
48950
+ memberId
48951
+ ) ?? []) {
48952
+ rows.add(valueId);
48953
+ }
48954
+ const bound = context.valuePlacements.valueIdByMemberId.get(memberId);
48955
+ if (bound !== void 0) rows.add(bound);
48956
+ }
48957
+ for (const placement of context.valuePlacements.placementsBySchemaKey.get(
48958
+ schemaKey
48959
+ ) ?? []) {
48960
+ if (placement.containerClassId === null) continue;
48961
+ const placedMemberId = classSchemaMemberId(
48962
+ context,
48963
+ placement.containerClassId,
48964
+ placement.schemaKey
48965
+ );
48966
+ if (placedMemberId === null) continue;
48967
+ if (!declaredMemberIds.has(placedMemberId)) continue;
48968
+ rows.add(placement.valueId);
48969
+ }
48970
+ return rows;
48971
+ }
48972
+ function valueOwningMemberIds(context, valueId) {
48973
+ const owners = /* @__PURE__ */ new Set();
48974
+ const pending = context.pendingValues.get(valueId);
48975
+ if (pending !== void 0) owners.add(pending.memberId);
48976
+ for (const memberId of context.valuePlacements.memberIdsByBoundValueId.get(
48977
+ valueId
48978
+ ) ?? []) {
48979
+ owners.add(memberId);
48980
+ }
48981
+ for (const placement of context.valuePlacements.placementsByValueId.get(
48982
+ valueId
48983
+ ) ?? []) {
48984
+ if (placement.containerClassId === null) continue;
48985
+ const memberId = classSchemaMemberId(
48986
+ context,
48987
+ placement.containerClassId,
48988
+ placement.schemaKey
48989
+ );
48990
+ if (memberId !== null) owners.add(memberId);
48991
+ }
48992
+ return owners;
47475
48993
  }
47476
- function indexValuePlacements(state) {
48994
+ function classSchemaMemberId(context, classId, schemaKey) {
48995
+ let current = classId;
48996
+ const visited = /* @__PURE__ */ new Set();
48997
+ while (current !== null && !visited.has(current)) {
48998
+ visited.add(current);
48999
+ const schemaClass2 = context.classes.get(current);
49000
+ if (schemaClass2 === void 0) return null;
49001
+ const memberId = schemaClass2.schema[schemaKey];
49002
+ if (typeof memberId === "string") return memberId;
49003
+ current = schemaClass2.extendsClassId;
49004
+ }
49005
+ return null;
49006
+ }
49007
+ function indexValuePlacements(state, options = {}) {
47477
49008
  const containerBodyByPlacement = /* @__PURE__ */ new Map();
47478
49009
  const mutablePlacedValueIds = /* @__PURE__ */ new Map();
47479
49010
  const valueIdsByContainerId = /* @__PURE__ */ new Map();
47480
- for (const record3 of Object.values(state)) {
47481
- if (record3.recordKind !== "value" || !isObjectRecord2(record3.data)) continue;
47482
- const containerId = record3.data.containerId;
47483
- if (typeof containerId === "string") {
47484
- const contained = valueIdsByContainerId.get(containerId) ?? /* @__PURE__ */ new Set();
47485
- contained.add(record3.recordId);
47486
- valueIdsByContainerId.set(containerId, contained);
47487
- }
47488
- const body = isObjectRecord2(record3.data.value) ? record3.data.value : null;
49011
+ const mutableValueIdsByMemberId = /* @__PURE__ */ new Map();
49012
+ const placementsBySchemaKey = /* @__PURE__ */ new Map();
49013
+ const placementsByValueId = /* @__PURE__ */ new Map();
49014
+ for (const row of unionValueRows(state, options)) {
49015
+ if (row.containerId !== null) {
49016
+ const contained = valueIdsByContainerId.get(row.containerId) ?? /* @__PURE__ */ new Set();
49017
+ contained.add(row.id);
49018
+ valueIdsByContainerId.set(row.containerId, contained);
49019
+ }
49020
+ if (row.memberId !== null) {
49021
+ const owned = mutableValueIdsByMemberId.get(row.memberId) ?? [];
49022
+ owned.push(row.id);
49023
+ mutableValueIdsByMemberId.set(row.memberId, owned);
49024
+ }
49025
+ const body = isObjectRecord2(row.body) ? row.body : null;
47489
49026
  if (body === null) continue;
47490
49027
  for (const [memberName, childValueId] of Object.entries(body)) {
47491
49028
  if (typeof childValueId !== "string") continue;
@@ -47496,16 +49033,73 @@ function indexValuePlacements(state) {
47496
49033
  const placed = mutablePlacedValueIds.get(memberName) ?? /* @__PURE__ */ new Set();
47497
49034
  placed.add(childValueId);
47498
49035
  mutablePlacedValueIds.set(memberName, placed);
49036
+ const placement = {
49037
+ valueId: childValueId,
49038
+ schemaKey: memberName,
49039
+ containerValueId: row.id,
49040
+ containerClassId: row.classId
49041
+ };
49042
+ const byKey = placementsBySchemaKey.get(memberName) ?? [];
49043
+ byKey.push(placement);
49044
+ placementsBySchemaKey.set(memberName, byKey);
49045
+ const byValue = placementsByValueId.get(childValueId) ?? [];
49046
+ byValue.push(placement);
49047
+ placementsByValueId.set(childValueId, byValue);
47499
49048
  }
47500
49049
  }
49050
+ const valueIdByMemberId = /* @__PURE__ */ new Map();
49051
+ const memberIdsByBoundValueId = /* @__PURE__ */ new Map();
49052
+ for (const record3 of Object.values(state)) {
49053
+ if (record3.recordKind !== "member" || !isObjectRecord2(record3.data))
49054
+ continue;
49055
+ const valueId = record3.data.valueId;
49056
+ if (typeof valueId !== "string") continue;
49057
+ if (options.deletedValueIds?.has(valueId) === true) continue;
49058
+ valueIdByMemberId.set(record3.recordId, valueId);
49059
+ const bound = memberIdsByBoundValueId.get(valueId) ?? [];
49060
+ bound.push(record3.recordId);
49061
+ memberIdsByBoundValueId.set(valueId, bound);
49062
+ }
47501
49063
  return {
47502
49064
  containerBodyByPlacement,
47503
49065
  placedValueIdsByMemberName: new Map(
47504
49066
  [...mutablePlacedValueIds].map(([name, ids]) => [name, [...ids]])
47505
49067
  ),
47506
- valueIdsByContainerId
49068
+ valueIdsByContainerId,
49069
+ valueIdsByMemberId: mutableValueIdsByMemberId,
49070
+ placementsBySchemaKey,
49071
+ placementsByValueId,
49072
+ valueIdByMemberId,
49073
+ memberIdsByBoundValueId
47507
49074
  };
47508
49075
  }
49076
+ function unionValueRows(state, options) {
49077
+ const rows = [];
49078
+ const pendingValues = options.pendingValues;
49079
+ for (const record3 of Object.values(state)) {
49080
+ if (record3.recordKind !== "value" || !isObjectRecord2(record3.data)) continue;
49081
+ if (options.deletedValueIds?.has(record3.recordId) === true) continue;
49082
+ if (pendingValues?.has(record3.recordId) === true) continue;
49083
+ rows.push({
49084
+ id: record3.recordId,
49085
+ containerId: typeof record3.data.containerId === "string" ? record3.data.containerId : null,
49086
+ body: record3.data.value,
49087
+ memberId: typeof record3.data.memberId === "string" ? record3.data.memberId : null,
49088
+ classId: typeof record3.data.classId === "string" ? record3.data.classId : null
49089
+ });
49090
+ }
49091
+ for (const row of pendingValues?.values() ?? []) {
49092
+ if (options.deletedValueIds?.has(row.id) === true) continue;
49093
+ rows.push({
49094
+ id: row.id,
49095
+ containerId: row.containerId ?? null,
49096
+ body: row.value,
49097
+ memberId: row.memberId,
49098
+ classId: row.classId ?? null
49099
+ });
49100
+ }
49101
+ return rows;
49102
+ }
47509
49103
  function validateDialogueEligibility(context, member, dialogueId) {
47510
49104
  const dialogue = stateData(context, "dialogue", dialogueId);
47511
49105
  if (!dialogue) {
@@ -47960,7 +49554,6 @@ function classValue(context, member, value, visited, targetTyped, outerEnvironme
47960
49554
  const projection = constructorProjectionSource(
47961
49555
  context,
47962
49556
  classId,
47963
- schema,
47964
49557
  value.value,
47965
49558
  visited
47966
49559
  );
@@ -48061,23 +49654,28 @@ function manifestMemberTypeName(context, member) {
48061
49654
  if (member.kind === "scriptFunction" || member.kind === "function") {
48062
49655
  return "FunctionRef";
48063
49656
  }
48064
- return member.kind;
49657
+ return fixedSourceTypeNameForKind(member.kind) ?? member.kind;
48065
49658
  }
48066
- function constructorProjectionSource(context, classId, schema, body, visited) {
48067
- const projections = context.manifestClasses.get(classId)?.constructorProjections ?? [];
49659
+ function constructorProjectionSource(context, classId, body, visited) {
49660
+ const projections = inheritedConstructorProjections(
49661
+ context.manifestClasses,
49662
+ classId
49663
+ );
48068
49664
  const argumentsValue = [];
48069
49665
  const memberIds = /* @__PURE__ */ new Set();
48070
49666
  const targetValueIds = [];
48071
49667
  for (const projection of projections) {
48072
- const schemaKey = Object.entries(schema).find(
48073
- ([, memberId]) => memberId === projection.memberId
48074
- )?.[0];
48075
- const childValueId = schemaKey ? body[schemaKey] : void 0;
49668
+ const schemaKey = inheritedProjectionSchemaKey(
49669
+ context.manifestClasses,
49670
+ classId,
49671
+ projection.memberId
49672
+ );
49673
+ const childValueId = schemaKey === null ? void 0 : body[schemaKey];
48076
49674
  const childValue = typeof childValueId === "string" ? context.values.get(childValueId) : void 0;
48077
49675
  const targetIds = Array.isArray(childValue?.value) ? childValue.value.filter(
48078
49676
  (entry) => typeof entry === "string"
48079
49677
  ) : [];
48080
- if (schemaKey === void 0 || typeof childValueId !== "string" || targetIds.length !== 1) {
49678
+ if (schemaKey === null || typeof childValueId !== "string" || targetIds.length !== 1) {
48081
49679
  throw new Error(
48082
49680
  `Class ${context.manifestClasses.get(classId)?.name ?? classId} constructor projection ${projection.parameterName} requires exactly one referenced value.`
48083
49681
  );
@@ -48257,7 +49855,7 @@ function lowerInstanceGenericEnvironment(context, classId, member, outerEnvironm
48257
49855
  });
48258
49856
  }
48259
49857
  }
48260
- const environment = /* @__PURE__ */ new Map();
49858
+ const environment = new Map(classGenericEnvironment(context, classId));
48261
49859
  for (const genericParamId of bindings.keys()) {
48262
49860
  const memberId = terminalGenericBindingMemberId(genericParamId, bindings);
48263
49861
  if (memberId !== void 0) environment.set(genericParamId, memberId);
@@ -48364,10 +49962,62 @@ function referenceValue(context, member, body, dialogue) {
48364
49962
  const symbol = context.symbolsByValueId.get(id2);
48365
49963
  if (symbol !== void 0) return `Reference(${symbol})`;
48366
49964
  const type = dialogue ? "Dialogue" : memberReferenceType(context, member);
49965
+ if (!dialogue) {
49966
+ const key = uniqueIndexKeyForTarget(context, member, id2);
49967
+ if (key !== null) return `Reference<${type}>(key: ${quote4(key)})`;
49968
+ }
48367
49969
  return `Reference<${type}>(id: ${quote4(id2)})`;
48368
49970
  });
48369
49971
  return member.multiselect === true ? `[${values.join(", ")}]` : values[0] ?? "null";
48370
49972
  }
49973
+ function uniqueIndexKeyForTarget(context, member, targetId) {
49974
+ const manifestMember = context.manifestMembers.get(stringField2(member, "id"));
49975
+ if (manifestMember?.kind !== "lookup") return null;
49976
+ const collection = context.manifestMembers.get(
49977
+ manifestMember.collectionMemberId
49978
+ );
49979
+ if (collection?.kind !== "list") return null;
49980
+ const unique = (collection.indexes ?? []).filter((index) => index.unique);
49981
+ const only = unique.length === 1 ? unique[0] : void 0;
49982
+ if (only === void 0) return null;
49983
+ const key = indexedFieldValue(context, targetId, only.schemaKey);
49984
+ if (key === null || key.length === 0) return null;
49985
+ const siblings = collectionSiblingIds(context, collection, targetId);
49986
+ let holders = 0;
49987
+ for (const sibling of siblings) {
49988
+ if (indexedFieldValue(context, sibling, only.schemaKey) === key) {
49989
+ holders += 1;
49990
+ }
49991
+ }
49992
+ return holders === 1 ? key : null;
49993
+ }
49994
+ function indexedFieldValue(context, rowId, schemaKey) {
49995
+ const row = context.values.get(rowId);
49996
+ const body = row?.value;
49997
+ if (!isObjectRecord2(body)) return null;
49998
+ const fieldRowId = body[schemaKey];
49999
+ if (typeof fieldRowId !== "string") return null;
50000
+ const fieldValue = context.values.get(fieldRowId)?.value;
50001
+ return typeof fieldValue === "string" ? fieldValue : null;
50002
+ }
50003
+ function collectionSiblingIds(context, collection, targetId) {
50004
+ if (collection.listKind === "unordered") {
50005
+ const containerId = context.values.get(targetId)?.containerId;
50006
+ if (typeof containerId !== "string") return [targetId];
50007
+ const siblings = [];
50008
+ for (const [id2, row] of context.values) {
50009
+ if (row.containerId === containerId) siblings.push(id2);
50010
+ }
50011
+ return siblings;
50012
+ }
50013
+ for (const row of context.values.values()) {
50014
+ const body = row.value;
50015
+ if (!Array.isArray(body)) continue;
50016
+ if (!body.includes(targetId)) continue;
50017
+ return body.filter((entry) => typeof entry === "string");
50018
+ }
50019
+ return [targetId];
50020
+ }
48371
50021
  function functionReferenceValue(context, body) {
48372
50022
  if (!isObjectRecord2(body) || typeof body.functionMemberId !== "string") {
48373
50023
  throw new Error("FunctionRef value is missing functionMemberId.");
@@ -48571,7 +50221,7 @@ function numberOr2(value, fallback) {
48571
50221
  function quote4(value) {
48572
50222
  return quoteNeoString(value);
48573
50223
  }
48574
- var INFERRED_GENERIC_CLASS_PREFIX, MEMBER_KIND_DICTIONARY, MEMBER_KIND_LIST, MEMBER_KIND_CLASS, MEMBER_KIND_SOURCE_NAMES;
50224
+ var INFERRED_GENERIC_CLASS_PREFIX, MEMBER_KIND_DICTIONARY, MEMBER_KIND_LIST, MEMBER_KIND_CLASS;
48575
50225
  var init_value_sources = __esm({
48576
50226
  "src/project-source/value-sources.ts"() {
48577
50227
  "use strict";
@@ -48586,18 +50236,11 @@ var init_value_sources = __esm({
48586
50236
  init_member_value_id();
48587
50237
  init_members();
48588
50238
  init_structured_leaf_source();
50239
+ init_member_kind_type_names();
48589
50240
  INFERRED_GENERIC_CLASS_PREFIX = "__inferred_class__:";
48590
50241
  MEMBER_KIND_DICTIONARY = 5;
48591
50242
  MEMBER_KIND_LIST = 6;
48592
50243
  MEMBER_KIND_CLASS = 7;
48593
- MEMBER_KIND_SOURCE_NAMES = /* @__PURE__ */ new Map([
48594
- [0, "null"],
48595
- [1, "bool"],
48596
- [2, "int"],
48597
- [3, "string"],
48598
- [4, "float"],
48599
- [20, "decimal"]
48600
- ]);
48601
50244
  }
48602
50245
  });
48603
50246
 
@@ -48662,7 +50305,7 @@ ${declarations.join("\n\n")}
48662
50305
  ])
48663
50306
  };
48664
50307
  }
48665
- function lowerProjectRootSourceV4(state, analysis, manifest) {
50308
+ function lowerProjectRootSourceV4(state, analysis, manifest, options = {}) {
48666
50309
  const validated = validateProjectRootEnvelopeV4(state, analysis);
48667
50310
  if (validated === null) {
48668
50311
  return { records: [], memberValueIds: /* @__PURE__ */ new Map(), seeds: /* @__PURE__ */ new Map() };
@@ -48675,7 +50318,10 @@ function lowerProjectRootSourceV4(state, analysis, manifest) {
48675
50318
  source: global.source,
48676
50319
  label: `root.${expected.name}`
48677
50320
  }));
48678
- return lowerStoredValueBindingsV4(state, manifest, bindings, { analysis });
50321
+ return lowerStoredValueBindingsV4(state, manifest, bindings, {
50322
+ analysis,
50323
+ registry: options.registry
50324
+ });
48679
50325
  }
48680
50326
  function validateProjectRootEnvelopeV4(state, analysis) {
48681
50327
  const project = Object.values(state).find(
@@ -48747,7 +50393,120 @@ function requiredObject(value, label) {
48747
50393
  function quote5(value) {
48748
50394
  return quoteNeoString(value);
48749
50395
  }
48750
- var ROOT_PROJECT_FIELDS;
50396
+ function resolveRootPathCollectionValuesV4(args) {
50397
+ const resolved = /* @__PURE__ */ new Map();
50398
+ for (const member of args.manifest.members) {
50399
+ if (member.kind !== "lookup") continue;
50400
+ const spelled = member.collectionValueId;
50401
+ if (typeof spelled !== "string") continue;
50402
+ if (!spelled.startsWith(ROOT_PATH_PENDING_PREFIX)) continue;
50403
+ const path = spelled.slice(ROOT_PATH_PENDING_PREFIX.length);
50404
+ const site = args.siteForMember(member.id);
50405
+ const fail = (message) => {
50406
+ args.registry.loweringFailures.push({
50407
+ message,
50408
+ site: {
50409
+ uri: site?.uri ?? "<schema>",
50410
+ declarationStart: site?.start ?? { line: 1, character: 0 },
50411
+ expression: null,
50412
+ label: member.name
50413
+ }
50414
+ });
50415
+ };
50416
+ const target = walkRootValuePath(path, args.state, args.registry, fail);
50417
+ if (target !== null) resolved.set(member.id, target);
50418
+ }
50419
+ return resolved;
50420
+ }
50421
+ function walkRootValuePath(path, state, registry, fail) {
50422
+ const segments = path.split(".");
50423
+ const slotName = segments[1];
50424
+ if (segments[0] !== "root" || slotName === void 0) {
50425
+ fail(
50426
+ `collectionValue path ${path} must start at a root slot (root.Assets, root.Save, or root.Session).`
50427
+ );
50428
+ return null;
50429
+ }
50430
+ const slotIndex = PROJECT_ROOT_SOURCE_SLOTS.findIndex(
50431
+ (slot) => slot.name === slotName
50432
+ );
50433
+ const project = Object.values(state).find(
50434
+ (record3) => record3.recordKind === "project"
50435
+ );
50436
+ if (slotIndex < 0 || project === void 0 || !isObjectRecord2(project.data)) {
50437
+ fail(
50438
+ `collectionValue path ${path} names root slot ${slotName ?? "<none>"}, which is not Assets, Save, or Session.`
50439
+ );
50440
+ return null;
50441
+ }
50442
+ const memberId = rootMemberIds(project.data)[slotIndex];
50443
+ const slotMember = memberId === void 0 ? void 0 : state[`member:${memberId}`];
50444
+ const slotValueId = isObjectRecord2(slotMember?.data) ? slotMember.data.valueId : void 0;
50445
+ if (typeof slotValueId !== "string") {
50446
+ fail(
50447
+ `collectionValue path ${path} cannot start: root slot ${slotName} has no stable value row.`
50448
+ );
50449
+ return null;
50450
+ }
50451
+ let cursor = slotValueId;
50452
+ for (const segment of segments.slice(2)) {
50453
+ const body = registry.pendingValues.get(cursor)?.value ?? (isObjectRecord2(state[`value:${cursor}`]?.data) ? (state[`value:${cursor}`]?.data).value : void 0);
50454
+ if (Array.isArray(body)) {
50455
+ fail(
50456
+ `collectionValue path ${path} travels through a list at segment ${segment}; a path cannot choose a list entry. Name the row with Reference(id: ...) instead.`
50457
+ );
50458
+ return null;
50459
+ }
50460
+ if (!isObjectRecord2(body)) {
50461
+ fail(
50462
+ `collectionValue path ${path} stops before segment ${segment}: the row it reaches stores no single-valued members.`
50463
+ );
50464
+ return null;
50465
+ }
50466
+ const next = body[segment];
50467
+ if (typeof next !== "string") {
50468
+ fail(
50469
+ `collectionValue path ${path} has no value at segment ${segment} after this push.`
50470
+ );
50471
+ return null;
50472
+ }
50473
+ cursor = next;
50474
+ }
50475
+ return cursor;
50476
+ }
50477
+ function rootValuePathsByValueId(records2) {
50478
+ const paths = /* @__PURE__ */ new Map();
50479
+ const project = [...records2.values()].find(
50480
+ (record3) => record3.recordKind === "project"
50481
+ );
50482
+ if (project === void 0 || !isObjectRecord2(project.data)) return paths;
50483
+ const queue = [];
50484
+ ROOT_PROJECT_FIELDS.forEach((field, index) => {
50485
+ const slot = PROJECT_ROOT_SOURCE_SLOTS[index];
50486
+ const memberId = isObjectRecord2(project.data) ? project.data[field] : void 0;
50487
+ if (slot === void 0 || typeof memberId !== "string") return;
50488
+ const member = records2.get(`member:${memberId}`);
50489
+ const valueId = isObjectRecord2(member?.data) ? member.data.valueId : void 0;
50490
+ if (typeof valueId !== "string") return;
50491
+ queue.push({ id: valueId, path: `root.${slot.name}` });
50492
+ });
50493
+ while (queue.length > 0) {
50494
+ const next = queue.shift();
50495
+ if (next === void 0) break;
50496
+ if (paths.has(next.id)) continue;
50497
+ paths.set(next.id, next.path);
50498
+ const row = records2.get(`value:${next.id}`);
50499
+ const body = isObjectRecord2(row?.data) ? row.data.value : void 0;
50500
+ if (!isObjectRecord2(body)) continue;
50501
+ for (const [schemaKey, child] of Object.entries(body)) {
50502
+ if (typeof child !== "string") continue;
50503
+ if (!records2.has(`value:${child}`)) continue;
50504
+ queue.push({ id: child, path: `${next.path}.${schemaKey}` });
50505
+ }
50506
+ }
50507
+ return paths;
50508
+ }
50509
+ var ROOT_PROJECT_FIELDS, ROOT_PATH_PENDING_PREFIX;
48751
50510
  var init_root_source = __esm({
48752
50511
  "src/project-source/root-source.ts"() {
48753
50512
  "use strict";
@@ -48760,6 +50519,7 @@ var init_root_source = __esm({
48760
50519
  "rootSaveFileMemberId",
48761
50520
  "rootSessionMemberId"
48762
50521
  ];
50522
+ ROOT_PATH_PENDING_PREFIX = "__pending__:root-path:";
48763
50523
  }
48764
50524
  });
48765
50525
 
@@ -48788,7 +50548,8 @@ function emitProjectDocumentFilesV4(records2) {
48788
50548
  staticInitializers: staticValues.initializers,
48789
50549
  defaultInitializers: memberDefaults.initializers,
48790
50550
  fileSymbols: qualifiedProjectFileSymbolsV4(records2),
48791
- relationEndpointExpressions: relationEndpointExpressions(records2)
50551
+ relationEndpointExpressions: relationEndpointExpressions(records2),
50552
+ collectionValuePaths: rootValuePathsByValueId(records2)
48792
50553
  });
48793
50554
  const source = {
48794
50555
  ...baseSource,
@@ -51061,9 +52822,11 @@ function isWorldAnimationStructuralMember(memberId, members) {
51061
52822
  function assertAnimationClipDocumentValid(document) {
51062
52823
  const context = new AnimationValidationContext(document);
51063
52824
  context.validateConstructorProjections();
52825
+ context.validateSegments();
51064
52826
  for (const member of document.members) {
51065
52827
  if (!isMemberClass(member)) continue;
51066
52828
  if (!context.classHasWorldKind(member.classId, "animationClip")) continue;
52829
+ if (member.isAbstract === true) continue;
51067
52830
  context.validateClipMember(member);
51068
52831
  }
51069
52832
  }
@@ -51134,7 +52897,7 @@ var init_animation_clips = __esm({
51134
52897
  }
51135
52898
  const parameterNames = /* @__PURE__ */ new Set();
51136
52899
  const memberIds = /* @__PURE__ */ new Set();
51137
- const directMemberIds = new Set(Object.values(schemaClass2.schema));
52900
+ const effectiveMemberIds = this.effectiveSchemaMemberIds(schemaClass2);
51138
52901
  for (const projection of projections) {
51139
52902
  if (parameterNames.has(projection.parameterName)) {
51140
52903
  throw new Error(
@@ -51146,9 +52909,9 @@ var init_animation_clips = __esm({
51146
52909
  `Class "${schemaClass2.name}" projects multiple constructor parameters onto member "${projection.memberId}".`
51147
52910
  );
51148
52911
  }
51149
- if (!directMemberIds.has(projection.memberId)) {
52912
+ if (!effectiveMemberIds.has(projection.memberId)) {
51150
52913
  throw new Error(
51151
- `Class "${schemaClass2.name}" constructor parameter "${projection.parameterName}" references member "${projection.memberId}" outside its direct schema.`
52914
+ `Class "${schemaClass2.name}" constructor parameter "${projection.parameterName}" references member "${projection.memberId}", which is neither in its schema nor inherited.`
51152
52915
  );
51153
52916
  }
51154
52917
  parameterNames.add(projection.parameterName);
@@ -51156,6 +52919,27 @@ var init_animation_clips = __esm({
51156
52919
  }
51157
52920
  }
51158
52921
  }
52922
+ /**
52923
+ * Every member id a class resolves under a schema key — its own, then each
52924
+ * ancestor's.
52925
+ *
52926
+ * A projection may name an inherited member: P48 §2.1 moved `Child` onto
52927
+ * `NeoAnimationTrackBase`, and `new NeoAnimationChildTrack(id: "…")` still
52928
+ * projects onto that one member from a class that no longer declares it.
52929
+ */
52930
+ effectiveSchemaMemberIds(schemaClass2) {
52931
+ const memberIds = /* @__PURE__ */ new Set();
52932
+ const visited = /* @__PURE__ */ new Set();
52933
+ let current = schemaClass2;
52934
+ while (current !== void 0 && !visited.has(current.id)) {
52935
+ visited.add(current.id);
52936
+ for (const memberId of Object.values(current.schema)) {
52937
+ memberIds.add(memberId);
52938
+ }
52939
+ current = current.extendsClassId === void 0 ? void 0 : this.classById.get(current.extendsClassId);
52940
+ }
52941
+ return memberIds;
52942
+ }
51159
52943
  classHasWorldKind(classId, worldKind) {
51160
52944
  const visited = /* @__PURE__ */ new Set();
51161
52945
  let current = this.classById.get(classId);
@@ -51172,16 +52956,8 @@ var init_animation_clips = __esm({
51172
52956
  WORLD_ANIMATION_CLIP_TARGET_PARAM_ID,
51173
52957
  `Animation clip "${clipMember.name}"`
51174
52958
  );
51175
- const effectiveStorages = this.storage.effectiveStorages(clipMember.id);
51176
- if (effectiveStorages.size === 0 || [...effectiveStorages].some(
51177
- (storage) => storage !== "immutable" /* Immutable */
51178
- )) {
51179
- throw new Error(
51180
- `Animation clip "${clipMember.name}" must resolve Immutable storage.`
51181
- );
51182
- }
51183
52959
  const clipNode = this.memberRootNode(clipMember);
51184
- const fps = this.requirePositiveIntegerField(
52960
+ this.requirePositiveIntegerField(
51185
52961
  clipNode,
51186
52962
  clipMember.classId,
51187
52963
  WORLD_ANIMATION_CLIP_FPS_MEMBER_ID,
@@ -51210,7 +52986,7 @@ var init_animation_clips = __esm({
51210
52986
  const index = this.requireIntegerField(
51211
52987
  frameNode,
51212
52988
  frameClassId,
51213
- WORLD_ANIMATION_FRAME_INDEX_MEMBER_ID,
52989
+ WORLD_ANIMATION_FRAME_BASE_INDEX_MEMBER_ID,
51214
52990
  `Animation clip "${clipMember.name}" frame Index`
51215
52991
  );
51216
52992
  if (index < 0 || index >= duration) {
@@ -51258,13 +53034,12 @@ var init_animation_clips = __esm({
51258
53034
  targetClassId
51259
53035
  });
51260
53036
  }
51261
- this.validateChildTracks({
53037
+ this.validateTracks({
51262
53038
  clipName: clipMember.name,
51263
53039
  clipNode,
51264
53040
  clipClassId: clipMember.classId,
51265
- targetClassId,
51266
53041
  childIds: authoredChildren.ids,
51267
- fps,
53042
+ childStoragePath: authoredChildren.storagePath,
51268
53043
  duration
51269
53044
  });
51270
53045
  }
@@ -51377,97 +53152,401 @@ var init_animation_clips = __esm({
51377
53152
  );
51378
53153
  }
51379
53154
  }
51380
- validateChildTracks(args) {
53155
+ /**
53156
+ * `NeoAnimationClip.Tracks` holds `NeoAnimationTrackBase` rows since P48
53157
+ * §2.1, so a row's kind is a property of the row rather than of the list.
53158
+ * Everything the base declares — `Child`, `StartFrame`, `Direction`, the
53159
+ * crop window — is validated once here against the base member ids, which
53160
+ * the segment track's covariant `Child` override still descends from; the
53161
+ * per-kind passes below see an already-checked child.
53162
+ */
53163
+ validateTracks(args) {
51381
53164
  for (const track of this.requireListField(
51382
53165
  args.clipNode,
51383
53166
  args.clipClassId,
51384
53167
  WORLD_ANIMATION_CLIP_TRACKS_MEMBER_ID,
51385
53168
  `Animation clip "${args.clipName}" Tracks`
51386
53169
  )) {
51387
- const trackClassId = this.requireNodeClassId(
53170
+ const row = this.requireTrackRow(track, args.clipName);
53171
+ const label = `Animation clip "${args.clipName}" ${row.trackLabel} "${track.id}"`;
53172
+ const child = this.validateTrackBase({
51388
53173
  track,
51389
- "animationChildTrack"
53174
+ trackClassId: row.classId,
53175
+ childIds: args.childIds,
53176
+ duration: args.duration,
53177
+ label
53178
+ });
53179
+ if (row.kind === "childClip") {
53180
+ this.validateChildClipTrack({
53181
+ track,
53182
+ trackClassId: row.classId,
53183
+ childId: child.childId,
53184
+ childClassId: child.childClassId,
53185
+ childNode: child.childNode,
53186
+ label
53187
+ });
53188
+ continue;
53189
+ }
53190
+ this.validateSegmentTrack({
53191
+ trackClassId: row.classId,
53192
+ childClassId: child.childClassId,
53193
+ childId: child.childId,
53194
+ childStoragePath: args.childStoragePath,
53195
+ label
53196
+ });
53197
+ }
53198
+ }
53199
+ /**
53200
+ * A `Tracks` row's concrete kind. Dispatching on the row rather than on the
53201
+ * list is the whole point of the base class, and a row that is neither
53202
+ * shipped kind is named — with its class — rather than reported as "not a
53203
+ * animationChildTrack row", which was true of every segment track too.
53204
+ */
53205
+ requireTrackRow(track, clipName) {
53206
+ const classId = track.classId;
53207
+ if (typeof classId !== "string") {
53208
+ throw new Error(
53209
+ `Animation clip "${clipName}" track value "${track.id}" has no class, so its track kind cannot be resolved.`
51390
53210
  );
51391
- const childId = this.requireSingleLookupField(
51392
- track,
51393
- trackClassId,
51394
- WORLD_ANIMATION_CHILD_TRACK_CHILD_MEMBER_ID,
51395
- `Animation clip "${args.clipName}" child track`
53211
+ }
53212
+ if (this.classHasWorldKind(classId, "animationChildTrack")) {
53213
+ return { classId, kind: "childClip", trackLabel: "child track" };
53214
+ }
53215
+ if (this.classHasWorldKind(classId, "animationSegmentTrack")) {
53216
+ return { classId, kind: "segment", trackLabel: "segment track" };
53217
+ }
53218
+ const className = this.classById.get(classId)?.name ?? classId;
53219
+ throw new Error(
53220
+ `Animation clip "${clipName}" track value "${track.id}" has class "${className}", which is neither a child clip track nor a segment track.`
53221
+ );
53222
+ }
53223
+ /**
53224
+ * The members `NeoAnimationTrackBase` declares, for either kind of row.
53225
+ *
53226
+ * P48 §2.3 deletes P29's fit error: content that runs past the owning clip's
53227
+ * end truncates, because clipping is what a clip does. What survives is a row
53228
+ * that can never play at all — a `StartFrame` outside the clip, or a crop
53229
+ * window the author wrote empty or inverted. Crop bounds against the
53230
+ * *resolved* content are runtime-clamped instead, since a lookup-backed
53231
+ * segment's length is instance data this document does not have.
53232
+ */
53233
+ validateTrackBase(args) {
53234
+ const childId = this.requireSingleLookupField(
53235
+ args.track,
53236
+ args.trackClassId,
53237
+ WORLD_ANIMATION_TRACK_CHILD_MEMBER_ID,
53238
+ args.label
53239
+ );
53240
+ if (!args.childIds.has(childId)) {
53241
+ throw new Error(
53242
+ `${args.label} references child "${childId}" outside the owner's authored Children graph.`
51396
53243
  );
51397
- if (!args.childIds.has(childId)) {
51398
- throw new Error(
51399
- `Animation clip "${args.clipName}" child track references child "${childId}" outside the owner's authored Children graph.`
51400
- );
51401
- }
51402
- const child = this.valueById.get(childId);
51403
- const childClassId = child?.classId;
51404
- if (child === void 0 || typeof childClassId !== "string") {
51405
- throw new Error(
51406
- `Animation clip "${args.clipName}" child track references missing class-backed child "${childId}".`
51407
- );
51408
- }
51409
- const clipKey = this.requireStringField(
51410
- track,
51411
- trackClassId,
51412
- WORLD_ANIMATION_CHILD_TRACK_CLIP_KEY_MEMBER_ID,
51413
- `Animation clip "${args.clipName}" child track ClipKey`
53244
+ }
53245
+ const childNode = this.valueById.get(childId);
53246
+ const childClassId = childNode?.classId;
53247
+ if (childNode === void 0 || typeof childClassId !== "string") {
53248
+ throw new Error(
53249
+ `${args.label} references missing class-backed child "${childId}".`
51414
53250
  );
51415
- const childClipEntry = mergeInstanceSurfaceSchema(
51416
- childClassId,
51417
- this.document.classes,
51418
- this.document.members
51419
- ).find((entry) => entry.schemaKey === clipKey);
51420
- if (childClipEntry === void 0) {
51421
- throw new Error(
51422
- `Animation clip "${args.clipName}" child track ClipKey "${clipKey}" does not resolve to a compatible clip on child "${childId}".`
51423
- );
51424
- }
51425
- const childClipMember = this.memberById.get(childClipEntry.memberId);
51426
- if (!isMemberClass(childClipMember) || !this.classHasWorldKind(childClipMember.classId, "animationClip") || this.requireClassBinding(
51427
- childClipMember,
51428
- WORLD_ANIMATION_CLIP_TARGET_PARAM_ID,
51429
- `Child clip "${clipKey}"`
51430
- ) !== childClassId) {
51431
- throw new Error(
51432
- `Animation clip "${args.clipName}" child track ClipKey "${clipKey}" does not resolve to a compatible clip on child "${childId}".`
51433
- );
51434
- }
51435
- const childClipNode = this.resolveDefinitionChild(
51436
- child,
51437
- childClipEntry.schemaKey,
51438
- childClipMember
53251
+ }
53252
+ const startFrame = this.requireIntegerField(
53253
+ args.track,
53254
+ args.trackClassId,
53255
+ WORLD_ANIMATION_TRACK_START_FRAME_MEMBER_ID,
53256
+ `${args.label} StartFrame`
53257
+ );
53258
+ if (startFrame < 0) {
53259
+ throw new Error(`${args.label} StartFrame ${startFrame} is negative.`);
53260
+ }
53261
+ if (startFrame >= args.duration) {
53262
+ throw new Error(
53263
+ `${args.label} StartFrame ${startFrame} is at or past the owning clip's Duration ${args.duration}, so the row can never play.`
51439
53264
  );
51440
- if (childClipNode === null) {
51441
- throw new Error(
51442
- `Child clip "${clipKey}" on child "${childId}" has no value graph.`
51443
- );
53265
+ }
53266
+ this.validateTrackDirection(args.track, args.trackClassId, args.label);
53267
+ this.validateTrackCropWindow(args.track, args.trackClassId, args.label);
53268
+ return { childId, childClassId, childNode };
53269
+ }
53270
+ validateTrackDirection(track, trackClassId, label) {
53271
+ const authored = this.scalarField(
53272
+ track,
53273
+ trackClassId,
53274
+ WORLD_ANIMATION_TRACK_DIRECTION_MEMBER_ID
53275
+ );
53276
+ if (authored === void 0 || authored === null) return;
53277
+ if (!Array.isArray(authored) || authored.length !== 1 || authored[0] !== WORLD_PLAY_DIRECTION_FORWARD_OPTION_ID && authored[0] !== WORLD_PLAY_DIRECTION_REVERSE_OPTION_ID) {
53278
+ throw new Error(
53279
+ `${label} Direction must be exactly one NeoPlayDirection option.`
53280
+ );
53281
+ }
53282
+ }
53283
+ validateTrackCropWindow(track, trackClassId, label) {
53284
+ const start = this.optionalIntegerField(
53285
+ track,
53286
+ trackClassId,
53287
+ WORLD_ANIMATION_TRACK_OFFSET_START_INDEX_MEMBER_ID,
53288
+ `${label} OffsetStartIndex`
53289
+ );
53290
+ const end = this.optionalIntegerField(
53291
+ track,
53292
+ trackClassId,
53293
+ WORLD_ANIMATION_TRACK_OFFSET_END_INDEX_MEMBER_ID,
53294
+ `${label} OffsetEndIndex`
53295
+ );
53296
+ if (start !== null && start < 0) {
53297
+ throw new Error(`${label} OffsetStartIndex ${start} is negative.`);
53298
+ }
53299
+ if (end !== null && end < 1) {
53300
+ throw new Error(
53301
+ `${label} OffsetEndIndex ${end} must be at least 1; a window has to contain a frame.`
53302
+ );
53303
+ }
53304
+ if (start === null || end === null) return;
53305
+ if (end <= start) {
53306
+ throw new Error(
53307
+ `${label} crop window [${start}, ${end}) is empty or inverted, so the row can never play.`
53308
+ );
53309
+ }
53310
+ }
53311
+ validateChildClipTrack(args) {
53312
+ const clipKey = this.requireStringField(
53313
+ args.track,
53314
+ args.trackClassId,
53315
+ WORLD_ANIMATION_CHILD_TRACK_CLIP_KEY_MEMBER_ID,
53316
+ `${args.label} ClipKey`
53317
+ );
53318
+ const childClipEntry = mergeInstanceSurfaceSchema(
53319
+ args.childClassId,
53320
+ this.document.classes,
53321
+ this.document.members
53322
+ ).find((entry) => entry.schemaKey === clipKey);
53323
+ if (childClipEntry === void 0) {
53324
+ throw new Error(
53325
+ `${args.label} ClipKey "${clipKey}" does not resolve to a compatible clip on child "${args.childId}".`
53326
+ );
53327
+ }
53328
+ const childClipMember = this.memberById.get(childClipEntry.memberId);
53329
+ if (!isMemberClass(childClipMember) || !this.classHasWorldKind(childClipMember.classId, "animationClip") || this.requireClassBinding(
53330
+ childClipMember,
53331
+ WORLD_ANIMATION_CLIP_TARGET_PARAM_ID,
53332
+ `Child clip "${clipKey}"`
53333
+ ) !== args.childClassId) {
53334
+ throw new Error(
53335
+ `${args.label} ClipKey "${clipKey}" does not resolve to a compatible clip on child "${args.childId}".`
53336
+ );
53337
+ }
53338
+ const childClipNode = this.resolveDefinitionChild(
53339
+ args.childNode,
53340
+ childClipEntry.schemaKey,
53341
+ childClipMember
53342
+ );
53343
+ if (childClipNode === null) {
53344
+ throw new Error(
53345
+ `Child clip "${clipKey}" on child "${args.childId}" has no value graph.`
53346
+ );
53347
+ }
53348
+ this.requirePositiveIntegerField(
53349
+ childClipNode,
53350
+ childClipMember.classId,
53351
+ WORLD_ANIMATION_CLIP_FPS_MEMBER_ID,
53352
+ `Child clip "${clipKey}" FPS`
53353
+ );
53354
+ this.requirePositiveIntegerField(
53355
+ childClipNode,
53356
+ childClipMember.classId,
53357
+ WORLD_ANIMATION_CLIP_DURATION_MEMBER_ID,
53358
+ `Child clip "${clipKey}" Duration`
53359
+ );
53360
+ }
53361
+ /**
53362
+ * P48 §7's target rule, as much of it as the record layer can answer.
53363
+ *
53364
+ * The write target is schema metadata — `@settings(target: Class.Member)` on
53365
+ * a concrete track class — so it is resolved from the row's class chain and
53366
+ * checked against the class's *bound* `TChild`, not against whatever child
53367
+ * this particular row happens to name. The instance side is checked too:
53368
+ * a row whose `Child` is not a `TChild` would type-check a getter body
53369
+ * against a class the row cannot deliver.
53370
+ *
53371
+ * What stays in the language (phase 2) is everything about the `Segment`
53372
+ * implementation itself — that every concrete subclass has one, and that a
53373
+ * getter body type-checks against `TChild`.
53374
+ */
53375
+ validateSegmentTrack(args) {
53376
+ const trackClassName = this.classById.get(args.trackClassId)?.name ?? args.trackClassId;
53377
+ const targetMemberId = this.resolveTargetMemberId(args.trackClassId);
53378
+ if (targetMemberId === null) {
53379
+ throw new Error(
53380
+ `${args.label} class "${trackClassName}" declares no target member, so it has nothing to write.`
53381
+ );
53382
+ }
53383
+ const env = resolveGenericEnv(args.trackClassId, this.document.classes);
53384
+ const boundChildClassId = this.envClassBinding(
53385
+ env,
53386
+ WORLD_ANIMATION_SEGMENT_TRACK_CHILD_PARAM_ID
53387
+ );
53388
+ if (boundChildClassId === null) {
53389
+ throw new Error(
53390
+ `${args.label} class "${trackClassName}" does not bind its TChild generic to a Class member.`
53391
+ );
53392
+ }
53393
+ if (!this.classDescendsFrom(args.childClassId, boundChildClassId)) {
53394
+ const boundName = this.classById.get(boundChildClassId)?.name ?? boundChildClassId;
53395
+ const childName = this.classById.get(args.childClassId)?.name ?? args.childClassId;
53396
+ throw new Error(
53397
+ `${args.label} plays against child "${args.childId}" of class "${childName}", which does not descend from the track's bound TChild "${boundName}".`
53398
+ );
53399
+ }
53400
+ const targetEntry = mergeStoredInstanceSchema(
53401
+ boundChildClassId,
53402
+ this.document.classes,
53403
+ this.document.members
53404
+ ).find((entry) => this.memberDescendsFrom(entry.memberId, targetMemberId));
53405
+ const boundChildName = this.classById.get(boundChildClassId)?.name ?? boundChildClassId;
53406
+ if (targetEntry === void 0) {
53407
+ throw new Error(
53408
+ `${args.label} class "${trackClassName}" targets member "${targetMemberId}", which "${boundChildName}" does not declare.`
53409
+ );
53410
+ }
53411
+ const targetMember = this.memberById.get(targetEntry.memberId);
53412
+ if (targetMember === void 0) {
53413
+ throw new Error(
53414
+ `${args.label} target member "${targetEntry.memberId}" is not in this document.`
53415
+ );
53416
+ }
53417
+ const valueBindingMember = this.envBindingMember(
53418
+ env,
53419
+ WORLD_ANIMATION_SEGMENT_TRACK_VALUE_PARAM_ID
53420
+ );
53421
+ if (valueBindingMember === null) {
53422
+ throw new Error(
53423
+ `${args.label} class "${trackClassName}" does not bind its TValue generic to a member.`
53424
+ );
53425
+ }
53426
+ if (targetMember.kind !== valueBindingMember.kind) {
53427
+ throw new Error(
53428
+ `${args.label} targets "${boundChildName}.${targetEntry.schemaKey}" of kind ${MemberKind[targetMember.kind]}, but its segment plays ${MemberKind[valueBindingMember.kind]} values.`
53429
+ );
53430
+ }
53431
+ if (!this.overrideLeafEligible(targetMember, [
53432
+ ...args.childStoragePath,
53433
+ targetMember.id
53434
+ ])) {
53435
+ throw new Error(
53436
+ `${args.label} targets "${boundChildName}.${targetEntry.schemaKey}", which is not a writable Save/Session override leaf.`
53437
+ );
53438
+ }
53439
+ }
53440
+ /** The `@settings(target:)` member a track class or one of its bases names. */
53441
+ resolveTargetMemberId(classId) {
53442
+ const visited = /* @__PURE__ */ new Set();
53443
+ let current = this.classById.get(classId);
53444
+ while (current !== void 0 && !visited.has(current.id)) {
53445
+ visited.add(current.id);
53446
+ if (typeof current.targetMemberId === "string") {
53447
+ return current.targetMemberId;
51444
53448
  }
51445
- const childFps = this.requirePositiveIntegerField(
51446
- childClipNode,
51447
- childClipMember.classId,
51448
- WORLD_ANIMATION_CLIP_FPS_MEMBER_ID,
51449
- `Child clip "${clipKey}" FPS`
53449
+ current = current.extendsClassId ? this.classById.get(current.extendsClassId) : void 0;
53450
+ }
53451
+ return null;
53452
+ }
53453
+ envBindingMember(env, paramId) {
53454
+ const entry = env.get(paramId);
53455
+ if (entry === void 0 || entry.kind !== "member") return null;
53456
+ if (typeof entry.memberId !== "string") return null;
53457
+ return this.memberById.get(entry.memberId) ?? null;
53458
+ }
53459
+ envClassBinding(env, paramId) {
53460
+ const member = this.envBindingMember(env, paramId);
53461
+ return isMemberClass(member) ? member.classId : null;
53462
+ }
53463
+ classDescendsFrom(classId, baseClassId) {
53464
+ const visited = /* @__PURE__ */ new Set();
53465
+ let current = this.classById.get(classId);
53466
+ while (current !== void 0 && !visited.has(current.id)) {
53467
+ if (current.id === baseClassId) return true;
53468
+ visited.add(current.id);
53469
+ current = current.extendsClassId ? this.classById.get(current.extendsClassId) : void 0;
53470
+ }
53471
+ return false;
53472
+ }
53473
+ /**
53474
+ * P48 §1 and §7's segment rules, for every segment the document holds.
53475
+ *
53476
+ * Segments are validated by *value*, not by clip: a segment is catalog data
53477
+ * that no clip owns, and the same rows are reachable from every track that
53478
+ * resolves through them. Both authoring shapes are walked — a member's own
53479
+ * default graph and a standalone row — because a stored segment member and a
53480
+ * segment sitting in a list are the same data written two ways.
53481
+ *
53482
+ * Storage is deliberately not asked about (§1.2): Immutable catalogs, Save
53483
+ * files, and Session all hold segments, and a Session-stored `Duration` a
53484
+ * game writes at runtime is a feature, not drift.
53485
+ */
53486
+ validateSegments() {
53487
+ const visited = /* @__PURE__ */ new Set();
53488
+ for (const member of this.document.members) {
53489
+ if (!isMemberClass(member)) continue;
53490
+ if (!this.classHasWorldKind(member.classId, "animationSegment")) continue;
53491
+ const node = this.optionalMemberRootNode(member);
53492
+ if (node === null) continue;
53493
+ this.validateSegmentNode(
53494
+ node,
53495
+ member.classId,
53496
+ `Animation segment "${member.name}"`,
53497
+ visited
51450
53498
  );
51451
- const childDuration = this.requirePositiveIntegerField(
51452
- childClipNode,
51453
- childClipMember.classId,
51454
- WORLD_ANIMATION_CLIP_DURATION_MEMBER_ID,
51455
- `Child clip "${clipKey}" Duration`
53499
+ }
53500
+ for (const row of this.document.values) {
53501
+ const classId = row.classId;
53502
+ if (typeof classId !== "string") continue;
53503
+ if (!this.classHasWorldKind(classId, "animationSegment")) continue;
53504
+ this.validateSegmentNode(
53505
+ row,
53506
+ classId,
53507
+ `Animation segment value "${row.id}"`,
53508
+ visited
51456
53509
  );
51457
- const startFrame = this.requireIntegerField(
51458
- track,
51459
- trackClassId,
51460
- WORLD_ANIMATION_CHILD_TRACK_START_FRAME_MEMBER_ID,
51461
- `Animation clip "${args.clipName}" child track StartFrame`
53510
+ }
53511
+ }
53512
+ validateSegmentNode(node, classId, label, visited) {
53513
+ if (visited.has(node.id)) return;
53514
+ visited.add(node.id);
53515
+ const duration = this.requirePositiveIntegerField(
53516
+ node,
53517
+ classId,
53518
+ WORLD_ANIMATION_SEGMENT_DURATION_MEMBER_ID,
53519
+ `${label} Duration`
53520
+ );
53521
+ const frameValueIdByIndex = /* @__PURE__ */ new Map();
53522
+ for (const frameNode of this.requireListField(
53523
+ node,
53524
+ classId,
53525
+ WORLD_ANIMATION_SEGMENT_FRAMES_MEMBER_ID,
53526
+ `${label} Frames`
53527
+ )) {
53528
+ const frameClassId = this.requireNodeClassId(
53529
+ frameNode,
53530
+ "animationSegmentFrame"
51462
53531
  );
51463
- const childLengthInParentFrames = Math.ceil(
51464
- childDuration * args.fps / childFps
53532
+ const index = this.requireIntegerField(
53533
+ frameNode,
53534
+ frameClassId,
53535
+ WORLD_ANIMATION_FRAME_BASE_INDEX_MEMBER_ID,
53536
+ `${label} frame Index`
51465
53537
  );
51466
- if (startFrame < 0 || startFrame + childLengthInParentFrames > args.duration) {
53538
+ if (index < 0 || index >= duration) {
53539
+ throw new Error(
53540
+ `${label} frame ${index} is outside Duration ${duration}; shrink operations must delete trailing frames.`
53541
+ );
53542
+ }
53543
+ const priorFrameValueId = frameValueIdByIndex.get(index);
53544
+ if (priorFrameValueId !== void 0) {
51467
53545
  throw new Error(
51468
- `Animation clip "${args.clipName}" child track "${clipKey}" does not fit: StartFrame ${startFrame} + ${childLengthInParentFrames} parent frames exceeds Duration ${args.duration}.`
53546
+ `${label} has duplicate frame Index ${index} in values "${priorFrameValueId}" and "${frameNode.id}".`
51469
53547
  );
51470
53548
  }
53549
+ frameValueIdByIndex.set(index, frameNode.id);
51471
53550
  }
51472
53551
  }
51473
53552
  validateSparseOverride(args) {
@@ -51868,6 +53947,20 @@ var init_animation_clips = __esm({
51868
53947
  }
51869
53948
  return value;
51870
53949
  }
53950
+ /**
53951
+ * {@link requireIntegerField} for a nullable int member such as the P48 §2.1
53952
+ * crop window. `null` covers both "authored null" and "no value and no
53953
+ * default"; anything that is neither null nor a safe integer still throws,
53954
+ * so a garbage crop bound is not silently read as an open window.
53955
+ */
53956
+ optionalIntegerField(parent, classId, memberId, label) {
53957
+ const value = this.scalarField(parent, classId, memberId);
53958
+ if (value === null || value === void 0) return null;
53959
+ if (typeof value !== "number" || !Number.isSafeInteger(value)) {
53960
+ throw new Error(`${label} must be an integer or null.`);
53961
+ }
53962
+ return value;
53963
+ }
51871
53964
  requireStringField(parent, classId, memberId, label) {
51872
53965
  const value = this.scalarField(parent, classId, memberId);
51873
53966
  if (typeof value !== "string" || value.length === 0) {
@@ -52269,6 +54362,7 @@ function computeWorkspaceStatus(workspace, options = {}) {
52269
54362
  seeds: /* @__PURE__ */ new Map()
52270
54363
  };
52271
54364
  let projectAnalysisV4 = null;
54365
+ const valueLowerRegistry = createValueLowerRegistryV4();
52272
54366
  try {
52273
54367
  if (!baseManifest) {
52274
54368
  throw new Error(
@@ -52343,7 +54437,8 @@ function computeWorkspaceStatus(workspace, options = {}) {
52343
54437
  memberDefaults = lowerMemberDefaultSourcesV4(
52344
54438
  workspace.state.records,
52345
54439
  analysis,
52346
- manifest
54440
+ manifest,
54441
+ { registry: valueLowerRegistry }
52347
54442
  );
52348
54443
  if (memberDefaults.memberDefaultValues.size > 0) {
52349
54444
  manifest = {
@@ -52360,7 +54455,7 @@ function computeWorkspaceStatus(workspace, options = {}) {
52360
54455
  );
52361
54456
  } catch (error) {
52362
54457
  parseErrors.push(
52363
- new SchemaSourceError(
54458
+ error instanceof SchemaSourceError ? error : new SchemaSourceError(
52364
54459
  error instanceof Error ? error.message : String(error),
52365
54460
  "<project>",
52366
54461
  1,
@@ -52417,16 +54512,54 @@ function computeWorkspaceStatus(workspace, options = {}) {
52417
54512
  const staticValues = lowerStaticValueSourcesV4(
52418
54513
  workspace.state.records,
52419
54514
  projectAnalysisV4,
52420
- manifest
54515
+ manifest,
54516
+ { registry: valueLowerRegistry }
52421
54517
  );
52422
54518
  staticValueSeeds = new Map([...staticValues.seeds, ...memberDefaults.seeds]);
52423
54519
  staticMemberValueIds = staticValues.memberValueIds;
52424
54520
  const rootValues = lowerProjectRootSourceV4(
52425
54521
  workspace.state.records,
52426
54522
  projectAnalysisV4,
52427
- manifest
54523
+ manifest,
54524
+ { registry: valueLowerRegistry }
52428
54525
  );
52429
54526
  staticValueSeeds = new Map([...staticValueSeeds, ...rootValues.seeds]);
54527
+ const rootPathResolutionState = overlayProspectiveSourceRecords(
54528
+ workspace.state.records,
54529
+ documents,
54530
+ [...staticValues.records, ...memberDefaults.records, ...rootValues.records],
54531
+ staticMemberValueIds
54532
+ );
54533
+ const resolvedCollectionPaths = resolveRootPathCollectionValuesV4({
54534
+ manifest,
54535
+ state: rootPathResolutionState,
54536
+ registry: valueLowerRegistry,
54537
+ siteForMember: (memberId) => {
54538
+ const span2 = sourceByKey.get(recordStateKey("member", memberId))?.span;
54539
+ return span2 === void 0 ? null : { uri: span2.path, start: span2.start };
54540
+ }
54541
+ });
54542
+ if (resolvedCollectionPaths.size > 0) {
54543
+ manifest = {
54544
+ ...manifest,
54545
+ members: manifest.members.map((member) => {
54546
+ const resolvedId = resolvedCollectionPaths.get(member.id);
54547
+ return resolvedId === void 0 || member.kind !== "lookup" ? member : { ...member, collectionValueId: resolvedId };
54548
+ })
54549
+ };
54550
+ for (const [memberId, resolvedId] of resolvedCollectionPaths) {
54551
+ const document = documentsByKey.get(recordStateKey("member", memberId));
54552
+ if (document !== void 0 && isObjectRecord2(document.data)) {
54553
+ document.data.collectionValueId = resolvedId;
54554
+ }
54555
+ const record3 = records2.find(
54556
+ (candidate) => candidate.recordKind === "member" && candidate.recordId === memberId
54557
+ );
54558
+ if (record3 !== void 0 && typeof record3.fileFields.collectionValueId === "string") {
54559
+ record3.fileFields.collectionValueId = resolvedId;
54560
+ }
54561
+ }
54562
+ }
52430
54563
  const rootRecords = rootValues.records;
52431
54564
  const supplementalRecords = lowerSupplementalProjectSourcesV4(
52432
54565
  workspace.root,
@@ -52434,7 +54567,7 @@ function computeWorkspaceStatus(workspace, options = {}) {
52434
54567
  projectAnalysisV4,
52435
54568
  { trustedPendingFiles: options.trustedPendingProjectFiles }
52436
54569
  );
52437
- const dialogueState = overlayProspectiveSourceRecords(
54570
+ const prospectiveState = overlayProspectiveSourceRecords(
52438
54571
  workspace.state.records,
52439
54572
  documents,
52440
54573
  [
@@ -52446,7 +54579,7 @@ function computeWorkspaceStatus(workspace, options = {}) {
52446
54579
  staticMemberValueIds
52447
54580
  );
52448
54581
  const dialogueRecords = lowerDialogueProjectSourcesV4(
52449
- dialogueState,
54582
+ prospectiveState,
52450
54583
  projectAnalysisV4
52451
54584
  );
52452
54585
  records2.push(
@@ -52520,6 +54653,7 @@ function computeWorkspaceStatus(workspace, options = {}) {
52520
54653
  });
52521
54654
  }
52522
54655
  }
54656
+ const deletedValueIds = /* @__PURE__ */ new Set();
52523
54657
  for (const [key, recordState] of Object.entries(workspace.state.records)) {
52524
54658
  if (recordState.file === void 0) continue;
52525
54659
  if (reconstructed3.has(key)) continue;
@@ -52527,6 +54661,9 @@ function computeWorkspaceStatus(workspace, options = {}) {
52527
54661
  (error) => error.file === recordState.file && isBlockingSchemaSourceError(error)
52528
54662
  ) || conflictedFiles.includes(recordState.file);
52529
54663
  if (fileStillBroken) continue;
54664
+ if (recordState.recordKind === "value") {
54665
+ deletedValueIds.add(recordState.recordId);
54666
+ }
52530
54667
  changes.push({
52531
54668
  kind: "delete",
52532
54669
  recordKind: recordState.recordKind,
@@ -52537,6 +54674,52 @@ function computeWorkspaceStatus(workspace, options = {}) {
52537
54674
  casBaseHash: recordState.conflictServerHash !== void 0 ? recordState.conflictServerHash : recordState.contentHash
52538
54675
  });
52539
54676
  }
54677
+ const referenceFailures = drainValueReferenceObligationsV4(
54678
+ prospectiveState,
54679
+ manifest,
54680
+ {
54681
+ registry: valueLowerRegistry,
54682
+ analysis: projectAnalysisV4,
54683
+ deletedValueIds,
54684
+ sourceTextByUri: new Map(
54685
+ sourceEntries.map((entry) => [entry.relPath, entry.source])
54686
+ )
54687
+ }
54688
+ );
54689
+ for (const failure of referenceFailures) {
54690
+ parseErrors.push(
54691
+ new SchemaSourceError(
54692
+ failure.message,
54693
+ failure.file,
54694
+ failure.line,
54695
+ failure.column
54696
+ )
54697
+ );
54698
+ }
54699
+ if (referenceFailures.length > 0) {
54700
+ return {
54701
+ changes: [],
54702
+ conflictedFiles,
54703
+ parseErrors,
54704
+ reconstructed: /* @__PURE__ */ new Map(),
54705
+ staticValueSeeds: /* @__PURE__ */ new Map(),
54706
+ binaryChanges: [],
54707
+ binaryFiles: []
54708
+ };
54709
+ }
54710
+ for (let index = changes.length - 1; index >= 0; index -= 1) {
54711
+ const change = changes[index];
54712
+ if (change === void 0 || change.kind !== "update") continue;
54713
+ const baseState = workspace.state.records[recordStateKey(change.recordKind, change.recordId)];
54714
+ if (baseState?.conflictServerHash !== void 0) continue;
54715
+ if (recordsSemanticallyEqual(
54716
+ change.recordKind,
54717
+ change.nextData,
54718
+ change.baseData
54719
+ )) {
54720
+ changes.splice(index, 1);
54721
+ }
54722
+ }
52540
54723
  for (const fileId of options.trustedPendingProjectFiles?.keys() ?? []) {
52541
54724
  const key = `project-file:${fileId}`;
52542
54725
  const base = workspace.state.records[key];
@@ -52639,7 +54822,7 @@ function validateProspectiveAnimationRecordsV4(records2) {
52639
54822
  function prospectiveAnimationDocumentV4(records2) {
52640
54823
  const candidates = [...records2];
52641
54824
  const hasAnimationSchema = candidates.some(
52642
- (record3) => record3.recordKind === "class" && (Array.isArray(record3.data.constructorProjections) || isObjectRecord2(record3.data.system) && typeof record3.data.system.worldKind === "string" && record3.data.system.worldKind.startsWith("animation"))
54825
+ (record3) => record3.recordKind === "class" && (Array.isArray(record3.data.constructorProjections) || declaresAnimationWorldKind(record3.data.system))
52643
54826
  );
52644
54827
  if (!hasAnimationSchema) return null;
52645
54828
  let project;
@@ -52678,7 +54861,35 @@ function prospectiveAnimationDocumentV4(records2) {
52678
54861
  }
52679
54862
  }
52680
54863
  if (project === void 0) return null;
52681
- return { project, classes, members, values };
54864
+ const classIdByName = new Map(classes.map((value) => [value.name, value.id]));
54865
+ const validationValues = values.map((value) => {
54866
+ if (typeof value.classId === "string") return value;
54867
+ const authoredInit = Reflect.get(value, "init");
54868
+ const init = isObjectRecord2(authoredInit) ? authoredInit : null;
54869
+ if (typeof init?.code !== "string") return value;
54870
+ let expression;
54871
+ try {
54872
+ expression = parseExpression(init.code);
54873
+ while (expression.kind === "annotated") {
54874
+ expression = expression.expression;
54875
+ }
54876
+ } catch {
54877
+ return value;
54878
+ }
54879
+ if (expression.kind !== "new" || expression.className === null)
54880
+ return value;
54881
+ const classId = classIdByName.get(expression.className);
54882
+ if (classId === void 0) return value;
54883
+ return { ...value, classId };
54884
+ });
54885
+ return { project, classes, members, values: validationValues };
54886
+ }
54887
+ function declaresAnimationWorldKind(system) {
54888
+ if (!isObjectRecord2(system)) return false;
54889
+ const worldKind = system.worldKind;
54890
+ if (typeof worldKind !== "string") return false;
54891
+ if (!isNeoWorldSystemClassKind(worldKind)) return false;
54892
+ return WORLD_SYSTEM_ANIMATION_KINDS.has(worldKind);
52682
54893
  }
52683
54894
  function serverEnvelope(stored) {
52684
54895
  if (!isObjectRecord2(stored)) return {};
@@ -52956,6 +55167,7 @@ var init_workspace_status = __esm({
52956
55167
  init_project_files();
52957
55168
  init_dialogue_lower();
52958
55169
  init_world_system_classes();
55170
+ init_core();
52959
55171
  init_value_sources();
52960
55172
  init_root_source();
52961
55173
  init_project_documents();
@@ -59608,13 +61820,24 @@ function createdSessionValuesSince(session, existingIds) {
59608
61820
  function evaluateNSGetter(getter, ctx) {
59609
61821
  return evaluateNSGetterWithEffects(getter, ctx).value;
59610
61822
  }
59611
- function evaluateNSGetterWithEffects(getter, ctx) {
61823
+ function evaluateNSGetterWithEffects(getter, ctx, argumentValues = []) {
59612
61824
  const ownsInvocationState = ctx.__executionState === void 0;
59613
61825
  const requestedWrites = [];
59614
61826
  const runtimeCtx = withEvaluationRuntime(ctx, requestedWrites);
59615
61827
  const writes = runtimeCtx.__executionState?.writes ?? requestedWrites;
59616
61828
  const existingIds = new Set(runtimeCtx.__runtimeSessionValues.keys());
59617
61829
  const scope = createTopLevelScope(runtimeCtx);
61830
+ const parameters = getter.parameters.slice(2);
61831
+ if (parameters.length !== argumentValues.length) {
61832
+ throw new NSGetterRuntimeError(
61833
+ `NeoScript getter expected ${parameters.length} argument(s), got ${argumentValues.length}.`
61834
+ );
61835
+ }
61836
+ for (let index = 0; index < argumentValues.length; index += 1) {
61837
+ const parameter3 = parameters[index];
61838
+ if (parameter3 === void 0) continue;
61839
+ scope.set(parameter3.id, argumentValues[index]);
61840
+ }
59618
61841
  try {
59619
61842
  const result = evalInstructions(
59620
61843
  getter.instructions,
@@ -63014,10 +65237,16 @@ function runDeclaredConstructorChain(args) {
63014
65237
  true
63015
65238
  );
63016
65239
  }
63017
- function constructionInitEvaluator(ctx, createdValues) {
63018
- return (member, init) => evaluateInitializerInContext(init, member, ctx, createdValues);
65240
+ function constructionInitEvaluator(ctx, createdValues, argumentValues = []) {
65241
+ return (member, init) => evaluateInitializerInContext(
65242
+ init,
65243
+ member,
65244
+ ctx,
65245
+ createdValues,
65246
+ argumentValues
65247
+ );
63019
65248
  }
63020
- function evaluateInitializerInContext(init, member, ctx, createdValues) {
65249
+ function evaluateInitializerInContext(init, member, ctx, createdValues, argumentValues = []) {
63021
65250
  const compiled = init.compiled;
63022
65251
  if (compiled === void 0) {
63023
65252
  throw new NSGetterRuntimeError(
@@ -63027,7 +65256,11 @@ function evaluateInitializerInContext(init, member, ctx, createdValues) {
63027
65256
  const closeFrame = pushConstructionFrame(ctx, `${member.name} initializer`);
63028
65257
  let result;
63029
65258
  try {
63030
- result = evaluateNSGetterWithEffects(compiled, { ...ctx, thisValue: null });
65259
+ result = evaluateNSGetterWithEffects(
65260
+ compiled,
65261
+ { ...ctx, thisValue: null },
65262
+ argumentValues
65263
+ );
63031
65264
  } finally {
63032
65265
  closeFrame();
63033
65266
  }
@@ -63087,7 +65320,11 @@ function constructDeclaredClassValueWithinFrame(descriptor, record3, info, scope
63087
65320
  topLevelClassId: classId,
63088
65321
  createdValues,
63089
65322
  storageKeyDeclarations,
63090
- initEvaluator: constructionInitEvaluator(ctx, createdValues),
65323
+ initEvaluator: constructionInitEvaluator(
65324
+ ctx,
65325
+ createdValues,
65326
+ argumentValues
65327
+ ),
63091
65328
  constructorRoot: {
63092
65329
  providedSchemaKeys: new Set(
63093
65330
  descriptor.fields.map((validated) => validated.field.schemaKey)
@@ -71206,6 +73443,11 @@ function verifyProjectSourceCommitAgainstStateV4(args) {
71206
73443
  first ? `Trusted source lowering failed: ${first.file}:${first.line}:${first.column} ${first.message}` : `Trusted source lowering found conflict markers in ${status.conflictedFiles[0]}.`
71207
73444
  );
71208
73445
  }
73446
+ auditAuthoredSeedRowIdentities(
73447
+ status.staticValueSeeds,
73448
+ args.stateRecords,
73449
+ assignments
73450
+ );
71209
73451
  const usedAssignments = /* @__PURE__ */ new Set();
71210
73452
  const expectedChanges = status.changes.map((change) => {
71211
73453
  const rewrittenData = change.nextData === void 0 ? void 0 : rewritePending(change.nextData, assignments, usedAssignments);
@@ -71275,6 +73517,32 @@ function isDerivedOnlyRefresh(change, stateRecords) {
71275
73517
  if (!isObjectRecord2(base.data)) return false;
71276
73518
  return canonicalStringify(comparisonData(change.recordKind, change.nextData)) === canonicalStringify(comparisonData(change.recordKind, base.data));
71277
73519
  }
73520
+ function auditAuthoredSeedRowIdentities(seeds, stateRecords, assignments) {
73521
+ const mintedIds = new Set(assignments.values());
73522
+ const authoredCreates = /* @__PURE__ */ new Set();
73523
+ for (const [memberId, seed] of seeds) {
73524
+ for (const row of seed.values ?? []) {
73525
+ if (isPendingId(row.id)) continue;
73526
+ if (stateRecords[`value:${row.id}`] !== void 0) continue;
73527
+ if (!isUuidV4Id(row.id)) {
73528
+ throw new ProjectSourceCommitVerificationError(
73529
+ `Static value seed for member ${memberId} creates row ${row.id}, which is not a UUID v4. A new row's authored identity must wear the same shape a minted one does.`
73530
+ );
73531
+ }
73532
+ if (mintedIds.has(row.id)) {
73533
+ throw new ProjectSourceCommitVerificationError(
73534
+ `Static value seed for member ${memberId} creates row ${row.id}, which a pending id assignment in the same push also claims. Authored and minted identities draw from one pool.`
73535
+ );
73536
+ }
73537
+ if (authoredCreates.has(row.id)) {
73538
+ throw new ProjectSourceCommitVerificationError(
73539
+ `Static value seed for member ${memberId} creates row ${row.id}, which another seed in the same push already creates. Two new rows cannot share one @id.`
73540
+ );
73541
+ }
73542
+ authoredCreates.add(row.id);
73543
+ }
73544
+ }
73545
+ }
71278
73546
  function validateAssignments(value) {
71279
73547
  const assignments = /* @__PURE__ */ new Map();
71280
73548
  const assignedIds = /* @__PURE__ */ new Set();
@@ -71285,9 +73553,7 @@ function validateAssignments(value) {
71285
73553
  );
71286
73554
  }
71287
73555
  if (pendingMemberValueMemberId(pendingId2) === null) {
71288
- if (!/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/u.test(
71289
- assignedId
71290
- )) {
73556
+ if (!isUuidV4Id(assignedId)) {
71291
73557
  throw new ProjectSourceCommitVerificationError(
71292
73558
  `Pending id assignment for ${pendingId2} is not a UUID v4.`
71293
73559
  );