@neocompose/cli 0.31.11 → 0.31.13

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,20 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.31.13] - 2026-08-18
4
+
5
+ ### Fixed
6
+
7
+ - Encode native and NeoScript function overrides with sparse inherited
8
+ signatures, preventing validation from rejecting callable overrides.
9
+
10
+ ## [0.31.12] - 2026-08-17
11
+
12
+ ### Fixed
13
+
14
+ - Re-emit stored variant member selections through canonical
15
+ `<Class>.Variants...` paths, including variants nested in class and list
16
+ rows, instead of failing with an unsupported numeric value kind.
17
+
3
18
  ## [0.31.11] - 2026-08-17
4
19
 
5
20
  ### Fixed
package/dist/neo.mjs CHANGED
@@ -35383,9 +35383,11 @@ function memberToDocumentFields(member, baseData3) {
35383
35383
  fields.templateId = member.templateId;
35384
35384
  }
35385
35385
  if (member.kind === "function" || member.kind === "scriptFunction") {
35386
- fields.returnTypeInfo = manifestReturnTypeToDocument(member.returnType);
35387
- fields.argumentTypes = member.arguments.map(manifestArgumentToDocument);
35388
- fields.deferred = member.deferred;
35386
+ if (member.overrideOf === null) {
35387
+ fields.returnTypeInfo = manifestReturnTypeToDocument(member.returnType);
35388
+ fields.argumentTypes = member.arguments.map(manifestArgumentToDocument);
35389
+ fields.deferred = member.deferred;
35390
+ }
35389
35391
  if (member.kind === "scriptFunction" && member.script !== null && member.bodyMode !== "ui") {
35390
35392
  fields.code = member.script.sourceText;
35391
35393
  }
@@ -49634,6 +49636,93 @@ var init_structured_leaf_source = __esm({
49634
49636
  }
49635
49637
  });
49636
49638
 
49639
+ // src/project-source/variant-selection-source.ts
49640
+ function isObject2(value) {
49641
+ return typeof value === "object" && value !== null && !Array.isArray(value);
49642
+ }
49643
+ function buildVariantPathsByIdV4(manifest) {
49644
+ const variants = manifest.variants ?? [];
49645
+ if (variants.length === 0) return /* @__PURE__ */ new Map();
49646
+ const classNames = new Map(
49647
+ manifest.classes.map((entry) => [entry.id, entry.name])
49648
+ );
49649
+ const folderPaths = new Map(
49650
+ (manifest.variantFolders ?? []).map(
49651
+ (entry) => [entry.id, entry.path]
49652
+ )
49653
+ );
49654
+ const paths = /* @__PURE__ */ new Map();
49655
+ for (const variant of variants) {
49656
+ const className = classNames.get(variant.classId);
49657
+ if (className === void 0) continue;
49658
+ paths.set(
49659
+ variant.id,
49660
+ neoVariantDottedPath({
49661
+ className,
49662
+ folderPath: variant.folderId === null ? "" : folderPaths.get(variant.folderId) ?? "",
49663
+ variantName: variant.name
49664
+ })
49665
+ );
49666
+ }
49667
+ return paths;
49668
+ }
49669
+ function renderVariantSelectionV4(context, memberName, value) {
49670
+ if (value === null) return "null";
49671
+ if (!isObject2(value)) {
49672
+ throw new Error(
49673
+ `Variant member ${memberName} stores ${JSON.stringify(value)}, which is not a { classId, variantId } selection.`
49674
+ );
49675
+ }
49676
+ const variantId = value.variantId;
49677
+ if (variantId === null) {
49678
+ const classId = value.classId;
49679
+ if (typeof classId !== "string") {
49680
+ throw new Error(
49681
+ `Variant member ${memberName} selects the base entry without naming a class.`
49682
+ );
49683
+ }
49684
+ const className = context.classes.get(classId)?.name;
49685
+ if (className === void 0) {
49686
+ throw new Error(
49687
+ `Variant member ${memberName} selects class ${JSON.stringify(classId)}, which this project does not declare.`
49688
+ );
49689
+ }
49690
+ return neoVariantDottedPath({
49691
+ className,
49692
+ folderPath: "",
49693
+ variantName: null
49694
+ });
49695
+ }
49696
+ if (typeof variantId !== "string") {
49697
+ throw new Error(
49698
+ `Variant member ${memberName} stores a variant id that is neither a string nor null.`
49699
+ );
49700
+ }
49701
+ const path = context.variantPathsById.get(variantId);
49702
+ if (path === void 0) {
49703
+ throw new Error(
49704
+ `Variant member ${memberName} selects variant ${JSON.stringify(variantId)}, which this project does not declare.`
49705
+ );
49706
+ }
49707
+ const rowValueId = value.rowValueId;
49708
+ if (rowValueId === null || rowValueId === void 0) return path;
49709
+ if (typeof rowValueId !== "string") {
49710
+ throw new Error(
49711
+ `Variant member ${memberName} stores a malformed rowValueId.`
49712
+ );
49713
+ }
49714
+ const rootPath = context.collectionValuePaths.get(rowValueId);
49715
+ const reference2 = rootPath === void 0 ? `Reference(id: ${quoteNeoString(rowValueId)})` : `Reference(${rootPath})`;
49716
+ return `${path}.Bind(${reference2})`;
49717
+ }
49718
+ var init_variant_selection_source = __esm({
49719
+ "src/project-source/variant-selection-source.ts"() {
49720
+ "use strict";
49721
+ init_src();
49722
+ init_source_format();
49723
+ }
49724
+ });
49725
+
49637
49726
  // src/project-source/emitter.ts
49638
49727
  function emitProjectSourcesV4(manifest, options = {}) {
49639
49728
  const context = {
@@ -49649,7 +49738,7 @@ function emitProjectSourcesV4(manifest, options = {}) {
49649
49738
  defaultInitializers: options.defaultInitializers ?? /* @__PURE__ */ new Map(),
49650
49739
  fileSymbols: options.fileSymbols ?? /* @__PURE__ */ new Map(),
49651
49740
  collectionValuePaths: options.collectionValuePaths ?? /* @__PURE__ */ new Map(),
49652
- variantPathsById: buildVariantPathsById(manifest),
49741
+ variantPathsById: buildVariantPathsByIdV4(manifest),
49653
49742
  templateNames: new Map(
49654
49743
  [...manifest.textureTemplates, ...manifest.audioTemplates].map(
49655
49744
  (template) => [template.id, template.name]
@@ -50525,7 +50614,7 @@ function renderDefault(context, member) {
50525
50614
  return renderDefaultValue(context, member, value);
50526
50615
  }
50527
50616
  function renderActionListeners(context, member, value) {
50528
- if (!isObject2(value)) {
50617
+ if (!isObject3(value)) {
50529
50618
  throw new Error(
50530
50619
  `NeoAction member ${member.name} stores a default that is not a listener set.`
50531
50620
  );
@@ -50537,7 +50626,7 @@ function renderActionListeners(context, member, value) {
50537
50626
  );
50538
50627
  }
50539
50628
  const names = listeners.map((listener, index) => {
50540
- if (!isObject2(listener)) {
50629
+ if (!isObject3(listener)) {
50541
50630
  throw new Error(
50542
50631
  `NeoAction member ${member.name} listener ${index} is not a member target.`
50543
50632
  );
@@ -50598,7 +50687,7 @@ function renderDefaultValue(context, member, wrapper) {
50598
50687
  const entry = required(context.members, member.entryMemberId, "list entry");
50599
50688
  return `[${value.map((item) => renderNestedValue(context, entry, item)).join(", ")}]`;
50600
50689
  }
50601
- if (member.kind === "dictionary" && isObject2(value)) {
50690
+ if (member.kind === "dictionary" && isObject3(value)) {
50602
50691
  const entry = required(
50603
50692
  context.members,
50604
50693
  member.entryMemberId,
@@ -50608,7 +50697,7 @@ function renderDefaultValue(context, member, wrapper) {
50608
50697
  ([key, item]) => `${quote(key)}: ${renderNestedValue(context, entry, item)}`
50609
50698
  ).join(", ")} }`;
50610
50699
  }
50611
- if (member.kind === "class" && isObject2(value)) {
50700
+ if (member.kind === "class" && isObject3(value)) {
50612
50701
  const target = required(context.classes, member.classId, "class");
50613
50702
  const assignments = Object.entries(value).map(([key, item]) => {
50614
50703
  const childId = target.schema[key];
@@ -50625,9 +50714,9 @@ function renderDefaultValue(context, member, wrapper) {
50625
50714
  return renderActionListeners(context, member, value);
50626
50715
  }
50627
50716
  if (member.kind === "variant") {
50628
- return renderVariantSelection(context, member, value);
50717
+ return renderVariantSelectionV4(context, member.name, value);
50629
50718
  }
50630
- if (member.kind === "functionRef" && isObject2(value) && typeof value.functionMemberId === "string") {
50719
+ if (member.kind === "functionRef" && isObject3(value) && typeof value.functionMemberId === "string") {
50631
50720
  return required(
50632
50721
  context.members,
50633
50722
  value.functionMemberId,
@@ -50663,7 +50752,7 @@ function renderDefaultValue(context, member, wrapper) {
50663
50752
  return `new { ${assignments.join(", ")} }`;
50664
50753
  }
50665
50754
  if (member.kind === "sprite" || member.kind === "audio") {
50666
- if (!isObject2(value)) return jsonValue2(value);
50755
+ if (!isObject3(value)) return jsonValue2(value);
50667
50756
  if (typeof value.fileId !== "string") {
50668
50757
  throw new Error(
50669
50758
  `${member.kind === "sprite" ? "Sprite" : "Audio"} member ${JSON.stringify(member.name)} default stores a file value with no fileId. Emitting it as null would delete the default on the next push.`
@@ -50682,7 +50771,7 @@ function renderDefaultValue(context, member, wrapper) {
50682
50771
  const target = symbol ?? (member.kind === "sprite" ? `Reference<NeoImage>(id: ${quote(value.fileId)})` : `Reference<NeoAudioClip>(id: ${quote(value.fileId)})`);
50683
50772
  return member.kind === "sprite" ? `${target}.Slice(${typeof value.sliceIndex === "number" ? value.sliceIndex : 0})` : target;
50684
50773
  }
50685
- if (leafKind !== null && isObject2(value)) {
50774
+ if (leafKind !== null && isObject3(value)) {
50686
50775
  return `new(${structuredLeafFieldKeys(leafKind).map((key) => jsonValue2(value[key])).join(", ")})`;
50687
50776
  }
50688
50777
  return jsonValue2(value);
@@ -50977,7 +51066,7 @@ function jsonValue2(value) {
50977
51066
  if (typeof value === "boolean") return String(value);
50978
51067
  if (value === null || value === void 0) return "null";
50979
51068
  if (Array.isArray(value)) return `[${value.map(jsonValue2).join(", ")}]`;
50980
- if (isObject2(value))
51069
+ if (isObject3(value))
50981
51070
  return `{ ${Object.entries(value).map(([key, entry]) => `${quote(key)}: ${jsonValue2(entry)}`).join(", ")} }`;
50982
51071
  throw new Error(`Cannot emit Neo value ${String(value)}.`);
50983
51072
  }
@@ -51182,81 +51271,9 @@ function required(map, key, kind) {
51182
51271
  if (!value) throw new Error(`Unknown ${kind} ${quote(key)}.`);
51183
51272
  return value;
51184
51273
  }
51185
- function isObject2(value) {
51274
+ function isObject3(value) {
51186
51275
  return typeof value === "object" && value !== null && !Array.isArray(value);
51187
51276
  }
51188
- function buildVariantPathsById(manifest) {
51189
- if (manifest.variants.length === 0) return /* @__PURE__ */ new Map();
51190
- const classNames = new Map(
51191
- manifest.classes.map((entry) => [entry.id, entry.name])
51192
- );
51193
- const folderPaths = new Map(
51194
- manifest.variantFolders.map((entry) => [entry.id, entry.path])
51195
- );
51196
- const paths = /* @__PURE__ */ new Map();
51197
- for (const variant of manifest.variants) {
51198
- const className = classNames.get(variant.classId);
51199
- if (className === void 0) continue;
51200
- paths.set(
51201
- variant.id,
51202
- neoVariantDottedPath({
51203
- className,
51204
- folderPath: variant.folderId === null ? "" : folderPaths.get(variant.folderId) ?? "",
51205
- variantName: variant.name
51206
- })
51207
- );
51208
- }
51209
- return paths;
51210
- }
51211
- function renderVariantSelection(context, member, value) {
51212
- if (value === null) return "null";
51213
- if (!isObject2(value)) {
51214
- throw new Error(
51215
- `Variant member ${member.name} stores ${JSON.stringify(value)}, which is not a { classId, variantId } selection.`
51216
- );
51217
- }
51218
- const variantId = value.variantId;
51219
- if (variantId === null) {
51220
- const classId = value.classId;
51221
- if (typeof classId !== "string") {
51222
- throw new Error(
51223
- `Variant member ${member.name} selects the base entry without naming a class.`
51224
- );
51225
- }
51226
- const className = context.classes.get(classId)?.name;
51227
- if (className === void 0) {
51228
- throw new Error(
51229
- `Variant member ${member.name} selects class ${JSON.stringify(classId)}, which this project does not declare.`
51230
- );
51231
- }
51232
- return neoVariantDottedPath({
51233
- className,
51234
- folderPath: "",
51235
- variantName: null
51236
- });
51237
- }
51238
- if (typeof variantId !== "string") {
51239
- throw new Error(
51240
- `Variant member ${member.name} stores a variant id that is neither a string nor null.`
51241
- );
51242
- }
51243
- const path = context.variantPathsById.get(variantId);
51244
- if (path === void 0) {
51245
- throw new Error(
51246
- `Variant member ${member.name} selects variant ${JSON.stringify(variantId)}, which this project does not declare.`
51247
- );
51248
- }
51249
- const rowValueId = value.rowValueId;
51250
- if (rowValueId === null || rowValueId === void 0) return path;
51251
- if (typeof rowValueId !== "string") {
51252
- throw new Error(
51253
- `Variant member ${member.name} stores a malformed rowValueId.`
51254
- );
51255
- }
51256
- const rootPath = context.collectionValuePaths.get(rowValueId);
51257
- const reference2 = rootPath === void 0 ? `Reference(id: ${quote(rowValueId)})` : `Reference(${rootPath})`;
51258
- return `${path}.Bind(${reference2})`;
51259
- }
51260
51277
  var SPECIALIZED_RELATION_PROPERTIES;
51261
51278
  var init_emitter = __esm({
51262
51279
  "src/project-source/emitter.ts"() {
@@ -51267,6 +51284,7 @@ var init_emitter = __esm({
51267
51284
  init_class_generics();
51268
51285
  init_members();
51269
51286
  init_structured_leaf_source();
51287
+ init_variant_selection_source();
51270
51288
  SPECIALIZED_RELATION_PROPERTIES = /* @__PURE__ */ new Map([
51271
51289
  ["world.grid.tile-import", { property: "tileImports", collection: true }],
51272
51290
  ["world.grid.object-import", { property: "objectImports", collection: true }],
@@ -73235,7 +73253,7 @@ async function fetchProjectDocumentPartitionRecords(runner, mapKey) {
73235
73253
  });
73236
73254
  }
73237
73255
  function readProjectDocumentManifestPage(value) {
73238
- if (!isObject3(value)) {
73256
+ if (!isObject4(value)) {
73239
73257
  throw new Error("Convex project document manifest page must be an object.");
73240
73258
  }
73241
73259
  const continueCursor = value.continueCursor;
@@ -73245,7 +73263,7 @@ function readProjectDocumentManifestPage(value) {
73245
73263
  );
73246
73264
  }
73247
73265
  const metadata = value.metadata ?? null;
73248
- if (metadata !== null && !isObject3(metadata)) {
73266
+ if (metadata !== null && !isObject4(metadata)) {
73249
73267
  throw new Error(
73250
73268
  "Convex project document manifest page metadata must be an object or null."
73251
73269
  );
@@ -73270,7 +73288,7 @@ function readProjectDocumentManifestRecords(value) {
73270
73288
  );
73271
73289
  }
73272
73290
  return value.map((record3, index) => {
73273
- if (!isObject3(record3)) {
73291
+ if (!isObject4(record3)) {
73274
73292
  throw new Error(
73275
73293
  `Convex project document manifest record at index ${index} was not an object.`
73276
73294
  );
@@ -73304,7 +73322,7 @@ function readProjectDocumentManifestRecords(value) {
73304
73322
  });
73305
73323
  }
73306
73324
  function readProjectDocumentManifestPageRecords(value) {
73307
- if (!isObject3(value)) {
73325
+ if (!isObject4(value)) {
73308
73326
  throw new Error("Convex project document manifest page must be an object.");
73309
73327
  }
73310
73328
  return readProjectDocumentManifestRecords(value.records);
@@ -73342,7 +73360,7 @@ async function fetchProjectDocumentSnapshots(fetchBatch, snapshotIds) {
73342
73360
  return snapshotsById;
73343
73361
  }
73344
73362
  function readProjectDocumentSnapshotBatch(value, requestedIds) {
73345
- if (!isObject3(value)) {
73363
+ if (!isObject4(value)) {
73346
73364
  throw new Error("Convex project record snapshot batch must be an object.");
73347
73365
  }
73348
73366
  const snapshots = value.snapshots;
@@ -73362,7 +73380,7 @@ function readProjectDocumentSnapshotBatch(value, requestedIds) {
73362
73380
  );
73363
73381
  }
73364
73382
  return snapshots.map((snapshot, index) => {
73365
- if (!isObject3(snapshot)) {
73383
+ if (!isObject4(snapshot)) {
73366
73384
  throw new Error(
73367
73385
  `Convex project record snapshot at index ${index} was not an object.`
73368
73386
  );
@@ -73544,7 +73562,7 @@ function appendBucketRecord(buckets, recordKind, data) {
73544
73562
  buckets[bucket].push(data);
73545
73563
  }
73546
73564
  function readProjectDocument(value, options = {}) {
73547
- if (!isObject3(value)) {
73565
+ if (!isObject4(value)) {
73548
73566
  throw new Error("Convex project document query returned a non-object.");
73549
73567
  }
73550
73568
  const localizationConfig = options.localizationConfig === "nullable" ? readOptionalNullableField(
@@ -73634,7 +73652,7 @@ function readProjectDocument(value, options = {}) {
73634
73652
  };
73635
73653
  }
73636
73654
  function readProjectDocumentRevisionMarker(value) {
73637
- if (!isObject3(value)) return null;
73655
+ if (!isObject4(value)) return null;
73638
73656
  if (!("documentRevision" in value)) return null;
73639
73657
  const marker = value.documentRevision;
73640
73658
  if (isProjectDocumentRevisionMarker(marker)) return marker;
@@ -73643,7 +73661,7 @@ function readProjectDocumentRevisionMarker(value) {
73643
73661
  );
73644
73662
  }
73645
73663
  function readProjectDocumentContentHashHeads(value) {
73646
- if (!isObject3(value)) {
73664
+ if (!isObject4(value)) {
73647
73665
  throw new Error(
73648
73666
  "Convex project document content hash source must be an object."
73649
73667
  );
@@ -73651,7 +73669,7 @@ function readProjectDocumentContentHashHeads(value) {
73651
73669
  return readArrayField(value, "projectDocumentContentHashHeads", isHashHead);
73652
73670
  }
73653
73671
  function isHashHead(value) {
73654
- if (!isObject3(value)) return false;
73672
+ if (!isObject4(value)) return false;
73655
73673
  if (!isProjectRecordKind(value.recordKind)) return false;
73656
73674
  if (typeof value.recordId !== "string") return false;
73657
73675
  if (typeof value.deleted !== "boolean") return false;
@@ -73687,12 +73705,12 @@ function readOptionalArrayField(source, key, isValid) {
73687
73705
  if (!Object.prototype.hasOwnProperty.call(source, key)) return [];
73688
73706
  return readArrayField(source, key, isValid);
73689
73707
  }
73690
- function isObject3(value) {
73708
+ function isObject4(value) {
73691
73709
  if (typeof value !== "object") return false;
73692
73710
  return value !== null;
73693
73711
  }
73694
73712
  function describeInvalidReadItem(value) {
73695
- if (!isObject3(value)) {
73713
+ if (!isObject4(value)) {
73696
73714
  return `received ${typeof value}`;
73697
73715
  }
73698
73716
  const parts = [];
@@ -93439,6 +93457,9 @@ function replayAnimationDeclarationInitializersV4(records2, document, fallbackDo
93439
93457
  rootOwners,
93440
93458
  rootKinds
93441
93459
  );
93460
+ const resolvedMembers = new Map(
93461
+ document.members.map((member) => [member.id, member])
93462
+ );
93442
93463
  const pulled = /* @__PURE__ */ new Map();
93443
93464
  for (const record3 of records2) {
93444
93465
  const recordId = record3.data.id;
@@ -93448,7 +93469,10 @@ function replayAnimationDeclarationInitializersV4(records2, document, fallbackDo
93448
93469
  recordId,
93449
93470
  contentHash: "prospective-animation-validation",
93450
93471
  deleted: false,
93451
- data: record3.data
93472
+ // Initializer replay consumes the runtime document shape too. Reuse the
93473
+ // transiently hydrated member instead of feeding sparse callable
93474
+ // overrides back through the strict full-record reader.
93475
+ data: record3.recordKind === "member" ? resolvedMembers.get(recordId) ?? record3.data : record3.data
93452
93476
  });
93453
93477
  }
93454
93478
  const replayDocument = readPulledProjectDocumentV4(pulled);
@@ -93566,6 +93590,11 @@ function prospectiveAnimationDocumentV4(records2) {
93566
93590
  (record3) => record3.recordKind === "class" && (Array.isArray(record3.data.constructorProjections) || declaresAnimationWorldKind(record3.data.system))
93567
93591
  );
93568
93592
  if (!hasAnimationSchema) return null;
93593
+ const memberRecords = new Map(
93594
+ candidates.flatMap(
93595
+ (record3) => record3.recordKind === "member" && typeof record3.data.id === "string" ? [[record3.data.id, record3.data]] : []
93596
+ )
93597
+ );
93569
93598
  let project;
93570
93599
  const classes = [];
93571
93600
  const members = [];
@@ -93586,12 +93615,16 @@ function prospectiveAnimationDocumentV4(records2) {
93586
93615
  }
93587
93616
  classes.push(record3.data);
93588
93617
  } else if (record3.recordKind === "member") {
93589
- if (!isMember(record3.data)) {
93618
+ const member = hydrateProspectiveCallableOverride(
93619
+ record3.data,
93620
+ memberRecords
93621
+ );
93622
+ if (!isMember(member)) {
93590
93623
  throw new Error(
93591
93624
  `Prospective animation validation received an invalid member record ${recordLabel(record3.data)}.`
93592
93625
  );
93593
93626
  }
93594
- members.push(record3.data);
93627
+ members.push(member);
93595
93628
  } else if (record3.recordKind === "value") {
93596
93629
  if (!isMemberValue(record3.data)) {
93597
93630
  throw new Error(
@@ -93625,6 +93658,34 @@ function prospectiveAnimationDocumentV4(records2) {
93625
93658
  });
93626
93659
  return { project, classes, members, values: validationValues };
93627
93660
  }
93661
+ function hydrateProspectiveCallableOverride(data, members) {
93662
+ if (data.kind !== 13 /* Function */ && data.kind !== 23 /* NSFunction */ || typeof data.extendsMemberId !== "string") {
93663
+ return data;
93664
+ }
93665
+ const hydrated = { ...data };
93666
+ for (const field of [
93667
+ "returnTypeInfo",
93668
+ "argumentTypes",
93669
+ "deferred"
93670
+ ]) {
93671
+ if (Object.hasOwn(hydrated, field)) continue;
93672
+ const inherited = resolveProspectiveMemberField(data, field, members);
93673
+ if (inherited !== void 0) hydrated[field] = inherited;
93674
+ }
93675
+ return hydrated;
93676
+ }
93677
+ function resolveProspectiveMemberField(start, field, members) {
93678
+ let current = start;
93679
+ const visited = /* @__PURE__ */ new Set();
93680
+ while (current !== void 0) {
93681
+ if (Object.hasOwn(current, field)) return current[field];
93682
+ const parentId = current.extendsMemberId;
93683
+ if (typeof parentId !== "string" || visited.has(parentId)) return void 0;
93684
+ visited.add(parentId);
93685
+ current = members.get(parentId);
93686
+ }
93687
+ return void 0;
93688
+ }
93628
93689
  function declaresAnimationWorldKind(system) {
93629
93690
  if (!isObjectRecord2(system)) return false;
93630
93691
  const worldKind = system.worldKind;
@@ -95207,6 +95268,7 @@ function buildValueEmitContext(records2, manifest) {
95207
95268
  mainLocale: projectMainLocale(records2),
95208
95269
  symbolsByValueId,
95209
95270
  rootPathsByValueId: rootValuePathsByValueId(records2.values()),
95271
+ variantPathsById: buildVariantPathsByIdV4(manifest),
95210
95272
  symbolsByDialogueId: new Map(
95211
95273
  [...dialogues].flatMap(
95212
95274
  ([id2, dialogue]) => typeof dialogue.sourceName === "string" ? [[id2, dialogue.sourceName]] : []
@@ -100098,6 +100160,17 @@ function emitValueBody(context, member, value, visited, targetTyped, environment
100098
100160
  if (kind === 24) return functionReferenceValue(context, body);
100099
100161
  if (kind === 25) return delegateValue(context, body);
100100
100162
  if (kind === 26) return actionValue(context, body);
100163
+ if (kind === 27 /* Variant */) {
100164
+ return renderVariantSelectionV4(
100165
+ {
100166
+ classes: context.manifestClasses,
100167
+ variantPathsById: context.variantPathsById,
100168
+ collectionValuePaths: context.rootPathsByValueId
100169
+ },
100170
+ String(member.name ?? member.id),
100171
+ body
100172
+ );
100173
+ }
100101
100174
  if (kind === 12) {
100102
100175
  return fileValue(
100103
100176
  context,
@@ -100120,8 +100193,9 @@ function emitValueBody(context, member, value, visited, targetTyped, environment
100120
100193
  targetTyped,
100121
100194
  environment
100122
100195
  );
100196
+ const kindName = MemberKind[kind];
100123
100197
  throw new Error(
100124
- `Stored member ${String(member.name ?? member.id)} has unsupported value kind ${kind}.`
100198
+ typeof kindName === "string" ? `Stored member ${String(member.name ?? member.id)} uses unsupported ${kindName} value kind (${kind}).` : `Stored member ${String(member.name ?? member.id)} has unknown value kind ${kind}.`
100125
100199
  );
100126
100200
  }
100127
100201
  function classValue(context, member, value, visited, targetTyped, outerEnvironment) {
@@ -101690,6 +101764,7 @@ var init_value_sources = __esm({
101690
101764
  init_structured_leaf_source();
101691
101765
  init_member_kind_type_names();
101692
101766
  init_initializer_replay();
101767
+ init_variant_selection_source();
101693
101768
  INFERRED_GENERIC_CLASS_PREFIX = "__inferred_class__:";
101694
101769
  MEMBER_KIND_DICTIONARY2 = 5;
101695
101770
  MEMBER_KIND_LIST2 = 6;
@@ -111519,7 +111594,7 @@ var init_registry2 = __esm({
111519
111594
  PROJECT_SCHEMA_CONTRACT = Object.freeze({
111520
111595
  formatVersion: 3,
111521
111596
  contractVersion: "3.13",
111522
- cliVersion: "0.31.11",
111597
+ cliVersion: "0.31.13",
111523
111598
  projectFileUploadBatchSize: 32,
111524
111599
  documentRecords: {
111525
111600
  member: {
@@ -118114,7 +118189,7 @@ There is no --keep-current: preserving current semantic state defines flatten.
118114
118189
  async function main() {
118115
118190
  const args = parseArgs(process.argv.slice(2));
118116
118191
  if (args.command === "--version") {
118117
- console.log("0.31.11");
118192
+ console.log("0.31.13");
118118
118193
  return;
118119
118194
  }
118120
118195
  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.31.11",
3
+ "version": "0.31.13",
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.31.11 -->
12
+ <!-- reviewed-through-cli: 0.31.13 -->
13
13
 
14
14
  # Neo Compose CLI
15
15
 
@@ -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.31.11 -->
86
+ <!-- reviewed-through-cli: 0.31.13 -->
87
87
  ```
88
88
 
89
89
  The quoted version above is checked too, so this instruction cannot go stale
@@ -56,6 +56,17 @@ or narrowing guard before the next access.
56
56
 
57
57
  Treat lookup-return values with the same nullable narrowing rules.
58
58
 
59
+ A declaration pattern introduced by `is` stays available after a guard when
60
+ every path that falls through matched the pattern, including when the guard's
61
+ other paths return from a nested block:
62
+
63
+ ```neo
64
+ if (!(item is BedItem bedItem)) {
65
+ throw "Expected a bed item";
66
+ }
67
+ return bedItem.Name;
68
+ ```
69
+
59
70
  Use `SpriteInfo.Empty` when a required sprite should intentionally render no
60
71
  image. It is the canonical non-null empty sprite: it has no backing file and
61
72
  uses slice index zero.