@neocompose/cli 0.62.0 → 0.62.1

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,11 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.62.1] - 2026-10-02
4
+
5
+ - Construct populated unordered member defaults through initializers so fresh entries survive test, push, and pull. Keep nested list rows in their existing representation.
6
+ - Accept bare `root.…` paths in `collectionValue` settings and report malformed arguments at their source.
7
+ - Collect independent schema validation errors in one run, preserve source locations for pending and durable identities, and deduplicate repeated phase errors.
8
+
3
9
  ## [0.62.0] - 2026-10-02
4
10
 
5
11
  - Accept covariant read-only overrides of interface-typed properties in project validation and Unity generation. Concrete classes implementing the interface and derived interfaces keep the inherited C# property type, including nullable contracts, without additional runtime adapters.
package/dist/neo.mjs CHANGED
@@ -19576,6 +19576,56 @@ var init_project_source_registry = __esm({
19576
19576
  }
19577
19577
  });
19578
19578
 
19579
+ // ../packages/neoscript-language/src/reference-argument.ts
19580
+ function parseReferenceArgument(raw) {
19581
+ let expression;
19582
+ try {
19583
+ expression = typeof raw === "string" ? parseExpression(raw) : raw;
19584
+ } catch {
19585
+ return null;
19586
+ }
19587
+ if (expression.kind === "litNull") return { kind: "none" };
19588
+ if (expression.kind === "call") {
19589
+ if (expression.callee.kind !== "ident" || expression.callee.name !== "Reference")
19590
+ return null;
19591
+ const idIndex = expression.argumentNames?.indexOf("id") ?? -1;
19592
+ const index = idIndex < 0 ? 0 : idIndex;
19593
+ if (expression.args.length < 1 || expression.args.length > 2) return null;
19594
+ for (let i2 = 0; i2 < expression.args.length; i2++) {
19595
+ const name = expression.argumentNames?.[i2];
19596
+ if (i2 === index) {
19597
+ if (name != null && name !== "id") return null;
19598
+ } else if (name !== "withProvenance" || expression.args[i2]?.kind !== "litBool")
19599
+ return null;
19600
+ }
19601
+ expression = expression.args[index];
19602
+ if (expression.kind === "litString") {
19603
+ return expression.value.length === 0 ? null : { kind: "id", value: expression.value };
19604
+ }
19605
+ }
19606
+ const segments = [];
19607
+ while (expression.kind === "member") {
19608
+ segments.push(expression.name);
19609
+ expression = expression.receiver;
19610
+ }
19611
+ if (expression.kind !== "ident" || expression.name !== "root" || segments.length === 0)
19612
+ return null;
19613
+ return { kind: "rootPath", value: ["root", ...segments.reverse()].join(".") };
19614
+ }
19615
+ function referenceArgumentRequirement(name) {
19616
+ if (name === "collectionValue")
19617
+ return '@settings collectionValue requires Reference("id") or a root path such as root.Assets.Collection.';
19618
+ if (name === "template")
19619
+ return '@settings template requires Reference("id") or a template symbol.';
19620
+ return `@settings ${name} requires Reference("id").`;
19621
+ }
19622
+ var init_reference_argument = __esm({
19623
+ "../packages/neoscript-language/src/reference-argument.ts"() {
19624
+ "use strict";
19625
+ init_executable_parser();
19626
+ }
19627
+ });
19628
+
19579
19629
  // ../packages/neoscript-language/src/project-schema-contract.generated.ts
19580
19630
  var PROJECT_FILE_UPLOAD_BATCH_SIZE, NEO_PROJECT_SOURCE_RECORD_CONTRACT;
19581
19631
  var init_project_schema_contract_generated = __esm({
@@ -24151,6 +24201,22 @@ function validateAnnotations(uri, annotations, context, environment, diagnostics
24151
24201
  if (field.name === "member" && (annotation2.name === "index" || annotation2.name === "column")) {
24152
24202
  continue;
24153
24203
  }
24204
+ if (annotation2.name === "settings" && (field.name === "collectionValue" || field.name === "group" || field.name === "template")) {
24205
+ const reference2 = parseReferenceArgument(argument2.text);
24206
+ if (reference2?.kind === "none" || reference2?.kind === "id" || reference2?.kind === "rootPath" && field.name === "collectionValue")
24207
+ continue;
24208
+ const templateSymbol = field.name === "template" && /^[A-Za-z_][A-Za-z0-9_]*$/.test(argument2.text.trim());
24209
+ if (!templateSymbol) {
24210
+ pushDiagnostic(
24211
+ diagnostics,
24212
+ uri,
24213
+ argument2.range,
24214
+ "invalid-reference-argument",
24215
+ referenceArgumentRequirement(field.name)
24216
+ );
24217
+ continue;
24218
+ }
24219
+ }
24154
24220
  validateExpressionText(
24155
24221
  uri,
24156
24222
  argument2.text,
@@ -25967,6 +26033,7 @@ var NEO_VARIANT_FOLDER_SETTING, primitiveType, LIST_COLUMN_INHERITANCE_KEY, SEMA
25967
26033
  var init_project_source_semantics = __esm({
25968
26034
  "../packages/neoscript-language/src/project-source-semantics.ts"() {
25969
26035
  "use strict";
26036
+ init_reference_argument();
25970
26037
  init_project_source_ast();
25971
26038
  init_project_local_values();
25972
26039
  init_language_spec();
@@ -35618,8 +35685,8 @@ var init_spelling_diagnostics = __esm({
35618
35685
  });
35619
35686
 
35620
35687
  // ../packages/neoscript-language/src/service.ts
35621
- function createNeoScriptLanguageService() {
35622
- return new LanguageService();
35688
+ function createNeoScriptLanguageService(options = {}) {
35689
+ return new LanguageService(options);
35623
35690
  }
35624
35691
  function isProjectDocument(document, context) {
35625
35692
  return context?.projectSourceKind !== "spec" && (document.languageId === "neo" || document.languageId === "neoflow");
@@ -35730,6 +35797,11 @@ var init_service = __esm({
35730
35797
  init_syntax();
35731
35798
  init_test_prelude();
35732
35799
  LanguageService = class {
35800
+ constructor(options) {
35801
+ this.options = options;
35802
+ }
35803
+ options;
35804
+ cachedSourceValidation;
35733
35805
  documents = /* @__PURE__ */ new Map();
35734
35806
  specSnapshots = /* @__PURE__ */ new Set();
35735
35807
  projectGeneration = 0;
@@ -35845,10 +35917,18 @@ var init_service = __esm({
35845
35917
  if (signal?.aborted) return [];
35846
35918
  const generation = ++state.validationGeneration;
35847
35919
  if (isProjectDocument(state.document, state.context)) {
35848
- const diagnostics = this.enhanceDiagnostics(
35849
- state,
35850
- projectDiagnostics(this.projectAnalysis(), uri)
35851
- );
35920
+ const projectGeneration = this.projectGeneration;
35921
+ const project = this.projectAnalysis();
35922
+ const extended = await this.validateProjectSources();
35923
+ if (projectGeneration !== this.projectGeneration) return [];
35924
+ const diagnostics = this.enhanceDiagnostics(state, [
35925
+ ...projectDiagnostics(project, uri),
35926
+ ...extended.filter(
35927
+ (diagnostic) => diagnostic.uri === uri && !project.diagnostics.some(
35928
+ (existing) => existing.uri === uri && existing.code === diagnostic.code && existing.message === diagnostic.message && existing.range.start.line === diagnostic.range.start.line && existing.range.start.character === diagnostic.range.start.character
35929
+ )
35930
+ )
35931
+ ]);
35852
35932
  if (signal?.aborted) return [];
35853
35933
  const current2 = this.documents.get(uri);
35854
35934
  if (!current2 || current2.validationGeneration !== generation) return [];
@@ -36051,6 +36131,31 @@ var init_service = __esm({
36051
36131
  )
36052
36132
  );
36053
36133
  }
36134
+ validateProjectSources() {
36135
+ const validate3 = this.options.validateProjectSources;
36136
+ if (validate3 === void 0) return Promise.resolve([]);
36137
+ if (this.cachedSourceValidation?.generation === this.projectGeneration)
36138
+ return this.cachedSourceValidation.result;
36139
+ const inputs = [...this.documents.values()].flatMap(
36140
+ ({ document, context }) => isProjectDocument(document, context) ? [
36141
+ {
36142
+ uri: document.uri,
36143
+ text: document.text,
36144
+ kind: document.languageId === "neoflow" ? "flow" : context.kind === "migration" ? "migration" : "definition"
36145
+ }
36146
+ ] : []
36147
+ );
36148
+ const result = validate3(inputs).catch((error) => {
36149
+ if (this.cachedSourceValidation?.result === result)
36150
+ this.cachedSourceValidation = void 0;
36151
+ throw error;
36152
+ });
36153
+ this.cachedSourceValidation = {
36154
+ generation: this.projectGeneration,
36155
+ result
36156
+ };
36157
+ return result;
36158
+ }
36054
36159
  projectAnalysis() {
36055
36160
  if (this.cachedProjectAnalysis?.generation === this.projectGeneration) {
36056
36161
  return this.cachedProjectAnalysis.analysis;
@@ -36437,6 +36542,7 @@ var init_src = __esm({
36437
36542
  init_project_local_value_refactorings();
36438
36543
  init_cell_pattern();
36439
36544
  init_tile_conversion();
36545
+ init_reference_argument();
36440
36546
  }
36441
36547
  });
36442
36548
 
@@ -38569,8 +38675,9 @@ var init_source_diagnostics = __esm({
38569
38675
  "neoflow-incomplete-draft"
38570
38676
  ]);
38571
38677
  SchemaSourceError = class extends Error {
38572
- constructor(message, file, line, column, code, severity = "error") {
38573
- super(`${file}:${line}:${column} \u2014 ${message}`);
38678
+ constructor(detail, file, line, column, code, severity = "error") {
38679
+ super(`${file}:${line}:${column} \u2014 ${detail}`);
38680
+ this.detail = detail;
38574
38681
  this.file = file;
38575
38682
  this.line = line;
38576
38683
  this.column = column;
@@ -38578,6 +38685,7 @@ var init_source_diagnostics = __esm({
38578
38685
  this.severity = severity;
38579
38686
  this.name = "SchemaSourceError";
38580
38687
  }
38688
+ detail;
38581
38689
  file;
38582
38690
  line;
38583
38691
  column;
@@ -62319,13 +62427,23 @@ function lowerClass(context, declaration) {
62319
62427
  ...placement
62320
62428
  });
62321
62429
  } catch (error) {
62322
- if (error instanceof SchemaSourceError) throw error;
62323
- throw new SchemaSourceError(
62430
+ const positioned = error instanceof SchemaSourceError ? error : new SchemaSourceError(
62324
62431
  error instanceof Error ? error.message : String(error),
62325
62432
  memberDeclaration.source.uri,
62326
62433
  memberDeclaration.source.range.start.line + 1,
62327
62434
  memberDeclaration.source.range.start.character + 1
62328
62435
  );
62436
+ if (context.onDeclarationError === void 0) throw positioned;
62437
+ context.onDeclarationError(positioned, memberDeclaration);
62438
+ const previous = context.baseMembers.get(memberId);
62439
+ if (previous !== void 0)
62440
+ context.loweredMembers.set(memberId, previous);
62441
+ else {
62442
+ delete schema[memberDeclaration.name];
62443
+ schemaKeyOrder.pop();
62444
+ placements.pop();
62445
+ }
62446
+ continue;
62329
62447
  }
62330
62448
  const existing = context.loweredMembers.get(memberId);
62331
62449
  if (existing && !sameSharedMember(existing, lowered)) {
@@ -62697,7 +62815,8 @@ function lowerFieldMember(context, ownerClass, declaration, id2, commonInput, ba
62697
62815
  base,
62698
62816
  id2,
62699
62817
  storesSelectionArray,
62700
- `${ownerClass.name}.${declaration.name}`
62818
+ `${ownerClass.name}.${declaration.name}`,
62819
+ !declaration.modifiers.includes("static") && contextualEnumArgument(settings, "kind") === "Unordered"
62701
62820
  )
62702
62821
  };
62703
62822
  const name = fieldType.name;
@@ -63558,12 +63677,12 @@ function lowerType(context, type, ownerClass) {
63558
63677
  if (type.name === "object") return { kind: "unknown", nullable: nullable2 };
63559
63678
  throw new Error(`Unknown Neo type ${JSON.stringify(type.name)}.`);
63560
63679
  }
63561
- function lowerDefault(context, initializer, type, ownerClass, base, memberId, storesSelectionArray, path) {
63680
+ function lowerDefault(context, initializer, type, ownerClass, base, memberId, storesSelectionArray, path, unorderedList = false) {
63562
63681
  if (initializer === null) {
63563
63682
  return base?.defaultValue && "serverValueId" in base.defaultValue ? base.defaultValue : null;
63564
63683
  }
63565
63684
  const expression = parseExpression(initializer);
63566
- if (positionRequiresEvaluation(context, expression, type, ownerClass)) {
63685
+ if (unorderedList && expression.kind === "litList" && expression.elements.length > 0 || positionRequiresEvaluation(context, expression, type, ownerClass)) {
63567
63686
  return {
63568
63687
  init: {
63569
63688
  code: context.settleInitializerReferences(
@@ -64715,7 +64834,15 @@ function storageChainStep(context, memberId) {
64715
64834
  }
64716
64835
  function referenceIdArgument(value, name) {
64717
64836
  const raw = argument(value, name);
64718
- return raw === null ? null : referenceId(raw);
64837
+ if (raw === null) return null;
64838
+ const parsed = parseReferenceArgument(raw);
64839
+ if (parsed?.kind === "none") return null;
64840
+ if (parsed?.kind === "id") return parsed.value;
64841
+ if (parsed?.kind === "rootPath" && name === "collectionValue")
64842
+ return `${PENDING_ID_PREFIX}root-path:${parsed.value}`;
64843
+ if (name === "template" && parseAnnotationExpression(raw).kind === "ident")
64844
+ return null;
64845
+ throw new Error(referenceArgumentRequirement(name));
64719
64846
  }
64720
64847
  function templateSymbolArgument(context, settings, ownerClass, declaration) {
64721
64848
  const raw = argument(settings, "template");
@@ -64731,32 +64858,9 @@ function templateSymbolArgument(context, settings, ownerClass, declaration) {
64731
64858
  return templateId;
64732
64859
  }
64733
64860
  function referenceId(raw) {
64734
- try {
64735
- const expression = typeof raw === "string" ? parseExpression(raw) : raw;
64736
- if (expression.kind !== "call") return null;
64737
- if (expression.callee.kind !== "ident" || expression.callee.name !== "Reference") {
64738
- return null;
64739
- }
64740
- const index = expression.argumentNames?.findIndex((entry) => entry === "id") ?? -1;
64741
- const value = expression.args[index >= 0 ? index : 0];
64742
- if (value?.kind === "litString") return value.value;
64743
- const path = value === void 0 ? null : rootValuePath(value);
64744
- return path === null ? null : `${PENDING_ID_PREFIX}root-path:${path}`;
64745
- } catch {
64746
- return null;
64747
- }
64748
- }
64749
- function rootValuePath(expression) {
64750
- const segments = [];
64751
- let cursor = expression;
64752
- while (cursor.kind === "member") {
64753
- segments.unshift(cursor.name);
64754
- cursor = cursor.receiver;
64755
- }
64756
- if (cursor.kind !== "ident") return null;
64757
- if (cursor.name !== "root") return null;
64758
- if (segments.length === 0) return null;
64759
- return ["root", ...segments].join(".");
64861
+ const parsed = parseReferenceArgument(raw);
64862
+ if (parsed === null || parsed.kind === "none") return null;
64863
+ return parsed.kind === "id" ? parsed.value : `${PENDING_ID_PREFIX}root-path:${parsed.value}`;
64760
64864
  }
64761
64865
  function isImpliedSchemaKeyOrder(order) {
64762
64866
  return order.every((key, index) => index === 0 || order[index - 1] <= key);
@@ -112398,16 +112502,8 @@ function validateDocumentInheritanceFlags(ctx) {
112398
112502
  const valueBearing = memberKindSupportsStorage(member.kind);
112399
112503
  const valueId = getField(member, "valueId");
112400
112504
  if (valueBearing) {
112401
- if (member.defaultValue != null) {
112402
- errors.push({
112403
- message: `Static member "${member.name}" (${member.id}) stores live content through valueId and cannot retain a schema defaultValue seed.`
112404
- });
112405
- }
112406
- if (isRequiredMember2(member) && (typeof valueId !== "string" || valueId.length === 0)) {
112407
- errors.push({
112408
- message: `Required static member "${member.name}" (${member.id}) must have an authored value binding.`
112409
- });
112410
- }
112505
+ if (ctx.phase !== "schema")
112506
+ errors.push(...validateStaticMemberValueBinding(member));
112411
112507
  } else if (member.storage != null || valueId != null || member.defaultValue != null) {
112412
112508
  errors.push({
112413
112509
  message: `Value-less static member "${member.name}" (${member.id}) cannot declare storage, valueId, or default content.`
@@ -112555,6 +112651,7 @@ function validateDocumentInheritanceFlags(ctx) {
112555
112651
  inheritedMetadata.declarationClassId
112556
112652
  );
112557
112653
  errors.push({
112654
+ memberId,
112558
112655
  message: `Schema member "${schemaKey}" on class "${schemaClass2.name}" (${schemaClass2.id}) does not match the inherited generated signature from member "${generatedAncestorMember.name}" (${generatedAncestorMember.id}). ${schemaClass2.name}.${schemaKey} generates "${ctx.signatureProjector.format(localSignature)}" but ${declaringClass?.name ?? inheritedMetadata.memberId}.${schemaKey} declares "${ctx.signatureProjector.format(ancestorSignature)}". Abstract implementations and re-abstractions must preserve the generated member contract or have a supported codegen bridge.`
112559
112656
  });
112560
112657
  }
@@ -112633,6 +112730,23 @@ function nearestAncestorDeclaration2(schemaClass2, schemaKey, classesById) {
112633
112730
  }
112634
112731
  return null;
112635
112732
  }
112733
+ function validateStaticMemberValueBinding(member) {
112734
+ if (!isStaticMember(member) || !memberKindSupportsStorage(member.kind))
112735
+ return [];
112736
+ const errors = [];
112737
+ if (member.defaultValue != null)
112738
+ errors.push({
112739
+ memberId: member.id,
112740
+ message: `Static member "${member.name}" (${member.id}) stores live content through valueId and cannot retain a schema defaultValue seed.`
112741
+ });
112742
+ const valueId = getField(member, "valueId");
112743
+ if (isRequiredMember2(member) && (typeof valueId !== "string" || valueId.length === 0))
112744
+ errors.push({
112745
+ memberId: member.id,
112746
+ message: `Required static member "${member.name}" (${member.id}) must have an authored value binding.`
112747
+ });
112748
+ return errors;
112749
+ }
112636
112750
  var init_inheritance_flags_validation = __esm({
112637
112751
  "../src/models/classes/inheritance-flags-validation.ts"() {
112638
112752
  "use strict";
@@ -114889,7 +115003,7 @@ function changesRequireInheritanceFlagsValidation(changes) {
114889
115003
  (change) => change.recordKind === "member" || change.recordKind === "class" || change.recordKind === "enum" || change.recordKind === "interface" || change.recordKind === "project"
114890
115004
  );
114891
115005
  }
114892
- function assertProjectDocumentInheritanceFlagsValid(document) {
115006
+ function collectProjectDocumentInheritanceFlagsErrors(document, phase = "complete") {
114893
115007
  let generatedSignatures = null;
114894
115008
  const getGeneratedSignatures = () => {
114895
115009
  if (generatedSignatures !== null) return generatedSignatures;
@@ -114915,6 +115029,7 @@ function assertProjectDocumentInheritanceFlagsValid(document) {
114915
115029
  return generatedSignatures;
114916
115030
  };
114917
115031
  const errors = validateDocumentInheritanceFlags({
115032
+ phase,
114918
115033
  members: document.members,
114919
115034
  classes: document.classes,
114920
115035
  rootMemberIds: new Set(
@@ -114939,9 +115054,11 @@ function assertProjectDocumentInheritanceFlagsValid(document) {
114939
115054
  hasImplementationBridge: (classId, schemaKey) => getGeneratedSignatures().hasImplementationBridge(classId, schemaKey)
114940
115055
  }
114941
115056
  });
114942
- if (errors.length > 0) {
114943
- throw new Error(errors[0].message);
114944
- }
115057
+ return errors;
115058
+ }
115059
+ function assertProjectDocumentInheritanceFlagsValid(document) {
115060
+ const errors = collectProjectDocumentInheritanceFlagsErrors(document);
115061
+ if (errors.length > 0) throw new Error(errors[0].message);
114945
115062
  }
114946
115063
  var init_project_document_inheritance_validation = __esm({
114947
115064
  "../src/database/project-document-inheritance-validation.ts"() {
@@ -117435,6 +117552,103 @@ var init_client_commit_preparation = __esm({
117435
117552
  }
117436
117553
  });
117437
117554
 
117555
+ // src/project-source/schema-source-validation.ts
117556
+ function sourceDiagnosticMapper(analysis) {
117557
+ const locations = /* @__PURE__ */ new Map();
117558
+ for (const declaration of analysis.schema.classes) {
117559
+ locations.set(
117560
+ materializedId(declaration, "class", declaration.name),
117561
+ declaration.source
117562
+ );
117563
+ for (const member of declaration.members) {
117564
+ locations.set(
117565
+ materializedId(member, "member", `${declaration.name}.${member.name}`),
117566
+ member.source
117567
+ );
117568
+ }
117569
+ }
117570
+ return (error, fallback = "<project>", recordId) => {
117571
+ const message = error instanceof SchemaSourceError ? error.detail : error instanceof Error ? error.message : String(error);
117572
+ if (error instanceof SchemaSourceError && !error.file.startsWith("<"))
117573
+ return error;
117574
+ let location3 = recordId === void 0 ? void 0 : locations.get(recordId);
117575
+ if (location3 === void 0) {
117576
+ for (const match of message.matchAll(
117577
+ /(__pending__:[^:\s]+:[^:\s]+:\d+:\d+:[^\s"()]*)|\(([^()]+)\)|"([^"\n]+)"/g
117578
+ )) {
117579
+ const id2 = match[1] ?? match[2] ?? match[3];
117580
+ location3 = locations.get(id2);
117581
+ if (location3 !== void 0) break;
117582
+ const pending = /^__pending__:[^:]+:([^:]+):(\d+):(\d+):/.exec(id2);
117583
+ if (pending !== null) {
117584
+ const start = {
117585
+ line: Number(pending[2]),
117586
+ character: Number(pending[3])
117587
+ };
117588
+ location3 = {
117589
+ uri: decodeURIComponent(pending[1]),
117590
+ range: { start, end: start }
117591
+ };
117592
+ break;
117593
+ }
117594
+ }
117595
+ }
117596
+ if (location3 === void 0 && error instanceof SchemaSourceError)
117597
+ return error;
117598
+ return new SchemaSourceError(
117599
+ message,
117600
+ location3?.uri ?? fallback,
117601
+ (location3?.range.start.line ?? 0) + 1,
117602
+ (location3?.range.start.character ?? 0) + 1,
117603
+ error instanceof SchemaSourceError ? error.code : "schema-validation"
117604
+ );
117605
+ };
117606
+ }
117607
+ function collectSourceSchemaErrors(document) {
117608
+ const rootMemberIds2 = new Set(
117609
+ PROJECT_ROOTS.map((root) => document.project[root.projectField])
117610
+ );
117611
+ const errors = [];
117612
+ for (const validate3 of [
117613
+ () => validateDocumentAccessModifiers({ ...document, rootMemberIds: rootMemberIds2 }),
117614
+ () => collectProjectDocumentInheritanceFlagsErrors(document, "schema")
117615
+ ]) {
117616
+ try {
117617
+ errors.push(...validate3());
117618
+ } catch (error) {
117619
+ errors.push({
117620
+ message: error instanceof Error ? error.message : String(error)
117621
+ });
117622
+ }
117623
+ }
117624
+ for (const member of document.members) {
117625
+ if (member.kind !== 6 /* List */) continue;
117626
+ errors.push(...validateListIndexDefinitions(document, member));
117627
+ }
117628
+ return errors;
117629
+ }
117630
+ function uniqueSourceErrors(errors) {
117631
+ const seen = /* @__PURE__ */ new Set();
117632
+ return errors.filter((error) => {
117633
+ const key = `${error.file}:${error.line}:${error.column}:${error.detail}`;
117634
+ if (seen.has(key)) return false;
117635
+ seen.add(key);
117636
+ return true;
117637
+ });
117638
+ }
117639
+ var init_schema_source_validation = __esm({
117640
+ "src/project-source/schema-source-validation.ts"() {
117641
+ "use strict";
117642
+ init_project_document_inheritance_validation();
117643
+ init_member_access_modifier_validation();
117644
+ init_list_indexes();
117645
+ init_member_kinds();
117646
+ init_project2();
117647
+ init_source_diagnostics();
117648
+ init_lower_support();
117649
+ }
117650
+ });
117651
+
117438
117652
  // src/project-sync/value-record-comparison.ts
117439
117653
  function valueRecordComparisonBody(value) {
117440
117654
  if (!isObjectRecord2(value)) return value;
@@ -117495,7 +117709,13 @@ function lowerConfiguration(base, analysis) {
117495
117709
  (global) => global.type.name === "Localization"
117496
117710
  );
117497
117711
  if (!localizationGlobal) {
117498
- throw new Error("Format-4 source requires one Localization global.");
117712
+ throw new SchemaSourceError(
117713
+ "Format-4 source requires one Localization global.",
117714
+ base.localization.source.span.path,
117715
+ 1,
117716
+ 1,
117717
+ "missing-localization-global"
117718
+ );
117499
117719
  }
117500
117720
  return {
117501
117721
  textureTemplates,
@@ -118426,6 +118646,7 @@ var wireEnumOptions, TEXTURE_TYPE_WIRE_OPTIONS, ALPHA_SOURCE_WIRE_OPTIONS, NON_P
118426
118646
  var init_lower_configuration = __esm({
118427
118647
  "src/project-source/lower-configuration.ts"() {
118428
118648
  "use strict";
118649
+ init_source_diagnostics();
118429
118650
  init_src();
118430
118651
  init_lower_support();
118431
118652
  wireEnumOptions = () => (options) => options;
@@ -119140,6 +119361,7 @@ function lowerProjectSchemaV4(base, analysis, options = {}) {
119140
119361
  )
119141
119362
  );
119142
119363
  const context = {
119364
+ onDeclarationError: options.onDeclarationError,
119143
119365
  base,
119144
119366
  baseClasses: new Map(base.classes.map((entry) => [entry.id, entry])),
119145
119367
  baseConstructors: new Map(
@@ -119198,17 +119420,29 @@ function lowerProjectSchemaV4(base, analysis, options = {}) {
119198
119420
  analysis.compiledBodies.filter((body) => body.unit === "function").map((body) => [`${body.ownerName}.${body.memberName}`, body.ir])
119199
119421
  )
119200
119422
  };
119201
- const classes = analysis.schema.classes.map(
119202
- (declaration) => lowerClass(context, declaration)
119203
- );
119204
- const constructors = analysis.schema.classes.flatMap(
119205
- (declaration) => lowerClassConstructors(
119206
- context,
119207
- declaration,
119208
- materializedId(declaration, "class", declaration.name),
119209
- (type, ownerClass) => lowerType(context, type, ownerClass)
119210
- )
119211
- );
119423
+ const classes = analysis.schema.classes.flatMap((declaration) => {
119424
+ try {
119425
+ return [lowerClass(context, declaration)];
119426
+ } catch (error) {
119427
+ if (options.onDeclarationError === void 0) throw error;
119428
+ options.onDeclarationError(error, declaration);
119429
+ return [];
119430
+ }
119431
+ });
119432
+ const constructors = analysis.schema.classes.flatMap((declaration) => {
119433
+ try {
119434
+ return lowerClassConstructors(
119435
+ context,
119436
+ declaration,
119437
+ materializedId(declaration, "class", declaration.name),
119438
+ (type, ownerClass) => lowerType(context, type, ownerClass)
119439
+ );
119440
+ } catch (error) {
119441
+ if (options.onDeclarationError === void 0) throw error;
119442
+ options.onDeclarationError(error, declaration);
119443
+ return [];
119444
+ }
119445
+ });
119212
119446
  normalizeLookupDeclaredTypes(context);
119213
119447
  for (const [memberId, placements] of context.placements) {
119214
119448
  const member = context.loweredMembers.get(memberId);
@@ -123643,6 +123877,9 @@ function computeWorkspaceStatus(workspace, options) {
123643
123877
  staticMemberOwnershipRecoveries.filter((recovery) => recovery.action === "delete").map((recovery) => recordStateKey("member", recovery.memberId))
123644
123878
  );
123645
123879
  let manifest;
123880
+ let rootEnvelopeValid = false;
123881
+ let invalidSchema = false;
123882
+ let schemaValidationDocument;
123646
123883
  let authoredValueSeeds = /* @__PURE__ */ new Map();
123647
123884
  let staticMemberValueIds = /* @__PURE__ */ new Map();
123648
123885
  let memberDefaults = {
@@ -123676,36 +123913,33 @@ function computeWorkspaceStatus(workspace, options) {
123676
123913
  }
123677
123914
  const analysis = options.readProjectAnalysisCache?.(workspace.root, compilationSources) ?? compileNeoProjectSources(compilationSources);
123678
123915
  projectAnalysisV4 = analysis;
123679
- for (const diagnostic of analysis.diagnostics) {
123916
+ for (const diagnostic2 of analysis.diagnostics) {
123680
123917
  const positioned = new SchemaSourceError(
123681
- `${diagnostic.code ?? "analysis"}: ${diagnostic.message}`,
123682
- diagnostic.uri,
123683
- diagnostic.range.start.line + 1,
123684
- diagnostic.range.start.character + 1,
123685
- diagnostic.code,
123686
- diagnostic.severity
123918
+ `${diagnostic2.code ?? "analysis"}: ${diagnostic2.message}`,
123919
+ diagnostic2.uri,
123920
+ diagnostic2.range.start.line + 1,
123921
+ diagnostic2.range.start.character + 1,
123922
+ diagnostic2.code,
123923
+ diagnostic2.severity
123687
123924
  );
123688
123925
  if (positioned.severity === "error") parseErrors.push(positioned);
123689
123926
  else parseWarnings.push(positioned);
123690
123927
  }
123691
- if (parseErrors.some(isBlockingSchemaSourceError)) {
123692
- return {
123693
- changes: [],
123694
- conflictedFiles,
123695
- parseErrors,
123696
- parseWarnings,
123697
- reconstructed: /* @__PURE__ */ new Map(),
123698
- authoredValueSeeds,
123699
- binaryChanges: [],
123700
- binaryFiles: []
123701
- };
123702
- }
123928
+ const rootDeclaration = analysis.configuration.globals.find(
123929
+ (global) => global.type.name === "Root"
123930
+ );
123931
+ const rootHasCompilerError = rootDeclaration !== void 0 && analysis.diagnostics.some(
123932
+ (error) => error.severity === "error" && error.uri === rootDeclaration.source.uri && sourceRangeContains(rootDeclaration.source.range, error.range.start)
123933
+ );
123703
123934
  try {
123704
- validateProjectRootEnvelopeV4(
123705
- loweringRecords,
123706
- analysis,
123707
- valueLowerRegistry.parsedInitializers
123708
- );
123935
+ if (!rootHasCompilerError) {
123936
+ validateProjectRootEnvelopeV4(
123937
+ loweringRecords,
123938
+ analysis,
123939
+ valueLowerRegistry.parsedInitializers
123940
+ );
123941
+ rootEnvelopeValid = true;
123942
+ }
123709
123943
  } catch (error) {
123710
123944
  const root = analysis.configuration.globals.find(
123711
123945
  (global) => global.type.name === "Root"
@@ -123718,18 +123952,48 @@ function computeWorkspaceStatus(workspace, options) {
123718
123952
  (root?.source.range.start.character ?? 0) + 1
123719
123953
  )
123720
123954
  );
123721
- return {
123722
- changes: [],
123723
- conflictedFiles,
123724
- parseErrors,
123725
- parseWarnings,
123726
- reconstructed: /* @__PURE__ */ new Map(),
123727
- authoredValueSeeds,
123728
- binaryChanges: [],
123729
- binaryFiles: []
123730
- };
123731
123955
  }
123732
- manifest = lowerProjectSchemaV4(baseManifest, analysis, {
123956
+ const diagnostic = sourceDiagnosticMapper(analysis);
123957
+ const errorsByUri = /* @__PURE__ */ new Map();
123958
+ for (const error of analysis.diagnostics) {
123959
+ if (error.severity !== "error") continue;
123960
+ const errors = errorsByUri.get(error.uri) ?? [];
123961
+ errors.push(error);
123962
+ errorsByUri.set(error.uri, errors);
123963
+ }
123964
+ const validAnalysis = {
123965
+ ...analysis,
123966
+ configuration: {
123967
+ globals: analysis.configuration.globals.filter(
123968
+ (global) => global.type.name !== "Root" || rootEnvelopeValid && !rootHasCompilerError
123969
+ )
123970
+ },
123971
+ schema: {
123972
+ ...analysis.schema,
123973
+ classes: analysis.schema.classes.map((declaration) => ({
123974
+ ...declaration,
123975
+ members: declaration.members.filter(
123976
+ (member) => !(errorsByUri.get(member.source.uri) ?? []).some(
123977
+ (error) => sourceRangeContains(member.source.range, error.range.start)
123978
+ )
123979
+ )
123980
+ }))
123981
+ }
123982
+ };
123983
+ const failedDeclarations = /* @__PURE__ */ new Set();
123984
+ manifest = lowerProjectSchemaV4(baseManifest, validAnalysis, {
123985
+ onDeclarationError: (error, declaration) => {
123986
+ failedDeclarations.add(declaration);
123987
+ parseErrors.push(
123988
+ error instanceof SchemaSourceError ? error : new SchemaSourceError(
123989
+ error instanceof Error ? error.message : String(error),
123990
+ declaration.source.uri,
123991
+ declaration.source.range.start.line + 1,
123992
+ declaration.source.range.start.character + 1,
123993
+ "schema-lowering"
123994
+ )
123995
+ );
123996
+ },
123733
123997
  staticValueIdsBySymbol: staticValueIdsBySymbol(loweringRecords),
123734
123998
  projectFileIdsBySymbol: projectFileIdsBySymbol(loweringRecords, analysis),
123735
123999
  rowBackedDefaultMemberIds: rowBackedDefaultMemberIdsV4(loweringRecords),
@@ -123737,9 +124001,61 @@ function computeWorkspaceStatus(workspace, options) {
123737
124001
  Object.values(loweringRecords)
123738
124002
  )
123739
124003
  });
124004
+ try {
124005
+ const schemaRecords = /* @__PURE__ */ new Map();
124006
+ for (const [key, record6] of Object.entries(loweringRecords)) {
124007
+ if (record6.recordKind !== "project") continue;
124008
+ schemaRecords.set(key, { ...record6, deleted: false });
124009
+ }
124010
+ for (const record6 of projectSchemaManifestToDocuments(
124011
+ manifest,
124012
+ manifestToDocumentContext(baseDocuments)
124013
+ )) {
124014
+ schemaRecords.set(recordStateKey(record6.recordKind, record6.recordId), {
124015
+ ...record6,
124016
+ contentHash: "",
124017
+ deleted: false
124018
+ });
124019
+ }
124020
+ if ([...schemaRecords.values()].some(
124021
+ (record6) => record6.recordKind === "project"
124022
+ )) {
124023
+ const document = readPulledProjectDocumentV4(schemaRecords);
124024
+ schemaValidationDocument = document;
124025
+ for (const error of collectSourceSchemaErrors(document)) {
124026
+ parseErrors.push(
124027
+ diagnostic(new Error(error.message), "<project>", error.memberId)
124028
+ );
124029
+ }
124030
+ }
124031
+ } catch (error) {
124032
+ parseErrors.push(diagnostic(error));
124033
+ }
124034
+ invalidSchema = parseErrors.some(isBlockingSchemaSourceError);
124035
+ const loweredMemberIds = new Set(
124036
+ manifest.members.map((member) => member.id)
124037
+ );
124038
+ projectAnalysisV4 = {
124039
+ ...validAnalysis,
124040
+ schema: {
124041
+ ...validAnalysis.schema,
124042
+ classes: validAnalysis.schema.classes.map((declaration) => ({
124043
+ ...declaration,
124044
+ members: declaration.members.filter(
124045
+ (member) => !failedDeclarations.has(member) && loweredMemberIds.has(
124046
+ materializedId(
124047
+ member,
124048
+ "member",
124049
+ `${declaration.name}.${member.name}`
124050
+ )
124051
+ )
124052
+ )
124053
+ }))
124054
+ }
124055
+ };
123740
124056
  memberDefaults = lowerMemberDefaultSourcesV4(
123741
124057
  loweringRecords,
123742
- analysis,
124058
+ projectAnalysisV4,
123743
124059
  manifest,
123744
124060
  { registry: valueLowerRegistry }
123745
124061
  );
@@ -123759,9 +124075,17 @@ function computeWorkspaceStatus(workspace, options) {
123759
124075
  );
123760
124076
  reportPhase("analysis-schema-defaults", "Reading stored values\u2026");
123761
124077
  } catch (error) {
124078
+ const missingLocalization = error instanceof SchemaSourceError && error.code === "missing-localization-global";
124079
+ const positioned = missingLocalization ? new SchemaSourceError(
124080
+ error.detail,
124081
+ loweringRecords[`localization-config:${baseManifest?.localization.id}`]?.file ?? "Localization.neo",
124082
+ 1,
124083
+ 1,
124084
+ error.code
124085
+ ) : error;
123762
124086
  parseErrors.push(
123763
- error instanceof SchemaSourceError ? error : new SchemaSourceError(
123764
- error instanceof Error ? error.message : String(error),
124087
+ positioned instanceof SchemaSourceError ? positioned : new SchemaSourceError(
124088
+ positioned instanceof Error ? positioned.message : String(positioned),
123765
124089
  "<project>",
123766
124090
  1,
123767
124091
  1
@@ -123823,12 +124147,25 @@ function computeWorkspaceStatus(workspace, options) {
123823
124147
  ...memberDefaults.seeds
123824
124148
  ]);
123825
124149
  staticMemberValueIds = staticValues.memberValueIds;
123826
- const rootValues = lowerProjectRootSourceV4(
123827
- loweringRecords,
123828
- projectAnalysisV4,
123829
- manifest,
123830
- { registry: valueLowerRegistry }
123831
- );
124150
+ if (schemaValidationDocument !== void 0) {
124151
+ const diagnostic = sourceDiagnosticMapper(projectAnalysisV4);
124152
+ for (const member of schemaValidationDocument.members) {
124153
+ const valueId = staticMemberValueIds.get(member.id);
124154
+ const finalized = valueId === void 0 ? member : { ...member, valueId, defaultValue: void 0 };
124155
+ for (const error of validateStaticMemberValueBinding(finalized)) {
124156
+ parseErrors.push(
124157
+ diagnostic(new Error(error.message), "<project>", member.id)
124158
+ );
124159
+ }
124160
+ }
124161
+ }
124162
+ const rootValues = rootEnvelopeValid ? lowerProjectRootSourceV4(loweringRecords, projectAnalysisV4, manifest, {
124163
+ registry: valueLowerRegistry
124164
+ }) : {
124165
+ records: [],
124166
+ memberValueIds: /* @__PURE__ */ new Map(),
124167
+ seeds: /* @__PURE__ */ new Map()
124168
+ };
123832
124169
  reportPhase("documents-static-root", "Reading variants and dialogues\u2026");
123833
124170
  authoredValueSeeds = new Map([...authoredValueSeeds, ...rootValues.seeds]);
123834
124171
  const rootPathResolutionState = overlayProspectiveSourceRecords(
@@ -124453,6 +124790,14 @@ function computeWorkspaceStatus(workspace, options) {
124453
124790
  }
124454
124791
  reportPhase("initializer-materialization");
124455
124792
  }
124793
+ const mapDiagnostic = projectAnalysisV4 === null ? null : sourceDiagnosticMapper(projectAnalysisV4);
124794
+ const positionedErrors = mapDiagnostic === null ? parseErrors : parseErrors.map((error) => mapDiagnostic(error, error.file));
124795
+ if (invalidSchema)
124796
+ return refusedStatus(
124797
+ conflictedFiles,
124798
+ uniqueSourceErrors(positionedErrors),
124799
+ parseWarnings
124800
+ );
124456
124801
  return {
124457
124802
  ...projectAnalysisV4 === null ? {} : {
124458
124803
  sourceSnapshot: {
@@ -124463,7 +124808,7 @@ function computeWorkspaceStatus(workspace, options) {
124463
124808
  localValues: valueLowerRegistry.localValues?.placements(),
124464
124809
  changes,
124465
124810
  conflictedFiles,
124466
- parseErrors,
124811
+ parseErrors: uniqueSourceErrors(positionedErrors),
124467
124812
  parseWarnings,
124468
124813
  reconstructed: reconstructed3,
124469
124814
  authoredValueSeeds,
@@ -125440,10 +125785,16 @@ function refusedStatus(conflictedFiles, parseErrors, parseWarnings) {
125440
125785
  binaryFiles: []
125441
125786
  };
125442
125787
  }
125788
+ function sourceRangeContains(range2, position) {
125789
+ return (position.line > range2.start.line || position.line === range2.start.line && position.character >= range2.start.character) && (position.line < range2.end.line || position.line === range2.end.line && position.character < range2.end.character);
125790
+ }
125443
125791
  var ANIMATION_DOCUMENT_RECORD_KINDS, ANIMATION_REPLAY_RECORD_KINDS, PROSPECTIVE_ANIMATION_RECORD_KINDS;
125444
125792
  var init_workspace_status_core = __esm({
125445
125793
  "src/project-source/workspace-status-core.ts"() {
125446
125794
  "use strict";
125795
+ init_lower_support();
125796
+ init_inheritance_flags_validation();
125797
+ init_schema_source_validation();
125447
125798
  init_members();
125448
125799
  init_collection_binding_materialization();
125449
125800
  init_localizable_member_value_writes();
@@ -125566,9 +125917,9 @@ function verifyProjectSourceCommitAgainstStateV4(args) {
125566
125917
  });
125567
125918
  const blockingErrors = status.parseErrors.filter(isBlockingSchemaSourceError);
125568
125919
  if (status.conflictedFiles.length > 0 || blockingErrors.length > 0) {
125569
- const first = blockingErrors[0];
125570
125920
  throw new ProjectSourceCommitVerificationError(
125571
- first ? `Trusted source lowering failed: ${first.file}:${first.line}:${first.column} ${first.message}` : `Trusted source lowering found conflict markers in ${status.conflictedFiles[0]}.`
125921
+ blockingErrors.length > 0 ? `Trusted source lowering failed:
125922
+ ${blockingErrors.map((error) => error.message).join("\n")}` : `Trusted source lowering found conflict markers in ${status.conflictedFiles.join(", ")}.`
125572
125923
  );
125573
125924
  }
125574
125925
  auditAuthoredSeedRowIdentities(
@@ -125601,8 +125952,10 @@ function verifyProjectSourceCommitAgainstStateV4(args) {
125601
125952
  );
125602
125953
  for (const pendingId2 of assignments.keys()) {
125603
125954
  if (!usedAssignments.has(pendingId2)) {
125955
+ const message = `Pending id assignment ${pendingId2} is not present in the verified source manifest.`;
125956
+ const analysis = status.sourceSnapshot?.analysis;
125604
125957
  throw new ProjectSourceCommitVerificationError(
125605
- `Pending id assignment ${pendingId2} is not present in the verified source manifest.`
125958
+ analysis === void 0 ? message : sourceDiagnosticMapper(analysis)(new Error(message)).message
125606
125959
  );
125607
125960
  }
125608
125961
  }
@@ -126096,6 +126449,7 @@ var ProjectSourceCommitVerificationError, ABSENT_DIFFERENCE_VALUE;
126096
126449
  var init_trusted_commit_verification = __esm({
126097
126450
  "src/project-source/trusted-commit-verification.ts"() {
126098
126451
  "use strict";
126452
+ init_schema_source_validation();
126099
126453
  init_projectRecordBuckets();
126100
126454
  init_workspace_status_core();
126101
126455
  init_supplemental_records();
@@ -128480,6 +128834,8 @@ function defaultRequiresOwnedRows(context, member, sourceExpression, source) {
128480
128834
  }
128481
128835
  function memberInitializerRequiresEvaluation(context, member, expression, source) {
128482
128836
  if (member.kind === "class" && member.partial === true) return false;
128837
+ if (source.purpose === "memberDefault" && member.id === source.memberId && member.kind === "list" && member.listKind === "unordered" && expression.kind === "litList" && expression.elements.length > 0)
128838
+ return true;
128483
128839
  const requiresEvaluation = isSchemaMemberValueRead(context, expression, source) || initializerRequiresEvaluation(
128484
128840
  context.declaredConstructors,
128485
128841
  expression,
@@ -146998,7 +147354,7 @@ var init_registry2 = __esm({
146998
147354
  "../packages/manifest/schema-contract/registry.mjs"() {
146999
147355
  "use strict";
147000
147356
  PROJECT_SCHEMA_CONTRACT = Object.freeze({
147001
- cliVersion: "0.62.0",
147357
+ cliVersion: "0.62.1",
147002
147358
  projectFileUploadBatchSize: 32,
147003
147359
  documentRecords: {
147004
147360
  member: {
@@ -155678,7 +156034,7 @@ There is no --keep-current: preserving current semantic state defines flatten.
155678
156034
  async function main() {
155679
156035
  const args = parseArgs(process.argv.slice(2));
155680
156036
  if (args.command === "--version") {
155681
- console.log("0.62.0");
156037
+ console.log("0.62.1");
155682
156038
  return;
155683
156039
  }
155684
156040
  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.62.0",
3
+ "version": "0.62.1",
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.62.0 -->
12
+ <!-- reviewed-through-cli: 0.62.1 -->
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.62.0 -->
113
+ <!-- reviewed-through-cli: 0.62.1 -->
114
114
  ```
115
115
 
116
116
  The quoted version above is checked too, so this instruction cannot go stale
@@ -387,3 +387,8 @@ class PlacedItem<
387
387
  ```
388
388
 
389
389
  Executable object initializer blocks can set immutable stored members on the newly constructed instance, including nested `new` expressions. Ordinary assignments after construction remain subject to storage restrictions. `readonly` declarations still change only through class defaults.
390
+
391
+ Populated unordered member defaults, such as `@settings(kind: .Unordered)` with
392
+ `List<Entry> Entries = [new Entry()]`, are constructed by an initializer for
393
+ each instance. A declaration has no container value row to own unordered entries.
394
+ Nested unordered lists already have container rows and retain their stored values.
@@ -131,7 +131,9 @@ assignability, collection membership, multiplicity, and group constraints.
131
131
 
132
132
  `collectionValue` references follow the same post-push resolution and
133
133
  membership rules as member defaults. Name the collection itself, for example
134
- `Reference(root.Assets.Quests.Daily)`. A path may traverse construction defaults
134
+ `root.Assets.Quests.Daily` or `Reference(root.Assets.Quests.Daily)`. Invalid
135
+ reference-valued settings are errors; they never silently remove the binding.
136
+ A path may traverse construction defaults
135
137
  and declarations created in the same push. Preparation materializes the target
136
138
  collection and its required ancestor rows together, preserving the virtual
137
139
  identities. No preliminary push or manual value ID is needed.