@neocompose/cli 0.34.0 → 0.34.2

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,27 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.34.2] - 2026-08-19
4
+
5
+ ### Fixed
6
+
7
+ - Treat unknown top-level document fields with a JSON `null` value as absent
8
+ while continuing to reject unknown non-null authored data, so harmless fields
9
+ emitted by an older CLI do not block project commands.
10
+
11
+ ## [0.34.1] - 2026-08-19
12
+
13
+ ### Fixed
14
+
15
+ - Accept the advertised positional `NeoLookupVariantFolder<TObject, TValue>`
16
+ constructor form and lower it identically to named `path`, `collection`, and
17
+ `collectionValue` arguments.
18
+ - Preserve lookup-variant member `TValue` through the CLI/server document
19
+ boundary under the canonical `valueTypeInfo` field, including an open generic
20
+ parameter authored by an enclosing class.
21
+ - Give `neo test` the candidate project's variants and variant folders, so
22
+ variants declared in the same pending push resolve alongside root values and
23
+ declared list indexes.
24
+
3
25
  ## [0.34.0] - 2026-08-19
4
26
 
5
27
  ### Added
package/dist/neo.mjs CHANGED
@@ -18762,7 +18762,7 @@ var init_project_schema_contract_generated = __esm({
18762
18762
  "../packages/neoscript-language/src/project-schema-contract.generated.ts"() {
18763
18763
  "use strict";
18764
18764
  PROJECT_SCHEMA_MANIFEST_FORMAT_VERSION = 3;
18765
- PROJECT_SCHEMA_MANIFEST_CONTRACT_VERSION = "3.13";
18765
+ PROJECT_SCHEMA_MANIFEST_CONTRACT_VERSION = "3.14";
18766
18766
  PROJECT_FILE_UPLOAD_BATCH_SIZE = 32;
18767
18767
  NEO_PROJECT_SOURCE_RECORD_CONTRACT = {
18768
18768
  "recordFields": {
@@ -18804,7 +18804,7 @@ var init_project_schema_contract_generated = __esm({
18804
18804
  "collectionValueId",
18805
18805
  "declaredTypeInfo",
18806
18806
  "targetTypeInfo",
18807
- "variantValueTypeInfo",
18807
+ "valueTypeInfo",
18808
18808
  "dialogueGroupId",
18809
18809
  "code",
18810
18810
  "setterCode",
@@ -20934,10 +20934,20 @@ var init_project_source_construction_diagnostics = __esm({
20934
20934
  });
20935
20935
 
20936
20936
  // ../packages/neoscript-language/src/project-source-variants.ts
20937
+ function neoLookupVariantFolderArgument(expression, name) {
20938
+ const index = expression.args.findIndex((_, argumentIndex) => {
20939
+ const authoredName = expression.argumentNames?.[argumentIndex];
20940
+ return (authoredName ?? NEO_LOOKUP_VARIANT_FOLDER_CONSTRUCTOR_ARGUMENTS[argumentIndex]?.name ?? null) === name;
20941
+ });
20942
+ return index < 0 ? void 0 : expression.args[index];
20943
+ }
20937
20944
  function neoVariantConstructionSiteCall(className, argumentNames, initializerNames) {
20938
20945
  if (className === NEO_LOOKUP_VARIANT_FOLDER_TYPE_NAME) {
20946
+ const logicalNames = argumentNames.map(
20947
+ (name, index) => name ?? NEO_LOOKUP_VARIANT_FOLDER_CONSTRUCTOR_ARGUMENTS[index]?.name ?? null
20948
+ );
20939
20949
  return {
20940
- argumentNames: argumentNames.filter(
20950
+ argumentNames: logicalNames.filter(
20941
20951
  (name) => name !== "collection" && name !== "collectionValue"
20942
20952
  ),
20943
20953
  initializerNames
@@ -21001,21 +21011,14 @@ function neoVariantFolderAnnotationSymbol(annotations) {
21001
21011
  function folderPath(declaration) {
21002
21012
  const expression = parseInitializer(declaration);
21003
21013
  if (expression === null || expression.kind !== "new") return null;
21004
- const pathIndex = expression.argumentNames?.findIndex(
21005
- (name) => name === "path"
21006
- );
21007
- const argument2 = expression.args[pathIndex !== void 0 && pathIndex >= 0 ? pathIndex : 0];
21014
+ const argument2 = neoLookupVariantFolderArgument(expression, "path");
21008
21015
  if (argument2 === void 0 || argument2.kind !== "litString") return null;
21009
21016
  return argument2.value;
21010
21017
  }
21011
21018
  function folderCollectionPath(declaration) {
21012
21019
  const expression = parseInitializer(declaration);
21013
21020
  if (expression === null || expression.kind !== "new") return null;
21014
- const index = expression.argumentNames?.findIndex(
21015
- (name) => name === "collection"
21016
- );
21017
- if (index === void 0 || index < 0) return null;
21018
- const argument2 = expression.args[index];
21021
+ const argument2 = neoLookupVariantFolderArgument(expression, "collection");
21019
21022
  if (argument2 === void 0) return null;
21020
21023
  const parts = [];
21021
21024
  let cursor = argument2;
@@ -21746,7 +21749,7 @@ function variantDiagnostic(entry, range2, code, message) {
21746
21749
  message
21747
21750
  };
21748
21751
  }
21749
- var NEO_VARIANT_CONSTRUCTOR_ARGUMENTS, NEO_VARIANT_MEMBER_ARGUMENTS, dottedScope;
21752
+ var NEO_VARIANT_CONSTRUCTOR_ARGUMENTS, NEO_LOOKUP_VARIANT_FOLDER_CONSTRUCTOR_ARGUMENTS, NEO_VARIANT_MEMBER_ARGUMENTS, dottedScope;
21750
21753
  var init_project_source_variants = __esm({
21751
21754
  "../packages/neoscript-language/src/project-source-variants.ts"() {
21752
21755
  "use strict";
@@ -21760,6 +21763,11 @@ var init_project_source_variants = __esm({
21760
21763
  "overrides",
21761
21764
  "childOverrides"
21762
21765
  ];
21766
+ NEO_LOOKUP_VARIANT_FOLDER_CONSTRUCTOR_ARGUMENTS = [
21767
+ { name: "path", type: "string" },
21768
+ { name: "collection", type: "List<TValue>" },
21769
+ { name: "collectionValue", type: "Reference<List<TValue>>" }
21770
+ ];
21763
21771
  NEO_VARIANT_MEMBER_ARGUMENTS = /* @__PURE__ */ new Map([
21764
21772
  ["overrides", "Overrides"],
21765
21773
  ["childOverrides", "ChildOverrides"]
@@ -23847,7 +23855,8 @@ function validateExpression(expression, expected, scope, environment, uri, range
23847
23855
  ) ?? [];
23848
23856
  const overload = declaration && declaration.constructors.length > 0 ? matchingDeclaredConstructor(declaration, expression.argumentNames) : null;
23849
23857
  expression.args.forEach((argument2, index) => {
23850
- const named = expression.argumentNames?.[index];
23858
+ const authoredName = expression.argumentNames?.[index];
23859
+ const named = declaration.name === NEO_LOOKUP_VARIANT_FOLDER_TYPE_NAME ? authoredName ?? NEO_LOOKUP_VARIANT_FOLDER_CONSTRUCTOR_ARGUMENTS[index]?.name : authoredName;
23851
23860
  if (declaration.name === NEO_LOOKUP_VARIANT_FOLDER_TYPE_NAME && named === "collection") {
23852
23861
  return;
23853
23862
  }
@@ -29003,11 +29012,7 @@ function projectConstructorArgumentCompletions(analysis, document, position) {
29003
29012
  }
29004
29013
  function lookupVariantFolderArgumentFields(typeName) {
29005
29014
  if (typeName !== NEO_LOOKUP_VARIANT_FOLDER_TYPE_NAME) return null;
29006
- return [
29007
- { name: "path", type: "string" },
29008
- { name: "collection", type: "List<TValue>" },
29009
- { name: "collectionValue", type: "Reference<List<TValue>>" }
29010
- ];
29015
+ return NEO_LOOKUP_VARIANT_FOLDER_CONSTRUCTOR_ARGUMENTS;
29011
29016
  }
29012
29017
  function declaredConstructorNamedArguments(analysis, typeName, used) {
29013
29018
  const contract = classConstructionContract(
@@ -34866,7 +34871,7 @@ var init_document_contracts = __esm({
34866
34871
  "collectionValueId",
34867
34872
  "declaredTypeInfo",
34868
34873
  "targetTypeInfo",
34869
- "variantValueTypeInfo",
34874
+ "valueTypeInfo",
34870
34875
  "dialogueGroupId",
34871
34876
  "code",
34872
34877
  "setterCode",
@@ -34905,6 +34910,7 @@ var init_document_contracts = __esm({
34905
34910
  collectionValueId: "missingIsNullOnFullMember",
34906
34911
  declaredTypeInfo: "nullIsAbsent",
34907
34912
  targetTypeInfo: "nullIsAbsent",
34913
+ valueTypeInfo: "nullIsAbsent",
34908
34914
  dialogueGroupId: "missingIsNullOnFullMember",
34909
34915
  templateId: "missingIsNullOnFullMember",
34910
34916
  setterCode: "nullIsAbsent",
@@ -35166,8 +35172,9 @@ function fieldDisposition(recordKind, field) {
35166
35172
  return null;
35167
35173
  }
35168
35174
  function assertKnownDocumentFields(recordKind, value) {
35169
- for (const field of Object.keys(value)) {
35175
+ for (const [field, fieldValue] of Object.entries(value)) {
35170
35176
  if (fieldDisposition(recordKind, field) !== null) continue;
35177
+ if (fieldValue === null) continue;
35171
35178
  throw new SchemaDocumentContractError(recordKind, field);
35172
35179
  }
35173
35180
  }
@@ -35182,6 +35189,7 @@ function partitionDocumentFields(recordKind, value) {
35182
35189
  if (fieldValue === void 0) continue;
35183
35190
  const disposition = fieldDisposition(recordKind, field);
35184
35191
  if (disposition === null) {
35192
+ if (fieldValue === null) continue;
35185
35193
  throw new SchemaDocumentContractError(recordKind, field);
35186
35194
  }
35187
35195
  result[disposition][field] = fieldValue;
@@ -35192,6 +35200,11 @@ function normalizeDocumentFields(recordKind, value) {
35192
35200
  assertKnownDocumentFields(recordKind, value);
35193
35201
  const contract = SCHEMA_DOCUMENT_FIELD_CONTRACTS[recordKind];
35194
35202
  const normalized = deepOmitUndefined(value);
35203
+ for (const [field, fieldValue] of Object.entries(normalized)) {
35204
+ if (fieldValue === null && fieldDisposition(recordKind, field) === null) {
35205
+ delete normalized[field];
35206
+ }
35207
+ }
35195
35208
  for (const [field, rule] of Object.entries(contract.normalization)) {
35196
35209
  const current = normalized[field];
35197
35210
  if (rule === "nullIsAbsent") {
@@ -36335,10 +36348,10 @@ function memberFromDocument(record3, members, owners, classNames, context) {
36335
36348
  record3,
36336
36349
  "targetTypeInfo"
36337
36350
  ),
36338
- valueType: field("variantValueTypeInfo", null) === null ? null : documentTypeToManifest(
36339
- field("variantValueTypeInfo", void 0),
36351
+ valueType: field("valueTypeInfo", null) === null ? null : documentTypeToManifest(
36352
+ field("valueTypeInfo", void 0),
36340
36353
  record3,
36341
- "variantValueTypeInfo"
36354
+ "valueTypeInfo"
36342
36355
  )
36343
36356
  };
36344
36357
  }
@@ -36488,7 +36501,7 @@ function memberToDocumentFields(member, baseData3) {
36488
36501
  }
36489
36502
  if (member.kind === "variant") {
36490
36503
  fields.targetTypeInfo = manifestTypeToDocument(member.targetType);
36491
- fields.variantValueTypeInfo = member.valueType === null ? null : manifestTypeToDocument(member.valueType);
36504
+ fields.valueTypeInfo = member.valueType === null ? null : manifestTypeToDocument(member.valueType);
36492
36505
  }
36493
36506
  if (member.kind === "generic") {
36494
36507
  fields.genericParamId = member.genericParamId;
@@ -39692,10 +39705,10 @@ function assertMember2(value, path) {
39692
39705
  member.valueType,
39693
39706
  `${path}.valueType`
39694
39707
  ).kind;
39695
- if (valueKind !== "class") {
39708
+ if (valueKind !== "class" && valueKind !== "generic") {
39696
39709
  invalid(
39697
39710
  `${path}.valueType.kind`,
39698
- "a lookup variant member's TValue must be a collection entry class."
39711
+ "a lookup variant member's TValue must be a collection entry class or an open generic parameter."
39699
39712
  );
39700
39713
  }
39701
39714
  }
@@ -44154,6 +44167,11 @@ function isMemberVariantBase(value) {
44154
44167
  if (v.targetTypeInfo.type !== 7 /* Class */ && v.targetTypeInfo.type !== 21 /* Generic */) {
44155
44168
  return false;
44156
44169
  }
44170
+ if (v.valueTypeInfo !== void 0 && v.valueTypeInfo !== null && (!isNSTypeInfo(v.valueTypeInfo) || // P68's concrete folder binding resolves to a Class, while an unclosed
44171
+ // owner class can still spell the same TValue as its Generic parameter.
44172
+ v.valueTypeInfo.type !== 7 /* Class */ && v.valueTypeInfo.type !== 21 /* Generic */)) {
44173
+ return false;
44174
+ }
44157
44175
  const defaultValue = v.defaultValue;
44158
44176
  if (defaultValue === void 0 || defaultValue === null) return true;
44159
44177
  if (!isMemberValueBase(defaultValue)) return false;
@@ -60262,13 +60280,11 @@ function folderBinding(context, global) {
60262
60280
  `Lookup variant folder ${global.name} must be declared with new(...).`
60263
60281
  );
60264
60282
  }
60265
- const named = /* @__PURE__ */ new Map();
60266
- expression.args.forEach((argument2, index) => {
60267
- const name = expression.argumentNames?.[index];
60268
- if (name !== void 0 && name !== null) named.set(name, argument2);
60269
- });
60270
- const collection = named.get("collection");
60271
- const collectionValue = named.get("collectionValue");
60283
+ const collection = neoLookupVariantFolderArgument(expression, "collection");
60284
+ const collectionValue = neoLookupVariantFolderArgument(
60285
+ expression,
60286
+ "collectionValue"
60287
+ );
60272
60288
  const collectionPath = collection === void 0 ? null : dottedPath(collection);
60273
60289
  if (collectionPath === null) {
60274
60290
  throw new Error(
@@ -113164,8 +113180,8 @@ var init_registry2 = __esm({
113164
113180
  "use strict";
113165
113181
  PROJECT_SCHEMA_CONTRACT = Object.freeze({
113166
113182
  formatVersion: 3,
113167
- contractVersion: "3.13",
113168
- cliVersion: "0.34.0",
113183
+ contractVersion: "3.14",
113184
+ cliVersion: "0.34.2",
113169
113185
  projectFileUploadBatchSize: 32,
113170
113186
  documentRecords: {
113171
113187
  member: {
@@ -113207,7 +113223,7 @@ var init_registry2 = __esm({
113207
113223
  "collectionValueId",
113208
113224
  "declaredTypeInfo",
113209
113225
  "targetTypeInfo",
113210
- "variantValueTypeInfo",
113226
+ "valueTypeInfo",
113211
113227
  "dialogueGroupId",
113212
113228
  "code",
113213
113229
  "setterCode",
@@ -113246,6 +113262,7 @@ var init_registry2 = __esm({
113246
113262
  collectionValueId: "missingIsNullOnFullMember",
113247
113263
  declaredTypeInfo: "nullIsAbsent",
113248
113264
  targetTypeInfo: "nullIsAbsent",
113265
+ valueTypeInfo: "nullIsAbsent",
113249
113266
  dialogueGroupId: "missingIsNullOnFullMember",
113250
113267
  templateId: "missingIsNullOnFullMember",
113251
113268
  setterCode: "nullIsAbsent",
@@ -114652,6 +114669,8 @@ function sharedEvaluatorBase(rawDocument) {
114652
114669
  enums: document.enums,
114653
114670
  interfaces: document.interfaces,
114654
114671
  constructors,
114672
+ variants: document.variants,
114673
+ variantFolders: document.variantFolders,
114655
114674
  projectFiles: document.projectFiles,
114656
114675
  localizationConfig: document.localizationConfig,
114657
114676
  localizedTexts: document.localizedTexts,
@@ -119764,7 +119783,7 @@ There is no --keep-current: preserving current semantic state defines flatten.
119764
119783
  async function main() {
119765
119784
  const args = parseArgs(process.argv.slice(2));
119766
119785
  if (args.command === "--version") {
119767
- console.log("0.34.0");
119786
+ console.log("0.34.2");
119768
119787
  return;
119769
119788
  }
119770
119789
  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.34.0",
3
+ "version": "0.34.2",
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.34.0 -->
12
+ <!-- reviewed-through-cli: 0.34.2 -->
13
13
 
14
14
  # Neo Compose CLI
15
15
 
@@ -47,8 +47,9 @@ Treat pull followed immediately by status or dry-run as a semantic no-op. If it
47
47
  reports project changes, stop and report a round-trip bug instead of pushing.
48
48
 
49
49
  The compiler reports file:line:column diagnostics and fails closed. Unknown
50
- authorable server fields require a CLI contract update; never preserve them as
51
- opaque JSON or guessed syntax.
50
+ non-null authorable server fields require a CLI contract update; never preserve
51
+ them as opaque JSON or guessed syntax. An unknown top-level document field whose
52
+ value is exactly JSON `null` is treated as absent.
52
53
 
53
54
  ## Honor hard authoring invariants
54
55
 
@@ -83,7 +83,7 @@ wrappers.
83
83
  The marker near the top of `SKILL.md` must exactly match the package version:
84
84
 
85
85
  ```html
86
- <!-- reviewed-through-cli: 0.34.0 -->
86
+ <!-- reviewed-through-cli: 0.34.2 -->
87
87
  ```
88
88
 
89
89
  The quoted version above is checked too, so this instruction cannot go stale