@neocompose/cli 0.22.6 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,48 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.23.0] - 2026-08-05
4
+
5
+ ### Added
6
+
7
+ - Member defaults may be operator expressions — arithmetic over statics,
8
+ coalesce, indexing, type tests, string interpolation, and negation of a
9
+ non-literal. They are stored as computed initializers evaluated during
10
+ construction, exactly like call defaults, instead of failing with
11
+ "Expression binary is not a persisted default." Positions that store no
12
+ rows keep requiring literals and now say so precisely: dictionary keys,
13
+ Vector/Color components, and an `@id` written on part of an unparenthesized
14
+ computed entry (parenthesize the expression so the annotation names the
15
+ row).
16
+
17
+ ### Fixed
18
+
19
+ - Function members lower with the contract's value-less `required: false`
20
+ shape, so a push that adds a `.neo` function passes prospective validation,
21
+ and stored functions stop generating phantom `required` updates on every
22
+ push. Compiled bodies that read static members validate through the
23
+ previously missing static-member pointer guard.
24
+ - Push preparation reads the prospective document with pending enum-option
25
+ ids admitted and source-authored localized texts completed through the
26
+ server's own create envelope, so a push that creates an enum, adds an
27
+ option, or authors localized text no longer fails initializer
28
+ materialization or prospective animation validation. Committed-document
29
+ reads keep their exact strictness.
30
+ - Stored function bodies re-emit whole. A body opening with a nested block
31
+ (`switch`, `if`) was truncated to that block — leaking trailing statements
32
+ to class scope — and a body that was exactly one block silently lost its
33
+ guard on pull. An authored empty body re-emits as `{ }` instead of mutating
34
+ into a `native` signature.
35
+
36
+ ## [0.22.7] - 2026-08-05
37
+
38
+ ### Fixed
39
+
40
+ - Pull and reset cache canonical required-constructor calls reconstructed from
41
+ legacy materialized rows that predate durable constructor arguments.
42
+ - A pull followed by status or push remains a semantic no-op for projected
43
+ animation track and generic segment-frame rows, while later constructor edits
44
+ are still detected normally.
45
+
3
46
  ## [0.22.6] - 2026-08-05
4
47
 
5
48
  ### Fixed
package/dist/neo.mjs CHANGED
@@ -35135,6 +35135,12 @@ function isNSPointerVariable(value) {
35135
35135
  const v = value;
35136
35136
  return v?.type === "variable" /* variable */ && typeof v?.variableId === "string";
35137
35137
  }
35138
+ function isNSPointerStaticMember(value) {
35139
+ const v = value;
35140
+ if (v?.type !== "staticMember" /* staticMember */) return false;
35141
+ if (typeof v.memberId !== "string") return false;
35142
+ return v.memberId.length > 0;
35143
+ }
35138
35144
  function isNSPointerValue(value) {
35139
35145
  const v = value;
35140
35146
  return v?.type === "value" /* value */ && isNSValue(v?.value);
@@ -35260,7 +35266,7 @@ function isNSPointerFunctionErrorCheck(value) {
35260
35266
  return isNSFunctionErrorCheckMode(v.mode);
35261
35267
  }
35262
35268
  function isNSPointer(value) {
35263
- return isNSPointerReference(value) || isNSPointerVariable(value) || isNSPointerValue(value) || isNSPointerOperation(value) || isNSPointerFunction(value) || isNSPointerKeyOf(value) || isNSPointerListLiteral(value) || isNSPointerDictLiteral(value) || isNSPointerForceUnwrap(value) || isNSPointerIsCheck(value) || isNSPointerCallGetter(value) || isNSPointerCoalesce(value) || isNSPointerToBool(value) || isNSPointerStringify(value) || isNSPointerCallFunction(value) || isNSPointerCallDelegate(value) || isNSPointerFunctionErrorCheck(value);
35269
+ return isNSPointerReference(value) || isNSPointerVariable(value) || isNSPointerStaticMember(value) || isNSPointerValue(value) || isNSPointerOperation(value) || isNSPointerFunction(value) || isNSPointerKeyOf(value) || isNSPointerListLiteral(value) || isNSPointerDictLiteral(value) || isNSPointerForceUnwrap(value) || isNSPointerIsCheck(value) || isNSPointerCallGetter(value) || isNSPointerCoalesce(value) || isNSPointerToBool(value) || isNSPointerStringify(value) || isNSPointerCallFunction(value) || isNSPointerCallDelegate(value) || isNSPointerFunctionErrorCheck(value);
35264
35270
  }
35265
35271
  function isNSPointers(value) {
35266
35272
  return Array.isArray(value) && value.every(isNSPointer);
@@ -40714,20 +40720,25 @@ function isValidEnumOptionId(id2) {
40714
40720
  if (typeof id2 !== "string" || id2.length === 0) return false;
40715
40721
  return OPTION_ID_REGEX.test(withoutSystemRecordIdPrefix(id2));
40716
40722
  }
40723
+ function isProspectiveEnumOptionId(id2) {
40724
+ if (typeof id2 !== "string" || id2.length === 0) return false;
40725
+ if (isPendingId(id2)) return true;
40726
+ return isValidEnumOptionId(id2);
40727
+ }
40717
40728
  function isValidEnumOptionName(name) {
40718
40729
  return isValidSchemaAuthoredIdentifier(name);
40719
40730
  }
40720
- function isEnumOption(value) {
40731
+ function isEnumOptionWithIds(value, isOptionId) {
40721
40732
  const v = value;
40722
- return !!v && isValidDocsText(v.docsText) && typeof v.text === "string" && typeof v.id === "string" && isValidEnumOptionId(v.id) && typeof v.name === "string" && isValidEnumOptionName(v.name);
40733
+ return !!v && isValidDocsText(v.docsText) && typeof v.text === "string" && typeof v.id === "string" && isOptionId(v.id) && typeof v.name === "string" && isValidEnumOptionName(v.name);
40723
40734
  }
40724
- function isEnumOptionsRecord(value) {
40735
+ function isEnumOptionsRecordWithIds(value, isOptionId) {
40725
40736
  if (value === null || value === void 0) return false;
40726
40737
  if (typeof value !== "object") return false;
40727
40738
  if (Array.isArray(value)) return false;
40728
40739
  for (const [k, v] of Object.entries(value)) {
40729
- if (!isValidEnumOptionId(k)) return false;
40730
- if (!isEnumOption(v)) return false;
40740
+ if (!isOptionId(k)) return false;
40741
+ if (!isEnumOptionWithIds(v, isOptionId)) return false;
40731
40742
  if (v.id !== k) return false;
40732
40743
  }
40733
40744
  return true;
@@ -40737,12 +40748,12 @@ function isOptionalOptionKeyOrder(value) {
40737
40748
  if (!Array.isArray(value)) return false;
40738
40749
  return value.every((key) => typeof key === "string");
40739
40750
  }
40740
- function isEnumBase(value) {
40751
+ function isEnumBaseWithIds(value, isOptionId) {
40741
40752
  const v = value;
40742
40753
  if (!v) return false;
40743
40754
  if (typeof v.name !== "string") return false;
40744
40755
  if (!isValidDocsText(v.docsText)) return false;
40745
- if (!isEnumOptionsRecord(v.options)) return false;
40756
+ if (!isEnumOptionsRecordWithIds(v.options, isOptionId)) return false;
40746
40757
  if (!isOptionalOptionKeyOrder(v.optionKeyOrder)) return false;
40747
40758
  if (v.system !== void 0 && v.system !== null) {
40748
40759
  return isSystemMetadata(v.system);
@@ -40767,17 +40778,24 @@ function getEnumOptionKeyOrder(enumDef) {
40767
40778
  }
40768
40779
  return result;
40769
40780
  }
40770
- function isEnumProps(value) {
40781
+ function isEnumPropsWithIds(value, isOptionId) {
40771
40782
  const v = value;
40772
- return isEnumBase(value) && isWithId(value) && typeof v?.projectId === "string" && isEpochMillis(v?.createdAt) && isEpochMillis(v?.updatedAt);
40783
+ return isEnumBaseWithIds(value, isOptionId) && isWithId(value) && typeof v?.projectId === "string" && isEpochMillis(v?.createdAt) && isEpochMillis(v?.updatedAt);
40784
+ }
40785
+ function isEnumProps(value) {
40786
+ return isEnumPropsWithIds(value, isValidEnumOptionId);
40773
40787
  }
40774
40788
  function isEnum(value) {
40775
40789
  return isEnumProps(value);
40776
40790
  }
40791
+ function isProspectiveEnum(value) {
40792
+ return isEnumPropsWithIds(value, isProspectiveEnumOptionId);
40793
+ }
40777
40794
  var OPTION_ID_REGEX;
40778
40795
  var init_enum_types = __esm({
40779
40796
  "../src/models/enum/enum-types.ts"() {
40780
40797
  "use strict";
40798
+ init_src();
40781
40799
  init_core();
40782
40800
  init_schema_identifiers();
40783
40801
  init_system_record_id();
@@ -43144,12 +43162,10 @@ ${prefix}${renderType(context, member.returnType)} ${member.name}${body}`;
43144
43162
  (argument2) => `${renderType(context, argument2.type)} ${argument2.name}`
43145
43163
  ).join(", ")})`;
43146
43164
  const tracked = member.kind === "scriptFunction" ? member.script?.sourceText ?? uiFunctionSource(context, member) : null;
43147
- const body = tracked ? extractBody(tracked) ?? `{
43148
- ${indentNeoSourceNonEmptyLines(tracked.trim(), 2)}
43149
- }` : null;
43165
+ const body = tracked === null ? null : emitFunctionBody(tracked);
43150
43166
  const abstractContract = member.modifier === "abstract" || member.modifier === "abstractOverride";
43151
43167
  return `${annotations.join("\n")}
43152
- ${body ? `${signature} ${body}` : abstractContract ? `${signature};` : `native ${signature};`}`;
43168
+ ${body !== null ? `${signature} ${body}` : abstractContract ? `${signature};` : `native ${signature};`}`;
43153
43169
  }
43154
43170
  const type = renderMemberType(context, member, enclosingClassId);
43155
43171
  const initializer = initDefaultSource(member) ?? (member.isStatic ? context.staticInitializers.get(member.id) : void 0) ?? context.defaultInitializers.get(member.id) ?? renderDefault(context, member);
@@ -43951,10 +43967,12 @@ function systemAnnotation2(system) {
43951
43967
  ];
43952
43968
  return `@system(${args.join(", ")})`;
43953
43969
  }
43954
- function extractBody(source) {
43955
- const start = source.indexOf("{");
43956
- const end = source.lastIndexOf("}");
43957
- return start >= 0 && end > start ? source.slice(start, end + 1).trim() : null;
43970
+ function emitFunctionBody(sourceText) {
43971
+ const code = sourceText.trim();
43972
+ if (code.length === 0) return "{\n}";
43973
+ return `{
43974
+ ${indentNeoSourceNonEmptyLines(code, 2)}
43975
+ }`;
43958
43976
  }
43959
43977
  function id(value) {
43960
43978
  return `@id(${quote(value)})
@@ -48226,6 +48244,7 @@ function initializerRequiresEvaluation(index, expression, targetClassName, runti
48226
48244
  return true;
48227
48245
  }
48228
48246
  if (expression.kind === "call") return !isLiteralCall(expression);
48247
+ if (isEvaluatedOnlyExpression(expression)) return true;
48229
48248
  if (expression.kind !== "new") return false;
48230
48249
  const className = expression.className ?? targetClassName;
48231
48250
  if (!declaresConstructors(index, className)) return false;
@@ -48301,6 +48320,24 @@ function expressionReadsRuntimeIdentifier(expression, runtimeIdentifiers) {
48301
48320
  return false;
48302
48321
  }
48303
48322
  }
48323
+ function isEvaluatedOnlyExpression(expression) {
48324
+ switch (expression.kind) {
48325
+ case "annotated":
48326
+ return isEvaluatedOnlyExpression(expression.expression);
48327
+ case "binary":
48328
+ case "coalesce":
48329
+ case "index":
48330
+ case "is":
48331
+ case "force":
48332
+ case "litInterp":
48333
+ return true;
48334
+ case "unary":
48335
+ if (expression.op !== "-") return true;
48336
+ return isEvaluatedOnlyExpression(expression.operand);
48337
+ default:
48338
+ return false;
48339
+ }
48340
+ }
48304
48341
  function isLiteralCall(expression) {
48305
48342
  if (expression.callee.kind === "ident" && expression.callee.name === "Reference") {
48306
48343
  return true;
@@ -49232,10 +49269,13 @@ function memberCommon(context, ownerClass, declaration, id2, owner, base) {
49232
49269
  accessModifier: owner.kind === "classMember" ? effectiveSourceAccessModifier(declaration.modifiers, "class") : "public",
49233
49270
  modifier: memberModifier3(declaration.modifiers, base?.modifier),
49234
49271
  locked: hasAnnotation2(declaration.annotations, "locked"),
49235
- // Every declaration kind carries a type, and its nullability is the whole
49236
- // of what `required` means. Properties and getters used to fall back to
49237
- // the pulled value, which left a fresh workspace calling them optional.
49238
- required: !declaration.type.nullable,
49272
+ // Every value-bearing declaration carries a type, and its nullability is
49273
+ // the whole of what `required` means. Properties and getters used to fall
49274
+ // back to the pulled value, which left a fresh workspace calling them
49275
+ // optional. A callable owns no value, so `required` is fixed `false`
49276
+ // (nsfunction-member.md §1.2, function-member.md) and the declared
49277
+ // nullability travels in `returnTypeInfo.required` instead.
49278
+ required: declaration.kind === "function" ? false : !declaration.type.nullable,
49239
49279
  defaultValue: null,
49240
49280
  overrideOf: inheritedId,
49241
49281
  system: systemMetadata(declaration.annotations),
@@ -49955,15 +49995,7 @@ function lowerDefault(context, initializer, type, ownerClass, base, memberId, st
49955
49995
  return base?.defaultValue && "serverValueId" in base.defaultValue ? base.defaultValue : null;
49956
49996
  }
49957
49997
  const expression = parseExpression(initializer);
49958
- if (!isStoredDelegateLiteral(type, expression) && initializerRequiresEvaluation(
49959
- context.declaredConstructors,
49960
- expression,
49961
- type.name,
49962
- requiredConstructorParameterNames(
49963
- context.declaredConstructors,
49964
- ownerClass.name
49965
- )
49966
- )) {
49998
+ if (positionRequiresEvaluation(context, expression, type, ownerClass)) {
49967
49999
  return { init: { code: normalizeInitializerSource(initializer) } };
49968
50000
  }
49969
50001
  if (context.rowBackedDefaultMemberIds.has(memberId) && base?.defaultValue && !("serverValueId" in base.defaultValue)) {
@@ -50029,15 +50061,7 @@ function lowerExpressionValue(context, expression, declaredExpected, ownerClass,
50029
50061
  declaredExpected,
50030
50062
  genericEnvironment
50031
50063
  );
50032
- if (!isStoredDelegateLiteral(expected, expression) && initializerRequiresEvaluation(
50033
- context.declaredConstructors,
50034
- expression,
50035
- expected.name,
50036
- requiredConstructorParameterNames(
50037
- context.declaredConstructors,
50038
- ownerClass.name
50039
- )
50040
- )) {
50064
+ if (positionRequiresEvaluation(context, expression, expected, ownerClass)) {
50041
50065
  return null;
50042
50066
  }
50043
50067
  if (expression.kind === "annotated") {
@@ -50116,10 +50140,20 @@ function lowerExpressionValue(context, expression, declaredExpected, ownerClass,
50116
50140
  const valueType = expected.arguments[1] ?? expected;
50117
50141
  const value = {};
50118
50142
  for (const entry of expression.entries) {
50143
+ if (positionRequiresEvaluation(
50144
+ context,
50145
+ entry.key,
50146
+ DICTIONARY_KEY_TYPE,
50147
+ ownerClass
50148
+ )) {
50149
+ throw new Error(
50150
+ `Dictionary key at ${path} is a computed expression. Dictionary keys must be string literals.`
50151
+ );
50152
+ }
50119
50153
  const key = lowerExpressionValue(
50120
50154
  context,
50121
50155
  entry.key,
50122
- { name: "string", nullable: false, arguments: [] },
50156
+ DICTIONARY_KEY_TYPE,
50123
50157
  ownerClass,
50124
50158
  path,
50125
50159
  genericEnvironment,
@@ -50293,6 +50327,18 @@ function lowerExpressionValue(context, expression, declaredExpected, ownerClass,
50293
50327
  );
50294
50328
  }
50295
50329
  }
50330
+ function positionRequiresEvaluation(context, expression, expected, ownerClass) {
50331
+ if (isStoredDelegateLiteral(expected, expression)) return false;
50332
+ return initializerRequiresEvaluation(
50333
+ context.declaredConstructors,
50334
+ expression,
50335
+ expected.name,
50336
+ requiredConstructorParameterNames(
50337
+ context.declaredConstructors,
50338
+ ownerClass.name
50339
+ )
50340
+ );
50341
+ }
50296
50342
  function isStoredDelegateLiteral(type, expression) {
50297
50343
  if (type.name !== "NeoDelegate") return false;
50298
50344
  let current = expression;
@@ -50374,10 +50420,20 @@ function lowerStructuredLeafDefault(context, kind, expression, expectedType, own
50374
50420
  });
50375
50421
  }
50376
50422
  function requiredComponentNumber(context, expression, ownerClass, label, path) {
50423
+ if (positionRequiresEvaluation(
50424
+ context,
50425
+ expression,
50426
+ STRUCTURED_LEAF_COMPONENT_TYPE,
50427
+ ownerClass
50428
+ )) {
50429
+ throw new Error(
50430
+ `${label} has a computed component. Structured leaf components must be numeric literals.`
50431
+ );
50432
+ }
50377
50433
  const value = lowerExpressionValue(
50378
50434
  context,
50379
50435
  expression,
50380
- { name: "float", nullable: false, arguments: [] },
50436
+ STRUCTURED_LEAF_COMPONENT_TYPE,
50381
50437
  ownerClass,
50382
50438
  path
50383
50439
  );
@@ -50965,7 +51021,7 @@ function* zip(left, right) {
50965
51021
  yield [left[index], right[index]];
50966
51022
  }
50967
51023
  }
50968
- var UNSET_LIST_COLUMN_WIDTH, EMPTY_GENERIC_TYPE_ENVIRONMENT, primitiveMemberKinds;
51024
+ var UNSET_LIST_COLUMN_WIDTH, EMPTY_GENERIC_TYPE_ENVIRONMENT, DICTIONARY_KEY_TYPE, STRUCTURED_LEAF_COMPONENT_TYPE, primitiveMemberKinds;
50969
51025
  var init_lower_members = __esm({
50970
51026
  "src/project-source/lower-members.ts"() {
50971
51027
  "use strict";
@@ -50983,6 +51039,16 @@ var init_lower_members = __esm({
50983
51039
  init_key_reference_spelling();
50984
51040
  UNSET_LIST_COLUMN_WIDTH = -1;
50985
51041
  EMPTY_GENERIC_TYPE_ENVIRONMENT = /* @__PURE__ */ new Map();
51042
+ DICTIONARY_KEY_TYPE = {
51043
+ name: "string",
51044
+ nullable: false,
51045
+ arguments: []
51046
+ };
51047
+ STRUCTURED_LEAF_COMPONENT_TYPE = {
51048
+ name: "float",
51049
+ nullable: false,
51050
+ arguments: []
51051
+ };
50986
51052
  primitiveMemberKinds = /* @__PURE__ */ new Set([
50987
51053
  "null",
50988
51054
  "bool",
@@ -55239,9 +55305,14 @@ function buildEvaluatorBaseIndexes(members, values, valueById = new Map(
55239
55305
  parentLinksByChildId: /* @__PURE__ */ new Map(),
55240
55306
  indexedRowIds: /* @__PURE__ */ new Set(),
55241
55307
  indexedOverlayValues: /* @__PURE__ */ new Map(),
55308
+ indexedOverlayRowCount: 0,
55242
55309
  shadowedBaseRowIds: /* @__PURE__ */ new Set(),
55243
55310
  listIdentityByArray: /* @__PURE__ */ new WeakMap(),
55244
- declaredListByArray: /* @__PURE__ */ new WeakMap()
55311
+ declaredListByArray: /* @__PURE__ */ new WeakMap(),
55312
+ rowsBySourceValueId: /* @__PURE__ */ new Map(),
55313
+ ownershipRootIdsByRowId: /* @__PURE__ */ new Map(),
55314
+ ownedValueAttachmentsByValueId: /* @__PURE__ */ new Map(),
55315
+ memberByRowId: /* @__PURE__ */ new Map()
55245
55316
  };
55246
55317
  const membersByValueId = /* @__PURE__ */ new Map();
55247
55318
  for (const member of members) {
@@ -55260,7 +55331,8 @@ function buildEvaluatorBaseIndexes(members, values, valueById = new Map(
55260
55331
  memberByValueId: indexes.memberByValueId,
55261
55332
  membersByValueId,
55262
55333
  parentLinksByChildId: indexes.parentLinksByChildId,
55263
- indexedRowIds: indexes.indexedRowIds
55334
+ indexedRowIds: indexes.indexedRowIds,
55335
+ rowsBySourceValueId: indexes.rowsBySourceValueId
55264
55336
  };
55265
55337
  }
55266
55338
  function evaluatorIndexes(ctx) {
@@ -55280,10 +55352,16 @@ function evaluatorIndexes(ctx) {
55280
55352
  indexedRowIds: /* @__PURE__ */ new Set(),
55281
55353
  baseIndexedRowIds: base?.indexedRowIds,
55282
55354
  indexedOverlayValues: /* @__PURE__ */ new Map(),
55355
+ indexedOverlayRowCount: 0,
55283
55356
  shadowedBaseRowIds: /* @__PURE__ */ new Set(),
55284
55357
  overlayRevision,
55285
55358
  listIdentityByArray: liveListIndexes?.identity ?? /* @__PURE__ */ new WeakMap(),
55286
- declaredListByArray: liveListIndexes?.declared ?? /* @__PURE__ */ new WeakMap()
55359
+ declaredListByArray: liveListIndexes?.declared ?? /* @__PURE__ */ new WeakMap(),
55360
+ rowsBySourceValueId: /* @__PURE__ */ new Map(),
55361
+ baseRowsBySourceValueId: base?.rowsBySourceValueId,
55362
+ ownershipRootIdsByRowId: /* @__PURE__ */ new Map(),
55363
+ ownedValueAttachmentsByValueId: /* @__PURE__ */ new Map(),
55364
+ memberByRowId: /* @__PURE__ */ new Map()
55287
55365
  };
55288
55366
  if (base === void 0) {
55289
55367
  for (const member of ctx.vm.members) {
@@ -55308,19 +55386,46 @@ function evaluatorIndexes(ctx) {
55308
55386
  }
55309
55387
  function syncLazyOverlayReferences(indexes, overlay) {
55310
55388
  if (!(overlay instanceof LazyValueOverlay)) return;
55311
- for (const row of overlay.values()) {
55312
- if (indexes.indexedOverlayValues.get(row.id) === row.value) continue;
55389
+ const materializedRows = overlay.materializedRowsSince(
55390
+ indexes.indexedOverlayRowCount
55391
+ );
55392
+ for (const row of materializedRows) {
55313
55393
  indexes.indexedOverlayValues.set(row.id, row.value);
55314
55394
  if (typeof row.value === "object" && row.value !== null) {
55315
55395
  indexes.rowByValueReference.set(row.value, row);
55316
55396
  }
55317
55397
  }
55398
+ indexes.indexedOverlayRowCount += materializedRows.length;
55318
55399
  }
55319
55400
  function indexEvaluatorRow(indexes, row, allowBaseShadow = false) {
55401
+ indexes.ownedValueAttachmentsByValueId.delete(row.id);
55402
+ if (Array.isArray(row.value)) {
55403
+ for (const childId of row.value) {
55404
+ if (typeof childId === "string") {
55405
+ indexes.ownedValueAttachmentsByValueId.delete(childId);
55406
+ }
55407
+ }
55408
+ } else if (typeof row.value === "object" && row.value !== null) {
55409
+ for (const childId of Object.values(row.value)) {
55410
+ if (typeof childId === "string") {
55411
+ indexes.ownedValueAttachmentsByValueId.delete(childId);
55412
+ }
55413
+ }
55414
+ }
55415
+ indexes.ownershipRootIdsByRowId.clear();
55416
+ indexes.memberByRowId.clear();
55320
55417
  if (indexes.indexedRowIds.has(row.id) || !allowBaseShadow && indexes.baseIndexedRowIds?.has(row.id) === true) {
55321
55418
  return;
55322
55419
  }
55323
55420
  indexes.indexedRowIds.add(row.id);
55421
+ if (typeof row.sourceValueId === "string") {
55422
+ const rows = indexes.rowsBySourceValueId.get(row.sourceValueId) ?? [];
55423
+ rows.push(row);
55424
+ indexes.rowsBySourceValueId.set(row.sourceValueId, rows);
55425
+ }
55426
+ if (typeof row.containerId === "string") {
55427
+ addEvaluatorParentLink(indexes, row.id, row.containerId, "");
55428
+ }
55324
55429
  if (typeof row.value === "object" && row.value !== null) {
55325
55430
  indexes.rowByValueReference.set(row.value, row);
55326
55431
  }
@@ -55354,6 +55459,57 @@ function evaluatorParentLinks(indexes, childId) {
55354
55459
  if (local.length === 0) return base;
55355
55460
  return [...base, ...local];
55356
55461
  }
55462
+ function evaluatorRowsForSourceValueId(indexes, sourceValueId) {
55463
+ const base = (indexes.baseRowsBySourceValueId?.get(sourceValueId) ?? []).filter((row) => !indexes.shadowedBaseRowIds.has(row.id));
55464
+ const local = indexes.rowsBySourceValueId.get(sourceValueId) ?? [];
55465
+ if (base.length === 0) return local;
55466
+ if (local.length === 0) return base;
55467
+ return [...base, ...local];
55468
+ }
55469
+ function evaluatorOwnershipRootIds(rowId, indexes) {
55470
+ const cached = indexes.ownershipRootIdsByRowId.get(rowId);
55471
+ if (cached !== void 0) return cached;
55472
+ const roots = /* @__PURE__ */ new Set();
55473
+ const visiting = /* @__PURE__ */ new Set();
55474
+ const visit = (currentId) => {
55475
+ if (visiting.has(currentId)) return;
55476
+ visiting.add(currentId);
55477
+ const parents = evaluatorParentLinks(indexes, currentId);
55478
+ if (parents.length === 0) roots.add(currentId);
55479
+ else for (const parent of parents) visit(parent.parentId);
55480
+ visiting.delete(currentId);
55481
+ };
55482
+ visit(rowId);
55483
+ if (roots.size === 0) roots.add(rowId);
55484
+ indexes.ownershipRootIdsByRowId.set(rowId, roots);
55485
+ return roots;
55486
+ }
55487
+ function resolveRuntimeReferenceRow(sourceValueId, ctx) {
55488
+ const direct = evalValueById(
55489
+ ctx.vm,
55490
+ sourceValueId,
55491
+ ctx.__runtimeSessionValues,
55492
+ ctx.__valueOverlay
55493
+ );
55494
+ const receiver = trackedRowForValueReference(ctx.thisValue, ctx);
55495
+ if (receiver === null) return direct;
55496
+ const indexes = evaluatorIndexes(ctx);
55497
+ const receiverRoots = evaluatorOwnershipRootIds(receiver.id, indexes);
55498
+ const matches = evaluatorRowsForSourceValueId(indexes, sourceValueId).filter(
55499
+ (candidate) => {
55500
+ const candidateRoots = evaluatorOwnershipRootIds(candidate.id, indexes);
55501
+ for (const rootId of candidateRoots) {
55502
+ if (receiverRoots.has(rootId)) return true;
55503
+ }
55504
+ return false;
55505
+ }
55506
+ );
55507
+ if (matches.length === 0) return direct;
55508
+ if (matches.length === 1) return matches[0] ?? direct;
55509
+ throw new NSGetterRuntimeError(
55510
+ `Value reference '${sourceValueId}' is ambiguous within the constructed object graph.`
55511
+ );
55512
+ }
55357
55513
  function invalidateEvaluatorIndexes(ctx) {
55358
55514
  if (ctx.__valueOverlay instanceof LazyValueOverlay) {
55359
55515
  ctx.__valueOverlay.markMutated();
@@ -55564,6 +55720,8 @@ function withEvaluationRuntime(ctx, writes) {
55564
55720
  ownedValueAttachments: /* @__PURE__ */ new Map(),
55565
55721
  constructionStack: [],
55566
55722
  loopIterations: 0,
55723
+ budgetedConstructedRowIds: /* @__PURE__ */ new Set(),
55724
+ budgetedProducedEntriesByRowId: /* @__PURE__ */ new Map(),
55567
55725
  budget: createExecutionBudget(ctx.executionBudgetLimits)
55568
55726
  },
55569
55727
  __indexes: ctx.__executionState === void 0 || ctx.__valueOverlay === void 0 ? void 0 : ctx.__indexes
@@ -57211,12 +57369,7 @@ function evalPointer(pointer, scope, ctx) {
57211
57369
  return scope.get(pointer.variableId);
57212
57370
  }
57213
57371
  case "reference" /* reference */: {
57214
- const row = evalValueById(
57215
- ctx.vm,
57216
- pointer.valueId,
57217
- ctx.__runtimeSessionValues,
57218
- ctx.__valueOverlay
57219
- );
57372
+ const row = resolveRuntimeReferenceRow(pointer.valueId, ctx);
57220
57373
  if (!row) {
57221
57374
  throw new NSGetterRuntimeError(
57222
57375
  `Missing value reference: ${pointer.valueId}`
@@ -58432,7 +58585,16 @@ function resolveLocalizedRowValueForMember(row, member, ctx) {
58432
58585
  if (member.localizable === false) return value;
58433
58586
  return resolveLocalizedTextId(value, ctx);
58434
58587
  }
58435
- function memberForValueRow(row, ctx, visited = /* @__PURE__ */ new Set()) {
58588
+ function memberForValueRow(row, ctx) {
58589
+ const indexes = evaluatorIndexes(ctx);
58590
+ if (indexes.memberByRowId.has(row.id)) {
58591
+ return indexes.memberByRowId.get(row.id) ?? null;
58592
+ }
58593
+ const member = resolveMemberForValueRow(row, ctx, /* @__PURE__ */ new Set());
58594
+ indexes.memberByRowId.set(row.id, member);
58595
+ return member;
58596
+ }
58597
+ function resolveMemberForValueRow(row, ctx, visited) {
58436
58598
  if (visited.has(row.id)) return null;
58437
58599
  visited.add(row.id);
58438
58600
  const indexes = evaluatorIndexes(ctx);
@@ -58452,7 +58614,11 @@ function memberForValueRow(row, ctx, visited = /* @__PURE__ */ new Set()) {
58452
58614
  ctx.__valueOverlay
58453
58615
  );
58454
58616
  if (parent === null) continue;
58455
- const parentMember = memberForValueRow(parent, ctx, new Set(visited));
58617
+ const parentMember = resolveMemberForValueRow(
58618
+ parent,
58619
+ ctx,
58620
+ new Set(visited)
58621
+ );
58456
58622
  if (parentMember !== null && isMemberList(parentMember)) {
58457
58623
  return evalMemberById(ctx.vm, parentMember.entryMemberId);
58458
58624
  }
@@ -59482,23 +59648,29 @@ function publishConstructedRows(args) {
59482
59648
  "Class construction requires an effect-capable evaluator Session scope."
59483
59649
  );
59484
59650
  }
59651
+ let newlyConstructedRows = 0;
59652
+ let newlyProducedEntries = 0;
59653
+ for (const row of createdValues) {
59654
+ if (!state.budgetedConstructedRowIds.has(row.id)) {
59655
+ state.budgetedConstructedRowIds.add(row.id);
59656
+ newlyConstructedRows += 1;
59657
+ }
59658
+ const currentEntries = Array.isArray(row.value) ? row.value.length : typeof row.value === "object" && row.value !== null ? Object.keys(row.value).length : 0;
59659
+ const priorEntries = state.budgetedProducedEntriesByRowId.get(row.id) ?? 0;
59660
+ if (currentEntries <= priorEntries) continue;
59661
+ newlyProducedEntries += currentEntries - priorEntries;
59662
+ state.budgetedProducedEntriesByRowId.set(row.id, currentEntries);
59663
+ }
59485
59664
  consumeBudget(
59486
59665
  ctx,
59487
59666
  "constructedSessionRows",
59488
- createdValues.length,
59667
+ newlyConstructedRows,
59489
59668
  "constructed Session row"
59490
59669
  );
59491
- let producedEntries = 0;
59492
- for (const row of createdValues) {
59493
- if (Array.isArray(row.value)) producedEntries += row.value.length;
59494
- else if (typeof row.value === "object" && row.value !== null) {
59495
- producedEntries += Object.keys(row.value).length;
59496
- }
59497
- }
59498
59670
  consumeBudget(
59499
59671
  ctx,
59500
59672
  "producedCollectionEntries",
59501
- producedEntries,
59673
+ newlyProducedEntries,
59502
59674
  "produced collection entry"
59503
59675
  );
59504
59676
  const retained = retainedIds;
@@ -60186,15 +60358,16 @@ function evaluateInitializerInContext(init, member, ctx, createdValues, argument
60186
60358
  }
60187
60359
  if (rootRow === null) {
60188
60360
  const existingValueRow = trackedRowForValueReference(result.value, ctx);
60361
+ const existingRuntimeClassId = existingValueRow === null ? null : classIdForValueRow(existingValueRow, ctx) ?? null;
60189
60362
  const existingConstructorRootId = existingValueRow === null ? null : constructorGroupForRow(existingValueRow.id, ctx);
60190
60363
  if (existingValueRow !== null && existingConstructorRootId === existingValueRow.id) {
60191
60364
  return {
60192
60365
  value: result.value,
60193
- classId: existingValueRow.classId ?? null,
60366
+ classId: existingRuntimeClassId,
60194
60367
  existingValueRow
60195
60368
  };
60196
60369
  }
60197
- return { value: result.value, classId: null };
60370
+ return { value: result.value, classId: existingRuntimeClassId };
60198
60371
  }
60199
60372
  ctx.__runtimeSessionValues?.delete(rootRow.id);
60200
60373
  ctx.__executionState?.constructorGroups.delete(rootRow.id);
@@ -60537,6 +60710,9 @@ function assertOwnedValueAttachable(valueId, destination, destinationName2, ctx,
60537
60710
  }
60538
60711
  }
60539
60712
  function currentOwnedValueAttachments(valueId, ctx) {
60713
+ const indexes = evaluatorIndexes(ctx);
60714
+ const cached = indexes.ownedValueAttachmentsByValueId.get(valueId);
60715
+ if (cached !== void 0) return [...cached];
60540
60716
  const found = /* @__PURE__ */ new Map();
60541
60717
  const add = (attachment) => {
60542
60718
  found.set(attachment.identity, attachment);
@@ -60575,9 +60751,7 @@ function currentOwnedValueAttachments(valueId, ctx) {
60575
60751
  }
60576
60752
  }
60577
60753
  const parentIds = new Set(
60578
- evaluatorParentLinks(evaluatorIndexes(ctx), valueId).map(
60579
- (link) => link.parentId
60580
- )
60754
+ evaluatorParentLinks(indexes, valueId).map((link) => link.parentId)
60581
60755
  );
60582
60756
  for (const parentId of parentIds) {
60583
60757
  const parent = evalValueById(
@@ -60588,21 +60762,25 @@ function currentOwnedValueAttachments(valueId, ctx) {
60588
60762
  );
60589
60763
  if (parent === null) continue;
60590
60764
  if (parent.id === valueId) continue;
60591
- let member = activeStaticBindingRoot(parent.id, ctx)?.member ?? memberForValueRow(parent, ctx);
60592
- if (member !== null) {
60593
- try {
60594
- member = resolveMember2(member, ctx.vm.members);
60595
- if (parent.genericBindings !== void 0) {
60596
- member = substituteMember(
60597
- member,
60598
- envFromStamp(parent.genericBindings),
60599
- ctx.vm.members
60600
- );
60765
+ let member = null;
60766
+ let classId = parent.classId;
60767
+ if (classId === void 0) {
60768
+ member = activeStaticBindingRoot(parent.id, ctx)?.member ?? memberForValueRow(parent, ctx);
60769
+ if (member !== null) {
60770
+ try {
60771
+ member = resolveMember2(member, ctx.vm.members);
60772
+ if (parent.genericBindings !== void 0) {
60773
+ member = substituteMember(
60774
+ member,
60775
+ envFromStamp(parent.genericBindings),
60776
+ ctx.vm.members
60777
+ );
60778
+ }
60779
+ } catch {
60601
60780
  }
60602
- } catch {
60603
60781
  }
60782
+ classId = member !== null && isMemberClassBase(member) ? member.classId : void 0;
60604
60783
  }
60605
- const classId = parent.classId ?? (member !== null && isMemberClassBase(member) ? member.classId : void 0);
60606
60784
  if (classId !== void 0 && typeof parent.value === "object" && parent.value !== null && !Array.isArray(parent.value)) {
60607
60785
  for (const [key, childId] of Object.entries(parent.value)) {
60608
60786
  if (childId !== valueId) continue;
@@ -60633,7 +60811,9 @@ function currentOwnedValueAttachments(valueId, ctx) {
60633
60811
  }
60634
60812
  }
60635
60813
  }
60636
- return [...found.values()];
60814
+ const attachments = [...found.values()];
60815
+ indexes.ownedValueAttachmentsByValueId.set(valueId, attachments);
60816
+ return attachments;
60637
60817
  }
60638
60818
  function constructorArgumentStorage(valueId, ctx, visiting = /* @__PURE__ */ new Set()) {
60639
60819
  if (ctx.__runtimeSessionValues?.has(valueId)) return "session" /* Session */;
@@ -61143,13 +61323,15 @@ function cloneClassValueGraph(receiver, expectedClassId, ctx) {
61143
61323
  `Class.Clone source value '${sourceId3}' could not be read.`
61144
61324
  );
61145
61325
  }
61146
- const runtimeClassId = classIdForValueRow(source, ctx);
61147
- if (runtimeClassId === void 0) {
61326
+ const resolvedRuntimeClassId = classIdForValueRow(source, ctx);
61327
+ const runtimeClassId = resolvedRuntimeClassId ?? expectedClassId;
61328
+ if (runtimeClassId !== expectedClassId && !resolveInheritanceChain(runtimeClassId, ctx.vm.classes).some(
61329
+ (candidate) => candidate.id === expectedClassId
61330
+ )) {
61148
61331
  throw new NSGetterRuntimeError(
61149
- `Class.Clone source value '${sourceId3}' has no Class runtime type.`
61332
+ `Class.Clone source value '${sourceId3}' has runtime Class '${runtimeClassId}', which is not assignable to '${expectedClassId}'.`
61150
61333
  );
61151
61334
  }
61152
- void expectedClassId;
61153
61335
  const destination = ctx.__runtimeSessionValues ?? (() => {
61154
61336
  throw new NSGetterRuntimeError(
61155
61337
  "Class.Clone requires an evaluator Session value registry."
@@ -61272,7 +61454,10 @@ function cloneClassValueGraph(receiver, expectedClassId, ctx) {
61272
61454
  return clone;
61273
61455
  };
61274
61456
  const rootMember = memberForValueRow(source, ctx);
61275
- return cloneRow(source, rootMember).value;
61457
+ const typedSource = source.classId === void 0 ? { ...source, classId: runtimeClassId } : source;
61458
+ const clonedRoot = cloneRow(typedSource, rootMember);
61459
+ clonedRoot.classId ??= runtimeClassId;
61460
+ return clonedRoot.value;
61276
61461
  }
61277
61462
  function ownedObjectChildMember(row, sourceMember, key, ctx) {
61278
61463
  if (sourceMember !== null && isMemberDictionary(sourceMember)) {
@@ -61486,7 +61671,7 @@ var init_evaluateNSGetter = __esm({
61486
61671
  workUnits: 1e5,
61487
61672
  collectionVisits: 1e5,
61488
61673
  producedCollectionEntries: 1e4,
61489
- constructedSessionRows: 1e3,
61674
+ constructedSessionRows: 4096,
61490
61675
  producedStringCharacters: 1024 * 1024
61491
61676
  });
61492
61677
  liveListIndexesByProject = /* @__PURE__ */ new WeakMap();
@@ -61499,6 +61684,7 @@ var init_evaluateNSGetter = __esm({
61499
61684
  }
61500
61685
  vm;
61501
61686
  revision = 0;
61687
+ materializedRows = [];
61502
61688
  sourceIndexes;
61503
61689
  sourceValueByClone = /* @__PURE__ */ new WeakMap();
61504
61690
  get(id2) {
@@ -61514,8 +61700,12 @@ var init_evaluateNSGetter = __esm({
61514
61700
  this.sourceValueByClone.set(value, source.value);
61515
61701
  }
61516
61702
  super.set(id2, clone);
61703
+ this.materializedRows.push(clone);
61517
61704
  return clone;
61518
61705
  }
61706
+ materializedRowsSince(index) {
61707
+ return this.materializedRows.slice(index);
61708
+ }
61519
61709
  cloneForValueReference(value) {
61520
61710
  const source = this.ensureSourceIndexes().byReference.get(value);
61521
61711
  return source === void 0 ? void 0 : this.get(source.id)?.value;
@@ -63210,7 +63400,11 @@ function readProjectDocument(value, options = {}) {
63210
63400
  isInternalRecordRelation
63211
63401
  ),
63212
63402
  values: readArrayField(value, "values", isMemberValue),
63213
- enums: readArrayField(value, "enums", isEnum),
63403
+ enums: readArrayField(
63404
+ value,
63405
+ "enums",
63406
+ options.identities === "prospective" ? isProspectiveEnum : isEnum
63407
+ ),
63214
63408
  interfaces: readArrayField(value, "interfaces", isNeoInterface),
63215
63409
  dialogues: readArrayField(value, "dialogues", isDialogue),
63216
63410
  dialogueRecords: readOptionalArrayField(
@@ -69928,7 +70122,9 @@ function materializedInitializerReconciliationFailuresV4(args) {
69928
70122
  [...args.reconciliations.values()].map((entry) => [entry.valueId, entry])
69929
70123
  );
69930
70124
  const candidateValueIds = new Set(
69931
- [...reconciliationByValueId.values()].filter((reconciliation) => reconciliation.storedConstructorArgs !== null).map((reconciliation) => reconciliation.valueId)
70125
+ [...reconciliationByValueId.values()].filter(
70126
+ (reconciliation) => reconciliation.storedConstructorArgs !== null || reconciliation.requiresCanonicalConstruction === true
70127
+ ).map((reconciliation) => reconciliation.valueId)
69932
70128
  );
69933
70129
  const candidateOwners = resolveOwnerMembersForValues(
69934
70130
  document,
@@ -69968,13 +70164,24 @@ function materializedInitializerReconciliationFailuresV4(args) {
69968
70164
  }
69969
70165
  }
69970
70166
  }
70167
+ const authoredConstructionChangedByValueId = /* @__PURE__ */ new Map();
69971
70168
  const replayableValueIds = /* @__PURE__ */ new Set();
69972
70169
  for (const valueId of candidateValueIds) {
69973
70170
  const reconciliation = reconciliationByValueId.get(valueId);
69974
70171
  const owner = candidateOwners.get(valueId);
69975
70172
  if (reconciliation === void 0 || owner === void 0) continue;
69976
- const authoredConstructionChanged = args.manifest !== void 0 && canonicalExpressions.get(valueId) !== constructorExpressionSlice(reconciliation.code);
69977
- if (authoredConstructionChanged || recompileTargets.valueIds.has(valueId)) {
70173
+ const canonicalExpression = canonicalExpressions.get(valueId);
70174
+ if (args.manifest !== void 0 && canonicalExpression === void 0) {
70175
+ throw new Error(
70176
+ `Cannot reconcile stored construction for value "${valueId}": its canonical pulled constructor expression is unavailable.`
70177
+ );
70178
+ }
70179
+ const authoredConstructionChanged = args.manifest !== void 0 && canonicalExpression !== constructorExpressionSlice(reconciliation.code);
70180
+ authoredConstructionChangedByValueId.set(
70181
+ valueId,
70182
+ authoredConstructionChanged
70183
+ );
70184
+ if (reconciliation.storedConstructorArgs !== null && (authoredConstructionChanged || recompileTargets.valueIds.has(valueId))) {
69978
70185
  replayableValueIds.add(valueId);
69979
70186
  }
69980
70187
  }
@@ -69982,7 +70189,26 @@ function materializedInitializerReconciliationFailuresV4(args) {
69982
70189
  const compilationProject = replayableValueIds.size === 0 ? void 0 : args.useBuildCaches === false ? createNeoScriptCompilationProject(document) : loadOrBuildNeoScriptProjectV1(args.workspace.root, document);
69983
70190
  const failures = [];
69984
70191
  for (const reconciliation of args.reconciliations.values()) {
69985
- if (reconciliation.storedConstructorArgs === null) continue;
70192
+ if (reconciliation.storedConstructorArgs === null) {
70193
+ if (reconciliation.requiresCanonicalConstruction !== true) continue;
70194
+ if (authoredConstructionChangedByValueId.get(reconciliation.valueId) !== true) {
70195
+ continue;
70196
+ }
70197
+ const position2 = composeReferenceSitePosition(
70198
+ reconciliation.site,
70199
+ args.sourceTextByUri
70200
+ );
70201
+ failures.push(
70202
+ new SchemaSourceError(
70203
+ `construction-arguments-conflict: Value "${reconciliation.valueId}" is already materialized without durable constructor arguments, and this construction no longer matches its pulled canonical projection. Delete and recreate the value to change its construction.`,
70204
+ position2.file,
70205
+ position2.line,
70206
+ position2.column,
70207
+ "construction-arguments-conflict"
70208
+ )
70209
+ );
70210
+ continue;
70211
+ }
69986
70212
  if (!replayableValueIds.has(reconciliation.valueId)) continue;
69987
70213
  const owner = owners.get(reconciliation.valueId);
69988
70214
  if (owner === void 0) {
@@ -70525,7 +70751,12 @@ function computeWorkspaceStatus(workspace, options) {
70525
70751
  seeds: /* @__PURE__ */ new Map()
70526
70752
  };
70527
70753
  let projectAnalysisV4 = null;
70528
- const valueLowerRegistry = createValueLowerRegistryV4();
70754
+ const valueLowerRegistry = createValueLowerRegistryV4({
70755
+ materializedConstructorExpressions: options.useBuildCaches === false ? /* @__PURE__ */ new Map() : readMaterializedConstructionBuildCacheV1(
70756
+ workspace.root,
70757
+ workspace.state
70758
+ ) ?? /* @__PURE__ */ new Map()
70759
+ });
70529
70760
  try {
70530
70761
  if (!baseManifest) {
70531
70762
  throw new Error(
@@ -71593,6 +71824,7 @@ var init_workspace_status_core = __esm({
71593
71824
  init_push_change_intent();
71594
71825
  init_initializer_replay();
71595
71826
  init_compiler_adapter();
71827
+ init_materialized_construction_cache();
71596
71828
  IGNORED_SCHEMA_DIRECTORIES = /* @__PURE__ */ new Set([
71597
71829
  ".git",
71598
71830
  ".neo",
@@ -81675,13 +81907,55 @@ function compilePulledMemberInitializerBodyV4(document, memberId, compilationPro
81675
81907
  });
81676
81908
  }
81677
81909
  function readPulledProjectDocumentV4(records2) {
81678
- return readProjectDocument(
81679
- // Replay compilation mutates authored `init` envelopes with transient IR.
81680
- // Keep that evaluator-local: the input records are also the source commit
81681
- // payload, where compiled bodies are deliberately absent.
81682
- structuredClone(pulledProjectDocumentRaw(replayWorkspace(records2))),
81683
- { constructors: "authored-or-compiled" }
81910
+ const raw = structuredClone(
81911
+ pulledProjectDocumentRaw(replayWorkspace(records2))
81912
+ );
81913
+ return readProjectDocument(completeProspectiveLocalizedTexts(raw), {
81914
+ constructors: "authored-or-compiled",
81915
+ identities: "prospective"
81916
+ });
81917
+ }
81918
+ function completeProspectiveLocalizedTexts(raw) {
81919
+ const localizedTexts = raw.localizedTexts;
81920
+ if (!Array.isArray(localizedTexts)) return raw;
81921
+ const incomplete = localizedTexts.find((text) => !isLocalizedText(text));
81922
+ if (incomplete === void 0) return raw;
81923
+ const config = localizedTextEnvelopeConfig(
81924
+ raw.localizationConfig,
81925
+ prospectiveLocalizedTextId(incomplete)
81684
81926
  );
81927
+ return {
81928
+ ...raw,
81929
+ localizedTexts: localizedTexts.map(
81930
+ (text) => isLocalizedText(text) ? text : completeLocalizedTextCreateEnvelope(text, config)
81931
+ )
81932
+ };
81933
+ }
81934
+ function localizedTextEnvelopeConfig(value, textId) {
81935
+ if (!isObjectRecord2(value)) {
81936
+ throw new Error(
81937
+ `Localized text "${textId}" needs the server create envelope, but the project carries no localization config record to mint it from.`
81938
+ );
81939
+ }
81940
+ if (typeof value.mainLocale !== "string") {
81941
+ throw new Error(
81942
+ `Localized text "${textId}" needs the server create envelope, but the project localization config declares no main locale.`
81943
+ );
81944
+ }
81945
+ if (typeof value.mainLocaleDefaultStatusId !== "string") {
81946
+ throw new Error(
81947
+ `Localized text "${textId}" needs the server create envelope, but the project localization config declares no main-locale default status.`
81948
+ );
81949
+ }
81950
+ return {
81951
+ mainLocale: value.mainLocale,
81952
+ mainLocaleDefaultStatusId: value.mainLocaleDefaultStatusId
81953
+ };
81954
+ }
81955
+ function prospectiveLocalizedTextId(value) {
81956
+ if (!isObjectRecord2(value)) return "<non-object>";
81957
+ if (typeof value.id !== "string") return "<unidentified>";
81958
+ return value.id;
81685
81959
  }
81686
81960
  function replayWorkspace(records2) {
81687
81961
  const stateRecords = {};
@@ -81720,6 +81994,7 @@ var init_initializer_replay = __esm({
81720
81994
  init_value_row_owner_members();
81721
81995
  init_inheritance();
81722
81996
  init_project_document_read();
81997
+ init_localization2();
81723
81998
  init_server_preparation_preflight();
81724
81999
  init_projection();
81725
82000
  init_constructors2();
@@ -81868,11 +82143,20 @@ function emitStoredConstructorExpression(context, member, valueId) {
81868
82143
  value,
81869
82144
  instanceGenericEnvironment(context, classId, resolvedMember, void 0)
81870
82145
  );
81871
- const targetValueIds = animationConstructorProjectionTargetValueIds(
82146
+ if (!isObjectRecord2(value.value)) {
82147
+ throw new Error(
82148
+ `Stored materialized value ${valueId} has no Class body to project.`
82149
+ );
82150
+ }
82151
+ const hasStoredConstruction = isObjectRecord2(value.constructorArgs);
82152
+ const projection = hasStoredConstruction ? null : constructorProjectionSource(
81872
82153
  context,
81873
82154
  classId,
81874
- value
82155
+ value.value,
82156
+ /* @__PURE__ */ new Set([valueId]),
82157
+ storedEnvironment
81875
82158
  );
82159
+ const targetValueIds = projection?.targetValueIds ?? animationConstructorProjectionTargetValueIds(context, classId, value);
81876
82160
  const environment = inferAnimationChildOverrideEmitEnvironment(
81877
82161
  context,
81878
82162
  classId,
@@ -81894,12 +82178,13 @@ function emitStoredConstructorExpression(context, member, valueId) {
81894
82178
  /* @__PURE__ */ new Set([valueId]),
81895
82179
  false
81896
82180
  );
81897
- if (constructor2 === null) {
82181
+ if (constructor2 !== null) return constructor2;
82182
+ if (projection === null || projection.arguments.length === 0 && schemaClass2.requiredConstructorId === void 0) {
81898
82183
  throw new Error(
81899
82184
  `Stored materialized value ${valueId} has no constructor arguments.`
81900
82185
  );
81901
82186
  }
81902
- return constructor2;
82187
+ return projection.arguments.length === 0 ? `new ${className}()` : constructorCallSource(`new ${className}`, projection.arguments);
81903
82188
  }
81904
82189
  function animationConstructorProjectionTargetValueIds(context, classId, value) {
81905
82190
  if (context.manifestClasses.get(classId)?.system?.worldKind !== "animationChildOverride" || !isObjectRecord2(value.value)) {
@@ -82040,8 +82325,9 @@ function qualifiedProjectFileSymbolsV4(records2) {
82040
82325
  }
82041
82326
  return result;
82042
82327
  }
82043
- function createValueLowerRegistryV4() {
82328
+ function createValueLowerRegistryV4(options = {}) {
82044
82329
  return {
82330
+ materializedConstructorExpressions: options.materializedConstructorExpressions ?? /* @__PURE__ */ new Map(),
82045
82331
  pendingValues: /* @__PURE__ */ new Map(),
82046
82332
  pendingLocalizedTexts: /* @__PURE__ */ new Map(),
82047
82333
  pendingBindingMembersByClassId: /* @__PURE__ */ new Map(),
@@ -82145,7 +82431,8 @@ function buildValueLowerContext(state, manifest, options = {}) {
82145
82431
  referenceObligations: registry.referenceObligations,
82146
82432
  loweringFailures: registry.loweringFailures,
82147
82433
  pendingValueIdentitySites: registry.pendingValueIdentitySites,
82148
- initializerReconciliations: registry.initializerReconciliations
82434
+ initializerReconciliations: registry.initializerReconciliations,
82435
+ materializedConstructorExpressions: registry.materializedConstructorExpressions
82149
82436
  };
82150
82437
  }
82151
82438
  function indexMemberOverrides(members) {
@@ -82792,6 +83079,13 @@ function constructorArgumentValueSlices(authoredSlice) {
82792
83079
  );
82793
83080
  });
82794
83081
  }
83082
+ function unattachedAuthoredRowId(authoredSlice) {
83083
+ if (authoredSlice === void 0) return null;
83084
+ const match = LEADING_AUTHORED_ROW_ID.exec(
83085
+ normalizeInitializerSource(authoredSlice)
83086
+ );
83087
+ return match?.[1] ?? null;
83088
+ }
82795
83089
  function initializerExpressionSlice(authoredSlice) {
82796
83090
  if (authoredSlice === void 0) return void 0;
82797
83091
  let text = normalizeInitializerSource(authoredSlice);
@@ -83162,6 +83456,12 @@ function lowerSeedRow(context, member, sourceExpression, source, valueId, path,
83162
83456
  memberClassName(context, resolvedMember),
83163
83457
  bindingRuntimeIdentifiers(context, source)
83164
83458
  )) {
83459
+ const unattachedId = annotated.id === null ? unattachedAuthoredRowId(authoredSlice) : null;
83460
+ if (unattachedId !== null) {
83461
+ throw new Error(
83462
+ `Value ${path} in ${source.label} writes @id(${JSON.stringify(unattachedId)}) on part of a computed expression rather than on the row. Parenthesize the expression so the annotation names the whole row.`
83463
+ );
83464
+ }
83165
83465
  if (annotated.id === null && isPendingId(valueId) && !context.pendingValueIdentitySites.has(valueId)) {
83166
83466
  context.pendingValueIdentitySites.set(valueId, {
83167
83467
  id: valueId,
@@ -83590,20 +83890,28 @@ function lowerValueRow(context, member, sourceExpression, expectedValueId, sourc
83590
83890
  }
83591
83891
  indexInitAuthoredRowIds(context, code, source.label);
83592
83892
  const storedConstructorArgs = isObjectRecord2(base.constructorArgs) ? base.constructorArgs : null;
83593
- const materializedPlainClass = resolvedMember.kind === "class" && isObjectRecord2(base.value) && context.classes.get(
83893
+ const materializedClass = resolvedMember.kind === "class" && isObjectRecord2(base.value);
83894
+ const materializedPlainClass = materializedClass && context.classes.get(
83594
83895
  typeof base.classId === "string" ? base.classId : resolvedMember.classId
83595
83896
  )?.requiredConstructorId === void 0;
83596
- if (storedConstructorArgs !== null || materializedPlainClass) {
83897
+ const requiresCanonicalConstruction = storedConstructorArgs === null && materializedClass && !materializedPlainClass;
83898
+ const canonicalConstruction = context.materializedConstructorExpressions.get(expectedValueId);
83899
+ const authoredConstruction = constructorExpressionSlice(code);
83900
+ const canonicalConstructionMatches = canonicalConstruction !== void 0 && canonicalConstruction === authoredConstruction;
83901
+ const canonicalConstructionMissing = requiresCanonicalConstruction && canonicalConstruction === void 0;
83902
+ const preserveRequiredStoredConstruction = requiresCanonicalConstruction && source.purpose === "stored";
83903
+ if (storedConstructorArgs !== null || materializedPlainClass || canonicalConstructionMatches || canonicalConstructionMissing || preserveRequiredStoredConstruction) {
83597
83904
  if (resolvedMember.kind !== "class") {
83598
83905
  throw new Error(
83599
83906
  `Value ${expectedValueId} stores constructor arguments but its declared member ${resolvedMember.name} is not a Class.`
83600
83907
  );
83601
83908
  }
83602
- if (source.purpose === "stored") {
83909
+ if (source.purpose === "stored" || canonicalConstructionMissing) {
83603
83910
  context.initializerReconciliations.set(expectedValueId, {
83604
83911
  valueId: expectedValueId,
83605
83912
  code,
83606
83913
  storedConstructorArgs: storedConstructorArgs === null ? null : structuredClone(storedConstructorArgs),
83914
+ ...requiresCanonicalConstruction ? { requiresCanonicalConstruction: true } : {},
83607
83915
  settleFields: expression.kind === "new" ? (expression.initializer ?? []).map(
83608
83916
  (assignment) => assignment.name
83609
83917
  ) : [],
@@ -85781,9 +86089,6 @@ function classValue(context, member, value, visited, targetTyped, outerEnvironme
85781
86089
  { cause: error }
85782
86090
  );
85783
86091
  }
85784
- if (storedConstruction !== null && typeof value.id === "string") {
85785
- context.materializedConstructors.set(value.id, storedConstruction);
85786
- }
85787
86092
  const replayConstruction = storedConstruction === null ? null : storedConstructorCallSource(
85788
86093
  context,
85789
86094
  schemaClass2,
@@ -85862,6 +86167,9 @@ ${fields.map((field) => indentNeoSourceNonEmptyLines(field, 2)).join(",\n")}
85862
86167
  }`;
85863
86168
  }
85864
86169
  const constructor2 = storedConstruction !== null ? storedConstruction : projection.arguments.length > 0 ? constructorCallSource(`new ${name}`, projection.arguments) : targetTyped ? "new()" : `new ${name}()`;
86170
+ if (typeof value.id === "string" && (storedConstruction !== null || projection.arguments.length > 0 || schemaClass2.requiredConstructorId !== void 0)) {
86171
+ context.materializedConstructors.set(value.id, constructor2);
86172
+ }
85865
86173
  return fields.length === 0 ? constructor2 : `${storedConstruction !== null || projection.arguments.length > 0 ? constructor2 : targetTyped ? "new()" : `new ${name}`} {
85866
86174
  ${fields.map((field) => indentNeoSourceNonEmptyLines(field, 2)).join(",\n")}
85867
86175
  }`;
@@ -86697,16 +87005,22 @@ function instanceGenericEnvironment(context, classId, member, outerEnvironment)
86697
87005
  const bindings = classGenericBindings(context.manifestClasses, classId);
86698
87006
  const memberId = stringField3(member, "id");
86699
87007
  const manifestMember = context.manifestMembers.get(memberId);
86700
- if (manifestMember?.kind === "class") {
86701
- for (const [genericParamId, argument2] of Object.entries(
86702
- manifestMember.classArguments
87008
+ const rawClassArguments = isObjectRecord2(member.classArguments) ? member.classArguments : null;
87009
+ const classArguments2 = rawClassArguments ?? (manifestMember?.kind === "class" ? manifestMember.classArguments : null);
87010
+ if (classArguments2 !== null) {
87011
+ for (const [genericParamId, rawArgument] of Object.entries(
87012
+ classArguments2
86703
87013
  )) {
87014
+ const argument2 = isObjectRecord2(rawArgument) ? rawArgument : null;
86704
87015
  if (bindings.get(genericParamId)?.kind === "member") continue;
86705
- if (argument2.kind === "member") {
86706
- bindings.set(genericParamId, argument2);
87016
+ if (argument2?.kind === "member" && typeof argument2.memberId === "string") {
87017
+ bindings.set(genericParamId, {
87018
+ kind: "member",
87019
+ memberId: argument2.memberId
87020
+ });
86707
87021
  continue;
86708
87022
  }
86709
- const outerMemberId = outerEnvironment?.get(argument2.genericParamId);
87023
+ const outerMemberId = argument2?.kind === "generic" && typeof argument2.genericParamId === "string" ? outerEnvironment?.get(argument2.genericParamId) : void 0;
86710
87024
  if (outerMemberId !== void 0) {
86711
87025
  bindings.set(genericParamId, {
86712
87026
  kind: "member",
@@ -87169,7 +87483,7 @@ function memberDefaultBody(member) {
87169
87483
  if (!("value" in defaultValue)) return null;
87170
87484
  return defaultValue.value;
87171
87485
  }
87172
- var INFERRED_GENERIC_CLASS_PREFIX, MEMBER_KIND_DICTIONARY, MEMBER_KIND_LIST, MEMBER_KIND_CLASS, CANONICAL_CONSTRUCTOR_INLINE_WIDTH;
87486
+ var INFERRED_GENERIC_CLASS_PREFIX, MEMBER_KIND_DICTIONARY, MEMBER_KIND_LIST, MEMBER_KIND_CLASS, LEADING_AUTHORED_ROW_ID, CANONICAL_CONSTRUCTOR_INLINE_WIDTH;
87173
87487
  var init_value_sources = __esm({
87174
87488
  "src/project-source/value-sources.ts"() {
87175
87489
  "use strict";
@@ -87196,6 +87510,7 @@ var init_value_sources = __esm({
87196
87510
  MEMBER_KIND_DICTIONARY = 5;
87197
87511
  MEMBER_KIND_LIST = 6;
87198
87512
  MEMBER_KIND_CLASS = 7;
87513
+ LEADING_AUTHORED_ROW_ID = /^@id\(\s*"((?:[^"\\]|\\.)*)"\s*\)/;
87199
87514
  CANONICAL_CONSTRUCTOR_INLINE_WIDTH = 88;
87200
87515
  }
87201
87516
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neocompose/cli",
3
- "version": "0.22.6",
3
+ "version": "0.23.0",
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.22.6 -->
12
+ <!-- reviewed-through-cli: 0.23.0 -->
13
13
 
14
14
  # Neo Compose CLI
15
15
 
@@ -78,7 +78,7 @@ wrappers.
78
78
  The marker near the top of `SKILL.md` must exactly match the package version:
79
79
 
80
80
  ```html
81
- <!-- reviewed-through-cli: 0.22.6 -->
81
+ <!-- reviewed-through-cli: 0.23.0 -->
82
82
  ```
83
83
 
84
84
  The quoted version above is checked too, so this instruction cannot go stale
@@ -108,6 +108,14 @@ construction rather than stored as a closed literal. It runs for every newly
108
108
  constructed instance in push, web, and SDK contexts; it does not rewrite
109
109
  existing saved instances.
110
110
 
111
+ Operator expressions are valid member defaults and store as computed
112
+ initializers, the same way calls do — `public float Derived =
113
+ Flags.Start / Flags.Scale;` is fine. Positions that store no rows still
114
+ require literals: dictionary keys must be string literals and Vector/Color
115
+ components must be numeric literals. When a computed list or dictionary entry
116
+ carries an authored `@id`, parenthesize the expression — `@id("row")
117
+ (A.B / A.C)` — so the annotation names the row rather than its left operand.
118
+
111
119
  Declare overloadable constructors as members when each constructor body owns
112
120
  its parameter scope:
113
121