@neocompose/cli 0.84.3 → 0.84.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3302,14 +3302,14 @@ function isNSFunctionContains(value) {
3302
3302
  if (v?.type !== "contains" /* contains */) return false;
3303
3303
  const info = v.info;
3304
3304
  if (typeof info !== "object" || info === null) return false;
3305
- return isNSPointer(info.collectionPointer) && isNSPointer(info.valuePointer) && isReferenceFlag(info.reference);
3305
+ return isNSPointer(info.collectionPointer) && isNSPointer(info.valuePointer) && isReferenceFlag(info.reference) && (info.float === void 0 || info.float === true) && (info.decimal === void 0 || info.decimal === true);
3306
3306
  }
3307
3307
  function isNSFunctionIndexOf(value) {
3308
3308
  const v = value;
3309
3309
  if (v?.type !== "indexOf" /* indexOf */) return false;
3310
3310
  const info = v.info;
3311
3311
  if (typeof info !== "object" || info === null) return false;
3312
- return isNSPointer(info.collectionPointer) && isNSPointer(info.valuePointer) && isReferenceFlag(info.reference);
3312
+ return isNSPointer(info.collectionPointer) && isNSPointer(info.valuePointer) && isReferenceFlag(info.reference) && (info.float === void 0 || info.float === true) && (info.decimal === void 0 || info.decimal === true);
3313
3313
  }
3314
3314
  var NS_STRING_OPS = [
3315
3315
  "toLower",
@@ -3483,7 +3483,7 @@ function isNSFunctionCollectionQuery(value) {
3483
3483
  case "Reverse":
3484
3484
  return true;
3485
3485
  case "ToSet":
3486
- return info.reference === void 0 || info.reference === true;
3486
+ return (info.reference === void 0 || info.reference === true) && (info.decimalEntries === void 0 || info.decimalEntries === true);
3487
3487
  default:
3488
3488
  return false;
3489
3489
  }
@@ -17029,16 +17029,18 @@ var UncompiledInitializerRuntimeError = class extends NSGetterRuntimeError {
17029
17029
 
17030
17030
  // ../packages/neoscript-runtime/src/neoscript/runtime-set.ts
17031
17031
  var RuntimeSet = class {
17032
- constructor(source, reference, enumEntries, slotReferences = null) {
17032
+ constructor(source, reference, enumEntries, slotReferences = null, decimalEntries = false) {
17033
17033
  this.source = source;
17034
17034
  this.reference = reference;
17035
17035
  this.enumEntries = enumEntries;
17036
17036
  this.slotReferences = slotReferences;
17037
+ this.decimalEntries = decimalEntries;
17037
17038
  }
17038
17039
  source;
17039
17040
  reference;
17040
17041
  enumEntries;
17041
17042
  slotReferences;
17043
+ decimalEntries;
17042
17044
  entries = /* @__PURE__ */ new Map();
17043
17045
  validations = /* @__PURE__ */ new Map();
17044
17046
  snapshot;
@@ -17048,6 +17050,9 @@ var RuntimeSet = class {
17048
17050
  has(key) {
17049
17051
  return this.entries.has(key);
17050
17052
  }
17053
+ *values() {
17054
+ for (const entry of this.entries.values()) yield entry.raw;
17055
+ }
17051
17056
  add(key, raw, reference) {
17052
17057
  if (this.entries.has(key)) return;
17053
17058
  this.entries.set(key, { raw, reference });
@@ -21440,7 +21445,14 @@ function evaluateNSGetterWithEffects(getter, ctx, argumentValues = [], options =
21440
21445
  for (let index = 0; index < argumentValues.length; index += 1) {
21441
21446
  const parameter3 = parameters[index];
21442
21447
  if (parameter3 === void 0) continue;
21443
- scope.setLocal(parameter3.id, argumentValues[index]);
21448
+ scope.setLocal(
21449
+ parameter3.id,
21450
+ coerceAssignmentValue(
21451
+ argumentValues[index],
21452
+ parameter3.typeInfo,
21453
+ "getter argument"
21454
+ )
21455
+ );
21444
21456
  }
21445
21457
  try {
21446
21458
  const result = evalBody(
@@ -21450,9 +21462,11 @@ function evaluateNSGetterWithEffects(getter, ctx, argumentValues = [], options =
21450
21462
  evaluationOptions(runtimeCtx, false)
21451
21463
  );
21452
21464
  if (result.kind === "return") {
21453
- if (getter.typeInfo.type === 20 /* Decimal */ && typeof result.value === "number") {
21454
- result.value = coerceDecimalOperand(result.value, "return");
21455
- }
21465
+ result.value = coerceAssignmentValue(
21466
+ result.value,
21467
+ getter.typeInfo,
21468
+ "return"
21469
+ );
21456
21470
  if (ownsInvocationState) {
21457
21471
  flushRuntimeSets(runtimeCtx);
21458
21472
  result.value = exportRuntimeSetValue(result.value, runtimeCtx);
@@ -22324,7 +22338,11 @@ function executeCompiledFunction(action, ctx, args, allowFallthroughReturn, runt
22324
22338
  const parameter3 = action.parameters[index + 2];
22325
22339
  const runtimeTypeInfo = runtimeArgumentTypes[index];
22326
22340
  const value2 = asDeclaredRuntimeSet(
22327
- restoreExportedReferences(filledArgs[index], runtimeTypeInfo, ctx),
22341
+ coerceAssignmentValue(
22342
+ restoreExportedReferences(filledArgs[index], runtimeTypeInfo, ctx),
22343
+ runtimeTypeInfo,
22344
+ "function argument"
22345
+ ),
22328
22346
  runtimeTypeInfo,
22329
22347
  ctx
22330
22348
  );
@@ -22358,9 +22376,7 @@ function executeCompiledFunction(action, ctx, args, allowFallthroughReturn, runt
22358
22376
  }
22359
22377
  return null;
22360
22378
  }
22361
- if (runtimeReturnType.type === 20 /* Decimal */ && typeof value === "number") {
22362
- value = coerceDecimalOperand(value, "function return");
22363
- }
22379
+ value = coerceAssignmentValue(value, runtimeReturnType, "function return");
22364
22380
  if (!runtimeValueMatchesType(value, runtimeReturnType, ctx)) {
22365
22381
  throw new NSGetterRuntimeError(
22366
22382
  "NSFunction returned a value that does not match its compiled return type."
@@ -22702,9 +22718,11 @@ function evalInstructions(instructions, scope, ctx, options) {
22702
22718
  }
22703
22719
  case "variable" /* variable */: {
22704
22720
  let value = evalPointer(ins.variable.pointer, scope, ctx);
22705
- if (ins.variable.typeInfo.type === 20 /* Decimal */ && typeof value === "number") {
22706
- value = coerceDecimalOperand(value, "variable initialization");
22707
- }
22721
+ value = coerceAssignmentValue(
22722
+ value,
22723
+ ins.variable.typeInfo,
22724
+ "variable initialization"
22725
+ );
22708
22726
  value = asDeclaredRuntimeSet(value, ins.variable.typeInfo, ctx);
22709
22727
  scope.setLocal(ins.variable.id, value);
22710
22728
  break;
@@ -22740,9 +22758,11 @@ function evalInstructions(instructions, scope, ctx, options) {
22740
22758
  }
22741
22759
  case "assign" /* assign */: {
22742
22760
  let value = evalPointer(ins.pointer, scope, ctx);
22743
- if (ins.target.typeInfo.type === 20 /* Decimal */ && typeof value === "number") {
22744
- value = coerceDecimalOperand(value, "assignment");
22745
- }
22761
+ value = coerceAssignmentValue(
22762
+ value,
22763
+ ins.target.typeInfo,
22764
+ "assignment"
22765
+ );
22746
22766
  if (ins.target.pointer.type === "variable" /* variable */) {
22747
22767
  assertVariableBindingWritable(scope, ins.target.pointer.variableId);
22748
22768
  scope.setLocal(ins.target.pointer.variableId, value);
@@ -24524,6 +24544,21 @@ function evalCollectionMutationInstruction(ins, scope, ctx, options) {
24524
24544
  );
24525
24545
  }
24526
24546
  const args = ins.args.map((arg) => evalPointer(arg, scope, ctx));
24547
+ const decimalEntries = "entryTypeInfo" in ins.target.typeInfo && ins.target.typeInfo.entryTypeInfo.type === 20 /* Decimal */;
24548
+ let entryArgumentIndex = null;
24549
+ const dictionary = ins.target.typeInfo.type === 5 /* Dictionary */;
24550
+ if (ins.mutation === "Add" /* Add */)
24551
+ entryArgumentIndex = dictionary ? 1 : 0;
24552
+ if (ins.mutation === "Insert" /* Insert */) entryArgumentIndex = 1;
24553
+ if (ins.mutation === "Remove" /* Remove */ && !dictionary)
24554
+ entryArgumentIndex = 0;
24555
+ if (entryArgumentIndex !== null && "entryTypeInfo" in ins.target.typeInfo) {
24556
+ args[entryArgumentIndex] = coerceAssignmentValue(
24557
+ args[entryArgumentIndex],
24558
+ ins.target.typeInfo.entryTypeInfo,
24559
+ "collection mutation entry"
24560
+ );
24561
+ }
24527
24562
  const setEntryMember = mutatesLocalVariable ? null : storedSetEntryMember(targetValue, ctx);
24528
24563
  const isLookupSet = setEntryMember === null && ins.target.typeInfo.type === 9 /* Lookup */;
24529
24564
  if (isLookupSet && Array.isArray(targetValue)) {
@@ -24531,7 +24566,8 @@ function evalCollectionMutationInstruction(ins, scope, ctx, options) {
24531
24566
  targetValue,
24532
24567
  ins.reference === true,
24533
24568
  ins.target.typeInfo.type === 9 /* Lookup */ && (ins.target.typeInfo.entryTypeInfo.type === 8 /* Enum */ || ins.target.typeInfo.entryTypeInfo.type === 18 /* DialogueLookup */),
24534
- ctx
24569
+ ctx,
24570
+ decimalEntries
24535
24571
  );
24536
24572
  }
24537
24573
  const setMatch = setEntryMember !== null && Array.isArray(targetValue) && (ins.mutation === "Add" /* Add */ || ins.mutation === "Remove" /* Remove */) ? findStoredSetEntry(targetValue, args[0], setEntryMember, ctx) : null;
@@ -24550,7 +24586,8 @@ function evalCollectionMutationInstruction(ins, scope, ctx, options) {
24550
24586
  ctx,
24551
24587
  isLookupSet,
24552
24588
  false,
24553
- ins.reference === true
24589
+ ins.reference === true,
24590
+ decimalEntries
24554
24591
  );
24555
24592
  updateCollectionReferenceProvenance(
24556
24593
  targetValue,
@@ -24628,7 +24665,8 @@ function evalCollectionMutationInstruction(ins, scope, ctx, options) {
24628
24665
  ctx,
24629
24666
  isLookupSet,
24630
24667
  ins.reference !== true && ins.mutation === "Remove" /* Remove */ && isArrayCollection(targetValue) && setEntryMember === null && !isLookupSet && findKnownRowIdByValueReference(args[0], ctx) === null,
24631
- ins.reference === true
24668
+ ins.reference === true,
24669
+ setEntryMember === null && decimalEntries
24632
24670
  );
24633
24671
  updateCollectionReferenceProvenance(
24634
24672
  targetValue,
@@ -24904,21 +24942,28 @@ function asDeclaredRuntimeSet(value, type, ctx) {
24904
24942
  value,
24905
24943
  entry === 7 /* Class */ || entry === 22 /* Interface */ || entry === 6 /* List */ || entry === 5 /* Dictionary */ || entry === 9 /* Lookup */,
24906
24944
  entry === 8 /* Enum */ || entry === 18 /* DialogueLookup */,
24907
- ctx
24945
+ ctx,
24946
+ entry === 20 /* Decimal */
24908
24947
  );
24909
24948
  }
24910
24949
  function runtimeSetKey(value, set, ctx) {
24911
24950
  if (set.slotReferences !== null) return slotReferenceKey(value);
24912
24951
  if (set.enumEntries && Array.isArray(value) && value.length === 1)
24913
24952
  value = value[0];
24914
- return toSetEntryKey(value, null, set.reference, ctx);
24953
+ return toSetEntryKey(value, null, set.reference, ctx, set.decimalEntries);
24915
24954
  }
24916
- function runtimeSet(array2, reference, enumEntries, ctx) {
24955
+ function runtimeSet(array2, reference, enumEntries, ctx, decimalEntries = false) {
24917
24956
  const sets = ctx.__runtimeSets ??= /* @__PURE__ */ new WeakMap();
24918
24957
  ctx.__dirtyRuntimeSets ??= /* @__PURE__ */ new Set();
24919
24958
  const existing = sets.get(array2);
24920
24959
  if (existing !== void 0) return existing;
24921
- const set = new RuntimeSet(array2, reference, enumEntries);
24960
+ const set = new RuntimeSet(
24961
+ array2,
24962
+ reference,
24963
+ enumEntries,
24964
+ null,
24965
+ decimalEntries
24966
+ );
24922
24967
  const references = collectionReferences(array2, ctx);
24923
24968
  for (let index = 0; index < array2.length; index++) {
24924
24969
  const raw = array2[index];
@@ -25037,7 +25082,7 @@ function removableEntryRowIds(targetValue, mutation, args, ctx) {
25037
25082
  }
25038
25083
  return rowIds;
25039
25084
  }
25040
- function applyLocalCollectionMutation(targetValue, mutation, args, ctx, unique = false, resolveRemovalEntries = false, reference = false) {
25085
+ function applyLocalCollectionMutation(targetValue, mutation, args, ctx, unique = false, resolveRemovalEntries = false, reference = false, decimal = false) {
25041
25086
  if (targetValue instanceof RuntimeSet) {
25042
25087
  (ctx.__dirtyRuntimeSets ??= /* @__PURE__ */ new Set()).add(targetValue);
25043
25088
  switch (mutation) {
@@ -25089,6 +25134,7 @@ function applyLocalCollectionMutation(targetValue, mutation, args, ctx, unique =
25089
25134
  case "Remove" /* Remove */:
25090
25135
  if (Array.isArray(targetValue)) {
25091
25136
  const references = resolveRemovalEntries ? collectionReferences(targetValue, ctx) : recordedReferenceEntries(targetValue, ctx) ?? 1 /* Values */;
25137
+ const targetKey = decimal ? decimalCollectionKey(args[0]) : args[0];
25092
25138
  let index = -1;
25093
25139
  for (let candidate = 0; candidate < targetValue.length; candidate += 1) {
25094
25140
  const entry = targetValue[candidate];
@@ -25099,10 +25145,8 @@ function applyLocalCollectionMutation(targetValue, mutation, args, ctx, unique =
25099
25145
  }
25100
25146
  continue;
25101
25147
  }
25102
- if (jsEqual(
25103
- typeof entry === "string" && isReferenceEntry(references, candidate) ? resolveValueIfId(entry, ctx) : entry,
25104
- args[0]
25105
- )) {
25148
+ const value = typeof entry === "string" && isReferenceEntry(references, candidate) ? resolveValueIfId(entry, ctx) : entry;
25149
+ if (jsEqual(decimal ? decimalCollectionKey(value) : value, targetKey)) {
25106
25150
  index = candidate;
25107
25151
  break;
25108
25152
  }
@@ -25589,12 +25633,22 @@ function evalPointerCore(pointer, scope, ctx, resultUsage = "value") {
25589
25633
  case "function" /* function */:
25590
25634
  return evalFunction(pointer.function, scope, ctx);
25591
25635
  case "listLiteral" /* listLiteral */:
25592
- return pointer.entries.map((e) => evalPointer(e, scope, ctx));
25636
+ return pointer.entries.map(
25637
+ (entry) => coerceAssignmentValue(
25638
+ evalPointer(entry, scope, ctx),
25639
+ pointer.typeInfo.entryTypeInfo,
25640
+ "list entry"
25641
+ )
25642
+ );
25593
25643
  case "dictLiteral" /* dictLiteral */: {
25594
25644
  const out = {};
25595
25645
  for (const e of pointer.entries) {
25596
25646
  const k = evalPointer(e.key, scope, ctx);
25597
- out[String(k)] = evalPointer(e.value, scope, ctx);
25647
+ out[String(k)] = coerceAssignmentValue(
25648
+ evalPointer(e.value, scope, ctx),
25649
+ pointer.typeInfo.entryTypeInfo,
25650
+ "dictionary entry"
25651
+ );
25598
25652
  }
25599
25653
  return out;
25600
25654
  }
@@ -27257,9 +27311,9 @@ function readRecordKey(receiver, k, memberId, ctx) {
27257
27311
  if (receiver === ctx.thisValue && typeof ctx.thisValueRowId === "string") {
27258
27312
  return ctx.thisValueRowId;
27259
27313
  }
27260
- const rowId = findKnownRowIdByValueReference(receiver, ctx);
27261
- if (rowId) return rowId;
27262
- throw new NSGetterRuntimeError("Class value has no backing row id.");
27314
+ const row = trackedRowForValueReference(receiver, ctx);
27315
+ if (row !== null && classIdForValueRow(row, ctx) !== void 0)
27316
+ return row.id;
27263
27317
  }
27264
27318
  const assetMember = resolveAssetRuntimeMember(receiver, k, ctx);
27265
27319
  if (assetMember.kind === "ok") return assetMember.value;
@@ -28690,7 +28744,11 @@ function evalDecimalMathOp(info, fn, scope, ctx) {
28690
28744
  }
28691
28745
  }
28692
28746
  function evalListRepeat(info, scope, ctx) {
28693
- const value = evalPointer(info.valuePointer, scope, ctx);
28747
+ const value = coerceAssignmentValue(
28748
+ evalPointer(info.valuePointer, scope, ctx),
28749
+ info.entryTypeInfo,
28750
+ "List.Repeat entry"
28751
+ );
28694
28752
  const count = numberArg(
28695
28753
  evalPointer(info.countPointer, scope, ctx),
28696
28754
  "List.Repeat count"
@@ -28778,13 +28836,18 @@ function jsEqual(a, b) {
28778
28836
  return a instanceof NSSlotReference && b instanceof NSSlotReference && a.targetId === b.targetId;
28779
28837
  }
28780
28838
  if (a === b) return true;
28781
- if (Array.isArray(a) && Array.isArray(b)) {
28782
- if (a.length !== b.length) return false;
28783
- for (let i = 0; i < a.length; i++) {
28784
- if (!jsEqual(a[i], b[i])) return false;
28839
+ if (Array.isArray(a) || a instanceof RuntimeSet) {
28840
+ if (!Array.isArray(b) && !(b instanceof RuntimeSet)) return false;
28841
+ const aSize = a instanceof RuntimeSet ? a.size : a.length;
28842
+ const bSize = b instanceof RuntimeSet ? b.size : b.length;
28843
+ if (aSize !== bSize) return false;
28844
+ const other = b.values();
28845
+ for (const entry of a.values()) {
28846
+ if (!jsEqual(entry, other.next().value)) return false;
28785
28847
  }
28786
28848
  return true;
28787
28849
  }
28850
+ if (b instanceof RuntimeSet) return false;
28788
28851
  if (typeof a === "object" && typeof b === "object" && a !== null && b !== null && !Array.isArray(a) && !Array.isArray(b)) {
28789
28852
  const aKeys = Object.keys(a);
28790
28853
  const bKeys = Object.keys(b);
@@ -29220,7 +29283,8 @@ function evalCollectionQuery(info, scope, ctx) {
29220
29283
  output,
29221
29284
  info.reference === true,
29222
29285
  info.enumEntries === true,
29223
- ctx
29286
+ ctx,
29287
+ info.decimalEntries === true
29224
29288
  ) : output;
29225
29289
  };
29226
29290
  if (info.op === "OrderBy" || info.op === "OrderByDescending") {
@@ -29272,7 +29336,8 @@ function evalCollectionQuery(info, scope, ctx) {
29272
29336
  entry,
29273
29337
  valueId,
29274
29338
  info.reference === true,
29275
- ctx
29339
+ ctx,
29340
+ info.decimalEntries === true
29276
29341
  );
29277
29342
  if (seenSetKeys.has(setKey)) return 0 /* Continue */;
29278
29343
  seenSetKeys.add(setKey);
@@ -29327,10 +29392,15 @@ function evalCollectionQuery(info, scope, ctx) {
29327
29392
  }
29328
29393
  return finish();
29329
29394
  }
29330
- function toSetEntryKey(entry, valueId, reference, ctx) {
29395
+ function toSetEntryKey(entry, valueId, reference, ctx, decimal = false) {
29331
29396
  if (reference) return valueId ?? referenceIdOf(entry, ctx) ?? entry;
29397
+ if (decimal) return decimalCollectionKey(entry);
29332
29398
  return typeof entry === "object" && entry !== null ? canonicalJsonStringify(entry) : entry;
29333
29399
  }
29400
+ function decimalCollectionKey(value) {
29401
+ if (value === null) return null;
29402
+ return decimalKey(coerceDecimalOperand(value, "decimal collection entry"));
29403
+ }
29334
29404
  function compareCollectionKeys(left, right, keyType) {
29335
29405
  if (left === null) return right === null ? 0 : -1;
29336
29406
  if (right === null) return 1;
@@ -29448,7 +29518,11 @@ function evalFunction(fn, scope, ctx) {
29448
29518
  }
29449
29519
  case "contains" /* contains */: {
29450
29520
  let c = evalPointer(fn.info.collectionPointer, scope, ctx);
29451
- const target = evalPointer(fn.info.valuePointer, scope, ctx);
29521
+ let target = evalPointer(fn.info.valuePointer, scope, ctx);
29522
+ if (fn.info.float === true && typeof target === "number")
29523
+ target = Math.fround(target);
29524
+ if (fn.info.decimal === true && target !== null)
29525
+ target = coerceDecimalOperand(target, "collection search");
29452
29526
  if (typeof c === "string") {
29453
29527
  if (typeof target !== "string") {
29454
29528
  throw new NSGetterRuntimeError(
@@ -29481,9 +29555,13 @@ function evalFunction(fn, scope, ctx) {
29481
29555
  return referenceIndexOf(c, target, ctx) !== null;
29482
29556
  }
29483
29557
  const targetReferenceId = typeof target === "string" ? null : findKnownRowIdByValueReference(target, ctx);
29558
+ const targetKey = fn.info.decimal ? decimalCollectionKey(target) : target;
29484
29559
  let containsResolvedEntry = false;
29485
29560
  iterateCollection(c, ctx, (entry, _key, valueId) => {
29486
- if (valueId !== null && valueId === targetReferenceId || jsEqual(entry, target)) {
29561
+ if (valueId !== null && valueId === targetReferenceId || jsEqual(
29562
+ fn.info.decimal ? decimalCollectionKey(entry) : entry,
29563
+ targetKey
29564
+ )) {
29487
29565
  containsResolvedEntry = true;
29488
29566
  return 1 /* Break */;
29489
29567
  }
@@ -29496,15 +29574,21 @@ function evalFunction(fn, scope, ctx) {
29496
29574
  evalPointer(fn.info.collectionPointer, scope, ctx),
29497
29575
  "IndexOf receiver"
29498
29576
  );
29499
- const target = evalPointer(fn.info.valuePointer, scope, ctx);
29577
+ let target = evalPointer(fn.info.valuePointer, scope, ctx);
29578
+ if (fn.info.float === true && typeof target === "number")
29579
+ target = Math.fround(target);
29500
29580
  if (fn.info.reference === true) {
29501
29581
  const index = referenceIndexOf(collection, target, ctx);
29502
29582
  return index === null ? -1 : numberArg(index, "IndexOf index");
29503
29583
  }
29504
29584
  const targetReferenceId = typeof target === "string" ? null : findKnownRowIdByValueReference(target, ctx);
29585
+ const targetKey = fn.info.decimal ? decimalCollectionKey(target) : target;
29505
29586
  let found = -1;
29506
29587
  iterateCollection(collection, ctx, (entry, key, valueId) => {
29507
- if (valueId !== null && valueId === targetReferenceId || jsEqual(entry, target)) {
29588
+ if (valueId !== null && valueId === targetReferenceId || jsEqual(
29589
+ fn.info.decimal ? decimalCollectionKey(entry) : entry,
29590
+ targetKey
29591
+ )) {
29508
29592
  found = numberArg(key, "IndexOf index");
29509
29593
  return 1 /* Break */;
29510
29594
  }
@@ -29712,7 +29796,19 @@ function evalFunction(fn, scope, ctx) {
29712
29796
  );
29713
29797
  iterateCollection(c, ctx, callback);
29714
29798
  }
29715
- if (found !== sentinel) return found;
29799
+ if (found !== sentinel) {
29800
+ if (typeof found === "string") {
29801
+ if (c instanceof RuntimeSet) {
29802
+ if (c.enumEntries) return [found];
29803
+ } else {
29804
+ const row = trackedRowForValueReference(c, ctx);
29805
+ const member = row === null ? null : memberForValueRow(row, ctx);
29806
+ if (member?.kind === 8 /* Enum */ || member?.kind === 18 /* DialogueLookup */)
29807
+ return [found];
29808
+ }
29809
+ }
29810
+ return found;
29811
+ }
29716
29812
  if (fn.type === "first" /* first */) {
29717
29813
  throw new NSGetterRuntimeError(
29718
29814
  innerFn ? "First() found no matching entry. Use FirstOrDefault() and handle null if no match is expected." : "First() called on an empty collection. Check that it has entries, or use FirstOrDefault() and handle null."
@@ -30926,10 +31022,19 @@ function constructionInitEvaluator(ctx, createdValues, argumentScopes, construct
30926
31022
  }
30927
31023
  return null;
30928
31024
  };
31025
+ const defaultScopeMemberId = (memberId) => walkExtendsMemberChain(
31026
+ memberId,
31027
+ ctx.vm.members,
31028
+ (declaration) => declaration.defaultValue === void 0 ? void 0 : declaration.id,
31029
+ {
31030
+ memberById: (id) => indexes4.schemaIndex.member(id) ?? null,
31031
+ maxHops: ctx.vm.members.length
31032
+ }
31033
+ );
30929
31034
  return (member, init, sourceValueId, genericEnv = instanceEnv) => {
30930
31035
  const memberId = Reflect.get(member, "id");
30931
31036
  const effectiveMemberId = typeof memberId === "string" ? memberId : null;
30932
- const scopeMemberId = indexes4.initializerScopeMemberIds.get(init) ?? (typeof sourceValueId !== "string" ? void 0 : indexes4.initializerScopeMemberIdsByValueId.get(sourceValueId)) ?? effectiveMemberId;
31037
+ const scopeMemberId = indexes4.initializerScopeMemberIds.get(init) ?? (typeof sourceValueId !== "string" ? void 0 : indexes4.initializerScopeMemberIdsByValueId.get(sourceValueId)) ?? (sourceValueId === void 0 && effectiveMemberId !== null ? defaultScopeMemberId(effectiveMemberId) : void 0) ?? effectiveMemberId;
30933
31038
  const compiled = ensureInitializerCompiled({
30934
31039
  init,
30935
31040
  member,
@@ -50069,7 +50174,7 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
50069
50174
  scope
50070
50175
  )
50071
50176
  );
50072
- const schemaClassInfo = toWireType(targetType, this.project);
50177
+ const schemaClassInfo = this.constructorWireType(targetType, expression);
50073
50178
  if (schemaClassInfo.type !== 7 /* Class */) {
50074
50179
  throw new Error(
50075
50180
  `NeoScript constructor ${className} has non-Class return type ${this.describe(signature.returnType)}.`
@@ -50087,6 +50192,16 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
50087
50192
  writability: "session" /* Session */
50088
50193
  };
50089
50194
  }
50195
+ /** Constructor storage arguments stay distinct from their expression read types. */
50196
+ constructorWireType(target, expression) {
50197
+ const typeArguments = expression.typeArguments?.map(
50198
+ (argument2) => resolveStoredReferenceTarget(argument2, this.context, this.project)
50199
+ );
50200
+ return toWireType(
50201
+ typeArguments === void 0 ? target : { ...target, typeArguments },
50202
+ this.project
50203
+ );
50204
+ }
50090
50205
  constructorTargetType(schemaClass2, expression, expected) {
50091
50206
  const typeArguments = expression.typeArguments?.map(
50092
50207
  (argument2) => this.resolveType(argument2)
@@ -50217,7 +50332,7 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
50217
50332
  scope
50218
50333
  );
50219
50334
  const classType = targetType;
50220
- const schemaClassInfo = toWireType(classType, this.project);
50335
+ const schemaClassInfo = this.constructorWireType(classType, expression);
50221
50336
  if (schemaClassInfo.type !== 7 /* Class */) {
50222
50337
  throw new Error(
50223
50338
  `NeoScript declared constructor ${className} projected a non-Class wire type.`
@@ -51080,6 +51195,8 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
51080
51195
  {
51081
51196
  collectionPointer: receiver.pointer,
51082
51197
  valuePointer: value.pointer,
51198
+ ...isPrimitive(expectedValueType, "float") ? { float: true } : {},
51199
+ ...isPrimitive(expectedValueType, "decimal") ? { decimal: true } : {},
51083
51200
  ...hasReferenceEquality(collection.valueType, this.project) ? { reference: true } : {}
51084
51201
  },
51085
51202
  { kind: "primitive", name: "int" }
@@ -51109,6 +51226,8 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
51109
51226
  {
51110
51227
  collectionPointer: receiver.pointer,
51111
51228
  valuePointer: value.pointer,
51229
+ ...isPrimitive(expectedValueType, "float") ? { float: true } : {},
51230
+ ...isPrimitive(expectedValueType, "decimal") ? { decimal: true } : {},
51112
51231
  ...hasReferenceEquality(collection.valueType, this.project) ? { reference: true } : {}
51113
51232
  },
51114
51233
  { kind: "primitive", name: "bool" }
@@ -51341,6 +51460,7 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
51341
51460
  info = {
51342
51461
  ...base,
51343
51462
  op: name,
51463
+ ...isPrimitive(collection.valueType, "decimal") ? { decimalEntries: true } : {},
51344
51464
  ...hasReferenceEquality(collection.valueType, this.project) ? { reference: true } : {}
51345
51465
  };
51346
51466
  resultType = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neocompose/cli",
3
- "version": "0.84.3",
3
+ "version": "0.84.4",
4
4
  "description": "Neo Compose native project-source CLI with bidirectional sync.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -9,7 +9,7 @@ description: >-
9
9
  `@neocompose/cli` or `node cli/bin/neo.mjs` in the neo-compose repository.
10
10
  ---
11
11
 
12
- <!-- reviewed-through-cli: 0.84.3 -->
12
+ <!-- reviewed-through-cli: 0.84.4 -->
13
13
 
14
14
  # Neo Compose CLI
15
15
 
@@ -129,7 +129,7 @@ wrappers.
129
129
  The marker near the top of `SKILL.md` must exactly match the package version:
130
130
 
131
131
  ```html
132
- <!-- reviewed-through-cli: 0.84.3 -->
132
+ <!-- reviewed-through-cli: 0.84.4 -->
133
133
  ```
134
134
 
135
135
  The quoted version above is checked too, so this instruction cannot go stale
@@ -335,6 +335,9 @@ Generated animation clip targets and reference lookup factories retain construct
335
335
  generic arguments, including supported forwarded bindings. Clip getters use the
336
336
  declared target type when the owner is a derived class. Export reports an error
337
337
  when a clip target does not match the owner or one of its constructed base types.
338
+ Inherited collection interface getters preserve supported detached temporaries.
339
+ Constructed entries such as `Frame<T>` use the interface's generic argument
340
+ family for both their collection type and value converters.
338
341
  New instances that reuse existing member-localized text retain its shared ID
339
342
  and translations in the export.
340
343
 
@@ -425,6 +425,10 @@ value-level target metadata.
425
425
 
426
426
  ## Computed initializers and declared constructors
427
427
 
428
+ Inherited member initializers read parameters from the constructor that declared
429
+ them, including when export materialization clones their bodies. A storage-only
430
+ override keeps that original constructor scope.
431
+
428
432
  Use an expression initializer when a member default must be evaluated during
429
433
  construction rather than stored as a closed literal. It is replayed for omitted
430
434
  members in push, web, and SDK contexts. A stored override remains pinned until
@@ -224,7 +224,8 @@ unconstrained generic `T` compares by value.
224
224
  Calling `Equals(other)` on a non-null generic value dynamically uses the
225
225
  runtime Class's one-argument `Equals` Function or NSFunction when it returns
226
226
  `bool`, including the effective override. Values without that member fall back
227
- to value equality.
227
+ to value equality. For local sets, that fallback compares their current entries
228
+ in insertion order, regardless of prior reads or mutations.
228
229
 
229
230
  Object initializers are available in method bodies and `.spec.neo` tests:
230
231
 
@@ -387,6 +388,11 @@ may be null. The receiver stays unchanged.
387
388
  return "banana".Replace("a", "o"); // "bonono"
388
389
  ```
389
390
 
391
+ Integer inputs assigned to `float` round to float32. This also applies to
392
+ function arguments and returns, collection entries, and `List.Repeat` with a
393
+ float entry type. `Contains` and `IndexOf` on float collections also convert
394
+ integer search arguments. Nullable assignments preserve `null`.
395
+
390
396
  ## Collection builtins
391
397
 
392
398
  Lists expose their size as either `items.Count` or `items.Count()`. Use
@@ -399,8 +405,9 @@ Use `Any()` or `Any((item) => predicate)` to check for a match, and
399
405
  `All((item) => predicate)` to check every entry. Both short-circuit; an empty
400
406
  collection has `Any() == false` and `All(...) == true`.
401
407
 
402
- `Last`, `LastOrDefault`, `Single`, and `SingleOrDefault` accept zero arguments
403
- or one predicate. `Last` throws when no entry matches. `Single` throws unless
408
+ `First`, `FirstOrDefault`, `Last`, `LastOrDefault`, `Single`, and `SingleOrDefault` accept zero arguments
409
+ or one predicate. `First` and `Last` throw when no entry matches. Enum sets
410
+ return enum values, including when a predicate selects the entry. `Single` throws unless
404
411
  exactly one matches; `SingleOrDefault` still throws for multiple matches.
405
412
  The `OrDefault` forms return nullable entries and use `null` for no match.
406
413
 
@@ -711,3 +718,7 @@ and may appear in any order. Use the existing nested positional override for a
711
718
  virtual owner; changing its listeners alone must not add a value override.
712
719
  Omitting a field inherits its listeners. An empty array suppresses inherited
713
720
  listeners and disappears when there is no inherited wiring to suppress.
721
+
722
+ Decimal collection membership compares numeric values: `1.0` and `1.00` are
723
+ equal for `ToSet`, `Contains`, `IndexOf`, `Add`, and `Remove`. Decimal scale
724
+ is retained for display; string entries such as `"1.0"` and `"1.00"` stay distinct.
@@ -190,6 +190,13 @@ EligibleIngredients = root.Assets.Crafting.FurnaceRecipes
190
190
  .ToSet()
191
191
  ```
192
192
 
193
+ `Box<Reference<Item>>` keeps its reference storage type through push and pull,
194
+ including explicit `new Box<Reference<Item>>()` calls. Its `Item` targets stay
195
+ shared; reading a class reference still produces the class value. A derived
196
+ construction in a base-typed slot retains reference bindings forwarded through
197
+ the inheritance chain, such as `Base<Reference<Item>>` holding
198
+ `new Derived<Reference<Item>>()`.
199
+
193
200
  Writing `Reference(recipe.Ingredient)` inside the callback is also valid.
194
201
  `Reference(value)` preserves an evaluated class value and its identity; it does
195
202
  not clone it or turn it into a separate wrapper object. An optional generic
@@ -276,6 +283,10 @@ that instance. Leaving the emitted construction unchanged creates no redundant
276
283
  value rows. A nullable member with no default remains bare; an explicit null
277
284
  default emits `= null`.
278
285
 
286
+ Changing a construction to another overload or shared argument rebuilds the
287
+ instance even when the argument values compare equal. Owned inline arguments
288
+ still compare by contents; unchanged shared arguments keep their identities.
289
+
279
290
  Spelling an unchanged child default, such as `FPS = 8`, stays a no-op after
280
291
  push even when sibling fields own stored rows. Adding a different child default
281
292
  preserves those siblings' row identities; it does not transfer their ownership.
@@ -399,3 +410,8 @@ is an error; remove the declaration to request deletion.
399
410
  New schema getters referenced by NeoFlow interpolation need no `@id`. Export and
400
411
  `neo dev` assign the same identity to the declaration and its references, including
401
412
  inherited getters. Repeated exports and the next push reuse the export identity ledger.
413
+
414
+ Dictionary string keys are data. An entry named `"Id"` reads its value through
415
+ `dictionary["Id"]`; class `.Id` continues to return the class instance identity.
416
+
417
+ You can remove a constructor and replace every call to it in the same edit. Push prepares the new calls together with the schema change and removes argument rows owned by the old constructions.