@neocompose/cli 0.48.1 → 0.48.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.48.3] - 2026-09-10
4
+
5
+ - Show inherited generic binding defaults in implicit constructions, so sparse
6
+ instances expose their effective values and no longer warn about unsettled
7
+ required members. Matching defaults remain virtual through pull/status and
8
+ edits create ordinary sparse overrides. Recorded constructors keep their
9
+ own settlement behavior.
10
+ - Preserve hidden collection argument defaults and ordering when lowering
11
+ unchanged generic types.
12
+
13
+ ## [0.48.2] - 2026-09-10
14
+
15
+ - Emit class member defaults with contextual `new()` when they construct the declared type, while keeping an explicit concrete type for subclass defaults.
16
+
3
17
  ## [0.48.1] - 2026-09-10
4
18
 
5
19
  - Emit empty class member defaults as `new Type()` even when their backing value row is virtual. Required members now read as initialized, and adding a `new() { ... }` override preserves sparse storage.
package/dist/neo.mjs CHANGED
@@ -64603,6 +64603,22 @@ function manifestTypeForMember(context, member) {
64603
64603
  }
64604
64604
  return primitive3;
64605
64605
  }
64606
+ if (member.kind === "list" || member.kind === "dictionary") {
64607
+ const entry = context.baseMembers.get(member.entryMemberId);
64608
+ if (entry === void 0)
64609
+ throw new Error(
64610
+ `Collection ${member.name} has no entry member ${member.entryMemberId}.`
64611
+ );
64612
+ const entryType = manifestTypeForMember(context, entry);
64613
+ if (member.kind === "list")
64614
+ return { name: "List", nullable: nullable2, arguments: [entryType] };
64615
+ const keyType = {
64616
+ name: member.key.kind === "string" ? "string" : context.baseEnums.get(member.key.enumId)?.name ?? "object",
64617
+ nullable: false,
64618
+ arguments: []
64619
+ };
64620
+ return { name: "Dictionary", nullable: nullable2, arguments: [keyType, entryType] };
64621
+ }
64606
64622
  if (member.kind === "class") {
64607
64623
  const schemaClass2 = context.baseClasses.get(member.classId);
64608
64624
  const classType = {
@@ -64848,6 +64864,16 @@ function lowerGenericArgument(context, ownerClass, type, memberId, genericParamI
64848
64864
  const base = context.baseMembers.get(memberId);
64849
64865
  if (base && JSON.stringify(manifestTypeForMember(context, base)) === JSON.stringify(type)) {
64850
64866
  context.loweredMembers.set(memberId, { ...base, owner });
64867
+ if (base.kind === "list" || base.kind === "dictionary") {
64868
+ lowerCollectionEntry(
64869
+ context,
64870
+ ownerClass,
64871
+ requiredTypeArgument(type, base.kind === "list" ? 0 : 1),
64872
+ base.entryMemberId,
64873
+ memberId,
64874
+ memberId
64875
+ );
64876
+ }
64851
64877
  return;
64852
64878
  }
64853
64879
  const declaration = {
@@ -113263,6 +113289,7 @@ function buildValueEmitContext(records2, manifest) {
113263
113289
  },
113264
113290
  members,
113265
113291
  classes,
113292
+ implicitGenericMembersByClassId: /* @__PURE__ */ new Map(),
113266
113293
  manifestMembers,
113267
113294
  manifestClasses: new Map(
113268
113295
  manifest.classes.map((schemaClass2) => [schemaClass2.id, schemaClass2])
@@ -113680,9 +113707,7 @@ function emitMemberDefaultSourcesV4(records2, manifest) {
113680
113707
  member,
113681
113708
  { classId: defaultValue.classId ?? null, value: backed.body },
113682
113709
  visited,
113683
- (defaultValue.classId ?? member.classId) === member.classId && manifestMember?.kind === "class" && Object.values(manifestMember.classArguments).some(
113684
- (argument2) => argument2.kind === "generic"
113685
- ),
113710
+ (defaultValue.classId ?? member.classId) === member.classId && manifestMember?.kind === "class",
113686
113711
  environment
113687
113712
  );
113688
113713
  } else if (backed.kind === "list") {
@@ -116499,18 +116524,32 @@ function lowerClassValue(context, member, expression, base, source, outerEnviron
116499
116524
  `Class value ${String(base.id)}.${assignment.name} cannot place existing value ${symbolId2} into a new structural slot. Create the nested value inline so the atomic construction transaction can own it.`
116500
116525
  );
116501
116526
  }
116502
- body[assignment.name] = lowerSeedChild(
116527
+ const rows = /* @__PURE__ */ new Map();
116528
+ const localizedTexts = /* @__PURE__ */ new Map();
116529
+ const seededId = lowerSeedChild(
116503
116530
  context,
116504
116531
  recursivePartialMember(member, childMember),
116505
116532
  assignment.value,
116506
116533
  source,
116507
116534
  `${String(base.id)}.${assignment.name}`,
116508
- /* @__PURE__ */ new Map(),
116509
- /* @__PURE__ */ new Map(),
116535
+ rows,
116536
+ localizedTexts,
116510
116537
  void 0,
116511
116538
  environment,
116512
116539
  assignmentSlices.get(assignment.name)
116513
116540
  );
116541
+ if (!partial && freshChildReplaysDeclaredDefault({
116542
+ context,
116543
+ childMember,
116544
+ childId: seededId,
116545
+ rows,
116546
+ localizedTexts,
116547
+ environment
116548
+ })) {
116549
+ deleteSeedRow(context, rows, seededId);
116550
+ continue;
116551
+ }
116552
+ body[assignment.name] = seededId;
116514
116553
  continue;
116515
116554
  }
116516
116555
  const symbolId = sourceValueSymbol(context, assignment.value);
@@ -118311,23 +118350,33 @@ function freshChildReplaysDeclaredDefault(args) {
118311
118350
  if (child.instanceVariantId != null) return false;
118312
118351
  if (child.instanceVariantRowValueId != null) return false;
118313
118352
  if (context.collectionBindingMemberIds.has(childMember.id)) return false;
118314
- const defaultValue = childMember.defaultValue;
118315
- if (defaultValue === null || defaultValue === void 0) return false;
118316
- if (!("value" in defaultValue)) return false;
118317
118353
  const resolved = resolveGenericSchemaMember(
118318
118354
  context,
118319
118355
  childMember,
118320
118356
  environment
118321
118357
  );
118358
+ let defaultValue = null;
118359
+ if (resolved.defaultValue && "value" in resolved.defaultValue) {
118360
+ defaultValue = resolved.defaultValue;
118361
+ } else if (resolved.defaultValue && "serverValueId" in resolved.defaultValue) {
118362
+ const storedMember = context.state[`member:${resolved.id}`]?.data;
118363
+ const storedDefault = isObjectRecord2(storedMember) ? storedMember.defaultValue : null;
118364
+ if (isObjectRecord2(storedDefault) && "value" in storedDefault) {
118365
+ defaultValue = {
118366
+ value: storedDefault.value,
118367
+ classId: storedDefault.classId
118368
+ };
118369
+ }
118370
+ }
118371
+ if (defaultValue === null) return false;
118322
118372
  if (resolved.kind === "lookup" || resolved.kind === "dialogueLookup") {
118323
118373
  return false;
118324
118374
  }
118325
118375
  if (resolved.kind === "class") {
118326
- return freshConstructionReplaysDeclaredDefault(
118327
- resolved,
118328
- child,
118329
- defaultValue
118330
- );
118376
+ return freshConstructionReplaysDeclaredDefault(resolved, child, {
118377
+ value: defaultValue.value,
118378
+ classId: defaultValue.classId
118379
+ });
118331
118380
  }
118332
118381
  if (resolved.kind === "list") {
118333
118382
  return Array.isArray(child.value) && child.value.length === 0 && !context.pendingContainerChildCounts.has(childId) && Array.isArray(defaultValue.value) && defaultValue.value.length === 0 && child.classId == null && (child.constructorArgs == null || Object.keys(child.constructorArgs).length === 0) && child.instanceConstructorId == null;
@@ -118905,6 +118954,41 @@ function classValue(context, member, value, visited, targetTyped, outerEnvironme
118905
118954
  );
118906
118955
  overrideKeys.add(key);
118907
118956
  }
118957
+ if (!partial && value.instanceVariantId == null && value.instanceVariantRowValueId == null) {
118958
+ for (const [key, genericMember] of implicitGenericMembers(
118959
+ context,
118960
+ classId
118961
+ )) {
118962
+ if (key in value.value) continue;
118963
+ const parameterId = stringField3(genericMember, "genericParamId");
118964
+ if (!environment.has(parameterId)) continue;
118965
+ const binding = resolveGenericValueMember(
118966
+ context,
118967
+ genericMember,
118968
+ environment
118969
+ );
118970
+ const kind = numberField(binding, "kind");
118971
+ if (!IMPLICIT_GENERIC_DEFAULT_KINDS.has(kind)) continue;
118972
+ if (kind === 3 /* String */ && isLocalizedStringMember(binding))
118973
+ continue;
118974
+ if (binding.payload === 1 /* Partial */) continue;
118975
+ const defaultValue = binding.defaultValue;
118976
+ if (!isObjectRecord2(defaultValue) || !("value" in defaultValue)) continue;
118977
+ const body = defaultValue.value;
118978
+ if (body === null) continue;
118979
+ if (Array.isArray(body) && body.length > 0) continue;
118980
+ if (isObjectRecord2(body) && Object.keys(body).length > 0) continue;
118981
+ const expression = emitValueBody(
118982
+ context,
118983
+ binding,
118984
+ defaultValue,
118985
+ /* @__PURE__ */ new Set(),
118986
+ (defaultValue.classId ?? binding.classId) === binding.classId,
118987
+ environment
118988
+ );
118989
+ fields.push(`${key} = ${expression}`);
118990
+ }
118991
+ }
118908
118992
  if (partial) {
118909
118993
  return fields.length === 0 ? "new { }" : `new {
118910
118994
  ${fields.map((field) => indentNeoSourceNonEmptyLines(field, 2)).join(",\n")}
@@ -118923,6 +119007,33 @@ ${fields.map((field) => indentNeoSourceNonEmptyLines(field, 2)).join(",\n")}
118923
119007
  ${fields.map((field) => indentNeoSourceNonEmptyLines(field, 2)).join(",\n")}
118924
119008
  }`;
118925
119009
  }
119010
+ function implicitGenericMembers(context, classId) {
119011
+ const cached = context.implicitGenericMembersByClassId.get(classId);
119012
+ if (cached !== void 0) return cached;
119013
+ const result = /* @__PURE__ */ new Map();
119014
+ const seenKeys = /* @__PURE__ */ new Set();
119015
+ const seenClasses = /* @__PURE__ */ new Set();
119016
+ let current = context.manifestClasses.get(classId);
119017
+ while (current !== void 0 && !seenClasses.has(current.id)) {
119018
+ seenClasses.add(current.id);
119019
+ if (current.requiredConstructorId || current.constructorIds?.length) {
119020
+ result.clear();
119021
+ context.implicitGenericMembersByClassId.set(classId, result);
119022
+ return result;
119023
+ }
119024
+ for (const [key, memberId] of Object.entries(current.schema)) {
119025
+ if (seenKeys.has(key)) continue;
119026
+ seenKeys.add(key);
119027
+ const member = context.members.get(memberId);
119028
+ if (member?.kind === 21 /* Generic */ && !isObjectRecord2(member.init)) {
119029
+ result.set(key, member);
119030
+ }
119031
+ }
119032
+ current = current.extendsClassId === null ? void 0 : context.manifestClasses.get(current.extendsClassId);
119033
+ }
119034
+ context.implicitGenericMembersByClassId.set(classId, result);
119035
+ return result;
119036
+ }
118926
119037
  function emitsAsMemberDefaultDerivedAbsence(context, member, valueId) {
118927
119038
  const value = context.values.get(valueId);
118928
119039
  if (value === void 0) return false;
@@ -120458,7 +120569,7 @@ function memberDefaultBody(member) {
120458
120569
  if (!("value" in defaultValue)) return null;
120459
120570
  return defaultValue.value;
120460
120571
  }
120461
- var INFERRED_GENERIC_CLASS_PREFIX, MEMBER_KIND_DICTIONARY2, MEMBER_KIND_LIST2, MEMBER_KIND_CLASS2, LEADING_AUTHORED_ROW_ID, CANONICAL_CONSTRUCTOR_INLINE_WIDTH;
120572
+ var INFERRED_GENERIC_CLASS_PREFIX, MEMBER_KIND_DICTIONARY2, MEMBER_KIND_LIST2, MEMBER_KIND_CLASS2, IMPLICIT_GENERIC_DEFAULT_KINDS, LEADING_AUTHORED_ROW_ID, CANONICAL_CONSTRUCTOR_INLINE_WIDTH;
120462
120573
  var init_value_sources = __esm({
120463
120574
  "src/project-source/value-sources.ts"() {
120464
120575
  "use strict";
@@ -120498,6 +120609,16 @@ var init_value_sources = __esm({
120498
120609
  MEMBER_KIND_DICTIONARY2 = 5;
120499
120610
  MEMBER_KIND_LIST2 = 6;
120500
120611
  MEMBER_KIND_CLASS2 = 7;
120612
+ IMPLICIT_GENERIC_DEFAULT_KINDS = /* @__PURE__ */ new Set([
120613
+ 1 /* Bool */,
120614
+ 2 /* Int */,
120615
+ 4 /* Float */,
120616
+ 20 /* Decimal */,
120617
+ 3 /* String */,
120618
+ 7 /* Class */,
120619
+ 6 /* List */,
120620
+ 5 /* Dictionary */
120621
+ ]);
120501
120622
  LEADING_AUTHORED_ROW_ID = /^@id\(\s*"((?:[^"\\]|\\.)*)"\s*\)/;
120502
120623
  CANONICAL_CONSTRUCTOR_INLINE_WIDTH = 88;
120503
120624
  }
@@ -131456,7 +131577,7 @@ var init_registry2 = __esm({
131456
131577
  "schema-contract/registry.mjs"() {
131457
131578
  "use strict";
131458
131579
  PROJECT_SCHEMA_CONTRACT = Object.freeze({
131459
- cliVersion: "0.48.1",
131580
+ cliVersion: "0.48.3",
131460
131581
  projectFileUploadBatchSize: 32,
131461
131582
  documentRecords: {
131462
131583
  member: {
@@ -138506,7 +138627,7 @@ There is no --keep-current: preserving current semantic state defines flatten.
138506
138627
  async function main() {
138507
138628
  const args = parseArgs(process.argv.slice(2));
138508
138629
  if (args.command === "--version") {
138509
- console.log("0.48.1");
138630
+ console.log("0.48.3");
138510
138631
  return;
138511
138632
  }
138512
138633
  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.48.1",
3
+ "version": "0.48.3",
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.48.1 -->
12
+ <!-- reviewed-through-cli: 0.48.3 -->
13
13
 
14
14
  # Neo Compose CLI
15
15
 
@@ -88,7 +88,7 @@ wrappers.
88
88
  The marker near the top of `SKILL.md` must exactly match the package version:
89
89
 
90
90
  ```html
91
- <!-- reviewed-through-cli: 0.48.1 -->
91
+ <!-- reviewed-through-cli: 0.48.3 -->
92
92
  ```
93
93
 
94
94
  The quoted version above is checked too, so this instruction cannot go stale
@@ -121,12 +121,14 @@ membership rules as member defaults.
121
121
 
122
122
  ## Defaults and localization
123
123
 
124
- A class member with an empty construction default emits `= new Type()` even
125
- when its value row is virtual. Edit that declaration with `new() { ... }` to
126
- change the shared default, or assign the member inside an instance initializer
127
- to override only that instance. Leaving the emitted construction unchanged
128
- creates no redundant value rows. A nullable member with no default remains bare;
129
- an explicit null default emits `= null`.
124
+ A class member whose default constructs its declared type emits a contextual
125
+ `= new()`, including populated defaults and defaults whose value row is
126
+ virtual. A default that constructs a subclass keeps the concrete type, such as
127
+ `Base Value = new Derived();`. Edit the declaration to change the shared
128
+ default, or assign the member inside an instance initializer to override only
129
+ that instance. Leaving the emitted construction unchanged creates no redundant
130
+ value rows. A nullable member with no default remains bare; an explicit null
131
+ default emits `= null`.
130
132
 
131
133
  Omitted members are virtual projections of the instance's recorded constructor
132
134
  or variant. Later edits to that source update every omitted member. Writing a
@@ -134,6 +136,12 @@ member creates a sparse override with its deterministic projected ID; removing
134
136
  that member from an existing initializer resets the override and resumes source
135
137
  tracking. Require either transition to appear in `neo diff`.
136
138
 
139
+ A concrete generic argument can carry a default that its type spelling does
140
+ not show. Pull writes nonlocalized scalar and empty aggregate binding defaults at implicit
141
+ construction sites, for example `DefaultData = new()`. Keeping that expression
142
+ unchanged or omitting it retains the virtual default; editing it creates an
143
+ override. A default that selects a subtype keeps the explicit subtype name.
144
+
137
145
  Editing an initializer updates sparse override rows in place: projected member
138
146
  IDs remain stable, and omitted virtual rows are never emitted as authored seed
139
147
  records. Spelling a declared default verbatim in a new initializer lowers to