@neocompose/cli 0.53.2 → 0.53.4

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/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.53.4] - 2026-09-19
4
+
5
+ - `neo test` preserves runtime types and getter overrides for nested save objects after writes rebuild the evaluator ownership index. Virtual children reuse their known owning graph when no stored parent link exists.
6
+
7
+ ## [0.53.3] - 2026-09-19
8
+
9
+ - Executable object initializers can set immutable stored members during construction. Later mutation and readonly-member restrictions still apply.
10
+ - Evaluator effect results encode assigned lists and dictionaries as entry rows while preserving collection aliases during execution.
11
+
3
12
  ## [0.53.2] - 2026-09-19
4
13
 
5
14
  - `Reference(Symbol)` and `Reference(LocalValue)` inside a constructed instance's initializer block compile. The stored initializer now carries `Reference<T>(id: "…")`, which the strict resolver accepts, so a list entry created with `new Stop(hour: 7) { MerchantRef = Reference(TestMerchant) }`, a member-default template, and the replay after a class change all push instead of failing with `Unknown function 'Reference'`. A local that carries no `@id` yet is refused by name.
package/dist/neo.mjs CHANGED
@@ -7456,7 +7456,7 @@ var init_strict_resolver = __esm({
7456
7456
  }
7457
7457
  return resolved.pointer.value;
7458
7458
  }
7459
- resolveAssignment(statement, scope) {
7459
+ resolveAssignment(statement, scope, assignmentContext = "ordinary") {
7460
7460
  if (!isAssignmentTarget(statement.target)) {
7461
7461
  throw new CompileError(
7462
7462
  "Assignment target must be a variable, member, or indexed value.",
@@ -7494,9 +7494,27 @@ var init_strict_resolver = __esm({
7494
7494
  statement.pos
7495
7495
  );
7496
7496
  }
7497
- if (!writability || writability === "readOnly" /* ReadOnly */ || writability === "immutable" /* Immutable */) {
7497
+ if (target.symbol?.readonlyDeclaration === true) {
7498
7498
  throw new CompileError(
7499
- 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.`,
7499
+ `Member '${target.symbol.name}' is readonly and can only be changed through its class default.`,
7500
+ statement.pos
7501
+ );
7502
+ }
7503
+ if (writability === void 0) {
7504
+ throw new CompileError(
7505
+ `Cannot assign to a read-only ${this.describe(target.type)} value.`,
7506
+ statement.pos
7507
+ );
7508
+ }
7509
+ if (writability === "readOnly" /* ReadOnly */) {
7510
+ throw new CompileError(
7511
+ `Cannot assign to a read-only ${this.describe(target.type)} value.`,
7512
+ statement.pos
7513
+ );
7514
+ }
7515
+ if (writability === "immutable" /* Immutable */ && assignmentContext !== "initializer") {
7516
+ throw new CompileError(
7517
+ "Cannot assign because the target's effective storage is immutable.",
7500
7518
  statement.pos
7501
7519
  );
7502
7520
  }
@@ -9281,16 +9299,22 @@ var init_strict_resolver = __esm({
9281
9299
  const registryEntry = this.resolveRegistryEntry(receiver, name, pos);
9282
9300
  if (registryEntry) return registryEntry;
9283
9301
  if (receiver.indexSurface) {
9284
- const index = (this.context.project.listIndexes ?? []).find(
9285
- (candidate) => candidate.listMemberId === receiver.indexSurface?.listMemberId && candidate.name === name
9302
+ const surface = receiver.indexSurface;
9303
+ const declared = this.indexesFor(surface).find(
9304
+ (candidate) => candidate.name === name
9286
9305
  );
9287
- if (!index) {
9306
+ if (!declared) {
9288
9307
  throw new CompileError(
9289
9308
  `List index surface has no declared index '${name}'.`,
9290
9309
  pos
9291
9310
  );
9292
9311
  }
9293
- const viewValueType = index.unique ? index.valueType : {
9312
+ const index = {
9313
+ ...declared,
9314
+ keyType: this.boundIndexKeyType(declared, surface.elementType),
9315
+ valueType: surface.elementType
9316
+ };
9317
+ const viewValueType = index.unique ? { ...index.valueType, nullable: true } : {
9294
9318
  kind: "list",
9295
9319
  elementType: { ...index.valueType, nullable: false },
9296
9320
  readOnly: true
@@ -9309,10 +9333,7 @@ var init_strict_resolver = __esm({
9309
9333
  indexCollectionPointer: receiver.pointer
9310
9334
  };
9311
9335
  }
9312
- const receiverListMemberId = receiver.type.kind === "list" ? receiver.type.listMemberId : void 0;
9313
- if (receiver.type.kind === "list" && name === "Index" && receiverListMemberId && (this.context.project.listIndexes ?? []).some(
9314
- (candidate) => candidate.listMemberId === receiverListMemberId
9315
- )) {
9336
+ if (receiver.type.kind === "list" && name === "Index" && this.indexesFor(receiver.type).length > 0) {
9316
9337
  return {
9317
9338
  pointer: receiver.pointer,
9318
9339
  type: { kind: "primitive", name: "unknown" },
@@ -9462,6 +9483,23 @@ var init_strict_resolver = __esm({
9462
9483
  pos
9463
9484
  );
9464
9485
  }
9486
+ boundIndexKeyType(index, entryType) {
9487
+ const entry = neoScriptMemberLookupType(entryType);
9488
+ if (entry.kind !== "named") return index.keyType;
9489
+ const declaring = this.project.typeById.get(entry.typeId);
9490
+ if (!declaring) return index.keyType;
9491
+ const key = declaring.members.find((member) => member.name === index.name);
9492
+ return substituteTypeParameters(
9493
+ key?.type ?? index.keyType,
9494
+ entry,
9495
+ declaring
9496
+ );
9497
+ }
9498
+ /** The indexes declared on a schema-backed List, keyed by its member. */
9499
+ indexesFor(type) {
9500
+ if (type.listMemberId === void 0) return [];
9501
+ return this.project.indexesByListMemberId.get(type.listMemberId) ?? [];
9502
+ }
9465
9503
  /**
9466
9504
  * P42 §2.3. `Images.<Name>` / `AudioClips.<Name>`.
9467
9505
  *
@@ -10341,7 +10379,8 @@ var init_strict_resolver = __esm({
10341
10379
  receiver: { kind: "ident", name, pos: entry.pos }
10342
10380
  }
10343
10381
  },
10344
- initializerScope
10382
+ initializerScope,
10383
+ "initializer"
10345
10384
  );
10346
10385
  if (assignment.type !== "assign" /* Assign */) {
10347
10386
  throw new CompileError(
@@ -16354,6 +16393,28 @@ function effectiveSourceAccessModifier(modifiers, ownerKind) {
16354
16393
  if (explicit !== void 0) return explicit;
16355
16394
  return ownerKind === "interface" ? "public" : "private";
16356
16395
  }
16396
+ function sourceMemberKeyArgument(annotation2) {
16397
+ const positional = annotation2.arguments[0];
16398
+ const argument2 = annotation2.arguments.find((entry) => entry.name === "member") ?? (positional !== void 0 && positional.name === void 0 ? positional : void 0);
16399
+ if (argument2 === void 0) return null;
16400
+ const text = argument2.text.trim();
16401
+ return {
16402
+ key: /^[A-Za-z_][A-Za-z0-9_]*$/.test(text) ? text : null,
16403
+ range: argument2.range
16404
+ };
16405
+ }
16406
+ function* sourceListIndexAnnotations(annotations) {
16407
+ for (const annotation2 of annotations) {
16408
+ if (annotation2.name !== "index") continue;
16409
+ const key = sourceMemberKeyArgument(annotation2)?.key;
16410
+ if (!key) continue;
16411
+ yield {
16412
+ key,
16413
+ unique: annotation2.arguments.find((argument2) => argument2.name === "kind")?.text.trim() === ".Unique",
16414
+ annotation: annotation2
16415
+ };
16416
+ }
16417
+ }
16357
16418
  var init_project_source_ast = __esm({
16358
16419
  "../packages/neoscript-language/src/project-source-ast.ts"() {
16359
16420
  "use strict";
@@ -22751,7 +22812,7 @@ function validateMember(uri, member, ownerName, documentKind, ownerScope, enviro
22751
22812
  );
22752
22813
  validateTypeAnnotations(uri, member.type, environment, diagnostics);
22753
22814
  if (documentKind !== "flow") {
22754
- validateListAnnotations(uri, member, environment, diagnostics);
22815
+ validateListAnnotations(uri, member, ownerName, environment, diagnostics);
22755
22816
  }
22756
22817
  const scope = new Map(ownerScope);
22757
22818
  if (member.kind === "function") {
@@ -23965,14 +24026,21 @@ function anchorPosition(anchor, offset) {
23965
24026
  return { line: anchor.start.line + line, character };
23966
24027
  }
23967
24028
  function sourceClassFieldByName(declaration, name, environment, initialBindings = /* @__PURE__ */ new Map()) {
24029
+ const resolved = sourceClassMemberByName(
24030
+ declaration,
24031
+ name,
24032
+ environment,
24033
+ initialBindings
24034
+ );
24035
+ return resolved?.member.kind === "field" ? resolved : void 0;
24036
+ }
24037
+ function sourceClassMemberByName(declaration, name, environment, initialBindings = /* @__PURE__ */ new Map()) {
23968
24038
  const visited = /* @__PURE__ */ new Set();
23969
24039
  let current = declaration;
23970
24040
  let bindings = initialBindings;
23971
24041
  while (current !== void 0 && !visited.has(current.name)) {
23972
24042
  visited.add(current.name);
23973
- const member = current.members.find(
23974
- (candidate) => candidate.kind === "field" && candidate.name === name
23975
- );
24043
+ const member = current.members.find((candidate) => candidate.name === name);
23976
24044
  if (member !== void 0) return { member, bindings };
23977
24045
  const base = current.baseTypes.find(
23978
24046
  (candidate) => environment.types.has(candidate.name)
@@ -24202,20 +24270,17 @@ function semanticMemberType(member, environment, bindings = /* @__PURE__ */ new
24202
24270
  if (type.name !== "List") return type;
24203
24271
  const entryType = type.arguments?.[0];
24204
24272
  if (!entryType) return type;
24205
- const entryClass = environment.types.get(entryType.name);
24206
- if (!entryClass) return type;
24207
24273
  const indexes2 = /* @__PURE__ */ new Map();
24208
- for (const annotation2 of member.annotations) {
24209
- if (annotation2.name !== "index") continue;
24210
- const key = memberKeyArgument(annotation2)?.key;
24211
- if (!key) continue;
24212
- const field = resolveEntryField(entryClass, key, environment);
24274
+ for (const index of sourceListIndexAnnotations(member.annotations)) {
24275
+ const field = sourceClassMemberForType(entryType, index.key, environment);
24213
24276
  if (!field) continue;
24214
- const unique = contextualAnnotationEnum([annotation2], "index", "kind") === "Unique";
24215
- const valueType = unique ? { ...entryType, nullable: true } : { name: "List", arguments: [{ ...entryType, nullable: false }] };
24216
- indexes2.set(key, {
24277
+ const valueType = index.unique ? { ...entryType, nullable: true } : { name: "List", arguments: [{ ...entryType, nullable: false }] };
24278
+ indexes2.set(index.key, {
24217
24279
  name: "Dictionary",
24218
- arguments: [semanticType(field.type), valueType]
24280
+ arguments: [
24281
+ substituteSemanticType(semanticType(field.member.type), field.bindings),
24282
+ valueType
24283
+ ]
24219
24284
  });
24220
24285
  }
24221
24286
  return indexes2.size === 0 ? type : { ...type, listIndexes: indexes2 };
@@ -24449,15 +24514,18 @@ function contextualAnnotationEnum(annotations, name, field) {
24449
24514
  const value = annotations.find((annotation2) => annotation2.name === name)?.arguments.find((argument2) => argument2.name === field)?.text.trim();
24450
24515
  return value?.startsWith(".") ? value.slice(1) : null;
24451
24516
  }
24452
- function validateListAnnotations(uri, member, environment, diagnostics) {
24517
+ function validateListAnnotations(uri, member, ownerName, environment, diagnostics) {
24453
24518
  const declared = member.annotations.filter(
24454
24519
  (annotation2) => annotation2.name === "index" || annotation2.name === "column"
24455
24520
  );
24456
24521
  if (declared.length === 0) return;
24457
- const entryClass = listEntryClass(member.type, environment);
24522
+ const entryType = listEntryType(
24523
+ member.type,
24524
+ environment.types.get(ownerName)
24525
+ );
24458
24526
  const seen = /* @__PURE__ */ new Set();
24459
24527
  for (const annotation2 of declared) {
24460
- const target = memberKeyArgument(annotation2);
24528
+ const target = sourceMemberKeyArgument(annotation2);
24461
24529
  if (target === null) {
24462
24530
  pushDiagnostic(
24463
24531
  diagnostics,
@@ -24492,20 +24560,25 @@ function validateListAnnotations(uri, member, environment, diagnostics) {
24492
24560
  if (annotation2.name === "column" && target.key === LIST_COLUMN_INHERITANCE_KEY) {
24493
24561
  continue;
24494
24562
  }
24495
- if (entryClass === null) continue;
24496
- const field = resolveEntryField(entryClass, target.key, environment);
24497
- if (field === null) {
24563
+ if (entryType === null) continue;
24564
+ if (!environment.types.has(entryType.name)) continue;
24565
+ const field = sourceClassMemberForType(entryType, target.key, environment);
24566
+ if (field === void 0) {
24498
24567
  pushDiagnostic(
24499
24568
  diagnostics,
24500
24569
  uri,
24501
24570
  target.range,
24502
24571
  "unresolved-list-annotation-member",
24503
- `@${annotation2.name}(member: ${target.key}) on '${member.name}' does not resolve on list entry class '${entryClass.name}'.`
24572
+ `@${annotation2.name}(member: ${target.key}) on '${member.name}' does not resolve on list entry class '${entryType.name}'.`
24504
24573
  );
24505
24574
  continue;
24506
24575
  }
24507
24576
  if (annotation2.name !== "index") continue;
24508
- const domainError = indexKeyDomainError(field, environment);
24577
+ const domainError = indexKeyDomainError(
24578
+ field.member,
24579
+ substituteSemanticType(semanticType(field.member.type), field.bindings),
24580
+ environment
24581
+ );
24509
24582
  if (domainError !== null) {
24510
24583
  pushDiagnostic(
24511
24584
  diagnostics,
@@ -24517,46 +24590,45 @@ function validateListAnnotations(uri, member, environment, diagnostics) {
24517
24590
  }
24518
24591
  }
24519
24592
  }
24520
- function memberKeyArgument(annotation2) {
24521
- const positional = annotation2.arguments[0];
24522
- const argument2 = annotation2.arguments.find((entry) => entry.name === "member") ?? (positional !== void 0 && positional.name === void 0 ? positional : void 0);
24523
- if (argument2 === void 0) return null;
24524
- const text = argument2.text.trim();
24525
- return {
24526
- key: IDENTIFIER_PATTERN.test(text) ? text : null,
24527
- range: argument2.range
24528
- };
24529
- }
24530
- function listEntryClass(type, environment) {
24593
+ function listEntryType(type, owner) {
24531
24594
  if (type.name !== "List" && type.name !== "Set") return null;
24532
- const entry = type.typeArguments[0];
24533
- if (entry === void 0) return null;
24534
- return environment.types.get(entry.name) ?? null;
24535
- }
24536
- function resolveEntryField(entryClass, key, environment) {
24595
+ let entry = type.typeArguments[0];
24537
24596
  const visited = /* @__PURE__ */ new Set();
24538
- let current = entryClass;
24539
- while (current !== void 0 && !visited.has(current.name)) {
24540
- visited.add(current.name);
24541
- const match = current.members.find((entry) => entry.name === key);
24542
- if (match !== void 0) return match;
24543
- current = current.baseTypes.map((base) => environment.types.get(base.name)).find((base) => base !== void 0);
24597
+ while (entry !== void 0) {
24598
+ const name = entry.name;
24599
+ const parameter4 = owner?.genericParameters.find(
24600
+ (candidate) => candidate.name === name
24601
+ );
24602
+ if (!parameter4) return semanticType(entry);
24603
+ if (visited.has(parameter4.name)) return null;
24604
+ visited.add(parameter4.name);
24605
+ entry = parameter4.constraint;
24544
24606
  }
24545
24607
  return null;
24546
24608
  }
24547
- function indexKeyDomainError(field, environment) {
24609
+ function sourceClassMemberForType(type, name, environment) {
24610
+ const declaration = environment.types.get(type.name);
24611
+ if (!declaration) return void 0;
24612
+ const bindings = /* @__PURE__ */ new Map();
24613
+ for (const [index, parameter4] of declaration.genericParameters.entries()) {
24614
+ const argument2 = type.arguments?.[index];
24615
+ if (argument2) bindings.set(parameter4.name, argument2);
24616
+ }
24617
+ return sourceClassMemberByName(declaration, name, environment, bindings);
24618
+ }
24619
+ function indexKeyDomainError(field, type, environment) {
24548
24620
  if (field.kind !== "field") {
24549
24621
  return `'${field.name}' is a ${field.kind}, and index keys require a stored field`;
24550
24622
  }
24551
- if (field.type.name === "string") {
24623
+ if (type.name === "string") {
24552
24624
  return annotationEnumOption(field.annotations, "settings", "format") === "Plain" ? null : `'${field.name}' is localized, and String index keys must be plain`;
24553
24625
  }
24554
- if (environment.enums.has(field.type.name)) return null;
24555
- const entry = field.type.typeArguments[0];
24556
- if (field.type.name === "List" && entry !== void 0 && environment.enums.has(entry.name) && annotationEnumOption(field.annotations, "settings", "selection") === "Multi") {
24626
+ if (environment.enums.has(type.name)) return null;
24627
+ const entry = type.arguments?.[0];
24628
+ if (type.name === "List" && entry !== void 0 && environment.enums.has(entry.name) && annotationEnumOption(field.annotations, "settings", "selection") === "Multi") {
24557
24629
  return `'${field.name}' is a multi-select Enum, and index keys require a single-select Enum`;
24558
24630
  }
24559
- return `'${field.name}' is a ${field.type.name} field, and index keys require a plain String or a single-select Enum`;
24631
+ return `'${field.name}' is a ${type.name} field, and index keys require a plain String or a single-select Enum`;
24560
24632
  }
24561
24633
  function annotationEnumOption(annotations, name, field) {
24562
24634
  const value = annotations.find((annotation2) => annotation2.name === name)?.arguments.find((argument2) => argument2.name === field)?.text.trim();
@@ -24711,10 +24783,11 @@ function classDerivesFromName(name, ancestorName, environment, seen = /* @__PURE
24711
24783
  return classDerivesFromName(base.name, ancestorName, environment, seen);
24712
24784
  });
24713
24785
  }
24714
- var NEO_VARIANT_FOLDER_SETTING, primitiveType, IDENTIFIER_PATTERN, LIST_COLUMN_INHERITANCE_KEY, SEMANTIC_PRIMITIVE_TYPE_NAMES;
24786
+ var NEO_VARIANT_FOLDER_SETTING, primitiveType, LIST_COLUMN_INHERITANCE_KEY, SEMANTIC_PRIMITIVE_TYPE_NAMES;
24715
24787
  var init_project_source_semantics = __esm({
24716
24788
  "../packages/neoscript-language/src/project-source-semantics.ts"() {
24717
24789
  "use strict";
24790
+ init_project_source_ast();
24718
24791
  init_project_local_values();
24719
24792
  init_language_spec();
24720
24793
  init_project_schema_contract_generated();
@@ -24727,7 +24800,6 @@ var init_project_source_semantics = __esm({
24727
24800
  init_project_root();
24728
24801
  NEO_VARIANT_FOLDER_SETTING = "folder";
24729
24802
  primitiveType = (name) => ({ name });
24730
- IDENTIFIER_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
24731
24803
  LIST_COLUMN_INHERITANCE_KEY = "__other__";
24732
24804
  SEMANTIC_PRIMITIVE_TYPE_NAMES = /* @__PURE__ */ new Set([
24733
24805
  "bool",
@@ -25326,8 +25398,29 @@ function buildProjectGraph(documents) {
25326
25398
  writable: false
25327
25399
  });
25328
25400
  }
25401
+ const listIndexes2 = [];
25402
+ for (const { member, symbol, uri } of memberInfo.values()) {
25403
+ if (symbol.type.kind !== "list") continue;
25404
+ const entryType = symbol.type.elementType;
25405
+ const entrySchema = neoScriptMemberLookupType(entryType);
25406
+ if (entrySchema.kind !== "named") continue;
25407
+ const entryMembers = effectiveMembers.get(entrySchema.typeId);
25408
+ for (const index of sourceListIndexAnnotations(member.annotations)) {
25409
+ const key = entryMembers?.find((field) => field.name === index.key);
25410
+ if (key === void 0) continue;
25411
+ listIndexes2.push({
25412
+ id: `${symbol.id}:index:${key.name}`,
25413
+ listMemberId: symbol.id,
25414
+ name: key.name,
25415
+ keyType: key.type,
25416
+ valueType: entryType,
25417
+ unique: index.unique,
25418
+ location: location(uri, index.annotation.range)
25419
+ });
25420
+ }
25421
+ }
25329
25422
  return {
25330
- project: { version: 1, types, globals, roots },
25423
+ project: { version: 1, types, globals, roots, listIndexes: listIndexes2 },
25331
25424
  declarationsByName,
25332
25425
  typeIdsByName: typeIds,
25333
25426
  memberInfo
@@ -25338,7 +25431,8 @@ function sourceMemberSymbol(member, owner, typeIds, source) {
25338
25431
  member.annotations,
25339
25432
  `member:${owner.id}:${member.name}`
25340
25433
  );
25341
- const type = sourceTypeRef(member.type, owner, typeIds);
25434
+ const declaredType = sourceTypeRef(member.type, owner, typeIds);
25435
+ const type = declaredType.kind === "list" ? { ...declaredType, listMemberId: id2 } : declaredType;
25342
25436
  const staticMember = member.modifiers.includes("static");
25343
25437
  const readonlyDeclaration = member.modifiers.includes("readonly");
25344
25438
  const common = {
@@ -71294,6 +71388,33 @@ function exportInitializerCollections(created, writes, ctx) {
71294
71388
  });
71295
71389
  return [...exported, ...extra];
71296
71390
  }
71391
+ function prepareSessionValueExport(ctx, checkpoint, writes) {
71392
+ const created = createdSessionValuesSince(ctx, checkpoint);
71393
+ if (created.length === 0) return;
71394
+ const invocationWrites = ctx.__executionState?.writes ?? writes;
71395
+ const exported = exportInitializerCollections(
71396
+ created,
71397
+ invocationWrites === writes || writes.length === 0 ? invocationWrites : [...invocationWrites, ...writes],
71398
+ ctx
71399
+ );
71400
+ if (exported === created) return;
71401
+ const replacements = /* @__PURE__ */ new Map();
71402
+ for (const row of exported) {
71403
+ const existing = ctx.__runtimeSessionValues?.get(row.id);
71404
+ if (existing === void 0) registerRuntimeSessionValue(ctx, row);
71405
+ else if (existing !== row) replacements.set(row.id, row);
71406
+ }
71407
+ return replacements;
71408
+ }
71409
+ function exportedSessionValuesSince(ctx, checkpoint, replacements) {
71410
+ const created = createdSessionValuesSince(ctx, checkpoint);
71411
+ if (replacements !== void 0) {
71412
+ for (const [index, row] of created.entries()) {
71413
+ created[index] = replacements.get(row.id) ?? row;
71414
+ }
71415
+ }
71416
+ return created;
71417
+ }
71297
71418
  function evaluateNSGetter(getter, ctx) {
71298
71419
  return evaluateNSGetterWithEffects(getter, ctx).value;
71299
71420
  }
@@ -71358,19 +71479,20 @@ function evaluateNSGetterWithEffects(getter, ctx, argumentValues = [], options =
71358
71479
  const rootIndex = created.findIndex(
71359
71480
  (row) => row.value === result.value
71360
71481
  );
71361
- const exported = exportInitializerCollections(
71482
+ const exported2 = exportInitializerCollections(
71362
71483
  created,
71363
71484
  writes,
71364
71485
  runtimeCtx
71365
71486
  );
71366
- if (exported !== created) {
71367
- for (const row of exported)
71487
+ if (exported2 !== created) {
71488
+ for (const row of exported2)
71368
71489
  registerRuntimeSessionValue(runtimeCtx, row);
71369
71490
  invalidateEvaluatorIndexes(runtimeCtx, null);
71370
71491
  }
71371
- const exportedRoot = exported[rootIndex];
71492
+ const exportedRoot = exported2[rootIndex];
71372
71493
  if (exportedRoot !== void 0) result.value = exportedRoot.value;
71373
71494
  }
71495
+ const exported = !options.exportInitializerValues && options.sessionValueExport !== "defer" ? prepareSessionValueExport(runtimeCtx, creationCheckpoint, writes) : void 0;
71374
71496
  finalizeConstructorAllocations(
71375
71497
  runtimeCtx,
71376
71498
  result.value,
@@ -71378,9 +71500,10 @@ function evaluateNSGetterWithEffects(getter, ctx, argumentValues = [], options =
71378
71500
  getter.typeInfo
71379
71501
  );
71380
71502
  result.value = preserveReadOnlyReturnIdentity(result.value, runtimeCtx);
71381
- const createdSessionValues = createdSessionValuesSince(
71503
+ const createdSessionValues = exportedSessionValuesSince(
71382
71504
  runtimeCtx,
71383
- creationCheckpoint
71505
+ creationCheckpoint,
71506
+ exported
71384
71507
  );
71385
71508
  return createdSessionValues.length === 0 ? { value: result.value, writes } : { value: result.value, writes, createdSessionValues };
71386
71509
  }
@@ -71399,6 +71522,7 @@ function evaluateNSVoidBody(action, ctx) {
71399
71522
  consumeBudget(runtimeCtx, "workUnits", 1, "work unit");
71400
71523
  const creationCheckpoint = sessionCreationCheckpoint(runtimeCtx);
71401
71524
  const scope = createTopLevelScope(runtimeCtx);
71525
+ let exported;
71402
71526
  try {
71403
71527
  const result = evalInstructions(action.instructions, scope, runtimeCtx, {
71404
71528
  allowFallthroughReturn: true,
@@ -71412,14 +71536,20 @@ function evaluateNSVoidBody(action, ctx) {
71412
71536
  }
71413
71537
  }
71414
71538
  rejectEscapedLoopTransfer(result, "NeoScript action");
71539
+ exported = prepareSessionValueExport(
71540
+ runtimeCtx,
71541
+ creationCheckpoint,
71542
+ writes
71543
+ );
71415
71544
  finalizeConstructorAllocations(runtimeCtx, void 0, ownsInvocationState);
71416
71545
  } catch (error) {
71417
71546
  finalizeConstructorAllocations(runtimeCtx, void 0, ownsInvocationState);
71418
71547
  throw error;
71419
71548
  }
71420
- const createdSessionValues = createdSessionValuesSince(
71549
+ const createdSessionValues = exportedSessionValuesSince(
71421
71550
  runtimeCtx,
71422
- creationCheckpoint
71551
+ creationCheckpoint,
71552
+ exported
71423
71553
  );
71424
71554
  return createdSessionValues.length === 0 ? { writes } : { writes, createdSessionValues };
71425
71555
  }
@@ -71431,6 +71561,7 @@ function evaluateNSFunction(action, ctx, args) {
71431
71561
  const creationCheckpoint = sessionCreationCheckpoint(runtimeCtx);
71432
71562
  const runtimeArgs = remapRuntimeArguments(args, runtimeCtx);
71433
71563
  let value;
71564
+ let exported;
71434
71565
  try {
71435
71566
  value = executeCompiledFunction(
71436
71567
  action,
@@ -71438,6 +71569,11 @@ function evaluateNSFunction(action, ctx, args) {
71438
71569
  runtimeArgs,
71439
71570
  action.typeInfo.type === 0 /* Null */
71440
71571
  );
71572
+ exported = prepareSessionValueExport(
71573
+ runtimeCtx,
71574
+ creationCheckpoint,
71575
+ writes
71576
+ );
71441
71577
  finalizeConstructorAllocations(
71442
71578
  runtimeCtx,
71443
71579
  value,
@@ -71448,9 +71584,10 @@ function evaluateNSFunction(action, ctx, args) {
71448
71584
  finalizeConstructorAllocations(runtimeCtx, void 0, ownsInvocationState);
71449
71585
  throw error;
71450
71586
  }
71451
- const createdSessionValues = createdSessionValuesSince(
71587
+ const createdSessionValues = exportedSessionValuesSince(
71452
71588
  runtimeCtx,
71453
- creationCheckpoint
71589
+ creationCheckpoint,
71590
+ exported
71454
71591
  );
71455
71592
  return createdSessionValues.length === 0 ? { value, writes } : { value, writes, createdSessionValues };
71456
71593
  }
@@ -74686,10 +74823,15 @@ function dispatchNSGetterById(memberId, receiver, ctx) {
74686
74823
  }
74687
74824
  const innerStack = new Set(stack);
74688
74825
  innerStack.add(memberId);
74689
- return evaluateNSGetter(getter, {
74690
- ...bindThisValue(ctx, receiver),
74691
- __getterCallStack: innerStack
74692
- });
74826
+ return evaluateNSGetterWithEffects(
74827
+ getter,
74828
+ {
74829
+ ...bindThisValue(ctx, receiver),
74830
+ __getterCallStack: innerStack
74831
+ },
74832
+ [],
74833
+ { sessionValueExport: "defer" }
74834
+ ).value;
74693
74835
  }
74694
74836
  function resolveCompiledGetter(memberId, ctx) {
74695
74837
  const cache = evaluatorResolutionCache(ctx).compiledGetterByMemberId;
@@ -75300,7 +75442,14 @@ function memberForValueRow(row, ctx) {
75300
75442
  if (indexes2.memberByRowId.has(row.id)) {
75301
75443
  return indexes2.memberByRowId.get(row.id) ?? null;
75302
75444
  }
75303
- const member = resolveMemberForValueRow(row, ctx, /* @__PURE__ */ new Set());
75445
+ let member = resolveMemberForValueRow(row, ctx, /* @__PURE__ */ new Set());
75446
+ if (member === null) {
75447
+ const rows = ctx.vm.databaseVM?.virtualInstanceRowsForValue(row.id);
75448
+ if (rows !== void 0 && rows.length > 0) {
75449
+ ensureVirtualExpansionIndexed(rows, ctx, indexes2);
75450
+ member = resolveMemberForValueRow(row, ctx, /* @__PURE__ */ new Set());
75451
+ }
75452
+ }
75304
75453
  indexes2.memberByRowId.set(row.id, member);
75305
75454
  return member;
75306
75455
  }
@@ -78015,7 +78164,10 @@ function evaluateInitializerInContext(init, member, ctx, createdValues, argument
78015
78164
  compiled,
78016
78165
  initializerCtx,
78017
78166
  argumentValues,
78018
- { initializerResult: { member, genericEnv } }
78167
+ {
78168
+ initializerResult: { member, genericEnv },
78169
+ sessionValueExport: "defer"
78170
+ }
78019
78171
  );
78020
78172
  } finally {
78021
78173
  closeFrame();
@@ -79953,15 +80105,22 @@ function cloneClassValueGraph(receiver, expectedClassId, ctx) {
79953
80105
  }
79954
80106
  const active = /* @__PURE__ */ new Set();
79955
80107
  const clonedBySourceId = /* @__PURE__ */ new Map();
79956
- const constructorArgumentReferenceCounts = /* @__PURE__ */ new Map();
79957
80108
  ctx.onUnattributableValueRead?.(`class-clone:${sourceId3}`);
79958
- for (const candidate of evaluatorValues(ctx)) {
79959
- for (const argument2 of ownedConstructorArgumentValues(candidate, ctx)) {
79960
- constructorArgumentReferenceCounts.set(
79961
- argument2.id,
79962
- (constructorArgumentReferenceCounts.get(argument2.id) ?? 0) + 1
79963
- );
80109
+ const constructorArgumentCandidates = evaluatorValues(ctx);
80110
+ let constructorArgumentReferenceCounts = null;
80111
+ function constructorArgumentReferenceCount(valueId) {
80112
+ if (constructorArgumentReferenceCounts === null) {
80113
+ constructorArgumentReferenceCounts = /* @__PURE__ */ new Map();
80114
+ for (const candidate of constructorArgumentCandidates) {
80115
+ for (const argument2 of ownedConstructorArgumentValues(candidate, ctx)) {
80116
+ constructorArgumentReferenceCounts.set(
80117
+ argument2.id,
80118
+ (constructorArgumentReferenceCounts.get(argument2.id) ?? 0) + 1
80119
+ );
80120
+ }
80121
+ }
79964
80122
  }
80123
+ return constructorArgumentReferenceCounts.get(valueId) ?? 0;
79965
80124
  }
79966
80125
  function cloneInitializerRow(sourceRow, sourceMember, id2, clonedContainerId, sourceTypeInfo) {
79967
80126
  if (sourceMember === null) {
@@ -80154,7 +80313,6 @@ function cloneClassValueGraph(receiver, expectedClassId, ctx) {
80154
80313
  constructorArgs[argument2.parameterId] = alreadyCloned.id;
80155
80314
  continue;
80156
80315
  }
80157
- if (constructorArgumentReferenceCounts.get(argument2.id) !== 1) continue;
80158
80316
  const argumentRow = evalValueById(
80159
80317
  ctx,
80160
80318
  argument2.id,
@@ -80165,6 +80323,7 @@ function cloneClassValueGraph(receiver, expectedClassId, ctx) {
80165
80323
  if (activeStaticBindingRoot(argument2.id, ctx) !== null || memberForValueRow(argumentRow, ctx) !== null) {
80166
80324
  continue;
80167
80325
  }
80326
+ if (constructorArgumentReferenceCount(argument2.id) !== 1) continue;
80168
80327
  constructorArgs[argument2.parameterId] = cloneRow2(
80169
80328
  argumentRow,
80170
80329
  null,
@@ -83106,7 +83265,7 @@ function rewriteExpandedRow(node, expansion) {
83106
83265
  const rewrite = (id2) => expansion.nodesByExpandedId.get(id2)?.virtualId ?? id2;
83107
83266
  const row = cloneRow(node.expandedRow);
83108
83267
  row.id = node.virtualId;
83109
- if (isMemberClassBase(node.member) || isMemberDictionaryBase(node.member)) {
83268
+ if (row.value !== null && (isMemberClassBase(node.member) || isMemberDictionaryBase(node.member))) {
83110
83269
  row.value = Object.fromEntries(
83111
83270
  Object.entries(stringRecord2(row.value)).map(([key, childId]) => [
83112
83271
  key,
@@ -83766,7 +83925,8 @@ function createHeadlessVirtualInstanceResolver(args) {
83766
83925
  requirement: 1 /* Required */
83767
83926
  });
83768
83927
  }
83769
- return emptyRows;
83928
+ const knownGraph = graphByValueId.get(receiverValueId);
83929
+ return knownGraph === void 0 ? emptyRows : expandRoot(knownGraph.instanceRootId, void 0);
83770
83930
  };
83771
83931
  return {
83772
83932
  virtualInstanceRowsForValue,
@@ -109271,7 +109431,7 @@ function inheritedMember(context, memberId) {
109271
109431
  function lowerListIndexes(ownerClass, declaration) {
109272
109432
  const declared = /* @__PURE__ */ new Set();
109273
109433
  return declaration.annotations.filter((entry) => entry.name === "index").map((entry) => {
109274
- const schemaKey = memberKeyArgument2(entry, ownerClass, declaration);
109434
+ const schemaKey = memberKeyArgument(entry, ownerClass, declaration);
109275
109435
  if (declared.has(schemaKey)) {
109276
109436
  throw new Error(
109277
109437
  `@index(member: ${schemaKey}) is declared more than once on ${ownerClass.name}.${declaration.name}.`
@@ -109287,7 +109447,7 @@ function lowerListIndexes(ownerClass, declaration) {
109287
109447
  function lowerListColumns(ownerClass, declaration) {
109288
109448
  const declared = /* @__PURE__ */ new Set();
109289
109449
  return declaration.annotations.filter((entry) => entry.name === "column").map((entry) => {
109290
- const memberKey = memberKeyArgument2(entry, ownerClass, declaration);
109450
+ const memberKey = memberKeyArgument(entry, ownerClass, declaration);
109291
109451
  if (declared.has(memberKey)) {
109292
109452
  throw new Error(
109293
109453
  `@column(member: ${memberKey}) is declared more than once on ${ownerClass.name}.${declaration.name}.`
@@ -109306,7 +109466,7 @@ function lowerListColumns(ownerClass, declaration) {
109306
109466
  };
109307
109467
  });
109308
109468
  }
109309
- function memberKeyArgument2(value, ownerClass, declaration) {
109469
+ function memberKeyArgument(value, ownerClass, declaration) {
109310
109470
  const positional = value.arguments[0];
109311
109471
  const raw = argument(value, "member") ?? (positional !== void 0 && positional.name === void 0 ? positional.expression : null);
109312
109472
  if (raw === null) {
@@ -138565,7 +138725,7 @@ var init_registry2 = __esm({
138565
138725
  "schema-contract/registry.mjs"() {
138566
138726
  "use strict";
138567
138727
  PROJECT_SCHEMA_CONTRACT = Object.freeze({
138568
- cliVersion: "0.53.2",
138728
+ cliVersion: "0.53.4",
138569
138729
  projectFileUploadBatchSize: 32,
138570
138730
  documentRecords: {
138571
138731
  member: {
@@ -146386,7 +146546,7 @@ There is no --keep-current: preserving current semantic state defines flatten.
146386
146546
  async function main() {
146387
146547
  const args = parseArgs(process.argv.slice(2));
146388
146548
  if (args.command === "--version") {
146389
- console.log("0.53.2");
146549
+ console.log("0.53.4");
146390
146550
  return;
146391
146551
  }
146392
146552
  if (args.command === null || args.command === "help" || args.command === "--help" || args.command === "-h") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neocompose/cli",
3
- "version": "0.53.2",
3
+ "version": "0.53.4",
4
4
  "description": "Neo Compose native project-source CLI with bidirectional sync.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -9,7 +9,7 @@ description: >-
9
9
  `@neocompose/cli` or `node cli/bin/neo.mjs` in the neo-compose repository.
10
10
  ---
11
11
 
12
- <!-- reviewed-through-cli: 0.53.2 -->
12
+ <!-- reviewed-through-cli: 0.53.4 -->
13
13
 
14
14
  # Neo Compose CLI
15
15
 
@@ -97,7 +97,7 @@ wrappers.
97
97
  The marker near the top of `SKILL.md` must exactly match the package version:
98
98
 
99
99
  ```html
100
- <!-- reviewed-through-cli: 0.53.2 -->
100
+ <!-- reviewed-through-cli: 0.53.4 -->
101
101
  ```
102
102
 
103
103
  The quoted version above is checked too, so this instruction cannot go stale
@@ -334,3 +334,5 @@ class PlacedItem<
334
334
  public TData Data = item.DefaultData.Clone();
335
335
  }
336
336
  ```
337
+
338
+ Executable object initializer blocks can set immutable stored members on the newly constructed instance, including nested `new` expressions. Ordinary assignments after construction remain subject to storage restrictions. `readonly` declarations still change only through class defaults.