@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.
package/dist/neo.mjs CHANGED
@@ -5959,6 +5959,10 @@ var init_neoflow_graph_parser = __esm({
5959
5959
  });
5960
5960
 
5961
5961
  // ../packages/neoscript-language/src/project-source-ast.ts
5962
+ function neoSourceTypeText(type) {
5963
+ const args = type.typeArguments.length > 0 ? `<${type.typeArguments.map(neoSourceTypeText).join(", ")}>` : "";
5964
+ return `${type.name}${args}${type.nullable ? "?" : ""}`;
5965
+ }
5962
5966
  function isNeoSourceAccessModifier(value) {
5963
5967
  return value === "public" || value === "protected" || value === "private";
5964
5968
  }
@@ -7892,6 +7896,9 @@ function tripleInterpolationEnd(text, start, limit) {
7892
7896
  }
7893
7897
  return null;
7894
7898
  }
7899
+ function rangesEqual(left, right) {
7900
+ return positionCompare(left.start, right.start) === 0 && positionCompare(left.end, right.end) === 0;
7901
+ }
7895
7902
  function positionCompare(left, right) {
7896
7903
  return left.line === right.line ? left.character - right.character : left.line - right.line;
7897
7904
  }
@@ -8367,7 +8374,8 @@ var init_project_schema_contract_generated = __esm({
8367
8374
  "DialogueMemoryContainer",
8368
8375
  "DialoguePriorityDefault",
8369
8376
  "PolicyImmutable",
8370
- "PolicyDeleteBlocked"
8377
+ "PolicyDeleteBlocked",
8378
+ "ScriptRuntime"
8371
8379
  ],
8372
8380
  "NeoMemberRequirement": [
8373
8381
  "Optional",
@@ -16363,7 +16371,7 @@ var NEOSCRIPT_COMPILER_REVISION;
16363
16371
  var init_strict_ir = __esm({
16364
16372
  "../packages/neoscript-language/src/strict-ir.ts"() {
16365
16373
  "use strict";
16366
- NEOSCRIPT_COMPILER_REVISION = 16;
16374
+ NEOSCRIPT_COMPILER_REVISION = 17;
16367
16375
  }
16368
16376
  });
16369
16377
 
@@ -20108,7 +20116,10 @@ var init_strict_resolver = __esm({
20108
20116
  canonicalDecimal(expression.raw, expression.pos)
20109
20117
  );
20110
20118
  }
20111
- return literal({ kind: "primitive", name: "float" }, expression.value);
20119
+ return literal(
20120
+ { kind: "primitive", name: "float" },
20121
+ Math.fround(expression.value)
20122
+ );
20112
20123
  }
20113
20124
  case "litString":
20114
20125
  return literal({ kind: "primitive", name: "string" }, expression.value);
@@ -24742,7 +24753,9 @@ var init_strict_resolver = __esm({
24742
24753
  arithmetic: {
24743
24754
  type: operator,
24744
24755
  pointers: [left.pointer, right.pointer],
24745
- ...isPrimitive(result, "decimal") ? { decimal: true } : {}
24756
+ ...isPrimitive(result, "decimal") ? { decimal: true } : {},
24757
+ ...isPrimitive(result, "int") ? { numeric: "int" } : {},
24758
+ ...isPrimitive(result, "float") ? { numeric: "float" } : {}
24746
24759
  }
24747
24760
  }
24748
24761
  },
@@ -26424,6 +26437,40 @@ var init_project_source_script_compiler = __esm({
26424
26437
  }
26425
26438
  });
26426
26439
 
26440
+ // ../packages/neoscript-language/src/project-source-lifecycle-hooks.ts
26441
+ function isScriptRuntimeInterface(declaration) {
26442
+ const kind = declaration.annotations.find((annotation2) => annotation2.name === "system")?.arguments.find((argument2) => argument2.name === "kind")?.text.trim();
26443
+ return kind === ".ScriptRuntime";
26444
+ }
26445
+ function lifecycleHooksByMemberName(interfaces) {
26446
+ const hooks = /* @__PURE__ */ new Map();
26447
+ for (const declaration of interfaces) {
26448
+ if (!isScriptRuntimeInterface(declaration)) continue;
26449
+ for (const member of declaration.members) {
26450
+ if (member.kind !== "function") continue;
26451
+ hooks.set(member.name.toLowerCase(), {
26452
+ interfaceName: declaration.name,
26453
+ member,
26454
+ ...declaration.docsText === void 0 ? {} : { docsText: declaration.docsText },
26455
+ unity: declaration.name.startsWith(UNITY_LIFECYCLE_INTERFACE_PREFIX)
26456
+ });
26457
+ }
26458
+ }
26459
+ return hooks;
26460
+ }
26461
+ function reservedLifecycleMemberMessage(memberName, interfaceName, owner) {
26462
+ const fix = owner === "class" ? "Implement the interface with a public method" : "Extend the interface";
26463
+ return `'${memberName}' is reserved for '${interfaceName}'. ${fix}, or rename the member.`;
26464
+ }
26465
+ var NEO_LIFECYCLE_OBJECT_CLASS_NAME, UNITY_LIFECYCLE_INTERFACE_PREFIX;
26466
+ var init_project_source_lifecycle_hooks = __esm({
26467
+ "../packages/neoscript-language/src/project-source-lifecycle-hooks.ts"() {
26468
+ "use strict";
26469
+ NEO_LIFECYCLE_OBJECT_CLASS_NAME = "NeoObjectBase";
26470
+ UNITY_LIFECYCLE_INTERFACE_PREFIX = "INeoUnity";
26471
+ }
26472
+ });
26473
+
26427
26474
  // ../packages/neoscript-language/src/project-source-type-checker.ts
26428
26475
  function validateProjectSourceTypes(documents) {
26429
26476
  const diagnostics = [];
@@ -26433,6 +26480,7 @@ function validateProjectSourceTypes(documents) {
26433
26480
  validateInheritanceCycles(environment, diagnostics);
26434
26481
  validateAllTypeApplications(documents, environment, diagnostics);
26435
26482
  validateMembers(environment, diagnostics);
26483
+ validateLifecycleHooks(environment, diagnostics);
26436
26484
  return diagnostics;
26437
26485
  }
26438
26486
  function createEnvironment2(documents) {
@@ -26489,7 +26537,16 @@ function createEnvironment2(documents) {
26489
26537
  }
26490
26538
  }
26491
26539
  }
26492
- return { declarations, classes, interfaces, memberOwners, typeKinds };
26540
+ return {
26541
+ declarations,
26542
+ classes,
26543
+ interfaces,
26544
+ memberOwners,
26545
+ typeKinds,
26546
+ lifecycleHooks: lifecycleHooksByMemberName(
26547
+ [...interfaces.values()].map((ref) => ref.declaration)
26548
+ )
26549
+ };
26493
26550
  }
26494
26551
  function validatePersistedNames(documents, diagnostics) {
26495
26552
  for (const [uri, document] of documents) {
@@ -27500,7 +27557,7 @@ function validateEffects(ref, environment, diagnostics) {
27500
27557
  kind = inheritedEffectKind(inherited, member.name);
27501
27558
  }
27502
27559
  if (kind !== "Auto") continue;
27503
- const problem = effectIneligibility(member);
27560
+ const problem = effectIneligibility(member) ?? (environment.lifecycleHooks.has(member.name.toLowerCase()) ? "the runtime already calls a lifecycle hook, so its first run would happen twice" : null);
27504
27561
  if (problem === null) continue;
27505
27562
  diagnose(
27506
27563
  diagnostics,
@@ -27550,6 +27607,110 @@ function effectIneligibility(member) {
27550
27607
  if (member.parameters.length > 0) return "an effect takes no parameters";
27551
27608
  return null;
27552
27609
  }
27610
+ function validateLifecycleHooks(environment, diagnostics) {
27611
+ const hooks = environment.lifecycleHooks;
27612
+ if (hooks.size === 0) return;
27613
+ for (const ref of environment.interfaces.values()) {
27614
+ if (isScriptRuntimeInterface(ref.declaration)) continue;
27615
+ for (const member of ref.declaration.members) {
27616
+ const hook = hooks.get(member.name.toLowerCase());
27617
+ if (hook === void 0) continue;
27618
+ diagnose(
27619
+ diagnostics,
27620
+ ref.uri,
27621
+ member.nameRange,
27622
+ "reserved-lifecycle-member",
27623
+ reservedLifecycleMemberMessage(
27624
+ member.name,
27625
+ hook.interfaceName,
27626
+ "interface"
27627
+ )
27628
+ );
27629
+ }
27630
+ }
27631
+ const unityHookByInterface = /* @__PURE__ */ new Map();
27632
+ for (const ref of environment.classes.values()) {
27633
+ if (ref.sourceKind === "flow") continue;
27634
+ let implemented;
27635
+ for (const member of ref.declaration.members) {
27636
+ const hook = hooks.get(member.name.toLowerCase());
27637
+ if (hook === void 0) continue;
27638
+ implemented ??= new Set(
27639
+ implementedInterfaces(ref, environment).map(
27640
+ (view) => view.owner.declaration.name
27641
+ )
27642
+ );
27643
+ if (member.kind === "function" && effectiveSourceAccessModifier(member.modifiers, "class") === "public" && !member.modifiers.includes("static") && !member.modifiers.includes("async") && implemented.has(hook.interfaceName)) {
27644
+ continue;
27645
+ }
27646
+ diagnose(
27647
+ diagnostics,
27648
+ ref.uri,
27649
+ member.nameRange,
27650
+ "reserved-lifecycle-member",
27651
+ reservedLifecycleMemberMessage(
27652
+ member.name,
27653
+ hook.interfaceName,
27654
+ "class"
27655
+ )
27656
+ );
27657
+ }
27658
+ let rendersObjects;
27659
+ for (const base of ref.declaration.baseTypes) {
27660
+ const target = environment.interfaces.get(base.name);
27661
+ if (target === void 0) continue;
27662
+ const unityHook = unityHookReachedBy(
27663
+ target,
27664
+ environment,
27665
+ unityHookByInterface
27666
+ );
27667
+ if (unityHook === null) continue;
27668
+ rendersObjects ??= extendsLifecycleObjectClass(ref, environment);
27669
+ if (rendersObjects) break;
27670
+ diagnostics.push({
27671
+ uri: ref.uri,
27672
+ range: base.range,
27673
+ severity: "warning",
27674
+ source: "neo-project",
27675
+ code: "lifecycle-hook-without-object",
27676
+ message: `'${unityHook}' does nothing on '${ref.declaration.name}'. Only classes that extend ${NEO_LIFECYCLE_OBJECT_CLASS_NAME} get Unity lifecycle calls.`
27677
+ });
27678
+ }
27679
+ }
27680
+ }
27681
+ function unityHookReachedBy(ref, environment, memo) {
27682
+ const name = ref.declaration.name;
27683
+ const known = memo.get(name);
27684
+ if (known !== void 0) return known;
27685
+ memo.set(name, null);
27686
+ let reached = null;
27687
+ if (isScriptRuntimeInterface(ref.declaration) && ref.declaration.members.some(
27688
+ (member) => environment.lifecycleHooks.get(member.name.toLowerCase())?.unity === true
27689
+ )) {
27690
+ reached = name;
27691
+ }
27692
+ for (const base of ref.declaration.baseTypes) {
27693
+ if (reached !== null) break;
27694
+ const target = environment.interfaces.get(base.name);
27695
+ if (target !== void 0) {
27696
+ reached = unityHookReachedBy(target, environment, memo);
27697
+ }
27698
+ }
27699
+ memo.set(name, reached);
27700
+ return reached;
27701
+ }
27702
+ function extendsLifecycleObjectClass(ref, environment) {
27703
+ const visited = /* @__PURE__ */ new Set();
27704
+ let current = ref;
27705
+ while (current !== null && !visited.has(current.declaration.name)) {
27706
+ if (current.declaration.name === NEO_LIFECYCLE_OBJECT_CLASS_NAME) {
27707
+ return true;
27708
+ }
27709
+ visited.add(current.declaration.name);
27710
+ current = classBase(current, identitySubstitution(current), environment)?.owner ?? null;
27711
+ }
27712
+ return false;
27713
+ }
27553
27714
  function isPlatformImplementedSystemMember(member) {
27554
27715
  if (!member.modifiers.includes("protected")) return false;
27555
27716
  return member.annotations.some((entry) => entry.name === "system");
@@ -28019,6 +28180,7 @@ var init_project_source_type_checker = __esm({
28019
28180
  init_strict_compile_error();
28020
28181
  init_project_source_accessors();
28021
28182
  init_project_source_identifiers();
28183
+ init_project_source_lifecycle_hooks();
28022
28184
  BUILTIN_ARITY = /* @__PURE__ */ new Map([
28023
28185
  ["List", 1],
28024
28186
  ["Set", 1],
@@ -28208,12 +28370,12 @@ function classifyConstantExpression(expression) {
28208
28370
  function defaultTypeMismatch(type, constant, enumOptions, parameterName) {
28209
28371
  if (constant.kind === "null") {
28210
28372
  if (type.nullable) return null;
28211
- return `Parameter '${parameterName}' defaults to null, but its type '${formatSourceType(type)}' is not nullable.`;
28373
+ return `Parameter '${parameterName}' defaults to null, but its type '${neoSourceTypeText(type)}' is not nullable.`;
28212
28374
  }
28213
28375
  const declaredEnumOptions = enumOptions.get(type.name);
28214
28376
  if (constant.kind === "enumOption") {
28215
28377
  if (declaredEnumOptions === void 0) {
28216
- return `Parameter '${parameterName}' defaults to enum option '.${constant.optionName}', but its type '${formatSourceType(type)}' is not an enum.`;
28378
+ return `Parameter '${parameterName}' defaults to enum option '.${constant.optionName}', but its type '${neoSourceTypeText(type)}' is not an enum.`;
28217
28379
  }
28218
28380
  if (!declaredEnumOptions.has(constant.optionName)) {
28219
28381
  return `Parameter '${parameterName}' defaults to '.${constant.optionName}', which enum '${type.name}' does not declare.`;
@@ -28222,9 +28384,9 @@ function defaultTypeMismatch(type, constant, enumOptions, parameterName) {
28222
28384
  }
28223
28385
  if (constantKindMatchesPrimitive(type.name, constant.kind)) return null;
28224
28386
  if (PRIMITIVE_PARAMETER_TYPES.has(type.name) || declaredEnumOptions !== void 0) {
28225
- return `Parameter '${parameterName}' default is a ${describeConstant(constant)}, which does not match its declared type '${formatSourceType(type)}'.`;
28387
+ return `Parameter '${parameterName}' default is a ${describeConstant(constant)}, which does not match its declared type '${neoSourceTypeText(type)}'.`;
28226
28388
  }
28227
- return `Parameter '${parameterName}' of type '${formatSourceType(type)}' can only default to null${type.nullable ? "" : ", and only when the type is nullable"}.`;
28389
+ return `Parameter '${parameterName}' of type '${neoSourceTypeText(type)}' can only default to null${type.nullable ? "" : ", and only when the type is nullable"}.`;
28228
28390
  }
28229
28391
  function constantKindMatchesPrimitive(typeName, kind) {
28230
28392
  if (typeName === "bool") return kind === "bool";
@@ -28240,15 +28402,12 @@ function describeConstant(constant) {
28240
28402
  if (constant.kind === "int") return "whole-number literal";
28241
28403
  return "fractional number literal";
28242
28404
  }
28243
- function formatSourceType(type) {
28244
- const argumentsText = type.typeArguments.length === 0 ? "" : `<${type.typeArguments.map(formatSourceType).join(", ")}>`;
28245
- return `${type.name}${argumentsText}${type.nullable ? "?" : ""}`;
28246
- }
28247
28405
  var PARAMETER_DEFAULT_DIAGNOSTIC_CODES, DEFAULT_BEFORE_REQUIRED_PARAMETER, PARAMETER_DEFAULT_TYPE_MISMATCH, NON_CONSTANT_PARAMETER_DEFAULT, PARAMETER_DEFAULT_ON_DEFERRED_FUNCTION, PRIMITIVE_PARAMETER_TYPES;
28248
28406
  var init_project_source_parameter_defaults = __esm({
28249
28407
  "../packages/neoscript-language/src/project-source-parameter-defaults.ts"() {
28250
28408
  "use strict";
28251
28409
  init_language_spec();
28410
+ init_project_source_ast();
28252
28411
  init_executable_parser();
28253
28412
  PARAMETER_DEFAULT_DIAGNOSTIC_CODES = {
28254
28413
  defaultBeforeRequiredParameter: "default-before-required-parameter",
@@ -29955,7 +30114,7 @@ var init_registry = __esm({
29955
30114
  "../packages/manifest/schema-contract/registry.mjs"() {
29956
30115
  "use strict";
29957
30116
  PROJECT_SCHEMA_CONTRACT = Object.freeze({
29958
- cliVersion: "0.65.1",
30117
+ cliVersion: "0.67.0",
29959
30118
  projectFileUploadBatchSize: 32,
29960
30119
  documentRecords: {
29961
30120
  member: {
@@ -30660,7 +30819,8 @@ var init_registry = __esm({
30660
30819
  "DialogueMemoryContainer",
30661
30820
  "DialoguePriorityDefault",
30662
30821
  "PolicyImmutable",
30663
- "PolicyDeleteBlocked"
30822
+ "PolicyDeleteBlocked",
30823
+ "ScriptRuntime"
30664
30824
  ],
30665
30825
  NeoMemberRequirement: ["Optional", "Required"],
30666
30826
  NeoMemberMutability: ["Mutable", "ReadOnly"],
@@ -34170,7 +34330,8 @@ var init_schema_member = __esm({
34170
34330
  "dialogueMemoryContainer",
34171
34331
  "dialoguePriorityDefault",
34172
34332
  "policyImmutable",
34173
- "policyDeleteBlocked"
34333
+ "policyDeleteBlocked",
34334
+ "scriptRuntime"
34174
34335
  ]);
34175
34336
  worldSystemClassKindSchema = picklist([
34176
34337
  "cellPattern",
@@ -35728,7 +35889,8 @@ function isSchemaSystemMetadata(value) {
35728
35889
  "dialogueMemoryContainer",
35729
35890
  "dialoguePriorityDefault",
35730
35891
  "policyImmutable",
35731
- "policyDeleteBlocked"
35892
+ "policyDeleteBlocked",
35893
+ "scriptRuntime"
35732
35894
  ]);
35733
35895
  const worlds = /* @__PURE__ */ new Set([
35734
35896
  "cellPattern",
@@ -39273,6 +39435,47 @@ var init_project_manifest = __esm({
39273
39435
  }
39274
39436
  });
39275
39437
 
39438
+ // ../src/models/members/float32.ts
39439
+ function isFloat32(value) {
39440
+ return typeof value === "number" && Number.isFinite(value) && Math.fround(value) === value;
39441
+ }
39442
+ function unroundedFloatPath(data, path = "") {
39443
+ if (typeof data === "number") {
39444
+ if (Number.isInteger(data)) return null;
39445
+ return isFloat32(data) ? null : path;
39446
+ }
39447
+ if (typeof data !== "object" || data === null) return null;
39448
+ if (Array.isArray(data)) {
39449
+ for (let index = 0; index < data.length; index++) {
39450
+ const found = unroundedFloatPath(data[index], `${path}[${index}]`);
39451
+ if (found !== null) return found;
39452
+ }
39453
+ return null;
39454
+ }
39455
+ for (const [key, value] of Object.entries(data)) {
39456
+ const found = unroundedFloatPath(
39457
+ value,
39458
+ path === "" ? key : `${path}.${key}`
39459
+ );
39460
+ if (found !== null) return found;
39461
+ }
39462
+ return null;
39463
+ }
39464
+ function float32Text(value) {
39465
+ if (Number.isInteger(value)) return String(value);
39466
+ if (Math.fround(value) !== value) return String(value);
39467
+ for (let precision = 1; precision <= 9; precision++) {
39468
+ const text = String(Number(value.toPrecision(precision)));
39469
+ if (Math.fround(Number(text)) === value) return text;
39470
+ }
39471
+ return String(value);
39472
+ }
39473
+ var init_float32 = __esm({
39474
+ "../src/models/members/float32.ts"() {
39475
+ "use strict";
39476
+ }
39477
+ });
39478
+
39276
39479
  // src/project-source/source-format.ts
39277
39480
  function quoteNeoString(value) {
39278
39481
  return JSON.stringify(value);
@@ -39293,7 +39496,7 @@ function formatNeoNumber(value) {
39293
39496
  if (!Number.isFinite(value)) {
39294
39497
  throw new Error(`Cannot emit non-finite Neo number ${String(value)}.`);
39295
39498
  }
39296
- const source = String(value);
39499
+ const source = float32Text(value);
39297
39500
  const match = /^(?<sign>-?)(?<integer>\d+)(?:\.(?<fraction>\d+))?[eE](?<exponent>[+-]?\d+)$/u.exec(
39298
39501
  source
39299
39502
  );
@@ -39317,6 +39520,7 @@ function invariantCaseKey(value) {
39317
39520
  var init_source_format = __esm({
39318
39521
  "src/project-source/source-format.ts"() {
39319
39522
  "use strict";
39523
+ init_float32();
39320
39524
  init_machine_strings();
39321
39525
  }
39322
39526
  });
@@ -40146,7 +40350,7 @@ function isEpochMillis(value) {
40146
40350
  return Number.isFinite(value);
40147
40351
  }
40148
40352
  function isSystemProtectionKind(value) {
40149
- return value === SystemProtectionKind.WorldAuthoring || value === SystemProtectionKind.DialogueMemorySchema || value === SystemProtectionKind.DialogueMemoryContainer || value === SystemProtectionKind.DialoguePriorityDefault || value === SystemProtectionKind.PolicyImmutable || value === SystemProtectionKind.PolicyDeleteBlocked;
40353
+ return value === SystemProtectionKind.WorldAuthoring || value === SystemProtectionKind.DialogueMemorySchema || value === SystemProtectionKind.DialogueMemoryContainer || value === SystemProtectionKind.DialoguePriorityDefault || value === SystemProtectionKind.PolicyImmutable || value === SystemProtectionKind.PolicyDeleteBlocked || value === SystemProtectionKind.ScriptRuntime;
40150
40354
  }
40151
40355
  function isSystemMetadata(value) {
40152
40356
  const v = value;
@@ -40190,7 +40394,8 @@ var init_core_types = __esm({
40190
40394
  DialogueMemoryContainer: "dialogueMemoryContainer",
40191
40395
  DialoguePriorityDefault: "dialoguePriorityDefault",
40192
40396
  PolicyImmutable: "policyImmutable",
40193
- PolicyDeleteBlocked: "policyDeleteBlocked"
40397
+ PolicyDeleteBlocked: "policyDeleteBlocked",
40398
+ ScriptRuntime: "scriptRuntime"
40194
40399
  };
40195
40400
  }
40196
40401
  });
@@ -40543,7 +40748,7 @@ function hasValidParameterDefault(value) {
40543
40748
  case 2 /* Int */:
40544
40749
  return typeof payload === "number" && Number.isInteger(payload);
40545
40750
  case 4 /* Float */:
40546
- return typeof payload === "number" && Number.isFinite(payload);
40751
+ return isFloat32(payload);
40547
40752
  case 20 /* Decimal */:
40548
40753
  case 3 /* String */:
40549
40754
  case 8 /* Enum */:
@@ -40829,6 +41034,9 @@ function isNSArithmeticOperationInfo(value) {
40829
41034
  const v = value;
40830
41035
  if (typeof v !== "object" || v === null) return false;
40831
41036
  if (!isNSArithmeticOperationType(v.type)) return false;
41037
+ if (v.numeric !== void 0 && v.numeric !== "int" && v.numeric !== "float") {
41038
+ return false;
41039
+ }
40832
41040
  return isNSPointers(v.pointers);
40833
41041
  }
40834
41042
  function isNSArithmeticOperation(value) {
@@ -41399,6 +41607,7 @@ var init_neoscript_guards = __esm({
41399
41607
  "use strict";
41400
41608
  init_neoscript_compiler_revision();
41401
41609
  init_member_kind_enum();
41610
+ init_float32();
41402
41611
  init_structural_narrow();
41403
41612
  init_core_types();
41404
41613
  init_neoscript_types();
@@ -41795,10 +42004,14 @@ function isStructuredLeafFieldValue(type, value) {
41795
42004
  return typeof value === "string" && value.length > 0;
41796
42005
  }
41797
42006
  if (typeof value !== "number" || !Number.isFinite(value)) return false;
41798
- if (type === "float") return true;
42007
+ if (type === "float") return isFloat32(value);
41799
42008
  if (type === "int") return Number.isInteger(value);
41800
42009
  if (type === "sliceIndex") return Number.isInteger(value) && value >= 0;
41801
- return value >= 0 && value <= 1;
42010
+ return isFloat32(value) && value >= 0 && value <= 1;
42011
+ }
42012
+ function storedStructuredLeafFieldNumber(type, value) {
42013
+ const float = type === "float" || type === "unitFloat";
42014
+ return float ? Math.fround(value) : value;
41802
42015
  }
41803
42016
  function isStructuredLeafPartialValue(value) {
41804
42017
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
@@ -41817,22 +42030,43 @@ function structuredLeafPartialViolation(kind, value, legalKeysLabel) {
41817
42030
  return `has no field "${key}"; ${legalKeysLabel} accepts ${structuredLeafFieldKeys(kind).join(", ")}`;
41818
42031
  }
41819
42032
  if (isStructuredLeafFieldValue(field.type, fieldValue)) continue;
41820
- return `field "${key}" ${structuredLeafFieldViolation(field.type)}`;
42033
+ return `field "${key}" ${structuredLeafFieldViolation(field.type, fieldValue)}`;
41821
42034
  }
41822
42035
  return null;
41823
42036
  }
41824
- function structuredLeafFieldViolation(type) {
42037
+ function structuredLeafRecordFieldViolation(kind, value) {
42038
+ if (typeof value !== "object" || value === null) return null;
42039
+ if (Array.isArray(value)) return null;
42040
+ const entries = Object.entries(value);
42041
+ if (entries.length !== structuredLeafFields(kind).length) return null;
42042
+ let found = null;
42043
+ for (const [key, fieldValue] of entries) {
42044
+ const field = structuredLeafField(kind, key);
42045
+ if (field === null) return null;
42046
+ if (typeof fieldValue !== "number") return null;
42047
+ if (found !== null) continue;
42048
+ if (isStructuredLeafFieldValue(field.type, fieldValue)) continue;
42049
+ found = {
42050
+ key,
42051
+ value: fieldValue,
42052
+ violation: structuredLeafFieldViolation(field.type, fieldValue)
42053
+ };
42054
+ }
42055
+ return found;
42056
+ }
42057
+ function structuredLeafFieldViolation(type, value) {
41825
42058
  if (type === "fileId") return "must be a non-empty file id";
41826
- if (type === "float") return "must be a finite number";
41827
42059
  if (type === "int") return "must be a whole number";
41828
42060
  if (type === "sliceIndex") return "must be a whole number 0 or greater";
41829
- return "must be a number between 0 and 1";
42061
+ if (!isFloat32(value)) return "must be a 32-bit float";
42062
+ return "must be between 0 and 1";
41830
42063
  }
41831
42064
  var STRUCTURED_LEAF_FIELDS, STRUCTURED_LEAF_PARTIAL_KEY;
41832
42065
  var init_structured_leaf_fields = __esm({
41833
42066
  "../src/models/members/structured-leaf-fields.ts"() {
41834
42067
  "use strict";
41835
42068
  init_member_kind_enum();
42069
+ init_float32();
41836
42070
  STRUCTURED_LEAF_FIELDS = {
41837
42071
  [11 /* Sprite */]: [
41838
42072
  { name: "FileId", key: "fileId", type: "fileId" },
@@ -41985,9 +42219,9 @@ function isMemberString(value) {
41985
42219
  function isMemberFloatBase(value) {
41986
42220
  const v = asMemberBaseForKind(value, 4 /* Float */);
41987
42221
  if (v === null) return false;
41988
- if (v.minValue !== void 0 && v.minValue !== null && typeof v.minValue !== "number")
42222
+ if (v.minValue !== void 0 && v.minValue !== null && !isFloat32(v.minValue))
41989
42223
  return false;
41990
- if (v.maxValue !== void 0 && v.maxValue !== null && typeof v.maxValue !== "number")
42224
+ if (v.maxValue !== void 0 && v.maxValue !== null && !isFloat32(v.maxValue))
41991
42225
  return false;
41992
42226
  return v.decimalPoints === void 0 || v.decimalPoints === null || typeof v.decimalPoints === "number";
41993
42227
  }
@@ -41996,12 +42230,8 @@ function hasExactKeys(value, keys) {
41996
42230
  if (actual.length !== keys.length) return false;
41997
42231
  return keys.every((key) => Object.prototype.hasOwnProperty.call(value, key));
41998
42232
  }
41999
- function isFiniteNumberValue(value) {
42000
- return typeof value === "number" && Number.isFinite(value);
42001
- }
42002
42233
  function isVectorComponentValue(value, integer3) {
42003
- if (!isFiniteNumberValue(value)) return false;
42004
- if (!integer3) return true;
42234
+ if (!integer3) return isFloat32(value);
42005
42235
  return Number.isInteger(value);
42006
42236
  }
42007
42237
  function isVector2ValueWithComponents(value, integer3) {
@@ -42035,7 +42265,7 @@ function isVector3IntValue(value) {
42035
42265
  return isVector3ValueWithComponents(value, true);
42036
42266
  }
42037
42267
  function isColorComponentValue(value) {
42038
- if (!isFiniteNumberValue(value)) return false;
42268
+ if (!isFloat32(value)) return false;
42039
42269
  return value >= 0 && value <= 1;
42040
42270
  }
42041
42271
  function isColorValue(value) {
@@ -43001,6 +43231,7 @@ var init_member_kinds = __esm({
43001
43231
  init_structural_narrow();
43002
43232
  init_core_types();
43003
43233
  init_docs_text2();
43234
+ init_float32();
43004
43235
  init_schema_identifiers();
43005
43236
  init_neoscript_guards();
43006
43237
  init_neoscript_types();
@@ -49515,6 +49746,8 @@ function structuredLeafPartialMemberViolation(member, value, position) {
49515
49746
  return structuredLeafPartialViolation(kind, value, MemberKind[kind]);
49516
49747
  }
49517
49748
  function storedLeafShapeIssueMessage(issue, style) {
49749
+ if (issue.kind === "float") return "must store a 32-bit float.";
49750
+ if (issue.kind === "field") return storedLeafFieldIssueMessage(issue);
49518
49751
  if (issue.kind === "sprite" || issue.kind === "audio") {
49519
49752
  return `must store a valid ${issue.kind === "sprite" ? "Sprite" : "Audio"} value.`;
49520
49753
  }
@@ -49525,12 +49758,15 @@ function storedLeafShapeIssueMessage(issue, style) {
49525
49758
  }
49526
49759
  return issue.kind === "decimal" ? `${issue.violation}.` : `must store ${issue.kind === "color" ? "color " : ""}${shape}.`;
49527
49760
  }
49761
+ function storedLeafFieldIssueMessage(issue) {
49762
+ return `${MemberKind[issue.leaf]} field "${issue.key}" ${issue.violation}, not ${issue.value}.`;
49763
+ }
49528
49764
  function storedLeafExpectation(kind) {
49529
- if (kind === "vector2") return "{ x: number, y: number }";
49765
+ if (kind === "vector2") return "{ x: float, y: float }";
49530
49766
  if (kind === "vector2Int") return "{ x: integer, y: integer }";
49531
- if (kind === "vector3") return "{ x: number, y: number, z: number }";
49767
+ if (kind === "vector3") return "{ x: float, y: float, z: float }";
49532
49768
  if (kind === "vector3Int") return "{ x: integer, y: integer, z: integer }";
49533
- if (kind === "color") return "{ r, g, b, a } in [0, 1]";
49769
+ if (kind === "color") return "{ r, g, b, a } 32-bit floats in [0, 1]";
49534
49770
  return "canonical decimal string";
49535
49771
  }
49536
49772
  function* storedValueChildReferences(args) {
@@ -49576,16 +49812,19 @@ function* storedValueChildEntries(args) {
49576
49812
  }
49577
49813
  function storedLeafShapeIssue(member, value) {
49578
49814
  switch (member.kind) {
49815
+ // Null is an optional Float's absent value.
49816
+ case 4 /* Float */:
49817
+ return value === null || isFloat32(value) ? null : { kind: "float" };
49579
49818
  case 14 /* Vector2 */:
49580
- return isVector2Value(value) ? null : { kind: "vector2" };
49819
+ return isVector2Value(value) ? null : leafIssue(member.kind, "vector2", value);
49581
49820
  case 15 /* Vector2Int */:
49582
- return isVector2IntValue(value) ? null : { kind: "vector2Int" };
49821
+ return isVector2IntValue(value) ? null : leafIssue(member.kind, "vector2Int", value);
49583
49822
  case 16 /* Vector3 */:
49584
- return isVector3Value(value) ? null : { kind: "vector3" };
49823
+ return isVector3Value(value) ? null : leafIssue(member.kind, "vector3", value);
49585
49824
  case 17 /* Vector3Int */:
49586
- return isVector3IntValue(value) ? null : { kind: "vector3Int" };
49825
+ return isVector3IntValue(value) ? null : leafIssue(member.kind, "vector3Int", value);
49587
49826
  case 19 /* Color */:
49588
- return isColorValue(value) ? null : { kind: "color" };
49827
+ return isColorValue(value) ? null : leafIssue(member.kind, "color", value);
49589
49828
  case 20 /* Decimal */: {
49590
49829
  const violation = getDecimalStringViolation(value);
49591
49830
  return violation === null ? null : { kind: "decimal", violation };
@@ -49598,12 +49837,17 @@ function storedLeafShapeIssue(member, value) {
49598
49837
  return null;
49599
49838
  }
49600
49839
  }
49840
+ function leafIssue(leaf, shape, value) {
49841
+ const field = structuredLeafRecordFieldViolation(leaf, value);
49842
+ return field === null ? { kind: shape } : { kind: "field", leaf, ...field };
49843
+ }
49601
49844
  var ANIMATION_OVERRIDE_GRAPH_MEMBER_IDS;
49602
49845
  var init_stored_value_shape = __esm({
49603
49846
  "../src/models/members/stored-value-shape.ts"() {
49604
49847
  "use strict";
49605
49848
  init_world_system_classes();
49606
49849
  init_member_kinds();
49850
+ init_float32();
49607
49851
  init_structured_leaf_fields();
49608
49852
  ANIMATION_OVERRIDE_GRAPH_MEMBER_IDS = /* @__PURE__ */ new Set([
49609
49853
  WORLD_ANIMATION_FRAME_OVERRIDES_MEMBER_ID,
@@ -50688,16 +50932,24 @@ function validateProjectDocumentFileMemberValue(document, member, body, position
50688
50932
  return;
50689
50933
  }
50690
50934
  const shapeIssue = storedLeafShapeIssue(member, body.value);
50935
+ if (shapeIssue?.kind === "float") {
50936
+ throw new Error(
50937
+ `Float value for member "${member.name}" must be a 32-bit float, not ${String(body.value)}.`
50938
+ );
50939
+ }
50940
+ if (shapeIssue?.kind === "field") {
50941
+ throw new Error(
50942
+ `Member "${member.name}" ${storedLeafFieldIssueMessage(shapeIssue)}`
50943
+ );
50944
+ }
50691
50945
  if (shapeIssue?.kind === "vector2") {
50692
- throw new Error("Vector2 value must be { x: number, y: number }.");
50946
+ throw new Error("Vector2 value must be { x: float, y: float }.");
50693
50947
  }
50694
50948
  if (shapeIssue?.kind === "vector2Int") {
50695
50949
  throw new Error("Vector2Int value must be { x: integer, y: integer }.");
50696
50950
  }
50697
50951
  if (shapeIssue?.kind === "vector3") {
50698
- throw new Error(
50699
- "Vector3 value must be { x: number, y: number, z: number }."
50700
- );
50952
+ throw new Error("Vector3 value must be { x: float, y: float, z: float }.");
50701
50953
  }
50702
50954
  if (shapeIssue?.kind === "vector3Int") {
50703
50955
  throw new Error(
@@ -50706,7 +50958,7 @@ function validateProjectDocumentFileMemberValue(document, member, body, position
50706
50958
  }
50707
50959
  if (shapeIssue?.kind === "color") {
50708
50960
  throw new Error(
50709
- "Color value must be { r, g, b, a } with each component in [0, 1]."
50961
+ "Color value must be { r, g, b, a } with each component a 32-bit float in [0, 1]."
50710
50962
  );
50711
50963
  }
50712
50964
  if (shapeIssue?.kind === "decimal") {
@@ -51537,10 +51789,13 @@ function lowerStructuredLeafFieldValue(kind, label, field, expression, lowering)
51537
51789
  if (field.type === "fileId") {
51538
51790
  return lowerStructuredLeafFileId(kind, label, field, expression, lowering);
51539
51791
  }
51540
- const value = lowering.number(expression);
51792
+ const value = storedStructuredLeafFieldNumber(
51793
+ field.type,
51794
+ lowering.number(expression)
51795
+ );
51541
51796
  if (isStructuredLeafFieldValue(field.type, value)) return value;
51542
51797
  throw new Error(
51543
- `${label} field ${JSON.stringify(field.name)} ${structuredLeafFieldRequirement(field.type)}, not ${value}.`
51798
+ `${label} field ${JSON.stringify(field.name)} ${structuredLeafFieldViolation(field.type, value)}, not ${value}.`
51544
51799
  );
51545
51800
  }
51546
51801
  function lowerStructuredLeafFileId(kind, label, field, expression, lowering) {
@@ -51587,10 +51842,13 @@ function lowerStructuredLeafComponents(kind, label, expression, lowering) {
51587
51842
  if (argument2 === void 0) {
51588
51843
  throw new Error(`${label} is missing component ${field.name}.`);
51589
51844
  }
51590
- const component = lowering.number(argument2);
51845
+ const component = storedStructuredLeafFieldNumber(
51846
+ field.type,
51847
+ lowering.number(argument2)
51848
+ );
51591
51849
  if (!isStructuredLeafFieldValue(field.type, component)) {
51592
51850
  throw new Error(
51593
- `${label} component ${field.name} ${structuredLeafFieldRequirement(field.type)}, not ${component}.`
51851
+ `${label} component ${field.name} ${structuredLeafFieldViolation(field.type, component)}, not ${component}.`
51594
51852
  );
51595
51853
  }
51596
51854
  value[field.key] = component;
@@ -51620,7 +51878,7 @@ function structuredLeafPartialAssignments(kind, label, value, emit) {
51620
51878
  function structuredLeafFieldSource(label, field, value, emit) {
51621
51879
  if (!isStructuredLeafFieldValue(field.type, value)) {
51622
51880
  throw new Error(
51623
- `${label} stores field ${JSON.stringify(field.name)} as ${JSON.stringify(value)}, which ${structuredLeafFieldRequirement(field.type)}.`
51881
+ `${label} stores field ${JSON.stringify(field.name)} as ${JSON.stringify(value)}, which ${structuredLeafFieldViolation(field.type, value)}.`
51624
51882
  );
51625
51883
  }
51626
51884
  if (typeof value === "string") return emit.fileId(value);
@@ -51629,13 +51887,6 @@ function structuredLeafFieldSource(label, field, value, emit) {
51629
51887
  `${label} stores field ${JSON.stringify(field.name)} as ${JSON.stringify(value)}, which is neither a number nor a file id.`
51630
51888
  );
51631
51889
  }
51632
- function structuredLeafFieldRequirement(type) {
51633
- if (type === "fileId") return "must be a non-empty file id";
51634
- if (type === "float") return "must be a finite number";
51635
- if (type === "int") return "must be a whole number";
51636
- if (type === "sliceIndex") return "must be a whole number 0 or greater";
51637
- return "must be a number between 0 and 1";
51638
- }
51639
51890
  var STRUCTURED_LEAF_SOURCE_FORMS, STRUCTURED_LEAF_SOURCE_ENTRIES;
51640
51891
  var init_structured_leaf_source = __esm({
51641
51892
  "src/project-source/structured-leaf-source.ts"() {
@@ -52216,7 +52467,9 @@ function emitInterface(context, value) {
52216
52467
  const deferred = member.deferred ? "async " : "";
52217
52468
  return `${emitDocsText(member.docsText)}${id(member.id)}${access}${deferred}${renderReturnType(context, member.returnType)} ${member.name}(${member.arguments.map((argument2) => renderParameter(context, argument2)).join(", ")});`;
52218
52469
  });
52219
- return `${emitDocsText(value.docsText)}${id(value.id)}interface ${value.name}${bases.length ? ` : ${bases.join(", ")}` : ""} {
52470
+ const system = value.system ? `${systemAnnotation2(value.system)}
52471
+ ` : "";
52472
+ return `${emitDocsText(value.docsText)}${id(value.id)}${system}interface ${value.name}${bases.length ? ` : ${bases.join(", ")}` : ""} {
52220
52473
  ${members.map((member) => indentNeoSourceNonEmptyLines(member, 2)).join("\n\n")}
52221
52474
  }
52222
52475
  `;
@@ -54348,7 +54601,7 @@ function lowerParameterDefaultValue(context, expression, type, path) {
54348
54601
  `Parameter ${path} defaults to a number literal, but its type is ${type.kind}.`
54349
54602
  );
54350
54603
  }
54351
- return expression.value;
54604
+ return type.kind === "float" ? Math.fround(expression.value) : expression.value;
54352
54605
  case "litFloat":
54353
54606
  if (type.kind === "decimal") return expression.raw;
54354
54607
  if (type.kind !== "float") {
@@ -54356,7 +54609,7 @@ function lowerParameterDefaultValue(context, expression, type, path) {
54356
54609
  `Parameter ${path} defaults to a fractional literal, but its type is ${type.kind}.`
54357
54610
  );
54358
54611
  }
54359
- return expression.value;
54612
+ return Math.fround(expression.value);
54360
54613
  case "unary": {
54361
54614
  if (expression.op !== "-") {
54362
54615
  throw new Error(
@@ -55495,8 +55748,8 @@ function lowerFieldMember(context, ownerClass, declaration, id2, commonInput, ba
55495
55748
  };
55496
55749
  }
55497
55750
  const inheritedFloat = inherited?.kind === "float" ? inherited : void 0;
55498
- const numericMin = min === null ? null : Number(min);
55499
- const numericMax = max === null ? null : Number(max);
55751
+ const numericMin = min === null ? null : Math.fround(Number(min));
55752
+ const numericMax = max === null ? null : Math.fround(Number(max));
55500
55753
  return {
55501
55754
  ...common,
55502
55755
  kind: "float",
@@ -56455,9 +56708,8 @@ function lowerExpressionValue(context, expression, declaredExpected, ownerClass,
56455
56708
  case "litString":
56456
56709
  return expression.value;
56457
56710
  case "litInt":
56458
- return expected.name === "decimal" ? expression.raw : expression.value;
56459
56711
  case "litFloat":
56460
- return expected.name === "decimal" ? expression.raw : expression.value;
56712
+ return numberLiteralValue(expression, expected);
56461
56713
  case "unary": {
56462
56714
  if (expression.op !== "-") {
56463
56715
  throw new Error(
@@ -57314,6 +57566,11 @@ function positionalContextualEnumArgument(value) {
57314
57566
  const expression = parseAnnotationExpression(positional.expression);
57315
57567
  return expression.kind === "contextualEnum" ? expression.name : null;
57316
57568
  }
57569
+ function numberLiteralValue(expression, expected) {
57570
+ if (expected.name === "decimal") return expression.raw;
57571
+ if (expected.name === "float") return Math.fround(expression.value);
57572
+ return expression.value;
57573
+ }
57317
57574
  function numberTextArgument(value, name) {
57318
57575
  const raw = argument(value, name);
57319
57576
  if (raw === null) return null;
@@ -72325,7 +72582,8 @@ function evalPointer(pointer, scope, ctx, resultUsage = "value") {
72325
72582
  callSiteId: pointer.callSiteId,
72326
72583
  invokeDelegate: (delegate, delegateArgs = []) => invokeDelegateValue(delegate, delegateArgs, ctx, pointer.callSiteId)
72327
72584
  });
72328
- if (intercepted?.handled === true) return intercepted.value;
72585
+ if (intercepted?.handled === true)
72586
+ return hostCallResult(member, intercepted.value, ctx);
72329
72587
  if (member === null) {
72330
72588
  if (pointer.missingMemberFallback === "valueEquality") {
72331
72589
  return jsEqual(innerThis, suppliedArgs[0]);
@@ -72361,7 +72619,8 @@ function evalPointer(pointer, scope, ctx, resultUsage = "value") {
72361
72619
  pointer.callSiteId
72362
72620
  )
72363
72621
  });
72364
- if (hosted?.handled === true) return hosted.value;
72622
+ if (hosted?.handled === true)
72623
+ return hostCallResult(member, hosted.value, ctx);
72365
72624
  const receiverId = trackedRowForValueReference(innerThis, ctx)?.id;
72366
72625
  throw new NativeFunctionDelegateUnavailableError(
72367
72626
  `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.`,
@@ -72652,7 +72911,8 @@ function invokeDelegateValue(value, args, ctx, callSiteId, ownerReceiver, result
72652
72911
  callSiteId,
72653
72912
  invokeDelegate: (delegate, delegateArgs = []) => invokeDelegateValue(delegate, delegateArgs, ctx, callSiteId)
72654
72913
  });
72655
- if (intercepted?.handled === true) return intercepted.value;
72914
+ if (intercepted?.handled === true)
72915
+ return hostCallResult(member, intercepted.value, ctx);
72656
72916
  state.delegateStack.push(frame);
72657
72917
  try {
72658
72918
  if (member.kind === 13 /* Function */) {
@@ -72667,7 +72927,8 @@ function invokeDelegateValue(value, args, ctx, callSiteId, ownerReceiver, result
72667
72927
  callSiteId,
72668
72928
  invokeDelegate: (delegate, delegateArgs = []) => invokeDelegateValue(delegate, delegateArgs, ctx, callSiteId)
72669
72929
  });
72670
- if (hosted?.handled === true) return hosted.value;
72930
+ if (hosted?.handled === true)
72931
+ return hostCallResult(member, hosted.value, ctx);
72671
72932
  const receiverId = trackedRowForValueReference(receiver, ctx)?.id;
72672
72933
  throw new NativeFunctionDelegateUnavailableError(
72673
72934
  `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.`,
@@ -73536,13 +73797,14 @@ function runtimeCollectionEntryMatchesType(value, typeInfo, ctx, seenCollections
73536
73797
  return resolved !== value && runtimeValueMatchesType(resolved, typeInfo, ctx, seenCollections);
73537
73798
  }
73538
73799
  function evalVectorConstructor(vectorType, componentPointers, scope, ctx) {
73539
- const values = componentPointers.map(
73540
- (pointer) => finiteNumberArg(
73800
+ const integer3 = vectorType === 15 /* Vector2Int */ || vectorType === 17 /* Vector3Int */;
73801
+ const values = componentPointers.map((pointer) => {
73802
+ const value = finiteNumberArg(
73541
73803
  evalPointer(pointer, scope, ctx),
73542
73804
  `${MemberKind[vectorType]} component`
73543
- )
73544
- );
73545
- const integer3 = vectorType === 15 /* Vector2Int */ || vectorType === 17 /* Vector3Int */;
73805
+ );
73806
+ return integer3 ? value : Math.fround(value);
73807
+ });
73546
73808
  if (integer3 && values.some((value) => !Number.isInteger(value))) {
73547
73809
  throw new NSGetterRuntimeError(
73548
73810
  `${MemberKind[vectorType]} components must be integers`
@@ -74433,11 +74695,17 @@ function evalOperation(operation, scope, ctx) {
74433
74695
  if (operation.type === "arithmetic" /* arithmetic */) {
74434
74696
  const op = operation.arithmetic;
74435
74697
  const operands = op.pointers.map((p) => evalPointer(p, scope, ctx));
74436
- return applyArithmetic(op.type, operands, op.decimal === true, ctx);
74698
+ return applyArithmetic(
74699
+ op.type,
74700
+ operands,
74701
+ op.decimal === true,
74702
+ ctx,
74703
+ op.numeric
74704
+ );
74437
74705
  }
74438
74706
  return evalBooleanExpression(operation.expression, scope, ctx);
74439
74707
  }
74440
- function applyArithmetic(op, operands, decimal2, ctx) {
74708
+ function applyArithmetic(op, operands, decimal2, ctx, numeric) {
74441
74709
  if (operands.length === 0) {
74442
74710
  throw new NSGetterRuntimeError("Arithmetic operation with no operands");
74443
74711
  }
@@ -74470,31 +74738,43 @@ function applyArithmetic(op, operands, decimal2, ctx) {
74470
74738
  `Arithmetic operand is not numeric: ${typeof o}`
74471
74739
  );
74472
74740
  });
74741
+ const round = numeric === "float" ? Math.fround : (value) => value;
74473
74742
  switch (op) {
74474
74743
  case "+" /* addition */:
74475
- return numbers.reduce((a, b) => a + b, 0);
74744
+ return numbers.reduce((a, b) => round(a + b), 0);
74476
74745
  case "-" /* subtraction */:
74477
- return numbers.reduce((a, b, i2) => i2 === 0 ? b : a - b);
74746
+ return numbers.reduce((a, b, i2) => i2 === 0 ? b : round(a - b));
74478
74747
  case "*" /* multiplication */:
74479
- return numbers.reduce((a, b) => a * b, 1);
74748
+ return numbers.reduce((a, b) => round(a * b), 1);
74480
74749
  case "/" /* division */:
74481
74750
  return numbers.reduce((a, b, i2) => {
74482
74751
  if (i2 === 0) return b;
74483
74752
  if (b === 0) throw new NSGetterRuntimeError("Division by zero");
74484
- return a / b;
74753
+ return numeric === "int" ? Math.trunc(a / b) : round(a / b);
74485
74754
  });
74486
74755
  case "%" /* remainder */:
74487
74756
  return numbers.reduce((a, b, i2) => {
74488
74757
  if (i2 === 0) return b;
74489
74758
  if (b === 0) throw new NSGetterRuntimeError("Modulo by zero");
74490
- return a % b;
74759
+ return round(a % b);
74491
74760
  });
74492
74761
  }
74493
74762
  }
74763
+ function hostCallResult(member, value, ctx) {
74764
+ if (typeof value !== "number" || member === null) return value;
74765
+ if (member.kind !== 13 /* Function */ && member.kind !== 23 /* NSFunction */) {
74766
+ return value;
74767
+ }
74768
+ const signature = resolveCallableSignature(member.id, member.kind, ctx);
74769
+ return signature?.returnTypeInfo.type === 4 /* Float */ ? Math.fround(value) : value;
74770
+ }
74494
74771
  function coerceAssignmentValue(value, targetTypeInfo, context) {
74495
74772
  if (targetTypeInfo.type === 8 /* Enum */ && typeof value === "string") {
74496
74773
  return [value];
74497
74774
  }
74775
+ if (targetTypeInfo.type === 4 /* Float */ && typeof value === "number") {
74776
+ return Math.fround(value);
74777
+ }
74498
74778
  return targetTypeInfo.type === 20 /* Decimal */ && typeof value === "number" ? coerceDecimalOperand(value, context) : value;
74499
74779
  }
74500
74780
  function coerceDecimalOperand(value, context) {
@@ -74644,7 +74924,7 @@ function evalMathOp(info, scope, ctx) {
74644
74924
  if (value < 0) return -1;
74645
74925
  return 0;
74646
74926
  case "sqrt":
74647
- return Math.sqrt(value);
74927
+ return Math.fround(Math.sqrt(value));
74648
74928
  }
74649
74929
  }
74650
74930
  function evalDecimalMathOp(info, fn, scope, ctx) {
@@ -75285,8 +75565,10 @@ function evalFunction(fn, scope, ctx) {
75285
75565
  );
75286
75566
  }
75287
75567
  case "toFloat":
75288
- return floatFromDecimalString(
75289
- coerceDecimalOperand(receiverRaw, "ToFloat")
75568
+ return Math.fround(
75569
+ floatFromDecimalString(
75570
+ coerceDecimalOperand(receiverRaw, "ToFloat")
75571
+ )
75290
75572
  );
75291
75573
  case "toDecimal": {
75292
75574
  if (typeof receiverRaw !== "number") {
@@ -75794,6 +76076,9 @@ function evaluateConstructorFields(descriptor, scope, ctx) {
75794
76076
  `constructor field '${validated.schemaKey}'`
75795
76077
  );
75796
76078
  }
76079
+ if (typeof value === "number" && validated.typeInfo.type === 4 /* Float */) {
76080
+ value = Math.fround(value);
76081
+ }
75797
76082
  if (value === null || value === void 0) {
75798
76083
  if (validated.requiredWithoutDefault) {
75799
76084
  throw new NSGetterRuntimeError(
@@ -76414,8 +76699,9 @@ function evaluateDeclaredConstructorArguments(info, record6, scope, ctx) {
76414
76699
  );
76415
76700
  }
76416
76701
  const value = byName.get(parameter4.name);
76417
- if (parameter4.type !== 20 /* Decimal */) return value;
76418
76702
  if (typeof value !== "number") return value;
76703
+ if (parameter4.type === 4 /* Float */) return Math.fround(value);
76704
+ if (parameter4.type !== 20 /* Decimal */) return value;
76419
76705
  return coerceDecimalOperand(
76420
76706
  value,
76421
76707
  `constructor argument '${parameter4.name}'`
@@ -89117,6 +89403,13 @@ function validateProjectRecordByKind(recordKind, data, options = {}) {
89117
89403
  if (recordKind === ProjectRecordKind.Member) {
89118
89404
  const memberGuard = options.members === "authored-or-compiled" ? isAnyMemberAuthoredOrCompiled : isAnyMember;
89119
89405
  if (memberGuard(data)) return;
89406
+ const unrounded = unroundedFloatPath(data);
89407
+ if (unrounded !== null) {
89408
+ const id2 = typeof data === "object" && data !== null && "id" in data ? String(data.id) : "";
89409
+ throw new Error(
89410
+ `Member "${id2}" field "${unrounded}" must be a 32-bit float.`
89411
+ );
89412
+ }
89120
89413
  }
89121
89414
  if (recordKind === ProjectRecordKind.Value) {
89122
89415
  if (isMemberValue(data)) {
@@ -89212,6 +89505,7 @@ var init_project_record_validation = __esm({
89212
89505
  init_localization_types();
89213
89506
  init_unity_import_settings();
89214
89507
  init_project_file_authoring_identifiers();
89508
+ init_float32();
89215
89509
  }
89216
89510
  });
89217
89511
 
@@ -97343,15 +97637,13 @@ function inspectStoredValueNodeInternal(options) {
97343
97637
  return terminal();
97344
97638
  }
97345
97639
  if (member.kind === 4 /* Float */) {
97346
- if (typeof row.value !== "number" || !Number.isFinite(row.value)) {
97347
- fail(`${path} must store a finite number.`);
97348
- }
97640
+ if (!isFloat32(row.value)) fail(`${path} must store a 32-bit float.`);
97349
97641
  assertNumberRange(member, row.value, path);
97350
97642
  return terminal();
97351
97643
  }
97352
- const leafIssue = storedLeafShapeIssue(member, row.value);
97353
- if (leafIssue !== null) {
97354
- fail(`${path} ${storedLeafShapeIssueMessage(leafIssue, "graph")}`);
97644
+ const leafIssue2 = storedLeafShapeIssue(member, row.value);
97645
+ if (leafIssue2 !== null) {
97646
+ fail(`${path} ${storedLeafShapeIssueMessage(leafIssue2, "graph")}`);
97355
97647
  }
97356
97648
  if (isMemberStringBase(member)) {
97357
97649
  if (typeof row.value !== "string") fail(`${path} must store a string.`);
@@ -97713,6 +98005,7 @@ var init_stored_value_graph_semantics = __esm({
97713
98005
  init_inheritance();
97714
98006
  init_generics();
97715
98007
  init_classes();
98008
+ init_float32();
97716
98009
  StoredValueSemanticError = class extends Error {
97717
98010
  name = "StoredValueSemanticError";
97718
98011
  };
@@ -104757,9 +105050,7 @@ function hasValidOpaqueParameterDefault(value) {
104757
105050
  if (value.type === 2) {
104758
105051
  return typeof payload === "number" && Number.isInteger(payload);
104759
105052
  }
104760
- if (value.type === 4) {
104761
- return typeof payload === "number" && Number.isFinite(payload);
104762
- }
105053
+ if (value.type === 4) return isFloat32(payload);
104763
105054
  if (value.type === 3 || value.type === 8 || value.type === 20) {
104764
105055
  return typeof payload === "string";
104765
105056
  }
@@ -106689,6 +106980,7 @@ var init_projectInterfaceValidation = __esm({
106689
106980
  init_classes();
106690
106981
  init_interface_types();
106691
106982
  init_member_kinds();
106983
+ init_float32();
106692
106984
  init_stored_value_shape();
106693
106985
  init_member_storage();
106694
106986
  NEO_OBJECT_WORLD_KIND = "object";
@@ -108107,7 +108399,7 @@ function csharpValueType(ctx, member, saved = false, env, genericBindingSaved =
108107
108399
  return isRequiredMember2(member) ? "bool" : "bool?";
108108
108400
  if (isMemberIntBase(member)) return isRequiredMember2(member) ? "int" : "int?";
108109
108401
  if (isMemberFloatBase(member))
108110
- return isRequiredMember2(member) ? "double" : "double?";
108402
+ return isRequiredMember2(member) ? "float" : "float?";
108111
108403
  if (isMemberDecimalBase(member)) {
108112
108404
  return isRequiredMember2(member) ? "decimal" : "decimal?";
108113
108405
  }
@@ -108322,7 +108614,7 @@ function csharpTypeInfoValueType(ctx, typeInfo, saved, genericEnv, genericBindin
108322
108614
  case 2 /* Int */:
108323
108615
  return optional2 ? "int?" : "int";
108324
108616
  case 4 /* Float */:
108325
- return optional2 ? "double?" : "double";
108617
+ return optional2 ? "float?" : "float";
108326
108618
  case 20 /* Decimal */:
108327
108619
  return optional2 ? "decimal?" : "decimal";
108328
108620
  case 3 /* String */:
@@ -109581,6 +109873,14 @@ var init_system_protection = __esm({
109581
109873
  disallow: [SystemDisallowedOperation.DeleteRecord],
109582
109874
  reasonKey: "systemProtection.reasons.policyDeleteBlocked",
109583
109875
  serverReason: "System default policy may be edited but must keep one default row."
109876
+ },
109877
+ [SystemProtectionKind.ScriptRuntime]: {
109878
+ disallow: [
109879
+ SystemDisallowedOperation.EditRecord,
109880
+ SystemDisallowedOperation.DeleteRecord
109881
+ ],
109882
+ reasonKey: "systemProtection.reasons.scriptRuntime",
109883
+ serverReason: "Lifecycle interface the Neo Compose script runtime calls."
109584
109884
  }
109585
109885
  };
109586
109886
  }
@@ -124580,8 +124880,9 @@ function lowerValueBody(context, member, expression, base, source, environment,
124580
124880
  case "bool":
124581
124881
  return { ...next, value: requiredBoolean(expression) };
124582
124882
  case "int":
124583
- case "float":
124584
124883
  return { ...next, value: requiredNumber2(expression) };
124884
+ case "float":
124885
+ return { ...next, value: Math.fround(requiredNumber2(expression)) };
124585
124886
  case "decimal":
124586
124887
  return { ...next, value: requiredDecimal(expression) };
124587
124888
  case "string":
@@ -142417,6 +142718,260 @@ var init_analyzer = __esm({
142417
142718
  }
142418
142719
  });
142419
142720
 
142721
+ // ../packages/neoscript-language/src/project-source-lifecycle-editor.ts
142722
+ function projectLifecycleHooks(analysis) {
142723
+ let hooks = PROJECT_LIFECYCLE_HOOKS.get(analysis);
142724
+ if (!hooks) {
142725
+ const interfaces = [];
142726
+ for (const document of analysis.documents.values()) {
142727
+ for (const declaration of document.declarations) {
142728
+ if (declaration.kind === "interface") interfaces.push(declaration);
142729
+ }
142730
+ }
142731
+ hooks = lifecycleHooksByMemberName(interfaces);
142732
+ PROJECT_LIFECYCLE_HOOKS.set(analysis, hooks);
142733
+ }
142734
+ return hooks;
142735
+ }
142736
+ function projectLifecycleQuickFixes(analysis, document, diagnostic, findType) {
142737
+ if (diagnostic.code !== "reserved-lifecycle-member") return [];
142738
+ const source = analysis.documents.get(document.uri);
142739
+ if (!source) return [];
142740
+ for (const declaration of source.declarations) {
142741
+ if (declaration.kind !== "class") continue;
142742
+ const member = declaration.members.find(
142743
+ (candidate) => rangesEqual(candidate.nameRange, diagnostic.range)
142744
+ );
142745
+ if (member?.kind !== "function") continue;
142746
+ const hook = projectLifecycleHooks(analysis).get(member.name.toLowerCase());
142747
+ if (hook === void 0) return [];
142748
+ const text = new SourceText(document.text);
142749
+ const tokens = source.syntax.tokens;
142750
+ const edits = [];
142751
+ if (!classLifecycleContext(declaration, findType).implemented.has(
142752
+ hook.interfaceName
142753
+ )) {
142754
+ edits.push(baseListEdit(text, tokens, declaration, hook.interfaceName));
142755
+ }
142756
+ if (effectiveSourceAccessModifier(member.modifiers, "class") !== "public") {
142757
+ edits.push(publicAccessEdit(text, tokens, member));
142758
+ }
142759
+ if (edits.length === 0) return [];
142760
+ return [
142761
+ {
142762
+ title: `Implement '${hook.interfaceName}'`,
142763
+ kind: "quickfix",
142764
+ diagnostics: [diagnostic],
142765
+ edit: { changes: { [document.uri]: edits } },
142766
+ preferred: true
142767
+ }
142768
+ ];
142769
+ }
142770
+ return [];
142771
+ }
142772
+ function lifecycleMemberCompletions(analysis, document, position, findType) {
142773
+ const hooks = projectLifecycleHooks(analysis);
142774
+ if (hooks.size === 0) return [];
142775
+ const source = analysis.documents.get(document.uri);
142776
+ const declaration = source?.declarations.find(
142777
+ (candidate) => candidate.kind === "class" && positionCompare(candidate.range.start, position) < 0 && positionCompare(position, candidate.range.end) < 0
142778
+ );
142779
+ if (!source || source.kind === "flow" || !declaration) return [];
142780
+ const text = new SourceText(document.text);
142781
+ const offset = text.offsetAt(position);
142782
+ const site = memberDeclarationSite(
142783
+ source.syntax.tokens,
142784
+ declaration,
142785
+ text,
142786
+ offset
142787
+ );
142788
+ if (site === null) return [];
142789
+ const own = new Set(
142790
+ declaration.members.map((member) => member.name.toLowerCase())
142791
+ );
142792
+ const context = classLifecycleContext(declaration, findType);
142793
+ const range2 = text.range(site.start, site.end);
142794
+ const typed = document.text.slice(site.start, site.modifiersEnd).trim();
142795
+ const items = [];
142796
+ for (const [key, hook] of hooks) {
142797
+ if (own.has(key)) continue;
142798
+ if (hook.unity && !context.rendersObjects) continue;
142799
+ const inherited = context.inherited.get(key);
142800
+ if (inherited !== void 0 && !isOverridable(inherited)) continue;
142801
+ const name = hook.member.name;
142802
+ const snippet = `public ${inherited === void 0 ? "" : "override "}${hookSignature(hook.member)} {
142803
+ $0
142804
+ }`;
142805
+ items.push({
142806
+ label: name,
142807
+ kind: "method",
142808
+ detail: hook.interfaceName,
142809
+ ...hook.docsText === void 0 ? {} : { documentation: hook.docsText.split("\n")[0] },
142810
+ insertText: snippet,
142811
+ insertTextFormat: "snippet",
142812
+ filterText: typed.length === 0 ? name : `${typed} ${name}`,
142813
+ textEdit: { range: range2, newText: snippet },
142814
+ ...context.implemented.has(hook.interfaceName) ? {} : {
142815
+ additionalTextEdits: [
142816
+ baseListEdit(
142817
+ text,
142818
+ source.syntax.tokens,
142819
+ declaration,
142820
+ hook.interfaceName
142821
+ )
142822
+ ]
142823
+ }
142824
+ });
142825
+ }
142826
+ return items;
142827
+ }
142828
+ function lifecycleHoverMarkdown(analysis, memberName) {
142829
+ const hook = projectLifecycleHooks(analysis).get(memberName.toLowerCase());
142830
+ if (hook === void 0) return "";
142831
+ const docs = hook.docsText === void 0 ? "" : ` ${hook.docsText}`;
142832
+ return `
142833
+
142834
+ Lifecycle hook from \`${hook.interfaceName}\`.${docs}`;
142835
+ }
142836
+ function classLifecycleContext(declaration, findType) {
142837
+ const implemented = /* @__PURE__ */ new Set();
142838
+ const inherited = /* @__PURE__ */ new Map();
142839
+ const pendingInterfaces = [];
142840
+ const visitedClasses = /* @__PURE__ */ new Set();
142841
+ let rendersObjects = false;
142842
+ let current = declaration;
142843
+ while (current !== void 0 && !visitedClasses.has(current.name)) {
142844
+ visitedClasses.add(current.name);
142845
+ if (current.name === NEO_LIFECYCLE_OBJECT_CLASS_NAME) rendersObjects = true;
142846
+ if (current !== declaration) {
142847
+ for (const member of current.members) {
142848
+ const key = member.name.toLowerCase();
142849
+ if (!inherited.has(key)) inherited.set(key, member);
142850
+ }
142851
+ }
142852
+ let next;
142853
+ for (const base of current.baseTypes) {
142854
+ const target = findType(base.name);
142855
+ if (target?.kind === "class") next ??= target;
142856
+ else if (target?.kind === "interface") pendingInterfaces.push(target);
142857
+ }
142858
+ current = next;
142859
+ }
142860
+ while (pendingInterfaces.length > 0) {
142861
+ const target = pendingInterfaces.pop();
142862
+ if (implemented.has(target.name)) continue;
142863
+ implemented.add(target.name);
142864
+ for (const base of target.baseTypes) {
142865
+ const parent = findType(base.name);
142866
+ if (parent?.kind === "interface") pendingInterfaces.push(parent);
142867
+ }
142868
+ }
142869
+ return { implemented, rendersObjects, inherited };
142870
+ }
142871
+ function isOverridable(member) {
142872
+ if (member.modifiers.includes("sealed")) return false;
142873
+ return member.modifiers.includes("virtual") || member.modifiers.includes("abstract") || member.modifiers.includes("override");
142874
+ }
142875
+ function hookSignature(member) {
142876
+ const parameters = member.parameters.map(
142877
+ (parameter4) => `${neoSourceTypeText(parameter4.type)} ${parameter4.name}`
142878
+ ).join(", ");
142879
+ return `${neoSourceTypeText(member.type)} ${member.name}(${parameters})`;
142880
+ }
142881
+ function baseListEdit(text, tokens, declaration, interfaceName) {
142882
+ const last = declaration.baseTypes[declaration.baseTypes.length - 1];
142883
+ if (last !== void 0) {
142884
+ const end = declaration.baseTypes.length === 1 && declaration.baseClause ? declaration.baseClause.range.end : last.range.end;
142885
+ const at4 = text.offsetAt(end);
142886
+ return { range: text.range(at4, at4), newText: `, ${interfaceName}` };
142887
+ }
142888
+ const at3 = headerEnd(tokens, text.offsetAt(declaration.nameRange.end));
142889
+ return { range: text.range(at3, at3), newText: ` : ${interfaceName}` };
142890
+ }
142891
+ function headerEnd(tokens, nameEnd) {
142892
+ let end = nameEnd;
142893
+ let depth = 0;
142894
+ for (const token of tokens) {
142895
+ if (token.start < nameEnd || token.kind === "comment") continue;
142896
+ if (token.text === "(") depth += 1;
142897
+ else if (token.text === ")") depth -= 1;
142898
+ else if (depth === 0 && token.text === "{") break;
142899
+ end = token.end;
142900
+ }
142901
+ return end;
142902
+ }
142903
+ function publicAccessEdit(text, tokens, member) {
142904
+ const start = text.offsetAt(member.range.start);
142905
+ const typeStart = text.offsetAt(member.type.range.start);
142906
+ let firstModifier;
142907
+ let depth = 0;
142908
+ for (const token of tokens) {
142909
+ if (token.start < start || token.kind === "comment") continue;
142910
+ if (token.start >= typeStart) break;
142911
+ if (token.text === "(") depth += 1;
142912
+ else if (token.text === ")") depth -= 1;
142913
+ if (depth !== 0 || token.kind !== "keyword") continue;
142914
+ if (isNeoSourceAccessModifier(token.text)) {
142915
+ return { range: token.range, newText: "public" };
142916
+ }
142917
+ firstModifier ??= token;
142918
+ }
142919
+ const at3 = firstModifier?.start ?? typeStart;
142920
+ return { range: text.range(at3, at3), newText: "public " };
142921
+ }
142922
+ function memberDeclarationSite(tokens, declaration, text, offset) {
142923
+ const headerEndOffset = text.offsetAt(
142924
+ declaration.baseClause?.range.end ?? declaration.nameRange.end
142925
+ );
142926
+ const classStart = text.offsetAt(declaration.range.start);
142927
+ const before = [];
142928
+ let depth = 0;
142929
+ for (const token of tokens) {
142930
+ if (token.start < classStart || token.kind === "comment") continue;
142931
+ if (token.kind === "eof" || token.start >= offset) break;
142932
+ before.push(token);
142933
+ if (token.start < headerEndOffset || token.interpolationHole) continue;
142934
+ if (token.text === "{") depth += 1;
142935
+ else if (token.text === "}") depth -= 1;
142936
+ }
142937
+ if (depth !== 1) return null;
142938
+ let index = before.length - 1;
142939
+ let end = offset;
142940
+ let word = before[index];
142941
+ if (word !== void 0 && (word.kind === "identifier" || word.kind === "type") && word.end === offset) {
142942
+ index -= 1;
142943
+ } else {
142944
+ word = void 0;
142945
+ }
142946
+ let start = word?.start ?? offset;
142947
+ const modifiersEnd = start;
142948
+ while (MEMBER_SITE_MODIFIERS.has(before[index]?.text ?? "")) {
142949
+ start = before[index].start;
142950
+ index -= 1;
142951
+ }
142952
+ const boundary = before[index]?.text;
142953
+ if (boundary !== "{" && boundary !== "}" && boundary !== ";") return null;
142954
+ if (word !== void 0) end = word.end;
142955
+ return { start, modifiersEnd, end };
142956
+ }
142957
+ var PROJECT_LIFECYCLE_HOOKS, MEMBER_SITE_MODIFIERS;
142958
+ var init_project_source_lifecycle_editor = __esm({
142959
+ "../packages/neoscript-language/src/project-source-lifecycle-editor.ts"() {
142960
+ "use strict";
142961
+ init_project_source_ast();
142962
+ init_project_source_lifecycle_hooks();
142963
+ init_project_source_tokens();
142964
+ init_source_text();
142965
+ PROJECT_LIFECYCLE_HOOKS = /* @__PURE__ */ new WeakMap();
142966
+ MEMBER_SITE_MODIFIERS = /* @__PURE__ */ new Set([
142967
+ "public",
142968
+ "protected",
142969
+ "private",
142970
+ "override"
142971
+ ]);
142972
+ }
142973
+ });
142974
+
142420
142975
  // ../packages/neoscript-language/src/quick-fixes.ts
142421
142976
  function quickFixFor(document, diagnostic) {
142422
142977
  if (diagnostic.code === "missing-semicolon") {
@@ -142895,6 +143450,16 @@ function projectFallbackCompletions(analysis, document, position) {
142895
143450
  document,
142896
143451
  position
142897
143452
  );
143453
+ if (!expression && !classBasePosition) {
143454
+ for (const item of lifecycleMemberCompletions(
143455
+ analysis,
143456
+ document,
143457
+ position,
143458
+ (name) => findTypeDeclaration(analysis, name)
143459
+ )) {
143460
+ items.set(`${item.detail}.${item.label}`, item);
143461
+ }
143462
+ }
142898
143463
  const keywords = expression ? afterNew ? [] : ["new"] : typePosition ? [] : [
142899
143464
  "class",
142900
143465
  "interface",
@@ -143338,7 +143903,7 @@ function declaredConstructorNamedArguments(analysis, typeName, used) {
143338
143903
  function parameterArgumentField(parameter4) {
143339
143904
  return {
143340
143905
  name: parameter4.name,
143341
- type: sourceTypeText(parameter4.type),
143906
+ type: neoSourceTypeText(parameter4.type),
143342
143907
  ...parameter4.defaultValue === void 0 ? {} : { defaultText: parameter4.defaultValue.text }
143343
143908
  };
143344
143909
  }
@@ -143358,7 +143923,7 @@ function projectInitializerMemberCompletions(analysis, document, position) {
143358
143923
  const items = unsettled.map((member) => ({
143359
143924
  label: member.name,
143360
143925
  kind: "property",
143361
- detail: `${sourceTypeText(member.member.type)} \u2014 required`,
143926
+ detail: `${neoSourceTypeText(member.member.type)} \u2014 required`,
143362
143927
  insertText: `${member.name} = `,
143363
143928
  // Required members sort ahead of defaulted ones: they are the only entries
143364
143929
  // whose absence is a diagnostic.
@@ -143369,7 +143934,7 @@ function projectInitializerMemberCompletions(analysis, document, position) {
143369
143934
  items.push({
143370
143935
  label: member.name,
143371
143936
  kind: "property",
143372
- detail: sourceTypeText(member.type),
143937
+ detail: neoSourceTypeText(member.type),
143373
143938
  insertText: `${member.name} = `,
143374
143939
  sortText: `1${member.name}`
143375
143940
  });
@@ -143407,10 +143972,6 @@ function functionParameterLabel(parameter4) {
143407
143972
  const suffix = parameter4.defaultValue === void 0 ? "" : ` = ${parameter4.defaultValue.text}`;
143408
143973
  return `${parameter4.type.name} ${parameter4.name}${suffix}`;
143409
143974
  }
143410
- function sourceTypeText(type) {
143411
- const args = type.typeArguments.length > 0 ? `<${type.typeArguments.map(sourceTypeText).join(", ")}>` : "";
143412
- return `${type.name}${args}${type.nullable ? "?" : ""}`;
143413
- }
143414
143975
  function projectConstructionIndex(analysis) {
143415
143976
  const cached = CONSTRUCTION_INDEX_CACHE.get(analysis);
143416
143977
  if (cached) return cached;
@@ -143688,9 +144249,10 @@ ${resolved.symbol.documentation}` : "";
143688
144249
  ID: \`${resolved.symbol.id}\`` : "\n\nPending ID";
143689
144250
  const detail = resolved.symbol.detail ? `: \`${resolved.symbol.detail}\`` : "";
143690
144251
  const contract = resolved.symbol.kind === "class" ? projectConstructionContractMarkdown(analysis, resolved.symbol.name) : "";
144252
+ const lifecycle = resolved.symbol.kind === "member" && resolved.symbol.callable === true && resolved.symbol.static !== true && analysis.documents.get(resolved.symbol.location.uri)?.kind !== "flow" ? lifecycleHoverMarkdown(analysis, resolved.symbol.name) : "";
143691
144253
  return {
143692
144254
  range: resolved.token.range,
143693
- markdown: `**${resolved.symbol.kind}** \`${resolved.symbol.name}\`${detail}${owner}${documentation}${contract}${id2}`
144255
+ markdown: `**${resolved.symbol.kind}** \`${resolved.symbol.name}\`${detail}${owner}${documentation}${lifecycle}${contract}${id2}`
143694
144256
  };
143695
144257
  }
143696
144258
  function projectBuiltinSymbolAt(document, position) {
@@ -143731,7 +144293,7 @@ Construct with \`new(${parameters})\` \u2014 this class declares a required cons
143731
144293
  const lines = [];
143732
144294
  if (required2.length > 0) {
143733
144295
  const members = required2.map(
143734
- (member) => `\`${sourceTypeText(member.member.type)} ${member.name}\``
144296
+ (member) => `\`${neoSourceTypeText(member.member.type)} ${member.name}\``
143735
144297
  ).join(", ");
143736
144298
  lines.push(`Every construction path must settle ${members}.`);
143737
144299
  }
@@ -144895,7 +145457,7 @@ function declarationConstructorParameters(declaration) {
144895
145457
  }
144896
145458
  function sourceParameterProjection(parameter4) {
144897
145459
  return {
144898
- type: sourceTypeText(parameter4.type),
145460
+ type: neoSourceTypeText(parameter4.type),
144899
145461
  name: parameter4.name,
144900
145462
  ...parameter4.defaultValue === void 0 ? {} : { defaultText: parameter4.defaultValue.text }
144901
145463
  };
@@ -145006,6 +145568,8 @@ var init_project_source_language_features = __esm({
145006
145568
  init_language_spec();
145007
145569
  init_project_source_semantics();
145008
145570
  init_project_source_variants();
145571
+ init_project_source_lifecycle_editor();
145572
+ init_project_source_ast();
145009
145573
  init_project();
145010
145574
  init_project_source_semantics();
145011
145575
  init_project_source_settlement();
@@ -145127,7 +145691,7 @@ function localValueRefactorings(analysis, document, range2) {
145127
145691
  start: { line: 0, character: 0 },
145128
145692
  end: { line: 0, character: 0 }
145129
145693
  },
145130
- newText: `${typeText2(type)} ${name} = ${selected};
145694
+ newText: `${neoSourceTypeText(type)} ${name} = ${selected};
145131
145695
  `
145132
145696
  }
145133
145697
  ]
@@ -145136,9 +145700,6 @@ function localValueRefactorings(analysis, document, range2) {
145136
145700
  }
145137
145701
  ];
145138
145702
  }
145139
- function typeText2(type) {
145140
- return type.name + (type.typeArguments.length === 0 ? "" : `<${type.typeArguments.map(typeText2).join(", ")}>`) + (type.nullable ? "?" : "");
145141
- }
145142
145703
  function withoutValueIdentities(text) {
145143
145704
  const tokens = lex(text).roots.filter((token) => token.kind !== "comment");
145144
145705
  const removals = [];
@@ -145215,6 +145776,7 @@ var init_project_local_value_refactorings = __esm({
145215
145776
  init_project_local_values();
145216
145777
  init_project_source_language_features();
145217
145778
  init_source_text();
145779
+ init_project_source_ast();
145218
145780
  }
145219
145781
  });
145220
145782
 
@@ -146143,6 +146705,7 @@ var init_service = __esm({
146143
146705
  init_project_source_identifiers();
146144
146706
  init_project_source_analysis();
146145
146707
  init_project_source_construction_quick_fixes();
146708
+ init_project_source_lifecycle_editor();
146146
146709
  init_project_source_reference_family();
146147
146710
  init_project_source_language_features();
146148
146711
  init_project_source_tokens();
@@ -146460,6 +147023,14 @@ var init_service = __esm({
146460
147023
  actions.push(
146461
147024
  ...projectConstructionQuickFixes(project, state.document, diagnostic)
146462
147025
  );
147026
+ actions.push(
147027
+ ...projectLifecycleQuickFixes(
147028
+ project,
147029
+ state.document,
147030
+ diagnostic,
147031
+ (name) => findTypeDeclaration(project, name)
147032
+ )
147033
+ );
146463
147034
  }
146464
147035
  actions.push(...spellingQuickFixes(state.document, diagnostic));
146465
147036
  const edit = quickFixFor(state.document, diagnostic);
@@ -156206,7 +156777,7 @@ There is no --keep-current: preserving current semantic state defines flatten.
156206
156777
  async function main() {
156207
156778
  const args = parseArgs(process.argv.slice(2));
156208
156779
  if (args.command === "--version") {
156209
- console.log("0.65.1");
156780
+ console.log("0.67.0");
156210
156781
  return;
156211
156782
  }
156212
156783
  if (args.command === null || args.command === "help" || args.command === "--help" || args.command === "-h") {