@neocompose/cli 0.5.1 → 0.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/neo.mjs +400 -41
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.5.3] - 2026-07-19
4
+
5
+ ### Fixed
6
+
7
+ - Emit row-backed instance-member defaults (class fields, ordered lists,
8
+ dictionaries) as inline constructed source instead of opaque `Reference(id)`
9
+ calls and raw value-row ID strings. Also resolve sprite/audio `@settings`
10
+ template references to their template symbol, file defaults to
11
+ `Images.X`/`AudioClips.X` registry symbols, and scalar enum default option
12
+ IDs to contextual `.Option` literals; resolve inherited class fields when
13
+ emitting nested value graphs; and stop indenting blank separator lines in
14
+ multi-line value emission.
15
+
16
+ ## [0.5.2] - 2026-07-18
17
+
18
+ ### Fixed
19
+
20
+ - Stop `neo pull` from emitting inherited `@system` annotations on member
21
+ overrides and abstract implementations whose own records are not system
22
+ protected.
23
+
3
24
  ## [0.5.1] - 2026-07-18
4
25
 
5
26
  ### Fixed
package/dist/neo.mjs CHANGED
@@ -1513,7 +1513,9 @@ function memberFromDocument(record3, members, owners, classNames, context) {
1513
1513
  required: booleanValue(field("required", false), record3, "required"),
1514
1514
  defaultValue,
1515
1515
  overrideOf,
1516
- system: systemFromDocument(field("system", null), record3, "system"),
1516
+ // System protection belongs to the declaration record. An override is
1517
+ // user-authored unless its own document explicitly marks it as system.
1518
+ system: systemFromDocument(data.system, record3, "system"),
1517
1519
  storage: normalizedStorage(field("storage", null), record3),
1518
1520
  storageKey: normalizedStorageKey(field("storageKey", null), record3)
1519
1521
  };
@@ -26943,7 +26945,8 @@ function lowerFieldMember(context, ownerClass, declaration, id2, commonInput, ba
26943
26945
  declaration.initializer,
26944
26946
  declaration.type,
26945
26947
  ownerClass,
26946
- base
26948
+ base,
26949
+ id2
26947
26950
  )
26948
26951
  };
26949
26952
  const settings = annotation(declaration.annotations, "settings");
@@ -26992,7 +26995,7 @@ function lowerFieldMember(context, ownerClass, declaration, id2, commonInput, ba
26992
26995
  return {
26993
26996
  ...common,
26994
26997
  kind: primitive3,
26995
- templateId: referenceIdArgument(settings, "template") ?? (base?.kind === primitive3 ? base.templateId : null)
26998
+ templateId: referenceIdArgument(settings, "template") ?? templateSymbolArgument(context, settings, ownerClass, declaration) ?? (base?.kind === primitive3 ? base.templateId : null)
26996
26999
  };
26997
27000
  }
26998
27001
  if (primitive3) return { ...common, kind: primitive3 };
@@ -27259,10 +27262,13 @@ function lowerType(context, type, ownerClass) {
27259
27262
  if (type.name === "object") return { kind: "unknown", nullable };
27260
27263
  throw new Error(`Unknown Neo type ${JSON.stringify(type.name)}.`);
27261
27264
  }
27262
- function lowerDefault(context, initializer, type, ownerClass, base) {
27265
+ function lowerDefault(context, initializer, type, ownerClass, base, memberId) {
27263
27266
  if (initializer === null) {
27264
27267
  return base?.defaultValue && "serverValueId" in base.defaultValue ? base.defaultValue : null;
27265
27268
  }
27269
+ if (context.rowBackedDefaultMemberIds.has(memberId) && base?.defaultValue && !("serverValueId" in base.defaultValue)) {
27270
+ return base.defaultValue;
27271
+ }
27266
27272
  const expression = parseExpression(initializer);
27267
27273
  let value = lowerExpressionValue(context, expression, type, ownerClass);
27268
27274
  if (value !== null && context.enumIdsByName.has(type.name) && !Array.isArray(value)) {
@@ -27399,16 +27405,25 @@ function lowerExpressionValue(context, expression, expected, ownerClass) {
27399
27405
  const idArgument = expression.args[index >= 0 ? index : 0];
27400
27406
  if (idArgument?.kind === "litString") return idArgument.value;
27401
27407
  }
27402
- if (expression.callee.kind === "member" && expression.callee.name === "Slice" && expression.callee.receiver.kind === "call") {
27403
- const receiver = expression.callee.receiver;
27404
- const index = receiver.argumentNames?.findIndex((name) => name === "id") ?? -1;
27405
- const fileId = receiver.args[index >= 0 ? index : 0];
27408
+ if (expression.callee.kind === "member" && expression.callee.name === "Slice") {
27406
27409
  const sliceIndex = expression.args[0];
27407
- if (fileId?.kind === "litString" && (sliceIndex?.kind === "litInt" || sliceIndex === void 0)) {
27408
- return {
27409
- fileId: fileId.value,
27410
- sliceIndex: sliceIndex?.value ?? 0
27411
- };
27410
+ if (sliceIndex !== void 0 && sliceIndex.kind !== "litInt") {
27411
+ throw new Error("Sprite Slice defaults require an int literal.");
27412
+ }
27413
+ const receiver = expression.callee.receiver;
27414
+ if (receiver.kind === "call") {
27415
+ const index = receiver.argumentNames?.findIndex((name) => name === "id") ?? -1;
27416
+ const fileId2 = receiver.args[index >= 0 ? index : 0];
27417
+ if (fileId2?.kind === "litString") {
27418
+ return {
27419
+ fileId: fileId2.value,
27420
+ sliceIndex: sliceIndex?.value ?? 0
27421
+ };
27422
+ }
27423
+ }
27424
+ const fileId = projectFileIdForPath(context, receiver);
27425
+ if (fileId !== null) {
27426
+ return { fileId, sliceIndex: sliceIndex?.value ?? 0 };
27412
27427
  }
27413
27428
  }
27414
27429
  throw new Error(
@@ -27416,6 +27431,8 @@ function lowerExpressionValue(context, expression, expected, ownerClass) {
27416
27431
  );
27417
27432
  }
27418
27433
  case "member": {
27434
+ const fileId = projectFileIdForPath(context, expression);
27435
+ if (fileId !== null) return { fileId };
27419
27436
  if (expression.name === "Slice" || expression.receiver.kind === "call") {
27420
27437
  throw new Error(
27421
27438
  "File defaults are lowered by the project-file compiler."
@@ -27429,6 +27446,17 @@ function lowerExpressionValue(context, expression, expected, ownerClass) {
27429
27446
  );
27430
27447
  }
27431
27448
  }
27449
+ function projectFileIdForPath(context, expression) {
27450
+ const path = expressionMemberPath(expression);
27451
+ if (path === null) return null;
27452
+ return context.projectFileIdsBySymbol.get(path) ?? null;
27453
+ }
27454
+ function expressionMemberPath(expression) {
27455
+ if (expression.kind === "ident") return expression.name;
27456
+ if (expression.kind !== "member") return null;
27457
+ const receiver = expressionMemberPath(expression.receiver);
27458
+ return receiver === null ? null : `${receiver}.${expression.name}`;
27459
+ }
27432
27460
  function manifestTypeForMember(context, member) {
27433
27461
  const nullable = !member.required;
27434
27462
  if (primitiveMemberKinds.has(member.kind)) {
@@ -27645,6 +27673,19 @@ function referenceIdArgument(value, name) {
27645
27673
  const raw = argument(value, name);
27646
27674
  return raw === null ? null : referenceId(raw);
27647
27675
  }
27676
+ function templateSymbolArgument(context, settings, ownerClass, declaration) {
27677
+ const raw = argument(settings, "template");
27678
+ if (raw === null) return null;
27679
+ const expression = parseAnnotationExpression(raw);
27680
+ if (expression.kind !== "ident") return null;
27681
+ const templateId = context.templateIdsByName.get(expression.name);
27682
+ if (templateId === void 0) {
27683
+ throw new Error(
27684
+ `Member ${ownerClass.name}.${declaration.name} references unknown template ${JSON.stringify(expression.name)}.`
27685
+ );
27686
+ }
27687
+ return templateId;
27688
+ }
27648
27689
  function referenceId(raw) {
27649
27690
  try {
27650
27691
  const expression = parseExpression(raw);
@@ -28175,7 +28216,24 @@ function lowerProjectSchemaV4(base, analysis, options = {}) {
28175
28216
  enumOptionsByName,
28176
28217
  loweredMembers: /* @__PURE__ */ new Map(),
28177
28218
  placements: /* @__PURE__ */ new Map(),
28178
- staticValueIdsBySymbol: options.staticValueIdsBySymbol ?? /* @__PURE__ */ new Map()
28219
+ staticValueIdsBySymbol: options.staticValueIdsBySymbol ?? /* @__PURE__ */ new Map(),
28220
+ templateIdsByName: new Map(
28221
+ analysis.configuration.globals.flatMap(
28222
+ (global) => global.type.name === "TextureTemplate" ? [
28223
+ [
28224
+ global.name,
28225
+ materializedId(global, "texture-template", global.name)
28226
+ ]
28227
+ ] : global.type.name === "AudioTemplate" ? [
28228
+ [
28229
+ global.name,
28230
+ materializedId(global, "audio-template", global.name)
28231
+ ]
28232
+ ] : []
28233
+ )
28234
+ ),
28235
+ projectFileIdsBySymbol: options.projectFileIdsBySymbol ?? /* @__PURE__ */ new Map(),
28236
+ rowBackedDefaultMemberIds: options.rowBackedDefaultMemberIds ?? /* @__PURE__ */ new Set()
28179
28237
  };
28180
28238
  const classes = analysis.schema.classes.map(
28181
28239
  (declaration) => lowerClass(context, declaration)
@@ -28324,7 +28382,14 @@ function emitProjectSourcesV4(manifest, options = {}) {
28324
28382
  interfaces: new Map(manifest.interfaces.map((entry) => [entry.id, entry])),
28325
28383
  enums: new Map(manifest.enums.map((entry) => [entry.id, entry])),
28326
28384
  members: new Map(manifest.members.map((entry) => [entry.id, entry])),
28327
- staticInitializers: options.staticInitializers ?? /* @__PURE__ */ new Map()
28385
+ staticInitializers: options.staticInitializers ?? /* @__PURE__ */ new Map(),
28386
+ defaultInitializers: options.defaultInitializers ?? /* @__PURE__ */ new Map(),
28387
+ fileSymbols: options.fileSymbols ?? /* @__PURE__ */ new Map(),
28388
+ templateNames: new Map(
28389
+ [...manifest.textureTemplates, ...manifest.audioTemplates].map(
28390
+ (template) => [template.id, template.name]
28391
+ )
28392
+ )
28328
28393
  };
28329
28394
  const files = [];
28330
28395
  const localizationStatusNames = new Map(
@@ -28659,7 +28724,7 @@ ${indentNeoSourceNonEmptyLines(tracked.trim(), 2)}
28659
28724
  ${body ? `${signature} ${body}` : abstractContract ? `${signature};` : `native ${signature};`}`;
28660
28725
  }
28661
28726
  const type = renderMemberType(context, member);
28662
- const initializer = (member.isStatic ? context.staticInitializers.get(member.id) : void 0) ?? renderDefault(context, member);
28727
+ const initializer = (member.isStatic ? context.staticInitializers.get(member.id) : void 0) ?? context.defaultInitializers.get(member.id) ?? renderDefault(context, member);
28663
28728
  return `${annotations.join("\n")}
28664
28729
  ${prefix}${type} ${member.name}${initializer === null ? ";" : ` = ${initializer};`}`;
28665
28730
  }
@@ -28720,7 +28785,10 @@ function memberSettings(context, member) {
28720
28785
  }
28721
28786
  if (member.multiselect) settings.push("multiselect: true");
28722
28787
  } else if ((member.kind === "sprite" || member.kind === "audio") && member.templateId) {
28723
- settings.push(`template: Reference<Type>(id: ${quote(member.templateId)})`);
28788
+ const templateName = context.templateNames.get(member.templateId);
28789
+ settings.push(
28790
+ templateName !== void 0 ? `template: ${templateName}` : `template: Reference<Type>(id: ${quote(member.templateId)})`
28791
+ );
28724
28792
  }
28725
28793
  return settings.length ? [`@settings(${settings.join(", ")})`] : [];
28726
28794
  }
@@ -28898,6 +28966,13 @@ function renderDefaultValue(context, member, wrapper) {
28898
28966
  );
28899
28967
  return member.multiselect ? `[${values.join(", ")}]` : values[0] ?? "null";
28900
28968
  }
28969
+ if (member.kind === "enum" && typeof value === "string") {
28970
+ const schemaEnum = required(context.enums, member.enumId, "enum");
28971
+ const option = schemaEnum.options.find(
28972
+ (candidate) => candidate.id === value || candidate.key === value
28973
+ );
28974
+ if (option !== void 0) return `.${option.name}`;
28975
+ }
28901
28976
  if (member.kind === "list" && Array.isArray(value)) {
28902
28977
  const entry = required(context.members, member.entryMemberId, "list entry");
28903
28978
  return `[${value.map((item) => renderNestedValue(context, entry, item)).join(", ")}]`;
@@ -28929,7 +29004,9 @@ function renderDefaultValue(context, member, wrapper) {
28929
29004
  return member.multiselect ? `[${refs.join(", ")}]` : refs[0] ?? "null";
28930
29005
  }
28931
29006
  if ((member.kind === "sprite" || member.kind === "audio") && isObject(value) && typeof value.fileId === "string") {
28932
- return member.kind === "sprite" ? `Reference<NeoImage>(id: ${quote(value.fileId)}).Slice(${typeof value.sliceIndex === "number" ? value.sliceIndex : 0})` : `Reference<NeoAudioClip>(id: ${quote(value.fileId)})`;
29007
+ const symbol = context.fileSymbols.get(value.fileId);
29008
+ const target = symbol ?? (member.kind === "sprite" ? `Reference<NeoImage>(id: ${quote(value.fileId)})` : `Reference<NeoAudioClip>(id: ${quote(value.fileId)})`);
29009
+ return member.kind === "sprite" ? `${target}.Slice(${typeof value.sliceIndex === "number" ? value.sliceIndex : 0})` : target;
28933
29010
  }
28934
29011
  if (isObject(value) && ["vector2", "vector2Int", "vector3", "vector3Int", "color"].includes(
28935
29012
  member.kind
@@ -38744,7 +38821,7 @@ function emitStaticValueSourcesV4(records2) {
38744
38821
  );
38745
38822
  return emitStoredValueBindingSourcesV4(records2, memberIds);
38746
38823
  }
38747
- function emitStoredValueBindingSourcesV4(records2, memberIds, options = {}) {
38824
+ function buildValueEmitContext(records2) {
38748
38825
  const members = dataById(records2, "member");
38749
38826
  const classes = dataById(records2, "class");
38750
38827
  const values = dataById(records2, "value");
@@ -38752,7 +38829,7 @@ function emitStoredValueBindingSourcesV4(records2, memberIds, options = {}) {
38752
38829
  const localizedTexts = dataById(records2, "localized-text");
38753
38830
  const dialogues = dataById(records2, "dialogue");
38754
38831
  const symbolsByValueId = staticSymbols(classes, members);
38755
- const context = {
38832
+ return {
38756
38833
  members,
38757
38834
  classes,
38758
38835
  values,
@@ -38769,6 +38846,10 @@ function emitStoredValueBindingSourcesV4(records2, memberIds, options = {}) {
38769
38846
  referencedValueIds: externallyReferencedValueIds(records2),
38770
38847
  localizedTextIds: /* @__PURE__ */ new Set()
38771
38848
  };
38849
+ }
38850
+ function emitStoredValueBindingSourcesV4(records2, memberIds, options = {}) {
38851
+ const context = buildValueEmitContext(records2);
38852
+ const members = context.members;
38772
38853
  const initializers = /* @__PURE__ */ new Map();
38773
38854
  const recordKeysByMember = /* @__PURE__ */ new Map();
38774
38855
  for (const memberId of memberIds) {
@@ -38798,6 +38879,102 @@ function emitStoredValueBindingSourcesV4(records2, memberIds, options = {}) {
38798
38879
  }
38799
38880
  return { initializers, recordKeysByMember };
38800
38881
  }
38882
+ function rowBackedDefaultBody(member, isPulledValueRow) {
38883
+ if (member.isStatic === true) return null;
38884
+ const defaultValue = member.defaultValue;
38885
+ if (!isObjectRecord2(defaultValue)) return null;
38886
+ const body = defaultValue.value;
38887
+ const referencesRows = (children) => children.length > 0 && children.every(
38888
+ (child) => typeof child === "string" && isPulledValueRow(child)
38889
+ );
38890
+ if (member.kind === MEMBER_KIND_CLASS && isObjectRecord2(body)) {
38891
+ return referencesRows(Object.values(body)) ? { kind: "class", body } : null;
38892
+ }
38893
+ if (member.kind === MEMBER_KIND_LIST && member.listKind !== "unordered" && Array.isArray(body)) {
38894
+ const rows = body.filter(
38895
+ (child) => typeof child === "string" && isPulledValueRow(child)
38896
+ );
38897
+ return rows.length > 0 && rows.length === body.length ? { kind: "list", body: rows } : null;
38898
+ }
38899
+ if (member.kind === MEMBER_KIND_DICTIONARY && isObjectRecord2(body)) {
38900
+ return referencesRows(Object.values(body)) ? { kind: "dictionary", body } : null;
38901
+ }
38902
+ return null;
38903
+ }
38904
+ function rowBackedDefaultMemberIdsV4(state) {
38905
+ const values = /* @__PURE__ */ new Set();
38906
+ for (const record3 of Object.values(state)) {
38907
+ if (record3.recordKind === "value") values.add(record3.recordId);
38908
+ }
38909
+ const result = /* @__PURE__ */ new Set();
38910
+ for (const record3 of Object.values(state)) {
38911
+ if (record3.recordKind !== "member" || !isObjectRecord2(record3.data)) {
38912
+ continue;
38913
+ }
38914
+ if (rowBackedDefaultBody(record3.data, (id2) => values.has(id2)) !== null) {
38915
+ result.add(record3.recordId);
38916
+ }
38917
+ }
38918
+ return result;
38919
+ }
38920
+ function emitMemberDefaultSourcesV4(records2) {
38921
+ const context = buildValueEmitContext(records2);
38922
+ const initializers = /* @__PURE__ */ new Map();
38923
+ const recordKeysByMember = /* @__PURE__ */ new Map();
38924
+ for (const record3 of records2.values()) {
38925
+ if (record3.deleted || record3.recordKind !== "member" || !isObjectRecord2(record3.data)) {
38926
+ continue;
38927
+ }
38928
+ const member = record3.data;
38929
+ const backed = rowBackedDefaultBody(member, (id2) => context.values.has(id2));
38930
+ if (backed === null) continue;
38931
+ const visited = /* @__PURE__ */ new Set();
38932
+ const defaultValue = isObjectRecord2(member.defaultValue) ? member.defaultValue : {};
38933
+ let expression;
38934
+ if (backed.kind === "class") {
38935
+ expression = classValue(
38936
+ context,
38937
+ member,
38938
+ { classId: defaultValue.classId ?? null, value: backed.body },
38939
+ visited,
38940
+ false
38941
+ );
38942
+ } else if (backed.kind === "list") {
38943
+ expression = listValue(context, member, { value: backed.body }, visited);
38944
+ } else {
38945
+ expression = dictionaryValue(context, member, backed.body, visited);
38946
+ }
38947
+ initializers.set(record3.recordId, expression);
38948
+ recordKeysByMember.set(
38949
+ record3.recordId,
38950
+ /* @__PURE__ */ new Set([
38951
+ ...[...visited].map((id2) => `value:${id2}`),
38952
+ ...[...context.localizedTextIds].map((id2) => `localized-text:${id2}`)
38953
+ ])
38954
+ );
38955
+ context.localizedTextIds.clear();
38956
+ }
38957
+ return { initializers, recordKeysByMember };
38958
+ }
38959
+ function qualifiedProjectFileSymbolsV4(records2) {
38960
+ const symbols = projectFileSymbols(records2);
38961
+ const result = /* @__PURE__ */ new Map();
38962
+ for (const record3 of records2.values()) {
38963
+ if (record3.deleted || record3.recordKind !== "project-file" || !isObjectRecord2(record3.data)) {
38964
+ continue;
38965
+ }
38966
+ const kind = record3.data.fileType;
38967
+ const symbol = symbols.get(record3.recordId);
38968
+ if (symbol === void 0 || kind !== "image" && kind !== "audio") {
38969
+ continue;
38970
+ }
38971
+ result.set(
38972
+ record3.recordId,
38973
+ `${kind === "image" ? "Images" : "AudioClips"}.${symbol}`
38974
+ );
38975
+ }
38976
+ return result;
38977
+ }
38801
38978
  function lowerStaticValueSourcesV4(state, analysis, manifest) {
38802
38979
  const bindings = [];
38803
38980
  for (const sourceClass of analysis.schema.classes) {
@@ -38820,7 +38997,7 @@ function lowerStaticValueSourcesV4(state, analysis, manifest) {
38820
38997
  }
38821
38998
  return lowerStoredValueBindingsV4(state, manifest, bindings, { analysis });
38822
38999
  }
38823
- function lowerStoredValueBindingsV4(state, manifest, bindings, options = {}) {
39000
+ function buildValueLowerContext(state, manifest, options = {}) {
38824
39001
  const members = new Map(
38825
39002
  manifest.members.map((member) => [member.id, member])
38826
39003
  );
@@ -38834,7 +39011,7 @@ function lowerStoredValueBindingsV4(state, manifest, bindings, options = {}) {
38834
39011
  manifest,
38835
39012
  parsedInitializers
38836
39013
  );
38837
- const context = {
39014
+ return {
38838
39015
  state,
38839
39016
  members,
38840
39017
  classes,
@@ -38858,6 +39035,9 @@ function lowerStoredValueBindingsV4(state, manifest, bindings, options = {}) {
38858
39035
  parsedInitializers,
38859
39036
  reconstructed: /* @__PURE__ */ new Map()
38860
39037
  };
39038
+ }
39039
+ function lowerStoredValueBindingsV4(state, manifest, bindings, options = {}) {
39040
+ const context = buildValueLowerContext(state, manifest, options);
38861
39041
  const memberValueIds = /* @__PURE__ */ new Map();
38862
39042
  const seeds = /* @__PURE__ */ new Map();
38863
39043
  for (const binding of bindings) {
@@ -38869,6 +39049,143 @@ function lowerStoredValueBindingsV4(state, manifest, bindings, options = {}) {
38869
39049
  seeds
38870
39050
  };
38871
39051
  }
39052
+ function lowerMemberDefaultSourcesV4(state, analysis, manifest) {
39053
+ const context = buildValueLowerContext(state, manifest, { analysis });
39054
+ const pulledValueIds = /* @__PURE__ */ new Set();
39055
+ for (const record3 of Object.values(state)) {
39056
+ if (record3.recordKind === "value") pulledValueIds.add(record3.recordId);
39057
+ }
39058
+ const memberDefaultValues = /* @__PURE__ */ new Map();
39059
+ for (const sourceClass of analysis.schema.classes) {
39060
+ for (const declaration of sourceClass.members) {
39061
+ if (declaration.kind !== "field" || declaration.initializer === null) {
39062
+ continue;
39063
+ }
39064
+ if (declaration.modifiers.includes("static")) continue;
39065
+ const label = `${sourceClass.name}.${declaration.name}`;
39066
+ const memberId = sourceIdentityId(declaration, "member", label);
39067
+ const baseData3 = stateData(context, "member", memberId);
39068
+ if (baseData3 === null) continue;
39069
+ const backed = rowBackedDefaultBody(
39070
+ baseData3,
39071
+ (id2) => pulledValueIds.has(id2)
39072
+ );
39073
+ if (backed === null) continue;
39074
+ const member = context.members.get(memberId);
39075
+ if (member === void 0) continue;
39076
+ const binding = {
39077
+ memberId,
39078
+ initializer: declaration.initializer,
39079
+ source: declaration.source,
39080
+ label
39081
+ };
39082
+ memberDefaultValues.set(
39083
+ memberId,
39084
+ lowerRowBackedDefault(context, member, backed, baseData3, binding)
39085
+ );
39086
+ }
39087
+ }
39088
+ return {
39089
+ records: [...context.reconstructed.values()],
39090
+ memberDefaultValues
39091
+ };
39092
+ }
39093
+ function lowerRowBackedDefault(context, member, backed, baseData3, binding) {
39094
+ const baseDefault = isObjectRecord2(baseData3.defaultValue) ? baseData3.defaultValue : {};
39095
+ const storedClassId = stringOrNull(baseDefault.classId);
39096
+ const expression = annotatedValue(
39097
+ parseCachedInitializer(context.parsedInitializers, binding.initializer)
39098
+ ).expression;
39099
+ if (member.kind === "class" && backed.kind === "class") {
39100
+ if (expression.kind !== "new") {
39101
+ throw new Error(`Default value ${binding.label} requires new(...).`);
39102
+ }
39103
+ const currentClassId = storedClassId ?? member.classId;
39104
+ const classId = expression.className === null ? currentClassId : requiredClassByName(context, expression.className).id;
39105
+ const baseBody = backed.body;
39106
+ const body = {};
39107
+ for (const assignment of expression.initializer ?? []) {
39108
+ const childMember = classMemberByName(context, classId, assignment.name);
39109
+ const existingId = isObjectRecord2(baseBody) ? baseBody[assignment.name] : void 0;
39110
+ const childId = typeof existingId === "string" ? existingId : annotatedValue(assignment.value).id;
39111
+ if (childId === null || childId === void 0) {
39112
+ throw new Error(
39113
+ `Default value ${binding.label}.${assignment.name} has no existing owned value row. Creating new default rows from source is not implemented by this slice.`
39114
+ );
39115
+ }
39116
+ body[assignment.name] = lowerValueRow(
39117
+ context,
39118
+ childMember,
39119
+ assignment.value,
39120
+ childId,
39121
+ binding
39122
+ );
39123
+ }
39124
+ return {
39125
+ value: body,
39126
+ classId: classId === currentClassId ? storedClassId : classId
39127
+ };
39128
+ }
39129
+ if (member.kind === "list" && backed.kind === "list") {
39130
+ if (expression.kind !== "litList") {
39131
+ throw new Error(`Default value ${binding.label} requires [...].`);
39132
+ }
39133
+ const entryMember = requiredMember(context, member.entryMemberId);
39134
+ const existingIds = new Set(backed.body);
39135
+ const ids = expression.elements.map((element, index) => {
39136
+ const symbolId = sourceValueSymbol(context, element);
39137
+ if (symbolId !== null) {
39138
+ if (!existingIds.has(symbolId)) {
39139
+ throw new Error(
39140
+ `Default list ${binding.label} adds value ${symbolId}. Changing default value placement is not implemented by this slice.`
39141
+ );
39142
+ }
39143
+ return symbolId;
39144
+ }
39145
+ const itemId = annotatedValue(element).id;
39146
+ if (itemId === null) {
39147
+ throw new Error(
39148
+ `Default list ${binding.label}[${index}] has no @id. Creating new default rows from source is not implemented by this slice.`
39149
+ );
39150
+ }
39151
+ return lowerValueRow(context, entryMember, element, itemId, binding);
39152
+ });
39153
+ return { value: ids, classId: storedClassId };
39154
+ }
39155
+ if (member.kind === "dictionary" && backed.kind === "dictionary") {
39156
+ if (expression.kind !== "litDict") {
39157
+ throw new Error(`Default value ${binding.label} requires {...}.`);
39158
+ }
39159
+ const entryMember = requiredMember(context, member.entryMemberId);
39160
+ const baseBody = backed.body;
39161
+ const body = {};
39162
+ for (const entry of expression.entries) {
39163
+ if (entry.key.kind !== "litString") {
39164
+ throw new Error(
39165
+ `Default dictionary ${binding.label} requires string keys.`
39166
+ );
39167
+ }
39168
+ const existingId = isObjectRecord2(baseBody) ? baseBody[entry.key.value] : void 0;
39169
+ const entryId = typeof existingId === "string" ? existingId : annotatedValue(entry.value).id;
39170
+ if (entryId === null || entryId === void 0) {
39171
+ throw new Error(
39172
+ `Default dictionary ${binding.label}[${JSON.stringify(entry.key.value)}] has no existing owned value row. Creating new default rows from source is not implemented by this slice.`
39173
+ );
39174
+ }
39175
+ body[entry.key.value] = lowerValueRow(
39176
+ context,
39177
+ entryMember,
39178
+ entry.value,
39179
+ entryId,
39180
+ binding
39181
+ );
39182
+ }
39183
+ return { value: body, classId: storedClassId };
39184
+ }
39185
+ throw new Error(
39186
+ `Default value ${binding.label} does not match its persisted ${member.kind} container shape.`
39187
+ );
39188
+ }
38872
39189
  function lowerStoredBinding(context, binding, memberValueIds, seeds) {
38873
39190
  const member = context.members.get(binding.memberId);
38874
39191
  if (member === void 0) {
@@ -39143,18 +39460,7 @@ function lowerClassValue(context, member, expression, base, source) {
39143
39460
  const baseBody = isObjectRecord2(base.value) ? base.value : {};
39144
39461
  const body = { ...baseBody };
39145
39462
  for (const assignment of expression.initializer ?? []) {
39146
- const childMemberId = schemaClass2.schema[assignment.name];
39147
- if (typeof childMemberId !== "string") {
39148
- throw new Error(
39149
- `Class ${schemaClass2.name} has no stored member ${assignment.name}.`
39150
- );
39151
- }
39152
- const childMember = context.members.get(childMemberId);
39153
- if (childMember === void 0) {
39154
- throw new Error(
39155
- `Class ${schemaClass2.name}.${assignment.name} is missing its member record.`
39156
- );
39157
- }
39463
+ const childMember = classMemberByName(context, classId, assignment.name);
39158
39464
  const childId = baseBody[assignment.name];
39159
39465
  if (typeof childId !== "string") {
39160
39466
  throw new Error(
@@ -39999,7 +40305,7 @@ function classValue(context, member, value, visited, targetTyped) {
39999
40305
  ...Object.keys(value.value).filter((key2) => !order.includes(key2))
40000
40306
  ]) {
40001
40307
  const childId = value.value[key];
40002
- const childMemberId = schema[key];
40308
+ const childMemberId = schema[key] ?? inheritedSchemaMemberId(context, schemaClass2, key);
40003
40309
  if (typeof childId !== "string" || typeof childMemberId !== "string")
40004
40310
  continue;
40005
40311
  const childMember = context.members.get(childMemberId);
@@ -40013,9 +40319,23 @@ function classValue(context, member, value, visited, targetTyped) {
40013
40319
  );
40014
40320
  }
40015
40321
  return fields.length === 0 ? targetTyped ? "new()" : `new ${name}()` : `${targetTyped ? "new()" : `new ${name}`} {
40016
- ${fields.map((field) => indentNeoSource(field, 2)).join(",\n")}
40322
+ ${fields.map((field) => indentNeoSourceNonEmptyLines(field, 2)).join(",\n")}
40017
40323
  }`;
40018
40324
  }
40325
+ function inheritedSchemaMemberId(context, schemaClass2, key) {
40326
+ const visited = /* @__PURE__ */ new Set();
40327
+ let current = typeof schemaClass2.extendsClassId === "string" ? schemaClass2.extendsClassId : void 0;
40328
+ while (current !== void 0 && !visited.has(current)) {
40329
+ visited.add(current);
40330
+ const parent = context.classes.get(current);
40331
+ if (parent === void 0) return void 0;
40332
+ const schema = isObjectRecord2(parent.schema) ? parent.schema : {};
40333
+ const memberId = schema[key];
40334
+ if (typeof memberId === "string") return memberId;
40335
+ current = typeof parent.extendsClassId === "string" ? parent.extendsClassId : void 0;
40336
+ }
40337
+ return void 0;
40338
+ }
40019
40339
  function listValue(context, member, value, visited) {
40020
40340
  if (value.value === null) return "null";
40021
40341
  const entryMember = context.members.get(stringField2(member, "entryMemberId"));
@@ -40027,7 +40347,7 @@ function listValue(context, member, value, visited) {
40027
40347
  if (ids.length === 0) return "[]";
40028
40348
  return `[
40029
40349
  ${ids.map(
40030
- (id2) => indentNeoSource(
40350
+ (id2) => indentNeoSourceNonEmptyLines(
40031
40351
  emitValue(context, entryMember, id2, {
40032
40352
  exposeIdentity: context.symbolsByValueId.get(id2) === void 0,
40033
40353
  visited
@@ -40050,7 +40370,7 @@ function dictionaryValue(context, member, body, visited) {
40050
40370
  return `{
40051
40371
  ${entries.flatMap(
40052
40372
  ([key, id2]) => typeof id2 === "string" ? [
40053
- indentNeoSource(
40373
+ indentNeoSourceNonEmptyLines(
40054
40374
  `${quote4(key)}: ${emitValue(context, entryMember, id2, {
40055
40375
  exposeIdentity: !context.symbolsByValueId.has(id2) && context.referencedValueIds.has(id2),
40056
40376
  visited
@@ -40241,6 +40561,7 @@ function numberOr2(value, fallback) {
40241
40561
  function quote4(value) {
40242
40562
  return quoteNeoString(value);
40243
40563
  }
40564
+ var MEMBER_KIND_DICTIONARY, MEMBER_KIND_LIST, MEMBER_KIND_CLASS;
40244
40565
  var init_value_sources = __esm({
40245
40566
  "src/project-source/value-sources.ts"() {
40246
40567
  "use strict";
@@ -40249,6 +40570,9 @@ var init_value_sources = __esm({
40249
40570
  init_project_files();
40250
40571
  init_lower_members();
40251
40572
  init_source_format();
40573
+ MEMBER_KIND_DICTIONARY = 5;
40574
+ MEMBER_KIND_LIST = 6;
40575
+ MEMBER_KIND_CLASS = 7;
40252
40576
  }
40253
40577
  });
40254
40578
 
@@ -40431,8 +40755,11 @@ function emitProjectDocumentFilesV4(records2) {
40431
40755
  }
40432
40756
  const manifest = documentsToProjectSchemaManifest(schemaRecords);
40433
40757
  const staticValues = emitStaticValueSourcesV4(records2);
40758
+ const memberDefaults = emitMemberDefaultSourcesV4(records2);
40434
40759
  const baseSource = emitProjectSourcesV4(manifest, {
40435
40760
  staticInitializers: staticValues.initializers,
40761
+ defaultInitializers: memberDefaults.initializers,
40762
+ fileSymbols: qualifiedProjectFileSymbolsV4(records2),
40436
40763
  relationEndpointExpressions: relationEndpointExpressions(records2)
40437
40764
  });
40438
40765
  const source = {
@@ -40440,7 +40767,11 @@ function emitProjectDocumentFilesV4(records2) {
40440
40767
  files: baseSource.files.map((file) => {
40441
40768
  const ownedKeys = file.recordKeys.flatMap((key) => {
40442
40769
  if (!key.startsWith("member:")) return [];
40443
- return [...staticValues.recordKeysByMember.get(key.slice(7)) ?? []];
40770
+ const memberId = key.slice(7);
40771
+ return [
40772
+ ...staticValues.recordKeysByMember.get(memberId) ?? [],
40773
+ ...memberDefaults.recordKeysByMember.get(memberId) ?? []
40774
+ ];
40444
40775
  });
40445
40776
  return ownedKeys.length === 0 ? file : { ...file, recordKeys: [...file.recordKeys, ...ownedKeys] };
40446
40777
  })
@@ -42732,6 +43063,10 @@ function computeWorkspaceStatus(workspace, options = {}) {
42732
43063
  let manifest;
42733
43064
  let staticValueSeeds = /* @__PURE__ */ new Map();
42734
43065
  let staticMemberValueIds = /* @__PURE__ */ new Map();
43066
+ let memberDefaults = {
43067
+ records: [],
43068
+ memberDefaultValues: /* @__PURE__ */ new Map()
43069
+ };
42735
43070
  let projectAnalysisV4 = null;
42736
43071
  try {
42737
43072
  if (!baseManifest) {
@@ -42795,8 +43130,26 @@ function computeWorkspaceStatus(workspace, options = {}) {
42795
43130
  };
42796
43131
  }
42797
43132
  manifest = lowerProjectSchemaV4(baseManifest, analysis, {
42798
- staticValueIdsBySymbol: staticValueIdsBySymbol2(workspace.state.records)
43133
+ staticValueIdsBySymbol: staticValueIdsBySymbol2(workspace.state.records),
43134
+ projectFileIdsBySymbol: projectFileIdsBySymbol(workspace.state.records),
43135
+ rowBackedDefaultMemberIds: rowBackedDefaultMemberIdsV4(
43136
+ workspace.state.records
43137
+ )
42799
43138
  });
43139
+ memberDefaults = lowerMemberDefaultSourcesV4(
43140
+ workspace.state.records,
43141
+ analysis,
43142
+ manifest
43143
+ );
43144
+ if (memberDefaults.memberDefaultValues.size > 0) {
43145
+ manifest = {
43146
+ ...manifest,
43147
+ members: manifest.members.map((member) => {
43148
+ const lowered = memberDefaults.memberDefaultValues.get(member.id);
43149
+ return lowered === void 0 ? member : { ...member, defaultValue: lowered };
43150
+ })
43151
+ };
43152
+ }
42800
43153
  (options.writeProjectAnalysisCache ?? writeProjectSourceAnalysisCacheV4)(
42801
43154
  workspace.root,
42802
43155
  analysis
@@ -42878,7 +43231,12 @@ function computeWorkspaceStatus(workspace, options = {}) {
42878
43231
  const dialogueState = overlayProspectiveSourceRecords(
42879
43232
  workspace.state.records,
42880
43233
  documents,
42881
- [...staticValues.records, ...rootRecords, ...supplementalRecords],
43234
+ [
43235
+ ...staticValues.records,
43236
+ ...memberDefaults.records,
43237
+ ...rootRecords,
43238
+ ...supplementalRecords
43239
+ ],
42882
43240
  staticMemberValueIds
42883
43241
  );
42884
43242
  const dialogueRecords = lowerDialogueProjectSourcesV4(
@@ -42887,6 +43245,7 @@ function computeWorkspaceStatus(workspace, options = {}) {
42887
43245
  );
42888
43246
  records2.push(
42889
43247
  ...staticValues.records,
43248
+ ...memberDefaults.records,
42890
43249
  ...rootRecords,
42891
43250
  ...supplementalRecords,
42892
43251
  ...dialogueRecords
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neocompose/cli",
3
- "version": "0.5.1",
3
+ "version": "0.5.3",
4
4
  "description": "Neo Compose native project-source CLI with bidirectional sync.",
5
5
  "license": "MIT",
6
6
  "type": "module",