@neocompose/cli 0.6.7 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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",
@@ -15782,6 +15794,7 @@ function buildProjectGraph(documents) {
15782
15794
  (memberId) => {
15783
15795
  const member = membersBySymbolId.get(memberId);
15784
15796
  if (!member || member.kind !== "field") return null;
15797
+ if (member.modifiers.includes("readonly")) return null;
15785
15798
  return {
15786
15799
  required: member.initializer === void 0,
15787
15800
  defaultValue: member.initializer?.text
@@ -15839,6 +15852,7 @@ function sourceMemberSymbol(member, owner, typeIds, source) {
15839
15852
  );
15840
15853
  const type = sourceTypeRef(member.type, owner, typeIds);
15841
15854
  const staticMember = member.modifiers.includes("static");
15855
+ const readonlyDeclaration = member.modifiers.includes("readonly");
15842
15856
  const common = {
15843
15857
  id: id2,
15844
15858
  name: member.name,
@@ -15851,7 +15865,8 @@ function sourceMemberSymbol(member, owner, typeIds, source) {
15851
15865
  owner.declaration.kind
15852
15866
  ),
15853
15867
  location: location(owner.uri, member.range),
15854
- selectionLocation: location(owner.uri, member.nameRange)
15868
+ selectionLocation: location(owner.uri, member.nameRange),
15869
+ ...readonlyDeclaration ? { readonlyDeclaration: true } : {}
15855
15870
  };
15856
15871
  if (member.kind === "function") {
15857
15872
  return {
@@ -15882,8 +15897,8 @@ function sourceMemberSymbol(member, owner, typeIds, source) {
15882
15897
  return {
15883
15898
  ...common,
15884
15899
  kind: "property",
15885
- writable: writability !== "immutable" && writability !== "readOnly",
15886
- writability
15900
+ writable: !readonlyDeclaration && writability !== "immutable" && writability !== "readOnly",
15901
+ writability: readonlyDeclaration ? "readOnly" : writability
15887
15902
  };
15888
15903
  }
15889
15904
  function substituteInheritedSourceMember(member, baseInfo, resolvedBaseType) {
@@ -16225,6 +16240,7 @@ function createEnvironment2(documents) {
16225
16240
  const declarations = /* @__PURE__ */ new Map();
16226
16241
  const classes = /* @__PURE__ */ new Map();
16227
16242
  const interfaces = /* @__PURE__ */ new Map();
16243
+ const memberOwners = /* @__PURE__ */ new Map();
16228
16244
  const typeKinds = /* @__PURE__ */ new Map();
16229
16245
  for (const [uri, document] of documents) {
16230
16246
  for (const declaration of document.declarations) {
@@ -16243,11 +16259,15 @@ function createEnvironment2(documents) {
16243
16259
  if (declaration.kind === "class") {
16244
16260
  const ref = {
16245
16261
  uri,
16262
+ sourceKind: document.kind,
16246
16263
  declaration,
16247
16264
  location: { uri, range: declaration.nameRange }
16248
16265
  };
16249
16266
  declarations.set(declaration.name, ref);
16250
16267
  classes.set(declaration.name, ref);
16268
+ for (const member of declaration.members) {
16269
+ memberOwners.set(member, ref);
16270
+ }
16251
16271
  typeKinds.set(declaration.name, {
16252
16272
  kind: "class",
16253
16273
  location: ref.location,
@@ -16256,6 +16276,7 @@ function createEnvironment2(documents) {
16256
16276
  } else {
16257
16277
  const ref = {
16258
16278
  uri,
16279
+ sourceKind: document.kind,
16259
16280
  declaration,
16260
16281
  location: { uri, range: declaration.nameRange }
16261
16282
  };
@@ -16269,7 +16290,7 @@ function createEnvironment2(documents) {
16269
16290
  }
16270
16291
  }
16271
16292
  }
16272
- return { declarations, classes, interfaces, typeKinds };
16293
+ return { declarations, classes, interfaces, memberOwners, typeKinds };
16273
16294
  }
16274
16295
  function validatePersistedNames(documents, diagnostics) {
16275
16296
  for (const [uri, document] of documents) {
@@ -16740,6 +16761,16 @@ function validateMembers(environment, diagnostics) {
16740
16761
  );
16741
16762
  }
16742
16763
  for (const modifier of member.modifiers) {
16764
+ if (modifier === "readonly") {
16765
+ diagnose(
16766
+ diagnostics,
16767
+ ref.uri,
16768
+ member.nameRange,
16769
+ "readonly-interface-member",
16770
+ `Interface member '${member.name}' cannot use 'readonly'; readonly is valid only on a class field.`
16771
+ );
16772
+ continue;
16773
+ }
16743
16774
  if (modifier === "async" && member.kind === "function") continue;
16744
16775
  if (isNeoSourceAccessModifier(modifier)) continue;
16745
16776
  diagnose(
@@ -16754,7 +16785,7 @@ function validateMembers(environment, diagnostics) {
16754
16785
  validateInterfaceMemberConflicts(ref, environment, diagnostics);
16755
16786
  }
16756
16787
  for (const ref of environment.classes.values()) {
16757
- validateClassMemberModifiers(ref, diagnostics);
16788
+ validateClassMemberModifiers(ref, environment, diagnostics);
16758
16789
  validateClassOverrides(ref, environment, diagnostics);
16759
16790
  if (ref.declaration.modifiers.includes("abstract")) {
16760
16791
  validateAbstractInterfaceMembers(ref, environment, diagnostics);
@@ -16863,10 +16894,58 @@ function validateAbstractInterfaceMembers(ref, environment, diagnostics) {
16863
16894
  );
16864
16895
  }
16865
16896
  }
16866
- function validateClassMemberModifiers(ref, diagnostics) {
16897
+ function validateClassMemberModifiers(ref, environment, diagnostics) {
16867
16898
  const classIsAbstract = ref.declaration.modifiers.includes("abstract");
16868
16899
  for (const member of ref.declaration.members) {
16869
16900
  const modifiers = new Set(member.modifiers);
16901
+ if (modifiers.has("readonly")) {
16902
+ if (ref.sourceKind === "flow") {
16903
+ diagnose(
16904
+ diagnostics,
16905
+ ref.uri,
16906
+ member.nameRange,
16907
+ "readonly-neoflow-binding",
16908
+ `NeoFlow binding '${member.name}' cannot use 'readonly'; readonly is valid only on a class field in a .neo definition.`
16909
+ );
16910
+ } else if (member.kind !== "field") {
16911
+ diagnose(
16912
+ diagnostics,
16913
+ ref.uri,
16914
+ member.nameRange,
16915
+ "readonly-non-field-member",
16916
+ `Member '${member.name}' cannot use 'readonly'; readonly is valid only on a class field.`
16917
+ );
16918
+ } else {
16919
+ if (modifiers.has("static")) {
16920
+ diagnose(
16921
+ diagnostics,
16922
+ ref.uri,
16923
+ member.nameRange,
16924
+ "static-readonly-member",
16925
+ `Read-only field '${member.name}' cannot be static.`
16926
+ );
16927
+ }
16928
+ if (member.initializer === void 0 && !modifiers.has("abstract")) {
16929
+ diagnose(
16930
+ diagnostics,
16931
+ ref.uri,
16932
+ member.nameRange,
16933
+ "readonly-member-initializer",
16934
+ `Read-only field '${member.name}' requires an explicit initializer.`
16935
+ );
16936
+ }
16937
+ const storage = resolvedSourceMemberStorage(ref, member, environment);
16938
+ if (storage !== "immutable") {
16939
+ diagnose(
16940
+ diagnostics,
16941
+ ref.uri,
16942
+ member.nameRange,
16943
+ "readonly-member-storage",
16944
+ `Read-only field '${member.name}' requires resolved @storage(allowed: .Immutable).`
16945
+ );
16946
+ }
16947
+ }
16948
+ }
16870
16949
  if (modifiers.has("sealed")) {
16871
16950
  diagnose(
16872
16951
  diagnostics,
@@ -17018,7 +17097,38 @@ function validateClassOverrides(ref, environment, diagnostics) {
17018
17097
  environment,
17019
17098
  true
17020
17099
  );
17100
+ const readOnlyContract = candidates.find(
17101
+ (candidate) => candidate.member.modifiers.includes("abstract") && candidate.member.modifiers.includes("readonly")
17102
+ );
17103
+ if (readOnlyContract !== void 0 && !member.modifiers.includes("readonly")) {
17104
+ diagnose(
17105
+ diagnostics,
17106
+ ref.uri,
17107
+ member.nameRange,
17108
+ "override-readonly-contract",
17109
+ `Override '${ref.declaration.name}.${member.name}' must remain readonly because '${readOnlyContract.owner.declaration.name}.${readOnlyContract.member.name}' is an abstract readonly contract.`,
17110
+ related(
17111
+ memberLocation(readOnlyContract),
17112
+ "The abstract readonly contract is declared here."
17113
+ )
17114
+ );
17115
+ continue;
17116
+ }
17021
17117
  if (mismatch) {
17118
+ if (mismatch === "the implementation must provide a setter" && member.modifiers.includes("readonly")) {
17119
+ diagnose(
17120
+ diagnostics,
17121
+ ref.uri,
17122
+ member.nameRange,
17123
+ "override-readonly-setter",
17124
+ `Read-only override '${ref.declaration.name}.${member.name}' cannot satisfy the setter required by abstract member '${target.owner.declaration.name}.${target.member.name}'.`,
17125
+ related(
17126
+ memberLocation(target),
17127
+ "The settable abstract member is declared here."
17128
+ )
17129
+ );
17130
+ continue;
17131
+ }
17022
17132
  diagnose(
17023
17133
  diagnostics,
17024
17134
  ref.uri,
@@ -17049,13 +17159,14 @@ function validateConcreteClass(ref, environment, diagnostics) {
17049
17159
  const requiredContracts = allRequiredInterfaceMembers(ref, environment);
17050
17160
  for (const required2 of requiredContracts.values()) {
17051
17161
  const implementation = effective.get(required2.member.name.toLowerCase());
17052
- if (implementation && !implementation.member.modifiers.includes("abstract") && !implementation.member.modifiers.includes("static") && memberCompatibility(
17162
+ const mismatch = implementation ? memberCompatibility(
17053
17163
  implementation.member,
17054
17164
  implementation.substitution,
17055
17165
  required2,
17056
17166
  true,
17057
17167
  environment
17058
- ) === null) {
17168
+ ) : null;
17169
+ if (implementation && !implementation.member.modifiers.includes("abstract") && !implementation.member.modifiers.includes("static") && mismatch === null) {
17059
17170
  continue;
17060
17171
  }
17061
17172
  diagnose(
@@ -17063,7 +17174,7 @@ function validateConcreteClass(ref, environment, diagnostics) {
17063
17174
  ref.uri,
17064
17175
  ref.declaration.nameRange,
17065
17176
  "interface-member-not-implemented",
17066
- 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}'.`,
17177
+ 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}'.`,
17067
17178
  [
17068
17179
  {
17069
17180
  location: interfaceMemberLocation(required2),
@@ -17196,7 +17307,7 @@ function memberCompatibility(implementation, implementationSubstitution, target,
17196
17307
  implementationSubstitution
17197
17308
  );
17198
17309
  const targetType = typeShape(target.member.type, target.substitution);
17199
- const targetAccessors = target.member.kind === "function" ? { get: false, set: false } : valueAccessors(target.member);
17310
+ const targetAccessors = target.member.kind === "function" ? { get: false, set: false } : valueAccessors(target.member, environment);
17200
17311
  const permitsCovariantGetter = allowStoredInterfaceImplementation && implementationFamily === "value" && targetAccessors.get && !targetAccessors.set;
17201
17312
  const permitsStoredOverrideNullability = allowStoredOverrideNullability && implementation.kind === "field" && target.member.kind === "field" && typesEqual(
17202
17313
  { ...implementationType, nullable: false },
@@ -17206,7 +17317,7 @@ function memberCompatibility(implementation, implementationSubstitution, target,
17206
17317
  return `type '${formatType2(implementationType)}' does not match '${formatType2(targetType)}'`;
17207
17318
  }
17208
17319
  if (implementation.kind !== "function" || target.member.kind !== "function") {
17209
- const implementationAccessors = valueAccessors(implementation);
17320
+ const implementationAccessors = valueAccessors(implementation, environment);
17210
17321
  if (targetAccessors.get && !implementationAccessors.get) {
17211
17322
  return "the implementation must provide a getter";
17212
17323
  }
@@ -17238,14 +17349,48 @@ function memberCompatibility(implementation, implementationSubstitution, target,
17238
17349
  }
17239
17350
  return null;
17240
17351
  }
17241
- function valueAccessors(member) {
17242
- if (member.kind === "field") return { get: true, set: true };
17352
+ function valueAccessors(member, environment) {
17353
+ if (member.kind === "field") {
17354
+ const owner = environment.memberOwners.get(member);
17355
+ const immutable = member.modifiers.includes("abstract") && owner !== void 0 && resolvedSourceMemberStorage(owner, member, environment) === "immutable";
17356
+ return {
17357
+ get: true,
17358
+ set: !member.modifiers.includes("readonly") && !immutable
17359
+ };
17360
+ }
17243
17361
  if (member.kind === "function") return { get: false, set: false };
17244
17362
  return {
17245
17363
  get: /\bget\b/.test(member.body.text),
17246
17364
  set: /\bset\b/.test(member.body.text)
17247
17365
  };
17248
17366
  }
17367
+ function resolvedSourceMemberStorage(owner, member, environment, seen = /* @__PURE__ */ new Set()) {
17368
+ const own = member.annotations.find(
17369
+ (annotation2) => annotation2.name === "storage"
17370
+ );
17371
+ if (own) {
17372
+ const allowed = own.arguments.find((argument2) => argument2.name === "allowed") ?? own.arguments[0];
17373
+ const normalized = allowed?.text.replace(/\s+/g, "").toLowerCase();
17374
+ if (normalized === ".immutable") return "immutable";
17375
+ if (normalized === ".save") return "save";
17376
+ if (normalized === ".session") return "session";
17377
+ return null;
17378
+ }
17379
+ if (!member.modifiers.includes("override")) return null;
17380
+ const key = `${owner.declaration.name}:${member.name}`;
17381
+ if (seen.has(key)) return null;
17382
+ seen.add(key);
17383
+ const candidates = inheritedClassMembers(owner, environment).filter(
17384
+ (candidate) => namesEqual(candidate.member.name, member.name)
17385
+ );
17386
+ const target = candidates[candidates.length - 1];
17387
+ return target ? resolvedSourceMemberStorage(
17388
+ target.owner,
17389
+ target.member,
17390
+ environment,
17391
+ seen
17392
+ ) : null;
17393
+ }
17249
17394
  function isTypeAssignable(actual, expected, environment, genericParameters) {
17250
17395
  if (actual.nullable && !expected.nullable) return false;
17251
17396
  if (typesEqual({ ...actual, nullable: false }, { ...expected, nullable: false })) {
@@ -18867,6 +19012,8 @@ function attachConstructorSignature(type, environment) {
18867
19012
  type.id,
18868
19013
  type.members,
18869
19014
  (memberId) => {
19015
+ const declarationMember = environment.members.get(memberId);
19016
+ if (declarationMember?.isReadOnly === true) return null;
18870
19017
  const member = effectiveConstructorMember(
18871
19018
  memberId,
18872
19019
  genericEnvironment,
@@ -19131,6 +19278,7 @@ function memberSymbol(member, schemaKey, environment, field) {
19131
19278
  const kind = string2(member.kind, `${field}.kind`);
19132
19279
  const modifier = string2(member.modifier, `${field}.modifier`);
19133
19280
  const isStatic = boolean(member.isStatic, `${field}.isStatic`);
19281
+ const isReadOnly = boolean(member.isReadOnly, `${field}.isReadOnly`);
19134
19282
  const owner = optionalRecord(member.owner);
19135
19283
  const ownerClassId2 = owner?.kind === "classMember" && typeof owner.classId === "string" ? owner.classId : void 0;
19136
19284
  const type = memberType(member, environment, /* @__PURE__ */ new Set(), field);
@@ -19151,6 +19299,7 @@ function memberSymbol(member, schemaKey, environment, field) {
19151
19299
  type,
19152
19300
  documentation: `${String(member.name ?? schemaKey)} (${id2})`,
19153
19301
  ...isStatic ? { static: true } : {},
19302
+ ...isReadOnly ? { readonlyDeclaration: true } : {},
19154
19303
  ...location3,
19155
19304
  ...scriptDefinitionLocations(member, environment, field),
19156
19305
  ...modifier.includes("abstract") ? { abstract: true } : {},
@@ -19182,9 +19331,9 @@ function memberSymbol(member, schemaKey, environment, field) {
19182
19331
  writable: false
19183
19332
  };
19184
19333
  }
19185
- const writable = kind === "computed" ? computedHasSetter(member, field) : modifier !== "abstract" && member.locked !== true;
19334
+ const writable = isReadOnly ? false : kind === "computed" ? computedHasSetter(member, field) : modifier !== "abstract" && member.locked !== true;
19186
19335
  const storage = typeof member.storage === "string" ? member.storage : void 0;
19187
- const writability = kind === "computed" ? writable ? "setter" : "readOnly" : !writable ? "readOnly" : storage === "immutable" || storage === "save" || storage === "session" ? storage : isStatic && ownerClassId2 ? staticDefaultWritability(ownerClassId2, environment) : void 0;
19336
+ 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;
19188
19337
  const lookupCollectionMemberId = kind === "lookup" ? string2(member.collectionMemberId, `${field}.collectionMemberId`) : null;
19189
19338
  const lookupCollectionWritability = lookupCollectionMemberId === null ? void 0 : environment.effectiveWritabilityByMemberId.get(
19190
19339
  lookupCollectionMemberId
@@ -20328,6 +20477,7 @@ function projectCompletions(analysis, document, position) {
20328
20477
  "sealed",
20329
20478
  "native",
20330
20479
  "override",
20480
+ "readonly",
20331
20481
  "virtual",
20332
20482
  "static",
20333
20483
  "new",
@@ -21261,7 +21411,7 @@ function constructorArgumentType(analysis, frame) {
21261
21411
  return null;
21262
21412
  }
21263
21413
  const fields = declaration.members.filter(
21264
- (member) => member.kind === "field" && !member.modifiers.includes("static")
21414
+ (member) => member.kind === "field" && !member.modifiers.includes("static") && !member.modifiers.includes("readonly")
21265
21415
  );
21266
21416
  if (frame.argumentName !== null) {
21267
21417
  return fields.find((field) => field.name === frame.argumentName)?.type ?? null;
@@ -21316,7 +21466,7 @@ function projectConstructorParameters(analysis, typeName) {
21316
21466
  function declarationConstructorParameters(declaration) {
21317
21467
  if (declaration.kind === "global" || declaration.kind === "enum") return [];
21318
21468
  return declaration.members.filter(
21319
- (member) => member.kind === "field" && !member.modifiers.includes("static")
21469
+ (member) => member.kind === "field" && !member.modifiers.includes("static") && !member.modifiers.includes("readonly")
21320
21470
  ).map((member) => ({ type: member.type.name, name: member.name }));
21321
21471
  }
21322
21472
  function graphConstructorParameters(typeName) {
@@ -22252,6 +22402,11 @@ function memberFromDocument(record3, members, owners, classNames, context) {
22252
22402
  record3,
22253
22403
  "isAbstract"
22254
22404
  );
22405
+ const isReadOnly = booleanValue(
22406
+ data.isReadOnly ?? false,
22407
+ record3,
22408
+ "isReadOnly"
22409
+ );
22255
22410
  const modifier = memberModifier(overrideOf, isVirtual, isAbstract);
22256
22411
  const defaultValue = defaultValueFromDocument(
22257
22412
  field("defaultValue", null),
@@ -22265,6 +22420,7 @@ function memberFromDocument(record3, members, owners, classNames, context) {
22265
22420
  name: requiredString(data, "name", record3),
22266
22421
  ...docsText === void 0 ? {} : { docsText },
22267
22422
  isStatic: booleanValue(data.isStatic, record3, "isStatic"),
22423
+ isReadOnly,
22268
22424
  accessModifier: accessModifierValue(
22269
22425
  data.accessModifierKind,
22270
22426
  record3,
@@ -22513,6 +22669,7 @@ function memberToDocumentFields(member, baseData3) {
22513
22669
  locked: member.locked,
22514
22670
  required: member.required,
22515
22671
  isStatic: member.isStatic,
22672
+ ...member.isReadOnly ? { isReadOnly: true } : {},
22516
22673
  accessModifierKind: member.accessModifier,
22517
22674
  ...member.docsText === void 0 ? {} : { docsText: member.docsText }
22518
22675
  };
@@ -24981,6 +25138,7 @@ function assertMember2(value, path) {
24981
25138
  }
24982
25139
  nonEmptyString(member.name, `${path}.name`);
24983
25140
  booleanAt(member.isStatic, `${path}.isStatic`);
25141
+ booleanAt(member.isReadOnly, `${path}.isReadOnly`);
24984
25142
  stringIn(
24985
25143
  member.accessModifier,
24986
25144
  /* @__PURE__ */ new Set(["public", "protected", "private"]),
@@ -25012,9 +25170,35 @@ function assertMember2(value, path) {
25012
25170
  "must be null unless the member modifier is override."
25013
25171
  );
25014
25172
  }
25173
+ if ((member.modifier === "abstract" || member.modifier === "abstractOverride") && member.defaultValue !== null) {
25174
+ invalid(
25175
+ `${path}.defaultValue`,
25176
+ "must be null for an abstract member; concrete implementations own defaults."
25177
+ );
25178
+ }
25015
25179
  assertSystem(member.system, `${path}.system`);
25016
25180
  nullableStorage(member.storage, `${path}.storage`);
25017
25181
  nullableNonEmptyString(member.storageKey, `${path}.storageKey`);
25182
+ if (member.isReadOnly === true) {
25183
+ if (owner.kind !== "classMember" && owner.kind !== "sharedClassMember") {
25184
+ invalid(`${path}.isReadOnly`, "is allowed only on a class field.");
25185
+ }
25186
+ if (member.isStatic === true) {
25187
+ invalid(`${path}.isReadOnly`, "cannot be combined with isStatic.");
25188
+ }
25189
+ if (kind === "computed" || kind === "function" || kind === "scriptFunction") {
25190
+ invalid(
25191
+ `${path}.isReadOnly`,
25192
+ `is not valid on value-less member kind ${JSON.stringify(kind)}.`
25193
+ );
25194
+ }
25195
+ if (member.storage !== "immutable" && (member.storage !== null || member.overrideOf === null)) {
25196
+ invalid(`${path}.storage`, "must be immutable when isReadOnly is true.");
25197
+ }
25198
+ if (member.defaultValue === null && member.modifier !== "abstract" && member.modifier !== "abstractOverride") {
25199
+ invalid(`${path}.defaultValue`, "is required when isReadOnly is true.");
25200
+ }
25201
+ }
25018
25202
  if (kind === "int") {
25019
25203
  nullableNumber(member.min, `${path}.min`);
25020
25204
  nullableNumber(member.max, `${path}.max`);
@@ -25023,6 +25207,9 @@ function assertMember2(value, path) {
25023
25207
  if (kind === "string") {
25024
25208
  booleanAt(member.localizable, `${path}.localizable`);
25025
25209
  booleanAt(member.searchKey, `${path}.searchKey`);
25210
+ if (member.isReadOnly === true && member.searchKey === true) {
25211
+ invalid(`${path}.searchKey`, "cannot be true on a read-only member.");
25212
+ }
25026
25213
  return;
25027
25214
  }
25028
25215
  if (kind === "float") {
@@ -25051,6 +25238,9 @@ function assertMember2(value, path) {
25051
25238
  );
25052
25239
  arrayOf(member.indexes, `${path}.indexes`, assertListIndex);
25053
25240
  arrayOf(member.columns, `${path}.columns`, assertListColumn);
25241
+ if (member.isReadOnly === true && Array.isArray(member.indexes) && member.indexes.length > 0) {
25242
+ invalid(`${path}.indexes`, "must be empty on a read-only member.");
25243
+ }
25054
25244
  return;
25055
25245
  }
25056
25246
  if (kind === "class") {
@@ -26236,6 +26426,7 @@ var init_validate = __esm({
26236
26426
  "owner",
26237
26427
  "name",
26238
26428
  "isStatic",
26429
+ "isReadOnly",
26239
26430
  "accessModifier",
26240
26431
  "modifier",
26241
26432
  "locked",
@@ -28046,6 +28237,7 @@ function lowerMember(context, ownerClass, declaration, id2, owner) {
28046
28237
  }
28047
28238
  function memberCommon(context, ownerClass, declaration, id2, owner, base) {
28048
28239
  const storage = annotation(declaration.annotations, "storage");
28240
+ const inheritedId = declaration.modifiers.includes("override") || declaration.modifiers.includes("sealed") ? base?.overrideOf ?? inheritedMemberId(context, ownerClass, declaration.name) : null;
28049
28241
  return {
28050
28242
  id: id2,
28051
28243
  source: sourceIdentity2(declaration, "member", declaration.name),
@@ -28053,6 +28245,7 @@ function memberCommon(context, ownerClass, declaration, id2, owner, base) {
28053
28245
  name: declaration.name,
28054
28246
  ...declaration.docsText === void 0 ? {} : { docsText: declaration.docsText },
28055
28247
  isStatic: declaration.modifiers.includes("static"),
28248
+ isReadOnly: declaration.modifiers.includes("readonly"),
28056
28249
  // Non-class placements (collection entries, generic arguments, loose
28057
28250
  // members) have no receiver type to scope visibility to; they are
28058
28251
  // structurally public. Class schema entries apply the C# omission default.
@@ -28061,14 +28254,43 @@ function memberCommon(context, ownerClass, declaration, id2, owner, base) {
28061
28254
  locked: hasAnnotation(declaration.annotations, "locked"),
28062
28255
  required: declaration.kind === "field" ? !declaration.type.nullable : base?.required ?? false,
28063
28256
  defaultValue: null,
28064
- overrideOf: declaration.modifiers.includes("override") || declaration.modifiers.includes("sealed") ? base?.overrideOf ?? inheritedMemberId(context, ownerClass, declaration.name) : null,
28257
+ overrideOf: inheritedId,
28065
28258
  system: systemMetadata(declaration.annotations),
28066
- storage: storage ? storageValue(storage, "allowed") : null,
28259
+ storage: storage ? storageValue(storage, "allowed") : declaration.modifiers.includes("readonly") ? base !== void 0 ? base.storage : inheritedId !== null && resolvedMemberStorage(context, inheritedId) === "immutable" ? "immutable" : null : null,
28067
28260
  storageKey: storage ? stringArgument(storage, "key") : null
28068
28261
  };
28069
28262
  }
28263
+ function resolvedMemberStorage(context, memberId, visited = /* @__PURE__ */ new Set()) {
28264
+ if (visited.has(memberId)) return null;
28265
+ visited.add(memberId);
28266
+ const lowered = context.loweredMembers.get(memberId);
28267
+ if (lowered?.storage !== null && lowered?.storage !== void 0) {
28268
+ return lowered.storage;
28269
+ }
28270
+ const persisted = context.baseMembers.get(memberId);
28271
+ if (persisted !== void 0) {
28272
+ if (persisted.storage !== null) return persisted.storage;
28273
+ if (persisted.overrideOf !== null) {
28274
+ return resolvedMemberStorage(context, persisted.overrideOf, visited);
28275
+ }
28276
+ }
28277
+ const sourcePlacement = context.sourceMembersById.get(memberId);
28278
+ if (sourcePlacement !== void 0) {
28279
+ const { ownerClass: sourceClass, member: sourceMember } = sourcePlacement;
28280
+ const storage = annotation(sourceMember.annotations, "storage");
28281
+ const localStorage = storage ? storageValue(storage, "allowed") : null;
28282
+ if (localStorage !== null) return localStorage;
28283
+ const inheritedId = inheritedMemberId(
28284
+ context,
28285
+ sourceClass,
28286
+ sourceMember.name
28287
+ );
28288
+ return inheritedId === null ? null : resolvedMemberStorage(context, inheritedId, visited);
28289
+ }
28290
+ return null;
28291
+ }
28070
28292
  function lowerFieldMember(context, ownerClass, declaration, id2, commonInput, base) {
28071
- const storedStatic = declaration.modifiers.includes("static");
28293
+ const storedStatic = declaration.modifiers.includes("static") && !declaration.modifiers.includes("readonly");
28072
28294
  const settings = annotation(declaration.annotations, "settings");
28073
28295
  const storesSelectionArray = context.enumIdsByName.has(declaration.type.name) || declaration.type.name === "Dialogue" || Boolean(settings && argument(settings, "collection"));
28074
28296
  const common = {
@@ -29349,6 +29571,26 @@ function lowerProjectSchemaV4(base, analysis, options = {}) {
29349
29571
  )
29350
29572
  );
29351
29573
  }
29574
+ const sourceClasses = new Map(
29575
+ analysis.schema.classes.map((entry) => [
29576
+ materializedId(entry, "class", entry.name),
29577
+ entry
29578
+ ])
29579
+ );
29580
+ const sourceMembersById = new Map(
29581
+ analysis.schema.classes.flatMap(
29582
+ (ownerClass) => ownerClass.members.map(
29583
+ (member) => [
29584
+ materializedId(
29585
+ member,
29586
+ "member",
29587
+ `${ownerClass.name}.${member.name}`
29588
+ ),
29589
+ { ownerClass, member }
29590
+ ]
29591
+ )
29592
+ )
29593
+ );
29352
29594
  const context = {
29353
29595
  base,
29354
29596
  baseClasses: new Map(base.classes.map((entry) => [entry.id, entry])),
@@ -29356,12 +29598,8 @@ function lowerProjectSchemaV4(base, analysis, options = {}) {
29356
29598
  baseInterfaces: new Map(base.interfaces.map((entry) => [entry.id, entry])),
29357
29599
  baseEnums: new Map(base.enums.map((entry) => [entry.id, entry])),
29358
29600
  baseRelationsById: indexFirstById(base.internalRecordRelations),
29359
- sourceClasses: new Map(
29360
- analysis.schema.classes.map((entry) => [
29361
- materializedId(entry, "class", entry.name),
29362
- entry
29363
- ])
29364
- ),
29601
+ sourceClasses,
29602
+ sourceMembersById,
29365
29603
  classIdsByName,
29366
29604
  interfaceIdsByName,
29367
29605
  enumIdsByName,
@@ -29898,7 +30136,8 @@ function memberModifier3(member) {
29898
30136
  const accessPrefix = `${member.accessModifier} `;
29899
30137
  const staticPrefix = member.isStatic ? "static " : "";
29900
30138
  const modifier = member.modifier === "plain" ? "" : member.modifier === "abstractOverride" ? "abstract override " : member.modifier === "sealedOverride" ? "override " : `${member.modifier} `;
29901
- return `${accessPrefix}${staticPrefix}${modifier}`;
30139
+ const readOnlyPrefix = member.isReadOnly ? "readonly " : "";
30140
+ return `${accessPrefix}${staticPrefix}${modifier}${readOnlyPrefix}`;
29902
30141
  }
29903
30142
  function renderExtendsGenericBindings(context, schemaClass2, base) {
29904
30143
  if (base.genericParameters.length === 0) return "";
@@ -33137,6 +33376,9 @@ function isNSPointerKeyOf(value) {
33137
33376
  const v = value;
33138
33377
  if (v?.type !== "keyOf" /* keyOf */) return false;
33139
33378
  if (!isNSKeyOf(v.keyOf)) return false;
33379
+ if (v.memberId !== void 0 && typeof v.memberId !== "string") {
33380
+ return false;
33381
+ }
33140
33382
  if (v.optional !== void 0 && typeof v.optional !== "boolean") {
33141
33383
  return false;
33142
33384
  }
@@ -33296,6 +33538,9 @@ function isNSConditionalBranch(value) {
33296
33538
  function isNSFunctionWithReturnType(value) {
33297
33539
  const v = value;
33298
33540
  if (typeof v !== "object" || v === null) return false;
33541
+ if (v.compilerRevision !== void 0 && (!Number.isSafeInteger(v.compilerRevision) || v.compilerRevision < 1 || v.compilerRevision > NEOSCRIPT_COMPILER_REVISION)) {
33542
+ return false;
33543
+ }
33299
33544
  if (!Array.isArray(v.parameters)) return false;
33300
33545
  if (!v.parameters.every(isNSVariable)) return false;
33301
33546
  if (!isNSInstructions(v.instructions)) return false;
@@ -33544,6 +33789,7 @@ var NS_STRING_OPS, NS_DECIMAL_OPS;
33544
33789
  var init_neoscript_guards = __esm({
33545
33790
  "../src/models/neoscript/neoscript-guards.ts"() {
33546
33791
  "use strict";
33792
+ init_src();
33547
33793
  init_member_kinds();
33548
33794
  init_core();
33549
33795
  init_neoscript_types();
@@ -33563,292 +33809,6 @@ var init_neoscript_guards = __esm({
33563
33809
  }
33564
33810
  });
33565
33811
 
33566
- // ../src/models/classes/classes.ts
33567
- function isGenericParamConstraint(value) {
33568
- const v = value;
33569
- if (!v || typeof v !== "object") return false;
33570
- if (v.kind === "class") {
33571
- if (typeof v.classId !== "string") return false;
33572
- if (v.classId.length === 0) return false;
33573
- return v.enumId === void 0;
33574
- }
33575
- if (v.kind === "enum") {
33576
- if (typeof v.enumId !== "string") return false;
33577
- if (v.enumId.length === 0) return false;
33578
- return v.classId === void 0;
33579
- }
33580
- return false;
33581
- }
33582
- function isGenericParamDeclaration(value) {
33583
- const v = value;
33584
- if (!v || typeof v !== "object") return false;
33585
- if (typeof v.id !== "string") return false;
33586
- if (v.id.length === 0) return false;
33587
- if (typeof v.name !== "string") return false;
33588
- if (v.name.length === 0) return false;
33589
- if (v.constraint === void 0 || v.constraint === null) return true;
33590
- return isGenericParamConstraint(v.constraint);
33591
- }
33592
- function isGenericBinding(value) {
33593
- const v = value;
33594
- if (!v || typeof v !== "object") return false;
33595
- if (v.kind === "generic") {
33596
- if (typeof v.genericParamId !== "string") return false;
33597
- if (v.genericParamId.length === 0) return false;
33598
- return v.memberId === void 0;
33599
- }
33600
- if (v.kind === "member") {
33601
- if (typeof v.memberId !== "string") return false;
33602
- if (v.memberId.length === 0) return false;
33603
- return v.genericParamId === void 0;
33604
- }
33605
- return false;
33606
- }
33607
- function isGenericBindingsRecord(value) {
33608
- if (value === null) return false;
33609
- if (typeof value !== "object") return false;
33610
- if (Array.isArray(value)) return false;
33611
- return Object.values(value).every(isGenericBinding);
33612
- }
33613
- function isNeoSchemaClassBase(value) {
33614
- const v = value;
33615
- return typeof v?.name === "string" && isValidDocsText(v.docsText) && isClassSchema(v?.schema) && isOptionalSchemaKeyOrder(v?.schemaKeyOrder) && (v.implementsInterfaceIds === void 0 || v.implementsInterfaceIds === null || Array.isArray(v.implementsInterfaceIds) && v.implementsInterfaceIds.every(
33616
- (interfaceId) => typeof interfaceId === "string" && interfaceId.length > 0
33617
- )) && 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.system === void 0 || v.system === null || isSystemMetadata(v.system));
33618
- }
33619
- function resolveAllowedStorage(classId, classes, draft) {
33620
- const visited = /* @__PURE__ */ new Set();
33621
- const lookup = (id2) => draft && draft.id === id2 ? draft : classes.find((candidate) => candidate.id === id2);
33622
- let current = lookup(classId);
33623
- while (current) {
33624
- if (visited.has(current.id)) return null;
33625
- visited.add(current.id);
33626
- if (current.allowedStorage != null) return current.allowedStorage;
33627
- if (!current.extendsClassId) return null;
33628
- current = lookup(current.extendsClassId);
33629
- }
33630
- return null;
33631
- }
33632
- function dedupeSchemaKeyOrder(order) {
33633
- const seen = /* @__PURE__ */ new Set();
33634
- const result = [];
33635
- for (const schemaKey of order) {
33636
- if (seen.has(schemaKey)) continue;
33637
- seen.add(schemaKey);
33638
- result.push(schemaKey);
33639
- }
33640
- return result;
33641
- }
33642
- function getClassSchemaKeyOrder(schemaClass2) {
33643
- const schemaKeys = Object.keys(schemaClass2.schema);
33644
- if (schemaClass2.schemaKeyOrder === void 0) return schemaKeys;
33645
- const orderedKeys2 = dedupeSchemaKeyOrder(schemaClass2.schemaKeyOrder);
33646
- const seen = new Set(orderedKeys2);
33647
- for (const schemaKey of schemaKeys) {
33648
- if (seen.has(schemaKey)) continue;
33649
- seen.add(schemaKey);
33650
- orderedKeys2.push(schemaKey);
33651
- }
33652
- return orderedKeys2;
33653
- }
33654
- function isClassSchema(value) {
33655
- if (value === null) return false;
33656
- if (typeof value !== "object") return false;
33657
- if (Array.isArray(value)) return false;
33658
- return Object.values(value).every(
33659
- (schemaValue) => typeof schemaValue === "string"
33660
- );
33661
- }
33662
- function isOptionalSchemaKeyOrder(value) {
33663
- if (value === void 0) return true;
33664
- if (!Array.isArray(value)) return false;
33665
- return value.every((key) => typeof key === "string");
33666
- }
33667
- function isNeoSchemaClassProps(value) {
33668
- const v = value;
33669
- return isNeoSchemaClassBase(value) && typeof v?.id === "string" && isEpochMillis(v?.createdAt) && isEpochMillis(v?.updatedAt) && typeof v?.projectId === "string";
33670
- }
33671
- function isNeoSchemaClass(value) {
33672
- return isNeoSchemaClassProps(value);
33673
- }
33674
- var init_classes = __esm({
33675
- "../src/models/classes/classes.ts"() {
33676
- "use strict";
33677
- init_core();
33678
- init_docs_text2();
33679
- init_member_storage();
33680
- }
33681
- });
33682
-
33683
- // ../src/models/classes/inheritance.ts
33684
- function resolveInheritanceChain(classId, classes, draft) {
33685
- const chain = [];
33686
- const visited = /* @__PURE__ */ new Set();
33687
- const lookup = (id2) => draft && draft.id === id2 ? draft : classes.find((t) => t.id === id2);
33688
- let current = lookup(classId);
33689
- while (current) {
33690
- if (visited.has(current.id)) {
33691
- throw new CircularInheritanceError(
33692
- current.id,
33693
- chain.map((c) => c.id)
33694
- );
33695
- }
33696
- visited.add(current.id);
33697
- chain.push(current);
33698
- if (!current.extendsClassId) break;
33699
- current = lookup(current.extendsClassId);
33700
- }
33701
- return chain;
33702
- }
33703
- function mergeSchemas(chain) {
33704
- const baseFirst = [...chain].reverse();
33705
- const map = /* @__PURE__ */ new Map();
33706
- const order = [];
33707
- for (const schemaClass2 of baseFirst) {
33708
- const localKeys = getClassSchemaKeyOrder(schemaClass2);
33709
- if (schemaClass2.schemaKeyOrder !== void 0) {
33710
- order.splice(
33711
- 0,
33712
- order.length,
33713
- ...localKeys,
33714
- ...order.filter((key) => !localKeys.includes(key))
33715
- );
33716
- }
33717
- for (const key of localKeys) {
33718
- if (!order.includes(key)) order.push(key);
33719
- }
33720
- for (const [key, memberId] of Object.entries(schemaClass2.schema)) {
33721
- map.set(key, {
33722
- schemaKey: key,
33723
- memberId,
33724
- ownerClassId: schemaClass2.id
33725
- });
33726
- }
33727
- }
33728
- return order.flatMap((k) => {
33729
- const entry = map.get(k);
33730
- return entry ? [entry] : [];
33731
- });
33732
- }
33733
- function mergeInstanceSchema(classId, classes, members) {
33734
- const membersById = new Map(members.map((member) => [member.id, member]));
33735
- return mergeSchemas(resolveInheritanceChain(classId, classes)).filter(
33736
- (entry) => membersById.get(entry.memberId)?.isStatic !== true
33737
- );
33738
- }
33739
- function findSchemaPlacement(memberId, classes) {
33740
- for (const schemaClass2 of classes) {
33741
- for (const [key, placedMemberId] of Object.entries(schemaClass2.schema)) {
33742
- if (placedMemberId === memberId) {
33743
- return { ownerClass: schemaClass2, schemaKey: key };
33744
- }
33745
- }
33746
- }
33747
- return null;
33748
- }
33749
- function walkExtendsMemberChain(startId, members, pick, options) {
33750
- const requireKind = options?.requireKind;
33751
- const maxHops = options?.maxHops ?? 16;
33752
- let cursor = members.find((a) => a.id === startId);
33753
- for (let i = 0; cursor && i < maxHops; i++) {
33754
- if (requireKind !== void 0 && cursor.kind !== requireKind) {
33755
- return void 0;
33756
- }
33757
- const v = pick(cursor);
33758
- if (v !== void 0) return v;
33759
- const nextId = getOptionalString(cursor, "extendsMemberId");
33760
- if (nextId === void 0) return void 0;
33761
- cursor = members.find((a) => a.id === nextId);
33762
- }
33763
- return void 0;
33764
- }
33765
- function resolveMember2(member, members) {
33766
- const chain = [];
33767
- const visited = /* @__PURE__ */ new Set();
33768
- let current = member;
33769
- while (current) {
33770
- const id2 = getOptionalString(current, "id");
33771
- if (id2 !== void 0) {
33772
- if (visited.has(id2)) {
33773
- throw new CircularInheritanceError(id2, [...visited]);
33774
- }
33775
- visited.add(id2);
33776
- }
33777
- chain.push(current);
33778
- if (!isMemberOverrideMarker(current)) break;
33779
- const parentId = current.extendsMemberId;
33780
- current = members.find((a) => a.id === parentId);
33781
- }
33782
- const tail = chain[chain.length - 1];
33783
- if (!tail || isMemberOverrideMarker(tail)) {
33784
- const startId = getOptionalString(member, "id") ?? "(unknown)";
33785
- throw new UnresolvedMemberInheritanceError(startId);
33786
- }
33787
- const baseFirst = [...chain].reverse();
33788
- const merged = {};
33789
- for (const link of baseFirst) {
33790
- for (const [key, value] of Object.entries(link)) {
33791
- if (value === void 0) continue;
33792
- merged[key] = value;
33793
- }
33794
- }
33795
- delete merged.extendsMemberId;
33796
- if (member.isAbstract === void 0) {
33797
- delete merged.isAbstract;
33798
- } else {
33799
- merged.isAbstract = member.isAbstract;
33800
- }
33801
- return merged;
33802
- }
33803
- function descendantClassIds(classId, allClasses) {
33804
- const result = /* @__PURE__ */ new Set();
33805
- let added = true;
33806
- while (added) {
33807
- added = false;
33808
- for (const t of allClasses) {
33809
- if (t.id === classId) continue;
33810
- if (result.has(t.id)) continue;
33811
- if (t.extendsClassId === classId || t.extendsClassId && result.has(t.extendsClassId)) {
33812
- result.add(t.id);
33813
- added = true;
33814
- }
33815
- }
33816
- }
33817
- return result;
33818
- }
33819
- var CircularInheritanceError, UnresolvedMemberInheritanceError;
33820
- var init_inheritance = __esm({
33821
- "../src/models/classes/inheritance.ts"() {
33822
- "use strict";
33823
- init_member_kinds();
33824
- init_core();
33825
- init_structural_narrow();
33826
- init_classes();
33827
- CircularInheritanceError = class extends Error {
33828
- constructor(nodeId, chain) {
33829
- super(
33830
- `Circular inheritance detected at "${nodeId}"; chain: ${chain.join(" -> ")}`
33831
- );
33832
- this.nodeId = nodeId;
33833
- this.chain = chain;
33834
- this.name = "CircularInheritanceError";
33835
- }
33836
- nodeId;
33837
- chain;
33838
- };
33839
- UnresolvedMemberInheritanceError = class extends Error {
33840
- constructor(memberId) {
33841
- super(
33842
- `Inheritance chain for member "${memberId}" did not terminate at a non-override member`
33843
- );
33844
- this.memberId = memberId;
33845
- this.name = "UnresolvedMemberInheritanceError";
33846
- }
33847
- memberId;
33848
- };
33849
- }
33850
- });
33851
-
33852
33812
  // ../src/models/interfaces/interface-graph.ts
33853
33813
  function resolveInterfaceClosure(interfaceId, interfaces) {
33854
33814
  const byId = new Map(
@@ -33983,6 +33943,123 @@ var init_neoscript = __esm({
33983
33943
  }
33984
33944
  });
33985
33945
 
33946
+ // ../src/models/classes/classes.ts
33947
+ function isGenericParamConstraint(value) {
33948
+ const v = value;
33949
+ if (!v || typeof v !== "object") return false;
33950
+ if (v.kind === "class") {
33951
+ if (typeof v.classId !== "string") return false;
33952
+ if (v.classId.length === 0) return false;
33953
+ return v.enumId === void 0;
33954
+ }
33955
+ if (v.kind === "enum") {
33956
+ if (typeof v.enumId !== "string") return false;
33957
+ if (v.enumId.length === 0) return false;
33958
+ return v.classId === void 0;
33959
+ }
33960
+ return false;
33961
+ }
33962
+ function isGenericParamDeclaration(value) {
33963
+ const v = value;
33964
+ if (!v || typeof v !== "object") return false;
33965
+ if (typeof v.id !== "string") return false;
33966
+ if (v.id.length === 0) return false;
33967
+ if (typeof v.name !== "string") return false;
33968
+ if (v.name.length === 0) return false;
33969
+ if (v.constraint === void 0 || v.constraint === null) return true;
33970
+ return isGenericParamConstraint(v.constraint);
33971
+ }
33972
+ function isGenericBinding(value) {
33973
+ const v = value;
33974
+ if (!v || typeof v !== "object") return false;
33975
+ if (v.kind === "generic") {
33976
+ if (typeof v.genericParamId !== "string") return false;
33977
+ if (v.genericParamId.length === 0) return false;
33978
+ return v.memberId === void 0;
33979
+ }
33980
+ if (v.kind === "member") {
33981
+ if (typeof v.memberId !== "string") return false;
33982
+ if (v.memberId.length === 0) return false;
33983
+ return v.genericParamId === void 0;
33984
+ }
33985
+ return false;
33986
+ }
33987
+ function isGenericBindingsRecord(value) {
33988
+ if (value === null) return false;
33989
+ if (typeof value !== "object") return false;
33990
+ if (Array.isArray(value)) return false;
33991
+ return Object.values(value).every(isGenericBinding);
33992
+ }
33993
+ function isNeoSchemaClassBase(value) {
33994
+ const v = value;
33995
+ return typeof v?.name === "string" && isValidDocsText(v.docsText) && isClassSchema(v?.schema) && isOptionalSchemaKeyOrder(v?.schemaKeyOrder) && (v.implementsInterfaceIds === void 0 || v.implementsInterfaceIds === null || Array.isArray(v.implementsInterfaceIds) && v.implementsInterfaceIds.every(
33996
+ (interfaceId) => typeof interfaceId === "string" && interfaceId.length > 0
33997
+ )) && 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.system === void 0 || v.system === null || isSystemMetadata(v.system));
33998
+ }
33999
+ function resolveAllowedStorage(classId, classes, draft) {
34000
+ const visited = /* @__PURE__ */ new Set();
34001
+ const lookup = (id2) => draft && draft.id === id2 ? draft : classes.find((candidate) => candidate.id === id2);
34002
+ let current = lookup(classId);
34003
+ while (current) {
34004
+ if (visited.has(current.id)) return null;
34005
+ visited.add(current.id);
34006
+ if (current.allowedStorage != null) return current.allowedStorage;
34007
+ if (!current.extendsClassId) return null;
34008
+ current = lookup(current.extendsClassId);
34009
+ }
34010
+ return null;
34011
+ }
34012
+ function dedupeSchemaKeyOrder(order) {
34013
+ const seen = /* @__PURE__ */ new Set();
34014
+ const result = [];
34015
+ for (const schemaKey of order) {
34016
+ if (seen.has(schemaKey)) continue;
34017
+ seen.add(schemaKey);
34018
+ result.push(schemaKey);
34019
+ }
34020
+ return result;
34021
+ }
34022
+ function getClassSchemaKeyOrder(schemaClass2) {
34023
+ const schemaKeys = Object.keys(schemaClass2.schema);
34024
+ if (schemaClass2.schemaKeyOrder === void 0) return schemaKeys;
34025
+ const orderedKeys2 = dedupeSchemaKeyOrder(schemaClass2.schemaKeyOrder);
34026
+ const seen = new Set(orderedKeys2);
34027
+ for (const schemaKey of schemaKeys) {
34028
+ if (seen.has(schemaKey)) continue;
34029
+ seen.add(schemaKey);
34030
+ orderedKeys2.push(schemaKey);
34031
+ }
34032
+ return orderedKeys2;
34033
+ }
34034
+ function isClassSchema(value) {
34035
+ if (value === null) return false;
34036
+ if (typeof value !== "object") return false;
34037
+ if (Array.isArray(value)) return false;
34038
+ return Object.values(value).every(
34039
+ (schemaValue) => typeof schemaValue === "string"
34040
+ );
34041
+ }
34042
+ function isOptionalSchemaKeyOrder(value) {
34043
+ if (value === void 0) return true;
34044
+ if (!Array.isArray(value)) return false;
34045
+ return value.every((key) => typeof key === "string");
34046
+ }
34047
+ function isNeoSchemaClassProps(value) {
34048
+ const v = value;
34049
+ return isNeoSchemaClassBase(value) && typeof v?.id === "string" && isEpochMillis(v?.createdAt) && isEpochMillis(v?.updatedAt) && typeof v?.projectId === "string";
34050
+ }
34051
+ function isNeoSchemaClass(value) {
34052
+ return isNeoSchemaClassProps(value);
34053
+ }
34054
+ var init_classes = __esm({
34055
+ "../src/models/classes/classes.ts"() {
34056
+ "use strict";
34057
+ init_core();
34058
+ init_docs_text2();
34059
+ init_member_storage();
34060
+ }
34061
+ });
34062
+
33986
34063
  // ../src/models/members/member-kinds.ts
33987
34064
  function isMemberAccessModifierKind(value) {
33988
34065
  if (value === "public") return true;
@@ -34433,6 +34510,9 @@ function isMemberBase(value) {
34433
34510
  if (typeof v.locked !== "boolean") return false;
34434
34511
  if (typeof v.required !== "boolean") return false;
34435
34512
  if (typeof v.isStatic !== "boolean") return false;
34513
+ if (v.isReadOnly !== void 0 && typeof v.isReadOnly !== "boolean") {
34514
+ return false;
34515
+ }
34436
34516
  if (!isMemberAccessModifierKind(v.accessModifierKind)) return false;
34437
34517
  if (v.isVirtual !== void 0 && typeof v.isVirtual !== "boolean") {
34438
34518
  return false;
@@ -34490,6 +34570,9 @@ function isMemberOverrideBase(value) {
34490
34570
  if (v.locked !== void 0 && typeof v.locked !== "boolean") return false;
34491
34571
  if (v.required !== void 0 && typeof v.required !== "boolean") return false;
34492
34572
  if (v.isStatic !== void 0 && v.isStatic !== false) return false;
34573
+ if (v.isReadOnly !== void 0 && typeof v.isReadOnly !== "boolean") {
34574
+ return false;
34575
+ }
34493
34576
  if (!isMemberAccessModifierKind(v.accessModifierKind)) return false;
34494
34577
  if (v.isVirtual !== void 0 && typeof v.isVirtual !== "boolean") {
34495
34578
  return false;
@@ -34674,175 +34757,184 @@ var init_member_kinds = __esm({
34674
34757
  }
34675
34758
  });
34676
34759
 
34677
- // ../src/models/members/member-kinds-db-response.ts
34678
- var init_member_kinds_db_response = __esm({
34679
- "../src/models/members/member-kinds-db-response.ts"() {
34680
- "use strict";
34681
- init_member_kinds();
34682
- }
34683
- });
34684
-
34685
- // ../src/models/members/unordered-list-membership.ts
34686
- var init_unordered_list_membership = __esm({
34687
- "../src/models/members/unordered-list-membership.ts"() {
34688
- "use strict";
34689
- init_member_kinds();
34690
- }
34691
- });
34692
-
34693
- // ../src/models/members/member-storage-key.ts
34694
- function normalizeStorageKeyDeclaration(declaration) {
34695
- if (declaration === void 0 || declaration === null) {
34696
- return STORAGE_KEY_INHERIT;
34697
- }
34698
- if (declaration.length === 0) return STORAGE_KEY_INHERIT;
34699
- if (declaration === STORAGE_KEY_ALL) {
34700
- throw new Error(
34701
- 'Storage key "all" is reserved for partition-scoped reads and cannot be declared on a member.'
34702
- );
34703
- }
34704
- return declaration;
34705
- }
34706
- function storageKeyReferencesParentClass(declaration) {
34707
- return declaration.includes(PARENT_CLASS_TEMPLATE_TOKEN);
34708
- }
34709
- function resolveMapKeyForCreatedValue(args) {
34710
- const declaration = normalizeStorageKeyDeclaration(args.declaration);
34711
- if (declaration === STORAGE_KEY_INHERIT) return args.parentMapKey;
34712
- if (declaration === MAIN_STORAGE_PARTITION) return null;
34713
- if (storageKeyReferencesParentClass(declaration)) {
34714
- if (args.parentClassId === void 0) {
34715
- throw new Error(
34716
- `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).`
34760
+ // ../src/models/classes/inheritance.ts
34761
+ function resolveInheritanceChain(classId, classes, draft) {
34762
+ const chain = [];
34763
+ const visited = /* @__PURE__ */ new Set();
34764
+ const lookup = (id2) => draft && draft.id === id2 ? draft : classes.find((t) => t.id === id2);
34765
+ let current = lookup(classId);
34766
+ while (current) {
34767
+ if (visited.has(current.id)) {
34768
+ throw new CircularInheritanceError(
34769
+ current.id,
34770
+ chain.map((c) => c.id)
34717
34771
  );
34718
34772
  }
34719
- return declaration.replaceAll(
34720
- PARENT_CLASS_TEMPLATE_TOKEN,
34721
- args.parentClassId
34722
- );
34773
+ visited.add(current.id);
34774
+ chain.push(current);
34775
+ if (!current.extendsClassId) break;
34776
+ current = lookup(current.extendsClassId);
34723
34777
  }
34724
- return declaration;
34778
+ return chain;
34725
34779
  }
34726
- var MAIN_STORAGE_PARTITION, STORAGE_KEY_INHERIT, STORAGE_KEY_ALL, PARENT_CLASS_TEMPLATE_TOKEN;
34727
- var init_member_storage_key = __esm({
34728
- "../src/models/members/member-storage-key.ts"() {
34729
- "use strict";
34730
- MAIN_STORAGE_PARTITION = "main";
34731
- STORAGE_KEY_INHERIT = "inherit";
34732
- STORAGE_KEY_ALL = "all";
34733
- PARENT_CLASS_TEMPLATE_TOKEN = "$parentClass";
34734
- }
34735
- });
34736
-
34737
- // ../src/models/members/storage-partitions.ts
34738
- function stampCreatedValuesMapKey(values, mapKey) {
34739
- const normalized = mapKey === void 0 || mapKey === null || mapKey.length === 0 || mapKey === MAIN_STORAGE_PARTITION ? null : mapKey;
34740
- for (const value of values) {
34741
- if (normalized === null) {
34742
- delete value.mapKey;
34743
- } else {
34744
- value.mapKey = normalized;
34780
+ function mergeSchemas(chain) {
34781
+ const baseFirst = [...chain].reverse();
34782
+ const map = /* @__PURE__ */ new Map();
34783
+ const order = [];
34784
+ for (const schemaClass2 of baseFirst) {
34785
+ const localKeys = getClassSchemaKeyOrder(schemaClass2);
34786
+ if (schemaClass2.schemaKeyOrder !== void 0) {
34787
+ order.splice(
34788
+ 0,
34789
+ order.length,
34790
+ ...localKeys,
34791
+ ...order.filter((key) => !localKeys.includes(key))
34792
+ );
34793
+ }
34794
+ for (const key of localKeys) {
34795
+ if (!order.includes(key)) order.push(key);
34796
+ }
34797
+ for (const [key, memberId] of Object.entries(schemaClass2.schema)) {
34798
+ map.set(key, {
34799
+ schemaKey: key,
34800
+ memberId,
34801
+ ownerClassId: schemaClass2.id
34802
+ });
34745
34803
  }
34746
34804
  }
34805
+ return order.flatMap((k) => {
34806
+ const entry = map.get(k);
34807
+ return entry ? [entry] : [];
34808
+ });
34747
34809
  }
34748
- function applyDeclaredStorageKeyOverrides(args) {
34749
- const createdById = new Map(
34750
- args.createdValues.map((value) => [value.id, value])
34810
+ function mergeInstanceSurfaceSchema(classId, classes, members) {
34811
+ const membersById = new Map(members.map((member) => [member.id, member]));
34812
+ return mergeSchemas(resolveInheritanceChain(classId, classes)).filter(
34813
+ (entry) => membersById.get(entry.memberId)?.isStatic !== true
34751
34814
  );
34752
- const parentOf = /* @__PURE__ */ new Map();
34753
- for (const value of args.createdValues) {
34754
- for (const childId of referencedValueIdCandidates(value.value)) {
34755
- if (createdById.has(childId)) parentOf.set(childId, value);
34815
+ }
34816
+ function mergeStoredInstanceSchema(classId, classes, members) {
34817
+ const membersById = new Map(members.map((member) => [member.id, member]));
34818
+ return mergeInstanceSurfaceSchema(classId, classes, members).filter(
34819
+ (entry) => membersById.get(entry.memberId)?.isReadOnly !== true
34820
+ );
34821
+ }
34822
+ function findSchemaPlacement(memberId, classes) {
34823
+ for (const schemaClass2 of classes) {
34824
+ for (const [key, placedMemberId] of Object.entries(schemaClass2.schema)) {
34825
+ if (placedMemberId === memberId) {
34826
+ return { ownerClass: schemaClass2, schemaKey: key };
34827
+ }
34756
34828
  }
34757
34829
  }
34758
- for (const value of args.createdValues) {
34759
- if (typeof value.containerId !== "string") continue;
34760
- const container = createdById.get(value.containerId);
34761
- if (container !== void 0) parentOf.set(value.id, container);
34762
- }
34763
- const depthOf = (value) => {
34764
- let depth = 0;
34765
- let current = value;
34766
- const seen = /* @__PURE__ */ new Set();
34767
- while (current !== void 0 && !seen.has(current.id)) {
34768
- seen.add(current.id);
34769
- current = parentOf.get(current.id);
34770
- if (current !== void 0) depth += 1;
34771
- }
34772
- return depth;
34773
- };
34774
- const boundaries = args.createdValues.filter((value) => {
34775
- const declaration = normalizeStorageKeyDeclaration(
34776
- args.declarationByValueId.get(value.id)
34777
- );
34778
- return declaration !== STORAGE_KEY_INHERIT;
34779
- }).sort((left, right) => depthOf(left) - depthOf(right));
34780
- for (const boundary of boundaries) {
34781
- const parentCreated = parentOf.get(boundary.id);
34782
- const parent = parentCreated !== void 0 ? {
34783
- mapKey: parentCreated.mapKey ?? null,
34784
- classId: parentCreated.classId ?? void 0
34785
- } : args.existingParentContextById?.get(boundary.id);
34786
- if (parent === void 0) {
34787
- throw new Error(
34788
- `Cannot resolve storage partition for created value "${boundary.id}": its placement parent is neither a created value nor supplied in existingParentContextById.`
34789
- );
34790
- }
34791
- const mapKey = resolveMapKeyForCreatedValue({
34792
- declaration: args.declarationByValueId.get(boundary.id),
34793
- parentMapKey: parent.mapKey,
34794
- parentClassId: parent.classId
34795
- });
34796
- for (const member of collectCreatedSubtreeValues(
34797
- boundary,
34798
- args.createdValues
34799
- )) {
34800
- if (mapKey === null || mapKey === MAIN_STORAGE_PARTITION) {
34801
- delete member.mapKey;
34802
- } else {
34803
- member.mapKey = mapKey;
34804
- }
34830
+ return null;
34831
+ }
34832
+ function walkExtendsMemberChain(startId, members, pick, options) {
34833
+ const requireKind = options?.requireKind;
34834
+ const maxHops = options?.maxHops ?? 16;
34835
+ let cursor = members.find((a) => a.id === startId);
34836
+ for (let i = 0; cursor && i < maxHops; i++) {
34837
+ if (requireKind !== void 0 && cursor.kind !== requireKind) {
34838
+ return void 0;
34805
34839
  }
34840
+ const v = pick(cursor);
34841
+ if (v !== void 0) return v;
34842
+ const nextId = getOptionalString(cursor, "extendsMemberId");
34843
+ if (nextId === void 0) return void 0;
34844
+ cursor = members.find((a) => a.id === nextId);
34806
34845
  }
34846
+ return void 0;
34807
34847
  }
34808
- function collectCreatedSubtreeValues(root, createdValues) {
34809
- const createdById = new Map(createdValues.map((value) => [value.id, value]));
34810
- const subtree = [];
34848
+ function resolveMember2(member, members) {
34849
+ const chain = [];
34811
34850
  const visited = /* @__PURE__ */ new Set();
34812
- const queue = [root];
34813
- while (queue.length > 0) {
34814
- const current = queue.shift();
34815
- if (current === void 0) break;
34816
- if (visited.has(current.id)) continue;
34817
- visited.add(current.id);
34818
- subtree.push(current);
34819
- for (const childId of referencedValueIdCandidates(current.value)) {
34820
- const child = createdById.get(childId);
34821
- if (child !== void 0) queue.push(child);
34851
+ let current = member;
34852
+ while (current) {
34853
+ const id2 = getOptionalString(current, "id");
34854
+ if (id2 !== void 0) {
34855
+ if (visited.has(id2)) {
34856
+ throw new CircularInheritanceError(id2, [...visited]);
34857
+ }
34858
+ visited.add(id2);
34822
34859
  }
34823
- for (const candidate of createdValues) {
34824
- if (candidate.containerId === current.id) queue.push(candidate);
34860
+ chain.push(current);
34861
+ if (!isMemberOverrideMarker(current)) break;
34862
+ const parentId = current.extendsMemberId;
34863
+ current = members.find((a) => a.id === parentId);
34864
+ }
34865
+ const tail = chain[chain.length - 1];
34866
+ if (!tail || isMemberOverrideMarker(tail)) {
34867
+ const startId = getOptionalString(member, "id") ?? "(unknown)";
34868
+ throw new UnresolvedMemberInheritanceError(startId);
34869
+ }
34870
+ const baseFirst = [...chain].reverse();
34871
+ const merged = {};
34872
+ for (const link of baseFirst) {
34873
+ for (const [key, value] of Object.entries(link)) {
34874
+ if (value === void 0) continue;
34875
+ merged[key] = value;
34825
34876
  }
34826
34877
  }
34827
- return subtree;
34828
- }
34829
- function referencedValueIdCandidates(value) {
34830
- if (Array.isArray(value)) {
34831
- return value.filter((entry) => typeof entry === "string");
34878
+ delete merged.extendsMemberId;
34879
+ if (member.isAbstract === void 0) {
34880
+ delete merged.isAbstract;
34881
+ } else {
34882
+ merged.isAbstract = member.isAbstract;
34832
34883
  }
34833
- if (typeof value === "object" && value !== null) {
34834
- return Object.values(value).filter(
34835
- (entry) => typeof entry === "string"
34836
- );
34884
+ if (member.isReadOnly === void 0) {
34885
+ delete merged.isReadOnly;
34886
+ } else {
34887
+ merged.isReadOnly = member.isReadOnly;
34837
34888
  }
34838
- return [];
34889
+ return merged;
34839
34890
  }
34840
- var init_storage_partitions = __esm({
34841
- "../src/models/members/storage-partitions.ts"() {
34891
+ function descendantClassIds(classId, allClasses) {
34892
+ const result = /* @__PURE__ */ new Set();
34893
+ let added = true;
34894
+ while (added) {
34895
+ added = false;
34896
+ for (const t of allClasses) {
34897
+ if (t.id === classId) continue;
34898
+ if (result.has(t.id)) continue;
34899
+ if (t.extendsClassId === classId || t.extendsClassId && result.has(t.extendsClassId)) {
34900
+ result.add(t.id);
34901
+ added = true;
34902
+ }
34903
+ }
34904
+ }
34905
+ return result;
34906
+ }
34907
+ var CircularInheritanceError, UnresolvedMemberInheritanceError, mergeInstanceSchema;
34908
+ var init_inheritance = __esm({
34909
+ "../src/models/classes/inheritance.ts"() {
34842
34910
  "use strict";
34843
- init_inheritance();
34844
- init_member_storage_key();
34845
- init_member_storage_key();
34911
+ init_member_kinds();
34912
+ init_core();
34913
+ init_structural_narrow();
34914
+ init_classes();
34915
+ CircularInheritanceError = class extends Error {
34916
+ constructor(nodeId, chain) {
34917
+ super(
34918
+ `Circular inheritance detected at "${nodeId}"; chain: ${chain.join(" -> ")}`
34919
+ );
34920
+ this.nodeId = nodeId;
34921
+ this.chain = chain;
34922
+ this.name = "CircularInheritanceError";
34923
+ }
34924
+ nodeId;
34925
+ chain;
34926
+ };
34927
+ UnresolvedMemberInheritanceError = class extends Error {
34928
+ constructor(memberId) {
34929
+ super(
34930
+ `Inheritance chain for member "${memberId}" did not terminate at a non-override member`
34931
+ );
34932
+ this.memberId = memberId;
34933
+ this.name = "UnresolvedMemberInheritanceError";
34934
+ }
34935
+ memberId;
34936
+ };
34937
+ mergeInstanceSchema = mergeInstanceSurfaceSchema;
34846
34938
  }
34847
34939
  });
34848
34940
 
@@ -35030,6 +35122,11 @@ function substituteMember(member, env, members) {
35030
35122
  } else {
35031
35123
  delete substituted.isAbstract;
35032
35124
  }
35125
+ if (resolved.isReadOnly !== void 0) {
35126
+ substituted.isReadOnly = resolved.isReadOnly;
35127
+ } else {
35128
+ delete substituted.isReadOnly;
35129
+ }
35033
35130
  delete substituted.extendsMemberId;
35034
35131
  return substituted;
35035
35132
  }
@@ -35321,6 +35418,195 @@ var init_generics = __esm({
35321
35418
  }
35322
35419
  });
35323
35420
 
35421
+ // ../src/models/members/read-only-members.ts
35422
+ var VALUELESS_KINDS;
35423
+ var init_read_only_members = __esm({
35424
+ "../src/models/members/read-only-members.ts"() {
35425
+ "use strict";
35426
+ init_inheritance();
35427
+ init_generics();
35428
+ init_member_kinds();
35429
+ init_member_storage();
35430
+ VALUELESS_KINDS = /* @__PURE__ */ new Set([
35431
+ 10 /* NSProperty */,
35432
+ 13 /* Function */,
35433
+ 23 /* NSFunction */
35434
+ ]);
35435
+ }
35436
+ });
35437
+
35438
+ // ../src/models/members/member-kinds-db-response.ts
35439
+ var init_member_kinds_db_response = __esm({
35440
+ "../src/models/members/member-kinds-db-response.ts"() {
35441
+ "use strict";
35442
+ init_member_kinds();
35443
+ }
35444
+ });
35445
+
35446
+ // ../src/models/members/unordered-list-membership.ts
35447
+ var init_unordered_list_membership = __esm({
35448
+ "../src/models/members/unordered-list-membership.ts"() {
35449
+ "use strict";
35450
+ init_member_kinds();
35451
+ }
35452
+ });
35453
+
35454
+ // ../src/models/members/member-storage-key.ts
35455
+ function normalizeStorageKeyDeclaration(declaration) {
35456
+ if (declaration === void 0 || declaration === null) {
35457
+ return STORAGE_KEY_INHERIT;
35458
+ }
35459
+ if (declaration.length === 0) return STORAGE_KEY_INHERIT;
35460
+ if (declaration === STORAGE_KEY_ALL) {
35461
+ throw new Error(
35462
+ 'Storage key "all" is reserved for partition-scoped reads and cannot be declared on a member.'
35463
+ );
35464
+ }
35465
+ return declaration;
35466
+ }
35467
+ function storageKeyReferencesParentClass(declaration) {
35468
+ return declaration.includes(PARENT_CLASS_TEMPLATE_TOKEN);
35469
+ }
35470
+ function resolveMapKeyForCreatedValue(args) {
35471
+ const declaration = normalizeStorageKeyDeclaration(args.declaration);
35472
+ if (declaration === STORAGE_KEY_INHERIT) return args.parentMapKey;
35473
+ if (declaration === MAIN_STORAGE_PARTITION) return null;
35474
+ if (storageKeyReferencesParentClass(declaration)) {
35475
+ if (args.parentClassId === void 0) {
35476
+ throw new Error(
35477
+ `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).`
35478
+ );
35479
+ }
35480
+ return declaration.replaceAll(
35481
+ PARENT_CLASS_TEMPLATE_TOKEN,
35482
+ args.parentClassId
35483
+ );
35484
+ }
35485
+ return declaration;
35486
+ }
35487
+ var MAIN_STORAGE_PARTITION, STORAGE_KEY_INHERIT, STORAGE_KEY_ALL, PARENT_CLASS_TEMPLATE_TOKEN;
35488
+ var init_member_storage_key = __esm({
35489
+ "../src/models/members/member-storage-key.ts"() {
35490
+ "use strict";
35491
+ MAIN_STORAGE_PARTITION = "main";
35492
+ STORAGE_KEY_INHERIT = "inherit";
35493
+ STORAGE_KEY_ALL = "all";
35494
+ PARENT_CLASS_TEMPLATE_TOKEN = "$parentClass";
35495
+ }
35496
+ });
35497
+
35498
+ // ../src/models/members/storage-partitions.ts
35499
+ function stampCreatedValuesMapKey(values, mapKey) {
35500
+ const normalized = mapKey === void 0 || mapKey === null || mapKey.length === 0 || mapKey === MAIN_STORAGE_PARTITION ? null : mapKey;
35501
+ for (const value of values) {
35502
+ if (normalized === null) {
35503
+ delete value.mapKey;
35504
+ } else {
35505
+ value.mapKey = normalized;
35506
+ }
35507
+ }
35508
+ }
35509
+ function applyDeclaredStorageKeyOverrides(args) {
35510
+ const createdById = new Map(
35511
+ args.createdValues.map((value) => [value.id, value])
35512
+ );
35513
+ const parentOf = /* @__PURE__ */ new Map();
35514
+ for (const value of args.createdValues) {
35515
+ for (const childId of referencedValueIdCandidates(value.value)) {
35516
+ if (createdById.has(childId)) parentOf.set(childId, value);
35517
+ }
35518
+ }
35519
+ for (const value of args.createdValues) {
35520
+ if (typeof value.containerId !== "string") continue;
35521
+ const container = createdById.get(value.containerId);
35522
+ if (container !== void 0) parentOf.set(value.id, container);
35523
+ }
35524
+ const depthOf = (value) => {
35525
+ let depth = 0;
35526
+ let current = value;
35527
+ const seen = /* @__PURE__ */ new Set();
35528
+ while (current !== void 0 && !seen.has(current.id)) {
35529
+ seen.add(current.id);
35530
+ current = parentOf.get(current.id);
35531
+ if (current !== void 0) depth += 1;
35532
+ }
35533
+ return depth;
35534
+ };
35535
+ const boundaries = args.createdValues.filter((value) => {
35536
+ const declaration = normalizeStorageKeyDeclaration(
35537
+ args.declarationByValueId.get(value.id)
35538
+ );
35539
+ return declaration !== STORAGE_KEY_INHERIT;
35540
+ }).sort((left, right) => depthOf(left) - depthOf(right));
35541
+ for (const boundary of boundaries) {
35542
+ const parentCreated = parentOf.get(boundary.id);
35543
+ const parent = parentCreated !== void 0 ? {
35544
+ mapKey: parentCreated.mapKey ?? null,
35545
+ classId: parentCreated.classId ?? void 0
35546
+ } : args.existingParentContextById?.get(boundary.id);
35547
+ if (parent === void 0) {
35548
+ throw new Error(
35549
+ `Cannot resolve storage partition for created value "${boundary.id}": its placement parent is neither a created value nor supplied in existingParentContextById.`
35550
+ );
35551
+ }
35552
+ const mapKey = resolveMapKeyForCreatedValue({
35553
+ declaration: args.declarationByValueId.get(boundary.id),
35554
+ parentMapKey: parent.mapKey,
35555
+ parentClassId: parent.classId
35556
+ });
35557
+ for (const member of collectCreatedSubtreeValues(
35558
+ boundary,
35559
+ args.createdValues
35560
+ )) {
35561
+ if (mapKey === null || mapKey === MAIN_STORAGE_PARTITION) {
35562
+ delete member.mapKey;
35563
+ } else {
35564
+ member.mapKey = mapKey;
35565
+ }
35566
+ }
35567
+ }
35568
+ }
35569
+ function collectCreatedSubtreeValues(root, createdValues) {
35570
+ const createdById = new Map(createdValues.map((value) => [value.id, value]));
35571
+ const subtree = [];
35572
+ const visited = /* @__PURE__ */ new Set();
35573
+ const queue = [root];
35574
+ while (queue.length > 0) {
35575
+ const current = queue.shift();
35576
+ if (current === void 0) break;
35577
+ if (visited.has(current.id)) continue;
35578
+ visited.add(current.id);
35579
+ subtree.push(current);
35580
+ for (const childId of referencedValueIdCandidates(current.value)) {
35581
+ const child = createdById.get(childId);
35582
+ if (child !== void 0) queue.push(child);
35583
+ }
35584
+ for (const candidate of createdValues) {
35585
+ if (candidate.containerId === current.id) queue.push(candidate);
35586
+ }
35587
+ }
35588
+ return subtree;
35589
+ }
35590
+ function referencedValueIdCandidates(value) {
35591
+ if (Array.isArray(value)) {
35592
+ return value.filter((entry) => typeof entry === "string");
35593
+ }
35594
+ if (typeof value === "object" && value !== null) {
35595
+ return Object.values(value).filter(
35596
+ (entry) => typeof entry === "string"
35597
+ );
35598
+ }
35599
+ return [];
35600
+ }
35601
+ var init_storage_partitions = __esm({
35602
+ "../src/models/members/storage-partitions.ts"() {
35603
+ "use strict";
35604
+ init_inheritance();
35605
+ init_member_storage_key();
35606
+ init_member_storage_key();
35607
+ }
35608
+ });
35609
+
35324
35610
  // ../src/models/unity/unity-import-settings.ts
35325
35611
  function buildDefaultUnityTexture2DImportSettings() {
35326
35612
  return {
@@ -36450,7 +36736,7 @@ function buildDefaultMemberValue(args) {
36450
36736
  const unboundParamId = firstUnboundParamId(childEnv);
36451
36737
  if (unboundParamId !== null) {
36452
36738
  throw new Error(
36453
- `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).`
36739
+ `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).`
36454
36740
  );
36455
36741
  }
36456
36742
  if (member.classArguments != null && Object.keys(member.classArguments).length > 0) {
@@ -36467,7 +36753,7 @@ function buildDefaultMemberValue(args) {
36467
36753
  );
36468
36754
  }
36469
36755
  }
36470
- const merged = mergeInstanceSchema(
36756
+ const merged = mergeStoredInstanceSchema(
36471
36757
  effectiveClassId,
36472
36758
  args.document.classes,
36473
36759
  args.document.members
@@ -36773,7 +37059,7 @@ function formatDefaultReferenceLocation(memberName, key) {
36773
37059
  }
36774
37060
  function getSchemaEntryMap(document, classId) {
36775
37061
  return new Map(
36776
- mergeInstanceSchema(classId, document.classes, document.members).map(
37062
+ mergeStoredInstanceSchema(classId, document.classes, document.members).map(
36777
37063
  (entry) => [entry.schemaKey, entry]
36778
37064
  )
36779
37065
  );
@@ -36880,7 +37166,7 @@ function primitiveFallbackValue(document, member) {
36880
37166
  }
36881
37167
  if (member.kind === 21 /* Generic */) {
36882
37168
  throw new Error(
36883
- `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).`
37169
+ `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).`
36884
37170
  );
36885
37171
  }
36886
37172
  throw new Error(
@@ -37313,7 +37599,7 @@ var init_effective_storage = __esm({
37313
37599
  }
37314
37600
  /**
37315
37601
  * The set of effective storages of `memberId` across every placement
37316
- * (specs/member-storage.md §2.3, generalized to the placement *graph*).
37602
+ * (https://github.com/ryanbliss/neo-compose-specs/tree/main/new-features/complete/member-storage.md §2.3, generalized to the placement *graph*).
37317
37603
  */
37318
37604
  effectiveStorages(memberId) {
37319
37605
  const cached = this.effectiveByMemberId.get(memberId);
@@ -37711,6 +37997,7 @@ var init_members = __esm({
37711
37997
  "../src/models/members/index.ts"() {
37712
37998
  "use strict";
37713
37999
  init_member_storage();
38000
+ init_read_only_members();
37714
38001
  init_member_kinds();
37715
38002
  init_member_kinds_db_response();
37716
38003
  init_unordered_list_membership();
@@ -52680,7 +52967,13 @@ function evalPointer(pointer, scope, ctx) {
52680
52967
  case "staticMember" /* staticMember */:
52681
52968
  return evalStaticMember(pointer.memberId, ctx);
52682
52969
  case "keyOf" /* keyOf */:
52683
- return evalKeyOf(pointer.keyOf, scope, ctx, pointer.optional === true);
52970
+ return evalKeyOf(
52971
+ pointer.keyOf,
52972
+ scope,
52973
+ ctx,
52974
+ pointer.optional === true,
52975
+ pointer.memberId
52976
+ );
52684
52977
  case "operation" /* operation */:
52685
52978
  return evalOperation(pointer.operation, scope, ctx);
52686
52979
  case "function" /* function */:
@@ -53443,7 +53736,7 @@ function isRuntimeSpriteValue(value) {
53443
53736
  const sliceIndex = value.sliceIndex;
53444
53737
  return typeof sliceIndex === "number" && Number.isInteger(sliceIndex) && sliceIndex >= 0;
53445
53738
  }
53446
- function evalKeyOf(keyOf, scope, ctx, optional = false) {
53739
+ function evalKeyOf(keyOf, scope, ctx, optional = false, memberId) {
53447
53740
  const receiver = evalPointer(keyOf.pointer, scope, ctx);
53448
53741
  if (optional && (receiver === null || receiver === void 0)) {
53449
53742
  return null;
@@ -53491,6 +53784,11 @@ function evalKeyOf(keyOf, scope, ctx, optional = false) {
53491
53784
  if (assetMember.kind === "ok") return assetMember.value;
53492
53785
  const dispatched = dispatchSchemaMember(receiver, k, ctx);
53493
53786
  if (dispatched.kind === "ok") return dispatched.value;
53787
+ if (!dispatched.matchedMember && memberId !== void 0) {
53788
+ const member = evalMemberById(ctx.vm, memberId);
53789
+ const defaultValue = readOnlyDeclarationDefault(member, memberId, ctx);
53790
+ if (defaultValue.matched) return defaultValue.value;
53791
+ }
53494
53792
  if (Object.prototype.hasOwnProperty.call(receiver, k)) {
53495
53793
  const at = receiver[k];
53496
53794
  return resolveValueIfId(at, ctx);
@@ -53503,13 +53801,15 @@ function evalKeyOf(keyOf, scope, ctx, optional = false) {
53503
53801
  }
53504
53802
  function dispatchSchemaMember(receiver, schemaKey, ctx) {
53505
53803
  if (typeof receiver !== "object" || receiver === null) {
53506
- return { kind: "no-info" };
53804
+ return { kind: "no-info", matchedMember: false };
53507
53805
  }
53508
53806
  const member = resolveRuntimeSchemaMember(receiver, schemaKey, ctx);
53509
- if (!member) return { kind: "no-info" };
53807
+ if (!member) return { kind: "no-info", matchedMember: false };
53808
+ const defaultValue = readOnlyDeclarationDefault(member, member.id, ctx);
53809
+ if (defaultValue.matched) return { kind: "ok", value: defaultValue.value };
53510
53810
  if (member.kind === 10 /* NSProperty */) {
53511
53811
  if (!resolveCompiledGetter(member.id, ctx)) {
53512
- return { kind: "no-info" };
53812
+ return { kind: "no-info", matchedMember: true };
53513
53813
  }
53514
53814
  return {
53515
53815
  kind: "ok",
@@ -53517,18 +53817,46 @@ function dispatchSchemaMember(receiver, schemaKey, ctx) {
53517
53817
  };
53518
53818
  }
53519
53819
  if (!Object.prototype.hasOwnProperty.call(receiver, schemaKey)) {
53520
- return { kind: "no-info" };
53820
+ return { kind: "no-info", matchedMember: true };
53521
53821
  }
53522
53822
  const at = receiver[schemaKey];
53523
53823
  return { kind: "ok", value: resolveValueIfIdForMember(at, member, ctx) };
53524
53824
  }
53825
+ function readOnlyDeclarationDefault(member, memberId, ctx) {
53826
+ const readOnly = member?.isReadOnly;
53827
+ if (readOnly !== true) return { matched: false };
53828
+ if (member?.defaultValue === void 0 || member.defaultValue === null) {
53829
+ throw new NSGetterRuntimeError(
53830
+ `Read-only member '${member?.name ?? memberId}' has no declaration default.`
53831
+ );
53832
+ }
53833
+ let value = member.defaultValue.value;
53834
+ if (isMemberString(member) && member.localizable !== false && typeof value === "string") {
53835
+ value = resolveLocalizedTextId(value, ctx);
53836
+ } else if (shouldUnwrapSingleLookupValue(member)) {
53837
+ value = unwrapSingleLookupValue(value, ctx);
53838
+ }
53839
+ return { matched: true, value };
53840
+ }
53525
53841
  function resolveRuntimeSchemaMember(receiver, schemaKey, ctx) {
53526
53842
  if (typeof receiver !== "object" || receiver === null) return null;
53527
53843
  const row = trackedRowForValueReference(receiver, ctx);
53528
- const runtimeClassId = row === null ? void 0 : classIdForValueRow(row, ctx);
53844
+ if (row === null) return null;
53845
+ const runtimeClassId = classIdForValueRow(row, ctx);
53529
53846
  if (!runtimeClassId) return null;
53530
53847
  const memberId = cachedMergedSchemaMemberId(runtimeClassId, schemaKey, ctx);
53531
- return memberId === null ? null : evalMemberById(ctx.vm, memberId);
53848
+ if (memberId === null) return null;
53849
+ const member = evalMemberById(ctx.vm, memberId);
53850
+ if (member === null) return null;
53851
+ if (member.kind !== 21 /* Generic */) return member;
53852
+ const env = runtimeGenericEnvForValueRow(row, ctx, /* @__PURE__ */ new Set());
53853
+ try {
53854
+ return substituteMember(member, env, ctx.vm.members);
53855
+ } catch (error) {
53856
+ throw new NSGetterRuntimeError(
53857
+ `Cannot resolve runtime member '${member.name}' for Class '${runtimeClassId}': ${error instanceof Error ? error.message : String(error)}`
53858
+ );
53859
+ }
53532
53860
  }
53533
53861
  function resolveAssetRuntimeMember(receiver, memberName, ctx) {
53534
53862
  const record3 = receiver;
@@ -54261,7 +54589,11 @@ function validateClassConstructorDescriptor(info, ctx) {
54261
54589
  }
54262
54590
  let mergedSchema;
54263
54591
  try {
54264
- mergedSchema = mergeInstanceSchema(classId, ctx.vm.classes, ctx.vm.members);
54592
+ mergedSchema = mergeStoredInstanceSchema(
54593
+ classId,
54594
+ ctx.vm.classes,
54595
+ ctx.vm.members
54596
+ );
54265
54597
  } catch (error) {
54266
54598
  throw new NSGetterRuntimeError(
54267
54599
  `Cannot validate constructor for '${schemaClass2.name}': ${error instanceof Error ? error.message : String(error)}`
@@ -54292,6 +54624,16 @@ function validateClassConstructorDescriptor(info, ctx) {
54292
54624
  }
54293
54625
  seenSchemaKeys.add(field.schemaKey);
54294
54626
  seenMemberIds.add(field.memberId);
54627
+ const surfaceEntry = mergeInstanceSchema(
54628
+ classId,
54629
+ ctx.vm.classes,
54630
+ ctx.vm.members
54631
+ ).find((entry) => entry.schemaKey === field.schemaKey);
54632
+ if (surfaceEntry !== void 0 && evalMemberById(ctx.vm, surfaceEntry.memberId)?.isReadOnly === true) {
54633
+ throw new NSGetterRuntimeError(
54634
+ `Constructor field '${field.schemaKey}' on '${schemaClass2.name}' is a read-only declaration member and cannot have an instance value.`
54635
+ );
54636
+ }
54295
54637
  const schemaEntry = schemaByKey.get(field.schemaKey);
54296
54638
  if (schemaEntry === void 0) {
54297
54639
  throw new NSGetterRuntimeError(
@@ -56334,7 +56676,7 @@ function validateCreatedMigrationGraph(args) {
56334
56676
  }
56335
56677
  let merged;
56336
56678
  try {
56337
- merged = mergeInstanceSchema(classId, args.classes, args.members);
56679
+ merged = mergeStoredInstanceSchema(classId, args.classes, args.members);
56338
56680
  } catch (error) {
56339
56681
  throw graphError(
56340
56682
  args,