@neocompose/cli 0.11.1 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/neo.mjs +310 -49
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.12.0] - 2026-07-28
4
+
5
+ ### Added
6
+
7
+ - Warn on `neo push` when an animation clip's owning class is also
8
+ instantiated as a nested `Children` row. P41 lets the players skip a child
9
+ reference they cannot resolve instead of throwing, which would otherwise
10
+ make one real defect silent: a nested row's own children are re-lowered
11
+ copies, so a clip naming its owner's class-default rows resolves none of
12
+ them on that instance. The advisory names the clip and the nested use site
13
+ it found, and points at authored provenance for nested rows (P44) or a
14
+ constructor (P43) as the fix. It never blocks the push, and it is removed
15
+ when P44 lands. `neo push --json`, `neo status --json` and `neo diff --json`
16
+ carry the same text in a new `warnings` array, separate from the blocking
17
+ `diagnostics`.
18
+
3
19
  ## [0.11.1] - 2026-07-27
4
20
 
5
21
  ### Fixed
package/dist/neo.mjs CHANGED
@@ -36062,6 +36062,14 @@ var init_world_system_classes_generated = __esm({
36062
36062
  defaultValue: { x: 1, y: 1, z: 0 },
36063
36063
  required: true,
36064
36064
  schemaKey: "Size"
36065
+ },
36066
+ {
36067
+ memberId: "system_4858148e-1c42-449d-8a03-c1601da529bd",
36068
+ memberKind: "bool",
36069
+ defaultValue: true,
36070
+ docsText: "When false, this object and its children are neither rendered nor collided with. The value stays live: member writes still apply and a running animation clip keeps playing. Disabling an object hides its whole subtree regardless of each child's own value, and re-enabling it restores exactly what was there.",
36071
+ required: true,
36072
+ schemaKey: "Enabled"
36065
36073
  }
36066
36074
  ],
36067
36075
  worldKind: "objectBase"
@@ -47699,6 +47707,24 @@ function assertAnimationClipDocumentValid(document) {
47699
47707
  context.validateClipMember(member);
47700
47708
  }
47701
47709
  }
47710
+ function collectAnimationClipNestedOwnerWarnings(document) {
47711
+ try {
47712
+ return new AnimationValidationContext(
47713
+ document
47714
+ ).collectNestedOwnerWarnings();
47715
+ } catch {
47716
+ return [];
47717
+ }
47718
+ }
47719
+ function compareNestedChildrenRowUseSites(left, right) {
47720
+ if (left.ownerClassName !== right.ownerClassName) {
47721
+ return left.ownerClassName < right.ownerClassName ? -1 : 1;
47722
+ }
47723
+ if (left.rowValueId !== right.rowValueId) {
47724
+ return left.rowValueId < right.rowValueId ? -1 : 1;
47725
+ }
47726
+ return 0;
47727
+ }
47702
47728
  function isRecord3(value) {
47703
47729
  return typeof value === "object" && value !== null && !Array.isArray(value);
47704
47730
  }
@@ -48268,13 +48294,19 @@ var init_animation_clips = __esm({
48268
48294
  return typeof member.valueId === "string" ? this.valueById.get(member.valueId) ?? null : null;
48269
48295
  }
48270
48296
  memberRootNode(member) {
48271
- const value = member.defaultValue?.value;
48297
+ const node = this.optionalMemberRootNode(member);
48298
+ if (node === null) {
48299
+ throw new Error(
48300
+ `Animation clip "${member.name}" must have a composite default-value graph.`
48301
+ );
48302
+ }
48303
+ return node;
48304
+ }
48305
+ /** {@link memberRootNode} without the throw, for diagnostics-only readers. */
48306
+ optionalMemberRootNode(member) {
48272
48307
  if (member.defaultValue !== void 0) {
48273
- if (!isRecord3(value)) {
48274
- throw new Error(
48275
- `Animation clip "${member.name}" must have a composite default-value graph.`
48276
- );
48277
- }
48308
+ const value = member.defaultValue.value;
48309
+ if (!isRecord3(value)) return null;
48278
48310
  return {
48279
48311
  id: `member-default:${member.id}`,
48280
48312
  projectId: member.projectId,
@@ -48285,13 +48317,8 @@ var init_animation_clips = __esm({
48285
48317
  };
48286
48318
  }
48287
48319
  const valueId = member.valueId;
48288
- if (typeof valueId === "string") {
48289
- const stored = this.valueById.get(valueId);
48290
- if (stored !== void 0) return stored;
48291
- }
48292
- throw new Error(
48293
- `Animation clip "${member.name}" must have a composite default-value graph.`
48294
- );
48320
+ if (typeof valueId !== "string") return null;
48321
+ return this.valueById.get(valueId) ?? null;
48295
48322
  }
48296
48323
  optionalFieldNode(parent, classId, memberId) {
48297
48324
  const field = this.field(classId, memberId);
@@ -48315,35 +48342,45 @@ var init_animation_clips = __esm({
48315
48342
  }
48316
48343
  requireListField(parent, classId, memberId, label) {
48317
48344
  const field = this.requireField(classId, memberId, label);
48345
+ const ids = this.listFieldIds(parent, field);
48346
+ if (!Array.isArray(ids)) {
48347
+ throw new Error(`${label} must be an ordered list.`);
48348
+ }
48349
+ const inferredClassId = this.listEntryClassId(field.member);
48350
+ return ids.map((id2) => {
48351
+ if (typeof id2 !== "string" || !this.valueById.has(id2)) {
48352
+ throw new Error(`${label} references missing entry "${String(id2)}".`);
48353
+ }
48354
+ const entry = this.valueById.get(id2);
48355
+ return typeof entry.classId === "string" || inferredClassId === void 0 ? entry : { ...entry, classId: inferredClassId };
48356
+ });
48357
+ }
48358
+ /**
48359
+ * The raw list payload behind a field: an explicit instance edge wins even
48360
+ * when it is dangling, then the member default, then the legacy value row.
48361
+ */
48362
+ listFieldIds(parent, field) {
48318
48363
  const mapped = isRecord3(parent.value) ? parent.value[field.schemaKey] : null;
48319
- let ids;
48320
48364
  if (typeof mapped === "string") {
48321
- ids = this.valueById.get(mapped)?.value;
48322
- } else if (field.member.defaultValue !== void 0) {
48323
- ids = field.member.defaultValue.value;
48324
- } else {
48325
- ids = typeof field.member.valueId === "string" ? this.valueById.get(field.member.valueId)?.value : void 0;
48365
+ return this.valueById.get(mapped)?.value;
48326
48366
  }
48327
- if (!Array.isArray(ids)) {
48328
- throw new Error(`${label} must be an ordered list.`);
48367
+ if (field.member.defaultValue !== void 0) {
48368
+ return field.member.defaultValue.value;
48329
48369
  }
48370
+ return typeof field.member.valueId === "string" ? this.valueById.get(field.member.valueId)?.value : void 0;
48371
+ }
48372
+ /** Class a list's rows carry when a row omits its own `classId`. */
48373
+ listEntryClassId(member) {
48330
48374
  const listMember = {
48331
- ...field.member,
48332
- ...resolveMember2(field.member, this.document.members)
48375
+ ...member,
48376
+ ...resolveMember2(member, this.document.members)
48333
48377
  };
48334
48378
  const entryMember = isMemberList(listMember) ? this.memberById.get(listMember.entryMemberId) : void 0;
48335
48379
  const resolvedEntryMember = entryMember === void 0 ? void 0 : {
48336
48380
  ...entryMember,
48337
48381
  ...resolveMember2(entryMember, this.document.members)
48338
48382
  };
48339
- const inferredClassId = isMemberClass(resolvedEntryMember) ? resolvedEntryMember.classId : void 0;
48340
- return ids.map((id2) => {
48341
- if (typeof id2 !== "string" || !this.valueById.has(id2)) {
48342
- throw new Error(`${label} references missing entry "${String(id2)}".`);
48343
- }
48344
- const entry = this.valueById.get(id2);
48345
- return typeof entry.classId === "string" || inferredClassId === void 0 ? entry : { ...entry, classId: inferredClassId };
48346
- });
48383
+ return isMemberClass(resolvedEntryMember) ? resolvedEntryMember.classId : void 0;
48347
48384
  }
48348
48385
  requireSingleLookupField(parent, classId, memberId, label) {
48349
48386
  const value = this.scalarField(parent, classId, memberId);
@@ -48388,18 +48425,23 @@ var init_animation_clips = __esm({
48388
48425
  return node.classId;
48389
48426
  }
48390
48427
  requireClassBinding(member, paramId, label) {
48428
+ const classId = this.optionalClassBinding(member, paramId);
48429
+ if (classId === null) {
48430
+ throw new Error(
48431
+ `${label} must bind its target generic to a Class member.`
48432
+ );
48433
+ }
48434
+ return classId;
48435
+ }
48436
+ /** {@link requireClassBinding} without the throw, for diagnostics-only readers. */
48437
+ optionalClassBinding(member, paramId) {
48391
48438
  const binding = resolveInstanceEnv(
48392
48439
  member.classId,
48393
48440
  member.classArguments,
48394
48441
  this.document.classes
48395
48442
  ).get(paramId);
48396
48443
  const bindingMember = binding?.kind === "member" ? this.memberById.get(binding.memberId) : void 0;
48397
- if (!isMemberClass(bindingMember)) {
48398
- throw new Error(
48399
- `${label} must bind its target generic to a Class member.`
48400
- );
48401
- }
48402
- return bindingMember.classId;
48444
+ return isMemberClass(bindingMember) ? bindingMember.classId : null;
48403
48445
  }
48404
48446
  requireClassMember(value) {
48405
48447
  if (!isMemberClass(value)) throw new Error("Expected Class member.");
@@ -48436,6 +48478,188 @@ var init_animation_clips = __esm({
48436
48478
  }
48437
48479
  return false;
48438
48480
  }
48481
+ // ---------------------------------------------------------------------------
48482
+ // P41 §2.3 SCAFFOLDING — DELETE EVERYTHING BELOW THIS BANNER WHEN P44 LANDS,
48483
+ // together with `collectAnimationClipNestedOwnerWarnings` and its tests. See
48484
+ // that function's doc block for why the check exists and why P44 retires it.
48485
+ // ---------------------------------------------------------------------------
48486
+ collectNestedOwnerWarnings() {
48487
+ const useSiteByClassId = this.nestedChildrenRowUseSites();
48488
+ if (useSiteByClassId.size === 0) return [];
48489
+ const warnings = [];
48490
+ for (const member of this.document.members) {
48491
+ if (!isMemberClass(member)) continue;
48492
+ if (!this.classHasWorldKind(member.classId, "animationClip")) continue;
48493
+ const ownerClassId2 = this.optionalClassBinding(
48494
+ member,
48495
+ WORLD_ANIMATION_CLIP_TARGET_PARAM_ID
48496
+ );
48497
+ if (ownerClassId2 === null) continue;
48498
+ const useSite = useSiteByClassId.get(ownerClassId2);
48499
+ if (useSite === void 0) continue;
48500
+ if (!this.clipDeclaresChildReferences(member)) continue;
48501
+ const ownerClassName = this.classById.get(ownerClassId2)?.name ?? ownerClassId2;
48502
+ const nestedRowClassName = this.classById.get(useSite.rowClassId)?.name ?? useSite.rowClassId;
48503
+ const nesting = useSite.rowClassId === ownerClassId2 ? "which is also used as a nested Children row" : `whose subclass "${nestedRowClassName}" is used as a nested Children row`;
48504
+ warnings.push({
48505
+ clipName: member.name,
48506
+ ownerClassName,
48507
+ nestedOwnerClassName: useSite.ownerClassName,
48508
+ nestedRowClassName,
48509
+ nestedRowValueId: useSite.rowValueId,
48510
+ message: `Animation clip "${member.name}" overrides or tracks children of "${ownerClassName}", ${nesting} (value "${useSite.rowValueId}" in class "${useSite.ownerClassName}"). A nested instance carries re-lowered copies of those rows, so the clip resolves none of them there. Give nested Children rows authored provenance (P44), or declare the child rows through a constructor (P43).`
48511
+ });
48512
+ }
48513
+ return warnings;
48514
+ }
48515
+ /**
48516
+ * Every class an authored `Children` list instantiates as a row, keyed by
48517
+ * that class and each of its ancestors — a nested row of a subclass inherits
48518
+ * the base class's clips, so the base is affected too.
48519
+ *
48520
+ * Both authoring shapes count. A class-default `Children` is one; a
48521
+ * `Children` list authored on a row inside one is the other, and it is the
48522
+ * shape P41 §3 itself writes — `CharacterBody.Children` holds a `HeadPart`
48523
+ * row whose own `Children` holds the hat. Scanning only class defaults would
48524
+ * miss every slot below the first level.
48525
+ *
48526
+ * A layer's directly placed objects hang off `NeoObjectLayerLink.Objects`, a
48527
+ * different member, so a directly placed class is never collected here.
48528
+ */
48529
+ nestedChildrenRowUseSites() {
48530
+ const declaringClasses = /* @__PURE__ */ new Map();
48531
+ for (const schemaClass2 of this.document.classes) {
48532
+ for (const [schemaKey, memberId] of Object.entries(schemaClass2.schema)) {
48533
+ if (typeof memberId !== "string") continue;
48534
+ if (declaringClasses.has(memberId)) continue;
48535
+ declaringClasses.set(memberId, { schemaClass: schemaClass2, schemaKey });
48536
+ }
48537
+ }
48538
+ const sites = [];
48539
+ const pending = [];
48540
+ for (const member of this.document.members) {
48541
+ if (!this.memberDescendsFrom(member.id, WORLD_OBJECT_CHILDREN_MEMBER_ID)) {
48542
+ continue;
48543
+ }
48544
+ const declaration = declaringClasses.get(member.id);
48545
+ if (declaration === void 0) continue;
48546
+ const node = this.resolveDefinitionChild(
48547
+ null,
48548
+ declaration.schemaKey,
48549
+ member
48550
+ );
48551
+ pending.push(
48552
+ ...this.childrenRowUseSites(node, member, declaration.schemaClass.name)
48553
+ );
48554
+ }
48555
+ const visited = /* @__PURE__ */ new Set();
48556
+ for (let index = 0; index < pending.length; index += 1) {
48557
+ const site = pending[index];
48558
+ if (site === void 0) continue;
48559
+ if (visited.has(site.rowValueId)) continue;
48560
+ visited.add(site.rowValueId);
48561
+ sites.push(site);
48562
+ const row = this.valueById.get(site.rowValueId);
48563
+ if (row === void 0) continue;
48564
+ const field = this.field(
48565
+ site.rowClassId,
48566
+ WORLD_OBJECT_CHILDREN_MEMBER_ID
48567
+ );
48568
+ if (field === null) continue;
48569
+ const rowClassName = this.classById.get(site.rowClassId)?.name ?? site.rowClassId;
48570
+ pending.push(
48571
+ ...this.childrenRowUseSites(
48572
+ { ...row, value: this.listFieldIds(row, field) },
48573
+ field.member,
48574
+ rowClassName
48575
+ )
48576
+ );
48577
+ }
48578
+ sites.sort(compareNestedChildrenRowUseSites);
48579
+ const useSiteByClassId = /* @__PURE__ */ new Map();
48580
+ for (const site of sites) {
48581
+ if (useSiteByClassId.has(site.rowClassId)) continue;
48582
+ useSiteByClassId.set(site.rowClassId, site);
48583
+ }
48584
+ for (const site of sites) {
48585
+ for (const classId of this.classAndAncestorIds(site.rowClassId)) {
48586
+ if (useSiteByClassId.has(classId)) continue;
48587
+ useSiteByClassId.set(classId, site);
48588
+ }
48589
+ }
48590
+ return useSiteByClassId;
48591
+ }
48592
+ /** The rows an authored `Children` list holds, as use sites of `ownerClassName`. */
48593
+ childrenRowUseSites(node, listMember, ownerClassName) {
48594
+ if (!Array.isArray(node?.value)) return [];
48595
+ const entryClassId = this.listEntryClassId(listMember);
48596
+ const sites = [];
48597
+ for (const rowValueId of node.value) {
48598
+ if (typeof rowValueId !== "string") continue;
48599
+ const row = this.valueById.get(rowValueId);
48600
+ const rowClassId = typeof row?.classId === "string" ? row.classId : entryClassId;
48601
+ if (rowClassId === void 0) continue;
48602
+ sites.push({ ownerClassName, rowValueId, rowClassId });
48603
+ }
48604
+ return sites;
48605
+ }
48606
+ /** True when the clip names at least one child through a track or override. */
48607
+ clipDeclaresChildReferences(clipMember) {
48608
+ const clipNode = this.optionalMemberRootNode(clipMember);
48609
+ if (clipNode === null) return false;
48610
+ const tracks = this.optionalListRows(
48611
+ clipNode,
48612
+ clipMember.classId,
48613
+ WORLD_ANIMATION_CLIP_TRACKS_MEMBER_ID
48614
+ );
48615
+ if (tracks !== null && tracks.length > 0) return true;
48616
+ const frames = this.optionalListRows(
48617
+ clipNode,
48618
+ clipMember.classId,
48619
+ WORLD_ANIMATION_CLIP_FRAMES_MEMBER_ID
48620
+ ) ?? [];
48621
+ for (const frame of frames) {
48622
+ if (typeof frame.classId !== "string") continue;
48623
+ const childOverrides = this.optionalListRows(
48624
+ frame,
48625
+ frame.classId,
48626
+ WORLD_ANIMATION_FRAME_CHILD_OVERRIDES_MEMBER_ID
48627
+ );
48628
+ if (childOverrides !== null && childOverrides.length > 0) return true;
48629
+ }
48630
+ return false;
48631
+ }
48632
+ /** {@link requireListField} without the throws, for diagnostics-only readers. */
48633
+ optionalListRows(parent, classId, memberId) {
48634
+ const field = this.field(classId, memberId);
48635
+ if (field === null) return null;
48636
+ const ids = this.listFieldIds(parent, field);
48637
+ if (!Array.isArray(ids)) return null;
48638
+ const inferredClassId = this.listEntryClassId(field.member);
48639
+ const rows = [];
48640
+ for (const id2 of ids) {
48641
+ if (typeof id2 !== "string") return null;
48642
+ const entry = this.valueById.get(id2);
48643
+ if (entry === void 0) return null;
48644
+ rows.push(
48645
+ typeof entry.classId === "string" || inferredClassId === void 0 ? entry : { ...entry, classId: inferredClassId }
48646
+ );
48647
+ }
48648
+ return rows;
48649
+ }
48650
+ /** The class itself followed by every ancestor it extends. */
48651
+ classAndAncestorIds(classId) {
48652
+ const ids = [];
48653
+ const visited = /* @__PURE__ */ new Set();
48654
+ let current = this.classById.get(classId);
48655
+ if (current === void 0) return [classId];
48656
+ while (current !== void 0 && !visited.has(current.id)) {
48657
+ visited.add(current.id);
48658
+ ids.push(current.id);
48659
+ current = current.extendsClassId ? this.classById.get(current.extendsClassId) : void 0;
48660
+ }
48661
+ return ids;
48662
+ }
48439
48663
  };
48440
48664
  }
48441
48665
  });
@@ -48714,6 +48938,7 @@ function compareWorkspacePaths(left, right) {
48714
48938
  function computeWorkspaceStatus(workspace, options = {}) {
48715
48939
  const conflictedFiles = [];
48716
48940
  const parseErrors = [];
48941
+ const warnings = [];
48717
48942
  const migrationSources = [];
48718
48943
  const projectSources = [];
48719
48944
  const sourceEntries = options.virtualSourceFiles === void 0 ? listProjectSourceFilesV4(workspace.root).map((filePath) => ({
@@ -48743,6 +48968,7 @@ function computeWorkspaceStatus(workspace, options = {}) {
48743
48968
  changes: [],
48744
48969
  conflictedFiles,
48745
48970
  parseErrors,
48971
+ warnings,
48746
48972
  reconstructed: /* @__PURE__ */ new Map(),
48747
48973
  staticValueSeeds: /* @__PURE__ */ new Map(),
48748
48974
  binaryChanges: [],
@@ -48791,6 +49017,7 @@ function computeWorkspaceStatus(workspace, options = {}) {
48791
49017
  changes: [],
48792
49018
  conflictedFiles,
48793
49019
  parseErrors,
49020
+ warnings,
48794
49021
  reconstructed: /* @__PURE__ */ new Map(),
48795
49022
  staticValueSeeds,
48796
49023
  binaryChanges: [],
@@ -48815,6 +49042,7 @@ function computeWorkspaceStatus(workspace, options = {}) {
48815
49042
  changes: [],
48816
49043
  conflictedFiles,
48817
49044
  parseErrors,
49045
+ warnings,
48818
49046
  reconstructed: /* @__PURE__ */ new Map(),
48819
49047
  staticValueSeeds,
48820
49048
  binaryChanges: [],
@@ -48862,6 +49090,7 @@ function computeWorkspaceStatus(workspace, options = {}) {
48862
49090
  changes: [],
48863
49091
  conflictedFiles,
48864
49092
  parseErrors,
49093
+ warnings,
48865
49094
  reconstructed: /* @__PURE__ */ new Map(),
48866
49095
  staticValueSeeds: /* @__PURE__ */ new Map(),
48867
49096
  binaryChanges: [],
@@ -49078,15 +49307,16 @@ function computeWorkspaceStatus(workspace, options = {}) {
49078
49307
  )) {
49079
49308
  parseErrors.push(new SchemaSourceError(message, "<project-files>", 1, 1));
49080
49309
  }
49310
+ let validatedAnimationRecords = null;
49081
49311
  try {
49082
- validateProspectiveAnimationRecordsV4(
49083
- prospectiveAnimationRecords(
49084
- workspace.state.records,
49085
- reconstructed3,
49086
- changes,
49087
- staticValueSeeds
49088
- )
49312
+ const animationRecords = prospectiveAnimationRecords(
49313
+ workspace.state.records,
49314
+ reconstructed3,
49315
+ changes,
49316
+ staticValueSeeds
49089
49317
  );
49318
+ validateProspectiveAnimationRecordsV4(animationRecords);
49319
+ validatedAnimationRecords = animationRecords;
49090
49320
  } catch (error) {
49091
49321
  parseErrors.push(
49092
49322
  new SchemaSourceError(
@@ -49097,6 +49327,11 @@ function computeWorkspaceStatus(workspace, options = {}) {
49097
49327
  )
49098
49328
  );
49099
49329
  }
49330
+ if (validatedAnimationRecords !== null) {
49331
+ warnings.push(
49332
+ ...collectProspectiveAnimationWarningsV4(validatedAnimationRecords)
49333
+ );
49334
+ }
49100
49335
  for (const binary of binaryChanges) {
49101
49336
  if (binary.action !== "missing-local") continue;
49102
49337
  parseErrors.push(
@@ -49112,6 +49347,7 @@ function computeWorkspaceStatus(workspace, options = {}) {
49112
49347
  changes,
49113
49348
  conflictedFiles,
49114
49349
  parseErrors,
49350
+ warnings,
49115
49351
  reconstructed: reconstructed3,
49116
49352
  staticValueSeeds,
49117
49353
  binaryChanges,
@@ -49119,11 +49355,27 @@ function computeWorkspaceStatus(workspace, options = {}) {
49119
49355
  };
49120
49356
  }
49121
49357
  function validateProspectiveAnimationRecordsV4(records2) {
49358
+ const document = prospectiveAnimationDocumentV4(records2);
49359
+ if (document === null) return;
49360
+ assertAnimationClipDocumentValid(document);
49361
+ }
49362
+ function collectProspectiveAnimationWarningsV4(records2) {
49363
+ try {
49364
+ const document = prospectiveAnimationDocumentV4(records2);
49365
+ if (document === null) return [];
49366
+ return collectAnimationClipNestedOwnerWarnings(document).map(
49367
+ (warning) => warning.message
49368
+ );
49369
+ } catch {
49370
+ return [];
49371
+ }
49372
+ }
49373
+ function prospectiveAnimationDocumentV4(records2) {
49122
49374
  const candidates = [...records2];
49123
49375
  const hasAnimationSchema = candidates.some(
49124
49376
  (record3) => record3.recordKind === "class" && (Array.isArray(record3.data.constructorProjections) || isObjectRecord2(record3.data.system) && typeof record3.data.system.worldKind === "string" && record3.data.system.worldKind.startsWith("animation"))
49125
49377
  );
49126
- if (!hasAnimationSchema) return;
49378
+ if (!hasAnimationSchema) return null;
49127
49379
  let project;
49128
49380
  const classes = [];
49129
49381
  const members = [];
@@ -49159,8 +49411,8 @@ function validateProspectiveAnimationRecordsV4(records2) {
49159
49411
  values.push(record3.data);
49160
49412
  }
49161
49413
  }
49162
- if (project === void 0) return;
49163
- assertAnimationClipDocumentValid({ project, classes, members, values });
49414
+ if (project === void 0) return null;
49415
+ return { project, classes, members, values };
49164
49416
  }
49165
49417
  function serverEnvelope(stored) {
49166
49418
  if (!isObjectRecord2(stored)) return {};
@@ -67829,6 +68081,9 @@ ${blockingErrors.map((error) => ` ${error.message}`).join("\n")}`
67829
68081
  changes: status.changes,
67830
68082
  binaryChanges: status.binaryChanges ?? []
67831
68083
  });
68084
+ if (options.json !== true) {
68085
+ for (const message of status.warnings) warn(message);
68086
+ }
67832
68087
  if (status.changes.length === 0) {
67833
68088
  if (options.json === true) {
67834
68089
  console.log(
@@ -67837,7 +68092,8 @@ ${blockingErrors.map((error) => ` ${error.message}`).join("\n")}`
67837
68092
  dryRun: options.dryRun,
67838
68093
  changeCount: 0,
67839
68094
  staticValueSeedCount: 0,
67840
- changes: []
68095
+ changes: [],
68096
+ warnings: status.warnings
67841
68097
  })
67842
68098
  );
67843
68099
  } else {
@@ -67899,7 +68155,8 @@ ${blockingErrors.map((error) => ` ${error.message}`).join("\n")}`
67899
68155
  kind: change.kind,
67900
68156
  recordKind: change.recordKind,
67901
68157
  recordId: change.recordId
67902
- }))
68158
+ })),
68159
+ warnings: status.warnings
67903
68160
  })
67904
68161
  );
67905
68162
  } else {
@@ -70103,6 +70360,7 @@ function projectStatusJsonV4(status, options) {
70103
70360
  blocking: isBlockingSchemaSourceError(error),
70104
70361
  message: error.message
70105
70362
  })),
70363
+ warnings: status.warnings,
70106
70364
  records: status.changes.map(
70107
70365
  (change) => recordChangeJsonV4(change, status, options)
70108
70366
  ),
@@ -70781,6 +71039,9 @@ async function main() {
70781
71039
  for (const error of status.parseErrors) {
70782
71040
  console.log(`${sym.warn} ${error.message}`);
70783
71041
  }
71042
+ for (const message of status.warnings) {
71043
+ console.log(`${sym.warn} ${message}`);
71044
+ }
70784
71045
  for (const group of groupProjectStatusChangesV4(status)) {
70785
71046
  console.log(color.bold(group.source));
70786
71047
  for (const change of group.changes) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neocompose/cli",
3
- "version": "0.11.1",
3
+ "version": "0.12.0",
4
4
  "description": "Neo Compose native project-source CLI with bidirectional sync.",
5
5
  "license": "MIT",
6
6
  "type": "module",