@neocompose/cli 0.54.0 → 0.55.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,17 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.55.0] - 2026-09-23
4
+
5
+ - `@settings` before a `List` element or `Dictionary` value type configures the collection's entries, such as `List<@settings(format: .Plain) string>` or `Dictionary<string, @settings(min: 0) int>`. `neo pull` writes entry settings in this form. Switching entries between Localized and Plain converts every entry's text exactly as the same change on a string member does. Annotations on a Dictionary key, another generic's type argument, or an override's element type are reported at their position. Run `neo pull` before your first push so entry settings set in the web app appear in source.
6
+ - Changing an existing collection's element type, such as `List<string>` to `List<int>`, fails with an error. It used to be ignored: the push kept the old type without saying so.
7
+ - Switching a string member from Plain to Localized converts the values it already stores, including values packed inside their parent row. It used to fail with "Localized text … was not pulled" or "Localizable value references missing localized text".
8
+ - Switching a string member from Localized to Plain keeps packed values packed, so `neo status` is clean after the push.
9
+ - Adding a value beside packed children that link localized text, such as a new field on an item whose names are packed, no longer fails with "must reference an existing localized text id."
10
+
11
+ ## [0.54.1] - 2026-09-22
12
+
13
+ - Variants with the same name in different folders, such as `Oak/Day1` and `Ash/Day1`, keep their own nested rows on push. Adding overrides to one of them no longer fails with "claims identity …, which another value created by this push already owns."
14
+
3
15
  ## [0.54.0] - 2026-09-22
4
16
 
5
17
  - `NeoSortingGroup` has a `SortPoint`: where the group sorts along the transparency sort axis, as a cell offset from the object's origin-cell corner. It defaults to `(0.5, 0)`, the bottom center of the origin cell. Generated Unity classes implement `INeoSortingGroup.SortPoint`, which requires Neo Compose SDK 0.40.0.
package/dist/neo.mjs CHANGED
@@ -9164,15 +9164,15 @@ var init_strict_resolver = __esm({
9164
9164
  symbol
9165
9165
  };
9166
9166
  }
9167
- const variantPath = this.resolveVariantPath(receiverAst, name, scope, pos);
9168
- if (variantPath !== null) {
9167
+ const variantPath2 = this.resolveVariantPath(receiverAst, name, scope, pos);
9168
+ if (variantPath2 !== null) {
9169
9169
  if (optional2) {
9170
9170
  throw new CompileError(
9171
9171
  "Optional chaining is not valid on a variant path.",
9172
9172
  pos
9173
9173
  );
9174
9174
  }
9175
- return variantPath;
9175
+ return variantPath2;
9176
9176
  }
9177
9177
  if (receiverAst.kind === "ident" && !scope.lookup(receiverAst.name)) {
9178
9178
  if (receiverAst.name === "SpriteInfo" && name === "Empty") {
@@ -22366,9 +22366,9 @@ function contextualAnnotationFields(document, position) {
22366
22366
  }
22367
22367
  const typeAnnotation = findTypeAnnotation(member.type, position);
22368
22368
  if (typeAnnotation) {
22369
- return fieldsForAnnotation(typeAnnotation.name, {
22369
+ return fieldsForAnnotation(typeAnnotation.annotation.name, {
22370
22370
  kind: "type",
22371
- type: semanticType(member.type)
22371
+ type: semanticType(typeAnnotation.type)
22372
22372
  });
22373
22373
  }
22374
22374
  }
@@ -22466,6 +22466,13 @@ function validateDeclaration(uri, declaration, documentKind, environment, diagno
22466
22466
  diagnostics
22467
22467
  );
22468
22468
  if (declaration.kind === "global") {
22469
+ validateTypeAnnotations(
22470
+ uri,
22471
+ declaration.type,
22472
+ "none",
22473
+ environment,
22474
+ diagnostics
22475
+ );
22469
22476
  if (declaration.type.name === "Root") {
22470
22477
  validateProjectRootGlobal(uri, declaration, environment, diagnostics);
22471
22478
  return;
@@ -22507,11 +22514,26 @@ function validateDeclaration(uri, declaration, documentKind, environment, diagno
22507
22514
  validateTypeAnnotations(
22508
22515
  uri,
22509
22516
  parameter4.constraint,
22517
+ "none",
22510
22518
  environment,
22511
22519
  diagnostics
22512
22520
  );
22513
22521
  }
22514
22522
  }
22523
+ for (const parameter4 of declaration.headerParameters ?? []) {
22524
+ validateTypeAnnotations(
22525
+ uri,
22526
+ parameter4.type,
22527
+ "none",
22528
+ environment,
22529
+ diagnostics
22530
+ );
22531
+ }
22532
+ }
22533
+ if (declaration.kind === "class" || declaration.kind === "interface") {
22534
+ for (const baseType of declaration.baseTypes) {
22535
+ validateTypeAnnotations(uri, baseType, "none", environment, diagnostics);
22536
+ }
22515
22537
  }
22516
22538
  const scope = new Map(environment.globals);
22517
22539
  if (declaration.kind === "class") {
@@ -22810,7 +22832,13 @@ function validateMember(uri, member, ownerName, documentKind, ownerScope, enviro
22810
22832
  environment,
22811
22833
  diagnostics
22812
22834
  );
22813
- validateTypeAnnotations(uri, member.type, environment, diagnostics);
22835
+ validateTypeAnnotations(
22836
+ uri,
22837
+ member.type,
22838
+ elementSettingsSite(member, documentKind),
22839
+ environment,
22840
+ diagnostics
22841
+ );
22814
22842
  if (documentKind !== "flow") {
22815
22843
  validateListAnnotations(uri, member, ownerName, environment, diagnostics);
22816
22844
  }
@@ -22818,7 +22846,13 @@ function validateMember(uri, member, ownerName, documentKind, ownerScope, enviro
22818
22846
  if (member.kind === "function") {
22819
22847
  for (const parameter4 of member.parameters) {
22820
22848
  scope.set(parameter4.name, semanticType(parameter4.type));
22821
- validateTypeAnnotations(uri, parameter4.type, environment, diagnostics);
22849
+ validateTypeAnnotations(
22850
+ uri,
22851
+ parameter4.type,
22852
+ "none",
22853
+ environment,
22854
+ diagnostics
22855
+ );
22822
22856
  }
22823
22857
  return;
22824
22858
  }
@@ -22919,16 +22953,77 @@ function validateFlowContent(uri, content, parentScope, environment, diagnostics
22919
22953
  }
22920
22954
  }
22921
22955
  }
22922
- function validateTypeAnnotations(uri, type, environment, diagnostics) {
22923
- validateAnnotations(
22956
+ function elementSettingsSite(member, documentKind) {
22957
+ if (documentKind === "flow" || member.kind !== "field") return "none";
22958
+ const overrides = member.modifiers.includes("override") || member.modifiers.includes("sealed");
22959
+ return overrides ? "override" : "declaringField";
22960
+ }
22961
+ function validateTypeAnnotations(uri, type, site, environment, diagnostics) {
22962
+ rejectTypeAnnotations(
22924
22963
  uri,
22925
- type.annotations,
22926
- { kind: "type", type: semanticType(type) },
22927
- environment,
22964
+ type,
22965
+ "is not valid before a declared type. Annotate the declaration instead.",
22928
22966
  diagnostics
22929
22967
  );
22930
- for (const argument2 of type.typeArguments) {
22931
- validateTypeAnnotations(uri, argument2, environment, diagnostics);
22968
+ validateTypeArgumentAnnotations(uri, type, site, environment, diagnostics);
22969
+ }
22970
+ function validateTypeArgumentAnnotations(uri, type, site, environment, diagnostics) {
22971
+ const elementIndex = collectionElementIndex(type.name);
22972
+ for (const [index, argument2] of type.typeArguments.entries()) {
22973
+ const element = index === elementIndex;
22974
+ if (!element) {
22975
+ rejectTypeAnnotations(
22976
+ uri,
22977
+ argument2,
22978
+ type.name === "Dictionary" ? "is not valid on a Dictionary key. Only the value type accepts element settings." : `is not valid on a ${type.name} type argument. Only a List element or Dictionary value accepts element settings.`,
22979
+ diagnostics
22980
+ );
22981
+ } else if (site === "override") {
22982
+ rejectTypeAnnotations(
22983
+ uri,
22984
+ argument2,
22985
+ "is not valid on an override's element type. Declare element settings on the member that declares the collection.",
22986
+ diagnostics
22987
+ );
22988
+ } else if (site === "none") {
22989
+ rejectTypeAnnotations(
22990
+ uri,
22991
+ argument2,
22992
+ "is not valid here. Only a field's List element or Dictionary value accepts element settings.",
22993
+ diagnostics
22994
+ );
22995
+ } else {
22996
+ validateAnnotations(
22997
+ uri,
22998
+ argument2.annotations,
22999
+ { kind: "type", type: semanticType(argument2) },
23000
+ environment,
23001
+ diagnostics
23002
+ );
23003
+ }
23004
+ validateTypeArgumentAnnotations(
23005
+ uri,
23006
+ argument2,
23007
+ element ? site : "none",
23008
+ environment,
23009
+ diagnostics
23010
+ );
23011
+ }
23012
+ }
23013
+ function collectionElementIndex(name) {
23014
+ if (name === "List") return 0;
23015
+ if (name === "Dictionary") return 1;
23016
+ return null;
23017
+ }
23018
+ function rejectTypeAnnotations(uri, type, reason, diagnostics) {
23019
+ for (const annotation2 of type.annotations) {
23020
+ pushDiagnostic(
23021
+ diagnostics,
23022
+ uri,
23023
+ annotation2.nameRange,
23024
+ "invalid-type-annotation",
23025
+ `@${annotation2.name} ${reason}`
23026
+ );
22932
23027
  }
22933
23028
  }
22934
23029
  function validateAnnotations(uri, annotations, context, environment, diagnostics) {
@@ -22997,6 +23092,7 @@ function fieldsForAnnotation(name, context, environment) {
22997
23092
  return context.kind === "class" ? [{ name: "visibility", type: "NeoClassVisibility" }] : null;
22998
23093
  }
22999
23094
  if (name === "settings") return settingsFields(context);
23095
+ if (context.kind === "type") return null;
23000
23096
  if (name === "storage") return storageFields(context);
23001
23097
  if (name === "distribution") return distributionFields(context);
23002
23098
  if (name === "relations") return relationFields(context, environment);
@@ -23072,7 +23168,7 @@ function settingsFields(context) {
23072
23168
  );
23073
23169
  }
23074
23170
  function distributionFields(context) {
23075
- if (context.kind === "member" || context.kind === "type") {
23171
+ if (context.kind === "member") {
23076
23172
  return [{ name: "kind", type: "NeoSubtreeDistribution" }];
23077
23173
  }
23078
23174
  return null;
@@ -23081,7 +23177,7 @@ function storageFields(context) {
23081
23177
  if (context.kind === "class") {
23082
23178
  return [{ name: "allowed", type: "NeoAllowedStorage" }];
23083
23179
  }
23084
- if (context.kind === "member" || context.kind === "type") {
23180
+ if (context.kind === "member") {
23085
23181
  return [
23086
23182
  { name: "allowed", type: "NeoStorage" },
23087
23183
  { name: "key", type: "string?" }
@@ -24636,13 +24732,13 @@ function annotationEnumOption(annotations, name, field) {
24636
24732
  return match?.[1] ?? null;
24637
24733
  }
24638
24734
  function isListContext(context) {
24639
- return (context.kind === "member" || context.kind === "type") && (context.type.name === "List" || context.type.name === "Set");
24735
+ return context.kind === "member" && (context.type.name === "List" || context.type.name === "Set");
24640
24736
  }
24641
24737
  function findTypeAnnotation(type, position) {
24642
24738
  const own = type.annotations.find(
24643
24739
  (annotation2) => inAnnotationArguments(annotation2, position)
24644
24740
  );
24645
- if (own) return own;
24741
+ if (own) return { annotation: own, type };
24646
24742
  for (const argument2 of type.typeArguments) {
24647
24743
  const nested = findTypeAnnotation(argument2, position);
24648
24744
  if (nested) return nested;
@@ -29523,13 +29619,13 @@ function activeNamedArgument(text, position) {
29523
29619
  return /([A-Za-z_][A-Za-z0-9_]*)\s*:\s*\.[A-Za-z0-9_]*$/.exec(prefix)?.[1] ?? null;
29524
29620
  }
29525
29621
  function projectHover(analysis, document, position) {
29526
- const variantPath = projectVariantPathSymbolAt(analysis, document, position);
29527
- if (variantPath) {
29622
+ const variantPath2 = projectVariantPathSymbolAt(analysis, document, position);
29623
+ if (variantPath2) {
29528
29624
  return {
29529
- range: variantPath.token.range,
29625
+ range: variantPath2.token.range,
29530
29626
  markdown: projectVariantHoverMarkdown(
29531
- variantPath.entry,
29532
- variantPath.entry.declaration.type.name === NEO_VARIANT_FOLDER_TYPE_NAME
29627
+ variantPath2.entry,
29628
+ variantPath2.entry.declaration.type.name === NEO_VARIANT_FOLDER_TYPE_NAME
29533
29629
  )
29534
29630
  };
29535
29631
  }
@@ -29629,12 +29725,12 @@ Construct with \`new(${parameters})\` \u2014 this class declares a required cons
29629
29725
  ${lines.join("\n\n")}`;
29630
29726
  }
29631
29727
  function projectDefinition(analysis, document, position) {
29632
- const variantPath = projectVariantPathSymbolAt(analysis, document, position);
29633
- if (variantPath) {
29728
+ const variantPath2 = projectVariantPathSymbolAt(analysis, document, position);
29729
+ if (variantPath2) {
29634
29730
  return [
29635
29731
  {
29636
- uri: variantPath.entry.uri,
29637
- range: variantPath.entry.declaration.nameRange
29732
+ uri: variantPath2.entry.uri,
29733
+ range: variantPath2.entry.declaration.nameRange
29638
29734
  }
29639
29735
  ];
29640
29736
  }
@@ -31446,10 +31542,12 @@ function flowArguments(argumentsValue) {
31446
31542
  }));
31447
31543
  }
31448
31544
  function manifestType(type) {
31545
+ const annotations = manifestAnnotations(type.annotations);
31449
31546
  return {
31450
31547
  name: type.name,
31451
31548
  nullable: type.nullable,
31452
- arguments: type.typeArguments.map(manifestType)
31549
+ arguments: type.typeArguments.map(manifestType),
31550
+ ...annotations.length === 0 ? {} : { annotations }
31453
31551
  };
31454
31552
  }
31455
31553
  function manifestAnnotations(annotations) {
@@ -31979,7 +32077,12 @@ function assertFlowArguments(value, path) {
31979
32077
  }
31980
32078
  function assertType(value, path) {
31981
32079
  const type = record(value, path);
31982
- exactKeys(type, path, ["name", "nullable", "arguments"]);
32080
+ exactKeysWithOptional(
32081
+ type,
32082
+ path,
32083
+ ["name", "nullable", "arguments"],
32084
+ ["annotations"]
32085
+ );
31983
32086
  string(type.name, `${path}.name`);
31984
32087
  if (typeof type.nullable !== "boolean") {
31985
32088
  throw new Error(`${path}.nullable must be boolean.`);
@@ -31987,6 +32090,10 @@ function assertType(value, path) {
31987
32090
  array(type.arguments, `${path}.arguments`).forEach(
31988
32091
  (argument2, index) => assertType(argument2, `${path}.arguments[${index}]`)
31989
32092
  );
32093
+ if (type.annotations === void 0) return;
32094
+ array(type.annotations, `${path}.annotations`).forEach(
32095
+ (entry, index) => assertAnnotation(entry, `${path}.annotations[${index}]`)
32096
+ );
31990
32097
  }
31991
32098
  function assertAnnotation(value, path) {
31992
32099
  const annotation2 = record(value, path);
@@ -58134,7 +58241,12 @@ ${signature} => ${expression};`;
58134
58241
  context.formAlternates.push({ shorthand, block });
58135
58242
  return shorthand;
58136
58243
  }
58137
- const type = renderMemberType(context, member, enclosingClassId);
58244
+ const type = renderMemberType(
58245
+ context,
58246
+ member,
58247
+ enclosingClassId,
58248
+ member.overrideOf === null
58249
+ );
58138
58250
  const initializer = initDefaultSource(member) ?? (member.modifier === "static" ? context.staticInitializers.get(member.id) : void 0) ?? context.defaultInitializers.get(member.id) ?? renderDefault(context, member, enclosingClassId);
58139
58251
  return `${annotations.join("\n")}
58140
58252
  ${prefix}${type} ${member.name}${initializer === null ? ";" : ` = ${initializer};`}`;
@@ -58271,7 +58383,18 @@ function listAnnotations(member) {
58271
58383
  })
58272
58384
  ];
58273
58385
  }
58274
- function renderMemberType(context, member, enclosingClassId) {
58386
+ function renderEntryType(context, entryMemberId, label, enclosingClassId, elementSettings) {
58387
+ const entry = required(context.members, entryMemberId, label);
58388
+ const type = renderMemberType(
58389
+ context,
58390
+ entry,
58391
+ enclosingClassId,
58392
+ elementSettings
58393
+ );
58394
+ const settings = elementSettings ? memberSettings(context, entry) : [];
58395
+ return settings.length === 0 ? type : `${settings.join(" ")} ${type}`;
58396
+ }
58397
+ function renderMemberType(context, member, enclosingClassId, elementSettings = false) {
58275
58398
  const nullable2 = !member.required;
58276
58399
  const closedGenerics = enclosingClassId === void 0 ? void 0 : classGenericMemberBindings(context.classes, enclosingClassId);
58277
58400
  let value;
@@ -58362,11 +58485,11 @@ function renderMemberType(context, member, enclosingClassId) {
58362
58485
  break;
58363
58486
  }
58364
58487
  case "list":
58365
- value = `List<${renderMemberType(context, required(context.members, member.entryMemberId, "list entry"), enclosingClassId)}>`;
58488
+ value = `List<${renderEntryType(context, member.entryMemberId, "list entry", enclosingClassId, elementSettings)}>`;
58366
58489
  break;
58367
58490
  case "dictionary": {
58368
58491
  const key = member.key.kind === "string" ? "string" : required(context.enums, member.key.enumId, "dictionary key enum").name;
58369
- value = `Dictionary<${key}, ${renderMemberType(context, required(context.members, member.entryMemberId, "dictionary entry"), enclosingClassId)}>`;
58492
+ value = `Dictionary<${key}, ${renderEntryType(context, member.entryMemberId, "dictionary entry", enclosingClassId, elementSettings)}>`;
58370
58493
  break;
58371
58494
  }
58372
58495
  case "lookup": {
@@ -90556,6 +90679,68 @@ function resolveLocalizableStringWriteOwners(document, changes, requestedValueId
90556
90679
  }
90557
90680
  return owners;
90558
90681
  }
90682
+ function packedLocalizationConversionChanges(args) {
90683
+ const beforeById = new Map(
90684
+ args.beforeMembers.map((member) => [member.id, member])
90685
+ );
90686
+ const becameLocalized = (member) => {
90687
+ if (!isMemberStringBase(member)) return false;
90688
+ if (!isLocalizedStringMember(member)) return false;
90689
+ const id2 = getOptionalString(member, "id");
90690
+ const before = id2 === void 0 ? void 0 : beforeById.get(id2);
90691
+ if (before === void 0) return false;
90692
+ const resolved = resolveMember2(before, args.beforeMembers);
90693
+ return isMemberStringBase(resolved) && !isLocalizedStringMember(resolved);
90694
+ };
90695
+ const postMembers = new Map(
90696
+ args.postDocument.members.map((member) => [member.id, member])
90697
+ );
90698
+ const flips = args.changes.some((change) => {
90699
+ if (change.recordKind !== "member" || change.operation === "delete") {
90700
+ return false;
90701
+ }
90702
+ const member = postMembers.get(change.recordId);
90703
+ return member !== void 0 && becameLocalized(resolveMember2(member, args.postDocument.members));
90704
+ });
90705
+ if (!flips) return [];
90706
+ const changedValueIds = new Set(
90707
+ args.changes.flatMap(
90708
+ (change) => change.recordKind === "value" ? [change.recordId] : []
90709
+ )
90710
+ );
90711
+ const omitted = /* @__PURE__ */ new Map();
90712
+ for (const change of args.submitted) {
90713
+ if (change.recordKind !== "value" || change.operation === "delete") {
90714
+ continue;
90715
+ }
90716
+ if (!isMemberValue(change.nextData)) continue;
90717
+ for (const child of decodePackedValueRow(change.nextData).children) {
90718
+ if (changedValueIds.has(child.id)) continue;
90719
+ if (typeof child.value !== "string" || child.value === "") continue;
90720
+ omitted.set(child.id, { child, intent: change.intent });
90721
+ }
90722
+ }
90723
+ if (omitted.size === 0) return [];
90724
+ const owners = resolveLocalizableStringWriteOwners(
90725
+ args.postDocument,
90726
+ args.changes,
90727
+ new Set(omitted.keys())
90728
+ );
90729
+ const conversions = [];
90730
+ for (const [valueId, { child, intent }] of omitted) {
90731
+ const owner = owners.get(valueId);
90732
+ if (owner === void 0 || !becameLocalized(owner)) continue;
90733
+ conversions.push({
90734
+ intent,
90735
+ recordKind: "value",
90736
+ recordId: valueId,
90737
+ operation: "update",
90738
+ deleted: false,
90739
+ nextData: child
90740
+ });
90741
+ }
90742
+ return conversions;
90743
+ }
90559
90744
  function reconcileMemberTextUsage(document, changes, before, referencedOutsideDocument, now, contentHashByKey) {
90560
90745
  const assignmentChanges = changes.filter(
90561
90746
  (change) => change.recordKind === "member" || change.recordKind === "value"
@@ -90828,6 +91013,7 @@ var init_localizable_member_value_writes = __esm({
90828
91013
  init_project_record_live_references();
90829
91014
  init_localization2();
90830
91015
  init_members();
91016
+ init_packed_value_encoding();
90831
91017
  init_variant_value_graph();
90832
91018
  init_project_version_intents();
90833
91019
  init_constructor_argument_ownership();
@@ -91219,10 +91405,11 @@ function commitPreparationMemberDefaultValueScope(args) {
91219
91405
  return { exactValueIds, descendantValueIds, evaluatesSource };
91220
91406
  }
91221
91407
  function planCommitPreparationInitialScope(args) {
91408
+ const submitted = args.changes;
91222
91409
  args = {
91223
91410
  ...args,
91224
91411
  changes: expandPackedValueWriteChanges({
91225
- changes: args.changes,
91412
+ changes: submitted,
91226
91413
  baseValues: args.document.values ?? [],
91227
91414
  basePhysicalValues: args.document.physicalValues,
91228
91415
  omitUnchangedChildren: true
@@ -91233,6 +91420,18 @@ function planCommitPreparationInitialScope(args) {
91233
91420
  );
91234
91421
  const currentDocument = overlayPlanningDocument(args.document, []);
91235
91422
  const postDocument = overlayPlanningDocument(currentDocument, args.changes);
91423
+ args = {
91424
+ ...args,
91425
+ changes: [
91426
+ ...args.changes,
91427
+ ...packedLocalizationConversionChanges({
91428
+ submitted,
91429
+ changes: args.changes,
91430
+ beforeMembers: currentDocument.members,
91431
+ postDocument
91432
+ })
91433
+ ]
91434
+ };
91236
91435
  const exactRecords = /* @__PURE__ */ new Map();
91237
91436
  const descendantRecords = /* @__PURE__ */ new Map();
91238
91437
  const rememberExact = (recordKind, recordId, includeDescendants = false) => {
@@ -91722,8 +91921,13 @@ function planCommitPreparationLoadedClosureScope(args) {
91722
91921
  postings.set(commitPreparationPostingRequestKey(posting), posting);
91723
91922
  };
91724
91923
  for (const record6 of args.records) {
91725
- if (record6.recordKind === "value" && isMemberValue(record6.data) && typeof record6.data.value === "string" && record6.data.value !== "") {
91726
- rememberExact("localized-text", record6.data.value);
91924
+ if (record6.recordKind === "value" && isMemberValue(record6.data)) {
91925
+ const decoded = decodePackedValueRow(record6.data);
91926
+ for (const row of [decoded.row, ...decoded.children]) {
91927
+ if (typeof row.value === "string" && row.value !== "") {
91928
+ rememberExact("localized-text", row.value);
91929
+ }
91930
+ }
91727
91931
  }
91728
91932
  if (record6.recordKind === "member" && isMemberStringBase(record6.data) && isLocalizedStringMember(record6.data) && typeof record6.data.defaultValue?.value === "string" && record6.data.defaultValue.value !== "") {
91729
91933
  rememberExact("localized-text", record6.data.defaultValue.value);
@@ -97069,6 +97273,14 @@ function* prepareServerOwnedSchemaCommitPasses(args) {
97069
97273
  true
97070
97274
  );
97071
97275
  authoredChanges = retargeted;
97276
+ authoredChanges.push(
97277
+ ...packedLocalizationConversionChanges({
97278
+ submitted: args.changes,
97279
+ changes: authoredChanges,
97280
+ beforeMembers: args.document.members,
97281
+ postDocument: authoredPost
97282
+ })
97283
+ );
97072
97284
  const editedValueIds = new Set(
97073
97285
  authoredChanges.filter(
97074
97286
  (change) => change.recordKind === "value" && change.operation === "update"
@@ -109196,7 +109408,15 @@ function lowerMemberKind(context, ownerClass, declaration, id2, owner) {
109196
109408
  deferred: declaration.modifiers.includes("async")
109197
109409
  };
109198
109410
  }
109199
- return lowerFieldMember(context, ownerClass, declaration, id2, common, base);
109411
+ return lowerFieldMember(
109412
+ context,
109413
+ ownerClass,
109414
+ declaration,
109415
+ id2,
109416
+ common,
109417
+ base,
109418
+ "declaration"
109419
+ );
109200
109420
  }
109201
109421
  function lowerUiFunctionAction(context, ownerName, declaration, base, memberId, sourceText) {
109202
109422
  const compiled = context.compiledFunctionBodiesByMemberId.get(memberId) ?? context.compiledFunctionBodiesBySourceName.get(
@@ -109343,7 +109563,7 @@ function resolvedMemberStorage(context, memberId, visited = /* @__PURE__ */ new
109343
109563
  }
109344
109564
  return null;
109345
109565
  }
109346
- function lowerFieldMember(context, ownerClass, declaration, id2, commonInput, base) {
109566
+ function lowerFieldMember(context, ownerClass, declaration, id2, commonInput, base, entrySettings) {
109347
109567
  const partial = declaration.type.name === "Partial";
109348
109568
  const fieldType = partial ? requiredTypeArgument(declaration.type, 0) : declaration.type;
109349
109569
  const storedStatic = declaration.modifiers.includes("static") && !declaration.modifiers.includes("readonly");
@@ -109506,6 +109726,7 @@ function lowerFieldMember(context, ownerClass, declaration, id2, commonInput, ba
109506
109726
  );
109507
109727
  }
109508
109728
  const multiselect = fieldType.name === "Set" || selection2 === "Multi";
109729
+ assertNoElementSettings(fieldType, ownerClass, declaration, "lookup");
109509
109730
  return {
109510
109731
  ...common,
109511
109732
  kind: "lookup",
@@ -109540,9 +109761,11 @@ function lowerFieldMember(context, ownerClass, declaration, id2, commonInput, ba
109540
109761
  }
109541
109762
  const multiselect = name === "Set" || selection2 === "Multi";
109542
109763
  if (enumId2 && multiselect) {
109764
+ assertNoElementSettings(fieldType, ownerClass, declaration, "enum");
109543
109765
  return { ...common, kind: "enum", enumId: enumId2, multiselect: true };
109544
109766
  }
109545
109767
  if (entryType.name === "Dialogue" && multiselect) {
109768
+ assertNoElementSettings(fieldType, ownerClass, declaration, "dialogue");
109546
109769
  return {
109547
109770
  ...common,
109548
109771
  kind: "dialogueLookup",
@@ -109567,7 +109790,8 @@ function lowerFieldMember(context, ownerClass, declaration, id2, commonInput, ba
109567
109790
  entryType,
109568
109791
  entryId,
109569
109792
  id2,
109570
- declaringMemberId
109793
+ declaringMemberId,
109794
+ entrySettings
109571
109795
  );
109572
109796
  return {
109573
109797
  ...common,
@@ -109596,7 +109820,8 @@ function lowerFieldMember(context, ownerClass, declaration, id2, commonInput, ba
109596
109820
  entryType,
109597
109821
  entryId,
109598
109822
  id2,
109599
- declaringMemberId
109823
+ declaringMemberId,
109824
+ entrySettings
109600
109825
  );
109601
109826
  return {
109602
109827
  ...common,
@@ -109657,6 +109882,12 @@ function lowerFieldMember(context, ownerClass, declaration, id2, commonInput, ba
109657
109882
  `Unsupported field type ${JSON.stringify(name)} on ${ownerClass.name}.${declaration.name}.`
109658
109883
  );
109659
109884
  }
109885
+ function assertNoElementSettings(fieldType, ownerClass, declaration, kind) {
109886
+ if (!fieldType.arguments[0]?.annotations?.length) return;
109887
+ throw new Error(
109888
+ `Member ${ownerClass.name}.${declaration.name} is a multi-select ${kind}, which has no entry member for element settings. Remove the annotations before its element type.`
109889
+ );
109890
+ }
109660
109891
  function inheritedMember(context, memberId) {
109661
109892
  if (memberId === null) return void 0;
109662
109893
  return context.loweredMembers.get(memberId) ?? context.baseMembers.get(memberId);
@@ -109756,7 +109987,7 @@ function overrideParentMemberId(context, memberId) {
109756
109987
  const overrides = member.modifiers.includes("override") || member.modifiers.includes("sealed");
109757
109988
  return overrides ? inheritedMemberId(context, ownerClass, member.name) : null;
109758
109989
  }
109759
- function lowerCollectionEntry(context, ownerClass, entryType, entryId, parentMemberId, declaringMemberId) {
109990
+ function lowerCollectionEntry(context, ownerClass, entryType, entryId, parentMemberId, declaringMemberId, entrySettings) {
109760
109991
  const declared = declaredCollectionEntryType(context, declaringMemberId);
109761
109992
  const expected = declared === null ? null : resolveManifestTypeInDescendant(
109762
109993
  context,
@@ -109783,7 +110014,7 @@ function lowerCollectionEntry(context, ownerClass, entryType, entryId, parentMem
109783
110014
  context.entryTypesByEntryId.set(entryId, entryDeclarationType);
109784
110015
  if (context.loweredMembers.has(entryId)) return;
109785
110016
  const base = context.baseMembers.get(entryId);
109786
- if (base) {
110017
+ if (base !== void 0 && entrySettings === "stored") {
109787
110018
  context.loweredMembers.set(base.id, {
109788
110019
  ...base,
109789
110020
  owner: { kind: "collectionEntry", parentMemberId }
@@ -109801,7 +110032,7 @@ function lowerCollectionEntry(context, ownerClass, entryType, entryId, parentMem
109801
110032
  name: "Item",
109802
110033
  type: entryDeclarationType,
109803
110034
  modifiers: [],
109804
- annotations: [],
110035
+ annotations: entryDeclarationType.annotations ?? [],
109805
110036
  initializer: null,
109806
110037
  body: null,
109807
110038
  parameters: []
@@ -109814,18 +110045,89 @@ function lowerCollectionEntry(context, ownerClass, entryType, entryId, parentMem
109814
110045
  { kind: "collectionEntry", parentMemberId },
109815
110046
  base
109816
110047
  );
110048
+ const lowered = lowerFieldMember(
110049
+ context,
110050
+ entryOwnerClass,
110051
+ declaration,
110052
+ entryId,
110053
+ common,
110054
+ base,
110055
+ entrySettings
110056
+ );
110057
+ if (base !== void 0 && entryTarget(base) !== entryTarget(lowered)) {
110058
+ throw new Error(
110059
+ `Collection member ${ownerClass.name}.${parentMemberId} declares element type ${formatManifestType(entryDeclarationType)}, but its stored entry member is a ${base.kind} of another type. An existing collection's element type cannot change; declare a new member instead.`
110060
+ );
110061
+ }
109817
110062
  context.loweredMembers.set(
109818
110063
  entryId,
109819
- lowerFieldMember(
109820
- context,
109821
- entryOwnerClass,
109822
- declaration,
109823
- entryId,
109824
- common,
109825
- base
109826
- )
110064
+ base === void 0 ? lowered : {
110065
+ ...withEntrySettings(base, lowered),
110066
+ owner: { kind: "collectionEntry", parentMemberId }
110067
+ }
109827
110068
  );
109828
110069
  }
110070
+ function entryTarget(member) {
110071
+ switch (member.kind) {
110072
+ case "class":
110073
+ return `class:${member.classId}`;
110074
+ case "enum":
110075
+ return `enum:${member.enumId}`;
110076
+ case "generic":
110077
+ return `generic:${member.genericParamId}`;
110078
+ default:
110079
+ return member.kind;
110080
+ }
110081
+ }
110082
+ function withEntrySettings(base, lowered) {
110083
+ switch (lowered.kind) {
110084
+ case "string":
110085
+ return base.kind === lowered.kind ? {
110086
+ ...base,
110087
+ localizable: lowered.localizable,
110088
+ searchKey: lowered.searchKey
110089
+ } : base;
110090
+ case "int":
110091
+ return base.kind === lowered.kind ? { ...base, min: lowered.min, max: lowered.max } : base;
110092
+ case "float":
110093
+ return base.kind === lowered.kind ? {
110094
+ ...base,
110095
+ min: lowered.min,
110096
+ max: lowered.max,
110097
+ decimalPoints: lowered.decimalPoints
110098
+ } : base;
110099
+ case "decimal":
110100
+ return base.kind === lowered.kind ? {
110101
+ ...base,
110102
+ min: lowered.min,
110103
+ max: lowered.max,
110104
+ decimalPoints: lowered.decimalPoints
110105
+ } : base;
110106
+ case "list":
110107
+ return base.kind === lowered.kind ? { ...base, listKind: lowered.listKind } : base;
110108
+ case "enum":
110109
+ return base.kind === lowered.kind ? { ...base, multiselect: lowered.multiselect } : base;
110110
+ case "lookup":
110111
+ return base.kind === lowered.kind ? {
110112
+ ...base,
110113
+ collectionMemberId: lowered.collectionMemberId,
110114
+ collectionValueId: lowered.collectionValueId,
110115
+ multiselect: lowered.multiselect
110116
+ } : base;
110117
+ case "dialogueLookup":
110118
+ return base.kind === lowered.kind ? {
110119
+ ...base,
110120
+ dialogueGroupId: lowered.dialogueGroupId,
110121
+ multiselect: lowered.multiselect
110122
+ } : base;
110123
+ case "sprite":
110124
+ return base.kind === lowered.kind ? { ...base, templateId: lowered.templateId } : base;
110125
+ case "audio":
110126
+ return base.kind === lowered.kind ? { ...base, templateId: lowered.templateId } : base;
110127
+ default:
110128
+ return base;
110129
+ }
110130
+ }
109829
110131
  function declaredCollectionEntryType(context, declaringMemberId) {
109830
110132
  const placement = context.sourceMembersById.get(declaringMemberId);
109831
110133
  if (placement === void 0) return null;
@@ -111065,7 +111367,8 @@ function lowerGenericArgument(context, ownerClass, type, memberId, genericParamI
111065
111367
  requiredTypeArgument(type, base.kind === "list" ? 0 : 1),
111066
111368
  base.entryMemberId,
111067
111369
  memberId,
111068
- memberId
111370
+ memberId,
111371
+ "stored"
111069
111372
  );
111070
111373
  }
111071
111374
  return;
@@ -111106,7 +111409,8 @@ function lowerGenericArgument(context, ownerClass, type, memberId, genericParamI
111106
111409
  storage: base?.storage ?? null,
111107
111410
  storageKey: base?.storageKey ?? null
111108
111411
  },
111109
- base
111412
+ base,
111413
+ "stored"
111110
111414
  )
111111
111415
  );
111112
111416
  }
@@ -111856,6 +112160,9 @@ function variantValueBindingsV4(manifest, analysis) {
111856
112160
  const classNames = new Map(
111857
112161
  manifest.classes.map((entry) => [entry.id, entry.name])
111858
112162
  );
112163
+ const folderPaths = new Map(
112164
+ manifest.variantFolders.map((folder) => [folder.id, folder.path])
112165
+ );
111859
112166
  return variantGlobals(analysis).flatMap((global) => {
111860
112167
  const variant2 = variantsById.get(
111861
112168
  variantRecordId(global, NEO_VARIANT_TYPE_NAME)
@@ -111877,11 +112184,31 @@ function variantValueBindingsV4(manifest, analysis) {
111877
112184
  valueId: variant2.valueId,
111878
112185
  initializer: global.initializer,
111879
112186
  source: global.source,
111880
- label: `${requiredClassName2(classNames, variant2.classId)}.${NEO_VARIANT_SCOPE_NAME}.${variant2.name}`
112187
+ label: variantLabel(
112188
+ requiredClassName2(classNames, variant2.classId),
112189
+ variant2,
112190
+ folderPaths
112191
+ )
111881
112192
  }
111882
112193
  ];
111883
112194
  });
111884
112195
  }
112196
+ function variantLabel(className, variant2, folderPaths) {
112197
+ if (variant2.folderId === null) {
112198
+ return variantPath(className, null, variant2.name);
112199
+ }
112200
+ const folderPath3 = folderPaths.get(variant2.folderId);
112201
+ if (folderPath3 === void 0) {
112202
+ throw new Error(
112203
+ `Variant ${variant2.name} sits in folder ${JSON.stringify(variant2.folderId)}, which this push does not declare.`
112204
+ );
112205
+ }
112206
+ return variantPath(className, folderPath3, variant2.name);
112207
+ }
112208
+ function variantPath(className, folderPath3, name) {
112209
+ const scope = folderPath3 === null || folderPath3 === "" ? `${className}.${NEO_VARIANT_SCOPE_NAME}` : `${className}.${NEO_VARIANT_SCOPE_NAME}.${folderPath3.split(NEO_VARIANT_FOLDER_PATH_SEPARATOR).join(".")}`;
112210
+ return `${scope}.${name}`;
112211
+ }
111885
112212
  function requiredClassName2(classNames, classId) {
111886
112213
  const name = classNames.get(classId);
111887
112214
  if (name === void 0) {
@@ -112013,8 +112340,7 @@ function buildVariantPathIndexV4(classIdsByName, analysis) {
112013
112340
  annotationsForFolderLookup(global.annotations)
112014
112341
  );
112015
112342
  const path = symbol === null ? null : foldersBySymbol.get(folderSymbolKey(className, symbol)) ?? null;
112016
- const scope = path === null || path === "" ? `${className}.${NEO_VARIANT_SCOPE_NAME}` : `${className}.${NEO_VARIANT_SCOPE_NAME}.${path.split(NEO_VARIANT_FOLDER_PATH_SEPARATOR).join(".")}`;
112017
- index.set(`${scope}.${global.name}`, {
112343
+ index.set(variantPath(className, path, global.name), {
112018
112344
  classId,
112019
112345
  variantId: variantRecordId(global, global.type.name),
112020
112346
  valueClassId: global.type.name === NEO_LOOKUP_VARIANT_TYPE_NAME ? classIdsByName.get(valueClassName(global)) ?? null : null
@@ -115252,6 +115578,8 @@ var init_root_value_paths = __esm({
115252
115578
  // src/project-source/packed-value-parity.ts
115253
115579
  function logicalValueBaseRecordsV4(records2) {
115254
115580
  let expanded = null;
115581
+ const parentByPackedId = /* @__PURE__ */ new Map();
115582
+ const rootByPackedId = /* @__PURE__ */ new Map();
115255
115583
  for (const [key, record6] of Object.entries(records2)) {
115256
115584
  if (record6.recordKind !== "value") continue;
115257
115585
  if (!isObjectRecord2(record6.data)) continue;
@@ -115263,6 +115591,12 @@ function logicalValueBaseRecordsV4(records2) {
115263
115591
  );
115264
115592
  }
115265
115593
  const decoded = decodePackedValueRow(authoredData);
115594
+ for (const [parentId, childIds] of decoded.packedChildIdsByParentId) {
115595
+ for (const childId of childIds) {
115596
+ parentByPackedId.set(childId, parentId);
115597
+ rootByPackedId.set(childId, record6.recordId);
115598
+ }
115599
+ }
115266
115600
  expanded ??= { ...records2 };
115267
115601
  expanded[key] = { ...record6, data: decoded.row };
115268
115602
  for (const child of decoded.children) {
@@ -115275,7 +115609,10 @@ function logicalValueBaseRecordsV4(records2) {
115275
115609
  };
115276
115610
  }
115277
115611
  }
115278
- return expanded ?? records2;
115612
+ return {
115613
+ records: expanded ?? records2,
115614
+ residency: { parentByPackedId, rootByPackedId }
115615
+ };
115279
115616
  }
115280
115617
  function foldCandidateValueRowsV4(args) {
115281
115618
  if (args.candidates.size === 0) return EMPTY_FOLD;
@@ -115287,7 +115624,11 @@ function foldCandidateValueRowsV4(args) {
115287
115624
  rows.push(row);
115288
115625
  inputById.set(valueId, row);
115289
115626
  }
115290
- const fold = foldPackedValueRows({ rows, document: args.schema });
115627
+ const fold = foldPackedValueRows({
115628
+ rows,
115629
+ document: args.schema,
115630
+ residency: args.residency
115631
+ });
115291
115632
  if (fold.rootByFoldedId.size === 0) return EMPTY_FOLD;
115292
115633
  const adoptedRootIds = /* @__PURE__ */ new Set();
115293
115634
  for (const [childId, rootId] of fold.rootByFoldedId) {
@@ -116388,9 +116729,7 @@ function computeWorkspaceStatus(workspace, options) {
116388
116729
  binaryFiles: []
116389
116730
  };
116390
116731
  }
116391
- const loweringRecords = logicalValueBaseRecordsV4(
116392
- loweringBaseRecords(workspace.state.records)
116393
- );
116732
+ const { records: loweringRecords, residency: packedResidency } = logicalValueBaseRecordsV4(loweringBaseRecords(workspace.state.records));
116394
116733
  const baseDocuments = schemaBaseDocuments(workspace);
116395
116734
  const staticMemberOwnershipRecoveries = [];
116396
116735
  const baseManifest = baseDocuments.length === 0 ? void 0 : documentsToProjectSchemaManifest(baseDocuments, {
@@ -116956,6 +117295,14 @@ function computeWorkspaceStatus(workspace, options) {
116956
117295
  workspace.config.projectId
116957
117296
  ),
116958
117297
  reconstructed: reconstructed3,
117298
+ changes,
117299
+ residency: packedResidency,
117300
+ rewrittenValueIds: valueLowerRegistry.localizationConversionValueIds
117301
+ });
117302
+ writeLocalizationConversionRowsV4({
117303
+ workspace,
117304
+ rootByPackedId: packedResidency.rootByPackedId,
117305
+ valueIds: valueLowerRegistry.localizationConversionValueIds,
116959
117306
  changes
116960
117307
  });
116961
117308
  reportPhase("packed-value-fold", "Checking initializers\u2026");
@@ -117590,7 +117937,7 @@ function applyPackedValueFoldV4(args) {
117590
117937
  candidates.set(record6.recordId, record6.fullData);
117591
117938
  const logicalBase = args.loweringRecords[key];
117592
117939
  const physicalBase = args.workspace.state.records[key];
117593
- if (logicalBase === void 0 || physicalBase === void 0 || physicalBase.conflictServerHash !== void 0 || isPendingId(record6.recordId) || !recordsSemanticallyEqual("value", record6.fullData, logicalBase.data)) {
117940
+ if (logicalBase === void 0 || physicalBase === void 0 || physicalBase.conflictServerHash !== void 0 || isPendingId(record6.recordId) || args.rewrittenValueIds.has(record6.recordId) || !recordsSemanticallyEqual("value", record6.fullData, logicalBase.data)) {
117594
117941
  touchedValueIds.add(record6.recordId);
117595
117942
  }
117596
117943
  }
@@ -117598,6 +117945,7 @@ function applyPackedValueFoldV4(args) {
117598
117945
  candidates,
117599
117946
  base: args.loweringRecords,
117600
117947
  physicalBase: args.workspace.state.records,
117948
+ residency: args.residency,
117601
117949
  touchedValueIds,
117602
117950
  projectId: args.workspace.config.projectId,
117603
117951
  schema: args.schema
@@ -117682,6 +118030,32 @@ function applyPackedValueFoldV4(args) {
117682
118030
  args.reconstructed.set(key, { ...record6, fullData: packedBody });
117683
118031
  }
117684
118032
  }
118033
+ function writeLocalizationConversionRowsV4(args) {
118034
+ if (args.valueIds.size === 0) return;
118035
+ const records2 = args.workspace.state.records;
118036
+ const written = new Set(
118037
+ args.changes.map(
118038
+ (change) => recordStateKey(change.recordKind, change.recordId)
118039
+ )
118040
+ );
118041
+ for (const valueId of args.valueIds) {
118042
+ const rootId = args.rootByPackedId.get(valueId) ?? valueId;
118043
+ const key = recordStateKey("value", rootId);
118044
+ if (written.has(key)) continue;
118045
+ const base = records2[key];
118046
+ if (base === void 0 || !isObjectRecord2(base.data)) continue;
118047
+ written.add(key);
118048
+ args.changes.push(
118049
+ recordUpsertChange({
118050
+ base,
118051
+ recordKind: "value",
118052
+ recordId: base.recordId,
118053
+ file: base.file ?? null,
118054
+ nextData: base.data
118055
+ })
118056
+ );
118057
+ }
118058
+ }
117685
118059
  function packedFoldSchemaV4(documents, base, projectId) {
117686
118060
  const seedEnvelope = { projectId, createdAt: 0, updatedAt: 0 };
117687
118061
  const completed = /* @__PURE__ */ new Map();
@@ -120235,6 +120609,7 @@ function createValueLowerRegistryV4(options = {}) {
120235
120609
  pendingContainerChildCounts: /* @__PURE__ */ new Map(),
120236
120610
  pendingLocalizedTexts: /* @__PURE__ */ new Map(),
120237
120611
  pendingBindingMembersByClassId: /* @__PURE__ */ new Map(),
120612
+ localizationConversionValueIds: /* @__PURE__ */ new Set(),
120238
120613
  referenceObligations: [],
120239
120614
  initAuthoredRowIds: /* @__PURE__ */ new Map(),
120240
120615
  loweringFailures: [],
@@ -120354,6 +120729,7 @@ function buildValueLowerContext(state, manifest, options = {}) {
120354
120729
  pendingValues: registry.pendingValues,
120355
120730
  pendingContainerChildCounts: registry.pendingContainerChildCounts,
120356
120731
  pendingLocalizedTexts: registry.pendingLocalizedTexts,
120732
+ localizationConversionValueIds: registry.localizationConversionValueIds,
120357
120733
  loweredMemberIdByValueId: /* @__PURE__ */ new Map(),
120358
120734
  storedClassSchemaMemberIds: /* @__PURE__ */ new Map(),
120359
120735
  pendingBindingMembersByClassId: registry.pendingBindingMembersByClassId,
@@ -123204,6 +123580,10 @@ function retainStoredValueSubgraph(context, valueId, source, visited, member, en
123204
123580
  `Localizable value ${valueId} has no existing localized-text link.`
123205
123581
  );
123206
123582
  }
123583
+ if (pulledStringMemberIsPlain(context.state, owningMember.id)) {
123584
+ context.localizationConversionValueIds.add(valueId);
123585
+ return;
123586
+ }
123207
123587
  const textState = context.state[`localized-text:${textId}`];
123208
123588
  if (!isObjectRecord2(textState?.data)) {
123209
123589
  throw new Error(`Localized text ${textId} was not pulled.`);
@@ -123954,6 +124334,10 @@ function lowerStringValue(context, member, expression, base, source) {
123954
124334
  `Localizable value ${String(base.id)} has no existing localized-text link.`
123955
124335
  );
123956
124336
  }
124337
+ if (pulledStringMemberIsPlain(context.state, member.id)) {
124338
+ context.localizationConversionValueIds.add(String(base.id));
124339
+ return expression.value;
124340
+ }
123957
124341
  return lowerLinkedLocalizedText(
123958
124342
  context,
123959
124343
  base.value,
@@ -123961,6 +124345,19 @@ function lowerStringValue(context, member, expression, base, source) {
123961
124345
  source
123962
124346
  );
123963
124347
  }
124348
+ function pulledStringMemberIsPlain(state, memberId) {
124349
+ const visited = /* @__PURE__ */ new Set();
124350
+ let id2 = memberId;
124351
+ while (typeof id2 === "string" && !visited.has(id2)) {
124352
+ visited.add(id2);
124353
+ const data = state[`member:${id2}`]?.data;
124354
+ if (!isObjectRecord2(data)) return false;
124355
+ if (data.format !== void 0)
124356
+ return data.format === 1 /* Plain */;
124357
+ id2 = data.extendsMemberId;
124358
+ }
124359
+ return false;
124360
+ }
123964
124361
  function lowerLinkedLocalizedText(context, textId, value, source) {
123965
124362
  const textState = context.state[`localized-text:${textId}`];
123966
124363
  if (!isObjectRecord2(textState?.data)) {
@@ -127607,7 +128004,7 @@ var init_project_document_cache = __esm({
127607
128004
  init_src();
127608
128005
  init_project_documents();
127609
128006
  PROJECT_SOURCE_BUILD_CACHE_PATH = ".neo/build/project-source-analysis-v4.json";
127610
- PROJECT_SOURCE_BUILD_CACHE_REVISION = 1;
128007
+ PROJECT_SOURCE_BUILD_CACHE_REVISION = 2;
127611
128008
  PROJECT_SOURCE_DOCUMENT_BUILD_CACHE_PATH = ".neo/build/project-source-documents-v1.json";
127612
128009
  PROJECT_SOURCE_DOCUMENT_BUILD_CACHE_REVISION = 1;
127613
128010
  }
@@ -138844,7 +139241,7 @@ var init_registry2 = __esm({
138844
139241
  "schema-contract/registry.mjs"() {
138845
139242
  "use strict";
138846
139243
  PROJECT_SCHEMA_CONTRACT = Object.freeze({
138847
- cliVersion: "0.54.0",
139244
+ cliVersion: "0.55.0",
138848
139245
  projectFileUploadBatchSize: 32,
138849
139246
  documentRecords: {
138850
139247
  member: {
@@ -146665,7 +147062,7 @@ There is no --keep-current: preserving current semantic state defines flatten.
146665
147062
  async function main() {
146666
147063
  const args = parseArgs(process.argv.slice(2));
146667
147064
  if (args.command === "--version") {
146668
- console.log("0.54.0");
147065
+ console.log("0.55.0");
146669
147066
  return;
146670
147067
  }
146671
147068
  if (args.command === null || args.command === "help" || args.command === "--help" || args.command === "-h") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neocompose/cli",
3
- "version": "0.54.0",
3
+ "version": "0.55.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.54.0 -->
12
+ <!-- reviewed-through-cli: 0.55.0 -->
13
13
 
14
14
  # Neo Compose CLI
15
15
 
@@ -97,7 +97,7 @@ wrappers.
97
97
  The marker near the top of `SKILL.md` must exactly match the package version:
98
98
 
99
99
  ```html
100
- <!-- reviewed-through-cli: 0.54.0 -->
100
+ <!-- reviewed-through-cli: 0.55.0 -->
101
101
  ```
102
102
 
103
103
  The quoted version above is checked too, so this instruction cannot go stale
@@ -85,6 +85,10 @@ Use context-aware typed annotations:
85
85
  - `@visibility(.Hidden)` only on a class; absence means visible.
86
86
  - Stateful settings use contextual enum cases, such as
87
87
  `@settings(format: .Plain)` and `@settings(selection: .Multi)`.
88
+ - `@settings(...)` before a `List` element or `Dictionary` value type
89
+ configures the collection's entries: `List<@settings(format: .Plain) string>`,
90
+ `Dictionary<string, @settings(min: 0) int>`. Only the member that declares
91
+ the collection spells them; overrides share its entries.
88
92
  - `@relations(...)` only on the source class of a specialized direct relation.
89
93
  - `@distribution(kind: .Sparse | .Packed)` only on a value-owning member
90
94
  declaration or override. It chooses whether the member's value keeps its