@neocompose/cli 0.65.1 → 0.67.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.
@@ -1755,7 +1755,7 @@ var TEST_GLOBALS = [
1755
1755
  ];
1756
1756
 
1757
1757
  // ../packages/neoscript-language/src/strict-ir.ts
1758
- var NEOSCRIPT_COMPILER_REVISION = 16;
1758
+ var NEOSCRIPT_COMPILER_REVISION = 17;
1759
1759
 
1760
1760
  // ../src/models/members/member-storage.ts
1761
1761
  function isMemberStorageKind(value) {
@@ -2124,7 +2124,8 @@ var SystemProtectionKind = {
2124
2124
  DialogueMemoryContainer: "dialogueMemoryContainer",
2125
2125
  DialoguePriorityDefault: "dialoguePriorityDefault",
2126
2126
  PolicyImmutable: "policyImmutable",
2127
- PolicyDeleteBlocked: "policyDeleteBlocked"
2127
+ PolicyDeleteBlocked: "policyDeleteBlocked",
2128
+ ScriptRuntime: "scriptRuntime"
2128
2129
  };
2129
2130
  function isWithId(value) {
2130
2131
  return typeof value?.id === "string";
@@ -2141,7 +2142,7 @@ function isEpochMillis(value) {
2141
2142
  return Number.isFinite(value);
2142
2143
  }
2143
2144
  function isSystemProtectionKind(value) {
2144
- return value === SystemProtectionKind.WorldAuthoring || value === SystemProtectionKind.DialogueMemorySchema || value === SystemProtectionKind.DialogueMemoryContainer || value === SystemProtectionKind.DialoguePriorityDefault || value === SystemProtectionKind.PolicyImmutable || value === SystemProtectionKind.PolicyDeleteBlocked;
2145
+ return value === SystemProtectionKind.WorldAuthoring || value === SystemProtectionKind.DialogueMemorySchema || value === SystemProtectionKind.DialogueMemoryContainer || value === SystemProtectionKind.DialoguePriorityDefault || value === SystemProtectionKind.PolicyImmutable || value === SystemProtectionKind.PolicyDeleteBlocked || value === SystemProtectionKind.ScriptRuntime;
2145
2146
  }
2146
2147
  function isSystemMetadata(value) {
2147
2148
  const v = value;
@@ -2166,6 +2167,11 @@ function isValidDocsText(value) {
2166
2167
  return textEncoder.encode(value).byteLength <= DOCS_TEXT_MAX_UTF8_BYTES;
2167
2168
  }
2168
2169
 
2170
+ // ../src/models/members/float32.ts
2171
+ function isFloat32(value) {
2172
+ return typeof value === "number" && Number.isFinite(value) && Math.fround(value) === value;
2173
+ }
2174
+
2169
2175
  // ../packages/neoscript-language/src/project-source-identifiers.ts
2170
2176
  var PROJECT_IDENTIFIER_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
2171
2177
  var PROJECT_RESERVED_NAMES = /* @__PURE__ */ new Set([
@@ -2506,7 +2512,7 @@ function hasValidParameterDefault(value) {
2506
2512
  case 2 /* Int */:
2507
2513
  return typeof payload === "number" && Number.isInteger(payload);
2508
2514
  case 4 /* Float */:
2509
- return typeof payload === "number" && Number.isFinite(payload);
2515
+ return isFloat32(payload);
2510
2516
  case 20 /* Decimal */:
2511
2517
  case 3 /* String */:
2512
2518
  case 8 /* Enum */:
@@ -2792,6 +2798,9 @@ function isNSArithmeticOperationInfo(value) {
2792
2798
  const v = value;
2793
2799
  if (typeof v !== "object" || v === null) return false;
2794
2800
  if (!isNSArithmeticOperationType(v.type)) return false;
2801
+ if (v.numeric !== void 0 && v.numeric !== "int" && v.numeric !== "float") {
2802
+ return false;
2803
+ }
2795
2804
  return isNSPointers(v.pointers);
2796
2805
  }
2797
2806
  function isNSArithmeticOperation(value) {
@@ -3702,6 +3711,9 @@ var STRUCTURED_LEAF_FIELDS = {
3702
3711
  function isStructuredLeafKind(kind) {
3703
3712
  return Object.prototype.hasOwnProperty.call(STRUCTURED_LEAF_FIELDS, kind);
3704
3713
  }
3714
+ function structuredLeafFields(kind) {
3715
+ return STRUCTURED_LEAF_FIELDS[kind];
3716
+ }
3705
3717
  function structuredLeafFieldKeys(kind) {
3706
3718
  return STRUCTURED_LEAF_FIELDS[kind].map((entry) => entry.key);
3707
3719
  }
@@ -3714,10 +3726,10 @@ function isStructuredLeafFieldValue(type, value) {
3714
3726
  return typeof value === "string" && value.length > 0;
3715
3727
  }
3716
3728
  if (typeof value !== "number" || !Number.isFinite(value)) return false;
3717
- if (type === "float") return true;
3729
+ if (type === "float") return isFloat32(value);
3718
3730
  if (type === "int") return Number.isInteger(value);
3719
3731
  if (type === "sliceIndex") return Number.isInteger(value) && value >= 0;
3720
- return value >= 0 && value <= 1;
3732
+ return isFloat32(value) && value >= 0 && value <= 1;
3721
3733
  }
3722
3734
  var STRUCTURED_LEAF_PARTIAL_KEY = "~partial";
3723
3735
  function isStructuredLeafPartialValue(value) {
@@ -3737,16 +3749,36 @@ function structuredLeafPartialViolation(kind, value, legalKeysLabel) {
3737
3749
  return `has no field "${key}"; ${legalKeysLabel} accepts ${structuredLeafFieldKeys(kind).join(", ")}`;
3738
3750
  }
3739
3751
  if (isStructuredLeafFieldValue(field.type, fieldValue)) continue;
3740
- return `field "${key}" ${structuredLeafFieldViolation(field.type)}`;
3752
+ return `field "${key}" ${structuredLeafFieldViolation(field.type, fieldValue)}`;
3741
3753
  }
3742
3754
  return null;
3743
3755
  }
3744
- function structuredLeafFieldViolation(type) {
3756
+ function structuredLeafRecordFieldViolation(kind, value) {
3757
+ if (typeof value !== "object" || value === null) return null;
3758
+ if (Array.isArray(value)) return null;
3759
+ const entries = Object.entries(value);
3760
+ if (entries.length !== structuredLeafFields(kind).length) return null;
3761
+ let found = null;
3762
+ for (const [key, fieldValue] of entries) {
3763
+ const field = structuredLeafField(kind, key);
3764
+ if (field === null) return null;
3765
+ if (typeof fieldValue !== "number") return null;
3766
+ if (found !== null) continue;
3767
+ if (isStructuredLeafFieldValue(field.type, fieldValue)) continue;
3768
+ found = {
3769
+ key,
3770
+ value: fieldValue,
3771
+ violation: structuredLeafFieldViolation(field.type, fieldValue)
3772
+ };
3773
+ }
3774
+ return found;
3775
+ }
3776
+ function structuredLeafFieldViolation(type, value) {
3745
3777
  if (type === "fileId") return "must be a non-empty file id";
3746
- if (type === "float") return "must be a finite number";
3747
3778
  if (type === "int") return "must be a whole number";
3748
3779
  if (type === "sliceIndex") return "must be a whole number 0 or greater";
3749
- return "must be a number between 0 and 1";
3780
+ if (!isFloat32(value)) return "must be a 32-bit float";
3781
+ return "must be between 0 and 1";
3750
3782
  }
3751
3783
 
3752
3784
  // ../src/models/members/member-kinds.ts
@@ -3893,9 +3925,9 @@ function isMemberString(value) {
3893
3925
  function isMemberFloatBase(value) {
3894
3926
  const v = asMemberBaseForKind(value, 4 /* Float */);
3895
3927
  if (v === null) return false;
3896
- if (v.minValue !== void 0 && v.minValue !== null && typeof v.minValue !== "number")
3928
+ if (v.minValue !== void 0 && v.minValue !== null && !isFloat32(v.minValue))
3897
3929
  return false;
3898
- if (v.maxValue !== void 0 && v.maxValue !== null && typeof v.maxValue !== "number")
3930
+ if (v.maxValue !== void 0 && v.maxValue !== null && !isFloat32(v.maxValue))
3899
3931
  return false;
3900
3932
  return v.decimalPoints === void 0 || v.decimalPoints === null || typeof v.decimalPoints === "number";
3901
3933
  }
@@ -3904,12 +3936,8 @@ function hasExactKeys(value, keys) {
3904
3936
  if (actual.length !== keys.length) return false;
3905
3937
  return keys.every((key) => Object.prototype.hasOwnProperty.call(value, key));
3906
3938
  }
3907
- function isFiniteNumberValue(value) {
3908
- return typeof value === "number" && Number.isFinite(value);
3909
- }
3910
3939
  function isVectorComponentValue(value, integer2) {
3911
- if (!isFiniteNumberValue(value)) return false;
3912
- if (!integer2) return true;
3940
+ if (!integer2) return isFloat32(value);
3913
3941
  return Number.isInteger(value);
3914
3942
  }
3915
3943
  function isVector2ValueWithComponents(value, integer2) {
@@ -3943,7 +3971,7 @@ function isVector3IntValue(value) {
3943
3971
  return isVector3ValueWithComponents(value, true);
3944
3972
  }
3945
3973
  function isColorComponentValue(value) {
3946
- if (!isFiniteNumberValue(value)) return false;
3974
+ if (!isFloat32(value)) return false;
3947
3975
  return value >= 0 && value <= 1;
3948
3976
  }
3949
3977
  function isColorValue(value) {
@@ -10037,6 +10065,9 @@ function structuredLeafPartialMemberViolation(member, value, position) {
10037
10065
  }
10038
10066
  return structuredLeafPartialViolation(kind, value, MemberKind[kind]);
10039
10067
  }
10068
+ function storedLeafFieldIssueMessage(issue) {
10069
+ return `${MemberKind[issue.leaf]} field "${issue.key}" ${issue.violation}, not ${issue.value}.`;
10070
+ }
10040
10071
  function* storedValueChildEntries(args) {
10041
10072
  const entryMemberId = args.member.entryMemberId;
10042
10073
  if (args.member.kind === 6 /* List */ && typeof entryMemberId === "string") {
@@ -10070,16 +10101,19 @@ function* storedValueChildEntries(args) {
10070
10101
  }
10071
10102
  function storedLeafShapeIssue(member, value) {
10072
10103
  switch (member.kind) {
10104
+ // Null is an optional Float's absent value.
10105
+ case 4 /* Float */:
10106
+ return value === null || isFloat32(value) ? null : { kind: "float" };
10073
10107
  case 14 /* Vector2 */:
10074
- return isVector2Value(value) ? null : { kind: "vector2" };
10108
+ return isVector2Value(value) ? null : leafIssue(member.kind, "vector2", value);
10075
10109
  case 15 /* Vector2Int */:
10076
- return isVector2IntValue(value) ? null : { kind: "vector2Int" };
10110
+ return isVector2IntValue(value) ? null : leafIssue(member.kind, "vector2Int", value);
10077
10111
  case 16 /* Vector3 */:
10078
- return isVector3Value(value) ? null : { kind: "vector3" };
10112
+ return isVector3Value(value) ? null : leafIssue(member.kind, "vector3", value);
10079
10113
  case 17 /* Vector3Int */:
10080
- return isVector3IntValue(value) ? null : { kind: "vector3Int" };
10114
+ return isVector3IntValue(value) ? null : leafIssue(member.kind, "vector3Int", value);
10081
10115
  case 19 /* Color */:
10082
- return isColorValue(value) ? null : { kind: "color" };
10116
+ return isColorValue(value) ? null : leafIssue(member.kind, "color", value);
10083
10117
  case 20 /* Decimal */: {
10084
10118
  const violation = getDecimalStringViolation(value);
10085
10119
  return violation === null ? null : { kind: "decimal", violation };
@@ -10092,6 +10126,10 @@ function storedLeafShapeIssue(member, value) {
10092
10126
  return null;
10093
10127
  }
10094
10128
  }
10129
+ function leafIssue(leaf, shape, value) {
10130
+ const field = structuredLeafRecordFieldViolation(leaf, value);
10131
+ return field === null ? { kind: shape } : { kind: "field", leaf, ...field };
10132
+ }
10095
10133
 
10096
10134
  // ../src/models/members/build-default-member-value.ts
10097
10135
  var DECLARATION_DEFAULT_POSITION = {
@@ -11173,16 +11211,24 @@ function validateProjectDocumentFileMemberValue(document2, member, body, positio
11173
11211
  return;
11174
11212
  }
11175
11213
  const shapeIssue = storedLeafShapeIssue(member, body.value);
11214
+ if (shapeIssue?.kind === "float") {
11215
+ throw new Error(
11216
+ `Float value for member "${member.name}" must be a 32-bit float, not ${String(body.value)}.`
11217
+ );
11218
+ }
11219
+ if (shapeIssue?.kind === "field") {
11220
+ throw new Error(
11221
+ `Member "${member.name}" ${storedLeafFieldIssueMessage(shapeIssue)}`
11222
+ );
11223
+ }
11176
11224
  if (shapeIssue?.kind === "vector2") {
11177
- throw new Error("Vector2 value must be { x: number, y: number }.");
11225
+ throw new Error("Vector2 value must be { x: float, y: float }.");
11178
11226
  }
11179
11227
  if (shapeIssue?.kind === "vector2Int") {
11180
11228
  throw new Error("Vector2Int value must be { x: integer, y: integer }.");
11181
11229
  }
11182
11230
  if (shapeIssue?.kind === "vector3") {
11183
- throw new Error(
11184
- "Vector3 value must be { x: number, y: number, z: number }."
11185
- );
11231
+ throw new Error("Vector3 value must be { x: float, y: float, z: float }.");
11186
11232
  }
11187
11233
  if (shapeIssue?.kind === "vector3Int") {
11188
11234
  throw new Error(
@@ -11191,7 +11237,7 @@ function validateProjectDocumentFileMemberValue(document2, member, body, positio
11191
11237
  }
11192
11238
  if (shapeIssue?.kind === "color") {
11193
11239
  throw new Error(
11194
- "Color value must be { r, g, b, a } with each component in [0, 1]."
11240
+ "Color value must be { r, g, b, a } with each component a 32-bit float in [0, 1]."
11195
11241
  );
11196
11242
  }
11197
11243
  if (shapeIssue?.kind === "decimal") {
@@ -17453,7 +17499,8 @@ function evalPointer(pointer, scope, ctx, resultUsage = "value") {
17453
17499
  callSiteId: pointer.callSiteId,
17454
17500
  invokeDelegate: (delegate, delegateArgs = []) => invokeDelegateValue(delegate, delegateArgs, ctx, pointer.callSiteId)
17455
17501
  });
17456
- if (intercepted?.handled === true) return intercepted.value;
17502
+ if (intercepted?.handled === true)
17503
+ return hostCallResult(member, intercepted.value, ctx);
17457
17504
  if (member === null) {
17458
17505
  if (pointer.missingMemberFallback === "valueEquality") {
17459
17506
  return jsEqual(innerThis, suppliedArgs[0]);
@@ -17489,7 +17536,8 @@ function evalPointer(pointer, scope, ctx, resultUsage = "value") {
17489
17536
  pointer.callSiteId
17490
17537
  )
17491
17538
  });
17492
- if (hosted?.handled === true) return hosted.value;
17539
+ if (hosted?.handled === true)
17540
+ return hostCallResult(member, hosted.value, ctx);
17493
17541
  const receiverId = trackedRowForValueReference(innerThis, ctx)?.id;
17494
17542
  throw new NativeFunctionDelegateUnavailableError(
17495
17543
  `No native delegate handled Function '${member.name}' (member id '${member.id}'${nativeReceiverDescription(member, innerThis, receiverId)}). Web preview and other non-native evaluator hosts must configure a native handler or call interceptor before evaluation.`,
@@ -17780,7 +17828,8 @@ function invokeDelegateValue(value, args, ctx, callSiteId, ownerReceiver, result
17780
17828
  callSiteId,
17781
17829
  invokeDelegate: (delegate, delegateArgs = []) => invokeDelegateValue(delegate, delegateArgs, ctx, callSiteId)
17782
17830
  });
17783
- if (intercepted?.handled === true) return intercepted.value;
17831
+ if (intercepted?.handled === true)
17832
+ return hostCallResult(member, intercepted.value, ctx);
17784
17833
  state.delegateStack.push(frame);
17785
17834
  try {
17786
17835
  if (member.kind === 13 /* Function */) {
@@ -17795,7 +17844,8 @@ function invokeDelegateValue(value, args, ctx, callSiteId, ownerReceiver, result
17795
17844
  callSiteId,
17796
17845
  invokeDelegate: (delegate, delegateArgs = []) => invokeDelegateValue(delegate, delegateArgs, ctx, callSiteId)
17797
17846
  });
17798
- if (hosted?.handled === true) return hosted.value;
17847
+ if (hosted?.handled === true)
17848
+ return hostCallResult(member, hosted.value, ctx);
17799
17849
  const receiverId = trackedRowForValueReference(receiver, ctx)?.id;
17800
17850
  throw new NativeFunctionDelegateUnavailableError(
17801
17851
  `No native delegate handled Function '${member.name}' (member id '${member.id}'${nativeReceiverDescription(member, receiver, receiverId)}). Web preview and other non-native evaluator hosts must configure a native handler or call interceptor before evaluation.`,
@@ -18664,13 +18714,14 @@ function runtimeCollectionEntryMatchesType(value, typeInfo, ctx, seenCollections
18664
18714
  return resolved !== value && runtimeValueMatchesType(resolved, typeInfo, ctx, seenCollections);
18665
18715
  }
18666
18716
  function evalVectorConstructor(vectorType, componentPointers, scope, ctx) {
18667
- const values = componentPointers.map(
18668
- (pointer) => finiteNumberArg(
18717
+ const integer2 = vectorType === 15 /* Vector2Int */ || vectorType === 17 /* Vector3Int */;
18718
+ const values = componentPointers.map((pointer) => {
18719
+ const value = finiteNumberArg(
18669
18720
  evalPointer(pointer, scope, ctx),
18670
18721
  `${MemberKind[vectorType]} component`
18671
- )
18672
- );
18673
- const integer2 = vectorType === 15 /* Vector2Int */ || vectorType === 17 /* Vector3Int */;
18722
+ );
18723
+ return integer2 ? value : Math.fround(value);
18724
+ });
18674
18725
  if (integer2 && values.some((value) => !Number.isInteger(value))) {
18675
18726
  throw new NSGetterRuntimeError(
18676
18727
  `${MemberKind[vectorType]} components must be integers`
@@ -19561,11 +19612,17 @@ function evalOperation(operation, scope, ctx) {
19561
19612
  if (operation.type === "arithmetic" /* arithmetic */) {
19562
19613
  const op = operation.arithmetic;
19563
19614
  const operands = op.pointers.map((p) => evalPointer(p, scope, ctx));
19564
- return applyArithmetic(op.type, operands, op.decimal === true, ctx);
19615
+ return applyArithmetic(
19616
+ op.type,
19617
+ operands,
19618
+ op.decimal === true,
19619
+ ctx,
19620
+ op.numeric
19621
+ );
19565
19622
  }
19566
19623
  return evalBooleanExpression(operation.expression, scope, ctx);
19567
19624
  }
19568
- function applyArithmetic(op, operands, decimal, ctx) {
19625
+ function applyArithmetic(op, operands, decimal, ctx, numeric) {
19569
19626
  if (operands.length === 0) {
19570
19627
  throw new NSGetterRuntimeError("Arithmetic operation with no operands");
19571
19628
  }
@@ -19598,31 +19655,43 @@ function applyArithmetic(op, operands, decimal, ctx) {
19598
19655
  `Arithmetic operand is not numeric: ${typeof o}`
19599
19656
  );
19600
19657
  });
19658
+ const round = numeric === "float" ? Math.fround : (value) => value;
19601
19659
  switch (op) {
19602
19660
  case "+" /* addition */:
19603
- return numbers.reduce((a, b) => a + b, 0);
19661
+ return numbers.reduce((a, b) => round(a + b), 0);
19604
19662
  case "-" /* subtraction */:
19605
- return numbers.reduce((a, b, i) => i === 0 ? b : a - b);
19663
+ return numbers.reduce((a, b, i) => i === 0 ? b : round(a - b));
19606
19664
  case "*" /* multiplication */:
19607
- return numbers.reduce((a, b) => a * b, 1);
19665
+ return numbers.reduce((a, b) => round(a * b), 1);
19608
19666
  case "/" /* division */:
19609
19667
  return numbers.reduce((a, b, i) => {
19610
19668
  if (i === 0) return b;
19611
19669
  if (b === 0) throw new NSGetterRuntimeError("Division by zero");
19612
- return a / b;
19670
+ return numeric === "int" ? Math.trunc(a / b) : round(a / b);
19613
19671
  });
19614
19672
  case "%" /* remainder */:
19615
19673
  return numbers.reduce((a, b, i) => {
19616
19674
  if (i === 0) return b;
19617
19675
  if (b === 0) throw new NSGetterRuntimeError("Modulo by zero");
19618
- return a % b;
19676
+ return round(a % b);
19619
19677
  });
19620
19678
  }
19621
19679
  }
19680
+ function hostCallResult(member, value, ctx) {
19681
+ if (typeof value !== "number" || member === null) return value;
19682
+ if (member.kind !== 13 /* Function */ && member.kind !== 23 /* NSFunction */) {
19683
+ return value;
19684
+ }
19685
+ const signature = resolveCallableSignature(member.id, member.kind, ctx);
19686
+ return signature?.returnTypeInfo.type === 4 /* Float */ ? Math.fround(value) : value;
19687
+ }
19622
19688
  function coerceAssignmentValue(value, targetTypeInfo, context) {
19623
19689
  if (targetTypeInfo.type === 8 /* Enum */ && typeof value === "string") {
19624
19690
  return [value];
19625
19691
  }
19692
+ if (targetTypeInfo.type === 4 /* Float */ && typeof value === "number") {
19693
+ return Math.fround(value);
19694
+ }
19626
19695
  return targetTypeInfo.type === 20 /* Decimal */ && typeof value === "number" ? coerceDecimalOperand(value, context) : value;
19627
19696
  }
19628
19697
  function coerceDecimalOperand(value, context) {
@@ -19785,7 +19854,7 @@ function evalMathOp(info, scope, ctx) {
19785
19854
  if (value < 0) return -1;
19786
19855
  return 0;
19787
19856
  case "sqrt":
19788
- return Math.sqrt(value);
19857
+ return Math.fround(Math.sqrt(value));
19789
19858
  }
19790
19859
  }
19791
19860
  function evalDecimalMathOp(info, fn, scope, ctx) {
@@ -20426,8 +20495,10 @@ function evalFunction(fn, scope, ctx) {
20426
20495
  );
20427
20496
  }
20428
20497
  case "toFloat":
20429
- return floatFromDecimalString(
20430
- coerceDecimalOperand(receiverRaw, "ToFloat")
20498
+ return Math.fround(
20499
+ floatFromDecimalString(
20500
+ coerceDecimalOperand(receiverRaw, "ToFloat")
20501
+ )
20431
20502
  );
20432
20503
  case "toDecimal": {
20433
20504
  if (typeof receiverRaw !== "number") {
@@ -20936,6 +21007,9 @@ function evaluateConstructorFields(descriptor, scope, ctx) {
20936
21007
  `constructor field '${validated.schemaKey}'`
20937
21008
  );
20938
21009
  }
21010
+ if (typeof value === "number" && validated.typeInfo.type === 4 /* Float */) {
21011
+ value = Math.fround(value);
21012
+ }
20939
21013
  if (value === null || value === void 0) {
20940
21014
  if (validated.requiredWithoutDefault) {
20941
21015
  throw new NSGetterRuntimeError(
@@ -21556,8 +21630,9 @@ function evaluateDeclaredConstructorArguments(info, record2, scope, ctx) {
21556
21630
  );
21557
21631
  }
21558
21632
  const value = byName.get(parameter3.name);
21559
- if (parameter3.type !== 20 /* Decimal */) return value;
21560
21633
  if (typeof value !== "number") return value;
21634
+ if (parameter3.type === 4 /* Float */) return Math.fround(value);
21635
+ if (parameter3.type !== 20 /* Decimal */) return value;
21561
21636
  return coerceDecimalOperand(
21562
21637
  value,
21563
21638
  `constructor argument '${parameter3.name}'`
@@ -32598,7 +32673,10 @@ var StrictNeoScriptResolver = class {
32598
32673
  canonicalDecimal(expression.raw, expression.pos)
32599
32674
  );
32600
32675
  }
32601
- return literal({ kind: "primitive", name: "float" }, expression.value);
32676
+ return literal(
32677
+ { kind: "primitive", name: "float" },
32678
+ Math.fround(expression.value)
32679
+ );
32602
32680
  }
32603
32681
  case "litString":
32604
32682
  return literal({ kind: "primitive", name: "string" }, expression.value);
@@ -37232,7 +37310,9 @@ var StrictNeoScriptResolver = class {
37232
37310
  arithmetic: {
37233
37311
  type: operator,
37234
37312
  pointers: [left.pointer, right.pointer],
37235
- ...isPrimitive(result, "decimal") ? { decimal: true } : {}
37313
+ ...isPrimitive(result, "decimal") ? { decimal: true } : {},
37314
+ ...isPrimitive(result, "int") ? { numeric: "int" } : {},
37315
+ ...isPrimitive(result, "float") ? { numeric: "float" } : {}
37236
37316
  }
37237
37317
  }
37238
37318
  },
@@ -44835,6 +44915,14 @@ var SYSTEM_PROTECTION_RULES = {
44835
44915
  disallow: [SystemDisallowedOperation.DeleteRecord],
44836
44916
  reasonKey: "systemProtection.reasons.policyDeleteBlocked",
44837
44917
  serverReason: "System default policy may be edited but must keep one default row."
44918
+ },
44919
+ [SystemProtectionKind.ScriptRuntime]: {
44920
+ disallow: [
44921
+ SystemDisallowedOperation.EditRecord,
44922
+ SystemDisallowedOperation.DeleteRecord
44923
+ ],
44924
+ reasonKey: "systemProtection.reasons.scriptRuntime",
44925
+ serverReason: "Lifecycle interface the Neo Compose script runtime calls."
44838
44926
  }
44839
44927
  };
44840
44928
 
@@ -46080,7 +46168,8 @@ function isSchemaSystemMetadata(value) {
46080
46168
  "dialogueMemoryContainer",
46081
46169
  "dialoguePriorityDefault",
46082
46170
  "policyImmutable",
46083
- "policyDeleteBlocked"
46171
+ "policyDeleteBlocked",
46172
+ "scriptRuntime"
46084
46173
  ]);
46085
46174
  const worlds = /* @__PURE__ */ new Set([
46086
46175
  "cellPattern",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neocompose/cli",
3
- "version": "0.65.1",
3
+ "version": "0.67.0",
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.65.1 -->
12
+ <!-- reviewed-through-cli: 0.67.0 -->
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.65.1 -->
113
+ <!-- reviewed-through-cli: 0.67.0 -->
114
114
  ```
115
115
 
116
116
  The quoted version above is checked too, so this instruction cannot go stale
@@ -197,6 +197,16 @@ itself; both are durable writes deduplicated by target identity, so
197
197
  re-running a subscription cannot double-subscribe. Invoke it as a statement —
198
198
  an action is never a value, never a return type, and never assigned with `=`.
199
199
 
200
+ Implement a system lifecycle interface to run code when the runtime does.
201
+ `INeoOnLoad` and `INeoOnUnload` run when an instance becomes live and when it
202
+ stops being live. The `INeoUnity*` interfaces, such as `INeoUnityUpdate` and
203
+ `INeoUnityOnTriggerEnter2D`, run for a rendered `NeoObjectBase` in play mode.
204
+ Their member names, such as `OnLoad` and `Update`, are reserved: declare one
205
+ only as the public, immediate instance method that implements its interface,
206
+ and never mark it `@effect`. Subscribe Session actions in `OnLoad` and remove
207
+ them in `OnUnload`. React to data changes with `@effect`, and keep `Update`
208
+ for work that really happens every frame.
209
+
200
210
  Keep generic project relations as typed top-level declarations in
201
211
  `Relations.neo`. Let the compiler derive identities for owned descriptors and
202
212
  concrete generic bindings.
@@ -202,6 +202,14 @@ Save, Session, Setter, or otherwise writable. A constructor writes only its
202
202
  `this` instance. A writable structured-leaf field still inherits the receiver's
203
203
  effective storage restrictions.
204
204
 
205
+ ## Numeric types
206
+
207
+ `int` is a 32-bit integer and `float` is a 32-bit float, matching C#. Float
208
+ literals, stored values, and every arithmetic step round to 32 bits, so
209
+ `0.1f + 0.2f` behaves like Unity. `int` division truncates toward zero.
210
+ `decimal` is exact base-10 and is the only type to use for currency or other
211
+ values that must not drift.
212
+
205
213
  ## Numeric builtins
206
214
 
207
215
  Use the `Math` namespace for numeric utilities with C# names and C# semantics: