@neocompose/cli 0.64.1 → 0.65.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.65.1] - 2026-10-03
4
+
5
+ - Pull and push reconciliation emit a stored `new NeoAnimationSegmentFrame<NeoCollider?>(value: null)`. A null constructor argument typed by a class's generic parameter now checks nullability against the row's type argument. Previously, a pushed segment holding a null frame committed, but source reconciliation failed with "Required constructor argument stores null".
6
+ - Pulled type arguments keep a nullable binding's `?`, in constructions, references, and declared member types. Previously `FrameBox<NeoCollider?> Box` pulled as `FrameBox<NeoCollider> Box`, and the next push made the binding required.
7
+ - Code compiled from stored records keeps a nullable binding's `?`. A member declared `NeoAnimationSegmentFrame<NeoCollider?>` keeps that type, so passing it where that type is expected compiles, and reading its value as a required `NeoCollider` reports an error. Previously, every bound type argument was typed as required.
8
+
9
+ ## [0.65.0] - 2026-10-03
10
+
11
+ - Move `Collider` from `NeoObject` to `NeoObjectBase`, so sprite objects and layer groups can carry a box collider too. Unity codegen implements `INeoColliderSource` on `NeoObjectBase`.
12
+ - Segment tracks may target a Class member, such as `NeoObjectBase.Collider`. Each frame plays a whole value or `null`. A segment whose class does not fit the target member reports which class it plays. A segment of nullable values cannot target a required member.
13
+ - Constructor arguments and object initializers typed by a class's generic parameter take the site's type argument. `new NeoAnimationSegmentFrame<NeoCollider?>(value: null)` and `{ Value = null }` now compile.
14
+ - Authoring `null` over a member that held a class instance drops the instance's class and constructor metadata. Previously the row kept them, and push rejected it as an invalid value record.
15
+
3
16
  ## [0.64.1] - 2026-10-02
4
17
 
5
18
  - Reserve `OnChanged` as a member name. Every generated Unity class declares the SDK's `OnChanged` change subscription, so a member by that name now reports `reserved-project-name` instead of pushing the SDK method aside as `OnChanged_2`.
package/dist/neo.mjs CHANGED
@@ -14192,11 +14192,18 @@ function validateExpression(expression, expected, scope, environment, uri, range
14192
14192
  siteArgumentNames,
14193
14193
  siteInitializerNames
14194
14194
  );
14195
+ const siteTypeArguments = expression.typeArguments?.map(semanticAstType) ?? (expression.className === null ? expected?.arguments : void 0) ?? [];
14196
+ const siteBindings = new Map(
14197
+ declaration.genericParameters.flatMap((parameter4, index) => {
14198
+ const argument2 = siteTypeArguments[index];
14199
+ return argument2 ? [[parameter4.name, argument2]] : [];
14200
+ })
14201
+ );
14195
14202
  for (const diagnostic of validateNeoConstructionSite(
14196
14203
  environment.construction,
14197
14204
  {
14198
14205
  className: declaration.name,
14199
- typeArguments: expression.typeArguments?.map(semanticAstType) ?? (expression.className === null ? expected?.arguments : void 0) ?? [],
14206
+ typeArguments: siteTypeArguments,
14200
14207
  argumentNames: variantSite?.argumentNames ?? siteArgumentNames,
14201
14208
  initializerNames: variantSite?.initializerNames ?? siteInitializerNames
14202
14209
  }
@@ -14232,7 +14239,7 @@ function validateExpression(expression, expected, scope, environment, uri, range
14232
14239
  (candidate) => candidate.name === (variantMember ?? named)
14233
14240
  ) : fields[index];
14234
14241
  const expectedType = parameter4?.type ?? field?.type;
14235
- const expectedSemanticType = declaration.name === NEO_LOOKUP_VARIANT_FOLDER_TYPE_NAME && named === "collectionValue" ? { name: "string" } : expectedType ? semanticType(expectedType) : void 0;
14242
+ const expectedSemanticType = declaration.name === NEO_LOOKUP_VARIANT_FOLDER_TYPE_NAME && named === "collectionValue" ? { name: "string" } : expectedType ? substituteSemanticType(semanticType(expectedType), siteBindings) : void 0;
14236
14243
  validateExpression(
14237
14244
  argument2,
14238
14245
  expectedSemanticType,
@@ -14253,8 +14260,11 @@ function validateExpression(expression, expected, scope, environment, uri, range
14253
14260
  validateExpression(
14254
14261
  entry.value,
14255
14262
  field ? substituteSemanticType(
14256
- semanticType(field.member.type),
14257
- field.bindings
14263
+ substituteSemanticType(
14264
+ semanticType(field.member.type),
14265
+ field.bindings
14266
+ ),
14267
+ siteBindings
14258
14268
  ) : void 0,
14259
14269
  scope,
14260
14270
  environment,
@@ -14469,7 +14479,7 @@ function sourceClassMemberByName(declaration, name, environment, initialBindings
14469
14479
  function substituteSemanticType(type, bindings) {
14470
14480
  const bound = bindings.get(type.name);
14471
14481
  if (bound !== void 0 && (type.arguments ?? []).length === 0) {
14472
- return type.nullable === void 0 ? bound : { ...bound, nullable: type.nullable };
14482
+ return type.nullable === true ? { ...bound, nullable: true } : bound;
14473
14483
  }
14474
14484
  const argumentsList2 = (type.arguments ?? []).map(
14475
14485
  (argument2) => substituteSemanticType(argument2, bindings)
@@ -22778,7 +22788,12 @@ var init_strict_resolver = __esm({
22778
22788
  };
22779
22789
  });
22780
22790
  fields.push(
22781
- ...this.resolveCallSiteInitializerFields(schemaClass2, expression, scope)
22791
+ ...this.resolveCallSiteInitializerFields(
22792
+ schemaClass2,
22793
+ targetType,
22794
+ expression,
22795
+ scope
22796
+ )
22782
22797
  );
22783
22798
  const schemaClassInfo = toWireType(targetType, this.project);
22784
22799
  if (schemaClassInfo.type !== 7 /* Class */) {
@@ -22904,6 +22919,7 @@ var init_strict_resolver = __esm({
22904
22919
  ) : [];
22905
22920
  const fields = this.resolveCallSiteInitializerFields(
22906
22921
  schemaClass2,
22922
+ targetType,
22907
22923
  expression,
22908
22924
  scope
22909
22925
  );
@@ -23018,7 +23034,7 @@ var init_strict_resolver = __esm({
23018
23034
  * the class's own schema exactly as a `classConstructor` field is, and
23019
23035
  * applied after the body so an explicit assignment always wins.
23020
23036
  */
23021
- resolveCallSiteInitializerFields(schemaClass2, expression, scope) {
23037
+ resolveCallSiteInitializerFields(schemaClass2, targetType, expression, scope) {
23022
23038
  const entries = expression.initializer ?? [];
23023
23039
  const seen = /* @__PURE__ */ new Set();
23024
23040
  return entries.map((entry) => {
@@ -23063,10 +23079,15 @@ var init_strict_resolver = __esm({
23063
23079
  entry.pos
23064
23080
  );
23065
23081
  }
23066
- const value = this.resolveExpression(entry.value, scope, member.type);
23067
- if (!isNeoScriptTypeAssignable(value.type, member.type, this.project)) {
23082
+ const memberType2 = substituteTypeParameters(
23083
+ member.type,
23084
+ targetType,
23085
+ schemaClass2
23086
+ );
23087
+ const value = this.resolveExpression(entry.value, scope, memberType2);
23088
+ if (!isNeoScriptTypeAssignable(value.type, memberType2, this.project)) {
23068
23089
  throw new CompileError(
23069
- `Object initializer '${member.name}' expected ${this.describe(member.type)} but got ${this.describe(value.type)}.`,
23090
+ `Object initializer '${member.name}' expected ${this.describe(memberType2)} but got ${this.describe(value.type)}.`,
23070
23091
  entry.pos
23071
23092
  );
23072
23093
  }
@@ -29934,7 +29955,7 @@ var init_registry = __esm({
29934
29955
  "../packages/manifest/schema-contract/registry.mjs"() {
29935
29956
  "use strict";
29936
29957
  PROJECT_SCHEMA_CONTRACT = Object.freeze({
29937
- cliVersion: "0.64.1",
29958
+ cliVersion: "0.65.1",
29938
29959
  projectFileUploadBatchSize: 32,
29939
29960
  documentRecords: {
29940
29961
  member: {
@@ -43302,6 +43323,9 @@ function createMergeMemo() {
43302
43323
  function resolveInheritanceChain(classId, classes, draft) {
43303
43324
  return [...indexClasses(classes).inheritanceChain(classId, draft)];
43304
43325
  }
43326
+ function resolveInheritanceChainFromIndex(classId, classIndex, draft) {
43327
+ return resolveChainFromIndex(classId, classIndex, draft);
43328
+ }
43305
43329
  function resolveChainFromIndex(classId, index, draft) {
43306
43330
  const chain = [];
43307
43331
  const visited = /* @__PURE__ */ new Set();
@@ -44374,6 +44398,14 @@ var init_world_system_classes_generated = __esm({
44374
44398
  docsText: "When false, this object and its children are neither rendered nor collided with. The value stays live: member writes still apply and a running animation clip keeps playing. Disabling an object hides its whole subtree regardless of each child's own value, and re-enabling it restores exactly what was there.",
44375
44399
  requirement: 1 /* Required */,
44376
44400
  schemaKey: "Enabled"
44401
+ },
44402
+ {
44403
+ memberId: "system_a0083c92-72f7-405f-8863-ff86f995d36d",
44404
+ memberKind: "class",
44405
+ defaultValue: null,
44406
+ docsText: "The object's box collider, or null for none. Any world object can carry one: placed objects, nested children, and sprites alike.",
44407
+ schemaClassWorldKind: "objectCollider",
44408
+ schemaKey: "Collider"
44377
44409
  }
44378
44410
  ],
44379
44411
  worldKind: "objectBase"
@@ -44583,13 +44615,6 @@ var init_world_system_classes_generated = __esm({
44583
44615
  requirement: 1 /* Required */,
44584
44616
  schemaKey: "Children"
44585
44617
  },
44586
- {
44587
- memberId: "system_a0083c92-72f7-405f-8863-ff86f995d36d",
44588
- memberKind: "class",
44589
- defaultValue: null,
44590
- schemaClassWorldKind: "objectCollider",
44591
- schemaKey: "Collider"
44592
- },
44593
44618
  {
44594
44619
  memberId: "system_c609fed3-53e4-5147-b493-e4aa23031d79",
44595
44620
  memberKind: "computed",
@@ -52500,7 +52525,7 @@ function renderMemberType(context, member, enclosingClassId, elementSettings = f
52500
52525
  context,
52501
52526
  required(context.members, boundMemberId, "generic binding"),
52502
52527
  enclosingClassId
52503
- ).replace(/\?$/u, "");
52528
+ );
52504
52529
  });
52505
52530
  value = `${schemaClass2.name}${args.length ? `<${args.join(", ")}>` : ""}`;
52506
52531
  }
@@ -61100,7 +61125,7 @@ function resolveMemberDeclaredTypeInfoInternal(member, members, seen) {
61100
61125
  members,
61101
61126
  nextSeen
61102
61127
  );
61103
- return argumentType === null ? [] : [[genericParamId, { ...argumentType, required: true }]];
61128
+ return argumentType === null ? [] : [[genericParamId, argumentType]];
61104
61129
  }
61105
61130
  )
61106
61131
  );
@@ -65443,13 +65468,11 @@ function classArguments(member, context, seen, genericEnvironment) {
65443
65468
  if (environmentEntry?.kind === "member") {
65444
65469
  const argument3 = analyzerMemberById(context, environmentEntry.memberId);
65445
65470
  return argument3 ? [
65446
- requiredType(
65447
- memberRuntimeType(
65448
- argument3,
65449
- context,
65450
- new Set(seen),
65451
- genericEnvironment
65452
- )
65471
+ memberRuntimeType(
65472
+ argument3,
65473
+ context,
65474
+ new Set(seen),
65475
+ genericEnvironment
65453
65476
  )
65454
65477
  ] : [];
65455
65478
  }
@@ -65464,13 +65487,11 @@ function classArguments(member, context, seen, genericEnvironment) {
65464
65487
  }
65465
65488
  const argument2 = analyzerMemberById(context, binding.memberId);
65466
65489
  return argument2 ? [
65467
- requiredType(
65468
- memberRuntimeType(
65469
- argument2,
65470
- context,
65471
- new Set(seen),
65472
- genericEnvironment
65473
- )
65490
+ memberRuntimeType(
65491
+ argument2,
65492
+ context,
65493
+ new Set(seen),
65494
+ genericEnvironment
65474
65495
  )
65475
65496
  ] : [];
65476
65497
  });
@@ -65657,11 +65678,7 @@ function genericConstraintLanguageType(constraint, context, genericEnvironment,
65657
65678
  ];
65658
65679
  }
65659
65680
  const member = analyzerMemberById(context, binding.memberId);
65660
- return member ? [
65661
- requiredType(
65662
- memberRuntimeType(member, context, /* @__PURE__ */ new Set(), genericEnvironment)
65663
- )
65664
- ] : [];
65681
+ return member ? [memberRuntimeType(member, context, /* @__PURE__ */ new Set(), genericEnvironment)] : [];
65665
65682
  });
65666
65683
  return {
65667
65684
  kind: "named",
@@ -73507,7 +73524,7 @@ function runtimeTypeCheck(value, checkType, ctx, seenCollections = /* @__PURE__
73507
73524
  function runtimeValueMatchesType(value, typeInfo, ctx, seenCollections = /* @__PURE__ */ new Set()) {
73508
73525
  if (typeInfo.type === NS_TYPE_UNKNOWN) return true;
73509
73526
  if (value === null || value === void 0) {
73510
- return typeInfo.type === 0 /* Null */ || !typeInfo.required;
73527
+ return typeInfo.type === 0 /* Null */ || typeInfo.type === 21 /* Generic */ || !typeInfo.required;
73511
73528
  }
73512
73529
  return runtimeTypeCheck(value, typeInfo, ctx, seenCollections);
73513
73530
  }
@@ -103116,11 +103133,26 @@ var init_animation_clips = __esm({
103116
103133
  `${args.label} targets "${boundChildName}.${targetEntry.schemaKey}" of kind ${MemberKind[targetMember.kind]}, but its segment plays ${MemberKind[valueBindingMember.kind]} values.`
103117
103134
  );
103118
103135
  }
103119
- if (!this.overrideLeafEligible(
103120
- targetMember,
103136
+ if (isMemberClass(targetMember) && isMemberClass(valueBindingMember) && !resolveInheritanceChainFromIndex(
103137
+ valueBindingMember.classId,
103138
+ this.classById
103139
+ ).some((current) => current.id === targetMember.classId)) {
103140
+ const targetClassName = this.classById.get(targetMember.classId)?.name ?? targetMember.classId;
103141
+ const valueClassName2 = this.classById.get(valueBindingMember.classId)?.name ?? valueBindingMember.classId;
103142
+ throw new Error(
103143
+ `${args.label} targets "${boundChildName}.${targetEntry.schemaKey}" of class ${targetClassName}, but its segment plays ${valueClassName2} values.`
103144
+ );
103145
+ }
103146
+ if (isRequiredMember2(targetMember) && !isRequiredMember2(valueBindingMember)) {
103147
+ throw new Error(
103148
+ `${args.label} targets "${boundChildName}.${targetEntry.schemaKey}", which is required, but its segment plays nullable values.`
103149
+ );
103150
+ }
103151
+ if (animationTrackStorage(
103121
103152
  [targetMember.id],
103153
+ this.memberById,
103122
103154
  args.childStoragePath
103123
- )) {
103155
+ ) === null) {
103124
103156
  throw new Error(
103125
103157
  `${args.label} targets "${boundChildName}.${targetEntry.schemaKey}", which is not a writable Save, Session, or Writable override leaf.`
103126
103158
  );
@@ -124540,7 +124572,7 @@ function lowerValueBody(context, member, expression, base, source, environment,
124540
124572
  if (member.required) {
124541
124573
  throw new Error(`Required member ${member.name} cannot store null.`);
124542
124574
  }
124543
- return { ...next, value: null };
124575
+ return overlayLiteralValueContent(next, { value: null });
124544
124576
  }
124545
124577
  switch (member.kind) {
124546
124578
  case "null":
@@ -127700,7 +127732,7 @@ function recordedStoredConstructorId(value, schemaClass2) {
127700
127732
  }
127701
127733
  function storedConstructorArgumentSource(context, type, value, environment, visited, cloneAggregateArguments = false, partial = false, constructorOwnsGraph = false) {
127702
127734
  if (value === null) {
127703
- if (!type.nullable) {
127735
+ if (!type.nullable && (type.kind !== "generic" || !storedGenericArgumentType(context, type, environment).nullable)) {
127704
127736
  throw new Error("Required constructor argument stores null.");
127705
127737
  }
127706
127738
  return "null";
@@ -127790,26 +127822,10 @@ function storedConstructorArgumentSource(context, type, value, environment, visi
127790
127822
  constructorOwnsGraph
127791
127823
  );
127792
127824
  }
127793
- case "generic": {
127794
- const bindingId = environment.get(type.genericParamId);
127795
- const inferredClassId = inferredGenericClassId(bindingId);
127796
- const resolvedType = inferredClassId === null ? bindingId === void 0 ? null : schemaTypeForBindingMember(context, bindingId, environment) : {
127797
- kind: "class",
127798
- nullable: type.nullable,
127799
- classId: inferredClassId,
127800
- classArguments: {}
127801
- };
127802
- if (resolvedType === null) {
127803
- throw new Error(
127804
- `Stored Generic constructor argument cannot resolve parameter ${type.genericParamId}.`
127805
- );
127806
- }
127825
+ case "generic":
127807
127826
  return storedConstructorArgumentSource(
127808
127827
  context,
127809
- withSchemaTypeNullability(
127810
- resolvedType,
127811
- type.nullable || resolvedType.nullable
127812
- ),
127828
+ storedGenericArgumentType(context, type, environment),
127813
127829
  value,
127814
127830
  environment,
127815
127831
  visited,
@@ -127817,7 +127833,6 @@ function storedConstructorArgumentSource(context, type, value, environment, visi
127817
127833
  partial,
127818
127834
  constructorOwnsGraph
127819
127835
  );
127820
- }
127821
127836
  case "interface":
127822
127837
  case "list":
127823
127838
  case "dictionary":
@@ -127868,6 +127883,25 @@ function storedConstructorArgumentSource(context, type, value, environment, visi
127868
127883
  );
127869
127884
  }
127870
127885
  }
127886
+ function storedGenericArgumentType(context, type, environment) {
127887
+ const bindingId = environment.get(type.genericParamId);
127888
+ const inferredClassId = inferredGenericClassId(bindingId);
127889
+ const resolvedType = inferredClassId === null ? bindingId === void 0 ? null : schemaTypeForBindingMember(context, bindingId, environment) : {
127890
+ kind: "class",
127891
+ nullable: type.nullable,
127892
+ classId: inferredClassId,
127893
+ classArguments: {}
127894
+ };
127895
+ if (resolvedType === null) {
127896
+ throw new Error(
127897
+ `Stored Generic constructor argument cannot resolve parameter ${type.genericParamId}.`
127898
+ );
127899
+ }
127900
+ return withSchemaTypeNullability(
127901
+ resolvedType,
127902
+ type.nullable || resolvedType.nullable
127903
+ );
127904
+ }
127871
127905
  function schemaTypeForBindingMember(context, memberId, environment, visited = /* @__PURE__ */ new Set()) {
127872
127906
  if (visited.has(memberId)) {
127873
127907
  throw new Error(
@@ -128033,6 +128067,10 @@ function storedAggregateReplaySource(context, type, valueId, environment, constr
128033
128067
  function isSharedConstructorArgument(context, valueId, constructorOwnsGraph) {
128034
128068
  return !constructorOwnsGraph && context.readMaterializedGraph().independentlyOwnedValueIds.has(valueId);
128035
128069
  }
128070
+ function storedTypeArgumentName(context, type, environment) {
128071
+ const settled = type.kind === "generic" ? storedGenericArgumentType(context, type, environment) : type;
128072
+ return `${storedSchemaTypeName(context, settled, environment)}${settled.nullable ? "?" : ""}`;
128073
+ }
128036
128074
  function storedSchemaTypeName(context, type, environment, valueId) {
128037
128075
  switch (type.kind) {
128038
128076
  case "unknown":
@@ -128045,7 +128083,7 @@ function storedSchemaTypeName(context, type, environment, valueId) {
128045
128083
  const argumentsValue = schemaClass2.genericParameters.flatMap(
128046
128084
  (parameter4) => {
128047
128085
  const argument2 = type.classArguments[parameter4.id];
128048
- return argument2 === void 0 ? [] : [storedSchemaTypeName(context, argument2, environment)];
128086
+ return argument2 === void 0 ? [] : [storedTypeArgumentName(context, argument2, environment)];
128049
128087
  }
128050
128088
  );
128051
128089
  return `${schemaClass2.name}${argumentsValue.length === 0 ? "" : `<${argumentsValue.join(", ")}>`}`;
@@ -128073,10 +128111,10 @@ function storedSchemaTypeName(context, type, environment, valueId) {
128073
128111
  return manifestMemberTypeName(context, member);
128074
128112
  }
128075
128113
  case "list":
128076
- return `List<${storedSchemaTypeName(context, type.entryType, environment)}>`;
128114
+ return `List<${storedTypeArgumentName(context, type.entryType, environment)}>`;
128077
128115
  case "dictionary": {
128078
128116
  const keyType = type.keyEnumId === null ? "string" : stringField3(context.enums.get(type.keyEnumId) ?? {}, "name");
128079
- return `Dictionary<${keyType}, ${storedSchemaTypeName(context, type.entryType, environment)}>`;
128117
+ return `Dictionary<${keyType}, ${storedTypeArgumentName(context, type.entryType, environment)}>`;
128080
128118
  }
128081
128119
  case "bool":
128082
128120
  case "int":
@@ -128293,10 +128331,13 @@ function classValueTypeName(context, classId, member, environment) {
128293
128331
  `Stored Class member ${String(member.name ?? member.id)} has no concrete ${parameter4.name} binding.`
128294
128332
  );
128295
128333
  }
128296
- return manifestMemberTypeName(context, bindingMember);
128334
+ return typeArgumentName(context, bindingMember);
128297
128335
  });
128298
128336
  return `${name}<${argumentsValue.join(", ")}>`;
128299
128337
  }
128338
+ function typeArgumentName(context, member) {
128339
+ return `${manifestMemberTypeName(context, member)}${member.required ? "" : "?"}`;
128340
+ }
128300
128341
  function manifestMemberTypeName(context, member) {
128301
128342
  if (member.kind === "class") {
128302
128343
  const schemaClass2 = context.manifestClasses.get(member.classId);
@@ -128317,7 +128358,7 @@ function manifestMemberTypeName(context, member) {
128317
128358
  `Stored generic binding ${binding.memberId} was not pulled.`
128318
128359
  );
128319
128360
  }
128320
- return manifestMemberTypeName(context, argument2);
128361
+ return typeArgumentName(context, argument2);
128321
128362
  });
128322
128363
  return `${schemaClass2.name}<${argumentsValue.join(", ")}>`;
128323
128364
  }
@@ -156165,7 +156206,7 @@ There is no --keep-current: preserving current semantic state defines flatten.
156165
156206
  async function main() {
156166
156207
  const args = parseArgs(process.argv.slice(2));
156167
156208
  if (args.command === "--version") {
156168
- console.log("0.64.1");
156209
+ console.log("0.65.1");
156169
156210
  return;
156170
156211
  }
156171
156212
  if (args.command === null || args.command === "help" || args.command === "--help" || args.command === "-h") {
@@ -6058,6 +6058,14 @@ var WORLD_SYSTEM_CLASS_DEFINITIONS = [
6058
6058
  docsText: "When false, this object and its children are neither rendered nor collided with. The value stays live: member writes still apply and a running animation clip keeps playing. Disabling an object hides its whole subtree regardless of each child's own value, and re-enabling it restores exactly what was there.",
6059
6059
  requirement: 1 /* Required */,
6060
6060
  schemaKey: "Enabled"
6061
+ },
6062
+ {
6063
+ memberId: "system_a0083c92-72f7-405f-8863-ff86f995d36d",
6064
+ memberKind: "class",
6065
+ defaultValue: null,
6066
+ docsText: "The object's box collider, or null for none. Any world object can carry one: placed objects, nested children, and sprites alike.",
6067
+ schemaClassWorldKind: "objectCollider",
6068
+ schemaKey: "Collider"
6061
6069
  }
6062
6070
  ],
6063
6071
  worldKind: "objectBase"
@@ -6267,13 +6275,6 @@ var WORLD_SYSTEM_CLASS_DEFINITIONS = [
6267
6275
  requirement: 1 /* Required */,
6268
6276
  schemaKey: "Children"
6269
6277
  },
6270
- {
6271
- memberId: "system_a0083c92-72f7-405f-8863-ff86f995d36d",
6272
- memberKind: "class",
6273
- defaultValue: null,
6274
- schemaClassWorldKind: "objectCollider",
6275
- schemaKey: "Collider"
6276
- },
6277
6278
  {
6278
6279
  memberId: "system_c609fed3-53e4-5147-b493-e4aa23031d79",
6279
6280
  memberKind: "computed",
@@ -18651,7 +18652,7 @@ function runtimeTypeCheck(value, checkType, ctx, seenCollections = /* @__PURE__
18651
18652
  function runtimeValueMatchesType(value, typeInfo, ctx, seenCollections = /* @__PURE__ */ new Set()) {
18652
18653
  if (typeInfo.type === NS_TYPE_UNKNOWN) return true;
18653
18654
  if (value === null || value === void 0) {
18654
- return typeInfo.type === 0 /* Null */ || !typeInfo.required;
18655
+ return typeInfo.type === 0 /* Null */ || typeInfo.type === 21 /* Generic */ || !typeInfo.required;
18655
18656
  }
18656
18657
  return runtimeTypeCheck(value, typeInfo, ctx, seenCollections);
18657
18658
  }
@@ -24779,7 +24780,7 @@ function resolveMemberDeclaredTypeInfoInternal(member, members, seen) {
24779
24780
  members,
24780
24781
  nextSeen
24781
24782
  );
24782
- return argumentType === null ? [] : [[genericParamId, { ...argumentType, required: true }]];
24783
+ return argumentType === null ? [] : [[genericParamId, argumentType]];
24783
24784
  }
24784
24785
  )
24785
24786
  );
@@ -35277,7 +35278,12 @@ var StrictNeoScriptResolver = class {
35277
35278
  };
35278
35279
  });
35279
35280
  fields.push(
35280
- ...this.resolveCallSiteInitializerFields(schemaClass, expression, scope)
35281
+ ...this.resolveCallSiteInitializerFields(
35282
+ schemaClass,
35283
+ targetType,
35284
+ expression,
35285
+ scope
35286
+ )
35281
35287
  );
35282
35288
  const schemaClassInfo = toWireType(targetType, this.project);
35283
35289
  if (schemaClassInfo.type !== 7 /* Class */) {
@@ -35403,6 +35409,7 @@ var StrictNeoScriptResolver = class {
35403
35409
  ) : [];
35404
35410
  const fields = this.resolveCallSiteInitializerFields(
35405
35411
  schemaClass,
35412
+ targetType,
35406
35413
  expression,
35407
35414
  scope
35408
35415
  );
@@ -35517,7 +35524,7 @@ var StrictNeoScriptResolver = class {
35517
35524
  * the class's own schema exactly as a `classConstructor` field is, and
35518
35525
  * applied after the body so an explicit assignment always wins.
35519
35526
  */
35520
- resolveCallSiteInitializerFields(schemaClass, expression, scope) {
35527
+ resolveCallSiteInitializerFields(schemaClass, targetType, expression, scope) {
35521
35528
  const entries = expression.initializer ?? [];
35522
35529
  const seen = /* @__PURE__ */ new Set();
35523
35530
  return entries.map((entry) => {
@@ -35562,10 +35569,15 @@ var StrictNeoScriptResolver = class {
35562
35569
  entry.pos
35563
35570
  );
35564
35571
  }
35565
- const value = this.resolveExpression(entry.value, scope, member.type);
35566
- if (!isNeoScriptTypeAssignable(value.type, member.type, this.project)) {
35572
+ const memberType = substituteTypeParameters(
35573
+ member.type,
35574
+ targetType,
35575
+ schemaClass
35576
+ );
35577
+ const value = this.resolveExpression(entry.value, scope, memberType);
35578
+ if (!isNeoScriptTypeAssignable(value.type, memberType, this.project)) {
35567
35579
  throw new CompileError(
35568
- `Object initializer '${member.name}' expected ${this.describe(member.type)} but got ${this.describe(value.type)}.`,
35580
+ `Object initializer '${member.name}' expected ${this.describe(memberType)} but got ${this.describe(value.type)}.`,
35569
35581
  entry.pos
35570
35582
  );
35571
35583
  }
@@ -40627,13 +40639,11 @@ function classArguments(member, context, seen, genericEnvironment) {
40627
40639
  if (environmentEntry?.kind === "member") {
40628
40640
  const argument3 = analyzerMemberById(context, environmentEntry.memberId);
40629
40641
  return argument3 ? [
40630
- requiredType(
40631
- memberRuntimeType(
40632
- argument3,
40633
- context,
40634
- new Set(seen),
40635
- genericEnvironment
40636
- )
40642
+ memberRuntimeType(
40643
+ argument3,
40644
+ context,
40645
+ new Set(seen),
40646
+ genericEnvironment
40637
40647
  )
40638
40648
  ] : [];
40639
40649
  }
@@ -40648,13 +40658,11 @@ function classArguments(member, context, seen, genericEnvironment) {
40648
40658
  }
40649
40659
  const argument2 = analyzerMemberById(context, binding.memberId);
40650
40660
  return argument2 ? [
40651
- requiredType(
40652
- memberRuntimeType(
40653
- argument2,
40654
- context,
40655
- new Set(seen),
40656
- genericEnvironment
40657
- )
40661
+ memberRuntimeType(
40662
+ argument2,
40663
+ context,
40664
+ new Set(seen),
40665
+ genericEnvironment
40658
40666
  )
40659
40667
  ] : [];
40660
40668
  });
@@ -40841,11 +40849,7 @@ function genericConstraintLanguageType(constraint, context, genericEnvironment,
40841
40849
  ];
40842
40850
  }
40843
40851
  const member = analyzerMemberById(context, binding.memberId);
40844
- return member ? [
40845
- requiredType(
40846
- memberRuntimeType(member, context, /* @__PURE__ */ new Set(), genericEnvironment)
40847
- )
40848
- ] : [];
40852
+ return member ? [memberRuntimeType(member, context, /* @__PURE__ */ new Set(), genericEnvironment)] : [];
40849
40853
  });
40850
40854
  return {
40851
40855
  kind: "named",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neocompose/cli",
3
- "version": "0.64.1",
3
+ "version": "0.65.1",
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.64.1 -->
12
+ <!-- reviewed-through-cli: 0.65.1 -->
13
13
 
14
14
  # Neo Compose CLI
15
15
 
@@ -388,6 +388,37 @@ frame after a watched dependency changes.
388
388
  When multiple tracks write the same member in one frame, apply list order and
389
389
  let the last write win.
390
390
 
391
+ A track can target a Class member. Each frame writes its whole value, and a
392
+ `null` frame clears the member. Every `NeoObjectBase`, sprites included,
393
+ declares `Collider`, so a per-frame trigger such as an attack hit box is a
394
+ Session `Collider` override on the child plus a track of `NeoCollider?`
395
+ frames:
396
+
397
+ ```neo
398
+ class ColliderAnimationSegment : NeoAnimationSegment<NeoCollider?> {
399
+ }
400
+
401
+ @settings(target: NeoObjectBase.Collider)
402
+ class SwingColliderTrack(NeoDelegate<SwordSprite> selector)
403
+ : NeoAnimationSegmentTrack<SwordSprite, NeoCollider?>(selector: selector) {
404
+ public override ColliderAnimationSegment Segment = new ColliderAnimationSegment() {
405
+ Duration = 3,
406
+ Frames = [
407
+ new NeoAnimationSegmentFrame<NeoCollider?>(value: new NeoCollider() {
408
+ Size = new(1, 1),
409
+ Offset = new(1, 1),
410
+ IsTrigger = true
411
+ }) { Index = 1 },
412
+ new NeoAnimationSegmentFrame<NeoCollider?>(value: null) { Index = 2 }
413
+ ]
414
+ };
415
+ }
416
+ ```
417
+
418
+ The segment's class must fit the target member's class. Like any track target,
419
+ the member must be a writable Save, Session, or Writable leaf on the child, for
420
+ example `@storage(allowed: .Session) public override NeoCollider? Collider;`.
421
+
391
422
  ## NeoFlow dialogues
392
423
 
393
424
  Lookup dialogue groups use the same collection paths as lookup members:
@@ -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.64.1 -->
113
+ <!-- reviewed-through-cli: 0.65.1 -->
114
114
  ```
115
115
 
116
116
  The quoted version above is checked too, so this instruction cannot go stale