@neocompose/cli 0.5.2 → 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 +13 -0
  2. package/dist/neo.mjs +417 -60
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
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
+
3
16
  ## [0.5.2] - 2026-07-18
4
17
 
5
18
  ### Fixed
package/dist/neo.mjs CHANGED
@@ -26945,7 +26945,8 @@ function lowerFieldMember(context, ownerClass, declaration, id2, commonInput, ba
26945
26945
  declaration.initializer,
26946
26946
  declaration.type,
26947
26947
  ownerClass,
26948
- base
26948
+ base,
26949
+ id2
26949
26950
  )
26950
26951
  };
26951
26952
  const settings = annotation(declaration.annotations, "settings");
@@ -26994,7 +26995,7 @@ function lowerFieldMember(context, ownerClass, declaration, id2, commonInput, ba
26994
26995
  return {
26995
26996
  ...common,
26996
26997
  kind: primitive3,
26997
- 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)
26998
26999
  };
26999
27000
  }
27000
27001
  if (primitive3) return { ...common, kind: primitive3 };
@@ -27261,10 +27262,13 @@ function lowerType(context, type, ownerClass) {
27261
27262
  if (type.name === "object") return { kind: "unknown", nullable };
27262
27263
  throw new Error(`Unknown Neo type ${JSON.stringify(type.name)}.`);
27263
27264
  }
27264
- function lowerDefault(context, initializer, type, ownerClass, base) {
27265
+ function lowerDefault(context, initializer, type, ownerClass, base, memberId) {
27265
27266
  if (initializer === null) {
27266
27267
  return base?.defaultValue && "serverValueId" in base.defaultValue ? base.defaultValue : null;
27267
27268
  }
27269
+ if (context.rowBackedDefaultMemberIds.has(memberId) && base?.defaultValue && !("serverValueId" in base.defaultValue)) {
27270
+ return base.defaultValue;
27271
+ }
27268
27272
  const expression = parseExpression(initializer);
27269
27273
  let value = lowerExpressionValue(context, expression, type, ownerClass);
27270
27274
  if (value !== null && context.enumIdsByName.has(type.name) && !Array.isArray(value)) {
@@ -27401,16 +27405,25 @@ function lowerExpressionValue(context, expression, expected, ownerClass) {
27401
27405
  const idArgument = expression.args[index >= 0 ? index : 0];
27402
27406
  if (idArgument?.kind === "litString") return idArgument.value;
27403
27407
  }
27404
- if (expression.callee.kind === "member" && expression.callee.name === "Slice" && expression.callee.receiver.kind === "call") {
27405
- const receiver = expression.callee.receiver;
27406
- const index = receiver.argumentNames?.findIndex((name) => name === "id") ?? -1;
27407
- const fileId = receiver.args[index >= 0 ? index : 0];
27408
+ if (expression.callee.kind === "member" && expression.callee.name === "Slice") {
27408
27409
  const sliceIndex = expression.args[0];
27409
- if (fileId?.kind === "litString" && (sliceIndex?.kind === "litInt" || sliceIndex === void 0)) {
27410
- return {
27411
- fileId: fileId.value,
27412
- sliceIndex: sliceIndex?.value ?? 0
27413
- };
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 };
27414
27427
  }
27415
27428
  }
27416
27429
  throw new Error(
@@ -27418,6 +27431,8 @@ function lowerExpressionValue(context, expression, expected, ownerClass) {
27418
27431
  );
27419
27432
  }
27420
27433
  case "member": {
27434
+ const fileId = projectFileIdForPath(context, expression);
27435
+ if (fileId !== null) return { fileId };
27421
27436
  if (expression.name === "Slice" || expression.receiver.kind === "call") {
27422
27437
  throw new Error(
27423
27438
  "File defaults are lowered by the project-file compiler."
@@ -27431,6 +27446,17 @@ function lowerExpressionValue(context, expression, expected, ownerClass) {
27431
27446
  );
27432
27447
  }
27433
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
+ }
27434
27460
  function manifestTypeForMember(context, member) {
27435
27461
  const nullable = !member.required;
27436
27462
  if (primitiveMemberKinds.has(member.kind)) {
@@ -27647,6 +27673,19 @@ function referenceIdArgument(value, name) {
27647
27673
  const raw = argument(value, name);
27648
27674
  return raw === null ? null : referenceId(raw);
27649
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
+ }
27650
27689
  function referenceId(raw) {
27651
27690
  try {
27652
27691
  const expression = parseExpression(raw);
@@ -28177,7 +28216,24 @@ function lowerProjectSchemaV4(base, analysis, options = {}) {
28177
28216
  enumOptionsByName,
28178
28217
  loweredMembers: /* @__PURE__ */ new Map(),
28179
28218
  placements: /* @__PURE__ */ new Map(),
28180
- 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()
28181
28237
  };
28182
28238
  const classes = analysis.schema.classes.map(
28183
28239
  (declaration) => lowerClass(context, declaration)
@@ -28326,7 +28382,14 @@ function emitProjectSourcesV4(manifest, options = {}) {
28326
28382
  interfaces: new Map(manifest.interfaces.map((entry) => [entry.id, entry])),
28327
28383
  enums: new Map(manifest.enums.map((entry) => [entry.id, entry])),
28328
28384
  members: new Map(manifest.members.map((entry) => [entry.id, entry])),
28329
- 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
+ )
28330
28393
  };
28331
28394
  const files = [];
28332
28395
  const localizationStatusNames = new Map(
@@ -28661,7 +28724,7 @@ ${indentNeoSourceNonEmptyLines(tracked.trim(), 2)}
28661
28724
  ${body ? `${signature} ${body}` : abstractContract ? `${signature};` : `native ${signature};`}`;
28662
28725
  }
28663
28726
  const type = renderMemberType(context, member);
28664
- 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);
28665
28728
  return `${annotations.join("\n")}
28666
28729
  ${prefix}${type} ${member.name}${initializer === null ? ";" : ` = ${initializer};`}`;
28667
28730
  }
@@ -28722,7 +28785,10 @@ function memberSettings(context, member) {
28722
28785
  }
28723
28786
  if (member.multiselect) settings.push("multiselect: true");
28724
28787
  } else if ((member.kind === "sprite" || member.kind === "audio") && member.templateId) {
28725
- 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
+ );
28726
28792
  }
28727
28793
  return settings.length ? [`@settings(${settings.join(", ")})`] : [];
28728
28794
  }
@@ -28900,6 +28966,13 @@ function renderDefaultValue(context, member, wrapper) {
28900
28966
  );
28901
28967
  return member.multiselect ? `[${values.join(", ")}]` : values[0] ?? "null";
28902
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
+ }
28903
28976
  if (member.kind === "list" && Array.isArray(value)) {
28904
28977
  const entry = required(context.members, member.entryMemberId, "list entry");
28905
28978
  return `[${value.map((item) => renderNestedValue(context, entry, item)).join(", ")}]`;
@@ -28931,7 +29004,9 @@ function renderDefaultValue(context, member, wrapper) {
28931
29004
  return member.multiselect ? `[${refs.join(", ")}]` : refs[0] ?? "null";
28932
29005
  }
28933
29006
  if ((member.kind === "sprite" || member.kind === "audio") && isObject(value) && typeof value.fileId === "string") {
28934
- 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;
28935
29010
  }
28936
29011
  if (isObject(value) && ["vector2", "vector2Int", "vector3", "vector3Int", "color"].includes(
28937
29012
  member.kind
@@ -30042,27 +30117,27 @@ var init_project_files = __esm({
30042
30117
  }
30043
30118
  });
30044
30119
 
30045
- // ../../../neo-compose/node_modules/uuid/dist-node/regex.js
30120
+ // ../node_modules/uuid/dist-node/regex.js
30046
30121
  var regex_default;
30047
30122
  var init_regex = __esm({
30048
- "../../../neo-compose/node_modules/uuid/dist-node/regex.js"() {
30123
+ "../node_modules/uuid/dist-node/regex.js"() {
30049
30124
  regex_default = /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/i;
30050
30125
  }
30051
30126
  });
30052
30127
 
30053
- // ../../../neo-compose/node_modules/uuid/dist-node/validate.js
30128
+ // ../node_modules/uuid/dist-node/validate.js
30054
30129
  function validate(uuid) {
30055
30130
  return typeof uuid === "string" && regex_default.test(uuid);
30056
30131
  }
30057
30132
  var validate_default;
30058
30133
  var init_validate2 = __esm({
30059
- "../../../neo-compose/node_modules/uuid/dist-node/validate.js"() {
30134
+ "../node_modules/uuid/dist-node/validate.js"() {
30060
30135
  init_regex();
30061
30136
  validate_default = validate;
30062
30137
  }
30063
30138
  });
30064
30139
 
30065
- // ../../../neo-compose/node_modules/uuid/dist-node/parse.js
30140
+ // ../node_modules/uuid/dist-node/parse.js
30066
30141
  function parse(uuid) {
30067
30142
  if (!validate_default(uuid)) {
30068
30143
  throw TypeError("Invalid UUID");
@@ -30072,19 +30147,19 @@ function parse(uuid) {
30072
30147
  }
30073
30148
  var parse_default;
30074
30149
  var init_parse = __esm({
30075
- "../../../neo-compose/node_modules/uuid/dist-node/parse.js"() {
30150
+ "../node_modules/uuid/dist-node/parse.js"() {
30076
30151
  init_validate2();
30077
30152
  parse_default = parse;
30078
30153
  }
30079
30154
  });
30080
30155
 
30081
- // ../../../neo-compose/node_modules/uuid/dist-node/stringify.js
30156
+ // ../node_modules/uuid/dist-node/stringify.js
30082
30157
  function unsafeStringify(arr, offset = 0) {
30083
30158
  return (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + "-" + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + "-" + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + "-" + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + "-" + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase();
30084
30159
  }
30085
30160
  var byteToHex;
30086
30161
  var init_stringify = __esm({
30087
- "../../../neo-compose/node_modules/uuid/dist-node/stringify.js"() {
30162
+ "../node_modules/uuid/dist-node/stringify.js"() {
30088
30163
  byteToHex = [];
30089
30164
  for (let i = 0; i < 256; ++i) {
30090
30165
  byteToHex.push((i + 256).toString(16).slice(1));
@@ -30092,18 +30167,18 @@ var init_stringify = __esm({
30092
30167
  }
30093
30168
  });
30094
30169
 
30095
- // ../../../neo-compose/node_modules/uuid/dist-node/rng.js
30170
+ // ../node_modules/uuid/dist-node/rng.js
30096
30171
  function rng() {
30097
30172
  return crypto.getRandomValues(rnds8);
30098
30173
  }
30099
30174
  var rnds8;
30100
30175
  var init_rng = __esm({
30101
- "../../../neo-compose/node_modules/uuid/dist-node/rng.js"() {
30176
+ "../node_modules/uuid/dist-node/rng.js"() {
30102
30177
  rnds8 = new Uint8Array(16);
30103
30178
  }
30104
30179
  });
30105
30180
 
30106
- // ../../../neo-compose/node_modules/uuid/dist-node/v35.js
30181
+ // ../node_modules/uuid/dist-node/v35.js
30107
30182
  function stringToBytes(str) {
30108
30183
  str = unescape(encodeURIComponent(str));
30109
30184
  const bytes = new Uint8Array(str.length);
@@ -30141,7 +30216,7 @@ function v35(version, hash, value, namespace, buf, offset) {
30141
30216
  }
30142
30217
  var DNS, URL2;
30143
30218
  var init_v35 = __esm({
30144
- "../../../neo-compose/node_modules/uuid/dist-node/v35.js"() {
30219
+ "../node_modules/uuid/dist-node/v35.js"() {
30145
30220
  init_parse();
30146
30221
  init_stringify();
30147
30222
  DNS = "6ba7b810-9dad-11d1-80b4-00c04fd430c8";
@@ -30149,7 +30224,7 @@ var init_v35 = __esm({
30149
30224
  }
30150
30225
  });
30151
30226
 
30152
- // ../../../neo-compose/node_modules/uuid/dist-node/v4.js
30227
+ // ../node_modules/uuid/dist-node/v4.js
30153
30228
  function v4(options, buf, offset) {
30154
30229
  if (!buf && !options && crypto.randomUUID) {
30155
30230
  return crypto.randomUUID();
@@ -30178,14 +30253,14 @@ function _v4(options, buf, offset) {
30178
30253
  }
30179
30254
  var v4_default;
30180
30255
  var init_v4 = __esm({
30181
- "../../../neo-compose/node_modules/uuid/dist-node/v4.js"() {
30256
+ "../node_modules/uuid/dist-node/v4.js"() {
30182
30257
  init_rng();
30183
30258
  init_stringify();
30184
30259
  v4_default = v4;
30185
30260
  }
30186
30261
  });
30187
30262
 
30188
- // ../../../neo-compose/node_modules/uuid/dist-node/sha1.js
30263
+ // ../node_modules/uuid/dist-node/sha1.js
30189
30264
  import { createHash as createHash2 } from "node:crypto";
30190
30265
  function sha1(bytes) {
30191
30266
  if (Array.isArray(bytes)) {
@@ -30197,18 +30272,18 @@ function sha1(bytes) {
30197
30272
  }
30198
30273
  var sha1_default;
30199
30274
  var init_sha1 = __esm({
30200
- "../../../neo-compose/node_modules/uuid/dist-node/sha1.js"() {
30275
+ "../node_modules/uuid/dist-node/sha1.js"() {
30201
30276
  sha1_default = sha1;
30202
30277
  }
30203
30278
  });
30204
30279
 
30205
- // ../../../neo-compose/node_modules/uuid/dist-node/v5.js
30280
+ // ../node_modules/uuid/dist-node/v5.js
30206
30281
  function v5(value, namespace, buf, offset) {
30207
30282
  return v35(80, sha1_default, value, namespace, buf, offset);
30208
30283
  }
30209
30284
  var v5_default;
30210
30285
  var init_v5 = __esm({
30211
- "../../../neo-compose/node_modules/uuid/dist-node/v5.js"() {
30286
+ "../node_modules/uuid/dist-node/v5.js"() {
30212
30287
  init_sha1();
30213
30288
  init_v35();
30214
30289
  v5.DNS = DNS;
@@ -30217,9 +30292,9 @@ var init_v5 = __esm({
30217
30292
  }
30218
30293
  });
30219
30294
 
30220
- // ../../../neo-compose/node_modules/uuid/dist-node/index.js
30295
+ // ../node_modules/uuid/dist-node/index.js
30221
30296
  var init_dist_node = __esm({
30222
- "../../../neo-compose/node_modules/uuid/dist-node/index.js"() {
30297
+ "../node_modules/uuid/dist-node/index.js"() {
30223
30298
  init_v4();
30224
30299
  init_v5();
30225
30300
  }
@@ -38746,7 +38821,7 @@ function emitStaticValueSourcesV4(records2) {
38746
38821
  );
38747
38822
  return emitStoredValueBindingSourcesV4(records2, memberIds);
38748
38823
  }
38749
- function emitStoredValueBindingSourcesV4(records2, memberIds, options = {}) {
38824
+ function buildValueEmitContext(records2) {
38750
38825
  const members = dataById(records2, "member");
38751
38826
  const classes = dataById(records2, "class");
38752
38827
  const values = dataById(records2, "value");
@@ -38754,7 +38829,7 @@ function emitStoredValueBindingSourcesV4(records2, memberIds, options = {}) {
38754
38829
  const localizedTexts = dataById(records2, "localized-text");
38755
38830
  const dialogues = dataById(records2, "dialogue");
38756
38831
  const symbolsByValueId = staticSymbols(classes, members);
38757
- const context = {
38832
+ return {
38758
38833
  members,
38759
38834
  classes,
38760
38835
  values,
@@ -38771,6 +38846,10 @@ function emitStoredValueBindingSourcesV4(records2, memberIds, options = {}) {
38771
38846
  referencedValueIds: externallyReferencedValueIds(records2),
38772
38847
  localizedTextIds: /* @__PURE__ */ new Set()
38773
38848
  };
38849
+ }
38850
+ function emitStoredValueBindingSourcesV4(records2, memberIds, options = {}) {
38851
+ const context = buildValueEmitContext(records2);
38852
+ const members = context.members;
38774
38853
  const initializers = /* @__PURE__ */ new Map();
38775
38854
  const recordKeysByMember = /* @__PURE__ */ new Map();
38776
38855
  for (const memberId of memberIds) {
@@ -38800,6 +38879,102 @@ function emitStoredValueBindingSourcesV4(records2, memberIds, options = {}) {
38800
38879
  }
38801
38880
  return { initializers, recordKeysByMember };
38802
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
+ }
38803
38978
  function lowerStaticValueSourcesV4(state, analysis, manifest) {
38804
38979
  const bindings = [];
38805
38980
  for (const sourceClass of analysis.schema.classes) {
@@ -38822,7 +38997,7 @@ function lowerStaticValueSourcesV4(state, analysis, manifest) {
38822
38997
  }
38823
38998
  return lowerStoredValueBindingsV4(state, manifest, bindings, { analysis });
38824
38999
  }
38825
- function lowerStoredValueBindingsV4(state, manifest, bindings, options = {}) {
39000
+ function buildValueLowerContext(state, manifest, options = {}) {
38826
39001
  const members = new Map(
38827
39002
  manifest.members.map((member) => [member.id, member])
38828
39003
  );
@@ -38836,7 +39011,7 @@ function lowerStoredValueBindingsV4(state, manifest, bindings, options = {}) {
38836
39011
  manifest,
38837
39012
  parsedInitializers
38838
39013
  );
38839
- const context = {
39014
+ return {
38840
39015
  state,
38841
39016
  members,
38842
39017
  classes,
@@ -38860,6 +39035,9 @@ function lowerStoredValueBindingsV4(state, manifest, bindings, options = {}) {
38860
39035
  parsedInitializers,
38861
39036
  reconstructed: /* @__PURE__ */ new Map()
38862
39037
  };
39038
+ }
39039
+ function lowerStoredValueBindingsV4(state, manifest, bindings, options = {}) {
39040
+ const context = buildValueLowerContext(state, manifest, options);
38863
39041
  const memberValueIds = /* @__PURE__ */ new Map();
38864
39042
  const seeds = /* @__PURE__ */ new Map();
38865
39043
  for (const binding of bindings) {
@@ -38871,6 +39049,143 @@ function lowerStoredValueBindingsV4(state, manifest, bindings, options = {}) {
38871
39049
  seeds
38872
39050
  };
38873
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
+ }
38874
39189
  function lowerStoredBinding(context, binding, memberValueIds, seeds) {
38875
39190
  const member = context.members.get(binding.memberId);
38876
39191
  if (member === void 0) {
@@ -39145,18 +39460,7 @@ function lowerClassValue(context, member, expression, base, source) {
39145
39460
  const baseBody = isObjectRecord2(base.value) ? base.value : {};
39146
39461
  const body = { ...baseBody };
39147
39462
  for (const assignment of expression.initializer ?? []) {
39148
- const childMemberId = schemaClass2.schema[assignment.name];
39149
- if (typeof childMemberId !== "string") {
39150
- throw new Error(
39151
- `Class ${schemaClass2.name} has no stored member ${assignment.name}.`
39152
- );
39153
- }
39154
- const childMember = context.members.get(childMemberId);
39155
- if (childMember === void 0) {
39156
- throw new Error(
39157
- `Class ${schemaClass2.name}.${assignment.name} is missing its member record.`
39158
- );
39159
- }
39463
+ const childMember = classMemberByName(context, classId, assignment.name);
39160
39464
  const childId = baseBody[assignment.name];
39161
39465
  if (typeof childId !== "string") {
39162
39466
  throw new Error(
@@ -40001,7 +40305,7 @@ function classValue(context, member, value, visited, targetTyped) {
40001
40305
  ...Object.keys(value.value).filter((key2) => !order.includes(key2))
40002
40306
  ]) {
40003
40307
  const childId = value.value[key];
40004
- const childMemberId = schema[key];
40308
+ const childMemberId = schema[key] ?? inheritedSchemaMemberId(context, schemaClass2, key);
40005
40309
  if (typeof childId !== "string" || typeof childMemberId !== "string")
40006
40310
  continue;
40007
40311
  const childMember = context.members.get(childMemberId);
@@ -40015,9 +40319,23 @@ function classValue(context, member, value, visited, targetTyped) {
40015
40319
  );
40016
40320
  }
40017
40321
  return fields.length === 0 ? targetTyped ? "new()" : `new ${name}()` : `${targetTyped ? "new()" : `new ${name}`} {
40018
- ${fields.map((field) => indentNeoSource(field, 2)).join(",\n")}
40322
+ ${fields.map((field) => indentNeoSourceNonEmptyLines(field, 2)).join(",\n")}
40019
40323
  }`;
40020
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
+ }
40021
40339
  function listValue(context, member, value, visited) {
40022
40340
  if (value.value === null) return "null";
40023
40341
  const entryMember = context.members.get(stringField2(member, "entryMemberId"));
@@ -40029,7 +40347,7 @@ function listValue(context, member, value, visited) {
40029
40347
  if (ids.length === 0) return "[]";
40030
40348
  return `[
40031
40349
  ${ids.map(
40032
- (id2) => indentNeoSource(
40350
+ (id2) => indentNeoSourceNonEmptyLines(
40033
40351
  emitValue(context, entryMember, id2, {
40034
40352
  exposeIdentity: context.symbolsByValueId.get(id2) === void 0,
40035
40353
  visited
@@ -40052,7 +40370,7 @@ function dictionaryValue(context, member, body, visited) {
40052
40370
  return `{
40053
40371
  ${entries.flatMap(
40054
40372
  ([key, id2]) => typeof id2 === "string" ? [
40055
- indentNeoSource(
40373
+ indentNeoSourceNonEmptyLines(
40056
40374
  `${quote4(key)}: ${emitValue(context, entryMember, id2, {
40057
40375
  exposeIdentity: !context.symbolsByValueId.has(id2) && context.referencedValueIds.has(id2),
40058
40376
  visited
@@ -40243,6 +40561,7 @@ function numberOr2(value, fallback) {
40243
40561
  function quote4(value) {
40244
40562
  return quoteNeoString(value);
40245
40563
  }
40564
+ var MEMBER_KIND_DICTIONARY, MEMBER_KIND_LIST, MEMBER_KIND_CLASS;
40246
40565
  var init_value_sources = __esm({
40247
40566
  "src/project-source/value-sources.ts"() {
40248
40567
  "use strict";
@@ -40251,6 +40570,9 @@ var init_value_sources = __esm({
40251
40570
  init_project_files();
40252
40571
  init_lower_members();
40253
40572
  init_source_format();
40573
+ MEMBER_KIND_DICTIONARY = 5;
40574
+ MEMBER_KIND_LIST = 6;
40575
+ MEMBER_KIND_CLASS = 7;
40254
40576
  }
40255
40577
  });
40256
40578
 
@@ -40433,8 +40755,11 @@ function emitProjectDocumentFilesV4(records2) {
40433
40755
  }
40434
40756
  const manifest = documentsToProjectSchemaManifest(schemaRecords);
40435
40757
  const staticValues = emitStaticValueSourcesV4(records2);
40758
+ const memberDefaults = emitMemberDefaultSourcesV4(records2);
40436
40759
  const baseSource = emitProjectSourcesV4(manifest, {
40437
40760
  staticInitializers: staticValues.initializers,
40761
+ defaultInitializers: memberDefaults.initializers,
40762
+ fileSymbols: qualifiedProjectFileSymbolsV4(records2),
40438
40763
  relationEndpointExpressions: relationEndpointExpressions(records2)
40439
40764
  });
40440
40765
  const source = {
@@ -40442,7 +40767,11 @@ function emitProjectDocumentFilesV4(records2) {
40442
40767
  files: baseSource.files.map((file) => {
40443
40768
  const ownedKeys = file.recordKeys.flatMap((key) => {
40444
40769
  if (!key.startsWith("member:")) return [];
40445
- 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
+ ];
40446
40775
  });
40447
40776
  return ownedKeys.length === 0 ? file : { ...file, recordKeys: [...file.recordKeys, ...ownedKeys] };
40448
40777
  })
@@ -42734,6 +43063,10 @@ function computeWorkspaceStatus(workspace, options = {}) {
42734
43063
  let manifest;
42735
43064
  let staticValueSeeds = /* @__PURE__ */ new Map();
42736
43065
  let staticMemberValueIds = /* @__PURE__ */ new Map();
43066
+ let memberDefaults = {
43067
+ records: [],
43068
+ memberDefaultValues: /* @__PURE__ */ new Map()
43069
+ };
42737
43070
  let projectAnalysisV4 = null;
42738
43071
  try {
42739
43072
  if (!baseManifest) {
@@ -42797,8 +43130,26 @@ function computeWorkspaceStatus(workspace, options = {}) {
42797
43130
  };
42798
43131
  }
42799
43132
  manifest = lowerProjectSchemaV4(baseManifest, analysis, {
42800
- 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
+ )
42801
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
+ }
42802
43153
  (options.writeProjectAnalysisCache ?? writeProjectSourceAnalysisCacheV4)(
42803
43154
  workspace.root,
42804
43155
  analysis
@@ -42880,7 +43231,12 @@ function computeWorkspaceStatus(workspace, options = {}) {
42880
43231
  const dialogueState = overlayProspectiveSourceRecords(
42881
43232
  workspace.state.records,
42882
43233
  documents,
42883
- [...staticValues.records, ...rootRecords, ...supplementalRecords],
43234
+ [
43235
+ ...staticValues.records,
43236
+ ...memberDefaults.records,
43237
+ ...rootRecords,
43238
+ ...supplementalRecords
43239
+ ],
42884
43240
  staticMemberValueIds
42885
43241
  );
42886
43242
  const dialogueRecords = lowerDialogueProjectSourcesV4(
@@ -42889,6 +43245,7 @@ function computeWorkspaceStatus(workspace, options = {}) {
42889
43245
  );
42890
43246
  records2.push(
42891
43247
  ...staticValues.records,
43248
+ ...memberDefaults.records,
42892
43249
  ...rootRecords,
42893
43250
  ...supplementalRecords,
42894
43251
  ...dialogueRecords
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neocompose/cli",
3
- "version": "0.5.2",
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",