@neocompose/cli 0.68.3 → 0.68.4

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,9 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.68.4] - 2026-10-05
4
+
5
+ - An empty header base clause such as `class Child(string name) : Base() { Name = name } {}` uses the base's member initializers when its declared constructors require arguments. The base block must settle remaining required members. Zero-argument and fully defaulted base constructors still run; required base constructors and declared `: base()` calls still require valid arguments. Fixes [#1186](https://github.com/ryanbliss/neo-compose/issues/1186). Use SDK 0.57.4 for matching Unity runtime behavior.
6
+
3
7
  ## [0.68.3] - 2026-10-05
4
8
 
5
9
  - `neo push --dry-run` preserves valid authored UUID-v4 IDs on new nested member and dictionary values, including values named by local declarations. Previously, verification dropped the ID and failed with an internal "has no pending id assignment" error. Status and push report malformed new member-value IDs at the annotation, name the affected `Class.Member`, and suggest removing `@id` or providing an unused UUID v4 ([#1189](https://github.com/ryanbliss/neo-compose/issues/1189)).
package/dist/neo.mjs CHANGED
@@ -22862,6 +22862,11 @@ var init_project_source_variants = __esm({
22862
22862
  });
22863
22863
 
22864
22864
  // ../packages/neoscript-language/src/project-source-settlement.ts
22865
+ function requiredConstructorParameters(declaration) {
22866
+ if (declaration.headerParameters !== void 0)
22867
+ return declaration.headerParameters;
22868
+ return declaration.initBlocks.length > 0 ? [] : void 0;
22869
+ }
22865
22870
  function buildNeoConstructionIndex(documents) {
22866
22871
  const classes = /* @__PURE__ */ new Map();
22867
22872
  const interfaceNames = /* @__PURE__ */ new Set();
@@ -22933,8 +22938,9 @@ function unsettledRequiredMembers(index, className, settledNames, typeArguments
22933
22938
  function classConstructionContract(index, className) {
22934
22939
  const declaration = index.classes.get(className)?.declaration;
22935
22940
  if (!declaration) return null;
22936
- if (declaration.headerParameters !== void 0) {
22937
- return { kind: "required", parameters: declaration.headerParameters };
22941
+ const parameters = requiredConstructorParameters(declaration);
22942
+ if (parameters !== void 0) {
22943
+ return { kind: "required", parameters };
22938
22944
  }
22939
22945
  if (declaration.constructors.length > 0) {
22940
22946
  return { kind: "declared", constructors: declaration.constructors };
@@ -22998,7 +23004,7 @@ function validateBaseConstruction(index, entry, diagnostics) {
22998
23004
  }
22999
23005
  for (const diagnostic of validateNeoConstructionSite(index, {
23000
23006
  className: baseClause.type.name,
23001
- site: "base",
23007
+ site: "headerBase",
23002
23008
  typeArguments: baseClause.type.typeArguments.map(typeShape),
23003
23009
  argumentNames: (baseClause.arguments ?? []).map(
23004
23010
  (argument2) => argument2.name ?? null
@@ -23024,11 +23030,10 @@ function validateBaseConstruction(index, entry, diagnostics) {
23024
23030
  }
23025
23031
  }
23026
23032
  function validateUnconstructedBase(entry, base, range2, diagnostics) {
23027
- const parameters = base.declaration.headerParameters;
23033
+ const parameters = requiredConstructorParameters(base.declaration);
23028
23034
  if (parameters === void 0) {
23029
- if (entry.declaration.headerParameters === void 0 && entry.declaration.initBlocks.length === 0)
23030
- return;
23031
- pushBaseCall(entry, base, [], () => range2, diagnostics);
23035
+ if (requiredConstructorParameters(entry.declaration) === void 0) return;
23036
+ pushBaseCall(entry, base, [], () => range2, diagnostics, "headerBase");
23032
23037
  return;
23033
23038
  }
23034
23039
  if (parameterListIsFullyDefaulted(parameters)) return;
@@ -23047,7 +23052,7 @@ function validateUnconstructedBase(entry, base, range2, diagnostics) {
23047
23052
  function validateDeclaredBaseCalls(entry, base, diagnostics) {
23048
23053
  for (const constructor2 of entry.declaration.constructors) {
23049
23054
  const baseArguments = constructor2.baseArguments ?? [];
23050
- if (constructor2.baseArguments === void 0 && base.declaration.headerParameters !== void 0) {
23055
+ if (constructor2.baseArguments === void 0 && requiredConstructorParameters(base.declaration) !== void 0) {
23051
23056
  continue;
23052
23057
  }
23053
23058
  pushBaseCall(
@@ -23062,13 +23067,13 @@ function validateDeclaredBaseCalls(entry, base, diagnostics) {
23062
23067
  );
23063
23068
  }
23064
23069
  }
23065
- function pushBaseCall(entry, base, argumentNames, rangeOf, diagnostics) {
23070
+ function pushBaseCall(entry, base, argumentNames, rangeOf, diagnostics, site = "base") {
23066
23071
  const siteDiagnostics = [];
23067
23072
  resolveInvokedConstructor(
23068
23073
  base.declaration,
23069
23074
  {
23070
23075
  className: base.declaration.name,
23071
- site: "base",
23076
+ site,
23072
23077
  argumentNames,
23073
23078
  initializerNames: []
23074
23079
  },
@@ -23127,7 +23132,7 @@ function validateDeclaredConstructionPaths(index, entry, diagnostics) {
23127
23132
  []
23128
23133
  ).filter((member) => owned || member.declaringClassName === declaration.name);
23129
23134
  if (outstanding.length === 0) return;
23130
- if (declaration.headerParameters !== void 0) {
23135
+ if (requiredConstructorParameters(declaration) !== void 0) {
23131
23136
  const initBlock = declaration.initBlocks[0];
23132
23137
  const assigned = assignedOnEveryPath(initBlock?.body.text ?? "{}");
23133
23138
  const unsettled = filterUnassigned(outstanding, assigned);
@@ -23168,13 +23173,30 @@ function filterUnassigned(members, assigned) {
23168
23173
  }
23169
23174
  function declarationSettledMembers(index, entry) {
23170
23175
  const settled = /* @__PURE__ */ new Set();
23171
- const baseClause = entry.declaration.baseClause;
23172
- if (!baseClause) return settled;
23173
- for (const field of baseClause.initializerFields ?? []) {
23176
+ const declaration = entry.declaration;
23177
+ const baseClause = declaration.baseClause;
23178
+ for (const field of baseClause?.initializerFields ?? []) {
23174
23179
  settled.add(field.name.toLowerCase());
23175
23180
  }
23176
- if ((baseClause.arguments ?? []).length === 0) return settled;
23177
- for (const member of requiredMembersForClass(index, baseClause.type.name)) {
23181
+ if (baseClause === void 0 && requiredConstructorParameters(declaration) === void 0)
23182
+ return settled;
23183
+ const baseName = baseClause?.type.name ?? declaration.baseTypes[0]?.name;
23184
+ const base = baseName === void 0 ? void 0 : index.classes.get(baseName);
23185
+ if (base === void 0) return settled;
23186
+ if (!resolveInvokedConstructor(
23187
+ base.declaration,
23188
+ {
23189
+ className: base.declaration.name,
23190
+ site: "headerBase",
23191
+ argumentNames: (baseClause?.arguments ?? []).map(
23192
+ (argument2) => argument2.name ?? null
23193
+ ),
23194
+ initializerNames: []
23195
+ },
23196
+ []
23197
+ ))
23198
+ return settled;
23199
+ for (const member of requiredMembersForClass(index, base.declaration.name)) {
23178
23200
  settled.add(member.name.toLowerCase());
23179
23201
  }
23180
23202
  return settled;
@@ -23192,7 +23214,7 @@ function baseClauseRange(declaration, anchor) {
23192
23214
  return baseClause.range;
23193
23215
  }
23194
23216
  function resolveInvokedConstructor(declaration, call, diagnostics) {
23195
- const headerParameters = declaration.headerParameters;
23217
+ const headerParameters = requiredConstructorParameters(declaration);
23196
23218
  if (headerParameters !== void 0) {
23197
23219
  if (call.argumentNames.length > 0) {
23198
23220
  validateArgumentsAgainstParameters(
@@ -23228,7 +23250,7 @@ function resolveInvokedConstructor(declaration, call, diagnostics) {
23228
23250
  const parameterless = declaration.constructors.some(
23229
23251
  (constructor2) => parameterListIsFullyDefaulted(constructor2.parameters)
23230
23252
  );
23231
- if (parameterless || call.site === "new") return parameterless;
23253
+ if (parameterless || call.site !== "base") return parameterless;
23232
23254
  diagnostics.push({
23233
23255
  code: "base-requires-construction",
23234
23256
  message: `The base call must pass arguments: '${declaration.name}' declares no parameterless constructor \u2014 candidates: ${describeConstructors(declaration)}.`,
@@ -23245,7 +23267,7 @@ function resolveInvokedConstructor(declaration, call, diagnostics) {
23245
23267
  );
23246
23268
  return true;
23247
23269
  }
23248
- if (call.site === "base") {
23270
+ if (call.site !== "new") {
23249
23271
  let positional = false;
23250
23272
  call.argumentNames.forEach((name, index) => {
23251
23273
  if (name !== null) return;
@@ -31690,7 +31712,7 @@ var init_registry = __esm({
31690
31712
  "../packages/manifest/schema-contract/registry.mjs"() {
31691
31713
  "use strict";
31692
31714
  PROJECT_SCHEMA_CONTRACT = Object.freeze({
31693
- cliVersion: "0.68.3",
31715
+ cliVersion: "0.68.4",
31694
31716
  projectFileUploadBatchSize: 32,
31695
31717
  documentRecords: {
31696
31718
  member: {
@@ -79315,6 +79337,8 @@ function resolveBaseConstructorRecord(record6, ctx) {
79315
79337
  );
79316
79338
  if (resolution.kind === "resolved") return resolution.overload;
79317
79339
  if (resolution.kind === "noMatch" && baseArguments.length === 0) {
79340
+ if (owningClass.requiredConstructorId === record6.id && evaluatorSchemaIndex(ctx).class(baseClassId)?.requiredConstructorId == null)
79341
+ return null;
79318
79342
  throw new NSGetterRuntimeError(
79319
79343
  `Constructor '${record6.id}' on '${owningClass.name}' must call ': base(...)': its base class declares constructors but none parameterless.`
79320
79344
  );
@@ -88861,6 +88885,8 @@ function resolveBaseConstructorOverload(args, owner) {
88861
88885
  );
88862
88886
  if (resolution.kind === "resolved") return resolution.overload;
88863
88887
  if (resolution.kind === "noMatch" && baseArguments.length === 0) {
88888
+ if (owner.requiredConstructorId === args.constructor.id && baseClass.requiredConstructorId == null)
88889
+ return null;
88864
88890
  throw new Error(
88865
88891
  `Constructor "${args.constructor.id}" must declare a base call: base class "${baseClass.name}" declares constructors but none parameterless \u2014 candidates: ${describeConstructorOverloads(candidates)}.`
88866
88892
  );
@@ -165112,7 +165138,7 @@ There is no --keep-current: preserving current semantic state defines flatten.
165112
165138
  async function main() {
165113
165139
  const args = parseArgs(process.argv.slice(2));
165114
165140
  if (args.command === "--version") {
165115
- console.log("0.68.3");
165141
+ console.log("0.68.4");
165116
165142
  return;
165117
165143
  }
165118
165144
  if (args.command === null || args.command === "help" || args.command === "--help" || args.command === "-h") {
@@ -22401,6 +22401,8 @@ function resolveBaseConstructorRecord(record3, ctx) {
22401
22401
  );
22402
22402
  if (resolution.kind === "resolved") return resolution.overload;
22403
22403
  if (resolution.kind === "noMatch" && baseArguments.length === 0) {
22404
+ if (owningClass.requiredConstructorId === record3.id && evaluatorSchemaIndex(ctx).class(baseClassId)?.requiredConstructorId == null)
22405
+ return null;
22404
22406
  throw new NSGetterRuntimeError(
22405
22407
  `Constructor '${record3.id}' on '${owningClass.name}' must call ': base(...)': its base class declares constructors but none parameterless.`
22406
22408
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neocompose/cli",
3
- "version": "0.68.3",
3
+ "version": "0.68.4",
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.68.3 -->
12
+ <!-- reviewed-through-cli: 0.68.4 -->
13
13
 
14
14
  # Neo Compose CLI
15
15
 
@@ -110,7 +110,7 @@ wrappers.
110
110
  The marker near the top of `SKILL.md` must exactly match the package version:
111
111
 
112
112
  ```html
113
- <!-- reviewed-through-cli: 0.68.3 -->
113
+ <!-- reviewed-through-cli: 0.68.4 -->
114
114
  ```
115
115
 
116
116
  The quoted version above is checked too, so this instruction cannot go stale
@@ -385,6 +385,14 @@ class Child(string name) : Base {
385
385
  }
386
386
  ```
387
387
 
388
+ A header base clause with no arguments, such as `: Base()` or `: Base`,
389
+ runs a zero-argument or fully defaulted base constructor when available.
390
+ Otherwise, if the base has only declared constructors, it uses the member
391
+ initializers and base block without running a base constructor. The base block
392
+ must settle any remaining required members. A required base constructor cannot
393
+ be skipped. A declared constructor's `: base()` must call a base constructor
394
+ when the base declares one.
395
+
388
396
  Bind generic base arguments explicitly when needed. Reject a subclass that
389
397
  omits required base arguments or leaves any inherited required member
390
398
  unsettled.