@neocompose/cli 0.15.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/dist/neo.mjs +2288 -342
  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"],
@@ -30914,10 +31103,17 @@ var init_world_system_kinds_generated = __esm({
30914
31103
  SmartTile: "smartTile",
30915
31104
  SmartTileRule: "smartTileRule",
30916
31105
  SmartTileNeighbor: "smartTileNeighbor",
31106
+ AnimationFrameBase: "animationFrameBase",
30917
31107
  AnimationClip: "animationClip",
30918
31108
  AnimationFrame: "animationFrame",
31109
+ AnimationSegmentFrame: "animationSegmentFrame",
31110
+ AnimationSegment: "animationSegment",
31111
+ SpriteAnimationSegment: "spriteAnimationSegment",
30919
31112
  AnimationChildOverride: "animationChildOverride",
30920
- AnimationChildTrack: "animationChildTrack"
31113
+ AnimationTrack: "animationTrack",
31114
+ AnimationChildTrack: "animationChildTrack",
31115
+ AnimationSegmentTrack: "animationSegmentTrack",
31116
+ SpriteAnimationSegmentTrack: "spriteAnimationSegmentTrack"
30921
31117
  };
30922
31118
  NEO_WORLD_SYSTEM_CLASS_KIND_VALUES = new Set(
30923
31119
  Object.values(NeoWorldSystemClassKind)
@@ -31995,7 +32191,7 @@ function isNeoSchemaClassBase(value) {
31995
32191
  (interfaceId) => typeof interfaceId === "string" && interfaceId.length > 0
31996
32192
  )) && 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
32193
  (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));
32194
+ ) && 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
32195
  }
32000
32196
  function isNeoClassConstructorProjection(value) {
32001
32197
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
@@ -32470,6 +32666,9 @@ function isMemberLookupBase(value) {
32470
32666
  if (v.collectionValueId !== void 0 && v.collectionValueId !== null && typeof v.collectionValueId !== "string") {
32471
32667
  return false;
32472
32668
  }
32669
+ if (v.declaredTypeInfo !== void 0 && v.declaredTypeInfo !== null && !isNSTypeInfo(v.declaredTypeInfo)) {
32670
+ return false;
32671
+ }
32473
32672
  return typeof v.multiselect === "boolean";
32474
32673
  }
32475
32674
  function isMemberLookup(value) {
@@ -32498,6 +32697,14 @@ function isMemberNSPropertyBase(value) {
32498
32697
  if (v.setterCode !== void 0 && v.setterCode.length === 0) return false;
32499
32698
  return isNSTypeInfo(v.returnTypeInfo);
32500
32699
  }
32700
+ function isMemberNSPropertyContractBase(value) {
32701
+ const v = asMemberBaseForKind(value, 10 /* NSProperty */);
32702
+ if (v === null) return false;
32703
+ if (v.isAbstract !== true) return false;
32704
+ if (v.code !== void 0) return false;
32705
+ if (v.setterCode !== void 0) return false;
32706
+ return isNSTypeInfo(v.returnTypeInfo);
32707
+ }
32501
32708
  function isMemberNSProperty(value) {
32502
32709
  if (!isMemberNSPropertyBase(value)) return false;
32503
32710
  if (!isWithId(value)) return false;
@@ -32696,6 +32903,11 @@ function memberKindSupportsStorage(kind) {
32696
32903
  if (kind === 23 /* NSFunction */) return false;
32697
32904
  return kind !== 13 /* Function */;
32698
32905
  }
32906
+ function memberKindOwnsStoredValue(kind) {
32907
+ if (kind === 10 /* NSProperty */) return false;
32908
+ if (kind === 23 /* NSFunction */) return false;
32909
+ return kind !== 13 /* Function */;
32910
+ }
32699
32911
  function hasValidStorageField(v) {
32700
32912
  if (v.storage === void 0) return true;
32701
32913
  if (v.storage === null) return true;
@@ -32712,7 +32924,7 @@ function isMemberBase(value) {
32712
32924
  if (partial !== void 0 && v?.kind !== 7 /* Class */ && v?.kind !== 21 /* Generic */) {
32713
32925
  return false;
32714
32926
  }
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);
32927
+ 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
32928
  if (!matchesConcreteType) return false;
32717
32929
  if (!isValidDocsText(v?.docsText)) return false;
32718
32930
  if (typeof v?.name !== "string") return false;
@@ -33167,11 +33379,10 @@ var init_inheritance = __esm({
33167
33379
  });
33168
33380
 
33169
33381
  // ../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;
33382
+ 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
33383
  var init_world_system_classes_generated = __esm({
33172
33384
  "../src/models/classes/world-system-classes.generated.ts"() {
33173
33385
  "use strict";
33174
- init_member_storage();
33175
33386
  WORLD_GRID_CHILDREN_MEMBER_ID = "system_98578ba3-a70e-4397-9283-996a898d44c8";
33176
33387
  WORLD_GRID_CHILDREN_ENTRY_MEMBER_ID = "system_c86da069-7ef9-5693-a54b-5c331755fde9";
33177
33388
  WORLD_OBJECT_CHILDREN_MEMBER_ID = "system_bb5d2cf1-a0dd-4eba-a62e-0e1bf0177071";
@@ -33190,16 +33401,25 @@ var init_world_system_classes_generated = __esm({
33190
33401
  WORLD_ANIMATION_CLIP_DURATION_MEMBER_ID = "system_e1a1ba86-2599-43dc-a19a-46d08a2e4256";
33191
33402
  WORLD_ANIMATION_CLIP_FRAMES_MEMBER_ID = "system_485b0f05-ec7b-4e61-ba80-9a5d7d262b39";
33192
33403
  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";
33404
+ WORLD_ANIMATION_FRAME_BASE_INDEX_MEMBER_ID = "system_0dbdd0f9-9dd2-4b4b-9403-50180c1500dd";
33194
33405
  WORLD_ANIMATION_FRAME_OVERRIDES_MEMBER_ID = "system_8ab7bb00-a475-43f0-8579-1b08f133d658";
33195
33406
  WORLD_ANIMATION_FRAME_CHILD_OVERRIDES_MEMBER_ID = "system_a5905750-c472-46a8-89e9-f04f0bf66696";
33196
33407
  WORLD_ANIMATION_FRAME_CHILD_OVERRIDES_ENTRY_MEMBER_ID = "system_86688965-6b26-529e-95f3-a4070f022582";
33197
33408
  WORLD_ANIMATION_FRAME_ACTIONS_MEMBER_ID = "system_23e05410-6428-4bd1-b085-c0390ed7fcb7";
33198
33409
  WORLD_ANIMATION_CHILD_OVERRIDE_CHILD_MEMBER_ID = "system_6e15db41-a6f2-43fd-9793-c52fe4f3d0f8";
33199
33410
  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";
33411
+ WORLD_ANIMATION_TRACK_CHILD_MEMBER_ID = "system_29abc85e-2d38-4cef-a200-88915d176d06";
33412
+ WORLD_ANIMATION_TRACK_START_FRAME_MEMBER_ID = "system_5af60692-74a0-45ea-a9ba-747854989b2a";
33413
+ WORLD_ANIMATION_TRACK_DIRECTION_MEMBER_ID = "system_75a314b5-c799-4af6-9e64-4cde6eae1b30";
33414
+ WORLD_ANIMATION_TRACK_OFFSET_START_INDEX_MEMBER_ID = "system_c71caaab-df72-48c6-a291-2772288351eb";
33415
+ WORLD_ANIMATION_TRACK_OFFSET_END_INDEX_MEMBER_ID = "system_6755f539-a459-46ec-b44a-cc74a18ad83e";
33201
33416
  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";
33417
+ WORLD_ANIMATION_SEGMENT_DURATION_MEMBER_ID = "system_8a159870-36a4-40e4-98bf-15919410650a";
33418
+ WORLD_ANIMATION_SEGMENT_FRAMES_MEMBER_ID = "system_40658ad2-b4a5-4404-bb6d-ed778088a772";
33419
+ WORLD_ANIMATION_SEGMENT_TRACK_CHILD_PARAM_ID = "system_d4fe9f71-d9d9-4baf-bc52-8de16933b655";
33420
+ WORLD_ANIMATION_SEGMENT_TRACK_VALUE_PARAM_ID = "system_03497842-3381-45a2-b2fa-b2dee36c2759";
33421
+ WORLD_PLAY_DIRECTION_FORWARD_OPTION_ID = "system_2e4ca40e-f305-49c6-a91b-b99d56239ba0";
33422
+ WORLD_PLAY_DIRECTION_REVERSE_OPTION_ID = "system_6478d195-3905-48db-befe-d276eb5478f0";
33203
33423
  WORLD_SYSTEM_CLASS_DEFINITIONS = [
33204
33424
  {
33205
33425
  classId: "system_b78f7f1d-f63c-4abb-ae65-852e69246534",
@@ -33784,11 +34004,27 @@ var init_world_system_classes_generated = __esm({
33784
34004
  ],
33785
34005
  worldKind: "smartTileNeighbor"
33786
34006
  },
34007
+ {
34008
+ classId: "system_6529416a-f68b-49a3-b3d2-2af8fda60cb4",
34009
+ name: "NeoAnimationFrameBase",
34010
+ 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.",
34011
+ isAbstract: true,
34012
+ schemaFields: [
34013
+ {
34014
+ memberId: "system_0dbdd0f9-9dd2-4b4b-9403-50180c1500dd",
34015
+ memberKind: "int",
34016
+ minValue: 0,
34017
+ required: true,
34018
+ schemaKey: "Index"
34019
+ }
34020
+ ],
34021
+ worldKind: "animationFrameBase"
34022
+ },
33787
34023
  {
33788
34024
  classId: "system_994b3886-7392-4bd2-8f9d-d5f7fd316a5a",
33789
34025
  name: "NeoAnimationClip",
34026
+ 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
34027
  isAbstract: false,
33791
- allowedStorage: "immutable" /* Immutable */,
33792
34028
  genericParams: [
33793
34029
  {
33794
34030
  id: "system_268e9d23-347b-4eb6-8bec-cf0edf326aa2",
@@ -33848,7 +34084,7 @@ var init_world_system_classes_generated = __esm({
33848
34084
  memberId: "system_f89776c0-a7ac-5e80-9fcb-df9a5a1033fc",
33849
34085
  memberKind: "class",
33850
34086
  defaultValue: {},
33851
- schemaClassWorldKind: "animationChildTrack",
34087
+ schemaClassWorldKind: "animationTrack",
33852
34088
  required: true,
33853
34089
  schemaKey: "__TrackEntry"
33854
34090
  }
@@ -33858,8 +34094,9 @@ var init_world_system_classes_generated = __esm({
33858
34094
  {
33859
34095
  classId: "system_ba921c72-e4b2-47f9-861f-f30ac1156965",
33860
34096
  name: "NeoAnimationFrame",
34097
+ 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.",
34098
+ extendsWorldKind: "animationFrameBase",
33861
34099
  isAbstract: false,
33862
- allowedStorage: "immutable" /* Immutable */,
33863
34100
  genericParams: [
33864
34101
  {
33865
34102
  id: "system_d2194ea1-08ec-4a0c-b9a0-97877031d39b",
@@ -33868,13 +34105,6 @@ var init_world_system_classes_generated = __esm({
33868
34105
  }
33869
34106
  ],
33870
34107
  schemaFields: [
33871
- {
33872
- memberId: "system_0dbdd0f9-9dd2-4b4b-9403-50180c1500dd",
33873
- memberKind: "int",
33874
- minValue: 0,
33875
- required: true,
33876
- schemaKey: "Index"
33877
- },
33878
34108
  {
33879
34109
  memberId: "system_8ab7bb00-a475-43f0-8579-1b08f133d658",
33880
34110
  memberKind: "generic",
@@ -33930,11 +34160,96 @@ var init_world_system_classes_generated = __esm({
33930
34160
  ],
33931
34161
  worldKind: "animationFrame"
33932
34162
  },
34163
+ {
34164
+ classId: "system_9c4f3bfb-f0d8-4231-a7e7-9115bab8d5ab",
34165
+ name: "NeoAnimationSegmentFrame",
34166
+ 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.",
34167
+ extendsWorldKind: "animationFrameBase",
34168
+ isAbstract: false,
34169
+ genericParams: [
34170
+ { id: "system_887b11d3-e56a-47ff-8d01-e2fcefb846a1", name: "T" }
34171
+ ],
34172
+ schemaFields: [
34173
+ {
34174
+ memberId: "system_b71d1f73-da72-49de-ab9a-911cc9bffa43",
34175
+ memberKind: "generic",
34176
+ genericParamId: "system_887b11d3-e56a-47ff-8d01-e2fcefb846a1",
34177
+ required: false,
34178
+ schemaKey: "Value"
34179
+ }
34180
+ ],
34181
+ worldKind: "animationSegmentFrame"
34182
+ },
34183
+ {
34184
+ classId: "system_2866011d-30fe-410a-8a04-d2ff291b77cd",
34185
+ name: "NeoAnimationSegment",
34186
+ 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.",
34187
+ isAbstract: true,
34188
+ genericParams: [
34189
+ { id: "system_a35d028e-bd71-49aa-9f9d-944b6e813b80", name: "T" }
34190
+ ],
34191
+ schemaFields: [
34192
+ {
34193
+ memberId: "system_8a159870-36a4-40e4-98bf-15919410650a",
34194
+ memberKind: "int",
34195
+ defaultValue: 1,
34196
+ minValue: 1,
34197
+ required: true,
34198
+ schemaKey: "Duration"
34199
+ },
34200
+ {
34201
+ memberId: "system_40658ad2-b4a5-4404-bb6d-ed778088a772",
34202
+ memberKind: "list",
34203
+ defaultValue: [],
34204
+ entryMemberId: "system_da56f93b-d3a6-5feb-9f51-3a9fd029272a",
34205
+ required: true,
34206
+ schemaKey: "Frames"
34207
+ }
34208
+ ],
34209
+ supportingFields: [
34210
+ {
34211
+ memberId: "system_da56f93b-d3a6-5feb-9f51-3a9fd029272a",
34212
+ memberKind: "class",
34213
+ schemaClassWorldKind: "animationSegmentFrame",
34214
+ classArguments: {
34215
+ "system_887b11d3-e56a-47ff-8d01-e2fcefb846a1": {
34216
+ kind: "generic",
34217
+ genericParamId: "system_a35d028e-bd71-49aa-9f9d-944b6e813b80"
34218
+ }
34219
+ },
34220
+ required: true,
34221
+ schemaKey: "__FrameEntry"
34222
+ }
34223
+ ],
34224
+ worldKind: "animationSegment"
34225
+ },
34226
+ {
34227
+ classId: "system_ffc766b3-f3ac-4c20-91cf-38ff7e8e88f3",
34228
+ name: "NeoSpriteAnimationSegment",
34229
+ 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.",
34230
+ extendsWorldKind: "animationSegment",
34231
+ isAbstract: false,
34232
+ extendsGenericBindings: {
34233
+ "system_a35d028e-bd71-49aa-9f9d-944b6e813b80": {
34234
+ kind: "member",
34235
+ memberId: "system_f10bc0fd-9b8e-5901-9f4b-38e7cdc7a241"
34236
+ }
34237
+ },
34238
+ supportingFields: [
34239
+ {
34240
+ memberId: "system_f10bc0fd-9b8e-5901-9f4b-38e7cdc7a241",
34241
+ memberKind: "sprite",
34242
+ required: true,
34243
+ schemaKey: "__SpriteInfoTypeArgument"
34244
+ }
34245
+ ],
34246
+ worldKind: "spriteAnimationSegment"
34247
+ },
33933
34248
  {
33934
34249
  classId: "system_e2e88eba-5335-4a16-9dcf-2c0e1951e8bd",
33935
34250
  name: "NeoAnimationChildOverride",
34251
+ docsText: "What one child looks like while a clip frame holds: which child, and the\nvalues to override on it.",
33936
34252
  isAbstract: false,
33937
- allowedStorage: "immutable" /* Immutable */,
33938
34253
  constructorProjections: [
33939
34254
  {
33940
34255
  memberId: "system_6e15db41-a6f2-43fd-9793-c52fe4f3d0f8",
@@ -33969,11 +34284,76 @@ var init_world_system_classes_generated = __esm({
33969
34284
  ],
33970
34285
  worldKind: "animationChildOverride"
33971
34286
  },
34287
+ {
34288
+ classId: "system_45755778-ecfe-4a6c-b65f-a08017a7fb56",
34289
+ name: "NeoAnimationTrackBase",
34290
+ 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.",
34291
+ isAbstract: true,
34292
+ schemaFields: [
34293
+ {
34294
+ memberId: "system_29abc85e-2d38-4cef-a200-88915d176d06",
34295
+ memberKind: "lookup",
34296
+ collectionMemberId: "system_bb5d2cf1-a0dd-4eba-a62e-0e1bf0177071",
34297
+ collectionValueId: null,
34298
+ multiselect: false,
34299
+ required: true,
34300
+ schemaKey: "Child"
34301
+ },
34302
+ {
34303
+ memberId: "system_5af60692-74a0-45ea-a9ba-747854989b2a",
34304
+ memberKind: "int",
34305
+ defaultValue: 0,
34306
+ minValue: 0,
34307
+ required: true,
34308
+ schemaKey: "StartFrame"
34309
+ },
34310
+ {
34311
+ memberId: "system_75a314b5-c799-4af6-9e64-4cde6eae1b30",
34312
+ memberKind: "enum",
34313
+ defaultValue: ["system_2e4ca40e-f305-49c6-a91b-b99d56239ba0"],
34314
+ docsText: "Which way the scheduled content plays: forward, or last frame first. This is playback order, not a direction in the world.",
34315
+ enumId: "system_705ccc39-e46e-4c9f-af3e-3ec8fd818709",
34316
+ multiselect: false,
34317
+ required: true,
34318
+ schemaKey: "Direction"
34319
+ },
34320
+ {
34321
+ memberId: "system_c71caaab-df72-48c6-a291-2772288351eb",
34322
+ memberKind: "int",
34323
+ defaultValue: 0,
34324
+ 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.",
34325
+ minValue: 0,
34326
+ required: false,
34327
+ schemaKey: "OffsetStartIndex"
34328
+ },
34329
+ {
34330
+ memberId: "system_6755f539-a459-46ec-b44a-cc74a18ad83e",
34331
+ memberKind: "int",
34332
+ defaultValue: null,
34333
+ 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.",
34334
+ minValue: 1,
34335
+ required: false,
34336
+ schemaKey: "OffsetEndIndex"
34337
+ },
34338
+ {
34339
+ memberId: "system_626f7a7a-66b7-4e54-aaaa-7174a74c9467",
34340
+ memberKind: "computed",
34341
+ accessModifierKind: "protected",
34342
+ isAbstract: true,
34343
+ 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.",
34344
+ returnTypeInfo: { type: 2, required: true },
34345
+ required: true,
34346
+ schemaKey: "BaseDuration"
34347
+ }
34348
+ ],
34349
+ worldKind: "animationTrack"
34350
+ },
33972
34351
  {
33973
34352
  classId: "system_8dc78ecf-15b8-4b8a-86f3-7691a5b487d0",
33974
34353
  name: "NeoAnimationChildTrack",
34354
+ 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.",
34355
+ extendsWorldKind: "animationTrack",
33975
34356
  isAbstract: false,
33976
- allowedStorage: "immutable" /* Immutable */,
33977
34357
  constructorProjections: [
33978
34358
  {
33979
34359
  memberId: "system_29abc85e-2d38-4cef-a200-88915d176d06",
@@ -33982,31 +34362,110 @@ var init_world_system_classes_generated = __esm({
33982
34362
  ],
33983
34363
  schemaFields: [
33984
34364
  {
33985
- memberId: "system_29abc85e-2d38-4cef-a200-88915d176d06",
34365
+ memberId: "system_2c816624-0281-46d3-9ed5-d20bd43519f4",
34366
+ memberKind: "string",
34367
+ localizable: false,
34368
+ required: true,
34369
+ schemaKey: "ClipKey"
34370
+ }
34371
+ ],
34372
+ worldKind: "animationChildTrack"
34373
+ },
34374
+ {
34375
+ classId: "system_fd551dd8-6e6b-4044-9f32-7445ab344200",
34376
+ name: "NeoAnimationSegmentTrack",
34377
+ 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.",
34378
+ extendsWorldKind: "animationTrack",
34379
+ isAbstract: true,
34380
+ constructorProjections: [
34381
+ {
34382
+ memberId: "system_87900a74-0d7c-4dfc-a6f6-3bd204ebb8f6",
34383
+ parameterName: "id"
34384
+ }
34385
+ ],
34386
+ genericParams: [
34387
+ {
34388
+ id: "system_d4fe9f71-d9d9-4baf-bc52-8de16933b655",
34389
+ name: "TChild",
34390
+ constraintClassWorldKind: "objectBase"
34391
+ },
34392
+ { id: "system_03497842-3381-45a2-b2fa-b2dee36c2759", name: "TValue" }
34393
+ ],
34394
+ schemaFields: [
34395
+ {
34396
+ memberId: "system_87900a74-0d7c-4dfc-a6f6-3bd204ebb8f6",
33986
34397
  memberKind: "lookup",
34398
+ extendsMemberId: "system_29abc85e-2d38-4cef-a200-88915d176d06",
34399
+ 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
34400
  collectionMemberId: "system_bb5d2cf1-a0dd-4eba-a62e-0e1bf0177071",
33988
34401
  collectionValueId: null,
33989
34402
  multiselect: false,
34403
+ declaredTypeInfo: {
34404
+ type: 21,
34405
+ required: true,
34406
+ ownerClassId: "system_fd551dd8-6e6b-4044-9f32-7445ab344200",
34407
+ genericParamId: "system_d4fe9f71-d9d9-4baf-bc52-8de16933b655"
34408
+ },
33990
34409
  required: true,
33991
34410
  schemaKey: "Child"
33992
34411
  },
33993
34412
  {
33994
- memberId: "system_2c816624-0281-46d3-9ed5-d20bd43519f4",
33995
- memberKind: "string",
33996
- localizable: false,
34413
+ memberId: "system_d2d0bf9b-211f-4b2f-bff3-3d6da5766abd",
34414
+ memberKind: "computed",
34415
+ isAbstract: true,
34416
+ 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.",
34417
+ returnTypeInfo: {
34418
+ type: 7,
34419
+ required: true,
34420
+ classId: "system_2866011d-30fe-410a-8a04-d2ff291b77cd",
34421
+ typeArguments: {
34422
+ "system_a35d028e-bd71-49aa-9f9d-944b6e813b80": {
34423
+ type: 21,
34424
+ required: true,
34425
+ ownerClassId: "system_fd551dd8-6e6b-4044-9f32-7445ab344200",
34426
+ genericParamId: "system_03497842-3381-45a2-b2fa-b2dee36c2759"
34427
+ }
34428
+ }
34429
+ },
33997
34430
  required: true,
33998
- schemaKey: "ClipKey"
34431
+ schemaKey: "Segment"
34432
+ }
34433
+ ],
34434
+ worldKind: "animationSegmentTrack"
34435
+ },
34436
+ {
34437
+ classId: "system_24b70585-0798-49ad-b650-4c118bac1eb1",
34438
+ name: "NeoSpriteAnimationSegmentTrack",
34439
+ 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.",
34440
+ extendsWorldKind: "animationSegmentTrack",
34441
+ isAbstract: true,
34442
+ extendsGenericBindings: {
34443
+ "system_d4fe9f71-d9d9-4baf-bc52-8de16933b655": {
34444
+ kind: "generic",
34445
+ genericParamId: "system_4de90637-6a3b-4bba-95ef-2ff9dfb05268"
33999
34446
  },
34447
+ "system_03497842-3381-45a2-b2fa-b2dee36c2759": {
34448
+ kind: "member",
34449
+ memberId: "system_65388373-2b28-53f4-b821-8c74dada7c8c"
34450
+ }
34451
+ },
34452
+ genericParams: [
34000
34453
  {
34001
- memberId: "system_5af60692-74a0-45ea-a9ba-747854989b2a",
34002
- memberKind: "int",
34003
- defaultValue: 0,
34004
- minValue: 0,
34454
+ id: "system_4de90637-6a3b-4bba-95ef-2ff9dfb05268",
34455
+ name: "TChild",
34456
+ constraintClassWorldKind: "spriteObject"
34457
+ }
34458
+ ],
34459
+ supportingFields: [
34460
+ {
34461
+ memberId: "system_65388373-2b28-53f4-b821-8c74dada7c8c",
34462
+ memberKind: "sprite",
34005
34463
  required: true,
34006
- schemaKey: "StartFrame"
34464
+ schemaKey: "__SpriteInfoTypeArgument"
34007
34465
  }
34008
34466
  ],
34009
- worldKind: "animationChildTrack"
34467
+ targetMemberId: "system_e9288ba9-f5a2-4485-8443-6afb155b31e0",
34468
+ worldKind: "spriteAnimationSegmentTrack"
34010
34469
  }
34011
34470
  ];
34012
34471
  }
@@ -34022,7 +34481,7 @@ function worldAnimationChildOverrideBindingMemberId(classId) {
34022
34481
  )
34023
34482
  );
34024
34483
  }
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;
34484
+ 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
34485
  var init_world_system_classes = __esm({
34027
34486
  "../src/models/classes/world-system-classes.ts"() {
34028
34487
  "use strict";
@@ -34034,12 +34493,19 @@ var init_world_system_classes = __esm({
34034
34493
  init_world_system_classes_generated();
34035
34494
  WORLD_ANIMATION_CHILD_OVERRIDE_BINDING_MEMBER_NAME = "NeoAnimationChildBinding";
34036
34495
  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
- ]);
34496
+ WORLD_SYSTEM_ANIMATION_KIND_MARKER = "animation";
34497
+ WORLD_SYSTEM_ANIMATION_KINDS = new Set(
34498
+ WORLD_SYSTEM_CLASS_DEFINITIONS.map(
34499
+ (definition2) => definition2.worldKind
34500
+ ).filter(
34501
+ (worldKind) => worldKind.toLowerCase().includes(WORLD_SYSTEM_ANIMATION_KIND_MARKER)
34502
+ )
34503
+ );
34504
+ GENERIC_WORLD_SYSTEM_CLASS_KINDS = new Set(
34505
+ WORLD_SYSTEM_CLASS_DEFINITIONS.filter(
34506
+ (definition2) => (definition2.genericParams?.length ?? 0) > 0
34507
+ ).map((definition2) => definition2.worldKind)
34508
+ );
34043
34509
  WORLD_SYSTEM_ALL_KINDS = new Set(
34044
34510
  WORLD_SYSTEM_CLASS_DEFINITIONS.map((definition2) => definition2.worldKind)
34045
34511
  );
@@ -35990,10 +36456,7 @@ function buildDefaultMemberValue(args) {
35990
36456
  for (const entry of member.partial === true ? [] : merged) {
35991
36457
  if (entry.memberId === null) continue;
35992
36458
  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
- }
36459
+ if (!memberKindOwnsStoredValue(childMember.kind)) continue;
35997
36460
  const resolvedChildMember = substituteMember(
35998
36461
  childMember,
35999
36462
  childEnv,
@@ -37582,6 +38045,57 @@ var init_structured_leaf_source = __esm({
37582
38045
  }
37583
38046
  });
37584
38047
 
38048
+ // src/project-source/member-kind-type-names.ts
38049
+ function fixedSourceTypeNameForKind(kind) {
38050
+ return MEMBER_KIND_SOURCE_TYPE_NAMES[kind] ?? null;
38051
+ }
38052
+ function fixedSourceTypeNameForKindNumber(kind) {
38053
+ return SOURCE_TYPE_NAME_BY_MEMBER_KIND_NUMBER.get(kind) ?? null;
38054
+ }
38055
+ var MEMBER_KIND_SOURCE_TYPE_NAMES, MEMBER_KIND_NUMBERS, SOURCE_TYPE_NAME_BY_MEMBER_KIND_NUMBER;
38056
+ var init_member_kind_type_names = __esm({
38057
+ "src/project-source/member-kind-type-names.ts"() {
38058
+ "use strict";
38059
+ init_members();
38060
+ MEMBER_KIND_SOURCE_TYPE_NAMES = {
38061
+ null: "null",
38062
+ bool: "bool",
38063
+ int: "int",
38064
+ string: "string",
38065
+ float: "float",
38066
+ decimal: "decimal",
38067
+ sprite: "SpriteInfo",
38068
+ audio: "AudioClipInfo",
38069
+ vector2: "Vector2",
38070
+ vector2Int: "Vector2Int",
38071
+ vector3: "Vector3",
38072
+ vector3Int: "Vector3Int",
38073
+ color: "Color"
38074
+ };
38075
+ MEMBER_KIND_NUMBERS = {
38076
+ null: 0 /* Null */,
38077
+ bool: 1 /* Bool */,
38078
+ int: 2 /* Int */,
38079
+ string: 3 /* String */,
38080
+ float: 4 /* Float */,
38081
+ decimal: 20 /* Decimal */,
38082
+ sprite: 11 /* Sprite */,
38083
+ audio: 12 /* Audio */,
38084
+ vector2: 14 /* Vector2 */,
38085
+ vector2Int: 15 /* Vector2Int */,
38086
+ vector3: 16 /* Vector3 */,
38087
+ vector3Int: 17 /* Vector3Int */,
38088
+ color: 19 /* Color */
38089
+ };
38090
+ SOURCE_TYPE_NAME_BY_MEMBER_KIND_NUMBER = new Map(
38091
+ Object.entries(MEMBER_KIND_NUMBERS).map(([slug, kind]) => [
38092
+ kind,
38093
+ MEMBER_KIND_SOURCE_TYPE_NAMES[slug]
38094
+ ])
38095
+ );
38096
+ }
38097
+ });
38098
+
37585
38099
  // src/project-source/lower-members.ts
37586
38100
  function lowerClass(context, declaration) {
37587
38101
  const id2 = materializedId(declaration, "class", declaration.name);
@@ -37626,6 +38140,7 @@ function lowerClass(context, declaration) {
37626
38140
  context.loweredMembers.set(memberId, existing ?? lowered);
37627
38141
  }
37628
38142
  const storage = annotation(declaration.annotations, "storage");
38143
+ const targetMemberId = lowerClassTargetMemberId(context, declaration, base);
37629
38144
  const genericParameters = declaration.genericParameters.map((parameter3) => {
37630
38145
  const parameterId = materializedIdentityId(
37631
38146
  parameter3.identity,
@@ -37657,6 +38172,7 @@ function lowerClass(context, declaration) {
37657
38172
  // Class storage keys never exist in source. Retain only the historical
37658
38173
  // null representation while the clean-break record migration completes.
37659
38174
  allowedStorageKeys: null,
38175
+ targetMemberId,
37660
38176
  genericParameters,
37661
38177
  extendsGenericBindings: extendsClassId && declaration.baseTypes[0]?.arguments.length ? lowerExtendsBindings(
37662
38178
  context,
@@ -37997,12 +38513,25 @@ function lowerFieldMember(context, ownerClass, declaration, id2, commonInput, ba
37997
38513
  `Lookup member ${ownerClass.name}.${declaration.name} has an unresolved collection.`
37998
38514
  );
37999
38515
  }
38516
+ const multiselect = booleanArgument(settings, "multiselect") ?? false;
38000
38517
  return {
38001
38518
  ...common,
38002
38519
  kind: "lookup",
38003
38520
  collectionMemberId,
38004
38521
  collectionValueId: referenceIdArgument(settings, "collectionValue"),
38005
- multiselect: booleanArgument(settings, "multiselect") ?? false
38522
+ multiselect,
38523
+ // The spelled type, kept only when it narrows the collection entry —
38524
+ // `normalizeLookupDeclaredTypes` clears the redundant ones once every
38525
+ // member is lowered and the entry can be resolved in either direction.
38526
+ declaredType: lookupDeclaredType(
38527
+ context,
38528
+ ownerClass,
38529
+ // A multiselect lookup is spelled `List<T>` and stores the element
38530
+ // type, which is what the emitter re-wraps. Keyed off the spelling
38531
+ // rather than off `multiselect` so a mismatched pair reads the type it
38532
+ // actually has instead of throwing for a missing type argument.
38533
+ fieldType.name === "List" ? requiredTypeArgument(fieldType, 0) : fieldType
38534
+ )
38006
38535
  };
38007
38536
  }
38008
38537
  if (name === "List") {
@@ -38323,6 +38852,44 @@ function lowerEnum(context, declaration) {
38323
38852
  system: systemMetadata(declaration.annotations) ?? base?.system ?? null
38324
38853
  };
38325
38854
  }
38855
+ function lookupDeclaredType(context, ownerClass, type) {
38856
+ if (type.name === "object") return null;
38857
+ const lowered = lowerType(context, type, ownerClass);
38858
+ if (lowered.kind === "class" || lowered.kind === "generic" || lowered.kind === "interface") {
38859
+ return lowered;
38860
+ }
38861
+ return null;
38862
+ }
38863
+ function normalizeLookupDeclaredTypes(context) {
38864
+ for (const [memberId, member] of context.loweredMembers) {
38865
+ if (member.kind !== "lookup") continue;
38866
+ if (member.declaredType === null) continue;
38867
+ if (!lookupDeclaredTypeNarrows(context, member)) {
38868
+ context.loweredMembers.set(memberId, { ...member, declaredType: null });
38869
+ }
38870
+ }
38871
+ }
38872
+ function lookupDeclaredTypeNarrows(context, member) {
38873
+ const declared = member.declaredType;
38874
+ if (declared === null) return false;
38875
+ const entry = lookupEntryMember(context, member.collectionMemberId);
38876
+ if (entry === void 0) return false;
38877
+ if (entry.kind === "class") {
38878
+ return declared.kind !== "class" || declared.classId !== entry.classId;
38879
+ }
38880
+ if (entry.kind === "generic") {
38881
+ return declared.kind !== "generic" || declared.genericParamId !== entry.genericParamId;
38882
+ }
38883
+ return true;
38884
+ }
38885
+ function lookupEntryMember(context, collectionMemberId) {
38886
+ const collection = context.loweredMembers.get(collectionMemberId) ?? context.baseMembers.get(collectionMemberId);
38887
+ if (collection === void 0) return void 0;
38888
+ if (collection.kind !== "list" && collection.kind !== "dictionary") {
38889
+ return void 0;
38890
+ }
38891
+ return context.loweredMembers.get(collection.entryMemberId) ?? context.baseMembers.get(collection.entryMemberId);
38892
+ }
38326
38893
  function lowerType(context, type, ownerClass) {
38327
38894
  const nullable = type.nullable;
38328
38895
  const primitive3 = primitiveTypeKind(type.name);
@@ -38412,14 +38979,56 @@ function lowerDefault(context, initializer, type, ownerClass, base, memberId, st
38412
38979
  classId: baseDefault && !("serverValueId" in baseDefault) && !("init" in baseDefault) ? baseDefault.classId : null
38413
38980
  };
38414
38981
  }
38415
- function lowerExpressionValue(context, expression, expected, ownerClass, path) {
38982
+ function substituteGenericTypeNames(type, environment) {
38983
+ if (environment.size === 0) return type;
38984
+ const bound = environment.get(type.name);
38985
+ if (bound !== void 0 && type.arguments.length === 0) {
38986
+ return type.nullable && !bound.nullable ? { ...bound, nullable: true } : bound;
38987
+ }
38988
+ if (type.arguments.length === 0) return type;
38989
+ return {
38990
+ ...type,
38991
+ arguments: type.arguments.map(
38992
+ (argument2) => substituteGenericTypeNames(argument2, environment)
38993
+ )
38994
+ };
38995
+ }
38996
+ function constructedGenericEnvironment(context, classId, expression, expectedType, outer) {
38997
+ const parameters = context.genericParametersByClass.get(classId);
38998
+ if (parameters === void 0 || parameters.size === 0) return outer;
38999
+ const authored = expression.className !== null && expression.className !== "Partial" ? expression.typeArguments : void 0;
39000
+ const environment = new Map(outer);
39001
+ [...parameters.keys()].forEach((parameterName, index) => {
39002
+ const authoredArgument = authored?.[index];
39003
+ const argument2 = authoredArgument !== void 0 ? manifestTypeFromAstType(authoredArgument) : expectedType.arguments[index];
39004
+ if (argument2 === void 0) return;
39005
+ environment.set(parameterName, substituteGenericTypeNames(argument2, outer));
39006
+ });
39007
+ return environment;
39008
+ }
39009
+ function manifestTypeFromAstType(type) {
39010
+ if (type.kind !== "named") {
39011
+ return { name: "object", nullable: false, arguments: [] };
39012
+ }
39013
+ return {
39014
+ name: type.name,
39015
+ nullable: false,
39016
+ arguments: (type.typeArguments ?? []).map(manifestTypeFromAstType)
39017
+ };
39018
+ }
39019
+ function lowerExpressionValue(context, expression, declaredExpected, ownerClass, path, genericEnvironment = EMPTY_GENERIC_TYPE_ENVIRONMENT) {
39020
+ const expected = substituteGenericTypeNames(
39021
+ declaredExpected,
39022
+ genericEnvironment
39023
+ );
38416
39024
  if (expression.kind === "annotated") {
38417
39025
  return lowerExpressionValue(
38418
39026
  context,
38419
39027
  expression.expression,
38420
39028
  expected,
38421
39029
  ownerClass,
38422
- path
39030
+ path,
39031
+ genericEnvironment
38423
39032
  );
38424
39033
  }
38425
39034
  switch (expression.kind) {
@@ -38443,7 +39052,8 @@ function lowerExpressionValue(context, expression, expected, ownerClass, path) {
38443
39052
  expression.operand,
38444
39053
  expected,
38445
39054
  ownerClass,
38446
- path
39055
+ path,
39056
+ genericEnvironment
38447
39057
  );
38448
39058
  if (typeof operand === "number") return -operand;
38449
39059
  if (expected.name === "decimal" && typeof operand === "string") {
@@ -38472,7 +39082,8 @@ function lowerExpressionValue(context, expression, expected, ownerClass, path) {
38472
39082
  element,
38473
39083
  entry,
38474
39084
  ownerClass,
38475
- `${path}[${index}]`
39085
+ `${path}[${index}]`,
39086
+ genericEnvironment
38476
39087
  )
38477
39088
  );
38478
39089
  }
@@ -38485,7 +39096,8 @@ function lowerExpressionValue(context, expression, expected, ownerClass, path) {
38485
39096
  entry.key,
38486
39097
  { name: "string", nullable: false, arguments: [] },
38487
39098
  ownerClass,
38488
- path
39099
+ path,
39100
+ genericEnvironment
38489
39101
  );
38490
39102
  if (typeof key !== "string")
38491
39103
  throw new Error("Dictionary keys must be strings.");
@@ -38494,7 +39106,8 @@ function lowerExpressionValue(context, expression, expected, ownerClass, path) {
38494
39106
  entry.value,
38495
39107
  valueType,
38496
39108
  ownerClass,
38497
- `${path}[${JSON.stringify(key)}]`
39109
+ `${path}[${JSON.stringify(key)}]`,
39110
+ genericEnvironment
38498
39111
  );
38499
39112
  }
38500
39113
  return value;
@@ -38535,31 +39148,35 @@ function lowerExpressionValue(context, expression, expected, ownerClass, path) {
38535
39148
  }
38536
39149
  if (expression.className === null && expression.initializer !== void 0 && expression.hasArgumentList !== true && !partialExpected) {
38537
39150
  throw new Error(
38538
- "Inferred `new { ... }` construction is only valid in a Partial position."
39151
+ `Inferred new { ... } construction is only valid in a Partial position (${path}).`
38539
39152
  );
38540
39153
  }
38541
39154
  const className = explicitPartial ? expectedType.name : expression.className ?? expectedType.name;
38542
39155
  const classId = requiredName(context.classIdsByName, className, "class");
38543
- const target = context.baseClasses.get(classId);
38544
- const sourceTarget = context.sourceClasses.get(classId);
39156
+ const innerEnvironment = constructedGenericEnvironment(
39157
+ context,
39158
+ classId,
39159
+ expression,
39160
+ expectedType,
39161
+ genericEnvironment
39162
+ );
38545
39163
  const result = {};
38546
39164
  for (const assignment of expression.initializer ?? []) {
38547
- const sourceMember = sourceTarget?.members.find(
38548
- (candidate) => candidate.name === assignment.name
39165
+ const memberId = effectiveMemberIdByName(
39166
+ context,
39167
+ classId,
39168
+ assignment.name
38549
39169
  );
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;
39170
+ const member = memberId !== null ? context.loweredMembers.get(memberId) ?? context.baseMembers.get(memberId) : void 0;
39171
+ const sourceMember = memberId !== null ? context.sourceMembersById.get(memberId)?.member : void 0;
38556
39172
  const declaredType = sourceMember?.type ?? (member ? manifestTypeForMember(context, member) : { name: "object", nullable: true, arguments: [] });
38557
39173
  result[assignment.name] = lowerExpressionValue(
38558
39174
  context,
38559
39175
  assignment.value,
38560
39176
  structuredLeafAssignmentType(partialExpected, declaredType),
38561
39177
  ownerClass,
38562
- `${path}.${assignment.name}`
39178
+ `${path}.${assignment.name}`,
39179
+ innerEnvironment
38563
39180
  );
38564
39181
  }
38565
39182
  return result;
@@ -38877,7 +39494,11 @@ function inheritedMemberId(context, ownerClass, name) {
38877
39494
  const directBase = ownerClass.baseTypes.find(
38878
39495
  (entry) => context.classIdsByName.has(entry.name)
38879
39496
  );
38880
- let classId = directBase ? requiredName(context.classIdsByName, directBase.name, "class") : null;
39497
+ const classId = directBase ? requiredName(context.classIdsByName, directBase.name, "class") : null;
39498
+ return effectiveMemberIdByName(context, classId, name);
39499
+ }
39500
+ function effectiveMemberIdByName(context, startClassId, name) {
39501
+ let classId = startClassId;
38881
39502
  const visited = /* @__PURE__ */ new Set();
38882
39503
  while (classId !== null && !visited.has(classId)) {
38883
39504
  visited.add(classId);
@@ -38907,6 +39528,20 @@ function inheritedMemberId(context, ownerClass, name) {
38907
39528
  }
38908
39529
  return null;
38909
39530
  }
39531
+ function lowerClassTargetMemberId(context, declaration, base) {
39532
+ const target = argument(
39533
+ annotation(declaration.annotations, "settings"),
39534
+ "target"
39535
+ );
39536
+ if (target === null) return base?.targetMemberId ?? null;
39537
+ const memberId = resolveQualifiedMemberId(context, target);
39538
+ if (memberId === null) {
39539
+ throw new Error(
39540
+ `Class ${declaration.name} declares @settings(target: ${target.trim()}), which names no member of a known class.`
39541
+ );
39542
+ }
39543
+ return memberId;
39544
+ }
38910
39545
  function resolveQualifiedMemberId(context, expression) {
38911
39546
  const match = /^([A-Za-z_][A-Za-z0-9_]*)\.([A-Za-z_][A-Za-z0-9_]*)$/.exec(
38912
39547
  expression.trim()
@@ -38914,14 +39549,7 @@ function resolveQualifiedMemberId(context, expression) {
38914
39549
  if (!match) return referenceId(expression);
38915
39550
  const classId = context.classIdsByName.get(match[1]);
38916
39551
  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}`);
39552
+ return effectiveMemberIdByName(context, classId, match[2]);
38925
39553
  }
38926
39554
  function hasAnnotation(annotations, name) {
38927
39555
  return annotation(annotations, name) !== void 0;
@@ -38978,11 +39606,25 @@ function referenceId(raw) {
38978
39606
  }
38979
39607
  const index = expression.argumentNames?.findIndex((entry) => entry === "id") ?? -1;
38980
39608
  const value = expression.args[index >= 0 ? index : 0];
38981
- return value?.kind === "litString" ? value.value : null;
39609
+ if (value?.kind === "litString") return value.value;
39610
+ const path = value === void 0 ? null : rootValuePath(value);
39611
+ return path === null ? null : `${PENDING_ID_PREFIX}root-path:${path}`;
38982
39612
  } catch {
38983
39613
  return null;
38984
39614
  }
38985
39615
  }
39616
+ function rootValuePath(expression) {
39617
+ const segments = [];
39618
+ let cursor = expression;
39619
+ while (cursor.kind === "member") {
39620
+ segments.unshift(cursor.name);
39621
+ cursor = cursor.receiver;
39622
+ }
39623
+ if (cursor.kind !== "ident") return null;
39624
+ if (cursor.name !== "root") return null;
39625
+ if (segments.length === 0) return null;
39626
+ return ["root", ...segments].join(".");
39627
+ }
38986
39628
  function isImpliedSchemaKeyOrder(order) {
38987
39629
  return order.every((key, index) => index === 0 || order[index - 1] <= key);
38988
39630
  }
@@ -39081,22 +39723,7 @@ function primitiveTypeKind(name) {
39081
39723
  return values[name] ?? null;
39082
39724
  }
39083
39725
  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";
39726
+ return fixedSourceTypeNameForKind(kind) ?? "object";
39100
39727
  }
39101
39728
  function requiredTypeArgument(type, index) {
39102
39729
  const value = type.arguments[index];
@@ -39113,20 +39740,23 @@ function* zip(left, right) {
39113
39740
  yield [left[index], right[index]];
39114
39741
  }
39115
39742
  }
39116
- var UNSET_LIST_COLUMN_WIDTH, primitiveMemberKinds;
39743
+ var UNSET_LIST_COLUMN_WIDTH, EMPTY_GENERIC_TYPE_ENVIRONMENT, primitiveMemberKinds;
39117
39744
  var init_lower_members = __esm({
39118
39745
  "src/project-source/lower-members.ts"() {
39119
39746
  "use strict";
39120
39747
  init_src();
39121
39748
  init_lower_support();
39122
39749
  init_lower_constructors();
39750
+ init_projection();
39123
39751
  init_declared_constructors2();
39124
39752
  init_init_source();
39125
39753
  init_ui_action_source();
39126
39754
  init_entry_member_id();
39127
39755
  init_generic_argument_member_id();
39128
39756
  init_structured_leaf_source();
39757
+ init_member_kind_type_names();
39129
39758
  UNSET_LIST_COLUMN_WIDTH = -1;
39759
+ EMPTY_GENERIC_TYPE_ENVIRONMENT = /* @__PURE__ */ new Map();
39130
39760
  primitiveMemberKinds = /* @__PURE__ */ new Set([
39131
39761
  "null",
39132
39762
  "bool",
@@ -39562,6 +40192,7 @@ function lowerProjectSchemaV4(base, analysis, options = {}) {
39562
40192
  (type, ownerClass) => lowerType(context, type, ownerClass)
39563
40193
  )
39564
40194
  );
40195
+ normalizeLookupDeclaredTypes(context);
39565
40196
  for (const [memberId, placements] of context.placements) {
39566
40197
  const member = context.loweredMembers.get(memberId);
39567
40198
  if (!member) continue;
@@ -39851,6 +40482,7 @@ function emitProjectSourcesV4(manifest, options = {}) {
39851
40482
  staticInitializers: options.staticInitializers ?? /* @__PURE__ */ new Map(),
39852
40483
  defaultInitializers: options.defaultInitializers ?? /* @__PURE__ */ new Map(),
39853
40484
  fileSymbols: options.fileSymbols ?? /* @__PURE__ */ new Map(),
40485
+ collectionValuePaths: options.collectionValuePaths ?? /* @__PURE__ */ new Map(),
39854
40486
  templateNames: new Map(
39855
40487
  [...manifest.textureTemplates, ...manifest.audioTemplates].map(
39856
40488
  (template) => [template.id, template.name]
@@ -40033,6 +40665,9 @@ function emitClass(context, schemaClass2, memberIds) {
40033
40665
  id(schemaClass2.id).trimEnd(),
40034
40666
  ...schemaClass2.hiddenInMemberSelector ? ["@hidden"] : [],
40035
40667
  ...schemaClass2.allowedStorage ? [`@storage(allowed: .${enumCase(schemaClass2.allowedStorage)})`] : [],
40668
+ ...schemaClass2.targetMemberId ? [
40669
+ `@settings(target: ${qualifiedMember(context, schemaClass2.targetMemberId)})`
40670
+ ] : [],
40036
40671
  ...schemaClass2.system ? [systemAnnotation(schemaClass2.system)] : [],
40037
40672
  ...relationsAnnotations(context, schemaClass2)
40038
40673
  ];
@@ -40327,8 +40962,11 @@ function memberSettings(context, member) {
40327
40962
  `collection: ${qualifiedMember(context, member.collectionMemberId)}`
40328
40963
  );
40329
40964
  if (member.collectionValueId) {
40965
+ const rootPath = context.collectionValuePaths.get(
40966
+ member.collectionValueId
40967
+ );
40330
40968
  settings.push(
40331
- `collectionValue: Reference(id: ${quote(member.collectionValueId)})`
40969
+ rootPath === void 0 ? `collectionValue: Reference(id: ${quote(member.collectionValueId)})` : `collectionValue: Reference(${rootPath})`
40332
40970
  );
40333
40971
  }
40334
40972
  if (member.multiselect) settings.push("multiselect: true");
@@ -40462,13 +41100,7 @@ function renderMemberType(context, member) {
40462
41100
  break;
40463
41101
  }
40464
41102
  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";
41103
+ const target = member.declaredType === null ? renderLookupEntryType(context, member.collectionMemberId) : renderType(context, member.declaredType).replace(/\?$/u, "");
40472
41104
  value = member.multiselect ? `List<${target}>` : target;
40473
41105
  break;
40474
41106
  }
@@ -40495,6 +41127,22 @@ function renderMemberType(context, member) {
40495
41127
  if (member.kind === "generic") return value;
40496
41128
  return nullable && value !== "null" ? `${value}?` : value;
40497
41129
  }
41130
+ function renderLookupEntryType(context, collectionMemberId) {
41131
+ const collection = required(
41132
+ context.members,
41133
+ collectionMemberId,
41134
+ "lookup collection"
41135
+ );
41136
+ if (collection.kind !== "list" && collection.kind !== "dictionary") {
41137
+ return "object";
41138
+ }
41139
+ const entry = required(
41140
+ context.members,
41141
+ collection.entryMemberId,
41142
+ "lookup entry"
41143
+ );
41144
+ return renderMemberType(context, entry).replace(/\?$/u, "");
41145
+ }
40498
41146
  function renderType(context, type) {
40499
41147
  let value;
40500
41148
  switch (type.kind) {
@@ -45471,7 +46119,17 @@ function qualifiedProjectFileSymbolsV4(records2) {
45471
46119
  }
45472
46120
  return result;
45473
46121
  }
45474
- function lowerStaticValueSourcesV4(state, analysis, manifest) {
46122
+ function createValueLowerRegistryV4() {
46123
+ return {
46124
+ pendingValues: /* @__PURE__ */ new Map(),
46125
+ pendingLocalizedTexts: /* @__PURE__ */ new Map(),
46126
+ pendingBindingMembersByClassId: /* @__PURE__ */ new Map(),
46127
+ referenceObligations: [],
46128
+ initAuthoredRowIds: /* @__PURE__ */ new Map(),
46129
+ loweringFailures: []
46130
+ };
46131
+ }
46132
+ function lowerStaticValueSourcesV4(state, analysis, manifest, options = {}) {
45475
46133
  const bindings = [];
45476
46134
  for (const sourceClass of analysis.schema.classes) {
45477
46135
  for (const declaration of sourceClass.members) {
@@ -45493,9 +46151,13 @@ function lowerStaticValueSourcesV4(state, analysis, manifest) {
45493
46151
  });
45494
46152
  }
45495
46153
  }
45496
- return lowerStoredValueBindingsV4(state, manifest, bindings, { analysis });
46154
+ return lowerStoredValueBindingsV4(state, manifest, bindings, {
46155
+ analysis,
46156
+ registry: options.registry
46157
+ });
45497
46158
  }
45498
46159
  function buildValueLowerContext(state, manifest, options = {}) {
46160
+ const registry = options.registry ?? createValueLowerRegistryV4();
45499
46161
  const members = new Map(
45500
46162
  manifest.members.map((member) => [member.id, member])
45501
46163
  );
@@ -45516,6 +46178,7 @@ function buildValueLowerContext(state, manifest, options = {}) {
45516
46178
  return {
45517
46179
  state,
45518
46180
  members,
46181
+ memberOverrides: indexMemberOverrides(members),
45519
46182
  classes,
45520
46183
  classesByName,
45521
46184
  interfaceIdsByName: new Map(
@@ -45531,22 +46194,43 @@ function buildValueLowerContext(state, manifest, options = {}) {
45531
46194
  dialogueTargetsBySymbol: dialogueTargetsBySymbol(options.analysis),
45532
46195
  projectFileIdsBySymbol: projectFileIdsBySymbol(state, options.analysis),
45533
46196
  mainLocale: projectMainLocaleFromState(state),
45534
- valuePlacements: indexValuePlacements(state),
46197
+ valuePlacements: options.valuePlacements ?? indexValuePlacements(state),
45535
46198
  parsedInitializers,
45536
46199
  declaredInitializers: indexDeclaredInitializers(options.analysis),
45537
46200
  reconstructed: /* @__PURE__ */ new Map(),
45538
- pendingValues: /* @__PURE__ */ new Map(),
45539
- pendingLocalizedTexts: /* @__PURE__ */ new Map(),
46201
+ pendingValues: registry.pendingValues,
46202
+ pendingLocalizedTexts: registry.pendingLocalizedTexts,
45540
46203
  loweredMemberIdByValueId: /* @__PURE__ */ new Map(),
45541
- pendingBindingMembersByClassId: /* @__PURE__ */ new Map(),
46204
+ pendingBindingMembersByClassId: registry.pendingBindingMembersByClassId,
45542
46205
  declaredConstructors: declaredConstructorIndex,
45543
- initAuthoredRowIds: indexInitializerAuthoredRowIds(
45544
- options.analysis,
45545
- declaredConstructorIndex,
45546
- classesByName
45547
- )
46206
+ initAuthoredRowIds: seedInitAuthoredRowIds(
46207
+ registry.initAuthoredRowIds,
46208
+ indexInitializerAuthoredRowIds(
46209
+ options.analysis,
46210
+ declaredConstructorIndex,
46211
+ classesByName
46212
+ )
46213
+ ),
46214
+ referenceObligations: registry.referenceObligations,
46215
+ loweringFailures: registry.loweringFailures
45548
46216
  };
45549
46217
  }
46218
+ function indexMemberOverrides(members) {
46219
+ const result = /* @__PURE__ */ new Map();
46220
+ for (const member of members.values()) {
46221
+ if (member.overrideOf === null) continue;
46222
+ const overrides = result.get(member.overrideOf) ?? [];
46223
+ overrides.push(member.id);
46224
+ result.set(member.overrideOf, overrides);
46225
+ }
46226
+ return result;
46227
+ }
46228
+ function seedInitAuthoredRowIds(shared, discovered) {
46229
+ for (const [id2, owner] of discovered) {
46230
+ if (!shared.has(id2)) shared.set(id2, owner);
46231
+ }
46232
+ return shared;
46233
+ }
45550
46234
  function indexInitializerAuthoredRowIds(analysis, declaredConstructors2, classesByName) {
45551
46235
  const index = /* @__PURE__ */ new Map();
45552
46236
  if (analysis === void 0) return index;
@@ -45664,8 +46348,11 @@ function lowerStoredValueBindingsV4(state, manifest, bindings, options = {}) {
45664
46348
  seeds
45665
46349
  };
45666
46350
  }
45667
- function lowerMemberDefaultSourcesV4(state, analysis, manifest) {
45668
- const context = buildValueLowerContext(state, manifest, { analysis });
46351
+ function lowerMemberDefaultSourcesV4(state, analysis, manifest, options = {}) {
46352
+ const context = buildValueLowerContext(state, manifest, {
46353
+ analysis,
46354
+ registry: options.registry
46355
+ });
45669
46356
  const pulledValueIds = /* @__PURE__ */ new Set();
45670
46357
  for (const record3 of Object.values(state)) {
45671
46358
  if (record3.recordKind === "value") pulledValueIds.add(record3.recordId);
@@ -45730,6 +46417,372 @@ function lowerMemberDefaultSourcesV4(state, analysis, manifest) {
45730
46417
  seeds
45731
46418
  };
45732
46419
  }
46420
+ function drainValueReferenceObligationsV4(state, manifest, options) {
46421
+ const deletedValueIds = options.deletedValueIds ?? /* @__PURE__ */ new Set();
46422
+ const survivingState = withoutDeletedValues(state, deletedValueIds);
46423
+ const context = buildValueLowerContext(survivingState, manifest, {
46424
+ analysis: options.analysis,
46425
+ registry: options.registry,
46426
+ valuePlacements: indexValuePlacements(survivingState, {
46427
+ pendingValues: options.registry.pendingValues,
46428
+ deletedValueIds
46429
+ })
46430
+ });
46431
+ const failures = [];
46432
+ for (const recorded of options.registry.loweringFailures) {
46433
+ failures.push({
46434
+ message: recorded.message,
46435
+ ...composeReferenceSitePosition(recorded.site, options.sourceTextByUri)
46436
+ });
46437
+ }
46438
+ for (const obligation of [
46439
+ ...collectionValueObligations(manifest),
46440
+ ...options.registry.referenceObligations
46441
+ ]) {
46442
+ const failure = settleReferenceObligation(
46443
+ context,
46444
+ obligation,
46445
+ deletedValueIds
46446
+ );
46447
+ if (failure === null) continue;
46448
+ failures.push({
46449
+ message: failure,
46450
+ ...composeReferenceSitePosition(obligation.site, options.sourceTextByUri)
46451
+ });
46452
+ }
46453
+ return failures;
46454
+ }
46455
+ function withoutDeletedValues(state, deletedValueIds) {
46456
+ if (deletedValueIds.size === 0) return state;
46457
+ const surviving = { ...state };
46458
+ for (const valueId of deletedValueIds) delete surviving[`value:${valueId}`];
46459
+ return surviving;
46460
+ }
46461
+ function collectionValueObligations(manifest) {
46462
+ const obligations = [];
46463
+ for (const member of manifest.members) {
46464
+ if (member.kind !== "lookup") continue;
46465
+ if (typeof member.collectionValueId !== "string") continue;
46466
+ obligations.push({
46467
+ kind: "collectionValue",
46468
+ site: {
46469
+ uri: member.source.span.path,
46470
+ declarationStart: member.source.span.start,
46471
+ expression: null,
46472
+ label: member.name
46473
+ },
46474
+ memberId: member.id,
46475
+ collectionMemberId: member.collectionMemberId,
46476
+ collectionValueId: member.collectionValueId
46477
+ });
46478
+ }
46479
+ return obligations;
46480
+ }
46481
+ function settleReferenceObligation(context, obligation, deletedValueIds) {
46482
+ try {
46483
+ if (obligation.kind === "reference") {
46484
+ settleReferenceMember(context, obligation, deletedValueIds);
46485
+ return null;
46486
+ }
46487
+ if (obligation.kind === "constructorProjection") {
46488
+ settleConstructorProjection(context, obligation, deletedValueIds);
46489
+ return null;
46490
+ }
46491
+ settleCollectionValue(context, obligation);
46492
+ return null;
46493
+ } catch (error) {
46494
+ return error instanceof Error ? error.message : String(error);
46495
+ }
46496
+ }
46497
+ function settleReferenceMember(context, obligation, deletedValueIds) {
46498
+ const member = context.members.get(obligation.memberId);
46499
+ if (member?.kind !== "lookup" && member?.kind !== "dialogueLookup") {
46500
+ throw new Error(
46501
+ `Reference member ${obligation.site.label} no longer declares a lookup after this push.`
46502
+ );
46503
+ }
46504
+ if (obligation.target.spelling === "key") {
46505
+ settleKeyReference(context, member, obligation.target, obligation);
46506
+ return;
46507
+ }
46508
+ const target = resolveObligationTarget(
46509
+ context,
46510
+ obligation.target,
46511
+ deletedValueIds,
46512
+ `Reference member ${member.name}`
46513
+ );
46514
+ validateReferenceContract(context, member, target, obligation.ownerValueId);
46515
+ }
46516
+ function settleKeyReference(context, member, spelling, obligation) {
46517
+ if (member.kind !== "lookup") {
46518
+ throw new Error(
46519
+ `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.`
46520
+ );
46521
+ }
46522
+ const collection = context.members.get(member.collectionMemberId);
46523
+ if (collection?.kind !== "list") {
46524
+ throw new Error(
46525
+ `Reference member ${member.name} uses the key form, but its declared collection ${collectionMemberName(context, member)} is not an indexed list.`
46526
+ );
46527
+ }
46528
+ const schemaKey = keyIndexSchemaKey(context, member, collection, spelling);
46529
+ const resolution = resolveLookupCollectionValueIds(
46530
+ context,
46531
+ member,
46532
+ collection,
46533
+ obligation.ownerValueId
46534
+ );
46535
+ if (resolution.valueIds.length === 0) {
46536
+ throw new Error(
46537
+ `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.`
46538
+ );
46539
+ }
46540
+ const matches = [];
46541
+ for (const collectionValueId of resolution.valueIds) {
46542
+ for (const entryId of collectionEntryIds(
46543
+ context,
46544
+ collection,
46545
+ collectionValueId
46546
+ )) {
46547
+ const entry = valueData(context, entryId);
46548
+ if (!entry || !isObjectRecord2(entry.value)) continue;
46549
+ const fieldRowId = entry.value[schemaKey];
46550
+ if (typeof fieldRowId !== "string") continue;
46551
+ if (valueData(context, fieldRowId)?.value !== spelling.key) continue;
46552
+ matches.push(entryId);
46553
+ }
46554
+ }
46555
+ if (matches.length === 0) {
46556
+ throw new Error(
46557
+ `Reference member ${member.name} key ${JSON.stringify(spelling.key)} matches no row of collection ${collectionMemberName(context, member)} under index ${schemaKey}.`
46558
+ );
46559
+ }
46560
+ if (matches.length > 1) {
46561
+ throw new Error(
46562
+ `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.`
46563
+ );
46564
+ }
46565
+ const targetId = matches[0];
46566
+ if (targetId === void 0) {
46567
+ throw new Error(
46568
+ `Reference member ${member.name} key ${JSON.stringify(spelling.key)} resolved to no target id.`
46569
+ );
46570
+ }
46571
+ const target = referenceTargetById(context, targetId, spelling.typeName);
46572
+ if (target === null) {
46573
+ throw new Error(
46574
+ `Reference member ${member.name} key ${JSON.stringify(spelling.key)} resolved to ${targetId}, which names no value row in the project after this push.`
46575
+ );
46576
+ }
46577
+ validateReferenceContract(context, member, target, obligation.ownerValueId);
46578
+ spelling.patch.ids[spelling.patch.index] = targetId;
46579
+ const patched = spelling.patch.ids.filter((id2) => !isPendingId(id2));
46580
+ if (new Set(patched).size !== patched.length) {
46581
+ throw new Error(
46582
+ `Reference member ${member.name} cannot contain duplicate targets.`
46583
+ );
46584
+ }
46585
+ }
46586
+ function keyIndexSchemaKey(context, member, collection, spelling) {
46587
+ const indexes = collection.indexes ?? [];
46588
+ if (spelling.indexSchemaKey !== null) {
46589
+ const named = indexes.find(
46590
+ (candidate) => candidate.schemaKey === spelling.indexSchemaKey
46591
+ );
46592
+ if (named === void 0) {
46593
+ throw new Error(
46594
+ `Reference member ${member.name} names index ${spelling.indexSchemaKey}, which collection ${collectionMemberName(context, member)} does not declare.`
46595
+ );
46596
+ }
46597
+ if (!named.unique) {
46598
+ throw new Error(
46599
+ `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.`
46600
+ );
46601
+ }
46602
+ return named.schemaKey;
46603
+ }
46604
+ const unique = indexes.filter((candidate) => candidate.unique);
46605
+ if (unique.length === 0) {
46606
+ throw new Error(
46607
+ `Reference member ${member.name} uses the key form, but collection ${collectionMemberName(context, member)} declares no unique index.`
46608
+ );
46609
+ }
46610
+ if (unique.length > 1) {
46611
+ throw new Error(
46612
+ `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.`
46613
+ );
46614
+ }
46615
+ const only = unique[0];
46616
+ if (only === void 0) {
46617
+ throw new Error(
46618
+ `Reference member ${member.name} resolved no unique index for collection ${collectionMemberName(context, member)}.`
46619
+ );
46620
+ }
46621
+ return only.schemaKey;
46622
+ }
46623
+ function collectionEntryIds(context, collection, collectionValueId) {
46624
+ if (collection.listKind === "unordered") {
46625
+ return [
46626
+ ...context.valuePlacements.valueIdsByContainerId.get(
46627
+ collectionValueId
46628
+ ) ?? []
46629
+ ];
46630
+ }
46631
+ const body = valueData(context, collectionValueId)?.value;
46632
+ return Array.isArray(body) ? body.filter((entry) => typeof entry === "string") : [];
46633
+ }
46634
+ function settleConstructorProjection(context, obligation, deletedValueIds) {
46635
+ const schemaClass2 = context.classes.get(obligation.schemaClassId);
46636
+ if (schemaClass2 === void 0) {
46637
+ throw new Error(
46638
+ `Constructor projection ${obligation.parameterName} names class ${obligation.schemaClassId}, which this push does not declare.`
46639
+ );
46640
+ }
46641
+ const projectedMember = context.members.get(obligation.projectedMemberId);
46642
+ if (projectedMember?.kind !== "lookup") {
46643
+ throw new Error(
46644
+ `Class ${schemaClass2.name} constructor projection ${obligation.parameterName} does not resolve to a Lookup field.`
46645
+ );
46646
+ }
46647
+ const target = referenceTargetById(context, obligation.targetId, "");
46648
+ if (target === null || target.kind !== "value") {
46649
+ const owner = context.initAuthoredRowIds.get(obligation.targetId);
46650
+ if (owner !== void 0) {
46651
+ throw new Error(
46652
+ `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.`
46653
+ );
46654
+ }
46655
+ if (deletedValueIds.has(obligation.targetId)) {
46656
+ throw new Error(
46657
+ `Class ${schemaClass2.name} constructor argument ${obligation.parameterName} targets value ${obligation.targetId}, which this push deletes.`
46658
+ );
46659
+ }
46660
+ throw new Error(
46661
+ `Class ${schemaClass2.name} constructor argument ${obligation.parameterName} targets a missing value ${obligation.targetId}. No row in the project carries that id after this push.`
46662
+ );
46663
+ }
46664
+ validateReferenceContract(
46665
+ context,
46666
+ projectedMember,
46667
+ { ...target, genericTypeName: null },
46668
+ obligation.ownerValueId
46669
+ );
46670
+ validateAnimationProjectionBinding(
46671
+ context,
46672
+ schemaClass2,
46673
+ obligation.environment,
46674
+ target.id
46675
+ );
46676
+ }
46677
+ function settleCollectionValue(context, obligation) {
46678
+ const collection = context.members.get(obligation.collectionMemberId);
46679
+ if (collection?.kind !== "list" && collection?.kind !== "dictionary") {
46680
+ throw new Error(
46681
+ `Lookup member ${obligation.site.label} declares collectionValue ${obligation.collectionValueId} for a collection member that this push does not declare.`
46682
+ );
46683
+ }
46684
+ const row = valueData(context, obligation.collectionValueId);
46685
+ if (row === null) {
46686
+ throw new Error(
46687
+ `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.`
46688
+ );
46689
+ }
46690
+ const declaredMemberIds = declaredCollectionMemberIds(
46691
+ context,
46692
+ obligation.collectionMemberId
46693
+ );
46694
+ const owners = valueOwningMemberIds(context, obligation.collectionValueId);
46695
+ if (owners.size === 0) return;
46696
+ for (const owner of owners) {
46697
+ if (declaredMemberIds.has(owner)) return;
46698
+ }
46699
+ throw new Error(
46700
+ `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}.`
46701
+ );
46702
+ }
46703
+ function resolveObligationTarget(context, spelling, deletedValueIds, subject) {
46704
+ if (spelling.spelling === "symbol") return spelling.resolved;
46705
+ if (spelling.spelling === "key") {
46706
+ throw new Error(
46707
+ `${subject} carries a key spelling with no collection to resolve it against.`
46708
+ );
46709
+ }
46710
+ const target = referenceTargetById(context, spelling.id, spelling.typeName);
46711
+ if (target !== null) return target;
46712
+ if (deletedValueIds.has(spelling.id)) {
46713
+ throw new Error(
46714
+ `${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.`
46715
+ );
46716
+ }
46717
+ const owner = context.initAuthoredRowIds.get(spelling.id);
46718
+ if (owner !== void 0) {
46719
+ throw new Error(
46720
+ `${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.`
46721
+ );
46722
+ }
46723
+ throw new Error(
46724
+ `${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.`
46725
+ );
46726
+ }
46727
+ function composeReferenceSitePosition(site, sourceTextByUri) {
46728
+ const declaration = {
46729
+ file: site.uri,
46730
+ line: site.declarationStart.line + 1,
46731
+ column: site.declarationStart.character + 1
46732
+ };
46733
+ const text = sourceTextByUri?.get(site.uri);
46734
+ if (site.expression === null || text === void 0) return declaration;
46735
+ const declarationOffset = sourceOffsetAtPosition(text, site.declarationStart);
46736
+ if (declarationOffset === null) return declaration;
46737
+ const initializerOffset = text.indexOf(
46738
+ site.expression.initializer,
46739
+ declarationOffset
46740
+ );
46741
+ if (initializerOffset < 0) return declaration;
46742
+ const origin = sourcePositionAtOffset(text, initializerOffset);
46743
+ const { line, column } = site.expression.pos;
46744
+ if (line === 1) {
46745
+ return {
46746
+ file: site.uri,
46747
+ line: origin.line + 1,
46748
+ column: origin.character + column
46749
+ };
46750
+ }
46751
+ return { file: site.uri, line: origin.line + line, column };
46752
+ }
46753
+ function sourceOffsetAtPosition(text, position) {
46754
+ let offset = 0;
46755
+ for (let line = 0; line < position.line; line += 1) {
46756
+ const next = text.indexOf("\n", offset);
46757
+ if (next < 0) return null;
46758
+ offset = next + 1;
46759
+ }
46760
+ const candidate = offset + position.character;
46761
+ return candidate > text.length ? null : candidate;
46762
+ }
46763
+ function sourcePositionAtOffset(text, offset) {
46764
+ let line = 0;
46765
+ let lineStart = 0;
46766
+ for (let cursor = 0; cursor < offset; cursor += 1) {
46767
+ if (text[cursor] !== "\n") continue;
46768
+ line += 1;
46769
+ lineStart = cursor + 1;
46770
+ }
46771
+ return { line, character: offset - lineStart };
46772
+ }
46773
+ function declaredCollectionMemberIds(context, collectionMemberId) {
46774
+ const ids = /* @__PURE__ */ new Set([collectionMemberId]);
46775
+ const pending = [collectionMemberId];
46776
+ while (pending.length > 0) {
46777
+ const current = pending.pop();
46778
+ for (const override of context.memberOverrides.get(current) ?? []) {
46779
+ if (ids.has(override)) continue;
46780
+ ids.add(override);
46781
+ pending.push(override);
46782
+ }
46783
+ }
46784
+ return ids;
46785
+ }
45733
46786
  function defaultRequiresOwnedRows(context, member, sourceExpression) {
45734
46787
  const expression = annotatedValue(sourceExpression).expression;
45735
46788
  if (initializerRequiresEvaluation(
@@ -45846,7 +46899,7 @@ function lowerRowBackedDefault(context, member, backed, baseData3, binding) {
45846
46899
  );
45847
46900
  for (const assignment of expression.initializer ?? []) {
45848
46901
  const childMember = classMemberByName(context, classId, assignment.name);
45849
- if (schemaClass2.constructorProjections?.some(
46902
+ if (inheritedConstructorProjections(context.classes, schemaClass2.id).some(
45850
46903
  (projection) => projection.memberId === childMember.id
45851
46904
  )) {
45852
46905
  throw new Error(
@@ -46128,11 +47181,7 @@ function lowerSeedRow(context, member, sourceExpression, source, valueId, path,
46128
47181
  ...containerId === void 0 ? {} : { containerId },
46129
47182
  ...initSourceValueId === null ? {} : { sourceValueId: initSourceValueId }
46130
47183
  };
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);
47184
+ registerSeedRow(context, rows, initRow, source, sourceExpression);
46136
47185
  return initRow;
46137
47186
  }
46138
47187
  let value;
@@ -46194,7 +47243,8 @@ function lowerSeedRow(context, member, sourceExpression, source, valueId, path,
46194
47243
  effectiveClass,
46195
47244
  expression,
46196
47245
  valueId,
46197
- environment
47246
+ environment,
47247
+ source
46198
47248
  )) {
46199
47249
  const childPath = `${path}.${schemaKey}`;
46200
47250
  const childValueId = pendingNestedValueId(source, childPath);
@@ -46220,9 +47270,10 @@ function lowerSeedRow(context, member, sourceExpression, source, valueId, path,
46220
47270
  effectiveClass.id,
46221
47271
  assignment.name
46222
47272
  );
46223
- if (effectiveClass.constructorProjections?.some(
46224
- (projection) => projection.memberId === childMember.id
46225
- )) {
47273
+ if (inheritedConstructorProjections(
47274
+ context.classes,
47275
+ effectiveClass.id
47276
+ ).some((projection) => projection.memberId === childMember.id)) {
46226
47277
  throw new Error(
46227
47278
  `Class value ${path}.${assignment.name} is constructor-projected and must be set through its named constructor argument.`
46228
47279
  );
@@ -46351,13 +47402,24 @@ function lowerSeedRow(context, member, sourceExpression, source, valueId, path,
46351
47402
  ...genericBindings === void 0 ? {} : { genericBindings },
46352
47403
  ...sourceValueId === null ? {} : { sourceValueId }
46353
47404
  };
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);
47405
+ registerSeedRow(context, rows, row, source, sourceExpression);
46359
47406
  return row;
46360
47407
  }
47408
+ function registerSeedRow(context, rows, row, source, expression) {
47409
+ if (rows.has(row.id)) {
47410
+ throw new Error(`Value construction reuses identity ${row.id}.`);
47411
+ }
47412
+ if (context.pendingValues.has(row.id)) {
47413
+ context.loweringFailures.push({
47414
+ message: `Value ${source.label} claims identity ${row.id}, which another value created by this push already owns. Two new rows cannot share one @id.`,
47415
+ site: referenceSite(source, expression)
47416
+ });
47417
+ rows.set(row.id, row);
47418
+ return;
47419
+ }
47420
+ rows.set(row.id, row);
47421
+ context.pendingValues.set(row.id, row);
47422
+ }
46361
47423
  function materializeRequiredDefaults(context, member, effectiveClass, body, source, path, rows, localizedTexts, environment) {
46362
47424
  if (member.partial === true) return;
46363
47425
  for (const [schemaKey, childMember] of storedSchemaEntries(
@@ -46373,7 +47435,13 @@ function materializeRequiredDefaults(context, member, effectiveClass, body, sour
46373
47435
  context,
46374
47436
  recursivePartialMember(member, childMember),
46375
47437
  parseCachedInitializer(context.parsedInitializers, initializer),
46376
- source,
47438
+ // P47 §1.6. This subtree's AST comes from the child member's own declared
47439
+ // initializer, not the binding's, and every position in it is measured
47440
+ // from there. Handing down a binding that names the text keeps "positions
47441
+ // are relative to `source.initializer`" true everywhere, which is what
47442
+ // `referenceSite` composes against. Identity derivation reads the uri and
47443
+ // range, so the rows this produces are unchanged.
47444
+ { ...source, initializer },
46377
47445
  `${path}.${schemaKey}`,
46378
47446
  rows,
46379
47447
  localizedTexts,
@@ -46544,7 +47612,8 @@ function lowerValueBody(context, member, expression, base, source, environment)
46544
47612
  context,
46545
47613
  member,
46546
47614
  expression,
46547
- typeof base.id === "string" ? base.id : null
47615
+ typeof base.id === "string" ? base.id : null,
47616
+ source
46548
47617
  )
46549
47618
  };
46550
47619
  case "sprite":
@@ -46642,7 +47711,7 @@ function lowerClassValue(context, member, expression, base, source, outerEnviron
46642
47711
  );
46643
47712
  for (const assignment of expression.initializer ?? []) {
46644
47713
  const childMember = classMemberByName(context, classId, assignment.name);
46645
- if (schemaClass2.constructorProjections?.some(
47714
+ if (inheritedConstructorProjections(context.classes, schemaClass2.id).some(
46646
47715
  (projection) => projection.memberId === childMember.id
46647
47716
  )) {
46648
47717
  throw new Error(
@@ -46686,7 +47755,8 @@ function lowerConstructorProjections(context, schemaClass2, expression, base, ba
46686
47755
  schemaClass2,
46687
47756
  expression,
46688
47757
  typeof base.id === "string" ? base.id : null,
46689
- environment
47758
+ environment,
47759
+ source
46690
47760
  );
46691
47761
  for (const { schemaKey, target } of resolved) {
46692
47762
  const childValueId = baseBody[schemaKey];
@@ -46706,8 +47776,44 @@ function lowerConstructorProjections(context, schemaClass2, expression, base, ba
46706
47776
  body[schemaKey] = childValueId;
46707
47777
  }
46708
47778
  }
46709
- function resolveConstructorProjectionArguments(context, schemaClass2, expression, ownerValueId, environment) {
46710
- const projections = schemaClass2.constructorProjections ?? [];
47779
+ function classInheritanceChain(classes, classId) {
47780
+ const chain = [];
47781
+ const visited = /* @__PURE__ */ new Set();
47782
+ let current = classId;
47783
+ while (current !== null && !visited.has(current)) {
47784
+ visited.add(current);
47785
+ const schemaClass2 = classes.get(current);
47786
+ if (schemaClass2 === void 0) break;
47787
+ chain.push(schemaClass2);
47788
+ current = schemaClass2.extendsClassId;
47789
+ }
47790
+ return chain;
47791
+ }
47792
+ function inheritedConstructorProjections(classes, classId) {
47793
+ const resolved = [];
47794
+ const claimed = /* @__PURE__ */ new Set();
47795
+ for (const schemaClass2 of classInheritanceChain(classes, classId)) {
47796
+ for (const projection of schemaClass2.constructorProjections ?? []) {
47797
+ if (claimed.has(projection.parameterName)) continue;
47798
+ claimed.add(projection.parameterName);
47799
+ resolved.push(projection);
47800
+ }
47801
+ }
47802
+ return resolved;
47803
+ }
47804
+ function inheritedProjectionSchemaKey(classes, classId, memberId) {
47805
+ for (const schemaClass2 of classInheritanceChain(classes, classId)) {
47806
+ for (const [schemaKey, declared] of Object.entries(schemaClass2.schema)) {
47807
+ if (declared === memberId) return schemaKey;
47808
+ }
47809
+ }
47810
+ return null;
47811
+ }
47812
+ function resolveConstructorProjectionArguments(context, schemaClass2, expression, ownerValueId, environment, source) {
47813
+ const projections = inheritedConstructorProjections(
47814
+ context.classes,
47815
+ schemaClass2.id
47816
+ );
46711
47817
  if (projections.length === 0) {
46712
47818
  if (expression.args.length > 0) {
46713
47819
  throw new Error(
@@ -46751,44 +47857,32 @@ function resolveConstructorProjectionArguments(context, schemaClass2, expression
46751
47857
  `Class ${schemaClass2.name} constructor argument ${parameterName} must be a value-row id string.`
46752
47858
  );
46753
47859
  }
46754
- const schemaKey = Object.entries(schemaClass2.schema).find(
46755
- ([, memberId]) => memberId === projection.memberId
46756
- )?.[0];
47860
+ const schemaKey = inheritedProjectionSchemaKey(
47861
+ context.classes,
47862
+ schemaClass2.id,
47863
+ projection.memberId
47864
+ );
46757
47865
  const projectedMember = context.members.get(projection.memberId);
46758
- if (schemaKey === void 0 || projectedMember?.kind !== "lookup") {
47866
+ if (schemaKey === null || projectedMember?.kind !== "lookup") {
46759
47867
  throw new Error(
46760
47868
  `Class ${schemaClass2.name} constructor projection ${parameterName} does not resolve to a Lookup field.`
46761
47869
  );
46762
47870
  }
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
- );
47871
+ context.referenceObligations.push({
47872
+ kind: "constructorProjection",
47873
+ site: referenceSite(source, argument2),
47874
+ schemaClassId: schemaClass2.id,
47875
+ parameterName,
47876
+ projectedMemberId: projectedMember.id,
47877
+ targetId: argument2.value,
47878
+ ownerValueId,
47879
+ environment
47880
+ });
46787
47881
  resolved.push({
46788
47882
  parameterName,
46789
47883
  projectedMember,
46790
47884
  schemaKey,
46791
- target: { id: target.id, kind: "value" }
47885
+ target: { id: argument2.value, kind: "value" }
46792
47886
  });
46793
47887
  }
46794
47888
  return resolved;
@@ -46887,7 +47981,7 @@ function genericBindingClassId(context, bindingMemberId) {
46887
47981
  return rawBinding?.kind === 7 && typeof rawBinding.classId === "string" ? rawBinding.classId : null;
46888
47982
  }
46889
47983
  function validateConstructedGenericArguments(context, schemaClass2, expression, environment, path) {
46890
- const actual = expression.typeArguments ?? [];
47984
+ const actual = expression.className === "Partial" ? [] : expression.typeArguments ?? [];
46891
47985
  const expected = schemaClass2.genericParameters;
46892
47986
  if (expected.length === 0) {
46893
47987
  if (actual.length > 0) {
@@ -46928,14 +48022,14 @@ function lowerRawMemberTypeName(context, memberId) {
46928
48022
  if (member.kind === 7 && typeof member.classId === "string") {
46929
48023
  return context.classes.get(member.classId)?.name ?? null;
46930
48024
  }
46931
- return typeof member.kind === "number" ? MEMBER_KIND_SOURCE_NAMES.get(member.kind) ?? null : null;
48025
+ return typeof member.kind === "number" ? fixedSourceTypeNameForKindNumber(member.kind) : null;
46932
48026
  }
46933
48027
  function lowerSchemaMemberTypeName(context, member) {
46934
48028
  if (member.kind === "class") {
46935
48029
  return context.classes.get(member.classId)?.name ?? null;
46936
48030
  }
46937
48031
  if (member.kind === "generic") return null;
46938
- return member.kind;
48032
+ return fixedSourceTypeNameForKind(member.kind) ?? member.kind;
46939
48033
  }
46940
48034
  function constructedClass(context, member, expression, currentClassId, path) {
46941
48035
  if (member.partial === true) {
@@ -46963,13 +48057,22 @@ function constructedClass(context, member, expression, currentClassId, path) {
46963
48057
  );
46964
48058
  }
46965
48059
  if (expression.className !== null) {
46966
- return requiredClassByName(context, expression.className);
48060
+ const named = requiredClassByName(context, expression.className);
48061
+ assertConstructedClassAssignable(context, named, member, path);
48062
+ return named;
46967
48063
  }
46968
48064
  const target = context.classes.get(currentClassId);
46969
48065
  if (target === void 0)
46970
48066
  throw new Error(`Unknown value class ${currentClassId}.`);
46971
48067
  return target;
46972
48068
  }
48069
+ function assertConstructedClassAssignable(context, named, member, path) {
48070
+ if (classAssignableToClass(context, named.id, member.classId)) return;
48071
+ const declaredName = context.classes.get(member.classId)?.name ?? member.classId;
48072
+ throw new Error(
48073
+ `Class value ${path} constructs ${named.name}, which does not descend from the declared class ${declaredName}.`
48074
+ );
48075
+ }
46973
48076
  function recursivePartialMember(parent, child) {
46974
48077
  if (parent.partial !== true) return child;
46975
48078
  if (child.kind === "class" || child.kind === "generic") {
@@ -47018,7 +48121,13 @@ function lowerListValue(context, member, expression, base, source, inheritedEnvi
47018
48121
  continue;
47019
48122
  }
47020
48123
  const itemId = annotatedItemId;
47021
- if (isPendingId(itemId) && context.state[`value:${itemId}`] === void 0) {
48124
+ if (context.state[`value:${itemId}`] === void 0) {
48125
+ if (!isPendingId(itemId) && !isUuidV4Id(itemId)) {
48126
+ context.loweringFailures.push({
48127
+ 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.`,
48128
+ site: referenceSite(source, element)
48129
+ });
48130
+ }
47022
48131
  ids.push(
47023
48132
  lowerPendingListItem(
47024
48133
  context,
@@ -47173,19 +48282,37 @@ function lowerEnum2(context, member, expression) {
47173
48282
  return id2;
47174
48283
  });
47175
48284
  }
47176
- function lowerReferences(context, member, expression, ownerValueId) {
48285
+ function lowerReferences(context, member, expression, ownerValueId, source) {
47177
48286
  const elements = member.multiselect ? expression.kind === "litList" ? expression.elements : null : [expression];
47178
48287
  if (elements === null) {
47179
48288
  throw new Error(`Reference member ${member.name} requires [...].`);
47180
48289
  }
47181
- const ids = elements.map((entry) => {
47182
- const target = resolveReferenceTarget(context, entry);
48290
+ const ids = [];
48291
+ for (const [index, entry] of elements.entries()) {
48292
+ const target = referenceTargetSpelling(context, entry);
47183
48293
  if (target === null) {
47184
48294
  throw new Error(`Reference member ${member.name} has an invalid target.`);
47185
48295
  }
47186
- validateReferenceContract(context, member, target, ownerValueId);
47187
- return target.id;
47188
- });
48296
+ if (target.spelling === "key") {
48297
+ ids.push(`${PENDING_ID_PREFIX}reference-key:${source.label}:${index}`);
48298
+ context.referenceObligations.push({
48299
+ kind: "reference",
48300
+ site: referenceSite(source, entry),
48301
+ memberId: member.id,
48302
+ ownerValueId,
48303
+ target: { ...target, patch: { ids, index } }
48304
+ });
48305
+ continue;
48306
+ }
48307
+ context.referenceObligations.push({
48308
+ kind: "reference",
48309
+ site: referenceSite(source, entry),
48310
+ memberId: member.id,
48311
+ ownerValueId,
48312
+ target
48313
+ });
48314
+ ids.push(target.spelling === "id" ? target.id : target.resolved.id);
48315
+ }
47189
48316
  if (new Set(ids).size !== ids.length) {
47190
48317
  throw new Error(
47191
48318
  `Reference member ${member.name} cannot contain duplicate targets.`
@@ -47193,6 +48320,27 @@ function lowerReferences(context, member, expression, ownerValueId) {
47193
48320
  }
47194
48321
  return ids;
47195
48322
  }
48323
+ function referenceSite(source, expression) {
48324
+ return {
48325
+ uri: source.source.uri,
48326
+ declarationStart: source.source.range.start,
48327
+ expression: {
48328
+ initializer: source.initializer,
48329
+ pos: expressionAnchorPos(expression)
48330
+ },
48331
+ label: source.label
48332
+ };
48333
+ }
48334
+ function expressionAnchorPos(expression) {
48335
+ if (expression.kind === "annotated") {
48336
+ return expressionAnchorPos(expression.expression);
48337
+ }
48338
+ if (expression.kind === "call") return expressionAnchorPos(expression.callee);
48339
+ if (expression.kind === "member") {
48340
+ return expressionAnchorPos(expression.receiver);
48341
+ }
48342
+ return { line: expression.pos.line, column: expression.pos.column };
48343
+ }
47196
48344
  function lowerFileValue(context, member, expression) {
47197
48345
  if (member.kind === "sprite") {
47198
48346
  if (expression.kind !== "call" || expression.callee.kind !== "member" || expression.callee.name !== "Slice") {
@@ -47281,24 +48429,53 @@ function sourceValueSymbol(context, expression) {
47281
48429
  const path = memberPath(unwrapped);
47282
48430
  return path === null ? null : context.staticValueIdsBySymbol.get(path) ?? null;
47283
48431
  }
47284
- function resolveReferenceTarget(context, expression) {
48432
+ function referenceTargetSpelling(context, expression) {
47285
48433
  if (expression.kind !== "call" || expression.callee.kind !== "ident" || expression.callee.name !== "Reference") {
47286
48434
  return null;
47287
48435
  }
47288
48436
  if ((expression.typeArguments?.length ?? 0) > 1) return null;
47289
48437
  const genericTypeName = expression.typeArguments?.[0] ? astTypeName(expression.typeArguments[0]) : null;
48438
+ const keyNamed = expression.argumentNames?.findIndex((name) => name === "key") ?? -1;
48439
+ if (keyNamed >= 0) {
48440
+ const keyArgument = expression.args[keyNamed];
48441
+ if (keyArgument?.kind !== "litString" || !genericTypeName) return null;
48442
+ const indexNamed = expression.argumentNames?.findIndex((name) => name === "index") ?? -1;
48443
+ if (expression.args.length !== (indexNamed >= 0 ? 2 : 1)) return null;
48444
+ if (indexNamed < 0) {
48445
+ return {
48446
+ spelling: "key",
48447
+ key: keyArgument.value,
48448
+ indexSchemaKey: null,
48449
+ typeName: genericTypeName
48450
+ };
48451
+ }
48452
+ const indexArgument = expression.args[indexNamed];
48453
+ if (indexArgument?.kind !== "ident") return null;
48454
+ return {
48455
+ spelling: "key",
48456
+ key: keyArgument.value,
48457
+ indexSchemaKey: indexArgument.name,
48458
+ typeName: genericTypeName
48459
+ };
48460
+ }
47290
48461
  const named = expression.argumentNames?.findIndex((name) => name === "id") ?? -1;
47291
48462
  const argument2 = expression.args[named >= 0 ? named : 0];
47292
48463
  if (expression.args.length !== 1 || !argument2) return null;
47293
48464
  if (named >= 0) {
47294
48465
  if (argument2.kind !== "litString" || !genericTypeName) return null;
47295
- return referenceTargetById(context, argument2.value, genericTypeName);
48466
+ return { spelling: "id", id: argument2.value, typeName: genericTypeName };
47296
48467
  }
47297
- const path = argument2 ? memberPath(argument2) : null;
48468
+ const resolved = referenceTargetBySymbol(context, argument2, genericTypeName);
48469
+ const path = memberPath(argument2);
48470
+ if (resolved === null || path === null) return null;
48471
+ return { spelling: "symbol", path, resolved };
48472
+ }
48473
+ function referenceTargetBySymbol(context, argument2, genericTypeName) {
48474
+ const path = memberPath(argument2);
47298
48475
  if (path === null) return null;
47299
48476
  const valueId = context.staticValueIdsBySymbol.get(path);
47300
48477
  if (valueId) {
47301
- const value = stateData(context, "value", valueId);
48478
+ const value = valueData(context, valueId);
47302
48479
  return {
47303
48480
  id: valueId,
47304
48481
  kind: "value",
@@ -47365,7 +48542,7 @@ function validateReferenceContract(context, member, target, ownerValueId) {
47365
48542
  const actualClassId = targetData && typeof targetData.classId === "string" ? targetData.classId : null;
47366
48543
  if (expectedClassId && actualClassId && !classAssignableToClass(context, actualClassId, expectedClassId)) {
47367
48544
  throw new Error(
47368
- `Lookup member ${member.name} target ${target.id} has an incompatible value type.`
48545
+ `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
48546
  );
47370
48547
  }
47371
48548
  if (target.genericTypeName && expectedTypeName && !referenceTypeNameAssignable(
@@ -47424,32 +48601,45 @@ function validateLookupMembership(context, member, targetId, ownerValueId) {
47424
48601
  `Lookup member ${member.name} references a missing collection member.`
47425
48602
  );
47426
48603
  }
47427
- const collectionValueIds = resolveLookupCollectionValueIds(
48604
+ const resolution = resolveLookupCollectionValueIds(
47428
48605
  context,
47429
48606
  member,
47430
48607
  collection,
47431
48608
  ownerValueId
47432
48609
  );
47433
- if (collectionValueIds.length === 0) {
48610
+ if (resolution.valueIds.length === 0) {
47434
48611
  throw new Error(
47435
- `Lookup member ${member.name} has no resolvable collection value for membership validation.`
48612
+ `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
48613
  );
47437
48614
  }
47438
- const present = collectionValueIds.some((collectionValueId) => {
48615
+ const present = resolution.valueIds.some((collectionValueId) => {
47439
48616
  const collectionValue = valueData(context, collectionValueId);
47440
48617
  if (!collectionValue) return false;
47441
48618
  const body = collectionValue.value;
47442
48619
  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
48620
  });
47444
- if (!present) {
48621
+ if (present) return;
48622
+ if (resolution.origin === "nameScan") {
47445
48623
  throw new Error(
47446
- `Lookup member ${member.name} target ${targetId} is not a member of collection ${collectionValueIds.join(", ")}.`
48624
+ `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
48625
  );
47448
48626
  }
48627
+ throw new Error(
48628
+ `Lookup member ${member.name} target ${targetId} is not a member of collection ${collectionMemberName(context, member)} (${resolution.valueIds.join(", ")}).`
48629
+ );
48630
+ }
48631
+ function collectionMemberName(context, member) {
48632
+ const collection = context.members.get(member.collectionMemberId);
48633
+ if (collection === void 0) return member.collectionMemberId;
48634
+ const owner = collection.owner;
48635
+ if (owner.kind !== "classMember") return collection.name;
48636
+ const ownerClass = context.classes.get(owner.classId);
48637
+ if (ownerClass === void 0) return collection.name;
48638
+ return `${ownerClass.name}.${collection.name}`;
47449
48639
  }
47450
48640
  function resolveLookupCollectionValueIds(context, member, collection, ownerValueId) {
47451
48641
  if (typeof member.collectionValueId === "string") {
47452
- return [member.collectionValueId];
48642
+ return { valueIds: [member.collectionValueId], origin: "declared" };
47453
48643
  }
47454
48644
  if (ownerValueId !== null) {
47455
48645
  const container = context.valuePlacements.containerBodyByPlacement.get(
@@ -47457,9 +48647,17 @@ function resolveLookupCollectionValueIds(context, member, collection, ownerValue
47457
48647
  );
47458
48648
  const siblingValueId = container?.[collection.name];
47459
48649
  if (typeof siblingValueId === "string") {
47460
- return [siblingValueId];
48650
+ return { valueIds: [siblingValueId], origin: "sibling" };
47461
48651
  }
47462
48652
  }
48653
+ const declaredRows = collectionRowsOfDeclaredMember(
48654
+ context,
48655
+ member.collectionMemberId,
48656
+ collection.name
48657
+ );
48658
+ if (declaredRows.size > 0) {
48659
+ return { valueIds: [...declaredRows], origin: "collectionMember" };
48660
+ }
47463
48661
  const candidates = /* @__PURE__ */ new Set();
47464
48662
  const collectionState = context.state[`member:${member.collectionMemberId}`];
47465
48663
  const collectionMemberData = isObjectRecord2(collectionState?.data) ? collectionState.data : {};
@@ -47471,21 +48669,92 @@ function resolveLookupCollectionValueIds(context, member, collection, ownerValue
47471
48669
  ) ?? []) {
47472
48670
  candidates.add(placedValueId);
47473
48671
  }
47474
- return [...candidates];
48672
+ return { valueIds: [...candidates], origin: "nameScan" };
48673
+ }
48674
+ function collectionRowsOfDeclaredMember(context, collectionMemberId, schemaKey) {
48675
+ const declaredMemberIds = declaredCollectionMemberIds(
48676
+ context,
48677
+ collectionMemberId
48678
+ );
48679
+ const rows = /* @__PURE__ */ new Set();
48680
+ for (const memberId of declaredMemberIds) {
48681
+ for (const valueId of context.valuePlacements.valueIdsByMemberId.get(
48682
+ memberId
48683
+ ) ?? []) {
48684
+ rows.add(valueId);
48685
+ }
48686
+ const bound = context.valuePlacements.valueIdByMemberId.get(memberId);
48687
+ if (bound !== void 0) rows.add(bound);
48688
+ }
48689
+ for (const placement of context.valuePlacements.placementsBySchemaKey.get(
48690
+ schemaKey
48691
+ ) ?? []) {
48692
+ if (placement.containerClassId === null) continue;
48693
+ const placedMemberId = classSchemaMemberId(
48694
+ context,
48695
+ placement.containerClassId,
48696
+ placement.schemaKey
48697
+ );
48698
+ if (placedMemberId === null) continue;
48699
+ if (!declaredMemberIds.has(placedMemberId)) continue;
48700
+ rows.add(placement.valueId);
48701
+ }
48702
+ return rows;
48703
+ }
48704
+ function valueOwningMemberIds(context, valueId) {
48705
+ const owners = /* @__PURE__ */ new Set();
48706
+ const pending = context.pendingValues.get(valueId);
48707
+ if (pending !== void 0) owners.add(pending.memberId);
48708
+ for (const memberId of context.valuePlacements.memberIdsByBoundValueId.get(
48709
+ valueId
48710
+ ) ?? []) {
48711
+ owners.add(memberId);
48712
+ }
48713
+ for (const placement of context.valuePlacements.placementsByValueId.get(
48714
+ valueId
48715
+ ) ?? []) {
48716
+ if (placement.containerClassId === null) continue;
48717
+ const memberId = classSchemaMemberId(
48718
+ context,
48719
+ placement.containerClassId,
48720
+ placement.schemaKey
48721
+ );
48722
+ if (memberId !== null) owners.add(memberId);
48723
+ }
48724
+ return owners;
48725
+ }
48726
+ function classSchemaMemberId(context, classId, schemaKey) {
48727
+ let current = classId;
48728
+ const visited = /* @__PURE__ */ new Set();
48729
+ while (current !== null && !visited.has(current)) {
48730
+ visited.add(current);
48731
+ const schemaClass2 = context.classes.get(current);
48732
+ if (schemaClass2 === void 0) return null;
48733
+ const memberId = schemaClass2.schema[schemaKey];
48734
+ if (typeof memberId === "string") return memberId;
48735
+ current = schemaClass2.extendsClassId;
48736
+ }
48737
+ return null;
47475
48738
  }
47476
- function indexValuePlacements(state) {
48739
+ function indexValuePlacements(state, options = {}) {
47477
48740
  const containerBodyByPlacement = /* @__PURE__ */ new Map();
47478
48741
  const mutablePlacedValueIds = /* @__PURE__ */ new Map();
47479
48742
  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;
48743
+ const mutableValueIdsByMemberId = /* @__PURE__ */ new Map();
48744
+ const placementsBySchemaKey = /* @__PURE__ */ new Map();
48745
+ const placementsByValueId = /* @__PURE__ */ new Map();
48746
+ for (const row of unionValueRows(state, options)) {
48747
+ if (row.containerId !== null) {
48748
+ const contained = valueIdsByContainerId.get(row.containerId) ?? /* @__PURE__ */ new Set();
48749
+ contained.add(row.id);
48750
+ valueIdsByContainerId.set(row.containerId, contained);
48751
+ }
48752
+ if (row.memberId !== null) {
48753
+ const owned = mutableValueIdsByMemberId.get(row.memberId) ?? [];
48754
+ owned.push(row.id);
48755
+ mutableValueIdsByMemberId.set(row.memberId, owned);
48756
+ }
48757
+ const body = isObjectRecord2(row.body) ? row.body : null;
47489
48758
  if (body === null) continue;
47490
48759
  for (const [memberName, childValueId] of Object.entries(body)) {
47491
48760
  if (typeof childValueId !== "string") continue;
@@ -47496,16 +48765,73 @@ function indexValuePlacements(state) {
47496
48765
  const placed = mutablePlacedValueIds.get(memberName) ?? /* @__PURE__ */ new Set();
47497
48766
  placed.add(childValueId);
47498
48767
  mutablePlacedValueIds.set(memberName, placed);
48768
+ const placement = {
48769
+ valueId: childValueId,
48770
+ schemaKey: memberName,
48771
+ containerValueId: row.id,
48772
+ containerClassId: row.classId
48773
+ };
48774
+ const byKey = placementsBySchemaKey.get(memberName) ?? [];
48775
+ byKey.push(placement);
48776
+ placementsBySchemaKey.set(memberName, byKey);
48777
+ const byValue = placementsByValueId.get(childValueId) ?? [];
48778
+ byValue.push(placement);
48779
+ placementsByValueId.set(childValueId, byValue);
47499
48780
  }
47500
48781
  }
48782
+ const valueIdByMemberId = /* @__PURE__ */ new Map();
48783
+ const memberIdsByBoundValueId = /* @__PURE__ */ new Map();
48784
+ for (const record3 of Object.values(state)) {
48785
+ if (record3.recordKind !== "member" || !isObjectRecord2(record3.data))
48786
+ continue;
48787
+ const valueId = record3.data.valueId;
48788
+ if (typeof valueId !== "string") continue;
48789
+ if (options.deletedValueIds?.has(valueId) === true) continue;
48790
+ valueIdByMemberId.set(record3.recordId, valueId);
48791
+ const bound = memberIdsByBoundValueId.get(valueId) ?? [];
48792
+ bound.push(record3.recordId);
48793
+ memberIdsByBoundValueId.set(valueId, bound);
48794
+ }
47501
48795
  return {
47502
48796
  containerBodyByPlacement,
47503
48797
  placedValueIdsByMemberName: new Map(
47504
48798
  [...mutablePlacedValueIds].map(([name, ids]) => [name, [...ids]])
47505
48799
  ),
47506
- valueIdsByContainerId
48800
+ valueIdsByContainerId,
48801
+ valueIdsByMemberId: mutableValueIdsByMemberId,
48802
+ placementsBySchemaKey,
48803
+ placementsByValueId,
48804
+ valueIdByMemberId,
48805
+ memberIdsByBoundValueId
47507
48806
  };
47508
48807
  }
48808
+ function unionValueRows(state, options) {
48809
+ const rows = [];
48810
+ const pendingValues = options.pendingValues;
48811
+ for (const record3 of Object.values(state)) {
48812
+ if (record3.recordKind !== "value" || !isObjectRecord2(record3.data)) continue;
48813
+ if (options.deletedValueIds?.has(record3.recordId) === true) continue;
48814
+ if (pendingValues?.has(record3.recordId) === true) continue;
48815
+ rows.push({
48816
+ id: record3.recordId,
48817
+ containerId: typeof record3.data.containerId === "string" ? record3.data.containerId : null,
48818
+ body: record3.data.value,
48819
+ memberId: typeof record3.data.memberId === "string" ? record3.data.memberId : null,
48820
+ classId: typeof record3.data.classId === "string" ? record3.data.classId : null
48821
+ });
48822
+ }
48823
+ for (const row of pendingValues?.values() ?? []) {
48824
+ if (options.deletedValueIds?.has(row.id) === true) continue;
48825
+ rows.push({
48826
+ id: row.id,
48827
+ containerId: row.containerId ?? null,
48828
+ body: row.value,
48829
+ memberId: row.memberId,
48830
+ classId: row.classId ?? null
48831
+ });
48832
+ }
48833
+ return rows;
48834
+ }
47509
48835
  function validateDialogueEligibility(context, member, dialogueId) {
47510
48836
  const dialogue = stateData(context, "dialogue", dialogueId);
47511
48837
  if (!dialogue) {
@@ -47960,7 +49286,6 @@ function classValue(context, member, value, visited, targetTyped, outerEnvironme
47960
49286
  const projection = constructorProjectionSource(
47961
49287
  context,
47962
49288
  classId,
47963
- schema,
47964
49289
  value.value,
47965
49290
  visited
47966
49291
  );
@@ -48061,23 +49386,28 @@ function manifestMemberTypeName(context, member) {
48061
49386
  if (member.kind === "scriptFunction" || member.kind === "function") {
48062
49387
  return "FunctionRef";
48063
49388
  }
48064
- return member.kind;
49389
+ return fixedSourceTypeNameForKind(member.kind) ?? member.kind;
48065
49390
  }
48066
- function constructorProjectionSource(context, classId, schema, body, visited) {
48067
- const projections = context.manifestClasses.get(classId)?.constructorProjections ?? [];
49391
+ function constructorProjectionSource(context, classId, body, visited) {
49392
+ const projections = inheritedConstructorProjections(
49393
+ context.manifestClasses,
49394
+ classId
49395
+ );
48068
49396
  const argumentsValue = [];
48069
49397
  const memberIds = /* @__PURE__ */ new Set();
48070
49398
  const targetValueIds = [];
48071
49399
  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;
49400
+ const schemaKey = inheritedProjectionSchemaKey(
49401
+ context.manifestClasses,
49402
+ classId,
49403
+ projection.memberId
49404
+ );
49405
+ const childValueId = schemaKey === null ? void 0 : body[schemaKey];
48076
49406
  const childValue = typeof childValueId === "string" ? context.values.get(childValueId) : void 0;
48077
49407
  const targetIds = Array.isArray(childValue?.value) ? childValue.value.filter(
48078
49408
  (entry) => typeof entry === "string"
48079
49409
  ) : [];
48080
- if (schemaKey === void 0 || typeof childValueId !== "string" || targetIds.length !== 1) {
49410
+ if (schemaKey === null || typeof childValueId !== "string" || targetIds.length !== 1) {
48081
49411
  throw new Error(
48082
49412
  `Class ${context.manifestClasses.get(classId)?.name ?? classId} constructor projection ${projection.parameterName} requires exactly one referenced value.`
48083
49413
  );
@@ -48364,10 +49694,62 @@ function referenceValue(context, member, body, dialogue) {
48364
49694
  const symbol = context.symbolsByValueId.get(id2);
48365
49695
  if (symbol !== void 0) return `Reference(${symbol})`;
48366
49696
  const type = dialogue ? "Dialogue" : memberReferenceType(context, member);
49697
+ if (!dialogue) {
49698
+ const key = uniqueIndexKeyForTarget(context, member, id2);
49699
+ if (key !== null) return `Reference<${type}>(key: ${quote4(key)})`;
49700
+ }
48367
49701
  return `Reference<${type}>(id: ${quote4(id2)})`;
48368
49702
  });
48369
49703
  return member.multiselect === true ? `[${values.join(", ")}]` : values[0] ?? "null";
48370
49704
  }
49705
+ function uniqueIndexKeyForTarget(context, member, targetId) {
49706
+ const manifestMember = context.manifestMembers.get(stringField2(member, "id"));
49707
+ if (manifestMember?.kind !== "lookup") return null;
49708
+ const collection = context.manifestMembers.get(
49709
+ manifestMember.collectionMemberId
49710
+ );
49711
+ if (collection?.kind !== "list") return null;
49712
+ const unique = (collection.indexes ?? []).filter((index) => index.unique);
49713
+ const only = unique.length === 1 ? unique[0] : void 0;
49714
+ if (only === void 0) return null;
49715
+ const key = indexedFieldValue(context, targetId, only.schemaKey);
49716
+ if (key === null || key.length === 0) return null;
49717
+ const siblings = collectionSiblingIds(context, collection, targetId);
49718
+ let holders = 0;
49719
+ for (const sibling of siblings) {
49720
+ if (indexedFieldValue(context, sibling, only.schemaKey) === key) {
49721
+ holders += 1;
49722
+ }
49723
+ }
49724
+ return holders === 1 ? key : null;
49725
+ }
49726
+ function indexedFieldValue(context, rowId, schemaKey) {
49727
+ const row = context.values.get(rowId);
49728
+ const body = row?.value;
49729
+ if (!isObjectRecord2(body)) return null;
49730
+ const fieldRowId = body[schemaKey];
49731
+ if (typeof fieldRowId !== "string") return null;
49732
+ const fieldValue = context.values.get(fieldRowId)?.value;
49733
+ return typeof fieldValue === "string" ? fieldValue : null;
49734
+ }
49735
+ function collectionSiblingIds(context, collection, targetId) {
49736
+ if (collection.listKind === "unordered") {
49737
+ const containerId = context.values.get(targetId)?.containerId;
49738
+ if (typeof containerId !== "string") return [targetId];
49739
+ const siblings = [];
49740
+ for (const [id2, row] of context.values) {
49741
+ if (row.containerId === containerId) siblings.push(id2);
49742
+ }
49743
+ return siblings;
49744
+ }
49745
+ for (const row of context.values.values()) {
49746
+ const body = row.value;
49747
+ if (!Array.isArray(body)) continue;
49748
+ if (!body.includes(targetId)) continue;
49749
+ return body.filter((entry) => typeof entry === "string");
49750
+ }
49751
+ return [targetId];
49752
+ }
48371
49753
  function functionReferenceValue(context, body) {
48372
49754
  if (!isObjectRecord2(body) || typeof body.functionMemberId !== "string") {
48373
49755
  throw new Error("FunctionRef value is missing functionMemberId.");
@@ -48571,7 +49953,7 @@ function numberOr2(value, fallback) {
48571
49953
  function quote4(value) {
48572
49954
  return quoteNeoString(value);
48573
49955
  }
48574
- var INFERRED_GENERIC_CLASS_PREFIX, MEMBER_KIND_DICTIONARY, MEMBER_KIND_LIST, MEMBER_KIND_CLASS, MEMBER_KIND_SOURCE_NAMES;
49956
+ var INFERRED_GENERIC_CLASS_PREFIX, MEMBER_KIND_DICTIONARY, MEMBER_KIND_LIST, MEMBER_KIND_CLASS;
48575
49957
  var init_value_sources = __esm({
48576
49958
  "src/project-source/value-sources.ts"() {
48577
49959
  "use strict";
@@ -48586,18 +49968,11 @@ var init_value_sources = __esm({
48586
49968
  init_member_value_id();
48587
49969
  init_members();
48588
49970
  init_structured_leaf_source();
49971
+ init_member_kind_type_names();
48589
49972
  INFERRED_GENERIC_CLASS_PREFIX = "__inferred_class__:";
48590
49973
  MEMBER_KIND_DICTIONARY = 5;
48591
49974
  MEMBER_KIND_LIST = 6;
48592
49975
  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
49976
  }
48602
49977
  });
48603
49978
 
@@ -48662,7 +50037,7 @@ ${declarations.join("\n\n")}
48662
50037
  ])
48663
50038
  };
48664
50039
  }
48665
- function lowerProjectRootSourceV4(state, analysis, manifest) {
50040
+ function lowerProjectRootSourceV4(state, analysis, manifest, options = {}) {
48666
50041
  const validated = validateProjectRootEnvelopeV4(state, analysis);
48667
50042
  if (validated === null) {
48668
50043
  return { records: [], memberValueIds: /* @__PURE__ */ new Map(), seeds: /* @__PURE__ */ new Map() };
@@ -48675,7 +50050,10 @@ function lowerProjectRootSourceV4(state, analysis, manifest) {
48675
50050
  source: global.source,
48676
50051
  label: `root.${expected.name}`
48677
50052
  }));
48678
- return lowerStoredValueBindingsV4(state, manifest, bindings, { analysis });
50053
+ return lowerStoredValueBindingsV4(state, manifest, bindings, {
50054
+ analysis,
50055
+ registry: options.registry
50056
+ });
48679
50057
  }
48680
50058
  function validateProjectRootEnvelopeV4(state, analysis) {
48681
50059
  const project = Object.values(state).find(
@@ -48747,7 +50125,120 @@ function requiredObject(value, label) {
48747
50125
  function quote5(value) {
48748
50126
  return quoteNeoString(value);
48749
50127
  }
48750
- var ROOT_PROJECT_FIELDS;
50128
+ function resolveRootPathCollectionValuesV4(args) {
50129
+ const resolved = /* @__PURE__ */ new Map();
50130
+ for (const member of args.manifest.members) {
50131
+ if (member.kind !== "lookup") continue;
50132
+ const spelled = member.collectionValueId;
50133
+ if (typeof spelled !== "string") continue;
50134
+ if (!spelled.startsWith(ROOT_PATH_PENDING_PREFIX)) continue;
50135
+ const path = spelled.slice(ROOT_PATH_PENDING_PREFIX.length);
50136
+ const site = args.siteForMember(member.id);
50137
+ const fail = (message) => {
50138
+ args.registry.loweringFailures.push({
50139
+ message,
50140
+ site: {
50141
+ uri: site?.uri ?? "<schema>",
50142
+ declarationStart: site?.start ?? { line: 1, character: 0 },
50143
+ expression: null,
50144
+ label: member.name
50145
+ }
50146
+ });
50147
+ };
50148
+ const target = walkRootValuePath(path, args.state, args.registry, fail);
50149
+ if (target !== null) resolved.set(member.id, target);
50150
+ }
50151
+ return resolved;
50152
+ }
50153
+ function walkRootValuePath(path, state, registry, fail) {
50154
+ const segments = path.split(".");
50155
+ const slotName = segments[1];
50156
+ if (segments[0] !== "root" || slotName === void 0) {
50157
+ fail(
50158
+ `collectionValue path ${path} must start at a root slot (root.Assets, root.Save, or root.Session).`
50159
+ );
50160
+ return null;
50161
+ }
50162
+ const slotIndex = PROJECT_ROOT_SOURCE_SLOTS.findIndex(
50163
+ (slot) => slot.name === slotName
50164
+ );
50165
+ const project = Object.values(state).find(
50166
+ (record3) => record3.recordKind === "project"
50167
+ );
50168
+ if (slotIndex < 0 || project === void 0 || !isObjectRecord2(project.data)) {
50169
+ fail(
50170
+ `collectionValue path ${path} names root slot ${slotName ?? "<none>"}, which is not Assets, Save, or Session.`
50171
+ );
50172
+ return null;
50173
+ }
50174
+ const memberId = rootMemberIds(project.data)[slotIndex];
50175
+ const slotMember = memberId === void 0 ? void 0 : state[`member:${memberId}`];
50176
+ const slotValueId = isObjectRecord2(slotMember?.data) ? slotMember.data.valueId : void 0;
50177
+ if (typeof slotValueId !== "string") {
50178
+ fail(
50179
+ `collectionValue path ${path} cannot start: root slot ${slotName} has no stable value row.`
50180
+ );
50181
+ return null;
50182
+ }
50183
+ let cursor = slotValueId;
50184
+ for (const segment of segments.slice(2)) {
50185
+ const body = registry.pendingValues.get(cursor)?.value ?? (isObjectRecord2(state[`value:${cursor}`]?.data) ? (state[`value:${cursor}`]?.data).value : void 0);
50186
+ if (Array.isArray(body)) {
50187
+ fail(
50188
+ `collectionValue path ${path} travels through a list at segment ${segment}; a path cannot choose a list entry. Name the row with Reference(id: ...) instead.`
50189
+ );
50190
+ return null;
50191
+ }
50192
+ if (!isObjectRecord2(body)) {
50193
+ fail(
50194
+ `collectionValue path ${path} stops before segment ${segment}: the row it reaches stores no single-valued members.`
50195
+ );
50196
+ return null;
50197
+ }
50198
+ const next = body[segment];
50199
+ if (typeof next !== "string") {
50200
+ fail(
50201
+ `collectionValue path ${path} has no value at segment ${segment} after this push.`
50202
+ );
50203
+ return null;
50204
+ }
50205
+ cursor = next;
50206
+ }
50207
+ return cursor;
50208
+ }
50209
+ function rootValuePathsByValueId(records2) {
50210
+ const paths = /* @__PURE__ */ new Map();
50211
+ const project = [...records2.values()].find(
50212
+ (record3) => record3.recordKind === "project"
50213
+ );
50214
+ if (project === void 0 || !isObjectRecord2(project.data)) return paths;
50215
+ const queue = [];
50216
+ ROOT_PROJECT_FIELDS.forEach((field, index) => {
50217
+ const slot = PROJECT_ROOT_SOURCE_SLOTS[index];
50218
+ const memberId = isObjectRecord2(project.data) ? project.data[field] : void 0;
50219
+ if (slot === void 0 || typeof memberId !== "string") return;
50220
+ const member = records2.get(`member:${memberId}`);
50221
+ const valueId = isObjectRecord2(member?.data) ? member.data.valueId : void 0;
50222
+ if (typeof valueId !== "string") return;
50223
+ queue.push({ id: valueId, path: `root.${slot.name}` });
50224
+ });
50225
+ while (queue.length > 0) {
50226
+ const next = queue.shift();
50227
+ if (next === void 0) break;
50228
+ if (paths.has(next.id)) continue;
50229
+ paths.set(next.id, next.path);
50230
+ const row = records2.get(`value:${next.id}`);
50231
+ const body = isObjectRecord2(row?.data) ? row.data.value : void 0;
50232
+ if (!isObjectRecord2(body)) continue;
50233
+ for (const [schemaKey, child] of Object.entries(body)) {
50234
+ if (typeof child !== "string") continue;
50235
+ if (!records2.has(`value:${child}`)) continue;
50236
+ queue.push({ id: child, path: `${next.path}.${schemaKey}` });
50237
+ }
50238
+ }
50239
+ return paths;
50240
+ }
50241
+ var ROOT_PROJECT_FIELDS, ROOT_PATH_PENDING_PREFIX;
48751
50242
  var init_root_source = __esm({
48752
50243
  "src/project-source/root-source.ts"() {
48753
50244
  "use strict";
@@ -48760,6 +50251,7 @@ var init_root_source = __esm({
48760
50251
  "rootSaveFileMemberId",
48761
50252
  "rootSessionMemberId"
48762
50253
  ];
50254
+ ROOT_PATH_PENDING_PREFIX = "__pending__:root-path:";
48763
50255
  }
48764
50256
  });
48765
50257
 
@@ -48788,7 +50280,8 @@ function emitProjectDocumentFilesV4(records2) {
48788
50280
  staticInitializers: staticValues.initializers,
48789
50281
  defaultInitializers: memberDefaults.initializers,
48790
50282
  fileSymbols: qualifiedProjectFileSymbolsV4(records2),
48791
- relationEndpointExpressions: relationEndpointExpressions(records2)
50283
+ relationEndpointExpressions: relationEndpointExpressions(records2),
50284
+ collectionValuePaths: rootValuePathsByValueId(records2)
48792
50285
  });
48793
50286
  const source = {
48794
50287
  ...baseSource,
@@ -51061,9 +52554,11 @@ function isWorldAnimationStructuralMember(memberId, members) {
51061
52554
  function assertAnimationClipDocumentValid(document) {
51062
52555
  const context = new AnimationValidationContext(document);
51063
52556
  context.validateConstructorProjections();
52557
+ context.validateSegments();
51064
52558
  for (const member of document.members) {
51065
52559
  if (!isMemberClass(member)) continue;
51066
52560
  if (!context.classHasWorldKind(member.classId, "animationClip")) continue;
52561
+ if (member.isAbstract === true) continue;
51067
52562
  context.validateClipMember(member);
51068
52563
  }
51069
52564
  }
@@ -51134,7 +52629,7 @@ var init_animation_clips = __esm({
51134
52629
  }
51135
52630
  const parameterNames = /* @__PURE__ */ new Set();
51136
52631
  const memberIds = /* @__PURE__ */ new Set();
51137
- const directMemberIds = new Set(Object.values(schemaClass2.schema));
52632
+ const effectiveMemberIds = this.effectiveSchemaMemberIds(schemaClass2);
51138
52633
  for (const projection of projections) {
51139
52634
  if (parameterNames.has(projection.parameterName)) {
51140
52635
  throw new Error(
@@ -51146,9 +52641,9 @@ var init_animation_clips = __esm({
51146
52641
  `Class "${schemaClass2.name}" projects multiple constructor parameters onto member "${projection.memberId}".`
51147
52642
  );
51148
52643
  }
51149
- if (!directMemberIds.has(projection.memberId)) {
52644
+ if (!effectiveMemberIds.has(projection.memberId)) {
51150
52645
  throw new Error(
51151
- `Class "${schemaClass2.name}" constructor parameter "${projection.parameterName}" references member "${projection.memberId}" outside its direct schema.`
52646
+ `Class "${schemaClass2.name}" constructor parameter "${projection.parameterName}" references member "${projection.memberId}", which is neither in its schema nor inherited.`
51152
52647
  );
51153
52648
  }
51154
52649
  parameterNames.add(projection.parameterName);
@@ -51156,6 +52651,27 @@ var init_animation_clips = __esm({
51156
52651
  }
51157
52652
  }
51158
52653
  }
52654
+ /**
52655
+ * Every member id a class resolves under a schema key — its own, then each
52656
+ * ancestor's.
52657
+ *
52658
+ * A projection may name an inherited member: P48 §2.1 moved `Child` onto
52659
+ * `NeoAnimationTrackBase`, and `new NeoAnimationChildTrack(id: "…")` still
52660
+ * projects onto that one member from a class that no longer declares it.
52661
+ */
52662
+ effectiveSchemaMemberIds(schemaClass2) {
52663
+ const memberIds = /* @__PURE__ */ new Set();
52664
+ const visited = /* @__PURE__ */ new Set();
52665
+ let current = schemaClass2;
52666
+ while (current !== void 0 && !visited.has(current.id)) {
52667
+ visited.add(current.id);
52668
+ for (const memberId of Object.values(current.schema)) {
52669
+ memberIds.add(memberId);
52670
+ }
52671
+ current = current.extendsClassId === void 0 ? void 0 : this.classById.get(current.extendsClassId);
52672
+ }
52673
+ return memberIds;
52674
+ }
51159
52675
  classHasWorldKind(classId, worldKind) {
51160
52676
  const visited = /* @__PURE__ */ new Set();
51161
52677
  let current = this.classById.get(classId);
@@ -51172,16 +52688,8 @@ var init_animation_clips = __esm({
51172
52688
  WORLD_ANIMATION_CLIP_TARGET_PARAM_ID,
51173
52689
  `Animation clip "${clipMember.name}"`
51174
52690
  );
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
52691
  const clipNode = this.memberRootNode(clipMember);
51184
- const fps = this.requirePositiveIntegerField(
52692
+ this.requirePositiveIntegerField(
51185
52693
  clipNode,
51186
52694
  clipMember.classId,
51187
52695
  WORLD_ANIMATION_CLIP_FPS_MEMBER_ID,
@@ -51210,7 +52718,7 @@ var init_animation_clips = __esm({
51210
52718
  const index = this.requireIntegerField(
51211
52719
  frameNode,
51212
52720
  frameClassId,
51213
- WORLD_ANIMATION_FRAME_INDEX_MEMBER_ID,
52721
+ WORLD_ANIMATION_FRAME_BASE_INDEX_MEMBER_ID,
51214
52722
  `Animation clip "${clipMember.name}" frame Index`
51215
52723
  );
51216
52724
  if (index < 0 || index >= duration) {
@@ -51258,13 +52766,12 @@ var init_animation_clips = __esm({
51258
52766
  targetClassId
51259
52767
  });
51260
52768
  }
51261
- this.validateChildTracks({
52769
+ this.validateTracks({
51262
52770
  clipName: clipMember.name,
51263
52771
  clipNode,
51264
52772
  clipClassId: clipMember.classId,
51265
- targetClassId,
51266
52773
  childIds: authoredChildren.ids,
51267
- fps,
52774
+ childStoragePath: authoredChildren.storagePath,
51268
52775
  duration
51269
52776
  });
51270
52777
  }
@@ -51377,97 +52884,401 @@ var init_animation_clips = __esm({
51377
52884
  );
51378
52885
  }
51379
52886
  }
51380
- validateChildTracks(args) {
52887
+ /**
52888
+ * `NeoAnimationClip.Tracks` holds `NeoAnimationTrackBase` rows since P48
52889
+ * §2.1, so a row's kind is a property of the row rather than of the list.
52890
+ * Everything the base declares — `Child`, `StartFrame`, `Direction`, the
52891
+ * crop window — is validated once here against the base member ids, which
52892
+ * the segment track's covariant `Child` override still descends from; the
52893
+ * per-kind passes below see an already-checked child.
52894
+ */
52895
+ validateTracks(args) {
51381
52896
  for (const track of this.requireListField(
51382
52897
  args.clipNode,
51383
52898
  args.clipClassId,
51384
52899
  WORLD_ANIMATION_CLIP_TRACKS_MEMBER_ID,
51385
52900
  `Animation clip "${args.clipName}" Tracks`
51386
52901
  )) {
51387
- const trackClassId = this.requireNodeClassId(
52902
+ const row = this.requireTrackRow(track, args.clipName);
52903
+ const label = `Animation clip "${args.clipName}" ${row.trackLabel} "${track.id}"`;
52904
+ const child = this.validateTrackBase({
51388
52905
  track,
51389
- "animationChildTrack"
52906
+ trackClassId: row.classId,
52907
+ childIds: args.childIds,
52908
+ duration: args.duration,
52909
+ label
52910
+ });
52911
+ if (row.kind === "childClip") {
52912
+ this.validateChildClipTrack({
52913
+ track,
52914
+ trackClassId: row.classId,
52915
+ childId: child.childId,
52916
+ childClassId: child.childClassId,
52917
+ childNode: child.childNode,
52918
+ label
52919
+ });
52920
+ continue;
52921
+ }
52922
+ this.validateSegmentTrack({
52923
+ trackClassId: row.classId,
52924
+ childClassId: child.childClassId,
52925
+ childId: child.childId,
52926
+ childStoragePath: args.childStoragePath,
52927
+ label
52928
+ });
52929
+ }
52930
+ }
52931
+ /**
52932
+ * A `Tracks` row's concrete kind. Dispatching on the row rather than on the
52933
+ * list is the whole point of the base class, and a row that is neither
52934
+ * shipped kind is named — with its class — rather than reported as "not a
52935
+ * animationChildTrack row", which was true of every segment track too.
52936
+ */
52937
+ requireTrackRow(track, clipName) {
52938
+ const classId = track.classId;
52939
+ if (typeof classId !== "string") {
52940
+ throw new Error(
52941
+ `Animation clip "${clipName}" track value "${track.id}" has no class, so its track kind cannot be resolved.`
51390
52942
  );
51391
- const childId = this.requireSingleLookupField(
51392
- track,
51393
- trackClassId,
51394
- WORLD_ANIMATION_CHILD_TRACK_CHILD_MEMBER_ID,
51395
- `Animation clip "${args.clipName}" child track`
52943
+ }
52944
+ if (this.classHasWorldKind(classId, "animationChildTrack")) {
52945
+ return { classId, kind: "childClip", trackLabel: "child track" };
52946
+ }
52947
+ if (this.classHasWorldKind(classId, "animationSegmentTrack")) {
52948
+ return { classId, kind: "segment", trackLabel: "segment track" };
52949
+ }
52950
+ const className = this.classById.get(classId)?.name ?? classId;
52951
+ throw new Error(
52952
+ `Animation clip "${clipName}" track value "${track.id}" has class "${className}", which is neither a child clip track nor a segment track.`
52953
+ );
52954
+ }
52955
+ /**
52956
+ * The members `NeoAnimationTrackBase` declares, for either kind of row.
52957
+ *
52958
+ * P48 §2.3 deletes P29's fit error: content that runs past the owning clip's
52959
+ * end truncates, because clipping is what a clip does. What survives is a row
52960
+ * that can never play at all — a `StartFrame` outside the clip, or a crop
52961
+ * window the author wrote empty or inverted. Crop bounds against the
52962
+ * *resolved* content are runtime-clamped instead, since a lookup-backed
52963
+ * segment's length is instance data this document does not have.
52964
+ */
52965
+ validateTrackBase(args) {
52966
+ const childId = this.requireSingleLookupField(
52967
+ args.track,
52968
+ args.trackClassId,
52969
+ WORLD_ANIMATION_TRACK_CHILD_MEMBER_ID,
52970
+ args.label
52971
+ );
52972
+ if (!args.childIds.has(childId)) {
52973
+ throw new Error(
52974
+ `${args.label} references child "${childId}" outside the owner's authored Children graph.`
51396
52975
  );
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`
52976
+ }
52977
+ const childNode = this.valueById.get(childId);
52978
+ const childClassId = childNode?.classId;
52979
+ if (childNode === void 0 || typeof childClassId !== "string") {
52980
+ throw new Error(
52981
+ `${args.label} references missing class-backed child "${childId}".`
51414
52982
  );
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
52983
+ }
52984
+ const startFrame = this.requireIntegerField(
52985
+ args.track,
52986
+ args.trackClassId,
52987
+ WORLD_ANIMATION_TRACK_START_FRAME_MEMBER_ID,
52988
+ `${args.label} StartFrame`
52989
+ );
52990
+ if (startFrame < 0) {
52991
+ throw new Error(`${args.label} StartFrame ${startFrame} is negative.`);
52992
+ }
52993
+ if (startFrame >= args.duration) {
52994
+ throw new Error(
52995
+ `${args.label} StartFrame ${startFrame} is at or past the owning clip's Duration ${args.duration}, so the row can never play.`
51439
52996
  );
51440
- if (childClipNode === null) {
51441
- throw new Error(
51442
- `Child clip "${clipKey}" on child "${childId}" has no value graph.`
51443
- );
52997
+ }
52998
+ this.validateTrackDirection(args.track, args.trackClassId, args.label);
52999
+ this.validateTrackCropWindow(args.track, args.trackClassId, args.label);
53000
+ return { childId, childClassId, childNode };
53001
+ }
53002
+ validateTrackDirection(track, trackClassId, label) {
53003
+ const authored = this.scalarField(
53004
+ track,
53005
+ trackClassId,
53006
+ WORLD_ANIMATION_TRACK_DIRECTION_MEMBER_ID
53007
+ );
53008
+ if (authored === void 0 || authored === null) return;
53009
+ if (!Array.isArray(authored) || authored.length !== 1 || authored[0] !== WORLD_PLAY_DIRECTION_FORWARD_OPTION_ID && authored[0] !== WORLD_PLAY_DIRECTION_REVERSE_OPTION_ID) {
53010
+ throw new Error(
53011
+ `${label} Direction must be exactly one NeoPlayDirection option.`
53012
+ );
53013
+ }
53014
+ }
53015
+ validateTrackCropWindow(track, trackClassId, label) {
53016
+ const start = this.optionalIntegerField(
53017
+ track,
53018
+ trackClassId,
53019
+ WORLD_ANIMATION_TRACK_OFFSET_START_INDEX_MEMBER_ID,
53020
+ `${label} OffsetStartIndex`
53021
+ );
53022
+ const end = this.optionalIntegerField(
53023
+ track,
53024
+ trackClassId,
53025
+ WORLD_ANIMATION_TRACK_OFFSET_END_INDEX_MEMBER_ID,
53026
+ `${label} OffsetEndIndex`
53027
+ );
53028
+ if (start !== null && start < 0) {
53029
+ throw new Error(`${label} OffsetStartIndex ${start} is negative.`);
53030
+ }
53031
+ if (end !== null && end < 1) {
53032
+ throw new Error(
53033
+ `${label} OffsetEndIndex ${end} must be at least 1; a window has to contain a frame.`
53034
+ );
53035
+ }
53036
+ if (start === null || end === null) return;
53037
+ if (end <= start) {
53038
+ throw new Error(
53039
+ `${label} crop window [${start}, ${end}) is empty or inverted, so the row can never play.`
53040
+ );
53041
+ }
53042
+ }
53043
+ validateChildClipTrack(args) {
53044
+ const clipKey = this.requireStringField(
53045
+ args.track,
53046
+ args.trackClassId,
53047
+ WORLD_ANIMATION_CHILD_TRACK_CLIP_KEY_MEMBER_ID,
53048
+ `${args.label} ClipKey`
53049
+ );
53050
+ const childClipEntry = mergeInstanceSurfaceSchema(
53051
+ args.childClassId,
53052
+ this.document.classes,
53053
+ this.document.members
53054
+ ).find((entry) => entry.schemaKey === clipKey);
53055
+ if (childClipEntry === void 0) {
53056
+ throw new Error(
53057
+ `${args.label} ClipKey "${clipKey}" does not resolve to a compatible clip on child "${args.childId}".`
53058
+ );
53059
+ }
53060
+ const childClipMember = this.memberById.get(childClipEntry.memberId);
53061
+ if (!isMemberClass(childClipMember) || !this.classHasWorldKind(childClipMember.classId, "animationClip") || this.requireClassBinding(
53062
+ childClipMember,
53063
+ WORLD_ANIMATION_CLIP_TARGET_PARAM_ID,
53064
+ `Child clip "${clipKey}"`
53065
+ ) !== args.childClassId) {
53066
+ throw new Error(
53067
+ `${args.label} ClipKey "${clipKey}" does not resolve to a compatible clip on child "${args.childId}".`
53068
+ );
53069
+ }
53070
+ const childClipNode = this.resolveDefinitionChild(
53071
+ args.childNode,
53072
+ childClipEntry.schemaKey,
53073
+ childClipMember
53074
+ );
53075
+ if (childClipNode === null) {
53076
+ throw new Error(
53077
+ `Child clip "${clipKey}" on child "${args.childId}" has no value graph.`
53078
+ );
53079
+ }
53080
+ this.requirePositiveIntegerField(
53081
+ childClipNode,
53082
+ childClipMember.classId,
53083
+ WORLD_ANIMATION_CLIP_FPS_MEMBER_ID,
53084
+ `Child clip "${clipKey}" FPS`
53085
+ );
53086
+ this.requirePositiveIntegerField(
53087
+ childClipNode,
53088
+ childClipMember.classId,
53089
+ WORLD_ANIMATION_CLIP_DURATION_MEMBER_ID,
53090
+ `Child clip "${clipKey}" Duration`
53091
+ );
53092
+ }
53093
+ /**
53094
+ * P48 §7's target rule, as much of it as the record layer can answer.
53095
+ *
53096
+ * The write target is schema metadata — `@settings(target: Class.Member)` on
53097
+ * a concrete track class — so it is resolved from the row's class chain and
53098
+ * checked against the class's *bound* `TChild`, not against whatever child
53099
+ * this particular row happens to name. The instance side is checked too:
53100
+ * a row whose `Child` is not a `TChild` would type-check a getter body
53101
+ * against a class the row cannot deliver.
53102
+ *
53103
+ * What stays in the language (phase 2) is everything about the `Segment`
53104
+ * implementation itself — that every concrete subclass has one, and that a
53105
+ * getter body type-checks against `TChild`.
53106
+ */
53107
+ validateSegmentTrack(args) {
53108
+ const trackClassName = this.classById.get(args.trackClassId)?.name ?? args.trackClassId;
53109
+ const targetMemberId = this.resolveTargetMemberId(args.trackClassId);
53110
+ if (targetMemberId === null) {
53111
+ throw new Error(
53112
+ `${args.label} class "${trackClassName}" declares no target member, so it has nothing to write.`
53113
+ );
53114
+ }
53115
+ const env = resolveGenericEnv(args.trackClassId, this.document.classes);
53116
+ const boundChildClassId = this.envClassBinding(
53117
+ env,
53118
+ WORLD_ANIMATION_SEGMENT_TRACK_CHILD_PARAM_ID
53119
+ );
53120
+ if (boundChildClassId === null) {
53121
+ throw new Error(
53122
+ `${args.label} class "${trackClassName}" does not bind its TChild generic to a Class member.`
53123
+ );
53124
+ }
53125
+ if (!this.classDescendsFrom(args.childClassId, boundChildClassId)) {
53126
+ const boundName = this.classById.get(boundChildClassId)?.name ?? boundChildClassId;
53127
+ const childName = this.classById.get(args.childClassId)?.name ?? args.childClassId;
53128
+ throw new Error(
53129
+ `${args.label} plays against child "${args.childId}" of class "${childName}", which does not descend from the track's bound TChild "${boundName}".`
53130
+ );
53131
+ }
53132
+ const targetEntry = mergeStoredInstanceSchema(
53133
+ boundChildClassId,
53134
+ this.document.classes,
53135
+ this.document.members
53136
+ ).find((entry) => this.memberDescendsFrom(entry.memberId, targetMemberId));
53137
+ const boundChildName = this.classById.get(boundChildClassId)?.name ?? boundChildClassId;
53138
+ if (targetEntry === void 0) {
53139
+ throw new Error(
53140
+ `${args.label} class "${trackClassName}" targets member "${targetMemberId}", which "${boundChildName}" does not declare.`
53141
+ );
53142
+ }
53143
+ const targetMember = this.memberById.get(targetEntry.memberId);
53144
+ if (targetMember === void 0) {
53145
+ throw new Error(
53146
+ `${args.label} target member "${targetEntry.memberId}" is not in this document.`
53147
+ );
53148
+ }
53149
+ const valueBindingMember = this.envBindingMember(
53150
+ env,
53151
+ WORLD_ANIMATION_SEGMENT_TRACK_VALUE_PARAM_ID
53152
+ );
53153
+ if (valueBindingMember === null) {
53154
+ throw new Error(
53155
+ `${args.label} class "${trackClassName}" does not bind its TValue generic to a member.`
53156
+ );
53157
+ }
53158
+ if (targetMember.kind !== valueBindingMember.kind) {
53159
+ throw new Error(
53160
+ `${args.label} targets "${boundChildName}.${targetEntry.schemaKey}" of kind ${MemberKind[targetMember.kind]}, but its segment plays ${MemberKind[valueBindingMember.kind]} values.`
53161
+ );
53162
+ }
53163
+ if (!this.overrideLeafEligible(targetMember, [
53164
+ ...args.childStoragePath,
53165
+ targetMember.id
53166
+ ])) {
53167
+ throw new Error(
53168
+ `${args.label} targets "${boundChildName}.${targetEntry.schemaKey}", which is not a writable Save/Session override leaf.`
53169
+ );
53170
+ }
53171
+ }
53172
+ /** The `@settings(target:)` member a track class or one of its bases names. */
53173
+ resolveTargetMemberId(classId) {
53174
+ const visited = /* @__PURE__ */ new Set();
53175
+ let current = this.classById.get(classId);
53176
+ while (current !== void 0 && !visited.has(current.id)) {
53177
+ visited.add(current.id);
53178
+ if (typeof current.targetMemberId === "string") {
53179
+ return current.targetMemberId;
51444
53180
  }
51445
- const childFps = this.requirePositiveIntegerField(
51446
- childClipNode,
51447
- childClipMember.classId,
51448
- WORLD_ANIMATION_CLIP_FPS_MEMBER_ID,
51449
- `Child clip "${clipKey}" FPS`
53181
+ current = current.extendsClassId ? this.classById.get(current.extendsClassId) : void 0;
53182
+ }
53183
+ return null;
53184
+ }
53185
+ envBindingMember(env, paramId) {
53186
+ const entry = env.get(paramId);
53187
+ if (entry === void 0 || entry.kind !== "member") return null;
53188
+ if (typeof entry.memberId !== "string") return null;
53189
+ return this.memberById.get(entry.memberId) ?? null;
53190
+ }
53191
+ envClassBinding(env, paramId) {
53192
+ const member = this.envBindingMember(env, paramId);
53193
+ return isMemberClass(member) ? member.classId : null;
53194
+ }
53195
+ classDescendsFrom(classId, baseClassId) {
53196
+ const visited = /* @__PURE__ */ new Set();
53197
+ let current = this.classById.get(classId);
53198
+ while (current !== void 0 && !visited.has(current.id)) {
53199
+ if (current.id === baseClassId) return true;
53200
+ visited.add(current.id);
53201
+ current = current.extendsClassId ? this.classById.get(current.extendsClassId) : void 0;
53202
+ }
53203
+ return false;
53204
+ }
53205
+ /**
53206
+ * P48 §1 and §7's segment rules, for every segment the document holds.
53207
+ *
53208
+ * Segments are validated by *value*, not by clip: a segment is catalog data
53209
+ * that no clip owns, and the same rows are reachable from every track that
53210
+ * resolves through them. Both authoring shapes are walked — a member's own
53211
+ * default graph and a standalone row — because a stored segment member and a
53212
+ * segment sitting in a list are the same data written two ways.
53213
+ *
53214
+ * Storage is deliberately not asked about (§1.2): Immutable catalogs, Save
53215
+ * files, and Session all hold segments, and a Session-stored `Duration` a
53216
+ * game writes at runtime is a feature, not drift.
53217
+ */
53218
+ validateSegments() {
53219
+ const visited = /* @__PURE__ */ new Set();
53220
+ for (const member of this.document.members) {
53221
+ if (!isMemberClass(member)) continue;
53222
+ if (!this.classHasWorldKind(member.classId, "animationSegment")) continue;
53223
+ const node = this.optionalMemberRootNode(member);
53224
+ if (node === null) continue;
53225
+ this.validateSegmentNode(
53226
+ node,
53227
+ member.classId,
53228
+ `Animation segment "${member.name}"`,
53229
+ visited
51450
53230
  );
51451
- const childDuration = this.requirePositiveIntegerField(
51452
- childClipNode,
51453
- childClipMember.classId,
51454
- WORLD_ANIMATION_CLIP_DURATION_MEMBER_ID,
51455
- `Child clip "${clipKey}" Duration`
53231
+ }
53232
+ for (const row of this.document.values) {
53233
+ const classId = row.classId;
53234
+ if (typeof classId !== "string") continue;
53235
+ if (!this.classHasWorldKind(classId, "animationSegment")) continue;
53236
+ this.validateSegmentNode(
53237
+ row,
53238
+ classId,
53239
+ `Animation segment value "${row.id}"`,
53240
+ visited
51456
53241
  );
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`
53242
+ }
53243
+ }
53244
+ validateSegmentNode(node, classId, label, visited) {
53245
+ if (visited.has(node.id)) return;
53246
+ visited.add(node.id);
53247
+ const duration = this.requirePositiveIntegerField(
53248
+ node,
53249
+ classId,
53250
+ WORLD_ANIMATION_SEGMENT_DURATION_MEMBER_ID,
53251
+ `${label} Duration`
53252
+ );
53253
+ const frameValueIdByIndex = /* @__PURE__ */ new Map();
53254
+ for (const frameNode of this.requireListField(
53255
+ node,
53256
+ classId,
53257
+ WORLD_ANIMATION_SEGMENT_FRAMES_MEMBER_ID,
53258
+ `${label} Frames`
53259
+ )) {
53260
+ const frameClassId = this.requireNodeClassId(
53261
+ frameNode,
53262
+ "animationSegmentFrame"
51462
53263
  );
51463
- const childLengthInParentFrames = Math.ceil(
51464
- childDuration * args.fps / childFps
53264
+ const index = this.requireIntegerField(
53265
+ frameNode,
53266
+ frameClassId,
53267
+ WORLD_ANIMATION_FRAME_BASE_INDEX_MEMBER_ID,
53268
+ `${label} frame Index`
51465
53269
  );
51466
- if (startFrame < 0 || startFrame + childLengthInParentFrames > args.duration) {
53270
+ if (index < 0 || index >= duration) {
51467
53271
  throw new Error(
51468
- `Animation clip "${args.clipName}" child track "${clipKey}" does not fit: StartFrame ${startFrame} + ${childLengthInParentFrames} parent frames exceeds Duration ${args.duration}.`
53272
+ `${label} frame ${index} is outside Duration ${duration}; shrink operations must delete trailing frames.`
51469
53273
  );
51470
53274
  }
53275
+ const priorFrameValueId = frameValueIdByIndex.get(index);
53276
+ if (priorFrameValueId !== void 0) {
53277
+ throw new Error(
53278
+ `${label} has duplicate frame Index ${index} in values "${priorFrameValueId}" and "${frameNode.id}".`
53279
+ );
53280
+ }
53281
+ frameValueIdByIndex.set(index, frameNode.id);
51471
53282
  }
51472
53283
  }
51473
53284
  validateSparseOverride(args) {
@@ -51868,6 +53679,20 @@ var init_animation_clips = __esm({
51868
53679
  }
51869
53680
  return value;
51870
53681
  }
53682
+ /**
53683
+ * {@link requireIntegerField} for a nullable int member such as the P48 §2.1
53684
+ * crop window. `null` covers both "authored null" and "no value and no
53685
+ * default"; anything that is neither null nor a safe integer still throws,
53686
+ * so a garbage crop bound is not silently read as an open window.
53687
+ */
53688
+ optionalIntegerField(parent, classId, memberId, label) {
53689
+ const value = this.scalarField(parent, classId, memberId);
53690
+ if (value === null || value === void 0) return null;
53691
+ if (typeof value !== "number" || !Number.isSafeInteger(value)) {
53692
+ throw new Error(`${label} must be an integer or null.`);
53693
+ }
53694
+ return value;
53695
+ }
51871
53696
  requireStringField(parent, classId, memberId, label) {
51872
53697
  const value = this.scalarField(parent, classId, memberId);
51873
53698
  if (typeof value !== "string" || value.length === 0) {
@@ -52269,6 +54094,7 @@ function computeWorkspaceStatus(workspace, options = {}) {
52269
54094
  seeds: /* @__PURE__ */ new Map()
52270
54095
  };
52271
54096
  let projectAnalysisV4 = null;
54097
+ const valueLowerRegistry = createValueLowerRegistryV4();
52272
54098
  try {
52273
54099
  if (!baseManifest) {
52274
54100
  throw new Error(
@@ -52343,7 +54169,8 @@ function computeWorkspaceStatus(workspace, options = {}) {
52343
54169
  memberDefaults = lowerMemberDefaultSourcesV4(
52344
54170
  workspace.state.records,
52345
54171
  analysis,
52346
- manifest
54172
+ manifest,
54173
+ { registry: valueLowerRegistry }
52347
54174
  );
52348
54175
  if (memberDefaults.memberDefaultValues.size > 0) {
52349
54176
  manifest = {
@@ -52417,16 +54244,48 @@ function computeWorkspaceStatus(workspace, options = {}) {
52417
54244
  const staticValues = lowerStaticValueSourcesV4(
52418
54245
  workspace.state.records,
52419
54246
  projectAnalysisV4,
52420
- manifest
54247
+ manifest,
54248
+ { registry: valueLowerRegistry }
52421
54249
  );
52422
54250
  staticValueSeeds = new Map([...staticValues.seeds, ...memberDefaults.seeds]);
52423
54251
  staticMemberValueIds = staticValues.memberValueIds;
52424
54252
  const rootValues = lowerProjectRootSourceV4(
52425
54253
  workspace.state.records,
52426
54254
  projectAnalysisV4,
52427
- manifest
54255
+ manifest,
54256
+ { registry: valueLowerRegistry }
52428
54257
  );
52429
54258
  staticValueSeeds = new Map([...staticValueSeeds, ...rootValues.seeds]);
54259
+ const resolvedCollectionPaths = resolveRootPathCollectionValuesV4({
54260
+ manifest,
54261
+ state: workspace.state.records,
54262
+ registry: valueLowerRegistry,
54263
+ siteForMember: (memberId) => {
54264
+ const span2 = sourceByKey.get(recordStateKey("member", memberId))?.span;
54265
+ return span2 === void 0 ? null : { uri: span2.path, start: span2.start };
54266
+ }
54267
+ });
54268
+ if (resolvedCollectionPaths.size > 0) {
54269
+ manifest = {
54270
+ ...manifest,
54271
+ members: manifest.members.map((member) => {
54272
+ const resolvedId = resolvedCollectionPaths.get(member.id);
54273
+ return resolvedId === void 0 || member.kind !== "lookup" ? member : { ...member, collectionValueId: resolvedId };
54274
+ })
54275
+ };
54276
+ for (const [memberId, resolvedId] of resolvedCollectionPaths) {
54277
+ const document = documentsByKey.get(recordStateKey("member", memberId));
54278
+ if (document !== void 0 && isObjectRecord2(document.data)) {
54279
+ document.data.collectionValueId = resolvedId;
54280
+ }
54281
+ const record3 = records2.find(
54282
+ (candidate) => candidate.recordKind === "member" && candidate.recordId === memberId
54283
+ );
54284
+ if (record3 !== void 0 && typeof record3.fileFields.collectionValueId === "string") {
54285
+ record3.fileFields.collectionValueId = resolvedId;
54286
+ }
54287
+ }
54288
+ }
52430
54289
  const rootRecords = rootValues.records;
52431
54290
  const supplementalRecords = lowerSupplementalProjectSourcesV4(
52432
54291
  workspace.root,
@@ -52434,7 +54293,7 @@ function computeWorkspaceStatus(workspace, options = {}) {
52434
54293
  projectAnalysisV4,
52435
54294
  { trustedPendingFiles: options.trustedPendingProjectFiles }
52436
54295
  );
52437
- const dialogueState = overlayProspectiveSourceRecords(
54296
+ const prospectiveState = overlayProspectiveSourceRecords(
52438
54297
  workspace.state.records,
52439
54298
  documents,
52440
54299
  [
@@ -52446,7 +54305,7 @@ function computeWorkspaceStatus(workspace, options = {}) {
52446
54305
  staticMemberValueIds
52447
54306
  );
52448
54307
  const dialogueRecords = lowerDialogueProjectSourcesV4(
52449
- dialogueState,
54308
+ prospectiveState,
52450
54309
  projectAnalysisV4
52451
54310
  );
52452
54311
  records2.push(
@@ -52520,6 +54379,7 @@ function computeWorkspaceStatus(workspace, options = {}) {
52520
54379
  });
52521
54380
  }
52522
54381
  }
54382
+ const deletedValueIds = /* @__PURE__ */ new Set();
52523
54383
  for (const [key, recordState] of Object.entries(workspace.state.records)) {
52524
54384
  if (recordState.file === void 0) continue;
52525
54385
  if (reconstructed3.has(key)) continue;
@@ -52527,6 +54387,9 @@ function computeWorkspaceStatus(workspace, options = {}) {
52527
54387
  (error) => error.file === recordState.file && isBlockingSchemaSourceError(error)
52528
54388
  ) || conflictedFiles.includes(recordState.file);
52529
54389
  if (fileStillBroken) continue;
54390
+ if (recordState.recordKind === "value") {
54391
+ deletedValueIds.add(recordState.recordId);
54392
+ }
52530
54393
  changes.push({
52531
54394
  kind: "delete",
52532
54395
  recordKind: recordState.recordKind,
@@ -52537,6 +54400,52 @@ function computeWorkspaceStatus(workspace, options = {}) {
52537
54400
  casBaseHash: recordState.conflictServerHash !== void 0 ? recordState.conflictServerHash : recordState.contentHash
52538
54401
  });
52539
54402
  }
54403
+ const referenceFailures = drainValueReferenceObligationsV4(
54404
+ prospectiveState,
54405
+ manifest,
54406
+ {
54407
+ registry: valueLowerRegistry,
54408
+ analysis: projectAnalysisV4,
54409
+ deletedValueIds,
54410
+ sourceTextByUri: new Map(
54411
+ sourceEntries.map((entry) => [entry.relPath, entry.source])
54412
+ )
54413
+ }
54414
+ );
54415
+ for (const failure of referenceFailures) {
54416
+ parseErrors.push(
54417
+ new SchemaSourceError(
54418
+ failure.message,
54419
+ failure.file,
54420
+ failure.line,
54421
+ failure.column
54422
+ )
54423
+ );
54424
+ }
54425
+ if (referenceFailures.length > 0) {
54426
+ return {
54427
+ changes: [],
54428
+ conflictedFiles,
54429
+ parseErrors,
54430
+ reconstructed: /* @__PURE__ */ new Map(),
54431
+ staticValueSeeds: /* @__PURE__ */ new Map(),
54432
+ binaryChanges: [],
54433
+ binaryFiles: []
54434
+ };
54435
+ }
54436
+ for (let index = changes.length - 1; index >= 0; index -= 1) {
54437
+ const change = changes[index];
54438
+ if (change === void 0 || change.kind !== "update") continue;
54439
+ const baseState = workspace.state.records[recordStateKey(change.recordKind, change.recordId)];
54440
+ if (baseState?.conflictServerHash !== void 0) continue;
54441
+ if (recordsSemanticallyEqual(
54442
+ change.recordKind,
54443
+ change.nextData,
54444
+ change.baseData
54445
+ )) {
54446
+ changes.splice(index, 1);
54447
+ }
54448
+ }
52540
54449
  for (const fileId of options.trustedPendingProjectFiles?.keys() ?? []) {
52541
54450
  const key = `project-file:${fileId}`;
52542
54451
  const base = workspace.state.records[key];
@@ -52639,7 +54548,7 @@ function validateProspectiveAnimationRecordsV4(records2) {
52639
54548
  function prospectiveAnimationDocumentV4(records2) {
52640
54549
  const candidates = [...records2];
52641
54550
  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"))
54551
+ (record3) => record3.recordKind === "class" && (Array.isArray(record3.data.constructorProjections) || declaresAnimationWorldKind(record3.data.system))
52643
54552
  );
52644
54553
  if (!hasAnimationSchema) return null;
52645
54554
  let project;
@@ -52680,6 +54589,13 @@ function prospectiveAnimationDocumentV4(records2) {
52680
54589
  if (project === void 0) return null;
52681
54590
  return { project, classes, members, values };
52682
54591
  }
54592
+ function declaresAnimationWorldKind(system) {
54593
+ if (!isObjectRecord2(system)) return false;
54594
+ const worldKind = system.worldKind;
54595
+ if (typeof worldKind !== "string") return false;
54596
+ if (!isNeoWorldSystemClassKind(worldKind)) return false;
54597
+ return WORLD_SYSTEM_ANIMATION_KINDS.has(worldKind);
54598
+ }
52683
54599
  function serverEnvelope(stored) {
52684
54600
  if (!isObjectRecord2(stored)) return {};
52685
54601
  const envelope = {};
@@ -52956,6 +54872,7 @@ var init_workspace_status = __esm({
52956
54872
  init_project_files();
52957
54873
  init_dialogue_lower();
52958
54874
  init_world_system_classes();
54875
+ init_core();
52959
54876
  init_value_sources();
52960
54877
  init_root_source();
52961
54878
  init_project_documents();
@@ -71206,6 +73123,11 @@ function verifyProjectSourceCommitAgainstStateV4(args) {
71206
73123
  first ? `Trusted source lowering failed: ${first.file}:${first.line}:${first.column} ${first.message}` : `Trusted source lowering found conflict markers in ${status.conflictedFiles[0]}.`
71207
73124
  );
71208
73125
  }
73126
+ auditAuthoredSeedRowIdentities(
73127
+ status.staticValueSeeds,
73128
+ args.stateRecords,
73129
+ assignments
73130
+ );
71209
73131
  const usedAssignments = /* @__PURE__ */ new Set();
71210
73132
  const expectedChanges = status.changes.map((change) => {
71211
73133
  const rewrittenData = change.nextData === void 0 ? void 0 : rewritePending(change.nextData, assignments, usedAssignments);
@@ -71275,6 +73197,32 @@ function isDerivedOnlyRefresh(change, stateRecords) {
71275
73197
  if (!isObjectRecord2(base.data)) return false;
71276
73198
  return canonicalStringify(comparisonData(change.recordKind, change.nextData)) === canonicalStringify(comparisonData(change.recordKind, base.data));
71277
73199
  }
73200
+ function auditAuthoredSeedRowIdentities(seeds, stateRecords, assignments) {
73201
+ const mintedIds = new Set(assignments.values());
73202
+ const authoredCreates = /* @__PURE__ */ new Set();
73203
+ for (const [memberId, seed] of seeds) {
73204
+ for (const row of seed.values ?? []) {
73205
+ if (isPendingId(row.id)) continue;
73206
+ if (stateRecords[`value:${row.id}`] !== void 0) continue;
73207
+ if (!isUuidV4Id(row.id)) {
73208
+ throw new ProjectSourceCommitVerificationError(
73209
+ `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.`
73210
+ );
73211
+ }
73212
+ if (mintedIds.has(row.id)) {
73213
+ throw new ProjectSourceCommitVerificationError(
73214
+ `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.`
73215
+ );
73216
+ }
73217
+ if (authoredCreates.has(row.id)) {
73218
+ throw new ProjectSourceCommitVerificationError(
73219
+ `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.`
73220
+ );
73221
+ }
73222
+ authoredCreates.add(row.id);
73223
+ }
73224
+ }
73225
+ }
71278
73226
  function validateAssignments(value) {
71279
73227
  const assignments = /* @__PURE__ */ new Map();
71280
73228
  const assignedIds = /* @__PURE__ */ new Set();
@@ -71285,9 +73233,7 @@ function validateAssignments(value) {
71285
73233
  );
71286
73234
  }
71287
73235
  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
- )) {
73236
+ if (!isUuidV4Id(assignedId)) {
71291
73237
  throw new ProjectSourceCommitVerificationError(
71292
73238
  `Pending id assignment for ${pendingId2} is not a UUID v4.`
71293
73239
  );