@neocompose/cli 0.24.8 → 0.25.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
@@ -1291,6 +1291,7 @@ var init_language_spec = __esm({
1291
1291
  "Partial",
1292
1292
  "FunctionRef",
1293
1293
  "NeoDelegate",
1294
+ "NeoAction",
1294
1295
  "Dialogue",
1295
1296
  "Trigger",
1296
1297
  "Node",
@@ -1958,6 +1959,12 @@ function isNeoScriptTypeAssignable(source, target, project) {
1958
1959
  return expected !== void 0 && isNeoScriptTypeAssignable(parameter4, expected, project) && isNeoScriptTypeAssignable(expected, parameter4, project);
1959
1960
  });
1960
1961
  }
1962
+ if (source.kind === "action" && target.kind === "action") {
1963
+ return source.parameterTypes.length === target.parameterTypes.length && source.parameterTypes.every((parameter4, index) => {
1964
+ const expected = target.parameterTypes[index];
1965
+ return expected !== void 0 && isNeoScriptTypeAssignable(parameter4, expected, project) && isNeoScriptTypeAssignable(expected, parameter4, project);
1966
+ });
1967
+ }
1961
1968
  return false;
1962
1969
  }
1963
1970
  function commonNeoScriptAssignableType(left, right, project) {
@@ -2019,6 +2026,8 @@ function formatType(type, project) {
2019
2026
  (parameter4) => formatType(parameter4, project)
2020
2027
  )
2021
2028
  ].join(", ")}>${suffix}`;
2029
+ case "action":
2030
+ return `NeoAction<${type.parameterTypes.map((parameter4) => formatType(parameter4, project)).join(", ")}>${suffix}`;
2022
2031
  }
2023
2032
  }
2024
2033
  var UNKNOWN_TYPE;
@@ -6919,6 +6928,8 @@ function neoScriptPositionalTypeKey(type) {
6919
6928
  return `typeParameter:${type.id}`;
6920
6929
  case "delegate":
6921
6930
  return `delegate:${neoScriptPositionalTypeKey(type.returnType)}(${type.parameterTypes.map(neoScriptPositionalTypeKey).join(",")})`;
6931
+ case "action":
6932
+ return `action:(${type.parameterTypes.map(neoScriptPositionalTypeKey).join(",")})`;
6922
6933
  }
6923
6934
  }
6924
6935
  function validateNeoScriptOverloadSignatures(className, signatures) {
@@ -7485,8 +7496,34 @@ function typeRefsEqual(left, right) {
7485
7496
  return other !== void 0 && typeRefsEqual(parameter4, other);
7486
7497
  });
7487
7498
  }
7499
+ if (left.kind === "action" && right.kind === "action") {
7500
+ return left.parameterTypes.length === right.parameterTypes.length && left.parameterTypes.every((parameter4, index) => {
7501
+ const other = right.parameterTypes[index];
7502
+ return other !== void 0 && typeRefsEqual(parameter4, other);
7503
+ });
7504
+ }
7488
7505
  return left.kind === "dictionary" && right.kind === "dictionary" ? typeRefsEqual(left.keyType, right.keyType) && typeRefsEqual(left.valueType, right.valueType) : false;
7489
7506
  }
7507
+ function actionListenerSignature(symbol) {
7508
+ if (symbol.kind === "function" || symbol.kind === "method") {
7509
+ return {
7510
+ returnsVoid: isVoid(symbol.returnType ?? symbol.type),
7511
+ parameterTypes: (symbol.parameters ?? []).map(
7512
+ (parameter4) => parameter4.type
7513
+ )
7514
+ };
7515
+ }
7516
+ if (symbol.type.kind === "delegate") {
7517
+ return {
7518
+ returnsVoid: isVoid(symbol.type.returnType),
7519
+ parameterTypes: symbol.type.parameterTypes
7520
+ };
7521
+ }
7522
+ if (symbol.type.kind === "action") {
7523
+ return { returnsVoid: true, parameterTypes: symbol.type.parameterTypes };
7524
+ }
7525
+ return null;
7526
+ }
7490
7527
  function variable(id2, type, pointer, project) {
7491
7528
  return {
7492
7529
  id: id2,
@@ -7867,6 +7904,14 @@ function toWireType(type, project) {
7867
7904
  (parameter4) => toWireType(parameter4, project)
7868
7905
  )
7869
7906
  };
7907
+ case "action":
7908
+ return {
7909
+ type: 26 /* Action */,
7910
+ required: true,
7911
+ argumentTypes: type.parameterTypes.map(
7912
+ (parameter4) => toWireType(parameter4, project)
7913
+ )
7914
+ };
7870
7915
  }
7871
7916
  }
7872
7917
  function fromWireType(type) {
@@ -7921,6 +7966,12 @@ function fromWireType(type) {
7921
7966
  nullable
7922
7967
  };
7923
7968
  }
7969
+ if (type.type === 26 /* Action */) {
7970
+ return {
7971
+ kind: "action",
7972
+ parameterTypes: type.argumentTypes.map(fromWireType)
7973
+ };
7974
+ }
7924
7975
  return { kind: "primitive", name: valueKindPrimitive(type.type), nullable };
7925
7976
  }
7926
7977
  function findGenericParameterOwner(parameterId, project) {
@@ -8149,10 +8200,17 @@ function substituteTypeParameters(member, receiver, declaring) {
8149
8200
  parameterTypes: type.parameterTypes.map(visit)
8150
8201
  };
8151
8202
  }
8203
+ if (type.kind === "action") {
8204
+ return { ...type, parameterTypes: type.parameterTypes.map(visit) };
8205
+ }
8152
8206
  return type;
8153
8207
  };
8154
8208
  return visit(member);
8155
8209
  }
8210
+ function isActionMemberPointer(pointer) {
8211
+ if (pointer.type === "staticMember" /* StaticMember */) return true;
8212
+ return pointer.type === "keyOf" /* KeyOf */;
8213
+ }
8156
8214
  function pointerToBooleanExpression(pointer) {
8157
8215
  if (pointer.type === "operation" /* Operation */ && pointer.operation.type === "boolean" /* Boolean */) {
8158
8216
  return pointer.operation.expression;
@@ -8771,6 +8829,12 @@ var init_strict_resolver = __esm({
8771
8829
  statement.init.pos
8772
8830
  );
8773
8831
  }
8832
+ if (initial.type.kind === "action") {
8833
+ throw new CompileError(
8834
+ `Cannot bind ${this.describe(initial.type)} to local '${statement.name}'; an action is not a value. Subscribe or invoke it through the member reference directly.`,
8835
+ statement.init.pos
8836
+ );
8837
+ }
8774
8838
  const localType = declared ?? initial.type;
8775
8839
  if (declared) {
8776
8840
  this.requireAssignable(
@@ -9003,6 +9067,12 @@ var init_strict_resolver = __esm({
9003
9067
  statement.iterator,
9004
9068
  iteratorScope
9005
9069
  );
9070
+ if (iterator.type !== "assign" /* Assign */) {
9071
+ throw new CompileError(
9072
+ "A for-loop iterator must be an assignment; an NSAction subscription is not a valid iterator.",
9073
+ statement.iterator.pos
9074
+ );
9075
+ }
9006
9076
  const initialConditionFalseScope = new Scope(loopScope);
9007
9077
  this.applyFactsToScope(loopScope, initialConditionFalseScope, [
9008
9078
  ...factsWhenFalse(statement.condition)
@@ -9593,6 +9663,15 @@ var init_strict_resolver = __esm({
9593
9663
  scope,
9594
9664
  false
9595
9665
  );
9666
+ if (target.type.kind === "action") {
9667
+ return this.resolveActionSubscription(
9668
+ statement,
9669
+ target,
9670
+ target.type,
9671
+ writability,
9672
+ scope
9673
+ );
9674
+ }
9596
9675
  let value;
9597
9676
  if (statement.op === "=") {
9598
9677
  if (!statement.value) {
@@ -9675,6 +9754,189 @@ var init_strict_resolver = __esm({
9675
9754
  pos
9676
9755
  );
9677
9756
  }
9757
+ /**
9758
+ * P62 §3.2. `+=` / `-=` on an NSAction member are subscription operators,
9759
+ * not arithmetic: the listener set *is* the member's value, so each one
9760
+ * compiles to a member-value write carrying the listener target.
9761
+ */
9762
+ resolveActionSubscription(statement, target, actionType, writability, scope) {
9763
+ if (statement.op === "=") {
9764
+ throw new CompileError(
9765
+ `Cannot assign to ${this.describe(actionType)}; a listener set is not reassignable from script. Use '+=' to add a listener or '-=' to remove one.`,
9766
+ statement.pos
9767
+ );
9768
+ }
9769
+ if (statement.op !== "+=" && statement.op !== "-=") {
9770
+ throw new CompileError(
9771
+ `Operator '${statement.op}' cannot be applied to ${this.describe(actionType)}; an action supports only '+=' and '-='.`,
9772
+ statement.pos
9773
+ );
9774
+ }
9775
+ if (!isActionMemberPointer(target.pointer)) {
9776
+ throw new CompileError(
9777
+ `Cannot apply '${statement.op}' to ${this.describe(actionType)} that is not a member reference; an action is not a value. Subscribe or invoke it through the member reference directly.`,
9778
+ statement.pos
9779
+ );
9780
+ }
9781
+ if (!statement.value) {
9782
+ throw new CompileError(
9783
+ `Assignment operator '${statement.op}' requires a right-hand listener.`,
9784
+ statement.pos
9785
+ );
9786
+ }
9787
+ const listener = this.resolveActionListener(
9788
+ statement.value,
9789
+ actionType,
9790
+ scope
9791
+ );
9792
+ const assignedPath = canonicalPath(statement.target);
9793
+ if (assignedPath) {
9794
+ const rootEntry = scope.lookup(pathRoot(assignedPath));
9795
+ if (rootEntry) scope.invalidate(rootEntry);
9796
+ }
9797
+ const kind = statement.op === "+=" ? "addActionListener" /* AddActionListener */ : "removeActionListener" /* RemoveActionListener */;
9798
+ return {
9799
+ type: kind,
9800
+ target: {
9801
+ pointer: target.pointer,
9802
+ typeInfo: target.wireType ?? toWireType(actionType, this.project),
9803
+ writability
9804
+ },
9805
+ listener
9806
+ };
9807
+ }
9808
+ /**
9809
+ * Lowers a `+=` / `-=` right-hand side to the member target a listener is
9810
+ * (P62 §3.2). A listener is always a member reference, so the accepted forms
9811
+ * are exactly the ones the authored listener list accepts: a method group or
9812
+ * a delegate/action-typed member on the current class, or the same through a
9813
+ * value alias or a static type name.
9814
+ */
9815
+ resolveActionListener(expression, expected, scope) {
9816
+ if (expression.kind === "lambda") {
9817
+ throw new CompileError(
9818
+ `A lambda cannot be an action listener: a closure has no identity to deduplicate or remove by. Extract the body to an NSFunction member and subscribe that member instead.`,
9819
+ expression.pos
9820
+ );
9821
+ }
9822
+ const resolved = this.resolveActionListenerSymbol(expression, scope);
9823
+ if (resolved === null) {
9824
+ throw new CompileError(
9825
+ `An action listener must be a Function, NSFunction, NeoDelegate, or NeoAction member reference.`,
9826
+ expression.pos
9827
+ );
9828
+ }
9829
+ const { symbol, owner, receiverType, valueId } = resolved;
9830
+ if (owner !== void 0) {
9831
+ this.assertMemberAccessible(owner, symbol, expression.pos);
9832
+ }
9833
+ if (symbol.deferred === true) {
9834
+ throw new CompileError(
9835
+ `Deferred member '${symbol.name}' cannot be an action listener; listeners run immediately.`,
9836
+ expression.pos
9837
+ );
9838
+ }
9839
+ this.requireActionListenerSignature(
9840
+ symbol,
9841
+ owner,
9842
+ receiverType,
9843
+ expected,
9844
+ expression.pos
9845
+ );
9846
+ return {
9847
+ type: "value" /* Value */,
9848
+ value: {
9849
+ typeInfo: toWireType(expected, this.project),
9850
+ value: { memberId: symbol.id, valueId }
9851
+ }
9852
+ };
9853
+ }
9854
+ /**
9855
+ * Resolves the member a listener expression names, along with the row the
9856
+ * target binds to. `valueId` is `null` for a member on the current class or
9857
+ * a static member — the same identity the authored listener list lowers to
9858
+ * — and a value alias contributes its own stable row.
9859
+ */
9860
+ resolveActionListenerSymbol(expression, scope) {
9861
+ if (expression.kind === "ident") {
9862
+ const implicit = this.implicitMember(expression.name);
9863
+ if (implicit === null) return null;
9864
+ return {
9865
+ symbol: implicit.symbol,
9866
+ owner: implicit.owner,
9867
+ receiverType: this.context.thisClass,
9868
+ valueId: null
9869
+ };
9870
+ }
9871
+ if (expression.kind !== "member") return null;
9872
+ if (expression.receiver.kind === "ident" && !scope.lookup(expression.receiver.name)) {
9873
+ const owner2 = this.project.typeByName.get(expression.receiver.name);
9874
+ const symbol2 = owner2?.members.find(
9875
+ (candidate) => candidate.name === expression.name
9876
+ );
9877
+ if (owner2 === void 0 || symbol2 === void 0) return null;
9878
+ if (symbol2.static !== true) {
9879
+ throw new CompileError(
9880
+ `Member '${owner2.name}.${symbol2.name}' requires an instance receiver.`,
9881
+ expression.pos
9882
+ );
9883
+ }
9884
+ return { symbol: symbol2, owner: owner2, receiverType: void 0, valueId: null };
9885
+ }
9886
+ const receiver = this.resolveExpression(expression.receiver, scope);
9887
+ if (receiver.type.kind !== "named") return null;
9888
+ const owner = this.project.typeById.get(receiver.type.typeId);
9889
+ const symbol = owner?.members.find(
9890
+ (candidate) => candidate.name === expression.name
9891
+ );
9892
+ if (owner === void 0 || symbol === void 0) return null;
9893
+ if (expression.receiver.kind === "ident" && expression.receiver.name === "this") {
9894
+ return { symbol, owner, receiverType: receiver.type, valueId: null };
9895
+ }
9896
+ if (receiver.pointer.type !== "reference" /* Reference */) {
9897
+ throw new CompileError(
9898
+ `Listener '${symbol.name}' cannot be subscribed through this receiver: a listener target is a stable (member, row) identity, so the receiver must be \`this\`, a project value, or a type name.`,
9899
+ expression.pos
9900
+ );
9901
+ }
9902
+ return {
9903
+ symbol,
9904
+ owner,
9905
+ receiverType: receiver.type,
9906
+ valueId: receiver.pointer.valueId
9907
+ };
9908
+ }
9909
+ /**
9910
+ * P62 §2.1. A listener's effective signature must be void-returning with
9911
+ * pairwise-identical post-substitution parameter types — the method-group
9912
+ * discipline with void-ness fixed true. A delegate or action member is
9913
+ * accepted by the signature it carries: subscribing it is an indirection
9914
+ * through the member, not a snapshot of its current value.
9915
+ */
9916
+ requireActionListenerSignature(symbol, owner, receiverType, expected, pos) {
9917
+ const substitute2 = (type) => {
9918
+ if (receiverType?.kind !== "named" || owner === void 0) return type;
9919
+ return substituteTypeParameters(type, receiverType, owner);
9920
+ };
9921
+ const signature = actionListenerSignature(symbol);
9922
+ if (signature === null) {
9923
+ throw new CompileError(
9924
+ `'${symbol.name}' is not a callable member, so it cannot be an action listener.`,
9925
+ pos
9926
+ );
9927
+ }
9928
+ const parameterTypes = signature.parameterTypes.map(substitute2);
9929
+ const matches = signature.returnsVoid && parameterTypes.length === expected.parameterTypes.length && parameterTypes.every((parameter4, index) => {
9930
+ const slot = expected.parameterTypes[index];
9931
+ return slot !== void 0 && isNeoScriptTypeAssignable(parameter4, slot, this.project) && isNeoScriptTypeAssignable(slot, parameter4, this.project);
9932
+ });
9933
+ if (!matches) {
9934
+ throw new CompileError(
9935
+ `Listener '${symbol.name}' is not assignable to ${this.describe(expected)}.`,
9936
+ pos
9937
+ );
9938
+ }
9939
+ }
9678
9940
  resolveExpression(expression, scope, expected) {
9679
9941
  if (expected?.kind === "delegate") {
9680
9942
  if (expression.kind === "lambda") {
@@ -9687,6 +9949,12 @@ var init_strict_resolver = __esm({
9687
9949
  );
9688
9950
  if (methodGroup !== null) return methodGroup;
9689
9951
  }
9952
+ if (expected?.kind === "action" && expression.kind === "lambda") {
9953
+ throw new CompileError(
9954
+ "A lambda cannot be an action listener: a closure has no identity to deduplicate or remove by. Extract the body to an NSFunction member and subscribe that member instead.",
9955
+ expression.pos
9956
+ );
9957
+ }
9690
9958
  switch (expression.kind) {
9691
9959
  case "litNull":
9692
9960
  return literal(
@@ -11032,6 +11300,13 @@ var init_strict_resolver = __esm({
11032
11300
  callee.kind === "member" && callee.optional === true
11033
11301
  );
11034
11302
  }
11303
+ const action = this.resolveActionCallee(callee, scope);
11304
+ if (action !== null) {
11305
+ throw new CompileError(
11306
+ `${this.describe(action.type)} is void and cannot be used as a value; call it as a statement instead.`,
11307
+ pos
11308
+ );
11309
+ }
11035
11310
  if (callee.kind === "ident") {
11036
11311
  const vector = vectorKind(callee.name);
11037
11312
  if (vector)
@@ -11254,6 +11529,37 @@ var init_strict_resolver = __esm({
11254
11529
  throw error;
11255
11530
  }
11256
11531
  }
11532
+ /**
11533
+ * The action-typed sibling of {@link resolveDelegateCallee}. Kept separate
11534
+ * so an action never leaks into the delegate call path: an action is
11535
+ * statement-only, and its two call sites want opposite outcomes — the
11536
+ * statement path compiles it, the expression path rejects it.
11537
+ */
11538
+ resolveActionCallee(callee, scope) {
11539
+ if (callee.kind === "ident") {
11540
+ const scoped = scope.lookup(callee.name);
11541
+ const implicit = this.implicitMember(callee.name);
11542
+ if (scoped === null && (implicit === null || implicit.symbol.kind === "function" || implicit.symbol.kind === "method")) {
11543
+ return null;
11544
+ }
11545
+ const resolved = this.resolveIdentifier(callee.name, scope, callee.pos);
11546
+ return resolved.type.kind === "action" ? resolved : null;
11547
+ }
11548
+ if (callee.kind !== "member") return null;
11549
+ try {
11550
+ const resolved = this.resolveMember(
11551
+ callee.receiver,
11552
+ callee.name,
11553
+ scope,
11554
+ callee.pos,
11555
+ callee.optional === true
11556
+ );
11557
+ return resolved.type.kind === "action" ? resolved : null;
11558
+ } catch (error) {
11559
+ if (error instanceof CompileError) return null;
11560
+ throw error;
11561
+ }
11562
+ }
11257
11563
  resolveDelegateCall(delegate, args, scope, pos, optional) {
11258
11564
  if (delegate.type.kind !== "delegate") {
11259
11565
  throw new Error("Delegate call received a non-delegate type.");
@@ -12448,8 +12754,48 @@ var init_strict_resolver = __esm({
12448
12754
  writeRoot: inferredWriteRoot ?? "unknown"
12449
12755
  };
12450
12756
  }
12757
+ /**
12758
+ * P62 §3.1. Statement-position invoke: every listener fires in stored order
12759
+ * and an empty listener set is a successful no-op, so there is nothing to
12760
+ * return and no optional form to carry.
12761
+ */
12762
+ resolveActionCall(action, actionType, expression, scope) {
12763
+ if (expression.callee.kind === "member" && expression.callee.optional) {
12764
+ throw new CompileError(
12765
+ `${this.describe(actionType)} is never null, so optional-chained invocation is not supported.`,
12766
+ expression.pos
12767
+ );
12768
+ }
12769
+ requireArgCount(
12770
+ "Action",
12771
+ expression.args,
12772
+ actionType.parameterTypes.length,
12773
+ expression.pos
12774
+ );
12775
+ const args = expression.args.map((argument2, index) => {
12776
+ const expected = requiredAt(actionType.parameterTypes, index);
12777
+ const resolved = this.resolveExpression(argument2, scope, expected);
12778
+ this.requireAssignable(
12779
+ resolved.type,
12780
+ expected,
12781
+ argument2.pos,
12782
+ `action argument ${index + 1}`
12783
+ );
12784
+ return resolved.pointer;
12785
+ });
12786
+ return {
12787
+ type: "callAction" /* CallAction */,
12788
+ action: action.pointer,
12789
+ args,
12790
+ callSiteId: this.nextCallSite(expression.pos)
12791
+ };
12792
+ }
12451
12793
  resolveFunctionStatement(expression, scope) {
12452
12794
  if (expression.kind !== "call") return null;
12795
+ const action = this.resolveActionCallee(expression.callee, scope);
12796
+ if (action?.type.kind === "action") {
12797
+ return this.resolveActionCall(action, action.type, expression, scope);
12798
+ }
12453
12799
  const delegate = this.resolveDelegateCallee(expression.callee, scope);
12454
12800
  if (delegate?.type.kind === "delegate") {
12455
12801
  const delegateType = delegate.type;
@@ -21797,6 +22143,9 @@ function substituteInheritedSourceMember(member, baseInfo, resolvedBaseType) {
21797
22143
  parameterTypes: type.parameterTypes.map(substitute2)
21798
22144
  };
21799
22145
  }
22146
+ if (type.kind === "action") {
22147
+ return { ...type, parameterTypes: type.parameterTypes.map(substitute2) };
22148
+ }
21800
22149
  return type;
21801
22150
  };
21802
22151
  return {
@@ -21876,6 +22225,14 @@ function sourceTypeRef(type, owner, typeIds) {
21876
22225
  ...nullable ? { nullable } : {}
21877
22226
  };
21878
22227
  }
22228
+ if (type.name === "NeoAction") {
22229
+ return {
22230
+ kind: "action",
22231
+ parameterTypes: type.typeArguments.map(
22232
+ (argument2) => sourceTypeRef(argument2, owner, typeIds)
22233
+ )
22234
+ };
22235
+ }
21879
22236
  if (type.name === "Reference" && type.typeArguments[0]) {
21880
22237
  return {
21881
22238
  ...sourceTypeRef(type.typeArguments[0], owner, typeIds),
@@ -22606,6 +22963,27 @@ function validateTypeApplication(uri, type, genericParameters, environment, diag
22606
22963
  }
22607
22964
  return;
22608
22965
  }
22966
+ if (type.name === "NeoAction") {
22967
+ if (type.typeArguments.length > 16) {
22968
+ diagnose(
22969
+ diagnostics,
22970
+ uri,
22971
+ type.range,
22972
+ "generic-argument-arity",
22973
+ `Type 'NeoAction' expects up to 16 parameter types, but ${type.typeArguments.length} type arguments were provided.`
22974
+ );
22975
+ }
22976
+ if (type.nullable) {
22977
+ diagnose(
22978
+ diagnostics,
22979
+ uri,
22980
+ type.range,
22981
+ "action-nullable",
22982
+ "Type 'NeoAction' cannot be nullable; an action's rest state is an empty listener set, not null."
22983
+ );
22984
+ }
22985
+ return;
22986
+ }
22609
22987
  const expected = target?.declaration.kind === "class" ? target.declaration.genericParameters.length : BUILTIN_ARITY.get(type.name) ?? 0;
22610
22988
  if (!target && !BUILTIN_TYPES.has(type.name)) return;
22611
22989
  if (type.typeArguments.length !== expected) {
@@ -22653,6 +23031,16 @@ function validateTypeApplication(uri, type, genericParameters, environment, diag
22653
23031
  });
22654
23032
  }
22655
23033
  function validateMemberTypeKind(uri, type, context, name, diagnostics) {
23034
+ if (type.name === "NeoAction" && context === "return") {
23035
+ diagnose(
23036
+ diagnostics,
23037
+ uri,
23038
+ type.range,
23039
+ "invalid-member-type",
23040
+ `Function '${name}' cannot return 'NeoAction'; an action is invoked as a statement, never used as a value.`
23041
+ );
23042
+ return;
23043
+ }
22656
23044
  if (context === "return") return;
22657
23045
  if (type.name !== "void" && type.name !== "null") return;
22658
23046
  diagnose(
@@ -30888,6 +31276,24 @@ function memberFromDocument(record3, members, owners, classNames, context) {
30888
31276
  )
30889
31277
  };
30890
31278
  }
31279
+ if (kind === MEMBER_KIND.NSAction) {
31280
+ if (common.required === true) {
31281
+ throw invalidDocument(
31282
+ record3,
31283
+ "required",
31284
+ "an NSAction member is never nullable"
31285
+ );
31286
+ }
31287
+ return {
31288
+ ...common,
31289
+ kind: "action",
31290
+ arguments: documentArgumentsToManifest(
31291
+ field("argumentTypes", []),
31292
+ record3,
31293
+ source.span
31294
+ )
31295
+ };
31296
+ }
30891
31297
  if (kind === MEMBER_KIND.Generic) {
30892
31298
  return {
30893
31299
  ...common,
@@ -31033,6 +31439,12 @@ function memberToDocumentFields(member, baseData3) {
31033
31439
  ...manifestTypeToDocument(argument2.type)
31034
31440
  }));
31035
31441
  }
31442
+ if (member.kind === "action") {
31443
+ fields.argumentTypes = member.arguments.map((argument2) => ({
31444
+ name: argument2.name,
31445
+ ...manifestTypeToDocument(argument2.type)
31446
+ }));
31447
+ }
31036
31448
  if (member.kind === "generic") {
31037
31449
  fields.genericParamId = member.genericParamId;
31038
31450
  if (member.partial) fields.partial = true;
@@ -31248,6 +31660,26 @@ function documentTypeToManifest(value, record3, path) {
31248
31660
  )
31249
31661
  };
31250
31662
  }
31663
+ if (kind === MEMBER_KIND.NSAction) {
31664
+ if (nullable) {
31665
+ throw invalidDocument(
31666
+ record3,
31667
+ `${path}.required`,
31668
+ "an NeoAction type is never nullable"
31669
+ );
31670
+ }
31671
+ return {
31672
+ kind: "action",
31673
+ nullable: false,
31674
+ argumentTypes: optionalArray(typeInfo.argumentTypes).map(
31675
+ (argument2, index) => documentTypeToManifest(
31676
+ argument2,
31677
+ record3,
31678
+ `${path}.argumentTypes.${index}`
31679
+ )
31680
+ )
31681
+ };
31682
+ }
31251
31683
  throw invalidDocument(
31252
31684
  record3,
31253
31685
  path,
@@ -31319,6 +31751,13 @@ function manifestTypeToDocument(type) {
31319
31751
  argumentTypes: type.argumentTypes.map(manifestTypeToDocument)
31320
31752
  };
31321
31753
  }
31754
+ if (type.kind === "action") {
31755
+ return {
31756
+ type: MEMBER_KIND.NSAction,
31757
+ required: true,
31758
+ argumentTypes: type.argumentTypes.map(manifestTypeToDocument)
31759
+ };
31760
+ }
31322
31761
  if (type.kind !== "lookup") {
31323
31762
  throw new Error(`No document type-info mapping for ${type.kind}.`);
31324
31763
  }
@@ -31711,7 +32150,8 @@ var init_codecs = __esm({
31711
32150
  Interface: 22,
31712
32151
  NSFunction: 23,
31713
32152
  FunctionRef: 24,
31714
- NSDelegate: 25
32153
+ NSDelegate: 25,
32154
+ NSAction: 26
31715
32155
  };
31716
32156
  TYPE_KIND_BY_MEMBER_KIND = /* @__PURE__ */ new Map([
31717
32157
  [MEMBER_KIND.Null, "null"],
@@ -31763,7 +32203,8 @@ var init_codecs = __esm({
31763
32203
  ["generic", MEMBER_KIND.Generic],
31764
32204
  ["scriptFunction", MEMBER_KIND.NSFunction],
31765
32205
  ["functionRef", MEMBER_KIND.FunctionRef],
31766
- ["delegate", MEMBER_KIND.NSDelegate]
32206
+ ["delegate", MEMBER_KIND.NSDelegate],
32207
+ ["action", MEMBER_KIND.NSAction]
31767
32208
  ]);
31768
32209
  TYPE_MEMBER_KIND_BY_KIND = /* @__PURE__ */ new Map([
31769
32210
  ["null", MEMBER_KIND.Null],
@@ -33967,6 +34408,16 @@ function assertMember2(value, path) {
33967
34408
  }
33968
34409
  return;
33969
34410
  }
34411
+ if (kind === "action") {
34412
+ arrayOf(member.arguments, `${path}.arguments`, assertFunctionArgument);
34413
+ if (Array.isArray(member.arguments) && member.arguments.length > 16) {
34414
+ invalid(`${path}.arguments`, "action arity cannot exceed 16.");
34415
+ }
34416
+ if (member.required !== false) {
34417
+ invalid(`${path}.required`, "an action member is never nullable.");
34418
+ }
34419
+ return;
34420
+ }
33970
34421
  if (kind === "generic") {
33971
34422
  nonEmptyString(member.genericParamId, `${path}.genericParamId`);
33972
34423
  if (member.partial !== void 0) {
@@ -34289,6 +34740,19 @@ function assertType2(value, path, depth = 0) {
34289
34740
  }
34290
34741
  return;
34291
34742
  }
34743
+ if (kind === "action") {
34744
+ knownKeys(type, path, ["kind", "nullable", "argumentTypes"]);
34745
+ exactValue(type.nullable, false, `${path}.nullable`);
34746
+ arrayOf(
34747
+ type.argumentTypes,
34748
+ `${path}.argumentTypes`,
34749
+ (entry, entryPath) => assertType2(entry, entryPath, depth + 1)
34750
+ );
34751
+ if (Array.isArray(type.argumentTypes) && type.argumentTypes.length > 16) {
34752
+ invalid(`${path}.argumentTypes`, "action arity cannot exceed 16.");
34753
+ }
34754
+ return;
34755
+ }
34292
34756
  invalid(`${path}.kind`, `unsupported schema type ${JSON.stringify(kind)}.`);
34293
34757
  }
34294
34758
  function assertInterface(value, path) {
@@ -35191,7 +35655,10 @@ var init_validate = __esm({
35191
35655
  scriptFunction: ["returnType", "arguments", "deferred", "script"],
35192
35656
  generic: ["genericParamId"],
35193
35657
  functionRef: [],
35194
- delegate: ["returnType", "arguments"]
35658
+ delegate: ["returnType", "arguments"],
35659
+ // P62 §2.1: an action is structurally void, so `arguments` is the only
35660
+ // kind-specific key it carries.
35661
+ action: ["arguments"]
35195
35662
  };
35196
35663
  PRIMITIVE_TYPE_KINDS = /* @__PURE__ */ new Set([
35197
35664
  "null",
@@ -36255,6 +36722,23 @@ function isNSTypeInfoDelegateInternal(value, ancestors) {
36255
36722
  ancestors.delete(delegate);
36256
36723
  }
36257
36724
  }
36725
+ function isNSTypeInfoActionInternal(value, ancestors) {
36726
+ if (!isNSTypeInfoBase(value) || value.type !== 26 /* NSAction */) {
36727
+ return false;
36728
+ }
36729
+ const action = value;
36730
+ if (ancestors.has(action)) return false;
36731
+ ancestors.add(action);
36732
+ try {
36733
+ if (!Array.isArray(action.argumentTypes)) return false;
36734
+ if (action.argumentTypes.length > 16) return false;
36735
+ return action.argumentTypes.every(
36736
+ (argument2) => isNSTypeInfoInternal(argument2, ancestors)
36737
+ );
36738
+ } finally {
36739
+ ancestors.delete(action);
36740
+ }
36741
+ }
36258
36742
  function isNSTypeInfoCollection(value) {
36259
36743
  return isNSTypeInfoCollectionInternal(value, /* @__PURE__ */ new Set());
36260
36744
  }
@@ -36311,7 +36795,7 @@ function isNSTypeInfo(value) {
36311
36795
  return isNSTypeInfoInternal(value, /* @__PURE__ */ new Set());
36312
36796
  }
36313
36797
  function isNSTypeInfoInternal(value, ancestors) {
36314
- return isNSTypeInfoUnknown(value) || isNSTypeInfoPrimitive(value) || isNSTypeInfoAsset(value) || isNSTypeInfoVector(value) || isNSTypeInfoColor(value) || isNSTypeInfoDialogueLookup(value) || isNSTypeInfoClassInternal(value, ancestors) || isNSTypeInfoInterface(value) || isNSTypeInfoGenericParam(value) || isNSTypeInfoDelegateInternal(value, ancestors) || isNSTypeInfoCollectionInternal(value, ancestors) || isNSTypeInfoLookupInternal(value, ancestors) || isNSTypeInfoEnum(value);
36798
+ return isNSTypeInfoUnknown(value) || isNSTypeInfoPrimitive(value) || isNSTypeInfoAsset(value) || isNSTypeInfoVector(value) || isNSTypeInfoColor(value) || isNSTypeInfoDialogueLookup(value) || isNSTypeInfoClassInternal(value, ancestors) || isNSTypeInfoInterface(value) || isNSTypeInfoGenericParam(value) || isNSTypeInfoDelegateInternal(value, ancestors) || isNSTypeInfoActionInternal(value, ancestors) || isNSTypeInfoCollectionInternal(value, ancestors) || isNSTypeInfoLookupInternal(value, ancestors) || isNSTypeInfoEnum(value);
36315
36799
  }
36316
36800
  function isNSArgumentTypeInfo(value) {
36317
36801
  return isRequiredType(value, isNSArgumentTypeInfoUnsafe);
@@ -36338,6 +36822,11 @@ function typeInfoContainsUnknown(value, ancestors) {
36338
36822
  (argument2) => typeInfoContainsUnknown(argument2, ancestors)
36339
36823
  );
36340
36824
  }
36825
+ if (value.type === 26 /* NSAction */) {
36826
+ return value.argumentTypes.some(
36827
+ (argument2) => typeInfoContainsUnknown(argument2, ancestors)
36828
+ );
36829
+ }
36341
36830
  if (value.type !== 7 /* Class */) return false;
36342
36831
  return Object.values(value.typeArguments ?? {}).some(
36343
36832
  (argument2) => typeInfoContainsUnknown(argument2, ancestors)
@@ -36488,6 +36977,16 @@ function isNSPointerCallDelegate(value) {
36488
36977
  }
36489
36978
  return v.optional === void 0 || typeof v.optional === "boolean";
36490
36979
  }
36980
+ function isNSPointerCallAction(value) {
36981
+ const v = value;
36982
+ if (v?.type !== "callAction" /* callAction */) return false;
36983
+ if (!isNSPointer(v.action)) return false;
36984
+ if (!Array.isArray(v.args) || !v.args.every(isNSPointer)) return false;
36985
+ if (typeof v.callSiteId !== "string" || v.callSiteId.length === 0) {
36986
+ return false;
36987
+ }
36988
+ return v.optional === void 0;
36989
+ }
36491
36990
  function isNSCallReceiver(value) {
36492
36991
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
36493
36992
  return false;
@@ -36506,7 +37005,7 @@ function isNSPointerFunctionErrorCheck(value) {
36506
37005
  return isNSFunctionErrorCheckMode(v.mode);
36507
37006
  }
36508
37007
  function isNSPointer(value) {
36509
- return isNSPointerReference(value) || isNSPointerVariable(value) || isNSPointerStaticMember(value) || isNSPointerValue(value) || isNSPointerOperation(value) || isNSPointerFunction(value) || isNSPointerKeyOf(value) || isNSPointerListLiteral(value) || isNSPointerDictLiteral(value) || isNSPointerForceUnwrap(value) || isNSPointerIsCheck(value) || isNSPointerCallGetter(value) || isNSPointerCoalesce(value) || isNSPointerToBool(value) || isNSPointerStringify(value) || isNSPointerCallFunction(value) || isNSPointerCallDelegate(value) || isNSPointerFunctionErrorCheck(value);
37008
+ return isNSPointerReference(value) || isNSPointerVariable(value) || isNSPointerStaticMember(value) || isNSPointerValue(value) || isNSPointerOperation(value) || isNSPointerFunction(value) || isNSPointerKeyOf(value) || isNSPointerListLiteral(value) || isNSPointerDictLiteral(value) || isNSPointerForceUnwrap(value) || isNSPointerIsCheck(value) || isNSPointerCallGetter(value) || isNSPointerCoalesce(value) || isNSPointerToBool(value) || isNSPointerStringify(value) || isNSPointerCallFunction(value) || isNSPointerCallDelegate(value) || isNSPointerCallAction(value) || isNSPointerFunctionErrorCheck(value);
36510
37009
  }
36511
37010
  function isNSPointers(value) {
36512
37011
  return Array.isArray(value) && value.every(isNSPointer);
@@ -36589,6 +37088,12 @@ function isNSFunctionWithReturnType(value) {
36589
37088
  if (nsInstructionsContainType(v.instructions, "try" /* try */) && (v.compilerRevision ?? 1) < 6) {
36590
37089
  return false;
36591
37090
  }
37091
+ if ((v.compilerRevision ?? 1) < 8 && ([
37092
+ "addActionListener" /* addActionListener */,
37093
+ "removeActionListener" /* removeActionListener */
37094
+ ].some((type) => nsInstructionsContainType(v.instructions ?? [], type)) || nsIRContainsPointerType(v.instructions, "callAction" /* callAction */))) {
37095
+ return false;
37096
+ }
36592
37097
  if (!isNSTypeInfo(v.typeInfo)) return false;
36593
37098
  return true;
36594
37099
  }
@@ -36814,18 +37319,39 @@ function isNSInstructionThrow(value) {
36814
37319
  if (v?.type !== "throw" /* throw */) return false;
36815
37320
  return isNSPointer(v.pointer);
36816
37321
  }
37322
+ function isNSWriteTarget(value) {
37323
+ const target = value;
37324
+ if (!target) return false;
37325
+ if (!isNSPointer(target.pointer)) return false;
37326
+ if (!isNSTypeInfo(target.typeInfo)) return false;
37327
+ if (target.writability === void 0) return true;
37328
+ return Object.values(NSWritability).includes(target.writability);
37329
+ }
36817
37330
  function isNSInstructionAssign(value) {
36818
37331
  const v = value;
36819
37332
  if (v?.type !== "assign" /* assign */) return false;
36820
37333
  if (v.operator === void 0) return false;
36821
37334
  if (!Object.values(NSAssignmentOperator).includes(v.operator)) return false;
36822
37335
  if (!isNSPointer(v.pointer)) return false;
36823
- const target = v.target;
36824
- if (!target) return false;
36825
- if (!isNSPointer(target.pointer)) return false;
36826
- if (!isNSTypeInfo(target.typeInfo)) return false;
36827
- if (target.writability === void 0) return true;
36828
- return Object.values(NSWritability).includes(target.writability);
37336
+ return isNSWriteTarget(v.target);
37337
+ }
37338
+ function isNSInstructionActionListener(value, type) {
37339
+ const v = value;
37340
+ if (v?.type !== type) return false;
37341
+ if (!isNSWriteTarget(v.target)) return false;
37342
+ return isNSPointer(v.listener);
37343
+ }
37344
+ function isNSInstructionAddActionListener(value) {
37345
+ return isNSInstructionActionListener(
37346
+ value,
37347
+ "addActionListener" /* addActionListener */
37348
+ );
37349
+ }
37350
+ function isNSInstructionRemoveActionListener(value) {
37351
+ return isNSInstructionActionListener(
37352
+ value,
37353
+ "removeActionListener" /* removeActionListener */
37354
+ );
36829
37355
  }
36830
37356
  function isNSInstructionCollectionCall(value) {
36831
37357
  const v = value;
@@ -36834,17 +37360,14 @@ function isNSInstructionCollectionCall(value) {
36834
37360
  if (!Object.values(NSCollectionMutation).includes(v.mutation)) return false;
36835
37361
  if (!Array.isArray(v.args)) return false;
36836
37362
  if (!v.args.every(isNSPointer)) return false;
36837
- const target = v.target;
36838
- if (!target) return false;
36839
- if (!isNSPointer(target.pointer)) return false;
36840
- if (!isNSTypeInfo(target.typeInfo)) return false;
36841
- if (target.writability === void 0) return true;
36842
- return Object.values(NSWritability).includes(target.writability);
37363
+ return isNSWriteTarget(v.target);
36843
37364
  }
36844
37365
  function isNSInstructionFunctionCall(value) {
36845
37366
  const v = value;
36846
37367
  if (v?.type !== "functionCall" /* functionCall */) return false;
36847
- return isNSPointerCallFunction(v.call) || isNSPointerCallDelegate(v.call);
37368
+ if (isNSPointerCallFunction(v.call)) return true;
37369
+ if (isNSPointerCallDelegate(v.call)) return true;
37370
+ return isNSPointerCallAction(v.call);
36848
37371
  }
36849
37372
  function isNSLoopBinding(value) {
36850
37373
  const binding = value;
@@ -36974,7 +37497,7 @@ function isNSInstructionTry(value) {
36974
37497
  return true;
36975
37498
  }
36976
37499
  function isNSInstruction(value) {
36977
- return isNSInstructionVariable(value) || isNSInstructionIfBranch(value) || isNSInstructionReturn(value) || isNSInstructionThrow(value) || isNSInstructionAssign(value) || isNSInstructionCollectionCall(value) || isNSInstructionFunctionCall(value) || isNSInstructionFor(value) || isNSInstructionForEach(value) || isNSInstructionBreak(value) || isNSInstructionContinue(value) || isNSInstructionSwitch(value) || isNSInstructionTry(value);
37500
+ return isNSInstructionVariable(value) || isNSInstructionIfBranch(value) || isNSInstructionReturn(value) || isNSInstructionThrow(value) || isNSInstructionAssign(value) || isNSInstructionCollectionCall(value) || isNSInstructionFunctionCall(value) || isNSInstructionFor(value) || isNSInstructionForEach(value) || isNSInstructionBreak(value) || isNSInstructionContinue(value) || isNSInstructionSwitch(value) || isNSInstructionTry(value) || isNSInstructionAddActionListener(value) || isNSInstructionRemoveActionListener(value);
36978
37501
  }
36979
37502
  function nsInstructionsContainType(instructions, type) {
36980
37503
  return instructions.some((instruction) => {
@@ -37000,6 +37523,21 @@ function nsInstructionsContainType(instructions, type) {
37000
37523
  return false;
37001
37524
  });
37002
37525
  }
37526
+ function nsIRContainsPointerType(node, type, visited = /* @__PURE__ */ new Set()) {
37527
+ if (Array.isArray(node)) {
37528
+ return node.some((entry) => nsIRContainsPointerType(entry, type, visited));
37529
+ }
37530
+ if (typeof node !== "object" || node === null) return false;
37531
+ if (visited.has(node)) return false;
37532
+ visited.add(node);
37533
+ const record3 = node;
37534
+ if (record3.type === type) return true;
37535
+ const isLiteralValue = "typeInfo" in record3 && "value" in record3 && !("type" in record3);
37536
+ return Object.entries(record3).some(([key, child]) => {
37537
+ if (isLiteralValue && key === "value") return false;
37538
+ return nsIRContainsPointerType(child, type, visited);
37539
+ });
37540
+ }
37003
37541
  function isNSInstructions(value) {
37004
37542
  return Array.isArray(value) && value.every(isNSInstruction);
37005
37543
  }
@@ -37041,7 +37579,7 @@ function isNSBoolGetter(value) {
37041
37579
  if (value.typeInfo.type !== 1 /* Bool */) return false;
37042
37580
  return value.typeInfo.required === true;
37043
37581
  }
37044
- function isNSVoidAction(value) {
37582
+ function isNSVoidBody(value) {
37045
37583
  if (!isNSFunctionWithReturnType(value)) return false;
37046
37584
  if (value.typeInfo.type !== 0 /* Null */) return false;
37047
37585
  return value.typeInfo.required === true;
@@ -37177,6 +37715,12 @@ function typeInfosInvariantlyEqual(left, right) {
37177
37715
  if (left.keyEnumId !== right.keyEnumId) return false;
37178
37716
  return typeInfosInvariantlyEqual(left.entryTypeInfo, right.entryTypeInfo);
37179
37717
  }
37718
+ if (left.type === 26 /* NSAction */ && right.type === 26 /* NSAction */) {
37719
+ if (left.argumentTypes.length !== right.argumentTypes.length) return false;
37720
+ return left.argumentTypes.every(
37721
+ (argument2, index) => typeInfosInvariantlyEqual(argument2, right.argumentTypes[index])
37722
+ );
37723
+ }
37180
37724
  if (left.type === 9 /* Lookup */ && right.type === 9 /* Lookup */) {
37181
37725
  if (left.collectionMemberId !== right.collectionMemberId) return false;
37182
37726
  if (left.collectionValueId !== right.collectionValueId) return false;
@@ -37377,6 +37921,7 @@ var init_member_kind_enum = __esm({
37377
37921
  MemberKind14[MemberKind14["NSFunction"] = 23] = "NSFunction";
37378
37922
  MemberKind14[MemberKind14["FunctionRef"] = 24] = "FunctionRef";
37379
37923
  MemberKind14[MemberKind14["NSDelegate"] = 25] = "NSDelegate";
37924
+ MemberKind14[MemberKind14["NSAction"] = 26] = "NSAction";
37380
37925
  return MemberKind14;
37381
37926
  })(MemberKind || {});
37382
37927
  }
@@ -37798,8 +38343,8 @@ function isMemberNSProperty(value) {
37798
38343
  const setterCode = getField(value, "setterCode");
37799
38344
  const setter = getField(value, "setter");
37800
38345
  if (setter !== void 0 && setterCode === void 0) return false;
37801
- if (setterCode !== void 0 && !isNSVoidAction(setter)) return false;
37802
- return setter === void 0 || isNSVoidAction(setter);
38346
+ if (setterCode !== void 0 && !isNSVoidBody(setter)) return false;
38347
+ return setter === void 0 || isNSVoidBody(setter);
37803
38348
  }
37804
38349
  function isFileValue(value) {
37805
38350
  const v = value;
@@ -37891,7 +38436,7 @@ function hasValidNSFunctionAction(value, returnTypeInfo, argumentTypes) {
37891
38436
  );
37892
38437
  }
37893
38438
  function isNSFunctionUIBody(value) {
37894
- if (!isNSVoidAction(value)) return false;
38439
+ if (!isNSVoidBody(value)) return false;
37895
38440
  if (value.instructions.length !== 1) return false;
37896
38441
  const instruction = value.instructions[0];
37897
38442
  if (instruction === void 0) return false;
@@ -38003,6 +38548,44 @@ function isMemberDelegateBase(value) {
38003
38548
  if (isInitValueContent(defaultValue)) return true;
38004
38549
  return isNSDelegateValue(defaultValue.value);
38005
38550
  }
38551
+ function nsActionListenerIdentity(listener) {
38552
+ return `${listener.memberId} ${listener.valueId ?? ""}`;
38553
+ }
38554
+ function isNSActionValue(value) {
38555
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
38556
+ const record3 = value;
38557
+ if (!hasExactKeys(record3, ["listeners"])) return false;
38558
+ if (!Array.isArray(record3.listeners)) return false;
38559
+ const identities = /* @__PURE__ */ new Set();
38560
+ for (const listener of record3.listeners) {
38561
+ if (!isMemberDelegateTarget(listener)) return false;
38562
+ const identity2 = nsActionListenerIdentity(listener);
38563
+ if (identities.has(identity2)) return false;
38564
+ identities.add(identity2);
38565
+ }
38566
+ return true;
38567
+ }
38568
+ function isMemberActionBase(value) {
38569
+ const v = asMemberBaseForKind(value, 26 /* NSAction */);
38570
+ if (v === null) return false;
38571
+ if (v.required === true) return false;
38572
+ if (!Array.isArray(v.argumentTypes) || v.argumentTypes.length > 16) {
38573
+ return false;
38574
+ }
38575
+ const names = /* @__PURE__ */ new Set();
38576
+ for (const argument2 of v.argumentTypes) {
38577
+ if (!isNSFunctionArgumentTypeInfo(argument2)) return false;
38578
+ if (!isValidCallableIdentifier(argument2.name)) return false;
38579
+ if (argument2.type === NS_TYPE_UNKNOWN) return false;
38580
+ if (names.has(argument2.name)) return false;
38581
+ names.add(argument2.name);
38582
+ }
38583
+ const defaultValue = v.defaultValue;
38584
+ if (defaultValue === void 0 || defaultValue === null) return true;
38585
+ if (!isMemberValueBase(defaultValue)) return false;
38586
+ if (isInitValueContent(defaultValue)) return true;
38587
+ return isNSActionValue(defaultValue.value);
38588
+ }
38006
38589
  function isFunctionRefValue(value) {
38007
38590
  if (!value || typeof value !== "object" || Array.isArray(value)) return false;
38008
38591
  const record3 = value;
@@ -38067,7 +38650,7 @@ function isMemberBase(value) {
38067
38650
  if (partial !== void 0 && v?.kind !== 7 /* Class */ && v?.kind !== 21 /* Generic */) {
38068
38651
  return false;
38069
38652
  }
38070
- const matchesConcreteType = isMemberNullBase(value) || isMemberBoolBase(value) || isMemberIntBase(value) || isMemberStringBase(value) || isMemberFloatBase(value) || isMemberDictionaryBase(value) || isMemberListBase(value) || isMemberClassBase(value) || isMemberEnumBase(value) || isMemberLookupBase(value) || isMemberDialogueLookupBase(value) || isMemberNSPropertyBase(value) || isMemberNSPropertyContractBase(value) || isMemberSpriteBase(value) || isMemberAudioBase(value) || isMemberFunctionBase(value) || isMemberNSFunctionBase(value) || isMemberNSFunction(value) || isMemberDelegateBase(value) || isMemberFunctionRefBase(value) || isMemberVector2Base(value) || isMemberVector2IntBase(value) || isMemberVector3Base(value) || isMemberVector3IntBase(value) || isMemberColorBase(value) || isMemberDecimalBase(value) || isMemberGenericBase(value);
38653
+ const matchesConcreteType = isMemberNullBase(value) || isMemberBoolBase(value) || isMemberIntBase(value) || isMemberStringBase(value) || isMemberFloatBase(value) || isMemberDictionaryBase(value) || isMemberListBase(value) || isMemberClassBase(value) || isMemberEnumBase(value) || isMemberLookupBase(value) || isMemberDialogueLookupBase(value) || isMemberNSPropertyBase(value) || isMemberNSPropertyContractBase(value) || isMemberSpriteBase(value) || isMemberAudioBase(value) || isMemberFunctionBase(value) || isMemberNSFunctionBase(value) || isMemberNSFunction(value) || isMemberDelegateBase(value) || isMemberActionBase(value) || isMemberFunctionRefBase(value) || isMemberVector2Base(value) || isMemberVector2IntBase(value) || isMemberVector3Base(value) || isMemberVector3IntBase(value) || isMemberColorBase(value) || isMemberDecimalBase(value) || isMemberGenericBase(value);
38071
38654
  if (!matchesConcreteType) return false;
38072
38655
  if (!isValidDocsText(v?.docsText)) return false;
38073
38656
  if (typeof v?.name !== "string") return false;
@@ -38258,8 +38841,8 @@ function isMemberOverrideProps(value) {
38258
38841
  if (!isEpochMillis(v?.updatedAt)) return false;
38259
38842
  if (v.getter !== void 0 && !isNSGetter(v.getter)) return false;
38260
38843
  if (v.setter !== void 0 && v.setterCode === void 0) return false;
38261
- if (v.setterCode !== void 0 && !isNSVoidAction(v.setter)) return false;
38262
- if (v.setter !== void 0 && !isNSVoidAction(v.setter)) return false;
38844
+ if (v.setterCode !== void 0 && !isNSVoidBody(v.setter)) return false;
38845
+ if (v.setter !== void 0 && !isNSVoidBody(v.setter)) return false;
38263
38846
  const isCallableOverride = v.kind === 13 /* Function */ || v.kind === 23 /* NSFunction */;
38264
38847
  if (isCallableOverride) {
38265
38848
  if (!isValidCallableIdentifier(v.name)) return false;
@@ -40142,6 +40725,18 @@ function substituteMember(member, env, members) {
40142
40725
  };
40143
40726
  return substituted;
40144
40727
  }
40728
+ if (isMemberActionBase(resolved)) {
40729
+ let argumentsChanged = false;
40730
+ const argumentTypes = resolved.argumentTypes.map((argument2) => {
40731
+ const substituted2 = substituteNestedTypeInfo(argument2, env, members);
40732
+ if (substituted2 === argument2) return argument2;
40733
+ argumentsChanged = true;
40734
+ return { ...substituted2, name: argument2.name };
40735
+ });
40736
+ if (!argumentsChanged) return resolved;
40737
+ const substituted = { ...resolved, argumentTypes };
40738
+ return substituted;
40739
+ }
40145
40740
  return resolved;
40146
40741
  }
40147
40742
  function substituteNestedTypeInfo(typeInfo, env, members) {
@@ -40199,6 +40794,14 @@ function substituteNestedTypeInfo(typeInfo, env, members) {
40199
40794
  )
40200
40795
  };
40201
40796
  }
40797
+ if (typeInfo.type === 26 /* NSAction */) {
40798
+ return {
40799
+ ...typeInfo,
40800
+ argumentTypes: typeInfo.argumentTypes.map(
40801
+ (argument2) => substituteNestedTypeInfo(argument2, env, members)
40802
+ )
40803
+ };
40804
+ }
40202
40805
  return typeInfo;
40203
40806
  }
40204
40807
  function genericBindingTypeInfo(rawMember, env, members) {
@@ -40551,6 +41154,9 @@ var init_generics = __esm({
40551
41154
  23 /* NSFunction */,
40552
41155
  24 /* FunctionRef */,
40553
41156
  25 /* NSDelegate */,
41157
+ // A signature carrier, not a binding target (P62 §2.1): `NeoAction<T>`
41158
+ // threads T through substitution, but no `T` is ever *bound* to an action.
41159
+ 26 /* NSAction */,
40554
41160
  21 /* Generic */
40555
41161
  ]);
40556
41162
  }
@@ -42857,6 +43463,9 @@ function primitiveFallbackValue(document, member) {
42857
43463
  }
42858
43464
  };
42859
43465
  }
43466
+ if (member.kind === 26 /* NSAction */) {
43467
+ return { value: { listeners: [] } };
43468
+ }
42860
43469
  if (member.kind === 21 /* Generic */) {
42861
43470
  throw new Error(
42862
43471
  `Unreachable: Generic member "${member.name}" must be substituted through its binding environment before default value construction \u2014 a substitution site was skipped upstream (https://github.com/ryanbliss/neo-compose-specs/tree/main/new-features/complete/class-generics.md \xA74.1).`
@@ -44744,13 +45353,23 @@ function renderMemberType(context, member, enclosingClassId) {
44744
45353
  )
44745
45354
  ].join(", ")}>`;
44746
45355
  break;
45356
+ case "action":
45357
+ value = renderActionType(
45358
+ member.arguments.map((argument2) => renderType(context, argument2.type))
45359
+ );
45360
+ break;
44747
45361
  }
44748
45362
  if ((member.kind === "class" || member.kind === "generic") && member.partial === true) {
44749
45363
  value = `Partial<${value}>`;
44750
45364
  }
44751
45365
  if (member.kind === "generic") return value;
45366
+ if (member.kind === "action") return value;
44752
45367
  return nullable && value !== "null" ? `${value}?` : value;
44753
45368
  }
45369
+ function renderActionType(argumentTypes) {
45370
+ if (argumentTypes.length === 0) return "NeoAction";
45371
+ return `NeoAction<${argumentTypes.join(", ")}>`;
45372
+ }
44754
45373
  function renderLookupEntryType(context, collectionMemberId) {
44755
45374
  const collection = required(
44756
45375
  context.members,
@@ -44837,6 +45456,10 @@ function renderType(context, type) {
44837
45456
  ...type.argumentTypes.map((argument2) => renderType(context, argument2))
44838
45457
  ].join(", ")}>`;
44839
45458
  break;
45459
+ case "action":
45460
+ return renderActionType(
45461
+ type.argumentTypes.map((argument2) => renderType(context, argument2))
45462
+ );
44840
45463
  }
44841
45464
  return type.nullable && value !== "null" ? `${value}?` : value;
44842
45465
  }
@@ -44851,6 +45474,38 @@ function renderDefault(context, member) {
44851
45474
  if ("init" in value) return value.init.code;
44852
45475
  return renderDefaultValue(context, member, value);
44853
45476
  }
45477
+ function renderActionListeners(context, member, value) {
45478
+ if (!isObject2(value)) {
45479
+ throw new Error(
45480
+ `NeoAction member ${member.name} stores a default that is not a listener set.`
45481
+ );
45482
+ }
45483
+ const { listeners } = value;
45484
+ if (!Array.isArray(listeners)) {
45485
+ throw new Error(
45486
+ `NeoAction member ${member.name} stores a default with no listeners array.`
45487
+ );
45488
+ }
45489
+ const names = listeners.map((listener, index) => {
45490
+ if (!isObject2(listener)) {
45491
+ throw new Error(
45492
+ `NeoAction member ${member.name} listener ${index} is not a member target.`
45493
+ );
45494
+ }
45495
+ if (typeof listener.memberId !== "string") {
45496
+ throw new Error(
45497
+ `NeoAction member ${member.name} listener ${index} is missing memberId.`
45498
+ );
45499
+ }
45500
+ if (listener.valueId !== null && listener.valueId !== void 0) {
45501
+ throw new Error(
45502
+ `NeoAction member ${member.name} listener ${index} is instance-bound, which no declaration default can spell.`
45503
+ );
45504
+ }
45505
+ return required(context.members, listener.memberId, "action listener").name;
45506
+ });
45507
+ return `[${names.join(", ")}]`;
45508
+ }
44854
45509
  function initDefaultSource(member) {
44855
45510
  const value = member.defaultValue;
44856
45511
  if (value === null || value === void 0) return void 0;
@@ -44902,6 +45557,9 @@ function renderDefaultValue(context, member, wrapper) {
44902
45557
  }
44903
45558
  return `new ${target.name}${assignments.length ? ` { ${assignments.join(", ")} }` : "()"}`;
44904
45559
  }
45560
+ if (member.kind === "action") {
45561
+ return renderActionListeners(context, member, value);
45562
+ }
44905
45563
  if (member.kind === "functionRef" && isObject2(value) && typeof value.functionMemberId === "string") {
44906
45564
  return required(
44907
45565
  context.members,
@@ -46962,7 +47620,7 @@ function isNSBoolGetter2(value) {
46962
47620
  if (value.typeInfo.type !== 1 /* Bool */) return false;
46963
47621
  return value.typeInfo.required === true;
46964
47622
  }
46965
- function isNSVoidAction2(value) {
47623
+ function isNSVoidBody2(value) {
46966
47624
  if (!isNSGetter(value)) return false;
46967
47625
  if (value.typeInfo.type !== 0 /* Null */) return false;
46968
47626
  return value.typeInfo.required === true;
@@ -46995,7 +47653,7 @@ function isUILogicAction(value) {
46995
47653
  if (typeof v !== "object") return false;
46996
47654
  if (v === null) return false;
46997
47655
  if (v.type !== 0 /* UI */) return false;
46998
- if (!isNSVoidAction2(v.action)) return false;
47656
+ if (!isNSVoidBody2(v.action)) return false;
46999
47657
  return isUIRenderableAction(v.action);
47000
47658
  }
47001
47659
  function isCodeLogicAction(value) {
@@ -47006,7 +47664,7 @@ function isCodeLogicAction(value) {
47006
47664
  if (!isNonEmptyString(v.sourceFunctionId)) return false;
47007
47665
  if (!isDialogueSourceIdentifier(v.sourceFunctionName)) return false;
47008
47666
  if (typeof v.code !== "string") return false;
47009
- return isNSVoidAction2(v.action);
47667
+ return isNSVoidBody2(v.action);
47010
47668
  }
47011
47669
  function isNonEmptyString(value) {
47012
47670
  return typeof value === "string" && value.length > 0;
@@ -50678,6 +51336,30 @@ function lowerFieldMember(context, ownerClass, declaration, id2, commonInput, ba
50678
51336
  }))
50679
51337
  };
50680
51338
  }
51339
+ if (name === "NeoAction") {
51340
+ if (partial) {
51341
+ throw new Error("Partial<T> requires a Class or Class generic target.");
51342
+ }
51343
+ if (declaration.type.nullable) {
51344
+ throw new Error(
51345
+ `NeoAction member ${ownerClass.name}.${declaration.name} cannot be nullable; an action's rest state is an empty listener list.`
51346
+ );
51347
+ }
51348
+ return {
51349
+ ...common,
51350
+ kind: "action",
51351
+ // An action is structurally void, so every type argument is a
51352
+ // parameter — there is no leading return slot to skip. `required` is
51353
+ // fixed false because nullability has no meaning here (P62 §2.1).
51354
+ required: false,
51355
+ arguments: fieldType.arguments.map((argumentType, index) => ({
51356
+ name: `p${index + 1}`,
51357
+ type: lowerType(context, argumentType, ownerClass),
51358
+ source: span(declaration),
51359
+ selectionSpan: span(declaration)
51360
+ }))
51361
+ };
51362
+ }
50681
51363
  if (name === "bool" || name === "null") {
50682
51364
  return { ...common, kind: name };
50683
51365
  }
@@ -51267,6 +51949,20 @@ function lowerType(context, type, ownerClass) {
51267
51949
  argumentTypes: type.arguments.slice(1).map((argument2) => lowerType(context, argument2, ownerClass))
51268
51950
  };
51269
51951
  }
51952
+ if (type.name === "NeoAction") {
51953
+ if (nullable) {
51954
+ throw new Error(
51955
+ "NeoAction cannot be nullable; an action's rest state is an empty listener list."
51956
+ );
51957
+ }
51958
+ return {
51959
+ kind: "action",
51960
+ nullable: false,
51961
+ argumentTypes: type.arguments.map(
51962
+ (argument2) => lowerType(context, argument2, ownerClass)
51963
+ )
51964
+ };
51965
+ }
51270
51966
  const classId = context.classIdsByName.get(type.name);
51271
51967
  if (classId) {
51272
51968
  const parameters = context.genericParametersByClass.get(classId);
@@ -51371,6 +52067,77 @@ function manifestTypeFromAstType(type) {
51371
52067
  arguments: (type.typeArguments ?? []).map(manifestTypeFromAstType)
51372
52068
  };
51373
52069
  }
52070
+ function lowerActionDefault(context, expression, ownerClass, path) {
52071
+ const elements = expression.kind === "litList" ? expression.elements : [expression];
52072
+ const listeners = [];
52073
+ const identities = /* @__PURE__ */ new Set();
52074
+ elements.forEach((element, index) => {
52075
+ const memberId = lowerActionListenerId(
52076
+ context,
52077
+ element,
52078
+ ownerClass,
52079
+ `${path}[${index}]`
52080
+ );
52081
+ if (identities.has(memberId)) {
52082
+ throw new Error(
52083
+ `NeoAction listener ${path}[${index}] repeats an earlier listener; a listener set holds each target once.`
52084
+ );
52085
+ }
52086
+ identities.add(memberId);
52087
+ listeners.push({ memberId, valueId: null });
52088
+ });
52089
+ return { listeners };
52090
+ }
52091
+ function lowerActionListenerId(context, element, ownerClass, path) {
52092
+ let current = element;
52093
+ while (current.kind === "annotated") current = current.expression;
52094
+ if (current.kind === "lambda") {
52095
+ throw new Error(
52096
+ `NeoAction listener ${path} is a lambda. A listener must be a member reference: extract the body to an NSFunction member and subscribe that member instead.`
52097
+ );
52098
+ }
52099
+ const name = actionListenerSymbol(current);
52100
+ if (name === null) {
52101
+ throw new Error(
52102
+ `NeoAction listener ${path} must name a callable member on the current Class.`
52103
+ );
52104
+ }
52105
+ const classId = materializedId(ownerClass, "class", ownerClass.name);
52106
+ const memberId = effectiveMemberIdByName(context, classId, name);
52107
+ if (memberId === null) {
52108
+ throw new Error(
52109
+ `NeoAction listener ${path} names ${JSON.stringify(name)}, which ${ownerClass.name} does not declare or inherit.`
52110
+ );
52111
+ }
52112
+ const declaration = context.sourceMembersById.get(memberId)?.member;
52113
+ if (declaration === void 0) return memberId;
52114
+ if (declaration.kind === "function") {
52115
+ if (declaration.type.name !== "void") {
52116
+ throw new Error(
52117
+ `NeoAction listener ${path} targets ${declaration.name}, which returns a value; a listener must be void.`
52118
+ );
52119
+ }
52120
+ if (declaration.modifiers.includes("async")) {
52121
+ throw new Error(
52122
+ `NeoAction listener ${path} targets deferred ${declaration.name}; listeners are immediate-only.`
52123
+ );
52124
+ }
52125
+ return memberId;
52126
+ }
52127
+ if (declaration.type.name !== "NeoAction" && declaration.type.name !== "NeoDelegate") {
52128
+ throw new Error(
52129
+ `NeoAction listener ${path} targets ${declaration.name}, which is not a function, NSFunction, NSDelegate, or NeoAction member.`
52130
+ );
52131
+ }
52132
+ return memberId;
52133
+ }
52134
+ function actionListenerSymbol(expression) {
52135
+ if (expression.kind === "ident") return expression.name;
52136
+ if (expression.kind !== "member") return null;
52137
+ if (expression.receiver.kind !== "ident") return null;
52138
+ if (expression.receiver.name !== "this") return null;
52139
+ return expression.name;
52140
+ }
51374
52141
  function lowerExpressionValue(context, expression, declaredExpected, ownerClass, path, genericEnvironment = EMPTY_GENERIC_TYPE_ENVIRONMENT, sourceText) {
51375
52142
  const expected = substituteGenericTypeNames(
51376
52143
  declaredExpected,
@@ -51390,6 +52157,9 @@ function lowerExpressionValue(context, expression, declaredExpected, ownerClass,
51390
52157
  sourceText
51391
52158
  );
51392
52159
  }
52160
+ if (expected.name === "NeoAction") {
52161
+ return lowerActionDefault(context, expression, ownerClass, path);
52162
+ }
51393
52163
  switch (expression.kind) {
51394
52164
  case "litNull":
51395
52165
  if (!expected.nullable) {
@@ -51644,6 +52414,7 @@ function lowerExpressionValue(context, expression, declaredExpected, ownerClass,
51644
52414
  }
51645
52415
  function positionRequiresEvaluation(context, expression, expected, ownerClass) {
51646
52416
  if (isStoredDelegateLiteral(expected, expression)) return false;
52417
+ if (isStoredActionLiteral(expected, expression)) return false;
51647
52418
  return initializerRequiresEvaluation(
51648
52419
  context.declaredConstructors,
51649
52420
  expression,
@@ -51660,6 +52431,12 @@ function isStoredDelegateLiteral(type, expression) {
51660
52431
  while (current.kind === "annotated") current = current.expression;
51661
52432
  return current.kind === "lambda";
51662
52433
  }
52434
+ function isStoredActionLiteral(type, expression) {
52435
+ if (type.name !== "NeoAction") return false;
52436
+ let current = expression;
52437
+ while (current.kind === "annotated") current = current.expression;
52438
+ return current.kind === "litList";
52439
+ }
51663
52440
  function lambdaExpressionSource(source, pos) {
51664
52441
  let start = 0;
51665
52442
  for (let line = 1; line < pos.line; line += 1) {
@@ -51852,6 +52629,15 @@ function manifestTypeForMember(context, member) {
51852
52629
  ]
51853
52630
  };
51854
52631
  }
52632
+ if (member.kind === "action") {
52633
+ return {
52634
+ name: "NeoAction",
52635
+ nullable: false,
52636
+ arguments: member.arguments.map(
52637
+ (argument2) => manifestTypeForSchemaType(context, argument2.type)
52638
+ )
52639
+ };
52640
+ }
51855
52641
  if (member.kind === "enum") {
51856
52642
  return {
51857
52643
  name: context.baseEnums.get(member.enumId)?.name ?? "object",
@@ -51952,6 +52738,14 @@ function manifestTypeForSchemaType(context, type) {
51952
52738
  )
51953
52739
  ]
51954
52740
  };
52741
+ case "action":
52742
+ return {
52743
+ name: "NeoAction",
52744
+ nullable: false,
52745
+ arguments: type.argumentTypes.map(
52746
+ (argument2) => manifestTypeForSchemaType(context, argument2)
52747
+ )
52748
+ };
51955
52749
  }
51956
52750
  }
51957
52751
  function genericParameterName(context, genericParamId) {
@@ -53286,6 +54080,15 @@ function memberRuntimeType(record3, context, seen = /* @__PURE__ */ new Set(), g
53286
54080
  ),
53287
54081
  nullable: !required2
53288
54082
  };
54083
+ case 26 /* NSAction */:
54084
+ if (!isMemberActionBase(member)) return UNKNOWN_TYPE2;
54085
+ return {
54086
+ kind: "action",
54087
+ parameterTypes: member.argumentTypes.map(
54088
+ (argument2) => toLanguageType(argument2, context, genericEnvironment)
54089
+ ),
54090
+ nullable: false
54091
+ };
53289
54092
  case 21 /* Generic */: {
53290
54093
  const genericParamId = getOptionalString(member, "genericParamId");
53291
54094
  if (!genericParamId) return UNKNOWN_TYPE2;
@@ -53493,6 +54296,14 @@ function toLanguageType(type, context, genericEnvironment) {
53493
54296
  ),
53494
54297
  nullable: !required2
53495
54298
  };
54299
+ case 26 /* NSAction */:
54300
+ return {
54301
+ kind: "action",
54302
+ parameterTypes: type.argumentTypes.map(
54303
+ (argument2) => toLanguageType(argument2, context, genericEnvironment)
54304
+ ),
54305
+ nullable: false
54306
+ };
53496
54307
  case 21 /* Generic */: {
53497
54308
  const environmentEntry = genericEnvironment?.get(type.genericParamId);
53498
54309
  if (environmentEntry?.kind === "member") {
@@ -53785,7 +54596,7 @@ function compileNSGetter(code, ctx) {
53785
54596
  })
53786
54597
  );
53787
54598
  }
53788
- function compileNSAction(code, ctx) {
54599
+ function compileNSVoidBody(code, ctx) {
53789
54600
  const context = createContext(ctx, {
53790
54601
  scriptKind: "action",
53791
54602
  dialogueContext: ctx.dialogueContext
@@ -53985,12 +54796,12 @@ __export(compiler_adapter_exports, {
53985
54796
  CompileError: () => CompileError,
53986
54797
  NEOSCRIPT_COMPILER_ADAPTER_REVISION: () => NEOSCRIPT_COMPILER_ADAPTER_REVISION,
53987
54798
  clearNeoScriptBodyCompileCache: () => clearNeoScriptBodyCompileCache,
53988
- compileNSAction: () => compileNSAction,
53989
54799
  compileNSConstructor: () => compileNSConstructor,
53990
54800
  compileNSFunction: () => compileNSFunction,
53991
54801
  compileNSGetter: () => compileNSGetter,
53992
54802
  compileNSInitializer: () => compileNSInitializer,
53993
54803
  compileNSSetter: () => compileNSSetter,
54804
+ compileNSVoidBody: () => compileNSVoidBody,
53994
54805
  createNeoScriptCompilationProject: () => createNeoScriptCompilationProject
53995
54806
  });
53996
54807
  var init_compiler_adapter = __esm({
@@ -54494,6 +55305,14 @@ function substituteConstructorBaseType(typeInfo, owner, args) {
54494
55305
  )
54495
55306
  };
54496
55307
  }
55308
+ if (typeInfo.type === 26 /* NSAction */) {
55309
+ return {
55310
+ ...typeInfo,
55311
+ argumentTypes: typeInfo.argumentTypes.map(
55312
+ (argument2) => substituteConstructorBaseType(argument2, owner, args)
55313
+ )
55314
+ };
55315
+ }
54497
55316
  if (typeInfo.type === 6 /* List */ || typeInfo.type === 5 /* Dictionary */ || typeInfo.type === 9 /* Lookup */) {
54498
55317
  return {
54499
55318
  ...typeInfo,
@@ -54727,6 +55546,17 @@ function resolveMemberDeclaredTypeInfoInternal(member, members, seen) {
54727
55546
  })
54728
55547
  };
54729
55548
  }
55549
+ if (isMemberActionBase(resolved)) {
55550
+ return {
55551
+ type: 26 /* NSAction */,
55552
+ required: false,
55553
+ argumentTypes: resolved.argumentTypes.map((argument2) => {
55554
+ const { name, ...typeInfo } = argument2;
55555
+ void name;
55556
+ return typeInfo;
55557
+ })
55558
+ };
55559
+ }
54730
55560
  if (isMemberListBase(resolved) || isMemberDictionaryBase(resolved)) {
54731
55561
  const entry = members.find(
54732
55562
  (candidate) => candidate.id === resolved.entryMemberId
@@ -54910,6 +55740,14 @@ function resolveOwnerMembersForValues(document, targetValueIds, additionalRoots,
54910
55740
  const resolved = /* @__PURE__ */ new Map();
54911
55741
  if (targetValueIds.size === 0) return resolved;
54912
55742
  const valuesById = new Map(document.values.map((value) => [value.id, value]));
55743
+ const membersById = new Map(
55744
+ document.members.map((member) => [member.id, member])
55745
+ );
55746
+ const classesById = new Map(
55747
+ document.classes.map((schemaClass2) => [schemaClass2.id, schemaClass2])
55748
+ );
55749
+ const storedSchemaByClassId = /* @__PURE__ */ new Map();
55750
+ const defaultInstanceEnvByClassId = /* @__PURE__ */ new Map();
54913
55751
  const rowsByContainerId = /* @__PURE__ */ new Map();
54914
55752
  for (const value of document.values) {
54915
55753
  if (typeof value.containerId !== "string") continue;
@@ -54933,7 +55771,25 @@ function resolveOwnerMembersForValues(document, targetValueIds, additionalRoots,
54933
55771
  if (typeof memberId !== "string") return /* @__PURE__ */ new Map();
54934
55772
  const classId = declaringClassIdByMemberId.get(memberId);
54935
55773
  if (classId === void 0) return /* @__PURE__ */ new Map();
54936
- return resolveInstanceEnv(classId, void 0, document.classes);
55774
+ return defaultInstanceEnv(classId);
55775
+ }
55776
+ function defaultInstanceEnv(classId) {
55777
+ const cached = defaultInstanceEnvByClassId.get(classId);
55778
+ if (cached !== void 0) return cached;
55779
+ const created = resolveInstanceEnv(classId, void 0, document.classes);
55780
+ defaultInstanceEnvByClassId.set(classId, created);
55781
+ return created;
55782
+ }
55783
+ function storedSchema(classId) {
55784
+ const cached = storedSchemaByClassId.get(classId);
55785
+ if (cached !== void 0) return cached;
55786
+ const created = mergeStoredInstanceSchema(
55787
+ classId,
55788
+ document.classes,
55789
+ document.members
55790
+ );
55791
+ storedSchemaByClassId.set(classId, created);
55792
+ return created;
54937
55793
  }
54938
55794
  function searchRow(member, valueId, rootOwner, env, rootKind) {
54939
55795
  if (targetValueIds.has(valueId) && !resolved.has(valueId)) {
@@ -54972,16 +55828,10 @@ function resolveOwnerMembersForValues(document, targetValueIds, additionalRoots,
54972
55828
  if (isMemberClassBase(member)) {
54973
55829
  if (!isRecordValue(body.value)) return;
54974
55830
  const effectiveClassId = body.classId ?? member.classId;
54975
- const schemaClass2 = document.classes.find(
54976
- (candidate) => candidate.id === effectiveClassId
54977
- );
55831
+ const schemaClass2 = classesById.get(effectiveClassId);
54978
55832
  if (schemaClass2 === void 0) return;
54979
- const merged = mergeStoredInstanceSchema(
54980
- effectiveClassId,
54981
- document.classes,
54982
- document.members
54983
- );
54984
- const env = resolveInstanceEnv(
55833
+ const merged = storedSchema(effectiveClassId);
55834
+ const env = member.classArguments === void 0 || member.classArguments === null ? defaultInstanceEnv(effectiveClassId) : resolveInstanceEnv(
54985
55835
  effectiveClassId,
54986
55836
  member.classArguments,
54987
55837
  document.classes
@@ -54990,9 +55840,7 @@ function resolveOwnerMembersForValues(document, targetValueIds, additionalRoots,
54990
55840
  if (entry.memberId === null) continue;
54991
55841
  const childValueId = body.value[entry.schemaKey];
54992
55842
  if (childValueId === void 0) continue;
54993
- const childMember = document.members.find(
54994
- (candidate) => candidate.id === entry.memberId
54995
- );
55843
+ const childMember = membersById.get(entry.memberId);
54996
55844
  if (childMember === void 0) continue;
54997
55845
  const substituted = trySubstituteMember(childMember, env);
54998
55846
  if (substituted === null) continue;
@@ -55002,9 +55850,7 @@ function resolveOwnerMembersForValues(document, targetValueIds, additionalRoots,
55002
55850
  }
55003
55851
  if (isMemberDictionaryBase(member)) {
55004
55852
  if (!isRecordValue(body.value)) return;
55005
- const entryMemberRecord = document.members.find(
55006
- (candidate) => candidate.id === member.entryMemberId
55007
- );
55853
+ const entryMemberRecord = membersById.get(member.entryMemberId);
55008
55854
  if (entryMemberRecord === void 0) return;
55009
55855
  const entryEnv = overlayStamp(ambientEnv, body.genericBindings);
55010
55856
  const entryMember = trySubstituteMember(entryMemberRecord, entryEnv);
@@ -55015,9 +55861,7 @@ function resolveOwnerMembersForValues(document, targetValueIds, additionalRoots,
55015
55861
  return;
55016
55862
  }
55017
55863
  if (isMemberListBase(member)) {
55018
- const entryMemberRecord = document.members.find(
55019
- (candidate) => candidate.id === member.entryMemberId
55020
- );
55864
+ const entryMemberRecord = membersById.get(member.entryMemberId);
55021
55865
  if (entryMemberRecord === void 0) return;
55022
55866
  const entryEnv = overlayStamp(ambientEnv, body.genericBindings);
55023
55867
  const entryMember = trySubstituteMember(entryMemberRecord, entryEnv);
@@ -56996,8 +57840,20 @@ function pushConstructionFrame(ctx, label) {
56996
57840
  state.constructionStack.pop();
56997
57841
  };
56998
57842
  }
57843
+ function schemaRevisionUnchanged(previous, next) {
57844
+ if (previous.length !== next.length) return false;
57845
+ return previous.every((cell, index) => cell === next[index]);
57846
+ }
56999
57847
  function evaluatorResolutionCache(ctx) {
57000
- ctx.__resolutionCache ??= {
57848
+ if (ctx.__resolutionCache !== void 0) return ctx.__resolutionCache;
57849
+ const { project, members, classes } = ctx.vm;
57850
+ const schemaRevision = ctx.vm.schemaRevision ?? NO_SCHEMA_REVISION;
57851
+ const entry = resolutionCacheByMembers.get(members);
57852
+ if (entry !== void 0 && entry.project === project && entry.classes === classes && schemaRevisionUnchanged(entry.schemaRevision, schemaRevision)) {
57853
+ ctx.__resolutionCache = entry.cache;
57854
+ return entry.cache;
57855
+ }
57856
+ const cache = {
57001
57857
  schemaKeyByMemberId: /* @__PURE__ */ new Map(),
57002
57858
  mergedSchemaByClassId: /* @__PURE__ */ new Map(),
57003
57859
  callableSignatureByKey: /* @__PURE__ */ new Map(),
@@ -57006,7 +57862,14 @@ function evaluatorResolutionCache(ctx) {
57006
57862
  compiledSetterByMemberId: /* @__PURE__ */ new Map(),
57007
57863
  effectiveStorageResolver: void 0
57008
57864
  };
57009
- return ctx.__resolutionCache;
57865
+ resolutionCacheByMembers.set(members, {
57866
+ project,
57867
+ classes,
57868
+ schemaRevision,
57869
+ cache
57870
+ });
57871
+ ctx.__resolutionCache = cache;
57872
+ return cache;
57010
57873
  }
57011
57874
  function createdSessionValuesSince(session, existingIds) {
57012
57875
  const created = [];
@@ -57075,7 +57938,7 @@ function evaluateNSGetterWithEffects(getter, ctx, argumentValues = []) {
57075
57938
  finalizeConstructorAllocations(runtimeCtx, void 0, ownsInvocationState);
57076
57939
  throw new NSGetterRuntimeError("Function ended without a return statement");
57077
57940
  }
57078
- function evaluateNSAction(action, ctx) {
57941
+ function evaluateNSVoidBody(action, ctx) {
57079
57942
  const ownsInvocationState = ctx.__executionState === void 0;
57080
57943
  const writes = [];
57081
57944
  const runtimeCtx = withEvaluationRuntime(ctx, writes);
@@ -57992,6 +58855,11 @@ function evalInstructions(instructions, scope, ctx, options) {
57992
58855
  });
57993
58856
  break;
57994
58857
  }
58858
+ case "addActionListener" /* addActionListener */:
58859
+ case "removeActionListener" /* removeActionListener */: {
58860
+ evalActionListenerInstruction(ins, scope, ctx, options);
58861
+ break;
58862
+ }
57995
58863
  case "collectionCall" /* collectionCall */: {
57996
58864
  evalCollectionMutationInstruction(ins, scope, ctx, options);
57997
58865
  break;
@@ -58173,6 +59041,61 @@ function evalInstructions(instructions, scope, ctx, options) {
58173
59041
  }
58174
59042
  return { kind: "fallthrough" };
58175
59043
  }
59044
+ function evalActionListenerInstruction(ins, scope, ctx, options) {
59045
+ const operator = ins.type === "addActionListener" /* addActionListener */ ? "+=" : "-=";
59046
+ if (ins.target.pointer.type === "variable" /* variable */) {
59047
+ throw new NSGetterRuntimeError(
59048
+ `NeoAction '${operator}' target must be a member, not a local variable.`
59049
+ );
59050
+ }
59051
+ if (ins.target.writability === "setter" /* Setter */) {
59052
+ throw new NSGetterRuntimeError(
59053
+ `NeoAction '${operator}' cannot route through a property setter; a listener set is stored data, not a computed value.`
59054
+ );
59055
+ }
59056
+ const listener = evalPointer(ins.listener, scope, ctx);
59057
+ if (!isMemberDelegateTarget(listener)) {
59058
+ throw new NSGetterRuntimeError(
59059
+ `NeoAction '${operator}' listener must resolve to a member target; closures are not listeners.`
59060
+ );
59061
+ }
59062
+ const current = actionListenersOf(
59063
+ evalPointer(ins.target.pointer, scope, ctx),
59064
+ () => `'${operator}' target`
59065
+ );
59066
+ const identity2 = nsActionListenerIdentity(listener);
59067
+ const present = current.some(
59068
+ (entry) => nsActionListenerIdentity(entry) === identity2
59069
+ );
59070
+ let listeners;
59071
+ if (ins.type === "addActionListener" /* addActionListener */) {
59072
+ if (present) return;
59073
+ listeners = [
59074
+ ...current,
59075
+ { memberId: listener.memberId, valueId: listener.valueId }
59076
+ ];
59077
+ } else {
59078
+ if (!present) return;
59079
+ listeners = current.filter(
59080
+ (entry) => nsActionListenerIdentity(entry) !== identity2
59081
+ );
59082
+ }
59083
+ const value = { listeners };
59084
+ validateAssignmentTarget(ins.target.pointer, scope, ctx);
59085
+ applyOverlayAssignment(
59086
+ ins.target.pointer,
59087
+ ins.target.typeInfo,
59088
+ value,
59089
+ scope,
59090
+ ctx
59091
+ );
59092
+ options.writes.push({
59093
+ kind: "assign",
59094
+ target: ins.target,
59095
+ value,
59096
+ ...resolvedDestinationFieldsForAssign(ins.target.pointer, scope, ctx)
59097
+ });
59098
+ }
58176
59099
  function applyOverlayAssignment(target, targetTypeInfo, value, scope, ctx) {
58177
59100
  if (target.type === "staticMember" /* staticMember */) {
58178
59101
  if (ctx.storedConstructionReplay === true) {
@@ -59016,6 +59939,23 @@ function evalPointer(pointer, scope, ctx) {
59016
59939
  const args = pointer.args.map((arg) => evalPointer(arg, scope, ctx));
59017
59940
  return invokeDelegateValue(delegate, args, ctx, pointer.callSiteId);
59018
59941
  }
59942
+ case "callAction" /* callAction */: {
59943
+ const read = readActionWithOwner(pointer.action, scope, ctx);
59944
+ const args = pointer.args.map((arg) => evalPointer(arg, scope, ctx));
59945
+ consumeBudget(ctx, "workUnits", 1, "work unit");
59946
+ invokeActionListeners(
59947
+ actionListenersOf(
59948
+ read.action,
59949
+ () => actionPointerLabel(pointer.action, ctx)
59950
+ ),
59951
+ args,
59952
+ ctx,
59953
+ () => actionInvocationFrame(pointer.action, read.owner, ctx),
59954
+ read.owner,
59955
+ pointer.callSiteId
59956
+ );
59957
+ return null;
59958
+ }
59019
59959
  case "functionErrorCheck" /* functionErrorCheck */: {
59020
59960
  try {
59021
59961
  evalPointer(pointer.call, scope, ctx);
@@ -59063,7 +60003,7 @@ function resolveEffectiveCallableMember(pointer, receiver, ctx) {
59063
60003
  const runtimeMember = resolveRuntimeSchemaMember(receiver, schemaKey, ctx);
59064
60004
  return runtimeMember ?? staticMember;
59065
60005
  }
59066
- function invokeDelegateValue(value, args, ctx, callSiteId) {
60006
+ function invokeDelegateValue(value, args, ctx, callSiteId, ownerReceiver) {
59067
60007
  if (isNSDelegateClosureValueDraft(value)) {
59068
60008
  throw new NSGetterRuntimeError(
59069
60009
  "NeoDelegate closure has source code but no compiled action."
@@ -59083,6 +60023,11 @@ function invokeDelegateValue(value, args, ctx, callSiteId) {
59083
60023
  value.action.typeInfo.type === 0 /* Null */
59084
60024
  );
59085
60025
  }
60026
+ if (isNSActionValue(value)) {
60027
+ throw new NSGetterRuntimeError(
60028
+ "An NSAction listener set is not a callable value; invoke the action member as a statement instead."
60029
+ );
60030
+ }
59086
60031
  if (!isMemberDelegateTarget(value)) {
59087
60032
  throw new NSGetterRuntimeError(
59088
60033
  "NeoDelegate value is neither a closure nor a bound member target."
@@ -59114,6 +60059,8 @@ function invokeDelegateValue(value, args, ctx, callSiteId) {
59114
60059
  );
59115
60060
  }
59116
60061
  receiver = row.value;
60062
+ } else if (ownerReceiver !== null && ownerReceiver !== void 0) {
60063
+ receiver = listenerReceiverOnOwner(member.id, ownerReceiver, ctx);
59117
60064
  }
59118
60065
  if (receiver !== null) {
59119
60066
  const schemaKey = cachedSchemaKeyForMember(member.id, ctx);
@@ -59197,6 +60144,17 @@ function invokeDelegateValue(value, args, ctx, callSiteId) {
59197
60144
  }
59198
60145
  return invokeDelegateValue(nested, args, ctx, callSiteId);
59199
60146
  }
60147
+ if (member.kind === 26 /* NSAction */) {
60148
+ invokeActionListeners(
60149
+ storedActionListeners(member, receiver, ctx),
60150
+ args,
60151
+ ctx,
60152
+ () => frame,
60153
+ receiver,
60154
+ callSiteId
60155
+ );
60156
+ return null;
60157
+ }
59200
60158
  throw new NSGetterRuntimeError(
59201
60159
  `NeoDelegate target '${member.name}' resolves to non-callable member kind ${MemberKind[member.kind]}.`
59202
60160
  );
@@ -59204,6 +60162,107 @@ function invokeDelegateValue(value, args, ctx, callSiteId) {
59204
60162
  state.delegateStack.pop();
59205
60163
  }
59206
60164
  }
60165
+ function actionListenersOf(value, describe2) {
60166
+ if (value === null || value === void 0) return [];
60167
+ if (!isNSActionValue(value)) {
60168
+ throw new NSGetterRuntimeError(
60169
+ `NeoAction ${describe2()} holds a value that is not a valid listener set \u2014 every entry must be a distinct member target.`
60170
+ );
60171
+ }
60172
+ return value.listeners;
60173
+ }
60174
+ function storedActionListeners(member, receiver, ctx) {
60175
+ const describe2 = () => `target '${member.name}'`;
60176
+ if (receiver !== null) {
60177
+ const schemaKey = cachedSchemaKeyForMember(member.id, ctx);
60178
+ if (schemaKey === null) {
60179
+ throw new NSGetterRuntimeError(
60180
+ `NeoAction target '${member.name}' has no schema key on its owning class.`
60181
+ );
60182
+ }
60183
+ if (typeof receiver !== "object" || !Object.prototype.hasOwnProperty.call(receiver, schemaKey)) {
60184
+ throw new NSGetterRuntimeError(
60185
+ `NeoAction target '${member.name}' is missing from its receiver.`
60186
+ );
60187
+ }
60188
+ return actionListenersOf(
60189
+ resolveValueIfIdForMember(
60190
+ receiver[schemaKey],
60191
+ member,
60192
+ ctx
60193
+ ),
60194
+ describe2
60195
+ );
60196
+ }
60197
+ if (member.isStatic === true) {
60198
+ return actionListenersOf(evalStaticMember(member.id, ctx), describe2);
60199
+ }
60200
+ if (member.defaultValue === void 0 || member.defaultValue === null) {
60201
+ return [];
60202
+ }
60203
+ if (!isLiteralValueContent(member.defaultValue)) {
60204
+ throw new NSGetterRuntimeError(
60205
+ `NeoAction target '${member.name}' has an unevaluated initializer instead of a declaration default listener set.`
60206
+ );
60207
+ }
60208
+ return actionListenersOf(member.defaultValue.value, describe2);
60209
+ }
60210
+ function listenerReceiverOnOwner(memberId, ownerReceiver, ctx) {
60211
+ const schemaKey = cachedSchemaKeyForMember(memberId, ctx);
60212
+ if (schemaKey === null) return null;
60213
+ const onOwner = resolveRuntimeSchemaMember(ownerReceiver, schemaKey, ctx);
60214
+ if (onOwner === null) return null;
60215
+ return ownerReceiver;
60216
+ }
60217
+ function invokeActionListeners(listeners, args, ctx, frame, ownerReceiver, callSiteId) {
60218
+ for (let index = 0; index < listeners.length; index += 1) {
60219
+ try {
60220
+ invokeDelegateValue(
60221
+ listeners[index],
60222
+ args,
60223
+ ctx,
60224
+ callSiteId,
60225
+ ownerReceiver
60226
+ );
60227
+ } catch (error) {
60228
+ if (!isCatchableNeoScriptRuntimeError(error)) throw error;
60229
+ throw new NSGetterRuntimeError(
60230
+ `${frame()} listener ${index} threw: ${error.message}`
60231
+ );
60232
+ }
60233
+ }
60234
+ }
60235
+ function readActionWithOwner(pointer, scope, ctx) {
60236
+ if (pointer.type !== "keyOf" /* keyOf */) {
60237
+ return { action: evalPointer(pointer, scope, ctx), owner: null };
60238
+ }
60239
+ let owner = null;
60240
+ const action = evalKeyOf(
60241
+ pointer.keyOf,
60242
+ scope,
60243
+ ctx,
60244
+ pointer.optional === true,
60245
+ pointer.memberId,
60246
+ (receiver) => {
60247
+ owner = receiver;
60248
+ }
60249
+ );
60250
+ return { action, owner };
60251
+ }
60252
+ function actionPointerLabel(pointer, ctx) {
60253
+ if (pointer.type === "staticMember" /* staticMember */) {
60254
+ return evalMemberById(ctx.vm, pointer.memberId)?.name ?? pointer.memberId;
60255
+ }
60256
+ if (pointer.type === "keyOf" /* keyOf */ && pointer.memberId !== void 0) {
60257
+ return evalMemberById(ctx.vm, pointer.memberId)?.name ?? pointer.memberId;
60258
+ }
60259
+ return "action";
60260
+ }
60261
+ function actionInvocationFrame(pointer, owner, ctx) {
60262
+ const name = actionPointerLabel(pointer, ctx);
60263
+ if (owner === null || owner === void 0) return `${name}[default]`;
60264
+ return `${name}[${findKnownRowIdByValueReference(owner, ctx) ?? "default"}]`;
60265
+ }
59207
60266
  function nativeReceiverDescription(member, receiver, receiverId) {
59208
60267
  if (receiverId !== void 0) return `, receiver value id '${receiverId}'`;
59209
60268
  return member.isStatic === true || receiver === null ? ", static receiver" : ", untracked instance receiver";
@@ -59748,6 +60807,8 @@ function runtimeTypeCheck(value, checkType, ctx, seenCollections = /* @__PURE__
59748
60807
  return typeof value === "string";
59749
60808
  case 25 /* NSDelegate */:
59750
60809
  return isNSDelegateClosureValue(value) || isMemberDelegateTarget(value);
60810
+ case 26 /* NSAction */:
60811
+ return isNSActionValue(value);
59751
60812
  case 11 /* Sprite */:
59752
60813
  return isRuntimeSpriteValue(value);
59753
60814
  case 12 /* Audio */:
@@ -59890,8 +60951,9 @@ function isRuntimeSpriteValue(value) {
59890
60951
  const sliceIndex = value.sliceIndex;
59891
60952
  return typeof sliceIndex === "number" && Number.isInteger(sliceIndex) && sliceIndex >= 0;
59892
60953
  }
59893
- function evalKeyOf(keyOf, scope, ctx, optional = false, memberId) {
60954
+ function evalKeyOf(keyOf, scope, ctx, optional = false, memberId, onReceiver) {
59894
60955
  const receiver = evalPointer(keyOf.pointer, scope, ctx);
60956
+ onReceiver?.(receiver);
59895
60957
  if (optional && (receiver === null || receiver === void 0)) {
59896
60958
  return null;
59897
60959
  }
@@ -63298,7 +64360,7 @@ function pushParams(parent, parameters, positional, isList) {
63298
64360
  }
63299
64361
  return child;
63300
64362
  }
63301
- var DELEGATE_LEXICAL_THIS, DELEGATE_LEXICAL_ROOT, NonCatchableNSGetterRuntimeError, NativeFunctionDelegateUnavailableError, CorruptNeoScriptIRError, NeoScriptResourceLimitError, NeoScriptWallClockTimeoutError, DEFAULT_NEO_SCRIPT_EXECUTION_BUDGET_LIMITS, liveListIndexesByProject, evaluatorOwnershipCachesByBase, MAX_CONSTRUCTION_DEPTH, MAX_LOOP_ITERATIONS, LazyValueOverlay, readonlyBindingErrorsByScope, READONLY_FOREACH_BINDING_ERROR, READONLY_CATCH_BINDING_ERROR;
64363
+ var DELEGATE_LEXICAL_THIS, DELEGATE_LEXICAL_ROOT, NonCatchableNSGetterRuntimeError, NativeFunctionDelegateUnavailableError, CorruptNeoScriptIRError, NeoScriptResourceLimitError, NeoScriptWallClockTimeoutError, DEFAULT_NEO_SCRIPT_EXECUTION_BUDGET_LIMITS, liveListIndexesByProject, evaluatorOwnershipCachesByBase, MAX_CONSTRUCTION_DEPTH, MAX_LOOP_ITERATIONS, resolutionCacheByMembers, NO_SCHEMA_REVISION, LazyValueOverlay, readonlyBindingErrorsByScope, READONLY_FOREACH_BINDING_ERROR, READONLY_CATCH_BINDING_ERROR;
63302
64364
  var init_evaluateNSGetter = __esm({
63303
64365
  "../src/view-models/neoscript-evaluator/evaluateNSGetter.ts"() {
63304
64366
  "use strict";
@@ -63352,6 +64414,8 @@ var init_evaluateNSGetter = __esm({
63352
64414
  evaluatorOwnershipCachesByBase = /* @__PURE__ */ new WeakMap();
63353
64415
  MAX_CONSTRUCTION_DEPTH = 64;
63354
64416
  MAX_LOOP_ITERATIONS = 1e4;
64417
+ resolutionCacheByMembers = /* @__PURE__ */ new WeakMap();
64418
+ NO_SCHEMA_REVISION = [];
63355
64419
  LazyValueOverlay = class extends Map {
63356
64420
  constructor(vm) {
63357
64421
  super();
@@ -63462,21 +64526,17 @@ function initializerEvaluatorLookups(document) {
63462
64526
  });
63463
64527
  return lookups;
63464
64528
  }
63465
- function buildInitializerRootValue(document) {
64529
+ function buildInitializerRootValue(document, lookups) {
63466
64530
  const rootValue = {};
63467
- const memberById = new Map(
63468
- document.members.map((member) => [member.id, member])
63469
- );
63470
- const valueById = new Map(document.values.map((row) => [row.id, row]));
63471
64531
  for (const { root, memberId } of projectRootMembersInDisplayOrder(
63472
64532
  document.project
63473
64533
  )) {
63474
- const member = memberById.get(memberId);
63475
- if (member === void 0 || typeof member.valueId !== "string") {
64534
+ const member = lookups.memberById(memberId);
64535
+ if (member === null || typeof member.valueId !== "string") {
63476
64536
  rootValue[root.key] = null;
63477
64537
  continue;
63478
64538
  }
63479
- rootValue[root.key] = valueById.get(member.valueId)?.value ?? null;
64539
+ rootValue[root.key] = lookups.valueById(member.valueId)?.value ?? null;
63480
64540
  }
63481
64541
  return rootValue;
63482
64542
  }
@@ -63489,6 +64549,7 @@ function evaluateMemberInitializer(args) {
63489
64549
  args.init
63490
64550
  );
63491
64551
  }
64552
+ const databaseVM = initializerEvaluatorLookups(args.document);
63492
64553
  const ctx = {
63493
64554
  vm: {
63494
64555
  project: args.document.project,
@@ -63502,10 +64563,10 @@ function evaluateMemberInitializer(args) {
63502
64563
  interfaces: args.document.interfaces,
63503
64564
  localizedTexts: args.document.localizedTexts,
63504
64565
  localizationConfig: args.document.localizationConfig,
63505
- databaseVM: initializerEvaluatorLookups(args.document)
64566
+ databaseVM
63506
64567
  },
63507
64568
  thisValue: null,
63508
- rootValue: buildInitializerRootValue(args.document),
64569
+ rootValue: buildInitializerRootValue(args.document, databaseVM),
63509
64570
  saveStaticBindings: args.saveStaticBindings,
63510
64571
  sessionStaticBindings: args.sessionStaticBindings,
63511
64572
  ...args.storedConstructionReplay === true ? {
@@ -75838,7 +76899,7 @@ function compileProjectMigrationAction(document, migration) {
75838
76899
  const thisClass = migration.targetClassId === null ? null : document.classes.find(
75839
76900
  (schemaClass2) => schemaClass2.id === migration.targetClassId
75840
76901
  ) ?? null;
75841
- return compileNSAction(migration.code, {
76902
+ return compileNSVoidBody(migration.code, {
75842
76903
  project: document.project,
75843
76904
  members: document.members,
75844
76905
  classes: document.classes,
@@ -76295,7 +77356,7 @@ function compileDialogueLogicWithProjectData(request, projectData) {
76295
77356
  });
76296
77357
  return {
76297
77358
  target: 1 /* Action */,
76298
- action: compileNSAction(request.code, ctx)
77359
+ action: compileNSVoidBody(request.code, ctx)
76299
77360
  };
76300
77361
  }
76301
77362
  function stringifyCompiledTextGetter(getter) {
@@ -76525,8 +77586,8 @@ function recompileDialogueNode(args) {
76525
77586
  if (nodeType === 2 /* Actions */) {
76526
77587
  const actions = args.node.actions;
76527
77588
  if (!Array.isArray(actions)) return false;
76528
- for (const actionValue of actions) {
76529
- const action = asObject(actionValue);
77589
+ for (const actionValue2 of actions) {
77590
+ const action = asObject(actionValue2);
76530
77591
  if (action?.type !== 0 /* EditMember */) continue;
76531
77592
  const logic = asObject(action.logic);
76532
77593
  if (!isCodeLogic(logic)) continue;
@@ -77219,6 +78280,14 @@ var init_project_version_static_value_writes = __esm({
77219
78280
  }
77220
78281
  return;
77221
78282
  }
78283
+ if (isMemberActionBase(member)) {
78284
+ if (!isNSActionValue(value.value)) {
78285
+ throw new Error(
78286
+ `${path} must store an NSAction listener set: { listeners: [...] } with a unique (memberId, valueId) identity per entry.`
78287
+ );
78288
+ }
78289
+ return;
78290
+ }
77222
78291
  if (isMemberClassBase(member)) {
77223
78292
  this.validateClass({ ...args, member, value });
77224
78293
  return;
@@ -80931,8 +82000,8 @@ function stripDialogueNodeDerivedBodies(node) {
80931
82000
  stripConditionCompanions(next.conditions);
80932
82001
  stripTextVariableCompanions(next.variables);
80933
82002
  if (Array.isArray(next.actions)) {
80934
- for (const actionValue of next.actions) {
80935
- const action = asRecord(actionValue);
82003
+ for (const actionValue2 of next.actions) {
82004
+ const action = asRecord(actionValue2);
80936
82005
  const logic = asRecord(action?.logic);
80937
82006
  if (logic?.type === 1 && typeof logic.code === "string") {
80938
82007
  delete logic.action;
@@ -81785,6 +82854,7 @@ function assertProjectVersionWholeGraphWritesValid(args) {
81785
82854
  changes: args.changes
81786
82855
  });
81787
82856
  assertStagedSchemaIdentifiersValid(projected, args.changes);
82857
+ assertStagedActionListenersValid(projected, args.changes);
81788
82858
  assertStagedUnitySingletonValid(projected, args.changes);
81789
82859
  assertStagedProjectInterfaceValid(projected, args.changes);
81790
82860
  assertStagedTileGridReferencesValid(args.document, projected, args.changes);
@@ -82260,6 +83330,207 @@ function assertStagedSchemaIdentifiersValid(projected, changes) {
82260
83330
  }
82261
83331
  }
82262
83332
  }
83333
+ function assertStagedActionListenersValid(projected, changes) {
83334
+ if (!changes.some(
83335
+ (change) => ACTION_LISTENER_TRIGGER_RECORD_KINDS.has(change.recordKind)
83336
+ )) {
83337
+ return;
83338
+ }
83339
+ const actions = projected.members.filter(
83340
+ (member) => member.kind === 26 /* NSAction */
83341
+ );
83342
+ if (actions.length === 0) return;
83343
+ const membersById = new Map(
83344
+ projected.members.map((member) => [member.id, member])
83345
+ );
83346
+ const valueIds = new Set(projected.values.map((value) => value.id));
83347
+ const graded = [];
83348
+ for (const action of actions) {
83349
+ const listeners = authoredActionListeners(action);
83350
+ if (listeners.length === 0) continue;
83351
+ graded.push({
83352
+ listeners,
83353
+ declaration: substituteMemberForListenerGrading(action, projected),
83354
+ label: describeActionMember(action, projected.classes)
83355
+ });
83356
+ }
83357
+ const candidateRowIds = candidateActionListenerRowIds(projected, actions);
83358
+ if (candidateRowIds.size > 0) {
83359
+ const rowsById = new Map(
83360
+ projected.values.map((value) => [value.id, value])
83361
+ );
83362
+ const owners = resolveOwnerMembersForValues(projected, candidateRowIds);
83363
+ for (const [rowId, owner] of owners) {
83364
+ if (owner.kind !== 26 /* NSAction */) continue;
83365
+ const row = rowsById.get(rowId);
83366
+ if (row === void 0) continue;
83367
+ const listeners = storedRowActionListeners(row);
83368
+ if (listeners.length === 0) continue;
83369
+ const ownerId = getMemberId(owner);
83370
+ if (ownerId === null) continue;
83371
+ const action = membersById.get(ownerId);
83372
+ if (action === void 0) continue;
83373
+ graded.push({
83374
+ listeners,
83375
+ declaration: owner,
83376
+ label: `${describeActionMember(action, projected.classes)} row "${rowId}"`
83377
+ });
83378
+ }
83379
+ }
83380
+ for (const entry of graded) {
83381
+ const expected = callableArgumentTypes(entry.declaration);
83382
+ if (expected === null) continue;
83383
+ assertActionListenerSetValid({
83384
+ listeners: entry.listeners,
83385
+ expected,
83386
+ owner: entry.label,
83387
+ projected,
83388
+ membersById,
83389
+ valueIds
83390
+ });
83391
+ }
83392
+ }
83393
+ function candidateActionListenerRowIds(projected, actions) {
83394
+ const actionIds = new Set(actions.map((action) => action.id));
83395
+ const actionSchemaKeys = /* @__PURE__ */ new Set();
83396
+ for (const schemaClass2 of projected.classes) {
83397
+ for (const [schemaKey, memberId] of Object.entries(schemaClass2.schema)) {
83398
+ if (actionIds.has(memberId)) actionSchemaKeys.add(schemaKey);
83399
+ }
83400
+ }
83401
+ const candidates = /* @__PURE__ */ new Set();
83402
+ for (const action of actions) {
83403
+ if (typeof action.valueId === "string") candidates.add(action.valueId);
83404
+ }
83405
+ if (actionSchemaKeys.size === 0) return candidates;
83406
+ for (const row of projected.values) {
83407
+ if (!isPlainRecord4(row.value)) continue;
83408
+ for (const schemaKey of actionSchemaKeys) {
83409
+ const childId = row.value[schemaKey];
83410
+ if (typeof childId === "string") candidates.add(childId);
83411
+ }
83412
+ }
83413
+ return candidates;
83414
+ }
83415
+ function storedRowActionListeners(row) {
83416
+ const body = row.value;
83417
+ if (!isPlainRecord4(body)) return [];
83418
+ const listeners = body.listeners;
83419
+ return Array.isArray(listeners) ? listeners : [];
83420
+ }
83421
+ function getMemberId(member) {
83422
+ const id2 = Reflect.get(member, "id");
83423
+ return typeof id2 === "string" ? id2 : null;
83424
+ }
83425
+ function assertActionListenerSetValid(args) {
83426
+ const { expected, owner, projected, membersById, valueIds } = args;
83427
+ const identities = /* @__PURE__ */ new Map();
83428
+ args.listeners.forEach((listener, index) => {
83429
+ if (!isMemberDelegateTarget(listener)) {
83430
+ throw new Error(
83431
+ `${owner} listener ${index} is not a member target; a listener is always a { memberId, valueId } reference, never a closure.`
83432
+ );
83433
+ }
83434
+ const identity2 = nsActionListenerIdentity(listener);
83435
+ const firstIndex = identities.get(identity2);
83436
+ if (firstIndex !== void 0) {
83437
+ throw new Error(
83438
+ `${owner} listener ${index} repeats the target identity of listener ${firstIndex} (member "${listener.memberId}", value ${listener.valueId ?? "default"}); a listener set holds each target once.`
83439
+ );
83440
+ }
83441
+ identities.set(identity2, index);
83442
+ if (listener.valueId !== null && !valueIds.has(listener.valueId)) {
83443
+ throw new Error(
83444
+ `${owner} listener ${index} names receiver value "${listener.valueId}", which this project version does not contain.`
83445
+ );
83446
+ }
83447
+ const target = membersById.get(listener.memberId);
83448
+ if (target === void 0) {
83449
+ throw new Error(
83450
+ `${owner} listener ${index} names member "${listener.memberId}", which this project version does not contain.`
83451
+ );
83452
+ }
83453
+ if (!ACTION_LISTENER_TARGET_KINDS.has(target.kind)) {
83454
+ throw new Error(
83455
+ `${owner} listener ${index} targets member "${target.name}" (${target.id}), whose kind ${MemberKind[target.kind]} is not callable; a listener must be a Function, NSFunction, NSDelegate, or NSAction member.`
83456
+ );
83457
+ }
83458
+ const signature = listenerTargetSignature(
83459
+ substituteMemberForListenerGrading(target, projected)
83460
+ );
83461
+ if (signature === null) {
83462
+ throw new Error(
83463
+ `${owner} listener ${index} targets member "${target.name}" (${target.id}), whose callable signature could not be resolved.`
83464
+ );
83465
+ }
83466
+ if (signature.deferred) {
83467
+ throw new Error(
83468
+ `${owner} listener ${index} targets deferred member "${target.name}" (${target.id}); listeners are immediate-only.`
83469
+ );
83470
+ }
83471
+ if (!signature.returnsVoid) {
83472
+ throw new Error(
83473
+ `${owner} listener ${index} targets member "${target.name}" (${target.id}), which returns a value; a listener must be void.`
83474
+ );
83475
+ }
83476
+ if (signature.argumentTypes.length !== expected.length) {
83477
+ throw new Error(
83478
+ `${owner} listener ${index} targets member "${target.name}" (${target.id}), which takes ${signature.argumentTypes.length} parameters; the action declares ${expected.length}.`
83479
+ );
83480
+ }
83481
+ expected.forEach((argument2, position) => {
83482
+ const candidate = signature.argumentTypes[position];
83483
+ if (typeInfosInvariantlyEqual(argument2, candidate)) return;
83484
+ throw new Error(
83485
+ `${owner} listener ${index} targets member "${target.name}" (${target.id}), whose parameter ${position + 1} "${candidate.name}" does not match the action's parameter "${argument2.name}" after generic substitution.`
83486
+ );
83487
+ });
83488
+ });
83489
+ }
83490
+ function authoredActionListeners(action) {
83491
+ const defaultValue = action.defaultValue;
83492
+ if (defaultValue === void 0 || defaultValue === null) return [];
83493
+ if (!isLiteralValueContent(defaultValue)) return [];
83494
+ const body = defaultValue.value;
83495
+ if (!isPlainRecord4(body)) return [];
83496
+ const listeners = body.listeners;
83497
+ return Array.isArray(listeners) ? listeners : [];
83498
+ }
83499
+ function substituteMemberForListenerGrading(member, projected) {
83500
+ const env = resolveDeclaringClassGenericEnv(member.id, projected.classes);
83501
+ try {
83502
+ return substituteMember(member, env, projected.members);
83503
+ } catch {
83504
+ return resolveMember2(member, projected.members);
83505
+ }
83506
+ }
83507
+ function listenerTargetSignature(target) {
83508
+ const argumentTypes = callableArgumentTypes(target);
83509
+ if (argumentTypes === null) return null;
83510
+ if (target.kind === 26 /* NSAction */) {
83511
+ return { returnsVoid: true, argumentTypes, deferred: false };
83512
+ }
83513
+ const returnTypeInfo = Reflect.get(target, "returnTypeInfo");
83514
+ if (!isNSFunctionReturnTypeInfo(returnTypeInfo)) return null;
83515
+ return {
83516
+ returnsVoid: returnTypeInfo.type === NS_TYPE_VOID,
83517
+ argumentTypes,
83518
+ deferred: Reflect.get(target, "deferred") === true
83519
+ };
83520
+ }
83521
+ function callableArgumentTypes(member) {
83522
+ const argumentTypes = Reflect.get(member, "argumentTypes");
83523
+ if (!Array.isArray(argumentTypes)) return null;
83524
+ const typed = argumentTypes.filter(isNSFunctionArgumentTypeInfo);
83525
+ return typed.length === argumentTypes.length ? typed : null;
83526
+ }
83527
+ function describeActionMember(action, classes) {
83528
+ const placement = findSchemaPlacement(action.id, classes);
83529
+ if (placement === null) {
83530
+ return `NSAction member "${action.name}" (${action.id})`;
83531
+ }
83532
+ return `NSAction member "${placement.ownerClass.name}.${action.name}" (${action.id})`;
83533
+ }
82263
83534
  function assertStagedUnitySingletonValid(projected, changes) {
82264
83535
  if (!changes.some(
82265
83536
  (change) => change.recordKind === "project" || change.recordKind === "member"
@@ -82317,7 +83588,7 @@ function assertStagedTileGridReferencesValid(current, projected, changes) {
82317
83588
  tileGridReferenceDeleteBlockedErrorData(blockers)
82318
83589
  );
82319
83590
  }
82320
- var INTERFACE_VALIDATED_RECORD_KINDS;
83591
+ var INTERFACE_VALIDATED_RECORD_KINDS, ACTION_LISTENER_TRIGGER_RECORD_KINDS, ACTION_LISTENER_TARGET_KINDS;
82321
83592
  var init_project_version_whole_graph_validation = __esm({
82322
83593
  "../src/database/project-version-whole-graph-validation.ts"() {
82323
83594
  "use strict";
@@ -82331,12 +83602,22 @@ var init_project_version_whole_graph_validation = __esm({
82331
83602
  init_project_version_schema_commit();
82332
83603
  init_project_version_system_protection_writes();
82333
83604
  init_members();
83605
+ init_generics();
83606
+ init_inheritance();
83607
+ init_neoscript();
82334
83608
  init_value_row_owner_members();
82335
83609
  init_init_backed_value_materialization();
82336
83610
  init_world_system_classes();
82337
83611
  init_project_world_reference_graph();
82338
83612
  init_project_record_semantics();
82339
83613
  INTERFACE_VALIDATED_RECORD_KINDS = /* @__PURE__ */ new Set(["member", "class", "constructor", "interface"]);
83614
+ ACTION_LISTENER_TRIGGER_RECORD_KINDS = /* @__PURE__ */ new Set(["member", "class", "value"]);
83615
+ ACTION_LISTENER_TARGET_KINDS = /* @__PURE__ */ new Set([
83616
+ 13 /* Function */,
83617
+ 23 /* NSFunction */,
83618
+ 25 /* NSDelegate */,
83619
+ 26 /* NSAction */
83620
+ ]);
82340
83621
  }
82341
83622
  });
82342
83623
 
@@ -84137,6 +85418,7 @@ function assertServerPreparationSucceeds(args) {
84137
85418
  prepared
84138
85419
  );
84139
85420
  }
85421
+ return prepared;
84140
85422
  } catch (error) {
84141
85423
  throw locateBodyCompileFailure(error, args.sourceByRecord);
84142
85424
  }
@@ -84354,7 +85636,7 @@ function replayStoredConstructionV4(args) {
84354
85636
  "Construction replay cannot compile document bodies without a compiler project."
84355
85637
  );
84356
85638
  }
84357
- compilePulledProjectDocumentBodiesV4(document, compilationProject);
85639
+ compilePulledProjectDocumentForEvaluationV4(document, compilationProject);
84358
85640
  }
84359
85641
  const root = document.values.find((value) => value.id === args.valueId);
84360
85642
  if (root === void 0) {
@@ -84550,7 +85832,16 @@ function compilePulledValueInitializerBodyV4(document, valueId, compilationProje
84550
85832
  ...compilationProject === void 0 ? {} : { compilationProject }
84551
85833
  });
84552
85834
  }
84553
- function compilePulledProjectDocumentBodiesV4(document, compilationProject) {
85835
+ function compilePulledProjectDocumentForEvaluationV4(document, compilationProject) {
85836
+ const sharedCompilationProject = compilationProject ?? createNeoScriptCompilationProject({
85837
+ project: document.project,
85838
+ projectFiles: document.projectFiles,
85839
+ members: document.members,
85840
+ classes: document.classes,
85841
+ enums: document.enums,
85842
+ interfaces: document.interfaces,
85843
+ constructors: document.constructors ?? []
85844
+ });
84554
85845
  const compileArgs = {
84555
85846
  project: document.project,
84556
85847
  projectFiles: document.projectFiles,
@@ -84559,7 +85850,7 @@ function compilePulledProjectDocumentBodiesV4(document, compilationProject) {
84559
85850
  enums: document.enums,
84560
85851
  interfaces: document.interfaces,
84561
85852
  constructors: document.constructors ?? [],
84562
- compilationProject
85853
+ compilationProject: sharedCompilationProject
84563
85854
  };
84564
85855
  for (const constructor2 of compileArgs.constructors) {
84565
85856
  compileConstructorRecord({ ...compileArgs, constructor: constructor2 });
@@ -84579,6 +85870,20 @@ function compilePulledProjectDocumentBodiesV4(document, compilationProject) {
84579
85870
  thisClass: ownerClassByMemberId.get(member.id) ?? null
84580
85871
  });
84581
85872
  }
85873
+ for (const site of valueInitializerCompilationSites(document).values()) {
85874
+ if (isInitValueContent(site.row) && site.row.init.compiled !== void 0) {
85875
+ continue;
85876
+ }
85877
+ compileValueRowInitializerBody({
85878
+ ...compileArgs,
85879
+ member: site.member,
85880
+ valueRow: site.row,
85881
+ valueId: String(Reflect.get(site.row, "id")),
85882
+ initializerOwnerClass: site.ownerClass,
85883
+ lexicalThisClass: site.lexicalThisClass,
85884
+ storedConstructionReplay: true
85885
+ });
85886
+ }
84582
85887
  }
84583
85888
  function assertPulledConstructorsCompiled(document) {
84584
85889
  const uncompiled = (document.constructors ?? []).find(
@@ -86223,7 +87528,7 @@ function lowerSeedRow(context, member, sourceExpression, source, valueId, path,
86223
87528
  member,
86224
87529
  inheritedEnvironment
86225
87530
  );
86226
- if (resolvedMember.kind !== "delegate" && initializerRequiresEvaluation(
87531
+ if (!isStoredCallableLiteral(resolvedMember.kind, expression) && initializerRequiresEvaluation(
86227
87532
  context.declaredConstructors,
86228
87533
  expression,
86229
87534
  memberClassName(context, resolvedMember),
@@ -86659,7 +87964,7 @@ function lowerValueRow(context, member, sourceExpression, expectedValueId, sourc
86659
87964
  member,
86660
87965
  environment
86661
87966
  );
86662
- if (resolvedMember.kind !== "delegate" && initializerRequiresEvaluation(
87967
+ if (!isStoredCallableLiteral(resolvedMember.kind, expression) && initializerRequiresEvaluation(
86663
87968
  context.declaredConstructors,
86664
87969
  expression,
86665
87970
  memberClassName(context, resolvedMember),
@@ -86813,6 +88118,11 @@ function lowerValueBody(context, member, expression, base, source, environment,
86813
88118
  authoredSlice
86814
88119
  )
86815
88120
  };
88121
+ case "action":
88122
+ return {
88123
+ ...next,
88124
+ value: lowerActionValue(context, expression, source.ownerClassId)
88125
+ };
86816
88126
  case "class":
86817
88127
  return lowerClassValue(
86818
88128
  context,
@@ -88593,6 +89903,65 @@ function lowerFunctionReference(context, expression, ownerClassId) {
88593
89903
  }
88594
89904
  return { functionMemberId: target.id };
88595
89905
  }
89906
+ function isStoredCallableLiteral(kind, expression) {
89907
+ if (kind === "delegate") return true;
89908
+ if (kind !== "action") return false;
89909
+ return expression.kind === "litList";
89910
+ }
89911
+ function lowerActionValue(context, expression, ownerClassId) {
89912
+ const elements = expression.kind === "litList" ? expression.elements : [expression];
89913
+ const listeners = [];
89914
+ const identities = /* @__PURE__ */ new Set();
89915
+ elements.forEach((element, index) => {
89916
+ const listener = lowerActionListener(context, element, ownerClassId, index);
89917
+ if (identities.has(listener.memberId)) {
89918
+ throw new Error(
89919
+ `NeoAction listener ${index} repeats an earlier listener; a listener set holds each target once.`
89920
+ );
89921
+ }
89922
+ identities.add(listener.memberId);
89923
+ listeners.push(listener);
89924
+ });
89925
+ return { listeners };
89926
+ }
89927
+ function lowerActionListener(context, element, ownerClassId, index) {
89928
+ const { expression } = annotatedValue(element);
89929
+ if (expression.kind === "lambda") {
89930
+ throw new Error(
89931
+ `NeoAction listener ${index} is a lambda. A listener must be a member reference: extract the body to an NSFunction member and subscribe that member instead.`
89932
+ );
89933
+ }
89934
+ const path = memberPath(expression);
89935
+ const symbol = path?.startsWith("this.") ? path.slice("this.".length) : path;
89936
+ if (symbol === null || symbol === void 0 || symbol.includes(".")) {
89937
+ throw new Error(
89938
+ `NeoAction listener ${index} must name a function, NSFunction, NSDelegate, or NeoAction member on the current Class.`
89939
+ );
89940
+ }
89941
+ if (ownerClassId === void 0) {
89942
+ throw new Error(
89943
+ `NeoAction listener ${symbol} cannot be resolved without its owning Class.`
89944
+ );
89945
+ }
89946
+ const target = classMemberByName(context, ownerClassId, symbol);
89947
+ if (target.kind === "action") return { memberId: target.id, valueId: null };
89948
+ if (target.kind !== "function" && target.kind !== "scriptFunction" && target.kind !== "delegate") {
89949
+ throw new Error(
89950
+ `NeoAction listener ${symbol} does not resolve to a function, NSFunction, NSDelegate, or NeoAction member.`
89951
+ );
89952
+ }
89953
+ if (target.returnType.kind !== "void") {
89954
+ throw new Error(
89955
+ `NeoAction listener ${symbol} returns a value; a listener must be void.`
89956
+ );
89957
+ }
89958
+ if (target.kind !== "delegate" && target.deferred) {
89959
+ throw new Error(
89960
+ `NeoAction listener ${symbol} is deferred; listeners are immediate-only.`
89961
+ );
89962
+ }
89963
+ return { memberId: target.id, valueId: null };
89964
+ }
88596
89965
  function lowerDelegateValue(context, expression, ownerClassId, authoredSlice) {
88597
89966
  if (expression.kind === "lambda") {
88598
89967
  const code = initializerExpressionSlice(authoredSlice);
@@ -88803,6 +90172,7 @@ function emitValueBody(context, member, value, visited, targetTyped, environment
88803
90172
  if (kind === 18) return referenceValue(context, member, body, true);
88804
90173
  if (kind === 24) return functionReferenceValue(context, body);
88805
90174
  if (kind === 25) return delegateValue(context, body);
90175
+ if (kind === 26) return actionValue(context, body);
88806
90176
  if (kind === 12) {
88807
90177
  return fileValue(
88808
90178
  context,
@@ -89174,6 +90544,8 @@ function storedConstructorArgumentSource(context, type, value, environment, visi
89174
90544
  );
89175
90545
  case "delegate":
89176
90546
  return delegateValue(context, value);
90547
+ case "action":
90548
+ return actionValue(context, value);
89177
90549
  case "class": {
89178
90550
  if (typeof value !== "string") {
89179
90551
  throw new Error(
@@ -89382,6 +90754,12 @@ function schemaTypeForBindingMember(context, memberId, environment, visited = /*
89382
90754
  returnType: member.returnType,
89383
90755
  argumentTypes: member.arguments.map((argument2) => argument2.type)
89384
90756
  };
90757
+ case "action":
90758
+ return {
90759
+ kind: "action",
90760
+ nullable: false,
90761
+ argumentTypes: member.arguments.map((argument2) => argument2.type)
90762
+ };
89385
90763
  case "generic":
89386
90764
  return schemaTypeForBindingMember(
89387
90765
  context,
@@ -89489,6 +90867,8 @@ function storedSchemaTypeName(context, type, environment, valueId) {
89489
90867
  return "Dialogue";
89490
90868
  case "delegate":
89491
90869
  return "object";
90870
+ case "action":
90871
+ return "object";
89492
90872
  case "null":
89493
90873
  return "null";
89494
90874
  }
@@ -90104,6 +91484,45 @@ function delegateValue(context, body) {
90104
91484
  }
90105
91485
  return target.name;
90106
91486
  }
91487
+ function actionValue(context, body) {
91488
+ if (!isObjectRecord2(body)) {
91489
+ throw new Error("NSAction value is not a listener set.");
91490
+ }
91491
+ if (!Array.isArray(body.listeners)) {
91492
+ throw new Error("NSAction value is missing its listeners array.");
91493
+ }
91494
+ const names = body.listeners.map(
91495
+ (listener, index) => actionListenerName(context, listener, index)
91496
+ );
91497
+ return `[${names.join(", ")}]`;
91498
+ }
91499
+ function actionListenerName(context, listener, index) {
91500
+ if (!isObjectRecord2(listener)) {
91501
+ throw new Error(`NSAction listener ${index} is not a member target.`);
91502
+ }
91503
+ if (typeof listener.code === "string") {
91504
+ throw new Error(
91505
+ `NSAction listener ${index} stores a closure; only member targets are listeners.`
91506
+ );
91507
+ }
91508
+ if (typeof listener.memberId !== "string") {
91509
+ throw new Error(`NSAction listener ${index} is missing memberId.`);
91510
+ }
91511
+ if (listener.valueId !== null && listener.valueId !== void 0) {
91512
+ throw new Error(
91513
+ `NSAction listener ${index} is instance-bound and cannot be represented by current project source.`
91514
+ );
91515
+ }
91516
+ const target = context.members.get(listener.memberId);
91517
+ if (target === void 0 || typeof target.name !== "string") {
91518
+ throw new Error(`NSAction listener ${listener.memberId} was not pulled.`);
91519
+ }
91520
+ const kind = numberField(target, "kind");
91521
+ if (kind !== 13 && kind !== 23 && kind !== 25 && kind !== 26) {
91522
+ throw new Error(`NSAction listener ${listener.memberId} is not callable.`);
91523
+ }
91524
+ return target.name;
91525
+ }
90107
91526
  function fileValue(context, kind, label, body, required2) {
90108
91527
  if (body === null || body === void 0) {
90109
91528
  if (required2) {
@@ -95537,7 +96956,7 @@ async function runMigrate(workspace, subcommand, positional, targetRef, json, de
95537
96956
  );
95538
96957
  assertMigrationCheckWorkspaceIsValid(status);
95539
96958
  const candidate = localMigrationCheckCandidate(status);
95540
- const compileAction = dependencies.compileAction ?? compileNSAction;
96959
+ const compileAction = dependencies.compileAction ?? compileNSVoidBody;
95541
96960
  const bodySourceLocator = createNeoScriptBodySourceLocator(
95542
96961
  workspace,
95543
96962
  status
@@ -95948,7 +97367,7 @@ async function runPendingMigrations(workspace, client, raw, migrations, onlyRef,
95948
97367
  const pinned = migration.action;
95949
97368
  const migrationCode = migration.code;
95950
97369
  const compiled = pinned !== void 0 && pinned !== null ? pinned : compileNeoScriptBodyOrThrow(
95951
- () => compileNSAction(migrationCode, {
97370
+ () => compileNSVoidBody(migrationCode, {
95952
97371
  project: document.project,
95953
97372
  members: document.members,
95954
97373
  classes: document.classes,
@@ -95973,7 +97392,7 @@ async function runPendingMigrations(workspace, client, raw, migrations, onlyRef,
95973
97392
  for (const instance of instances) {
95974
97393
  let result;
95975
97394
  try {
95976
- result = evaluateNSAction(
97395
+ result = evaluateNSVoidBody(
95977
97396
  compiled,
95978
97397
  {
95979
97398
  vm: {
@@ -97246,7 +98665,7 @@ async function runScript(workspace, command, options, dependencies = {}) {
97246
98665
  );
97247
98666
  } else if (command === "apply" || options.mode === "action") {
97248
98667
  compiled = compileNeoScriptBodyOrThrow(
97249
- () => compileNSAction(source, compileContext),
98668
+ () => compileNSVoidBody(source, compileContext),
97250
98669
  bodySite("action"),
97251
98670
  null
97252
98671
  );
@@ -97410,7 +98829,10 @@ ${JSON.stringify(report2, null, 2)}`
97410
98829
  );
97411
98830
  return;
97412
98831
  }
97413
- const result = evaluateNSAction(compiled, evaluatorContext2);
98832
+ const result = evaluateNSVoidBody(
98833
+ compiled,
98834
+ evaluatorContext2
98835
+ );
97414
98836
  const report = {
97415
98837
  ok: true,
97416
98838
  writes: result.writes,
@@ -97660,12 +99082,12 @@ function buildStoredNeoScriptChecks(document) {
97660
99082
  }
97661
99083
  if (node.type === 2 /* Actions */) {
97662
99084
  if (!Array.isArray(node.actions)) continue;
97663
- node.actions.forEach((actionValue, actionIndex) => {
97664
- if (!isObjectRecord2(actionValue)) return;
97665
- if (actionValue.type !== 0 /* EditMember */) return;
97666
- const logic = isObjectRecord2(actionValue.logic) ? actionValue.logic : null;
99085
+ node.actions.forEach((actionValue2, actionIndex) => {
99086
+ if (!isObjectRecord2(actionValue2)) return;
99087
+ if (actionValue2.type !== 0 /* EditMember */) return;
99088
+ const logic = isObjectRecord2(actionValue2.logic) ? actionValue2.logic : null;
97667
99089
  if (logic?.type !== 1 /* Code */) return;
97668
- const actionId = stableRecordId(actionValue, `action-${actionIndex}`);
99090
+ const actionId = stableRecordId(actionValue2, `action-${actionIndex}`);
97669
99091
  checks.push({
97670
99092
  recordKind: "dialogue-node",
97671
99093
  id: nodeId,
@@ -100098,6 +101520,7 @@ async function prepareLocalPushArtifactsV4(workspace, status, onPhase = () => vo
100098
101520
  status.changes,
100099
101521
  localizedTextCreateEnvelopeConfig2(workspace, status.changes)
100100
101522
  );
101523
+ let preparedChanges = null;
100101
101524
  if (options.fullValidation) {
100102
101525
  onPhase("Verifying the complete source round trip\u2026");
100103
101526
  verifyProjectSourceCommitAgainstStateV4({
@@ -100114,7 +101537,7 @@ async function prepareLocalPushArtifactsV4(workspace, status, onPhase = () => vo
100114
101537
  onPhase(
100115
101538
  `Rehearsing server validation for ${transportChanges.length.toLocaleString("en-US")} change(s)\u2026`
100116
101539
  );
100117
- assertServerPreparationSucceeds({
101540
+ preparedChanges = assertServerPreparationSucceeds({
100118
101541
  workspace,
100119
101542
  changes: transportChanges,
100120
101543
  authoredValueSeeds: transportSeeds,
@@ -100129,7 +101552,8 @@ async function prepareLocalPushArtifactsV4(workspace, status, onPhase = () => vo
100129
101552
  transactionOperation,
100130
101553
  transportChanges,
100131
101554
  transportSeeds,
100132
- preparedFiles
101555
+ preparedFiles,
101556
+ preparedChanges
100133
101557
  };
100134
101558
  }
100135
101559
  async function prepareLocalCandidateV4(workspace, options = {}) {
@@ -100203,8 +101627,23 @@ async function prepareLocalCandidateV4(workspace, options = {}) {
100203
101627
  if (typeof baseId === "string") {
100204
101628
  records2.delete(`${change.recordKind}:${baseId}`);
100205
101629
  }
101630
+ }
101631
+ const candidateChanges = preparedLocal?.preparedChanges ?? documentStatus.changes.map((change) => ({
101632
+ recordKind: change.recordKind,
101633
+ recordId: change.recordId,
101634
+ operation: change.kind,
101635
+ nextData: change.nextData
101636
+ }));
101637
+ for (const change of candidateChanges) {
100206
101638
  records2.delete(`${change.recordKind}:${change.recordId}`);
100207
- if (change.nextData === void 0) continue;
101639
+ if (change.operation === "delete" || change.nextData === void 0) {
101640
+ continue;
101641
+ }
101642
+ if (!isObjectRecord2(change.nextData)) {
101643
+ throw new Error(
101644
+ `Prepared ${change.recordKind} record ${JSON.stringify(change.recordId)} has invalid data.`
101645
+ );
101646
+ }
100208
101647
  const nextId = typeof change.nextData.id === "string" ? change.nextData.id : change.recordId;
100209
101648
  records2.set(`${change.recordKind}:${nextId}`, {
100210
101649
  recordKind: change.recordKind,
@@ -100214,9 +101653,11 @@ async function prepareLocalCandidateV4(workspace, options = {}) {
100214
101653
  data: change.nextData
100215
101654
  });
100216
101655
  }
101656
+ const document = readPulledProjectDocumentV4(records2);
101657
+ compilePulledProjectDocumentForEvaluationV4(document);
100217
101658
  return {
100218
101659
  status,
100219
- document: readPulledProjectDocumentV4(records2),
101660
+ document,
100220
101661
  sourceHash,
100221
101662
  preparedLocal
100222
101663
  };
@@ -101562,7 +103003,7 @@ function compileMigrationAction(schema, migrationData, locator) {
101562
103003
  ) ?? null : null;
101563
103004
  const code = String(migrationData.code);
101564
103005
  return compileNeoScriptBodyOrThrow(
101565
- () => compileNSAction2(code, {
103006
+ () => compileNSVoidBody2(code, {
101566
103007
  project: schema.project,
101567
103008
  members: schema.members,
101568
103009
  classes: schema.classes,
@@ -101583,7 +103024,7 @@ function compileMigrationAction(schema, migrationData, locator) {
101583
103024
  locator
101584
103025
  );
101585
103026
  }
101586
- var compileNSAction2, compileNSFunction2, compileNSGetter2, compileNSSetter2, ProjectTransactionInterruptedError, ProjectTransactionFailedError, PROJECT_TRANSACTION_POLL_DELAYS_MS, LocalCandidateArtifactError, SERVER_MINTED_PUSH_RECORD_KINDS;
103027
+ var compileNSVoidBody2, compileNSFunction2, compileNSGetter2, compileNSSetter2, ProjectTransactionInterruptedError, ProjectTransactionFailedError, PROJECT_TRANSACTION_POLL_DELAYS_MS, LocalCandidateArtifactError, SERVER_MINTED_PUSH_RECORD_KINDS;
101587
103028
  var init_push = __esm({
101588
103029
  "src/commands/push.ts"() {
101589
103030
  "use strict";
@@ -101619,7 +103060,12 @@ var init_push = __esm({
101619
103060
  init_merge();
101620
103061
  init_push_change_intent();
101621
103062
  init_push_body_diagnostics();
101622
- ({ compileNSAction: compileNSAction2, compileNSFunction: compileNSFunction2, compileNSGetter: compileNSGetter2, compileNSSetter: compileNSSetter2 } = compiler_adapter_exports);
103063
+ ({
103064
+ compileNSVoidBody: compileNSVoidBody2,
103065
+ compileNSFunction: compileNSFunction2,
103066
+ compileNSGetter: compileNSGetter2,
103067
+ compileNSSetter: compileNSSetter2
103068
+ } = compiler_adapter_exports);
101623
103069
  ProjectTransactionInterruptedError = class extends Error {
101624
103070
  constructor(transactionId) {
101625
103071
  super(
@@ -101670,7 +103116,7 @@ var init_registry2 = __esm({
101670
103116
  PROJECT_SCHEMA_CONTRACT = Object.freeze({
101671
103117
  formatVersion: 3,
101672
103118
  contractVersion: "3.9",
101673
- cliVersion: "0.24.8",
103119
+ cliVersion: "0.25.0",
101674
103120
  projectFileUploadBatchSize: 32,
101675
103121
  documentRecords: {
101676
103122
  member: {
@@ -103125,7 +104571,11 @@ function preparedHookCandidate(workspace) {
103125
104571
  }
103126
104572
  try {
103127
104573
  const directory = realpathSync(configuredDirectory);
103128
- const cacheRoot = resolve4(workspace.root, ".neo", "test-build");
104574
+ const cacheRoot = resolve4(
104575
+ realpathSync(workspace.root),
104576
+ ".neo",
104577
+ "test-build"
104578
+ );
103129
104579
  const pathFromRoot = relative6(cacheRoot, directory);
103130
104580
  if (isAbsolute2(pathFromRoot)) {
103131
104581
  throw new NeoTestPreparedCandidateError(
@@ -105738,7 +107188,7 @@ function runActions(node, label, subject, world, report, violate) {
105738
107188
  const ctx = evaluatorContext(subject, world);
105739
107189
  let result;
105740
107190
  try {
105741
- result = evaluateNSAction(compiled, ctx);
107191
+ result = evaluateNSVoidBody(compiled, ctx);
105742
107192
  } catch (error) {
105743
107193
  if (error instanceof NativeFunctionDelegateUnavailableError) {
105744
107194
  const message = error.message;