@neocompose/cli 0.6.6 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/neo.mjs CHANGED
@@ -828,6 +828,7 @@ var init_document_contracts = __esm({
828
828
  "locked",
829
829
  "required",
830
830
  "isStatic",
831
+ "isReadOnly",
831
832
  "isVirtual",
832
833
  "isAbstract",
833
834
  "accessModifierKind",
@@ -870,6 +871,7 @@ var init_document_contracts = __esm({
870
871
  isAbstract: "missingIsFalseOnFullMember",
871
872
  locked: "missingIsFalseOnFullMember",
872
873
  required: "missingIsFalseOnFullMember",
874
+ isReadOnly: "missingIsFalseOnFullMember",
873
875
  defaultValue: "nullIsAbsent",
874
876
  extendsMemberId: "nullIsAbsent",
875
877
  system: "nullIsAbsent",
@@ -1452,6 +1454,7 @@ var init_language_spec = __esm({
1452
1454
  "private",
1453
1455
  "protected",
1454
1456
  "public",
1457
+ "readonly",
1455
1458
  "return",
1456
1459
  "sealed",
1457
1460
  "static",
@@ -6023,9 +6026,11 @@ var init_strict_parser = __esm({
6023
6026
  });
6024
6027
 
6025
6028
  // ../packages/neoscript-language/src/strict-ir.ts
6029
+ var NEOSCRIPT_COMPILER_REVISION;
6026
6030
  var init_strict_ir = __esm({
6027
6031
  "../packages/neoscript-language/src/strict-ir.ts"() {
6028
6032
  "use strict";
6033
+ NEOSCRIPT_COMPILER_REVISION = 2;
6029
6034
  }
6030
6035
  });
6031
6036
 
@@ -6799,6 +6804,7 @@ var init_strict_resolver = __esm({
6799
6804
  "use strict";
6800
6805
  init_project();
6801
6806
  init_strict_compile_error();
6807
+ init_strict_ir();
6802
6808
  Scope = class {
6803
6809
  constructor(parent) {
6804
6810
  this.parent = parent;
@@ -6976,6 +6982,7 @@ var init_strict_resolver = __esm({
6976
6982
  if (this.valueVariable) parameters.push(this.valueVariable);
6977
6983
  parameters.push(...this.argumentVariables.map((entry) => entry.variable));
6978
6984
  return {
6985
+ compilerRevision: NEOSCRIPT_COMPILER_REVISION,
6979
6986
  parameters,
6980
6987
  instructions,
6981
6988
  typeInfo: toWireType(returnType, this.project)
@@ -7237,7 +7244,7 @@ var init_strict_resolver = __esm({
7237
7244
  }
7238
7245
  if (!writability || writability === "readOnly" /* ReadOnly */ || writability === "immutable" /* Immutable */) {
7239
7246
  throw new CompileError(
7240
- writability === "immutable" /* Immutable */ ? "Cannot assign because the target's effective storage is immutable." : `Cannot assign to a read-only ${this.describe(target.type)} value.`,
7247
+ target.symbol?.readonlyDeclaration === true ? `Member '${target.symbol.name}' is readonly and can only be changed through its class default.` : writability === "immutable" /* Immutable */ ? "Cannot assign because the target's effective storage is immutable." : `Cannot assign to a read-only ${this.describe(target.type)} value.`,
7241
7248
  statement.pos
7242
7249
  );
7243
7250
  }
@@ -8054,7 +8061,8 @@ var init_strict_resolver = __esm({
8054
8061
  receiver,
8055
8062
  member.schemaKey ?? member.name,
8056
8063
  memberType2,
8057
- optional
8064
+ optional,
8065
+ member.id
8058
8066
  ),
8059
8067
  symbol: member,
8060
8068
  ...writability ? { writability } : {},
@@ -8091,10 +8099,11 @@ var init_strict_resolver = __esm({
8091
8099
  }
8092
8100
  return targetWritability;
8093
8101
  }
8094
- keyOf(receiver, key, type, optional) {
8102
+ keyOf(receiver, key, type, optional, memberId) {
8095
8103
  return {
8096
8104
  pointer: {
8097
8105
  type: "keyOf" /* KeyOf */,
8106
+ ...memberId ? { memberId } : {},
8098
8107
  keyOf: { pointer: receiver.pointer, key: stringPointer(key) },
8099
8108
  ...optional ? { optional: true } : {}
8100
8109
  },
@@ -8846,6 +8855,7 @@ var init_strict_resolver = __esm({
8846
8855
  );
8847
8856
  }
8848
8857
  return {
8858
+ compilerRevision: NEOSCRIPT_COMPILER_REVISION,
8849
8859
  parameters,
8850
8860
  instructions,
8851
8861
  typeInfo: toWireType(returnType, this.project)
@@ -11115,6 +11125,7 @@ var init_project_source_parser = __esm({
11115
11125
  "private",
11116
11126
  "protected",
11117
11127
  "public",
11128
+ "readonly",
11118
11129
  "sealed",
11119
11130
  "static",
11120
11131
  "virtual"
@@ -11801,7 +11812,7 @@ var init_project_schema_contract_generated = __esm({
11801
11812
  "../packages/neoscript-language/src/project-schema-contract.generated.ts"() {
11802
11813
  "use strict";
11803
11814
  PROJECT_SCHEMA_MANIFEST_FORMAT_VERSION = 3;
11804
- PROJECT_SCHEMA_MANIFEST_CONTRACT_VERSION = "3.2";
11815
+ PROJECT_SCHEMA_MANIFEST_CONTRACT_VERSION = "3.3";
11805
11816
  NEO_PROJECT_SOURCE_RECORD_CONTRACT = {
11806
11817
  "recordFields": {
11807
11818
  "member": [
@@ -11812,6 +11823,7 @@ var init_project_schema_contract_generated = __esm({
11812
11823
  "locked",
11813
11824
  "required",
11814
11825
  "isStatic",
11826
+ "isReadOnly",
11815
11827
  "isVirtual",
11816
11828
  "isAbstract",
11817
11829
  "accessModifierKind",
@@ -13981,6 +13993,7 @@ function validateProjectSourceSemantics(documents) {
13981
13993
  );
13982
13994
  }
13983
13995
  }
13996
+ validateWorldLayerLinkSourceContracts(documents, environment, diagnostics);
13984
13997
  return diagnostics;
13985
13998
  }
13986
13999
  function contextualAnnotationFields(document, position) {
@@ -14500,6 +14513,188 @@ function relationFields(context, environment) {
14500
14513
  }
14501
14514
  return [];
14502
14515
  }
14516
+ function validateWorldLayerLinkSourceContracts(documents, environment, diagnostics) {
14517
+ for (const [uri, document] of documents) {
14518
+ for (const declaration of document.declarations) {
14519
+ if (declaration.kind !== "class") continue;
14520
+ const descriptor = worldLayerLinkSourceDescriptor(
14521
+ declaration,
14522
+ environment
14523
+ );
14524
+ if (descriptor === null) continue;
14525
+ const directTargets = directLayerLinkTargets(declaration, uri);
14526
+ const systemWorldKind = contextualAnnotationEnum(
14527
+ declaration.annotations,
14528
+ "system",
14529
+ "worldKind"
14530
+ );
14531
+ const isSystemBase = systemWorldKind === descriptor.linkWorldKind;
14532
+ if (isSystemBase) {
14533
+ if (!declaration.modifiers.includes("abstract")) {
14534
+ pushDiagnostic(
14535
+ diagnostics,
14536
+ uri,
14537
+ declaration.nameRange,
14538
+ "concrete-system-layer-link-base",
14539
+ `System layer-link base '${declaration.name}' must be abstract.`
14540
+ );
14541
+ }
14542
+ if (directTargets.length > 0) {
14543
+ pushDiagnostic(
14544
+ diagnostics,
14545
+ uri,
14546
+ directTargets[0].range,
14547
+ "system-layer-link-target-relation",
14548
+ `System layer-link base '${declaration.name}' cannot declare targetLayer. Declare the relation on a project-authored descendant instead.`
14549
+ );
14550
+ }
14551
+ }
14552
+ for (const target of directTargets) {
14553
+ validateLayerLinkTargetSource(
14554
+ declaration,
14555
+ descriptor,
14556
+ target,
14557
+ environment,
14558
+ diagnostics
14559
+ );
14560
+ }
14561
+ if (isSystemBase || declaration.modifiers.includes("abstract")) continue;
14562
+ const effectiveTargets = effectiveLayerLinkTargets(
14563
+ declaration,
14564
+ uri,
14565
+ environment
14566
+ );
14567
+ if (effectiveTargets.length === 0) {
14568
+ pushDiagnostic(
14569
+ diagnostics,
14570
+ uri,
14571
+ declaration.nameRange,
14572
+ "concrete-layer-link-missing-target",
14573
+ `Concrete layer-link class '${declaration.name}' must resolve exactly one inherited or directly declared targetLayer relation.`
14574
+ );
14575
+ } else if (effectiveTargets.length > 1) {
14576
+ pushDiagnostic(
14577
+ diagnostics,
14578
+ uri,
14579
+ declaration.nameRange,
14580
+ "concrete-layer-link-multiple-targets",
14581
+ `Concrete layer-link class '${declaration.name}' resolves ${effectiveTargets.length} targetLayer relations at the nearest declaration depth; exactly one is required.`
14582
+ );
14583
+ }
14584
+ }
14585
+ }
14586
+ }
14587
+ function worldLayerLinkSourceDescriptor(declaration, environment) {
14588
+ const worldKind = effectiveWorldKind(declaration, environment);
14589
+ if (worldKind === "TileLayerLink") {
14590
+ return {
14591
+ linkWorldKind: "TileLayerLink",
14592
+ targetWorldKind: "TileLayer"
14593
+ };
14594
+ }
14595
+ if (worldKind === "ObjectLayerLink") {
14596
+ return {
14597
+ linkWorldKind: "ObjectLayerLink",
14598
+ targetWorldKind: "ObjectLayer"
14599
+ };
14600
+ }
14601
+ return null;
14602
+ }
14603
+ function directLayerLinkTargets(declaration, uri) {
14604
+ const result = [];
14605
+ for (const annotation2 of declaration.annotations) {
14606
+ if (annotation2.name !== "relations") continue;
14607
+ for (const argument2 of annotation2.arguments) {
14608
+ if (argument2.name !== "targetLayer") continue;
14609
+ let expression;
14610
+ try {
14611
+ expression = parseExpression(argument2.text);
14612
+ } catch (error) {
14613
+ if (error instanceof CompileError) continue;
14614
+ throw error;
14615
+ }
14616
+ const entries = expression.kind === "litList" ? expression.elements : [expression];
14617
+ for (const entry of entries) {
14618
+ result.push({
14619
+ expression: unwrapAnnotatedExpression(entry),
14620
+ range: argument2.range,
14621
+ uri
14622
+ });
14623
+ }
14624
+ }
14625
+ }
14626
+ return result;
14627
+ }
14628
+ function effectiveLayerLinkTargets(declaration, uri, environment) {
14629
+ const visited = /* @__PURE__ */ new Set();
14630
+ let current = declaration;
14631
+ let currentUri = uri;
14632
+ while (current !== void 0 && !visited.has(current.name)) {
14633
+ visited.add(current.name);
14634
+ const systemWorldKind = contextualAnnotationEnum(
14635
+ current.annotations,
14636
+ "system",
14637
+ "worldKind"
14638
+ );
14639
+ if (systemWorldKind === "TileLayerLink" || systemWorldKind === "ObjectLayerLink") {
14640
+ return [];
14641
+ }
14642
+ const direct = directLayerLinkTargets(current, currentUri);
14643
+ if (direct.length > 0) return direct;
14644
+ const parent = current.baseTypes.map((base) => environment.types.get(base.name)).find((candidate) => candidate !== void 0);
14645
+ current = parent;
14646
+ currentUri = uri;
14647
+ }
14648
+ return [];
14649
+ }
14650
+ function validateLayerLinkTargetSource(declaration, descriptor, targetSource, environment, diagnostics) {
14651
+ const expression = targetSource.expression;
14652
+ if (isReferenceCall(expression)) return;
14653
+ if (expression.kind !== "ident") {
14654
+ pushDiagnostic(
14655
+ diagnostics,
14656
+ targetSource.uri,
14657
+ targetSource.range,
14658
+ "invalid-layer-link-target",
14659
+ `Layer-link class '${declaration.name}' targetLayer must be a layer class symbol or typed Reference.`
14660
+ );
14661
+ return;
14662
+ }
14663
+ const target = environment.types.get(expression.name);
14664
+ if (target === void 0) {
14665
+ pushDiagnostic(
14666
+ diagnostics,
14667
+ targetSource.uri,
14668
+ targetSource.range,
14669
+ "unknown-layer-link-target",
14670
+ `Layer-link class '${declaration.name}' targets unknown class '${expression.name}'.`
14671
+ );
14672
+ return;
14673
+ }
14674
+ const targetWorldKind = effectiveWorldKind(target, environment);
14675
+ if (targetWorldKind !== descriptor.targetWorldKind) {
14676
+ pushDiagnostic(
14677
+ diagnostics,
14678
+ targetSource.uri,
14679
+ targetSource.range,
14680
+ "wrong-layer-link-target-kind",
14681
+ `Layer-link class '${declaration.name}' targetLayer '${target.name}' must extend world class kind '${descriptor.targetWorldKind}'.`
14682
+ );
14683
+ return;
14684
+ }
14685
+ if (target.modifiers.includes("abstract")) {
14686
+ pushDiagnostic(
14687
+ diagnostics,
14688
+ targetSource.uri,
14689
+ targetSource.range,
14690
+ "abstract-layer-link-target",
14691
+ `Layer-link class '${declaration.name}' targetLayer '${target.name}' must be concrete.`
14692
+ );
14693
+ }
14694
+ }
14695
+ function unwrapAnnotatedExpression(expression) {
14696
+ return expression.kind === "annotated" ? unwrapAnnotatedExpression(expression.expression) : expression;
14697
+ }
14503
14698
  function effectiveWorldKind(declaration, environment) {
14504
14699
  const queue = [declaration];
14505
14700
  const seen = /* @__PURE__ */ new Set();
@@ -14829,7 +15024,26 @@ function validateExpression(expression, expected, scope, environment, uri, range
14829
15024
  if (expression.kind === "new") {
14830
15025
  const typeName = expression.className ?? expected?.name;
14831
15026
  const declaration = typeName ? environment.types.get(typeName) : void 0;
15027
+ for (const entry of expression.initializer ?? []) {
15028
+ if (entry.name !== "layerClassId") continue;
15029
+ pushDiagnostic(
15030
+ diagnostics,
15031
+ uri,
15032
+ range2,
15033
+ "reserved-layer-class-id-source",
15034
+ "layerClassId is an unsupported legacy field. Declare @relations(targetLayer: ...) on the concrete layer-link class instead."
15035
+ );
15036
+ }
14832
15037
  if (!declaration) return;
15038
+ if (declaration.modifiers.includes("abstract")) {
15039
+ pushDiagnostic(
15040
+ diagnostics,
15041
+ uri,
15042
+ range2,
15043
+ "abstract-class-instantiation",
15044
+ `Cannot instantiate abstract class '${declaration.name}'. Use a concrete descendant instead.`
15045
+ );
15046
+ }
14833
15047
  const fields = declaration?.members.filter(
14834
15048
  (member) => member.kind === "field" && !member.modifiers.includes("static")
14835
15049
  ) ?? [];
@@ -14847,8 +15061,10 @@ function validateExpression(expression, expected, scope, environment, uri, range
14847
15061
  );
14848
15062
  });
14849
15063
  for (const entry of expression.initializer ?? []) {
14850
- const field = declaration?.members.find(
14851
- (candidate) => candidate.name === entry.name
15064
+ const field = sourceClassFieldByName(
15065
+ declaration,
15066
+ entry.name,
15067
+ environment
14852
15068
  );
14853
15069
  validateExpression(
14854
15070
  entry.value,
@@ -14950,6 +15166,19 @@ function validateExpression(expression, expected, scope, environment, uri, range
14950
15166
  );
14951
15167
  }
14952
15168
  }
15169
+ function sourceClassFieldByName(declaration, name, environment) {
15170
+ const visited = /* @__PURE__ */ new Set();
15171
+ let current = declaration;
15172
+ while (current !== void 0 && !visited.has(current.name)) {
15173
+ visited.add(current.name);
15174
+ const member = current.members.find(
15175
+ (candidate) => candidate.kind === "field" && candidate.name === name
15176
+ );
15177
+ if (member !== void 0) return member;
15178
+ current = current.baseTypes.map((base) => environment.types.get(base.name)).find((candidate) => candidate !== void 0);
15179
+ }
15180
+ return void 0;
15181
+ }
14953
15182
  function semanticTypesAssignable(actual, expected) {
14954
15183
  if (actual.name === expected.name) return true;
14955
15184
  if (actual.name === "int" && (expected.name === "float" || expected.name === "decimal")) {
@@ -15565,6 +15794,7 @@ function buildProjectGraph(documents) {
15565
15794
  (memberId) => {
15566
15795
  const member = membersBySymbolId.get(memberId);
15567
15796
  if (!member || member.kind !== "field") return null;
15797
+ if (member.modifiers.includes("readonly")) return null;
15568
15798
  return {
15569
15799
  required: member.initializer === void 0,
15570
15800
  defaultValue: member.initializer?.text
@@ -15622,6 +15852,7 @@ function sourceMemberSymbol(member, owner, typeIds, source) {
15622
15852
  );
15623
15853
  const type = sourceTypeRef(member.type, owner, typeIds);
15624
15854
  const staticMember = member.modifiers.includes("static");
15855
+ const readonlyDeclaration = member.modifiers.includes("readonly");
15625
15856
  const common = {
15626
15857
  id: id2,
15627
15858
  name: member.name,
@@ -15634,7 +15865,8 @@ function sourceMemberSymbol(member, owner, typeIds, source) {
15634
15865
  owner.declaration.kind
15635
15866
  ),
15636
15867
  location: location(owner.uri, member.range),
15637
- selectionLocation: location(owner.uri, member.nameRange)
15868
+ selectionLocation: location(owner.uri, member.nameRange),
15869
+ ...readonlyDeclaration ? { readonlyDeclaration: true } : {}
15638
15870
  };
15639
15871
  if (member.kind === "function") {
15640
15872
  return {
@@ -15665,8 +15897,8 @@ function sourceMemberSymbol(member, owner, typeIds, source) {
15665
15897
  return {
15666
15898
  ...common,
15667
15899
  kind: "property",
15668
- writable: writability !== "immutable" && writability !== "readOnly",
15669
- writability
15900
+ writable: !readonlyDeclaration && writability !== "immutable" && writability !== "readOnly",
15901
+ writability: readonlyDeclaration ? "readOnly" : writability
15670
15902
  };
15671
15903
  }
15672
15904
  function substituteInheritedSourceMember(member, baseInfo, resolvedBaseType) {
@@ -16026,6 +16258,7 @@ function createEnvironment2(documents) {
16026
16258
  if (declaration.kind === "class") {
16027
16259
  const ref = {
16028
16260
  uri,
16261
+ sourceKind: document.kind,
16029
16262
  declaration,
16030
16263
  location: { uri, range: declaration.nameRange }
16031
16264
  };
@@ -16039,6 +16272,7 @@ function createEnvironment2(documents) {
16039
16272
  } else {
16040
16273
  const ref = {
16041
16274
  uri,
16275
+ sourceKind: document.kind,
16042
16276
  declaration,
16043
16277
  location: { uri, range: declaration.nameRange }
16044
16278
  };
@@ -16523,6 +16757,16 @@ function validateMembers(environment, diagnostics) {
16523
16757
  );
16524
16758
  }
16525
16759
  for (const modifier of member.modifiers) {
16760
+ if (modifier === "readonly") {
16761
+ diagnose(
16762
+ diagnostics,
16763
+ ref.uri,
16764
+ member.nameRange,
16765
+ "readonly-interface-member",
16766
+ `Interface member '${member.name}' cannot use 'readonly'; readonly is valid only on a class field.`
16767
+ );
16768
+ continue;
16769
+ }
16526
16770
  if (modifier === "async" && member.kind === "function") continue;
16527
16771
  if (isNeoSourceAccessModifier(modifier)) continue;
16528
16772
  diagnose(
@@ -16537,7 +16781,7 @@ function validateMembers(environment, diagnostics) {
16537
16781
  validateInterfaceMemberConflicts(ref, environment, diagnostics);
16538
16782
  }
16539
16783
  for (const ref of environment.classes.values()) {
16540
- validateClassMemberModifiers(ref, diagnostics);
16784
+ validateClassMemberModifiers(ref, environment, diagnostics);
16541
16785
  validateClassOverrides(ref, environment, diagnostics);
16542
16786
  if (ref.declaration.modifiers.includes("abstract")) {
16543
16787
  validateAbstractInterfaceMembers(ref, environment, diagnostics);
@@ -16646,10 +16890,67 @@ function validateAbstractInterfaceMembers(ref, environment, diagnostics) {
16646
16890
  );
16647
16891
  }
16648
16892
  }
16649
- function validateClassMemberModifiers(ref, diagnostics) {
16893
+ function validateClassMemberModifiers(ref, environment, diagnostics) {
16650
16894
  const classIsAbstract = ref.declaration.modifiers.includes("abstract");
16651
16895
  for (const member of ref.declaration.members) {
16652
16896
  const modifiers = new Set(member.modifiers);
16897
+ if (modifiers.has("readonly")) {
16898
+ if (ref.sourceKind === "flow") {
16899
+ diagnose(
16900
+ diagnostics,
16901
+ ref.uri,
16902
+ member.nameRange,
16903
+ "readonly-neoflow-binding",
16904
+ `NeoFlow binding '${member.name}' cannot use 'readonly'; readonly is valid only on a class field in a .neo definition.`
16905
+ );
16906
+ } else if (member.kind !== "field") {
16907
+ diagnose(
16908
+ diagnostics,
16909
+ ref.uri,
16910
+ member.nameRange,
16911
+ "readonly-non-field-member",
16912
+ `Member '${member.name}' cannot use 'readonly'; readonly is valid only on a class field.`
16913
+ );
16914
+ } else {
16915
+ if (modifiers.has("static")) {
16916
+ diagnose(
16917
+ diagnostics,
16918
+ ref.uri,
16919
+ member.nameRange,
16920
+ "static-readonly-member",
16921
+ `Read-only field '${member.name}' cannot be static.`
16922
+ );
16923
+ }
16924
+ if (modifiers.has("abstract")) {
16925
+ diagnose(
16926
+ diagnostics,
16927
+ ref.uri,
16928
+ member.nameRange,
16929
+ "abstract-readonly-member",
16930
+ `Read-only field '${member.name}' cannot be abstract.`
16931
+ );
16932
+ }
16933
+ if (member.initializer === void 0) {
16934
+ diagnose(
16935
+ diagnostics,
16936
+ ref.uri,
16937
+ member.nameRange,
16938
+ "readonly-member-initializer",
16939
+ `Read-only field '${member.name}' requires an explicit initializer.`
16940
+ );
16941
+ }
16942
+ const storage = resolvedSourceMemberStorage(ref, member, environment);
16943
+ if (storage !== "immutable") {
16944
+ diagnose(
16945
+ diagnostics,
16946
+ ref.uri,
16947
+ member.nameRange,
16948
+ "readonly-member-storage",
16949
+ `Read-only field '${member.name}' requires resolved @storage(allowed: .Immutable).`
16950
+ );
16951
+ }
16952
+ }
16953
+ }
16653
16954
  if (modifiers.has("sealed")) {
16654
16955
  diagnose(
16655
16956
  diagnostics,
@@ -16832,13 +17133,14 @@ function validateConcreteClass(ref, environment, diagnostics) {
16832
17133
  const requiredContracts = allRequiredInterfaceMembers(ref, environment);
16833
17134
  for (const required2 of requiredContracts.values()) {
16834
17135
  const implementation = effective.get(required2.member.name.toLowerCase());
16835
- if (implementation && !implementation.member.modifiers.includes("abstract") && !implementation.member.modifiers.includes("static") && memberCompatibility(
17136
+ const mismatch = implementation ? memberCompatibility(
16836
17137
  implementation.member,
16837
17138
  implementation.substitution,
16838
17139
  required2,
16839
17140
  true,
16840
17141
  environment
16841
- ) === null) {
17142
+ ) : null;
17143
+ if (implementation && !implementation.member.modifiers.includes("abstract") && !implementation.member.modifiers.includes("static") && mismatch === null) {
16842
17144
  continue;
16843
17145
  }
16844
17146
  diagnose(
@@ -16846,7 +17148,7 @@ function validateConcreteClass(ref, environment, diagnostics) {
16846
17148
  ref.uri,
16847
17149
  ref.declaration.nameRange,
16848
17150
  "interface-member-not-implemented",
16849
- implementation ? `Class '${ref.declaration.name}' member '${implementation.member.name}' does not match interface contract '${required2.owner.declaration.name}.${required2.member.name}'.` : `Class '${ref.declaration.name}' does not implement interface member '${required2.owner.declaration.name}.${required2.member.name}'.`,
17151
+ implementation ? implementation.member.modifiers.includes("readonly") && mismatch === "the implementation must provide a setter" ? `Read-only member '${ref.declaration.name}.${implementation.member.name}' cannot implement settable interface member '${required2.owner.declaration.name}.${required2.member.name}'.` : `Class '${ref.declaration.name}' member '${implementation.member.name}' does not match interface contract '${required2.owner.declaration.name}.${required2.member.name}'${mismatch ? `: ${mismatch}` : ""}.` : `Class '${ref.declaration.name}' does not implement interface member '${required2.owner.declaration.name}.${required2.member.name}'.`,
16850
17152
  [
16851
17153
  {
16852
17154
  location: interfaceMemberLocation(required2),
@@ -17022,13 +17324,45 @@ function memberCompatibility(implementation, implementationSubstitution, target,
17022
17324
  return null;
17023
17325
  }
17024
17326
  function valueAccessors(member) {
17025
- if (member.kind === "field") return { get: true, set: true };
17327
+ if (member.kind === "field") {
17328
+ return {
17329
+ get: true,
17330
+ set: !member.modifiers.includes("readonly")
17331
+ };
17332
+ }
17026
17333
  if (member.kind === "function") return { get: false, set: false };
17027
17334
  return {
17028
17335
  get: /\bget\b/.test(member.body.text),
17029
17336
  set: /\bset\b/.test(member.body.text)
17030
17337
  };
17031
17338
  }
17339
+ function resolvedSourceMemberStorage(owner, member, environment, seen = /* @__PURE__ */ new Set()) {
17340
+ const own = member.annotations.find(
17341
+ (annotation2) => annotation2.name === "storage"
17342
+ );
17343
+ if (own) {
17344
+ const allowed = own.arguments.find((argument2) => argument2.name === "allowed") ?? own.arguments[0];
17345
+ const normalized = allowed?.text.replace(/\s+/g, "").toLowerCase();
17346
+ if (normalized === ".immutable") return "immutable";
17347
+ if (normalized === ".save") return "save";
17348
+ if (normalized === ".session") return "session";
17349
+ return null;
17350
+ }
17351
+ if (!member.modifiers.includes("override")) return null;
17352
+ const key = `${owner.declaration.name}:${member.name}`;
17353
+ if (seen.has(key)) return null;
17354
+ seen.add(key);
17355
+ const candidates = inheritedClassMembers(owner, environment).filter(
17356
+ (candidate) => namesEqual(candidate.member.name, member.name)
17357
+ );
17358
+ const target = candidates[candidates.length - 1];
17359
+ return target ? resolvedSourceMemberStorage(
17360
+ target.owner,
17361
+ target.member,
17362
+ environment,
17363
+ seen
17364
+ ) : null;
17365
+ }
17032
17366
  function isTypeAssignable(actual, expected, environment, genericParameters) {
17033
17367
  if (actual.nullable && !expected.nullable) return false;
17034
17368
  if (typesEqual({ ...actual, nullable: false }, { ...expected, nullable: false })) {
@@ -18650,6 +18984,8 @@ function attachConstructorSignature(type, environment) {
18650
18984
  type.id,
18651
18985
  type.members,
18652
18986
  (memberId) => {
18987
+ const declarationMember = environment.members.get(memberId);
18988
+ if (declarationMember?.isReadOnly === true) return null;
18653
18989
  const member = effectiveConstructorMember(
18654
18990
  memberId,
18655
18991
  genericEnvironment,
@@ -18914,6 +19250,7 @@ function memberSymbol(member, schemaKey, environment, field) {
18914
19250
  const kind = string2(member.kind, `${field}.kind`);
18915
19251
  const modifier = string2(member.modifier, `${field}.modifier`);
18916
19252
  const isStatic = boolean(member.isStatic, `${field}.isStatic`);
19253
+ const isReadOnly = boolean(member.isReadOnly, `${field}.isReadOnly`);
18917
19254
  const owner = optionalRecord(member.owner);
18918
19255
  const ownerClassId2 = owner?.kind === "classMember" && typeof owner.classId === "string" ? owner.classId : void 0;
18919
19256
  const type = memberType(member, environment, /* @__PURE__ */ new Set(), field);
@@ -18934,6 +19271,7 @@ function memberSymbol(member, schemaKey, environment, field) {
18934
19271
  type,
18935
19272
  documentation: `${String(member.name ?? schemaKey)} (${id2})`,
18936
19273
  ...isStatic ? { static: true } : {},
19274
+ ...isReadOnly ? { readonlyDeclaration: true } : {},
18937
19275
  ...location3,
18938
19276
  ...scriptDefinitionLocations(member, environment, field),
18939
19277
  ...modifier.includes("abstract") ? { abstract: true } : {},
@@ -18965,9 +19303,9 @@ function memberSymbol(member, schemaKey, environment, field) {
18965
19303
  writable: false
18966
19304
  };
18967
19305
  }
18968
- const writable = kind === "computed" ? computedHasSetter(member, field) : modifier !== "abstract" && member.locked !== true;
19306
+ const writable = isReadOnly ? false : kind === "computed" ? computedHasSetter(member, field) : modifier !== "abstract" && member.locked !== true;
18969
19307
  const storage = typeof member.storage === "string" ? member.storage : void 0;
18970
- const writability = kind === "computed" ? writable ? "setter" : "readOnly" : !writable ? "readOnly" : storage === "immutable" || storage === "save" || storage === "session" ? storage : isStatic && ownerClassId2 ? staticDefaultWritability(ownerClassId2, environment) : void 0;
19308
+ const writability = isReadOnly ? "readOnly" : kind === "computed" ? writable ? "setter" : "readOnly" : !writable ? "readOnly" : storage === "immutable" || storage === "save" || storage === "session" ? storage : isStatic && ownerClassId2 ? staticDefaultWritability(ownerClassId2, environment) : void 0;
18971
19309
  const lookupCollectionMemberId = kind === "lookup" ? string2(member.collectionMemberId, `${field}.collectionMemberId`) : null;
18972
19310
  const lookupCollectionWritability = lookupCollectionMemberId === null ? void 0 : environment.effectiveWritabilityByMemberId.get(
18973
19311
  lookupCollectionMemberId
@@ -20111,6 +20449,7 @@ function projectCompletions(analysis, document, position) {
20111
20449
  "sealed",
20112
20450
  "native",
20113
20451
  "override",
20452
+ "readonly",
20114
20453
  "virtual",
20115
20454
  "static",
20116
20455
  "new",
@@ -21044,7 +21383,7 @@ function constructorArgumentType(analysis, frame) {
21044
21383
  return null;
21045
21384
  }
21046
21385
  const fields = declaration.members.filter(
21047
- (member) => member.kind === "field" && !member.modifiers.includes("static")
21386
+ (member) => member.kind === "field" && !member.modifiers.includes("static") && !member.modifiers.includes("readonly")
21048
21387
  );
21049
21388
  if (frame.argumentName !== null) {
21050
21389
  return fields.find((field) => field.name === frame.argumentName)?.type ?? null;
@@ -21099,7 +21438,7 @@ function projectConstructorParameters(analysis, typeName) {
21099
21438
  function declarationConstructorParameters(declaration) {
21100
21439
  if (declaration.kind === "global" || declaration.kind === "enum") return [];
21101
21440
  return declaration.members.filter(
21102
- (member) => member.kind === "field" && !member.modifiers.includes("static")
21441
+ (member) => member.kind === "field" && !member.modifiers.includes("static") && !member.modifiers.includes("readonly")
21103
21442
  ).map((member) => ({ type: member.type.name, name: member.name }));
21104
21443
  }
21105
21444
  function graphConstructorParameters(typeName) {
@@ -22035,6 +22374,11 @@ function memberFromDocument(record3, members, owners, classNames, context) {
22035
22374
  record3,
22036
22375
  "isAbstract"
22037
22376
  );
22377
+ const isReadOnly = booleanValue(
22378
+ data.isReadOnly ?? false,
22379
+ record3,
22380
+ "isReadOnly"
22381
+ );
22038
22382
  const modifier = memberModifier(overrideOf, isVirtual, isAbstract);
22039
22383
  const defaultValue = defaultValueFromDocument(
22040
22384
  field("defaultValue", null),
@@ -22048,6 +22392,7 @@ function memberFromDocument(record3, members, owners, classNames, context) {
22048
22392
  name: requiredString(data, "name", record3),
22049
22393
  ...docsText === void 0 ? {} : { docsText },
22050
22394
  isStatic: booleanValue(data.isStatic, record3, "isStatic"),
22395
+ isReadOnly,
22051
22396
  accessModifier: accessModifierValue(
22052
22397
  data.accessModifierKind,
22053
22398
  record3,
@@ -22296,6 +22641,7 @@ function memberToDocumentFields(member, baseData3) {
22296
22641
  locked: member.locked,
22297
22642
  required: member.required,
22298
22643
  isStatic: member.isStatic,
22644
+ ...member.isReadOnly ? { isReadOnly: true } : {},
22299
22645
  accessModifierKind: member.accessModifier,
22300
22646
  ...member.docsText === void 0 ? {} : { docsText: member.docsText }
22301
22647
  };
@@ -24764,6 +25110,7 @@ function assertMember2(value, path) {
24764
25110
  }
24765
25111
  nonEmptyString(member.name, `${path}.name`);
24766
25112
  booleanAt(member.isStatic, `${path}.isStatic`);
25113
+ booleanAt(member.isReadOnly, `${path}.isReadOnly`);
24767
25114
  stringIn(
24768
25115
  member.accessModifier,
24769
25116
  /* @__PURE__ */ new Set(["public", "protected", "private"]),
@@ -24798,6 +25145,32 @@ function assertMember2(value, path) {
24798
25145
  assertSystem(member.system, `${path}.system`);
24799
25146
  nullableStorage(member.storage, `${path}.storage`);
24800
25147
  nullableNonEmptyString(member.storageKey, `${path}.storageKey`);
25148
+ if (member.isReadOnly === true) {
25149
+ if (owner.kind !== "classMember" && owner.kind !== "sharedClassMember") {
25150
+ invalid(
25151
+ `${path}.isReadOnly`,
25152
+ "is allowed only on a concrete class field."
25153
+ );
25154
+ }
25155
+ if (member.isStatic === true) {
25156
+ invalid(`${path}.isReadOnly`, "cannot be combined with isStatic.");
25157
+ }
25158
+ if (kind === "computed" || kind === "function" || kind === "scriptFunction") {
25159
+ invalid(
25160
+ `${path}.isReadOnly`,
25161
+ `is not valid on value-less member kind ${JSON.stringify(kind)}.`
25162
+ );
25163
+ }
25164
+ if (member.modifier === "abstract" || member.modifier === "abstractOverride") {
25165
+ invalid(`${path}.isReadOnly`, "cannot be combined with abstract.");
25166
+ }
25167
+ if (member.storage !== "immutable") {
25168
+ invalid(`${path}.storage`, "must be immutable when isReadOnly is true.");
25169
+ }
25170
+ if (member.defaultValue === null) {
25171
+ invalid(`${path}.defaultValue`, "is required when isReadOnly is true.");
25172
+ }
25173
+ }
24801
25174
  if (kind === "int") {
24802
25175
  nullableNumber(member.min, `${path}.min`);
24803
25176
  nullableNumber(member.max, `${path}.max`);
@@ -24806,6 +25179,9 @@ function assertMember2(value, path) {
24806
25179
  if (kind === "string") {
24807
25180
  booleanAt(member.localizable, `${path}.localizable`);
24808
25181
  booleanAt(member.searchKey, `${path}.searchKey`);
25182
+ if (member.isReadOnly === true && member.searchKey === true) {
25183
+ invalid(`${path}.searchKey`, "cannot be true on a read-only member.");
25184
+ }
24809
25185
  return;
24810
25186
  }
24811
25187
  if (kind === "float") {
@@ -24834,6 +25210,9 @@ function assertMember2(value, path) {
24834
25210
  );
24835
25211
  arrayOf(member.indexes, `${path}.indexes`, assertListIndex);
24836
25212
  arrayOf(member.columns, `${path}.columns`, assertListColumn);
25213
+ if (member.isReadOnly === true && Array.isArray(member.indexes) && member.indexes.length > 0) {
25214
+ invalid(`${path}.indexes`, "must be empty on a read-only member.");
25215
+ }
24837
25216
  return;
24838
25217
  }
24839
25218
  if (kind === "class") {
@@ -26019,6 +26398,7 @@ var init_validate = __esm({
26019
26398
  "owner",
26020
26399
  "name",
26021
26400
  "isStatic",
26401
+ "isReadOnly",
26022
26402
  "accessModifier",
26023
26403
  "modifier",
26024
26404
  "locked",
@@ -27836,6 +28216,7 @@ function memberCommon(context, ownerClass, declaration, id2, owner, base) {
27836
28216
  name: declaration.name,
27837
28217
  ...declaration.docsText === void 0 ? {} : { docsText: declaration.docsText },
27838
28218
  isStatic: declaration.modifiers.includes("static"),
28219
+ isReadOnly: declaration.modifiers.includes("readonly"),
27839
28220
  // Non-class placements (collection entries, generic arguments, loose
27840
28221
  // members) have no receiver type to scope visibility to; they are
27841
28222
  // structurally public. Class schema entries apply the C# omission default.
@@ -27851,7 +28232,7 @@ function memberCommon(context, ownerClass, declaration, id2, owner, base) {
27851
28232
  };
27852
28233
  }
27853
28234
  function lowerFieldMember(context, ownerClass, declaration, id2, commonInput, base) {
27854
- const storedStatic = declaration.modifiers.includes("static");
28235
+ const storedStatic = declaration.modifiers.includes("static") && !declaration.modifiers.includes("readonly");
27855
28236
  const settings = annotation(declaration.annotations, "settings");
27856
28237
  const storesSelectionArray = context.enumIdsByName.has(declaration.type.name) || declaration.type.name === "Dialogue" || Boolean(settings && argument(settings, "collection"));
27857
28238
  const common = {
@@ -29681,7 +30062,8 @@ function memberModifier3(member) {
29681
30062
  const accessPrefix = `${member.accessModifier} `;
29682
30063
  const staticPrefix = member.isStatic ? "static " : "";
29683
30064
  const modifier = member.modifier === "plain" ? "" : member.modifier === "abstractOverride" ? "abstract override " : member.modifier === "sealedOverride" ? "override " : `${member.modifier} `;
29684
- return `${accessPrefix}${staticPrefix}${modifier}`;
30065
+ const readOnlyPrefix = member.isReadOnly ? "readonly " : "";
30066
+ return `${accessPrefix}${staticPrefix}${modifier}${readOnlyPrefix}`;
29685
30067
  }
29686
30068
  function renderExtendsGenericBindings(context, schemaClass2, base) {
29687
30069
  if (base.genericParameters.length === 0) return "";
@@ -32920,6 +33302,9 @@ function isNSPointerKeyOf(value) {
32920
33302
  const v = value;
32921
33303
  if (v?.type !== "keyOf" /* keyOf */) return false;
32922
33304
  if (!isNSKeyOf(v.keyOf)) return false;
33305
+ if (v.memberId !== void 0 && typeof v.memberId !== "string") {
33306
+ return false;
33307
+ }
32923
33308
  if (v.optional !== void 0 && typeof v.optional !== "boolean") {
32924
33309
  return false;
32925
33310
  }
@@ -33079,6 +33464,9 @@ function isNSConditionalBranch(value) {
33079
33464
  function isNSFunctionWithReturnType(value) {
33080
33465
  const v = value;
33081
33466
  if (typeof v !== "object" || v === null) return false;
33467
+ if (v.compilerRevision !== void 0 && (!Number.isSafeInteger(v.compilerRevision) || v.compilerRevision < 1 || v.compilerRevision > NEOSCRIPT_COMPILER_REVISION)) {
33468
+ return false;
33469
+ }
33082
33470
  if (!Array.isArray(v.parameters)) return false;
33083
33471
  if (!v.parameters.every(isNSVariable)) return false;
33084
33472
  if (!isNSInstructions(v.instructions)) return false;
@@ -33327,6 +33715,7 @@ var NS_STRING_OPS, NS_DECIMAL_OPS;
33327
33715
  var init_neoscript_guards = __esm({
33328
33716
  "../src/models/neoscript/neoscript-guards.ts"() {
33329
33717
  "use strict";
33718
+ init_src();
33330
33719
  init_member_kinds();
33331
33720
  init_core();
33332
33721
  init_neoscript_types();
@@ -33346,6 +33735,140 @@ var init_neoscript_guards = __esm({
33346
33735
  }
33347
33736
  });
33348
33737
 
33738
+ // ../src/models/interfaces/interface-graph.ts
33739
+ function resolveInterfaceClosure(interfaceId, interfaces) {
33740
+ const byId = new Map(
33741
+ interfaces.map((neoInterface) => [neoInterface.id, neoInterface])
33742
+ );
33743
+ const result = [];
33744
+ const emitted = /* @__PURE__ */ new Set();
33745
+ const visiting = [];
33746
+ const visitingSet = /* @__PURE__ */ new Set();
33747
+ const visit = (id2) => {
33748
+ if (visitingSet.has(id2)) {
33749
+ throw new CircularInterfaceExtensionError(id2, [...visiting, id2]);
33750
+ }
33751
+ if (emitted.has(id2)) return;
33752
+ const neoInterface = byId.get(id2);
33753
+ if (!neoInterface) return;
33754
+ visiting.push(id2);
33755
+ visitingSet.add(id2);
33756
+ result.push(neoInterface);
33757
+ emitted.add(id2);
33758
+ for (const parentId of neoInterface.extendsInterfaceIds ?? []) {
33759
+ visit(parentId);
33760
+ }
33761
+ visiting.pop();
33762
+ visitingSet.delete(id2);
33763
+ };
33764
+ visit(interfaceId);
33765
+ return result;
33766
+ }
33767
+ function resolveImplementedInterfaces(classId, classes, interfaces) {
33768
+ const result = [];
33769
+ const seen = /* @__PURE__ */ new Set();
33770
+ for (const schemaClass2 of resolveInheritanceChain(classId, classes)) {
33771
+ for (const interfaceId of schemaClass2.implementsInterfaceIds ?? []) {
33772
+ for (const neoInterface of resolveInterfaceClosure(
33773
+ interfaceId,
33774
+ interfaces
33775
+ )) {
33776
+ if (seen.has(neoInterface.id)) continue;
33777
+ seen.add(neoInterface.id);
33778
+ result.push(neoInterface);
33779
+ }
33780
+ }
33781
+ }
33782
+ return result;
33783
+ }
33784
+ function classImplementsInterface(classId, interfaceId, classes, interfaces) {
33785
+ return resolveImplementedInterfaces(classId, classes, interfaces).some(
33786
+ (neoInterface) => neoInterface.id === interfaceId
33787
+ );
33788
+ }
33789
+ var CircularInterfaceExtensionError;
33790
+ var init_interface_graph = __esm({
33791
+ "../src/models/interfaces/interface-graph.ts"() {
33792
+ "use strict";
33793
+ init_inheritance();
33794
+ CircularInterfaceExtensionError = class extends Error {
33795
+ constructor(interfaceId, chain) {
33796
+ super(
33797
+ `Circular interface extension detected at "${interfaceId}"; chain: ${chain.join(" -> ")}`
33798
+ );
33799
+ this.interfaceId = interfaceId;
33800
+ this.chain = chain;
33801
+ this.name = "CircularInterfaceExtensionError";
33802
+ }
33803
+ interfaceId;
33804
+ chain;
33805
+ };
33806
+ }
33807
+ });
33808
+
33809
+ // ../src/models/neoscript/type-info-compatibility.ts
33810
+ function typeInfosInvariantlyEqual(left, right) {
33811
+ if (left.type !== right.type) return false;
33812
+ if (left.required !== right.required) return false;
33813
+ if (left.type === NS_TYPE_VOID || right.type === NS_TYPE_VOID) return true;
33814
+ if (left.type === NS_TYPE_UNKNOWN || right.type === NS_TYPE_UNKNOWN)
33815
+ return true;
33816
+ if (left.type === 7 /* Class */ && right.type === 7 /* Class */) {
33817
+ if (left.classId !== right.classId) return false;
33818
+ const leftArguments = left.typeArguments ?? {};
33819
+ const rightArguments = right.typeArguments ?? {};
33820
+ const leftKeys = Object.keys(leftArguments);
33821
+ const rightKeys = Object.keys(rightArguments);
33822
+ if (leftKeys.length !== rightKeys.length) return false;
33823
+ for (const key of leftKeys) {
33824
+ const rightArgument = rightArguments[key];
33825
+ if (!rightArgument) return false;
33826
+ if (!typeInfosInvariantlyEqual(leftArguments[key], rightArgument)) {
33827
+ return false;
33828
+ }
33829
+ }
33830
+ return true;
33831
+ }
33832
+ if (left.type === 22 /* Interface */ && right.type === 22 /* Interface */) {
33833
+ return left.interfaceId === right.interfaceId;
33834
+ }
33835
+ if (left.type === 21 /* Generic */ && right.type === 21 /* Generic */) {
33836
+ return left.ownerClassId === right.ownerClassId && left.genericParamId === right.genericParamId;
33837
+ }
33838
+ if (left.type === 8 /* Enum */ && right.type === 8 /* Enum */) {
33839
+ return left.enumId === right.enumId;
33840
+ }
33841
+ if ((left.type === 6 /* List */ || left.type === 5 /* Dictionary */) && (right.type === 6 /* List */ || right.type === 5 /* Dictionary */)) {
33842
+ if (left.keyEnumId !== right.keyEnumId) return false;
33843
+ return typeInfosInvariantlyEqual(left.entryTypeInfo, right.entryTypeInfo);
33844
+ }
33845
+ if (left.type === 9 /* Lookup */ && right.type === 9 /* Lookup */) {
33846
+ if (left.collectionMemberId !== right.collectionMemberId) return false;
33847
+ if (left.collectionValueId !== right.collectionValueId) return false;
33848
+ return typeInfosInvariantlyEqual(left.entryTypeInfo, right.entryTypeInfo);
33849
+ }
33850
+ return true;
33851
+ }
33852
+ var init_type_info_compatibility = __esm({
33853
+ "../src/models/neoscript/type-info-compatibility.ts"() {
33854
+ "use strict";
33855
+ init_member_kinds();
33856
+ init_inheritance();
33857
+ init_interface_graph();
33858
+ init_neoscript_types();
33859
+ }
33860
+ });
33861
+
33862
+ // ../src/models/neoscript/index.ts
33863
+ var init_neoscript = __esm({
33864
+ "../src/models/neoscript/index.ts"() {
33865
+ "use strict";
33866
+ init_neoscript_types();
33867
+ init_neoscript_guards();
33868
+ init_type_info_compatibility();
33869
+ }
33870
+ });
33871
+
33349
33872
  // ../src/models/classes/classes.ts
33350
33873
  function isGenericParamConstraint(value) {
33351
33874
  const v = value;
@@ -33463,309 +33986,6 @@ var init_classes = __esm({
33463
33986
  }
33464
33987
  });
33465
33988
 
33466
- // ../src/models/classes/inheritance.ts
33467
- function resolveInheritanceChain(classId, classes, draft) {
33468
- const chain = [];
33469
- const visited = /* @__PURE__ */ new Set();
33470
- const lookup = (id2) => draft && draft.id === id2 ? draft : classes.find((t) => t.id === id2);
33471
- let current = lookup(classId);
33472
- while (current) {
33473
- if (visited.has(current.id)) {
33474
- throw new CircularInheritanceError(
33475
- current.id,
33476
- chain.map((c) => c.id)
33477
- );
33478
- }
33479
- visited.add(current.id);
33480
- chain.push(current);
33481
- if (!current.extendsClassId) break;
33482
- current = lookup(current.extendsClassId);
33483
- }
33484
- return chain;
33485
- }
33486
- function mergeSchemas(chain) {
33487
- const baseFirst = [...chain].reverse();
33488
- const map = /* @__PURE__ */ new Map();
33489
- const order = [];
33490
- for (const schemaClass2 of baseFirst) {
33491
- const localKeys = getClassSchemaKeyOrder(schemaClass2);
33492
- if (schemaClass2.schemaKeyOrder !== void 0) {
33493
- order.splice(
33494
- 0,
33495
- order.length,
33496
- ...localKeys,
33497
- ...order.filter((key) => !localKeys.includes(key))
33498
- );
33499
- }
33500
- for (const key of localKeys) {
33501
- if (!order.includes(key)) order.push(key);
33502
- }
33503
- for (const [key, memberId] of Object.entries(schemaClass2.schema)) {
33504
- map.set(key, {
33505
- schemaKey: key,
33506
- memberId,
33507
- ownerClassId: schemaClass2.id
33508
- });
33509
- }
33510
- }
33511
- return order.flatMap((k) => {
33512
- const entry = map.get(k);
33513
- return entry ? [entry] : [];
33514
- });
33515
- }
33516
- function mergeInstanceSchema(classId, classes, members) {
33517
- const membersById = new Map(members.map((member) => [member.id, member]));
33518
- return mergeSchemas(resolveInheritanceChain(classId, classes)).filter(
33519
- (entry) => membersById.get(entry.memberId)?.isStatic !== true
33520
- );
33521
- }
33522
- function findSchemaPlacement(memberId, classes) {
33523
- for (const schemaClass2 of classes) {
33524
- for (const [key, placedMemberId] of Object.entries(schemaClass2.schema)) {
33525
- if (placedMemberId === memberId) {
33526
- return { ownerClass: schemaClass2, schemaKey: key };
33527
- }
33528
- }
33529
- }
33530
- return null;
33531
- }
33532
- function walkExtendsMemberChain(startId, members, pick, options) {
33533
- const requireKind = options?.requireKind;
33534
- const maxHops = options?.maxHops ?? 16;
33535
- let cursor = members.find((a) => a.id === startId);
33536
- for (let i = 0; cursor && i < maxHops; i++) {
33537
- if (requireKind !== void 0 && cursor.kind !== requireKind) {
33538
- return void 0;
33539
- }
33540
- const v = pick(cursor);
33541
- if (v !== void 0) return v;
33542
- const nextId = getOptionalString(cursor, "extendsMemberId");
33543
- if (nextId === void 0) return void 0;
33544
- cursor = members.find((a) => a.id === nextId);
33545
- }
33546
- return void 0;
33547
- }
33548
- function resolveMember2(member, members) {
33549
- const chain = [];
33550
- const visited = /* @__PURE__ */ new Set();
33551
- let current = member;
33552
- while (current) {
33553
- const id2 = getOptionalString(current, "id");
33554
- if (id2 !== void 0) {
33555
- if (visited.has(id2)) {
33556
- throw new CircularInheritanceError(id2, [...visited]);
33557
- }
33558
- visited.add(id2);
33559
- }
33560
- chain.push(current);
33561
- if (!isMemberOverrideMarker(current)) break;
33562
- const parentId = current.extendsMemberId;
33563
- current = members.find((a) => a.id === parentId);
33564
- }
33565
- const tail = chain[chain.length - 1];
33566
- if (!tail || isMemberOverrideMarker(tail)) {
33567
- const startId = getOptionalString(member, "id") ?? "(unknown)";
33568
- throw new UnresolvedMemberInheritanceError(startId);
33569
- }
33570
- const baseFirst = [...chain].reverse();
33571
- const merged = {};
33572
- for (const link of baseFirst) {
33573
- for (const [key, value] of Object.entries(link)) {
33574
- if (value === void 0) continue;
33575
- merged[key] = value;
33576
- }
33577
- }
33578
- delete merged.extendsMemberId;
33579
- if (member.isAbstract === void 0) {
33580
- delete merged.isAbstract;
33581
- } else {
33582
- merged.isAbstract = member.isAbstract;
33583
- }
33584
- return merged;
33585
- }
33586
- function descendantClassIds(classId, allClasses) {
33587
- const result = /* @__PURE__ */ new Set();
33588
- let added = true;
33589
- while (added) {
33590
- added = false;
33591
- for (const t of allClasses) {
33592
- if (t.id === classId) continue;
33593
- if (result.has(t.id)) continue;
33594
- if (t.extendsClassId === classId || t.extendsClassId && result.has(t.extendsClassId)) {
33595
- result.add(t.id);
33596
- added = true;
33597
- }
33598
- }
33599
- }
33600
- return result;
33601
- }
33602
- var CircularInheritanceError, UnresolvedMemberInheritanceError;
33603
- var init_inheritance = __esm({
33604
- "../src/models/classes/inheritance.ts"() {
33605
- "use strict";
33606
- init_member_kinds();
33607
- init_core();
33608
- init_structural_narrow();
33609
- init_classes();
33610
- CircularInheritanceError = class extends Error {
33611
- constructor(nodeId, chain) {
33612
- super(
33613
- `Circular inheritance detected at "${nodeId}"; chain: ${chain.join(" -> ")}`
33614
- );
33615
- this.nodeId = nodeId;
33616
- this.chain = chain;
33617
- this.name = "CircularInheritanceError";
33618
- }
33619
- nodeId;
33620
- chain;
33621
- };
33622
- UnresolvedMemberInheritanceError = class extends Error {
33623
- constructor(memberId) {
33624
- super(
33625
- `Inheritance chain for member "${memberId}" did not terminate at a non-override member`
33626
- );
33627
- this.memberId = memberId;
33628
- this.name = "UnresolvedMemberInheritanceError";
33629
- }
33630
- memberId;
33631
- };
33632
- }
33633
- });
33634
-
33635
- // ../src/models/interfaces/interface-graph.ts
33636
- function resolveInterfaceClosure(interfaceId, interfaces) {
33637
- const byId = new Map(
33638
- interfaces.map((neoInterface) => [neoInterface.id, neoInterface])
33639
- );
33640
- const result = [];
33641
- const emitted = /* @__PURE__ */ new Set();
33642
- const visiting = [];
33643
- const visitingSet = /* @__PURE__ */ new Set();
33644
- const visit = (id2) => {
33645
- if (visitingSet.has(id2)) {
33646
- throw new CircularInterfaceExtensionError(id2, [...visiting, id2]);
33647
- }
33648
- if (emitted.has(id2)) return;
33649
- const neoInterface = byId.get(id2);
33650
- if (!neoInterface) return;
33651
- visiting.push(id2);
33652
- visitingSet.add(id2);
33653
- result.push(neoInterface);
33654
- emitted.add(id2);
33655
- for (const parentId of neoInterface.extendsInterfaceIds ?? []) {
33656
- visit(parentId);
33657
- }
33658
- visiting.pop();
33659
- visitingSet.delete(id2);
33660
- };
33661
- visit(interfaceId);
33662
- return result;
33663
- }
33664
- function resolveImplementedInterfaces(classId, classes, interfaces) {
33665
- const result = [];
33666
- const seen = /* @__PURE__ */ new Set();
33667
- for (const schemaClass2 of resolveInheritanceChain(classId, classes)) {
33668
- for (const interfaceId of schemaClass2.implementsInterfaceIds ?? []) {
33669
- for (const neoInterface of resolveInterfaceClosure(
33670
- interfaceId,
33671
- interfaces
33672
- )) {
33673
- if (seen.has(neoInterface.id)) continue;
33674
- seen.add(neoInterface.id);
33675
- result.push(neoInterface);
33676
- }
33677
- }
33678
- }
33679
- return result;
33680
- }
33681
- function classImplementsInterface(classId, interfaceId, classes, interfaces) {
33682
- return resolveImplementedInterfaces(classId, classes, interfaces).some(
33683
- (neoInterface) => neoInterface.id === interfaceId
33684
- );
33685
- }
33686
- var CircularInterfaceExtensionError;
33687
- var init_interface_graph = __esm({
33688
- "../src/models/interfaces/interface-graph.ts"() {
33689
- "use strict";
33690
- init_inheritance();
33691
- CircularInterfaceExtensionError = class extends Error {
33692
- constructor(interfaceId, chain) {
33693
- super(
33694
- `Circular interface extension detected at "${interfaceId}"; chain: ${chain.join(" -> ")}`
33695
- );
33696
- this.interfaceId = interfaceId;
33697
- this.chain = chain;
33698
- this.name = "CircularInterfaceExtensionError";
33699
- }
33700
- interfaceId;
33701
- chain;
33702
- };
33703
- }
33704
- });
33705
-
33706
- // ../src/models/neoscript/type-info-compatibility.ts
33707
- function typeInfosInvariantlyEqual(left, right) {
33708
- if (left.type !== right.type) return false;
33709
- if (left.required !== right.required) return false;
33710
- if (left.type === NS_TYPE_VOID || right.type === NS_TYPE_VOID) return true;
33711
- if (left.type === NS_TYPE_UNKNOWN || right.type === NS_TYPE_UNKNOWN)
33712
- return true;
33713
- if (left.type === 7 /* Class */ && right.type === 7 /* Class */) {
33714
- if (left.classId !== right.classId) return false;
33715
- const leftArguments = left.typeArguments ?? {};
33716
- const rightArguments = right.typeArguments ?? {};
33717
- const leftKeys = Object.keys(leftArguments);
33718
- const rightKeys = Object.keys(rightArguments);
33719
- if (leftKeys.length !== rightKeys.length) return false;
33720
- for (const key of leftKeys) {
33721
- const rightArgument = rightArguments[key];
33722
- if (!rightArgument) return false;
33723
- if (!typeInfosInvariantlyEqual(leftArguments[key], rightArgument)) {
33724
- return false;
33725
- }
33726
- }
33727
- return true;
33728
- }
33729
- if (left.type === 22 /* Interface */ && right.type === 22 /* Interface */) {
33730
- return left.interfaceId === right.interfaceId;
33731
- }
33732
- if (left.type === 21 /* Generic */ && right.type === 21 /* Generic */) {
33733
- return left.ownerClassId === right.ownerClassId && left.genericParamId === right.genericParamId;
33734
- }
33735
- if (left.type === 8 /* Enum */ && right.type === 8 /* Enum */) {
33736
- return left.enumId === right.enumId;
33737
- }
33738
- if ((left.type === 6 /* List */ || left.type === 5 /* Dictionary */) && (right.type === 6 /* List */ || right.type === 5 /* Dictionary */)) {
33739
- if (left.keyEnumId !== right.keyEnumId) return false;
33740
- return typeInfosInvariantlyEqual(left.entryTypeInfo, right.entryTypeInfo);
33741
- }
33742
- if (left.type === 9 /* Lookup */ && right.type === 9 /* Lookup */) {
33743
- if (left.collectionMemberId !== right.collectionMemberId) return false;
33744
- if (left.collectionValueId !== right.collectionValueId) return false;
33745
- return typeInfosInvariantlyEqual(left.entryTypeInfo, right.entryTypeInfo);
33746
- }
33747
- return true;
33748
- }
33749
- var init_type_info_compatibility = __esm({
33750
- "../src/models/neoscript/type-info-compatibility.ts"() {
33751
- "use strict";
33752
- init_member_kinds();
33753
- init_inheritance();
33754
- init_interface_graph();
33755
- init_neoscript_types();
33756
- }
33757
- });
33758
-
33759
- // ../src/models/neoscript/index.ts
33760
- var init_neoscript = __esm({
33761
- "../src/models/neoscript/index.ts"() {
33762
- "use strict";
33763
- init_neoscript_types();
33764
- init_neoscript_guards();
33765
- init_type_info_compatibility();
33766
- }
33767
- });
33768
-
33769
33989
  // ../src/models/members/member-kinds.ts
33770
33990
  function isMemberAccessModifierKind(value) {
33771
33991
  if (value === "public") return true;
@@ -34216,6 +34436,9 @@ function isMemberBase(value) {
34216
34436
  if (typeof v.locked !== "boolean") return false;
34217
34437
  if (typeof v.required !== "boolean") return false;
34218
34438
  if (typeof v.isStatic !== "boolean") return false;
34439
+ if (v.isReadOnly !== void 0 && typeof v.isReadOnly !== "boolean") {
34440
+ return false;
34441
+ }
34219
34442
  if (!isMemberAccessModifierKind(v.accessModifierKind)) return false;
34220
34443
  if (v.isVirtual !== void 0 && typeof v.isVirtual !== "boolean") {
34221
34444
  return false;
@@ -34273,6 +34496,9 @@ function isMemberOverrideBase(value) {
34273
34496
  if (v.locked !== void 0 && typeof v.locked !== "boolean") return false;
34274
34497
  if (v.required !== void 0 && typeof v.required !== "boolean") return false;
34275
34498
  if (v.isStatic !== void 0 && v.isStatic !== false) return false;
34499
+ if (v.isReadOnly !== void 0 && typeof v.isReadOnly !== "boolean") {
34500
+ return false;
34501
+ }
34276
34502
  if (!isMemberAccessModifierKind(v.accessModifierKind)) return false;
34277
34503
  if (v.isVirtual !== void 0 && typeof v.isVirtual !== "boolean") {
34278
34504
  return false;
@@ -34457,175 +34683,184 @@ var init_member_kinds = __esm({
34457
34683
  }
34458
34684
  });
34459
34685
 
34460
- // ../src/models/members/member-kinds-db-response.ts
34461
- var init_member_kinds_db_response = __esm({
34462
- "../src/models/members/member-kinds-db-response.ts"() {
34463
- "use strict";
34464
- init_member_kinds();
34465
- }
34466
- });
34467
-
34468
- // ../src/models/members/unordered-list-membership.ts
34469
- var init_unordered_list_membership = __esm({
34470
- "../src/models/members/unordered-list-membership.ts"() {
34471
- "use strict";
34472
- init_member_kinds();
34473
- }
34474
- });
34475
-
34476
- // ../src/models/members/member-storage-key.ts
34477
- function normalizeStorageKeyDeclaration(declaration) {
34478
- if (declaration === void 0 || declaration === null) {
34479
- return STORAGE_KEY_INHERIT;
34480
- }
34481
- if (declaration.length === 0) return STORAGE_KEY_INHERIT;
34482
- if (declaration === STORAGE_KEY_ALL) {
34483
- throw new Error(
34484
- 'Storage key "all" is reserved for partition-scoped reads and cannot be declared on a member.'
34485
- );
34486
- }
34487
- return declaration;
34488
- }
34489
- function storageKeyReferencesParentClass(declaration) {
34490
- return declaration.includes(PARENT_CLASS_TEMPLATE_TOKEN);
34491
- }
34492
- function resolveMapKeyForCreatedValue(args) {
34493
- const declaration = normalizeStorageKeyDeclaration(args.declaration);
34494
- if (declaration === STORAGE_KEY_INHERIT) return args.parentMapKey;
34495
- if (declaration === MAIN_STORAGE_PARTITION) return null;
34496
- if (storageKeyReferencesParentClass(declaration)) {
34497
- if (args.parentClassId === void 0) {
34498
- throw new Error(
34499
- `Storage key "${declaration}" references ${PARENT_CLASS_TEMPLATE_TOKEN}, but the placement parent value has no classId (only class-valued parents can anchor a ${PARENT_CLASS_TEMPLATE_TOKEN} partition).`
34686
+ // ../src/models/classes/inheritance.ts
34687
+ function resolveInheritanceChain(classId, classes, draft) {
34688
+ const chain = [];
34689
+ const visited = /* @__PURE__ */ new Set();
34690
+ const lookup = (id2) => draft && draft.id === id2 ? draft : classes.find((t) => t.id === id2);
34691
+ let current = lookup(classId);
34692
+ while (current) {
34693
+ if (visited.has(current.id)) {
34694
+ throw new CircularInheritanceError(
34695
+ current.id,
34696
+ chain.map((c) => c.id)
34500
34697
  );
34501
34698
  }
34502
- return declaration.replaceAll(
34503
- PARENT_CLASS_TEMPLATE_TOKEN,
34504
- args.parentClassId
34505
- );
34699
+ visited.add(current.id);
34700
+ chain.push(current);
34701
+ if (!current.extendsClassId) break;
34702
+ current = lookup(current.extendsClassId);
34506
34703
  }
34507
- return declaration;
34704
+ return chain;
34508
34705
  }
34509
- var MAIN_STORAGE_PARTITION, STORAGE_KEY_INHERIT, STORAGE_KEY_ALL, PARENT_CLASS_TEMPLATE_TOKEN;
34510
- var init_member_storage_key = __esm({
34511
- "../src/models/members/member-storage-key.ts"() {
34512
- "use strict";
34513
- MAIN_STORAGE_PARTITION = "main";
34514
- STORAGE_KEY_INHERIT = "inherit";
34515
- STORAGE_KEY_ALL = "all";
34516
- PARENT_CLASS_TEMPLATE_TOKEN = "$parentClass";
34517
- }
34518
- });
34519
-
34520
- // ../src/models/members/storage-partitions.ts
34521
- function stampCreatedValuesMapKey(values, mapKey) {
34522
- const normalized = mapKey === void 0 || mapKey === null || mapKey.length === 0 || mapKey === MAIN_STORAGE_PARTITION ? null : mapKey;
34523
- for (const value of values) {
34524
- if (normalized === null) {
34525
- delete value.mapKey;
34526
- } else {
34527
- value.mapKey = normalized;
34706
+ function mergeSchemas(chain) {
34707
+ const baseFirst = [...chain].reverse();
34708
+ const map = /* @__PURE__ */ new Map();
34709
+ const order = [];
34710
+ for (const schemaClass2 of baseFirst) {
34711
+ const localKeys = getClassSchemaKeyOrder(schemaClass2);
34712
+ if (schemaClass2.schemaKeyOrder !== void 0) {
34713
+ order.splice(
34714
+ 0,
34715
+ order.length,
34716
+ ...localKeys,
34717
+ ...order.filter((key) => !localKeys.includes(key))
34718
+ );
34719
+ }
34720
+ for (const key of localKeys) {
34721
+ if (!order.includes(key)) order.push(key);
34722
+ }
34723
+ for (const [key, memberId] of Object.entries(schemaClass2.schema)) {
34724
+ map.set(key, {
34725
+ schemaKey: key,
34726
+ memberId,
34727
+ ownerClassId: schemaClass2.id
34728
+ });
34528
34729
  }
34529
34730
  }
34731
+ return order.flatMap((k) => {
34732
+ const entry = map.get(k);
34733
+ return entry ? [entry] : [];
34734
+ });
34530
34735
  }
34531
- function applyDeclaredStorageKeyOverrides(args) {
34532
- const createdById = new Map(
34533
- args.createdValues.map((value) => [value.id, value])
34736
+ function mergeInstanceSurfaceSchema(classId, classes, members) {
34737
+ const membersById = new Map(members.map((member) => [member.id, member]));
34738
+ return mergeSchemas(resolveInheritanceChain(classId, classes)).filter(
34739
+ (entry) => membersById.get(entry.memberId)?.isStatic !== true
34534
34740
  );
34535
- const parentOf = /* @__PURE__ */ new Map();
34536
- for (const value of args.createdValues) {
34537
- for (const childId of referencedValueIdCandidates(value.value)) {
34538
- if (createdById.has(childId)) parentOf.set(childId, value);
34741
+ }
34742
+ function mergeStoredInstanceSchema(classId, classes, members) {
34743
+ const membersById = new Map(members.map((member) => [member.id, member]));
34744
+ return mergeInstanceSurfaceSchema(classId, classes, members).filter(
34745
+ (entry) => membersById.get(entry.memberId)?.isReadOnly !== true
34746
+ );
34747
+ }
34748
+ function findSchemaPlacement(memberId, classes) {
34749
+ for (const schemaClass2 of classes) {
34750
+ for (const [key, placedMemberId] of Object.entries(schemaClass2.schema)) {
34751
+ if (placedMemberId === memberId) {
34752
+ return { ownerClass: schemaClass2, schemaKey: key };
34753
+ }
34539
34754
  }
34540
34755
  }
34541
- for (const value of args.createdValues) {
34542
- if (typeof value.containerId !== "string") continue;
34543
- const container = createdById.get(value.containerId);
34544
- if (container !== void 0) parentOf.set(value.id, container);
34545
- }
34546
- const depthOf = (value) => {
34547
- let depth = 0;
34548
- let current = value;
34549
- const seen = /* @__PURE__ */ new Set();
34550
- while (current !== void 0 && !seen.has(current.id)) {
34551
- seen.add(current.id);
34552
- current = parentOf.get(current.id);
34553
- if (current !== void 0) depth += 1;
34554
- }
34555
- return depth;
34556
- };
34557
- const boundaries = args.createdValues.filter((value) => {
34558
- const declaration = normalizeStorageKeyDeclaration(
34559
- args.declarationByValueId.get(value.id)
34560
- );
34561
- return declaration !== STORAGE_KEY_INHERIT;
34562
- }).sort((left, right) => depthOf(left) - depthOf(right));
34563
- for (const boundary of boundaries) {
34564
- const parentCreated = parentOf.get(boundary.id);
34565
- const parent = parentCreated !== void 0 ? {
34566
- mapKey: parentCreated.mapKey ?? null,
34567
- classId: parentCreated.classId ?? void 0
34568
- } : args.existingParentContextById?.get(boundary.id);
34569
- if (parent === void 0) {
34570
- throw new Error(
34571
- `Cannot resolve storage partition for created value "${boundary.id}": its placement parent is neither a created value nor supplied in existingParentContextById.`
34572
- );
34573
- }
34574
- const mapKey = resolveMapKeyForCreatedValue({
34575
- declaration: args.declarationByValueId.get(boundary.id),
34576
- parentMapKey: parent.mapKey,
34577
- parentClassId: parent.classId
34578
- });
34579
- for (const member of collectCreatedSubtreeValues(
34580
- boundary,
34581
- args.createdValues
34582
- )) {
34583
- if (mapKey === null || mapKey === MAIN_STORAGE_PARTITION) {
34584
- delete member.mapKey;
34585
- } else {
34586
- member.mapKey = mapKey;
34587
- }
34756
+ return null;
34757
+ }
34758
+ function walkExtendsMemberChain(startId, members, pick, options) {
34759
+ const requireKind = options?.requireKind;
34760
+ const maxHops = options?.maxHops ?? 16;
34761
+ let cursor = members.find((a) => a.id === startId);
34762
+ for (let i = 0; cursor && i < maxHops; i++) {
34763
+ if (requireKind !== void 0 && cursor.kind !== requireKind) {
34764
+ return void 0;
34588
34765
  }
34766
+ const v = pick(cursor);
34767
+ if (v !== void 0) return v;
34768
+ const nextId = getOptionalString(cursor, "extendsMemberId");
34769
+ if (nextId === void 0) return void 0;
34770
+ cursor = members.find((a) => a.id === nextId);
34589
34771
  }
34772
+ return void 0;
34590
34773
  }
34591
- function collectCreatedSubtreeValues(root, createdValues) {
34592
- const createdById = new Map(createdValues.map((value) => [value.id, value]));
34593
- const subtree = [];
34774
+ function resolveMember2(member, members) {
34775
+ const chain = [];
34594
34776
  const visited = /* @__PURE__ */ new Set();
34595
- const queue = [root];
34596
- while (queue.length > 0) {
34597
- const current = queue.shift();
34598
- if (current === void 0) break;
34599
- if (visited.has(current.id)) continue;
34600
- visited.add(current.id);
34601
- subtree.push(current);
34602
- for (const childId of referencedValueIdCandidates(current.value)) {
34603
- const child = createdById.get(childId);
34604
- if (child !== void 0) queue.push(child);
34777
+ let current = member;
34778
+ while (current) {
34779
+ const id2 = getOptionalString(current, "id");
34780
+ if (id2 !== void 0) {
34781
+ if (visited.has(id2)) {
34782
+ throw new CircularInheritanceError(id2, [...visited]);
34783
+ }
34784
+ visited.add(id2);
34605
34785
  }
34606
- for (const candidate of createdValues) {
34607
- if (candidate.containerId === current.id) queue.push(candidate);
34786
+ chain.push(current);
34787
+ if (!isMemberOverrideMarker(current)) break;
34788
+ const parentId = current.extendsMemberId;
34789
+ current = members.find((a) => a.id === parentId);
34790
+ }
34791
+ const tail = chain[chain.length - 1];
34792
+ if (!tail || isMemberOverrideMarker(tail)) {
34793
+ const startId = getOptionalString(member, "id") ?? "(unknown)";
34794
+ throw new UnresolvedMemberInheritanceError(startId);
34795
+ }
34796
+ const baseFirst = [...chain].reverse();
34797
+ const merged = {};
34798
+ for (const link of baseFirst) {
34799
+ for (const [key, value] of Object.entries(link)) {
34800
+ if (value === void 0) continue;
34801
+ merged[key] = value;
34608
34802
  }
34609
34803
  }
34610
- return subtree;
34611
- }
34612
- function referencedValueIdCandidates(value) {
34613
- if (Array.isArray(value)) {
34614
- return value.filter((entry) => typeof entry === "string");
34804
+ delete merged.extendsMemberId;
34805
+ if (member.isAbstract === void 0) {
34806
+ delete merged.isAbstract;
34807
+ } else {
34808
+ merged.isAbstract = member.isAbstract;
34615
34809
  }
34616
- if (typeof value === "object" && value !== null) {
34617
- return Object.values(value).filter(
34618
- (entry) => typeof entry === "string"
34619
- );
34810
+ if (member.isReadOnly === void 0) {
34811
+ delete merged.isReadOnly;
34812
+ } else {
34813
+ merged.isReadOnly = member.isReadOnly;
34620
34814
  }
34621
- return [];
34815
+ return merged;
34622
34816
  }
34623
- var init_storage_partitions = __esm({
34624
- "../src/models/members/storage-partitions.ts"() {
34817
+ function descendantClassIds(classId, allClasses) {
34818
+ const result = /* @__PURE__ */ new Set();
34819
+ let added = true;
34820
+ while (added) {
34821
+ added = false;
34822
+ for (const t of allClasses) {
34823
+ if (t.id === classId) continue;
34824
+ if (result.has(t.id)) continue;
34825
+ if (t.extendsClassId === classId || t.extendsClassId && result.has(t.extendsClassId)) {
34826
+ result.add(t.id);
34827
+ added = true;
34828
+ }
34829
+ }
34830
+ }
34831
+ return result;
34832
+ }
34833
+ var CircularInheritanceError, UnresolvedMemberInheritanceError, mergeInstanceSchema;
34834
+ var init_inheritance = __esm({
34835
+ "../src/models/classes/inheritance.ts"() {
34625
34836
  "use strict";
34626
- init_inheritance();
34627
- init_member_storage_key();
34628
- init_member_storage_key();
34837
+ init_member_kinds();
34838
+ init_core();
34839
+ init_structural_narrow();
34840
+ init_classes();
34841
+ CircularInheritanceError = class extends Error {
34842
+ constructor(nodeId, chain) {
34843
+ super(
34844
+ `Circular inheritance detected at "${nodeId}"; chain: ${chain.join(" -> ")}`
34845
+ );
34846
+ this.nodeId = nodeId;
34847
+ this.chain = chain;
34848
+ this.name = "CircularInheritanceError";
34849
+ }
34850
+ nodeId;
34851
+ chain;
34852
+ };
34853
+ UnresolvedMemberInheritanceError = class extends Error {
34854
+ constructor(memberId) {
34855
+ super(
34856
+ `Inheritance chain for member "${memberId}" did not terminate at a non-override member`
34857
+ );
34858
+ this.memberId = memberId;
34859
+ this.name = "UnresolvedMemberInheritanceError";
34860
+ }
34861
+ memberId;
34862
+ };
34863
+ mergeInstanceSchema = mergeInstanceSurfaceSchema;
34629
34864
  }
34630
34865
  });
34631
34866
 
@@ -34813,6 +35048,11 @@ function substituteMember(member, env, members) {
34813
35048
  } else {
34814
35049
  delete substituted.isAbstract;
34815
35050
  }
35051
+ if (resolved.isReadOnly !== void 0) {
35052
+ substituted.isReadOnly = resolved.isReadOnly;
35053
+ } else {
35054
+ delete substituted.isReadOnly;
35055
+ }
34816
35056
  delete substituted.extendsMemberId;
34817
35057
  return substituted;
34818
35058
  }
@@ -35104,6 +35344,195 @@ var init_generics = __esm({
35104
35344
  }
35105
35345
  });
35106
35346
 
35347
+ // ../src/models/members/read-only-members.ts
35348
+ var VALUELESS_KINDS;
35349
+ var init_read_only_members = __esm({
35350
+ "../src/models/members/read-only-members.ts"() {
35351
+ "use strict";
35352
+ init_inheritance();
35353
+ init_generics();
35354
+ init_member_kinds();
35355
+ init_member_storage();
35356
+ VALUELESS_KINDS = /* @__PURE__ */ new Set([
35357
+ 10 /* NSProperty */,
35358
+ 13 /* Function */,
35359
+ 23 /* NSFunction */
35360
+ ]);
35361
+ }
35362
+ });
35363
+
35364
+ // ../src/models/members/member-kinds-db-response.ts
35365
+ var init_member_kinds_db_response = __esm({
35366
+ "../src/models/members/member-kinds-db-response.ts"() {
35367
+ "use strict";
35368
+ init_member_kinds();
35369
+ }
35370
+ });
35371
+
35372
+ // ../src/models/members/unordered-list-membership.ts
35373
+ var init_unordered_list_membership = __esm({
35374
+ "../src/models/members/unordered-list-membership.ts"() {
35375
+ "use strict";
35376
+ init_member_kinds();
35377
+ }
35378
+ });
35379
+
35380
+ // ../src/models/members/member-storage-key.ts
35381
+ function normalizeStorageKeyDeclaration(declaration) {
35382
+ if (declaration === void 0 || declaration === null) {
35383
+ return STORAGE_KEY_INHERIT;
35384
+ }
35385
+ if (declaration.length === 0) return STORAGE_KEY_INHERIT;
35386
+ if (declaration === STORAGE_KEY_ALL) {
35387
+ throw new Error(
35388
+ 'Storage key "all" is reserved for partition-scoped reads and cannot be declared on a member.'
35389
+ );
35390
+ }
35391
+ return declaration;
35392
+ }
35393
+ function storageKeyReferencesParentClass(declaration) {
35394
+ return declaration.includes(PARENT_CLASS_TEMPLATE_TOKEN);
35395
+ }
35396
+ function resolveMapKeyForCreatedValue(args) {
35397
+ const declaration = normalizeStorageKeyDeclaration(args.declaration);
35398
+ if (declaration === STORAGE_KEY_INHERIT) return args.parentMapKey;
35399
+ if (declaration === MAIN_STORAGE_PARTITION) return null;
35400
+ if (storageKeyReferencesParentClass(declaration)) {
35401
+ if (args.parentClassId === void 0) {
35402
+ throw new Error(
35403
+ `Storage key "${declaration}" references ${PARENT_CLASS_TEMPLATE_TOKEN}, but the placement parent value has no classId (only class-valued parents can anchor a ${PARENT_CLASS_TEMPLATE_TOKEN} partition).`
35404
+ );
35405
+ }
35406
+ return declaration.replaceAll(
35407
+ PARENT_CLASS_TEMPLATE_TOKEN,
35408
+ args.parentClassId
35409
+ );
35410
+ }
35411
+ return declaration;
35412
+ }
35413
+ var MAIN_STORAGE_PARTITION, STORAGE_KEY_INHERIT, STORAGE_KEY_ALL, PARENT_CLASS_TEMPLATE_TOKEN;
35414
+ var init_member_storage_key = __esm({
35415
+ "../src/models/members/member-storage-key.ts"() {
35416
+ "use strict";
35417
+ MAIN_STORAGE_PARTITION = "main";
35418
+ STORAGE_KEY_INHERIT = "inherit";
35419
+ STORAGE_KEY_ALL = "all";
35420
+ PARENT_CLASS_TEMPLATE_TOKEN = "$parentClass";
35421
+ }
35422
+ });
35423
+
35424
+ // ../src/models/members/storage-partitions.ts
35425
+ function stampCreatedValuesMapKey(values, mapKey) {
35426
+ const normalized = mapKey === void 0 || mapKey === null || mapKey.length === 0 || mapKey === MAIN_STORAGE_PARTITION ? null : mapKey;
35427
+ for (const value of values) {
35428
+ if (normalized === null) {
35429
+ delete value.mapKey;
35430
+ } else {
35431
+ value.mapKey = normalized;
35432
+ }
35433
+ }
35434
+ }
35435
+ function applyDeclaredStorageKeyOverrides(args) {
35436
+ const createdById = new Map(
35437
+ args.createdValues.map((value) => [value.id, value])
35438
+ );
35439
+ const parentOf = /* @__PURE__ */ new Map();
35440
+ for (const value of args.createdValues) {
35441
+ for (const childId of referencedValueIdCandidates(value.value)) {
35442
+ if (createdById.has(childId)) parentOf.set(childId, value);
35443
+ }
35444
+ }
35445
+ for (const value of args.createdValues) {
35446
+ if (typeof value.containerId !== "string") continue;
35447
+ const container = createdById.get(value.containerId);
35448
+ if (container !== void 0) parentOf.set(value.id, container);
35449
+ }
35450
+ const depthOf = (value) => {
35451
+ let depth = 0;
35452
+ let current = value;
35453
+ const seen = /* @__PURE__ */ new Set();
35454
+ while (current !== void 0 && !seen.has(current.id)) {
35455
+ seen.add(current.id);
35456
+ current = parentOf.get(current.id);
35457
+ if (current !== void 0) depth += 1;
35458
+ }
35459
+ return depth;
35460
+ };
35461
+ const boundaries = args.createdValues.filter((value) => {
35462
+ const declaration = normalizeStorageKeyDeclaration(
35463
+ args.declarationByValueId.get(value.id)
35464
+ );
35465
+ return declaration !== STORAGE_KEY_INHERIT;
35466
+ }).sort((left, right) => depthOf(left) - depthOf(right));
35467
+ for (const boundary of boundaries) {
35468
+ const parentCreated = parentOf.get(boundary.id);
35469
+ const parent = parentCreated !== void 0 ? {
35470
+ mapKey: parentCreated.mapKey ?? null,
35471
+ classId: parentCreated.classId ?? void 0
35472
+ } : args.existingParentContextById?.get(boundary.id);
35473
+ if (parent === void 0) {
35474
+ throw new Error(
35475
+ `Cannot resolve storage partition for created value "${boundary.id}": its placement parent is neither a created value nor supplied in existingParentContextById.`
35476
+ );
35477
+ }
35478
+ const mapKey = resolveMapKeyForCreatedValue({
35479
+ declaration: args.declarationByValueId.get(boundary.id),
35480
+ parentMapKey: parent.mapKey,
35481
+ parentClassId: parent.classId
35482
+ });
35483
+ for (const member of collectCreatedSubtreeValues(
35484
+ boundary,
35485
+ args.createdValues
35486
+ )) {
35487
+ if (mapKey === null || mapKey === MAIN_STORAGE_PARTITION) {
35488
+ delete member.mapKey;
35489
+ } else {
35490
+ member.mapKey = mapKey;
35491
+ }
35492
+ }
35493
+ }
35494
+ }
35495
+ function collectCreatedSubtreeValues(root, createdValues) {
35496
+ const createdById = new Map(createdValues.map((value) => [value.id, value]));
35497
+ const subtree = [];
35498
+ const visited = /* @__PURE__ */ new Set();
35499
+ const queue = [root];
35500
+ while (queue.length > 0) {
35501
+ const current = queue.shift();
35502
+ if (current === void 0) break;
35503
+ if (visited.has(current.id)) continue;
35504
+ visited.add(current.id);
35505
+ subtree.push(current);
35506
+ for (const childId of referencedValueIdCandidates(current.value)) {
35507
+ const child = createdById.get(childId);
35508
+ if (child !== void 0) queue.push(child);
35509
+ }
35510
+ for (const candidate of createdValues) {
35511
+ if (candidate.containerId === current.id) queue.push(candidate);
35512
+ }
35513
+ }
35514
+ return subtree;
35515
+ }
35516
+ function referencedValueIdCandidates(value) {
35517
+ if (Array.isArray(value)) {
35518
+ return value.filter((entry) => typeof entry === "string");
35519
+ }
35520
+ if (typeof value === "object" && value !== null) {
35521
+ return Object.values(value).filter(
35522
+ (entry) => typeof entry === "string"
35523
+ );
35524
+ }
35525
+ return [];
35526
+ }
35527
+ var init_storage_partitions = __esm({
35528
+ "../src/models/members/storage-partitions.ts"() {
35529
+ "use strict";
35530
+ init_inheritance();
35531
+ init_member_storage_key();
35532
+ init_member_storage_key();
35533
+ }
35534
+ });
35535
+
35107
35536
  // ../src/models/unity/unity-import-settings.ts
35108
35537
  function buildDefaultUnityTexture2DImportSettings() {
35109
35538
  return {
@@ -36233,7 +36662,7 @@ function buildDefaultMemberValue(args) {
36233
36662
  const unboundParamId = firstUnboundParamId(childEnv);
36234
36663
  if (unboundParamId !== null) {
36235
36664
  throw new Error(
36236
- `Cannot create a value of open generic class "${effectiveClass?.name ?? effectiveClassId}": generic param "${unboundParamId}" is unbound \u2014 pick a closed descendant or construct the slot's class arguments (specs/class-generics.md Decision 6).`
36665
+ `Cannot create a value of open generic class "${effectiveClass?.name ?? effectiveClassId}": generic param "${unboundParamId}" is unbound \u2014 pick a closed descendant or construct the slot's class arguments (https://github.com/ryanbliss/neo-compose-specs/tree/main/new-features/complete/class-generics.md Decision 6).`
36237
36666
  );
36238
36667
  }
36239
36668
  if (member.classArguments != null && Object.keys(member.classArguments).length > 0) {
@@ -36250,7 +36679,7 @@ function buildDefaultMemberValue(args) {
36250
36679
  );
36251
36680
  }
36252
36681
  }
36253
- const merged = mergeInstanceSchema(
36682
+ const merged = mergeStoredInstanceSchema(
36254
36683
  effectiveClassId,
36255
36684
  args.document.classes,
36256
36685
  args.document.members
@@ -36556,7 +36985,7 @@ function formatDefaultReferenceLocation(memberName, key) {
36556
36985
  }
36557
36986
  function getSchemaEntryMap(document, classId) {
36558
36987
  return new Map(
36559
- mergeInstanceSchema(classId, document.classes, document.members).map(
36988
+ mergeStoredInstanceSchema(classId, document.classes, document.members).map(
36560
36989
  (entry) => [entry.schemaKey, entry]
36561
36990
  )
36562
36991
  );
@@ -36663,7 +37092,7 @@ function primitiveFallbackValue(document, member) {
36663
37092
  }
36664
37093
  if (member.kind === 21 /* Generic */) {
36665
37094
  throw new Error(
36666
- `Unreachable: Generic member "${member.name}" must be substituted through its binding environment before default value construction \u2014 a substitution site was skipped upstream (specs/class-generics.md \xA74.1).`
37095
+ `Unreachable: Generic member "${member.name}" must be substituted through its binding environment before default value construction \u2014 a substitution site was skipped upstream (https://github.com/ryanbliss/neo-compose-specs/tree/main/new-features/complete/class-generics.md \xA74.1).`
36667
37096
  );
36668
37097
  }
36669
37098
  throw new Error(
@@ -37096,7 +37525,7 @@ var init_effective_storage = __esm({
37096
37525
  }
37097
37526
  /**
37098
37527
  * The set of effective storages of `memberId` across every placement
37099
- * (specs/member-storage.md §2.3, generalized to the placement *graph*).
37528
+ * (https://github.com/ryanbliss/neo-compose-specs/tree/main/new-features/complete/member-storage.md §2.3, generalized to the placement *graph*).
37100
37529
  */
37101
37530
  effectiveStorages(memberId) {
37102
37531
  const cached = this.effectiveByMemberId.get(memberId);
@@ -37494,6 +37923,7 @@ var init_members = __esm({
37494
37923
  "../src/models/members/index.ts"() {
37495
37924
  "use strict";
37496
37925
  init_member_storage();
37926
+ init_read_only_members();
37497
37927
  init_member_kinds();
37498
37928
  init_member_kinds_db_response();
37499
37929
  init_unordered_list_membership();
@@ -40331,6 +40761,11 @@ function lowerSeedRow(context, member, sourceExpression, source, valueId, path,
40331
40761
  if (effectiveClass === void 0) {
40332
40762
  throw new Error(`Unknown value class ${member.classId}.`);
40333
40763
  }
40764
+ if (effectiveClass.declarationModifier === "abstract") {
40765
+ throw new Error(
40766
+ `Cannot instantiate abstract class ${effectiveClass.name} for ${path}. Use a concrete descendant instead.`
40767
+ );
40768
+ }
40334
40769
  classId = effectiveClass.id;
40335
40770
  const body = {};
40336
40771
  for (const assignment of expression.initializer ?? []) {
@@ -40528,6 +40963,11 @@ function lowerClassValue(context, member, expression, base, source) {
40528
40963
  const schemaClass2 = context.classes.get(classId);
40529
40964
  if (schemaClass2 === void 0)
40530
40965
  throw new Error(`Unknown value class ${classId}.`);
40966
+ if (schemaClass2.declarationModifier === "abstract") {
40967
+ throw new Error(
40968
+ `Cannot instantiate abstract class ${schemaClass2.name} for value ${String(base.id)}. Use a concrete descendant instead.`
40969
+ );
40970
+ }
40531
40971
  const baseBody = isObjectRecord2(base.value) ? base.value : {};
40532
40972
  const body = { ...baseBody };
40533
40973
  for (const assignment of expression.initializer ?? []) {
@@ -41368,7 +41808,7 @@ function classValue(context, member, value, visited, targetTyped) {
41368
41808
  return targetTyped ? "new()" : `new ${name}()`;
41369
41809
  const schema = isObjectRecord2(schemaClass2.schema) ? schemaClass2.schema : {};
41370
41810
  const order = Array.isArray(schemaClass2.schemaKeyOrder) ? schemaClass2.schemaKeyOrder.filter(
41371
- (entry) => typeof entry === "string"
41811
+ (entry) => typeof entry === "string" && typeof schema[entry] === "string"
41372
41812
  ) : Object.keys(schema);
41373
41813
  const fields = [];
41374
41814
  for (const key of [
@@ -44914,7 +45354,7 @@ var init_project_migration_types = __esm({
44914
45354
  });
44915
45355
 
44916
45356
  // ../src/models/project/internal-record-relations.ts
44917
- function worldClassContract(relationKind, sourceWorldKind, targetWorldKind, targetClassPolicy, merge, allowAbstractTarget) {
45357
+ function worldClassContract(relationKind, sourceWorldKind, targetWorldKind, targetClassPolicy, merge, allowAbstractTarget, allowDirectSystemSource = true) {
44918
45358
  return {
44919
45359
  relationKind,
44920
45360
  endpointPairs: [{ sourceRecordKind: "class", targetRecordKind: "class" }],
@@ -44924,6 +45364,7 @@ function worldClassContract(relationKind, sourceWorldKind, targetWorldKind, targ
44924
45364
  allowCycles: false,
44925
45365
  sourceWorldKind,
44926
45366
  targetWorldKind,
45367
+ allowDirectSystemSource,
44927
45368
  allowAbstractTarget
44928
45369
  };
44929
45370
  }
@@ -45018,6 +45459,7 @@ var init_internal_record_relations = __esm({
45018
45459
  NeoWorldSystemClassKind.TileLayer,
45019
45460
  "exact",
45020
45461
  "nearest-single",
45462
+ false,
45021
45463
  false
45022
45464
  ),
45023
45465
  worldClassContract(
@@ -45026,6 +45468,7 @@ var init_internal_record_relations = __esm({
45026
45468
  NeoWorldSystemClassKind.ObjectLayer,
45027
45469
  "exact",
45028
45470
  "nearest-single",
45471
+ false,
45029
45472
  false
45030
45473
  ),
45031
45474
  {
@@ -52450,7 +52893,13 @@ function evalPointer(pointer, scope, ctx) {
52450
52893
  case "staticMember" /* staticMember */:
52451
52894
  return evalStaticMember(pointer.memberId, ctx);
52452
52895
  case "keyOf" /* keyOf */:
52453
- return evalKeyOf(pointer.keyOf, scope, ctx, pointer.optional === true);
52896
+ return evalKeyOf(
52897
+ pointer.keyOf,
52898
+ scope,
52899
+ ctx,
52900
+ pointer.optional === true,
52901
+ pointer.memberId
52902
+ );
52454
52903
  case "operation" /* operation */:
52455
52904
  return evalOperation(pointer.operation, scope, ctx);
52456
52905
  case "function" /* function */:
@@ -53213,7 +53662,7 @@ function isRuntimeSpriteValue(value) {
53213
53662
  const sliceIndex = value.sliceIndex;
53214
53663
  return typeof sliceIndex === "number" && Number.isInteger(sliceIndex) && sliceIndex >= 0;
53215
53664
  }
53216
- function evalKeyOf(keyOf, scope, ctx, optional = false) {
53665
+ function evalKeyOf(keyOf, scope, ctx, optional = false, memberId) {
53217
53666
  const receiver = evalPointer(keyOf.pointer, scope, ctx);
53218
53667
  if (optional && (receiver === null || receiver === void 0)) {
53219
53668
  return null;
@@ -53261,6 +53710,11 @@ function evalKeyOf(keyOf, scope, ctx, optional = false) {
53261
53710
  if (assetMember.kind === "ok") return assetMember.value;
53262
53711
  const dispatched = dispatchSchemaMember(receiver, k, ctx);
53263
53712
  if (dispatched.kind === "ok") return dispatched.value;
53713
+ if (!dispatched.matchedMember && memberId !== void 0) {
53714
+ const member = evalMemberById(ctx.vm, memberId);
53715
+ const defaultValue = readOnlyDeclarationDefault(member, memberId, ctx);
53716
+ if (defaultValue.matched) return defaultValue.value;
53717
+ }
53264
53718
  if (Object.prototype.hasOwnProperty.call(receiver, k)) {
53265
53719
  const at = receiver[k];
53266
53720
  return resolveValueIfId(at, ctx);
@@ -53273,13 +53727,15 @@ function evalKeyOf(keyOf, scope, ctx, optional = false) {
53273
53727
  }
53274
53728
  function dispatchSchemaMember(receiver, schemaKey, ctx) {
53275
53729
  if (typeof receiver !== "object" || receiver === null) {
53276
- return { kind: "no-info" };
53730
+ return { kind: "no-info", matchedMember: false };
53277
53731
  }
53278
53732
  const member = resolveRuntimeSchemaMember(receiver, schemaKey, ctx);
53279
- if (!member) return { kind: "no-info" };
53733
+ if (!member) return { kind: "no-info", matchedMember: false };
53734
+ const defaultValue = readOnlyDeclarationDefault(member, member.id, ctx);
53735
+ if (defaultValue.matched) return { kind: "ok", value: defaultValue.value };
53280
53736
  if (member.kind === 10 /* NSProperty */) {
53281
53737
  if (!resolveCompiledGetter(member.id, ctx)) {
53282
- return { kind: "no-info" };
53738
+ return { kind: "no-info", matchedMember: true };
53283
53739
  }
53284
53740
  return {
53285
53741
  kind: "ok",
@@ -53287,18 +53743,46 @@ function dispatchSchemaMember(receiver, schemaKey, ctx) {
53287
53743
  };
53288
53744
  }
53289
53745
  if (!Object.prototype.hasOwnProperty.call(receiver, schemaKey)) {
53290
- return { kind: "no-info" };
53746
+ return { kind: "no-info", matchedMember: true };
53291
53747
  }
53292
53748
  const at = receiver[schemaKey];
53293
53749
  return { kind: "ok", value: resolveValueIfIdForMember(at, member, ctx) };
53294
53750
  }
53751
+ function readOnlyDeclarationDefault(member, memberId, ctx) {
53752
+ const readOnly = member?.isReadOnly;
53753
+ if (readOnly !== true) return { matched: false };
53754
+ if (member?.defaultValue === void 0 || member.defaultValue === null) {
53755
+ throw new NSGetterRuntimeError(
53756
+ `Read-only member '${member?.name ?? memberId}' has no declaration default.`
53757
+ );
53758
+ }
53759
+ let value = member.defaultValue.value;
53760
+ if (isMemberString(member) && member.localizable !== false && typeof value === "string") {
53761
+ value = resolveLocalizedTextId(value, ctx);
53762
+ } else if (shouldUnwrapSingleLookupValue(member)) {
53763
+ value = unwrapSingleLookupValue(value, ctx);
53764
+ }
53765
+ return { matched: true, value };
53766
+ }
53295
53767
  function resolveRuntimeSchemaMember(receiver, schemaKey, ctx) {
53296
53768
  if (typeof receiver !== "object" || receiver === null) return null;
53297
53769
  const row = trackedRowForValueReference(receiver, ctx);
53298
- const runtimeClassId = row === null ? void 0 : classIdForValueRow(row, ctx);
53770
+ if (row === null) return null;
53771
+ const runtimeClassId = classIdForValueRow(row, ctx);
53299
53772
  if (!runtimeClassId) return null;
53300
53773
  const memberId = cachedMergedSchemaMemberId(runtimeClassId, schemaKey, ctx);
53301
- return memberId === null ? null : evalMemberById(ctx.vm, memberId);
53774
+ if (memberId === null) return null;
53775
+ const member = evalMemberById(ctx.vm, memberId);
53776
+ if (member === null) return null;
53777
+ if (member.kind !== 21 /* Generic */) return member;
53778
+ const env = runtimeGenericEnvForValueRow(row, ctx, /* @__PURE__ */ new Set());
53779
+ try {
53780
+ return substituteMember(member, env, ctx.vm.members);
53781
+ } catch (error) {
53782
+ throw new NSGetterRuntimeError(
53783
+ `Cannot resolve runtime member '${member.name}' for Class '${runtimeClassId}': ${error instanceof Error ? error.message : String(error)}`
53784
+ );
53785
+ }
53302
53786
  }
53303
53787
  function resolveAssetRuntimeMember(receiver, memberName, ctx) {
53304
53788
  const record3 = receiver;
@@ -54031,7 +54515,11 @@ function validateClassConstructorDescriptor(info, ctx) {
54031
54515
  }
54032
54516
  let mergedSchema;
54033
54517
  try {
54034
- mergedSchema = mergeInstanceSchema(classId, ctx.vm.classes, ctx.vm.members);
54518
+ mergedSchema = mergeStoredInstanceSchema(
54519
+ classId,
54520
+ ctx.vm.classes,
54521
+ ctx.vm.members
54522
+ );
54035
54523
  } catch (error) {
54036
54524
  throw new NSGetterRuntimeError(
54037
54525
  `Cannot validate constructor for '${schemaClass2.name}': ${error instanceof Error ? error.message : String(error)}`
@@ -54062,6 +54550,16 @@ function validateClassConstructorDescriptor(info, ctx) {
54062
54550
  }
54063
54551
  seenSchemaKeys.add(field.schemaKey);
54064
54552
  seenMemberIds.add(field.memberId);
54553
+ const surfaceEntry = mergeInstanceSchema(
54554
+ classId,
54555
+ ctx.vm.classes,
54556
+ ctx.vm.members
54557
+ ).find((entry) => entry.schemaKey === field.schemaKey);
54558
+ if (surfaceEntry !== void 0 && evalMemberById(ctx.vm, surfaceEntry.memberId)?.isReadOnly === true) {
54559
+ throw new NSGetterRuntimeError(
54560
+ `Constructor field '${field.schemaKey}' on '${schemaClass2.name}' is a read-only declaration member and cannot have an instance value.`
54561
+ );
54562
+ }
54065
54563
  const schemaEntry = schemaByKey.get(field.schemaKey);
54066
54564
  if (schemaEntry === void 0) {
54067
54565
  throw new NSGetterRuntimeError(
@@ -56104,7 +56602,7 @@ function validateCreatedMigrationGraph(args) {
56104
56602
  }
56105
56603
  let merged;
56106
56604
  try {
56107
- merged = mergeInstanceSchema(classId, args.classes, args.members);
56605
+ merged = mergeStoredInstanceSchema(classId, args.classes, args.members);
56108
56606
  } catch (error) {
56109
56607
  throw graphError(
56110
56608
  args,